@@ -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 | ||||
} | } | ||||
} | } | ||||