ソースを参照

update

tags/Baseline_30082024_FRONTEND_UAT
MSI\derek 1年前
コミット
7d8a2a994a
8個のファイルの変更203行の追加156行の削除
  1. +7
    -3
      src/app/(main)/settings/team/edit/page.tsx
  2. +2
    -1
      src/app/api/team/actions.ts
  3. +18
    -0
      src/app/api/team/index.ts
  4. +22
    -0
      src/components/CreateTeam/TeamInfo.tsx
  5. +4
    -0
      src/components/EditTeam/Allocation.tsx
  6. +87
    -105
      src/components/EditTeam/EditTeam.tsx
  7. +9
    -18
      src/components/EditTeam/EditTeamWrapper.tsx
  8. +54
    -29
      src/components/EditTeam/TeamInfo.tsx

+ 7
- 3
src/app/(main)/settings/team/edit/page.tsx ファイルの表示

@@ -2,19 +2,23 @@ import { Edit } from "@mui/icons-material";
import { useSearchParams } from "next/navigation";
// import EditStaff from "@/components/EditStaff";
import { Suspense } from "react";
import { I18nProvider } from "@/i18n";
import { I18nProvider, getServerI18n } from "@/i18n";
// import EditStaffWrapper from "@/components/EditStaff/EditStaffWrapper";
import { Metadata } from "next";
import EditTeam from "@/components/EditTeam";
import { searchParamsProps } from "@/app/utils/fetchUtil";


const EditTeamPage: React.FC = () => {
const EditTeamPage: React.FC<searchParamsProps> = async ({
searchParams,
}) => {
const { t } = await getServerI18n("team");

return (
<>
<I18nProvider namespaces={["team", "common"]}>
<Suspense fallback={<EditTeam.Loading />}>
<EditTeam />
<EditTeam id={parseInt(searchParams.id as string)}/>
</Suspense>
</I18nProvider>
{/* <EditStaff /> */}


+ 2
- 1
src/app/api/team/actions.ts ファイルの表示

@@ -8,7 +8,8 @@ import { revalidateTag } from "next/cache";

export interface CreateTeamInputs {
id: number | null;
// name: string;
name: string;
code: string;
// team: string;
// staffId: string;
// grade: string;


+ 18
- 0
src/app/api/team/index.ts ファイルの表示

@@ -15,7 +15,19 @@ export interface TeamResult {
posLabel: string;
posCode: string;
teamLead: number;
}

export type IndivTeam = {
team: IndividualTeam
staffIds: number[]
}

export type IndividualTeam = {
id: number;
description: string;
name: string;
code: string;
teamLead: number;
}

export interface comboProp {
@@ -33,6 +45,12 @@ export const fetchTeam = cache(async () => {
});
});

export const fetchIndivTeam = cache(async (id: number) => {
return serverFetchJson<IndivTeam>(`${BASE_API_URL}/team/${id}`, {
next: { tags: ["team"] },
});
});

export const preloadTeamDetail = () => {
fetchTeamDetail();
};


+ 22
- 0
src/components/CreateTeam/TeamInfo.tsx ファイルの表示

@@ -43,6 +43,28 @@ const TeamInfo: React.FC = (
{t("Team Info")}
</Typography>
<Grid container spacing={2} columns={{ xs: 6, sm: 12 }}>
<Grid item xs={6}>
<TextField
label={t("name")}
fullWidth
{...register("name", {
required: true,
})}
error={Boolean(errors.name)}
helperText={Boolean(errors.name) && (errors.name?.message ? t(errors.name.message) : t("Please input correct name"))}
/>
</Grid>
<Grid item xs={6}>
<TextField
label={t("code")}
fullWidth
{...register("code", {
required: true,
})}
error={Boolean(errors.code)}
helperText={Boolean(errors.code) && (errors.code?.message ? t(errors.code.message) : t("Please input correct code"))}
/>
</Grid>
<Grid item xs={12}>
<TextField
label={t("Team Description")}


+ 4
- 0
src/components/EditTeam/Allocation.tsx ファイルの表示

@@ -62,6 +62,10 @@ const Allocation: React.FC<Props> = ({ allStaffs: staff, teamLead }) => {
);
const [deletedStaffIds, setDeletedStaffIds] = useState<number[]>([]);

console.log(getValues("addStaffIds"));
console.log(filteredStaff);
console.log(selectedStaff)

// Adding / Removing staff
const addStaff = useCallback((staff: StaffResult) => {
setSelectedStaff((s) => [...s, staff]);


+ 87
- 105
src/components/EditTeam/EditTeam.tsx ファイルの表示

@@ -1,6 +1,6 @@
"use client";
import { useRouter, useSearchParams } from "next/navigation";
import { useCallback, useEffect, useMemo, useState } from "react";
import React, { useCallback, useEffect, useMemo, useState } from "react";
import SearchResults, { Column } from "../SearchResults";
// import { TeamResult } from "@/app/api/team";
import { useTranslation } from "react-i18next";
@@ -13,40 +13,27 @@ import {
useForm,
useFormContext,
} from "react-hook-form";
import { Check, Close, Error } from "@mui/icons-material";
import { Check, Close, Error, RestartAlt } from "@mui/icons-material";
import TeamInfo from "../EditTeam/TeamInfo";
import Allocation from "./Allocation";
import { StaffResult } from "@/app/api/staff";

interface desc {
id: number;
name: string;
description: string;
teamLead: number;
}
import { IndivTeam } from "@/app/api/team";

interface Props {
staff: StaffResult[];
desc: desc[];
// teamLead: StaffResult[]
teamInfo: IndivTeam;
}

const EditTeam: React.FC<Props> = async ({ staff, desc }) => {
// console.log(desc)
const EditTeam: React.FC<Props> = async ({ staff, teamInfo }) => {
const { team, staffIds } = teamInfo;
const { t } = useTranslation();
const formProps = useForm<CreateTeamInputs>();
const searchParams = useSearchParams();
const idString = searchParams.get("id");
const [filteredItems, setFilteredItems] = useState<StaffResult[]>();
const [allStaffs, setAllStaffs] = useState<StaffResult[]>();
const [filteredDesc, setFilteredDesc] = useState<string>();
const [filteredName, setFilteredName] = useState<string>();
const [teamLead, setTeamLead] = useState<number>();
const [tabIndex, setTabIndex] = useState(0);
const router = useRouter();
// const [selectedStaff, setSelectedStaff] = useState<typeof filteredItems>(
// initialStaffs.filter((s) => getValues("addStaffIds")?.includes(s.id))
// );

const errors = formProps.formState.errors;

@@ -58,64 +45,36 @@ const EditTeam: React.FC<Props> = async ({ staff, desc }) => {
);

const [serverError, setServerError] = useState("");
const cols = useMemo<Column<StaffResult>[]>(
() => [
{ label: t("Staff Id"), name: "staffId" },
{ label: t("Name"), name: "staffName" },
// { label: t("Current Position"), name: "posCode" },
],
[t]
);

useEffect(() => {
let idList: number[] = []
let idList: number[] = [];
// console.log(desc)
if (idString) {
const filteredTeam = staff.filter(
(item) => {
return (item.teamId === parseInt(idString))}
);
// console.log(filteredTeam)
const tempDesc = desc.filter(
(item) => item.id === parseInt(idString)
)
// const leader = teamLead.filter(
// (staff) => staff.teamId === parseInt(idString)
// )
// console.log(leader)
console.log(tempDesc[0].teamLead)
setTeamLead(tempDesc[0].teamLead)
const filteredTeam = staff.filter((item) => {
return item.teamId === parseInt(idString);
});
console.log(team.teamLead);
setTeamLead(team.teamLead);
if (filteredTeam.length > 0) {
const filteredIds: number[] = filteredTeam.map((i) => (
i.id
))
// const teamLead = tempDesc[0].teamLead
// const index = filteredIds.indexOf(teamLead);
// if (index !== -1) {
// filteredIds.splice(index, 1);
// filteredIds.unshift(teamLead);
// }
const filteredIds: number[] = filteredTeam.map((i) => i.id);

// const teamLead = tempDesc[0].teamLead
// const index = filteredIds.indexOf(teamLead);

// if (index !== -1) {
// filteredIds.splice(index, 1);
// filteredIds.unshift(teamLead);
// }

idList = filteredIds
// console.log(filteredIds)
idList = filteredIds;
}
// console.log(idList)
setFilteredItems(filteredTeam);
formProps.reset({description: tempDesc[0].description, addStaffIds: idList})
setFilteredDesc(tempDesc[0].description)
setFilteredName(tempDesc[0].name)
formProps.reset({ description: team.description, addStaffIds: idList });
}
// console.log(staff)

setAllStaffs(staff)
setAllStaffs(staff);
}, [searchParams]);

// useEffect(() => {
// console.log(allStaffs)
// }, [allStaffs]);

const hasErrorsInTab = (
tabIndex: number,
errors: FieldErrors<CreateTeamInputs>
@@ -133,14 +92,14 @@ const EditTeam: React.FC<Props> = async ({ staff, desc }) => {
try {
// console.log(data);
const tempData = {
description: data.description,
addStaffIds: data.addStaffIds,
deleteStaffIds: data.deleteStaffIds,
id: parseInt(idString!!)
}
console.log(tempData)
await saveTeam(tempData);
router.replace("/settings/team");
description: data.description,
addStaffIds: data.addStaffIds,
deleteStaffIds: data.deleteStaffIds,
id: parseInt(idString!!),
};
console.log(tempData);
// await saveTeam(tempData);
// router.replace("/settings/team");
} catch (e) {
console.log(e);
setServerError(t("An error has occurred. Please try again later."));
@@ -149,6 +108,20 @@ const EditTeam: React.FC<Props> = async ({ staff, desc }) => {
[router]
);

const resetTeam = React.useCallback(() => {
formProps.reset({
name: team.name,
code: team.code,
description: team.description,
addStaffIds: staffIds,
deleteStaffIds: []
});
}, []);

useEffect(() => {
resetTeam();
}, [team]);

return (
<>
{serverError && (
@@ -157,41 +130,50 @@ const EditTeam: React.FC<Props> = async ({ staff, desc }) => {
</Typography>
)}
<FormProvider {...formProps}>
<Stack
<Stack
spacing={2}
component="form"
onSubmit={formProps.handleSubmit(onSubmit)}
>

<Typography variant="h4" marginInlineEnd={2}>
{t("Edit Team")} - {filteredName}
</Typography>
<Stack
direction="row"
justifyContent="space-between"
flexWrap="wrap"
rowGap={2}
>
<Tabs
value={tabIndex}
onChange={handleTabChange}
variant="scrollable"
<Typography variant="h4" marginInlineEnd={2}>
{t("Edit Team")}
{/* - {team.name} */}
</Typography>
<Stack
direction="row"
justifyContent="space-between"
flexWrap="wrap"
rowGap={2}
>
<Tab
label={t("Team Info")}
icon={
hasErrorsInTab(0, errors) ? (
<Error sx={{ marginInlineEnd: 1 }} color="error" />
) : undefined
}
iconPosition="end"
/>
<Tab label={t("Staff Allocation")} iconPosition="end" />
</Tabs>
</Stack>
{tabIndex === 0 && <TeamInfo value={filteredDesc!!} />}
{tabIndex === 1 && <Allocation allStaffs={allStaffs!!} teamLead={teamLead!!}/>}
<Stack direction="row" justifyContent="flex-end" gap={1}>
<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("Staff Allocation")} iconPosition="end" />
</Tabs>
</Stack>
{tabIndex === 0 && <TeamInfo />}
{tabIndex === 1 && (
<Allocation allStaffs={allStaffs!!} teamLead={teamLead!!} />
)}
<Stack direction="row" justifyContent="flex-end" gap={1}>
<Button
variant="text"
startIcon={<RestartAlt />}
onClick={resetTeam}
>
{t("Reset")}
</Button>
<Button
variant="outlined"
startIcon={<Close />}
@@ -208,7 +190,7 @@ const EditTeam: React.FC<Props> = async ({ staff, desc }) => {
{t("Confirm")}
</Button>
</Stack>
</Stack>
</Stack>
</FormProvider>
</>
);


+ 9
- 18
src/components/EditTeam/EditTeamWrapper.tsx ファイルの表示

@@ -3,37 +3,28 @@ import EditTeam from "./EditTeam";
import EditTeamLoading from "./EditTeamLoading";
// import { fetchTeam, fetchTeamLeads } from "@/app/api/Team";
import { useSearchParams } from "next/navigation";
import { fetchTeam, fetchTeamDetail } from "@/app/api/team";
import { fetchIndivTeam, fetchTeam, fetchTeamDetail } from "@/app/api/team";
import { fetchStaff, fetchStaffWithoutTeam, fetchTeamLeads } from "@/app/api/staff";

interface SubComponents {
Loading: typeof EditTeamLoading;
}

const EditTeamWrapper: React.FC & SubComponents = async () => {
interface Props {
id: number
}

const EditTeamWrapper: React.FC<Props> & SubComponents = async ({ id }) => {

const [
staff,
allTeams,
// teamLead,
team,
] = await Promise.all([
fetchStaff(),
fetchTeam(),
// fetchTeamLeads(),
fetchIndivTeam(id),
]);
console.log(allTeams)

const teamDesc = allTeams.map((i) => {
return (
{
id: i.id,
name: i.name,
description: i.description,
teamLead: i.teamLead
}
)})

return <EditTeam staff={staff} desc={teamDesc} />;
return <EditTeam staff={staff} teamInfo={team} />;
};

EditTeamWrapper.Loading = EditTeamLoading;


+ 54
- 29
src/components/EditTeam/TeamInfo.tsx ファイルの表示

@@ -16,14 +16,10 @@ import { useCallback } from "react";
import { CreateTeamInputs } from "@/app/api/team/actions";

interface Props {
value: string;
}

const TeamInfo: React.FC<Props> = (
{
value
}
) => {
const TeamInfo: React.FC<Props> = () => {
const { t } = useTranslation();
const {
register,
@@ -34,13 +30,6 @@ const TeamInfo: React.FC<Props> = (
setValue,
} = useFormContext<CreateTeamInputs>();

const resetCustomer = useCallback(() => {
console.log(defaultValues);
if (defaultValues !== undefined) {
resetField("description");
}
}, [defaultValues]);
return (
<>
<Card sx={{ display: "block" }}>
@@ -49,22 +38,58 @@ const TeamInfo: React.FC<Props> = (
<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,
})}
defaultValue={value}
error={Boolean(errors.description)}
helperText={Boolean(errors.description) && (errors.description?.message ? t(errors.description.message) : t("Please input correct description"))}
/>
</Grid>
</Grid>
<Grid container spacing={2} columns={{ xs: 6, sm: 12 }}>
<Grid item xs={6}>
<TextField
label={t("name")}
fullWidth
{...register("name", {
required: true,
})}
error={Boolean(errors.name)}
helperText={
Boolean(errors.name) &&
(errors.name?.message
? t(errors.name.message)
: t("Please input correct name"))
}
/>
</Grid>
<Grid item xs={6}>
<TextField
label={t("code")}
fullWidth
{...register("code", {
required: true,
})}
error={Boolean(errors.code)}
helperText={
Boolean(errors.code) &&
(errors.code?.message
? t(errors.code.message)
: t("Please input correct code"))
}
/>
</Grid>
<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>


読み込み中…
キャンセル
保存