@@ -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 ( | |||
<> | |||
<Stack | |||
@@ -31,14 +36,14 @@ const Company: React.FC = async () => { | |||
<Typography variant="h4" marginInlineEnd={2}> | |||
{t("Company")} | |||
</Typography> | |||
<Button | |||
{maintainCompany && <Button | |||
variant="contained" | |||
startIcon={<Add />} | |||
LinkComponent={Link} | |||
href="/settings/company/create" | |||
> | |||
{t("Create Company")} | |||
</Button> | |||
</Button>} | |||
</Stack> | |||
<Suspense fallback={<CompanySearch.Loading />}> | |||
<CompanySearch/> | |||
@@ -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 () => { | |||
<Typography variant="h4" marginInlineEnd={2}> | |||
{t("Customer")} | |||
</Typography> | |||
<Button | |||
{maintainClient && <Button | |||
variant="contained" | |||
startIcon={<Add />} | |||
LinkComponent={Link} | |||
href="/settings/customer/create" | |||
> | |||
{t("Create Customer")} | |||
</Button> | |||
</Button>} | |||
</Stack> | |||
<I18nProvider namespaces={["customer", "common"]}> | |||
<Suspense fallback={<CustomerSearch.Loading />}> | |||
@@ -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 ( | |||
<> | |||
<Stack | |||
@@ -31,14 +36,14 @@ const Department: React.FC = async () => { | |||
<Typography variant="h4" marginInlineEnd={2}> | |||
{t("Department")} | |||
</Typography> | |||
<Button | |||
{maintainDepartment && <Button | |||
variant="contained" | |||
startIcon={<Add />} | |||
LinkComponent={Link} | |||
href="/settings/department/new" | |||
> | |||
{t("Create Department")} | |||
</Button> | |||
</Button>} | |||
</Stack> | |||
<Suspense fallback={<DepartmentSearch.Loading />}> | |||
<DepartmentSearch/> | |||
@@ -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 ( | |||
<> | |||
<Stack | |||
@@ -31,14 +36,14 @@ const Position: React.FC = async () => { | |||
<Typography variant="h4" marginInlineEnd={2}> | |||
{t("Position")} | |||
</Typography> | |||
<Button | |||
{maintainPosition && <Button | |||
variant="contained" | |||
startIcon={<Add />} | |||
LinkComponent={Link} | |||
href="/settings/position/new" | |||
> | |||
{t("Create Position")} | |||
</Button> | |||
</Button>} | |||
</Stack> | |||
<Suspense fallback={<PositionSearch.Loading />}> | |||
<PositionSearch/> | |||
@@ -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 ( | |||
<> | |||
<Stack | |||
@@ -29,14 +35,14 @@ const Skill: React.FC = async () => { | |||
<Typography variant="h4" marginInlineEnd={2}> | |||
{t("Skill")} | |||
</Typography> | |||
<Button | |||
{maintainSkill && <Button | |||
variant="contained" | |||
startIcon={<Add />} | |||
LinkComponent={Link} | |||
href="/settings/skill/create" | |||
> | |||
{t("Create Skill")} | |||
</Button> | |||
</Button>} | |||
</Stack> | |||
<I18nProvider namespaces={["skill", "common"]}> | |||
<Suspense fallback={<SkillSearch.Loading />}> | |||
@@ -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 ( | |||
<> | |||
<Stack | |||
@@ -29,14 +35,14 @@ const Staff: React.FC = async () => { | |||
<Typography variant="h4" marginInlineEnd={2}> | |||
{t("Staff")} | |||
</Typography> | |||
<Button | |||
{maintainStaff && <Button | |||
variant="contained" | |||
startIcon={<Add />} | |||
LinkComponent={Link} | |||
href="/settings/staff/create" | |||
> | |||
{t("Create Staff")} | |||
</Button> | |||
</Button>} | |||
</Stack> | |||
<I18nProvider namespaces={["staff", "common"]}> | |||
<Suspense fallback={<StaffSearch.Loading />}> | |||
@@ -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 () => { | |||
<Typography variant="h4" marginInlineEnd={2}> | |||
{t("Subsidiary")} | |||
</Typography> | |||
<Button | |||
{maintainSubsidiary && <Button | |||
variant="contained" | |||
startIcon={<Add />} | |||
LinkComponent={Link} | |||
href="/settings/subsidiary/create" | |||
> | |||
{t("Create Subsidiary")} | |||
</Button> | |||
</Button>} | |||
</Stack> | |||
<I18nProvider namespaces={["subsidiary", "common"]}> | |||
<Suspense fallback={<SubsidiarySearch.Loading />}> | |||
@@ -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 ( | |||
<> | |||
<Stack | |||
@@ -32,14 +38,14 @@ export const metadata: Metadata = { | |||
<Typography variant="h4" marginInlineEnd={2}> | |||
{t("Team")} | |||
</Typography> | |||
<Button | |||
{maintainTeam && <Button | |||
variant="contained" | |||
startIcon={<Add />} | |||
LinkComponent={Link} | |||
href="/settings/team/create" | |||
> | |||
{t("Create Team")} | |||
</Button> | |||
</Button>} | |||
</Stack> | |||
<I18nProvider namespaces={["team", "common"]}> | |||
<Suspense fallback={<TeamSearch.Loading />}> | |||
@@ -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<Props> = ({ holidays }) => { | |||
const CompanyHoliday: React.FC<Props> = ({ 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<Props> = ({ holidays }) => { | |||
}} | |||
/> | |||
<CompanyHolidayDialog | |||
open={open} | |||
open={maintainHoliday && open} | |||
onClose={handleClose} | |||
title={ | |||
!editable | |||
@@ -6,6 +6,7 @@ import { fetchCompanys } from "@/app/api/companys"; | |||
import Holidays from "date-holidays"; | |||
import { HolidaysResult, fetchHolidays, HolidaysList } from "@/app/api/holidays"; | |||
import { convertDateArrayToString } from "@/app/utils/formatUtil"; | |||
import { getUserAbilities } from "@/app/utils/commonUtil"; | |||
interface SubComponents { | |||
Loading: typeof CompanyHolidayLoading; | |||
@@ -14,7 +15,7 @@ interface SubComponents { | |||
const CompanyHolidayWrapper: React.FC & SubComponents = async () => { | |||
// 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 <CompanyHoliday holidays={convertedHolidays as HolidaysList[]} />; | |||
return <CompanyHoliday holidays={convertedHolidays as HolidaysList[]} abilities={abilities}/>; | |||
}; | |||
CompanyHolidayWrapper.Loading = CompanyHolidayLoading; | |||
@@ -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<Omit<CompanyResult, "id">>; | |||
type SearchParamNames = keyof SearchQuery; | |||
const CompanySearch: React.FC<Props> = ({ companys }) => { | |||
const CompanySearch: React.FC<Props> = ({ companys, abilities }) => { | |||
const { t } = useTranslation("companys"); | |||
const router = useRouter() | |||
const [filteredCompanys, setFilteredCompanys] = useState(companys); | |||
const maintainCompany = abilities.includes(MAINTAIN_COMPANY) | |||
const searchCriteria: Criterion<SearchParamNames>[] = useMemo( | |||
() => [ | |||
{ label: t("Company code"), paramName: "companyCode", type: "text" }, | |||
@@ -62,6 +66,7 @@ const CompanySearch: React.FC<Props> = ({ companys }) => { | |||
label: t("Details"), | |||
onClick: onProjectClick, | |||
buttonIcon: <EditNote />, | |||
isHidden: !maintainCompany, | |||
}, | |||
{ name: "companyCode", label: t("Company Code") }, | |||
{ name: "name", label: t("Company Name") }, | |||
@@ -74,7 +79,8 @@ const CompanySearch: React.FC<Props> = ({ companys }) => { | |||
label: t("Delete"), | |||
onClick: onDeleteClick, | |||
buttonIcon: <DeleteIcon />, | |||
color: "error" | |||
color: "error", | |||
isHidden: !maintainCompany, | |||
}, | |||
], | |||
[t, onProjectClick], | |||
@@ -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 <CompanySearch companys={Companys} />; | |||
return <CompanySearch companys={Companys} abilities={abilities}/>; | |||
}; | |||
CompanySearchWrapper.Loading = CompanySearchLoading; | |||
@@ -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<Omit<Customer, "id">>; | |||
type SearchParamNames = keyof SearchQuery; | |||
const CustomerSearch: React.FC<Props> = ({ customers }) => { | |||
const CustomerSearch: React.FC<Props> = ({ 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<Props> = ({ customers }) => { | |||
label: t("Details"), | |||
onClick: onTaskClick, | |||
buttonIcon: <EditNote />, | |||
isHidden: !maintainClient | |||
}, | |||
{ name: "code", label: t("Customer Code") }, | |||
{ name: "name", label: t("Customer Name") }, | |||
@@ -68,7 +72,8 @@ const CustomerSearch: React.FC<Props> = ({ customers }) => { | |||
label: t("Delete"), | |||
onClick: onDeleteClick, | |||
buttonIcon: <DeleteIcon />, | |||
color: "error" | |||
color: "error", | |||
isHidden: !maintainClient | |||
}, | |||
], | |||
[onTaskClick, t], | |||
@@ -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 <CustomerSearch customers={customers} />; | |||
return <CustomerSearch customers={customers} abilities={abilities}/>; | |||
}; | |||
CustomerSearchWrapper.Loading = CustomerSearchLoading; | |||
@@ -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<Omit<DepartmentResult, "id">>; | |||
type SearchParamNames = keyof SearchQuery; | |||
const DepartmentSearch: React.FC<Props> = ({ departments }) => { | |||
const DepartmentSearch: React.FC<Props> = ({ departments, abilities }) => { | |||
const { t } = useTranslation("departments"); | |||
const router = useRouter(); | |||
const [filteredDepartments, setFilteredDepartments] = useState(departments); | |||
const maintainDepartment = abilities.includes(MAINTAIN_DEPARTMENT) | |||
const searchCriteria: Criterion<SearchParamNames>[] = useMemo( | |||
() => [ | |||
@@ -60,6 +63,7 @@ const DepartmentSearch: React.FC<Props> = ({ departments }) => { | |||
label: t("Details"), | |||
onClick: onProjectClick, | |||
buttonIcon: <EditNote />, | |||
isHidden: !maintainDepartment, | |||
}, | |||
{ name: "code", label: t("Department Code") }, | |||
{ name: "name", label: t("Department Name") }, | |||
@@ -69,7 +73,8 @@ const DepartmentSearch: React.FC<Props> = ({ departments }) => { | |||
label: t("Delete"), | |||
onClick: onDeleteClick, | |||
buttonIcon: <DeleteIcon />, | |||
color: "error" | |||
color: "error", | |||
isHidden: !maintainDepartment, | |||
}, | |||
], | |||
[t, onProjectClick], | |||
@@ -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 <DepartmentSearch departments={Departments} />; | |||
return <DepartmentSearch departments={Departments} abilities={abilities} />; | |||
}; | |||
DepartmentSearchWrapper.Loading = DepartmentSearchLoading; | |||
@@ -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<Props> = ({ abilities, username }) => { | |||
icon: <Settings />, | |||
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: <GroupIcon />, label: "Client", path: "/settings/customer" }, | |||
{ | |||
icon: <GroupIcon />, | |||
label: "Client", | |||
path: "/settings/customer", | |||
isHidden: ![VIEW_CLIENT, MAINTAIN_CLIENT].some((ability) => abilities!.includes(ability),), | |||
}, | |||
{ | |||
icon: <BusinessIcon />, | |||
label: "Subsidiary", | |||
path: "/settings/subsidiary", | |||
isHidden: ![VIEW_SUBSIDIARY, MAINTAIN_SUBSIDIARY].some((ability) => abilities!.includes(ability),), | |||
}, | |||
{ | |||
icon: <Staff />, | |||
label: "Staff", | |||
path: "/settings/staff", | |||
isHidden: ![VIEW_STAFF, MAINTAIN_STAFF].some((ability) => abilities!.includes(ability),), | |||
}, | |||
{ | |||
icon: <Company />, | |||
label: "Company", | |||
path: "/settings/company", | |||
isHidden: ![VIEW_COMPANY, MAINTAIN_COMPANY].some((ability) => abilities!.includes(ability),), | |||
}, | |||
{ | |||
icon: <EmojiEventsIcon />, | |||
label: "Skill", | |||
path: "/settings/skill", | |||
isHidden: ![VIEW_SKILL, MAINTAIN_SKILL].some((ability) => abilities!.includes(ability),), | |||
}, | |||
{ icon: <Staff />, label: "Staff", path: "/settings/staff" }, | |||
{ icon: <Company />, label: "Company", path: "/settings/company" }, | |||
{ icon: <EmojiEventsIcon />, label: "Skill", path: "/settings/skill" }, | |||
{ | |||
icon: <Department />, | |||
label: "Department", | |||
path: "/settings/department", | |||
isHidden: ![VIEW_DEPARTMENT, MAINTAIN_DEPARTMENT].some((ability) => abilities!.includes(ability),), | |||
}, | |||
{ | |||
icon: <Position />, | |||
label: "Position", | |||
path: "/settings/position", | |||
isHidden: ![VIEW_POSITION, MAINTAIN_POSITION].some((ability) => abilities!.includes(ability),), | |||
}, | |||
{ | |||
icon: <Salary />, | |||
label: "Salary", | |||
path: "/settings/salary", | |||
isHidden: ![VIEW_SALARY, MAINTAIN_SALARY].some((ability) => abilities!.includes(ability),), | |||
}, | |||
{ | |||
icon: <Team />, | |||
label: "Team", | |||
path: "/settings/team", | |||
isHidden: ![VIEW_TEAM, MAINTAIN_TEAM].some((ability) => abilities!.includes(ability),), | |||
}, | |||
{ icon: <Position />, label: "Position", path: "/settings/position" }, | |||
{ icon: <Salary />, label: "Salary", path: "/settings/salary" }, | |||
{ icon: <Team />, label: "Team", path: "/settings/team" }, | |||
// { icon: <ManageAccountsIcon />, label: "User", path: "/settings/user" }, | |||
{ | |||
icon: <ManageAccountsIcon />, | |||
label: "User Group", | |||
path: "/settings/group", | |||
isHidden: ![VIEW_USER_GROUP, MAINTAIN_USER_GROUP].some((ability) => abilities!.includes(ability),), | |||
}, | |||
{ | |||
icon: <Holiday />, | |||
label: "Holiday", | |||
path: "/settings/holiday", | |||
isHidden: ![VIEW_HOLIDAY, MAINTAIN_HOLIDAY].some((ability) => abilities!.includes(ability),), | |||
}, | |||
{ icon: <Holiday />, label: "Holiday", path: "/settings/holiday"}, | |||
{ icon: <FileUploadIcon />, label: "Import Excel File", path: "/settings/import", isHidden: username !== "2fi"}, | |||
{ icon: <FileUploadIcon />, label: "Import Excel File", path: "/settings/import", isHidden: username !== "2fi" }, | |||
], | |||
}, | |||
]; | |||
@@ -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<Omit<PositionResult, "id">>; | |||
type SearchParamNames = keyof SearchQuery; | |||
const PositionSearch: React.FC<Props> = ({ positions }) => { | |||
const PositionSearch: React.FC<Props> = ({ positions, abilities }) => { | |||
const { t } = useTranslation("positions"); | |||
const router = useRouter(); | |||
const [filteredPositions, setFilteredPositions] = useState(positions); | |||
const maintainPosition = abilities.includes(MAINTAIN_POSITION) | |||
const searchCriteria: Criterion<SearchParamNames>[] = useMemo( | |||
() => [ | |||
@@ -61,6 +64,7 @@ const PositionSearch: React.FC<Props> = ({ positions }) => { | |||
label: t("Details"), | |||
onClick: onPositionClick, | |||
buttonIcon: <EditNote />, | |||
isHidden: !maintainPosition, | |||
}, | |||
{ name: "code", label: t("Position Code") }, | |||
{ name: "name", label: t("Position Name") }, | |||
@@ -70,7 +74,8 @@ const PositionSearch: React.FC<Props> = ({ positions }) => { | |||
label: t("Delete"), | |||
onClick: onDeleteClick, | |||
buttonIcon: <DeleteIcon />, | |||
color: "error" | |||
color: "error", | |||
isHidden: !maintainPosition, | |||
}, | |||
], | |||
[t, onPositionClick], | |||
@@ -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 <PositionSearch positions={Positions} />; | |||
return <PositionSearch positions={Positions} abilities={abilities}/>; | |||
}; | |||
PositionSearchWrapper.Loading = PositionSearchLoading; | |||
@@ -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<Omit<SalaryResult, "id">>; | |||
type SearchParamNames = keyof SearchQuery; | |||
const SalarySearch: React.FC<Props> = ({ salarys }) => { | |||
const SalarySearch: React.FC<Props> = ({ salarys, abilities }) => { | |||
const { t } = useTranslation("salarys"); | |||
const [filteredSalarys, setFilteredSalarys] = useState(salarys); | |||
const maintainSalary = abilities.includes(MAINTAIN_SALARY) | |||
const searchCriteria: Criterion<SearchParamNames>[] = useMemo( | |||
() => [ | |||
@@ -109,7 +112,7 @@ const SalarySearch: React.FC<Props> = ({ salarys }) => { | |||
return ( | |||
<> | |||
<Stack | |||
{maintainSalary && <Stack | |||
direction="row" | |||
justifyContent="right" | |||
flexWrap="wrap" | |||
@@ -130,7 +133,7 @@ const SalarySearch: React.FC<Props> = ({ salarys }) => { | |||
{t("Export Salary")} | |||
</Button> | |||
</ButtonGroup> | |||
</Stack> | |||
</Stack>} | |||
<SearchBox | |||
criteria={searchCriteria} | |||
onSearch={(query) => { | |||
@@ -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 <SalarySearch salarys={salarysWithHourlyRate} />; | |||
return <SalarySearch salarys={salarysWithHourlyRate} abilities={abilities}/>; | |||
}; | |||
SalarySearchWrapper.Loading = SalarySearchLoading; | |||
@@ -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<Omit<SkillResult, "id">>; | |||
type SearchParamNames = keyof SearchQuery; | |||
const SkillSearch: React.FC<Props> = ({ skill }) => { | |||
const SkillSearch: React.FC<Props> = ({ skill, abilities }) => { | |||
const { t } = useTranslation(); | |||
const [filteredSkill, setFilteredSkill] = useState(skill); | |||
const router = useRouter(); | |||
@@ -30,6 +32,8 @@ const SkillSearch: React.FC<Props> = ({ skill }) => { | |||
const code = t("code") | |||
const description = t("description") | |||
const maintainSkill = abilities.includes(MAINTAIN_SKILL) | |||
const searchCriteria: Criterion<SearchParamNames>[] = useMemo( | |||
() => [ | |||
{ | |||
@@ -73,6 +77,7 @@ const SkillSearch: React.FC<Props> = ({ skill }) => { | |||
label: edit, | |||
onClick: onSkillClick, | |||
buttonIcon: <EditNote />, | |||
isHidden: !maintainSkill, | |||
}, | |||
{ name: "name", label: name }, | |||
{ name: "code", label: code }, | |||
@@ -83,6 +88,7 @@ const SkillSearch: React.FC<Props> = ({ skill }) => { | |||
onClick: deleteClick, | |||
buttonIcon: <DeleteIcon />, | |||
color: "error", | |||
isHidden: !maintainSkill, | |||
}, | |||
], | |||
[t, onSkillClick, deleteClick] | |||
@@ -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 <SkillSearch skill={skill} />; | |||
return <SkillSearch skill={skill} abilities={abilities}/>; | |||
}; | |||
SkillSearchWrapper.Loading = SkillSearchLoading; | |||
@@ -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<Omit<StaffResult, "id">>; | |||
type SearchParamNames = keyof SearchQuery; | |||
const StaffSearch: React.FC<Props> = ({ staff, teams, grades, positions, isAuthed }) => { | |||
const StaffSearch: React.FC<Props> = ({ staff, teams, grades, positions, abilities }) => { | |||
const { t } = useTranslation(); | |||
const [filteredStaff, setFilteredStaff] = useState(staff); | |||
const router = useRouter(); | |||
@@ -35,6 +35,9 @@ const StaffSearch: React.FC<Props> = ({ 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<SearchParamNames>[] = useMemo( | |||
() => [ | |||
{ | |||
@@ -104,13 +107,14 @@ const StaffSearch: React.FC<Props> = ({ staff, teams, grades, positions, isAuthe | |||
label: t("Actions"), | |||
onClick: onStaffClick, | |||
buttonIcon: <EditNote />, | |||
isHidden: !maintainStaff, | |||
}, | |||
{ | |||
name: "id", | |||
label: t("Users"), | |||
onClick: onUserClick, | |||
buttonIcon: <Person />, | |||
isHidden: isAuthed, | |||
isHidden: !maintainUser, | |||
}, | |||
{ name: "team", label: t("Team") }, | |||
{ name: "name", label: t("Staff Name") }, | |||
@@ -123,6 +127,7 @@ const StaffSearch: React.FC<Props> = ({ staff, teams, grades, positions, isAuthe | |||
onClick: deleteClick, | |||
buttonIcon: <DeleteIcon />, | |||
color: "error", | |||
isHidden: !maintainStaff, | |||
}, | |||
], | |||
[t, onStaffClick, deleteClick] | |||
@@ -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 <StaffSearch staff={staff} teams={teams} grades={grades} positions={positions} isAuthed={isAuthed}/>; | |||
return <StaffSearch staff={staff} teams={teams} grades={grades} positions={positions} abilities={abilities}/>; | |||
}; | |||
StaffSearchWrapper.Loading = StaffSearchLoading; | |||
@@ -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<Omit<Subsidiary, "id">>; | |||
type SearchParamNames = keyof SearchQuery; | |||
const SubsidiarySearch: React.FC<Props> = ({ subsidiaries }) => { | |||
const SubsidiarySearch: React.FC<Props> = ({ 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<Props> = ({ subsidiaries }) => { | |||
label: t("Details"), | |||
onClick: onTaskClick, | |||
buttonIcon: <EditNote />, | |||
isHidden: !maintainSubsidiary, | |||
}, | |||
{ name: "code", label: t("Subsidiary Code") }, | |||
{ name: "name", label: t("Subsidiary Name") }, | |||
@@ -71,7 +75,8 @@ const SubsidiarySearch: React.FC<Props> = ({ subsidiaries }) => { | |||
label: t("Delete"), | |||
onClick: onDeleteClick, | |||
buttonIcon: <DeleteIcon />, | |||
color: "error" | |||
color: "error", | |||
isHidden: !maintainSubsidiary, | |||
}, | |||
], | |||
[onTaskClick, t], | |||
@@ -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 <SubsidiarySearch subsidiaries={subsidiaries} />; | |||
return <SubsidiarySearch subsidiaries={subsidiaries} abilities={abilities} />; | |||
}; | |||
SubsidiarySearchWrapper.Loading = SubsidiarySearchLoading; | |||
@@ -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<Omit<TeamResult, "id">>; | |||
type SearchParamNames = keyof SearchQuery; | |||
const TeamSearch: React.FC<Props> = ({ team }) => { | |||
const TeamSearch: React.FC<Props> = ({ team, abilities }) => { | |||
const { t } = useTranslation(); | |||
const [filteredTeam, setFilteredTeam] = useState(team); | |||
const router = useRouter(); | |||
@@ -29,6 +31,8 @@ const TeamSearch: React.FC<Props> = ({ team }) => { | |||
const teamLead = t("teamLead") | |||
const delete_t = t("delete") | |||
const maintainTeam = abilities.includes(MAINTAIN_TEAM) | |||
const searchCriteria: Criterion<SearchParamNames>[] = useMemo( | |||
() => [ | |||
{ | |||
@@ -77,6 +81,7 @@ const TeamSearch: React.FC<Props> = ({ team }) => { | |||
label: edit, | |||
onClick: onTeamClick, | |||
buttonIcon: <EditNote />, | |||
isHidden: !maintainTeam, | |||
}, | |||
{ name: "name", label: name }, | |||
{ name: "code", label: code }, | |||
@@ -87,7 +92,8 @@ const TeamSearch: React.FC<Props> = ({ team }) => { | |||
label: delete_t, | |||
onClick: onDeleteClick, | |||
buttonIcon: <DeleteIcon />, | |||
color: "error" | |||
color: "error", | |||
isHidden: !maintainTeam, | |||
}, | |||
], | |||
[t] | |||
@@ -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 <TeamSearch team={Team} />; | |||
return <TeamSearch team={Team} abilities={abilities}/>; | |||
}; | |||
TeamSearchWrapper.Loading = TeamSearchLoading; | |||
@@ -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 | |||
} | |||
} | |||