mirror of
https://github.com/hay-kot/homebox.git
synced 2024-12-18 21:16:31 +00:00
feat: add archive item options (#122)
Add archive option feature. Archived items can only be seen on the items page when including archived is selected. Archived items are excluded from the count and from other views
This commit is contained in:
parent
c722495fdd
commit
a886fa86ca
27 changed files with 325 additions and 38 deletions
|
@ -47,15 +47,24 @@ func (ctrl *V1Controller) HandleItemsGetAll() server.HandlerFunc {
|
|||
return i
|
||||
}
|
||||
|
||||
getBool := func(s string) bool {
|
||||
b, err := strconv.ParseBool(s)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
extractQuery := func(r *http.Request) repo.ItemQuery {
|
||||
params := r.URL.Query()
|
||||
|
||||
return repo.ItemQuery{
|
||||
Page: intOrNegativeOne(params.Get("page")),
|
||||
PageSize: intOrNegativeOne(params.Get("perPage")),
|
||||
Search: params.Get("q"),
|
||||
LocationIDs: uuidList(params, "locations"),
|
||||
LabelIDs: uuidList(params, "labels"),
|
||||
Page: intOrNegativeOne(params.Get("page")),
|
||||
PageSize: intOrNegativeOne(params.Get("perPage")),
|
||||
Search: params.Get("q"),
|
||||
LocationIDs: uuidList(params, "locations"),
|
||||
LabelIDs: uuidList(params, "labels"),
|
||||
IncludeArchived: getBool(params.Get("includeArchived")),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1274,6 +1274,9 @@ const docTemplate = `{
|
|||
"repo.ItemOut": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"archived": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"attachments": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
|
@ -1383,6 +1386,9 @@ const docTemplate = `{
|
|||
"repo.ItemSummary": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"archived": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"createdAt": {
|
||||
"type": "string"
|
||||
},
|
||||
|
@ -1421,6 +1427,9 @@ const docTemplate = `{
|
|||
"repo.ItemUpdate": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"archived": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
|
|
|
@ -1266,6 +1266,9 @@
|
|||
"repo.ItemOut": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"archived": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"attachments": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
|
@ -1375,6 +1378,9 @@
|
|||
"repo.ItemSummary": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"archived": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"createdAt": {
|
||||
"type": "string"
|
||||
},
|
||||
|
@ -1413,6 +1419,9 @@
|
|||
"repo.ItemUpdate": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"archived": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
|
|
|
@ -85,6 +85,8 @@ definitions:
|
|||
type: object
|
||||
repo.ItemOut:
|
||||
properties:
|
||||
archived:
|
||||
type: boolean
|
||||
attachments:
|
||||
items:
|
||||
$ref: '#/definitions/repo.ItemAttachment'
|
||||
|
@ -161,6 +163,8 @@ definitions:
|
|||
type: object
|
||||
repo.ItemSummary:
|
||||
properties:
|
||||
archived:
|
||||
type: boolean
|
||||
createdAt:
|
||||
type: string
|
||||
description:
|
||||
|
@ -187,6 +191,8 @@ definitions:
|
|||
type: object
|
||||
repo.ItemUpdate:
|
||||
properties:
|
||||
archived:
|
||||
type: boolean
|
||||
description:
|
||||
type: string
|
||||
fields:
|
||||
|
|
|
@ -35,6 +35,8 @@ type Item struct {
|
|||
Quantity int `json:"quantity,omitempty"`
|
||||
// Insured holds the value of the "insured" field.
|
||||
Insured bool `json:"insured,omitempty"`
|
||||
// Archived holds the value of the "archived" field.
|
||||
Archived bool `json:"archived,omitempty"`
|
||||
// SerialNumber holds the value of the "serial_number" field.
|
||||
SerialNumber string `json:"serial_number,omitempty"`
|
||||
// ModelNumber holds the value of the "model_number" field.
|
||||
|
@ -170,7 +172,7 @@ func (*Item) scanValues(columns []string) ([]any, error) {
|
|||
values := make([]any, len(columns))
|
||||
for i := range columns {
|
||||
switch columns[i] {
|
||||
case item.FieldInsured, item.FieldLifetimeWarranty:
|
||||
case item.FieldInsured, item.FieldArchived, item.FieldLifetimeWarranty:
|
||||
values[i] = new(sql.NullBool)
|
||||
case item.FieldPurchasePrice, item.FieldSoldPrice:
|
||||
values[i] = new(sql.NullFloat64)
|
||||
|
@ -257,6 +259,12 @@ func (i *Item) assignValues(columns []string, values []any) error {
|
|||
} else if value.Valid {
|
||||
i.Insured = value.Bool
|
||||
}
|
||||
case item.FieldArchived:
|
||||
if value, ok := values[j].(*sql.NullBool); !ok {
|
||||
return fmt.Errorf("unexpected type %T for field archived", values[j])
|
||||
} else if value.Valid {
|
||||
i.Archived = value.Bool
|
||||
}
|
||||
case item.FieldSerialNumber:
|
||||
if value, ok := values[j].(*sql.NullString); !ok {
|
||||
return fmt.Errorf("unexpected type %T for field serial_number", values[j])
|
||||
|
@ -443,6 +451,9 @@ func (i *Item) String() string {
|
|||
builder.WriteString("insured=")
|
||||
builder.WriteString(fmt.Sprintf("%v", i.Insured))
|
||||
builder.WriteString(", ")
|
||||
builder.WriteString("archived=")
|
||||
builder.WriteString(fmt.Sprintf("%v", i.Archived))
|
||||
builder.WriteString(", ")
|
||||
builder.WriteString("serial_number=")
|
||||
builder.WriteString(i.SerialNumber)
|
||||
builder.WriteString(", ")
|
||||
|
|
|
@ -29,6 +29,8 @@ const (
|
|||
FieldQuantity = "quantity"
|
||||
// FieldInsured holds the string denoting the insured field in the database.
|
||||
FieldInsured = "insured"
|
||||
// FieldArchived holds the string denoting the archived field in the database.
|
||||
FieldArchived = "archived"
|
||||
// FieldSerialNumber holds the string denoting the serial_number field in the database.
|
||||
FieldSerialNumber = "serial_number"
|
||||
// FieldModelNumber holds the string denoting the model_number field in the database.
|
||||
|
@ -125,6 +127,7 @@ var Columns = []string{
|
|||
FieldNotes,
|
||||
FieldQuantity,
|
||||
FieldInsured,
|
||||
FieldArchived,
|
||||
FieldSerialNumber,
|
||||
FieldModelNumber,
|
||||
FieldManufacturer,
|
||||
|
@ -188,6 +191,8 @@ var (
|
|||
DefaultQuantity int
|
||||
// DefaultInsured holds the default value on creation for the "insured" field.
|
||||
DefaultInsured bool
|
||||
// DefaultArchived holds the default value on creation for the "archived" field.
|
||||
DefaultArchived bool
|
||||
// SerialNumberValidator is a validator for the "serial_number" field. It is called by the builders before save.
|
||||
SerialNumberValidator func(string) error
|
||||
// ModelNumberValidator is a validator for the "model_number" field. It is called by the builders before save.
|
||||
|
|
|
@ -138,6 +138,13 @@ func Insured(v bool) predicate.Item {
|
|||
})
|
||||
}
|
||||
|
||||
// Archived applies equality check predicate on the "archived" field. It's identical to ArchivedEQ.
|
||||
func Archived(v bool) predicate.Item {
|
||||
return predicate.Item(func(s *sql.Selector) {
|
||||
s.Where(sql.EQ(s.C(FieldArchived), v))
|
||||
})
|
||||
}
|
||||
|
||||
// SerialNumber applies equality check predicate on the "serial_number" field. It's identical to SerialNumberEQ.
|
||||
func SerialNumber(v string) predicate.Item {
|
||||
return predicate.Item(func(s *sql.Selector) {
|
||||
|
@ -873,6 +880,20 @@ func InsuredNEQ(v bool) predicate.Item {
|
|||
})
|
||||
}
|
||||
|
||||
// ArchivedEQ applies the EQ predicate on the "archived" field.
|
||||
func ArchivedEQ(v bool) predicate.Item {
|
||||
return predicate.Item(func(s *sql.Selector) {
|
||||
s.Where(sql.EQ(s.C(FieldArchived), v))
|
||||
})
|
||||
}
|
||||
|
||||
// ArchivedNEQ applies the NEQ predicate on the "archived" field.
|
||||
func ArchivedNEQ(v bool) predicate.Item {
|
||||
return predicate.Item(func(s *sql.Selector) {
|
||||
s.Where(sql.NEQ(s.C(FieldArchived), v))
|
||||
})
|
||||
}
|
||||
|
||||
// SerialNumberEQ applies the EQ predicate on the "serial_number" field.
|
||||
func SerialNumberEQ(v string) predicate.Item {
|
||||
return predicate.Item(func(s *sql.Selector) {
|
||||
|
|
|
@ -130,6 +130,20 @@ func (ic *ItemCreate) SetNillableInsured(b *bool) *ItemCreate {
|
|||
return ic
|
||||
}
|
||||
|
||||
// SetArchived sets the "archived" field.
|
||||
func (ic *ItemCreate) SetArchived(b bool) *ItemCreate {
|
||||
ic.mutation.SetArchived(b)
|
||||
return ic
|
||||
}
|
||||
|
||||
// SetNillableArchived sets the "archived" field if the given value is not nil.
|
||||
func (ic *ItemCreate) SetNillableArchived(b *bool) *ItemCreate {
|
||||
if b != nil {
|
||||
ic.SetArchived(*b)
|
||||
}
|
||||
return ic
|
||||
}
|
||||
|
||||
// SetSerialNumber sets the "serial_number" field.
|
||||
func (ic *ItemCreate) SetSerialNumber(s string) *ItemCreate {
|
||||
ic.mutation.SetSerialNumber(s)
|
||||
|
@ -528,6 +542,10 @@ func (ic *ItemCreate) defaults() {
|
|||
v := item.DefaultInsured
|
||||
ic.mutation.SetInsured(v)
|
||||
}
|
||||
if _, ok := ic.mutation.Archived(); !ok {
|
||||
v := item.DefaultArchived
|
||||
ic.mutation.SetArchived(v)
|
||||
}
|
||||
if _, ok := ic.mutation.LifetimeWarranty(); !ok {
|
||||
v := item.DefaultLifetimeWarranty
|
||||
ic.mutation.SetLifetimeWarranty(v)
|
||||
|
@ -583,6 +601,9 @@ func (ic *ItemCreate) check() error {
|
|||
if _, ok := ic.mutation.Insured(); !ok {
|
||||
return &ValidationError{Name: "insured", err: errors.New(`ent: missing required field "Item.insured"`)}
|
||||
}
|
||||
if _, ok := ic.mutation.Archived(); !ok {
|
||||
return &ValidationError{Name: "archived", err: errors.New(`ent: missing required field "Item.archived"`)}
|
||||
}
|
||||
if v, ok := ic.mutation.SerialNumber(); ok {
|
||||
if err := item.SerialNumberValidator(v); err != nil {
|
||||
return &ValidationError{Name: "serial_number", err: fmt.Errorf(`ent: validator failed for field "Item.serial_number": %w`, err)}
|
||||
|
@ -720,6 +741,14 @@ func (ic *ItemCreate) createSpec() (*Item, *sqlgraph.CreateSpec) {
|
|||
})
|
||||
_node.Insured = value
|
||||
}
|
||||
if value, ok := ic.mutation.Archived(); ok {
|
||||
_spec.Fields = append(_spec.Fields, &sqlgraph.FieldSpec{
|
||||
Type: field.TypeBool,
|
||||
Value: value,
|
||||
Column: item.FieldArchived,
|
||||
})
|
||||
_node.Archived = value
|
||||
}
|
||||
if value, ok := ic.mutation.SerialNumber(); ok {
|
||||
_spec.Fields = append(_spec.Fields, &sqlgraph.FieldSpec{
|
||||
Type: field.TypeString,
|
||||
|
|
|
@ -121,6 +121,20 @@ func (iu *ItemUpdate) SetNillableInsured(b *bool) *ItemUpdate {
|
|||
return iu
|
||||
}
|
||||
|
||||
// SetArchived sets the "archived" field.
|
||||
func (iu *ItemUpdate) SetArchived(b bool) *ItemUpdate {
|
||||
iu.mutation.SetArchived(b)
|
||||
return iu
|
||||
}
|
||||
|
||||
// SetNillableArchived sets the "archived" field if the given value is not nil.
|
||||
func (iu *ItemUpdate) SetNillableArchived(b *bool) *ItemUpdate {
|
||||
if b != nil {
|
||||
iu.SetArchived(*b)
|
||||
}
|
||||
return iu
|
||||
}
|
||||
|
||||
// SetSerialNumber sets the "serial_number" field.
|
||||
func (iu *ItemUpdate) SetSerialNumber(s string) *ItemUpdate {
|
||||
iu.mutation.SetSerialNumber(s)
|
||||
|
@ -795,6 +809,13 @@ func (iu *ItemUpdate) sqlSave(ctx context.Context) (n int, err error) {
|
|||
Column: item.FieldInsured,
|
||||
})
|
||||
}
|
||||
if value, ok := iu.mutation.Archived(); ok {
|
||||
_spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{
|
||||
Type: field.TypeBool,
|
||||
Value: value,
|
||||
Column: item.FieldArchived,
|
||||
})
|
||||
}
|
||||
if value, ok := iu.mutation.SerialNumber(); ok {
|
||||
_spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{
|
||||
Type: field.TypeString,
|
||||
|
@ -1387,6 +1408,20 @@ func (iuo *ItemUpdateOne) SetNillableInsured(b *bool) *ItemUpdateOne {
|
|||
return iuo
|
||||
}
|
||||
|
||||
// SetArchived sets the "archived" field.
|
||||
func (iuo *ItemUpdateOne) SetArchived(b bool) *ItemUpdateOne {
|
||||
iuo.mutation.SetArchived(b)
|
||||
return iuo
|
||||
}
|
||||
|
||||
// SetNillableArchived sets the "archived" field if the given value is not nil.
|
||||
func (iuo *ItemUpdateOne) SetNillableArchived(b *bool) *ItemUpdateOne {
|
||||
if b != nil {
|
||||
iuo.SetArchived(*b)
|
||||
}
|
||||
return iuo
|
||||
}
|
||||
|
||||
// SetSerialNumber sets the "serial_number" field.
|
||||
func (iuo *ItemUpdateOne) SetSerialNumber(s string) *ItemUpdateOne {
|
||||
iuo.mutation.SetSerialNumber(s)
|
||||
|
@ -2091,6 +2126,13 @@ func (iuo *ItemUpdateOne) sqlSave(ctx context.Context) (_node *Item, err error)
|
|||
Column: item.FieldInsured,
|
||||
})
|
||||
}
|
||||
if value, ok := iuo.mutation.Archived(); ok {
|
||||
_spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{
|
||||
Type: field.TypeBool,
|
||||
Value: value,
|
||||
Column: item.FieldArchived,
|
||||
})
|
||||
}
|
||||
if value, ok := iuo.mutation.SerialNumber(); ok {
|
||||
_spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{
|
||||
Type: field.TypeString,
|
||||
|
|
|
@ -170,6 +170,7 @@ var (
|
|||
{Name: "notes", Type: field.TypeString, Nullable: true, Size: 1000},
|
||||
{Name: "quantity", Type: field.TypeInt, Default: 1},
|
||||
{Name: "insured", Type: field.TypeBool, Default: false},
|
||||
{Name: "archived", Type: field.TypeBool, Default: false},
|
||||
{Name: "serial_number", Type: field.TypeString, Nullable: true, Size: 255},
|
||||
{Name: "model_number", Type: field.TypeString, Nullable: true, Size: 255},
|
||||
{Name: "manufacturer", Type: field.TypeString, Nullable: true, Size: 255},
|
||||
|
@ -195,19 +196,19 @@ var (
|
|||
ForeignKeys: []*schema.ForeignKey{
|
||||
{
|
||||
Symbol: "items_groups_items",
|
||||
Columns: []*schema.Column{ItemsColumns[22]},
|
||||
Columns: []*schema.Column{ItemsColumns[23]},
|
||||
RefColumns: []*schema.Column{GroupsColumns[0]},
|
||||
OnDelete: schema.Cascade,
|
||||
},
|
||||
{
|
||||
Symbol: "items_items_children",
|
||||
Columns: []*schema.Column{ItemsColumns[23]},
|
||||
Columns: []*schema.Column{ItemsColumns[24]},
|
||||
RefColumns: []*schema.Column{ItemsColumns[0]},
|
||||
OnDelete: schema.SetNull,
|
||||
},
|
||||
{
|
||||
Symbol: "items_locations_items",
|
||||
Columns: []*schema.Column{ItemsColumns[24]},
|
||||
Columns: []*schema.Column{ItemsColumns[25]},
|
||||
RefColumns: []*schema.Column{LocationsColumns[0]},
|
||||
OnDelete: schema.Cascade,
|
||||
},
|
||||
|
@ -221,16 +222,21 @@ var (
|
|||
{
|
||||
Name: "item_manufacturer",
|
||||
Unique: false,
|
||||
Columns: []*schema.Column{ItemsColumns[11]},
|
||||
Columns: []*schema.Column{ItemsColumns[12]},
|
||||
},
|
||||
{
|
||||
Name: "item_model_number",
|
||||
Unique: false,
|
||||
Columns: []*schema.Column{ItemsColumns[10]},
|
||||
Columns: []*schema.Column{ItemsColumns[11]},
|
||||
},
|
||||
{
|
||||
Name: "item_serial_number",
|
||||
Unique: false,
|
||||
Columns: []*schema.Column{ItemsColumns[10]},
|
||||
},
|
||||
{
|
||||
Name: "item_archived",
|
||||
Unique: false,
|
||||
Columns: []*schema.Column{ItemsColumns[9]},
|
||||
},
|
||||
},
|
||||
|
|
|
@ -4133,6 +4133,7 @@ type ItemMutation struct {
|
|||
quantity *int
|
||||
addquantity *int
|
||||
insured *bool
|
||||
archived *bool
|
||||
serial_number *string
|
||||
model_number *string
|
||||
manufacturer *string
|
||||
|
@ -4623,6 +4624,42 @@ func (m *ItemMutation) ResetInsured() {
|
|||
m.insured = nil
|
||||
}
|
||||
|
||||
// SetArchived sets the "archived" field.
|
||||
func (m *ItemMutation) SetArchived(b bool) {
|
||||
m.archived = &b
|
||||
}
|
||||
|
||||
// Archived returns the value of the "archived" field in the mutation.
|
||||
func (m *ItemMutation) Archived() (r bool, exists bool) {
|
||||
v := m.archived
|
||||
if v == nil {
|
||||
return
|
||||
}
|
||||
return *v, true
|
||||
}
|
||||
|
||||
// OldArchived returns the old "archived" field's value of the Item entity.
|
||||
// If the Item object wasn't provided to the builder, the object is fetched from the database.
|
||||
// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
|
||||
func (m *ItemMutation) OldArchived(ctx context.Context) (v bool, err error) {
|
||||
if !m.op.Is(OpUpdateOne) {
|
||||
return v, errors.New("OldArchived is only allowed on UpdateOne operations")
|
||||
}
|
||||
if m.id == nil || m.oldValue == nil {
|
||||
return v, errors.New("OldArchived requires an ID field in the mutation")
|
||||
}
|
||||
oldValue, err := m.oldValue(ctx)
|
||||
if err != nil {
|
||||
return v, fmt.Errorf("querying old value for OldArchived: %w", err)
|
||||
}
|
||||
return oldValue.Archived, nil
|
||||
}
|
||||
|
||||
// ResetArchived resets all changes to the "archived" field.
|
||||
func (m *ItemMutation) ResetArchived() {
|
||||
m.archived = nil
|
||||
}
|
||||
|
||||
// SetSerialNumber sets the "serial_number" field.
|
||||
func (m *ItemMutation) SetSerialNumber(s string) {
|
||||
m.serial_number = &s
|
||||
|
@ -5613,7 +5650,7 @@ func (m *ItemMutation) Type() string {
|
|||
// order to get all numeric fields that were incremented/decremented, call
|
||||
// AddedFields().
|
||||
func (m *ItemMutation) Fields() []string {
|
||||
fields := make([]string, 0, 21)
|
||||
fields := make([]string, 0, 22)
|
||||
if m.created_at != nil {
|
||||
fields = append(fields, item.FieldCreatedAt)
|
||||
}
|
||||
|
@ -5638,6 +5675,9 @@ func (m *ItemMutation) Fields() []string {
|
|||
if m.insured != nil {
|
||||
fields = append(fields, item.FieldInsured)
|
||||
}
|
||||
if m.archived != nil {
|
||||
fields = append(fields, item.FieldArchived)
|
||||
}
|
||||
if m.serial_number != nil {
|
||||
fields = append(fields, item.FieldSerialNumber)
|
||||
}
|
||||
|
@ -5701,6 +5741,8 @@ func (m *ItemMutation) Field(name string) (ent.Value, bool) {
|
|||
return m.Quantity()
|
||||
case item.FieldInsured:
|
||||
return m.Insured()
|
||||
case item.FieldArchived:
|
||||
return m.Archived()
|
||||
case item.FieldSerialNumber:
|
||||
return m.SerialNumber()
|
||||
case item.FieldModelNumber:
|
||||
|
@ -5752,6 +5794,8 @@ func (m *ItemMutation) OldField(ctx context.Context, name string) (ent.Value, er
|
|||
return m.OldQuantity(ctx)
|
||||
case item.FieldInsured:
|
||||
return m.OldInsured(ctx)
|
||||
case item.FieldArchived:
|
||||
return m.OldArchived(ctx)
|
||||
case item.FieldSerialNumber:
|
||||
return m.OldSerialNumber(ctx)
|
||||
case item.FieldModelNumber:
|
||||
|
@ -5843,6 +5887,13 @@ func (m *ItemMutation) SetField(name string, value ent.Value) error {
|
|||
}
|
||||
m.SetInsured(v)
|
||||
return nil
|
||||
case item.FieldArchived:
|
||||
v, ok := value.(bool)
|
||||
if !ok {
|
||||
return fmt.Errorf("unexpected type %T for field %s", value, name)
|
||||
}
|
||||
m.SetArchived(v)
|
||||
return nil
|
||||
case item.FieldSerialNumber:
|
||||
v, ok := value.(string)
|
||||
if !ok {
|
||||
|
@ -6127,6 +6178,9 @@ func (m *ItemMutation) ResetField(name string) error {
|
|||
case item.FieldInsured:
|
||||
m.ResetInsured()
|
||||
return nil
|
||||
case item.FieldArchived:
|
||||
m.ResetArchived()
|
||||
return nil
|
||||
case item.FieldSerialNumber:
|
||||
m.ResetSerialNumber()
|
||||
return nil
|
||||
|
|
|
@ -271,36 +271,40 @@ func init() {
|
|||
itemDescInsured := itemFields[3].Descriptor()
|
||||
// item.DefaultInsured holds the default value on creation for the insured field.
|
||||
item.DefaultInsured = itemDescInsured.Default.(bool)
|
||||
// itemDescArchived is the schema descriptor for archived field.
|
||||
itemDescArchived := itemFields[4].Descriptor()
|
||||
// item.DefaultArchived holds the default value on creation for the archived field.
|
||||
item.DefaultArchived = itemDescArchived.Default.(bool)
|
||||
// itemDescSerialNumber is the schema descriptor for serial_number field.
|
||||
itemDescSerialNumber := itemFields[4].Descriptor()
|
||||
itemDescSerialNumber := itemFields[5].Descriptor()
|
||||
// item.SerialNumberValidator is a validator for the "serial_number" field. It is called by the builders before save.
|
||||
item.SerialNumberValidator = itemDescSerialNumber.Validators[0].(func(string) error)
|
||||
// itemDescModelNumber is the schema descriptor for model_number field.
|
||||
itemDescModelNumber := itemFields[5].Descriptor()
|
||||
itemDescModelNumber := itemFields[6].Descriptor()
|
||||
// item.ModelNumberValidator is a validator for the "model_number" field. It is called by the builders before save.
|
||||
item.ModelNumberValidator = itemDescModelNumber.Validators[0].(func(string) error)
|
||||
// itemDescManufacturer is the schema descriptor for manufacturer field.
|
||||
itemDescManufacturer := itemFields[6].Descriptor()
|
||||
itemDescManufacturer := itemFields[7].Descriptor()
|
||||
// item.ManufacturerValidator is a validator for the "manufacturer" field. It is called by the builders before save.
|
||||
item.ManufacturerValidator = itemDescManufacturer.Validators[0].(func(string) error)
|
||||
// itemDescLifetimeWarranty is the schema descriptor for lifetime_warranty field.
|
||||
itemDescLifetimeWarranty := itemFields[7].Descriptor()
|
||||
itemDescLifetimeWarranty := itemFields[8].Descriptor()
|
||||
// item.DefaultLifetimeWarranty holds the default value on creation for the lifetime_warranty field.
|
||||
item.DefaultLifetimeWarranty = itemDescLifetimeWarranty.Default.(bool)
|
||||
// itemDescWarrantyDetails is the schema descriptor for warranty_details field.
|
||||
itemDescWarrantyDetails := itemFields[9].Descriptor()
|
||||
itemDescWarrantyDetails := itemFields[10].Descriptor()
|
||||
// item.WarrantyDetailsValidator is a validator for the "warranty_details" field. It is called by the builders before save.
|
||||
item.WarrantyDetailsValidator = itemDescWarrantyDetails.Validators[0].(func(string) error)
|
||||
// itemDescPurchasePrice is the schema descriptor for purchase_price field.
|
||||
itemDescPurchasePrice := itemFields[12].Descriptor()
|
||||
itemDescPurchasePrice := itemFields[13].Descriptor()
|
||||
// item.DefaultPurchasePrice holds the default value on creation for the purchase_price field.
|
||||
item.DefaultPurchasePrice = itemDescPurchasePrice.Default.(float64)
|
||||
// itemDescSoldPrice is the schema descriptor for sold_price field.
|
||||
itemDescSoldPrice := itemFields[15].Descriptor()
|
||||
itemDescSoldPrice := itemFields[16].Descriptor()
|
||||
// item.DefaultSoldPrice holds the default value on creation for the sold_price field.
|
||||
item.DefaultSoldPrice = itemDescSoldPrice.Default.(float64)
|
||||
// itemDescSoldNotes is the schema descriptor for sold_notes field.
|
||||
itemDescSoldNotes := itemFields[16].Descriptor()
|
||||
itemDescSoldNotes := itemFields[17].Descriptor()
|
||||
// item.SoldNotesValidator is a validator for the "sold_notes" field. It is called by the builders before save.
|
||||
item.SoldNotesValidator = itemDescSoldNotes.Validators[0].(func(string) error)
|
||||
// itemDescID is the schema descriptor for id field.
|
||||
|
|
|
@ -28,6 +28,7 @@ func (Item) Indexes() []ent.Index {
|
|||
index.Fields("manufacturer"),
|
||||
index.Fields("model_number"),
|
||||
index.Fields("serial_number"),
|
||||
index.Fields("archived"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -45,6 +46,8 @@ func (Item) Fields() []ent.Field {
|
|||
Default(1),
|
||||
field.Bool("insured").
|
||||
Default(false),
|
||||
field.Bool("archived").
|
||||
Default(false),
|
||||
|
||||
// ------------------------------------
|
||||
// item identification
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
-- disable the enforcement of foreign-keys constraints
|
||||
PRAGMA foreign_keys = off;
|
||||
-- create "new_items" table
|
||||
CREATE TABLE `new_items` (`id` uuid NOT NULL, `created_at` datetime NOT NULL, `updated_at` datetime NOT NULL, `name` text NOT NULL, `description` text NULL, `import_ref` text NULL, `notes` text NULL, `quantity` integer NOT NULL DEFAULT 1, `insured` bool NOT NULL DEFAULT false, `archived` bool NOT NULL DEFAULT false, `serial_number` text NULL, `model_number` text NULL, `manufacturer` text NULL, `lifetime_warranty` bool NOT NULL DEFAULT false, `warranty_expires` datetime NULL, `warranty_details` text NULL, `purchase_time` datetime NULL, `purchase_from` text NULL, `purchase_price` real NOT NULL DEFAULT 0, `sold_time` datetime NULL, `sold_to` text NULL, `sold_price` real NOT NULL DEFAULT 0, `sold_notes` text NULL, `group_items` uuid NOT NULL, `item_children` uuid NULL, `location_items` uuid NULL, PRIMARY KEY (`id`), CONSTRAINT `items_groups_items` FOREIGN KEY (`group_items`) REFERENCES `groups` (`id`) ON DELETE CASCADE, CONSTRAINT `items_items_children` FOREIGN KEY (`item_children`) REFERENCES `items` (`id`) ON DELETE SET NULL, CONSTRAINT `items_locations_items` FOREIGN KEY (`location_items`) REFERENCES `locations` (`id`) ON DELETE CASCADE);
|
||||
-- copy rows from old table "items" to new temporary table "new_items"
|
||||
INSERT INTO `new_items` (`id`, `created_at`, `updated_at`, `name`, `description`, `import_ref`, `notes`, `quantity`, `insured`, `serial_number`, `model_number`, `manufacturer`, `lifetime_warranty`, `warranty_expires`, `warranty_details`, `purchase_time`, `purchase_from`, `purchase_price`, `sold_time`, `sold_to`, `sold_price`, `sold_notes`, `group_items`, `item_children`, `location_items`) SELECT `id`, `created_at`, `updated_at`, `name`, `description`, `import_ref`, `notes`, `quantity`, `insured`, `serial_number`, `model_number`, `manufacturer`, `lifetime_warranty`, `warranty_expires`, `warranty_details`, `purchase_time`, `purchase_from`, `purchase_price`, `sold_time`, `sold_to`, `sold_price`, `sold_notes`, `group_items`, `item_children`, `location_items` FROM `items`;
|
||||
-- drop "items" table after copying rows
|
||||
DROP TABLE `items`;
|
||||
-- rename temporary table "new_items" to "items"
|
||||
ALTER TABLE `new_items` RENAME TO `items`;
|
||||
-- create index "item_name" to table: "items"
|
||||
CREATE INDEX `item_name` ON `items` (`name`);
|
||||
-- create index "item_manufacturer" to table: "items"
|
||||
CREATE INDEX `item_manufacturer` ON `items` (`manufacturer`);
|
||||
-- create index "item_model_number" to table: "items"
|
||||
CREATE INDEX `item_model_number` ON `items` (`model_number`);
|
||||
-- create index "item_serial_number" to table: "items"
|
||||
CREATE INDEX `item_serial_number` ON `items` (`serial_number`);
|
||||
-- create index "item_archived" to table: "items"
|
||||
CREATE INDEX `item_archived` ON `items` (`archived`);
|
||||
-- enable back the enforcement of foreign-keys constraints
|
||||
PRAGMA foreign_keys = on;
|
|
@ -1,5 +1,6 @@
|
|||
h1:mYTnmyrnBDST/r93NGJM33mIJqhp/U9qR440zI99eqQ=
|
||||
h1:i76VRMDIPdcmQtXTe9bzrgITAzLGjjVy9y8XaXIchAs=
|
||||
20220929052825_init.sql h1:ZlCqm1wzjDmofeAcSX3jE4h4VcdTNGpRg2eabztDy9Q=
|
||||
20221001210956_group_invitations.sql h1:YQKJFtE39wFOcRNbZQ/d+ZlHwrcfcsZlcv/pLEYdpjw=
|
||||
20221009173029_add_user_roles.sql h1:vWmzAfgEWQeGk0Vn70zfVPCcfEZth3E0JcvyKTjpYyU=
|
||||
20221020043305_allow_nesting_types.sql h1:4AyJpZ7l7SSJtJAQETYY802FHJ64ufYPJTqvwdiGn3M=
|
||||
20221101041931_add_archived_field.sql h1:L2WxiOh1svRn817cNURgqnEQg6DIcodZ1twK4tvxW94=
|
||||
|
|
|
@ -20,12 +20,13 @@ type ItemsRepository struct {
|
|||
|
||||
type (
|
||||
ItemQuery struct {
|
||||
Page int
|
||||
PageSize int
|
||||
Search string `json:"search"`
|
||||
LocationIDs []uuid.UUID `json:"locationIds"`
|
||||
LabelIDs []uuid.UUID `json:"labelIds"`
|
||||
SortBy string `json:"sortBy"`
|
||||
Page int
|
||||
PageSize int
|
||||
Search string `json:"search"`
|
||||
LocationIDs []uuid.UUID `json:"locationIds"`
|
||||
LabelIDs []uuid.UUID `json:"labelIds"`
|
||||
SortBy string `json:"sortBy"`
|
||||
IncludeArchived bool `json:"includeArchived"`
|
||||
}
|
||||
|
||||
ItemField struct {
|
||||
|
@ -55,6 +56,7 @@ type (
|
|||
Description string `json:"description"`
|
||||
Quantity int `json:"quantity"`
|
||||
Insured bool `json:"insured"`
|
||||
Archived bool `json:"archived"`
|
||||
|
||||
// Edges
|
||||
LocationID uuid.UUID `json:"locationId"`
|
||||
|
@ -93,6 +95,7 @@ type (
|
|||
Description string `json:"description"`
|
||||
Quantity int `json:"quantity"`
|
||||
Insured bool `json:"insured"`
|
||||
Archived bool `json:"archived"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
|
||||
|
@ -157,6 +160,7 @@ func mapItemSummary(item *ent.Item) ItemSummary {
|
|||
Quantity: item.Quantity,
|
||||
CreatedAt: item.CreatedAt,
|
||||
UpdatedAt: item.UpdatedAt,
|
||||
Archived: item.Archived,
|
||||
|
||||
// Edges
|
||||
Location: location,
|
||||
|
@ -276,7 +280,20 @@ func (e *ItemsRepository) GetOneByGroup(ctx context.Context, gid, id uuid.UUID)
|
|||
|
||||
// QueryByGroup returns a list of items that belong to a specific group based on the provided query.
|
||||
func (e *ItemsRepository) QueryByGroup(ctx context.Context, gid uuid.UUID, q ItemQuery) (PaginationResult[ItemSummary], error) {
|
||||
qb := e.db.Item.Query().Where(item.HasGroupWith(group.ID(gid)))
|
||||
qb := e.db.Item.Query().Where(
|
||||
item.HasGroupWith(group.ID(gid)),
|
||||
)
|
||||
|
||||
if q.IncludeArchived {
|
||||
qb = qb.Where(
|
||||
item.Or(
|
||||
item.Archived(true),
|
||||
item.Archived(false),
|
||||
),
|
||||
)
|
||||
} else {
|
||||
qb = qb.Where(item.Archived(false))
|
||||
}
|
||||
|
||||
if len(q.LabelIDs) > 0 {
|
||||
labels := make([]predicate.Item, 0, len(q.LabelIDs))
|
||||
|
@ -384,6 +401,7 @@ func (e *ItemsRepository) UpdateByGroup(ctx context.Context, gid uuid.UUID, data
|
|||
SetSerialNumber(data.SerialNumber).
|
||||
SetModelNumber(data.ModelNumber).
|
||||
SetManufacturer(data.Manufacturer).
|
||||
SetArchived(data.Archived).
|
||||
SetPurchaseTime(data.PurchaseTime).
|
||||
SetPurchaseFrom(data.PurchaseFrom).
|
||||
SetPurchasePrice(data.PurchasePrice).
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/group"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/item"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/label"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/predicate"
|
||||
)
|
||||
|
@ -68,7 +69,9 @@ func (r *LabelRepository) getOne(ctx context.Context, where ...predicate.Label)
|
|||
return mapLabelOutErr(r.db.Label.Query().
|
||||
Where(where...).
|
||||
WithGroup().
|
||||
WithItems().
|
||||
WithItems(func(iq *ent.ItemQuery) {
|
||||
iq.Where(item.Archived(false))
|
||||
}).
|
||||
Only(ctx),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/group"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/item"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/location"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/predicate"
|
||||
)
|
||||
|
@ -105,6 +106,7 @@ func (r *LocationRepository) GetAll(ctx context.Context, groupId uuid.UUID) ([]L
|
|||
items
|
||||
WHERE
|
||||
items.location_items = locations.id
|
||||
AND items.archived = false
|
||||
) as item_count
|
||||
FROM
|
||||
locations
|
||||
|
@ -139,7 +141,7 @@ func (r *LocationRepository) getOne(ctx context.Context, where ...predicate.Loca
|
|||
Where(where...).
|
||||
WithGroup().
|
||||
WithItems(func(iq *ent.ItemQuery) {
|
||||
iq.WithLabel()
|
||||
iq.Where(item.Archived(false)).WithLabel()
|
||||
}).
|
||||
WithParent().
|
||||
WithChildren().
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<div v-if="!inline" class="form-control w-full">
|
||||
<label class="label cursor-pointer">
|
||||
<span class="label-text"> {{ label }}</span>
|
||||
<input v-model="value" type="checkbox" class="checkbox" />
|
||||
<input v-model="value" type="checkbox" class="checkbox checkbox-primary" />
|
||||
</label>
|
||||
</div>
|
||||
<div v-else class="label cursor-pointer sm:grid sm:grid-cols-4 sm:items-start sm:gap-4">
|
||||
|
@ -11,7 +11,7 @@
|
|||
{{ label }}
|
||||
</span>
|
||||
</label>
|
||||
<input v-model="value" type="checkbox" class="checkbox" />
|
||||
<input v-model="value" type="checkbox" class="checkbox checkbox-primary" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
<h2 class="card-title">
|
||||
<Icon name="mdi-package-variant" />
|
||||
{{ item.name }}
|
||||
<Icon v-if="item.archived" class="ml-auto" name="mdi-archive-outline" />
|
||||
</h2>
|
||||
<p>{{ description }}</p>
|
||||
<div class="flex gap-2 flex-wrap justify-end">
|
||||
|
|
3
frontend/components/global/Spacer.vue
Normal file
3
frontend/components/global/Spacer.vue
Normal file
|
@ -0,0 +1,3 @@
|
|||
<template>
|
||||
<div class="grow-1 max-w-full"></div>
|
||||
</template>
|
|
@ -10,13 +10,19 @@ export function useItemSearch(client: UserClient, opts?: SearchOptions) {
|
|||
const locations = ref<LocationSummary[]>([]);
|
||||
const labels = ref<LabelSummary[]>([]);
|
||||
const results = ref<ItemSummary[]>([]);
|
||||
const includeArchived = ref(false);
|
||||
|
||||
watchDebounced(query, search, { debounce: 250, maxWait: 1000 });
|
||||
async function search() {
|
||||
const locIds = locations.value.map(l => l.id);
|
||||
const labelIds = labels.value.map(l => l.id);
|
||||
|
||||
const { data, error } = await client.items.getAll({ q: query.value, locations: locIds, labels: labelIds });
|
||||
const { data, error } = await client.items.getAll({
|
||||
q: query.value,
|
||||
locations: locIds,
|
||||
labels: labelIds,
|
||||
includeArchived: includeArchived.value,
|
||||
});
|
||||
if (error) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import {
|
|||
import { AttachmentTypes, PaginationResult } from "../types/non-generated";
|
||||
|
||||
export type ItemsQuery = {
|
||||
includeArchived?: boolean;
|
||||
page?: number;
|
||||
pageSize?: number;
|
||||
locations?: string[];
|
||||
|
|
|
@ -63,6 +63,7 @@ export interface ItemField {
|
|||
}
|
||||
|
||||
export interface ItemOut {
|
||||
archived: boolean;
|
||||
attachments: ItemAttachment[];
|
||||
children: ItemSummary[];
|
||||
createdAt: Date;
|
||||
|
@ -107,6 +108,7 @@ export interface ItemOut {
|
|||
}
|
||||
|
||||
export interface ItemSummary {
|
||||
archived: boolean;
|
||||
createdAt: Date;
|
||||
description: string;
|
||||
id: string;
|
||||
|
@ -121,6 +123,7 @@ export interface ItemSummary {
|
|||
}
|
||||
|
||||
export interface ItemUpdate {
|
||||
archived: boolean;
|
||||
description: string;
|
||||
fields: ItemField[];
|
||||
id: string;
|
||||
|
|
|
@ -115,6 +115,11 @@
|
|||
label: "Insured",
|
||||
ref: "insured",
|
||||
},
|
||||
{
|
||||
type: "checkbox",
|
||||
label: "Archived",
|
||||
ref: "archived",
|
||||
},
|
||||
];
|
||||
|
||||
const purchaseFields: FormField[] = [
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script setup lang="ts">
|
||||
import { Detail, Details } from "~~/components/global/DetailsSection/types";
|
||||
import { CustomDetail, Detail, Details } from "~~/components/global/DetailsSection/types";
|
||||
import { ItemAttachment } from "~~/lib/api/types/data-contracts";
|
||||
|
||||
definePageMeta({
|
||||
|
@ -70,7 +70,7 @@
|
|||
);
|
||||
});
|
||||
|
||||
const itemDetails = computed(() => {
|
||||
const itemDetails = computed<Details>(() => {
|
||||
return [
|
||||
{
|
||||
name: "Description",
|
||||
|
@ -107,11 +107,11 @@
|
|||
const url = maybeUrl(field.textValue);
|
||||
if (url.isUrl) {
|
||||
return {
|
||||
type: "link",
|
||||
name: field.name,
|
||||
text: url.text,
|
||||
type: "link",
|
||||
href: url.url,
|
||||
};
|
||||
} as CustomDetail;
|
||||
}
|
||||
|
||||
return {
|
||||
|
|
|
@ -23,7 +23,12 @@
|
|||
const locations = selectedLocations.value.map(l => l.id);
|
||||
const labels = selectedLabels.value.map(l => l.id);
|
||||
|
||||
const { data, error } = await api.items.getAll({ q: query.value, locations, labels });
|
||||
const { data, error } = await api.items.getAll({
|
||||
q: query.value,
|
||||
locations,
|
||||
labels,
|
||||
includeArchived: includeArchived.value,
|
||||
});
|
||||
if (error) {
|
||||
loading.value = false;
|
||||
return;
|
||||
|
@ -46,6 +51,7 @@
|
|||
const advanced = ref(false);
|
||||
const selectedLocations = ref([]);
|
||||
const selectedLabels = ref([]);
|
||||
const includeArchived = ref(false);
|
||||
|
||||
watchEffect(() => {
|
||||
if (!advanced.value) {
|
||||
|
@ -57,6 +63,7 @@
|
|||
watchDebounced(query, search, { debounce: 250, maxWait: 1000 });
|
||||
watchDebounced(selectedLocations, search, { debounce: 250, maxWait: 1000 });
|
||||
watchDebounced(selectedLabels, search, { debounce: 250, maxWait: 1000 });
|
||||
watch(includeArchived, search);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -77,6 +84,13 @@
|
|||
<div class="px-4 pb-4">
|
||||
<FormMultiselect v-model="selectedLabels" label="Labels" :items="labels ?? []" />
|
||||
<FormMultiselect v-model="selectedLocations" label="Locations" :items="locations ?? []" />
|
||||
<div class="flex pb-2 pt-5">
|
||||
<label class="label cursor-pointer mr-auto">
|
||||
<input v-model="includeArchived" type="checkbox" class="toggle toggle-primary" />
|
||||
<span class="label-text ml-4"> Include Archived Items </span>
|
||||
</label>
|
||||
<Spacer />
|
||||
</div>
|
||||
</div>
|
||||
</BaseCard>
|
||||
<section class="mt-10">
|
||||
|
|
Loading…
Reference in a new issue