From eaca24aa58f1b8973639b144a1835d39aa8d7f8a Mon Sep 17 00:00:00 2001 From: AI Assistant Date: Thu, 19 Feb 2026 07:12:21 +0200 Subject: [PATCH] feat(word-xml): remove POT/CUT auto-calculation toggle --- .../word-xml/components/word-xml-module.tsx | 31 +++--- .../word-xml/components/xml-preview.tsx | 81 +++++++++----- .../word-xml/components/xml-settings.tsx | 38 +++---- src/modules/word-xml/hooks/use-xml-config.ts | 104 ++++++++++-------- .../word-xml/services/xml-generator.ts | 72 ++++-------- src/modules/word-xml/types.ts | 3 +- 6 files changed, 171 insertions(+), 158 deletions(-) diff --git a/src/modules/word-xml/components/word-xml-module.tsx b/src/modules/word-xml/components/word-xml-module.tsx index ad1eac1..479f13f 100644 --- a/src/modules/word-xml/components/word-xml-module.tsx +++ b/src/modules/word-xml/components/word-xml-module.tsx @@ -1,18 +1,25 @@ -'use client'; +"use client"; -import { useXmlConfig } from '../hooks/use-xml-config'; -import { XmlSettings } from './xml-settings'; -import { CategoryManager } from './category-manager'; -import { XmlPreview } from './xml-preview'; -import { Separator } from '@/shared/components/ui/separator'; -import { Button } from '@/shared/components/ui/button'; -import { RotateCcw } from 'lucide-react'; +import { useXmlConfig } from "../hooks/use-xml-config"; +import { XmlSettings } from "./xml-settings"; +import { CategoryManager } from "./category-manager"; +import { XmlPreview } from "./xml-preview"; +import { Separator } from "@/shared/components/ui/separator"; +import { Button } from "@/shared/components/ui/button"; +import { RotateCcw } from "lucide-react"; export function WordXmlModule() { const { - config, setMode, setBaseNamespace, setComputeMetrics, - setCurrentCategory, updateCategoryFields, addCategory, - removeCategory, resetCategoryToPreset, clearCategoryFields, resetAll, + config, + setMode, + setBaseNamespace, + setCurrentCategory, + updateCategoryFields, + addCategory, + removeCategory, + resetCategoryToPreset, + clearCategoryFields, + resetAll, } = useXmlConfig(); return ( @@ -21,10 +28,8 @@ export function WordXmlModule() { diff --git a/src/modules/word-xml/components/xml-preview.tsx b/src/modules/word-xml/components/xml-preview.tsx index a46ac5b..0c34908 100644 --- a/src/modules/word-xml/components/xml-preview.tsx +++ b/src/modules/word-xml/components/xml-preview.tsx @@ -1,28 +1,37 @@ -'use client'; +"use client"; -import { useMemo, useState } from 'react'; -import { Copy, Download, FileArchive } from 'lucide-react'; -import { Button } from '@/shared/components/ui/button'; -import type { XmlGeneratorConfig } from '../types'; -import { generateAllCategories, downloadXmlFile, downloadZipAll } from '../services/xml-generator'; +import { useMemo, useState } from "react"; +import { Copy, Download, FileArchive } from "lucide-react"; +import { Button } from "@/shared/components/ui/button"; +import type { XmlGeneratorConfig } from "../types"; +import { + generateAllCategories, + downloadXmlFile, + downloadZipAll, +} from "../services/xml-generator"; interface XmlPreviewProps { config: XmlGeneratorConfig; } export function XmlPreview({ config }: XmlPreviewProps) { - const [copied, setCopied] = useState<'xml' | 'xpath' | null>(null); + const [copied, setCopied] = useState<"xml" | "xpath" | null>(null); const allOutputs = useMemo( - () => generateAllCategories(config.categories, config.baseNamespace, config.mode, config.computeMetrics), - [config.categories, config.baseNamespace, config.mode, config.computeMetrics], + () => + generateAllCategories( + config.categories, + config.baseNamespace, + config.mode, + ), + [config.categories, config.baseNamespace, config.mode], ); const current = allOutputs[config.currentCategory]; - const xml = current?.xml || ''; - const xpaths = current?.xpaths || ''; + const xml = current?.xml || ""; + const xpaths = current?.xpaths || ""; - const handleCopy = async (text: string, type: 'xml' | 'xpath') => { + const handleCopy = async (text: string, type: "xml" | "xpath") => { try { await navigator.clipboard.writeText(text); setCopied(type); @@ -32,9 +41,9 @@ export function XmlPreview({ config }: XmlPreviewProps) { } }; - const safeCatName = (config.currentCategory || 'unknown') - .replace(/\s+/g, '_') - .replace(/[^A-Za-z0-9_.-]/g, ''); + const safeCatName = (config.currentCategory || "unknown") + .replace(/\s+/g, "_") + .replace(/[^A-Za-z0-9_.-]/g, ""); const handleDownloadCurrent = () => { if (!xml) return; @@ -42,7 +51,7 @@ export function XmlPreview({ config }: XmlPreviewProps) { }; const handleDownloadZip = async () => { - await downloadZipAll(config.categories, config.baseNamespace, config.mode, config.computeMetrics); + await downloadZipAll(config.categories, config.baseNamespace, config.mode); }; return ( @@ -50,7 +59,12 @@ export function XmlPreview({ config }: XmlPreviewProps) {

Preview & Export

-
-            {xml || ''}
+            {xml ||
+              ""}
           
{/* XPath preview */}
- XPaths — {config.currentCategory} -
-            {xpaths || ''}
+            {xpaths || ""}
           
diff --git a/src/modules/word-xml/components/xml-settings.tsx b/src/modules/word-xml/components/xml-settings.tsx index a738cf7..faf41e7 100644 --- a/src/modules/word-xml/components/xml-settings.tsx +++ b/src/modules/word-xml/components/xml-settings.tsx @@ -1,23 +1,22 @@ -'use client'; +"use client"; -import type { XmlGeneratorMode } from '../types'; -import { Input } from '@/shared/components/ui/input'; -import { Label } from '@/shared/components/ui/label'; -import { Switch } from '@/shared/components/ui/switch'; -import { cn } from '@/shared/lib/utils'; +import type { XmlGeneratorMode } from "../types"; +import { Input } from "@/shared/components/ui/input"; +import { Label } from "@/shared/components/ui/label"; +import { cn } from "@/shared/lib/utils"; interface XmlSettingsProps { baseNamespace: string; mode: XmlGeneratorMode; - computeMetrics: boolean; onSetBaseNamespace: (ns: string) => void; onSetMode: (mode: XmlGeneratorMode) => void; - onSetComputeMetrics: (v: boolean) => void; } export function XmlSettings({ - baseNamespace, mode, computeMetrics, - onSetBaseNamespace, onSetMode, onSetComputeMetrics, + baseNamespace, + mode, + onSetBaseNamespace, + onSetMode, }: XmlSettingsProps) { return (
@@ -38,31 +37,28 @@ export function XmlSettings({
- {(['simple', 'advanced'] as XmlGeneratorMode[]).map((m) => ( + {(["simple", "advanced"] as XmlGeneratorMode[]).map((m) => ( ))}

- {mode === 'simple' ? 'Doar câmpurile definite.' : '+ Short / Upper / Lower / Initials / First.'} + {mode === "simple" + ? "Doar câmpurile definite." + : "+ Short / Upper / Lower / Initials / First."}

- -
- - -
); diff --git a/src/modules/word-xml/hooks/use-xml-config.ts b/src/modules/word-xml/hooks/use-xml-config.ts index dfede80..2487d84 100644 --- a/src/modules/word-xml/hooks/use-xml-config.ts +++ b/src/modules/word-xml/hooks/use-xml-config.ts @@ -1,20 +1,19 @@ -'use client'; +"use client"; -import { useState, useCallback, useMemo } from 'react'; -import type { XmlGeneratorConfig, XmlGeneratorMode } from '../types'; -import { DEFAULT_PRESETS } from '../services/category-presets'; +import { useState, useCallback, useMemo } from "react"; +import type { XmlGeneratorConfig, XmlGeneratorMode } from "../types"; +import { DEFAULT_PRESETS } from "../services/category-presets"; function createDefaultConfig(): XmlGeneratorConfig { const categories: Record = {}; for (const [name, fields] of Object.entries(DEFAULT_PRESETS)) { - categories[name] = { name, fieldsText: fields.join('\n') }; + categories[name] = { name, fieldsText: fields.join("\n") }; } return { - baseNamespace: 'http://schemas.beletage.ro/contract', - mode: 'advanced', - computeMetrics: true, + baseNamespace: "http://schemas.beletage.ro/contract", + mode: "advanced", categories, - currentCategory: 'Beneficiar', + currentCategory: "Beneficiar", }; } @@ -29,34 +28,33 @@ export function useXmlConfig() { setConfig((prev) => ({ ...prev, baseNamespace })); }, []); - const setComputeMetrics = useCallback((computeMetrics: boolean) => { - setConfig((prev) => ({ ...prev, computeMetrics })); - }, []); - const setCurrentCategory = useCallback((name: string) => { setConfig((prev) => ({ ...prev, currentCategory: name })); }, []); - const updateCategoryFields = useCallback((categoryName: string, fieldsText: string) => { - setConfig((prev) => { - const existing = prev.categories[categoryName]; - if (!existing) return prev; - return { - ...prev, - categories: { - ...prev.categories, - [categoryName]: { name: existing.name, fieldsText }, - }, - }; - }); - }, []); + const updateCategoryFields = useCallback( + (categoryName: string, fieldsText: string) => { + setConfig((prev) => { + const existing = prev.categories[categoryName]; + if (!existing) return prev; + return { + ...prev, + categories: { + ...prev.categories, + [categoryName]: { name: existing.name, fieldsText }, + }, + }; + }); + }, + [], + ); const addCategory = useCallback((name: string) => { setConfig((prev) => { if (prev.categories[name]) return prev; return { ...prev, - categories: { ...prev.categories, [name]: { name, fieldsText: '' } }, + categories: { ...prev.categories, [name]: { name, fieldsText: "" } }, currentCategory: name, }; }); @@ -70,7 +68,9 @@ export function useXmlConfig() { return { ...prev, categories: next, - currentCategory: keys.includes(prev.currentCategory) ? prev.currentCategory : keys[0] || '', + currentCategory: keys.includes(prev.currentCategory) + ? prev.currentCategory + : keys[0] || "", }; }); }, []); @@ -82,7 +82,7 @@ export function useXmlConfig() { ...prev, categories: { ...prev.categories, - [name]: { name, fieldsText: preset.join('\n') }, + [name]: { name, fieldsText: preset.join("\n") }, }, })); }, []); @@ -95,7 +95,7 @@ export function useXmlConfig() { ...prev, categories: { ...prev.categories, - [name]: { name: existing.name, fieldsText: '' }, + [name]: { name: existing.name, fieldsText: "" }, }, }; }); @@ -109,20 +109,32 @@ export function useXmlConfig() { setConfig(loaded); }, []); - return useMemo(() => ({ - config, - setMode, - setBaseNamespace, - setComputeMetrics, - setCurrentCategory, - updateCategoryFields, - addCategory, - removeCategory, - resetCategoryToPreset, - clearCategoryFields, - resetAll, - loadConfig, - }), [config, setMode, setBaseNamespace, setComputeMetrics, setCurrentCategory, - updateCategoryFields, addCategory, removeCategory, resetCategoryToPreset, - clearCategoryFields, resetAll, loadConfig]); + return useMemo( + () => ({ + config, + setMode, + setBaseNamespace, + setCurrentCategory, + updateCategoryFields, + addCategory, + removeCategory, + resetCategoryToPreset, + clearCategoryFields, + resetAll, + loadConfig, + }), + [ + config, + setMode, + setBaseNamespace, + setCurrentCategory, + updateCategoryFields, + addCategory, + removeCategory, + resetCategoryToPreset, + clearCategoryFields, + resetAll, + loadConfig, + ], + ); } diff --git a/src/modules/word-xml/services/xml-generator.ts b/src/modules/word-xml/services/xml-generator.ts index 260d2c6..00d8014 100644 --- a/src/modules/word-xml/services/xml-generator.ts +++ b/src/modules/word-xml/services/xml-generator.ts @@ -1,21 +1,21 @@ -import type { XmlGeneratorMode, XmlCategory, GeneratedOutput } from '../types'; +import type { XmlGeneratorMode, XmlCategory, GeneratedOutput } from "../types"; function sanitizeName(name: string): string | null { const trimmed = name.trim(); if (!trimmed) return null; - let n = trimmed.replace(/\s+/g, '_').replace(/[^A-Za-z0-9_.-]/g, ''); - if (!/^[A-Za-z_]/.test(n)) n = '_' + n; + let n = trimmed.replace(/\s+/g, "_").replace(/[^A-Za-z0-9_.-]/g, ""); + if (!/^[A-Za-z_]/.test(n)) n = "_" + n; return n || null; } function getCategoryNamespace(baseNs: string, category: string): string { const safeCat = sanitizeName(category) || category; - return baseNs.replace(/\/+$/, '') + '/' + safeCat; + return baseNs.replace(/\/+$/, "") + "/" + safeCat; } function getCategoryRoot(category: string): string { const safeCat = sanitizeName(category) || category; - return safeCat + 'Data'; + return safeCat + "Data"; } interface FieldEntry { @@ -29,14 +29,13 @@ export function generateCategoryXml( catData: XmlCategory, baseNamespace: string, mode: XmlGeneratorMode, - computeMetrics: boolean, ): GeneratedOutput { const raw = catData.fieldsText .split(/\r?\n/) .map((l) => l.trim()) .filter((l) => l.length > 0); - if (raw.length === 0) return { xml: '', xpaths: '' }; + if (raw.length === 0) return { xml: "", xpaths: "" }; const ns = getCategoryNamespace(baseNamespace, category); const root = getCategoryRoot(category); @@ -51,19 +50,19 @@ export function generateCategoryXml( let baseName = base; let idx = 2; while (usedNames.has(baseName)) { - baseName = base + '_' + idx; + baseName = base + "_" + idx; idx++; } usedNames.add(baseName); const variants = [baseName]; - if (mode === 'advanced') { - const suffixes = ['Short', 'Upper', 'Lower', 'Initials', 'First']; + if (mode === "advanced") { + const suffixes = ["Short", "Upper", "Lower", "Initials", "First"]; for (const suffix of suffixes) { let vn = baseName + suffix; let k = 2; while (usedNames.has(vn)) { - vn = baseName + suffix + '_' + k; + vn = baseName + suffix + "_" + k; k++; } usedNames.add(vn); @@ -74,24 +73,7 @@ export function generateCategoryXml( fields.push({ label, baseName, variants }); } - // Auto-add POT/CUT for Suprafete category - const extraMetricFields: FieldEntry[] = []; - if (computeMetrics && category.toLowerCase().includes('suprafete')) { - const hasTeren = fields.some((f) => f.baseName.toLowerCase().includes('suprafatateren')); - const hasLaSol = fields.some((f) => f.baseName.toLowerCase().includes('suprafataconstruitalasol')); - const hasDesf = fields.some((f) => f.baseName.toLowerCase().includes('suprafatadesfasurata')); - - if (hasTeren && hasLaSol && !usedNames.has('POT')) { - usedNames.add('POT'); - extraMetricFields.push({ label: 'Procent Ocupare Teren', baseName: 'POT', variants: ['POT'] }); - } - if (hasTeren && hasDesf && !usedNames.has('CUT')) { - usedNames.add('CUT'); - extraMetricFields.push({ label: 'Coeficient Utilizare Teren', baseName: 'CUT', variants: ['CUT'] }); - } - } - - const allFields = fields.concat(extraMetricFields); + const allFields = fields; // Build XML let xml = '\n'; @@ -110,18 +92,8 @@ export function generateCategoryXml( for (const v of f.variants) { xp += `/${root}/${v}\n`; } - xp += '\n'; + xp += "\n"; } - if (extraMetricFields.length > 0) { - xp += '# Metrici auto (POT / CUT)\n'; - for (const f of extraMetricFields) { - for (const v of f.variants) { - xp += `/${root}/${v}\n`; - } - } - xp += '\n'; - } - return { xml, xpaths: xp }; } @@ -129,20 +101,19 @@ export function generateAllCategories( categories: Record, baseNamespace: string, mode: XmlGeneratorMode, - computeMetrics: boolean, ): Record { const results: Record = {}; for (const cat of Object.keys(categories)) { const catData = categories[cat]; if (!catData) continue; - results[cat] = generateCategoryXml(cat, catData, baseNamespace, mode, computeMetrics); + results[cat] = generateCategoryXml(cat, catData, baseNamespace, mode); } return results; } export function downloadXmlFile(xml: string, filename: string): void { - const blob = new Blob([xml], { type: 'application/xml' }); - const a = document.createElement('a'); + const blob = new Blob([xml], { type: "application/xml" }); + const a = document.createElement("a"); a.href = URL.createObjectURL(blob); a.download = filename; document.body.appendChild(a); @@ -155,13 +126,12 @@ export async function downloadZipAll( categories: Record, baseNamespace: string, mode: XmlGeneratorMode, - computeMetrics: boolean, ): Promise { - const JSZip = (await import('jszip')).default; - const results = generateAllCategories(categories, baseNamespace, mode, computeMetrics); + const JSZip = (await import("jszip")).default; + const results = generateAllCategories(categories, baseNamespace, mode); const zip = new JSZip(); - const folder = zip.folder('customXmlParts')!; + const folder = zip.folder("customXmlParts")!; let hasAny = false; for (const cat of Object.keys(results)) { @@ -175,10 +145,10 @@ export async function downloadZipAll( if (!hasAny) return; - const content = await zip.generateAsync({ type: 'blob' }); - const a = document.createElement('a'); + const content = await zip.generateAsync({ type: "blob" }); + const a = document.createElement("a"); a.href = URL.createObjectURL(content); - a.download = 'beletage_custom_xml_parts.zip'; + a.download = "beletage_custom_xml_parts.zip"; document.body.appendChild(a); a.click(); document.body.removeChild(a); diff --git a/src/modules/word-xml/types.ts b/src/modules/word-xml/types.ts index 1a6f6ed..dff3be7 100644 --- a/src/modules/word-xml/types.ts +++ b/src/modules/word-xml/types.ts @@ -1,4 +1,4 @@ -export type XmlGeneratorMode = 'simple' | 'advanced'; +export type XmlGeneratorMode = "simple" | "advanced"; export interface XmlCategory { name: string; @@ -8,7 +8,6 @@ export interface XmlCategory { export interface XmlGeneratorConfig { baseNamespace: string; mode: XmlGeneratorMode; - computeMetrics: boolean; categories: Record; currentCategory: string; }