@@ -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" |