diff --git a/backend/internal/data/repo/repo_items.go b/backend/internal/data/repo/repo_items.go index dd11b356..571eb916 100644 --- a/backend/internal/data/repo/repo_items.go +++ b/backend/internal/data/repo/repo_items.go @@ -579,6 +579,10 @@ func (e *ItemsRepository) Create(ctx context.Context, gid uuid.UUID, data ItemCr SetLocationID(data.LocationID). SetAssetID(int(data.AssetID)) + if data.ParentID != uuid.Nil { + q.SetParentID(data.ParentID) + } + if len(data.LabelIDs) > 0 { q.AddLabelIDs(data.LabelIDs...) } diff --git a/frontend/components/Item/CreateModal.vue b/frontend/components/Item/CreateModal.vue index 191e3919..136ee082 100644 --- a/frontend/components/Item/CreateModal.vue +++ b/frontend/components/Item/CreateModal.vue @@ -2,6 +2,15 @@
+ labelStore.labels); const route = useRoute(); + const router = useRouter(); + + const parent = ref(); + const { query, results } = useItemSearch(api, { immediate: false }); + const subItemCreateParam = useRouteQuery("subItemCreate", "n"); + const subItemCreate = ref(); const labelId = computed(() => { if (route.fullPath.includes("/label/")) { @@ -175,12 +191,20 @@ return null; }); + const itemId = computed(() => { + if (route.fullPath.includes("/item/")) { + return route.params.id; + } + return null; + }); + const nameInput = ref(null); const loading = ref(false); const focused = ref(false); const form = reactive({ location: locations.value && locations.value.length > 0 ? locations.value[0] : ({} as LocationOut), + parentId: null, name: "", quantity: 1, description: "", @@ -189,6 +213,18 @@ photos: [] as PhotoPreview[], }); + watch( + parent, + newParent => { + if (newParent && newParent.id && subItemCreate.value) { + form.parentId = newParent.id; + } else { + form.parentId = null; + } + }, + { immediate: true } + ); + const { shift } = useMagicKeys(); function deleteImage(index: number) { @@ -223,10 +259,43 @@ watch( () => activeDialog.value, - active => { + async active => { if (active === "create-item") { - if (locationId.value) { - const found = locations.value.find(l => l.id === locationId.value); + // needed since URL will be cleared in the next step => ParentId Selection should stay though + subItemCreate.value = subItemCreateParam.value === "y"; + let parentItemLocationId = null; + + if (subItemCreate.value && itemId.value) { + const itemIdRead = typeof itemId.value === "string" ? (itemId.value as string) : itemId.value[0]; + const { data, error } = await api.items.get(itemIdRead); + if (error || !data) { + toast.error("Failed to load parent item - please select manually"); + console.error("Parent item fetch error:", error); + } + + if (data) { + parent.value = data; + } + + if (data.location) { + const { location } = data; + parentItemLocationId = location.id; + } + + // clear URL Parameter (subItemCreate) since intention was communicated and received + const currentQuery = { ...route.query }; + delete currentQuery.subItemCreate; + await router.push({ query: currentQuery }); + } else { + // since Input is hidden in this case, make sure no accidental parent information is sent out + parent.value = {}; + form.parentId = null; + } + + const locId = locationId.value ? locationId.value : parentItemLocationId; + + if (locId) { + const found = locations.value.find(l => l.id === locId); if (found) { form.location = found; } @@ -254,7 +323,7 @@ if (shift.value) close = false; const out: ItemCreate = { - parentId: null, + parentId: form.parentId, name: form.name, quantity: form.quantity, description: form.description, diff --git a/frontend/locales/de.json b/frontend/locales/de.json index 2c9d5a51..63131d72 100644 --- a/frontend/locales/de.json +++ b/frontend/locales/de.json @@ -67,6 +67,7 @@ "item_name": "Gegenstandsname", "item_photo": "Artikel Bild", "item_quantity": "Artikel Menge", + "parent_item" :"Übergeordneter Gegenstand", "title": "Gegenstand erstellen", "upload_photos": "Upload Bilder" }, @@ -125,6 +126,7 @@ "create": "Erstellen", "create_and_add": "Erstellen und weiteren hinzufügen", "created": "Erstellt", + "create_subitem": "Sub-Gegenstand erstellen", "delete": "Löschen", "details": "Details", "duplicate": "Duplizieren", @@ -214,7 +216,7 @@ "options": "Optionen", "order_by": "Sortieren nach", "pages": "Seite { page } von { totalPages }", - "parent_item": "Übergeordnetes Element", + "parent_item": "Übergeordneter Gegenstand", "photo": "Foto", "photos": "Fotos", "prev_page": "Vorherige Seite", diff --git a/frontend/locales/en.json b/frontend/locales/en.json index 96271fd9..f07fc0e8 100644 --- a/frontend/locales/en.json +++ b/frontend/locales/en.json @@ -67,6 +67,7 @@ "item_name": "Item Name", "item_photo": "Item Photo 📷", "item_quantity": "Item Quantity", + "parent_item" :"Parent Item", "title": "Create Item", "upload_photos": "Upload Photos" }, @@ -125,6 +126,7 @@ "create": "Create", "create_and_add": "Create and Add Another", "created": "Created", + "create_subitem": "Create Subitem", "delete": "Delete", "details": "Details", "duplicate": "Duplicate", diff --git a/frontend/pages/item/[id]/index.vue b/frontend/pages/item/[id]/index.vue index 1fd38b09..0a9bad31 100644 --- a/frontend/pages/item/[id]/index.vue +++ b/frontend/pages/item/[id]/index.vue @@ -32,6 +32,7 @@ }); const route = useRoute(); + const router = useRouter(); const api = useUserApi(); const itemId = computed(() => route.params.id as string); @@ -509,6 +510,17 @@ toast.success("Item deleted"); navigateTo("/home"); } + + async function createSubitem() { + // setting URL Parameter that is read and immidiately removed in the Item-CreateModal + await router.push({ + query: { + subItemCreate: "y", + }, + }); + + openDialog("create-item"); + }