mirror of
https://github.com/sysadminsmedia/homebox.git
synced 2025-12-28 16:06:37 +01:00
* feat: Add item templates feature (#435) Add ability to create and manage item templates for quick item creation. Templates store default values and custom fields that can be applied when creating new items. Backend changes: - New ItemTemplate and TemplateField Ent schemas - Template CRUD API endpoints - Create item from template endpoint Frontend changes: - Templates management page with create/edit/delete - Template selector in item creation modal - 'Use as Template' action on item detail page - Templates link in navigation menu * refactor: Improve template item creation with a single query - Add `CreateFromTemplate` method to ItemsRepository that creates items with all template data (including custom fields) in a single atomic transaction, replacing the previous two-phase create-then-update pattern - Fix `GetOne` to require group ID parameter so templates can only be accessed by users in the owning group (security fix) - Simplify `HandleItemTemplatesCreateItem` handler using the new transactional method * Refactor item template types and formatting Updated type annotations in CreateModal.vue to use specific ItemTemplate types instead of 'any'. Improved code formatting for template fields and manufacturer display. Also refactored warranty field logic in item details page for better readability. This resolves the linter issues as well that the bot in github keeps nagging at. * Add 'id' property to template fields Introduces an 'id' property to each field object in CreateModal.vue and item details page to support unique identification of fields. This change prepares the codebase for future enhancements that may require field-level identification. * Removed redundant SQL migrations. Removed redundant SQL migrations per @tankerkiller125's findings. * Updates to PR #1099. Regarding pull #1099. Fixed an issue causing some conflict with GUIDs and old rows in the migration files. * Add new fields and location edge to ItemTemplate Addresses recommendations from @tonyaellie. * Relocated add template button * Added more default fields to the template * Added translation of all strings (think so?) * Make oval buttons round * Added duplicate button to the template (this required a rewrite of the migration files, I made sure only 1 exists per DB type) * Added a Save as template button to a item detail view (this creates a template with all the current data of that item) * Changed all occurrences of space to gap and flex where applicable. * Made template selection persistent after item created. * Collapsible template info on creation view. * Updates to translation and fix for labels/locations I also added a test in here because I keep missing small function tests. That should prevent that from happening again. * Linted * Bring up to date with main, fix some lint/type check issues * In theory fix playwright tests * Fix defaults being unable to be nullable/empty (and thus limiting flexibility) * Last few fixes I think * Forgot to fix the golang tests --------- Co-authored-by: Matthew Kilgore <matthew@kilgore.dev>
377 lines
15 KiB
Go
Generated
377 lines
15 KiB
Go
Generated
// Code generated by ent, DO NOT EDIT.
|
|
|
|
package ent
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
"entgo.io/ent"
|
|
"entgo.io/ent/dialect/sql"
|
|
"github.com/google/uuid"
|
|
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/group"
|
|
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/itemtemplate"
|
|
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/location"
|
|
)
|
|
|
|
// ItemTemplate is the model entity for the ItemTemplate schema.
|
|
type ItemTemplate struct {
|
|
config `json:"-"`
|
|
// ID of the ent.
|
|
ID uuid.UUID `json:"id,omitempty"`
|
|
// CreatedAt holds the value of the "created_at" field.
|
|
CreatedAt time.Time `json:"created_at,omitempty"`
|
|
// UpdatedAt holds the value of the "updated_at" field.
|
|
UpdatedAt time.Time `json:"updated_at,omitempty"`
|
|
// Name holds the value of the "name" field.
|
|
Name string `json:"name,omitempty"`
|
|
// Description holds the value of the "description" field.
|
|
Description string `json:"description,omitempty"`
|
|
// Notes holds the value of the "notes" field.
|
|
Notes string `json:"notes,omitempty"`
|
|
// DefaultQuantity holds the value of the "default_quantity" field.
|
|
DefaultQuantity int `json:"default_quantity,omitempty"`
|
|
// DefaultInsured holds the value of the "default_insured" field.
|
|
DefaultInsured bool `json:"default_insured,omitempty"`
|
|
// Default name template for items (can use placeholders)
|
|
DefaultName string `json:"default_name,omitempty"`
|
|
// Default description for items created from this template
|
|
DefaultDescription string `json:"default_description,omitempty"`
|
|
// DefaultManufacturer holds the value of the "default_manufacturer" field.
|
|
DefaultManufacturer string `json:"default_manufacturer,omitempty"`
|
|
// Default model number for items created from this template
|
|
DefaultModelNumber string `json:"default_model_number,omitempty"`
|
|
// DefaultLifetimeWarranty holds the value of the "default_lifetime_warranty" field.
|
|
DefaultLifetimeWarranty bool `json:"default_lifetime_warranty,omitempty"`
|
|
// DefaultWarrantyDetails holds the value of the "default_warranty_details" field.
|
|
DefaultWarrantyDetails string `json:"default_warranty_details,omitempty"`
|
|
// Whether to include warranty fields in items created from this template
|
|
IncludeWarrantyFields bool `json:"include_warranty_fields,omitempty"`
|
|
// Whether to include purchase fields in items created from this template
|
|
IncludePurchaseFields bool `json:"include_purchase_fields,omitempty"`
|
|
// Whether to include sold fields in items created from this template
|
|
IncludeSoldFields bool `json:"include_sold_fields,omitempty"`
|
|
// Default label IDs for items created from this template
|
|
DefaultLabelIds []uuid.UUID `json:"default_label_ids,omitempty"`
|
|
// Edges holds the relations/edges for other nodes in the graph.
|
|
// The values are being populated by the ItemTemplateQuery when eager-loading is set.
|
|
Edges ItemTemplateEdges `json:"edges"`
|
|
group_item_templates *uuid.UUID
|
|
item_template_location *uuid.UUID
|
|
selectValues sql.SelectValues
|
|
}
|
|
|
|
// ItemTemplateEdges holds the relations/edges for other nodes in the graph.
|
|
type ItemTemplateEdges struct {
|
|
// Group holds the value of the group edge.
|
|
Group *Group `json:"group,omitempty"`
|
|
// Fields holds the value of the fields edge.
|
|
Fields []*TemplateField `json:"fields,omitempty"`
|
|
// Location holds the value of the location edge.
|
|
Location *Location `json:"location,omitempty"`
|
|
// loadedTypes holds the information for reporting if a
|
|
// type was loaded (or requested) in eager-loading or not.
|
|
loadedTypes [3]bool
|
|
}
|
|
|
|
// GroupOrErr returns the Group value or an error if the edge
|
|
// was not loaded in eager-loading, or loaded but was not found.
|
|
func (e ItemTemplateEdges) GroupOrErr() (*Group, error) {
|
|
if e.Group != nil {
|
|
return e.Group, nil
|
|
} else if e.loadedTypes[0] {
|
|
return nil, &NotFoundError{label: group.Label}
|
|
}
|
|
return nil, &NotLoadedError{edge: "group"}
|
|
}
|
|
|
|
// FieldsOrErr returns the Fields value or an error if the edge
|
|
// was not loaded in eager-loading.
|
|
func (e ItemTemplateEdges) FieldsOrErr() ([]*TemplateField, error) {
|
|
if e.loadedTypes[1] {
|
|
return e.Fields, nil
|
|
}
|
|
return nil, &NotLoadedError{edge: "fields"}
|
|
}
|
|
|
|
// LocationOrErr returns the Location value or an error if the edge
|
|
// was not loaded in eager-loading, or loaded but was not found.
|
|
func (e ItemTemplateEdges) LocationOrErr() (*Location, error) {
|
|
if e.Location != nil {
|
|
return e.Location, nil
|
|
} else if e.loadedTypes[2] {
|
|
return nil, &NotFoundError{label: location.Label}
|
|
}
|
|
return nil, &NotLoadedError{edge: "location"}
|
|
}
|
|
|
|
// scanValues returns the types for scanning values from sql.Rows.
|
|
func (*ItemTemplate) scanValues(columns []string) ([]any, error) {
|
|
values := make([]any, len(columns))
|
|
for i := range columns {
|
|
switch columns[i] {
|
|
case itemtemplate.FieldDefaultLabelIds:
|
|
values[i] = new([]byte)
|
|
case itemtemplate.FieldDefaultInsured, itemtemplate.FieldDefaultLifetimeWarranty, itemtemplate.FieldIncludeWarrantyFields, itemtemplate.FieldIncludePurchaseFields, itemtemplate.FieldIncludeSoldFields:
|
|
values[i] = new(sql.NullBool)
|
|
case itemtemplate.FieldDefaultQuantity:
|
|
values[i] = new(sql.NullInt64)
|
|
case itemtemplate.FieldName, itemtemplate.FieldDescription, itemtemplate.FieldNotes, itemtemplate.FieldDefaultName, itemtemplate.FieldDefaultDescription, itemtemplate.FieldDefaultManufacturer, itemtemplate.FieldDefaultModelNumber, itemtemplate.FieldDefaultWarrantyDetails:
|
|
values[i] = new(sql.NullString)
|
|
case itemtemplate.FieldCreatedAt, itemtemplate.FieldUpdatedAt:
|
|
values[i] = new(sql.NullTime)
|
|
case itemtemplate.FieldID:
|
|
values[i] = new(uuid.UUID)
|
|
case itemtemplate.ForeignKeys[0]: // group_item_templates
|
|
values[i] = &sql.NullScanner{S: new(uuid.UUID)}
|
|
case itemtemplate.ForeignKeys[1]: // item_template_location
|
|
values[i] = &sql.NullScanner{S: new(uuid.UUID)}
|
|
default:
|
|
values[i] = new(sql.UnknownType)
|
|
}
|
|
}
|
|
return values, nil
|
|
}
|
|
|
|
// assignValues assigns the values that were returned from sql.Rows (after scanning)
|
|
// to the ItemTemplate fields.
|
|
func (_m *ItemTemplate) assignValues(columns []string, values []any) error {
|
|
if m, n := len(values), len(columns); m < n {
|
|
return fmt.Errorf("mismatch number of scan values: %d != %d", m, n)
|
|
}
|
|
for i := range columns {
|
|
switch columns[i] {
|
|
case itemtemplate.FieldID:
|
|
if value, ok := values[i].(*uuid.UUID); !ok {
|
|
return fmt.Errorf("unexpected type %T for field id", values[i])
|
|
} else if value != nil {
|
|
_m.ID = *value
|
|
}
|
|
case itemtemplate.FieldCreatedAt:
|
|
if value, ok := values[i].(*sql.NullTime); !ok {
|
|
return fmt.Errorf("unexpected type %T for field created_at", values[i])
|
|
} else if value.Valid {
|
|
_m.CreatedAt = value.Time
|
|
}
|
|
case itemtemplate.FieldUpdatedAt:
|
|
if value, ok := values[i].(*sql.NullTime); !ok {
|
|
return fmt.Errorf("unexpected type %T for field updated_at", values[i])
|
|
} else if value.Valid {
|
|
_m.UpdatedAt = value.Time
|
|
}
|
|
case itemtemplate.FieldName:
|
|
if value, ok := values[i].(*sql.NullString); !ok {
|
|
return fmt.Errorf("unexpected type %T for field name", values[i])
|
|
} else if value.Valid {
|
|
_m.Name = value.String
|
|
}
|
|
case itemtemplate.FieldDescription:
|
|
if value, ok := values[i].(*sql.NullString); !ok {
|
|
return fmt.Errorf("unexpected type %T for field description", values[i])
|
|
} else if value.Valid {
|
|
_m.Description = value.String
|
|
}
|
|
case itemtemplate.FieldNotes:
|
|
if value, ok := values[i].(*sql.NullString); !ok {
|
|
return fmt.Errorf("unexpected type %T for field notes", values[i])
|
|
} else if value.Valid {
|
|
_m.Notes = value.String
|
|
}
|
|
case itemtemplate.FieldDefaultQuantity:
|
|
if value, ok := values[i].(*sql.NullInt64); !ok {
|
|
return fmt.Errorf("unexpected type %T for field default_quantity", values[i])
|
|
} else if value.Valid {
|
|
_m.DefaultQuantity = int(value.Int64)
|
|
}
|
|
case itemtemplate.FieldDefaultInsured:
|
|
if value, ok := values[i].(*sql.NullBool); !ok {
|
|
return fmt.Errorf("unexpected type %T for field default_insured", values[i])
|
|
} else if value.Valid {
|
|
_m.DefaultInsured = value.Bool
|
|
}
|
|
case itemtemplate.FieldDefaultName:
|
|
if value, ok := values[i].(*sql.NullString); !ok {
|
|
return fmt.Errorf("unexpected type %T for field default_name", values[i])
|
|
} else if value.Valid {
|
|
_m.DefaultName = value.String
|
|
}
|
|
case itemtemplate.FieldDefaultDescription:
|
|
if value, ok := values[i].(*sql.NullString); !ok {
|
|
return fmt.Errorf("unexpected type %T for field default_description", values[i])
|
|
} else if value.Valid {
|
|
_m.DefaultDescription = value.String
|
|
}
|
|
case itemtemplate.FieldDefaultManufacturer:
|
|
if value, ok := values[i].(*sql.NullString); !ok {
|
|
return fmt.Errorf("unexpected type %T for field default_manufacturer", values[i])
|
|
} else if value.Valid {
|
|
_m.DefaultManufacturer = value.String
|
|
}
|
|
case itemtemplate.FieldDefaultModelNumber:
|
|
if value, ok := values[i].(*sql.NullString); !ok {
|
|
return fmt.Errorf("unexpected type %T for field default_model_number", values[i])
|
|
} else if value.Valid {
|
|
_m.DefaultModelNumber = value.String
|
|
}
|
|
case itemtemplate.FieldDefaultLifetimeWarranty:
|
|
if value, ok := values[i].(*sql.NullBool); !ok {
|
|
return fmt.Errorf("unexpected type %T for field default_lifetime_warranty", values[i])
|
|
} else if value.Valid {
|
|
_m.DefaultLifetimeWarranty = value.Bool
|
|
}
|
|
case itemtemplate.FieldDefaultWarrantyDetails:
|
|
if value, ok := values[i].(*sql.NullString); !ok {
|
|
return fmt.Errorf("unexpected type %T for field default_warranty_details", values[i])
|
|
} else if value.Valid {
|
|
_m.DefaultWarrantyDetails = value.String
|
|
}
|
|
case itemtemplate.FieldIncludeWarrantyFields:
|
|
if value, ok := values[i].(*sql.NullBool); !ok {
|
|
return fmt.Errorf("unexpected type %T for field include_warranty_fields", values[i])
|
|
} else if value.Valid {
|
|
_m.IncludeWarrantyFields = value.Bool
|
|
}
|
|
case itemtemplate.FieldIncludePurchaseFields:
|
|
if value, ok := values[i].(*sql.NullBool); !ok {
|
|
return fmt.Errorf("unexpected type %T for field include_purchase_fields", values[i])
|
|
} else if value.Valid {
|
|
_m.IncludePurchaseFields = value.Bool
|
|
}
|
|
case itemtemplate.FieldIncludeSoldFields:
|
|
if value, ok := values[i].(*sql.NullBool); !ok {
|
|
return fmt.Errorf("unexpected type %T for field include_sold_fields", values[i])
|
|
} else if value.Valid {
|
|
_m.IncludeSoldFields = value.Bool
|
|
}
|
|
case itemtemplate.FieldDefaultLabelIds:
|
|
if value, ok := values[i].(*[]byte); !ok {
|
|
return fmt.Errorf("unexpected type %T for field default_label_ids", values[i])
|
|
} else if value != nil && len(*value) > 0 {
|
|
if err := json.Unmarshal(*value, &_m.DefaultLabelIds); err != nil {
|
|
return fmt.Errorf("unmarshal field default_label_ids: %w", err)
|
|
}
|
|
}
|
|
case itemtemplate.ForeignKeys[0]:
|
|
if value, ok := values[i].(*sql.NullScanner); !ok {
|
|
return fmt.Errorf("unexpected type %T for field group_item_templates", values[i])
|
|
} else if value.Valid {
|
|
_m.group_item_templates = new(uuid.UUID)
|
|
*_m.group_item_templates = *value.S.(*uuid.UUID)
|
|
}
|
|
case itemtemplate.ForeignKeys[1]:
|
|
if value, ok := values[i].(*sql.NullScanner); !ok {
|
|
return fmt.Errorf("unexpected type %T for field item_template_location", values[i])
|
|
} else if value.Valid {
|
|
_m.item_template_location = new(uuid.UUID)
|
|
*_m.item_template_location = *value.S.(*uuid.UUID)
|
|
}
|
|
default:
|
|
_m.selectValues.Set(columns[i], values[i])
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Value returns the ent.Value that was dynamically selected and assigned to the ItemTemplate.
|
|
// This includes values selected through modifiers, order, etc.
|
|
func (_m *ItemTemplate) Value(name string) (ent.Value, error) {
|
|
return _m.selectValues.Get(name)
|
|
}
|
|
|
|
// QueryGroup queries the "group" edge of the ItemTemplate entity.
|
|
func (_m *ItemTemplate) QueryGroup() *GroupQuery {
|
|
return NewItemTemplateClient(_m.config).QueryGroup(_m)
|
|
}
|
|
|
|
// QueryFields queries the "fields" edge of the ItemTemplate entity.
|
|
func (_m *ItemTemplate) QueryFields() *TemplateFieldQuery {
|
|
return NewItemTemplateClient(_m.config).QueryFields(_m)
|
|
}
|
|
|
|
// QueryLocation queries the "location" edge of the ItemTemplate entity.
|
|
func (_m *ItemTemplate) QueryLocation() *LocationQuery {
|
|
return NewItemTemplateClient(_m.config).QueryLocation(_m)
|
|
}
|
|
|
|
// Update returns a builder for updating this ItemTemplate.
|
|
// Note that you need to call ItemTemplate.Unwrap() before calling this method if this ItemTemplate
|
|
// was returned from a transaction, and the transaction was committed or rolled back.
|
|
func (_m *ItemTemplate) Update() *ItemTemplateUpdateOne {
|
|
return NewItemTemplateClient(_m.config).UpdateOne(_m)
|
|
}
|
|
|
|
// Unwrap unwraps the ItemTemplate entity that was returned from a transaction after it was closed,
|
|
// so that all future queries will be executed through the driver which created the transaction.
|
|
func (_m *ItemTemplate) Unwrap() *ItemTemplate {
|
|
_tx, ok := _m.config.driver.(*txDriver)
|
|
if !ok {
|
|
panic("ent: ItemTemplate is not a transactional entity")
|
|
}
|
|
_m.config.driver = _tx.drv
|
|
return _m
|
|
}
|
|
|
|
// String implements the fmt.Stringer.
|
|
func (_m *ItemTemplate) String() string {
|
|
var builder strings.Builder
|
|
builder.WriteString("ItemTemplate(")
|
|
builder.WriteString(fmt.Sprintf("id=%v, ", _m.ID))
|
|
builder.WriteString("created_at=")
|
|
builder.WriteString(_m.CreatedAt.Format(time.ANSIC))
|
|
builder.WriteString(", ")
|
|
builder.WriteString("updated_at=")
|
|
builder.WriteString(_m.UpdatedAt.Format(time.ANSIC))
|
|
builder.WriteString(", ")
|
|
builder.WriteString("name=")
|
|
builder.WriteString(_m.Name)
|
|
builder.WriteString(", ")
|
|
builder.WriteString("description=")
|
|
builder.WriteString(_m.Description)
|
|
builder.WriteString(", ")
|
|
builder.WriteString("notes=")
|
|
builder.WriteString(_m.Notes)
|
|
builder.WriteString(", ")
|
|
builder.WriteString("default_quantity=")
|
|
builder.WriteString(fmt.Sprintf("%v", _m.DefaultQuantity))
|
|
builder.WriteString(", ")
|
|
builder.WriteString("default_insured=")
|
|
builder.WriteString(fmt.Sprintf("%v", _m.DefaultInsured))
|
|
builder.WriteString(", ")
|
|
builder.WriteString("default_name=")
|
|
builder.WriteString(_m.DefaultName)
|
|
builder.WriteString(", ")
|
|
builder.WriteString("default_description=")
|
|
builder.WriteString(_m.DefaultDescription)
|
|
builder.WriteString(", ")
|
|
builder.WriteString("default_manufacturer=")
|
|
builder.WriteString(_m.DefaultManufacturer)
|
|
builder.WriteString(", ")
|
|
builder.WriteString("default_model_number=")
|
|
builder.WriteString(_m.DefaultModelNumber)
|
|
builder.WriteString(", ")
|
|
builder.WriteString("default_lifetime_warranty=")
|
|
builder.WriteString(fmt.Sprintf("%v", _m.DefaultLifetimeWarranty))
|
|
builder.WriteString(", ")
|
|
builder.WriteString("default_warranty_details=")
|
|
builder.WriteString(_m.DefaultWarrantyDetails)
|
|
builder.WriteString(", ")
|
|
builder.WriteString("include_warranty_fields=")
|
|
builder.WriteString(fmt.Sprintf("%v", _m.IncludeWarrantyFields))
|
|
builder.WriteString(", ")
|
|
builder.WriteString("include_purchase_fields=")
|
|
builder.WriteString(fmt.Sprintf("%v", _m.IncludePurchaseFields))
|
|
builder.WriteString(", ")
|
|
builder.WriteString("include_sold_fields=")
|
|
builder.WriteString(fmt.Sprintf("%v", _m.IncludeSoldFields))
|
|
builder.WriteString(", ")
|
|
builder.WriteString("default_label_ids=")
|
|
builder.WriteString(fmt.Sprintf("%v", _m.DefaultLabelIds))
|
|
builder.WriteByte(')')
|
|
return builder.String()
|
|
}
|
|
|
|
// ItemTemplates is a parsable slice of ItemTemplate.
|
|
type ItemTemplates []*ItemTemplate
|