From fbdefac3f60d1a68f00bdf99b7277cd0f1061af4 Mon Sep 17 00:00:00 2001 From: Tobias Gleiter Date: Mon, 24 Aug 2026 18:54:41 -0400 Subject: [PATCH 01/19] refactor: prepare dashboard page. Settings had to move because of path overlappings. #35 --- cmd/web/handlers:dashboard.go | 13 +++++++++ cmd/web/handlers:orgs.go | 2 +- cmd/web/routes.go | 12 ++++----- internal/templates/components/icons.tmpl | 4 +++ internal/templates/pages/dashboard.tmpl | 5 ++++ internal/templates/pages/orgs/details.tmpl | 4 +-- internal/templates/pages/orgs/index.tmpl | 4 +-- .../orgs/{settings.tmpl => localization.tmpl} | 2 +- internal/templates/pages/pages.go | 5 +++- internal/templates/partials/header.tmpl | 27 ++++++++++++++++--- .../partials/org-settings-sidebar.tmpl | 8 +++--- 11 files changed, 66 insertions(+), 20 deletions(-) create mode 100644 cmd/web/handlers:dashboard.go create mode 100644 internal/templates/pages/dashboard.tmpl rename internal/templates/pages/orgs/{settings.tmpl => localization.tmpl} (99%) diff --git a/cmd/web/handlers:dashboard.go b/cmd/web/handlers:dashboard.go new file mode 100644 index 0000000..8e9f775 --- /dev/null +++ b/cmd/web/handlers:dashboard.go @@ -0,0 +1,13 @@ +package main + +import ( + "net/http" + + "github.com/bit8bytes/gearberg/internal/httperr" + "github.com/bit8bytes/gearberg/internal/templates/pages" +) + +func (app *application) getDashboard(w http.ResponseWriter, r *http.Request) *httperr.Error { + tmplData := app.html.TemplateData(r) + return app.html.Render(w, r, http.StatusOK, pages.Dashboard, tmplData) +} diff --git a/cmd/web/handlers:orgs.go b/cmd/web/handlers:orgs.go index 0c849c0..9453a58 100644 --- a/cmd/web/handlers:orgs.go +++ b/cmd/web/handlers:orgs.go @@ -159,7 +159,7 @@ func (app *application) postSettingsOrg(w http.ResponseWriter, r *http.Request) return httperr.InternalServerError(err) } - http.Redirect(w, r, "/orgs/"+id, http.StatusSeeOther) //nolint:gosec // id is a parsed and validated KSUID, not an open redirect + http.Redirect(w, r, "/orgs/"+id+"/settings", http.StatusSeeOther) //nolint:gosec // id is a parsed and validated KSUID, not an open redirect return nil } diff --git a/cmd/web/routes.go b/cmd/web/routes.go index 34ed866..5c0484e 100644 --- a/cmd/web/routes.go +++ b/cmd/web/routes.go @@ -76,10 +76,6 @@ func (app *application) routes() (http.Handler, error) { mux.Handle("POST /orgs/new", app.withLogin(app.html.Handle(app.postOrgsNew))) // For any route that includes org_id, the permissions of the account must be checked via [app.withPermission] - mux.Handle("DELETE /orgs/{org_id}", app.withLogin(app.withPermission(app.html.Handle(app.deleteOrg)))) - mux.Handle("GET /orgs/{org_id}", app.withLogin(app.withPermission(app.html.Handle(app.getSettingsOrg)))) - mux.Handle("POST /orgs/{org_id}", app.withLogin(app.withPermission(app.html.Handle(app.postSettingsOrg)))) - // Import mux.Handle("GET /orgs/{org_id}/import", app.withLogin(app.withPermission(app.html.Handle(app.getImport)))) mux.Handle("GET /orgs/{org_id}/import/template", app.withLogin(app.withPermission(app.html.Handle(app.getImportTemplate)))) @@ -90,6 +86,7 @@ func (app *application) routes() (http.Handler, error) { mux.Handle("POST /orgs/{org_id}/import/{id}/delete", app.withLogin(app.withPermission(app.html.Handle(app.postImportDelete)))) // Equipment + mux.Handle("GET /orgs/{org_id}/", app.withLogin(app.withPermission(app.html.Handle(app.getDashboard)))) mux.Handle("GET /orgs/{org_id}/equipment", app.withLogin(app.withPermission(app.html.Handle(app.getEquipment)))) mux.Handle("GET /orgs/{org_id}/equipment/export", app.withLogin(app.withPermission(app.html.Handle(app.getExport)))) mux.Handle("GET /orgs/{org_id}/equipment/print", app.withLogin(app.withPermission(app.html.Handle(app.getEquipmentPrint)))) @@ -127,8 +124,11 @@ func (app *application) routes() (http.Handler, error) { mux.Handle("GET /settings/organizations", app.withLogin(app.html.Handle(app.getOrgs))) // Org related settings - mux.Handle("GET /orgs/{org_id}/settings", app.withLogin(app.withPermission(app.html.Handle(app.getOrgSettings)))) - mux.Handle("POST /orgs/{org_id}/settings", app.withLogin(app.withPermission(app.html.Handle(app.postOrgSettings)))) + mux.Handle("DELETE /orgs/{org_id}/settings", app.withLogin(app.withPermission(app.html.Handle(app.deleteOrg)))) + mux.Handle("GET /orgs/{org_id}/settings", app.withLogin(app.withPermission(app.html.Handle(app.getSettingsOrg)))) + mux.Handle("POST /orgs/{org_id}/settings", app.withLogin(app.withPermission(app.html.Handle(app.postSettingsOrg)))) + mux.Handle("GET /orgs/{org_id}/settings/localization", app.withLogin(app.withPermission(app.html.Handle(app.getOrgSettings)))) + mux.Handle("POST /orgs/{org_id}/settings/localization", app.withLogin(app.withPermission(app.html.Handle(app.postOrgSettings)))) mux.Handle("GET /orgs/{org_id}/settings/equipment-categories", app.withLogin(app.withPermission(app.html.Handle(app.getEquipmentCategories)))) mux.Handle("GET /orgs/{org_id}/settings/equipment-categories/new", app.withLogin(app.withPermission(app.html.Handle(app.getEquipmentCategoryNew)))) mux.Handle("POST /orgs/{org_id}/settings/equipment-categories/new", app.withLogin(app.withPermission(app.html.Handle(app.postEquipmentCategoryNew)))) diff --git a/internal/templates/components/icons.tmpl b/internal/templates/components/icons.tmpl index 23a030f..f4bc854 100644 --- a/internal/templates/components/icons.tmpl +++ b/internal/templates/components/icons.tmpl @@ -10,6 +10,10 @@ {{ end }} +{{ define "dashboard-icon" }} + +{{ end }} + {{ define "equipment-icon" }} {{ end }} diff --git a/internal/templates/pages/dashboard.tmpl b/internal/templates/pages/dashboard.tmpl new file mode 100644 index 0000000..65b8422 --- /dev/null +++ b/internal/templates/pages/dashboard.tmpl @@ -0,0 +1,5 @@ +{{ define "page" }} +
+ Dashboard +
+{{ end }} \ No newline at end of file diff --git a/internal/templates/pages/orgs/details.tmpl b/internal/templates/pages/orgs/details.tmpl index 76b081d..9b1bedd 100644 --- a/internal/templates/pages/orgs/details.tmpl +++ b/internal/templates/pages/orgs/details.tmpl @@ -11,7 +11,7 @@

Org name and basic information.

-
+
Cancel
diff --git a/internal/templates/pages/orgs/index.tmpl b/internal/templates/pages/orgs/index.tmpl index 30b92c8..a67baff 100644 --- a/internal/templates/pages/orgs/index.tmpl +++ b/internal/templates/pages/orgs/index.tmpl @@ -41,10 +41,10 @@ + data-tip="Go to dashboard"> {{ template "arrow-right-icon" . }} - {{ template "settings-icon" . }} diff --git a/internal/templates/pages/orgs/settings.tmpl b/internal/templates/pages/orgs/localization.tmpl similarity index 99% rename from internal/templates/pages/orgs/settings.tmpl rename to internal/templates/pages/orgs/localization.tmpl index d33d01a..46d8fcc 100644 --- a/internal/templates/pages/orgs/settings.tmpl +++ b/internal/templates/pages/orgs/localization.tmpl @@ -11,7 +11,7 @@

Regional defaults for currency, VAT rate, and timezone.

-
+
+
From 3452b00702ac692da6144178b2968cf184235548 Mon Sep 17 00:00:00 2001 From: Tobias Gleiter Date: Tue, 25 Aug 2026 17:33:37 -0400 Subject: [PATCH 06/19] refactor: indicate inspection status with red, organge, adn green to give the user an easy overview of the inspection status on the equipment page --- internal/assets/dist/index.css | 15 ++++ .../queries/gen/equipment/equipment.sql.go | 76 +++++++++++-------- .../database/queries/sqlite/equipment.sql | 14 +++- internal/equipment/model.go | 42 ++++++++++ internal/equipment/repository.go | 7 ++ internal/templates/fragments/equipment.tmpl | 13 +++- internal/templates/pages/equipment/index.tmpl | 7 +- 7 files changed, 133 insertions(+), 41 deletions(-) diff --git a/internal/assets/dist/index.css b/internal/assets/dist/index.css index e648884..c09dad0 100644 --- a/internal/assets/dist/index.css +++ b/internal/assets/dist/index.css @@ -6843,6 +6843,9 @@ .max-w-7xl { max-width: var(--container-7xl); } + .max-w-32 { + max-width: calc(var(--spacing) * 32); + } .max-w-40 { max-width: calc(var(--spacing) * 40); } @@ -6867,9 +6870,15 @@ .min-w-0 { min-width: calc(var(--spacing) * 0); } + .min-w-40 { + min-width: calc(var(--spacing) * 40); + } .min-w-48 { min-width: calc(var(--spacing) * 48); } + .min-w-56 { + min-width: calc(var(--spacing) * 56); + } .flex-1 { flex: 1; } @@ -7834,6 +7843,9 @@ .bg-red-500\! { background-color: var(--color-red-500) !important; } + .bg-success { + background-color: var(--color-success); + } .bg-teal-400 { background-color: var(--color-teal-400); } @@ -9123,6 +9135,9 @@ .text-success { color: var(--color-success); } + .text-success-content { + color: var(--color-success-content); + } .text-teal-800 { color: var(--color-teal-800); } diff --git a/internal/database/queries/gen/equipment/equipment.sql.go b/internal/database/queries/gen/equipment/equipment.sql.go index b79dc3e..82f9f3b 100644 --- a/internal/database/queries/gen/equipment/equipment.sql.go +++ b/internal/database/queries/gen/equipment/equipment.sql.go @@ -449,19 +449,27 @@ SELECT e.wire_gauge_mm2_x100, e.updated_at, e.created_at, - COUNT(*) OVER() AS total_records + COUNT(*) OVER() AS total_records, + insp.min_next_inspection_at FROM equipment e LEFT JOIN equipment_categories ec ON ec.id = e.category_id LEFT JOIN warehouse_locations wl ON wl.id = e.location_id LEFT JOIN equipment_types et ON et.id = e.equipment_type_id LEFT JOIN tracking_types tt ON tt.id = e.tracking_type_id +LEFT JOIN ( + SELECT equipment_id, MIN(next_inspection_at) AS min_next_inspection_at + FROM equipment_serialized_items + WHERE next_inspection_at IS NOT NULL + GROUP BY equipment_id +) insp ON insp.equipment_id = e.id WHERE e.org_id = ?1 AND (?2 = '' OR e.name LIKE '%' || ?2 || '%' OR EXISTS (SELECT 1 FROM equipment_serialized_items esi WHERE esi.equipment_id = e.id AND esi.serial_number LIKE '%' || ?2 || '%')) AND (?3 = '' OR ec.name = ?3) AND (?4 = -1 OR e.is_archived = ?4) AND (?5 = '' - OR (?5 = 'overdue' AND EXISTS (SELECT 1 FROM equipment_serialized_items esi WHERE esi.equipment_id = e.id AND esi.next_inspection_at IS NOT NULL AND esi.next_inspection_at < unixepoch())) - OR (?5 = 'due-30d' AND EXISTS (SELECT 1 FROM equipment_serialized_items esi WHERE esi.equipment_id = e.id AND esi.next_inspection_at IS NOT NULL AND esi.next_inspection_at >= unixepoch() AND esi.next_inspection_at <= unixepoch() + 30 * 86400))) + OR (?5 = 'overdue' AND EXISTS (SELECT 1 FROM equipment_serialized_items esi WHERE esi.equipment_id = e.id AND esi.next_inspection_at IS NOT NULL AND esi.next_inspection_at < unixepoch())) + OR (?5 = 'due-30d' AND EXISTS (SELECT 1 FROM equipment_serialized_items esi WHERE esi.equipment_id = e.id AND esi.next_inspection_at IS NOT NULL AND esi.next_inspection_at >= unixepoch() AND esi.next_inspection_at <= unixepoch() + 30 * 86400)) + OR (?5 = 'up-to-date' AND EXISTS (SELECT 1 FROM equipment_serialized_items esi WHERE esi.equipment_id = e.id AND esi.next_inspection_at IS NOT NULL AND esi.next_inspection_at > unixepoch() + 30 * 86400))) ORDER BY category_name ASC, e.name ASC LIMIT ?7 OFFSET ?6 ` @@ -477,36 +485,37 @@ type ListParams struct { } type ListRow struct { - ID string - OrgID string - Name string - CategoryID sql.NullString - CategoryName string - ManufacturerID sql.NullString - LocationID sql.NullString - LocationName string - StorageObjectID sql.NullString - EquipmentTypeID int64 - EquipmentTypeName string - TrackingTypeID sql.NullInt64 - TrackingTypeName string - UsageTypeID int64 - TotalStock int64 - IsArchived int64 - RentalPrice *units.Cents - ResalePrice *units.Cents - Notes sql.NullString - WeightG *units.Grams - WidthMm *units.Millimeters - HeightMm *units.Millimeters - DepthMm *units.Millimeters - VoltageMv *units.Millivolts - CurrentMa *units.Milliamps - PowerMw *units.Milliwatts - WireGaugeMm2X100 *units.WireGauge - UpdatedAt int64 - CreatedAt int64 - TotalRecords int64 + ID string + OrgID string + Name string + CategoryID sql.NullString + CategoryName string + ManufacturerID sql.NullString + LocationID sql.NullString + LocationName string + StorageObjectID sql.NullString + EquipmentTypeID int64 + EquipmentTypeName string + TrackingTypeID sql.NullInt64 + TrackingTypeName string + UsageTypeID int64 + TotalStock int64 + IsArchived int64 + RentalPrice *units.Cents + ResalePrice *units.Cents + Notes sql.NullString + WeightG *units.Grams + WidthMm *units.Millimeters + HeightMm *units.Millimeters + DepthMm *units.Millimeters + VoltageMv *units.Millivolts + CurrentMa *units.Milliamps + PowerMw *units.Milliwatts + WireGaugeMm2X100 *units.WireGauge + UpdatedAt int64 + CreatedAt int64 + TotalRecords int64 + MinNextInspectionAt interface{} } func (q *Queries) List(ctx context.Context, arg ListParams) ([]ListRow, error) { @@ -557,6 +566,7 @@ func (q *Queries) List(ctx context.Context, arg ListParams) ([]ListRow, error) { &i.UpdatedAt, &i.CreatedAt, &i.TotalRecords, + &i.MinNextInspectionAt, ); err != nil { return nil, err } diff --git a/internal/database/queries/sqlite/equipment.sql b/internal/database/queries/sqlite/equipment.sql index 65d211e..3a94d04 100644 --- a/internal/database/queries/sqlite/equipment.sql +++ b/internal/database/queries/sqlite/equipment.sql @@ -33,19 +33,27 @@ SELECT e.wire_gauge_mm2_x100, e.updated_at, e.created_at, - COUNT(*) OVER() AS total_records + COUNT(*) OVER() AS total_records, + insp.min_next_inspection_at FROM equipment e LEFT JOIN equipment_categories ec ON ec.id = e.category_id LEFT JOIN warehouse_locations wl ON wl.id = e.location_id LEFT JOIN equipment_types et ON et.id = e.equipment_type_id LEFT JOIN tracking_types tt ON tt.id = e.tracking_type_id +LEFT JOIN ( + SELECT equipment_id, MIN(next_inspection_at) AS min_next_inspection_at + FROM equipment_serialized_items + WHERE next_inspection_at IS NOT NULL + GROUP BY equipment_id +) insp ON insp.equipment_id = e.id WHERE e.org_id = sqlc.arg(org_id) AND (sqlc.arg(name_query) = '' OR e.name LIKE '%' || sqlc.arg(name_query) || '%' OR EXISTS (SELECT 1 FROM equipment_serialized_items esi WHERE esi.equipment_id = e.id AND esi.serial_number LIKE '%' || sqlc.arg(name_query) || '%')) AND (sqlc.arg(category) = '' OR ec.name = sqlc.arg(category)) AND (sqlc.arg(is_archived) = -1 OR e.is_archived = sqlc.arg(is_archived)) AND (sqlc.arg(inspection_filter) = '' - OR (sqlc.arg(inspection_filter) = 'overdue' AND EXISTS (SELECT 1 FROM equipment_serialized_items esi WHERE esi.equipment_id = e.id AND esi.next_inspection_at IS NOT NULL AND esi.next_inspection_at < unixepoch())) - OR (sqlc.arg(inspection_filter) = 'due-30d' AND EXISTS (SELECT 1 FROM equipment_serialized_items esi WHERE esi.equipment_id = e.id AND esi.next_inspection_at IS NOT NULL AND esi.next_inspection_at >= unixepoch() AND esi.next_inspection_at <= unixepoch() + 30 * 86400))) + OR (sqlc.arg(inspection_filter) = 'overdue' AND EXISTS (SELECT 1 FROM equipment_serialized_items esi WHERE esi.equipment_id = e.id AND esi.next_inspection_at IS NOT NULL AND esi.next_inspection_at < unixepoch())) + OR (sqlc.arg(inspection_filter) = 'due-30d' AND EXISTS (SELECT 1 FROM equipment_serialized_items esi WHERE esi.equipment_id = e.id AND esi.next_inspection_at IS NOT NULL AND esi.next_inspection_at >= unixepoch() AND esi.next_inspection_at <= unixepoch() + 30 * 86400)) + OR (sqlc.arg(inspection_filter) = 'up-to-date' AND EXISTS (SELECT 1 FROM equipment_serialized_items esi WHERE esi.equipment_id = e.id AND esi.next_inspection_at IS NOT NULL AND esi.next_inspection_at > unixepoch() + 30 * 86400))) ORDER BY category_name ASC, e.name ASC LIMIT sqlc.arg(page_limit) OFFSET sqlc.arg(page_offset); diff --git a/internal/equipment/model.go b/internal/equipment/model.go index 8b81e05..3e4d77f 100644 --- a/internal/equipment/model.go +++ b/internal/equipment/model.go @@ -197,6 +197,7 @@ type Equipment struct { IsArchived bool Notes string InspectionIntervalDays *int64 + InspectionStatus InspectionStatus Pricing Pricing Properties Properties CreatedAt int64 @@ -268,6 +269,47 @@ func (u *Unit) IsInspectionOverdue() bool { return u.NextInspectionAt != nil && *u.NextInspectionAt < time.Now().Unix() } +// InspectionStatus is the traffic-light health state of an equipment item +// derived from its units' inspection dates. +type InspectionStatus string + +const ( + // InspectionStatusNone means no inspection date is set on any unit. + InspectionStatusNone InspectionStatus = "" + // InspectionStatusOverdue means at least one unit's inspection date has passed. + InspectionStatusOverdue InspectionStatus = "overdue" + // InspectionStatusDueSoon means at least one unit's inspection is due within 30 days. + InspectionStatusDueSoon InspectionStatus = "due-30d" + // InspectionStatusUpToDate means all unit inspections are more than 30 days away. + InspectionStatusUpToDate InspectionStatus = "up-to-date" +) + +// NewInspectionStatus derives the status from the earliest unit inspection +// timestamp. Returns InspectionStatusNone when minNextAt is nil. +func NewInspectionStatus(minNextAt *int64) InspectionStatus { + if minNextAt == nil { + return InspectionStatusNone + } + now := time.Now().Unix() + switch { + case *minNextAt < now: + return InspectionStatusOverdue + case *minNextAt <= now+30*86400: + return InspectionStatusDueSoon + default: + return InspectionStatusUpToDate + } +} + +// IsOverdue reports whether the status is overdue. +func (s InspectionStatus) IsOverdue() bool { return s == InspectionStatusOverdue } + +// IsDueSoon reports whether the status is due within 30 days. +func (s InspectionStatus) IsDueSoon() bool { return s == InspectionStatusDueSoon } + +// IsUpToDate reports whether all inspections are more than 30 days away. +func (s InspectionStatus) IsUpToDate() bool { return s == InspectionStatusUpToDate } + // IsActive returns true when the unit's is_active flag is set. func (u *Unit) IsActive() bool { return u.StatusID == 1 } diff --git a/internal/equipment/repository.go b/internal/equipment/repository.go index f0d6187..e10cac2 100644 --- a/internal/equipment/repository.go +++ b/internal/equipment/repository.go @@ -121,6 +121,13 @@ func listRowsToEquipment(rows []genequip.ListRow) ([]Equipment, int, error) { }, UpdatedAt: row.UpdatedAt, CreatedAt: row.CreatedAt, + InspectionStatus: func() InspectionStatus { + v, ok := row.MinNextInspectionAt.(int64) + if !ok { + return InspectionStatusNone + } + return NewInspectionStatus(&v) + }(), }) } return items, int(totalRecords), nil diff --git a/internal/templates/fragments/equipment.tmpl b/internal/templates/fragments/equipment.tmpl index 15cce8f..f7e8570 100644 --- a/internal/templates/fragments/equipment.tmpl +++ b/internal/templates/fragments/equipment.tmpl @@ -4,7 +4,7 @@ - Name + Name Category Stock Actions @@ -30,7 +30,16 @@ -
{{ .Name }} +
+ {{ .Name }} + {{ if .InspectionStatus.IsOverdue }} + + {{ else if .InspectionStatus.IsDueSoon }} + + {{ else if .InspectionStatus.IsUpToDate }} + + {{ end }} +
{{ if .CategoryName }} diff --git a/internal/templates/pages/equipment/index.tmpl b/internal/templates/pages/equipment/index.tmpl index 8a8ef03..1b47d7c 100644 --- a/internal/templates/pages/equipment/index.tmpl +++ b/internal/templates/pages/equipment/index.tmpl @@ -108,9 +108,10 @@ hx-indicator="#search-indicator" hx-push-url="true" > - - - + + + + From b22f15522a32c31c71e48fa1f6abb1762944fa61 Mon Sep 17 00:00:00 2001 From: Tobias Gleiter Date: Tue, 25 Aug 2026 17:46:01 -0400 Subject: [PATCH 07/19] style: add status column and show overdue and due soon icons --- internal/assets/dist/index.css | 30 ++++++++++++--------- internal/templates/components/icons.tmpl | 4 +-- internal/templates/fragments/equipment.tmpl | 29 ++++++++++++-------- 3 files changed, 38 insertions(+), 25 deletions(-) diff --git a/internal/assets/dist/index.css b/internal/assets/dist/index.css index c09dad0..776a1e8 100644 --- a/internal/assets/dist/index.css +++ b/internal/assets/dist/index.css @@ -6774,6 +6774,9 @@ .w-1\/2 { width: calc(1 / 2 * 100%); } + .w-3 { + width: calc(var(--spacing) * 3); + } .w-4 { width: calc(var(--spacing) * 4); } @@ -6786,6 +6789,9 @@ .w-10 { width: calc(var(--spacing) * 10); } + .w-12 { + width: calc(var(--spacing) * 12); + } .w-14 { width: calc(var(--spacing) * 14); } @@ -6843,9 +6849,6 @@ .max-w-7xl { max-width: var(--container-7xl); } - .max-w-32 { - max-width: calc(var(--spacing) * 32); - } .max-w-40 { max-width: calc(var(--spacing) * 40); } @@ -6870,9 +6873,6 @@ .min-w-0 { min-width: calc(var(--spacing) * 0); } - .min-w-40 { - min-width: calc(var(--spacing) * 40); - } .min-w-48 { min-width: calc(var(--spacing) * 48); } @@ -7822,6 +7822,9 @@ .bg-blue-400 { background-color: var(--color-blue-400); } + .bg-error { + background-color: var(--color-error); + } .bg-gray-950 { background-color: var(--color-gray-950); } @@ -7843,15 +7846,15 @@ .bg-red-500\! { background-color: var(--color-red-500) !important; } - .bg-success { - background-color: var(--color-success); - } .bg-teal-400 { background-color: var(--color-teal-400); } .bg-transparent { background-color: transparent; } + .bg-warning { + background-color: var(--color-warning); + } .divider-accent { @layer daisyui.l1.l2 { &:before, &:after { @@ -9105,6 +9108,9 @@ .text-error { color: var(--color-error); } + .text-error-content { + color: var(--color-error-content); + } .text-gray-800 { color: var(--color-gray-800); } @@ -9135,15 +9141,15 @@ .text-success { color: var(--color-success); } - .text-success-content { - color: var(--color-success-content); - } .text-teal-800 { color: var(--color-teal-800); } .text-warning { color: var(--color-warning); } + .text-warning-content { + color: var(--color-warning-content); + } .text-white { color: var(--color-white); } diff --git a/internal/templates/components/icons.tmpl b/internal/templates/components/icons.tmpl index c65e3cd..f3aa66f 100644 --- a/internal/templates/components/icons.tmpl +++ b/internal/templates/components/icons.tmpl @@ -118,8 +118,8 @@ {{ end }} -{{ define "log-inspection-icon"}} - +{{ define "wrench-icon"}} + {{ end }} {{ define "delete-icon" }} diff --git a/internal/templates/fragments/equipment.tmpl b/internal/templates/fragments/equipment.tmpl index f7e8570..fe3fc24 100644 --- a/internal/templates/fragments/equipment.tmpl +++ b/internal/templates/fragments/equipment.tmpl @@ -5,6 +5,7 @@ Name + Status Category Stock Actions @@ -30,16 +31,22 @@ -
- {{ .Name }} - {{ if .InspectionStatus.IsOverdue }} - - {{ else if .InspectionStatus.IsDueSoon }} - - {{ else if .InspectionStatus.IsUpToDate }} - - {{ end }} -
+ {{ .Name }} + + + {{ if .InspectionStatus.IsOverdue }} + + + {{ template "wrench-icon" . }} + + + {{ else if .InspectionStatus.IsDueSoon }} + + + {{ template "wrench-icon" . }} + + + {{ end }} {{ if .CategoryName }} @@ -88,7 +95,7 @@ {{ end }} {{ else }} - +
{{ if .Data.Query }}

No items found for "{{ .Data.Query }}".

From 1dbae8df9f9514a904f9140f307fb96be0243df5 Mon Sep 17 00:00:00 2001 From: Tobias Gleiter Date: Tue, 25 Aug 2026 19:09:29 -0400 Subject: [PATCH 08/19] style: add quick links to naviagte to details, pricing, properties, units, and content tab quicker --- internal/templates/components/icons.tmpl | 12 ++++++++ internal/templates/fragments/equipment.tmpl | 33 +++++++++++++++++++-- 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/internal/templates/components/icons.tmpl b/internal/templates/components/icons.tmpl index f3aa66f..c6a6d39 100644 --- a/internal/templates/components/icons.tmpl +++ b/internal/templates/components/icons.tmpl @@ -199,4 +199,16 @@ {{ define "soon-inspections-icon"}} +{{ end }} + +{{ define "pricing-icon" }} + +{{ end }} + +{{ define "properties-icon" }} + +{{ end }} + +{{ define "details-icon" }} + {{ end }} \ No newline at end of file diff --git a/internal/templates/fragments/equipment.tmpl b/internal/templates/fragments/equipment.tmpl index fe3fc24..95cafcd 100644 --- a/internal/templates/fragments/equipment.tmpl +++ b/internal/templates/fragments/equipment.tmpl @@ -77,10 +77,39 @@