update 9/20/25
This commit is contained in:
12
.gitignore
vendored
12
.gitignore
vendored
@@ -67,4 +67,14 @@ dist-ssr
|
||||
/TODOS.md
|
||||
/SECURITY_MIGRATION.md
|
||||
/employee_report_schema.json
|
||||
*.env*
|
||||
*.env*
|
||||
*.ignore.*
|
||||
*.ignore
|
||||
*.py
|
||||
*.md
|
||||
.github/instructions
|
||||
schema.sql
|
||||
setup-neondb.sh
|
||||
functions/migrations/seed.js
|
||||
dev-setup.sh
|
||||
docker-compose.yml
|
||||
|
||||
1
functions/.tool-versions
Normal file
1
functions/.tool-versions
Normal file
@@ -0,0 +1 @@
|
||||
direnv 2.37.1
|
||||
@@ -5,11 +5,13 @@
|
||||
"name": "auditly-functions",
|
||||
"dependencies": {
|
||||
"@google-cloud/vertexai": "^1.10.0",
|
||||
"firebase-admin": "^12.7.0",
|
||||
"@neondatabase/serverless": "^0.9.5",
|
||||
"firebase-functions": "^6.4.0",
|
||||
"pg": "^8.12.0",
|
||||
"stripe": "^18.5.0",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/pg": "^8.11.6",
|
||||
"firebase-functions-test": "^3.4.1",
|
||||
},
|
||||
},
|
||||
@@ -183,6 +185,8 @@
|
||||
|
||||
"@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@0.2.12", "", { "dependencies": { "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", "@tybys/wasm-util": "^0.10.0" } }, "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ=="],
|
||||
|
||||
"@neondatabase/serverless": ["@neondatabase/serverless@0.9.5", "", { "dependencies": { "@types/pg": "8.11.6" } }, "sha512-siFas6gItqv6wD/pZnvdu34wEqgG3nSE6zWZdq5j2DEsa+VvX8i/5HXJOo06qrw5axPXn+lGCxeR+NLaSPIXug=="],
|
||||
|
||||
"@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="],
|
||||
|
||||
"@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="],
|
||||
@@ -259,6 +263,8 @@
|
||||
|
||||
"@types/node": ["@types/node@22.17.2", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-gL6z5N9Jm9mhY+U2KXZpteb+09zyffliRkZyZOHODGATyC5B1Jt/7TzuuiLkFsSUMLbS1OLmlj/E+/3KF4Q/4w=="],
|
||||
|
||||
"@types/pg": ["@types/pg@8.15.5", "", { "dependencies": { "@types/node": "*", "pg-protocol": "*", "pg-types": "^2.2.0" } }, "sha512-LF7lF6zWEKxuT3/OR8wAZGzkg4ENGXFNyiV/JeOt9z5B+0ZVwbql9McqX5c/WStFq1GaGso7H1AzP/qSzmlCKQ=="],
|
||||
|
||||
"@types/qs": ["@types/qs@6.14.0", "", {}, "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ=="],
|
||||
|
||||
"@types/range-parser": ["@types/range-parser@1.2.7", "", {}, "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ=="],
|
||||
@@ -419,7 +425,7 @@
|
||||
|
||||
"cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
|
||||
|
||||
"debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="],
|
||||
"debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
|
||||
|
||||
"dedent": ["dedent@1.6.0", "", { "peerDependencies": { "babel-plugin-macros": "^3.1.0" }, "optionalPeers": ["babel-plugin-macros"] }, "sha512-F1Z+5UCFpmQUzJa11agbyPVMbpgT/qA3/SKyJ1jyBgm7dUcUEa8v9JwDkerSQXfakBwFljIxhOJqGkjUwZ9FSA=="],
|
||||
|
||||
@@ -773,6 +779,8 @@
|
||||
|
||||
"object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="],
|
||||
|
||||
"obuf": ["obuf@1.1.2", "", {}, "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg=="],
|
||||
|
||||
"on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="],
|
||||
|
||||
"once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="],
|
||||
@@ -801,6 +809,24 @@
|
||||
|
||||
"path-to-regexp": ["path-to-regexp@0.1.12", "", {}, "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ=="],
|
||||
|
||||
"pg": ["pg@8.16.3", "", { "dependencies": { "pg-connection-string": "^2.9.1", "pg-pool": "^3.10.1", "pg-protocol": "^1.10.3", "pg-types": "2.2.0", "pgpass": "1.0.5" }, "optionalDependencies": { "pg-cloudflare": "^1.2.7" }, "peerDependencies": { "pg-native": ">=3.0.1" }, "optionalPeers": ["pg-native"] }, "sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw=="],
|
||||
|
||||
"pg-cloudflare": ["pg-cloudflare@1.2.7", "", {}, "sha512-YgCtzMH0ptvZJslLM1ffsY4EuGaU0cx4XSdXLRFae8bPP4dS5xL1tNB3k2o/N64cHJpwU7dxKli/nZ2lUa5fLg=="],
|
||||
|
||||
"pg-connection-string": ["pg-connection-string@2.9.1", "", {}, "sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w=="],
|
||||
|
||||
"pg-int8": ["pg-int8@1.0.1", "", {}, "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw=="],
|
||||
|
||||
"pg-numeric": ["pg-numeric@1.0.2", "", {}, "sha512-BM/Thnrw5jm2kKLE5uJkXqqExRUY/toLHda65XgFTBTFYZyopbKjBe29Ii3RbkvlsMoFwD+tHeGaCjjv0gHlyw=="],
|
||||
|
||||
"pg-pool": ["pg-pool@3.10.1", "", { "peerDependencies": { "pg": ">=8.0" } }, "sha512-Tu8jMlcX+9d8+QVzKIvM/uJtp07PKr82IUOYEphaWcoBhIYkoHpLXN3qO59nAI11ripznDsEzEv8nUxBVWajGg=="],
|
||||
|
||||
"pg-protocol": ["pg-protocol@1.10.3", "", {}, "sha512-6DIBgBQaTKDJyxnXaLiLR8wBpQQcGWuAESkRBX/t6OwA8YsqP+iVSiond2EDy6Y/dsGk8rh/jtax3js5NeV7JQ=="],
|
||||
|
||||
"pg-types": ["pg-types@2.2.0", "", { "dependencies": { "pg-int8": "1.0.1", "postgres-array": "~2.0.0", "postgres-bytea": "~1.0.0", "postgres-date": "~1.0.4", "postgres-interval": "^1.1.0" } }, "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA=="],
|
||||
|
||||
"pgpass": ["pgpass@1.0.5", "", { "dependencies": { "split2": "^4.1.0" } }, "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug=="],
|
||||
|
||||
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
|
||||
|
||||
"picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="],
|
||||
@@ -809,6 +835,16 @@
|
||||
|
||||
"pkg-dir": ["pkg-dir@4.2.0", "", { "dependencies": { "find-up": "^4.0.0" } }, "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ=="],
|
||||
|
||||
"postgres-array": ["postgres-array@2.0.0", "", {}, "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA=="],
|
||||
|
||||
"postgres-bytea": ["postgres-bytea@1.0.0", "", {}, "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w=="],
|
||||
|
||||
"postgres-date": ["postgres-date@1.0.7", "", {}, "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q=="],
|
||||
|
||||
"postgres-interval": ["postgres-interval@1.2.0", "", { "dependencies": { "xtend": "^4.0.0" } }, "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ=="],
|
||||
|
||||
"postgres-range": ["postgres-range@1.1.4", "", {}, "sha512-i/hbxIE9803Alj/6ytL7UHQxRvZkI9O4Sy+J3HGc4F4oo/2eQAjTSNJ0bfxyse3bH0nuVesCk+3IRLaMtG3H6w=="],
|
||||
|
||||
"pretty-format": ["pretty-format@30.0.5", "", { "dependencies": { "@jest/schemas": "30.0.5", "ansi-styles": "^5.2.0", "react-is": "^18.3.1" } }, "sha512-D1tKtYvByrBkFLe2wHJl2bwMJIiT8rW+XA+TiataH79/FszLQMrpGEvzUVkzPau7OCO0Qnrhpe87PqtOAIB8Yw=="],
|
||||
|
||||
"proto3-json-serializer": ["proto3-json-serializer@2.0.2", "", { "dependencies": { "protobufjs": "^7.2.5" } }, "sha512-SAzp/O4Yh02jGdRc+uIrGoe87dkN/XtwxfZ4ZyafJHymd79ozp5VG5nyZ7ygqPM5+cpLDjjGnYFUkngonyDPOQ=="],
|
||||
@@ -871,6 +907,8 @@
|
||||
|
||||
"source-map-support": ["source-map-support@0.5.13", "", { "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" } }, "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w=="],
|
||||
|
||||
"split2": ["split2@4.2.0", "", {}, "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg=="],
|
||||
|
||||
"sprintf-js": ["sprintf-js@1.0.3", "", {}, "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="],
|
||||
|
||||
"stack-utils": ["stack-utils@2.0.6", "", { "dependencies": { "escape-string-regexp": "^2.0.0" } }, "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ=="],
|
||||
@@ -969,6 +1007,8 @@
|
||||
|
||||
"write-file-atomic": ["write-file-atomic@5.0.1", "", { "dependencies": { "imurmurhash": "^0.1.4", "signal-exit": "^4.0.1" } }, "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw=="],
|
||||
|
||||
"xtend": ["xtend@4.0.2", "", {}, "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="],
|
||||
|
||||
"y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="],
|
||||
|
||||
"yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="],
|
||||
@@ -979,12 +1019,16 @@
|
||||
|
||||
"yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="],
|
||||
|
||||
"@babel/core/debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="],
|
||||
|
||||
"@babel/core/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
|
||||
|
||||
"@babel/helper-compilation-targets/lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="],
|
||||
|
||||
"@babel/helper-compilation-targets/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
|
||||
|
||||
"@babel/traverse/debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="],
|
||||
|
||||
"@google-cloud/storage/uuid": ["uuid@8.3.2", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="],
|
||||
|
||||
"@isaacs/cliui/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="],
|
||||
@@ -995,26 +1039,32 @@
|
||||
|
||||
"@istanbuljs/load-nyc-config/camelcase": ["camelcase@5.3.1", "", {}, "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="],
|
||||
|
||||
"@neondatabase/serverless/@types/pg": ["@types/pg@8.11.6", "", { "dependencies": { "@types/node": "*", "pg-protocol": "*", "pg-types": "^4.0.1" } }, "sha512-/2WmmBXHLsfRqzfHW7BNZ8SbYzE8OSk7i3WjFYvfgRHj7S1xj+16Je5fUKv3lVdVzk/zn9TXOqf+avFCFIE0yQ=="],
|
||||
|
||||
"anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
||||
|
||||
"body-parser/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
|
||||
"debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
|
||||
|
||||
"execa/signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="],
|
||||
|
||||
"express/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
|
||||
|
||||
"finalhandler/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
|
||||
|
||||
"gaxios/uuid": ["uuid@9.0.1", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA=="],
|
||||
|
||||
"google-gax/uuid": ["uuid@9.0.1", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA=="],
|
||||
|
||||
"http-proxy-agent/agent-base": ["agent-base@6.0.2", "", { "dependencies": { "debug": "4" } }, "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ=="],
|
||||
|
||||
"http-proxy-agent/debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="],
|
||||
|
||||
"https-proxy-agent/debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="],
|
||||
|
||||
"istanbul-lib-source-maps/debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="],
|
||||
|
||||
"jest-worker/supports-color": ["supports-color@8.1.1", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q=="],
|
||||
|
||||
"jsonwebtoken/jws": ["jws@3.2.2", "", { "dependencies": { "jwa": "^1.4.1", "safe-buffer": "^5.0.1" } }, "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA=="],
|
||||
|
||||
"jwks-rsa/debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="],
|
||||
|
||||
"micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
||||
|
||||
"p-locate/p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="],
|
||||
@@ -1023,8 +1073,6 @@
|
||||
|
||||
"pretty-format/ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="],
|
||||
|
||||
"send/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
|
||||
|
||||
"send/encodeurl": ["encodeurl@1.0.2", "", {}, "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w=="],
|
||||
|
||||
"send/mime": ["mime@1.6.0", "", { "bin": { "mime": "cli.js" } }, "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="],
|
||||
@@ -1045,18 +1093,22 @@
|
||||
|
||||
"@isaacs/cliui/wrap-ansi/ansi-styles": ["ansi-styles@6.2.1", "", {}, "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="],
|
||||
|
||||
"body-parser/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
|
||||
|
||||
"express/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
|
||||
|
||||
"finalhandler/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
|
||||
"@neondatabase/serverless/@types/pg/pg-types": ["pg-types@4.1.0", "", { "dependencies": { "pg-int8": "1.0.1", "pg-numeric": "1.0.2", "postgres-array": "~3.0.1", "postgres-bytea": "~3.0.0", "postgres-date": "~2.1.0", "postgres-interval": "^3.0.0", "postgres-range": "^1.1.1" } }, "sha512-o2XFanIMy/3+mThw69O8d4n1E5zsLhdO+OPqswezu7Z5ekP4hYDqlDjlmOpYMbzY2Br0ufCwJLdDIXeNVwcWFg=="],
|
||||
|
||||
"jsonwebtoken/jws/jwa": ["jwa@1.4.2", "", { "dependencies": { "buffer-equal-constant-time": "^1.0.1", "ecdsa-sig-formatter": "1.0.11", "safe-buffer": "^5.0.1" } }, "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw=="],
|
||||
|
||||
"send/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
|
||||
|
||||
"teeny-request/https-proxy-agent/agent-base": ["agent-base@6.0.2", "", { "dependencies": { "debug": "4" } }, "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ=="],
|
||||
|
||||
"teeny-request/https-proxy-agent/debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="],
|
||||
|
||||
"test-exclude/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="],
|
||||
|
||||
"@neondatabase/serverless/@types/pg/pg-types/postgres-array": ["postgres-array@3.0.4", "", {}, "sha512-nAUSGfSDGOaOAEGwqsRY27GPOea7CNipJPOA7lPbdEpx5Kg3qzdP0AaWC5MlhTWV9s4hFX39nomVZ+C4tnGOJQ=="],
|
||||
|
||||
"@neondatabase/serverless/@types/pg/pg-types/postgres-bytea": ["postgres-bytea@3.0.0", "", { "dependencies": { "obuf": "~1.1.2" } }, "sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw=="],
|
||||
|
||||
"@neondatabase/serverless/@types/pg/pg-types/postgres-date": ["postgres-date@2.1.0", "", {}, "sha512-K7Juri8gtgXVcDfZttFKVmhglp7epKb1K4pgrkLxehjqkrgPhfG6OO8LHLkfaqkbpjNRnra018XwAr1yQFWGcA=="],
|
||||
|
||||
"@neondatabase/serverless/@types/pg/pg-types/postgres-interval": ["postgres-interval@3.0.0", "", {}, "sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw=="],
|
||||
}
|
||||
}
|
||||
|
||||
98
functions/database.js
Normal file
98
functions/database.js
Normal file
@@ -0,0 +1,98 @@
|
||||
const { Pool } = require('pg');
|
||||
const { neon } = require('@neondatabase/serverless');
|
||||
|
||||
// Database configuration
|
||||
const DB_CONFIG = {
|
||||
// Use Neon serverless for edge deployments
|
||||
connectionString: process.env.DATABASE_URL || process.env.NEON_DATABASE_URL,
|
||||
ssl: process.env.NODE_ENV === 'production' ? { rejectUnauthorized: false } : false,
|
||||
};
|
||||
|
||||
// Create connection pool for traditional deployment
|
||||
const pool = new Pool(DB_CONFIG);
|
||||
|
||||
// Create Neon serverless client for edge/serverless deployment
|
||||
let neonClient = null;
|
||||
if (DB_CONFIG.connectionString) {
|
||||
neonClient = neon(DB_CONFIG.connectionString);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get database client - either pool connection or Neon serverless
|
||||
* @returns {Promise<any>} Database client
|
||||
*/
|
||||
async function getDbClient() {
|
||||
if (process.env.USE_NEON_SERVERLESS === 'true' && neonClient) {
|
||||
return neonClient;
|
||||
}
|
||||
|
||||
return pool.connect();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a query using the appropriate client
|
||||
* @param {string} query - SQL query
|
||||
* @param {any[]} params - Query parameters
|
||||
* @returns {Promise<any>} Query result
|
||||
*/
|
||||
async function executeQuery(query, params = []) {
|
||||
if (process.env.USE_NEON_SERVERLESS === 'true' && neonClient) {
|
||||
// Neon serverless client
|
||||
return await neonClient(query, params);
|
||||
} else {
|
||||
// Traditional pool connection
|
||||
const client = await pool.connect();
|
||||
try {
|
||||
const result = await client.query(query, params);
|
||||
return result.rows;
|
||||
} finally {
|
||||
client.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a transaction
|
||||
* @param {Function} callback - Function to execute within transaction
|
||||
* @returns {Promise<any>} Transaction result
|
||||
*/
|
||||
async function executeTransaction(callback) {
|
||||
if (process.env.USE_NEON_SERVERLESS === 'true' && neonClient) {
|
||||
// For Neon serverless, we'll need to handle this differently
|
||||
// Note: Neon serverless doesn't support traditional transactions
|
||||
// Each query is automatically committed
|
||||
return await callback(neonClient);
|
||||
} else {
|
||||
const client = await pool.connect();
|
||||
try {
|
||||
await client.query('BEGIN');
|
||||
const result = await callback(client);
|
||||
await client.query('COMMIT');
|
||||
return result;
|
||||
} catch (error) {
|
||||
await client.query('ROLLBACK');
|
||||
throw error;
|
||||
} finally {
|
||||
client.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Close all database connections
|
||||
*/
|
||||
async function closeConnections() {
|
||||
if (pool) {
|
||||
await pool.end();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
pool,
|
||||
neonClient,
|
||||
getDbClient,
|
||||
executeQuery,
|
||||
executeTransaction,
|
||||
closeConnections,
|
||||
DB_CONFIG
|
||||
};
|
||||
@@ -1,23 +1,12 @@
|
||||
const { onRequest } = require("firebase-functions/v2/https");
|
||||
const { setGlobalOptions, logger } = require("firebase-functions/v2");
|
||||
const admin = require("firebase-admin");
|
||||
const { VertexAI } = require('@google-cloud/vertexai');
|
||||
const Stripe = require("stripe");
|
||||
const { executeQuery, executeTransaction } = require('./database');
|
||||
|
||||
// Set global options for all functions to use us-central1 region
|
||||
|
||||
setGlobalOptions({ region: "us-central1" });
|
||||
|
||||
// const serviceAccount = require("./auditly-consulting-firebase-adminsdk-fbsvc-e4b51ef5cf.json");
|
||||
const serviceAccount = require("./auditly-c0027-firebase-adminsdk-fbsvc-1db7c58141.json")
|
||||
|
||||
admin.initializeApp({
|
||||
credential: admin.credential.cert(serviceAccount)
|
||||
});
|
||||
|
||||
//region Interface Clients
|
||||
const db = admin.firestore();
|
||||
|
||||
// Initialize Vertex AI with your project ID
|
||||
// This automatically uses IAM authentication from the service account
|
||||
const vertexAI = new VertexAI({
|
||||
@@ -441,59 +430,78 @@ const RESPONSE_FORMAT_COMPANY = {
|
||||
//endregion Constants
|
||||
|
||||
//region Helper Functions
|
||||
const validateAuthAndGetContext = async (req, res) => {
|
||||
const authHeader = req.headers.authorization;
|
||||
async function validateAuthAndGetContext(req, res) {
|
||||
// Set CORS headers for all requests
|
||||
res.setHeader('Access-Control-Allow-Origin', '*')
|
||||
.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS')
|
||||
.setHeader('Access-Control-Allow-Headers', 'Authorization, Content-Type');
|
||||
|
||||
if (req.method == "OPTIONS") {
|
||||
res.headers['Access-Control-Allow-Origin'] = '*';
|
||||
res.headers['Access-Control-Allow-Methods'] = 'GET, POST, OPTIONS';
|
||||
res.headers['Access-Control-Allow-Headers'] = 'Authorization, Content-Type';
|
||||
if (req.method === "OPTIONS") {
|
||||
res.status(204).send('');
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
|
||||
const authHeader = req.headers.authorization;
|
||||
|
||||
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
||||
throw new Error('Missing or invalid authorization header');
|
||||
res.status(401).json({ error: 'Missing or invalid authorization header' });
|
||||
return null;
|
||||
}
|
||||
|
||||
const token = authHeader.substring(7);
|
||||
|
||||
// Validate token format (should start with 'session_')
|
||||
if (!token.startsWith('session_')) {
|
||||
throw new Error('Invalid token format');
|
||||
// Validate token format more thoroughly
|
||||
if (!token.startsWith('session_') || token.length < 20) {
|
||||
res.status(401).json({ error: 'Invalid token format' });
|
||||
return null;
|
||||
}
|
||||
|
||||
// Look up token in Firestore
|
||||
const tokenDoc = await db.collection("authTokens").doc(token).get();
|
||||
try {
|
||||
// Look up token in PostgreSQL
|
||||
const tokenRows = await executeQuery(
|
||||
'SELECT * FROM auth_tokens WHERE token = $1 AND is_active = true',
|
||||
[token]
|
||||
);
|
||||
|
||||
if (!tokenDoc.exists) {
|
||||
throw new Error('Token not found');
|
||||
if (tokenRows.length === 0) {
|
||||
res.status(401).json({ error: 'Token not found' });
|
||||
return null;
|
||||
}
|
||||
|
||||
const tokenData = tokenRows[0];
|
||||
|
||||
if (Date.now() > tokenData.expires_at) {
|
||||
res.status(401).json({ error: 'Token has expired' });
|
||||
return null;
|
||||
}
|
||||
|
||||
// Update last used timestamp (don't await to avoid blocking)
|
||||
executeQuery(
|
||||
'UPDATE auth_tokens SET last_used_at = $1 WHERE token = $2',
|
||||
[Date.now(), token]
|
||||
).catch(error => {
|
||||
console.warn('Failed to update token lastUsedAt:', error);
|
||||
});
|
||||
|
||||
// Get user's organizations
|
||||
const userOrgsRows = await executeQuery(
|
||||
'SELECT organization_id FROM user_organizations WHERE user_id = $1',
|
||||
[tokenData.user_id]
|
||||
);
|
||||
|
||||
const orgIds = userOrgsRows.map(row => row.organization_id);
|
||||
|
||||
return {
|
||||
userId: tokenData.user_id,
|
||||
orgIds: orgIds,
|
||||
orgId: orgIds[0] || null,
|
||||
token: token
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Auth validation error:', error);
|
||||
res.status(500).json({ error: 'Authentication validation failed' });
|
||||
return null;
|
||||
}
|
||||
|
||||
const tokenData = tokenDoc.data();
|
||||
|
||||
if (!tokenData.isActive) {
|
||||
throw new Error('Token is inactive');
|
||||
}
|
||||
|
||||
if (Date.now() > tokenData.expiresAt) {
|
||||
throw new Error('Token has expired');
|
||||
}
|
||||
|
||||
// Update last used timestamp
|
||||
await tokenDoc.ref.update({ lastUsedAt: Date.now() });
|
||||
|
||||
// Get user's organizations
|
||||
const userOrgsSnapshot = await db.collection("users").doc(tokenData.userId).collection("organizations").get();
|
||||
const orgIds = userOrgsSnapshot.docs.map(doc => doc.id);
|
||||
|
||||
return {
|
||||
userId: tokenData.userId,
|
||||
orgIds: orgIds,
|
||||
// For backward compatibility, use first org as default
|
||||
orgId: orgIds[0] || null,
|
||||
token: token
|
||||
};
|
||||
};
|
||||
|
||||
const generateOTP = () => {
|
||||
@@ -640,9 +648,18 @@ const verifyUserAuthorization = async (userId, orgId) => {
|
||||
}
|
||||
|
||||
try {
|
||||
// Check if user exists in the organization's employees collection
|
||||
const employeeDoc = await db.collection("orgs").doc(orgId).collection("employees").doc(userId).get();
|
||||
return employeeDoc.exists;
|
||||
// Check if user exists in the organization's employees or as an organization member
|
||||
const employeeRows = await executeQuery(
|
||||
'SELECT id FROM employees WHERE organization_id = $1 AND (id = $2 OR user_id = $2)',
|
||||
[orgId, userId]
|
||||
);
|
||||
|
||||
const userOrgRows = await executeQuery(
|
||||
'SELECT user_id FROM user_organizations WHERE user_id = $1 AND organization_id = $2',
|
||||
[userId, orgId]
|
||||
);
|
||||
|
||||
return employeeRows.length > 0 || userOrgRows.length > 0;
|
||||
} catch (error) {
|
||||
console.error("Authorization check error:", error);
|
||||
return false;
|
||||
@@ -667,14 +684,19 @@ exports.sendOTP = onRequest({cors: true}, async (req, res) => {
|
||||
const otp = generateOTP();
|
||||
const expiresAt = Date.now() + (5 * 60 * 1000); // 5 minutes expiry
|
||||
|
||||
// Store OTP in Firestore
|
||||
await db.collection("otps").doc(email).set({
|
||||
otp,
|
||||
expiresAt,
|
||||
attempts: 0,
|
||||
inviteCode: inviteCode || null,
|
||||
createdAt: Date.now(),
|
||||
});
|
||||
// Store OTP in PostgreSQL
|
||||
await executeQuery(
|
||||
`INSERT INTO otps (email, otp, expires_at, attempts, invite_code, created_at)
|
||||
VALUES ($1, $2, $3, $4, $5, $6)
|
||||
ON CONFLICT (email)
|
||||
DO UPDATE SET
|
||||
otp = EXCLUDED.otp,
|
||||
expires_at = EXCLUDED.expires_at,
|
||||
attempts = 0,
|
||||
invite_code = EXCLUDED.invite_code,
|
||||
created_at = EXCLUDED.created_at`,
|
||||
[email, otp, expiresAt, 0, inviteCode || null, Date.now()]
|
||||
);
|
||||
|
||||
// In production, send actual email
|
||||
console.log(`📧 OTP for ${email}: ${otp} (expires in 5 minutes)`);
|
||||
@@ -705,56 +727,59 @@ exports.verifyOTP = onRequest({cors: true}, async (req, res) => {
|
||||
}
|
||||
|
||||
try {
|
||||
// Retrieve OTP document
|
||||
const otpDoc = await db.collection("otps").doc(email).get();
|
||||
// Retrieve OTP from PostgreSQL
|
||||
const otpRows = await executeQuery(
|
||||
'SELECT * FROM otps WHERE email = $1',
|
||||
[email]
|
||||
);
|
||||
|
||||
if (!otpDoc.exists) {
|
||||
if (otpRows.length === 0) {
|
||||
return res.status(400).json({ error: "Invalid verification code" });
|
||||
}
|
||||
|
||||
const otpData = otpDoc.data();
|
||||
const otpData = otpRows[0];
|
||||
|
||||
// Check if OTP is expired
|
||||
if (Date.now() > otpData.expiresAt) {
|
||||
await otpDoc.ref.delete();
|
||||
if (Date.now() > otpData.expires_at) {
|
||||
await executeQuery('DELETE FROM otps WHERE email = $1', [email]);
|
||||
return res.status(400).json({ error: "Verification code has expired" });
|
||||
}
|
||||
|
||||
// Check if too many attempts
|
||||
if (otpData.attempts >= 5) {
|
||||
await otpDoc.ref.delete();
|
||||
await executeQuery('DELETE FROM otps WHERE email = $1', [email]);
|
||||
return res.status(400).json({ error: "Too many failed attempts" });
|
||||
}
|
||||
|
||||
// Verify OTP
|
||||
if (otpData.otp !== otp) {
|
||||
await otpDoc.ref.update({
|
||||
attempts: (otpData.attempts || 0) + 1,
|
||||
});
|
||||
await executeQuery(
|
||||
'UPDATE otps SET attempts = $1 WHERE email = $2',
|
||||
[(otpData.attempts || 0) + 1, email]
|
||||
);
|
||||
return res.status(400).json({ error: "Invalid verification code" });
|
||||
}
|
||||
|
||||
// OTP is valid - clean up and create/find user
|
||||
await otpDoc.ref.delete();
|
||||
await executeQuery('DELETE FROM otps WHERE email = $1', [email]);
|
||||
|
||||
// Generate a unique user ID for this email if it doesn't exist
|
||||
let userId;
|
||||
let userDoc;
|
||||
let userExists = false;
|
||||
|
||||
// Check if user already exists by email
|
||||
const existingUserQuery = await db.collection("users")
|
||||
.where("email", "==", email)
|
||||
.limit(1)
|
||||
.get();
|
||||
const existingUserRows = await executeQuery(
|
||||
'SELECT id FROM users WHERE email = $1 LIMIT 1',
|
||||
[email]
|
||||
);
|
||||
|
||||
if (!existingUserQuery.empty) {
|
||||
if (existingUserRows.length > 0) {
|
||||
// User exists, get their ID
|
||||
userDoc = existingUserQuery.docs[0];
|
||||
userId = userDoc.id;
|
||||
userId = existingUserRows[0].id;
|
||||
userExists = true;
|
||||
} else {
|
||||
// Create new user
|
||||
userId = `user_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
||||
userDoc = null;
|
||||
}
|
||||
|
||||
// Prepare user object for response
|
||||
@@ -765,53 +790,52 @@ exports.verifyOTP = onRequest({cors: true}, async (req, res) => {
|
||||
emailVerified: true,
|
||||
};
|
||||
|
||||
// Create or update user document in Firestore
|
||||
const userRef = db.collection("users").doc(userId);
|
||||
|
||||
const userData = {
|
||||
id: userId,
|
||||
email: email,
|
||||
displayName: email.split("@")[0],
|
||||
emailVerified: true,
|
||||
lastLoginAt: Date.now(),
|
||||
};
|
||||
|
||||
if (!userDoc) {
|
||||
// Create or update user document in PostgreSQL
|
||||
const currentTime = Date.now();
|
||||
|
||||
if (!userExists) {
|
||||
// Create new user document
|
||||
userData.createdAt = Date.now();
|
||||
await userRef.set(userData);
|
||||
await executeQuery(
|
||||
`INSERT INTO users (id, email, display_name, email_verified, created_at, last_login_at)
|
||||
VALUES ($1, $2, $3, $4, $5, $6)`,
|
||||
[userId, email, email.split("@")[0], true, currentTime, currentTime]
|
||||
);
|
||||
} else {
|
||||
// Update existing user with latest login info
|
||||
await userRef.update({
|
||||
lastLoginAt: Date.now(),
|
||||
});
|
||||
await executeQuery(
|
||||
'UPDATE users SET last_login_at = $1 WHERE id = $2',
|
||||
[currentTime, userId]
|
||||
);
|
||||
}
|
||||
|
||||
// Generate a simple session token (in production, use proper JWT)
|
||||
const customToken = `session_${userId}_${Date.now()}`;
|
||||
|
||||
// Store auth token in Firestore for validation
|
||||
await db.collection("authTokens").doc(customToken).set({
|
||||
userId: userId,
|
||||
createdAt: Date.now(),
|
||||
expiresAt: Date.now() + (30 * 24 * 60 * 60 * 1000), // 30 days
|
||||
lastUsedAt: Date.now(),
|
||||
isActive: true
|
||||
});
|
||||
// Store auth token in PostgreSQL for validation
|
||||
await executeQuery(
|
||||
`INSERT INTO auth_tokens (token, user_id, created_at, expires_at, last_used_at, is_active)
|
||||
VALUES ($1, $2, $3, $4, $5, $6)`,
|
||||
[
|
||||
customToken,
|
||||
userId,
|
||||
currentTime,
|
||||
currentTime + (30 * 24 * 60 * 60 * 1000), // 30 days
|
||||
currentTime,
|
||||
true
|
||||
]
|
||||
);
|
||||
|
||||
// Handle invitation if present
|
||||
let inviteData = null;
|
||||
if (otpData.inviteCode) {
|
||||
if (otpData.invite_code) {
|
||||
try {
|
||||
const inviteDoc = await db
|
||||
.collectionGroup("invites")
|
||||
.where("code", "==", otpData.inviteCode)
|
||||
.where("status", "==", "pending")
|
||||
.limit(1)
|
||||
.get();
|
||||
const inviteRows = await executeQuery(
|
||||
'SELECT * FROM invites WHERE code = $1 AND status = $2 LIMIT 1',
|
||||
[otpData.invite_code, 'pending']
|
||||
);
|
||||
|
||||
if (!inviteDoc.empty) {
|
||||
inviteData = inviteDoc.docs[0].data();
|
||||
if (inviteRows.length > 0) {
|
||||
inviteData = inviteRows[0];
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching invite:", error);
|
||||
@@ -833,9 +857,8 @@ exports.verifyOTP = onRequest({cors: true}, async (req, res) => {
|
||||
|
||||
//region Create Invitation
|
||||
exports.createInvitation = onRequest({cors: true}, async (req, res) => {
|
||||
|
||||
if (req.method === 'OPTIONS') {
|
||||
res.status(204).send('');
|
||||
const authContext = await validateAuthAndGetContext(req, res);
|
||||
if (!authContext) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -844,8 +867,6 @@ exports.createInvitation = onRequest({cors: true}, async (req, res) => {
|
||||
}
|
||||
|
||||
try {
|
||||
// Validate auth token and get user context
|
||||
const authContext = await validateAuthAndGetContext(req, res);
|
||||
const { name, email, role = "employee", department } = req.body;
|
||||
|
||||
if (!email || !name) {
|
||||
@@ -925,7 +946,7 @@ exports.createInvitation = onRequest({cors: true}, async (req, res) => {
|
||||
},
|
||||
},
|
||||
],
|
||||
from: { email: 'no-reply@auditly.com', name: 'Auditly' },
|
||||
from: { email: 'no-reply@orbitly.com', name: 'Orbitly' },
|
||||
template_id: process.env.SENDGRID_TEMPLATE_ID,
|
||||
}),
|
||||
});
|
||||
@@ -1196,6 +1217,9 @@ exports.submitEmployeeAnswers = onRequest({cors: true}, async (req, res) => {
|
||||
} else {
|
||||
// Authenticated submission
|
||||
const authContext = await validateAuthAndGetContext(req, res);
|
||||
if (!authContext) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!employeeId || !answers) {
|
||||
return res.status(400).json({ error: "Employee ID and answers are required for authenticated submissions" });
|
||||
@@ -1391,9 +1415,8 @@ Be thorough, professional, and focus on actionable insights.
|
||||
|
||||
//region Generate Employee Report
|
||||
exports.generateEmployeeReport = onRequest({cors: true}, async (req, res) => {
|
||||
|
||||
if (req.method === 'OPTIONS') {
|
||||
res.status(204).send('');
|
||||
const authContext = await validateAuthAndGetContext(req, res);
|
||||
if (!authContext) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1511,8 +1534,8 @@ Be thorough, professional, and focus on actionable insights.
|
||||
|
||||
//region Generate Company Report
|
||||
exports.generateCompanyReport = onRequest({cors: true}, async (req, res) => {
|
||||
if (req.method === 'OPTIONS') {
|
||||
res.status(204).send('');
|
||||
const authContext = await validateAuthAndGetContext(req, res);
|
||||
if (!authContext) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1520,8 +1543,6 @@ exports.generateCompanyReport = onRequest({cors: true}, async (req, res) => {
|
||||
return res.status(405).json({ error: "Method not allowed" });
|
||||
}
|
||||
|
||||
const authContext = await validateAuthAndGetContext(req, res);
|
||||
|
||||
const orgId = authContext.orgId;
|
||||
if (!orgId) {
|
||||
return res.status(400).json({ error: "User has no associated organizations" });
|
||||
@@ -1646,9 +1667,8 @@ Be thorough, professional, and focus on actionable insights.`;
|
||||
|
||||
//region Chat
|
||||
exports.chat = onRequest({cors: true}, async (req, res) => {
|
||||
|
||||
if (req.method === 'OPTIONS') {
|
||||
res.status(204).send('');
|
||||
const authContext = await validateAuthAndGetContext(req, res);
|
||||
if (!authContext) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1739,8 +1759,8 @@ Instructions:
|
||||
|
||||
//region Create Organization
|
||||
exports.createOrganization = onRequest({cors: true}, async (req, res) => {
|
||||
if (req.method === 'OPTIONS') {
|
||||
res.status(204).send('');
|
||||
const authContext = await validateAuthAndGetContext(req, res);
|
||||
if (!authContext) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1749,90 +1769,63 @@ exports.createOrganization = onRequest({cors: true}, async (req, res) => {
|
||||
}
|
||||
|
||||
try {
|
||||
// Validate auth token and get user context
|
||||
const authContext = await validateAuthAndGetContext(req, res);
|
||||
const { name } = req.body;
|
||||
|
||||
if (!name) {
|
||||
return res.status(400).json({ error: "Organization name is required" });
|
||||
}
|
||||
|
||||
// Generate unique organization ID
|
||||
const orgId = `org_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
||||
const currentTime = Date.now();
|
||||
|
||||
// Create comprehensive organization document
|
||||
const orgData = {
|
||||
name,
|
||||
createdAt: Date.now(),
|
||||
updatedAt: Date.now(),
|
||||
onboardingCompleted: false,
|
||||
ownerId: authContext.userId,
|
||||
// Subscription fields (will be populated after Stripe setup)
|
||||
subscription: {
|
||||
status: 'trial', // trial, active, past_due, canceled
|
||||
stripeCustomerId: null,
|
||||
stripeSubscriptionId: null,
|
||||
currentPeriodStart: null,
|
||||
currentPeriodEnd: null,
|
||||
trialEnd: Date.now() + (14 * 24 * 60 * 60 * 1000), // 14 day trial
|
||||
},
|
||||
// Usage tracking
|
||||
usage: {
|
||||
employeeCount: 0,
|
||||
reportsGenerated: 0,
|
||||
lastReportGeneration: null,
|
||||
},
|
||||
// Organization settings
|
||||
settings: {
|
||||
allowedEmployeeCount: 50, // Default limit
|
||||
featuresEnabled: {
|
||||
aiReports: true,
|
||||
chat: true,
|
||||
analytics: true,
|
||||
}
|
||||
await executeTransaction(async (client) => {
|
||||
// Create comprehensive organization document
|
||||
if (process.env.USE_NEON_SERVERLESS === 'true') {
|
||||
await client(
|
||||
`INSERT INTO organizations (
|
||||
id, name, owner_id, onboarding_completed,
|
||||
subscription_status, trial_end, employee_count, reports_generated,
|
||||
allowed_employee_count, features_enabled, created_at, updated_at
|
||||
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)`,
|
||||
[
|
||||
orgId, name, authContext.userId, false,
|
||||
'trial', currentTime + (14 * 24 * 60 * 60 * 1000), // 14 day trial
|
||||
0, 0, 50,
|
||||
JSON.stringify({"aiReports": true, "chat": true, "analytics": true}),
|
||||
currentTime, currentTime
|
||||
]
|
||||
);
|
||||
|
||||
// Add organization to user's organizations (for multi-org support)
|
||||
await client(
|
||||
`INSERT INTO user_organizations (user_id, organization_id, role, onboarding_completed, joined_at)
|
||||
VALUES ($1, $2, $3, $4, $5)`,
|
||||
[authContext.userId, orgId, 'owner', false, currentTime]
|
||||
);
|
||||
} else {
|
||||
await client.query(
|
||||
`INSERT INTO organizations (
|
||||
id, name, owner_id, onboarding_completed,
|
||||
subscription_status, trial_end, employee_count, reports_generated,
|
||||
allowed_employee_count, features_enabled, created_at, updated_at
|
||||
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)`,
|
||||
[
|
||||
orgId, name, authContext.userId, false,
|
||||
'trial', currentTime + (14 * 24 * 60 * 60 * 1000), // 14 day trial
|
||||
0, 0, 50,
|
||||
JSON.stringify({"aiReports": true, "chat": true, "analytics": true}),
|
||||
currentTime, currentTime
|
||||
]
|
||||
);
|
||||
|
||||
// Add organization to user's organizations (for multi-org support)
|
||||
await client.query(
|
||||
`INSERT INTO user_organizations (user_id, organization_id, role, onboarding_completed, joined_at)
|
||||
VALUES ($1, $2, $3, $4, $5)`,
|
||||
[authContext.userId, orgId, 'owner', false, currentTime]
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const orgRef = db.collection("orgs").doc(orgId);
|
||||
await orgRef.set(orgData);
|
||||
|
||||
// Get user information from Firestore (since we don't use Firebase Auth)
|
||||
const userRef = db.collection("users").doc(authContext.userId);
|
||||
const userDoc = await userRef.get();
|
||||
|
||||
if (!userDoc.exists) {
|
||||
console.error("User document not found:", authContext.userId);
|
||||
return res.status(400).json({ error: "User not found" });
|
||||
}
|
||||
|
||||
const userData = userDoc.data();
|
||||
|
||||
// Add owner info to organization document (owners are NOT employees)
|
||||
const ownerInfo = {
|
||||
id: authContext.userId,
|
||||
name: userData.displayName || userData.email.split("@")[0],
|
||||
email: userData.email,
|
||||
joinedAt: Date.now()
|
||||
};
|
||||
|
||||
// Update org document with owner info
|
||||
await orgRef.update({
|
||||
ownerInfo: ownerInfo,
|
||||
updatedAt: Date.now()
|
||||
});
|
||||
|
||||
// Add organization to user's organizations (for multi-org support)
|
||||
const userOrgRef = db.collection("users").doc(authContext.userId).collection("organizations").doc(orgId);
|
||||
await userOrgRef.set({
|
||||
orgId,
|
||||
name,
|
||||
role: "owner",
|
||||
onboardingCompleted: false,
|
||||
joinedAt: Date.now(),
|
||||
});
|
||||
|
||||
// Update user document with latest activity
|
||||
await userRef.update({
|
||||
lastLoginAt: Date.now(),
|
||||
});
|
||||
|
||||
res.json({
|
||||
@@ -1841,9 +1834,12 @@ exports.createOrganization = onRequest({cors: true}, async (req, res) => {
|
||||
name,
|
||||
role: "owner",
|
||||
onboardingCompleted: false,
|
||||
joinedAt: Date.now(),
|
||||
subscription: orgData.subscription,
|
||||
requiresSubscription: true, // Signal frontend to show subscription flow
|
||||
joinedAt: currentTime,
|
||||
subscription: {
|
||||
status: 'trial',
|
||||
trialEnd: currentTime + (14 * 24 * 60 * 60 * 1000)
|
||||
},
|
||||
requiresSubscription: true,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Create organization error:", error);
|
||||
@@ -1853,17 +1849,9 @@ exports.createOrganization = onRequest({cors: true}, async (req, res) => {
|
||||
//endregion Create Organization
|
||||
|
||||
//region Get Organizations
|
||||
exports.getUserOrganizations = onRequest(async (req, res) => {
|
||||
let authContext;
|
||||
try {
|
||||
authContext = await validateAuthAndGetContext(req, res);
|
||||
} catch (error) {
|
||||
logger.debug("Auth validation failed:", error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (req.method === 'OPTIONS') {
|
||||
res.status(204).send('');
|
||||
exports.getUserOrganizations = onRequest({cors: true}, async (req, res) => {
|
||||
const authContext = await validateAuthAndGetContext(req, res);
|
||||
if (!authContext) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1872,8 +1860,6 @@ exports.getUserOrganizations = onRequest(async (req, res) => {
|
||||
}
|
||||
|
||||
try {
|
||||
// Validate auth token and get user context
|
||||
|
||||
// Get user's organizations
|
||||
const userOrgsSnapshot = await db
|
||||
.collection("users")
|
||||
@@ -1906,8 +1892,8 @@ exports.getUserOrganizations = onRequest(async (req, res) => {
|
||||
|
||||
//region Join Organization
|
||||
exports.joinOrganization = onRequest({cors: true}, async (req, res) => {
|
||||
if (req.method === 'OPTIONS') {
|
||||
res.status(204).send('');
|
||||
const authContext = await validateAuthAndGetContext(req, res);
|
||||
if (!authContext) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1916,8 +1902,6 @@ exports.joinOrganization = onRequest({cors: true}, async (req, res) => {
|
||||
}
|
||||
|
||||
try {
|
||||
// Validate auth token and get user context
|
||||
const authContext = await validateAuthAndGetContext(req, res);
|
||||
const { inviteCode } = req.body;
|
||||
|
||||
if (!inviteCode) {
|
||||
@@ -2167,7 +2151,7 @@ exports.joinOrganization = onRequest({cors: true}, async (req, res) => {
|
||||
// res.status(500).json({ error: 'Webhook handler failed' });
|
||||
// }
|
||||
// });
|
||||
//endregion Stripe Webhook
|
||||
//#endregion Stripe Webhook
|
||||
|
||||
//region Get Sub Status
|
||||
// exports.getSubscriptionStatus = onRequest(async (req, res) => {
|
||||
@@ -2228,7 +2212,7 @@ exports.joinOrganization = onRequest({cors: true}, async (req, res) => {
|
||||
// res.status(500).json({ error: "Failed to get subscription status" });
|
||||
// }
|
||||
// });
|
||||
//endregion Get Sub Status
|
||||
//#endregion Get Sub Status
|
||||
|
||||
//region Save Company Report
|
||||
// exports.saveCompanyReport = onRequest(async (req, res) => {
|
||||
@@ -2272,12 +2256,12 @@ exports.joinOrganization = onRequest({cors: true}, async (req, res) => {
|
||||
// res.status(500).json({ error: "Failed to save company report" });
|
||||
// }
|
||||
// });
|
||||
//endregion Save Company Report
|
||||
//#endregion Save Company Report
|
||||
|
||||
//region Get Org Data
|
||||
exports.getOrgData = onRequest({cors: true}, async (req, res) => {
|
||||
if (req.method === 'OPTIONS') {
|
||||
res.status(204).send('');
|
||||
const authContext = await validateAuthAndGetContext(req, res);
|
||||
if (!authContext) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2286,21 +2270,34 @@ exports.getOrgData = onRequest({cors: true}, async (req, res) => {
|
||||
}
|
||||
|
||||
try {
|
||||
// Validate auth token and get user context
|
||||
const authContext = await validateAuthAndGetContext(req, res);
|
||||
|
||||
const orgId = authContext.orgId;
|
||||
if (!orgId) {
|
||||
return res.status(400).json({ error: "User has no associated organizations" });
|
||||
}
|
||||
|
||||
// Get organization data
|
||||
const orgDoc = await db.collection("orgs").doc(orgId).get();
|
||||
if (!orgDoc.exists) {
|
||||
const orgRows = await executeQuery(
|
||||
'SELECT * FROM organizations WHERE id = $1',
|
||||
[orgId]
|
||||
);
|
||||
|
||||
if (orgRows.length === 0) {
|
||||
return res.status(404).json({ error: "Organization not found" });
|
||||
}
|
||||
|
||||
const orgData = { id: orgId, ...orgDoc.data() };
|
||||
const orgData = {
|
||||
id: orgRows[0].id,
|
||||
name: orgRows[0].name,
|
||||
industry: orgRows[0].industry,
|
||||
description: orgRows[0].description,
|
||||
mission: orgRows[0].mission,
|
||||
vision: orgRows[0].vision,
|
||||
values: orgRows[0].values,
|
||||
onboardingCompleted: orgRows[0].onboarding_completed,
|
||||
onboardingData: orgRows[0].onboarding_data,
|
||||
createdAt: orgRows[0].created_at,
|
||||
updatedAt: orgRows[0].updated_at
|
||||
};
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
@@ -2308,10 +2305,6 @@ exports.getOrgData = onRequest({cors: true}, async (req, res) => {
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Get org data error:", error);
|
||||
if (error.message.includes('Missing or invalid authorization') ||
|
||||
error.message.includes('Token')) {
|
||||
return res.status(401).json({ error: error.message });
|
||||
}
|
||||
res.status(500).json({ error: "Failed to get organization data" });
|
||||
}
|
||||
});
|
||||
@@ -2319,8 +2312,8 @@ exports.getOrgData = onRequest({cors: true}, async (req, res) => {
|
||||
|
||||
//region Update Organization Data
|
||||
exports.updateOrgData = onRequest({cors: true}, async (req, res) => {
|
||||
if (req.method === 'OPTIONS') {
|
||||
res.status(204).send('');
|
||||
const authContext = await validateAuthAndGetContext(req, res);
|
||||
if (!authContext) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2329,8 +2322,6 @@ exports.updateOrgData = onRequest({cors: true}, async (req, res) => {
|
||||
}
|
||||
|
||||
try {
|
||||
// Validate auth token and get user context
|
||||
const authContext = await validateAuthAndGetContext(req, res);
|
||||
const { data } = req.body;
|
||||
|
||||
if (!data) {
|
||||
@@ -2366,8 +2357,8 @@ exports.updateOrgData = onRequest({cors: true}, async (req, res) => {
|
||||
|
||||
//region Get Employees
|
||||
exports.getEmployees = onRequest({cors: true}, async (req, res) => {
|
||||
if (req.method === 'OPTIONS') {
|
||||
res.status(204).send('');
|
||||
const authContext = await validateAuthAndGetContext(req, res);
|
||||
if (!authContext) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2376,9 +2367,6 @@ exports.getEmployees = onRequest({cors: true}, async (req, res) => {
|
||||
}
|
||||
|
||||
try {
|
||||
// Validate auth token and get user context
|
||||
const authContext = await validateAuthAndGetContext(req, res);
|
||||
|
||||
const orgId = authContext.orgId;
|
||||
if (!orgId) {
|
||||
return res.status(400).json({ error: "User has no associated organizations" });
|
||||
@@ -2413,8 +2401,8 @@ exports.getEmployees = onRequest({cors: true}, async (req, res) => {
|
||||
|
||||
//region Get Submissions
|
||||
exports.getSubmissions = onRequest({cors: true}, async (req, res) => {
|
||||
if (req.method === 'OPTIONS') {
|
||||
res.status(204).send('');
|
||||
const authContext = await validateAuthAndGetContext(req, res);
|
||||
if (!authContext) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2423,9 +2411,6 @@ exports.getSubmissions = onRequest({cors: true}, async (req, res) => {
|
||||
}
|
||||
|
||||
try {
|
||||
// Validate auth token and get user context
|
||||
const authContext = await validateAuthAndGetContext(req, res);
|
||||
|
||||
const orgId = authContext.orgId;
|
||||
if (!orgId) {
|
||||
return res.status(400).json({ error: "User has no associated organizations" });
|
||||
@@ -2456,8 +2441,8 @@ exports.getSubmissions = onRequest({cors: true}, async (req, res) => {
|
||||
|
||||
//region Get Reports
|
||||
exports.getReports = onRequest({cors: true}, async (req, res) => {
|
||||
if (req.method === 'OPTIONS') {
|
||||
res.status(204).send('');
|
||||
const authContext = await validateAuthAndGetContext(req, res);
|
||||
if (!authContext) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2466,9 +2451,6 @@ exports.getReports = onRequest({cors: true}, async (req, res) => {
|
||||
}
|
||||
|
||||
try {
|
||||
// Validate auth token and get user context
|
||||
const authContext = await validateAuthAndGetContext(req, res);
|
||||
|
||||
const orgId = authContext.orgId;
|
||||
if (!orgId) {
|
||||
return res.status(400).json({ error: "User has no associated organizations" });
|
||||
@@ -2552,8 +2534,8 @@ exports.getReports = onRequest({cors: true}, async (req, res) => {
|
||||
|
||||
//region Save Report
|
||||
exports.saveReport = onRequest({cors: true}, async (req, res) => {
|
||||
if (req.method === 'OPTIONS') {
|
||||
res.status(204).send('');
|
||||
const authContext = await validateAuthAndGetContext(req, res);
|
||||
if (!authContext) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2568,12 +2550,6 @@ exports.saveReport = onRequest({cors: true}, async (req, res) => {
|
||||
}
|
||||
|
||||
try {
|
||||
// Verify user authorization
|
||||
const isAuthorized = await verifyUserAuthorization(userId, orgId);
|
||||
if (!isAuthorized) {
|
||||
return res.status(403).json({ error: "Unauthorized access to organization" });
|
||||
}
|
||||
|
||||
// Add metadata
|
||||
const currentTime = Date.now();
|
||||
if (!reportData.id) {
|
||||
@@ -2603,8 +2579,8 @@ exports.saveReport = onRequest({cors: true}, async (req, res) => {
|
||||
|
||||
//region Get Company Reports
|
||||
exports.getCompanyReports = onRequest({cors: true}, async (req, res) => {
|
||||
if (req.method === 'OPTIONS') {
|
||||
res.status(204).send('');
|
||||
const authContext = await validateAuthAndGetContext(req, res);
|
||||
if (!authContext) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2613,8 +2589,6 @@ exports.getCompanyReports = onRequest({cors: true}, async (req, res) => {
|
||||
}
|
||||
|
||||
try {
|
||||
const authContext = await validateAuthAndGetContext(req, res);
|
||||
|
||||
const orgId = authContext.orgId;
|
||||
if (!orgId) {
|
||||
return res.status(400).json({ error: "User has no associated organizations" });
|
||||
@@ -2646,8 +2620,8 @@ exports.getCompanyReports = onRequest({cors: true}, async (req, res) => {
|
||||
|
||||
//region Upload Image
|
||||
exports.uploadImage = onRequest({cors: true}, async (req, res) => {
|
||||
if (req.method === 'OPTIONS') {
|
||||
res.status(204).send('');
|
||||
const authContext = await validateAuthAndGetContext(req, res);
|
||||
if (!authContext) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2655,19 +2629,14 @@ exports.uploadImage = onRequest({cors: true}, async (req, res) => {
|
||||
return res.status(405).json({ error: "Method not allowed" });
|
||||
}
|
||||
|
||||
const { orgId, userId, imageData } = req.body;
|
||||
const { orgId, userId } = authContext;
|
||||
const { imageData } = req.body;
|
||||
|
||||
if (!orgId || !userId || !imageData) {
|
||||
return res.status(400).json({ error: "Organization ID, user ID, and image data are required" });
|
||||
}
|
||||
|
||||
try {
|
||||
// Verify user authorization
|
||||
const isAuthorized = await verifyUserAuthorization(userId, orgId);
|
||||
if (!isAuthorized) {
|
||||
return res.status(403).json({ error: "Unauthorized access to organization" });
|
||||
}
|
||||
|
||||
// Validate image data
|
||||
const { collectionName, documentId, dataUrl, filename, originalSize, compressedSize, width, height } = imageData;
|
||||
|
||||
@@ -2764,7 +2733,7 @@ exports.uploadImage = onRequest({cors: true}, async (req, res) => {
|
||||
// res.status(500).json({ error: "Failed to get image" });
|
||||
// }
|
||||
// });
|
||||
//endregion Get Image
|
||||
//#endregion Get Image
|
||||
|
||||
//region Delete Image
|
||||
// exports.deleteImage = onRequest(async (req, res) => {
|
||||
@@ -2809,4 +2778,4 @@ exports.uploadImage = onRequest({cors: true}, async (req, res) => {
|
||||
// res.status(500).json({ error: "Failed to delete image" });
|
||||
// }
|
||||
// });
|
||||
//endregion Delete Image
|
||||
//#endregion Delete Image
|
||||
|
||||
358
functions/migrated_functions.js
Normal file
358
functions/migrated_functions.js
Normal file
@@ -0,0 +1,358 @@
|
||||
// Complete Migration Helper Script for remaining functions
|
||||
// This script contains the migrated versions of key functions
|
||||
|
||||
const { executeQuery, executeTransaction } = require('./database');
|
||||
|
||||
/**
|
||||
* Get User Organizations - Migrated to PostgreSQL
|
||||
*/
|
||||
exports.getUserOrganizations = onRequest({cors: true}, async (req, res) => {
|
||||
const authContext = await validateAuthAndGetContext(req, res);
|
||||
if (!authContext) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (req.method !== "GET") {
|
||||
return res.status(405).json({ error: "Method not allowed" });
|
||||
}
|
||||
|
||||
try {
|
||||
// Get user's organizations
|
||||
const userOrgsRows = await executeQuery(
|
||||
`SELECT uo.organization_id as orgId, uo.role, uo.joined_at as joinedAt,
|
||||
uo.onboarding_completed as onboardingCompleted, o.name
|
||||
FROM user_organizations uo
|
||||
JOIN organizations o ON uo.organization_id = o.id
|
||||
WHERE uo.user_id = $1`,
|
||||
[authContext.userId]
|
||||
);
|
||||
|
||||
const organizations = userOrgsRows.map(row => ({
|
||||
orgId: row.orgid,
|
||||
name: row.name,
|
||||
role: row.role,
|
||||
onboardingCompleted: row.onboardingcompleted,
|
||||
joinedAt: row.joinedat
|
||||
}));
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
organizations,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Get user organizations error:", error);
|
||||
res.status(500).json({ error: "Failed to get user organizations" });
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Get Organization Data - Migrated to PostgreSQL
|
||||
*/
|
||||
exports.getOrgData = onRequest({cors: true}, async (req, res) => {
|
||||
const authContext = await validateAuthAndGetContext(req, res);
|
||||
if (!authContext) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (req.method !== "GET") {
|
||||
return res.status(405).json({ error: "Method not allowed" });
|
||||
}
|
||||
|
||||
try {
|
||||
const orgId = authContext.orgId;
|
||||
if (!orgId) {
|
||||
return res.status(400).json({ error: "User has no associated organizations" });
|
||||
}
|
||||
|
||||
// Get organization data
|
||||
const orgRows = await executeQuery(
|
||||
'SELECT * FROM organizations WHERE id = $1',
|
||||
[orgId]
|
||||
);
|
||||
|
||||
if (orgRows.length === 0) {
|
||||
return res.status(404).json({ error: "Organization not found" });
|
||||
}
|
||||
|
||||
const orgData = {
|
||||
id: orgRows[0].id,
|
||||
name: orgRows[0].name,
|
||||
industry: orgRows[0].industry,
|
||||
description: orgRows[0].description,
|
||||
mission: orgRows[0].mission,
|
||||
vision: orgRows[0].vision,
|
||||
values: orgRows[0].values,
|
||||
onboardingCompleted: orgRows[0].onboarding_completed,
|
||||
onboardingData: orgRows[0].onboarding_data,
|
||||
createdAt: orgRows[0].created_at,
|
||||
updatedAt: orgRows[0].updated_at
|
||||
};
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
org: orgData
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Get org data error:", error);
|
||||
res.status(500).json({ error: "Failed to get organization data" });
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Update Organization Data - Migrated to PostgreSQL
|
||||
*/
|
||||
exports.updateOrgData = onRequest({cors: true}, async (req, res) => {
|
||||
const authContext = await validateAuthAndGetContext(req, res);
|
||||
if (!authContext) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (req.method !== "PUT") {
|
||||
return res.status(405).json({ error: "Method not allowed" });
|
||||
}
|
||||
|
||||
try {
|
||||
const { data } = req.body;
|
||||
|
||||
if (!data) {
|
||||
return res.status(400).json({ error: "Data is required" });
|
||||
}
|
||||
|
||||
const orgId = authContext.orgId;
|
||||
if (!orgId) {
|
||||
return res.status(400).json({ error: "User has no associated organizations" });
|
||||
}
|
||||
|
||||
// Build dynamic update query
|
||||
const updateFields = [];
|
||||
const values = [];
|
||||
let paramCount = 1;
|
||||
|
||||
for (const [key, value] of Object.entries(data)) {
|
||||
// Convert camelCase to snake_case for database columns
|
||||
const dbKey = key.replace(/([A-Z])/g, '_$1').toLowerCase();
|
||||
updateFields.push(`${dbKey} = $${paramCount}`);
|
||||
values.push(typeof value === 'object' ? JSON.stringify(value) : value);
|
||||
paramCount++;
|
||||
}
|
||||
|
||||
if (updateFields.length === 0) {
|
||||
return res.status(400).json({ error: "No valid fields to update" });
|
||||
}
|
||||
|
||||
updateFields.push(`updated_at = $${paramCount}`);
|
||||
values.push(Date.now());
|
||||
values.push(orgId); // for WHERE clause
|
||||
|
||||
const query = `UPDATE organizations SET ${updateFields.join(', ')} WHERE id = $${paramCount + 1}`;
|
||||
await executeQuery(query, values);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: "Organization data updated successfully"
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Update org data error:", error);
|
||||
res.status(500).json({ error: "Failed to update organization data" });
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Get Employees - Migrated to PostgreSQL
|
||||
*/
|
||||
exports.getEmployees = onRequest({cors: true}, async (req, res) => {
|
||||
const authContext = await validateAuthAndGetContext(req, res);
|
||||
if (!authContext) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (req.method !== "GET") {
|
||||
return res.status(405).json({ error: "Method not allowed" });
|
||||
}
|
||||
|
||||
try {
|
||||
const orgId = authContext.orgId;
|
||||
if (!orgId) {
|
||||
return res.status(400).json({ error: "User has no associated organizations" });
|
||||
}
|
||||
|
||||
// Get all employees
|
||||
const employeesRows = await executeQuery(
|
||||
'SELECT * FROM employees WHERE organization_id = $1 AND status != $2 ORDER BY name',
|
||||
[orgId, 'deleted']
|
||||
);
|
||||
|
||||
const employees = employeesRows.map(row => ({
|
||||
id: row.id,
|
||||
name: row.name,
|
||||
email: row.email,
|
||||
role: row.role,
|
||||
department: row.department,
|
||||
status: row.status,
|
||||
joinedAt: row.joined_at,
|
||||
createdAt: row.created_at
|
||||
}));
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
employees
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Get employees error:", error);
|
||||
res.status(500).json({ error: "Failed to get employees" });
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Create Invitation - Migrated to PostgreSQL
|
||||
*/
|
||||
exports.createInvitation = onRequest({cors: true}, async (req, res) => {
|
||||
const authContext = await validateAuthAndGetContext(req, res);
|
||||
if (!authContext) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (req.method !== "POST") {
|
||||
return res.status(405).json({ error: "Method not allowed" });
|
||||
}
|
||||
|
||||
try {
|
||||
const { name, email, role = "employee", department } = req.body;
|
||||
|
||||
if (!email || !name) {
|
||||
return res.status(400).json({ error: "Name and email are required" });
|
||||
}
|
||||
|
||||
const orgId = authContext.orgId;
|
||||
if (!orgId) {
|
||||
return res.status(400).json({ error: "User has no associated organizations" });
|
||||
}
|
||||
|
||||
// Generate invite code
|
||||
const code = Math.random().toString(36).substring(2, 15);
|
||||
|
||||
// Generate employee ID
|
||||
const employeeId = `emp_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
||||
const currentTime = Date.now();
|
||||
|
||||
// Create employee data
|
||||
const employeeData = {
|
||||
id: employeeId,
|
||||
name: name.trim(),
|
||||
email: email.trim(),
|
||||
role: role?.trim() || "employee",
|
||||
department: department?.trim() || "General",
|
||||
status: "invited",
|
||||
inviteCode: code
|
||||
};
|
||||
|
||||
await executeTransaction(async (client) => {
|
||||
// Store invitation
|
||||
if (process.env.USE_NEON_SERVERLESS === 'true') {
|
||||
await client(
|
||||
`INSERT INTO invites (code, organization_id, email, employee_data, status, created_at, expires_at)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7)`,
|
||||
[
|
||||
code, orgId, email, JSON.stringify(employeeData), 'pending',
|
||||
currentTime, currentTime + (7 * 24 * 60 * 60 * 1000) // 7 days
|
||||
]
|
||||
);
|
||||
|
||||
// Create employee record
|
||||
await client(
|
||||
`INSERT INTO employees (id, organization_id, name, email, role, department, status, invite_code, created_at)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)`,
|
||||
[employeeId, orgId, name.trim(), email.trim(), role || 'employee', department || 'General', 'invited', code, currentTime]
|
||||
);
|
||||
} else {
|
||||
await client.query(
|
||||
`INSERT INTO invites (code, organization_id, email, employee_data, status, created_at, expires_at)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7)`,
|
||||
[
|
||||
code, orgId, email, JSON.stringify(employeeData), 'pending',
|
||||
currentTime, currentTime + (7 * 24 * 60 * 60 * 1000) // 7 days
|
||||
]
|
||||
);
|
||||
|
||||
// Create employee record
|
||||
await client.query(
|
||||
`INSERT INTO employees (id, organization_id, name, email, role, department, status, invite_code, created_at)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)`,
|
||||
[employeeId, orgId, name.trim(), email.trim(), role || 'employee', department || 'General', 'invited', code, currentTime]
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// Generate invite links
|
||||
const baseUrl = process.env.CLIENT_URL || 'https://auditly-one.vercel.app';
|
||||
const inviteLink = `${baseUrl}/#/employee-form/${code}`;
|
||||
|
||||
console.log(`📧 Invite link: ${inviteLink}`);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
code,
|
||||
employee: employeeData,
|
||||
inviteLink,
|
||||
message: "Invitation sent successfully",
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Create invitation error:", error);
|
||||
res.status(500).json({ error: "Failed to create invitation" });
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Get Invitation Status - Migrated to PostgreSQL
|
||||
*/
|
||||
exports.getInvitationStatus = onRequest({cors: true}, async (req, res) => {
|
||||
if (req.method === 'OPTIONS') {
|
||||
res.status(204).send('');
|
||||
return;
|
||||
}
|
||||
|
||||
if (req.method !== "GET") {
|
||||
return res.status(405).json({ error: "Method not allowed" });
|
||||
}
|
||||
|
||||
const { code } = req.query;
|
||||
|
||||
if (!code) {
|
||||
return res.status(400).json({ error: "Invitation code is required" });
|
||||
}
|
||||
|
||||
try {
|
||||
const inviteRows = await executeQuery(
|
||||
'SELECT * FROM invites WHERE code = $1',
|
||||
[code]
|
||||
);
|
||||
|
||||
if (inviteRows.length === 0) {
|
||||
return res.status(404).json({ error: "Invitation not found" });
|
||||
}
|
||||
|
||||
const invite = inviteRows[0];
|
||||
|
||||
// Check if expired
|
||||
if (Date.now() > invite.expires_at) {
|
||||
return res.status(400).json({ error: "Invitation has expired" });
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
used: invite.status !== 'pending',
|
||||
employee: invite.employee_data,
|
||||
invite: {
|
||||
...invite,
|
||||
employee: invite.employee_data
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Get invitation status error:", error);
|
||||
res.status(500).json({ error: "Failed to get invitation status" });
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
// Export these functions so they can be used to replace the existing ones
|
||||
};
|
||||
227
functions/migrations/001_create_initial_schema.sql
Normal file
227
functions/migrations/001_create_initial_schema.sql
Normal file
@@ -0,0 +1,227 @@
|
||||
-- Initial migration to create all tables for the Auditly application
|
||||
-- Migration: 001_create_initial_schema.sql
|
||||
|
||||
-- Create users table
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
id VARCHAR(255) PRIMARY KEY,
|
||||
email VARCHAR(255) UNIQUE NOT NULL,
|
||||
display_name VARCHAR(255),
|
||||
email_verified BOOLEAN DEFAULT false,
|
||||
created_at BIGINT NOT NULL DEFAULT EXTRACT(epoch FROM NOW()) * 1000,
|
||||
last_login_at BIGINT,
|
||||
updated_at BIGINT DEFAULT EXTRACT(epoch FROM NOW()) * 1000
|
||||
);
|
||||
|
||||
-- Create organizations table
|
||||
CREATE TABLE IF NOT EXISTS organizations (
|
||||
id VARCHAR(255) PRIMARY KEY,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
owner_id VARCHAR(255) NOT NULL REFERENCES users(id),
|
||||
industry VARCHAR(255),
|
||||
size VARCHAR(255),
|
||||
description TEXT,
|
||||
mission TEXT,
|
||||
vision TEXT,
|
||||
values TEXT,
|
||||
founding_year VARCHAR(255),
|
||||
evolution TEXT,
|
||||
major_milestones TEXT,
|
||||
advantages TEXT,
|
||||
vulnerabilities TEXT,
|
||||
competitors TEXT,
|
||||
market_position TEXT,
|
||||
current_challenges TEXT,
|
||||
short_term_goals TEXT,
|
||||
long_term_goals TEXT,
|
||||
key_metrics TEXT,
|
||||
culture_description TEXT,
|
||||
work_environment TEXT,
|
||||
leadership_style TEXT,
|
||||
communication_style TEXT,
|
||||
additional_context TEXT,
|
||||
onboarding_completed BOOLEAN DEFAULT false,
|
||||
onboarding_data JSONB,
|
||||
created_at BIGINT NOT NULL DEFAULT EXTRACT(epoch FROM NOW()) * 1000,
|
||||
updated_at BIGINT DEFAULT EXTRACT(epoch FROM NOW()) * 1000,
|
||||
|
||||
-- Subscription fields
|
||||
subscription_status VARCHAR(50) DEFAULT 'trial',
|
||||
stripe_customer_id VARCHAR(255),
|
||||
stripe_subscription_id VARCHAR(255),
|
||||
current_period_start BIGINT,
|
||||
current_period_end BIGINT,
|
||||
trial_end BIGINT,
|
||||
|
||||
-- Usage tracking
|
||||
employee_count INTEGER DEFAULT 0,
|
||||
reports_generated INTEGER DEFAULT 0,
|
||||
last_report_generation BIGINT,
|
||||
|
||||
-- Settings
|
||||
allowed_employee_count INTEGER DEFAULT 50,
|
||||
features_enabled JSONB DEFAULT '{
|
||||
"aiReports": true,
|
||||
"chat": true,
|
||||
"analytics": true
|
||||
}'::jsonb
|
||||
);
|
||||
|
||||
-- Create user_organizations junction table for multi-org support
|
||||
CREATE TABLE IF NOT EXISTS user_organizations (
|
||||
user_id VARCHAR(255) REFERENCES users(id) ON DELETE CASCADE,
|
||||
organization_id VARCHAR(255) REFERENCES organizations(id) ON DELETE CASCADE,
|
||||
role VARCHAR(50) NOT NULL DEFAULT 'employee', -- owner, admin, employee
|
||||
joined_at BIGINT NOT NULL DEFAULT EXTRACT(epoch FROM NOW()) * 1000,
|
||||
onboarding_completed BOOLEAN DEFAULT false,
|
||||
PRIMARY KEY (user_id, organization_id)
|
||||
);
|
||||
|
||||
-- Create employees table
|
||||
CREATE TABLE IF NOT EXISTS employees (
|
||||
id VARCHAR(255) PRIMARY KEY,
|
||||
organization_id VARCHAR(255) NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
|
||||
user_id VARCHAR(255) REFERENCES users(id) ON DELETE SET NULL, -- nullable for invite-only employees
|
||||
name VARCHAR(255) NOT NULL,
|
||||
email VARCHAR(255) NOT NULL,
|
||||
role VARCHAR(255) DEFAULT 'employee',
|
||||
department VARCHAR(255) DEFAULT 'General',
|
||||
status VARCHAR(50) DEFAULT 'invited', -- invited, active, inactive
|
||||
invite_code VARCHAR(255),
|
||||
joined_at BIGINT DEFAULT EXTRACT(epoch FROM NOW()) * 1000,
|
||||
created_at BIGINT NOT NULL DEFAULT EXTRACT(epoch FROM NOW()) * 1000,
|
||||
updated_at BIGINT DEFAULT EXTRACT(epoch FROM NOW()) * 1000,
|
||||
|
||||
UNIQUE(organization_id, email)
|
||||
);
|
||||
|
||||
-- Create auth_tokens table
|
||||
CREATE TABLE IF NOT EXISTS auth_tokens (
|
||||
token VARCHAR(255) PRIMARY KEY,
|
||||
user_id VARCHAR(255) NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
created_at BIGINT NOT NULL DEFAULT EXTRACT(epoch FROM NOW()) * 1000,
|
||||
expires_at BIGINT NOT NULL,
|
||||
last_used_at BIGINT DEFAULT EXTRACT(epoch FROM NOW()) * 1000,
|
||||
is_active BOOLEAN DEFAULT true
|
||||
);
|
||||
|
||||
-- Create otps table for email verification
|
||||
CREATE TABLE IF NOT EXISTS otps (
|
||||
email VARCHAR(255) PRIMARY KEY,
|
||||
otp VARCHAR(6) NOT NULL,
|
||||
expires_at BIGINT NOT NULL,
|
||||
attempts INTEGER DEFAULT 0,
|
||||
invite_code VARCHAR(255),
|
||||
created_at BIGINT NOT NULL DEFAULT EXTRACT(epoch FROM NOW()) * 1000
|
||||
);
|
||||
|
||||
-- Create invites table
|
||||
CREATE TABLE IF NOT EXISTS invites (
|
||||
code VARCHAR(255) PRIMARY KEY,
|
||||
organization_id VARCHAR(255) NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
|
||||
email VARCHAR(255) NOT NULL,
|
||||
employee_data JSONB NOT NULL, -- Contains employee info like name, role, department
|
||||
status VARCHAR(50) DEFAULT 'pending', -- pending, consumed, expired
|
||||
created_at BIGINT NOT NULL DEFAULT EXTRACT(epoch FROM NOW()) * 1000,
|
||||
expires_at BIGINT NOT NULL,
|
||||
consumed_at BIGINT,
|
||||
consumed_by VARCHAR(255)
|
||||
);
|
||||
|
||||
-- Create submissions table
|
||||
CREATE TABLE IF NOT EXISTS submissions (
|
||||
id SERIAL PRIMARY KEY,
|
||||
employee_id VARCHAR(255) NOT NULL REFERENCES employees(id) ON DELETE CASCADE,
|
||||
organization_id VARCHAR(255) NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
|
||||
answers JSONB NOT NULL,
|
||||
status VARCHAR(50) DEFAULT 'completed',
|
||||
submission_type VARCHAR(50) DEFAULT 'regular', -- regular, invite
|
||||
invite_code VARCHAR(255),
|
||||
submitted_at BIGINT NOT NULL DEFAULT EXTRACT(epoch FROM NOW()) * 1000,
|
||||
created_at BIGINT NOT NULL DEFAULT EXTRACT(epoch FROM NOW()) * 1000,
|
||||
|
||||
UNIQUE(employee_id) -- One submission per employee
|
||||
);
|
||||
|
||||
-- Create employee_reports table
|
||||
CREATE TABLE IF NOT EXISTS employee_reports (
|
||||
id SERIAL PRIMARY KEY,
|
||||
employee_id VARCHAR(255) NOT NULL REFERENCES employees(id) ON DELETE CASCADE,
|
||||
organization_id VARCHAR(255) NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
|
||||
employee_name VARCHAR(255) NOT NULL,
|
||||
role VARCHAR(255),
|
||||
email VARCHAR(255),
|
||||
summary TEXT,
|
||||
submission_id INTEGER REFERENCES submissions(id),
|
||||
company_context JSONB,
|
||||
|
||||
-- Report sections
|
||||
role_and_output JSONB,
|
||||
insights JSONB,
|
||||
strengths TEXT[],
|
||||
weaknesses TEXT[],
|
||||
opportunities JSONB[],
|
||||
risks TEXT[],
|
||||
recommendations TEXT[],
|
||||
grading_overview JSONB,
|
||||
|
||||
generated_at BIGINT NOT NULL DEFAULT EXTRACT(epoch FROM NOW()) * 1000,
|
||||
created_at BIGINT NOT NULL DEFAULT EXTRACT(epoch FROM NOW()) * 1000,
|
||||
updated_at BIGINT DEFAULT EXTRACT(epoch FROM NOW()) * 1000,
|
||||
|
||||
UNIQUE(employee_id) -- One report per employee
|
||||
);
|
||||
|
||||
-- Create company_reports table
|
||||
CREATE TABLE IF NOT EXISTS company_reports (
|
||||
id SERIAL PRIMARY KEY,
|
||||
organization_id VARCHAR(255) NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
|
||||
report_data JSONB NOT NULL, -- Contains the full company report structure
|
||||
executive_summary TEXT,
|
||||
generated_at BIGINT NOT NULL DEFAULT EXTRACT(epoch FROM NOW()) * 1000,
|
||||
created_at BIGINT NOT NULL DEFAULT EXTRACT(epoch FROM NOW()) * 1000,
|
||||
updated_at BIGINT DEFAULT EXTRACT(epoch FROM NOW()) * 1000
|
||||
);
|
||||
|
||||
-- Create indexes for better performance
|
||||
CREATE INDEX IF NOT EXISTS idx_users_email ON users(email);
|
||||
CREATE INDEX IF NOT EXISTS idx_organizations_owner ON organizations(owner_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_user_organizations_user ON user_organizations(user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_user_organizations_org ON user_organizations(organization_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_employees_org ON employees(organization_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_employees_user ON employees(user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_employees_email ON employees(organization_id, email);
|
||||
CREATE INDEX IF NOT EXISTS idx_auth_tokens_user ON auth_tokens(user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_auth_tokens_expires ON auth_tokens(expires_at);
|
||||
CREATE INDEX IF NOT EXISTS idx_invites_org ON invites(organization_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_invites_email ON invites(email);
|
||||
CREATE INDEX IF NOT EXISTS idx_invites_status ON invites(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_submissions_employee ON submissions(employee_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_submissions_org ON submissions(organization_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_employee_reports_employee ON employee_reports(employee_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_employee_reports_org ON employee_reports(organization_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_company_reports_org ON company_reports(organization_id);
|
||||
|
||||
-- Create trigger function to update updated_at timestamp
|
||||
CREATE OR REPLACE FUNCTION update_updated_at_column()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.updated_at = EXTRACT(epoch FROM NOW()) * 1000;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ language 'plpgsql';
|
||||
|
||||
-- Create triggers to automatically update updated_at
|
||||
CREATE TRIGGER update_users_updated_at BEFORE UPDATE ON users
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
CREATE TRIGGER update_organizations_updated_at BEFORE UPDATE ON organizations
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
CREATE TRIGGER update_employees_updated_at BEFORE UPDATE ON employees
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
CREATE TRIGGER update_employee_reports_updated_at BEFORE UPDATE ON employee_reports
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
CREATE TRIGGER update_company_reports_updated_at BEFORE UPDATE ON company_reports
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
106
functions/migrations/002_fix_company_report.sql
Normal file
106
functions/migrations/002_fix_company_report.sql
Normal file
@@ -0,0 +1,106 @@
|
||||
DROP TABLE IF EXISTS company_reports; -- it's empty
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
DO $a$
|
||||
BEGIN
|
||||
CREATE TYPE department_breakdown_item AS (
|
||||
"department" VARCHAR,
|
||||
"count" int
|
||||
);
|
||||
CREATE TYPE company_report_overview AS (
|
||||
"total_employees" int,
|
||||
"department_breakdown" department_breakdown_item[],
|
||||
"submissionRate" double precision,
|
||||
"last_updated" TIMESTAMP,
|
||||
"average_performance_score" double precision,
|
||||
"risk_level" VARCHAR
|
||||
);
|
||||
CREATE TYPE weaknesses AS (
|
||||
"title" VARCHAR,
|
||||
"description" TEXT
|
||||
);
|
||||
CREATE TYPE new_hire AS (
|
||||
"name" VARCHAR,
|
||||
"department" VARCHAR,
|
||||
"role" VARCHAR,
|
||||
"impact" TEXT
|
||||
);
|
||||
CREATE TYPE promotion AS (
|
||||
"name" VARCHAR,
|
||||
"from_role" VARCHAR,
|
||||
"to_role" VARCHAR,
|
||||
"impact" TEXT
|
||||
);
|
||||
CREATE TYPE departure AS (
|
||||
"name" VARCHAR,
|
||||
"department" VARCHAR,
|
||||
"reason" TEXT,
|
||||
"impact" TEXT
|
||||
|
||||
);
|
||||
CREATE TYPE personnel_changes AS (
|
||||
"new_hires" new_hire[],
|
||||
"promotions" promotion[],
|
||||
"departures" departure[]
|
||||
);
|
||||
CREATE TYPE immediate_hiring_needs AS (
|
||||
"department" VARCHAR,
|
||||
"role" VARCHAR,
|
||||
"priority" VARCHAR,
|
||||
"reasoning" TEXT
|
||||
);
|
||||
CREATE TYPE forward_operating_plan AS (
|
||||
"title" VARCHAR,
|
||||
"details" TEXT
|
||||
);
|
||||
CREATE TYPE organizational_impact_summary_employee AS (
|
||||
"employee_name" VARCHAR,
|
||||
"impact" TEXT,
|
||||
"description" TEXT,
|
||||
"suggested_pay" double precision
|
||||
);
|
||||
CREATE TYPE organizational_impact_summary AS (
|
||||
"category" VARCHAR,
|
||||
"employees" organizational_impact_summary_employee[]
|
||||
);
|
||||
CREATE TYPE team_score AS (
|
||||
"employee_name" VARCHAR,
|
||||
"grade" VARCHAR,
|
||||
"reliability" double precision,
|
||||
"role_fit" double precision,
|
||||
"scalability" double precision,
|
||||
"output" double precision,
|
||||
"initiative" double precision
|
||||
);
|
||||
CREATE TYPE grading_breakdown AS (
|
||||
"department_name_short" VARCHAR,
|
||||
"department_name" VARCHAR,
|
||||
"lead" VARCHAR,
|
||||
"support" VARCHAR,
|
||||
"department_grade" VARCHAR,
|
||||
"executive_summary" TEXT,
|
||||
"team_scores" team_score[]
|
||||
);
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN RAISE NOTICE 'Type department_breakdown_item or company_report_overview already exists, skipping creation.';
|
||||
END
|
||||
$a$ LANGUAGE plpgsql;
|
||||
|
||||
END
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS company_reports (
|
||||
"id" SERIAL PRIMARY KEY,
|
||||
"organization_id" VARCHAR NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
|
||||
"prompt_used" TEXT,
|
||||
"created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"overview" company_report_overview,
|
||||
"strengths" TEXT[],
|
||||
"weaknesses" weaknesses[],
|
||||
"personnel_changes" personnel_changes,
|
||||
"immediate_hiring_needs" immediate_hiring_needs[],
|
||||
"forward_operating_plan" forward_operating_plan[],
|
||||
"organizational_impact_summary" organizational_impact_summary[],
|
||||
"grading_breakdown" grading_breakdown[]
|
||||
);
|
||||
131
functions/migrations/migrate.js
Normal file
131
functions/migrations/migrate.js
Normal file
@@ -0,0 +1,131 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { executeQuery, executeTransaction } = require('../database');
|
||||
|
||||
/**
|
||||
* Run all database migrations
|
||||
*/
|
||||
async function runMigrations() {
|
||||
console.log('🚀 Starting database migrations...');
|
||||
|
||||
try {
|
||||
// Create migrations table to track what's been run
|
||||
await executeQuery(`
|
||||
CREATE TABLE IF NOT EXISTS migrations (
|
||||
id SERIAL PRIMARY KEY,
|
||||
filename VARCHAR(255) NOT NULL UNIQUE,
|
||||
executed_at BIGINT NOT NULL DEFAULT EXTRACT(epoch FROM NOW()) * 1000
|
||||
);
|
||||
`);
|
||||
|
||||
// Get list of executed migrations
|
||||
const executedMigrations = await executeQuery(
|
||||
'SELECT filename FROM migrations ORDER BY executed_at'
|
||||
);
|
||||
const executedFiles = new Set(executedMigrations.map(m => m.filename));
|
||||
|
||||
// Read migration files
|
||||
const migrationsDir = path.join(__dirname);
|
||||
const migrationFiles = fs.readdirSync(migrationsDir)
|
||||
.filter(file => file.endsWith('.sql'))
|
||||
.sort();
|
||||
|
||||
console.log(`📁 Found ${migrationFiles.length} migration files`);
|
||||
|
||||
for (const file of migrationFiles) {
|
||||
if (executedFiles.has(file)) {
|
||||
console.log(`⏭️ Skipping ${file} (already executed)`);
|
||||
continue;
|
||||
}
|
||||
|
||||
console.log(`🔄 Executing ${file}...`);
|
||||
|
||||
const filePath = path.join(migrationsDir, file);
|
||||
const sql = fs.readFileSync(filePath, 'utf8');
|
||||
|
||||
await executeTransaction(async (client) => {
|
||||
// Execute the migration SQL
|
||||
if (process.env.USE_NEON_SERVERLESS === 'true') {
|
||||
// For Neon serverless, split by statements and execute individually
|
||||
const statements = sql
|
||||
.split(';')
|
||||
.map(s => s.trim())
|
||||
.filter(s => s.length > 0);
|
||||
|
||||
for (const statement of statements) {
|
||||
if (statement.trim()) {
|
||||
await client(statement);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
await client.query(sql);
|
||||
}
|
||||
|
||||
// Record that this migration was executed
|
||||
if (process.env.USE_NEON_SERVERLESS === 'true') {
|
||||
await client('INSERT INTO migrations (filename) VALUES ($1)', [file]);
|
||||
} else {
|
||||
await client.query('INSERT INTO migrations (filename) VALUES ($1)', [file]);
|
||||
}
|
||||
});
|
||||
|
||||
console.log(`✅ Completed ${file}`);
|
||||
}
|
||||
|
||||
console.log('🎉 All migrations completed successfully!');
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Migration failed:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new migration file
|
||||
* @param {string} name - Migration name
|
||||
*/
|
||||
function createMigration(name) {
|
||||
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').split('T')[0];
|
||||
const filename = `${timestamp}_${name}.sql`;
|
||||
const filepath = path.join(__dirname, filename);
|
||||
|
||||
const template = `-- Migration: ${filename}
|
||||
-- Description: ${name}
|
||||
|
||||
-- Add your SQL here
|
||||
|
||||
`;
|
||||
|
||||
fs.writeFileSync(filepath, template);
|
||||
console.log(`📝 Created migration: ${filename}`);
|
||||
return filename;
|
||||
}
|
||||
|
||||
// Run migrations if this file is executed directly
|
||||
if (require.main === module) {
|
||||
const command = process.argv[2];
|
||||
|
||||
if (command === 'create') {
|
||||
const name = process.argv[3];
|
||||
if (!name) {
|
||||
console.error('❌ Please provide a migration name: npm run db:migrate create migration_name');
|
||||
process.exit(1);
|
||||
}
|
||||
createMigration(name);
|
||||
} else {
|
||||
runMigrations()
|
||||
.then(() => {
|
||||
console.log('🏁 Migration process completed');
|
||||
process.exit(0);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('💥 Migration process failed:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
runMigrations,
|
||||
createMigration
|
||||
};
|
||||
@@ -1,25 +1,29 @@
|
||||
{
|
||||
"name": "functions",
|
||||
"description": "Cloud Functions for Firebase",
|
||||
"scripts": {
|
||||
"serve": "firebase emulators:start --only functions",
|
||||
"shell": "firebase functions:shell",
|
||||
"start": "npm run shell",
|
||||
"deploy": "firebase deploy --only functions",
|
||||
"logs": "firebase functions:log"
|
||||
},
|
||||
"engines": {
|
||||
"node": "22"
|
||||
},
|
||||
"main": "index.js",
|
||||
"dependencies": {
|
||||
"@google-cloud/vertexai": "^1.10.0",
|
||||
"firebase-admin": "^12.7.0",
|
||||
"firebase-functions": "^6.4.0",
|
||||
"stripe": "^18.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"firebase-functions-test": "^3.4.1"
|
||||
},
|
||||
"private": true
|
||||
}
|
||||
"name": "functions",
|
||||
"description": "NeonDB Backend Functions",
|
||||
"private": true,
|
||||
"main": "index.js",
|
||||
"engines": {
|
||||
"node": "22"
|
||||
},
|
||||
"scripts": {
|
||||
"deploy": "firebase deploy --only functions",
|
||||
"logs": "firebase functions:log",
|
||||
"serve": "firebase emulators:start --only functions",
|
||||
"shell": "firebase functions:shell",
|
||||
"start": "npm run shell",
|
||||
"db:migrate": "node migrations/migrate.js",
|
||||
"db:seed": "node migrations/seed.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@google-cloud/vertexai": "^1.10.0",
|
||||
"@neondatabase/serverless": "^0.9.5",
|
||||
"pg": "^8.12.0",
|
||||
"stripe": "^18.5.0",
|
||||
"firebase-functions": "^6.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/pg": "^8.11.6",
|
||||
"firebase-functions-test": "^3.4.1"
|
||||
}
|
||||
}
|
||||
|
||||
333
index.css
333
index.css
@@ -17,83 +17,76 @@
|
||||
@keyframes blinkLightGreen {
|
||||
|
||||
0% {
|
||||
box-shadow: inset 1px 0px 3px -1px rgba(255, 255, 255, 0.2),
|
||||
inset -1px 0px 3px 1px rgba(0, 0, 0, 0.15),
|
||||
0px 0px 2px 1px #32ff6f67;
|
||||
box-shadow: 0px 0px 3px 0.5px #32ff6f67;
|
||||
}
|
||||
|
||||
50% {
|
||||
box-shadow: inset -1px 0px 3px 1px rgba(255, 255, 255, 0.2),
|
||||
inset 1px 0px 3px -1px rgba(0, 0, 0, 0.15);
|
||||
25%,
|
||||
75% {
|
||||
box-shadow: revert;
|
||||
}
|
||||
|
||||
100% {
|
||||
box-shadow: inset 1px 0px 3px -1px rgba(255, 255, 255, 0.2),
|
||||
inset -1px 0px 3px 1px rgba(0, 0, 0, 0.15),
|
||||
0px 0px 2px 1px #32ff6f67;
|
||||
box-shadow: 0px 0px 3px 0.5px #32ff6f67;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes blinkLightYellow {
|
||||
|
||||
0% {
|
||||
box-shadow: inset 1px 0px 3px 0px rgba(255, 255, 255, 0.20),
|
||||
inset -1px 0px 3px 0px rgba(78, 78, 78, 0.15),
|
||||
0px 0px 2px 1px #ffef3c6c;
|
||||
box-shadow: 0px 0px 3px 0.5px #ffef3c6c;
|
||||
}
|
||||
|
||||
50% {
|
||||
box-shadow: inset 1px 0px 3px 0px rgba(255, 255, 255, 0.20),
|
||||
inset -1px 0px 3px 0px rgba(78, 78, 78, 0.15);
|
||||
25%,
|
||||
75% {
|
||||
box-shadow: revert;
|
||||
}
|
||||
|
||||
100% {
|
||||
box-shadow: inset 1px 0px 3px 0px rgba(255, 255, 255, 0.20),
|
||||
inset -1px 0px 3px 0px rgba(78, 78, 78, 0.15),
|
||||
0px 0px 2px 1px #ffef3c6c;
|
||||
box-shadow: 0px 0px 3px 0.5px #ffef3c6c;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes blinkLightBlue {
|
||||
|
||||
0% {
|
||||
box-shadow: inset 1px 0px 3px 0px rgba(255, 255, 255, 0.20),
|
||||
inset -1px 0px 3px 0px rgba(78, 78, 78, 0.15),
|
||||
0px 0px 2px 1px #39a2f362;
|
||||
box-shadow: 0px 0px 3px 0.5px #39a2f362;
|
||||
}
|
||||
|
||||
50% {
|
||||
box-shadow: inset 1px 0px 3px 0px rgba(255, 255, 255, 0.20),
|
||||
inset -1px 0px 3px 0px rgba(78, 78, 78, 0.15);
|
||||
25%,
|
||||
75% {
|
||||
box-shadow: revert;
|
||||
}
|
||||
|
||||
100% {
|
||||
box-shadow: inset 1px 0px 3px 0px rgba(255, 255, 255, 0.20),
|
||||
inset -1px 0px 3px 0px rgba(78, 78, 78, 0.15),
|
||||
0px 0px 2px 1px #39a2f362;
|
||||
box-shadow: 0px 0px 3px 0.5px #39a2f362;
|
||||
}
|
||||
}
|
||||
|
||||
div:has(>button.bg-\[--Brand-Orange\]:disabled) {
|
||||
background: transparent !important;
|
||||
;
|
||||
}
|
||||
|
||||
@keyframes blinkLightRed {
|
||||
|
||||
0% {
|
||||
box-shadow: inset 1px 0px 3px 0px rgba(255, 255, 255, 0.20),
|
||||
inset -1px 0px 3px 0px rgba(78, 78, 78, 0.15),
|
||||
0px 0px 2px 1px #ff2d5e63;
|
||||
box-shadow: 0px 0px 3px 0.5px #ff2d5e63;
|
||||
}
|
||||
|
||||
50% {
|
||||
box-shadow: inset 1px 0px 3px 0px rgba(255, 255, 255, 0.20),
|
||||
inset -1px 0px 3px 0px rgba(78, 78, 78, 0.15);
|
||||
25%,
|
||||
75% {
|
||||
box-shadow: revert;
|
||||
}
|
||||
|
||||
100% {
|
||||
box-shadow: inset 1px 0px 3px 0px rgba(255, 255, 255, 0.20),
|
||||
inset -1px 0px 3px 0px rgba(78, 78, 78, 0.15),
|
||||
0px 0px 2px 1px #ff2d5e63;
|
||||
box-shadow: 0px 0px 3px 0.5px #ff2d5e63;
|
||||
}
|
||||
}
|
||||
|
||||
div.bg-gradient-to-b {
|
||||
background: var(--button-border-primary);
|
||||
}
|
||||
|
||||
.blinkLightBlue,
|
||||
.blinkLightGreen,
|
||||
.blinkLightRed,
|
||||
@@ -101,39 +94,30 @@
|
||||
animation-duration : 5s;
|
||||
animation-timing-function: ease-in-out;
|
||||
animation-iteration-count: infinite;
|
||||
border : solid 1px transparent;
|
||||
}
|
||||
|
||||
.blinkLightBlue {
|
||||
animation-name: blinkLightBlue;
|
||||
box-shadow : inset 1px 0px 3px 0px rgba(255, 255, 255, 0.20),
|
||||
inset -1px 0px 3px 0px rgba(78, 78, 78, 0.15),
|
||||
0px 0px 2px 1px #39a2f32d;
|
||||
border: solid 1px #39a2f362;
|
||||
box-shadow : 0px 0px 3px 0.5px #39a2f32d;
|
||||
border : solid 1px #39a2f362;
|
||||
}
|
||||
|
||||
.blinkLightGreen {
|
||||
animation-name: blinkLightGreen;
|
||||
box-shadow : inset 1px 0px 3px 0px rgba(255, 255, 255, 0.20),
|
||||
inset -1px 0px 3px 0px rgba(78, 78, 78, 0.15),
|
||||
0px 0px 2px 1px #a5ffc075;
|
||||
border: solid 1px rgba(187, 248, 185, 0.31);
|
||||
box-shadow : 0px 0px 3px 0.5px #a5ffc075;
|
||||
border : solid 1px rgba(187, 248, 185, 0.31);
|
||||
}
|
||||
|
||||
.blinkLightRed {
|
||||
animation-name: blinkLightRed;
|
||||
box-shadow : inset 1px 0px 3px 0px rgba(255, 255, 255, 0.20),
|
||||
inset -1px 0px 3px 0px rgba(78, 78, 78, 0.15),
|
||||
0px 0px 2px 1px #f63d6875;
|
||||
border: solid 1px #e4547656;
|
||||
box-shadow : 0px 0px 3px 0.5px #f63d6875;
|
||||
border : solid 1px #e4547656;
|
||||
}
|
||||
|
||||
.blinkLightYellow {
|
||||
animation-name: blinkLightYellow;
|
||||
box-shadow : inset 1px 0px 3px 0px rgba(255, 255, 255, 0.20),
|
||||
inset -1px 0px 3px 0px rgba(78, 78, 78, 0.15),
|
||||
0px 0px 2px 1px #f7f3c275;
|
||||
border: solid 1px #e4e25456;
|
||||
box-shadow : 0px 0px 3px 0.5px #f7f3c275;
|
||||
border : solid 1px #e4e25456;
|
||||
}
|
||||
|
||||
.blinkLightActive {
|
||||
@@ -141,50 +125,41 @@
|
||||
}
|
||||
|
||||
.blinkLightEmpty {
|
||||
box-shadow: inset 1px 0px 3px 0px rgba(255, 255, 255, 0.20),
|
||||
inset -1px 0px 3px 0px rgba(78, 78, 78, 0.15);
|
||||
box-shadow: inset 1px 0px 3px 0px rgba(255, 255, 255, 0.1),
|
||||
inset -1px 0px 3px 0px rgba(78, 78, 78, 0.1);
|
||||
animation : none !important;
|
||||
border : none !important;
|
||||
background-color: var(--Neutrals-NeutralSlate400)
|
||||
}
|
||||
|
||||
div:has(>input[type="text"]:focus:not(.focus\:ring-2)):not(:has(>label)),
|
||||
div:has(>input[type="email"]:focus):not(:has(>label)),
|
||||
div:has(>input[type="password"]:focus):not(:has(>label)),
|
||||
div:has(>textarea:focus) {
|
||||
animation : blinkLightBlue 2s ease infinite;
|
||||
box-shadow : 0px 0px 3px 0.5px #39a2f32d;
|
||||
border : 1px solid;
|
||||
mask-border-outset: 1px;
|
||||
margin : -1px;
|
||||
outline : none;
|
||||
outline-offset : none;
|
||||
}
|
||||
|
||||
div:has(>input[type="text"]:focus:not(.focus\:ring-2)):not(:has(>label))>input[type="text"]:focus,
|
||||
div:has(>input[type="email"]:focus):not(:has(>label))>input[type="email"]:focus,
|
||||
div:has(>input[type="password"]:focus):not(:has(>label))>input[type="password"]:focus {
|
||||
outline: none;
|
||||
border : none;
|
||||
}
|
||||
|
||||
.Outline {
|
||||
background: linear-gradient(180deg, #66B2FF 0%, #0073E5 100%);
|
||||
}
|
||||
|
||||
:root {
|
||||
--Brand-Orange: #3399FF;
|
||||
|
||||
|
||||
--background-primary : #FFFFFF;
|
||||
--background-secondary: #FDFDFD;
|
||||
--background-tertiary : #FAFAFA;
|
||||
|
||||
--text-primary : #0A0D12;
|
||||
--text-secondary: #717680;
|
||||
--text-tertiary : #A4A7AE;
|
||||
|
||||
--accent : #5E48FC;
|
||||
--accent-hover: #4C3CF0;
|
||||
--accent-text : #FFFFFF;
|
||||
|
||||
--border-color: #E9EAEB;
|
||||
--border-light: #F5F5F5;
|
||||
|
||||
--sidebar-bg : #FDFDFD;
|
||||
--sidebar-text : #717680;
|
||||
--sidebar-active-bg : #5E48FC;
|
||||
--sidebar-active-text: #FFFFFF;
|
||||
|
||||
--input-bg : #F5F5F5;
|
||||
--input-border : #E9EAEB;
|
||||
--input-placeholder: #717680;
|
||||
|
||||
--button-secondary-bg : #F5F5F5;
|
||||
--button-secondary-hover: #E9EAEB;
|
||||
|
||||
--color-red : #FB3748;
|
||||
--color-green : #1FC16B;
|
||||
--color-orange : #5E48FC;
|
||||
--color-light-orange: #F38744;
|
||||
--color-yellow : #FEEE95;
|
||||
|
||||
--Neutrals-NeutralState950: #0A0D12;
|
||||
--Neutrals-NeutralSlate950: #0A0D12;
|
||||
--Neutrals-NeutralSlate900: #181D27;
|
||||
--Neutrals-NeutralSlate800: #252B37;
|
||||
--Neutrals-NeutralSlate700: #414651;
|
||||
@@ -197,24 +172,76 @@
|
||||
--Neutrals-NeutralSlate50 : #FAFAFA;
|
||||
--Neutrals-NeutralSlate0 : #FDFDFD;
|
||||
|
||||
--neutral-100: #A4A7AE;
|
||||
--neutral-200: #D5D7DA;
|
||||
--neutral-300: #E9EAEB;
|
||||
--neutral-400: #F5F5F5;
|
||||
--neutral-500: #FAFAFA;
|
||||
--neutral-600: #FDFDFD;
|
||||
--neutral-700: #FEFEFE;
|
||||
--background-primary : var(--Brand-Orange);
|
||||
--background-secondary: var(--Neutrals-NeutralSlate0);
|
||||
--background-tertiary : var(--Neutrals-NeutralSlate50);
|
||||
|
||||
--Brand-Orange: #3399FF;
|
||||
--text-primary : var(--Neutrals-NeutralSlate950);
|
||||
--text-secondary : #717680;
|
||||
--text-tertiary : var(--Neutrals-NeutralSlate400);
|
||||
--Light-Grays-l-gray08: #FFFFFF;
|
||||
|
||||
--accent : #5E48FC;
|
||||
--accent-hover: #4C3CF0;
|
||||
--accent-text : #FFFFFF;
|
||||
|
||||
--border-color: var(--Neutrals-NeutralSlate200);
|
||||
--border-light: var(--Neutrals-NeutralSlate100);
|
||||
|
||||
--Text-White-00: var(--Neutrals-NeutralSlate200);
|
||||
|
||||
--Text-Gray-0 : var(--Neutrals-NeutralSlate200);
|
||||
--Text-Gray-50 : var(--Neutrals-NeutralSlate50);
|
||||
--Text-Gray-100: var(--Neutrals-NeutralSlate100);
|
||||
--Text-Gray-200: var(--Neutrals-NeutralSlate200);
|
||||
--Text-Gray-300: var(--Neutrals-NeutralSlate300);
|
||||
--Text-Gray-400: var(--Neutrals-NeutralSlate400);
|
||||
--Text-Gray-500: var(--Neutrals-NeutralSlate500);
|
||||
--Text-Gray-600: var(--Neutrals-NeutralSlate600);
|
||||
--Text-Gray-700: var(--Neutrals-NeutralSlate700);
|
||||
--Text-Gray-800: var(--Neutrals-NeutralSlate800);
|
||||
--Text-Gray-900: var(--Neutrals-NeutralSlate900);
|
||||
--Text-Gray-950: var(--Neutrals-NeutralSlate950);
|
||||
--Text-Dark-950: var(--Neutrals-NeutralSlate950);
|
||||
|
||||
--Outline-Outline-Gray-200: var(--Neutrals-NeutralSlate200);
|
||||
--Button-Secondary : var(--Neutrals-NeutralSlate100);
|
||||
--Main-BG-Gray-100 : var(--Neutrals-NeutralSlate100);
|
||||
--red-200 : #FFC0C5;
|
||||
|
||||
--sidebar-bg : var(--Neutrals-NeutralSlate200);
|
||||
--sidebar-text : #717680;
|
||||
--sidebar-active-bg : #5E48FC;
|
||||
--sidebar-active-text: #FFFFFF;
|
||||
|
||||
--input-bg : var(--Neutrals-NeutralSlate100);
|
||||
--input-border : var(--Neutrals-NeutralSlate200);
|
||||
--input-placeholder: #717680;
|
||||
|
||||
--button-secondary-bg : var(--Neutrals-NeutralSlate100);
|
||||
--button-secondary-hover: var(--Neutrals-NeutralSlate200);
|
||||
--button-bg-primary : var(--Brand-Orange);
|
||||
--button-border-primary : linear-gradient(180deg, #66B2FF 0%, #0073E5 100%);
|
||||
|
||||
--color-red : #FB3748;
|
||||
--color-green : #1FC16B;
|
||||
--color-orange : #5E48FC;
|
||||
--color-light-orange: #F38744;
|
||||
--color-yellow : #FEEE95;
|
||||
|
||||
--Other-Red : #FB3748;
|
||||
--Other-Green: #1FC16B;
|
||||
--Other-White: #FFFFFF;
|
||||
|
||||
--button-bg-primary : #5E48FC;
|
||||
--button-border-primary: #7B64FF;
|
||||
|
||||
--white: #FFFFFF;
|
||||
|
||||
div:has(>input[type="text"]:focus):not(:has(>label)),
|
||||
div:has(>input[type="email"]:focus):not(:has(>label)),
|
||||
div:has(>input[type="password"]:focus):not(:has(>label)),
|
||||
div:has(>textarea:focus) {
|
||||
border-color: color-mix(in srgb, var(--Brand-Orange), white 20%);
|
||||
}
|
||||
}
|
||||
|
||||
.dark {
|
||||
@@ -258,23 +285,8 @@
|
||||
--button-secondary-bg : #414651;
|
||||
/* Dark 4 */
|
||||
--button-secondary-hover: #535862;
|
||||
/* Dark 3 */
|
||||
--status-red : #F63D68;
|
||||
/* Other Red */
|
||||
--status-green : #3CCB7F;
|
||||
/* Other Green */
|
||||
--status-orange : #FF4405;
|
||||
/* Other Orange */
|
||||
--status-yellow : #FEEE95;
|
||||
/* Other Yellow */
|
||||
--neutral-200 : #717670;
|
||||
--neutral-300 : #535862;
|
||||
--neutral-400 : #414651;
|
||||
--neutral-500 : #252B37;
|
||||
--neutral-600 : #181D27;
|
||||
--neutral-700 : #0A0D12;
|
||||
|
||||
--Neutrals-NeutralState950: #FDFDFD;
|
||||
--Neutrals-NeutralSlate950: #FDFDFD;
|
||||
--Neutrals-NeutralSlate900: #FAFAFA;
|
||||
--Neutrals-NeutralSlate800: #F5F5F5;
|
||||
--Neutrals-NeutralSlate700: #E9EAEB;
|
||||
@@ -287,6 +299,15 @@
|
||||
--Neutrals-NeutralSlate50 : #181D27;
|
||||
--Neutrals-NeutralSlate0 : #0A0D12;
|
||||
|
||||
--Light-Grays-l-gray08: #0F0F0F;
|
||||
|
||||
div:has(>input[type="text"]:focus):not(:has(>label)),
|
||||
div:has(>input[type="email"]:focus):not(:has(>label)),
|
||||
div:has(>input[type="password"]:focus):not(:has(>label)),
|
||||
div:has(>textarea:focus) {
|
||||
border-color: color-mix(in srgb, var(--Brand-Orange), black 20%);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
* {
|
||||
@@ -380,7 +401,7 @@ input[type="radio"]:focus {
|
||||
/* Markdown Content Styling */
|
||||
.markdown-content {
|
||||
line-height: 1.6;
|
||||
color: var(--Neutrals-NeutralSlate950);
|
||||
color : var(--Neutrals-NeutralSlate950);
|
||||
}
|
||||
|
||||
.markdown-content h1,
|
||||
@@ -389,9 +410,9 @@ input[type="radio"]:focus {
|
||||
.markdown-content h4,
|
||||
.markdown-content h5,
|
||||
.markdown-content h6 {
|
||||
font-weight: 600;
|
||||
line-height: 1.3;
|
||||
margin-top: 1em;
|
||||
font-weight : 600;
|
||||
line-height : 1.3;
|
||||
margin-top : 1em;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
@@ -415,7 +436,7 @@ input[type="radio"]:focus {
|
||||
.markdown-content ul,
|
||||
.markdown-content ol {
|
||||
margin-bottom: 1em;
|
||||
padding-left: 1.5em;
|
||||
padding-left : 1.5em;
|
||||
}
|
||||
|
||||
.markdown-content li {
|
||||
@@ -428,75 +449,75 @@ input[type="radio"]:focus {
|
||||
|
||||
.markdown-content strong {
|
||||
font-weight: 600;
|
||||
color: var(--Neutrals-NeutralSlate950);
|
||||
color : var(--Neutrals-NeutralSlate950);
|
||||
}
|
||||
|
||||
.markdown-content code {
|
||||
font-family: 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Consolas, 'Courier New', monospace;
|
||||
font-size: 0.875em;
|
||||
padding: 0.2em 0.4em;
|
||||
font-family : 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Consolas, 'Courier New', monospace;
|
||||
font-size : 0.875em;
|
||||
padding : 0.2em 0.4em;
|
||||
background-color: var(--Neutrals-NeutralSlate100);
|
||||
border-radius: 4px;
|
||||
color: var(--Neutrals-NeutralSlate800);
|
||||
border-radius : 4px;
|
||||
color : var(--Neutrals-NeutralSlate800);
|
||||
}
|
||||
|
||||
.markdown-content pre {
|
||||
font-family: 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Consolas, 'Courier New', monospace;
|
||||
font-size: 0.875em;
|
||||
line-height: 1.5;
|
||||
font-family : 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Consolas, 'Courier New', monospace;
|
||||
font-size : 0.875em;
|
||||
line-height : 1.5;
|
||||
background-color: var(--Neutrals-NeutralSlate100);
|
||||
border-radius: 8px;
|
||||
padding: 1rem;
|
||||
overflow-x: auto;
|
||||
margin-bottom: 1em;
|
||||
border-radius : 8px;
|
||||
padding : 1rem;
|
||||
overflow-x : auto;
|
||||
margin-bottom : 1em;
|
||||
}
|
||||
|
||||
.markdown-content pre code {
|
||||
background: none;
|
||||
padding: 0;
|
||||
background : none;
|
||||
padding : 0;
|
||||
border-radius: 0;
|
||||
color: inherit;
|
||||
color : inherit;
|
||||
}
|
||||
|
||||
.markdown-content blockquote {
|
||||
border-left: 4px solid var(--Neutrals-NeutralSlate300);
|
||||
border-left : 4px solid var(--Neutrals-NeutralSlate300);
|
||||
padding-left: 1rem;
|
||||
margin: 1em 0;
|
||||
color: var(--Neutrals-NeutralSlate700);
|
||||
font-style: italic;
|
||||
margin : 1em 0;
|
||||
color : var(--Neutrals-NeutralSlate700);
|
||||
font-style : italic;
|
||||
}
|
||||
|
||||
.markdown-content a {
|
||||
color: var(--Brand-Orange);
|
||||
color : var(--Brand-Orange);
|
||||
text-decoration: underline;
|
||||
transition: color 0.2s;
|
||||
transition : color 0.2s;
|
||||
}
|
||||
|
||||
.markdown-content a:hover {
|
||||
color: var(--Brand-Orange);
|
||||
color : var(--Brand-Orange);
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.markdown-content table {
|
||||
width: 100%;
|
||||
width : 100%;
|
||||
border-collapse: collapse;
|
||||
margin-bottom: 1em;
|
||||
border: 1px solid var(--Neutrals-NeutralSlate200);
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
margin-bottom : 1em;
|
||||
border : 1px solid var(--Neutrals-NeutralSlate200);
|
||||
border-radius : 8px;
|
||||
overflow : hidden;
|
||||
}
|
||||
|
||||
.markdown-content th,
|
||||
.markdown-content td {
|
||||
padding: 0.75rem 1rem;
|
||||
text-align: left;
|
||||
padding : 0.75rem 1rem;
|
||||
text-align : left;
|
||||
border-bottom: 1px solid var(--Neutrals-NeutralSlate200);
|
||||
}
|
||||
|
||||
.markdown-content th {
|
||||
background-color: var(--Neutrals-NeutralSlate100);
|
||||
font-weight: 600;
|
||||
color: var(--Neutrals-NeutralSlate950);
|
||||
font-weight : 600;
|
||||
color : var(--Neutrals-NeutralSlate950);
|
||||
}
|
||||
|
||||
.markdown-content tr:last-child td {
|
||||
@@ -504,7 +525,7 @@ input[type="radio"]:focus {
|
||||
}
|
||||
|
||||
.markdown-content hr {
|
||||
border: none;
|
||||
border : none;
|
||||
border-top: 1px solid var(--Neutrals-NeutralSlate200);
|
||||
margin: 1.5em 0;
|
||||
}
|
||||
margin : 1.5em 0;
|
||||
}
|
||||
|
||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"name": "auditly",
|
||||
"name": "orbitly",
|
||||
"version": "0.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "auditly",
|
||||
"name": "orbitly",
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"@google/genai": "^1.13.0",
|
||||
|
||||
11
package.json
11
package.json
@@ -1,13 +1,13 @@
|
||||
{
|
||||
"name": "auditly",
|
||||
"name": "orbitly",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"version": "0.1.1",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "concurrently -k \"bun run dev:firebase\" \"bun run dev:client\"",
|
||||
"dev:client": "vite",
|
||||
"dev:server": "bun --watch server/index.js",
|
||||
"dev:firebase": "firebase emulators:start --only functions,firestore,database",
|
||||
"dev:firebase": "firebase emulators:start --only functions",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"deploy:functions": "firebase deploy --only functions",
|
||||
@@ -19,11 +19,14 @@
|
||||
"@google/genai": "^1.15.0",
|
||||
"@tailwindcss/postcss": "^4.1.12",
|
||||
"@tailwindcss/vite": "^4.1.12",
|
||||
"@types/jspdf": "^2.0.0",
|
||||
"cors": "^2.8.5",
|
||||
"express": "^5.1.0",
|
||||
"firebase": "^12.1.0",
|
||||
"firebase-admin": "^13.4.0",
|
||||
"firebase-functions": "^6.4.0",
|
||||
"html2canvas": "^1.4.1",
|
||||
"jspdf": "^3.0.3",
|
||||
"lucide-react": "^0.539.0",
|
||||
"openai": "^5.15.0",
|
||||
"postcss": "^8.5.6",
|
||||
@@ -50,4 +53,4 @@
|
||||
"typescript": "~5.8.3",
|
||||
"vite": "^7.1.3"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
19
src/App.tsx
19
src/App.tsx
@@ -8,6 +8,7 @@ import { OrgProvider, useOrg } from './contexts/OrgContext';
|
||||
// Lazy load all page components for better performance
|
||||
const Layout = React.lazy(() => import('./components/UiKit').then(module => ({ default: module.Layout })));
|
||||
const CompanyWiki = React.lazy(() => import('./pages/CompanyWiki'));
|
||||
const CompanyWikiNew = React.lazy(() => import('./pages/CompanyWikiNew'));
|
||||
const Reports = React.lazy(() => import('./pages/Reports'));
|
||||
const Submissions = React.lazy(() => import('./pages/Submissions'));
|
||||
const Chat = React.lazy(() => import('./pages/Chat'));
|
||||
@@ -20,7 +21,7 @@ const EmployeeQuestionnaireNew = React.lazy(() => import('./pages/EmployeeQuesti
|
||||
const QuestionTypesDemo = React.lazy(() => import('./pages/QuestionTypesDemo'));
|
||||
const FormsDashboard = React.lazy(() => import('./pages/FormsDashboard'));
|
||||
const SubscriptionSetup = React.lazy(() => import('./pages/SubscriptionSetup'));
|
||||
const MarkdownTest = React.lazy(() => import('./pages/MarkdownTest'));
|
||||
// const MarkdownTest = React.lazy(() => import('./pages/MarkdownTest'));
|
||||
|
||||
// Loading component for Suspense fallback
|
||||
const LoadingSpinner: React.FC = () => (
|
||||
@@ -238,10 +239,22 @@ function App() {
|
||||
<Route path="/company-wiki" element={<SuspenseWrapper><CompanyWiki /></SuspenseWrapper>} />
|
||||
<Route path="/submissions" element={<SuspenseWrapper><Submissions /></SuspenseWrapper>} />
|
||||
<Route path="/reports" element={<SuspenseWrapper><Reports /></SuspenseWrapper>} />
|
||||
<Route
|
||||
path="/help"
|
||||
element={
|
||||
<SuspenseWrapper><HelpNew /></SuspenseWrapper>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/chat"
|
||||
element={
|
||||
<SuspenseWrapper><Chat /></SuspenseWrapper>
|
||||
}
|
||||
/>
|
||||
</Route>
|
||||
|
||||
{/* Debug routes */}
|
||||
<Route path="/markdown-test" element={<SuspenseWrapper><MarkdownTest /></SuspenseWrapper>} />
|
||||
{/* <Route path="/markdown-test" element={<SuspenseWrapper><MarkdownTest /></SuspenseWrapper>} /> */}
|
||||
<Route
|
||||
path="/question-types-demo"
|
||||
element={
|
||||
@@ -274,4 +287,4 @@ function App() {
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
export default App;
|
||||
|
||||
@@ -57,9 +57,9 @@ export const CompanyWikiCompletedState: React.FC<CompanyWikiCompletedStateProps>
|
||||
return (
|
||||
<div className="flex-1 self-stretch inline-flex justify-start items-center">
|
||||
{/* Table of Contents */}
|
||||
<div className="flex-1 self-stretch max-w-64 min-w-64 border-r border-Outline-Outline-Gray-200 dark:border-[--$1] inline-flex flex-col justify-start items-start">
|
||||
<div className="flex-1 self-stretch max-w-64 min-w-64 border-r border-[--Outline-Outline-Gray-200] inline-flex flex-col justify-start items-start">
|
||||
<div className="self-stretch p-5 inline-flex justify-start items-center gap-2.5">
|
||||
<div className="flex-1 justify-start text-[--Neutrals-NeutralSlate950] dark:text-[--Neutrals-NeutralSlate950] text-base font-medium font-['Inter'] leading-normal">Table of contents</div>
|
||||
<div className="flex-1 justify-start text-[--Text-Gray-950] text-base font-medium font-['Inter'] leading-normal">Table of contents</div>
|
||||
</div>
|
||||
<div className="self-stretch px-3 flex flex-col justify-start items-start gap-1.5">
|
||||
{sections.map((section, index) => {
|
||||
@@ -70,14 +70,14 @@ export const CompanyWikiCompletedState: React.FC<CompanyWikiCompletedStateProps>
|
||||
<div
|
||||
key={index}
|
||||
onClick={() => onSectionClick?.(sectionNumber)}
|
||||
className={`self-stretch p-2 rounded-[10px] inline-flex justify-start items-center gap-2 overflow-hidden cursor-pointer hover:bg-[--Neutrals-NeutralSlate50] dark:hover:bg-[--Neutrals-NeutralSlate700] ${isActive ? 'bg-[--Neutrals-NeutralSlate100] dark:bg-[--Neutrals-NeutralSlate700] shadow-[0px_1px_2px_0px_rgba(10,13,20,0.03)]' : ''}`}
|
||||
className={`self-stretch p-2 rounded-[10px] inline-flex justify-start items-center gap-2 overflow-hidden cursor-pointer ${isActive ? 'bg-[--Main-BG-Gray-100] hover:bg-[--Neutrals-NeutralSlate200] shadow-[0px_1px_2px_0px_rgba(10,13,20,0.03)]' : 'hover:bg-[--Main-BG-Gray-100]'}`}
|
||||
>
|
||||
<div className={`h-5 p-0.5 rounded-[999px] inline-flex flex-col justify-center items-center gap-0.5 overflow-hidden ${isActive ? 'bg-[--Brand-Orange]' : 'bg-[--Neutrals-NeutralSlate100] dark:bg-[--Neutrals-NeutralSlate600]'}`}>
|
||||
<div className={`w-4 text-center justify-start text-xs font-medium font-['Inter'] leading-none ${isActive ? 'text-[--Neutrals-NeutralSlate0]' : 'text-Text-Dark-950 dark:text-[--Neutrals-NeutralSlate0]'}`}>
|
||||
<div className={`h-5 p-0.5 rounded-[999px] inline-flex flex-col justify-center items-center gap-0.5 overflow-hidden ${isActive ? 'bg-[--Brand-Orange]' : 'bg-[--Text-Gray-100]'}`}>
|
||||
<div className={`w-4 text-center justify-start text-xs font-medium font-['Inter'] leading-none ${isActive ? 'text-[--Text-Gray-0]' : 'text-[--Text-Dark-950]'}`}>
|
||||
{sectionNumber}
|
||||
</div>
|
||||
</div>
|
||||
<div className={`flex-1 justify-start text-xs font-medium font-['Inter'] leading-none ${isActive ? 'text-[--Neutrals-NeutralSlate800] dark:text-[--Neutrals-NeutralSlate100]' : 'text-Text-Gray-500 dark:text-[--Neutrals-NeutralSlate100]'}`}>
|
||||
<div className={`flex-1 justify-start text-xs font-medium font-['Inter'] leading-none ${isActive ? 'text-[--Text-Gray-800]' : 'text-[--Text-Gray-500]'}`}>
|
||||
{section}
|
||||
</div>
|
||||
</div>
|
||||
@@ -89,25 +89,25 @@ export const CompanyWikiCompletedState: React.FC<CompanyWikiCompletedStateProps>
|
||||
{/* Main Content */}
|
||||
<div className="flex-1 self-stretch inline-flex flex-col justify-start items-start">
|
||||
<div className="self-stretch p-5 inline-flex justify-start items-center gap-2.5">
|
||||
<div className="flex-1 justify-start text-[--Neutrals-NeutralSlate800] dark:text-[--Neutrals-NeutralSlate800] text-xl font-medium font-['Neue_Montreal'] leading-normal">
|
||||
<div className="flex-1 justify-start text-[--Text-Gray-800] text-xl font-medium font-['Neue_Montreal'] leading-normal">
|
||||
{sections[activeSection - 1]}
|
||||
</div>
|
||||
</div>
|
||||
<div className="self-stretch px-5 flex flex-col justify-start items-start gap-4">
|
||||
{qaItems.map((item, index) => (
|
||||
<div key={index} className="self-stretch p-3 bg-[--Neutrals-NeutralSlate100] dark:bg-[--Neutrals-NeutralSlate100] rounded-2xl shadow-[0px_1px_2px_0px_rgba(0,0,0,0.02)] flex flex-col justify-center items-start gap-2 overflow-hidden">
|
||||
<div key={index} className="self-stretch p-3 bg-[--Neutrals-NeutralSlate100] rounded-2xl shadow-[0px_1px_2px_0px_rgba(0,0,0,0.02)] flex flex-col justify-center items-start gap-2 overflow-hidden">
|
||||
<div className="self-stretch px-3 py-px inline-flex justify-start items-center gap-2.5">
|
||||
<div className="flex-1 flex justify-center items-center gap-3">
|
||||
<div className="w-3 self-stretch justify-start text-[--Neutrals-NeutralSlate300] dark:text-[--Neutrals-NeutralSlate300] text-base font-semibold font-['Inter'] leading-normal">Q</div>
|
||||
<div className="flex-1 justify-start text-[--Neutrals-NeutralSlate600] dark:text-[--Neutrals-NeutralSlate600] text-sm font-medium font-['Inter'] leading-tight">
|
||||
<div className="w-3 self-stretch justify-start text-[--Text-Gray-300] text-base font-semibold font-['Inter'] leading-normal">Q</div>
|
||||
<div className="flex-1 justify-start text-[--Text-Gray-600] text-sm font-medium font-['Inter'] leading-tight">
|
||||
{item.question}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="self-stretch px-3 py-2 bg-[--Neutrals-NeutralSlate0] dark:bg-[--Neutrals-NeutralSlate0] rounded-[10px] inline-flex justify-between items-center">
|
||||
<div className="self-stretch px-3 py-2 bg-[--Neutrals-NeutralSlate0] rounded-[10px] inline-flex justify-between items-center">
|
||||
<div className="flex-1 flex justify-start items-start gap-3">
|
||||
<div className="w-3.5 h-6 justify-center text-[--Neutrals-NeutralSlate300] dark:text-[--Neutrals-NeutralSlate300] text-base font-semibold font-['Inter'] leading-normal">A</div>
|
||||
<div className="flex-1 justify-start text-[--Neutrals-NeutralSlate800] dark:text-[--Neutrals-NeutralSlate800] text-base font-normal font-['Inter'] leading-normal whitespace-pre-line">
|
||||
<div className="w-3.5 h-6 justify-center text-[--Text-Gray-300] text-base font-semibold font-['Inter'] leading-normal">A</div>
|
||||
<div className="flex-1 justify-start text-[--Text-Gray-800] text-base font-normal font-['Inter'] leading-normal whitespace-pre-line">
|
||||
{item.answer}
|
||||
</div>
|
||||
</div>
|
||||
@@ -118,11 +118,11 @@ export const CompanyWikiCompletedState: React.FC<CompanyWikiCompletedStateProps>
|
||||
{/* Additional Questions */}
|
||||
<div className="self-stretch pl-3 pr-6 pt-3 pb-6 bg-[--Neutrals-NeutralSlate100] rounded-2xl shadow-[0px_1px_2px_0px_rgba(0,0,0,0.02)] flex flex-col justify-center items-start gap-2 overflow-hidden">
|
||||
<div className="self-stretch inline-flex justify-center items-center gap-3">
|
||||
<div className="flex-1 justify-start text-Text-Gray-600 text-sm font-medium font-['Inter'] leading-tight">What is the mission of your company?</div>
|
||||
<div className="flex-1 justify-start text-[--Text-Gray-600] text-sm font-medium font-['Inter'] leading-tight">What is the mission of your company?</div>
|
||||
</div>
|
||||
<div className="self-stretch px-3 py-2 bg-Light-Grays-l-gray08 rounded-[10px] outline outline-1 outline-offset-[-1px] outline-Text-Gray-200 inline-flex justify-between items-center">
|
||||
<div className="self-stretch px-3 py-2 bg-[--Neutrals-NeutralSlate200] rounded-[10px] outline outline-1 outline-offset-[-1px] outline-[--Neutrals-NeutralSlate300] inline-flex justify-between items-center">
|
||||
<div className="flex-1 flex justify-start items-start gap-3">
|
||||
<div className="flex-1 justify-start text-Text-Gray-800 text-base font-normal font-['Inter'] leading-normal">
|
||||
<div className="flex-1 justify-start text-[--Text-Gray-800] text-base font-normal font-['Inter'] leading-normal">
|
||||
Our mission is to not only create value but also to foster a collaborative environment where innovation thrives. We aim to empower our team members to contribute their unique skills and perspectives, ensuring that every project we undertake is a reflection of our collective creativity and dedication. By prioritizing both individual growth and teamwork, we strive to build a company culture that values excellence and continuous improvement. Together, we can achieve remarkable results that benefit not just our organization, but also our clients and the community at...
|
||||
</div>
|
||||
</div>
|
||||
@@ -132,14 +132,14 @@ export const CompanyWikiCompletedState: React.FC<CompanyWikiCompletedStateProps>
|
||||
<div className="self-stretch p-3 bg-[--Neutrals-NeutralSlate100] rounded-2xl shadow-[0px_1px_2px_0px_rgba(0,0,0,0.02)] flex flex-col justify-center items-start gap-2 overflow-hidden">
|
||||
<div className="self-stretch px-3 py-px inline-flex justify-start items-center gap-2.5">
|
||||
<div className="flex-1 flex justify-center items-center gap-3">
|
||||
<div className="w-3 self-stretch justify-start text-[--Neutrals-NeutralSlate300] text-base font-semibold font-['Inter'] leading-normal">Q</div>
|
||||
<div className="flex-1 justify-start text-[--Neutrals-NeutralSlate600] text-sm font-medium font-['Inter'] leading-tight">What is the mission of your company?</div>
|
||||
<div className="w-3 self-stretch justify-start text-[--Text-Gray-300] text-base font-semibold font-['Inter'] leading-normal">Q</div>
|
||||
<div className="flex-1 justify-start text-[--Text-Gray-600] text-sm font-medium font-['Inter'] leading-tight">What is the mission of your company?</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="self-stretch px-3 py-2 bg-Light-Grays-l-gray08 rounded-[10px] inline-flex justify-between items-center">
|
||||
<div className="self-stretch px-3 py-2 bg-[--Light-Grays-l-gray08] rounded-[10px] inline-flex justify-between items-center">
|
||||
<div className="flex-1 flex justify-start items-start gap-3">
|
||||
<div className="w-3.5 h-6 justify-center text-[--Neutrals-NeutralSlate300] text-base font-semibold font-['Inter'] leading-normal">A</div>
|
||||
<div className="flex-1 justify-start text-[--Neutrals-NeutralSlate800] text-base font-normal font-['Inter'] leading-normal">The mission is to create value as well as working</div>
|
||||
<div className="w-3.5 h-6 justify-center text-[--Text-Gray-300] text-base font-semibold font-['Inter'] leading-normal">A</div>
|
||||
<div className="flex-1 justify-start text-[--Text-Gray-800] text-base font-normal font-['Inter'] leading-normal">The mission is to create value as well as working</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -147,4 +147,4 @@ export const CompanyWikiCompletedState: React.FC<CompanyWikiCompletedStateProps>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
@@ -17,56 +17,56 @@ export const CompanyWikiEmptyState: React.FC<CompanyWikiEmptyStateProps> = ({
|
||||
<div className="flex-1 self-stretch inline-flex justify-start items-center">
|
||||
<div className="flex-1 self-stretch max-w-64 min-w-64 border-r border-Outline-Outline-Gray-200 inline-flex flex-col justify-start items-start">
|
||||
<div className="self-stretch p-5 inline-flex justify-start items-center gap-2.5">
|
||||
<div className="flex-1 justify-start text-[--Neutrals-NeutralSlate950] text-base font-medium font-['Inter'] leading-normal">Table of contents</div>
|
||||
<div className="flex-1 justify-start text-[--Text-Gray-950] text-base font-medium font-['Inter'] leading-normal">Table of contents</div>
|
||||
</div>
|
||||
<div className="self-stretch px-3 flex flex-col justify-start items-start gap-1.5">
|
||||
<div className="self-stretch p-2 bg-[--Neutrals-NeutralSlate100] rounded-full shadow-[0px_1px_2px_0px_rgba(10,13,20,0.03)] inline-flex justify-start items-center gap-2 overflow-hidden">
|
||||
<div className="h-5 p-0.5 bg-[--Brand-Orange] rounded-[999px] inline-flex flex-col justify-center items-center gap-0.5 overflow-hidden">
|
||||
<div className="w-4 text-center justify-start text-[--Neutrals-NeutralSlate0] text-xs font-medium font-['Inter'] leading-none">1</div>
|
||||
<div className="w-4 text-center justify-start text-[--Text-Gray-0] text-xs font-medium font-['Inter'] leading-none">1</div>
|
||||
</div>
|
||||
<div className="flex-1 justify-start text-[--Neutrals-NeutralSlate800] text-xs font-medium font-['Inter'] leading-none">Company Overview & Vision</div>
|
||||
<div className="flex-1 justify-start text-[--Text-Gray-800] text-xs font-medium font-['Inter'] leading-none">Company Overview & Vision</div>
|
||||
</div>
|
||||
<div className="self-stretch p-2 rounded-[10px] inline-flex justify-start items-center gap-2 overflow-hidden">
|
||||
<div className="h-5 p-0.5 bg-[--Neutrals-NeutralSlate100] rounded-[999px] inline-flex flex-col justify-center items-center gap-0.5 overflow-hidden">
|
||||
<div className="w-4 text-center justify-start text-Text-Dark-950 text-xs font-normal font-['Inter'] leading-none">2</div>
|
||||
<div className="w-4 text-center justify-start text-[--Text-Dark-950] text-xs font-normal font-['Inter'] leading-none">2</div>
|
||||
</div>
|
||||
<div className="flex-1 justify-start text-Text-Gray-500 text-xs font-medium font-['Inter'] leading-none">Leadership & Organizational Structure</div>
|
||||
<div className="flex-1 justify-start text-[--Text-Gray-500] text-xs font-medium font-['Inter'] leading-none">Leadership & Organizational Structure</div>
|
||||
</div>
|
||||
<div className="self-stretch p-2 rounded-[10px] inline-flex justify-start items-center gap-2 overflow-hidden">
|
||||
<div className="h-5 p-0.5 bg-[--Neutrals-NeutralSlate100] rounded-[999px] inline-flex flex-col justify-center items-center gap-0.5 overflow-hidden">
|
||||
<div className="w-4 text-center justify-start text-Text-Dark-950 text-xs font-normal font-['Inter'] leading-none">3</div>
|
||||
<div className="w-4 text-center justify-start text-[--Text-Dark-950] text-xs font-normal font-['Inter'] leading-none">3</div>
|
||||
</div>
|
||||
<div className="flex-1 justify-start text-Text-Gray-500 text-xs font-medium font-['Inter'] leading-none">Operations & Execution</div>
|
||||
<div className="flex-1 justify-start text-[--Text-Gray-500] text-xs font-medium font-['Inter'] leading-none">Operations & Execution</div>
|
||||
</div>
|
||||
<div className="self-stretch p-2 rounded-[10px] inline-flex justify-start items-center gap-2 overflow-hidden">
|
||||
<div className="h-5 p-0.5 bg-[--Neutrals-NeutralSlate100] rounded-[999px] inline-flex flex-col justify-center items-center gap-0.5 overflow-hidden">
|
||||
<div className="w-4 text-center justify-start text-Text-Dark-950 text-xs font-normal font-['Inter'] leading-none">4</div>
|
||||
<div className="w-4 text-center justify-start text-[--Text-Dark-950] text-xs font-normal font-['Inter'] leading-none">4</div>
|
||||
</div>
|
||||
<div className="flex-1 justify-start text-Text-Gray-500 text-xs font-medium font-['Inter'] leading-none">Culture & Team Health</div>
|
||||
<div className="flex-1 justify-start text-[--Text-Gray-500] text-xs font-medium font-['Inter'] leading-none">Culture & Team Health</div>
|
||||
</div>
|
||||
<div className="self-stretch p-2 rounded-[10px] inline-flex justify-start items-center gap-2 overflow-hidden">
|
||||
<div className="h-5 p-0.5 bg-[--Neutrals-NeutralSlate100] rounded-[999px] inline-flex flex-col justify-center items-center gap-0.5 overflow-hidden">
|
||||
<div className="w-4 text-center justify-start text-Text-Dark-950 text-xs font-normal font-['Inter'] leading-none">5</div>
|
||||
<div className="w-4 text-center justify-start text-[--Text-Dark-950] text-xs font-normal font-['Inter'] leading-none">5</div>
|
||||
</div>
|
||||
<div className="flex-1 justify-start text-Text-Gray-500 text-xs font-medium font-['Inter'] leading-none">Sales, Marketing & Growth</div>
|
||||
<div className="flex-1 justify-start text-[--Text-Gray-500] text-xs font-medium font-['Inter'] leading-none">Sales, Marketing & Growth</div>
|
||||
</div>
|
||||
<div className="self-stretch p-2 rounded-[10px] inline-flex justify-start items-center gap-2 overflow-hidden">
|
||||
<div className="h-5 p-0.5 bg-[--Neutrals-NeutralSlate100] rounded-[999px] inline-flex flex-col justify-center items-center gap-0.5 overflow-hidden">
|
||||
<div className="w-4 text-center justify-start text-Text-Dark-950 text-xs font-normal font-['Inter'] leading-none">6</div>
|
||||
<div className="w-4 text-center justify-start text-[--Text-Dark-950] text-xs font-normal font-['Inter'] leading-none">6</div>
|
||||
</div>
|
||||
<div className="flex-1 justify-start text-Text-Gray-500 text-xs font-medium font-['Inter'] leading-none">Financial Health & Metrics</div>
|
||||
<div className="flex-1 justify-start text-[--Text-Gray-500] text-xs font-medium font-['Inter'] leading-none">Financial Health & Metrics</div>
|
||||
</div>
|
||||
<div className="self-stretch p-2 rounded-[10px] inline-flex justify-start items-center gap-2 overflow-hidden">
|
||||
<div className="h-5 p-0.5 bg-[--Neutrals-NeutralSlate100] rounded-[999px] inline-flex flex-col justify-center items-center gap-0.5 overflow-hidden">
|
||||
<div className="w-4 text-center justify-start text-Text-Dark-950 text-xs font-normal font-['Inter'] leading-none">7</div>
|
||||
<div className="w-4 text-center justify-start text-[--Text-Dark-950] text-xs font-normal font-['Inter'] leading-none">7</div>
|
||||
</div>
|
||||
<div className="flex-1 justify-start text-Text-Gray-500 text-xs font-medium font-['Inter'] leading-none">Innovation & Product/Service Strategy</div>
|
||||
<div className="flex-1 justify-start text-[--Text-Gray-500] text-xs font-medium font-['Inter'] leading-none">Innovation & Product/Service Strategy</div>
|
||||
</div>
|
||||
<div className="self-stretch p-2 rounded-[10px] inline-flex justify-start items-center gap-2 overflow-hidden">
|
||||
<div className="h-5 p-0.5 bg-[--Neutrals-NeutralSlate100] rounded-[999px] inline-flex flex-col justify-center items-center gap-0.5 overflow-hidden">
|
||||
<div className="w-4 text-center justify-start text-Text-Dark-950 text-xs font-normal font-['Inter'] leading-none">8</div>
|
||||
<div className="w-4 text-center justify-start text-[--Text-Dark-950] text-xs font-normal font-['Inter'] leading-none">8</div>
|
||||
</div>
|
||||
<div className="flex-1 justify-start text-Text-Gray-500 text-xs font-medium font-['Inter'] leading-none">Personal Leadership & Risk</div>
|
||||
<div className="flex-1 justify-start text-[--Text-Gray-500] text-xs font-medium font-['Inter'] leading-none">Personal Leadership & Risk</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -81,7 +81,7 @@ export const CompanyWikiEmptyState: React.FC<CompanyWikiEmptyStateProps> = ({
|
||||
<path d="M12 30V18.6667C12 17.3867 12 16.7467 12.1453 16.248C12.2731 15.8071 12.5171 15.4109 12.848 15.1053C13.2133 14.7667 13.7066 14.6667 14.6933 14.6667H17.3067C18.2934 14.6667 18.7867 14.7667 19.152 15.1053C19.4829 15.4109 19.7269 15.8071 19.8547 16.248C20 16.7467 20 17.3867 20 18.6667V30M14.6903 3.68533L6.04715 11.5188C5.44269 12.0684 5.14047 12.3431 4.92271 12.6778C4.73015 12.9739 4.58613 13.3073 4.49871 13.6608C4.4 14.0575 4.4 14.4803 4.4 15.3261V23.7333C4.4 25.2267 4.4 25.9733 4.69065 26.544C4.94631 27.0458 5.35421 27.4537 5.85603 27.7093C6.42669 28 7.17323 28 8.66667 28H23.3333C24.8268 28 25.5733 28 26.144 27.7093C26.6458 27.4537 27.0537 27.0458 27.3093 26.544C27.6 25.9733 27.6 25.2267 27.6 23.7333V15.3261C27.6 14.4803 27.6 14.0575 27.5013 13.6608C27.4139 13.3073 27.2699 12.9739 27.0773 12.6778C26.8595 12.3431 26.5573 12.0684 25.9529 11.5188L17.3097 3.68533C16.8413 3.27241 16.6071 3.06595 16.3485 2.98821C16.1203 2.9184 15.8797 2.9184 15.6515 2.98821C15.3929 3.06595 15.1587 3.27241 14.6903 3.68533Z" stroke="var(--Neutrals-NeutralSlate400)" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</svg>
|
||||
</div>
|
||||
<div className="text-[--Neutrals-NeutralSlate600] text-sm">Company Wiki Empty State</div>
|
||||
<div className="text-[--Text-Gray-600] text-sm">Company Wiki Empty State</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -89,10 +89,10 @@ export const CompanyWikiEmptyState: React.FC<CompanyWikiEmptyStateProps> = ({
|
||||
{/* Progress and Call to Action */}
|
||||
<div className="text-center max-w-md">
|
||||
<div className="mb-6">
|
||||
<div className="text-[--Neutrals-NeutralSlate950] text-2xl font-medium font-['Neue_Montreal'] leading-normal mb-2">
|
||||
<div className="text-[--Text-Gray-950] text-2xl font-medium font-['Neue_Montreal'] leading-normal mb-2">
|
||||
You're {progress}% Done
|
||||
</div>
|
||||
<div className="text-[--Neutrals-NeutralSlate600] text-base leading-normal">
|
||||
<div className="text-[--Text-Gray-600] text-base leading-normal">
|
||||
Complete your onboarding to unlock your Company Wiki
|
||||
</div>
|
||||
</div>
|
||||
@@ -116,4 +116,4 @@ export const CompanyWikiEmptyState: React.FC<CompanyWikiEmptyStateProps> = ({
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
@@ -27,9 +27,9 @@ export const CompanyWikiEmptyState: React.FC<CompanyWikiEmptyStateProps> = ({
|
||||
return (
|
||||
<div className="flex-1 self-stretch inline-flex justify-start items-center">
|
||||
{/* Table of Contents */}
|
||||
<div className="flex-1 self-stretch max-w-64 min-w-64 border-r border-Outline-Outline-Gray-200 dark:border-[--$1] inline-flex flex-col justify-start items-start">
|
||||
<div className="flex-1 self-stretch max-w-64 min-w-64 border-r border-[--Outline-Outline-Gray-200] inline-flex flex-col justify-start items-start">
|
||||
<div className="self-stretch p-5 inline-flex justify-start items-center gap-2.5">
|
||||
<div className="flex-1 justify-start text-[--Neutrals-NeutralSlate950] dark:text-[--Neutrals-NeutralSlate950] text-base font-medium font-['Inter'] leading-normal">Table of contents</div>
|
||||
<div className="flex-1 justify-start text-[--Text-Gray-950] text-base font-medium font-['Inter'] leading-normal">Table of contents</div>
|
||||
</div>
|
||||
<div className="self-stretch px-3 flex flex-col justify-start items-start gap-1.5">
|
||||
{sections.map((section, index) => {
|
||||
@@ -39,14 +39,14 @@ export const CompanyWikiEmptyState: React.FC<CompanyWikiEmptyStateProps> = ({
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
className={`self-stretch p-2 rounded-[10px] inline-flex justify-start items-center gap-2 overflow-hidden ${isActive ? 'bg-[--Neutrals-NeutralSlate100] dark:bg-[--Neutrals-NeutralSlate800] shadow-[0px_1px_2px_0px_rgba(10,13,20,0.03)]' : 'hover:bg-[--Neutrals-NeutralSlate50] dark:hover:bg-[--Neutrals-NeutralSlate700]'}`}
|
||||
className={`self-stretch p-2 rounded-[10px] inline-flex justify-start items-center gap-2 overflow-hidden ${isActive ? 'bg-[--Neutrals-NeutralSlate100] shadow-[0px_1px_2px_0px_rgba(10,13,20,0.03)]' : 'hover:bg-[--Neutrals-NeutralSlate50]'}`}
|
||||
>
|
||||
<div className={`h-5 p-0.5 rounded-[999px] inline-flex flex-col justify-center items-center gap-0.5 overflow-hidden ${isActive ? 'bg-[--Brand-Orange]' : 'bg-[--Neutrals-NeutralSlate100] dark:bg-[--Neutrals-NeutralSlate600]'}`}>
|
||||
<div className={`w-4 text-center justify-start text-xs font-medium font-['Inter'] leading-none ${isActive ? 'text-[--Neutrals-NeutralSlate0]' : 'text-Text-Dark-950 dark:text-[--Neutrals-NeutralSlate0]'}`}>
|
||||
<div className={`h-5 p-0.5 rounded-[999px] inline-flex flex-col justify-center items-center gap-0.5 overflow-hidden ${isActive ? 'bg-[--Brand-Orange]' : 'bg-[--Neutrals-NeutralSlate100]'}`}>
|
||||
<div className={`w-4 text-center justify-start text-xs font-medium font-['Inter'] leading-none ${isActive ? 'text-[--Text-Gray-0]' : 'text-[--Text-Dark-950]'}`}>
|
||||
{sectionNumber}
|
||||
</div>
|
||||
</div>
|
||||
<div className={`flex-1 justify-start text-xs font-medium font-['Inter'] leading-none ${isActive ? 'text-[--Neutrals-NeutralSlate800] dark:text-[--Neutrals-NeutralSlate100]' : 'text-Text-Gray-500 dark:text-[--Neutrals-NeutralSlate100]'}`}>
|
||||
<div className={`flex-1 justify-start text-xs font-medium font-['Inter'] leading-none ${isActive ? 'text-[--Text-Gray-800]' : 'text-[--Text-Gray-500]'}`}>
|
||||
{section}
|
||||
</div>
|
||||
</div>
|
||||
@@ -58,14 +58,14 @@ export const CompanyWikiEmptyState: React.FC<CompanyWikiEmptyStateProps> = ({
|
||||
{/* Main Content */}
|
||||
<div className="flex-1 self-stretch inline-flex flex-col justify-start items-start">
|
||||
<div className="self-stretch p-5 inline-flex justify-start items-center gap-2.5">
|
||||
<div className="flex-1 justify-start text-[--Neutrals-NeutralSlate800] dark:text-[--Neutrals-NeutralSlate800] text-xl font-medium font-['Neue_Montreal'] leading-normal">
|
||||
<div className="flex-1 justify-start text-[--Text-Gray-800] text-xl font-medium font-['Neue_Montreal'] leading-normal">
|
||||
Company Overview & Vision
|
||||
</div>
|
||||
</div>
|
||||
<div className="self-stretch flex-1 p-5 flex justify-center items-center">
|
||||
<div className="w-[440px] flex flex-col justify-center items-center gap-8">
|
||||
{/* Progress Illustration Placeholder */}
|
||||
<div className="w-[280px] h-[200px] bg-[--Neutrals-NeutralSlate100] dark:bg-[--Neutrals-NeutralSlate700] rounded-2xl flex items-center justify-center">
|
||||
<div className="w-[280px] h-[200px] bg-[--Neutrals-NeutralSlate100] rounded-2xl flex items-center justify-center">
|
||||
<div className="text-center">
|
||||
<div className="w-16 h-16 mx-auto mb-4 bg-[--Brand-Orange] rounded-full flex items-center justify-center">
|
||||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" className="text-white">
|
||||
@@ -73,21 +73,21 @@ export const CompanyWikiEmptyState: React.FC<CompanyWikiEmptyStateProps> = ({
|
||||
<circle cx="16" cy="16" r="12" stroke="currentColor" strokeWidth="2" />
|
||||
</svg>
|
||||
</div>
|
||||
<div className="text-Text-Gray-600 dark:text-[--Neutrals-NeutralSlate300] text-sm">Progress Illustration</div>
|
||||
<div className="text-[--Text-Gray-600] text-sm">Progress Illustration</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Progress Content */}
|
||||
<div className="self-stretch flex flex-col justify-center items-center gap-4 text-center">
|
||||
<div className="text-[--Neutrals-NeutralSlate800] dark:text-[--Neutrals-NeutralSlate800] text-2xl font-semibold font-['Neue_Montreal'] leading-8">
|
||||
<div className="text-[--Text-Gray-800] text-2xl font-semibold font-['Neue_Montreal'] leading-8">
|
||||
You're {progress}% Done
|
||||
</div>
|
||||
<div className="self-stretch text-Text-Gray-600 dark:text-[--Neutrals-NeutralSlate300] text-base font-normal font-['Inter'] leading-normal">
|
||||
<div className="self-stretch text-[--Text-Gray-600] text-base font-normal font-['Inter'] leading-normal">
|
||||
Complete your company onboarding to unlock your company wiki and comprehensive insights about your organization.
|
||||
</div>
|
||||
|
||||
{/* Progress Bar */}
|
||||
<div className="self-stretch h-2 bg-[--Neutrals-NeutralSlate100] dark:bg-[--Neutrals-NeutralSlate700] rounded-full overflow-hidden">
|
||||
<div className="self-stretch h-2 bg-[--Neutrals-NeutralSlate100] rounded-full overflow-hidden">
|
||||
<div
|
||||
className="h-full bg-[--Brand-Orange] rounded-full transition-all duration-300"
|
||||
style={{ width: `${progress}%` }}
|
||||
@@ -97,7 +97,7 @@ export const CompanyWikiEmptyState: React.FC<CompanyWikiEmptyStateProps> = ({
|
||||
{/* Action Button */}
|
||||
<button
|
||||
onClick={onCompleteOnboarding}
|
||||
className="w-full px-6 py-3 bg-[--Brand-Orange] hover:bg-orange-600 text-[--Neutrals-NeutralSlate0] text-base font-medium font-['Inter'] leading-normal rounded-xl transition-colors"
|
||||
className="w-full px-6 py-3 bg-[--Brand-Orange] hover:bg-blue-600 text-[--Text-Gray-0] text-base font-medium font-['Inter'] leading-normal rounded-xl transition-colors"
|
||||
>
|
||||
Complete Onboarding
|
||||
</button>
|
||||
@@ -107,4 +107,4 @@ export const CompanyWikiEmptyState: React.FC<CompanyWikiEmptyStateProps> = ({
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
@@ -27,15 +27,15 @@ export const InviteEmployeesModal: React.FC<InviteEmployeesModalProps> = ({
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
||||
<div className="w-[420px] bg-[--Neutrals-NeutralSlate0] dark:bg-[--Neutrals-NeutralSlate0] rounded-3xl shadow-[0px_25px_50px_-12px_rgba(0,0,0,0.25)] flex flex-col justify-start items-start overflow-hidden">
|
||||
<div className="w-[420px] bg-[--Neutrals-NeutralSlate0] rounded-3xl shadow-[0px_25px_50px_-12px_rgba(0,0,0,0.25)] flex flex-col justify-start items-start overflow-hidden">
|
||||
{/* Header */}
|
||||
<div className="self-stretch p-6 inline-flex justify-between items-center">
|
||||
<div className="flex justify-start items-center gap-2.5">
|
||||
<div className="justify-start text-Text-Dark-950 dark:text-[--Neutrals-NeutralSlate50] text-lg font-semibold font-['Inter'] leading-7">Invite employees</div>
|
||||
<div className="justify-start text-[--Text-Dark-950] text-lg font-semibold font-['Inter'] leading-7">Invite employees</div>
|
||||
</div>
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="w-6 h-6 flex justify-center items-center hover:bg-[--Neutrals-NeutralSlate100] dark:hover:bg-[--Neutrals-NeutralSlate700] rounded"
|
||||
className="w-6 h-6 flex justify-center items-center hover:bg-[--Neutrals-NeutralSlate100] rounded"
|
||||
>
|
||||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none">
|
||||
<path d="M13 1L1 13M1 1L13 13" stroke="#666D80" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
||||
@@ -46,16 +46,16 @@ export const InviteEmployeesModal: React.FC<InviteEmployeesModalProps> = ({
|
||||
{/* Form */}
|
||||
<form onSubmit={handleSubmit} className="self-stretch px-6 flex flex-col justify-start items-start gap-4">
|
||||
<div className="self-stretch flex flex-col justify-start items-start gap-1">
|
||||
<div className="self-stretch justify-start text-[--Neutrals-NeutralSlate950] dark:text-[--Neutrals-NeutralSlate950] text-sm font-medium font-['Inter'] leading-tight">
|
||||
<div className="self-stretch justify-start text-[--Text-Gray-950] text-sm font-medium font-['Inter'] leading-tight">
|
||||
Email
|
||||
</div>
|
||||
<div className="self-stretch h-10 px-3 py-2 bg-[--Neutrals-NeutralSlate0] dark:bg-[--Neutrals-NeutralSlate0] rounded-lg border border-Outline-Outline-Gray-300 dark:border-[--$1] inline-flex justify-start items-center gap-2">
|
||||
<div className="self-stretch h-10 px-3 py-2 bg-[--Neutrals-NeutralSlate0] rounded-lg border border-[--Outline-Outline-Gray-300] inline-flex justify-start items-center gap-2">
|
||||
<input
|
||||
type="email"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
placeholder="Enter email address"
|
||||
className="flex-1 text-[--Neutrals-NeutralSlate950] dark:text-[--Neutrals-NeutralSlate950] text-sm font-normal font-['Inter'] leading-tight bg-transparent outline-none placeholder:text-Text-Gray-500 dark:placeholder:text-[--Neutrals-NeutralSlate950]"
|
||||
className="flex-1 text-[--Text-Gray-950] text-sm font-normal font-['Inter'] leading-tight bg-transparent outline-none placeholder:text-[--Text-Gray-500]"
|
||||
autoFocus
|
||||
/>
|
||||
</div>
|
||||
@@ -73,14 +73,14 @@ export const InviteEmployeesModal: React.FC<InviteEmployeesModalProps> = ({
|
||||
<div className="flex justify-start items-start gap-3">
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="px-4 py-2 bg-[--Neutrals-NeutralSlate0] dark:bg-[--Neutrals-NeutralSlate0] rounded-lg border border-Outline-Outline-Gray-300 dark:border-[--$1] text-[--Neutrals-NeutralSlate700] dark:text-[--Neutrals-NeutralSlate700] text-sm font-medium font-['Inter'] leading-tight hover:bg-[--Neutrals-NeutralSlate50] dark:hover:bg-[--Neutrals-NeutralSlate0]"
|
||||
className="px-4 py-2 bg-[--Neutrals-NeutralSlate0] rounded-lg border border-[--Outline-Outline-Gray-300] text-[--Text-Gray-700] text-sm font-medium font-['Inter'] leading-tight hover:bg-[--Neutrals-NeutralSlate50]"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
onClick={handleSubmit}
|
||||
disabled={!email.trim()}
|
||||
className="px-4 py-2 bg-[--Brand-Orange] rounded-lg text-[--Neutrals-NeutralSlate0] text-sm font-medium font-['Inter'] leading-tight hover:bg-orange-600 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
className="px-4 py-2 bg-[--Brand-Orange] rounded-lg text-[--Text-Gray-0] text-sm font-medium font-['Inter'] leading-tight hover:bg-orange-600 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>
|
||||
Send Invite
|
||||
</button>
|
||||
@@ -89,4 +89,4 @@ export const InviteEmployeesModal: React.FC<InviteEmployeesModalProps> = ({
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
@@ -60,17 +60,17 @@ export const InviteMultipleEmployeesModal: React.FC<InviteMultipleEmployeesModal
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
||||
<div className="w-[480px] bg-[--Neutrals-NeutralSlate0] dark:bg-[--Neutrals-NeutralSlate0] rounded-3xl shadow-[0px_25px_50px_-12px_rgba(0,0,0,0.25)] flex flex-col justify-start items-start overflow-hidden">
|
||||
<div className="w-[480px] bg-[--Neutrals-NeutralSlate0] rounded-3xl shadow-[0px_25px_50px_-12px_rgba(0,0,0,0.25)] flex flex-col justify-start items-start overflow-hidden">
|
||||
{/* Header */}
|
||||
<div className="self-stretch p-6 inline-flex justify-between items-center">
|
||||
<div className="flex justify-start items-center gap-2.5">
|
||||
<div className="justify-start text-Text-Dark-950 dark:text-[--Neutrals-NeutralSlate50] text-lg font-semibold font-['Inter'] leading-7">
|
||||
<div className="justify-start text-[--Text-Dark-950] text-lg font-semibold font-['Inter'] leading-7">
|
||||
Invite multiple employees
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="w-6 h-6 flex justify-center items-center hover:bg-[--Neutrals-NeutralSlate100] dark:hover:bg-[--Neutrals-NeutralSlate700] rounded"
|
||||
className="w-6 h-6 flex justify-center items-center hover:bg-[--Neutrals-NeutralSlate100] rounded"
|
||||
>
|
||||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none">
|
||||
<path d="M13 1L1 13M1 1L13 13" stroke="#666D80" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
||||
@@ -81,11 +81,11 @@ export const InviteMultipleEmployeesModal: React.FC<InviteMultipleEmployeesModal
|
||||
{/* Search Input with Dropdown */}
|
||||
<div className="self-stretch px-6 flex flex-col justify-start items-start gap-4">
|
||||
<div className="self-stretch flex flex-col justify-start items-start gap-1 relative">
|
||||
<div className="self-stretch justify-start text-[--Neutrals-NeutralSlate950] text-sm font-medium font-['Inter'] leading-tight">
|
||||
<div className="self-stretch justify-start text-[--Text-Gray-950] text-sm font-medium font-['Inter'] leading-tight">
|
||||
Search employees
|
||||
</div>
|
||||
<div className="self-stretch h-10 px-3 py-2 bg-[--Neutrals-NeutralSlate0] rounded-lg border border-Outline-Outline-Gray-300 inline-flex justify-start items-center gap-2">
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" className="text-Text-Gray-400">
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" className="text-[--Text-Gray-400]">
|
||||
<path d="M15 15L11.15 11.15M12.6667 7.33333C12.6667 10.2789 10.2789 12.6667 7.33333 12.6667C4.38781 12.6667 2 10.2789 2 7.33333C2 4.38781 4.38781 2 7.33333 2C10.2789 2 12.6667 4.38781 12.6667 7.33333Z" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</svg>
|
||||
<input
|
||||
@@ -97,7 +97,7 @@ export const InviteMultipleEmployeesModal: React.FC<InviteMultipleEmployeesModal
|
||||
}}
|
||||
onFocus={() => setShowDropdown(searchTerm.length > 0)}
|
||||
placeholder="Type name or email to search..."
|
||||
className="flex-1 text-[--Neutrals-NeutralSlate950] text-sm font-normal font-['Inter'] leading-tight bg-transparent outline-none placeholder:text-Text-Gray-500"
|
||||
className="flex-1 text-[--Text-Gray-950] text-sm font-normal font-['Inter'] leading-tight bg-transparent outline-none placeholder:text-[--Text-Gray-500]"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -114,8 +114,8 @@ export const InviteMultipleEmployeesModal: React.FC<InviteMultipleEmployeesModal
|
||||
{employee.name.charAt(0)}
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<div className="text-sm font-medium text-[--Neutrals-NeutralSlate950]">{employee.name}</div>
|
||||
<div className="text-xs text-Text-Gray-500">{employee.email}</div>
|
||||
<div className="text-sm font-medium text-[--Text-Gray-950]">{employee.name}</div>
|
||||
<div className="text-xs text-[--Text-Gray-500]">{employee.email}</div>
|
||||
</div>
|
||||
</button>
|
||||
))}
|
||||
@@ -126,7 +126,7 @@ export const InviteMultipleEmployeesModal: React.FC<InviteMultipleEmployeesModal
|
||||
{/* Selected Employees */}
|
||||
{selectedEmployees.length > 0 && (
|
||||
<div className="self-stretch flex flex-col justify-start items-start gap-2">
|
||||
<div className="text-sm font-medium text-[--Neutrals-NeutralSlate950]">
|
||||
<div className="text-sm font-medium text-[--Text-Gray-950]">
|
||||
Selected ({selectedEmployees.length})
|
||||
</div>
|
||||
<div className="self-stretch flex flex-wrap gap-2">
|
||||
@@ -138,7 +138,7 @@ export const InviteMultipleEmployeesModal: React.FC<InviteMultipleEmployeesModal
|
||||
<div className="w-5 h-5 bg-[--Brand-Orange] rounded-full flex items-center justify-center text-white text-xs font-medium">
|
||||
{employee.name.charAt(0)}
|
||||
</div>
|
||||
<span className="text-sm text-[--Neutrals-NeutralSlate950]">{employee.name}</span>
|
||||
<span className="text-sm text-[--Text-Gray-950]">{employee.name}</span>
|
||||
<button
|
||||
onClick={() => handleEmployeeRemove(employee.id)}
|
||||
className="w-4 h-4 flex items-center justify-center hover:bg-[--Brand-Orange] hover:bg-opacity-20 rounded-full"
|
||||
@@ -159,14 +159,14 @@ export const InviteMultipleEmployeesModal: React.FC<InviteMultipleEmployeesModal
|
||||
<div className="flex justify-start items-start gap-3">
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="px-4 py-2 bg-[--Neutrals-NeutralSlate0] rounded-lg border border-Outline-Outline-Gray-300 text-[--Neutrals-NeutralSlate700] text-sm font-medium font-['Inter'] leading-tight hover:bg-[--Neutrals-NeutralSlate50]"
|
||||
className="px-4 py-2 bg-[--Neutrals-NeutralSlate0] rounded-lg border border-Outline-Outline-Gray-300 text-[--Text-Gray-700] text-sm font-medium font-['Inter'] leading-tight hover:bg-[--Neutrals-NeutralSlate50]"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
onClick={handleInvite}
|
||||
disabled={selectedEmployees.length === 0}
|
||||
className="px-4 py-2 bg-[--Brand-Orange] rounded-lg text-[--Neutrals-NeutralSlate0] text-sm font-medium font-['Inter'] leading-tight hover:bg-orange-600 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
className="px-4 py-2 bg-[--Brand-Orange] rounded-lg text-[--Text-Gray-0] text-sm font-medium font-['Inter'] leading-tight hover:bg-orange-600 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>
|
||||
Send Invites ({selectedEmployees.length})
|
||||
</button>
|
||||
@@ -175,4 +175,4 @@ export const InviteMultipleEmployeesModal: React.FC<InviteMultipleEmployeesModal
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
@@ -15,118 +15,118 @@ export const MarkdownRenderer: React.FC<MarkdownRendererProps> = ({ content, cla
|
||||
components={{
|
||||
// Headers
|
||||
h1: ({ children }) => (
|
||||
<h1 className="text-lg font-bold text-[--Neutrals-NeutralSlate950] mt-4 mb-3 first:mt-0">
|
||||
<h1 className="text-lg font-bold text-[--Text-Gray-950] mt-4 mb-3 first:mt-0">
|
||||
{children}
|
||||
</h1>
|
||||
),
|
||||
h2: ({ children }) => (
|
||||
<h2 className="text-base font-bold text-[--Neutrals-NeutralSlate950] mt-4 mb-3 first:mt-0">
|
||||
<h2 className="text-base font-bold text-[--Text-Gray-950] mt-4 mb-3 first:mt-0">
|
||||
{children}
|
||||
</h2>
|
||||
),
|
||||
h3: ({ children }) => (
|
||||
<h3 className="text-base font-semibold text-[--Neutrals-NeutralSlate950] mt-3 mb-2 first:mt-0">
|
||||
<h3 className="text-base font-semibold text-[--Text-Gray-950] mt-3 mb-2 first:mt-0">
|
||||
{children}
|
||||
</h3>
|
||||
),
|
||||
h4: ({ children }) => (
|
||||
<h4 className="text-sm font-semibold text-[--Neutrals-NeutralSlate950] mt-3 mb-2 first:mt-0">
|
||||
<h4 className="text-sm font-semibold text-[--Text-Gray-950] mt-3 mb-2 first:mt-0">
|
||||
{children}
|
||||
</h4>
|
||||
),
|
||||
h5: ({ children }) => (
|
||||
<h5 className="text-sm font-medium text-[--Neutrals-NeutralSlate950] mt-2 mb-1 first:mt-0">
|
||||
<h5 className="text-sm font-medium text-[--Text-Gray-950] mt-2 mb-1 first:mt-0">
|
||||
{children}
|
||||
</h5>
|
||||
),
|
||||
h6: ({ children }) => (
|
||||
<h6 className="text-sm font-medium text-[--Neutrals-NeutralSlate800] mt-2 mb-1 first:mt-0">
|
||||
<h6 className="text-sm font-medium text-[--Text-Gray-800] mt-2 mb-1 first:mt-0">
|
||||
{children}
|
||||
</h6>
|
||||
),
|
||||
|
||||
|
||||
// Paragraphs
|
||||
p: ({ children }) => (
|
||||
<p className="text-sm leading-relaxed mb-3 last:mb-0">
|
||||
{children}
|
||||
</p>
|
||||
),
|
||||
|
||||
|
||||
// Strong/Bold text
|
||||
strong: ({ children }) => (
|
||||
<strong className="font-semibold text-[--Neutrals-NeutralSlate950]">
|
||||
<strong className="font-semibold text-[--Text-Gray-950]">
|
||||
{children}
|
||||
</strong>
|
||||
),
|
||||
|
||||
|
||||
// Emphasis/Italic text
|
||||
em: ({ children }) => (
|
||||
<em className="italic">
|
||||
{children}
|
||||
</em>
|
||||
),
|
||||
|
||||
|
||||
// Unordered lists
|
||||
ul: ({ children }) => (
|
||||
<ul className="list-disc list-inside space-y-1 mb-3 ml-2">
|
||||
{children}
|
||||
</ul>
|
||||
),
|
||||
|
||||
|
||||
// Ordered lists
|
||||
ol: ({ children }) => (
|
||||
<ol className="list-decimal list-inside space-y-1 mb-3 ml-2">
|
||||
{children}
|
||||
</ol>
|
||||
),
|
||||
|
||||
|
||||
// List items
|
||||
li: ({ children }) => (
|
||||
<li className="text-sm leading-relaxed ml-1">
|
||||
{children}
|
||||
</li>
|
||||
),
|
||||
|
||||
|
||||
// Code blocks
|
||||
pre: ({ children }) => (
|
||||
<pre className="bg-[--Neutrals-NeutralSlate100] p-3 rounded-lg overflow-x-auto mb-3 text-sm font-mono">
|
||||
{children}
|
||||
</pre>
|
||||
),
|
||||
|
||||
|
||||
// Inline code
|
||||
code: ({ children, className }) => {
|
||||
// Check if this is inline code (no className) or code block
|
||||
const isInline = !className;
|
||||
if (isInline) {
|
||||
return (
|
||||
<code className="bg-[--Neutrals-NeutralSlate100] px-1.5 py-0.5 rounded text-sm font-mono text-[--Neutrals-NeutralSlate800]">
|
||||
<code className="bg-[--Neutrals-NeutralSlate100] px-1.5 py-0.5 rounded text-sm font-mono text-[--Text-Gray-800]">
|
||||
{children}
|
||||
</code>
|
||||
);
|
||||
}
|
||||
return <code className="text-sm font-mono">{children}</code>;
|
||||
},
|
||||
|
||||
|
||||
// Blockquotes
|
||||
blockquote: ({ children }) => (
|
||||
<blockquote className="border-l-4 border-[--Neutrals-NeutralSlate300] pl-4 py-2 mb-3 italic text-[--Neutrals-NeutralSlate700]">
|
||||
<blockquote className="border-l-4 border-[--Neutrals-NeutralSlate300] pl-4 py-2 mb-3 italic text-[--Text-Gray-700]">
|
||||
{children}
|
||||
</blockquote>
|
||||
),
|
||||
|
||||
|
||||
// Links
|
||||
a: ({ href, children }) => (
|
||||
<a
|
||||
href={href}
|
||||
target="_blank"
|
||||
<a
|
||||
href={href}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-[--Brand-Orange] underline hover:text-[--Brand-Orange]/80 transition-colors"
|
||||
>
|
||||
{children}
|
||||
</a>
|
||||
),
|
||||
|
||||
|
||||
// Tables
|
||||
table: ({ children }) => (
|
||||
<div className="overflow-x-auto mb-3">
|
||||
@@ -135,42 +135,42 @@ export const MarkdownRenderer: React.FC<MarkdownRendererProps> = ({ content, cla
|
||||
</table>
|
||||
</div>
|
||||
),
|
||||
|
||||
|
||||
thead: ({ children }) => (
|
||||
<thead className="bg-[--Neutrals-NeutralSlate100]">
|
||||
{children}
|
||||
</thead>
|
||||
),
|
||||
|
||||
|
||||
tbody: ({ children }) => (
|
||||
<tbody className="bg-[--Neutrals-NeutralSlate0]">
|
||||
{children}
|
||||
</tbody>
|
||||
),
|
||||
|
||||
|
||||
tr: ({ children }) => (
|
||||
<tr className="border-b border-[--Neutrals-NeutralSlate200] last:border-b-0">
|
||||
{children}
|
||||
</tr>
|
||||
),
|
||||
|
||||
|
||||
th: ({ children }) => (
|
||||
<th className="px-4 py-2 text-left text-sm font-semibold text-[--Neutrals-NeutralSlate950]">
|
||||
<th className="px-4 py-2 text-left text-sm font-semibold text-[--Text-Gray-950]">
|
||||
{children}
|
||||
</th>
|
||||
),
|
||||
|
||||
|
||||
td: ({ children }) => (
|
||||
<td className="px-4 py-2 text-sm text-[--Neutrals-NeutralSlate800]">
|
||||
<td className="px-4 py-2 text-sm text-[--Text-Gray-800]">
|
||||
{children}
|
||||
</td>
|
||||
),
|
||||
|
||||
|
||||
// Horizontal rule
|
||||
hr: () => (
|
||||
<hr className="border-[--Neutrals-NeutralSlate200] my-4" />
|
||||
),
|
||||
|
||||
|
||||
// Line breaks
|
||||
br: () => <br className="leading-relaxed" />,
|
||||
}}
|
||||
@@ -181,4 +181,4 @@ export const MarkdownRenderer: React.FC<MarkdownRendererProps> = ({ content, cla
|
||||
);
|
||||
};
|
||||
|
||||
export default MarkdownRenderer;
|
||||
export default MarkdownRenderer;
|
||||
|
||||
@@ -26,8 +26,15 @@ export const HelpIcon = ({ className }: { className?: string }) => (
|
||||
export const SettingsIcon = ({ className }: { className?: string }) => (
|
||||
<svg className={className} viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M19.14 12.94a2.001 2.001 0 0 0-2.28 0l-.71.71a2 2 0 0 0 0 2.83l.71.71a2 2 0 0 0 2.83 0l.71-.71a2 2 0 0 0 0-2.83l-.71-.71zM4.86 12.94a2 2 0 0 0 0 2.83l.71.71a2 2 0 0 0 2.83 0l.71-.71a2 2 0 0 0 0-2.83l-.71-.71a2 2 0 0 0-2.83 0zM12 10a2 2 0 1 0 0 4 2 2 0 0 0 0-4zm7.78-1.94a2 2 0 0 0-2.83 0l-.71.71a2 2 0 0 0 0 2.83l.71.71a2 2 0 0 0 2.83 0l.71-.71a2 2 0 0 0 0-2.83l-.71-.71zM4.86 5.22a2 2 0 0 0 0 2.83l.71.71a2 2 0 0 0 2.83 0l.71-.71a2 2 0 0 0 0-2.83l-.71-.71a2 2 0 0 0-2.83 0z" fill="currentColor" /></svg>
|
||||
);
|
||||
export const CopyIcon = ({ className }: { className?: string }) => (
|
||||
<svg className={className} viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M16 2H8a2 2 0 0 0-2 2v12h2V4h8v4h4v8h2V8l-4-4h-2z" fill="currentColor" /><path d="M4 8h12v12H4z" fill="currentColor" opacity="0.5" /></svg>
|
||||
export const CopyIcon = ({ className, scale }: { className?: string, scale?: number }) => (
|
||||
<svg width={16 * (scale || 1)} height={16 * (scale || 1)} viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5 15C4.06812 15 3.60218 15 3.23463 14.8478C2.74458 14.6448 2.35523 14.2554 2.15224 13.7654C2 13.3978 2 12.9319 2 12V5.2C2 4.0799 2 3.51984 2.21799 3.09202C2.40973 2.71569 2.71569 2.40973 3.09202 2.21799C3.51984 2 4.0799 2 5.2 2H12C12.9319 2 13.3978 2 13.7654 2.15224C14.2554 2.35523 14.6448 2.74458 14.8478 3.23463C15 3.60218 15 4.06812 15 5M12.2 22H18.8C19.9201 22 20.4802 22 20.908 21.782C21.2843 21.5903 21.5903 21.2843 21.782 20.908C22 20.4802 22 19.9201 22 18.8V12.2C22 11.0799 22 10.5198 21.782 10.092C21.5903 9.71569 21.2843 9.40973 20.908 9.21799C20.4802 9 19.9201 9 18.8 9H12.2C11.0799 9 10.5198 9 10.092 9.21799C9.71569 9.40973 9.40973 9.71569 9.21799 10.092C9 10.5198 9 11.0799 9 12.2V18.8C9 19.9201 9 20.4802 9.21799 20.908C9.40973 21.2843 9.71569 21.5903 10.092 21.782C10.5198 22 11.0799 22 12.2 22Z" stroke="white" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</svg>
|
||||
);
|
||||
export const LinkIcon = ({ className, scale }: { className?: string, scale?: number }) => (
|
||||
<svg width={17 * (scale || 1)} height={16 * (scale || 1)} viewBox="0 0 17 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8.97167 12.2423L8.02886 13.1851C6.72711 14.4868 4.61656 14.4868 3.31481 13.1851C2.01306 11.8834 2.01306 9.7728 3.31481 8.47106L4.25762 7.52825M12.7429 8.47106L13.6857 7.52825C14.9875 6.2265 14.9875 4.11595 13.6857 2.8142C12.384 1.51245 10.2734 1.51245 8.97167 2.8142L8.02886 3.75701M6.16693 10.333L10.8336 5.6663" stroke="var(--Other-White, white)" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" />
|
||||
</svg>
|
||||
);
|
||||
export const PlusIcon = ({ className }: { className?: string }) => (
|
||||
<svg className={className} viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z" fill="currentColor" /></svg>
|
||||
@@ -126,7 +133,7 @@ const Sidebar = () => {
|
||||
<aside className="w-64 flex-shrink-0 bg-[--sidebar-bg] border-r border-[--border-color] flex flex-col p-4">
|
||||
<div className="flex items-center mb-8">
|
||||
<div className="w-8 h-8 bg-[--accent] rounded-full flex items-center justify-center font-bold text-white text-lg mr-2">A</div>
|
||||
<h1 className="text-xl font-bold text-[--text-primary]">{org?.companyName || 'Auditly'}</h1>
|
||||
<h1 className="text-xl font-bold text-[--text-primary]">{org?.companyName || 'Orbitly'}</h1>
|
||||
</div>
|
||||
<nav className="flex-1">
|
||||
<ul className="space-y-2">
|
||||
@@ -139,7 +146,7 @@ const Sidebar = () => {
|
||||
</ul>
|
||||
<div className="p-4 rounded-lg bg-[--background-tertiary] text-center">
|
||||
<h3 className="font-bold text-sm text-[--text-primary]">Build [Company]'s Report</h3>
|
||||
<p className="text-xs text-[--text-secondary] mt-1 mb-3">Share this form with your team members to capture valuable info about your company to train Auditly.</p>
|
||||
<p className="text-xs text-[--text-secondary] mt-1 mb-3">Share this form with your team members to capture valuable info about your company to train Orbitly.</p>
|
||||
<div className="flex space-x-2">
|
||||
<Button size="sm" className="w-full" onClick={() => setShowInviteModal(true)}>
|
||||
<PlusIcon className="w-4 h-4 mr-1" /> Invite
|
||||
@@ -270,11 +277,13 @@ export const Layout = () => {
|
||||
const { org } = useOrg();
|
||||
|
||||
return (
|
||||
<div className="flex h-screen bg-[--Neutrals-NeutralSlate0]">
|
||||
<FigmaSidebar companyName={org?.companyName || "Auditly"} />
|
||||
<main className="flex-1 overflow-y-auto">
|
||||
<Outlet />
|
||||
</main>
|
||||
<div className="w-[100vw] h-[100vh] p-4 bg-[--Neutrals-NeutralSlate200] inline-flex justify-start items-start overflow-y-hidden">
|
||||
<div className="flex-1 h-full rounded-3xl self-stretch shadow-[0px_0px_15px_0px_rgba(0,0,0,0.08)] flex justify-between items-start overflow-y-auto relative">
|
||||
<FigmaSidebar companyName={org?.companyName || "Orbitly"} />
|
||||
<main className="flex-1 overflow-y-auto">
|
||||
<Outlet />
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -309,11 +318,11 @@ interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
||||
}
|
||||
|
||||
export const Button: React.FC<ButtonProps> = ({ children, variant = 'primary', size = 'md', className, ...props }) => {
|
||||
const baseClasses = 'inline-flex items-center justify-center font-semibold rounded-lg focus:outline-none transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed';
|
||||
const baseClasses = 'inline-flex items-center justify-center font-semibold rounded-[999px] focus:outline-none transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed';
|
||||
|
||||
const variantClasses = {
|
||||
primary: 'bg-[--Brand-Orange] text-[--accent-text] hover:bg-blue-400 focus:ring-[--accent-hover]',
|
||||
secondary: 'bg-[--button-secondary-bg] text-[--text-primary] hover:bg-[--button-secondary-hover] focus:ring-[--accent]',
|
||||
secondary: 'bg-[--Neutrals-NeutralSlate100] text-[--text-primary] hover:bg-[--button-secondary-hover] focus:ring-[--accent]',
|
||||
danger: 'bg-[--status-red] text-white hover:bg-red-700 focus:ring-red-500',
|
||||
ghost: 'bg-[--Neutrals-NeutralSlate100] text-[--text-primary] hover:bg-[--background-tertiary]'
|
||||
};
|
||||
@@ -363,4 +372,4 @@ export const Accordion = ({ items }: AccordionProps) => {
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
@@ -110,7 +110,7 @@ export const EnhancedFigmaQuestion: React.FC<FigmaQuestionProps> = ({
|
||||
{currentStep === 1 ?
|
||||
<div className="self-stretch inline-flex flex-col justify-start items-start gap-2">
|
||||
<div className="self-stretch inline-flex justify-start items-center gap-0.5">
|
||||
<div className="justify-start text-[--Neutrals-NeutralSlate950] text-sm font-normal font-['Inter'] leading-tight">Company Logo</div>
|
||||
<div className="justify-start text-[--Text-Gray-950] text-sm font-normal font-['Inter'] leading-tight">Company Logo</div>
|
||||
</div>
|
||||
<div className="self-stretch p-4 rounded-3xl outline outline-1 outline-offset-[-1px] outline-[--Neutrals-NeutralSlate200] inline-flex justify-start items-center gap-4">
|
||||
<ImageUpload
|
||||
@@ -129,7 +129,7 @@ export const EnhancedFigmaQuestion: React.FC<FigmaQuestionProps> = ({
|
||||
<path d="M14 2H2M12 8.66667L8 4.66667M8 4.66667L4 8.66667M8 4.66667V14" stroke="var(--Neutrals-NeutralSlate950, #FDFDFD)" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" />
|
||||
</svg>
|
||||
</div>
|
||||
<div className="justify-center text-[--Neutrals-NeutralSlate950] text-sm font-medium font-['Inter'] leading-tight">
|
||||
<div className="justify-center text-[--Text-Gray-950] text-sm font-medium font-['Inter'] leading-tight">
|
||||
{currentImage ? 'Change image' : 'Upload image'}
|
||||
</div>
|
||||
</div>
|
||||
@@ -138,7 +138,7 @@ export const EnhancedFigmaQuestion: React.FC<FigmaQuestionProps> = ({
|
||||
<div className="text-red-500 text-xs">{uploadError}</div>
|
||||
)}
|
||||
{currentImage && (
|
||||
<div className="text-[--Neutrals-NeutralSlate500] text-xs">
|
||||
<div className="text-[--Text-Gray-500] text-xs">
|
||||
{Math.round(currentImage.compressedSize / 1024)}KB • {currentImage.width}×{currentImage.height}px
|
||||
</div>
|
||||
)}
|
||||
@@ -146,7 +146,7 @@ export const EnhancedFigmaQuestion: React.FC<FigmaQuestionProps> = ({
|
||||
</div>
|
||||
</div>
|
||||
:
|
||||
<div className="self-stretch text-center justify-start text-[--Neutrals-NeutralSlate950] text-2xl font-medium font-['Neue_Montreal'] leading-normal">
|
||||
<div className="self-stretch text-center justify-start text-[--Text-Gray-950] text-2xl font-medium font-['Neue_Montreal'] leading-normal">
|
||||
{question}
|
||||
</div>
|
||||
}
|
||||
@@ -164,7 +164,7 @@ export const EnhancedFigmaQuestion: React.FC<FigmaQuestionProps> = ({
|
||||
onClick={!backDisabled ? onBack : undefined}
|
||||
>
|
||||
<div className="px-1 flex justify-center items-center">
|
||||
<div className="justify-center text-[--Neutrals-NeutralSlate950] text-sm font-medium font-['Inter'] leading-tight">
|
||||
<div className="justify-center text-[--Text-Gray-950] text-sm font-medium font-['Inter'] leading-tight">
|
||||
{backText}
|
||||
</div>
|
||||
</div>
|
||||
@@ -190,14 +190,14 @@ export const EnhancedFigmaQuestion: React.FC<FigmaQuestionProps> = ({
|
||||
|
||||
{/* Step indicator - top left */}
|
||||
<div className="px-3 py-1.5 left-[24px] top-[24px] absolute bg-[--Neutrals-NeutralSlate100] rounded-[50px] inline-flex justify-center items-center gap-2 overflow-hidden">
|
||||
<div className="justify-start text-[--Neutrals-NeutralSlate500] text-sm font-medium font-['Inter'] uppercase leading-none">
|
||||
<div className="justify-start text-[--Text-Gray-500] text-sm font-medium font-['Inter'] uppercase leading-none">
|
||||
{currentStep} of {totalSteps}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Skip button - top right */}
|
||||
<div className="px-3 py-1.5 left-[1363px] top-[24px] absolute bg-[--Neutrals-NeutralSlate100] rounded-[50px] inline-flex justify-center items-center gap-2 overflow-hidden cursor-pointer hover:bg-[--Neutrals-NeutralSlate200]">
|
||||
<div className="justify-start text-[--Neutrals-NeutralSlate500] text-sm font-medium font-['Inter'] leading-none">
|
||||
<div className="justify-start text-[--Text-Gray-500] text-sm font-medium font-['Inter'] leading-none">
|
||||
Skip
|
||||
</div>
|
||||
</div>
|
||||
@@ -207,7 +207,7 @@ export const EnhancedFigmaQuestion: React.FC<FigmaQuestionProps> = ({
|
||||
<div className="p-4 bg-[--Neutrals-NeutralSlate100] rounded-[50px] inline-flex justify-center items-center gap-2 overflow-hidden">
|
||||
{renderProgressDots()}
|
||||
</div>
|
||||
<div className="self-stretch text-center justify-start text-[--Neutrals-NeutralSlate500] text-base font-medium font-['Neue_Montreal'] leading-normal">
|
||||
<div className="self-stretch text-center justify-start text-[--Text-Gray-500] text-base font-medium font-['Neue_Montreal'] leading-normal">
|
||||
{stepTitle}
|
||||
</div>
|
||||
</div>
|
||||
@@ -244,11 +244,11 @@ export const FigmaQuestionCard: React.FC<FigmaQuestionCardProps> = ({
|
||||
// {/* <div className="self-stretch inline-flex justify-start items-start gap-3">
|
||||
// <div className="justify-start text-zinc-300 text-xl font-medium font-['Inter'] leading-loose">Q</div>
|
||||
// <div className="flex-1 inline-flex flex-col justify-start items-start gap-2">
|
||||
// <div className="self-stretch justify-start text-[--Neutrals-NeutralSlate950] text-xl font-semibold font-['Inter'] leading-loose">
|
||||
// <div className="self-stretch justify-start text-[--Text-Gray-950] text-xl font-semibold font-['Inter'] leading-loose">
|
||||
// {question}
|
||||
// </div>
|
||||
// {description && (
|
||||
// <div className="self-stretch justify-start text-[--Neutrals-NeutralSlate500] text-sm font-normal font-['Inter'] leading-tight">
|
||||
// <div className="self-stretch justify-start text-[--Text-Gray-500] text-sm font-normal font-['Inter'] leading-tight">
|
||||
// {description}
|
||||
// </div>
|
||||
// )}
|
||||
@@ -284,7 +284,7 @@ export const EnhancedFigmaInput: React.FC<EnhancedFigmaInputProps> = ({
|
||||
className = ""
|
||||
}) => {
|
||||
const baseClasses = "self-stretch min-h-40 p-5 relative bg-[--Neutrals-NeutralSlate50] rounded-xl inline-flex justify-start items-start gap-2.5 overflow-hidden";
|
||||
const inputClasses = "flex self-stretch w-100 text-[--Neutrals-NeutralSlate500] text-base font-normal font-['Inter'] leading-normal bg-transparent border-none outline-none resize-none";
|
||||
const inputClasses = "flex self-stretch w-100 text-[--Text-Gray-500] text-base font-normal font-['Inter'] leading-normal bg-transparent border-none outline-none resize-none";
|
||||
|
||||
if (multiline) {
|
||||
return (
|
||||
|
||||
@@ -27,9 +27,9 @@ export const FigmaAlert: React.FC<FigmaAlertProps> = ({
|
||||
|
||||
return (
|
||||
<div className={`p-4 relative bg-white rounded-lg shadow-[0px_2px_2px_-1px_rgba(10,13,18,0.04)] shadow-[0px_4px_6px_-2px_rgba(10,13,18,0.03)] shadow-[0px_12px_16px_-4px_rgba(10,13,18,0.08)] outline outline-1 outline-offset-[-1px] outline-[--Neutrals-NeutralSlate200] inline-flex justify-center items-center gap-2.5 overflow-hidden ${className}`}>
|
||||
<div className="w-96 max-w-96 justify-start text-[--Neutrals-NeutralSlate950] text-sm font-medium font-['Inter'] leading-tight">
|
||||
<div className="w-96 max-w-96 justify-start text-[--Text-Gray-950] text-sm font-medium font-['Inter'] leading-tight">
|
||||
{title}
|
||||
{children && <div className="mt-1 text-xs text-[--Neutrals-NeutralSlate600]">{children}</div>}
|
||||
{children && <div className="mt-1 text-xs text-[--Text-Gray-600]">{children}</div>}
|
||||
</div>
|
||||
|
||||
{onClose && (
|
||||
|
||||
99
src/components/figma/FigmaButton.tsx
Normal file
99
src/components/figma/FigmaButton.tsx
Normal file
@@ -0,0 +1,99 @@
|
||||
import React from 'react';
|
||||
|
||||
interface FigmaButtonProps {
|
||||
text?: string;
|
||||
onClick?: () => void;
|
||||
size?: 'tiny' | 'small' | 'big';
|
||||
grow?: boolean;
|
||||
disabled?: boolean;
|
||||
iconLeft?: React.ReactElement | null;
|
||||
iconRight?: React.ReactElement | null;
|
||||
buttonExtra?: string;
|
||||
containerExtra?: string;
|
||||
onInput?: () => void;
|
||||
extra?: React.ButtonHTMLAttributes<HTMLButtonElement>;
|
||||
}
|
||||
|
||||
export const FigmaPrimaryButton: React.FC<FigmaButtonProps> = ({
|
||||
text = 'Button',
|
||||
onClick,
|
||||
size = 'big',
|
||||
grow = true,
|
||||
disabled = false,
|
||||
iconLeft = null,
|
||||
iconRight = null,
|
||||
buttonExtra = '',
|
||||
containerExtra = '',
|
||||
onInput = null
|
||||
}) => {
|
||||
const sizeClasses = {
|
||||
tiny: 'px-4 py-1',
|
||||
small: 'px-4 py-2',
|
||||
big: 'px-4 py-3'
|
||||
};
|
||||
|
||||
const iconSize = {
|
||||
tiny: 16,
|
||||
small: 16,
|
||||
big: 20
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={`p-[0.1rem] ${grow ? 'flex-1 flex-grow' : ''} self-stretch justify-center flex rounded-[999px] gap-1 ${!!containerExtra && containerExtra}`} style={{ scale: '95%', background: 'var(--button-border-primary' }}>
|
||||
<button
|
||||
disabled={disabled}
|
||||
onClick={onClick}
|
||||
className={`text-white hover:bg-[--Brand-Orange]/90 bg-[--Brand-Orange] flex-grow ${sizeClasses[size]} rounded-[999px] inline-flex items-center justify-center transition-colors ${disabled ? 'disabled:bg-[--Neutrals-NeutralSlate100] disabled:cursor-not-allowed' : ''} ${!!buttonExtra && buttonExtra}`}
|
||||
>
|
||||
{iconLeft && <div className="relative items-center justify-center flex pr-1">{React.cloneElement(iconLeft, { 'size': iconSize[size] })}</div>}
|
||||
<div className="px-1 flex justify-center items-center">
|
||||
<div className="justify-center text-sm font-medium font-['Inter'] leading-tight">{text}</div>
|
||||
</div>
|
||||
{iconRight && <div className="relative items-center justify-center flex pl-1">{React.cloneElement(iconRight, { 'size': iconSize[size] })}</div>}
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export const FigmaSecondaryButton: React.FC<FigmaButtonProps> = ({
|
||||
text = 'Button',
|
||||
onClick,
|
||||
size = 'big',
|
||||
grow = true,
|
||||
disabled = false,
|
||||
iconLeft = null,
|
||||
iconRight = null,
|
||||
buttonExtra = '',
|
||||
containerExtra = '',
|
||||
onInput = null
|
||||
}) => {
|
||||
const sizeClasses = {
|
||||
tiny: 'px-4 py-1.5',
|
||||
small: 'px-4 py-2.5',
|
||||
big: 'px-4 py-3.5'
|
||||
};
|
||||
|
||||
const iconSize = {
|
||||
tiny: 16,
|
||||
small: 16,
|
||||
big: 20
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={`p-[0.1rem] ${grow ? 'flex-1 flex-grow' : ''} self-stretch justify-center flex rounded-[999px] gap-1`} style={{ scale: '95%', background: 'transparent' }}>
|
||||
<button
|
||||
disabled={disabled}
|
||||
onClick={onClick}
|
||||
className={`text-[--Neutrals-NeutralSlate950] flex-grow ${sizeClasses[size]} bg-[--Neutrals-NeutralSlate100] rounded-[999px] inline-flex items-center justify-center hover:bg-[--Neutrals-NeutralSlate100] transition-colors ${disabled ? 'disabled:bg-[--Neutrals-NeutralSlate100] disabled:cursor-not-allowed' : ''} ${!!buttonExtra && buttonExtra}`}
|
||||
>
|
||||
{iconLeft && <div className="relative items-center justify-center flex pr-1">{React.cloneElement(iconLeft, { 'size': iconSize[size] })}</div>}
|
||||
<div className="px-1 flex justify-center items-center">
|
||||
<div className="justify-center text-sm font-medium font-['Inter'] leading-tight">{text}</div>
|
||||
</div>
|
||||
{iconRight && <div className="relative items-center justify-center flex pl-1">{React.cloneElement(iconRight, { 'size': iconSize[size] })}</div>}
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default FigmaPrimaryButton;
|
||||
@@ -1,54 +1,54 @@
|
||||
import React, { useState } from 'react';
|
||||
|
||||
// Icon SVG Component - From Figma designs
|
||||
export const AuditlyIcon: React.FC = () => (
|
||||
export const OrbitlyIcon: React.FC = () => (
|
||||
<div data-svg-wrapper className="relative">
|
||||
<svg width="24" height="30" viewBox="0 0 24 30" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path opacity="0.5" fill-rule="evenodd" clip-rule="evenodd" d="M2.57408 17.8138C3.11835 18.3649 3.11834 19.2585 2.57406 19.8097L2.54619 19.8379C2.00191 20.389 1.11946 20.389 0.57519 19.8379C0.030919 19.2867 0.0309274 18.3931 0.575208 17.842L0.603083 17.8137C1.14736 17.2626 2.02981 17.2626 2.57408 17.8138Z" fill="url(#paint0_linear_984_10696)" />
|
||||
<path opacity="0.7" fill-rule="evenodd" clip-rule="evenodd" d="M9.12583 18.2374C9.66912 18.7896 9.66752 19.6832 9.12226 20.2333L5.2617 24.1286C4.71644 24.6787 3.83399 24.6771 3.2907 24.125C2.74741 23.5728 2.74901 22.6792 3.29427 22.1291L7.15483 18.2338C7.70009 17.6837 8.58254 17.6853 9.12583 18.2374Z" fill="url(#paint1_linear_984_10696)" />
|
||||
<path opacity="0.5" fill-rule="evenodd" clip-rule="evenodd" d="M14.3656 24.9431C14.7925 24.2945 15.6578 24.1193 16.2983 24.5516L16.3819 24.6081C17.0224 25.0404 17.1954 25.9167 16.7685 26.5652C16.3415 27.2138 15.4762 27.389 14.8357 26.9567L14.7521 26.9002C14.1117 26.4678 13.9386 25.5916 14.3656 24.9431Z" fill="url(#paint2_linear_984_10696)" />
|
||||
<path opacity="0.7" fill-rule="evenodd" clip-rule="evenodd" d="M23.5637 17.7278C24.108 18.279 24.108 19.1726 23.5637 19.7237L21.6683 21.6431C21.124 22.1943 20.2415 22.1943 19.6973 21.6431C19.153 21.092 19.153 20.1984 19.6973 19.6472L21.5927 17.7278C22.137 17.1767 23.0194 17.1767 23.5637 17.7278Z" fill="url(#paint3_linear_984_10696)" />
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M23.592 10.6302C24.1363 11.1813 24.1363 12.0749 23.592 12.6261L9.58526 26.8098C9.04098 27.361 8.15854 27.361 7.61426 26.8098C7.06999 26.2587 7.06999 25.3651 7.61426 24.8139L21.621 10.6302C22.1653 10.079 23.0477 10.079 23.592 10.6302Z" fill="url(#paint4_linear_984_10696)" />
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M20.9426 6.26883C21.4869 6.81999 21.4869 7.71359 20.9426 8.26474L12.887 16.4222C12.3427 16.9733 11.4603 16.9733 10.916 16.4222C10.3717 15.871 10.3717 14.9774 10.916 14.4263L18.9716 6.26883C19.5159 5.71768 20.3984 5.71768 20.9426 6.26883Z" fill="url(#paint5_linear_984_10696)" />
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M16.6918 3.50254C17.2364 4.05332 17.237 4.94692 16.6931 5.49844L6.33787 15.9987C5.79396 16.5502 4.91152 16.5508 4.36688 16C3.82224 15.4492 3.82164 14.5556 4.36555 14.0041L14.7208 3.50388C15.2647 2.95236 16.1471 2.95176 16.6918 3.50254Z" fill="url(#paint6_linear_984_10696)" />
|
||||
<path opacity="0.7" fill-rule="evenodd" clip-rule="evenodd" d="M6.43658 6.86235C6.97992 7.41445 6.9784 8.30805 6.43319 8.85825L2.37751 12.9511C1.83229 13.5013 0.94985 13.4997 0.406512 12.9476C-0.136826 12.3955 -0.135307 11.5019 0.409905 10.9517L4.46559 6.85892C5.0108 6.30871 5.89324 6.31025 6.43658 6.86235Z" fill="url(#paint7_linear_984_10696)" />
|
||||
<path opacity="0.5" fill-rule="evenodd" clip-rule="evenodd" d="M8.92007 2.7793C9.6898 2.7793 10.3138 3.41117 10.3138 4.19062V4.33175C10.3138 5.1112 9.6898 5.74307 8.92007 5.74307C8.15035 5.74307 7.52637 5.1112 7.52637 4.33175V4.19062C7.52637 3.41117 8.15035 2.7793 8.92007 2.7793Z" fill="url(#paint8_linear_984_10696)" />
|
||||
<path opacity="0.5" fill-rule="evenodd" clipRule="evenodd" d="M2.57408 17.8138C3.11835 18.3649 3.11834 19.2585 2.57406 19.8097L2.54619 19.8379C2.00191 20.389 1.11946 20.389 0.57519 19.8379C0.030919 19.2867 0.0309274 18.3931 0.575208 17.842L0.603083 17.8137C1.14736 17.2626 2.02981 17.2626 2.57408 17.8138Z" fill="url(#paint0_linear_984_10696)" />
|
||||
<path opacity="0.7" fill-rule="evenodd" clipRule="evenodd" d="M9.12583 18.2374C9.66912 18.7896 9.66752 19.6832 9.12226 20.2333L5.2617 24.1286C4.71644 24.6787 3.83399 24.6771 3.2907 24.125C2.74741 23.5728 2.74901 22.6792 3.29427 22.1291L7.15483 18.2338C7.70009 17.6837 8.58254 17.6853 9.12583 18.2374Z" fill="url(#paint1_linear_984_10696)" />
|
||||
<path opacity="0.5" fill-rule="evenodd" clipRule="evenodd" d="M14.3656 24.9431C14.7925 24.2945 15.6578 24.1193 16.2983 24.5516L16.3819 24.6081C17.0224 25.0404 17.1954 25.9167 16.7685 26.5652C16.3415 27.2138 15.4762 27.389 14.8357 26.9567L14.7521 26.9002C14.1117 26.4678 13.9386 25.5916 14.3656 24.9431Z" fill="url(#paint2_linear_984_10696)" />
|
||||
<path opacity="0.7" fill-rule="evenodd" clipRule="evenodd" d="M23.5637 17.7278C24.108 18.279 24.108 19.1726 23.5637 19.7237L21.6683 21.6431C21.124 22.1943 20.2415 22.1943 19.6973 21.6431C19.153 21.092 19.153 20.1984 19.6973 19.6472L21.5927 17.7278C22.137 17.1767 23.0194 17.1767 23.5637 17.7278Z" fill="url(#paint3_linear_984_10696)" />
|
||||
<path fill-rule="evenodd" clipRule="evenodd" d="M23.592 10.6302C24.1363 11.1813 24.1363 12.0749 23.592 12.6261L9.58526 26.8098C9.04098 27.361 8.15854 27.361 7.61426 26.8098C7.06999 26.2587 7.06999 25.3651 7.61426 24.8139L21.621 10.6302C22.1653 10.079 23.0477 10.079 23.592 10.6302Z" fill="url(#paint4_linear_984_10696)" />
|
||||
<path fill-rule="evenodd" clipRule="evenodd" d="M20.9426 6.26883C21.4869 6.81999 21.4869 7.71359 20.9426 8.26474L12.887 16.4222C12.3427 16.9733 11.4603 16.9733 10.916 16.4222C10.3717 15.871 10.3717 14.9774 10.916 14.4263L18.9716 6.26883C19.5159 5.71768 20.3984 5.71768 20.9426 6.26883Z" fill="url(#paint5_linear_984_10696)" />
|
||||
<path fill-rule="evenodd" clipRule="evenodd" d="M16.6918 3.50254C17.2364 4.05332 17.237 4.94692 16.6931 5.49844L6.33787 15.9987C5.79396 16.5502 4.91152 16.5508 4.36688 16C3.82224 15.4492 3.82164 14.5556 4.36555 14.0041L14.7208 3.50388C15.2647 2.95236 16.1471 2.95176 16.6918 3.50254Z" fill="url(#paint6_linear_984_10696)" />
|
||||
<path opacity="0.7" fill-rule="evenodd" clipRule="evenodd" d="M6.43658 6.86235C6.97992 7.41445 6.9784 8.30805 6.43319 8.85825L2.37751 12.9511C1.83229 13.5013 0.94985 13.4997 0.406512 12.9476C-0.136826 12.3955 -0.135307 11.5019 0.409905 10.9517L4.46559 6.85892C5.0108 6.30871 5.89324 6.31025 6.43658 6.86235Z" fill="url(#paint7_linear_984_10696)" />
|
||||
<path opacity="0.5" fill-rule="evenodd" clipRule="evenodd" d="M8.92007 2.7793C9.6898 2.7793 10.3138 3.41117 10.3138 4.19062V4.33175C10.3138 5.1112 9.6898 5.74307 8.92007 5.74307C8.15035 5.74307 7.52637 5.1112 7.52637 4.33175V4.19062C7.52637 3.41117 8.15035 2.7793 8.92007 2.7793Z" fill="url(#paint8_linear_984_10696)" />
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_984_10696" x1="1.57463" y1="17.4004" x2="1.57463" y2="20.2513" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="white" stop-opacity="0.8" />
|
||||
<stop offset="1" stop-color="white" stop-opacity="0.5" />
|
||||
<stop stopColor="white" stopOpacity="0.8" />
|
||||
<stop offset="1" stopColor="white" stopOpacity="0.5" />
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_984_10696" x1="6.20827" y1="17.8223" x2="6.20827" y2="24.5401" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="white" stop-opacity="0.8" />
|
||||
<stop offset="1" stop-color="white" stop-opacity="0.5" />
|
||||
<stop stopColor="white" stopOpacity="0.8" />
|
||||
<stop offset="1" stopColor="white" stopOpacity="0.5" />
|
||||
</linearGradient>
|
||||
<linearGradient id="paint2_linear_984_10696" x1="15.567" y1="24.3145" x2="15.567" y2="27.1938" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="white" stop-opacity="0.8" />
|
||||
<stop offset="1" stop-color="white" stop-opacity="0.5" />
|
||||
<stop stopColor="white" stopOpacity="0.8" />
|
||||
<stop offset="1" stopColor="white" stopOpacity="0.5" />
|
||||
</linearGradient>
|
||||
<linearGradient id="paint3_linear_984_10696" x1="21.6305" y1="17.3145" x2="21.6305" y2="22.0565" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="white" stop-opacity="0.8" />
|
||||
<stop offset="1" stop-color="white" stop-opacity="0.5" />
|
||||
<stop stopColor="white" stopOpacity="0.8" />
|
||||
<stop offset="1" stopColor="white" stopOpacity="0.5" />
|
||||
</linearGradient>
|
||||
<linearGradient id="paint4_linear_984_10696" x1="15.6031" y1="10.2168" x2="15.6031" y2="27.2232" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="white" stop-opacity="0.8" />
|
||||
<stop offset="1" stop-color="white" stop-opacity="0.5" />
|
||||
<stop stopColor="white" stopOpacity="0.8" />
|
||||
<stop offset="1" stopColor="white" stopOpacity="0.5" />
|
||||
</linearGradient>
|
||||
<linearGradient id="paint5_linear_984_10696" x1="15.9293" y1="5.85547" x2="15.9293" y2="16.8355" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="white" stop-opacity="0.8" />
|
||||
<stop offset="1" stop-color="white" stop-opacity="0.5" />
|
||||
<stop stopColor="white" stopOpacity="0.8" />
|
||||
<stop offset="1" stopColor="white" stopOpacity="0.5" />
|
||||
</linearGradient>
|
||||
<linearGradient id="paint6_linear_984_10696" x1="10.5293" y1="3.08984" x2="10.5293" y2="16.4127" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="white" stop-opacity="0.8" />
|
||||
<stop offset="1" stop-color="white" stop-opacity="0.5" />
|
||||
<stop stopColor="white" stopOpacity="0.8" />
|
||||
<stop offset="1" stopColor="white" stopOpacity="0.5" />
|
||||
</linearGradient>
|
||||
<linearGradient id="paint7_linear_984_10696" x1="3.42155" y1="6.44727" x2="3.42155" y2="13.3627" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="white" stop-opacity="0.8" />
|
||||
<stop offset="1" stop-color="white" stop-opacity="0.5" />
|
||||
<stop stopColor="white" stopOpacity="0.8" />
|
||||
<stop offset="1" stopColor="white" stopOpacity="0.5" />
|
||||
</linearGradient>
|
||||
<linearGradient id="paint8_linear_984_10696" x1="8.92007" y1="2.7793" x2="8.92007" y2="5.74307" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="white" stop-opacity="0.8" />
|
||||
<stop offset="1" stop-color="white" stop-opacity="0.5" />
|
||||
<stop stopColor="white" stopOpacity="0.8" />
|
||||
<stop offset="1" stopColor="white" stopOpacity="0.5" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
@@ -81,7 +81,7 @@ export const SectionProgressBar: React.FC<{ currentSection: number; totalSection
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div className="self-stretch text-center justify-start text-[--Neutrals-NeutralSlate500] text-base font-medium font-['Neue_Montreal'] leading-normal">
|
||||
<div className="self-stretch text-center justify-start text-[--Text-Gray-500] text-base font-medium font-['Neue_Montreal'] leading-normal">
|
||||
{sectionName}
|
||||
</div>
|
||||
</div>
|
||||
@@ -101,14 +101,14 @@ export const WelcomeScreen: React.FC<{
|
||||
<div className="w-12 h-12 relative bg-[--Brand-Orange] rounded-xl outline outline-2 outline-offset-[-2px] outline-blue-400 overflow-hidden">
|
||||
<div className="w-12 h-12 left-0 top-0 absolute bg-gradient-to-b from-white/0 to-white/10" />
|
||||
<div className="left-[12px] top-[9.33px] absolute">
|
||||
<AuditlyIcon />
|
||||
<OrbitlyIcon />
|
||||
</div>
|
||||
</div>
|
||||
<div className="self-stretch flex flex-col justify-start items-start gap-4">
|
||||
<div className="self-stretch justify-start text-[--Neutrals-NeutralSlate800] text-5xl font-medium font-['Neue_Montreal'] leading-[48px]">
|
||||
<div className="self-stretch justify-start text-[--Text-Gray-800] text-5xl font-medium font-['Neue_Montreal'] leading-[48px]">
|
||||
Welcome to the Internal Staff Survey
|
||||
</div>
|
||||
<div className="self-stretch justify-center text-[--Neutrals-NeutralSlate500] text-base font-normal font-['Inter'] leading-normal">
|
||||
<div className="self-stretch justify-center text-[--Text-Gray-500] text-base font-normal font-['Inter'] leading-normal">
|
||||
The survey takes around 15 minutes to complete.
|
||||
</div>
|
||||
</div>
|
||||
@@ -148,19 +148,19 @@ export const SectionIntro: React.FC<{
|
||||
<div className="w-12 h-12 relative bg-[--Brand-Orange] rounded-xl outline outline-2 outline-offset-[-2px] outline-blue-400 overflow-hidden">
|
||||
<div className="w-12 h-12 left-0 top-0 absolute bg-gradient-to-b from-white/0 to-white/10" />
|
||||
<div className="left-[12px] top-[9.33px] absolute">
|
||||
<AuditlyIcon />
|
||||
<OrbitlyIcon />
|
||||
</div>
|
||||
</div>
|
||||
<div className="self-stretch flex flex-col justify-start items-start gap-4">
|
||||
<div className="px-3 py-1.5 bg-[--Neutrals-NeutralSlate100] rounded-[50px] inline-flex justify-center items-center gap-2 overflow-hidden">
|
||||
<div className="justify-start text-[--Neutrals-NeutralSlate500] text-sm font-medium font-['Inter'] uppercase leading-none">
|
||||
<div className="justify-start text-[--Text-Gray-500] text-sm font-medium font-['Inter'] uppercase leading-none">
|
||||
{sectionNumber}
|
||||
</div>
|
||||
</div>
|
||||
<div className="self-stretch justify-start text-[--Neutrals-NeutralSlate800] text-5xl font-medium font-['Neue_Montreal'] leading-[48px]">
|
||||
<div className="self-stretch justify-start text-[--Text-Gray-800] text-5xl font-medium font-['Neue_Montreal'] leading-[48px]">
|
||||
{title}
|
||||
</div>
|
||||
<div className="self-stretch justify-center text-[--Neutrals-NeutralSlate500] text-base font-normal font-['Inter'] leading-normal">
|
||||
<div className="self-stretch justify-center text-[--Text-Gray-500] text-base font-normal font-['Inter'] leading-normal">
|
||||
{description}
|
||||
</div>
|
||||
</div>
|
||||
@@ -196,7 +196,7 @@ export const FormField: React.FC<{
|
||||
return (
|
||||
<div className="self-stretch flex flex-col justify-start items-start gap-2">
|
||||
<div className="self-stretch inline-flex justify-start items-center gap-0.5">
|
||||
<div className="justify-start text-[--Neutrals-NeutralSlate900] text-sm font-normal font-['Inter'] leading-tight">
|
||||
<div className="justify-start text-[--Text-Gray-900] text-sm font-normal font-['Inter'] leading-tight">
|
||||
{label}
|
||||
</div>
|
||||
{required && (
|
||||
@@ -209,7 +209,7 @@ export const FormField: React.FC<{
|
||||
type={type}
|
||||
value={value}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
className="flex-1 bg-transparent text-[--Neutrals-NeutralSlate950] text-sm font-normal font-['Inter'] leading-tight placeholder:text-[--Neutrals-NeutralSlate950] outline-none"
|
||||
className="flex-1 bg-transparent text-[--Text-Gray-950] text-sm font-normal font-['Inter'] leading-tight placeholder:text-[--Text-Gray-950] outline-none"
|
||||
placeholder={placeholder}
|
||||
/>
|
||||
</div>
|
||||
@@ -230,7 +230,7 @@ export const PersonalInfoForm: React.FC<{
|
||||
<div className="w-full self-stretch h-[810px] px-[488px] py-32 bg-[--Neutrals-NeutralSlate0] inline-flex flex-col justify-center items-center gap-9">
|
||||
<div className="w-full max-w-[464px] min-w-[464px] flex flex-col justify-start items-start gap-12">
|
||||
<div className="self-stretch flex flex-col justify-start items-start gap-8">
|
||||
<div className="self-stretch text-center justify-start text-[--Neutrals-NeutralSlate950] text-2xl font-medium font-['Neue_Montreal'] leading-normal">
|
||||
<div className="self-stretch text-center justify-start text-[--Text-Gray-950] text-2xl font-medium font-['Neue_Montreal'] leading-normal">
|
||||
Personal Information
|
||||
</div>
|
||||
<div className="self-stretch flex flex-col justify-start items-start gap-6">
|
||||
@@ -289,14 +289,14 @@ export const TextAreaQuestion: React.FC<{
|
||||
<div className="w-full h-full py-6 relative bg-[--Neutrals-NeutralSlate0] grid grid-cols-12 grid-rows-5 justify-center items-center gap-3">
|
||||
<div className="w-full max-w-[464px] min-w-[464px] flex flex-col row-start-3 col-span-12 self-center justify-self-center justify-center gap-12">
|
||||
<div className="self-stretch flex flex-col justify-start items-start gap-8">
|
||||
<div className="self-stretch text-center justify-start text-[--Neutrals-NeutralSlate950] text-2xl font-medium font-['Neue_Montreal'] leading-normal">
|
||||
<div className="self-stretch text-center justify-start text-[--Text-Gray-950] text-2xl font-medium font-['Neue_Montreal'] leading-normal">
|
||||
{question}
|
||||
</div>
|
||||
<div className="self-stretch min-h-40 p-5 relative bg-[--Neutrals-NeutralSlate100] rounded-xl inline-flex justify-start items-start gap-2.5">
|
||||
<textarea
|
||||
value={value}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
className="flex-1 bg-transparent text-[--Neutrals-NeutralSlate950] text-base font-normal font-['Inter'] leading-normal placeholder:text-[--Neutrals-NeutralSlate950] outline-none resize-none"
|
||||
className="flex-1 bg-transparent text-[--Text-Gray-950] text-base font-normal font-['Inter'] leading-normal placeholder:text-[--Text-Gray-950] outline-none resize-none"
|
||||
placeholder={placeholder}
|
||||
rows={6}
|
||||
/>
|
||||
@@ -313,7 +313,7 @@ export const TextAreaQuestion: React.FC<{
|
||||
className="h-12 px-8 py-3.5 bg-[--Neutrals-NeutralSlate100] rounded-[999px] flex justify-center items-center gap-1 overflow-hidden hover:bg-[--Neutrals-NeutralSlate200] transition-colors"
|
||||
>
|
||||
<div className="px-1 flex justify-center items-center">
|
||||
<div className="justify-center text-[--Neutrals-NeutralSlate950] text-sm font-medium font-['Inter'] leading-tight">Back</div>
|
||||
<div className="justify-center text-[--Text-Gray-950] text-sm font-medium font-['Inter'] leading-tight">Back</div>
|
||||
</div>
|
||||
</button>
|
||||
)}
|
||||
@@ -331,7 +331,7 @@ export const TextAreaQuestion: React.FC<{
|
||||
{/* Step indicator */}
|
||||
{currentStep && totalSteps && (
|
||||
<div className="px-3 py-1.5 left-[24px] top-[24px] absolute bg-[--Neutrals-NeutralSlate100] rounded-[50px] inline-flex justify-center items-center gap-2 overflow-hidden">
|
||||
<div className="justify-start text-[--Neutrals-NeutralSlate500] text-sm font-medium font-['Inter'] uppercase leading-none">
|
||||
<div className="justify-start text-[--Text-Gray-500] text-sm font-medium font-['Inter'] uppercase leading-none">
|
||||
{currentStep} of {totalSteps}
|
||||
</div>
|
||||
</div>
|
||||
@@ -343,7 +343,7 @@ export const TextAreaQuestion: React.FC<{
|
||||
onClick={onSkip}
|
||||
className="px-3 py-1.5 right-[24px] top-[24px] absolute bg-[--Neutrals-NeutralSlate100] rounded-[50px] inline-flex justify-center items-center gap-2 overflow-hidden hover:bg-[--Neutrals-NeutralSlate200] transition-colors"
|
||||
>
|
||||
<div className="justify-start text-[--Neutrals-NeutralSlate500] text-sm font-medium font-['Inter'] leading-none">Skip</div>
|
||||
<div className="justify-start text-[--Text-Gray-500] text-sm font-medium font-['Inter'] leading-none">Skip</div>
|
||||
</button>
|
||||
)}
|
||||
|
||||
@@ -378,11 +378,11 @@ export const RatingScaleQuestion: React.FC<{
|
||||
<div className="w-full h-full py-6 relative bg-[--Neutrals-NeutralSlate0] grid grid-cols-12 grid-rows-5 justify-center items-center gap-3">
|
||||
<div className="w-full max-w-[464px] min-w-[464px] flex flex-col row-start-3 col-span-12 self-center justify-self-center justify-center gap-12">
|
||||
<div className="self-stretch flex flex-col justify-center items-center gap-8">
|
||||
<div className="self-stretch text-center justify-start text-[--Neutrals-NeutralSlate950] text-2xl font-medium font-['Neue_Montreal'] leading-normal">
|
||||
<div className="self-stretch text-center justify-start text-[--Text-Gray-950] text-2xl font-medium font-['Neue_Montreal'] leading-normal">
|
||||
{question}
|
||||
</div>
|
||||
<div className="inline-flex justify-center w-max items-center gap-3">
|
||||
<div className="justify-center text-[--Neutrals-NeutralSlate950] text-sm font-medium font-['Inter'] leading-tight">
|
||||
<div className="justify-center text-[--Text-Gray-950] text-sm font-medium font-['Inter'] leading-tight">
|
||||
{leftLabel}
|
||||
</div>
|
||||
{Array.from({ length: scale }, (_, index) => {
|
||||
@@ -395,14 +395,14 @@ export const RatingScaleQuestion: React.FC<{
|
||||
className={`w-12 h-12 relative rounded-[576.35px] overflow-hidden transition-colors ${isSelected ? 'bg-[--Neutrals-NeutralSlate50]' : 'bg-[--Neutrals-NeutralSlate800] hover:bg-[--Neutrals-NeutralSlate50]'
|
||||
}`}
|
||||
>
|
||||
<div className={`absolute inset-0 flex items-center justify-center text-xl font-medium font-['Inter'] leading-7 ${isSelected ? 'text-[--Neutrals-NeutralSlate950] bg-[--Neutrals-NeutralSlate50]' : 'text-[--Neutrals-NeutralSlate0] hover:text-[--Neutrals-NeutralSlate800]'
|
||||
<div className={`absolute inset-0 flex items-center justify-center text-xl font-medium font-['Inter'] leading-7 ${isSelected ? 'text-[--Text-Gray-950] bg-[--Neutrals-NeutralSlate50]' : 'text-[--Text-Gray-950] hover:text-[--Text-Gray-950]'
|
||||
}`}>
|
||||
{ratingValue}
|
||||
</div>
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
<div className="justify-center text-[--Neutrals-NeutralSlate950] text-sm font-medium font-['Inter'] leading-tight">
|
||||
<div className="justify-center text-[--Text-Gray-950] text-sm font-medium font-['Inter'] leading-tight">
|
||||
{rightLabel}
|
||||
</div>
|
||||
</div>
|
||||
@@ -414,7 +414,7 @@ export const RatingScaleQuestion: React.FC<{
|
||||
className="h-12 px-8 py-3.5 bg-[--Neutrals-NeutralSlate100] rounded-[999px] flex justify-center items-center gap-1 overflow-hidden hover:bg-neutral-200 transition-colors"
|
||||
>
|
||||
<div className="px-1 flex justify-center items-center">
|
||||
<div className="justify-center text-[--Neutrals-NeutralSlate950] text-sm font-medium font-['Inter'] leading-tight">Back</div>
|
||||
<div className="justify-center text-[--Text-Gray-950] text-sm font-medium font-['Inter'] leading-tight">Back</div>
|
||||
</div>
|
||||
</button>
|
||||
)}
|
||||
@@ -433,7 +433,7 @@ export const RatingScaleQuestion: React.FC<{
|
||||
{/* Step indicator */}
|
||||
{currentStep && totalSteps && (
|
||||
<div className="px-3 py-1.5 left-[24px] top-[24px] absolute bg-[--Neutrals-NeutralSlate100] rounded-[50px] inline-flex justify-center items-center gap-2 overflow-hidden">
|
||||
<div className="justify-start text-[--Neutrals-NeutralSlate500] text-sm font-medium font-['Inter'] uppercase leading-none">
|
||||
<div className="justify-start text-[--Text-Gray-500] text-sm font-medium font-['Inter'] uppercase leading-none">
|
||||
{currentStep} of {totalSteps}
|
||||
</div>
|
||||
</div>
|
||||
@@ -445,7 +445,7 @@ export const RatingScaleQuestion: React.FC<{
|
||||
onClick={onSkip}
|
||||
className="px-3 py-1.5 right-[24px] top-[24px] absolute bg-[--Neutrals-NeutralSlate100] rounded-[50px] inline-flex justify-center items-center gap-2 overflow-hidden hover:bg-neutral-200 transition-colors"
|
||||
>
|
||||
<div className="justify-start text-[--Neutrals-NeutralSlate500] text-sm font-medium font-['Inter'] leading-none">Skip</div>
|
||||
<div className="justify-start text-[--Text-Gray-500] text-sm font-medium font-['Inter'] leading-none">Skip</div>
|
||||
</button>
|
||||
)}
|
||||
|
||||
@@ -477,7 +477,7 @@ export const YesNoChoice: React.FC<{
|
||||
<div className="w-full h-full py-6 relative bg-[--Neutrals-NeutralSlate0] grid grid-cols-12 grid-rows-5 justify-center items-center gap-3">
|
||||
<div className="w-full max-w-[464px] min-w-[464px] flex flex-col row-start-3 col-span-12 self-center justify-self-center justify-center gap-12">
|
||||
<div className="self-stretch flex flex-col justify-start items-start gap-8">
|
||||
<div className="self-stretch text-center justify-start text-[--Neutrals-NeutralSlate950] text-2xl font-medium font-['Neue_Montreal'] leading-normal">
|
||||
<div className="self-stretch text-center justify-start text-[--Text-Gray-950] text-2xl font-medium font-['Neue_Montreal'] leading-normal">
|
||||
{question}
|
||||
</div>
|
||||
<div className="self-stretch inline-flex justify-center items-center gap-3">
|
||||
@@ -486,7 +486,7 @@ export const YesNoChoice: React.FC<{
|
||||
className={`w-20 h-20 relative rounded-[999px] overflow-hidden transition-colors ${value === 'No' ? 'bg-[--Neutrals-NeutralSlate50]' : 'bg-[--Neutrals-NeutralSlate800] hover:bg-[--Neutrals-NeutralSlate50]'
|
||||
}`}
|
||||
>
|
||||
<div className={`absolute inset-0 flex items-center justify-center text-base font-normal font-['Inter'] leading-normal ${value === 'No' ? 'text-[--Neutrals-NeutralSlate950] bg-[--Neutrals-NeutralSlate50]' : 'text-[--Neutrals-NeutralSlate50] hover:bg-[--Neutrals-NeutralSlate50] hover:text-[--Neutrals-NeutralSlate950]'
|
||||
<div className={`absolute inset-0 flex items-center justify-center text-base font-normal font-['Inter'] leading-normal ${value === 'No' ? 'text-[--Text-Gray-950] bg-[--Neutrals-NeutralSlate50]' : 'text-[--Text-Gray-950] hover:bg-[--Neutrals-NeutralSlate50] hover:text-[--Text-Gray-950]'
|
||||
}`}>
|
||||
No
|
||||
</div>
|
||||
@@ -496,7 +496,7 @@ export const YesNoChoice: React.FC<{
|
||||
className={`w-20 h-20 relative rounded-[999px] overflow-hidden transition-colors ${value === 'Yes' ? 'bg-[--Neutrals-NeutralSlate50]' : 'bg-[--Neutrals-NeutralSlate800] hover:bg-[--Neutrals-NeutralSlate50]'
|
||||
}`}
|
||||
>
|
||||
<div className={`absolute inset-0 flex items-center justify-center text-base font-normal font-['Inter'] leading-normal ${value === 'Yes' ? 'text-[--Neutrals-NeutralSlate950] bg-[--Neutrals-NeutralSlate50]' : 'text-[--Neutrals-NeutralSlate50] hover:bg-[--Neutrals-NeutralSlate50] hover:text-[--Neutrals-NeutralSlate950]'
|
||||
<div className={`absolute inset-0 flex items-center justify-center text-base font-normal font-['Inter'] leading-normal ${value === 'Yes' ? 'text-[--Text-Gray-950] bg-[--Neutrals-NeutralSlate50]' : 'text-[--Text-Gray-950] hover:bg-[--Neutrals-NeutralSlate50] hover:text-[--Text-Gray-950]'
|
||||
}`}>
|
||||
Yes
|
||||
</div>
|
||||
@@ -510,7 +510,7 @@ export const YesNoChoice: React.FC<{
|
||||
className="h-12 px-8 py-3.5 bg-[--Neutrals-NeutralSlate100] rounded-[999px] flex justify-center items-center gap-1 overflow-hidden hover:bg-neutral-200 transition-colors"
|
||||
>
|
||||
<div className="px-1 flex justify-center items-center">
|
||||
<div className="justify-center text-[--Neutrals-NeutralSlate950] text-sm font-medium font-['Inter'] leading-tight">Back</div>
|
||||
<div className="justify-center text-[--Text-Gray-950] text-sm font-medium font-['Inter'] leading-tight">Back</div>
|
||||
</div>
|
||||
</button>
|
||||
)}
|
||||
@@ -529,7 +529,7 @@ export const YesNoChoice: React.FC<{
|
||||
{/* Step indicator */}
|
||||
{currentStep && totalSteps && (
|
||||
<div className="px-3 py-1.5 left-[24px] top-[24px] absolute bg-[--Neutrals-NeutralSlate100] rounded-[50px] inline-flex justify-center items-center gap-2 overflow-hidden">
|
||||
<div className="justify-start text-[--Neutrals-NeutralSlate500] text-sm font-medium font-['Inter'] uppercase leading-none">
|
||||
<div className="justify-start text-[--Text-Gray-500] text-sm font-medium font-['Inter'] uppercase leading-none">
|
||||
{currentStep} of {totalSteps}
|
||||
</div>
|
||||
</div>
|
||||
@@ -541,7 +541,7 @@ export const YesNoChoice: React.FC<{
|
||||
onClick={onSkip}
|
||||
className="px-3 py-1.5 right-[24px] top-[24px] absolute bg-[--Neutrals-NeutralSlate100] rounded-[50px] inline-flex justify-center items-center gap-2 overflow-hidden hover:bg-neutral-200 transition-colors"
|
||||
>
|
||||
<div className="justify-start text-[--Neutrals-NeutralSlate500] text-sm font-medium font-['Inter'] leading-none">Skip</div>
|
||||
<div className="justify-start text-[--Text-Gray-500] text-sm font-medium font-['Inter'] leading-none">Skip</div>
|
||||
</button>
|
||||
)}
|
||||
|
||||
@@ -567,14 +567,14 @@ export const ThankYouPage: React.FC = () => {
|
||||
<div className="w-12 h-12 relative bg-[--Brand-Orange] rounded-xl outline outline-2 outline-offset-[-2px] outline-blue-400 overflow-hidden">
|
||||
<div className="w-12 h-12 left-0 top-0 absolute bg-gradient-to-b from-white/0 to-white/10" />
|
||||
<div className="left-[12px] top-[9.33px] absolute">
|
||||
<AuditlyIcon />
|
||||
<OrbitlyIcon />
|
||||
</div>
|
||||
</div>
|
||||
<div className="self-stretch flex flex-col justify-start items-start gap-4">
|
||||
<div className="self-stretch justify-start text-[--Neutrals-NeutralSlate800] text-5xl font-medium font-['Neue_Montreal'] leading-[48px]">
|
||||
<div className="self-stretch justify-start text-[--Text-Gray-800] text-5xl font-medium font-['Neue_Montreal'] leading-[48px]">
|
||||
Thank you your form has been submitted!
|
||||
</div>
|
||||
<div className="self-stretch justify-center text-[--Neutrals-NeutralSlate500] text-base font-normal font-['Inter'] leading-normal">
|
||||
<div className="self-stretch justify-center text-[--Text-Gray-500] text-base font-normal font-['Inter'] leading-normal">
|
||||
Your responses have been recorded and your AI-powered performance report will be generated shortly.
|
||||
</div>
|
||||
</div>
|
||||
@@ -588,4 +588,4 @@ export const ThankYouPage: React.FC = () => {
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
@@ -31,7 +31,7 @@ export const FigmaInput: React.FC<FigmaInputProps> = ({
|
||||
<div className={`self-stretch flex flex-col justify-start items-start gap-2 ${className}`}>
|
||||
{showLabel && label && (
|
||||
<div className="self-stretch inline-flex justify-start items-center gap-0.5">
|
||||
<div className="justify-start text-[--Neutrals-NeutralSlate900] text-sm font-normal font-['Inter'] leading-tight">
|
||||
<div className="justify-start text-[--Text-Gray-900] text-sm font-normal font-['Inter'] leading-tight">
|
||||
{label}
|
||||
</div>
|
||||
{required && (
|
||||
@@ -52,7 +52,7 @@ export const FigmaInput: React.FC<FigmaInputProps> = ({
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
placeholder={placeholder}
|
||||
className="flex-1 justify-start text-[--Neutrals-NeutralSlate950] text-sm font-normal font-['Inter'] leading-tight bg-transparent border-none outline-none placeholder:text-[--Neutrals-NeutralSlate500]"
|
||||
className="flex-1 justify-start text-[--Text-Gray-950] text-sm font-normal font-['Inter'] leading-tight bg-transparent border-none outline-none placeholder:text-[--Text-Gray-950]"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -62,7 +62,7 @@ export const FigmaInput: React.FC<FigmaInputProps> = ({
|
||||
onClick={onButtonClick}
|
||||
className="w-32 max-w-32 px-4 py-3.5 bg-[--Neutrals-NeutralSlate100] rounded-[999px] flex justify-center items-center gap-1 overflow-hidden hover:bg-[--Neutrals-NeutralSlate200] transition-colors"
|
||||
>
|
||||
<div className="justify-center text-[--Neutrals-NeutralSlate500] text-sm font-medium font-['Inter'] leading-tight">
|
||||
<div className="justify-center text-[--Text-Gray-500] text-sm font-medium font-['Inter'] leading-tight">
|
||||
{buttonText}
|
||||
</div>
|
||||
</button>
|
||||
@@ -95,7 +95,7 @@ export const FigmaSelect: React.FC<FigmaSelectProps> = ({
|
||||
<div className={`self-stretch flex flex-col justify-start items-start gap-2 ${className}`}>
|
||||
{label && (
|
||||
<div className="self-stretch inline-flex justify-start items-center gap-0.5">
|
||||
<div className="justify-start text-[--Neutrals-NeutralSlate900] text-sm font-normal font-['Inter'] leading-tight">
|
||||
<div className="justify-start text-[--Text-Gray-900] text-sm font-normal font-['Inter'] leading-tight">
|
||||
{label}
|
||||
</div>
|
||||
{required && (
|
||||
@@ -109,9 +109,9 @@ export const FigmaSelect: React.FC<FigmaSelectProps> = ({
|
||||
<select
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
className="flex-1 justify-start text-[--Neutrals-NeutralSlate950] text-sm font-normal font-['Inter'] leading-tight bg-transparent border-none outline-none appearance-none"
|
||||
className="flex-1 justify-start text-[--Text-Gray-950] text-sm font-normal font-['Inter'] leading-tight bg-transparent border-none outline-none appearance-none"
|
||||
>
|
||||
<option value="" className="text-[--Neutrals-NeutralSlate500]">{placeholder}</option>
|
||||
<option value="" className="text-[--Text-Gray-500]">{placeholder}</option>
|
||||
{options.map((option, index) => (
|
||||
<option key={index} value={option.value}>
|
||||
{option.label}
|
||||
|
||||
@@ -27,8 +27,8 @@ export const FigmaMultipleChoice: React.FC<FigmaMultipleChoiceProps> = ({
|
||||
onClick={() => onSelect?.(option)}
|
||||
>
|
||||
<div className={`absolute inset-0 flex items-center justify-center text-center text-base font-normal font-['Inter'] leading-normal ${isSelected
|
||||
? 'text-[--Neutrals-NeutralSlate0]'
|
||||
: 'text-[--Neutrals-NeutralSlate950]'
|
||||
? 'text-[--Text-Gray-0]'
|
||||
: 'text-[--Text-Gray-950]'
|
||||
}`}>
|
||||
{option}
|
||||
</div>
|
||||
@@ -39,4 +39,4 @@ export const FigmaMultipleChoice: React.FC<FigmaMultipleChoiceProps> = ({
|
||||
);
|
||||
};
|
||||
|
||||
export default FigmaMultipleChoice;
|
||||
export default FigmaMultipleChoice;
|
||||
|
||||
@@ -23,12 +23,12 @@ const ProgressStep: React.FC<ProgressStepProps> = ({ number, title, isActive = f
|
||||
: "h-5 p-0.5 bg-[--Neutrals-NeutralSlate0] rounded-[999px] outline outline-1 outline-offset-[-1px] outline-[--Neutrals-NeutralSlate200]";
|
||||
|
||||
const numberTextClasses = isActive || isCompleted
|
||||
? "w-4 text-center justify-start text-[--Neutrals-NeutralSlate0] text-xs font-medium font-['Inter'] leading-none"
|
||||
: "w-4 text-center justify-start text-[--Neutrals-NeutralSlate600] text-xs font-medium font-['Inter'] leading-none";
|
||||
? "w-4 text-center justify-start text-[--Text-Gray-0] text-xs font-medium font-['Inter'] leading-none"
|
||||
: "w-4 text-center justify-start text-[--Text-Gray-600] text-xs font-medium font-['Inter'] leading-none";
|
||||
|
||||
const titleClasses = isActive
|
||||
? "flex-1 justify-start text-[--Neutrals-NeutralSlate950] text-sm font-normal font-['Inter'] leading-tight"
|
||||
: "flex-1 justify-start text-[--Neutrals-NeutralSlate600] text-sm font-normal font-['Inter'] leading-tight";
|
||||
? "flex-1 justify-start text-[--Text-Gray-950] text-sm font-normal font-['Inter'] leading-tight"
|
||||
: "flex-1 justify-start text-[--Text-Gray-600] text-sm font-normal font-['Inter'] leading-tight";
|
||||
|
||||
return (
|
||||
<div className={`self-stretch inline-flex justify-start items-center gap-2.5 overflow-hidden ${stepClasses}`}>
|
||||
|
||||
@@ -28,18 +28,18 @@ export const FigmaQuestion: React.FC<FigmaQuestionProps> = ({
|
||||
className = ''
|
||||
}) => {
|
||||
return (
|
||||
<div className={`w-[600px] px-5 pt-5 pb-6 bg-white rounded-2xl outline outline-1 outline-offset-[-1px] outline-[--$1] inline-flex flex-col justify-end items-end gap-4 ${className}`}>
|
||||
<div className={`w-[600px] px-5 pt-5 pb-6 bg-white rounded-2xl outline outline-1 outline-offset-[-1px] outline-Neutrals-NeutralSlate200 inline-flex flex-col justify-end items-end gap-4 ${className}`}>
|
||||
{/* Question Header */}
|
||||
<div className="self-stretch inline-flex justify-start items-start gap-3">
|
||||
<div className="justify-start text-zinc-300 text-xl font-medium font-['Inter'] leading-loose">
|
||||
{questionNumber}
|
||||
</div>
|
||||
<div className="flex-1 inline-flex flex-col justify-start items-start gap-2">
|
||||
<div className="self-stretch justify-start text-[--Neutrals-NeutralSlate950] text-xl font-semibold font-['Inter'] leading-loose">
|
||||
<div className="self-stretch justify-start text-[--Text-Gray-950] text-xl font-semibold font-['Inter'] leading-loose">
|
||||
{title}
|
||||
</div>
|
||||
{description && (
|
||||
<div className="self-stretch justify-start text-[--Neutrals-NeutralSlate500] text-sm font-normal font-['Inter'] leading-tight">
|
||||
<div className="self-stretch justify-start text-[--Text-Gray-500] text-sm font-normal font-['Inter'] leading-tight">
|
||||
{description}
|
||||
</div>
|
||||
)}
|
||||
@@ -75,7 +75,7 @@ export const FigmaQuestion: React.FC<FigmaQuestionProps> = ({
|
||||
value={answer}
|
||||
onChange={(e) => onAnswerChange?.(e.target.value)}
|
||||
placeholder="Type your answer...."
|
||||
className="w-full bg-transparent outline-none resize-none text-[--Neutrals-NeutralSlate950] text-base font-normal font-['Inter'] leading-normal placeholder:text-[--Neutrals-NeutralSlate950] min-h-[100px]"
|
||||
className="w-full bg-transparent outline-none resize-none text-[--Text-Gray-950] text-base font-normal font-['Inter'] leading-normal placeholder:text-[--Text-Gray-950] min-h-[100px]"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -94,7 +94,7 @@ export const FigmaQuestion: React.FC<FigmaQuestionProps> = ({
|
||||
</svg>
|
||||
</div>
|
||||
<div className="px-1 flex justify-center items-center">
|
||||
<div className="justify-center text-[--Neutrals-NeutralSlate950] text-sm font-medium font-['Inter'] leading-tight">
|
||||
<div className="justify-center text-[--Text-Gray-950] text-sm font-medium font-['Inter'] leading-tight">
|
||||
{backLabel}
|
||||
</div>
|
||||
</div>
|
||||
@@ -157,9 +157,9 @@ export const FigmaRatingScale: React.FC<{
|
||||
<div className="w-[1440px] h-[810px] py-6 relative bg-[--Neutrals-NeutralSlate0] inline-flex flex-col justify-center items-center gap-9">
|
||||
<div className="w-full max-w-[464px] min-w-[464px] flex flex-col justify-center items-center gap-12">
|
||||
<div className="flex flex-col justify-center items-center gap-8">
|
||||
<div className="self-stretch text-center justify-start text-[--Neutrals-NeutralSlate950] text-2xl font-medium font-['Neue_Montreal'] leading-normal">{question}</div>
|
||||
<div className="self-stretch text-center justify-start text-[--Text-Gray-950] text-2xl font-medium font-['Neue_Montreal'] leading-normal">{question}</div>
|
||||
<div className="inline-flex justify-center items-center gap-3">
|
||||
<div className="justify-center text-[--Neutrals-NeutralSlate950] text-sm font-medium font-['Inter'] leading-tight">{leftLabel}</div>
|
||||
<div className="justify-center text-[--Text-Gray-950] text-sm font-medium font-['Inter'] leading-tight">{leftLabel}</div>
|
||||
{Array.from({ length: scale }, (_, index) => {
|
||||
const number = index + 1;
|
||||
const isSelected = value === number;
|
||||
@@ -170,14 +170,14 @@ export const FigmaRatingScale: React.FC<{
|
||||
className={`w-12 h-12 relative rounded-[576.35px] overflow-hidden cursor-pointer transition-colors ${isSelected ? 'bg-[--Brand-Orange]' : 'bg-[--Neutrals-NeutralSlate100] hover:bg-[--Neutrals-NeutralSlate200]'
|
||||
}`}
|
||||
>
|
||||
<div className={`absolute inset-0 flex items-center justify-center text-xl font-medium font-['Inter'] leading-7 ${isSelected ? 'text-white' : 'text-[--Neutrals-NeutralSlate950]'
|
||||
<div className={`absolute inset-0 flex items-center justify-center text-xl font-medium font-['Inter'] leading-7 ${isSelected ? 'text-white' : 'text-[--Text-Gray-950]'
|
||||
}`}>
|
||||
{number}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
<div className="justify-center text-[--Neutrals-NeutralSlate950] text-sm font-medium font-['Inter'] leading-tight">{rightLabel}</div>
|
||||
<div className="justify-center text-[--Text-Gray-950] text-sm font-medium font-['Inter'] leading-tight">{rightLabel}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -196,18 +196,18 @@ export const FigmaTextArea: React.FC<{
|
||||
<div className="w-[1440px] h-[810px] py-6 relative bg-[--Neutrals-NeutralSlate0] inline-flex flex-col justify-center items-center gap-9">
|
||||
<div className="w-full max-w-[464px] min-w-[464px] flex flex-col justify-start items-start gap-12">
|
||||
<div className="self-stretch flex flex-col justify-start items-start gap-8">
|
||||
<div className="self-stretch text-center justify-start text-[--Neutrals-NeutralSlate950] text-2xl font-medium font-['Neue_Montreal'] leading-normal">{question}</div>
|
||||
<div className="self-stretch text-center justify-start text-[--Text-Gray-950] text-2xl font-medium font-['Neue_Montreal'] leading-normal">{question}</div>
|
||||
<div className="self-stretch min-h-40 p-5 relative bg-[--Neutrals-NeutralSlate100] rounded-xl inline-flex justify-start items-start gap-2.5">
|
||||
<textarea
|
||||
value={value}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
className="flex-1 bg-transparent text-[--Neutrals-NeutralSlate950] text-base font-normal font-['Inter'] leading-normal placeholder:text-[--Neutrals-NeutralSlate950] outline-none resize-none"
|
||||
className="flex-1 bg-transparent text-[--Text-Gray-950] text-base font-normal font-['Inter'] leading-normal placeholder:text-[--Text-Gray-950] outline-none resize-none"
|
||||
placeholder={placeholder}
|
||||
rows={6}
|
||||
/>
|
||||
<div className="w-3 h-3 absolute right-5 bottom-5">
|
||||
<div className="w-2 h-2 absolute top-0.5 left-0.5 outline outline-1 outline-offset-[-0.50px] outline-[--$1]" />
|
||||
<div className="w-1 h-1 absolute bottom-0 right-0 outline outline-1 outline-offset-[-0.50px] outline-[--$1]" />
|
||||
<div className="w-2 h-2 absolute top-0.5 left-0.5 outline outline-1 outline-offset-[-0.50px] outline-[--Neutrals-NeutralSlate500]" />
|
||||
<div className="w-1 h-1 absolute bottom-0 right-0 outline outline-1 outline-offset-[-0.50px] outline-[--Neutrals-NeutralSlate500]" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -230,7 +230,7 @@ export const FigmaNavigationButtons: React.FC<{
|
||||
{/* Progress indicator */}
|
||||
{currentStep && totalSteps && (
|
||||
<div className="px-3 py-1.5 left-[24px] top-[24px] absolute bg-[--Neutrals-NeutralSlate100] rounded-[50px] inline-flex justify-center items-center gap-2 overflow-hidden">
|
||||
<div className="justify-start text-[--Neutrals-NeutralSlate500] text-sm font-medium font-['Inter'] uppercase leading-none">{currentStep} of {totalSteps}</div>
|
||||
<div className="justify-start text-[--Text-Gray-500] text-sm font-medium font-['Inter'] uppercase leading-none">{currentStep} of {totalSteps}</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -240,7 +240,7 @@ export const FigmaNavigationButtons: React.FC<{
|
||||
onClick={onSkip}
|
||||
className="px-3 py-1.5 left-[1363px] top-[24px] absolute bg-[--Neutrals-NeutralSlate100] rounded-[50px] inline-flex justify-center items-center gap-2 overflow-hidden cursor-pointer hover:bg-[--Neutrals-NeutralSlate100]"
|
||||
>
|
||||
<div className="justify-start text-[--Neutrals-NeutralSlate500] text-sm font-medium font-['Inter'] leading-none">Skip</div>
|
||||
<div className="justify-start text-[--Text-Gray-500] text-sm font-medium font-['Inter'] leading-none">Skip</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -259,7 +259,7 @@ export const FigmaNavigationButtons: React.FC<{
|
||||
className="h-12 px-8 py-3.5 bg-[--Neutrals-NeutralSlate100] rounded-[999px] flex justify-center items-center gap-1 overflow-hidden hover:bg-[--Neutrals-NeutralSlate100]"
|
||||
>
|
||||
<div className="px-1 flex justify-center items-center">
|
||||
<div className="justify-center text-[--Neutrals-NeutralSlate950] text-sm font-medium font-['Inter'] leading-tight">Back</div>
|
||||
<div className="justify-center text-[--Text-Gray-950] text-sm font-medium font-['Inter'] leading-tight">Back</div>
|
||||
</div>
|
||||
</button>
|
||||
)}
|
||||
|
||||
@@ -162,35 +162,35 @@ export default function Sidebar({ companyName = "Zitlac Media", collapsed = fals
|
||||
navigate(path);
|
||||
};
|
||||
return (
|
||||
<div className="h-full w-64 max-w-64 min-w-64 px-3 pt-4 pb-3 bg-[--Neutrals-NeutralSlate0] border-r border-[--Neutrals-NeutralSlate200] inline-flex flex-col justify-between items-center overflow-hidden">
|
||||
<div className="h-full sticky top-0 w-64 max-w-64 min-w-64 px-3 pr-5 pt-4 pb-3 bg-[--Neutrals-NeutralSlate0] border-r border-[--Neutrals-NeutralSlate200] inline-flex flex-col justify-between items-center overflow-y-auto">
|
||||
{/* Header Section */}
|
||||
<div className="self-stretch flex flex-col justify-start items-start gap-5">
|
||||
{/* Company Selector Dropdown */}
|
||||
<div className="relative w-60" ref={dropdownRef}>
|
||||
<div className="relative w-full" ref={dropdownRef}>
|
||||
<div
|
||||
className="w-60 pl-2 pr-4 py-2 bg-[--Neutrals-NeutralSlate0] rounded-3xl outline outline-1 outline-offset-[-1px] outline-[--Neutrals-NeutralSlate200] inline-flex justify-between items-center overflow-hidden cursor-pointer hover:bg-[--Neutrals-NeutralSlate50]"
|
||||
className="self-stretch w-full pl-2 pr-4 py-2 bg-[--Neutrals-NeutralSlate0] rounded-3xl outline outline-1 outline-offset-[-1px] outline-[--Neutrals-NeutralSlate200] inline-flex justify-between items-center overflow-hidden cursor-pointer hover:bg-[--Neutrals-NeutralSlate50]"
|
||||
onClick={() => refreshOrganizations() && setShowOrgDropdown(!showOrgDropdown)}
|
||||
>
|
||||
<div className="flex-1 flex justify-start items-center gap-2">
|
||||
<div className="w-8 h-8 rounded-full flex justify-start items-center gap-2.5">
|
||||
<div className="w-8 h-8 relative bg-[--Brand-Orange] rounded-full outline outline-[1.60px] outline-offset-[-1.60px] outline-white/10 overflow-hidden">
|
||||
<div className="w-8 h-8 left-0 top-0 absolute bg-gradient-to-b from-white/0 to-white/10" />
|
||||
<div data-svg-wrapper className="left-[7px] top-[7px] absolute">
|
||||
<div className="w-8 h-8 left-0 top-0 absolute bg-gradient-to-b from-[#66B2FF] to-[#0073E5]" />
|
||||
<div data-svg-wrapper className="left-[6.4px] top-[6.9px] absolute">
|
||||
<svg width="20" height="21" viewBox="0 0 20 21" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g filter="url(#filter0_d_1141_1906)">
|
||||
<path opacity="0.5" fill-rule="evenodd" clip-rule="evenodd" d="M4.34354 10.6855C4.67011 11.0162 4.6701 11.5524 4.34353 11.8831L4.32681 11.9C4.00024 12.2307 3.47077 12.2307 3.14421 11.9C2.81765 11.5693 2.81765 11.0331 3.14422 10.7024L3.16095 10.6855C3.48751 10.3548 4.01698 10.3548 4.34354 10.6855Z" fill="url(#paint0_linear_1141_1906)" />
|
||||
<path opacity="0.7" fill-rule="evenodd" clip-rule="evenodd" d="M8.27545 10.9405C8.60142 11.2718 8.60046 11.808 8.27331 12.1381L5.95697 14.4752C5.62981 14.8053 5.10035 14.8043 4.77437 14.473C4.4484 14.1417 4.44936 13.6056 4.77651 13.2755L7.09285 10.9383C7.42001 10.6082 7.94947 10.6092 8.27545 10.9405Z" fill="url(#paint1_linear_1141_1906)" />
|
||||
<path opacity="0.5" fill-rule="evenodd" clip-rule="evenodd" d="M11.4179 14.9651C11.6741 14.5759 12.1932 14.4708 12.5775 14.7302L12.6277 14.7641C13.012 15.0235 13.1158 15.5492 12.8596 15.9384C12.6034 16.3275 12.0842 16.4326 11.7 16.1732L11.6498 16.1393C11.2655 15.8799 11.1617 15.3542 11.4179 14.9651Z" fill="url(#paint2_linear_1141_1906)" />
|
||||
<path opacity="0.7" fill-rule="evenodd" clip-rule="evenodd" d="M16.9375 10.6347C17.264 10.9654 17.264 11.5016 16.9375 11.8323L15.8002 12.9839C15.4736 13.3146 14.9442 13.3146 14.6176 12.9839C14.291 12.6532 14.291 12.1171 14.6176 11.7864L15.7549 10.6347C16.0814 10.304 16.6109 10.304 16.9375 10.6347Z" fill="url(#paint3_linear_1141_1906)" />
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M16.9542 6.37693C17.2808 6.70762 17.2808 7.24378 16.9542 7.57447L8.5502 16.0847C8.22364 16.4154 7.69417 16.4154 7.3676 16.0847C7.04104 15.754 7.04104 15.2179 7.3676 14.8872L15.7717 6.37693C16.0982 6.04623 16.6277 6.04623 16.9542 6.37693Z" fill="url(#paint4_linear_1141_1906)" />
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M15.3649 3.75974C15.6915 4.09043 15.6915 4.62659 15.3649 4.95728L10.5315 9.85174C10.205 10.1824 9.67549 10.1824 9.34893 9.85174C9.02236 9.52104 9.02236 8.98489 9.34893 8.65419L14.1823 3.75974C14.5089 3.42905 15.0383 3.42905 15.3649 3.75974Z" fill="url(#paint5_linear_1141_1906)" />
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.8146 2.09918C13.1414 2.42965 13.1417 2.96581 12.8154 3.29672L6.60224 9.59685C6.27589 9.92777 5.74642 9.92813 5.41964 9.59766C5.09285 9.26719 5.0925 8.73103 5.41884 8.40011L11.632 2.09998C11.9583 1.76907 12.4878 1.76871 12.8146 2.09918Z" fill="url(#paint6_linear_1141_1906)" />
|
||||
<path opacity="0.7" fill-rule="evenodd" clip-rule="evenodd" d="M6.66127 4.11624C6.98727 4.4475 6.98636 4.98366 6.65923 5.31378L4.22582 7.76948C3.89869 8.0996 3.36923 8.09868 3.04322 7.76741C2.71722 7.43615 2.71813 6.9 3.04526 6.56987L5.47867 4.11418C5.8058 3.78405 6.33526 3.78498 6.66127 4.11624Z" fill="url(#paint7_linear_1141_1906)" />
|
||||
<path opacity="0.5" fill-rule="evenodd" clip-rule="evenodd" d="M8.15116 1.66602C8.613 1.66602 8.98739 2.04514 8.98739 2.51281V2.59749C8.98739 3.06516 8.613 3.44428 8.15116 3.44428C7.68933 3.44428 7.31494 3.06516 7.31494 2.59749V2.51281C7.31494 2.04514 7.68933 1.66602 8.15116 1.66602Z" fill="url(#paint8_linear_1141_1906)" />
|
||||
<path opacity="0.5" fill-rule="evenodd" clipRule="evenodd" d="M4.34354 10.6855C4.67011 11.0162 4.6701 11.5524 4.34353 11.8831L4.32681 11.9C4.00024 12.2307 3.47077 12.2307 3.14421 11.9C2.81765 11.5693 2.81765 11.0331 3.14422 10.7024L3.16095 10.6855C3.48751 10.3548 4.01698 10.3548 4.34354 10.6855Z" fill="url(#paint0_linear_1141_1906)" />
|
||||
<path opacity="0.7" fill-rule="evenodd" clipRule="evenodd" d="M8.27545 10.9405C8.60142 11.2718 8.60046 11.808 8.27331 12.1381L5.95697 14.4752C5.62981 14.8053 5.10035 14.8043 4.77437 14.473C4.4484 14.1417 4.44936 13.6056 4.77651 13.2755L7.09285 10.9383C7.42001 10.6082 7.94947 10.6092 8.27545 10.9405Z" fill="url(#paint1_linear_1141_1906)" />
|
||||
<path opacity="0.5" fill-rule="evenodd" clipRule="evenodd" d="M11.4179 14.9651C11.6741 14.5759 12.1932 14.4708 12.5775 14.7302L12.6277 14.7641C13.012 15.0235 13.1158 15.5492 12.8596 15.9384C12.6034 16.3275 12.0842 16.4326 11.7 16.1732L11.6498 16.1393C11.2655 15.8799 11.1617 15.3542 11.4179 14.9651Z" fill="url(#paint2_linear_1141_1906)" />
|
||||
<path opacity="0.7" fill-rule="evenodd" clipRule="evenodd" d="M16.9375 10.6347C17.264 10.9654 17.264 11.5016 16.9375 11.8323L15.8002 12.9839C15.4736 13.3146 14.9442 13.3146 14.6176 12.9839C14.291 12.6532 14.291 12.1171 14.6176 11.7864L15.7549 10.6347C16.0814 10.304 16.6109 10.304 16.9375 10.6347Z" fill="url(#paint3_linear_1141_1906)" />
|
||||
<path fill-rule="evenodd" clipRule="evenodd" d="M16.9542 6.37693C17.2808 6.70762 17.2808 7.24378 16.9542 7.57447L8.5502 16.0847C8.22364 16.4154 7.69417 16.4154 7.3676 16.0847C7.04104 15.754 7.04104 15.2179 7.3676 14.8872L15.7717 6.37693C16.0982 6.04623 16.6277 6.04623 16.9542 6.37693Z" fill="url(#paint4_linear_1141_1906)" />
|
||||
<path fill-rule="evenodd" clipRule="evenodd" d="M15.3649 3.75974C15.6915 4.09043 15.6915 4.62659 15.3649 4.95728L10.5315 9.85174C10.205 10.1824 9.67549 10.1824 9.34893 9.85174C9.02236 9.52104 9.02236 8.98489 9.34893 8.65419L14.1823 3.75974C14.5089 3.42905 15.0383 3.42905 15.3649 3.75974Z" fill="url(#paint5_linear_1141_1906)" />
|
||||
<path fill-rule="evenodd" clipRule="evenodd" d="M12.8146 2.09918C13.1414 2.42965 13.1417 2.96581 12.8154 3.29672L6.60224 9.59685C6.27589 9.92777 5.74642 9.92813 5.41964 9.59766C5.09285 9.26719 5.0925 8.73103 5.41884 8.40011L11.632 2.09998C11.9583 1.76907 12.4878 1.76871 12.8146 2.09918Z" fill="url(#paint6_linear_1141_1906)" />
|
||||
<path opacity="0.7" fill-rule="evenodd" clipRule="evenodd" d="M6.66127 4.11624C6.98727 4.4475 6.98636 4.98366 6.65923 5.31378L4.22582 7.76948C3.89869 8.0996 3.36923 8.09868 3.04322 7.76741C2.71722 7.43615 2.71813 6.9 3.04526 6.56987L5.47867 4.11418C5.8058 3.78405 6.33526 3.78498 6.66127 4.11624Z" fill="url(#paint7_linear_1141_1906)" />
|
||||
<path opacity="0.5" fill-rule="evenodd" clipRule="evenodd" d="M8.15116 1.66602C8.613 1.66602 8.98739 2.04514 8.98739 2.51281V2.59749C8.98739 3.06516 8.613 3.44428 8.15116 3.44428C7.68933 3.44428 7.31494 3.06516 7.31494 2.59749V2.51281C7.31494 2.04514 7.68933 1.66602 8.15116 1.66602Z" fill="url(#paint8_linear_1141_1906)" />
|
||||
</g>
|
||||
<defs>
|
||||
<filter id="filter0_d_1141_1906" x="0.399316" y="-0.400781" width="19.2008" height="22.3996" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix" />
|
||||
<filter id="filter0_d_1141_1906" x="0.399316" y="-0.400781" width="19.2008" height="22.3996" filterUnits="userSpaceOnUse" colorInterpolationFilters="sRGB">
|
||||
<feFlood floodOpacity="0" result="BackgroundImageFix" />
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha" />
|
||||
<feMorphology radius="1.2" operator="erode" in="SourceAlpha" result="effect1_dropShadow_1141_1906" />
|
||||
<feOffset dy="1.8" />
|
||||
@@ -201,40 +201,40 @@ export default function Sidebar({ companyName = "Zitlac Media", collapsed = fals
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_1141_1906" result="shape" />
|
||||
</filter>
|
||||
<linearGradient id="paint0_linear_1141_1906" x1="3.74388" y1="10.4375" x2="3.74388" y2="12.148" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="white" stop-opacity="0.8" />
|
||||
<stop offset="1" stop-color="white" stop-opacity="0.5" />
|
||||
<stop stopColor="white" stopOpacity="0.8" />
|
||||
<stop offset="1" stopColor="white" stopOpacity="0.5" />
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_1141_1906" x1="6.52491" y1="10.6914" x2="6.52491" y2="14.7221" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="white" stop-opacity="0.8" />
|
||||
<stop offset="1" stop-color="white" stop-opacity="0.5" />
|
||||
<stop stopColor="white" stopOpacity="0.8" />
|
||||
<stop offset="1" stopColor="white" stopOpacity="0.5" />
|
||||
</linearGradient>
|
||||
<linearGradient id="paint2_linear_1141_1906" x1="12.1387" y1="14.5879" x2="12.1387" y2="16.3155" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="white" stop-opacity="0.8" />
|
||||
<stop offset="1" stop-color="white" stop-opacity="0.5" />
|
||||
<stop stopColor="white" stopOpacity="0.8" />
|
||||
<stop offset="1" stopColor="white" stopOpacity="0.5" />
|
||||
</linearGradient>
|
||||
<linearGradient id="paint3_linear_1141_1906" x1="15.7775" y1="10.3867" x2="15.7775" y2="13.2319" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="white" stop-opacity="0.8" />
|
||||
<stop offset="1" stop-color="white" stop-opacity="0.5" />
|
||||
<stop stopColor="white" stopOpacity="0.8" />
|
||||
<stop offset="1" stopColor="white" stopOpacity="0.5" />
|
||||
</linearGradient>
|
||||
<linearGradient id="paint4_linear_1141_1906" x1="12.1609" y1="6.12891" x2="12.1609" y2="16.3327" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="white" stop-opacity="0.8" />
|
||||
<stop offset="1" stop-color="white" stop-opacity="0.5" />
|
||||
<stop stopColor="white" stopOpacity="0.8" />
|
||||
<stop offset="1" stopColor="white" stopOpacity="0.5" />
|
||||
</linearGradient>
|
||||
<linearGradient id="paint5_linear_1141_1906" x1="12.3569" y1="3.51172" x2="12.3569" y2="10.0998" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="white" stop-opacity="0.8" />
|
||||
<stop offset="1" stop-color="white" stop-opacity="0.5" />
|
||||
<stop stopColor="white" stopOpacity="0.8" />
|
||||
<stop offset="1" stopColor="white" stopOpacity="0.5" />
|
||||
</linearGradient>
|
||||
<linearGradient id="paint6_linear_1141_1906" x1="9.11711" y1="1.85156" x2="9.11711" y2="9.84527" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="white" stop-opacity="0.8" />
|
||||
<stop offset="1" stop-color="white" stop-opacity="0.5" />
|
||||
<stop stopColor="white" stopOpacity="0.8" />
|
||||
<stop offset="1" stopColor="white" stopOpacity="0.5" />
|
||||
</linearGradient>
|
||||
<linearGradient id="paint7_linear_1141_1906" x1="4.85224" y1="3.86719" x2="4.85224" y2="8.01647" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="white" stop-opacity="0.8" />
|
||||
<stop offset="1" stop-color="white" stop-opacity="0.5" />
|
||||
<stop stopColor="white" stopOpacity="0.8" />
|
||||
<stop offset="1" stopColor="white" stopOpacity="0.5" />
|
||||
</linearGradient>
|
||||
<linearGradient id="paint8_linear_1141_1906" x1="8.15117" y1="1.66602" x2="8.15117" y2="3.44428" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="white" stop-opacity="0.8" />
|
||||
<stop offset="1" stop-color="white" stop-opacity="0.5" />
|
||||
<stop stopColor="white" stopOpacity="0.8" />
|
||||
<stop offset="1" stopColor="white" stopOpacity="0.5" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
@@ -242,12 +242,12 @@ export default function Sidebar({ companyName = "Zitlac Media", collapsed = fals
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-1 inline-flex flex-col justify-start items-start gap-0.5">
|
||||
<div className="self-stretch justify-start text-[--Neutrals-NeutralSlate950] text-base font-medium font-['Inter'] leading-normal">{org?.name || 'Select Organization'}</div>
|
||||
<div className="self-stretch justify-start text-[--Text-Gray-950] text-base font-medium font-['Inter'] leading-normal">{org?.name || 'Select Organization'}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className={`transition-transform duration-200 ${showOrgDropdown ? 'rotate-180' : ''}`}>
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5.83301 7.50001L9.99967 11.6667L14.1663 7.50001" stroke="var(--Neutrals-NeutralSlate400, #A4A7AE)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<path d="M5.83301 7.50001L9.99967 11.6667L14.1663 7.50001" stroke="var(--Neutrals-NeutralSlate400)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
@@ -268,9 +268,9 @@ export default function Sidebar({ companyName = "Zitlac Media", collapsed = fals
|
||||
{organization.name.charAt(0).toUpperCase()}
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<div className="text-[--Neutrals-NeutralSlate950] text-sm font-medium">{organization.name}</div>
|
||||
<div className="text-[--Text-Gray-950] text-sm font-medium">{organization.name}</div>
|
||||
{organization.name && (
|
||||
<div className="text-[--Neutrals-NeutralSlate500] text-xs">{organization.name}</div>
|
||||
<div className="text-[--Text-Gray-500] text-xs">{organization.name}</div>
|
||||
)}
|
||||
</div>
|
||||
{org?.orgId === organization.orgId && (
|
||||
@@ -290,7 +290,7 @@ export default function Sidebar({ companyName = "Zitlac Media", collapsed = fals
|
||||
|
||||
{/* Create New Organization */}
|
||||
<div
|
||||
className="w-full px-3 py-2.5 rounded-xl flex items-center gap-3 cursor-pointer hover:bg-[--Neutrals-NeutralSlate50] text-[--Neutrals-NeutralSlate600]"
|
||||
className="w-full px-3 py-2.5 rounded-xl flex items-center gap-3 cursor-pointer hover:bg-[--Neutrals-NeutralSlate50] text-[--Text-Gray-600]"
|
||||
onClick={() => {
|
||||
setShowOrgDropdown(false);
|
||||
setShowCreateOrgModal(true);
|
||||
@@ -309,25 +309,25 @@ export default function Sidebar({ companyName = "Zitlac Media", collapsed = fals
|
||||
</div>
|
||||
|
||||
{/* Navigation Items */}
|
||||
<div className="self-stretch flex flex-col justify-start items-start gap-5">
|
||||
<div className="self-stretch w-full flex flex-col justify-start items-start gap-5">
|
||||
<div className="self-stretch flex flex-col justify-start items-start gap-1.5">
|
||||
{navItems.map((item, index) => (
|
||||
<div
|
||||
key={index}
|
||||
onClick={() => handleNavClick(item.path)}
|
||||
className={`w-60 px-4 py-2.5 rounded-[34px] inline-flex justify-start items-center gap-2 cursor-pointer ${item.active
|
||||
className={`w-full px-4 py-2.5 rounded-[34px] inline-flex justify-start items-center gap-2 cursor-pointer ${item.active
|
||||
? 'bg-[--Neutrals-NeutralSlate100]'
|
||||
: 'hover:bg-[--Neutrals-NeutralSlate50]'
|
||||
}`}
|
||||
>
|
||||
<div className="relative">
|
||||
{React.cloneElement(item.icon, {
|
||||
stroke: item.active ? "var(--Brand-Orange)" : "var(--Neutrals-NeutralSlate400, #A4A7AE)"
|
||||
stroke: item.active ? "var(--Brand-Orange)" : "var(--Neutrals-NeutralSlate400)"
|
||||
})}
|
||||
</div>
|
||||
<div className={`justify-start text-sm font-medium font-['Inter'] leading-tight ${item.active
|
||||
? 'text-[--Neutrals-NeutralSlate950]'
|
||||
: 'text-[--Neutrals-NeutralSlate500]'
|
||||
? 'text-[--Text-Gray-950]'
|
||||
: 'text-[--Text-Gray-500]'
|
||||
}`}>
|
||||
{item.label}
|
||||
</div>
|
||||
@@ -341,24 +341,24 @@ export default function Sidebar({ companyName = "Zitlac Media", collapsed = fals
|
||||
{showCreateOrgModal && (
|
||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
||||
<div className="bg-[--Neutrals-NeutralSlate0] p-6 rounded-2xl max-w-md w-full mx-4 shadow-[0px_20px_40px_0px_rgba(14,18,27,0.25)]">
|
||||
<h3 className="text-lg font-semibold text-[--Neutrals-NeutralSlate950] mb-4">Create New Organization</h3>
|
||||
<h3 className="text-lg font-semibold text-[--Text-Gray-950] mb-4">Create New Organization</h3>
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-[--Neutrals-NeutralSlate700] mb-2">Organization Name</label>
|
||||
<label className="block text-sm font-medium text-[--Text-Gray-700] mb-2">Organization Name</label>
|
||||
<input
|
||||
type="text"
|
||||
value={createOrgForm.name}
|
||||
onChange={(e) => setCreateOrgForm(prev => ({ ...prev, name: e.target.value }))}
|
||||
className="w-full px-3 py-2.5 border border-[--Neutrals-NeutralSlate200] rounded-xl bg-[--Neutrals-NeutralSlate0] text-[--Neutrals-NeutralSlate950] focus:outline-none focus:ring-2 focus:ring-[--Brand-Orange] focus:border-transparent"
|
||||
className="w-full px-3 py-2.5 border border-[--Neutrals-NeutralSlate200] rounded-xl bg-[--Neutrals-NeutralSlate0] text-[--Text-Gray-950] focus:outline-none focus:ring-2 focus:ring-[--Brand-Orange] focus:border-transparent"
|
||||
placeholder="Enter organization name"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-[--Neutrals-NeutralSlate700] mb-2">Description (Optional)</label>
|
||||
<label className="block text-sm font-medium text-[--Text-Gray-700] mb-2">Description (Optional)</label>
|
||||
<textarea
|
||||
value={createOrgForm.description}
|
||||
onChange={(e) => setCreateOrgForm(prev => ({ ...prev, description: e.target.value }))}
|
||||
className="w-full px-3 py-2.5 border border-[--Neutrals-NeutralSlate200] rounded-xl bg-[--Neutrals-NeutralSlate0] text-[--Neutrals-NeutralSlate950] focus:outline-none focus:ring-2 focus:ring-[--Brand-Orange] focus:border-transparent resize-none"
|
||||
className="w-full px-3 py-2.5 border border-[--Neutrals-NeutralSlate200] rounded-xl bg-[--Neutrals-NeutralSlate0] text-[--Text-Gray-950] focus:outline-none focus:ring-2 focus:ring-[--Brand-Orange] focus:border-transparent resize-none"
|
||||
rows={3}
|
||||
placeholder="Brief description of your organization"
|
||||
/>
|
||||
@@ -503,7 +503,7 @@ export default function Sidebar({ companyName = "Zitlac Media", collapsed = fals
|
||||
{/* Settings */}
|
||||
<div
|
||||
onClick={() => handleNavClick("/settings")}
|
||||
className={`w-60 px-4 py-2.5 rounded-[34px] inline-flex justify-start items-center gap-2 cursor-pointer ${location.pathname === "/settings"
|
||||
className={`w-full px-4 py-2.5 rounded-[34px] inline-flex justify-start items-center gap-2 cursor-pointer ${location.pathname === "/settings"
|
||||
? 'bg-[--Neutrals-NeutralSlate100]'
|
||||
: 'hover:bg-[--Neutrals-NeutralSlate50]'
|
||||
}`}
|
||||
@@ -514,8 +514,8 @@ export default function Sidebar({ companyName = "Zitlac Media", collapsed = fals
|
||||
})}
|
||||
</div>
|
||||
<div className={`flex-1 justify-start text-sm font-medium font-['Inter'] leading-tight ${location.pathname === "/settings"
|
||||
? 'text-[--Neutrals-NeutralSlate950]'
|
||||
: 'text-[--Neutrals-NeutralSlate500]'
|
||||
? 'text-[--Text-Gray-950]'
|
||||
: 'text-[--Text-Gray-500]'
|
||||
}`}>
|
||||
Settings
|
||||
</div>
|
||||
@@ -524,7 +524,7 @@ export default function Sidebar({ companyName = "Zitlac Media", collapsed = fals
|
||||
{/* Build Report Card */}
|
||||
<div className="self-stretch bg-[--Neutrals-NeutralSlate0] rounded-[20px] shadow-[0px_1px_4px_0px_rgba(14,18,27,0.04)] outline outline-1 outline-offset-[-1px] outline-[--Neutrals-NeutralSlate200] flex flex-col justify-start items-start overflow-hidden">
|
||||
<div className="self-stretch h-24 relative">
|
||||
<div className="w-60 p-3 origin-top-left rotate-[-28.34deg] bg-[--Neutrals-NeutralSlate0] rounded-xl shadow-[0px_10px_20px_4px_rgba(14,18,27,0.08)] outline outline-1 outline-offset-[-1px] outline-[--Neutrals-NeutralSlate100]
|
||||
<div className="w-full p-3 origin-top-left rotate-[-28.34deg] bg-[--Neutrals-NeutralSlate0] rounded-xl shadow-[0px_10px_20px_4px_rgba(14,18,27,0.08)] outline outline-1 outline-offset-[-1px] outline-[--Neutrals-NeutralSlate100]
|
||||
inline-flex flex-col justify-start items-start gap-3 overflow-hidden">
|
||||
<div className="self-stretch flex flex-col justify-start items-start gap-2">
|
||||
<div className="self-stretch h-2 bg-[--Neutrals-NeutralSlate100] rounded-sm" />
|
||||
@@ -541,23 +541,22 @@ export default function Sidebar({ companyName = "Zitlac Media", collapsed = fals
|
||||
</div>
|
||||
</div>
|
||||
<div className="self-stretch p-3 flex flex-col justify-start items-start gap-1">
|
||||
<div className="self-stretch justify-start text-[--Neutrals-NeutralSlate800] text-sm font-semibold font-['Inter'] leading-tight">Build {org.name}'s Report</div>
|
||||
<div className="self-stretch justify-start text-[--Neutrals-NeutralSlate500] text-xs font-normal font-['Inter'] leading-none">Share this form with your team members to capture valuable info about your company to train Auditly.</div>
|
||||
<div className="self-stretch justify-start text-[--Text-Gray-800] text-sm font-semibold font-['Inter'] leading-tight">Build {org.name}'s Report</div>
|
||||
<div className="self-stretch justify-start text-[--Text-Gray-500] text-xs font-normal font-['Inter'] leading-none">Share this form with your team members to capture valuable info about your company to train Auditly.</div>
|
||||
</div>
|
||||
<div className="self-stretch px-3 pb-3 flex flex-col justify-start items-start gap-8">
|
||||
<div className="self-stretch inline-flex justify-start items-start gap-2">
|
||||
<Button size="sm" className="w-full" onClick={() => setShowInviteModal(true)}>
|
||||
<Button variant="secondary" size="sm" className="w-full rounded-[999px]" onClick={() => setShowInviteModal(true)}>
|
||||
<PlusIcon className="w-4 h-4 mr-1" /> Invite
|
||||
</Button>
|
||||
|
||||
<Button size="sm" variant="secondary" className="w-full" onClick={() => (emailLink || inviteLink) && copyToClipboard(emailLink || inviteLink)}>
|
||||
<CopyIcon className="w-4 h-4 mr-1" /> Copy
|
||||
<Button size="sm" variant="primary" className="w-full rounded-[999px]" onClick={() => (emailLink || inviteLink) && copyToClipboard(emailLink || inviteLink)}>
|
||||
<CopyIcon className="mr-2" /> Copy
|
||||
</Button>
|
||||
</div>
|
||||
<Button size="sm" variant="ghost" className="w-full mt-2" onClick={signOutUser}>Sign out</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div >
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
2035
src/components/figma/figmaIcon.tsx
Normal file
2035
src/components/figma/figmaIcon.tsx
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,5 @@
|
||||
import React from 'react';
|
||||
import { FigmaPrimaryButton } from '../figma/FigmaButton';
|
||||
|
||||
interface FigmaOnboardingIntroProps {
|
||||
section: number;
|
||||
@@ -24,51 +25,51 @@ export const FigmaOnboardingIntro: React.FC<FigmaOnboardingIntroProps> = ({
|
||||
<div className="w-12 h-12 left-0 top-0 absolute bg-gradient-to-b from-white/0 to-white/10" />
|
||||
<div data-svg-wrapper className="left-[12px] top-[9.33px] absolute">
|
||||
<svg width="24" height="30" viewBox="0 0 24 30" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path opacity="0.5" fill-rule="evenodd" clip-rule="evenodd" d="M2.57408 17.8128C3.11835 18.3639 3.11834 19.2575 2.57406 19.8087L2.54619 19.8369C2.00191 20.3881 1.11946 20.3881 0.57519 19.8369C0.030919 19.2857 0.0309274 18.3921 0.575208 17.841L0.603083 17.8128C1.14736 17.2616 2.02981 17.2616 2.57408 17.8128Z" fill="url(#paint0_linear_759_16243)" />
|
||||
<path opacity="0.7" fill-rule="evenodd" clip-rule="evenodd" d="M9.12583 18.2379C9.66912 18.7901 9.66752 19.6837 9.12226 20.2338L5.2617 24.1291C4.71644 24.6792 3.83399 24.6776 3.2907 24.1255C2.74741 23.5733 2.74901 22.6797 3.29427 22.1296L7.15483 18.2343C7.70009 17.6842 8.58254 17.6858 9.12583 18.2379Z" fill="url(#paint1_linear_759_16243)" />
|
||||
<path opacity="0.5" fill-rule="evenodd" clip-rule="evenodd" d="M14.3656 24.9431C14.7925 24.2945 15.6578 24.1193 16.2983 24.5516L16.3819 24.6081C17.0224 25.0404 17.1954 25.9167 16.7685 26.5652C16.3415 27.2138 15.4762 27.389 14.8357 26.9567L14.7521 26.9002C14.1117 26.4678 13.9386 25.5916 14.3656 24.9431Z" fill="url(#paint2_linear_759_16243)" />
|
||||
<path opacity="0.7" fill-rule="evenodd" clip-rule="evenodd" d="M23.5637 17.7278C24.108 18.279 24.108 19.1726 23.5637 19.7237L21.6683 21.6431C21.124 22.1943 20.2415 22.1943 19.6973 21.6431C19.153 21.092 19.153 20.1984 19.6973 19.6472L21.5927 17.7278C22.137 17.1767 23.0194 17.1767 23.5637 17.7278Z" fill="url(#paint3_linear_759_16243)" />
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M23.592 10.6292C24.1363 11.1803 24.1363 12.0739 23.592 12.6251L9.58526 26.8089C9.04098 27.36 8.15854 27.36 7.61426 26.8089C7.06999 26.2577 7.06999 25.3641 7.61426 24.8129L21.621 10.6292C22.1653 10.078 23.0477 10.078 23.592 10.6292Z" fill="url(#paint4_linear_759_16243)" />
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M20.9426 6.26835C21.4869 6.8195 21.4869 7.7131 20.9426 8.26425L12.887 16.4217C12.3427 16.9728 11.4603 16.9728 10.916 16.4217C10.3717 15.8705 10.3717 14.9769 10.916 14.4258L18.9716 6.26835C19.5159 5.71719 20.3984 5.71719 20.9426 6.26835Z" fill="url(#paint5_linear_759_16243)" />
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M16.6918 3.50156C17.2364 4.05235 17.237 4.94594 16.6931 5.49747L6.33787 15.9977C5.79396 16.5492 4.91152 16.5498 4.36688 15.999C3.82224 15.4482 3.82164 14.5546 4.36555 14.0031L14.7208 3.5029C15.2647 2.95138 16.1471 2.95078 16.6918 3.50156Z" fill="url(#paint6_linear_759_16243)" />
|
||||
<path opacity="0.7" fill-rule="evenodd" clip-rule="evenodd" d="M6.43658 6.86284C6.97992 7.41494 6.9784 8.30854 6.43319 8.85874L2.37751 12.9516C1.83229 13.5018 0.94985 13.5002 0.406512 12.9481C-0.136826 12.396 -0.135307 11.5024 0.409905 10.9522L4.46559 6.8594C5.0108 6.3092 5.89324 6.31074 6.43658 6.86284Z" fill="url(#paint7_linear_759_16243)" />
|
||||
<path opacity="0.5" fill-rule="evenodd" clip-rule="evenodd" d="M8.92007 2.77832C9.6898 2.77832 10.3138 3.41019 10.3138 4.18964V4.33077C10.3138 5.11022 9.6898 5.74209 8.92007 5.74209C8.15035 5.74209 7.52637 5.11022 7.52637 4.33077V4.18964C7.52637 3.41019 8.15035 2.77832 8.92007 2.77832Z" fill="url(#paint8_linear_759_16243)" />
|
||||
<path opacity="0.5" fill-rule="evenodd" clipRule="evenodd" d="M2.57408 17.8128C3.11835 18.3639 3.11834 19.2575 2.57406 19.8087L2.54619 19.8369C2.00191 20.3881 1.11946 20.3881 0.57519 19.8369C0.030919 19.2857 0.0309274 18.3921 0.575208 17.841L0.603083 17.8128C1.14736 17.2616 2.02981 17.2616 2.57408 17.8128Z" fill="url(#paint0_linear_759_16243)" />
|
||||
<path opacity="0.7" fill-rule="evenodd" clipRule="evenodd" d="M9.12583 18.2379C9.66912 18.7901 9.66752 19.6837 9.12226 20.2338L5.2617 24.1291C4.71644 24.6792 3.83399 24.6776 3.2907 24.1255C2.74741 23.5733 2.74901 22.6797 3.29427 22.1296L7.15483 18.2343C7.70009 17.6842 8.58254 17.6858 9.12583 18.2379Z" fill="url(#paint1_linear_759_16243)" />
|
||||
<path opacity="0.5" fill-rule="evenodd" clipRule="evenodd" d="M14.3656 24.9431C14.7925 24.2945 15.6578 24.1193 16.2983 24.5516L16.3819 24.6081C17.0224 25.0404 17.1954 25.9167 16.7685 26.5652C16.3415 27.2138 15.4762 27.389 14.8357 26.9567L14.7521 26.9002C14.1117 26.4678 13.9386 25.5916 14.3656 24.9431Z" fill="url(#paint2_linear_759_16243)" />
|
||||
<path opacity="0.7" fill-rule="evenodd" clipRule="evenodd" d="M23.5637 17.7278C24.108 18.279 24.108 19.1726 23.5637 19.7237L21.6683 21.6431C21.124 22.1943 20.2415 22.1943 19.6973 21.6431C19.153 21.092 19.153 20.1984 19.6973 19.6472L21.5927 17.7278C22.137 17.1767 23.0194 17.1767 23.5637 17.7278Z" fill="url(#paint3_linear_759_16243)" />
|
||||
<path fill-rule="evenodd" clipRule="evenodd" d="M23.592 10.6292C24.1363 11.1803 24.1363 12.0739 23.592 12.6251L9.58526 26.8089C9.04098 27.36 8.15854 27.36 7.61426 26.8089C7.06999 26.2577 7.06999 25.3641 7.61426 24.8129L21.621 10.6292C22.1653 10.078 23.0477 10.078 23.592 10.6292Z" fill="url(#paint4_linear_759_16243)" />
|
||||
<path fill-rule="evenodd" clipRule="evenodd" d="M20.9426 6.26835C21.4869 6.8195 21.4869 7.7131 20.9426 8.26425L12.887 16.4217C12.3427 16.9728 11.4603 16.9728 10.916 16.4217C10.3717 15.8705 10.3717 14.9769 10.916 14.4258L18.9716 6.26835C19.5159 5.71719 20.3984 5.71719 20.9426 6.26835Z" fill="url(#paint5_linear_759_16243)" />
|
||||
<path fill-rule="evenodd" clipRule="evenodd" d="M16.6918 3.50156C17.2364 4.05235 17.237 4.94594 16.6931 5.49747L6.33787 15.9977C5.79396 16.5492 4.91152 16.5498 4.36688 15.999C3.82224 15.4482 3.82164 14.5546 4.36555 14.0031L14.7208 3.5029C15.2647 2.95138 16.1471 2.95078 16.6918 3.50156Z" fill="url(#paint6_linear_759_16243)" />
|
||||
<path opacity="0.7" fill-rule="evenodd" clipRule="evenodd" d="M6.43658 6.86284C6.97992 7.41494 6.9784 8.30854 6.43319 8.85874L2.37751 12.9516C1.83229 13.5018 0.94985 13.5002 0.406512 12.9481C-0.136826 12.396 -0.135307 11.5024 0.409905 10.9522L4.46559 6.8594C5.0108 6.3092 5.89324 6.31074 6.43658 6.86284Z" fill="url(#paint7_linear_759_16243)" />
|
||||
<path opacity="0.5" fill-rule="evenodd" clipRule="evenodd" d="M8.92007 2.77832C9.6898 2.77832 10.3138 3.41019 10.3138 4.18964V4.33077C10.3138 5.11022 9.6898 5.74209 8.92007 5.74209C8.15035 5.74209 7.52637 5.11022 7.52637 4.33077V4.18964C7.52637 3.41019 8.15035 2.77832 8.92007 2.77832Z" fill="url(#paint8_linear_759_16243)" />
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_759_16243" x1="1.57463" y1="17.3994" x2="1.57463" y2="20.2503" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="white" stop-opacity="0.8" />
|
||||
<stop offset="1" stop-color="white" stop-opacity="0.5" />
|
||||
<stop stopColor="white" stopOpacity="0.8" />
|
||||
<stop offset="1" stopColor="white" stopOpacity="0.5" />
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_759_16243" x1="6.20827" y1="17.8228" x2="6.20827" y2="24.5406" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="white" stop-opacity="0.8" />
|
||||
<stop offset="1" stop-color="white" stop-opacity="0.5" />
|
||||
<stop stopColor="white" stopOpacity="0.8" />
|
||||
<stop offset="1" stopColor="white" stopOpacity="0.5" />
|
||||
</linearGradient>
|
||||
<linearGradient id="paint2_linear_759_16243" x1="15.567" y1="24.3145" x2="15.567" y2="27.1938" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="white" stop-opacity="0.8" />
|
||||
<stop offset="1" stop-color="white" stop-opacity="0.5" />
|
||||
<stop stopColor="white" stopOpacity="0.8" />
|
||||
<stop offset="1" stopColor="white" stopOpacity="0.5" />
|
||||
</linearGradient>
|
||||
<linearGradient id="paint3_linear_759_16243" x1="21.6305" y1="17.3145" x2="21.6305" y2="22.0565" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="white" stop-opacity="0.8" />
|
||||
<stop offset="1" stop-color="white" stop-opacity="0.5" />
|
||||
<stop stopColor="white" stopOpacity="0.8" />
|
||||
<stop offset="1" stopColor="white" stopOpacity="0.5" />
|
||||
</linearGradient>
|
||||
<linearGradient id="paint4_linear_759_16243" x1="15.6031" y1="10.2158" x2="15.6031" y2="27.2222" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="white" stop-opacity="0.8" />
|
||||
<stop offset="1" stop-color="white" stop-opacity="0.5" />
|
||||
<stop stopColor="white" stopOpacity="0.8" />
|
||||
<stop offset="1" stopColor="white" stopOpacity="0.5" />
|
||||
</linearGradient>
|
||||
<linearGradient id="paint5_linear_759_16243" x1="15.9293" y1="5.85498" x2="15.9293" y2="16.835" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="white" stop-opacity="0.8" />
|
||||
<stop offset="1" stop-color="white" stop-opacity="0.5" />
|
||||
<stop stopColor="white" stopOpacity="0.8" />
|
||||
<stop offset="1" stopColor="white" stopOpacity="0.5" />
|
||||
</linearGradient>
|
||||
<linearGradient id="paint6_linear_759_16243" x1="10.5293" y1="3.08887" x2="10.5293" y2="16.4117" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="white" stop-opacity="0.8" />
|
||||
<stop offset="1" stop-color="white" stop-opacity="0.5" />
|
||||
<stop stopColor="white" stopOpacity="0.8" />
|
||||
<stop offset="1" stopColor="white" stopOpacity="0.5" />
|
||||
</linearGradient>
|
||||
<linearGradient id="paint7_linear_759_16243" x1="3.42155" y1="6.44775" x2="3.42155" y2="13.3632" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="white" stop-opacity="0.8" />
|
||||
<stop offset="1" stop-color="white" stop-opacity="0.5" />
|
||||
<stop stopColor="white" stopOpacity="0.8" />
|
||||
<stop offset="1" stopColor="white" stopOpacity="0.5" />
|
||||
</linearGradient>
|
||||
<linearGradient id="paint8_linear_759_16243" x1="8.92007" y1="2.77832" x2="8.92007" y2="5.74209" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="white" stop-opacity="0.8" />
|
||||
<stop offset="1" stop-color="white" stop-opacity="0.5" />
|
||||
<stop stopColor="white" stopOpacity="0.8" />
|
||||
<stop offset="1" stopColor="white" stopOpacity="0.5" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
@@ -76,26 +77,19 @@ export const FigmaOnboardingIntro: React.FC<FigmaOnboardingIntroProps> = ({
|
||||
</div>
|
||||
<div className="self-stretch flex flex-col justify-start items-start gap-4">
|
||||
<div className="px-3 py-1.5 bg-[--Neutrals-NeutralSlate100] rounded-[50px] inline-flex justify-center items-center gap-2 overflow-hidden">
|
||||
<div className="justify-start text-[--Neutrals-NeutralSlate500] text-sm font-medium font-['Inter'] uppercase leading-none">
|
||||
<div className="justify-start text-[--Text-Gray-500] text-sm font-medium font-['Inter'] uppercase leading-none">
|
||||
{section} of {totalSections}
|
||||
</div>
|
||||
</div>
|
||||
<div className="self-stretch justify-start text-[--Neutrals-NeutralSlate800] text-5xl font-medium font-['Neue_Montreal'] leading-[48px]">
|
||||
<div className="self-stretch justify-start text-[--Text-Gray-800] text-5xl font-medium font-['Neue_Montreal'] leading-[48px]">
|
||||
{title}
|
||||
</div>
|
||||
<div className="self-stretch justify-center text-[--Neutrals-NeutralSlate500] text-base font-normal font-['Inter'] leading-normal">
|
||||
<div className="self-stretch justify-center text-[--Text-Gray-500] text-base font-normal font-['Inter'] leading-normal">
|
||||
{description}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
onClick={onStart}
|
||||
className="self-stretch px-4 py-3.5 bg-[--Brand-Orange] rounded-[999px] outline outline-2 outline-offset-[-2px] outline-blue-400 inline-flex justify-center items-center gap-1 overflow-hidden hover:bg-[--Brand-Orange]/90 transition-colors"
|
||||
>
|
||||
<div className="px-1 flex justify-center items-center">
|
||||
<div className="justify-center text-white text-sm font-medium font-['Inter'] leading-tight">Start</div>
|
||||
</div>
|
||||
</button>
|
||||
<FigmaPrimaryButton text="Start" onClick={onStart} size="big" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="self-stretch h-[810px] px-20 py-16 inline-flex justify-center items-center gap-2.5 overflow-hidden">
|
||||
@@ -161,53 +155,45 @@ export const FigmaOnboardingQuestion: React.FC<FigmaOnboardingQuestionProps> = (
|
||||
<div className="w-[1440px] h-[810px] py-6 relative bg-[--Neutrals-NeutralSlate0] inline-flex flex-col justify-center items-center gap-36">
|
||||
<div className="w-full max-w-[464px] min-w-[464px] flex flex-col justify-start items-start gap-12">
|
||||
<div className="self-stretch flex flex-col justify-start items-start gap-8">
|
||||
<div className="self-stretch text-center justify-start text-[--Neutrals-NeutralSlate950] text-2xl font-medium font-['Neue_Montreal'] leading-normal">
|
||||
<div className="self-stretch text-center justify-start text-[--Text-Gray-950] text-2xl font-medium font-['Neue_Montreal'] leading-normal">
|
||||
{question}
|
||||
</div>
|
||||
<div className="self-stretch min-h-40 p-5 relative bg-[--Neutrals-NeutralSlate100] rounded-xl inline-flex justify-start items-start gap-2.5">
|
||||
<textarea
|
||||
value={value}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
className="flex-1 bg-transparent text-[--Neutrals-NeutralSlate950] text-base font-normal font-['Inter'] leading-normal placeholder:text-[--Neutrals-NeutralSlate950] outline-none resize-none"
|
||||
className="flex-1 bg-transparent text-[--Text-Gray-950] text-base font-normal font-['Inter'] leading-normal placeholder:text-[--Text-Gray-950] outline-none resize-none"
|
||||
placeholder={placeholder}
|
||||
rows={rows}
|
||||
/>
|
||||
<div className="w-3 h-3 absolute right-3 bottom-3">
|
||||
<div className="w-2 h-2 absolute top-0.5 left-0.5 outline outline-1 outline-offset-[-0.50px] outline-[--$1]" />
|
||||
<div className="w-1 h-1 absolute top-2 left-2 outline outline-1 outline-offset-[-0.50px] outline-[--$1]" />
|
||||
<div className="w-2 h-2 absolute top-0.5 left-0.5 outline outline-1 outline-offset-[-0.50px] outline-[--Neutrals-NeutralSlate500]" />
|
||||
<div className="w-1 h-1 absolute top-2 left-2 outline outline-1 outline-offset-[-0.50px] outline-[--Neutrals-NeutralSlate500]" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="self-stretch inline-flex justify-start items-start gap-2">
|
||||
<button
|
||||
onClick={onBack}
|
||||
className="h-12 px-8 py-3.5 bg-[--Neutrals-NeutralSlate100] rounded-[999px] flex justify-center items-center gap-1 overflow-hidden hover:bg-[--Neutrals-NeutralSlate100] transition-colors"
|
||||
className="px-8 py-3.5 bg-[--Neutrals-NeutralSlate100] rounded-[999px] flex justify-center items-center gap-1 overflow-hidden hover:bg-[--Neutrals-NeutralSlate100] transition-colors"
|
||||
>
|
||||
<div className="px-1 flex justify-center items-center">
|
||||
<div className="justify-center text-[--Neutrals-NeutralSlate950] text-sm font-medium font-['Inter'] leading-tight">Back</div>
|
||||
</div>
|
||||
</button>
|
||||
<button
|
||||
onClick={onNext}
|
||||
disabled={!canProceed}
|
||||
className="flex-1 h-12 px-4 py-3.5 bg-[--Brand-Orange] rounded-[999px] outline outline-2 outline-offset-[-2px] outline-blue-400 flex justify-center items-center gap-1 overflow-hidden disabled:opacity-50 disabled:cursor-not-allowed hover:bg-[--Brand-Orange]/90 transition-colors"
|
||||
>
|
||||
<div className="px-1 flex justify-center items-center">
|
||||
<div className="justify-center text-white text-sm font-medium font-['Inter'] leading-tight">Next</div>
|
||||
<div className="justify-center text-[--Text-Gray-950] text-sm font-medium font-['Inter'] leading-tight">Back</div>
|
||||
</div>
|
||||
</button>
|
||||
<FigmaPrimaryButton text="Next" onClick={onNext} size="big" disabled={!canProceed} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="px-3 py-1.5 left-[24px] top-[24px] absolute bg-[--Neutrals-NeutralSlate100] rounded-[50px] inline-flex justify-center items-center gap-2 overflow-hidden">
|
||||
<div className="justify-start text-[--Neutrals-NeutralSlate500] text-sm font-medium font-['Inter'] uppercase leading-none">
|
||||
<div className="justify-start text-[--Text-Gray-500] text-sm font-medium font-['Inter'] uppercase leading-none">
|
||||
{sectionPosition} of {totalInSection}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{canSkip && (
|
||||
<div className="px-3 py-1.5 right-[24px] top-[24px] absolute bg-[--Neutrals-NeutralSlate100] rounded-[50px] inline-flex justify-center items-center gap-2 overflow-hidden cursor-pointer hover:bg-[--Neutrals-NeutralSlate100] transition-colors">
|
||||
<div className="justify-start text-[--Neutrals-NeutralSlate500] text-sm font-medium font-['Inter'] leading-none">Skip</div>
|
||||
<div className="justify-start text-[--Text-Gray-500] text-sm font-medium font-['Inter'] leading-none">Skip</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -215,7 +201,7 @@ export const FigmaOnboardingQuestion: React.FC<FigmaOnboardingQuestionProps> = (
|
||||
<div className="p-4 bg-[--Neutrals-NeutralSlate100] rounded-[50px] inline-flex justify-center items-center gap-2 overflow-hidden">
|
||||
{renderProgressDots()}
|
||||
</div>
|
||||
<div className="self-stretch text-center justify-start text-[--Neutrals-NeutralSlate500] text-base font-medium font-['Neue_Montreal'] leading-normal">
|
||||
<div className="self-stretch text-center justify-start text-[--Text-Gray-500] text-base font-medium font-['Neue_Montreal'] leading-normal">
|
||||
{sectionName}
|
||||
</div>
|
||||
</div>
|
||||
@@ -271,7 +257,7 @@ export const FigmaOnboardingMultipleChoice: React.FC<FigmaOnboardingMultipleChoi
|
||||
<div className="w-[1440px] h-[810px] py-6 relative bg-[--Neutrals-NeutralSlate0] inline-flex flex-col justify-center items-center gap-9">
|
||||
<div className="w-full max-w-[464px] min-w-[464px] flex flex-col justify-start items-start gap-12">
|
||||
<div className="self-stretch flex flex-col justify-start items-start gap-8">
|
||||
<div className="self-stretch text-center justify-start text-[--Neutrals-NeutralSlate950] text-2xl font-medium font-['Neue_Montreal'] leading-normal">
|
||||
<div className="self-stretch text-center justify-start text-[--Text-Gray-950] text-2xl font-medium font-['Neue_Montreal'] leading-normal">
|
||||
{question}
|
||||
</div>
|
||||
<div className="self-stretch inline-flex justify-center items-center gap-3">
|
||||
@@ -285,8 +271,8 @@ export const FigmaOnboardingMultipleChoice: React.FC<FigmaOnboardingMultipleChoi
|
||||
}`}
|
||||
>
|
||||
<div className={`absolute inset-0 flex items-center justify-center text-center text-base font-normal font-['Inter'] leading-normal ${selectedValue === option
|
||||
? 'text-[--Neutrals-NeutralSlate0]'
|
||||
: 'text-[--Neutrals-NeutralSlate950]'
|
||||
? 'text-[--Text-Gray-0]'
|
||||
: 'text-[--Text-Gray-950]'
|
||||
}`}>
|
||||
{option}
|
||||
</div>
|
||||
@@ -297,33 +283,25 @@ export const FigmaOnboardingMultipleChoice: React.FC<FigmaOnboardingMultipleChoi
|
||||
<div className="self-stretch inline-flex justify-start items-start gap-2">
|
||||
<button
|
||||
onClick={onBack}
|
||||
className="h-12 px-8 py-3.5 bg-[--Neutrals-NeutralSlate100] rounded-[999px] flex justify-center items-center gap-1 overflow-hidden hover:bg-[--Neutrals-NeutralSlate100] transition-colors"
|
||||
className="px-8 py-3.5 bg-[--Neutrals-NeutralSlate100] rounded-[999px] flex justify-center items-center gap-1 overflow-hidden hover:bg-[--Neutrals-NeutralSlate100] transition-colors"
|
||||
>
|
||||
<div className="px-1 flex justify-center items-center">
|
||||
<div className="justify-center text-[--Neutrals-NeutralSlate950] text-sm font-medium font-['Inter'] leading-tight">Back</div>
|
||||
</div>
|
||||
</button>
|
||||
<button
|
||||
onClick={onNext}
|
||||
disabled={!selectedValue}
|
||||
className="flex-1 h-12 px-4 py-3.5 bg-[--Brand-Orange] rounded-[999px] outline outline-2 outline-offset-[-2px] outline-blue-400 flex justify-center items-center gap-1 overflow-hidden disabled:opacity-50 disabled:cursor-not-allowed hover:bg-[--Brand-Orange]/90 transition-colors"
|
||||
>
|
||||
<div className="px-1 flex justify-center items-center">
|
||||
<div className="justify-center text-white text-sm font-medium font-['Inter'] leading-tight">Next</div>
|
||||
<div className="justify-center text-[--Text-Gray-950] text-sm font-medium font-['Inter'] leading-tight">Back</div>
|
||||
</div>
|
||||
</button>
|
||||
<FigmaPrimaryButton text="Next" onClick={onNext} size="big" disabled={!selectedValue} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="px-3 py-1.5 left-[24px] top-[24px] absolute bg-[--Neutrals-NeutralSlate100] rounded-[50px] inline-flex justify-center items-center gap-2 overflow-hidden">
|
||||
<div className="justify-start text-[--Neutrals-NeutralSlate500] text-sm font-medium font-['Inter'] uppercase leading-none">
|
||||
<div className="justify-start text-[--Text-Gray-500] text-sm font-medium font-['Inter'] uppercase leading-none">
|
||||
{sectionPosition} of {totalInSection}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{canSkip && (
|
||||
<div className="px-3 py-1.5 right-[24px] top-[24px] absolute bg-[--Neutrals-NeutralSlate100] rounded-[50px] inline-flex justify-center items-center gap-2 overflow-hidden cursor-pointer hover:bg-[--Neutrals-NeutralSlate100] transition-colors">
|
||||
<div className="justify-start text-[--Neutrals-NeutralSlate500] text-sm font-medium font-['Inter'] leading-none">Skip</div>
|
||||
<div className="justify-start text-[--Text-Gray-500] text-sm font-medium font-['Inter'] leading-none">Skip</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -331,7 +309,7 @@ export const FigmaOnboardingMultipleChoice: React.FC<FigmaOnboardingMultipleChoi
|
||||
<div className="p-4 bg-[--Neutrals-NeutralSlate100] rounded-[50px] inline-flex justify-center items-center gap-2 overflow-hidden">
|
||||
{renderProgressDots()}
|
||||
</div>
|
||||
<div className="self-stretch text-center justify-start text-[--Neutrals-NeutralSlate500] text-base font-medium font-['Neue_Montreal'] leading-normal">
|
||||
<div className="self-stretch text-center justify-start text-[--Text-Gray-500] text-base font-medium font-['Neue_Montreal'] leading-normal">
|
||||
{sectionName}
|
||||
</div>
|
||||
</div>
|
||||
@@ -371,15 +349,15 @@ export const FigmaOnboardingForm: React.FC<FigmaOnboardingFormProps> = ({
|
||||
<div className="w-[1440px] h-[810px] px-[488px] py-32 bg-[--Neutrals-NeutralSlate0] inline-flex flex-col justify-center items-center gap-9">
|
||||
<div className="w-full max-w-[464px] min-w-[464px] flex flex-col justify-start items-start gap-12">
|
||||
<div className="self-stretch flex flex-col justify-start items-start gap-8">
|
||||
<div className="self-stretch text-center justify-start text-[--Neutrals-NeutralSlate950] text-2xl font-medium font-['Neue_Montreal'] leading-normal">
|
||||
<div className="self-stretch text-center justify-start text-[--Text-Gray-950] text-2xl font-medium font-['Neue_Montreal'] leading-normal">
|
||||
Company Details
|
||||
</div>
|
||||
<div className="self-stretch flex flex-col justify-start items-start gap-6">
|
||||
<div className="self-stretch flex flex-col justify-start items-start gap-2">
|
||||
<div className="self-stretch inline-flex justify-start items-center gap-0.5">
|
||||
<div className="justify-start text-[--Neutrals-NeutralSlate950] text-sm font-normal font-['Inter'] leading-tight">Company Logo</div>
|
||||
<div className="justify-start text-[--Text-Gray-950] text-sm font-normal font-['Inter'] leading-tight">Company Logo</div>
|
||||
</div>
|
||||
<div className="self-stretch p-4 rounded-3xl outline outline-1 outline-offset-[-1px] outline-[--$1] inline-flex justify-start items-center gap-4">
|
||||
<div className="self-stretch p-4 rounded-3xl outline outline-1 outline-offset-[-1px] outline-[--Neutrals-NeutralSlate200] inline-flex justify-start items-center gap-4">
|
||||
<div className="w-16 h-16 relative bg-gray-200 rounded-[250px]">
|
||||
<div className="w-16 h-16 left-0 top-0 absolute opacity-10 rounded-[250px]" />
|
||||
{companyLogo && (
|
||||
@@ -391,10 +369,10 @@ export const FigmaOnboardingForm: React.FC<FigmaOnboardingFormProps> = ({
|
||||
<label className="flex justify-start items-center gap-2 cursor-pointer hover:opacity-80 transition-opacity">
|
||||
<div>
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M14 2H2M12 8.66667L8 4.66667M8 4.66667L4 8.66667M8 4.66667V14" stroke="var(--Neutrals-NeutralSlate950, #0A0D12)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<path d="M14 2H2M12 8.66667L8 4.66667M8 4.66667L4 8.66667M8 4.66667V14" stroke="var(--Neutrals-NeutralSlate950)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</svg>
|
||||
</div>
|
||||
<div className="justify-center text-[--Neutrals-NeutralSlate950] text-sm font-medium font-['Inter'] leading-tight">Upload image</div>
|
||||
<div className="justify-center text-[--Text-Gray-950] text-sm font-medium font-['Inter'] leading-tight">Upload image</div>
|
||||
<input
|
||||
type="file"
|
||||
accept="image/*"
|
||||
@@ -408,7 +386,7 @@ export const FigmaOnboardingForm: React.FC<FigmaOnboardingFormProps> = ({
|
||||
</div>
|
||||
<div className="self-stretch flex flex-col justify-start items-start gap-2">
|
||||
<div className="self-stretch inline-flex justify-start items-center gap-0.5">
|
||||
<div className="justify-start text-[--Neutrals-NeutralSlate900] text-sm font-normal font-['Inter'] leading-tight">Your Name</div>
|
||||
<div className="justify-start text-[--Text-Gray-900] text-sm font-normal font-['Inter'] leading-tight">Your Name</div>
|
||||
<div className="justify-start text-[--Brand-Orange] text-sm font-medium font-['Inter'] leading-tight">*</div>
|
||||
</div>
|
||||
<div className="self-stretch flex flex-col justify-start items-start gap-1">
|
||||
@@ -417,7 +395,7 @@ export const FigmaOnboardingForm: React.FC<FigmaOnboardingFormProps> = ({
|
||||
type="text"
|
||||
value={yourName}
|
||||
onChange={(e) => onYourNameChange(e.target.value)}
|
||||
className="flex-1 bg-transparent text-[--Neutrals-NeutralSlate950] text-sm font-normal font-['Inter'] leading-tight placeholder:text-[--Neutrals-NeutralSlate950] outline-none"
|
||||
className="flex-1 bg-transparent text-[--Text-Gray-950] text-sm font-normal font-['Inter'] leading-tight placeholder:text-[--Text-Gray-950] outline-none"
|
||||
placeholder="John Doe"
|
||||
/>
|
||||
</div>
|
||||
@@ -425,7 +403,7 @@ export const FigmaOnboardingForm: React.FC<FigmaOnboardingFormProps> = ({
|
||||
</div>
|
||||
<div className="self-stretch flex flex-col justify-start items-start gap-2">
|
||||
<div className="self-stretch inline-flex justify-start items-center gap-0.5">
|
||||
<div className="justify-start text-[--Neutrals-NeutralSlate900] text-sm font-normal font-['Inter'] leading-tight">Company Name</div>
|
||||
<div className="justify-start text-[--Text-Gray-900] text-sm font-normal font-['Inter'] leading-tight">Company Name</div>
|
||||
<div className="justify-start text-[--Brand-Orange] text-sm font-medium font-['Inter'] leading-tight">*</div>
|
||||
</div>
|
||||
<div className="self-stretch flex flex-col justify-start items-start gap-1">
|
||||
@@ -434,7 +412,7 @@ export const FigmaOnboardingForm: React.FC<FigmaOnboardingFormProps> = ({
|
||||
type="text"
|
||||
value={companyName}
|
||||
onChange={(e) => onCompanyNameChange(e.target.value)}
|
||||
className="flex-1 bg-transparent text-[--Neutrals-NeutralSlate950] text-sm font-normal font-['Inter'] leading-tight placeholder:text-[--Neutrals-NeutralSlate950] outline-none"
|
||||
className="flex-1 bg-transparent text-[--Text-Gray-950] text-sm font-normal font-['Inter'] leading-tight placeholder:text-[--Text-Gray-950] outline-none"
|
||||
placeholder="Doe Enterprises"
|
||||
/>
|
||||
</div>
|
||||
@@ -454,4 +432,4 @@ export const FigmaOnboardingForm: React.FC<FigmaOnboardingFormProps> = ({
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
@@ -3,10 +3,10 @@ import React from 'react';
|
||||
type AlertVariant = 'info' | 'success' | 'warning' | 'error';
|
||||
|
||||
const variantStyles: Record<AlertVariant, string> = {
|
||||
info: 'bg-blue-50 text-blue-800 border-blue-300 dark:bg-blue-900/30 dark:text-blue-200 dark:border-blue-700',
|
||||
success: 'bg-green-50 text-green-800 border-green-300 dark:bg-green-900/30 dark:text-green-200 dark:border-green-700',
|
||||
warning: 'bg-amber-50 text-amber-800 border-amber-300 dark:bg-amber-900/30 dark:text-amber-200 dark:border-amber-700',
|
||||
error: 'bg-red-50 text-red-800 border-red-300 dark:bg-red-900/30 dark:text-red-200 dark:border-red-700'
|
||||
info: 'bg-blue-50 text-blue-800 border-blue-300',
|
||||
success: 'bg-green-50 text-green-800 border-green-300',
|
||||
warning: 'bg-amber-50 text-amber-800 border-amber-300',
|
||||
error: 'bg-red-50 text-red-800 border-red-300'
|
||||
};
|
||||
|
||||
export interface AlertProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
|
||||
@@ -10,7 +10,7 @@ export const Breadcrumbs: React.FC<{ items: BreadcrumbItem[]; onNavigate?: (href
|
||||
{item.href ? (
|
||||
<button
|
||||
onClick={() => onNavigate?.(item.href!)}
|
||||
className="text-blue-600 hover:underline dark:text-blue-400"
|
||||
className="text-blue-600 hover:underline"
|
||||
>{item.label}</button>
|
||||
) : (
|
||||
<span className="text-[--text-primary] font-medium">{item.label}</span>
|
||||
|
||||
@@ -18,7 +18,7 @@ const isLocalhost = typeof window !== 'undefined' &&
|
||||
|
||||
export const API_URL = 'https://us-central1-auditly-c0027.cloudfunctions.net';
|
||||
// export const API_URL = 'https://us-central1-auditly-consulting.cloudfunctions.net';
|
||||
// export const API_URL = 'http://127.0.0.1:5002/auditly-consulting/us-central1';
|
||||
// export const API_URL = 'http://127.0.0.1:5002/auditly-c0027/us-central1';
|
||||
|
||||
// Log URL configuration in development
|
||||
if (import.meta.env.DEV) {
|
||||
@@ -341,5 +341,5 @@ export const SAMPLE_COMPANY_REPORT: CompanyReport = {
|
||||
]
|
||||
}
|
||||
],
|
||||
executiveSummary: `Welcome to Auditly! Generate your first AI-powered company report by inviting employees and completing the onboarding process.`
|
||||
};
|
||||
executiveSummary: `Welcome to Orbitly! Generate your first AI-powered company report by inviting employees and completing the onboarding process.`
|
||||
};
|
||||
|
||||
@@ -25,7 +25,7 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children
|
||||
|
||||
// Demo/OTP mode: Check localStorage for persisted session
|
||||
console.log('Checking for persisted OTP session');
|
||||
const sessionUser = localStorage.getItem('auditly_demo_session');
|
||||
const sessionUser = localStorage.getItem('orbitly_demo_session');
|
||||
if (sessionUser) {
|
||||
try {
|
||||
const parsedUser = JSON.parse(sessionUser);
|
||||
@@ -33,7 +33,7 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children
|
||||
setUser(parsedUser as User);
|
||||
} catch (error) {
|
||||
console.error('Failed to parse session user:', error);
|
||||
localStorage.removeItem('auditly_demo_session');
|
||||
localStorage.removeItem('orbitly_demo_session');
|
||||
setUser(null);
|
||||
}
|
||||
} else {
|
||||
@@ -65,9 +65,9 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children
|
||||
}
|
||||
|
||||
// Always clear all local session data
|
||||
localStorage.removeItem('auditly_demo_session');
|
||||
localStorage.removeItem('auditly_auth_token');
|
||||
localStorage.removeItem('auditly_selected_org');
|
||||
localStorage.removeItem('orbitly_demo_session');
|
||||
localStorage.removeItem('orbitly_auth_token');
|
||||
localStorage.removeItem('orbitly_selected_org');
|
||||
sessionStorage.clear();
|
||||
|
||||
setUser(null);
|
||||
@@ -148,8 +148,8 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children
|
||||
} as unknown as User;
|
||||
|
||||
setUser(mockUser);
|
||||
localStorage.setItem('auditly_demo_session', JSON.stringify(mockUser));
|
||||
localStorage.setItem('auditly_auth_token', data.token);
|
||||
localStorage.setItem('orbitly_demo_session', JSON.stringify(mockUser));
|
||||
localStorage.setItem('orbitly_auth_token', data.token);
|
||||
|
||||
return data;
|
||||
}, []);
|
||||
|
||||
@@ -26,7 +26,7 @@ interface OrgContextType {
|
||||
user?: User;
|
||||
orgId: string;
|
||||
employees: Employee[];
|
||||
submissions: Record<string, Submission>;
|
||||
submissions: Submission[];
|
||||
reports: Record<string, EmployeeReport>;
|
||||
loading: boolean;
|
||||
upsertOrg: (data: Partial<OrgData>) => Promise<void>;
|
||||
@@ -58,11 +58,11 @@ export const OrgProvider: React.FC<{ children: React.ReactNode; selectedOrgId: s
|
||||
const { user } = useAuth();
|
||||
const [org, setOrg] = useState<OrgData | null>(null);
|
||||
const [employees, setEmployees] = useState<Employee[]>([]);
|
||||
const [submissions, setSubmissions] = useState<Record<string, Submission>>({});
|
||||
const [submissions, setSubmissions] = useState<Submission[]>([]);
|
||||
const [reports, setReports] = useState<Record<string, EmployeeReport>>({});
|
||||
const [, setReportVersions] = useState<Record<string, Array<{ id: string; createdAt: number; report: EmployeeReport }>>>({});
|
||||
const [, setCompanyReports] = useState<Array<{ id: string; createdAt: number; summary: string }>>([]);
|
||||
const [, setFullCompanyReports] = useState<CompanyReport[]>([]);
|
||||
const [reportVersions, setReportVersions] = useState<Record<string, Array<{ id: string; createdAt: number; report: EmployeeReport }>>>({});
|
||||
const [companyReports, setCompanyReports] = useState<Array<{ id: string; createdAt: number; summary: string }>>([]);
|
||||
const [fullCompanyReports, setFullCompanyReports] = useState<CompanyReport[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
// Use the provided selectedOrgId instead of deriving from user
|
||||
@@ -85,7 +85,7 @@ export const OrgProvider: React.FC<{ children: React.ReactNode; selectedOrgId: s
|
||||
const [orgData, employeesData, submissionsData, reportsData, companyReportsData] = await Promise.allSettled([
|
||||
secureApi.getOrgData().catch(() => null),
|
||||
secureApi.getEmployees().catch(() => []),
|
||||
secureApi.getSubmissions().catch(() => ({})),
|
||||
secureApi.getSubmissions().catch(() => ([])),
|
||||
secureApi.getReports().catch(() => ({})),
|
||||
secureApi.getCompanyReports().catch(() => [])
|
||||
]);
|
||||
@@ -116,7 +116,7 @@ export const OrgProvider: React.FC<{ children: React.ReactNode; selectedOrgId: s
|
||||
setSubmissions(submissionsData.value);
|
||||
} else {
|
||||
console.warn('Could not load submissions');
|
||||
setSubmissions({});
|
||||
setSubmissions([]);
|
||||
}
|
||||
|
||||
// Process reports data
|
||||
@@ -291,7 +291,7 @@ export const OrgProvider: React.FC<{ children: React.ReactNode; selectedOrgId: s
|
||||
const seedInitialData = async () => {
|
||||
// Start with clean slate - let users invite their own employees and generate real data
|
||||
setEmployees([]);
|
||||
setSubmissions({});
|
||||
setSubmissions([]);
|
||||
setReports({});
|
||||
setFullCompanyReports([]);
|
||||
};
|
||||
|
||||
@@ -52,7 +52,7 @@ export const UserOrganizationsProvider: React.FC<{ children: React.ReactNode }>
|
||||
|
||||
// Initialize selected org from localStorage (persistent across sessions)
|
||||
useEffect(() => {
|
||||
const savedOrgId = localStorage.getItem('auditly_selected_org');
|
||||
const savedOrgId = localStorage.getItem('orbitly_selected_org');
|
||||
if (savedOrgId) {
|
||||
setSelectedOrgId(savedOrgId);
|
||||
}
|
||||
@@ -94,12 +94,12 @@ export const UserOrganizationsProvider: React.FC<{ children: React.ReactNode }>
|
||||
console.log('Switching to organization:', orgId);
|
||||
|
||||
// Clear any cached data when switching organizations for security
|
||||
sessionStorage.removeItem('auditly_cached_employees');
|
||||
sessionStorage.removeItem('auditly_cached_submissions');
|
||||
sessionStorage.removeItem('auditly_cached_reports');
|
||||
sessionStorage.removeItem('orbitly_cached_employees');
|
||||
sessionStorage.removeItem('orbitly_cached_submissions');
|
||||
sessionStorage.removeItem('orbitly_cached_reports');
|
||||
|
||||
setSelectedOrgId(orgId);
|
||||
localStorage.setItem('auditly_selected_org', orgId);
|
||||
localStorage.setItem('orbitly_selected_org', orgId);
|
||||
|
||||
// Dispatch event to notify other contexts about the org switch
|
||||
window.dispatchEvent(new CustomEvent('organizationChanged', {
|
||||
@@ -224,4 +224,4 @@ export const useUserOrganizations = () => {
|
||||
throw new Error('useUserOrganizations must be used within UserOrganizationsProvider');
|
||||
}
|
||||
return context;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -43,6 +43,7 @@ const Chat: React.FC = () => {
|
||||
const inputRef = useRef<HTMLTextAreaElement>(null);
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
const messagesEndRef = useRef<HTMLDivElement>(null);
|
||||
const textDisplayRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const [state, setState] = useState<ChatState>({
|
||||
messages: [],
|
||||
@@ -58,6 +59,8 @@ const Chat: React.FC = () => {
|
||||
const [currentInput, setCurrentInput] = useState('');
|
||||
const [selectedCategory, setSelectedCategory] = useState('Accountability');
|
||||
const [isInputFocused, setIsInputFocused] = useState(false);
|
||||
const [cursorPosition, setCursorPosition] = useState(0);
|
||||
const [cursorPixelPosition, setCursorPixelPosition] = useState({ x: 0, y: 0 });
|
||||
|
||||
// Auto-resize textarea function
|
||||
const adjustTextareaHeight = () => {
|
||||
@@ -69,12 +72,100 @@ const Chat: React.FC = () => {
|
||||
}
|
||||
};
|
||||
|
||||
// Track cursor position changes
|
||||
const handleCursorPositionChange = () => {
|
||||
if (inputRef.current) {
|
||||
const newPosition = inputRef.current.selectionStart || 0;
|
||||
setCursorPosition(newPosition);
|
||||
calculateCursorPixelPosition(newPosition);
|
||||
}
|
||||
};
|
||||
|
||||
// Calculate cursor pixel position using canvas text measurement
|
||||
const calculateCursorPixelPosition = (position: number) => {
|
||||
if (!textDisplayRef.current) return;
|
||||
|
||||
const text = currentInput || '';
|
||||
|
||||
if (!text) {
|
||||
// Position at start of placeholder text
|
||||
setCursorPixelPosition({ x: 0, y: 2 });
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a temporary span element to measure text width accurately
|
||||
const measureSpan = document.createElement('span');
|
||||
const computedStyle = window.getComputedStyle(textDisplayRef.current);
|
||||
|
||||
// Copy the relevant styles to the measurement span
|
||||
measureSpan.style.font = computedStyle.font;
|
||||
measureSpan.style.fontSize = computedStyle.fontSize;
|
||||
measureSpan.style.fontFamily = computedStyle.fontFamily;
|
||||
measureSpan.style.fontWeight = computedStyle.fontWeight;
|
||||
measureSpan.style.letterSpacing = computedStyle.letterSpacing;
|
||||
measureSpan.style.whiteSpace = 'pre'; // Preserve spaces
|
||||
measureSpan.style.visibility = 'hidden';
|
||||
measureSpan.style.position = 'absolute';
|
||||
measureSpan.style.left = '-9999px';
|
||||
|
||||
// Set the text content up to cursor position
|
||||
const textBeforeCursor = text.substring(0, position);
|
||||
measureSpan.textContent = textBeforeCursor;
|
||||
|
||||
// Append to body, measure, then remove
|
||||
document.body.appendChild(measureSpan);
|
||||
const textWidth = measureSpan.getBoundingClientRect().width;
|
||||
document.body.removeChild(measureSpan);
|
||||
|
||||
// Position cursor at the measured width
|
||||
setCursorPixelPosition({ x: textWidth, y: 2 });
|
||||
};
|
||||
|
||||
// Render text without inline cursor
|
||||
const renderText = () => {
|
||||
if (!currentInput) {
|
||||
return (
|
||||
<span className="text-[--Text-Gray-950]">Ask anything, use @ to tag staff and ask questions.</span>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<span className="text-[--Text-Gray-950]">{currentInput}</span>
|
||||
);
|
||||
};
|
||||
|
||||
// Render cursor as absolutely positioned overlay
|
||||
const renderCursorOverlay = () => {
|
||||
if (!isInputFocused) return null;
|
||||
|
||||
return (
|
||||
<div
|
||||
className="absolute pointer-events-none"
|
||||
style={{
|
||||
left: `${cursorPixelPosition.x}px`,
|
||||
top: `${cursorPixelPosition.y}px`,
|
||||
width: '2px',
|
||||
height: '16px',
|
||||
backgroundColor: 'var(--Neutrals-NeutralSlate600)',
|
||||
animation: 'blink 1s infinite',
|
||||
zIndex: 30
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!user) {
|
||||
navigate('/login');
|
||||
}
|
||||
}, [user, navigate]);
|
||||
|
||||
// Recalculate cursor position when input or cursor position changes
|
||||
useEffect(() => {
|
||||
if (isInputFocused) {
|
||||
calculateCursorPixelPosition(cursorPosition);
|
||||
}
|
||||
}, [currentInput, cursorPosition, isInputFocused]);
|
||||
|
||||
// Auto-scroll to bottom when new messages arrive
|
||||
useEffect(() => {
|
||||
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
|
||||
@@ -149,6 +240,7 @@ const Chat: React.FC = () => {
|
||||
}));
|
||||
|
||||
setCurrentInput('');
|
||||
setCursorPosition(0);
|
||||
|
||||
try {
|
||||
// Get mentioned employees' data for context
|
||||
@@ -208,15 +300,16 @@ const Chat: React.FC = () => {
|
||||
|
||||
const handleInputChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
||||
const value = e.target.value;
|
||||
const cursorPosition = e.target.selectionStart;
|
||||
const newCursorPosition = e.target.selectionStart;
|
||||
|
||||
setCurrentInput(value);
|
||||
setCursorPosition(newCursorPosition);
|
||||
|
||||
// Auto-resize textarea
|
||||
setTimeout(adjustTextareaHeight, 0);
|
||||
|
||||
// Enhanced @ mention detection for real-time search
|
||||
const beforeCursor = value.substring(0, cursorPosition);
|
||||
const beforeCursor = value.substring(0, newCursorPosition);
|
||||
const lastAtIndex = beforeCursor.lastIndexOf('@');
|
||||
|
||||
if (lastAtIndex !== -1) {
|
||||
@@ -253,8 +346,10 @@ const Chat: React.FC = () => {
|
||||
const beforeMention = currentInput.substring(0, state.mentionStartIndex);
|
||||
const afterCursor = currentInput.substring(inputRef.current?.selectionStart || currentInput.length);
|
||||
const newValue = `${beforeMention}@${employee.name} ${afterCursor}`;
|
||||
const newCursorPosition = beforeMention.length + employee.name.length + 2;
|
||||
|
||||
setCurrentInput(newValue);
|
||||
setCursorPosition(newCursorPosition);
|
||||
setState(prev => ({
|
||||
...prev,
|
||||
showEmployeeMenu: false,
|
||||
@@ -265,7 +360,6 @@ const Chat: React.FC = () => {
|
||||
// Focus back to input and position cursor after the mention
|
||||
setTimeout(() => {
|
||||
if (inputRef.current) {
|
||||
const newCursorPosition = beforeMention.length + employee.name.length + 2;
|
||||
inputRef.current.focus();
|
||||
inputRef.current.setSelectionRange(newCursorPosition, newCursorPosition);
|
||||
}
|
||||
@@ -313,6 +407,9 @@ const Chat: React.FC = () => {
|
||||
handleSendMessage();
|
||||
}
|
||||
// Allow Shift+Enter and Alt+Enter for line breaks (default behavior)
|
||||
|
||||
// Track cursor position for arrow keys and other navigation
|
||||
setTimeout(handleCursorPositionChange, 0);
|
||||
};
|
||||
|
||||
const handleFileUpload = async (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
@@ -352,6 +449,7 @@ const Chat: React.FC = () => {
|
||||
|
||||
const handleQuestionClick = (question: string) => {
|
||||
setCurrentInput(question);
|
||||
setCursorPosition(question.length);
|
||||
};
|
||||
|
||||
const renderEmployeeMenu = () => {
|
||||
@@ -360,7 +458,7 @@ const Chat: React.FC = () => {
|
||||
return (
|
||||
<div className="absolute bottom-full left-[285px] mb-2 w-64 p-2 bg-[--Neutrals-NeutralSlate0] rounded-2xl shadow-[0px_1px_4px_4px_rgba(14,18,27,0.08)] border border-[--Neutrals-NeutralSlate200] max-h-64 overflow-y-auto z-50">
|
||||
{state.mentionQuery && (
|
||||
<div className="px-3 py-2 text-xs text-[--Neutrals-NeutralSlate500] border-b border-[--Neutrals-NeutralSlate100]">
|
||||
<div className="px-3 py-2 text-xs text-[--Text-Gray-500] border-b border-[--Neutrals-NeutralSlate100]">
|
||||
{filteredEmployees.length} employee{filteredEmployees.length !== 1 ? 's' : ''} found
|
||||
</div>
|
||||
)}
|
||||
@@ -370,7 +468,7 @@ const Chat: React.FC = () => {
|
||||
onClick={() => handleEmployeeSelect({ id: employee.id, name: employee.name })}
|
||||
onMouseEnter={() => setState(prev => ({ ...prev, selectedEmployeeIndex: index }))}
|
||||
className={`px-3 py-2 rounded-xl flex items-center space-x-3 cursor-pointer transition-colors ${index === state.selectedEmployeeIndex
|
||||
? 'bg-[--Neutrals-NeutralSlate100] text-[--Neutrals-NeutralSlate950]'
|
||||
? 'bg-[--Neutrals-NeutralSlate100] text-[--Text-Gray-950]'
|
||||
: 'hover:bg-[--Neutrals-NeutralSlate50]'
|
||||
}`}
|
||||
>
|
||||
@@ -378,11 +476,11 @@ const Chat: React.FC = () => {
|
||||
{employee.initials || employee.name.split(' ').map(n => n[0]).join('').toUpperCase()}
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className={`font-medium text-sm text-[--Neutrals-NeutralSlate950] truncate ${index === state.selectedEmployeeIndex ? 'font-medium' : 'font-normal'
|
||||
<div className={`font-medium text-sm text-[--Text-Gray-950] truncate ${index === state.selectedEmployeeIndex ? 'font-medium' : 'font-normal'
|
||||
}`}>
|
||||
{employee.name}
|
||||
</div>
|
||||
<div className="text-xs text-[--Neutrals-NeutralSlate500] truncate">
|
||||
<div className="text-xs text-[--Text-Gray-500] truncate">
|
||||
{employee.role || employee.email}
|
||||
</div>
|
||||
</div>
|
||||
@@ -408,7 +506,7 @@ const Chat: React.FC = () => {
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-1 justify-start text-[--Neutrals-NeutralSlate950] text-sm font-medium font-['Inter'] leading-tight">{file.name}</div>
|
||||
<div className="flex-1 justify-start text-[--Text-Gray-950] text-sm font-medium font-['Inter'] leading-tight">{file.name}</div>
|
||||
</div>
|
||||
<div onClick={() => removeFile(index)} className="cursor-pointer">
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
@@ -427,7 +525,7 @@ const Chat: React.FC = () => {
|
||||
return (
|
||||
<div className="h-full flex flex-col p-6">
|
||||
<div className="w-fit flex-1 flex flex-shrink-0 flex-col justify-center items-center gap-4 self-center">
|
||||
<div className="justify-start text-[--Neutrals-NeutralSlate800] text-2xl font-medium font-['Neue_Montreal'] leading-normal">What would you like to understand?</div>
|
||||
<div className="justify-start text-[--Text-Gray-800] text-2xl font-medium font-['Neue_Montreal'] leading-normal">What would you like to understand?</div>
|
||||
<div className="p-1 bg-[--Neutrals-NeutralSlate100] rounded-xl inline-flex justify-start items-center gap-1">
|
||||
{categories.map((category) => (
|
||||
<div
|
||||
@@ -437,7 +535,7 @@ const Chat: React.FC = () => {
|
||||
}`}
|
||||
>
|
||||
<div className="px-0.5 flex justify-center items-center">
|
||||
<div className={`justify-start text-xs font-medium font-['Inter'] leading-none ${selectedCategory === category ? 'text-[--Neutrals-NeutralSlate900]' : 'text-[--Neutrals-NeutralSlate600]'
|
||||
<div className={`justify-start text-xs font-medium font-['Inter'] leading-none ${selectedCategory === category ? 'text-[--Text-Gray-900]' : 'text-[--Text-Gray-900]'
|
||||
}`}>{category}</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -463,20 +561,20 @@ const Chat: React.FC = () => {
|
||||
</defs>
|
||||
</svg>
|
||||
</div>
|
||||
<div className="self-stretch justify-start text-[--Neutrals-NeutralSlate800] text-base font-normal font-['Inter'] leading-normal">{question}</div>
|
||||
<div className="self-stretch justify-start text-[--Text-Gray-800] text-base font-normal font-['Inter'] leading-normal">{question}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Enhanced instructions for @ mentions */}
|
||||
<div className="text-center text-[--Neutrals-NeutralSlate500] mt-8 max-w-[600px] mx-auto">
|
||||
<div className="text-center text-[--Text-Gray-500] mt-8 max-w-[600px] mx-auto">
|
||||
<div className="text-sm mb-2">Ask about your team, company data, or get insights.</div>
|
||||
<div className="text-sm">Use <span className="bg-[--Neutrals-NeutralSlate100] px-2 py-1 rounded text-[--Neutrals-NeutralSlate800] font-mono">@</span> to mention team members.</div>
|
||||
<div className="text-sm">Use <span className="bg-[--Neutrals-NeutralSlate100] px-2 py-1 rounded text-[--Text-Gray-800] font-mono">@</span> to mention team members.</div>
|
||||
|
||||
{/* Sample questions */}
|
||||
<div className="mt-6 space-y-2">
|
||||
<div className="text-sm font-medium text-[--Neutrals-NeutralSlate700] mb-3">Try asking:</div>
|
||||
<div className="text-sm font-medium text-[--Text-Gray-700] mb-3">Try asking:</div>
|
||||
<div className="space-y-2 text-sm">
|
||||
<div className="bg-[--Neutrals-NeutralSlate50] p-3 rounded-lg text-left max-w-md mx-auto">
|
||||
"How is the team performing overall?"
|
||||
@@ -508,14 +606,14 @@ const Chat: React.FC = () => {
|
||||
<div key={message.id} className={`flex ${message.role === 'user' ? 'justify-end' : 'justify-start'}`}>
|
||||
<div className={`max-w-[80%] p-4 rounded-2xl ${message.role === 'user'
|
||||
? 'bg-[--Brand-Orange] text-white'
|
||||
: 'bg-[--Neutrals-NeutralSlate100] text-[--Neutrals-NeutralSlate950] border border-[--Neutrals-NeutralSlate200]'
|
||||
: 'bg-[--Neutrals-NeutralSlate100] text-[--Text-Gray-950] border border-[--Neutrals-NeutralSlate200]'
|
||||
}`}>
|
||||
{message.role === 'user' ? (
|
||||
<div className="text-sm leading-relaxed whitespace-pre-wrap">{message.content}</div>
|
||||
) : (
|
||||
<MarkdownRenderer
|
||||
content={message.content}
|
||||
className="text-sm leading-relaxed"
|
||||
<MarkdownRenderer
|
||||
content={message.content}
|
||||
className="text-sm leading-relaxed"
|
||||
/>
|
||||
)}
|
||||
{message.attachments && message.attachments.length > 0 && (
|
||||
@@ -523,7 +621,7 @@ const Chat: React.FC = () => {
|
||||
{message.attachments.map((file, index) => (
|
||||
<div key={index} className={`inline-flex items-center gap-2 px-3 py-1.5 rounded-lg text-xs ${message.role === 'user'
|
||||
? 'bg-white/20 text-white/90'
|
||||
: 'bg-[--Neutrals-NeutralSlate200] text-[--Neutrals-NeutralSlate700]'
|
||||
: 'bg-[--Neutrals-NeutralSlate200] text-[--Text-Gray-700]'
|
||||
}`}>
|
||||
<div className="w-4 h-4">
|
||||
{file.type.startsWith('image/') ? (
|
||||
@@ -541,11 +639,11 @@ const Chat: React.FC = () => {
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
<div className={`text-xs mt-2 ${message.role === 'user' ? 'text-white/70' : 'text-[--Neutrals-NeutralSlate500]'}`}>
|
||||
<div className={`text-xs mt-2 ${message.role === 'user' ? 'text-white/70' : 'text-[--Text-Gray-500]'}`}>
|
||||
{message.timestamp.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
|
||||
</div>
|
||||
{message.mentions && message.mentions.length > 0 && (
|
||||
<div className={`text-xs mt-1 ${message.role === 'user' ? 'text-white/60' : 'text-[--Neutrals-NeutralSlate400]'}`}>
|
||||
<div className={`text-xs mt-1 ${message.role === 'user' ? 'text-white/60' : 'text-[--Text-Gray-400]'}`}>
|
||||
Mentioned: {message.mentions.map(m => m.name).join(', ')}
|
||||
</div>
|
||||
)}
|
||||
@@ -554,7 +652,7 @@ const Chat: React.FC = () => {
|
||||
))}
|
||||
{state.isLoading && (
|
||||
<div className="flex justify-start">
|
||||
<div className="bg-[--Neutrals-NeutralSlate100] text-[--Neutrals-NeutralSlate950] border border-[--Neutrals-NeutralSlate200] p-4 rounded-2xl">
|
||||
<div className="bg-[--Neutrals-NeutralSlate100] text-[--Text-Gray-950] border border-[--Neutrals-NeutralSlate200] p-4 rounded-2xl">
|
||||
<div className="flex items-center space-x-2">
|
||||
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-[--Brand-Orange]"></div>
|
||||
<span className="text-sm">AI is analyzing...</span>
|
||||
@@ -578,29 +676,12 @@ const Chat: React.FC = () => {
|
||||
return (
|
||||
<div className="self-stretch pl-5 pr-3 pt-5 pb-3 relative bg-[--Neutrals-NeutralSlate50] rounded-3xl flex flex-col justify-start items-start gap-4 min-h-[80px]">
|
||||
{renderUploadedFiles()}
|
||||
<div className="self-stretch justify-start text-[--Neutrals-NeutralSlate500] text-base font-normal font-['Inter'] leading-normal relative pointer-events-none min-h-[24px] whitespace-pre-wrap">
|
||||
<span className={`${currentInput ? 'text-[--Neutrals-NeutralSlate950]' : 'text-[--Neutrals-NeutralSlate500]'}`}>
|
||||
{currentInput || "Ask anything, use @ to tag staff and ask questions."}
|
||||
</span>
|
||||
{/* Custom blinking cursor when focused and has text */}
|
||||
{currentInput && isInputFocused && (
|
||||
<span
|
||||
className="inline-block w-0.5 h-6 bg-[--Neutrals-NeutralSlate800] ml-0.5"
|
||||
style={{
|
||||
animation: 'blink 1s infinite',
|
||||
verticalAlign: 'text-top'
|
||||
}}
|
||||
></span>
|
||||
)}
|
||||
{/* Custom blinking cursor when focused and no text */}
|
||||
{!currentInput && isInputFocused && (
|
||||
<span
|
||||
className="absolute left-0 top-0 w-0.5 h-6 bg-[--Neutrals-NeutralSlate800]"
|
||||
style={{
|
||||
animation: 'blink 1s infinite'
|
||||
}}
|
||||
></span>
|
||||
)}
|
||||
<div
|
||||
ref={textDisplayRef}
|
||||
className="self-stretch justify-start text-[--Text-Gray-500] text-base font-normal font-['Inter'] leading-normal relative pointer-events-none min-h-[24px] whitespace-pre-wrap"
|
||||
>
|
||||
{renderText()}
|
||||
{renderCursorOverlay()}
|
||||
</div>
|
||||
|
||||
<div className="self-stretch inline-flex justify-between items-center relative z-20">
|
||||
@@ -644,7 +725,7 @@ const Chat: React.FC = () => {
|
||||
</div>
|
||||
|
||||
{/* Enhanced help text for keyboard navigation */}
|
||||
<div className="absolute bottom-2 right-16 text-xs text-[--Neutrals-NeutralSlate400]">
|
||||
<div className="absolute bottom-2 right-16 text-xs text-[--Text-Gray-400]">
|
||||
{state.showEmployeeMenu ? '↑↓ Navigate • Enter/Tab Select • Esc Cancel' : 'Enter to send • Shift+Enter new line'}
|
||||
</div>
|
||||
|
||||
@@ -656,6 +737,9 @@ const Chat: React.FC = () => {
|
||||
onKeyDown={handleKeyDown}
|
||||
onFocus={() => setIsInputFocused(true)}
|
||||
onBlur={() => setIsInputFocused(false)}
|
||||
onClick={handleCursorPositionChange}
|
||||
onKeyUp={handleCursorPositionChange}
|
||||
onSelect={handleCursorPositionChange}
|
||||
className="absolute inset-0 w-full resize-none outline-none bg-transparent text-transparent caret-transparent text-base font-normal font-['Inter'] leading-normal p-5 z-10 overflow-hidden"
|
||||
placeholder=""
|
||||
style={{
|
||||
@@ -681,15 +765,15 @@ const Chat: React.FC = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex h-screen bg-[--Neutrals-NeutralSlate0]">
|
||||
<Sidebar companyName={org?.companyName || "Auditly"} />
|
||||
<main className="flex-1 overflow-hidden flex flex-col">
|
||||
<div className="flex-1 overflow-hidden">
|
||||
<div className="w-[100vw] h-[100vh] p-4 bg-[--Neutrals-NeutralSlate200] inline-flex justify-start items-start overflow-y-hidden">
|
||||
<div className="flex-1 h-full rounded-3xl self-stretch shadow-[0px_0px_15px_0px_rgba(0,0,0,0.08)] flex justify-between items-start overflow-y-auto relative">
|
||||
<Sidebar companyName={org?.companyName || "Orbitly"} />
|
||||
<main className="h-full flex-1 overflow-y-auto bg-[--Neutrals-NeutralSlate0]">
|
||||
{renderChatInterface()}
|
||||
</div>
|
||||
</main>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Chat;
|
||||
export default Chat;
|
||||
|
||||
@@ -210,7 +210,7 @@ const CompanyWiki: React.FC = () => {
|
||||
<div className="flex-1 flex items-center justify-center">
|
||||
<div className="text-center">
|
||||
<div className="animate-spin w-8 h-8 border-2 border-Brand-Orange border-t-transparent rounded-full mx-auto mb-4"></div>
|
||||
<p className="text-[--$1]">Loading company wiki...</p>
|
||||
<p className="text-[--Text-Gray-600]">Loading company wiki...</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -245,7 +245,7 @@ const CompanyWiki: React.FC = () => {
|
||||
{/* Table of Contents Sidebar */}
|
||||
<div className="flex-1 self-stretch max-w-64 min-w-64 border-r border-[--Neutrals-NeutralSlate200] inline-flex flex-col justify-start items-start">
|
||||
<div className="self-stretch p-5 inline-flex justify-start items-center gap-2.5">
|
||||
<div className="flex-1 justify-start text-[--$1] text-base font-medium font-['Inter'] leading-normal">Table of contents</div>
|
||||
<div className="flex-1 justify-start text-[--Text-Gray-950] text-base font-medium font-['Inter'] leading-normal">Table of contents</div>
|
||||
</div>
|
||||
<div className="self-stretch px-3 flex flex-col justify-start items-start gap-1.5">
|
||||
{[1, 2, 3, 4, 5, 6, 7].map((section, index) => (
|
||||
@@ -255,9 +255,9 @@ const CompanyWiki: React.FC = () => {
|
||||
onClick={() => setActiveSection(section)}
|
||||
>
|
||||
<div className={`h-5 p-0.5 ${activeSection === section ? 'bg-[--Brand-Orange]' : 'bg-[--Neutrals-NeutralSlate100]'} rounded-[999px] inline-flex flex-col justify-center items-center gap-0.5 overflow-hidden`}>
|
||||
<div className={`w-4 text-center justify-start ${activeSection === section ? 'text-[--Neutrals-NeutralSlate0]' : 'text-[--Neutrals-NeutralSlate950]'} text-xs ${activeSection === section ? 'font-medium' : 'font-normal'} font-['Inter'] leading-none`}>{section}</div>
|
||||
<div className={`w-4 text-center justify-start ${activeSection === section ? 'text-[--Other-White]' : 'text-[--Text-Dark-950]'} text-xs ${activeSection === section ? 'font-medium' : 'font-normal'} font-['Inter'] leading-none`}>{section}</div>
|
||||
</div>
|
||||
<div className={`flex-1 justify-start ${activeSection === section ? 'text-[--Neutrals-NeutralSlate800]' : 'text-[--Neutrals-NeutralSlate500]'} text-xs font-medium font-['Inter'] leading-none`}>
|
||||
<div className={`flex-1 justify-start ${activeSection === section ? 'text-[--Text-Gray-800]' : 'text-[--Text-Gray-800]'} text-xs font-medium font-['Inter'] leading-none`}>
|
||||
{getSectionTitle(section)}
|
||||
</div>
|
||||
</div>
|
||||
@@ -268,7 +268,7 @@ const CompanyWiki: React.FC = () => {
|
||||
{/* Questions Content */}
|
||||
<div className="flex-1 self-stretch inline-flex flex-col justify-start items-start">
|
||||
<div className="self-stretch p-5 inline-flex justify-start items-center gap-2.5">
|
||||
<div className="flex-1 justify-start text-[--Neutrals-NeutralSlate800] text-base font-medium font-['Inter'] leading-normal">
|
||||
<div className="flex-1 justify-start text-[--Text-Gray-800] text-base font-medium font-['Inter'] leading-normal">
|
||||
{getSectionTitle(activeSection)}
|
||||
</div>
|
||||
</div>
|
||||
@@ -277,16 +277,16 @@ const CompanyWiki: React.FC = () => {
|
||||
<div key={qa.field} className="self-stretch p-3 bg-[--Neutrals-NeutralSlate100] rounded-2xl shadow-[0px_1px_2px_0px_rgba(0,0,0,0.02)] flex flex-col justify-center items-start gap-2 overflow-hidden">
|
||||
<div className="self-stretch px-3 py-px inline-flex justify-start items-center gap-2.5">
|
||||
<div className="flex-1 flex justify-center items-center gap-3">
|
||||
<div className="w-3 self-stretch justify-start text-[--Neutrals-NeutralSlate300] text-base font-semibold font-['Inter'] leading-normal">Q</div>
|
||||
<div className="flex-1 justify-start text-[--Neutrals-NeutralSlate600] text-sm font-medium font-['Inter'] leading-tight">
|
||||
<div className="w-3 self-stretch justify-start text-[--Text-Gray-300] text-base font-semibold font-['Inter'] leading-normal">Q</div>
|
||||
<div className="flex-1 justify-start text-[--Text-Gray-600] text-sm font-medium font-['Inter'] leading-tight">
|
||||
{qa.question}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="self-stretch px-3 py-2 bg-[--Neutrals-NeutralSlate0] rounded-[10px] inline-flex justify-between items-center">
|
||||
<div className="flex-1 flex justify-start items-start gap-3">
|
||||
<div className="w-3.5 h-6 justify-center text-[--$1] text-base font-semibold font-['Inter'] leading-normal">A</div>
|
||||
<div className="flex-1 justify-start text-[--$1] text-base font-normal font-['Inter'] leading-normal whitespace-pre-wrap">
|
||||
<div className="w-3.5 h-6 justify-center text-[--Text-Gray-300] text-base font-semibold font-['Inter'] leading-normal">A</div>
|
||||
<div className="flex-1 justify-start text-[--Text-Gray-800] text-base font-normal font-['Inter'] leading-normal whitespace-pre-wrap">
|
||||
{qa.answer}
|
||||
</div>
|
||||
</div>
|
||||
@@ -301,4 +301,4 @@ const CompanyWiki: React.FC = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default CompanyWiki;
|
||||
export default CompanyWiki;
|
||||
|
||||
@@ -119,7 +119,7 @@ const CompanyWiki: React.FC = () => {
|
||||
<div className="flex-1 flex items-center justify-center">
|
||||
<div className="text-center">
|
||||
<div className="animate-spin w-8 h-8 border-2 border-Brand-Orange border-t-transparent rounded-full mx-auto mb-4"></div>
|
||||
<p className="text-[--$1]">Loading company wiki...</p>
|
||||
<p className="text-[Neutrals-NeutralSlate600]">Loading company wiki...</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -149,10 +149,10 @@ const CompanyWiki: React.FC = () => {
|
||||
<div className="w-[1440px] h-[810px] p-4 bg-[--Neutrals-NeutralSlate200] inline-flex justify-start items-start overflow-hidden">
|
||||
<div className="flex-1 self-stretch rounded-3xl shadow-[0px_0px_15px_0px_rgba(0,0,0,0.08)] flex justify-between items-start overflow-hidden">
|
||||
{/* Left Sidebar - Company Navigation */}
|
||||
<div className="w-64 self-stretch max-w-64 min-w-64 px-3 pt-4 pb-3 bg-[--Neutrals-NeutralSlate0] border-r border-[--$1] inline-flex flex-col justify-between items-center overflow-hidden">
|
||||
<div className="w-64 self-stretch max-w-64 min-w-64 px-3 pt-4 pb-3 bg-[--Neutrals-NeutralSlate0] border-r border-[--Neutrals-NeutralSlate200] inline-flex flex-col justify-between items-center overflow-hidden">
|
||||
{/* Company Header */}
|
||||
<div className="self-stretch flex flex-col justify-start items-start gap-5">
|
||||
<div className="w-60 pl-2 pr-4 py-2 bg-[--Neutrals-NeutralSlate0] rounded-3xl outline outline-1 outline-offset-[-1px] outline-[--$1] inline-flex justify-between items-center overflow-hidden">
|
||||
<div className="w-60 pl-2 pr-4 py-2 bg-[--Neutrals-NeutralSlate0] rounded-3xl outline outline-1 outline-offset-[-1px] outline-[--Neutrals-NeutralSlate200] inline-flex justify-between items-center overflow-hidden">
|
||||
<div className="flex-1 flex justify-start items-center gap-2">
|
||||
<div className="w-8 h-8 rounded-full flex justify-start items-center gap-2.5">
|
||||
<div className="w-8 h-8 relative bg-Brand-Orange rounded-full outline outline-[1.60px] outline-offset-[-1.60px] outline-white/10 overflow-hidden">
|
||||
@@ -160,7 +160,7 @@ const CompanyWiki: React.FC = () => {
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-1 inline-flex flex-col justify-start items-start gap-0.5">
|
||||
<div className="self-stretch justify-start text-[--$1] text-base font-medium font-['Inter'] leading-normal">
|
||||
<div className="self-stretch justify-start text-[--Neutrals-NeutralSlate950] text-base font-medium font-['Inter'] leading-normal">
|
||||
{onboardingData?.companyName || 'Company Name'}
|
||||
</div>
|
||||
</div>
|
||||
@@ -171,19 +171,19 @@ const CompanyWiki: React.FC = () => {
|
||||
<div className="self-stretch flex flex-col justify-start items-start gap-5">
|
||||
<div className="self-stretch flex flex-col justify-start items-start gap-1.5">
|
||||
<div className="w-60 px-4 py-2.5 bg-[--Neutrals-NeutralSlate100] rounded-[34px] inline-flex justify-start items-center gap-2">
|
||||
<div className="justify-start text-[--$1] text-sm font-medium font-['Inter'] leading-tight">Company Wiki</div>
|
||||
<div className="justify-start text-[--Neutrals-NeutralSlate950] text-sm font-medium font-['Inter'] leading-tight">Company Wiki</div>
|
||||
</div>
|
||||
<div className="w-60 px-4 py-2.5 rounded-[34px] inline-flex justify-start items-center gap-2">
|
||||
<div className="justify-start text-[--$1] text-sm font-medium font-['Inter'] leading-tight">Submissions</div>
|
||||
<div className="justify-start text-[--Neutrals-NeutralSlate500] text-sm font-medium font-['Inter'] leading-tight">Submissions</div>
|
||||
</div>
|
||||
<div className="w-60 px-4 py-2.5 rounded-[34px] inline-flex justify-start items-center gap-2">
|
||||
<div className="justify-start text-[--$1] text-sm font-medium font-['Inter'] leading-tight">Reports</div>
|
||||
<div className="justify-start text-[--Neutrals-NeutralSlate500] text-sm font-medium font-['Inter'] leading-tight">Reports</div>
|
||||
</div>
|
||||
<div className="w-60 px-4 py-2.5 rounded-[34px] inline-flex justify-start items-center gap-2">
|
||||
<div className="justify-start text-[--$1] text-sm font-medium font-['Inter'] leading-tight">Chat</div>
|
||||
<div className="justify-start text-[--Neutrals-NeutralSlate500] text-sm font-medium font-['Inter'] leading-tight">Chat</div>
|
||||
</div>
|
||||
<div className="w-60 px-4 py-2.5 rounded-[34px] inline-flex justify-start items-center gap-2">
|
||||
<div className="justify-start text-[--$1] text-sm font-medium font-['Inter'] leading-tight">Help</div>
|
||||
<div className="justify-start text-[--Neutrals-NeutralSlate500] text-sm font-medium font-['Inter'] leading-tight">Help</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -192,7 +192,7 @@ const CompanyWiki: React.FC = () => {
|
||||
{/* Bottom Section */}
|
||||
<div className="self-stretch flex flex-col justify-start items-start gap-3">
|
||||
<div className="w-60 px-4 py-2.5 rounded-[34px] inline-flex justify-start items-center gap-2">
|
||||
<div className="flex-1 justify-start text-[--$1] text-sm font-medium font-['Inter'] leading-tight">Settings</div>
|
||||
<div className="flex-1 justify-start text-[--Neutrals-NeutralSlate500] text-sm font-medium font-['Inter'] leading-tight">Settings</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -201,21 +201,21 @@ const CompanyWiki: React.FC = () => {
|
||||
<div className="flex-1 self-stretch bg-[--Neutrals-NeutralSlate0] rounded-tr-3xl rounded-br-3xl inline-flex flex-col justify-start items-start">
|
||||
<div className="self-stretch flex-1 inline-flex justify-start items-center">
|
||||
{/* Table of Contents Sidebar */}
|
||||
<div className="flex-1 self-stretch max-w-64 min-w-64 border-r border-Outline-Outline-Gray-200 inline-flex flex-col justify-start items-start">
|
||||
<div className="flex-1 self-stretch max-w-64 min-w-64 border-r border-[--Outline-Outline-Gray-200] inline-flex flex-col justify-start items-start">
|
||||
<div className="self-stretch p-5 inline-flex justify-start items-center gap-2.5">
|
||||
<div className="flex-1 justify-start text-[--$1] text-base font-medium font-['Inter'] leading-normal">Table of contents</div>
|
||||
<div className="flex-1 justify-start text-[--Neutrals-NeutralSlate950] text-base font-medium font-['Inter'] leading-normal">Table of contents</div>
|
||||
</div>
|
||||
<div className="self-stretch px-3 flex flex-col justify-start items-start gap-1.5">
|
||||
{[1, 2, 3, 4, 5, 6, 7].map((section, index) => (
|
||||
<div
|
||||
key={section}
|
||||
className={`self-stretch p-2 ${activeSection === section ? 'bg-Main-BG-Gray-100 rounded-full shadow-[0px_1px_2px_0px_rgba(10,13,20,0.03)]' : 'rounded-[10px]'} inline-flex justify-start items-center gap-2 overflow-hidden cursor-pointer`}
|
||||
className={`self-stretch p-2 ${activeSection === section ? 'bg-[--Main-BG-Gray-100] rounded-full shadow-[0px_1px_2px_0px_rgba(10,13,20,0.03)]' : 'rounded-[10px]'} inline-flex justify-start items-center gap-2 overflow-hidden cursor-pointer`}
|
||||
onClick={() => setActiveSection(section)}
|
||||
>
|
||||
<div className={`h-5 p-0.5 ${activeSection === section ? 'bg-Brand-Orange' : 'bg-Text-Gray-100'} rounded-[999px] inline-flex flex-col justify-center items-center gap-0.5 overflow-hidden`}>
|
||||
<div className={`w-4 text-center justify-start ${activeSection === section ? 'text-[--$1]' : 'text-Text-Dark-950'} text-xs ${activeSection === section ? 'font-medium' : 'font-normal'} font-['Inter'] leading-none`}>{section}</div>
|
||||
<div className={`h-5 p-0.5 ${activeSection === section ? 'bg-[--Brand-Orange]' : 'bg-[--Text-Gray-100]'} rounded-[999px] inline-flex flex-col justify-center items-center gap-0.5 overflow-hidden`}>
|
||||
<div className={`w-4 text-center justify-start ${activeSection === section ? 'text-[--Neutrals-NeutralSlate0]' : 'text-[--Text-Dark-950]'} text-xs ${activeSection === section ? 'font-medium' : 'font-normal'} font-['Inter'] leading-none`}>{section}</div>
|
||||
</div>
|
||||
<div className={`flex-1 justify-start ${activeSection === section ? 'text-[--$1]' : 'text-Text-Gray-500'} text-xs font-medium font-['Inter'] leading-none`}>
|
||||
<div className={`flex-1 justify-start ${activeSection === section ? 'text-[--Neutrals-NeutralSlate800]' : 'text-[--Text-Gray-500]'} text-xs font-medium font-['Inter'] leading-none`}>
|
||||
{getSectionTitle(section)}
|
||||
</div>
|
||||
</div>
|
||||
@@ -226,7 +226,7 @@ const CompanyWiki: React.FC = () => {
|
||||
{/* Questions Content */}
|
||||
<div className="flex-1 self-stretch inline-flex flex-col justify-start items-start">
|
||||
<div className="self-stretch p-5 inline-flex justify-start items-center gap-2.5">
|
||||
<div className="flex-1 justify-start text-[--$1] text-base font-medium font-['Inter'] leading-normal">
|
||||
<div className="flex-1 justify-start text-[--Neutrals-NeutralSlate800] text-base font-medium font-['Inter'] leading-normal">
|
||||
{getSectionTitle(activeSection)}
|
||||
</div>
|
||||
</div>
|
||||
@@ -235,16 +235,16 @@ const CompanyWiki: React.FC = () => {
|
||||
<div key={qa.field} className="self-stretch p-3 bg-[--Neutrals-NeutralSlate100] rounded-2xl shadow-[0px_1px_2px_0px_rgba(0,0,0,0.02)] flex flex-col justify-center items-start gap-2 overflow-hidden">
|
||||
<div className="self-stretch px-3 py-px inline-flex justify-start items-center gap-2.5">
|
||||
<div className="flex-1 flex justify-center items-center gap-3">
|
||||
<div className="w-3 self-stretch justify-start text-[--$1] text-base font-semibold font-['Inter'] leading-normal">Q</div>
|
||||
<div className="flex-1 justify-start text-[--$1] text-sm font-medium font-['Inter'] leading-tight">
|
||||
<div className="w-3 self-stretch justify-start text-[--Neutrals-NeutralSlate300] text-base font-semibold font-['Inter'] leading-normal">Q</div>
|
||||
<div className="flex-1 justify-start text-[--Neutrals-NeutralSlate600] text-sm font-medium font-['Inter'] leading-tight">
|
||||
{qa.question}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="self-stretch px-3 py-2 bg-[--Neutrals-NeutralSlate0] rounded-[10px] inline-flex justify-between items-center">
|
||||
<div className="flex-1 flex justify-start items-start gap-3">
|
||||
<div className="w-3.5 h-6 justify-center text-[--$1] text-base font-semibold font-['Inter'] leading-normal">A</div>
|
||||
<div className="flex-1 justify-start text-[--$1] text-base font-normal font-['Inter'] leading-normal whitespace-pre-wrap">
|
||||
<div className="w-3.5 h-6 justify-center text-[--Neutrals-NeutralSlate300] text-base font-semibold font-['Inter'] leading-normal">A</div>
|
||||
<div className="flex-1 justify-start text-[--Neutrals-NeutralSlate600] text-base font-normal font-['Inter'] leading-normal whitespace-pre-wrap">
|
||||
{qa.answer}
|
||||
</div>
|
||||
</div>
|
||||
@@ -260,4 +260,4 @@ const CompanyWiki: React.FC = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default CompanyWiki;
|
||||
export default CompanyWiki;
|
||||
|
||||
@@ -162,8 +162,8 @@ const EmployeeQuestionnaire: React.FC = () => {
|
||||
<div className="w-16 h-16 bg-[--Brand-Orange] rounded-full flex items-center justify-center font-bold text-white text-2xl mx-auto mb-4">
|
||||
A
|
||||
</div>
|
||||
<h1 className="text-3xl font-bold text-[--Neutrals-NeutralSlate950] mb-4">Loading Your Invitation...</h1>
|
||||
<p className="text-[--Neutrals-NeutralSlate500]">Please wait while we verify your invitation.</p>
|
||||
<h1 className="text-3xl font-bold text-[--Text-Gray-950] mb-4">Loading Your Invitation...</h1>
|
||||
<p className="text-[--Text-Gray-500]">Please wait while we verify your invitation.</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -177,8 +177,8 @@ const EmployeeQuestionnaire: React.FC = () => {
|
||||
<div className="w-16 h-16 bg-red-500 rounded-full flex items-center justify-center font-bold text-white text-2xl mx-auto mb-4">
|
||||
!
|
||||
</div>
|
||||
<h1 className="text-3xl font-bold text-[--Neutrals-NeutralSlate950] mb-4">Invitation Error</h1>
|
||||
<p className="text-[--Neutrals-NeutralSlate500] mb-6">{error}</p>
|
||||
<h1 className="text-3xl font-bold text-[--Text-Gray-950] mb-4">Invitation Error</h1>
|
||||
<p className="text-[--Text-Gray-500] mb-6">{error}</p>
|
||||
<button
|
||||
onClick={() => window.location.href = '/'}
|
||||
className="px-6 py-3 bg-[--Brand-Orange] text-white rounded-lg hover:bg-orange-600"
|
||||
@@ -575,11 +575,11 @@ const EmployeeQuestionnaire: React.FC = () => {
|
||||
return (
|
||||
<div className="min-h-screen bg-[--Neutrals-NeutralSlate0] py-8 px-4 flex items-center justify-center">
|
||||
<div className="max-w-4xl mx-auto text-center">
|
||||
<div className="w-16 h-16 bg-[--Brand-Orange] rounded-full flex items-center justify-center font-bold text-white text-2xl mx-auto mb-4 animate-pulse">
|
||||
<div className="w-16 h-16 bg-[--Brand-Orange] rounded-full flex items-center justify-center font-bold text-[--Neutrals-NeutralSlate0 text-2xl mx-auto mb-4 animate-pulse">
|
||||
A
|
||||
</div>
|
||||
<h1 className="text-3xl font-bold text-[--Neutrals-NeutralSlate950] mb-4">Submitting Your Responses...</h1>
|
||||
<p className="text-[--Neutrals-NeutralSlate500]">Please wait while we process your assessment and generate your report.</p>
|
||||
<h1 className="text-3xl font-bold text-[--Text-Gray-950] mb-4">Submitting Your Responses...</h1>
|
||||
<p className="text-[--Text-Gray-500]">Please wait while we process your assessment and generate your report.</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -597,4 +597,4 @@ const EmployeeQuestionnaire: React.FC = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default EmployeeQuestionnaire;
|
||||
export default EmployeeQuestionnaire;
|
||||
|
||||
@@ -27,16 +27,16 @@ const FormsDashboard: React.FC = () => {
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-[--background-primary] py-8 px-4">
|
||||
<div className="min-h-screen bg-[--Main-BG-Gray-100] py-8 px-4">
|
||||
<div className="max-w-6xl mx-auto">
|
||||
<div className="text-center mb-8">
|
||||
<div className="w-16 h-16 bg-gradient-to-r from-blue-500 to-purple-600 rounded-full flex items-center justify-center font-bold text-white text-2xl mx-auto mb-4">
|
||||
<div className="w-16 h-16 bg-gradient-to-r from-blue-500 to-purple-600 rounded-full flex items-center justify-center font-bold text-[--Text-White-00] text-2xl mx-auto mb-4">
|
||||
📝
|
||||
</div>
|
||||
<h1 className="text-3xl font-bold text-[--text-primary] mb-2">
|
||||
<h1 className="text-3xl font-bold text-[--Text-Gray-800] mb-2">
|
||||
Employee Forms Dashboard
|
||||
</h1>
|
||||
<p className="text-[--text-secondary] max-w-2xl mx-auto">
|
||||
<p className="text-[--Text-Gray-500] max-w-2xl mx-auto">
|
||||
Explore the different employee questionnaire implementations.
|
||||
Each form demonstrates different UX approaches and question types based on the Figma designs.
|
||||
</p>
|
||||
@@ -51,7 +51,7 @@ const FormsDashboard: React.FC = () => {
|
||||
<h3 className="text-xl font-semibold text-[--text-primary] mb-2">
|
||||
{form.title}
|
||||
</h3>
|
||||
<p className="text-[--text-secondary] mb-4 text-sm">
|
||||
<p className="text-[--Text-Gray-500] mb-4 text-sm">
|
||||
{form.description}
|
||||
</p>
|
||||
<div className="space-y-2 mb-4">
|
||||
@@ -82,7 +82,7 @@ const FormsDashboard: React.FC = () => {
|
||||
<h3 className="text-lg font-semibold text-[--text-primary] mb-3">
|
||||
Question Types Implemented
|
||||
</h3>
|
||||
<ul className="space-y-2 text-[--text-secondary]">
|
||||
<ul className="space-y-2 text-[--Text-Gray-500]">
|
||||
<li>• <strong>Text Input:</strong> Single-line text for names, titles</li>
|
||||
<li>• <strong>Textarea:</strong> Multi-line for detailed responses</li>
|
||||
<li>• <strong>Scale Rating:</strong> 1-10 sliders with custom labels</li>
|
||||
@@ -91,10 +91,10 @@ const FormsDashboard: React.FC = () => {
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-[--text-primary] mb-3">
|
||||
<h3 className="text-lg font-semibold text-[--Text-Gray-800] mb-3">
|
||||
Key Features
|
||||
</h3>
|
||||
<ul className="space-y-2 text-[--text-secondary]">
|
||||
<ul className="space-y-2 text-[--Text-Gray-500]">
|
||||
<li>• <strong>Conditional Logic:</strong> Follow-up questions based on answers</li>
|
||||
<li>• <strong>Progress Tracking:</strong> Visual completion indicators</li>
|
||||
<li>• <strong>Responsive Design:</strong> Mobile-friendly layouts</li>
|
||||
|
||||
@@ -66,62 +66,65 @@ const HelpNew: React.FC = () => {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex-1 self-stretch shadow-[0px_0px_15px_0px_rgba(0,0,0,0.08)] flex justify-between items-start h-full">
|
||||
<Sidebar companyName={org.name} />
|
||||
<div className="flex-1 self-stretch pt-8 pb-6 bg-[--Neutrals-NeutralSlate0] inline-flex flex-col justify-start items-center gap-6">
|
||||
<div className="w-[680px] justify-start text-[--Neutrals-NeutralSlate800] text-2xl font-medium font-['Neue_Montreal'] leading-normal">Help & Support</div>
|
||||
<div className="w-[680px] flex flex-col justify-start items-start gap-4">
|
||||
{faqItems.map((item, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="self-stretch p-3 bg-[--Neutrals-NeutralSlate100] rounded-[20px] shadow-[0px_1px_2px_0px_rgba(0,0,0,0.02)] flex flex-col justify-center items-start gap-1 overflow-hidden"
|
||||
>
|
||||
<div className="w-[100vw] h-[100vh] p-4 bg-[--Neutrals-NeutralSlate200] inline-flex justify-start items-start overflow-hidden">
|
||||
<div className="flex-1 self-stretch rounded-3xl shadow-[0px_0px_15px_0px_rgba(0,0,0,0.08)] flex justify-between items-start overflow-hidden">
|
||||
|
||||
<Sidebar companyName={org.name} />
|
||||
<div className="flex-1 self-stretch pt-8 pb-6 bg-[--Neutrals-NeutralSlate0] inline-flex flex-col justify-start items-center gap-6">
|
||||
<div className="w-[680px] justify-start text-[--Text-Gray-800] text-2xl font-medium font-['Neue_Montreal'] leading-normal">Help & Support</div>
|
||||
<div className="w-[680px] flex flex-col justify-start items-start gap-4">
|
||||
{faqItems.map((item, index) => (
|
||||
<div
|
||||
onClick={() => toggleFAQ(index)}
|
||||
className="self-stretch px-3 py-2 inline-flex justify-start items-center gap-2 cursor-pointer"
|
||||
key={index}
|
||||
className="self-stretch p-3 bg-[--Neutrals-NeutralSlate100] rounded-[20px] shadow-[0px_1px_2px_0px_rgba(0,0,0,0.02)] flex flex-col justify-center items-start gap-1 overflow-hidden"
|
||||
>
|
||||
<div className="flex-1 justify-start text-[--Neutrals-NeutralSlate950] text-base font-medium font-['Inter'] leading-normal">
|
||||
{item.question}
|
||||
</div>
|
||||
<div>
|
||||
{item.isOpen ? (
|
||||
<svg width="12" height="2" viewBox="0 0 12 2" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11 1L1 1" stroke="var(--Neutrals-NeutralSlate500)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</svg>
|
||||
) : (
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M9.99996 4.16797V15.8346M4.16663 10.0013H15.8333" stroke="var(--Neutrals-NeutralSlate400)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</svg>
|
||||
)}
|
||||
</div>
|
||||
<div className="w-5 h-5 opacity-0 border border-zinc-800" />
|
||||
</div>
|
||||
{item.isOpen && (
|
||||
<div className="self-stretch p-6 bg-[--Neutrals-NeutralSlate0] rounded-2xl outline outline-1 outline-offset-[-1px] outline-[--Neutrals-NeutralSlate200] flex flex-col justify-start items-start gap-4">
|
||||
<div className="self-stretch justify-start text-[--Neutrals-NeutralSlate800] text-base font-normal font-['Inter'] leading-normal">
|
||||
{item.answer}
|
||||
<div
|
||||
onClick={() => toggleFAQ(index)}
|
||||
className="self-stretch px-3 py-2 inline-flex justify-start items-center gap-2 cursor-pointer"
|
||||
>
|
||||
<div className="flex-1 justify-start text-[--Text-Gray-950] text-base font-medium font-['Inter'] leading-normal">
|
||||
{item.question}
|
||||
</div>
|
||||
<div>
|
||||
{item.isOpen ? (
|
||||
<svg width="12" height="2" viewBox="0 0 12 2" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11 1L1 1" stroke="var(--Neutrals-NeutralSlate500)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</svg>
|
||||
) : (
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M9.99996 4.16797V15.8346M4.16663 10.0013H15.8333" stroke="var(--Neutrals-NeutralSlate400)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</svg>
|
||||
)}
|
||||
</div>
|
||||
<div className="w-5 h-5 opacity-0 border border-zinc-800" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="w-[680px] px-5 py-4 bg-[--Neutrals-NeutralSlate800] rounded-2xl backdrop-blur-blur inline-flex justify-center items-center gap-12 overflow-hidden">
|
||||
<div className="flex-1 inline-flex flex-col justify-center items-start gap-2">
|
||||
<div className="self-stretch justify-start text-[--Neutrals-NeutralSlate0] text-base font-medium font-['Inter'] leading-normal">Still have questions?</div>
|
||||
<div className="self-stretch justify-start text-[--Neutrals-NeutralSlate400] text-sm font-normal font-['Inter'] leading-tight">We are available for 24/7</div>
|
||||
{item.isOpen && (
|
||||
<div className="self-stretch p-6 bg-[--Neutrals-NeutralSlate0] rounded-2xl outline outline-1 outline-offset-[-1px] outline-[--Neutrals-NeutralSlate200] flex flex-col justify-start items-start gap-4">
|
||||
<div className="self-stretch justify-start text-[--Text-Gray-800] text-base font-normal font-['Inter'] leading-normal">
|
||||
{item.answer}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div
|
||||
onClick={handleContactUs}
|
||||
className="px-3 py-2.5 bg-[--Brand-Orange] rounded-[999px] outline outline-2 outline-offset-[-2px] outline-blue-400 flex justify-center items-center gap-1 overflow-hidden cursor-pointer hover:bg-[--Brand-Orange]/90"
|
||||
>
|
||||
<div>
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5.58685 5.90223C6.05085 6.86865 6.68337 7.77441 7.48443 8.57546C8.28548 9.37651 9.19124 10.009 10.1577 10.473C10.2408 10.5129 10.2823 10.5329 10.3349 10.5482C10.5218 10.6027 10.7513 10.5636 10.9096 10.4502C10.9542 10.4183 10.9923 10.3802 11.0685 10.304C11.3016 10.071 11.4181 9.95443 11.5353 9.87824C11.9772 9.59091 12.5469 9.59091 12.9889 9.87824C13.106 9.95443 13.2226 10.071 13.4556 10.304L13.5856 10.4339C13.9398 10.7882 14.117 10.9654 14.2132 11.1556C14.4046 11.534 14.4046 11.9809 14.2132 12.3592C14.117 12.5495 13.9399 12.7266 13.5856 13.0809L13.4805 13.186C13.1274 13.5391 12.9508 13.7156 12.7108 13.8505C12.4445 14.0001 12.0308 14.1077 11.7253 14.1068C11.45 14.1059 11.2619 14.0525 10.8856 13.9457C8.86333 13.3718 6.95509 12.2888 5.36311 10.6968C3.77112 9.10479 2.68814 7.19655 2.11416 5.17429C2.00735 4.79799 1.95395 4.60984 1.95313 4.33455C1.95222 4.02906 2.0598 3.6154 2.20941 3.34907C2.34424 3.10904 2.52078 2.9325 2.87386 2.57942L2.97895 2.47433C3.33325 2.12004 3.5104 1.94289 3.70065 1.84666C4.07903 1.65528 4.52587 1.65528 4.90424 1.84666C5.0945 1.94289 5.27164 2.12004 5.62594 2.47433L5.75585 2.60424C5.98892 2.83732 6.10546 2.95385 6.18165 3.07104C6.46898 3.51296 6.46898 4.08268 6.18165 4.52461C6.10546 4.6418 5.98892 4.75833 5.75585 4.9914C5.67964 5.06761 5.64154 5.10571 5.60965 5.15026C5.4963 5.30854 5.45717 5.53805 5.51165 5.72495C5.52698 5.77754 5.54694 5.81911 5.58685 5.90223Z" stroke="var(--white)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</svg>
|
||||
<div className="w-[680px] px-5 py-4 bg-[--Neutrals-NeutralSlate800] rounded-2xl backdrop-blur-blur inline-flex justify-center items-center gap-12 overflow-hidden">
|
||||
<div className="flex-1 inline-flex flex-col justify-center items-start gap-2">
|
||||
<div className="self-stretch justify-start text-[--Text-Gray-0] text-base font-medium font-['Inter'] leading-normal">Still have questions?</div>
|
||||
<div className="self-stretch justify-start text-[--Text-Gray-400] text-sm font-normal font-['Inter'] leading-tight">We are available for 24/7</div>
|
||||
</div>
|
||||
<div className="px-1 flex justify-center items-center">
|
||||
<div className="justify-center text-white text-sm font-medium font-['Inter'] leading-tight">Contact Us</div>
|
||||
<div
|
||||
onClick={handleContactUs}
|
||||
className="px-3 py-2.5 bg-[--Brand-Orange] rounded-[999px] outline outline-2 outline-offset-[-2px] outline-blue-400 flex justify-center items-center gap-1 overflow-hidden cursor-pointer hover:bg-[--Brand-Orange]/90"
|
||||
>
|
||||
<div>
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5.58685 5.90223C6.05085 6.86865 6.68337 7.77441 7.48443 8.57546C8.28548 9.37651 9.19124 10.009 10.1577 10.473C10.2408 10.5129 10.2823 10.5329 10.3349 10.5482C10.5218 10.6027 10.7513 10.5636 10.9096 10.4502C10.9542 10.4183 10.9923 10.3802 11.0685 10.304C11.3016 10.071 11.4181 9.95443 11.5353 9.87824C11.9772 9.59091 12.5469 9.59091 12.9889 9.87824C13.106 9.95443 13.2226 10.071 13.4556 10.304L13.5856 10.4339C13.9398 10.7882 14.117 10.9654 14.2132 11.1556C14.4046 11.534 14.4046 11.9809 14.2132 12.3592C14.117 12.5495 13.9399 12.7266 13.5856 13.0809L13.4805 13.186C13.1274 13.5391 12.9508 13.7156 12.7108 13.8505C12.4445 14.0001 12.0308 14.1077 11.7253 14.1068C11.45 14.1059 11.2619 14.0525 10.8856 13.9457C8.86333 13.3718 6.95509 12.2888 5.36311 10.6968C3.77112 9.10479 2.68814 7.19655 2.11416 5.17429C2.00735 4.79799 1.95395 4.60984 1.95313 4.33455C1.95222 4.02906 2.0598 3.6154 2.20941 3.34907C2.34424 3.10904 2.52078 2.9325 2.87386 2.57942L2.97895 2.47433C3.33325 2.12004 3.5104 1.94289 3.70065 1.84666C4.07903 1.65528 4.52587 1.65528 4.90424 1.84666C5.0945 1.94289 5.27164 2.12004 5.62594 2.47433L5.75585 2.60424C5.98892 2.83732 6.10546 2.95385 6.18165 3.07104C6.46898 3.51296 6.46898 4.08268 6.18165 4.52461C6.10546 4.6418 5.98892 4.75833 5.75585 4.9914C5.67964 5.06761 5.64154 5.10571 5.60965 5.15026C5.4963 5.30854 5.45717 5.53805 5.51165 5.72495C5.52698 5.77754 5.54694 5.81911 5.58685 5.90223Z" stroke="var(--Other-White)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</svg>
|
||||
</div>
|
||||
<div className="px-1 flex justify-center items-center">
|
||||
<div className="justify-center text-white text-sm font-medium font-['Inter'] leading-tight">Contact Us</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -130,4 +133,4 @@ const HelpNew: React.FC = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default HelpNew;
|
||||
export default HelpNew;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useLocation, useNavigate, useParams } from 'react-router-dom';
|
||||
import { useAuth } from '../contexts/AuthContext';
|
||||
import FigmaPrimaryButton from '../components/figma/FigmaButton';
|
||||
|
||||
type AuthStep = 'email' | 'otp' | 'password-fallback';
|
||||
|
||||
@@ -116,6 +117,33 @@ const ModernLogin: React.FC = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const handleOtpPaste = (index: number, e: React.ClipboardEvent) => {
|
||||
e.preventDefault();
|
||||
const pastedData = e.clipboardData.getData('text');
|
||||
|
||||
// Only process if pasted data contains only digits and is 6 characters or less
|
||||
if (/^\d{1,6}$/.test(pastedData)) {
|
||||
const newOtp = [...otp];
|
||||
const pastedDigits = pastedData.split('');
|
||||
|
||||
// Fill the OTP fields starting from the current index
|
||||
for (let i = 0; i < pastedDigits.length && (index + i) < 6; i++) {
|
||||
newOtp[index + i] = pastedDigits[i];
|
||||
}
|
||||
|
||||
setOtp(newOtp);
|
||||
|
||||
// Focus the next empty field or the last filled field
|
||||
const nextEmptyIndex = newOtp.findIndex((digit, idx) => idx > index && digit === '');
|
||||
const focusIndex = nextEmptyIndex !== -1 ? nextEmptyIndex : Math.min(index + pastedDigits.length, 5);
|
||||
|
||||
setTimeout(() => {
|
||||
const nextInput = document.getElementById(`otp-${focusIndex}`);
|
||||
nextInput?.focus();
|
||||
}, 0);
|
||||
}
|
||||
};
|
||||
|
||||
const handleOtpKeyDown = (index: number, e: React.KeyboardEvent) => {
|
||||
if (e.key === 'Backspace' && !otp[index] && index > 0) {
|
||||
const prevInput = document.getElementById(`otp-${index - 1}`);
|
||||
@@ -239,10 +267,10 @@ const ModernLogin: React.FC = () => {
|
||||
<div className="self-stretch flex flex-col justify-start items-center gap-9">
|
||||
<div className="self-stretch flex flex-col justify-start items-start gap-4">
|
||||
<div className="self-stretch flex flex-col justify-start items-start gap-2">
|
||||
<div className="self-stretch justify-start text-[--Neutrals-NeutralSlate800] text-2xl font-medium font-['Neue_Montreal'] leading-normal">Welcome to Auditly.</div>
|
||||
<div className="self-stretch justify-start text-[--Neutrals-NeutralSlate400] text-2xl font-medium font-['Neue_Montreal'] leading-normal">The company intelligence tool.</div>
|
||||
<div className="self-stretch justify-start text-[--Text-Gray-800] text-2xl font-medium font-['Neue_Montreal'] leading-normal">Welcome to Orbitly.</div>
|
||||
<div className="self-stretch justify-start text-[--Text-Gray-400] text-2xl font-medium font-['Neue_Montreal'] leading-normal">The company intelligence tool.</div>
|
||||
</div>
|
||||
<div className="self-stretch justify-center text-[--Neutrals-NeutralSlate500] text-base font-normal font-['Inter'] leading-normal">Log in or create an account.</div>
|
||||
<div className="self-stretch justify-center text-[--Text-Gray-500] text-base font-normal font-['Inter'] leading-normal">Log in or create an account.</div>
|
||||
</div>
|
||||
<form onSubmit={handleEmailSubmit} className="self-stretch flex flex-col justify-start items-start gap-4">
|
||||
<div className={`self-stretch px-4 py-3.5 ${hasEmail ? 'bg-[--Neutrals-NeutralSlate50]' : 'bg-[--Neutrals-NeutralSlate100]'} rounded-[999px] inline-flex justify-center items-center gap-2 overflow-hidden`}>
|
||||
@@ -261,24 +289,24 @@ const ModernLogin: React.FC = () => {
|
||||
</defs>
|
||||
</svg>
|
||||
</div>
|
||||
<div className="justify-center text-[--Neutrals-NeutralSlate800] text-sm font-medium font-['Inter'] leading-tight cursor-pointer" onClick={handleGoogleAuth}>Continue with Google</div>
|
||||
<div className="justify-center text-[--Text-Gray-800] text-sm font-medium font-['Inter'] leading-tight cursor-pointer" onClick={handleGoogleAuth}>Continue with Google</div>
|
||||
</div>
|
||||
<div className="self-stretch inline-flex justify-center items-center gap-3">
|
||||
<div className="flex-1 h-0 outline outline-1 outline-offset-[-0.50px] outline-[--$1]"></div>
|
||||
<div className="justify-start text-[--Neutrals-NeutralSlate400] text-sm font-normal font-['Inter'] leading-tight">or</div>
|
||||
<div className="flex-1 h-0 outline outline-1 outline-offset-[-0.50px] outline-[--$1]"></div>
|
||||
<div className="flex-1 h-0 outline outline-1 outline-offset-[-0.50px] outline-[--Neutrals-NeutralSlate200]"></div>
|
||||
<div className="justify-start text-[--Text-Gray-400] text-sm font-normal font-['Inter'] leading-tight">or</div>
|
||||
<div className="flex-1 h-0 outline outline-1 outline-offset-[-0.50px] outline-[--Neutrals-NeutralSlate200]"></div>
|
||||
</div>
|
||||
<div className="self-stretch flex flex-col justify-center items-end gap-2">
|
||||
<div className="self-stretch inline-flex justify-end items-end gap-2">
|
||||
<div data-property-1="Default" data-show-hint="false" data-show-icon-left="true" data-show-icon-right="false" data-show-label="true" data-show-mandatory="false" className="flex-1 inline-flex flex-col justify-start items-start gap-2">
|
||||
<div className="mt-4 self-stretch w-full inline-flex justify-end items-end gap-2 h-12">
|
||||
<div className="flex-1 inline-flex flex-col justify-start items-start gap-2">
|
||||
<div className="self-stretch inline-flex justify-start items-center gap-0.5">
|
||||
<div className="justify-start text-[--Neutrals-NeutralSlate900] text-sm font-normal font-['Inter'] leading-tight">Email</div>
|
||||
<div className="justify-start text-[--Text-Gray-900] text-sm font-normal font-['Inter'] leading-tight">Email</div>
|
||||
</div>
|
||||
<div className="self-stretch flex flex-col justify-start items-start gap-1">
|
||||
<div className="self-stretch px-4 py-3.5 bg-[--Neutrals-NeutralSlate100] rounded-[999px] inline-flex justify-start items-center gap-2 overflow-hidden">
|
||||
<div data-svg-wrapper className="relative">
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M1.66667 5.8335L8.47077 10.5964C9.02175 10.982 9.29724 11.1749 9.59689 11.2496C9.86158 11.3156 10.1384 11.3156 10.4031 11.2496C10.7028 11.1749 10.9783 10.982 11.5292 10.5964L18.3333 5.8335M5.66667 16.6668H14.3333C15.7335 16.6668 16.4335 16.6668 16.9683 16.3943C17.4387 16.1547 17.8212 15.7722 18.0609 15.3018C18.3333 14.767 18.3333 14.067 18.3333 12.6668V7.3335C18.3333 5.93336 18.3333 5.2333 18.0609 4.69852C17.8212 4.22811 17.4387 3.84566 16.9683 3.60598C16.4335 3.3335 15.7335 3.3335 14.3333 3.3335H5.66667C4.26654 3.3335 3.56647 3.3335 3.0317 3.60598C2.56129 3.84566 2.17884 4.22811 1.93916 4.69852C1.66667 5.2333 1.66667 5.93336 1.66667 7.3335V12.6668C1.66667 14.067 1.66667 14.767 1.93916 15.3018C2.17884 15.7722 2.56129 16.1547 3.0317 16.3943C3.56647 16.6668 4.26654 16.6668 5.66667 16.6668Z" stroke={hasEmail ? "var(--Brand-Orange, #3399FF)" : "var(--Neutrals-NeutralSlate500, #717680)"} strokeWidth="1.5" strokeMiterlimit="10" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<path d="M1.66667 5.8335L8.47077 10.5964C9.02175 10.982 9.29724 11.1749 9.59689 11.2496C9.86158 11.3156 10.1384 11.3156 10.4031 11.2496C10.7028 11.1749 10.9783 10.982 11.5292 10.5964L18.3333 5.8335M5.66667 16.6668H14.3333C15.7335 16.6668 16.4335 16.6668 16.9683 16.3943C17.4387 16.1547 17.8212 15.7722 18.0609 15.3018C18.3333 14.767 18.3333 14.067 18.3333 12.6668V7.3335C18.3333 5.93336 18.3333 5.2333 18.0609 4.69852C17.8212 4.22811 17.4387 3.84566 16.9683 3.60598C16.4335 3.3335 15.7335 3.3335 14.3333 3.3335H5.66667C4.26654 3.3335 3.56647 3.3335 3.0317 3.60598C2.56129 3.84566 2.17884 4.22811 1.93916 4.69852C1.66667 5.2333 1.66667 5.93336 1.66667 7.3335V12.6668C1.66667 14.067 1.66667 14.767 1.93916 15.3018C2.17884 15.7722 2.56129 16.1547 3.0317 16.3943C3.56647 16.6668 4.26654 16.6668 5.66667 16.6668Z" stroke={hasEmail ? "var(--Brand-Orange, #3399FF)" : "var(--Neutrals-NeutralSlate500)"} strokeWidth="1.5" strokeMiterlimit="10" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</svg>
|
||||
</div>
|
||||
<input
|
||||
@@ -286,26 +314,13 @@ const ModernLogin: React.FC = () => {
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
placeholder="Enter your email"
|
||||
className={`flex-1 justify-start text-sm font-normal font-['Inter'] leading-tight bg-transparent outline-none ${hasEmail ? 'text-[--Neutrals-NeutralSlate950]' : 'text-[--Neutrals-NeutralSlate500]'}`}
|
||||
className={`flex-1 justify-start text-sm font-normal font-['Inter'] leading-tight bg-transparent outline-none ${hasEmail ? 'text-[--Text-Gray-950]' : 'text-[--Text-Gray-950]'}`}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
type="submit"
|
||||
disabled={isLoading || !email.trim()}
|
||||
className={`${hasEmail && !isLoading ?
|
||||
'w-32 px-4 py-3.5 bg-[--Brand-Orange] rounded-[999px] outline outline-2 outline-offset-[-2px] outline-blue-400 flex justify-center items-center gap-1 overflow-hidden' :
|
||||
'px-7 py-3.5 bg-[--Neutrals-NeutralSlate100] rounded-[999px] flex justify-center items-center gap-1 overflow-hidden'
|
||||
} transition-all duration-200`}
|
||||
>
|
||||
<div className="px-1 flex justify-center items-center">
|
||||
<div className={`justify-center text-sm font-medium font-['Inter'] leading-tight ${hasEmail && !isLoading ? 'text-white' : 'text-[--Neutrals-NeutralSlate500]'}`}>
|
||||
{isLoading ? 'Sending...' : 'Continue'}
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
<FigmaPrimaryButton text={isLoading ? 'Sending...' : 'Continue'} disabled={isLoading || !email.trim()} size="tiny" grow={false} buttonExtra={`${hasEmail && !isLoading ? '' : 'bg-[--Neutrals-NeutralSlate100]'}`} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -320,7 +335,7 @@ const ModernLogin: React.FC = () => {
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setStep('password-fallback')}
|
||||
className="text-sm text-[--Neutrals-NeutralSlate500] hover:text-[--Neutrals-NeutralSlate700] transition-colors"
|
||||
className="text-sm text-[--Text-Gray-500] hover:text-[--Text-Gray-500] transition-colors"
|
||||
>
|
||||
Use password instead
|
||||
</button>
|
||||
@@ -361,19 +376,19 @@ const ModernLogin: React.FC = () => {
|
||||
<div className="self-stretch flex flex-col justify-center items-start gap-6">
|
||||
<div className="self-stretch flex flex-col justify-start items-start gap-4">
|
||||
<div className="self-stretch flex flex-col justify-start items-start gap-2">
|
||||
<div className="self-stretch justify-start text-[--Neutrals-NeutralSlate800] text-2xl font-medium font-['Neue_Montreal'] leading-normal">Check your email.</div>
|
||||
<div className="self-stretch justify-start text-[--Neutrals-NeutralSlate400] text-2xl font-medium font-['Neue_Montreal'] leading-normal">We've sent you a code.</div>
|
||||
<div className="self-stretch justify-start text-[--Text-Gray-800] text-2xl font-medium font-['Neue_Montreal'] leading-normal">Check your email.</div>
|
||||
<div className="self-stretch justify-start text-[--Text-Gray-400] text-2xl font-medium font-['Neue_Montreal'] leading-normal">We've sent you a code.</div>
|
||||
</div>
|
||||
<div className="self-stretch justify-center">
|
||||
<span className="text-[--Neutrals-NeutralSlate500] text-base font-normal font-['Inter'] leading-normal">Didn't receive code? </span>
|
||||
<span className="text-[--Text-Gray-500] text-base font-normal font-['Inter'] leading-normal">Didn't receive code? </span>
|
||||
<button
|
||||
onClick={() => sendOTP(email)}
|
||||
disabled={resendCooldown > 0 || isLoading}
|
||||
className="text-[--Neutrals-NeutralSlate500] text-base font-medium font-['Inter'] leading-normal hover:text-[--Neutrals-NeutralSlate700] transition-colors disabled:opacity-50"
|
||||
className="text-[--Text-Gray-500] text-base font-medium font-['Inter'] leading-normal hover:text-[--Text-Gray-500] transition-colors disabled:opacity-50"
|
||||
>
|
||||
{resendCooldown > 0 ? `Resend in ${resendCooldown}s` : 'Resend'}
|
||||
</button>
|
||||
<span className="text-[--Neutrals-NeutralSlate500] text-base font-normal font-['Inter'] leading-normal">.</span>
|
||||
<span className="text-[--Text-Gray-500] text-base font-normal font-['Inter'] leading-normal">.</span>
|
||||
</div>
|
||||
</div>
|
||||
<form onSubmit={handleOTPSubmit} className="self-stretch flex flex-col gap-6">
|
||||
@@ -386,7 +401,8 @@ const ModernLogin: React.FC = () => {
|
||||
value={digit}
|
||||
onChange={(e) => handleOtpChange(index, e.target.value)}
|
||||
onKeyDown={(e) => handleOtpKeyDown(index, e)}
|
||||
className={`flex-1 text-center justify-start text-sm font-medium font-['Inter'] leading-tight bg-transparent outline-none ${digit ? 'text-[--Neutrals-NeutralSlate800]' : 'text-[--Neutrals-NeutralSlate500]'}`}
|
||||
onPaste={(e) => handleOtpPaste(index, e)}
|
||||
className={`flex-1 text-center justify-start text-sm font-medium font-['Inter'] leading-tight bg-transparent outline-none ${digit ? 'text-[--Text-Gray-800]' : 'text-[--Text-Gray-800]'}`}
|
||||
maxLength={1}
|
||||
/>
|
||||
</div>
|
||||
@@ -413,27 +429,17 @@ const ModernLogin: React.FC = () => {
|
||||
</div>
|
||||
)}
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
disabled={isLoading || !allOtpFilled}
|
||||
className={`self-stretch px-7 py-3.5 rounded-[999px] inline-flex justify-center items-center gap-1 overflow-hidden transition-all duration-200 ${allOtpFilled && !isLoading ?
|
||||
'bg-[--Brand-Orange] outline outline-2 outline-offset-[-2px] outline-blue-400' :
|
||||
'bg-[--Neutrals-NeutralSlate100]'
|
||||
}`}
|
||||
>
|
||||
<div className="px-1 flex justify-center items-center">
|
||||
<div className={`justify-center text-sm font-medium font-['Inter'] leading-tight ${allOtpFilled && !isLoading ? 'text-white' : 'text-[--Neutrals-NeutralSlate500]'
|
||||
}`}>
|
||||
{isLoading ? 'Verifying...' : 'Continue'}
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
<FigmaPrimaryButton buttonExtra={`${allOtpFilled && !isLoading ?
|
||||
'' :
|
||||
'bg-[--Neutrals-NeutralSlate100]'
|
||||
}`} text={isLoading ? 'Verifying...' : 'Continue'} disabled={isLoading || !allOtpFilled} />
|
||||
|
||||
|
||||
<div className="self-stretch text-center">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => { setStep('email'); setError(''); setOtp(['', '', '', '', '', '']); }}
|
||||
className="text-sm text-[--Neutrals-NeutralSlate500] hover:text-[--Neutrals-NeutralSlate700] transition-colors"
|
||||
className="text-sm text-[--Text-Gray-500] hover:text-[--Text-Gray-500] transition-colors"
|
||||
>
|
||||
← Change email address
|
||||
</button>
|
||||
@@ -465,8 +471,8 @@ const ModernLogin: React.FC = () => {
|
||||
<div className="self-stretch flex flex-col justify-start items-center gap-9">
|
||||
<div className="self-stretch flex flex-col justify-start items-start gap-4">
|
||||
<div className="self-stretch flex flex-col justify-start items-start gap-2">
|
||||
<div className="self-stretch justify-start text-[--Neutrals-NeutralSlate800] text-2xl font-medium font-['Neue_Montreal'] leading-normal">Sign in with password.</div>
|
||||
<div className="self-stretch justify-start text-[--Neutrals-NeutralSlate400] text-2xl font-medium font-['Neue_Montreal'] leading-normal">Enter your password for {email}.</div>
|
||||
<div className="self-stretch justify-start text-[--Text-Gray-800] text-2xl font-medium font-['Neue_Montreal'] leading-normal">Sign in with password.</div>
|
||||
<div className="self-stretch justify-start text-[--Text-Gray-400] text-2xl font-medium font-['Neue_Montreal'] leading-normal">Enter your password for {email}.</div>
|
||||
</div>
|
||||
</div>
|
||||
<form onSubmit={handlePasswordFallback} className="self-stretch flex flex-col justify-start items-start gap-4">
|
||||
@@ -474,7 +480,7 @@ const ModernLogin: React.FC = () => {
|
||||
<div className="self-stretch inline-flex justify-end items-end gap-2">
|
||||
<div className="flex-1 inline-flex flex-col justify-start items-start gap-2">
|
||||
<div className="self-stretch inline-flex justify-start items-center gap-0.5">
|
||||
<div className="justify-start text-[--Neutrals-NeutralSlate900] text-sm font-normal font-['Inter'] leading-tight">Password</div>
|
||||
<div className="justify-start text-[--Text-Gray-900] text-sm font-normal font-['Inter'] leading-tight">Password</div>
|
||||
</div>
|
||||
<div className="self-stretch flex flex-col justify-start items-start gap-1">
|
||||
<div className="self-stretch px-4 py-3.5 bg-[--Neutrals-NeutralSlate100] rounded-[999px] inline-flex justify-start items-center gap-2 overflow-hidden">
|
||||
@@ -483,7 +489,7 @@ const ModernLogin: React.FC = () => {
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
placeholder="Enter your password"
|
||||
className="flex-1 justify-start text-[--Neutrals-NeutralSlate950] text-sm font-normal font-['Inter'] leading-tight bg-transparent outline-none"
|
||||
className="flex-1 justify-start text-[--Text-Gray-950] text-sm font-normal font-['Inter'] leading-tight bg-transparent outline-none"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
@@ -498,7 +504,7 @@ const ModernLogin: React.FC = () => {
|
||||
}`}
|
||||
>
|
||||
<div className="px-1 flex justify-center items-center">
|
||||
<div className={`justify-center text-sm font-medium font-['Inter'] leading-tight ${password.trim() && !isLoading ? 'text-white' : 'text-[--Neutrals-NeutralSlate500]'
|
||||
<div className={`justify-center text-sm font-medium font-['Inter'] leading-tight ${password.trim() && !isLoading ? 'text-[--Text-White-00]' : 'text-[--Text-Gray-500]'
|
||||
}`}>
|
||||
{isLoading ? 'Signing in...' : 'Sign In'}
|
||||
</div>
|
||||
@@ -517,7 +523,7 @@ const ModernLogin: React.FC = () => {
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => { setStep('email'); setError(''); setPassword(''); }}
|
||||
className="text-sm text-[--Neutrals-NeutralSlate500] hover:text-[--Neutrals-NeutralSlate700] transition-colors"
|
||||
className="text-sm text-[--Text-Gray-500] hover:text-[--Text-Gray-500] transition-colors"
|
||||
>
|
||||
← Back to email verification
|
||||
</button>
|
||||
@@ -535,7 +541,7 @@ const ModernLogin: React.FC = () => {
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="w-[1440px] h-[810px] bg-[--Neutrals-NeutralSlate0] flex items-center justify-center">
|
||||
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-Brand-Orange"></div>
|
||||
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-[--Brand-Orange]"></div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -549,4 +555,4 @@ const ModernLogin: React.FC = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default ModernLogin;
|
||||
export default ModernLogin;
|
||||
|
||||
@@ -109,7 +109,7 @@ const Onboarding: React.FC = () => {
|
||||
const handleLogoUpload = async (file: File) => {
|
||||
try {
|
||||
// Upload image to Firebase storage and get the stored image data
|
||||
const uploadedImage = await uploadCompanyLogo(file, orgId);
|
||||
const uploadedImage = await uploadCompanyLogo(file, org.orgId);
|
||||
// Store the dataUrl (base64) from the uploaded image
|
||||
updateFormData('companyLogo', uploadedImage.dataUrl);
|
||||
} catch (error) {
|
||||
@@ -227,10 +227,10 @@ const Onboarding: React.FC = () => {
|
||||
return (
|
||||
<div className="min-h-screen bg-[--Neutrals-NeutralSlate0] flex items-center justify-center">
|
||||
<div className="text-center">
|
||||
<div className="text-2xl font-medium text-[--Neutrals-NeutralSlate950] mb-4">
|
||||
<div className="text-2xl font-medium text-[--Text-Gray-950] mb-4">
|
||||
Completing your setup...
|
||||
</div>
|
||||
<div className="text-base text-[--Neutrals-NeutralSlate500]">
|
||||
<div className="text-base text-[--Text-Gray-500]">
|
||||
Please wait while we process your information.
|
||||
</div>
|
||||
</div>
|
||||
@@ -245,4 +245,4 @@ const Onboarding: React.FC = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default Onboarding;
|
||||
export default Onboarding;
|
||||
|
||||
@@ -3,6 +3,7 @@ import { useNavigate, useLocation } from 'react-router-dom';
|
||||
import { useAuth } from '../contexts/AuthContext';
|
||||
import { useUserOrganizations } from '../contexts/UserOrganizationsContext';
|
||||
import { Card, Button } from '../components/UiKit';
|
||||
import FigmaPrimaryButton from '../components/figma/FigmaButton';
|
||||
|
||||
const OrgSelection: React.FC = () => {
|
||||
const navigate = useNavigate();
|
||||
@@ -100,7 +101,7 @@ const OrgSelection: React.FC = () => {
|
||||
<div className="w-16 h-16 bg-[--accent] rounded-full flex items-center justify-center font-bold text-white text-2xl mx-auto mb-4">
|
||||
A
|
||||
</div>
|
||||
<h1 className="text-3xl font-bold text-[--text-primary] mb-2">Welcome to Auditly</h1>
|
||||
<h1 className="text-3xl font-bold text-[--text-primary] mb-2">Welcome to Orbitly</h1>
|
||||
<p className="text-[--text-secondary]">Select an organization to continue</p>
|
||||
</div>
|
||||
|
||||
@@ -112,8 +113,7 @@ const OrgSelection: React.FC = () => {
|
||||
{organizations.map((org) => (
|
||||
<div
|
||||
key={org.orgId}
|
||||
className="flex items-center justify-between p-3 border border-[--border-color] rounded-lg hover:bg-[--background-secondary] cursor-pointer"
|
||||
onClick={() => handleSelectOrg(org.orgId)}
|
||||
className="flex items-center justify-between p-3 border border-[--border-color] rounded-lg hover:bg-[--background-secondary]"
|
||||
>
|
||||
<div>
|
||||
<div className="font-medium text-[--text-primary]">{org.name}</div>
|
||||
@@ -121,10 +121,9 @@ const OrgSelection: React.FC = () => {
|
||||
{org.role} • {org.onboardingCompleted ? 'Active' : 'Setup Required'}
|
||||
</div>
|
||||
</div>
|
||||
<Button variant="secondary" size="sm">
|
||||
Enter
|
||||
</Button>
|
||||
<FigmaPrimaryButton text="Enter" grow={false} containerExtra='h-8 self-center' size="tiny" onClick={() => handleSelectOrg(org.orgId)} />
|
||||
</div>
|
||||
|
||||
))}
|
||||
</div>
|
||||
</Card>
|
||||
@@ -165,7 +164,7 @@ const OrgSelection: React.FC = () => {
|
||||
value={newOrgName}
|
||||
onChange={(e) => setNewOrgName(e.target.value)}
|
||||
placeholder="Enter organization name"
|
||||
className="w-full px-3 py-2 border border-[--input-border] rounded-lg bg-[--input-bg] text-[--text-primary] placeholder-[--input-placeholder] focus:outline-none focus:ring-2 focus:ring-[--accent] focus:border-[--accent]"
|
||||
className="w-full px-3 py-2 border border-[--input-border] rounded-lg bg-[--input-bg] text-[--text-primary] placeholder-[--input-placeholder] focus:outline-none focus:ring-0 focus:ring-[--accent] focus:border-[--accent]"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex space-x-3">
|
||||
@@ -187,29 +186,6 @@ const OrgSelection: React.FC = () => {
|
||||
</div>
|
||||
)}
|
||||
</Card>
|
||||
|
||||
{/* Manual Invite Code Entry */}
|
||||
{!inviteCode && (
|
||||
<Card className="mt-6">
|
||||
<h2 className="text-lg font-semibold mb-4 text-[--text-primary]">Have an invite code?</h2>
|
||||
<div className="flex space-x-3">
|
||||
<input
|
||||
type="text"
|
||||
value={inviteCode}
|
||||
onChange={(e) => setInviteCode(e.target.value)}
|
||||
placeholder="Enter invite code"
|
||||
className="flex-1 px-3 py-2 border border-[--input-border] rounded-lg bg-[--input-bg] text-[--text-primary] placeholder-[--input-placeholder] focus:outline-none focus:ring-2 focus:ring-[--accent] focus:border-[--accent]"
|
||||
/>
|
||||
<Button
|
||||
onClick={handleJoinWithInvite}
|
||||
disabled={!inviteCode.trim() || isJoining}
|
||||
variant="secondary"
|
||||
>
|
||||
{isJoining ? 'Joining...' : 'Join'}
|
||||
</Button>
|
||||
</div>
|
||||
</Card>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -5,10 +5,13 @@ import { secureApi } from '../services/secureApi';
|
||||
import { CompanyReport, Employee, EmployeeReport } from '../types';
|
||||
import { SAMPLE_COMPANY_REPORT } from '../constants';
|
||||
import RadarPerformanceChart from '../components/charts/RadarPerformanceChart';
|
||||
import { downloadCompanyReportPDF, downloadEmployeeReportPDF } from '../utils/pdfUtils';
|
||||
import FigmaPrimaryButton from '../components/figma/FigmaButton';
|
||||
import { DownloadIcon } from '../components/figma/figmaIcon';
|
||||
|
||||
const Reports: React.FC = () => {
|
||||
const location = useLocation();
|
||||
const { employees, reports, submissions, user, isOwner, getFullCompanyReportHistory, generateEmployeeReport, generateCompanyReport, orgId } = useOrg();
|
||||
const { employees, reports, submissions, user, isOwner, getFullCompanyReportHistory, generateEmployeeReport, generateCompanyReport, orgId, org } = useOrg();
|
||||
const [companyReport, setCompanyReport] = useState<CompanyReport | null>(null);
|
||||
const [selectedReport, setSelectedReport] = useState<{ report: CompanyReport | EmployeeReport; type: 'company' | 'employee'; employeeName?: string } | null>(null);
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
@@ -166,12 +169,12 @@ const Reports: React.FC = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex-1 self-stretch rounded-3xl shadow-[0px_0px_15px_0px_rgba(0,0,0,0.08)] flex justify-between items-start overflow-hidden">
|
||||
<>
|
||||
{/* Middle Section - Employee List */}
|
||||
<div className="flex-1 self-stretch bg-[--Neutrals-NeutralSlate0] flex justify-start items-center">
|
||||
<div className="h-100vh flex-1 self-stretch bg-[--Neutrals-NeutralSlate0] flex justify-start items-center">
|
||||
<div className="flex-1 self-stretch max-w-64 min-w-64 border-r border-[--Neutrals-NeutralSlate200] inline-flex flex-col justify-start items-start">
|
||||
<div className="self-stretch p-5 inline-flex justify-start items-center gap-2.5">
|
||||
<div className="flex-1 justify-start text-[--Neutrals-NeutralSlate950] text-base font-medium font-['Inter'] leading-normal">Employees</div>
|
||||
<div className="flex-1 justify-start text-[--Text-Gray-950] text-base font-medium font-['Inter'] leading-normal">Employees</div>
|
||||
</div>
|
||||
<div className="self-stretch flex flex-col justify-start items-start gap-2">
|
||||
{/* Search */}
|
||||
@@ -188,7 +191,7 @@ const Reports: React.FC = () => {
|
||||
placeholder="Search"
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
className="flex-1 bg-transparent text-[--Neutrals-NeutralSlate500] text-sm font-normal font-['Inter'] leading-tight outline-none"
|
||||
className="flex-1 bg-transparent text-[--Text-Gray-500] text-sm font-normal font-['Inter'] leading-tight outline-none"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -204,11 +207,11 @@ const Reports: React.FC = () => {
|
||||
onClick={handleCompanyReportSelect}
|
||||
>
|
||||
<div className="w-7 h-7 p-1 bg-[--Brand-Orange] rounded-[666.67px] flex justify-center items-center">
|
||||
<div className="text-center justify-start text-[--Neutrals-NeutralSlate0] text-xs font-medium font-['Inter'] leading-none">
|
||||
<div className="text-center justify-start text-[--Text-Gray-0] text-xs font-medium font-['Inter'] leading-none">
|
||||
C
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-1 justify-start text-[--Neutrals-NeutralSlate950] text-sm font-normal font-['Inter'] leading-tight">Company Report</div>
|
||||
<div className="flex-1 justify-start text-[--Text-Gray-950] text-sm font-normal font-['Inter'] leading-tight">Company Report</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -226,7 +229,7 @@ const Reports: React.FC = () => {
|
||||
onClick={() => handleEmployeeSelect(employee)}
|
||||
>
|
||||
<div className="w-7 h-7 p-1 bg-[--Neutrals-NeutralSlate100] rounded-[666.67px] flex justify-center items-center relative">
|
||||
<div className="text-center justify-start text-[--Neutrals-NeutralSlate500] text-xs font-medium font-['Inter'] leading-none">
|
||||
<div className="text-center justify-start text-[--Text-Gray-500] text-xs font-medium font-['Inter'] leading-none">
|
||||
{employee.initials}
|
||||
</div>
|
||||
{/* Status indicator */}
|
||||
@@ -240,7 +243,7 @@ const Reports: React.FC = () => {
|
||||
<div className="absolute -top-1 -right-1 w-3 h-3 bg-gray-300 rounded-full" title="No submission yet" />
|
||||
)}
|
||||
</div>
|
||||
<div className="flex-1 justify-start text-[--Neutrals-NeutralSlate800] text-sm font-normal font-['Inter'] leading-tight">
|
||||
<div className="flex-1 justify-start text-[--Text-Gray-800] text-sm font-normal font-['Inter'] leading-tight">
|
||||
{employee.name}
|
||||
</div>
|
||||
{isGenerating && (
|
||||
@@ -261,6 +264,7 @@ const Reports: React.FC = () => {
|
||||
report={selectedReport.report as CompanyReport}
|
||||
onRegenerate={handleGenerateCompanyReport}
|
||||
isGenerating={generatingCompanyReport}
|
||||
org={org}
|
||||
/>
|
||||
) : (
|
||||
(() => {
|
||||
@@ -277,6 +281,8 @@ const Reports: React.FC = () => {
|
||||
isGenerating={generatingEmployeeReport === employeeId}
|
||||
hasSubmission={!!submissions[employeeId]}
|
||||
showGenerateButton={!reports[employeeId] && !!submissions[employeeId]}
|
||||
employees={employees}
|
||||
org={org}
|
||||
/>
|
||||
);
|
||||
})()
|
||||
@@ -284,10 +290,10 @@ const Reports: React.FC = () => {
|
||||
) : (
|
||||
<div className="flex-1 flex items-center justify-center">
|
||||
<div className="text-center">
|
||||
<h3 className="text-lg font-semibold text-[--Neutrals-NeutralSlate950] mb-2">
|
||||
<h3 className="text-lg font-semibold text-[--Text-Gray-950] mb-2">
|
||||
Select a Report
|
||||
</h3>
|
||||
<p className="text-[--Neutrals-NeutralSlate500]">
|
||||
<p className="text-[--Text-Gray-500]">
|
||||
Choose a company or employee report from the list to view details.
|
||||
</p>
|
||||
</div>
|
||||
@@ -295,7 +301,7 @@ const Reports: React.FC = () => {
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -304,7 +310,8 @@ const CompanyReportContent: React.FC<{
|
||||
report: CompanyReport;
|
||||
onRegenerate: () => void;
|
||||
isGenerating: boolean;
|
||||
}> = ({ report, onRegenerate, isGenerating }) => {
|
||||
org?: any;
|
||||
}> = ({ report, onRegenerate, isGenerating, org }) => {
|
||||
// Default to the first department in the array
|
||||
const [activeDepartmentTab, setActiveDepartmentTab] = useState(() =>
|
||||
report?.gradingBreakdown?.[0]?.departmentNameShort || 'Campaigns'
|
||||
@@ -317,7 +324,7 @@ const CompanyReportContent: React.FC<{
|
||||
<>
|
||||
{/* Header */}
|
||||
<div className="self-stretch px-5 py-3 inline-flex justify-start items-center gap-2.5">
|
||||
<div className="flex-1 justify-start text-[--Neutrals-NeutralSlate800] text-base font-medium font-['Inter'] leading-normal">
|
||||
<div className="flex-1 justify-start text-[--Text-Gray-800] text-base font-medium font-['Inter'] leading-normal">
|
||||
Company Report
|
||||
</div>
|
||||
<div className="flex justify-start items-center gap-3">
|
||||
@@ -337,21 +344,17 @@ const CompanyReportContent: React.FC<{
|
||||
)}
|
||||
</div>
|
||||
<div className="px-1 flex justify-center items-center">
|
||||
<div className="justify-center text-[--Neutrals-NeutralSlate800] text-sm font-medium font-['Inter'] leading-tight">
|
||||
<div className="justify-center text-[--Text-Gray-800] text-sm font-medium font-['Inter'] leading-tight">
|
||||
{isGenerating ? 'Generating...' : 'Refresh Report'}
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
<div className="px-3 py-2.5 bg-[--Brand-Orange] rounded-[999px] outline outline-2 outline-offset-[-2px] outline-blue-400 flex justify-center items-center gap-1 overflow-hidden">
|
||||
<div className="relative">
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M14 14H2M12 7.33333L8 11.3333M8 11.3333L4 7.33333M8 11.3333V2" stroke="var(--Neutrals-NeutralSlate0)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</svg>
|
||||
</div>
|
||||
<div className="px-1 flex justify-center items-center">
|
||||
<div className="justify-center text-[--Neutrals-NeutralSlate0] text-sm font-medium font-['Inter'] leading-tight">Download as PDF</div>
|
||||
</div>
|
||||
</div>
|
||||
<FigmaPrimaryButton
|
||||
onClick={() => downloadCompanyReportPDF(report, org?.companyName || 'Company')}
|
||||
text="Download as PDF"
|
||||
size="tiny"
|
||||
iconLeft={<DownloadIcon />}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -360,14 +363,14 @@ const CompanyReportContent: React.FC<{
|
||||
{/* Company Weaknesses */}
|
||||
<div className="w-full p-3 bg-[--Neutrals-NeutralSlate100] rounded-[20px] shadow-[0px_1px_2px_0px_rgba(0,0,0,0.02)] flex flex-col justify-center items-start gap-1 overflow-hidden">
|
||||
<div className="self-stretch px-3 py-2 inline-flex justify-start items-center gap-2">
|
||||
<div className="justify-start text-[--Neutrals-NeutralSlate950] text-xl font-medium font-['Neue_Montreal'] leading-normal">Company Weaknesses</div>
|
||||
<div className="justify-start text-[--Text-Gray-950] text-xl font-medium font-['Neue_Montreal'] leading-normal">Company Weaknesses</div>
|
||||
</div>
|
||||
<div className="self-stretch p-6 bg-[--Neutrals-NeutralSlate100] rounded-2xl outline outline-1 outline-offset-[-1px] outline-[--Neutrals-NeutralSlate200] flex flex-col justify-start items-start gap-4">
|
||||
{report?.weaknesses?.map((weakness, index) => (
|
||||
<div key={index}>
|
||||
<div className="self-stretch inline-flex justify-between items-start">
|
||||
<div className="w-48 justify-start text-[--Neutrals-NeutralSlate800] text-base font-semibold font-['Inter'] leading-normal">{weakness.title}:</div>
|
||||
<div className="flex-1 justify-start text-[--Neutrals-NeutralSlate800] text-base font-normal font-['Inter'] leading-normal">{weakness.description}</div>
|
||||
<div className="w-48 justify-start text-[--Text-Gray-800] text-base font-semibold font-['Inter'] leading-normal">{weakness.title}:</div>
|
||||
<div className="flex-1 justify-start text-[--Text-Gray-800] text-base font-normal font-['Inter'] leading-normal">{weakness.description}</div>
|
||||
</div>
|
||||
{index < (report?.weaknesses?.length || 0) - 1 && (
|
||||
<div className="mt-4 self-stretch w-full flex">
|
||||
@@ -379,8 +382,8 @@ const CompanyReportContent: React.FC<{
|
||||
</div>
|
||||
)) || (
|
||||
<div className="self-stretch inline-flex justify-between items-start">
|
||||
<div className="w-48 justify-start text-[--Neutrals-NeutralSlate800] text-base font-semibold font-['Inter'] leading-normal">Lack of Structure:</div>
|
||||
<div className="flex-1 justify-start text-[--Neutrals-NeutralSlate800] text-base font-normal font-['Inter'] leading-normal">People are confused about their roles. No one knows who's responsible.</div>
|
||||
<div className="w-48 justify-start text-[--Text-Gray-800] text-base font-semibold font-['Inter'] leading-normal">Lack of Structure:</div>
|
||||
<div className="flex-1 justify-start text-[--Text-Gray-800] text-base font-normal font-['Inter'] leading-normal">People are confused about their roles. No one knows who's responsible.</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@@ -390,13 +393,13 @@ const CompanyReportContent: React.FC<{
|
||||
{report?.personnelChanges && (
|
||||
<div className="w-full p-3 bg-[--Neutrals-NeutralSlate100] rounded-[20px] shadow-[0px_1px_2px_0px_rgba(0,0,0,0.02)] flex flex-col justify-center items-start gap-1 overflow-hidden">
|
||||
<div className="self-stretch px-3 py-2 inline-flex justify-start items-center gap-2">
|
||||
<div className="justify-start text-[--Neutrals-NeutralSlate950] text-xl font-medium font-['Neue_Montreal'] leading-normal">Personnel Changes</div>
|
||||
<div className="justify-start text-[--Text-Gray-950] text-xl font-medium font-['Neue_Montreal'] leading-normal">Personnel Changes</div>
|
||||
</div>
|
||||
<div className="self-stretch p-6 bg-[--Neutrals-NeutralSlate50] rounded-2xl outline outline-1 outline-offset-[-1px] outline-[--Neutrals-NeutralSlate200] flex flex-col justify-start items-start gap-6">
|
||||
{/* New Hires */}
|
||||
{report.personnelChanges.newHires && report.personnelChanges.newHires.length > 0 && (
|
||||
<div className="self-stretch flex flex-col justify-start items-start gap-3">
|
||||
<div className="text-[--Neutrals-NeutralSlate800] text-base font-semibold font-['Inter'] leading-normal">New Hires</div>
|
||||
<div className="text-[--Text-Gray-800] text-base font-semibold font-['Inter'] leading-normal">New Hires</div>
|
||||
{report.personnelChanges.newHires.map((hire, index) => (
|
||||
<div key={index} className="self-stretch p-3 bg-green-50 rounded-lg border border-green-200">
|
||||
<div className="font-medium text-green-800">{hire.name} - {hire.role}</div>
|
||||
@@ -410,7 +413,7 @@ const CompanyReportContent: React.FC<{
|
||||
{/* Promotions */}
|
||||
{report.personnelChanges.promotions && report.personnelChanges.promotions.length > 0 && (
|
||||
<div className="self-stretch flex flex-col justify-start items-start gap-3">
|
||||
<div className="text-[--Neutrals-NeutralSlate800] text-base font-semibold font-['Inter'] leading-normal">Promotions</div>
|
||||
<div className="text-[--Text-Gray-800] text-base font-semibold font-['Inter'] leading-normal">Promotions</div>
|
||||
{report.personnelChanges.promotions.map((promotion, index) => (
|
||||
<div key={index} className="self-stretch p-3 bg-blue-50 rounded-lg border border-blue-200">
|
||||
<div className="font-medium text-blue-800">{promotion.name}</div>
|
||||
@@ -424,7 +427,7 @@ const CompanyReportContent: React.FC<{
|
||||
{/* Departures */}
|
||||
{report.personnelChanges.departures && report.personnelChanges.departures.length > 0 && (
|
||||
<div className="self-stretch flex flex-col justify-start items-start gap-3">
|
||||
<div className="text-[--Neutrals-NeutralSlate800] text-base font-semibold font-['Inter'] leading-normal">Departures</div>
|
||||
<div className="text-[--Text-Gray-800] text-base font-semibold font-['Inter'] leading-normal">Departures</div>
|
||||
{report.personnelChanges.departures.map((departure, index) => (
|
||||
<div key={index} className="self-stretch p-3 bg-red-50 rounded-lg border border-red-200">
|
||||
<div className="font-medium text-red-800">{departure.name}</div>
|
||||
@@ -440,7 +443,7 @@ const CompanyReportContent: React.FC<{
|
||||
{(!report.personnelChanges.newHires || report.personnelChanges.newHires.length === 0) &&
|
||||
(!report.personnelChanges.promotions || report.personnelChanges.promotions.length === 0) &&
|
||||
(!report.personnelChanges.departures || report.personnelChanges.departures.length === 0) && (
|
||||
<div className="self-stretch text-[--Neutrals-NeutralSlate500] text-base font-normal font-['Inter'] leading-normal">
|
||||
<div className="self-stretch text-[--Text-Gray-500] text-base font-normal font-['Inter'] leading-normal">
|
||||
No personnel changes to report.
|
||||
</div>
|
||||
)}
|
||||
@@ -452,14 +455,14 @@ const CompanyReportContent: React.FC<{
|
||||
{report?.immediateHiringNeeds && report.immediateHiringNeeds.length > 0 && (
|
||||
<div className="w-full p-3 bg-[--Neutrals-NeutralSlate100] rounded-[20px] shadow-[0px_1px_2px_0px_rgba(0,0,0,0.02)] flex flex-col justify-center items-start gap-1 overflow-hidden">
|
||||
<div className="self-stretch px-3 py-2 inline-flex justify-start items-center gap-2">
|
||||
<div className="justify-start text-[--Neutrals-NeutralSlate950] text-xl font-medium font-['Neue_Montreal'] leading-normal">Immediate Hiring Needs</div>
|
||||
<div className="justify-start text-[--Text-Gray-950] text-xl font-medium font-['Neue_Montreal'] leading-normal">Immediate Hiring Needs</div>
|
||||
</div>
|
||||
<div className="self-stretch p-6 bg-[--Neutrals-NeutralSlate50] rounded-2xl outline outline-1 outline-offset-[-1px] outline-[--Neutrals-NeutralSlate200] inline-flex flex-col justify-start items-start gap-4">
|
||||
{report.immediateHiringNeeds.map((need, index) => (
|
||||
<React.Fragment key={index}>
|
||||
<div key={index} className="self-stretch content-end inline-flex justify-around items-center ">
|
||||
<div className="flex-2 w-max p-3 justify-start text-[--Neutrals-NeutralSlate800] text-base font-semibold font-['Inter'] leading-normal">{need.role} - {need.department}</div>
|
||||
<div className="flex-9 justify-start text-[--Neutrals-NeutralSlate800] text-base font-normal font-['Inter'] leading-normal">{need.reasoning}</div>
|
||||
<div className="flex-2 w-max p-3 justify-start text-[--Text-Gray-800] text-base font-semibold font-['Inter'] leading-normal">{need.role} - {need.department}</div>
|
||||
<div className="flex-9 justify-start text-[--Text-Gray-800] text-base font-normal font-['Inter'] leading-normal">{need.reasoning}</div>
|
||||
</div>
|
||||
{index !== report.immediateHiringNeeds.length - 1 && (
|
||||
<div className="inline-flex self-stretch" data-svg-wrapper>
|
||||
@@ -478,14 +481,14 @@ const CompanyReportContent: React.FC<{
|
||||
{report?.forwardOperatingPlan && (
|
||||
<div className="w-full self-stretch p-3 bg-[--Neutrals-NeutralSlate100] rounded-[20px] shadow-[0px_1px_2px_0px_rgba(0,0,0,0.02)] flex flex-col justify-center items-start gap-1 overflow-hidden">
|
||||
<div className="self-stretch px-3 py-2 inline-flex justify-start items-center gap-2">
|
||||
<div className="justify-start text-[--Neutrals-NeutralSlate950] text-xl font-medium font-['Neue_Montreal'] leading-normal">Forward Plan</div>
|
||||
<div className="justify-start text-[--Text-Gray-950] text-xl font-medium font-['Neue_Montreal'] leading-normal">Forward Plan</div>
|
||||
</div>
|
||||
<div className="self-stretch p-6 bg-[--Neutrals-NeutralSlate50] rounded-2xl outline outline-1 outline-offset-[-1px] outline-[--Neutrals-NeutralSlate200] flex flex-col justify-start items-start gap-4">
|
||||
{
|
||||
report.forwardOperatingPlan.map((plan, index) => (
|
||||
<div key={index} className="self-stretch flex flex-col justify-start items-start gap-4">
|
||||
<div className="self-stretch flex flex-col justify-start items-start gap-4">
|
||||
<div className="justify-start text-[--Neutrals-NeutralSlate800] text-base font-normal tracking-wide font-['Neue_Montreal'] leading-normal">{plan.title}</div>
|
||||
<div className="justify-start text-[--Text-Gray-800] text-base font-normal tracking-wide font-['Neue_Montreal'] leading-normal">{plan.title}</div>
|
||||
<div className="self-stretch flex flex-col justify-start items-start gap-3">
|
||||
<ul className="list-none space-y-1">
|
||||
{plan.details.map((detail, idx) => (
|
||||
@@ -511,7 +514,7 @@ const CompanyReportContent: React.FC<{
|
||||
{report?.strengths && (
|
||||
<div className="self-stretch p-3 bg-[--Neutrals-NeutralSlate100] rounded-[20px] shadow-[0px_1px_2px_0px_rgba(0,0,0,0.02)] inline-flex flex-col justify-center items-start gap-1 overflow-hidden">
|
||||
<div className="self-stretch px-3 py-2 inline-flex justify-start items-center gap-2">
|
||||
<div className="justify-start text-[--Neutrals-NeutralSlate950] text-xl font-medium font-['Neue_Montreal'] leading-normal">Strengths</div>
|
||||
<div className="justify-start text-[--Text-Gray-950] text-xl font-medium font-['Neue_Montreal'] leading-normal">Strengths</div>
|
||||
</div>
|
||||
<div className="self-stretch p-6 bg-[--Neutrals-NeutralSlate50] rounded-2xl outline outline-1 outline-offset-[-1px] outline-[--Neutrals-NeutralSlate200] flex flex-col justify-start items-start gap-4">
|
||||
{report.strengths.map((strength, idx) => (
|
||||
@@ -524,7 +527,7 @@ const CompanyReportContent: React.FC<{
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-1 justify-start text-[--Neutrals-NeutralSlate800] text-base font-normal font-['Inter'] leading-normal">{strength}</div>
|
||||
<div className="flex-1 justify-start text-[--Text-Gray-800] text-base font-normal font-['Inter'] leading-normal">{strength}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
@@ -538,20 +541,20 @@ const CompanyReportContent: React.FC<{
|
||||
{report?.organizationalImpactSummary && (
|
||||
<div className="w-full p-3 bg-[--Neutrals-NeutralSlate100] rounded-[20px] shadow-[0px_1px_2px_0px_rgba(0,0,0,0.02)] flex flex-col justify-center items-start gap-1 overflow-hidden">
|
||||
<div className="self-stretch px-3 py-2 inline-flex justify-start items-center gap-2">
|
||||
<div className="justify-start text-[--Neutrals-NeutralSlate950] text-xl font-medium font-['Neue_Montreal'] leading-normal">Organizational Impact Summary</div>
|
||||
<div className="justify-start text-[--Text-Gray-950] text-xl font-medium font-['Neue_Montreal'] leading-normal">Organizational Impact Summary</div>
|
||||
</div>
|
||||
<div className="self-stretch p-6 bg-[--Neutrals-NeutralSlate50] rounded-2xl outline outline-1 outline-offset-[-1px] outline-[--Neutrals-NeutralSlate200] flex flex-col justify-start items-start gap-6">
|
||||
|
||||
<div className="self-stretch flex flex-col justify-start items-start gap-3">
|
||||
<div className="flex justify-start items-center gap-2">
|
||||
<div className="w-3 h-3 bg-red-500 rounded-full"></div>
|
||||
<div className="text-[--Neutrals-NeutralSlate800] text-base font-semibold font-['Inter'] leading-normal">Mission Critical</div>
|
||||
<div className="text-[--Text-Gray-800] text-base font-semibold font-['Inter'] leading-normal">Mission Critical</div>
|
||||
</div>
|
||||
<div className="self-stretch flex flex-col justify-start items-start gap-3">
|
||||
{report.organizationalImpactSummary.missionCritical.map((employee, index) => (
|
||||
<div key={index} className="self-stretch flex justify-between items-start">
|
||||
<div className="w-48 text-[--Neutrals-NeutralSlate800] text-sm font-medium font-['Inter'] leading-tight">{employee.employeeName}</div>
|
||||
<div className="flex-1 text-[--Neutrals-NeutralSlate800] text-sm font-normal font-['Inter'] leading-tight">{employee.description}</div>
|
||||
<div className="w-48 text-[--Text-Gray-800] text-sm font-medium font-['Inter'] leading-tight">{employee.employeeName}</div>
|
||||
<div className="flex-1 text-[--Text-Gray-800] text-sm font-normal font-['Inter'] leading-tight">{employee.description}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
@@ -560,13 +563,13 @@ const CompanyReportContent: React.FC<{
|
||||
<div className="self-stretch flex flex-col justify-start items-start gap-3">
|
||||
<div className="flex justify-start items-center gap-2">
|
||||
<div className="w-3 h-3 bg-orange-500 rounded-full"></div>
|
||||
<div className="text-[--Neutrals-NeutralSlate800] text-base font-semibold font-['Inter'] leading-normal">Highly Valuable</div>
|
||||
<div className="text-[--Text-Gray-800] text-base font-semibold font-['Inter'] leading-normal">Highly Valuable</div>
|
||||
</div>
|
||||
<div className="self-stretch flex flex-col justify-start items-start gap-3">
|
||||
{report.organizationalImpactSummary.highlyValuable.map((employee, index) => (
|
||||
<div key={index} className="self-stretch flex justify-between items-start">
|
||||
<div className="w-48 text-[--Neutrals-NeutralSlate800] text-sm font-medium font-['Inter'] leading-tight">{employee.employeeName}</div>
|
||||
<div className="flex-1 text-[--Neutrals-NeutralSlate800] text-sm font-normal font-['Inter'] leading-tight">{employee.description}</div>
|
||||
<div className="w-48 text-[--Text-Gray-800] text-sm font-medium font-['Inter'] leading-tight">{employee.employeeName}</div>
|
||||
<div className="flex-1 text-[--Text-Gray-800] text-sm font-normal font-['Inter'] leading-tight">{employee.description}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
@@ -575,13 +578,13 @@ const CompanyReportContent: React.FC<{
|
||||
<div className="self-stretch flex flex-col justify-start items-start gap-3">
|
||||
<div className="flex justify-start items-center gap-2">
|
||||
<div className="w-3 h-3 bg-yellow-500 rounded-full"></div>
|
||||
<div className="text-[--Neutrals-NeutralSlate800] text-base font-semibold font-['Inter'] leading-normal">Core Support</div>
|
||||
<div className="text-[--Text-Gray-800] text-base font-semibold font-['Inter'] leading-normal">Core Support</div>
|
||||
</div>
|
||||
<div className="self-stretch flex flex-col justify-start items-start gap-3">
|
||||
{report.organizationalImpactSummary.coreSupport.map((employee, index) => (
|
||||
<div key={index} className="self-stretch flex justify-between items-start">
|
||||
<div className="w-48 text-[--Neutrals-NeutralSlate800] text-sm font-medium font-['Inter'] leading-tight">{employee.employeeName}</div>
|
||||
<div className="flex-1 text-[--Neutrals-NeutralSlate800] text-sm font-normal font-['Inter'] leading-tight">{employee.description}</div>
|
||||
<div className="w-48 text-[--Text-Gray-800] text-sm font-medium font-['Inter'] leading-tight">{employee.employeeName}</div>
|
||||
<div className="flex-1 text-[--Text-Gray-800] text-sm font-normal font-['Inter'] leading-tight">{employee.description}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
@@ -590,13 +593,13 @@ const CompanyReportContent: React.FC<{
|
||||
<div className="self-stretch flex flex-col justify-start items-start gap-3">
|
||||
<div className="flex justify-start items-center gap-2">
|
||||
<div className="w-3 h-3 bg-green-500 rounded-full"></div>
|
||||
<div className="text-[--Neutrals-NeutralSlate800] text-base font-semibold font-['Inter'] leading-normal">Low Criticality</div>
|
||||
<div className="text-[--Text-Gray-800] text-base font-semibold font-['Inter'] leading-normal">Low Criticality</div>
|
||||
</div>
|
||||
<div className="self-stretch flex flex-col justify-start items-start gap-3">
|
||||
{report.organizationalImpactSummary.lowCriticality.map((employee, index) => (
|
||||
<div key={index} className="self-stretch flex justify-between items-start">
|
||||
<div className="w-48 text-[--Neutrals-NeutralSlate800] text-sm font-medium font-['Inter'] leading-tight">{employee.employeeName}</div>
|
||||
<div className="flex-1 text-[--Neutrals-NeutralSlate800] text-sm font-normal font-['Inter'] leading-tight">{employee.description}</div>
|
||||
<div className="w-48 text-[--Text-Gray-800] text-sm font-medium font-['Inter'] leading-tight">{employee.employeeName}</div>
|
||||
<div className="flex-1 text-[--Text-Gray-800] text-sm font-normal font-['Inter'] leading-tight">{employee.description}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
@@ -608,7 +611,7 @@ const CompanyReportContent: React.FC<{
|
||||
|
||||
<div className="w-full p-3 bg-[--Neutrals-NeutralSlate100] rounded-[20px] shadow-[0px_1px_2px_0px_rgba(0,0,0,0.02)] flex flex-col justify-center items-start gap-1 overflow-hidden">
|
||||
<div className="self-stretch px-3 py-2 inline-flex justify-start items-center gap-2">
|
||||
<div className="justify-start text-[--Neutrals-NeutralSlate950] text-xl font-medium font-['Neue_Montreal'] leading-normal">Organizational Impact Summary</div>
|
||||
<div className="justify-start text-[--Text-Gray-950] text-xl font-medium font-['Neue_Montreal'] leading-normal">Organizational Impact Summary</div>
|
||||
</div>
|
||||
<div className="self-stretch flex flex-col justify-start items-start gap-3">
|
||||
<div className="p-1 bg-[--Neutrals-NeutralSlate200] rounded-full outline outline-1 outline-offset-[-1px] outline-[--Neutrals-NeutralSlate50] inline-flex justify-start items-center gap-1">
|
||||
@@ -623,7 +626,7 @@ const CompanyReportContent: React.FC<{
|
||||
onClick={() => setActiveImpactSummary(dept.category)}
|
||||
>
|
||||
<div className="px-0.5 flex justify-center items-center">
|
||||
<div className={`justify-start text-xs flex items-center font-medium font-['Inter'] leading-none ${activeImpactSummary === dept.category ? 'text-[--Neutrals-NeutralSlate950]' : 'text-zinc-600'
|
||||
<div className={`justify-start text-xs flex items-center font-medium font-['Inter'] leading-none ${activeImpactSummary === dept.category ? 'text-[--Text-Gray-950]' : 'text-zinc-600'
|
||||
}`}>
|
||||
<span className={`flex-shrink-0 w-2 h-2 rounded-full mr-2
|
||||
${dept?.employees.length === 0 ? 'blinkLightEmpty bg-[--Neutrals-NeutralSlate100]' : ''}
|
||||
@@ -655,10 +658,10 @@ const CompanyReportContent: React.FC<{
|
||||
<div className="self-stretch flex flex-col justify-start items-start gap-4">
|
||||
<div className="self-stretch flex flex-col justify-start items-start gap-4">
|
||||
<div className="self-stretch inline-flex justify-start items-center">
|
||||
<div className="w-48 justify-start text-[--Neutrals-NeutralSlate800] text-base font-semibold font-['Inter'] leading-normal w-fit">{item.employeeName} - {item.impact}</div>
|
||||
<div className="flex-1 text-right justify-start text-[--Neutrals-NeutralSlate800] text-base font-normal font-['Inter'] leading-normal">{item.suggestedPay}</div>
|
||||
<div className="w-48 justify-start text-[--Text-Gray-800] text-base font-semibold font-['Inter'] leading-normal w-fit">{item.employeeName} - {item.impact}</div>
|
||||
<div className="flex-1 text-right justify-start text-[--Text-Gray-800] text-base font-normal font-['Inter'] leading-normal">{item.suggestedPay}</div>
|
||||
</div>
|
||||
<div className="self-stretch justify-start text-[--Neutrals-NeutralSlate800] text-base font-normal font-['Inter'] leading-normal">{item.description}</div>
|
||||
<div className="self-stretch justify-start text-[--Text-Gray-800] text-base font-normal font-['Inter'] leading-normal">{item.description}</div>
|
||||
</div>
|
||||
<div className="self-stretch h-0 outline outline-1 outline-offset-[-0.50px] outline-[--Neutrals-NeutralSlate200]" />
|
||||
</div>
|
||||
@@ -674,7 +677,7 @@ const CompanyReportContent: React.FC<{
|
||||
{/* Grading Overview */}
|
||||
<div className="w-full p-3 bg-[--Neutrals-NeutralSlate100] rounded-[20px] shadow-[0px_1px_2px_0px_rgba(0,0,0,0.02)] flex flex-col justify-center items-start gap-1 overflow-hidden">
|
||||
<div className="self-stretch px-3 py-2 inline-flex justify-start items-center gap-2">
|
||||
<div className="justify-start text-[--Neutrals-NeutralSlate950] text-xl font-medium font-['Neue_Montreal'] leading-normal">Grading Overview</div>
|
||||
<div className="justify-start text-[--Text-Gray-950] text-xl font-medium font-['Neue_Montreal'] leading-normal">Grading Overview</div>
|
||||
</div>
|
||||
<div className="self-stretch flex flex-col justify-start items-start gap-3">
|
||||
<div className="p-1 bg-[--Neutrals-NeutralSlate200] rounded-full outline outline-1 outline-offset-[-1px] outline-[--Neutrals-NeutralSlate50] inline-flex justify-start items-center gap-1">
|
||||
@@ -689,7 +692,7 @@ const CompanyReportContent: React.FC<{
|
||||
onClick={() => setActiveDepartmentTab(dept.departmentNameShort)}
|
||||
>
|
||||
<div className="px-0.5 flex justify-center items-center">
|
||||
<div className={`justify-start text-xs font-medium font-['Inter'] leading-none ${activeDepartmentTab === dept.departmentNameShort ? 'text-[--Neutrals-NeutralSlate950]' : 'text-zinc-600'
|
||||
<div className={`justify-start text-xs font-medium font-['Inter'] leading-none ${activeDepartmentTab === dept.departmentNameShort ? 'text-[--Text-Gray-950]' : 'text-zinc-600'
|
||||
}`}>
|
||||
{dept.departmentNameShort}
|
||||
</div>
|
||||
@@ -707,11 +710,11 @@ const CompanyReportContent: React.FC<{
|
||||
{/* Department Overview Section */}
|
||||
<div className="self-stretch p-5 bg-[--Neutrals-NeutralSlate100] rounded-2xl outline outline-1 outline-offset-[-1px] outline-[--Neutrals-NeutralSlate200] flex flex-col justify-start items-start gap-3 overflow-hidden">
|
||||
<div className="self-stretch inline-flex justify-between items-center">
|
||||
<div className="w-48 justify-start text-[--Neutrals-NeutralSlate800] text-base font-semibold font-['Inter'] leading-normal">Department Overview</div>
|
||||
<div className="w-48 justify-start text-[--Text-Gray-800] text-base font-semibold font-['Inter'] leading-normal">Department Overview</div>
|
||||
<div className="pl-3 pr-1 py-1 bg-green-500/10 rounded-2xl flex justify-start items-center gap-2">
|
||||
<div className="justify-start text-[--Neutrals-NeutralSlate800] text-sm font-medium font-['Inter'] leading-tight">Overall Grade</div>
|
||||
<div className="justify-start text-[--Text-Gray-800] text-sm font-medium font-['Inter'] leading-tight">Overall Grade</div>
|
||||
<div className="px-2 bg-green-500 rounded-2xl flex justify-start items-center">
|
||||
<div className="text-center justify-start text-[--Neutrals-NeutralSlate0] text-sm font-medium font-['Inter'] leading-tight">A</div>
|
||||
<div className="text-center justify-start text-[--Text-Gray-0] text-sm font-medium font-['Inter'] leading-tight">A</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -720,14 +723,14 @@ const CompanyReportContent: React.FC<{
|
||||
<path d="M0 1H752" stroke="var(--Neutrals-NeutralSlate300)" />
|
||||
</svg>
|
||||
</div>
|
||||
<div className="self-stretch justify-start text-[--Neutrals-NeutralSlate800] text-base font-normal font-['Inter'] leading-normal">
|
||||
<div className="self-stretch justify-start text-[--Text-Gray-800] text-base font-normal font-['Inter'] leading-normal">
|
||||
Overall company performance shows strong collaboration and delivery with opportunities for process improvement.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Employee Radar Charts Section */}
|
||||
<div className="self-stretch flex flex-col justify-start items-start gap-4">
|
||||
<div className="text-[--Neutrals-NeutralSlate800] text-base font-semibold font-['Inter'] leading-normal">Team Performance Analysis</div>
|
||||
<div className="text-[--Text-Gray-800] text-base font-semibold font-['Inter'] leading-normal">Team Performance Analysis</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 w-full">
|
||||
{currentDepartment.teamScores?.map((teamScore, index) => {
|
||||
const radarData = [
|
||||
@@ -741,7 +744,7 @@ const CompanyReportContent: React.FC<{
|
||||
return (
|
||||
<div key={index} className="p-4 bg-[--Neutrals-NeutralSlate50] rounded-lg shadow-sm border border-[--Neutrals-NeutralSlate200] flex flex-col justify-start items-center gap-3">
|
||||
<div className="flex flex-col items-center gap-1">
|
||||
<h3 className="text-base font-semibold text-[--Neutrals-NeutralSlate800]">{teamScore.employeeName}</h3>
|
||||
<h3 className="text-base font-semibold text-[--Text-Gray-800]">{teamScore.employeeName}</h3>
|
||||
<div className={`px-2 py-1 rounded-full text-xs font-medium ${teamScore.grade.startsWith('A') ? 'bg-green-100 text-green-800' :
|
||||
teamScore.grade.startsWith('B') ? 'bg-blue-100 text-blue-800' :
|
||||
teamScore.grade.startsWith('C') ? 'bg-yellow-100 text-yellow-800' :
|
||||
@@ -785,12 +788,14 @@ const EmployeeReportContent: React.FC<{
|
||||
isGenerating?: boolean;
|
||||
hasSubmission?: boolean;
|
||||
showGenerateButton?: boolean;
|
||||
}> = ({ report, employeeName, onGenerateReport, isGenerating = false, hasSubmission = false, showGenerateButton = false }) => {
|
||||
employees?: Employee[];
|
||||
org?: any;
|
||||
}> = ({ report, employeeName, onGenerateReport, isGenerating = false, hasSubmission = false, showGenerateButton = false, employees, org }) => {
|
||||
return (
|
||||
<>
|
||||
{/* Header */}
|
||||
<div className="self-stretch px-5 py-3 inline-flex justify-start items-center gap-2.5">
|
||||
<div className="flex-1 justify-start text-[--Neutrals-NeutralSlate800] text-base font-medium font-['Inter'] leading-normal">
|
||||
<div className="flex-1 justify-start text-[--Text-Gray-800] text-base font-medium font-['Inter'] leading-normal">
|
||||
{employeeName}'s Report
|
||||
</div>
|
||||
<div className="flex justify-start items-center gap-3">
|
||||
@@ -820,16 +825,18 @@ const EmployeeReportContent: React.FC<{
|
||||
|
||||
{/* Download PDF Button - only show for actual reports */}
|
||||
{!showGenerateButton && (
|
||||
<div className="px-3 py-2.5 bg-[--Brand-Orange] rounded-[999px] outline outline-2 outline-offset-[-2px] outline-blue-400 flex justify-center items-center gap-1 overflow-hidden">
|
||||
<div className="relative">
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M14 14H2M12 7.33333L8 11.3333M8 11.3333L4 7.33333M8 11.3333V2" stroke="var(--white, white)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</svg>
|
||||
</div>
|
||||
<div className="px-1 flex justify-center items-center">
|
||||
<div className="justify-center text-white text-sm font-medium font-['Inter'] leading-tight">Download as PDF</div>
|
||||
</div>
|
||||
</div>
|
||||
<FigmaPrimaryButton
|
||||
containerExtra='h-9.5'
|
||||
text="Download as PDF"
|
||||
onClick={() => {
|
||||
const employee = employees.find(emp => emp.name === employeeName);
|
||||
if (employee) {
|
||||
downloadEmployeeReportPDF(employee, report, org?.companyName || 'Company');
|
||||
}
|
||||
}}
|
||||
size="tiny"
|
||||
iconLeft={<DownloadIcon />}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
@@ -839,10 +846,10 @@ const EmployeeReportContent: React.FC<{
|
||||
{/* Role & Responsibilities */}
|
||||
<div className="w-full p-3 bg-[--Neutrals-NeutralSlate100] rounded-[20px] shadow-[0px_1px_2px_0px_rgba(0,0,0,0.02)] flex flex-col justify-center items-start gap-1 overflow-hidden">
|
||||
<div className="self-stretch px-3 py-2 inline-flex justify-start items-center gap-2">
|
||||
<div className="justify-start text-Text-Dark-950 text-xl font-medium font-['Neue_Montreal'] leading-normal">Role & Responsibilities</div>
|
||||
<div className="justify-start text-[--Text-Dark-950] text-xl font-medium font-['Neue_Montreal'] leading-normal">Role & Responsibilities</div>
|
||||
</div>
|
||||
<div className="self-stretch p-6 bg-Light-Grays-l-gray08 rounded-2xl outline outline-1 outline-offset-[-1px] outline-Text-Gray-200 flex flex-col justify-start items-start gap-4">
|
||||
<p className="text-Text-Gray-800 text-base font-normal font-['Inter'] leading-normal">
|
||||
<p className="text-[--Text-Gray-800] text-base font-normal font-['Inter'] leading-normal">
|
||||
{report.roleAndOutput?.responsibilities || 'No role information available.'}
|
||||
</p>
|
||||
</div>
|
||||
@@ -852,10 +859,10 @@ const EmployeeReportContent: React.FC<{
|
||||
{report.roleAndOutput && report.roleAndOutput?.selfRatedOutput && (
|
||||
<div className="w-full p-3 bg-[--Neutrals-NeutralSlate100] rounded-[20px] shadow-[0px_1px_2px_0px_rgba(0,0,0,0.02)] flex flex-col justify-center items-start gap-1 overflow-hidden">
|
||||
<div className="self-stretch px-3 py-2 inline-flex justify-start items-center gap-2">
|
||||
<div className="justify-start text-Text-Dark-950 text-xl font-medium font-['Neue_Montreal'] leading-normal">Self-Rated Output</div>
|
||||
<div className="justify-start text-[--Text-Dark-950] text-xl font-medium font-['Neue_Montreal'] leading-normal">Self-Rated Output</div>
|
||||
</div>
|
||||
<div className="self-stretch p-6 bg-Light-Grays-l-gray08 rounded-2xl outline outline-1 outline-offset-[-1px] outline-Text-Gray-200 flex flex-col justify-start items-start gap-4">
|
||||
<p className="text-Text-Gray-800 text-base font-normal font-['Inter'] leading-normal">
|
||||
<p className="text-[--Text-Gray-800] text-base font-normal font-['Inter'] leading-normal">
|
||||
{report.roleAndOutput.selfRatedOutput}
|
||||
</p>
|
||||
</div>
|
||||
@@ -866,29 +873,29 @@ const EmployeeReportContent: React.FC<{
|
||||
{report.insights && (
|
||||
<div className="w-full p-3 bg-[--Neutrals-NeutralSlate100] rounded-[20px] shadow-[0px_1px_2px_0px_rgba(0,0,0,0.02)] flex flex-col justify-center items-start gap-1 overflow-hidden">
|
||||
<div className="self-stretch px-3 py-2 inline-flex justify-start items-center gap-2">
|
||||
<div className="justify-start text-Text-Dark-950 text-xl font-medium font-['Neue_Montreal'] leading-normal">Insights & Traits</div>
|
||||
<div className="justify-start text-[--Text-Dark-950] text-xl font-medium font-['Neue_Montreal'] leading-normal">Insights & Traits</div>
|
||||
</div>
|
||||
<div className="self-stretch p-6 bg-Light-Grays-l-gray08 rounded-2xl outline outline-1 outline-offset-[-1px] outline-Text-Gray-200 flex flex-col justify-start items-start gap-4">
|
||||
{report.insights.personalityTraits && (
|
||||
<div className="self-stretch">
|
||||
<div className="text-Text-Gray-800 text-base font-semibold font-['Inter'] leading-normal mb-2">Personality Traits</div>
|
||||
<p className="text-Text-Gray-800 text-base font-normal font-['Inter'] leading-normal">
|
||||
<div className="text-[--Text-Gray-800] text-base font-semibold font-['Inter'] leading-normal mb-2">Personality Traits</div>
|
||||
<p className="text-[--Text-Gray-800] text-base font-normal font-['Inter'] leading-normal">
|
||||
{report.insights.personalityTraits}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
{report.insights.selfAwareness && (
|
||||
<div className="self-stretch">
|
||||
<div className="text-Text-Gray-800 text-base font-semibold font-['Inter'] leading-normal mb-2">Self Awareness</div>
|
||||
<p className="text-Text-Gray-800 text-base font-normal font-['Inter'] leading-normal">
|
||||
<div className="text-[--Text-Gray-800] text-base font-semibold font-['Inter'] leading-normal mb-2">Self Awareness</div>
|
||||
<p className="text-[--Text-Gray-800] text-base font-normal font-['Inter'] leading-normal">
|
||||
{report.insights.selfAwareness}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
{report.insights.growthDesire && (
|
||||
<div className="self-stretch">
|
||||
<div className="text-Text-Gray-800 text-base font-semibold font-['Inter'] leading-normal mb-2">Growth Desire</div>
|
||||
<p className="text-Text-Gray-800 text-base font-normal font-['Inter'] leading-normal">
|
||||
<div className="text-[--Text-Gray-800] text-base font-semibold font-['Inter'] leading-normal mb-2">Growth Desire</div>
|
||||
<p className="text-[--Text-Gray-800] text-base font-normal font-['Inter'] leading-normal">
|
||||
{report.insights.growthDesire}
|
||||
</p>
|
||||
</div>
|
||||
@@ -901,7 +908,7 @@ const EmployeeReportContent: React.FC<{
|
||||
{report.strengths && report.strengths.length > 0 && (
|
||||
<div className="w-full p-3 bg-[--Neutrals-NeutralSlate100] rounded-[20px] shadow-[0px_1px_2px_0px_rgba(0,0,0,0.02)] flex flex-col justify-center items-start gap-1 overflow-hidden">
|
||||
<div className="self-stretch px-3 py-2 inline-flex justify-start items-center gap-2">
|
||||
<div className="justify-start text-Text-Dark-950 text-xl font-medium font-['Neue_Montreal'] leading-normal">Strengths</div>
|
||||
<div className="justify-start text-[--Text-Dark-950] text-xl font-medium font-['Neue_Montreal'] leading-normal">Strengths</div>
|
||||
</div>
|
||||
<div className="self-stretch p-6 bg-Light-Grays-l-gray08 rounded-2xl outline outline-1 outline-offset-[-1px] outline-Text-Gray-200 flex flex-col justify-start items-start gap-4">
|
||||
{report.strengths.map((strength, index) => (
|
||||
@@ -914,7 +921,7 @@ const EmployeeReportContent: React.FC<{
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-1 justify-start text-Text-Gray-800 text-base font-normal font-['Inter'] leading-normal">{strength}</div>
|
||||
<div className="flex-1 justify-start text-[--Text-Gray-800] text-base font-normal font-['Inter'] leading-normal">{strength}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
@@ -925,11 +932,11 @@ const EmployeeReportContent: React.FC<{
|
||||
{report.recommendations && report.recommendations.length > 0 && (
|
||||
<div className="w-full p-3 bg-[--Neutrals-NeutralSlate100] rounded-[20px] shadow-[0px_1px_2px_0px_rgba(0,0,0,0.02)] flex flex-col justify-center items-start gap-1 overflow-hidden">
|
||||
<div className="self-stretch px-3 py-2 inline-flex justify-start items-center gap-2">
|
||||
<div className="justify-start text-Text-Dark-950 text-xl font-medium font-['Neue_Montreal'] leading-normal">Recommendations</div>
|
||||
<div className="justify-start text-[--Text-Dark-950] text-xl font-medium font-['Neue_Montreal'] leading-normal">Recommendations</div>
|
||||
</div>
|
||||
<div className="self-stretch p-6 bg-Light-Grays-l-gray08 rounded-2xl outline outline-1 outline-offset-[-1px] outline-Text-Gray-200 flex flex-col justify-start items-start gap-4">
|
||||
{report.recommendations.map((recommendation, index) => (
|
||||
<div key={index} className="self-stretch text-Text-Gray-800 text-base font-normal font-['Inter'] leading-normal">
|
||||
<div key={index} className="self-stretch text-[--Text-Gray-800] text-base font-normal font-['Inter'] leading-normal">
|
||||
• {recommendation}
|
||||
</div>
|
||||
))}
|
||||
@@ -941,4 +948,4 @@ const EmployeeReportContent: React.FC<{
|
||||
);
|
||||
};
|
||||
|
||||
export default Reports;
|
||||
export default Reports;
|
||||
|
||||
@@ -5,6 +5,7 @@ import { useAuth } from '../contexts/AuthContext';
|
||||
import Sidebar from '../components/figma/Sidebar';
|
||||
import { Button } from '../components/UiKit';
|
||||
import { Theme } from '../types';
|
||||
import { FigmaPrimaryButton, FigmaSecondaryButton } from '../components/figma/FigmaButton';
|
||||
|
||||
interface UserProfile {
|
||||
fullName: string;
|
||||
@@ -16,7 +17,7 @@ type ThemeMode = 'system' | 'light' | 'dark';
|
||||
|
||||
const SettingsNew: React.FC = () => {
|
||||
const { theme, setTheme } = useTheme();
|
||||
const { user } = useAuth();
|
||||
const { user, signOutUser } = useAuth();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const [activeTab, setActiveTab] = useState<'general' | 'billing'>('general');
|
||||
@@ -57,234 +58,233 @@ const SettingsNew: React.FC = () => {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex-1 self-stretch shadow-[0px_0px_15px_0px_rgba(0,0,0,0.08)] flex justify-between items-start h-full">
|
||||
<Sidebar companyName="Zitlac Media" />
|
||||
<div className="flex-1 self-stretch bg-[--Neutrals-NeutralSlate0] inline-flex flex-col justify-start items-start">
|
||||
{/* Tab Navigation */}
|
||||
<div className="self-stretch px-6 pt-6 border-b border-[--Neutrals-NeutralSlate200] flex flex-col justify-start items-end">
|
||||
<div className="self-stretch inline-flex justify-start items-start gap-6">
|
||||
<div
|
||||
onClick={() => setActiveTab('general')}
|
||||
className={`w-32 inline-flex flex-col justify-start items-start gap-3 cursor-pointer ${activeTab === 'general' ? '' : 'opacity-60'
|
||||
}`}
|
||||
>
|
||||
<div className={`self-stretch text-center justify-center text-base font-['Inter'] leading-normal ${activeTab === 'general'
|
||||
? 'text-Text-Gray-800 font-semibold'
|
||||
: 'text-Text-Gray-500 font-normal'
|
||||
}`}>
|
||||
General Settings
|
||||
<div className="w-[100vw] h-[100vh] p-4 bg-[--Neutrals-NeutralSlate200] inline-flex justify-start items-start overflow-hidden">
|
||||
<div className="flex-1 self-stretch rounded-3xl shadow-[0px_0px_15px_0px_rgba(0,0,0,0.08)] flex justify-between items-start overflow-hidden">
|
||||
<Sidebar companyName="Zitlac Media" />
|
||||
<div className="flex-1 self-stretch bg-[--Neutrals-NeutralSlate0] inline-flex flex-col justify-start items-start">
|
||||
{/* Tab Navigation */}
|
||||
<div className="self-stretch px-6 pt-6 border-b border-[--Neutrals-NeutralSlate200] flex flex-col justify-start items-end">
|
||||
<div className="self-stretch inline-flex justify-start items-start gap-6">
|
||||
<div
|
||||
onClick={() => setActiveTab('general')}
|
||||
className={`w-fit inline-flex flex-col justify-start items-start gap-3 cursor-pointer ${activeTab === 'general' ? '' : 'opacity-60'
|
||||
}`}
|
||||
>
|
||||
<div className={`self-stretch text-center justify-center text-base font-['Inter'] leading-normal ${activeTab === 'general'
|
||||
? 'text-[--Text-Gray-800]'
|
||||
: 'text-[--Text-Gray-500]'
|
||||
}`}>
|
||||
General Settings
|
||||
</div>
|
||||
{activeTab === 'general' && (
|
||||
<div className="self-stretch h-0.5 bg-[--Neutrals-NeutralSlate800] rounded-tl-lg rounded-tr-lg" />
|
||||
)}
|
||||
</div>
|
||||
{activeTab === 'general' && (
|
||||
<div className="self-stretch h-0.5 bg-[--Neutrals-NeutralSlate800] rounded-tl-lg rounded-tr-lg" />
|
||||
)}
|
||||
</div>
|
||||
<div
|
||||
onClick={() => setActiveTab('billing')}
|
||||
className={`inline-flex flex-col justify-start items-start gap-3 cursor-pointer ${activeTab === 'billing' ? '' : 'opacity-60'
|
||||
}`}
|
||||
>
|
||||
<div className={`text-center justify-center text-base font-['Inter'] leading-normal ${activeTab === 'billing'
|
||||
? 'text-Text-Gray-800 font-semibold'
|
||||
: 'text-Text-Gray-500 font-normal'
|
||||
}`}>
|
||||
Plan & Billings
|
||||
<div
|
||||
onClick={() => setActiveTab('billing')}
|
||||
className={`inline-flex flex-col justify-start items-start gap-3 cursor-pointer ${activeTab === 'billing' ? '' : 'opacity-60'
|
||||
}`}
|
||||
>
|
||||
<div className={`text-center justify-center text-base font-['Inter'] leading-normal ${activeTab === 'billing'
|
||||
? 'text-[--Text-Gray-800]'
|
||||
: 'text-[--Text-Gray-500]'
|
||||
}`}>
|
||||
Plan & Billings
|
||||
</div>
|
||||
{activeTab === 'billing' && (
|
||||
<div className="w-24 h-0.5 bg-[--Neutrals-NeutralSlate800] rounded-tl-lg rounded-tr-lg" />
|
||||
)}
|
||||
</div>
|
||||
{activeTab === 'billing' && (
|
||||
<div className="w-24 h-0.5 bg-[--Neutrals-NeutralSlate800] rounded-tl-lg rounded-tr-lg" />
|
||||
)}
|
||||
</div>
|
||||
<div className="w-24 h-0.5 opacity-0 bg-[--Neutrals-NeutralSlate800] rounded-tl-lg rounded-tr-lg" />
|
||||
</div>
|
||||
<div className="w-24 h-0.5 opacity-0 bg-[--Neutrals-NeutralSlate800] rounded-tl-lg rounded-tr-lg" />
|
||||
</div>
|
||||
|
||||
{/* General Settings Content */}
|
||||
{activeTab === 'general' && (
|
||||
<>
|
||||
{/* Profile Information Section */}
|
||||
<div className="w-full h-72 p-6 flex flex-col justify-start items-start gap-6">
|
||||
<div className="flex flex-col justify-start items-start gap-1">
|
||||
<div className="self-stretch justify-start text-[--Neutrals-NeutralSlate800] text-lg font-semibold font-['Inter'] leading-7">Profile Information</div>
|
||||
<div className="self-stretch justify-start text-[--Neutrals-NeutralSlate500] text-sm font-normal font-['Inter'] leading-tight">Update your personal details, and keep your profile up to date.</div>
|
||||
</div>
|
||||
<div className="self-stretch flex flex-col justify-start items-start gap-6">
|
||||
{/* Profile Picture Section */}
|
||||
<div className="w-[664px] px-3 py-2.5 bg-[--Neutrals-NeutralSlate0] rounded-2xl outline outline-1 outline-offset-[-1px] outline-[--Neutrals-NeutralSlate200] inline-flex justify-between items-center">
|
||||
<div className="flex-1 flex justify-start items-center gap-3">
|
||||
<div className="w-14 h-14 relative bg-red-200 rounded-[999px]">
|
||||
{/* General Settings Content */}
|
||||
{activeTab === 'general' && (
|
||||
<>
|
||||
{/* Profile Information Section */}
|
||||
<div className="w-full h-fit p-6 flex flex-col justify-start items-start gap-6">
|
||||
<div className="flex flex-col justify-start items-start gap-1">
|
||||
<div className="self-stretch justify-start text-[--Text-Gray-800] text-lg font-semibold font-['Inter'] leading-7">Profile Information</div>
|
||||
<div className="self-stretch justify-start text-[--Text-Gray-500] text-sm font-normal font-['Inter'] leading-tight">Update your personal details, and keep your profile up to date.</div>
|
||||
</div>
|
||||
<div className="self-stretch flex flex-col justify-start items-start gap-6">
|
||||
{/* Profile Picture Section */}
|
||||
<div className="w-[664px] px-3 py-2.5 bg-[--Neutrals-NeutralSlate0] rounded-2xl outline outline-1 outline-offset-[-1px] outline-[--Neutrals-NeutralSlate200] inline-flex justify-between items-center">
|
||||
<div className="flex-1 flex justify-start items-center gap-3">
|
||||
<div className="w-14 h-14 relative bg-red-200 rounded-[999px]">
|
||||
<div>
|
||||
<svg width="56" height="56" viewBox="0 0 56 56" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clipPath="url(#clip0_1042_3786)">
|
||||
<ellipse cx="28" cy="54.6008" rx="22.4" ry="16.8" fill="white" fillOpacity="0.72" />
|
||||
<circle opacity="0.9" cx="28" cy="22.3992" r="11.2" fill="white" />
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_1042_3786">
|
||||
<rect width="56" height="56" rx="28" fill="white" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-1 inline-flex flex-col justify-start items-start gap-1">
|
||||
<div className="self-stretch justify-center text-[--Text-Gray-800] text-base font-semibold font-['Inter'] leading-normal">Profile Picture</div>
|
||||
<div className="self-stretch justify-center text-[--Text-Gray-500] text-xs font-normal font-['Inter'] leading-none">PNG, JPEG, GIF Under 10MB</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
onClick={handlePhotoUpload}
|
||||
className="px-3 py-2.5 bg-[--Neutrals-NeutralSlate100] rounded-[999px] flex justify-center items-center gap-1 overflow-hidden cursor-pointer hover:bg-[--Neutrals-NeutralSlate100]"
|
||||
>
|
||||
<div>
|
||||
<svg width="56" height="56" viewBox="0 0 56 56" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clipPath="url(#clip0_1042_3786)">
|
||||
<ellipse cx="28" cy="54.6008" rx="22.4" ry="16.8" fill="white" fillOpacity="0.72" />
|
||||
<circle opacity="0.9" cx="28" cy="22.3992" r="11.2" fill="white" />
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_1042_3786">
|
||||
<rect width="56" height="56" rx="28" fill="white" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M2.66665 10.8282C1.86266 10.29 1.33331 9.37347 1.33331 8.33333C1.33331 6.77095 2.52765 5.48753 4.05314 5.34625C4.36519 3.44809 6.01348 2 7.99998 2C9.98648 2 11.6348 3.44809 11.9468 5.34625C13.4723 5.48753 14.6666 6.77095 14.6666 8.33333C14.6666 9.37347 14.1373 10.29 13.3333 10.8282M5.33331 10.6667L7.99998 8M7.99998 8L10.6666 10.6667M7.99998 8V14" stroke="var(--Neutrals-NeutralSlate950)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-1 inline-flex flex-col justify-start items-start gap-1">
|
||||
<div className="self-stretch justify-center text-[--Neutrals-NeutralSlate800] text-base font-semibold font-['Inter'] leading-normal">Profile Picture</div>
|
||||
<div className="self-stretch justify-center text-[--Neutrals-NeutralSlate500] text-xs font-normal font-['Inter'] leading-none">PNG, JPEG, GIF Under 10MB</div>
|
||||
<div className="px-1 flex justify-center items-center">
|
||||
<div className="justify-center text-[--Text-Gray-950] text-sm font-medium font-['Inter'] leading-tight">Upload Photo</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Name and Email Fields */}
|
||||
<div className="w-[664px] inline-flex justify-start items-center gap-4">
|
||||
<div className="flex-1 inline-flex flex-col justify-start items-start gap-2">
|
||||
<div className="self-stretch inline-flex justify-start items-center gap-0.5">
|
||||
<div className="justify-start text-[--Text-Gray-900] text-sm font-normal font-['Inter'] leading-tight">Full Name</div>
|
||||
</div>
|
||||
<div className="self-stretch flex flex-col justify-start items-start gap-1">
|
||||
<div className="self-stretch px-4 py-3.5 bg-[--Neutrals-NeutralSlate100] rounded-lg inline-flex justify-start items-center gap-2 overflow-hidden">
|
||||
<div>
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10 12.5C7.35828 12.5 5.00901 13.7755 3.51334 15.755C3.19143 16.181 3.03047 16.394 3.03574 16.6819C3.03981 16.9043 3.17948 17.1849 3.35448 17.3222C3.581 17.5 3.8949 17.5 4.5227 17.5H15.4773C16.1051 17.5 16.419 17.5 16.6455 17.3222C16.8205 17.1849 16.9602 16.9043 16.9643 16.6819C16.9695 16.394 16.8086 16.181 16.4867 15.755C14.991 13.7755 12.6417 12.5 10 12.5Z" stroke="var(--Neutrals-NeutralSlate600)" strokeWidth="1.5" strokeMiterlimit="10" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<path d="M10 10C12.0711 10 13.75 8.32107 13.75 6.25C13.75 4.17893 12.0711 2.5 10 2.5C7.92894 2.5 6.25001 4.17893 6.25001 6.25C6.25001 8.32107 7.92894 10 10 10Z" stroke="var(--Neutrals-NeutralSlate600)" strokeWidth="1.5" strokeMiterlimit="10" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</svg>
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
value={userProfile.fullName}
|
||||
onChange={(e) => handleProfileUpdate('fullName', e.target.value)}
|
||||
className="flex-1 bg-transparent text-[--Text-Gray-950] text-sm font-normal font-['Inter'] leading-tight outline-none"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-1 inline-flex flex-col justify-start items-start gap-2">
|
||||
<div className="self-stretch inline-flex justify-start items-center gap-0.5">
|
||||
<div className="justify-start text-[--Text-Gray-900] text-sm font-normal font-['Inter'] leading-tight">Email Address</div>
|
||||
</div>
|
||||
<div className="self-stretch flex flex-col justify-start items-start gap-1">
|
||||
<div className="self-stretch px-4 py-3.5 bg-[--Neutrals-NeutralSlate100] rounded-lg inline-flex justify-start items-center gap-2 overflow-hidden">
|
||||
<div>
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M1.66669 5.83203L8.47079 10.5949C9.02176 10.9806 9.29725 11.1734 9.59691 11.2481C9.8616 11.3141 10.1384 11.3141 10.4031 11.2481C10.7028 11.1734 10.9783 10.9806 11.5293 10.5949L18.3334 5.83203M5.66669 16.6654H14.3334C15.7335 16.6654 16.4336 16.6654 16.9683 16.3929C17.4387 16.1532 17.8212 15.7707 18.0609 15.3003C18.3334 14.7656 18.3334 14.0655 18.3334 12.6654V7.33203C18.3334 5.9319 18.3334 5.23183 18.0609 4.69705C17.8212 4.22665 17.4387 3.8442 16.9683 3.60451C16.4336 3.33203 15.7335 3.33203 14.3334 3.33203H5.66669C4.26656 3.33203 3.56649 3.33203 3.03171 3.60451C2.56131 3.8442 2.17885 4.22665 1.93917 4.69705C1.66669 5.23183 1.66669 5.9319 1.66669 7.33203V12.6654C1.66669 14.0655 1.66669 14.7656 1.93917 15.3003C2.17885 15.7707 2.56131 16.1532 3.03171 16.3929C3.56649 16.6654 4.26656 16.6654 5.66669 16.6654Z" stroke="var(--Neutrals-NeutralSlate600)" strokeWidth="1.5" strokeMiterlimit="10" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</svg>
|
||||
</div>
|
||||
<input
|
||||
type="email"
|
||||
value={userProfile.email}
|
||||
onChange={(e) => handleProfileUpdate('email', e.target.value)}
|
||||
className="flex-1 bg-transparent text-[--Text-Gray-950] text-sm font-normal font-['Inter'] leading-tight outline-none"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-[664px] inline-flex justify-start items-center gap-4">
|
||||
<FigmaSecondaryButton text="Log Out" grow={false} size="small" buttonExtra='text-[--Other-Red]' onClick={() => { signOutUser(); navigate('/login'); }} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{/* Divider */}
|
||||
<div className="border-b-[1px] border-solid border-[--Neutrals-NeutralSlate400] w-full"></div>
|
||||
|
||||
|
||||
{/* Theme Customization Section */}
|
||||
<div className="w-full self-stretch p-6 flex flex-col justify-start items-start gap-6">
|
||||
<div className="w-[584px] flex flex-col justify-start items-start gap-1">
|
||||
<div className="self-stretch justify-start text-[--Text-Gray-800] text-lg font-semibold font-['Inter'] leading-7">Theme Customization</div>
|
||||
<div className="self-stretch justify-start text-[--Text-Gray-500] text-sm font-normal font-['Inter'] leading-tight">Personalize your interface with light or dark mode and enhance your visual experience.</div>
|
||||
</div>
|
||||
<div className="inline-flex justify-start items-start gap-3 flex-wrap content-start">
|
||||
{/* System Preference */}
|
||||
<div
|
||||
onClick={handlePhotoUpload}
|
||||
className="px-3 py-2.5 bg-[--Neutrals-NeutralSlate100] rounded-[999px] flex justify-center items-center gap-1 overflow-hidden cursor-pointer hover:bg-[--Neutrals-NeutralSlate100]"
|
||||
onClick={() => setSelectedTheme('system')}
|
||||
className={`max-w-60 inline-flex flex-col justify-start items-start gap-3 cursor-pointer ${selectedTheme === 'system' ? 'opacity-100' : 'opacity-70'
|
||||
}`}
|
||||
>
|
||||
<div>
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M2.66665 10.8282C1.86266 10.29 1.33331 9.37347 1.33331 8.33333C1.33331 6.77095 2.52765 5.48753 4.05314 5.34625C4.36519 3.44809 6.01348 2 7.99998 2C9.98648 2 11.6348 3.44809 11.9468 5.34625C13.4723 5.48753 14.6666 6.77095 14.6666 8.33333C14.6666 9.37347 14.1373 10.29 13.3333 10.8282M5.33331 10.6667L7.99998 8M7.99998 8L10.6666 10.6667M7.99998 8V14" stroke="var(--Neutrals-NeutralSlate950)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</svg>
|
||||
</div>
|
||||
<div className="px-1 flex justify-center items-center">
|
||||
<div className="justify-center text-[--Neutrals-NeutralSlate950] text-sm font-medium font-['Inter'] leading-tight">Upload Photo</div>
|
||||
</div>
|
||||
<button className="inline-flex justify-start items-center" onClick={() => setTheme(Theme.System)}>
|
||||
<img className="w-24 h-28 rounded-tl-lg rounded-bl-lg" src="/image/39a0d5e73dec8bece795a718c5800f02df8f8631.png"></img>
|
||||
<img className="w-24 h-28 rounded-tr-lg rounded-br-lg" src="/image/bc07fdc9eec8a78357aaf70e9deae41d4b7a7d2d.png"></img>
|
||||
</button>
|
||||
<div className="self-stretch h-5 justify-start text-[--Text-Gray-800] text-sm font-normal font-['Inter'] leading-tight">System preference</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Name and Email Fields */}
|
||||
<div className="w-[664px] inline-flex justify-start items-center gap-4">
|
||||
<div className="flex-1 inline-flex flex-col justify-start items-start gap-2">
|
||||
<div className="self-stretch inline-flex justify-start items-center gap-0.5">
|
||||
<div className="justify-start text-[--Neutrals-NeutralSlate900] text-sm font-normal font-['Inter'] leading-tight">Full Name</div>
|
||||
</div>
|
||||
<div className="self-stretch flex flex-col justify-start items-start gap-1">
|
||||
<div className="self-stretch px-4 py-3.5 bg-[--Neutrals-NeutralSlate100] rounded-[999px] inline-flex justify-start items-center gap-2 overflow-hidden">
|
||||
<div>
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10 12.5C7.35828 12.5 5.00901 13.7755 3.51334 15.755C3.19143 16.181 3.03047 16.394 3.03574 16.6819C3.03981 16.9043 3.17948 17.1849 3.35448 17.3222C3.581 17.5 3.8949 17.5 4.5227 17.5H15.4773C16.1051 17.5 16.419 17.5 16.6455 17.3222C16.8205 17.1849 16.9602 16.9043 16.9643 16.6819C16.9695 16.394 16.8086 16.181 16.4867 15.755C14.991 13.7755 12.6417 12.5 10 12.5Z" stroke="var(--Neutrals-NeutralSlate600)" strokeWidth="1.5" strokeMiterlimit="10" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<path d="M10 10C12.0711 10 13.75 8.32107 13.75 6.25C13.75 4.17893 12.0711 2.5 10 2.5C7.92894 2.5 6.25001 4.17893 6.25001 6.25C6.25001 8.32107 7.92894 10 10 10Z" stroke="var(--Neutrals-NeutralSlate600)" strokeWidth="1.5" strokeMiterlimit="10" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</svg>
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
value={userProfile.fullName}
|
||||
onChange={(e) => handleProfileUpdate('fullName', e.target.value)}
|
||||
className="flex-1 bg-transparent text-[--Neutrals-NeutralSlate950] text-sm font-normal font-['Inter'] leading-tight outline-none"
|
||||
/>
|
||||
{/* Light Mode */}
|
||||
<div
|
||||
onClick={() => setSelectedTheme('light')}
|
||||
className={`w-48 max-w-60 inline-flex flex-col justify-start items-start gap-3 cursor-pointer ${selectedTheme === 'light' ? 'opacity-100' : 'opacity-70'
|
||||
}`}
|
||||
>
|
||||
<div className="self-stretch h-28 relative bg-[--Neutrals-NeutralSlate0] rounded-lg overflow-hidden">
|
||||
<div className={`w-48 h-28 left-0 top-0 absolute bg-[--Neutrals-NeutralSlate0] rounded-[10px] outline outline-1 outline-offset-[-1px] overflow-hidden ${selectedTheme === 'light' ? 'outline-Brand-Orange' : 'outline-[--Neutrals-NeutralSlate200]'
|
||||
}`}>
|
||||
<button className="w-48 h-28 left-0 top-0 absolute rounded-lg" style={{ backgroundImage: 'url("/image/39a0d5e73dec8bece795a718c5800f02df8f8631.png")', backgroundSize: "cover" }} onClick={() => setTheme(Theme.Light)}></button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="self-stretch h-5 justify-start text-[--Text-Gray-800] text-sm font-normal font-['Inter'] leading-tight">Light Mode</div>
|
||||
</div>
|
||||
<div className="flex-1 inline-flex flex-col justify-start items-start gap-2">
|
||||
<div className="self-stretch inline-flex justify-start items-center gap-0.5">
|
||||
<div className="justify-start text-[--Neutrals-NeutralSlate900] text-sm font-normal font-['Inter'] leading-tight">Email Address</div>
|
||||
</div>
|
||||
<div className="self-stretch flex flex-col justify-start items-start gap-1">
|
||||
<div className="self-stretch px-4 py-3.5 bg-[--Neutrals-NeutralSlate100] rounded-[999px] inline-flex justify-start items-center gap-2 overflow-hidden">
|
||||
<div>
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M1.66669 5.83203L8.47079 10.5949C9.02176 10.9806 9.29725 11.1734 9.59691 11.2481C9.8616 11.3141 10.1384 11.3141 10.4031 11.2481C10.7028 11.1734 10.9783 10.9806 11.5293 10.5949L18.3334 5.83203M5.66669 16.6654H14.3334C15.7335 16.6654 16.4336 16.6654 16.9683 16.3929C17.4387 16.1532 17.8212 15.7707 18.0609 15.3003C18.3334 14.7656 18.3334 14.0655 18.3334 12.6654V7.33203C18.3334 5.9319 18.3334 5.23183 18.0609 4.69705C17.8212 4.22665 17.4387 3.8442 16.9683 3.60451C16.4336 3.33203 15.7335 3.33203 14.3334 3.33203H5.66669C4.26656 3.33203 3.56649 3.33203 3.03171 3.60451C2.56131 3.8442 2.17885 4.22665 1.93917 4.69705C1.66669 5.23183 1.66669 5.9319 1.66669 7.33203V12.6654C1.66669 14.0655 1.66669 14.7656 1.93917 15.3003C2.17885 15.7707 2.56131 16.1532 3.03171 16.3929C3.56649 16.6654 4.26656 16.6654 5.66669 16.6654Z" stroke="var(--Neutrals-NeutralSlate600)" strokeWidth="1.5" strokeMiterlimit="10" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</svg>
|
||||
</div>
|
||||
<input
|
||||
type="email"
|
||||
value={userProfile.email}
|
||||
onChange={(e) => handleProfileUpdate('email', e.target.value)}
|
||||
className="flex-1 bg-transparent text-[--Neutrals-NeutralSlate950] text-sm font-normal font-['Inter'] leading-tight outline-none"
|
||||
/>
|
||||
|
||||
{/* Dark Mode */}
|
||||
<div
|
||||
onClick={() => setSelectedTheme('dark')}
|
||||
className={`max-w-60 inline-flex flex-col justify-start items-start gap-3 cursor-pointer ${selectedTheme === 'dark' ? 'opacity-100' : 'opacity-70'
|
||||
}`}
|
||||
>
|
||||
<div className="w-48 h-28 relative bg-[--Neutrals-NeutralSlate950] rounded-lg overflow-hidden">
|
||||
<div className={`w-48 h-28 left-0 top-0 absolute bg-[--Neutrals-NeutralSlate0] rounded-[10px] outline outline-1 outline-offset-[-1px] overflow-hidden ${selectedTheme === 'dark' ? 'outline-Brand-Orange' : 'outline-[--Neutrals-NeutralSlate200]'
|
||||
}`}>
|
||||
<button className="w-48 h-28 left-0 top-0 absolute rounded-lg" style={{ backgroundImage: 'url("/image/bc07fdc9eec8a78357aaf70e9deae41d4b7a7d2d.png")', backgroundSize: "cover" }} onClick={() => setTheme(Theme.Dark)}></button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="self-stretch h-5 justify-start text-[--Text-Gray-800] text-sm font-normal font-['Inter'] leading-tight">Dark Mode</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Divider */}
|
||||
<div className="border-b-[1px] border-solid border-[--Neutrals-NeutralSlate200] w-full"></div>
|
||||
{/* Another Divider */}
|
||||
<div className="border-b-[1px] border-solid border-[--Neutrals-NeutralSlate400] w-full"></div>
|
||||
|
||||
|
||||
{/* Theme Customization Section */}
|
||||
<div className="w-full self-stretch p-6 flex flex-col justify-start items-start gap-6">
|
||||
<div className="w-[584px] flex flex-col justify-start items-start gap-1">
|
||||
<div className="self-stretch justify-start text-[--Neutrals-NeutralSlate800] text-lg font-semibold font-['Inter'] leading-7">Theme Customization</div>
|
||||
<div className="self-stretch justify-start text-[--Neutrals-NeutralSlate500] text-sm font-normal font-['Inter'] leading-tight">Personalize your interface with light or dark mode and enhance your visual experience.</div>
|
||||
</div>
|
||||
<div className="inline-flex justify-start items-start gap-3 flex-wrap content-start">
|
||||
{/* System Preference */}
|
||||
{/* Action Buttons */}
|
||||
<div className="w-[1175px] p-6 inline-flex justify-start items-center gap-2">
|
||||
<div
|
||||
onClick={() => setSelectedTheme('system')}
|
||||
className={`max-w-60 inline-flex flex-col justify-start items-start gap-3 cursor-pointer ${selectedTheme === 'system' ? 'opacity-100' : 'opacity-70'
|
||||
}`}
|
||||
onClick={handleReset}
|
||||
className="px-3 py-2.5 bg-[--Neutrals-NeutralSlate100] rounded-[999px] flex justify-center items-center gap-1 overflow-hidden cursor-pointer hover:bg-[--Neutrals-NeutralSlate100]"
|
||||
>
|
||||
<button className="inline-flex justify-start items-center" onClick={() => setTheme(Theme.System)}>
|
||||
<img className="w-24 h-28 rounded-tl-lg rounded-bl-lg" src="/image/39a0d5e73dec8bece795a718c5800f02df8f8631.png"></img>
|
||||
<img className="w-24 h-28 rounded-tr-lg rounded-br-lg" src="/image/bc07fdc9eec8a78357aaf70e9deae41d4b7a7d2d.png"></img>
|
||||
</button>
|
||||
<div className="self-stretch h-5 justify-start text-Text-Gray-800 text-sm font-normal font-['Inter'] leading-tight">System preference</div>
|
||||
</div>
|
||||
|
||||
{/* Light Mode */}
|
||||
<div
|
||||
onClick={() => setSelectedTheme('light')}
|
||||
className={`w-48 max-w-60 inline-flex flex-col justify-start items-start gap-3 cursor-pointer ${selectedTheme === 'light' ? 'opacity-100' : 'opacity-70'
|
||||
}`}
|
||||
>
|
||||
<div className="self-stretch h-28 relative bg-[--Neutrals-NeutralSlate0] rounded-lg overflow-hidden">
|
||||
<div className={`w-48 h-28 left-0 top-0 absolute bg-[--Neutrals-NeutralSlate0] rounded-[10px] outline outline-1 outline-offset-[-1px] overflow-hidden ${selectedTheme === 'light' ? 'outline-Brand-Orange' : 'outline-[--Neutrals-NeutralSlate200]'
|
||||
}`}>
|
||||
<button className="w-48 h-28 left-0 top-0 absolute rounded-lg" style={{ backgroundImage: 'url("/image/39a0d5e73dec8bece795a718c5800f02df8f8631.png")', backgroundSize: "cover" }} onClick={() => setTheme(Theme.Light)}></button>
|
||||
</div>
|
||||
<div className="px-1 flex justify-center items-center">
|
||||
<div className="justify-center text-[--Text-Gray-950] text-sm font-medium font-['Inter'] leading-tight">Reset</div>
|
||||
</div>
|
||||
<div className="self-stretch h-5 justify-start text-[--Neutrals-NeutralSlate800] text-sm font-normal font-['Inter'] leading-tight">Light Mode</div>
|
||||
</div>
|
||||
<FigmaPrimaryButton text="Save Changes" grow={false} containerExtra='' size="small" onClick={handleSaveChanges} />
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* Dark Mode */}
|
||||
<div
|
||||
onClick={() => setSelectedTheme('dark')}
|
||||
className={`max-w-60 inline-flex flex-col justify-start items-start gap-3 cursor-pointer ${selectedTheme === 'dark' ? 'opacity-100' : 'opacity-70'
|
||||
}`}
|
||||
>
|
||||
<div className="w-48 h-28 relative bg-[--Neutrals-NeutralSlate950] rounded-lg overflow-hidden">
|
||||
<div className={`w-48 h-28 left-0 top-0 absolute bg-[--Neutrals-NeutralSlate0] rounded-[10px] outline outline-1 outline-offset-[-1px] overflow-hidden ${selectedTheme === 'dark' ? 'outline-Brand-Orange' : 'outline-[--Neutrals-NeutralSlate200]'
|
||||
}`}>
|
||||
<button className="w-48 h-28 left-0 top-0 absolute rounded-lg" style={{ backgroundImage: 'url("/image/bc07fdc9eec8a78357aaf70e9deae41d4b7a7d2d.png")', backgroundSize: "cover" }} onClick={() => setTheme(Theme.Dark)}></button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="self-stretch h-5 justify-start text-[--Neutrals-NeutralSlate800] text-sm font-normal font-['Inter'] leading-tight">Dark Mode</div>
|
||||
</div>
|
||||
{/* Billing Content */}
|
||||
{activeTab === 'billing' && (
|
||||
<div className="flex-1 flex items-center justify-center self-stretch">
|
||||
<div className="text-center">
|
||||
<h2 className="text-2xl font-semibold text-[--Text-Gray-800] mb-4">Plan & Billing</h2>
|
||||
<p className="text-[--Text-Gray-500]">Billing management features would be implemented here.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Another Divider */}
|
||||
<div className="border-b-[1px] border-solid border-[--Neutrals-NeutralSlate200] w-full"></div>
|
||||
|
||||
{/* Action Buttons */}
|
||||
<div className="w-[1175px] p-6 inline-flex justify-start items-center gap-2">
|
||||
<div
|
||||
onClick={handleReset}
|
||||
className="px-3 py-2.5 bg-[--Neutrals-NeutralSlate100] rounded-[999px] flex justify-center items-center gap-1 overflow-hidden cursor-pointer hover:bg-[--Neutrals-NeutralSlate100]"
|
||||
>
|
||||
<div className="px-1 flex justify-center items-center">
|
||||
<div className="justify-center text-[--Neutrals-NeutralSlate950] text-sm font-medium font-['Inter'] leading-tight">Reset</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
onClick={handleSaveChanges}
|
||||
className="px-3 py-2.5 bg-[--Brand-Orange] rounded-[999px] outline outline-2 outline-offset-[-2px] outline-blue-400 flex justify-center items-center gap-1 overflow-hidden cursor-pointer hover:bg-[--Brand-Orange]/90"
|
||||
>
|
||||
<div className="px-1 flex justify-center items-center">
|
||||
<div className="justify-center text-white text-sm font-medium font-['Inter'] leading-tight">Save Changes</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* Billing Content */}
|
||||
{activeTab === 'billing' && (
|
||||
<div className="flex-1 flex items-center justify-center">
|
||||
<div className="text-center">
|
||||
<h2 className="text-2xl font-semibold text-[--Neutrals-NeutralSlate800] mb-4">Plan & Billing</h2>
|
||||
<p className="text-[--Neutrals-NeutralSlate500]">Billing management features would be implemented here.</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SettingsNew;
|
||||
export default SettingsNew;
|
||||
|
||||
@@ -195,7 +195,7 @@ const Submissions: React.FC = () => {
|
||||
return (
|
||||
<div className="flex-1 flex items-center justify-center">
|
||||
<div className="text-center">
|
||||
<h3 className="text-lg font-semibold text-[--Neutrals-NeutralSlate950] mb-2">
|
||||
<h3 className="text-lg font-semibold text-[--Text-Gray-950] mb-2">
|
||||
Loading Submissions...
|
||||
</h3>
|
||||
</div>
|
||||
@@ -204,12 +204,12 @@ const Submissions: React.FC = () => {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="h-full flex-1 self-stretch rounded-3xl shadow-[0px_0px_15px_0px_rgba(0,0,0,0.08)] flex justify-between items-start overflow-hidden">
|
||||
<>
|
||||
{/* Middle Section - Employee List */}
|
||||
<div className="flex-1 self-stretch bg-[--Neutrals-NeutralSlate0] flex justify-start items-center">
|
||||
<div className="flex-1 self-stretch max-w-64 min-w-64 border-r border-[--Neutrals-NeutralSlate200] inline-flex flex-col justify-start items-start">
|
||||
<div className="self-stretch p-5 inline-flex justify-start items-center gap-2.5">
|
||||
<div className="flex-1 justify-start text-[--Neutrals-NeutralSlate950] text-base font-medium font-['Inter'] leading-normal">Employees</div>
|
||||
<div className="flex-1 justify-start text-[--Text-Gray-950] text-base font-medium font-['Inter'] leading-normal">Employees</div>
|
||||
</div>
|
||||
<div className="self-stretch flex flex-col justify-start items-start gap-2">
|
||||
{/* Search */}
|
||||
@@ -226,7 +226,7 @@ const Submissions: React.FC = () => {
|
||||
placeholder="search team"
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
className="flex-1 bg-transparent text-[--Neutrals-NeutralSlate500] text-sm font-normal font-['Inter'] leading-tight outline-none"
|
||||
className="flex-1 bg-transparent text-[--Text-Gray-500] text-sm font-normal font-['Inter'] leading-tight outline-none"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -235,7 +235,7 @@ const Submissions: React.FC = () => {
|
||||
{/* Employee List */}
|
||||
<div className="self-stretch px-3 flex flex-col justify-start items-start">
|
||||
{visibleEmployees.length === 0 ? (
|
||||
<div className="self-stretch p-4 text-center text-[--Neutrals-NeutralSlate500] text-sm">
|
||||
<div className="self-stretch p-4 text-center text-[--Text-Gray-500] text-sm">
|
||||
{searchQuery ? 'No employees found matching your search.' : 'No employee submissions found.'}
|
||||
</div>
|
||||
) : (
|
||||
@@ -247,11 +247,11 @@ const Submissions: React.FC = () => {
|
||||
onClick={() => handleEmployeeSelect(employee)}
|
||||
>
|
||||
<div className="w-7 h-7 p-1 bg-[--Neutrals-NeutralSlate700] rounded-[666.67px] flex justify-center items-center">
|
||||
<div className="text-center justify-start text-[--Neutrals-NeutralSlate0] text-xs font-medium font-['Inter'] leading-none">
|
||||
<div className="text-center justify-start text-[--Text-Gray-0] text-xs font-medium font-['Inter'] leading-none">
|
||||
{employee.initials}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-1 justify-start text-[--Neutrals-NeutralSlate800] text-sm font-normal font-['Inter'] leading-tight">
|
||||
<div className="flex-1 justify-start text-[--Text-Gray-800] text-sm font-normal font-['Inter'] leading-tight">
|
||||
{employee.name}
|
||||
</div>
|
||||
</div>
|
||||
@@ -274,10 +274,10 @@ const Submissions: React.FC = () => {
|
||||
) : (
|
||||
<div className="flex-1 self-stretch flex items-center justify-center">
|
||||
<div className="text-center">
|
||||
<h3 className="text-lg font-semibold text-[--Neutrals-NeutralSlate950] mb-2">
|
||||
<h3 className="text-lg font-semibold text-[--Text-Gray-950] mb-2">
|
||||
Select an Employee
|
||||
</h3>
|
||||
<p className="text-[--Neutrals-NeutralSlate500]">
|
||||
<p className="text-[--Text-Gray-500]">
|
||||
Choose an employee from the list to view their submission answers.
|
||||
</p>
|
||||
</div>
|
||||
@@ -285,7 +285,7 @@ const Submissions: React.FC = () => {
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -301,7 +301,7 @@ const SubmissionContent: React.FC<{
|
||||
<>
|
||||
{/* Header */}
|
||||
<div className="self-stretch px-5 py-3 inline-flex justify-start items-center gap-2.5">
|
||||
<div className="flex-1 justify-start text-[--Neutrals-NeutralSlate800] text-base font-medium font-['Inter'] leading-normal">
|
||||
<div className="flex-1 justify-start text-[--Text-Gray-800] text-base font-medium font-['Inter'] leading-normal">
|
||||
{employee.name}'s Answers
|
||||
</div>
|
||||
<div className="flex justify-start items-center gap-3">
|
||||
@@ -315,7 +315,7 @@ const SubmissionContent: React.FC<{
|
||||
</svg>
|
||||
</div>
|
||||
<div className="px-1 flex justify-center items-center">
|
||||
<div className="justify-center text-[--Neutrals-NeutralSlate800] text-sm font-medium font-['Inter'] leading-tight">Chat With AI</div>
|
||||
<div className="justify-center text-[--Text-Gray-800] text-sm font-medium font-['Inter'] leading-tight">Chat With AI</div>
|
||||
</div>
|
||||
</button>
|
||||
<button
|
||||
@@ -335,7 +335,7 @@ const SubmissionContent: React.FC<{
|
||||
</svg>
|
||||
</div>
|
||||
<div className="px-1 flex justify-center items-center">
|
||||
<div className="justify-center text-[--Neutrals-NeutralSlate800] text-sm font-medium font-['Inter'] leading-tight">View Report</div>
|
||||
<div className="justify-center text-[--Text-Gray-800] text-sm font-medium font-['Inter'] leading-tight">View Report</div>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
@@ -345,7 +345,7 @@ const SubmissionContent: React.FC<{
|
||||
<div className="self-stretch px-5 py-2 flex flex-col justify-start items-start gap-4 overflow-y-auto">
|
||||
{questionsAndAnswers.length === 0 ? (
|
||||
<div className="self-stretch text-center py-8">
|
||||
<p className="text-[--Neutrals-NeutralSlate500]">No submission data available for this employee.</p>
|
||||
<p className="text-[--Text-Gray-500]">No submission data available for this employee.</p>
|
||||
</div>
|
||||
) : (
|
||||
questionsAndAnswers.map((qa, index) => (
|
||||
@@ -356,14 +356,14 @@ const SubmissionContent: React.FC<{
|
||||
>
|
||||
<div className="self-stretch px-3 py-px inline-flex justify-start items-center gap-2.5">
|
||||
<div className="flex-1 flex justify-center items-center gap-3">
|
||||
<div className="w-3 self-stretch justify-start text-[--Neutrals-NeutralSlate300] text-base font-semibold font-['Inter'] leading-normal">Q</div>
|
||||
<div className="flex-1 justify-start text-[--Neutrals-NeutralSlate600] text-sm font-medium font-['Inter'] leading-tight">{qa.question}</div>
|
||||
<div className="w-3 self-stretch justify-start text-[--Text-Gray-300] text-base font-semibold font-['Inter'] leading-normal">Q</div>
|
||||
<div className="flex-1 justify-start text-[--Text-Gray-600] text-sm font-medium font-['Inter'] leading-tight">{qa.question}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="self-stretch px-3 py-2 bg-[--Neutrals-NeutralSlate0] rounded-[10px] inline-flex justify-between items-center">
|
||||
<div className="flex-1 flex justify-start items-start gap-3">
|
||||
<div className="w-3.5 h-6 justify-center text-[--Neutrals-NeutralSlate300] text-base font-semibold font-['Inter'] leading-normal">A</div>
|
||||
<div className="flex-1 justify-start text-[--Neutrals-NeutralSlate800] text-base font-normal font-['Inter'] leading-normal">
|
||||
<div className="w-3.5 h-6 justify-center text-[--Text-Gray-300] text-base font-semibold font-['Inter'] leading-normal">A</div>
|
||||
<div className="flex-1 justify-start text-[--Text-Gray-800] text-base font-normal font-['Inter'] leading-normal">
|
||||
{qa.answer}
|
||||
</div>
|
||||
</div>
|
||||
@@ -376,4 +376,4 @@ const SubmissionContent: React.FC<{
|
||||
);
|
||||
};
|
||||
|
||||
export default Submissions;
|
||||
export default Submissions;
|
||||
|
||||
@@ -145,7 +145,7 @@ const SubscriptionSetup: React.FC = () => {
|
||||
</div>
|
||||
|
||||
<div className="border rounded-lg p-4">
|
||||
<h3 className="text-lg font-medium text-gray-900 mb-4">Auditly Standard Plan</h3>
|
||||
<h3 className="text-lg font-medium text-gray-900 mb-4">Orbitly Standard Plan</h3>
|
||||
<div className="space-y-2 mb-4">
|
||||
<div className="flex items-center">
|
||||
<svg className="h-4 w-4 text-green-500 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
@@ -207,4 +207,4 @@ const SubscriptionSetup: React.FC = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default SubscriptionSetup;
|
||||
export default SubscriptionSetup;
|
||||
|
||||
@@ -5,10 +5,10 @@ const ChatAIResponse: React.FC = () => {
|
||||
<div className="w-full h-[810px] p-4 bg-[--Neutrals-NeutralSlate200] inline-flex justify-start items-start overflow-hidden">
|
||||
<div className="flex-1 self-stretch rounded-3xl shadow-[0px_0px_15px_0px_rgba(0,0,0,0.08)] flex justify-between items-start overflow-hidden">
|
||||
{/* Sidebar */}
|
||||
<div className="w-64 self-stretch max-w-64 min-w-64 px-3 pt-4 pb-3 bg-[--Neutrals-NeutralSlate0] border-r border-[--$1] inline-flex flex-col justify-between items-center overflow-hidden">
|
||||
<div className="w-64 self-stretch max-w-64 min-w-64 px-3 pt-4 pb-3 bg-[--Neutrals-NeutralSlate0] border-r border-[--Neutrals-NeutralSlate200] inline-flex flex-col justify-between items-center overflow-hidden">
|
||||
<div className="self-stretch flex flex-col justify-start items-start gap-5">
|
||||
{/* Company Selector */}
|
||||
<div className="w-60 pl-2 pr-4 py-2 bg-[--Neutrals-NeutralSlate0] rounded-3xl outline outline-1 outline-offset-[-1px] outline-[--$1] inline-flex justify-between items-center overflow-hidden">
|
||||
<div className="w-60 pl-2 pr-4 py-2 bg-[--Neutrals-NeutralSlate0] rounded-3xl outline outline-1 outline-offset-[-1px] outline-[--Neutrals-NeutralSlate200] inline-flex justify-between items-center overflow-hidden">
|
||||
<div className="flex-1 flex justify-start items-center gap-2">
|
||||
<div className="w-8 h-8 rounded-full flex justify-start items-center gap-2.5">
|
||||
<div className="w-8 h-8 relative bg-[--Brand-Orange] rounded-full outline outline-[1.60px] outline-offset-[-1.60px] outline-white/10 overflow-hidden">
|
||||
@@ -29,7 +29,7 @@ const ChatAIResponse: React.FC = () => {
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-1 inline-flex flex-col justify-start items-start gap-0.5">
|
||||
<div className="self-stretch justify-start text-[--Neutrals-NeutralSlate950] text-base font-medium font-['Inter'] leading-normal">Zitlac Media</div>
|
||||
<div className="self-stretch justify-start text-[--Text-Gray-950] text-base font-medium font-['Inter'] leading-normal">Zitlac Media</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="relative">
|
||||
@@ -48,7 +48,7 @@ const ChatAIResponse: React.FC = () => {
|
||||
<path d="M7.5 17.5016V11.3349C7.5 10.8682 7.5 10.6348 7.59083 10.4566C7.67072 10.2998 7.79821 10.1723 7.95501 10.0924C8.13327 10.0016 8.36662 10.0016 8.83333 10.0016H11.1667C11.6334 10.0016 11.8667 10.0016 12.045 10.0924C12.2018 10.1723 12.3293 10.2998 12.4092 10.4566C12.5 10.6348 12.5 10.8682 12.5 11.3349V17.5016M9.18141 2.30492L3.52949 6.70086C3.15168 6.99471 2.96278 7.14163 2.82669 7.32563C2.70614 7.48862 2.61633 7.67224 2.56169 7.86746C2.5 8.08785 2.5 8.32717 2.5 8.8058V14.8349C2.5 15.7683 2.5 16.235 2.68166 16.5916C2.84144 16.9052 3.09641 17.1601 3.41002 17.3199C3.76654 17.5016 4.23325 17.5016 5.16667 17.5016H14.8333C15.7668 17.5016 16.2335 17.5016 16.59 17.3199C16.9036 17.1601 17.1586 16.9052 17.3183 16.5916C17.5 16.235 17.5 15.7683 17.5 14.8349V8.8058C17.5 8.32717 17.5 8.08785 17.4383 7.86746C17.3837 7.67224 17.2939 7.48862 17.1733 7.32563C17.0372 7.14163 16.8483 6.99471 16.4705 6.70086L10.8186 2.30492C10.5258 2.07721 10.3794 1.96335 10.2178 1.91959C10.0752 1.88097 9.92484 1.88097 9.78221 1.91959C9.62057 1.96335 9.47418 2.07721 9.18141 2.30492Z" stroke="var(--Neutrals-NeutralSlate400, #A4A7AE)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</svg>
|
||||
</div>
|
||||
<div className="justify-start text-[--Neutrals-NeutralSlate500] text-sm font-medium font-['Inter'] leading-tight">Company Wiki</div>
|
||||
<div className="justify-start text-[--Text-Gray-500] text-sm font-medium font-['Inter'] leading-tight">Company Wiki</div>
|
||||
</div>
|
||||
<div className="w-60 px-4 py-2.5 rounded-[34px] inline-flex justify-start items-center gap-2">
|
||||
<div className="relative">
|
||||
@@ -56,7 +56,7 @@ const ChatAIResponse: React.FC = () => {
|
||||
<path d="M11.6666 9.16797H6.66659M8.33325 12.5013H6.66659M13.3333 5.83464H6.66659M16.6666 5.66797V14.3346C16.6666 15.7348 16.6666 16.4348 16.3941 16.9696C16.1544 17.44 15.772 17.8225 15.3016 18.0622C14.7668 18.3346 14.0667 18.3346 12.6666 18.3346H7.33325C5.93312 18.3346 5.23306 18.3346 4.69828 18.0622C4.22787 17.8225 3.84542 17.44 3.60574 16.9696C3.33325 16.4348 3.33325 15.7348 3.33325 14.3346V5.66797C3.33325 4.26784 3.33325 3.56777 3.60574 3.03299C3.84542 2.56259 4.22787 2.18014 4.69828 1.94045C5.23306 1.66797 5.93312 1.66797 7.33325 1.66797H12.6666C14.0667 1.66797 14.7668 1.66797 15.3016 1.94045C15.772 2.18014 16.1544 2.56259 16.3941 3.03299C16.6666 3.56777 16.6666 4.26784 16.6666 5.66797Z" stroke="var(--Neutrals-NeutralSlate400, #A4A7AE)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</svg>
|
||||
</div>
|
||||
<div className="justify-start text-[--Neutrals-NeutralSlate500] text-sm font-medium font-['Inter'] leading-tight">Submissions</div>
|
||||
<div className="justify-start text-[--Text-Gray-500] text-sm font-medium font-['Inter'] leading-tight">Submissions</div>
|
||||
</div>
|
||||
<div className="w-60 px-4 py-2.5 rounded-[34px] inline-flex justify-start items-center gap-2">
|
||||
<div className="relative">
|
||||
@@ -71,7 +71,7 @@ const ChatAIResponse: React.FC = () => {
|
||||
</defs>
|
||||
</svg>
|
||||
</div>
|
||||
<div className="justify-start text-[--Neutrals-NeutralSlate500] text-sm font-medium font-['Inter'] leading-tight">Reports</div>
|
||||
<div className="justify-start text-[--Text-Gray-500] text-sm font-medium font-['Inter'] leading-tight">Reports</div>
|
||||
</div>
|
||||
{/* Active Chat Item */}
|
||||
<div className="w-60 px-4 py-2.5 bg-[--Neutrals-NeutralSlate100] rounded-[34px] inline-flex justify-start items-center gap-2">
|
||||
@@ -80,7 +80,7 @@ const ChatAIResponse: React.FC = () => {
|
||||
<path d="M17.4996 9.58333C17.4996 13.4953 14.3283 16.6667 10.4163 16.6667C9.51896 16.6667 8.66061 16.4998 7.87057 16.1954C7.72612 16.1398 7.6539 16.112 7.59647 16.0987C7.53998 16.0857 7.49908 16.0803 7.44116 16.0781C7.38226 16.0758 7.31764 16.0825 7.18841 16.0958L2.92089 16.537C2.51402 16.579 2.31059 16.6001 2.19058 16.5269C2.08606 16.4631 2.01487 16.3566 1.99592 16.2356C1.97416 16.0968 2.07138 15.9168 2.2658 15.557L3.62885 13.034C3.7411 12.8262 3.79723 12.7223 3.82265 12.6225C3.84776 12.5238 3.85383 12.4527 3.8458 12.3512C3.83766 12.2484 3.79258 12.1147 3.70241 11.8472C3.46281 11.1363 3.33294 10.375 3.33294 9.58333C3.33294 5.67132 6.50426 2.5 10.4163 2.5C14.3283 2.5 17.4996 5.67132 17.4996 9.58333Z" stroke="var(--Brand-Orange, #3399FF)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</svg>
|
||||
</div>
|
||||
<div className="justify-start text-[--Neutrals-NeutralSlate950] text-sm font-medium font-['Inter'] leading-tight">Chat</div>
|
||||
<div className="justify-start text-[--Text-Gray-950] text-sm font-medium font-['Inter'] leading-tight">Chat</div>
|
||||
</div>
|
||||
<div className="w-60 px-4 py-2.5 rounded-[34px] inline-flex justify-start items-center gap-2">
|
||||
<div className="relative">
|
||||
@@ -90,7 +90,7 @@ const ChatAIResponse: React.FC = () => {
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
<div className="justify-start text-[--Neutrals-NeutralSlate500] text-sm font-medium font-['Inter'] leading-tight">Help</div>
|
||||
<div className="justify-start text-[--Text-Gray-500] text-sm font-medium font-['Inter'] leading-tight">Help</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -102,40 +102,40 @@ const ChatAIResponse: React.FC = () => {
|
||||
<div className="relative">
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clipPath="url(#clip0_1042_733)">
|
||||
<path d="M10.0001 12.5013C11.3808 12.5013 12.5001 11.382 12.5001 10.0013C12.5001 8.62059 11.3808 7.5013 10.0001 7.5013C8.61937 7.5013 7.50008 8.62059 7.50008 10.0013C7.50008 11.382 8.61937 12.5013 10.0001 12.5013Z" stroke="var(--Neutrals-NeutralSlate400, #A4A7AE)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<path d="M10.0001 12.5013C11.3808 12.5013 12.5001 11.382 12.5001 10.0013C12.5001 8.62059 11.3808 7.5013 10.0001 7.5013C8.61937 7.5013 7.50008 8.62059 7.50008 10.0013C7.50008 11.382 8.61937 12.5013 10.0001 12.5013Z" stroke="var(--Neutrals-NeutralSlate400)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
<div className="flex-1 justify-start text-[--Neutrals-NeutralSlate500] text-sm font-medium font-['Inter'] leading-tight">Settings</div>
|
||||
<div className="flex-1 justify-start text-[--Text-Gray-500] text-sm font-medium font-['Inter'] leading-tight">Settings</div>
|
||||
</div>
|
||||
|
||||
{/* Company Report Card */}
|
||||
<div className="self-stretch bg-[--Neutrals-NeutralSlate0] rounded-[20px] shadow-[0px_1px_4px_0px_rgba(14,18,27,0.04)] outline outline-1 outline-offset-[-1px] outline-[--$1] flex flex-col justify-start items-start overflow-hidden">
|
||||
<div className="self-stretch bg-[--Neutrals-NeutralSlate0] rounded-[20px] shadow-[0px_1px_4px_0px_rgba(14,18,27,0.04)] outline outline-1 outline-offset-[-1px] outline-[--Neutrals-NeutralSlate200] flex flex-col justify-start items-start overflow-hidden">
|
||||
<div className="self-stretch h-24 relative">
|
||||
<div className="w-60 h-32 left-0 top-[-0.50px] absolute bg-gradient-to-b from-black to-black/0" />
|
||||
<div className="w-60 p-3 left-[18.12px] top-[42.52px] absolute origin-top-left rotate-[-28.34deg] bg-[--Neutrals-NeutralSlate0] rounded-xl shadow-[0px_10px_20px_4px_rgba(14,18,27,0.08)] outline outline-1 outline-offset-[-1px] outline-[--$1] inline-flex flex-col justify-start items-start gap-3 overflow-hidden" />
|
||||
<div className="w-60 p-3 left-[31.44px] top-[22px] absolute origin-top-left rotate-[-28.34deg] bg-[--Neutrals-NeutralSlate0] rounded-xl shadow-[0px_10px_20px_4px_rgba(14,18,27,0.08)] outline outline-1 outline-offset-[-1px] outline-[--$1] inline-flex flex-col justify-start items-start gap-3 overflow-hidden" />
|
||||
<div className="w-60 p-3 left-[18.12px] top-[42.52px] absolute origin-top-left rotate-[-28.34deg] bg-[--Neutrals-NeutralSlate0] rounded-xl shadow-[0px_10px_20px_4px_rgba(14,18,27,0.08)] outline outline-1 outline-offset-[-1px] outline-[--Neutrals-NeutralSlate200] inline-flex flex-col justify-start items-start gap-3 overflow-hidden" />
|
||||
<div className="w-60 p-3 left-[31.44px] top-[22px] absolute origin-top-left rotate-[-28.34deg] bg-[--Neutrals-NeutralSlate0] rounded-xl shadow-[0px_10px_20px_4px_rgba(14,18,27,0.08)] outline outline-1 outline-offset-[-1px] outline-[--Neutrals-NeutralSlate200] inline-flex flex-col justify-start items-start gap-3 overflow-hidden" />
|
||||
</div>
|
||||
<div className="self-stretch p-3 flex flex-col justify-start items-start gap-1">
|
||||
<div className="self-stretch justify-start text-[--Neutrals-NeutralSlate800] text-sm font-semibold font-['Inter'] leading-tight">Build [Company]'s Report</div>
|
||||
<div className="self-stretch justify-start text-[--Neutrals-NeutralSlate500] text-xs font-normal font-['Inter'] leading-none">Share this form with your team members to capture valuable info about your company to train Auditly.</div>
|
||||
<div className="self-stretch justify-start text-[--Text-Gray-800] text-sm font-semibold font-['Inter'] leading-tight">Build [Company]'s Report</div>
|
||||
<div className="self-stretch justify-start text-[--Text-Gray-500] text-xs font-normal font-['Inter'] leading-none">Share this form with your team members to capture valuable info about your company to train Orbitly.</div>
|
||||
</div>
|
||||
<div className="self-stretch px-3 pb-3 flex flex-col justify-start items-start gap-8">
|
||||
<div className="self-stretch inline-flex justify-start items-start gap-2">
|
||||
<div className="flex-1 px-3 py-1.5 bg-Button-Secondary rounded-[999px] flex justify-center items-center gap-0.5 overflow-hidden">
|
||||
<div className="flex-1 px-3 py-1.5 bg-[--Button-Secondary] rounded-[999px] flex justify-center items-center gap-0.5 overflow-hidden">
|
||||
<div className="px-1 flex justify-center items-center">
|
||||
<div className="justify-center text-[--Neutrals-NeutralSlate950] text-sm font-medium font-['Inter'] leading-tight">Invite</div>
|
||||
<div className="justify-center text-[--Text-Gray-950] text-sm font-medium font-['Inter'] leading-tight">Invite</div>
|
||||
</div>
|
||||
<div className="relative">
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M7.99992 3.33203V12.6654M3.33325 7.9987H12.6666" stroke="var(--Neutrals-NeutralSlate950, #0A0D12)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<path d="M7.99992 3.33203V12.6654M3.33325 7.9987H12.6666" stroke="var(--Neutrals-NeutralSlate950)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-1 px-3 py-1.5 bg-[--Brand-Orange] rounded-[999px] outline outline-1 outline-offset-[-1px] outline-blue-400 flex justify-center items-center gap-0.5 overflow-hidden">
|
||||
<div className="relative">
|
||||
<svg width="17" height="16" viewBox="0 0 17 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8.97179 12.2442L8.02898 13.1871C6.72723 14.4888 4.61668 14.4888 3.31493 13.1871C2.01319 11.8853 2.01319 9.77476 3.31493 8.47301L4.25774 7.5302M12.743 8.47301L13.6858 7.5302C14.9876 6.22845 14.9876 4.1179 13.6858 2.81615C12.3841 1.51441 10.2735 1.51441 8.97179 2.81615L8.02898 3.75896M6.16705 10.3349L10.8337 5.66826" stroke="var(--white, white)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<path d="M8.97179 12.2442L8.02898 13.1871C6.72723 14.4888 4.61668 14.4888 3.31493 13.1871C2.01319 11.8853 2.01319 9.77476 3.31493 8.47301L4.25774 7.5302M12.743 8.47301L13.6858 7.5302C14.9876 6.22845 14.9876 4.1179 13.6858 2.81615C12.3841 1.51441 10.2735 1.51441 8.97179 2.81615L8.02898 3.75896M6.16705 10.3349L10.8337 5.66826" stroke="var(--white)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</svg>
|
||||
</div>
|
||||
<div className="px-1 flex justify-center items-center">
|
||||
@@ -154,52 +154,52 @@ const ChatAIResponse: React.FC = () => {
|
||||
<div className="self-stretch flex flex-col justify-start items-end gap-4">
|
||||
{/* User Question */}
|
||||
<div className="px-4 py-3 bg-[--Neutrals-NeutralSlate100] rounded-2xl inline-flex justify-start items-center gap-3 overflow-hidden">
|
||||
<div className="justify-start text-Text-Gray-800 text-base font-normal font-['Inter'] leading-normal">What are the main characteristics?</div>
|
||||
<div className="justify-start text-[--Text-Gray-800] text-base font-normal font-['Inter'] leading-normal">What are the main characteristics?</div>
|
||||
<div className="pl-1.5 pr-2 py-0.5 bg-[--Neutrals-NeutralSlate300] rounded-2xl flex justify-start items-center gap-1.5">
|
||||
<div className="relative">
|
||||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clipPath="url(#clip0_818_20157)">
|
||||
<path d="M9.33318 4.66761V7.58427C9.33318 8.0484 9.51755 8.49352 9.84574 8.82171C10.1739 9.1499 10.619 9.33427 11.0832 9.33427C11.5473 9.33427 11.9924 9.1499 12.3206 8.82171C12.6488 8.49352 12.8332 8.0484 12.8332 7.58427V7.00094C12.8331 5.68437 12.3876 4.40655 11.5693 3.37525C10.7509 2.34395 9.60767 1.61982 8.32555 1.32061C7.04343 1.02141 5.6978 1.16471 4.50746 1.72724C3.31712 2.28976 2.35207 3.23841 1.76924 4.41895C1.18641 5.59948 1.02007 6.94245 1.29727 8.22951C1.57447 9.51656 2.27891 10.672 3.29604 11.5079C4.31317 12.3439 5.58318 12.8111 6.89955 12.8338C8.21592 12.8564 9.50124 12.4331 10.5465 11.6326M9.33318 7.00094C9.33318 8.2896 8.28851 9.33427 6.99984 9.33427C5.71118 9.33427 4.66651 8.2896 4.66651 7.00094C4.66651 5.71228 5.71118 4.66761 6.99984 4.66761C8.28851 4.66761 9.33318 5.71228 9.33318 7.00094Z" stroke="var(--Text-Gray-600, #535862)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<path d="M9.33318 4.66761V7.58427C9.33318 8.0484 9.51755 8.49352 9.84574 8.82171C10.1739 9.1499 10.619 9.33427 11.0832 9.33427C11.5473 9.33427 11.9924 9.1499 12.3206 8.82171C12.6488 8.49352 12.8332 8.0484 12.8332 7.58427V7.00094C12.8331 5.68437 12.3876 4.40655 11.5693 3.37525C10.7509 2.34395 9.60767 1.61982 8.32555 1.32061C7.04343 1.02141 5.6978 1.16471 4.50746 1.72724C3.31712 2.28976 2.35207 3.23841 1.76924 4.41895C1.18641 5.59948 1.02007 6.94245 1.29727 8.22951C1.57447 9.51656 2.27891 10.672 3.29604 11.5079C4.31317 12.3439 5.58318 12.8111 6.89955 12.8338C8.21592 12.8564 9.50124 12.4331 10.5465 11.6326M9.33318 7.00094C9.33318 8.2896 8.28851 9.33427 6.99984 9.33427C5.71118 9.33427 4.66651 8.2896 4.66651 7.00094C4.66651 5.71228 5.71118 4.66761 6.99984 4.66761C8.28851 4.66761 9.33318 5.71228 9.33318 7.00094Z" stroke="var(--Text-Gray-600)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
<div className="text-center justify-start text-Text-Gray-600 text-sm font-medium font-['Inter'] leading-tight">Alex Green</div>
|
||||
<div className="text-center justify-start text-[--Text-Gray-600] text-sm font-medium font-['Inter'] leading-tight">Alex Green</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* AI Response */}
|
||||
<div className="self-stretch justify-start">
|
||||
<span className="text-Text-Gray-800 text-base font-normal font-['Inter'] leading-normal">Main characteristics refer to the most important features or qualities that define or distinguish something. These can be physical, emotional, mental, or moral attributes that help identify and differentiate a person, object, or concept. <br />Elaboration:<br /></span>
|
||||
<span className="text-Text-Gray-800 text-base font-medium font-['Inter'] leading-normal">Physical Characteristics:</span>
|
||||
<span className="text-Text-Gray-800 text-base font-normal font-['Inter'] leading-normal"> These relate to the outward appearance of something, such as its shape, size, color, or material. For example, the physical characteristics of a chair might include its height, the material it's made of (wood, metal, etc.), and its color. <br /></span>
|
||||
<span className="text-Text-Gray-800 text-base font-medium font-['Inter'] leading-normal">Emotional Characteristics:</span>
|
||||
<span className="text-Text-Gray-800 text-base font-normal font-['Inter'] leading-normal"> These describe how someone or something responds to situations or experiences. Examples include being happy, sad, angry, or calm. <br /></span>
|
||||
<span className="text-Text-Gray-800 text-base font-medium font-['Inter'] leading-normal">Mental Characteristics:</span>
|
||||
<span className="text-Text-Gray-800 text-base font-normal font-['Inter'] leading-normal"> These relate to a person's cognitive abilities and thinking patterns. Examples include intelligence, creativity, and curiosity. <br /></span>
|
||||
<span className="text-Text-Gray-800 text-base font-medium font-['Inter'] leading-normal">Moral Characteristics:</span>
|
||||
<span className="text-Text-Gray-800 text-base font-normal font-['Inter'] leading-normal"> These describe a person's ethical or value-based behavior. Examples include kindness, honesty, and generosity. </span>
|
||||
<span className="text-[--Text-Gray-800] text-base font-normal font-['Inter'] leading-normal">Main characteristics refer to the most important features or qualities that define or distinguish something. These can be physical, emotional, mental, or moral attributes that help identify and differentiate a person, object, or concept. <br />Elaboration:<br /></span>
|
||||
<span className="text-[--Text-Gray-800] text-base font-medium font-['Inter'] leading-normal">Physical Characteristics:</span>
|
||||
<span className="text-[--Text-Gray-800] text-base font-normal font-['Inter'] leading-normal"> These relate to the outward appearance of something, such as its shape, size, color, or material. For example, the physical characteristics of a chair might include its height, the material it's made of (wood, metal, etc.), and its color. <br /></span>
|
||||
<span className="text-[--Text-Gray-800] text-base font-medium font-['Inter'] leading-normal">Emotional Characteristics:</span>
|
||||
<span className="text-[--Text-Gray-800] text-base font-normal font-['Inter'] leading-normal"> These describe how someone or something responds to situations or experiences. Examples include being happy, sad, angry, or calm. <br /></span>
|
||||
<span className="text-[--Text-Gray-800] text-base font-medium font-['Inter'] leading-normal">Mental Characteristics:</span>
|
||||
<span className="text-[--Text-Gray-800] text-base font-normal font-['Inter'] leading-normal"> These relate to a person's cognitive abilities and thinking patterns. Examples include intelligence, creativity, and curiosity. <br /></span>
|
||||
<span className="text-[--Text-Gray-800] text-base font-medium font-['Inter'] leading-normal">Moral Characteristics:</span>
|
||||
<span className="text-[--Text-Gray-800] text-base font-normal font-['Inter'] leading-normal"> These describe a person's ethical or value-based behavior. Examples include kindness, honesty, and generosity. </span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Chat Input */}
|
||||
<div className="self-stretch pl-5 pr-3 pt-5 pb-3 bg-[--Neutrals-NeutralSlate50] rounded-3xl flex flex-col justify-start items-start gap-4">
|
||||
<div className="self-stretch justify-start text-Text-Gray-500 text-base font-normal font-['Inter'] leading-normal">Ask anything, use @ to tag staff and ask questions.</div>
|
||||
<div className="self-stretch justify-start text-[--Text-Gray-500] text-base font-normal font-['Inter'] leading-normal">Ask anything, use @ to tag staff and ask questions.</div>
|
||||
<div className="self-stretch inline-flex justify-between items-center">
|
||||
<div className="flex justify-start items-center gap-4">
|
||||
<div className="relative">
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M17.6271 9.08442L10.1141 16.5974C8.40556 18.306 5.63546 18.306 3.92692 16.5974C2.21837 14.8889 2.21837 12.1188 3.92692 10.4102L11.4399 2.89724C12.579 1.75821 14.4257 1.75821 15.5647 2.89724C16.7037 4.03627 16.7037 5.883 15.5647 7.02203L8.34633 14.2404C7.77682 14.8099 6.85345 14.8099 6.28394 14.2404C5.71442 13.6709 5.71442 12.7475 6.28394 12.178L12.6184 5.84352" stroke="var(--Text-Gray-500, #717680)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<path d="M17.6271 9.08442L10.1141 16.5974C8.40556 18.306 5.63546 18.306 3.92692 16.5974C2.21837 14.8889 2.21837 12.1188 3.92692 10.4102L11.4399 2.89724C12.579 1.75821 14.4257 1.75821 15.5647 2.89724C16.7037 4.03627 16.7037 5.883 15.5647 7.02203L8.34633 14.2404C7.77682 14.8099 6.85345 14.8099 6.28394 14.2404C5.71442 13.6709 5.71442 12.7475 6.28394 12.178L12.6184 5.84352" stroke="var(--Text-Gray-500)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</svg>
|
||||
</div>
|
||||
<div className="relative">
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M13.5 17.5H5.77614C5.2713 17.5 5.01887 17.5 4.90199 17.4002C4.80056 17.3135 4.74674 17.1836 4.75721 17.0506C4.76927 16.8974 4.94776 16.7189 5.30474 16.3619L12.3905 9.27614C12.7205 8.94613 12.8855 8.78112 13.0758 8.7193C13.2432 8.66492 13.4235 8.66492 13.5908 8.7193C13.7811 8.78112 13.9461 8.94613 14.2761 9.27614L17.5 12.5V13.5M13.5 17.5C14.9001 17.5 15.6002 17.5 16.135 17.2275C16.6054 16.9878 16.9878 16.6054 17.2275 16.135C17.5 15.6002 17.5 14.9001 17.5 13.5M13.5 17.5H6.5C5.09987 17.5 4.3998 17.5 3.86502 17.2275C3.39462 16.9878 3.01217 16.6054 2.77248 16.135C2.5 15.6002 2.5 14.9001 2.5 13.5V6.5C2.5 5.09987 2.5 4.3998 2.77248 3.86502C3.01217 3.39462 3.39462 3.01217 3.86502 2.77248C4.3998 2.5 5.09987 2.5 6.5 2.5H13.5C14.9001 2.5 15.6002 2.5 16.135 2.77248C16.6054 3.01217 16.9878 3.39462 17.2275 3.86502C17.5 4.3998 17.5 5.09987 17.5 6.5V13.5M8.75 7.08333C8.75 8.00381 8.00381 8.75 7.08333 8.75C6.16286 8.75 5.41667 8.00381 5.41667 7.08333C5.41667 6.16286 6.16286 5.41667 7.08333 5.41667C8.00381 5.41667 8.75 6.16286 8.75 7.08333Z" stroke="var(--Text-Gray-500, #717680)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<path d="M13.5 17.5H5.77614C5.2713 17.5 5.01887 17.5 4.90199 17.4002C4.80056 17.3135 4.74674 17.1836 4.75721 17.0506C4.76927 16.8974 4.94776 16.7189 5.30474 16.3619L12.3905 9.27614C12.7205 8.94613 12.8855 8.78112 13.0758 8.7193C13.2432 8.66492 13.4235 8.66492 13.5908 8.7193C13.7811 8.78112 13.9461 8.94613 14.2761 9.27614L17.5 12.5V13.5M13.5 17.5C14.9001 17.5 15.6002 17.5 16.135 17.2275C16.6054 16.9878 16.9878 16.6054 17.2275 16.135C17.5 15.6002 17.5 14.9001 17.5 13.5M13.5 17.5H6.5C5.09987 17.5 4.3998 17.5 3.86502 17.2275C3.39462 16.9878 3.01217 16.6054 2.77248 16.135C2.5 15.6002 2.5 14.9001 2.5 13.5V6.5C2.5 5.09987 2.5 4.3998 2.77248 3.86502C3.01217 3.39462 3.39462 3.01217 3.86502 2.77248C4.3998 2.5 5.09987 2.5 6.5 2.5H13.5C14.9001 2.5 15.6002 2.5 16.135 2.77248C16.6054 3.01217 16.9878 3.39462 17.2275 3.86502C17.5 4.3998 17.5 5.09987 17.5 6.5V13.5M8.75 7.08333C8.75 8.00381 8.00381 8.75 7.08333 8.75C6.16286 8.75 5.41667 8.00381 5.41667 7.08333C5.41667 6.16286 6.16286 5.41667 7.08333 5.41667C8.00381 5.41667 8.75 6.16286 8.75 7.08333Z" stroke="var(--Text-Gray-500)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</svg>
|
||||
</div>
|
||||
<div className="relative">
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clipPath="url(#clip0_818_20047)">
|
||||
<path d="M13.3334 6.66745V10.8341C13.3334 11.4972 13.5968 12.133 14.0657 12.6019C14.5345 13.0707 15.1704 13.3341 15.8334 13.3341C16.4965 13.3341 17.1324 13.0707 17.6012 12.6019C18.07 12.133 18.3334 11.4972 18.3334 10.8341V10.0008C18.3333 8.11998 17.6969 6.29452 16.5278 4.82123C15.3587 3.34794 13.7256 2.31347 11.894 1.88603C10.0624 1.45859 8.14003 1.66332 6.43955 2.46692C4.73906 3.27053 3.36042 4.62575 2.5278 6.31222C1.69519 7.99869 1.45756 9.91723 1.85356 11.7559C2.24956 13.5945 3.2559 15.2451 4.70895 16.4393C6.16199 17.6335 7.97628 18.3011 9.85681 18.3334C11.7373 18.3657 13.5735 17.761 15.0668 16.6175M13.3334 10.0008C13.3334 11.8417 11.841 13.3341 10.0001 13.3341C8.15914 13.3341 6.66676 11.8417 6.66676 10.0008C6.66676 8.15984 8.15914 6.66745 10.0001 6.66745C11.841 6.66745 13.3334 8.15984 13.3334 10.0008Z" stroke="var(--Text-Gray-500, #717680)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<path d="M13.3334 6.66745V10.8341C13.3334 11.4972 13.5968 12.133 14.0657 12.6019C14.5345 13.0707 15.1704 13.3341 15.8334 13.3341C16.4965 13.3341 17.1324 13.0707 17.6012 12.6019C18.07 12.133 18.3334 11.4972 18.3334 10.8341V10.0008C18.3333 8.11998 17.6969 6.29452 16.5278 4.82123C15.3587 3.34794 13.7256 2.31347 11.894 1.88603C10.0624 1.45859 8.14003 1.66332 6.43955 2.46692C4.73906 3.27053 3.36042 4.62575 2.5278 6.31222C1.69519 7.99869 1.45756 9.91723 1.85356 11.7559C2.24956 13.5945 3.2559 15.2451 4.70895 16.4393C6.16199 17.6335 7.97628 18.3011 9.85681 18.3334C11.7373 18.3657 13.5735 17.761 15.0668 16.6175M13.3334 10.0008C13.3334 11.8417 11.841 13.3341 10.0001 13.3341C8.15914 13.3341 6.66676 11.8417 6.66676 10.0008C6.66676 8.15984 8.15914 6.66745 10.0001 6.66745C11.841 6.66745 13.3334 8.15984 13.3334 10.0008Z" stroke="var(--Text-Gray-500)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
@@ -207,7 +207,7 @@ const ChatAIResponse: React.FC = () => {
|
||||
<div className="p-2.5 bg-[--Neutrals-NeutralSlate300] rounded-[999px] flex justify-start items-center gap-2.5 overflow-hidden">
|
||||
<div className="relative">
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8 13.3346V2.66797M8 2.66797L4 6.66797M8 2.66797L12 6.66797" stroke="var(--Text-White-00, #FDFDFD)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<path d="M8 13.3346V2.66797M8 2.66797L4 6.66797M8 2.66797L12 6.66797" stroke="var(--Text-White-00)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
@@ -220,4 +220,4 @@ const ChatAIResponse: React.FC = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default ChatAIResponse;
|
||||
export default ChatAIResponse;
|
||||
|
||||
@@ -5,10 +5,10 @@ const ChatLight: React.FC = () => {
|
||||
<div className="w-full h-[810px] p-4 bg-[--Neutrals-NeutralSlate200] inline-flex justify-start items-start overflow-hidden">
|
||||
<div className="flex-1 self-stretch rounded-3xl shadow-[0px_0px_15px_0px_rgba(0,0,0,0.08)] flex justify-between items-start overflow-hidden">
|
||||
{/* Sidebar */}
|
||||
<div className="w-64 self-stretch max-w-64 min-w-64 px-3 pt-4 pb-3 bg-[--Neutrals-NeutralSlate0] border-r border-[--$1] inline-flex flex-col justify-between items-center overflow-hidden">
|
||||
<div className="w-64 self-stretch max-w-64 min-w-64 px-3 pt-4 pb-3 bg-[--Neutrals-NeutralSlate0] border-r border-[--Neutrals-NeutralSlate200] inline-flex flex-col justify-between items-center overflow-hidden">
|
||||
<div className="self-stretch flex flex-col justify-start items-start gap-5">
|
||||
{/* Company Selector */}
|
||||
<div className="w-60 pl-2 pr-4 py-2 bg-[--Neutrals-NeutralSlate0] rounded-3xl outline outline-1 outline-offset-[-1px] outline-[--$1] inline-flex justify-between items-center overflow-hidden">
|
||||
<div className="w-60 pl-2 pr-4 py-2 bg-[--Neutrals-NeutralSlate0] rounded-3xl outline outline-1 outline-offset-[-1px] outline-[--Neutrals-NeutralSlate200] inline-flex justify-between items-center overflow-hidden">
|
||||
<div className="flex-1 flex justify-start items-center gap-2">
|
||||
<div className="w-8 h-8 rounded-full flex justify-start items-center gap-2.5">
|
||||
<div className="w-8 h-8 relative bg-[--Brand-Orange] rounded-full outline outline-[1.60px] outline-offset-[-1.60px] outline-white/10 overflow-hidden">
|
||||
@@ -29,12 +29,12 @@ const ChatLight: React.FC = () => {
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-1 inline-flex flex-col justify-start items-start gap-0.5">
|
||||
<div className="self-stretch justify-start text-[--Neutrals-NeutralSlate950] text-base font-medium font-['Inter'] leading-normal">Zitlac Media</div>
|
||||
<div className="self-stretch justify-start text-[--Text-Gray-950] text-base font-medium font-['Inter'] leading-normal">Zitlac Media</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="relative">
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5.83333 12.4987L9.99999 16.6654L14.1667 12.4987M5.83333 7.4987L9.99999 3.33203L14.1667 7.4987" stroke="var(--Neutrals-NeutralSlate400, #A4A7AE)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<path d="M5.83333 12.4987L9.99999 16.6654L14.1667 12.4987M5.83333 7.4987L9.99999 3.33203L14.1667 7.4987" stroke="var(--Neutrals-NeutralSlate400)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
@@ -45,47 +45,47 @@ const ChatLight: React.FC = () => {
|
||||
<div className="w-60 px-4 py-2.5 rounded-[34px] inline-flex justify-start items-center gap-2">
|
||||
<div className="relative">
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M7.5 17.5016V11.3349C7.5 10.8682 7.5 10.6348 7.59083 10.4566C7.67072 10.2998 7.79821 10.1723 7.95501 10.0924C8.13327 10.0016 8.36662 10.0016 8.83333 10.0016H11.1667C11.6334 10.0016 11.8667 10.0016 12.045 10.0924C12.2018 10.1723 12.3293 10.2998 12.4092 10.4566C12.5 10.6348 12.5 10.8682 12.5 11.3349V17.5016M9.18141 2.30492L3.52949 6.70086C3.15168 6.99471 2.96278 7.14163 2.82669 7.32563C2.70614 7.48862 2.61633 7.67224 2.56169 7.86746C2.5 8.08785 2.5 8.32717 2.5 8.8058V14.8349C2.5 15.7683 2.5 16.235 2.68166 16.5916C2.84144 16.9052 3.09641 17.1601 3.41002 17.3199C3.76654 17.5016 4.23325 17.5016 5.16667 17.5016H14.8333C15.7668 17.5016 16.2335 17.5016 16.59 17.3199C16.9036 17.1601 17.1586 16.9052 17.3183 16.5916C17.5 16.235 17.5 15.7683 17.5 14.8349V8.8058C17.5 8.32717 17.5 8.08785 17.4383 7.86746C17.3837 7.67224 17.2939 7.48862 17.1733 7.32563C17.0372 7.14163 16.8483 6.99471 16.4705 6.70086L10.8186 2.30492C10.5258 2.07721 10.3794 1.96335 10.2178 1.91959C10.0752 1.88097 9.92484 1.88097 9.78221 1.91959C9.62057 1.96335 9.47418 2.07721 9.18141 2.30492Z" stroke="var(--Neutrals-NeutralSlate400, #A4A7AE)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<path d="M7.5 17.5016V11.3349C7.5 10.8682 7.5 10.6348 7.59083 10.4566C7.67072 10.2998 7.79821 10.1723 7.95501 10.0924C8.13327 10.0016 8.36662 10.0016 8.83333 10.0016H11.1667C11.6334 10.0016 11.8667 10.0016 12.045 10.0924C12.2018 10.1723 12.3293 10.2998 12.4092 10.4566C12.5 10.6348 12.5 10.8682 12.5 11.3349V17.5016M9.18141 2.30492L3.52949 6.70086C3.15168 6.99471 2.96278 7.14163 2.82669 7.32563C2.70614 7.48862 2.61633 7.67224 2.56169 7.86746C2.5 8.08785 2.5 8.32717 2.5 8.8058V14.8349C2.5 15.7683 2.5 16.235 2.68166 16.5916C2.84144 16.9052 3.09641 17.1601 3.41002 17.3199C3.76654 17.5016 4.23325 17.5016 5.16667 17.5016H14.8333C15.7668 17.5016 16.2335 17.5016 16.59 17.3199C16.9036 17.1601 17.1586 16.9052 17.3183 16.5916C17.5 16.235 17.5 15.7683 17.5 14.8349V8.8058C17.5 8.32717 17.5 8.08785 17.4383 7.86746C17.3837 7.67224 17.2939 7.48862 17.1733 7.32563C17.0372 7.14163 16.8483 6.99471 16.4705 6.70086L10.8186 2.30492C10.5258 2.07721 10.3794 1.96335 10.2178 1.91959C10.0752 1.88097 9.92484 1.88097 9.78221 1.91959C9.62057 1.96335 9.47418 2.07721 9.18141 2.30492Z" stroke="var(--Neutrals-NeutralSlate400)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</svg>
|
||||
</div>
|
||||
<div className="justify-start text-[--Neutrals-NeutralSlate500] text-sm font-medium font-['Inter'] leading-tight">Company Wiki</div>
|
||||
<div className="justify-start text-[--Text-Gray-500] text-sm font-medium font-['Inter'] leading-tight">Company Wiki</div>
|
||||
</div>
|
||||
<div className="w-60 px-4 py-2.5 rounded-[34px] inline-flex justify-start items-center gap-2">
|
||||
<div className="relative">
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11.6667 9.16797H6.66667M8.33333 12.5013H6.66667M13.3333 5.83464H6.66667M16.6667 5.66797V14.3346C16.6667 15.7348 16.6667 16.4348 16.3942 16.9696C16.1545 17.44 15.772 17.8225 15.3016 18.0622C14.7669 18.3346 14.0668 18.3346 12.6667 18.3346H7.33333C5.9332 18.3346 5.23314 18.3346 4.69836 18.0622C4.22795 17.8225 3.8455 17.44 3.60582 16.9696C3.33333 16.4348 3.33333 15.7348 3.33333 14.3346V5.66797C3.33333 4.26784 3.33333 3.56777 3.60582 3.03299C3.8455 2.56259 4.22795 2.18014 4.69836 1.94045C5.23314 1.66797 5.9332 1.66797 7.33333 1.66797H12.6667C14.0668 1.66797 14.7669 1.66797 15.3016 1.94045C15.772 2.18014 16.1545 2.56259 16.3942 3.03299C16.6667 3.56777 16.6667 4.26784 16.6667 5.66797Z" stroke="var(--Neutrals-NeutralSlate400, #A4A7AE)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<path d="M11.6667 9.16797H6.66667M8.33333 12.5013H6.66667M13.3333 5.83464H6.66667M16.6667 5.66797V14.3346C16.6667 15.7348 16.6667 16.4348 16.3942 16.9696C16.1545 17.44 15.772 17.8225 15.3016 18.0622C14.7669 18.3346 14.0668 18.3346 12.6667 18.3346H7.33333C5.9332 18.3346 5.23314 18.3346 4.69836 18.0622C4.22795 17.8225 3.8455 17.44 3.60582 16.9696C3.33333 16.4348 3.33333 15.7348 3.33333 14.3346V5.66797C3.33333 4.26784 3.33333 3.56777 3.60582 3.03299C3.8455 2.56259 4.22795 2.18014 4.69836 1.94045C5.23314 1.66797 5.9332 1.66797 7.33333 1.66797H12.6667C14.0668 1.66797 14.7669 1.66797 15.3016 1.94045C15.772 2.18014 16.1545 2.56259 16.3942 3.03299C16.6667 3.56777 16.6667 4.26784 16.6667 5.66797Z" stroke="var(--Neutrals-NeutralSlate400)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</svg>
|
||||
</div>
|
||||
<div className="justify-start text-[--Neutrals-NeutralSlate500] text-sm font-medium font-['Inter'] leading-tight">Submissions</div>
|
||||
<div className="justify-start text-[--Text-Gray-500] text-sm font-medium font-['Inter'] leading-tight">Submissions</div>
|
||||
</div>
|
||||
<div className="w-60 px-4 py-2.5 rounded-[34px] inline-flex justify-start items-center gap-2">
|
||||
<div className="relative">
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clipPath="url(#clip0_1042_1469)">
|
||||
<path d="M10 1.66797C11.0943 1.66797 12.178 1.88352 13.189 2.30231C14.2001 2.7211 15.1187 3.33493 15.8926 4.10875C16.6664 4.88257 17.2802 5.80123 17.699 6.81228C18.1178 7.82332 18.3333 8.90696 18.3333 10.0013M10 1.66797V10.0013M10 1.66797C5.39763 1.66797 1.66667 5.39893 1.66667 10.0013C1.66667 14.6037 5.39763 18.3346 10 18.3346C14.6024 18.3346 18.3333 14.6037 18.3333 10.0013M10 1.66797C14.6024 1.66797 18.3333 5.39893 18.3333 10.0013M18.3333 10.0013L10 10.0013M18.3333 10.0013C18.3333 11.3164 18.0221 12.6128 17.4251 13.7846C16.828 14.9563 15.9621 15.9701 14.8982 16.7431L10 10.0013" stroke="var(--Neutrals-NeutralSlate400, #A4A7AE)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<path d="M10 1.66797C11.0943 1.66797 12.178 1.88352 13.189 2.30231C14.2001 2.7211 15.1187 3.33493 15.8926 4.10875C16.6664 4.88257 17.2802 5.80123 17.699 6.81228C18.1178 7.82332 18.3333 8.90696 18.3333 10.0013M10 1.66797V10.0013M10 1.66797C5.39763 1.66797 1.66667 5.39893 1.66667 10.0013C1.66667 14.6037 5.39763 18.3346 10 18.3346C14.6024 18.3346 18.3333 14.6037 18.3333 10.0013M10 1.66797C14.6024 1.66797 18.3333 5.39893 18.3333 10.0013M18.3333 10.0013L10 10.0013M18.3333 10.0013C18.3333 11.3164 18.0221 12.6128 17.4251 13.7846C16.828 14.9563 15.9621 15.9701 14.8982 16.7431L10 10.0013" stroke="var(--Neutrals-NeutralSlate400)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
<div className="justify-start text-[--Neutrals-NeutralSlate500] text-sm font-medium font-['Inter'] leading-tight">Reports</div>
|
||||
<div className="justify-start text-[--Text-Gray-500] text-sm font-medium font-['Inter'] leading-tight">Reports</div>
|
||||
</div>
|
||||
{/* Active Chat Item */}
|
||||
<div className="w-60 px-4 py-2.5 bg-[--Neutrals-NeutralSlate100] rounded-[34px] inline-flex justify-start items-center gap-2">
|
||||
<div className="relative">
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M17.4997 9.58333C17.4997 13.4953 14.3284 16.6667 10.4164 16.6667C9.51904 16.6667 8.66069 16.4998 7.87065 16.1954C7.7262 16.1398 7.65398 16.112 7.59655 16.0987C7.54006 16.0857 7.49917 16.0803 7.44124 16.0781C7.38234 16.0758 7.31772 16.0825 7.18849 16.0958L2.92097 16.537C2.5141 16.579 2.31067 16.6001 2.19067 16.5269C2.08614 16.4631 2.01495 16.3566 1.996 16.2356C1.97425 16.0968 2.07146 15.9168 2.26588 15.557L3.62893 13.034C3.74118 12.8262 3.79731 12.7223 3.82273 12.6225C3.84784 12.5238 3.85391 12.4527 3.84588 12.3512C3.83775 12.2484 3.79266 12.1147 3.7025 11.8472C3.46289 11.1363 3.33302 10.375 3.33302 9.58333C3.33302 5.67132 6.50434 2.5 10.4164 2.5C14.3284 2.5 17.4997 5.67132 17.4997 9.58333Z" stroke="var(--Brand-Orange, #3399FF)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<path d="M17.4997 9.58333C17.4997 13.4953 14.3284 16.6667 10.4164 16.6667C9.51904 16.6667 8.66069 16.4998 7.87065 16.1954C7.7262 16.1398 7.65398 16.112 7.59655 16.0987C7.54006 16.0857 7.49917 16.0803 7.44124 16.0781C7.38234 16.0758 7.31772 16.0825 7.18849 16.0958L2.92097 16.537C2.5141 16.579 2.31067 16.6001 2.19067 16.5269C2.08614 16.4631 2.01495 16.3566 1.996 16.2356C1.97425 16.0968 2.07146 15.9168 2.26588 15.557L3.62893 13.034C3.74118 12.8262 3.79731 12.7223 3.82273 12.6225C3.84784 12.5238 3.85391 12.4527 3.84588 12.3512C3.83775 12.2484 3.79266 12.1147 3.7025 11.8472C3.46289 11.1363 3.33302 10.375 3.33302 9.58333C3.33302 5.67132 6.50434 2.5 10.4164 2.5C14.3284 2.5 17.4997 5.67132 17.4997 9.58333Z" stroke="var(--Brand-Orange)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</svg>
|
||||
</div>
|
||||
<div className="justify-start text-[--Neutrals-NeutralSlate950] text-sm font-medium font-['Inter'] leading-tight">Chat</div>
|
||||
<div className="justify-start text-[--Text-Gray-950] text-sm font-medium font-['Inter'] leading-tight">Chat</div>
|
||||
</div>
|
||||
<div className="w-60 px-4 py-2.5 rounded-[34px] inline-flex justify-start items-center gap-2">
|
||||
<div className="relative">
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clipPath="url(#clip0_1042_1477)">
|
||||
<path d="M7.575 7.5013C7.77092 6.94436 8.15763 6.47472 8.66663 6.17558C9.17563 5.87643 9.77408 5.76708 10.356 5.8669C10.9379 5.96671 11.4657 6.26924 11.8459 6.72091C12.2261 7.17258 12.4342 7.74424 12.4333 8.33464C12.4333 10.0013 9.93333 10.8346 9.93333 10.8346M10 14.168H10.0083M18.3333 10.0013C18.3333 14.6037 14.6024 18.3346 10 18.3346C5.39763 18.3346 1.66667 14.6037 1.66667 10.0013C1.66667 5.39893 5.39763 1.66797 10 1.66797C14.6024 1.66797 18.3333 5.39893 18.3333 10.0013Z" stroke="var(--Neutrals-NeutralSlate400, #A4A7AE)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<path d="M7.575 7.5013C7.77092 6.94436 8.15763 6.47472 8.66663 6.17558C9.17563 5.87643 9.77408 5.76708 10.356 5.8669C10.9379 5.96671 11.4657 6.26924 11.8459 6.72091C12.2261 7.17258 12.4342 7.74424 12.4333 8.33464C12.4333 10.0013 9.93333 10.8346 9.93333 10.8346M10 14.168H10.0083M18.3333 10.0013C18.3333 14.6037 14.6024 18.3346 10 18.3346C5.39763 18.3346 1.66667 14.6037 1.66667 10.0013C1.66667 5.39893 5.39763 1.66797 10 1.66797C14.6024 1.66797 18.3333 5.39893 18.3333 10.0013Z" stroke="var(--Neutrals-NeutralSlate400)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
<div className="justify-start text-[--Neutrals-NeutralSlate500] text-sm font-medium font-['Inter'] leading-tight">Help</div>
|
||||
<div className="justify-start text-[--Text-Gray-500] text-sm font-medium font-['Inter'] leading-tight">Help</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -97,40 +97,40 @@ const ChatLight: React.FC = () => {
|
||||
<div className="relative">
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clipPath="url(#clip0_1042_1482)">
|
||||
<path d="M10 12.5013C11.3807 12.5013 12.5 11.382 12.5 10.0013C12.5 8.62059 11.3807 7.5013 10 7.5013C8.61929 7.5013 7.5 8.62059 7.5 10.0013C7.5 11.382 8.61929 12.5013 10 12.5013Z" stroke="var(--Neutrals-NeutralSlate400, #A4A7AE)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<path d="M10 12.5013C11.3807 12.5013 12.5 11.382 12.5 10.0013C12.5 8.62059 11.3807 7.5013 10 7.5013C8.61929 7.5013 7.5 8.62059 7.5 10.0013C7.5 11.382 8.61929 12.5013 10 12.5013Z" stroke="var(--Neutrals-NeutralSlate400)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
<div className="flex-1 justify-start text-[--Neutrals-NeutralSlate500] text-sm font-medium font-['Inter'] leading-tight">Settings</div>
|
||||
<div className="flex-1 justify-start text-[--Text-Gray-500] text-sm font-medium font-['Inter'] leading-tight">Settings</div>
|
||||
</div>
|
||||
|
||||
{/* Company Report Card */}
|
||||
<div className="self-stretch bg-[--Neutrals-NeutralSlate0] rounded-[20px] shadow-[0px_1px_4px_0px_rgba(14,18,27,0.04)] outline outline-1 outline-offset-[-1px] outline-[--$1] flex flex-col justify-start items-start overflow-hidden">
|
||||
<div className="self-stretch bg-[--Neutrals-NeutralSlate0] rounded-[20px] shadow-[0px_1px_4px_0px_rgba(14,18,27,0.04)] outline outline-1 outline-offset-[-1px] outline-[--Neutrals-NeutralSlate200] flex flex-col justify-start items-start overflow-hidden">
|
||||
<div className="self-stretch h-24 relative">
|
||||
<div className="w-60 h-32 left-0 top-[-0.50px] absolute bg-gradient-to-b from-black to-black/0" />
|
||||
<div className="w-60 p-3 left-[18.12px] top-[42.52px] absolute origin-top-left rotate-[-28.34deg] bg-[--Neutrals-NeutralSlate0] rounded-xl shadow-[0px_10px_20px_4px_rgba(14,18,27,0.08)] outline outline-1 outline-offset-[-1px] outline-[--$1] inline-flex flex-col justify-start items-start gap-3 overflow-hidden" />
|
||||
<div className="w-60 p-3 left-[31.44px] top-[22px] absolute origin-top-left rotate-[-28.34deg] bg-[--Neutrals-NeutralSlate0] rounded-xl shadow-[0px_10px_20px_4px_rgba(14,18,27,0.08)] outline outline-1 outline-offset-[-1px] outline-[--$1] inline-flex flex-col justify-start items-start gap-3 overflow-hidden" />
|
||||
<div className="w-60 p-3 left-[18.12px] top-[42.52px] absolute origin-top-left rotate-[-28.34deg] bg-[--Neutrals-NeutralSlate0] rounded-xl shadow-[0px_10px_20px_4px_rgba(14,18,27,0.08)] outline outline-1 outline-offset-[-1px] outline-[--Neutrals-NeutralSlate200] inline-flex flex-col justify-start items-start gap-3 overflow-hidden" />
|
||||
<div className="w-60 p-3 left-[31.44px] top-[22px] absolute origin-top-left rotate-[-28.34deg] bg-[--Neutrals-NeutralSlate0] rounded-xl shadow-[0px_10px_20px_4px_rgba(14,18,27,0.08)] outline outline-1 outline-offset-[-1px] outline-[--Neutrals-NeutralSlate200] inline-flex flex-col justify-start items-start gap-3 overflow-hidden" />
|
||||
</div>
|
||||
<div className="self-stretch p-3 flex flex-col justify-start items-start gap-1">
|
||||
<div className="self-stretch justify-start text-[--Neutrals-NeutralSlate800] text-sm font-semibold font-['Inter'] leading-tight">Build [Company]'s Report</div>
|
||||
<div className="self-stretch justify-start text-[--Neutrals-NeutralSlate500] text-xs font-normal font-['Inter'] leading-none">Share this form with your team members to capture valuable info about your company to train Auditly.</div>
|
||||
<div className="self-stretch justify-start text-[--Text-Gray-800] text-sm font-semibold font-['Inter'] leading-tight">Build [Company]'s Report</div>
|
||||
<div className="self-stretch justify-start text-[--Text-Gray-500] text-xs font-normal font-['Inter'] leading-none">Share this form with your team members to capture valuable info about your company to train Orbitly.</div>
|
||||
</div>
|
||||
<div className="self-stretch px-3 pb-3 flex flex-col justify-start items-start gap-8">
|
||||
<div className="self-stretch inline-flex justify-start items-start gap-2">
|
||||
<div className="flex-1 px-3 py-1.5 bg-Button-Secondary rounded-[999px] flex justify-center items-center gap-0.5 overflow-hidden">
|
||||
<div className="flex-1 px-3 py-1.5 bg-[--Button-Secondary] rounded-[999px] flex justify-center items-center gap-0.5 overflow-hidden">
|
||||
<div className="px-1 flex justify-center items-center">
|
||||
<div className="justify-center text-[--Neutrals-NeutralSlate950] text-sm font-medium font-['Inter'] leading-tight">Invite</div>
|
||||
<div className="justify-center text-[--Text-Gray-950] text-sm font-medium font-['Inter'] leading-tight">Invite</div>
|
||||
</div>
|
||||
<div className="relative">
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8.00004 3.33398V12.6673M3.33337 8.00065H12.6667" stroke="var(--Neutrals-NeutralSlate950, #0A0D12)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<path d="M8.00004 3.33398V12.6673M3.33337 8.00065H12.6667" stroke="var(--Neutrals-NeutralSlate950)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-1 px-3 py-1.5 bg-[--Brand-Orange] rounded-[999px] outline outline-1 outline-offset-[-1px] outline-blue-400 flex justify-center items-center gap-0.5 overflow-hidden">
|
||||
<div className="relative">
|
||||
<svg width="17" height="16" viewBox="0 0 17 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8.97167 12.2423L8.02886 13.1851C6.72711 14.4868 4.61656 14.4868 3.31481 13.1851C2.01306 11.8834 2.01306 9.7728 3.31481 8.47106L4.25762 7.52825M12.7429 8.47106L13.6857 7.52825C14.9875 6.2265 14.9875 4.11595 13.6857 2.8142C12.384 1.51245 10.2734 1.51245 8.97167 2.8142L8.02886 3.75701M6.16693 10.333L10.8336 5.6663" stroke="var(--white, white)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<path d="M8.97167 12.2423L8.02886 13.1851C6.72711 14.4868 4.61656 14.4868 3.31481 13.1851C2.01306 11.8834 2.01306 9.7728 3.31481 8.47106L4.25762 7.52825M12.7429 8.47106L13.6857 7.52825C14.9875 6.2265 14.9875 4.11595 13.6857 2.8142C12.384 1.51245 10.2734 1.51245 8.97167 2.8142L8.02886 3.75701M6.16693 10.333L10.8336 5.6663" stroke="var(--white)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</svg>
|
||||
</div>
|
||||
<div className="px-1 flex justify-center items-center">
|
||||
@@ -147,26 +147,26 @@ const ChatLight: React.FC = () => {
|
||||
<div className="flex-1 self-stretch py-6 bg-[--Neutrals-NeutralSlate0] inline-flex flex-col justify-center items-center gap-2.5">
|
||||
<div className="w-[736px] flex-1 max-w-[736px] pt-48 flex flex-col justify-between items-center">
|
||||
<div className="self-stretch flex flex-col justify-start items-center gap-6">
|
||||
<div className="justify-start text-Text-Gray-800 text-2xl font-medium font-['Neue_Montreal'] leading-normal">What would you like to understand?</div>
|
||||
<div className="justify-start text-[--Text-Gray-800] text-2xl font-medium font-['Neue_Montreal'] leading-normal">What would you like to understand?</div>
|
||||
<div className="p-1 bg-[--Neutrals-NeutralSlate100] rounded-xl inline-flex justify-start items-center gap-1">
|
||||
<div className="px-3 py-1.5 bg-white rounded-lg shadow-[0px_1px_2px_0px_rgba(16,24,40,0.05)] shadow-[inset_0px_-2px_0px_0px_rgba(10,13,18,0.05)] shadow-[inset_0px_0px_0px_1px_rgba(10,13,18,0.18)] flex justify-center items-center gap-1 overflow-hidden">
|
||||
<div className="px-0.5 flex justify-center items-center">
|
||||
<div className="justify-start text-[--Neutrals-NeutralSlate900] text-xs font-medium font-['Inter'] leading-none">Accountability</div>
|
||||
<div className="justify-start text-[--Text-Gray-900] text-xs font-medium font-['Inter'] leading-none">Accountability</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="px-3 py-1.5 rounded-lg shadow-[0px_1px_2px_0px_rgba(16,24,40,0.05)] shadow-[inset_0px_-2px_0px_0px_rgba(10,13,18,0.05)] shadow-[inset_0px_0px_0px_1px_rgba(10,13,18,0.18)] flex justify-center items-center gap-1 overflow-hidden">
|
||||
<div className="px-0.5 flex justify-center items-center">
|
||||
<div className="justify-start text-[--Neutrals-NeutralSlate600] text-xs font-medium font-['Inter'] leading-none">Employee Growth</div>
|
||||
<div className="justify-start text-[--Text-Gray-600] text-xs font-medium font-['Inter'] leading-none">Employee Growth</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="px-3 py-1.5 rounded-lg shadow-[0px_1px_2px_0px_rgba(16,24,40,0.05)] shadow-[inset_0px_-2px_0px_0px_rgba(10,13,18,0.05)] shadow-[inset_0px_0px_0px_1px_rgba(10,13,18,0.18)] flex justify-center items-center gap-1 overflow-hidden">
|
||||
<div className="px-0.5 flex justify-center items-center">
|
||||
<div className="justify-start text-[--Neutrals-NeutralSlate600] text-xs font-medium font-['Inter'] leading-none">Customer Focus</div>
|
||||
<div className="justify-start text-[--Text-Gray-600] text-xs font-medium font-['Inter'] leading-none">Customer Focus</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="px-3 py-1.5 rounded-lg shadow-[0px_1px_2px_0px_rgba(16,24,40,0.05)] shadow-[inset_0px_-2px_0px_0px_rgba(10,13,18,0.05)] shadow-[inset_0px_0px_0px_1px_rgba(10,13,18,0.18)] flex justify-center items-center gap-1 overflow-hidden">
|
||||
<div className="px-0.5 flex justify-center items-center">
|
||||
<div className="justify-start text-[--Neutrals-NeutralSlate600] text-xs font-medium font-['Inter'] leading-none">Teamwork</div>
|
||||
<div className="justify-start text-[--Text-Gray-600] text-xs font-medium font-['Inter'] leading-none">Teamwork</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -180,7 +180,7 @@ const ChatLight: React.FC = () => {
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
<div className="self-stretch justify-start text-Text-Gray-800 text-base font-normal font-['Inter'] leading-normal">How can the company serve them better?</div>
|
||||
<div className="self-stretch justify-start text-[--Text-Gray-800] text-base font-normal font-['Inter'] leading-normal">How can the company serve them better?</div>
|
||||
</div>
|
||||
<div className="flex-1 h-48 px-3 py-4 bg-[--Neutrals-NeutralSlate50] rounded-2xl inline-flex flex-col justify-between items-start overflow-hidden">
|
||||
<div className="relative">
|
||||
@@ -190,7 +190,7 @@ const ChatLight: React.FC = () => {
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
<div className="self-stretch justify-start text-Text-Gray-800 text-base font-normal font-['Inter'] leading-normal">How can the company serve them better?</div>
|
||||
<div className="self-stretch justify-start text-[--Text-Gray-800] text-base font-normal font-['Inter'] leading-normal">How can the company serve them better?</div>
|
||||
</div>
|
||||
<div className="flex-1 h-48 px-3 py-4 bg-[--Neutrals-NeutralSlate50] rounded-2xl inline-flex flex-col justify-between items-start overflow-hidden">
|
||||
<div className="relative">
|
||||
@@ -200,7 +200,7 @@ const ChatLight: React.FC = () => {
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
<div className="self-stretch justify-start text-Text-Gray-800 text-base font-normal font-['Inter'] leading-normal">How can the company serve them better?</div>
|
||||
<div className="self-stretch justify-start text-[--Text-Gray-800] text-base font-normal font-['Inter'] leading-normal">How can the company serve them better?</div>
|
||||
</div>
|
||||
<div className="flex-1 h-48 px-3 py-4 bg-[--Neutrals-NeutralSlate50] rounded-2xl inline-flex flex-col justify-between items-start overflow-hidden">
|
||||
<div className="relative">
|
||||
@@ -210,13 +210,13 @@ const ChatLight: React.FC = () => {
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
<div className="self-stretch justify-start text-Text-Gray-800 text-base font-normal font-['Inter'] leading-normal">How can the company serve them better?</div>
|
||||
<div className="self-stretch justify-start text-[--Text-Gray-800] text-base font-normal font-['Inter'] leading-normal">How can the company serve them better?</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="self-stretch pl-5 pr-3 pt-5 pb-3 bg-[--Neutrals-NeutralSlate50] rounded-3xl flex flex-col justify-start items-start gap-4">
|
||||
<div className="self-stretch justify-start text-Text-Gray-500 text-base font-normal font-['Inter'] leading-normal">Ask anything, use @ to tag staff and ask questions.</div>
|
||||
<div className="self-stretch justify-start text-[--Text-Gray-500] text-base font-normal font-['Inter'] leading-normal">Ask anything, use @ to tag staff and ask questions.</div>
|
||||
<div className="self-stretch inline-flex justify-between items-center">
|
||||
<div className="flex justify-start items-center gap-4">
|
||||
<div className="relative">
|
||||
@@ -253,4 +253,4 @@ const ChatLight: React.FC = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default ChatLight;
|
||||
export default ChatLight;
|
||||
|
||||
@@ -2,7 +2,7 @@ import { API_URL } from '../constants';
|
||||
|
||||
// Get auth token from localStorage (persistent across sessions)
|
||||
const getAuthToken = (): string | null => {
|
||||
return localStorage.getItem('auditly_auth_token');
|
||||
return localStorage.getItem('orbitly_auth_token');
|
||||
};
|
||||
|
||||
// Make authenticated API call
|
||||
@@ -85,4 +85,4 @@ export const apiPut = (endpoint: string, data: any = {}, orgId?: string) =>
|
||||
}, orgId);
|
||||
|
||||
export const apiDelete = (endpoint: string, orgId?: string) =>
|
||||
makeAuthenticatedRequest(endpoint, { method: 'DELETE' }, orgId);
|
||||
makeAuthenticatedRequest(endpoint, { method: 'DELETE' }, orgId);
|
||||
|
||||
@@ -111,7 +111,7 @@ class SecureApiService {
|
||||
|
||||
// Add auth token for authenticated requests
|
||||
if (requireAuth) {
|
||||
const authToken = localStorage.getItem('auditly_auth_token');
|
||||
const authToken = localStorage.getItem('orbitly_auth_token');
|
||||
if (!authToken) {
|
||||
throw new Error('Authentication token not found. Please log in again.');
|
||||
}
|
||||
@@ -134,8 +134,8 @@ class SecureApiService {
|
||||
// Handle auth errors specifically
|
||||
if (requireAuth && (response.status === 401 || response.status === 403)) {
|
||||
// Clear invalid token and redirect to login
|
||||
localStorage.removeItem('auditly_auth_token');
|
||||
localStorage.removeItem('auditly_demo_session');
|
||||
localStorage.removeItem('orbitly_auth_token');
|
||||
localStorage.removeItem('orbitly_demo_session');
|
||||
throw new Error('Authentication failed. Please log in again.');
|
||||
}
|
||||
|
||||
@@ -190,11 +190,11 @@ class SecureApiService {
|
||||
}
|
||||
|
||||
// Submission Methods
|
||||
async getSubmissions(): Promise<GetSubmissions> {
|
||||
async getSubmissions(): Promise<Submission[]> {
|
||||
const response = await this.makeRequest<{ submissions: Submission[] }>(
|
||||
'getSubmissions'
|
||||
);
|
||||
return response;
|
||||
return response.submissions;
|
||||
}
|
||||
|
||||
// Report Methods
|
||||
|
||||
283
src/utils/pdfUtils.ts
Normal file
283
src/utils/pdfUtils.ts
Normal file
@@ -0,0 +1,283 @@
|
||||
import jsPDF from 'jspdf';
|
||||
import html2canvas from 'html2canvas';
|
||||
import type { EmployeeReport, CompanyReport, Employee } from '../types';
|
||||
|
||||
export const downloadCompanyReportPDF = async (companyReport: CompanyReport, companyName: string) => {
|
||||
try {
|
||||
const pdf = new jsPDF('portrait', 'pt', 'a4');
|
||||
const pageWidth = pdf.internal.pageSize.getWidth();
|
||||
const pageHeight = pdf.internal.pageSize.getHeight();
|
||||
const margin = 40;
|
||||
const contentWidth = pageWidth - (2 * margin);
|
||||
|
||||
let yPos = margin;
|
||||
|
||||
// Helper function to add text with wrapping
|
||||
const addWrappedText = (text: string, x: number, y: number, maxWidth: number, fontSize: number = 12, isBold: boolean = false) => {
|
||||
pdf.setFont('helvetica', isBold ? 'bold' : 'normal');
|
||||
pdf.setFontSize(fontSize);
|
||||
|
||||
const lines = pdf.splitTextToSize(text, maxWidth);
|
||||
lines.forEach((line: string, index: number) => {
|
||||
if (y + (index * (fontSize + 4)) > pageHeight - margin) {
|
||||
pdf.addPage();
|
||||
y = margin;
|
||||
}
|
||||
pdf.text(line, x, y + (index * (fontSize + 4)));
|
||||
});
|
||||
|
||||
return y + (lines.length * (fontSize + 4)) + 10;
|
||||
};
|
||||
|
||||
// Header
|
||||
pdf.setFont('helvetica', 'bold');
|
||||
pdf.setFontSize(24);
|
||||
pdf.text(`${companyName} - Company Report`, margin, yPos);
|
||||
yPos += 40;
|
||||
|
||||
// Timestamp
|
||||
pdf.setFont('helvetica', 'normal');
|
||||
pdf.setFontSize(10);
|
||||
pdf.text(`Generated on: ${new Date().toLocaleDateString()}`, margin, yPos);
|
||||
yPos += 30;
|
||||
|
||||
// Company Weaknesses
|
||||
if (companyReport.companyWeaknesses?.length > 0) {
|
||||
yPos = addWrappedText('Company Weaknesses', margin, yPos, contentWidth, 16, true);
|
||||
companyReport.companyWeaknesses.forEach((weakness, index) => {
|
||||
yPos = addWrappedText(`${index + 1}. ${weakness}`, margin + 20, yPos, contentWidth - 20, 12);
|
||||
});
|
||||
yPos += 10;
|
||||
}
|
||||
|
||||
// Personnel Changes
|
||||
if (companyReport.personnelChanges?.length > 0) {
|
||||
yPos = addWrappedText('Personnel Changes', margin, yPos, contentWidth, 16, true);
|
||||
companyReport.personnelChanges.forEach((change, index) => {
|
||||
yPos = addWrappedText(`${index + 1}. ${change}`, margin + 20, yPos, contentWidth - 20, 12);
|
||||
});
|
||||
yPos += 10;
|
||||
}
|
||||
|
||||
// Hiring Needs
|
||||
if (companyReport.hiringNeeds?.length > 0) {
|
||||
yPos = addWrappedText('Immediate Hiring Needs', margin, yPos, contentWidth, 16, true);
|
||||
companyReport.hiringNeeds.forEach((need) => {
|
||||
yPos = addWrappedText(`• ${need.role} - ${need.description}`, margin + 20, yPos, contentWidth - 20, 12);
|
||||
});
|
||||
yPos += 10;
|
||||
}
|
||||
|
||||
// Forward Plan
|
||||
if (companyReport.forwardPlan?.length > 0) {
|
||||
yPos = addWrappedText('Forward Plan', margin, yPos, contentWidth, 16, true);
|
||||
companyReport.forwardPlan.forEach((plan, index) => {
|
||||
yPos = addWrappedText(`Phase ${index + 1}: ${plan}`, margin + 20, yPos, contentWidth - 20, 12);
|
||||
});
|
||||
yPos += 10;
|
||||
}
|
||||
|
||||
// Company Strengths
|
||||
if (companyReport.companyStrengths?.length > 0) {
|
||||
yPos = addWrappedText('Company Strengths', margin, yPos, contentWidth, 16, true);
|
||||
companyReport.companyStrengths.forEach((strength, index) => {
|
||||
yPos = addWrappedText(`${index + 1}. ${strength}`, margin + 20, yPos, contentWidth - 20, 12);
|
||||
});
|
||||
yPos += 10;
|
||||
}
|
||||
|
||||
// Grading Overview
|
||||
if (companyReport.gradingOverview) {
|
||||
yPos = addWrappedText('Grading Overview', margin, yPos, contentWidth, 16, true);
|
||||
|
||||
const grading = companyReport.gradingOverview;
|
||||
yPos = addWrappedText(`Average Performance: ${grading.averagePerformance}/10`, margin + 20, yPos, contentWidth - 20, 12);
|
||||
yPos = addWrappedText(`Team Cohesion: ${grading.teamCohesion}/10`, margin + 20, yPos, contentWidth - 20, 12);
|
||||
yPos = addWrappedText(`Leadership Effectiveness: ${grading.leadershipEffectiveness}/10`, margin + 20, yPos, contentWidth - 20, 12);
|
||||
yPos = addWrappedText(`Innovation Index: ${grading.innovationIndex}/10`, margin + 20, yPos, contentWidth - 20, 12);
|
||||
yPos = addWrappedText(`Overall Grade: ${grading.overallGrade}`, margin + 20, yPos, contentWidth - 20, 12, true);
|
||||
}
|
||||
|
||||
// Save the PDF
|
||||
pdf.save(`${companyName.replace(/\s+/g, '_')}_Company_Report_${new Date().toISOString().split('T')[0]}.pdf`);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error generating company report PDF:', error);
|
||||
alert('Failed to generate PDF. Please try again.');
|
||||
}
|
||||
};
|
||||
|
||||
export const downloadEmployeeReportPDF = async (employee: Employee, employeeReport: EmployeeReport, companyName: string) => {
|
||||
try {
|
||||
const pdf = new jsPDF('portrait', 'pt', 'a4');
|
||||
const pageWidth = pdf.internal.pageSize.getWidth();
|
||||
const pageHeight = pdf.internal.pageSize.getHeight();
|
||||
const margin = 40;
|
||||
const contentWidth = pageWidth - (2 * margin);
|
||||
|
||||
let yPos = margin;
|
||||
|
||||
// Helper function to add text with wrapping
|
||||
const addWrappedText = (text: string, x: number, y: number, maxWidth: number, fontSize: number = 12, isBold: boolean = false) => {
|
||||
pdf.setFont('helvetica', isBold ? 'bold' : 'normal');
|
||||
pdf.setFontSize(fontSize);
|
||||
|
||||
const lines = pdf.splitTextToSize(text, maxWidth);
|
||||
lines.forEach((line: string, index: number) => {
|
||||
if (y + (index * (fontSize + 4)) > pageHeight - margin) {
|
||||
pdf.addPage();
|
||||
y = margin;
|
||||
}
|
||||
pdf.text(line, x, y + (index * (fontSize + 4)));
|
||||
});
|
||||
|
||||
return y + (lines.length * (fontSize + 4)) + 10;
|
||||
};
|
||||
|
||||
// Header
|
||||
pdf.setFont('helvetica', 'bold');
|
||||
pdf.setFontSize(24);
|
||||
pdf.text(`${employee.name} - Employee Report`, margin, yPos);
|
||||
yPos += 40;
|
||||
|
||||
// Employee Details
|
||||
pdf.setFont('helvetica', 'normal');
|
||||
pdf.setFontSize(10);
|
||||
pdf.text(`Generated on: ${new Date().toLocaleDateString()}`, margin, yPos);
|
||||
yPos += 20;
|
||||
pdf.text(`Company: ${companyName}`, margin, yPos);
|
||||
yPos += 20;
|
||||
pdf.text(`Department: ${employee.department || 'Not specified'}`, margin, yPos);
|
||||
yPos += 20;
|
||||
pdf.text(`Role: ${employee.role || 'Not specified'}`, margin, yPos);
|
||||
yPos += 30;
|
||||
|
||||
// Role and Output
|
||||
if (employeeReport.roleAndOutput) {
|
||||
yPos = addWrappedText('Role & Output Analysis', margin, yPos, contentWidth, 16, true);
|
||||
|
||||
if (employeeReport.roleAndOutput.responsibilities) {
|
||||
yPos = addWrappedText('Key Responsibilities:', margin, yPos, contentWidth, 14, true);
|
||||
yPos = addWrappedText(employeeReport.roleAndOutput.responsibilities, margin + 20, yPos, contentWidth - 20, 12);
|
||||
}
|
||||
|
||||
if (employeeReport.roleAndOutput.clarityOnRole) {
|
||||
yPos = addWrappedText('Role Clarity:', margin, yPos, contentWidth, 14, true);
|
||||
yPos = addWrappedText(employeeReport.roleAndOutput.clarityOnRole, margin + 20, yPos, contentWidth - 20, 12);
|
||||
}
|
||||
|
||||
if (employeeReport.roleAndOutput.selfRatedOutput) {
|
||||
yPos = addWrappedText('Self-Rated Output:', margin, yPos, contentWidth, 14, true);
|
||||
yPos = addWrappedText(employeeReport.roleAndOutput.selfRatedOutput, margin + 20, yPos, contentWidth - 20, 12);
|
||||
}
|
||||
|
||||
yPos += 10;
|
||||
}
|
||||
|
||||
// Insights
|
||||
if (employeeReport.insights) {
|
||||
yPos = addWrappedText('Employee Insights', margin, yPos, contentWidth, 16, true);
|
||||
|
||||
if (employeeReport.insights.personalityTraits) {
|
||||
yPos = addWrappedText('Personality Traits:', margin, yPos, contentWidth, 14, true);
|
||||
yPos = addWrappedText(employeeReport.insights.personalityTraits, margin + 20, yPos, contentWidth - 20, 12);
|
||||
}
|
||||
|
||||
if (employeeReport.insights.selfAwareness) {
|
||||
yPos = addWrappedText('Self-Awareness:', margin, yPos, contentWidth, 14, true);
|
||||
yPos = addWrappedText(employeeReport.insights.selfAwareness, margin + 20, yPos, contentWidth - 20, 12);
|
||||
}
|
||||
|
||||
if (employeeReport.insights.growthDesire) {
|
||||
yPos = addWrappedText('Growth Desire:', margin, yPos, contentWidth, 14, true);
|
||||
yPos = addWrappedText(employeeReport.insights.growthDesire, margin + 20, yPos, contentWidth - 20, 12);
|
||||
}
|
||||
|
||||
yPos += 10;
|
||||
}
|
||||
|
||||
// Strengths
|
||||
if (employeeReport.strengths?.length > 0) {
|
||||
yPos = addWrappedText('Strengths', margin, yPos, contentWidth, 16, true);
|
||||
employeeReport.strengths.forEach((strength, index) => {
|
||||
yPos = addWrappedText(`${index + 1}. ${strength}`, margin + 20, yPos, contentWidth - 20, 12);
|
||||
});
|
||||
yPos += 10;
|
||||
}
|
||||
|
||||
// Weaknesses
|
||||
if (employeeReport.weaknesses?.length > 0) {
|
||||
yPos = addWrappedText('Areas for Improvement', margin, yPos, contentWidth, 16, true);
|
||||
employeeReport.weaknesses.forEach((weakness, index) => {
|
||||
yPos = addWrappedText(`${index + 1}. ${weakness}`, margin + 20, yPos, contentWidth - 20, 12);
|
||||
});
|
||||
yPos += 10;
|
||||
}
|
||||
|
||||
// Opportunities
|
||||
if (employeeReport.opportunities?.length > 0) {
|
||||
yPos = addWrappedText('Growth Opportunities', margin, yPos, contentWidth, 16, true);
|
||||
employeeReport.opportunities.forEach((opportunity, index) => {
|
||||
yPos = addWrappedText(`${index + 1}. ${opportunity.title}`, margin + 20, yPos, contentWidth - 20, 12, true);
|
||||
yPos = addWrappedText(opportunity.description, margin + 40, yPos, contentWidth - 40, 11);
|
||||
});
|
||||
yPos += 10;
|
||||
}
|
||||
|
||||
// Recommendations
|
||||
if (employeeReport.recommendations?.length > 0) {
|
||||
yPos = addWrappedText('Recommendations', margin, yPos, contentWidth, 16, true);
|
||||
employeeReport.recommendations.forEach((recommendation, index) => {
|
||||
yPos = addWrappedText(`${index + 1}. ${recommendation}`, margin + 20, yPos, contentWidth - 20, 12);
|
||||
});
|
||||
yPos += 10;
|
||||
}
|
||||
|
||||
// Grading Overview
|
||||
if (employeeReport.gradingOverview) {
|
||||
yPos = addWrappedText('Performance Overview', margin, yPos, contentWidth, 16, true);
|
||||
|
||||
const grading = employeeReport.gradingOverview;
|
||||
yPos = addWrappedText(`Overall Grade: ${grading.grade}`, margin + 20, yPos, contentWidth - 20, 14, true);
|
||||
yPos = addWrappedText(`Scalability: ${grading.scalability}/5`, margin + 20, yPos, contentWidth - 20, 12);
|
||||
yPos = addWrappedText(`Reliability: ${grading.reliability}/5`, margin + 20, yPos, contentWidth - 20, 12);
|
||||
yPos = addWrappedText(`Role Fit: ${grading.roleFit}/5`, margin + 20, yPos, contentWidth - 20, 12);
|
||||
yPos = addWrappedText(`Output Quality: ${grading.output}/5`, margin + 20, yPos, contentWidth - 20, 12);
|
||||
yPos = addWrappedText(`Initiative: ${grading.initiative}/10`, margin + 20, yPos, contentWidth - 20, 12);
|
||||
}
|
||||
|
||||
// Try to capture performance chart if it exists
|
||||
const chartElement = document.querySelector('[data-chart="employee-performance"]');
|
||||
if (chartElement) {
|
||||
try {
|
||||
const canvas = await html2canvas(chartElement as HTMLElement, {
|
||||
backgroundColor: '#ffffff',
|
||||
scale: 2
|
||||
});
|
||||
|
||||
const imgData = canvas.toDataURL('image/png');
|
||||
const imgWidth = 300;
|
||||
const imgHeight = (canvas.height * imgWidth) / canvas.width;
|
||||
|
||||
// Check if we need a new page for the chart
|
||||
if (yPos + imgHeight > pageHeight - margin) {
|
||||
pdf.addPage();
|
||||
yPos = margin;
|
||||
}
|
||||
|
||||
yPos = addWrappedText('Performance Chart', margin, yPos, contentWidth, 16, true);
|
||||
pdf.addImage(imgData, 'PNG', margin, yPos, imgWidth, imgHeight);
|
||||
} catch (chartError) {
|
||||
console.warn('Could not capture performance chart:', chartError);
|
||||
}
|
||||
}
|
||||
|
||||
// Save the PDF
|
||||
const fileName = `${employee.name.replace(/\s+/g, '_')}_Employee_Report_${new Date().toISOString().split('T')[0]}.pdf`;
|
||||
pdf.save(fileName);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error generating employee report PDF:', error);
|
||||
alert('Failed to generate PDF. Please try again.');
|
||||
}
|
||||
};
|
||||
@@ -5,7 +5,7 @@ export default {
|
||||
"./**/*.{js,ts,jsx,tsx}",
|
||||
"./*.{js,ts,jsx,tsx,html,css}"
|
||||
],
|
||||
darkMode: 'class',
|
||||
darkMode: 'media',
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
@@ -25,6 +25,19 @@ export default {
|
||||
'Neutrals-NeutralSlate800': 'var(--Neutrals-NeutralSlate800)',
|
||||
'Neutrals-NeutralSlate900': 'var(--Neutrals-NeutralSlate900)',
|
||||
'Neutrals-NeutralSlate950': 'var(--Neutrals-NeutralSlate950)',
|
||||
'Text-White-00': 'var(--Text-White-00)',
|
||||
'Text-Gray-100': 'var(--Text-Gray-100)',
|
||||
'Text-Gray-200': 'var(--Text-Gray-200)',
|
||||
'Text-Gray-300': 'var(--Text-Gray-300)',
|
||||
'Text-Gray-400': 'var(--Text-Gray-400)',
|
||||
'Text-Gray-500': 'var(--Text-Gray-500)',
|
||||
'Text-Gray-600': 'var(--Text-Gray-600)',
|
||||
'Text-Gray-700': 'var(--Text-Gray-700)',
|
||||
'Text-Gray-800': 'var(--Text-Gray-800)',
|
||||
'Text-Dark-950': 'var(--Text-Dark-950)',
|
||||
'Outline-Outline-Gray-200': 'var(--Outline-Outline-Gray-200)',
|
||||
'Button-Secondary': 'var(--Button-Secondary)',
|
||||
'red-200': 'var(--red-200)',
|
||||
|
||||
// Figma Other colors
|
||||
'Other-White': 'var(--Other-White)',
|
||||
|
||||
Reference in New Issue
Block a user