diff --git a/src/app/(main)/settings/skill/create/page.tsx b/src/app/(main)/settings/skill/create/page.tsx
new file mode 100644
index 0000000..c98f993
--- /dev/null
+++ b/src/app/(main)/settings/skill/create/page.tsx
@@ -0,0 +1,48 @@
+// '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 CreateSkill from "@/components/CreateSkill";
+
+// const Title = ["title1", "title2"];
+
+const CreateStaff: React.FC = async () => {
+ const { t } = await getServerI18n("staff");
+
+ const title = ['', t('Additional Info')]
+ // const regex = new RegExp("^[a-z0-9._%+-]+@[a-z0-9.-]+\\.[a-z]{2,4}$")
+ // console.log(regex)
+
+ return (
+ <>
+ {t("Create Skill")}
+
+
+
+ >
+ );
+};
+
+export default CreateStaff;
diff --git a/src/app/(main)/settings/skill/page.tsx b/src/app/(main)/settings/skill/page.tsx
new file mode 100644
index 0000000..f263c87
--- /dev/null
+++ b/src/app/(main)/settings/skill/page.tsx
@@ -0,0 +1,50 @@
+import { preloadClaims } from "@/app/api/claims";
+// import { preloadSkill, preloadTeamLeads } from "@/app/api/staff";
+import SkillSearch from "@/components/SkillSearch";
+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: "Skill",
+};
+
+const Skill: React.FC = async () => {
+ const { t } = await getServerI18n("skill");
+// preloadTeamLeads();
+// preloadSkill();
+ return (
+ <>
+
+
+ {t("Skill")}
+
+ }
+ LinkComponent={Link}
+ href="/settings/skill/create"
+ >
+ {t("Create Skill")}
+
+
+
+ }>
+
+
+
+ >
+ );
+};
+
+export default Skill;
diff --git a/src/app/(main)/settings/user/page.tsx b/src/app/(main)/settings/user/page.tsx
new file mode 100644
index 0000000..95973ab
--- /dev/null
+++ b/src/app/(main)/settings/user/page.tsx
@@ -0,0 +1,54 @@
+import { preloadClaims } from "@/app/api/claims";
+import { preloadStaff, preloadTeamLeads } from "@/app/api/staff";
+import StaffSearch from "@/components/StaffSearch";
+import TeamSearch from "@/components/TeamSearch";
+import UserSearch from "@/components/UserSearch";
+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: "User",
+ };
+
+
+ const User: React.FC = async () => {
+ const { t } = await getServerI18n("User");
+ // preloadTeamLeads();
+ // preloadStaff();
+ return (
+ <>
+
+
+ {t("User")}
+
+ }
+ LinkComponent={Link}
+ href="/settings/team/create"
+ >
+ {t("Create User")}
+
+
+
+ }>
+
+
+
+ >
+ );
+ };
+
+ export default User;
\ No newline at end of file
diff --git a/src/app/api/skill/actions.ts b/src/app/api/skill/actions.ts
index eda7f39..6a0deca 100644
--- a/src/app/api/skill/actions.ts
+++ b/src/app/api/skill/actions.ts
@@ -5,6 +5,13 @@ import { serverFetchJson } from "@/app/utils/fetchUtil";
import { BASE_API_URL } from "@/config/api";
import { cache } from "react";
+export interface CreateSkillInputs {
+ id?: number;
+ name: String;
+ code: String;
+ description: String;
+}
+
export interface comboProp {
id: any;
label: string;
@@ -18,4 +25,13 @@ export const fetchSkillCombo = cache(async () => {
return serverFetchJson(`${BASE_API_URL}/skill/combo`, {
next: { tags: ["skill"] },
});
- });
\ No newline at end of file
+ });
+
+
+export const saveSkill = async (data: CreateSkillInputs) => {
+ return serverFetchJson(`${BASE_API_URL}/skill/save`, {
+ method: "POST",
+ body: JSON.stringify(data),
+ headers: { "Content-Type": "application/json" },
+ });
+ };
\ No newline at end of file
diff --git a/src/app/api/skill/index.ts b/src/app/api/skill/index.ts
new file mode 100644
index 0000000..cf6ebec
--- /dev/null
+++ b/src/app/api/skill/index.ts
@@ -0,0 +1,22 @@
+import { serverFetchJson } from "@/app/utils/fetchUtil";
+import { BASE_API_URL } from "@/config/api";
+import { cache } from "react";
+import "server-only";
+
+export interface SkillResult {
+ action: any;
+ id: number;
+ name: string;
+ description: string;
+ code: string;
+ }
+
+ export const preloadSkill = () => {
+ fetchSkill();
+ };
+
+ export const fetchSkill = cache(async () => {
+ return serverFetchJson(`${BASE_API_URL}/skill`, {
+ next: { tags: ["sill"] },
+ });
+ });
\ No newline at end of file
diff --git a/src/app/api/staff/actions.ts b/src/app/api/staff/actions.ts
index 9416d2d..a2235d6 100644
--- a/src/app/api/staff/actions.ts
+++ b/src/app/api/staff/actions.ts
@@ -1,5 +1,5 @@
"use server";
-import { serverFetchJson } from "@/app/utils/fetchUtil";
+import { serverFetchJson, serverFetchWithNoContent } from "@/app/utils/fetchUtil";
import { BASE_API_URL } from "@/config/api";
import { StaffResult, data } from ".";
import { cache } from "react";
@@ -59,8 +59,8 @@ export const testing = async (data: CreateStaffInputs) => {
});
};
-export const deleteStaff = async (data: StaffResult) => {
- return serverFetchJson(`${BASE_API_URL}/staffs/delete/${data.id}`, {
+export const deleteStaff = async (id: number) => {
+ return serverFetchWithNoContent(`${BASE_API_URL}/staffs/delete/${id}`, {
method: "DELETE",
// body: JSON.stringify(data),
headers: { "Content-Type": "application/json" },
diff --git a/src/app/api/team/actions.ts b/src/app/api/team/actions.ts
index 28496d0..47e1a82 100644
--- a/src/app/api/team/actions.ts
+++ b/src/app/api/team/actions.ts
@@ -1,5 +1,5 @@
"use server";
-import { serverFetchJson } from "@/app/utils/fetchUtil";
+import { serverFetchJson, serverFetchWithNoContent } from "@/app/utils/fetchUtil";
import { BASE_API_URL } from "@/config/api";
import { cache } from "react";
import { TeamResult } from ".";
@@ -53,10 +53,9 @@ export const saveTeam = async (data: CreateTeamInputs) => {
};
-export const deleteTeam = async (data: TeamResult) => {
- return serverFetchJson(`${BASE_API_URL}/team/delete/${data.id}`, {
+export const deleteTeam = async (id: number) => {
+ return serverFetchWithNoContent(`${BASE_API_URL}/team/delete/${id}`, {
method: "DELETE",
- // body: JSON.stringify(data),
headers: { "Content-Type": "application/json" },
});
};
diff --git a/src/app/api/user/actions.ts b/src/app/api/user/actions.ts
new file mode 100644
index 0000000..5df734a
--- /dev/null
+++ b/src/app/api/user/actions.ts
@@ -0,0 +1,27 @@
+"use server";
+
+import { serverFetchJson, serverFetchWithNoContent } from "@/app/utils/fetchUtil";
+import { BASE_API_URL } from "@/config/api";
+import { revalidateTag } from "next/cache";
+import { UserDetail, UserResult } from ".";
+import { cache } from "react";
+
+export interface UserInputs {
+ username: string;
+ firstname: string;
+ lastname: string;
+}
+
+
+export const fetchUserDetails = cache(async (id: number) => {
+ return serverFetchJson(`${BASE_API_URL}/user/${id}`, {
+ next: { tags: ["user"] },
+ });
+ });
+
+export const deleteUser = async (id: number) => {
+ return serverFetchWithNoContent(`${BASE_API_URL}/user/${id}`, {
+ method: "DELETE",
+ headers: { "Content-Type": "application/json" },
+ });
+ };
\ No newline at end of file
diff --git a/src/app/api/user/index.ts b/src/app/api/user/index.ts
new file mode 100644
index 0000000..9a6065b
--- /dev/null
+++ b/src/app/api/user/index.ts
@@ -0,0 +1,43 @@
+import { serverFetchJson } from "@/app/utils/fetchUtil";
+import { BASE_API_URL } from "@/config/api";
+import { cache } from "react";
+import "server-only";
+
+
+export interface UserResult {
+ action: any;
+ id: number;
+ name: string;
+ locale: string;
+ username: string;
+ fullName: string;
+ firstname: string;
+ lastname: string;
+ title: string;
+ department: string;
+ email: string;
+ phone1: string;
+ phone2: string;
+ remarks: string;
+ }
+
+// export interface DetailedUser extends UserResult {
+// username: string;
+// password: string
+// }
+
+export interface UserDetail {
+ authIds: number[];
+ data: UserResult;
+ groupIds: number[];
+ }
+
+ export const preloadUser = () => {
+ fetchUser();
+ };
+
+ export const fetchUser = cache(async () => {
+ return serverFetchJson(`${BASE_API_URL}/user`, {
+ next: { tags: ["user"] },
+ });
+ });
\ No newline at end of file
diff --git a/src/components/CreateSkill/CreateSkill.tsx b/src/components/CreateSkill/CreateSkill.tsx
new file mode 100644
index 0000000..d264b34
--- /dev/null
+++ b/src/components/CreateSkill/CreateSkill.tsx
@@ -0,0 +1,122 @@
+"use client";
+
+import {
+ FieldErrors,
+ FormProvider,
+ SubmitErrorHandler,
+ SubmitHandler,
+ useForm,
+} from "react-hook-form";
+import { Button, Stack, Tab, Tabs, TabsProps, Typography } from "@mui/material";
+import { Check, Close, RestartAlt } from "@mui/icons-material";
+import { useCallback, useState } from "react";
+import { useRouter, useSearchParams } from "next/navigation";
+import { useTranslation } from "react-i18next";
+import { CreateSkillInputs, saveSkill } from "@/app/api/skill/actions";
+import { Error } from "@mui/icons-material";
+import SkillInfo from "./SkillInfo";
+
+interface Props {}
+
+const CreateSkill: React.FC = () => {
+ const formProps = useForm();
+ const [serverError, setServerError] = useState("");
+ const router = useRouter();
+ const { t } = useTranslation();
+ const [tabIndex, setTabIndex] = useState(0);
+ const errors = formProps.formState.errors;
+
+ const onSubmit = useCallback>(
+ async (data) => {
+ try {
+ console.log(data);
+ await saveSkill(data)
+ router.replace(`/settings/skill`)
+ } catch (e) {
+ console.log(e);
+ setServerError(t("An error has occurred. Please try again later."));
+ }
+ },
+ [router]
+ );
+
+ const handleCancel = () => {
+ router.back();
+ };
+
+// const handleReset = useCallback(() => {
+// console.log(defaultValues)
+// }, [defaultValues])
+
+ const handleTabChange = useCallback>(
+ (_e, newValue) => {
+ setTabIndex(newValue);
+ },
+ []
+ );
+
+ const hasErrorsInTab = (
+ tabIndex: number,
+ errors: FieldErrors
+ ) => {
+ switch (tabIndex) {
+ case 0:
+ return Object.keys(errors).length > 0;
+ default:
+ false;
+ }
+ };
+ return (
+ <>
+
+
+
+
+ ) : undefined
+ }
+ iconPosition="end"
+ />
+ {/* */}
+
+ {serverError && (
+
+ {serverError}
+
+ )}
+ {tabIndex === 0 && }
+
+ }
+ onClick={handleCancel}
+ >
+ {t("Cancel")}
+
+ }
+ type="submit"
+ // disabled={Boolean(formProps.watch("isGridEditing"))}
+ >
+ {t("Confirm")}
+
+
+
+
+ >
+ );
+};
+
+export default CreateSkill;
diff --git a/src/components/CreateSkill/CreateSkillLoading.tsx b/src/components/CreateSkill/CreateSkillLoading.tsx
new file mode 100644
index 0000000..f7d17bf
--- /dev/null
+++ b/src/components/CreateSkill/CreateSkillLoading.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 CreateSkillLoading: React.FC = () => {
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
+ CreateSkill
+
+
+
+
+
+
+
+
+
+ >
+ );
+};
+
+export default CreateSkillLoading;
diff --git a/src/components/CreateSkill/CreateSkillWrapper.tsx b/src/components/CreateSkill/CreateSkillWrapper.tsx
new file mode 100644
index 0000000..f2f667a
--- /dev/null
+++ b/src/components/CreateSkill/CreateSkillWrapper.tsx
@@ -0,0 +1,19 @@
+import React from "react";
+import CreateSkill from "./CreateSkill";
+import CreateSkillLoading from "./CreateSkillLoading";
+import { fetchStaff, fetchTeamLeads } from "@/app/api/staff";
+import { useSearchParams } from "next/navigation";
+
+interface SubComponents {
+ Loading: typeof CreateSkillLoading;
+}
+
+const CreateSkillWrapper: React.FC & SubComponents = async () => {
+
+
+ return ;
+};
+
+CreateSkillWrapper.Loading = CreateSkillLoading;
+
+export default CreateSkillWrapper;
diff --git a/src/components/CreateSkill/SkillInfo.tsx b/src/components/CreateSkill/SkillInfo.tsx
new file mode 100644
index 0000000..be9724d
--- /dev/null
+++ b/src/components/CreateSkill/SkillInfo.tsx
@@ -0,0 +1,90 @@
+"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 { CreateSkillInputs } from "@/app/api/skill/actions";
+
+const SkillInfo: React.FC = (
+) => {
+ const { t } = useTranslation();
+ const {
+ register,
+ formState: { errors, defaultValues },
+ control,
+ reset,
+ resetField,
+ setValue,
+ } = useFormContext();
+
+ const resetSkill = useCallback(() => {
+ console.log(defaultValues);
+ if (defaultValues !== undefined) {
+ resetField("name");
+ }
+ }, [defaultValues]);
+
+ return (
+ <>
+
+
+
+
+ {t("Skill Info")}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+};
+export default SkillInfo;
diff --git a/src/components/CreateSkill/index.ts b/src/components/CreateSkill/index.ts
new file mode 100644
index 0000000..044c4cf
--- /dev/null
+++ b/src/components/CreateSkill/index.ts
@@ -0,0 +1 @@
+export { default } from "./CreateSkillWrapper";
diff --git a/src/components/CreateTeam/CreateTeam.tsx b/src/components/CreateTeam/CreateTeam.tsx
index 93b585e..64159c0 100644
--- a/src/components/CreateTeam/CreateTeam.tsx
+++ b/src/components/CreateTeam/CreateTeam.tsx
@@ -89,7 +89,7 @@ const hasErrorsInTab = (
}
iconPosition="end"
/>
-
+
{serverError && (
diff --git a/src/components/CreateTeam/StaffAllocation.tsx b/src/components/CreateTeam/StaffAllocation.tsx
index c51b839..bbd768c 100644
--- a/src/components/CreateTeam/StaffAllocation.tsx
+++ b/src/components/CreateTeam/StaffAllocation.tsx
@@ -18,9 +18,21 @@ import { StaffResult } 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 {
+ 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';
+import StarsIcon from "@mui/icons-material/Stars";
export interface Props {
allStaffs: StaffResult[];
@@ -35,16 +47,15 @@ const StaffAllocation: React.FC = ({ allStaffs: staff }) => {
reset,
resetField,
} = useFormContext();
-
+
const initialStaffs = staff.map((s) => ({ ...s }));
-// console.log(initialStaffs)
+ // console.log(initialStaffs)
const [filteredStaff, setFilteredStaff] = useState(initialStaffs);
const [selectedStaff, setSelectedStaff] = useState(
initialStaffs.filter((s) => getValues("addStaffIds")?.includes(s.id))
);
- const [seletedTeamLead, setSeletedTeamLead] = useState()
- // Adding / Removing staff
+ // Adding / Removing staff
const addStaff = useCallback((staff: StaffResult) => {
setSelectedStaff((s) => [...s, staff]);
}, []);
@@ -53,27 +64,31 @@ const StaffAllocation: React.FC = ({ allStaffs: staff }) => {
setSelectedStaff((s) => s.filter((s) => s.id !== staff.id));
}, []);
- const setTeamLead = useCallback((staff: StaffResult) => {
-
- setSeletedTeamLead(staff.id)
- const rearrangedList = getValues("addStaffIds").reduce((acc, num, index) => {
- if (num === staff.id && index !== 0) {
+ const setTeamLead = useCallback(
+ (staff: StaffResult) => {
+ 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) => {
+ 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[]);
+ console.log(rearrangedStaff);
+ setSelectedStaff(rearrangedStaff as StaffResult[]);
- setValue("addStaffIds", rearrangedList)
- }, [addStaff, selectedStaff]);
+ setValue("addStaffIds", rearrangedList);
+ },
+ [addStaff, selectedStaff]
+ );
const clearSubsidiary = useCallback(() => {
if (defaultValues !== undefined) {
@@ -86,7 +101,7 @@ const StaffAllocation: React.FC = ({ allStaffs: staff }) => {
// Sync with form
useEffect(() => {
- console.log(selectedStaff)
+ console.log(selectedStaff);
setValue(
"addStaffIds",
selectedStaff.map((s) => s.id)
@@ -94,7 +109,7 @@ const StaffAllocation: React.FC = ({ allStaffs: staff }) => {
}, [selectedStaff, setValue]);
useEffect(() => {
- console.log(selectedStaff)
+ console.log(selectedStaff);
}, [selectedStaff]);
const StaffPoolColumns = useMemo[]>(
@@ -107,7 +122,7 @@ const StaffAllocation: React.FC = ({ allStaffs: staff }) => {
},
{ label: t("Staff Id"), name: "staffId" },
{ label: t("Staff Name"), name: "name" },
- { label: t("Current Position"), name: "currentPosition" },
+ { label: t("Position"), name: "currentPosition" },
],
[addStaff, t]
);
@@ -122,7 +137,7 @@ const StaffAllocation: React.FC = ({ allStaffs: staff }) => {
},
{ label: t("Staff Id"), name: "staffId" },
{ label: t("Staff Name"), name: "name" },
- { label: t("Current Position"), name: "currentPosition" },
+ { label: t("Position"), name: "currentPosition" },
{
label: t("Team Lead"),
name: "action",
@@ -144,16 +159,16 @@ const StaffAllocation: React.FC = ({ allStaffs: staff }) => {
}, []);
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))
- // })
- // );
+ setFilteredStaff(
+ initialStaffs.filter((i) => {
+ const q = query.toLowerCase();
+ return (
+ i.staffId.toLowerCase().includes(q) ||
+ i.name.toLowerCase().includes(q) ||
+ i.currentPosition.toLowerCase().includes(q)
+ );
+ })
+ );
}, [staff, query]);
const resetStaff = React.useCallback(() => {
@@ -161,8 +176,7 @@ const StaffAllocation: React.FC = ({ allStaffs: staff }) => {
clearSubsidiary();
}, [clearQueryInput, clearSubsidiary]);
- const formProps = useForm({
- });
+ const formProps = useForm({});
// Tab related
const [tabIndex, setTabIndex] = React.useState(0);
@@ -170,7 +184,7 @@ const StaffAllocation: React.FC = ({ allStaffs: staff }) => {
(_e, newValue) => {
setTabIndex(newValue);
},
- [],
+ []
);
return (
@@ -185,48 +199,48 @@ const StaffAllocation: React.FC = ({ allStaffs: staff }) => {
{t("staff")}
-
-
-
-
-
-
-
- ),
- }}
- />
+
+
+
+
+
+
+
+ ),
+ }}
+ />
+
-
-
-
-
-
-
- {tabIndex === 0 && (
-
- )}
- {tabIndex === 1 && (
-
+
+
- )}
-
+
+
+ {tabIndex === 0 && (
+
+ )}
+ {tabIndex === 1 && (
+
+ )}
+
diff --git a/src/components/NavigationContent/NavigationContent.tsx b/src/components/NavigationContent/NavigationContent.tsx
index ad68823..9016052 100644
--- a/src/components/NavigationContent/NavigationContent.tsx
+++ b/src/components/NavigationContent/NavigationContent.tsx
@@ -31,6 +31,8 @@ import { NAVIGATION_CONTENT_WIDTH } from "@/config/uiConfig";
import Logo from "../Logo";
import GroupIcon from '@mui/icons-material/Group';
import BusinessIcon from '@mui/icons-material/Business';
+import ManageAccountsIcon from '@mui/icons-material/ManageAccounts';
+import EmojiEventsIcon from '@mui/icons-material/EmojiEvents';
interface NavigationItem {
icon: React.ReactNode;
@@ -117,10 +119,12 @@ const navigationItems: NavigationItem[] = [
{ icon: , label: "Subsidiary", path: "/settings/subsidiary" },
{ icon: , label: "Staff", path: "/settings/staff" },
{ icon: , label: "Company", path: "/settings/company" },
+ { icon: , label: "Skill", path: "/settings/skill" },
{ icon: , label: "Department", path: "/settings/department" },
{ icon: , label: "Position", path: "/settings/position" },
{ icon: , label: "Salary", path: "/settings/salary" },
{ icon: , label: "Team", path: "/settings/team" },
+ { icon: , label: "User", path: "/settings/user" },
],
},
];
diff --git a/src/components/SkillSearch/SkillSearch.tsx b/src/components/SkillSearch/SkillSearch.tsx
new file mode 100644
index 0000000..01db336
--- /dev/null
+++ b/src/components/SkillSearch/SkillSearch.tsx
@@ -0,0 +1,96 @@
+"use client";
+import React, { useCallback, useEffect, useMemo, useState } from "react";
+import SearchBox, { Criterion } from "../SearchBox/index";
+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 { useRouter } from "next/navigation";
+import { deleteDialog, successDialog } from "../Swal/CustomAlerts";
+import { SkillResult } from "@/app/api/skill";
+
+interface Props {
+ skill: SkillResult[];
+}
+
+type SearchQuery = Partial>;
+type SearchParamNames = keyof SearchQuery;
+
+const SkillSearch: React.FC = ({ skill }) => {
+ const { t } = useTranslation();
+ const [filteredStaff, setFilteredStaff] = useState(skill);
+ const router = useRouter();
+
+ const searchCriteria: Criterion[] = useMemo(
+ () => [
+ {
+ label: t("Staff Name"),
+ paramName: "name",
+ type: "text",
+ },
+ ],
+ [t]
+ );
+
+ const onSkillClick = useCallback(
+ (skill: SkillResult) => {
+ console.log(skill);
+ const id = skill.id;
+ // router.push(`/settings/skill/edit?id=${id}`);
+ },
+ [router, t]
+ );
+
+ const deleteClick = useCallback((skill: SkillResult) => {
+ // deleteDialog(async () => {
+ // await deleteStaff(skill.id);
+ // successDialog("Delete Success", t);
+ // setFilteredStaff((prev) => prev.filter((obj) => obj.id !== skill.id));
+ // }, t);
+ }, []);
+
+ const columns = useMemo[]>(
+ () => [
+ {
+ name: "action",
+ label: t("Actions"),
+ onClick: onSkillClick,
+ buttonIcon: ,
+ },
+ { name: "name", label: t("Name") },
+ { name: "code", label: t("Code") },
+ { name: "description", label: t("Description") },
+ {
+ name: "action",
+ label: t("Actions"),
+ onClick: deleteClick,
+ buttonIcon: ,
+ color: "error",
+ },
+ ],
+ [t, onSkillClick, deleteClick]
+ );
+
+ return (
+ <>
+ {
+ // setFilteredStaff(
+ // skill.filter(
+ // (s) =>
+ // s.skillId.toLowerCase().includes(query.skillId.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),
+ // )
+ // );
+ }}
+ />
+ items={filteredStaff} columns={columns} />
+ >
+ );
+};
+
+export default SkillSearch;
diff --git a/src/components/SkillSearch/SkillSearchLoading.tsx b/src/components/SkillSearch/SkillSearchLoading.tsx
new file mode 100644
index 0000000..a5959e9
--- /dev/null
+++ b/src/components/SkillSearch/SkillSearchLoading.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 SkillSearchLoading: React.FC = () => {
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+};
+
+export default SkillSearchLoading;
diff --git a/src/components/SkillSearch/SkillSearchWrapper.tsx b/src/components/SkillSearch/SkillSearchWrapper.tsx
new file mode 100644
index 0000000..33d0547
--- /dev/null
+++ b/src/components/SkillSearch/SkillSearchWrapper.tsx
@@ -0,0 +1,27 @@
+import React from "react";
+import SkillSearch from "./SkillSearch";
+import SkillSearchLoading from "./SkillSearchLoading";
+import { comboProp, fetchCompanyCombo } from "@/app/api/companys/actions";
+import { fetchTeamCombo } from "@/app/api/team/actions";
+import { fetchDepartmentCombo } from "@/app/api/departments/actions";
+import { fetchPositionCombo } from "@/app/api/positions/actions";
+import { fetchGradeCombo } from "@/app/api/grades/actions";
+import { fetchSkillCombo } from "@/app/api/skill/actions";
+import { fetchSalaryCombo } from "@/app/api/salarys/actions";
+import { SkillResult, fetchSkill } from "@/app/api/skill";
+// import { preloadStaff } from "@/app/api/staff";
+
+interface SubComponents {
+ Loading: typeof SkillSearchLoading;
+}
+
+const SkillSearchWrapper: React.FC & SubComponents = async () => {
+ const skill = await fetchSkill()
+ console.log(skill);
+
+ return ;
+};
+
+SkillSearchWrapper.Loading = SkillSearchLoading;
+
+export default SkillSearchWrapper;
diff --git a/src/components/SkillSearch/index.ts b/src/components/SkillSearch/index.ts
new file mode 100644
index 0000000..5833a58
--- /dev/null
+++ b/src/components/SkillSearch/index.ts
@@ -0,0 +1 @@
+export { default } from "./SkillSearchWrapper";
diff --git a/src/components/StaffSearch/ConfirmDeleteModal.tsx b/src/components/StaffSearch/ConfirmDeleteModal.tsx
deleted file mode 100644
index abeb962..0000000
--- a/src/components/StaffSearch/ConfirmDeleteModal.tsx
+++ /dev/null
@@ -1,106 +0,0 @@
-"use client";
-import React, { useCallback, useMemo, useState } from "react";
-import Button from "@mui/material/Button";
-import { Card, Modal, Stack, Typography } from "@mui/material";
-import { useTranslation } from "react-i18next";
-import { Add } from "@mui/icons-material";
-import Check from "@mui/icons-material/Check";
-import Close from "@mui/icons-material/Close";
-import { TSMS_BUTTON_THEME } from "@/theme/colorConst";
-import { ThemeProvider } from "@emotion/react";
-
-interface Props {
- isOpen: boolean;
- onConfirm: (data: any) => void;
- onCancel: (data: any | null) => void;
- // staff: StaffResult[];
-}
-
-const ConfirmModal: React.FC = ({ ...props }) => {
- const { t } = useTranslation();
- return (
- <>
-
-
- <>
-
- {t("Confirm")}
-
- <>
-
- {t("Are You Sure")}
-
- >
- {/* */}
-
- }
- sx={{
- flex: 1,
- ml: 5,
- mr: 2,
- mt: 4,
- justifyContent: "space-between",
- }}
- onClick={props.onConfirm}
- // LinkComponent={Link}
- // href="/settings/department/new"
- >
- Proceed
-
- }
- sx={{
- flex: 1,
- mr: 5,
- mt: 4,
- justifyContent: "space-between",
- }}
- color="warning"
- onClick={props.onCancel}
- // LinkComponent={Link}
- // href="/settings/department/new"
- >
- Cancel
-
-
- {/* */}
- >
-
-
- >
- );
-};
-
-export default ConfirmModal;
diff --git a/src/components/StaffSearch/StaffSearch.tsx b/src/components/StaffSearch/StaffSearch.tsx
index e65cfe7..fc6204d 100644
--- a/src/components/StaffSearch/StaffSearch.tsx
+++ b/src/components/StaffSearch/StaffSearch.tsx
@@ -5,15 +5,11 @@ import SearchBox, { Criterion } from "../SearchBox/index";
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 ConfirmModal from "./ConfirmDeleteModal";
+import DeleteIcon from "@mui/icons-material/Delete";
import { deleteStaff } from "@/app/api/staff/actions";
import { useRouter } from "next/navigation";
+import { deleteDialog, successDialog } from "../Swal/CustomAlerts";
-interface combo {
- id: any;
- label: string;
-}
interface Props {
staff: StaffResult[];
}
@@ -24,8 +20,6 @@ type SearchParamNames = keyof SearchQuery;
const StaffSearch: React.FC = ({ staff }) => {
const { t } = useTranslation();
const [filteredStaff, setFilteredStaff] = useState(staff);
- const [data, setData] = useState();
- const [isOpen, setIsOpen] = useState(false);
const router = useRouter();
const searchCriteria: Criterion[] = useMemo(
@@ -41,10 +35,10 @@ const StaffSearch: React.FC = ({ staff }) => {
paramName: "name",
type: "text",
},
- {
- label: t("Staff ID"),
- paramName: "staffId",
- type: "text"
+ {
+ label: t("Staff ID"),
+ paramName: "staffId",
+ type: "text",
},
{
label: t("Grade"),
@@ -59,39 +53,26 @@ const StaffSearch: React.FC = ({ staff }) => {
options: ["pos1", "CEO"],
},
],
- [t],
+ [t]
);
- const onStaffClick = useCallback((staff: StaffResult) => {
- console.log(staff);
- const id = staff.id
- router.push(`/settings/staff/edit?id=${id}`);
- }, [router, t]);
-
- const deleteClick = (staff: StaffResult) => {
- console.log(staff);
- setData(staff)
- setIsOpen(!isOpen)
- };
-
- const onConfirm = useCallback(async (staff: StaffResult) => {
- console.log(staff);
- if (data)
- await deleteStaff(data)
- setIsOpen(false)
- window.location.reload;
- }, [deleteStaff, data]);
+ const onStaffClick = useCallback(
+ (staff: StaffResult) => {
+ console.log(staff);
+ const id = staff.id;
+ router.push(`/settings/staff/edit?id=${id}`);
+ },
+ [router, t]
+ );
- const onCancel = useCallback((staff: StaffResult) => {
- console.log(staff);
- setIsOpen(false)
+ const deleteClick = useCallback((staff: StaffResult) => {
+ deleteDialog(async () => {
+ await deleteStaff(staff.id);
+ successDialog("Delete Success", t);
+ setFilteredStaff((prev) => prev.filter((obj) => obj.id !== staff.id));
+ }, t);
}, []);
- // useEffect(() => {
- // console.log("id");
- // console.log(id);
- // }, [id]);
-
const columns = useMemo[]>(
() => [
{
@@ -110,34 +91,30 @@ const StaffSearch: React.FC = ({ staff }) => {
label: t("Actions"),
onClick: deleteClick,
buttonIcon: ,
+ color: "error",
},
],
- [t, onStaffClick, deleteClick],
+ [t, onStaffClick, deleteClick]
);
return (
<>
{
+ onSearch={(query) => {
setFilteredStaff(
staff.filter(
- (s) =>
- s.staffId.toLowerCase().includes(query.staffId.toLowerCase()) &&
- s.name.toLowerCase().includes(query.name.toLowerCase())
+ (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),
)
- )
+ );
}}
/>
items={filteredStaff} columns={columns} />
-
>
);
};
diff --git a/src/components/TeamSearch/ConfirmDeleteModal.tsx b/src/components/TeamSearch/ConfirmDeleteModal.tsx
deleted file mode 100644
index a5e7ed0..0000000
--- a/src/components/TeamSearch/ConfirmDeleteModal.tsx
+++ /dev/null
@@ -1,105 +0,0 @@
-"use client";
-import React, { useCallback, useMemo, useState } from "react";
-import Button from "@mui/material/Button";
-import { Card, Modal, Stack, Typography } from "@mui/material";
-import { useTranslation } from "react-i18next";
-import { Add } from "@mui/icons-material";
-import Check from "@mui/icons-material/Check";
-import Close from "@mui/icons-material/Close";
-import { TSMS_BUTTON_THEME } from "@/theme/colorConst";
-import { ThemeProvider } from "@emotion/react";
-
-interface Props {
- isOpen: boolean;
- onConfirm: (data: any) => void;
- onCancel: (data: any | null) => void;
-}
-
-const ConfirmModal: React.FC = ({ ...props }) => {
- const { t } = useTranslation();
- return (
- <>
-
-
- <>
-
- {t("Confirm")}
-
- <>
-
- {t("Are You Sure")}
-
- >
- {/* */}
-
- }
- sx={{
- flex: 1,
- ml: 5,
- mr: 2,
- mt: 4,
- justifyContent: "space-between",
- }}
- onClick={props.onConfirm}
- // LinkComponent={Link}
- // href="/settings/department/new"
- >
- Proceed
-
- }
- sx={{
- flex: 1,
- mr: 5,
- mt: 4,
- justifyContent: "space-between",
- }}
- color="warning"
- onClick={props.onCancel}
- // LinkComponent={Link}
- // href="/settings/department/new"
- >
- Cancel
-
-
- {/* */}
- >
-
-
- >
- );
-};
-
-export default ConfirmModal;
diff --git a/src/components/TeamSearch/TeamSearch.tsx b/src/components/TeamSearch/TeamSearch.tsx
index b2cc9e8..a1db872 100644
--- a/src/components/TeamSearch/TeamSearch.tsx
+++ b/src/components/TeamSearch/TeamSearch.tsx
@@ -6,12 +6,10 @@ import { useCallback, 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 DeleteIcon from "@mui/icons-material/Delete";
import { useRouter } from "next/navigation";
-import ConfirmModal from "./ConfirmDeleteModal";
import { deleteTeam } from "@/app/api/team/actions";
-
+import { deleteDialog, successDialog } from "../Swal/CustomAlerts";
interface Props {
team: TeamResult[];
@@ -20,109 +18,90 @@ type SearchQuery = Partial>;
type SearchParamNames = keyof SearchQuery;
const TeamSearch: React.FC = ({ team }) => {
- const { t } = useTranslation();
- const [filteredTeam, setFilteredTeam] = useState(team);
- const [data, setData] = useState();
- const [isOpen, setIsOpen] = useState(false);
- const router = useRouter();
-
- const searchCriteria: Criterion[] = 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 { t } = useTranslation();
+ const [filteredTeam, setFilteredTeam] = useState(team);
+ const router = useRouter();
- const onTeamClick = useCallback((team: TeamResult) => {
- console.log(team);
- const id = team.id
- router.push(`/settings/team/edit?id=${id}`);
- }, [router, t]);
-
- // const onDeleteClick = useCallback((team: TeamResult) => {
- // console.log(team);
- // deleteTeam
+ const searchCriteria: Criterion[] = 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]
+ );
- // }, [router, t]);
+ const onTeamClick = useCallback(
+ (team: TeamResult) => {
+ console.log(team);
+ const id = team.id;
+ router.push(`/settings/team/edit?id=${id}`);
+ },
+ [router, t]
+ );
- const onDeleteClick = (team: TeamResult) => {
- console.log(team);
- setData(team)
- setIsOpen(!isOpen)
- };
+ const onDeleteClick = useCallback((team: TeamResult) => {
+ deleteDialog(async () => {
+ await deleteTeam(team.id);
- const onConfirm = useCallback(async (team: TeamResult) => {
- console.log(team);
- if (data)
- await deleteTeam(data)
- setIsOpen(false)
- window.location.reload;
- }, [deleteTeam, data]);
+ successDialog("Delete Success", t);
- const onCancel = useCallback(() => {
- setIsOpen(false)
- }, []);
+ setFilteredTeam((prev) => prev.filter((obj) => obj.id !== team.id));
+ }, t);
+ }, []);
- const columns = useMemo[]>(
- () => [
- {
- name: "action",
- label: t("Edit"),
- onClick: onTeamClick,
- buttonIcon: ,
- },
- { name: "name", label: t("Name") },
- { name: "code", label: t("Code") },
- { name: "description", label: t("description") },
- {
- name: "action",
- label: t("Delete"),
- onClick: onDeleteClick,
- buttonIcon: ,
- },
- ],
- [t],
- );
+ const columns = useMemo[]>(
+ () => [
+ {
+ name: "action",
+ label: t("Edit"),
+ onClick: onTeamClick,
+ buttonIcon: ,
+ },
+ { name: "name", label: t("Name") },
+ { name: "code", label: t("Code") },
+ { name: "description", label: t("description") },
+ { name: "staffName", label: t("TeamLead") },
+ {
+ name: "action",
+ label: t("Delete"),
+ onClick: onDeleteClick,
+ buttonIcon: ,
+ color: "error"
+ },
+ ],
+ [t]
+ );
return (
- <>
-
+ {
- // 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),
- // )
- // )
+ onSearch={(query) => {
+ setFilteredTeam(
+ team.filter(
+ (t) =>
+ t.name.toLowerCase().includes(query.name.toLowerCase()) &&
+ t.code.toLowerCase().includes(query.code.toLowerCase()) &&
+ t.description.toLowerCase().includes(query.description.toLowerCase())
+ )
+ )
}}
/>
items={filteredTeam} columns={columns} />
-
-
- >
+ >
);
};
export default TeamSearch;
diff --git a/src/components/UserSearch/UserSearch.tsx b/src/components/UserSearch/UserSearch.tsx
new file mode 100644
index 0000000..095c544
--- /dev/null
+++ b/src/components/UserSearch/UserSearch.tsx
@@ -0,0 +1,98 @@
+"use client";
+
+import SearchBox, { Criterion } from "../SearchBox";
+import { useCallback, 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 { useRouter } from "next/navigation";
+import { deleteDialog, successDialog } from "../Swal/CustomAlerts";
+import { UserResult } from "@/app/api/user";
+import { deleteUser } from "@/app/api/user/actions";
+
+interface Props {
+ users: UserResult[];
+}
+type SearchQuery = Partial>;
+type SearchParamNames = keyof SearchQuery;
+
+const UserSearch: React.FC = ({ users }) => {
+ const { t } = useTranslation();
+ const [filteredUser, setFilteredUser] = useState(users);
+ const router = useRouter();
+
+ const searchCriteria: Criterion[] = useMemo(
+ () => [
+ {
+ label: t("User Name"),
+ paramName: "title",
+ type: "text",
+ },
+ ],
+ [t]
+ );
+
+ const onUserClick = useCallback(
+ (users: UserResult) => {
+ console.log(users);
+ router.push(`/settings/user/edit?id=${users.id}`)
+ },
+ [router, t]
+ );
+
+ const onDeleteClick = useCallback((users: UserResult) => {
+ deleteDialog(async () => {
+ await deleteUser(users.id);
+
+ successDialog("Delete Success", t);
+
+ setFilteredUser((prev) => prev.filter((obj) => obj.id !== users.id));
+ }, t);
+ }, []);
+
+ const columns = useMemo[]>(
+ () => [
+ {
+ name: "action",
+ label: t("Edit"),
+ onClick: onUserClick,
+ buttonIcon: ,
+ },
+ { name: "name", label: t("UserName") },
+ { name: "fullName", label: t("FullName") },
+ { name: "title", label: t("Title") },
+ { name: "department", label: t("Department") },
+ { name: "email", label: t("Email") },
+ { name: "phone1", label: t("Phone") },
+ {
+ name: "action",
+ label: t("Delete"),
+ onClick: onDeleteClick,
+ buttonIcon: ,
+ color: "error"
+ },
+ ],
+ [t]
+ );
+
+ return (
+ <>
+ {
+ // setFilteredUser(
+ // users.filter(
+ // (t) =>
+ // t.name.toLowerCase().includes(query.name.toLowerCase()) &&
+ // t.code.toLowerCase().includes(query.code.toLowerCase()) &&
+ // t.description.toLowerCase().includes(query.description.toLowerCase())
+ // )
+ // )
+ }}
+ />
+ items={filteredUser} columns={columns} />
+ >
+ );
+};
+export default UserSearch;
diff --git a/src/components/UserSearch/UserSearchLoading.tsx b/src/components/UserSearch/UserSearchLoading.tsx
new file mode 100644
index 0000000..535a751
--- /dev/null
+++ b/src/components/UserSearch/UserSearchLoading.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 UserSearchLoading: React.FC = () => {
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+};
+
+export default UserSearchLoading;
diff --git a/src/components/UserSearch/UserSearchWrapper.tsx b/src/components/UserSearch/UserSearchWrapper.tsx
new file mode 100644
index 0000000..beaef92
--- /dev/null
+++ b/src/components/UserSearch/UserSearchWrapper.tsx
@@ -0,0 +1,19 @@
+import React from "react";
+import UserSearch from "./UserSearch";
+import UserSearchLoading from "./UserSearchLoading";
+import { UserResult, fetchUser } from "@/app/api/user";
+
+interface SubComponents {
+ Loading: typeof UserSearchLoading;
+}
+
+const UserSearchWrapper: React.FC & SubComponents = async () => {
+const users = await fetchUser()
+ console.log(users);
+
+ return ;
+};
+
+UserSearchWrapper.Loading = UserSearchLoading;
+
+export default UserSearchWrapper;
diff --git a/src/components/UserSearch/index.ts b/src/components/UserSearch/index.ts
new file mode 100644
index 0000000..c2e98d7
--- /dev/null
+++ b/src/components/UserSearch/index.ts
@@ -0,0 +1 @@
+export { default } from "./UserSearchWrapper";