diff --git a/src/modules/registratura/components/registry-entry-form.tsx b/src/modules/registratura/components/registry-entry-form.tsx index a60f580..2e8bb28 100644 --- a/src/modules/registratura/components/registry-entry-form.tsx +++ b/src/modules/registratura/components/registry-entry-form.tsx @@ -27,6 +27,7 @@ import { filterSubjects, getRecommendedTemplates, getSubjectPlaceholder, + createDynamicTemplate, } from "../services/subject-template-service"; import type { SubjectTemplate } from "../services/subject-template-service"; import { SubjectTemplateInput } from "./subject-template-input"; @@ -912,6 +913,7 @@ export function RegistryEntryForm({
  • Contract nr. 15/2026 — Proiect X
  • Selecteaza un sablon sau tasteaza liber.

    +

    Tip: scrie {"{proiect}"} pentru dropdown de proiecte.

    ) : (
    @@ -923,6 +925,7 @@ export function RegistryEntryForm({
  • Notificare incepere lucrari — Proiect X
  • Selecteaza un sablon sau tasteaza liber.

    +

    Tip: scrie {"{proiect}"} pentru dropdown de proiecte.

    )} @@ -949,8 +952,29 @@ export function RegistryEntryForm({ { - setSubjectQuery(e.target.value); - setSubject(e.target.value); + const val = e.target.value; + setSubjectQuery(val); + setSubject(val); + + // Auto-detect {proiect}, {nr}, {an}, {detalii}, {text} placeholders + // and switch to template mode when a closing } is typed + if (val.includes("{") && val.includes("}")) { + const dynTemplate = createDynamicTemplate(val); + if (dynTemplate) { + // Pre-fill default values (e.g. current year for {an}) + const prefill: Record = {}; + for (const field of dynTemplate.fields) { + if (field.defaultValue) { + prefill[field.id] = field.defaultValue; + } + } + setActiveTemplate(dynTemplate); + setTemplateFieldValues(prefill); + setSubject(assembleSubject(dynTemplate, prefill)); + setSubjectFocused(false); + return; + } + } }} onFocus={() => setSubjectFocused(true)} onBlur={() => setTimeout(() => setSubjectFocused(false), 200)} diff --git a/src/modules/registratura/services/subject-template-service.ts b/src/modules/registratura/services/subject-template-service.ts index 91d4dad..ae80be9 100644 --- a/src/modules/registratura/services/subject-template-service.ts +++ b/src/modules/registratura/services/subject-template-service.ts @@ -589,6 +589,73 @@ export function filterTemplates( ); } +/** Recognized field placeholder names that can be used in dynamic templates */ +const DYNAMIC_FIELD_NAMES = new Set(["nr", "an", "detalii", "proiect", "text"]); + +/** Regex to detect at least one valid {fieldName} placeholder */ +const HAS_PLACEHOLDER_RE = /\{(nr|an|detalii|proiect|text)\}/; + +/** + * Create a dynamic SubjectTemplate from free text containing `{fieldName}` + * placeholders. Recognised names: nr, an, detalii, proiect, text. + * + * Returns `null` when the text contains no valid placeholder. + * + * Example: + * createDynamicTemplate("Cerere CU — {proiect}") + * → SubjectTemplate with static "Cerere CU — " + field(proiect) + */ +export function createDynamicTemplate( + pattern: string, +): SubjectTemplate | null { + if (!HAS_PLACEHOLDER_RE.test(pattern)) return null; + + const tokens: TemplateToken[] = []; + const fields: TemplateField[] = []; + let fIdx = 0; + + // Split by {fieldName} placeholders (keeping the delimiters) + const parts = pattern.split(/(\{[^}]+\})/); + for (const part of parts) { + if (!part) continue; + const fieldMatch = part.match(/^\{(\w+)\}$/); + if (fieldMatch) { + const name = fieldMatch[1]!; + if (!DYNAMIC_FIELD_NAMES.has(name)) { + // Unknown placeholder — treat as static text + tokens.push({ type: "static", value: part }); + continue; + } + const ft = resolveFieldType(name); + const field: TemplateField = { + id: `f${fIdx}`, + name, + placeholder: fieldPlaceholder(ft), + width: fieldWidth(ft), + fieldType: ft, + defaultValue: ft === "an" ? CURRENT_YEAR : undefined, + }; + fields.push(field); + tokens.push({ type: "field", value: "", field }); + fIdx++; + } else { + tokens.push({ type: "static", value: part }); + } + } + + if (fields.length === 0) return null; + + return { + id: `dyn_${hashPattern(pattern)}`, + pattern, + tokens, + fields, + frequency: 0, + exampleSubject: pattern, + isSeed: true, + }; +} + /** * Filter existing subjects by query (plain text substring match). * Returns unique matches, limited to `limit`.