pre-launch hardening: Address Book type sort, Hot Desk proportions, TVA calculator, ROADMAP Phase 4B

- Address Book: type dropdown always sorted alphabetically (ro locale), including custom types
- Hot Desk: window ~half height (top-[35%] bottom-[35%]), door ~double height (h-16)
- Mini Utilities: TVA calculator (19%) with add/extract modes, RON formatting, copy buttons
- ROADMAP: new Phase 4B Pre-Launch Hardening with 10 structured tasks
- CLAUDE.md: bumped versions (Address Book 0.1.1, Mini Utilities 0.1.1, Hot Desk 0.1.1), Visual Copilot separate repo note
This commit is contained in:
AI Assistant
2026-03-08 14:08:48 +02:00
parent a6fa94deec
commit 4d2f924537
6 changed files with 269 additions and 47 deletions
@@ -80,15 +80,22 @@ export function AddressBookModule() {
null,
);
// Collect all contact types (defaults + custom ones from existing contacts)
// Collect all contact types (defaults + custom ones from existing contacts), sorted alphabetically by label
const allTypes = useMemo(() => {
const types = { ...DEFAULT_TYPE_LABELS };
const types: Record<string, string> = { ...DEFAULT_TYPE_LABELS };
for (const c of allContacts) {
if (c.type && !types[c.type]) {
types[c.type] = c.type; // custom type — label is the type itself
}
}
return types;
// Sort entries alphabetically by label
const sorted: Record<string, string> = {};
for (const [k, v] of Object.entries(types).sort((a, b) =>
a[1].localeCompare(b[1], "ro"),
)) {
sorted[k] = v;
}
return sorted;
}, [allContacts]);
const handleSubmit = async (
@@ -652,15 +659,16 @@ function CreatableTypeSelect({
<SelectValue />
</SelectTrigger>
<SelectContent>
{Object.entries(DEFAULT_TYPE_LABELS).map(([k, label]) => (
<SelectItem key={k} value={k}>
{label}
</SelectItem>
))}
{/* Show current custom value if not in defaults */}
{value && !DEFAULT_TYPE_LABELS[value] && (
<SelectItem value={value}>{value}</SelectItem>
)}
{Object.entries(DEFAULT_TYPE_LABELS)
.concat(
value && !DEFAULT_TYPE_LABELS[value] ? [[value, value]] : [],
)
.sort((a, b) => a[1].localeCompare(b[1], "ro"))
.map(([k, label]) => (
<SelectItem key={k} value={k}>
{label}
</SelectItem>
))}
</SelectContent>
</Select>
<Button