diff --git a/src/app/(main)/settings/team/edit/page.tsx b/src/app/(main)/settings/team/edit/page.tsx
new file mode 100644
index 0000000..94458f4
--- /dev/null
+++ b/src/app/(main)/settings/team/edit/page.tsx
@@ -0,0 +1,25 @@
+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 EditStaffWrapper from "@/components/EditStaff/EditStaffWrapper";
+import { Metadata } from "next";
+import EditTeam from "@/components/EditTeam";
+
+
+const EditTeamPage: React.FC = () => {
+
+ return (
+ <>
+
+ }>
+
+
+
+ {/* */}
+ >
+ );
+};
+
+export default EditTeamPage;
diff --git a/src/app/api/staff/actions.ts b/src/app/api/staff/actions.ts
index 1098508..9416d2d 100644
--- a/src/app/api/staff/actions.ts
+++ b/src/app/api/staff/actions.ts
@@ -3,6 +3,7 @@ import { serverFetchJson } from "@/app/utils/fetchUtil";
import { BASE_API_URL } from "@/config/api";
import { StaffResult, data } from ".";
import { cache } from "react";
+import { Team, staff } from "../team/actions";
export interface CreateCustomInputs {
// Project details
projectCode: string;
@@ -28,11 +29,20 @@ export interface CreateStaffInputs {
emergContactPhone: string;
employType: string;
joinDate: string | null;
- departDate: string | null;
- departReason: string | null;
- remark: string | null;
+ departDate?: string | null;
+ departReason?: string | null;
+ remark?: string | null;
}
+ export interface records {
+ id: number;
+ name: string;
+ // team: Team[];
+ }
+ export interface Staff4TransferList {
+ records: records[];
+ }
+
export const saveStaff = async (data: CreateStaffInputs) => {
return serverFetchJson(`${BASE_API_URL}/staffs/save`, {
method: "POST",
@@ -68,3 +78,9 @@ export const fetchStaffEdit = cache(async (id: number) => {
// fetchStaffEdit(id);
// };
+export const fetchStaffCombo = cache(async () => {
+ return serverFetchJson(`${BASE_API_URL}/staffs/combo`, {
+ next: { tags: ["staffs"] },
+ });
+});
+
diff --git a/src/app/api/staff/index.ts b/src/app/api/staff/index.ts
index afa082f..2c3f66d 100644
--- a/src/app/api/staff/index.ts
+++ b/src/app/api/staff/index.ts
@@ -6,6 +6,26 @@ import "server-only";
export interface data {
[key: string]: any;
}
+
+export interface StaffGroup {
+ id: number;
+ name: string;
+}
+export interface Staff {
+ id: number;
+ staffId: string;
+ name: string;
+ // description: string | null;
+ currentPosition: StaffGroup;
+}
+export interface StaffTeamTable {
+ id: number;
+ staffId: string;
+ name: string;
+ // description: string | null;
+ currentPosition: string;
+}
+
export interface StaffResult {
action: any;
id: number;
@@ -16,6 +36,8 @@ export interface StaffResult {
joinPosition: string;
currentPosition: string;
data: data;
+ teamId: number;
+ staffName: string;
}
export interface searchInput {
staffId: string;
@@ -44,3 +66,9 @@ export const fetchStaff = cache(async () => {
next: { tags: ["staffs"] },
});
});
+
+// export const fetchStaffCombo = cache(async () => {
+// return serverFetchJson(`${BASE_API_URL}/staffs/combo`, {
+// next: { tags: ["staffs"] },
+// });
+// });
diff --git a/src/components/CreateTeam/TeamInfo.tsx b/src/components/CreateTeam/TeamInfo.tsx
index 9dd4060..4e61f4b 100644
--- a/src/components/CreateTeam/TeamInfo.tsx
+++ b/src/components/CreateTeam/TeamInfo.tsx
@@ -16,9 +16,6 @@ import { useCallback } from "react";
import { CreateTeamInputs } from "@/app/api/team/actions";
const TeamInfo: React.FC = (
- {
- // customerTypes,
- }
) => {
const { t } = useTranslation();
const {
@@ -36,7 +33,7 @@ const TeamInfo: React.FC = (
resetField("description");
}
}, [defaultValues]);
-
+
return (
<>
diff --git a/src/components/EditTeam/Allocation.tsx b/src/components/EditTeam/Allocation.tsx
new file mode 100644
index 0000000..96b4b46
--- /dev/null
+++ b/src/components/EditTeam/Allocation.tsx
@@ -0,0 +1,263 @@
+"use client";
+import React, { useCallback, useEffect, useMemo, useState } from "react";
+import CustomInputForm from "../CustomInputForm";
+import { useRouter, useSearchParams } from "next/navigation";
+import { useTranslation } from "react-i18next";
+import {
+ FieldErrors,
+ FormProvider,
+ SubmitErrorHandler,
+ SubmitHandler,
+ useForm,
+ useFormContext,
+} from "react-hook-form";
+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 Allocation: React.FC = ({ allStaffs: staff }) => {
+ const { t } = useTranslation();
+ const searchParams = useSearchParams();
+ const idString = searchParams.get("id");
+ const {
+ setValue,
+ getValues,
+ formState: { defaultValues },
+ reset,
+ resetField,
+ } = useFormContext();
+
+ // let firstFilter: StaffResult[] = []
+
+ const initialStaffs = staff.map((s) => ({ ...s }));
+ const [filteredStaff, setFilteredStaff] = useState(initialStaffs);
+ const [selectedStaff, setSelectedStaff] = useState(
+ filteredStaff.filter((s) => getValues("addStaffIds")?.includes(s.id))
+ );
+ const [seletedTeamLead, setSeletedTeamLead] = useState();
+ const [deletedStaffIds, setDeletedStaffIds] = useState([]);
+
+ // Adding / Removing staff
+ const addStaff = useCallback((staff: StaffResult) => {
+ setSelectedStaff((s) => [...s, staff]);
+ // setDeletedStaffIds((s) => s.filter((s) => s === selectedStaff.id))
+ }, []);
+
+ const removeStaff = useCallback((staff: StaffResult) => {
+ setSelectedStaff((s) => s.filter((s) => s.id !== staff.id));
+ setDeletedStaffIds((s) => s)
+ // setValue("deleteStaffIds", [...staff.id])
+ }, []);
+
+ const setTeamLead = useCallback(
+ (staff: StaffResult) => {
+ setSeletedTeamLead(staff.id);
+ const rearrangedList = getValues("addStaffIds").reduce(
+ (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);
+ },
+ [addStaff, selectedStaff]
+ );
+
+ const clearSubsidiary = useCallback(() => {
+ if (defaultValues !== undefined) {
+ resetField("addStaffIds");
+ setSelectedStaff(
+ initialStaffs.filter((s) => defaultValues.addStaffIds?.includes(s.id))
+ );
+ }
+ }, [defaultValues]);
+
+ // Sync with form
+ useEffect(() => {
+ setValue(
+ "addStaffIds",
+ selectedStaff.map((s) => s.id)
+ );
+ }, [selectedStaff, setValue]);
+
+ useEffect(() => {
+ setValue("deleteStaffIds", deletedStaffIds)
+ console.log(deletedStaffIds)
+ }, [deletedStaffIds, setValue]);
+
+ const StaffPoolColumns = useMemo[]>(
+ () => [
+ {
+ label: t("Add"),
+ name: "id",
+ onClick: addStaff,
+ buttonIcon: ,
+ },
+ { label: t("Staff Id"), name: "staffId" },
+ { label: t("Staff Name"), name: "name" },
+ { label: t("Current Position"), name: "currentPosition" },
+ ],
+ [addStaff, t]
+ );
+
+ const allocatedStaffColumns = useMemo[]>(
+ () => [
+ {
+ label: t("Remove"),
+ name: "action",
+ onClick: removeStaff,
+ buttonIcon: ,
+ },
+ { 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: ,
+ },
+ ],
+ [removeStaff, selectedStaff, t]
+ );
+
+ const [query, setQuery] = React.useState("");
+ const onQueryInputChange = React.useCallback<
+ React.ChangeEventHandler
+ >((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]);
+
+ useEffect(() => {
+ // console.log(getValues("addStaffIds"))
+ }, [initialStaffs]);
+
+ const resetStaff = React.useCallback(() => {
+ clearQueryInput();
+ clearSubsidiary();
+ }, [clearQueryInput, clearSubsidiary]);
+
+ const formProps = useForm({});
+
+ // Tab related
+ const [tabIndex, setTabIndex] = React.useState(0);
+ const handleTabChange = React.useCallback>(
+ (_e, newValue) => {
+ setTabIndex(newValue);
+ },
+ []
+ );
+ return (
+ <>
+
+
+
+
+
+ {t("staff")}
+
+
+
+
+
+
+
+
+
+ ),
+ }}
+ />
+
+
+
+
+
+
+
+ {tabIndex === 0 && (
+
+ )}
+ {tabIndex === 1 && (
+
+ )}
+
+
+
+
+
+ >
+ );
+};
+
+export default Allocation;
diff --git a/src/components/EditTeam/EditTeam.tsx b/src/components/EditTeam/EditTeam.tsx
new file mode 100644
index 0000000..4b93754
--- /dev/null
+++ b/src/components/EditTeam/EditTeam.tsx
@@ -0,0 +1,204 @@
+"use client";
+import { useRouter, useSearchParams } from "next/navigation";
+import { useCallback, useEffect, useMemo, useState } from "react";
+import SearchResults, { Column } from "../SearchResults";
+// import { TeamResult } from "@/app/api/team";
+import { useTranslation } from "react-i18next";
+import { Button, Stack, Tab, Tabs, TabsProps, Typography } from "@mui/material";
+import { CreateTeamInputs, saveTeam } from "@/app/api/team/actions";
+import {
+ FieldErrors,
+ FormProvider,
+ SubmitHandler,
+ useForm,
+ useFormContext,
+} from "react-hook-form";
+import { Check, Close, Error } from "@mui/icons-material";
+import TeamInfo from "../EditTeam/TeamInfo";
+import Allocation from "./Allocation";
+import { StaffResult } from "@/app/api/staff";
+
+interface desc {
+ id: number;
+ description: string;
+}
+
+interface Props {
+ staff: StaffResult[];
+ desc: desc[];
+}
+
+const EditTeam: React.FC = async ({ staff, desc }) => {
+ // console.log(desc)
+ const { t } = useTranslation();
+ const formProps = useForm();
+ const searchParams = useSearchParams();
+ const idString = searchParams.get("id");
+ const [filteredItems, setFilteredItems] = useState();
+ const [allStaffs, setAllStaffs] = useState();
+ const [filteredDesc, setFilteredDesc] = useState();
+ const [tabIndex, setTabIndex] = useState(0);
+ const router = useRouter();
+ // const [selectedStaff, setSelectedStaff] = useState(
+ // initialStaffs.filter((s) => getValues("addStaffIds")?.includes(s.id))
+ // );
+
+ const errors = formProps.formState.errors;
+
+ const handleTabChange = useCallback>(
+ (_e, newValue) => {
+ setTabIndex(newValue);
+ },
+ []
+ );
+
+ const [serverError, setServerError] = useState("");
+ const cols = useMemo[]>(
+ () => [
+ { label: t("Staff Id"), name: "staffId" },
+ { label: t("Name"), name: "staffName" },
+ // { label: t("Current Position"), name: "posCode" },
+ ],
+ [t]
+ );
+ useEffect(() => {
+ let idList: number[] = []
+ if (idString) {
+ const filteredTeam = staff.filter(
+ (item) => item.teamId === parseInt(idString)
+ );
+ const tempDesc = desc.filter(
+ (item) => item.id === parseInt(idString)
+ )
+ // console.log(filteredTeam);
+ if (filteredTeam.length > 0) {
+ const filteredIds: number[] = filteredTeam.map((i) => (
+ i.id
+ ))
+ idList = filteredIds
+ }
+ // console.log(filteredIds)
+ setFilteredItems(filteredTeam);
+ formProps.reset({description: tempDesc[0].description, addStaffIds: idList})
+ setFilteredDesc(tempDesc[0].description)
+ }
+ // console.log(staff);
+ // const desc = staff[0]?.description
+ // setDesc(desc)
+ // const staff = staff.map((item) => {
+ // return {
+ // id: item.id,
+ // name: item.name,
+ // staffId: item.staffId,
+ // teamId: item.teamId,
+ // staffName: item.staffName,
+ // currentPosition: item.currentPosition
+
+ // } as StaffResult
+ // })
+ console.log(staff)
+ setAllStaffs(staff)
+
+ }, [searchParams]);
+
+// useEffect(() => {
+// console.log(allStaffs)
+// }, [allStaffs]);
+
+ const hasErrorsInTab = (
+ tabIndex: number,
+ errors: FieldErrors
+ ) => {
+ switch (tabIndex) {
+ case 0:
+ return Object.keys(errors).length > 0;
+ default:
+ false;
+ }
+ };
+
+ const onSubmit = useCallback>(
+ async (data) => {
+ try {
+ console.log(data);
+ const tempData = {
+ description: data.description,
+ addStaffIds: data.addStaffIds,
+ id: parseInt(idString!!)
+ }
+ console.log(tempData)
+ // await saveTeam(tempData);
+ // router.replace("/settings/staff");
+ } catch (e) {
+ console.log(e);
+ setServerError(t("An error has occurred. Please try again later."));
+ }
+ },
+ [router]
+ );
+
+ return (
+ <>
+ {serverError && (
+
+ {serverError}
+
+ )}
+
+
+
+
+ {t("Edit Team")}
+
+
+
+
+ ) : undefined
+ }
+ iconPosition="end"
+ />
+
+
+
+ {tabIndex === 0 && }
+ {tabIndex === 1 && }
+
+ }
+ // onClick={handleCancel}
+ >
+ {t("Cancel")}
+
+ }
+ type="submit"
+ // disabled={Boolean(formProps.watch("isGridEditing"))}
+ >
+ {t("Confirm")}
+
+
+
+
+ >
+ );
+};
+export default EditTeam;
diff --git a/src/components/EditTeam/EditTeamLoading.tsx b/src/components/EditTeam/EditTeamLoading.tsx
new file mode 100644
index 0000000..ed3d763
--- /dev/null
+++ b/src/components/EditTeam/EditTeamLoading.tsx
@@ -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 EditTeamLoading: React.FC = () => {
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
+ EditTeam
+
+
+
+
+
+
+
+
+
+ >
+ );
+};
+
+export default EditTeamLoading;
diff --git a/src/components/EditTeam/EditTeamWrapper.tsx b/src/components/EditTeam/EditTeamWrapper.tsx
new file mode 100644
index 0000000..bb58a09
--- /dev/null
+++ b/src/components/EditTeam/EditTeamWrapper.tsx
@@ -0,0 +1,29 @@
+import React from "react";
+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 { fetchStaff } from "@/app/api/staff";
+
+interface SubComponents {
+ Loading: typeof EditTeamLoading;
+}
+
+const EditTeamWrapper: React.FC & SubComponents = async () => {
+ const staff = await fetchStaff()
+ const allTeams = await fetchTeam();
+ const teamDesc = allTeams.map((i) => (
+ {
+ id: i.id,
+ description: i.description
+ }
+ ))
+ console.log(staff)
+ console.log(teamDesc)
+ return ;
+};
+
+EditTeamWrapper.Loading = EditTeamLoading;
+
+export default EditTeamWrapper;
diff --git a/src/components/EditTeam/TeamInfo.tsx b/src/components/EditTeam/TeamInfo.tsx
new file mode 100644
index 0000000..4a54f6c
--- /dev/null
+++ b/src/components/EditTeam/TeamInfo.tsx
@@ -0,0 +1,74 @@
+"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";
+
+interface Props {
+ value: string;
+}
+
+const TeamInfo: React.FC = (
+ {
+ value
+ }
+) => {
+ const { t } = useTranslation();
+ const {
+ register,
+ formState: { errors, defaultValues },
+ control,
+ reset,
+ resetField,
+ setValue,
+ } = useFormContext();
+
+ const resetCustomer = useCallback(() => {
+ console.log(defaultValues);
+ if (defaultValues !== undefined) {
+ resetField("description");
+ }
+ }, [defaultValues]);
+
+ return (
+ <>
+
+
+
+
+ {t("Team Info")}
+
+
+
+
+
+
+
+
+
+ >
+ );
+};
+export default TeamInfo;
diff --git a/src/components/EditTeam/index.ts b/src/components/EditTeam/index.ts
new file mode 100644
index 0000000..fa63ef7
--- /dev/null
+++ b/src/components/EditTeam/index.ts
@@ -0,0 +1 @@
+export { default } from "./EditTeamWrapper";
diff --git a/src/components/TeamSearch/TeamSearch.tsx b/src/components/TeamSearch/TeamSearch.tsx
index 85d970d..9aae6f1 100644
--- a/src/components/TeamSearch/TeamSearch.tsx
+++ b/src/components/TeamSearch/TeamSearch.tsx
@@ -2,7 +2,7 @@
import { TeamResult } from "@/app/api/team";
import SearchBox, { Criterion } from "../SearchBox";
-import { useMemo, useState } from "react";
+import { useCallback, useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
import SearchResults, { Column } from "../SearchResults/index";
import EditNote from "@mui/icons-material/EditNote";
@@ -44,14 +44,20 @@ const TeamSearch: React.FC = ({ team }) => {
[t],
);
+ const onTeamClick = useCallback((team: TeamResult) => {
+ console.log(team);
+ const id = team.id
+ router.push(`/settings/team/edit?id=${id}`);
+ }, [router, t]);
+
const columns = useMemo[]>(
() => [
- // {
- // name: "action",
- // label: t("Actions"),
- // onClick: onStaffClick,
- // buttonIcon: ,
- // },
+ {
+ name: "action",
+ label: t("Actions"),
+ onClick: onTeamClick,
+ buttonIcon: ,
+ },
{ name: "name", label: t("Name") },
{ name: "code", label: t("Code") },
{ name: "description", label: t("description") },