backoffice first commit

This commit is contained in:
2026-07-17 11:34:15 +02:00
parent 761db06329
commit 1c40ef6601
159 changed files with 25830 additions and 7875 deletions
File diff suppressed because one or more lines are too long
+1
View File
@@ -0,0 +1 @@
:root{--brand: #1a4b8c;--brand-dark: #143a6b;--bg: #f5f6f8;--card: #ffffff;--border: #e4e7ec;--text: #1f2937;--muted: #6b7280;--danger: #c0392b;--ok: #1e874b}*{box-sizing:border-box}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif;background:var(--bg);color:var(--text)}.app-header{background:var(--brand);color:#fff;padding:18px 20px}.app-header h1{margin:0;font-size:18px;font-weight:600}.container{max-width:780px;margin:0 auto;padding:20px 16px 60px}.card{background:var(--card);border:1px solid var(--border);border-radius:10px;padding:16px;margin-bottom:14px}.item{display:flex;gap:14px}.item img{width:90px;height:90px;object-fit:contain;background:#fafafa;border:1px solid var(--border);border-radius:8px;flex:none}.item-body{flex:1;min-width:0}.item-title{font-weight:600;margin:0 0 2px}.item-sub{color:var(--muted);font-size:13px;margin-bottom:6px}.conf{font-size:13px;color:#374151;line-height:1.5}.conf b{color:var(--muted);font-weight:500}.item-foot{display:flex;align-items:center;justify-content:space-between;margin-top:8px}.price{font-weight:700}.totale-row{display:flex;justify-content:space-between;align-items:baseline;font-size:20px;font-weight:700;color:var(--brand);padding-top:8px}.iva-nota{color:var(--muted);font-size:12px;font-weight:400}button{font:inherit;cursor:pointer;border:none;border-radius:8px;padding:12px 18px}.btn-primary{background:var(--brand);color:#fff;font-weight:600;width:100%}.btn-primary:hover{background:var(--brand-dark)}.btn-primary:disabled{opacity:.6;cursor:not-allowed}.btn-link{background:none;color:var(--muted);padding:6px;text-decoration:underline}.btn-remove{background:none;color:var(--danger);padding:4px 8px;font-size:13px}.field{margin-bottom:12px}.field label{display:block;font-size:13px;color:var(--muted);margin-bottom:4px}.field input,.field textarea{width:100%;padding:10px 12px;border:1px solid var(--border);border-radius:8px;font:inherit}.field .err{color:var(--danger);font-size:12px;margin-top:3px}.grid-2{display:grid;grid-template-columns:1fr 1fr;gap:12px}@media(max-width:520px){.grid-2{grid-template-columns:1fr}}.muted{color:var(--muted)}.center{text-align:center}.empty{text-align:center;padding:40px 20px;color:var(--muted)}.success-icon{font-size:48px}.alert{background:#fdecea;color:var(--danger);padding:10px 12px;border-radius:8px;margin-bottom:12px;font-size:14px}
+13
View File
@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="it">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Il tuo preventivo · Infissionline</title>
<script type="module" crossorigin src="/assets/index-BidQ3v5l.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-DNK0MLY8.css">
</head>
<body>
<div id="app"></div>
</body>
</html>
+13
View File
@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="it">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="icon" type="image/jpeg" href="/kreios-logo.jpg" />
<title>Il tuo preventivo · Krèios</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>
+18
View File
@@ -0,0 +1,18 @@
{
"name": "kreios-cliente",
"version": "0.1.0",
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"vue": "^3.5.13"
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.2.1",
"vite": "^6.0.7"
}
}
+758
View File
@@ -0,0 +1,758 @@
lockfileVersion: '6.0'
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
dependencies:
vue:
specifier: ^3.5.13
version: 3.5.40
devDependencies:
'@vitejs/plugin-vue':
specifier: ^5.2.1
version: 5.2.4(vite@6.4.3)(vue@3.5.40)
vite:
specifier: ^6.0.7
version: 6.4.3
packages:
/@babel/helper-string-parser@7.29.7:
resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==}
engines: {node: '>=6.9.0'}
/@babel/helper-validator-identifier@7.29.7:
resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==}
engines: {node: '>=6.9.0'}
/@babel/parser@7.29.7:
resolution: {integrity: sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==}
engines: {node: '>=6.0.0'}
hasBin: true
dependencies:
'@babel/types': 7.29.7
/@babel/types@7.29.7:
resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/helper-string-parser': 7.29.7
'@babel/helper-validator-identifier': 7.29.7
/@esbuild/aix-ppc64@0.25.12:
resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==}
engines: {node: '>=18'}
cpu: [ppc64]
os: [aix]
requiresBuild: true
dev: true
optional: true
/@esbuild/android-arm64@0.25.12:
resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==}
engines: {node: '>=18'}
cpu: [arm64]
os: [android]
requiresBuild: true
dev: true
optional: true
/@esbuild/android-arm@0.25.12:
resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==}
engines: {node: '>=18'}
cpu: [arm]
os: [android]
requiresBuild: true
dev: true
optional: true
/@esbuild/android-x64@0.25.12:
resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==}
engines: {node: '>=18'}
cpu: [x64]
os: [android]
requiresBuild: true
dev: true
optional: true
/@esbuild/darwin-arm64@0.25.12:
resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==}
engines: {node: '>=18'}
cpu: [arm64]
os: [darwin]
requiresBuild: true
dev: true
optional: true
/@esbuild/darwin-x64@0.25.12:
resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==}
engines: {node: '>=18'}
cpu: [x64]
os: [darwin]
requiresBuild: true
dev: true
optional: true
/@esbuild/freebsd-arm64@0.25.12:
resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==}
engines: {node: '>=18'}
cpu: [arm64]
os: [freebsd]
requiresBuild: true
dev: true
optional: true
/@esbuild/freebsd-x64@0.25.12:
resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==}
engines: {node: '>=18'}
cpu: [x64]
os: [freebsd]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-arm64@0.25.12:
resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==}
engines: {node: '>=18'}
cpu: [arm64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-arm@0.25.12:
resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==}
engines: {node: '>=18'}
cpu: [arm]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-ia32@0.25.12:
resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==}
engines: {node: '>=18'}
cpu: [ia32]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-loong64@0.25.12:
resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==}
engines: {node: '>=18'}
cpu: [loong64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-mips64el@0.25.12:
resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==}
engines: {node: '>=18'}
cpu: [mips64el]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-ppc64@0.25.12:
resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==}
engines: {node: '>=18'}
cpu: [ppc64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-riscv64@0.25.12:
resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==}
engines: {node: '>=18'}
cpu: [riscv64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-s390x@0.25.12:
resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==}
engines: {node: '>=18'}
cpu: [s390x]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-x64@0.25.12:
resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==}
engines: {node: '>=18'}
cpu: [x64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/netbsd-arm64@0.25.12:
resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==}
engines: {node: '>=18'}
cpu: [arm64]
os: [netbsd]
requiresBuild: true
dev: true
optional: true
/@esbuild/netbsd-x64@0.25.12:
resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==}
engines: {node: '>=18'}
cpu: [x64]
os: [netbsd]
requiresBuild: true
dev: true
optional: true
/@esbuild/openbsd-arm64@0.25.12:
resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==}
engines: {node: '>=18'}
cpu: [arm64]
os: [openbsd]
requiresBuild: true
dev: true
optional: true
/@esbuild/openbsd-x64@0.25.12:
resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==}
engines: {node: '>=18'}
cpu: [x64]
os: [openbsd]
requiresBuild: true
dev: true
optional: true
/@esbuild/openharmony-arm64@0.25.12:
resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==}
engines: {node: '>=18'}
cpu: [arm64]
os: [openharmony]
requiresBuild: true
dev: true
optional: true
/@esbuild/sunos-x64@0.25.12:
resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==}
engines: {node: '>=18'}
cpu: [x64]
os: [sunos]
requiresBuild: true
dev: true
optional: true
/@esbuild/win32-arm64@0.25.12:
resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==}
engines: {node: '>=18'}
cpu: [arm64]
os: [win32]
requiresBuild: true
dev: true
optional: true
/@esbuild/win32-ia32@0.25.12:
resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==}
engines: {node: '>=18'}
cpu: [ia32]
os: [win32]
requiresBuild: true
dev: true
optional: true
/@esbuild/win32-x64@0.25.12:
resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==}
engines: {node: '>=18'}
cpu: [x64]
os: [win32]
requiresBuild: true
dev: true
optional: true
/@jridgewell/sourcemap-codec@1.5.5:
resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
/@rollup/rollup-android-arm-eabi@4.62.2:
resolution: {integrity: sha512-6o7ZLZK+BeenkZCFNDXqpbjw9bD6nuWonvS/lwQJp7NoVVxm6p3qE7qQ5jGuBjiFsgvqjD8mZAU5oWxTmbOeOg==}
cpu: [arm]
os: [android]
requiresBuild: true
dev: true
optional: true
/@rollup/rollup-android-arm64@4.62.2:
resolution: {integrity: sha512-BaH7BllCACHoH1LguOU56UItGfUWjujlO65kS9LAodViaN4bwIKd7oeW/ZHJ/4ljr/7MIiENnNy3HJ0zXv8Zkw==}
cpu: [arm64]
os: [android]
requiresBuild: true
dev: true
optional: true
/@rollup/rollup-darwin-arm64@4.62.2:
resolution: {integrity: sha512-v39RCCvj4He82I9sFmk+M1VZ0PLM9sfsLVikjfx2hYBNALhrrOR2D3JjQA6AhlaSOgcR+RzrKY7e1+bT6SUO/A==}
cpu: [arm64]
os: [darwin]
requiresBuild: true
dev: true
optional: true
/@rollup/rollup-darwin-x64@4.62.2:
resolution: {integrity: sha512-yl0y2vq3S3lHeuXhEdss6TWfKW8vkujImO12tn4ZkG/4oghr09LvdYm2RElVjokTQiUvDUGXLGsYeLqUMCKpGA==}
cpu: [x64]
os: [darwin]
requiresBuild: true
dev: true
optional: true
/@rollup/rollup-freebsd-arm64@4.62.2:
resolution: {integrity: sha512-tT4pvt4qXD+vEoezupCWi+a1F0vvDiksiHc+PxRlYTOH1I6/X4id9jPxTP+Fg+545euaFT1jJVs4CEdHZAU1vw==}
cpu: [arm64]
os: [freebsd]
requiresBuild: true
dev: true
optional: true
/@rollup/rollup-freebsd-x64@4.62.2:
resolution: {integrity: sha512-6nU5F2wCW+qvCBhTn1pdIU3bzsIoF7EUwsCDRxilWGprQR6yd508YnH9+OKFCwpfS8pjZqDUmnCAr7exax0XCg==}
cpu: [x64]
os: [freebsd]
requiresBuild: true
dev: true
optional: true
/@rollup/rollup-linux-arm-gnueabihf@4.62.2:
resolution: {integrity: sha512-n1GJHPOvpIfhi3TmrCeh6S6URt9BFCt0KQE3qvexyGCTAKpR4Lg+eWvNZEqu7epxwus/8ElT3hacYEucm49SZg==}
cpu: [arm]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@rollup/rollup-linux-arm-musleabihf@4.62.2:
resolution: {integrity: sha512-JqgflS8wEB+UXV/vS1RpRbifGBeN4D5lz8D8oOFbFZw4vedvdOgCFAjfBmIMdW3yL10XpQQ0Ambepw6MXrhOnA==}
cpu: [arm]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@rollup/rollup-linux-arm64-gnu@4.62.2:
resolution: {integrity: sha512-wnFJkogWvN4jm/hQRF2UBaeUmk20j5+DmHvoyWii2b8HJDyvz1MF2OU/6ynXt2KR63rbZLWkFpoytpdc/yBuSA==}
cpu: [arm64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@rollup/rollup-linux-arm64-musl@4.62.2:
resolution: {integrity: sha512-HVu2bp0zhvJ8xHEV9+UUs7S90VadmBSY3LcIMvozbPo4AuMGDWlz3ymHLHZPX4hR67TKTt8Qp5PJ5RBg/i+RMQ==}
cpu: [arm64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@rollup/rollup-linux-loong64-gnu@4.62.2:
resolution: {integrity: sha512-mQqqAV8QaoSgr9I2fKDLY2BAVvmKjWoGiu/cSYQonsLvtqwEn1E4QYfnCOcp5zoEqNhsDYin1s6jx/VJmrxlZg==}
cpu: [loong64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@rollup/rollup-linux-loong64-musl@4.62.2:
resolution: {integrity: sha512-IxKLoxCQ2IWi6bT2akyDUBGsOImDKB+sPp4EsTmwFQ/fMwpCKm8uLSSgP/Kx/QYUgKis6SEZ5/Nlhup0DIA0PQ==}
cpu: [loong64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@rollup/rollup-linux-ppc64-gnu@4.62.2:
resolution: {integrity: sha512-Mk5ha2RQSgyFfmYYLkBpPnUk8D8FriBxesO1u9O75X0mHgXL1UQcH5Itl2lurWL2tj0RxV9b9tJgipac0hRY9A==}
cpu: [ppc64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@rollup/rollup-linux-ppc64-musl@4.62.2:
resolution: {integrity: sha512-CjvEnqJL/0/TQ3TXX3OPIJ/kmBellrWd4heXUmHeJlTnmwjKpSJzoehLaL6Xk0ZnMHBu9dZuFADNOrtjF4v+2w==}
cpu: [ppc64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@rollup/rollup-linux-riscv64-gnu@4.62.2:
resolution: {integrity: sha512-1SiZbzwdkaDURsew/tSOrooKiYy7EQGT6m8ufavAi9NEyQb/6VuIxFXAL1fqa4iZe3g4NbNk4P7J32z2tw5Mgg==}
cpu: [riscv64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@rollup/rollup-linux-riscv64-musl@4.62.2:
resolution: {integrity: sha512-nQts12zJ3NQRoE6uYljOH89v7szzLDvG2JD/vsX+vGXU8w/At1GowTZ5/7qeFQ8m7L55rpR8Okugnuo5bgjy2Q==}
cpu: [riscv64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@rollup/rollup-linux-s390x-gnu@4.62.2:
resolution: {integrity: sha512-E9/ll019jhPIJgpzfZoIkBGhcz+kKNgVWYRY0zr9srBdPPFVpvOKW8VaJKUbeK+eZXyQF9ltME+Kk6affeaPgg==}
cpu: [s390x]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@rollup/rollup-linux-x64-gnu@4.62.2:
resolution: {integrity: sha512-5BqxR/pshjey51iliyzTD5Xi3EN0aLmQ2lZ3lvefVV9c82BvrLo2/6OT55iifpWBufs6kdwWbuOKS841DrmK9A==}
cpu: [x64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@rollup/rollup-linux-x64-musl@4.62.2:
resolution: {integrity: sha512-uNN83XxQrRAh/w0/pmAfibcwyb6YWt4gP+dpnQKPVJshAloQ785ii8CT8ZCIxkGg9opVsvAlGhFitSm6D1Jjpg==}
cpu: [x64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@rollup/rollup-openbsd-x64@4.62.2:
resolution: {integrity: sha512-srjEIxSH3LRnJN6THczDHWQplqEMFiAJrTab0msUryh9kwNpkICf3Ea6q6MN/2cZwRFUNx5w+h6Hpi4QuHS6Zg==}
cpu: [x64]
os: [openbsd]
requiresBuild: true
dev: true
optional: true
/@rollup/rollup-openharmony-arm64@4.62.2:
resolution: {integrity: sha512-8hOJnxgbyObnCm5AlRA3A931xX19xq80RjVTKgJOvEKWqJruP/Uf12IbAOaDjjEXYRewwHLfmF0YRIdK3OwKWA==}
cpu: [arm64]
os: [openharmony]
requiresBuild: true
dev: true
optional: true
/@rollup/rollup-win32-arm64-msvc@4.62.2:
resolution: {integrity: sha512-mmF4AY1i0hG/bLWUctUq59gtmgaSIRa3cu/A3JFRp/sCNEme2bgDEiDS22P9FbnJB8NJNF4jPJiSP5RHQpUTDg==}
cpu: [arm64]
os: [win32]
requiresBuild: true
dev: true
optional: true
/@rollup/rollup-win32-ia32-msvc@4.62.2:
resolution: {integrity: sha512-DZgkknc6jhHrk46V25vbAM0zZkyP0nSDkJB8/dRkLTxv470dOmWDqGoEJl/9A0dFfS7yE3REOwNDxpHwSLSt0Q==}
cpu: [ia32]
os: [win32]
requiresBuild: true
dev: true
optional: true
/@rollup/rollup-win32-x64-gnu@4.62.2:
resolution: {integrity: sha512-T6xr6ucWSFto+VGajA8YH26LdpHRuP4YLHEKAtCWvJDOlnmWcDZVCI2Jmjr+IFHDlt2zRaTAKE4tfjTaWLgJBg==}
cpu: [x64]
os: [win32]
requiresBuild: true
dev: true
optional: true
/@rollup/rollup-win32-x64-msvc@4.62.2:
resolution: {integrity: sha512-BfzEnDJOt9T8M989/lA37EcJgat01wLRnoi5dQf3QzOH7jzpqTAzdDbVfRljVr5r+jzKqpbHeyOfAaXxAd0PAA==}
cpu: [x64]
os: [win32]
requiresBuild: true
dev: true
optional: true
/@types/estree@1.0.9:
resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==}
dev: true
/@vitejs/plugin-vue@5.2.4(vite@6.4.3)(vue@3.5.40):
resolution: {integrity: sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==}
engines: {node: ^18.0.0 || >=20.0.0}
peerDependencies:
vite: ^5.0.0 || ^6.0.0
vue: ^3.2.25
dependencies:
vite: 6.4.3
vue: 3.5.40
dev: true
/@vue/compiler-core@3.5.40:
resolution: {integrity: sha512-39E8IgOhTbVDnoJFMKc2DvYnypcZwUqgUhQkccva/0m6FUwtIKSGV7n1hpVmYcFaoRAwf9pBcwnKlCEsN63ZEQ==}
dependencies:
'@babel/parser': 7.29.7
'@vue/shared': 3.5.40
entities: 7.0.1
estree-walker: 2.0.2
source-map-js: 1.2.1
/@vue/compiler-dom@3.5.40:
resolution: {integrity: sha512-pwkx4vqlqOspFstrcmzwkKLePVMD3PT65imRzLhanU2V1Fj4K13g6OXjanOyzw3aTAuRk84BOmY8f3rEHqPaVA==}
dependencies:
'@vue/compiler-core': 3.5.40
'@vue/shared': 3.5.40
/@vue/compiler-sfc@3.5.40:
resolution: {integrity: sha512-gIf497P4kpuALcvs5n3AEg1Vdn0pSY4XbjASIfHNYF1/MP3T2Mf2STERTubysBxCRxzJGJYtF/O7vwJrxFB3Vw==}
dependencies:
'@babel/parser': 7.29.7
'@vue/compiler-core': 3.5.40
'@vue/compiler-dom': 3.5.40
'@vue/compiler-ssr': 3.5.40
'@vue/shared': 3.5.40
estree-walker: 2.0.2
magic-string: 0.30.21
postcss: 8.5.19
source-map-js: 1.2.1
/@vue/compiler-ssr@3.5.40:
resolution: {integrity: sha512-rrE5xiXG663+vHCHa3J9p2z5OcBRjXmoqenprJxAFQxg5pSshzeBiCE6pu46axapRJ2Adk0YDA2BRZVjiHXnhg==}
dependencies:
'@vue/compiler-dom': 3.5.40
'@vue/shared': 3.5.40
/@vue/reactivity@3.5.40:
resolution: {integrity: sha512-B7ot9UlUZOi1zbq61/LvE88ZLTV8IlajTdiZTAEiDQgrnIMIZoPr9kGw0Zw46ObW62O9+H/Be3kMbfb7kYPQZA==}
dependencies:
'@vue/shared': 3.5.40
/@vue/runtime-core@3.5.40:
resolution: {integrity: sha512-KAZLweuZ6uUJPK1PMSQPgBU5gCjgrrfjUhSglmU9NhH+Zjepa8cnwSydPWDWHDwOgY4g3VcZ+PljbiHlURNCbw==}
dependencies:
'@vue/reactivity': 3.5.40
'@vue/shared': 3.5.40
/@vue/runtime-dom@3.5.40:
resolution: {integrity: sha512-ZfrX8ssZQds900L9pr8AuK05ddnMsR4MPMZr8cPN9GoqoPWcXLhjvvbIA2SMv+7a97sJ1vv9pj/zxK0Cq/eEFQ==}
dependencies:
'@vue/reactivity': 3.5.40
'@vue/runtime-core': 3.5.40
'@vue/shared': 3.5.40
csstype: 3.2.3
/@vue/server-renderer@3.5.40:
resolution: {integrity: sha512-XNJym9WpevhTVt1HuwOrCRJ5Q+9z4BjTMrDtjTrvx74SmUll8spNTw6whWJa9mEkO4PKn5TihI/bm/8ds2QVJw==}
dependencies:
'@vue/compiler-ssr': 3.5.40
'@vue/runtime-dom': 3.5.40
'@vue/shared': 3.5.40
/@vue/shared@3.5.40:
resolution: {integrity: sha512-WxnBtruIqOoV3rA4jeKDWzrYI5h7Cp4+pjwDi8kWGHz+IslhiN+wguLVVhtv2l8VoU02rzDCVfDjgCl1lNpZVg==}
/csstype@3.2.3:
resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==}
/entities@7.0.1:
resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==}
engines: {node: '>=0.12'}
/esbuild@0.25.12:
resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==}
engines: {node: '>=18'}
hasBin: true
requiresBuild: true
optionalDependencies:
'@esbuild/aix-ppc64': 0.25.12
'@esbuild/android-arm': 0.25.12
'@esbuild/android-arm64': 0.25.12
'@esbuild/android-x64': 0.25.12
'@esbuild/darwin-arm64': 0.25.12
'@esbuild/darwin-x64': 0.25.12
'@esbuild/freebsd-arm64': 0.25.12
'@esbuild/freebsd-x64': 0.25.12
'@esbuild/linux-arm': 0.25.12
'@esbuild/linux-arm64': 0.25.12
'@esbuild/linux-ia32': 0.25.12
'@esbuild/linux-loong64': 0.25.12
'@esbuild/linux-mips64el': 0.25.12
'@esbuild/linux-ppc64': 0.25.12
'@esbuild/linux-riscv64': 0.25.12
'@esbuild/linux-s390x': 0.25.12
'@esbuild/linux-x64': 0.25.12
'@esbuild/netbsd-arm64': 0.25.12
'@esbuild/netbsd-x64': 0.25.12
'@esbuild/openbsd-arm64': 0.25.12
'@esbuild/openbsd-x64': 0.25.12
'@esbuild/openharmony-arm64': 0.25.12
'@esbuild/sunos-x64': 0.25.12
'@esbuild/win32-arm64': 0.25.12
'@esbuild/win32-ia32': 0.25.12
'@esbuild/win32-x64': 0.25.12
dev: true
/estree-walker@2.0.2:
resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
/fdir@6.5.0(picomatch@4.0.5):
resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==}
engines: {node: '>=12.0.0'}
peerDependencies:
picomatch: ^3 || ^4
peerDependenciesMeta:
picomatch:
optional: true
dependencies:
picomatch: 4.0.5
dev: true
/fsevents@2.3.3:
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
os: [darwin]
requiresBuild: true
dev: true
optional: true
/magic-string@0.30.21:
resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}
dependencies:
'@jridgewell/sourcemap-codec': 1.5.5
/nanoid@3.3.16:
resolution: {integrity: sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==}
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
hasBin: true
/picocolors@1.1.1:
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
/picomatch@4.0.5:
resolution: {integrity: sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==}
engines: {node: '>=12'}
dev: true
/postcss@8.5.19:
resolution: {integrity: sha512-Mz8SaolMd8nB+G13WkORcxQKHZ/NE4xXevtkJHVuG+guo9/wYKlIMTKAqGdEmYOXR2ijPjTYNHssizdaVSUNdQ==}
engines: {node: ^10 || ^12 || >=14}
dependencies:
nanoid: 3.3.16
picocolors: 1.1.1
source-map-js: 1.2.1
/rollup@4.62.2:
resolution: {integrity: sha512-RFnrW4lhXA3s3eqHDZvN654g8OTjzRfqpIRJYczCGB6HzphckVAi/Qh4tbPUbRuDi7s1Llv8g/NspLkttY3gTA==}
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
hasBin: true
dependencies:
'@types/estree': 1.0.9
optionalDependencies:
'@rollup/rollup-android-arm-eabi': 4.62.2
'@rollup/rollup-android-arm64': 4.62.2
'@rollup/rollup-darwin-arm64': 4.62.2
'@rollup/rollup-darwin-x64': 4.62.2
'@rollup/rollup-freebsd-arm64': 4.62.2
'@rollup/rollup-freebsd-x64': 4.62.2
'@rollup/rollup-linux-arm-gnueabihf': 4.62.2
'@rollup/rollup-linux-arm-musleabihf': 4.62.2
'@rollup/rollup-linux-arm64-gnu': 4.62.2
'@rollup/rollup-linux-arm64-musl': 4.62.2
'@rollup/rollup-linux-loong64-gnu': 4.62.2
'@rollup/rollup-linux-loong64-musl': 4.62.2
'@rollup/rollup-linux-ppc64-gnu': 4.62.2
'@rollup/rollup-linux-ppc64-musl': 4.62.2
'@rollup/rollup-linux-riscv64-gnu': 4.62.2
'@rollup/rollup-linux-riscv64-musl': 4.62.2
'@rollup/rollup-linux-s390x-gnu': 4.62.2
'@rollup/rollup-linux-x64-gnu': 4.62.2
'@rollup/rollup-linux-x64-musl': 4.62.2
'@rollup/rollup-openbsd-x64': 4.62.2
'@rollup/rollup-openharmony-arm64': 4.62.2
'@rollup/rollup-win32-arm64-msvc': 4.62.2
'@rollup/rollup-win32-ia32-msvc': 4.62.2
'@rollup/rollup-win32-x64-gnu': 4.62.2
'@rollup/rollup-win32-x64-msvc': 4.62.2
fsevents: 2.3.3
dev: true
/source-map-js@1.2.1:
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
engines: {node: '>=0.10.0'}
/tinyglobby@0.2.17:
resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==}
engines: {node: '>=12.0.0'}
dependencies:
fdir: 6.5.0(picomatch@4.0.5)
picomatch: 4.0.5
dev: true
/vite@6.4.3:
resolution: {integrity: sha512-NTKlcQjlAK7MlQoyb6LgaqHc8sso/pVyUJYWMws3jg21uTJw/LddqIFPcPqP6PzpgbIcZyKI85sFE4HBrQDA8A==}
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
hasBin: true
peerDependencies:
'@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0
jiti: '>=1.21.0'
less: '*'
lightningcss: ^1.21.0
sass: '*'
sass-embedded: '*'
stylus: '*'
sugarss: '*'
terser: ^5.16.0
tsx: ^4.8.1
yaml: ^2.4.2
peerDependenciesMeta:
'@types/node':
optional: true
jiti:
optional: true
less:
optional: true
lightningcss:
optional: true
sass:
optional: true
sass-embedded:
optional: true
stylus:
optional: true
sugarss:
optional: true
terser:
optional: true
tsx:
optional: true
yaml:
optional: true
dependencies:
esbuild: 0.25.12
fdir: 6.5.0(picomatch@4.0.5)
picomatch: 4.0.5
postcss: 8.5.19
rollup: 4.62.2
tinyglobby: 0.2.17
optionalDependencies:
fsevents: 2.3.3
dev: true
/vue@3.5.40:
resolution: {integrity: sha512-+8PJ4SJXdn/cHGImF4CKdxlWHIN5Dkt7DoufRREM6h6uVCx2m7QxgcEQmmzyOK8A9mcafg7sFbJFYsdFVubTig==}
peerDependencies:
typescript: '*'
peerDependenciesMeta:
typescript:
optional: true
dependencies:
'@vue/compiler-dom': 3.5.40
'@vue/compiler-sfc': 3.5.40
'@vue/runtime-dom': 3.5.40
'@vue/server-renderer': 3.5.40
'@vue/shared': 3.5.40
Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

+222
View File
@@ -0,0 +1,222 @@
<script setup>
import { ref, reactive, computed, onMounted } from 'vue'
import { api, resolveToken, resolveReturnUrl, clearToken, formatEuro } from './api.js'
const step = ref('cart') // cart | checkout | done
const loading = ref(true)
const submitting = ref(false)
const errorMsg = ref('')
const fieldErrors = ref({})
const token = ref(null)
const carrello = reactive({ items: [], totale: 0 })
const numeroConfermato = ref(null)
const returnUrl = ref(null)
const form = reactive({
nome: '', cognome: '', ragione_sociale: '', email: '', telefono: '',
indirizzo: '', cap: '', citta: '', provincia: '', note: '',
})
const isEmpty = computed(() => !carrello.items || carrello.items.length === 0)
async function loadCarrello() {
loading.value = true
errorMsg.value = ''
try {
token.value = resolveToken()
returnUrl.value = resolveReturnUrl()
if (!token.value) {
carrello.items = []
carrello.totale = 0
return
}
const data = await api.getCarrello(token.value)
carrello.items = data.carrello.items
carrello.totale = data.carrello.totale
} catch (e) {
errorMsg.value = e.message
} finally {
loading.value = false
}
}
async function rimuovi(itemId) {
try {
const data = await api.rimuoviItem(token.value, itemId)
carrello.items = data.carrello.items
carrello.totale = data.carrello.totale
} catch (e) {
errorMsg.value = e.message
}
}
async function conferma() {
submitting.value = true
errorMsg.value = ''
fieldErrors.value = {}
try {
const data = await api.conferma(token.value, { ...form })
numeroConfermato.value = data.numero
clearToken()
step.value = 'done'
} catch (e) {
if (e.status === 422 && e.errors) {
fieldErrors.value = e.errors
errorMsg.value = 'Controlla i campi evidenziati.'
} else {
errorMsg.value = e.message
}
} finally {
submitting.value = false
}
}
onMounted(loadCarrello)
</script>
<template>
<header class="app-header">
<img src="/kreios-logo.jpg" alt="Krèios" />
<h1>Il tuo preventivo</h1>
</header>
<div class="container">
<p v-if="loading" class="muted center">Caricamento</p>
<div v-else-if="errorMsg && step !== 'checkout'" class="alert">{{ errorMsg }}</div>
<!-- STEP CARRELLO -->
<template v-if="!loading && step === 'cart'">
<div v-if="isEmpty" class="empty">
<p>Il tuo preventivo è vuoto.</p>
<p class="muted">Configura un prodotto e aggiungilo al preventivo per vederlo qui.</p>
</div>
<template v-else>
<div v-for="item in carrello.items" :key="item.id" class="card item">
<img v-if="item.immagine_url" :src="item.immagine_url" alt="Anteprima" />
<div class="item-body">
<p class="item-title">{{ item.nome_modello || 'Prodotto' }}</p>
<p class="item-sub">
<span v-if="item.sistema">{{ item.sistema }}</span>
<span v-if="item.larghezza && item.altezza"> · {{ item.larghezza }}×{{ item.altezza }} cm</span>
</p>
<div class="conf">
<div v-for="row in item.configurazione" :key="row.label">
<b>{{ row.label }}:</b> {{ row.value }}
</div>
</div>
<div class="item-foot">
<button class="btn-remove" @click="rimuovi(item.id)">Rimuovi</button>
<span class="price">
{{ item.quantita }} × {{ formatEuro(item.prezzo_unitario) }} =
{{ formatEuro(item.prezzo_totale) }}
</span>
</div>
</div>
</div>
<div class="card">
<div class="totale-row">
<span>Totale <span class="iva-nota">IVA 22% inclusa</span></span>
<span>{{ formatEuro(carrello.totale) }}</span>
</div>
</div>
<button class="btn-primary" @click="step = 'checkout'">
Richiedi il preventivo
</button>
<a v-if="returnUrl" class="btn-link" style="width:100%;margin-top:8px" :href="returnUrl">
Torna al configuratore
</a>
</template>
</template>
<!-- STEP CHECKOUT -->
<template v-else-if="!loading && step === 'checkout'">
<div class="card">
<p class="muted" style="margin-top:0">
Inserisci i tuoi dati: ti invieremo il preventivo via email e ti ricontatteremo.
</p>
<div v-if="errorMsg" class="alert">{{ errorMsg }}</div>
<div class="grid-2">
<div class="field">
<label>Nome *</label>
<input v-model="form.nome" />
<div v-if="fieldErrors.nome" class="err">{{ fieldErrors.nome[0] }}</div>
</div>
<div class="field">
<label>Cognome</label>
<input v-model="form.cognome" />
</div>
</div>
<div class="field">
<label>Ragione sociale (opzionale)</label>
<input v-model="form.ragione_sociale" />
</div>
<div class="grid-2">
<div class="field">
<label>Email *</label>
<input v-model="form.email" type="email" />
<div v-if="fieldErrors.email" class="err">{{ fieldErrors.email[0] }}</div>
</div>
<div class="field">
<label>Telefono</label>
<input v-model="form.telefono" type="tel" />
</div>
</div>
<div class="field">
<label>Indirizzo</label>
<input v-model="form.indirizzo" />
</div>
<div class="grid-2">
<div class="field">
<label>CAP</label>
<input v-model="form.cap" />
</div>
<div class="field">
<label>Città</label>
<input v-model="form.citta" />
</div>
</div>
<div class="field">
<label>Provincia</label>
<input v-model="form.provincia" maxlength="5" />
</div>
<div class="field">
<label>Note</label>
<textarea v-model="form.note" rows="3"></textarea>
</div>
<button class="btn-primary" :disabled="submitting" @click="conferma">
{{ submitting ? 'Invio in corso' : 'Conferma e invia' }}
</button>
<button class="btn-link" style="width:100%;margin-top:8px" @click="step = 'cart'">
Torna al preventivo
</button>
</div>
</template>
<!-- STEP CONFERMA -->
<template v-else-if="step === 'done'">
<div class="card center">
<img class="done-logo" src="/kreios-logo.jpg" alt="Krèios" />
<div class="success-icon"></div>
<h2>Grazie!</h2>
<p>La tua richiesta di preventivo <b>n. {{ numeroConfermato }}</b> è stata inviata.</p>
<p class="muted">
Riceverai a breve il PDF riassuntivo via email. Un nostro consulente ti ricontatterà al più presto.
</p>
<a v-if="returnUrl" class="btn-link" :href="returnUrl"> Torna al configuratore</a>
</div>
</template>
</div>
</template>
+59
View File
@@ -0,0 +1,59 @@
const BASE = import.meta.env.VITE_API_BASE || 'http://localhost:8000/api'
async function request(path, options = {}) {
const res = await fetch(`${BASE}${path}`, {
headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
...options,
})
const data = await res.json().catch(() => ({}))
if (!res.ok) {
const err = new Error(data.message || 'Errore di rete')
err.status = res.status
err.errors = data.errors
throw err
}
return data
}
export const api = {
getCarrello: (token) => request(`/carrello/${token}`),
rimuoviItem: (token, itemId) => request(`/carrello/${token}/item/${itemId}`, { method: 'DELETE' }),
conferma: (token, dati) =>
request(`/carrello/${token}/conferma`, { method: 'POST', body: JSON.stringify(dati) }),
}
const STORAGE_KEY = 'kreios_cart_token'
const RETURN_URL_KEY = 'kreios_return_url'
export function resolveToken() {
const url = new URL(window.location.href)
const fromUrl = url.searchParams.get('token')
const ritorno = url.searchParams.get('ritorno')
if (ritorno) {
localStorage.setItem(RETURN_URL_KEY, ritorno)
}
if (fromUrl) {
localStorage.setItem(STORAGE_KEY, fromUrl)
// pulisci l'URL mantenendo token/ritorno in localStorage
url.searchParams.delete('token')
url.searchParams.delete('ritorno')
window.history.replaceState({}, '', url.pathname + url.search)
return fromUrl
}
return localStorage.getItem(STORAGE_KEY)
}
// URL del configuratore da cui arriva il carrello: passato dinamicamente come parametro
// (vedi mostraLinkPreventivo in starter.js), non hardcoded, perché il configuratore può girare
// su domini/porte diverse tra dev e produzione.
export function resolveReturnUrl() {
return localStorage.getItem(RETURN_URL_KEY)
}
export function clearToken() {
localStorage.removeItem(STORAGE_KEY)
}
export function formatEuro(value) {
return new Intl.NumberFormat('it-IT', { style: 'currency', currency: 'EUR' }).format(value || 0)
}
+5
View File
@@ -0,0 +1,5 @@
import { createApp } from 'vue'
import App from './App.vue'
import './style.css'
createApp(App).mount('#app')
+93
View File
@@ -0,0 +1,93 @@
:root {
/* Palette del configuratore Krèios */
--brand: #12373C;
--brand-dark: #0b2427;
--accent: #ffd700;
--accent-dark: #e6c200;
--bg: #f5f6f8;
--card: #ffffff;
--border: #e4e7ec;
--text: #1f2937;
--muted: #6b7280;
--danger: #c0392b;
--ok: #1e874b;
}
* { box-sizing: border-box; }
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
background: var(--bg);
color: var(--text);
}
.app-header {
background: var(--brand);
color: #fff;
padding: 14px 20px;
display: flex;
align-items: center;
gap: 12px;
}
.app-header img { height: 34px; width: auto; border-radius: 3px; }
.app-header h1 { margin: 0; font-size: 18px; font-weight: 600; }
.container { max-width: 780px; margin: 0 auto; padding: 20px 16px 60px; }
.card {
background: var(--card);
border: 1px solid var(--border);
border-radius: 10px;
padding: 16px;
margin-bottom: 14px;
}
.item { display: flex; gap: 14px; }
.item img {
width: 90px; height: 90px; object-fit: contain;
background: #fafafa; border: 1px solid var(--border); border-radius: 8px; flex: none;
}
.item-body { flex: 1; min-width: 0; }
.item-title { font-weight: 600; margin: 0 0 2px; }
.item-sub { color: var(--muted); font-size: 13px; margin-bottom: 6px; }
.conf { font-size: 13px; color: #374151; line-height: 1.5; }
.conf b { color: var(--muted); font-weight: 500; }
.item-foot { display: flex; align-items: center; justify-content: space-between; margin-top: 8px; }
.price { font-weight: 700; }
.totale-row {
display: flex; justify-content: space-between; align-items: baseline;
font-size: 20px; font-weight: 700; color: var(--brand);
padding-top: 8px;
}
.iva-nota { color: var(--muted); font-size: 12px; font-weight: 400; }
button {
font: inherit; cursor: pointer; border: none; border-radius: 8px; padding: 12px 18px;
}
.btn-primary {
background: var(--accent); color: #000; font-weight: 700; width: 100%;
text-transform: uppercase; letter-spacing: .3px;
}
.btn-primary:hover { background: var(--accent-dark); }
.btn-primary:disabled { opacity: .6; cursor: not-allowed; }
.btn-link { background: none; color: var(--muted); padding: 6px; text-decoration: underline; }
a.btn-link { display: inline-block; margin-top: 16px; text-align: center; }
.btn-remove { background: none; color: var(--danger); padding: 4px 8px; font-size: 13px; }
.field { margin-bottom: 12px; }
.field label { display: block; font-size: 13px; color: var(--muted); margin-bottom: 4px; }
.field input, .field textarea {
width: 100%; padding: 10px 12px; border: 1px solid var(--border); border-radius: 8px; font: inherit;
}
.field .err { color: var(--danger); font-size: 12px; margin-top: 3px; }
.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
@media (max-width: 520px) { .grid-2 { grid-template-columns: 1fr; } }
.muted { color: var(--muted); }
.center { text-align: center; }
.empty { text-align: center; padding: 40px 20px; color: var(--muted); }
.success-icon { font-size: 48px; }
.done-logo { width: auto; border-radius: 3px; margin-bottom: 16px; }
.alert { background: #fdecea; color: var(--danger); padding: 10px 12px; border-radius: 8px; margin-bottom: 12px; font-size: 14px; }
+11
View File
@@ -0,0 +1,11 @@
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [vue()],
server: {
host: true,
port: 5199,
strictPort: true,
},
})