mvc-kit 2.13.0 → 2.13.2

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.
Files changed (218) hide show
  1. package/BEST_PRACTICES.md +1390 -0
  2. package/agent-config/claude-code/agents/mvc-kit-architect.md +8 -3
  3. package/agent-config/claude-code/skills/{guide → mvc-kit}/SKILL.md +10 -1
  4. package/agent-config/lib/install-claude.mjs +39 -110
  5. package/examples/primitive/channel.ts +109 -0
  6. package/examples/primitive/collection.ts +118 -0
  7. package/examples/primitive/controller.ts +118 -0
  8. package/examples/primitive/counter.ts +108 -0
  9. package/examples/primitive/env.d.ts +1 -0
  10. package/examples/primitive/eventbus.ts +77 -0
  11. package/examples/primitive/feed.ts +162 -0
  12. package/examples/primitive/model.ts +82 -0
  13. package/examples/primitive/pagination.ts +91 -0
  14. package/examples/primitive/pending.ts +189 -0
  15. package/examples/primitive/persistent-collection.ts +116 -0
  16. package/examples/primitive/resource.ts +114 -0
  17. package/examples/primitive/selection.ts +96 -0
  18. package/examples/primitive/sorting.ts +112 -0
  19. package/examples/primitive/timer.ts +58 -0
  20. package/examples/primitive/trackable.ts +225 -0
  21. package/examples/primitive/tsconfig.json +20 -0
  22. package/examples/primitive/viewmodel-service.ts +161 -0
  23. package/examples/react/AuthExample/index.html +12 -0
  24. package/examples/react/AuthExample/src/App.tsx +29 -0
  25. package/examples/react/AuthExample/src/components/AdminPage.tsx +51 -0
  26. package/examples/react/AuthExample/src/components/AppHeader.tsx +32 -0
  27. package/examples/react/AuthExample/src/components/AuthGuard.tsx +50 -0
  28. package/examples/react/AuthExample/src/components/AuthScreen.tsx +181 -0
  29. package/examples/react/AuthExample/src/components/DashboardPage.tsx +41 -0
  30. package/examples/react/AuthExample/src/components/ProfilePage.tsx +44 -0
  31. package/examples/react/AuthExample/src/components/Toast.tsx +41 -0
  32. package/examples/react/AuthExample/src/env.d.ts +10 -0
  33. package/examples/react/AuthExample/src/events/AppEventBus.ts +7 -0
  34. package/examples/react/AuthExample/src/main.tsx +10 -0
  35. package/examples/react/AuthExample/src/mock/api.ts +78 -0
  36. package/examples/react/AuthExample/src/models/LoginFormModel.ts +19 -0
  37. package/examples/react/AuthExample/src/models/RegisterFormModel.ts +25 -0
  38. package/examples/react/AuthExample/src/services/AuthService.ts +21 -0
  39. package/examples/react/AuthExample/src/styles.css +445 -0
  40. package/examples/react/AuthExample/src/types/auth.ts +12 -0
  41. package/examples/react/AuthExample/src/viewmodels/AuthViewModel.ts +111 -0
  42. package/examples/react/AuthExample/tsconfig.json +22 -0
  43. package/examples/react/AuthExample/vite.config.ts +18 -0
  44. package/examples/react/ComplexApp/index.html +12 -0
  45. package/examples/react/ComplexApp/src/App.tsx +17 -0
  46. package/examples/react/ComplexApp/src/channels/ActivityChannel.ts +24 -0
  47. package/examples/react/ComplexApp/src/channels/DashboardChannel.ts +26 -0
  48. package/examples/react/ComplexApp/src/channels/ErrorsChannel.ts +5 -0
  49. package/examples/react/ComplexApp/src/channels/LatencyChannel.ts +5 -0
  50. package/examples/react/ComplexApp/src/channels/OrdersChannel.ts +5 -0
  51. package/examples/react/ComplexApp/src/channels/RevenueChannel.ts +5 -0
  52. package/examples/react/ComplexApp/src/channels/TrafficChannel.ts +5 -0
  53. package/examples/react/ComplexApp/src/channels/UsersMetricChannel.ts +5 -0
  54. package/examples/react/ComplexApp/src/collections/DashboardCollection.ts +6 -0
  55. package/examples/react/ComplexApp/src/collections/ErrorsCollection.ts +3 -0
  56. package/examples/react/ComplexApp/src/collections/LatencyCollection.ts +3 -0
  57. package/examples/react/ComplexApp/src/collections/OrdersCollection.ts +3 -0
  58. package/examples/react/ComplexApp/src/collections/RevenueCollection.ts +3 -0
  59. package/examples/react/ComplexApp/src/collections/TrafficCollection.ts +3 -0
  60. package/examples/react/ComplexApp/src/collections/UsersMetricCollection.ts +3 -0
  61. package/examples/react/ComplexApp/src/components/activity/ActivityFeed.tsx +31 -0
  62. package/examples/react/ComplexApp/src/components/activity/ActivityItemRow.tsx +35 -0
  63. package/examples/react/ComplexApp/src/components/dashboard/DashboardCard.tsx +37 -0
  64. package/examples/react/ComplexApp/src/components/dashboard/DashboardPage.tsx +34 -0
  65. package/examples/react/ComplexApp/src/components/layout/Navbar.tsx +32 -0
  66. package/examples/react/ComplexApp/src/components/layout/SocialFeedPanel.tsx +57 -0
  67. package/examples/react/ComplexApp/src/components/shared/Spinner.tsx +3 -0
  68. package/examples/react/ComplexApp/src/components/shared/StatusIndicator.tsx +13 -0
  69. package/examples/react/ComplexApp/src/components/shared/Toast.tsx +40 -0
  70. package/examples/react/ComplexApp/src/env.d.ts +10 -0
  71. package/examples/react/ComplexApp/src/events/AppEventBus.ts +7 -0
  72. package/examples/react/ComplexApp/src/main.tsx +10 -0
  73. package/examples/react/ComplexApp/src/mock-remote/MockWebSocket.ts +38 -0
  74. package/examples/react/ComplexApp/src/mock-remote/activity-api.ts +48 -0
  75. package/examples/react/ComplexApp/src/mock-remote/dashboard-generators.ts +45 -0
  76. package/examples/react/ComplexApp/src/mock-remote/delay.ts +18 -0
  77. package/examples/react/ComplexApp/src/mock-remote/social-api.ts +55 -0
  78. package/examples/react/ComplexApp/src/resources/ActivityResource.ts +12 -0
  79. package/examples/react/ComplexApp/src/resources/SocialFeedResource.ts +17 -0
  80. package/examples/react/ComplexApp/src/styles.css +463 -0
  81. package/examples/react/ComplexApp/src/types/activity.ts +8 -0
  82. package/examples/react/ComplexApp/src/types/dashboard.ts +5 -0
  83. package/examples/react/ComplexApp/src/types/social.ts +8 -0
  84. package/examples/react/ComplexApp/src/types/users.ts +6 -0
  85. package/examples/react/ComplexApp/src/viewmodels/ActivityFeedViewModel.ts +68 -0
  86. package/examples/react/ComplexApp/src/viewmodels/AppStateViewModel.ts +26 -0
  87. package/examples/react/ComplexApp/src/viewmodels/DashboardCardViewModel.ts +69 -0
  88. package/examples/react/ComplexApp/src/viewmodels/ErrorsCardViewModel.ts +9 -0
  89. package/examples/react/ComplexApp/src/viewmodels/LatencyCardViewModel.ts +9 -0
  90. package/examples/react/ComplexApp/src/viewmodels/OrdersCardViewModel.ts +9 -0
  91. package/examples/react/ComplexApp/src/viewmodels/RevenueCardViewModel.ts +9 -0
  92. package/examples/react/ComplexApp/src/viewmodels/SocialFeedViewModel.ts +39 -0
  93. package/examples/react/ComplexApp/src/viewmodels/TrafficCardViewModel.ts +9 -0
  94. package/examples/react/ComplexApp/src/viewmodels/UsersMetricCardViewModel.ts +9 -0
  95. package/examples/react/ComplexApp/tsconfig.json +22 -0
  96. package/examples/react/ComplexApp/vite.config.ts +18 -0
  97. package/examples/react/FullApp/index.html +12 -0
  98. package/examples/react/FullApp/src/App.tsx +28 -0
  99. package/examples/react/FullApp/src/collections/ConversationsCollection.ts +4 -0
  100. package/examples/react/FullApp/src/collections/LocationsCollection.ts +4 -0
  101. package/examples/react/FullApp/src/components/auth/LoginPage.tsx +80 -0
  102. package/examples/react/FullApp/src/components/dashboard/DashboardPage.tsx +29 -0
  103. package/examples/react/FullApp/src/components/dashboard/RecentActivityCard.tsx +35 -0
  104. package/examples/react/FullApp/src/components/dashboard/StatsCard.tsx +19 -0
  105. package/examples/react/FullApp/src/components/layout/AppShell.tsx +31 -0
  106. package/examples/react/FullApp/src/components/layout/Header.tsx +25 -0
  107. package/examples/react/FullApp/src/components/layout/Sidebar.tsx +29 -0
  108. package/examples/react/FullApp/src/components/locations/LocationFilters.tsx +60 -0
  109. package/examples/react/FullApp/src/components/locations/LocationForm.tsx +112 -0
  110. package/examples/react/FullApp/src/components/locations/LocationProfilePage.tsx +81 -0
  111. package/examples/react/FullApp/src/components/locations/LocationsPage.tsx +127 -0
  112. package/examples/react/FullApp/src/components/messaging/ConversationList.tsx +59 -0
  113. package/examples/react/FullApp/src/components/messaging/MessageBubble.tsx +22 -0
  114. package/examples/react/FullApp/src/components/messaging/MessageThread.tsx +100 -0
  115. package/examples/react/FullApp/src/components/messaging/MessagingPage.tsx +52 -0
  116. package/examples/react/FullApp/src/components/shared/ErrorBanner.tsx +3 -0
  117. package/examples/react/FullApp/src/components/shared/Spinner.tsx +7 -0
  118. package/examples/react/FullApp/src/components/shared/Toast.tsx +41 -0
  119. package/examples/react/FullApp/src/components/users/UserFilters.tsx +59 -0
  120. package/examples/react/FullApp/src/components/users/UsersPage.tsx +80 -0
  121. package/examples/react/FullApp/src/components/users/UsersTable.tsx +52 -0
  122. package/examples/react/FullApp/src/env.d.ts +10 -0
  123. package/examples/react/FullApp/src/events/AppEventBus.ts +7 -0
  124. package/examples/react/FullApp/src/main.tsx +10 -0
  125. package/examples/react/FullApp/src/mock/delay.ts +21 -0
  126. package/examples/react/FullApp/src/mock/locations.ts +76 -0
  127. package/examples/react/FullApp/src/mock/messages.ts +237 -0
  128. package/examples/react/FullApp/src/mock/users.ts +84 -0
  129. package/examples/react/FullApp/src/models/LocationFormModel.ts +31 -0
  130. package/examples/react/FullApp/src/models/LoginFormModel.ts +19 -0
  131. package/examples/react/FullApp/src/resources/UsersResource.ts +12 -0
  132. package/examples/react/FullApp/src/services/AuthService.ts +18 -0
  133. package/examples/react/FullApp/src/services/LocationService.ts +23 -0
  134. package/examples/react/FullApp/src/services/MessageService.ts +65 -0
  135. package/examples/react/FullApp/src/services/UserService.ts +23 -0
  136. package/examples/react/FullApp/src/styles.css +767 -0
  137. package/examples/react/FullApp/src/types/conversation.ts +7 -0
  138. package/examples/react/FullApp/src/types/location.ts +12 -0
  139. package/examples/react/FullApp/src/types/message.ts +7 -0
  140. package/examples/react/FullApp/src/types/user.ts +10 -0
  141. package/examples/react/FullApp/src/viewmodels/AuthViewModel.ts +51 -0
  142. package/examples/react/FullApp/src/viewmodels/ConversationsViewModel.ts +89 -0
  143. package/examples/react/FullApp/src/viewmodels/DashboardViewModel.ts +56 -0
  144. package/examples/react/FullApp/src/viewmodels/LocationProfileViewModel.ts +81 -0
  145. package/examples/react/FullApp/src/viewmodels/LocationsViewModel.ts +113 -0
  146. package/examples/react/FullApp/src/viewmodels/MessageThreadViewModel.ts +83 -0
  147. package/examples/react/FullApp/src/viewmodels/UsersViewModel.ts +88 -0
  148. package/examples/react/FullApp/tsconfig.json +22 -0
  149. package/examples/react/FullApp/vite.config.ts +18 -0
  150. package/examples/react/WorkerApp/index.html +12 -0
  151. package/examples/react/WorkerApp/src/App.tsx +24 -0
  152. package/examples/react/WorkerApp/src/channels/MessagingChannel.ts +46 -0
  153. package/examples/react/WorkerApp/src/channels/WorkerStatusChannel.ts +35 -0
  154. package/examples/react/WorkerApp/src/components/auth/LoginPage.tsx +60 -0
  155. package/examples/react/WorkerApp/src/components/layout/AppShell.tsx +31 -0
  156. package/examples/react/WorkerApp/src/components/layout/Header.tsx +23 -0
  157. package/examples/react/WorkerApp/src/components/layout/Sidebar.tsx +28 -0
  158. package/examples/react/WorkerApp/src/components/messaging/ComposeBar.tsx +33 -0
  159. package/examples/react/WorkerApp/src/components/messaging/ConversationList.tsx +59 -0
  160. package/examples/react/WorkerApp/src/components/messaging/MessageBubble.tsx +45 -0
  161. package/examples/react/WorkerApp/src/components/messaging/MessageThread.tsx +93 -0
  162. package/examples/react/WorkerApp/src/components/messaging/MessagingPage.tsx +53 -0
  163. package/examples/react/WorkerApp/src/components/shared/ErrorBanner.tsx +3 -0
  164. package/examples/react/WorkerApp/src/components/shared/PendingBanner.tsx +37 -0
  165. package/examples/react/WorkerApp/src/components/shared/Spinner.tsx +7 -0
  166. package/examples/react/WorkerApp/src/components/shared/Toast.tsx +41 -0
  167. package/examples/react/WorkerApp/src/components/shift/ShiftPage.tsx +98 -0
  168. package/examples/react/WorkerApp/src/components/shift/ShiftTimer.tsx +24 -0
  169. package/examples/react/WorkerApp/src/components/shift/SiteSelector.tsx +27 -0
  170. package/examples/react/WorkerApp/src/components/sites/SiteFilters.tsx +61 -0
  171. package/examples/react/WorkerApp/src/components/sites/SitesPage.tsx +102 -0
  172. package/examples/react/WorkerApp/src/env.d.ts +10 -0
  173. package/examples/react/WorkerApp/src/events/AppEventBus.ts +7 -0
  174. package/examples/react/WorkerApp/src/main.tsx +10 -0
  175. package/examples/react/WorkerApp/src/mock/MockWebSocket.ts +38 -0
  176. package/examples/react/WorkerApp/src/mock/delay.ts +31 -0
  177. package/examples/react/WorkerApp/src/mock/messages.ts +120 -0
  178. package/examples/react/WorkerApp/src/mock/shifts.ts +57 -0
  179. package/examples/react/WorkerApp/src/mock/sites.ts +14 -0
  180. package/examples/react/WorkerApp/src/mock/workers.ts +12 -0
  181. package/examples/react/WorkerApp/src/models/ComposeMessageModel.ts +17 -0
  182. package/examples/react/WorkerApp/src/resources/ConversationsResource.ts +10 -0
  183. package/examples/react/WorkerApp/src/resources/MessagesResource.ts +32 -0
  184. package/examples/react/WorkerApp/src/resources/ShiftResource.ts +73 -0
  185. package/examples/react/WorkerApp/src/resources/SitesResource.ts +11 -0
  186. package/examples/react/WorkerApp/src/resources/WorkersResource.ts +11 -0
  187. package/examples/react/WorkerApp/src/styles.css +756 -0
  188. package/examples/react/WorkerApp/src/types/conversation.ts +7 -0
  189. package/examples/react/WorkerApp/src/types/message.ts +7 -0
  190. package/examples/react/WorkerApp/src/types/shift.ts +13 -0
  191. package/examples/react/WorkerApp/src/types/site.ts +8 -0
  192. package/examples/react/WorkerApp/src/types/worker.ts +8 -0
  193. package/examples/react/WorkerApp/src/viewmodels/AuthViewModel.ts +41 -0
  194. package/examples/react/WorkerApp/src/viewmodels/ConversationsViewModel.ts +83 -0
  195. package/examples/react/WorkerApp/src/viewmodels/MessageThreadViewModel.ts +113 -0
  196. package/examples/react/WorkerApp/src/viewmodels/ShiftViewModel.ts +147 -0
  197. package/examples/react/WorkerApp/src/viewmodels/SitesViewModel.ts +82 -0
  198. package/examples/react/WorkerApp/tsconfig.json +22 -0
  199. package/examples/react/WorkerApp/vite.config.ts +18 -0
  200. package/package.json +4 -2
  201. /package/agent-config/claude-code/skills/{guide → mvc-kit}/anti-patterns.md +0 -0
  202. /package/agent-config/claude-code/skills/{guide → mvc-kit}/api-reference.md +0 -0
  203. /package/agent-config/claude-code/skills/{guide → mvc-kit}/patterns.md +0 -0
  204. /package/agent-config/claude-code/skills/{guide → mvc-kit}/recipes.md +0 -0
  205. /package/agent-config/claude-code/skills/{guide → mvc-kit}/testing.md +0 -0
  206. /package/agent-config/claude-code/skills/{review → mvc-kit-review}/SKILL.md +0 -0
  207. /package/agent-config/claude-code/skills/{review → mvc-kit-review}/checklist.md +0 -0
  208. /package/agent-config/claude-code/skills/{scaffold → mvc-kit-scaffold}/SKILL.md +0 -0
  209. /package/agent-config/claude-code/skills/{scaffold → mvc-kit-scaffold}/templates/channel.md +0 -0
  210. /package/agent-config/claude-code/skills/{scaffold → mvc-kit-scaffold}/templates/collection.md +0 -0
  211. /package/agent-config/claude-code/skills/{scaffold → mvc-kit-scaffold}/templates/controller.md +0 -0
  212. /package/agent-config/claude-code/skills/{scaffold → mvc-kit-scaffold}/templates/eventbus.md +0 -0
  213. /package/agent-config/claude-code/skills/{scaffold → mvc-kit-scaffold}/templates/model.md +0 -0
  214. /package/agent-config/claude-code/skills/{scaffold → mvc-kit-scaffold}/templates/page-component.md +0 -0
  215. /package/agent-config/claude-code/skills/{scaffold → mvc-kit-scaffold}/templates/persistent-collection.md +0 -0
  216. /package/agent-config/claude-code/skills/{scaffold → mvc-kit-scaffold}/templates/resource.md +0 -0
  217. /package/agent-config/claude-code/skills/{scaffold → mvc-kit-scaffold}/templates/service.md +0 -0
  218. /package/agent-config/claude-code/skills/{scaffold → mvc-kit-scaffold}/templates/viewmodel.md +0 -0
@@ -0,0 +1,756 @@
1
+ *, *::before, *::after {
2
+ box-sizing: border-box;
3
+ margin: 0;
4
+ padding: 0;
5
+ }
6
+
7
+ :root {
8
+ --color-bg: #f5f7fa;
9
+ --color-surface: #ffffff;
10
+ --color-primary: #2563eb;
11
+ --color-primary-hover: #1d4ed8;
12
+ --color-text: #1e293b;
13
+ --color-text-secondary: #64748b;
14
+ --color-border: #e2e8f0;
15
+ --color-error: #ef4444;
16
+ --color-success: #22c55e;
17
+ --color-warning: #f59e0b;
18
+ --radius: 8px;
19
+ --sidebar-width: 220px;
20
+ --header-height: 56px;
21
+ }
22
+
23
+ body {
24
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
25
+ background: var(--color-bg);
26
+ color: var(--color-text);
27
+ line-height: 1.5;
28
+ }
29
+
30
+ /* App Layout */
31
+ .app-shell {
32
+ display: flex;
33
+ min-height: 100vh;
34
+ }
35
+
36
+ .sidebar {
37
+ width: var(--sidebar-width);
38
+ background: var(--color-surface);
39
+ border-right: 1px solid var(--color-border);
40
+ padding: 1rem;
41
+ display: flex;
42
+ flex-direction: column;
43
+ position: fixed;
44
+ top: 0;
45
+ left: 0;
46
+ bottom: 0;
47
+ }
48
+
49
+ .sidebar-logo {
50
+ font-size: 1.25rem;
51
+ font-weight: 700;
52
+ color: var(--color-primary);
53
+ margin-bottom: 2rem;
54
+ padding: 0.5rem;
55
+ }
56
+
57
+ .sidebar-nav {
58
+ display: flex;
59
+ flex-direction: column;
60
+ gap: 0.25rem;
61
+ }
62
+
63
+ .sidebar-link {
64
+ display: block;
65
+ padding: 0.625rem 0.75rem;
66
+ border-radius: var(--radius);
67
+ text-decoration: none;
68
+ color: var(--color-text-secondary);
69
+ font-weight: 500;
70
+ transition: background 0.15s, color 0.15s;
71
+ }
72
+
73
+ .sidebar-link:hover {
74
+ background: var(--color-bg);
75
+ color: var(--color-text);
76
+ }
77
+
78
+ .sidebar-link.active {
79
+ background: #dbeafe;
80
+ color: var(--color-primary);
81
+ }
82
+
83
+ .main-area {
84
+ margin-left: var(--sidebar-width);
85
+ flex: 1;
86
+ display: flex;
87
+ flex-direction: column;
88
+ }
89
+
90
+ .header {
91
+ height: var(--header-height);
92
+ background: var(--color-surface);
93
+ border-bottom: 1px solid var(--color-border);
94
+ display: flex;
95
+ align-items: center;
96
+ justify-content: space-between;
97
+ padding: 0 1.5rem;
98
+ position: sticky;
99
+ top: 0;
100
+ z-index: 10;
101
+ }
102
+
103
+ .header-user {
104
+ display: flex;
105
+ align-items: center;
106
+ gap: 0.75rem;
107
+ }
108
+
109
+ .avatar {
110
+ width: 32px;
111
+ height: 32px;
112
+ border-radius: 50%;
113
+ background: var(--color-primary);
114
+ color: white;
115
+ display: flex;
116
+ align-items: center;
117
+ justify-content: center;
118
+ font-weight: 600;
119
+ font-size: 0.75rem;
120
+ }
121
+
122
+ .page-content {
123
+ padding: 1.5rem;
124
+ flex: 1;
125
+ }
126
+
127
+ .page-title {
128
+ font-size: 1.5rem;
129
+ font-weight: 700;
130
+ margin-bottom: 1.5rem;
131
+ }
132
+
133
+ /* Cards */
134
+ .card {
135
+ background: var(--color-surface);
136
+ border: 1px solid var(--color-border);
137
+ border-radius: var(--radius);
138
+ padding: 1.5rem;
139
+ }
140
+
141
+ /* Tables */
142
+ .table-container {
143
+ background: var(--color-surface);
144
+ border: 1px solid var(--color-border);
145
+ border-radius: var(--radius);
146
+ overflow: hidden;
147
+ }
148
+
149
+ table {
150
+ width: 100%;
151
+ border-collapse: collapse;
152
+ }
153
+
154
+ th {
155
+ text-align: left;
156
+ padding: 0.75rem 1rem;
157
+ font-weight: 600;
158
+ font-size: 0.8125rem;
159
+ color: var(--color-text-secondary);
160
+ text-transform: uppercase;
161
+ letter-spacing: 0.05em;
162
+ background: var(--color-bg);
163
+ border-bottom: 1px solid var(--color-border);
164
+ }
165
+
166
+ td {
167
+ padding: 0.75rem 1rem;
168
+ border-bottom: 1px solid var(--color-border);
169
+ font-size: 0.875rem;
170
+ }
171
+
172
+ tr:last-child td {
173
+ border-bottom: none;
174
+ }
175
+
176
+ tr:hover td {
177
+ background: #f8fafc;
178
+ }
179
+
180
+ /* Forms */
181
+ .form-group {
182
+ margin-bottom: 1rem;
183
+ }
184
+
185
+ .form-label {
186
+ display: block;
187
+ font-weight: 500;
188
+ font-size: 0.875rem;
189
+ margin-bottom: 0.375rem;
190
+ color: var(--color-text);
191
+ }
192
+
193
+ .form-input {
194
+ width: 100%;
195
+ padding: 0.5rem 0.75rem;
196
+ border: 1px solid var(--color-border);
197
+ border-radius: var(--radius);
198
+ font-size: 0.875rem;
199
+ outline: none;
200
+ transition: border-color 0.15s;
201
+ }
202
+
203
+ .form-input:focus {
204
+ border-color: var(--color-primary);
205
+ box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.1);
206
+ }
207
+
208
+ .form-select {
209
+ width: 100%;
210
+ padding: 0.5rem 0.75rem;
211
+ border: 1px solid var(--color-border);
212
+ border-radius: var(--radius);
213
+ font-size: 0.875rem;
214
+ outline: none;
215
+ background: white;
216
+ cursor: pointer;
217
+ }
218
+
219
+ /* Buttons */
220
+ .btn {
221
+ display: inline-flex;
222
+ align-items: center;
223
+ justify-content: center;
224
+ padding: 0.5rem 1rem;
225
+ border: none;
226
+ border-radius: var(--radius);
227
+ font-size: 0.875rem;
228
+ font-weight: 500;
229
+ cursor: pointer;
230
+ transition: background 0.15s, opacity 0.15s;
231
+ }
232
+
233
+ .btn:disabled {
234
+ opacity: 0.5;
235
+ cursor: not-allowed;
236
+ }
237
+
238
+ .btn-primary {
239
+ background: var(--color-primary);
240
+ color: white;
241
+ }
242
+
243
+ .btn-primary:hover:not(:disabled) {
244
+ background: var(--color-primary-hover);
245
+ }
246
+
247
+ .btn-secondary {
248
+ background: var(--color-bg);
249
+ color: var(--color-text);
250
+ border: 1px solid var(--color-border);
251
+ }
252
+
253
+ .btn-secondary:hover:not(:disabled) {
254
+ background: var(--color-border);
255
+ }
256
+
257
+ .btn-danger {
258
+ background: var(--color-error);
259
+ color: white;
260
+ }
261
+
262
+ .btn-danger:hover:not(:disabled) {
263
+ background: #dc2626;
264
+ }
265
+
266
+ .btn-sm {
267
+ padding: 0.25rem 0.5rem;
268
+ font-size: 0.75rem;
269
+ }
270
+
271
+ .btn-link {
272
+ background: none;
273
+ border: none;
274
+ color: var(--color-primary);
275
+ cursor: pointer;
276
+ font-size: 0.6875rem;
277
+ text-decoration: underline;
278
+ padding: 0;
279
+ margin-left: 0.375rem;
280
+ }
281
+
282
+ /* Filters */
283
+ .filters {
284
+ display: flex;
285
+ gap: 0.75rem;
286
+ margin-bottom: 1rem;
287
+ flex-wrap: wrap;
288
+ align-items: end;
289
+ }
290
+
291
+ .filter-group {
292
+ display: flex;
293
+ flex-direction: column;
294
+ gap: 0.25rem;
295
+ }
296
+
297
+ .filter-label {
298
+ font-size: 0.75rem;
299
+ font-weight: 500;
300
+ color: var(--color-text-secondary);
301
+ }
302
+
303
+ .filter-input {
304
+ padding: 0.5rem 0.75rem;
305
+ border: 1px solid var(--color-border);
306
+ border-radius: var(--radius);
307
+ font-size: 0.875rem;
308
+ outline: none;
309
+ min-width: 200px;
310
+ }
311
+
312
+ .filter-input:focus {
313
+ border-color: var(--color-primary);
314
+ box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.1);
315
+ }
316
+
317
+ .filter-select {
318
+ padding: 0.5rem 0.75rem;
319
+ border: 1px solid var(--color-border);
320
+ border-radius: var(--radius);
321
+ font-size: 0.875rem;
322
+ background: white;
323
+ cursor: pointer;
324
+ }
325
+
326
+ /* Badges */
327
+ .badge {
328
+ display: inline-block;
329
+ padding: 0.125rem 0.5rem;
330
+ border-radius: 999px;
331
+ font-size: 0.75rem;
332
+ font-weight: 500;
333
+ }
334
+
335
+ .badge-active { background: #dcfce7; color: #166534; }
336
+ .badge-paused { background: #fef3c7; color: #92400e; }
337
+ .badge-completed { background: #e0e7ff; color: #3730a3; }
338
+ .badge-inactive { background: #fee2e2; color: #991b1b; }
339
+ .badge-maintenance { background: #fef3c7; color: #92400e; }
340
+ .badge-admin { background: #e0e7ff; color: #3730a3; }
341
+ .badge-manager { background: #dbeafe; color: #1e40af; }
342
+ .badge-member { background: #f1f5f9; color: #475569; }
343
+
344
+ /* Login Page */
345
+ .login-page {
346
+ min-height: 100vh;
347
+ display: flex;
348
+ align-items: center;
349
+ justify-content: center;
350
+ background: var(--color-bg);
351
+ }
352
+
353
+ .login-card {
354
+ background: var(--color-surface);
355
+ border: 1px solid var(--color-border);
356
+ border-radius: var(--radius);
357
+ padding: 2rem;
358
+ width: 100%;
359
+ max-width: 400px;
360
+ }
361
+
362
+ .login-title {
363
+ font-size: 1.5rem;
364
+ font-weight: 700;
365
+ margin-bottom: 0.5rem;
366
+ text-align: center;
367
+ }
368
+
369
+ .login-subtitle {
370
+ color: var(--color-text-secondary);
371
+ text-align: center;
372
+ margin-bottom: 1.5rem;
373
+ font-size: 0.875rem;
374
+ }
375
+
376
+ /* Shift */
377
+ .shift-timer {
378
+ text-align: center;
379
+ margin-bottom: 1.5rem;
380
+ }
381
+
382
+ .shift-timer-site {
383
+ font-size: 1rem;
384
+ font-weight: 600;
385
+ color: var(--color-text-secondary);
386
+ margin-bottom: 0.75rem;
387
+ }
388
+
389
+ .shift-timer-clock {
390
+ margin-bottom: 0.75rem;
391
+ }
392
+
393
+ .timer-value {
394
+ font-size: 2.5rem;
395
+ font-weight: 700;
396
+ font-variant-numeric: tabular-nums;
397
+ color: var(--color-primary);
398
+ }
399
+
400
+ .timer-label {
401
+ font-size: 0.875rem;
402
+ color: var(--color-text-secondary);
403
+ }
404
+
405
+ .shift-timer-break {
406
+ display: flex;
407
+ align-items: center;
408
+ justify-content: center;
409
+ gap: 0.75rem;
410
+ }
411
+
412
+ .timer-break-value {
413
+ font-size: 0.875rem;
414
+ color: var(--color-text-secondary);
415
+ font-variant-numeric: tabular-nums;
416
+ }
417
+
418
+ .shift-actions {
419
+ display: flex;
420
+ gap: 0.75rem;
421
+ justify-content: center;
422
+ }
423
+
424
+ /* Pending Banner */
425
+ .pending-banner {
426
+ display: flex;
427
+ align-items: flex-start;
428
+ justify-content: space-between;
429
+ padding: 0.625rem 1rem;
430
+ margin-bottom: 1rem;
431
+ background: #fef2f2;
432
+ border: 1px solid #fecaca;
433
+ border-radius: var(--radius);
434
+ font-size: 0.875rem;
435
+ color: #991b1b;
436
+ }
437
+
438
+ .pending-banner-info {
439
+ flex: 1;
440
+ }
441
+
442
+ .pending-banner-list {
443
+ list-style: none;
444
+ margin-top: 0.375rem;
445
+ font-size: 0.8125rem;
446
+ opacity: 0.85;
447
+ }
448
+
449
+ .pending-banner-list li {
450
+ padding: 0.125rem 0;
451
+ }
452
+
453
+ .pending-banner-actions {
454
+ display: flex;
455
+ gap: 0.5rem;
456
+ flex-shrink: 0;
457
+ }
458
+
459
+ /* Messaging */
460
+ .messaging-layout {
461
+ display: flex;
462
+ height: calc(100vh - var(--header-height) - 3rem - 3.5rem);
463
+ gap: 1rem;
464
+ }
465
+
466
+ .conversation-list {
467
+ width: 300px;
468
+ background: var(--color-surface);
469
+ border: 1px solid var(--color-border);
470
+ border-radius: var(--radius);
471
+ display: flex;
472
+ flex-direction: column;
473
+ overflow: hidden;
474
+ flex-shrink: 0;
475
+ }
476
+
477
+ .conversation-list-header {
478
+ padding: 0.75rem 1rem;
479
+ border-bottom: 1px solid var(--color-border);
480
+ }
481
+
482
+ .conversation-list-items {
483
+ flex: 1;
484
+ overflow-y: auto;
485
+ }
486
+
487
+ .conversation-item {
488
+ padding: 0.75rem 1rem;
489
+ cursor: pointer;
490
+ border-bottom: 1px solid var(--color-border);
491
+ transition: background 0.15s;
492
+ }
493
+
494
+ .conversation-item:hover {
495
+ background: #f8fafc;
496
+ }
497
+
498
+ .conversation-item.selected {
499
+ background: #dbeafe;
500
+ border-left: 3px solid var(--color-primary);
501
+ }
502
+
503
+ .conversation-item-header {
504
+ display: flex;
505
+ justify-content: space-between;
506
+ align-items: center;
507
+ }
508
+
509
+ .conversation-item-name {
510
+ font-weight: 500;
511
+ font-size: 0.875rem;
512
+ }
513
+
514
+ .conversation-item-preview {
515
+ color: var(--color-text-secondary);
516
+ font-size: 0.8125rem;
517
+ white-space: nowrap;
518
+ overflow: hidden;
519
+ text-overflow: ellipsis;
520
+ margin-top: 0.25rem;
521
+ }
522
+
523
+ .conversation-unread {
524
+ background: var(--color-primary);
525
+ color: white;
526
+ font-size: 0.6875rem;
527
+ padding: 0.0625rem 0.375rem;
528
+ border-radius: 999px;
529
+ font-weight: 600;
530
+ }
531
+
532
+ .message-thread {
533
+ flex: 1;
534
+ background: var(--color-surface);
535
+ border: 1px solid var(--color-border);
536
+ border-radius: var(--radius);
537
+ display: flex;
538
+ flex-direction: column;
539
+ overflow: hidden;
540
+ }
541
+
542
+ .message-thread-header {
543
+ padding: 0.75rem 1rem;
544
+ border-bottom: 1px solid var(--color-border);
545
+ font-weight: 600;
546
+ }
547
+
548
+ .message-thread-messages {
549
+ flex: 1;
550
+ overflow-y: auto;
551
+ padding: 1rem;
552
+ display: flex;
553
+ flex-direction: column;
554
+ gap: 0.75rem;
555
+ }
556
+
557
+ .message-bubble {
558
+ max-width: 70%;
559
+ padding: 0.625rem 0.875rem;
560
+ border-radius: 12px;
561
+ font-size: 0.875rem;
562
+ line-height: 1.4;
563
+ }
564
+
565
+ .message-bubble.mine {
566
+ align-self: flex-end;
567
+ background: var(--color-primary);
568
+ color: white;
569
+ border-bottom-right-radius: 4px;
570
+ }
571
+
572
+ .message-bubble.theirs {
573
+ align-self: flex-start;
574
+ background: var(--color-bg);
575
+ color: var(--color-text);
576
+ border-bottom-left-radius: 4px;
577
+ }
578
+
579
+ .message-sender {
580
+ font-size: 0.75rem;
581
+ font-weight: 600;
582
+ margin-bottom: 0.125rem;
583
+ }
584
+
585
+ .message-time {
586
+ font-size: 0.6875rem;
587
+ opacity: 0.7;
588
+ margin-top: 0.25rem;
589
+ }
590
+
591
+ .message-status {
592
+ font-style: italic;
593
+ }
594
+
595
+ .message-compose {
596
+ padding: 0.75rem 1rem;
597
+ border-top: 1px solid var(--color-border);
598
+ display: flex;
599
+ gap: 0.5rem;
600
+ }
601
+
602
+ .message-compose input {
603
+ flex: 1;
604
+ padding: 0.5rem 0.75rem;
605
+ border: 1px solid var(--color-border);
606
+ border-radius: var(--radius);
607
+ outline: none;
608
+ font-size: 0.875rem;
609
+ }
610
+
611
+ .message-compose input:focus {
612
+ border-color: var(--color-primary);
613
+ }
614
+
615
+ .message-thread-empty {
616
+ flex: 1;
617
+ display: flex;
618
+ align-items: center;
619
+ justify-content: center;
620
+ color: var(--color-text-secondary);
621
+ background: var(--color-surface);
622
+ border: 1px solid var(--color-border);
623
+ border-radius: var(--radius);
624
+ }
625
+
626
+ /* Empty / Loading / Error states */
627
+ .empty-state {
628
+ text-align: center;
629
+ padding: 3rem 1rem;
630
+ color: var(--color-text-secondary);
631
+ }
632
+
633
+ .error-banner {
634
+ background: #fef2f2;
635
+ border: 1px solid #fecaca;
636
+ color: #991b1b;
637
+ padding: 0.75rem 1rem;
638
+ border-radius: var(--radius);
639
+ margin-bottom: 1rem;
640
+ font-size: 0.875rem;
641
+ }
642
+
643
+ /* Spinner */
644
+ .spinner {
645
+ display: inline-block;
646
+ width: 20px;
647
+ height: 20px;
648
+ border: 2px solid var(--color-border);
649
+ border-top-color: var(--color-primary);
650
+ border-radius: 50%;
651
+ animation: spin 0.6s linear infinite;
652
+ }
653
+
654
+ .spinner-lg {
655
+ width: 32px;
656
+ height: 32px;
657
+ border-width: 3px;
658
+ }
659
+
660
+ .spinner-center {
661
+ display: flex;
662
+ justify-content: center;
663
+ padding: 2rem;
664
+ }
665
+
666
+ @keyframes spin {
667
+ to { transform: rotate(360deg); }
668
+ }
669
+
670
+ /* Toast */
671
+ .toast-container {
672
+ position: fixed;
673
+ bottom: 1.5rem;
674
+ right: 1.5rem;
675
+ z-index: 1000;
676
+ display: flex;
677
+ flex-direction: column;
678
+ gap: 0.5rem;
679
+ }
680
+
681
+ .toast {
682
+ padding: 0.75rem 1rem;
683
+ border-radius: var(--radius);
684
+ font-size: 0.875rem;
685
+ font-weight: 500;
686
+ box-shadow: 0 4px 12px rgba(0,0,0,0.15);
687
+ animation: slideUp 0.3s ease-out;
688
+ min-width: 250px;
689
+ }
690
+
691
+ .toast-success {
692
+ background: #166534;
693
+ color: white;
694
+ }
695
+
696
+ .toast-error {
697
+ background: #991b1b;
698
+ color: white;
699
+ }
700
+
701
+ .toast-info {
702
+ background: #1e40af;
703
+ color: white;
704
+ }
705
+
706
+ @keyframes slideUp {
707
+ from { opacity: 0; transform: translateY(10px); }
708
+ to { opacity: 1; transform: translateY(0); }
709
+ }
710
+
711
+ /* Results bar */
712
+ .results-bar {
713
+ display: flex;
714
+ justify-content: space-between;
715
+ align-items: center;
716
+ margin-bottom: 1rem;
717
+ font-size: 0.875rem;
718
+ color: var(--color-text-secondary);
719
+ }
720
+
721
+ /* Selection bar */
722
+ .selection-bar {
723
+ display: flex;
724
+ align-items: center;
725
+ gap: 0.75rem;
726
+ padding: 0.5rem 1rem;
727
+ margin-bottom: 1rem;
728
+ background: #dbeafe;
729
+ border: 1px solid var(--color-primary);
730
+ border-radius: var(--radius);
731
+ font-size: 0.875rem;
732
+ font-weight: 500;
733
+ color: var(--color-primary);
734
+ }
735
+
736
+ /* Pagination bar */
737
+ .pagination-bar {
738
+ display: flex;
739
+ align-items: center;
740
+ gap: 0.75rem;
741
+ padding: 0.75rem 1rem;
742
+ font-size: 0.875rem;
743
+ color: var(--color-text-secondary);
744
+ }
745
+
746
+ /* Feed end state */
747
+ .feed-end {
748
+ text-align: center;
749
+ padding: 1rem;
750
+ color: var(--color-text-secondary);
751
+ font-size: 0.8125rem;
752
+ }
753
+
754
+ /* CardList in conversation context */
755
+ .conversation-card-list { list-style: none; padding: 0; margin: 0; }
756
+ .conversation-card-list li { list-style: none; }