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:
@@ -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** |
|
| 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 |
|
| 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** |
|
| 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** |
|
| 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.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** |
|
| 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 |
|
| 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 |
|
| 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 |
|
| 12 | **Word Templates** | `/word-templates` | 0.1.0 | Template library, 8 categories, version tracking, .docx placeholder auto-detection |
|
||||||
|
|||||||
+19
-6
@@ -36,13 +36,13 @@
|
|||||||
| 2 | Email Signature | 0.1.0 | COMPLETE | US/SDT addresses may need update | AD sync, branding packs, promo banners |
|
| 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 |
|
| 3 | Word XML | 0.1.0 | COMPLETE | — | Schema validator, visual mapper |
|
||||||
| 4 | Digital Signatures | 0.1.0 | COMPLETE | — | Permission layers, document insertion |
|
| 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 |
|
| 6 | IT Inventory | 0.2.0 | COMPLETE | — | Network scan import |
|
||||||
| 7 | Address Book | 0.1.1 | COMPLETE | — | Email sync, deduplication |
|
| 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 |
|
| 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 |
|
| 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 |
|
| 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 |
|
| 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 |
|
| 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 | — | — |
|
| 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.
|
**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.
|
**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.
|
**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.
|
**What:** Test all modules on 375px/768px. Fix overflowing tables, forms, sidebar.
|
||||||
|
|
||||||
|
|||||||
@@ -2527,6 +2527,7 @@ function ColorPaletteExtractor() {
|
|||||||
// ─── Calculator Scară ─────────────────────────────────────────────────────────
|
// ─── Calculator Scară ─────────────────────────────────────────────────────────
|
||||||
|
|
||||||
const SCALE_PRESETS = [
|
const SCALE_PRESETS = [
|
||||||
|
{ label: "1:20", value: 20 },
|
||||||
{ label: "1:50", value: 50 },
|
{ label: "1:50", value: 50 },
|
||||||
{ label: "1:100", value: 100 },
|
{ label: "1:100", value: 100 },
|
||||||
{ label: "1:200", value: 200 },
|
{ label: "1:200", value: 200 },
|
||||||
@@ -2535,6 +2536,15 @@ const SCALE_PRESETS = [
|
|||||||
{ label: "1:5000", value: 5000 },
|
{ 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() {
|
function ScaleCalculator() {
|
||||||
const [scale, setScale] = useState(100);
|
const [scale, setScale] = useState(100);
|
||||||
const [customScale, setCustomScale] = useState("");
|
const [customScale, setCustomScale] = useState("");
|
||||||
@@ -2547,27 +2557,22 @@ function ScaleCalculator() {
|
|||||||
customScale !== "" ? parseFloat(customScale) || scale : scale;
|
customScale !== "" ? parseFloat(customScale) || scale : scale;
|
||||||
const val = parseFloat(inputVal);
|
const val = parseFloat(inputVal);
|
||||||
|
|
||||||
// Real→Desen: val(m) × 1000 → mm, ÷ scale → mm pe desen
|
// drawing_mm = real_cm × 10 / scale
|
||||||
// Desen→Real: val(mm) × scale → mm real, ÷ 1000 → m
|
// real_cm = drawing_mm × scale / 10
|
||||||
const result = !isNaN(val)
|
const result = !isNaN(val)
|
||||||
? mode === "real-to-drawing"
|
? mode === "real-to-drawing"
|
||||||
? (val * 1000) / effectiveScale
|
? (val * 10) / effectiveScale // cm → mm on drawing
|
||||||
: (val * effectiveScale) / 1000
|
: (val * effectiveScale) / 10 // mm drawing → cm real
|
||||||
: NaN;
|
: NaN;
|
||||||
|
|
||||||
const unitIn = mode === "real-to-drawing" ? "m (real)" : "mm (desen)";
|
const fmt2 = (n: number) =>
|
||||||
|
|
||||||
const fmtDesen = (n: number) =>
|
|
||||||
isNaN(n)
|
isNaN(n)
|
||||||
? "—"
|
? "—"
|
||||||
: n.toLocaleString("ro-RO", { minimumFractionDigits: 2, maximumFractionDigits: 2 }) + " mm";
|
: n.toLocaleString("ro-RO", { minimumFractionDigits: 2, maximumFractionDigits: 2 });
|
||||||
const fmtReal = (n: number) =>
|
|
||||||
isNaN(n)
|
|
||||||
? "—"
|
|
||||||
: n.toLocaleString("ro-RO", { minimumFractionDigits: 3, maximumFractionDigits: 3 }) + " m";
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
|
{/* Scale picker */}
|
||||||
<div>
|
<div>
|
||||||
<Label className="text-xs text-muted-foreground">Scară</Label>
|
<Label className="text-xs text-muted-foreground">Scară</Label>
|
||||||
<div className="mt-1 flex flex-wrap items-center gap-2">
|
<div className="mt-1 flex flex-wrap items-center gap-2">
|
||||||
@@ -2595,6 +2600,8 @@ function ScaleCalculator() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Mode */}
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<Button
|
<Button
|
||||||
variant={mode === "real-to-drawing" ? "default" : "outline"}
|
variant={mode === "real-to-drawing" ? "default" : "outline"}
|
||||||
@@ -2611,49 +2618,71 @@ function ScaleCalculator() {
|
|||||||
Desen → Real
|
Desen → Real
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Input */}
|
||||||
<div>
|
<div>
|
||||||
<Label>
|
<Label>
|
||||||
{mode === "real-to-drawing"
|
{mode === "real-to-drawing"
|
||||||
? "Dimensiune reală (m)"
|
? "Dimensiune reală (cm)"
|
||||||
: "Dimensiune desen (mm)"}
|
: "Dimensiune desen (mm)"}
|
||||||
</Label>
|
</Label>
|
||||||
<div className="mt-1 flex gap-2">
|
<div className="mt-1 flex gap-2 items-center">
|
||||||
<Input
|
<Input
|
||||||
type="number"
|
type="number"
|
||||||
|
min={0}
|
||||||
|
step={0.5}
|
||||||
value={inputVal}
|
value={inputVal}
|
||||||
onChange={(e) => setInputVal(e.target.value)}
|
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"
|
className="flex-1"
|
||||||
/>
|
/>
|
||||||
<span className="flex items-center text-sm text-muted-foreground px-2">
|
<span className="text-sm text-muted-foreground shrink-0">
|
||||||
{unitIn}
|
{mode === "real-to-drawing" ? "cm" : "mm"}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Result */}
|
||||||
{!isNaN(val) && val > 0 && (
|
{!isNaN(val) && val > 0 && (
|
||||||
<div className="rounded-md border bg-muted/30 p-4 space-y-2 text-sm">
|
<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}
|
Scară 1:{effectiveScale}
|
||||||
</p>
|
</p>
|
||||||
<p className="text-base font-medium">
|
{mode === "real-to-drawing" ? (
|
||||||
{mode === "real-to-drawing" ? (
|
<>
|
||||||
<>
|
<p>
|
||||||
{val} m real →{" "}
|
Real: <strong>{fmt2(val)} cm</strong>{" "}
|
||||||
<strong className="text-primary">{fmtDesen(result)}</strong> pe desen
|
<span className="text-muted-foreground">
|
||||||
<CopyButton text={isNaN(result) ? "" : result.toFixed(2)} />
|
({fmt2(val / 100)} m)
|
||||||
</>
|
</span>
|
||||||
) : (
|
</p>
|
||||||
<>
|
<p className="text-base border-t pt-2">
|
||||||
{val} mm desen →{" "}
|
Pe desen:{" "}
|
||||||
<strong className="text-primary">{fmtReal(result)}</strong> real
|
<strong className="text-primary">{fmt2(result)} mm</strong>
|
||||||
<CopyButton text={isNaN(result) ? "" : result.toFixed(3)} />
|
<CopyButton text={fmt2(result)} />
|
||||||
</>
|
</p>
|
||||||
)}
|
{!isNaN(result) && (
|
||||||
</p>
|
<p className="text-xs text-muted-foreground">
|
||||||
{mode === "real-to-drawing" && !isNaN(result) && (
|
= {fmt2(result / 10)} cm pe desen
|
||||||
<p className="text-xs text-muted-foreground">
|
</p>
|
||||||
= {(result / 10).toFixed(2)} 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 · {fmt2(result * 10)} mm
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
Reference in New Issue
Block a user