@@ -0,0 +1,45 @@ | |||||
// 'use client'; | |||||
import { I18nProvider, getServerI18n } from "@/i18n"; | |||||
import CustomInputForm from "@/components/CustomInputForm"; | |||||
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 Tab from "@mui/material/Tab"; | |||||
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, | |||||
SubmitErrorHandler, | |||||
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 { Grid, Typography } from "@mui/material"; | |||||
import CreateStaffForm from "@/components/CreateStaff/CreateStaff"; | |||||
import CreateTeam from "@/components/CreateTeam"; | |||||
const CreateTeamPage: React.FC = async () => { | |||||
const { t } = await getServerI18n("team"); | |||||
const title = ['', t('Additional Info')] | |||||
// const regex = new RegExp("^[a-z0-9._%+-]+@[a-z0-9.-]+\\.[a-z]{2,4}$") | |||||
// console.log(regex) | |||||
return ( | |||||
<> | |||||
<Typography variant="h4">{t("Create Team")}</Typography> | |||||
<I18nProvider namespaces={["Team"]}> | |||||
<CreateTeam/> | |||||
</I18nProvider> | |||||
</> | |||||
); | |||||
}; | |||||
export default CreateTeamPage; |
@@ -0,0 +1,53 @@ | |||||
import { preloadClaims } from "@/app/api/claims"; | |||||
import { preloadStaff, preloadTeamLeads } from "@/app/api/staff"; | |||||
import StaffSearch from "@/components/StaffSearch"; | |||||
import TeamSearch from "@/components/TeamSearch"; | |||||
import { I18nProvider, 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 { Metadata } from "next"; | |||||
import Link from "next/link"; | |||||
import { Suspense } from "react"; | |||||
export const metadata: Metadata = { | |||||
title: "Team", | |||||
}; | |||||
const Team: React.FC = async () => { | |||||
const { t } = await getServerI18n("Team"); | |||||
// preloadTeamLeads(); | |||||
// preloadStaff(); | |||||
return ( | |||||
<> | |||||
<Stack | |||||
direction="row" | |||||
justifyContent="space-between" | |||||
flexWrap="wrap" | |||||
rowGap={2} | |||||
> | |||||
<Typography variant="h4" marginInlineEnd={2}> | |||||
{t("Team")} | |||||
</Typography> | |||||
<Button | |||||
variant="contained" | |||||
startIcon={<Add />} | |||||
LinkComponent={Link} | |||||
href="/settings/team/create" | |||||
> | |||||
{t("Create Team")} | |||||
</Button> | |||||
</Stack> | |||||
<I18nProvider namespaces={["Team", "common"]}> | |||||
<Suspense fallback={<TeamSearch.Loading />}> | |||||
<TeamSearch /> | |||||
</Suspense> | |||||
</I18nProvider> | |||||
</> | |||||
); | |||||
}; | |||||
export default Team; |
@@ -0,0 +1,20 @@ | |||||
import { serverFetchJson } from "@/app/utils/fetchUtil"; | |||||
import { BASE_API_URL } from "@/config/api"; | |||||
import { cache } from "react"; | |||||
import "server-only"; | |||||
export interface TeamResult { | |||||
action: any; | |||||
id: number; | |||||
name: string; | |||||
code: string; | |||||
description: string; | |||||
} | |||||
export const fetchTeam = cache(async () => { | |||||
return serverFetchJson<TeamResult[]>(`${BASE_API_URL}/team`, { | |||||
next: { tags: ["team"] }, | |||||
}); | |||||
}); |
@@ -0,0 +1,126 @@ | |||||
"use client"; | |||||
import { | |||||
FieldErrors, | |||||
FormProvider, | |||||
SubmitErrorHandler, | |||||
SubmitHandler, | |||||
useForm, | |||||
} from "react-hook-form"; | |||||
import StaffAllocation from "./StaffAllocation"; | |||||
import { StaffResult } from "@/app/api/staff"; | |||||
import { CreateTeamInputs, saveTeam } from "@/app/api/team/actions"; | |||||
import { Button, Stack, Tab, Tabs, TabsProps, Typography } from "@mui/material"; | |||||
import { Check, Close } from "@mui/icons-material"; | |||||
import { useCallback, useState } from "react"; | |||||
import { useRouter, useSearchParams } from "next/navigation"; | |||||
import { useTranslation } from "react-i18next"; | |||||
import { Error } from "@mui/icons-material"; | |||||
import TeamInfo from "./TeamInfo"; | |||||
export interface Props { | |||||
allstaff: StaffResult[]; | |||||
} | |||||
const CreateTeam: React.FC<Props> = ({ allstaff }) => { | |||||
const formProps = useForm<CreateTeamInputs>(); | |||||
const [serverError, setServerError] = useState(""); | |||||
const router = useRouter(); | |||||
const [tabIndex, setTabIndex] = useState(0); | |||||
const { t } = useTranslation(); | |||||
const searchParams = useSearchParams() | |||||
const errors = formProps.formState.errors; | |||||
const onSubmit = useCallback<SubmitHandler<CreateTeamInputs>>( | |||||
async (data) => { | |||||
try { | |||||
console.log(data); | |||||
await saveTeam(data); | |||||
router.replace("/settings/team"); | |||||
} catch (e) { | |||||
console.log(e); | |||||
setServerError(t("An error has occurred. Please try again later.")); | |||||
} | |||||
}, | |||||
[router] | |||||
); | |||||
const handleCancel = () => { | |||||
router.back(); | |||||
}; | |||||
const handleTabChange = useCallback<NonNullable<TabsProps["onChange"]>>( | |||||
(_e, newValue) => { | |||||
setTabIndex(newValue); | |||||
}, | |||||
[], | |||||
); | |||||
const hasErrorsInTab = ( | |||||
tabIndex: number, | |||||
errors: FieldErrors<CreateTeamInputs>, | |||||
) => { | |||||
switch (tabIndex) { | |||||
case 0: | |||||
return Object.keys(errors).length > 0; | |||||
default: | |||||
false; | |||||
} | |||||
}; | |||||
return ( | |||||
<> | |||||
<FormProvider {...formProps}> | |||||
<Stack | |||||
spacing={2} | |||||
component="form" | |||||
onSubmit={formProps.handleSubmit(onSubmit)} | |||||
> | |||||
<Tabs | |||||
value={tabIndex} | |||||
onChange={handleTabChange} | |||||
variant="scrollable" | |||||
> | |||||
<Tab | |||||
label={t("Team Info")} | |||||
icon={ | |||||
hasErrorsInTab(0, errors) ? ( | |||||
<Error sx={{ marginInlineEnd: 1 }} color="error" /> | |||||
) : undefined | |||||
} | |||||
iconPosition="end" | |||||
/> | |||||
<Tab label={t("Subsidiary Allocation")} iconPosition="end" /> | |||||
</Tabs> | |||||
{serverError && ( | |||||
<Typography variant="body2" color="error" alignSelf="flex-end"> | |||||
{serverError} | |||||
</Typography> | |||||
)} | |||||
{tabIndex === 0 && <TeamInfo/>} | |||||
{tabIndex === 1 && <StaffAllocation allStaffs={allstaff} />} | |||||
{/* <StaffAllocation allStaffs={allstaff} /> */} | |||||
<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" | |||||
// disabled={Boolean(formProps.watch("isGridEditing"))} | |||||
> | |||||
{t("Confirm")} | |||||
</Button> | |||||
</Stack> | |||||
</Stack> | |||||
</FormProvider> | |||||
</> | |||||
); | |||||
}; | |||||
export default CreateTeam; |
@@ -0,0 +1,40 @@ | |||||
import Card from "@mui/material/Card"; | |||||
import CardContent from "@mui/material/CardContent"; | |||||
import Skeleton from "@mui/material/Skeleton"; | |||||
import Stack from "@mui/material/Stack"; | |||||
import React from "react"; | |||||
// Can make this nicer | |||||
export const CreateTeamLoading: React.FC = () => { | |||||
return ( | |||||
<> | |||||
<Card> | |||||
<CardContent> | |||||
<Stack spacing={2}> | |||||
<Skeleton variant="rounded" height={60} /> | |||||
<Skeleton variant="rounded" height={60} /> | |||||
<Skeleton variant="rounded" height={60} /> | |||||
<Skeleton | |||||
variant="rounded" | |||||
height={50} | |||||
width={100} | |||||
sx={{ alignSelf: "flex-end" }} | |||||
/> | |||||
</Stack> | |||||
</CardContent> | |||||
</Card> | |||||
<Card>CreateTeam | |||||
<CardContent> | |||||
<Stack spacing={2}> | |||||
<Skeleton variant="rounded" height={40} /> | |||||
<Skeleton variant="rounded" height={40} /> | |||||
<Skeleton variant="rounded" height={40} /> | |||||
<Skeleton variant="rounded" height={40} /> | |||||
</Stack> | |||||
</CardContent> | |||||
</Card> | |||||
</> | |||||
); | |||||
}; | |||||
export default CreateTeamLoading; |
@@ -0,0 +1,26 @@ | |||||
import React from "react"; | |||||
import CreateTeam from "./CreateTeam"; | |||||
import CreateTeamLoading from "./CreateTeamLoading"; | |||||
// import { fetchTeam, fetchTeamLeads } from "@/app/api/team"; | |||||
import { useSearchParams } from "next/navigation"; | |||||
import { fetchStaffCombo } from "@/app/api/staff/actions"; | |||||
import { fetchStaff } from "@/app/api/staff"; | |||||
interface SubComponents { | |||||
Loading: typeof CreateTeamLoading; | |||||
} | |||||
const CreateTeamWrapper: React.FC & SubComponents = async () => { | |||||
const [ | |||||
staff, | |||||
] = await Promise.all([ | |||||
fetchStaff(), | |||||
]); | |||||
return <CreateTeam allstaff={staff}/>; | |||||
}; | |||||
CreateTeamWrapper.Loading = CreateTeamLoading; | |||||
export default CreateTeamWrapper; |
@@ -0,0 +1,233 @@ | |||||
"use client"; | |||||
import React, { useCallback, useEffect, useMemo, useState } from "react"; | |||||
import CustomInputForm from "../CustomInputForm"; | |||||
import { useRouter } from "next/navigation"; | |||||
import { useTranslation } from "react-i18next"; | |||||
import { | |||||
FieldErrors, | |||||
FormProvider, | |||||
SubmitErrorHandler, | |||||
SubmitHandler, | |||||
useForm, | |||||
useFormContext, | |||||
} from "react-hook-form"; | |||||
import CreateTeamForm from "../CreateTeamForm"; | |||||
import { CreateTeamInputs } from "@/app/api/team/actions"; | |||||
import { Staff4TransferList, fetchStaffCombo } from "@/app/api/staff/actions"; | |||||
import { StaffResult, StaffTeamTable } from "@/app/api/staff"; | |||||
import SearchResults, { Column } from "../SearchResults"; | |||||
import { Clear, PersonAdd, PersonRemove, Search } from "@mui/icons-material"; | |||||
import { Card } from "reactstrap"; | |||||
import { Box, CardContent, Grid, IconButton, InputAdornment, Stack, Tab, Tabs, TabsProps, TextField, Typography } from "@mui/material"; | |||||
import { differenceBy } from "lodash"; | |||||
import StarsIcon from '@mui/icons-material/Stars'; | |||||
export interface Props { | |||||
allStaffs: StaffResult[]; | |||||
} | |||||
const StaffAllocation: React.FC<Props> = ({ allStaffs: staff }) => { | |||||
const { t } = useTranslation(); | |||||
const { | |||||
setValue, | |||||
getValues, | |||||
formState: { defaultValues }, | |||||
reset, | |||||
resetField, | |||||
} = useFormContext<CreateTeamInputs>(); | |||||
const initialStaffs = staff.map((s) => ({ ...s })); | |||||
// console.log(initialStaffs) | |||||
const [filteredStaff, setFilteredStaff] = useState(initialStaffs); | |||||
const [selectedStaff, setSelectedStaff] = useState<typeof filteredStaff>( | |||||
initialStaffs.filter((s) => getValues("addStaffIds")?.includes(s.id)) | |||||
); | |||||
const [seletedTeamLead, setSeletedTeamLead] = useState<number>() | |||||
// Adding / Removing staff | |||||
const addStaff = useCallback((staff: StaffResult) => { | |||||
setSelectedStaff((s) => [...s, staff]); | |||||
}, []); | |||||
const removeStaff = useCallback((staff: StaffResult) => { | |||||
setSelectedStaff((s) => s.filter((s) => s.id !== staff.id)); | |||||
}, []); | |||||
const setTeamLead = useCallback((staff: StaffResult) => { | |||||
setSeletedTeamLead(staff.id) | |||||
const rearrangedList = getValues("addStaffIds").reduce<number[]>((acc, num, index) => { | |||||
if (num === staff.id && index !== 0) { | |||||
acc.splice(index, 1); | |||||
acc.unshift(num); | |||||
} | |||||
return acc; | |||||
}, getValues("addStaffIds")); | |||||
console.log(rearrangedList) | |||||
console.log(selectedStaff) | |||||
const rearrangedStaff = rearrangedList.map((id) => { | |||||
return selectedStaff.find((staff) => staff.id === id); | |||||
}); | |||||
console.log(rearrangedStaff) | |||||
// setSelectedStaff(rearrangedStaff as StaffResult[]); | |||||
setValue("addStaffIds", rearrangedList) | |||||
}, []); | |||||
const clearSubsidiary = useCallback(() => { | |||||
if (defaultValues !== undefined) { | |||||
resetField("addStaffIds"); | |||||
setSelectedStaff( | |||||
initialStaffs.filter((s) => defaultValues.addStaffIds?.includes(s.id)) | |||||
); | |||||
} | |||||
}, [defaultValues]); | |||||
// Sync with form | |||||
useEffect(() => { | |||||
console.log(selectedStaff) | |||||
setValue( | |||||
"addStaffIds", | |||||
selectedStaff.map((s) => s.id) | |||||
); | |||||
}, [selectedStaff, setValue]); | |||||
const StaffPoolColumns = useMemo<Column<StaffResult>[]>( | |||||
() => [ | |||||
{ | |||||
label: t("Add"), | |||||
name: "id", | |||||
onClick: addStaff, | |||||
buttonIcon: <PersonAdd />, | |||||
}, | |||||
{ label: t("Staff Id"), name: "staffId" }, | |||||
{ label: t("Staff Name"), name: "name" }, | |||||
{ label: t("Current Position"), name: "currentPosition" }, | |||||
], | |||||
[addStaff, t] | |||||
); | |||||
const allocatedStaffColumns = useMemo<Column<StaffResult>[]>( | |||||
() => [ | |||||
{ | |||||
label: t("Remove"), | |||||
name: "action", | |||||
onClick: removeStaff, | |||||
buttonIcon: <PersonRemove />, | |||||
}, | |||||
{ label: t("Staff Id"), name: "staffId" }, | |||||
{ label: t("Staff Name"), name: "name" }, | |||||
{ label: t("Current Position"), name: "currentPosition" }, | |||||
{ | |||||
label: t("Team Lead"), | |||||
name: "action", | |||||
onClick: setTeamLead, | |||||
buttonIcon: <StarsIcon />, | |||||
}, | |||||
], | |||||
[removeStaff, t] | |||||
); | |||||
const [query, setQuery] = React.useState(""); | |||||
const onQueryInputChange = React.useCallback< | |||||
React.ChangeEventHandler<HTMLInputElement> | |||||
>((e) => { | |||||
setQuery(e.target.value); | |||||
}, []); | |||||
const clearQueryInput = React.useCallback(() => { | |||||
setQuery(""); | |||||
}, []); | |||||
React.useEffect(() => { | |||||
// setFilteredStaff( | |||||
// initialStaffs.filter((s) => { | |||||
// const q = query.toLowerCase(); | |||||
// // s.staffId.toLowerCase().includes(q) | |||||
// // const q = query.toLowerCase(); | |||||
// // return s.name.toLowerCase().includes(q); | |||||
// // s.code.toString().includes(q) || | |||||
// // (s.brNo != null && s.brNo.toLowerCase().includes(q)) | |||||
// }) | |||||
// ); | |||||
}, [staff, query]); | |||||
const resetStaff = React.useCallback(() => { | |||||
clearQueryInput(); | |||||
clearSubsidiary(); | |||||
}, [clearQueryInput, clearSubsidiary]); | |||||
const formProps = useForm({ | |||||
}); | |||||
// Tab related | |||||
const [tabIndex, setTabIndex] = React.useState(0); | |||||
const handleTabChange = React.useCallback<NonNullable<TabsProps["onChange"]>>( | |||||
(_e, newValue) => { | |||||
setTabIndex(newValue); | |||||
}, | |||||
[], | |||||
); | |||||
return ( | |||||
<> | |||||
<FormProvider {...formProps}> | |||||
<Card sx={{ display: "block" }}> | |||||
<CardContent | |||||
sx={{ display: "flex", flexDirection: "column", gap: 1 }} | |||||
> | |||||
<Stack gap={2}> | |||||
<Typography variant="overline" display="block"> | |||||
{t("staff")} | |||||
</Typography> | |||||
<Grid container spacing={2} columns={{ xs: 6, sm: 12 }}> | |||||
<Grid item xs={6} display="flex" alignItems="center"> | |||||
<Search sx={{ marginInlineEnd: 1 }} /> | |||||
<TextField | |||||
variant="standard" | |||||
fullWidth | |||||
onChange={onQueryInputChange} | |||||
value={query} | |||||
placeholder={t("Search by subsidiary code, name or br no.")} | |||||
InputProps={{ | |||||
endAdornment: query && ( | |||||
<InputAdornment position="end"> | |||||
<IconButton onClick={clearQueryInput}> | |||||
<Clear /> | |||||
</IconButton> | |||||
</InputAdornment> | |||||
), | |||||
}} | |||||
/> | |||||
</Grid> | |||||
</Grid> | |||||
<Tabs value={tabIndex} onChange={handleTabChange}> | |||||
<Tab label={t("Staff Pool")} /> | |||||
<Tab | |||||
label={`${t("Allocated Staff")} (${selectedStaff.length})`} | |||||
/> | |||||
</Tabs> | |||||
<Box sx={{ marginInline: -3 }}> | |||||
{tabIndex === 0 && ( | |||||
<SearchResults | |||||
noWrapper | |||||
items={differenceBy(filteredStaff, selectedStaff, "id")} | |||||
columns={StaffPoolColumns} | |||||
/> | |||||
)} | |||||
{tabIndex === 1 && ( | |||||
<SearchResults | |||||
noWrapper | |||||
items={selectedStaff} | |||||
columns={allocatedStaffColumns} | |||||
/> | |||||
)} | |||||
</Box> | |||||
</Stack> | |||||
</CardContent> | |||||
</Card> | |||||
</FormProvider> | |||||
</> | |||||
); | |||||
}; | |||||
export default StaffAllocation; |
@@ -0,0 +1,69 @@ | |||||
"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 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 { FormControl, InputLabel, MenuItem, Select } from "@mui/material"; | |||||
import { useCallback } from "react"; | |||||
import { CreateTeamInputs } from "@/app/api/team/actions"; | |||||
const TeamInfo: React.FC = ( | |||||
{ | |||||
// customerTypes, | |||||
} | |||||
) => { | |||||
const { t } = useTranslation(); | |||||
const { | |||||
register, | |||||
formState: { errors, defaultValues }, | |||||
control, | |||||
reset, | |||||
resetField, | |||||
setValue, | |||||
} = useFormContext<CreateTeamInputs>(); | |||||
const resetCustomer = useCallback(() => { | |||||
console.log(defaultValues); | |||||
if (defaultValues !== undefined) { | |||||
resetField("description"); | |||||
} | |||||
}, [defaultValues]); | |||||
return ( | |||||
<> | |||||
<Card sx={{ display: "block" }}> | |||||
<CardContent component={Stack} spacing={4}> | |||||
<Box> | |||||
<Typography variant="overline" display="block" marginBlockEnd={1}> | |||||
{t("Team Info")} | |||||
</Typography> | |||||
<Grid container spacing={2} columns={{ xs: 6, sm: 12 }}> | |||||
<Grid item xs={12}> | |||||
<TextField | |||||
label={t("Team Description")} | |||||
fullWidth | |||||
multiline | |||||
rows={4} | |||||
{...register("description", { | |||||
required: true, | |||||
})} | |||||
error={Boolean(errors.description)} | |||||
helperText={Boolean(errors.description) && (errors.description?.message ? t(errors.description.message) : t("Please input correct description"))} | |||||
/> | |||||
</Grid> | |||||
</Grid> | |||||
</Box> | |||||
</CardContent> | |||||
</Card> | |||||
</> | |||||
); | |||||
}; | |||||
export default TeamInfo; |
@@ -0,0 +1 @@ | |||||
export { default } from "./CreateTeamWrapper"; |
@@ -0,0 +1,90 @@ | |||||
"use client"; | |||||
import { TeamResult } from "@/app/api/team"; | |||||
import SearchBox, { Criterion } from "../SearchBox"; | |||||
import { useMemo, useState } from "react"; | |||||
import { useTranslation } from "react-i18next"; | |||||
import SearchResults, { Column } from "../SearchResults/index"; | |||||
import EditNote from "@mui/icons-material/EditNote"; | |||||
import DeleteIcon from '@mui/icons-material/Delete'; | |||||
import { deleteStaff } from "@/app/api/staff/actions"; | |||||
import { useRouter } from "next/navigation"; | |||||
interface Props { | |||||
team: TeamResult[]; | |||||
} | |||||
type SearchQuery = Partial<Omit<TeamResult, "id">>; | |||||
type SearchParamNames = keyof SearchQuery; | |||||
const TeamSearch: React.FC<Props> = ({ team }) => { | |||||
const { t } = useTranslation(); | |||||
const [filteredTeam, setFilteredTeam] = useState(team); | |||||
const [data, setData] = useState<TeamResult>(); | |||||
const [isOpen, setIsOpen] = useState(false); | |||||
const router = useRouter(); | |||||
const searchCriteria: Criterion<SearchParamNames>[] = useMemo( | |||||
() => [ | |||||
{ | |||||
label: t("Team Name"), | |||||
paramName: "name", | |||||
type: "text", | |||||
}, | |||||
{ | |||||
label: t("Team Code"), | |||||
paramName: "code", | |||||
type: "text", | |||||
}, | |||||
{ | |||||
label: t("Team Description"), | |||||
paramName: "description", | |||||
type: "text", | |||||
}, | |||||
], | |||||
[t], | |||||
); | |||||
const columns = useMemo<Column<TeamResult>[]>( | |||||
() => [ | |||||
// { | |||||
// name: "action", | |||||
// label: t("Actions"), | |||||
// onClick: onStaffClick, | |||||
// buttonIcon: <EditNote />, | |||||
// }, | |||||
{ name: "name", label: t("Name") }, | |||||
{ name: "code", label: t("Code") }, | |||||
{ name: "description", label: t("description") }, | |||||
// { | |||||
// name: "action", | |||||
// label: t("Actions"), | |||||
// onClick: deleteClick, | |||||
// buttonIcon: <DeleteIcon />, | |||||
// }, | |||||
], | |||||
[t], | |||||
); | |||||
return ( | |||||
<> | |||||
<SearchBox | |||||
criteria={searchCriteria} | |||||
onSearch={(query) => { | |||||
// setFilteredStaff( | |||||
// staff.filter( | |||||
// (s) => | |||||
// s.staffId.toLowerCase().includes(query.staffId.toLowerCase()) && | |||||
// s.name.toLowerCase().includes(query.name.toLowerCase()) | |||||
// // (query.team === "All" || s.team === query.team) && | |||||
// // (query.category === "All" || s.category === query.category) && | |||||
// // (query.team === "All" || s.team === query.team), | |||||
// ) | |||||
// ) | |||||
}} | |||||
/> | |||||
<SearchResults<TeamResult> items={filteredTeam} columns={columns} /> | |||||
</> | |||||
); | |||||
}; | |||||
export default TeamSearch; |
@@ -0,0 +1,40 @@ | |||||
import Card from "@mui/material/Card"; | |||||
import CardContent from "@mui/material/CardContent"; | |||||
import Skeleton from "@mui/material/Skeleton"; | |||||
import Stack from "@mui/material/Stack"; | |||||
import React from "react"; | |||||
// Can make this nicer | |||||
export const TeamSearchLoading: React.FC = () => { | |||||
return ( | |||||
<> | |||||
<Card> | |||||
<CardContent> | |||||
<Stack spacing={2}> | |||||
<Skeleton variant="rounded" height={60} /> | |||||
<Skeleton variant="rounded" height={60} /> | |||||
<Skeleton variant="rounded" height={60} /> | |||||
<Skeleton | |||||
variant="rounded" | |||||
height={50} | |||||
width={100} | |||||
sx={{ alignSelf: "flex-end" }} | |||||
/> | |||||
</Stack> | |||||
</CardContent> | |||||
</Card> | |||||
<Card> | |||||
<CardContent> | |||||
<Stack spacing={2}> | |||||
<Skeleton variant="rounded" height={40} /> | |||||
<Skeleton variant="rounded" height={40} /> | |||||
<Skeleton variant="rounded" height={40} /> | |||||
<Skeleton variant="rounded" height={40} /> | |||||
</Stack> | |||||
</CardContent> | |||||
</Card> | |||||
</> | |||||
); | |||||
}; | |||||
export default TeamSearchLoading; |
@@ -0,0 +1,21 @@ | |||||
// import { fetchTeam, fetchTeamLeads } from "@/app/api/Team"; | |||||
import React from "react"; | |||||
import TeamSearch from "./TeamSearch"; | |||||
import TeamSearchLoading from "./TeamSearchLoading"; | |||||
import { fetchTeam } from "@/app/api/team"; | |||||
// import { preloadTeam } from "@/app/api/Team"; | |||||
interface SubComponents { | |||||
Loading: typeof TeamSearchLoading; | |||||
} | |||||
const TeamSearchWrapper: React.FC & SubComponents = async () => { | |||||
const Team = await fetchTeam(); | |||||
console.log(Team); | |||||
return <TeamSearch team={Team} />; | |||||
}; | |||||
TeamSearchWrapper.Loading = TeamSearchLoading; | |||||
export default TeamSearchWrapper; |
@@ -0,0 +1 @@ | |||||
export { default } from "./TeamSearchWrapper"; |