mvc-kit 2.12.5 → 2.13.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/BEST_PRACTICES.md +1390 -0
- package/agent-config/bin/postinstall.mjs +4 -3
- package/agent-config/bin/setup.mjs +5 -1
- package/agent-config/claude-code/agents/mvc-kit-architect.md +16 -8
- package/agent-config/claude-code/skills/guide/SKILL.md +29 -7
- package/agent-config/claude-code/skills/guide/patterns.md +12 -0
- package/agent-config/claude-code/skills/guide/recipes.md +510 -0
- package/agent-config/claude-code/skills/guide/testing.md +297 -0
- package/agent-config/claude-code/skills/review/SKILL.md +3 -13
- package/agent-config/claude-code/skills/review/checklist.md +30 -5
- package/agent-config/claude-code/skills/scaffold/SKILL.md +4 -13
- package/agent-config/lib/install-claude.mjs +90 -25
- package/dist/Channel.cjs +276 -300
- package/dist/Channel.cjs.map +1 -1
- package/dist/Channel.js +275 -299
- package/dist/Channel.js.map +1 -1
- package/dist/Collection.cjs +424 -504
- package/dist/Collection.cjs.map +1 -1
- package/dist/Collection.js +423 -503
- package/dist/Collection.js.map +1 -1
- package/dist/Controller.cjs +70 -67
- package/dist/Controller.cjs.map +1 -1
- package/dist/Controller.js +69 -66
- package/dist/Controller.js.map +1 -1
- package/dist/EventBus.cjs +77 -88
- package/dist/EventBus.cjs.map +1 -1
- package/dist/EventBus.js +76 -87
- package/dist/EventBus.js.map +1 -1
- package/dist/Feed.cjs +81 -77
- package/dist/Feed.cjs.map +1 -1
- package/dist/Feed.js +80 -76
- package/dist/Feed.js.map +1 -1
- package/dist/Model.cjs +181 -207
- package/dist/Model.cjs.map +1 -1
- package/dist/Model.js +179 -205
- package/dist/Model.js.map +1 -1
- package/dist/Pagination.cjs +75 -73
- package/dist/Pagination.cjs.map +1 -1
- package/dist/Pagination.js +74 -72
- package/dist/Pagination.js.map +1 -1
- package/dist/Pending.cjs +255 -287
- package/dist/Pending.cjs.map +1 -1
- package/dist/Pending.js +253 -285
- package/dist/Pending.js.map +1 -1
- package/dist/PersistentCollection.cjs +242 -285
- package/dist/PersistentCollection.cjs.map +1 -1
- package/dist/PersistentCollection.js +241 -284
- package/dist/PersistentCollection.js.map +1 -1
- package/dist/Resource.cjs +166 -174
- package/dist/Resource.cjs.map +1 -1
- package/dist/Resource.js +164 -172
- package/dist/Resource.js.map +1 -1
- package/dist/Selection.cjs +84 -94
- package/dist/Selection.cjs.map +1 -1
- package/dist/Selection.js +83 -93
- package/dist/Selection.js.map +1 -1
- package/dist/Service.cjs +54 -55
- package/dist/Service.cjs.map +1 -1
- package/dist/Service.js +53 -54
- package/dist/Service.js.map +1 -1
- package/dist/Sorting.cjs +102 -101
- package/dist/Sorting.cjs.map +1 -1
- package/dist/Sorting.js +102 -101
- package/dist/Sorting.js.map +1 -1
- package/dist/Trackable.cjs +112 -80
- package/dist/Trackable.cjs.map +1 -1
- package/dist/Trackable.js +111 -79
- package/dist/Trackable.js.map +1 -1
- package/dist/ViewModel.cjs +528 -576
- package/dist/ViewModel.cjs.map +1 -1
- package/dist/ViewModel.js +525 -573
- package/dist/ViewModel.js.map +1 -1
- package/dist/bindPublicMethods.cjs +43 -24
- package/dist/bindPublicMethods.cjs.map +1 -1
- package/dist/bindPublicMethods.js +43 -24
- package/dist/bindPublicMethods.js.map +1 -1
- package/dist/errors.cjs +67 -68
- package/dist/errors.cjs.map +1 -1
- package/dist/errors.js +68 -71
- package/dist/errors.js.map +1 -1
- package/dist/mvc-kit.cjs +44 -46
- package/dist/mvc-kit.js +5 -32
- package/dist/produceDraft.cjs +105 -95
- package/dist/produceDraft.cjs.map +1 -1
- package/dist/produceDraft.js +106 -97
- package/dist/produceDraft.js.map +1 -1
- package/dist/react/components/CardList.cjs +30 -40
- package/dist/react/components/CardList.cjs.map +1 -1
- package/dist/react/components/CardList.js +31 -41
- package/dist/react/components/CardList.js.map +1 -1
- package/dist/react/components/DataTable.cjs +146 -169
- package/dist/react/components/DataTable.cjs.map +1 -1
- package/dist/react/components/DataTable.js +147 -170
- package/dist/react/components/DataTable.js.map +1 -1
- package/dist/react/components/InfiniteScroll.cjs +51 -42
- package/dist/react/components/InfiniteScroll.cjs.map +1 -1
- package/dist/react/components/InfiniteScroll.js +52 -43
- package/dist/react/components/InfiniteScroll.js.map +1 -1
- package/dist/react/components/types.cjs +10 -6
- package/dist/react/components/types.cjs.map +1 -1
- package/dist/react/components/types.js +11 -9
- package/dist/react/components/types.js.map +1 -1
- package/dist/react/guards.cjs +10 -6
- package/dist/react/guards.cjs.map +1 -1
- package/dist/react/guards.js +11 -9
- package/dist/react/guards.js.map +1 -1
- package/dist/react/provider.cjs +23 -20
- package/dist/react/provider.cjs.map +1 -1
- package/dist/react/provider.js +23 -21
- package/dist/react/provider.js.map +1 -1
- package/dist/react/use-event-bus.cjs +24 -20
- package/dist/react/use-event-bus.cjs.map +1 -1
- package/dist/react/use-event-bus.js +24 -21
- package/dist/react/use-event-bus.js.map +1 -1
- package/dist/react/use-instance.cjs +43 -36
- package/dist/react/use-instance.cjs.map +1 -1
- package/dist/react/use-instance.js +43 -36
- package/dist/react/use-instance.js.map +1 -1
- package/dist/react/use-local.cjs +48 -64
- package/dist/react/use-local.cjs.map +1 -1
- package/dist/react/use-local.js +47 -63
- package/dist/react/use-local.js.map +1 -1
- package/dist/react/use-model.cjs +84 -98
- package/dist/react/use-model.cjs.map +1 -1
- package/dist/react/use-model.js +84 -100
- package/dist/react/use-model.js.map +1 -1
- package/dist/react/use-singleton.cjs +19 -23
- package/dist/react/use-singleton.cjs.map +1 -1
- package/dist/react/use-singleton.js +16 -20
- package/dist/react/use-singleton.js.map +1 -1
- package/dist/react/use-subscribe-only.cjs +28 -22
- package/dist/react/use-subscribe-only.cjs.map +1 -1
- package/dist/react/use-subscribe-only.js +28 -22
- package/dist/react/use-subscribe-only.js.map +1 -1
- package/dist/react/use-teardown.cjs +20 -19
- package/dist/react/use-teardown.cjs.map +1 -1
- package/dist/react/use-teardown.js +20 -19
- package/dist/react/use-teardown.js.map +1 -1
- package/dist/react-native/NativeCollection.cjs +98 -78
- package/dist/react-native/NativeCollection.cjs.map +1 -1
- package/dist/react-native/NativeCollection.js +97 -77
- package/dist/react-native/NativeCollection.js.map +1 -1
- package/dist/react-native.cjs +2 -4
- package/dist/react-native.js +1 -4
- package/dist/react.cjs +24 -26
- package/dist/react.js +1 -17
- package/dist/singleton.cjs +28 -22
- package/dist/singleton.cjs.map +1 -1
- package/dist/singleton.js +29 -26
- package/dist/singleton.js.map +1 -1
- package/dist/walkPrototypeChain.cjs +20 -12
- package/dist/walkPrototypeChain.cjs.map +1 -1
- package/dist/walkPrototypeChain.js +21 -13
- package/dist/walkPrototypeChain.js.map +1 -1
- package/dist/web/IndexedDBCollection.cjs +53 -36
- package/dist/web/IndexedDBCollection.cjs.map +1 -1
- package/dist/web/IndexedDBCollection.js +52 -35
- package/dist/web/IndexedDBCollection.js.map +1 -1
- package/dist/web/WebStorageCollection.cjs +82 -84
- package/dist/web/WebStorageCollection.cjs.map +1 -1
- package/dist/web/WebStorageCollection.js +81 -83
- package/dist/web/WebStorageCollection.js.map +1 -1
- package/dist/web/idb.cjs +107 -99
- package/dist/web/idb.cjs.map +1 -1
- package/dist/web/idb.js +108 -105
- package/dist/web/idb.js.map +1 -1
- package/dist/web.cjs +4 -6
- package/dist/web.js +1 -5
- package/dist/wrapAsyncMethods.cjs +141 -168
- package/dist/wrapAsyncMethods.cjs.map +1 -1
- package/dist/wrapAsyncMethods.js +141 -168
- package/dist/wrapAsyncMethods.js.map +1 -1
- package/examples/primitive/channel.ts +109 -0
- package/examples/primitive/collection.ts +118 -0
- package/examples/primitive/controller.ts +118 -0
- package/examples/primitive/counter.ts +108 -0
- package/examples/primitive/env.d.ts +1 -0
- package/examples/primitive/eventbus.ts +77 -0
- package/examples/primitive/feed.ts +162 -0
- package/examples/primitive/model.ts +82 -0
- package/examples/primitive/pagination.ts +91 -0
- package/examples/primitive/pending.ts +189 -0
- package/examples/primitive/persistent-collection.ts +116 -0
- package/examples/primitive/resource.ts +114 -0
- package/examples/primitive/selection.ts +96 -0
- package/examples/primitive/sorting.ts +112 -0
- package/examples/primitive/timer.ts +58 -0
- package/examples/primitive/trackable.ts +225 -0
- package/examples/primitive/tsconfig.json +20 -0
- package/examples/primitive/viewmodel-service.ts +161 -0
- package/examples/react/AuthExample/index.html +12 -0
- package/examples/react/AuthExample/src/App.tsx +29 -0
- package/examples/react/AuthExample/src/components/AdminPage.tsx +51 -0
- package/examples/react/AuthExample/src/components/AppHeader.tsx +32 -0
- package/examples/react/AuthExample/src/components/AuthGuard.tsx +50 -0
- package/examples/react/AuthExample/src/components/AuthScreen.tsx +181 -0
- package/examples/react/AuthExample/src/components/DashboardPage.tsx +41 -0
- package/examples/react/AuthExample/src/components/ProfilePage.tsx +44 -0
- package/examples/react/AuthExample/src/components/Toast.tsx +41 -0
- package/examples/react/AuthExample/src/env.d.ts +10 -0
- package/examples/react/AuthExample/src/events/AppEventBus.ts +7 -0
- package/examples/react/AuthExample/src/main.tsx +10 -0
- package/examples/react/AuthExample/src/mock/api.ts +78 -0
- package/examples/react/AuthExample/src/models/LoginFormModel.ts +19 -0
- package/examples/react/AuthExample/src/models/RegisterFormModel.ts +25 -0
- package/examples/react/AuthExample/src/services/AuthService.ts +21 -0
- package/examples/react/AuthExample/src/styles.css +445 -0
- package/examples/react/AuthExample/src/types/auth.ts +12 -0
- package/examples/react/AuthExample/src/viewmodels/AuthViewModel.ts +111 -0
- package/examples/react/AuthExample/tsconfig.json +22 -0
- package/examples/react/AuthExample/vite.config.ts +18 -0
- package/examples/react/ComplexApp/index.html +12 -0
- package/examples/react/ComplexApp/src/App.tsx +17 -0
- package/examples/react/ComplexApp/src/channels/ActivityChannel.ts +24 -0
- package/examples/react/ComplexApp/src/channels/DashboardChannel.ts +26 -0
- package/examples/react/ComplexApp/src/channels/ErrorsChannel.ts +5 -0
- package/examples/react/ComplexApp/src/channels/LatencyChannel.ts +5 -0
- package/examples/react/ComplexApp/src/channels/OrdersChannel.ts +5 -0
- package/examples/react/ComplexApp/src/channels/RevenueChannel.ts +5 -0
- package/examples/react/ComplexApp/src/channels/TrafficChannel.ts +5 -0
- package/examples/react/ComplexApp/src/channels/UsersMetricChannel.ts +5 -0
- package/examples/react/ComplexApp/src/collections/DashboardCollection.ts +6 -0
- package/examples/react/ComplexApp/src/collections/ErrorsCollection.ts +3 -0
- package/examples/react/ComplexApp/src/collections/LatencyCollection.ts +3 -0
- package/examples/react/ComplexApp/src/collections/OrdersCollection.ts +3 -0
- package/examples/react/ComplexApp/src/collections/RevenueCollection.ts +3 -0
- package/examples/react/ComplexApp/src/collections/TrafficCollection.ts +3 -0
- package/examples/react/ComplexApp/src/collections/UsersMetricCollection.ts +3 -0
- package/examples/react/ComplexApp/src/components/activity/ActivityFeed.tsx +31 -0
- package/examples/react/ComplexApp/src/components/activity/ActivityItemRow.tsx +35 -0
- package/examples/react/ComplexApp/src/components/dashboard/DashboardCard.tsx +37 -0
- package/examples/react/ComplexApp/src/components/dashboard/DashboardPage.tsx +34 -0
- package/examples/react/ComplexApp/src/components/layout/Navbar.tsx +32 -0
- package/examples/react/ComplexApp/src/components/layout/SocialFeedPanel.tsx +57 -0
- package/examples/react/ComplexApp/src/components/shared/Spinner.tsx +3 -0
- package/examples/react/ComplexApp/src/components/shared/StatusIndicator.tsx +13 -0
- package/examples/react/ComplexApp/src/components/shared/Toast.tsx +40 -0
- package/examples/react/ComplexApp/src/env.d.ts +10 -0
- package/examples/react/ComplexApp/src/events/AppEventBus.ts +7 -0
- package/examples/react/ComplexApp/src/main.tsx +10 -0
- package/examples/react/ComplexApp/src/mock-remote/MockWebSocket.ts +38 -0
- package/examples/react/ComplexApp/src/mock-remote/activity-api.ts +48 -0
- package/examples/react/ComplexApp/src/mock-remote/dashboard-generators.ts +45 -0
- package/examples/react/ComplexApp/src/mock-remote/delay.ts +18 -0
- package/examples/react/ComplexApp/src/mock-remote/social-api.ts +55 -0
- package/examples/react/ComplexApp/src/resources/ActivityResource.ts +12 -0
- package/examples/react/ComplexApp/src/resources/SocialFeedResource.ts +17 -0
- package/examples/react/ComplexApp/src/styles.css +463 -0
- package/examples/react/ComplexApp/src/types/activity.ts +8 -0
- package/examples/react/ComplexApp/src/types/dashboard.ts +5 -0
- package/examples/react/ComplexApp/src/types/social.ts +8 -0
- package/examples/react/ComplexApp/src/types/users.ts +6 -0
- package/examples/react/ComplexApp/src/viewmodels/ActivityFeedViewModel.ts +68 -0
- package/examples/react/ComplexApp/src/viewmodels/AppStateViewModel.ts +26 -0
- package/examples/react/ComplexApp/src/viewmodels/DashboardCardViewModel.ts +69 -0
- package/examples/react/ComplexApp/src/viewmodels/ErrorsCardViewModel.ts +9 -0
- package/examples/react/ComplexApp/src/viewmodels/LatencyCardViewModel.ts +9 -0
- package/examples/react/ComplexApp/src/viewmodels/OrdersCardViewModel.ts +9 -0
- package/examples/react/ComplexApp/src/viewmodels/RevenueCardViewModel.ts +9 -0
- package/examples/react/ComplexApp/src/viewmodels/SocialFeedViewModel.ts +39 -0
- package/examples/react/ComplexApp/src/viewmodels/TrafficCardViewModel.ts +9 -0
- package/examples/react/ComplexApp/src/viewmodels/UsersMetricCardViewModel.ts +9 -0
- package/examples/react/ComplexApp/tsconfig.json +22 -0
- package/examples/react/ComplexApp/vite.config.ts +18 -0
- package/examples/react/FullApp/index.html +12 -0
- package/examples/react/FullApp/src/App.tsx +28 -0
- package/examples/react/FullApp/src/collections/ConversationsCollection.ts +4 -0
- package/examples/react/FullApp/src/collections/LocationsCollection.ts +4 -0
- package/examples/react/FullApp/src/components/auth/LoginPage.tsx +80 -0
- package/examples/react/FullApp/src/components/dashboard/DashboardPage.tsx +29 -0
- package/examples/react/FullApp/src/components/dashboard/RecentActivityCard.tsx +35 -0
- package/examples/react/FullApp/src/components/dashboard/StatsCard.tsx +19 -0
- package/examples/react/FullApp/src/components/layout/AppShell.tsx +31 -0
- package/examples/react/FullApp/src/components/layout/Header.tsx +25 -0
- package/examples/react/FullApp/src/components/layout/Sidebar.tsx +29 -0
- package/examples/react/FullApp/src/components/locations/LocationFilters.tsx +60 -0
- package/examples/react/FullApp/src/components/locations/LocationForm.tsx +112 -0
- package/examples/react/FullApp/src/components/locations/LocationProfilePage.tsx +81 -0
- package/examples/react/FullApp/src/components/locations/LocationsPage.tsx +127 -0
- package/examples/react/FullApp/src/components/messaging/ConversationList.tsx +59 -0
- package/examples/react/FullApp/src/components/messaging/MessageBubble.tsx +22 -0
- package/examples/react/FullApp/src/components/messaging/MessageThread.tsx +100 -0
- package/examples/react/FullApp/src/components/messaging/MessagingPage.tsx +52 -0
- package/examples/react/FullApp/src/components/shared/ErrorBanner.tsx +3 -0
- package/examples/react/FullApp/src/components/shared/Spinner.tsx +7 -0
- package/examples/react/FullApp/src/components/shared/Toast.tsx +41 -0
- package/examples/react/FullApp/src/components/users/UserFilters.tsx +59 -0
- package/examples/react/FullApp/src/components/users/UsersPage.tsx +80 -0
- package/examples/react/FullApp/src/components/users/UsersTable.tsx +52 -0
- package/examples/react/FullApp/src/env.d.ts +10 -0
- package/examples/react/FullApp/src/events/AppEventBus.ts +7 -0
- package/examples/react/FullApp/src/main.tsx +10 -0
- package/examples/react/FullApp/src/mock/delay.ts +21 -0
- package/examples/react/FullApp/src/mock/locations.ts +76 -0
- package/examples/react/FullApp/src/mock/messages.ts +237 -0
- package/examples/react/FullApp/src/mock/users.ts +84 -0
- package/examples/react/FullApp/src/models/LocationFormModel.ts +31 -0
- package/examples/react/FullApp/src/models/LoginFormModel.ts +19 -0
- package/examples/react/FullApp/src/resources/UsersResource.ts +12 -0
- package/examples/react/FullApp/src/services/AuthService.ts +18 -0
- package/examples/react/FullApp/src/services/LocationService.ts +23 -0
- package/examples/react/FullApp/src/services/MessageService.ts +65 -0
- package/examples/react/FullApp/src/services/UserService.ts +23 -0
- package/examples/react/FullApp/src/styles.css +767 -0
- package/examples/react/FullApp/src/types/conversation.ts +7 -0
- package/examples/react/FullApp/src/types/location.ts +12 -0
- package/examples/react/FullApp/src/types/message.ts +7 -0
- package/examples/react/FullApp/src/types/user.ts +10 -0
- package/examples/react/FullApp/src/viewmodels/AuthViewModel.ts +51 -0
- package/examples/react/FullApp/src/viewmodels/ConversationsViewModel.ts +89 -0
- package/examples/react/FullApp/src/viewmodels/DashboardViewModel.ts +56 -0
- package/examples/react/FullApp/src/viewmodels/LocationProfileViewModel.ts +81 -0
- package/examples/react/FullApp/src/viewmodels/LocationsViewModel.ts +113 -0
- package/examples/react/FullApp/src/viewmodels/MessageThreadViewModel.ts +83 -0
- package/examples/react/FullApp/src/viewmodels/UsersViewModel.ts +88 -0
- package/examples/react/FullApp/tsconfig.json +22 -0
- package/examples/react/FullApp/vite.config.ts +18 -0
- package/examples/react/WorkerApp/index.html +12 -0
- package/examples/react/WorkerApp/src/App.tsx +24 -0
- package/examples/react/WorkerApp/src/channels/MessagingChannel.ts +46 -0
- package/examples/react/WorkerApp/src/channels/WorkerStatusChannel.ts +35 -0
- package/examples/react/WorkerApp/src/components/auth/LoginPage.tsx +60 -0
- package/examples/react/WorkerApp/src/components/layout/AppShell.tsx +31 -0
- package/examples/react/WorkerApp/src/components/layout/Header.tsx +23 -0
- package/examples/react/WorkerApp/src/components/layout/Sidebar.tsx +28 -0
- package/examples/react/WorkerApp/src/components/messaging/ComposeBar.tsx +33 -0
- package/examples/react/WorkerApp/src/components/messaging/ConversationList.tsx +59 -0
- package/examples/react/WorkerApp/src/components/messaging/MessageBubble.tsx +45 -0
- package/examples/react/WorkerApp/src/components/messaging/MessageThread.tsx +93 -0
- package/examples/react/WorkerApp/src/components/messaging/MessagingPage.tsx +53 -0
- package/examples/react/WorkerApp/src/components/shared/ErrorBanner.tsx +3 -0
- package/examples/react/WorkerApp/src/components/shared/PendingBanner.tsx +37 -0
- package/examples/react/WorkerApp/src/components/shared/Spinner.tsx +7 -0
- package/examples/react/WorkerApp/src/components/shared/Toast.tsx +41 -0
- package/examples/react/WorkerApp/src/components/shift/ShiftPage.tsx +98 -0
- package/examples/react/WorkerApp/src/components/shift/ShiftTimer.tsx +24 -0
- package/examples/react/WorkerApp/src/components/shift/SiteSelector.tsx +27 -0
- package/examples/react/WorkerApp/src/components/sites/SiteFilters.tsx +61 -0
- package/examples/react/WorkerApp/src/components/sites/SitesPage.tsx +102 -0
- package/examples/react/WorkerApp/src/env.d.ts +10 -0
- package/examples/react/WorkerApp/src/events/AppEventBus.ts +7 -0
- package/examples/react/WorkerApp/src/main.tsx +10 -0
- package/examples/react/WorkerApp/src/mock/MockWebSocket.ts +38 -0
- package/examples/react/WorkerApp/src/mock/delay.ts +31 -0
- package/examples/react/WorkerApp/src/mock/messages.ts +120 -0
- package/examples/react/WorkerApp/src/mock/shifts.ts +57 -0
- package/examples/react/WorkerApp/src/mock/sites.ts +14 -0
- package/examples/react/WorkerApp/src/mock/workers.ts +12 -0
- package/examples/react/WorkerApp/src/models/ComposeMessageModel.ts +17 -0
- package/examples/react/WorkerApp/src/resources/ConversationsResource.ts +10 -0
- package/examples/react/WorkerApp/src/resources/MessagesResource.ts +32 -0
- package/examples/react/WorkerApp/src/resources/ShiftResource.ts +73 -0
- package/examples/react/WorkerApp/src/resources/SitesResource.ts +11 -0
- package/examples/react/WorkerApp/src/resources/WorkersResource.ts +11 -0
- package/examples/react/WorkerApp/src/styles.css +756 -0
- package/examples/react/WorkerApp/src/types/conversation.ts +7 -0
- package/examples/react/WorkerApp/src/types/message.ts +7 -0
- package/examples/react/WorkerApp/src/types/shift.ts +13 -0
- package/examples/react/WorkerApp/src/types/site.ts +8 -0
- package/examples/react/WorkerApp/src/types/worker.ts +8 -0
- package/examples/react/WorkerApp/src/viewmodels/AuthViewModel.ts +41 -0
- package/examples/react/WorkerApp/src/viewmodels/ConversationsViewModel.ts +83 -0
- package/examples/react/WorkerApp/src/viewmodels/MessageThreadViewModel.ts +113 -0
- package/examples/react/WorkerApp/src/viewmodels/ShiftViewModel.ts +147 -0
- package/examples/react/WorkerApp/src/viewmodels/SitesViewModel.ts +82 -0
- package/examples/react/WorkerApp/tsconfig.json +22 -0
- package/examples/react/WorkerApp/vite.config.ts +18 -0
- package/package.json +11 -9
- package/src/Pending.test.ts +1 -2
- package/src/Sorting.test.ts +1 -1
- package/src/produceDraft.test.ts +3 -3
- package/src/react/components/CardList.test.tsx +1 -1
- package/src/react/components/DataTable.test.tsx +1 -1
- package/src/react/components/InfiniteScroll.test.tsx +5 -5
- package/dist/mvc-kit.cjs.map +0 -1
- package/dist/mvc-kit.js.map +0 -1
- package/dist/react-native.cjs.map +0 -1
- package/dist/react-native.js.map +0 -1
- package/dist/react.cjs.map +0 -1
- package/dist/react.js.map +0 -1
- package/dist/web.cjs.map +0 -1
- package/dist/web.js.map +0 -1
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import type { LocationState } from '../types/location';
|
|
2
|
+
|
|
3
|
+
export const mockLocations: LocationState[] = [
|
|
4
|
+
{
|
|
5
|
+
id: 'loc1',
|
|
6
|
+
name: 'Downtown HQ',
|
|
7
|
+
type: 'office',
|
|
8
|
+
status: 'active',
|
|
9
|
+
city: 'New York',
|
|
10
|
+
state: 'NY',
|
|
11
|
+
address: '100 Broadway',
|
|
12
|
+
capacity: 250,
|
|
13
|
+
managerId: 'u1',
|
|
14
|
+
createdAt: '2024-01-01T00:00:00Z',
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
id: 'loc2',
|
|
18
|
+
name: 'West Warehouse',
|
|
19
|
+
type: 'warehouse',
|
|
20
|
+
status: 'active',
|
|
21
|
+
city: 'Chicago',
|
|
22
|
+
state: 'IL',
|
|
23
|
+
address: '500 Industrial Ave',
|
|
24
|
+
capacity: 800,
|
|
25
|
+
managerId: 'u2',
|
|
26
|
+
createdAt: '2024-02-01T00:00:00Z',
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
id: 'loc3',
|
|
30
|
+
name: 'Main Street Store',
|
|
31
|
+
type: 'retail',
|
|
32
|
+
status: 'active',
|
|
33
|
+
city: 'Los Angeles',
|
|
34
|
+
state: 'CA',
|
|
35
|
+
address: '220 Main St',
|
|
36
|
+
capacity: 50,
|
|
37
|
+
managerId: 'u5',
|
|
38
|
+
createdAt: '2024-03-01T00:00:00Z',
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
id: 'loc4',
|
|
42
|
+
name: 'North Office',
|
|
43
|
+
type: 'office',
|
|
44
|
+
status: 'maintenance',
|
|
45
|
+
city: 'Seattle',
|
|
46
|
+
state: 'WA',
|
|
47
|
+
address: '75 Pine St',
|
|
48
|
+
capacity: 120,
|
|
49
|
+
managerId: 'u7',
|
|
50
|
+
createdAt: '2024-04-01T00:00:00Z',
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
id: 'loc5',
|
|
54
|
+
name: 'East Distribution Center',
|
|
55
|
+
type: 'warehouse',
|
|
56
|
+
status: 'active',
|
|
57
|
+
city: 'Miami',
|
|
58
|
+
state: 'FL',
|
|
59
|
+
address: '1200 Port Blvd',
|
|
60
|
+
capacity: 1500,
|
|
61
|
+
managerId: 'u2',
|
|
62
|
+
createdAt: '2024-05-01T00:00:00Z',
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
id: 'loc6',
|
|
66
|
+
name: 'Harbor Retail',
|
|
67
|
+
type: 'retail',
|
|
68
|
+
status: 'inactive',
|
|
69
|
+
city: 'San Francisco',
|
|
70
|
+
state: 'CA',
|
|
71
|
+
address: '45 Pier Ave',
|
|
72
|
+
capacity: 40,
|
|
73
|
+
managerId: 'u5',
|
|
74
|
+
createdAt: '2024-06-01T00:00:00Z',
|
|
75
|
+
},
|
|
76
|
+
];
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import type { ConversationState } from '../types/conversation';
|
|
2
|
+
import type { MessageState } from '../types/message';
|
|
3
|
+
|
|
4
|
+
export const mockConversations: ConversationState[] = [
|
|
5
|
+
{
|
|
6
|
+
id: 'conv1',
|
|
7
|
+
participantIds: ['u1', 'u2'],
|
|
8
|
+
lastMessage: 'Sure, I will review the report today.',
|
|
9
|
+
lastMessageAt: '2024-07-10T14:30:00Z',
|
|
10
|
+
unreadCount: 2,
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
id: 'conv2',
|
|
14
|
+
participantIds: ['u1', 'u5'],
|
|
15
|
+
lastMessage: 'The new store layout looks great!',
|
|
16
|
+
lastMessageAt: '2024-07-10T11:15:00Z',
|
|
17
|
+
unreadCount: 0,
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
id: 'conv3',
|
|
21
|
+
participantIds: ['u2', 'u5', 'u6'],
|
|
22
|
+
lastMessage: 'Meeting moved to 3pm tomorrow.',
|
|
23
|
+
lastMessageAt: '2024-07-09T17:45:00Z',
|
|
24
|
+
unreadCount: 1,
|
|
25
|
+
},
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
export const mockMessages: MessageState[] = [
|
|
29
|
+
// conv1: Alice & Bob — extended conversation (~20 messages for Feed pagination)
|
|
30
|
+
{
|
|
31
|
+
id: 'msg-c1-01',
|
|
32
|
+
conversationId: 'conv1',
|
|
33
|
+
senderId: 'u1',
|
|
34
|
+
text: 'Good morning Bob! Quick question about the warehouse.',
|
|
35
|
+
sentAt: '2024-07-10T09:00:00Z',
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
id: 'msg-c1-02',
|
|
39
|
+
conversationId: 'conv1',
|
|
40
|
+
senderId: 'u2',
|
|
41
|
+
text: 'Morning Alice! What do you need?',
|
|
42
|
+
sentAt: '2024-07-10T09:05:00Z',
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
id: 'msg-c1-03',
|
|
46
|
+
conversationId: 'conv1',
|
|
47
|
+
senderId: 'u1',
|
|
48
|
+
text: 'Do we have the updated capacity numbers for the Chicago warehouse?',
|
|
49
|
+
sentAt: '2024-07-10T09:10:00Z',
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
id: 'msg-c1-04',
|
|
53
|
+
conversationId: 'conv1',
|
|
54
|
+
senderId: 'u2',
|
|
55
|
+
text: 'Let me check. I think the facilities team sent those over last week.',
|
|
56
|
+
sentAt: '2024-07-10T09:15:00Z',
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
id: 'msg-c1-05',
|
|
60
|
+
conversationId: 'conv1',
|
|
61
|
+
senderId: 'u2',
|
|
62
|
+
text: 'Found it — Chicago is at 85% capacity right now. They expanded the east wing.',
|
|
63
|
+
sentAt: '2024-07-10T09:25:00Z',
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
id: 'msg-c1-06',
|
|
67
|
+
conversationId: 'conv1',
|
|
68
|
+
senderId: 'u1',
|
|
69
|
+
text: 'That is higher than I expected. Are we planning any overflow to the Detroit facility?',
|
|
70
|
+
sentAt: '2024-07-10T09:30:00Z',
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
id: 'msg-c1-07',
|
|
74
|
+
conversationId: 'conv1',
|
|
75
|
+
senderId: 'u2',
|
|
76
|
+
text: 'Yeah, Frank mentioned routing some of the Q3 shipments there. I will get the details.',
|
|
77
|
+
sentAt: '2024-07-10T09:40:00Z',
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
id: 'msg-c1-08',
|
|
81
|
+
conversationId: 'conv1',
|
|
82
|
+
senderId: 'u1',
|
|
83
|
+
text: 'Perfect. Can you also pull the inventory report while you are at it?',
|
|
84
|
+
sentAt: '2024-07-10T09:45:00Z',
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
id: 'msg-c1-09',
|
|
88
|
+
conversationId: 'conv1',
|
|
89
|
+
senderId: 'u2',
|
|
90
|
+
text: 'Sure thing. I will have both ready by lunch.',
|
|
91
|
+
sentAt: '2024-07-10T09:50:00Z',
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
id: 'msg-c1-10',
|
|
95
|
+
conversationId: 'conv1',
|
|
96
|
+
senderId: 'u1',
|
|
97
|
+
text: 'Thanks Bob. Also, did the new barcode scanners arrive?',
|
|
98
|
+
sentAt: '2024-07-10T10:15:00Z',
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
id: 'msg-c1-11',
|
|
102
|
+
conversationId: 'conv1',
|
|
103
|
+
senderId: 'u2',
|
|
104
|
+
text: 'They came in yesterday. The team is setting them up now.',
|
|
105
|
+
sentAt: '2024-07-10T10:20:00Z',
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
id: 'msg-c1-12',
|
|
109
|
+
conversationId: 'conv1',
|
|
110
|
+
senderId: 'u1',
|
|
111
|
+
text: 'Great. Make sure the firmware is updated before they go live.',
|
|
112
|
+
sentAt: '2024-07-10T10:30:00Z',
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
id: 'msg-c1-13',
|
|
116
|
+
conversationId: 'conv1',
|
|
117
|
+
senderId: 'u2',
|
|
118
|
+
text: 'Already on it. IT is handling the firmware updates this afternoon.',
|
|
119
|
+
sentAt: '2024-07-10T10:45:00Z',
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
id: 'msg-c1-14',
|
|
123
|
+
conversationId: 'conv1',
|
|
124
|
+
senderId: 'u1',
|
|
125
|
+
text: 'One more thing — the safety inspection for the loading dock is next Tuesday.',
|
|
126
|
+
sentAt: '2024-07-10T11:00:00Z',
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
id: 'msg-c1-15',
|
|
130
|
+
conversationId: 'conv1',
|
|
131
|
+
senderId: 'u2',
|
|
132
|
+
text: 'Got it on the calendar. I will make sure the dock area is prepped.',
|
|
133
|
+
sentAt: '2024-07-10T11:15:00Z',
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
id: 'msg-c1-16',
|
|
137
|
+
conversationId: 'conv1',
|
|
138
|
+
senderId: 'u1',
|
|
139
|
+
text: 'How is the Chicago shipment going by the way?',
|
|
140
|
+
sentAt: '2024-07-10T13:00:00Z',
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
id: 'msg-c1-17',
|
|
144
|
+
conversationId: 'conv1',
|
|
145
|
+
senderId: 'u2',
|
|
146
|
+
text: 'Running a bit behind. Truck was delayed but should arrive by 3pm.',
|
|
147
|
+
sentAt: '2024-07-10T13:15:00Z',
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
id: 'msg-c1-18',
|
|
151
|
+
conversationId: 'conv1',
|
|
152
|
+
senderId: 'u1',
|
|
153
|
+
text: 'OK keep me posted. Hey did you get a chance to look at the warehouse inventory report?',
|
|
154
|
+
sentAt: '2024-07-10T14:00:00Z',
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
id: 'msg-c1-19',
|
|
158
|
+
conversationId: 'conv1',
|
|
159
|
+
senderId: 'u2',
|
|
160
|
+
text: 'Not yet, been busy with the Chicago shipment all morning.',
|
|
161
|
+
sentAt: '2024-07-10T14:10:00Z',
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
id: 'msg-c1-20',
|
|
165
|
+
conversationId: 'conv1',
|
|
166
|
+
senderId: 'u1',
|
|
167
|
+
text: 'No worries, just need it by end of week. Let me know if you have questions.',
|
|
168
|
+
sentAt: '2024-07-10T14:20:00Z',
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
id: 'msg-c1-21',
|
|
172
|
+
conversationId: 'conv1',
|
|
173
|
+
senderId: 'u2',
|
|
174
|
+
text: 'Sure, I will review the report today.',
|
|
175
|
+
sentAt: '2024-07-10T14:30:00Z',
|
|
176
|
+
},
|
|
177
|
+
|
|
178
|
+
// conv2: Alice & Eva
|
|
179
|
+
{
|
|
180
|
+
id: 'msg5',
|
|
181
|
+
conversationId: 'conv2',
|
|
182
|
+
senderId: 'u5',
|
|
183
|
+
text: 'Alice, I finished the floor plan for the new Main Street layout.',
|
|
184
|
+
sentAt: '2024-07-10T10:30:00Z',
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
id: 'msg6',
|
|
188
|
+
conversationId: 'conv2',
|
|
189
|
+
senderId: 'u1',
|
|
190
|
+
text: 'That was fast! Can you share the mockups?',
|
|
191
|
+
sentAt: '2024-07-10T10:45:00Z',
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
id: 'msg7',
|
|
195
|
+
conversationId: 'conv2',
|
|
196
|
+
senderId: 'u5',
|
|
197
|
+
text: 'Just uploaded them to the shared drive. Check the Locations folder.',
|
|
198
|
+
sentAt: '2024-07-10T11:00:00Z',
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
id: 'msg8',
|
|
202
|
+
conversationId: 'conv2',
|
|
203
|
+
senderId: 'u1',
|
|
204
|
+
text: 'The new store layout looks great!',
|
|
205
|
+
sentAt: '2024-07-10T11:15:00Z',
|
|
206
|
+
},
|
|
207
|
+
|
|
208
|
+
// conv3: Bob, Eva & Frank
|
|
209
|
+
{
|
|
210
|
+
id: 'msg9',
|
|
211
|
+
conversationId: 'conv3',
|
|
212
|
+
senderId: 'u2',
|
|
213
|
+
text: 'Team, we need to discuss the Q3 distribution plan.',
|
|
214
|
+
sentAt: '2024-07-09T16:00:00Z',
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
id: 'msg10',
|
|
218
|
+
conversationId: 'conv3',
|
|
219
|
+
senderId: 'u6',
|
|
220
|
+
text: 'I can put together the shipping estimates by tomorrow.',
|
|
221
|
+
sentAt: '2024-07-09T16:30:00Z',
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
id: 'msg11',
|
|
225
|
+
conversationId: 'conv3',
|
|
226
|
+
senderId: 'u5',
|
|
227
|
+
text: 'Great. Let us sync tomorrow afternoon then.',
|
|
228
|
+
sentAt: '2024-07-09T17:00:00Z',
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
id: 'msg12',
|
|
232
|
+
conversationId: 'conv3',
|
|
233
|
+
senderId: 'u2',
|
|
234
|
+
text: 'Meeting moved to 3pm tomorrow.',
|
|
235
|
+
sentAt: '2024-07-09T17:45:00Z',
|
|
236
|
+
},
|
|
237
|
+
];
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import type { UserState } from '../types/user';
|
|
2
|
+
|
|
3
|
+
export const mockUsers: UserState[] = [
|
|
4
|
+
{
|
|
5
|
+
id: 'u1',
|
|
6
|
+
firstName: 'Alice',
|
|
7
|
+
lastName: 'Johnson',
|
|
8
|
+
email: 'alice@example.com',
|
|
9
|
+
role: 'admin',
|
|
10
|
+
status: 'active',
|
|
11
|
+
avatarUrl: '',
|
|
12
|
+
createdAt: '2024-01-15T10:00:00Z',
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
id: 'u2',
|
|
16
|
+
firstName: 'Bob',
|
|
17
|
+
lastName: 'Smith',
|
|
18
|
+
email: 'bob@example.com',
|
|
19
|
+
role: 'manager',
|
|
20
|
+
status: 'active',
|
|
21
|
+
avatarUrl: '',
|
|
22
|
+
createdAt: '2024-02-20T14:30:00Z',
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
id: 'u3',
|
|
26
|
+
firstName: 'Carol',
|
|
27
|
+
lastName: 'Davis',
|
|
28
|
+
email: 'carol@example.com',
|
|
29
|
+
role: 'member',
|
|
30
|
+
status: 'active',
|
|
31
|
+
avatarUrl: '',
|
|
32
|
+
createdAt: '2024-03-10T09:15:00Z',
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
id: 'u4',
|
|
36
|
+
firstName: 'Dan',
|
|
37
|
+
lastName: 'Wilson',
|
|
38
|
+
email: 'dan@example.com',
|
|
39
|
+
role: 'member',
|
|
40
|
+
status: 'inactive',
|
|
41
|
+
avatarUrl: '',
|
|
42
|
+
createdAt: '2024-03-25T16:45:00Z',
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
id: 'u5',
|
|
46
|
+
firstName: 'Eva',
|
|
47
|
+
lastName: 'Martinez',
|
|
48
|
+
email: 'eva@example.com',
|
|
49
|
+
role: 'manager',
|
|
50
|
+
status: 'active',
|
|
51
|
+
avatarUrl: '',
|
|
52
|
+
createdAt: '2024-04-05T11:00:00Z',
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
id: 'u6',
|
|
56
|
+
firstName: 'Frank',
|
|
57
|
+
lastName: 'Brown',
|
|
58
|
+
email: 'frank@example.com',
|
|
59
|
+
role: 'member',
|
|
60
|
+
status: 'active',
|
|
61
|
+
avatarUrl: '',
|
|
62
|
+
createdAt: '2024-05-12T08:30:00Z',
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
id: 'u7',
|
|
66
|
+
firstName: 'Grace',
|
|
67
|
+
lastName: 'Lee',
|
|
68
|
+
email: 'grace@example.com',
|
|
69
|
+
role: 'admin',
|
|
70
|
+
status: 'active',
|
|
71
|
+
avatarUrl: '',
|
|
72
|
+
createdAt: '2024-06-01T13:20:00Z',
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
id: 'u8',
|
|
76
|
+
firstName: 'Henry',
|
|
77
|
+
lastName: 'Taylor',
|
|
78
|
+
email: 'henry@example.com',
|
|
79
|
+
role: 'member',
|
|
80
|
+
status: 'inactive',
|
|
81
|
+
avatarUrl: '',
|
|
82
|
+
createdAt: '2024-06-18T15:10:00Z',
|
|
83
|
+
},
|
|
84
|
+
];
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Model } from 'mvc-kit';
|
|
2
|
+
import type { ValidationErrors } from 'mvc-kit';
|
|
3
|
+
import type { LocationState } from '../types/location';
|
|
4
|
+
|
|
5
|
+
export interface LocationFormState {
|
|
6
|
+
name: string;
|
|
7
|
+
type: LocationState['type'];
|
|
8
|
+
city: string;
|
|
9
|
+
state: string;
|
|
10
|
+
address: string;
|
|
11
|
+
capacity: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export class LocationFormModel extends Model<LocationFormState> {
|
|
15
|
+
setName(name: string) { this.set({ name }); }
|
|
16
|
+
setType(type: LocationFormState['type']) { this.set({ type }); }
|
|
17
|
+
setCity(city: string) { this.set({ city }); }
|
|
18
|
+
setStateName(state: string) { this.set({ state }); }
|
|
19
|
+
setAddress(address: string) { this.set({ address }); }
|
|
20
|
+
setCapacity(capacity: number) { this.set({ capacity }); }
|
|
21
|
+
|
|
22
|
+
protected validate(state: LocationFormState): ValidationErrors<LocationFormState> {
|
|
23
|
+
const errors: Partial<Record<keyof LocationFormState, string>> = {};
|
|
24
|
+
if (!state.name.trim()) errors.name = 'Name is required';
|
|
25
|
+
if (!state.city.trim()) errors.city = 'City is required';
|
|
26
|
+
if (!state.state.trim()) errors.state = 'State is required';
|
|
27
|
+
if (!state.address.trim()) errors.address = 'Address is required';
|
|
28
|
+
if (state.capacity <= 0) errors.capacity = 'Must be greater than 0';
|
|
29
|
+
return errors;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Model } from 'mvc-kit';
|
|
2
|
+
import type { ValidationErrors } from 'mvc-kit';
|
|
3
|
+
|
|
4
|
+
export interface LoginFormState {
|
|
5
|
+
email: string;
|
|
6
|
+
password: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export class LoginFormModel extends Model<LoginFormState> {
|
|
10
|
+
setEmail(email: string) { this.set({ email }); }
|
|
11
|
+
setPassword(password: string) { this.set({ password }); }
|
|
12
|
+
|
|
13
|
+
protected validate(state: LoginFormState): ValidationErrors<LoginFormState> {
|
|
14
|
+
const errors: Partial<Record<keyof LoginFormState, string>> = {};
|
|
15
|
+
if (!state.email.includes('@')) errors.email = 'Valid email required';
|
|
16
|
+
if (state.password.length < 6) errors.password = 'Must be at least 6 characters';
|
|
17
|
+
return errors;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Resource, singleton } from 'mvc-kit';
|
|
2
|
+
import type { UserState } from '../types/user';
|
|
3
|
+
import { UserService } from '../services/UserService';
|
|
4
|
+
|
|
5
|
+
export class UsersResource extends Resource<UserState> {
|
|
6
|
+
private api = singleton(UserService);
|
|
7
|
+
|
|
8
|
+
async loadAll() {
|
|
9
|
+
const data = await this.api.getAll(this.disposeSignal);
|
|
10
|
+
this.reset(data);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Service, HttpError } from 'mvc-kit';
|
|
2
|
+
import type { UserState } from '../types/user';
|
|
3
|
+
import { mockUsers } from '../mock/users';
|
|
4
|
+
import { mockFetch } from '../mock/delay';
|
|
5
|
+
|
|
6
|
+
export class AuthService extends Service {
|
|
7
|
+
async login(email: string, password: string, signal?: AbortSignal): Promise<UserState> {
|
|
8
|
+
if (!email.includes('@') || password.length < 6) {
|
|
9
|
+
throw new HttpError(401, 'Invalid credentials');
|
|
10
|
+
}
|
|
11
|
+
// Return Alice as the logged-in user
|
|
12
|
+
return mockFetch({ ...mockUsers[0] }, 400, signal);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async logout(signal?: AbortSignal): Promise<void> {
|
|
16
|
+
await mockFetch(undefined, 200, signal);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Service, HttpError } from 'mvc-kit';
|
|
2
|
+
import type { LocationState } from '../types/location';
|
|
3
|
+
import { mockLocations } from '../mock/locations';
|
|
4
|
+
import { mockFetch } from '../mock/delay';
|
|
5
|
+
|
|
6
|
+
export class LocationService extends Service {
|
|
7
|
+
async getAll(signal?: AbortSignal): Promise<LocationState[]> {
|
|
8
|
+
return mockFetch(mockLocations.map(l => ({ ...l })), 300, signal);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
async getById(id: string, signal?: AbortSignal): Promise<LocationState> {
|
|
12
|
+
const location = mockLocations.find(l => l.id === id);
|
|
13
|
+
if (!location) throw new HttpError(404, 'Location not found');
|
|
14
|
+
return mockFetch({ ...location }, 200, signal);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async update(id: string, data: Partial<LocationState>, signal?: AbortSignal): Promise<LocationState> {
|
|
18
|
+
const idx = mockLocations.findIndex(l => l.id === id);
|
|
19
|
+
if (idx === -1) throw new HttpError(404, 'Location not found');
|
|
20
|
+
Object.assign(mockLocations[idx], data);
|
|
21
|
+
return mockFetch({ ...mockLocations[idx] }, 400, signal);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { Service } from 'mvc-kit';
|
|
2
|
+
import type { FeedPage } from 'mvc-kit';
|
|
3
|
+
import type { ConversationState } from '../types/conversation';
|
|
4
|
+
import type { MessageState } from '../types/message';
|
|
5
|
+
import { mockConversations, mockMessages } from '../mock/messages';
|
|
6
|
+
import { mockFetch } from '../mock/delay';
|
|
7
|
+
|
|
8
|
+
export class MessageService extends Service {
|
|
9
|
+
async getConversations(userId: string, signal?: AbortSignal): Promise<ConversationState[]> {
|
|
10
|
+
const conversations = mockConversations
|
|
11
|
+
.filter(c => c.participantIds.includes(userId))
|
|
12
|
+
.map(c => ({ ...c }));
|
|
13
|
+
return mockFetch(conversations, 300, signal);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async getMessages(
|
|
17
|
+
conversationId: string,
|
|
18
|
+
signal?: AbortSignal,
|
|
19
|
+
options?: { cursor?: string | null; limit?: number },
|
|
20
|
+
): Promise<FeedPage<MessageState>> {
|
|
21
|
+
const limit = options?.limit ?? 8;
|
|
22
|
+
|
|
23
|
+
// All messages for this conversation, sorted newest-first
|
|
24
|
+
let messages = mockMessages
|
|
25
|
+
.filter(m => m.conversationId === conversationId)
|
|
26
|
+
.sort((a, b) => new Date(b.sentAt).getTime() - new Date(a.sentAt).getTime());
|
|
27
|
+
|
|
28
|
+
// If cursor provided, filter to messages older than cursor
|
|
29
|
+
if (options?.cursor) {
|
|
30
|
+
const cursorTime = new Date(options.cursor).getTime();
|
|
31
|
+
messages = messages.filter(m => new Date(m.sentAt).getTime() < cursorTime);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const page = messages.slice(0, limit).map(m => ({ ...m }));
|
|
35
|
+
const hasMore = messages.length > limit;
|
|
36
|
+
const cursor = page.length > 0 ? page[page.length - 1].sentAt : null;
|
|
37
|
+
|
|
38
|
+
return mockFetch({ items: page, hasMore, cursor }, 250, signal);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async sendMessage(
|
|
42
|
+
conversationId: string,
|
|
43
|
+
senderId: string,
|
|
44
|
+
text: string,
|
|
45
|
+
signal?: AbortSignal,
|
|
46
|
+
): Promise<MessageState> {
|
|
47
|
+
const message: MessageState = {
|
|
48
|
+
id: `msg-${Date.now()}`,
|
|
49
|
+
conversationId,
|
|
50
|
+
senderId,
|
|
51
|
+
text,
|
|
52
|
+
sentAt: new Date().toISOString(),
|
|
53
|
+
};
|
|
54
|
+
mockMessages.push(message);
|
|
55
|
+
|
|
56
|
+
// Update conversation's last message
|
|
57
|
+
const conv = mockConversations.find(c => c.id === conversationId);
|
|
58
|
+
if (conv) {
|
|
59
|
+
conv.lastMessage = text;
|
|
60
|
+
conv.lastMessageAt = message.sentAt;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return mockFetch(message, 200, signal);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Service, HttpError } from 'mvc-kit';
|
|
2
|
+
import type { UserState } from '../types/user';
|
|
3
|
+
import { mockUsers } from '../mock/users';
|
|
4
|
+
import { mockFetch } from '../mock/delay';
|
|
5
|
+
|
|
6
|
+
export class UserService extends Service {
|
|
7
|
+
async getAll(signal?: AbortSignal): Promise<UserState[]> {
|
|
8
|
+
return mockFetch(mockUsers.map(u => ({ ...u })), 300, signal);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
async getById(id: string, signal?: AbortSignal): Promise<UserState> {
|
|
12
|
+
const user = mockUsers.find(u => u.id === id);
|
|
13
|
+
if (!user) throw new HttpError(404, 'User not found');
|
|
14
|
+
return mockFetch({ ...user }, 200, signal);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async update(id: string, data: Partial<UserState>, signal?: AbortSignal): Promise<UserState> {
|
|
18
|
+
const idx = mockUsers.findIndex(u => u.id === id);
|
|
19
|
+
if (idx === -1) throw new HttpError(404, 'User not found');
|
|
20
|
+
Object.assign(mockUsers[idx], data);
|
|
21
|
+
return mockFetch({ ...mockUsers[idx] }, 300, signal);
|
|
22
|
+
}
|
|
23
|
+
}
|