diff --git a/package.json b/package.json index 6d6a7282..4886d4d1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "iSunFA", - "version": "0.8.2+19", + "version": "0.8.2+20", "private": false, "scripts": { "dev": "next dev", diff --git a/src/components/voucher/new_voucher_form.tsx b/src/components/voucher/new_voucher_form.tsx new file mode 100644 index 00000000..eb6df175 --- /dev/null +++ b/src/components/voucher/new_voucher_form.tsx @@ -0,0 +1,170 @@ +import React, { useState } from 'react'; +import { FaChevronDown } from 'react-icons/fa6'; +import { BiSave } from 'react-icons/bi'; +import { useTranslation } from 'next-i18next'; +import useOuterClick from '@/lib/hooks/use_outer_click'; +import { Button } from '@/components/button/button'; +import DatePicker, { DatePickerType } from '@/components/date_picker/date_picker'; +import Toggle from '@/components/toggle/toggle'; +import { IDatePeriod } from '@/interfaces/date_period'; +import { default30DayPeriodInSec } from '@/constants/display'; +import { VoucherType } from '@/constants/account'; + +const NewVoucherForm = () => { + const { t } = useTranslation('common'); + + const [date, setDate] = useState(default30DayPeriodInSec); + const [type, setType] = useState(VoucherType.EXPENSE); + const [note, setNote] = useState(''); + const [counterparty, setCounterparty] = useState(''); + const [isRecurring, setIsRecurring] = useState(false); + + // ToDo: (20240926 - Julian) Add 'credit not equal to debit' + const saveBtnDisabled = (date.startTimeStamp === 0 && date.endTimeStamp === 0) || type === ''; + + const { + targetRef: typeRef, + componentVisible: typeVisible, + setComponentVisible: setTypeVisible, + } = useOuterClick(false); + + const typeToggleHandler = () => { + setTypeVisible(!typeVisible); + }; + + const noteChangeHandler = (e: React.ChangeEvent) => { + setNote(e.target.value); + }; + + const counterpartyChangeHandler = (e: React.ChangeEvent) => { + setCounterparty(e.target.value); + }; + + const recurringToggleHandler = () => { + setIsRecurring(!isRecurring); + }; + + // ToDo: (20240926 - Julian) Save voucher function + const saveVoucher = (e: React.FormEvent) => { + e.preventDefault(); + }; + + // ToDo: (20240926 - Julian) type 字串轉換 + const translateType = (voucherType: string) => { + return t(`journal:ADD_NEW_VOUCHER.TYPE_${voucherType.toUpperCase()}`); + }; + + const typeDropdownMenu = typeVisible ? ( +
+ {Object.values(VoucherType).map((voucherType) => { + const typeClickHandler = () => { + setType(voucherType); + setTypeVisible(false); + }; + + return ( + + ); + })} +
+ ) : null; + + return ( +
+ {/* ToDo: (20240926 - Julian) AI analyze */} +
+ This is AI analyze +
+ {/* ToDo: (20240926 - Julian) Uploaded certificates */} +
+ Uploaded certificates +
+ + {/* Info: (20240926 - Julian) form */} +
+ {/* Info: (20240926 - Julian) Date */} +
+

+ {t('journal:ADD_NEW_VOUCHER.VOUCHER_DATE')} + * +

+ +
+ {/* Info: (20240926 - Julian) Type */} +
+

+ {t('journal:ADD_NEW_VOUCHER.VOUCHER_TYPE')} + * +

+
+

{translateType(type)}

+ + {/* Info: (20240926 - Julian) Type dropdown */} + {typeDropdownMenu} +
+
+ {/* Info: (20240926 - Julian) Note */} +
+

{t('journal:ADD_NEW_VOUCHER.NOTE')}

+ +
+ {/* Info: (20240926 - Julian) Counterparty */} +
+

+ {t('journal:ADD_NEW_VOUCHER.COUNTERPARTY')} +

+ +
+ {/* Info: (20240926 - Julian) switch */} +
+ +

{t('journal:ADD_NEW_VOUCHER.RECURRING_ENTRY')}

+
+ {/* ToDo: (20240926 - Julian) voucher block */} +
+ This is voucher block +
+ {/* Info: (20240926 - Julian) buttons */} +
+ + +
+
+
+ ); +}; + +export default NewVoucherForm; diff --git a/src/locales/cn/journal.json b/src/locales/cn/journal.json index cc4fbbc1..7edad74f 100644 --- a/src/locales/cn/journal.json +++ b/src/locales/cn/journal.json @@ -220,5 +220,16 @@ "EXPORT_BTN": "汇出", "FROM_PLACEHOLDER": "从", "TO_PLACEHOLDER": "到" + }, + "ADD_NEW_VOUCHER": { + "PAGE_TITLE": "新增传票", + "VOUCHER_DATE": "传票日期", + "VOUCHER_TYPE": "传票类型", + "TYPE_RECEIVE": "收入", + "TYPE_EXPENSE": "支付", + "TYPE_TRANSFER": "转帐", + "NOTE": "备注", + "COUNTERPARTY": "交易对象", + "RECURRING_ENTRY": "周期性分录" } } diff --git a/src/locales/en/journal.json b/src/locales/en/journal.json index d24c17a0..0e0a2608 100644 --- a/src/locales/en/journal.json +++ b/src/locales/en/journal.json @@ -220,5 +220,16 @@ "EXPORT_BTN": "Export", "FROM_PLACEHOLDER": "From", "TO_PLACEHOLDER": "To" + }, + "ADD_NEW_VOUCHER": { + "PAGE_TITLE": "Add New Voucher", + "VOUCHER_DATE": "Voucher Date", + "VOUCHER_TYPE": "Voucher Type", + "TYPE_RECEIVE": "Receiving", + "TYPE_EXPENSE": "Payment", + "TYPE_TRANSFER": "Transfer", + "NOTE": "Note", + "COUNTERPARTY": "Counterparty", + "RECURRING_ENTRY": "Recurring Entry" } } diff --git a/src/locales/tw/journal.json b/src/locales/tw/journal.json index d1b98765..0cd71713 100644 --- a/src/locales/tw/journal.json +++ b/src/locales/tw/journal.json @@ -220,5 +220,16 @@ "EXPORT_BTN": "匯出", "FROM_PLACEHOLDER": "從", "TO_PLACEHOLDER": "到" + }, + "ADD_NEW_VOUCHER": { + "PAGE_TITLE": "新增傳票", + "VOUCHER_DATE": "傳票日期", + "VOUCHER_TYPE": "傳票類型", + "TYPE_RECEIVE": "收入", + "TYPE_EXPENSE": "支付", + "TYPE_TRANSFER": "轉帳", + "NOTE": "備註", + "COUNTERPARTY": "交易對象", + "RECURRING_ENTRY": "週期性分錄" } } diff --git a/src/pages/users/accounting/add_new_voucher.tsx b/src/pages/users/accounting/add_new_voucher.tsx new file mode 100644 index 00000000..6b15cac7 --- /dev/null +++ b/src/pages/users/accounting/add_new_voucher.tsx @@ -0,0 +1,70 @@ +import React, { useState } from 'react'; +import Head from 'next/head'; +import { useTranslation } from 'next-i18next'; +import { serverSideTranslations } from 'next-i18next/serverSideTranslations'; +import { ILocale } from '@/interfaces/locale'; +import NewVoucherForm from '@/components/voucher/new_voucher_form'; + +const AddNewVoucherPage = () => { + const { t } = useTranslation('common'); + + const [isSidebarOpen, setIsSidebarOpen] = useState(true); + + return ( + <> + + + + + {t('journal:ADD_NEW_VOUCHER.PAGE_TITLE')} - iSunFA + + + + +
+ This is header +
+
+ This is sidebar +
+ + {/* Info: (20240925 - Julian) Body */} +
+ +
+ + ); +}; + +const getStaticPropsFunction = async ({ locale }: ILocale) => ({ + props: { + ...(await serverSideTranslations(locale, [ + 'common', + 'journal', + 'kyc', + 'project', + 'report_401', + 'salary', + 'setting', + 'terms', + 'asset', + ])), + locale, + }, +}); + +export const getStaticProps = getStaticPropsFunction; + +export default AddNewVoucherPage;