From 8463b702290f2e70efcbd77d6d8b7f90487cd39a Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sun, 14 Dec 2025 02:16:49 +0000
Subject: [PATCH] Add frontend support for label parent/child relationships
Co-authored-by: tankerkiller125 <3457368+tankerkiller125@users.noreply.github.com>
---
frontend/components/Label/Chip.vue | 9 +++++
frontend/components/Label/CreateModal.vue | 17 ++++++++-
frontend/components/Label/ParentSelector.vue | 40 ++++++++++++++++++++
frontend/pages/label/[id].vue | 29 ++++++++++++++
4 files changed, 94 insertions(+), 1 deletion(-)
create mode 100644 frontend/components/Label/ParentSelector.vue
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;
+ };
@@ -42,6 +47,10 @@
+
+ {{ label.parent.name }}
+ /
+
{{ label.name }}
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") }}:
+
+
+
+
+
+