forked from administration/panel
116 lines
3.6 KiB
TypeScript
116 lines
3.6 KiB
TypeScript
"use client"
|
|
|
|
import { useEffect, useState } from "react";
|
|
import { Textarea } from "../ui/textarea";
|
|
import { toast } from "../ui/use-toast";
|
|
import { SafetyNotes, fetchSafetyNote, updateSafetyNote } from "@/lib/db";
|
|
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "../ui/card";
|
|
import { useSession } from "next-auth/react";
|
|
import dayjs from "dayjs";
|
|
import relativeTime from "dayjs/plugin/relativeTime";
|
|
import ReactMarkdown from 'react-markdown';
|
|
import remarkGfm from 'remark-gfm';
|
|
|
|
dayjs.extend(relativeTime);
|
|
|
|
export default function SafetyNotesCard({ objectId, type, title }: {
|
|
objectId: string,
|
|
type: SafetyNotes["_id"]["type"],
|
|
title?: string
|
|
}) {
|
|
const session = useSession();
|
|
const [draft, setDraft] = useState("");
|
|
const [value, setValue] = useState<SafetyNotes | null>(null);
|
|
const [ready, setReady] = useState(false);
|
|
const [error, setError] = useState<string | null>(null);
|
|
const [editing, setEditing] = useState(false);
|
|
|
|
useEffect(() => {
|
|
fetchSafetyNote(objectId, type)
|
|
.then((note) => {
|
|
setDraft(note?.text || "");
|
|
setValue(note);
|
|
setReady(true);
|
|
})
|
|
.catch((e) => {
|
|
setError(String(e));
|
|
});
|
|
}, [objectId, type]);
|
|
|
|
return (
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>{title ?? type.charAt(0).toUpperCase() + type.slice(1) + " notes"}</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
{
|
|
editing
|
|
? <Textarea
|
|
placeholder={
|
|
error
|
|
? error
|
|
: ready
|
|
? "Enter notes here... (save on unfocus)"
|
|
: "Fetching notes..."
|
|
}
|
|
className="!min-h-[80px] max-h-[50vh]"
|
|
disabled={!ready || error != null}
|
|
value={ready ? draft : undefined} // not defaulting to undefined causes next to complain
|
|
autoFocus
|
|
onChange={(e) => setDraft(e.currentTarget.value)}
|
|
onBlur={async () => {
|
|
if (draft === value?.text ?? "") return setEditing(false);
|
|
|
|
try {
|
|
await updateSafetyNote(objectId, type, draft);
|
|
setValue({
|
|
_id: { id: objectId, type: type },
|
|
edited_at: new Date(Date.now()),
|
|
edited_by: session.data?.user?.email || "",
|
|
text: draft,
|
|
});
|
|
setEditing(false);
|
|
toast({
|
|
title: "Updated notes",
|
|
});
|
|
} catch (err) {
|
|
setEditing(false);
|
|
toast({
|
|
title: "Failed to update notes",
|
|
description: String(err),
|
|
variant: "destructive",
|
|
});
|
|
}
|
|
}}
|
|
/>
|
|
: <div onClick={() => setEditing(true)}>
|
|
{
|
|
error
|
|
? <>{error}</>
|
|
: value?.text
|
|
? <ReactMarkdown
|
|
className="prose prose-a:text-[#fd6671] prose-img:max-h-96"
|
|
remarkPlugins={[remarkGfm]}
|
|
>
|
|
{value.text}
|
|
</ReactMarkdown>
|
|
: ready
|
|
? <i>Click to add a note</i>
|
|
: <i>Fetching notes...</i>
|
|
}
|
|
</div>
|
|
}
|
|
</CardContent>
|
|
<CardFooter className="-my-2">
|
|
<CardDescription>
|
|
{
|
|
value
|
|
? <>Last edited {dayjs(value.edited_at).fromNow()} by {value.edited_by}</>
|
|
: <>No object note set</>
|
|
}
|
|
</CardDescription>
|
|
</CardFooter>
|
|
</Card>
|
|
)
|
|
}
|