tasko/components/modals/card-modal/description.tsx
2024-02-14 21:30:10 -05:00

126 lines
3.6 KiB
TypeScript

"use client";
import { useEventListener, useOnClickOutside } from "usehooks-ts";
import { useQueryClient } from "@tanstack/react-query";
import { useState, ElementRef, useRef } from "react";
import { useParams } from "next/navigation";
import { AlignLeft } from "lucide-react";
import { toast } from "sonner";
import { FormTextarea } from "@/components/form/form-textarea";
import { FormSubmit } from "@/components/form/form-submit";
import { Skeleton } from "@/components/ui/skeleton";
import { updateCard } from "@/actions/update-card";
import { Button } from "@/components/ui/button";
import { useAction } from "@/hooks/use-action";
import { CardWithList } from "@/types";
interface DescriptionProps {
data: CardWithList;
}
export const Description = ({ data }: DescriptionProps) => {
const queryClient = useQueryClient();
const params = useParams();
const [isEditing, setIsEditing] = useState(false);
const textareaRef = useRef<ElementRef<"textarea">>(null);
const formRef = useRef<ElementRef<"form">>(null);
const enaleEditing = () => {
setIsEditing(true);
setTimeout(() => {
textareaRef.current?.focus();
});
};
const disableEditing = () => {
setIsEditing(false);
};
const onKeyDown = (e: KeyboardEvent) => {
if (e.key === "Escape") {
disableEditing();
}
};
useEventListener("keydown", onKeyDown);
useOnClickOutside(formRef, disableEditing);
const { execute, fieldErrors } = useAction(updateCard, {
onSuccess: (data) => {
queryClient.invalidateQueries({ queryKey: ["card", data.id] });
queryClient.invalidateQueries({ queryKey: ["card-logs", data.id] });
toast.success(`Card "${data.title}" updated`);
disableEditing();
},
onError: (error) => {
toast.error(error);
},
});
const onSubmit = (formData: FormData) => {
const description = formData.get("description") as string;
const boardId = params.boardId as string;
execute({
id: data.id,
description,
boardId,
});
};
return (
<div className="flex items-start gap-x-3 w-full">
<AlignLeft className="w-5 h-5 mt-0.5 text-neutral-700" />
<div className="w-full">
<p className="font-semibold text-neutral-700 mb-2">Description</p>
{isEditing ? (
<form ref={formRef} className="space-y-2" action={onSubmit}>
<FormTextarea
id="description"
ref={textareaRef}
className="w-full mt-2"
placeholder="Add a more detailed description..."
defaultValue={data.description ?? undefined}
errors={fieldErrors}
/>
<div className="flex items-center gap-x-2">
<FormSubmit>Save</FormSubmit>
<Button
type="button"
onClick={disableEditing}
size="sm"
variant="ghost"
>
Cancel
</Button>
</div>
</form>
) : (
<div
onClick={enaleEditing}
role="button"
className="min-h-[78px] bg-neutral-200 text-sm font-medium py-3 px-3.5 rounded-md"
>
{data.description ?? "Add a more detailed description..."}
</div>
)}
</div>
</div>
);
};
Description.Skeleton = function DescriptionSkeleton() {
return (
<div className="flex items-start gap-x-3 w-full">
<Skeleton className="h-6 w-6 bg-neutral-200" />
<div className="w-full">
<Skeleton className="h-6 w-24 mb-2 bg-neutral-200" />
<Skeleton className="w-full h-[78px] bg-neutral-200" />
</div>
</div>
);
};