A small IT asset inventory system built around a nested folder tree, matching the project brief: Go + html/template + Bootstrap 5 + vanilla JS, SQLite storage (WAL, FKs, migrations), embedded templates/static assets, single compiled binary. - AJAX-driven inventory tree: lazy-loaded folder expansion with per-user persisted expand state, drag-and-drop move for folders/assets, safe delete-with-relocate-or-cascade confirmation, search with breadcrumbs. - Assets: CRUD, duplicate, move, auto-generated IT-000001-style IDs, CSV import (with optional folder auto-create) and export. - Admin-defined item types with custom fields (text/long_text/number/date/ boolean/dropdown/url/ip_address). - Configurable statuses, session-based auth (bcrypt, secure cookies), admin/user roles, user management. - CLI: serve, version, database migrate, database backup; /healthz; graceful shutdown; Dockerfile/docker-compose/systemd unit; Go test suite.
113 lines
5.2 KiB
HTML
113 lines
5.2 KiB
HTML
{{define "modals"}}
|
|
{{if .User.IsAdmin}}
|
|
<!-- Create / rename folder -->
|
|
<div class="modal fade" id="folderModal" tabindex="-1">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<form id="folderForm">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title" id="folderModalTitle">New Folder</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<input type="hidden" name="folder_id" id="folderFormId">
|
|
<input type="hidden" name="parent_id" id="folderFormParentId">
|
|
<div class="mb-3">
|
|
<label class="form-label">Name</label>
|
|
<input type="text" class="form-control" name="name" id="folderFormName" required autofocus>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label">Description</label>
|
|
<textarea class="form-control" name="description" id="folderFormDescription" rows="2"></textarea>
|
|
</div>
|
|
<div class="row g-2">
|
|
<div class="col">
|
|
<label class="form-label">Icon <span class="text-body-secondary">(Bootstrap Icons class, optional)</span></label>
|
|
<input type="text" class="form-control" name="icon" id="folderFormIcon" placeholder="bi-folder-fill">
|
|
</div>
|
|
<div class="col-4">
|
|
<label class="form-label">Sort Order</label>
|
|
<input type="number" class="form-control" name="sort_order" id="folderFormSortOrder" value="0">
|
|
</div>
|
|
</div>
|
|
<div class="invalid-feedback d-block d-none" id="folderFormError"></div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Cancel</button>
|
|
<button type="submit" class="btn btn-primary">Save Folder</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Delete folder confirmation -->
|
|
<div class="modal fade" id="deleteFolderModal" tabindex="-1">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title"><i class="bi bi-exclamation-triangle-fill text-danger"></i> Delete Folder</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<p>Delete <strong id="deleteFolderName"></strong>?</p>
|
|
<div id="deleteFolderEmptyNotice" class="d-none">
|
|
<p class="text-body-secondary">This folder is empty and can be deleted safely.</p>
|
|
</div>
|
|
<div id="deleteFolderNotEmptyNotice" class="d-none">
|
|
<div class="alert alert-warning py-2">
|
|
This folder contains <span id="deleteFolderCounts"></span>. Choose what to do with its contents.
|
|
</div>
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="radio" name="delete_mode" id="modeRelocate" value="relocate" checked>
|
|
<label class="form-check-label" for="modeRelocate">Move contents to…</label>
|
|
</div>
|
|
<select class="form-select mt-1 mb-2" id="deleteFolderDestination"></select>
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="radio" name="delete_mode" id="modeCascade" value="cascade">
|
|
<label class="form-check-label text-danger" for="modeCascade">Permanently delete everything inside</label>
|
|
</div>
|
|
</div>
|
|
</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-danger" id="confirmDeleteFolderBtn">Delete Folder</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Import CSV -->
|
|
<div class="modal fade" id="importModal" tabindex="-1">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<form id="importForm">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">Import Assets from CSV</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div class="mb-3">
|
|
<label class="form-label">CSV File</label>
|
|
<input type="file" class="form-control" name="file" id="importFile" accept=".csv" required>
|
|
</div>
|
|
<div class="form-check mb-2">
|
|
<input class="form-check-input" type="checkbox" name="create_missing_folders" id="importCreateFolders" value="1" checked>
|
|
<label class="form-check-label" for="importCreateFolders">Create missing folders referenced in the Folder column</label>
|
|
</div>
|
|
<p class="text-body-secondary small mb-0">
|
|
Columns: Asset ID, Name, Item Type, Folder, Status, Assigned User, Notes, plus <code>cf_<field_key></code> for custom fields.
|
|
</p>
|
|
<div id="importResult" class="mt-3"></div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Close</button>
|
|
<button type="submit" class="btn btn-primary" id="importSubmitBtn">Import</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{{end}}
|
|
{{end}}
|