diff --git a/src/app/(main)/settings/company/page.tsx b/src/app/(main)/settings/company/page.tsx index 26332c1..3b6e140 100644 --- a/src/app/(main)/settings/company/page.tsx +++ b/src/app/(main)/settings/company/page.tsx @@ -8,6 +8,8 @@ import Typography from "@mui/material/Typography"; import Link from "next/link"; import { Suspense } from "react"; import { fetchCompanys, preloadCompanys } from "@/app/api/companys"; +import { getUserAbilities } from "@/app/utils/commonUtil"; +import { MAINTAIN_COMPANY } from "@/middleware"; export const metadata: Metadata = { title: "Company", @@ -20,6 +22,9 @@ const Company: React.FC = async () => { fetchCompanys(); preloadCompanys(); + const abilities = await getUserAbilities() + const maintainCompany = abilities.includes(MAINTAIN_COMPANY) + return ( <> { {t("Company")} - + } }> diff --git a/src/app/(main)/settings/customer/page.tsx b/src/app/(main)/settings/customer/page.tsx index 1f55f78..91f5ac3 100644 --- a/src/app/(main)/settings/customer/page.tsx +++ b/src/app/(main)/settings/customer/page.tsx @@ -9,6 +9,8 @@ import { Metadata } from "next"; import Link from "next/link"; import { Suspense } from "react"; import { I18nProvider } from "@/i18n"; +import { getUserAbilities } from "@/app/utils/commonUtil"; +import { MAINTAIN_CLIENT } from "@/middleware"; export const metadata: Metadata = { title: "Customer", @@ -17,6 +19,8 @@ export const metadata: Metadata = { const Customer: React.FC = async () => { const { t } = await getServerI18n("customer"); preloadAllCustomers(); + const abilities = await getUserAbilities() + const maintainClient = abilities.includes(MAINTAIN_CLIENT) return ( <> @@ -29,14 +33,14 @@ const Customer: React.FC = async () => { {t("Customer")} - + } }> diff --git a/src/app/(main)/settings/department/page.tsx b/src/app/(main)/settings/department/page.tsx index a051721..be2b265 100644 --- a/src/app/(main)/settings/department/page.tsx +++ b/src/app/(main)/settings/department/page.tsx @@ -8,6 +8,8 @@ import Typography from "@mui/material/Typography"; import Link from "next/link"; import { Suspense } from "react"; import { fetchDepartments, preloadDepartments } from "@/app/api/departments"; +import { getUserAbilities } from "@/app/utils/commonUtil"; +import { MAINTAIN_DEPARTMENT } from "@/middleware"; export const metadata: Metadata = { title: "Department", @@ -20,6 +22,9 @@ const Department: React.FC = async () => { // fetchDepartments(); // preloadDepartments(); + const abilities = await getUserAbilities() + const maintainDepartment = abilities.includes(MAINTAIN_DEPARTMENT) + return ( <> { {t("Department")} - + } }> diff --git a/src/app/(main)/settings/position/page.tsx b/src/app/(main)/settings/position/page.tsx index 5342145..5eccd8d 100644 --- a/src/app/(main)/settings/position/page.tsx +++ b/src/app/(main)/settings/position/page.tsx @@ -8,6 +8,8 @@ import Typography from "@mui/material/Typography"; import Link from "next/link"; import { Suspense } from "react"; import { fetchPositions, preloadPositions } from "@/app/api/positions"; +import { getUserAbilities } from "@/app/utils/commonUtil"; +import { MAINTAIN_POSITION } from "@/middleware"; export const metadata: Metadata = { title: "Position", @@ -20,6 +22,9 @@ const Position: React.FC = async () => { // fetchPositions(); // preloadPositions(); + const abilities = await getUserAbilities() + const maintainPosition = abilities.includes(MAINTAIN_POSITION) + return ( <> { {t("Position")} - + } }> diff --git a/src/app/(main)/settings/skill/page.tsx b/src/app/(main)/settings/skill/page.tsx index bcb14ab..44d3c53 100644 --- a/src/app/(main)/settings/skill/page.tsx +++ b/src/app/(main)/settings/skill/page.tsx @@ -1,7 +1,9 @@ import { preloadClaims } from "@/app/api/claims"; +import { getUserAbilities } from "@/app/utils/commonUtil"; // import { preloadSkill, preloadTeamLeads } from "@/app/api/staff"; import SkillSearch from "@/components/SkillSearch"; import { I18nProvider, getServerI18n } from "@/i18n"; +import { MAINTAIN_SKILL } from "@/middleware"; import Add from "@mui/icons-material/Add"; import Button from "@mui/material/Button"; import Stack from "@mui/material/Stack"; @@ -18,6 +20,10 @@ const Skill: React.FC = async () => { const { t } = await getServerI18n("skill"); // preloadTeamLeads(); // preloadSkill(); + + const abilities = await getUserAbilities() + const maintainSkill = abilities.includes(MAINTAIN_SKILL) + return ( <> { {t("Skill")} - + } }> diff --git a/src/app/(main)/settings/staff/page.tsx b/src/app/(main)/settings/staff/page.tsx index db26d1b..5bb84cb 100644 --- a/src/app/(main)/settings/staff/page.tsx +++ b/src/app/(main)/settings/staff/page.tsx @@ -1,7 +1,9 @@ import { preloadClaims } from "@/app/api/claims"; import { preloadStaff, preloadTeamLeads } from "@/app/api/staff"; +import { getUserAbilities } from "@/app/utils/commonUtil"; import StaffSearch from "@/components/StaffSearch"; import { I18nProvider, getServerI18n } from "@/i18n"; +import { MAINTAIN_STAFF } from "@/middleware"; import Add from "@mui/icons-material/Add"; import Button from "@mui/material/Button"; import Stack from "@mui/material/Stack"; @@ -18,6 +20,10 @@ const Staff: React.FC = async () => { const { t } = await getServerI18n("staff"); preloadTeamLeads(); preloadStaff(); + + const abilities = await getUserAbilities(); + const maintainStaff = abilities.includes(MAINTAIN_STAFF) + return ( <> { {t("Staff")} - + } }> diff --git a/src/app/(main)/settings/subsidiary/page.tsx b/src/app/(main)/settings/subsidiary/page.tsx index cadb670..ad7fd74 100644 --- a/src/app/(main)/settings/subsidiary/page.tsx +++ b/src/app/(main)/settings/subsidiary/page.tsx @@ -9,6 +9,8 @@ import { Suspense } from "react"; import { I18nProvider } from "@/i18n"; import { preloadAllSubsidiaries } from "@/app/api/subsidiary"; import SubsidiarySearch from "@/components/SubsidiarySearch"; +import { getUserAbilities } from "@/app/utils/commonUtil"; +import { MAINTAIN_SUBSIDIARY } from "@/middleware"; export const metadata: Metadata = { title: "Subsidiary", @@ -17,6 +19,8 @@ export const metadata: Metadata = { const Subsidiary: React.FC = async () => { const { t } = await getServerI18n("subsidiary"); preloadAllSubsidiaries(); + const abilities = await getUserAbilities() + const maintainSubsidiary = abilities.includes(MAINTAIN_SUBSIDIARY) return ( <> @@ -29,14 +33,14 @@ const Subsidiary: React.FC = async () => { {t("Subsidiary")} - + } }> diff --git a/src/app/(main)/settings/team/page.tsx b/src/app/(main)/settings/team/page.tsx index b2b67ab..d2965a1 100644 --- a/src/app/(main)/settings/team/page.tsx +++ b/src/app/(main)/settings/team/page.tsx @@ -1,8 +1,10 @@ import { preloadClaims } from "@/app/api/claims"; import { preloadStaff, preloadTeamLeads } from "@/app/api/staff"; +import { getUserAbilities } from "@/app/utils/commonUtil"; import StaffSearch from "@/components/StaffSearch"; import TeamSearch from "@/components/TeamSearch"; import { I18nProvider, getServerI18n } from "@/i18n"; +import { MAINTAIN_TEAM } from "@/middleware"; import Add from "@mui/icons-material/Add"; import Button from "@mui/material/Button"; import Stack from "@mui/material/Stack"; @@ -21,6 +23,10 @@ export const metadata: Metadata = { const { t } = await getServerI18n("team"); // preloadTeamLeads(); // preloadStaff(); + + const abilities = await getUserAbilities() + const maintainTeam = abilities.includes(MAINTAIN_TEAM) + return ( <> {t("Team")} - + } }> diff --git a/src/components/CompanyHoliday/CompanyHoliday.tsx b/src/components/CompanyHoliday/CompanyHoliday.tsx index ec92960..c3d9f32 100644 --- a/src/components/CompanyHoliday/CompanyHoliday.tsx +++ b/src/components/CompanyHoliday/CompanyHoliday.tsx @@ -23,16 +23,19 @@ import { import { useRouter } from "next/navigation"; import { deleteDialog, submitDialog } from "../Swal/CustomAlerts"; import { getPublicHolidaysForNYears } from "@/app/utils/holidayUtils"; +import { MAINTAIN_HOLIDAY } from "@/middleware"; interface Props { holidays: HolidaysList[]; + abilities: String[]; } -const CompanyHoliday: React.FC = ({ holidays }) => { +const CompanyHoliday: React.FC = ({ holidays, abilities }) => { const { t } = useTranslation("holidays"); const router = useRouter(); const formValues = useFormContext(); const [serverError, setServerError] = useState(""); + const maintainHoliday = abilities.includes(MAINTAIN_HOLIDAY) const companyHolidays = useMemo( () => [...getPublicHolidaysForNYears(2), ...holidays], @@ -139,7 +142,7 @@ const CompanyHoliday: React.FC = ({ holidays }) => { }} /> { // const Companys = await fetchCompanys(); - const companyHolidays: HolidaysResult[] = await fetchHolidays() + const [companyHolidays, abilities] = await Promise.all([fetchHolidays(), getUserAbilities()]) // console.log(companyHolidays) const convertedHolidays = companyHolidays.map((holiday) => { @@ -26,7 +27,7 @@ const CompanyHolidayWrapper: React.FC & SubComponents = async () => { }) - return ; + return ; }; CompanyHolidayWrapper.Loading = CompanyHolidayLoading; diff --git a/src/components/CompanySearch/CompanySearch.tsx b/src/components/CompanySearch/CompanySearch.tsx index 8a12f75..94cc1b6 100644 --- a/src/components/CompanySearch/CompanySearch.tsx +++ b/src/components/CompanySearch/CompanySearch.tsx @@ -10,21 +10,25 @@ import { useRouter } from "next/navigation"; import { deleteDialog, successDialog } from "../Swal/CustomAlerts"; import { deleteCompany } from "@/app/api/companys/actions"; import DeleteIcon from '@mui/icons-material/Delete'; +import { MAINTAIN_COMPANY } from "@/middleware"; interface Props { companys: CompanyResult[]; + abilities: String[]; } type SearchQuery = Partial>; type SearchParamNames = keyof SearchQuery; -const CompanySearch: React.FC = ({ companys }) => { +const CompanySearch: React.FC = ({ companys, abilities }) => { const { t } = useTranslation("companys"); const router = useRouter() const [filteredCompanys, setFilteredCompanys] = useState(companys); + const maintainCompany = abilities.includes(MAINTAIN_COMPANY) + const searchCriteria: Criterion[] = useMemo( () => [ { label: t("Company code"), paramName: "companyCode", type: "text" }, @@ -62,6 +66,7 @@ const CompanySearch: React.FC = ({ companys }) => { label: t("Details"), onClick: onProjectClick, buttonIcon: , + isHidden: !maintainCompany, }, { name: "companyCode", label: t("Company Code") }, { name: "name", label: t("Company Name") }, @@ -74,7 +79,8 @@ const CompanySearch: React.FC = ({ companys }) => { label: t("Delete"), onClick: onDeleteClick, buttonIcon: , - color: "error" + color: "error", + isHidden: !maintainCompany, }, ], [t, onProjectClick], diff --git a/src/components/CompanySearch/CompanySearchWrapper.tsx b/src/components/CompanySearch/CompanySearchWrapper.tsx index f7e61a0..2fdfe55 100644 --- a/src/components/CompanySearch/CompanySearchWrapper.tsx +++ b/src/components/CompanySearch/CompanySearchWrapper.tsx @@ -3,6 +3,7 @@ import React from "react"; import CompanySearch from "./CompanySearch"; import CompanySearchLoading from "./CompanySearchLoading"; import { fetchCompanys } from "@/app/api/companys"; +import { getUserAbilities } from "@/app/utils/commonUtil"; interface SubComponents { Loading: typeof CompanySearchLoading; @@ -10,9 +11,9 @@ interface SubComponents { const CompanySearchWrapper: React.FC & SubComponents = async () => { // const Companys = await fetchCompanys(); - const Companys = await fetchCompanys(); + const [Companys, abilities] = await Promise.all([fetchCompanys(), getUserAbilities()]); - return ; + return ; }; CompanySearchWrapper.Loading = CompanySearchLoading; diff --git a/src/components/CustomerSearch/CustomerSearch.tsx b/src/components/CustomerSearch/CustomerSearch.tsx index cacf5fa..7b39ade 100644 --- a/src/components/CustomerSearch/CustomerSearch.tsx +++ b/src/components/CustomerSearch/CustomerSearch.tsx @@ -10,18 +10,21 @@ import DeleteIcon from '@mui/icons-material/Delete'; import { useRouter, useSearchParams } from "next/navigation"; import { deleteDialog, successDialog } from "../Swal/CustomAlerts"; import { deleteCustomer } from "@/app/api/customer/actions"; +import { MAINTAIN_CLIENT } from "@/middleware"; interface Props { customers: Customer[]; + abilities: String[]; } type SearchQuery = Partial>; type SearchParamNames = keyof SearchQuery; -const CustomerSearch: React.FC = ({ customers }) => { +const CustomerSearch: React.FC = ({ customers, abilities }) => { const { t } = useTranslation(); const router = useRouter() const searchParams = useSearchParams() + const maintainClient = abilities.includes(MAINTAIN_CLIENT) const [filteredCustomers, setFilteredCustomers] = useState(customers); useEffect(() => { @@ -60,6 +63,7 @@ const CustomerSearch: React.FC = ({ customers }) => { label: t("Details"), onClick: onTaskClick, buttonIcon: , + isHidden: !maintainClient }, { name: "code", label: t("Customer Code") }, { name: "name", label: t("Customer Name") }, @@ -68,7 +72,8 @@ const CustomerSearch: React.FC = ({ customers }) => { label: t("Delete"), onClick: onDeleteClick, buttonIcon: , - color: "error" + color: "error", + isHidden: !maintainClient }, ], [onTaskClick, t], diff --git a/src/components/CustomerSearch/CustomerSearchWrapper.tsx b/src/components/CustomerSearch/CustomerSearchWrapper.tsx index 531adb6..0b696b4 100644 --- a/src/components/CustomerSearch/CustomerSearchWrapper.tsx +++ b/src/components/CustomerSearch/CustomerSearchWrapper.tsx @@ -2,15 +2,16 @@ import { fetchAllCustomers } from "@/app/api/customer"; import React from "react"; import CustomerSearch from "./CustomerSearch"; import CustomerSearchLoading from "./CustomerSearchLoading"; +import { getUserAbilities } from "@/app/utils/commonUtil"; interface SubComponents { Loading: typeof CustomerSearchLoading; } const CustomerSearchWrapper: React.FC & SubComponents = async () => { - const [customers] = await Promise.all([fetchAllCustomers()]); + const [customers, abilities] = await Promise.all([fetchAllCustomers(), getUserAbilities()]); - return ; + return ; }; CustomerSearchWrapper.Loading = CustomerSearchLoading; diff --git a/src/components/DepartmentSearch/DepartmentSearch.tsx b/src/components/DepartmentSearch/DepartmentSearch.tsx index bbbd0a7..cb95aa2 100644 --- a/src/components/DepartmentSearch/DepartmentSearch.tsx +++ b/src/components/DepartmentSearch/DepartmentSearch.tsx @@ -10,19 +10,22 @@ import { useRouter } from "next/navigation"; import DeleteIcon from '@mui/icons-material/Delete'; import { deleteDialog, successDialog } from "../Swal/CustomAlerts"; import { deleteDepartment } from "@/app/api/departments/actions"; +import { MAINTAIN_DEPARTMENT } from "@/middleware"; interface Props { departments: DepartmentResult[]; + abilities: String[]; } type SearchQuery = Partial>; type SearchParamNames = keyof SearchQuery; -const DepartmentSearch: React.FC = ({ departments }) => { +const DepartmentSearch: React.FC = ({ departments, abilities }) => { const { t } = useTranslation("departments"); const router = useRouter(); const [filteredDepartments, setFilteredDepartments] = useState(departments); + const maintainDepartment = abilities.includes(MAINTAIN_DEPARTMENT) const searchCriteria: Criterion[] = useMemo( () => [ @@ -60,6 +63,7 @@ const DepartmentSearch: React.FC = ({ departments }) => { label: t("Details"), onClick: onProjectClick, buttonIcon: , + isHidden: !maintainDepartment, }, { name: "code", label: t("Department Code") }, { name: "name", label: t("Department Name") }, @@ -69,7 +73,8 @@ const DepartmentSearch: React.FC = ({ departments }) => { label: t("Delete"), onClick: onDeleteClick, buttonIcon: , - color: "error" + color: "error", + isHidden: !maintainDepartment, }, ], [t, onProjectClick], diff --git a/src/components/DepartmentSearch/DepartmentSearchWrapper.tsx b/src/components/DepartmentSearch/DepartmentSearchWrapper.tsx index 4249dfd..109f058 100644 --- a/src/components/DepartmentSearch/DepartmentSearchWrapper.tsx +++ b/src/components/DepartmentSearch/DepartmentSearchWrapper.tsx @@ -3,16 +3,17 @@ import React from "react"; import DepartmentSearch from "./DepartmentSearch"; import DepartmentSearchLoading from "./DepartmentSearchLoading"; import { fetchDepartments } from "@/app/api/departments"; +import { getUserAbilities } from "@/app/utils/commonUtil"; interface SubComponents { Loading: typeof DepartmentSearchLoading; } const DepartmentSearchWrapper: React.FC & SubComponents = async () => { - const Departments = await fetchDepartments(); + const [Departments, abilities] = await Promise.all([fetchDepartments(), getUserAbilities()]); // const Departments:any[] = [] - return ; + return ; }; DepartmentSearchWrapper.Loading = DepartmentSearchLoading; diff --git a/src/components/NavigationContent/NavigationContent.tsx b/src/components/NavigationContent/NavigationContent.tsx index 1332fe6..110870d 100644 --- a/src/components/NavigationContent/NavigationContent.tsx +++ b/src/components/NavigationContent/NavigationContent.tsx @@ -39,15 +39,34 @@ import { GENERATE_REPORTS, IMPORT_INVOICE, IMPORT_RECEIPT, - MAINTAIN_MASTERDATA, MAINTAIN_PROJECT, MAINTAIN_TASK_TEMPLATE, MAINTAIN_USER, VIEW_DASHBOARD_ALL, VIEW_DASHBOARD_SELF, - VIEW_MASTERDATA, VIEW_PROJECT, - VIEW_USER, + VIEW_CLIENT, + VIEW_SUBSIDIARY, + VIEW_STAFF, + VIEW_COMPANY, + VIEW_SKILL, + VIEW_DEPARTMENT, + VIEW_POSITION, + VIEW_SALARY, + VIEW_TEAM, + VIEW_USER_GROUP, + VIEW_HOLIDAY, + MAINTAIN_CLIENT, + MAINTAIN_SUBSIDIARY, + MAINTAIN_STAFF, + MAINTAIN_COMPANY, + MAINTAIN_SKILL, + MAINTAIN_DEPARTMENT, + MAINTAIN_POSITION, + MAINTAIN_SALARY, + MAINTAIN_TEAM, + MAINTAIN_USER_GROUP, + MAINTAIN_HOLIDAY, } from "@/middleware"; import { SessionWithAbilities } from "../AppBar/NavigationToggle"; import { authOptions } from "@/config/authConfig"; @@ -217,35 +236,101 @@ const NavigationContent: React.FC = ({ abilities, username }) => { icon: , label: "Setting", path: "", - isHidden: ![VIEW_MASTERDATA, MAINTAIN_MASTERDATA].some((ability) => + isHidden: ![ + VIEW_CLIENT, + VIEW_SUBSIDIARY, + VIEW_STAFF, + VIEW_COMPANY, + VIEW_SKILL, + VIEW_DEPARTMENT, + VIEW_POSITION, + VIEW_SALARY, + VIEW_TEAM, + VIEW_USER_GROUP, + VIEW_HOLIDAY, + MAINTAIN_CLIENT, + MAINTAIN_SUBSIDIARY, + MAINTAIN_STAFF, + MAINTAIN_COMPANY, + MAINTAIN_SKILL, + MAINTAIN_DEPARTMENT, + MAINTAIN_POSITION, + MAINTAIN_SALARY, + MAINTAIN_TEAM, + MAINTAIN_USER_GROUP, + MAINTAIN_HOLIDAY + ].some((ability) => abilities!.includes(ability), ), children: [ - { icon: , label: "Client", path: "/settings/customer" }, + { + icon: , + label: "Client", + path: "/settings/customer", + isHidden: ![VIEW_CLIENT, MAINTAIN_CLIENT].some((ability) => abilities!.includes(ability),), + }, { icon: , label: "Subsidiary", path: "/settings/subsidiary", + isHidden: ![VIEW_SUBSIDIARY, MAINTAIN_SUBSIDIARY].some((ability) => abilities!.includes(ability),), + }, + { + icon: , + label: "Staff", + path: "/settings/staff", + isHidden: ![VIEW_STAFF, MAINTAIN_STAFF].some((ability) => abilities!.includes(ability),), + }, + { + icon: , + label: "Company", + path: "/settings/company", + isHidden: ![VIEW_COMPANY, MAINTAIN_COMPANY].some((ability) => abilities!.includes(ability),), + }, + { + icon: , + label: "Skill", + path: "/settings/skill", + isHidden: ![VIEW_SKILL, MAINTAIN_SKILL].some((ability) => abilities!.includes(ability),), }, - { icon: , label: "Staff", path: "/settings/staff" }, - { icon: , label: "Company", path: "/settings/company" }, - { icon: , label: "Skill", path: "/settings/skill" }, { icon: , label: "Department", path: "/settings/department", + isHidden: ![VIEW_DEPARTMENT, MAINTAIN_DEPARTMENT].some((ability) => abilities!.includes(ability),), + }, + { + icon: , + label: "Position", + path: "/settings/position", + isHidden: ![VIEW_POSITION, MAINTAIN_POSITION].some((ability) => abilities!.includes(ability),), + }, + { + icon: , + label: "Salary", + path: "/settings/salary", + isHidden: ![VIEW_SALARY, MAINTAIN_SALARY].some((ability) => abilities!.includes(ability),), + }, + { + icon: , + label: "Team", + path: "/settings/team", + isHidden: ![VIEW_TEAM, MAINTAIN_TEAM].some((ability) => abilities!.includes(ability),), }, - { icon: , label: "Position", path: "/settings/position" }, - { icon: , label: "Salary", path: "/settings/salary" }, - { icon: , label: "Team", path: "/settings/team" }, // { icon: , label: "User", path: "/settings/user" }, { icon: , label: "User Group", path: "/settings/group", + isHidden: ![VIEW_USER_GROUP, MAINTAIN_USER_GROUP].some((ability) => abilities!.includes(ability),), + }, + { + icon: , + label: "Holiday", + path: "/settings/holiday", + isHidden: ![VIEW_HOLIDAY, MAINTAIN_HOLIDAY].some((ability) => abilities!.includes(ability),), }, - { icon: , label: "Holiday", path: "/settings/holiday"}, - { icon: , label: "Import Excel File", path: "/settings/import", isHidden: username !== "2fi"}, + { icon: , label: "Import Excel File", path: "/settings/import", isHidden: username !== "2fi" }, ], }, ]; diff --git a/src/components/PositionSearch/PositionSearch.tsx b/src/components/PositionSearch/PositionSearch.tsx index e700e2a..26085fe 100644 --- a/src/components/PositionSearch/PositionSearch.tsx +++ b/src/components/PositionSearch/PositionSearch.tsx @@ -10,19 +10,22 @@ import { useRouter } from "next/navigation"; import DeleteIcon from '@mui/icons-material/Delete'; import { deleteDialog, successDialog } from "../Swal/CustomAlerts"; import { deletePosition } from "@/app/api/positions/actions"; +import { MAINTAIN_POSITION } from "@/middleware"; interface Props { positions: PositionResult[]; + abilities: String[]; } type SearchQuery = Partial>; type SearchParamNames = keyof SearchQuery; -const PositionSearch: React.FC = ({ positions }) => { +const PositionSearch: React.FC = ({ positions, abilities }) => { const { t } = useTranslation("positions"); const router = useRouter(); const [filteredPositions, setFilteredPositions] = useState(positions); + const maintainPosition = abilities.includes(MAINTAIN_POSITION) const searchCriteria: Criterion[] = useMemo( () => [ @@ -61,6 +64,7 @@ const PositionSearch: React.FC = ({ positions }) => { label: t("Details"), onClick: onPositionClick, buttonIcon: , + isHidden: !maintainPosition, }, { name: "code", label: t("Position Code") }, { name: "name", label: t("Position Name") }, @@ -70,7 +74,8 @@ const PositionSearch: React.FC = ({ positions }) => { label: t("Delete"), onClick: onDeleteClick, buttonIcon: , - color: "error" + color: "error", + isHidden: !maintainPosition, }, ], [t, onPositionClick], diff --git a/src/components/PositionSearch/PositionSearchWrapper.tsx b/src/components/PositionSearch/PositionSearchWrapper.tsx index f115a77..a76f302 100644 --- a/src/components/PositionSearch/PositionSearchWrapper.tsx +++ b/src/components/PositionSearch/PositionSearchWrapper.tsx @@ -3,16 +3,17 @@ import React from "react"; import PositionSearch from "./PositionSearch"; import PositionSearchLoading from "./PositionSearchLoading"; import { fetchPositions } from "@/app/api/positions"; +import { getUserAbilities } from "@/app/utils/commonUtil"; interface SubComponents { Loading: typeof PositionSearchLoading; } const PositionSearchWrapper: React.FC & SubComponents = async () => { - const Positions = await fetchPositions(); + const [Positions, abilities] = await Promise.all([fetchPositions(), getUserAbilities()]); // const Positions:any[] = [] - return ; + return ; }; PositionSearchWrapper.Loading = PositionSearchLoading; diff --git a/src/components/SalarySearch/SalarySearch.tsx b/src/components/SalarySearch/SalarySearch.tsx index 5c3a49d..0022a48 100644 --- a/src/components/SalarySearch/SalarySearch.tsx +++ b/src/components/SalarySearch/SalarySearch.tsx @@ -13,18 +13,21 @@ import FileUploadIcon from '@mui/icons-material/FileUpload'; import { exportSalary, importSalarys } from "@/app/api/salarys/actions"; import { downloadFile } from "@/app/utils/commonUtil"; import { errorDialog, successDialog } from "../Swal/CustomAlerts"; +import { MAINTAIN_SALARY } from "@/middleware"; interface Props { salarys: SalaryResult[]; + abilities: String[]; } type SearchQuery = Partial>; type SearchParamNames = keyof SearchQuery; -const SalarySearch: React.FC = ({ salarys }) => { +const SalarySearch: React.FC = ({ salarys, abilities }) => { const { t } = useTranslation("salarys"); const [filteredSalarys, setFilteredSalarys] = useState(salarys); + const maintainSalary = abilities.includes(MAINTAIN_SALARY) const searchCriteria: Criterion[] = useMemo( () => [ @@ -109,7 +112,7 @@ const SalarySearch: React.FC = ({ salarys }) => { return ( <> - = ({ salarys }) => { {t("Export Salary")} - + } { diff --git a/src/components/SalarySearch/SalarySearchWrapper.tsx b/src/components/SalarySearch/SalarySearchWrapper.tsx index 83fe593..650a011 100644 --- a/src/components/SalarySearch/SalarySearchWrapper.tsx +++ b/src/components/SalarySearch/SalarySearchWrapper.tsx @@ -3,6 +3,7 @@ import React from "react"; import SalarySearch from "./SalarySearch"; import SalarySearchLoading from "./SalarySearchLoading"; import { fetchSalarys } from "@/app/api/salarys"; +import { getUserAbilities } from "@/app/utils/commonUtil"; interface SubComponents { Loading: typeof SalarySearchLoading; @@ -14,7 +15,7 @@ interface SubComponents { // } const SalarySearchWrapper: React.FC & SubComponents = async () => { - const Salarys = await fetchSalarys(); + const [Salarys, abilities] = await Promise.all([fetchSalarys(), getUserAbilities()]); // const Salarys:any[] = [] const salarysWithHourlyRate = Salarys.map((salary) => { // const hourlyRate = calculateHourlyRate(Number(salary.lowerLimit), Number(salary.upperLimit),22, 8) @@ -27,7 +28,7 @@ const SalarySearchWrapper: React.FC & SubComponents = async () => { }) // console.log(salarysWithHourlyRate) - return ; + return ; }; SalarySearchWrapper.Loading = SalarySearchLoading; diff --git a/src/components/SkillSearch/SkillSearch.tsx b/src/components/SkillSearch/SkillSearch.tsx index 0e883b7..0b5a481 100644 --- a/src/components/SkillSearch/SkillSearch.tsx +++ b/src/components/SkillSearch/SkillSearch.tsx @@ -9,15 +9,17 @@ import { useRouter } from "next/navigation"; import { deleteDialog, successDialog } from "../Swal/CustomAlerts"; import { SkillResult } from "@/app/api/skill"; import { deleteSkill } from "@/app/api/skill/actions"; +import { MAINTAIN_SKILL } from "@/middleware"; interface Props { skill: SkillResult[]; + abilities: String[]; } type SearchQuery = Partial>; type SearchParamNames = keyof SearchQuery; -const SkillSearch: React.FC = ({ skill }) => { +const SkillSearch: React.FC = ({ skill, abilities }) => { const { t } = useTranslation(); const [filteredSkill, setFilteredSkill] = useState(skill); const router = useRouter(); @@ -30,6 +32,8 @@ const SkillSearch: React.FC = ({ skill }) => { const code = t("code") const description = t("description") + const maintainSkill = abilities.includes(MAINTAIN_SKILL) + const searchCriteria: Criterion[] = useMemo( () => [ { @@ -73,6 +77,7 @@ const SkillSearch: React.FC = ({ skill }) => { label: edit, onClick: onSkillClick, buttonIcon: , + isHidden: !maintainSkill, }, { name: "name", label: name }, { name: "code", label: code }, @@ -83,6 +88,7 @@ const SkillSearch: React.FC = ({ skill }) => { onClick: deleteClick, buttonIcon: , color: "error", + isHidden: !maintainSkill, }, ], [t, onSkillClick, deleteClick] diff --git a/src/components/SkillSearch/SkillSearchWrapper.tsx b/src/components/SkillSearch/SkillSearchWrapper.tsx index ba5be7d..8936b56 100644 --- a/src/components/SkillSearch/SkillSearchWrapper.tsx +++ b/src/components/SkillSearch/SkillSearchWrapper.tsx @@ -2,15 +2,16 @@ import React from "react"; import SkillSearch from "./SkillSearch"; import SkillSearchLoading from "./SkillSearchLoading"; import { fetchSkill } from "@/app/api/skill"; +import { getUserAbilities } from "@/app/utils/commonUtil"; interface SubComponents { Loading: typeof SkillSearchLoading; } const SkillSearchWrapper: React.FC & SubComponents = async () => { - const skill = await fetchSkill() + const [skill, abilities] = await Promise.all([fetchSkill(), getUserAbilities()]) - return ; + return ; }; SkillSearchWrapper.Loading = SkillSearchLoading; diff --git a/src/components/StaffSearch/StaffSearch.tsx b/src/components/StaffSearch/StaffSearch.tsx index 16b4dba..b38b005 100644 --- a/src/components/StaffSearch/StaffSearch.tsx +++ b/src/components/StaffSearch/StaffSearch.tsx @@ -10,7 +10,7 @@ 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'; -import { MAINTAIN_USER, VIEW_USER } from "@/middleware"; +import { MAINTAIN_STAFF, MAINTAIN_USER } from "@/middleware"; import { TeamResult } from "@/app/api/team"; import { Grade } from "@/app/api/grades"; import { PositionResult } from "@/app/api/positions"; @@ -20,13 +20,13 @@ interface Props { teams: TeamResult[] grades: Grade[] positions: PositionResult[] - isAuthed: boolean + abilities: String[] } type SearchQuery = Partial>; type SearchParamNames = keyof SearchQuery; -const StaffSearch: React.FC = ({ staff, teams, grades, positions, isAuthed }) => { +const StaffSearch: React.FC = ({ staff, teams, grades, positions, abilities }) => { const { t } = useTranslation(); const [filteredStaff, setFilteredStaff] = useState(staff); const router = useRouter(); @@ -35,6 +35,9 @@ const StaffSearch: React.FC = ({ staff, teams, grades, positions, isAuthe const gradeCombo = grades.map(g => `${g.name}`) const positionCombo = positions.map(p => `${p.name}`) + const maintainUser = abilities.includes(MAINTAIN_USER) + const maintainStaff = abilities.includes(MAINTAIN_STAFF) + const searchCriteria: Criterion[] = useMemo( () => [ { @@ -104,13 +107,14 @@ const StaffSearch: React.FC = ({ staff, teams, grades, positions, isAuthe label: t("Actions"), onClick: onStaffClick, buttonIcon: , + isHidden: !maintainStaff, }, { name: "id", label: t("Users"), onClick: onUserClick, buttonIcon: , - isHidden: isAuthed, + isHidden: !maintainUser, }, { name: "team", label: t("Team") }, { name: "name", label: t("Staff Name") }, @@ -123,6 +127,7 @@ const StaffSearch: React.FC = ({ staff, teams, grades, positions, isAuthe onClick: deleteClick, buttonIcon: , color: "error", + isHidden: !maintainStaff, }, ], [t, onStaffClick, deleteClick] diff --git a/src/components/StaffSearch/StaffSearchWrapper.tsx b/src/components/StaffSearch/StaffSearchWrapper.tsx index 42f5f83..6a4854e 100644 --- a/src/components/StaffSearch/StaffSearchWrapper.tsx +++ b/src/components/StaffSearch/StaffSearchWrapper.tsx @@ -7,7 +7,7 @@ import { authOptions } from "@/config/authConfig"; import { fetchTeam } from "@/app/api/team"; import { fetchPositions } from "@/app/api/positions"; import { fetchGrades } from "@/app/api/grades"; -import { MAINTAIN_USER, VIEW_USER } from "@/middleware"; +import { MAINTAIN_USER } from "@/middleware"; interface SubComponents { Loading: typeof StaffSearchLoading; @@ -19,14 +19,14 @@ interface SessionWithAbilities extends Session { const StaffSearchWrapper: React.FC & SubComponents = async () => { const session = await getServerSession(authOptions) as SessionWithAbilities; - const isAuthed = ![MAINTAIN_USER, VIEW_USER].some((ability) => session.abilities!.includes(ability)) + const abilities = session.abilities! const staff = await fetchStaff(); const teams = await fetchTeam(); const grades = await fetchGrades(); const positions = await fetchPositions(); - return ; + return ; }; StaffSearchWrapper.Loading = StaffSearchLoading; diff --git a/src/components/SubsidiarySearch/SubsidiarySearch.tsx b/src/components/SubsidiarySearch/SubsidiarySearch.tsx index a50f4a9..79bab48 100644 --- a/src/components/SubsidiarySearch/SubsidiarySearch.tsx +++ b/src/components/SubsidiarySearch/SubsidiarySearch.tsx @@ -10,18 +10,21 @@ import { useRouter, useSearchParams } from "next/navigation"; import { deleteDialog, successDialog } from "../Swal/CustomAlerts"; import { Subsidiary } from "@/app/api/subsidiary"; import { deleteSubsidiary } from "@/app/api/subsidiary/actions"; +import { MAINTAIN_SUBSIDIARY } from "@/middleware"; interface Props { subsidiaries: Subsidiary[]; + abilities: String[]; } type SearchQuery = Partial>; type SearchParamNames = keyof SearchQuery; -const SubsidiarySearch: React.FC = ({ subsidiaries }) => { +const SubsidiarySearch: React.FC = ({ subsidiaries, abilities }) => { const { t } = useTranslation(); const router = useRouter() const searchParams = useSearchParams() + const maintainSubsidiary = abilities.includes(MAINTAIN_SUBSIDIARY) const [filteredSubsidiaries, setFilteredSubsidiaries] = useState(subsidiaries); useEffect(() => { @@ -63,6 +66,7 @@ const SubsidiarySearch: React.FC = ({ subsidiaries }) => { label: t("Details"), onClick: onTaskClick, buttonIcon: , + isHidden: !maintainSubsidiary, }, { name: "code", label: t("Subsidiary Code") }, { name: "name", label: t("Subsidiary Name") }, @@ -71,7 +75,8 @@ const SubsidiarySearch: React.FC = ({ subsidiaries }) => { label: t("Delete"), onClick: onDeleteClick, buttonIcon: , - color: "error" + color: "error", + isHidden: !maintainSubsidiary, }, ], [onTaskClick, t], diff --git a/src/components/SubsidiarySearch/SubsidiarySearchWrapper.tsx b/src/components/SubsidiarySearch/SubsidiarySearchWrapper.tsx index 13e8312..e6a8134 100644 --- a/src/components/SubsidiarySearch/SubsidiarySearchWrapper.tsx +++ b/src/components/SubsidiarySearch/SubsidiarySearchWrapper.tsx @@ -2,15 +2,16 @@ import React from "react"; import SubsidiarySearch from "./SubsidiarySearch"; import SubsidiarySearchLoading from "./SubsidiarySearchLoading"; import { fetchAllSubsidiaries } from "@/app/api/subsidiary"; +import { getUserAbilities } from "@/app/utils/commonUtil"; interface SubComponents { Loading: typeof SubsidiarySearchLoading; } const SubsidiarySearchWrapper: React.FC & SubComponents = async () => { - const subsidiaries = await fetchAllSubsidiaries(); + const [subsidiaries, abilities] = await Promise.all([fetchAllSubsidiaries(), getUserAbilities()]); - return ; + return ; }; SubsidiarySearchWrapper.Loading = SubsidiarySearchLoading; diff --git a/src/components/TeamSearch/TeamSearch.tsx b/src/components/TeamSearch/TeamSearch.tsx index 571a6ab..3c16a11 100644 --- a/src/components/TeamSearch/TeamSearch.tsx +++ b/src/components/TeamSearch/TeamSearch.tsx @@ -10,14 +10,16 @@ import DeleteIcon from "@mui/icons-material/Delete"; import { useRouter } from "next/navigation"; import { deleteTeam } from "@/app/api/team/actions"; import { deleteDialog, successDialog } from "../Swal/CustomAlerts"; +import { MAINTAIN_TEAM } from "@/middleware"; interface Props { team: TeamResult[]; + abilities: String[]; } type SearchQuery = Partial>; type SearchParamNames = keyof SearchQuery; -const TeamSearch: React.FC = ({ team }) => { +const TeamSearch: React.FC = ({ team, abilities }) => { const { t } = useTranslation(); const [filteredTeam, setFilteredTeam] = useState(team); const router = useRouter(); @@ -29,6 +31,8 @@ const TeamSearch: React.FC = ({ team }) => { const teamLead = t("teamLead") const delete_t = t("delete") + const maintainTeam = abilities.includes(MAINTAIN_TEAM) + const searchCriteria: Criterion[] = useMemo( () => [ { @@ -77,6 +81,7 @@ const TeamSearch: React.FC = ({ team }) => { label: edit, onClick: onTeamClick, buttonIcon: , + isHidden: !maintainTeam, }, { name: "name", label: name }, { name: "code", label: code }, @@ -87,7 +92,8 @@ const TeamSearch: React.FC = ({ team }) => { label: delete_t, onClick: onDeleteClick, buttonIcon: , - color: "error" + color: "error", + isHidden: !maintainTeam, }, ], [t] diff --git a/src/components/TeamSearch/TeamSearchWrapper.tsx b/src/components/TeamSearch/TeamSearchWrapper.tsx index bd4f714..f27f228 100644 --- a/src/components/TeamSearch/TeamSearchWrapper.tsx +++ b/src/components/TeamSearch/TeamSearchWrapper.tsx @@ -3,6 +3,7 @@ import React from "react"; import TeamSearch from "./TeamSearch"; import TeamSearchLoading from "./TeamSearchLoading"; import { fetchTeam } from "@/app/api/team"; +import { getUserAbilities } from "@/app/utils/commonUtil"; // import { preloadTeam } from "@/app/api/Team"; interface SubComponents { @@ -10,10 +11,10 @@ interface SubComponents { } const TeamSearchWrapper: React.FC & SubComponents = async () => { - const Team = await fetchTeam(); + const [Team, abilities] = await Promise.all([fetchTeam(), getUserAbilities()]); console.log(Team); - return ; + return ; }; TeamSearchWrapper.Loading = TeamSearchLoading; diff --git a/src/middleware.ts b/src/middleware.ts index 6dce586..a719d65 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -11,22 +11,41 @@ export const [ NORMAL_STAFF, SUPPORTING_STAFF ] = [ - "Super Admin", - "Top Management", - "Team Leader", - "Normal Staff", - "Supporting Staff" -] + "Super Admin", + "Top Management", + "Team Leader", + "Normal Staff", + "Supporting Staff" + ] // abilities export const [ - VIEW_USER, MAINTAIN_USER, MAINTAIN_TIMESHEET, VIEW_TASK_TEMPLATE, VIEW_GROUP, - VIEW_MASTERDATA, - MAINTAIN_MASTERDATA, + VIEW_CLIENT, + VIEW_SUBSIDIARY, + VIEW_STAFF, + VIEW_COMPANY, + VIEW_SKILL, + VIEW_DEPARTMENT, + VIEW_POSITION, + VIEW_SALARY, + VIEW_TEAM, + VIEW_USER_GROUP, + VIEW_HOLIDAY, + MAINTAIN_CLIENT, + MAINTAIN_SUBSIDIARY, + MAINTAIN_STAFF, + MAINTAIN_COMPANY, + MAINTAIN_SKILL, + MAINTAIN_DEPARTMENT, + MAINTAIN_POSITION, + MAINTAIN_SALARY, + MAINTAIN_TEAM, + MAINTAIN_USER_GROUP, + MAINTAIN_HOLIDAY, VIEW_DASHBOARD_SELF, VIEW_DASHBOARD_ALL, IMPORT_INVOICE, @@ -41,27 +60,46 @@ export const [ DELETE_PROJECT, MAINTAIN_TIMESHEET_FAST_TIME_ENTRY, ] = [ - 'VIEW_USER', - 'MAINTAIN_USER', - 'MAINTAIN_TIMESHEET', - 'VIEW_TASK_TEMPLATE', - 'VIEW_GROUP', - 'VIEW_MASTERDATA', - 'MAINTAIN_MASTERDATA', - 'VIEW_DASHBOARD_SELF', - 'VIEW_DASHBOARD_ALL', - 'IMPORT_INVOICE', - 'MAINTAIN_GROUP', - 'GENERATE_REPORTS', - 'VIEW_STAFF_PROFILE', - 'IMPORT_RECEIPT', - 'MAINTAIN_TASK_TEMPLATE', - 'MAINTAIN_TIMESHEET_7DAYS', - 'VIEW_PROJECT', - 'MAINTAIN_PROJECT', - 'DELETE_PROJECT', - 'MAINTAIN_TIMESHEET_FAST_TIME_ENTRY' -] + 'MAINTAIN_USER', + 'MAINTAIN_TIMESHEET', + 'VIEW_TASK_TEMPLATE', + 'VIEW_GROUP', + 'VIEW_CLIENT', + 'VIEW_SUBSIDIARY', + 'VIEW_STAFF', + 'VIEW_COMPANY', + 'VIEW_SKILL', + 'VIEW_DEPARTMENT', + 'VIEW_POSITION', + 'VIEW_SALARY', + 'VIEW_TEAM', + 'VIEW_USER_GROUP', + 'VIEW_HOLIDAY', + 'MAINTAIN_CLIENT', + 'MAINTAIN_SUBSIDIARY', + 'MAINTAIN_STAFF', + 'MAINTAIN_COMPANY', + 'MAINTAIN_SKILL', + 'MAINTAIN_DEPARTMENT', + 'MAINTAIN_POSITION', + 'MAINTAIN_SALARY', + 'MAINTAIN_TEAM', + 'MAINTAIN_USER_GROUP', + 'MAINTAIN_HOLIDAY', + 'VIEW_DASHBOARD_SELF', + 'VIEW_DASHBOARD_ALL', + 'IMPORT_INVOICE', + 'MAINTAIN_GROUP', + 'GENERATE_REPORTS', + 'VIEW_STAFF_PROFILE', + 'IMPORT_RECEIPT', + 'MAINTAIN_TASK_TEMPLATE', + 'MAINTAIN_TIMESHEET_7DAYS', + 'VIEW_PROJECT', + 'MAINTAIN_PROJECT', + 'DELETE_PROJECT', + 'MAINTAIN_TIMESHEET_FAST_TIME_ENTRY' + ] const PRIVATE_ROUTES = [ "/analytics", @@ -93,7 +131,7 @@ export default async function middleware( const authMiddleware = withAuth({ pages: authOptions.pages, callbacks: { - authorized: ({req, token}) => { + authorized: ({ req, token }) => { let isAuth = Boolean(token); if (!Boolean(token)) { return Boolean(token) @@ -109,26 +147,96 @@ export default async function middleware( } if (req.nextUrl.pathname.startsWith('/settings')) { - isAuth = [VIEW_MASTERDATA, MAINTAIN_MASTERDATA].some((ability) => abilities.includes(ability)); - } + isAuth = [ + VIEW_CLIENT, + VIEW_SUBSIDIARY, + VIEW_STAFF, + VIEW_COMPANY, + VIEW_SKILL, + VIEW_DEPARTMENT, + VIEW_POSITION, + VIEW_SALARY, + VIEW_TEAM, + VIEW_USER_GROUP, + VIEW_HOLIDAY, + MAINTAIN_CLIENT, + MAINTAIN_SUBSIDIARY, + MAINTAIN_STAFF, + MAINTAIN_COMPANY, + MAINTAIN_SKILL, + MAINTAIN_DEPARTMENT, + MAINTAIN_POSITION, + MAINTAIN_SALARY, + MAINTAIN_TEAM, + MAINTAIN_USER_GROUP, + MAINTAIN_HOLIDAY + ].some((ability) => abilities.includes(ability)); + } + + if (req.nextUrl.pathname.startsWith('/settings/customer/create') || req.nextUrl.pathname.startsWith('/settings/customer/edit')) { + isAuth = [MAINTAIN_CLIENT].some((ability) => abilities.includes(ability)); + } + + if (req.nextUrl.pathname.startsWith('/settings/subsidiary/create') || req.nextUrl.pathname.startsWith('/settings/subsidiary/edit')) { + isAuth = [MAINTAIN_SUBSIDIARY].some((ability) => abilities.includes(ability)); + } + + if (req.nextUrl.pathname.startsWith('/settings/staff/create') || req.nextUrl.pathname.startsWith('/settings/staff/edit')) { + isAuth = [MAINTAIN_STAFF].some((ability) => abilities.includes(ability)); + } + + if (req.nextUrl.pathname.startsWith('/settings/company/create') || req.nextUrl.pathname.startsWith('/settings/company/edit')) { + isAuth = [MAINTAIN_COMPANY].some((ability) => abilities.includes(ability)); + } + + if (req.nextUrl.pathname.startsWith('/settings/skill/create') || req.nextUrl.pathname.startsWith('/settings/skill/edit')) { + isAuth = [MAINTAIN_SKILL].some((ability) => abilities.includes(ability)); + } + + if (req.nextUrl.pathname.startsWith('/settings/department/create') || req.nextUrl.pathname.startsWith('/settings/department/edit')) { + isAuth = [MAINTAIN_DEPARTMENT].some((ability) => abilities.includes(ability)); + } + + if (req.nextUrl.pathname.startsWith('/settings/position/create') || req.nextUrl.pathname.startsWith('/settings/position/edit')) { + isAuth = [MAINTAIN_POSITION].some((ability) => abilities.includes(ability)); + } + + if (req.nextUrl.pathname.startsWith('/settings/team/create') || req.nextUrl.pathname.startsWith('/settings/team/edit')) { + isAuth = [MAINTAIN_TEAM].some((ability) => abilities.includes(ability)); + } + + if (req.nextUrl.pathname.startsWith('/settings/group/create') || req.nextUrl.pathname.startsWith('/settings/group/edit')) { + isAuth = [MAINTAIN_USER_GROUP].some((ability) => abilities.includes(ability)); + } + + if (req.nextUrl.pathname.startsWith('/settings/holiday/create') || req.nextUrl.pathname.startsWith('/settings/holiday/edit')) { + isAuth = [MAINTAIN_HOLIDAY].some((ability) => abilities.includes(ability)); + } + if (req.nextUrl.pathname.startsWith('/settings/user')) { - isAuth = [MAINTAIN_USER, VIEW_USER].some((ability) => abilities.includes(ability)); + isAuth = [MAINTAIN_USER].some((ability) => abilities.includes(ability)); } + if (req.nextUrl.pathname.startsWith('/settings/staff/user')) { - isAuth = [MAINTAIN_USER, VIEW_USER].some((ability) => abilities.includes(ability)); + isAuth = [MAINTAIN_USER].some((ability) => abilities.includes(ability)); } + if (req.nextUrl.pathname.startsWith('/analytics')) { isAuth = [GENERATE_REPORTS].some((ability) => abilities.includes(ability)); } + if (req.nextUrl.pathname.startsWith('/settings/staff/edit')) { isAuth = [VIEW_STAFF_PROFILE].some((ability) => abilities.includes(ability)); } + if (req.nextUrl.pathname.startsWith('/invoice')) { isAuth = [IMPORT_INVOICE, IMPORT_RECEIPT].some((ability) => abilities.includes(ability)); } + if (req.nextUrl.pathname.startsWith('/dashboard')) { isAuth = [VIEW_DASHBOARD_ALL, VIEW_DASHBOARD_SELF].some((ability) => abilities.includes(ability)); } + return isAuth } }