Quellcode durchsuchen

Edit Company

Type import Inovice
tags/Baseline_30082024_FRONTEND_UAT
MSI\2Fi vor 1 Jahr
Ursprung
Commit
d2ed0abc04
9 geänderte Dateien mit 262 neuen und 99 gelöschten Zeilen
  1. +30
    -0
      src/app/(main)/settings/company/edit/page.tsx
  2. +31
    -1
      src/app/api/companys/actions.ts
  3. +10
    -0
      src/app/api/companys/index.ts
  4. +19
    -0
      src/app/utils/formatUtil.ts
  5. +28
    -4
      src/components/CompanySearch/CompanySearch.tsx
  6. +91
    -74
      src/components/CreateCompany/CompanyDetails.tsx
  7. +33
    -16
      src/components/CreateCompany/CreateCompany.tsx
  8. +18
    -2
      src/components/CreateCompany/CreateCompanyWrapper.tsx
  9. +2
    -2
      src/components/InvoiceSearch/InvoiceSearch.tsx

+ 30
- 0
src/app/(main)/settings/company/edit/page.tsx Datei anzeigen

@@ -0,0 +1,30 @@
import CreateCompany from "@/components/CreateCompany";
import { I18nProvider, getServerI18n } from "@/i18n";
import Typography from "@mui/material/Typography";
import { Metadata } from "next";

export const metadata: Metadata = {
title: "Create Comapny",
};

interface Props {
searchParams: { [key: string]: string | undefined };
}
const Companys: React.FC<Props> = async ({searchParams}) => {
const { t } = await getServerI18n("companys");
const companyId = searchParams["id"];

return(
<>
<Typography variant="h4">{t("Create Company")}</Typography>
<I18nProvider namespaces={["companys"]}>
<CreateCompany isEdit={true} companyId={companyId} />
</I18nProvider>
</>
)

}

export default Companys;

+ 31
- 1
src/app/api/companys/actions.ts Datei anzeigen

@@ -1,6 +1,6 @@
"use server";

import { serverFetchJson } from "@/app/utils/fetchUtil";
import { serverFetchJson, serverFetchWithNoContent } from "@/app/utils/fetchUtil";
import { BASE_API_URL } from "@/config/api";
import { Dayjs } from "dayjs";
import { cache } from "react";
@@ -15,6 +15,7 @@ export interface combo {
}

export interface CreateCompanyInputs {
id?: number;
companyCode: string;
companyName: string;
brNo: string;
@@ -30,6 +31,23 @@ export interface CreateCompanyInputs {
email: string;
}

export interface EditCompanyInputs {
id?: number;
companyCode: string;
name: string;
brNo: string;
contactName: string;
phone: string;
otHourTo: number[];
otHourFrom: number[];
normalHourTo: number[];
normalHourFrom: number[];
currency: string;
address: string;
district: string;
email: string;
}

export const saveCompany = async (data: CreateCompanyInputs) => {
return serverFetchJson(`${BASE_API_URL}/companys/new`, {
method: "POST",
@@ -43,3 +61,15 @@ export const fetchCompanyCombo = cache(async () => {
next: { tags: ["company"] },
});
});

export const deleteCompany = async (id: number) => {
const department = await serverFetchWithNoContent(
`${BASE_API_URL}/companys/${id}`,
{
method: "DELETE",
headers: { "Content-Type": "application/json" },
},
);

return department
};

+ 10
- 0
src/app/api/companys/index.ts Datei anzeigen

@@ -2,6 +2,7 @@ import { serverFetchJson } from "@/app/utils/fetchUtil";
import { BASE_API_URL } from "@/config/api";
import { cache } from "react";
import "server-only";
import { CreateCompanyInputs, EditCompanyInputs } from "./actions";

export interface CompanyResult {
id: number;
@@ -21,4 +22,13 @@ export const fetchCompanys = cache(async () => {
return serverFetchJson<CompanyResult[]>(`${BASE_API_URL}/companys`, {
next: { tags: ["companys"] },
});
});

export const fetchCompanyDetails = cache(async (companyId: string) => {
return serverFetchJson<EditCompanyInputs>(
`${BASE_API_URL}/companys/companyDetails/${companyId}`,
{
next: { tags: [`departmentDetail${companyId}`] },
},
);
});

+ 19
- 0
src/app/utils/formatUtil.ts Datei anzeigen

@@ -19,6 +19,8 @@ export const INPUT_DATE_FORMAT = "YYYY-MM-DD";

export const OUTPUT_DATE_FORMAT = "YYYY/MM/DD";

export const OUTPUT_TIME_FORMAT = "HH:mm:ss";

export const convertDateToString = (date: Date, format: string = OUTPUT_DATE_FORMAT) => {
return dayjs(date).format(format)
}
@@ -38,6 +40,23 @@ export const convertDateArrayToString = (dateArray: number[], format: string = O
}
}

export const convertTimeArrayToString = (timeArray: number[], format: string = OUTPUT_TIME_FORMAT, needTime: boolean = false) => {
let timeString = '';

if (timeArray !== null && timeArray !== undefined) {
const hour = timeArray[0] || 0;
const minute = timeArray[1] || 0;
timeString = dayjs()
.set('hour', hour)
.set('minute', minute)
.set('second', 0)
.format('HH:mm:ss');
}
return timeString
}

const shortDateFormatter_en = new Intl.DateTimeFormat("en-HK", {
weekday: "short",
year: "numeric",


+ 28
- 4
src/components/CompanySearch/CompanySearch.tsx Datei anzeigen

@@ -6,7 +6,10 @@ import SearchBox, { Criterion } from "../SearchBox";
import { useTranslation } from "react-i18next";
import SearchResults, { Column } from "../SearchResults";
import EditNote from "@mui/icons-material/EditNote";
import uniq from "lodash/uniq";
import { useRouter } from "next/navigation";
import { deleteDialog, successDialog } from "../Swal/CustomAlerts";
import { deleteCompany } from "@/app/api/companys/actions";
import DeleteIcon from '@mui/icons-material/Delete';

interface Props {
companys: CompanyResult[];
@@ -18,6 +21,8 @@ type SearchParamNames = keyof SearchQuery;
const CompanySearch: React.FC<Props> = ({ companys }) => {
const { t } = useTranslation("companys");

const router = useRouter()

const [filteredCompanys, setFilteredCompanys] = useState(companys);

const searchCriteria: Criterion<SearchParamNames>[] = useMemo(
@@ -34,10 +39,22 @@ const CompanySearch: React.FC<Props> = ({ companys }) => {
setFilteredCompanys(companys);
}, [companys]);

const onProjectClick = useCallback((project: CompanyResult) => {
console.log(project);
const onProjectClick = useCallback((company: CompanyResult) => {
console.log(company);
router.push(`/settings/company/edit?id=${company.id}`);
}, []);

const onDeleteClick = useCallback((company: CompanyResult) => {

deleteDialog(async() => {
await deleteCompany(company.id)
successDialog("Delete Success", t)
setFilteredCompanys((prev) => prev.filter((obj) => obj.id !== company.id))
}, t)
}, []);

const columns = useMemo<Column<CompanyResult>[]>(
() => [
{
@@ -51,7 +68,14 @@ const CompanySearch: React.FC<Props> = ({ companys }) => {
{ name: "brNo", label: t("brNo") },
{ name: "contactName", label: t("Contact Name") },
{ name: "phone", label: t("Contact No.") },
{ name: "email", label: t("Contact Email") }
{ name: "email", label: t("Contact Email") },
{
name: "id",
label: t("Delete"),
onClick: onDeleteClick,
buttonIcon: <DeleteIcon />,
color: "error"
},
],
[t, onProjectClick],
);


+ 91
- 74
src/components/CreateCompany/CompanyDetails.tsx Datei anzeigen

@@ -19,18 +19,39 @@ import { Controller, UseFormRegister, useFormContext } from "react-hook-form";
import { CreateCompanyInputs } from "@/app/api/companys/actions";
import { TimePicker } from "@mui/x-date-pickers";
import dayjs from 'dayjs';
import { useEffect } from "react";
import { convertTimeArrayToString } from "@/app/utils/formatUtil";

const CompanyDetails: React.FC = ({
interface Props{
content : Content;
}

interface Content {
normalHourFrom: number[];
normalHourTo: number[];
otHourFrom: number[];
otHourTo: number[];
}

const CompanyDetails: React.FC<Props> = ({
content,
}) => {
const { t } = useTranslation();
const {
register,
formState: { errors },
control,
setValue,
getValues,
} = useFormContext<CreateCompanyInputs>();

console.log(content)

useEffect(() => {
setValue("normalHourFrom", convertTimeArrayToString(content.normalHourFrom, "HH:mm:ss", false));
setValue("normalHourTo", convertTimeArrayToString(content.normalHourTo, "HH:mm:ss", false));
setValue("otHourFrom", convertTimeArrayToString(content.otHourFrom, "HH:mm:ss", false));
setValue("otHourTo", convertTimeArrayToString(content.otHourTo, "HH:mm:ss", false));
}, [content])

return (
<Card>
@@ -101,84 +122,80 @@ const CompanyDetails: React.FC = ({
/>
</Grid>
<Grid item xs={3}>
<Controller
control={control}
name="normalHourFrom"
rules={{ required: true }}
render={({ field }) => {
return (
<TimePicker
label="Normal Hour From"
inputRef={field.ref}
onChange={(time) => {
const formattedTime = time ? dayjs(time as string).format('HH:mm:ss') : '';
field.onChange(formattedTime);
}}
sx={{ width: '100%' }}
/>
);
}}
/>
<FormControl fullWidth>
<TimePicker
label={t("Normal Hour From")}
value={content.normalHourFrom !== undefined && content.normalHourFrom !== null ?
dayjs().hour(content.normalHourFrom[0]).minute(content.normalHourFrom[1]) :
dayjs().hour(9).minute(0)}
onChange={(time) => {
if (!time) return;
setValue("normalHourFrom", time.format("HH:mm:ss"));
}}
slotProps={{
textField: {
helperText: 'HH:mm:ss',
},
}}
/>
</FormControl>
</Grid>
<Grid item xs={3}>
<Controller
control={control}
name="normalHourTo"
rules={{ required: true }}
render={({ field }) => {
return (
<TimePicker
label="Normal Hour To"
inputRef={field.ref}
onChange={(time) => {
const formattedTime = time ? dayjs(time as string).format('HH:mm:ss') : '';
field.onChange(formattedTime);
}}
sx={{ width: '100%' }}
/>
);
}}
/>
<FormControl fullWidth>
<TimePicker
label={t("Normal Hour To")}
value={content.normalHourTo !== undefined && content.normalHourTo !== null ?
dayjs().hour(content.normalHourTo[0]).minute(content.normalHourTo[1]) :
dayjs().hour(18).minute(0)}
onChange={(time) => {
if (!time) return;
setValue("normalHourTo", time.format("HH:mm:ss"));
}}
slotProps={{
textField: {
helperText: 'HH:mm:ss',
},
}}
/>
</FormControl>
</Grid>
<Grid item xs={3}>
<Controller
control={control}
name="otHourFrom"
rules={{ required: true }}
render={({ field }) => {
return (
<TimePicker
label="OT Hour From"
inputRef={field.ref}
onChange={(time) => {
const formattedTime = time ? dayjs(time as string).format('HH:mm:ss') : '';
field.onChange(formattedTime);
}}
sx={{ width: '100%' }}
/>
);
}}
/>
<FormControl fullWidth>
<TimePicker
label={t("OT Hour From")}
value={content.otHourFrom !== undefined && content.otHourFrom !== null ?
dayjs().hour(content.otHourFrom[0]).minute(content.otHourFrom[1]) :
dayjs().hour(20).minute(0)}
onChange={(time) => {
if (!time) return;
setValue("otHourFrom", time.format("HH:mm:ss"));
}}
slotProps={{
textField: {
helperText: 'HH:mm:ss',
},
}}
/>
</FormControl>
</Grid>
<Grid item xs={3}>
<Controller
control={control}
name="otHourTo"
rules={{ required: true }}
render={({ field }) => {
return (
<TimePicker
label="OT Hour To"
inputRef={field.ref}
onChange={(time) => {
const formattedTime = time ? dayjs(time as string).format('HH:mm:ss') : '';
field.onChange(formattedTime);
}}
sx={{ width: '100%' }}
/>
);
}}
/>
<FormControl fullWidth>
<TimePicker
label={t("OT Hour To")}
value={content.otHourTo !== undefined && content.otHourTo !== null ?
dayjs().hour(content.otHourTo[0]).minute(content.otHourTo[1]) :
dayjs().hour(8).minute(0)}
onChange={(time) => {
if (!time) return;
setValue("otHourTo", time.format("HH:mm:ss"));
}}
slotProps={{
textField: {
helperText: 'HH:mm:ss',
},
}}
/>
</FormControl>
</Grid>
<Grid item xs={6}>
<TextField


+ 33
- 16
src/components/CreateCompany/CreateCompany.tsx Datei anzeigen

@@ -4,9 +4,9 @@ import Check from "@mui/icons-material/Check";
import Close from "@mui/icons-material/Close";
import Button from "@mui/material/Button";
import Stack from "@mui/material/Stack";
import { CreateCompanyInputs, saveCompany } from "@/app/api/companys/actions";
import { CreateCompanyInputs, EditCompanyInputs, saveCompany } from "@/app/api/companys/actions";
import { useRouter } from "next/navigation";
import React, { useCallback, useState } from "react";
import React, { useCallback, useDebugValue, useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import {
FieldErrors,
@@ -19,9 +19,16 @@ import CompanyDetails from "./CompanyDetails";
import { LocalizationProvider } from '@mui/x-date-pickers';
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'
import dayjs from "dayjs";
import { convertTimeArrayToString } from "@/app/utils/formatUtil";

const CreateCompany: React.FC = ({
interface Props {
isEdit: Boolean;
company?: EditCompanyInputs;
}

const CreateCompany: React.FC<Props> = ({
isEdit,
company,
}) => {
const [serverError, setServerError] = useState("");
const { t } = useTranslation();
@@ -55,23 +62,24 @@ const CreateCompany: React.FC = ({

const formProps = useForm<CreateCompanyInputs>({
defaultValues: {
companyCode: "",
companyName: "",
brNo: "",
contactName: "",
phone: "",
id: company?.id,
companyCode: company?.companyCode,
companyName: company?.name,
brNo: company?.brNo,
contactName: company?.contactName,
phone: company?.phone,
otHourTo: "",
otHourFrom: "",
normalHourTo: dayjs().format('HH:mm:ss'),
normalHourFrom: dayjs().format('HH:mm:ss'),
currency: "",
address: "",
district: "",
email: "",
normalHourTo: "",
normalHourFrom: "",
currency: company?.currency,
address: company?.address,
district: company?.district,
email: company?.email,
},
});
const errors = formProps.formState.errors;
const errors = formProps.formState.errors;

return(
<FormProvider {...formProps}>
@@ -82,7 +90,16 @@ const CreateCompany: React.FC = ({
>
{
<LocalizationProvider dateAdapter={AdapterDayjs}>
<CompanyDetails />
<CompanyDetails
content={
{
normalHourFrom: company?.normalHourFrom as number[],
normalHourTo: company?.normalHourTo as number[],
otHourFrom: company?.otHourFrom as number[],
otHourTo: company?.otHourTo as number[]
}
}
/>
</LocalizationProvider>
}



+ 18
- 2
src/components/CreateCompany/CreateCompanyWrapper.tsx Datei anzeigen

@@ -1,8 +1,24 @@
import { fetchCompanyDetails } from "@/app/api/companys";
import CreateCompany from "./CreateCompany";

const CreateCompanyWrapper: React.FC = async () => {
type CreateCompanyProps = {isEdit: false}
interface EditCompanyProps {
isEdit: true;
companyId?: string;
}

type Props = CreateCompanyProps | EditCompanyProps;

const CreateCompanyWrapper: React.FC<Props> = async (props) => {

console.log(props)
const companyDetails = props.isEdit
? await fetchCompanyDetails(props.companyId!)
: undefined;

return (
<CreateCompany
<CreateCompany isEdit company={companyDetails}
/>
)
}


+ 2
- 2
src/components/InvoiceSearch/InvoiceSearch.tsx Datei anzeigen

@@ -208,7 +208,7 @@ const InvoiceSearch: React.FC<Props> = ({ issuedInvoice, receivedInvoice }) => {
{ name: "paymentMilestone", label: t("Payment Milestone") },
{ name: "invoiceDate", label: t("Invocie Date") },
{ name: "dueDate", label: t("Due Date") },
{ name: "issuedAmount", label: t("Amount (HKD") },
{ name: "issuedAmount", label: t("Amount (HKD)") },
],
[t],
);
@@ -316,7 +316,7 @@ const InvoiceSearch: React.FC<Props> = ({ issuedInvoice, receivedInvoice }) => {
}
<Tabs value={tabIndex} onChange={handleTabChange} variant="scrollable">
<Tab label={t("Issued Invoices")}/>
<Tab label={t("Recieved Invoices")}/>
<Tab label={t("Received Invoices")}/>
</Tabs>
{
tabIndex == 0 &&


Laden…
Abbrechen
Speichern