@@ -17,7 +17,7 @@ const Projects: React.FC = async () => { | |||
return ( | |||
<> | |||
<Typography variant="h4">{t("Create Customer")}</Typography> | |||
<I18nProvider namespaces={["customer"]}> | |||
<I18nProvider namespaces={["customer", "common"]}> | |||
<CreateCustomer /> | |||
</I18nProvider> | |||
</> |
@@ -33,7 +33,7 @@ const Customer: React.FC = async () => { | |||
variant="contained" | |||
startIcon={<Add />} | |||
LinkComponent={Link} | |||
href="/customer/create" | |||
href="/settings/customer/create" | |||
> | |||
{t("Create Customer")} | |||
</Button> |
@@ -2,12 +2,14 @@ | |||
import { serverFetchJson } from "@/app/utils/fetchUtil"; | |||
import { BASE_API_URL } from "@/config/api"; | |||
import { Customer } from "."; | |||
import { Contact, NewCustomerResponse } from "."; | |||
import { revalidateTag } from "next/cache"; | |||
export interface CustomerFormInputs { | |||
// Customer details | |||
id: number | null; | |||
name: string; | |||
code: string; | |||
address: string | null; | |||
@@ -16,14 +18,22 @@ export interface CustomerFormInputs { | |||
phone: string | null; | |||
contactName: string | null; | |||
brNo: string | null; | |||
typeId: number; | |||
// Subsidiary | |||
addSubsidiaryIds: number[]; | |||
deleteSubsidiaryIds: number[]; | |||
// Contact | |||
addContacts: Contact[]; | |||
deleteContactIds: number[]; | |||
// is grid editing | |||
isGridEditing: boolean | null; | |||
} | |||
export const saveCustomer = async (data: CustomerFormInputs) => { | |||
const saveCustomer = await serverFetchJson<Customer>( | |||
const saveCustomer = await serverFetchJson<NewCustomerResponse>( | |||
`${BASE_API_URL}/customer/save`, | |||
{ | |||
method: "POST", | |||
@@ -9,6 +9,16 @@ export interface Customer { | |||
name: string; | |||
} | |||
export interface NewCustomerResponse { | |||
customer: Customer; | |||
message: string; | |||
} | |||
export interface CustomerType { | |||
id: number; | |||
name: string; | |||
} | |||
export interface Subsidiary { | |||
id: number; | |||
code: string; | |||
@@ -22,6 +32,14 @@ export interface Subsidiary { | |||
email: string | null; | |||
} | |||
export interface Contact { | |||
id: number; | |||
name: string; | |||
phone: string; | |||
email: string; | |||
isNew: boolean; | |||
} | |||
export const preloadAllCustomers = () => { | |||
fetchAllCustomers(); | |||
}; | |||
@@ -38,3 +56,12 @@ export const fetchSubsidiaries = cache(async () => { | |||
}, | |||
); | |||
}); | |||
export const fetchCustomerTypes = cache(async () => { | |||
return serverFetchJson<CustomerType[]>( | |||
`${BASE_API_URL}/customer/types`, | |||
{ | |||
next: { tags: ["CustomerTypes"] }, | |||
}, | |||
); | |||
}); |
@@ -14,8 +14,8 @@ const pathToLabelMap: { [path: string]: string } = { | |||
"/projects/create": "Create Project", | |||
"/tasks": "Task Template", | |||
"/tasks/create": "Create Task Template", | |||
"/customer": "Customer", | |||
"/customer/create": "Create Customer", | |||
"/settings/customer": "Customer", | |||
"/settings/customer/create": "Create Customer", | |||
"/settings": "Settings", | |||
"/company": "Company", | |||
"/settings/department": "Department", | |||
@@ -0,0 +1,288 @@ | |||
"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 Grid from "@mui/material/Grid"; | |||
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 AddIcon from '@mui/icons-material/Add'; | |||
import EditIcon from '@mui/icons-material/Edit'; | |||
import DeleteIcon from '@mui/icons-material/DeleteOutlined'; | |||
import SaveIcon from '@mui/icons-material/Save'; | |||
import CancelIcon from '@mui/icons-material/Close'; | |||
import { | |||
GridRowsProp, | |||
GridRowModesModel, | |||
GridRowModes, | |||
DataGrid, | |||
GridColDef, | |||
GridToolbarContainer, | |||
GridActionsCellItem, | |||
GridEventListener, | |||
GridRowId, | |||
GridRowModel, | |||
GridRowEditStopReasons, | |||
GridPreProcessEditCellProps, | |||
GridCellParams, | |||
} from '@mui/x-data-grid'; | |||
import CustomDatagrid from "../CustomDatagrid/CustomDatagrid"; | |||
import { Contact } from "@/app/api/customer"; | |||
import { useFieldArray, useFormContext } from "react-hook-form"; | |||
import { useCallback, useEffect, useMemo, useState } from "react"; | |||
interface Props { | |||
} | |||
interface EditToolbarProps { | |||
setRows: (newRows: (oldRows: GridRowsProp) => GridRowsProp) => void; | |||
setRowModesModel: ( | |||
newModel: (oldModel: GridRowModesModel) => GridRowModesModel, | |||
) => void; | |||
} | |||
var rowId = -1 | |||
function EditToolbar(props: EditToolbarProps) { | |||
const { setRows, setRowModesModel } = props; | |||
const { t } = useTranslation(); | |||
const handleClick = () => { | |||
const id = rowId; | |||
rowId = rowId - 1; | |||
setRows((oldRows) => [{ id, name: '', phone: '', email: '', isNew: true }, ...oldRows]); | |||
setRowModesModel((oldModel) => ({ | |||
...oldModel, | |||
[id]: { mode: GridRowModes.Edit, fieldToFocus: 'name' }, | |||
})); | |||
}; | |||
return ( | |||
<GridToolbarContainer> | |||
<Button color="primary" startIcon={<AddIcon />} onClick={handleClick}> | |||
{t("Add Contact Person")} | |||
</Button> | |||
</GridToolbarContainer> | |||
); | |||
} | |||
const ContactDetails: React.FC<Props> = ({ | |||
}) => { | |||
const { t } = useTranslation(); | |||
const { control, setValue, getValues, formState: { errors }, setError, clearErrors } = useFormContext(); | |||
const { fields } = useFieldArray({ | |||
control, | |||
name: "addContacts" | |||
}) | |||
const initialRows: GridRowsProp = fields.map((item, index) => { | |||
return ({ | |||
id: Number(getValues(`addContacts[${index}].id`)), | |||
name: getValues(`addContacts[${index}].name`), | |||
phone: getValues(`addContacts[${index}].phone`), | |||
email: getValues(`addContacts[${index}].email`), | |||
isNew: false, | |||
}) | |||
}) | |||
const [rows, setRows] = useState(initialRows); | |||
const [rowModesModel, setRowModesModel] = useState<GridRowModesModel>({}); | |||
const handleRowEditStop: GridEventListener<'rowEditStop'> = (params, event) => { | |||
if (params.reason === GridRowEditStopReasons.rowFocusOut) { | |||
event.defaultMuiPrevented = true; | |||
} | |||
}; | |||
const handleEditClick = (id: GridRowId) => () => { | |||
setRowModesModel({ ...rowModesModel, [id]: { mode: GridRowModes.Edit } }); | |||
}; | |||
const handleSaveClick = (id: GridRowId) => () => { | |||
setRowModesModel({ ...rowModesModel, [id]: { mode: GridRowModes.View } }); | |||
}; | |||
const handleDeleteClick = (id: GridRowId) => () => { | |||
const updatedRows = rows.filter((row) => row.id !== id) | |||
setRows(updatedRows); | |||
setValue("addContacts", updatedRows) | |||
}; | |||
const handleCancelClick = (id: GridRowId) => () => { | |||
setRowModesModel({ | |||
...rowModesModel, | |||
[id]: { mode: GridRowModes.View, ignoreModifications: true }, | |||
}); | |||
const editedRow = rows.find((row) => row.id === id); | |||
if (editedRow!.isNew) { | |||
setRows(rows.filter((row) => row.id !== id)); | |||
} | |||
}; | |||
const processRowUpdate = useCallback((newRow: GridRowModel) => { | |||
const updatedRow = { ...newRow }; | |||
const updatedRows = rows.map((row) => (row.id === newRow.id ? updatedRow : row)) | |||
setRows(updatedRows); | |||
setValue("addContacts", updatedRows) | |||
return updatedRow; | |||
}, [rows]); | |||
const handleRowModesModelChange = useCallback((newRowModesModel: GridRowModesModel) => { | |||
setRowModesModel(newRowModesModel); | |||
}, [rows]); | |||
const columns = useMemo<GridColDef[]>( | |||
() => [ | |||
{ | |||
field: 'name', | |||
headerName: t('Contact Name'), | |||
editable: true, | |||
flex: 1, | |||
}, | |||
{ | |||
field: 'phone', | |||
headerName: t('Contact Phone'), | |||
editable: true, | |||
flex: 1, | |||
}, | |||
{ | |||
field: 'email', | |||
headerName: t('Contact Email'), | |||
editable: true, | |||
flex: 1, | |||
}, | |||
{ | |||
field: 'actions', | |||
type: 'actions', | |||
headerName: '', | |||
flex: 0.6, | |||
// width: 100, | |||
cellClassName: 'actions', | |||
getActions: ({ id, ...params }) => { | |||
const isInEditMode = rowModesModel[id]?.mode === GridRowModes.Edit; | |||
if (isInEditMode) { | |||
return [ | |||
<GridActionsCellItem | |||
icon={<SaveIcon />} | |||
label="Save" | |||
sx={{ | |||
color: 'primary.main', | |||
}} | |||
onClick={handleSaveClick(id)} | |||
/>, | |||
<GridActionsCellItem | |||
icon={<CancelIcon />} | |||
label="Cancel" | |||
className="textPrimary" | |||
onClick={handleCancelClick(id)} | |||
color="inherit" | |||
/>, | |||
]; | |||
} | |||
return [ | |||
<GridActionsCellItem | |||
icon={<EditIcon />} | |||
label="Edit" | |||
className="textPrimary" | |||
onClick={handleEditClick(id)} | |||
color="inherit" | |||
/>, | |||
<GridActionsCellItem | |||
icon={<DeleteIcon />} | |||
label="Delete" | |||
onClick={handleDeleteClick(id)} | |||
color="inherit" | |||
/>, | |||
]; | |||
}, | |||
}, | |||
], | |||
[rows, rowModesModel, t], | |||
); | |||
// check error | |||
useEffect(() => { | |||
if (getValues("addContacts").length === 0) { | |||
clearErrors("addContacts") | |||
} else { | |||
const errorRows = rows.filter(row => String(row.name).trim().length === 0 || String(row.phone).trim().length === 0 || String(row.email).trim().length === 0) | |||
if (errorRows.length > 0) { | |||
setError("addContacts", { message: "Contact details include empty fields", type: "required" }) | |||
} else { | |||
const errorRows_EmailFormat = rows.filter(row => !/^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}$/.test(String(row.email))) | |||
if (errorRows_EmailFormat.length > 0) { | |||
setError("addContacts", { message: "Contact details include empty fields", type: "email_format" }) | |||
} else { | |||
clearErrors("addContacts") | |||
} | |||
} | |||
} | |||
}, [rows]) | |||
// check editing | |||
useEffect(() => { | |||
const filteredByKey = Object.fromEntries( | |||
Object.entries(rowModesModel).filter(([key, value]) => rowModesModel[key].mode === 'edit')) | |||
if (Object.keys(filteredByKey).length > 0) { | |||
setValue("isGridEditing", true) | |||
} else { | |||
setValue("isGridEditing", false) | |||
} | |||
}, [rowModesModel]) | |||
return ( | |||
<Card sx={{ display: "block" }}> | |||
<CardContent sx={{ display: "flex", flexDirection: "column", gap: 1 }}> | |||
<Stack gap={2}> | |||
{/* <div> */} | |||
<Typography variant="overline" display='inline-block' noWrap> | |||
{t("Contact Details")} | |||
</Typography> | |||
{Boolean(errors.addContacts?.type === "required") && <Typography sx={(theme) => ({ color: theme.palette.error.main })} variant="overline" display='inline-block' noWrap> | |||
{t("Please ensure all the fields are inputted and saved")} | |||
</Typography>} | |||
{Boolean(errors.addContacts?.type === "email_format") && <Typography sx={(theme) => ({ color: theme.palette.error.main })} variant="overline" display='inline-block' noWrap> | |||
{t("Please ensure all the email formats are correct")} | |||
</Typography>} | |||
{/* </div> */} | |||
<CustomDatagrid | |||
rows={[...rows]} | |||
columns={columns} | |||
editMode="row" | |||
rowModesModel={rowModesModel} | |||
onRowEditStop={handleRowEditStop} | |||
processRowUpdate={processRowUpdate} | |||
// onProcessRowUpdateError={handleProcessRowUpdateError} | |||
onRowModesModelChange={handleRowModesModelChange} | |||
slots={{ | |||
toolbar: EditToolbar, | |||
}} | |||
slotProps={{ | |||
toolbar: { setRows, setRowModesModel }, | |||
}} | |||
sx={{ | |||
height: '100%' | |||
}} | |||
/> | |||
<CardActions sx={{ justifyContent: "flex-end" }}> | |||
<Button variant="text" startIcon={<RestartAlt />}> | |||
{t("Reset")} | |||
</Button> | |||
</CardActions> | |||
</Stack> | |||
</CardContent> | |||
</Card> | |||
); | |||
}; | |||
export default ContactDetails; |
@@ -9,7 +9,6 @@ import Tabs, { TabsProps } from "@mui/material/Tabs"; | |||
import { useRouter } from "next/navigation"; | |||
import React, { useCallback, useState } from "react"; | |||
import { useTranslation } from "react-i18next"; | |||
import { Task, TaskTemplate } from "@/app/api/tasks"; | |||
import { | |||
FieldErrors, | |||
FormProvider, | |||
@@ -17,18 +16,18 @@ import { | |||
SubmitHandler, | |||
useForm, | |||
} from "react-hook-form"; | |||
import { CreateProjectInputs, saveProject } from "@/app/api/projects/actions"; | |||
import { Error } from "@mui/icons-material"; | |||
import { ProjectCategory } from "@/app/api/projects"; | |||
import { Typography } from "@mui/material"; | |||
import { CustomerFormInputs, saveCustomer } from "@/app/api/customer/actions"; | |||
import CustomerDetails from "./CustomerDetails"; | |||
import SubsidiaryAllocation from "./SubsidiaryAllocation"; | |||
import { Subsidiary } from "@/app/api/customer"; | |||
import { CustomerType, Subsidiary } from "@/app/api/customer"; | |||
import { getDeletedRecordWithRefList } from "@/app/utils/commonUtil"; | |||
import { errorDialog, submitDialog, successDialog, warningDialog } from "../Swal/CustomAlerts"; | |||
export interface Props { | |||
subsidiaries: Subsidiary[], | |||
customerTypes: CustomerType[], | |||
} | |||
const hasErrorsInTab = ( | |||
@@ -37,7 +36,7 @@ const hasErrorsInTab = ( | |||
) => { | |||
switch (tabIndex) { | |||
case 0: | |||
return errors.name; | |||
return Object.keys(errors).length > 0; | |||
default: | |||
false; | |||
} | |||
@@ -45,6 +44,7 @@ const hasErrorsInTab = ( | |||
const CreateCustomer: React.FC<Props> = ({ | |||
subsidiaries, | |||
customerTypes, | |||
}) => { | |||
const [serverError, setServerError] = useState(""); | |||
const [tabIndex, setTabIndex] = useState(0); | |||
@@ -54,6 +54,10 @@ const CreateCustomer: React.FC<Props> = ({ | |||
defaultValues: { | |||
code: "", | |||
name: "", | |||
addContacts: [], | |||
addSubsidiaryIds: [], | |||
deleteSubsidiaryIds: [], | |||
deleteContactIds: [] | |||
}, | |||
}); | |||
@@ -71,27 +75,42 @@ const CreateCustomer: React.FC<Props> = ({ | |||
const onSubmit = useCallback<SubmitHandler<CustomerFormInputs>>( | |||
async (data) => { | |||
try { | |||
if (data.isGridEditing) { | |||
warningDialog(t("Please save all the rows before submitting"), t) | |||
return false | |||
} | |||
console.log(data); | |||
let haveError = false | |||
if (data.name.length === 0) { | |||
haveError = true | |||
formProps.setError("name", {message: "Name is empty", type: "required"}) | |||
formProps.setError("name", { message: "Name is empty", type: "required" }) | |||
} | |||
if (data.code.length === 0) { | |||
haveError = true | |||
formProps.setError("code", {message: "Code is empty", type: "required"}) | |||
formProps.setError("code", { message: "Code is empty", type: "required" }) | |||
} | |||
if (data.email && data.email?.length > 0 && !/^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}$/.test(data.email)) { | |||
haveError = true | |||
formProps.setError("email", {message: "Email format is not valid", type: "custom"}) | |||
formProps.setError("email", { message: "Email format is not valid", type: "custom" }) | |||
} | |||
if (data.brNo && data.brNo?.length > 0 && !/[0-9]{8}/.test(data.brNo)) { | |||
haveError = true | |||
formProps.setError("brNo", {message: "Br No. format is not valid", type: "custom"}) | |||
formProps.setError("brNo", { message: "Br No. format is not valid", type: "custom" }) | |||
} | |||
if (data.addContacts.length === 0 || data.addContacts.filter(row => String(row.name).trim().length === 0 || String(row.phone).trim().length === 0 || String(row.email).trim().length === 0).length > 0) { | |||
haveError = true | |||
formProps.setError("addContacts", { message: "Contact details include empty fields", type: "required" }) | |||
} | |||
if (data.addContacts.length > 0 && data.addContacts.filter(row => !/^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}$/.test(String(row.email))).length > 0) { | |||
haveError = true | |||
formProps.setError("addContacts", { message: "Contact details include empty fields", type: "email_format" }) | |||
} | |||
if (haveError) { | |||
@@ -100,11 +119,28 @@ const CreateCustomer: React.FC<Props> = ({ | |||
return false | |||
} | |||
data.deleteSubsidiaryIds = [] | |||
// data.deleteSubsidiaryIds = data.deleteSubsidiaryIds ?? [] | |||
// data.addSubsidiaryIds = data.addSubsidiaryIds ?? [] | |||
// data.deleteContactIds = data.deleteContactIds ?? [] | |||
setServerError(""); | |||
await saveCustomer(data); | |||
router.replace("/customer"); | |||
submitDialog(async () => { | |||
const response = await saveCustomer(data); | |||
if (response.message === "Success") { | |||
successDialog(t("Submit Success"), t).then(() => { | |||
router.replace("/settings/customer"); | |||
}) | |||
} else { | |||
errorDialog(t("Submit Fail"), t).then(() => { | |||
formProps.setError("code", { message: response.message, type: "custom" }) | |||
setTabIndex(0) | |||
return false | |||
}) | |||
} | |||
}, t) | |||
} catch (e) { | |||
console.log(e) | |||
setServerError(t("An error has occurred. Please try again later.")); | |||
} | |||
}, | |||
@@ -114,7 +150,7 @@ const CreateCustomer: React.FC<Props> = ({ | |||
const onSubmitError = useCallback<SubmitErrorHandler<CustomerFormInputs>>( | |||
(errors) => { | |||
// Set the tab so that the focus will go there | |||
if (errors.name || errors.code) { | |||
if (Object.keys(errors).length > 0) { | |||
setTabIndex(0); | |||
} | |||
}, | |||
@@ -147,8 +183,8 @@ const CreateCustomer: React.FC<Props> = ({ | |||
{serverError} | |||
</Typography> | |||
)} | |||
{tabIndex === 0 && <CustomerDetails/>} | |||
{tabIndex === 1 && <SubsidiaryAllocation subsidiaries={subsidiaries}/>} | |||
{tabIndex === 0 && <CustomerDetails customerTypes={customerTypes} />} | |||
{tabIndex === 1 && <SubsidiaryAllocation subsidiaries={subsidiaries} />} | |||
<Stack direction="row" justifyContent="flex-end" gap={1}> | |||
<Button | |||
@@ -2,17 +2,18 @@ | |||
// import CreateProject from "./CreateProject"; | |||
// import { fetchProjectCategories } from "@/app/api/projects"; | |||
// import { fetchTeamLeads } from "@/app/api/staff"; | |||
import { fetchSubsidiaries } from "@/app/api/customer"; | |||
import { fetchCustomerTypes, fetchSubsidiaries } from "@/app/api/customer"; | |||
import CreateCustomer from "./CreateCustomer"; | |||
const CreateCustomerWrapper: React.FC = async () => { | |||
const [subsidiaries] = | |||
const [subsidiaries, customerTypes] = | |||
await Promise.all([ | |||
fetchSubsidiaries(), | |||
fetchCustomerTypes(), | |||
]); | |||
return ( | |||
<CreateCustomer subsidiaries={subsidiaries}/> | |||
<CreateCustomer subsidiaries={subsidiaries} customerTypes={customerTypes}/> | |||
); | |||
}; | |||
@@ -11,22 +11,30 @@ 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 { useFormContext } from "react-hook-form"; | |||
import { Controller, useFormContext } from "react-hook-form"; | |||
import { CustomerFormInputs } from "@/app/api/customer/actions"; | |||
import { CustomerType } from "@/app/api/customer"; | |||
import { FormControl, InputLabel, MenuItem, Select } from "@mui/material"; | |||
import ContactDetails from "./ContactDetails"; | |||
interface Props { | |||
customerTypes: CustomerType[], | |||
} | |||
const CustomerDetails: React.FC<Props> = ({ | |||
customerTypes, | |||
}) => { | |||
const { t } = useTranslation(); | |||
const { | |||
register, | |||
formState: { errors }, | |||
control, | |||
reset | |||
} = useFormContext<CustomerFormInputs>(); | |||
return ( | |||
<Card sx={{ display: "block"}}> | |||
<> | |||
<Card sx={{ display: "block" }}> | |||
<CardContent component={Stack} spacing={4}> | |||
<Box> | |||
<Typography variant="overline" display="block" marginBlockEnd={1}> | |||
@@ -41,7 +49,7 @@ const CustomerDetails: React.FC<Props> = ({ | |||
required: true, | |||
})} | |||
error={Boolean(errors.code)} | |||
helperText={Boolean(errors.code) && t("Please input correct customer code")} | |||
helperText={Boolean(errors.code) && (errors.code?.message ? t(errors.code.message) : t("Please input correct customer code"))} | |||
/> | |||
</Grid> | |||
<Grid item xs={6}> | |||
@@ -69,7 +77,7 @@ const CustomerDetails: React.FC<Props> = ({ | |||
{...register("district")} | |||
/> | |||
</Grid> | |||
<Grid item xs={6}> | |||
{/* <Grid item xs={6}> | |||
<TextField | |||
label={t("Customer Email")} | |||
fullWidth | |||
@@ -93,6 +101,28 @@ const CustomerDetails: React.FC<Props> = ({ | |||
fullWidth | |||
{...register("contactName")} | |||
/> | |||
</Grid> */} | |||
<Grid item xs={6}> | |||
<FormControl fullWidth> | |||
<InputLabel>{t("Customer Type")}</InputLabel> | |||
<Controller | |||
defaultValue={customerTypes[0].id} | |||
control={control} | |||
name="typeId" | |||
render={({ field }) => ( | |||
<Select label={t("Project Category")} {...field}> | |||
{customerTypes.map((type, index) => ( | |||
<MenuItem | |||
key={`${type.id}-${index}`} | |||
value={type.id} | |||
> | |||
{t(type.name)} | |||
</MenuItem> | |||
))} | |||
</Select> | |||
)} | |||
/> | |||
</FormControl> | |||
</Grid> | |||
<Grid item xs={6}> | |||
<TextField | |||
@@ -108,12 +138,14 @@ const CustomerDetails: React.FC<Props> = ({ | |||
</Grid> | |||
</Box> | |||
<CardActions sx={{ justifyContent: "flex-end" }}> | |||
<Button variant="text" startIcon={<RestartAlt />}> | |||
<Button onClick={() => reset()} variant="text" startIcon={<RestartAlt />}> | |||
{t("Reset")} | |||
</Button> | |||
</CardActions> | |||
</CardContent> | |||
</Card> | |||
<ContactDetails/> | |||
</> | |||
); | |||
}; | |||
@@ -99,8 +99,8 @@ const navigationItems: NavigationItem[] = [ | |||
{ | |||
icon: <Settings />, label: "Setting", path: "", | |||
children: [ | |||
{ icon: <GroupIcon />, label: "Customer", path: "/customer" }, | |||
{ icon: <Staff />, label: "Staff", path: "/staff" }, | |||
{ icon: <GroupIcon />, label: "Customer", path: "/settings/customer" }, | |||
{ icon: <Staff />, label: "Staff", path: "/settings/staff" }, | |||
{ icon: <Company />, label: "Company", path: "/settings/company" }, | |||
{ icon: <Department />, label: "Department", path: "/settings/department" }, | |||
{ icon: <Position />, label: "Position", path: "/settings/position" }, | |||
@@ -1,3 +1,5 @@ | |||
import React from 'react'; | |||
import { useTranslation } from 'react-i18next'; | |||
import Swal from "sweetalert2"; | |||
export const msg = (text) => { | |||
@@ -20,3 +22,46 @@ export const msg = (text) => { | |||
export const popup = (text) => { | |||
Swal.fire(text); | |||
}; | |||
export const successDialog = (text, t) => { | |||
return Swal.fire({ | |||
icon: "success", | |||
title: text, | |||
confirmButtonText: t("Confirm"), | |||
showConfirmButton: true, | |||
}) | |||
} | |||
export const errorDialog = (text, t) => { | |||
return Swal.fire({ | |||
icon: "error", | |||
title: text, | |||
confirmButtonText: t("Confirm"), | |||
showConfirmButton: true, | |||
}) | |||
} | |||
export const warningDialog = (text, t) => { | |||
return Swal.fire({ | |||
icon: "warning", | |||
title: text, | |||
confirmButtonText: t("Confirm"), | |||
showConfirmButton: true, | |||
}) | |||
} | |||
export const submitDialog = (confirmAction, t) => { | |||
// const { t } = useTranslation("common") | |||
return Swal.fire({ | |||
icon: "question", | |||
title: t("Do you want to submit?"), | |||
cancelButtonText: t("Cancel"), | |||
confirmButtonText: t("Submit"), | |||
showCancelButton: true, | |||
showConfirmButton: true, | |||
}).then((result) => { | |||
if (result.isConfirmed) { | |||
confirmAction() | |||
} | |||
}) | |||
} |
@@ -5,5 +5,6 @@ | |||
"Search Criteria": "Search Criteria", | |||
"Cancel": "Cancel", | |||
"Confirm": "Confirm", | |||
"Submit": "Submit", | |||
"Reset": "Reset" | |||
} |
@@ -11,11 +11,13 @@ | |||
"Customer Contact Name": "Client Contact Name", | |||
"Customer Br No.": "Client Br No.", | |||
"Customer Details": "Client Details", | |||
"Customer Type": "Client Type", | |||
"Please input correct customer code": "Please input correct client code", | |||
"Please input correct customer name": "Please input correct client name", | |||
"Please input correct customer email": "Please input correct client email", | |||
"Please input correct customer br no.": "Please input correct client br no.", | |||
"The customer code has already existed": "The customer code has already existed", | |||
"Subsidiary" : "Subsidiary", | |||
"Subsidiary Allocation": "Subsidiary Allocation", | |||
@@ -32,11 +34,23 @@ | |||
"Subsidiary Br No.": "Subsidiary Br No.", | |||
"Subsidiary Details": "Subsidiary Details", | |||
"Add Contact Person": "Add Contact Person", | |||
"Contact Details": "Contact Details", | |||
"Contact Name": "Contact Name", | |||
"Contact Email": "Contact Email", | |||
"Contact Phone": "Contact Phone", | |||
"Please ensure all the fields are inputted and saved": "Please ensure all the fields are inputted and saved", | |||
"Please ensure all the email formats are correct": "Please ensure all the email formats are correct", | |||
"Do you want to submit?": "Do you want to submit?", | |||
"Submit Success": "Submit Success", | |||
"Add": "Add", | |||
"Details": "Details", | |||
"Search": "Search", | |||
"Search Criteria": "Search Criteria", | |||
"Cancel": "Cancel", | |||
"Confirm": "Confirm", | |||
"Submit": "Submit", | |||
"Reset": "Reset" | |||
} |
@@ -3,5 +3,6 @@ | |||
"Search Criteria": "搜尋條件", | |||
"Cancel": "取消", | |||
"Confirm": "確認", | |||
"Submit": "提交", | |||
"Reset": "重置" | |||
} |
@@ -11,11 +11,13 @@ | |||
"Customer Contact Name": "客戶聯絡名稱", | |||
"Customer Br No.": "客戶商業登記號碼", | |||
"Customer Details": "客戶詳請", | |||
"Customer Type": "客戶類型", | |||
"Please input correct customer code": "請輸入客戶編號", | |||
"Please input correct customer name": "請輸入客戶編號", | |||
"Please input correct customer email": "請輸入正確客戶電郵", | |||
"Please input correct customer br no.": "請輸入正確客戶商業登記號碼", | |||
"The customer code has already existed": "該客戶編號已存在", | |||
"Subsidiary": "子公司", | |||
"Subsidiary Allocation": "子公司分配", | |||
@@ -32,11 +34,23 @@ | |||
"Subsidiary Br No.": "子公司商業登記號碼", | |||
"Subsidiary Details": "子公司詳請", | |||
"Add Contact Person": "新增聯絡人", | |||
"Contact Details": "聯絡詳請", | |||
"Contact Name": "聯絡姓名", | |||
"Contact Email": "聯絡電郵", | |||
"Contact Phone": "聯絡電話", | |||
"Please ensure all the fields are inputted and saved": "請確保所有欄位已輸入及儲存", | |||
"Please ensure all the email formats are correct": "請確保所有電郵格式輸入正確", | |||
"Do you want to submit?": "你是否確認要提交?", | |||
"Submit Success": "提交成功", | |||
"Add": "新增", | |||
"Details": "詳請", | |||
"Search": "搜尋", | |||
"Search Criteria": "搜尋條件", | |||
"Cancel": "取消", | |||
"Confirm": "確認", | |||
"Submit": "提交", | |||
"Reset": "重置" | |||
} |