Spaces:
Running
Add Onboarding tour (#48)
Browse files* Upgraded onboarding experience by adding OnboardingTour component and related configuration.Using Reactour each indivual component is highlighted. Updated package.json to include @reactour/tour for onboarding functionality. Introduced TESTING_ONBOARDING flag in App.js to control tour visibility during development this allows for quick and easy changes.
* Refactor onboarding logic to utilize environment variable for TESTING_ONBOARDING flag in App.js. Enhanced OnboardingTour component by integrating dynamic feature and advisor data from AppConfigContext, improving the onboarding experience with customizable content based on backend configuration.
* Add onboarding and canvas configurations to YAML files and backend models
- Introduced onboarding features for both PhD and undergraduate configurations, including AI advisor advice, conversation saving, document uploads, and progress tracking.
- Added canvas configurations with tour titles and descriptions for PhD and undergraduate journeys.
- Updated backend models to include new onboarding and canvas configurations, ensuring they are integrated into the application settings.
* Pass `REACT_APP_TESTING_ONBOARDING` envvar to frontend container
* Rebase Complete
* Update App.js
* Refactor onboarding configuration by removing canvas settings
- Removed the CanvasConfig from the onboarding configuration in both the YAML and backend model files.
- Updated the OnboardingTour component to reference the onboarding configuration for tour titles and bodies instead of the removed canvas settings, streamlining the onboarding experience.
---------
Co-authored-by: Daniel McKnight <daniel@neon.ai>
- docker-compose.yml +1 -0
- multi_llm_chatbot_backend/app/config.py +8 -0
- phd-advisor-frontend/package-lock.json +67 -31
- phd-advisor-frontend/package.json +1 -0
- phd-advisor-frontend/src/App.js +5 -0
- phd-advisor-frontend/src/components/OnboardingTour.js +320 -0
- phd-advisor-frontend/src/pages/ChatPage.js +3 -0
- phd-advisor-frontend/src/styles/OnboardingTour.css +12 -0
- phd_config.yaml +13 -0
- undergrad_config.yaml +15 -0
|
@@ -32,6 +32,7 @@ services:
|
|
| 32 |
- backend
|
| 33 |
environment:
|
| 34 |
REACT_APP_API_URL: ${REACT_APP_API_URL:-http://localhost:8000}
|
|
|
|
| 35 |
database:
|
| 36 |
image: mongo:8.0
|
| 37 |
volumes:
|
|
|
|
| 32 |
- backend
|
| 33 |
environment:
|
| 34 |
REACT_APP_API_URL: ${REACT_APP_API_URL:-http://localhost:8000}
|
| 35 |
+
REACT_APP_TESTING_ONBOARDING: ${REACT_APP_TESTING_ONBOARDING:-false}
|
| 36 |
database:
|
| 37 |
image: mongo:8.0
|
| 38 |
volumes:
|
|
@@ -88,6 +88,12 @@ class ChatPageConfig(BaseModel):
|
|
| 88 |
examples: List[ExampleCategory] = []
|
| 89 |
|
| 90 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 91 |
class PersonaItemConfig(_IconValidatorMixin):
|
| 92 |
id: str
|
| 93 |
name: str
|
|
@@ -305,6 +311,7 @@ class AppSettings(BaseModel):
|
|
| 305 |
homepage: HomepageConfig = HomepageConfig()
|
| 306 |
login: LoginConfig = LoginConfig()
|
| 307 |
chat_page: ChatPageConfig = ChatPageConfig()
|
|
|
|
| 308 |
personas: PersonasConfig = PersonasConfig()
|
| 309 |
orchestrator: OrchestratorConfig = OrchestratorConfig()
|
| 310 |
auth: AuthConfig = AuthConfig()
|
|
@@ -326,6 +333,7 @@ class AppSettings(BaseModel):
|
|
| 326 |
"homepage": self.homepage.dict(),
|
| 327 |
"login": self.login.dict(),
|
| 328 |
"chat_page": self.chat_page.dict(),
|
|
|
|
| 329 |
"personas": {
|
| 330 |
"items": [p.to_frontend_config() for p in self.personas.items],
|
| 331 |
},
|
|
|
|
| 88 |
examples: List[ExampleCategory] = []
|
| 89 |
|
| 90 |
|
| 91 |
+
class OnboardingConfig(BaseModel):
|
| 92 |
+
features: List[FeatureConfig] = []
|
| 93 |
+
tour_title: str = ""
|
| 94 |
+
tour_body: str = ""
|
| 95 |
+
|
| 96 |
+
|
| 97 |
class PersonaItemConfig(_IconValidatorMixin):
|
| 98 |
id: str
|
| 99 |
name: str
|
|
|
|
| 311 |
homepage: HomepageConfig = HomepageConfig()
|
| 312 |
login: LoginConfig = LoginConfig()
|
| 313 |
chat_page: ChatPageConfig = ChatPageConfig()
|
| 314 |
+
onboarding: OnboardingConfig = OnboardingConfig()
|
| 315 |
personas: PersonasConfig = PersonasConfig()
|
| 316 |
orchestrator: OrchestratorConfig = OrchestratorConfig()
|
| 317 |
auth: AuthConfig = AuthConfig()
|
|
|
|
| 333 |
"homepage": self.homepage.dict(),
|
| 334 |
"login": self.login.dict(),
|
| 335 |
"chat_page": self.chat_page.dict(),
|
| 336 |
+
"onboarding": self.onboarding.dict(),
|
| 337 |
"personas": {
|
| 338 |
"items": [p.to_frontend_config() for p in self.personas.items],
|
| 339 |
},
|
|
@@ -8,6 +8,7 @@
|
|
| 8 |
"name": "phd-advisor-frontend",
|
| 9 |
"version": "0.1.0",
|
| 10 |
"dependencies": {
|
|
|
|
| 11 |
"@testing-library/dom": "^10.4.0",
|
| 12 |
"@testing-library/jest-dom": "^6.6.3",
|
| 13 |
"@testing-library/react": "^16.3.0",
|
|
@@ -3090,6 +3091,57 @@
|
|
| 3090 |
}
|
| 3091 |
}
|
| 3092 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3093 |
"node_modules/@rollup/plugin-babel": {
|
| 3094 |
"version": "5.3.1",
|
| 3095 |
"resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz",
|
|
@@ -3169,6 +3221,15 @@
|
|
| 3169 |
"integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==",
|
| 3170 |
"license": "MIT"
|
| 3171 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3172 |
"node_modules/@rtsao/scc": {
|
| 3173 |
"version": "1.1.0",
|
| 3174 |
"resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz",
|
|
@@ -3866,16 +3927,6 @@
|
|
| 3866 |
"integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==",
|
| 3867 |
"license": "MIT"
|
| 3868 |
},
|
| 3869 |
-
"node_modules/@types/react": {
|
| 3870 |
-
"version": "19.1.9",
|
| 3871 |
-
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.9.tgz",
|
| 3872 |
-
"integrity": "sha512-WmdoynAX8Stew/36uTSVMcLJJ1KRh6L3IZRx1PZ7qJtBqT3dYTgyDTx8H1qoRghErydW7xw9mSJ3wS//tCRpFA==",
|
| 3873 |
-
"license": "MIT",
|
| 3874 |
-
"peer": true,
|
| 3875 |
-
"dependencies": {
|
| 3876 |
-
"csstype": "^3.0.2"
|
| 3877 |
-
}
|
| 3878 |
-
},
|
| 3879 |
"node_modules/@types/resolve": {
|
| 3880 |
"version": "1.17.1",
|
| 3881 |
"resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz",
|
|
@@ -6486,13 +6537,6 @@
|
|
| 6486 |
"integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==",
|
| 6487 |
"license": "MIT"
|
| 6488 |
},
|
| 6489 |
-
"node_modules/csstype": {
|
| 6490 |
-
"version": "3.1.3",
|
| 6491 |
-
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
|
| 6492 |
-
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
|
| 6493 |
-
"license": "MIT",
|
| 6494 |
-
"peer": true
|
| 6495 |
-
},
|
| 6496 |
"node_modules/damerau-levenshtein": {
|
| 6497 |
"version": "1.0.8",
|
| 6498 |
"resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz",
|
|
@@ -15563,6 +15607,12 @@
|
|
| 15563 |
"integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
|
| 15564 |
"license": "MIT"
|
| 15565 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 15566 |
"node_modules/resolve": {
|
| 15567 |
"version": "1.22.10",
|
| 15568 |
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
|
|
@@ -17704,20 +17754,6 @@
|
|
| 17704 |
"is-typedarray": "^1.0.0"
|
| 17705 |
}
|
| 17706 |
},
|
| 17707 |
-
"node_modules/typescript": {
|
| 17708 |
-
"version": "4.9.5",
|
| 17709 |
-
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
|
| 17710 |
-
"integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
|
| 17711 |
-
"license": "Apache-2.0",
|
| 17712 |
-
"peer": true,
|
| 17713 |
-
"bin": {
|
| 17714 |
-
"tsc": "bin/tsc",
|
| 17715 |
-
"tsserver": "bin/tsserver"
|
| 17716 |
-
},
|
| 17717 |
-
"engines": {
|
| 17718 |
-
"node": ">=4.2.0"
|
| 17719 |
-
}
|
| 17720 |
-
},
|
| 17721 |
"node_modules/unbox-primitive": {
|
| 17722 |
"version": "1.1.0",
|
| 17723 |
"resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz",
|
|
|
|
| 8 |
"name": "phd-advisor-frontend",
|
| 9 |
"version": "0.1.0",
|
| 10 |
"dependencies": {
|
| 11 |
+
"@reactour/tour": "^3.8.0",
|
| 12 |
"@testing-library/dom": "^10.4.0",
|
| 13 |
"@testing-library/jest-dom": "^6.6.3",
|
| 14 |
"@testing-library/react": "^16.3.0",
|
|
|
|
| 3091 |
}
|
| 3092 |
}
|
| 3093 |
},
|
| 3094 |
+
"node_modules/@reactour/mask": {
|
| 3095 |
+
"version": "1.2.0",
|
| 3096 |
+
"resolved": "https://registry.npmjs.org/@reactour/mask/-/mask-1.2.0.tgz",
|
| 3097 |
+
"integrity": "sha512-XLgBLWfKJybtZjNTSO5lt/SIvRlCZBadB6JfE/hO1ErqURRjYhnv+edC0Ki1haUCqMGFppWk3lwcPCjmK0xNog==",
|
| 3098 |
+
"license": "MIT",
|
| 3099 |
+
"dependencies": {
|
| 3100 |
+
"@reactour/utils": "*"
|
| 3101 |
+
},
|
| 3102 |
+
"peerDependencies": {
|
| 3103 |
+
"react": "16.x || 17.x || 18.x || 19.x"
|
| 3104 |
+
}
|
| 3105 |
+
},
|
| 3106 |
+
"node_modules/@reactour/popover": {
|
| 3107 |
+
"version": "1.3.0",
|
| 3108 |
+
"resolved": "https://registry.npmjs.org/@reactour/popover/-/popover-1.3.0.tgz",
|
| 3109 |
+
"integrity": "sha512-YdyjSmHPvEeQEcJM4gcGFa5pI/Yf4nZGqwG4JnT+rK1SyUJBIPnm4Gkl/h7/+1g0KCFMkwNwagS3ZiXvZB7ThA==",
|
| 3110 |
+
"license": "MIT",
|
| 3111 |
+
"dependencies": {
|
| 3112 |
+
"@reactour/utils": "*"
|
| 3113 |
+
},
|
| 3114 |
+
"peerDependencies": {
|
| 3115 |
+
"react": "16.x || 17.x || 18.x || 19.x"
|
| 3116 |
+
}
|
| 3117 |
+
},
|
| 3118 |
+
"node_modules/@reactour/tour": {
|
| 3119 |
+
"version": "3.8.0",
|
| 3120 |
+
"resolved": "https://registry.npmjs.org/@reactour/tour/-/tour-3.8.0.tgz",
|
| 3121 |
+
"integrity": "sha512-KZTFi1pAvoTVKKRdBN5+XCYxXBp4k4Ql/acZcXyPvec8VU24fkMSEeV+v8krfYQpoVcewxIu3gM6xWZZLjxi7w==",
|
| 3122 |
+
"license": "MIT",
|
| 3123 |
+
"dependencies": {
|
| 3124 |
+
"@reactour/mask": "*",
|
| 3125 |
+
"@reactour/popover": "*",
|
| 3126 |
+
"@reactour/utils": "*"
|
| 3127 |
+
},
|
| 3128 |
+
"peerDependencies": {
|
| 3129 |
+
"react": "16.x || 17.x || 18.x || 19.x"
|
| 3130 |
+
}
|
| 3131 |
+
},
|
| 3132 |
+
"node_modules/@reactour/utils": {
|
| 3133 |
+
"version": "0.6.0",
|
| 3134 |
+
"resolved": "https://registry.npmjs.org/@reactour/utils/-/utils-0.6.0.tgz",
|
| 3135 |
+
"integrity": "sha512-GqaLjQi7MJsgtAKjdiw2Eak1toFkADoLRnm1+HZpaD+yl+DkaHpC1N7JAl+kVOO5I17bWInPA+OFbXjO9Co8Qg==",
|
| 3136 |
+
"license": "MIT",
|
| 3137 |
+
"dependencies": {
|
| 3138 |
+
"@rooks/use-mutation-observer": "^4.11.2",
|
| 3139 |
+
"resize-observer-polyfill": "^1.5.1"
|
| 3140 |
+
},
|
| 3141 |
+
"peerDependencies": {
|
| 3142 |
+
"react": "16.x || 17.x || 18.x || 19.x"
|
| 3143 |
+
}
|
| 3144 |
+
},
|
| 3145 |
"node_modules/@rollup/plugin-babel": {
|
| 3146 |
"version": "5.3.1",
|
| 3147 |
"resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz",
|
|
|
|
| 3221 |
"integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==",
|
| 3222 |
"license": "MIT"
|
| 3223 |
},
|
| 3224 |
+
"node_modules/@rooks/use-mutation-observer": {
|
| 3225 |
+
"version": "4.11.2",
|
| 3226 |
+
"resolved": "https://registry.npmjs.org/@rooks/use-mutation-observer/-/use-mutation-observer-4.11.2.tgz",
|
| 3227 |
+
"integrity": "sha512-vpsdrZdr6TkB1zZJcHx+fR1YC/pHs2BaqcuYiEGjBVbwY5xcC49+h0hAUtQKHth3oJqXfIX/Ng8S7s5HFHdM/A==",
|
| 3228 |
+
"license": "MIT",
|
| 3229 |
+
"peerDependencies": {
|
| 3230 |
+
"react": ">=16.8.0"
|
| 3231 |
+
}
|
| 3232 |
+
},
|
| 3233 |
"node_modules/@rtsao/scc": {
|
| 3234 |
"version": "1.1.0",
|
| 3235 |
"resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz",
|
|
|
|
| 3927 |
"integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==",
|
| 3928 |
"license": "MIT"
|
| 3929 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3930 |
"node_modules/@types/resolve": {
|
| 3931 |
"version": "1.17.1",
|
| 3932 |
"resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz",
|
|
|
|
| 6537 |
"integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==",
|
| 6538 |
"license": "MIT"
|
| 6539 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6540 |
"node_modules/damerau-levenshtein": {
|
| 6541 |
"version": "1.0.8",
|
| 6542 |
"resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz",
|
|
|
|
| 15607 |
"integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
|
| 15608 |
"license": "MIT"
|
| 15609 |
},
|
| 15610 |
+
"node_modules/resize-observer-polyfill": {
|
| 15611 |
+
"version": "1.5.1",
|
| 15612 |
+
"resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz",
|
| 15613 |
+
"integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==",
|
| 15614 |
+
"license": "MIT"
|
| 15615 |
+
},
|
| 15616 |
"node_modules/resolve": {
|
| 15617 |
"version": "1.22.10",
|
| 15618 |
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
|
|
|
|
| 17754 |
"is-typedarray": "^1.0.0"
|
| 17755 |
}
|
| 17756 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 17757 |
"node_modules/unbox-primitive": {
|
| 17758 |
"version": "1.1.0",
|
| 17759 |
"resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz",
|
|
@@ -10,6 +10,7 @@
|
|
| 10 |
"lucide-react": "^0.544.0",
|
| 11 |
"react": "^19.1.0",
|
| 12 |
"react-dom": "^19.1.0",
|
|
|
|
| 13 |
"react-markdown": "^10.1.0",
|
| 14 |
"react-scripts": "5.0.1",
|
| 15 |
"remark-gfm": "^4.0.1",
|
|
|
|
| 10 |
"lucide-react": "^0.544.0",
|
| 11 |
"react": "^19.1.0",
|
| 12 |
"react-dom": "^19.1.0",
|
| 13 |
+
"@reactour/tour": "^3.8.0",
|
| 14 |
"react-markdown": "^10.1.0",
|
| 15 |
"react-scripts": "5.0.1",
|
| 16 |
"remark-gfm": "^4.0.1",
|
|
@@ -8,6 +8,11 @@ import CanvasPage from './pages/CanvasPage';
|
|
| 8 |
import UserGuide from './components/UserGuide';
|
| 9 |
import './styles/components.css';
|
| 10 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 11 |
function App() {
|
| 12 |
const [currentView, setCurrentView] = useState('home');
|
| 13 |
const [isAuthenticated, setIsAuthenticated] = useState(false);
|
|
|
|
| 8 |
import UserGuide from './components/UserGuide';
|
| 9 |
import './styles/components.css';
|
| 10 |
|
| 11 |
+
// Set REACT_APP_TESTING_ONBOARDING=true in your .env to force the onboarding
|
| 12 |
+
// tour to run on every page load. Leave unset in production — tour will only
|
| 13 |
+
// show once per user (localStorage).
|
| 14 |
+
export const TESTING_ONBOARDING = process.env.REACT_APP_TESTING_ONBOARDING === 'true';
|
| 15 |
+
|
| 16 |
function App() {
|
| 17 |
const [currentView, setCurrentView] = useState('home');
|
| 18 |
const [isAuthenticated, setIsAuthenticated] = useState(false);
|
|
@@ -0,0 +1,320 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import React, { useEffect, useMemo } from 'react';
|
| 2 |
+
import { TourProvider, useTour } from '@reactour/tour';
|
| 3 |
+
import { Hand, GraduationCap, Plus, MessageCircle, Paperclip, BarChart3 } from 'lucide-react';
|
| 4 |
+
import { useTheme } from '../contexts/ThemeContext';
|
| 5 |
+
import { useAppConfig } from '../contexts/AppConfigContext';
|
| 6 |
+
import { TESTING_ONBOARDING } from '../App';
|
| 7 |
+
import '../styles/OnboardingTour.css';
|
| 8 |
+
|
| 9 |
+
const STORAGE_KEY = 'hasSeenOnboardingTour';
|
| 10 |
+
|
| 11 |
+
// Fallbacks used when config.onboarding.* fields aren't provided by the backend.
|
| 12 |
+
const DEFAULT_FEATURES = [
|
| 13 |
+
{ Icon: GraduationCap, label: 'Get advice from specialized AI advisors' },
|
| 14 |
+
{ Icon: MessageCircle, label: 'Save and revisit every conversation' },
|
| 15 |
+
{ Icon: Paperclip, label: 'Upload PDFs and documents for context-aware answers' },
|
| 16 |
+
{ Icon: BarChart3, label: 'Track your progress on a structured canvas' },
|
| 17 |
+
];
|
| 18 |
+
const DEFAULT_CANVAS_STEP = {
|
| 19 |
+
title: 'PhD Progress Canvas',
|
| 20 |
+
body: 'A dashboard view of your PhD journey — research progress, methodology, next steps, all in one place.',
|
| 21 |
+
};
|
| 22 |
+
|
| 23 |
+
const buildAdvisorBody = (advisors) => {
|
| 24 |
+
const names = Object.values(advisors || {}).map((a) => a.name).filter(Boolean);
|
| 25 |
+
if (names.length === 0) {
|
| 26 |
+
return "AI personas are ready to help. Click here anytime to see who's available.";
|
| 27 |
+
}
|
| 28 |
+
const firstThree = names.slice(0, 3).join(', ');
|
| 29 |
+
return `${names.length} AI personas are ready to help — ${firstThree}, and more. Click here anytime to see who's available.`;
|
| 30 |
+
};
|
| 31 |
+
|
| 32 |
+
const buildFeatures = (config, resolveIcon) => {
|
| 33 |
+
const fromConfig = config?.onboarding?.features;
|
| 34 |
+
if (!Array.isArray(fromConfig) || fromConfig.length === 0) return DEFAULT_FEATURES;
|
| 35 |
+
return fromConfig.map((f) => ({
|
| 36 |
+
Icon: f.icon ? resolveIcon(f.icon) : GraduationCap,
|
| 37 |
+
label: f.label || f.description || f.title || '',
|
| 38 |
+
}));
|
| 39 |
+
};
|
| 40 |
+
|
| 41 |
+
// Theme-resolved colors that match the rest of the app exactly
|
| 42 |
+
const palette = (isDark) => ({
|
| 43 |
+
bg: isDark ? '#1F2937' : '#FFFFFF',
|
| 44 |
+
bgSubtle: isDark ? '#111827' : '#F9FAFB',
|
| 45 |
+
text: isDark ? '#F9FAFB' : '#111827',
|
| 46 |
+
textMuted: isDark ? '#D1D5DB' : '#6B7280',
|
| 47 |
+
textDim: isDark ? '#9CA3AF' : '#9CA3AF',
|
| 48 |
+
border: isDark ? '#374151' : '#E5E7EB',
|
| 49 |
+
accent: '#2663EB',
|
| 50 |
+
accentGrad: '#2663EB',
|
| 51 |
+
accentShadow: 'rgba(38, 99, 235, 0.45)',
|
| 52 |
+
});
|
| 53 |
+
|
| 54 |
+
// --- Step content -------------------------------------------------------
|
| 55 |
+
const titleStyle = (c) => ({
|
| 56 |
+
margin: 0, fontSize: 22, fontWeight: 700, lineHeight: 1.2,
|
| 57 |
+
color: c.text, WebkitTextFillColor: c.text, letterSpacing: '-0.02em',
|
| 58 |
+
});
|
| 59 |
+
const bodyStyle = (c) => ({
|
| 60 |
+
margin: 0, fontSize: 15.5, lineHeight: 1.55,
|
| 61 |
+
color: c.textMuted, WebkitTextFillColor: c.textMuted,
|
| 62 |
+
});
|
| 63 |
+
|
| 64 |
+
const StepBody = ({ title, body, Icon, c }) => (
|
| 65 |
+
<div style={{ fontFamily: 'system-ui, -apple-system, sans-serif' }}>
|
| 66 |
+
<div style={{ display: 'flex', alignItems: 'center', gap: 14, marginBottom: 14 }}>
|
| 67 |
+
<div style={{
|
| 68 |
+
width: 48, height: 48, borderRadius: 14,
|
| 69 |
+
background: c.accent,
|
| 70 |
+
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
| 71 |
+
flexShrink: 0,
|
| 72 |
+
boxShadow: `0 6px 16px -4px ${c.accentShadow}`,
|
| 73 |
+
}}>
|
| 74 |
+
<Icon size={24} color="#fff" strokeWidth={2.2} />
|
| 75 |
+
</div>
|
| 76 |
+
<div style={titleStyle(c)}>{title}</div>
|
| 77 |
+
</div>
|
| 78 |
+
<div style={bodyStyle(c)}>{body}</div>
|
| 79 |
+
</div>
|
| 80 |
+
);
|
| 81 |
+
|
| 82 |
+
const WelcomeBody = ({ c, title, subtitle, features }) => {
|
| 83 |
+
return (
|
| 84 |
+
<div style={{ fontFamily: 'system-ui, -apple-system, sans-serif', textAlign: 'center', padding: '4px 8px' }}>
|
| 85 |
+
<div style={{
|
| 86 |
+
width: 72, height: 72, borderRadius: 20, margin: '0 auto 20px',
|
| 87 |
+
background: c.accent,
|
| 88 |
+
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
| 89 |
+
boxShadow: `0 12px 32px -8px ${c.accentShadow}`,
|
| 90 |
+
}}>
|
| 91 |
+
<Hand size={36} color="#fff" strokeWidth={2.2} />
|
| 92 |
+
</div>
|
| 93 |
+
<div style={{
|
| 94 |
+
margin: '0 0 12px', fontSize: 30, fontWeight: 800, lineHeight: 1.15,
|
| 95 |
+
color: c.text, WebkitTextFillColor: c.text, letterSpacing: '-0.025em',
|
| 96 |
+
}}>
|
| 97 |
+
Welcome to your<br />{title}
|
| 98 |
+
</div>
|
| 99 |
+
<div style={{
|
| 100 |
+
margin: '0 0 22px', fontSize: 16, lineHeight: 1.55,
|
| 101 |
+
color: c.textMuted, WebkitTextFillColor: c.textMuted,
|
| 102 |
+
}}>
|
| 103 |
+
{subtitle}
|
| 104 |
+
</div>
|
| 105 |
+
<div style={{
|
| 106 |
+
display: 'grid', gap: 14, textAlign: 'left',
|
| 107 |
+
background: c.bgSubtle,
|
| 108 |
+
border: `1px solid ${c.border}`,
|
| 109 |
+
borderRadius: 14, padding: '18px 20px',
|
| 110 |
+
}}>
|
| 111 |
+
{features.map(({ Icon, label }) => (
|
| 112 |
+
<div key={label} style={{ display: 'flex', alignItems: 'center', gap: 14 }}>
|
| 113 |
+
<div style={{
|
| 114 |
+
width: 32, height: 32, borderRadius: 8,
|
| 115 |
+
background: c.accent,
|
| 116 |
+
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
| 117 |
+
flexShrink: 0,
|
| 118 |
+
}}>
|
| 119 |
+
<Icon size={18} color="#fff" strokeWidth={2.2} />
|
| 120 |
+
</div>
|
| 121 |
+
<span style={{ fontSize: 15, color: c.text, lineHeight: 1.4 }}>
|
| 122 |
+
{label}
|
| 123 |
+
</span>
|
| 124 |
+
</div>
|
| 125 |
+
))}
|
| 126 |
+
</div>
|
| 127 |
+
</div>
|
| 128 |
+
);
|
| 129 |
+
};
|
| 130 |
+
|
| 131 |
+
const buildSteps = (c, data) => [
|
| 132 |
+
{
|
| 133 |
+
selector: 'body',
|
| 134 |
+
position: 'center',
|
| 135 |
+
content: <WelcomeBody c={c} title={data.title} subtitle={data.subtitle} features={data.features} />,
|
| 136 |
+
styles: {
|
| 137 |
+
maskArea: (base) => ({ ...base, x: -10000, y: -10000, width: 0, height: 0 }),
|
| 138 |
+
popover: (base) => ({
|
| 139 |
+
...base,
|
| 140 |
+
maxWidth: 540,
|
| 141 |
+
minWidth: 460,
|
| 142 |
+
padding: '32px 30px 24px',
|
| 143 |
+
borderRadius: 22,
|
| 144 |
+
background: c.bg,
|
| 145 |
+
backgroundColor: c.bg,
|
| 146 |
+
color: c.text,
|
| 147 |
+
border: `1px solid ${c.border}`,
|
| 148 |
+
boxShadow: '0 30px 80px -10px rgba(0,0,0,0.5)',
|
| 149 |
+
}),
|
| 150 |
+
},
|
| 151 |
+
},
|
| 152 |
+
{
|
| 153 |
+
selector: '.advisor-status-button',
|
| 154 |
+
content: <StepBody c={c} Icon={GraduationCap} title="Meet your advisors" body={data.advisorBody} />,
|
| 155 |
+
},
|
| 156 |
+
{
|
| 157 |
+
selector: '.new-chat-button',
|
| 158 |
+
content: <StepBody c={c} Icon={Plus} title="Start a new chat" body="Begin a fresh conversation. Each chat is saved automatically so you can return to it later." />,
|
| 159 |
+
},
|
| 160 |
+
{
|
| 161 |
+
selector: '.sessions-list',
|
| 162 |
+
content: <StepBody c={c} Icon={MessageCircle} title="Your past chats" body="All your previous conversations live here. Click any session to pick up where you left off." />,
|
| 163 |
+
},
|
| 164 |
+
{
|
| 165 |
+
selector: '.enhanced-chat-input-container',
|
| 166 |
+
content: <StepBody c={c} Icon={Paperclip} title="Ask anything" body="Type a question, attach a PDF or document for context, and your advisors will respond with diverse perspectives." />,
|
| 167 |
+
},
|
| 168 |
+
{
|
| 169 |
+
selector: '.sidebar-canvas-btn',
|
| 170 |
+
content: <StepBody c={c} Icon={BarChart3} title={data.canvas.title} body={data.canvas.body} />,
|
| 171 |
+
},
|
| 172 |
+
];
|
| 173 |
+
|
| 174 |
+
// --- Custom Buttons -----------------------------------------------------
|
| 175 |
+
const makeButtons = (c) => {
|
| 176 |
+
const ghostBtn = {
|
| 177 |
+
background: 'transparent', color: c.textMuted,
|
| 178 |
+
border: `1px solid ${c.border}`, padding: '10px 18px',
|
| 179 |
+
borderRadius: 10, fontSize: 14, fontWeight: 600, cursor: 'pointer',
|
| 180 |
+
};
|
| 181 |
+
const primaryBtn = {
|
| 182 |
+
background: c.accent, color: '#fff',
|
| 183 |
+
border: 'none', padding: '10px 22px',
|
| 184 |
+
borderRadius: 10, fontSize: 14, fontWeight: 600, cursor: 'pointer',
|
| 185 |
+
boxShadow: `0 4px 14px -4px ${c.accentShadow}`,
|
| 186 |
+
};
|
| 187 |
+
|
| 188 |
+
const PrevButton = ({ currentStep, setCurrentStep }) => {
|
| 189 |
+
if (currentStep === 0) return <span />;
|
| 190 |
+
return (
|
| 191 |
+
<button style={ghostBtn} onClick={() => setCurrentStep(currentStep - 1)}>Back</button>
|
| 192 |
+
);
|
| 193 |
+
};
|
| 194 |
+
|
| 195 |
+
const NextButton = ({ currentStep, stepsLength, setCurrentStep, setIsOpen }) => {
|
| 196 |
+
const isFirst = currentStep === 0;
|
| 197 |
+
const isLast = currentStep === stepsLength - 1;
|
| 198 |
+
const label = isFirst ? 'Begin tour' : isLast ? 'Get started' : 'Next';
|
| 199 |
+
const padded = isFirst ? { ...primaryBtn, padding: '12px 28px', fontSize: 15 } : primaryBtn;
|
| 200 |
+
return (
|
| 201 |
+
<button
|
| 202 |
+
style={padded}
|
| 203 |
+
onClick={() => (isLast ? setIsOpen(false) : setCurrentStep(currentStep + 1))}
|
| 204 |
+
>
|
| 205 |
+
{label}
|
| 206 |
+
</button>
|
| 207 |
+
);
|
| 208 |
+
};
|
| 209 |
+
|
| 210 |
+
const SkipButton = ({ onClick }) => (
|
| 211 |
+
<button
|
| 212 |
+
onClick={onClick}
|
| 213 |
+
style={{
|
| 214 |
+
position: 'absolute', top: 14, right: 16,
|
| 215 |
+
background: 'transparent', border: 'none',
|
| 216 |
+
color: c.textDim, fontSize: 13, fontWeight: 500,
|
| 217 |
+
cursor: 'pointer', padding: '4px 8px', borderRadius: 6,
|
| 218 |
+
}}
|
| 219 |
+
>
|
| 220 |
+
Skip tour
|
| 221 |
+
</button>
|
| 222 |
+
);
|
| 223 |
+
|
| 224 |
+
return { PrevButton, NextButton, SkipButton };
|
| 225 |
+
};
|
| 226 |
+
|
| 227 |
+
// Auto-opens the tour, writes flag on close.
|
| 228 |
+
const TourLauncher = () => {
|
| 229 |
+
const { setIsOpen, isOpen } = useTour();
|
| 230 |
+
|
| 231 |
+
useEffect(() => {
|
| 232 |
+
const seen = localStorage.getItem(STORAGE_KEY) === 'true';
|
| 233 |
+
if (TESTING_ONBOARDING || !seen) {
|
| 234 |
+
const t = setTimeout(() => setIsOpen(true), 400);
|
| 235 |
+
return () => clearTimeout(t);
|
| 236 |
+
}
|
| 237 |
+
}, [setIsOpen]);
|
| 238 |
+
|
| 239 |
+
useEffect(() => {
|
| 240 |
+
if (!isOpen) {
|
| 241 |
+
if (sessionStorage.getItem('__tourStarted__')) {
|
| 242 |
+
localStorage.setItem(STORAGE_KEY, 'true');
|
| 243 |
+
}
|
| 244 |
+
} else {
|
| 245 |
+
sessionStorage.setItem('__tourStarted__', '1');
|
| 246 |
+
}
|
| 247 |
+
}, [isOpen]);
|
| 248 |
+
|
| 249 |
+
return null;
|
| 250 |
+
};
|
| 251 |
+
|
| 252 |
+
const OnboardingTour = ({ children }) => {
|
| 253 |
+
const { isDark } = useTheme();
|
| 254 |
+
const { config, advisors, resolveIcon } = useAppConfig();
|
| 255 |
+
const c = useMemo(() => palette(isDark), [isDark]);
|
| 256 |
+
|
| 257 |
+
const stepData = useMemo(() => ({
|
| 258 |
+
title: config?.app?.title || 'PhD Advisory Panel',
|
| 259 |
+
subtitle: config?.app?.subtitle || 'AI-Powered Guidance',
|
| 260 |
+
features: buildFeatures(config, resolveIcon),
|
| 261 |
+
advisorBody: buildAdvisorBody(advisors),
|
| 262 |
+
canvas: {
|
| 263 |
+
title: config?.onboarding?.tour_title || DEFAULT_CANVAS_STEP.title,
|
| 264 |
+
body: config?.onboarding?.tour_body || DEFAULT_CANVAS_STEP.body,
|
| 265 |
+
},
|
| 266 |
+
}), [config, advisors, resolveIcon]);
|
| 267 |
+
|
| 268 |
+
const steps = useMemo(() => buildSteps(c, stepData), [c, stepData]);
|
| 269 |
+
const { PrevButton, NextButton, SkipButton } = useMemo(() => makeButtons(c), [c]);
|
| 270 |
+
|
| 271 |
+
return (
|
| 272 |
+
<TourProvider
|
| 273 |
+
steps={steps}
|
| 274 |
+
showBadge={false}
|
| 275 |
+
disableInteraction
|
| 276 |
+
prevButton={PrevButton}
|
| 277 |
+
nextButton={NextButton}
|
| 278 |
+
components={{ Close: SkipButton }}
|
| 279 |
+
padding={{ mask: 8, popover: [16, 12] }}
|
| 280 |
+
styles={{
|
| 281 |
+
popover: (base) => ({
|
| 282 |
+
...base,
|
| 283 |
+
borderRadius: 18,
|
| 284 |
+
padding: '28px 26px 22px',
|
| 285 |
+
maxWidth: 440,
|
| 286 |
+
minWidth: 360,
|
| 287 |
+
background: c.bg,
|
| 288 |
+
backgroundColor: c.bg,
|
| 289 |
+
color: c.text,
|
| 290 |
+
boxShadow: '0 30px 80px -10px rgba(0,0,0,0.5)',
|
| 291 |
+
border: `1px solid ${c.border}`,
|
| 292 |
+
}),
|
| 293 |
+
maskWrapper: (base) => ({ ...base, color: 'rgba(0, 0, 0, 0.82)' }),
|
| 294 |
+
maskArea: (base) => ({ ...base, rx: 14 }),
|
| 295 |
+
controls: (base) => ({
|
| 296 |
+
...base,
|
| 297 |
+
marginTop: 22,
|
| 298 |
+
paddingTop: 18,
|
| 299 |
+
borderTop: `1px solid ${c.border}`,
|
| 300 |
+
alignItems: 'center',
|
| 301 |
+
justifyContent: 'space-between',
|
| 302 |
+
}),
|
| 303 |
+
dot: (base, { current }) => ({
|
| 304 |
+
...base,
|
| 305 |
+
width: current ? 24 : 8,
|
| 306 |
+
height: 8,
|
| 307 |
+
borderRadius: 999,
|
| 308 |
+
background: current ? c.accent : c.border,
|
| 309 |
+
transition: 'all 0.25s ease',
|
| 310 |
+
}),
|
| 311 |
+
navigation: (base) => ({ ...base, gap: 7 }),
|
| 312 |
+
}}
|
| 313 |
+
>
|
| 314 |
+
<TourLauncher />
|
| 315 |
+
{children}
|
| 316 |
+
</TourProvider>
|
| 317 |
+
);
|
| 318 |
+
};
|
| 319 |
+
|
| 320 |
+
export default OnboardingTour;
|
|
@@ -16,6 +16,7 @@ import '../styles/ChatPage.css';
|
|
| 16 |
import '../styles/EnhancedChatInput.css';
|
| 17 |
import AdvisorStatusDropdown from '../components/AdvisorStatusDropdown';
|
| 18 |
import AdvisorCarousel from '../components/AdvisorCarousel';
|
|
|
|
| 19 |
|
| 20 |
const ChatPage = ({ user, authToken, onNavigateToHome, onNavigateToCanvas, onSignOut, onUserUpdate }) => {
|
| 21 |
const { config, advisors, getAdvisorColors } = useAppConfig();
|
|
@@ -752,6 +753,7 @@ const handleNewChat = async (sessionId = null) => {
|
|
| 752 |
const chatPlaceholder = config?.chat_page?.placeholder || "Ask your advisors anything...";
|
| 753 |
|
| 754 |
return (
|
|
|
|
| 755 |
<div className="chat-page-with-sidebar">
|
| 756 |
{/* Sidebar Component */}
|
| 757 |
<Sidebar
|
|
@@ -998,6 +1000,7 @@ const handleNewChat = async (sessionId = null) => {
|
|
| 998 |
</div>
|
| 999 |
</div>
|
| 1000 |
</div>
|
|
|
|
| 1001 |
);
|
| 1002 |
};
|
| 1003 |
|
|
|
|
| 16 |
import '../styles/EnhancedChatInput.css';
|
| 17 |
import AdvisorStatusDropdown from '../components/AdvisorStatusDropdown';
|
| 18 |
import AdvisorCarousel from '../components/AdvisorCarousel';
|
| 19 |
+
import OnboardingTour from '../components/OnboardingTour';
|
| 20 |
|
| 21 |
const ChatPage = ({ user, authToken, onNavigateToHome, onNavigateToCanvas, onSignOut, onUserUpdate }) => {
|
| 22 |
const { config, advisors, getAdvisorColors } = useAppConfig();
|
|
|
|
| 753 |
const chatPlaceholder = config?.chat_page?.placeholder || "Ask your advisors anything...";
|
| 754 |
|
| 755 |
return (
|
| 756 |
+
<OnboardingTour>
|
| 757 |
<div className="chat-page-with-sidebar">
|
| 758 |
{/* Sidebar Component */}
|
| 759 |
<Sidebar
|
|
|
|
| 1000 |
</div>
|
| 1001 |
</div>
|
| 1002 |
</div>
|
| 1003 |
+
</OnboardingTour>
|
| 1004 |
);
|
| 1005 |
};
|
| 1006 |
|
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/* Force popover to match app theme via CSS vars set on :root[data-theme] */
|
| 2 |
+
[data-tour-elem="popover"] {
|
| 3 |
+
background: var(--bg-primary) !important;
|
| 4 |
+
background-color: var(--bg-primary) !important;
|
| 5 |
+
color: var(--text-primary) !important;
|
| 6 |
+
border: 1px solid var(--border-primary) !important;
|
| 7 |
+
}
|
| 8 |
+
|
| 9 |
+
/* All text inside should respect theme */
|
| 10 |
+
[data-tour-elem="popover"] * {
|
| 11 |
+
-webkit-text-fill-color: inherit;
|
| 12 |
+
}
|
|
@@ -84,6 +84,19 @@ chat_page:
|
|
| 84 |
- "Should I speak up about unclear expectations or just try to figure it out quietly?"
|
| 85 |
- "What are the unspoken expectations no one tells you about?"
|
| 86 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 87 |
# ── Personas ───────────────────────────────────────────────────────────────
|
| 88 |
|
| 89 |
personas:
|
|
|
|
| 84 |
- "Should I speak up about unclear expectations or just try to figure it out quietly?"
|
| 85 |
- "What are the unspoken expectations no one tells you about?"
|
| 86 |
|
| 87 |
+
onboarding:
|
| 88 |
+
features:
|
| 89 |
+
- title: "Get advice from specialized AI advisors"
|
| 90 |
+
icon: "GraduationCap"
|
| 91 |
+
- title: "Save and revisit every conversation"
|
| 92 |
+
icon: "MessageCircle"
|
| 93 |
+
- title: "Upload PDFs for context-aware answers"
|
| 94 |
+
icon: "Paperclip"
|
| 95 |
+
- title: "Track your PhD progress on a structured canvas"
|
| 96 |
+
icon: "BarChart3"
|
| 97 |
+
tour_title: "PhD Progress Canvas"
|
| 98 |
+
tour_body: "A dashboard view of your PhD journey — research progress, methodology, next steps, all in one place."
|
| 99 |
+
|
| 100 |
# ── Personas ───────────────────────────────────────────────────────────────
|
| 101 |
|
| 102 |
personas:
|
|
@@ -86,6 +86,21 @@ chat_page:
|
|
| 86 |
- "How do I balance self-care with a heavy course load?"
|
| 87 |
- "What campus resources exist for mental health, tutoring, or financial aid?"
|
| 88 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 89 |
# -- Personas ----------------------------------------------------------------
|
| 90 |
|
| 91 |
personas:
|
|
|
|
| 86 |
- "How do I balance self-care with a heavy course load?"
|
| 87 |
- "What campus resources exist for mental health, tutoring, or financial aid?"
|
| 88 |
|
| 89 |
+
onboarding:
|
| 90 |
+
features:
|
| 91 |
+
- title: "Get advice from specialized AI advisors"
|
| 92 |
+
icon: "GraduationCap"
|
| 93 |
+
- title: "Save and revisit every conversation"
|
| 94 |
+
icon: "MessageCircle"
|
| 95 |
+
- title: "Upload course syllabi and documents for context-aware answers"
|
| 96 |
+
icon: "Paperclip"
|
| 97 |
+
- title: "Track your progress on a structured canvas"
|
| 98 |
+
icon: "BarChart3"
|
| 99 |
+
|
| 100 |
+
canvas:
|
| 101 |
+
tour_title: "Academic Progress Canvas"
|
| 102 |
+
tour_body: "A dashboard view of your college journey -- courses, milestones, next steps, all in one place."
|
| 103 |
+
|
| 104 |
# -- Personas ----------------------------------------------------------------
|
| 105 |
|
| 106 |
personas:
|