create-einja-app 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (235) hide show
  1. package/README.md +307 -0
  2. package/dist/cli.d.ts +2 -0
  3. package/dist/cli.js +1041 -0
  4. package/dist/cli.js.map +1 -0
  5. package/package.json +62 -0
  6. package/templates/turborepo-pandacss/.biomeignore +15 -0
  7. package/templates/turborepo-pandacss/.claude/hooks/einja/biome-format.sh +49 -0
  8. package/templates/turborepo-pandacss/.claude/hooks/einja/design-doc-check.sh +61 -0
  9. package/templates/turborepo-pandacss/.claude/hooks/einja/detect-secrets.sh +62 -0
  10. package/templates/turborepo-pandacss/.claude/hooks/einja/large-file-warning.sh +42 -0
  11. package/templates/turborepo-pandacss/.claude/hooks/einja/playwright-resize.sh +36 -0
  12. package/templates/turborepo-pandacss/.claude/hooks/einja/typecheck.sh +37 -0
  13. package/templates/turborepo-pandacss/.claude/hooks/einja/unset-volta-recursion.sh +32 -0
  14. package/templates/turborepo-pandacss/.claude/hooks/einja/validate-git-commit.sh +239 -0
  15. package/templates/turborepo-pandacss/.claude/hooks/einja/warn-index-ts.sh +34 -0
  16. package/templates/turborepo-pandacss/.claude/hooks/einja/warn-relative-import.sh +48 -0
  17. package/templates/turborepo-pandacss/.claude/settings.json +174 -0
  18. package/templates/turborepo-pandacss/.claude/skills/create-einja-app-release/SKILL.md +186 -0
  19. package/templates/turborepo-pandacss/.claude/skills/dev-cli-release/SKILL.md +173 -0
  20. package/templates/turborepo-pandacss/.cursor/commands/spec-create.md +227 -0
  21. package/templates/turborepo-pandacss/.cursor/commands/start-dev.md +98 -0
  22. package/templates/turborepo-pandacss/.cursor/commands/task-exec.md +287 -0
  23. package/templates/turborepo-pandacss/.cursor/commands/task-vibe-kanban-loop.md +532 -0
  24. package/templates/turborepo-pandacss/.cursor/commands/update-docs-by-task-specs.md +448 -0
  25. package/templates/turborepo-pandacss/.cursor/mcp.json +45 -0
  26. package/templates/turborepo-pandacss/.cursor/rules/api-rules.mdc +171 -0
  27. package/templates/turborepo-pandacss/.cursor/rules/api-test-rules.mdc +181 -0
  28. package/templates/turborepo-pandacss/.cursor/rules/base-code.mdc +70 -0
  29. package/templates/turborepo-pandacss/.cursor/rules/base-commit-rules.mdc +174 -0
  30. package/templates/turborepo-pandacss/.cursor/rules/base-design.mdc +12 -0
  31. package/templates/turborepo-pandacss/.cursor/rules/base-rules.mdc +231 -0
  32. package/templates/turborepo-pandacss/.cursor/rules/error-handling-rules.mdc +188 -0
  33. package/templates/turborepo-pandacss/.cursor/rules/refactor-rules.mdc +93 -0
  34. package/templates/turborepo-pandacss/.dockerignore +126 -0
  35. package/templates/turborepo-pandacss/.einja-sync.json +35 -0
  36. package/templates/turborepo-pandacss/.env.ci +25 -0
  37. package/templates/turborepo-pandacss/.env.example +35 -0
  38. package/templates/turborepo-pandacss/.env.personal.example +27 -0
  39. package/templates/turborepo-pandacss/.envrc +4 -0
  40. package/templates/turborepo-pandacss/.gitattributes +5 -0
  41. package/templates/turborepo-pandacss/.husky/pre-commit +1 -0
  42. package/templates/turborepo-pandacss/.lintstagedrc.js +24 -0
  43. package/templates/turborepo-pandacss/.mcp.json +45 -0
  44. package/templates/turborepo-pandacss/.node-version +1 -0
  45. package/templates/turborepo-pandacss/.templateignore +60 -0
  46. package/templates/turborepo-pandacss/.vscode/extensions.json +3 -0
  47. package/templates/turborepo-pandacss/CLAUDE.md +415 -0
  48. package/templates/turborepo-pandacss/README.md +322 -0
  49. package/templates/turborepo-pandacss/apps/web/middleware.ts +28 -0
  50. package/templates/turborepo-pandacss/apps/web/next.config.ts +10 -0
  51. package/templates/turborepo-pandacss/apps/web/package.json +80 -0
  52. package/templates/turborepo-pandacss/apps/web/panda.config.ts +114 -0
  53. package/templates/turborepo-pandacss/apps/web/postcss.config.cjs +6 -0
  54. package/templates/turborepo-pandacss/apps/web/public/file.svg +1 -0
  55. package/templates/turborepo-pandacss/apps/web/public/globe.svg +1 -0
  56. package/templates/turborepo-pandacss/apps/web/public/next.svg +1 -0
  57. package/templates/turborepo-pandacss/apps/web/public/vercel.svg +1 -0
  58. package/templates/turborepo-pandacss/apps/web/public/window.svg +1 -0
  59. package/templates/turborepo-pandacss/apps/web/src/app/(authenticated)/dashboard/page.tsx +79 -0
  60. package/templates/turborepo-pandacss/apps/web/src/app/(authenticated)/data/_components/UserTable.tsx +203 -0
  61. package/templates/turborepo-pandacss/apps/web/src/app/(authenticated)/data/page.tsx +57 -0
  62. package/templates/turborepo-pandacss/apps/web/src/app/(authenticated)/layout-client.tsx +31 -0
  63. package/templates/turborepo-pandacss/apps/web/src/app/(authenticated)/layout.tsx +17 -0
  64. package/templates/turborepo-pandacss/apps/web/src/app/(authenticated)/profile/page.tsx +59 -0
  65. package/templates/turborepo-pandacss/apps/web/src/app/api/auth/[...nextauth]/route.ts +3 -0
  66. package/templates/turborepo-pandacss/apps/web/src/app/api/auth/signup/route.ts +70 -0
  67. package/templates/turborepo-pandacss/apps/web/src/app/error.tsx +106 -0
  68. package/templates/turborepo-pandacss/apps/web/src/app/favicon.ico +0 -0
  69. package/templates/turborepo-pandacss/apps/web/src/app/global-error.tsx +110 -0
  70. package/templates/turborepo-pandacss/apps/web/src/app/globals.css +121 -0
  71. package/templates/turborepo-pandacss/apps/web/src/app/layout.tsx +28 -0
  72. package/templates/turborepo-pandacss/apps/web/src/app/not-found.tsx +54 -0
  73. package/templates/turborepo-pandacss/apps/web/src/app/page.module.css +165 -0
  74. package/templates/turborepo-pandacss/apps/web/src/app/page.test.tsx +52 -0
  75. package/templates/turborepo-pandacss/apps/web/src/app/page.tsx +284 -0
  76. package/templates/turborepo-pandacss/apps/web/src/app/signin/page.tsx +296 -0
  77. package/templates/turborepo-pandacss/apps/web/src/app/signup/page.tsx +395 -0
  78. package/templates/turborepo-pandacss/apps/web/src/application/use-cases/UserUseCases.test.ts +229 -0
  79. package/templates/turborepo-pandacss/apps/web/src/application/use-cases/UserUseCases.ts +115 -0
  80. package/templates/turborepo-pandacss/apps/web/src/components/auth/login-button.tsx +35 -0
  81. package/templates/turborepo-pandacss/apps/web/src/components/auth/logout-button.tsx +24 -0
  82. package/templates/turborepo-pandacss/apps/web/src/components/auth/user-avatar.test.tsx +68 -0
  83. package/templates/turborepo-pandacss/apps/web/src/components/auth/user-avatar.tsx +43 -0
  84. package/templates/turborepo-pandacss/apps/web/src/components/dashboard/dashboard-stats.tsx +128 -0
  85. package/templates/turborepo-pandacss/apps/web/src/components/providers/query-provider.tsx +30 -0
  86. package/templates/turborepo-pandacss/apps/web/src/components/providers/session-provider.tsx +12 -0
  87. package/templates/turborepo-pandacss/apps/web/src/components/shared/Sidebar.tsx +175 -0
  88. package/templates/turborepo-pandacss/apps/web/src/components/shared/header.tsx +166 -0
  89. package/templates/turborepo-pandacss/apps/web/src/components/ui/accordion.tsx +64 -0
  90. package/templates/turborepo-pandacss/apps/web/src/components/ui/alert-dialog.tsx +135 -0
  91. package/templates/turborepo-pandacss/apps/web/src/components/ui/alert.tsx +60 -0
  92. package/templates/turborepo-pandacss/apps/web/src/components/ui/aspect-ratio.tsx +9 -0
  93. package/templates/turborepo-pandacss/apps/web/src/components/ui/avatar.tsx +41 -0
  94. package/templates/turborepo-pandacss/apps/web/src/components/ui/badge.tsx +39 -0
  95. package/templates/turborepo-pandacss/apps/web/src/components/ui/breadcrumb.tsx +101 -0
  96. package/templates/turborepo-pandacss/apps/web/src/components/ui/button.tsx +56 -0
  97. package/templates/turborepo-pandacss/apps/web/src/components/ui/card.tsx +75 -0
  98. package/templates/turborepo-pandacss/apps/web/src/components/ui/checkbox.tsx +29 -0
  99. package/templates/turborepo-pandacss/apps/web/src/components/ui/data-table.tsx +189 -0
  100. package/templates/turborepo-pandacss/apps/web/src/components/ui/dialog-hook.tsx +210 -0
  101. package/templates/turborepo-pandacss/apps/web/src/components/ui/dialog.tsx +129 -0
  102. package/templates/turborepo-pandacss/apps/web/src/components/ui/drawer.tsx +124 -0
  103. package/templates/turborepo-pandacss/apps/web/src/components/ui/dropdown-menu.tsx +228 -0
  104. package/templates/turborepo-pandacss/apps/web/src/components/ui/form.tsx +152 -0
  105. package/templates/turborepo-pandacss/apps/web/src/components/ui/hover-card.tsx +38 -0
  106. package/templates/turborepo-pandacss/apps/web/src/components/ui/input.tsx +21 -0
  107. package/templates/turborepo-pandacss/apps/web/src/components/ui/label.tsx +21 -0
  108. package/templates/turborepo-pandacss/apps/web/src/components/ui/pagination.tsx +105 -0
  109. package/templates/turborepo-pandacss/apps/web/src/components/ui/popover.tsx +42 -0
  110. package/templates/turborepo-pandacss/apps/web/src/components/ui/progress.tsx +28 -0
  111. package/templates/turborepo-pandacss/apps/web/src/components/ui/select.tsx +170 -0
  112. package/templates/turborepo-pandacss/apps/web/src/components/ui/separator.tsx +28 -0
  113. package/templates/turborepo-pandacss/apps/web/src/components/ui/skeleton.tsx +13 -0
  114. package/templates/turborepo-pandacss/apps/web/src/components/ui/sonner.tsx +25 -0
  115. package/templates/turborepo-pandacss/apps/web/src/components/ui/table.tsx +92 -0
  116. package/templates/turborepo-pandacss/apps/web/src/components/ui/tabs.tsx +54 -0
  117. package/templates/turborepo-pandacss/apps/web/src/components/ui/textarea.tsx +18 -0
  118. package/templates/turborepo-pandacss/apps/web/src/components/ui/tooltip.tsx +57 -0
  119. package/templates/turborepo-pandacss/apps/web/src/components/ui/typography.tsx +158 -0
  120. package/templates/turborepo-pandacss/apps/web/src/lib/auth/guard.ts +36 -0
  121. package/templates/turborepo-pandacss/apps/web/src/lib/auth/index.ts +22 -0
  122. package/templates/turborepo-pandacss/apps/web/src/lib/prisma.ts +3 -0
  123. package/templates/turborepo-pandacss/apps/web/src/lib/utils.ts +6 -0
  124. package/templates/turborepo-pandacss/apps/web/test/globals.d.ts +1 -0
  125. package/templates/turborepo-pandacss/apps/web/test/matchers.d.ts +1 -0
  126. package/templates/turborepo-pandacss/apps/web/test/setup.ts +22 -0
  127. package/templates/turborepo-pandacss/apps/web/tsconfig.json +37 -0
  128. package/templates/turborepo-pandacss/apps/web/vitest.config.ts +20 -0
  129. package/templates/turborepo-pandacss/apps/web/vitest.d.ts +2 -0
  130. package/templates/turborepo-pandacss/biome.json +60 -0
  131. package/templates/turborepo-pandacss/components.json +21 -0
  132. package/templates/turborepo-pandacss/docker-compose.yml +27 -0
  133. package/templates/turborepo-pandacss/middleware.ts +32 -0
  134. package/templates/turborepo-pandacss/next.config.ts +10 -0
  135. package/templates/turborepo-pandacss/package-lock.json +9346 -0
  136. package/templates/turborepo-pandacss/package.json +64 -0
  137. package/templates/turborepo-pandacss/packages/config/package.json +41 -0
  138. package/templates/turborepo-pandacss/packages/config/panda.config.ts +114 -0
  139. package/templates/turborepo-pandacss/packages/config/src/index.ts +24 -0
  140. package/templates/turborepo-pandacss/packages/config/src/worktree-config-loader.ts +129 -0
  141. package/templates/turborepo-pandacss/packages/config/src/worktree-config.ts +75 -0
  142. package/templates/turborepo-pandacss/packages/config/tsconfig.build.json +19 -0
  143. package/templates/turborepo-pandacss/packages/config/tsconfig.json +24 -0
  144. package/templates/turborepo-pandacss/packages/config/typescript/base.json +19 -0
  145. package/templates/turborepo-pandacss/packages/front-core/package.json +24 -0
  146. package/templates/turborepo-pandacss/packages/front-core/src/auth/config.ts +84 -0
  147. package/templates/turborepo-pandacss/packages/front-core/src/auth/index.ts +5 -0
  148. package/templates/turborepo-pandacss/packages/front-core/src/auth/types/next-auth.d.ts +20 -0
  149. package/templates/turborepo-pandacss/packages/front-core/src/auth/utils.ts +29 -0
  150. package/templates/turborepo-pandacss/packages/front-core/src/context/index.ts +2 -0
  151. package/templates/turborepo-pandacss/packages/front-core/src/hooks/index.ts +2 -0
  152. package/templates/turborepo-pandacss/packages/front-core/src/index.ts +4 -0
  153. package/templates/turborepo-pandacss/packages/front-core/src/utils/index.ts +2 -0
  154. package/templates/turborepo-pandacss/packages/front-core/tsconfig.json +14 -0
  155. package/templates/turborepo-pandacss/packages/server-core/package.json +32 -0
  156. package/templates/turborepo-pandacss/packages/server-core/prisma/schema.prisma +102 -0
  157. package/templates/turborepo-pandacss/packages/server-core/prisma/seed.ts +67 -0
  158. package/templates/turborepo-pandacss/packages/server-core/prisma.config.ts +8 -0
  159. package/templates/turborepo-pandacss/packages/server-core/src/__generated__/fabbrica/index.d.ts +270 -0
  160. package/templates/turborepo-pandacss/packages/server-core/src/__generated__/fabbrica/index.js +484 -0
  161. package/templates/turborepo-pandacss/packages/server-core/src/core/result.test.ts +78 -0
  162. package/templates/turborepo-pandacss/packages/server-core/src/core/result.ts +53 -0
  163. package/templates/turborepo-pandacss/packages/server-core/src/domain/.gitkeep +0 -0
  164. package/templates/turborepo-pandacss/packages/server-core/src/domain/entities/User.test.ts +232 -0
  165. package/templates/turborepo-pandacss/packages/server-core/src/domain/entities/User.ts +105 -0
  166. package/templates/turborepo-pandacss/packages/server-core/src/domain/repository-interfaces/IUserRepository.ts +101 -0
  167. package/templates/turborepo-pandacss/packages/server-core/src/infrastructure/database/client.ts +15 -0
  168. package/templates/turborepo-pandacss/packages/server-core/src/infrastructure/database/mappers/UserMapper.test.ts +278 -0
  169. package/templates/turborepo-pandacss/packages/server-core/src/infrastructure/database/mappers/UserMapper.ts +103 -0
  170. package/templates/turborepo-pandacss/packages/server-core/src/infrastructure/database/repositories/UserRepository.test.ts +317 -0
  171. package/templates/turborepo-pandacss/packages/server-core/src/infrastructure/database/repositories/UserRepository.ts +169 -0
  172. package/templates/turborepo-pandacss/packages/server-core/src/testing/factories/index.ts +22 -0
  173. package/templates/turborepo-pandacss/packages/server-core/src/testing/factories/user.factory.ts +123 -0
  174. package/templates/turborepo-pandacss/packages/server-core/src/testing/fixtures/users.ts +92 -0
  175. package/templates/turborepo-pandacss/packages/server-core/src/testing/helpers/date.ts +49 -0
  176. package/templates/turborepo-pandacss/packages/server-core/src/testing/helpers/password.ts +50 -0
  177. package/templates/turborepo-pandacss/packages/server-core/src/testing/index.ts +26 -0
  178. package/templates/turborepo-pandacss/packages/server-core/tsconfig.json +14 -0
  179. package/templates/turborepo-pandacss/packages/server-core/vitest.config.ts +15 -0
  180. package/templates/turborepo-pandacss/packages/ui/package.json +37 -0
  181. package/templates/turborepo-pandacss/packages/ui/src/accordion.tsx +66 -0
  182. package/templates/turborepo-pandacss/packages/ui/src/alert-dialog.tsx +157 -0
  183. package/templates/turborepo-pandacss/packages/ui/src/alert.tsx +66 -0
  184. package/templates/turborepo-pandacss/packages/ui/src/aspect-ratio.tsx +11 -0
  185. package/templates/turborepo-pandacss/packages/ui/src/avatar.tsx +53 -0
  186. package/templates/turborepo-pandacss/packages/ui/src/badge.tsx +46 -0
  187. package/templates/turborepo-pandacss/packages/ui/src/breadcrumb.tsx +108 -0
  188. package/templates/turborepo-pandacss/packages/ui/src/button.tsx +59 -0
  189. package/templates/turborepo-pandacss/packages/ui/src/card.tsx +92 -0
  190. package/templates/turborepo-pandacss/packages/ui/src/checkbox.tsx +32 -0
  191. package/templates/turborepo-pandacss/packages/ui/src/data-table.tsx +216 -0
  192. package/templates/turborepo-pandacss/packages/ui/src/dialog-hook.tsx +226 -0
  193. package/templates/turborepo-pandacss/packages/ui/src/dialog.tsx +143 -0
  194. package/templates/turborepo-pandacss/packages/ui/src/drawer.tsx +135 -0
  195. package/templates/turborepo-pandacss/packages/ui/src/dropdown-menu.tsx +257 -0
  196. package/templates/turborepo-pandacss/packages/ui/src/form.tsx +168 -0
  197. package/templates/turborepo-pandacss/packages/ui/src/hover-card.tsx +44 -0
  198. package/templates/turborepo-pandacss/packages/ui/src/input.tsx +21 -0
  199. package/templates/turborepo-pandacss/packages/ui/src/label.tsx +24 -0
  200. package/templates/turborepo-pandacss/packages/ui/src/lib/utils.ts +6 -0
  201. package/templates/turborepo-pandacss/packages/ui/src/pagination.tsx +126 -0
  202. package/templates/turborepo-pandacss/packages/ui/src/popover.tsx +48 -0
  203. package/templates/turborepo-pandacss/packages/ui/src/progress.tsx +31 -0
  204. package/templates/turborepo-pandacss/packages/ui/src/select.tsx +185 -0
  205. package/templates/turborepo-pandacss/packages/ui/src/separator.tsx +28 -0
  206. package/templates/turborepo-pandacss/packages/ui/src/skeleton.tsx +13 -0
  207. package/templates/turborepo-pandacss/packages/ui/src/sonner.tsx +25 -0
  208. package/templates/turborepo-pandacss/packages/ui/src/table.tsx +116 -0
  209. package/templates/turborepo-pandacss/packages/ui/src/tabs.tsx +66 -0
  210. package/templates/turborepo-pandacss/packages/ui/src/textarea.tsx +18 -0
  211. package/templates/turborepo-pandacss/packages/ui/src/tooltip.tsx +61 -0
  212. package/templates/turborepo-pandacss/packages/ui/src/typography.tsx +187 -0
  213. package/templates/turborepo-pandacss/packages/ui/tsconfig.json +20 -0
  214. package/templates/turborepo-pandacss/panda.config.ts +114 -0
  215. package/templates/turborepo-pandacss/pnpm-lock.yaml +9032 -0
  216. package/templates/turborepo-pandacss/pnpm-workspace.yaml +11 -0
  217. package/templates/turborepo-pandacss/postcss.config.cjs +6 -0
  218. package/templates/turborepo-pandacss/prisma/schema.prisma +82 -0
  219. package/templates/turborepo-pandacss/public/file.svg +1 -0
  220. package/templates/turborepo-pandacss/public/globe.svg +1 -0
  221. package/templates/turborepo-pandacss/public/next.svg +1 -0
  222. package/templates/turborepo-pandacss/public/vercel.svg +1 -0
  223. package/templates/turborepo-pandacss/public/window.svg +1 -0
  224. package/templates/turborepo-pandacss/scripts/cli-template-update.ts +387 -0
  225. package/templates/turborepo-pandacss/scripts/env-show.ts +129 -0
  226. package/templates/turborepo-pandacss/scripts/env.ts +555 -0
  227. package/templates/turborepo-pandacss/scripts/init.sh +92 -0
  228. package/templates/turborepo-pandacss/scripts/setup-dev.ts +640 -0
  229. package/templates/turborepo-pandacss/scripts/template-update.ts +277 -0
  230. package/templates/turborepo-pandacss/scripts/worktree/dev.ts +872 -0
  231. package/templates/turborepo-pandacss/test/globals.d.ts +1 -0
  232. package/templates/turborepo-pandacss/test/setup.ts +22 -0
  233. package/templates/turborepo-pandacss/tsconfig.json +46 -0
  234. package/templates/turborepo-pandacss/turbo.json +57 -0
  235. package/templates/turborepo-pandacss/vitest.config.ts +20 -0
@@ -0,0 +1,296 @@
1
+ "use client";
2
+
3
+ import { Button } from "@/components/ui/button";
4
+ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
5
+ import { Input } from "@/components/ui/input";
6
+ import { Label } from "@/components/ui/label";
7
+ import { signIn } from "next-auth/react";
8
+ import Link from "next/link";
9
+ import { useRouter } from "next/navigation";
10
+ import { useState } from "react";
11
+ import { css } from "../../../styled-system/css";
12
+ import { center, vstack } from "../../../styled-system/patterns";
13
+
14
+ export default function LoginPage() {
15
+ const [email, setEmail] = useState("");
16
+ const [password, setPassword] = useState("");
17
+ const [isLoading, setIsLoading] = useState(false);
18
+ const [error, setError] = useState("");
19
+ const router = useRouter();
20
+
21
+ const handleSubmit = async (e: React.FormEvent) => {
22
+ e.preventDefault();
23
+ setIsLoading(true);
24
+ setError("");
25
+
26
+ try {
27
+ // redirect: falseで手動リダイレクト制御
28
+ const result = await signIn("credentials", {
29
+ email,
30
+ password,
31
+ redirect: false,
32
+ });
33
+
34
+ console.log("SignIn result:", result);
35
+
36
+ if (result?.error) {
37
+ console.error("SignIn error:", result.error);
38
+ setError("ログインに失敗しました。メールアドレスとパスワードを確認してください。");
39
+ } else if (result?.ok) {
40
+ console.log("SignIn successful, redirecting to dashboard");
41
+ // ログイン成功時に手動でダッシュボードにリダイレクト
42
+ router.push("/dashboard");
43
+ router.refresh(); // セッション情報を確実に更新
44
+ } else {
45
+ console.warn("Unexpected signIn result:", result);
46
+ setError("ログインの結果が不明です。");
47
+ }
48
+ } catch (error) {
49
+ setError("ログイン中にエラーが発生しました。");
50
+ } finally {
51
+ setIsLoading(false);
52
+ }
53
+ };
54
+
55
+ return (
56
+ <div
57
+ className={css({
58
+ minHeight: "100vh",
59
+ background: "linear-gradient(135deg, {colors.blue.50} 0%, {colors.purple.50} 100%)",
60
+ display: "flex",
61
+ alignItems: "center",
62
+ justifyContent: "center",
63
+ padding: "2rem 1rem",
64
+ })}
65
+ >
66
+ <div
67
+ className={css({
68
+ width: "100%",
69
+ maxWidth: "md",
70
+ display: "flex",
71
+ flexDirection: "column",
72
+ gap: "2rem",
73
+ })}
74
+ >
75
+ {/* ロゴ・ヘッダー部分 */}
76
+ <div className={center({ flexDirection: "column", gap: "1rem" })}>
77
+ <div
78
+ className={css({
79
+ width: "4rem",
80
+ height: "4rem",
81
+ background: "linear-gradient(135deg, {colors.blue.600}, {colors.purple.600})",
82
+ borderRadius: "50%",
83
+ display: "flex",
84
+ alignItems: "center",
85
+ justifyContent: "center",
86
+ boxShadow: "lg",
87
+ })}
88
+ >
89
+ <span
90
+ className={css({
91
+ fontSize: "1.5rem",
92
+ fontWeight: "bold",
93
+ color: "white",
94
+ })}
95
+ >
96
+ E
97
+ </span>
98
+ </div>
99
+ <h1
100
+ className={css({
101
+ fontSize: "2xl",
102
+ fontWeight: "bold",
103
+ color: "{colors.gray.800}",
104
+ textAlign: "center",
105
+ })}
106
+ >
107
+ Einja Management
108
+ </h1>
109
+ </div>
110
+
111
+ {/* ログインカード */}
112
+ <Card
113
+ className={css({
114
+ boxShadow: "2xl",
115
+ border: "none",
116
+ background: "white/90",
117
+ backdropFilter: "blur(10px)",
118
+ })}
119
+ >
120
+ <CardHeader
121
+ className={vstack({
122
+ gap: "0.5rem",
123
+ alignItems: "center",
124
+ padding: "1.5rem",
125
+ })}
126
+ >
127
+ <CardTitle
128
+ className={css({
129
+ fontSize: "1.5rem",
130
+ fontWeight: "semibold",
131
+ color: "{colors.gray.800}",
132
+ })}
133
+ >
134
+ ログイン
135
+ </CardTitle>
136
+ <CardDescription
137
+ className={css({
138
+ color: "{colors.gray.600}",
139
+ textAlign: "center",
140
+ })}
141
+ >
142
+ アカウントにログインしてください
143
+ </CardDescription>
144
+ </CardHeader>
145
+ <CardContent className={css({ padding: "1.5rem", paddingTop: "0" })}>
146
+ <form onSubmit={handleSubmit} className={vstack({ gap: "1.5rem" })}>
147
+ <div className={vstack({ gap: "0.5rem", width: "100%" })}>
148
+ <Label
149
+ htmlFor="email"
150
+ className={css({
151
+ fontSize: "sm",
152
+ fontWeight: "medium",
153
+ color: "{colors.gray.700}",
154
+ })}
155
+ >
156
+ メールアドレス
157
+ </Label>
158
+ <Input
159
+ id="email"
160
+ type="email"
161
+ placeholder="your@email.com"
162
+ value={email}
163
+ onChange={(e) => setEmail(e.target.value)}
164
+ required
165
+ disabled={isLoading}
166
+ className={css({
167
+ height: "2.75rem",
168
+ fontSize: "sm",
169
+ border: "2px solid {colors.gray.200}",
170
+ _focus: {
171
+ borderColor: "{colors.blue.500}",
172
+ boxShadow: "0 0 0 3px {colors.blue.100}",
173
+ },
174
+ _disabled: {
175
+ opacity: 0.6,
176
+ cursor: "not-allowed",
177
+ },
178
+ })}
179
+ />
180
+ </div>
181
+ <div className={vstack({ gap: "0.5rem", width: "100%" })}>
182
+ <Label
183
+ htmlFor="password"
184
+ className={css({
185
+ fontSize: "sm",
186
+ fontWeight: "medium",
187
+ color: "{colors.gray.700}",
188
+ })}
189
+ >
190
+ パスワード
191
+ </Label>
192
+ <Input
193
+ id="password"
194
+ type="password"
195
+ placeholder="パスワードを入力"
196
+ value={password}
197
+ onChange={(e) => setPassword(e.target.value)}
198
+ required
199
+ disabled={isLoading}
200
+ className={css({
201
+ height: "2.75rem",
202
+ fontSize: "sm",
203
+ border: "2px solid {colors.gray.200}",
204
+ _focus: {
205
+ borderColor: "{colors.blue.500}",
206
+ boxShadow: "0 0 0 3px {colors.blue.100}",
207
+ },
208
+ _disabled: {
209
+ opacity: 0.6,
210
+ cursor: "not-allowed",
211
+ },
212
+ })}
213
+ />
214
+ </div>
215
+ {error && (
216
+ <div
217
+ className={css({
218
+ padding: "0.75rem",
219
+ background: "{colors.red.50}",
220
+ border: "1px solid {colors.red.200}",
221
+ borderRadius: "md",
222
+ fontSize: "sm",
223
+ color: "{colors.red.700}",
224
+ textAlign: "center",
225
+ width: "100%",
226
+ })}
227
+ >
228
+ {error}
229
+ </div>
230
+ )}
231
+ <Button
232
+ type="submit"
233
+ disabled={isLoading}
234
+ className={css({
235
+ width: "100%",
236
+ height: "2.75rem",
237
+ background: "linear-gradient(135deg, {colors.blue.600}, {colors.purple.600})",
238
+ border: "none",
239
+ borderRadius: "md",
240
+ color: "white",
241
+ fontSize: "sm",
242
+ fontWeight: "medium",
243
+ cursor: "pointer",
244
+ transition: "all 0.2s",
245
+ _hover: {
246
+ transform: "translateY(-1px)",
247
+ boxShadow: "lg",
248
+ },
249
+ _active: {
250
+ transform: "translateY(0)",
251
+ },
252
+ _disabled: {
253
+ opacity: 0.6,
254
+ cursor: "not-allowed",
255
+ transform: "none",
256
+ },
257
+ })}
258
+ >
259
+ {isLoading ? "ログイン中..." : "ログイン"}
260
+ </Button>
261
+ </form>
262
+
263
+ {/* サインアップリンク */}
264
+ <div
265
+ className={css({
266
+ marginTop: "1.5rem",
267
+ textAlign: "center",
268
+ })}
269
+ >
270
+ <p
271
+ className={css({
272
+ fontSize: "sm",
273
+ color: "{colors.gray.600}",
274
+ })}
275
+ >
276
+ アカウントをお持ちでない方は{" "}
277
+ <Link
278
+ href="/signup"
279
+ className={css({
280
+ color: "{colors.blue.600}",
281
+ fontWeight: "medium",
282
+ _hover: {
283
+ textDecoration: "underline",
284
+ },
285
+ })}
286
+ >
287
+ アカウント作成
288
+ </Link>
289
+ </p>
290
+ </div>
291
+ </CardContent>
292
+ </Card>
293
+ </div>
294
+ </div>
295
+ );
296
+ }
@@ -0,0 +1,395 @@
1
+ "use client";
2
+
3
+ import { Button } from "@/components/ui/button";
4
+ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
5
+ import { Input } from "@/components/ui/input";
6
+ import { Label } from "@/components/ui/label";
7
+ import Link from "next/link";
8
+ import { useRouter } from "next/navigation";
9
+ import { useState } from "react";
10
+ import { css } from "../../../styled-system/css";
11
+ import { center, vstack } from "../../../styled-system/patterns";
12
+
13
+ export default function SignUpPage() {
14
+ const [name, setName] = useState("");
15
+ const [email, setEmail] = useState("");
16
+ const [password, setPassword] = useState("");
17
+ const [confirmPassword, setConfirmPassword] = useState("");
18
+ const [isLoading, setIsLoading] = useState(false);
19
+ const [error, setError] = useState("");
20
+ const [success, setSuccess] = useState("");
21
+ const router = useRouter();
22
+
23
+ const handleSubmit = async (e: React.FormEvent) => {
24
+ e.preventDefault();
25
+ setIsLoading(true);
26
+ setError("");
27
+ setSuccess("");
28
+
29
+ // バリデーション
30
+ if (password !== confirmPassword) {
31
+ setError("パスワードが一致しません。");
32
+ setIsLoading(false);
33
+ return;
34
+ }
35
+
36
+ if (password.length < 8) {
37
+ setError("パスワードは8文字以上で入力してください。");
38
+ setIsLoading(false);
39
+ return;
40
+ }
41
+
42
+ try {
43
+ const response = await fetch("/api/auth/signup", {
44
+ method: "POST",
45
+ headers: {
46
+ "Content-Type": "application/json",
47
+ },
48
+ body: JSON.stringify({
49
+ name,
50
+ email,
51
+ password,
52
+ }),
53
+ });
54
+
55
+ const data = await response.json();
56
+
57
+ if (!response.ok) {
58
+ const errorMessage = data.error || "登録に失敗しました。";
59
+ const details = data.details ? ` (${data.details})` : "";
60
+ throw new Error(errorMessage + details);
61
+ }
62
+
63
+ setSuccess("アカウントが作成されました。サインインページに移動します...");
64
+
65
+ setTimeout(() => {
66
+ router.push("/dashboard");
67
+ }, 2000);
68
+ } catch (error) {
69
+ console.error("Signup frontend error:", error);
70
+ setError(error instanceof Error ? error.message : "登録中にエラーが発生しました。");
71
+ } finally {
72
+ setIsLoading(false);
73
+ }
74
+ };
75
+
76
+ return (
77
+ <div
78
+ className={css({
79
+ minHeight: "100vh",
80
+ background: "linear-gradient(135deg, {colors.green.50} 0%, {colors.blue.50} 100%)",
81
+ display: "flex",
82
+ alignItems: "center",
83
+ justifyContent: "center",
84
+ padding: "2rem 1rem",
85
+ })}
86
+ >
87
+ <div
88
+ className={css({
89
+ width: "100%",
90
+ maxWidth: "md",
91
+ display: "flex",
92
+ flexDirection: "column",
93
+ gap: "2rem",
94
+ })}
95
+ >
96
+ {/* ロゴ・ヘッダー部分 */}
97
+ <div className={center({ flexDirection: "column", gap: "1rem" })}>
98
+ <div
99
+ className={css({
100
+ width: "4rem",
101
+ height: "4rem",
102
+ background: "linear-gradient(135deg, {colors.green.600}, {colors.blue.600})",
103
+ borderRadius: "50%",
104
+ display: "flex",
105
+ alignItems: "center",
106
+ justifyContent: "center",
107
+ boxShadow: "lg",
108
+ })}
109
+ >
110
+ <span
111
+ className={css({
112
+ fontSize: "1.5rem",
113
+ fontWeight: "bold",
114
+ color: "white",
115
+ })}
116
+ >
117
+ E
118
+ </span>
119
+ </div>
120
+ <h1
121
+ className={css({
122
+ fontSize: "2xl",
123
+ fontWeight: "bold",
124
+ color: "{colors.gray.800}",
125
+ textAlign: "center",
126
+ })}
127
+ >
128
+ Einja Management
129
+ </h1>
130
+ </div>
131
+
132
+ {/* サインアップカード */}
133
+ <Card
134
+ className={css({
135
+ boxShadow: "2xl",
136
+ border: "none",
137
+ background: "white/90",
138
+ backdropFilter: "blur(10px)",
139
+ })}
140
+ >
141
+ <CardHeader className={vstack({ gap: "0.5rem", alignItems: "center" })}>
142
+ <CardTitle
143
+ className={css({
144
+ fontSize: "1.5rem",
145
+ fontWeight: "semibold",
146
+ color: "{colors.gray.800}",
147
+ })}
148
+ >
149
+ アカウント作成
150
+ </CardTitle>
151
+ <CardDescription
152
+ className={css({
153
+ color: "{colors.gray.600}",
154
+ textAlign: "center",
155
+ })}
156
+ >
157
+ 新しいアカウントを作成してください
158
+ </CardDescription>
159
+ </CardHeader>
160
+ <CardContent>
161
+ <form onSubmit={handleSubmit} className={vstack({ gap: "1.5rem" })}>
162
+ <div className={vstack({ gap: "0.5rem", width: "100%" })}>
163
+ <Label
164
+ htmlFor="name"
165
+ className={css({
166
+ fontSize: "sm",
167
+ fontWeight: "medium",
168
+ color: "{colors.gray.700}",
169
+ })}
170
+ >
171
+ お名前
172
+ </Label>
173
+ <Input
174
+ id="name"
175
+ type="text"
176
+ placeholder="山田 太郎"
177
+ value={name}
178
+ onChange={(e) => setName(e.target.value)}
179
+ required
180
+ disabled={isLoading}
181
+ className={css({
182
+ height: "2.75rem",
183
+ fontSize: "sm",
184
+ border: "2px solid {colors.gray.200}",
185
+ _focus: {
186
+ borderColor: "{colors.green.500}",
187
+ boxShadow: "0 0 0 3px {colors.green.100}",
188
+ },
189
+ _disabled: {
190
+ opacity: 0.6,
191
+ cursor: "not-allowed",
192
+ },
193
+ })}
194
+ />
195
+ </div>
196
+ <div className={vstack({ gap: "0.5rem", width: "100%" })}>
197
+ <Label
198
+ htmlFor="email"
199
+ className={css({
200
+ fontSize: "sm",
201
+ fontWeight: "medium",
202
+ color: "{colors.gray.700}",
203
+ })}
204
+ >
205
+ メールアドレス
206
+ </Label>
207
+ <Input
208
+ id="email"
209
+ type="email"
210
+ placeholder="your@email.com"
211
+ value={email}
212
+ onChange={(e) => setEmail(e.target.value)}
213
+ required
214
+ disabled={isLoading}
215
+ className={css({
216
+ height: "2.75rem",
217
+ fontSize: "sm",
218
+ border: "2px solid {colors.gray.200}",
219
+ _focus: {
220
+ borderColor: "{colors.green.500}",
221
+ boxShadow: "0 0 0 3px {colors.green.100}",
222
+ },
223
+ _disabled: {
224
+ opacity: 0.6,
225
+ cursor: "not-allowed",
226
+ },
227
+ })}
228
+ />
229
+ </div>
230
+ <div className={vstack({ gap: "0.5rem", width: "100%" })}>
231
+ <Label
232
+ htmlFor="password"
233
+ className={css({
234
+ fontSize: "sm",
235
+ fontWeight: "medium",
236
+ color: "{colors.gray.700}",
237
+ })}
238
+ >
239
+ パスワード
240
+ </Label>
241
+ <Input
242
+ id="password"
243
+ type="password"
244
+ placeholder="8文字以上で入力"
245
+ value={password}
246
+ onChange={(e) => setPassword(e.target.value)}
247
+ required
248
+ disabled={isLoading}
249
+ className={css({
250
+ height: "2.75rem",
251
+ fontSize: "sm",
252
+ border: "2px solid {colors.gray.200}",
253
+ _focus: {
254
+ borderColor: "{colors.green.500}",
255
+ boxShadow: "0 0 0 3px {colors.green.100}",
256
+ },
257
+ _disabled: {
258
+ opacity: 0.6,
259
+ cursor: "not-allowed",
260
+ },
261
+ })}
262
+ />
263
+ </div>
264
+ <div className={vstack({ gap: "0.5rem", width: "100%" })}>
265
+ <Label
266
+ htmlFor="confirmPassword"
267
+ className={css({
268
+ fontSize: "sm",
269
+ fontWeight: "medium",
270
+ color: "{colors.gray.700}",
271
+ })}
272
+ >
273
+ パスワード(確認)
274
+ </Label>
275
+ <Input
276
+ id="confirmPassword"
277
+ type="password"
278
+ placeholder="パスワードを再入力"
279
+ value={confirmPassword}
280
+ onChange={(e) => setConfirmPassword(e.target.value)}
281
+ required
282
+ disabled={isLoading}
283
+ className={css({
284
+ height: "2.75rem",
285
+ fontSize: "sm",
286
+ border: "2px solid {colors.gray.200}",
287
+ _focus: {
288
+ borderColor: "{colors.green.500}",
289
+ boxShadow: "0 0 0 3px {colors.green.100}",
290
+ },
291
+ _disabled: {
292
+ opacity: 0.6,
293
+ cursor: "not-allowed",
294
+ },
295
+ })}
296
+ />
297
+ </div>
298
+ {error && (
299
+ <div
300
+ className={css({
301
+ padding: "0.75rem",
302
+ background: "{colors.red.50}",
303
+ border: "1px solid {colors.red.200}",
304
+ borderRadius: "md",
305
+ fontSize: "sm",
306
+ color: "{colors.red.700}",
307
+ textAlign: "center",
308
+ width: "100%",
309
+ })}
310
+ >
311
+ {error}
312
+ </div>
313
+ )}
314
+ {success && (
315
+ <div
316
+ className={css({
317
+ padding: "0.75rem",
318
+ background: "{colors.green.50}",
319
+ border: "1px solid {colors.green.200}",
320
+ borderRadius: "md",
321
+ fontSize: "sm",
322
+ color: "{colors.green.700}",
323
+ textAlign: "center",
324
+ width: "100%",
325
+ })}
326
+ >
327
+ {success}
328
+ </div>
329
+ )}
330
+ <Button
331
+ type="submit"
332
+ disabled={isLoading}
333
+ className={css({
334
+ width: "100%",
335
+ height: "2.75rem",
336
+ background: "linear-gradient(135deg, {colors.green.600}, {colors.blue.600})",
337
+ border: "none",
338
+ borderRadius: "md",
339
+ color: "white",
340
+ fontSize: "sm",
341
+ fontWeight: "medium",
342
+ cursor: "pointer",
343
+ transition: "all 0.2s",
344
+ _hover: {
345
+ transform: "translateY(-1px)",
346
+ boxShadow: "lg",
347
+ },
348
+ _active: {
349
+ transform: "translateY(0)",
350
+ },
351
+ _disabled: {
352
+ opacity: 0.6,
353
+ cursor: "not-allowed",
354
+ transform: "none",
355
+ },
356
+ })}
357
+ >
358
+ {isLoading ? "アカウント作成中..." : "アカウント作成"}
359
+ </Button>
360
+ </form>
361
+
362
+ {/* サインインリンク */}
363
+ <div
364
+ className={css({
365
+ marginTop: "1.5rem",
366
+ textAlign: "center",
367
+ })}
368
+ >
369
+ <p
370
+ className={css({
371
+ fontSize: "sm",
372
+ color: "{colors.gray.600}",
373
+ })}
374
+ >
375
+ 既にアカウントをお持ちですか?{" "}
376
+ <Link
377
+ href="/signin"
378
+ className={css({
379
+ color: "{colors.blue.600}",
380
+ fontWeight: "medium",
381
+ _hover: {
382
+ textDecoration: "underline",
383
+ },
384
+ })}
385
+ >
386
+ サインイン
387
+ </Link>
388
+ </p>
389
+ </div>
390
+ </CardContent>
391
+ </Card>
392
+ </div>
393
+ </div>
394
+ );
395
+ }