From 95e9da348fe9a84445c7a8a19511accec45c3b2f Mon Sep 17 00:00:00 2001 From: Arik Date: Wed, 28 Aug 2024 16:59:32 +0300 Subject: [PATCH] added changes for edit and delete guides --- src/api/guide.client.ts | 14 +++- src/api/types/Guide.ts | 15 ++-- .../new_dashboard/guides/[id]/page.tsx | 33 +++++---- .../components/DeleteEditButtons/index.tsx | 42 +++++++++++ .../guides/components/Editor/index.tsx | 35 +++++---- .../guides/edit/[id]/hooks/useUpdateGuide.ts | 7 -- .../new_dashboard/guides/edit/[id]/page.tsx | 74 ++++++++++++++----- .../guides/hooks/guideClientHooks.ts | 63 ++++++++++++++++ .../new_dashboard/shared/Card/index.tsx | 35 +++++++-- src/app/globals.css | 2 +- 10 files changed, 248 insertions(+), 72 deletions(-) create mode 100644 src/app/(admin)/new_dashboard/guides/components/DeleteEditButtons/index.tsx delete mode 100644 src/app/(admin)/new_dashboard/guides/edit/[id]/hooks/useUpdateGuide.ts create mode 100644 src/app/(admin)/new_dashboard/guides/hooks/guideClientHooks.ts diff --git a/src/api/guide.client.ts b/src/api/guide.client.ts index 3afc510..76cd7be 100644 --- a/src/api/guide.client.ts +++ b/src/api/guide.client.ts @@ -1,6 +1,6 @@ import { CreateReviewDto } from "@/app/(admin)/new_dashboard/guides/dto/CreateReviewDto"; import { BaseClient } from "./base.client"; -import { CreateGuideRequest, Guide } from "./types/Guide"; +import { Guide, GuideRequestUpdate } from "./types/Guide"; import { Review } from "./types/Review"; export class GuideClient extends BaseClient { @@ -12,7 +12,15 @@ export class GuideClient extends BaseClient { return await this.get("guides/" + guideId); } - async createGuide(createGuideRequest: CreateGuideRequest) { - return await this.post("guides", createGuideRequest); + async deleteGuide(guideId: number) { + return await this.delete("guides/" + guideId); + } + + async addReview(review: CreateReviewDto) { + return await this.post("reviews", review); + } + + async updateGuide(id: number, guide: GuideRequestUpdate) { + return await this.patch("guides/" + id, guide); } } diff --git a/src/api/types/Guide.ts b/src/api/types/Guide.ts index 6033db2..f6c42f3 100644 --- a/src/api/types/Guide.ts +++ b/src/api/types/Guide.ts @@ -12,9 +12,14 @@ const guideSchema = z.object({ reviews: z.array(reviewSchema).optional(), }); -export type Guide = z.infer; - -export type CreateGuideRequest = { - title: string; - contentHTML: string; +export type UpdateGuidePayload = { + id: number; + guide: { + title: string; + contentHTML: string; + }; }; + +export type GuideRequestUpdate = Partial; + +export type Guide = z.infer; diff --git a/src/app/(admin)/new_dashboard/guides/[id]/page.tsx b/src/app/(admin)/new_dashboard/guides/[id]/page.tsx index bd0bbcc..17e4b33 100644 --- a/src/app/(admin)/new_dashboard/guides/[id]/page.tsx +++ b/src/app/(admin)/new_dashboard/guides/[id]/page.tsx @@ -3,10 +3,8 @@ import React from "react"; import PageContainer from "@/components/PageContainer"; import { useParams } from "next/navigation"; +import { useGuide } from "../hooks/guideClientHooks"; import DashboardCard from "../../shared/Card"; - -import { useGuide } from "@/hooks"; - import { Typography, CircularProgress, @@ -17,17 +15,20 @@ import { import ReviewList from "../components/ReviewList"; import parse from "html-react-parser"; import "quill/dist/quill.snow.css"; -import { useChat } from "@/app/hooks/useChat"; -import ChatPopup from "../../components/ChatPopup"; +import DeleteEditButtons from "../components/DeleteEditButtons"; const GuidePage: React.FC = () => { const params = useParams(); const id = params?.id ? Number(params.id) : null; - const { data: response, isLoading, isError, error, isSuccess } = useGuide(id ?? 0); - - const {selectedContact} = useChat(); + const { + data: response, + isLoading, + isError, + error, + isSuccess, + } = useGuide(id ?? 0); if (isLoading) { return ; @@ -48,15 +49,17 @@ const GuidePage: React.FC = () => { } return ( - - {guide.title} - {creatorAndDateInfo} - - {parse(guide.contentHTML)} - + + } + > + {parse(guide.contentHTML)} + - + ); } }; diff --git a/src/app/(admin)/new_dashboard/guides/components/DeleteEditButtons/index.tsx b/src/app/(admin)/new_dashboard/guides/components/DeleteEditButtons/index.tsx new file mode 100644 index 0000000..855cf74 --- /dev/null +++ b/src/app/(admin)/new_dashboard/guides/components/DeleteEditButtons/index.tsx @@ -0,0 +1,42 @@ +"use client"; + +import React from "react"; +import { Box, IconButton } from "@mui/material"; +import ModeEditOutlineOutlinedIcon from "@mui/icons-material/ModeEditOutlineOutlined"; +import DeleteOutlinedIcon from "@mui/icons-material/DeleteOutlined"; +import { useDeleteGuide } from "../../hooks/guideClientHooks"; +import { useParams, useRouter } from "next/navigation"; + +const DeleteEditButtons: React.FC = () => { + const router = useRouter(); + const params = useParams(); + + const id = params?.id ? Number(params.id) : 0; + const deleteMutation = useDeleteGuide(id); + + const handleDelete = () => { + deleteMutation.mutate(); + }; + + const handleEdit = () => { + router.push(`/new_dashboard/guides/edit/${id}`); + }; + + return ( + + + + + + + + + ); +}; + +export default DeleteEditButtons; diff --git a/src/app/(admin)/new_dashboard/guides/components/Editor/index.tsx b/src/app/(admin)/new_dashboard/guides/components/Editor/index.tsx index 1de4666..8e050ed 100644 --- a/src/app/(admin)/new_dashboard/guides/components/Editor/index.tsx +++ b/src/app/(admin)/new_dashboard/guides/components/Editor/index.tsx @@ -1,30 +1,26 @@ import dynamic from "next/dynamic"; -import { useState } from "react"; +import { ChangeEvent, useState } from "react"; import "react-quill/dist/quill.snow.css"; import { Box, Button, TextField } from "@mui/material"; import SaveAsIcon from "@mui/icons-material/SaveAs"; -import { UseFormRegister, UseFormSetValue } from "react-hook-form"; -import { CreateGuideRequest } from "@/api/types/Guide"; const DynamicReactQuill = dynamic(() => import("react-quill")); interface Props { initialTitle?: string; + initialIssue?: string; initialContent?: string; - onSave: () => void; - register: UseFormRegister; - error: Error | null; - setValue: UseFormSetValue; + onSave: (title: string, content: string) => void; } const GuideEditor = ({ - initialContent = "", initialTitle = "", + initialContent = "", onSave, - register, - error, - setValue, }: Props) => { + const [guideTitle, setGuideTitle] = useState(initialTitle); + const [contentHTML, setContent] = useState(initialContent); + const modules = { toolbar: [ [{ header: [1, 2, 3, 4, 5, 6, false] }], @@ -55,32 +51,35 @@ const GuideEditor = ({ "align", ]; + const handleTitleChange = (event: ChangeEvent) => { + setGuideTitle(event.target.value); + }; + return ( <> setValue("title", e.target.value)} sx={{ mb: 2 }} + value={guideTitle} + onChange={handleTitleChange} /> + setValue("contentHTML", content)} + onChange={(content) => setContent(content)} /> - diff --git a/src/app/(admin)/new_dashboard/guides/edit/[id]/hooks/useUpdateGuide.ts b/src/app/(admin)/new_dashboard/guides/edit/[id]/hooks/useUpdateGuide.ts deleted file mode 100644 index 3b23f43..0000000 --- a/src/app/(admin)/new_dashboard/guides/edit/[id]/hooks/useUpdateGuide.ts +++ /dev/null @@ -1,7 +0,0 @@ -export const useUpdateGuide = () => { - const handleSave = (content: string) => { - console.log("Saving new guide:", content); - }; - - return { handleSave }; -}; diff --git a/src/app/(admin)/new_dashboard/guides/edit/[id]/page.tsx b/src/app/(admin)/new_dashboard/guides/edit/[id]/page.tsx index 0c6eb8c..ce7f5ed 100644 --- a/src/app/(admin)/new_dashboard/guides/edit/[id]/page.tsx +++ b/src/app/(admin)/new_dashboard/guides/edit/[id]/page.tsx @@ -1,26 +1,66 @@ "use client"; import PageContainer from "@/components/PageContainer"; import DashboardCard from "../../../shared/Card"; -import { Box } from "@mui/material"; +import { Alert, Box, CircularProgress, Typography } from "@mui/material"; import GuideEditor from "../../components/Editor"; -import { useUpdateGuide } from "./hooks/useUpdateGuide"; +import { useGuide, useUpdateGuide } from "../../hooks/guideClientHooks"; +import { useParams, useRouter } from "next/navigation"; const Page = () => { - const { handleSave } = useUpdateGuide(); - - return ( - - - - - - - - ); + const handleSave = useUpdateGuide(); + const params = useParams(); + const router = useRouter(); + + const id = params?.id ? Number(params.id) : 0; + + const handleOnSave = (id: number, title: string, contentHTML: string) => { + handleSave.mutate({ id, guide: { title, contentHTML } }); + }; + + const { + data: response, + isLoading, + isError, + error, + isSuccess, + } = useGuide(id ?? 0); + + if (isLoading) { + return ; + } + + if (isError) { + return {error.message}; + } + + if (isSuccess && "data" in response) { + const guide = response.data; + + const creatorAndDateInfo = `Created by ${ + guide.creator?.username + } on ${new Date(guide.createdAt).toLocaleDateString()}`; + + if (!guide.title) { + return Guide not found; + } + return ( + + + + handleOnSave(id, title, content)} + initialTitle={guide.title} + initialContent={guide.contentHTML} + /> + + + + ); + } }; export default Page; diff --git a/src/app/(admin)/new_dashboard/guides/hooks/guideClientHooks.ts b/src/app/(admin)/new_dashboard/guides/hooks/guideClientHooks.ts new file mode 100644 index 0000000..fc85493 --- /dev/null +++ b/src/app/(admin)/new_dashboard/guides/hooks/guideClientHooks.ts @@ -0,0 +1,63 @@ +import { GuideClient } from "@/api/guide.client"; +import { useQuery, useMutation } from "@tanstack/react-query"; +import { CreateReviewDto } from "../dto/CreateReviewDto"; +import { + Guide, + GuideRequestUpdate, + UpdateGuidePayload, +} from "@/api/types/Guide"; +import { useRouter } from "next/navigation"; + +const guideClient = new GuideClient(); + +export function useAllGuides() { + return useQuery({ + queryKey: ["guides"], + queryFn: () => guideClient.getAllGuides(), + }); +} + +export function useGuide(guideId: number) { + return useQuery({ + queryKey: ["guide", guideId], + queryFn: () => guideClient.getGuide(guideId), + }); +} + +export function useDeleteGuide(guideId: number) { + const router = useRouter(); + return useMutation({ + mutationFn: () => guideClient.deleteGuide(guideId), + onSuccess: () => { + router.push("/new_dashboard/guides"); + }, + onError: () => { + alert("Failed to delete guide. Please try again."); + }, + }); +} + +export const useUpdateGuide = () => { + const router = useRouter(); + return useMutation({ + mutationFn: async (payload: UpdateGuidePayload) => { + const { id, guide } = payload; + return guideClient.updateGuide(id, guide); + }, + onSuccess: (data, variables) => { + const { id } = variables; + router.push(`/new_dashboard/guides/${id}`); + }, + onError: (error) => { + console.log(error); + + alert("Failed to update guide. Please try again."); + }, + }); +}; + +export function useAddReview() { + return useMutation({ + mutationFn: (review: CreateReviewDto) => guideClient.addReview(review), + }); +} diff --git a/src/app/(admin)/new_dashboard/shared/Card/index.tsx b/src/app/(admin)/new_dashboard/shared/Card/index.tsx index e7c120d..b26ff18 100644 --- a/src/app/(admin)/new_dashboard/shared/Card/index.tsx +++ b/src/app/(admin)/new_dashboard/shared/Card/index.tsx @@ -11,6 +11,7 @@ type Props = { children?: JSX.Element; middlecontent?: string | JSX.Element; fullHeight?: boolean; + buttons?: JSX.Element; }; const DashboardCard = ({ @@ -24,6 +25,7 @@ const DashboardCard = ({ headsubtitle, middlecontent, fullHeight = false, + buttons, }: Props) => { return ( - - {title ? {title} : ""} + + + {title ? ( + + {title} + + ) : ( + "" + )} - {subtitle ? ( - - {subtitle} - + {subtitle ? ( + + {subtitle} + + ) : ( + "" + )} + + {buttons ? ( + + {buttons} + ) : ( "" )} diff --git a/src/app/globals.css b/src/app/globals.css index 07fb699..e1eebe9 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -3,7 +3,7 @@ @tailwind utilities; .quill-content { - @apply font-sans text-gray-700 p-4 bg-white rounded-lg shadow-md; + @apply font-sans text-gray-700 p-4 bg-white rounded-lg; line-height: 1.6; }