mirror of
https://github.com/sysadminsmedia/homebox.git
synced 2025-12-21 13:23:14 +01:00
Compare commits
16 Commits
v0.22.0-rc
...
copilot/im
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1942b59eb3 | ||
|
|
bc7ca76ab5 | ||
|
|
8463b70229 | ||
|
|
a23e0f5909 | ||
|
|
5da734c8f1 | ||
|
|
422faffbe0 | ||
|
|
f6c0dc783c | ||
|
|
e007b12a2d | ||
|
|
2d1d3d927b | ||
|
|
540028a22e | ||
|
|
14b0d51894 | ||
|
|
4334f926c0 | ||
|
|
1088972ff0 | ||
|
|
55e247ac71 | ||
|
|
05a2700718 | ||
|
|
06c11cdcd5 |
@@ -243,6 +243,6 @@ jobs:
|
||||
uses: actions/attest-build-provenance@v1
|
||||
if: (github.event_name == 'schedule' || startsWith(github.ref, 'refs/tags/'))
|
||||
with:
|
||||
subject-name: ${{ env.DOCKERHUB_REPO }}
|
||||
subject-name: docker.io/${{ env.DOCKERHUB_REPO }}
|
||||
subject-digest: ${{ steps.push-dockerhub.outputs.digest }}
|
||||
push-to-registry: true
|
||||
|
||||
@@ -245,6 +245,6 @@ jobs:
|
||||
uses: actions/attest-build-provenance@v1
|
||||
if: (github.event_name == 'schedule' || startsWith(github.ref, 'refs/tags/'))
|
||||
with:
|
||||
subject-name: ${{ env.DOCKERHUB_REPO }}
|
||||
subject-name: docker.io/${{ env.DOCKERHUB_REPO }}
|
||||
subject-digest: ${{ steps.push-dockerhub.outputs.digest }}
|
||||
push-to-registry: true
|
||||
|
||||
2
.github/workflows/docker-publish.yaml
vendored
2
.github/workflows/docker-publish.yaml
vendored
@@ -236,6 +236,6 @@ jobs:
|
||||
uses: actions/attest-build-provenance@v1
|
||||
if: (github.event_name == 'schedule' || startsWith(github.ref, 'refs/tags/'))
|
||||
with:
|
||||
subject-name: ${{ env.DOCKERHUB_REPO }}
|
||||
subject-name: docker.io/${{ env.DOCKERHUB_REPO }}
|
||||
subject-digest: ${{ steps.push-dockerhub.outputs.digest }}
|
||||
push-to-registry: true
|
||||
|
||||
14
.scaffold/go.sum
Normal file
14
.scaffold/go.sum
Normal file
@@ -0,0 +1,14 @@
|
||||
entgo.io/ent v0.14.5 h1:Rj2WOYJtCkWyFo6a+5wB3EfBRP0rnx1fMk6gGA0UUe4=
|
||||
entgo.io/ent v0.14.5/go.mod h1:zTzLmWtPvGpmSwtkaayM2cm5m819NdM7z7tYPq3vN0U=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/sysadminsmedia/homebox/backend v0.0.0-20251212183312-2d1d3d927bfd h1:QULUJSgHc4rSlTjb2qYT6FIgwDWFCqEpnYqc/ltsrkk=
|
||||
github.com/sysadminsmedia/homebox/backend v0.0.0-20251212183312-2d1d3d927bfd/go.mod h1:jB+tPmHtPDM1VnAjah0gvcRfP/s7c+rtQwpA8cvZD/U=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
@@ -3216,6 +3216,13 @@ const docTemplate = `{
|
||||
"ent.LabelEdges": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"children": {
|
||||
"description": "Children holds the value of the children edge.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/ent.Label"
|
||||
}
|
||||
},
|
||||
"group": {
|
||||
"description": "Group holds the value of the group edge.",
|
||||
"allOf": [
|
||||
@@ -3230,6 +3237,14 @@ const docTemplate = `{
|
||||
"items": {
|
||||
"$ref": "#/definitions/ent.Item"
|
||||
}
|
||||
},
|
||||
"parent": {
|
||||
"description": "Parent holds the value of the parent edge.",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ent.Label"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -4437,12 +4452,22 @@ const docTemplate = `{
|
||||
"type": "string",
|
||||
"maxLength": 255,
|
||||
"minLength": 1
|
||||
},
|
||||
"parentId": {
|
||||
"type": "string",
|
||||
"x-nullable": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"repo.LabelOut": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"children": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/repo.LabelSummary"
|
||||
}
|
||||
},
|
||||
"color": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -4458,6 +4483,9 @@ const docTemplate = `{
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"parent": {
|
||||
"$ref": "#/definitions/repo.LabelSummary"
|
||||
},
|
||||
"updatedAt": {
|
||||
"type": "string"
|
||||
}
|
||||
|
||||
@@ -3413,6 +3413,13 @@
|
||||
"ent.LabelEdges": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"children": {
|
||||
"description": "Children holds the value of the children edge.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/ent.Label"
|
||||
}
|
||||
},
|
||||
"group": {
|
||||
"description": "Group holds the value of the group edge.",
|
||||
"allOf": [
|
||||
@@ -3427,6 +3434,14 @@
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/ent.Item"
|
||||
}
|
||||
},
|
||||
"parent": {
|
||||
"description": "Parent holds the value of the parent edge.",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/ent.Label"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -4634,12 +4649,22 @@
|
||||
"type": "string",
|
||||
"maxLength": 255,
|
||||
"minLength": 1
|
||||
},
|
||||
"parentId": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"repo.LabelOut": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"children": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/repo.LabelSummary"
|
||||
}
|
||||
},
|
||||
"color": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -4655,6 +4680,9 @@
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"parent": {
|
||||
"$ref": "#/components/schemas/repo.LabelSummary"
|
||||
},
|
||||
"updatedAt": {
|
||||
"type": "string"
|
||||
}
|
||||
|
||||
@@ -2112,6 +2112,11 @@ components:
|
||||
ent.LabelEdges:
|
||||
type: object
|
||||
properties:
|
||||
children:
|
||||
description: Children holds the value of the children edge.
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/ent.Label"
|
||||
group:
|
||||
description: Group holds the value of the group edge.
|
||||
allOf:
|
||||
@@ -2121,6 +2126,10 @@ components:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/ent.Item"
|
||||
parent:
|
||||
description: Parent holds the value of the parent edge.
|
||||
allOf:
|
||||
- $ref: "#/components/schemas/ent.Label"
|
||||
ent.Location:
|
||||
type: object
|
||||
properties:
|
||||
@@ -2956,9 +2965,16 @@ components:
|
||||
type: string
|
||||
maxLength: 255
|
||||
minLength: 1
|
||||
parentId:
|
||||
type: string
|
||||
nullable: true
|
||||
repo.LabelOut:
|
||||
type: object
|
||||
properties:
|
||||
children:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/repo.LabelSummary"
|
||||
color:
|
||||
type: string
|
||||
createdAt:
|
||||
@@ -2969,6 +2985,8 @@ components:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
parent:
|
||||
$ref: "#/components/schemas/repo.LabelSummary"
|
||||
updatedAt:
|
||||
type: string
|
||||
repo.LabelSummary:
|
||||
|
||||
@@ -3214,6 +3214,13 @@
|
||||
"ent.LabelEdges": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"children": {
|
||||
"description": "Children holds the value of the children edge.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/ent.Label"
|
||||
}
|
||||
},
|
||||
"group": {
|
||||
"description": "Group holds the value of the group edge.",
|
||||
"allOf": [
|
||||
@@ -3228,6 +3235,14 @@
|
||||
"items": {
|
||||
"$ref": "#/definitions/ent.Item"
|
||||
}
|
||||
},
|
||||
"parent": {
|
||||
"description": "Parent holds the value of the parent edge.",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ent.Label"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -4435,12 +4450,22 @@
|
||||
"type": "string",
|
||||
"maxLength": 255,
|
||||
"minLength": 1
|
||||
},
|
||||
"parentId": {
|
||||
"type": "string",
|
||||
"x-nullable": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"repo.LabelOut": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"children": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/repo.LabelSummary"
|
||||
}
|
||||
},
|
||||
"color": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -4456,6 +4481,9 @@
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"parent": {
|
||||
"$ref": "#/definitions/repo.LabelSummary"
|
||||
},
|
||||
"updatedAt": {
|
||||
"type": "string"
|
||||
}
|
||||
|
||||
@@ -534,6 +534,11 @@ definitions:
|
||||
type: object
|
||||
ent.LabelEdges:
|
||||
properties:
|
||||
children:
|
||||
description: Children holds the value of the children edge.
|
||||
items:
|
||||
$ref: '#/definitions/ent.Label'
|
||||
type: array
|
||||
group:
|
||||
allOf:
|
||||
- $ref: '#/definitions/ent.Group'
|
||||
@@ -543,6 +548,10 @@ definitions:
|
||||
items:
|
||||
$ref: '#/definitions/ent.Item'
|
||||
type: array
|
||||
parent:
|
||||
allOf:
|
||||
- $ref: '#/definitions/ent.Label'
|
||||
description: Parent holds the value of the parent edge.
|
||||
type: object
|
||||
ent.Location:
|
||||
properties:
|
||||
@@ -1371,11 +1380,18 @@ definitions:
|
||||
maxLength: 255
|
||||
minLength: 1
|
||||
type: string
|
||||
parentId:
|
||||
type: string
|
||||
x-nullable: true
|
||||
required:
|
||||
- name
|
||||
type: object
|
||||
repo.LabelOut:
|
||||
properties:
|
||||
children:
|
||||
items:
|
||||
$ref: '#/definitions/repo.LabelSummary'
|
||||
type: array
|
||||
color:
|
||||
type: string
|
||||
createdAt:
|
||||
@@ -1386,6 +1402,8 @@ definitions:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
parent:
|
||||
$ref: '#/definitions/repo.LabelSummary'
|
||||
updatedAt:
|
||||
type: string
|
||||
type: object
|
||||
|
||||
@@ -325,8 +325,6 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/
|
||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mattn/go-sqlite3 v1.14.32 h1:JD12Ag3oLy1zQA+BNn74xRgaBbdhbNIDYvQUEuuErjs=
|
||||
github.com/mattn/go-sqlite3 v1.14.32/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
github.com/mfridman/interpolate v0.0.2 h1:pnuTK7MQIxxFz1Gr+rjSIx9u7qVjf5VOoM/u6BbAxPY=
|
||||
@@ -349,8 +347,6 @@ github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOF
|
||||
github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||
github.com/olahol/melody v1.4.0 h1:Pa5SdeZL/zXPi1tJuMAPDbl4n3gQOThSL6G1p4qZ4SI=
|
||||
github.com/olahol/melody v1.4.0/go.mod h1:GgkTl6Y7yWj/HtfD48Q5vLKPVoZOH+Qqgfa7CvJgJM4=
|
||||
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
|
||||
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
|
||||
github.com/onsi/ginkgo/v2 v2.9.2 h1:BA2GMJOtfGAfagzYtrAlufIP0lq6QERkFmHLMLPwFSU=
|
||||
github.com/onsi/ginkgo/v2 v2.9.2/go.mod h1:WHcJJG2dIlcCqVfBAwUCrJxSPFb6v4azBwgxeMeDuts=
|
||||
github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE=
|
||||
@@ -393,10 +389,6 @@ github.com/shirou/gopsutil/v4 v4.25.11 h1:X53gB7muL9Gnwwo2evPSE+SfOrltMoR6V3xJAX
|
||||
github.com/shirou/gopsutil/v4 v4.25.11/go.mod h1:EivAfP5x2EhLp2ovdpKSozecVXn1TmuG7SMzs/Wh4PU=
|
||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
|
||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
|
||||
github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
|
||||
github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spiffe/go-spiffe/v2 v2.6.0 h1:l+DolpxNWYgruGQVV0xsfeya3CsC7m8iBzDnMpsbLuo=
|
||||
github.com/spiffe/go-spiffe/v2 v2.6.0/go.mod h1:gm2SeUoMZEtpnzPNs2Csc0D/gX33k1xIx7lEzqblHEs=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
|
||||
32
backend/internal/data/ent/client.go
generated
32
backend/internal/data/ent/client.go
generated
@@ -1902,6 +1902,38 @@ func (c *LabelClient) QueryGroup(_m *Label) *GroupQuery {
|
||||
return query
|
||||
}
|
||||
|
||||
// QueryParent queries the parent edge of a Label.
|
||||
func (c *LabelClient) QueryParent(_m *Label) *LabelQuery {
|
||||
query := (&LabelClient{config: c.config}).Query()
|
||||
query.path = func(context.Context) (fromV *sql.Selector, _ error) {
|
||||
id := _m.ID
|
||||
step := sqlgraph.NewStep(
|
||||
sqlgraph.From(label.Table, label.FieldID, id),
|
||||
sqlgraph.To(label.Table, label.FieldID),
|
||||
sqlgraph.Edge(sqlgraph.M2O, true, label.ParentTable, label.ParentColumn),
|
||||
)
|
||||
fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step)
|
||||
return fromV, nil
|
||||
}
|
||||
return query
|
||||
}
|
||||
|
||||
// QueryChildren queries the children edge of a Label.
|
||||
func (c *LabelClient) QueryChildren(_m *Label) *LabelQuery {
|
||||
query := (&LabelClient{config: c.config}).Query()
|
||||
query.path = func(context.Context) (fromV *sql.Selector, _ error) {
|
||||
id := _m.ID
|
||||
step := sqlgraph.NewStep(
|
||||
sqlgraph.From(label.Table, label.FieldID, id),
|
||||
sqlgraph.To(label.Table, label.FieldID),
|
||||
sqlgraph.Edge(sqlgraph.O2M, false, label.ChildrenTable, label.ChildrenColumn),
|
||||
)
|
||||
fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step)
|
||||
return fromV, nil
|
||||
}
|
||||
return query
|
||||
}
|
||||
|
||||
// QueryItems queries the items edge of a Label.
|
||||
func (c *LabelClient) QueryItems(_m *Label) *ItemQuery {
|
||||
query := (&ItemClient{config: c.config}).Query()
|
||||
|
||||
54
backend/internal/data/ent/label.go
generated
54
backend/internal/data/ent/label.go
generated
@@ -31,20 +31,25 @@ type Label struct {
|
||||
Color string `json:"color,omitempty"`
|
||||
// Edges holds the relations/edges for other nodes in the graph.
|
||||
// The values are being populated by the LabelQuery when eager-loading is set.
|
||||
Edges LabelEdges `json:"edges"`
|
||||
group_labels *uuid.UUID
|
||||
selectValues sql.SelectValues
|
||||
Edges LabelEdges `json:"edges"`
|
||||
group_labels *uuid.UUID
|
||||
label_children *uuid.UUID
|
||||
selectValues sql.SelectValues
|
||||
}
|
||||
|
||||
// LabelEdges holds the relations/edges for other nodes in the graph.
|
||||
type LabelEdges struct {
|
||||
// Group holds the value of the group edge.
|
||||
Group *Group `json:"group,omitempty"`
|
||||
// Parent holds the value of the parent edge.
|
||||
Parent *Label `json:"parent,omitempty"`
|
||||
// Children holds the value of the children edge.
|
||||
Children []*Label `json:"children,omitempty"`
|
||||
// Items holds the value of the items edge.
|
||||
Items []*Item `json:"items,omitempty"`
|
||||
// loadedTypes holds the information for reporting if a
|
||||
// type was loaded (or requested) in eager-loading or not.
|
||||
loadedTypes [2]bool
|
||||
loadedTypes [4]bool
|
||||
}
|
||||
|
||||
// GroupOrErr returns the Group value or an error if the edge
|
||||
@@ -58,10 +63,30 @@ func (e LabelEdges) GroupOrErr() (*Group, error) {
|
||||
return nil, &NotLoadedError{edge: "group"}
|
||||
}
|
||||
|
||||
// ParentOrErr returns the Parent value or an error if the edge
|
||||
// was not loaded in eager-loading, or loaded but was not found.
|
||||
func (e LabelEdges) ParentOrErr() (*Label, error) {
|
||||
if e.Parent != nil {
|
||||
return e.Parent, nil
|
||||
} else if e.loadedTypes[1] {
|
||||
return nil, &NotFoundError{label: label.Label}
|
||||
}
|
||||
return nil, &NotLoadedError{edge: "parent"}
|
||||
}
|
||||
|
||||
// ChildrenOrErr returns the Children value or an error if the edge
|
||||
// was not loaded in eager-loading.
|
||||
func (e LabelEdges) ChildrenOrErr() ([]*Label, error) {
|
||||
if e.loadedTypes[2] {
|
||||
return e.Children, nil
|
||||
}
|
||||
return nil, &NotLoadedError{edge: "children"}
|
||||
}
|
||||
|
||||
// ItemsOrErr returns the Items value or an error if the edge
|
||||
// was not loaded in eager-loading.
|
||||
func (e LabelEdges) ItemsOrErr() ([]*Item, error) {
|
||||
if e.loadedTypes[1] {
|
||||
if e.loadedTypes[3] {
|
||||
return e.Items, nil
|
||||
}
|
||||
return nil, &NotLoadedError{edge: "items"}
|
||||
@@ -80,6 +105,8 @@ func (*Label) scanValues(columns []string) ([]any, error) {
|
||||
values[i] = new(uuid.UUID)
|
||||
case label.ForeignKeys[0]: // group_labels
|
||||
values[i] = &sql.NullScanner{S: new(uuid.UUID)}
|
||||
case label.ForeignKeys[1]: // label_children
|
||||
values[i] = &sql.NullScanner{S: new(uuid.UUID)}
|
||||
default:
|
||||
values[i] = new(sql.UnknownType)
|
||||
}
|
||||
@@ -138,6 +165,13 @@ func (_m *Label) assignValues(columns []string, values []any) error {
|
||||
_m.group_labels = new(uuid.UUID)
|
||||
*_m.group_labels = *value.S.(*uuid.UUID)
|
||||
}
|
||||
case label.ForeignKeys[1]:
|
||||
if value, ok := values[i].(*sql.NullScanner); !ok {
|
||||
return fmt.Errorf("unexpected type %T for field label_children", values[i])
|
||||
} else if value.Valid {
|
||||
_m.label_children = new(uuid.UUID)
|
||||
*_m.label_children = *value.S.(*uuid.UUID)
|
||||
}
|
||||
default:
|
||||
_m.selectValues.Set(columns[i], values[i])
|
||||
}
|
||||
@@ -156,6 +190,16 @@ func (_m *Label) QueryGroup() *GroupQuery {
|
||||
return NewLabelClient(_m.config).QueryGroup(_m)
|
||||
}
|
||||
|
||||
// QueryParent queries the "parent" edge of the Label entity.
|
||||
func (_m *Label) QueryParent() *LabelQuery {
|
||||
return NewLabelClient(_m.config).QueryParent(_m)
|
||||
}
|
||||
|
||||
// QueryChildren queries the "children" edge of the Label entity.
|
||||
func (_m *Label) QueryChildren() *LabelQuery {
|
||||
return NewLabelClient(_m.config).QueryChildren(_m)
|
||||
}
|
||||
|
||||
// QueryItems queries the "items" edge of the Label entity.
|
||||
func (_m *Label) QueryItems() *ItemQuery {
|
||||
return NewLabelClient(_m.config).QueryItems(_m)
|
||||
|
||||
48
backend/internal/data/ent/label/label.go
generated
48
backend/internal/data/ent/label/label.go
generated
@@ -27,6 +27,10 @@ const (
|
||||
FieldColor = "color"
|
||||
// EdgeGroup holds the string denoting the group edge name in mutations.
|
||||
EdgeGroup = "group"
|
||||
// EdgeParent holds the string denoting the parent edge name in mutations.
|
||||
EdgeParent = "parent"
|
||||
// EdgeChildren holds the string denoting the children edge name in mutations.
|
||||
EdgeChildren = "children"
|
||||
// EdgeItems holds the string denoting the items edge name in mutations.
|
||||
EdgeItems = "items"
|
||||
// Table holds the table name of the label in the database.
|
||||
@@ -38,6 +42,14 @@ const (
|
||||
GroupInverseTable = "groups"
|
||||
// GroupColumn is the table column denoting the group relation/edge.
|
||||
GroupColumn = "group_labels"
|
||||
// ParentTable is the table that holds the parent relation/edge.
|
||||
ParentTable = "labels"
|
||||
// ParentColumn is the table column denoting the parent relation/edge.
|
||||
ParentColumn = "label_children"
|
||||
// ChildrenTable is the table that holds the children relation/edge.
|
||||
ChildrenTable = "labels"
|
||||
// ChildrenColumn is the table column denoting the children relation/edge.
|
||||
ChildrenColumn = "label_children"
|
||||
// ItemsTable is the table that holds the items relation/edge. The primary key declared below.
|
||||
ItemsTable = "label_items"
|
||||
// ItemsInverseTable is the table name for the Item entity.
|
||||
@@ -59,6 +71,7 @@ var Columns = []string{
|
||||
// table and are not defined as standalone fields in the schema.
|
||||
var ForeignKeys = []string{
|
||||
"group_labels",
|
||||
"label_children",
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -139,6 +152,27 @@ func ByGroupField(field string, opts ...sql.OrderTermOption) OrderOption {
|
||||
}
|
||||
}
|
||||
|
||||
// ByParentField orders the results by parent field.
|
||||
func ByParentField(field string, opts ...sql.OrderTermOption) OrderOption {
|
||||
return func(s *sql.Selector) {
|
||||
sqlgraph.OrderByNeighborTerms(s, newParentStep(), sql.OrderByField(field, opts...))
|
||||
}
|
||||
}
|
||||
|
||||
// ByChildrenCount orders the results by children count.
|
||||
func ByChildrenCount(opts ...sql.OrderTermOption) OrderOption {
|
||||
return func(s *sql.Selector) {
|
||||
sqlgraph.OrderByNeighborsCount(s, newChildrenStep(), opts...)
|
||||
}
|
||||
}
|
||||
|
||||
// ByChildren orders the results by children terms.
|
||||
func ByChildren(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption {
|
||||
return func(s *sql.Selector) {
|
||||
sqlgraph.OrderByNeighborTerms(s, newChildrenStep(), append([]sql.OrderTerm{term}, terms...)...)
|
||||
}
|
||||
}
|
||||
|
||||
// ByItemsCount orders the results by items count.
|
||||
func ByItemsCount(opts ...sql.OrderTermOption) OrderOption {
|
||||
return func(s *sql.Selector) {
|
||||
@@ -159,6 +193,20 @@ func newGroupStep() *sqlgraph.Step {
|
||||
sqlgraph.Edge(sqlgraph.M2O, true, GroupTable, GroupColumn),
|
||||
)
|
||||
}
|
||||
func newParentStep() *sqlgraph.Step {
|
||||
return sqlgraph.NewStep(
|
||||
sqlgraph.From(Table, FieldID),
|
||||
sqlgraph.To(Table, FieldID),
|
||||
sqlgraph.Edge(sqlgraph.M2O, true, ParentTable, ParentColumn),
|
||||
)
|
||||
}
|
||||
func newChildrenStep() *sqlgraph.Step {
|
||||
return sqlgraph.NewStep(
|
||||
sqlgraph.From(Table, FieldID),
|
||||
sqlgraph.To(Table, FieldID),
|
||||
sqlgraph.Edge(sqlgraph.O2M, false, ChildrenTable, ChildrenColumn),
|
||||
)
|
||||
}
|
||||
func newItemsStep() *sqlgraph.Step {
|
||||
return sqlgraph.NewStep(
|
||||
sqlgraph.From(Table, FieldID),
|
||||
|
||||
46
backend/internal/data/ent/label/where.go
generated
46
backend/internal/data/ent/label/where.go
generated
@@ -399,6 +399,52 @@ func HasGroupWith(preds ...predicate.Group) predicate.Label {
|
||||
})
|
||||
}
|
||||
|
||||
// HasParent applies the HasEdge predicate on the "parent" edge.
|
||||
func HasParent() predicate.Label {
|
||||
return predicate.Label(func(s *sql.Selector) {
|
||||
step := sqlgraph.NewStep(
|
||||
sqlgraph.From(Table, FieldID),
|
||||
sqlgraph.Edge(sqlgraph.M2O, true, ParentTable, ParentColumn),
|
||||
)
|
||||
sqlgraph.HasNeighbors(s, step)
|
||||
})
|
||||
}
|
||||
|
||||
// HasParentWith applies the HasEdge predicate on the "parent" edge with a given conditions (other predicates).
|
||||
func HasParentWith(preds ...predicate.Label) predicate.Label {
|
||||
return predicate.Label(func(s *sql.Selector) {
|
||||
step := newParentStep()
|
||||
sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) {
|
||||
for _, p := range preds {
|
||||
p(s)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// HasChildren applies the HasEdge predicate on the "children" edge.
|
||||
func HasChildren() predicate.Label {
|
||||
return predicate.Label(func(s *sql.Selector) {
|
||||
step := sqlgraph.NewStep(
|
||||
sqlgraph.From(Table, FieldID),
|
||||
sqlgraph.Edge(sqlgraph.O2M, false, ChildrenTable, ChildrenColumn),
|
||||
)
|
||||
sqlgraph.HasNeighbors(s, step)
|
||||
})
|
||||
}
|
||||
|
||||
// HasChildrenWith applies the HasEdge predicate on the "children" edge with a given conditions (other predicates).
|
||||
func HasChildrenWith(preds ...predicate.Label) predicate.Label {
|
||||
return predicate.Label(func(s *sql.Selector) {
|
||||
step := newChildrenStep()
|
||||
sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) {
|
||||
for _, p := range preds {
|
||||
p(s)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// HasItems applies the HasEdge predicate on the "items" edge.
|
||||
func HasItems() predicate.Label {
|
||||
return predicate.Label(func(s *sql.Selector) {
|
||||
|
||||
67
backend/internal/data/ent/label_create.go
generated
67
backend/internal/data/ent/label_create.go
generated
@@ -110,6 +110,40 @@ func (_c *LabelCreate) SetGroup(v *Group) *LabelCreate {
|
||||
return _c.SetGroupID(v.ID)
|
||||
}
|
||||
|
||||
// SetParentID sets the "parent" edge to the Label entity by ID.
|
||||
func (_c *LabelCreate) SetParentID(id uuid.UUID) *LabelCreate {
|
||||
_c.mutation.SetParentID(id)
|
||||
return _c
|
||||
}
|
||||
|
||||
// SetNillableParentID sets the "parent" edge to the Label entity by ID if the given value is not nil.
|
||||
func (_c *LabelCreate) SetNillableParentID(id *uuid.UUID) *LabelCreate {
|
||||
if id != nil {
|
||||
_c = _c.SetParentID(*id)
|
||||
}
|
||||
return _c
|
||||
}
|
||||
|
||||
// SetParent sets the "parent" edge to the Label entity.
|
||||
func (_c *LabelCreate) SetParent(v *Label) *LabelCreate {
|
||||
return _c.SetParentID(v.ID)
|
||||
}
|
||||
|
||||
// AddChildIDs adds the "children" edge to the Label entity by IDs.
|
||||
func (_c *LabelCreate) AddChildIDs(ids ...uuid.UUID) *LabelCreate {
|
||||
_c.mutation.AddChildIDs(ids...)
|
||||
return _c
|
||||
}
|
||||
|
||||
// AddChildren adds the "children" edges to the Label entity.
|
||||
func (_c *LabelCreate) AddChildren(v ...*Label) *LabelCreate {
|
||||
ids := make([]uuid.UUID, len(v))
|
||||
for i := range v {
|
||||
ids[i] = v[i].ID
|
||||
}
|
||||
return _c.AddChildIDs(ids...)
|
||||
}
|
||||
|
||||
// AddItemIDs adds the "items" edge to the Item entity by IDs.
|
||||
func (_c *LabelCreate) AddItemIDs(ids ...uuid.UUID) *LabelCreate {
|
||||
_c.mutation.AddItemIDs(ids...)
|
||||
@@ -275,6 +309,39 @@ func (_c *LabelCreate) createSpec() (*Label, *sqlgraph.CreateSpec) {
|
||||
_node.group_labels = &nodes[0]
|
||||
_spec.Edges = append(_spec.Edges, edge)
|
||||
}
|
||||
if nodes := _c.mutation.ParentIDs(); len(nodes) > 0 {
|
||||
edge := &sqlgraph.EdgeSpec{
|
||||
Rel: sqlgraph.M2O,
|
||||
Inverse: true,
|
||||
Table: label.ParentTable,
|
||||
Columns: []string{label.ParentColumn},
|
||||
Bidi: false,
|
||||
Target: &sqlgraph.EdgeTarget{
|
||||
IDSpec: sqlgraph.NewFieldSpec(label.FieldID, field.TypeUUID),
|
||||
},
|
||||
}
|
||||
for _, k := range nodes {
|
||||
edge.Target.Nodes = append(edge.Target.Nodes, k)
|
||||
}
|
||||
_node.label_children = &nodes[0]
|
||||
_spec.Edges = append(_spec.Edges, edge)
|
||||
}
|
||||
if nodes := _c.mutation.ChildrenIDs(); len(nodes) > 0 {
|
||||
edge := &sqlgraph.EdgeSpec{
|
||||
Rel: sqlgraph.O2M,
|
||||
Inverse: false,
|
||||
Table: label.ChildrenTable,
|
||||
Columns: []string{label.ChildrenColumn},
|
||||
Bidi: false,
|
||||
Target: &sqlgraph.EdgeTarget{
|
||||
IDSpec: sqlgraph.NewFieldSpec(label.FieldID, field.TypeUUID),
|
||||
},
|
||||
}
|
||||
for _, k := range nodes {
|
||||
edge.Target.Nodes = append(edge.Target.Nodes, k)
|
||||
}
|
||||
_spec.Edges = append(_spec.Edges, edge)
|
||||
}
|
||||
if nodes := _c.mutation.ItemsIDs(); len(nodes) > 0 {
|
||||
edge := &sqlgraph.EdgeSpec{
|
||||
Rel: sqlgraph.M2M,
|
||||
|
||||
180
backend/internal/data/ent/label_query.go
generated
180
backend/internal/data/ent/label_query.go
generated
@@ -22,13 +22,15 @@ import (
|
||||
// LabelQuery is the builder for querying Label entities.
|
||||
type LabelQuery struct {
|
||||
config
|
||||
ctx *QueryContext
|
||||
order []label.OrderOption
|
||||
inters []Interceptor
|
||||
predicates []predicate.Label
|
||||
withGroup *GroupQuery
|
||||
withItems *ItemQuery
|
||||
withFKs bool
|
||||
ctx *QueryContext
|
||||
order []label.OrderOption
|
||||
inters []Interceptor
|
||||
predicates []predicate.Label
|
||||
withGroup *GroupQuery
|
||||
withParent *LabelQuery
|
||||
withChildren *LabelQuery
|
||||
withItems *ItemQuery
|
||||
withFKs bool
|
||||
// intermediate query (i.e. traversal path).
|
||||
sql *sql.Selector
|
||||
path func(context.Context) (*sql.Selector, error)
|
||||
@@ -87,6 +89,50 @@ func (_q *LabelQuery) QueryGroup() *GroupQuery {
|
||||
return query
|
||||
}
|
||||
|
||||
// QueryParent chains the current query on the "parent" edge.
|
||||
func (_q *LabelQuery) QueryParent() *LabelQuery {
|
||||
query := (&LabelClient{config: _q.config}).Query()
|
||||
query.path = func(ctx context.Context) (fromU *sql.Selector, err error) {
|
||||
if err := _q.prepareQuery(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
selector := _q.sqlQuery(ctx)
|
||||
if err := selector.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
step := sqlgraph.NewStep(
|
||||
sqlgraph.From(label.Table, label.FieldID, selector),
|
||||
sqlgraph.To(label.Table, label.FieldID),
|
||||
sqlgraph.Edge(sqlgraph.M2O, true, label.ParentTable, label.ParentColumn),
|
||||
)
|
||||
fromU = sqlgraph.SetNeighbors(_q.driver.Dialect(), step)
|
||||
return fromU, nil
|
||||
}
|
||||
return query
|
||||
}
|
||||
|
||||
// QueryChildren chains the current query on the "children" edge.
|
||||
func (_q *LabelQuery) QueryChildren() *LabelQuery {
|
||||
query := (&LabelClient{config: _q.config}).Query()
|
||||
query.path = func(ctx context.Context) (fromU *sql.Selector, err error) {
|
||||
if err := _q.prepareQuery(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
selector := _q.sqlQuery(ctx)
|
||||
if err := selector.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
step := sqlgraph.NewStep(
|
||||
sqlgraph.From(label.Table, label.FieldID, selector),
|
||||
sqlgraph.To(label.Table, label.FieldID),
|
||||
sqlgraph.Edge(sqlgraph.O2M, false, label.ChildrenTable, label.ChildrenColumn),
|
||||
)
|
||||
fromU = sqlgraph.SetNeighbors(_q.driver.Dialect(), step)
|
||||
return fromU, nil
|
||||
}
|
||||
return query
|
||||
}
|
||||
|
||||
// QueryItems chains the current query on the "items" edge.
|
||||
func (_q *LabelQuery) QueryItems() *ItemQuery {
|
||||
query := (&ItemClient{config: _q.config}).Query()
|
||||
@@ -296,13 +342,15 @@ func (_q *LabelQuery) Clone() *LabelQuery {
|
||||
return nil
|
||||
}
|
||||
return &LabelQuery{
|
||||
config: _q.config,
|
||||
ctx: _q.ctx.Clone(),
|
||||
order: append([]label.OrderOption{}, _q.order...),
|
||||
inters: append([]Interceptor{}, _q.inters...),
|
||||
predicates: append([]predicate.Label{}, _q.predicates...),
|
||||
withGroup: _q.withGroup.Clone(),
|
||||
withItems: _q.withItems.Clone(),
|
||||
config: _q.config,
|
||||
ctx: _q.ctx.Clone(),
|
||||
order: append([]label.OrderOption{}, _q.order...),
|
||||
inters: append([]Interceptor{}, _q.inters...),
|
||||
predicates: append([]predicate.Label{}, _q.predicates...),
|
||||
withGroup: _q.withGroup.Clone(),
|
||||
withParent: _q.withParent.Clone(),
|
||||
withChildren: _q.withChildren.Clone(),
|
||||
withItems: _q.withItems.Clone(),
|
||||
// clone intermediate query.
|
||||
sql: _q.sql.Clone(),
|
||||
path: _q.path,
|
||||
@@ -320,6 +368,28 @@ func (_q *LabelQuery) WithGroup(opts ...func(*GroupQuery)) *LabelQuery {
|
||||
return _q
|
||||
}
|
||||
|
||||
// WithParent tells the query-builder to eager-load the nodes that are connected to
|
||||
// the "parent" edge. The optional arguments are used to configure the query builder of the edge.
|
||||
func (_q *LabelQuery) WithParent(opts ...func(*LabelQuery)) *LabelQuery {
|
||||
query := (&LabelClient{config: _q.config}).Query()
|
||||
for _, opt := range opts {
|
||||
opt(query)
|
||||
}
|
||||
_q.withParent = query
|
||||
return _q
|
||||
}
|
||||
|
||||
// WithChildren tells the query-builder to eager-load the nodes that are connected to
|
||||
// the "children" edge. The optional arguments are used to configure the query builder of the edge.
|
||||
func (_q *LabelQuery) WithChildren(opts ...func(*LabelQuery)) *LabelQuery {
|
||||
query := (&LabelClient{config: _q.config}).Query()
|
||||
for _, opt := range opts {
|
||||
opt(query)
|
||||
}
|
||||
_q.withChildren = query
|
||||
return _q
|
||||
}
|
||||
|
||||
// WithItems tells the query-builder to eager-load the nodes that are connected to
|
||||
// the "items" edge. The optional arguments are used to configure the query builder of the edge.
|
||||
func (_q *LabelQuery) WithItems(opts ...func(*ItemQuery)) *LabelQuery {
|
||||
@@ -410,12 +480,14 @@ func (_q *LabelQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*Label,
|
||||
nodes = []*Label{}
|
||||
withFKs = _q.withFKs
|
||||
_spec = _q.querySpec()
|
||||
loadedTypes = [2]bool{
|
||||
loadedTypes = [4]bool{
|
||||
_q.withGroup != nil,
|
||||
_q.withParent != nil,
|
||||
_q.withChildren != nil,
|
||||
_q.withItems != nil,
|
||||
}
|
||||
)
|
||||
if _q.withGroup != nil {
|
||||
if _q.withGroup != nil || _q.withParent != nil {
|
||||
withFKs = true
|
||||
}
|
||||
if withFKs {
|
||||
@@ -445,6 +517,19 @@ func (_q *LabelQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*Label,
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if query := _q.withParent; query != nil {
|
||||
if err := _q.loadParent(ctx, query, nodes, nil,
|
||||
func(n *Label, e *Label) { n.Edges.Parent = e }); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if query := _q.withChildren; query != nil {
|
||||
if err := _q.loadChildren(ctx, query, nodes,
|
||||
func(n *Label) { n.Edges.Children = []*Label{} },
|
||||
func(n *Label, e *Label) { n.Edges.Children = append(n.Edges.Children, e) }); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if query := _q.withItems; query != nil {
|
||||
if err := _q.loadItems(ctx, query, nodes,
|
||||
func(n *Label) { n.Edges.Items = []*Item{} },
|
||||
@@ -487,6 +572,69 @@ func (_q *LabelQuery) loadGroup(ctx context.Context, query *GroupQuery, nodes []
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (_q *LabelQuery) loadParent(ctx context.Context, query *LabelQuery, nodes []*Label, init func(*Label), assign func(*Label, *Label)) error {
|
||||
ids := make([]uuid.UUID, 0, len(nodes))
|
||||
nodeids := make(map[uuid.UUID][]*Label)
|
||||
for i := range nodes {
|
||||
if nodes[i].label_children == nil {
|
||||
continue
|
||||
}
|
||||
fk := *nodes[i].label_children
|
||||
if _, ok := nodeids[fk]; !ok {
|
||||
ids = append(ids, fk)
|
||||
}
|
||||
nodeids[fk] = append(nodeids[fk], nodes[i])
|
||||
}
|
||||
if len(ids) == 0 {
|
||||
return nil
|
||||
}
|
||||
query.Where(label.IDIn(ids...))
|
||||
neighbors, err := query.All(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, n := range neighbors {
|
||||
nodes, ok := nodeids[n.ID]
|
||||
if !ok {
|
||||
return fmt.Errorf(`unexpected foreign-key "label_children" returned %v`, n.ID)
|
||||
}
|
||||
for i := range nodes {
|
||||
assign(nodes[i], n)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (_q *LabelQuery) loadChildren(ctx context.Context, query *LabelQuery, nodes []*Label, init func(*Label), assign func(*Label, *Label)) error {
|
||||
fks := make([]driver.Value, 0, len(nodes))
|
||||
nodeids := make(map[uuid.UUID]*Label)
|
||||
for i := range nodes {
|
||||
fks = append(fks, nodes[i].ID)
|
||||
nodeids[nodes[i].ID] = nodes[i]
|
||||
if init != nil {
|
||||
init(nodes[i])
|
||||
}
|
||||
}
|
||||
query.withFKs = true
|
||||
query.Where(predicate.Label(func(s *sql.Selector) {
|
||||
s.Where(sql.InValues(s.C(label.ChildrenColumn), fks...))
|
||||
}))
|
||||
neighbors, err := query.All(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, n := range neighbors {
|
||||
fk := n.label_children
|
||||
if fk == nil {
|
||||
return fmt.Errorf(`foreign-key "label_children" is nil for node %v`, n.ID)
|
||||
}
|
||||
node, ok := nodeids[*fk]
|
||||
if !ok {
|
||||
return fmt.Errorf(`unexpected referenced foreign-key "label_children" returned %v for node %v`, *fk, n.ID)
|
||||
}
|
||||
assign(node, n)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (_q *LabelQuery) loadItems(ctx context.Context, query *ItemQuery, nodes []*Label, init func(*Label), assign func(*Label, *Item)) error {
|
||||
edgeIDs := make([]driver.Value, len(nodes))
|
||||
byID := make(map[uuid.UUID]*Label)
|
||||
|
||||
270
backend/internal/data/ent/label_update.go
generated
270
backend/internal/data/ent/label_update.go
generated
@@ -102,6 +102,40 @@ func (_u *LabelUpdate) SetGroup(v *Group) *LabelUpdate {
|
||||
return _u.SetGroupID(v.ID)
|
||||
}
|
||||
|
||||
// SetParentID sets the "parent" edge to the Label entity by ID.
|
||||
func (_u *LabelUpdate) SetParentID(id uuid.UUID) *LabelUpdate {
|
||||
_u.mutation.SetParentID(id)
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetNillableParentID sets the "parent" edge to the Label entity by ID if the given value is not nil.
|
||||
func (_u *LabelUpdate) SetNillableParentID(id *uuid.UUID) *LabelUpdate {
|
||||
if id != nil {
|
||||
_u = _u.SetParentID(*id)
|
||||
}
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetParent sets the "parent" edge to the Label entity.
|
||||
func (_u *LabelUpdate) SetParent(v *Label) *LabelUpdate {
|
||||
return _u.SetParentID(v.ID)
|
||||
}
|
||||
|
||||
// AddChildIDs adds the "children" edge to the Label entity by IDs.
|
||||
func (_u *LabelUpdate) AddChildIDs(ids ...uuid.UUID) *LabelUpdate {
|
||||
_u.mutation.AddChildIDs(ids...)
|
||||
return _u
|
||||
}
|
||||
|
||||
// AddChildren adds the "children" edges to the Label entity.
|
||||
func (_u *LabelUpdate) AddChildren(v ...*Label) *LabelUpdate {
|
||||
ids := make([]uuid.UUID, len(v))
|
||||
for i := range v {
|
||||
ids[i] = v[i].ID
|
||||
}
|
||||
return _u.AddChildIDs(ids...)
|
||||
}
|
||||
|
||||
// AddItemIDs adds the "items" edge to the Item entity by IDs.
|
||||
func (_u *LabelUpdate) AddItemIDs(ids ...uuid.UUID) *LabelUpdate {
|
||||
_u.mutation.AddItemIDs(ids...)
|
||||
@@ -128,6 +162,33 @@ func (_u *LabelUpdate) ClearGroup() *LabelUpdate {
|
||||
return _u
|
||||
}
|
||||
|
||||
// ClearParent clears the "parent" edge to the Label entity.
|
||||
func (_u *LabelUpdate) ClearParent() *LabelUpdate {
|
||||
_u.mutation.ClearParent()
|
||||
return _u
|
||||
}
|
||||
|
||||
// ClearChildren clears all "children" edges to the Label entity.
|
||||
func (_u *LabelUpdate) ClearChildren() *LabelUpdate {
|
||||
_u.mutation.ClearChildren()
|
||||
return _u
|
||||
}
|
||||
|
||||
// RemoveChildIDs removes the "children" edge to Label entities by IDs.
|
||||
func (_u *LabelUpdate) RemoveChildIDs(ids ...uuid.UUID) *LabelUpdate {
|
||||
_u.mutation.RemoveChildIDs(ids...)
|
||||
return _u
|
||||
}
|
||||
|
||||
// RemoveChildren removes "children" edges to Label entities.
|
||||
func (_u *LabelUpdate) RemoveChildren(v ...*Label) *LabelUpdate {
|
||||
ids := make([]uuid.UUID, len(v))
|
||||
for i := range v {
|
||||
ids[i] = v[i].ID
|
||||
}
|
||||
return _u.RemoveChildIDs(ids...)
|
||||
}
|
||||
|
||||
// ClearItems clears all "items" edges to the Item entity.
|
||||
func (_u *LabelUpdate) ClearItems() *LabelUpdate {
|
||||
_u.mutation.ClearItems()
|
||||
@@ -267,6 +328,80 @@ func (_u *LabelUpdate) sqlSave(ctx context.Context) (_node int, err error) {
|
||||
}
|
||||
_spec.Edges.Add = append(_spec.Edges.Add, edge)
|
||||
}
|
||||
if _u.mutation.ParentCleared() {
|
||||
edge := &sqlgraph.EdgeSpec{
|
||||
Rel: sqlgraph.M2O,
|
||||
Inverse: true,
|
||||
Table: label.ParentTable,
|
||||
Columns: []string{label.ParentColumn},
|
||||
Bidi: false,
|
||||
Target: &sqlgraph.EdgeTarget{
|
||||
IDSpec: sqlgraph.NewFieldSpec(label.FieldID, field.TypeUUID),
|
||||
},
|
||||
}
|
||||
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
|
||||
}
|
||||
if nodes := _u.mutation.ParentIDs(); len(nodes) > 0 {
|
||||
edge := &sqlgraph.EdgeSpec{
|
||||
Rel: sqlgraph.M2O,
|
||||
Inverse: true,
|
||||
Table: label.ParentTable,
|
||||
Columns: []string{label.ParentColumn},
|
||||
Bidi: false,
|
||||
Target: &sqlgraph.EdgeTarget{
|
||||
IDSpec: sqlgraph.NewFieldSpec(label.FieldID, field.TypeUUID),
|
||||
},
|
||||
}
|
||||
for _, k := range nodes {
|
||||
edge.Target.Nodes = append(edge.Target.Nodes, k)
|
||||
}
|
||||
_spec.Edges.Add = append(_spec.Edges.Add, edge)
|
||||
}
|
||||
if _u.mutation.ChildrenCleared() {
|
||||
edge := &sqlgraph.EdgeSpec{
|
||||
Rel: sqlgraph.O2M,
|
||||
Inverse: false,
|
||||
Table: label.ChildrenTable,
|
||||
Columns: []string{label.ChildrenColumn},
|
||||
Bidi: false,
|
||||
Target: &sqlgraph.EdgeTarget{
|
||||
IDSpec: sqlgraph.NewFieldSpec(label.FieldID, field.TypeUUID),
|
||||
},
|
||||
}
|
||||
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
|
||||
}
|
||||
if nodes := _u.mutation.RemovedChildrenIDs(); len(nodes) > 0 && !_u.mutation.ChildrenCleared() {
|
||||
edge := &sqlgraph.EdgeSpec{
|
||||
Rel: sqlgraph.O2M,
|
||||
Inverse: false,
|
||||
Table: label.ChildrenTable,
|
||||
Columns: []string{label.ChildrenColumn},
|
||||
Bidi: false,
|
||||
Target: &sqlgraph.EdgeTarget{
|
||||
IDSpec: sqlgraph.NewFieldSpec(label.FieldID, field.TypeUUID),
|
||||
},
|
||||
}
|
||||
for _, k := range nodes {
|
||||
edge.Target.Nodes = append(edge.Target.Nodes, k)
|
||||
}
|
||||
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
|
||||
}
|
||||
if nodes := _u.mutation.ChildrenIDs(); len(nodes) > 0 {
|
||||
edge := &sqlgraph.EdgeSpec{
|
||||
Rel: sqlgraph.O2M,
|
||||
Inverse: false,
|
||||
Table: label.ChildrenTable,
|
||||
Columns: []string{label.ChildrenColumn},
|
||||
Bidi: false,
|
||||
Target: &sqlgraph.EdgeTarget{
|
||||
IDSpec: sqlgraph.NewFieldSpec(label.FieldID, field.TypeUUID),
|
||||
},
|
||||
}
|
||||
for _, k := range nodes {
|
||||
edge.Target.Nodes = append(edge.Target.Nodes, k)
|
||||
}
|
||||
_spec.Edges.Add = append(_spec.Edges.Add, edge)
|
||||
}
|
||||
if _u.mutation.ItemsCleared() {
|
||||
edge := &sqlgraph.EdgeSpec{
|
||||
Rel: sqlgraph.M2M,
|
||||
@@ -403,6 +538,40 @@ func (_u *LabelUpdateOne) SetGroup(v *Group) *LabelUpdateOne {
|
||||
return _u.SetGroupID(v.ID)
|
||||
}
|
||||
|
||||
// SetParentID sets the "parent" edge to the Label entity by ID.
|
||||
func (_u *LabelUpdateOne) SetParentID(id uuid.UUID) *LabelUpdateOne {
|
||||
_u.mutation.SetParentID(id)
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetNillableParentID sets the "parent" edge to the Label entity by ID if the given value is not nil.
|
||||
func (_u *LabelUpdateOne) SetNillableParentID(id *uuid.UUID) *LabelUpdateOne {
|
||||
if id != nil {
|
||||
_u = _u.SetParentID(*id)
|
||||
}
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetParent sets the "parent" edge to the Label entity.
|
||||
func (_u *LabelUpdateOne) SetParent(v *Label) *LabelUpdateOne {
|
||||
return _u.SetParentID(v.ID)
|
||||
}
|
||||
|
||||
// AddChildIDs adds the "children" edge to the Label entity by IDs.
|
||||
func (_u *LabelUpdateOne) AddChildIDs(ids ...uuid.UUID) *LabelUpdateOne {
|
||||
_u.mutation.AddChildIDs(ids...)
|
||||
return _u
|
||||
}
|
||||
|
||||
// AddChildren adds the "children" edges to the Label entity.
|
||||
func (_u *LabelUpdateOne) AddChildren(v ...*Label) *LabelUpdateOne {
|
||||
ids := make([]uuid.UUID, len(v))
|
||||
for i := range v {
|
||||
ids[i] = v[i].ID
|
||||
}
|
||||
return _u.AddChildIDs(ids...)
|
||||
}
|
||||
|
||||
// AddItemIDs adds the "items" edge to the Item entity by IDs.
|
||||
func (_u *LabelUpdateOne) AddItemIDs(ids ...uuid.UUID) *LabelUpdateOne {
|
||||
_u.mutation.AddItemIDs(ids...)
|
||||
@@ -429,6 +598,33 @@ func (_u *LabelUpdateOne) ClearGroup() *LabelUpdateOne {
|
||||
return _u
|
||||
}
|
||||
|
||||
// ClearParent clears the "parent" edge to the Label entity.
|
||||
func (_u *LabelUpdateOne) ClearParent() *LabelUpdateOne {
|
||||
_u.mutation.ClearParent()
|
||||
return _u
|
||||
}
|
||||
|
||||
// ClearChildren clears all "children" edges to the Label entity.
|
||||
func (_u *LabelUpdateOne) ClearChildren() *LabelUpdateOne {
|
||||
_u.mutation.ClearChildren()
|
||||
return _u
|
||||
}
|
||||
|
||||
// RemoveChildIDs removes the "children" edge to Label entities by IDs.
|
||||
func (_u *LabelUpdateOne) RemoveChildIDs(ids ...uuid.UUID) *LabelUpdateOne {
|
||||
_u.mutation.RemoveChildIDs(ids...)
|
||||
return _u
|
||||
}
|
||||
|
||||
// RemoveChildren removes "children" edges to Label entities.
|
||||
func (_u *LabelUpdateOne) RemoveChildren(v ...*Label) *LabelUpdateOne {
|
||||
ids := make([]uuid.UUID, len(v))
|
||||
for i := range v {
|
||||
ids[i] = v[i].ID
|
||||
}
|
||||
return _u.RemoveChildIDs(ids...)
|
||||
}
|
||||
|
||||
// ClearItems clears all "items" edges to the Item entity.
|
||||
func (_u *LabelUpdateOne) ClearItems() *LabelUpdateOne {
|
||||
_u.mutation.ClearItems()
|
||||
@@ -598,6 +794,80 @@ func (_u *LabelUpdateOne) sqlSave(ctx context.Context) (_node *Label, err error)
|
||||
}
|
||||
_spec.Edges.Add = append(_spec.Edges.Add, edge)
|
||||
}
|
||||
if _u.mutation.ParentCleared() {
|
||||
edge := &sqlgraph.EdgeSpec{
|
||||
Rel: sqlgraph.M2O,
|
||||
Inverse: true,
|
||||
Table: label.ParentTable,
|
||||
Columns: []string{label.ParentColumn},
|
||||
Bidi: false,
|
||||
Target: &sqlgraph.EdgeTarget{
|
||||
IDSpec: sqlgraph.NewFieldSpec(label.FieldID, field.TypeUUID),
|
||||
},
|
||||
}
|
||||
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
|
||||
}
|
||||
if nodes := _u.mutation.ParentIDs(); len(nodes) > 0 {
|
||||
edge := &sqlgraph.EdgeSpec{
|
||||
Rel: sqlgraph.M2O,
|
||||
Inverse: true,
|
||||
Table: label.ParentTable,
|
||||
Columns: []string{label.ParentColumn},
|
||||
Bidi: false,
|
||||
Target: &sqlgraph.EdgeTarget{
|
||||
IDSpec: sqlgraph.NewFieldSpec(label.FieldID, field.TypeUUID),
|
||||
},
|
||||
}
|
||||
for _, k := range nodes {
|
||||
edge.Target.Nodes = append(edge.Target.Nodes, k)
|
||||
}
|
||||
_spec.Edges.Add = append(_spec.Edges.Add, edge)
|
||||
}
|
||||
if _u.mutation.ChildrenCleared() {
|
||||
edge := &sqlgraph.EdgeSpec{
|
||||
Rel: sqlgraph.O2M,
|
||||
Inverse: false,
|
||||
Table: label.ChildrenTable,
|
||||
Columns: []string{label.ChildrenColumn},
|
||||
Bidi: false,
|
||||
Target: &sqlgraph.EdgeTarget{
|
||||
IDSpec: sqlgraph.NewFieldSpec(label.FieldID, field.TypeUUID),
|
||||
},
|
||||
}
|
||||
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
|
||||
}
|
||||
if nodes := _u.mutation.RemovedChildrenIDs(); len(nodes) > 0 && !_u.mutation.ChildrenCleared() {
|
||||
edge := &sqlgraph.EdgeSpec{
|
||||
Rel: sqlgraph.O2M,
|
||||
Inverse: false,
|
||||
Table: label.ChildrenTable,
|
||||
Columns: []string{label.ChildrenColumn},
|
||||
Bidi: false,
|
||||
Target: &sqlgraph.EdgeTarget{
|
||||
IDSpec: sqlgraph.NewFieldSpec(label.FieldID, field.TypeUUID),
|
||||
},
|
||||
}
|
||||
for _, k := range nodes {
|
||||
edge.Target.Nodes = append(edge.Target.Nodes, k)
|
||||
}
|
||||
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
|
||||
}
|
||||
if nodes := _u.mutation.ChildrenIDs(); len(nodes) > 0 {
|
||||
edge := &sqlgraph.EdgeSpec{
|
||||
Rel: sqlgraph.O2M,
|
||||
Inverse: false,
|
||||
Table: label.ChildrenTable,
|
||||
Columns: []string{label.ChildrenColumn},
|
||||
Bidi: false,
|
||||
Target: &sqlgraph.EdgeTarget{
|
||||
IDSpec: sqlgraph.NewFieldSpec(label.FieldID, field.TypeUUID),
|
||||
},
|
||||
}
|
||||
for _, k := range nodes {
|
||||
edge.Target.Nodes = append(edge.Target.Nodes, k)
|
||||
}
|
||||
_spec.Edges.Add = append(_spec.Edges.Add, edge)
|
||||
}
|
||||
if _u.mutation.ItemsCleared() {
|
||||
edge := &sqlgraph.EdgeSpec{
|
||||
Rel: sqlgraph.M2M,
|
||||
|
||||
8
backend/internal/data/ent/migrate/schema.go
generated
8
backend/internal/data/ent/migrate/schema.go
generated
@@ -305,6 +305,7 @@ var (
|
||||
{Name: "description", Type: field.TypeString, Nullable: true, Size: 1000},
|
||||
{Name: "color", Type: field.TypeString, Nullable: true, Size: 255},
|
||||
{Name: "group_labels", Type: field.TypeUUID},
|
||||
{Name: "label_children", Type: field.TypeUUID, Nullable: true},
|
||||
}
|
||||
// LabelsTable holds the schema information for the "labels" table.
|
||||
LabelsTable = &schema.Table{
|
||||
@@ -318,6 +319,12 @@ var (
|
||||
RefColumns: []*schema.Column{GroupsColumns[0]},
|
||||
OnDelete: schema.Cascade,
|
||||
},
|
||||
{
|
||||
Symbol: "labels_labels_children",
|
||||
Columns: []*schema.Column{LabelsColumns[7]},
|
||||
RefColumns: []*schema.Column{LabelsColumns[0]},
|
||||
OnDelete: schema.Cascade,
|
||||
},
|
||||
},
|
||||
}
|
||||
// LocationsColumns holds the columns for the "locations" table.
|
||||
@@ -549,6 +556,7 @@ func init() {
|
||||
ItemTemplatesTable.ForeignKeys[0].RefTable = GroupsTable
|
||||
ItemTemplatesTable.ForeignKeys[1].RefTable = LocationsTable
|
||||
LabelsTable.ForeignKeys[0].RefTable = GroupsTable
|
||||
LabelsTable.ForeignKeys[1].RefTable = LabelsTable
|
||||
LocationsTable.ForeignKeys[0].RefTable = GroupsTable
|
||||
LocationsTable.ForeignKeys[1].RefTable = LocationsTable
|
||||
MaintenanceEntriesTable.ForeignKeys[0].RefTable = ItemsTable
|
||||
|
||||
182
backend/internal/data/ent/mutation.go
generated
182
backend/internal/data/ent/mutation.go
generated
@@ -8692,23 +8692,28 @@ func (m *ItemTemplateMutation) ResetEdge(name string) error {
|
||||
// LabelMutation represents an operation that mutates the Label nodes in the graph.
|
||||
type LabelMutation struct {
|
||||
config
|
||||
op Op
|
||||
typ string
|
||||
id *uuid.UUID
|
||||
created_at *time.Time
|
||||
updated_at *time.Time
|
||||
name *string
|
||||
description *string
|
||||
color *string
|
||||
clearedFields map[string]struct{}
|
||||
group *uuid.UUID
|
||||
clearedgroup bool
|
||||
items map[uuid.UUID]struct{}
|
||||
removeditems map[uuid.UUID]struct{}
|
||||
cleareditems bool
|
||||
done bool
|
||||
oldValue func(context.Context) (*Label, error)
|
||||
predicates []predicate.Label
|
||||
op Op
|
||||
typ string
|
||||
id *uuid.UUID
|
||||
created_at *time.Time
|
||||
updated_at *time.Time
|
||||
name *string
|
||||
description *string
|
||||
color *string
|
||||
clearedFields map[string]struct{}
|
||||
group *uuid.UUID
|
||||
clearedgroup bool
|
||||
parent *uuid.UUID
|
||||
clearedparent bool
|
||||
children map[uuid.UUID]struct{}
|
||||
removedchildren map[uuid.UUID]struct{}
|
||||
clearedchildren bool
|
||||
items map[uuid.UUID]struct{}
|
||||
removeditems map[uuid.UUID]struct{}
|
||||
cleareditems bool
|
||||
done bool
|
||||
oldValue func(context.Context) (*Label, error)
|
||||
predicates []predicate.Label
|
||||
}
|
||||
|
||||
var _ ent.Mutation = (*LabelMutation)(nil)
|
||||
@@ -9060,6 +9065,99 @@ func (m *LabelMutation) ResetGroup() {
|
||||
m.clearedgroup = false
|
||||
}
|
||||
|
||||
// SetParentID sets the "parent" edge to the Label entity by id.
|
||||
func (m *LabelMutation) SetParentID(id uuid.UUID) {
|
||||
m.parent = &id
|
||||
}
|
||||
|
||||
// ClearParent clears the "parent" edge to the Label entity.
|
||||
func (m *LabelMutation) ClearParent() {
|
||||
m.clearedparent = true
|
||||
}
|
||||
|
||||
// ParentCleared reports if the "parent" edge to the Label entity was cleared.
|
||||
func (m *LabelMutation) ParentCleared() bool {
|
||||
return m.clearedparent
|
||||
}
|
||||
|
||||
// ParentID returns the "parent" edge ID in the mutation.
|
||||
func (m *LabelMutation) ParentID() (id uuid.UUID, exists bool) {
|
||||
if m.parent != nil {
|
||||
return *m.parent, true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ParentIDs returns the "parent" edge IDs in the mutation.
|
||||
// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use
|
||||
// ParentID instead. It exists only for internal usage by the builders.
|
||||
func (m *LabelMutation) ParentIDs() (ids []uuid.UUID) {
|
||||
if id := m.parent; id != nil {
|
||||
ids = append(ids, *id)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ResetParent resets all changes to the "parent" edge.
|
||||
func (m *LabelMutation) ResetParent() {
|
||||
m.parent = nil
|
||||
m.clearedparent = false
|
||||
}
|
||||
|
||||
// AddChildIDs adds the "children" edge to the Label entity by ids.
|
||||
func (m *LabelMutation) AddChildIDs(ids ...uuid.UUID) {
|
||||
if m.children == nil {
|
||||
m.children = make(map[uuid.UUID]struct{})
|
||||
}
|
||||
for i := range ids {
|
||||
m.children[ids[i]] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
// ClearChildren clears the "children" edge to the Label entity.
|
||||
func (m *LabelMutation) ClearChildren() {
|
||||
m.clearedchildren = true
|
||||
}
|
||||
|
||||
// ChildrenCleared reports if the "children" edge to the Label entity was cleared.
|
||||
func (m *LabelMutation) ChildrenCleared() bool {
|
||||
return m.clearedchildren
|
||||
}
|
||||
|
||||
// RemoveChildIDs removes the "children" edge to the Label entity by IDs.
|
||||
func (m *LabelMutation) RemoveChildIDs(ids ...uuid.UUID) {
|
||||
if m.removedchildren == nil {
|
||||
m.removedchildren = make(map[uuid.UUID]struct{})
|
||||
}
|
||||
for i := range ids {
|
||||
delete(m.children, ids[i])
|
||||
m.removedchildren[ids[i]] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
// RemovedChildren returns the removed IDs of the "children" edge to the Label entity.
|
||||
func (m *LabelMutation) RemovedChildrenIDs() (ids []uuid.UUID) {
|
||||
for id := range m.removedchildren {
|
||||
ids = append(ids, id)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ChildrenIDs returns the "children" edge IDs in the mutation.
|
||||
func (m *LabelMutation) ChildrenIDs() (ids []uuid.UUID) {
|
||||
for id := range m.children {
|
||||
ids = append(ids, id)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ResetChildren resets all changes to the "children" edge.
|
||||
func (m *LabelMutation) ResetChildren() {
|
||||
m.children = nil
|
||||
m.clearedchildren = false
|
||||
m.removedchildren = nil
|
||||
}
|
||||
|
||||
// AddItemIDs adds the "items" edge to the Item entity by ids.
|
||||
func (m *LabelMutation) AddItemIDs(ids ...uuid.UUID) {
|
||||
if m.items == nil {
|
||||
@@ -9330,10 +9428,16 @@ func (m *LabelMutation) ResetField(name string) error {
|
||||
|
||||
// AddedEdges returns all edge names that were set/added in this mutation.
|
||||
func (m *LabelMutation) AddedEdges() []string {
|
||||
edges := make([]string, 0, 2)
|
||||
edges := make([]string, 0, 4)
|
||||
if m.group != nil {
|
||||
edges = append(edges, label.EdgeGroup)
|
||||
}
|
||||
if m.parent != nil {
|
||||
edges = append(edges, label.EdgeParent)
|
||||
}
|
||||
if m.children != nil {
|
||||
edges = append(edges, label.EdgeChildren)
|
||||
}
|
||||
if m.items != nil {
|
||||
edges = append(edges, label.EdgeItems)
|
||||
}
|
||||
@@ -9348,6 +9452,16 @@ func (m *LabelMutation) AddedIDs(name string) []ent.Value {
|
||||
if id := m.group; id != nil {
|
||||
return []ent.Value{*id}
|
||||
}
|
||||
case label.EdgeParent:
|
||||
if id := m.parent; id != nil {
|
||||
return []ent.Value{*id}
|
||||
}
|
||||
case label.EdgeChildren:
|
||||
ids := make([]ent.Value, 0, len(m.children))
|
||||
for id := range m.children {
|
||||
ids = append(ids, id)
|
||||
}
|
||||
return ids
|
||||
case label.EdgeItems:
|
||||
ids := make([]ent.Value, 0, len(m.items))
|
||||
for id := range m.items {
|
||||
@@ -9360,7 +9474,10 @@ func (m *LabelMutation) AddedIDs(name string) []ent.Value {
|
||||
|
||||
// RemovedEdges returns all edge names that were removed in this mutation.
|
||||
func (m *LabelMutation) RemovedEdges() []string {
|
||||
edges := make([]string, 0, 2)
|
||||
edges := make([]string, 0, 4)
|
||||
if m.removedchildren != nil {
|
||||
edges = append(edges, label.EdgeChildren)
|
||||
}
|
||||
if m.removeditems != nil {
|
||||
edges = append(edges, label.EdgeItems)
|
||||
}
|
||||
@@ -9371,6 +9488,12 @@ func (m *LabelMutation) RemovedEdges() []string {
|
||||
// the given name in this mutation.
|
||||
func (m *LabelMutation) RemovedIDs(name string) []ent.Value {
|
||||
switch name {
|
||||
case label.EdgeChildren:
|
||||
ids := make([]ent.Value, 0, len(m.removedchildren))
|
||||
for id := range m.removedchildren {
|
||||
ids = append(ids, id)
|
||||
}
|
||||
return ids
|
||||
case label.EdgeItems:
|
||||
ids := make([]ent.Value, 0, len(m.removeditems))
|
||||
for id := range m.removeditems {
|
||||
@@ -9383,10 +9506,16 @@ func (m *LabelMutation) RemovedIDs(name string) []ent.Value {
|
||||
|
||||
// ClearedEdges returns all edge names that were cleared in this mutation.
|
||||
func (m *LabelMutation) ClearedEdges() []string {
|
||||
edges := make([]string, 0, 2)
|
||||
edges := make([]string, 0, 4)
|
||||
if m.clearedgroup {
|
||||
edges = append(edges, label.EdgeGroup)
|
||||
}
|
||||
if m.clearedparent {
|
||||
edges = append(edges, label.EdgeParent)
|
||||
}
|
||||
if m.clearedchildren {
|
||||
edges = append(edges, label.EdgeChildren)
|
||||
}
|
||||
if m.cleareditems {
|
||||
edges = append(edges, label.EdgeItems)
|
||||
}
|
||||
@@ -9399,6 +9528,10 @@ func (m *LabelMutation) EdgeCleared(name string) bool {
|
||||
switch name {
|
||||
case label.EdgeGroup:
|
||||
return m.clearedgroup
|
||||
case label.EdgeParent:
|
||||
return m.clearedparent
|
||||
case label.EdgeChildren:
|
||||
return m.clearedchildren
|
||||
case label.EdgeItems:
|
||||
return m.cleareditems
|
||||
}
|
||||
@@ -9412,6 +9545,9 @@ func (m *LabelMutation) ClearEdge(name string) error {
|
||||
case label.EdgeGroup:
|
||||
m.ClearGroup()
|
||||
return nil
|
||||
case label.EdgeParent:
|
||||
m.ClearParent()
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("unknown Label unique edge %s", name)
|
||||
}
|
||||
@@ -9423,6 +9559,12 @@ func (m *LabelMutation) ResetEdge(name string) error {
|
||||
case label.EdgeGroup:
|
||||
m.ResetGroup()
|
||||
return nil
|
||||
case label.EdgeParent:
|
||||
m.ResetParent()
|
||||
return nil
|
||||
case label.EdgeChildren:
|
||||
m.ResetChildren()
|
||||
return nil
|
||||
case label.EdgeItems:
|
||||
m.ResetItems()
|
||||
return nil
|
||||
|
||||
@@ -2,6 +2,7 @@ package schema
|
||||
|
||||
import (
|
||||
"entgo.io/ent"
|
||||
"entgo.io/ent/dialect/entsql"
|
||||
"entgo.io/ent/schema/edge"
|
||||
"entgo.io/ent/schema/field"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/schema/mixins"
|
||||
@@ -32,6 +33,12 @@ func (Label) Fields() []ent.Field {
|
||||
// Edges of the Label.
|
||||
func (Label) Edges() []ent.Edge {
|
||||
return []ent.Edge{
|
||||
edge.To("children", Label.Type).
|
||||
Annotations(entsql.Annotation{
|
||||
OnDelete: entsql.Cascade,
|
||||
}).
|
||||
From("parent").
|
||||
Unique(),
|
||||
edge.To("items", Item.Type),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
-- +goose Up
|
||||
ALTER TABLE users ALTER COLUMN password DROP NOT NULL;
|
||||
@@ -0,0 +1,3 @@
|
||||
-- +goose Up
|
||||
-- Add label_children column to labels table for hierarchical label organization
|
||||
ALTER TABLE labels ADD COLUMN label_children UUID REFERENCES labels(id) ON DELETE CASCADE;
|
||||
@@ -0,0 +1,3 @@
|
||||
-- +goose Up
|
||||
-- Add label_children column to labels table for hierarchical label organization
|
||||
ALTER TABLE labels ADD COLUMN label_children TEXT REFERENCES labels(id) ON DELETE CASCADE;
|
||||
@@ -2,6 +2,7 @@ package repo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
@@ -19,9 +20,10 @@ type LabelRepository struct {
|
||||
|
||||
type (
|
||||
LabelCreate struct {
|
||||
Name string `json:"name" validate:"required,min=1,max=255"`
|
||||
Description string `json:"description" validate:"max=1000"`
|
||||
Color string `json:"color"`
|
||||
Name string `json:"name" validate:"required,min=1,max=255"`
|
||||
Description string `json:"description" validate:"max=1000"`
|
||||
Color string `json:"color"`
|
||||
ParentID uuid.UUID `json:"parentId" extensions:"x-nullable"`
|
||||
}
|
||||
|
||||
LabelUpdate struct {
|
||||
@@ -29,6 +31,7 @@ type (
|
||||
Name string `json:"name" validate:"required,min=1,max=255"`
|
||||
Description string `json:"description" validate:"max=1000"`
|
||||
Color string `json:"color"`
|
||||
ParentID uuid.UUID `json:"parentId" extensions:"x-nullable"`
|
||||
}
|
||||
|
||||
LabelSummary struct {
|
||||
@@ -41,7 +44,9 @@ type (
|
||||
}
|
||||
|
||||
LabelOut struct {
|
||||
Parent *LabelSummary `json:"parent,omitempty"`
|
||||
LabelSummary
|
||||
Children []LabelSummary `json:"children"`
|
||||
}
|
||||
)
|
||||
|
||||
@@ -62,7 +67,20 @@ var (
|
||||
)
|
||||
|
||||
func mapLabelOut(label *ent.Label) LabelOut {
|
||||
var parent *LabelSummary
|
||||
if label.Edges.Parent != nil {
|
||||
p := mapLabelSummary(label.Edges.Parent)
|
||||
parent = &p
|
||||
}
|
||||
|
||||
children := make([]LabelSummary, 0, len(label.Edges.Children))
|
||||
for _, c := range label.Edges.Children {
|
||||
children = append(children, mapLabelSummary(c))
|
||||
}
|
||||
|
||||
return LabelOut{
|
||||
Parent: parent,
|
||||
Children: children,
|
||||
LabelSummary: mapLabelSummary(label),
|
||||
}
|
||||
}
|
||||
@@ -73,10 +91,59 @@ func (r *LabelRepository) publishMutationEvent(gid uuid.UUID) {
|
||||
}
|
||||
}
|
||||
|
||||
const maxLabelDepth = 20
|
||||
|
||||
// validateParentNotCircular checks if setting parentID as parent of labelID would create a circular reference
|
||||
func (r *LabelRepository) validateParentNotCircular(ctx context.Context, labelID, parentID uuid.UUID) error {
|
||||
// Check if label is being set as its own parent
|
||||
if labelID == parentID {
|
||||
return fmt.Errorf("label cannot be its own parent")
|
||||
}
|
||||
|
||||
// Check if parent exists and get its parent chain
|
||||
depth := 0
|
||||
currentID := parentID
|
||||
|
||||
for currentID != uuid.Nil && depth < maxLabelDepth {
|
||||
l, err := r.db.Label.Query().
|
||||
Where(label.ID(currentID)).
|
||||
WithParent().
|
||||
Only(ctx)
|
||||
if err != nil {
|
||||
if ent.IsNotFound(err) {
|
||||
return fmt.Errorf("parent label not found")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Check if we've reached the label we're trying to set a parent for (circular reference)
|
||||
if l.ID == labelID {
|
||||
return fmt.Errorf("circular reference detected in label hierarchy")
|
||||
}
|
||||
|
||||
depth++
|
||||
if depth >= maxLabelDepth {
|
||||
return fmt.Errorf("label hierarchy exceeds maximum depth of 20")
|
||||
}
|
||||
|
||||
if l.Edges.Parent != nil {
|
||||
currentID = l.Edges.Parent.ID
|
||||
} else {
|
||||
currentID = uuid.Nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *LabelRepository) getOne(ctx context.Context, where ...predicate.Label) (LabelOut, error) {
|
||||
return mapLabelOutErr(r.db.Label.Query().
|
||||
Where(where...).
|
||||
WithGroup().
|
||||
WithParent().
|
||||
WithChildren(func(lq *ent.LabelQuery) {
|
||||
lq.Order(label.ByName())
|
||||
}).
|
||||
Only(ctx),
|
||||
)
|
||||
}
|
||||
@@ -99,19 +166,62 @@ func (r *LabelRepository) GetAll(ctx context.Context, groupID uuid.UUID) ([]Labe
|
||||
}
|
||||
|
||||
func (r *LabelRepository) Create(ctx context.Context, groupID uuid.UUID, data LabelCreate) (LabelOut, error) {
|
||||
label, err := r.db.Label.Create().
|
||||
// Validate parent if provided
|
||||
if data.ParentID != uuid.Nil {
|
||||
// Check that parent exists and belongs to same group
|
||||
parent, err := r.db.Label.Query().
|
||||
Where(label.ID(data.ParentID), label.HasGroupWith(group.ID(groupID))).
|
||||
Only(ctx)
|
||||
if err != nil {
|
||||
if ent.IsNotFound(err) {
|
||||
return LabelOut{}, fmt.Errorf("parent label not found or does not belong to the same group")
|
||||
}
|
||||
return LabelOut{}, err
|
||||
}
|
||||
|
||||
// Validate parent hierarchy depth
|
||||
depth := 0
|
||||
currentID := parent.ID
|
||||
for currentID != uuid.Nil && depth < maxLabelDepth {
|
||||
l, err := r.db.Label.Query().
|
||||
Where(label.ID(currentID)).
|
||||
WithParent().
|
||||
Only(ctx)
|
||||
if err != nil {
|
||||
return LabelOut{}, err
|
||||
}
|
||||
|
||||
depth++
|
||||
if depth >= maxLabelDepth {
|
||||
return LabelOut{}, fmt.Errorf("label hierarchy exceeds maximum depth of 20")
|
||||
}
|
||||
|
||||
if l.Edges.Parent != nil {
|
||||
currentID = l.Edges.Parent.ID
|
||||
} else {
|
||||
currentID = uuid.Nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
q := r.db.Label.Create().
|
||||
SetName(data.Name).
|
||||
SetDescription(data.Description).
|
||||
SetColor(data.Color).
|
||||
SetGroupID(groupID).
|
||||
Save(ctx)
|
||||
SetGroupID(groupID)
|
||||
|
||||
if data.ParentID != uuid.Nil {
|
||||
q.SetParentID(data.ParentID)
|
||||
}
|
||||
|
||||
label, err := q.Save(ctx)
|
||||
if err != nil {
|
||||
return LabelOut{}, err
|
||||
}
|
||||
|
||||
label.Edges.Group = &ent.Group{ID: groupID} // bootstrap group ID
|
||||
r.publishMutationEvent(groupID)
|
||||
return mapLabelOut(label), err
|
||||
return mapLabelOut(label), nil
|
||||
}
|
||||
|
||||
func (r *LabelRepository) update(ctx context.Context, data LabelUpdate, where ...predicate.Label) (int, error) {
|
||||
@@ -119,15 +229,41 @@ func (r *LabelRepository) update(ctx context.Context, data LabelUpdate, where ..
|
||||
panic("empty where not supported empty")
|
||||
}
|
||||
|
||||
return r.db.Label.Update().
|
||||
q := r.db.Label.Update().
|
||||
Where(where...).
|
||||
SetName(data.Name).
|
||||
SetDescription(data.Description).
|
||||
SetColor(data.Color).
|
||||
Save(ctx)
|
||||
SetColor(data.Color)
|
||||
|
||||
if data.ParentID != uuid.Nil {
|
||||
q.SetParentID(data.ParentID)
|
||||
} else {
|
||||
q.ClearParent()
|
||||
}
|
||||
|
||||
return q.Save(ctx)
|
||||
}
|
||||
|
||||
func (r *LabelRepository) UpdateByGroup(ctx context.Context, gid uuid.UUID, data LabelUpdate) (LabelOut, error) {
|
||||
// Validate parent if provided
|
||||
if data.ParentID != uuid.Nil {
|
||||
// Check that parent exists and belongs to same group
|
||||
parent, err := r.db.Label.Query().
|
||||
Where(label.ID(data.ParentID), label.HasGroupWith(group.ID(gid))).
|
||||
Only(ctx)
|
||||
if err != nil {
|
||||
if ent.IsNotFound(err) {
|
||||
return LabelOut{}, fmt.Errorf("parent label not found or does not belong to the same group")
|
||||
}
|
||||
return LabelOut{}, err
|
||||
}
|
||||
|
||||
// Validate no circular reference
|
||||
if err := r.validateParentNotCircular(ctx, data.ID, parent.ID); err != nil {
|
||||
return LabelOut{}, err
|
||||
}
|
||||
}
|
||||
|
||||
_, err := r.update(ctx, data, label.ID(data.ID), label.HasGroupWith(group.ID(gid)))
|
||||
if err != nil {
|
||||
return LabelOut{}, err
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
@@ -101,3 +102,208 @@ func TestLabelRepository_Delete(t *testing.T) {
|
||||
_, err = tRepos.Labels.GetOne(context.Background(), loc.ID)
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func TestLabelRepository_ParentChild_Create(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
// Create parent label
|
||||
parent, err := tRepos.Labels.Create(ctx, tGroup.ID, labelFactory())
|
||||
require.NoError(t, err)
|
||||
defer tRepos.Labels.delete(ctx, parent.ID)
|
||||
|
||||
// Create child label
|
||||
childData := labelFactory()
|
||||
childData.ParentID = parent.ID
|
||||
child, err := tRepos.Labels.Create(ctx, tGroup.ID, childData)
|
||||
require.NoError(t, err)
|
||||
defer tRepos.Labels.delete(ctx, child.ID)
|
||||
|
||||
// Verify child has parent
|
||||
foundChild, err := tRepos.Labels.GetOne(ctx, child.ID)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, foundChild.Parent)
|
||||
assert.Equal(t, parent.ID, foundChild.Parent.ID)
|
||||
|
||||
// Verify parent has child
|
||||
foundParent, err := tRepos.Labels.GetOne(ctx, parent.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, foundParent.Children, 1)
|
||||
assert.Equal(t, child.ID, foundParent.Children[0].ID)
|
||||
}
|
||||
|
||||
func TestLabelRepository_ParentChild_CascadeDelete(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
// Create parent label
|
||||
parent, err := tRepos.Labels.Create(ctx, tGroup.ID, labelFactory())
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create child labels
|
||||
child1Data := labelFactory()
|
||||
child1Data.ParentID = parent.ID
|
||||
child1, err := tRepos.Labels.Create(ctx, tGroup.ID, child1Data)
|
||||
require.NoError(t, err)
|
||||
|
||||
child2Data := labelFactory()
|
||||
child2Data.ParentID = parent.ID
|
||||
child2, err := tRepos.Labels.Create(ctx, tGroup.ID, child2Data)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Delete parent
|
||||
err = tRepos.Labels.delete(ctx, parent.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify children are also deleted (cascade)
|
||||
_, err = tRepos.Labels.GetOne(ctx, child1.ID)
|
||||
require.Error(t, err)
|
||||
|
||||
_, err = tRepos.Labels.GetOne(ctx, child2.ID)
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func TestLabelRepository_ParentChild_CircularReference_Self(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
// Create a label
|
||||
label1, err := tRepos.Labels.Create(ctx, tGroup.ID, labelFactory())
|
||||
require.NoError(t, err)
|
||||
defer tRepos.Labels.delete(ctx, label1.ID)
|
||||
|
||||
// Try to set itself as parent - should fail
|
||||
updateData := LabelUpdate{
|
||||
ID: label1.ID,
|
||||
Name: label1.Name,
|
||||
Description: label1.Description,
|
||||
Color: label1.Color,
|
||||
ParentID: label1.ID,
|
||||
}
|
||||
|
||||
_, err = tRepos.Labels.UpdateByGroup(ctx, tGroup.ID, updateData)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "cannot be its own parent")
|
||||
}
|
||||
|
||||
func TestLabelRepository_ParentChild_CircularReference_Chain(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
// Create label chain: label1 -> label2 -> label3
|
||||
label1, err := tRepos.Labels.Create(ctx, tGroup.ID, labelFactory())
|
||||
require.NoError(t, err)
|
||||
defer tRepos.Labels.delete(ctx, label1.ID)
|
||||
|
||||
label2Data := labelFactory()
|
||||
label2Data.ParentID = label1.ID
|
||||
label2, err := tRepos.Labels.Create(ctx, tGroup.ID, label2Data)
|
||||
require.NoError(t, err)
|
||||
defer tRepos.Labels.delete(ctx, label2.ID)
|
||||
|
||||
label3Data := labelFactory()
|
||||
label3Data.ParentID = label2.ID
|
||||
label3, err := tRepos.Labels.Create(ctx, tGroup.ID, label3Data)
|
||||
require.NoError(t, err)
|
||||
defer tRepos.Labels.delete(ctx, label3.ID)
|
||||
|
||||
// Try to set label3 as parent of label1 (would create circular reference)
|
||||
updateData := LabelUpdate{
|
||||
ID: label1.ID,
|
||||
Name: label1.Name,
|
||||
Description: label1.Description,
|
||||
Color: label1.Color,
|
||||
ParentID: label3.ID,
|
||||
}
|
||||
|
||||
_, err = tRepos.Labels.UpdateByGroup(ctx, tGroup.ID, updateData)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "circular reference")
|
||||
}
|
||||
|
||||
func TestLabelRepository_ParentChild_MaxDepth(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
// Create a chain of 20 labels
|
||||
labels := make([]LabelOut, 20)
|
||||
for i := 0; i < 20; i++ {
|
||||
labelData := labelFactory()
|
||||
if i > 0 {
|
||||
labelData.ParentID = labels[i-1].ID
|
||||
}
|
||||
label, err := tRepos.Labels.Create(ctx, tGroup.ID, labelData)
|
||||
require.NoError(t, err)
|
||||
labels[i] = label
|
||||
}
|
||||
|
||||
// Clean up
|
||||
defer func() {
|
||||
for i := len(labels) - 1; i >= 0; i-- {
|
||||
_ = tRepos.Labels.delete(ctx, labels[i].ID)
|
||||
}
|
||||
}()
|
||||
|
||||
// Try to create 21st level - should fail
|
||||
labelData := labelFactory()
|
||||
labelData.ParentID = labels[19].ID
|
||||
_, err := tRepos.Labels.Create(ctx, tGroup.ID, labelData)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "maximum depth")
|
||||
}
|
||||
|
||||
func TestLabelRepository_ParentChild_Update(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
// Create labels
|
||||
parent1, err := tRepos.Labels.Create(ctx, tGroup.ID, labelFactory())
|
||||
require.NoError(t, err)
|
||||
defer tRepos.Labels.delete(ctx, parent1.ID)
|
||||
|
||||
parent2, err := tRepos.Labels.Create(ctx, tGroup.ID, labelFactory())
|
||||
require.NoError(t, err)
|
||||
defer tRepos.Labels.delete(ctx, parent2.ID)
|
||||
|
||||
childData := labelFactory()
|
||||
childData.ParentID = parent1.ID
|
||||
child, err := tRepos.Labels.Create(ctx, tGroup.ID, childData)
|
||||
require.NoError(t, err)
|
||||
defer tRepos.Labels.delete(ctx, child.ID)
|
||||
|
||||
// Update child to have different parent
|
||||
updateData := LabelUpdate{
|
||||
ID: child.ID,
|
||||
Name: child.Name,
|
||||
Description: child.Description,
|
||||
Color: child.Color,
|
||||
ParentID: parent2.ID,
|
||||
}
|
||||
|
||||
updated, err := tRepos.Labels.UpdateByGroup(ctx, tGroup.ID, updateData)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, updated.Parent)
|
||||
assert.Equal(t, parent2.ID, updated.Parent.ID)
|
||||
}
|
||||
|
||||
func TestLabelRepository_ParentChild_ClearParent(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
// Create parent and child
|
||||
parent, err := tRepos.Labels.Create(ctx, tGroup.ID, labelFactory())
|
||||
require.NoError(t, err)
|
||||
defer tRepos.Labels.delete(ctx, parent.ID)
|
||||
|
||||
childData := labelFactory()
|
||||
childData.ParentID = parent.ID
|
||||
child, err := tRepos.Labels.Create(ctx, tGroup.ID, childData)
|
||||
require.NoError(t, err)
|
||||
defer tRepos.Labels.delete(ctx, child.ID)
|
||||
|
||||
// Clear parent by setting ParentID to uuid.Nil
|
||||
updateData := LabelUpdate{
|
||||
ID: child.ID,
|
||||
Name: child.Name,
|
||||
Description: child.Description,
|
||||
Color: child.Color,
|
||||
ParentID: uuid.Nil,
|
||||
}
|
||||
|
||||
updated, err := tRepos.Labels.UpdateByGroup(ctx, tGroup.ID, updateData)
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, updated.Parent)
|
||||
}
|
||||
|
||||
@@ -3413,6 +3413,13 @@
|
||||
"ent.LabelEdges": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"children": {
|
||||
"description": "Children holds the value of the children edge.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/ent.Label"
|
||||
}
|
||||
},
|
||||
"group": {
|
||||
"description": "Group holds the value of the group edge.",
|
||||
"allOf": [
|
||||
@@ -3427,6 +3434,14 @@
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/ent.Item"
|
||||
}
|
||||
},
|
||||
"parent": {
|
||||
"description": "Parent holds the value of the parent edge.",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/ent.Label"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -4634,12 +4649,22 @@
|
||||
"type": "string",
|
||||
"maxLength": 255,
|
||||
"minLength": 1
|
||||
},
|
||||
"parentId": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"repo.LabelOut": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"children": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/repo.LabelSummary"
|
||||
}
|
||||
},
|
||||
"color": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -4655,6 +4680,9 @@
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"parent": {
|
||||
"$ref": "#/components/schemas/repo.LabelSummary"
|
||||
},
|
||||
"updatedAt": {
|
||||
"type": "string"
|
||||
}
|
||||
|
||||
@@ -2112,6 +2112,11 @@ components:
|
||||
ent.LabelEdges:
|
||||
type: object
|
||||
properties:
|
||||
children:
|
||||
description: Children holds the value of the children edge.
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/ent.Label"
|
||||
group:
|
||||
description: Group holds the value of the group edge.
|
||||
allOf:
|
||||
@@ -2121,6 +2126,10 @@ components:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/ent.Item"
|
||||
parent:
|
||||
description: Parent holds the value of the parent edge.
|
||||
allOf:
|
||||
- $ref: "#/components/schemas/ent.Label"
|
||||
ent.Location:
|
||||
type: object
|
||||
properties:
|
||||
@@ -2956,9 +2965,16 @@ components:
|
||||
type: string
|
||||
maxLength: 255
|
||||
minLength: 1
|
||||
parentId:
|
||||
type: string
|
||||
nullable: true
|
||||
repo.LabelOut:
|
||||
type: object
|
||||
properties:
|
||||
children:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/repo.LabelSummary"
|
||||
color:
|
||||
type: string
|
||||
createdAt:
|
||||
@@ -2969,6 +2985,8 @@ components:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
parent:
|
||||
$ref: "#/components/schemas/repo.LabelSummary"
|
||||
updatedAt:
|
||||
type: string
|
||||
repo.LabelSummary:
|
||||
|
||||
@@ -3214,6 +3214,13 @@
|
||||
"ent.LabelEdges": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"children": {
|
||||
"description": "Children holds the value of the children edge.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/ent.Label"
|
||||
}
|
||||
},
|
||||
"group": {
|
||||
"description": "Group holds the value of the group edge.",
|
||||
"allOf": [
|
||||
@@ -3228,6 +3235,14 @@
|
||||
"items": {
|
||||
"$ref": "#/definitions/ent.Item"
|
||||
}
|
||||
},
|
||||
"parent": {
|
||||
"description": "Parent holds the value of the parent edge.",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ent.Label"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -4435,12 +4450,22 @@
|
||||
"type": "string",
|
||||
"maxLength": 255,
|
||||
"minLength": 1
|
||||
},
|
||||
"parentId": {
|
||||
"type": "string",
|
||||
"x-nullable": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"repo.LabelOut": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"children": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/repo.LabelSummary"
|
||||
}
|
||||
},
|
||||
"color": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -4456,6 +4481,9 @@
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"parent": {
|
||||
"$ref": "#/definitions/repo.LabelSummary"
|
||||
},
|
||||
"updatedAt": {
|
||||
"type": "string"
|
||||
}
|
||||
|
||||
@@ -534,6 +534,11 @@ definitions:
|
||||
type: object
|
||||
ent.LabelEdges:
|
||||
properties:
|
||||
children:
|
||||
description: Children holds the value of the children edge.
|
||||
items:
|
||||
$ref: '#/definitions/ent.Label'
|
||||
type: array
|
||||
group:
|
||||
allOf:
|
||||
- $ref: '#/definitions/ent.Group'
|
||||
@@ -543,6 +548,10 @@ definitions:
|
||||
items:
|
||||
$ref: '#/definitions/ent.Item'
|
||||
type: array
|
||||
parent:
|
||||
allOf:
|
||||
- $ref: '#/definitions/ent.Label'
|
||||
description: Parent holds the value of the parent edge.
|
||||
type: object
|
||||
ent.Location:
|
||||
properties:
|
||||
@@ -1371,11 +1380,18 @@ definitions:
|
||||
maxLength: 255
|
||||
minLength: 1
|
||||
type: string
|
||||
parentId:
|
||||
type: string
|
||||
x-nullable: true
|
||||
required:
|
||||
- name
|
||||
type: object
|
||||
repo.LabelOut:
|
||||
properties:
|
||||
children:
|
||||
items:
|
||||
$ref: '#/definitions/repo.LabelSummary'
|
||||
type: array
|
||||
color:
|
||||
type: string
|
||||
createdAt:
|
||||
@@ -1386,6 +1402,8 @@ definitions:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
parent:
|
||||
$ref: '#/definitions/repo.LabelSummary'
|
||||
updatedAt:
|
||||
type: string
|
||||
type: object
|
||||
|
||||
@@ -22,7 +22,7 @@ aside: false
|
||||
| HBOX_WEB_IDLE_TIMEOUT | 30s | Idle timeout of HTTP server |
|
||||
| HBOX_STORAGE_CONN_STRING | file:///./ | path to the data directory, do not change this if you're using docker |
|
||||
| HBOX_STORAGE_PREFIX_PATH | .data | prefix path for the storage, if not set the storage will be used as is |
|
||||
| HBOX_LOG_LEVEL | `info` | log level to use, can be one of `trace`, `debug`, `info`, `warn`, `error`, `critical` |
|
||||
| HBOX_LOG_LEVEL | `info` | log level to use, can be one of `trace`, `debug`, `info`, `warn`, `error`, `fatal`, `panic` |
|
||||
| HBOX_LOG_FORMAT | `text` | log format to use, can be one of: `text`, `json` |
|
||||
| HBOX_MAILER_HOST | | email host to use, if not set no email provider will be used |
|
||||
| HBOX_MAILER_PORT | 587 | email port to use |
|
||||
@@ -71,6 +71,7 @@ aside: false
|
||||
| HBOX_THUMBNAIL_ENABLED | true | enable thumbnail generation for images, supports PNG, JPEG, AVIF, WEBP, GIF file types |
|
||||
| HBOX_THUMBNAIL_WIDTH | 500 | width for generated thumbnails in pixels |
|
||||
| HBOX_THUMBNAIL_HEIGHT | 500 | height for generated thumbnails in pixels |
|
||||
| HBOX_BARCODE_TOKEN_BARCODESPIDER | | API token for BarcodeSpider.com service used for barcode product lookups. If not set, barcode product lookups will not be performed. |
|
||||
|
||||
### HBOX_WEB_HOST examples
|
||||
|
||||
@@ -204,70 +205,79 @@ If you're deploying without docker you can use command line arguments to configu
|
||||
for more information.
|
||||
|
||||
```sh
|
||||
Usage: api [options] [arguments]
|
||||
|
||||
OPTIONS
|
||||
--mode/$HBOX_MODE <string> (default: development)
|
||||
--web-port/$HBOX_WEB_PORT <string> (default: 7745)
|
||||
--web-host/$HBOX_WEB_HOST <string>
|
||||
--web-max-upload-size/$HBOX_WEB_MAX_UPLOAD_SIZE <int> (default: 10)
|
||||
--storage-conn-string/$HBOX_STORAGE_CONN_STRING <string> (default: file:///./)
|
||||
--storage-prefix-path/$HBOX_STORAGE_PREFIX_PATH <string> (default: .data)
|
||||
--log-level/$HBOX_LOG_LEVEL <string> (default: info)
|
||||
--log-format/$HBOX_LOG_FORMAT <string> (default: text)
|
||||
--mailer-host/$HBOX_MAILER_HOST <string>
|
||||
--mailer-port/$HBOX_MAILER_PORT <int>
|
||||
--mailer-username/$HBOX_MAILER_USERNAME <string>
|
||||
--mailer-password/$HBOX_MAILER_PASSWORD <string>
|
||||
--mailer-from/$HBOX_MAILER_FROM <string>
|
||||
--demo/$HBOX_DEMO <bool>
|
||||
--debug-enabled/$HBOX_DEBUG_ENABLED <bool> (default: false)
|
||||
--debug-port/$HBOX_DEBUG_PORT <string> (default: 4000)
|
||||
--database-driver/$HBOX_DATABASE_DRIVER <string> (default: sqlite3)
|
||||
--database-sqlite-path/$HBOX_DATABASE_SQLITE_PATH <string> (default: ./.data/homebox.db?_pragma=busy_timeout=999&_pragma=journal_mode=WAL&_fk=1&_time_format=sqlite)
|
||||
--database-host/$HBOX_DATABASE_HOST <string>
|
||||
--database-port/$HBOX_DATABASE_PORT <string>
|
||||
--database-username/$HBOX_DATABASE_USERNAME <string>
|
||||
--database-password/$HBOX_DATABASE_PASSWORD <string>
|
||||
--database-database/$HBOX_DATABASE_DATABASE <string>
|
||||
--database-ssl-mode/$HBOX_DATABASE_SSL_MODE <string> (default: prefer)
|
||||
--options-allow-registration/$HBOX_OPTIONS_ALLOW_REGISTRATION <bool> (default: true)
|
||||
--options-auto-increment-asset-id/$HBOX_OPTIONS_AUTO_INCREMENT_ASSET_ID <bool> (default: true)
|
||||
--options-currency-config/$HBOX_OPTIONS_CURRENCY_CONFIG <string>
|
||||
--options-github-release-check/$HBOX_OPTIONS_GITHUB_RELEASE_CHECK <bool> (default: true)
|
||||
--options-allow-analytics/$HBOX_OPTIONS_ALLOW_ANALYTICS <bool> (default: false)
|
||||
--options-allow-local-login/$HBOX_OPTIONS_ALLOW_LOCAL_LOGIN <bool> (default: true)
|
||||
--options-trust-proxy/$HBOX_OPTIONS_TRUST_PROXY <bool> (default: false)
|
||||
--options-hostname/$HBOX_OPTIONS_HOSTNAME <string>
|
||||
--oidc-enabled/$HBOX_OIDC_ENABLED <bool> (default: false)
|
||||
--oidc-issuer-url/$HBOX_OIDC_ISSUER_URL <string>
|
||||
--oidc-client-id/$HBOX_OIDC_CLIENT_ID <string>
|
||||
--oidc-client-secret/$HBOX_OIDC_CLIENT_SECRET <string>
|
||||
--oidc-scope/$HBOX_OIDC_SCOPE <string> (default: openid profile email)
|
||||
--oidc-allowed-groups/$HBOX_OIDC_ALLOWED_GROUPS <string>
|
||||
--oidc-auto-redirect/$HBOX_OIDC_AUTO_REDIRECT <bool> (default: false)
|
||||
--oidc-verify-email/$HBOX_OIDC_VERIFY_EMAIL <bool> (default: false)
|
||||
--oidc-group-claim/$HBOX_OIDC_GROUP_CLAIM <string> (default: groups)
|
||||
--oidc-email-claim/$HBOX_OIDC_EMAIL_CLAIM <string> (default: email)
|
||||
--oidc-name-claim/$HBOX_OIDC_NAME_CLAIM <string> (default: name)
|
||||
--oidc-email-verified-claim/$HBOX_OIDC_EMAIL_VERIFIED_CLAIM <string> (default: email_verified)
|
||||
--oidc-button-text/$HBOX_OIDC_BUTTON_TEXT <string> (default: Sign in with OIDC)
|
||||
--oidc-state-expiry/$HBOX_OIDC_STATE_EXPIRY <duration> (default: 10m)
|
||||
--oidc-request-timeout/$HBOX_OIDC_REQUEST_TIMEOUT <duration> (default: 30s)
|
||||
--label-maker-width/$HBOX_LABEL_MAKER_WIDTH <int> (default: 526)
|
||||
--label-maker-height/$HBOX_LABEL_MAKER_HEIGHT <int> (default: 200)
|
||||
--label-maker-padding/$HBOX_LABEL_MAKER_PADDING <int> (default: 32)
|
||||
--label-maker-margin/$HBOX_LABEL_MAKER_MARGIN <int> (default: 32)
|
||||
--label-maker-font-size/$HBOX_LABEL_MAKER_FONT_SIZE <float> (default: 32.0)
|
||||
--label-maker-print-command/$HBOX_LABEL_MAKER_PRINT_COMMAND <string>
|
||||
--label-maker-dynamic-length/$HBOX_LABEL_MAKER_DYNAMIC_LENGTH <bool> (default: true)
|
||||
--label-maker-additional-information/$HBOX_LABEL_MAKER_ADDITIONAL_INFORMATION <string>
|
||||
--label-maker-regular-font-path/$HBOX_LABEL_MAKER_REGULAR_FONT_PATH <string>
|
||||
--label-maker-bold-font-path/$HBOX_LABEL_MAKER_BOLD_FONT_PATH <string>
|
||||
--thumbnail-enabled/$HBOX_THUMBNAIL_ENABLED <bool> (default: true)
|
||||
--thumbnail-width/$HBOX_THUMBNAIL_WIDTH <int> (default: 500)
|
||||
--thumbnail-height/$HBOX_THUMBNAIL_HEIGHT <int> (default: 500)
|
||||
--help/-h display this help message
|
||||
Options:
|
||||
--barcode-token-barcodespider <string>
|
||||
--database-database <string>
|
||||
--database-driver <string> (default: sqlite3)
|
||||
--database-host <string>
|
||||
--database-password <string>
|
||||
--database-port <string>
|
||||
--database-pub-sub-conn-string <string> (default: mem://{{ .Topic }})
|
||||
--database-sqlite-path <string> (default: ./.data/homebox.db?_pragma=busy_timeout=999&_pragma=journal_mode=WAL&_fk=1&_time_format=sqlite)
|
||||
--database-ssl-cert <string>
|
||||
--database-ssl-key <string>
|
||||
--database-ssl-mode <string> (default: require)
|
||||
--database-ssl-root-cert <string>
|
||||
--database-username <string>
|
||||
--debug-enabled <bool> (default: false)
|
||||
--debug-port <string> (default: 4000)
|
||||
--demo <bool>
|
||||
-h, --help display this help message
|
||||
--label-maker-additional-information <string>
|
||||
--label-maker-bold-font-path <string>
|
||||
--label-maker-dynamic-length <bool> (default: true)
|
||||
--label-maker-font-size <float> (default: 32.0)
|
||||
--label-maker-height <int> (default: 200)
|
||||
--label-maker-label-service-timeout <int>
|
||||
--label-maker-label-service-url <string>
|
||||
--label-maker-margin <int> (default: 32)
|
||||
--label-maker-padding <int> (default: 32)
|
||||
--label-maker-print-command <string>
|
||||
--label-maker-regular-font-path <string>
|
||||
--label-maker-width <int> (default: 526)
|
||||
--log-format <string> (default: text)
|
||||
--log-level <string> (default: info)
|
||||
--mailer-from <string>
|
||||
--mailer-host <string>
|
||||
--mailer-password <string>
|
||||
--mailer-port <int>
|
||||
--mailer-username <string>
|
||||
--mode <string> (default: development)
|
||||
--oidc-allowed-groups <string>
|
||||
--oidc-auto-redirect <bool> (default: false)
|
||||
--oidc-button-text <string> (default: Sign in with OIDC)
|
||||
--oidc-client-id <string>
|
||||
--oidc-client-secret <string>
|
||||
--oidc-email-claim <string> (default: email)
|
||||
--oidc-email-verified-claim <string> (default: email_verified)
|
||||
--oidc-enabled <bool> (default: false)
|
||||
--oidc-group-claim <string> (default: groups)
|
||||
--oidc-issuer-url <string>
|
||||
--oidc-name-claim <string> (default: name)
|
||||
--oidc-request-timeout <duration> (default: 30s)
|
||||
--oidc-scope <string> (default: openid profile email)
|
||||
--oidc-state-expiry <duration> (default: 10m)
|
||||
--oidc-verify-email <bool> (default: false)
|
||||
--options-allow-analytics <bool> (default: false)
|
||||
--options-allow-local-login <bool> (default: true)
|
||||
--options-allow-registration <bool> (default: true)
|
||||
--options-auto-increment-asset-id <bool> (default: true)
|
||||
--options-currency-config <string>
|
||||
--options-github-release-check <bool> (default: true)
|
||||
--options-hostname <string>
|
||||
--options-trust-proxy <bool> (default: false)
|
||||
--storage-conn-string <string> (default: file:///./)
|
||||
--storage-prefix-path <string> (default: .data)
|
||||
--thumbnail-enabled <bool> (default: true)
|
||||
--thumbnail-height <int> (default: 500)
|
||||
--thumbnail-width <int> (default: 500)
|
||||
-v, --version display version
|
||||
--web-host <string>
|
||||
--web-idle-timeout <duration> (default: 30s)
|
||||
--web-max-upload-size <int> (default: 10)
|
||||
--web-port <string> (default: 7745)
|
||||
--web-read-timeout <duration> (default: 10s)
|
||||
--web-write-timeout <duration> (default: 10s)
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
@@ -30,19 +30,19 @@ the bucket name in the connection string.
|
||||
### S3-Compatible Storage
|
||||
|
||||
You can also use S3-compatible storage by setting the `HBOX_STORAGE_CONN_STRING` to
|
||||
`s3://my-bucket?awssdk=v2&endpoint=http://my-s3-compatible-endpoint.tld&disableSSL=true&s3ForcePathStyle=true`.
|
||||
`s3://my-bucket?awssdk=v2&endpoint=http://my-s3-compatible-endpoint.tld&disable_https=true&s3ForcePathStyle=true`.
|
||||
|
||||
This allows you to connect to S3-compatible services like MinIO, DigitalOcean Spaces, or any other service that supports
|
||||
the S3 API. Configure the `disableSSL`, `s3ForcePathStyle`, and `endpoint` parameters as needed for your specific
|
||||
the S3 API. Configure the `disable_https`, `s3ForcePathStyle`, and `endpoint` parameters as needed for your specific
|
||||
service.
|
||||
|
||||
#### Tested S3-Compatible Storage
|
||||
|
||||
| Service | Working | Connection String |
|
||||
|---------------------|---------|--------------------------------------------------------------------------------------------------------------------------|
|
||||
| MinIO | Yes | `s3://my-bucket?awssdk=v2&endpoint=http://minio:9000&disableSSL=true&s3ForcePathStyle=true` |
|
||||
| Cloudflare R2 | Yes | `s3://my-bucket?awssdk=v2&endpoint=https://<account-id>.r2.cloudflarestorage.com&disableSSL=false&s3ForcePathStyle=true` |
|
||||
| Backblaze B2 | Yes | `s3://my-bucket?awssdk=v2&endpoint=https://s3.us-west-004.backblazeb2.com&disableSSL=false&s3ForcePathStyle=true` |
|
||||
| MinIO | Yes | `s3://my-bucket?awssdk=v2&endpoint=http://minio:9000&disable_https=true&s3ForcePathStyle=true` |
|
||||
| Cloudflare R2 | Yes | `s3://my-bucket?awssdk=v2&endpoint=https://<account-id>.r2.cloudflarestorage.com&disable_https=false&s3ForcePathStyle=true` |
|
||||
| Backblaze B2 | Yes | `s3://my-bucket?awssdk=v2&endpoint=https://s3.us-west-004.backblazeb2.com&disable_https=false&s3ForcePathStyle=true` |
|
||||
|
||||
::: info
|
||||
If you know of any other S3-compatible storage that works with Homebox, please let us know or create a pull request to update the table.
|
||||
@@ -57,7 +57,7 @@ Additionally, the parameters in the URL can be used to configure specific S3 set
|
||||
features.)
|
||||
- `endpoint`: The custom endpoint for S3-compatible storage services.
|
||||
- `s3ForcePathStyle`: Whether to force path-style access (set to `true` or `false`).
|
||||
- `disableSSL`: Whether to disable SSL (set to `true` or `false`).
|
||||
- `disable_https`: Whether to disable SSL (set to `true` or `false`).
|
||||
- `sseType`: The server-side encryption type (e.g., `AES256` or `aws:kms` or `aws:kms:dsse`).
|
||||
- `kmskeyid`: The KMS key ID for server-side encryption.
|
||||
- `fips`: Whether to use FIPS endpoints (set to `true` or `false`).
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -42,6 +47,10 @@
|
||||
<MdiArrowUp class="hidden group-hover/label-chip:block" />
|
||||
</div>
|
||||
</div>
|
||||
<template v-if="isLabelOut(label) && label.parent">
|
||||
<span class="opacity-70">{{ label.parent.name }}</span>
|
||||
<span class="opacity-50">/</span>
|
||||
</template>
|
||||
{{ label.name }}
|
||||
</NuxtLink>
|
||||
</template>
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
:max-length="1000"
|
||||
/>
|
||||
<ColorSelector v-model="form.color" :label="$t('components.label.create_modal.label_color')" :show-hex="true" />
|
||||
<LabelParentSelector v-model="form.parentId" :labels="labels" />
|
||||
<div class="mt-4 flex flex-row-reverse">
|
||||
<ButtonGroup>
|
||||
<Button :disabled="loading" type="submit">{{ $t("global.create") }}</Button>
|
||||
@@ -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<LabelOut[]>([]);
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
40
frontend/components/Label/ParentSelector.vue
Normal file
40
frontend/components/Label/ParentSelector.vue
Normal file
@@ -0,0 +1,40 @@
|
||||
<template>
|
||||
<div class="flex flex-col gap-1">
|
||||
<Label :for="id">
|
||||
{{ $t('components.label.parent_selector.label') }}
|
||||
</Label>
|
||||
<Select v-model="modelValue">
|
||||
<SelectTrigger :id="id">
|
||||
<SelectValue :placeholder="$t('components.label.parent_selector.placeholder')" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="">{{ $t('components.label.parent_selector.no_parent') }}</SelectItem>
|
||||
<SelectItem v-for="label in props.labels" :key="label.id" :value="label.id">
|
||||
{{ label.name }}
|
||||
</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||
import type { LabelOut } from "~/lib/api/types/data-contracts";
|
||||
|
||||
const id = useId();
|
||||
|
||||
const emit = defineEmits(["update:modelValue"]);
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: String as () => string | null,
|
||||
default: null,
|
||||
},
|
||||
labels: {
|
||||
type: Array as () => LabelOut[],
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
const modelValue = useVModel(props, "modelValue", emit);
|
||||
</script>
|
||||
7
frontend/lib/api/types/data-contracts.ts
generated
7
frontend/lib/api/types/data-contracts.ts
generated
@@ -381,10 +381,14 @@ export interface EntLabel {
|
||||
}
|
||||
|
||||
export interface EntLabelEdges {
|
||||
/** Children holds the value of the children edge. */
|
||||
children: EntLabel[];
|
||||
/** Group holds the value of the group edge. */
|
||||
group: EntGroup;
|
||||
/** Items holds the value of the items edge. */
|
||||
items: EntItem[];
|
||||
/** Parent holds the value of the parent edge. */
|
||||
parent: EntLabel;
|
||||
}
|
||||
|
||||
export interface EntLocation {
|
||||
@@ -857,14 +861,17 @@ export interface LabelCreate {
|
||||
* @maxLength 255
|
||||
*/
|
||||
name: string;
|
||||
parentId?: string | null;
|
||||
}
|
||||
|
||||
export interface LabelOut {
|
||||
children: LabelSummary[];
|
||||
color: string;
|
||||
createdAt: Date | string;
|
||||
description: string;
|
||||
id: string;
|
||||
name: string;
|
||||
parent: LabelSummary;
|
||||
updatedAt: Date | string;
|
||||
}
|
||||
|
||||
|
||||
@@ -202,6 +202,11 @@
|
||||
"label_name_too_long": "Label name must not be longer than 50 characters"
|
||||
}
|
||||
},
|
||||
"parent_selector": {
|
||||
"label": "Parent Label",
|
||||
"no_parent": "No Parent",
|
||||
"placeholder": "Select a parent label"
|
||||
},
|
||||
"selector": {
|
||||
"select_labels": "Select Labels"
|
||||
}
|
||||
@@ -506,8 +511,10 @@
|
||||
"warranty_expires": "Warranty Expires"
|
||||
},
|
||||
"labels": {
|
||||
"child_labels": "Child Labels",
|
||||
"label_delete_confirm": "Are you sure you want to delete this label? This action cannot be undone.",
|
||||
"no_results": "No Labels Found",
|
||||
"parent_label": "Parent Label",
|
||||
"toast": {
|
||||
"failed_delete_label": "Failed to delete label",
|
||||
"failed_load_label": "Failed to load label",
|
||||
|
||||
@@ -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"
|
||||
/>
|
||||
<LabelParentSelector v-if="allLabels" v-model="updateData.parentId" :labels="allLabels.filter(l => l.id !== labelId)" />
|
||||
<DialogFooter>
|
||||
<Button type="submit" :loading="updating"> {{ $t("global.update") }} </Button>
|
||||
</DialogFooter>
|
||||
@@ -204,6 +214,25 @@
|
||||
</header>
|
||||
<Separator v-if="label && label.description" />
|
||||
<Markdown v-if="label && label.description" class="mt-3 text-base" :source="label.description" />
|
||||
|
||||
<!-- Display parent and children -->
|
||||
<div v-if="label && (label.parent || (label.children && label.children.length > 0))" class="mt-3">
|
||||
<Separator />
|
||||
<div class="mt-3">
|
||||
<div v-if="label.parent" class="mb-2">
|
||||
<span class="text-sm font-medium">{{ $t("labels.parent_label") }}:</span>
|
||||
<div class="mt-1">
|
||||
<LabelChip :label="label.parent" size="sm" />
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="label.children && label.children.length > 0">
|
||||
<span class="text-sm font-medium">{{ $t("labels.child_labels") }}:</span>
|
||||
<div class="mt-1 flex flex-wrap gap-2">
|
||||
<LabelChip v-for="child in label.children" :key="child.id" :label="child" size="sm" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
<section v-if="label && items">
|
||||
<ItemViewSelectable :items="items.items" @refresh="refreshItemList" />
|
||||
|
||||
Reference in New Issue
Block a user