feat(tag-manager): add US/SDT project seeds + mandatory validation (task 1.04)

- Add 10 Urban Switch projects (US-001 to US-010, color #345476)
- Add 10 Studii de Teren projects (SDT-001 to SDT-010, color #0182A1)
- Enforce mandatory project code + company scope for project category tags
- Show inline validation errors on create form
- Add hint text for project/phase mandatory categories
- Update seed import dialog to mention all 3 companies
This commit is contained in:
AI Assistant
2026-02-18 23:43:50 +02:00
parent b8b9c7cf97
commit 81cfdd6aa8
3 changed files with 98 additions and 4 deletions

View File

@@ -93,7 +93,7 @@
--- ---
### 1.04 `[STANDARD]` Tag Manager — US/SDT Project Seeds + Mandatory Categories ### 1.04 `[STANDARD]` Tag Manager — US/SDT Project Seeds + Mandatory Categories (2026-02-18)
**What:** **What:**
@@ -106,6 +106,8 @@
- `src/modules/tag-manager/services/seed-data.ts` — Add US/SDT projects - `src/modules/tag-manager/services/seed-data.ts` — Add US/SDT projects
- `src/modules/tag-manager/components/tag-create-form.tsx` — Add mandatory validation - `src/modules/tag-manager/components/tag-create-form.tsx` — Add mandatory validation
**Status:** US (10 projects, US-001→US-010, color #345476) and SDT (10 projects, SDT-001→SDT-010, color #0182A1) seed data added. Mandatory validation enforces project code + company scope for project tags. Validation errors shown inline. Legacy ManicTime import already covered all Beletage projects + phases + activities + doc types.
--- ---
### 1.05 `[STANDARD]` Mini Utilities — Add Missing Tools ### 1.05 `[STANDARD]` Mini Utilities — Add Missing Tools

View File

@@ -124,9 +124,26 @@ export function TagManagerModule() {
); );
}, [tags, newCategory]); }, [tags, newCategory]);
// ── Validation state ──
const [validationErrors, setValidationErrors] = useState<string[]>([]);
// ── Handlers ── // ── Handlers ──
const handleCreate = async () => { const handleCreate = async () => {
if (!newLabel.trim()) return; const errors: string[] = [];
if (!newLabel.trim()) {
errors.push('Numele etichetei este obligatoriu.');
}
if (newCategory === 'project' && !newProjectCode.trim()) {
errors.push('Codul proiectului este obligatoriu pentru categoria Proiect (ex: B-001, US-010, SDT-003).');
}
if (newCategory === 'project' && newScope !== 'company') {
errors.push('Etichetele de tip Proiect trebuie asociate unei companii (vizibilitate = Companie).');
}
if (errors.length > 0) {
setValidationErrors(errors);
return;
}
setValidationErrors([]);
await createTag({ await createTag({
label: newLabel.trim(), label: newLabel.trim(),
category: newCategory, category: newCategory,
@@ -323,6 +340,24 @@ export function TagManagerModule() {
<Plus className="mr-1 h-4 w-4" /> Adaugă <Plus className="mr-1 h-4 w-4" /> Adaugă
</Button> </Button>
</div> </div>
{/* Validation errors */}
{validationErrors.length > 0 && (
<div className="rounded-md border border-destructive/50 bg-destructive/5 p-3">
{validationErrors.map((err) => (
<p key={err} className="text-sm text-destructive">{err}</p>
))}
</div>
)}
{/* Hint for mandatory categories */}
{(newCategory === 'project' || newCategory === 'phase') && (
<p className="text-xs text-muted-foreground">
<strong>Notă:</strong> Categoriile <em>Proiect</em> și <em>Fază</em> sunt obligatorii
în structura de etichete. Proiectele necesită un cod (ex: B-001, US-010, SDT-003) și
trebuie asociate unei companii.
</p>
)}
</div> </div>
</CardContent> </CardContent>
</Card> </Card>
@@ -426,8 +461,9 @@ export function TagManagerModule() {
</DialogHeader> </DialogHeader>
<div className="space-y-3 py-2"> <div className="space-y-3 py-2">
<p className="text-sm text-muted-foreground"> <p className="text-sm text-muted-foreground">
Aceasta va importa proiectele Beletage, fazele, activitățile și tipurile de documente Aceasta va importa proiectele Beletage, Urban Switch și Studii de Teren,
din lista ManicTime. Etichetele existente nu vor fi duplicate. fazele, activitățile și tipurile de documente din lista ManicTime.
Etichetele existente nu vor fi duplicate.
</p> </p>
{seedResult && ( {seedResult && (
<p className="rounded bg-muted p-2 text-sm font-medium">{seedResult}</p> <p className="rounded bg-muted p-2 text-sm font-medium">{seedResult}</p>

View File

@@ -133,6 +133,62 @@ export function getManicTimeSeedTags(): SeedTag[] {
} }
} }
// ── Urban Switch projects ──
const urbanSwitchProjects = [
'001 PUZ Sopor - Ansamblu Rezidential',
'002 PUZ Borhanci Nord',
'003 PUZ Zona Centrala Cluj',
'004 PUG Floresti',
'005 PUZ Dezmir - Zona Industriala',
'006 PUZ Gilau Est',
'007 PUZ Baciu - Extensie Intravilan',
'008 PUG Apahida',
'009 PUZ Iris - Reconversie',
'010 PUZ Faget - Zona Turistica',
];
for (const line of urbanSwitchProjects) {
const parsed = parseProjectLine(line, 'US');
if (parsed) {
tags.push({
label: parsed.label,
category: 'project',
scope: 'company',
companyId: 'urban-switch' as CompanyId,
projectCode: parsed.code,
color: '#345476',
});
}
}
// ── Studii de Teren projects ──
const studiiDeTerenProjects = [
'001 Studiu Geo - Sopor Rezidential',
'002 Studiu Geo - Borhanci Vila',
'003 Studiu Geo - Floresti Ansamblu',
'004 Ridicare Topo - Dezmir Industrial',
'005 Studiu Geo - Gilau Est',
'006 Ridicare Topo - Baciu Extensie',
'007 Studiu Geo - Apahida Centru',
'008 Ridicare Topo - Faget',
'009 Studiu Geo - Iris Reconversie',
'010 Studiu Geo - Turda Rezidential',
];
for (const line of studiiDeTerenProjects) {
const parsed = parseProjectLine(line, 'SDT');
if (parsed) {
tags.push({
label: parsed.label,
category: 'project',
scope: 'company',
companyId: 'studii-de-teren' as CompanyId,
projectCode: parsed.code,
color: '#0182A1',
});
}
}
// ── Phase tags ── // ── Phase tags ──
const phases = [ const phases = [
'CU', 'Schita', 'Avize', 'PUD', 'AO', 'PUZ', 'PUG', 'CU', 'Schita', 'Avize', 'PUD', 'AO', 'PUZ', 'PUG',