| @@ -1,11 +1,9 @@ | |||||
| import { Subsidiary } from '@/app/api/customer'; | |||||
| "use server"; | "use server"; | ||||
| import { serverFetchJson, serverFetchWithNoContent } from "@/app/utils/fetchUtil"; | import { serverFetchJson, serverFetchWithNoContent } from "@/app/utils/fetchUtil"; | ||||
| import { BASE_API_URL } from "@/config/api"; | import { BASE_API_URL } from "@/config/api"; | ||||
| import { Contact, Customer, SaveCustomerResponse } from "."; | import { Contact, Customer, SaveCustomerResponse } from "."; | ||||
| import { revalidateTag } from "next/cache"; | |||||
| import { cache } from "react"; | |||||
| import { revalidatePath, revalidateTag } from "next/cache"; | |||||
| export interface CustomerFormInputs { | export interface CustomerFormInputs { | ||||
| @@ -72,6 +70,7 @@ export const deleteCustomer = async (id: number) => { | |||||
| headers: { "Content-Type": "application/json" }, | headers: { "Content-Type": "application/json" }, | ||||
| }, | }, | ||||
| ); | ); | ||||
| revalidateTag("customers") | |||||
| return customer | return customer | ||||
| }; | }; | ||||
| @@ -72,7 +72,9 @@ export const preloadAllCustomers = () => { | |||||
| }; | }; | ||||
| export const fetchAllCustomers = cache(async () => { | export const fetchAllCustomers = cache(async () => { | ||||
| return serverFetchJson<Customer[]>(`${BASE_API_URL}/customer`); | |||||
| return serverFetchJson<Customer[]>(`${BASE_API_URL}/customer`, { | |||||
| next: { tags: ["customers"]} | |||||
| }); | |||||
| }); | }); | ||||
| export const fetchAllSubsidiaries = cache(async () => { | export const fetchAllSubsidiaries = cache(async () => { | ||||
| @@ -70,5 +70,7 @@ export const deleteSubsidiary = async (id: number) => { | |||||
| }, | }, | ||||
| ); | ); | ||||
| revalidateTag("subsidiaries"); | |||||
| return subsidiary | return subsidiary | ||||
| }; | }; | ||||
| @@ -74,7 +74,7 @@ export const fetchAllSubsidiaries = cache(async () => { | |||||
| return serverFetchJson<Subsidiary[]>( | return serverFetchJson<Subsidiary[]>( | ||||
| `${BASE_API_URL}/subsidiary`, | `${BASE_API_URL}/subsidiary`, | ||||
| { | { | ||||
| next: { tags: ["subsidiary"] }, | |||||
| next: { tags: ["subsidiaries"] }, | |||||
| }, | }, | ||||
| ); | ); | ||||
| }); | }); | ||||
| @@ -1,7 +1,7 @@ | |||||
| "use client"; | "use client"; | ||||
| import { Customer } from "@/app/api/customer"; | import { Customer } from "@/app/api/customer"; | ||||
| import React, { useCallback, useMemo, useState } from "react"; | |||||
| import React, { useCallback, useEffect, useMemo, useState } from "react"; | |||||
| import SearchBox, { Criterion } from "../SearchBox"; | import SearchBox, { Criterion } from "../SearchBox"; | ||||
| import { useTranslation } from "react-i18next"; | import { useTranslation } from "react-i18next"; | ||||
| import SearchResults, { Column } from "../SearchResults"; | import SearchResults, { Column } from "../SearchResults"; | ||||
| @@ -24,6 +24,9 @@ const CustomerSearch: React.FC<Props> = ({ customers }) => { | |||||
| const searchParams = useSearchParams() | const searchParams = useSearchParams() | ||||
| const [filteredCustomers, setFilteredCustomers] = useState(customers); | const [filteredCustomers, setFilteredCustomers] = useState(customers); | ||||
| useEffect(() => { | |||||
| setFilteredCustomers(customers) | |||||
| }, [customers]) | |||||
| const searchCriteria: Criterion<SearchParamNames>[] = useMemo( | const searchCriteria: Criterion<SearchParamNames>[] = useMemo( | ||||
| () => [ | () => [ | ||||
| { label: t("Customer Code"), paramName: "code", type: "text" }, | { label: t("Customer Code"), paramName: "code", type: "text" }, | ||||
| @@ -47,8 +50,6 @@ const CustomerSearch: React.FC<Props> = ({ customers }) => { | |||||
| await deleteCustomer(customer.id) | await deleteCustomer(customer.id) | ||||
| successDialog("Delete Success", t) | successDialog("Delete Success", t) | ||||
| setFilteredCustomers((prev) => prev.filter((obj) => obj.id !== customer.id)) | |||||
| }, t) | }, t) | ||||
| }, []); | }, []); | ||||
| @@ -8,7 +8,7 @@ interface SubComponents { | |||||
| } | } | ||||
| const CustomerSearchWrapper: React.FC & SubComponents = async () => { | const CustomerSearchWrapper: React.FC & SubComponents = async () => { | ||||
| const customers = await fetchAllCustomers(); | |||||
| const [customers] = await Promise.all([fetchAllCustomers()]); | |||||
| return <CustomerSearch customers={customers} />; | return <CustomerSearch customers={customers} />; | ||||
| }; | }; | ||||
| @@ -66,7 +66,7 @@ const NavigationContent: React.FC<Props> = ({ abilities }) => { | |||||
| { | { | ||||
| icon: <WorkHistory />, | icon: <WorkHistory />, | ||||
| label: "User Workspace", | label: "User Workspace", | ||||
| path: "/home", | |||||
| path: "/home", | |||||
| showOnMobile: true, | showOnMobile: true, | ||||
| }, | }, | ||||
| { | { | ||||
| @@ -131,7 +131,14 @@ const NavigationContent: React.FC<Props> = ({ abilities }) => { | |||||
| // }, | // }, | ||||
| { icon: <Assignment />, label: "Project Management", path: "/projects", isHidden: ![MAINTAIN_PROJECT].some((ability) => abilities?.includes(ability)) }, | { icon: <Assignment />, label: "Project Management", path: "/projects", isHidden: ![MAINTAIN_PROJECT].some((ability) => abilities?.includes(ability)) }, | ||||
| { icon: <Task />, label: "Task Template", path: "/tasks", isHidden: ![MAINTAIN_TASK_TEMPLATE].some((ability) => abilities?.includes(ability)) }, | { icon: <Task />, label: "Task Template", path: "/tasks", isHidden: ![MAINTAIN_TASK_TEMPLATE].some((ability) => abilities?.includes(ability)) }, | ||||
| { icon: <Payments />, label: "Invoice", path: "/invoice" }, | |||||
| { | |||||
| icon: <Payments />, | |||||
| label: "Invoice", | |||||
| path: "/invoice", | |||||
| isHidden: ![VIEW_MASTERDATA, MAINTAIN_MASTERDATA].some((ability) => | |||||
| abilities!.includes(ability), | |||||
| ), | |||||
| }, | |||||
| { | { | ||||
| icon: <Analytics />, | icon: <Analytics />, | ||||
| label: "Analysis Report", | label: "Analysis Report", | ||||
| @@ -1,6 +1,6 @@ | |||||
| "use client"; | "use client"; | ||||
| import React, { useCallback, useMemo, useState } from "react"; | |||||
| import React, { useCallback, useEffect, useMemo, useState } from "react"; | |||||
| import SearchBox, { Criterion } from "../SearchBox"; | import SearchBox, { Criterion } from "../SearchBox"; | ||||
| import { useTranslation } from "react-i18next"; | import { useTranslation } from "react-i18next"; | ||||
| import SearchResults, { Column } from "../SearchResults"; | import SearchResults, { Column } from "../SearchResults"; | ||||
| @@ -24,6 +24,10 @@ const SubsidiarySearch: React.FC<Props> = ({ subsidiaries }) => { | |||||
| const searchParams = useSearchParams() | const searchParams = useSearchParams() | ||||
| const [filteredSubsidiaries, setFilteredSubsidiaries] = useState(subsidiaries); | const [filteredSubsidiaries, setFilteredSubsidiaries] = useState(subsidiaries); | ||||
| useEffect(() => { | |||||
| setFilteredSubsidiaries(subsidiaries) | |||||
| }, [subsidiaries]) | |||||
| const searchCriteria: Criterion<SearchParamNames>[] = useMemo( | const searchCriteria: Criterion<SearchParamNames>[] = useMemo( | ||||
| () => [ | () => [ | ||||
| { label: t("Subsidiary Code"), paramName: "code", type: "text" }, | { label: t("Subsidiary Code"), paramName: "code", type: "text" }, | ||||
| @@ -48,7 +52,7 @@ const SubsidiarySearch: React.FC<Props> = ({ subsidiaries }) => { | |||||
| successDialog(t("Delete Success"), t) | successDialog(t("Delete Success"), t) | ||||
| setFilteredSubsidiaries((prev) => prev.filter((obj) => obj.id !== subsidiary.id)) | |||||
| // setFilteredSubsidiaries((prev) => prev.filter((obj) => obj.id !== subsidiary.id)) | |||||
| }, t) | }, t) | ||||
| }, []); | }, []); | ||||
| @@ -121,6 +121,9 @@ export default async function middleware( | |||||
| if (req.nextUrl.pathname.startsWith('/settings/staff/edit')) { | if (req.nextUrl.pathname.startsWith('/settings/staff/edit')) { | ||||
| isAuth = [VIEW_STAFF_PROFILE].some((ability) => abilities.includes(ability)); | isAuth = [VIEW_STAFF_PROFILE].some((ability) => abilities.includes(ability)); | ||||
| } | } | ||||
| if (req.nextUrl.pathname.startsWith('/invoice')) { | |||||
| isAuth = [IMPORT_INVOICE].some((ability) => abilities.includes(ability)); | |||||
| } | |||||
| return isAuth | return isAuth | ||||
| } | } | ||||
| } | } | ||||