agentic-dev 0.2.9 → 0.2.11

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 (291) hide show
  1. package/README.md +23 -8
  2. package/bin/agentic-dev.mjs +692 -55
  3. package/lib/scaffold.mjs +109 -6
  4. package/package.json +1 -1
  5. package/client/admin/.dockerignore +0 -3
  6. package/client/admin/.env.example +0 -1
  7. package/client/admin/Dockerfile +0 -16
  8. package/client/admin/Dockerfile.dev +0 -18
  9. package/client/admin/README.md +0 -20
  10. package/client/admin/index.html +0 -12
  11. package/client/admin/package.json +0 -41
  12. package/client/admin/postcss.config.js +0 -6
  13. package/client/admin/scripts/ui-parity-admin-adapter.mjs +0 -65
  14. package/client/admin/src/api/alerts.ts +0 -33
  15. package/client/admin/src/api/client.ts +0 -71
  16. package/client/admin/src/api/orders.ts +0 -33
  17. package/client/admin/src/api/support.ts +0 -11
  18. package/client/admin/src/app/App.tsx +0 -23
  19. package/client/admin/src/auth/AuthProvider.tsx +0 -122
  20. package/client/admin/src/auth/ProtectedRoute.tsx +0 -22
  21. package/client/admin/src/auth/auth-client.ts +0 -38
  22. package/client/admin/src/auth/types.ts +0 -18
  23. package/client/admin/src/components/AdminNotificationsDrawer.tsx +0 -162
  24. package/client/admin/src/components/AdminShell.tsx +0 -76
  25. package/client/admin/src/components/ui/button.tsx +0 -34
  26. package/client/admin/src/components/ui/input.tsx +0 -21
  27. package/client/admin/src/lib/cn.ts +0 -6
  28. package/client/admin/src/lib/specRouteCatalog.json +0 -30
  29. package/client/admin/src/lib/specScreens.json +0 -22
  30. package/client/admin/src/main.tsx +0 -17
  31. package/client/admin/src/pages/AdminDashboardPage.tsx +0 -171
  32. package/client/admin/src/pages/AdminLoginPage.tsx +0 -75
  33. package/client/admin/src/pages/AdminQueuePage.tsx +0 -107
  34. package/client/admin/src/pages/AdminSupportPage.tsx +0 -61
  35. package/client/admin/src/styles/globals.css +0 -17
  36. package/client/admin/src/theme-vars.ts +0 -18
  37. package/client/admin/src/theme.ts +0 -25
  38. package/client/admin/src/vite-env.d.ts +0 -1
  39. package/client/admin/tailwind.config.js +0 -8
  40. package/client/admin/tsconfig.json +0 -25
  41. package/client/admin/vite.config.ts +0 -12
  42. package/client/landing/.dockerignore +0 -3
  43. package/client/landing/.env.example +0 -1
  44. package/client/landing/Dockerfile +0 -16
  45. package/client/landing/Dockerfile.dev +0 -18
  46. package/client/landing/README.md +0 -18
  47. package/client/landing/index.html +0 -12
  48. package/client/landing/package.json +0 -41
  49. package/client/landing/postcss.config.js +0 -6
  50. package/client/landing/scripts/ui-parity-landing-adapter.mjs +0 -65
  51. package/client/landing/src/App.tsx +0 -21
  52. package/client/landing/src/api/catalog.ts +0 -30
  53. package/client/landing/src/api/client.ts +0 -30
  54. package/client/landing/src/auth/AuthProvider.tsx +0 -122
  55. package/client/landing/src/auth/ProtectedRoute.tsx +0 -22
  56. package/client/landing/src/auth/auth-client.ts +0 -38
  57. package/client/landing/src/auth/types.ts +0 -18
  58. package/client/landing/src/components/LandingShell.tsx +0 -34
  59. package/client/landing/src/lib/specRouteCatalog.json +0 -23
  60. package/client/landing/src/lib/specScreens.json +0 -17
  61. package/client/landing/src/main.tsx +0 -17
  62. package/client/landing/src/pages/LandingHomePage.tsx +0 -215
  63. package/client/landing/src/pages/LandingLoginPage.tsx +0 -90
  64. package/client/landing/src/pages/LandingWorkspacePage.tsx +0 -126
  65. package/client/landing/src/styles/globals.css +0 -17
  66. package/client/landing/src/theme-vars.ts +0 -16
  67. package/client/landing/src/theme.ts +0 -21
  68. package/client/landing/src/vite-env.d.ts +0 -1
  69. package/client/landing/tailwind.config.js +0 -8
  70. package/client/landing/tsconfig.json +0 -25
  71. package/client/landing/vite.config.ts +0 -12
  72. package/client/mobile/.dockerignore +0 -2
  73. package/client/mobile/.env.example +0 -1
  74. package/client/mobile/Dockerfile +0 -16
  75. package/client/mobile/Dockerfile.dev +0 -18
  76. package/client/mobile/README.md +0 -19
  77. package/client/mobile/index.html +0 -12
  78. package/client/mobile/package.json +0 -42
  79. package/client/mobile/postcss.config.js +0 -6
  80. package/client/mobile/scripts/ui-parity-mobile-adapter.mjs +0 -67
  81. package/client/mobile/src/App.tsx +0 -1
  82. package/client/mobile/src/api/client.ts +0 -62
  83. package/client/mobile/src/api/fulfillment.ts +0 -55
  84. package/client/mobile/src/api/shipping.ts +0 -56
  85. package/client/mobile/src/app/App.tsx +0 -23
  86. package/client/mobile/src/auth/AuthProvider.tsx +0 -122
  87. package/client/mobile/src/auth/ProtectedRoute.tsx +0 -27
  88. package/client/mobile/src/auth/auth-client.ts +0 -38
  89. package/client/mobile/src/auth/types.ts +0 -18
  90. package/client/mobile/src/components/InShell.tsx +0 -74
  91. package/client/mobile/src/components/ui/button.tsx +0 -35
  92. package/client/mobile/src/components/ui/card.tsx +0 -15
  93. package/client/mobile/src/components/ui/input.tsx +0 -21
  94. package/client/mobile/src/lib/cn.ts +0 -6
  95. package/client/mobile/src/lib/specRouteCatalog.json +0 -26
  96. package/client/mobile/src/lib/specScreens.json +0 -22
  97. package/client/mobile/src/lib/useSpeechRecognitionInput.ts +0 -271
  98. package/client/mobile/src/main.tsx +0 -17
  99. package/client/mobile/src/pages/DashboardPage.tsx +0 -172
  100. package/client/mobile/src/pages/FulfillmentPage.tsx +0 -138
  101. package/client/mobile/src/pages/LoginPage.tsx +0 -74
  102. package/client/mobile/src/pages/ShippingPage.tsx +0 -338
  103. package/client/mobile/src/styles/globals.css +0 -23
  104. package/client/mobile/src/theme-vars.ts +0 -16
  105. package/client/mobile/src/theme.ts +0 -21
  106. package/client/mobile/src/vite-env.d.ts +0 -1
  107. package/client/mobile/tailwind.config.js +0 -8
  108. package/client/mobile/tsconfig.json +0 -25
  109. package/client/mobile/vite.config.ts +0 -12
  110. package/client/web/.dockerignore +0 -3
  111. package/client/web/.env.example +0 -1
  112. package/client/web/Dockerfile +0 -16
  113. package/client/web/Dockerfile.dev +0 -18
  114. package/client/web/README.md +0 -47
  115. package/client/web/index.html +0 -12
  116. package/client/web/package.json +0 -42
  117. package/client/web/postcss.config.js +0 -6
  118. package/client/web/scripts/ui-parity-web-adapter.mjs +0 -66
  119. package/client/web/src/api/client.ts +0 -30
  120. package/client/web/src/api/orders.ts +0 -42
  121. package/client/web/src/app/App.tsx +0 -21
  122. package/client/web/src/auth/AuthProvider.tsx +0 -122
  123. package/client/web/src/auth/ProtectedRoute.tsx +0 -22
  124. package/client/web/src/auth/auth-client.ts +0 -38
  125. package/client/web/src/auth/types.ts +0 -18
  126. package/client/web/src/components/AppShell.tsx +0 -59
  127. package/client/web/src/components/ui/button.tsx +0 -35
  128. package/client/web/src/components/ui/card.tsx +0 -7
  129. package/client/web/src/components/ui/input.tsx +0 -21
  130. package/client/web/src/lib/cn.ts +0 -6
  131. package/client/web/src/lib/specRouteCatalog.json +0 -23
  132. package/client/web/src/lib/specScreens.json +0 -17
  133. package/client/web/src/main.tsx +0 -17
  134. package/client/web/src/pages/DashboardPage.tsx +0 -158
  135. package/client/web/src/pages/LoginPage.tsx +0 -72
  136. package/client/web/src/pages/OrdersPage.tsx +0 -123
  137. package/client/web/src/styles/globals.css +0 -17
  138. package/client/web/src/theme-vars.ts +0 -18
  139. package/client/web/src/theme.ts +0 -25
  140. package/client/web/src/vite-env.d.ts +0 -1
  141. package/client/web/tailwind.config.js +0 -8
  142. package/client/web/tsconfig.json +0 -25
  143. package/client/web/vite.config.ts +0 -12
  144. package/server/.dockerignore +0 -4
  145. package/server/.env.example +0 -19
  146. package/server/Dockerfile +0 -22
  147. package/server/Dockerfile.dev +0 -19
  148. package/server/README.md +0 -33
  149. package/server/__init__.py +0 -0
  150. package/server/api/__init__.py +0 -1
  151. package/server/api/http/__init__.py +0 -4
  152. package/server/api/http/app.py +0 -53
  153. package/server/api/http/router.py +0 -24
  154. package/server/config.py +0 -52
  155. package/server/contexts/__init__.py +0 -12
  156. package/server/contexts/alerts/__init__.py +0 -1
  157. package/server/contexts/alerts/application/__init__.py +0 -13
  158. package/server/contexts/alerts/application/services.py +0 -41
  159. package/server/contexts/alerts/contracts/__init__.py +0 -3
  160. package/server/contexts/alerts/contracts/http/__init__.py +0 -3
  161. package/server/contexts/alerts/contracts/http/router.py +0 -37
  162. package/server/contexts/alerts/domain/__init__.py +0 -15
  163. package/server/contexts/alerts/domain/models.py +0 -29
  164. package/server/contexts/alerts/infrastructure/__init__.py +0 -11
  165. package/server/contexts/alerts/infrastructure/repository.py +0 -41
  166. package/server/contexts/auth/__init__.py +0 -1
  167. package/server/contexts/auth/application/__init__.py +0 -3
  168. package/server/contexts/auth/application/ports.py +0 -10
  169. package/server/contexts/auth/application/services.py +0 -64
  170. package/server/contexts/auth/contracts/__init__.py +0 -4
  171. package/server/contexts/auth/contracts/http/__init__.py +0 -4
  172. package/server/contexts/auth/contracts/http/dependencies.py +0 -37
  173. package/server/contexts/auth/contracts/http/router.py +0 -19
  174. package/server/contexts/auth/domain/__init__.py +0 -3
  175. package/server/contexts/auth/domain/models.py +0 -24
  176. package/server/contexts/auth/infrastructure/__init__.py +0 -4
  177. package/server/contexts/auth/infrastructure/adapters/memory.py +0 -19
  178. package/server/contexts/auth/infrastructure/adapters/mongodb.py +0 -24
  179. package/server/contexts/auth/infrastructure/adapters/sqlalchemy.py +0 -74
  180. package/server/contexts/auth/infrastructure/repository.py +0 -28
  181. package/server/contexts/catalog/__init__.py +0 -1
  182. package/server/contexts/catalog/application/__init__.py +0 -28
  183. package/server/contexts/catalog/application/ports.py +0 -15
  184. package/server/contexts/catalog/application/services.py +0 -154
  185. package/server/contexts/catalog/contracts/__init__.py +0 -3
  186. package/server/contexts/catalog/contracts/http/__init__.py +0 -3
  187. package/server/contexts/catalog/contracts/http/router.py +0 -60
  188. package/server/contexts/catalog/domain/__init__.py +0 -45
  189. package/server/contexts/catalog/domain/models.py +0 -113
  190. package/server/contexts/catalog/infrastructure/__init__.py +0 -4
  191. package/server/contexts/catalog/infrastructure/adapters/memory.py +0 -62
  192. package/server/contexts/catalog/infrastructure/repository.py +0 -8
  193. package/server/contexts/fulfillment/__init__.py +0 -1
  194. package/server/contexts/fulfillment/application/__init__.py +0 -13
  195. package/server/contexts/fulfillment/application/ports.py +0 -20
  196. package/server/contexts/fulfillment/application/services.py +0 -85
  197. package/server/contexts/fulfillment/contracts/__init__.py +0 -3
  198. package/server/contexts/fulfillment/contracts/http/__init__.py +0 -3
  199. package/server/contexts/fulfillment/contracts/http/router.py +0 -40
  200. package/server/contexts/fulfillment/domain/__init__.py +0 -25
  201. package/server/contexts/fulfillment/domain/models.py +0 -73
  202. package/server/contexts/fulfillment/infrastructure/__init__.py +0 -13
  203. package/server/contexts/fulfillment/infrastructure/adapters/memory.py +0 -43
  204. package/server/contexts/fulfillment/infrastructure/repository.py +0 -97
  205. package/server/contexts/health/__init__.py +0 -1
  206. package/server/contexts/health/application/__init__.py +0 -3
  207. package/server/contexts/health/application/services.py +0 -2
  208. package/server/contexts/health/contracts/__init__.py +0 -3
  209. package/server/contexts/health/contracts/http/__init__.py +0 -3
  210. package/server/contexts/health/contracts/http/router.py +0 -10
  211. package/server/contexts/inventory/__init__.py +0 -1
  212. package/server/contexts/inventory/application/__init__.py +0 -28
  213. package/server/contexts/inventory/application/ports.py +0 -11
  214. package/server/contexts/inventory/application/services.py +0 -214
  215. package/server/contexts/inventory/contracts/__init__.py +0 -3
  216. package/server/contexts/inventory/contracts/http/__init__.py +0 -3
  217. package/server/contexts/inventory/contracts/http/router.py +0 -82
  218. package/server/contexts/inventory/domain/__init__.py +0 -33
  219. package/server/contexts/inventory/domain/models.py +0 -93
  220. package/server/contexts/inventory/infrastructure/__init__.py +0 -4
  221. package/server/contexts/inventory/infrastructure/adapters/memory.py +0 -24
  222. package/server/contexts/inventory/infrastructure/repository.py +0 -8
  223. package/server/contexts/orders/__init__.py +0 -1
  224. package/server/contexts/orders/application/__init__.py +0 -19
  225. package/server/contexts/orders/application/services.py +0 -127
  226. package/server/contexts/orders/contracts/__init__.py +0 -3
  227. package/server/contexts/orders/contracts/http/__init__.py +0 -3
  228. package/server/contexts/orders/contracts/http/router.py +0 -82
  229. package/server/contexts/orders/domain/__init__.py +0 -29
  230. package/server/contexts/orders/domain/models.py +0 -95
  231. package/server/contexts/orders/infrastructure/__init__.py +0 -7
  232. package/server/contexts/orders/infrastructure/repository.py +0 -104
  233. package/server/contexts/shipping/__init__.py +0 -1
  234. package/server/contexts/shipping/application/__init__.py +0 -13
  235. package/server/contexts/shipping/application/services.py +0 -92
  236. package/server/contexts/shipping/contracts/__init__.py +0 -3
  237. package/server/contexts/shipping/contracts/http/__init__.py +0 -3
  238. package/server/contexts/shipping/contracts/http/router.py +0 -40
  239. package/server/contexts/shipping/domain/__init__.py +0 -19
  240. package/server/contexts/shipping/domain/models.py +0 -48
  241. package/server/contexts/shipping/infrastructure/__init__.py +0 -9
  242. package/server/contexts/shipping/infrastructure/repository.py +0 -50
  243. package/server/contexts/support/__init__.py +0 -1
  244. package/server/contexts/support/application/__init__.py +0 -13
  245. package/server/contexts/support/application/services.py +0 -29
  246. package/server/contexts/support/contracts/__init__.py +0 -3
  247. package/server/contexts/support/contracts/http/__init__.py +0 -3
  248. package/server/contexts/support/contracts/http/router.py +0 -40
  249. package/server/contexts/support/domain/__init__.py +0 -13
  250. package/server/contexts/support/domain/models.py +0 -27
  251. package/server/contexts/support/infrastructure/__init__.py +0 -11
  252. package/server/contexts/support/infrastructure/repository.py +0 -70
  253. package/server/contexts/user/__init__.py +0 -1
  254. package/server/contexts/user/application/__init__.py +0 -3
  255. package/server/contexts/user/application/ports.py +0 -11
  256. package/server/contexts/user/application/services.py +0 -44
  257. package/server/contexts/user/contracts/__init__.py +0 -3
  258. package/server/contexts/user/contracts/http/__init__.py +0 -3
  259. package/server/contexts/user/contracts/http/router.py +0 -26
  260. package/server/contexts/user/domain/__init__.py +0 -3
  261. package/server/contexts/user/domain/models.py +0 -22
  262. package/server/contexts/user/infrastructure/__init__.py +0 -3
  263. package/server/contexts/user/infrastructure/adapters/memory.py +0 -27
  264. package/server/contexts/user/infrastructure/adapters/mongodb.py +0 -41
  265. package/server/contexts/user/infrastructure/adapters/sqlalchemy.py +0 -94
  266. package/server/contexts/user/infrastructure/factory.py +0 -28
  267. package/server/data/README.md +0 -24
  268. package/server/data/bootstrap/alerts.json +0 -38
  269. package/server/data/bootstrap/auth_accounts.json +0 -18
  270. package/server/data/bootstrap/catalog_products.json +0 -179
  271. package/server/data/bootstrap/fulfillment_events.json +0 -5
  272. package/server/data/bootstrap/fulfillment_notes.json +0 -5
  273. package/server/data/bootstrap/fulfillment_tasks.json +0 -50
  274. package/server/data/bootstrap/inventory_levels.json +0 -80
  275. package/server/data/bootstrap/orders.json +0 -62
  276. package/server/data/bootstrap/shipping_shipments.json +0 -50
  277. package/server/data/bootstrap/support_faqs.json +0 -26
  278. package/server/data/bootstrap/users.json +0 -20
  279. package/server/data/bootstrap_loader.py +0 -15
  280. package/server/docker-entrypoint.sh +0 -56
  281. package/server/main.py +0 -3
  282. package/server/pyproject.toml +0 -36
  283. package/server/shared/__init__.py +0 -1
  284. package/server/shared/application/__init__.py +0 -3
  285. package/server/shared/application/health.py +0 -2
  286. package/server/shared/infrastructure/__init__.py +0 -10
  287. package/server/shared/infrastructure/runtime.py +0 -6
  288. package/server/shared/infrastructure/security.py +0 -33
  289. package/server/tests/e2e/test_domain_feature_flows.py +0 -483
  290. package/server/tests/test_health.py +0 -49
  291. package/server/uv.lock +0 -1169
@@ -1,30 +0,0 @@
1
- const API_BASE_URL = (import.meta.env.VITE_API_BASE_URL as string | undefined)?.replace(/\/$/, "") ?? "";
2
-
3
- interface RequestOptions {
4
- accessToken?: string;
5
- }
6
-
7
- async function readJson<T>(response: Response): Promise<T> {
8
- if (!response.ok) {
9
- let message = "Request failed";
10
- try {
11
- const data = (await response.json()) as { detail?: string };
12
- if (typeof data.detail === "string" && data.detail.length > 0) {
13
- message = data.detail;
14
- }
15
- } catch {
16
- message = response.statusText || message;
17
- }
18
- throw new Error(message);
19
- }
20
-
21
- return (await response.json()) as T;
22
- }
23
-
24
- export async function apiGet<T>(path: string, options?: RequestOptions): Promise<T> {
25
- const response = await fetch(`${API_BASE_URL}${path}`, {
26
- headers: options?.accessToken ? { Authorization: `Bearer ${options.accessToken}` } : undefined,
27
- });
28
-
29
- return readJson<T>(response);
30
- }
@@ -1,42 +0,0 @@
1
- import { apiGet } from "@/api/client";
2
-
3
- export interface DashboardStat {
4
- label: string;
5
- value: string;
6
- tone?: string | null;
7
- }
8
-
9
- export interface OrderActivity {
10
- order_id: string;
11
- date: string;
12
- customer: string;
13
- status: string;
14
- }
15
-
16
- export interface SelectedOrder {
17
- product_name: string;
18
- customer_name: string;
19
- status: string;
20
- amount: string;
21
- }
22
-
23
- export interface OrderOverviewResponse {
24
- stats: DashboardStat[];
25
- recent_activity: OrderActivity[];
26
- selected_order: SelectedOrder;
27
- }
28
-
29
- export interface OrderSummary {
30
- id: string;
31
- product_name: string;
32
- customer_name: string;
33
- status: string;
34
- }
35
-
36
- export function fetchOrderOverview(accessToken: string) {
37
- return apiGet<OrderOverviewResponse>("/orders/overview", { accessToken });
38
- }
39
-
40
- export function fetchOrders(accessToken: string) {
41
- return apiGet<OrderSummary[]>("/orders", { accessToken });
42
- }
@@ -1,21 +0,0 @@
1
- import { Route, Routes } from "react-router-dom";
2
-
3
- import { ProtectedRoute } from "@/auth/ProtectedRoute";
4
- import { AppShell } from "@/components/AppShell";
5
- import { DashboardPage } from "@/pages/DashboardPage";
6
- import { LoginPage } from "@/pages/LoginPage";
7
- import { OrdersPage } from "@/pages/OrdersPage";
8
-
9
- export function App() {
10
- return (
11
- <Routes>
12
- <Route path="/login" element={<LoginPage />} />
13
- <Route element={<ProtectedRoute />}>
14
- <Route element={<AppShell />}>
15
- <Route path="/" element={<DashboardPage />} />
16
- <Route path="/orders" element={<OrdersPage />} />
17
- </Route>
18
- </Route>
19
- </Routes>
20
- );
21
- }
@@ -1,122 +0,0 @@
1
- import { createContext, useContext, useEffect, useMemo, useState, type ReactNode } from "react";
2
-
3
- import { getMe, login as loginRequest } from "@/auth/auth-client";
4
- import type { AuthToken, AuthUser, LoginCommand } from "@/auth/types";
5
-
6
- const STORAGE_KEY = "web.auth.token";
7
-
8
- interface AuthContextValue {
9
- user: AuthUser | null;
10
- token: AuthToken | null;
11
- initializing: boolean;
12
- authenticating: boolean;
13
- login: (command: LoginCommand) => Promise<void>;
14
- logout: () => void;
15
- }
16
-
17
- const AuthContext = createContext<AuthContextValue | undefined>(undefined);
18
-
19
- function readStoredToken(): AuthToken | null {
20
- const raw = window.localStorage.getItem(STORAGE_KEY);
21
- if (!raw) {
22
- return null;
23
- }
24
-
25
- try {
26
- return JSON.parse(raw) as AuthToken;
27
- } catch {
28
- window.localStorage.removeItem(STORAGE_KEY);
29
- return null;
30
- }
31
- }
32
-
33
- function storeToken(token: AuthToken | null) {
34
- if (token) {
35
- window.localStorage.setItem(STORAGE_KEY, JSON.stringify(token));
36
- return;
37
- }
38
-
39
- window.localStorage.removeItem(STORAGE_KEY);
40
- }
41
-
42
- export function AuthProvider({ children }: { children: ReactNode }) {
43
- const [token, setToken] = useState<AuthToken | null>(() => readStoredToken());
44
- const [user, setUser] = useState<AuthUser | null>(null);
45
- const [initializing, setInitializing] = useState(true);
46
- const [authenticating, setAuthenticating] = useState(false);
47
-
48
- useEffect(() => {
49
- let cancelled = false;
50
-
51
- async function bootstrap() {
52
- if (!token) {
53
- setUser(null);
54
- setInitializing(false);
55
- return;
56
- }
57
-
58
- try {
59
- const currentUser = await getMe(token.access_token);
60
- if (!cancelled) {
61
- setUser(currentUser);
62
- }
63
- } catch {
64
- if (!cancelled) {
65
- setToken(null);
66
- setUser(null);
67
- storeToken(null);
68
- }
69
- } finally {
70
- if (!cancelled) {
71
- setInitializing(false);
72
- }
73
- }
74
- }
75
-
76
- void bootstrap();
77
-
78
- return () => {
79
- cancelled = true;
80
- };
81
- }, [token]);
82
-
83
- const value = useMemo<AuthContextValue>(
84
- () => ({
85
- user,
86
- token,
87
- initializing,
88
- authenticating,
89
- async login(command) {
90
- setAuthenticating(true);
91
- try {
92
- const nextToken = await loginRequest(command);
93
- storeToken(nextToken);
94
- setToken(nextToken);
95
- const currentUser = await getMe(nextToken.access_token);
96
- setUser(currentUser);
97
- } finally {
98
- setAuthenticating(false);
99
- setInitializing(false);
100
- }
101
- },
102
- logout() {
103
- storeToken(null);
104
- setToken(null);
105
- setUser(null);
106
- setInitializing(false);
107
- },
108
- }),
109
- [authenticating, initializing, token, user],
110
- );
111
-
112
- return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
113
- }
114
-
115
- export function useAuth() {
116
- const context = useContext(AuthContext);
117
- if (!context) {
118
- throw new Error("useAuth must be used within an AuthProvider");
119
- }
120
-
121
- return context;
122
- }
@@ -1,22 +0,0 @@
1
- import { Navigate, Outlet, useLocation } from "react-router-dom";
2
-
3
- import { useAuth } from "@/auth/AuthProvider";
4
-
5
- export function ProtectedRoute() {
6
- const { initializing, user } = useAuth();
7
- const location = useLocation();
8
-
9
- if (initializing) {
10
- return (
11
- <div className="flex min-h-screen items-center justify-center bg-[var(--app-shell-bg)] text-sm font-medium text-[#5f6e86]">
12
- 세션을 확인하는 중입니다.
13
- </div>
14
- );
15
- }
16
-
17
- if (!user) {
18
- return <Navigate to="/login" replace state={{ from: location }} />;
19
- }
20
-
21
- return <Outlet />;
22
- }
@@ -1,38 +0,0 @@
1
- import type { AuthToken, AuthUser, LoginCommand } from "@/auth/types";
2
-
3
- const API_BASE_URL = (import.meta.env.VITE_API_BASE_URL as string | undefined)?.replace(/\/$/, "") ?? "";
4
-
5
- async function readJson<T>(response: Response): Promise<T> {
6
- if (!response.ok) {
7
- let message = "Request failed";
8
- try {
9
- const data = (await response.json()) as { detail?: string };
10
- if (typeof data.detail === "string" && data.detail.length > 0) {
11
- message = data.detail;
12
- }
13
- } catch {
14
- message = response.statusText || message;
15
- }
16
- throw new Error(message);
17
- }
18
-
19
- return (await response.json()) as T;
20
- }
21
-
22
- export async function login(command: LoginCommand): Promise<AuthToken> {
23
- const response = await fetch(`${API_BASE_URL}/auth/login`, {
24
- method: "POST",
25
- headers: { "Content-Type": "application/json" },
26
- body: JSON.stringify(command),
27
- });
28
-
29
- return readJson<AuthToken>(response);
30
- }
31
-
32
- export async function getMe(accessToken: string): Promise<AuthUser> {
33
- const response = await fetch(`${API_BASE_URL}/auth/me`, {
34
- headers: { Authorization: `Bearer ${accessToken}` },
35
- });
36
-
37
- return readJson<AuthUser>(response);
38
- }
@@ -1,18 +0,0 @@
1
- export interface AuthToken {
2
- access_token: string;
3
- token_type: string;
4
- user_id: string;
5
- }
6
-
7
- export interface AuthUser {
8
- id: string;
9
- name: string;
10
- email: string;
11
- role: string;
12
- status: string;
13
- }
14
-
15
- export interface LoginCommand {
16
- email: string;
17
- password: string;
18
- }
@@ -1,59 +0,0 @@
1
- import { Bell, Search } from "lucide-react";
2
- import { NavLink, Outlet } from "react-router-dom";
3
-
4
- import { useAuth } from "@/auth/AuthProvider";
5
- import { Button } from "@/components/ui/button";
6
- import { Input } from "@/components/ui/input";
7
- import { appTheme } from "@/theme";
8
- import { appThemeVars } from "@/theme-vars";
9
-
10
- const navItems = [
11
- { to: "/", label: "Overview" },
12
- { to: "/orders", label: "Orders" },
13
- ];
14
-
15
- export function AppShell() {
16
- const { logout, user } = useAuth();
17
-
18
- return (
19
- <div className="min-h-screen bg-[var(--app-shell-bg)] text-[#22314d]" style={appThemeVars(appTheme)}>
20
- <header className="border-b border-[var(--app-border)] bg-white">
21
- <div className="mx-auto flex max-w-7xl items-center gap-6 px-6 py-4">
22
- <div className="font-black tracking-[0.18em] text-[#16253f]">PRODUCT</div>
23
- <nav className="flex items-center gap-2">
24
- {navItems.map((item) => (
25
- <NavLink
26
- key={item.to}
27
- to={item.to}
28
- end={item.to === "/"}
29
- className={({ isActive }) =>
30
- `rounded-full px-4 py-2 text-sm font-semibold ${isActive ? "bg-[#eef2ff] text-[#223a82]" : "text-[#6b7890]"}`
31
- }
32
- >
33
- {item.label}
34
- </NavLink>
35
- ))}
36
- </nav>
37
- <div className="ml-auto flex items-center gap-3">
38
- <div className="relative w-[280px]">
39
- <Search className="pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-[#8d97a8]" />
40
- <Input className="pl-9" placeholder="Search workspace" />
41
- </div>
42
- <button className="inline-flex h-10 w-10 items-center justify-center rounded-full border border-[var(--app-border)] bg-white">
43
- <Bell className="h-4 w-4 text-[#5f6e86]" />
44
- </button>
45
- <div className="hidden rounded-full bg-[#f7f9fc] px-4 py-2 text-sm font-semibold text-[#314157] md:block">
46
- {user?.name ?? user?.email}
47
- </div>
48
- <Button variant="outline" onClick={logout}>
49
- Log out
50
- </Button>
51
- </div>
52
- </div>
53
- </header>
54
- <main className="mx-auto max-w-7xl px-6 py-6">
55
- <Outlet />
56
- </main>
57
- </div>
58
- );
59
- }
@@ -1,35 +0,0 @@
1
- import * as React from "react";
2
- import { cva, type VariantProps } from "class-variance-authority";
3
-
4
- import { cn } from "@/lib/cn";
5
-
6
- const buttonVariants = cva(
7
- "inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors disabled:pointer-events-none disabled:opacity-50",
8
- {
9
- variants: {
10
- variant: {
11
- default: "bg-[#1f3a8a] text-white hover:bg-[#15306f]",
12
- secondary: "bg-[#eef2ff] text-[#22314d] hover:bg-[#dfe6fb]",
13
- outline: "border border-[var(--app-border)] bg-white text-[#314157] hover:bg-[#f7f9fc]",
14
- },
15
- size: {
16
- default: "h-10 px-4 py-2",
17
- sm: "h-9 px-3",
18
- lg: "h-11 px-5",
19
- },
20
- },
21
- defaultVariants: {
22
- variant: "default",
23
- size: "default",
24
- },
25
- },
26
- );
27
-
28
- export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> {}
29
-
30
- const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(({ className, variant, size, ...props }, ref) => {
31
- return <button ref={ref} className={cn(buttonVariants({ variant, size }), className)} {...props} />;
32
- });
33
- Button.displayName = "Button";
34
-
35
- export { Button, buttonVariants };
@@ -1,7 +0,0 @@
1
- import type { HTMLAttributes } from "react";
2
-
3
- import { cn } from "@/lib/cn";
4
-
5
- export function Card({ className, ...props }: HTMLAttributes<HTMLDivElement>) {
6
- return <div className={cn("rounded-[var(--app-card-radius)] border border-[var(--app-border)] bg-white", className)} {...props} />;
7
- }
@@ -1,21 +0,0 @@
1
- import * as React from "react";
2
-
3
- import { cn } from "@/lib/cn";
4
-
5
- export interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {}
6
-
7
- const Input = React.forwardRef<HTMLInputElement, InputProps>(({ className, ...props }, ref) => {
8
- return (
9
- <input
10
- ref={ref}
11
- className={cn(
12
- "flex h-10 w-full rounded-md border border-[var(--app-border)] bg-white px-3 py-2 text-sm text-[#22314d] outline-none placeholder:text-[#8d97a8] focus:ring-2 focus:ring-[#bfd0ff]",
13
- className,
14
- )}
15
- {...props}
16
- />
17
- );
18
- });
19
- Input.displayName = "Input";
20
-
21
- export { Input };
@@ -1,6 +0,0 @@
1
- import { clsx, type ClassValue } from "clsx";
2
- import { twMerge } from "tailwind-merge";
3
-
4
- export function cn(...inputs: ClassValue[]) {
5
- return twMerge(clsx(inputs));
6
- }
@@ -1,23 +0,0 @@
1
- [
2
- {
3
- "id": "TMP_001",
4
- "route": "/",
5
- "binding": "direct",
6
- "notes": ["Primary signed-in workspace route."],
7
- "tags": ["shell"]
8
- },
9
- {
10
- "id": "TMP_002",
11
- "route": "/orders",
12
- "binding": "direct",
13
- "notes": ["Collection route for the order table."],
14
- "tags": ["table"]
15
- },
16
- {
17
- "id": "TMP_003",
18
- "route": "/login",
19
- "binding": "direct",
20
- "notes": ["Unauthenticated entry route."],
21
- "tags": ["public"]
22
- }
23
- ]
@@ -1,17 +0,0 @@
1
- [
2
- {
3
- "id": "TMP_001",
4
- "title": "Dashboard",
5
- "tags": ["core", "workspace"]
6
- },
7
- {
8
- "id": "TMP_002",
9
- "title": "Orders",
10
- "tags": ["operations", "list"]
11
- },
12
- {
13
- "id": "TMP_003",
14
- "title": "Login",
15
- "tags": ["auth"]
16
- }
17
- ]
@@ -1,17 +0,0 @@
1
- import React from "react";
2
- import ReactDOM from "react-dom/client";
3
- import { BrowserRouter } from "react-router-dom";
4
-
5
- import { AuthProvider } from "@/auth/AuthProvider";
6
- import { App } from "./app/App";
7
- import "./styles/globals.css";
8
-
9
- ReactDOM.createRoot(document.getElementById("root")!).render(
10
- <React.StrictMode>
11
- <AuthProvider>
12
- <BrowserRouter>
13
- <App />
14
- </BrowserRouter>
15
- </AuthProvider>
16
- </React.StrictMode>,
17
- );
@@ -1,158 +0,0 @@
1
- import { useEffect, useState } from "react";
2
-
3
- import { useAuth } from "@/auth/AuthProvider";
4
- import { fetchOrderOverview, type OrderOverviewResponse } from "@/api/orders";
5
- import { Card } from "@/components/ui/card";
6
-
7
- export function DashboardPage() {
8
- const { token } = useAuth();
9
- const [overview, setOverview] = useState<OrderOverviewResponse | null>(null);
10
- const [loading, setLoading] = useState(true);
11
- const [error, setError] = useState<string | null>(null);
12
-
13
- useEffect(() => {
14
- if (!token?.access_token) {
15
- return;
16
- }
17
-
18
- let cancelled = false;
19
-
20
- setLoading(true);
21
- setError(null);
22
-
23
- fetchOrderOverview(token.access_token)
24
- .then((response) => {
25
- if (!cancelled) {
26
- setOverview(response);
27
- }
28
- })
29
- .catch((nextError: Error) => {
30
- if (!cancelled) {
31
- setError(nextError.message);
32
- setOverview(null);
33
- }
34
- })
35
- .finally(() => {
36
- if (!cancelled) {
37
- setLoading(false);
38
- }
39
- });
40
-
41
- return () => {
42
- cancelled = true;
43
- };
44
- }, [token?.access_token]);
45
-
46
- const recentActivity = overview?.recent_activity ?? [];
47
- const selectedOrder = overview?.selected_order;
48
-
49
- return (
50
- <div className="grid gap-6 xl:grid-cols-[1fr_var(--app-panel-width)]">
51
- <div className="space-y-6">
52
- <section className="grid gap-4 md:grid-cols-3">
53
- {(overview?.stats ?? []).map((stat) => (
54
- <Card key={stat.label} className="p-[var(--app-card-padding)]">
55
- <p className="text-sm text-[var(--app-muted)]">{stat.label}</p>
56
- <p className={`mt-3 text-3xl font-bold ${stat.tone ?? "text-[#22314d]"}`}>{stat.value}</p>
57
- </Card>
58
- ))}
59
- {loading && !overview ? (
60
- <Card className="p-[var(--app-card-padding)] md:col-span-3">
61
- <p className="text-sm text-[var(--app-muted)]">Overview</p>
62
- <p className="mt-3 text-lg font-semibold text-[#22314d]">Loading dashboard data...</p>
63
- </Card>
64
- ) : null}
65
- {error ? (
66
- <Card className="p-[var(--app-card-padding)] md:col-span-3">
67
- <p className="text-sm text-[var(--app-muted)]">Overview</p>
68
- <p className="mt-3 text-lg font-semibold text-[var(--app-danger)]">{error}</p>
69
- </Card>
70
- ) : null}
71
- </section>
72
-
73
- <Card className="overflow-hidden">
74
- <div className="p-[var(--app-card-padding)]">
75
- <h2 className="text-lg font-bold">Recent activity</h2>
76
- </div>
77
- <table className="w-full border-collapse text-sm">
78
- <thead className="bg-[#f8fafe] text-left text-[#516174]">
79
- <tr>
80
- {["Order", "Date", "Customer", "Status"].map((cell) => (
81
- <th key={cell} className="border-y border-[var(--app-border)] px-[var(--app-table-cell-px)] py-[var(--app-table-cell-py)] font-semibold">
82
- {cell}
83
- </th>
84
- ))}
85
- </tr>
86
- </thead>
87
- <tbody>
88
- {loading && !recentActivity.length ? (
89
- <tr>
90
- <td className="border-b border-[var(--app-border)] px-[var(--app-table-cell-px)] py-[var(--app-table-cell-py)] text-[var(--app-muted)]" colSpan={4}>
91
- Recent activity is loading.
92
- </td>
93
- </tr>
94
- ) : null}
95
- {error ? (
96
- <tr>
97
- <td
98
- className="border-b border-[var(--app-border)] px-[var(--app-table-cell-px)] py-[var(--app-table-cell-py)] text-[var(--app-danger)]"
99
- colSpan={4}
100
- >
101
- {error}
102
- </td>
103
- </tr>
104
- ) : null}
105
- {!loading && !error && !recentActivity.length ? (
106
- <tr>
107
- <td className="border-b border-[var(--app-border)] px-[var(--app-table-cell-px)] py-[var(--app-table-cell-py)] text-[var(--app-muted)]" colSpan={4}>
108
- No recent activity found.
109
- </td>
110
- </tr>
111
- ) : null}
112
- {recentActivity.map((row) => (
113
- <tr key={row.order_id}>
114
- <td className="border-b border-[var(--app-border)] px-[var(--app-table-cell-px)] py-[var(--app-table-cell-py)] text-[var(--app-accent)]">
115
- {row.order_id}
116
- </td>
117
- <td className="border-b border-[var(--app-border)] px-[var(--app-table-cell-px)] py-[var(--app-table-cell-py)]">{row.date}</td>
118
- <td className="border-b border-[var(--app-border)] px-[var(--app-table-cell-px)] py-[var(--app-table-cell-py)]">{row.customer}</td>
119
- <td className="border-b border-[var(--app-border)] px-[var(--app-table-cell-px)] py-[var(--app-table-cell-py)]">
120
- <span className={row.status === "At risk" ? "text-[var(--app-danger)]" : ""}>{row.status}</span>
121
- </td>
122
- </tr>
123
- ))}
124
- </tbody>
125
- </table>
126
- </Card>
127
- </div>
128
-
129
- <Card className="p-[var(--app-card-padding)]">
130
- <h2 className="text-lg font-bold">Selected detail</h2>
131
- {loading && !selectedOrder ? <p className="mt-5 text-sm text-[var(--app-muted)]">Loading selected order...</p> : null}
132
- {error ? <p className="mt-5 text-sm font-semibold text-[var(--app-danger)]">{error}</p> : null}
133
- {selectedOrder ? (
134
- <div className="mt-5 space-y-4 text-sm">
135
- <div>
136
- <p className="text-[var(--app-muted)]">Product</p>
137
- <p className="mt-1 font-semibold">{selectedOrder.product_name}</p>
138
- </div>
139
- <div>
140
- <p className="text-[var(--app-muted)]">Customer</p>
141
- <p className="mt-1 font-semibold">{selectedOrder.customer_name}</p>
142
- </div>
143
- <div>
144
- <p className="text-[var(--app-muted)]">Status</p>
145
- <p className={`mt-1 font-semibold ${selectedOrder.status === "At risk" ? "text-[var(--app-danger)]" : "text-[#314157]"}`}>
146
- {selectedOrder.status}
147
- </p>
148
- </div>
149
- <div>
150
- <p className="text-[var(--app-muted)]">Amount</p>
151
- <p className="mt-1 font-semibold">{selectedOrder.amount}</p>
152
- </div>
153
- </div>
154
- ) : null}
155
- </Card>
156
- </div>
157
- );
158
- }