feat(cms): Add CMS details page and update related components

- Implement new edit marketing page with dynamic routing
- Update CMS and contact us table components with placeholder content
- Add new query hook for fetching CMS details by ID
- Modify CMS service to support fetching individual CMS details
- Update type definitions for CMS-related data structures
- Add initial breadcrumb navigation for edit marketing page
- Prepare infrastructure for future CMS editing functionality
This commit is contained in:
AmirReza Jamali
2025-11-10 09:53:22 +03:30
parent ed18d86960
commit 5640febf40
7 changed files with 210 additions and 155 deletions
+33
View File
@@ -0,0 +1,33 @@
import Loading from "@/app/loading";
import Breadcrumb from "@/components/Breadcrumbs/Breadcrumb";
import { useGetCmsDetails } from "@/hooks/queries/useCmsQueries";
import { BreadcrumbItem } from "@/types/shared";
import { use } from "react";
interface IProps {
params: Promise<{ id: string }>;
}
const EditMarketingPage = async ({ params }: IProps) => {
const { id } = use(params);
const { data, isPending } = useGetCmsDetails(id);
const breadcrumbItems: BreadcrumbItem[] = [
{
href: "/",
name: "Dashboard",
},
{
href: "/marketing",
name: "Marketing",
},
{
href: `/marketing/${id}`,
name: "Edit Marketing",
},
];
if (isPending) return <Loading />;
return (
<>
<Breadcrumb items={breadcrumbItems} />
</>
);
};
+1 -1
View File
@@ -28,7 +28,7 @@ const CmsTable = () => {
columns={columns} columns={columns}
emptyMessage="No Cms were found" emptyMessage="No Cms were found"
> >
Hollo Cms list will be added
</EmptyListWrapper> </EmptyListWrapper>
</TableBody> </TableBody>
</Table> </Table>
+11 -9
View File
@@ -1,16 +1,12 @@
"use client"; "use client";
import { import EmptyListWrapper from "@/components/HOC/EmptyListWrapper";
Table, import { Table, TableHead, TableHeader, TableRow } from "@/components/ui/table";
TableBody,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import TableSkeleton from "@/components/ui/TableSkeleton"; import TableSkeleton from "@/components/ui/TableSkeleton";
import { useContactUsListPresenter } from "./useContactUsListPresenter"; import { useContactUsListPresenter } from "./useContactUsListPresenter";
const ContactUsTable = () => { const ContactUsTable = () => {
const { columns, pathname, router, isPending } = useContactUsListPresenter(); const { columns, pathname, router, isPending, data } =
useContactUsListPresenter();
return ( 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"> <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> <Table>
@@ -28,7 +24,13 @@ const ContactUsTable = () => {
</TableRow> </TableRow>
</TableHeader> </TableHeader>
{isPending && <TableSkeleton column={columns.length} />} {isPending && <TableSkeleton column={columns.length} />}
<TableBody></TableBody> <EmptyListWrapper
list={data ?? []}
columns={columns}
emptyMessage="No contact us is submitted"
>
Contact us table will be initialized here
</EmptyListWrapper>
</Table> </Table>
</div> </div>
); );
@@ -2,7 +2,7 @@
import { useReadAllContactUs } from "@/hooks/queries/useContactUsQuery"; import { useReadAllContactUs } from "@/hooks/queries/useContactUsQuery";
import { usePathname, useRouter } from "next/navigation"; import { usePathname, useRouter } from "next/navigation";
export const useContactUsListPresenter = () => { export const useContactUsListPresenter = () => {
const columns: string[] = []; const columns: string[] = ["row", "actions"];
const pathname = usePathname(); const pathname = usePathname();
const router = useRouter(); const router = useRouter();
const { data, isPending } = useReadAllContactUs(); const { data, isPending } = useReadAllContactUs();
+7
View File
@@ -10,6 +10,13 @@ export const useGetCmsList = () => {
queryFn: cmsService.getCms, queryFn: cmsService.getCms,
}); });
}; };
export const useGetCmsDetails = (id: string) => {
const queryKey = useMemo(() => [API_ENDPOINTS.CMS.BASE(id)], [id]);
return useQuery({
queryKey,
queryFn: () => cmsService.getCmsDetails(id),
});
};
export const useCreateCms = () => { export const useCreateCms = () => {
const mutationKey = useMemo( const mutationKey = useMemo(
() => [API_ENDPOINTS.CMS.BASE(), "Create cms"], () => [API_ENDPOINTS.CMS.BASE(), "Create cms"],
+10 -2
View File
@@ -1,10 +1,10 @@
import api from "../axios"; import api from "../axios";
import { API_ENDPOINTS } from "../endpoints"; import { API_ENDPOINTS } from "../endpoints";
import { ApiResponse } from "../types"; import { ApiResponse } from "../types";
import { TCmsResponse } from "../types/TCms"; import { TCmsListResponse } from "../types/TCms";
export const cmsService = { export const cmsService = {
getCms: async (): Promise<ApiResponse<TCmsResponse>> => { getCms: async (): Promise<ApiResponse<TCmsListResponse>> => {
try { try {
return (await api.get(API_ENDPOINTS.CMS.BASE())).data; return (await api.get(API_ENDPOINTS.CMS.BASE())).data;
} catch (error) { } catch (error) {
@@ -20,4 +20,12 @@ export const cmsService = {
throw error; throw error;
} }
}, },
getCmsDetails: async (id: string) => {
try {
return (await api.get(API_ENDPOINTS.CMS.BASE(id))).data;
} catch (error) {
console.error("Error Caught In CMS Service => getCmsDetails: ", error);
throw error;
}
},
}; };
+147 -142
View File
@@ -1,155 +1,160 @@
import { z } from "zod"; import { z } from "zod";
export const CmsCredentials = z.object({ advantages: z.object({ export const CmsCredentials = z.object({
cards: z.array( advantages: z.object({
z.object({ cards: z.array(
description: z.string(), z.object({
icon: z.string(),
title: z.string(),
}),
),
description: z.string(), description: z.string(),
icon: z.string(),
title: z.string(), title: z.string(),
}), }),
challenges: z.object({ ),
cards: z.array( description: z.string(),
z.object({ title: z.string(),
description: z.string(), }),
icon: z.string(), challenges: z.object({
title: z.string(), cards: z.array(
}), z.object({
),
description: z.string(), description: z.string(),
icon: z.string(),
title: z.string(), title: z.string(),
}), }),
chart: z.object({ ),
data: z.array( description: z.string(),
z.object({ title: z.string(),
key: z.string(), }),
value: z.number(), chart: z.object({
}), data: z.array(
), z.object({
missedAmount: z.string(), key: z.string(),
title: z.string(), value: z.number(),
}), }),
contact: z.object({ ),
missedAmount: z.string(),
title: z.string(),
}),
contact: z.object({
description: z.string(),
fields: z.array(
z.object({
label: z.string(),
name: z.string(),
placeholder: z.string(),
required: z.boolean(),
}),
),
submitButtonText: z.string(),
title: z.string(),
}),
features: z.object({
cards: z.array(
z.object({
description: z.string(), description: z.string(),
fields: z.array( icon: z.string(),
z.object({
label: z.string(),
name: z.string(),
placeholder: z.string(),
required: z.boolean(),
}),
),
submitButtonText: z.string(),
title: z.string(), title: z.string(),
}), }),
features: z.object({ ),
cards: z.array( description: z.string(),
z.object({ title: z.string(),
description: z.string(), }),
icon: z.string(), footer: z.object({
title: z.string(), copyright: z.string(),
}), email: z.string(),
), location: z.string(),
description: z.string(), phone: z.string(),
title: z.string(), tagline: z.string(),
}), }),
footer: z.object({ hero: z.object({
copyright: z.string(), buttonLink: z.string(),
email: z.string(), buttonText: z.string(),
location: z.string(), description: z.string(),
phone: z.string(), gifFile: z.string(),
tagline: z.string(), title: z.string(),
}), }),
hero: z.object({
buttonLink: z.string(),
buttonText: z.string(),
description: z.string(),
gifFile: z.string(),
title: z.string(),
})})
const CmsSchema = z.object({
cms: z.array(
z.object({
advantages: z.object({
cards: z.array(
z.object({
description: z.string(),
icon: z.string(),
title: z.string(),
}),
),
description: z.string(),
title: z.string(),
}),
challenges: z.object({
cards: z.array(
z.object({
description: z.string(),
icon: z.string(),
title: z.string(),
}),
),
description: z.string(),
title: z.string(),
}),
chart: z.object({
data: z.array(
z.object({
key: z.string(),
value: z.number(),
}),
),
missedAmount: z.string(),
title: z.string(),
}),
contact: z.object({
description: z.string(),
fields: z.array(
z.object({
label: z.string(),
name: z.string(),
placeholder: z.string(),
required: z.boolean(),
}),
),
submitButtonText: z.string(),
title: z.string(),
}),
features: z.object({
cards: z.array(
z.object({
description: z.string(),
icon: z.string(),
title: z.string(),
}),
),
description: z.string(),
title: z.string(),
}),
footer: z.object({
copyright: z.string(),
email: z.string(),
location: z.string(),
phone: z.string(),
tagline: z.string(),
}),
hero: z.object({
buttonLink: z.string(),
buttonText: z.string(),
description: z.string(),
gifFile: z.string(),
title: z.string(),
}),
created_at: z.number(),
updated_at: z.number(),
id: z.string(),
key: z.string(),
type: z.string(),
}),
),
}); });
export type TCmsResponse = z.infer<typeof CmsSchema>; const CmsSchema = z.object({
export type TCmsCredentials = z.infer<typeof CmsCredentials> advantages: z.object({
cards: z.array(
z.object({
description: z.string(),
icon: z.string(),
title: z.string(),
}),
),
description: z.string(),
title: z.string(),
}),
challenges: z.object({
cards: z.array(
z.object({
description: z.string(),
icon: z.string(),
title: z.string(),
}),
),
description: z.string(),
title: z.string(),
}),
chart: z.object({
data: z.array(
z.object({
key: z.string(),
value: z.number(),
}),
),
missedAmount: z.string(),
title: z.string(),
}),
contact: z.object({
description: z.string(),
fields: z.array(
z.object({
label: z.string(),
name: z.string(),
placeholder: z.string(),
required: z.boolean(),
}),
),
submitButtonText: z.string(),
title: z.string(),
}),
features: z.object({
cards: z.array(
z.object({
description: z.string(),
icon: z.string(),
title: z.string(),
}),
),
description: z.string(),
title: z.string(),
}),
footer: z.object({
copyright: z.string(),
email: z.string(),
location: z.string(),
phone: z.string(),
tagline: z.string(),
}),
hero: z.object({
buttonLink: z.string(),
buttonText: z.string(),
description: z.string(),
gifFile: z.string(),
title: z.string(),
}),
created_at: z.number(),
updated_at: z.number(),
id: z.string(),
key: z.string(),
type: z.string(),
});
export const cmsListResponseSchema = z.object({
cms: z.array(CmsSchema),
});
export const cmsDetailsResponse = z.object({
cms: CmsSchema,
});
export type TCmsListResponse = z.infer<typeof cmsListResponseSchema>;
export type TCmsDetailsResponse = z.infer<typeof cmsDetailsResponse>
export type TCmsCredentials = z.infer<typeof CmsCredentials>;