feat(sidebar): add calls section to navigation menu
This commit introduces a new "Calls" section to the main sidebar navigation to provide users with access to call logs. - Adds a new collapsible "Calls" menu item to the sidebar configuration. - Includes sub-links for "All Calls", "Incoming", "Outgoing", and "Missed" calls. - Creates new custom icons (GlobeSearch, CallIncome, CallMade, CallMissed, CallReceived) to support the new menu items. - Updates `apexcharts` and `react-apexcharts` dependencies to newer versions.
This commit is contained in:
Generated
+9
-9
@@ -11,7 +11,7 @@
|
|||||||
"@tanstack/react-query": "^5.85.6",
|
"@tanstack/react-query": "^5.85.6",
|
||||||
"@types/js-cookie": "^3.0.6",
|
"@types/js-cookie": "^3.0.6",
|
||||||
"@types/react-paginate": "^7.1.4",
|
"@types/react-paginate": "^7.1.4",
|
||||||
"apexcharts": "^4.5.0",
|
"apexcharts": "^4.7.0",
|
||||||
"axios": "^1.11.0",
|
"axios": "^1.11.0",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
@@ -26,7 +26,7 @@
|
|||||||
"next-themes": "^0.4.4",
|
"next-themes": "^0.4.4",
|
||||||
"nextjs-toploader": "^3.7.15",
|
"nextjs-toploader": "^3.7.15",
|
||||||
"react": "19.0.0",
|
"react": "19.0.0",
|
||||||
"react-apexcharts": "^1.7.0",
|
"react-apexcharts": "^1.8.0",
|
||||||
"react-dom": "19.0.0",
|
"react-dom": "19.0.0",
|
||||||
"react-hook-form": "^7.62.0",
|
"react-hook-form": "^7.62.0",
|
||||||
"react-intersection-observer": "^9.16.0",
|
"react-intersection-observer": "^9.16.0",
|
||||||
@@ -2353,9 +2353,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/apexcharts": {
|
"node_modules/apexcharts": {
|
||||||
"version": "4.5.0",
|
"version": "4.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-4.5.0.tgz",
|
"resolved": "https://registry.npmmirror.com/apexcharts/-/apexcharts-4.7.0.tgz",
|
||||||
"integrity": "sha512-E7ZkrVqPNBUWy/Rmg8DEIqHNBmElzICE/oxOX5Ekvs2ICQUOK/VkEkMH09JGJu+O/EA0NL31hxlmF+wrwrSLaQ==",
|
"integrity": "sha512-iZSrrBGvVlL+nt2B1NpqfDuBZ9jX61X9I2+XV0hlYXHtTwhwLTHDKGXjNXAgFBDLuvSYCB/rq2nPWVPRv2DrGA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@svgdotjs/svg.draggable.js": "^3.0.4",
|
"@svgdotjs/svg.draggable.js": "^3.0.4",
|
||||||
@@ -6200,10 +6200,10 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react-apexcharts": {
|
"node_modules/react-apexcharts": {
|
||||||
"version": "1.7.0",
|
"version": "1.8.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-apexcharts/-/react-apexcharts-1.7.0.tgz",
|
"resolved": "https://registry.npmmirror.com/react-apexcharts/-/react-apexcharts-1.8.0.tgz",
|
||||||
"integrity": "sha512-03oScKJyNLRf0Oe+ihJxFZliBQM9vW3UWwomVn4YVRTN1jsIR58dLWt0v1sb8RwJVHDMbeHiKQueM0KGpn7nOA==",
|
"integrity": "sha512-MuEp56gc0NMO2UUgY94fxQzoBE4XEjmcCha4xYY0vJdRrc1yfFFZE4QrCekOt2wcS3nibghzca/q/CbgkAgN5w==",
|
||||||
"license": "MIT",
|
"license": "SEE LICENSE IN LICENSE",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"prop-types": "^15.8.1"
|
"prop-types": "^15.8.1"
|
||||||
},
|
},
|
||||||
|
|||||||
+2
-2
@@ -12,7 +12,7 @@
|
|||||||
"@tanstack/react-query": "^5.85.6",
|
"@tanstack/react-query": "^5.85.6",
|
||||||
"@types/js-cookie": "^3.0.6",
|
"@types/js-cookie": "^3.0.6",
|
||||||
"@types/react-paginate": "^7.1.4",
|
"@types/react-paginate": "^7.1.4",
|
||||||
"apexcharts": "^4.5.0",
|
"apexcharts": "^4.7.0",
|
||||||
"axios": "^1.11.0",
|
"axios": "^1.11.0",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
"next-themes": "^0.4.4",
|
"next-themes": "^0.4.4",
|
||||||
"nextjs-toploader": "^3.7.15",
|
"nextjs-toploader": "^3.7.15",
|
||||||
"react": "19.0.0",
|
"react": "19.0.0",
|
||||||
"react-apexcharts": "^1.7.0",
|
"react-apexcharts": "^1.8.0",
|
||||||
"react-dom": "19.0.0",
|
"react-dom": "19.0.0",
|
||||||
"react-hook-form": "^7.62.0",
|
"react-hook-form": "^7.62.0",
|
||||||
"react-intersection-observer": "^9.16.0",
|
"react-intersection-observer": "^9.16.0",
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import Breadcrumb from "@/components/Breadcrumbs/Breadcrumb";
|
||||||
|
import ContactUsTable from "@/components/Tables/contact-us";
|
||||||
|
import { BreadcrumbItem } from "@/types/shared";
|
||||||
|
|
||||||
|
const ContactUsPage = () => {
|
||||||
|
const breadcrumbItems: BreadcrumbItem[] = [
|
||||||
|
{
|
||||||
|
href: "/",
|
||||||
|
name: "Dashboard",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: "/contact-us",
|
||||||
|
name: "Contact us",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Breadcrumb items={breadcrumbItems} />
|
||||||
|
<ContactUsTable />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ContactUsPage;
|
||||||
@@ -0,0 +1,134 @@
|
|||||||
|
"use client";
|
||||||
|
import InputGroup from "@/components/FormElements/InputGroup";
|
||||||
|
import { TextAreaGroup } from "@/components/FormElements/InputGroup/text-area";
|
||||||
|
import { ShowcaseSection } from "@/components/Layouts/showcase-section";
|
||||||
|
import { FormErrorMessages } from "@/constants/Texts";
|
||||||
|
import useHeroStepPresenter from "./useHeroStepPresenter";
|
||||||
|
|
||||||
|
const HeroStep = () => {
|
||||||
|
const { errors, handleSubmit, onSubmit, register } = useHeroStepPresenter();
|
||||||
|
return (
|
||||||
|
<form
|
||||||
|
className=" items-start gap-9 rounded-xl bg-gray-1 p-10 dark:bg-gray-7 sm:grid-cols-2"
|
||||||
|
onSubmit={handleSubmit(onSubmit)}
|
||||||
|
>
|
||||||
|
<div className="flex flex-col gap-9">
|
||||||
|
<ShowcaseSection
|
||||||
|
title="Hero Information"
|
||||||
|
className="flex flex-col gap-5"
|
||||||
|
>
|
||||||
|
<InputGroup
|
||||||
|
{...register("key", {
|
||||||
|
required: { message: FormErrorMessages.required, value: true },
|
||||||
|
minLength: {
|
||||||
|
value: 2,
|
||||||
|
message: FormErrorMessages.minLength(2),
|
||||||
|
},
|
||||||
|
maxLength: {
|
||||||
|
value: 200,
|
||||||
|
message: FormErrorMessages.maxLength(200),
|
||||||
|
},
|
||||||
|
})}
|
||||||
|
label="Key"
|
||||||
|
name="key"
|
||||||
|
required
|
||||||
|
type="text"
|
||||||
|
placeholder="Enter Key"
|
||||||
|
/>
|
||||||
|
{errors.key && (
|
||||||
|
<p className="mt-1 text-sm text-red-500">{errors.key.message}</p>
|
||||||
|
)}
|
||||||
|
<InputGroup
|
||||||
|
{...register("title", {
|
||||||
|
required: { message: FormErrorMessages.required, value: true },
|
||||||
|
minLength: {
|
||||||
|
value: 2,
|
||||||
|
message: FormErrorMessages.minLength(2),
|
||||||
|
},
|
||||||
|
maxLength: {
|
||||||
|
value: 200,
|
||||||
|
message: FormErrorMessages.maxLength(200),
|
||||||
|
},
|
||||||
|
})}
|
||||||
|
label="Title"
|
||||||
|
name="title"
|
||||||
|
required
|
||||||
|
type="text"
|
||||||
|
placeholder="Enter Title"
|
||||||
|
/>
|
||||||
|
{errors.title && (
|
||||||
|
<p className="mt-1 text-sm text-red-500">{errors.title.message}</p>
|
||||||
|
)}
|
||||||
|
<TextAreaGroup
|
||||||
|
label="Description"
|
||||||
|
{...register("description", {
|
||||||
|
minLength: {
|
||||||
|
value: 10,
|
||||||
|
message: FormErrorMessages.minLength(10),
|
||||||
|
},
|
||||||
|
maxLength: {
|
||||||
|
value: 1000,
|
||||||
|
message: FormErrorMessages.maxLength(1000),
|
||||||
|
},
|
||||||
|
})}
|
||||||
|
name="description"
|
||||||
|
placeholder="Enter Description"
|
||||||
|
/>
|
||||||
|
{errors.description && (
|
||||||
|
<p className="mt-1 text-sm text-red-500">
|
||||||
|
{errors.description.message}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
<InputGroup
|
||||||
|
{...register("button_text", {
|
||||||
|
required: { message: FormErrorMessages.required, value: true },
|
||||||
|
minLength: {
|
||||||
|
value: 2,
|
||||||
|
message: FormErrorMessages.minLength(2),
|
||||||
|
},
|
||||||
|
maxLength: {
|
||||||
|
value: 50,
|
||||||
|
message: FormErrorMessages.maxLength(50),
|
||||||
|
},
|
||||||
|
})}
|
||||||
|
label="Button Text"
|
||||||
|
name="button_text"
|
||||||
|
required
|
||||||
|
type="text"
|
||||||
|
placeholder="Enter Button Text"
|
||||||
|
/>
|
||||||
|
{errors.button_text && (
|
||||||
|
<p className="mt-1 text-sm text-red-500">
|
||||||
|
{errors.button_text.message}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
<InputGroup
|
||||||
|
{...register("button_link", {
|
||||||
|
required: { message: FormErrorMessages.required, value: true },
|
||||||
|
minLength: {
|
||||||
|
value: 2,
|
||||||
|
message: FormErrorMessages.minLength(2),
|
||||||
|
},
|
||||||
|
maxLength: {
|
||||||
|
value: 200,
|
||||||
|
message: FormErrorMessages.maxLength(200),
|
||||||
|
},
|
||||||
|
})}
|
||||||
|
label="Button Link"
|
||||||
|
name="button_link"
|
||||||
|
required
|
||||||
|
type="url"
|
||||||
|
placeholder="Enter Button Link"
|
||||||
|
/>
|
||||||
|
{errors.button_link && (
|
||||||
|
<p className="mt-1 text-sm text-red-500">
|
||||||
|
{errors.button_link.message}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</ShowcaseSection>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default HeroStep;
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
"use client";
|
||||||
|
import { useForm } from "react-hook-form";
|
||||||
|
|
||||||
|
export interface IHeroStepFields {
|
||||||
|
key: string;
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
button_text: string;
|
||||||
|
button_link: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const useHeroStepPresenter = () => {
|
||||||
|
const {
|
||||||
|
register,
|
||||||
|
handleSubmit,
|
||||||
|
formState: { errors },
|
||||||
|
} = useForm<IHeroStepFields>();
|
||||||
|
|
||||||
|
const onSubmit = (data: IHeroStepFields) => {
|
||||||
|
console.log(data);
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
register,
|
||||||
|
handleSubmit,
|
||||||
|
errors,
|
||||||
|
onSubmit,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useHeroStepPresenter;
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
"use client";
|
||||||
|
import Breadcrumb from "@/components/Breadcrumbs/Breadcrumb";
|
||||||
|
import Stepper, { Step } from "@/components/ui/Stepper";
|
||||||
|
import { BreadcrumbItem } from "@/types/shared";
|
||||||
|
import { useState } from "react";
|
||||||
|
import { ShowcaseSection } from "../../components/Layouts/showcase-section";
|
||||||
|
import HeroStep from "./_components/HeroStep";
|
||||||
|
|
||||||
|
const Marketing = () => {
|
||||||
|
const [step, setStep] = useState(0);
|
||||||
|
const breadcrumbItems: BreadcrumbItem[] = [
|
||||||
|
{
|
||||||
|
href: "/",
|
||||||
|
name: "Dashboard",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: "/marketing",
|
||||||
|
name: "Marketing",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const steps: Step[] = [
|
||||||
|
{
|
||||||
|
title: "Hero",
|
||||||
|
description: "Create the hero section",
|
||||||
|
content: <HeroStep />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Profile",
|
||||||
|
description: "Setup your profile",
|
||||||
|
content: <div>step 2</div>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Preferences",
|
||||||
|
description: "Choose preferences",
|
||||||
|
content: <div>step 3</div>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Review",
|
||||||
|
description: "Review and confirm",
|
||||||
|
content: <div>step 4</div>,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Breadcrumb items={breadcrumbItems} />
|
||||||
|
<ShowcaseSection title="Create Marketing page">
|
||||||
|
<Stepper
|
||||||
|
steps={steps}
|
||||||
|
currentStep={step}
|
||||||
|
onStepChange={setStep}
|
||||||
|
></Stepper>
|
||||||
|
</ShowcaseSection>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Marketing;
|
||||||
@@ -67,6 +67,7 @@ export function FilterIcon(props: IconProps) {
|
|||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function MoneyIcon(props: IconProps) {
|
export function MoneyIcon(props: IconProps) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
@@ -182,6 +183,67 @@ export function TrendingUpIcon(props: IconProps) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function GlobeSearch(props: IconProps) {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M22 12C22 6.48 17.52 2 12 2C6.48 2 2 6.48 2 12C2 17.52 6.48 22 12 22"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M7.99998 3H8.99998C7.04998 8.84 7.04998 15.16 8.99998 21H7.99998"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M15 3C15.97 5.92 16.46 8.96 16.46 12"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M3 16V15C5.92 15.97 8.96 16.46 12 16.46"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M3 9.0001C8.84 7.0501 15.16 7.0501 21 9.0001"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M18.2 21.4C19.9673 21.4 21.4 19.9673 21.4 18.2C21.4 16.4327 19.9673 15 18.2 15C16.4327 15 15 16.4327 15 18.2C15 19.9673 16.4327 21.4 18.2 21.4Z"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M22 22L21 21"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
export function CheckIcon(props: IconProps) {
|
export function CheckIcon(props: IconProps) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
|||||||
@@ -107,6 +107,7 @@ export function UserInfo() {
|
|||||||
onClick={() => {
|
onClick={() => {
|
||||||
setIsOpen(false);
|
setIsOpen(false);
|
||||||
logout();
|
logout();
|
||||||
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<LogOutIcon />
|
<LogOutIcon />
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { GlobeSearch } from "@/assets/icons";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import * as Icons from "../icons";
|
import * as Icons from "../icons";
|
||||||
|
|
||||||
@@ -72,6 +73,19 @@ export const NAV_DATA: NavData = [
|
|||||||
icon: Icons.NotificationIcon,
|
icon: Icons.NotificationIcon,
|
||||||
items: [],
|
items: [],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: "Contact Us",
|
||||||
|
url: "/contact-us",
|
||||||
|
icon: Icons.CallIncome,
|
||||||
|
items: [],
|
||||||
|
visible: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Marketing",
|
||||||
|
url: "/marketing",
|
||||||
|
icon: GlobeSearch,
|
||||||
|
items: [],
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -19,6 +19,32 @@ export function ChevronUp(props: PropsType) {
|
|||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
export function CallIncome(props: PropsType) {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M21.97 18.33C21.97 18.69 21.89 19.06 21.72 19.42C21.55 19.78 21.33 20.12 21.04 20.44C20.55 20.98 20.01 21.37 19.4 21.62C18.8 21.87 18.15 22 17.45 22C16.43 22 15.34 21.76 14.19 21.27C13.04 20.78 11.89 20.12 10.75 19.29C9.6 18.45 8.51 17.52 7.47 16.49C6.44 15.45 5.51 14.36 4.68 13.22C3.86 12.08 3.2 10.94 2.72 9.81C2.24 8.67 2 7.58 2 6.54C2 5.86 2.12 5.21 2.36 4.61C2.6 4 2.98 3.44 3.51 2.94C4.15 2.31 4.85 2 5.59 2C5.87 2 6.15 2.06 6.4 2.18C6.66 2.3 6.89 2.48 7.07 2.74L9.39 6.01C9.57 6.26 9.7 6.49 9.79 6.71C9.88 6.92 9.93 7.13 9.93 7.32C9.93 7.56 9.86 7.8 9.72 8.03C9.59 8.26 9.4 8.5 9.16 8.74L8.4 9.53C8.29 9.64 8.24 9.77 8.24 9.93C8.24 10.01 8.25 10.08 8.27 10.16C8.3 10.24 8.33 10.3 8.35 10.36C8.53 10.69 8.84 11.12 9.28 11.64C9.73 12.16 10.21 12.69 10.73 13.22C11.27 13.75 11.79 14.24 12.32 14.69C12.84 15.13 13.27 15.43 13.61 15.61C13.66 15.63 13.72 15.66 13.79 15.69C13.87 15.72 13.95 15.73 14.04 15.73C14.21 15.73 14.34 15.67 14.45 15.56L15.21 14.81C15.46 14.56 15.7 14.37 15.93 14.25C16.16 14.11 16.39 14.04 16.64 14.04C16.83 14.04 17.03 14.08 17.25 14.17C17.47 14.26 17.7 14.39 17.95 14.56L21.26 16.91C21.52 17.09 21.7 17.3 21.81 17.55C21.91 17.8 21.97 18.05 21.97 18.33Z"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeMiterlimit="10"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M16.2 7.8H21M16.2 7.8V3V7.8Z"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
export function NotificationIcon(props: PropsType) {
|
export function NotificationIcon(props: PropsType) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
|||||||
@@ -21,9 +21,7 @@ import Link from "next/link";
|
|||||||
import { Tooltip } from "react-tooltip";
|
import { Tooltip } from "react-tooltip";
|
||||||
import { useCompanyListPresenter } from "./useCompanyListPresenter";
|
import { useCompanyListPresenter } from "./useCompanyListPresenter";
|
||||||
|
|
||||||
interface IProps {}
|
const CompaniesTable = () => {
|
||||||
|
|
||||||
const CompaniesTable = ({}: IProps) => {
|
|
||||||
const {
|
const {
|
||||||
isPending,
|
isPending,
|
||||||
pathName,
|
pathName,
|
||||||
@@ -47,7 +45,7 @@ const CompaniesTable = ({}: IProps) => {
|
|||||||
{columns.map((columns) => (
|
{columns.map((columns) => (
|
||||||
<TableHead
|
<TableHead
|
||||||
colSpan={100}
|
colSpan={100}
|
||||||
className="text-start font-extrabold uppercase"
|
className="text-start uppercase"
|
||||||
key={columns}
|
key={columns}
|
||||||
>
|
>
|
||||||
{columns}
|
{columns}
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
"use client";
|
||||||
|
import {
|
||||||
|
Table,
|
||||||
|
TableBody,
|
||||||
|
TableHead,
|
||||||
|
TableHeader,
|
||||||
|
TableRow,
|
||||||
|
} from "@/components/ui/table";
|
||||||
|
import TableSkeleton from "@/components/ui/TableSkeleton";
|
||||||
|
import { useContactUsListPresenter } from "./useContactUsListPresenter";
|
||||||
|
|
||||||
|
const ContactUsTable = () => {
|
||||||
|
const { columns, pathname, router, isPending } = useContactUsListPresenter();
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col rounded-[10px] bg-white px-7.5 pb-4 pt-7.5 shadow-1 dark:bg-gray-dark dark:shadow-card">
|
||||||
|
<Table>
|
||||||
|
<TableHeader>
|
||||||
|
<TableRow>
|
||||||
|
{columns.map((columns) => (
|
||||||
|
<TableHead
|
||||||
|
colSpan={100}
|
||||||
|
className="text-start uppercase"
|
||||||
|
key={columns}
|
||||||
|
>
|
||||||
|
{columns}
|
||||||
|
</TableHead>
|
||||||
|
))}
|
||||||
|
</TableRow>
|
||||||
|
</TableHeader>
|
||||||
|
{isPending && <TableSkeleton column={columns.length} />}
|
||||||
|
<TableBody></TableBody>
|
||||||
|
</Table>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ContactUsTable;
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
"use client";
|
||||||
|
import { useReadAllContactUs } from "@/hooks/queries/useContactUsQuery";
|
||||||
|
import { usePathname, useRouter } from "next/navigation";
|
||||||
|
export const useContactUsListPresenter = () => {
|
||||||
|
const columns: string[] = [];
|
||||||
|
const pathname = usePathname();
|
||||||
|
const router = useRouter();
|
||||||
|
const { data, isPending } = useReadAllContactUs();
|
||||||
|
return {
|
||||||
|
columns,
|
||||||
|
pathname,
|
||||||
|
router,
|
||||||
|
isPending,
|
||||||
|
data,
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
import InputGroup from "@/components/FormElements/InputGroup";
|
||||||
|
import { Select } from "@/components/FormElements/select";
|
||||||
|
import {
|
||||||
|
FieldValues,
|
||||||
|
UseFormHandleSubmit,
|
||||||
|
UseFormRegister,
|
||||||
|
UseFormSetValue,
|
||||||
|
UseFormWatch,
|
||||||
|
} from "react-hook-form";
|
||||||
|
|
||||||
|
interface NotificationHistoryListFiltersProps {
|
||||||
|
filterRegister: UseFormRegister<FieldValues>;
|
||||||
|
setIsFilterModalOpen: (isOpen: boolean) => void;
|
||||||
|
isMutating: number;
|
||||||
|
handleFilterSubmit: UseFormHandleSubmit<FieldValues>;
|
||||||
|
search: (data: any) => void;
|
||||||
|
watch: UseFormWatch<FieldValues>;
|
||||||
|
setValue: UseFormSetValue<any>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const NotificationHistoryListFilters = ({
|
||||||
|
filterRegister,
|
||||||
|
setIsFilterModalOpen,
|
||||||
|
isMutating,
|
||||||
|
handleFilterSubmit,
|
||||||
|
search,
|
||||||
|
}: NotificationHistoryListFiltersProps) => {
|
||||||
|
return (
|
||||||
|
<form
|
||||||
|
className="grid !min-w-full grid-cols-1 gap-4 xl:grid-cols-2"
|
||||||
|
onSubmit={handleFilterSubmit(search)}
|
||||||
|
>
|
||||||
|
<Select
|
||||||
|
{...filterRegister("seen")}
|
||||||
|
items={[
|
||||||
|
{ label: "Seen", value: "true" },
|
||||||
|
{ label: "Not seen", value: "false" },
|
||||||
|
]}
|
||||||
|
label="Seen"
|
||||||
|
name="seen"
|
||||||
|
/>
|
||||||
|
<InputGroup
|
||||||
|
{...filterRegister("search")}
|
||||||
|
name="search"
|
||||||
|
label="search"
|
||||||
|
placeholder="search"
|
||||||
|
type="search"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className="flex gap-4">
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
className="mt-6 flex w-fit justify-center rounded-lg bg-primary p-[13px] font-medium capitalize text-white hover:bg-opacity-90"
|
||||||
|
>
|
||||||
|
{isMutating ? (
|
||||||
|
<div className="h-5 w-5 animate-spin rounded-full border-b-2 border-gray-1"></div>
|
||||||
|
) : (
|
||||||
|
<span>Search</span>
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="mt-6 flex w-fit justify-center rounded-lg bg-error p-[13px] font-medium capitalize text-white hover:bg-opacity-90"
|
||||||
|
onClick={() => setIsFilterModalOpen(false)}
|
||||||
|
>
|
||||||
|
Close
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default NotificationHistoryListFilters;
|
||||||
@@ -14,10 +14,12 @@ import {
|
|||||||
import TableSkeleton from "@/components/ui/TableSkeleton";
|
import TableSkeleton from "@/components/ui/TableSkeleton";
|
||||||
import { _TooltipDefaultParams } from "@/constants/tooltip";
|
import { _TooltipDefaultParams } from "@/constants/tooltip";
|
||||||
import { truncateString, unixToDate } from "@/utils/shared";
|
import { truncateString, unixToDate } from "@/utils/shared";
|
||||||
import Link from "next/link";
|
|
||||||
import { Tooltip } from "react-tooltip";
|
import { Tooltip } from "react-tooltip";
|
||||||
|
|
||||||
import Boolean from "@/components/ui/Boolean";
|
import Boolean from "@/components/ui/Boolean";
|
||||||
|
import ListHeader from "@/components/ui/ListHeader";
|
||||||
|
import Modal from "@/components/ui/modal";
|
||||||
|
import NotificationHistoryListFilters from "./NotificationHistoryListFilters";
|
||||||
import useNotificationHistoryTablePresenter from "./useNotificationHistoryTablePresenter";
|
import useNotificationHistoryTablePresenter from "./useNotificationHistoryTablePresenter";
|
||||||
|
|
||||||
const NotificationHistoryTable = () => {
|
const NotificationHistoryTable = () => {
|
||||||
@@ -28,14 +30,24 @@ const NotificationHistoryTable = () => {
|
|||||||
notificationHistory,
|
notificationHistory,
|
||||||
pathName,
|
pathName,
|
||||||
router,
|
router,
|
||||||
|
isFilterModalOpen,
|
||||||
|
setIsFilterModalOpen,
|
||||||
setParams,
|
setParams,
|
||||||
|
handleSubmit,
|
||||||
|
register,
|
||||||
|
setValue,
|
||||||
|
watch,
|
||||||
|
search,
|
||||||
|
isMutating,
|
||||||
} = useNotificationHistoryTablePresenter();
|
} = useNotificationHistoryTablePresenter();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
<ShowcaseSection className="flex flex-col">
|
<ShowcaseSection className="flex flex-col">
|
||||||
<button className="mb-5 flex w-fit justify-center self-end rounded-lg bg-primary p-[13px] font-medium text-white hover:bg-opacity-90">
|
<ListHeader
|
||||||
<Link href={`${pathName}/create`}>Create Notification </Link>
|
createButtonText="Create notification"
|
||||||
</button>
|
onFilter={() => setIsFilterModalOpen(true)}
|
||||||
|
/>
|
||||||
<Table>
|
<Table>
|
||||||
<TableHeader>
|
<TableHeader>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
@@ -66,7 +78,9 @@ const NotificationHistoryTable = () => {
|
|||||||
unix: item.created_at,
|
unix: item.created_at,
|
||||||
})}
|
})}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell colSpan={100}>{item.recipient?.full_name}</TableCell>
|
<TableCell colSpan={100}>
|
||||||
|
{item.recipient?.full_name}
|
||||||
|
</TableCell>
|
||||||
<TableCell colSpan={100} className="truncate">
|
<TableCell colSpan={100} className="truncate">
|
||||||
{truncateString(item.message, 20, 20)}
|
{truncateString(item.message, 20, 20)}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
@@ -116,6 +130,22 @@ const NotificationHistoryTable = () => {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</ShowcaseSection>
|
</ShowcaseSection>
|
||||||
|
<Modal
|
||||||
|
isOpen={isFilterModalOpen}
|
||||||
|
onClose={() => setIsFilterModalOpen(false)}
|
||||||
|
showButtons={false}
|
||||||
|
>
|
||||||
|
<NotificationHistoryListFilters
|
||||||
|
filterRegister={register}
|
||||||
|
handleFilterSubmit={handleSubmit}
|
||||||
|
isMutating={isMutating}
|
||||||
|
watch={watch}
|
||||||
|
setValue={setValue}
|
||||||
|
setIsFilterModalOpen={setIsFilterModalOpen}
|
||||||
|
search={search}
|
||||||
|
/>
|
||||||
|
</Modal>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,20 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import { apiDefaultParams } from "@/constants/Api_Params";
|
import { apiDefaultParams } from "@/constants/Api_Params";
|
||||||
import { useGetNotificationHistoryQuery } from "@/hooks/queries/useNotificationQueries";
|
import { useGetNotificationHistoryQuery } from "@/hooks/queries/useNotificationQueries";
|
||||||
|
import { deleteEmptyKeys } from "@/utils/shared";
|
||||||
|
import { useIsMutating } from "@tanstack/react-query";
|
||||||
import { usePathname, useRouter } from "next/navigation";
|
import { usePathname, useRouter } from "next/navigation";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
import { useForm } from "react-hook-form";
|
||||||
|
|
||||||
const useNotificationHistoryTablePresenter = () => {
|
const useNotificationHistoryTablePresenter = () => {
|
||||||
|
const { register, handleSubmit, watch, setValue } = useForm();
|
||||||
|
const [isFilterModalOpen, setIsFilterModalOpen] = useState(false);
|
||||||
const [params, setParams] = useState<Record<string, any>>({
|
const [params, setParams] = useState<Record<string, any>>({
|
||||||
...apiDefaultParams,
|
...apiDefaultParams,
|
||||||
});
|
});
|
||||||
const { data, isLoading } = useGetNotificationHistoryQuery(params);
|
const { data, isLoading } = useGetNotificationHistoryQuery(params);
|
||||||
|
const isMutating = useIsMutating();
|
||||||
const columns: string[] = [
|
const columns: string[] = [
|
||||||
"row",
|
"row",
|
||||||
"title",
|
"title",
|
||||||
@@ -25,6 +30,14 @@ const useNotificationHistoryTablePresenter = () => {
|
|||||||
];
|
];
|
||||||
const pathName = usePathname();
|
const pathName = usePathname();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const search = (data: any) => {
|
||||||
|
const newParams = { ...params, ...data, offset: 0 };
|
||||||
|
const cleanedParams = deleteEmptyKeys(newParams);
|
||||||
|
setParams(cleanedParams);
|
||||||
|
const queryString = new URLSearchParams(cleanedParams).toString();
|
||||||
|
router.push(`${pathName}?${queryString}`);
|
||||||
|
setIsFilterModalOpen(false);
|
||||||
|
};
|
||||||
return {
|
return {
|
||||||
notificationHistory: data?.data,
|
notificationHistory: data?.data,
|
||||||
metadata: data?.meta,
|
metadata: data?.meta,
|
||||||
@@ -33,6 +46,14 @@ const useNotificationHistoryTablePresenter = () => {
|
|||||||
router,
|
router,
|
||||||
pathName,
|
pathName,
|
||||||
setParams,
|
setParams,
|
||||||
|
register,
|
||||||
|
handleSubmit,
|
||||||
|
watch,
|
||||||
|
setValue,
|
||||||
|
isFilterModalOpen,
|
||||||
|
setIsFilterModalOpen,
|
||||||
|
isMutating,
|
||||||
|
search,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,135 @@
|
|||||||
|
import { CheckIcon } from "@/assets/icons";
|
||||||
|
import React, { ReactNode, useState } from "react";
|
||||||
|
export interface Step {
|
||||||
|
title: string;
|
||||||
|
description?: string;
|
||||||
|
content: ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface StepperProps {
|
||||||
|
steps: Step[];
|
||||||
|
currentStep?: number;
|
||||||
|
onStepChange?: (step: number) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Stepper: React.FC<StepperProps> = ({
|
||||||
|
steps,
|
||||||
|
currentStep: controlledStep,
|
||||||
|
onStepChange,
|
||||||
|
}) => {
|
||||||
|
const [internalStep, setInternalStep] = useState(0);
|
||||||
|
const currentStep =
|
||||||
|
controlledStep !== undefined ? controlledStep : internalStep;
|
||||||
|
|
||||||
|
const handleStepClick = (index: number) => {
|
||||||
|
if (index <= currentStep) {
|
||||||
|
if (onStepChange) {
|
||||||
|
onStepChange(index);
|
||||||
|
} else {
|
||||||
|
setInternalStep(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const nextStep = () => {
|
||||||
|
if (currentStep < steps.length - 1) {
|
||||||
|
const newStep = currentStep + 1;
|
||||||
|
if (onStepChange) {
|
||||||
|
onStepChange(newStep);
|
||||||
|
} else {
|
||||||
|
setInternalStep(newStep);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const prevStep = () => {
|
||||||
|
if (currentStep > 0) {
|
||||||
|
const newStep = currentStep - 1;
|
||||||
|
if (onStepChange) {
|
||||||
|
onStepChange(newStep);
|
||||||
|
} else {
|
||||||
|
setInternalStep(newStep);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="mx-auto w-full max-w-4xl p-8">
|
||||||
|
<div className="mb-8 flex items-center justify-between">
|
||||||
|
{steps.map((step, index) => (
|
||||||
|
<React.Fragment key={index}>
|
||||||
|
<div className="flex flex-1 flex-col items-center">
|
||||||
|
<button
|
||||||
|
onClick={() => handleStepClick(index)}
|
||||||
|
disabled={index > currentStep}
|
||||||
|
className={`flex h-12 w-12 items-center justify-center rounded-full font-semibold transition-all duration-200 ${
|
||||||
|
index < currentStep
|
||||||
|
? "cursor-pointer bg-primary text-white hover:bg-primary/90"
|
||||||
|
: index === currentStep
|
||||||
|
? "bg-primary text-white ring-4 ring-primary/20"
|
||||||
|
: "cursor-not-allowed bg-gray-2 text-gray-5"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{index < currentStep ? (
|
||||||
|
<CheckIcon className="h-6 w-6" />
|
||||||
|
) : (
|
||||||
|
<span>{index + 1}</span>
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
<div className="mt-2 text-center">
|
||||||
|
<div
|
||||||
|
className={`text-sm font-medium ${
|
||||||
|
index <= currentStep ? "text-dark-2" : "text-gray-4"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{step.title}
|
||||||
|
</div>
|
||||||
|
{step.description && (
|
||||||
|
<div className="mt-1 text-xs text-gray-5">
|
||||||
|
{step.description}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{index < steps.length - 1 && (
|
||||||
|
<div
|
||||||
|
className={`mx-4 -mt-12 h-1 flex-1 transition-all duration-300 ${
|
||||||
|
index < currentStep ? "bg-primary" : "bg-stroke"
|
||||||
|
}`}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</React.Fragment>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>{steps[currentStep].content}</div>
|
||||||
|
|
||||||
|
<div className="mt-6 flex justify-between">
|
||||||
|
<button
|
||||||
|
onClick={prevStep}
|
||||||
|
disabled={currentStep === 0}
|
||||||
|
className={`rounded-lg px-6 py-2 font-medium transition-all ${
|
||||||
|
currentStep === 0
|
||||||
|
? "cursor-not-allowed bg-gray-1 text-gray-4"
|
||||||
|
: "bg-gray-2 text-gray-7 hover:bg-gray-3"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
Previous
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={nextStep}
|
||||||
|
disabled={currentStep === steps.length - 1}
|
||||||
|
className={`rounded-lg px-6 py-2 font-medium transition-all ${
|
||||||
|
currentStep === steps.length - 1
|
||||||
|
? "cursor-not-allowed bg-gray-1 text-gray-4"
|
||||||
|
: "bg-primary text-white hover:bg-primary/90"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{currentStep === steps.length - 1 ? "Completed" : "Next"}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Stepper;
|
||||||
@@ -14,12 +14,9 @@ import React, {
|
|||||||
} from "react";
|
} from "react";
|
||||||
|
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import { z } from "zod";
|
|
||||||
import { ILoginResponse, IUser, UserSchema } from "../lib/api/types";
|
import { ILoginResponse, IUser, UserSchema } from "../lib/api/types";
|
||||||
import { COOKIE_KEYS } from "../lib/shared/cookies";
|
import { COOKIE_KEYS } from "../lib/shared/cookies";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
interface UserContextType {
|
interface UserContextType {
|
||||||
user: IUser | null;
|
user: IUser | null;
|
||||||
accessToken: string | null;
|
accessToken: string | null;
|
||||||
@@ -27,7 +24,7 @@ interface UserContextType {
|
|||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
setAuthState: (data: ILoginResponse) => void;
|
setAuthState: (data: ILoginResponse) => void;
|
||||||
logout: () => void;
|
logout: () => void;
|
||||||
setUser:Dispatch<SetStateAction<IUser|null>>
|
setUser: Dispatch<SetStateAction<IUser | null>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const UserContext = createContext<UserContextType | undefined>(undefined);
|
const UserContext = createContext<UserContextType | undefined>(undefined);
|
||||||
@@ -48,7 +45,7 @@ export const UserProvider: React.FC<UserProviderProps> = ({ children }) => {
|
|||||||
Cookies.remove(COOKIE_KEYS.access_token);
|
Cookies.remove(COOKIE_KEYS.access_token);
|
||||||
Cookies.remove(COOKIE_KEYS.refresh_token);
|
Cookies.remove(COOKIE_KEYS.refresh_token);
|
||||||
Cookies.remove(COOKIE_KEYS.user);
|
Cookies.remove(COOKIE_KEYS.user);
|
||||||
router.refresh();
|
window.location.href = "/auth/sign-in";
|
||||||
}, [router]);
|
}, [router]);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import { API_ENDPOINTS } from "@/lib/api";
|
||||||
|
import { contactUsService } from "@/lib/api/services/contact-us-service";
|
||||||
|
import { useQuery } from "@tanstack/react-query";
|
||||||
|
import { useMemo } from "react";
|
||||||
|
|
||||||
|
export const useReadAllContactUs = () => {
|
||||||
|
const queryKey = useMemo(() => [API_ENDPOINTS.CONTACT_US.READ_ALL], []);
|
||||||
|
|
||||||
|
return useQuery({
|
||||||
|
queryKey,
|
||||||
|
queryFn: () => contactUsService.getAllContactUs(),
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -58,4 +58,7 @@ export const API_ENDPOINTS = {
|
|||||||
MY_NOTIFICATIONS: "notifications/my",
|
MY_NOTIFICATIONS: "notifications/my",
|
||||||
MARK_AS_SEEN: (id: string) => `notifications/mark-seen/${id}`,
|
MARK_AS_SEEN: (id: string) => `notifications/mark-seen/${id}`,
|
||||||
},
|
},
|
||||||
|
CONTACT_US: {
|
||||||
|
READ_ALL: "contacts",
|
||||||
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import api from "../axios";
|
||||||
|
import { API_ENDPOINTS } from "../endpoints";
|
||||||
|
|
||||||
|
export const contactUsService = {
|
||||||
|
getAllContactUs: async () => {
|
||||||
|
try {
|
||||||
|
return (await api.get(API_ENDPOINTS.CONTACT_US.READ_ALL)).data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(
|
||||||
|
"ERROR caught in contact-us service => getAllContactUs",
|
||||||
|
error,
|
||||||
|
);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user