Bulk multi-select actions, barcode format, batch printing, site name on login
Multi-select (Ctrl/Cmd+click toggle, Shift+click range, right-click the
active selection for bulk actions - same conventions as a native file
manager):
- Print Labels (N) / Export CSV (N) for any mix of individually-picked
assets and whole folders. Labels/export now take asset_ids/folder_ids
instead of a single asset_id/folder_id.
- Edit Status..., Check Out..., Duplicate, Move to..., and Delete, bulk
versions of the existing single-asset actions. Checkout/Move modals
were generalized to accept an array of ids instead of just one; status
editing got its own lightweight POST /app/assets/{id}/status endpoint
(assets.SetStatus) so a bulk status change doesn't need to resend every
other field the way a full PATCH would.
Labels:
- Choose QR (a /scan/<tag> URL, for a phone camera) or Code128 barcode
(the bare Asset ID, for a USB scanner typing into the search box) via a
format selector on the print page. New barcodeDataURI using
github.com/boombuler/barcode alongside the existing QR generator.
- "Start at #" lets printing resume partway into an already-used sheet
instead of wasting it on a fresh one.
- Added the Avery 5260 label template (same grid as 5160, rounded
corners, so it isn't flagged approximate).
Also: the configured Site Name now shows on the login page too, not just
the authenticated navbar.
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Sign in · inventree</title>
|
||||
<title>Sign in · {{siteName}}</title>
|
||||
<link rel="stylesheet" href="/static/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="/static/icons/bootstrap-icons.css">
|
||||
<link rel="stylesheet" href="/static/css/app.css">
|
||||
@@ -14,7 +14,7 @@
|
||||
<div class="col-11 col-sm-8 col-md-5 col-lg-4">
|
||||
<div class="text-center mb-4">
|
||||
<div class="display-6"><i class="bi bi-diagram-3-fill"></i></div>
|
||||
<h1 class="h4 mt-2 mb-0">inventree</h1>
|
||||
<h1 class="h4 mt-2 mb-0">{{siteName}}</h1>
|
||||
<div class="text-body-secondary small">IT asset inventory</div>
|
||||
</div>
|
||||
<div class="card shadow-sm">
|
||||
|
||||
@@ -5,27 +5,42 @@
|
||||
body { margin: 0; background: #fff; }
|
||||
.label-sheet { margin: 0; }
|
||||
}
|
||||
.label-tag { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-weight: 700; font-size: 11pt; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
||||
.label-name { font-size: 8pt; color: #333; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.label-tag { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-weight: 700; font-size: 11pt; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; }
|
||||
.label-name { font-size: 8pt; color: #333; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100%; }
|
||||
.label.format-barcode .label-text { text-align: center; }
|
||||
</style>
|
||||
|
||||
<div class="print-toolbar d-flex flex-wrap align-items-center gap-3 p-3 border-bottom">
|
||||
<button class="btn btn-primary" onclick="window.print()"><i class="bi bi-printer"></i> Print</button>
|
||||
<span class="text-body-secondary">{{len .Labels}} label(s)</span>
|
||||
<form method="get" class="d-flex align-items-center gap-2 ms-auto">
|
||||
{{if .AssetID}}<input type="hidden" name="asset_id" value="{{.AssetID}}">{{end}}
|
||||
{{if .FolderID}}<input type="hidden" name="folder_id" value="{{.FolderID}}">{{end}}
|
||||
{{if .Recursive}}<input type="hidden" name="recursive" value="1">{{end}}
|
||||
<label class="form-label mb-0 small text-body-secondary" for="labelTemplateSelect">Label sheet</label>
|
||||
<form method="get" class="d-flex flex-wrap align-items-center gap-2 ms-auto">
|
||||
{{if .AssetIDs}}<input type="hidden" name="asset_ids" value="{{.AssetIDs}}">{{end}}
|
||||
{{if .FolderIDs}}<input type="hidden" name="folder_ids" value="{{.FolderIDs}}">{{end}}
|
||||
|
||||
<label class="form-label mb-0 small text-body-secondary" for="labelFormatSelect">Code</label>
|
||||
<select class="form-select form-select-sm" id="labelFormatSelect" name="format" onchange="this.form.submit()">
|
||||
<option value="qr" {{if eq .Format "qr"}}selected{{end}}>QR code (opens on scan)</option>
|
||||
<option value="barcode" {{if eq .Format "barcode"}}selected{{end}}>Barcode (Code128)</option>
|
||||
</select>
|
||||
|
||||
<label class="form-label mb-0 small text-body-secondary" for="labelTemplateSelect">Sheet</label>
|
||||
<select class="form-select form-select-sm" id="labelTemplateSelect" name="template" onchange="this.form.submit()">
|
||||
{{range $key := .TemplateKeys}}
|
||||
{{$t := index $.Templates $key}}
|
||||
<option value="{{$key}}" {{if eq $key $.Selected.Key}}selected{{end}}>{{$t.DisplayName}}</option>
|
||||
{{end}}
|
||||
</select>
|
||||
|
||||
<label class="form-label mb-0 small text-body-secondary" for="labelStartInput">Start at #</label>
|
||||
<input type="number" class="form-control form-control-sm" id="labelStartInput" name="start" value="{{.StartPos}}" min="1" style="width: 70px" onchange="this.form.submit()">
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<p class="text-body-secondary small px-3 pt-2 mb-0">
|
||||
Already used some labels off this sheet? Set "Start at #" to the next free position (counting left-to-right,
|
||||
top-to-bottom) instead of wasting them and starting a new sheet.
|
||||
</p>
|
||||
|
||||
{{if .Selected.Approximate}}
|
||||
<div class="alert alert-warning m-3">
|
||||
This layout's grid/margins aren't from a verified spec sheet - print one test page on plain paper and
|
||||
@@ -34,10 +49,11 @@
|
||||
{{end}}
|
||||
|
||||
<div class="label-sheet">
|
||||
{{range .BlankCells}}<div class="label-blank"></div>{{end}}
|
||||
{{range .Labels}}
|
||||
<div class="label">
|
||||
<img src="{{.QRData}}" alt="QR code for {{.Asset.AssetTag}}">
|
||||
<div class="min-w-0">
|
||||
<div class="label format-{{$.Format}}">
|
||||
<img src="{{.ImageData}}" alt="{{if eq $.Format "barcode"}}Barcode{{else}}QR code{{end}} for {{.Asset.AssetTag}}">
|
||||
<div class="min-w-0 label-text">
|
||||
<div class="label-tag">{{.Asset.AssetTag}}</div>
|
||||
<div class="label-name">{{.Asset.Name}}</div>
|
||||
</div>
|
||||
|
||||
@@ -111,7 +111,7 @@
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Move Asset</h5>
|
||||
<h5 class="modal-title" id="moveAssetModalTitle">Move Asset</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
@@ -151,6 +151,28 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Bulk status change (multi-select > Edit Status...) -->
|
||||
<div class="modal fade" id="bulkStatusModal" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="bulkStatusModalTitle">Edit Status</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<label class="form-label">New Status</label>
|
||||
<select class="form-select" id="bulkStatusSelect">
|
||||
{{range .Statuses}}<option value="{{.ID}}">{{.Name}}</option>{{end}}
|
||||
</select>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Cancel</button>
|
||||
<button type="button" class="btn btn-primary" id="confirmBulkStatusBtn">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Import CSV into a specific folder (right-click a folder > Import CSV...) -->
|
||||
<div class="modal fade" id="importModal" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
|
||||
Reference in New Issue
Block a user