2. Add Invoice search pagetags/Baseline_30082024_FRONTEND_UAT
| @@ -0,0 +1,25 @@ | |||||
| import { Metadata } from "next"; | |||||
| import { getServerI18n } from "@/i18n"; | |||||
| import Add from "@mui/icons-material/Add"; | |||||
| import Button from "@mui/material/Button"; | |||||
| import Stack from "@mui/material/Stack"; | |||||
| import Typography from "@mui/material/Typography"; | |||||
| import Link from "next/link"; | |||||
| export const metadata: Metadata = { | |||||
| title: "Create Invoice", | |||||
| }; | |||||
| const Invoice: React.FC = async () => { | |||||
| const { t } = await getServerI18n("Create Invoice"); | |||||
| return ( | |||||
| <> | |||||
| <Typography variant="h4" marginInlineEnd={2}> | |||||
| {t("Create Invoice")} | |||||
| </Typography> | |||||
| </> | |||||
| ) | |||||
| }; | |||||
| export default Invoice; | |||||
| @@ -1,11 +1,40 @@ | |||||
| import { Metadata } from "next"; | import { Metadata } from "next"; | ||||
| import { getServerI18n } from "@/i18n"; | |||||
| import Add from "@mui/icons-material/Add"; | |||||
| import Button from "@mui/material/Button"; | |||||
| import Stack from "@mui/material/Stack"; | |||||
| import Typography from "@mui/material/Typography"; | |||||
| import Link from "next/link"; | |||||
| export const metadata: Metadata = { | export const metadata: Metadata = { | ||||
| title: "Invoice", | title: "Invoice", | ||||
| }; | }; | ||||
| const Invoice: React.FC = async () => { | const Invoice: React.FC = async () => { | ||||
| return "Invoice"; | |||||
| const { t } = await getServerI18n("Invoice"); | |||||
| return ( | |||||
| <> | |||||
| <Stack | |||||
| direction="row" | |||||
| justifyContent="space-between" | |||||
| flexWrap="wrap" | |||||
| rowGap={2} | |||||
| > | |||||
| <Typography variant="h4" marginInlineEnd={2}> | |||||
| {t("Invoice")} | |||||
| </Typography> | |||||
| <Button | |||||
| variant="contained" | |||||
| startIcon={<Add />} | |||||
| LinkComponent={Link} | |||||
| href="/invoice/new" | |||||
| > | |||||
| {t("Create Invoice")} | |||||
| </Button> | |||||
| </Stack> | |||||
| </> | |||||
| ) | |||||
| }; | }; | ||||
| export default Invoice; | export default Invoice; | ||||
| @@ -1,20 +1,22 @@ | |||||
| import { fetchProjectCategories } from "@/app/api/projects"; | |||||
| import { preloadStaff } from "@/app/api/staff"; | |||||
| import { fetchAllTasks, fetchTaskTemplates } from "@/app/api/tasks"; | |||||
| import CreateProject from "@/components/CreateProject"; | |||||
| import CreateCompany from "@/components/CreateCompany"; | |||||
| import { I18nProvider, getServerI18n } from "@/i18n"; | import { I18nProvider, getServerI18n } from "@/i18n"; | ||||
| import Typography from "@mui/material/Typography"; | import Typography from "@mui/material/Typography"; | ||||
| import { Metadata } from "next"; | import { Metadata } from "next"; | ||||
| export const metadata: Metadata = { | export const metadata: Metadata = { | ||||
| title: "Create Project", | |||||
| title: "Create Comapny", | |||||
| }; | }; | ||||
| const Companys: React.FC = async () => { | const Companys: React.FC = async () => { | ||||
| const { t } = await getServerI18n("projects"); | |||||
| const { t } = await getServerI18n("companys"); | |||||
| return( | return( | ||||
| <>AAAA</> | |||||
| <> | |||||
| <Typography variant="h4">{t("Create Company")}</Typography> | |||||
| <I18nProvider namespaces={["companys"]}> | |||||
| <CreateCompany /> | |||||
| </I18nProvider> | |||||
| </> | |||||
| ) | ) | ||||
| } | } | ||||
| @@ -1,4 +1,5 @@ | |||||
| "use server"; | "use server"; | ||||
| import { serverFetchJson } from "@/app/utils/fetchUtil"; | import { serverFetchJson } from "@/app/utils/fetchUtil"; | ||||
| import { BASE_API_URL } from "@/config/api"; | import { BASE_API_URL } from "@/config/api"; | ||||
| import { cache } from "react"; | import { cache } from "react"; | ||||
| @@ -12,6 +13,30 @@ export interface combo { | |||||
| records: comboProp[]; | records: comboProp[]; | ||||
| } | } | ||||
| export interface CreateCompanyInputs { | |||||
| companyCode: String; | |||||
| companyName: String; | |||||
| brNo: String; | |||||
| contactName: String; | |||||
| phone: String; | |||||
| otHourTo: String; | |||||
| otHourFrom: String; | |||||
| normalHourTo: String; | |||||
| normalHourFrom: String; | |||||
| currency: String; | |||||
| address: String; | |||||
| distract: String; | |||||
| email: String; | |||||
| } | |||||
| export const saveCompany = async (data: CreateCompanyInputs) => { | |||||
| return serverFetchJson(`${BASE_API_URL}/companys/new`, { | |||||
| method: "POST", | |||||
| body: JSON.stringify(data), | |||||
| headers: { "Content-Type": "application/json" }, | |||||
| }); | |||||
| }; | |||||
| export const fetchCompanyCombo = cache(async () => { | export const fetchCompanyCombo = cache(async () => { | ||||
| return serverFetchJson<combo>(`${BASE_API_URL}/companys/combo`, { | return serverFetchJson<combo>(`${BASE_API_URL}/companys/combo`, { | ||||
| next: { tags: ["company"] }, | next: { tags: ["company"] }, | ||||
| @@ -0,0 +1,110 @@ | |||||
| "use client"; | |||||
| import Stack from "@mui/material/Stack"; | |||||
| import Box from "@mui/material/Box"; | |||||
| import Card from "@mui/material/Card"; | |||||
| import CardContent from "@mui/material/CardContent"; | |||||
| import FormControl from "@mui/material/FormControl"; | |||||
| import Grid from "@mui/material/Grid"; | |||||
| import InputLabel from "@mui/material/InputLabel"; | |||||
| import MenuItem from "@mui/material/MenuItem"; | |||||
| import Select from "@mui/material/Select"; | |||||
| import TextField from "@mui/material/TextField"; | |||||
| import Typography from "@mui/material/Typography"; | |||||
| import { useTranslation } from "react-i18next"; | |||||
| import CardActions from "@mui/material/CardActions"; | |||||
| import RestartAlt from "@mui/icons-material/RestartAlt"; | |||||
| import Button from "@mui/material/Button"; | |||||
| import { Controller, useFormContext } from "react-hook-form"; | |||||
| import { CreateCompanyInputs } from "@/app/api/companys/actions"; | |||||
| const CompanyDetails: React.FC = ({ | |||||
| }) => { | |||||
| const { t } = useTranslation(); | |||||
| const { | |||||
| register, | |||||
| formState: { errors }, | |||||
| control, | |||||
| } = useFormContext<CreateCompanyInputs>(); | |||||
| return ( | |||||
| <Card> | |||||
| <CardContent component={Stack} spacing={4}> | |||||
| <Box> | |||||
| <Typography variant="overline" display="block" marginBlockEnd={1}> | |||||
| {t("Company Details")} | |||||
| </Typography> | |||||
| <Grid container spacing={2} columns={{ xs: 6, sm: 12 }}> | |||||
| <Grid item xs={6}> | |||||
| <TextField | |||||
| label={t("Company Code")} | |||||
| fullWidth | |||||
| {...register("companyCode", { | |||||
| required: "Company code required!", | |||||
| })} | |||||
| error={Boolean(errors.companyCode)} | |||||
| /> | |||||
| </Grid> | |||||
| <Grid item xs={6}> | |||||
| <TextField | |||||
| label={t("Company Name")} | |||||
| fullWidth | |||||
| {...register("companyName", { | |||||
| required: "Company name required!", | |||||
| })} | |||||
| error={Boolean(errors.companyName)} | |||||
| /> | |||||
| </Grid> | |||||
| <Grid item xs={6}> | |||||
| <TextField | |||||
| label={t("Company brNo")} | |||||
| fullWidth | |||||
| {...register("brNo", { | |||||
| required: "Please enter a brNo", | |||||
| })} | |||||
| error={Boolean(errors.brNo)} | |||||
| /> | |||||
| </Grid> | |||||
| <Grid item xs={6}> | |||||
| <TextField | |||||
| label={t("Company contact name")} | |||||
| fullWidth | |||||
| {...register("contactName", { | |||||
| required: "Please enter a contact name", | |||||
| })} | |||||
| error={Boolean(errors.contactName)} | |||||
| /> | |||||
| </Grid> | |||||
| <Grid item xs={6}> | |||||
| <TextField | |||||
| label={t("Company contact number")} | |||||
| fullWidth | |||||
| {...register("phone", { | |||||
| required: "Please enter a contact number", | |||||
| })} | |||||
| error={Boolean(errors.phone)} | |||||
| /> | |||||
| </Grid> | |||||
| <Grid item xs={6}> | |||||
| <TextField | |||||
| label={t("Company contact email")} | |||||
| fullWidth | |||||
| {...register("email", { | |||||
| required: "Please enter a contact email", | |||||
| })} | |||||
| error={Boolean(errors.email)} | |||||
| /> | |||||
| </Grid> | |||||
| </Grid> | |||||
| </Box> | |||||
| {/* <CardActions sx={{ justifyContent: "flex-end" }}> | |||||
| <Button variant="text" startIcon={<RestartAlt />}> | |||||
| {t("Reset")} | |||||
| </Button> | |||||
| </CardActions> */} | |||||
| </CardContent> | |||||
| </Card> | |||||
| ); | |||||
| }; | |||||
| export default CompanyDetails; | |||||
| @@ -0,0 +1,101 @@ | |||||
| "use client"; | |||||
| 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 { useRouter } from "next/navigation"; | |||||
| import React, { useCallback, useState } from "react"; | |||||
| import { useTranslation } from "react-i18next"; | |||||
| import { | |||||
| FieldErrors, | |||||
| FormProvider, | |||||
| SubmitErrorHandler, | |||||
| SubmitHandler, | |||||
| useForm, | |||||
| } from "react-hook-form"; | |||||
| import CompanyDetails from "./CompanyDetails"; | |||||
| const CreateCompany: React.FC = ({ | |||||
| }) => { | |||||
| const [serverError, setServerError] = useState(""); | |||||
| const { t } = useTranslation(); | |||||
| const router = useRouter(); | |||||
| const handleCancel = () => { | |||||
| router.back(); | |||||
| }; | |||||
| const onSubmit = useCallback<SubmitHandler<CreateCompanyInputs>>( | |||||
| async (data) => { | |||||
| try { | |||||
| console.log(data); | |||||
| setServerError(""); | |||||
| // console.log(JSON.stringify(data)); | |||||
| await saveCompany(data) | |||||
| router.replace("/settings/companys"); | |||||
| } catch (e) { | |||||
| setServerError(t("An error has occurred. Please try again later.")); | |||||
| } | |||||
| }, | |||||
| [router, t], | |||||
| ); | |||||
| const onSubmitError = useCallback<SubmitErrorHandler<CreateCompanyInputs>>( | |||||
| (errors) => { | |||||
| console.log(errors) | |||||
| }, | |||||
| [], | |||||
| ); | |||||
| const formProps = useForm<CreateCompanyInputs>({ | |||||
| defaultValues: { | |||||
| companyCode: "", | |||||
| companyName: "", | |||||
| brNo: "", | |||||
| contactName: "", | |||||
| phone: "", | |||||
| otHourTo: "", | |||||
| otHourFrom: "", | |||||
| normalHourTo: "", | |||||
| normalHourFrom: "", | |||||
| currency: "", | |||||
| address: "", | |||||
| distract: "", | |||||
| email: "", | |||||
| }, | |||||
| }); | |||||
| const errors = formProps.formState.errors; | |||||
| return( | |||||
| <FormProvider {...formProps}> | |||||
| <Stack | |||||
| spacing={2} | |||||
| component="form" | |||||
| onSubmit={formProps.handleSubmit(onSubmit, onSubmitError)} | |||||
| > | |||||
| { | |||||
| <CompanyDetails /> | |||||
| } | |||||
| <Stack direction="row" justifyContent="flex-end" gap={1}> | |||||
| <Button | |||||
| variant="outlined" | |||||
| startIcon={<Close />} | |||||
| onClick={handleCancel} | |||||
| > | |||||
| {t("Cancel")} | |||||
| </Button> | |||||
| <Button variant="contained" startIcon={<Check />} type="submit"> | |||||
| {t("Confirm")} | |||||
| </Button> | |||||
| </Stack> | |||||
| </Stack> | |||||
| </FormProvider> | |||||
| ) | |||||
| } | |||||
| export default CreateCompany; | |||||
| @@ -0,0 +1,10 @@ | |||||
| import CreateCompany from "./CreateCompany"; | |||||
| const CreateCompanyWrapper: React.FC = async () => { | |||||
| return ( | |||||
| <CreateCompany | |||||
| /> | |||||
| ) | |||||
| } | |||||
| export default CreateCompanyWrapper; | |||||
| @@ -0,0 +1 @@ | |||||
| export { default } from "./CreateCompanyWrapper" | |||||