save button
This commit is contained in:
120
frontend/app/(auth)/dashboard/blogs/new/editor.tsx
Normal file
120
frontend/app/(auth)/dashboard/blogs/new/editor.tsx
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useState } from "react";
|
||||||
|
import {
|
||||||
|
MDXEditor,
|
||||||
|
toolbarPlugin,
|
||||||
|
headingsPlugin,
|
||||||
|
listsPlugin,
|
||||||
|
quotePlugin,
|
||||||
|
thematicBreakPlugin,
|
||||||
|
markdownShortcutPlugin,
|
||||||
|
BoldItalicUnderlineToggles,
|
||||||
|
ListsToggle,
|
||||||
|
BoldItalicUnderlineTogglesProps,
|
||||||
|
MDXEditorProps,
|
||||||
|
UndoRedo,
|
||||||
|
InsertImage,
|
||||||
|
CreateLink
|
||||||
|
} from "@mdxeditor/editor";
|
||||||
|
import DOMPurify from "isomorphic-dompurify";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import useApiMutation from "@/hooks/use-api";
|
||||||
|
|
||||||
|
export default function Editor() {
|
||||||
|
const [blogContent, setBlogContent] = useState("");
|
||||||
|
const [cursor, setCursor] = useState<{ line: number; column: number }>({
|
||||||
|
line: 0,
|
||||||
|
column: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
const getCursorPosition = (newText: string) => {
|
||||||
|
const cursorPos = newText.length;
|
||||||
|
const lines = newText.substring(0, cursorPos).split("\n");
|
||||||
|
const line = lines.length; // Current line number
|
||||||
|
const column = lines[lines.length - 1].length + 1; // Current column number
|
||||||
|
|
||||||
|
setCursor({ line, column });
|
||||||
|
};
|
||||||
|
|
||||||
|
const sanitized = DOMPurify.sanitize(blogContent);
|
||||||
|
const propsa: BoldItalicUnderlineTogglesProps = { options: ["Bold", "Italic", "Underline"] }
|
||||||
|
const mdxEditorProps: MDXEditorProps = {}
|
||||||
|
|
||||||
|
const {
|
||||||
|
trigger,
|
||||||
|
isMutating: loading,
|
||||||
|
isSuccess,
|
||||||
|
} = useApiMutation(
|
||||||
|
"/blog/new",
|
||||||
|
undefined,
|
||||||
|
"POST",
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className="m-10">
|
||||||
|
<div className="flex">
|
||||||
|
<div className="flex-1">
|
||||||
|
<MDXEditor
|
||||||
|
plugins={[
|
||||||
|
headingsPlugin(),
|
||||||
|
listsPlugin(),
|
||||||
|
quotePlugin(),
|
||||||
|
thematicBreakPlugin(),
|
||||||
|
markdownShortcutPlugin(),
|
||||||
|
toolbarPlugin({
|
||||||
|
toolbarContents: () => (
|
||||||
|
<div className="flex flex-row">
|
||||||
|
<UndoRedo />
|
||||||
|
<BoldItalicUnderlineToggles {...propsa} />
|
||||||
|
<InsertImage />
|
||||||
|
<CreateLink />
|
||||||
|
<ListsToggle />
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
]}
|
||||||
|
onChange={setBlogContent}
|
||||||
|
onBlur={() => getCursorPosition(blogContent)}
|
||||||
|
markdown="ok"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
Line: {cursor.line}; Column: {cursor.column}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
className="mt-4 w-1/2 p-2 bg-gray-100 border rounded-md text-sm text-black"
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: sanitized,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Button className="text-black bg-white" onClick={async () => {
|
||||||
|
try {
|
||||||
|
const res = await trigger({
|
||||||
|
label: "This is my label",
|
||||||
|
summary: "A summary",
|
||||||
|
image: "none",
|
||||||
|
href: "none",
|
||||||
|
blogID: "id",
|
||||||
|
slug: "myslug",
|
||||||
|
content: blogContent,
|
||||||
|
published: "",
|
||||||
|
});
|
||||||
|
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 >
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,77 +1,9 @@
|
|||||||
|
import Editor from "./editor"
|
||||||
"use client";
|
|
||||||
|
|
||||||
import { useEffect, useState, useRef } from "react";
|
|
||||||
import { Button } from "@/components/ui/button";
|
|
||||||
import { Bold, Italic, Strikethrough, Underline } from "lucide-react";
|
|
||||||
import { MDXEditor } from "@mdxeditor/editor"; // Assuming react-mdx-editor or similar
|
|
||||||
import DOMPurify from "isomorphic-dompurify";
|
|
||||||
|
|
||||||
export default function NewBlog() {
|
export default function NewBlog() {
|
||||||
const [text, setText] = useState("");
|
return (
|
||||||
const [cursor, setCursor] = useState<{ line: number; column: number }>({
|
<>
|
||||||
line: 0,
|
<Editor />
|
||||||
column: 0,
|
</>
|
||||||
});
|
)
|
||||||
|
|
||||||
// Function to update the cursor position
|
|
||||||
const getCursorPosition = (newText: string) => {
|
|
||||||
const cursorPos = newText.length;
|
|
||||||
const lines = newText.substring(0, cursorPos).split("\n");
|
|
||||||
const line = lines.length; // Current line number
|
|
||||||
const column = lines[lines.length - 1].length + 1; // Current column number
|
|
||||||
|
|
||||||
setCursor({ line, column });
|
|
||||||
};
|
|
||||||
|
|
||||||
const execCommand = (command: string) => {
|
|
||||||
const pre = text.slice(0, cursor.column);
|
|
||||||
const post = text.slice(cursor.column);
|
|
||||||
|
|
||||||
setText(pre + command + post);
|
|
||||||
getCursorPosition(pre + command + post); // Update cursor position after change
|
|
||||||
};
|
|
||||||
|
|
||||||
// Sanitize the MDX text before rendering
|
|
||||||
const sanitized = DOMPurify.sanitize(text);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<section className="flex m-5">
|
|
||||||
<div className="flex-1">
|
|
||||||
<div className="flex gap-2 mb-2 border-b pb-2">
|
|
||||||
<Button variant="outline" size="icon" onClick={() => execCommand("**")}>
|
|
||||||
<Bold size={16} />
|
|
||||||
</Button>
|
|
||||||
<Button variant="outline" size="icon" onClick={() => execCommand("*")}>
|
|
||||||
<Italic size={16} />
|
|
||||||
</Button>
|
|
||||||
<Button variant="outline" size="icon" onClick={() => execCommand("__")}>
|
|
||||||
<Underline size={16} />
|
|
||||||
</Button>
|
|
||||||
<Button variant="outline" size="icon" onClick={() => execCommand("~~")}>
|
|
||||||
<Strikethrough size={16} />
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Using MDXEditor instead of a basic Textarea */}
|
|
||||||
<MDXEditor
|
|
||||||
onChange={setText}
|
|
||||||
onBlur={() => getCursorPosition(text)}
|
|
||||||
markdown="ok"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
Line: {cursor.line}; Column: {cursor.column}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
|
||||||
className="mt-4 p-2 bg-gray-100 border rounded-md text-sm text-black"
|
|
||||||
dangerouslySetInnerHTML={{
|
|
||||||
__html: sanitized,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</section>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ const apiUrl =
|
|||||||
|
|
||||||
const nextConfig: NextConfig = {
|
const nextConfig: NextConfig = {
|
||||||
/* config options here */
|
/* config options here */
|
||||||
|
transpilePackages: ['@mdxeditor/editor'],
|
||||||
output: "standalone",
|
output: "standalone",
|
||||||
compiler: {
|
compiler: {
|
||||||
removeConsole: process.env.NODE_ENV === "production",
|
removeConsole: process.env.NODE_ENV === "production",
|
||||||
|
|||||||
Reference in New Issue
Block a user