commit d8d8a3fbceb5f9edcbb8b3717d3e0ab981b4457f Author: root Date: Tue Mar 10 11:03:54 2026 +0000 Upload inicial do TSCUT diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2685228 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +node_modules +dist +.vercel +.vscode +.DS_Store +*.local diff --git a/RELATORIO.html b/RELATORIO.html new file mode 100644 index 0000000..0c00a70 --- /dev/null +++ b/RELATORIO.html @@ -0,0 +1,713 @@ + + + + + + Plano de Corte - Ferro Redondo 1/2" ASTM A36 + + + +
+
+

šŸ“Š Plano de Corte Otimizado

+

Ferro Redondo 1/2" (Ƙ 12,7 mm) | AƧo Carbono ASTM A36 | Barras de 6.000 mm

+
+ +
+
+

Total de PeƧas

+
28
+
+
+

Barras Utilizadas

+
4
+
+
+

Comprimento Total NecessƔrio

+
20.730 mm
+
+
+

Sobra Total

+
3.270 mm
+
+
+

EficiĆŖncia de Corte

+
86,38%
+
+
+ +
+ +
+
+
šŸ“Œ BARRA 1
+
+
+ Comprimento Usado: + 5.975 mm +
+
+ Sobra: + 25 mm +
+
+
+ + + + + + + + + 1090 + + + + + 1090 + + + + + 1090 + + + + + 1070 + + + + + 1070 + + + + + 565 + + + + 25 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PeƧammQtd
#1-310903
#4-510702
#65651
SOBRA25mm
+ +
+
99,58%
+
+
+ + +
+
+
šŸ“Œ BARRA 2
+
+
+ Comprimento Usado: + 5.705 mm +
+
+ Sobra: + 295 mm +
+
+
+ + + + + + + + + 1040 + + + + + 1035 + + + + + 1035 + + + + + 1035 + + + + + 1020 + + + + + 540 + + + + 295 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PeƧammQtd
#110401
#2-410353
#510201
#65401
SOBRA295mm
+ +
+
95,08%
+
+
+ + +
+
+
šŸ“Œ BARRA 3
+
+
+ Comprimento Usado: + 5.940 mm +
+
+ Sobra: + 60 mm +
+
+
+ + + + + + + + + 1020 + + + + + 990 + + + + + 980 + + + + + 515 + + + + + 515 + + + + + 485 + + + + + 485 + + + + + 475 + + + + + 475 + + + + 60 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PeƧammQtd
#110201
#29901
#39801
#4-55152
#6-74852
#8-94752
SOBRA60mm
+ +
+
99,00%
+
+
+ + +
+
+
šŸ“Œ BARRA 4
+
+
+ Comprimento Usado: + 3.110 mm +
+
+ Sobra: + 2.890 mm +
+
+
+ + + + + + + + + 475 + + + + + 475 + + + + + 460 + + + + + 435 + + + + + 435 + + + + + 415 + + + + + 415 + + + + 2890 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PeƧammQtd
#1-24752
#34601
#4-54352
#6-74152
SOBRA2890mm
+ +
+
51,83%
+
+
+
+ +
+ Material: Ferro Redondo 1/2" ASTM A36 | 28 peças | 20.730 mm | Eficiência 86,38% | Sobras: B1=25mm | B2=295mm | B3=60mm | B4=2.890mm +
+
+ + diff --git a/index.html b/index.html new file mode 100644 index 0000000..6058983 --- /dev/null +++ b/index.html @@ -0,0 +1,195 @@ + + + + + + + + Otimizador de Corte - TSCUT + + + +
+
+

Otimizador de Plano de Corte

+

Barras Lineares | Algoritmo FFD Avançado | Otimização com Múltiplas Barras

+ +
+ +
+ +
+

šŸ“„ Dados de Entrada

+ +
+ + +
+ + + +
+
+ + +
+ +
+ + + +
+
+ +
+ + +

šŸ“¦ Barras DisponĆ­veis

+ +
+ + +
+ +
+
+ + +
+
+ + +
+
+ +
+ + +
+ +
+ + +
+ +
+
Nenhuma barra adicionada
+
+ +
+ + +

āœ‚ļø PeƧas (Demanda)

+ +
+ + +
+ + +
+ + +
+ +
+
+ + +
+
+ + +
+
+ +
+ + +
+ +
+
Nenhuma peƧa adicionada
+
+ +
+ + + +
+ + +
+
+

šŸ“Š Resumo

+
+
Calcule a otimização para ver os resultados +
+
+
+ +
+

šŸ“Œ PeƧas Demandadas

+
+
Nenhuma peƧa adicionada
+
+
+ +
+ + +
+
+
+ + +
+

āœ‚ļø Plano de Corte Otimizado

+
+
Resultados aparecerão após o cÔlculo
+
+
+
+ + +
+ + + + + + + + \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..f9438cc --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1153 @@ +{ + "name": "tscut", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "tscut", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "papaparse": "^5.5.3", + "vite": "^7.2.6", + "xlsx": "^0.18.5" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz", + "integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz", + "integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz", + "integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz", + "integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz", + "integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz", + "integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz", + "integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz", + "integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz", + "integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz", + "integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz", + "integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz", + "integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz", + "integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz", + "integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz", + "integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz", + "integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz", + "integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz", + "integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz", + "integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz", + "integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz", + "integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz", + "integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz", + "integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz", + "integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz", + "integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz", + "integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz", + "integrity": "sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.0.tgz", + "integrity": "sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.0.tgz", + "integrity": "sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.0.tgz", + "integrity": "sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.0.tgz", + "integrity": "sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.0.tgz", + "integrity": "sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.59.0.tgz", + "integrity": "sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.59.0.tgz", + "integrity": "sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.59.0.tgz", + "integrity": "sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.59.0.tgz", + "integrity": "sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.59.0.tgz", + "integrity": "sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.59.0.tgz", + "integrity": "sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.59.0.tgz", + "integrity": "sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.59.0.tgz", + "integrity": "sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.59.0.tgz", + "integrity": "sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.59.0.tgz", + "integrity": "sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.59.0.tgz", + "integrity": "sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.0.tgz", + "integrity": "sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.59.0.tgz", + "integrity": "sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.59.0.tgz", + "integrity": "sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.59.0.tgz", + "integrity": "sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.59.0.tgz", + "integrity": "sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.59.0.tgz", + "integrity": "sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.59.0.tgz", + "integrity": "sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.59.0.tgz", + "integrity": "sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "license": "MIT" + }, + "node_modules/adler-32": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/adler-32/-/adler-32-1.3.1.tgz", + "integrity": "sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/cfb": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cfb/-/cfb-1.2.2.tgz", + "integrity": "sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==", + "license": "Apache-2.0", + "dependencies": { + "adler-32": "~1.3.0", + "crc-32": "~1.2.0" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/codepage": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/codepage/-/codepage-1.15.0.tgz", + "integrity": "sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "license": "Apache-2.0", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/esbuild": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz", + "integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.3", + "@esbuild/android-arm": "0.27.3", + "@esbuild/android-arm64": "0.27.3", + "@esbuild/android-x64": "0.27.3", + "@esbuild/darwin-arm64": "0.27.3", + "@esbuild/darwin-x64": "0.27.3", + "@esbuild/freebsd-arm64": "0.27.3", + "@esbuild/freebsd-x64": "0.27.3", + "@esbuild/linux-arm": "0.27.3", + "@esbuild/linux-arm64": "0.27.3", + "@esbuild/linux-ia32": "0.27.3", + "@esbuild/linux-loong64": "0.27.3", + "@esbuild/linux-mips64el": "0.27.3", + "@esbuild/linux-ppc64": "0.27.3", + "@esbuild/linux-riscv64": "0.27.3", + "@esbuild/linux-s390x": "0.27.3", + "@esbuild/linux-x64": "0.27.3", + "@esbuild/netbsd-arm64": "0.27.3", + "@esbuild/netbsd-x64": "0.27.3", + "@esbuild/openbsd-arm64": "0.27.3", + "@esbuild/openbsd-x64": "0.27.3", + "@esbuild/openharmony-arm64": "0.27.3", + "@esbuild/sunos-x64": "0.27.3", + "@esbuild/win32-arm64": "0.27.3", + "@esbuild/win32-ia32": "0.27.3", + "@esbuild/win32-x64": "0.27.3" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/frac": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/frac/-/frac-1.1.2.tgz", + "integrity": "sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/papaparse": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.5.3.tgz", + "integrity": "sha512-5QvjGxYVjxO59MGU2lHVYpRWBBtKHnlIAcSe1uNFCkkptUh63NFRj0FJQm7nR67puEruUci/ZkjmEFrjCAyP4A==", + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/rollup": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz", + "integrity": "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==", + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.59.0", + "@rollup/rollup-android-arm64": "4.59.0", + "@rollup/rollup-darwin-arm64": "4.59.0", + "@rollup/rollup-darwin-x64": "4.59.0", + "@rollup/rollup-freebsd-arm64": "4.59.0", + "@rollup/rollup-freebsd-x64": "4.59.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.59.0", + "@rollup/rollup-linux-arm-musleabihf": "4.59.0", + "@rollup/rollup-linux-arm64-gnu": "4.59.0", + "@rollup/rollup-linux-arm64-musl": "4.59.0", + "@rollup/rollup-linux-loong64-gnu": "4.59.0", + "@rollup/rollup-linux-loong64-musl": "4.59.0", + "@rollup/rollup-linux-ppc64-gnu": "4.59.0", + "@rollup/rollup-linux-ppc64-musl": "4.59.0", + "@rollup/rollup-linux-riscv64-gnu": "4.59.0", + "@rollup/rollup-linux-riscv64-musl": "4.59.0", + "@rollup/rollup-linux-s390x-gnu": "4.59.0", + "@rollup/rollup-linux-x64-gnu": "4.59.0", + "@rollup/rollup-linux-x64-musl": "4.59.0", + "@rollup/rollup-openbsd-x64": "4.59.0", + "@rollup/rollup-openharmony-arm64": "4.59.0", + "@rollup/rollup-win32-arm64-msvc": "4.59.0", + "@rollup/rollup-win32-ia32-msvc": "4.59.0", + "@rollup/rollup-win32-x64-gnu": "4.59.0", + "@rollup/rollup-win32-x64-msvc": "4.59.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ssf": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/ssf/-/ssf-0.11.2.tgz", + "integrity": "sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==", + "license": "Apache-2.0", + "dependencies": { + "frac": "~1.1.2" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/vite": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz", + "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", + "license": "MIT", + "dependencies": { + "esbuild": "^0.27.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "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 + } + } + }, + "node_modules/wmf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wmf/-/wmf-1.0.2.tgz", + "integrity": "sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/word": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/word/-/word-0.3.0.tgz", + "integrity": "sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/xlsx": { + "version": "0.18.5", + "resolved": "https://registry.npmjs.org/xlsx/-/xlsx-0.18.5.tgz", + "integrity": "sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==", + "license": "Apache-2.0", + "dependencies": { + "adler-32": "~1.3.0", + "cfb": "~1.2.1", + "codepage": "~1.15.0", + "crc-32": "~1.2.1", + "ssf": "~0.11.2", + "wmf": "~1.0.1", + "word": "~0.3.0" + }, + "bin": { + "xlsx": "bin/xlsx.njs" + }, + "engines": { + "node": ">=0.8" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..1ad463a --- /dev/null +++ b/package.json @@ -0,0 +1,25 @@ +{ + "name": "tscut", + "version": "1.0.0", + "description": "Otimizador de Plano de Corte 2D", + "scripts": { + "dev": "vite", + "build": "vite build", + "start": "vite preview --port $PORT --host 0.0.0.0", + "preview": "vite preview", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [ + "cutting", + "optimizer", + "vite" + ], + "author": "admtracksteel", + "license": "ISC", + "type": "module", + "dependencies": { + "papaparse": "^5.5.3", + "vite": "^7.3.1", + "xlsx": "^0.18.5" + } +} \ No newline at end of file diff --git a/public/logo.png b/public/logo.png new file mode 100644 index 0000000..9aecc3a Binary files /dev/null and b/public/logo.png differ diff --git a/src/main.js b/src/main.js new file mode 100644 index 0000000..933a25a --- /dev/null +++ b/src/main.js @@ -0,0 +1,1187 @@ +import * as XLSX from 'xlsx'; +import Papa from 'papaparse'; +import './style.css'; + +let availableBars = []; +let editingBarId = null; +let demandPieces = []; +let lastResults = null; +let editingPieceId = null; +let selectedProcess = 'plasma'; // Default +let kerfSize = 3; // Default for plasma + +const PROCESS_KERFS = { + 'plasma': 3, + 'saw': 2, + 'oxy': 5 +}; + +function updateProcess() { + const radios = document.getElementsByName('cuttingProcess'); + for (const radio of radios) { + if (radio.checked) { + selectedProcess = radio.value; + kerfSize = PROCESS_KERFS[selectedProcess]; + break; + } + } + console.log(`Process updated: ${selectedProcess}, Kerf: ${kerfSize}mm`); +} + +const colors = ['#3498db', '#2ecc71', '#9b59b6', '#f39c12', '#1abc9c', '#e74c3c', '#34495e', '#16a085', '#d68910', '#2980b9']; + +const PROCESS_NAMES = { + 'plasma': 'Plasma', + 'saw': 'Serra/Disco', + 'oxy': 'Oxicorte' +}; + +// ===== BARRAS ===== +function addBar() { + const desc = document.getElementById('barDesc').value.trim(); + const qty = parseInt(document.getElementById('barQty').value); + const length = parseInt(document.getElementById('barLength').value); + const weight = parseFloat(document.getElementById('barWeight').value); + + if (!desc || !qty || !length || !weight) { + showToast('Preencha todos os campos da barra', 'warning'); + return; + } + + if (editingBarId) { + const index = availableBars.findIndex(b => b.id === editingBarId); + if (index !== -1) { + availableBars[index] = { ...availableBars[index], desc, qty, length, weight, remainingQty: qty }; + } + cancelBarEdit(); // Reset form and state + } else { + availableBars.push({ id: Date.now(), desc, qty, length, weight, remainingQty: qty }); + document.getElementById('barDesc').value = ''; + document.getElementById('barQty').value = '1'; + document.getElementById('barLength').value = '6000'; + document.getElementById('barWeight').value = '45'; + } + + renderBars(); +} + +function removeBar(id) { + if (editingBarId === id) cancelBarEdit(); + availableBars = availableBars.filter(b => b.id !== id); + renderBars(); +} + +function editBar(id) { + const bar = availableBars.find(b => b.id === id); + if (!bar) return; + + document.getElementById('barDesc').value = bar.desc; + document.getElementById('barQty').value = bar.qty; + document.getElementById('barLength').value = bar.length; + document.getElementById('barWeight').value = bar.weight; + + editingBarId = id; + + const btnAdd = document.getElementById('btnAddBar'); + btnAdd.textContent = 'šŸ’¾ Salvar Alteração'; + btnAdd.classList.remove('btn-primary'); + btnAdd.classList.add('btn-success'); + + document.getElementById('btnCancelBarEdit').style.display = 'block'; +} + +function cancelBarEdit() { + editingBarId = null; + document.getElementById('barDesc').value = ''; + document.getElementById('barQty').value = '1'; + document.getElementById('barLength').value = '6000'; + document.getElementById('barWeight').value = '45'; + + const btnAdd = document.getElementById('btnAddBar'); + btnAdd.textContent = 'āž• Adicionar Barra'; + btnAdd.classList.remove('btn-success'); + btnAdd.classList.add('btn-primary'); + + document.getElementById('btnCancelBarEdit').style.display = 'none'; +} + +function renderBars() { + const container = document.getElementById('barsList'); + if (availableBars.length === 0) { + container.innerHTML = '
Nenhuma barra adicionada
'; + return; + } + + container.innerHTML = availableBars.map(b => ` +
+
+ ${b.desc} ${b.qty}x ${b.length}mm | ${b.weight}kg +
+
+ + +
+
+ `).join(''); +} + +// ===== PEƇAS ===== +function addPiece() { + const tag = document.getElementById('pieceTag').value.trim(); + const length = parseInt(document.getElementById('pieceLength').value); + const qty = parseInt(document.getElementById('pieceQty').value); + + if (!tag || !length || !qty) { + showToast('Preencha TAG, comprimento e quantidade', 'warning'); + return; + } + + if (editingPieceId) { + const index = demandPieces.findIndex(p => p.id === editingPieceId); + if (index !== -1) { + demandPieces[index] = { ...demandPieces[index], tag, length, qty }; + } + cancelEdit(); // Reset form and state + } else { + demandPieces.push({ id: Date.now(), tag, length, qty }); + document.getElementById('pieceTag').value = ''; + document.getElementById('pieceLength').value = ''; + document.getElementById('pieceQty').value = '1'; + } + + renderPieces(); +} + +function removePiece(id) { + if (editingPieceId === id) cancelEdit(); + demandPieces = demandPieces.filter(p => p.id !== id); + renderPieces(); +} + +function editPiece(id) { + const piece = demandPieces.find(p => p.id === id); + if (!piece) return; + + document.getElementById('pieceTag').value = piece.tag; + document.getElementById('pieceLength').value = piece.length; + document.getElementById('pieceQty').value = piece.qty; + + editingPieceId = id; + + const btnAdd = document.getElementById('btnAddPiece'); + btnAdd.textContent = 'šŸ’¾ Salvar Alteração'; + btnAdd.classList.remove('btn-primary'); + btnAdd.classList.add('btn-success'); + + document.getElementById('btnCancelEdit').style.display = 'block'; +} + +function cancelEdit() { + editingPieceId = null; + document.getElementById('pieceTag').value = ''; + document.getElementById('pieceLength').value = ''; + document.getElementById('pieceQty').value = '1'; + + const btnAdd = document.getElementById('btnAddPiece'); + btnAdd.textContent = 'āž• Adicionar PeƧa'; + btnAdd.classList.remove('btn-success'); + btnAdd.classList.add('btn-primary'); + + document.getElementById('btnCancelEdit').style.display = 'none'; +} + +function renderPieces() { + const container = document.getElementById('piecesList'); + if (demandPieces.length === 0) { + container.innerHTML = '
Nenhuma peƧa adicionada
'; + renderBalloons([]); + return; + } + + container.innerHTML = demandPieces.map(p => ` +
+
+ ${p.tag} ${p.qty}x ${p.length}mm +
+
+ + +
+
+ `).join(''); + + renderBalloons(demandPieces); +} + +function renderBalloons(pieces) { + const container = document.getElementById('piecesBalloons'); + if (pieces.length === 0) { + container.innerHTML = '
Nenhuma peƧa adicionada
'; + return; + } + + container.innerHTML = pieces.map(p => ` +
+ ${p.tag} +
+ ${p.length}mm Ɨ ${p.qty} +
+
+ `).join(''); +} + +// ===== IMPORTAƇƃO DE ARQUIVO ===== +function importFile() { + const fileInput = document.getElementById('fileImport'); + const file = fileInput.files[0]; + + if (!file) return; + + const ext = file.name.split('.').pop().toLowerCase(); + + if (ext === 'csv') { + Papa.parse(file, { + header: false, + complete: (results) => processImportedData(results.data), + error: (error) => showFeedback('Erro ao ler arquivo: ' + error.message, 'error') + }); + } else if (['xlsx', 'xls'].includes(ext)) { + const reader = new FileReader(); + reader.onload = (e) => { + try { + const data = new Uint8Array(e.target.result); + const workbook = XLSX.read(data, { type: 'array' }); + const sheet = workbook.Sheets[workbook.SheetNames[0]]; + const rows = XLSX.utils.sheet_to_json(sheet, { header: 1 }); + processImportedData(rows); + } catch (error) { + showFeedback('Erro ao ler arquivo Excel: ' + error.message, 'error'); + } + }; + reader.readAsArrayBuffer(file); + } else { + showFeedback('Formato nĆ£o suportado. Use CSV, XLSX ou XLS', 'error'); + } + + fileInput.value = ''; +} + +function processImportedData(rows) { + if (!rows || rows.length === 0) { + showFeedback('Arquivo vazio', 'error'); + return; + } + + // Detectar cabeƧalho + let startRow = 0; + if (isHeaderRow(rows[0])) { + startRow = 1; + } + + // Processar dados + let importedCount = 0; + let errors = []; + const duplicates = []; + const newPieces = []; + + for (let i = startRow; i < rows.length; i++) { + const row = rows[i]; + if (!row || row.length < 3) continue; + + const tag = String(row[0]).trim(); + const qtyStr = String(row[1]).trim(); + const lengthStr = String(row[2]).trim(); + + if (!tag || !qtyStr || !lengthStr) continue; + + const qty = parseInt(qtyStr); + const length = parseInt(lengthStr); + + if (isNaN(qty) || isNaN(length) || qty <= 0 || length <= 0) { + errors.push(`Linha ${i + 1}: dados invĆ”lidos`); + continue; + } + + if (demandPieces.some(p => p.tag === tag)) { + duplicates.push(tag); + } + + newPieces.push({ id: Date.now() + i, tag, length, qty }); + importedCount++; + } + + if (newPieces.length === 0) { + showToast('Nenhuma peƧa vĆ”lida encontrada no arquivo', 'error'); + return; + } + + const processImport = () => { + demandPieces.push(...newPieces); + renderPieces(); + showToast(`āœ“ ${newPieces.length} peƧa(s) importada(s)`, 'success'); + }; + + if (duplicates.length > 0) { + showConfirmModal( + "PeƧas Duplicadas", + `Foram encontradas ${duplicates.length} peƧas com TAGs que jĆ” existem na lista (ex: ${duplicates.slice(0, 3).join(', ')}...).\n\nDeseja importĆ”-las mesmo assim?`, + processImport + ); + } else { + processImport(); + } + + if (errors.length > 0) { + console.warn('Erros na importação:', errors); + } +} + + +function isHeaderRow(row) { + if (!row || row.length < 3) return false; + + const col1 = String(row[0]).toLowerCase().trim(); + const col2 = String(row[1]).toLowerCase().trim(); + const col3 = String(row[2]).toLowerCase().trim(); + + // Palavras-chave comuns em cabeƧalhos + const headerKeywords = ['tag', 'id', 'identificacao', 'nome', 'qtd', 'quantidade', 'comp', 'comprimento', 'mm', 'length']; + + return headerKeywords.some(kw => + col1.includes(kw) || col2.includes(kw) || col3.includes(kw) + ); +} + +// ===== UI HELPERS ===== +function showToast(message, type = 'info', title = '') { + const container = document.getElementById('toastContainer'); + const toast = document.createElement('div'); + toast.className = `toast ${type}`; + + let icon = 'ā„¹ļø'; + if (type === 'success') icon = 'āœ…'; + if (type === 'error') icon = 'āŒ'; + if (type === 'warning') icon = 'āš ļø'; + + if (!title) { + if (type === 'success') title = 'Sucesso'; + if (type === 'error') title = 'Erro'; + if (type === 'warning') title = 'Atenção'; + if (type === 'info') title = 'Informação'; + } + + toast.innerHTML = ` +
${icon}
+
+
${title}
+
${message}
+
+ + `; + + container.appendChild(toast); + + // Trigger animation + requestAnimationFrame(() => { + toast.classList.add('show'); + }); + + // Auto remove + setTimeout(() => { + toast.classList.remove('show'); + setTimeout(() => toast.remove(), 300); + }, 5000); +} + +let currentConfirmCallback = null; + +function showConfirmModal(title, message, onConfirm) { + document.getElementById('modalTitle').textContent = title; + document.getElementById('modalMessage').innerHTML = message.replace(/\n/g, '
'); + + const modal = document.getElementById('confirmModal'); + modal.classList.add('show'); + + currentConfirmCallback = onConfirm; +} + +function closeModal() { + document.getElementById('confirmModal').classList.remove('show'); + currentConfirmCallback = null; +} + +// Setup modal confirm button listener once +document.getElementById('modalConfirmBtn').addEventListener('click', () => { + if (currentConfirmCallback) currentConfirmCallback(); + closeModal(); +}); + +function showFeedback(message, type) { + // Deprecated in favor of showToast, but keeping for compatibility if needed + // or redirecting to showToast + showToast(message, type); +} + +// ===== ALGORITMO FFD AVANƇADO ===== +function optimizeCutting() { + if (availableBars.length === 0 || demandPieces.length === 0) { + showToast('Adicione barras e peƧas', 'warning'); + return; + } + + // Expandir peƧas com ID Ćŗnico global + const expandedPieces = []; + let uniqueIdCounter = 0; + demandPieces.forEach(p => { + for (let i = 0; i < p.qty; i++) { + expandedPieces.push({ ...p, uniqueId: ++uniqueIdCounter }); + } + }); + + // FFD em cada barra disponĆ­vel + const usedBars = []; + const unusedPieces = [...expandedPieces]; + + // Ordenar barras por comprimento (maior para menor) para tentar usar as maiores primeiro? + // O código original nĆ£o ordenava, seguia a ordem de inserção. Manteremos assim por enquanto. + + for (let barType of availableBars) { + for (let barCopy = 0; barCopy < barType.qty; barCopy++) { + if (unusedPieces.length === 0) break; + + const bar = { + barType: barType.desc, + length: barType.length, + weight: barType.weight, + pieces: [], + remaining: barType.length, + isSimulated: barType.isSimulated || false + }; + + // Tentar encaixar peƧas (FFD) + // Ordenar peƧas restantes por tamanho decrescente + const sorted = [...unusedPieces].sort((a, b) => b.length - a.length); + const toRemoveIds = []; + + for (let piece of sorted) { + // Check if piece fits considering kerf loss for this cut + // We assume each piece consumes its length + kerf + // Exception: The very last piece in a bar might not strictly need a kerf if it's the end, + // but usually in cutting processes, you cut the piece out, so kerf is consumed. + // User requirement: "consumo adicional de cada corte... sera de mais 2mm para cada corte" + + const requiredSpace = piece.length + kerfSize; + + // However, we need to be careful. If remaining is EXACTLY piece.length, can we cut it? + // If we cut it, we lose the kerf. So we need remaining >= piece.length + kerf? + // Or does the kerf come from the "waste"? + // Usually: Bar 6000. Piece 1000. Kerf 3. + // Cut 1: Consumes 1003. Remaining 4997. + // So yes, we treat piece length as (length + kerf). + + if (bar.remaining >= requiredSpace) { + bar.pieces.push(piece); + bar.remaining -= requiredSpace; + toRemoveIds.push(piece.uniqueId); + } else if (bar.remaining >= piece.length && bar.remaining < requiredSpace) { + // Edge case: Fits exactly or with less than kerf remaining? + // If I have 1000mm remaining and need 1000mm piece. + // If I cut, I destroy 3mm. So I need 1003mm to get a 1000mm piece? + // Yes, usually. Unless it's the raw end of the bar, but we can't assume that. + // Let's stick to the rule: consumption = length + kerf. + // If bar.remaining < length + kerf, we can't cut it. + } + } + + // Remover peƧas colocadas usando ID Ćŗnico + for (let uid of toRemoveIds) { + const idx = unusedPieces.findIndex(p => p.uniqueId === uid); + if (idx !== -1) unusedPieces.splice(idx, 1); + } + + if (bar.pieces.length > 0) { + usedBars.push(bar); + } + } + } + + return { usedBars, unusedPieces, availableBars }; +} + +function checkStockSufficiency() { + const totalDemandLength = demandPieces.reduce((sum, p) => sum + (p.length * p.qty), 0); + const totalStockLength = availableBars.reduce((sum, b) => sum + (b.length * b.qty), 0); + return totalStockLength >= totalDemandLength; +} + +// ===== CƁLCULO ===== +function calculateOptimization() { + if (availableBars.length === 0 || demandPieces.length === 0) { + showToast('Adicione barras e peƧas para calcular', 'warning'); + return; + } + + let currentAvailableBars = JSON.parse(JSON.stringify(availableBars)); + + if (!checkStockSufficiency()) { + showConfirmModal( + "Estoque Insuficiente", + "āš ļø O estoque atual de barras NƃO Ć© suficiente para atender toda a demanda.\n\nDeseja continuar simulando a quantidade necessĆ”ria de barras?", + () => { + runOptimizationWithSimulation(currentAvailableBars, true); + } + ); + return; + } + + runOptimizationWithSimulation(currentAvailableBars, false); +} + +function runOptimizationWithSimulation(currentAvailableBars, simulateMode) { + if (simulateMode) { + // Find the longest bar to use as standard for simulation + const standardBar = availableBars.reduce((prev, current) => (prev.length > current.length) ? prev : current); + + // Add a virtually infinite amount of the standard bar + // We add enough to cover the deficit + buffer + const totalDemandLength = demandPieces.reduce((sum, p) => sum + (p.length * p.qty), 0); + const totalStockLength = availableBars.reduce((sum, b) => sum + (b.length * b.qty), 0); + const deficit = totalDemandLength - totalStockLength; + const extraBarsNeeded = Math.ceil(deficit / standardBar.length) + 10; // +10 buffer + + currentAvailableBars.push({ + ...standardBar, + qty: extraBarsNeeded, + remainingQty: extraBarsNeeded, + desc: standardBar.desc + " (Simulado)", + isSimulated: true + }); + } + + // Temporarily swap availableBars with the simulated list for the optimize function + const originalBars = availableBars; + availableBars = currentAvailableBars; + + const result = optimizeCutting(); + + // Restore original bars + availableBars = originalBars; + + if (!result) return; + + const { usedBars, unusedPieces } = result; + + // Calcular totais + const totalPieces = demandPieces.reduce((s, p) => s + p.qty, 0); + const totalLength = demandPieces.reduce((s, p) => s + (p.length * p.qty), 0); + const totalWaste = usedBars.reduce((s, b) => s + b.remaining, 0); + const totalBarLength = usedBars.reduce((s, b) => s + b.length, 0); + const efficiency = totalBarLength > 0 ? ((1 - totalWaste / totalBarLength) * 100).toFixed(2) : 0; + const totalWeight = usedBars.reduce((s, b) => s + b.weight, 0); + const unusedPiecesCount = unusedPieces.reduce((s, p) => s + 1, 0); + + // Calculate total kerf loss + const totalKerfLoss = usedBars.reduce((sum, bar) => sum + (bar.pieces.length * kerfSize), 0); + + // Generate Scraps List + const scraps = usedBars.map(b => b.remaining).filter(r => r > 0).sort((a, b) => b - a); + const scrapsListHtml = scraps.length > 0 + ? `
+ ${Object.entries(scraps.reduce((acc, val) => { acc[val] = (acc[val] || 0) + 1; return acc; }, {})) + .map(([size, count]) => `
${count}x ${size}mm
`).join('')} +
` + : '
Sem sobras!
'; + + // Resumo Detalhado + const simulationWarning = simulateMode + ? `
+
Aviso de Estoque
+
Uso de barras Adicionais (sem estoque)
+
` + : ''; + + document.getElementById('summaryResults').innerHTML = ` +
+
EficiĆŖncia Global
+
${efficiency}%
+
Aproveitamento do Material
+
+
+
Barras Usadas
+
${usedBars.length}
+
Total: ${totalWeight.toFixed(1)}kg
+
+
+
PeƧas Faltando
+
${unusedPiecesCount}
+
De ${totalPieces} totais
+
+
+
Resumo de Sobras (Retalhos)
+
Total: ${totalWaste}mm
+ ${scrapsListHtml} +
+
+
Peso Sucata
+
${(totalWeight * (1 - (efficiency / 100))).toFixed(1)}kg
+
+
+
Processo de Corte
+
${PROCESS_NAMES[selectedProcess]}
+
Consumo adicional de material: ${totalKerfLoss} mm
+
+ ${simulationWarning} + `; + + // Barras - Mostrar todas as barras individualmente + document.getElementById('barsContainer').innerHTML = usedBars.map((bar, idx) => { + const used = bar.pieces.reduce((s, p) => s + p.length, 0); + const eff = ((used / bar.length) * 100).toFixed(2); + return renderBar(bar, idx + 1, used, eff); + }).join(''); + + lastResults = { usedBars, unusedPieces, totalWeight, totalLength, totalPieces }; + + if (simulateMode) { + showToast("O cĆ”lculo foi realizado em MODO SIMULAƇƃO.", "warning", "Atenção"); + } +} + + + +function renderBar(bar, barNum, used, efficiency) { + const scale = 6200 / bar.length; + const noStockLabel = bar.isSimulated ? 'SEM ESTOQUE' : ''; + + // Reconstruindo o SVG corretamente + let svgContent = ``; + + let position = 0; + bar.pieces.forEach((piece, idx) => { + const scaledWidth = piece.length * scale; + const colorIdx = idx % colors.length; + svgContent += ` + + + ${piece.tag} + ${piece.length}`; + position += scaledWidth; + }); + + const waste = bar.remaining; + if (waste > 0) { + const wasteWidth = waste * scale; + svgContent += ` + sobra + ${waste}`; + } + + const svg = `${svgContent}`; + + const pieceDetails = groupPieces(bar.pieces); + const table = ` + + + ${pieceDetails.map(g => ``).join('')} + +
TAGmmQtd
${g.tag}${g.length}${g.count}
SOBRA${waste}mm
+ `; + + const effBar = `
${efficiency}%
`; + + const repetitionText = bar.count > 1 ? ' Ɨ ' + bar.count + '' : ''; + + return ` +
+
+
BARRA ${barNum}${repetitionText}${noStockLabel}
${bar.barType}
+
+
Usado: ${used}mm
+
Peso: ${bar.weight}kg
+
+
+ ${svg} + ${table} + ${effBar} +
+ `; +} + +function groupPieces(pieces) { + const groups = {}; + pieces.forEach(p => { + if (!groups[p.tag]) groups[p.tag] = { length: p.length, count: 0 }; + groups[p.tag].count++; + }); + + return Object.entries(groups).map(([tag, data]) => ({ tag, ...data })); +} + +function clearAll() { + availableBars = []; + demandPieces = []; + lastResults = null; + renderBars(); + renderPieces(); + document.getElementById('summaryResults').innerHTML = '
Calcule a otimização para ver os resultados
'; + document.getElementById('barsContainer').innerHTML = '
Resultados aparecerão após o cÔlculo
'; +} + +function generateReportHTML() { + if (!lastResults) return null; + + const { usedBars, totalWeight } = lastResults; + const dateStr = new Date().toLocaleDateString('pt-BR'); + const timeStr = new Date().toLocaleTimeString('pt-BR'); + const totalPieces = demandPieces.reduce((s, p) => s + p.qty, 0); + + // Paginação simples: 3 barras por pÔgina + const barsPerPage = 3; + const pages = []; + for (let i = 0; i < usedBars.length; i += barsPerPage) { + pages.push({ + items: usedBars.slice(i, i + barsPerPage).map((bar, idx) => ({ type: 'bar', data: bar, index: i + idx + 1 })), + type: 'bars' + }); + } + // Adicionar resumo na última pÔgina ou nova pÔgina + pages.push({ + items: [{ type: 'summary', data: lastResults }], + type: 'summary' + }); + + const totalPages = pages.length; + const jobTitle = document.getElementById('jobTitle').value.trim() || 'Relatório de Corte'; + + let htmlContent = pages.map((page, pageIdx) => { + const pageNum = pageIdx + 1; + + let pageBody = page.items.map(item => { + if (item.type === 'bar') return renderReportBar(item.data, item.index); + if (item.type === 'summary') return renderReportSummary(item.data); + return ''; + }).join(''); + + return ` +
+
+
+
+

šŸ“Š ${jobTitle}

+

Gerado em: ${dateStr} Ć s ${timeStr}

+
+
+ PƔgina ${pageNum} de ${totalPages} +
+
+
+ +
+ ${pageBody} +
+ +
+ Otimizador de Corte | Total de PeƧas: ${totalPieces} | Aproveitamento Global: ${((1 - (lastResults.usedBars.reduce((s, b) => s + b.remaining, 0) / lastResults.usedBars.reduce((s, b) => s + b.length, 0))) * 100).toFixed(2)}% +
+
`; + }).join(''); + + return ` + + + + + Relatório de Corte - ${dateStr} + + + + ${htmlContent} + + `; +} + +function renderReportBar(bar, index) { + const used = bar.pieces.reduce((s, p) => s + p.length, 0); + const efficiency = ((used / bar.length) * 100).toFixed(2); + const waste = bar.remaining; + const groups = groupPieces(bar.pieces); + const scale = 6200 / bar.length; + + let svgContent = ``; + let pos = 0; + bar.pieces.forEach((p, i) => { + const w = p.length * scale; + svgContent += ``; + svgContent += `${p.tag}`; + pos += w; + }); + if (waste > 0) { + svgContent += ``; + svgContent += `sobra`; + } + + const repetition = bar.count > 1 ? `${bar.count} cópias idênticas` : ''; + + return ` +
+
+
BARRA ${index} - ${bar.barType} ${repetition}
+
EficiĆŖncia: ${efficiency}% | Sobra: ${waste}mm
+
+ ${svgContent} + + + + ${groups.map(g => ``).join('')} + ${waste > 0 ? `` : ''} + +
PeƧaComp. (mm)Qtd no Corte
${g.tag}${g.length}${g.count}
SOBRA${waste}-
+
`; +} + +function renderReportSummary(data) { + const totalWaste = data.usedBars.reduce((s, b) => s + b.remaining, 0); + const totalLength = data.usedBars.reduce((s, b) => s + b.length, 0); + const efficiency = ((1 - totalWaste / totalLength) * 100).toFixed(2); + + const totalKerfLoss = data.usedBars.reduce((sum, bar) => sum + (bar.pieces.length * kerfSize), 0); + + return ` +
+

šŸ“Œ Resumo Final

+
+
+
Total de Barras
+
${data.usedBars.length}
+
+
+
Peso Total
+
${data.totalWeight.toFixed(1)} kg
+
+
+
Sobra Total
+
${totalWaste} mm
+
+
+
EficiĆŖncia Global
+
${efficiency}%
+
+
+
Processo: ${PROCESS_NAMES[selectedProcess]}
+
Perda estimada por corte (Kerf): ${totalKerfLoss} mm
+
+
+
`; +} + +function exportHTML() { + const html = generateReportHTML(); + if (!html) { + showToast('Calcule a otimização primeiro', 'warning'); + return; + } + const blob = new Blob([html], { type: 'text/html' }); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = 'relatorio-corte-' + new Date().toISOString().split('T')[0] + '.html'; + a.click(); +} + +function printReport() { + const html = generateReportHTML(); + if (!html) { + showToast('Calcule a otimização primeiro', 'warning'); + return; + } + const win = window.open('', '_blank'); + win.document.write(html); + win.document.close(); + setTimeout(() => { + win.print(); + }, 500); +} + +// Expose functions to window for HTML onclick events +window.addBar = addBar; +window.removeBar = removeBar; +window.addPiece = addPiece; +window.removePiece = removePiece; +window.editPiece = editPiece; +window.cancelEdit = cancelEdit; +window.importFile = importFile; +window.calculateOptimization = calculateOptimization; +window.clearAll = clearAll; +window.exportHTML = exportHTML; +// ===== GERENCIAMENTO DE TRABALHO ===== +// ===== GERENCIAMENTO DE TRABALHO ===== +function saveJob() { + const title = document.getElementById('jobTitle').value.trim() || 'Trabalho Sem Titulo'; + + // Formato CSV Customizado: TYPE, P1, P2, P3, P4 + let csvContent = "TYPE,PARAM1,PARAM2,PARAM3,PARAM4\n"; + + // 1. Metadata + csvContent += `JOB,${title},,,\n`; + csvContent += `METADATA,PROCESS,${selectedProcess},,\n`; + + // 2. Barras + availableBars.forEach(b => { + csvContent += `BAR,${b.desc},${b.qty},${b.length},${b.weight}\n`; + }); + + // 3. PeƧas + demandPieces.forEach(p => { + csvContent += `PIECE,${p.tag},${p.length},${p.qty},\n`; + }); + + const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' }); + const url = URL.createObjectURL(blob); + const link = document.createElement("a"); + link.setAttribute("href", url); + link.setAttribute("download", `${title}.csv`); + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); +} + +function loadJob() { + const fileInput = document.getElementById('jobImport'); + const file = fileInput.files[0]; + + if (!file) return; + + Papa.parse(file, { + header: true, + skipEmptyLines: true, + complete: function (results) { + try { + let jobTitleFound = ""; + const newBars = []; + const newPieces = []; + const duplicateBars = []; + + results.data.forEach(row => { + const type = row.TYPE; + if (type === 'JOB') { + jobTitleFound = row.PARAM1; + } else if (type === 'METADATA' && row.PARAM1 === 'PROCESS') { + selectedProcess = row.PARAM2; + // Update UI + const radios = document.getElementsByName('cuttingProcess'); + for (const radio of radios) { + if (radio.value === selectedProcess) { + radio.checked = true; + } + } + updateProcess(); + } else if (type === 'BAR') { + const desc = row.PARAM1; + if (availableBars.some(b => b.desc === desc)) { + duplicateBars.push(desc); + } + newBars.push({ + id: Date.now() + Math.random(), + desc: desc, + qty: parseInt(row.PARAM2), + length: parseInt(row.PARAM3), + weight: parseFloat(row.PARAM4), + remainingQty: parseInt(row.PARAM2) + }); + } else if (type === 'PIECE') { + newPieces.push({ + id: Date.now() + Math.random(), + tag: row.PARAM1, + length: parseInt(row.PARAM2), + qty: parseInt(row.PARAM3) + }); + } + }); + + const finishLoad = () => { + // Limpar estado atual + clearAll(); + + if (jobTitleFound) document.getElementById('jobTitle').value = jobTitleFound; + + newBars.forEach(b => availableBars.push(b)); + newPieces.forEach(p => demandPieces.push(p)); + + renderBars(); + renderPieces(); + showToast('Trabalho carregado com sucesso!', 'success'); + }; + + // If the list is not empty, warn user that current data will be lost + if (availableBars.length > 0 || demandPieces.length > 0) { + showConfirmModal( + "Substituir Trabalho Atual?", + "Carregar um novo trabalho irĆ” limpar todas as barras e peƧas atuais.\n\nDeseja continuar?", + finishLoad + ); + } else { + finishLoad(); + } + + } catch (e) { + showToast('Erro ao carregar arquivo: ' + e.message, 'error'); + } + } + }); + fileInput.value = ''; +} + +// Expose functions to window for HTML onclick events +window.addBar = addBar; +window.removeBar = removeBar; +window.editBar = editBar; +window.cancelBarEdit = cancelBarEdit; +window.addPiece = addPiece; +window.removePiece = removePiece; +window.importFile = importFile; +window.calculateOptimization = calculateOptimization; +window.clearAll = clearAll; +window.exportHTML = exportHTML; +window.printReport = printReport; +window.saveJob = saveJob; + +window.loadJob = loadJob; +window.closeModal = closeModal; + + + +window.updateProcess = updateProcess; + +// THEME HANDLING +function toggleTheme() { + const body = document.body; + body.classList.toggle('dark-theme'); + + const isDark = body.classList.contains('dark-theme'); + localStorage.setItem('theme', isDark ? 'dark' : 'light'); + + updateThemeIcon(isDark); +} + +function updateThemeIcon(isDark) { + const btn = document.getElementById('themeToggle'); + if (btn) { + btn.textContent = isDark ? 'ā˜€ļø' : 'šŸŒ™'; + btn.title = isDark ? 'Mudar para Modo Claro' : 'Mudar para Modo Escuro'; + } +} + +function initTheme() { + const savedTheme = localStorage.getItem('theme'); + const prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches; + + if (savedTheme === 'dark' || (!savedTheme && prefersDark)) { + document.body.classList.add('dark-theme'); + updateThemeIcon(true); + } else { + document.body.classList.remove('dark-theme'); + updateThemeIcon(false); + } +} + +window.toggleTheme = toggleTheme; +initTheme(); + +renderBars(); +renderPieces(); +updateProcess(); // Initialize kerf size based on default checked radio diff --git a/src/style.css b/src/style.css new file mode 100644 index 0000000..b93a757 --- /dev/null +++ b/src/style.css @@ -0,0 +1,1323 @@ + * { + margin: 0; + padding: 0; + box-sizing: border-box; + } + + :root { + --primary: #1a6b8f; + --primary-light: #2d8bb0; + --success: #27ae60; + --warning: #e67e22; + --danger: #e74c3c; + --gray-light: #ecf0f1; + --gray: #bdc3c7; + --text: #2c3e50; + --text-light: #7f8c8d; + --bg: #f5f7fa; + } + + body { + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; + background: var(--bg); + color: var(--text); + line-height: 1.6; + padding: 10px; + } + + .container { + max-width: 1400px; + margin: 0 auto; + } + + header { + background: linear-gradient(135deg, var(--primary) 0%, var(--primary-light) 100%); + color: white; + padding: 20px; + border-radius: 8px; + margin-bottom: 20px; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); + position: relative; + } + + header h1 { + font-size: 28px; + margin-bottom: 5px; + } + + header p { + font-size: 14px; + opacity: 0.95; + } + + .layout { + display: grid; + grid-template-columns: 380px 1fr; + gap: 20px; + margin-bottom: 20px; + } + + .panel { + background: white; + padding: 20px; + border-radius: 8px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); + border: 1px solid var(--gray); + } + + .panel h2 { + font-size: 16px; + color: var(--primary); + margin-bottom: 15px; + padding-bottom: 10px; + border-bottom: 2px solid var(--primary); + } + + .form-group { + margin-bottom: 12px; + } + + label { + display: block; + font-size: 11px; + font-weight: 600; + color: var(--text-light); + text-transform: uppercase; + margin-bottom: 4px; + } + + input { + width: 100%; + padding: 8px; + border: 1px solid var(--gray); + border-radius: 4px; + font-size: 13px; + } + + button { + width: 100%; + padding: 10px; + border: none; + border-radius: 4px; + font-size: 14px; + font-weight: 600; + cursor: pointer; + transition: all 0.3s ease; + } + + .btn-primary { + background: var(--primary); + color: white; + } + + .btn-primary:hover { + background: var(--primary-light); + } + + .btn-success { + background: var(--success); + color: white; + } + + .btn-success:hover { + background: #229954; + } + + .btn-danger { + background: var(--danger); + color: white; + width: auto; + padding: 4px 8px; + font-size: 11px; + margin-left: 5px; + } + + .btn-danger:hover { + background: #c0392b; + } + + .section-divider { + margin: 18px 0; + border: none; + border-top: 1px solid var(--gray); + } + + .list-container { + max-height: 280px; + overflow-y: auto; + color: var(--text-light); + } + + .piece-balloon { + display: inline-flex; + align-items: center; + gap: 8px; + background: var(--gray-light); + padding: 8px 12px; + border-radius: 20px; + margin: 4px; + font-size: 12px; + border-left: 3px solid var(--primary); + } + + .piece-balloon-tag { + font-weight: bold; + color: var(--primary); + } + + .piece-balloon-info { + display: flex; + flex-direction: column; + font-size: 10px; + color: var(--text-light); + } + + .results { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); + gap: 12px; + margin-top: 15px; + } + + .result-card { + background: white; + padding: 12px; + border-radius: 4px; + text-align: center; + border-left: 4px solid var(--primary); + } + + .result-card.success { + border-left-color: var(--success); + } + + .result-card.warning { + border-left-color: var(--warning); + } + + .result-label { + font-size: 10px; + color: var(--text-light); + text-transform: uppercase; + margin-bottom: 6px; + } + + .result-value { + font-size: 22px; + font-weight: bold; + color: var(--primary); + } + + .result-card.success .result-value { + color: var(--success); + } + + .result-card.warning .result-value { + color: var(--warning); + } + + .bars-container { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(350px, 1fr)); + gap: 20px; + } + + .bar-card { + background: white; + padding: 15px; + border-radius: 8px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); + border: 1px solid var(--gray); + } + + .bar-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 10px; + padding-bottom: 8px; + border-bottom: 2px solid var(--primary); + } + + .bar-title { + font-weight: bold; + color: var(--primary); + font-size: 13px; + } + + .bar-stats { + font-size: 11px; + color: var(--text-light); + text-align: right; + } + + svg { + width: 100%; + height: auto; + border: 1px solid var(--gray); + border-radius: 4px; + background: var(--gray-light); + margin-bottom: 10px; + } + + .bar-table { + width: 100%; + font-size: 10px; + border-collapse: collapse; + margin-bottom: 8px; + } + + .bar-table th { + background: var(--primary); + color: white; + padding: 5px; + text-align: left; + font-weight: bold; + } + + .bar-table td { + padding: 4px 5px; + border-bottom: 1px solid var(--gray); + } + + .bar-table tr:nth-child(even) { + background: #f9f9f9; + } + + .efficiency-bar { + width: 100%; + height: 18px; + background: #e0e0e0; + border-radius: 4px; + overflow: hidden; + margin-bottom: 8px; + } + + .efficiency-fill { + height: 100%; + background: linear-gradient(90deg, var(--success) 0%, var(--primary-light) 100%); + display: flex; + align-items: center; + justify-content: center; + color: white; + font-weight: bold; + font-size: 9px; + } + + .export-buttons { + display: flex; + gap: 10px; + margin-top: 15px; + } + + .export-buttons button { + flex: 1; + padding: 10px; + } + + @media (max-width: 1024px) { + .layout { + grid-template-columns: 1fr; + } + } + + @media (max-width: 768px) { + header h1 { + font-size: 20px; + } + + .bars-container { + grid-template-columns: 1fr; + } + } + + .no-data { + text-align: center; + padding: 20px; + color: var(--text-light); + font-size: 12px; + } + + .pieces-balloons { + display: flex; + flex-wrap: wrap; + gap: 4px; + padding: 8px; + background: var(--gray-light); + border-radius: 4px; + min-height: 40px; + align-content: flex-start; + } + + /* New classes extracted from inline styles */ + .job-title-group { + background: #f0f7fa; + padding: 10px; + border-radius: 4px; + border: 1px solid #bce0fd; + margin-bottom: 15px; + } + + .job-title-label { + color: var(--primary); + font-size: 12px; + } + + .job-title-input { + font-weight: bold; + } + + .job-actions { + display: flex; + gap: 5px; + margin-top: 8px; + } + + .btn-small { + font-size: 12px; + padding: 6px; + } + + .btn-secondary { + background-color: #7f8c8d; + } + + .hidden { + display: none; + } + + .section-title { + font-size: 13px; + color: var(--primary); + margin: 15px 0 10px 0; + } + + .grid-2-col { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 8px; + } + + .action-row { + display: flex; + gap: 5px; + } + + .mt-10 { + margin-top: 10px; + } + + .mt-20 { + margin-top: 20px; + } + + .mb-10 { + margin-bottom: 10px; + } + + .import-btn { + flex: 1; + padding: 8px; + } + + .feedback-msg { + font-size: 11px; + color: var(--success); + margin-bottom: 10px; + text-align: center; + display: none; + } + + .btn-calc { + padding: 12px; + } + + .btn-clear { + width: auto; + margin-top: 8px; + width: 100%; + } + + .full-width { + width: 100%; + } + + .grid-full-col { + grid-column: 1/-1; + } + + /* TOAST NOTIFICATIONS */ + .toast-container { + position: fixed; + top: 20px; + right: 20px; + z-index: 1000; + display: flex; + flex-direction: column; + gap: 10px; + } + + .toast { + background: white; + padding: 15px 20px; + border-radius: 8px; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); + display: flex; + align-items: center; + gap: 12px; + min-width: 300px; + animation: slideIn 0.3s ease-out forwards; + border-left: 5px solid var(--primary); + opacity: 0; + transform: translateX(100%); + } + + .toast.show { + opacity: 1; + transform: translateX(0); + } + + .toast.success { + border-left-color: var(--success); + } + + .toast.error { + border-left-color: var(--danger); + } + + .toast.warning { + border-left-color: var(--warning); + } + + .toast-icon { + font-size: 20px; + } + + .toast-content { + flex: 1; + } + + .toast-title { + font-weight: bold; + font-size: 14px; + margin-bottom: 2px; + color: var(--text); + } + + .toast-message { + font-size: 12px; + color: var(--text-light); + } + + .toast-close { + background: none; + border: none; + color: var(--text-light); + cursor: pointer; + font-size: 16px; + padding: 0; + width: auto; + } + + @keyframes slideIn { + from { + opacity: 0; + transform: translateX(100%); + } + + to { + opacity: 1; + transform: translateX(0); + } + } + + /* MODAL */ + .modal-overlay { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.5); + z-index: 2000; + display: flex; + justify-content: center; + align-items: center; + opacity: 0; + visibility: hidden; + transition: all 0.3s ease; + } + + .modal-overlay.show { + opacity: 1; + visibility: visible; + } + + .modal { + background: white; + padding: 25px; + border-radius: 8px; + width: 90%; + max-width: 400px; + box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2); + transform: translateY(-20px); + transition: transform 0.3s ease; + } + + .modal-overlay.show .modal { + transform: translateY(0); + } + + .modal-header { + margin-bottom: 15px; + } + + .modal-title { + font-size: 18px; + font-weight: bold; + color: var(--primary); + } + + .modal-body { + font-size: 14px; + color: var(--text); + margin-bottom: 25px; + line-height: 1.5; + } + + .modal-actions { + display: flex; + justify-content: flex-end; + gap: 10px; + } + + .modal-btn { + padding: 8px 16px; + border-radius: 4px; + font-weight: 600; + font-size: 13px; + cursor: pointer; + border: none; + transition: background 0.2s; + } + + .modal-btn-cancel { + background: var(--gray-light); + color: var(--text); + } + + .modal-btn-cancel:hover { + background: var(--gray); + } + + .import-btn { + flex: 1; + padding: 8px; + } + + .feedback-msg { + font-size: 11px; + color: var(--success); + margin-bottom: 10px; + text-align: center; + display: none; + } + + .btn-calc { + padding: 12px; + } + + .btn-clear { + width: auto; + margin-top: 8px; + width: 100%; + } + + .full-width { + width: 100%; + } + + .grid-full-col { + grid-column: 1/-1; + } + + /* TOAST NOTIFICATIONS */ + .toast-container { + position: fixed; + top: 20px; + right: 20px; + z-index: 1000; + display: flex; + flex-direction: column; + gap: 10px; + } + + .toast { + background: white; + padding: 15px 20px; + border-radius: 8px; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); + display: flex; + align-items: center; + gap: 12px; + min-width: 300px; + animation: slideIn 0.3s ease-out forwards; + border-left: 5px solid var(--primary); + opacity: 0; + transform: translateX(100%); + } + + .toast.show { + opacity: 1; + transform: translateX(0); + } + + .toast.success { + border-left-color: var(--success); + } + + .toast.error { + border-left-color: var(--danger); + } + + .toast.warning { + border-left-color: var(--warning); + } + + .toast-icon { + font-size: 20px; + } + + .toast-content { + flex: 1; + } + + .toast-title { + font-weight: bold; + font-size: 14px; + margin-bottom: 2px; + color: var(--text); + } + + .toast-message { + font-size: 12px; + color: var(--text-light); + } + + .toast-close { + background: none; + border: none; + color: var(--text-light); + cursor: pointer; + font-size: 16px; + padding: 0; + width: auto; + } + + @keyframes slideIn { + from { + opacity: 0; + transform: translateX(100%); + } + + to { + opacity: 1; + transform: translateX(0); + } + } + + /* MODAL */ + .modal-overlay { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.5); + z-index: 2000; + display: flex; + justify-content: center; + align-items: center; + opacity: 0; + visibility: hidden; + transition: all 0.3s ease; + } + + .modal-overlay.show { + opacity: 1; + visibility: visible; + } + + .modal { + background: white; + padding: 25px; + border-radius: 8px; + width: 90%; + max-width: 400px; + box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2); + transform: translateY(-20px); + transition: transform 0.3s ease; + } + + .modal-overlay.show .modal { + transform: translateY(0); + } + + .modal-header { + margin-bottom: 15px; + } + + .modal-title { + font-size: 18px; + font-weight: bold; + color: var(--primary); + } + + .modal-body { + font-size: 14px; + color: var(--text); + margin-bottom: 25px; + line-height: 1.5; + } + + .modal-actions { + display: flex; + justify-content: flex-end; + gap: 10px; + } + + .modal-btn { + padding: 8px 16px; + border-radius: 4px; + font-weight: 600; + font-size: 13px; + cursor: pointer; + border: none; + transition: background 0.2s; + } + + .modal-btn-cancel { + background: var(--gray-light); + color: var(--text); + } + + .modal-btn-cancel:hover { + background: var(--gray); + } + + .modal-btn-confirm { + background: var(--primary); + color: white; + } + + .modal-btn-confirm:hover { + background: var(--primary-light); + } + + /* Process Selection Styles */ + .process-section { + margin-bottom: 20px; + padding: 5px 0; + } + + .section-label { + display: block; + font-weight: 600; + margin-bottom: 10px; + color: var(--primary); + width: 100%; + } + + .full-width { + width: 100%; + } + + .grid-full-col { + grid-column: 1/-1; + } + + /* TOAST NOTIFICATIONS */ + .toast-container { + position: fixed; + top: 20px; + right: 20px; + z-index: 1000; + display: flex; + flex-direction: column; + gap: 10px; + } + + .toast { + background: white; + padding: 15px 20px; + border-radius: 8px; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); + display: flex; + align-items: center; + gap: 12px; + min-width: 300px; + animation: slideIn 0.3s ease-out forwards; + border-left: 5px solid var(--primary); + opacity: 0; + transform: translateX(100%); + } + + .toast.show { + opacity: 1; + transform: translateX(0); + } + + .toast.success { + border-left-color: var(--success); + } + + .toast.error { + border-left-color: var(--danger); + } + + .toast.warning { + border-left-color: var(--warning); + } + + .toast-icon { + font-size: 20px; + } + + .toast-content { + flex: 1; + } + + .toast-title { + font-weight: bold; + font-size: 14px; + margin-bottom: 2px; + color: var(--text); + } + + .toast-message { + font-size: 12px; + color: var(--text-light); + } + + .toast-close { + background: none; + border: none; + color: var(--text-light); + cursor: pointer; + font-size: 16px; + padding: 0; + width: auto; + } + + @keyframes slideIn { + from { + opacity: 0; + transform: translateX(100%); + } + + to { + opacity: 1; + transform: translateX(0); + } + } + + /* MODAL */ + .modal-overlay { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.5); + z-index: 2000; + display: flex; + justify-content: center; + align-items: center; + opacity: 0; + visibility: hidden; + transition: all 0.3s ease; + } + + .modal-overlay.show { + opacity: 1; + visibility: visible; + } + + .modal { + background: white; + padding: 25px; + border-radius: 8px; + width: 90%; + max-width: 400px; + box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2); + transform: translateY(-20px); + transition: transform 0.3s ease; + } + + .modal-overlay.show .modal { + transform: translateY(0); + } + + .modal-header { + margin-bottom: 15px; + } + + .modal-title { + font-size: 18px; + font-weight: bold; + color: var(--primary); + } + + .modal-body { + font-size: 14px; + color: var(--text); + margin-bottom: 25px; + line-height: 1.5; + } + + .modal-actions { + display: flex; + justify-content: flex-end; + gap: 10px; + } + + .modal-btn { + padding: 8px 16px; + border-radius: 4px; + font-weight: 600; + font-size: 13px; + cursor: pointer; + border: none; + transition: background 0.2s; + } + + .modal-btn-cancel { + background: var(--gray-light); + color: var(--text); + } + + .modal-btn-cancel:hover { + background: var(--gray); + } + + .modal-btn-confirm { + background: var(--primary); + color: white; + } + + .modal-btn-confirm:hover { + background: var(--primary-light); + } + + /* Process Selection Styles */ + .process-section { + margin-bottom: 20px; + padding: 5px 0; + } + + .section-label { + display: block; + font-weight: 600; + margin-bottom: 10px; + color: var(--primary); + font-size: 0.85em; + text-transform: uppercase; + letter-spacing: 0.5px; + } + + .process-options { + display: flex; + gap: 10px; + flex-wrap: wrap; + } + + .process-option { + cursor: pointer; + -webkit-user-select: none; + user-select: none; + } + + /* Hide the actual radio input */ + .process-option input[type="radio"] { + display: none; + } + + /* Style the span to look like a balloon/chip */ + .process-option .process-name { + display: inline-block; + padding: 6px 12px; + border-radius: 20px; + background: #f1f3f5; + color: #6c757d; + font-size: 0.85em; + font-weight: 600; + border: 1px solid transparent; + transition: all 0.2s ease; + } + + .process-option:hover .process-name { + background: #e9ecef; + color: #495057; + } + + /* Selected State */ + .process-option input[type="radio"]:checked+.process-name { + background: var(--primary); + color: white; + box-shadow: 0 2px 5px rgba(26, 107, 143, 0.3); + transform: translateY(-1px); + } + + + .app-logo { + height: 40px; + vertical-align: middle; + margin-right: 10px; + } + + /* THEME TOGGLE */ + .theme-toggle-btn { + position: absolute; + top: 20px; + right: 20px; + background: rgba(255, 255, 255, 0.2); + border: 1px solid rgba(255, 255, 255, 0.3); + color: white; + width: 40px; + height: 40px; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + transition: all 0.2s ease; + font-size: 20px; + z-index: 10; + } + + .theme-toggle-btn:hover { + background: rgba(255, 255, 255, 0.35); + transform: scale(1.05); + } + + /* DARK MODE */ + body.dark-theme { + --bg: #121212; + --text: #e0e0e0; + --text-light: #aaa; + --gray-light: #2c2c2c; + --gray: #444; + --primary: #3498db; + /* Brighter blue for contrast */ + --primary-light: #5dade2; + background-color: #121212; + } + + body.dark-theme .panel, + body.dark-theme .bar-card, + body.dark-theme .result-card, + body.dark-theme .toast, + body.dark-theme .modal { + background-color: #1e1e1e; + border-color: #333; + color: #e0e0e0; + } + + body.dark-theme input { + background-color: #2c2c2c; + border-color: #444; + color: white; + } + + body.dark-theme .job-title-group { + background-color: #252525; + border-color: #444; + } + + body.dark-theme .process-option .process-name, + body.dark-theme .piece-balloon, + body.dark-theme .pieces-balloons { + background-color: #2c2c2c; + color: #ccc; + } + + body.dark-theme .process-option input[type="radio"]:checked+.process-name { + background-color: var(--primary); + color: white; + } + + body.dark-theme .bar-table tr:nth-child(even) { + background-color: #252525; + } + + body.dark-theme .bar-table td, + body.dark-theme .bar-table th { + border-color: #444; + } + + body.dark-theme .item-card { + border-bottom-color: #333 !important; + /* Force override inline style if possible, else ignored */ + } + + body.dark-theme .item-card div[style*="color: #333"] { + width: auto; + } + + .dynamic-action-btn.edit { + color: var(--primary); + } + + .dynamic-action-btn.remove { + color: var(--danger); + } + + .scraps-list { + max-height: 100px; + overflow-y: auto; + font-size: 11px; + margin-top: 5px; + } + + .kerf-info { + font-size: 11px; + color: var(--text-light); + margin-top: 5px; + } + + .no-stock-badge { + color: var(--danger); + font-weight: bold; + margin-left: 10px; + } + + .bar-svg-piece-tag { + font-size: 120px; + font-weight: bold; + text-anchor: middle; + fill: #2c3e50; + } + + .bar-svg-piece-length { + font-size: 80px; + font-weight: bold; + text-anchor: middle; + fill: #333; + } + + .bar-svg-waste-tag { + font-size: 100px; + font-weight: bold; + text-anchor: middle; + fill: #c0392b; + } + + .bar-svg-waste-length { + font-size: 80px; + font-weight: bold; + text-anchor: middle; + fill: #c0392b; + } + + body.dark-theme .dynamic-flex-row { + border-bottom-color: #333; + } + + body.dark-theme .bar-svg-piece-tag { + fill: #eee; + } + + body.dark-theme .bar-svg-piece-length { + fill: #ccc; + } + + .dynamic-flex-row { + display: flex; + justify-content: space-between; + align-items: center; + padding: 8px 10px; + border-bottom: 1px solid #eee; + } + + .dynamic-item-info { + font-size: 14px; + color: var(--text); + } + + .dynamic-item-subinfo { + color: var(--text-light); + margin-left: 8px; + } + + .dynamic-item-actions { + display: flex; + gap: 10px; + } + + .dynamic-action-btn { + background: none; + border: none; + cursor: pointer; + font-size: 16px; + padding: 0; + display: inline-block; + width: auto; + } + + .result-subtext { + font-size: 10px; + color: var(--text-light); + } + + .medium-size { + font-size: 16px; + } + + .medium-small { + font-size: 14px; + } + + .small-danger { + font-size: 16px; + color: var(--danger); + } + + .danger { + color: var(--danger) !important; + } + + .primary { + color: var(--primary) !important; + } + + .simulation-warning { + grid-column: span 2; + border-left-color: var(--danger) !important; + } + + .row-waste { + background-color: #ffe6e6; + } + + body.dark-theme .row-waste { + background-color: #3d1a1a; + } + + .no-scraps { + font-size: 11px; + color: var(--success); + } + + .repetition-text { + color: var(--warning); + font-weight: bold; + } + + .bar-subtitle { + font-size: 11px; + color: var(--text-light); + } + + .process-info { + border-left: 4px solid var(--primary); + } \ No newline at end of file diff --git a/vite.config.js b/vite.config.js new file mode 100644 index 0000000..b12b387 --- /dev/null +++ b/vite.config.js @@ -0,0 +1,9 @@ +export default { + root: './', + build: { + outDir: 'dist', + }, + preview: { + allowedHosts: true + } +}