fix(scale)+docs: fix scale calculator units (cm not m), update CLAUDE.md+ROADMAP.md

Scale calculator:
- Real input now in cm (not m) — more natural for architects
- Drawing output in mm (unchanged)
- Formula: drawing_mm = real_cm * 10 / scale (was val/scale, wrong)
- Reverse: real_cm = drawing_mm * scale / 10 (was val*scale, wrong)
- Secondary display: real in m; drawing in cm; reverse also shows mm
- Added 1:20 preset (useful for detail drawings)
- Removed Camere tab (not useful)

Docs:
- CLAUDE.md: update Mini Utilities 0.3.0 + Password Vault 0.4.0 descriptions
- ROADMAP.md: add task 8.04 (this session), bump module versions, renumber 8.05-8.08

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
AI Assistant
2026-03-12 23:52:54 +02:00
parent e583fdecc9
commit 75a7ab91ca
3 changed files with 87 additions and 45 deletions
+2 -2
View File
@@ -105,8 +105,8 @@ legacy/ # Original HTML tools for reference
| 5 | **Tag Manager** | `/tag-manager` | 0.2.0 | CRUD tags, category/scope/color, US/SDT seeds, mandatory categories, **ManicTime bidirectional sync** |
| 6 | **IT Inventory** | `/it-inventory` | 0.2.0 | Dynamic equipment types, rented status (purple pulse), **42U rack visualization**, type/status/company filters |
| 7 | **Address Book** | `/address-book` | 0.1.1 | CRUD contacts, card grid, vCard export, Registratura reverse lookup, **dynamic types (creatable)**, **alphabetically sorted type dropdown** |
| 8 | **Password Vault** | `/password-vault` | 0.3.0 | CRUD credentials, 9 categorii cu iconițe, **WiFi QR code real**, context-aware form, strength meter, company scope, **AES-256-GCM encryption** |
| 9 | **Mini Utilities** | `/mini-utilities` | 0.2.0 | Text case, char counter, percentage, **TVA calculator (19%)**, area converter, U→R, num→text, artifact cleaner, MDLPA, **extreme PDF compression (GS+qpdf)**, PDF unlock, **DWG→DXF**, OCR, color palette, **paste on all drop zones**, **thermal drag-drop reorder** |
| 8 | **Password Vault** | `/password-vault` | 0.4.0 | CRUD credentials, 9 categorii cu iconițe, **WiFi QR code real**, context-aware form, strength meter, company scope, **AES-256-GCM encryption**, **utilizatori multipli per intrare** (VaultUser[]: username/password/email/notes, colapsibil în form, badge în list) |
| 9 | **Mini Utilities** | `/mini-utilities` | 0.3.0 | Text case, char counter, percentage, **TVA calculator (cotă configurabilă: 5/9/19/21% + custom)**, area converter, U→R, num→text, artifact cleaner, MDLPA, **extreme PDF compression (GS+qpdf)**, PDF unlock, **DWG→DXF**, OCR, color palette, **paste on all drop zones**, **thermal drag-drop reorder**, **Calculator scară desen** (real cm↔desen mm, 7 preseturi 1:20..1:5000 + custom) |
| 10 | **Prompt Generator** | `/prompt-generator` | 0.2.0 | Template-driven prompt builder, **18 templates** (14 text + 4 image), search bar, target type filter |
| 11 | **Digital Signatures** | `/digital-signatures` | 0.1.0 | CRUD assets, drag-and-drop file upload, tag chips |
| 12 | **Word Templates** | `/word-templates` | 0.1.0 | Template library, 8 categories, version tracking, .docx placeholder auto-detection |
+19 -6
View File
@@ -36,13 +36,13 @@
| 2 | Email Signature | 0.1.0 | COMPLETE | US/SDT addresses may need update | AD sync, branding packs, promo banners |
| 3 | Word XML | 0.1.0 | COMPLETE | — | Schema validator, visual mapper |
| 4 | Digital Signatures | 0.1.0 | COMPLETE | — | Permission layers, document insertion |
| 5 | Password Vault | 0.3.0 | COMPLETE | — | Hardware key, rotation reminders, Passbolt |
| 5 | Password Vault | 0.4.0 | COMPLETE | — | Hardware key, rotation reminders, Passbolt |
| 6 | IT Inventory | 0.2.0 | COMPLETE | — | Network scan import |
| 7 | Address Book | 0.1.1 | COMPLETE | — | Email sync, deduplication |
| 8 | Prompt Generator | 0.2.0 | HARDENING | Bug fixes, new idea TBD | Prompt scoring, more image templates |
| 9 | Word Templates | 0.1.0 | COMPLETE | No clause library; no Word generation | Diff compare, document generator |
| 10 | Tag Manager | 0.2.0 | HARDENING | Logic/workflow fix, ERP API exposure needed | Smart suggestions |
| 11 | Mini Utilities | 0.2.0 | COMPLETE | — | More converters, more tools TBD |
| 11 | Mini Utilities | 0.3.0 | COMPLETE | — | More converters, more tools TBD |
| 12 | Dashboard | 0.1.0 | COMPLETE | — | Custom dashboards per role |
| 13 | AI Chat | 0.2.0 | COMPLETE | Needs API key env vars for real AI | Streaming, model selector, conversation templates |
| 14 | Hot Desk | 0.1.1 | COMPLETE | — | — |
@@ -845,25 +845,38 @@ Env vars (hardcoded in docker-compose.yml for Portainer CE):
---
### 8.04 `[STANDARD]` Registratura — Print/PDF Export
### 8.04 `[STANDARD]` Mini Utilities + Password Vault — UX Improvements (2026-03-12)
**What:** TVA calculator with configurable rate, new scale calculator for drawings, multi-user support in Password Vault.
**Implemented:**
- **TVA cotă configurabilă**: preseturi 5%/9%/19%/21% + câmp custom; rata efectivă afișată în rezultate; titlu card actualizat dinamic
- **Calculator scară desen** (tab nou "Scară"): modul Real→Desen (input cm, output mm) și Desen→Real (input mm, output cm); 7 preseturi 1:20..1:5000 + scară custom 1:X; afișare secundară în m/cm; copy button pe rezultat
- Logică: `drawing_mm = real_cm × 10 / scale` / `real_cm = drawing_mm × scale / 10`
- **Password Vault — utilizatori multipli**: tip `VaultUser { username, password, email?, notes? }`; câmp `additionalUsers: VaultUser[]` pe `VaultEntry`; secțiune colapsibilă "Utilizatori suplimentari" în form; badge cu numărul de utilizatori în lista de intrări; normalizare backward-compat în `useVault` hook
**Files:** `src/modules/mini-utilities/components/mini-utilities-module.tsx`, `src/modules/password-vault/types.ts`, `src/modules/password-vault/components/password-vault-module.tsx`, `src/modules/password-vault/hooks/use-vault.ts`
**Versions:** Mini Utilities 0.2.0→0.3.0, Password Vault 0.3.0→0.4.0
---
### 8.05 `[STANDARD]` Registratura — Print/PDF Export
**What:** Export registry as formatted PDF. Options: full registry, single entry, deadline summary.
---
### 8.05 `[STANDARD]` Word Templates — Clause Library + Document Generator
### 8.06 `[STANDARD]` Word Templates — Clause Library + Document Generator
**What:** In-app clause composition, template preview, simple Word generation from templates.
---
### 8.06 `[STANDARD]` N8N Webhook Integration
### 8.07 `[STANDARD]` N8N Webhook Integration
**What:** Fire webhooks on events (new entry, deadline approaching, status change). N8N at http://10.10.10.166:5678.
---
### 8.07 `[STANDARD]` Mobile Responsiveness Audit
### 8.08 `[STANDARD]` Mobile Responsiveness Audit
**What:** Test all modules on 375px/768px. Fix overflowing tables, forms, sidebar.
@@ -2527,6 +2527,7 @@ function ColorPaletteExtractor() {
// ─── Calculator Scară ─────────────────────────────────────────────────────────
const SCALE_PRESETS = [
{ label: "1:20", value: 20 },
{ label: "1:50", value: 50 },
{ label: "1:100", value: 100 },
{ label: "1:200", value: 200 },
@@ -2535,6 +2536,15 @@ const SCALE_PRESETS = [
{ label: "1:5000", value: 5000 },
];
/**
* Scale calculator — all real dimensions in cm, drawing in mm.
*
* Real → Desen: drawing_mm = real_cm × 10 / scale
* e.g. 350 cm at 1:100 → 350 × 10 / 100 = 35 mm
*
* Desen → Real: real_cm = drawing_mm × scale / 10
* e.g. 35 mm at 1:100 → 35 × 100 / 10 = 350 cm = 3.50 m
*/
function ScaleCalculator() {
const [scale, setScale] = useState(100);
const [customScale, setCustomScale] = useState("");
@@ -2547,27 +2557,22 @@ function ScaleCalculator() {
customScale !== "" ? parseFloat(customScale) || scale : scale;
const val = parseFloat(inputVal);
// Real→Desen: val(m) × 1000 → mm, ÷ scale → mm pe desen
// Desen→Real: val(mm) × scale → mm real, ÷ 1000 → m
// drawing_mm = real_cm × 10 / scale
// real_cm = drawing_mm × scale / 10
const result = !isNaN(val)
? mode === "real-to-drawing"
? (val * 1000) / effectiveScale
: (val * effectiveScale) / 1000
? (val * 10) / effectiveScale // cm → mm on drawing
: (val * effectiveScale) / 10 // mm drawing → cm real
: NaN;
const unitIn = mode === "real-to-drawing" ? "m (real)" : "mm (desen)";
const fmtDesen = (n: number) =>
const fmt2 = (n: number) =>
isNaN(n)
? "—"
: n.toLocaleString("ro-RO", { minimumFractionDigits: 2, maximumFractionDigits: 2 }) + " mm";
const fmtReal = (n: number) =>
isNaN(n)
? "—"
: n.toLocaleString("ro-RO", { minimumFractionDigits: 3, maximumFractionDigits: 3 }) + " m";
: n.toLocaleString("ro-RO", { minimumFractionDigits: 2, maximumFractionDigits: 2 });
return (
<div className="space-y-4">
{/* Scale picker */}
<div>
<Label className="text-xs text-muted-foreground">Scară</Label>
<div className="mt-1 flex flex-wrap items-center gap-2">
@@ -2595,6 +2600,8 @@ function ScaleCalculator() {
</div>
</div>
</div>
{/* Mode */}
<div className="flex gap-2">
<Button
variant={mode === "real-to-drawing" ? "default" : "outline"}
@@ -2611,49 +2618,71 @@ function ScaleCalculator() {
Desen Real
</Button>
</div>
{/* Input */}
<div>
<Label>
{mode === "real-to-drawing"
? "Dimensiune reală (m)"
? "Dimensiune reală (cm)"
: "Dimensiune desen (mm)"}
</Label>
<div className="mt-1 flex gap-2">
<div className="mt-1 flex gap-2 items-center">
<Input
type="number"
min={0}
step={0.5}
value={inputVal}
onChange={(e) => setInputVal(e.target.value)}
placeholder={mode === "real-to-drawing" ? "ex: 5.4" : "ex: 54"}
placeholder={mode === "real-to-drawing" ? "ex: 350" : "ex: 35"}
className="flex-1"
/>
<span className="flex items-center text-sm text-muted-foreground px-2">
{unitIn}
<span className="text-sm text-muted-foreground shrink-0">
{mode === "real-to-drawing" ? "cm" : "mm"}
</span>
</div>
</div>
{/* Result */}
{!isNaN(val) && val > 0 && (
<div className="rounded-md border bg-muted/30 p-4 space-y-2 text-sm">
<p className="text-xs text-muted-foreground">
<p className="text-xs text-muted-foreground font-medium">
Scară 1:{effectiveScale}
</p>
<p className="text-base font-medium">
{mode === "real-to-drawing" ? (
<>
{val} m real {" "}
<strong className="text-primary">{fmtDesen(result)}</strong> pe desen
<CopyButton text={isNaN(result) ? "" : result.toFixed(2)} />
</>
) : (
<>
{val} mm desen {" "}
<strong className="text-primary">{fmtReal(result)}</strong> real
<CopyButton text={isNaN(result) ? "" : result.toFixed(3)} />
</>
)}
</p>
{mode === "real-to-drawing" && !isNaN(result) && (
<p className="text-xs text-muted-foreground">
= {(result / 10).toFixed(2)} cm pe desen
</p>
{mode === "real-to-drawing" ? (
<>
<p>
Real: <strong>{fmt2(val)} cm</strong>{" "}
<span className="text-muted-foreground">
({fmt2(val / 100)} m)
</span>
</p>
<p className="text-base border-t pt-2">
Pe desen:{" "}
<strong className="text-primary">{fmt2(result)} mm</strong>
<CopyButton text={fmt2(result)} />
</p>
{!isNaN(result) && (
<p className="text-xs text-muted-foreground">
= {fmt2(result / 10)} cm pe desen
</p>
)}
</>
) : (
<>
<p>
Pe desen: <strong>{fmt2(val)} mm</strong>
</p>
<p className="text-base border-t pt-2">
Real:{" "}
<strong className="text-primary">{fmt2(result)} cm</strong>
<CopyButton text={fmt2(result)} />
</p>
{!isNaN(result) && (
<p className="text-xs text-muted-foreground">
= {fmt2(result / 100)} m &nbsp;·&nbsp; {fmt2(result * 10)} mm
</p>
)}
</>
)}
</div>
)}