From 2d664795ad916ec428306e0cb3c1fea7b5218cc8 Mon Sep 17 00:00:00 2001 From: AmirReza Jamali Date: Tue, 4 Nov 2025 14:12:20 +0330 Subject: [PATCH] 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. --- package-lock.json | 18 +- package.json | 4 +- src/app/contact-us/page.tsx | 24 +++ src/app/marketing/_components/HeroStep.tsx | 134 ++++++++++++ .../_components/useHeroStepPresenter.ts | 31 +++ src/app/marketing/page.tsx | 57 ++++++ src/assets/icons.tsx | 62 ++++++ .../Layouts/header/user-info/index.tsx | 1 + src/components/Layouts/sidebar/data/index.ts | 14 ++ src/components/Layouts/sidebar/icons.tsx | 26 +++ src/components/Tables/companies/index.tsx | 6 +- src/components/Tables/contact-us/index.tsx | 37 ++++ .../contact-us/useContactUsListPresenter.ts | 16 ++ .../NotificationHistoryListFilters.tsx | 73 +++++++ .../Tables/notification-history/index.tsx | 192 ++++++++++-------- .../useNotificationHistoryTablePresenter.ts | 23 ++- src/components/ui/Stepper.tsx | 135 ++++++++++++ src/contexts/User.ctx.tsx | 7 +- src/hooks/queries/useContactUsQuery.ts | 13 ++ src/lib/api/endpoints.ts | 3 + src/lib/api/services/contact-us-service.ts | 16 ++ src/utils/timeframe-extractor.ts | 2 +- 22 files changed, 791 insertions(+), 103 deletions(-) create mode 100644 src/app/contact-us/page.tsx create mode 100644 src/app/marketing/_components/HeroStep.tsx create mode 100644 src/app/marketing/_components/useHeroStepPresenter.ts create mode 100644 src/app/marketing/page.tsx create mode 100644 src/components/Tables/contact-us/index.tsx create mode 100644 src/components/Tables/contact-us/useContactUsListPresenter.ts create mode 100644 src/components/Tables/notification-history/NotificationHistoryListFilters.tsx create mode 100644 src/components/ui/Stepper.tsx create mode 100644 src/hooks/queries/useContactUsQuery.ts create mode 100644 src/lib/api/services/contact-us-service.ts diff --git a/package-lock.json b/package-lock.json index 9b2745b..9021093 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,7 @@ "@tanstack/react-query": "^5.85.6", "@types/js-cookie": "^3.0.6", "@types/react-paginate": "^7.1.4", - "apexcharts": "^4.5.0", + "apexcharts": "^4.7.0", "axios": "^1.11.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", @@ -26,7 +26,7 @@ "next-themes": "^0.4.4", "nextjs-toploader": "^3.7.15", "react": "19.0.0", - "react-apexcharts": "^1.7.0", + "react-apexcharts": "^1.8.0", "react-dom": "19.0.0", "react-hook-form": "^7.62.0", "react-intersection-observer": "^9.16.0", @@ -2353,9 +2353,9 @@ } }, "node_modules/apexcharts": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-4.5.0.tgz", - "integrity": "sha512-E7ZkrVqPNBUWy/Rmg8DEIqHNBmElzICE/oxOX5Ekvs2ICQUOK/VkEkMH09JGJu+O/EA0NL31hxlmF+wrwrSLaQ==", + "version": "4.7.0", + "resolved": "https://registry.npmmirror.com/apexcharts/-/apexcharts-4.7.0.tgz", + "integrity": "sha512-iZSrrBGvVlL+nt2B1NpqfDuBZ9jX61X9I2+XV0hlYXHtTwhwLTHDKGXjNXAgFBDLuvSYCB/rq2nPWVPRv2DrGA==", "license": "MIT", "dependencies": { "@svgdotjs/svg.draggable.js": "^3.0.4", @@ -6200,10 +6200,10 @@ } }, "node_modules/react-apexcharts": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/react-apexcharts/-/react-apexcharts-1.7.0.tgz", - "integrity": "sha512-03oScKJyNLRf0Oe+ihJxFZliBQM9vW3UWwomVn4YVRTN1jsIR58dLWt0v1sb8RwJVHDMbeHiKQueM0KGpn7nOA==", - "license": "MIT", + "version": "1.8.0", + "resolved": "https://registry.npmmirror.com/react-apexcharts/-/react-apexcharts-1.8.0.tgz", + "integrity": "sha512-MuEp56gc0NMO2UUgY94fxQzoBE4XEjmcCha4xYY0vJdRrc1yfFFZE4QrCekOt2wcS3nibghzca/q/CbgkAgN5w==", + "license": "SEE LICENSE IN LICENSE", "dependencies": { "prop-types": "^15.8.1" }, diff --git a/package.json b/package.json index cf6d5b2..d456ffe 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "@tanstack/react-query": "^5.85.6", "@types/js-cookie": "^3.0.6", "@types/react-paginate": "^7.1.4", - "apexcharts": "^4.5.0", + "apexcharts": "^4.7.0", "axios": "^1.11.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", @@ -27,7 +27,7 @@ "next-themes": "^0.4.4", "nextjs-toploader": "^3.7.15", "react": "19.0.0", - "react-apexcharts": "^1.7.0", + "react-apexcharts": "^1.8.0", "react-dom": "19.0.0", "react-hook-form": "^7.62.0", "react-intersection-observer": "^9.16.0", diff --git a/src/app/contact-us/page.tsx b/src/app/contact-us/page.tsx new file mode 100644 index 0000000..9276e61 --- /dev/null +++ b/src/app/contact-us/page.tsx @@ -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 ( + <> + + + + ); +}; + +export default ContactUsPage; diff --git a/src/app/marketing/_components/HeroStep.tsx b/src/app/marketing/_components/HeroStep.tsx new file mode 100644 index 0000000..6425656 --- /dev/null +++ b/src/app/marketing/_components/HeroStep.tsx @@ -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 ( +
+
+ + + {errors.key && ( +

{errors.key.message}

+ )} + + {errors.title && ( +

{errors.title.message}

+ )} + + {errors.description && ( +

+ {errors.description.message} +

+ )} + + {errors.button_text && ( +

+ {errors.button_text.message} +

+ )} + + {errors.button_link && ( +

+ {errors.button_link.message} +

+ )} +
+
+
+ ); +}; + +export default HeroStep; diff --git a/src/app/marketing/_components/useHeroStepPresenter.ts b/src/app/marketing/_components/useHeroStepPresenter.ts new file mode 100644 index 0000000..4bd3934 --- /dev/null +++ b/src/app/marketing/_components/useHeroStepPresenter.ts @@ -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(); + + const onSubmit = (data: IHeroStepFields) => { + console.log(data); + }; + + return { + register, + handleSubmit, + errors, + onSubmit, + }; +}; + +export default useHeroStepPresenter; diff --git a/src/app/marketing/page.tsx b/src/app/marketing/page.tsx new file mode 100644 index 0000000..ff2d2a2 --- /dev/null +++ b/src/app/marketing/page.tsx @@ -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: , + }, + { + title: "Profile", + description: "Setup your profile", + content:
step 2
, + }, + { + title: "Preferences", + description: "Choose preferences", + content:
step 3
, + }, + { + title: "Review", + description: "Review and confirm", + content:
step 4
, + }, + ]; + return ( + <> + + + + + + ); +}; + +export default Marketing; diff --git a/src/assets/icons.tsx b/src/assets/icons.tsx index 4ac130f..719ef43 100644 --- a/src/assets/icons.tsx +++ b/src/assets/icons.tsx @@ -67,6 +67,7 @@ export function FilterIcon(props: IconProps) { ); } + export function MoneyIcon(props: IconProps) { return ( + + + + + + + + + ); +} export function CheckIcon(props: IconProps) { return ( { setIsOpen(false); logout(); + }} > diff --git a/src/components/Layouts/sidebar/data/index.ts b/src/components/Layouts/sidebar/data/index.ts index 8366822..5e0e837 100644 --- a/src/components/Layouts/sidebar/data/index.ts +++ b/src/components/Layouts/sidebar/data/index.ts @@ -1,3 +1,4 @@ +import { GlobeSearch } from "@/assets/icons"; import { z } from "zod"; import * as Icons from "../icons"; @@ -72,6 +73,19 @@ export const NAV_DATA: NavData = [ icon: Icons.NotificationIcon, items: [], }, + { + title: "Contact Us", + url: "/contact-us", + icon: Icons.CallIncome, + items: [], + visible: false, + }, + { + title: "Marketing", + url: "/marketing", + icon: GlobeSearch, + items: [], + }, ], }, ]; diff --git a/src/components/Layouts/sidebar/icons.tsx b/src/components/Layouts/sidebar/icons.tsx index d0a177e..8ba9248 100644 --- a/src/components/Layouts/sidebar/icons.tsx +++ b/src/components/Layouts/sidebar/icons.tsx @@ -19,6 +19,32 @@ export function ChevronUp(props: PropsType) { ); } +export function CallIncome(props: PropsType) { + return ( + + + + + ); +} export function NotificationIcon(props: PropsType) { return ( { +const CompaniesTable = () => { const { isPending, pathName, @@ -47,7 +45,7 @@ const CompaniesTable = ({}: IProps) => { {columns.map((columns) => ( {columns} diff --git a/src/components/Tables/contact-us/index.tsx b/src/components/Tables/contact-us/index.tsx new file mode 100644 index 0000000..1e6cd14 --- /dev/null +++ b/src/components/Tables/contact-us/index.tsx @@ -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 ( +
+ + + + {columns.map((columns) => ( + + {columns} + + ))} + + + {isPending && } + +
+
+ ); +}; + +export default ContactUsTable; diff --git a/src/components/Tables/contact-us/useContactUsListPresenter.ts b/src/components/Tables/contact-us/useContactUsListPresenter.ts new file mode 100644 index 0000000..5185618 --- /dev/null +++ b/src/components/Tables/contact-us/useContactUsListPresenter.ts @@ -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, + }; +}; diff --git a/src/components/Tables/notification-history/NotificationHistoryListFilters.tsx b/src/components/Tables/notification-history/NotificationHistoryListFilters.tsx new file mode 100644 index 0000000..ac3f397 --- /dev/null +++ b/src/components/Tables/notification-history/NotificationHistoryListFilters.tsx @@ -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; + setIsFilterModalOpen: (isOpen: boolean) => void; + isMutating: number; + handleFilterSubmit: UseFormHandleSubmit; + search: (data: any) => void; + watch: UseFormWatch; + setValue: UseFormSetValue; +} + +const NotificationHistoryListFilters = ({ + filterRegister, + setIsFilterModalOpen, + isMutating, + handleFilterSubmit, + search, +}: NotificationHistoryListFiltersProps) => { + return ( +
+