| @@ -1,11 +1,11 @@ | |||
| import { preloadExpense } from "@/app/api/expenses" | |||
| import { preloadProjectExpenses } from "@/app/api/projectExpenses" | |||
| import ExpenseSearch from "@/components/ExpenseSearch" | |||
| import { getServerI18n, I18nProvider } from "@/i18n" | |||
| import { Stack, Typography } from "@mui/material" | |||
| import { Suspense } from "react" | |||
| const Expense: React.FC = async () => { | |||
| preloadExpense() | |||
| preloadProjectExpenses() | |||
| const { t } = await getServerI18n("expense") | |||
| return( | |||
| @@ -1,28 +0,0 @@ | |||
| "use server"; | |||
| import { cache } from "react"; | |||
| import { serverFetchJson } from "@/app/utils/fetchUtil"; | |||
| import { BASE_API_URL } from "@/config/api"; | |||
| export type ExpensesResult = { | |||
| id: number | |||
| projectCode: string | |||
| projectName: string | |||
| staffCode: string | |||
| staffName: string | |||
| description: string | |||
| amount: number | |||
| approvedAmount: number | |||
| verifiedDatetime: number[] | |||
| remark: string | |||
| } | |||
| export const preloadExpense = () => { | |||
| fetchExpenses() | |||
| }; | |||
| export const fetchExpenses = cache(async () => { | |||
| return serverFetchJson<ExpensesResult[]>(`${BASE_API_URL}/expense`, { | |||
| next: { tags: ["expense"] }, | |||
| }); | |||
| }); | |||
| @@ -0,0 +1,25 @@ | |||
| "use server" | |||
| import { serverFetchJson, serverFetchWithNoContent } from "@/app/utils/fetchUtil"; | |||
| import { BASE_API_URL } from "@/config/api"; | |||
| import { cache } from "react"; | |||
| import { revalidateTag } from "next/cache"; | |||
| export type CreateNewExpense = { | |||
| expenseNo: string | undefined, | |||
| projectCode: string | undefined, | |||
| issueDate: Date | |||
| issuedAmount: number, | |||
| receiptDate: Date | |||
| receivedAmount: number | |||
| } | |||
| export type PostExpenseData = { | |||
| expenseNo?: string | |||
| projectId: number | |||
| projectCode: string, | |||
| amount: number | |||
| issueDate: string | |||
| receiptDate?: string | |||
| } | |||
| @@ -0,0 +1,31 @@ | |||
| "use server"; | |||
| import { cache } from "react"; | |||
| import { serverFetchJson } from "@/app/utils/fetchUtil"; | |||
| import { BASE_API_URL } from "@/config/api"; | |||
| export type ProjectExpensesResult = { | |||
| id: number | |||
| expenseNo?: string | |||
| projectCode: string | |||
| projectName: string | |||
| teamCode: string | |||
| teamName: string | |||
| amount: number | |||
| issueDate: number[] | |||
| receiptDate: number[] | |||
| } | |||
| export type ProjectExpensesResultFormatted = Omit<ProjectExpensesResult, 'issueDate' | 'receiptDate'> & { | |||
| issueDate: string; | |||
| receiptDate: string; | |||
| }; | |||
| export const preloadProjectExpenses = () => { | |||
| fetchProjectExpenses() | |||
| }; | |||
| export const fetchProjectExpenses = cache(async () => { | |||
| return serverFetchJson<ProjectExpensesResult[]>(`${BASE_API_URL}/project-expense`, { | |||
| next: { tags: ["projectExpenses"] }, | |||
| }); | |||
| }); | |||
| @@ -12,8 +12,11 @@ import { | |||
| import { useTranslation } from 'react-i18next'; | |||
| import { FormProvider, SubmitHandler, useForm } from 'react-hook-form'; | |||
| import { Check, Close } from "@mui/icons-material"; | |||
| import InvoiceTable from './ExpenseTable'; | |||
| import { ProjectResult } from '@/app/api/projects'; | |||
| import { CreateNewExpense, PostExpenseData } from '@/app/api/projectExpenses/actions'; | |||
| import ExpenseTable from './ExpenseTable'; | |||
| import dayjs from 'dayjs'; | |||
| import { INPUT_DATE_FORMAT } from '@/app/utils/formatUtil'; | |||
| interface Props { | |||
| isOpen: boolean, | |||
| @@ -32,13 +35,30 @@ const modalSx: SxProps= { | |||
| bgcolor: 'background.paper', | |||
| }; | |||
| const CreateInvoiceModal: React.FC<Props> = ({isOpen, onClose, projects}) => { | |||
| type postData = { | |||
| data: PostExpenseData[] | |||
| } | |||
| const CreateExpenseModal: React.FC<Props> = ({isOpen, onClose, projects}) => { | |||
| const { t } = useTranslation() | |||
| const formProps = useForm<any>(); | |||
| const formProps = useForm<postData>(); | |||
| const onSubmit = useCallback<SubmitHandler<any>>( | |||
| const onSubmit = useCallback<SubmitHandler<postData>>( | |||
| (data) => { | |||
| console.log(data) | |||
| const _data = data.data | |||
| console.log(_data[0]) | |||
| console.log(_data[0].issueDate) | |||
| console.log(_data[1].issueDate) | |||
| const postData: PostExpenseData[] = _data.map(item => { | |||
| console.log(item.issueDate) | |||
| return ({ | |||
| expenseNo: item.expenseNo, | |||
| issueDate: dayjs(item.issueDate).format(INPUT_DATE_FORMAT), | |||
| amount: item.amount, | |||
| projectId: projects.find(p => p.code === item.projectCode)!.id, | |||
| projectCode: item.projectCode, | |||
| })} | |||
| ) | |||
| console.log(postData) | |||
| } | |||
| , []) | |||
| @@ -63,7 +83,7 @@ const CreateInvoiceModal: React.FC<Props> = ({isOpen, onClose, projects}) => { | |||
| marginBlock: 2, | |||
| }} | |||
| > | |||
| <InvoiceTable projects={projects}/> | |||
| <ExpenseTable projects={projects}/> | |||
| </Box> | |||
| <CardActions sx={{ justifyContent: "flex-end" }}> | |||
| <Button | |||
| @@ -84,4 +104,4 @@ const CreateInvoiceModal: React.FC<Props> = ({isOpen, onClose, projects}) => { | |||
| ); | |||
| }; | |||
| export default CreateInvoiceModal; | |||
| export default CreateExpenseModal; | |||
| @@ -1,6 +1,6 @@ | |||
| "use client"; | |||
| import { ExpensesResult } from "@/app/api/expenses"; | |||
| import { useCallback, useMemo, useState } from "react"; | |||
| import { ProjectExpensesResult, ProjectExpensesResultFormatted } from "@/app/api/projectExpenses"; | |||
| import { useCallback, useEffect, useMemo, useState } from "react"; | |||
| import { useTranslation } from "react-i18next"; | |||
| import SearchBox, { Criterion } from "../SearchBox"; | |||
| import SearchResults, { Column } from "../SearchResults"; | |||
| @@ -18,41 +18,55 @@ import { | |||
| import { moneyFormatter } from "@/app/utils/formatUtil"; | |||
| import { EditNote } from "@mui/icons-material"; | |||
| import AddIcon from '@mui/icons-material/Add'; | |||
| import { uniq } from "lodash"; | |||
| import CreateExpenseModal from "./CreateExpenseModal"; | |||
| import { ProjectResult } from "@/app/api/projects"; | |||
| interface Props { | |||
| expenses: ExpensesResult[] | |||
| expenses: ProjectExpensesResultFormatted[] | |||
| projects: ProjectResult[]; | |||
| } | |||
| type SearchQuery = Partial<Omit<ExpensesResult, "id">>; | |||
| type SearchQuery = Partial<Omit<ProjectExpensesResultFormatted, "id">>; | |||
| type SearchParamNames = keyof SearchQuery; | |||
| const ExpenseSearch: React.FC<Props> = ({ expenses }) => { | |||
| console.log(expenses) | |||
| type Modals = { | |||
| createInvoiceModal: boolean | |||
| } | |||
| const initState: Modals = { | |||
| createInvoiceModal: false, | |||
| } | |||
| const ExpenseSearch: React.FC<Props> = ({ expenses, projects }) => { | |||
| const router = useRouter(); | |||
| const { t } = useTranslation("expenses"); | |||
| const [filteredExpenses, setFilteredExpenses] = useState(expenses); | |||
| const [modalsOpen, setModalsOpen] = useState(initState) | |||
| const toggleModals = useCallback((key: keyof Modals) => { | |||
| setModalsOpen((prev) => ({...prev, [key]: !prev[key]})) | |||
| }, [modalsOpen]); | |||
| const searchCriteria: Criterion<SearchParamNames>[] = useMemo( | |||
| () => [ | |||
| // { label: t("Expense No"), paramName: "ExpenseNo", type: "text" }, | |||
| { label: t("Project Code"), paramName: "projectCode", type: "text" }, | |||
| { label: t("Project Name"), paramName: "projectName", type: "text" }, | |||
| { | |||
| label: t("Verified Date"), | |||
| label2: t("Verified Date To"), | |||
| paramName: "verifiedDatetime", | |||
| type: "dateRange", | |||
| }, | |||
| // { | |||
| // label: t("Team"), | |||
| // paramName: "team", | |||
| // type: "select", | |||
| // options: uniq(expenses.map((expenses) => expenses.teamCode)), | |||
| // }, | |||
| ], | |||
| [] | |||
| ); | |||
| const onExpenseClick = useCallback( | |||
| (expenses?: ExpensesResult) => {}, | |||
| (expenses?: ProjectExpensesResultFormatted) => {}, | |||
| [router] | |||
| ); | |||
| const columns = useMemo<Column<ExpensesResult>[]>( | |||
| const columns = useMemo<Column<ProjectExpensesResultFormatted>[]>( | |||
| () => [ | |||
| { | |||
| name: "id", | |||
| @@ -63,7 +77,9 @@ const ExpenseSearch: React.FC<Props> = ({ expenses }) => { | |||
| }, | |||
| { name: "projectCode", label: t("Project Code") }, | |||
| { name: "projectName", label: t("Project Name") }, | |||
| { name: "verifiedDatetime", label: t("verifiedDatetime") }, | |||
| { name: "amount", label: t("Amount") }, | |||
| { name: "teamCode", label: t("Team") }, | |||
| { name: "issueDate", label: t("Issue Date") }, | |||
| ], | |||
| [t, onExpenseClick] | |||
| ); | |||
| @@ -72,6 +88,7 @@ const ExpenseSearch: React.FC<Props> = ({ expenses }) => { | |||
| }, []); | |||
| return ( | |||
| <> | |||
| <Stack | |||
| spacing={2} | |||
| > | |||
| @@ -87,7 +104,7 @@ const ExpenseSearch: React.FC<Props> = ({ expenses }) => { | |||
| startIcon={<AddIcon />} | |||
| variant="outlined" | |||
| component="label" | |||
| onClick={() => console.log()} | |||
| onClick={() => toggleModals("createInvoiceModal")} | |||
| > | |||
| {t("Create expense")} | |||
| </Button> | |||
| @@ -134,11 +151,17 @@ const ExpenseSearch: React.FC<Props> = ({ expenses }) => { | |||
| </CardContent> | |||
| </Card> | |||
| <Divider sx={{ paddingBlockEnd: 2 }} /> | |||
| <SearchResults<ExpensesResult> | |||
| <SearchResults<ProjectExpensesResultFormatted> | |||
| items={filteredExpenses} | |||
| columns={columns} | |||
| /> | |||
| </Stack> | |||
| <CreateExpenseModal | |||
| isOpen={modalsOpen.createInvoiceModal} | |||
| onClose={() => toggleModals("createInvoiceModal")} | |||
| projects={projects} | |||
| /> | |||
| </> | |||
| ); | |||
| }; | |||
| export default ExpenseSearch; | |||
| @@ -1,10 +1,12 @@ | |||
| import React from "react"; | |||
| import ExpenseSearch from "./ExpenseSearch"; | |||
| import ExpenseSearchLoading from "./ExpenseSearchLoading"; | |||
| import { fetchExpenses } from "@/app/api/expenses"; | |||
| import { fetchProjectExpenses } from "@/app/api/projectExpenses"; | |||
| import dayjs from "dayjs"; | |||
| import arraySupport from "dayjs/plugin/arraySupport"; | |||
| import { OUTPUT_DATE_FORMAT, OUTPUT_TIME_FORMAT } from "@/app/utils/formatUtil"; | |||
| import { fetchUserStaff } from "@/app/utils/fetchUtil"; | |||
| import { fetchProjects } from "@/app/api/projects"; | |||
| interface SubComponents { | |||
| @@ -14,20 +16,31 @@ dayjs.extend(arraySupport) | |||
| const ExpenseSearchWrapper: React.FC & SubComponents = async () => { | |||
| const [ | |||
| Expenses | |||
| expenses, | |||
| projects | |||
| ] = await Promise.all([ | |||
| fetchExpenses(), | |||
| fetchProjectExpenses(), | |||
| fetchProjects(), | |||
| ]); | |||
| const _expenses = Expenses.map((e) => { | |||
| const date: number[] = e.verifiedDatetime as number[]; | |||
| const formattedDate = dayjs([date[0], date[1], date[2]].join()).format(OUTPUT_DATE_FORMAT) | |||
| // const userStaff = await fetchUserStaff() | |||
| // const teamId = userStaff?.teamId | |||
| // const _projects = projects | |||
| console.log(expenses) | |||
| const _expenses = expenses.map((e) => { | |||
| const issueDate = e.issueDate; | |||
| const receiptDate = e.receiptDate; | |||
| const formattedIssueDate = dayjs([issueDate[0], issueDate[1], issueDate[2]].join()).format(OUTPUT_DATE_FORMAT) | |||
| const formattedReceiptDate = dayjs([receiptDate[0], receiptDate[1], receiptDate[2]].join()).format(OUTPUT_DATE_FORMAT) | |||
| return ({ | |||
| ...e, | |||
| verifiedDatetime: formattedDate | |||
| issueDate: formattedIssueDate, | |||
| receiptDate: formattedReceiptDate | |||
| }) | |||
| }) | |||
| return <ExpenseSearch | |||
| expenses={_expenses} | |||
| projects={projects} | |||
| /> | |||
| }; | |||
| @@ -7,8 +7,6 @@ import { moneyFormatter } from "@/app/utils/formatUtil" | |||
| import { Button, ButtonGroup, Stack, Tab, Tabs, TabsProps, Dialog, DialogTitle, DialogContent, DialogContentText, DialogActions, TextField, CardContent, Typography, Divider, Card, Box, Autocomplete, MenuItem } from "@mui/material"; | |||
| import FileUploadIcon from '@mui/icons-material/FileUpload'; | |||
| import { Add, Check, Close, Delete } from "@mui/icons-material"; | |||
| import { deleteInvoice, importIssuedInovice, importReceivedInovice, updateInvoice } from "@/app/api/invoices/actions"; | |||
| import { deleteDialog, errorDialogWithContent, successDialog } from "../Swal/CustomAlerts"; | |||
| import { invoiceList, issuedInvoiceList, issuedInvoiceSearchForm, receivedInvoiceList, receivedInvoiceSearchForm } from "@/app/api/invoices"; | |||
| import EditOutlinedIcon from '@mui/icons-material/EditOutlined'; | |||
| import { | |||
| @@ -19,7 +17,8 @@ import { | |||
| GridRowModel, | |||
| GridRowModes, | |||
| GridRowModesModel, | |||
| GridRenderEditCellParams, | |||
| GridRenderEditCellParams, | |||
| useGridApiContext, | |||
| } from "@mui/x-data-grid"; | |||
| import { useGridApiRef } from "@mui/x-data-grid"; | |||
| import StyledDataGrid from "../StyledDataGrid"; | |||
| @@ -32,15 +31,17 @@ import { th } from "@faker-js/faker"; | |||
| import { GridRowIdGetter } from "@mui/x-data-grid"; | |||
| import { useFormContext } from "react-hook-form"; | |||
| import { ProjectResult } from "@/app/api/projects"; | |||
| import { ProjectExpensesResultFormatted } from "@/app/api/projectExpenses"; | |||
| import { GridRenderCellParams } from "@mui/x-data-grid"; | |||
| type InvoiceListError = { | |||
| [field in keyof invoiceList]?: string; | |||
| type ExpenseListError = { | |||
| [field in keyof ProjectExpensesResultFormatted]?: string; | |||
| }; | |||
| type invoiceListRow = Partial< | |||
| invoiceList & { | |||
| type ExpenseListRow = Partial< | |||
| ProjectExpensesResultFormatted & { | |||
| _isNew: boolean; | |||
| _error: InvoiceListError; | |||
| _error: ExpenseListError; | |||
| } | |||
| >; | |||
| @@ -49,12 +50,12 @@ interface Props { | |||
| } | |||
| class ProcessRowUpdateError extends Error { | |||
| public readonly row: invoiceListRow; | |||
| public readonly errors: InvoiceListError | undefined; | |||
| public readonly row: ExpenseListRow; | |||
| public readonly errors: ExpenseListError | undefined; | |||
| constructor( | |||
| row: invoiceListRow, | |||
| row: ExpenseListRow, | |||
| message?: string, | |||
| errors?: InvoiceListError, | |||
| errors?: ExpenseListError, | |||
| ) { | |||
| super(message); | |||
| this.row = row; | |||
| @@ -67,34 +68,25 @@ type project = { | |||
| label: string; | |||
| value: number; | |||
| } | |||
| const InvoiceTable: React.FC<Props> = ({ projects }) => { | |||
| const ExpenseTable: React.FC<Props> = ({ projects }) => { | |||
| console.log(projects) | |||
| const projectCombos = projects.map(item => item.code) | |||
| const { t } = useTranslation() | |||
| const [rowModesModel, setRowModesModel] = useState<GridRowModesModel>({}); | |||
| const [selectedRow, setSelectedRow] = useState<invoiceListRow[] | []>([]); | |||
| const [selectedRow, setSelectedRow] = useState<ExpenseListRow[] | []>([]); | |||
| const { getValues, setValue, clearErrors, setError } = | |||
| useFormContext<any>(); | |||
| const apiRef = useGridApiRef(); | |||
| const [projectCode, setProjectCode] = useState<project>({label: "", value: 0}) | |||
| const validateInvoiceEntry = ( | |||
| entry: Partial<invoiceList>, | |||
| ): InvoiceListError | undefined => { | |||
| const validateExpenseEntry = ( | |||
| entry: Partial<ProjectExpensesResultFormatted>, | |||
| ): ExpenseListError | undefined => { | |||
| // Test for errors | |||
| const error: any = {}; | |||
| console.log(entry) | |||
| if (!entry.issuedAmount) { | |||
| error.issuedAmount = "Please input issued amount "; | |||
| } else if (!entry.issuedAmount) { | |||
| error.receivedAmount = "Please input received amount"; | |||
| } else if (entry.invoiceNo === "") { | |||
| error.invoiceNo = "Please input invoice number"; | |||
| } else if (!entry.issuedDate) { | |||
| error.issuedDate = "Please input issue date"; | |||
| } else if (!entry.receiptDate){ | |||
| } | |||
| const error: ExpenseListError = {}; | |||
| if (!entry.issueDate) error.issueDate = "Please input issued date"; | |||
| if (!entry.amount) error.amount = "Please input amount"; | |||
| if (!entry.projectCode) error.projectCode = "Please input project code"; | |||
| console.log(error) | |||
| return Object.keys(error).length > 0 ? error : undefined; | |||
| } | |||
| @@ -105,8 +97,8 @@ const InvoiceTable: React.FC<Props> = ({ projects }) => { | |||
| "", | |||
| ) | |||
| const error = validateInvoiceEntry(row); | |||
| // console.log(error) | |||
| const error = validateExpenseEntry(row); | |||
| console.log(error) | |||
| // Test for warnings | |||
| // apiRef.current.updateRows([{ id, _error: error }]); | |||
| @@ -159,8 +151,8 @@ const InvoiceTable: React.FC<Props> = ({ projects }) => { | |||
| const processRowUpdate = useCallback( | |||
| ( | |||
| newRow: GridRowModel<invoiceListRow>, | |||
| originalRow: GridRowModel<invoiceListRow>, | |||
| newRow: GridRowModel<ExpenseListRow>, | |||
| originalRow: GridRowModel<ExpenseListRow>, | |||
| ) => { | |||
| const errors = validateRow(newRow.id!!); | |||
| if (errors) { | |||
| @@ -178,7 +170,7 @@ const InvoiceTable: React.FC<Props> = ({ projects }) => { | |||
| const rowToSave = { | |||
| ...updatedRow, | |||
| } satisfies invoiceListRow; | |||
| } satisfies ExpenseListRow; | |||
| console.log(newRow) | |||
| @@ -198,7 +190,7 @@ const InvoiceTable: React.FC<Props> = ({ projects }) => { | |||
| (updateError: ProcessRowUpdateError) => { | |||
| const errors = updateError.errors; | |||
| const oldRow = updateError.row; | |||
| // console.log(errors) | |||
| console.log(errors) | |||
| apiRef.current.updateRows([{ ...oldRow, _error: errors }]); | |||
| }, | |||
| [apiRef] | |||
| @@ -209,38 +201,64 @@ const InvoiceTable: React.FC<Props> = ({ projects }) => { | |||
| setValue("data", selectedRow) | |||
| }, [selectedRow, setValue]); | |||
| function renderAutocomplete(params: GridRenderCellParams<any, number>) { | |||
| return( | |||
| <Box sx={{ display: 'flex', alignItems: 'center', pr: 2 }}> | |||
| <Autocomplete | |||
| readOnly | |||
| sx={{ width: 300 }} | |||
| value={params.row.projectCode} | |||
| options={projectCombos} | |||
| renderInput={(params) => <TextField {...params} />} | |||
| /> | |||
| </Box> | |||
| ) | |||
| } | |||
| function AutocompleteInput(props: GridRenderCellParams<any, number>) { | |||
| const { id, value, field, hasFocus } = props; | |||
| const apiRef = useGridApiContext(); | |||
| const ref = React.useRef<HTMLElement>(null); | |||
| const handleValueChange = useCallback((newValue: any) => { | |||
| console.log(newValue) | |||
| apiRef.current.setEditCellValue({ id, field, value: newValue }) | |||
| }, []); | |||
| return ( | |||
| <Box sx={{ display: 'flex', alignItems: 'center', pr: 2 }}> | |||
| <Autocomplete | |||
| disablePortal | |||
| options={projectCombos} | |||
| sx={{ width: 300 }} | |||
| onChange={(event: React.SyntheticEvent<Element, Event>, value: string | null, ) => handleValueChange(value)} | |||
| renderInput={(params) => <TextField {...params} />} | |||
| /> | |||
| </Box> | |||
| ); | |||
| } | |||
| const renderAutocompleteInput: GridColDef['renderCell'] = (params) => { | |||
| return <AutocompleteInput {...params} />; | |||
| }; | |||
| const editCombinedColumns = useMemo<GridColDef[]>( | |||
| () => [ | |||
| { field: "invoiceNo", headerName: t("Invoice No"), editable: true, flex: 0.5 }, | |||
| { field: "expenseNo", headerName: t("Expense No"), editable: true, flex: 0.5 }, | |||
| { field: "projectCode", | |||
| headerName: t("Project Code"), | |||
| editable: true, | |||
| flex: 0.3, | |||
| renderEditCell(params: GridRenderEditCellParams<invoiceListRow, number>){ | |||
| return( | |||
| <Autocomplete | |||
| disablePortal | |||
| options={[]} | |||
| sx={{width: '100%'}} | |||
| renderInput={(params) => <TextField {...params} />} | |||
| /> | |||
| ) | |||
| } | |||
| renderCell: renderAutocomplete, | |||
| renderEditCell: renderAutocompleteInput | |||
| }, | |||
| { field: "issuedDate", | |||
| { field: "issueDate", | |||
| headerName: t("Issue Date"), | |||
| editable: true, | |||
| flex: 0.4, | |||
| // type: 'date', | |||
| // valueGetter: (params) => { | |||
| // // console.log(params.row.issuedDate) | |||
| // return new Date(params.row.issuedDate) | |||
| // }, | |||
| type: 'date', | |||
| }, | |||
| { field: "issuedAmount", | |||
| { field: "amount", | |||
| headerName: t("Amount (HKD)"), | |||
| editable: true, | |||
| flex: 0.5, | |||
| @@ -251,13 +269,8 @@ const editCombinedColumns = useMemo<GridColDef[]>( | |||
| headerName: t("Settle Date"), | |||
| editable: true, | |||
| flex: 0.4, | |||
| }, | |||
| { field: "receivedAmount", | |||
| headerName: t("Actual Received Amount (HKD)"), | |||
| editable: true, | |||
| flex: 0.5, | |||
| type: 'number' | |||
| }, | |||
| type: 'date', | |||
| }, | |||
| ], | |||
| [t] | |||
| ) | |||
| @@ -271,7 +284,7 @@ const footer = ( | |||
| onClick={addRow} | |||
| size="small" | |||
| > | |||
| {t("Create Invoice")} | |||
| {t("Create Expense")} | |||
| </Button> | |||
| </Box> | |||
| ); | |||
| @@ -301,9 +314,9 @@ const footer = ( | |||
| columns={editCombinedColumns} | |||
| processRowUpdate={processRowUpdate} | |||
| onProcessRowUpdateError={onProcessRowUpdateError} | |||
| getCellClassName={(params: GridCellParams<invoiceListRow>) => { | |||
| getCellClassName={(params: GridCellParams<ExpenseListRow>) => { | |||
| let classname = ""; | |||
| if (params.row._error?.[params.field as keyof invoiceList]) { | |||
| if (params.row._error?.[params.field as keyof ProjectExpensesResultFormatted]) { | |||
| classname = "hasError"; | |||
| } | |||
| return classname; | |||
| @@ -320,7 +333,7 @@ const footer = ( | |||
| ) | |||
| } | |||
| export default InvoiceTable | |||
| export default ExpenseTable | |||
| const NoRowsOverlay: React.FC = () => { | |||
| const { t } = useTranslation("home"); | |||
| @@ -19,18 +19,18 @@ interface SubComponents { | |||
| // } | |||
| const InvoiceSearchWrapper: React.FC & SubComponents = async () => { | |||
| const userStaff = await fetchUserStaff() | |||
| const teamId = userStaff?.teamId | |||
| // const userStaff = await fetchUserStaff() | |||
| // const teamId = userStaff?.teamId | |||
| const invoices = await fetchInvoicesV3() | |||
| const projects = await fetchProjects() | |||
| const filteredProjects = projects.filter(project => project.teamId === teamId) | |||
| // const filteredProjects = projects.filter(project => project.teamId === teamId) | |||
| let filteredInvoice = invoices | |||
| if (teamId) { | |||
| filteredInvoice = invoices.filter(invoice => invoice.teamId === teamId) | |||
| } | |||
| // let filteredInvoice = invoices | |||
| // if (teamId) { | |||
| // filteredInvoice = invoices.filter(invoice => invoice.teamId === teamId) | |||
| // } | |||
| const convertedInvoices = filteredInvoice.map((invoice)=>{ | |||
| const convertedInvoices = invoices.map((invoice)=>{ | |||
| return{ | |||
| id: invoice.id, | |||
| invoiceNo: invoice.invoiceNo, | |||
| @@ -47,7 +47,7 @@ const InvoiceSearchWrapper: React.FC & SubComponents = async () => { | |||
| return <InvoiceSearch | |||
| invoices={convertedInvoices} | |||
| projects={filteredProjects} | |||
| projects={projects} | |||
| /> | |||
| }; | |||