create-einja-app 0.2.15 → 0.2.18

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 (272) hide show
  1. package/package.json +2 -2
  2. package/templates/default/.claude/hooks/einja/biome-format.sh +2 -2
  3. package/templates/default/.claude/hooks/einja/playwright-resize.sh +12 -2
  4. package/templates/default/.claude/settings.json +15 -0
  5. package/templates/default/.cursor/commands/task-vibe-kanban-loop.md +107 -42
  6. package/templates/default/.env.develop +0 -4
  7. package/templates/default/.env.example +1 -0
  8. package/templates/default/.env.preview +0 -4
  9. package/templates/default/.env.staging +19 -0
  10. package/templates/default/.github/actions/ci/action.yml +39 -0
  11. package/templates/default/.github/actions/migrate/action.yml +39 -0
  12. package/templates/default/.github/actions/neon-export-env/action.yml +28 -0
  13. package/templates/default/.github/actions/setup/action.yml +20 -0
  14. package/templates/default/.github/workflows/claude.yml +1 -0
  15. package/templates/default/.github/workflows/{cleanup-neon-branches.yml → cleanup-pr-preview-db.yml} +28 -24
  16. package/templates/default/.github/workflows/cleanup-pr-preview-on-close.yml +50 -0
  17. package/templates/default/.github/workflows/deploy-pr-preview.yml +398 -0
  18. package/templates/default/.github/workflows/deploy-stable-branches.yml +259 -0
  19. package/templates/default/.github/workflows/release-create-einja-app.yml +95 -0
  20. package/templates/default/.mcp.json +6 -9
  21. package/templates/default/.serena/project.yml +22 -1
  22. package/templates/default/CLAUDE.md +52 -10
  23. package/templates/default/README.md +5 -14
  24. package/templates/default/apps/admin/next.config.ts +11 -0
  25. package/templates/default/apps/admin/package.json +55 -0
  26. package/templates/default/apps/admin/postcss.config.cjs +5 -0
  27. package/templates/default/apps/admin/src/app/(auth)/forgot-password/page.tsx +97 -0
  28. package/templates/default/apps/admin/src/app/(auth)/layout.tsx +18 -0
  29. package/templates/default/apps/admin/src/app/(auth)/otp/page.tsx +121 -0
  30. package/templates/default/apps/admin/src/app/(auth)/sign-in/page.tsx +145 -0
  31. package/templates/default/apps/admin/src/app/(auth)/sign-up/page.tsx +199 -0
  32. package/templates/default/apps/admin/src/app/(errors)/401/page.tsx +27 -0
  33. package/templates/default/apps/admin/src/app/(errors)/403/page.tsx +28 -0
  34. package/templates/default/apps/admin/src/app/(errors)/500/page.tsx +29 -0
  35. package/templates/default/apps/admin/src/app/(errors)/layout.tsx +7 -0
  36. package/templates/default/apps/admin/src/app/(errors)/maintenance/page.tsx +25 -0
  37. package/templates/default/apps/admin/src/app/dashboard/_components/analytics-chart.tsx +68 -0
  38. package/templates/default/apps/admin/src/app/dashboard/_components/analytics.tsx +182 -0
  39. package/templates/default/apps/admin/src/app/dashboard/_components/dashboard-page.tsx +74 -0
  40. package/templates/default/apps/admin/src/app/dashboard/_components/metric-cards.tsx +49 -0
  41. package/templates/default/apps/admin/src/app/dashboard/_components/overview-chart.tsx +73 -0
  42. package/templates/default/apps/admin/src/app/dashboard/_components/recent-sales.tsx +75 -0
  43. package/templates/default/apps/admin/src/app/dashboard/apps/_components/apps-page.tsx +135 -0
  44. package/templates/default/apps/admin/src/app/dashboard/apps/page.tsx +10 -0
  45. package/templates/default/apps/admin/src/app/dashboard/chats/_components/chat-list.tsx +82 -0
  46. package/templates/default/apps/admin/src/app/dashboard/chats/_components/chat-messages.tsx +194 -0
  47. package/templates/default/apps/admin/src/app/dashboard/chats/_components/chats-page.tsx +99 -0
  48. package/templates/default/apps/admin/src/app/dashboard/chats/_components/new-chat.tsx +118 -0
  49. package/templates/default/apps/admin/src/app/dashboard/chats/page.tsx +10 -0
  50. package/templates/default/apps/admin/src/app/dashboard/layout.tsx +9 -0
  51. package/templates/default/apps/admin/src/app/dashboard/not-found.tsx +14 -0
  52. package/templates/default/apps/admin/src/app/dashboard/page.tsx +10 -0
  53. package/templates/default/apps/admin/src/app/dashboard/settings/_components/content-section.tsx +20 -0
  54. package/templates/default/apps/admin/src/app/dashboard/settings/_components/sidebar-nav.tsx +66 -0
  55. package/templates/default/apps/admin/src/app/dashboard/settings/account/page.tsx +173 -0
  56. package/templates/default/apps/admin/src/app/dashboard/settings/appearance/page.tsx +156 -0
  57. package/templates/default/apps/admin/src/app/dashboard/settings/display/page.tsx +125 -0
  58. package/templates/default/apps/admin/src/app/dashboard/settings/layout.tsx +30 -0
  59. package/templates/default/apps/admin/src/app/dashboard/settings/notifications/page.tsx +196 -0
  60. package/templates/default/apps/admin/src/app/dashboard/settings/page.tsx +5 -0
  61. package/templates/default/apps/admin/src/app/dashboard/settings/profile/page.tsx +176 -0
  62. package/templates/default/apps/admin/src/app/dashboard/tasks/_components/data-table-bulk-actions.tsx +183 -0
  63. package/templates/default/apps/admin/src/app/dashboard/tasks/_components/data-table-row-actions.tsx +79 -0
  64. package/templates/default/apps/admin/src/app/dashboard/tasks/_components/tasks-columns.tsx +107 -0
  65. package/templates/default/apps/admin/src/app/dashboard/tasks/_components/tasks-dialogs.tsx +71 -0
  66. package/templates/default/apps/admin/src/app/dashboard/tasks/_components/tasks-import-dialog.tsx +106 -0
  67. package/templates/default/apps/admin/src/app/dashboard/tasks/_components/tasks-multi-delete-dialog.tsx +90 -0
  68. package/templates/default/apps/admin/src/app/dashboard/tasks/_components/tasks-mutate-drawer.tsx +207 -0
  69. package/templates/default/apps/admin/src/app/dashboard/tasks/_components/tasks-page.tsx +31 -0
  70. package/templates/default/apps/admin/src/app/dashboard/tasks/_components/tasks-primary-buttons.tsx +19 -0
  71. package/templates/default/apps/admin/src/app/dashboard/tasks/_components/tasks-provider.tsx +37 -0
  72. package/templates/default/apps/admin/src/app/dashboard/tasks/_components/tasks-table.tsx +155 -0
  73. package/templates/default/apps/admin/src/app/dashboard/tasks/page.tsx +14 -0
  74. package/templates/default/apps/admin/src/app/dashboard/users/_components/data-table-bulk-actions.tsx +136 -0
  75. package/templates/default/apps/admin/src/app/dashboard/users/_components/data-table-row-actions.tsx +62 -0
  76. package/templates/default/apps/admin/src/app/dashboard/users/_components/users-action-dialog.tsx +297 -0
  77. package/templates/default/apps/admin/src/app/dashboard/users/_components/users-columns.tsx +121 -0
  78. package/templates/default/apps/admin/src/app/dashboard/users/_components/users-delete-dialog.tsx +72 -0
  79. package/templates/default/apps/admin/src/app/dashboard/users/_components/users-dialogs.tsx +49 -0
  80. package/templates/default/apps/admin/src/app/dashboard/users/_components/users-invite-dialog.tsx +139 -0
  81. package/templates/default/apps/admin/src/app/dashboard/users/_components/users-multi-delete-dialog.tsx +89 -0
  82. package/templates/default/apps/admin/src/app/dashboard/users/_components/users-page.tsx +30 -0
  83. package/templates/default/apps/admin/src/app/dashboard/users/_components/users-primary-buttons.tsx +19 -0
  84. package/templates/default/apps/admin/src/app/dashboard/users/_components/users-provider.tsx +35 -0
  85. package/templates/default/apps/admin/src/app/dashboard/users/_components/users-table.tsx +157 -0
  86. package/templates/default/apps/admin/src/app/dashboard/users/page.tsx +10 -0
  87. package/templates/default/apps/admin/src/app/globals.css +109 -0
  88. package/templates/default/apps/admin/src/app/layout.tsx +32 -0
  89. package/templates/default/apps/admin/src/app/not-found.tsx +14 -0
  90. package/templates/default/apps/admin/src/app/page.tsx +5 -0
  91. package/templates/default/apps/admin/src/components/layout/admin-layout.tsx +16 -0
  92. package/templates/default/apps/admin/src/components/layout/app-sidebar.tsx +52 -0
  93. package/templates/default/apps/admin/src/components/layout/nav-config.ts +131 -0
  94. package/templates/default/apps/admin/src/components/providers/theme-provider.tsx +10 -0
  95. package/templates/default/apps/admin/src/components/shared/long-text.tsx +78 -0
  96. package/templates/default/apps/admin/src/components/shared/search-input.tsx +16 -0
  97. package/templates/default/apps/admin/src/components/shared/select-dropdown.tsx +64 -0
  98. package/templates/default/apps/admin/src/data/apps.tsx +116 -0
  99. package/templates/default/apps/admin/src/data/chats.ts +114 -0
  100. package/templates/default/apps/admin/src/data/tasks.ts +114 -0
  101. package/templates/default/apps/admin/src/data/users.ts +90 -0
  102. package/templates/default/apps/admin/src/hooks/use-dialog-state.ts +17 -0
  103. package/templates/default/apps/admin/src/hooks/use-table-url-state.ts +243 -0
  104. package/templates/default/apps/admin/src/lib/show-submitted-data.tsx +12 -0
  105. package/templates/default/apps/admin/src/types/table.d.ts +9 -0
  106. package/templates/default/apps/admin/tsconfig.json +32 -0
  107. package/templates/default/apps/web/next.config.ts +1 -0
  108. package/templates/default/apps/web/package.json +1 -22
  109. package/templates/default/apps/web/postcss.config.cjs +0 -1
  110. package/templates/default/apps/web/src/app/(authenticated)/dashboard/page.tsx +4 -20
  111. package/templates/default/apps/web/src/app/(authenticated)/data/_components/UserTable.tsx +4 -4
  112. package/templates/default/apps/web/src/app/(authenticated)/data/page.tsx +1 -1
  113. package/templates/default/apps/web/src/app/(authenticated)/profile/page.tsx +1 -1
  114. package/templates/default/apps/web/src/app/error.tsx +8 -70
  115. package/templates/default/apps/web/src/app/global-error.tsx +8 -70
  116. package/templates/default/apps/web/src/app/globals.css +20 -0
  117. package/templates/default/apps/web/src/app/not-found.tsx +5 -39
  118. package/templates/default/apps/web/src/app/page.tsx +27 -203
  119. package/templates/default/apps/web/src/app/signin/page.tsx +27 -191
  120. package/templates/default/apps/web/src/app/signup/page.tsx +33 -240
  121. package/templates/default/apps/web/src/components/dashboard/dashboard-stats.tsx +11 -75
  122. package/templates/default/apps/web/src/components/shared/Sidebar.tsx +3 -3
  123. package/templates/default/apps/web/src/components/shared/header.tsx +17 -112
  124. package/templates/default/apps/web/tsconfig.json +0 -6
  125. package/templates/default/biome.json +30 -2
  126. package/templates/default/components.json +2 -2
  127. package/templates/default/docker-compose.yml +1 -1
  128. package/templates/default/gitignore +4 -0
  129. package/templates/default/package.json +2 -0
  130. package/templates/default/packages/admin-ui/catalog/catalog.css +54 -0
  131. package/templates/default/packages/admin-ui/catalog/catalog.tsx +401 -0
  132. package/templates/default/packages/admin-ui/catalog/index.html +12 -0
  133. package/templates/default/packages/admin-ui/catalog/main.tsx +9 -0
  134. package/templates/default/packages/admin-ui/components.json +21 -0
  135. package/templates/default/packages/admin-ui/package.json +105 -0
  136. package/templates/default/packages/admin-ui/src/command-menu/index.tsx +174 -0
  137. package/templates/default/packages/admin-ui/src/data-table/bulk-actions.tsx +215 -0
  138. package/templates/default/packages/admin-ui/src/data-table/column-header.tsx +73 -0
  139. package/templates/default/packages/admin-ui/src/data-table/data-table.tsx +127 -0
  140. package/templates/default/packages/admin-ui/src/data-table/faceted-filter.tsx +148 -0
  141. package/templates/default/packages/admin-ui/src/data-table/index.tsx +9 -0
  142. package/templates/default/packages/admin-ui/src/data-table/pagination.tsx +101 -0
  143. package/templates/default/packages/admin-ui/src/data-table/toolbar.tsx +87 -0
  144. package/templates/default/packages/admin-ui/src/data-table/view-options.tsx +57 -0
  145. package/templates/default/packages/admin-ui/src/hooks/use-mobile.tsx +23 -0
  146. package/templates/default/packages/admin-ui/src/layout/header.tsx +55 -0
  147. package/templates/default/packages/admin-ui/src/layout/index.ts +10 -0
  148. package/templates/default/packages/admin-ui/src/layout/main.tsx +23 -0
  149. package/templates/default/packages/admin-ui/src/layout/nav-group.tsx +111 -0
  150. package/templates/default/packages/admin-ui/src/layout/nav-user.tsx +114 -0
  151. package/templates/default/packages/admin-ui/src/layout/theme-switch.tsx +40 -0
  152. package/templates/default/packages/admin-ui/src/layout/types.ts +21 -0
  153. package/templates/default/packages/admin-ui/src/lib/utils.ts +6 -0
  154. package/templates/default/packages/admin-ui/src/styles/base.css +65 -0
  155. package/templates/default/packages/admin-ui/src/styles/tokens.css +91 -0
  156. package/templates/default/packages/admin-ui/src/tanstack-table.d.ts +10 -0
  157. package/templates/default/packages/admin-ui/src/ui/alert-dialog.tsx +157 -0
  158. package/templates/default/packages/admin-ui/src/ui/alert.tsx +66 -0
  159. package/templates/default/packages/admin-ui/src/ui/avatar.tsx +53 -0
  160. package/templates/default/packages/admin-ui/src/ui/badge.tsx +46 -0
  161. package/templates/default/packages/admin-ui/src/ui/breadcrumb.tsx +108 -0
  162. package/templates/default/packages/admin-ui/src/ui/button.tsx +59 -0
  163. package/templates/default/packages/admin-ui/src/ui/calendar.tsx +69 -0
  164. package/templates/default/packages/admin-ui/src/ui/card.tsx +92 -0
  165. package/templates/default/packages/admin-ui/src/ui/chart.tsx +345 -0
  166. package/templates/default/packages/admin-ui/src/ui/checkbox.tsx +32 -0
  167. package/templates/default/packages/admin-ui/src/ui/collapsible.tsx +27 -0
  168. package/templates/default/packages/admin-ui/src/ui/command.tsx +161 -0
  169. package/templates/default/packages/admin-ui/src/ui/confirm-dialog.tsx +72 -0
  170. package/templates/default/packages/admin-ui/src/ui/date-picker.tsx +53 -0
  171. package/templates/default/packages/admin-ui/src/ui/dialog.tsx +143 -0
  172. package/templates/default/packages/admin-ui/src/ui/dropdown-menu.tsx +257 -0
  173. package/templates/default/packages/admin-ui/src/ui/form.tsx +168 -0
  174. package/templates/default/packages/admin-ui/src/ui/input-otp.tsx +84 -0
  175. package/templates/default/packages/admin-ui/src/ui/input.tsx +21 -0
  176. package/templates/default/packages/admin-ui/src/ui/label.tsx +24 -0
  177. package/templates/default/packages/admin-ui/src/ui/pagination.tsx +126 -0
  178. package/templates/default/packages/admin-ui/src/ui/password-input.tsx +46 -0
  179. package/templates/default/packages/admin-ui/src/ui/popover.tsx +48 -0
  180. package/templates/default/packages/admin-ui/src/ui/progress.tsx +31 -0
  181. package/templates/default/packages/admin-ui/src/ui/radio-group.tsx +45 -0
  182. package/templates/default/packages/admin-ui/src/ui/scroll-area.tsx +52 -0
  183. package/templates/default/packages/admin-ui/src/ui/select.tsx +185 -0
  184. package/templates/default/packages/admin-ui/src/ui/separator.tsx +28 -0
  185. package/templates/default/packages/admin-ui/src/ui/sheet.tsx +149 -0
  186. package/templates/default/packages/admin-ui/src/ui/sidebar.tsx +728 -0
  187. package/templates/default/packages/admin-ui/src/ui/skeleton.tsx +13 -0
  188. package/templates/default/packages/admin-ui/src/ui/sonner.tsx +25 -0
  189. package/templates/default/packages/admin-ui/src/ui/switch.tsx +31 -0
  190. package/templates/default/packages/admin-ui/src/ui/table.tsx +116 -0
  191. package/templates/default/packages/admin-ui/src/ui/tabs.tsx +66 -0
  192. package/templates/default/packages/admin-ui/src/ui/textarea.tsx +18 -0
  193. package/templates/default/packages/admin-ui/src/ui/toggle-group.tsx +60 -0
  194. package/templates/default/packages/admin-ui/src/ui/toggle.tsx +44 -0
  195. package/templates/default/packages/admin-ui/src/ui/tooltip.tsx +61 -0
  196. package/templates/default/packages/admin-ui/tsconfig.json +8 -0
  197. package/templates/default/packages/admin-ui/vite.config.ts +11 -0
  198. package/templates/default/packages/config/package.json +0 -2
  199. package/templates/default/packages/server-core/package.json +2 -0
  200. package/templates/default/packages/ui/components.json +21 -0
  201. package/templates/default/packages/ui/package.json +42 -5
  202. package/templates/default/packages/ui/src/accordion.tsx +1 -1
  203. package/templates/default/packages/ui/src/alert-dialog.tsx +4 -4
  204. package/templates/default/packages/ui/src/alert.tsx +1 -1
  205. package/templates/default/packages/ui/src/avatar.tsx +1 -1
  206. package/templates/default/packages/ui/src/badge.tsx +1 -1
  207. package/templates/default/packages/ui/src/breadcrumb.tsx +1 -1
  208. package/templates/default/packages/ui/src/button.tsx +1 -1
  209. package/templates/default/packages/ui/src/card.tsx +1 -1
  210. package/templates/default/packages/ui/src/checkbox.tsx +1 -1
  211. package/templates/default/packages/ui/src/dialog.tsx +3 -3
  212. package/templates/default/packages/ui/src/drawer.tsx +3 -3
  213. package/templates/default/packages/ui/src/dropdown-menu.tsx +3 -3
  214. package/templates/default/packages/ui/src/form.tsx +2 -2
  215. package/templates/default/packages/ui/src/hover-card.tsx +2 -2
  216. package/templates/default/packages/ui/src/input.tsx +1 -1
  217. package/templates/default/packages/ui/src/label.tsx +1 -1
  218. package/templates/default/packages/ui/src/pagination.tsx +2 -2
  219. package/templates/default/packages/ui/src/popover.tsx +2 -2
  220. package/templates/default/packages/ui/src/progress.tsx +1 -1
  221. package/templates/default/packages/ui/src/select.tsx +2 -2
  222. package/templates/default/packages/ui/src/separator.tsx +1 -1
  223. package/templates/default/packages/ui/src/skeleton.tsx +1 -1
  224. package/templates/default/packages/ui/src/table.tsx +1 -1
  225. package/templates/default/packages/ui/src/tabs.tsx +1 -1
  226. package/templates/default/packages/ui/src/textarea.tsx +1 -1
  227. package/templates/default/packages/ui/src/tooltip.tsx +3 -3
  228. package/templates/default/packages/ui/src/typography.tsx +1 -1
  229. package/templates/default/packages/ui/tsconfig.json +1 -6
  230. package/templates/default/pnpm-lock.yaml +1319 -936
  231. package/templates/default/postcss.config.cjs +0 -1
  232. package/templates/default/turbo.json +17 -8
  233. package/templates/default/worktree.config.json +5 -0
  234. package/templates/default/.env.ci +0 -32
  235. package/templates/default/.github/workflows/ci.yml +0 -99
  236. package/templates/default/.github/workflows/preview-db.yml +0 -134
  237. package/templates/default/.playwright-mcp/dashboard.png +0 -0
  238. package/templates/default/.playwright-mcp/web-home.png +0 -0
  239. package/templates/default/apps/web/panda.config.ts +0 -114
  240. package/templates/default/apps/web/src/components/ui/accordion.tsx +0 -64
  241. package/templates/default/apps/web/src/components/ui/alert-dialog.tsx +0 -135
  242. package/templates/default/apps/web/src/components/ui/alert.tsx +0 -60
  243. package/templates/default/apps/web/src/components/ui/aspect-ratio.tsx +0 -9
  244. package/templates/default/apps/web/src/components/ui/avatar.tsx +0 -41
  245. package/templates/default/apps/web/src/components/ui/badge.tsx +0 -39
  246. package/templates/default/apps/web/src/components/ui/breadcrumb.tsx +0 -101
  247. package/templates/default/apps/web/src/components/ui/button.tsx +0 -56
  248. package/templates/default/apps/web/src/components/ui/card.tsx +0 -75
  249. package/templates/default/apps/web/src/components/ui/checkbox.tsx +0 -29
  250. package/templates/default/apps/web/src/components/ui/data-table.tsx +0 -189
  251. package/templates/default/apps/web/src/components/ui/dialog-hook.tsx +0 -210
  252. package/templates/default/apps/web/src/components/ui/dialog.tsx +0 -129
  253. package/templates/default/apps/web/src/components/ui/drawer.tsx +0 -124
  254. package/templates/default/apps/web/src/components/ui/dropdown-menu.tsx +0 -228
  255. package/templates/default/apps/web/src/components/ui/form.tsx +0 -152
  256. package/templates/default/apps/web/src/components/ui/hover-card.tsx +0 -38
  257. package/templates/default/apps/web/src/components/ui/input.tsx +0 -21
  258. package/templates/default/apps/web/src/components/ui/label.tsx +0 -21
  259. package/templates/default/apps/web/src/components/ui/pagination.tsx +0 -105
  260. package/templates/default/apps/web/src/components/ui/popover.tsx +0 -42
  261. package/templates/default/apps/web/src/components/ui/progress.tsx +0 -28
  262. package/templates/default/apps/web/src/components/ui/select.tsx +0 -170
  263. package/templates/default/apps/web/src/components/ui/separator.tsx +0 -28
  264. package/templates/default/apps/web/src/components/ui/skeleton.tsx +0 -13
  265. package/templates/default/apps/web/src/components/ui/sonner.tsx +0 -25
  266. package/templates/default/apps/web/src/components/ui/table.tsx +0 -92
  267. package/templates/default/apps/web/src/components/ui/tabs.tsx +0 -54
  268. package/templates/default/apps/web/src/components/ui/textarea.tsx +0 -18
  269. package/templates/default/apps/web/src/components/ui/tooltip.tsx +0 -57
  270. package/templates/default/apps/web/src/components/ui/typography.tsx +0 -158
  271. package/templates/default/packages/config/panda.config.ts +0 -114
  272. package/templates/default/panda.config.ts +0 -114
@@ -0,0 +1,82 @@
1
+ "use client";
2
+
3
+ import type { ChatUser } from "@/data/chats";
4
+ import { cn } from "{{packageName}}/admin-ui/lib/utils";
5
+ import { Avatar, AvatarFallback, AvatarImage } from "{{packageName}}/admin-ui/ui/avatar";
6
+ import { ScrollArea } from "{{packageName}}/admin-ui/ui/scroll-area";
7
+ import { Separator } from "{{packageName}}/admin-ui/ui/separator";
8
+ import { Search as SearchIcon } from "lucide-react";
9
+ import { Fragment } from "react";
10
+
11
+ interface ChatListProps {
12
+ filteredChatList: ChatUser[];
13
+ selectedUser: ChatUser | null;
14
+ search: string;
15
+ onSearchChange: (search: string) => void;
16
+ onSelectUser: (user: ChatUser) => void;
17
+ }
18
+
19
+ export function ChatList({
20
+ filteredChatList,
21
+ selectedUser,
22
+ search,
23
+ onSearchChange,
24
+ onSelectUser,
25
+ }: ChatListProps) {
26
+ return (
27
+ <>
28
+ <label
29
+ className={cn(
30
+ "focus-within:ring-1 focus-within:ring-ring focus-within:outline-hidden",
31
+ "flex h-10 w-full items-center space-x-0 rounded-md border border-border ps-2"
32
+ )}
33
+ >
34
+ <SearchIcon size={15} className="me-2 stroke-slate-500" />
35
+ <span className="sr-only">Search</span>
36
+ <input
37
+ type="text"
38
+ className="w-full flex-1 bg-inherit text-sm focus-visible:outline-hidden"
39
+ placeholder="Search chat..."
40
+ value={search}
41
+ onChange={(e) => onSearchChange(e.target.value)}
42
+ />
43
+ </label>
44
+
45
+ <ScrollArea className="-mx-3 h-full overflow-scroll p-3">
46
+ {filteredChatList.map((chatUsr) => {
47
+ const { id, profile, username, messages, fullName } = chatUsr;
48
+ const lastConvo = messages[0];
49
+ const lastMsg =
50
+ lastConvo.sender === "You" ? `You: ${lastConvo.message}` : lastConvo.message;
51
+ return (
52
+ <Fragment key={id}>
53
+ <button
54
+ type="button"
55
+ className={cn(
56
+ "group hover:bg-accent hover:text-accent-foreground",
57
+ "flex w-full rounded-md px-2 py-2 text-start text-sm",
58
+ selectedUser?.id === id && "sm:bg-muted"
59
+ )}
60
+ onClick={() => onSelectUser(chatUsr)}
61
+ >
62
+ <div className="flex gap-2">
63
+ <Avatar>
64
+ <AvatarImage src={profile} alt={username} />
65
+ <AvatarFallback>{username}</AvatarFallback>
66
+ </Avatar>
67
+ <div>
68
+ <span className="col-start-2 row-span-2 font-medium">{fullName}</span>
69
+ <span className="col-start-2 row-span-2 row-start-2 line-clamp-2 text-ellipsis text-muted-foreground group-hover:text-accent-foreground/90">
70
+ {lastMsg}
71
+ </span>
72
+ </div>
73
+ </div>
74
+ </button>
75
+ <Separator className="my-1" />
76
+ </Fragment>
77
+ );
78
+ })}
79
+ </ScrollArea>
80
+ </>
81
+ );
82
+ }
@@ -0,0 +1,194 @@
1
+ "use client";
2
+
3
+ import type { ChatUser, Convo } from "@/data/chats";
4
+ import { cn } from "{{packageName}}/admin-ui/lib/utils";
5
+ import { Avatar, AvatarFallback, AvatarImage } from "{{packageName}}/admin-ui/ui/avatar";
6
+ import { Button } from "{{packageName}}/admin-ui/ui/button";
7
+ import { format } from "date-fns";
8
+ import {
9
+ ArrowLeft,
10
+ ImagePlus,
11
+ MoreVertical,
12
+ Paperclip,
13
+ Phone,
14
+ Plus,
15
+ Send,
16
+ Video,
17
+ } from "lucide-react";
18
+ import { Fragment, useState } from "react";
19
+
20
+ interface ChatMessagesProps {
21
+ selectedUser: ChatUser;
22
+ mobileSelectedUser: ChatUser | null;
23
+ onCloseMobile: () => void;
24
+ }
25
+
26
+ export function ChatMessages({
27
+ selectedUser,
28
+ mobileSelectedUser,
29
+ onCloseMobile,
30
+ }: ChatMessagesProps) {
31
+ const [messageText, setMessageText] = useState("");
32
+
33
+ const currentMessage = selectedUser?.messages.reduce((acc: Record<string, Convo[]>, obj) => {
34
+ const key = format(obj.timestamp, "d MMM, yyyy");
35
+
36
+ // Create an array for the category if it doesn't exist
37
+ if (!acc[key]) {
38
+ acc[key] = [];
39
+ }
40
+
41
+ // Push the current object to the array
42
+ acc[key].push(obj);
43
+
44
+ return acc;
45
+ }, {});
46
+
47
+ const handleSubmit = (e: React.FormEvent) => {
48
+ e.preventDefault();
49
+ // ローカルで処理(APIなし)
50
+ console.log("Send message:", messageText);
51
+ setMessageText("");
52
+ };
53
+
54
+ return (
55
+ <div
56
+ className={cn(
57
+ "absolute inset-0 start-full z-[var(--z-mobile-panel)] hidden w-full flex-1 flex-col border bg-background shadow-xs sm:static sm:z-auto sm:flex sm:rounded-md",
58
+ mobileSelectedUser && "start-0 flex"
59
+ )}
60
+ >
61
+ {/* Top Part */}
62
+ <div className="mb-1 flex flex-none justify-between bg-card p-4 shadow-lg sm:rounded-t-md">
63
+ {/* Left */}
64
+ <div className="flex gap-3">
65
+ <Button
66
+ size="icon"
67
+ variant="ghost"
68
+ className="-ms-2 h-full sm:hidden"
69
+ onClick={onCloseMobile}
70
+ >
71
+ <ArrowLeft className="rtl:rotate-180" />
72
+ </Button>
73
+ <div className="flex items-center gap-2 lg:gap-4">
74
+ <Avatar className="size-9 lg:size-11">
75
+ <AvatarImage src={selectedUser.profile} alt={selectedUser.username} />
76
+ <AvatarFallback>{selectedUser.username}</AvatarFallback>
77
+ </Avatar>
78
+ <div>
79
+ <span className="col-start-2 row-span-2 text-sm font-medium lg:text-base">
80
+ {selectedUser.fullName}
81
+ </span>
82
+ <span className="col-start-2 row-span-2 row-start-2 line-clamp-1 block max-w-32 text-xs text-nowrap text-ellipsis text-muted-foreground lg:max-w-none lg:text-sm">
83
+ {selectedUser.title}
84
+ </span>
85
+ </div>
86
+ </div>
87
+ </div>
88
+
89
+ {/* Right */}
90
+ <div className="-me-1 flex items-center gap-1 lg:gap-2">
91
+ <Button
92
+ size="icon"
93
+ variant="ghost"
94
+ className="hidden size-8 rounded-full sm:inline-flex lg:size-10"
95
+ >
96
+ <Video size={22} className="stroke-muted-foreground" />
97
+ </Button>
98
+ <Button
99
+ size="icon"
100
+ variant="ghost"
101
+ className="hidden size-8 rounded-full sm:inline-flex lg:size-10"
102
+ >
103
+ <Phone size={22} className="stroke-muted-foreground" />
104
+ </Button>
105
+ <Button
106
+ size="icon"
107
+ variant="ghost"
108
+ className="h-10 rounded-md sm:h-8 sm:w-4 lg:h-10 lg:w-6"
109
+ >
110
+ <MoreVertical className="stroke-muted-foreground sm:size-5" />
111
+ </Button>
112
+ </div>
113
+ </div>
114
+
115
+ {/* Conversation */}
116
+ <div className="flex flex-1 flex-col gap-2 rounded-md px-4 pt-0 pb-4">
117
+ <div className="flex size-full flex-1">
118
+ <div className="chat-text-container relative -me-4 flex flex-1 flex-col overflow-y-hidden">
119
+ <div className="chat-flex flex h-40 w-full grow flex-col-reverse justify-start gap-4 overflow-y-auto py-2 pe-4 pb-4">
120
+ {currentMessage &&
121
+ Object.keys(currentMessage).map((key) => (
122
+ <Fragment key={key}>
123
+ {currentMessage[key].map((msg, index) => (
124
+ <div
125
+ key={`${msg.sender}-${msg.timestamp}-${index}`}
126
+ className={cn(
127
+ "chat-box max-w-72 px-3 py-2 wrap-break-word shadow-lg",
128
+ msg.sender === "You"
129
+ ? "self-end rounded-[16px_16px_0_16px] bg-primary/90 text-primary-foreground/75"
130
+ : "self-start rounded-[16px_16px_16px_0] bg-muted"
131
+ )}
132
+ >
133
+ {msg.message}{" "}
134
+ <span
135
+ className={cn(
136
+ "mt-1 block text-xs font-light text-foreground/75 italic",
137
+ msg.sender === "You" && "text-end text-primary-foreground/85"
138
+ )}
139
+ >
140
+ {format(msg.timestamp, "h:mm a")}
141
+ </span>
142
+ </div>
143
+ ))}
144
+ <div className="text-center text-xs">{key}</div>
145
+ </Fragment>
146
+ ))}
147
+ </div>
148
+ </div>
149
+ </div>
150
+ <form onSubmit={handleSubmit} className="flex w-full flex-none gap-2">
151
+ <div className="flex flex-1 items-center gap-2 rounded-md border border-input bg-card px-2 py-1 focus-within:ring-1 focus-within:ring-ring focus-within:outline-hidden lg:gap-4">
152
+ <div className="space-x-1">
153
+ <Button size="icon" type="button" variant="ghost" className="h-8 rounded-md">
154
+ <Plus size={20} className="stroke-muted-foreground" />
155
+ </Button>
156
+ <Button
157
+ size="icon"
158
+ type="button"
159
+ variant="ghost"
160
+ className="hidden h-8 rounded-md lg:inline-flex"
161
+ >
162
+ <ImagePlus size={20} className="stroke-muted-foreground" />
163
+ </Button>
164
+ <Button
165
+ size="icon"
166
+ type="button"
167
+ variant="ghost"
168
+ className="hidden h-8 rounded-md lg:inline-flex"
169
+ >
170
+ <Paperclip size={20} className="stroke-muted-foreground" />
171
+ </Button>
172
+ </div>
173
+ <label className="flex-1">
174
+ <span className="sr-only">Chat Text Box</span>
175
+ <input
176
+ type="text"
177
+ placeholder="Type your messages..."
178
+ className="h-8 w-full bg-inherit focus-visible:outline-hidden"
179
+ value={messageText}
180
+ onChange={(e) => setMessageText(e.target.value)}
181
+ />
182
+ </label>
183
+ <Button variant="ghost" size="icon" type="submit" className="hidden sm:inline-flex">
184
+ <Send size={20} />
185
+ </Button>
186
+ </div>
187
+ <Button type="submit" className="h-full sm:hidden">
188
+ <Send size={18} /> Send
189
+ </Button>
190
+ </form>
191
+ </div>
192
+ </div>
193
+ );
194
+ }
@@ -0,0 +1,99 @@
1
+ "use client";
2
+
3
+ import type { ChatUser } from "@/data/chats";
4
+ import { conversations } from "@/data/chats";
5
+ import { Header } from "{{packageName}}/admin-ui/layout";
6
+ import { Main } from "{{packageName}}/admin-ui/layout";
7
+ import { Button } from "{{packageName}}/admin-ui/ui/button";
8
+ import { Edit, MessagesSquare } from "lucide-react";
9
+ import { useState } from "react";
10
+ import { ChatList } from "./chat-list";
11
+ import { ChatMessages } from "./chat-messages";
12
+ import { NewChat } from "./new-chat";
13
+
14
+ export function ChatsPage() {
15
+ const [search, setSearch] = useState("");
16
+ const [selectedUser, setSelectedUser] = useState<ChatUser | null>(null);
17
+ const [mobileSelectedUser, setMobileSelectedUser] = useState<ChatUser | null>(null);
18
+ const [createConversationDialogOpened, setCreateConversationDialog] = useState(false);
19
+
20
+ // Filtered data based on the search query
21
+ const filteredChatList = conversations.filter(({ fullName }) =>
22
+ fullName.toLowerCase().includes(search.trim().toLowerCase())
23
+ );
24
+
25
+ const users = conversations.map(({ messages, ...user }) => user);
26
+
27
+ return (
28
+ <>
29
+ <Header>
30
+ <div className="flex items-center gap-2">
31
+ <h1 className="text-2xl font-bold">Chats</h1>
32
+ <MessagesSquare size={20} />
33
+ </div>
34
+ </Header>
35
+
36
+ <Main fixed>
37
+ <section className="flex h-full gap-6">
38
+ {/* Left Side */}
39
+ <div className="flex w-full flex-col gap-2 sm:w-56 lg:w-72 2xl:w-80">
40
+ <div className="sticky top-0 z-[var(--z-sticky)] -mx-4 bg-background px-4 pb-3 shadow-md sm:static sm:z-auto sm:mx-0 sm:p-0 sm:shadow-none">
41
+ <div className="flex items-center justify-between py-2">
42
+ <div className="flex gap-2">
43
+ <h2 className="text-xl font-bold">Inbox</h2>
44
+ </div>
45
+
46
+ <Button
47
+ size="icon"
48
+ variant="ghost"
49
+ onClick={() => setCreateConversationDialog(true)}
50
+ className="rounded-lg"
51
+ >
52
+ <Edit size={24} className="stroke-muted-foreground" />
53
+ </Button>
54
+ </div>
55
+ </div>
56
+
57
+ <ChatList
58
+ filteredChatList={filteredChatList}
59
+ selectedUser={selectedUser}
60
+ search={search}
61
+ onSearchChange={setSearch}
62
+ onSelectUser={(user) => {
63
+ setSelectedUser(user);
64
+ setMobileSelectedUser(user);
65
+ }}
66
+ />
67
+ </div>
68
+
69
+ {/* Right Side */}
70
+ {selectedUser ? (
71
+ <ChatMessages
72
+ selectedUser={selectedUser}
73
+ mobileSelectedUser={mobileSelectedUser}
74
+ onCloseMobile={() => setMobileSelectedUser(null)}
75
+ />
76
+ ) : (
77
+ <div className="absolute inset-0 start-full z-[var(--z-mobile-panel)] hidden w-full flex-1 flex-col justify-center rounded-md border bg-card shadow-xs sm:static sm:z-auto sm:flex">
78
+ <div className="flex flex-col items-center space-y-6">
79
+ <div className="flex size-16 items-center justify-center rounded-full border-2 border-border">
80
+ <MessagesSquare className="size-8" />
81
+ </div>
82
+ <div className="space-y-2 text-center">
83
+ <h2 className="text-xl font-semibold">Your messages</h2>
84
+ <p className="text-sm text-muted-foreground">Send a message to start a chat.</p>
85
+ </div>
86
+ <Button onClick={() => setCreateConversationDialog(true)}>Send message</Button>
87
+ </div>
88
+ </div>
89
+ )}
90
+ </section>
91
+ <NewChat
92
+ users={users}
93
+ onOpenChange={setCreateConversationDialog}
94
+ open={createConversationDialogOpened}
95
+ />
96
+ </Main>
97
+ </>
98
+ );
99
+ }
@@ -0,0 +1,118 @@
1
+ "use client";
2
+
3
+ import type { ChatUser } from "@/data/chats";
4
+ import { Badge } from "{{packageName}}/admin-ui/ui/badge";
5
+ import { Button } from "{{packageName}}/admin-ui/ui/button";
6
+ import {
7
+ Command,
8
+ CommandEmpty,
9
+ CommandGroup,
10
+ CommandInput,
11
+ CommandItem,
12
+ CommandList,
13
+ } from "{{packageName}}/admin-ui/ui/command";
14
+ import { Dialog, DialogContent, DialogHeader, DialogTitle } from "{{packageName}}/admin-ui/ui/dialog";
15
+ import { Check, X } from "lucide-react";
16
+ import { useState } from "react";
17
+
18
+ type User = Omit<ChatUser, "messages">;
19
+
20
+ interface NewChatProps {
21
+ users: User[];
22
+ open: boolean;
23
+ onOpenChange: (open: boolean) => void;
24
+ }
25
+
26
+ export function NewChat({ users, onOpenChange, open }: NewChatProps) {
27
+ const [selectedUsers, setSelectedUsers] = useState<User[]>([]);
28
+
29
+ const handleSelectUser = (user: User) => {
30
+ if (!selectedUsers.find((u) => u.id === user.id)) {
31
+ setSelectedUsers([...selectedUsers, user]);
32
+ } else {
33
+ handleRemoveUser(user.id);
34
+ }
35
+ };
36
+
37
+ const handleRemoveUser = (userId: string) => {
38
+ setSelectedUsers(selectedUsers.filter((user) => user.id !== userId));
39
+ };
40
+
41
+ const handleOpenChange = (newOpen: boolean) => {
42
+ onOpenChange(newOpen);
43
+ // Reset selected users when dialog closes
44
+ if (!newOpen) {
45
+ setSelectedUsers([]);
46
+ }
47
+ };
48
+
49
+ const handleStartChat = () => {
50
+ // ローカルで処理(APIなし)
51
+ console.log("Start chat with:", selectedUsers);
52
+ handleOpenChange(false);
53
+ };
54
+
55
+ return (
56
+ <Dialog open={open} onOpenChange={handleOpenChange}>
57
+ <DialogContent className="sm:max-w-[600px]">
58
+ <DialogHeader>
59
+ <DialogTitle>New message</DialogTitle>
60
+ </DialogHeader>
61
+ <div className="flex flex-col gap-4">
62
+ <div className="flex flex-wrap items-baseline-last gap-2">
63
+ <span className="min-h-6 text-sm text-muted-foreground">To:</span>
64
+ {selectedUsers.map((user) => (
65
+ <Badge key={user.id} variant="default">
66
+ {user.fullName}
67
+ <button
68
+ className="ms-1 rounded-full ring-offset-background outline-hidden focus:ring-2 focus:ring-ring focus:ring-offset-2"
69
+ type="button"
70
+ onKeyDown={(e) => {
71
+ if (e.key === "Enter") {
72
+ handleRemoveUser(user.id);
73
+ }
74
+ }}
75
+ onClick={() => handleRemoveUser(user.id)}
76
+ >
77
+ <X className="h-3 w-3 text-muted-foreground hover:text-foreground" />
78
+ </button>
79
+ </Badge>
80
+ ))}
81
+ </div>
82
+ <Command className="rounded-lg border">
83
+ <CommandInput placeholder="Search people..." className="text-foreground" />
84
+ <CommandList>
85
+ <CommandEmpty>No people found.</CommandEmpty>
86
+ <CommandGroup>
87
+ {users.map((user) => (
88
+ <CommandItem
89
+ key={user.id}
90
+ onSelect={() => handleSelectUser(user)}
91
+ className="flex items-center justify-between gap-2 hover:bg-accent hover:text-accent-foreground"
92
+ >
93
+ <div className="flex items-center gap-2">
94
+ <img
95
+ src={user.profile || "/placeholder.svg"}
96
+ alt={user.fullName}
97
+ className="h-8 w-8 rounded-full"
98
+ />
99
+ <div className="flex flex-col">
100
+ <span className="text-sm font-medium">{user.fullName}</span>
101
+ <span className="text-xs text-accent-foreground/70">{user.username}</span>
102
+ </div>
103
+ </div>
104
+
105
+ {selectedUsers.find((u) => u.id === user.id) && <Check className="h-4 w-4" />}
106
+ </CommandItem>
107
+ ))}
108
+ </CommandGroup>
109
+ </CommandList>
110
+ </Command>
111
+ <Button variant="default" onClick={handleStartChat} disabled={selectedUsers.length === 0}>
112
+ Chat
113
+ </Button>
114
+ </div>
115
+ </DialogContent>
116
+ </Dialog>
117
+ );
118
+ }
@@ -0,0 +1,10 @@
1
+ import type { Metadata } from "next";
2
+ import { ChatsPage } from "./_components/chats-page";
3
+
4
+ export const metadata: Metadata = {
5
+ title: "Chats",
6
+ };
7
+
8
+ export default function Page() {
9
+ return <ChatsPage />;
10
+ }
@@ -0,0 +1,9 @@
1
+ import { AdminLayout } from "@/components/layout/admin-layout";
2
+
3
+ export default function DashboardLayout({
4
+ children,
5
+ }: {
6
+ children: React.ReactNode;
7
+ }) {
8
+ return <AdminLayout>{children}</AdminLayout>;
9
+ }
@@ -0,0 +1,14 @@
1
+ import { Button } from "{{packageName}}/admin-ui/ui/button";
2
+ import Link from "next/link";
3
+
4
+ export default function DashboardNotFound() {
5
+ return (
6
+ <div className="flex h-full flex-1 flex-col items-center justify-center gap-4">
7
+ <h1 className="text-4xl font-bold">404</h1>
8
+ <p className="text-muted-foreground">ページが見つかりません</p>
9
+ <Button asChild>
10
+ <Link href="/dashboard">ダッシュボードに戻る</Link>
11
+ </Button>
12
+ </div>
13
+ );
14
+ }
@@ -0,0 +1,10 @@
1
+ import type { Metadata } from "next";
2
+ import { DashboardPage } from "./_components/dashboard-page";
3
+
4
+ export const metadata: Metadata = {
5
+ title: "Dashboard",
6
+ };
7
+
8
+ export default function Page() {
9
+ return <DashboardPage />;
10
+ }
@@ -0,0 +1,20 @@
1
+ import { Separator } from "{{packageName}}/admin-ui/ui/separator";
2
+
3
+ type ContentSectionProps = {
4
+ title: string;
5
+ desc: string;
6
+ children: React.ReactNode;
7
+ };
8
+
9
+ export function ContentSection({ title, desc, children }: ContentSectionProps) {
10
+ return (
11
+ <div className="space-y-6">
12
+ <div>
13
+ <h3 className="text-lg font-medium">{title}</h3>
14
+ <p className="text-sm text-muted-foreground">{desc}</p>
15
+ </div>
16
+ <Separator />
17
+ <div className="max-w-2xl">{children}</div>
18
+ </div>
19
+ );
20
+ }
@@ -0,0 +1,66 @@
1
+ "use client";
2
+
3
+ import { cn } from "{{packageName}}/admin-ui/lib/utils";
4
+ import { buttonVariants } from "{{packageName}}/admin-ui/ui/button";
5
+ import { ScrollArea } from "{{packageName}}/admin-ui/ui/scroll-area";
6
+ import { Bell, Monitor, Palette, UserCog, Wrench } from "lucide-react";
7
+ import Link from "next/link";
8
+ import { usePathname } from "next/navigation";
9
+
10
+ const sidebarNavItems = [
11
+ {
12
+ title: "Profile",
13
+ href: "/dashboard/settings/profile",
14
+ icon: UserCog,
15
+ },
16
+ {
17
+ title: "Account",
18
+ href: "/dashboard/settings/account",
19
+ icon: Wrench,
20
+ },
21
+ {
22
+ title: "Appearance",
23
+ href: "/dashboard/settings/appearance",
24
+ icon: Palette,
25
+ },
26
+ {
27
+ title: "Notifications",
28
+ href: "/dashboard/settings/notifications",
29
+ icon: Bell,
30
+ },
31
+ {
32
+ title: "Display",
33
+ href: "/dashboard/settings/display",
34
+ icon: Monitor,
35
+ },
36
+ ];
37
+
38
+ export function SidebarNav() {
39
+ const pathname = usePathname();
40
+
41
+ return (
42
+ <ScrollArea className="h-full w-full">
43
+ <nav className="flex flex-col space-y-1">
44
+ {sidebarNavItems.map((item) => {
45
+ const Icon = item.icon;
46
+ return (
47
+ <Link
48
+ key={item.href}
49
+ href={item.href}
50
+ className={cn(
51
+ buttonVariants({ variant: "ghost" }),
52
+ pathname === item.href
53
+ ? "bg-muted hover:bg-muted"
54
+ : "hover:bg-transparent hover:underline",
55
+ "justify-start"
56
+ )}
57
+ >
58
+ <Icon className="mr-2 size-4" />
59
+ {item.title}
60
+ </Link>
61
+ );
62
+ })}
63
+ </nav>
64
+ </ScrollArea>
65
+ );
66
+ }