diff --git a/dist.zip b/dist.zip deleted file mode 100644 index 08930d2..0000000 Binary files a/dist.zip and /dev/null differ diff --git a/package-lock.json b/package-lock.json index 29ba3ea..3f13ceb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,14 +10,20 @@ "dependencies": { "@astrojs/react": "^4.2.1", "@astrojs/tailwind": "^6.0.0", + "@radix-ui/react-dialog": "^1.1.6", + "@radix-ui/react-select": "^2.1.6", "@shadcn/ui": "^0.0.4", + "@types/react": "^19.0.12", "astro": "^5.5.2", "autoprefixer": "^10.4.21", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", + "lucide-react": "^0.484.0", + "pocketbase": "^0.25.2", "postcss": "^8.5.3", "tailwind-merge": "^3.0.2", - "tailwindcss": "^3.4.17" + "tailwindcss": "^3.4.17", + "tailwindcss-animate": "^1.0.7" } }, "node_modules/@alloc/quick-lru": { @@ -842,6 +848,44 @@ "node": ">=18" } }, + "node_modules/@floating-ui/core": { + "version": "1.6.9", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.9.tgz", + "integrity": "sha512-uMXCuQ3BItDUbAMhIXw7UPXRfAlOAvZzdK9BWpE60MCn+Svt3aLn9jsPTi/WNGlRUu2uI0v5S7JiIUsbsvh3fw==", + "license": "MIT", + "dependencies": { + "@floating-ui/utils": "^0.2.9" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.6.13", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.13.tgz", + "integrity": "sha512-umqzocjDgNRGTuO7Q8CU32dkHkECqI8ZdMZ5Swb6QAM0t5rnlrN3lGo1hdpscRd3WS8T6DKYK4ephgIH9iRh3w==", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.6.0", + "@floating-ui/utils": "^0.2.9" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.2.tgz", + "integrity": "sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A==", + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^1.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.9.tgz", + "integrity": "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==", + "license": "MIT" + }, "node_modules/@img/sharp-darwin-arm64": { "version": "0.33.5", "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz", @@ -1359,6 +1403,543 @@ "node": ">=14" } }, + "node_modules/@radix-ui/number": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.0.tgz", + "integrity": "sha512-V3gRzhVNU1ldS5XhAPTom1fOIo4ccrjjJgmE+LI2h/WaFpHmx0MQApT+KZHnx8abG6Avtfcz4WoEciMnpFT3HQ==", + "license": "MIT" + }, + "node_modules/@radix-ui/primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.1.tgz", + "integrity": "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA==", + "license": "MIT" + }, + "node_modules/@radix-ui/react-arrow": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.2.tgz", + "integrity": "sha512-G+KcpzXHq24iH0uGG/pF8LyzpFJYGD4RfLjCIBfGdSLXvjLHST31RUiRVrupIBMvIppMgSzQ6l66iAxl03tdlg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.0.2" + }, + "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.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.2.tgz", + "integrity": "sha512-9z54IEKRxIa9VityapoEYMuByaG42iSy1ZXlY2KcuLSEtq8x4987/N6m15ppoMffgZX72gER2uHe1D9Y6Unlcw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-slot": "1.1.2" + }, + "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-compose-refs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz", + "integrity": "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==", + "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.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.1.tgz", + "integrity": "sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q==", + "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-dialog": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.6.tgz", + "integrity": "sha512-/IVhJV5AceX620DUJ4uYVMymzsipdKBzo3edo+omeskCKGm9FRHM0ebIdbPnlQVJqyuHbuBltQUOG2mOTq2IYw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-dismissable-layer": "1.1.5", + "@radix-ui/react-focus-guards": "1.1.1", + "@radix-ui/react-focus-scope": "1.1.2", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-portal": "1.1.4", + "@radix-ui/react-presence": "1.1.2", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-slot": "1.1.2", + "@radix-ui/react-use-controllable-state": "1.1.0", + "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-direction": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.0.tgz", + "integrity": "sha512-BUuBvgThEiAXh2DWu93XsT+a3aWrGqolGlqqw5VU1kG7p/ZH2cuDlM1sRLNnY3QcBS69UIz2mcKhMxDsdewhjg==", + "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.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.5.tgz", + "integrity": "sha512-E4TywXY6UsXNRhFrECa5HAvE5/4BFcGyfTyK36gP+pAW1ed7UTK4vKwdr53gAJYwqbfCWC6ATvJa3J3R/9+Qrg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-use-callback-ref": "1.1.0", + "@radix-ui/react-use-escape-keydown": "1.1.0" + }, + "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.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.1.tgz", + "integrity": "sha512-pSIwfrT1a6sIoDASCSpFwOasEwKTZWDw/iBdtnqKO7v6FeOzYJ7U53cPzYFVR3geGGXgVHaH+CdngrrAzqUGxg==", + "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.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.2.tgz", + "integrity": "sha512-zxwE80FCU7lcXUGWkdt6XpTTCKPitG1XKOwViTxHVKIJhZl9MvIl2dVHeZENCWD9+EdWv05wlaEkRXUykU27RA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-use-callback-ref": "1.1.0" + }, + "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.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.0.tgz", + "integrity": "sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.0" + }, + "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-popper": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.2.tgz", + "integrity": "sha512-Rvqc3nOpwseCyj/rgjlJDYAgyfw7OC1tTkKn2ivhaMGcYt8FSBlahHOZak2i3QwkRXUXgGgzeEe2RuqeEHuHgA==", + "license": "MIT", + "dependencies": { + "@floating-ui/react-dom": "^2.0.0", + "@radix-ui/react-arrow": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-use-callback-ref": "1.1.0", + "@radix-ui/react-use-layout-effect": "1.1.0", + "@radix-ui/react-use-rect": "1.1.0", + "@radix-ui/react-use-size": "1.1.0", + "@radix-ui/rect": "1.1.0" + }, + "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.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.4.tgz", + "integrity": "sha512-sn2O9k1rPFYVyKd5LAJfo96JlSGVFpa1fS6UuBJfrZadudiw5tAmru+n1x7aMRQ84qDM71Zh1+SzK5QwU0tJfA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-use-layout-effect": "1.1.0" + }, + "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.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.2.tgz", + "integrity": "sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-use-layout-effect": "1.1.0" + }, + "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.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.2.tgz", + "integrity": "sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.1.2" + }, + "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": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.1.6.tgz", + "integrity": "sha512-T6ajELxRvTuAMWH0YmRJ1qez+x4/7Nq7QIx7zJ0VK3qaEWdnWpNbEDnmWldG1zBDwqrLy5aLMUWcoGirVj5kMg==", + "license": "MIT", + "dependencies": { + "@radix-ui/number": "1.1.0", + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-collection": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-direction": "1.1.0", + "@radix-ui/react-dismissable-layer": "1.1.5", + "@radix-ui/react-focus-guards": "1.1.1", + "@radix-ui/react-focus-scope": "1.1.2", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-popper": "1.2.2", + "@radix-ui/react-portal": "1.1.4", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-slot": "1.1.2", + "@radix-ui/react-use-callback-ref": "1.1.0", + "@radix-ui/react-use-controllable-state": "1.1.0", + "@radix-ui/react-use-layout-effect": "1.1.0", + "@radix-ui/react-use-previous": "1.1.0", + "@radix-ui/react-visually-hidden": "1.1.2", + "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-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.2.tgz", + "integrity": "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "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-callback-ref": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz", + "integrity": "sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==", + "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.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.1.0.tgz", + "integrity": "sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-callback-ref": "1.1.0" + }, + "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.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.0.tgz", + "integrity": "sha512-L7vwWlR1kTTQ3oh7g1O0CBF3YCyyTj8NmhLR+phShpyA50HCfBFKVJTpshm9PzLiKmehsrQzTYTpX9HvmC9rhw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-callback-ref": "1.1.0" + }, + "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.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.0.tgz", + "integrity": "sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w==", + "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.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.0.tgz", + "integrity": "sha512-Z/e78qg2YFnnXcW88A4JmTtm4ADckLno6F7OXotmkQfeuCVaKuYzqAATPhVzl3delXE7CxIV8shofPn3jPc5Og==", + "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.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.0.tgz", + "integrity": "sha512-0Fmkebhr6PiseyZlYAOtLS+nb7jLmpqTrJyv61Pe68MKYW6OWdRE2kI70TaYY27u7H0lajqM3hSMMLFq18Z7nQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/rect": "1.1.0" + }, + "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.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.0.tgz", + "integrity": "sha512-XW3/vWuIXHa+2Uwcc2ABSfcCledmXhhQPlGbfcRXbiUQI5Icjcg19BGCZVKKInYbvUCut/ufbbLLPFC5cbb1hw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.0" + }, + "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.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.1.2.tgz", + "integrity": "sha512-1SzA4ns2M1aRlvxErqhLHsBHoS5eI5UUcI2awAMgGUp4LoaoWOKYmvqDY2s/tltuPkh3Yk77YF/r3IRj+Amx4Q==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.0.2" + }, + "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.0", + "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.0.tgz", + "integrity": "sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg==", + "license": "MIT" + }, "node_modules/@rollup/pluginutils": { "version": "5.1.4", "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.4.tgz", @@ -1830,11 +2411,10 @@ } }, "node_modules/@types/react": { - "version": "19.0.11", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.11.tgz", - "integrity": "sha512-vrdxRZfo9ALXth6yPfV16PYTLZwsUWhVjjC+DkfE5t1suNSbBrWC9YqSuuxJZ8Ps6z1o2ycRpIqzZJIgklq4Tw==", + "version": "19.0.12", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.12.tgz", + "integrity": "sha512-V6Ar115dBDrjbtXSrS+/Oruobc+qVbbUxDFC1RSbRqLt5SYvxxyIDrSC85RWml54g+jfNeEMZhEj7wW07ONQhA==", "license": "MIT", - "peer": true, "dependencies": { "csstype": "^3.0.2" } @@ -2009,6 +2589,18 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "license": "Python-2.0" }, + "node_modules/aria-hidden": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.4.tgz", + "integrity": "sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/aria-query": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", @@ -2639,8 +3231,7 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/data-uri-to-buffer": { "version": "4.0.1", @@ -2724,6 +3315,12 @@ "node": ">=8" } }, + "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/deterministic-object-hash": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/deterministic-object-hash/-/deterministic-object-hash-2.0.2.tgz", @@ -3138,6 +3735,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "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-stream": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", @@ -3807,6 +4413,15 @@ "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "license": "ISC" }, + "node_modules/lucide-react": { + "version": "0.484.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.484.0.tgz", + "integrity": "sha512-oZy8coK9kZzvqhSgfbGkPtTgyjpBvs3ukLgDPv14dSOZtBtboryWF5o8i3qen7QbGg7JhiJBz5mK1p8YoMZTLQ==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/magic-string": { "version": "0.30.17", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", @@ -5097,6 +5712,12 @@ "node": ">= 6" } }, + "node_modules/pocketbase": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/pocketbase/-/pocketbase-0.25.2.tgz", + "integrity": "sha512-ONZl1+qHJMnhR2uacBlBJ90lm7njtL/zy0606+1ROfK9hSL4LRBRc8r89rMcNRzPzRqCNyoFTh2Qg/lYXdEC1w==", + "license": "MIT" + }, "node_modules/postcss": { "version": "8.5.3", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", @@ -5339,6 +5960,75 @@ "node": ">=0.10.0" } }, + "node_modules/react-remove-scroll": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.6.3.tgz", + "integrity": "sha512-pnAi91oOk8g8ABQKGF5/M9qxmmOPxaAnopyTHYfqYEwJhyFrbbBtHuSgtKEoH0jpcxx5o3hXqH1mNd9/Oi+8iQ==", + "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-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", @@ -6139,6 +6829,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/tailwindcss/node_modules/chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", @@ -6304,8 +7003,7 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD", - "optional": true + "license": "0BSD" }, "node_modules/type-fest": { "version": "4.37.0", @@ -6624,6 +7322,49 @@ "browserslist": ">= 4.21.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/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", diff --git a/package.json b/package.json index 18e3b4d..aba8c08 100644 --- a/package.json +++ b/package.json @@ -12,14 +12,20 @@ "dependencies": { "@astrojs/react": "^4.2.1", "@astrojs/tailwind": "^6.0.0", + "@radix-ui/react-dialog": "^1.1.6", + "@radix-ui/react-select": "^2.1.6", "@shadcn/ui": "^0.0.4", + "@types/react": "^19.0.12", "astro": "^5.5.2", "autoprefixer": "^10.4.21", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", + "lucide-react": "^0.484.0", + "pocketbase": "^0.25.2", "postcss": "^8.5.3", "tailwind-merge": "^3.0.2", - "tailwindcss": "^3.4.17" + "tailwindcss": "^3.4.17", + "tailwindcss-animate": "^1.0.7" }, "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e" } diff --git a/public/assets/eye-close.svg b/public/assets/eye-close.svg new file mode 100644 index 0000000..3f35f8a --- /dev/null +++ b/public/assets/eye-close.svg @@ -0,0 +1 @@ + diff --git a/public/assets/eye.svg b/public/assets/eye.svg new file mode 100644 index 0000000..14c7300 --- /dev/null +++ b/public/assets/eye.svg @@ -0,0 +1 @@ + diff --git a/public/assets/facebook.svg b/public/assets/facebook.svg new file mode 100644 index 0000000..13c1cb7 --- /dev/null +++ b/public/assets/facebook.svg @@ -0,0 +1 @@ + diff --git a/public/assets/github.svg b/public/assets/github.svg new file mode 100644 index 0000000..18f7a45 --- /dev/null +++ b/public/assets/github.svg @@ -0,0 +1 @@ + diff --git a/public/assets/google.svg b/public/assets/google.svg new file mode 100644 index 0000000..6cc6fec --- /dev/null +++ b/public/assets/google.svg @@ -0,0 +1 @@ + diff --git a/src/components/DomainSetupForm.jsx b/src/components/DomainSetupForm.jsx index f5b78fd..4f448f1 100644 --- a/src/components/DomainSetupForm.jsx +++ b/src/components/DomainSetupForm.jsx @@ -18,7 +18,7 @@ export const DomainSetupForm = ({ defaultSubdomain }) => { const [customDomain, setCustomDomain] = useState(''); const [customSubdomain, setCustomSubdomain] = useState(''); const [domainType, setDomainType] = useState('domain'); // 'domain' or 'subdomain' - + // Domain validation states const [isValidating, setIsValidating] = useState(false); const [isValidDomain, setIsValidDomain] = useState(false); @@ -43,15 +43,6 @@ export const DomainSetupForm = ({ defaultSubdomain }) => { // File upload reference const fileInputRef = React.useRef(null); - // Function to clean domain input - const cleanDomainInput = (input) => { - // Remove http://, https://, www., and trailing slashes - return input - .replace(/^(https?:\/\/)?(www\.)?/i, '') - .replace(/\/+$/, '') - .trim(); - }; - // Effect for handling domain type changes useEffect(() => { if (!useCustomDomain) { @@ -140,12 +131,12 @@ export const DomainSetupForm = ({ defaultSubdomain }) => { // Handle domain and subdomain input changes const handleDomainChange = (e) => { - const cleanedValue = cleanDomainInput(e.target.value); + const cleanedValue = e.target.value.replace(/^(https?:\/\/)?(www\.)?/i, '').replace(/\/+$/, '').trim(); setCustomDomain(cleanedValue); }; const handleSubdomainChange = (e) => { - const cleanedValue = cleanDomainInput(e.target.value); + const cleanedValue = e.target.value.replace(/^(https?:\/\/)?/i, '').replace(/\/+$/, '').trim(); setCustomSubdomain(cleanedValue); }; @@ -210,6 +201,38 @@ export const DomainSetupForm = ({ defaultSubdomain }) => { }; // Check DNS configuration + const checkSubDomainCname = () => { + const domainToCheck = customDomain || customSubdomain; + + fetch('http://localhost:2058/host-api/v1/check-c-name/', { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + body: `domain=${encodeURIComponent(domainToCheck)}` + }) + .then(response => { + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + return response.json(); + }) + .then(data => { + if (data.status === 'success') { + checkDnsConfig('cname'); + showToast(`Checking ${type}... (This would verify DNS in a real app)`); + console.log('CNAME record:', data.cname); + // Handle success - update UI + } else { + console.error('Error:', data.message); + // Handle error + } + }) + .catch(error => { + console.error('Fetch error:', error); + // Show error to user + }); + }; const checkDnsConfig = (type) => { showToast(`Checking ${type}... (This would verify DNS in a real app)`); @@ -544,10 +567,10 @@ export const DomainSetupForm = ({ defaultSubdomain }) => { {/* Domain Validation */} @@ -607,7 +630,7 @@ export const DomainSetupForm = ({ defaultSubdomain }) => {
+
+ + + + + {/* Search */} +
+
+ setSearchTerm(e.target.value)} + className="mb-4" + /> +
+
+ + {/* AI Agent Grid */} +
+ {filteredAgents.map((agent) => ( + + + {agent.title} + {agent.description} + + +
+

Key Capabilities:

+
+ {agent.capabilities.map((capability, index) => ( + + {capability} + + ))} +
+
+
+
+ Response Time: + {agent.responseTime} +
+
+ Pricing: + {agent.pricing} +
+
+
+ + + +
+ ))} +
+ + {/* AI Agent Details Dialog */} + + + {selectedAgent && ( + <> + + {selectedAgent.title} + + {selectedAgent.description} + + + +
+
+

Capabilities:

+
+ {selectedAgent.capabilities.map((capability, index) => ( + + {capability} + + ))} +
+
+ +
+

Common Use Cases:

+
    + {selectedAgent.useCases.map((useCase, index) => ( +
  • {useCase}
  • + ))} +
+
+ +
+
+

Response Time:

+

{selectedAgent.responseTime}

+
+
+

Pricing:

+

{selectedAgent.pricing}

+
+
+ +
+

Subscription Includes:

+
    +
  • + + + + Unlimited requests +
  • +
  • + + + + 24/7 availability +
  • +
  • + + + + API access +
  • +
  • + + + + Export capabilities +
  • +
+
+
+ + + + + + + )} +
+
+ + {/* Comparison Section */} +
+

AI Agents vs. Human Developers

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FeatureAI AgentsHuman Developers
Availability24/7, instant accessLimited by working hours and availability
CostLow monthly subscriptionHigher hourly or project-based rates
Task ComplexityBest for repetitive, well-defined tasksBetter for complex, novel problems
CreativityLimited to patterns in training dataHigher level of creativity and innovation
ScalabilityHighly scalable at no extra costRequires additional hiring and onboarding
+
+
+ + {/* CTA Section */} +
+

Not sure which AI agent is right for you?

+

+ Our AI solution experts can help you determine which AI agent best fits your specific needs and use cases. +

+ +
+ + ); +} diff --git a/src/components/HireDeveloper.tsx b/src/components/HireDeveloper.tsx new file mode 100644 index 0000000..4ba5ef2 --- /dev/null +++ b/src/components/HireDeveloper.tsx @@ -0,0 +1,263 @@ +"use client"; + +import { useState } from "react"; +import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "./ui/card"; +import { Button } from "./ui/button"; +import { Input } from "./ui/input"; +import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "./ui/dialog"; + +// Define the Developer type +interface Developer { + id: number; + title: string; + description: string; + skills: string[]; + availability: "High" | "Medium" | "Low"; + minBudget: string; + contractFee: string; +} + +// Developer types data +const developerTypes: Developer[] = [ + { + id: 1, + title: "Web Developer", + description: "Frontend, backend or full-stack developers specialized in web technologies", + skills: ["React", "Angular", "Vue", "Node.js", "Django", "Ruby on Rails", "Laravel", "Express", "Flask", "PHP", "JavaScript", "TypeScript", "HTML", "CSS", "SASS", "LESS", "Bootstrap", "Tailwind CSS", "Material-UI", "Ant Design", "REST APIs", "GraphQL", "WebSockets", "OAuth", "JWT", "WebRTC", "PWA", "SEO", "Accessibility", "Performance", "Security"], + availability: "High", + minBudget: "Budget-friendly", + contractFee: "Nominal" + }, + { + id: 2, + title: "App Developer", + description: "iOS and Android native or cross-platform mobile app developers", + skills: ["Swift", "Kotlin", "React Native", "Flutter", "Xamarin", "Ionic", "PhoneGap", "Cordova", "NativeScript", "Appcelerator", "Java", "Objective-C", "Firebase", "Realm", "Core Data", "SQLite", "Push Notifications", "In-App Purchases", "ARKit", "Core ML", "Android Jetpack", "Material Design", "Google Play Services", "App Store Connect", "Google Play Console"], + availability: "Medium", + minBudget: "Limited", + contractFee: "Modest" + }, + { + id: 3, + title: "C/C++ Developer", + description: "Systems programmers and embedded systems specialists", + skills: ["C", "C++", "Embedded Systems", "Linux Kernel", "Real-time Systems", "RTOS", "Device Drivers", "Firmware", "Microcontrollers", "ARM", "AVR", "PIC", "Raspberry Pi", "Arduino", "BeagleBone", "Zephyr", "FreeRTOS", "VxWorks", "QNX", "Bare Metal"], + availability: "Low", + minBudget: "Increased", + contractFee: "Considerable" + }, + { + id: 4, + title: "Go Developer", + description: "Backend and systems engineers specializing in Go language", + skills: ["Go", "Microservices", "Docker", "Kubernetes", "API Development", "gRPC", "Protobuf", "WebSockets", "WebAssembly", "Concurrency", "Performance Tuning", "Module Building", "Error Handling", "Testing", "Security"], + availability: "Medium", + minBudget: "Boosted", + contractFee: "Elevated" + }, + { + id: 5, + title: "Machine Learning Engineer", + description: "AI and ML specialists for implementing intelligent solutions", + skills: ["Python", "TensorFlow", "PyTorch", "Data Science", "MLOps", "NLP", "Computer Vision", "Reinforcement Learning", "Time Series Analysis", "Anomaly Detection", "Recommendation Systems", "Predictive Modeling", "Deep Learning", "Fraud Detection", "Sentiment Analysis", "Speech Recognition", "Image Processing", "Object Detection", "Generative Models", "AutoML", "Image Recognition", "Text Classification", "Chatbots", "AI Ethics"], + availability: "Low", + minBudget: "Premium", + contractFee: "Lavish" + }, + { + id: 6, + title: "DevOps Engineer", + description: "Specialists in CI/CD, cloud infrastructure, and deployment automation", + skills: ["AWS", "Azure", "GCP", "Terraform", "Ansible", "Jenkins", "Docker", "Kubernetes", "GitOps", "Monitoring", "Logging", "Security", "Compliance", "Scalability", "Reliability", "Resilience", "Performance", "Cost Optimization", "Disaster Recovery", "Incident Response", "SRE"], + availability: "Medium", + minBudget: "Pricier", + contractFee: "Substantial" + } +]; + +export default function HireHumanDeveloper() { + const [selectedType, setSelectedType] = useState(null); + const [searchTerm, setSearchTerm] = useState(""); + const [dialogOpen, setDialogOpen] = useState(false); + + // Filter developers based on search term + const filteredDevelopers = developerTypes.filter(dev => + dev.title.toLowerCase().includes(searchTerm.toLowerCase()) || + dev.description.toLowerCase().includes(searchTerm.toLowerCase()) || + dev.skills.some(skill => skill.toLowerCase().includes(searchTerm.toLowerCase())) + ); + + return ( +
+

Hire a Human Developer

+

+ Connect with skilled developers across various technologies and specialties +

+ + {/* Search and Filter */} +
+
+ setSearchTerm(e.target.value)} + className="mb-4" + /> +
+
+ + {/* Developer Types Grid */} +
+ {filteredDevelopers.map((developer) => ( + + + {developer.title} + {developer.description} + + +
+

Key Skills:

+
+ {developer.skills.map((skill, index) => ( + + {skill} + + ))} +
+
+
+
+ Availability: + + {developer.availability} + +
+
+ Starting at: + {developer.minBudget} +
+
+ Contract Fee: + {developer.contractFee} +
+
+
+ + + +
+ ))} +
+ + {/* Developer Details Dialog */} + + + {selectedType && ( + <> + + {selectedType.title} Details + + Review the details and proceed to hire + + + +
+
+

Overview:

+

{selectedType.description}

+
+ +
+

Specialized Skills:

+
+ {selectedType.skills.map((skill, index) => ( + + {skill} + + ))} +
+
+ +
+
+

Availability:

+

+ {selectedType.availability} +

+
+
+

Minimum Budget:

+

{selectedType.minBudget}

+
+
+

Contract Fee:

+

{selectedType.contractFee}

+
+
+

Usual Turnaround:

+

1-3 weeks

+
+
+
+ + + + + + + )} +
+
+ + {/* Additional Information */} +
+

Why Hire Our Human Developers?

+
+
+

Vetted Experts

+

+ All our developers go through a rigorous vetting process to ensure top-quality talent. +

+
+
+

Flexible Engagement

+

+ Hire on hourly, project-based, or long-term contracts based on your needs. +

+
+
+

Satisfaction Guarantee

+

+ Not satisfied with the talent? We'll match you with another developer at no extra cost. +

+
+
+
+
+ ); +} diff --git a/src/components/Login.tsx b/src/components/Login.tsx new file mode 100644 index 0000000..16828d7 --- /dev/null +++ b/src/components/Login.tsx @@ -0,0 +1,266 @@ +import { useState } from 'react'; +import type { FormEvent } from 'react'; +import PocketBase from 'pocketbase'; +import { Input } from "./ui/input"; +import { Button } from "./ui/button"; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "./ui/card"; +import { Eye, EyeOff, Loader2 } from "lucide-react"; +import { Separator } from "./ui/separator"; +import { Label } from "./ui/label"; + +interface AuthStatus { + message: string; + isError: boolean; +} + +interface UserRecord { + id: string; + email: string; + name?: string; + avatar?: string; + [key: string]: any; +} + +interface AuthResponse { + token: string; + record: UserRecord; +} + +const LoginPage = () => { + const [email, setEmail] = useState('suvodip@siliconpin.com'); + const [password, setPassword] = useState('Simple2pass'); + const [passwordVisible, setPasswordVisible] = useState(false); + const [status, setStatus] = useState({ message: '', isError: false }); + const [isLoading, setIsLoading] = useState(false); + + const pb = new PocketBase("https://tst-pb.s38.siliconpin.com"); + + const handleSubmit = async (e: FormEvent) => { + e.preventDefault(); + setIsLoading(true); + setStatus({ message: '', isError: false }); + + try { + const authData = await pb.collection("users").authWithPassword(email, password); + + const authResponse: AuthResponse = { + token: authData.token, + record: { + query: 'new', + id: authData.record.id, + email: authData.record.email, + name: authData.record.name || '', + avatar: authData.record.avatar || '' + } + }; + + await syncSessionWithBackend(authResponse); + // window.location.href = '/profile'; + } catch (error) { + console.error("Login failed:", error); + setStatus({ + message: "Login failed. Please check your credentials.", + isError: true + }); + } finally { + setIsLoading(false); + } + }; + + const loginWithOAuth2 = async (provider: 'google' | 'facebook' | 'github') => { + try { + setIsLoading(true); + setStatus({ message: '', isError: false }); + + const authData = await pb.collection('users').authWithOAuth2({ provider }); + + if (!authData?.record) { + throw new Error("No user record found"); + } + + const authResponse: AuthResponse = { + token: authData.token, + record: { + query: 'new', + id: authData.record.id, + email: authData.record.email || '', + name: authData.record.name || '', + avatar: authData.record.avatar || '' + } + }; + + await syncSessionWithBackend(authResponse); + // window.location.href = '/profile'; + } catch (error) { + console.error(`${provider} Login failed:`, error); + setStatus({ + message: `${provider} login failed. Please try again.`, + isError: true + }); + } finally { + setIsLoading(false); + } + }; + + const syncSessionWithBackend = async (authData: AuthResponse) => { + try { + const response = await fetch('http://localhost:2058/host-api/v1/users/session/', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + query: 'new', + accessToken: authData.token, + email: authData.record.email, + name: authData.record.name, + avatar: authData.record.avatar + ? pb.files.getUrl(authData.record, authData.record.avatar) + : '', + isAuthenticated: true, + id: authData.record.id + }) + }); + + if (!response.ok) { + throw new Error('Failed to sync session'); + } + + const data = await response.json(); + console.log('Session synced with backend:', data); + } catch (error) { + console.error('Error syncing session:', error); + } + }; + + return ( +
+ + + Welcome Back + + Sign in to access your account + + + + + {status.message && ( +
+ {status.message} +
+ )} + +
+
+ + setEmail(e.target.value)} + placeholder="you@example.com" + required + className="focus:ring-2 focus:ring-blue-500" + /> +
+ +
+
+ + +
+
+ setPassword(e.target.value)} + placeholder="••••••••" + required + className="focus:ring-2 focus:ring-blue-500 pr-10" + /> + +
+
+ + +
+ +
+
+ +
+
+ + Or continue with + +
+
+ +
+ + + +
+
+ +
+ Don't have an account?{' '} + + Sign up + +
+
+
+ ); +}; + +export default LoginPage; \ No newline at end of file diff --git a/src/components/ServiceCard.tsx b/src/components/ServiceCard.tsx index c30c0c0..2f2d60a 100644 --- a/src/components/ServiceCard.tsx +++ b/src/components/ServiceCard.tsx @@ -36,7 +36,7 @@ export function ServiceCard({ title, description, imageUrl, features, learnMoreU > - {feature} + {feature} ))} diff --git a/src/components/UserProfile.tsx b/src/components/UserProfile.tsx new file mode 100644 index 0000000..744f0d2 --- /dev/null +++ b/src/components/UserProfile.tsx @@ -0,0 +1,210 @@ +import { Avatar, AvatarFallback, AvatarImage } from "./ui/avatar"; +import { Button } from "./ui/button"; +import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "./ui/card"; +import { Input } from "./ui/input"; +import { Label } from "./ui/label"; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue,} from "./ui/select"; +import { Separator } from "./ui/separator"; +import { Textarea } from "./ui/textarea"; +import React, { useState, useEffect } from 'react'; + +interface SessionData { + [key: string]: any; +} + +interface UserData { + success: boolean; + session_data: SessionData; + user_avatar: string; +} + + +export default function ProfilePage() { + const [userData, setUserData] = useState(null); + const [error, setError] = useState(null); + + useEffect(() => { + const fetchData = async () => { + try { + const response = await fetch( + 'http://localhost:2058/host-api/v1/users/get-profile-data/', + { + credentials: 'include', + headers: { + 'Accept': 'application/json', + } + } + ); + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + const data: UserData = await response.json(); + // console.log('success message', data.success); + if(data.success === true){ + setUserData(data); + // console.log('User Data', data); + } + + } catch (error) { + console.error('Fetch error:', error); + setError(error instanceof Error ? error.message : 'An unknown error occurred'); + } + }; + + fetchData(); + }, []); + + if (error) { + return
Error: {error}
; + } + + if (!userData) { + return
Loading profile data...
; + } + return ( +
+
+

Profile

+

+ Update your profile settings. +

+
+ +
+
+ + + Personal Information + + Update your personal information and avatar. + + + +
+ + + JP + +
+ +

+ JPG, GIF or PNG. 1MB max. +

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