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`.