diff --git a/frontend/components/Label/Chip.vue b/frontend/components/Label/Chip.vue index c1b4a0f8..d8bc7d68 100644 --- a/frontend/components/Label/Chip.vue +++ b/frontend/components/Label/Chip.vue @@ -15,6 +15,11 @@ default: "md", }, }); + + // Type guard to check if label is LabelOut (has parent/children) + const isLabelOut = (label: LabelOut | LabelSummary): label is LabelOut => { + return 'parent' in label || 'children' in label; + }; diff --git a/frontend/components/Label/CreateModal.vue b/frontend/components/Label/CreateModal.vue index b0a70c0c..76df9e80 100644 --- a/frontend/components/Label/CreateModal.vue +++ b/frontend/components/Label/CreateModal.vue @@ -15,6 +15,7 @@ :max-length="1000" /> +
@@ -37,6 +38,8 @@ import FormTextField from "~/components/Form/TextField.vue"; import FormTextArea from "~/components/Form/TextArea.vue"; import { Button, ButtonGroup } from "~/components/ui/button"; + import LabelParentSelector from "@/components/Label/ParentSelector.vue"; + import type { LabelOut } from "~/lib/api/types/data-contracts"; const { t } = useI18n(); @@ -49,13 +52,25 @@ const form = reactive({ name: "", description: "", - color: "", // Future! + color: "", + parentId: null as string | null, + }); + + const labels = ref([]); + + // Load labels for parent selection + onMounted(async () => { + const { data } = await api.labels.getAll(); + if (data) { + labels.value = data; + } }); function reset() { form.name = ""; form.description = ""; form.color = ""; + form.parentId = null; focused.value = false; loading.value = false; } diff --git a/frontend/components/Label/ParentSelector.vue b/frontend/components/Label/ParentSelector.vue new file mode 100644 index 00000000..c6413f35 --- /dev/null +++ b/frontend/components/Label/ParentSelector.vue @@ -0,0 +1,40 @@ + + + diff --git a/frontend/pages/label/[id].vue b/frontend/pages/label/[id].vue index 83620867..39465a74 100644 --- a/frontend/pages/label/[id].vue +++ b/frontend/pages/label/[id].vue @@ -21,6 +21,8 @@ import PageQRCode from "~/components/global/PageQRCode.vue"; import Markdown from "~/components/global/Markdown.vue"; import ItemViewSelectable from "~/components/Item/View/Selectable.vue"; + import LabelParentSelector from "@/components/Label/ParentSelector.vue"; + import LabelChip from "@/components/Label/Chip.vue"; definePageMeta({ middleware: ["auth"], @@ -69,12 +71,19 @@ name: "", description: "", color: "", + parentId: null as string | null, + }); + + const { data: allLabels } = useAsyncData("all-labels", async () => { + const { data } = await api.labels.getAll(); + return data || []; }); function openUpdate() { updateData.name = label.value?.name || ""; updateData.description = label.value?.description || ""; updateData.color = ""; + updateData.parentId = label.value?.parent?.id || null; openDialog(DialogID.UpdateLabel); } @@ -151,6 +160,7 @@ :show-hex="true" :starting-color="label.color" /> + @@ -204,6 +214,25 @@ + + +
+ +
+
+ {{ $t("labels.parent_label") }}: +
+ +
+
+
+ {{ $t("labels.child_labels") }}: +
+ +
+
+
+