Skip to content

Commit 49e51db

Browse files
committed
feat(inventory): hard-delete + cloudSync anti-resurrection, updatedAt rename, auto-container
Spool sync — ISO with printer pattern: - markSpoolDeleted now does a Firestore batch.delete() (no tombstone) Twin is hard-deleted in the same batch - purgeLegacyTombstones: auto-cleans pre-v1.7.4 deleted:true docs on every live snapshot (one-shot migration, no-op once complete) - Remove "Show deleted" feature: debug tab, HTML section, CSS rules and all JS functions (renderDeletedSpoolsList, restoreDeletedSpool, purgeDeletedSpool) removed entirely - Rename last_update -> updatedAt ISO with printer model; all writes now use FieldValue.serverTimestamp(); normalizeRow reads updatedAt with fallback to last_update / updated_at for legacy documents Container auto-assignment: - resolveContainerForBrand(brandId): mirrors Flutter _resolveSpoolForBrand (brand match -> Generic fallback -> first entry) - autoAssignMissingContainers(uid, raw): called on every live snapshot, batch-writes container_id + container_weight + updatedAt to spools that have none; new spools from Add Product are covered automatically on the next snapshot; no-op once all spools have a container Bump version 1.7.3 -> 1.7.4 Update CHANGELOG, ROADMAP, CODEMAP
1 parent 92c2430 commit 49e51db

7 files changed

Lines changed: 140 additions & 253 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,23 @@ Versions follow [Semantic Versioning](https://semver.org/).
55

66
---
77

8+
## v1.7.4 — 2026-05-20
9+
10+
### Spool sync — ISO with printer pattern
11+
12+
- **Hard delete for spools** — `markSpoolDeleted` now issues a Firestore `batch.delete()` instead of writing a `deleted: true` tombstone. Twin is hard-deleted in the same batch. No resurrection possible once the doc is gone.
13+
- **Anti-resurrection guard** — `cloudSync` flag (local-only, never pushed to Firestore) marks every spool that has ever reached the cloud. If Tiger Studio later hard-deletes it and Flutter reconnects, Flutter's push path skips the entry instead of sending it back. ISO with the existing printer pattern.
14+
- **`purgeLegacyTombstones`** — on every live Firestore snapshot, any remaining `deleted: true` docs (written by pre-v1.7.4 clients) are automatically hard-deleted. One-shot migration; no-op once migration is complete.
15+
- **Removed "Show deleted" feature** — spools are now always hard-deleted; the debug panel "Deleted" tab and its HTML/CSS/JS were removed entirely. Cleaner architecture, no stale data accumulation.
16+
- **`updatedAt` field** — renamed `last_update` → `updatedAt` (ISO with the printer data model). All Firestore writes now use `FieldValue.serverTimestamp()` for `updatedAt`. `normalizeRow` reads `updatedAt` first with fallback to `last_update` for legacy documents already in Firestore.
17+
18+
### Container auto-assignment
19+
20+
- **`resolveContainerForBrand(brandId)`** — mirrors Flutter `_resolveSpoolForBrand`: (1) brand-specific match, (2) Generic fallback (`brandId == 0` → `custom_cardboard`), (3) first catalog entry.
21+
- **`autoAssignMissingContainers(uid, inventoryRaw)`** — called on every live Firestore snapshot. Finds spools without `container_id`, resolves the container from brand, and batch-writes `container_id` + `container_weight` + `updatedAt`. Self-healing: new spools added via "Add Product" get a container automatically on the next snapshot. No-op once all spools have a container.
22+
23+
---
24+
825
## v1.7.3 — 2026-05-19
926

1027
### Hotfix — Firebase Auth broken after v1.7.2 Windows fix

‎ROADMAP.md‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ Sizes: **S** = a few hours · **M** = a day · **L** = several days · **XL** =
1515
Grouped by domain. Versions in parentheses are the release that landed the feature; the changelog has the detail.
1616

1717
### Inventory & spools
18+
- ✅ **Hard delete + anti-resurrection (cloudSync)** — spools deleted from Tiger Studio are hard-deleted from Firestore (no tombstone). Flutter's `cloudSync` flag prevents resurrection on reconnect. Legacy `deleted: true` tombstones auto-purged on first live snapshot. ISO with the printer pattern. (v1.7.4)
19+
- ✅ **`updatedAt` rename** — `last_update` → `updatedAt` (ISO with printer model). All writes use `FieldValue.serverTimestamp()`. Legacy docs handled via `normalizeRow` fallback. (v1.7.4)
20+
- ✅ **Container auto-assignment** — `resolveContainerForBrand` + `autoAssignMissingContainers`: every spool without a `container_id` gets one automatically on the next live snapshot, matched to its brand. (v1.7.4)
1821
- ✅ Real-time Firestore sync of inventory · table + grid views · column sort · search filter (v1.0+)
1922
- ✅ Spool detail side panel — color block, print settings, weight slider with debounced auto-save, links, container, raw JSON (v1.0+)
2023
- ✅ NFC RFID reading via ACR122U (`nfc-pcsc`) — auto-opens the matching spool (v1.0+)

‎package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "tigertag-inventory",
3-
"version": "1.7.3",
3+
"version": "1.7.4",
44
"description": "Tiger Studio Manager — desktop app",
55
"main": "main.js",
66
"engines": {

‎renderer/CODEMAP.md‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ L… Add-printer flow (mDNS → port-scan → Add by IP → manual pr
119119
| 4355-4365 | Debug panel toggle | |
120120
| 4366-4439 | Diagnostic / report-problem modal | |
121121
| 4440-4523 | Settings → About → auto-update toggle + "Check for updates now" | |
122-
| 4524-4696 | **Deleted spools list** (debug tab) | |
122+
| 4524-4696 | ~~Deleted spools list (debug tab)~~ — **removed in v1.7.4** (hard-delete architecture, no tombstones) | |
123123
| 4697-4777 | **Firestore explorer** (debug tab) — type a path, fetch JSON, copy | |
124124
| 4778-4788 | Community buttons (GitHub, Discord, MakerWorld) | |
125125
| 4789-4813 | Language select | |
@@ -157,8 +157,10 @@ Big section divider at L5533. Ported from the Flutter `SnapmakerWebSocketPage`.
157157
| 5946-6159 | **Manual filament edit — bottom sheet (entry)** — vendor/material lists, summary, render flow | `openSnapFilamentEdit`, `closeSnapFilamentEdit` |
158158
| 6160-6201 | Bottom-sheet sub-pickers (Filament + Color stack on top of summary) | `sfeOpenFilamentSheet`, `sfeOpenColorSheet` |
159159
| 6202-6243 | Filament screen (vendor → material click handlers) | |
160-
| 6244-6343 | **Color screen** — 5×5 grid (24 presets + custom), inline custom slot | `sfeRenderColorGrid` |
161-
| 6344-6486 | **Moonraker file/thumbnail helpers** — path normalize, thumbnail URL, RGBA/hex parse, format temp/duration | `snapFileUrl`, `snapThumbUrl`, `snapParseRgbaHex` |
160+
| 6244-6321 | **Color screen** — 5×5 grid (24 presets + custom), inline custom slot | `sfeRenderColorGrid` |
161+
| 6322-6357 | **Hard delete** — `markSpoolDeleted` (batch.delete doc + twin) + `purgeLegacyTombstones` (one-shot auto-cleanup of pre-v1.7.4 `deleted:true` docs) | `markSpoolDeleted`, `purgeLegacyTombstones` |
162+
| 6358-6405 | **Container auto-assign** — `resolveContainerForBrand` (brand → catalog entry, ISO with Flutter) + `autoAssignMissingContainers` (batch-write on every live snapshot; no-op once all spools have a container) | `resolveContainerForBrand`, `autoAssignMissingContainers` |
163+
| 6406-6450 | **Moonraker file/thumbnail helpers** — path normalize, thumbnail URL, RGBA/hex parse, format temp/duration | `snapFileUrl`, `snapThumbUrl`, `snapParseRgbaHex` |
162164
| 6487-6519 | Text-color contrast picker for color squares | `snapTextColor` |
163165
| 6520-6680 | **Live block render** — `renderSnapmakerLiveInner()`: connection header, camera, print-job card, temperature row, **filament grid (big colored squares)** | `renderSnapmakerLiveInner` |
164166
| 6681-7130 | **WS request log** — push, custom JSON send, render | `snapLogPush`, `snapSendCustomJson`, `renderSnapmakerLogInner` |

‎renderer/css/70-detail-misc.css‎

Lines changed: 0 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -880,66 +880,6 @@
880880
.dbg-tab:hover:not(.active) { color: var(--text); }
881881
.dbg-pane { min-height: 0; }
882882

883-
/* Deleted-spools list (debug → Deleted tab) */
884-
.dbg-del-row {
885-
display: flex; align-items: center; gap: 10px;
886-
padding: 8px 10px;
887-
background: var(--surface-2); border: 1px solid var(--border);
888-
border-radius: 8px;
889-
}
890-
.dbg-del-puck {
891-
width: 22px; height: 36px; flex-shrink: 0;
892-
border-radius: 4px;
893-
border: 1px solid rgba(0,0,0,.12);
894-
box-shadow: inset 0 0 0 1px rgba(255,255,255,.18);
895-
}
896-
.dbg-del-meta { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
897-
.dbg-del-name {
898-
font-size: 12px; font-weight: 600; color: var(--text);
899-
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
900-
}
901-
.dbg-del-sub {
902-
font-size: 10.5px; color: var(--muted);
903-
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
904-
}
905-
.dbg-del-tech {
906-
font-size: 9.5px; color: var(--muted); font-family: var(--mono);
907-
opacity: .75;
908-
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
909-
}
910-
.dbg-del-restore {
911-
flex-shrink: 0;
912-
width: 30px; height: 30px;
913-
display: flex; align-items: center; justify-content: center;
914-
font-size: 16px;
915-
}
916-
.dbg-del-restore:hover { color: var(--primary); border-color: var(--primary); }
917-
.dbg-del-restore:disabled { opacity: .5; cursor: not-allowed; }
918-
919-
/* Permanent-delete button — red on hover to signal irreversibility */
920-
.dbg-del-purge {
921-
flex-shrink: 0;
922-
width: 30px; height: 30px;
923-
display: flex; align-items: center; justify-content: center;
924-
font-size: 14px;
925-
}
926-
.dbg-del-purge:hover { color: var(--danger); border-color: var(--danger); background: var(--danger-soft, rgba(220,38,38,.08)); }
927-
.dbg-del-purge:disabled { opacity: .5; cursor: not-allowed; }
928-
929-
/* Search box for the Deleted tab */
930-
.dbg-del-search { position: relative; }
931-
.dbg-del-search .icon {
932-
position: absolute; right: 10px; top: 50%; transform: translateY(-50%);
933-
background: var(--muted); pointer-events: none;
934-
}
935-
.dbg-del-search input {
936-
width: 100%; padding: 7px 30px 7px 10px;
937-
background: var(--surface); border: 1px solid var(--border);
938-
border-radius: 8px; color: var(--text); font-size: 12px;
939-
font-family: inherit; outline: none;
940-
transition: border-color .15s;
941-
}
942-
.dbg-del-search input:focus { border-color: var(--primary); }
943883
.dbg-select {
944884
padding: 6px 8px; background: var(--surface-2); border: 1px solid var(--border);
945885
border-radius: 8px; font-size: 12px; color: var(--text); outline: none; cursor: pointer;

‎renderer/inventory.html‎

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1072,7 +1072,6 @@
10721072
<div class="dbg-tabs">
10731073
<button class="dbg-tab active" data-tab="api">API</button>
10741074
<button class="dbg-tab" data-tab="fs">Firestore</button>
1075-
<button class="dbg-tab" data-tab="del" data-i18n="dbgDeletedTab">Deleted</button>
10761075
</div>
10771076

10781077
<!-- API tab -->
@@ -1101,22 +1100,6 @@
11011100
<pre class="json" id="fsExplPre" style="flex:1;margin:0;overflow:auto">{}</pre>
11021101
</div>
11031102

1104-
<!-- Deleted spools tab -->
1105-
<div class="dbg-pane hidden" id="dbgPaneDel" style="display:flex;flex-direction:column;gap:8px;padding:12px 14px;flex:1;overflow:hidden">
1106-
<div style="display:flex;align-items:center;justify-content:space-between;flex-shrink:0">
1107-
<div style="font-size:11px;color:var(--muted)">
1108-
<span data-i18n="dbgDeletedSub">Soft-deleted spools — click ↺ to restore</span>
1109-
<span style="margin-left:6px">(<span id="dbgDelCount">0</span>)</span>
1110-
</div>
1111-
<button id="dbgDelRefresh" class="ghost sm" title="Refresh">↻</button>
1112-
</div>
1113-
<div class="dbg-del-search" style="position:relative;flex-shrink:0">
1114-
<input id="dbgDelSearch" type="text" data-i18n-placeholder="searchPlaceholder" placeholder="Search material, brand, color, UID…" />
1115-
<span class="icon icon-search icon-13"></span>
1116-
</div>
1117-
<div id="dbgDelList" style="flex:1;overflow-y:auto;display:flex;flex-direction:column;gap:6px;padding-right:2px"></div>
1118-
</div>
1119-
11201103
</div>
11211104
</div>
11221105

0 commit comments

Comments
 (0)