diff --git a/package-lock.json b/package-lock.json index 3b12f0b..4f23825 100644 --- a/package-lock.json +++ b/package-lock.json @@ -32,6 +32,7 @@ "@babel/cli": "^7.20.7", "@babel/core": "^7.20.12", "@babel/preset-typescript": "^7.18.6", + "@types/react-toastify": "^4.0.2", "@typescript-eslint/eslint-plugin": "^5.49.0", "@typescript-eslint/parser": "^5.49.0", "eslint": "^8.32.0", @@ -4213,6 +4214,27 @@ "@types/react": "*" } }, + "node_modules/@types/react-toastify": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/react-toastify/-/react-toastify-4.0.2.tgz", + "integrity": "sha512-pHjCstnN0ZgopIWQ9UiWsD9n+HsXs1PnMQC4hIZuSzpDO0lRjigpTuqsUtnBkMbLIg+mGFSAsBjL49SspzoLKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/react": "*", + "@types/react-transition-group": "*" + } + }, + "node_modules/@types/react-transition-group": { + "version": "4.4.12", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz", + "integrity": "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "*" + } + }, "node_modules/@types/resolve": { "version": "1.17.1", "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", @@ -21723,6 +21745,23 @@ "@types/react": "*" } }, + "@types/react-toastify": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/react-toastify/-/react-toastify-4.0.2.tgz", + "integrity": "sha512-pHjCstnN0ZgopIWQ9UiWsD9n+HsXs1PnMQC4hIZuSzpDO0lRjigpTuqsUtnBkMbLIg+mGFSAsBjL49SspzoLKA==", + "dev": true, + "requires": { + "@types/react": "*", + "@types/react-transition-group": "*" + } + }, + "@types/react-transition-group": { + "version": "4.4.12", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz", + "integrity": "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==", + "dev": true, + "requires": {} + }, "@types/resolve": { "version": "1.17.1", "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", diff --git a/package.json b/package.json index bbe6f1b..d664795 100644 --- a/package.json +++ b/package.json @@ -56,6 +56,7 @@ "@babel/cli": "^7.20.7", "@babel/core": "^7.20.12", "@babel/preset-typescript": "^7.18.6", + "@types/react-toastify": "^4.0.2", "@typescript-eslint/eslint-plugin": "^5.49.0", "@typescript-eslint/parser": "^5.49.0", "eslint": "^8.32.0", diff --git a/src/App.tsx b/src/App.tsx index f9fb05b..c13f921 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,11 +1,13 @@ import React from 'react'; import Router from './Router'; import UserContextProvider from '@/contexts/UserContextProvider'; - +import "react-toastify/dist/ReactToastify.css"; +import { ToastContainer } from "react-toastify"; function App() { return (
+
diff --git a/src/components/article/Revisions.tsx b/src/components/article/Revisions.tsx new file mode 100644 index 0000000..a393b98 --- /dev/null +++ b/src/components/article/Revisions.tsx @@ -0,0 +1,54 @@ + +import { useRevisions,useRevertRevision } from "@/lib/hooks/useRevisions"; +import React from "react"; + +import { toast } from "react-toastify"; +interface RevisionsProps { + articleId: number; + isAdmin: boolean; +} + +const Revisions: React.FC = ({ articleId, isAdmin }) => { + const { data: revisions, isLoading, error } = useRevisions(articleId); + const revertMutation = useRevertRevision(); + + const handleRevert = async (revisionId: number) => { + try { + await revertMutation.mutateAsync({ articleId, revisionId }); + toast.success("Article reverted successfully!"); + } catch (err) { + toast.error("Failed to revert article"); + } + }; + + if (isLoading) return

Loading revisions...

; + if (error) return

Error loading revisions

; + + return ( +
+

Revision History

+ +
+ ); +}; + +export default Revisions; diff --git a/src/lib/hooks/useRevisions.ts b/src/lib/hooks/useRevisions.ts new file mode 100644 index 0000000..849e34d --- /dev/null +++ b/src/lib/hooks/useRevisions.ts @@ -0,0 +1,35 @@ +import apiClient from '@/repositories/apiClient'; +import { useMutation, useQuery } from '@tanstack/react-query'; + +// Revision Type +export interface Revision { + id: number; + article_id: number; + content: string; + created_at: string; +} + +// Fetch Revisions +export const useRevisions = (articleId: number) => + useQuery(["revisions", articleId], async () => { + const { data } = await apiClient.get(`/articles/${articleId}/revisions`); + return data; + }); + +// Fetch Single Revision +export const useRevision = (articleId: number, revisionId: number) => + useQuery(["revision", articleId, revisionId], async () => { + const { data } = await apiClient.get( + `/articles/${articleId}/revisions/${revisionId}` + ); + return data; + }); + +// Revert to a Revision +export const useRevertRevision = () => + useMutation(async ({ articleId, revisionId }: { articleId: number; revisionId: number }) => { + const { data } = await apiClient.post( + `/articles/${articleId}/revisions/${revisionId}/revert` + ); + return data; + }); diff --git a/src/pages/ArticlePage.tsx b/src/pages/ArticlePage.tsx index a71c90f..11a3e5e 100644 --- a/src/pages/ArticlePage.tsx +++ b/src/pages/ArticlePage.tsx @@ -8,11 +8,17 @@ import { UserContext } from '@/contexts/UserContextProvider'; import Comment from '@/components/article/Comment'; import routerMeta from '@/lib/routerMeta'; import convertToDate from '@/lib/utils/convertToDate'; +//import Revisions from '@/components/Revisions'; // ✅ Import Revisions +import { useGetUserQuery } from '@/queries/user.query'; +import Revisions from '@/components/article/Revisions'; const ArticlePage = () => { const { state } = useLocation(); const [articleInfo, commentsInfo] = useGetArticleQueries(state); - const { isLogin } = useContext(UserContext); + const { isLogin } = useContext(UserContext); // ✅ Get user info for admin check + const { data } = useGetUserQuery(); + const articleId = articleInfo.data.id; + const isAdmin = data?.is_admin || data?.id === articleInfo.data.author.id; // ✅ Check if user is admin or author return (
@@ -55,6 +61,11 @@ const ArticlePage = () => {

+ {/* ✅ Add Revisions Section */} +
+ +
+