| @@ -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 { 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: "Invoice", | |||
| }; | |||
| 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; | |||
| @@ -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 Typography from "@mui/material/Typography"; | |||
| import { Metadata } from "next"; | |||
| export const metadata: Metadata = { | |||
| title: "Create Project", | |||
| title: "Create Comapny", | |||
| }; | |||
| const Companys: React.FC = async () => { | |||
| const { t } = await getServerI18n("projects"); | |||
| const { t } = await getServerI18n("companys"); | |||
| return( | |||
| <>AAAA</> | |||
| <> | |||
| <Typography variant="h4">{t("Create Company")}</Typography> | |||
| <I18nProvider namespaces={["companys"]}> | |||
| <CreateCompany /> | |||
| </I18nProvider> | |||
| </> | |||
| ) | |||
| } | |||
| @@ -1,4 +1,5 @@ | |||
| "use server"; | |||
| import { serverFetchJson } from "@/app/utils/fetchUtil"; | |||
| import { BASE_API_URL } from "@/config/api"; | |||
| import { cache } from "react"; | |||
| @@ -12,6 +13,30 @@ export interface combo { | |||
| 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 () => { | |||
| return serverFetchJson<combo>(`${BASE_API_URL}/companys/combo`, { | |||
| 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" | |||