Can create new blogs
This commit is contained in:
141
frontend/app/(auth)/dashboard/blogs/new/new.tsx
Normal file
141
frontend/app/(auth)/dashboard/blogs/new/new.tsx
Normal file
@@ -0,0 +1,141 @@
|
||||
"use client";
|
||||
import EditableText from "@/components/editable-text";
|
||||
import { LocalEditor } from "@/components/editor";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Dialog, DialogTrigger, DialogContent } from "@/components/ui/dialog";
|
||||
import useApiMutation from "@/hooks/use-api";
|
||||
import sluggify from "@/lib/sluggify";
|
||||
import { NewBlog } from "@/types/types";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { DialogTitle } from "@radix-ui/react-dialog";
|
||||
import { useToast } from "@/hooks/use-toast";
|
||||
import {
|
||||
ActionButton,
|
||||
ActionButtonDefault,
|
||||
ActionButtonError,
|
||||
ActionButtonLoading,
|
||||
ActionButtonSuccess,
|
||||
} from "@/components/action-button";
|
||||
|
||||
export default function BlogEditor() {
|
||||
const { toast } = useToast();
|
||||
const [title, setTitle] = useState("");
|
||||
const [imageUrl, setImageUrl] = useState<string>("/placeholder.svg");
|
||||
|
||||
const content = localStorage.getItem("blog_draft") ?? "";
|
||||
|
||||
useEffect(() => {
|
||||
const localImage = localStorage.getItem("blog_draft_image");
|
||||
setImageUrl(
|
||||
localImage && localImage.length > 0
|
||||
? localImage
|
||||
: "/placeholder.svg",
|
||||
);
|
||||
}, []);
|
||||
|
||||
const [summary, setSummary] = useState("");
|
||||
const slug = useMemo(() => sluggify(title), [title]);
|
||||
const {
|
||||
trigger: newBlog,
|
||||
isMutating: isSending,
|
||||
isSuccess,
|
||||
error,
|
||||
} = useApiMutation<undefined, NewBlog>(
|
||||
"/blogs/new",
|
||||
{},
|
||||
"POST",
|
||||
true,
|
||||
false,
|
||||
);
|
||||
|
||||
return (
|
||||
<section className="m-10 flex flex-col gap-4">
|
||||
<div className="flex flex-col gap-4">
|
||||
<EditableText onChange={setTitle}>
|
||||
<h1>
|
||||
{title.length > 0 ? title : "Un titre doit-être fourni"}
|
||||
</h1>
|
||||
</EditableText>
|
||||
<p className="italic">{slug}</p>
|
||||
<Dialog>
|
||||
<DialogTrigger>
|
||||
<img
|
||||
src={imageUrl}
|
||||
alt="Blog cover"
|
||||
className="w-full h-60 object-cover cursor-pointer rounded-lg"
|
||||
/>
|
||||
</DialogTrigger>
|
||||
<DialogTitle></DialogTitle>
|
||||
<DialogContent>
|
||||
<Input
|
||||
type="text"
|
||||
placeholder="Enter image URL"
|
||||
value={imageUrl}
|
||||
onChange={(e) => {
|
||||
setImageUrl(e.target.value);
|
||||
localStorage.setItem(
|
||||
"blog_draft_image",
|
||||
e.currentTarget.value,
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
<Input
|
||||
type="text"
|
||||
placeholder="Enter a summary"
|
||||
value={summary}
|
||||
onChange={(e) => setSummary(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex">
|
||||
<div className="flex-1">
|
||||
<LocalEditor content={content} setTitle={setTitle} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ActionButton
|
||||
isLoading={isSending}
|
||||
isSuccess={isSuccess}
|
||||
error={error}
|
||||
onClick={async () => {
|
||||
try {
|
||||
const blogContent = localStorage.getItem("blog_draft");
|
||||
if (!blogContent) return;
|
||||
if (title.length < 1) return;
|
||||
const res = await newBlog({
|
||||
title,
|
||||
summary,
|
||||
image: imageUrl,
|
||||
slug,
|
||||
content: blogContent,
|
||||
});
|
||||
if (!res) {
|
||||
toast({ title: "Aucune réponse du serveur." });
|
||||
return;
|
||||
}
|
||||
if (res.status === "Error") {
|
||||
toast({
|
||||
title: "Erreur.",
|
||||
content: "Une erreur est survenue.",
|
||||
});
|
||||
}
|
||||
if (res.data) console.log(res.data);
|
||||
return res;
|
||||
} catch (error: any) {
|
||||
toast({
|
||||
title: "Erreur.",
|
||||
content: "Une erreur est survenue.",
|
||||
});
|
||||
}
|
||||
}}
|
||||
>
|
||||
<ActionButtonDefault>Publier</ActionButtonDefault>
|
||||
<ActionButtonSuccess />
|
||||
<ActionButtonError />
|
||||
<ActionButtonLoading />
|
||||
</ActionButton>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@@ -1,73 +1,13 @@
|
||||
"use client";
|
||||
import EditableText from "@/components/editable-text";
|
||||
import { LocalEditor } from "@/components/editor";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import useApiMutation from "@/hooks/use-api";
|
||||
import sluggify from "@/lib/sluggify";
|
||||
import { NewBlog } from "@/types/types";
|
||||
import { useMemo, useState } from "react";
|
||||
|
||||
export default function BlogEditor() {
|
||||
const [title, setTitle] = useState("");
|
||||
const slug = useMemo(() => sluggify(title), [title]);
|
||||
const {
|
||||
trigger: newBlog,
|
||||
isMutating: loading,
|
||||
isSuccess,
|
||||
} = useApiMutation<undefined, NewBlog>(
|
||||
"/blog/new",
|
||||
undefined,
|
||||
"POST",
|
||||
true,
|
||||
false,
|
||||
);
|
||||
import { Loader2 } from "lucide-react";
|
||||
import dynamic from "next/dynamic";
|
||||
|
||||
const content = localStorage.getItem("blog_draft") ?? "";
|
||||
const BlogEditor = dynamic(() => import("./new").then((mod) => mod.default), {
|
||||
ssr: false,
|
||||
loading: () => <Loader2 className="animate-spin" />,
|
||||
});
|
||||
|
||||
return (
|
||||
<section className="m-10">
|
||||
{/* This div should have a proper layout, ONLY PART TO BE MODIFIED*/}
|
||||
<div>
|
||||
{/*Should have an image or a placeholder that opens a dialog when clicked on to set the url of an image.*/}
|
||||
<EditableText onChange={setTitle}>
|
||||
<h1>{title}</h1>
|
||||
</EditableText>
|
||||
<p className="text-muted">{slug}</p>
|
||||
{/*Should have a summary input box or textarea*/}
|
||||
</div>
|
||||
<div className="flex">
|
||||
<div className="flex-1">
|
||||
<LocalEditor content={content} setTitle={setTitle} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
className="text-black bg-white"
|
||||
onClick={async () => {
|
||||
try {
|
||||
const blogContent = localStorage.getItem("blog_draft");
|
||||
if (!blogContent) return;
|
||||
if (title.length < 1) return;
|
||||
const res = await newBlog({
|
||||
title,
|
||||
summary: "A summary",
|
||||
image: "none",
|
||||
slug,
|
||||
content: blogContent,
|
||||
});
|
||||
if (!res)
|
||||
throw new Error("The server hasn't responded.");
|
||||
if (res.status === "Error")
|
||||
throw new Error(res.message);
|
||||
if (res.data) console.log(res.data);
|
||||
return res;
|
||||
} catch (error: any) {
|
||||
throw new Error(error.message);
|
||||
}
|
||||
}}
|
||||
>
|
||||
Sauvegarder
|
||||
</Button>
|
||||
</section>
|
||||
);
|
||||
export default function Page() {
|
||||
return <BlogEditor />;
|
||||
}
|
||||
|
||||
@@ -72,4 +72,8 @@ body {
|
||||
body {
|
||||
@apply bg-background text-foreground;
|
||||
}
|
||||
|
||||
h1 {
|
||||
@apply text-4xl font-bold;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user