Initial commit: ArchiTools modular dashboard platform
Complete Next.js 16 application with 13 fully implemented modules: Email Signature, Word XML Generator, Registratura, Dashboard, Tag Manager, IT Inventory, Address Book, Password Vault, Mini Utilities, Prompt Generator, Digital Signatures, Word Templates, and AI Chat. Includes core platform systems (module registry, feature flags, storage abstraction, i18n, theming, auth stub, tagging), 16 technical documentation files, Docker deployment config, and legacy HTML tool reference. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
456
legacy/emailsignature/emailsignature-config.html
Normal file
456
legacy/emailsignature/emailsignature-config.html
Normal file
@@ -0,0 +1,456 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ro">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Configurator semnatura e-mail</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;700&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body { font-family: 'Inter', sans-serif; }
|
||||
.no-select { -webkit-user-select: none; -ms-user-select: none; user-select: none; }
|
||||
|
||||
input[type=range] {
|
||||
-webkit-appearance: none; appearance: none; width: 100%; height: 4px;
|
||||
background: #e5e7eb; border-radius: 5px; outline: none; transition: background 0.2s ease;
|
||||
}
|
||||
input[type=range]:hover { background: #d1d5db; }
|
||||
input[type=range]::-webkit-slider-thumb {
|
||||
-webkit-appearance: none; appearance: none; width: 12px; height: 20px;
|
||||
background: #22B5AB; cursor: pointer; border-radius: 4px;
|
||||
margin-top: -8px; box-shadow: 0 1px 3px rgba(0,0,0,0.2);
|
||||
transition: transform 0.1s ease-in-out, box-shadow 0.1s ease-in-out;
|
||||
}
|
||||
input[type=range]::-webkit-slider-thumb:active { transform: scale(1.15); box-shadow: 0 2px 6px rgba(0,0,0,0.3); }
|
||||
input[type=range]::-moz-range-thumb {
|
||||
width: 12px; height: 20px; background: #22B5AB; cursor: pointer;
|
||||
border-radius: 4px; border: none; box-shadow: 0 1px 3px rgba(0,0,0,0.2);
|
||||
}
|
||||
#preview-wrapper { transition: transform 0.2s ease-in-out; transform-origin: top left; }
|
||||
.color-swatch {
|
||||
width: 24px; height: 24px; border-radius: 9999px; cursor: pointer;
|
||||
border: 2px solid transparent; transition: all 0.2s ease;
|
||||
}
|
||||
.color-swatch.active { border-color: #22B5AB; transform: scale(1.1); box-shadow: 0 0 0 2px white, 0 0 0 4px #22B5AB; }
|
||||
.collapsible-content { max-height: 0; overflow: hidden; transition: max-height 0.3s ease-out; }
|
||||
.collapsible-content.open { max-height: 1000px; /* Valoare mare pentru a permite extinderea */ }
|
||||
.collapsible-trigger svg { transition: transform 0.3s ease; }
|
||||
.collapsible-trigger.open svg { transform: rotate(90deg); }
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-gray-50 text-gray-800 no-select">
|
||||
|
||||
<div class="container mx-auto p-4 md:p-8">
|
||||
<header class="text-center mb-10">
|
||||
<h1 class="text-3xl md:text-4xl font-bold text-gray-900">Configurator semnatura e-mail</h1>
|
||||
</header>
|
||||
|
||||
<div class="flex flex-col lg:flex-row gap-8">
|
||||
|
||||
<!-- Panoul de control -->
|
||||
<aside class="lg:w-2/5 bg-white p-6 rounded-2xl shadow-lg border border-gray-200">
|
||||
<div id="controls">
|
||||
<!-- Secțiunea Date Personale -->
|
||||
<div class="mb-4">
|
||||
<h3 class="text-lg font-semibold text-gray-800 border-b pb-2 mb-4">Date Personale</h3>
|
||||
<div class="space-y-3">
|
||||
<div>
|
||||
<label for="input-prefix" class="block text-sm font-medium text-gray-700 mb-1">Titulatură (prefix)</label>
|
||||
<input type="text" id="input-prefix" value="arh." class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:ring-teal-500 focus:border-teal-500">
|
||||
</div>
|
||||
<div>
|
||||
<label for="input-name" class="block text-sm font-medium text-gray-700 mb-1">Nume și Prenume</label>
|
||||
<input type="text" id="input-name" value="Marius TĂRĂU" class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:ring-teal-500 focus:border-teal-500">
|
||||
</div>
|
||||
<div>
|
||||
<label for="input-title" class="block text-sm font-medium text-gray-700 mb-1">Funcția</label>
|
||||
<input type="text" id="input-title" value="Arhitect • Beletage SRL" class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:ring-teal-500 focus:border-teal-500">
|
||||
</div>
|
||||
<div>
|
||||
<label for="input-phone" class="block text-sm font-medium text-gray-700 mb-1">Telefon (format 07xxxxxxxx)</label>
|
||||
<input type="tel" id="input-phone" value="0785123433" class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:ring-teal-500 focus:border-teal-500">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Culori Text (Collapsible) -->
|
||||
<div class="mb-4">
|
||||
<div class="collapsible-trigger flex justify-between items-center cursor-pointer border-b pb-2 mb-2">
|
||||
<h3 class="text-lg font-semibold text-gray-800">Culori Text</h3>
|
||||
<svg class="w-4 h-4 text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path></svg>
|
||||
</div>
|
||||
<div class="collapsible-content">
|
||||
<div id="color-controls" class="space-y-2 pt-2"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Secțiunea Stil & Aranjare (Collapsible) -->
|
||||
<div class="mb-4">
|
||||
<div class="collapsible-trigger flex justify-between items-center cursor-pointer border-b pb-2 mb-2">
|
||||
<h3 class="text-lg font-semibold text-gray-800">Stil & Aranjare</h3>
|
||||
<svg class="w-4 h-4 text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path></svg>
|
||||
</div>
|
||||
<div class="collapsible-content">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-x-6 gap-y-3 pt-2">
|
||||
<div>
|
||||
<label for="green-line-width" class="block text-sm font-medium text-gray-700 mb-2">Lungime linie verde (<span id="green-line-value">97</span>px)</label>
|
||||
<input id="green-line-width" type="range" min="50" max="300" value="97">
|
||||
</div>
|
||||
<div>
|
||||
<label for="section-spacing" class="block text-sm font-medium text-gray-700 mb-2">Spațiere vert. secțiuni (<span id="section-spacing-value">10</span>px)</label>
|
||||
<input id="section-spacing" type="range" min="0" max="30" value="10">
|
||||
</div>
|
||||
<div>
|
||||
<label for="logo-spacing" class="block text-sm font-medium text-gray-700 mb-2">Spațiere vert. Logo (<span id="logo-spacing-value">10</span>px)</label>
|
||||
<input id="logo-spacing" type="range" min="0" max="30" value="10">
|
||||
</div>
|
||||
<div>
|
||||
<label for="title-spacing" class="block text-sm font-medium text-gray-700 mb-2">Spațiere vert. funcție (<span id="title-spacing-value">2</span>px)</label>
|
||||
<input id="title-spacing" type="range" min="0" max="20" value="2">
|
||||
</div>
|
||||
<div>
|
||||
<label for="b-gutter-width" class="block text-sm font-medium text-gray-700 mb-2">Aliniere contact (<span id="b-gutter-value">13</span>px)</label>
|
||||
<input id="b-gutter-width" type="range" min="0" max="150" value="13">
|
||||
</div>
|
||||
<div>
|
||||
<label for="icon-text-spacing" class="block text-sm font-medium text-gray-700 mb-2">Spațiu Icon-Text (<span id="icon-text-spacing-value">5</span>px)</label>
|
||||
<input id="icon-text-spacing" type="range" min="-10" max="30" value="5">
|
||||
</div>
|
||||
<div>
|
||||
<label for="icon-vertical-pos" class="block text-sm font-medium text-gray-700 mb-2">Aliniere vert. iconițe (<span id="icon-vertical-value">1</span>px)</label>
|
||||
<input id="icon-vertical-pos" type="range" min="-10" max="10" value="1">
|
||||
</div>
|
||||
<div>
|
||||
<label for="motto-spacing" class="block text-sm font-medium text-gray-700 mb-2">Spațiere vert. motto (<span id="motto-spacing-value">3</span>px)</label>
|
||||
<input id="motto-spacing" type="range" min="0" max="20" value="3">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Opțiuni -->
|
||||
<div class="mb-4">
|
||||
<h3 class="text-lg font-semibold text-gray-800 border-b pb-2 mb-4">Opțiuni</h3>
|
||||
<div class="space-y-2">
|
||||
<label class="flex items-center space-x-3 cursor-pointer">
|
||||
<input type="checkbox" id="reply-variant-checkbox" class="h-4 w-4 rounded border-gray-300 text-teal-600 focus:ring-teal-500">
|
||||
<span class="text-sm font-medium text-gray-700">Variantă simplă (fără logo/adresă)</span>
|
||||
</label>
|
||||
<label class="flex items-center space-x-3 cursor-pointer">
|
||||
<input type="checkbox" id="super-reply-variant-checkbox" class="h-4 w-4 rounded border-gray-300 text-teal-600 focus:ring-teal-500">
|
||||
<span class="text-sm font-medium text-gray-700">Super-simplă (doar nume/telefon)</span>
|
||||
</label>
|
||||
<label class="flex items-center space-x-3 cursor-pointer">
|
||||
<input type="checkbox" id="use-svg-checkbox" class="h-4 w-4 rounded border-gray-300 text-teal-600 focus:ring-teal-500">
|
||||
<span class="text-sm font-medium text-gray-700">Folosește imagini SVG (calitate maximă)</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Buton de Export -->
|
||||
<div class="mt-8 pt-6 border-t">
|
||||
<button id="export-btn" class="w-full bg-teal-600 text-white font-bold py-3 px-4 rounded-lg hover:bg-teal-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-teal-500 transition-all duration-300 ease-in-out transform hover:scale-105">
|
||||
Descarcă HTML
|
||||
</button>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<!-- Previzualizare Live -->
|
||||
<main class="lg:w-3/5 bg-white p-6 rounded-2xl shadow-lg border border-gray-200 overflow-hidden">
|
||||
<div class="flex justify-between items-center border-b pb-3 mb-4">
|
||||
<h2 class="text-2xl font-bold text-gray-900">Previzualizare Live</h2>
|
||||
<button id="zoom-btn" class="text-sm bg-gray-200 text-gray-700 px-3 py-1 rounded-md hover:bg-gray-300">Zoom 100%</button>
|
||||
</div>
|
||||
<div id="preview-wrapper" class="overflow-auto">
|
||||
<div id="preview-container">
|
||||
<!-- Aici este inserat codul HTML al semnăturii -->
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const controls = {
|
||||
prefix: document.getElementById('input-prefix'),
|
||||
name: document.getElementById('input-name'),
|
||||
title: document.getElementById('input-title'),
|
||||
phone: document.getElementById('input-phone'),
|
||||
greenLine: document.getElementById('green-line-width'),
|
||||
gutter: document.getElementById('b-gutter-width'),
|
||||
iconTextSpacing: document.getElementById('icon-text-spacing'),
|
||||
iconVertical: document.getElementById('icon-vertical-pos'),
|
||||
mottoSpacing: document.getElementById('motto-spacing'),
|
||||
sectionSpacing: document.getElementById('section-spacing'),
|
||||
titleSpacing: document.getElementById('title-spacing'),
|
||||
logoSpacing: document.getElementById('logo-spacing'),
|
||||
replyCheckbox: document.getElementById('reply-variant-checkbox'),
|
||||
superReplyCheckbox: document.getElementById('super-reply-variant-checkbox'),
|
||||
useSvgCheckbox: document.getElementById('use-svg-checkbox'),
|
||||
exportBtn: document.getElementById('export-btn'),
|
||||
zoomBtn: document.getElementById('zoom-btn'),
|
||||
colorControls: document.getElementById('color-controls')
|
||||
};
|
||||
|
||||
const values = {
|
||||
greenLine: document.getElementById('green-line-value'),
|
||||
gutter: document.getElementById('b-gutter-value'),
|
||||
iconTextSpacing: document.getElementById('icon-text-spacing-value'),
|
||||
iconVertical: document.getElementById('icon-vertical-value'),
|
||||
mottoSpacing: document.getElementById('motto-spacing-value'),
|
||||
sectionSpacing: document.getElementById('section-spacing-value'),
|
||||
titleSpacing: document.getElementById('title-spacing-value'),
|
||||
logoSpacing: document.getElementById('logo-spacing-value')
|
||||
};
|
||||
|
||||
const previewContainer = document.getElementById('preview-container');
|
||||
const previewWrapper = document.getElementById('preview-wrapper');
|
||||
|
||||
const imageSets = {
|
||||
png: {
|
||||
logo: 'https://beletage.ro/img/Semnatura-Logo.png',
|
||||
greySlash: 'https://beletage.ro/img/Grey-slash.png',
|
||||
greenSlash: 'https://beletage.ro/img/Green-slash.png'
|
||||
},
|
||||
svg: {
|
||||
logo: 'https://beletage.ro/img/Logo-Beletage.svg',
|
||||
greySlash: 'https://beletage.ro/img/Grey-slash.svg',
|
||||
greenSlash: 'https://beletage.ro/img/Green-slash.svg'
|
||||
}
|
||||
};
|
||||
|
||||
const beletageColors = {
|
||||
verde: '#22B5AB',
|
||||
griInchis: '#54504F',
|
||||
griDeschis: '#A7A9AA',
|
||||
negru: '#323232'
|
||||
};
|
||||
|
||||
const colorConfig = {
|
||||
prefix: { label: 'Titulatură', default: beletageColors.griInchis },
|
||||
name: { label: 'Nume', default: beletageColors.griInchis },
|
||||
title: { label: 'Funcție', default: beletageColors.griDeschis },
|
||||
address: { label: 'Adresă', default: beletageColors.griDeschis },
|
||||
phone: { label: 'Telefon', default: beletageColors.griInchis },
|
||||
website: { label: 'Website', default: beletageColors.griInchis },
|
||||
motto: { label: 'Motto', default: beletageColors.verde }
|
||||
};
|
||||
|
||||
let currentColors = {};
|
||||
|
||||
function createColorPickers() {
|
||||
for (const [key, config] of Object.entries(colorConfig)) {
|
||||
currentColors[key] = config.default;
|
||||
const controlRow = document.createElement('div');
|
||||
controlRow.className = 'flex items-center justify-between';
|
||||
const label = document.createElement('span');
|
||||
label.className = 'text-sm font-medium text-gray-700';
|
||||
label.textContent = config.label;
|
||||
controlRow.appendChild(label);
|
||||
const swatchesContainer = document.createElement('div');
|
||||
swatchesContainer.className = 'flex items-center space-x-2';
|
||||
swatchesContainer.dataset.controlKey = key;
|
||||
for (const color of Object.values(beletageColors)) {
|
||||
const swatch = document.createElement('div');
|
||||
swatch.className = 'color-swatch';
|
||||
swatch.style.backgroundColor = color;
|
||||
swatch.dataset.color = color;
|
||||
if (color === config.default) swatch.classList.add('active');
|
||||
swatchesContainer.appendChild(swatch);
|
||||
}
|
||||
controlRow.appendChild(swatchesContainer);
|
||||
controls.colorControls.appendChild(controlRow);
|
||||
}
|
||||
|
||||
controls.colorControls.addEventListener('click', (e) => {
|
||||
if (e.target.classList.contains('color-swatch')) {
|
||||
const key = e.target.parentElement.dataset.controlKey;
|
||||
currentColors[key] = e.target.dataset.color;
|
||||
e.target.parentElement.querySelectorAll('.color-swatch').forEach(s => s.classList.remove('active'));
|
||||
e.target.classList.add('active');
|
||||
updatePreview();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function generateSignatureHTML(data) {
|
||||
const {
|
||||
prefix, name, title, phone, phoneLink, greenLineWidth, gutterWidth,
|
||||
iconTextSpacing, iconVerticalOffset, mottoSpacing, sectionSpacing, titleSpacing, logoSpacing,
|
||||
isReply, isSuperReply, colors, images
|
||||
} = data;
|
||||
|
||||
const hideTitle = isReply || isSuperReply ? 'mso-hide:all;display:none!important;max-height:0;overflow:hidden;font-size:0;line-height:0;' : '';
|
||||
const hideLogoAddress = isReply || isSuperReply ? 'mso-hide:all;display:none!important;max-height:0;overflow:hidden;font-size:0;line-height:0;' : '';
|
||||
const hideBottom = isSuperReply ? 'mso-hide:all;display:none!important;max-height:0;overflow:hidden;font-size:0;line-height:0;' : '';
|
||||
const hidePhoneIcon = isSuperReply ? 'mso-hide:all;display:none!important;max-height:0;overflow:hidden;font-size:0;line-height:0;' : '';
|
||||
|
||||
const spacerWidth = Math.max(0, iconTextSpacing);
|
||||
const textPaddingLeft = Math.max(0, -iconTextSpacing);
|
||||
|
||||
const prefixHTML = prefix ? `<span style="font-size:13px; color:${colors.prefix};">${prefix} </span>` : '';
|
||||
const logoWidth = controls.useSvgCheckbox.checked ? 162 : 162;
|
||||
const logoHeight = controls.useSvgCheckbox.checked ? 24 : 24;
|
||||
|
||||
return `
|
||||
<table role="presentation" cellpadding="0" cellspacing="0" border="0" width="540" style="font-family: Arial, Helvetica, sans-serif; color:#333333; font-size:14px; line-height:18px;">
|
||||
<tbody>
|
||||
<tr><td style="padding:0 0 ${titleSpacing}px 0;">${prefixHTML}<span style="font-size:15px; color:${colors.name}; font-weight:700;">${name}</span></td></tr>
|
||||
<tr style="${hideTitle}"><td style="padding:0 0 8px 0;"><span style="font-size:12px; color:${colors.title};">${title}</span></td></tr>
|
||||
<tr style="${hideBottom}">
|
||||
<td style="padding:0; font-size:0; line-height:0;">
|
||||
<table role="presentation" cellpadding="0" cellspacing="0" border="0" width="540">
|
||||
<tr>
|
||||
<td width="${greenLineWidth}" height="2" bgcolor="${beletageColors.verde}" style="font-size:0; line-height:0; height:2px;"></td>
|
||||
<td width="${540 - greenLineWidth}" height="2" style="font-size:0; line-height:0; height:2px;"></td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr style="${hideLogoAddress}"><td style="padding:${logoSpacing}px 0 ${parseInt(logoSpacing, 10) + 2}px 0;">
|
||||
<a href="https://www.beletage.ro" style="text-decoration:none; border:0;">
|
||||
<img src="${images.logo}" alt="Beletage" style="display:block; border:0; height:${logoHeight}px; width:${logoWidth}px;" height="${logoHeight}" width="${logoWidth}">
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr>
|
||||
<td style="padding-top:${hideLogoAddress ? '0' : sectionSpacing}px;">
|
||||
<table role="presentation" cellpadding="0" cellspacing="0" border="0" width="540" style="font-size:13px; line-height:18px;">
|
||||
<tbody>
|
||||
<tr style="${hideLogoAddress}">
|
||||
<td width="${gutterWidth}" style="width:${gutterWidth}px; font-size:0; line-height:0;"></td>
|
||||
<td width="11" style="width:11px; vertical-align:top; padding-top:${4 + iconVerticalOffset}px;">
|
||||
<img src="${images.greySlash}" alt="" width="11" height="11" style="display: block; border:0;">
|
||||
</td>
|
||||
<td width="${spacerWidth}" style="width:${spacerWidth}px; font-size:0; line-height:0;"></td>
|
||||
<td style="vertical-align:top; padding:0 0 0 ${textPaddingLeft}px;">
|
||||
<a href="https://maps.google.com/?q=str.%20Unirii%203%2C%20ap.%2026%2C%20Cluj-Napoca%20400417%2C%20Rom%C3%A2nia" style="color:${colors.address}; text-decoration:none;"><span style="color:${colors.address}; text-decoration:none;">str. Unirii, nr. 3, ap. 26<br>Cluj-Napoca, Cluj 400417<br>România</span></a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="${gutterWidth}" style="width:${gutterWidth}px; font-size:0; line-height:0;"></td>
|
||||
<td width="11" style="width:11px; vertical-align:top; padding-top:${12 + iconVerticalOffset}px; ${hidePhoneIcon}">
|
||||
<img src="${images.greenSlash}" alt="" width="11" height="7" style="display: block; border:0;">
|
||||
</td>
|
||||
<td width="${isSuperReply ? 0 : spacerWidth}" style="width:${isSuperReply ? 0 : spacerWidth}px; font-size:0; line-height:0;"></td>
|
||||
<td style="vertical-align:top; padding:8px 0 0 ${isSuperReply ? 0 : textPaddingLeft}px;">
|
||||
<a href="${phoneLink}" style="color:${colors.phone}; text-decoration:none;"><span style="color:${colors.phone}; text-decoration:none;">${phone}</span></a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr style="${hideBottom}"><td style="padding:${sectionSpacing}px 0 ${mottoSpacing}px 0;"><a href="https://www.beletage.ro" style="color:${colors.website}; text-decoration:none;"><span style="color:${colors.website}; text-decoration:none;">www.beletage.ro</span></a></td></tr>
|
||||
<tr style="${hideBottom}">
|
||||
<td style="padding:0; font-size:0; line-height:0;">
|
||||
<table role="presentation" cellpadding="0" cellspacing="0" border="0" width="540">
|
||||
<tr>
|
||||
<td width="${greenLineWidth}" height="1" bgcolor="${beletageColors.verde}" style="font-size:0; line-height:0; height:1px;"></td>
|
||||
<td width="${540 - greenLineWidth}" height="1" style="font-size:0; line-height:0; height:1px;"></td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr style="${hideBottom}"><td style="padding:${mottoSpacing}px 0 0 0;"><span style="font-size:12px; color:${colors.motto}; font-style:italic;">we make complex simple</span></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
`;
|
||||
}
|
||||
|
||||
function updatePreview() {
|
||||
const phoneRaw = controls.phone.value.replace(/\s/g, '');
|
||||
let formattedPhone = controls.phone.value;
|
||||
let phoneLink = `tel:${phoneRaw}`;
|
||||
|
||||
if (phoneRaw.length === 10 && phoneRaw.startsWith('07')) {
|
||||
formattedPhone = `+40 ${phoneRaw.substring(1, 4)} ${phoneRaw.substring(4, 7)} ${phoneRaw.substring(7, 10)}`;
|
||||
phoneLink = `tel:+40${phoneRaw.substring(1)}`;
|
||||
}
|
||||
|
||||
if (controls.superReplyCheckbox.checked) {
|
||||
controls.replyCheckbox.checked = true;
|
||||
controls.replyCheckbox.disabled = true;
|
||||
} else {
|
||||
controls.replyCheckbox.disabled = false;
|
||||
}
|
||||
|
||||
const data = {
|
||||
prefix: controls.prefix.value,
|
||||
name: controls.name.value,
|
||||
title: controls.title.value,
|
||||
phone: formattedPhone,
|
||||
phoneLink: phoneLink,
|
||||
greenLineWidth: controls.greenLine.value,
|
||||
gutterWidth: controls.gutter.value,
|
||||
iconTextSpacing: controls.iconTextSpacing.value,
|
||||
iconVerticalOffset: parseInt(controls.iconVertical.value, 10),
|
||||
mottoSpacing: controls.mottoSpacing.value,
|
||||
sectionSpacing: controls.sectionSpacing.value,
|
||||
titleSpacing: controls.titleSpacing.value,
|
||||
logoSpacing: controls.logoSpacing.value,
|
||||
isReply: controls.replyCheckbox.checked,
|
||||
isSuperReply: controls.superReplyCheckbox.checked,
|
||||
colors: { ...currentColors },
|
||||
images: controls.useSvgCheckbox.checked ? imageSets.svg : imageSets.png
|
||||
};
|
||||
|
||||
values.greenLine.textContent = data.greenLineWidth;
|
||||
values.gutter.textContent = data.gutterWidth;
|
||||
values.iconTextSpacing.textContent = data.iconTextSpacing;
|
||||
values.iconVertical.textContent = data.iconVerticalOffset;
|
||||
values.mottoSpacing.textContent = data.mottoSpacing;
|
||||
values.sectionSpacing.textContent = data.sectionSpacing;
|
||||
values.titleSpacing.textContent = data.titleSpacing;
|
||||
values.logoSpacing.textContent = data.logoSpacing;
|
||||
|
||||
previewContainer.innerHTML = generateSignatureHTML(data);
|
||||
}
|
||||
|
||||
// --- Inițializare ---
|
||||
createColorPickers();
|
||||
|
||||
Object.values(controls).forEach(control => {
|
||||
if (control.id !== 'export-btn' && control.id !== 'zoom-btn') {
|
||||
control.addEventListener('input', updatePreview);
|
||||
}
|
||||
});
|
||||
|
||||
document.querySelectorAll('.collapsible-trigger').forEach(trigger => {
|
||||
trigger.addEventListener('click', () => {
|
||||
const content = trigger.nextElementSibling;
|
||||
trigger.classList.toggle('open');
|
||||
content.classList.toggle('open');
|
||||
});
|
||||
});
|
||||
|
||||
controls.zoomBtn.addEventListener('click', () => {
|
||||
const isZoomed = previewWrapper.style.transform === 'scale(2)';
|
||||
if (isZoomed) {
|
||||
previewWrapper.style.transform = 'scale(1)';
|
||||
controls.zoomBtn.textContent = 'Zoom 200%';
|
||||
} else {
|
||||
previewWrapper.style.transform = 'scale(2)';
|
||||
controls.zoomBtn.textContent = 'Zoom 100%';
|
||||
}
|
||||
});
|
||||
|
||||
controls.exportBtn.addEventListener('click', () => {
|
||||
const finalHTML = previewContainer.innerHTML;
|
||||
const blob = new Blob([finalHTML], { type: 'text/html' });
|
||||
const a = document.createElement('a');
|
||||
a.href = URL.createObjectURL(blob);
|
||||
a.download = 'semnatura-beletage.html';
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
});
|
||||
|
||||
updatePreview();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
148
legacy/manicprojects/current manic time Tags.txt
Executable file
148
legacy/manicprojects/current manic time Tags.txt
Executable file
@@ -0,0 +1,148 @@
|
||||
Pauza de masa
|
||||
Timp personal
|
||||
|
||||
Concediu
|
||||
Compensare overtime
|
||||
|
||||
Beletage
|
||||
Ofertare
|
||||
Configurari
|
||||
Organizare initiala
|
||||
Pregatire Portofoliu
|
||||
Website
|
||||
Documentare
|
||||
Design grafic
|
||||
Design interior
|
||||
Design exterior
|
||||
|
||||
Releveu
|
||||
Reclama
|
||||
|
||||
000 Farmacie
|
||||
002 Cladire birouri Stratec
|
||||
003 PUZ Bellavista
|
||||
007 Design Apartament Teodora
|
||||
010 Casa Doinei
|
||||
016 Duplex Eremia
|
||||
024 Bloc Petofi
|
||||
028 PUZ Borhanci-Sopor
|
||||
033 Mansardare Branului
|
||||
039 Cabinete Stoma Scala
|
||||
041 Imobil mixt Progresului
|
||||
045 Casa Andrei Muresanu
|
||||
052 PUZ Carpenului
|
||||
059 PUZ Nordului
|
||||
064 Casa Salicea
|
||||
066 Terasa Gherase
|
||||
070 Bloc Fanatelor
|
||||
073 Case Frumoasa
|
||||
074 PUG Cosbuc
|
||||
076 Casa Copernicus
|
||||
077 PUZ Schimbare destinatie Brancusi
|
||||
078 Service auto Linistei
|
||||
079 Amenajare drum Servitute Eremia
|
||||
080 Bloc Tribunul
|
||||
081 Extindere casa Gherase
|
||||
083 Modificari casa Zsigmund 18
|
||||
084 Mansardare Petofi 21
|
||||
085 Container CT Spital Tabacarilor
|
||||
086 Imprejmuire casa sat Gheorgheni
|
||||
087 Duplex Oasului fn
|
||||
089 PUZ A-Liu Sopor
|
||||
090 VR MedEvents
|
||||
091 Reclama Caparol
|
||||
092 Imobil birouri 13 Septembrie
|
||||
093 Casa Salistea Noua
|
||||
094 PUD Casa Rediu
|
||||
095 Duplex Vanatorului
|
||||
096 Design apartament Sopor
|
||||
097 Cabana Gilau
|
||||
101 PUZ Gilau
|
||||
102 PUZ Ghimbav
|
||||
103 Piscine Lunca Noua
|
||||
104 PUZ REGHIN
|
||||
105 CUT&Crust
|
||||
106 PUZ Mihai Romanu Nord
|
||||
108 Reabilitare Bloc Beiusului
|
||||
109 Case Samboleni
|
||||
110 Penny Crasna
|
||||
111 Anexa Piscina Borhanci
|
||||
112 PUZ Blocuri Bistrita
|
||||
113 PUZ VARATEC-FIRIZA
|
||||
114 PUG Husi
|
||||
115 PUG Josenii Bargaului
|
||||
116 PUG Monor
|
||||
117 Schimbare Destinatie Mihai Viteazu 2
|
||||
120 Anexa Brasov
|
||||
121 Imprejurare imobil Mesterul Manole 9
|
||||
122 Fastfood Bashar
|
||||
123 PUD Rediu 2
|
||||
127 Casa Socaciu Ciurila
|
||||
128 Schimbare de destinatie Danubius
|
||||
129 (re) Casa Sarca-Sorescu
|
||||
130 Casa Suta-Wonderland
|
||||
131 PUD Oasului Hufi
|
||||
132 Reabilitare Camin Cultural Baciu
|
||||
133 PUG Feldru
|
||||
134 DALI Blocuri Murfatlar
|
||||
135 Case de vacanta Dianei
|
||||
136 PUG BROSTENI
|
||||
139 Casa Turda
|
||||
140 Releveu Bistrita (Morariu)
|
||||
141 PUZ Janovic Jeno
|
||||
142 Penny Borhanci
|
||||
143 Pavilion Politie Radauti
|
||||
149 Duplex Sorescu 31-33
|
||||
150 DALI SF Scoala Baciu
|
||||
151 Casa Alexandru Bohatiel 17
|
||||
152 PUZ Penny Tautii Magheraus
|
||||
153 PUG Banita
|
||||
155 PT Scoala Floresti
|
||||
156 Case Sorescu
|
||||
157 Gradi-Cresa Baciu
|
||||
158 Duplex Sorescu 21-23
|
||||
159 Amenajare Spatiu Grenke PBC
|
||||
160 Etajare Primaria Baciu
|
||||
161 Extindere Ap Baciu
|
||||
164 SD salon Aurel Vlaicu
|
||||
165 Reclama Marasti
|
||||
166 Catei Apahida
|
||||
167 Apartament Mircea Zaciu 13-15
|
||||
169 Casa PETRILA 37
|
||||
170 Cabana Campeni AB
|
||||
171 Camin Apahida
|
||||
L089 PUZ TUSA-BOJAN
|
||||
172 Design casa Iugoslaviei 18
|
||||
173 Reabilitare spitale Sighetu
|
||||
174 StudX UMFST
|
||||
176 - 2025 - ReAC Ansamblu rezi Bibescu
|
||||
|
||||
|
||||
CU
|
||||
Schita
|
||||
Avize
|
||||
PUD
|
||||
AO
|
||||
PUZ
|
||||
PUG
|
||||
DTAD
|
||||
DTAC
|
||||
PT
|
||||
Detalii de Executie
|
||||
Studii de fundamentare
|
||||
|
||||
Regulament
|
||||
Parte desenata
|
||||
Parte scrisa
|
||||
Consultanta client
|
||||
Macheta
|
||||
Consultanta receptie
|
||||
|
||||
Redactare
|
||||
Depunere
|
||||
Ridicare
|
||||
Verificare proiect
|
||||
|
||||
Vizita santier
|
||||
|
||||
Master MATDR
|
||||
694
legacy/wordXMLgenerator/word-xml-generator-advanced.html
Normal file
694
legacy/wordXMLgenerator/word-xml-generator-advanced.html
Normal file
@@ -0,0 +1,694 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ro">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Beletage – Word XML Data Engine</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<!-- JSZip pentru arhivă ZIP -->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"
|
||||
integrity="sha512-FGv7V3GpCr3C6wz6Q4z8F1v8y4mZohwPqhwKiPfz0btvAvOE0tfLOgvBcFQncn1C3KW0y5fN9c7v1sQW8vGfMQ=="
|
||||
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
<style>
|
||||
:root {
|
||||
color-scheme: dark;
|
||||
}
|
||||
body {
|
||||
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||||
margin: 0;
|
||||
padding: 1.5rem;
|
||||
background: #020617;
|
||||
color: #e5e7eb;
|
||||
}
|
||||
h1 {
|
||||
font-size: 1.7rem;
|
||||
margin-bottom: .25rem;
|
||||
}
|
||||
.subtitle {
|
||||
font-size: .9rem;
|
||||
color: #9ca3af;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.card {
|
||||
background: #020617;
|
||||
border-radius: 1rem;
|
||||
padding: 1.1rem 1.3rem;
|
||||
margin-bottom: 1rem;
|
||||
border: 1px solid #1e293b;
|
||||
box-shadow: 0 15px 35px rgba(0,0,0,.45);
|
||||
}
|
||||
label {
|
||||
font-size: .8rem;
|
||||
color: #9ca3af;
|
||||
display: block;
|
||||
margin-bottom: .2rem;
|
||||
}
|
||||
input, textarea, select {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
padding: .5rem .6rem;
|
||||
border-radius: .5rem;
|
||||
border: 1px solid #334155;
|
||||
background: #020617;
|
||||
color: #e5e7eb;
|
||||
font-family: inherit;
|
||||
font-size: .9rem;
|
||||
outline: none;
|
||||
}
|
||||
input:focus, textarea:focus, select:focus {
|
||||
border-color: #38bdf8;
|
||||
box-shadow: 0 0 0 1px #38bdf8;
|
||||
}
|
||||
textarea { min-height: 140px; resize: vertical; }
|
||||
.row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 1rem;
|
||||
}
|
||||
.col-3 { flex: 1 1 220px; }
|
||||
.col-6 { flex: 1 1 320px; }
|
||||
.col-9 { flex: 3 1 420px; }
|
||||
|
||||
button {
|
||||
padding: .55rem 1.1rem;
|
||||
border-radius: 999px;
|
||||
border: none;
|
||||
background: linear-gradient(135deg, #38bdf8, #6366f1);
|
||||
color: #fff;
|
||||
font-size: .9rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 12px 25px rgba(37,99,235,.4);
|
||||
}
|
||||
button:hover { filter: brightness(1.05); transform: translateY(-1px); }
|
||||
button:active { transform: translateY(0); box-shadow: 0 8px 18px rgba(37,99,235,.6); }
|
||||
|
||||
.btn-secondary {
|
||||
background: transparent;
|
||||
border: 1px solid #4b5563;
|
||||
box-shadow: none;
|
||||
}
|
||||
.btn-secondary:hover {
|
||||
background: #020617;
|
||||
box-shadow: 0 8px 20px rgba(0,0,0,.6);
|
||||
}
|
||||
.btn-small {
|
||||
font-size: .8rem;
|
||||
padding: .35rem .8rem;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.toggle {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: .4rem;
|
||||
font-size: .8rem;
|
||||
color: #cbd5f5;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
.toggle input { width: auto; }
|
||||
|
||||
.pill-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: .4rem;
|
||||
margin-bottom: .4rem;
|
||||
}
|
||||
.pill {
|
||||
padding: .25rem .7rem;
|
||||
border-radius: 999px;
|
||||
border: 1px solid #334155;
|
||||
font-size: .8rem;
|
||||
cursor: pointer;
|
||||
background: #020617;
|
||||
color: #e5e7eb;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: .35rem;
|
||||
}
|
||||
.pill.active {
|
||||
background: linear-gradient(135deg, #38bdf8, #6366f1);
|
||||
border-color: transparent;
|
||||
color: #0f172a;
|
||||
}
|
||||
.pill span.remove {
|
||||
font-size: .8rem;
|
||||
opacity: .7;
|
||||
}
|
||||
.pill span.remove:hover { opacity: 1; }
|
||||
|
||||
.small {
|
||||
font-size: .8rem;
|
||||
color: #9ca3af;
|
||||
margin-top: .25rem;
|
||||
}
|
||||
|
||||
pre {
|
||||
background: #020617;
|
||||
border-radius: .75rem;
|
||||
padding: .7rem .8rem;
|
||||
border: 1px solid #1f2937;
|
||||
overflow: auto;
|
||||
font-size: .8rem;
|
||||
max-height: 340px;
|
||||
}
|
||||
.badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: .15rem .45rem;
|
||||
border-radius: 999px;
|
||||
font-size: .7rem;
|
||||
background: rgba(148,163,184,.18);
|
||||
margin-right: .4rem;
|
||||
margin-bottom: .25rem;
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
body { padding: 1rem; }
|
||||
.card { padding: 1rem; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>Beletage – Word XML Data Engine</h1>
|
||||
<p class="subtitle">
|
||||
Generator de <strong>Custom XML Parts</strong> pentru Word, pe categorii (Beneficiar, Proiect, Suprafete, Meta etc.),
|
||||
cu mod <em>Simple</em> / <em>Advanced</em> și câmpuri derivate (Short, Upper, Initials) + POT/CUT pregătite.
|
||||
</p>
|
||||
|
||||
<!-- SETĂRI GLOBALE -->
|
||||
<div class="card">
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<label for="baseNs">Bază Namespace (se completează automat cu /Categorie)</label>
|
||||
<input id="baseNs" type="text" value="http://schemas.beletage.ro/contract">
|
||||
<div class="small">Ex: <code>http://schemas.beletage.ro/contract</code> → pentru categoria „Proiect” devine
|
||||
<code>http://schemas.beletage.ro/contract/Proiect</code>.
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<label>Mod generare câmpuri</label>
|
||||
<div class="pill-row">
|
||||
<div class="pill active" id="modeSimplePill" onclick="setMode('simple')">Simple</div>
|
||||
<div class="pill" id="modeAdvancedPill" onclick="setMode('advanced')">Advanced</div>
|
||||
</div>
|
||||
<div class="small">
|
||||
<strong>Simple</strong>: doar câmpurile tale.<br>
|
||||
<strong>Advanced</strong>: + Short / Upper / Lower / Initials / First pentru fiecare câmp.
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<label>Opțiuni extra</label>
|
||||
<div class="small" style="margin-top:.25rem;">
|
||||
<label class="toggle">
|
||||
<input type="checkbox" id="computeMetrics" checked>
|
||||
<span>Adaugă câmpuri POT / CUT în categoria Suprafete</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- CATEGORII -->
|
||||
<div class="card">
|
||||
<div class="row">
|
||||
<div class="col-3">
|
||||
<label>Categorii de date</label>
|
||||
<div id="categoryPills" class="pill-row"></div>
|
||||
<button class="btn-secondary btn-small" onclick="addCategoryPrompt()">+ Adaugă categorie</button>
|
||||
<div class="small">
|
||||
Exemple de organizare: <code>Beneficiar</code>, <code>Proiect</code>, <code>Suprafete</code>, <code>Meta</code>.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-9">
|
||||
<label>Câmpuri pentru categoria selectată</label>
|
||||
<textarea id="fieldsArea"></textarea>
|
||||
<div class="small">
|
||||
Un câmp pe linie. Poți edita lista. Butonul „Reset categorie la preset” reîncarcă valorile default pentru
|
||||
categoria curentă (dacă există).
|
||||
</div>
|
||||
<div style="margin-top:.5rem; display:flex; gap:.5rem; flex-wrap:wrap;">
|
||||
<button class="btn-secondary btn-small" onclick="resetCategoryToPreset()">Reset categorie la preset</button>
|
||||
<button class="btn-secondary btn-small" onclick="clearCategoryFields()">Curăță câmpurile</button>
|
||||
</div>
|
||||
<div class="small" id="nsRootInfo" style="margin-top:.6rem;"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- GENERARE & DOWNLOAD -->
|
||||
<div class="card">
|
||||
<div style="display:flex; flex-wrap:wrap; gap:.5rem; align-items:center; margin-bottom:.5rem;">
|
||||
<button onclick="generateAll()">Generează XML pentru toate categoriile</button>
|
||||
<button class="btn-secondary" onclick="downloadCurrentXml()">Descarcă XML categorie curentă</button>
|
||||
<button class="btn-secondary" onclick="downloadZipAll()">Descarcă ZIP cu toate XML-urile</button>
|
||||
</div>
|
||||
<div class="small">
|
||||
<span class="badge">Tip</span>
|
||||
În Word, fiecare fișier generat devine un Custom XML Part separat (ex: <code>BeneficiarData.xml</code>,
|
||||
<code>ProiectData.xml</code> etc.), perfect pentru organizarea mapping-urilor.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- PREVIEW -->
|
||||
<div class="card">
|
||||
<h3 style="margin-top:0;">Preview XML & XPaths</h3>
|
||||
<div class="small" style="margin-bottom:.4rem;">
|
||||
Selectează o categorie pentru a vedea XML-ul și XPaths-urile aferente.
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<div class="badge">XML categorie curentă</div>
|
||||
<pre id="xmlPreview"></pre>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="badge">XPaths categorie curentă</div>
|
||||
<pre id="xpathPreview"></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// --- PRESETURI CATEGORII ---
|
||||
const defaultPresets = {
|
||||
"Beneficiar": [
|
||||
"NumeClient",
|
||||
"Adresa",
|
||||
"CUI",
|
||||
"CNP",
|
||||
"Reprezentant",
|
||||
"Email",
|
||||
"Telefon"
|
||||
],
|
||||
"Proiect": [
|
||||
"TitluProiect",
|
||||
"AdresaImobil",
|
||||
"NrCadastral",
|
||||
"NrCF",
|
||||
"Localitate",
|
||||
"Judet"
|
||||
],
|
||||
"Suprafete": [
|
||||
"SuprafataTeren",
|
||||
"SuprafataConstruitaLaSol",
|
||||
"SuprafataDesfasurata",
|
||||
"SuprafataUtila"
|
||||
],
|
||||
"Meta": [
|
||||
"NrContract",
|
||||
"DataContract",
|
||||
"Responsabil",
|
||||
"VersiuneDocument",
|
||||
"DataGenerarii"
|
||||
]
|
||||
};
|
||||
|
||||
// --- STATE ---
|
||||
let categories = {}; // { Categorie: { fieldsText: "..." } }
|
||||
let currentCategory = "Beneficiar";
|
||||
let mode = "advanced"; // "simple" | "advanced"
|
||||
const xmlParts = {}; // { Categorie: xmlString }
|
||||
const xpathParts = {}; // { Categorie: xpathString }
|
||||
|
||||
// --- UTILITARE ---
|
||||
function sanitizeName(name) {
|
||||
if (!name) return null;
|
||||
let n = name.trim();
|
||||
if (!n) return null;
|
||||
n = n.replace(/\s+/g, "_").replace(/[^A-Za-z0-9_.-]/g, "");
|
||||
if (!/^[A-Za-z_]/.test(n)) n = "_" + n;
|
||||
return n;
|
||||
}
|
||||
|
||||
function initialsFromLabel(label) {
|
||||
if (!label) return "";
|
||||
return label.trim().split(/\s+/).map(s => s.charAt(0).toUpperCase() + ".").join("");
|
||||
}
|
||||
|
||||
function firstToken(label) {
|
||||
if (!label) return "";
|
||||
return label.trim().split(/\s+/)[0] || "";
|
||||
}
|
||||
|
||||
function getBaseNamespace() {
|
||||
const val = document.getElementById("baseNs").value.trim();
|
||||
return val || "http://schemas.beletage.ro/contract";
|
||||
}
|
||||
|
||||
function getCategoryNamespace(cat) {
|
||||
const base = getBaseNamespace();
|
||||
const safeCat = sanitizeName(cat) || cat;
|
||||
return base.replace(/\/+$/,"") + "/" + safeCat;
|
||||
}
|
||||
|
||||
function getCategoryRoot(cat) {
|
||||
const safeCat = sanitizeName(cat) || cat;
|
||||
return safeCat + "Data";
|
||||
}
|
||||
|
||||
// --- MOD SIMPLE/ADVANCED ---
|
||||
function setMode(m) {
|
||||
mode = m === "advanced" ? "advanced" : "simple";
|
||||
document.getElementById("modeSimplePill").classList.toggle("active", mode === "simple");
|
||||
document.getElementById("modeAdvancedPill").classList.toggle("active", mode === "advanced");
|
||||
// regenerăm previw dacă avem ceva
|
||||
generateAll(false);
|
||||
}
|
||||
|
||||
// --- CATEGORII: INIT, UI, STORAGE ---
|
||||
function initCategories() {
|
||||
// încarcă din localStorage, altfel default
|
||||
const saved = window.localStorage.getItem("beletage_xml_categories");
|
||||
if (saved) {
|
||||
try {
|
||||
const parsed = JSON.parse(saved);
|
||||
categories = parsed.categories || {};
|
||||
currentCategory = parsed.currentCategory || "Beneficiar";
|
||||
} catch(e) {
|
||||
Object.keys(defaultPresets).forEach(cat => {
|
||||
categories[cat] = { fieldsText: defaultPresets[cat].join("\n") };
|
||||
});
|
||||
currentCategory = "Beneficiar";
|
||||
}
|
||||
} else {
|
||||
Object.keys(defaultPresets).forEach(cat => {
|
||||
categories[cat] = { fieldsText: defaultPresets[cat].join("\n") };
|
||||
});
|
||||
currentCategory = "Beneficiar";
|
||||
}
|
||||
|
||||
renderCategoryPills();
|
||||
loadCategoryToUI(currentCategory);
|
||||
}
|
||||
|
||||
function persistCategories() {
|
||||
try {
|
||||
window.localStorage.setItem("beletage_xml_categories", JSON.stringify({
|
||||
categories,
|
||||
currentCategory
|
||||
}));
|
||||
} catch(e){}
|
||||
}
|
||||
|
||||
function renderCategoryPills() {
|
||||
const container = document.getElementById("categoryPills");
|
||||
container.innerHTML = "";
|
||||
Object.keys(categories).forEach(cat => {
|
||||
const pill = document.createElement("div");
|
||||
pill.className = "pill" + (cat === currentCategory ? " active" : "");
|
||||
pill.onclick = () => switchCategory(cat);
|
||||
pill.textContent = cat;
|
||||
|
||||
// nu permitem ștergerea preset-urilor de bază direct (doar la custom)
|
||||
if (!defaultPresets[cat]) {
|
||||
const remove = document.createElement("span");
|
||||
remove.className = "remove";
|
||||
remove.textContent = "×";
|
||||
remove.onclick = (ev) => {
|
||||
ev.stopPropagation();
|
||||
deleteCategory(cat);
|
||||
};
|
||||
pill.appendChild(remove);
|
||||
}
|
||||
container.appendChild(pill);
|
||||
});
|
||||
}
|
||||
|
||||
function switchCategory(cat) {
|
||||
saveCurrentCategoryFields();
|
||||
currentCategory = cat;
|
||||
renderCategoryPills();
|
||||
loadCategoryToUI(cat);
|
||||
updateNsRootInfo();
|
||||
showPreview(cat);
|
||||
persistCategories();
|
||||
}
|
||||
|
||||
function loadCategoryToUI(cat) {
|
||||
const area = document.getElementById("fieldsArea");
|
||||
area.value = categories[cat]?.fieldsText || "";
|
||||
updateNsRootInfo();
|
||||
}
|
||||
|
||||
function saveCurrentCategoryFields() {
|
||||
const area = document.getElementById("fieldsArea");
|
||||
if (!categories[currentCategory]) {
|
||||
categories[currentCategory] = { fieldsText: "" };
|
||||
}
|
||||
categories[currentCategory].fieldsText = area.value;
|
||||
}
|
||||
|
||||
function deleteCategory(cat) {
|
||||
if (!confirm(`Sigur ștergi categoria "${cat}"?`)) return;
|
||||
delete categories[cat];
|
||||
const keys = Object.keys(categories);
|
||||
currentCategory = keys[0] || "Beneficiar";
|
||||
renderCategoryPills();
|
||||
loadCategoryToUI(currentCategory);
|
||||
updateNsRootInfo();
|
||||
persistCategories();
|
||||
}
|
||||
|
||||
function addCategoryPrompt() {
|
||||
const name = prompt("Nume categorie nouă (ex: Urbanism, Fiscal, Altele):");
|
||||
if (!name) return;
|
||||
const trimmed = name.trim();
|
||||
if (!trimmed) return;
|
||||
if (categories[trimmed]) {
|
||||
alert("Categoria există deja.");
|
||||
return;
|
||||
}
|
||||
categories[trimmed] = { fieldsText: "" };
|
||||
currentCategory = trimmed;
|
||||
renderCategoryPills();
|
||||
loadCategoryToUI(currentCategory);
|
||||
updateNsRootInfo();
|
||||
persistCategories();
|
||||
}
|
||||
|
||||
function resetCategoryToPreset() {
|
||||
if (!defaultPresets[currentCategory]) {
|
||||
alert("Categoria curentă nu are preset definit.");
|
||||
return;
|
||||
}
|
||||
if (!confirm("Resetezi lista de câmpuri la presetul standard pentru această categorie?")) return;
|
||||
categories[currentCategory].fieldsText = defaultPresets[currentCategory].join("\n");
|
||||
loadCategoryToUI(currentCategory);
|
||||
persistCategories();
|
||||
}
|
||||
|
||||
function clearCategoryFields() {
|
||||
categories[currentCategory].fieldsText = "";
|
||||
loadCategoryToUI(currentCategory);
|
||||
persistCategories();
|
||||
}
|
||||
|
||||
function updateNsRootInfo() {
|
||||
const ns = getCategoryNamespace(currentCategory);
|
||||
const root = getCategoryRoot(currentCategory);
|
||||
document.getElementById("nsRootInfo").innerHTML =
|
||||
`<strong>Namespace:</strong> <code>${ns}</code><br>` +
|
||||
`<strong>Root element:</strong> <code><${root}></code>`;
|
||||
}
|
||||
|
||||
// --- GENERARE XML PENTRU O CATEGORIE ---
|
||||
function generateCategory(cat) {
|
||||
const entry = categories[cat];
|
||||
if (!entry) return { xml: "", xpaths: "" };
|
||||
|
||||
const raw = (entry.fieldsText || "").split(/\r?\n/)
|
||||
.map(l => l.trim())
|
||||
.filter(l => l.length > 0);
|
||||
|
||||
if (raw.length === 0) {
|
||||
return { xml: "", xpaths: "" };
|
||||
}
|
||||
|
||||
const ns = getCategoryNamespace(cat);
|
||||
const root = getCategoryRoot(cat);
|
||||
const computeMetrics = document.getElementById("computeMetrics").checked;
|
||||
|
||||
const usedNames = new Set();
|
||||
const fields = []; // { label, baseName, variants: [] }
|
||||
|
||||
for (const label of raw) {
|
||||
const base = sanitizeName(label);
|
||||
if (!base) continue;
|
||||
|
||||
let baseName = base;
|
||||
let idx = 2;
|
||||
while (usedNames.has(baseName)) {
|
||||
baseName = base + "_" + idx;
|
||||
idx++;
|
||||
}
|
||||
usedNames.add(baseName);
|
||||
|
||||
const variants = [baseName];
|
||||
if (mode === "advanced") {
|
||||
const advCandidates = [
|
||||
baseName + "Short",
|
||||
baseName + "Upper",
|
||||
baseName + "Lower",
|
||||
baseName + "Initials",
|
||||
baseName + "First"
|
||||
];
|
||||
for (let v of advCandidates) {
|
||||
let vn = v;
|
||||
let k = 2;
|
||||
while (usedNames.has(vn)) {
|
||||
vn = v + "_" + k;
|
||||
k++;
|
||||
}
|
||||
usedNames.add(vn);
|
||||
variants.push(vn);
|
||||
}
|
||||
}
|
||||
|
||||
fields.push({ label, baseName, variants });
|
||||
}
|
||||
|
||||
// detectăm câmpuri pentru metrici (în special categoria Suprafete)
|
||||
const extraMetricFields = [];
|
||||
if (computeMetrics && cat.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) {
|
||||
if (!usedNames.has("POT")) {
|
||||
usedNames.add("POT");
|
||||
extraMetricFields.push({ label: "Procent Ocupare Teren", baseName: "POT", variants: ["POT"] });
|
||||
}
|
||||
}
|
||||
if (hasTeren && hasDesf) {
|
||||
if (!usedNames.has("CUT")) {
|
||||
usedNames.add("CUT");
|
||||
extraMetricFields.push({ label: "Coeficient Utilizare Teren", baseName: "CUT", variants: ["CUT"] });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// generăm XML
|
||||
let xml = '<?xml version="1.0" encoding="UTF-8"?>\n';
|
||||
xml += `<${root} xmlns="${ns}">\n`;
|
||||
|
||||
const allFieldEntries = fields.concat(extraMetricFields);
|
||||
|
||||
for (const f of allFieldEntries) {
|
||||
for (const v of f.variants) {
|
||||
xml += ` <${v}></${v}>\n`;
|
||||
}
|
||||
}
|
||||
|
||||
xml += `</${root}>\n`;
|
||||
|
||||
// generăm XPaths
|
||||
let xp = `Categorie: ${cat}\nNamespace: ${ns}\nRoot: /${root}\n\n`;
|
||||
for (const f of fields) {
|
||||
xp += `# ${f.label}\n`;
|
||||
for (const v of f.variants) {
|
||||
xp += `/${root}/${v}\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 };
|
||||
}
|
||||
|
||||
// --- GENERARE PENTRU TOATE CATEGORIILE ---
|
||||
function generateAll(showForCurrent = true) {
|
||||
saveCurrentCategoryFields();
|
||||
Object.keys(categories).forEach(cat => {
|
||||
const { xml, xpaths } = generateCategory(cat);
|
||||
xmlParts[cat] = xml;
|
||||
xpathParts[cat] = xpaths;
|
||||
});
|
||||
if (showForCurrent) {
|
||||
showPreview(currentCategory);
|
||||
}
|
||||
persistCategories();
|
||||
}
|
||||
|
||||
// --- PREVIEW ---
|
||||
function showPreview(cat) {
|
||||
document.getElementById("xmlPreview").textContent = xmlParts[cat] || "<!-- Niciun XML generat încă pentru această categorie. -->";
|
||||
document.getElementById("xpathPreview").textContent = xpathParts[cat] || "";
|
||||
}
|
||||
|
||||
// --- DOWNLOAD: XML CATEGORIE ---
|
||||
function downloadCurrentXml() {
|
||||
generateAll(false);
|
||||
const xml = xmlParts[currentCategory];
|
||||
if (!xml) {
|
||||
alert("Nu există XML generat pentru categoria curentă. Apasă întâi „Generează XML pentru toate categoriile”.");
|
||||
return;
|
||||
}
|
||||
const root = getCategoryRoot(currentCategory);
|
||||
const fileName = root + ".xml";
|
||||
const blob = new Blob([xml], { type: "application/xml" });
|
||||
const a = document.createElement("a");
|
||||
a.href = URL.createObjectURL(blob);
|
||||
a.download = fileName;
|
||||
a.click();
|
||||
URL.revokeObjectURL(a.href);
|
||||
}
|
||||
|
||||
// --- DOWNLOAD: ZIP CU TOATE XML-URILE ---
|
||||
async function downloadZipAll() {
|
||||
generateAll(false);
|
||||
const cats = Object.keys(categories);
|
||||
if (cats.length === 0) {
|
||||
alert("Nu există categorii.");
|
||||
return;
|
||||
}
|
||||
|
||||
const zip = new JSZip();
|
||||
const folder = zip.folder("customXmlParts");
|
||||
|
||||
let hasAny = false;
|
||||
for (const cat of cats) {
|
||||
const xml = xmlParts[cat];
|
||||
if (!xml) continue;
|
||||
hasAny = true;
|
||||
const root = getCategoryRoot(cat);
|
||||
const fileName = root + ".xml";
|
||||
folder.file(fileName, xml);
|
||||
}
|
||||
|
||||
if (!hasAny) {
|
||||
alert("Nu există XML generat încă. Apasă întâi „Generează XML pentru toate categoriile”.");
|
||||
return;
|
||||
}
|
||||
|
||||
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.click();
|
||||
URL.revokeObjectURL(a.href);
|
||||
}
|
||||
|
||||
// --- INIT ---
|
||||
window.addEventListener("DOMContentLoaded", () => {
|
||||
initCategories();
|
||||
updateNsRootInfo();
|
||||
generateAll();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
151
legacy/wordXMLgenerator/word-xml-generator-basic.html
Normal file
151
legacy/wordXMLgenerator/word-xml-generator-basic.html
Normal file
@@ -0,0 +1,151 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ro">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Generator XML Word – Versiune Extinsă</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<style>
|
||||
body {
|
||||
font-family: system-ui, sans-serif;
|
||||
padding: 1.5rem;
|
||||
background: #0f172a;
|
||||
color: #e5e7eb;
|
||||
}
|
||||
.card {
|
||||
background: #020617;
|
||||
border-radius: 1rem;
|
||||
padding: 1.25rem;
|
||||
box-shadow: 0 10px 30px rgba(0,0,0,0.3);
|
||||
border: 1px solid #1e293b;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
label { font-size: .85rem; color: #94a3b8; }
|
||||
input, textarea {
|
||||
width: 100%; padding: .55rem .7rem;
|
||||
border-radius: .5rem; border: 1px solid #334155;
|
||||
background: #020617; color: #e5e7eb;
|
||||
}
|
||||
textarea { min-height: 120px; }
|
||||
button {
|
||||
padding: .6rem 1.2rem; border-radius: 999px; border: none;
|
||||
background: linear-gradient(135deg,#38bdf8,#6366f1);
|
||||
font-weight: 600; color: white; cursor: pointer;
|
||||
}
|
||||
pre {
|
||||
background: #000; padding: .8rem; border-radius: .7rem;
|
||||
border: 1px solid #1e293b; max-height: 350px; overflow: auto;
|
||||
font-size: .85rem;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h1>Generator Word XML – Varianta Extinsă (cu Short / Upper / Lower / Initials)</h1>
|
||||
|
||||
<div class="card">
|
||||
<label>Namespace URI</label>
|
||||
<input id="nsUri" value="http://schemas.beletage.ro/word/contract">
|
||||
|
||||
<label style="margin-top:1rem;">Element rădăcină</label>
|
||||
<input id="rootElement" value="ContractData">
|
||||
|
||||
<label style="margin-top:1rem;">Lista de câmpuri (unul pe linie)</label>
|
||||
<textarea id="fieldList">NumeClient
|
||||
TitluProiect
|
||||
Adresa</textarea>
|
||||
|
||||
<button onclick="generateXML()" style="margin-top:1rem;">Generează XML complet</button>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h3>Custom XML Part (item1.xml)</h3>
|
||||
<pre id="xmlOutput"></pre>
|
||||
<button onclick="downloadXML()">Descarcă XML</button>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h3>XPaths pentru mapping</h3>
|
||||
<pre id="xpathOutput"></pre>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function sanitize(name) {
|
||||
if (!name) return null;
|
||||
let n = name.trim();
|
||||
if (!n) return null;
|
||||
n = n.replace(/\s+/g,"_").replace(/[^A-Za-z0-9_.-]/g,"");
|
||||
if (!/^[A-Za-z_]/.test(n)) n = "_" + n;
|
||||
return n;
|
||||
}
|
||||
|
||||
function initials(str) {
|
||||
return str.split(/\s+/).map(s => s[0]?.toUpperCase() + ".").join("");
|
||||
}
|
||||
|
||||
function generateXML() {
|
||||
const ns = document.getElementById("nsUri").value.trim();
|
||||
const root = sanitize(document.getElementById("rootElement").value) || "Root";
|
||||
const fieldRaw = document.getElementById("fieldList").value;
|
||||
|
||||
const lines = fieldRaw.split(/\r?\n/)
|
||||
.map(l => l.trim()).filter(l => l.length);
|
||||
|
||||
const fields = [];
|
||||
|
||||
for (let l of lines) {
|
||||
const base = sanitize(l);
|
||||
if (!base) continue;
|
||||
|
||||
fields.push({
|
||||
base,
|
||||
variants: [
|
||||
base, // original
|
||||
base + "Short", // prescurtat
|
||||
base + "Upper", // caps
|
||||
base + "Lower", // lowercase
|
||||
base + "Initials", // inițiale
|
||||
base + "First" // primul cuvânt
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
// === GENERĂM XML ===
|
||||
let xml = '<?xml version="1.0" encoding="UTF-8"?>\n';
|
||||
xml += `<${root} xmlns="${ns}">\n`;
|
||||
|
||||
for (const f of fields) {
|
||||
for (const v of f.variants) {
|
||||
xml += ` <${v}></${v}>\n`;
|
||||
}
|
||||
}
|
||||
|
||||
xml += `</${root}>`;
|
||||
|
||||
document.getElementById("xmlOutput").textContent = xml;
|
||||
|
||||
// === GENERĂM XPATHS ===
|
||||
let xp = `Namespace: ${ns}\nRoot: /${root}\n\n`;
|
||||
for (const f of fields) {
|
||||
xp += `# ${f.base}\n`;
|
||||
xp += `/${root}/${f.base}\n`;
|
||||
xp += `/${root}/${f.base}Short\n`;
|
||||
xp += `/${root}/${f.base}Upper\n`;
|
||||
xp += `/${root}/${f.base}Lower\n`;
|
||||
xp += `/${root}/${f.base}Initials\n`;
|
||||
xp += `/${root}/${f.base}First\n\n`;
|
||||
}
|
||||
document.getElementById("xpathOutput").textContent = xp;
|
||||
}
|
||||
|
||||
function downloadXML() {
|
||||
const text = document.getElementById("xmlOutput").textContent;
|
||||
const blob = new Blob([text], { type: "application/xml" });
|
||||
const a = document.createElement("a");
|
||||
a.href = URL.createObjectURL(blob);
|
||||
a.download = "item1.xml";
|
||||
a.click();
|
||||
}
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
330
legacy/wordXMLgenerator/word-xml-generator-medium.html
Normal file
330
legacy/wordXMLgenerator/word-xml-generator-medium.html
Normal file
@@ -0,0 +1,330 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ro">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Generator Word XML Custom Part</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<style>
|
||||
body {
|
||||
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||||
margin: 0;
|
||||
padding: 1.5rem;
|
||||
background: #0f172a;
|
||||
color: #e5e7eb;
|
||||
}
|
||||
h1 {
|
||||
font-size: 1.6rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
.container {
|
||||
max-width: 1100px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.card {
|
||||
background: #020617;
|
||||
border-radius: 1rem;
|
||||
padding: 1.25rem 1.5rem;
|
||||
box-shadow: 0 15px 40px rgba(0,0,0,0.35);
|
||||
border: 1px solid #1f2937;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
label {
|
||||
display: block;
|
||||
font-size: 0.85rem;
|
||||
color: #9ca3af;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
input, textarea {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
padding: 0.5rem 0.6rem;
|
||||
border-radius: 0.5rem;
|
||||
border: 1px solid #374151;
|
||||
background: #020617;
|
||||
color: #e5e7eb;
|
||||
font-family: inherit;
|
||||
font-size: 0.9rem;
|
||||
outline: none;
|
||||
}
|
||||
input:focus, textarea:focus {
|
||||
border-color: #38bdf8;
|
||||
box-shadow: 0 0 0 1px #38bdf8;
|
||||
}
|
||||
textarea {
|
||||
min-height: 140px;
|
||||
resize: vertical;
|
||||
}
|
||||
.row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 1rem;
|
||||
}
|
||||
.col-6 {
|
||||
flex: 1 1 260px;
|
||||
}
|
||||
button {
|
||||
padding: 0.6rem 1.2rem;
|
||||
border-radius: 999px;
|
||||
border: none;
|
||||
font-size: 0.9rem;
|
||||
cursor: pointer;
|
||||
margin-top: 0.75rem;
|
||||
background: linear-gradient(135deg, #38bdf8, #6366f1);
|
||||
color: white;
|
||||
font-weight: 600;
|
||||
box-shadow: 0 10px 25px rgba(37, 99, 235, 0.4);
|
||||
}
|
||||
button:hover {
|
||||
filter: brightness(1.05);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
button:active {
|
||||
transform: translateY(0);
|
||||
box-shadow: 0 6px 18px rgba(37,99,235,0.6);
|
||||
}
|
||||
pre {
|
||||
background: #020617;
|
||||
border-radius: 0.75rem;
|
||||
padding: 0.75rem 1rem;
|
||||
overflow: auto;
|
||||
font-size: 0.8rem;
|
||||
border: 1px solid #1f2937;
|
||||
max-height: 360px;
|
||||
}
|
||||
.subtitle {
|
||||
font-size: 0.85rem;
|
||||
color: #9ca3af;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.pill {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
font-size: 0.75rem;
|
||||
padding: 0.2rem 0.5rem;
|
||||
border-radius: 999px;
|
||||
background: rgba(148, 163, 184, 0.2);
|
||||
margin-right: 0.25rem;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
.pill span {
|
||||
opacity: 0.8;
|
||||
}
|
||||
.small {
|
||||
font-size: 0.8rem;
|
||||
color: #9ca3af;
|
||||
margin-top: 0.4rem;
|
||||
}
|
||||
.btn-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem;
|
||||
margin-top: 0.75rem;
|
||||
}
|
||||
.btn-secondary {
|
||||
background: transparent;
|
||||
border: 1px solid #4b5563;
|
||||
box-shadow: none;
|
||||
color: #e5e7eb;
|
||||
}
|
||||
.btn-secondary:hover {
|
||||
background: #111827;
|
||||
box-shadow: 0 8px 18px rgba(0,0,0,0.5);
|
||||
}
|
||||
@media (max-width: 640px) {
|
||||
body {
|
||||
padding: 1rem;
|
||||
}
|
||||
.card {
|
||||
padding: 1rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>Generator XML pentru Word Custom XML Part</h1>
|
||||
<p class="subtitle">
|
||||
Introdu câmpurile (unul pe linie) și obții XML pentru <strong>Custom XML Part</strong>, plus XPaths pentru mapping în Word.
|
||||
</p>
|
||||
|
||||
<div class="card">
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<label for="nsUri">Namespace URI (obligatoriu)</label>
|
||||
<input id="nsUri" type="text"
|
||||
value="http://schemas.beletage.ro/word/data">
|
||||
<div class="small">
|
||||
Exemplu: <code>http://schemas.firma-ta.ro/word/contract</code>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<label for="rootElement">Nume element rădăcină</label>
|
||||
<input id="rootElement" type="text" value="Root">
|
||||
<div class="small">
|
||||
Exemplu: <code>ContractData</code>, <code>ClientInfo</code> etc.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="margin-top:1rem;">
|
||||
<label for="fieldList">Lista de câmpuri (unul pe linie)</label>
|
||||
<textarea id="fieldList" placeholder="Exemplu:
|
||||
NumeClient
|
||||
Adresa
|
||||
DataContract
|
||||
ValoareTotala"></textarea>
|
||||
<div class="small">
|
||||
Numele va fi curățat automat pentru a fi valid ca nume de element XML
|
||||
(spațiile devin <code>_</code>, caracterele ciudate se elimină).
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="btn-row">
|
||||
<button type="button" onclick="generateXML()">Generează XML</button>
|
||||
<button type="button" class="btn-secondary" onclick="fillDemo()">Exemplu demo</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="pill"><strong>1</strong><span>Custom XML Part (item1.xml)</span></div>
|
||||
<pre id="xmlOutput"></pre>
|
||||
<div class="btn-row">
|
||||
<button type="button" class="btn-secondary" onclick="copyToClipboard('xmlOutput')">
|
||||
Copiază XML
|
||||
</button>
|
||||
<button type="button" class="btn-secondary" onclick="downloadXML()">
|
||||
Descarcă item1.xml
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="pill"><strong>2</strong><span>XPaths pentru mapping în Word</span></div>
|
||||
<pre id="xpathOutput"></pre>
|
||||
<button type="button" class="btn-secondary" onclick="copyToClipboard('xpathOutput')">
|
||||
Copiază XPaths
|
||||
</button>
|
||||
<p class="small">
|
||||
În Word → <strong>Developer</strong> → <strong>XML Mapping Pane</strong> → alegi Custom XML Part-ul
|
||||
→ pentru fiecare câmp, click dreapta → <em>Insert Content Control</em> → tipul dorit.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function sanitizeXmlName(name) {
|
||||
if (!name) return null;
|
||||
let n = name.trim();
|
||||
if (!n) return null;
|
||||
|
||||
// înlocuim spații cu underscore
|
||||
n = n.replace(/\s+/g, "_");
|
||||
|
||||
// eliminăm caractere invalide pentru nume de element XML
|
||||
n = n.replace(/[^A-Za-z0-9_.-]/g, "");
|
||||
|
||||
// numele XML nu are voie să înceapă cu cifră sau punct sau cratimă
|
||||
if (!/^[A-Za-z_]/.test(n)) {
|
||||
n = "_" + n;
|
||||
}
|
||||
|
||||
return n || null;
|
||||
}
|
||||
|
||||
function generateXML() {
|
||||
const nsUri = document.getElementById("nsUri").value.trim();
|
||||
const root = sanitizeXmlName(document.getElementById("rootElement").value) || "Root";
|
||||
const fieldRaw = document.getElementById("fieldList").value;
|
||||
const xmlOutput = document.getElementById("xmlOutput");
|
||||
const xpathOutput = document.getElementById("xpathOutput");
|
||||
|
||||
if (!nsUri) {
|
||||
alert("Te rog completează Namespace URI.");
|
||||
return;
|
||||
}
|
||||
|
||||
const lines = fieldRaw.split(/\r?\n/)
|
||||
.map(l => l.trim())
|
||||
.filter(l => l.length > 0);
|
||||
|
||||
const fields = [];
|
||||
const used = new Set();
|
||||
|
||||
for (let line of lines) {
|
||||
const clean = sanitizeXmlName(line);
|
||||
if (!clean) continue;
|
||||
let finalName = clean;
|
||||
let idx = 2;
|
||||
while (used.has(finalName)) {
|
||||
finalName = clean + "_" + idx;
|
||||
idx++;
|
||||
}
|
||||
used.add(finalName);
|
||||
fields.push({ original: line, xmlName: finalName });
|
||||
}
|
||||
|
||||
if (fields.length === 0) {
|
||||
xmlOutput.textContent = "<!-- Niciun câmp valid. Completează lista de câmpuri. -->";
|
||||
xpathOutput.textContent = "";
|
||||
return;
|
||||
}
|
||||
|
||||
// Generăm XML-ul pentru Custom XML Part
|
||||
let xml = '<?xml version="1.0" encoding="UTF-8"?>\n';
|
||||
xml += `<${root} xmlns="${nsUri}">\n`;
|
||||
for (const f of fields) {
|
||||
xml += ` <${f.xmlName}></${f.xmlName}>\n`;
|
||||
}
|
||||
xml += `</${root}>\n`;
|
||||
|
||||
xmlOutput.textContent = xml;
|
||||
|
||||
// Generăm lista de XPaths
|
||||
let xpaths = `Namespace: ${nsUri}\nRoot: /${root}\n\nCâmpuri:\n`;
|
||||
for (const f of fields) {
|
||||
xpaths += `- ${f.original} => /${root}/${f.xmlName}\n`;
|
||||
}
|
||||
xpathOutput.textContent = xpaths;
|
||||
}
|
||||
|
||||
function copyToClipboard(elementId) {
|
||||
const el = document.getElementById(elementId);
|
||||
if (!el || !el.textContent) return;
|
||||
navigator.clipboard.writeText(el.textContent)
|
||||
.then(() => alert("Copiat în clipboard."))
|
||||
.catch(() => alert("Nu am reușit să copiez în clipboard."));
|
||||
}
|
||||
|
||||
function downloadXML() {
|
||||
const xmlText = document.getElementById("xmlOutput").textContent;
|
||||
if (!xmlText || xmlText.startsWith("<!--")) {
|
||||
alert("Nu există XML valid de descărcat.");
|
||||
return;
|
||||
}
|
||||
const blob = new Blob([xmlText], { type: "application/xml" });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement("a");
|
||||
a.href = url;
|
||||
a.download = "item1.xml";
|
||||
a.click();
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
|
||||
function fillDemo() {
|
||||
document.getElementById("nsUri").value = "http://schemas.beletage.ro/word/contract";
|
||||
document.getElementById("rootElement").value = "ContractData";
|
||||
document.getElementById("fieldList").value = [
|
||||
"NumeClient",
|
||||
"AdresaClient",
|
||||
"Proiect",
|
||||
"DataContract",
|
||||
"ValoareTotala",
|
||||
"Moneda",
|
||||
"TermenExecutie"
|
||||
].join("\n");
|
||||
generateXML();
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user