diff --git a/src/app/(main)/settings/group/edit/page.tsx b/src/app/(main)/settings/group/edit/page.tsx
index ae51d16..9055358 100644
--- a/src/app/(main)/settings/group/edit/page.tsx
+++ b/src/app/(main)/settings/group/edit/page.tsx
@@ -8,7 +8,7 @@ export const metadata: Metadata = {
title: "Edit User Group",
};
-const Positions: React.FC = async () => {
+const Group: React.FC = async () => {
const { t } = await getServerI18n("group");
// Preload necessary dependencies
@@ -23,4 +23,4 @@ const Positions: React.FC = async () => {
);
};
-export default Positions;
\ No newline at end of file
+export default Group;
\ No newline at end of file
diff --git a/src/app/(main)/settings/staff/create/page.tsx b/src/app/(main)/settings/staff/create/page.tsx
index bc40f6a..24f3589 100644
--- a/src/app/(main)/settings/staff/create/page.tsx
+++ b/src/app/(main)/settings/staff/create/page.tsx
@@ -22,7 +22,7 @@ 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 CreateStaff from "@/components/CreateStaff";
interface CreateCustomInputs {
projectCode: string;
@@ -31,23 +31,17 @@ interface CreateCustomInputs {
// const Title = ["title1", "title2"];
-const CreateStaff: React.FC = async () => {
+const CreateStaffPage: 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 Staff")}
-
+
>
);
};
-export default CreateStaff;
+export default CreateStaffPage;
diff --git a/src/app/api/positions/actions.ts b/src/app/api/positions/actions.ts
index 8826cc7..3caf1ab 100644
--- a/src/app/api/positions/actions.ts
+++ b/src/app/api/positions/actions.ts
@@ -15,6 +15,8 @@ export interface combo {
}
export interface CreatePositionInputs {
+ positionCode: string;
+ positionName: string;
code: string;
name: string;
description: string;
diff --git a/src/app/api/user/actions.ts b/src/app/api/user/actions.ts
index 4d353c3..fff3da4 100644
--- a/src/app/api/user/actions.ts
+++ b/src/app/api/user/actions.ts
@@ -11,6 +11,12 @@ export interface UserInputs {
email: string;
}
+export interface PasswordInputs {
+ password: string;
+ newPassword: string;
+ newPasswordCheck: string;
+}
+
export const fetchUserDetails = cache(async (id: number) => {
return serverFetchJson(`${BASE_API_URL}/user/${id}`, {
@@ -31,4 +37,12 @@ export const deleteUser = async (id: number) => {
method: "DELETE",
headers: { "Content-Type": "application/json" },
});
+ };
+
+export const changePassword = async (data: any) => {
+ return serverFetchJson(`${BASE_API_URL}/user/change-password`, {
+ method: "PATCH",
+ body: JSON.stringify(data),
+ headers: { "Content-Type": "application/json" },
+ });
};
\ No newline at end of file
diff --git a/src/components/AppBar/Profile.tsx b/src/components/AppBar/Profile.tsx
index 8cb49cf..7b48190 100644
--- a/src/components/AppBar/Profile.tsx
+++ b/src/components/AppBar/Profile.tsx
@@ -10,6 +10,7 @@ import Divider from "@mui/material/Divider";
import Typography from "@mui/material/Typography";
import { useTranslation } from "react-i18next";
import { signOut } from "next-auth/react";
+import { useRouter } from "next/navigation";
type Props = Pick;
@@ -26,6 +27,7 @@ const Profile: React.FC = ({ avatarImageSrc, profileName }) => {
};
const { t } = useTranslation("login");
+ const router = useRouter();
return (
<>
@@ -52,6 +54,7 @@ const Profile: React.FC = ({ avatarImageSrc, profileName }) => {
{profileName}
+
>
diff --git a/src/components/CreateStaff/CreateStaff.tsx b/src/components/CreateStaff/CreateStaff.tsx
index 94e7f71..00fa507 100644
--- a/src/components/CreateStaff/CreateStaff.tsx
+++ b/src/components/CreateStaff/CreateStaff.tsx
@@ -22,7 +22,6 @@ import { fetchSkillCombo } from "@/app/api/skill/actions";
import { fetchSalaryCombo } from "@/app/api/salarys/actions";
interface Field {
- // subtitle: string;
id: string;
label: string;
type: string;
@@ -33,12 +32,6 @@ interface Field {
options?: any[];
readOnly?: boolean;
}
-
-interface formProps {
- Title?: string[];
- // fieldLists: Field[][];
-}
-
export interface comboItem {
company: comboProp[];
team: comboProp[];
@@ -49,101 +42,14 @@ export interface comboItem {
salary: comboProp[];
}
-const CreateStaff: React.FC = ({ Title }) => {
- // const router = useRouter();
- const { t } = useTranslation();
- const [companyCombo, setCompanyCombo] = useState();
- const [teamCombo, setTeamCombo] = useState();
- const [departmentCombo, setDepartmentCombo] = useState();
- const [positionCombo, setPositionCombo] = useState();
- const [gradeCombo, setGradeCombo] = useState();
- const [skillCombo, setSkillCombo] = useState();
- const [salaryCombo, setSalaryCombo] = useState();
- // const [serverError, setServerError] = useState("");
-
- let comboItem: comboItem = {
- company: [],
- team: [],
- department: [],
- position: [],
- grade: [],
- skill: [],
- salary: [],
- };
-
- const fetchCompany = async () => {
- await fetchCompanyCombo().then((data) => {
- if (data) setCompanyCombo(data.records);
- });
- }
-
- const fetchTeam = async () => {
- await fetchTeamCombo().then((data) => {
- if (data) setTeamCombo(data.records);
- });
- }
-
- const fetchDepartment = async () => {
- await fetchDepartmentCombo().then((data) => {
- if (data) setDepartmentCombo(data.records);
- });
- }
-
- const fetchPosition = async () => {
- await fetchPositionCombo().then((data) => {
- if (data) setPositionCombo(data.records);
- });
- }
-
- const fetchGrade = async () => {
- await fetchGradeCombo().then((data) => {
- if (data) setGradeCombo(data.records);
- });
- }
-
- const fetchSkill = async () => {
- await fetchSkillCombo().then((data) => {
- if (data) setSkillCombo(data.records);
- });
- }
-
- const fetchSalary = async () => {
- await fetchSalaryCombo().then((data) => {
- if (data) setSalaryCombo(data.records);
- });
- }
-
- useEffect(() => {
- fetchCompany()
- fetchTeam()
- fetchDepartment()
- fetchPosition()
- fetchGrade()
- fetchSkill()
- fetchSalary()
- }, []);
-
- useEffect(() => {
- if(!companyCombo)
- fetchCompany()
- if(!teamCombo)
- fetchTeam()
- if(!departmentCombo)
- fetchDepartment()
- if(!positionCombo)
- fetchPosition()
- if(!gradeCombo)
- fetchGrade()
- if(!skillCombo)
- fetchSkill()
- if(!salaryCombo)
- fetchSalary()
+interface formProps {
+ Title?: string[];
+ combos: comboItem;
+}
- }, [companyCombo, teamCombo, departmentCombo, positionCombo, gradeCombo, skillCombo, salaryCombo]);
- // useEffect(() => {
- // console.log(companyCombo)
- // }, [companyCombo]);
+const CreateStaff: React.FC = ({ Title, combos }) => {
+ const { t } = useTranslation();
const fieldLists: Field[][] = [
[
@@ -163,49 +69,49 @@ const CreateStaff: React.FC = ({ Title }) => {
id: "companyId",
label: t("Company"),
type: "combo-Obj",
- options: companyCombo || [],
+ options: combos.company || [],
required: true,
},
{
id: "teamId",
label: t("Team"),
type: "combo-Obj",
- options: teamCombo || [],
+ options: combos.team || [],
required: false,
},
{
id: "departmentId",
label: t("Department"),
type: "combo-Obj",
- options: departmentCombo || [],
+ options: combos.department || [],
required: true,
},
{
id: "gradeId",
label: t("Grade"),
type: "combo-Obj",
- options: gradeCombo || [],
+ options: combos.grade || [],
required: false,
},
{
id: "skillSetId",
label: t("Skillset"),
type: "multiSelect-Obj",
- options: skillCombo || [],
+ options: combos.skill || [],
required: false,
},
{
id: "currentPositionId",
label: t("Current Position"),
type: "combo-Obj",
- options: positionCombo || [],
+ options: combos.position || [],
required: true,
},
{
id: "salaryId",
label: t("Salary Point"),
type: "combo-Obj",
- options: salaryCombo || [],
+ options: combos.salary || [],
required: true,
},
// {
@@ -279,7 +185,7 @@ const CreateStaff: React.FC = ({ Title }) => {
id: "joinPositionId",
label: t("Join Position"),
type: "combo-Obj",
- options: positionCombo || [],
+ options: combos.position || [],
required: true,
},
{
diff --git a/src/components/CreateStaff/CreateStaffWrapper.tsx b/src/components/CreateStaff/CreateStaffWrapper.tsx
index c54ad87..825fc35 100644
--- a/src/components/CreateStaff/CreateStaffWrapper.tsx
+++ b/src/components/CreateStaff/CreateStaffWrapper.tsx
@@ -1,17 +1,48 @@
import React from "react";
-import CreateStaff from "./CreateStaff";
+import CreateStaff, { comboItem } from "./CreateStaff";
import CreateStaffLoading from "./CreateStaffLoading";
import { fetchStaff, fetchTeamLeads } from "@/app/api/staff";
import { useSearchParams } from "next/navigation";
-
+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 { fetchCompanyCombo } from "@/app/api/companys/actions";
interface SubComponents {
Loading: typeof CreateStaffLoading;
}
const CreateStaffWrapper: React.FC & SubComponents = async () => {
+ const [
+ CompanyCombo,
+ TeamCombo,
+ DepartmentCombo,
+ PositionCombo,
+ GradeCombo,
+ SkillCombo,
+ SalaryCombo,
+ ] = await Promise.all([
+ fetchCompanyCombo(),
+ fetchTeamCombo(),
+ fetchDepartmentCombo(),
+ fetchPositionCombo(),
+ fetchGradeCombo(),
+ fetchSkillCombo(),
+ fetchSalaryCombo(),
+ ]);
+ const combos: comboItem = {
+ company: CompanyCombo.records,
+ team: TeamCombo.records,
+ department: DepartmentCombo.records,
+ position: PositionCombo.records,
+ grade: GradeCombo.records,
+ skill: SkillCombo.records,
+ salary: SalaryCombo.records,
+ }
-
- return ;
+ return ;
};
CreateStaffWrapper.Loading = CreateStaffLoading;
diff --git a/src/components/EditStaffForm/EditStaffForm.tsx b/src/components/EditStaffForm/EditStaffForm.tsx
index f15be32..96f8b1d 100644
--- a/src/components/EditStaffForm/EditStaffForm.tsx
+++ b/src/components/EditStaffForm/EditStaffForm.tsx
@@ -83,7 +83,7 @@ const EditStaffForm: React.FC = ({ Title, fieldLists }) => {
};
return (
<>
- {serverError && (
+ {serverError && (
{serverError}
diff --git a/src/components/EditUser/EditUser.tsx b/src/components/EditUser/EditUser.tsx
new file mode 100644
index 0000000..db52fac
--- /dev/null
+++ b/src/components/EditUser/EditUser.tsx
@@ -0,0 +1,164 @@
+"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,
+ Card,
+ CardContent,
+ Grid,
+ Stack,
+ Tab,
+ Tabs,
+ TabsProps,
+ TextField,
+ Typography,
+} from "@mui/material";
+import {
+ FieldErrors,
+ FormProvider,
+ SubmitErrorHandler,
+ SubmitHandler,
+ useForm,
+ useFormContext,
+} from "react-hook-form";
+import { Check, Close, Error, RestartAlt } from "@mui/icons-material";
+import { StaffResult } from "@/app/api/staff";
+import { editUser, fetchUserDetails } from "@/app/api/user/actions";
+import UserDetail from "./UserDetail";
+import { UserResult } from "@/app/api/user";
+
+interface Props {
+ // users: UserResult[]
+}
+
+const EditUser: React.FC = async ({ }) => {
+ const { t } = useTranslation();
+ const formProps = useForm();
+ const searchParams = useSearchParams();
+ const id = parseInt(searchParams.get("id") || "0");
+ const [tabIndex, setTabIndex] = useState(0);
+ const router = useRouter();
+
+ const handleTabChange = useCallback>(
+ (_e, newValue) => {
+ setTabIndex(newValue);
+ },
+ []
+ );
+
+ const [serverError, setServerError] = useState("");
+ const [data, setData] = useState();
+
+ const fetchUserDetail = async () => {
+ console.log(id);
+ try {
+ const userDetail = await fetchUserDetails(id);
+ console.log(userDetail);
+ const _data = userDetail.data as UserResult;
+ console.log(_data);
+ setData(_data);
+ formProps.reset({
+ username: _data.username,
+ firstname: _data.firstname,
+ lastname: _data.lastname,
+ title: _data.title,
+ department: _data.department,
+ email: _data.email,
+ phone1: _data.phone1,
+ phone2: _data.phone2,
+ remarks: _data.remarks,
+ });
+ } catch (error) {
+ console.log(error);
+ setServerError(t("An error has occurred. Please try again later."));
+ }
+ };
+
+ useEffect(() => {
+ fetchUserDetail();
+ }, []);
+
+ const hasErrorsInTab = (
+ tabIndex: number,
+ errors: FieldErrors
+ ) => {
+ switch (tabIndex) {
+ case 0:
+ return Object.keys(errors).length > 0;
+ default:
+ false;
+ }
+ };
+
+ const handleCancel = () => {
+ router.back();
+ };
+
+ const onSubmit = useCallback>(
+ async (data) => {
+ try {
+ console.log(data);
+ const tempData = {
+ username: data.username,
+ email: data.email,
+ locked: false
+ }
+ console.log(tempData);
+ await editUser(id, tempData);
+ router.replace("/settings/staff");
+ } catch (e) {
+ console.log(e);
+ setServerError(t("An error has occurred. Please try again later."));
+ }
+ },
+ [router]
+ );
+ const onSubmitError = useCallback>(
+ (errors) => {
+ console.log(errors);
+ },
+ []
+ );
+
+ return (
+ <>
+ {serverError && (
+
+ {serverError}
+
+ )}
+
+
+
+
+ }
+ // onClick={() => console.log("asdasd")}
+ >
+ {t("Reset")}
+
+ }
+ onClick={handleCancel}
+ >
+ {t("Cancel")}
+
+ } type="submit">
+ {t("Confirm")}
+
+
+
+
+ >
+ );
+};
+export default EditUser;
diff --git a/src/components/EditUser/EditUserLoading.tsx b/src/components/EditUser/EditUserLoading.tsx
new file mode 100644
index 0000000..971c9e4
--- /dev/null
+++ b/src/components/EditUser/EditUserLoading.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 EditUserLoading: React.FC = () => {
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
+ EditUser
+
+
+
+
+
+
+
+
+
+ >
+ );
+};
+
+export default EditUserLoading;
diff --git a/src/components/EditUser/EditUserWrapper.tsx b/src/components/EditUser/EditUserWrapper.tsx
new file mode 100644
index 0000000..eaf7aa5
--- /dev/null
+++ b/src/components/EditUser/EditUserWrapper.tsx
@@ -0,0 +1,23 @@
+import React from "react";
+import EditUser from "./EditUser";
+import EditUserLoading from "./EditUserLoading";
+// 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";
+import { fetchUser } from "@/app/api/user";
+
+interface SubComponents {
+ Loading: typeof EditUserLoading;
+}
+
+const EditUserWrapper: React.FC & SubComponents = async () => {
+ // const users = await fetchUser()
+ // console.log(users)
+
+ return ;
+};
+
+EditUserWrapper.Loading = EditUserLoading;
+
+export default EditUserWrapper;
diff --git a/src/components/EditUser/UserDetail.tsx b/src/components/EditUser/UserDetail.tsx
new file mode 100644
index 0000000..ed7ad2a
--- /dev/null
+++ b/src/components/EditUser/UserDetail.tsx
@@ -0,0 +1,136 @@
+"use client";
+
+import { UserResult } from "@/app/api/user";
+import {
+ Card,
+ CardContent,
+ Grid,
+ Stack,
+ TextField,
+ Typography,
+} from "@mui/material";
+import { useFormContext } from "react-hook-form";
+import { useTranslation } from "react-i18next";
+
+interface Props {
+ data: UserResult
+}
+
+
+const UserDetail: React.FC = ({
+ data
+}) => {
+ const { t } = useTranslation();
+ const {
+ register,
+ formState: { errors },
+ control,
+ } = useFormContext();
+
+ return (
+
+
+
+ {t("User Detail")}
+
+
+
+
+
+ {/*
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ */}
+
+
+
+ );
+};
+
+export default UserDetail;
diff --git a/src/components/EditUser/index.ts b/src/components/EditUser/index.ts
new file mode 100644
index 0000000..c12dc8e
--- /dev/null
+++ b/src/components/EditUser/index.ts
@@ -0,0 +1 @@
+export { default } from "./EditUserWrapper";
diff --git a/src/components/EditUserGroup/EditUserGroupWrapper.tsx b/src/components/EditUserGroup/EditUserGroupWrapper.tsx
index 84f7501..dffd5e7 100644
--- a/src/components/EditUserGroup/EditUserGroupWrapper.tsx
+++ b/src/components/EditUserGroup/EditUserGroupWrapper.tsx
@@ -14,14 +14,11 @@ const EditUserGroupWrapper: React.FC & SubComponents = async () => {
const [
groups,
- // auths,
users,
] = await Promise.all([
fetchGroup(),
- // fetchAuth(),
fetchUser(),
]);
- console.log(users)
return ;
};
diff --git a/src/components/StaffSearch/StaffSearch.tsx b/src/components/StaffSearch/StaffSearch.tsx
index 4111d14..5bfb017 100644
--- a/src/components/StaffSearch/StaffSearch.tsx
+++ b/src/components/StaffSearch/StaffSearch.tsx
@@ -9,6 +9,7 @@ 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";
+import Person from '@mui/icons-material/Person';
interface Props {
staff: StaffResult[];
@@ -65,6 +66,14 @@ const StaffSearch: React.FC = ({ staff }) => {
[router, t]
);
+ const onUserClick = useCallback(
+ (staff: StaffResult) => {
+ console.log(staff);
+ router.push(`/settings/staff/user?id=${staff.id}`);
+ },
+ [router, t]
+ );
+
const deleteClick = useCallback((staff: StaffResult) => {
deleteDialog(async () => {
await deleteStaff(staff.id);
@@ -81,6 +90,12 @@ const StaffSearch: React.FC = ({ staff }) => {
onClick: onStaffClick,
buttonIcon: ,
},
+ {
+ name: "id",
+ label: t("Actions"),
+ onClick: onUserClick,
+ buttonIcon: ,
+ },
{ name: "team", label: t("Team") },
{ name: "name", label: t("Staff Name") },
{ name: "staffId", label: t("Staff ID") },
diff --git a/src/middleware.ts b/src/middleware.ts
index 078ace6..a793a79 100644
--- a/src/middleware.ts
+++ b/src/middleware.ts
@@ -71,9 +71,6 @@ export default async function middleware(
return response;
}
- // const session = await getServerSession(authOptions);
- // console.log(session);
-
let abilities: string[] = []
if (token) {
abilities = (token.abilities as ability[]).map((item: ability) => item.actionSubjectCombo);
@@ -93,6 +90,9 @@ export default async function middleware(
if (req.nextUrl.pathname.startsWith('/settings/user')) {
isAuth = [MAINTAIN_USER, VIEW_USER].some((ability) => abilities.includes(ability));
}
+ if (req.nextUrl.pathname.startsWith('/settings/staff/user')) {
+ isAuth = [MAINTAIN_USER, VIEW_USER].some((ability) => abilities.includes(ability));
+ }
if (req.nextUrl.pathname.startsWith('/analytics')) {
isAuth = [GENERATE_REPORTS].some((ability) => abilities.includes(ability));
}
@@ -104,40 +104,6 @@ export default async function middleware(
}
});
-
- // for (const obj of abilities) {
- // switch (obj.actionSubjectCombo.toLowerCase()) {
- // case "maintain_user":
- // // appendRoutes(settings)
- // break;
- // case "maintain_group":
- // // appendRoutes("/testing-maintain_user")
- // break;
- // case "view_user":
- // // appendRoutes("/testing-maintain_user")
- // break;
- // case "view_group":
- // // appendRoutes("/testing-maintain_user")
- // break;
- // }
- // }
-
-// console.log("TESTING_ROUTES: ")
-// console.log(TESTING_ROUTES)
-
-// TESTING_ROUTES.some((route) => {
-// if (req.nextUrl.pathname.startsWith(route)) {
-// console.log("////////////////start//////////////// ")
-// console.log("TESTING_ROUTES:")
-// console.log("route:")
-// console.log(route)
-// console.log("pathname:")
-// console.log(req.nextUrl.pathname)
-// console.log("////////////////end////////////////")
-// }
-// return (req.nextUrl.pathname.startsWith(route))
-// })
-
// Matcher for using the auth middleware
return PRIVATE_ROUTES.some((route) => req.nextUrl.pathname.startsWith(route))
? await authMiddleware(req, event) // Let auth middleware handle response