Add commission type example image
This commit is contained in:
@ -2,16 +2,20 @@
|
||||
|
||||
import type { Value } from 'platejs';
|
||||
|
||||
import { deleteCommissionExample, uploadCommissionExample } from "@/actions/commissions/examples";
|
||||
import { saveGuidelines } from '@/actions/commissions/guidelines/saveGuidelines';
|
||||
import { BasicBlocksKit } from '@/components/editor/plugins/basic-blocks-kit';
|
||||
import { BasicMarksKit } from '@/components/editor/plugins/basic-marks-kit';
|
||||
import { CodeBlockKit } from '@/components/editor/plugins/code-block-kit';
|
||||
import { ListKit } from '@/components/editor/plugins/list-kit';
|
||||
import { MarkdownKit } from '@/components/editor/plugins/markdown-kit';
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Editor, EditorContainer } from '@/components/ui/editor';
|
||||
import { FixedToolbar } from '@/components/ui/fixed-toolbar';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { BulletedListToolbarButton, NumberedListToolbarButton } from '@/components/ui/list-toolbar-button';
|
||||
import { MarkToolbarButton } from '@/components/ui/mark-toolbar-button';
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||
import { ToolbarButton } from '@/components/ui/toolbar';
|
||||
import {
|
||||
Bold,
|
||||
@ -27,14 +31,25 @@ import {
|
||||
Underline
|
||||
} from "lucide-react";
|
||||
import { Plate, usePlateEditor } from 'platejs/react';
|
||||
import { useEffect } from 'react';
|
||||
import { useEffect, useMemo, useState, useTransition } from 'react';
|
||||
|
||||
const initialValue: Value = [
|
||||
];
|
||||
|
||||
|
||||
export default function GuidelinesEditor({ markdown }: { markdown: string | null }) {
|
||||
export default function GuidelinesEditor({
|
||||
markdown,
|
||||
exampleImageUrl,
|
||||
examples,
|
||||
}: {
|
||||
markdown: string | null;
|
||||
exampleImageUrl: string | null;
|
||||
examples: { key: string; url: string; size: number | null; lastModified: string | null }[];
|
||||
}) {
|
||||
// const [isSaving, setIsSaving] = useState(false);
|
||||
const [exampleItems, setExampleItems] = useState(examples);
|
||||
const [selectedKey, setSelectedKey] = useState<string | null>(null);
|
||||
const [isPending, startTransition] = useTransition();
|
||||
const editor = usePlateEditor({
|
||||
plugins: [
|
||||
...BasicBlocksKit,
|
||||
@ -54,17 +69,104 @@ export default function GuidelinesEditor({ markdown }: { markdown: string | null
|
||||
}
|
||||
}, [editor, markdown]);
|
||||
|
||||
useEffect(() => {
|
||||
const match = exampleItems.find((item) => item.url === exampleImageUrl);
|
||||
setSelectedKey(match?.key ?? null);
|
||||
}, [exampleImageUrl, exampleItems]);
|
||||
|
||||
const selectedUrl = useMemo(() => {
|
||||
if (!selectedKey) return "";
|
||||
return exampleItems.find((item) => item.key === selectedKey)?.url ?? "";
|
||||
}, [exampleItems, selectedKey]);
|
||||
|
||||
const handleSave = async () => {
|
||||
// console.log(editor);
|
||||
if (!editor.api.markdown.serialize) return;
|
||||
// setIsSaving(true);
|
||||
const markdown = editor.api.markdown.serialize();
|
||||
await saveGuidelines(markdown);
|
||||
await saveGuidelines(markdown, selectedUrl || null);
|
||||
// setIsSaving(false);
|
||||
};
|
||||
|
||||
const handleUpload = (file: File) => {
|
||||
const fd = new FormData();
|
||||
fd.append("file", file);
|
||||
|
||||
startTransition(async () => {
|
||||
const item = await uploadCommissionExample(fd);
|
||||
setExampleItems((prev) => [item, ...prev]);
|
||||
setSelectedKey(item.key);
|
||||
});
|
||||
};
|
||||
|
||||
const handleDelete = () => {
|
||||
if (!selectedKey) return;
|
||||
if (!window.confirm("Delete this example image from S3?")) return;
|
||||
|
||||
startTransition(async () => {
|
||||
await deleteCommissionExample(selectedKey);
|
||||
setExampleItems((prev) => prev.filter((item) => item.key !== selectedKey));
|
||||
setSelectedKey(null);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Plate editor={editor}> {/* Provides editor context */}
|
||||
<div className="px-4 pt-4 flex flex-col gap-3">
|
||||
<div className="flex flex-col gap-2">
|
||||
<Label className="text-sm font-medium">Example image</Label>
|
||||
<div className="flex flex-col gap-2 sm:flex-row sm:items-center">
|
||||
<Select
|
||||
value={selectedKey ?? undefined}
|
||||
onValueChange={(value) =>
|
||||
setSelectedKey(value === "__none__" ? null : value)
|
||||
}
|
||||
>
|
||||
<SelectTrigger className="w-full sm:max-w-md">
|
||||
<SelectValue placeholder="Select an example image" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="__none__">None</SelectItem>
|
||||
{exampleItems.map((item) => (
|
||||
<SelectItem key={item.key} value={item.key}>
|
||||
{item.key.replace("commissions/examples/", "")}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
{selectedUrl ? (
|
||||
<a
|
||||
href={selectedUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-sm text-primary underline"
|
||||
>
|
||||
Open
|
||||
</a>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-2 sm:flex-row sm:items-center">
|
||||
<input
|
||||
type="file"
|
||||
accept="image/*"
|
||||
onChange={(e) => {
|
||||
const file = e.target.files?.[0];
|
||||
if (file) handleUpload(file);
|
||||
e.currentTarget.value = "";
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
type="button"
|
||||
variant="secondary"
|
||||
onClick={handleDelete}
|
||||
disabled={!selectedKey || isPending}
|
||||
>
|
||||
Delete selected
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<FixedToolbar className="justify-start rounded-t-lg">
|
||||
{/* Blocks */}
|
||||
<ToolbarButton onClick={() => editor.tf.h1.toggle()} tooltip="Heading 1">
|
||||
@ -110,4 +212,4 @@ export default function GuidelinesEditor({ markdown }: { markdown: string | null
|
||||
</EditorContainer>
|
||||
</Plate>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user