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,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 = "landing.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(--landing-bg)] text-sm font-medium text-[var(--landing-muted)]">
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,34 +0,0 @@
1
- import { LogOut } from "lucide-react";
2
- import { NavLink, Outlet } from "react-router-dom";
3
-
4
- import { useAuth } from "@/auth/AuthProvider";
5
- import { landingTheme } from "@/theme";
6
- import { landingThemeVars } from "@/theme-vars";
7
-
8
- export function LandingShell() {
9
- const { logout, user } = useAuth();
10
-
11
- return (
12
- <div className="min-h-screen bg-[var(--landing-bg)] text-[var(--landing-text)]" style={landingThemeVars(landingTheme)}>
13
- <header className="mx-auto flex max-w-6xl items-center justify-between px-6 py-6">
14
- <div className="text-[20px] font-black tracking-[0.18em]">PRODUCT</div>
15
- <div className="flex items-center gap-3">
16
- <NavLink className="rounded-full border border-[#d8cabb] px-4 py-2 text-sm font-semibold" to="/workspace">
17
- Workspace
18
- </NavLink>
19
- <div className="hidden rounded-full bg-white px-4 py-2 text-sm font-semibold text-[#2c261f] md:block">{user?.name ?? user?.email}</div>
20
- <button
21
- className="inline-flex items-center gap-2 rounded-full bg-[#2c261f] px-4 py-2 text-sm font-semibold text-white"
22
- onClick={logout}
23
- >
24
- <LogOut className="h-4 w-4" />
25
- Log out
26
- </button>
27
- </div>
28
- </header>
29
- <main>
30
- <Outlet />
31
- </main>
32
- </div>
33
- );
34
- }
@@ -1,23 +0,0 @@
1
- [
2
- {
3
- "id": "TLAND_001",
4
- "route": "/",
5
- "binding": "direct",
6
- "notes": ["Public marketing entry route."],
7
- "tags": ["public"]
8
- },
9
- {
10
- "id": "TLAND_002",
11
- "route": "/workspace",
12
- "binding": "direct",
13
- "notes": ["Authenticated member workspace route."],
14
- "tags": ["protected"]
15
- },
16
- {
17
- "id": "TLAND_003",
18
- "route": "/login",
19
- "binding": "direct",
20
- "notes": ["Member sign-in route."],
21
- "tags": ["public"]
22
- }
23
- ]
@@ -1,17 +0,0 @@
1
- [
2
- {
3
- "id": "TLAND_001",
4
- "title": "Landing Home",
5
- "tags": ["marketing", "public"]
6
- },
7
- {
8
- "id": "TLAND_002",
9
- "title": "Landing Workspace",
10
- "tags": ["member", "workspace"]
11
- },
12
- {
13
- "id": "TLAND_003",
14
- "title": "Landing 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";
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,215 +0,0 @@
1
- import { useEffect, useState } from "react";
2
- import { Archive, ArrowRight, Boxes, CheckCircle2, Clock3 } from "lucide-react";
3
- import { Link } from "react-router-dom";
4
-
5
- import { fetchCatalogProducts, type CatalogProductSummary } from "@/api/catalog";
6
- import { useAuth } from "@/auth/AuthProvider";
7
- import { landingTheme } from "@/theme";
8
- import { landingThemeVars } from "@/theme-vars";
9
-
10
- const productStateCopy: Record<string, string> = {
11
- active: "즉시 연결 가능한 template surface",
12
- draft: "구성 중인 화면 및 플로우 실험",
13
- archived: "보관된 이전 실험 기록",
14
- };
15
-
16
- function productStateClassName(state: string) {
17
- if (state === "active") {
18
- return "bg-[var(--landing-accent-soft)] text-[var(--landing-accent)]";
19
- }
20
- if (state === "draft") {
21
- return "bg-[#fff2e4] text-[#b46c35]";
22
- }
23
-
24
- return "bg-[#efe4d7] text-[#7f5b39]";
25
- }
26
-
27
- function productStateIcon(state: string) {
28
- if (state === "active") {
29
- return CheckCircle2;
30
- }
31
- if (state === "draft") {
32
- return Clock3;
33
- }
34
-
35
- return Archive;
36
- }
37
-
38
- export function LandingHomePage() {
39
- const { user } = useAuth();
40
- const ctaTo = user ? "/workspace" : "/login";
41
- const ctaLabel = user ? "Open workspace" : "Sign in";
42
- const [products, setProducts] = useState<CatalogProductSummary[]>([]);
43
- const [loading, setLoading] = useState(true);
44
- const [error, setError] = useState<string | null>(null);
45
-
46
- useEffect(() => {
47
- let cancelled = false;
48
-
49
- setLoading(true);
50
- setError(null);
51
-
52
- fetchCatalogProducts()
53
- .then((response) => {
54
- if (!cancelled) {
55
- setProducts(response);
56
- }
57
- })
58
- .catch((nextError: Error) => {
59
- if (!cancelled) {
60
- setError(nextError.message);
61
- setProducts([]);
62
- }
63
- })
64
- .finally(() => {
65
- if (!cancelled) {
66
- setLoading(false);
67
- }
68
- });
69
-
70
- return () => {
71
- cancelled = true;
72
- };
73
- }, []);
74
-
75
- const activeCount = products.filter((product) => product.status === "active").length;
76
- const highlightedProduct = products[0];
77
-
78
- return (
79
- <div className="min-h-screen bg-[var(--landing-bg)] text-[var(--landing-text)]" style={landingThemeVars(landingTheme)}>
80
- <header className="mx-auto flex max-w-6xl items-center justify-between px-6 py-6">
81
- <div className="text-[20px] font-black tracking-[0.18em]">PRODUCT</div>
82
- <nav className="hidden items-center gap-8 text-sm font-semibold md:flex">
83
- <a href="#features">Features</a>
84
- <a href="#proof">Proof</a>
85
- <a href="#cta">Contact</a>
86
- <Link className="rounded-full border border-[#d8cabb] px-4 py-2" to={ctaTo}>
87
- {ctaLabel}
88
- </Link>
89
- </nav>
90
- </header>
91
-
92
- <main className="space-y-[var(--landing-section-gap)]">
93
- <section className="mx-auto grid max-w-6xl gap-10 px-6 py-[var(--landing-hero-pad-y)] lg:grid-cols-[1.1fr_0.9fr]">
94
- <div>
95
- <p className="inline-flex rounded-full bg-[var(--landing-accent-soft)] px-4 py-2 text-sm font-semibold text-[var(--landing-accent)]">
96
- Built for product-grade delivery
97
- </p>
98
- <h1 className="mt-6 max-w-3xl text-5xl font-black leading-[1.05] tracking-[-0.04em]">
99
- 서비스로 바로 이어지는
100
- <br />
101
- 프론트엔드 템플릿
102
- </h1>
103
- <p className="mt-6 max-w-2xl text-lg leading-8 text-[var(--landing-muted)]">
104
- 제품 DOM은 유지하고, 시각 계약만 안전하게 조정하는 React 보일러플레이트다. 랜딩, 일반 앱, 어드민 콘솔을 같은 철학으로 시작할 수 있다.
105
- </p>
106
- <div className="mt-8 flex flex-wrap gap-3">
107
- <Link
108
- className="inline-flex items-center gap-2 rounded-full bg-[var(--landing-accent)] px-6 py-3 text-sm font-semibold text-white"
109
- to={ctaTo}
110
- >
111
- {ctaLabel}
112
- <ArrowRight className="h-4 w-4" />
113
- </Link>
114
- <a className="rounded-full border border-[#d8cabb] px-6 py-3 text-sm font-semibold" href="#features">
115
- See examples
116
- </a>
117
- </div>
118
- </div>
119
-
120
- <div className="rounded-[var(--landing-card-radius)] border border-[#eadbcb] bg-[#fffaf5] p-6 shadow-[0_24px_70px_rgba(123,78,42,0.08)]">
121
- <div className="grid gap-4 md:grid-cols-2">
122
- <div className="rounded-[20px] bg-[#2c261f] p-5 text-white">
123
- <p className="text-sm text-white/70">Live catalog</p>
124
- <p className="mt-4 text-3xl font-black">{loading ? "..." : products.length}</p>
125
- </div>
126
- <div className="rounded-[20px] bg-[var(--landing-accent-soft)] p-5 text-[var(--landing-accent)]">
127
- <p className="text-sm opacity-80">Active products</p>
128
- <p className="mt-4 text-3xl font-black">{loading ? "..." : activeCount}</p>
129
- </div>
130
- </div>
131
- <div className="mt-4 rounded-[20px] border border-[#eadbcb] bg-white p-5">
132
- <p className="text-sm text-[var(--landing-muted)]">Catalog snapshot</p>
133
- {error ? <p className="mt-3 text-lg font-bold text-[#b85b2a]">{error}</p> : null}
134
- {!error ? <p className="mt-3 text-lg font-bold">{loading ? "Loading live catalog..." : highlightedProduct?.name ?? "No live products"}</p> : null}
135
- {!loading && highlightedProduct ? <p className="mt-2 text-sm text-[var(--landing-muted)]">State: {highlightedProduct.status}</p> : null}
136
- </div>
137
- </div>
138
- </section>
139
-
140
- <section id="features" className="mx-auto max-w-6xl px-6 py-10">
141
- <div className="grid gap-4 lg:grid-cols-3">
142
- {loading ? (
143
- <div className="rounded-[var(--landing-card-radius)] border border-[#eadbcb] bg-white px-6 py-7 lg:col-span-3">
144
- <p className="text-sm text-[var(--landing-muted)]">Loading live catalog...</p>
145
- </div>
146
- ) : null}
147
- {!loading && !error && !products.length ? (
148
- <div className="rounded-[var(--landing-card-radius)] border border-[#eadbcb] bg-white px-6 py-7 lg:col-span-3">
149
- <p className="text-sm text-[var(--landing-muted)]">No catalog products are available yet.</p>
150
- </div>
151
- ) : null}
152
- {products.map((product) => {
153
- const Icon = productStateIcon(product.status);
154
- return (
155
- <div key={product.id} className="rounded-[var(--landing-card-radius)] border border-[#eadbcb] bg-white px-6 py-7">
156
- <div className={`inline-flex rounded-2xl p-3 ${productStateClassName(product.status)}`}>
157
- <Icon className="h-5 w-5" />
158
- </div>
159
- <div className="mt-5 flex items-center justify-between gap-4">
160
- <h2 className="text-xl font-bold">{product.name}</h2>
161
- <span className={`rounded-full px-3 py-1 text-xs font-semibold uppercase ${productStateClassName(product.status)}`}>{product.status}</span>
162
- </div>
163
- <p className="mt-2 text-sm font-semibold text-[#7f5b39]">{product.brand}</p>
164
- <p className="mt-3 text-sm leading-7 text-[var(--landing-muted)]">{product.short_description}</p>
165
- <p className="mt-3 text-xs uppercase tracking-[0.16em] text-[var(--landing-muted)]">
166
- {productStateCopy[product.status] ?? "Catalog product delivered from the live backend contract."}
167
- </p>
168
- </div>
169
- );
170
- })}
171
- </div>
172
- </section>
173
-
174
- <section id="proof" className="mx-auto max-w-6xl px-6 py-4">
175
- <div className="rounded-[32px] border border-[#eadbcb] bg-white px-8 py-8 shadow-[0_18px_48px_rgba(123,78,42,0.06)]">
176
- <div className="flex flex-wrap items-center justify-between gap-4">
177
- <div>
178
- <p className="text-sm font-semibold uppercase tracking-[0.18em] text-[var(--landing-accent)]">Live proof</p>
179
- <h2 className="mt-3 text-3xl font-black tracking-[-0.04em]">`/api/v1/catalog/products` 기반 catalog surface</h2>
180
- </div>
181
- <div className="inline-flex items-center gap-2 rounded-full bg-[var(--landing-accent-soft)] px-4 py-2 text-sm font-semibold text-[var(--landing-accent)]">
182
- <Boxes className="h-4 w-4" />
183
- {loading ? "Syncing..." : `${products.length} products synced`}
184
- </div>
185
- </div>
186
- <div className="mt-6 grid gap-4 md:grid-cols-3">
187
- {[
188
- { label: "Active", value: products.filter((product) => product.status === "active").length.toString() },
189
- { label: "Draft", value: products.filter((product) => product.status === "draft").length.toString() },
190
- { label: "Archived", value: products.filter((product) => product.status === "archived").length.toString() },
191
- ].map((entry) => (
192
- <div key={entry.label} className="rounded-[24px] bg-[#fff7f0] px-5 py-5">
193
- <p className="text-sm text-[var(--landing-muted)]">{entry.label}</p>
194
- <p className="mt-3 text-3xl font-black text-[#2c261f]">{loading ? "..." : entry.value}</p>
195
- </div>
196
- ))}
197
- </div>
198
- </div>
199
- </section>
200
-
201
- <section id="cta" className="mx-auto max-w-6xl px-6 pb-16">
202
- <div className="rounded-[32px] bg-[#2c261f] px-8 py-10 text-white">
203
- <p className="text-sm text-white/60">Ready to start</p>
204
- <div className="mt-4 flex flex-col gap-6 lg:flex-row lg:items-end lg:justify-between">
205
- <h2 className="max-w-2xl text-3xl font-black leading-tight">제품 구조를 무너뜨리지 않는 보일러플레이트로 시작하세요.</h2>
206
- <Link className="rounded-full bg-white px-6 py-3 text-sm font-semibold text-[#2c261f]" to={ctaTo}>
207
- {ctaLabel}
208
- </Link>
209
- </div>
210
- </div>
211
- </section>
212
- </main>
213
- </div>
214
- );
215
- }
@@ -1,90 +0,0 @@
1
- import { useState } from "react";
2
- import { Link, Navigate, useLocation } from "react-router-dom";
3
-
4
- import { useAuth } from "@/auth/AuthProvider";
5
- import { landingTheme } from "@/theme";
6
- import { landingThemeVars } from "@/theme-vars";
7
-
8
- export function LandingLoginPage() {
9
- const { authenticating, login, user } = useAuth();
10
- const location = useLocation();
11
- const [email, setEmail] = useState("admin@example.com");
12
- const [password, setPassword] = useState("");
13
- const [error, setError] = useState<string | null>(null);
14
-
15
- if (user) {
16
- const destination = (location.state as { from?: { pathname?: string } } | null)?.from?.pathname ?? "/workspace";
17
- return <Navigate to={destination} replace />;
18
- }
19
-
20
- async function handleSubmit(event: React.FormEvent<HTMLFormElement>) {
21
- event.preventDefault();
22
- setError(null);
23
-
24
- try {
25
- await login({ email, password });
26
- } catch (submitError) {
27
- setError(submitError instanceof Error ? submitError.message : "로그인에 실패했습니다.");
28
- }
29
- }
30
-
31
- return (
32
- <div className="min-h-screen bg-[var(--landing-bg)] px-6 py-10 text-[var(--landing-text)]" style={landingThemeVars(landingTheme)}>
33
- <div className="mx-auto grid max-w-5xl overflow-hidden rounded-[36px] border border-[#eadbcb] bg-white shadow-[0_28px_80px_rgba(123,78,42,0.12)] lg:grid-cols-[1fr_420px]">
34
- <section className="bg-[#fff7ef] px-8 py-10 sm:px-12 sm:py-14">
35
- <p className="inline-flex rounded-full bg-[var(--landing-accent-soft)] px-4 py-2 text-sm font-semibold text-[var(--landing-accent)]">
36
- Member access
37
- </p>
38
- <h1 className="mt-7 text-4xl font-black tracking-[-0.04em] text-[#2c261f]">랜딩에서 바로 이어지는 로그인</h1>
39
- <p className="mt-5 max-w-md text-base leading-8 text-[var(--landing-muted)]">
40
- 로그인 성공 시 토큰을 저장하고 보호된 `/workspace`로 이동합니다. 새로고침 후에도 `auth/me`로 세션을 복원합니다.
41
- </p>
42
- <Link className="mt-8 inline-flex rounded-full border border-[#d8cabb] px-5 py-3 text-sm font-semibold" to="/">
43
- Back to landing
44
- </Link>
45
- </section>
46
-
47
- <section className="px-8 py-10 sm:px-10 sm:py-14">
48
- <form className="space-y-5" onSubmit={handleSubmit}>
49
- <div>
50
- <p className="text-sm font-semibold uppercase tracking-[0.18em] text-[var(--landing-accent)]">Sign in</p>
51
- <h2 className="mt-3 text-3xl font-black text-[#2c261f]">Open workspace</h2>
52
- </div>
53
-
54
- <label className="block space-y-2">
55
- <span className="text-sm font-semibold text-[#5b5145]">Email</span>
56
- <input
57
- className="h-11 w-full rounded-xl border border-[#e2d5c7] px-4 text-sm outline-none focus:ring-2 focus:ring-[#e9c590]"
58
- type="email"
59
- value={email}
60
- onChange={(event) => setEmail(event.target.value)}
61
- required
62
- />
63
- </label>
64
-
65
- <label className="block space-y-2">
66
- <span className="text-sm font-semibold text-[#5b5145]">Password</span>
67
- <input
68
- className="h-11 w-full rounded-xl border border-[#e2d5c7] px-4 text-sm outline-none focus:ring-2 focus:ring-[#e9c590]"
69
- type="password"
70
- value={password}
71
- onChange={(event) => setPassword(event.target.value)}
72
- required
73
- />
74
- </label>
75
-
76
- {error ? <p className="text-sm font-medium text-[#c05b2d]">{error}</p> : null}
77
-
78
- <button
79
- className="inline-flex h-11 w-full items-center justify-center rounded-full bg-[#2c261f] px-5 text-sm font-semibold text-white disabled:opacity-60"
80
- disabled={authenticating}
81
- type="submit"
82
- >
83
- {authenticating ? "Signing in..." : "Continue"}
84
- </button>
85
- </form>
86
- </section>
87
- </div>
88
- </div>
89
- );
90
- }