feat(web): rename vocabularies + edit/delete terms in place (#30)
This commit is contained in:
@@ -2,7 +2,8 @@ import { useState, type FormEvent } from "react";
|
||||
import { NavLink } from "react-router-dom";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { useVocabularies, useCreateVocabulary } from "../api/queries";
|
||||
import { useVocabularies, useCreateVocabulary, useRenameVocabulary, useDeleteVocabulary } from "../api/queries";
|
||||
import { DeleteConfirmDialog } from "../components/delete-confirm-dialog";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
@@ -13,8 +14,12 @@ export function VocabularyList() {
|
||||
const { data, isLoading, isError } = useVocabularies();
|
||||
|
||||
const create = useCreateVocabulary();
|
||||
const renameVocabulary = useRenameVocabulary();
|
||||
const deleteVocabulary = useDeleteVocabulary();
|
||||
|
||||
const [key, setKey] = useState("");
|
||||
const [editingId, setEditingId] = useState<string | null>(null);
|
||||
const [draftKey, setDraftKey] = useState("");
|
||||
|
||||
const onCreate = (event: FormEvent) => {
|
||||
event.preventDefault();
|
||||
@@ -56,15 +61,59 @@ export function VocabularyList() {
|
||||
<li className="p-3 text-sm text-neutral-500">{t("vocab.empty")}</li>
|
||||
)}
|
||||
{data?.map((v) => (
|
||||
<li key={v.id}>
|
||||
<NavLink
|
||||
to={`/vocabularies/${v.id}`}
|
||||
className={({ isActive }) =>
|
||||
`block border-b px-3 py-2 text-sm ${isActive ? "bg-indigo-50" : "hover:bg-neutral-50"}`
|
||||
}
|
||||
>
|
||||
{v.key}
|
||||
</NavLink>
|
||||
<li key={v.id} className="flex items-center gap-1 border-b pr-2">
|
||||
{editingId === v.id ? (
|
||||
<form
|
||||
className="flex flex-1 flex-wrap gap-1 p-1"
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
renameVocabulary.mutate(
|
||||
{ id: v.id, key: draftKey.trim() },
|
||||
{ onSuccess: () => setEditingId(null) },
|
||||
);
|
||||
}}
|
||||
>
|
||||
<Input
|
||||
aria-label={t("vocab.key")}
|
||||
value={draftKey}
|
||||
onChange={(e) => setDraftKey(e.target.value)}
|
||||
/>
|
||||
<Button type="submit" size="sm" disabled={renameVocabulary.isPending}>
|
||||
{t("actions.save")}
|
||||
</Button>
|
||||
<Button type="button" variant="ghost" size="sm" onClick={() => setEditingId(null)}>
|
||||
{t("form.cancel")}
|
||||
</Button>
|
||||
{renameVocabulary.isError && (
|
||||
<p role="alert" className="text-xs text-red-600">
|
||||
{t("form.rejected")}
|
||||
</p>
|
||||
)}
|
||||
</form>
|
||||
) : (
|
||||
<>
|
||||
<NavLink
|
||||
to={`/vocabularies/${v.id}`}
|
||||
className={({ isActive }) =>
|
||||
`block flex-1 px-3 py-2 text-sm ${isActive ? "bg-indigo-50" : "hover:bg-neutral-50"}`
|
||||
}
|
||||
>
|
||||
{v.key}
|
||||
</NavLink>
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => { setEditingId(v.id); setDraftKey(v.key); }}
|
||||
>
|
||||
{t("actions.rename")}
|
||||
</Button>
|
||||
<DeleteConfirmDialog
|
||||
description={t("actions.confirmDeleteVocabulary")}
|
||||
onConfirm={() => deleteVocabulary.mutateAsync(v.id)}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
Reference in New Issue
Block a user