diff --git a/frontend/components.json b/frontend/components.json new file mode 100644 index 0000000..7e2d1a3 --- /dev/null +++ b/frontend/components.json @@ -0,0 +1,25 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": false, + "tsx": false, + "tailwind": { + "config": "tailwind.config.js", + "css": "src/index.css", + "baseColor": "neutral", + "cssVariables": true, + "prefix": "" + }, + "iconLibrary": "lucide", + "rtl": false, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "registries": { + "@react-bits": "https://reactbits.dev/r/{name}.json" + } +} diff --git a/frontend/jsconfig.json b/frontend/jsconfig.json new file mode 100644 index 0000000..3255bae --- /dev/null +++ b/frontend/jsconfig.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "paths": { + "@/*": [ + "./src/*" + ] + } + } +} \ No newline at end of file diff --git a/frontend/package-lock.json b/frontend/package-lock.json index f6e01de..7c0920e 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -8,13 +8,27 @@ "name": "frontend", "version": "0.0.0", "dependencies": { + "@iconify/react": "^6.0.2", + "@radix-ui/react-checkbox": "^1.3.3", + "@radix-ui/react-label": "^2.1.8", + "@radix-ui/react-select": "^2.2.6", + "@radix-ui/react-slot": "^1.2.4", + "@radix-ui/react-switch": "^1.2.6", "axios": "^1.13.5", - "framer-motion": "^10.0.1", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "embla-carousel-react": "^8.6.0", + "framer-motion": "^10.18.0", + "lucide-react": "^0.564.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-icons": "^5.5.0", "react-router-dom": "^6.8.1", - "tailwindcss": "^3.4.19" + "styled-components": "^6.3.9", + "tailwind-merge": "^3.4.1", + "tailwindcss": "^3.4.19", + "tailwindcss-animate": "^1.0.7", + "usehooks-ts": "^3.1.1" }, "devDependencies": { "@eslint/js": "^9.39.1", @@ -341,6 +355,12 @@ "license": "MIT", "optional": true }, + "node_modules/@emotion/unitless": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz", + "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==", + "license": "MIT" + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.27.3", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz", @@ -923,6 +943,44 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@floating-ui/core": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.4.tgz", + "integrity": "sha512-C3HlIdsBxszvm5McXlB8PeOEWfBhcGBTZGkGlWc2U0KFY5IwG5OQEuQ8rq52DZmcHDlPLd+YFBK+cZcytwIFWg==", + "license": "MIT", + "dependencies": { + "@floating-ui/utils": "^0.2.10" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.5.tgz", + "integrity": "sha512-N0bD2kIPInNHUHehXhMke1rBGs1dwqvC9O9KYMyyjK7iXt7GAhnro7UlcuYcGdS/yYOlq0MAVgrow8IbWJwyqg==", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.7.4", + "@floating-ui/utils": "^0.2.10" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.7.tgz", + "integrity": "sha512-0tLRojf/1Go2JgEVm+3Frg9A3IW8bJgKgdO0BN5RkF//ufuz2joZM63Npau2ff3J6lUVYgDSNzNkR+aH3IVfjg==", + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^1.7.5" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz", + "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==", + "license": "MIT" + }, "node_modules/@humanfs/core": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", @@ -975,6 +1033,27 @@ "url": "https://github.com/sponsors/nzakas" } }, + "node_modules/@iconify/react": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@iconify/react/-/react-6.0.2.tgz", + "integrity": "sha512-SMmC2sactfpJD427WJEDN6PMyznTFMhByK9yLW0gOTtnjzzbsi/Ke/XqsumsavFPwNiXs8jSiYeZTmLCLwO+Fg==", + "license": "MIT", + "dependencies": { + "@iconify/types": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/cyberalien" + }, + "peerDependencies": { + "react": ">=16" + } + }, + "node_modules/@iconify/types": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz", + "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==", + "license": "MIT" + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", @@ -1056,6 +1135,685 @@ "node": ">= 8" } }, + "node_modules/@radix-ui/number": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.1.tgz", + "integrity": "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==", + "license": "MIT" + }, + "node_modules/@radix-ui/primitive": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz", + "integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==", + "license": "MIT" + }, + "node_modules/@radix-ui/react-arrow": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.7.tgz", + "integrity": "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-checkbox": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.3.3.tgz", + "integrity": "sha512-wBbpv+NQftHDdG86Qc0pIyXk5IR3tM8Vd0nWLKDcX8nNn4nXFOFwsKuqw2okA/1D/mpaAkmuyndrPJTYDNZtFw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-previous": "1.1.1", + "@radix-ui/react-use-size": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-collection": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.7.tgz", + "integrity": "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-collection/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", + "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-context": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", + "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-direction": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.1.tgz", + "integrity": "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.11.tgz", + "integrity": "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-escape-keydown": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-guards": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.3.tgz", + "integrity": "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-scope": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.7.tgz", + "integrity": "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-id": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz", + "integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-label": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.8.tgz", + "integrity": "sha512-FmXs37I6hSBVDlO4y764TNz1rLgKwjJMQ0EGte6F3Cb3f4bIuHB/iLa/8I9VKkmOy+gNHq8rql3j686ACVV21A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-label/node_modules/@radix-ui/react-primitive": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.4.tgz", + "integrity": "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.4" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popper": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.8.tgz", + "integrity": "sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==", + "license": "MIT", + "dependencies": { + "@floating-ui/react-dom": "^2.0.0", + "@radix-ui/react-arrow": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-layout-effect": "1.1.1", + "@radix-ui/react-use-rect": "1.1.1", + "@radix-ui/react-use-size": "1.1.1", + "@radix-ui/rect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-portal": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz", + "integrity": "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-presence": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.5.tgz", + "integrity": "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-primitive/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-select": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.2.6.tgz", + "integrity": "sha512-I30RydO+bnn2PQztvo25tswPH+wFBjehVGtmagkU78yMdwTwVf12wnAOF+AeP8S2N8xD+5UPbGhkUfPyvT+mwQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/number": "1.1.1", + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-focus-guards": "1.1.3", + "@radix-ui/react-focus-scope": "1.1.7", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-popper": "1.2.8", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-layout-effect": "1.1.1", + "@radix-ui/react-use-previous": "1.1.1", + "@radix-ui/react-visually-hidden": "1.2.3", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-select/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-slot": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.4.tgz", + "integrity": "sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-switch": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-switch/-/react-switch-1.2.6.tgz", + "integrity": "sha512-bByzr1+ep1zk4VubeEVViV592vu2lHE2BZY5OnzehZqOOgogN80+mNtCqPkhn2gklJqOpxWgPoYTSnhBCqpOXQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-previous": "1.1.1", + "@radix-ui/react-use-size": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz", + "integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz", + "integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-effect-event": "0.0.2", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-effect-event": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz", + "integrity": "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-escape-keydown": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz", + "integrity": "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-callback-ref": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz", + "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-previous": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.1.tgz", + "integrity": "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-rect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.1.tgz", + "integrity": "sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==", + "license": "MIT", + "dependencies": { + "@radix-ui/rect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-size": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.1.tgz", + "integrity": "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-visually-hidden": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.3.tgz", + "integrity": "sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/rect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.1.tgz", + "integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==", + "license": "MIT" + }, "node_modules/@remix-run/router": { "version": "1.23.2", "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.2.tgz", @@ -1471,14 +2229,14 @@ "version": "15.7.15", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/@types/react": { "version": "18.3.28", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.28.tgz", "integrity": "sha512-z9VXpC7MWrhfWipitjNdgCauoMLRdIILQsAEV+ZesIzBq/oUlxk0m3ApZuMFCXdnS4U7KrI+l3WRUEGQ8K1QKw==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "@types/prop-types": "*", @@ -1489,12 +2247,18 @@ "version": "18.3.7", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz", "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", - "dev": true, + "devOptional": true, "license": "MIT", "peerDependencies": { "@types/react": "^18.0.0" } }, + "node_modules/@types/stylis": { + "version": "4.2.7", + "resolved": "https://registry.npmjs.org/@types/stylis/-/stylis-4.2.7.tgz", + "integrity": "sha512-VgDNokpBoKF+wrdvhAAfS55OMQpL6QRglwTwNC3kIgBrzZxA4WsFj+2eLfEA/uMUDzBcEhYmjSbwQakn/i3ajA==", + "license": "MIT" + }, "node_modules/@vitejs/plugin-react": { "version": "5.1.4", "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.1.4.tgz", @@ -1616,6 +2380,18 @@ "dev": true, "license": "Python-2.0" }, + "node_modules/aria-hidden": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz", + "integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -1788,6 +2564,15 @@ "node": ">= 6" } }, + "node_modules/camelize": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.1.tgz", + "integrity": "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/caniuse-lite": { "version": "1.0.30001769", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001769.tgz", @@ -1862,6 +2647,27 @@ "node": ">= 6" } }, + "node_modules/class-variance-authority": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz", + "integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==", + "license": "Apache-2.0", + "dependencies": { + "clsx": "^2.1.1" + }, + "funding": { + "url": "https://polar.sh/cva" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -1932,6 +2738,26 @@ "node": ">= 8" } }, + "node_modules/css-color-keywords": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", + "integrity": "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==", + "license": "ISC", + "engines": { + "node": ">=4" + } + }, + "node_modules/css-to-react-native": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz", + "integrity": "sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==", + "license": "MIT", + "dependencies": { + "camelize": "^1.0.0", + "css-color-keywords": "^1.0.0", + "postcss-value-parser": "^4.0.2" + } + }, "node_modules/cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", @@ -1948,7 +2774,6 @@ "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "dev": true, "license": "MIT" }, "node_modules/debug": { @@ -1985,6 +2810,12 @@ "node": ">=0.4.0" } }, + "node_modules/detect-node-es": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", + "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", + "license": "MIT" + }, "node_modules/didyoumean": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", @@ -2018,6 +2849,34 @@ "dev": true, "license": "ISC" }, + "node_modules/embla-carousel": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/embla-carousel/-/embla-carousel-8.6.0.tgz", + "integrity": "sha512-SjWyZBHJPbqxHOzckOfo8lHisEaJWmwd23XppYFYVh10bU66/Pn5tkVkbkCMZVdbUE5eTCI2nD8OyIP4Z+uwkA==", + "license": "MIT" + }, + "node_modules/embla-carousel-react": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/embla-carousel-react/-/embla-carousel-react-8.6.0.tgz", + "integrity": "sha512-0/PjqU7geVmo6F734pmPqpyHqiM99olvyecY7zdweCw+6tKEXnrE90pBiBbMMU8s5tICemzpQ3hi5EpxzGW+JA==", + "license": "MIT", + "dependencies": { + "embla-carousel": "8.6.0", + "embla-carousel-reactive-utils": "8.6.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.1 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + } + }, + "node_modules/embla-carousel-reactive-utils": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/embla-carousel-reactive-utils/-/embla-carousel-reactive-utils-8.6.0.tgz", + "integrity": "sha512-fMVUDUEx0/uIEDM0Mz3dHznDhfX+znCCDCeIophYb1QGVM7YThSWX+wz11zlYwWFOr74b4QLGg0hrGPJeG2s4A==", + "license": "MIT", + "peerDependencies": { + "embla-carousel": "8.6.0" + } + }, "node_modules/es-define-property": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", @@ -2598,6 +3457,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-nonce": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", + "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/get-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", @@ -2948,6 +3816,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "license": "MIT" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -2977,6 +3851,15 @@ "yallist": "^3.0.2" } }, + "node_modules/lucide-react": { + "version": "0.564.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.564.0.tgz", + "integrity": "sha512-JJ8GVTQqFwuliifD48U6+h7DXEHdkhJ/E87kksGByII3qHxtPciVb8T8woQONHBQgHVOl7rSMrrip3SeVNy7Fg==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -3502,6 +4385,53 @@ "node": ">=0.10.0" } }, + "node_modules/react-remove-scroll": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.2.tgz", + "integrity": "sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==", + "license": "MIT", + "dependencies": { + "react-remove-scroll-bar": "^2.3.7", + "react-style-singleton": "^2.2.3", + "tslib": "^2.1.0", + "use-callback-ref": "^1.3.3", + "use-sidecar": "^1.1.3" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-remove-scroll-bar": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz", + "integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==", + "license": "MIT", + "dependencies": { + "react-style-singleton": "^2.2.2", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/react-router": { "version": "6.30.3", "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.30.3.tgz", @@ -3534,6 +4464,28 @@ "react-dom": ">=16.8" } }, + "node_modules/react-style-singleton": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz", + "integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==", + "license": "MIT", + "dependencies": { + "get-nonce": "^1.0.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -3708,6 +4660,12 @@ "semver": "bin/semver.js" } }, + "node_modules/shallowequal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", + "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==", + "license": "MIT" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -3753,6 +4711,88 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/styled-components": { + "version": "6.3.9", + "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.3.9.tgz", + "integrity": "sha512-J72R4ltw0UBVUlEjTzI0gg2STOqlI9JBhQOL4Dxt7aJOnnSesy0qJDn4PYfMCafk9cWOaVg129Pesl5o+DIh0Q==", + "license": "MIT", + "dependencies": { + "@emotion/is-prop-valid": "1.4.0", + "@emotion/unitless": "0.10.0", + "@types/stylis": "4.2.7", + "css-to-react-native": "3.2.0", + "csstype": "3.2.3", + "postcss": "8.4.49", + "shallowequal": "1.1.0", + "stylis": "4.3.6", + "tslib": "2.8.1" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/styled-components" + }, + "peerDependencies": { + "react": ">= 16.8.0", + "react-dom": ">= 16.8.0" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/styled-components/node_modules/@emotion/is-prop-valid": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.4.0.tgz", + "integrity": "sha512-QgD4fyscGcbbKwJmqNvUMSE02OsHUa+lAWKdEUIJKgqe5IwRSKd7+KhibEWdaKwgjLj0DRSHA9biAIqGBk05lw==", + "license": "MIT", + "dependencies": { + "@emotion/memoize": "^0.9.0" + } + }, + "node_modules/styled-components/node_modules/@emotion/memoize": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", + "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==", + "license": "MIT" + }, + "node_modules/styled-components/node_modules/postcss": { + "version": "8.4.49", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", + "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", + "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.7", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/stylis": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.6.tgz", + "integrity": "sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==", + "license": "MIT" + }, "node_modules/sucrase": { "version": "3.35.1", "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz", @@ -3800,6 +4840,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/tailwind-merge": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.4.1.tgz", + "integrity": "sha512-2OA0rFqWOkITEAOFWSBSApYkDeH9t2B3XSJuI4YztKBzK3mX0737A2qtxDZ7xkw9Zfh0bWl+r34sF3HXV+Ig7Q==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" + } + }, "node_modules/tailwindcss": { "version": "3.4.19", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.19.tgz", @@ -3837,6 +4887,15 @@ "node": ">=14.0.0" } }, + "node_modules/tailwindcss-animate": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/tailwindcss-animate/-/tailwindcss-animate-1.0.7.tgz", + "integrity": "sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==", + "license": "MIT", + "peerDependencies": { + "tailwindcss": ">=3.0.0 || insiders" + } + }, "node_modules/thenify": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", @@ -3952,6 +5011,64 @@ "punycode": "^2.1.0" } }, + "node_modules/use-callback-ref": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz", + "integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-sidecar": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz", + "integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==", + "license": "MIT", + "dependencies": { + "detect-node-es": "^1.1.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/usehooks-ts": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/usehooks-ts/-/usehooks-ts-3.1.1.tgz", + "integrity": "sha512-I4diPp9Cq6ieSUH2wu+fDAVQO43xwtulo+fKEidHUwZPnYImbtkTjzIJYcDcJqxgmX31GVqNFURodvcgHcW0pA==", + "license": "MIT", + "dependencies": { + "lodash.debounce": "^4.0.8" + }, + "engines": { + "node": ">=16.15.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17 || ^18 || ^19 || ^19.0.0-rc" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", diff --git a/frontend/package.json b/frontend/package.json index 381582f..5559983 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -10,13 +10,27 @@ "preview": "vite preview" }, "dependencies": { + "@iconify/react": "^6.0.2", + "@radix-ui/react-checkbox": "^1.3.3", + "@radix-ui/react-label": "^2.1.8", + "@radix-ui/react-select": "^2.2.6", + "@radix-ui/react-slot": "^1.2.4", + "@radix-ui/react-switch": "^1.2.6", "axios": "^1.13.5", - "framer-motion": "^10.0.1", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "embla-carousel-react": "^8.6.0", + "framer-motion": "^10.18.0", + "lucide-react": "^0.564.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-icons": "^5.5.0", "react-router-dom": "^6.8.1", - "tailwindcss": "^3.4.19" + "styled-components": "^6.3.9", + "tailwind-merge": "^3.4.1", + "tailwindcss": "^3.4.19", + "tailwindcss-animate": "^1.0.7", + "usehooks-ts": "^3.1.1" }, "devDependencies": { "@eslint/js": "^9.39.1", diff --git a/frontend/public/Gambar1.jpg b/frontend/public/Gambar1.jpg new file mode 100644 index 0000000..0ff8fde Binary files /dev/null and b/frontend/public/Gambar1.jpg differ diff --git a/frontend/public/Gambar2.jpeg b/frontend/public/Gambar2.jpeg new file mode 100644 index 0000000..aac5644 Binary files /dev/null and b/frontend/public/Gambar2.jpeg differ diff --git a/frontend/public/Gambar3.jpeg b/frontend/public/Gambar3.jpeg new file mode 100644 index 0000000..6560a98 Binary files /dev/null and b/frontend/public/Gambar3.jpeg differ diff --git a/frontend/public/gambar_header.png b/frontend/public/gambar_header.png new file mode 100644 index 0000000..9a2dba7 Binary files /dev/null and b/frontend/public/gambar_header.png differ diff --git a/frontend/public/logo_polije.png b/frontend/public/logo_polije.png new file mode 100644 index 0000000..637fd70 Binary files /dev/null and b/frontend/public/logo_polije.png differ diff --git a/frontend/public/logo_polijecare.png b/frontend/public/logo_polijecare.png index f1eecd6..f16df76 100644 Binary files a/frontend/public/logo_polijecare.png and b/frontend/public/logo_polijecare.png differ diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index d895b3a..0c38b8e 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -4,6 +4,8 @@ import { ThemeProvider } from './contexts/ThemeContext'; import { AuthProvider, useAuth } from './contexts/AuthContext'; import ProtectedRoute from './components/ProtectedRoute'; +import { ScrollParticles } from './components/ui/scroll-particles'; + // Pages import LandingPage from './pages/LandingPage'; import UserDashboard from './pages/UserDashboard'; @@ -13,6 +15,7 @@ import OperatorDashboard from './pages/OperatorDashboard'; function App() { return ( + @@ -22,36 +25,36 @@ function App() { } /> } /> } /> - + {/* Protected Dashboard Routes */} - - } + } /> - - } + } /> - - } + } /> - + {/* Redirect */} } /> - + {/* 404 */} } /> @@ -64,11 +67,11 @@ function App() { // Redirect component for role-based navigation const RedirectDashboard = () => { const { user, isAuthenticated } = useAuth(); - + if (!isAuthenticated || !user) { return ; } - + switch (user.role) { case 'user': return ; diff --git a/frontend/src/components/About.jsx b/frontend/src/components/About.jsx index ddeafbd..51f49a3 100644 --- a/frontend/src/components/About.jsx +++ b/frontend/src/components/About.jsx @@ -1,239 +1,5 @@ -import React from 'react'; -import { motion } from 'framer-motion'; -import { fadeIn, slideUp, slideLeft, staggerChildren } from '../utils/motionVariants'; +import AboutSection from "@/components/ui/about-section"; -const About = () => { - const features = [ - { - icon: ( - - - - ), - title: 'Perlindungan', - description: 'Melindungi korban dan saksi dari segala bentuk ancaman atau intimidasi' - }, - { - icon: ( - - - - ), - title: 'Pendampingan', - description: 'Memberikan dukungan psikologis dan hukum yang dibutuhkan' - }, - { - icon: ( - - - - ), - title: 'Kerahasiaan', - description: 'Menjaga identitas dan informasi pelapor dengan ketat' - }, - { - icon: ( - - - - ), - title: 'Keadilan', - description: 'Memastikan proses yang adil dan transparan untuk semua pihak' - } - ]; - - return ( -
- {/* Background Pattern */} -
-
-
-
- -
- {/* Section Header */} - - - Tentang Satgas PPKPT - - - Satuan Tugas Pencegahan dan Penanganan Kekerasan Seksual Politeknik Negeri Jember - - - -
- {/* Left Content - Logo and Description */} - - {/* PolijeCare Logo */} -
- -
- {/* Outer gradient ring */} -
-
-
- PolijeCare -
-
-
- {/* Floating decoration */} - - - - - -
-
-
- - -

- Layanan Pengaduan dan Pendampingan Terpercaya -

-

- PolijeCare merupakan kanal resmi pengaduan Satgas PPKPT Politeknik Negeri Jember yang menangani laporan kekerasan seksual secara empati, profesional, dan menjaga kerahasiaan. -

-

- Kami berkomitmen untuk menciptakan lingkungan kampus yang aman, mendukung, dan bebas dari kekerasan seksual bagi seluruh sivitas akademika. -

-
- - {/* Stats */} - -
-
24/7
-
Layanan Darurat
-
-
-
100%
-
Rahasia Terjamin
-
-
-
- - {/* Right Content - Features Grid */} - - - {features.map((feature, index) => ( - -
-
- {feature.icon} -
-
-

- {feature.title} -

-

- {feature.description} -

-
-
-
- ))} -
- - {/* Quote */} - -

- "Setiap individu berhak mendapatkan perlindungan dan rasa aman dalam menempuh pendidikan. Mari bersama-sama menjaga kampus kita sebagai tempat yang aman dan mendukung bagi semua." -

-
- — Satgas PPKPT Polije -
-
-
-
-
-
- ); -}; - -export default About; +export default function About() { + return ; +} diff --git a/frontend/src/components/Articles.jsx b/frontend/src/components/Articles.jsx index 44097c9..29ad5c1 100644 --- a/frontend/src/components/Articles.jsx +++ b/frontend/src/components/Articles.jsx @@ -1,7 +1,8 @@ import React, { useState, useEffect } from 'react'; import { motion } from 'framer-motion'; import { Link } from 'react-router-dom'; -import { fadeIn, slideUp, staggerChildren } from '../utils/motionVariants'; +import { fadeIn, slideUp } from '../utils/motionVariants'; +import { Gallery4 } from './gallery4'; const Articles = () => { const [articles, setArticles] = useState([]); @@ -13,240 +14,126 @@ const Articles = () => { const mockArticles = [ { id: 1, - title: 'Pentingnya Menjaga Lingkungan Kampus Aman dari Kekerasan Seksual', - slug: 'pentingnya-menjaga-lingkungan-kampus-aman-dari-kekerasan-seksual', - image: 'articles/safe-campus.jpg', - content: 'Lingkungan kampus yang aman adalah hak setiap sivitas akademika.', + title: 'Guru Besar UGM Diduga Lakukan Kekerasan Seksual, Diberhentikan Sementara', + slug: 'https://www.detik.com/jateng/berita/d-6204001/dugaan-kekerasan-seksual-guru-besar-ugm-dipecat-sebagai-dosen', + image: 'https://images.unsplash.com/photo-1592280771800-45cb10bd3dcf?q=80&w=1740&auto=format&fit=crop', + content: 'Universitas Gadjah Mada (UGM) mengambil tindakan tegas dengan memberhentikan sementara seorang guru besar yang diduga terlibat kasus kekerasan seksual.', is_published: true, - published_at: '2024-01-07T00:00:00.000000Z' + published_at: '2025-01-20T00:00:00.000000Z' }, { id: 2, - title: 'Prosedur Pelaporan Kasus Kekerasan Seksual di Polije', - slug: 'prosedur-pelaporan-kasus-kekerasan-seksual-di-polije', - image: 'articles/reporting-procedure.jpg', - content: 'Prosedur pelaporan kasus kekerasan seksual di Politeknik Negeri Jember.', + title: 'Rektor Universitas Pancasila Nonaktif Jalani Pemeriksaan Kasus Pelecehan', + slug: 'https://metro.tempo.co/read/1840000/kasus-pelecehan-seksual-rektor-universitas-pancasila', + image: 'https://images.unsplash.com/photo-1541339907198-e08756dedf3f?q=80&w=1740&auto=format&fit=crop', + content: 'Polda Metro Jaya memeriksa Rektor Universitas Pancasila nonaktif terkait laporan dugaan pelecehan seksual terhadap pegawai kampus.', is_published: true, - published_at: '2024-01-05T00:00:00.000000Z' + published_at: '2024-06-15T00:00:00.000000Z' }, { id: 3, - title: 'Hak dan Kewajiban Korban dan Pelapor Kekerasan Seksual', - slug: 'hak-dan-kewajiban-korban-dan-pelapor-kekerasan-seksual', - image: 'articles/rights-responsibilities.jpg', - content: 'Sebagai korban atau pelapor kekerasan seksual, Anda memiliki hak-hak.', + title: 'Unand Resmikan Satgas PPK, Perluas Cakupan Penanganan Kekerasan', + slug: 'https://www.unand.ac.id/id/berita-peristiwa/berita/item/5799-resmikan-satgas-ppk-rektor-unand-kawal-kampus-aman.html', + image: 'https://images.unsplash.com/photo-1523050854058-8df90110c9f1?q=80&w=1740&auto=format&fit=crop', + content: 'Universitas Andalas meresmikan Satuan Tugas Pencegahan dan Penanganan Kekerasan (PPK) untuk menciptakan lingkungan kampus yang aman dan inklusif.', is_published: true, - published_at: '2024-01-03T00:00:00.000000Z' + published_at: '2024-11-10T00:00:00.000000Z' + }, + { + id: 4, + title: 'Mahasiswa Unsri Tuntut Penuntasan Kasus Pelecehan Seksual', + slug: 'https://www.cnnindonesia.com/nasional/20211203145209-12-729000/mahasiswa-unsri-demo-tuntut-usut-tuntas-dugaan-pelecehan-seksual', + image: 'https://images.unsplash.com/photo-1555848960-8c3af5e4860c?q=80&w=1740&auto=format&fit=crop', + content: 'Ratusan mahasiswa Universitas Sriwijaya menggelar aksi damai menuntut pengusutan tuntas kasus dugaan pelecehan seksual oleh oknum dosen.', + is_published: true, + published_at: '2024-10-05T00:00:00.000000Z' + }, + { + id: 5, + title: 'Kemendikbudristek Cabut Izin Kampus yang Abaikan Kasus Kekerasan Seksual', + slug: 'https://nasional.kompas.com/read/2023/06/07/11261391/izin-23-perguruan-tinggi-dicabut-ada-kampus-yang-abaikan-kasus-kekerasan', + image: 'https://images.unsplash.com/photo-1450101499163-c8848c66ca85?q=80&w=1740&auto=format&fit=crop', + content: 'Kemendikbudristek mengambil langkah tegas mencabut izin operasional perguruan tinggi yang terbukti melakukan pelanggaran berat, termasuk pembiaran kekerasan seksual.', + is_published: true, + published_at: '2024-02-01T00:00:00.000000Z' + }, + { + id: 6, + title: 'Puan Maharani: Kampus Harus Jadi Ruang Aman Bebas Kekerasan Seksual', + slug: 'https://www.dpr.go.id/berita/detail/id/35000/t/Ketua+DPR+Minta+Kampus+Jadi+Ruang+Aman+dari+Kekerasan+Seksual', + image: 'https://images.unsplash.com/photo-1557804506-669a67965ba0?q=80&w=1740&auto=format&fit=crop', + content: 'Ketua DPR RI Puan Maharani menegaskan pentingnya komitmen perguruan tinggi dalam menciptakan ruang aman bebas dari segala bentuk kekerasan seksual.', + is_published: true, + published_at: '2024-09-15T00:00:00.000000Z' + }, + { + id: 7, + title: 'Komnas Perempuan: Kekerasan Seksual di Lingkungan Pendidikan Masih Mengkhawatirkan', + slug: 'https://www.kompas.id/baca/humaniora/2024/03/07/kekerasan-seksual-di-lingkungan-pendidikan-masih-tinggi', + image: 'https://images.unsplash.com/photo-1573164713988-8665fc963095?q=80&w=1740&auto=format&fit=crop', + content: 'Komnas Perempuan mencatat angka kekerasan seksual di lingkungan pendidikan masih tinggi dan memerlukan penanganan sistemik yang lebih serius.', + is_published: true, + published_at: '2024-03-08T00:00:00.000000Z' + }, + { + id: 8, + title: 'Pentingnya Pendidikan Seksual di Kampus untuk Cegah Tindakan Asusila', + slug: 'https://edukasi.kompas.com/read/2021/11/12/100000371/pentingnya-pendidikan-seksual-sejak-dini-untuk-cegah-pelecehan', + image: 'https://images.unsplash.com/photo-1544531586-fde5298cdd40?q=80&w=1740&auto=format&fit=crop', + content: 'Pendidikan seksual yang komprehensif di lingkungan kampus dinilai efektif sebagai langkah preventif untuk mencegah terjadinya tindak asusila.', + is_published: true, + published_at: '2024-01-10T00:00:00.000000Z' } ]; - + setArticles(mockArticles); setLoading(false); }, []); - const formatDate = (dateString) => { - const options = { year: 'numeric', month: 'long', day: 'numeric' }; - return new Date(dateString).toLocaleDateString('id-ID', options); - }; + const galleryItems = articles.map(article => ({ + id: article.id.toString(), + title: article.title, + description: article.content, + href: article.slug, + image: article.image + })); + + const SectionTitle = ( + + Artikel & Pengumuman + + ); return ( -
- {/* Background Pattern */} -
-
-
+
+ {/* Background Decorations */} +
+
+
-
- {/* Section Header */} - - - Artikel & Pengumuman - - - Dapatkan informasi terbaru seputar layanan, edukasi, dan pengumuman penting dari Satgas PPKPT Polije. - - - - {/* Loading State */} +
{loading && ( - -
-
-

Memuat artikel...

-
-
+
+
+
)} - {/* Error State */} {error && ( - -
-
- - - -
-

Gagal Memuat Artikel

-

{error}

- -
-
+
+

{error}

+
)} - {/* Articles Grid */} {!loading && !error && ( - <> - {articles.length === 0 ? ( - -
-
- - - -
-

Belum Ada Artikel

-

Belum ada artikel atau pengumuman yang tersedia saat ini.

-
-
- ) : ( - - {articles.map((article, index) => ( - - {/* Article Image */} -
- {article.image ? ( - {article.title} - ) : ( -
- - - -
- )} -
-
- - {/* Article Content */} -
- {/* Date */} -
- - - - {formatDate(article.published_at)} -
- - {/* Title */} -

- {article.title} -

- - {/* Excerpt */} -

- {article.excerpt} -

- - {/* Read More Link */} -
- - Baca Selengkapnya - - - - -
-
-
- ))} -
- )} - - )} - - {/* View All Button */} - {!loading && !error && articles.length > 0 && ( - - - Lihat Semua Artikel - - - - - + )}
-
+ ); }; diff --git a/frontend/src/components/Hero.jsx b/frontend/src/components/Hero.jsx index cd86fbd..4593712 100644 --- a/frontend/src/components/Hero.jsx +++ b/frontend/src/components/Hero.jsx @@ -1,9 +1,12 @@ import React from 'react'; -import { Link } from 'react-router-dom'; +import { Link, useNavigate } from 'react-router-dom'; +import { Component as ReportButton } from './button'; +import { Icon } from '@iconify/react'; import { motion } from 'framer-motion'; import { fadeIn, slideUp, slideLeft, slideRight } from '../utils/motionVariants'; const Hero = ({ heroData }) => { + const navigate = useNavigate(); const defaultHero = { title: 'Aman Bicara, Aman Melapor', subtitle: 'Satgas PPKPT Politeknik Negeri Jember', @@ -13,32 +16,32 @@ const Hero = ({ heroData }) => { const hero = heroData || defaultHero; return ( -
{/* Background Decorations */} -
- + - { />
-
-
+
+
{/* Left Content */} - - - { > {hero.title} - - { - - {hero.description} + {hero.description ? ( + hero.description + ) : ( + <> + Kami siap mendengar dan membantu Anda dengan{' '} + profesionalisme dan{' '} + kerahasiaan terjamin.{' '} + Setiap laporan akan ditangani dengan{' '} + empati dan{' '} + seksama. + + )} - - { whileHover={{ scale: 1.05 }} whileTap={{ scale: 0.95 }} > - Butuh Bantuan Darurat - - + } + title="Butuh Bantuan Darurat" + size="sm" + className="rounded-full bg-red-600 hover:bg-red-700 border-0" + gradientLight={{ from: "from-red-600", via: "via-red-600", to: "to-red-600" }} + gradientDark={{ from: "from-red-600", via: "via-red-600", to: "to-red-600" }} + onClick={() => window.open('https://wa.me/6281234567890', '_blank')} + /> + + { whileHover={{ scale: 1.05 }} whileTap={{ scale: 0.95 }} > - - Buat Laporan - + } + title="Buat Laporan" + size="sm" + className="rounded-full bg-[#191970] hover:bg-blue-900 border-0" + gradientLight={{ from: "from-[#191970]", via: "via-[#191970]", to: "to-[#191970]" }} + gradientDark={{ from: "from-[#191970]", via: "via-[#191970]", to: "to-[#191970]" }} + onClick={() => navigate('/artikel')} + /> {/* Trust Indicators */} - -
-
- 100% Rahasia -
- -
-
- Profesional -
- -
-
- 24/7 Support -
-
+ {/* Right Content - Logo & Branding */} - { transition={{ duration: 0.8, delay: 0.3 }} > {/* Main Logo Container */} - -
-
- {/* Logo Image */} - -
- Polijecare Logo -
- {/* Glow Effect */} -
-
- - {/* Brand Text */} - -

Polijecare

-

Satgas PPKPT Polije

-
-
-
-
+ header gambar
- {/* Background Shape */} -
+ {/* Background Shape */} +
+ + {/* Brand Stats Banner - Glassmorphic Light Design */} +
- {/* Scroll Indicator */} - - - Scroll ke bawah -
-
-
-
-
+
); }; diff --git a/frontend/src/components/Navbar.jsx b/frontend/src/components/Navbar.jsx index 03cdb4d..7a6deea 100644 --- a/frontend/src/components/Navbar.jsx +++ b/frontend/src/components/Navbar.jsx @@ -1,13 +1,17 @@ import React, { useState, useEffect } from 'react'; +import { Home, Info, FileText, BookOpen, Phone } from 'lucide-react'; +import { ExpandableTabs } from "@/components/ui/expandable-tabs"; import { Link, useNavigate, useLocation } from 'react-router-dom'; import { motion, AnimatePresence } from 'framer-motion'; import { useAuth } from '../hooks/useAuth'; import { fadeIn, slideDown } from '../utils/motionVariants'; -import ThemeToggle from './ThemeToggle'; +import Switch from './sky-toggle'; import LoginModal from './LoginModal'; + const Navbar = () => { const [isScrolled, setIsScrolled] = useState(false); + const [activeLink, setActiveLink] = useState('#hero'); const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false); const [isLoginModalOpen, setIsLoginModalOpen] = useState(false); const { isAuthenticated, user, logout } = useAuth(); @@ -22,14 +26,62 @@ const Navbar = () => { { name: 'Kontak', href: '#contact' } ]; + const tabs = [ + { title: "Beranda", icon: Home }, + { title: "Tentang Kami", icon: Info }, + { title: "Cara Melapor", icon: FileText }, + { title: "Artikel", icon: BookOpen }, + { title: "Kontak", icon: Phone }, + ]; + + const handleNavClick = (href) => { + if (href.startsWith('#')) { + const element = document.querySelector(href); + if (element) { + element.scrollIntoView({ behavior: 'smooth' }); + } + } + setIsMobileMenuOpen(false); + }; + + + useEffect(() => { + let ticking = false; const handleScroll = () => { - setIsScrolled(window.scrollY > 20); + if (!ticking) { + requestAnimationFrame(() => { + // Logic for switching navbar type (Standard vs Expandable) + const aboutSection = document.getElementById('about'); + const threshold = aboutSection ? aboutSection.offsetTop - 400 : window.innerHeight - 200; + setIsScrolled(window.scrollY > threshold); + + // ScrollSpy Logic + const sections = navLinks.map(link => link.href.substring(1)); + let currentSection = ""; + + for (const section of sections) { + const element = document.getElementById(section); + if (element) { + const rect = element.getBoundingClientRect(); + if (rect.top <= 150 && rect.bottom >= 150) { + currentSection = "#" + section; + } + } + } + + if (currentSection && currentSection !== activeLink) { + setActiveLink(currentSection); + } + ticking = false; + }); + ticking = true; + } }; - window.addEventListener('scroll', handleScroll); + window.addEventListener('scroll', handleScroll, { passive: true }); return () => window.removeEventListener('scroll', handleScroll); - }, []); + }, [navLinks, activeLink]); const handleLogout = () => { logout(); @@ -38,7 +90,7 @@ const Navbar = () => { const handleDashboardRedirect = () => { if (!user) return; - + switch (user.role) { case 'user': navigate('/user/dashboard'); @@ -55,226 +107,248 @@ const Navbar = () => { } }; - const handleNavClick = (href) => { - if (href.startsWith('#')) { - const element = document.querySelector(href); - if (element) { - element.scrollIntoView({ behavior: 'smooth' }); - } - } - setIsMobileMenuOpen(false); - }; - return ( <> - -
-
- {/* Logo */} - -
- Polijecare Logo -
- - Polijecare - - - - {/* Desktop Navigation */} -
- - {navLinks.map((link, index) => ( - - {link.href.startsWith('#') ? ( - - ) : ( - - {link.name} - - - )} - - ))} - - - {/* Auth Buttons & Theme Toggle */} -
- {isAuthenticated ? ( - <> - - - - ) : ( - - )} - - {/* Theme Toggle */} - -
-
- - {/* Mobile menu button */} -
- -
-
-
- - {/* Mobile menu */} - - {isMobileMenuOpen && ( - -
- {navLinks.map((link, index) => ( - - {link.href.startsWith('#') ? ( - - ) : ( - setIsMobileMenuOpen(false)} - className="block w-full px-4 py-3 text-gray-600 dark:text-gray-300 hover:text-primary dark:hover:text-primary-light hover:bg-gray-50 dark:hover:bg-gray-700 rounded-lg transition-all duration-200 font-medium" - > - {link.name} - - )} - - ))} - -
+ + {isScrolled ? ( + + link.href === activeLink)} + onChange={(index) => { + if (index !== null) { + const href = navLinks[index].href; + setActiveLink(href); + handleNavClick(href); + } + }} + trailingElement={ +
+ {isAuthenticated ? ( <> ) : ( )}
+ } + /> +
+ ) : ( + +
+
+ {/* Logo Section - Left */} + + Logo Polije + Polijecare Logo + + + {/* Centered Navigation Links */} +
+ {navLinks.map((link) => ( + + ))} +
+ + {/* Right Section - Auth & Theme */} +
+ + + {isAuthenticated ? ( + <> + + + + ) : ( + + )} +
+ + {/* Mobile menu button */} +
+ +
- - )} - - - - {/* Login Modal */} - setIsLoginModalOpen(false)} +
+ + {/* Mobile menu */} + + {isMobileMenuOpen && ( + +
+ {navLinks.map((link, index) => ( + + {link.href.startsWith('#') ? ( + + ) : ( + setIsMobileMenuOpen(false)} + className="block w-full px-4 py-3 text-gray-600 dark:text-gray-300 hover:text-primary dark:hover:text-primary-light hover:bg-gray-50 dark:hover:bg-gray-700 rounded-lg transition-all duration-200 font-medium" + > + {link.name} + + )} + + ))} + +
+ {isAuthenticated ? ( + <> + + + + ) : ( + + )} +
+
+
+ )} +
+
+ )} +
+ + setIsLoginModalOpen(false)} /> ); diff --git a/frontend/src/components/Services.jsx b/frontend/src/components/Services.jsx index 33f4ebd..3659a0c 100644 --- a/frontend/src/components/Services.jsx +++ b/frontend/src/components/Services.jsx @@ -2,99 +2,119 @@ import React from 'react'; import { Link } from 'react-router-dom'; import { motion } from 'framer-motion'; import { fadeIn, slideUp, staggerChildren } from '../utils/motionVariants'; +import { + MessageCircle, + FileText, + Shield, + Users, + HeartHandshake, + Phone, + CheckCircle2, + Search, + Gavel, + Smile +} from 'lucide-react'; +import { SpotlightCard } from './ui/spotlight-card'; +import { FlowButton } from './ui/flow-button'; const Services = () => { const reportingMethods = [ { id: 'whatsapp', title: 'Via WhatsApp', - description: 'Laporkan secara langsung melalui WhatsApp untuk respons cepat dan konsultasi awal dengan tim kami.', - icon: ( - - - - ), - features: ['Respons 24/7', 'Konsultasi awal', 'Bimbingan langkah selanjutnya'], - buttonText: 'Butuh Bantuan Darurat', - buttonColor: 'bg-accent hover:bg-accent-dark', - buttonLink: 'https://wa.me/6281234567890' + description: 'Layanan cepat tanggap untuk konsultasi awal dan pelaporan darurat. Terhubung langsung dengan tim satgas kami.', + icon: , + features: ['Respons 24/7', 'Konsultasi Privat', 'Pendampingan Awal'], + buttonText: 'Chat WhatsApp Sekarang', + buttonColor: 'bg-green-600 hover:bg-green-700', + buttonLink: 'https://wa.me/6281234567890', + gradient: 'from-green-50 to-emerald-50 dark:from-green-900/20 dark:to-emerald-900/20', + iconColor: 'text-green-600 dark:text-green-400', + spotlightColor: 'rgba(34, 197, 94, 0.2)' // Green glow for WhatsApp }, { id: 'form', - title: 'Form Pengaduan Online', - description: 'Isi form pengaduan secara online dengan detail lengkap dan upload bukti pendukung untuk proses yang lebih terstruktur.', - icon: ( - - - - - ), - features: ['Form terstruktur', 'Upload bukti', 'Tracking status laporan'], - buttonText: 'Laporkan Sekarang', - buttonColor: 'bg-primary hover:bg-primary-dark', - buttonLink: '/artikel' + title: 'Form Pengaduan', + description: 'Saluran resmi untuk pelaporan mendetail. Mendukung lampiran bukti dan kronologi lengkap untuk investigasi.', + icon: , + features: ['Form Terstruktur', 'Upload Bukti Aman', 'Tracking Status'], + buttonText: 'Isi Form Laporan', + buttonColor: 'bg-[#191970] hover:bg-blue-900', + buttonLink: '/artikel', + gradient: 'from-blue-50 to-indigo-50 dark:from-blue-900/20 dark:to-indigo-900/20', + iconColor: 'text-[#191970] dark:text-blue-400', + spotlightColor: 'rgba(139, 92, 246, 0.2)' // Purple glow for Form + } + ]; + + const handlingFlow = [ + { + step: '01', + title: 'Pelaporan', + desc: 'Laporan masuk via WA atau Website.', + icon: + }, + { + step: '02', + title: 'Verifikasi', + desc: 'Validasi data oleh tim Satgas.', + icon: + }, + { + step: '03', + title: 'Tindak Lanjut', + desc: 'Investigasi atau mediasi kasus.', + icon: + }, + { + step: '04', + title: 'Penyelesaian', + desc: 'Pemulihan dan penutupan kasus.', + icon: } ]; const importantInfo = [ { - icon: '🛡️', - title: 'Aman', - description: 'Identitas Anda akan dirahasiakan sepenuhnya' + icon: , + title: 'Dijamin Aman', + description: 'Identitas pelapor dirahasiakan sepenuhnya sesuai kode etik.' }, { - icon: '👥', + icon: , title: 'Profesional', - description: 'Ditangani oleh tim yang berpengalaman' + description: 'Ditangani oleh tim ahli yang berpengalaman dan objektif.' }, { - icon: '💚', - title: 'Support', - description: 'Dapatkan pendampingan penuh dari kami' + icon: , + title: 'Pendampingan', + description: 'Dukungan psikologis dan hukum selama proses berjalan.' } ]; return ( -
- {/* Background Pattern */} -
-
-
+
+ {/* Background Decorations */} +
+
+
- {/* Section Header */} - - - Cara Melapor - - - Pilih metode pelaporan yang paling nyaman untuk Anda. Kami siap membantu dengan profesionalisme dan kerahasiaan terjamin. - - - {/* Reporting Methods Cards */} - +

+ Cara Melapor +

+

+ Kami menyediakan ruang aman bagi Anda untuk bersuara. Pilih metode yang paling nyaman, kami siap mendampingi setiap langkahnya. +

+
+ + {/* Reporting Methods Grid */} + { {reportingMethods.map((method, index) => ( - {/* Icon */} - - {method.icon} - + {/* Gradient Blob Background */} +
- {/* Content */} -
-

- {method.title} -

-

- {method.description} -

- - {/* Features */} -
- {method.features.map((feature, featureIndex) => ( -
-
- {feature} +
+
+
+ {method.icon}
- ))} -
- {/* Button */} - + +
+ - {method.buttonText} - - ) : ( - - {method.buttonText} - - )} + target={method.buttonLink.startsWith('http') ? "_blank" : undefined} + colorStr={method.id === 'whatsapp' ? '#16a34a' : '#2563eb'} + hoverColorStr={method.id === 'whatsapp' ? '#16a34a' : '#1e40af'} + className="w-full max-w-[280px]" + /> +
-
+ ))} - {/* Important Information */} - + + {/* Trust Indicators (Left) */} + +

Kenapa Kami?

+
+ {importantInfo.map((info, index) => ( +
+
+ {info.icon} +
+
+

{info.title}

+

{info.description}

+
+
+ ))} +
+
+ + {/* Process Flow (Right - Timeline) */} + + {/* Background Pattern */} +
+
+ +

Alur Penanganan

+ +
+ {/* Progress Line Background */} +
+ + {/* Animated Progress Line */} + + {/* Continuous Shimmer Animation */} + + + +
+ {handlingFlow.map((step, index) => ( + +
+
+ {step.icon} +
+ {step.step} +

{step.title}

+

{step.desc}

+
+
+ ))} +
+
+
+ +
+ + {/* Emergency Banner */} + -
-

- Penting Untuk Diketahui -

-

- Kami memastikan setiap proses pelaporan berjalan dengan aman, profesional, dan mendukung. -

-
- - - {importantInfo.map((info, index) => ( - -
{info.icon}
-

{info.title}

-

{info.description}

-
- ))} -
-
- - {/* Emergency Notice */} - -
-
-
- - - -
+
+
+
-
-

Darurat? Hubungi Kami Sekarang

-

- Jika Anda atau orang lain berada dalam situasi darurat, segera hubungi WhatsApp kami untuk respons cepat 24/7. -

-
-
- - Hubungi Darurat - +
+

Butuh Bantuan Darurat?

+

Jangan ragu untuk menghubungi kami jika situasi mendesak.

+ +
); diff --git a/frontend/src/components/button.jsx b/frontend/src/components/button.jsx new file mode 100644 index 0000000..a29ee8e --- /dev/null +++ b/frontend/src/components/button.jsx @@ -0,0 +1,53 @@ +import React from "react"; + +export const Component = ({ + icon, + title, + subtitle, + size = "md", + className = "", + ...props +}) => { + const sizes = { + sm: "px-10 py-4 rounded-full text-base", + md: "p-4 rounded-2xl", + lg: "p-6 rounded-3xl", + }; + + return ( + + ); +}; diff --git a/frontend/src/components/gallery4.jsx b/frontend/src/components/gallery4.jsx new file mode 100644 index 0000000..e39ecc3 --- /dev/null +++ b/frontend/src/components/gallery4.jsx @@ -0,0 +1,203 @@ +"use client";; +import { ArrowLeft, ArrowRight } from "lucide-react"; +import { Link } from "react-router-dom"; +import { useEffect, useState } from "react"; + +import { Button } from "@/components/ui/button"; +import { Carousel, CarouselContent, CarouselItem } from "@/components/ui/carousel"; + +const data = [ + { + id: "shadcn-ui", + title: "shadcn/ui: Building a Modern Component Library", + description: + "Explore how shadcn/ui revolutionized React component libraries by providing a unique approach to component distribution and customization, making it easier for developers to build beautiful, accessible applications.", + href: "https://ui.shadcn.com", + image: + "https://images.unsplash.com/photo-1551250928-243dc937c49d?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w2NDI3NzN8MHwxfGFsbHwxMjN8fHx8fHwyfHwxNzIzODA2OTM5fA&ixlib=rb-4.0.3&q=80&w=1080", + }, + { + id: "tailwind", + title: "Tailwind CSS: The Utility-First Revolution", + description: + "Discover how Tailwind CSS transformed the way developers style their applications, offering a utility-first approach that speeds up development while maintaining complete design flexibility.", + href: "https://tailwindcss.com", + image: + "https://images.unsplash.com/photo-1551250928-e4a05afaed1e?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w2NDI3NzN8MHwxfGFsbHwxMjR8fHx8fHwyfHwxNzIzODA2OTM5fA&ixlib=rb-4.0.3&q=80&w=1080", + }, + { + id: "astro", + title: "Astro: The All-in-One Web Framework", + description: + "Learn how Astro's innovative 'Islands Architecture' and zero-JS-by-default approach is helping developers build faster websites while maintaining rich interactivity where needed.", + href: "https://astro.build", + image: + "https://images.unsplash.com/photo-1536735561749-fc87494598cb?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w2NDI3NzN8MHwxfGFsbHwxNzd8fHx8fHwyfHwxNzIzNjM0NDc0fA&ixlib=rb-4.0.3&q=80&w=1080", + }, + { + id: "react", + title: "React: Pioneering Component-Based UI", + description: + "See how React continues to shape modern web development with its component-based architecture, enabling developers to build complex user interfaces with reusable, maintainable code.", + href: "https://react.dev", + image: + "https://images.unsplash.com/photo-1548324215-9133768e4094?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w2NDI3NzN8MHwxfGFsbHwxMzF8fHx8fHwyfHwxNzIzNDM1MzA1fA&ixlib=rb-4.0.3&q=80&w=1080", + }, + { + id: "nextjs", + title: "Next.js: The React Framework for Production", + description: + "Explore how Next.js has become the go-to framework for building full-stack React applications, offering features like server components, file-based routing, and automatic optimization.", + href: "https://nextjs.org", + image: + "https://images.unsplash.com/photo-1550070881-a5d71eda5800?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w2NDI3NzN8MHwxfGFsbHwxMjV8fHx8fHwyfHwxNzIzNDM1Mjk4fA&ixlib=rb-4.0.3&q=80&w=1080", + }, +]; + +const Gallery4 = ({ + title = "Case Studies", + description = "Discover how leading companies and developers are leveraging modern web technologies to build exceptional digital experiences. These case studies showcase real-world applications and success stories.", + items = data +}) => { + const [carouselApi, setCarouselApi] = useState(); + const [canScrollPrev, setCanScrollPrev] = useState(false); + const [canScrollNext, setCanScrollNext] = useState(false); + const [currentSlide, setCurrentSlide] = useState(0); + + useEffect(() => { + if (!carouselApi) { + return; + } + const updateSelection = () => { + setCanScrollPrev(carouselApi.canScrollPrev()); + setCanScrollNext(carouselApi.canScrollNext()); + setCurrentSlide(carouselApi.selectedScrollSnap()); + }; + updateSelection(); + carouselApi.on("select", updateSelection); + return () => { + carouselApi.off("select", updateSelection); + }; + }, [carouselApi]); + + return ( +
+
+
+
+

+ {title} +

+

{description}

+
+
+ + +
+
+
+
+ + + {items.map((item) => ( + + {item.href.startsWith('/') ? ( + +
+ {item.title} +
+
+
+ {item.title} +
+
+ {item.description} +
+
+ Read more{" "} + +
+
+
+ + ) : ( + +
+ {item.title} +
+
+
+ {item.title} +
+
+ {item.description} +
+
+ Read more{" "} + +
+
+
+
+ )} + + ))} + + +
+ {items.map((_, index) => ( +
+
+
+ ); +}; + +export { Gallery4 }; diff --git a/frontend/src/components/sky-toggle.jsx b/frontend/src/components/sky-toggle.jsx new file mode 100644 index 0000000..eb95dcb --- /dev/null +++ b/frontend/src/components/sky-toggle.jsx @@ -0,0 +1,251 @@ +import React from 'react'; +import styled from 'styled-components'; +import { useTheme } from '../contexts/ThemeContext'; + +const Switch = () => { + const { toggleTheme, isDark } = useTheme(); + + const handleChange = () => { + toggleTheme(); + }; + + return ( + +