feat(word-xml): remove POT/CUT auto-calculation toggle
This commit is contained in:
@@ -1,18 +1,25 @@
|
|||||||
'use client';
|
"use client";
|
||||||
|
|
||||||
import { useXmlConfig } from '../hooks/use-xml-config';
|
import { useXmlConfig } from "../hooks/use-xml-config";
|
||||||
import { XmlSettings } from './xml-settings';
|
import { XmlSettings } from "./xml-settings";
|
||||||
import { CategoryManager } from './category-manager';
|
import { CategoryManager } from "./category-manager";
|
||||||
import { XmlPreview } from './xml-preview';
|
import { XmlPreview } from "./xml-preview";
|
||||||
import { Separator } from '@/shared/components/ui/separator';
|
import { Separator } from "@/shared/components/ui/separator";
|
||||||
import { Button } from '@/shared/components/ui/button';
|
import { Button } from "@/shared/components/ui/button";
|
||||||
import { RotateCcw } from 'lucide-react';
|
import { RotateCcw } from "lucide-react";
|
||||||
|
|
||||||
export function WordXmlModule() {
|
export function WordXmlModule() {
|
||||||
const {
|
const {
|
||||||
config, setMode, setBaseNamespace, setComputeMetrics,
|
config,
|
||||||
setCurrentCategory, updateCategoryFields, addCategory,
|
setMode,
|
||||||
removeCategory, resetCategoryToPreset, clearCategoryFields, resetAll,
|
setBaseNamespace,
|
||||||
|
setCurrentCategory,
|
||||||
|
updateCategoryFields,
|
||||||
|
addCategory,
|
||||||
|
removeCategory,
|
||||||
|
resetCategoryToPreset,
|
||||||
|
clearCategoryFields,
|
||||||
|
resetAll,
|
||||||
} = useXmlConfig();
|
} = useXmlConfig();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -21,10 +28,8 @@ export function WordXmlModule() {
|
|||||||
<XmlSettings
|
<XmlSettings
|
||||||
baseNamespace={config.baseNamespace}
|
baseNamespace={config.baseNamespace}
|
||||||
mode={config.mode}
|
mode={config.mode}
|
||||||
computeMetrics={config.computeMetrics}
|
|
||||||
onSetBaseNamespace={setBaseNamespace}
|
onSetBaseNamespace={setBaseNamespace}
|
||||||
onSetMode={setMode}
|
onSetMode={setMode}
|
||||||
onSetComputeMetrics={setComputeMetrics}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Separator />
|
<Separator />
|
||||||
|
|||||||
@@ -1,28 +1,37 @@
|
|||||||
'use client';
|
"use client";
|
||||||
|
|
||||||
import { useMemo, useState } from 'react';
|
import { useMemo, useState } from "react";
|
||||||
import { Copy, Download, FileArchive } from 'lucide-react';
|
import { Copy, Download, FileArchive } from "lucide-react";
|
||||||
import { Button } from '@/shared/components/ui/button';
|
import { Button } from "@/shared/components/ui/button";
|
||||||
import type { XmlGeneratorConfig } from '../types';
|
import type { XmlGeneratorConfig } from "../types";
|
||||||
import { generateAllCategories, downloadXmlFile, downloadZipAll } from '../services/xml-generator';
|
import {
|
||||||
|
generateAllCategories,
|
||||||
|
downloadXmlFile,
|
||||||
|
downloadZipAll,
|
||||||
|
} from "../services/xml-generator";
|
||||||
|
|
||||||
interface XmlPreviewProps {
|
interface XmlPreviewProps {
|
||||||
config: XmlGeneratorConfig;
|
config: XmlGeneratorConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function XmlPreview({ config }: XmlPreviewProps) {
|
export function XmlPreview({ config }: XmlPreviewProps) {
|
||||||
const [copied, setCopied] = useState<'xml' | 'xpath' | null>(null);
|
const [copied, setCopied] = useState<"xml" | "xpath" | null>(null);
|
||||||
|
|
||||||
const allOutputs = useMemo(
|
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 current = allOutputs[config.currentCategory];
|
||||||
const xml = current?.xml || '';
|
const xml = current?.xml || "";
|
||||||
const xpaths = current?.xpaths || '';
|
const xpaths = current?.xpaths || "";
|
||||||
|
|
||||||
const handleCopy = async (text: string, type: 'xml' | 'xpath') => {
|
const handleCopy = async (text: string, type: "xml" | "xpath") => {
|
||||||
try {
|
try {
|
||||||
await navigator.clipboard.writeText(text);
|
await navigator.clipboard.writeText(text);
|
||||||
setCopied(type);
|
setCopied(type);
|
||||||
@@ -32,9 +41,9 @@ export function XmlPreview({ config }: XmlPreviewProps) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const safeCatName = (config.currentCategory || 'unknown')
|
const safeCatName = (config.currentCategory || "unknown")
|
||||||
.replace(/\s+/g, '_')
|
.replace(/\s+/g, "_")
|
||||||
.replace(/[^A-Za-z0-9_.-]/g, '');
|
.replace(/[^A-Za-z0-9_.-]/g, "");
|
||||||
|
|
||||||
const handleDownloadCurrent = () => {
|
const handleDownloadCurrent = () => {
|
||||||
if (!xml) return;
|
if (!xml) return;
|
||||||
@@ -42,7 +51,7 @@ export function XmlPreview({ config }: XmlPreviewProps) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleDownloadZip = async () => {
|
const handleDownloadZip = async () => {
|
||||||
await downloadZipAll(config.categories, config.baseNamespace, config.mode, config.computeMetrics);
|
await downloadZipAll(config.categories, config.baseNamespace, config.mode);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -50,7 +59,12 @@ export function XmlPreview({ config }: XmlPreviewProps) {
|
|||||||
<div className="flex flex-wrap items-center gap-2">
|
<div className="flex flex-wrap items-center gap-2">
|
||||||
<h2 className="text-lg font-semibold">Preview & Export</h2>
|
<h2 className="text-lg font-semibold">Preview & Export</h2>
|
||||||
<div className="ml-auto flex gap-2">
|
<div className="ml-auto flex gap-2">
|
||||||
<Button variant="outline" size="sm" onClick={handleDownloadCurrent} disabled={!xml}>
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
onClick={handleDownloadCurrent}
|
||||||
|
disabled={!xml}
|
||||||
|
>
|
||||||
<Download className="mr-1 h-4 w-4" /> XML curent
|
<Download className="mr-1 h-4 w-4" /> XML curent
|
||||||
</Button>
|
</Button>
|
||||||
<Button size="sm" onClick={handleDownloadZip}>
|
<Button size="sm" onClick={handleDownloadZip}>
|
||||||
@@ -63,28 +77,45 @@ export function XmlPreview({ config }: XmlPreviewProps) {
|
|||||||
{/* XML preview */}
|
{/* XML preview */}
|
||||||
<div>
|
<div>
|
||||||
<div className="mb-1.5 flex items-center justify-between">
|
<div className="mb-1.5 flex items-center justify-between">
|
||||||
<span className="text-xs font-medium text-muted-foreground">XML — {config.currentCategory}</span>
|
<span className="text-xs font-medium text-muted-foreground">
|
||||||
<Button variant="ghost" size="sm" className="h-6 text-xs" onClick={() => handleCopy(xml, 'xml')} disabled={!xml}>
|
XML — {config.currentCategory}
|
||||||
|
</span>
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="sm"
|
||||||
|
className="h-6 text-xs"
|
||||||
|
onClick={() => handleCopy(xml, "xml")}
|
||||||
|
disabled={!xml}
|
||||||
|
>
|
||||||
<Copy className="mr-1 h-3 w-3" />
|
<Copy className="mr-1 h-3 w-3" />
|
||||||
{copied === 'xml' ? 'Copiat!' : 'Copiază'}
|
{copied === "xml" ? "Copiat!" : "Copiază"}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<pre className="max-h-80 overflow-auto rounded-lg border bg-muted/30 p-3 text-xs">
|
<pre className="max-h-80 overflow-auto rounded-lg border bg-muted/30 p-3 text-xs">
|
||||||
{xml || '<!-- Niciun XML generat. Adaugă câmpuri în categoria curentă. -->'}
|
{xml ||
|
||||||
|
"<!-- Niciun XML generat. Adaugă câmpuri în categoria curentă. -->"}
|
||||||
</pre>
|
</pre>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* XPath preview */}
|
{/* XPath preview */}
|
||||||
<div>
|
<div>
|
||||||
<div className="mb-1.5 flex items-center justify-between">
|
<div className="mb-1.5 flex items-center justify-between">
|
||||||
<span className="text-xs font-medium text-muted-foreground">XPaths — {config.currentCategory}</span>
|
<span className="text-xs font-medium text-muted-foreground">
|
||||||
<Button variant="ghost" size="sm" className="h-6 text-xs" onClick={() => handleCopy(xpaths, 'xpath')} disabled={!xpaths}>
|
XPaths — {config.currentCategory}
|
||||||
|
</span>
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="sm"
|
||||||
|
className="h-6 text-xs"
|
||||||
|
onClick={() => handleCopy(xpaths, "xpath")}
|
||||||
|
disabled={!xpaths}
|
||||||
|
>
|
||||||
<Copy className="mr-1 h-3 w-3" />
|
<Copy className="mr-1 h-3 w-3" />
|
||||||
{copied === 'xpath' ? 'Copiat!' : 'Copiază'}
|
{copied === "xpath" ? "Copiat!" : "Copiază"}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<pre className="max-h-80 overflow-auto rounded-lg border bg-muted/30 p-3 text-xs">
|
<pre className="max-h-80 overflow-auto rounded-lg border bg-muted/30 p-3 text-xs">
|
||||||
{xpaths || ''}
|
{xpaths || ""}
|
||||||
</pre>
|
</pre>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,23 +1,22 @@
|
|||||||
'use client';
|
"use client";
|
||||||
|
|
||||||
import type { XmlGeneratorMode } from '../types';
|
import type { XmlGeneratorMode } from "../types";
|
||||||
import { Input } from '@/shared/components/ui/input';
|
import { Input } from "@/shared/components/ui/input";
|
||||||
import { Label } from '@/shared/components/ui/label';
|
import { Label } from "@/shared/components/ui/label";
|
||||||
import { Switch } from '@/shared/components/ui/switch';
|
import { cn } from "@/shared/lib/utils";
|
||||||
import { cn } from '@/shared/lib/utils';
|
|
||||||
|
|
||||||
interface XmlSettingsProps {
|
interface XmlSettingsProps {
|
||||||
baseNamespace: string;
|
baseNamespace: string;
|
||||||
mode: XmlGeneratorMode;
|
mode: XmlGeneratorMode;
|
||||||
computeMetrics: boolean;
|
|
||||||
onSetBaseNamespace: (ns: string) => void;
|
onSetBaseNamespace: (ns: string) => void;
|
||||||
onSetMode: (mode: XmlGeneratorMode) => void;
|
onSetMode: (mode: XmlGeneratorMode) => void;
|
||||||
onSetComputeMetrics: (v: boolean) => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function XmlSettings({
|
export function XmlSettings({
|
||||||
baseNamespace, mode, computeMetrics,
|
baseNamespace,
|
||||||
onSetBaseNamespace, onSetMode, onSetComputeMetrics,
|
mode,
|
||||||
|
onSetBaseNamespace,
|
||||||
|
onSetMode,
|
||||||
}: XmlSettingsProps) {
|
}: XmlSettingsProps) {
|
||||||
return (
|
return (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
@@ -38,31 +37,28 @@ export function XmlSettings({
|
|||||||
<div>
|
<div>
|
||||||
<Label className="mb-1.5 block">Mod generare</Label>
|
<Label className="mb-1.5 block">Mod generare</Label>
|
||||||
<div className="flex gap-1.5">
|
<div className="flex gap-1.5">
|
||||||
{(['simple', 'advanced'] as XmlGeneratorMode[]).map((m) => (
|
{(["simple", "advanced"] as XmlGeneratorMode[]).map((m) => (
|
||||||
<button
|
<button
|
||||||
key={m}
|
key={m}
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => onSetMode(m)}
|
onClick={() => onSetMode(m)}
|
||||||
className={cn(
|
className={cn(
|
||||||
'rounded-full border px-3 py-1 text-xs font-medium transition-colors',
|
"rounded-full border px-3 py-1 text-xs font-medium transition-colors",
|
||||||
mode === m
|
mode === m
|
||||||
? 'border-primary bg-primary text-primary-foreground'
|
? "border-primary bg-primary text-primary-foreground"
|
||||||
: 'border-border hover:bg-accent'
|
: "border-border hover:bg-accent",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{m === 'simple' ? 'Simple' : 'Advanced'}
|
{m === "simple" ? "Simple" : "Advanced"}
|
||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<p className="mt-1 text-xs text-muted-foreground">
|
<p className="mt-1 text-xs text-muted-foreground">
|
||||||
{mode === 'simple' ? 'Doar câmpurile definite.' : '+ Short / Upper / Lower / Initials / First.'}
|
{mode === "simple"
|
||||||
|
? "Doar câmpurile definite."
|
||||||
|
: "+ Short / Upper / Lower / Initials / First."}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="ml-auto flex items-center gap-2">
|
|
||||||
<Switch checked={computeMetrics} onCheckedChange={onSetComputeMetrics} id="xml-metrics" />
|
|
||||||
<Label htmlFor="xml-metrics" className="cursor-pointer text-sm">POT / CUT automat</Label>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,20 +1,19 @@
|
|||||||
'use client';
|
"use client";
|
||||||
|
|
||||||
import { useState, useCallback, useMemo } from 'react';
|
import { useState, useCallback, useMemo } from "react";
|
||||||
import type { XmlGeneratorConfig, XmlGeneratorMode } from '../types';
|
import type { XmlGeneratorConfig, XmlGeneratorMode } from "../types";
|
||||||
import { DEFAULT_PRESETS } from '../services/category-presets';
|
import { DEFAULT_PRESETS } from "../services/category-presets";
|
||||||
|
|
||||||
function createDefaultConfig(): XmlGeneratorConfig {
|
function createDefaultConfig(): XmlGeneratorConfig {
|
||||||
const categories: Record<string, { name: string; fieldsText: string }> = {};
|
const categories: Record<string, { name: string; fieldsText: string }> = {};
|
||||||
for (const [name, fields] of Object.entries(DEFAULT_PRESETS)) {
|
for (const [name, fields] of Object.entries(DEFAULT_PRESETS)) {
|
||||||
categories[name] = { name, fieldsText: fields.join('\n') };
|
categories[name] = { name, fieldsText: fields.join("\n") };
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
baseNamespace: 'http://schemas.beletage.ro/contract',
|
baseNamespace: "http://schemas.beletage.ro/contract",
|
||||||
mode: 'advanced',
|
mode: "advanced",
|
||||||
computeMetrics: true,
|
|
||||||
categories,
|
categories,
|
||||||
currentCategory: 'Beneficiar',
|
currentCategory: "Beneficiar",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29,15 +28,12 @@ export function useXmlConfig() {
|
|||||||
setConfig((prev) => ({ ...prev, baseNamespace }));
|
setConfig((prev) => ({ ...prev, baseNamespace }));
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const setComputeMetrics = useCallback((computeMetrics: boolean) => {
|
|
||||||
setConfig((prev) => ({ ...prev, computeMetrics }));
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const setCurrentCategory = useCallback((name: string) => {
|
const setCurrentCategory = useCallback((name: string) => {
|
||||||
setConfig((prev) => ({ ...prev, currentCategory: name }));
|
setConfig((prev) => ({ ...prev, currentCategory: name }));
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const updateCategoryFields = useCallback((categoryName: string, fieldsText: string) => {
|
const updateCategoryFields = useCallback(
|
||||||
|
(categoryName: string, fieldsText: string) => {
|
||||||
setConfig((prev) => {
|
setConfig((prev) => {
|
||||||
const existing = prev.categories[categoryName];
|
const existing = prev.categories[categoryName];
|
||||||
if (!existing) return prev;
|
if (!existing) return prev;
|
||||||
@@ -49,14 +45,16 @@ export function useXmlConfig() {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}, []);
|
},
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
const addCategory = useCallback((name: string) => {
|
const addCategory = useCallback((name: string) => {
|
||||||
setConfig((prev) => {
|
setConfig((prev) => {
|
||||||
if (prev.categories[name]) return prev;
|
if (prev.categories[name]) return prev;
|
||||||
return {
|
return {
|
||||||
...prev,
|
...prev,
|
||||||
categories: { ...prev.categories, [name]: { name, fieldsText: '' } },
|
categories: { ...prev.categories, [name]: { name, fieldsText: "" } },
|
||||||
currentCategory: name,
|
currentCategory: name,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@@ -70,7 +68,9 @@ export function useXmlConfig() {
|
|||||||
return {
|
return {
|
||||||
...prev,
|
...prev,
|
||||||
categories: next,
|
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,
|
...prev,
|
||||||
categories: {
|
categories: {
|
||||||
...prev.categories,
|
...prev.categories,
|
||||||
[name]: { name, fieldsText: preset.join('\n') },
|
[name]: { name, fieldsText: preset.join("\n") },
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
}, []);
|
}, []);
|
||||||
@@ -95,7 +95,7 @@ export function useXmlConfig() {
|
|||||||
...prev,
|
...prev,
|
||||||
categories: {
|
categories: {
|
||||||
...prev.categories,
|
...prev.categories,
|
||||||
[name]: { name: existing.name, fieldsText: '' },
|
[name]: { name: existing.name, fieldsText: "" },
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@@ -109,11 +109,11 @@ export function useXmlConfig() {
|
|||||||
setConfig(loaded);
|
setConfig(loaded);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return useMemo(() => ({
|
return useMemo(
|
||||||
|
() => ({
|
||||||
config,
|
config,
|
||||||
setMode,
|
setMode,
|
||||||
setBaseNamespace,
|
setBaseNamespace,
|
||||||
setComputeMetrics,
|
|
||||||
setCurrentCategory,
|
setCurrentCategory,
|
||||||
updateCategoryFields,
|
updateCategoryFields,
|
||||||
addCategory,
|
addCategory,
|
||||||
@@ -122,7 +122,19 @@ export function useXmlConfig() {
|
|||||||
clearCategoryFields,
|
clearCategoryFields,
|
||||||
resetAll,
|
resetAll,
|
||||||
loadConfig,
|
loadConfig,
|
||||||
}), [config, setMode, setBaseNamespace, setComputeMetrics, setCurrentCategory,
|
}),
|
||||||
updateCategoryFields, addCategory, removeCategory, resetCategoryToPreset,
|
[
|
||||||
clearCategoryFields, resetAll, loadConfig]);
|
config,
|
||||||
|
setMode,
|
||||||
|
setBaseNamespace,
|
||||||
|
setCurrentCategory,
|
||||||
|
updateCategoryFields,
|
||||||
|
addCategory,
|
||||||
|
removeCategory,
|
||||||
|
resetCategoryToPreset,
|
||||||
|
clearCategoryFields,
|
||||||
|
resetAll,
|
||||||
|
loadConfig,
|
||||||
|
],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +1,21 @@
|
|||||||
import type { XmlGeneratorMode, XmlCategory, GeneratedOutput } from '../types';
|
import type { XmlGeneratorMode, XmlCategory, GeneratedOutput } from "../types";
|
||||||
|
|
||||||
function sanitizeName(name: string): string | null {
|
function sanitizeName(name: string): string | null {
|
||||||
const trimmed = name.trim();
|
const trimmed = name.trim();
|
||||||
if (!trimmed) return null;
|
if (!trimmed) return null;
|
||||||
let n = trimmed.replace(/\s+/g, '_').replace(/[^A-Za-z0-9_.-]/g, '');
|
let n = trimmed.replace(/\s+/g, "_").replace(/[^A-Za-z0-9_.-]/g, "");
|
||||||
if (!/^[A-Za-z_]/.test(n)) n = '_' + n;
|
if (!/^[A-Za-z_]/.test(n)) n = "_" + n;
|
||||||
return n || null;
|
return n || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getCategoryNamespace(baseNs: string, category: string): string {
|
function getCategoryNamespace(baseNs: string, category: string): string {
|
||||||
const safeCat = sanitizeName(category) || category;
|
const safeCat = sanitizeName(category) || category;
|
||||||
return baseNs.replace(/\/+$/, '') + '/' + safeCat;
|
return baseNs.replace(/\/+$/, "") + "/" + safeCat;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getCategoryRoot(category: string): string {
|
function getCategoryRoot(category: string): string {
|
||||||
const safeCat = sanitizeName(category) || category;
|
const safeCat = sanitizeName(category) || category;
|
||||||
return safeCat + 'Data';
|
return safeCat + "Data";
|
||||||
}
|
}
|
||||||
|
|
||||||
interface FieldEntry {
|
interface FieldEntry {
|
||||||
@@ -29,14 +29,13 @@ export function generateCategoryXml(
|
|||||||
catData: XmlCategory,
|
catData: XmlCategory,
|
||||||
baseNamespace: string,
|
baseNamespace: string,
|
||||||
mode: XmlGeneratorMode,
|
mode: XmlGeneratorMode,
|
||||||
computeMetrics: boolean,
|
|
||||||
): GeneratedOutput {
|
): GeneratedOutput {
|
||||||
const raw = catData.fieldsText
|
const raw = catData.fieldsText
|
||||||
.split(/\r?\n/)
|
.split(/\r?\n/)
|
||||||
.map((l) => l.trim())
|
.map((l) => l.trim())
|
||||||
.filter((l) => l.length > 0);
|
.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 ns = getCategoryNamespace(baseNamespace, category);
|
||||||
const root = getCategoryRoot(category);
|
const root = getCategoryRoot(category);
|
||||||
@@ -51,19 +50,19 @@ export function generateCategoryXml(
|
|||||||
let baseName = base;
|
let baseName = base;
|
||||||
let idx = 2;
|
let idx = 2;
|
||||||
while (usedNames.has(baseName)) {
|
while (usedNames.has(baseName)) {
|
||||||
baseName = base + '_' + idx;
|
baseName = base + "_" + idx;
|
||||||
idx++;
|
idx++;
|
||||||
}
|
}
|
||||||
usedNames.add(baseName);
|
usedNames.add(baseName);
|
||||||
|
|
||||||
const variants = [baseName];
|
const variants = [baseName];
|
||||||
if (mode === 'advanced') {
|
if (mode === "advanced") {
|
||||||
const suffixes = ['Short', 'Upper', 'Lower', 'Initials', 'First'];
|
const suffixes = ["Short", "Upper", "Lower", "Initials", "First"];
|
||||||
for (const suffix of suffixes) {
|
for (const suffix of suffixes) {
|
||||||
let vn = baseName + suffix;
|
let vn = baseName + suffix;
|
||||||
let k = 2;
|
let k = 2;
|
||||||
while (usedNames.has(vn)) {
|
while (usedNames.has(vn)) {
|
||||||
vn = baseName + suffix + '_' + k;
|
vn = baseName + suffix + "_" + k;
|
||||||
k++;
|
k++;
|
||||||
}
|
}
|
||||||
usedNames.add(vn);
|
usedNames.add(vn);
|
||||||
@@ -74,24 +73,7 @@ export function generateCategoryXml(
|
|||||||
fields.push({ label, baseName, variants });
|
fields.push({ label, baseName, variants });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Auto-add POT/CUT for Suprafete category
|
const allFields = fields;
|
||||||
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);
|
|
||||||
|
|
||||||
// Build XML
|
// Build XML
|
||||||
let xml = '<?xml version="1.0" encoding="UTF-8"?>\n';
|
let xml = '<?xml version="1.0" encoding="UTF-8"?>\n';
|
||||||
@@ -110,18 +92,8 @@ export function generateCategoryXml(
|
|||||||
for (const v of f.variants) {
|
for (const v of f.variants) {
|
||||||
xp += `/${root}/${v}\n`;
|
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 };
|
return { xml, xpaths: xp };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,20 +101,19 @@ export function generateAllCategories(
|
|||||||
categories: Record<string, XmlCategory>,
|
categories: Record<string, XmlCategory>,
|
||||||
baseNamespace: string,
|
baseNamespace: string,
|
||||||
mode: XmlGeneratorMode,
|
mode: XmlGeneratorMode,
|
||||||
computeMetrics: boolean,
|
|
||||||
): Record<string, GeneratedOutput> {
|
): Record<string, GeneratedOutput> {
|
||||||
const results: Record<string, GeneratedOutput> = {};
|
const results: Record<string, GeneratedOutput> = {};
|
||||||
for (const cat of Object.keys(categories)) {
|
for (const cat of Object.keys(categories)) {
|
||||||
const catData = categories[cat];
|
const catData = categories[cat];
|
||||||
if (!catData) continue;
|
if (!catData) continue;
|
||||||
results[cat] = generateCategoryXml(cat, catData, baseNamespace, mode, computeMetrics);
|
results[cat] = generateCategoryXml(cat, catData, baseNamespace, mode);
|
||||||
}
|
}
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function downloadXmlFile(xml: string, filename: string): void {
|
export function downloadXmlFile(xml: string, filename: string): void {
|
||||||
const blob = new Blob([xml], { type: 'application/xml' });
|
const blob = new Blob([xml], { type: "application/xml" });
|
||||||
const a = document.createElement('a');
|
const a = document.createElement("a");
|
||||||
a.href = URL.createObjectURL(blob);
|
a.href = URL.createObjectURL(blob);
|
||||||
a.download = filename;
|
a.download = filename;
|
||||||
document.body.appendChild(a);
|
document.body.appendChild(a);
|
||||||
@@ -155,13 +126,12 @@ export async function downloadZipAll(
|
|||||||
categories: Record<string, XmlCategory>,
|
categories: Record<string, XmlCategory>,
|
||||||
baseNamespace: string,
|
baseNamespace: string,
|
||||||
mode: XmlGeneratorMode,
|
mode: XmlGeneratorMode,
|
||||||
computeMetrics: boolean,
|
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const JSZip = (await import('jszip')).default;
|
const JSZip = (await import("jszip")).default;
|
||||||
const results = generateAllCategories(categories, baseNamespace, mode, computeMetrics);
|
const results = generateAllCategories(categories, baseNamespace, mode);
|
||||||
|
|
||||||
const zip = new JSZip();
|
const zip = new JSZip();
|
||||||
const folder = zip.folder('customXmlParts')!;
|
const folder = zip.folder("customXmlParts")!;
|
||||||
|
|
||||||
let hasAny = false;
|
let hasAny = false;
|
||||||
for (const cat of Object.keys(results)) {
|
for (const cat of Object.keys(results)) {
|
||||||
@@ -175,10 +145,10 @@ export async function downloadZipAll(
|
|||||||
|
|
||||||
if (!hasAny) return;
|
if (!hasAny) return;
|
||||||
|
|
||||||
const content = await zip.generateAsync({ type: 'blob' });
|
const content = await zip.generateAsync({ type: "blob" });
|
||||||
const a = document.createElement('a');
|
const a = document.createElement("a");
|
||||||
a.href = URL.createObjectURL(content);
|
a.href = URL.createObjectURL(content);
|
||||||
a.download = 'beletage_custom_xml_parts.zip';
|
a.download = "beletage_custom_xml_parts.zip";
|
||||||
document.body.appendChild(a);
|
document.body.appendChild(a);
|
||||||
a.click();
|
a.click();
|
||||||
document.body.removeChild(a);
|
document.body.removeChild(a);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
export type XmlGeneratorMode = 'simple' | 'advanced';
|
export type XmlGeneratorMode = "simple" | "advanced";
|
||||||
|
|
||||||
export interface XmlCategory {
|
export interface XmlCategory {
|
||||||
name: string;
|
name: string;
|
||||||
@@ -8,7 +8,6 @@ export interface XmlCategory {
|
|||||||
export interface XmlGeneratorConfig {
|
export interface XmlGeneratorConfig {
|
||||||
baseNamespace: string;
|
baseNamespace: string;
|
||||||
mode: XmlGeneratorMode;
|
mode: XmlGeneratorMode;
|
||||||
computeMetrics: boolean;
|
|
||||||
categories: Record<string, XmlCategory>;
|
categories: Record<string, XmlCategory>;
|
||||||
currentCategory: string;
|
currentCategory: string;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user