Conversation
Introduces demo mode support by adding mock API responses for events, projects, user, and workspaces. Refactors API modules to use a withDemoMock utility for conditional mock injection. Updates EventsList and store modules for improved debug logging and error handling. Demo mode can be enabled automatically or via browser console for local development.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
|
Thanks for adding a description — the PR is now marked as Ready for Review. |
Introduce a demo mode backed by a mock database and organized mock API modules. Added a demo Vuex module (store/modules/demo), wired demo state into the root store and persisted state, and updated the router to support /demo paths and set demo tokens. Replaced the old withDemoMock utility with withMockDemo and updated API wrappers (events, projects, workspaces, user) to load mocks from new ./mocks/* locations. Created a central mock-db (users, workspaces, events) with demo data and multiple new mock responses for charts, releases, project visits, and current user. UI changes include a demo button on the login page and i18n strings for demo mode. Removed legacy mock files and simplified mock loading logic.
Introduce a centralized mock-db and refactor demo mocks to use shared DEMO_* data. Add new mock-db files (charts.ts, releases.ts) and re-export them from src/api/mock-db/index.ts. Simplify and standardize event/project/user mock modules to return values or factory functions from the mock-db. Rewrite withMockDemo to accept either a mock name (string) or factory, preload mock modules via import.meta.glob, resolve mock paths, support debug options, and fallback to the real API on errors. Update API call sites to the new withMockDemo signature and enable debug or options where appropriate. Misc: add ImportMeta.glob typing in shims, make router demo activation asynchronous and handle demo auth flow, and tweak demo event data (visitedBy handling) in the mock DB.
Add a centralized utils/time.ts with common time constants (ms/sec and day offsets) and replace scattered magic numbers across mock data and mock-db with these constants. Update mocks to include explicit TypeScript return types and more consistent timestamp calculations (daily/hour offsets), refine repetitions generation (ID offset and hour constants), and import project release types for DEMO_RELEASES/DEMO_RELEASE_DETAILS. Also minor cleanup: nullish return in getEvent and reshaped GraphQL-like mock return objects for project/user/workspace endpoints to improve type safety and clarity.
Apply consistent formatting and minor type/JSdoc adjustments across the codebase. Changes include: aligned object/argument indentation in API calls, split long lines, replaced inline numeric literals with named constants in time utilities, tightened imports (use `import type`), added a few JSDoc param tags, replaced broad `Function` types with `Commit` where appropriate, removed trailing empty lines, and small mock file tweaks (array mapping arrow style and mock data structure spacing). No intended behavioral changes — primarily styling, typing and readability improvements.
Replace short mock identifiers with absolute '@/...' mock paths across API modules (events, projects, user, workspaces) to ensure consistent mock loading. Enhance resolveMockPath to handle /src/ and @/ prefixes and try .ts/.js variants when resolving mock modules. Also enable debug option for projects.fetchChartData with withMockDemo.
Introduce a useDemo composable to centralize demo-mode checks and actions (isDemoActive, enableDemo, disableDemo, toggleDemo). Update router to use the composable instead of direct store calls when enabling demo mode and when preserving /demo routes. Switch i18n to composition API usage (legacy: false, globalInjection: true) and set locale via i18n.global.locale.value. Refactor withMockDemo to remove the preloaded import.meta.glob approach and instead dynamically import mock modules at runtime. Require explicit mock paths (supporting @/ and /src/ prefixes), normalize candidates, and throw clear errors when mocks can't be resolved. Also switch demo-mode checks in withMockDemo to use isDemoActive from the new composable and improve logging and error handling during mock loading.
Switch local type imports to the centralized @hawk.so/types package and standardize demo/mock timestamps to use seconds (and seconds-based constants) instead of milliseconds. Update time calculations and constants across mock-db and mock API files, convert event/repetition timestamps to seconds, and simplify a couple of project release mocks to return raw ReleaseData/ReleaseDetails (remove GraphQL wrapper shape). Affects events, projects, user, workspaces and mock-db release/user/chart files.
Introduce a refactored demo-mode system and UI banner. Add a composable (useDemo) exposing isEnabled ref and demo controls, handle ?demo=1 query param, set demo tokens, and register demo module for persistence. Update router and App/AppShell to use the new demo state, show a demo banner with a Disable Demo button, and avoid redirecting to login when demo is enabled. Simplify withMockDemo to load explicit mock paths and rely on isEnabled.value; update many API calls to point to explicit mock files. Minor UX/text updates to i18n (disable button) and Login link to trigger demo via query. Remove various debug/console logs and a removed ImportMeta extension in shims.
Fix setLanguage to assign i18n.global.locale directly (remove incorrect .value usage) to properly update the locale. Also remove an extraneous blank line in src/store/modules/events/index.ts to tidy up the module.
Prefix store.dispatch and router.replace calls with `void` in src/composables/useDemo.ts to explicitly ignore their returned promises and satisfy linting (no-floating-promises) / avoid unused Promise warnings. This is a non-functional change to address static checks.
Convert useDemo to a shared composable (createSharedComposable) and centralize demo state management. The previous top-level isEnabled ref was removed and is now returned from useDemo as a Ref along with typed controls (isDemoActive, enableDemo, disableDemo, toggleDemo). The demo URL watcher and setDemoState logic were improved to avoid duplicate initialization and to synchronize Vuex tokens/state. Update callsites (App.vue, AppShell.vue, router.ts, withMockDemo.ts) to consume useDemo and use the returned isEnabled (exposed as isDemoEnabled in components). Also bump @hawk.so/types version in package.json.
Switch demo checks from ref.value to the new demo API and store-based flow. App and AppShell now use isDemoEnabled (non-ref), account settings logout uses useDemo to disable an active demo, and withMockDemo calls useDemo(). Router now handles ?demo=1 by dispatching demo/enableDemo and seeding demo tokens, and auth guard checks store.state.demo?.isActive instead of the removed composable import. Also simplified i18n initialization by removing legacy/globalInjection and setting i18n.global.locale directly.
Add demo-mode detection and friendly messaging for unauthenticated GraphQL responses (DEMO_ACCESS_TOKEN and isDemoModeEnabled) in the API. Update error handling when loading notification settings: store action now validates fetched notifications and throws a demo-specific or generic error when missing, and the Notifications component wraps the fetch in try/catch to report errors to Hawk and show a notifier. Also enable legacy mode in i18n configuration.
Replace hardcoded Russian demo-mode message with i18n lookup in API error handling. Import i18n in src/api/index.ts and call i18n.global.t('demo.functionUnavailable') for UNAUTHENTICATED demo responses. Add corresponding "functionUnavailable" keys to English and Russian message files so the message is localized.
Refactor user notification API calls to use api.call and to expose request helpers wrapped with withMockDemo for demo mocks. Added mock implementations and in-memory notification state (fetchNotificationsSettings.mock.ts, updateNotificationsChannel.mock.ts, updateNotificationsReceiveType.mock.ts, notificationsState.ts) and updated exports in src/api/user/index.js to use these mocks. Also updated fetchBankCards to use api.call and optional chaining, and changed withMockDemo to forward arguments to mock functions so mocks can react to input.
Enhance the mock fetchDailyEventsPortion API to accept sort, filters and search parameters (with defaults) and normalize the search string. Events are now filtered by starred/resolved/ignored marks and title, and can be sorted by date, count or affected users. Update mapping to use the filtered/sorted list. Also extend createDemoEvent in the mock DB to accept isResolved/isIgnored flags and apply them to event marks, and mark a couple of demo events accordingly.
Wrap several API calls with withMockDemo and add mock implementations + mock DB data for demo mode. Added mocks for plans, business operations, workspace list/balance and admin/remove actions, and exported business-operations from mock-db. Updated demo workspace plan data (free/RUB/limits). Adjusted UI: simplified ChooseTariffPlanPopup layout to vertical plans, removed TariffPlan horizontal prop and updated its styles, and block payment actions in PaymentDetailsDialog when demo mode is active with a notification.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 58 out of 59 changed files in this pull request and generated 9 comments.
Comments suppressed due to low confidence (1)
src/components/modals/ChooseTariffPlanPopup.vue:266
- The
choose-plan__plans--horizontalmodifier class is no longer applied in the template, so this CSS block is now dead code (and currently duplicates the baseflex-direction: column). It should be removed to avoid confusion, or the template logic should be restored if it’s still needed.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| export default function mockGetBusinessOperations(ids: string[]): BusinessOperation[] { | ||
| // Return demo operations if requesting for demo workspace | ||
| if (ids.includes('6213b6a01e6281087467cc7a')) { | ||
| return DEMO_BUSINESS_OPERATIONS; | ||
| } | ||
| return []; |
There was a problem hiding this comment.
This mock hard-codes the demo workspace id string. Since DEMO_WORKSPACE_ID is already defined in @/api/mock-db, using the shared constant here would prevent drift if the demo ids ever change.
| * Returns success status for updating last project visit timestamp | ||
| * @param projectId | ||
| */ | ||
| export default async function updateLastProjectVisitMock(projectId: string): Promise<boolean> { |
There was a problem hiding this comment.
projectId is unused in this mock; with the repo’s unused-vars rules it’s better to prefix it with _ (or remove it) to avoid lint warnings and make intent explicit.
| export default async function updateLastProjectVisitMock(projectId: string): Promise<boolean> { | |
| export default async function updateLastProjectVisitMock(_projectId: string): Promise<boolean> { |
| async function loadMockModule(mockPath: string): Promise<any> { | ||
| try { | ||
| return await import(/* @vite-ignore */ mockPath); | ||
| } catch (error) { | ||
| throw new Error(`Mock module not found: ${mockPath}`); | ||
| } |
There was a problem hiding this comment.
loadMockModule() uses import(/* @vite-ignore */ mockPath) with a runtime string path. In Vite production builds this typically won’t include the mock modules in the bundle, so demo mode can break at runtime when the import can’t be resolved. Consider switching to an import.meta.glob() registry (or passing the mock factory directly) so the mocks are statically discoverable and bundled.
Rename the demo mock utility and its usages: src/utils/withMockDemo.ts -> src/utils/withDemoMock.ts and change the exported function name from withMockDemo to withDemoMock. Update imports/usages across API modules (billing, events, plans, projects, user, workspaces) to reference the new name/path. No functional changes — only a rename for clearer naming and consistency.
| export default function mockGetEvent(): HawkEvent | null { | ||
| // In demo mode, we just return the first demo event | ||
| // In a real scenario, we could match by eventId from args | ||
| const event = DEMO_EVENTS[0]; |
There was a problem hiding this comment.
it is not the best solution. Its better to create a list of mocked events and find a particular event in there
src/api/plans/index.ts
Outdated
| export const getPlans = withMockDemo( | ||
| getPlansRequest, | ||
| '/src/api/plans/mocks/getPlans.mock.ts' | ||
| ); |
There was a problem hiding this comment.
I'd suggest to get rid of mock here and allowlist this query for anons on the API side.
It can be done easily by adding @allowAnon directive here https://github.com/codex-team/hawk.api.nodejs/blob/3d9de276b2c001e0111c564aa8c9cdb857a45ff7/src/typeDefs/plans.ts#L43
This will ensure plans list is actual. It may be important for users who discover Hawk by demo mode.
src/api/index.ts
Outdated
| * Hawk API endpoint URL | ||
| */ | ||
| export const API_ENDPOINT: string = import.meta.env.VITE_API_ENDPOINT || ''; | ||
| const DEMO_ACCESS_TOKEN = 'demo-access-token'; |
There was a problem hiding this comment.
this constant is declared several times. It's better to reuse the single one.
| function isDemoModeEnabled(): boolean { | ||
| const authHeader = axios.defaults.headers.common.Authorization; | ||
|
|
||
| return typeof authHeader === 'string' && authHeader.includes(DEMO_ACCESS_TOKEN); | ||
| } |
There was a problem hiding this comment.
please describe the logic in jsdoc. Why this way is used?
src/composables/useDemo.ts
Outdated
| try { | ||
| return isEnabled.value || (store?.state?.demo?.isActive ?? false); | ||
| } catch (error) { | ||
| console.warn('[useDemo] Could not access store, demo mode disabled:', error); |
There was a problem hiding this comment.
its better to throw error in this case so we'll see it in Hawk and start debugging.
There was a problem hiding this comment.
same in other similar places below
src/composables/useDemo.ts
Outdated
| isEnabled, | ||
| isDemoActive, |
There was a problem hiding this comment.
what is the difference between these two? It's better to leave a single one (ref or computed ref is preferable)
There was a problem hiding this comment.
these changes are redundant sinсe useDemo already contains this logic.
Replace static DEMO_CHART_DATA with dynamic chart generation driven by DEMO_EVENTS in both events and projects mocks. Added timestamp helpers (alignToDay, toUnixSeconds, getBucketStart), timezoneOffset support, bucketization (days or groupBy), and rate-limited event detection. Updated mock DB to include NOW_SECONDS, set timestamps on existing events, and bulk-generate many synthetic events with varied minute offsets to produce realistic time series. Mocks now return per-day/per-bucket 'accepted' (and 'rate-limited' for projects) chart lines for the requested range or originalEventId.
Introduce project-scoped demo events and a second demo project. Add getDemoEventsByProjectId and DEMO_SECOND_PROJECT/DEMO_PROJECTS, assign generated events to the second project, and update createDemoEvent to accept projectId (used in payload URLs). Update mocks (events, projects, workspaces) to accept projectId arguments and filter DEMO_EVENTS by project: fetchChartData, fetchDailyEventsPortion, getEvent, getRepetitionsPortion, and workspace mock now return project-specific daily events. Also add safe handling when no base event is found for repetitions and extend the demo events set with additional mobile-oriented samples.
Replace the previous isEnabled / isDemoActive function API with a single computed isDemoActive ref from the useDemo composable. Update imports, types and all consumers to use the new computed ref (.value where needed), and remove the old isEnabled return value. Files updated: src/composables/useDemo.ts (API/type changes and implementation), src/App.vue, src/components/AppShell.vue, src/components/account/settings/Layout.vue, and src/utils/withDemoMock.ts to use the new isDemoActive computed ref. This centralizes demo-mode state handling and simplifies consumer logic.
Export DEMO_ACCESS_TOKEN and DEMO_REFRESH_TOKEN from the demo composable and remove duplicate local constants. Update src/api/index.ts and src/router.ts to import and use the exported tokens (replace hardcoded string literals). This centralizes demo auth tokens, reduces duplication, and adds brief JSDoc comments for the exported constants.
Exported getBalance directly (replacing getBalanceRequest) and removed the withDemoMock wrapper and its mock file. The implementation now calls the API directly and the mock at src/api/workspaces/mocks/getBalance.mock.ts was deleted. Note: the return type was adjusted from Workspace[] to Workspace, which may be a breaking change for callers expecting an array.
Replace the previous getPlansRequest + withDemoMock wrapper by exporting getPlans directly from src/api/plans/index.ts. Remove the withDemoMock import and delete the demo mock file src/api/plans/mocks/getPlans.mock.ts. getPlans now returns (await api.callOld(QUERY_PLANS)).plans, removing the demo/mock fallback.
Demo mode feature on
/?demo=1urlMock api requests, show defined data