diff --git a/frontend/package-lock.json b/frontend/package-lock.json index f1800a6..56b6cbe 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -2240,29 +2240,6 @@ "license": "MIT", "optional": true }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "optional": true, - "peer": true - }, "node_modules/baseline-browser-mapping": { "version": "2.10.0", "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.0.tgz", @@ -2299,20 +2276,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, "node_modules/brace-expansion": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", @@ -2371,33 +2334,6 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, "node_modules/call-bind-apply-helpers": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", @@ -2442,23 +2378,6 @@ ], "license": "CC-BY-4.0" }, - "node_modules/canvas": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/canvas/-/canvas-3.2.1.tgz", - "integrity": "sha512-ej1sPFR5+0YWtaVp6S1N1FVz69TQCqmrkGeRvQxZeAB1nAIcjNTHVwrZtYtWFFBmQsF40/uDLehsW5KuYC99mg==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "node-addon-api": "^7.0.0", - "prebuild-install": "^7.1.3" - }, - "engines": { - "node": "^18.12.0 || >= 20.9.0" - } - }, "node_modules/chai": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", @@ -2507,15 +2426,6 @@ "node": ">= 6" } }, - "node_modules/chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", - "dev": true, - "license": "ISC", - "optional": true, - "peer": true - }, "node_modules/cli-width": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", @@ -2775,36 +2685,6 @@ "devOptional": true, "license": "MIT" }, - "node_modules/decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "mimic-response": "^3.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=4.0.0" - } - }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -2915,18 +2795,6 @@ "devOptional": true, "license": "MIT" }, - "node_modules/end-of-stream": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", - "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "once": "^1.4.0" - } - }, "node_modules/entities": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", @@ -3107,18 +2975,6 @@ "node": ">=0.10.0" } }, - "node_modules/expand-template": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", - "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", - "dev": true, - "license": "(MIT OR WTFPL)", - "optional": true, - "peer": true, - "engines": { - "node": ">=6" - } - }, "node_modules/expect-type": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", @@ -3546,15 +3402,6 @@ "url": "https://github.com/sponsors/rawify" } }, - "node_modules/fs-constants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true - }, "node_modules/fs-minipass": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", @@ -3705,15 +3552,6 @@ "node": ">= 0.4" } }, - "node_modules/github-from-package": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", - "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true - }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -3919,29 +3757,6 @@ "node": ">=0.10.0" } }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "BSD-3-Clause", - "optional": true, - "peer": true - }, "node_modules/indent-string": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", @@ -3971,15 +3786,6 @@ "license": "ISC", "optional": true }, - "node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "dev": true, - "license": "ISC", - "optional": true, - "peer": true - }, "node_modules/invariant": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", @@ -4307,21 +4113,6 @@ "node": ">= 0.6" } }, - "node_modules/mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/min-indent": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", @@ -4345,18 +4136,6 @@ "node": "*" } }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/minipass": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", @@ -4414,15 +4193,6 @@ "node": ">=10" } }, - "node_modules/mkdirp-classic": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", - "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true - }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -4523,54 +4293,6 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, - "node_modules/napi-build-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", - "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true - }, - "node_modules/node-abi": { - "version": "3.87.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.87.0.tgz", - "integrity": "sha512-+CGM1L1CgmtheLcBuleyYOn7NWPVu0s0EJH2C4puxgEZb9h8QpR9G2dBfZJOAUhi7VQxuBPMd0hiISWcTyiYyQ==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "semver": "^7.3.5" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/node-abi/node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", - "dev": true, - "license": "ISC", - "optional": true, - "peer": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/node-addon-api": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", - "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true - }, "node_modules/node-fetch": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", @@ -4989,36 +4711,6 @@ "dev": true, "license": "MIT" }, - "node_modules/prebuild-install": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", - "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", - "deprecated": "No longer maintained. Please contact the author of the relevant native addon; alternatives are available.", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "detect-libc": "^2.0.0", - "expand-template": "^2.0.3", - "github-from-package": "0.0.0", - "minimist": "^1.2.3", - "mkdirp-classic": "^0.5.3", - "napi-build-utils": "^2.0.0", - "node-abi": "^3.3.0", - "pump": "^3.0.0", - "rc": "^1.2.7", - "simple-get": "^4.0.0", - "tar-fs": "^2.0.0", - "tunnel-agent": "^0.6.0" - }, - "bin": { - "prebuild-install": "bin.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/pretty-format": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", @@ -5073,19 +4765,6 @@ "url": "https://github.com/sponsors/lupomontero" } }, - "node_modules/pump": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", - "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -5124,24 +4803,6 @@ ], "license": "MIT" }, - "node_modules/rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "dev": true, - "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", - "optional": true, - "peer": true, - "dependencies": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "bin": { - "rc": "cli.js" - } - }, "node_modules/react": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", @@ -5598,34 +5259,6 @@ "license": "MIT", "optional": true }, - "node_modules/simple-get": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", - "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "decompress-response": "^6.0.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" - } - }, "node_modules/sonner": { "version": "1.7.4", "resolved": "https://registry.npmjs.org/sonner/-/sonner-1.7.4.tgz", @@ -5738,18 +5371,6 @@ "node": ">=8" } }, - "node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/sucrase": { "version": "3.35.1", "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz", @@ -5863,40 +5484,6 @@ "node": ">=10" } }, - "node_modules/tar-fs": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz", - "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "chownr": "^1.1.1", - "mkdirp-classic": "^0.5.2", - "pump": "^3.0.0", - "tar-stream": "^2.1.4" - } - }, - "node_modules/tar-stream": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", - "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/tar/node_modules/chownr": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", @@ -6084,21 +5671,6 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "license": "0BSD" }, - "node_modules/tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", - "dev": true, - "license": "Apache-2.0", - "optional": true, - "peer": true, - "dependencies": { - "safe-buffer": "^5.0.1" - }, - "engines": { - "node": "*" - } - }, "node_modules/type-fest": { "version": "5.4.4", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-5.4.4.tgz", diff --git a/frontend/src/components/layout/Header.tsx b/frontend/src/components/layout/Header.tsx index a61c5b3..e9421cb 100644 --- a/frontend/src/components/layout/Header.tsx +++ b/frontend/src/components/layout/Header.tsx @@ -1,7 +1,7 @@ import { useState, useEffect, useRef } from 'react'; import { Link } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; -import { FileText, Moon, Sun, Menu, X, ChevronDown, UserRound, Coins } from 'lucide-react'; +import { FileText, Moon, Sun, Menu, X, ChevronDown, UserRound, Coins, ArrowRight } from 'lucide-react'; import { useAuthStore } from '@/stores/authStore'; import { ensureLanguageResources } from '@/i18n'; interface LangOption { @@ -67,49 +67,54 @@ export default function Header() { }; return ( -
+
{/* Logo */} - - - {t('common.appName')} + +
+ +
+ + {t('common.appName')} + {/* Desktop Navigation */} - )}
diff --git a/frontend/src/components/shared/HeroUploadZone.tsx b/frontend/src/components/shared/HeroUploadZone.tsx index f77ef3c..979df44 100644 --- a/frontend/src/components/shared/HeroUploadZone.tsx +++ b/frontend/src/components/shared/HeroUploadZone.tsx @@ -2,7 +2,7 @@ import { useState, useCallback } from 'react'; import { useDropzone } from 'react-dropzone'; import { useNavigate } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; -import { UploadCloud, Sparkles, PenLine } from 'lucide-react'; +import { UploadCloud, PenLine, ChevronRight, FileCheck } from 'lucide-react'; import ToolSelectorModal from '@/components/shared/ToolSelectorModal'; import { useFileStore } from '@/stores/fileStore'; import { getToolsForFile, detectFileCategory, getCategoryLabel } from '@/utils/fileRouting'; @@ -24,6 +24,15 @@ const ACCEPTED_TYPES = { 'application/vnd.openxmlformats-officedocument.wordprocessingml.document': ['.docx'], }; +const FORMAT_BADGES = [ + { label: 'PDF', color: 'bg-red-50 text-red-700 ring-red-100 dark:bg-red-900/20 dark:text-red-400 dark:ring-red-800/40' }, + { label: 'Word', color: 'bg-blue-50 text-blue-700 ring-blue-100 dark:bg-blue-900/20 dark:text-blue-400 dark:ring-blue-800/40' }, + { label: 'JPG', color: 'bg-amber-50 text-amber-700 ring-amber-100 dark:bg-amber-900/20 dark:text-amber-400 dark:ring-amber-800/40' }, + { label: 'PNG', color: 'bg-green-50 text-green-700 ring-green-100 dark:bg-green-900/20 dark:text-green-400 dark:ring-green-800/40' }, + { label: 'WebP', color: 'bg-violet-50 text-violet-700 ring-violet-100 dark:bg-violet-900/20 dark:text-violet-400 dark:ring-violet-800/40' }, + { label: 'MP4', color: 'bg-slate-100 text-slate-600 ring-slate-200 dark:bg-slate-700 dark:text-slate-300 dark:ring-slate-600' }, +]; + export default function HeroUploadZone() { const { t } = useTranslation(); const navigate = useNavigate(); @@ -81,6 +90,16 @@ export default function HeroUploadZone() { setMatchedTools([]); }, []); + const iconGlowClass = isDragActive + ? 'bg-primary-300/50 scale-125' + : 'bg-primary-100/0 group-hover:bg-primary-200/40 group-hover:scale-110'; + const iconContainerClass = isDragActive + ? 'bg-primary-100 shadow-primary-200 dark:bg-primary-900/50' + : 'bg-primary-50 shadow-sm dark:bg-slate-700/80'; + const uploadIconClass = isDragActive + ? 'text-primary-600 dark:text-primary-400' + : 'text-primary-400 group-hover:text-primary-600 dark:text-primary-500 dark:group-hover:text-primary-400'; + return ( <>
@@ -90,38 +109,30 @@ export default function HeroUploadZone() { > - {/* Animated icon */} -
- + {/* Cloud icon with animated ring */} +
+ {/* Outer glow ring */} +
+
+ +
{/* Heading */} -

+

{isDragActive ? t('home.dropFileHere', 'Drop your file here…') : t('home.dragDropTitle', 'Drag & drop your file here')}

-

+

{t('common.dragDrop', 'or click the button to browse from your device')}

{/* CTA Buttons */} -
+
- {/* Supported formats */} + {/* Divider */} +
+
+ + {t('home.supportedFormats', 'Supported formats')} + +
+
+ + {/* Coloured format badges */}
- {['PDF', 'Word', 'JPG', 'PNG', 'WebP', 'MP4'].map((format) => ( + {FORMAT_BADGES.map(({ label, color }) => ( - {format} + {label} ))}
- {/* File size hint */} + {/* Size hint */}

- + {t('home.uploadSubtitle')}

diff --git a/frontend/src/components/shared/ToolCard.tsx b/frontend/src/components/shared/ToolCard.tsx index 2ad1406..03a0a83 100644 --- a/frontend/src/components/shared/ToolCard.tsx +++ b/frontend/src/components/shared/ToolCard.tsx @@ -1,5 +1,6 @@ import { Link } from 'react-router-dom'; import type { ReactNode } from 'react'; +import { ArrowRight } from 'lucide-react'; interface ToolCardProps { /** Tool route path */ @@ -23,18 +24,28 @@ export default function ToolCard({ }: ToolCardProps) { return ( -
-
-
- {icon} +
+ {/* Top color accent bar — slides in on hover */} +
+ +
+ {/* Icon + title */} +
+
+ {icon} +
+

+ {title} +

-

- {title} -

+ + {/* Arrow indicator */} +
-

+ +

{description}

diff --git a/frontend/src/pages/HomePage.tsx b/frontend/src/pages/HomePage.tsx index 5e506ac..9546fc1 100644 --- a/frontend/src/pages/HomePage.tsx +++ b/frontend/src/pages/HomePage.tsx @@ -1,6 +1,7 @@ import { useDeferredValue } from 'react'; import { useTranslation } from 'react-i18next'; import { useSearchParams } from 'react-router-dom'; +import { Link } from 'react-router-dom'; import SEOHead from '@/components/seo/SEOHead'; import { generateOrganization, generateWebSite, getSiteOrigin } from '@/utils/seo'; import { @@ -38,6 +39,15 @@ import { Wrench, Presentation, Barcode, + ShieldCheck, + Zap, + Globe, + UploadCloud, + MousePointerClick, + Download, + ArrowRight, + Star, + CheckCircle2, } from 'lucide-react'; import ToolCard from '@/components/shared/ToolCard'; import HeroUploadZone from '@/components/shared/HeroUploadZone'; @@ -78,6 +88,39 @@ function manifestToToolInfo(tools: readonly ToolEntry[]): ToolInfo[] { const pdfTools: ToolInfo[] = manifestToToolInfo(getHomepageTools('pdf')); const otherTools: ToolInfo[] = manifestToToolInfo(getHomepageTools('other')); +const HOW_IT_WORKS = [ + { + step: '01', + icon: UploadCloud, + titleKey: 'home.howStep1Title', + titleDefault: 'Upload your file', + descKey: 'home.howStep1Desc', + descDefault: 'Drag & drop or click to select. PDF, Word, images and more — up to 200 MB.', + color: 'bg-blue-600', + glow: 'shadow-blue-200 dark:shadow-blue-900/40', + }, + { + step: '02', + icon: MousePointerClick, + titleKey: 'home.howStep2Title', + titleDefault: 'Choose a tool', + descKey: 'home.howStep2Desc', + descDefault: 'We detect your file type and suggest the best tools automatically.', + color: 'bg-violet-600', + glow: 'shadow-violet-200 dark:shadow-violet-900/40', + }, + { + step: '03', + icon: Download, + titleKey: 'home.howStep3Title', + titleDefault: 'Download instantly', + descKey: 'home.howStep3Desc', + descDefault: 'Your file is ready in seconds. No account needed — files are auto-deleted.', + color: 'bg-emerald-600', + glow: 'shadow-emerald-200 dark:shadow-emerald-900/40', + }, +]; + export default function HomePage() { const { t } = useTranslation(); const siteOrigin = getSiteOrigin(typeof window !== 'undefined' ? window.location.origin : ''); @@ -122,41 +165,103 @@ export default function HomePage() { ]} /> - {/* Hero Section */} -
-
- {/* Badge */} -
+ {/* ── Hero Section ──────────────────────────────────────────── */} +
+ {/* Decorative blobs */} +
+
+ +
+ {/* Animated badge */} +
- - {t('home.heroBadge', 'Free Online PDF Tools')} + + {t('home.heroBadge', 'Free Online PDF & File Tools')}

{t('home.hero')}

-

+

{t('home.heroSub')}

+ {/* Trust strip */} +
+ {[ + { icon: ShieldCheck, text: t('home.trustNoSignup', 'No sign-up needed') }, + { icon: Zap, text: t('home.trustFast', 'Results in seconds') }, + { icon: Lock, text: t('home.trustSecure', 'Files auto-deleted') }, + { icon: Globe, text: t('home.trust30Tools', '30+ free tools') }, + ].map(({ icon: Icon, text }) => ( +
+ + {text} +
+ ))} +
+ {/* Smart Upload Zone */}
- {/* Ad Slot */} + {/* ── Ad Slot ───────────────────────────────────────────────── */} + {/* ── Social Proof Strip ────────────────────────────────────── */} -
+ {/* ── How It Works ──────────────────────────────────────────── */} +
+
+

+ {t('home.howItWorksLabel', 'Simple process')} +

+

+ {t('home.howItWorksTitle', 'Convert & edit in 3 steps')} +

+

+ {t('home.howItWorksSubtitle', 'No account, no installation, no waiting. Just upload, choose a tool, and download.')} +

+
+ +
+ {HOW_IT_WORKS.map(({ step, icon: Icon, titleKey, titleDefault, descKey, descDefault, color, glow }, idx) => ( +
+ {/* Connector line (between steps, hidden on mobile) */} + {idx < HOW_IT_WORKS.length - 1 && ( +
+ )} +
+ {/* Numbered icon */} +
+ + + {parseInt(step, 10)} + +
+

+ {t(titleKey, titleDefault)} +

+

+ {t(descKey, descDefault)} +

+
+
+ ))} +
+
+ + {/* ── Search & Tools ────────────────────────────────────────── */} +

{t('common.search')}

-

+

{t('home.searchToolsPlaceholder')}

@@ -184,41 +289,17 @@ export default function HomePage() {
-
-
-
-

- {t('common.developers')} -

-

- {t('pages.developers.ctaTitle')} -

-

- {t('pages.developers.ctaSubtitle')} -

-
- + {/* ── PDF Tools Grid ────────────────────────────────────────── */} +
+
+

+ {t('home.pdfTools')} +

+ + {t('common.allTools')} + +
-
- - {/* Tools Grid */} -
-

- {t('home.pdfTools')} -

{filteredPdfTools.map((tool) => ( -

+

{t('home.otherTools', 'Other Tools')}

@@ -257,49 +338,149 @@ export default function HomePage() { )}
- {/* Features / Why Choose Us */} -
-

- {t('home.featuresTitle', 'A smarter way to convert and edit online')} -

-
-
-
- + {/* ── Features / Why Choose Us ──────────────────────────────── */} +
+
+

+ {t('home.whyChooseLabel', 'Why Dociva')} +

+

+ {t('home.featuresTitle', 'A smarter way to work with files')} +

+
+ +
+ {[ + { + icon: Layers, + bg: 'bg-blue-100 dark:bg-blue-900/30', + color: 'text-blue-600 dark:text-blue-400', + titleKey: 'home.feature1Title', + titleDefault: 'One complete workspace', + descKey: 'home.feature1Desc', + descDefault: 'Edit, convert, compress, merge, split — without switching tabs.', + perks: [ + t('home.feature1Perk1', '30+ tools in one place'), + t('home.feature1Perk2', 'PDF, image & video support'), + ], + }, + { + icon: CheckCircle2, + bg: 'bg-emerald-100 dark:bg-emerald-900/30', + color: 'text-emerald-600 dark:text-emerald-400', + titleKey: 'home.feature2Title', + titleDefault: 'Accuracy you can trust', + descKey: 'home.feature2Desc', + descDefault: 'Pixel-perfect, editable output in seconds with zero quality loss.', + perks: [ + t('home.feature2Perk1', 'Preserve fonts & layouts'), + t('home.feature2Perk2', 'Batch-tested quality'), + ], + }, + { + icon: ShieldCheck, + bg: 'bg-violet-100 dark:bg-violet-900/30', + color: 'text-violet-600 dark:text-violet-400', + titleKey: 'home.feature3Title', + titleDefault: 'Built-in security', + descKey: 'home.feature3Desc', + descDefault: 'Files are automatically deleted after processing. No account required.', + perks: [ + t('home.feature3Perk1', 'Auto-deletion after 1 hour'), + t('home.feature3Perk2', 'Encrypted transfers'), + ], + }, + ].map(({ icon: Icon, bg, color, titleKey, titleDefault, descKey, descDefault, perks }) => ( +
+
+ +
+

+ {t(titleKey, titleDefault)} +

+

+ {t(descKey, descDefault)} +

+
    + {perks.map((perk) => ( +
  • + + {perk} +
  • + ))} +
-

- {t('home.feature1Title', 'One complete workspace')} -

-

- {t('home.feature1Desc', 'Edit, convert, compress, merge, split without switching tabs.')} + ))} +

+
+ + {/* ── Developer API Banner ──────────────────────────────────── */} +
+
+
+

+ {t('common.developers')} +

+

+ {t('pages.developers.ctaTitle')} +

+

+ {t('pages.developers.ctaSubtitle')}

-
-
- 100% -
-

- {t('home.feature2Title', 'Accuracy you can trust')} -

-

- {t('home.feature2Desc', 'Get pixel-perfect, editable files in seconds with zero quality loss.')} -

-
-
-
- -
-

- {t('home.feature3Title', 'Built-in security')} -

-

- {t('home.feature3Desc', 'Access files securely, protected by automatic encryption.')} -

+
+ + {t('pages.developers.openDocs')} + + + + {t('pages.developers.getApiKey')} +
- {/* Ad Slot - Bottom */} + {/* ── Bottom CTA Banner ─────────────────────────────────────── */} +
+ {/* Decorative blobs */} +
+
+ +
+

+ {t('home.ctaBannerLabel', 'Get started today')} +

+

+ {t('home.ctaBannerTitle', 'Ready to convert your files?')} +

+

+ {t('home.ctaBannerSubtitle', 'Join thousands of users who convert, compress, and edit their files every day — completely free.')} +

+
+ + {t('home.ctaBrowseTools', 'Browse All Tools')} + + + + {t('home.ctaCreateAccount', 'Create Free Account')} + +
+
+
+ + {/* ── Ad Slot - Bottom ──────────────────────────────────────── */} ); diff --git a/frontend/src/styles/global.css b/frontend/src/styles/global.css index c9fd3cc..86edbc0 100644 --- a/frontend/src/styles/global.css +++ b/frontend/src/styles/global.css @@ -114,17 +114,84 @@ animation: fadeSlideIn 0.15s ease-out; } -/* Hero upload zone — larger variant for the homepage */ +/* ────────────────────────────────────────────────────────────────────────── + Hero Upload Zone — premium glassmorphism card for the homepage + ────────────────────────────────────────────────────────────────────────── */ .hero-upload-zone { - @apply flex flex-col items-center justify-center rounded-3xl border-2 border-dashed border-slate-300 bg-gradient-to-b from-slate-50 to-white p-10 text-center transition-all duration-300 ease-in-out cursor-pointer sm:p-14 dark:border-slate-600 dark:from-slate-800/60 dark:to-slate-800/30; + @apply relative flex flex-col items-center justify-center rounded-3xl border border-slate-200/80 bg-white/80 backdrop-blur-sm p-10 text-center transition-all duration-300 ease-in-out cursor-pointer sm:p-14 shadow-sm dark:border-slate-700/60 dark:bg-slate-800/60 dark:backdrop-blur-sm; + background-image: radial-gradient(ellipse at top, rgba(219, 234, 254, 0.3) 0%, transparent 70%); +} + +.dark .hero-upload-zone { + background-image: radial-gradient(ellipse at top, rgba(30, 58, 138, 0.15) 0%, transparent 70%); +} + +.hero-upload-zone::before { + content: ''; + @apply absolute inset-0 rounded-3xl transition-opacity duration-300 opacity-0; + background: linear-gradient(135deg, rgba(59, 130, 246, 0.06) 0%, rgba(168, 85, 247, 0.04) 100%); +} + +.hero-upload-zone:hover::before { + @apply opacity-100; } .hero-upload-zone:hover { - @apply border-primary-400 bg-gradient-to-b from-primary-50 to-white shadow-xl -translate-y-0.5 dark:border-primary-500 dark:from-primary-900/20 dark:to-slate-800/30; + @apply border-primary-300 shadow-lg shadow-primary-100/50 -translate-y-1 dark:border-primary-600/60 dark:shadow-primary-900/30; } .hero-upload-zone.drag-active { - @apply border-primary-500 bg-gradient-to-b from-primary-100 to-primary-50/80 ring-2 ring-primary-300 shadow-2xl scale-[1.02] dark:border-primary-400 dark:from-primary-900/30 dark:to-primary-900/10 dark:ring-primary-600; + @apply border-primary-500 shadow-2xl shadow-primary-200/60 scale-[1.02] dark:border-primary-400 dark:shadow-primary-900/40; + background-image: radial-gradient(ellipse at top, rgba(191, 219, 254, 0.5) 0%, rgba(219, 234, 254, 0.2) 100%); +} + +/* ────────────────────────────────────────────────────────────────────────── + Glassmorphism card utility + ────────────────────────────────────────────────────────────────────────── */ +.glass-card { + @apply bg-white/70 backdrop-blur-md border border-white/50 shadow-sm dark:bg-slate-800/60 dark:border-slate-700/50; +} + +/* ────────────────────────────────────────────────────────────────────────── + Gradient hero mesh background + ────────────────────────────────────────────────────────────────────────── */ +.hero-gradient-bg { + background: + radial-gradient(ellipse 80% 60% at 50% -20%, rgba(59, 130, 246, 0.12) 0%, transparent 70%), + radial-gradient(ellipse 60% 40% at 80% 20%, rgba(168, 85, 247, 0.06) 0%, transparent 60%), + linear-gradient(180deg, #f8fafc 0%, #ffffff 100%); +} + +.dark .hero-gradient-bg { + background: + radial-gradient(ellipse 80% 60% at 50% -20%, rgba(30, 58, 138, 0.3) 0%, transparent 70%), + radial-gradient(ellipse 60% 40% at 80% 20%, rgba(88, 28, 135, 0.15) 0%, transparent 60%), + linear-gradient(180deg, #0f172a 0%, #0f172a 100%); +} + +/* ────────────────────────────────────────────────────────────────────────── + Shimmer loading effect + ────────────────────────────────────────────────────────────────────────── */ +@keyframes shimmer-sweep { + 0% { background-position: -200% center; } + 100% { background-position: 200% center; } +} + +.shimmer-text { + background: linear-gradient(90deg, #1e40af 30%, #7c3aed 50%, #1e40af 70%); + background-size: 200% auto; + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; + animation: shimmer-sweep 4s linear infinite; +} + +/* ────────────────────────────────────────────────────────────────────────── + How it Works — connector line between steps + ────────────────────────────────────────────────────────────────────────── */ +.step-connector { + @apply absolute top-8 left-[calc(50%+2.5rem)] hidden h-px w-[calc(100%-5rem)] sm:block; + background: linear-gradient(90deg, rgba(59, 130, 246, 0.4) 0%, rgba(59, 130, 246, 0.1) 100%); } /* Modal animations */ diff --git a/frontend/tailwind.config.js b/frontend/tailwind.config.js index d7214f7..1f9cc16 100644 --- a/frontend/tailwind.config.js +++ b/frontend/tailwind.config.js @@ -49,6 +49,40 @@ export default { transitionTimingFunction: { 'smooth': 'cubic-bezier(0.4, 0, 0.2, 1)', }, + animation: { + 'float': 'float 3s ease-in-out infinite', + 'shimmer': 'shimmer 2.5s linear infinite', + 'fade-up': 'fadeUp 0.5s ease-out forwards', + 'fade-in': 'fadeIn 0.4s ease-out forwards', + 'scale-in': 'scaleIn 0.3s ease-out forwards', + 'pulse-slow': 'pulse 3s cubic-bezier(0.4, 0, 0.6, 1) infinite', + }, + keyframes: { + float: { + '0%, 100%': { transform: 'translateY(0px)' }, + '50%': { transform: 'translateY(-10px)' }, + }, + shimmer: { + '0%': { backgroundPosition: '-200% center' }, + '100%': { backgroundPosition: '200% center' }, + }, + fadeUp: { + '0%': { opacity: '0', transform: 'translateY(24px)' }, + '100%': { opacity: '1', transform: 'translateY(0)' }, + }, + fadeIn: { + '0%': { opacity: '0' }, + '100%': { opacity: '1' }, + }, + scaleIn: { + '0%': { opacity: '0', transform: 'scale(0.92)' }, + '100%': { opacity: '1', transform: 'scale(1)' }, + }, + }, + backgroundImage: { + 'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))', + 'gradient-conic': 'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))', + }, }, }, plugins: [],