create-nextdevtpl 2.6.0

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 (425) hide show
  1. package/README.md +29 -0
  2. package/catalog.json +385 -0
  3. package/dist/catalog.d.ts +3 -0
  4. package/dist/catalog.js +112 -0
  5. package/dist/catalog.js.map +1 -0
  6. package/dist/cli.d.ts +3 -0
  7. package/dist/cli.js +115 -0
  8. package/dist/cli.js.map +1 -0
  9. package/dist/filesystem.d.ts +10 -0
  10. package/dist/filesystem.js +86 -0
  11. package/dist/filesystem.js.map +1 -0
  12. package/dist/generate.d.ts +5 -0
  13. package/dist/generate.js +758 -0
  14. package/dist/generate.js.map +1 -0
  15. package/dist/index.d.ts +4 -0
  16. package/dist/index.js +4 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/selection.d.ts +2 -0
  19. package/dist/selection.js +111 -0
  20. package/dist/selection.js.map +1 -0
  21. package/dist/service-templates.d.ts +4 -0
  22. package/dist/service-templates.js +180 -0
  23. package/dist/service-templates.js.map +1 -0
  24. package/dist/types.d.ts +65 -0
  25. package/dist/types.js +2 -0
  26. package/dist/types.js.map +1 -0
  27. package/package.json +54 -0
  28. package/template/.dockerignore +14 -0
  29. package/template/.env.example +282 -0
  30. package/template/.gitattributes +17 -0
  31. package/template/.node-version +1 -0
  32. package/template/Dockerfile +45 -0
  33. package/template/LICENSE +21 -0
  34. package/template/README.md +267 -0
  35. package/template/biome.json +68 -0
  36. package/template/cloudflare/templates/logger.ts +162 -0
  37. package/template/cloudflare/templates/mail/server.ts +6 -0
  38. package/template/cloudflare/templates/mail/templates.ts +87 -0
  39. package/template/cloudflare/templates/mail/utils.ts +69 -0
  40. package/template/cloudflare/worker.mjs +25 -0
  41. package/template/components.json +22 -0
  42. package/template/compose.yaml +74 -0
  43. package/template/deploy/check-health.mjs +25 -0
  44. package/template/deploy/docker/README.md +14 -0
  45. package/template/deploy/server/README.md +24 -0
  46. package/template/deploy/server/build.sh +24 -0
  47. package/template/deploy/server/nextdevtpl.service +19 -0
  48. package/template/deploy/server/start.sh +19 -0
  49. package/template/deploy/vercel/README.md +16 -0
  50. package/template/deploy/verify-production-health.mjs +86 -0
  51. package/template/drizzle.config.ts +47 -0
  52. package/template/gitignore +62 -0
  53. package/template/manifest.json +35 -0
  54. package/template/messages/en.json +766 -0
  55. package/template/messages/zh.json +762 -0
  56. package/template/next.config.mjs +28 -0
  57. package/template/open-next.config.ts +3 -0
  58. package/template/package.json +125 -0
  59. package/template/pnpm-lock.yaml +15901 -0
  60. package/template/postcss.config.mjs +7 -0
  61. package/template/public/file.svg +1 -0
  62. package/template/public/globe.svg +1 -0
  63. package/template/public/logo.svg +6 -0
  64. package/template/public/next.svg +1 -0
  65. package/template/public/vercel.svg +1 -0
  66. package/template/public/window.svg +1 -0
  67. package/template/sentry.client.config.ts +10 -0
  68. package/template/sentry.edge.config.ts +10 -0
  69. package/template/sentry.server.config.ts +10 -0
  70. package/template/source.config.ts +59 -0
  71. package/template/src/adapters/ai/anthropic.ts +113 -0
  72. package/template/src/adapters/ai/index.ts +6 -0
  73. package/template/src/adapters/ai/openai-compatible.ts +88 -0
  74. package/template/src/adapters/ai/workers-ai.ts +63 -0
  75. package/template/src/adapters/index.ts +10 -0
  76. package/template/src/adapters/jobs/cloudflare-workflows.ts +46 -0
  77. package/template/src/adapters/jobs/index.ts +5 -0
  78. package/template/src/adapters/jobs/inngest/adapter.ts +29 -0
  79. package/template/src/adapters/jobs/inngest/client.ts +8 -0
  80. package/template/src/adapters/jobs/inngest/functions.ts +17 -0
  81. package/template/src/adapters/jobs/inngest/handler.ts +6 -0
  82. package/template/src/adapters/jobs/inngest/index.ts +4 -0
  83. package/template/src/adapters/mail/cloudflare-email.ts +50 -0
  84. package/template/src/adapters/mail/disabled.ts +17 -0
  85. package/template/src/adapters/mail/index.ts +7 -0
  86. package/template/src/adapters/mail/resend.ts +76 -0
  87. package/template/src/adapters/mail/smtp.ts +82 -0
  88. package/template/src/adapters/payment/creem.ts +269 -0
  89. package/template/src/adapters/payment/index.ts +2 -0
  90. package/template/src/adapters/payment/stripe.ts +302 -0
  91. package/template/src/adapters/rate-limit/cloudflare.ts +42 -0
  92. package/template/src/adapters/rate-limit/index.ts +6 -0
  93. package/template/src/adapters/rate-limit/noop.ts +27 -0
  94. package/template/src/adapters/rate-limit/upstash.ts +113 -0
  95. package/template/src/adapters/registry.ts +228 -0
  96. package/template/src/adapters/storage/index.ts +3 -0
  97. package/template/src/adapters/storage/r2-binding.ts +105 -0
  98. package/template/src/adapters/storage/s3-compatible.ts +149 -0
  99. package/template/src/app/[locale]/(admin)/admin/layout.tsx +36 -0
  100. package/template/src/app/[locale]/(admin)/admin/page.tsx +345 -0
  101. package/template/src/app/[locale]/(admin)/admin/tickets/[id]/page.tsx +261 -0
  102. package/template/src/app/[locale]/(admin)/admin/tickets/page.tsx +236 -0
  103. package/template/src/app/[locale]/(admin)/admin/users/page.tsx +557 -0
  104. package/template/src/app/[locale]/(auth)/forgot-password/page.tsx +9 -0
  105. package/template/src/app/[locale]/(auth)/layout.tsx +28 -0
  106. package/template/src/app/[locale]/(auth)/sign-in/page.tsx +9 -0
  107. package/template/src/app/[locale]/(auth)/sign-up/page.tsx +9 -0
  108. package/template/src/app/[locale]/(dashboard)/dashboard/credits/buy/buy-credits-view.tsx +170 -0
  109. package/template/src/app/[locale]/(dashboard)/dashboard/credits/buy/page.tsx +12 -0
  110. package/template/src/app/[locale]/(dashboard)/dashboard/credits/page.tsx +5 -0
  111. package/template/src/app/[locale]/(dashboard)/dashboard/loading.tsx +41 -0
  112. package/template/src/app/[locale]/(dashboard)/dashboard/page.tsx +299 -0
  113. package/template/src/app/[locale]/(dashboard)/dashboard/settings/page.tsx +38 -0
  114. package/template/src/app/[locale]/(dashboard)/dashboard/support/[id]/page.tsx +220 -0
  115. package/template/src/app/[locale]/(dashboard)/dashboard/support/new/page.tsx +183 -0
  116. package/template/src/app/[locale]/(dashboard)/dashboard/support/page.tsx +145 -0
  117. package/template/src/app/[locale]/(dashboard)/layout.tsx +35 -0
  118. package/template/src/app/[locale]/(marketing)/blog/[slug]/page.tsx +178 -0
  119. package/template/src/app/[locale]/(marketing)/blog/blog-post-card.tsx +95 -0
  120. package/template/src/app/[locale]/(marketing)/blog/page.tsx +127 -0
  121. package/template/src/app/[locale]/(marketing)/demo/plan-badges/page.tsx +36 -0
  122. package/template/src/app/[locale]/(marketing)/layout.tsx +15 -0
  123. package/template/src/app/[locale]/(marketing)/legal/[slug]/page.tsx +130 -0
  124. package/template/src/app/[locale]/(marketing)/page.tsx +94 -0
  125. package/template/src/app/[locale]/(marketing)/pseo/[slug]/page.tsx +68 -0
  126. package/template/src/app/[locale]/(marketing)/pseo/page.tsx +137 -0
  127. package/template/src/app/[locale]/docs/[[...slug]]/page.tsx +63 -0
  128. package/template/src/app/[locale]/docs/layout.tsx +39 -0
  129. package/template/src/app/[locale]/layout.tsx +80 -0
  130. package/template/src/app/[locale]/opengraph-image.tsx +20 -0
  131. package/template/src/app/[locale]/twitter-image.tsx +20 -0
  132. package/template/src/app/api/auth/[...all]/route.ts +20 -0
  133. package/template/src/app/api/health/route.ts +45 -0
  134. package/template/src/app/api/inngest/route.ts +12 -0
  135. package/template/src/app/api/jobs/credits/expire/route.ts +102 -0
  136. package/template/src/app/api/search/route.ts +17 -0
  137. package/template/src/app/api/upload/presigned/route.ts +98 -0
  138. package/template/src/app/api/webhooks/payment/route.ts +363 -0
  139. package/template/src/app/globals.css +406 -0
  140. package/template/src/app/icon.svg +6 -0
  141. package/template/src/app/image-proxy/[...path]/route.ts +137 -0
  142. package/template/src/app/layout.tsx +73 -0
  143. package/template/src/app/robots.ts +29 -0
  144. package/template/src/app/sitemap.ts +68 -0
  145. package/template/src/components/motion/reveal.tsx +49 -0
  146. package/template/src/components/seo/json-ld.tsx +87 -0
  147. package/template/src/components/seo/og-image-template.tsx +118 -0
  148. package/template/src/components/ui/accordion.tsx +66 -0
  149. package/template/src/components/ui/alert-dialog.tsx +195 -0
  150. package/template/src/components/ui/avatar.tsx +53 -0
  151. package/template/src/components/ui/badge.tsx +46 -0
  152. package/template/src/components/ui/button.tsx +65 -0
  153. package/template/src/components/ui/card.tsx +92 -0
  154. package/template/src/components/ui/checkbox.tsx +32 -0
  155. package/template/src/components/ui/collapsible.tsx +33 -0
  156. package/template/src/components/ui/dialog.tsx +124 -0
  157. package/template/src/components/ui/dropdown-menu.tsx +257 -0
  158. package/template/src/components/ui/form.tsx +233 -0
  159. package/template/src/components/ui/index.ts +2 -0
  160. package/template/src/components/ui/input.tsx +21 -0
  161. package/template/src/components/ui/label.tsx +24 -0
  162. package/template/src/components/ui/navigation-menu.tsx +167 -0
  163. package/template/src/components/ui/pagination.tsx +125 -0
  164. package/template/src/components/ui/popover.tsx +48 -0
  165. package/template/src/components/ui/progress.tsx +27 -0
  166. package/template/src/components/ui/radio-group.tsx +45 -0
  167. package/template/src/components/ui/select.tsx +190 -0
  168. package/template/src/components/ui/separator.tsx +28 -0
  169. package/template/src/components/ui/sheet.tsx +140 -0
  170. package/template/src/components/ui/switch.tsx +31 -0
  171. package/template/src/components/ui/tabs.tsx +75 -0
  172. package/template/src/components/ui/textarea.tsx +18 -0
  173. package/template/src/config/index.ts +31 -0
  174. package/template/src/config/nav.ts +238 -0
  175. package/template/src/config/payment.ts +282 -0
  176. package/template/src/config/site.ts +53 -0
  177. package/template/src/config/subscription-plan.ts +164 -0
  178. package/template/src/content/blog/en/hello-world.mdx +48 -0
  179. package/template/src/content/blog/zh/hello-world.mdx +48 -0
  180. package/template/src/content/blog/zh/nextdevtpl-linuxdo-intro.mdx +98 -0
  181. package/template/src/content/docs/en/admin-and-support.mdx +65 -0
  182. package/template/src/content/docs/en/authentication.mdx +71 -0
  183. package/template/src/content/docs/en/background-jobs.mdx +53 -0
  184. package/template/src/content/docs/en/configuration.mdx +71 -0
  185. package/template/src/content/docs/en/credits.mdx +69 -0
  186. package/template/src/content/docs/en/customization.mdx +95 -0
  187. package/template/src/content/docs/en/database.mdx +66 -0
  188. package/template/src/content/docs/en/deployment.mdx +62 -0
  189. package/template/src/content/docs/en/email.mdx +48 -0
  190. package/template/src/content/docs/en/index.mdx +48 -0
  191. package/template/src/content/docs/en/internationalization.mdx +78 -0
  192. package/template/src/content/docs/en/meta.json +22 -0
  193. package/template/src/content/docs/en/observability.mdx +69 -0
  194. package/template/src/content/docs/en/payments.mdx +64 -0
  195. package/template/src/content/docs/en/project-structure.mdx +82 -0
  196. package/template/src/content/docs/en/quick-start.mdx +113 -0
  197. package/template/src/content/docs/en/storage.mdx +51 -0
  198. package/template/src/content/docs/zh/admin-and-support.mdx +65 -0
  199. package/template/src/content/docs/zh/authentication.mdx +71 -0
  200. package/template/src/content/docs/zh/background-jobs.mdx +53 -0
  201. package/template/src/content/docs/zh/configuration.mdx +70 -0
  202. package/template/src/content/docs/zh/credits.mdx +69 -0
  203. package/template/src/content/docs/zh/customization.mdx +95 -0
  204. package/template/src/content/docs/zh/database.mdx +66 -0
  205. package/template/src/content/docs/zh/deployment.mdx +61 -0
  206. package/template/src/content/docs/zh/email.mdx +47 -0
  207. package/template/src/content/docs/zh/index.mdx +48 -0
  208. package/template/src/content/docs/zh/internationalization.mdx +78 -0
  209. package/template/src/content/docs/zh/meta.json +22 -0
  210. package/template/src/content/docs/zh/observability.mdx +68 -0
  211. package/template/src/content/docs/zh/payments.mdx +64 -0
  212. package/template/src/content/docs/zh/project-structure.mdx +82 -0
  213. package/template/src/content/docs/zh/quick-start.mdx +110 -0
  214. package/template/src/content/docs/zh/storage.mdx +51 -0
  215. package/template/src/content/legal/en/cookie-policy.mdx +78 -0
  216. package/template/src/content/legal/en/privacy.mdx +131 -0
  217. package/template/src/content/legal/en/terms.mdx +93 -0
  218. package/template/src/content/legal/zh/cookie-policy.mdx +78 -0
  219. package/template/src/content/legal/zh/privacy.mdx +131 -0
  220. package/template/src/content/legal/zh/terms.mdx +92 -0
  221. package/template/src/core/modules/contract.ts +225 -0
  222. package/template/src/core/modules/index.ts +1 -0
  223. package/template/src/core/services/ai.ts +31 -0
  224. package/template/src/core/services/common.ts +112 -0
  225. package/template/src/core/services/index.ts +8 -0
  226. package/template/src/core/services/jobs.ts +24 -0
  227. package/template/src/core/services/mail.ts +35 -0
  228. package/template/src/core/services/manifest.ts +51 -0
  229. package/template/src/core/services/payment.ts +92 -0
  230. package/template/src/core/services/rate-limit.ts +47 -0
  231. package/template/src/core/services/storage.ts +42 -0
  232. package/template/src/db/index.ts +71 -0
  233. package/template/src/db/schema/auth.ts +67 -0
  234. package/template/src/db/schema/credits.ts +94 -0
  235. package/template/src/db/schema/index.ts +5 -0
  236. package/template/src/db/schema/mail.ts +14 -0
  237. package/template/src/db/schema/subscription.ts +21 -0
  238. package/template/src/db/schema/support.ts +56 -0
  239. package/template/src/db/schema.ts +2 -0
  240. package/template/src/features/admin/components/admin-sidebar.tsx +213 -0
  241. package/template/src/features/admin/components/index.ts +2 -0
  242. package/template/src/features/admin/index.ts +3 -0
  243. package/template/src/features/admin/manifest.ts +41 -0
  244. package/template/src/features/analytics/components/analytics.tsx +62 -0
  245. package/template/src/features/analytics/components/index.ts +2 -0
  246. package/template/src/features/analytics/index.ts +3 -0
  247. package/template/src/features/analytics/manifest.ts +13 -0
  248. package/template/src/features/auth/components/auth-brand-panel.tsx +82 -0
  249. package/template/src/features/auth/components/auth-error-alert.tsx +28 -0
  250. package/template/src/features/auth/components/auth-footer.tsx +52 -0
  251. package/template/src/features/auth/components/auth-logo.tsx +30 -0
  252. package/template/src/features/auth/components/forgot-password-form.tsx +146 -0
  253. package/template/src/features/auth/components/index.ts +9 -0
  254. package/template/src/features/auth/components/sign-in-form.tsx +241 -0
  255. package/template/src/features/auth/components/sign-up-form.tsx +307 -0
  256. package/template/src/features/auth/index.ts +3 -0
  257. package/template/src/features/auth/manifest.ts +30 -0
  258. package/template/src/features/blog/components/blog-post-item.tsx +72 -0
  259. package/template/src/features/blog/components/index.ts +1 -0
  260. package/template/src/features/blog/data/mock-posts.ts +62 -0
  261. package/template/src/features/blog/index.ts +3 -0
  262. package/template/src/features/blog/manifest.ts +22 -0
  263. package/template/src/features/credits/account.ts +76 -0
  264. package/template/src/features/credits/actions.ts +427 -0
  265. package/template/src/features/credits/components/credit-balance-badge.tsx +54 -0
  266. package/template/src/features/credits/components/credit-usage-section.tsx +160 -0
  267. package/template/src/features/credits/components/index.ts +7 -0
  268. package/template/src/features/credits/components/transaction-history.tsx +346 -0
  269. package/template/src/features/credits/config.ts +70 -0
  270. package/template/src/features/credits/consume.ts +140 -0
  271. package/template/src/features/credits/core.ts +23 -0
  272. package/template/src/features/credits/errors.ts +26 -0
  273. package/template/src/features/credits/expire.ts +112 -0
  274. package/template/src/features/credits/grant.ts +168 -0
  275. package/template/src/features/credits/index.ts +6 -0
  276. package/template/src/features/credits/manifest.ts +36 -0
  277. package/template/src/features/credits/query.ts +65 -0
  278. package/template/src/features/credits/types.ts +48 -0
  279. package/template/src/features/dashboard/components/index.ts +3 -0
  280. package/template/src/features/dashboard/components/main-wrapper.tsx +100 -0
  281. package/template/src/features/dashboard/components/sidebar.tsx +341 -0
  282. package/template/src/features/dashboard/context/index.ts +2 -0
  283. package/template/src/features/dashboard/context/sidebar-context.tsx +72 -0
  284. package/template/src/features/dashboard/index.ts +4 -0
  285. package/template/src/features/dashboard/manifest.ts +28 -0
  286. package/template/src/features/mail/actions.ts +175 -0
  287. package/template/src/features/mail/index.ts +5 -0
  288. package/template/src/features/mail/manifest.ts +13 -0
  289. package/template/src/features/mail/server.ts +8 -0
  290. package/template/src/features/mail/templates/index.ts +11 -0
  291. package/template/src/features/mail/templates/primary-action-email.tsx +224 -0
  292. package/template/src/features/mail/templates/welcome-email.tsx +121 -0
  293. package/template/src/features/mail/utils.ts +190 -0
  294. package/template/src/features/marketing/components/animated-price.tsx +51 -0
  295. package/template/src/features/marketing/components/cookie-consent.tsx +255 -0
  296. package/template/src/features/marketing/components/cta-section.tsx +50 -0
  297. package/template/src/features/marketing/components/faq-section.tsx +59 -0
  298. package/template/src/features/marketing/components/feature-grid.tsx +80 -0
  299. package/template/src/features/marketing/components/footer.tsx +128 -0
  300. package/template/src/features/marketing/components/header.tsx +255 -0
  301. package/template/src/features/marketing/components/hero-section.tsx +323 -0
  302. package/template/src/features/marketing/components/how-it-works.tsx +115 -0
  303. package/template/src/features/marketing/components/index.ts +14 -0
  304. package/template/src/features/marketing/components/nav-menu.tsx +236 -0
  305. package/template/src/features/marketing/components/pricing-section.tsx +446 -0
  306. package/template/src/features/marketing/components/testimonials.tsx +59 -0
  307. package/template/src/features/marketing/components/use-cases-section.tsx +78 -0
  308. package/template/src/features/marketing/index.ts +3 -0
  309. package/template/src/features/marketing/manifest.ts +35 -0
  310. package/template/src/features/payment/actions.ts +182 -0
  311. package/template/src/features/payment/index.ts +4 -0
  312. package/template/src/features/payment/manifest.ts +18 -0
  313. package/template/src/features/payment/server.ts +7 -0
  314. package/template/src/features/payment/types.ts +202 -0
  315. package/template/src/features/pseo/components/index.ts +6 -0
  316. package/template/src/features/pseo/components/pseo-cta.tsx +51 -0
  317. package/template/src/features/pseo/components/pseo-faq.tsx +40 -0
  318. package/template/src/features/pseo/components/pseo-feature-grid.tsx +74 -0
  319. package/template/src/features/pseo/components/pseo-hero.tsx +106 -0
  320. package/template/src/features/pseo/components/pseo-related.tsx +62 -0
  321. package/template/src/features/pseo/components/pseo-use-cases.tsx +50 -0
  322. package/template/src/features/pseo/data/pseo-pages.json +534 -0
  323. package/template/src/features/pseo/index.ts +4 -0
  324. package/template/src/features/pseo/lib/pseo-data.ts +140 -0
  325. package/template/src/features/pseo/manifest.ts +22 -0
  326. package/template/src/features/settings/actions/delete-account.ts +50 -0
  327. package/template/src/features/settings/actions/index.ts +3 -0
  328. package/template/src/features/settings/actions/update-profile.ts +49 -0
  329. package/template/src/features/settings/components/billing-section.tsx +283 -0
  330. package/template/src/features/settings/components/index.ts +3 -0
  331. package/template/src/features/settings/components/profile-form.tsx +154 -0
  332. package/template/src/features/settings/components/security-section.tsx +158 -0
  333. package/template/src/features/settings/components/settings-profile-view.tsx +506 -0
  334. package/template/src/features/settings/index.ts +5 -0
  335. package/template/src/features/settings/manifest.ts +28 -0
  336. package/template/src/features/settings/schemas/index.ts +2 -0
  337. package/template/src/features/settings/schemas/update-profile.ts +21 -0
  338. package/template/src/features/shared/components/cookie-settings-dialog.tsx +282 -0
  339. package/template/src/features/shared/components/index.ts +5 -0
  340. package/template/src/features/shared/components/language-switcher.tsx +82 -0
  341. package/template/src/features/shared/components/max-width-wrapper.tsx +49 -0
  342. package/template/src/features/shared/components/mode-toggle.tsx +119 -0
  343. package/template/src/features/shared/icons/google-icon.tsx +39 -0
  344. package/template/src/features/shared/icons/index.ts +7 -0
  345. package/template/src/features/shared/index.ts +5 -0
  346. package/template/src/features/shared/manifest.ts +13 -0
  347. package/template/src/features/shared/providers.tsx +59 -0
  348. package/template/src/features/storage/actions.ts +107 -0
  349. package/template/src/features/storage/index.ts +6 -0
  350. package/template/src/features/storage/manifest.ts +22 -0
  351. package/template/src/features/storage/types.ts +143 -0
  352. package/template/src/features/storage/utils.ts +85 -0
  353. package/template/src/features/storage/validation.ts +151 -0
  354. package/template/src/features/subscription/actions/get-user-plan.ts +26 -0
  355. package/template/src/features/subscription/actions/index.ts +5 -0
  356. package/template/src/features/subscription/components/current-plan-badge.tsx +18 -0
  357. package/template/src/features/subscription/components/index.ts +6 -0
  358. package/template/src/features/subscription/components/plan-badge.tsx +98 -0
  359. package/template/src/features/subscription/index.ts +25 -0
  360. package/template/src/features/subscription/manifest.ts +13 -0
  361. package/template/src/features/subscription/services/user-plan.ts +181 -0
  362. package/template/src/features/support/actions/admin-ticket.ts +172 -0
  363. package/template/src/features/support/actions/admin-users.ts +295 -0
  364. package/template/src/features/support/actions/index.ts +22 -0
  365. package/template/src/features/support/actions/ticket.ts +12 -0
  366. package/template/src/features/support/actions/user-ticket.ts +153 -0
  367. package/template/src/features/support/components/admin-ticket-reply-form.tsx +109 -0
  368. package/template/src/features/support/components/admin-ticket-status-select.tsx +119 -0
  369. package/template/src/features/support/components/index.ts +6 -0
  370. package/template/src/features/support/components/ticket-message-form.tsx +99 -0
  371. package/template/src/features/support/components/user-role-select.tsx +107 -0
  372. package/template/src/features/support/index.ts +5 -0
  373. package/template/src/features/support/manifest.ts +53 -0
  374. package/template/src/features/support/schemas/index.ts +15 -0
  375. package/template/src/features/support/schemas/ticket.ts +81 -0
  376. package/template/src/hooks/index.ts +2 -0
  377. package/template/src/i18n/index.ts +12 -0
  378. package/template/src/i18n/request.ts +24 -0
  379. package/template/src/i18n/routing.ts +23 -0
  380. package/template/src/instrumentation.ts +19 -0
  381. package/template/src/lib/ai/index.ts +16 -0
  382. package/template/src/lib/api-logger.ts +24 -0
  383. package/template/src/lib/auth/admin.ts +54 -0
  384. package/template/src/lib/auth/api.ts +17 -0
  385. package/template/src/lib/auth/client.ts +227 -0
  386. package/template/src/lib/auth/edge.ts +84 -0
  387. package/template/src/lib/auth/errors.ts +99 -0
  388. package/template/src/lib/auth/index.ts +159 -0
  389. package/template/src/lib/auth/server.ts +46 -0
  390. package/template/src/lib/auth/session-context.tsx +96 -0
  391. package/template/src/lib/cloudflare/bindings.ts +31 -0
  392. package/template/src/lib/cookie-consent.ts +33 -0
  393. package/template/src/lib/file-utils.ts +45 -0
  394. package/template/src/lib/fingerprint/index.ts +73 -0
  395. package/template/src/lib/health/checks.ts +6 -0
  396. package/template/src/lib/health/core.ts +86 -0
  397. package/template/src/lib/health/index.ts +11 -0
  398. package/template/src/lib/health/types.ts +13 -0
  399. package/template/src/lib/index.ts +2 -0
  400. package/template/src/lib/logger/index.ts +258 -0
  401. package/template/src/lib/monitoring/index.ts +384 -0
  402. package/template/src/lib/rate-limit/anonymous.ts +85 -0
  403. package/template/src/lib/rate-limit/index.ts +111 -0
  404. package/template/src/lib/safe-action.ts +127 -0
  405. package/template/src/lib/seo/json-ld.ts +188 -0
  406. package/template/src/lib/source.ts +122 -0
  407. package/template/src/lib/utils.ts +6 -0
  408. package/template/src/middleware.ts +162 -0
  409. package/template/src/modules/index.ts +10 -0
  410. package/template/src/modules/presets.ts +8 -0
  411. package/template/src/modules/registry.ts +34 -0
  412. package/template/src/modules/selection.ts +45 -0
  413. package/template/src/services/ai.ts +49 -0
  414. package/template/src/services/index.ts +6 -0
  415. package/template/src/services/jobs.ts +3 -0
  416. package/template/src/services/mail.ts +7 -0
  417. package/template/src/services/payment.ts +3 -0
  418. package/template/src/services/rate-limit.ts +12 -0
  419. package/template/src/services/storage.ts +3 -0
  420. package/template/src/types/fumadocs-source.d.ts +14 -0
  421. package/template/src/types/index.ts +2 -0
  422. package/template/tsconfig.json +53 -0
  423. package/template/vercel.json +11 -0
  424. package/template/vitest.config.ts +51 -0
  425. package/template/wrangler.jsonc +30 -0
@@ -0,0 +1,68 @@
1
+ import type { MetadataRoute } from "next";
2
+
3
+ import { siteConfig } from "@/config";
4
+ import { getAllPseoParams } from "@/features/pseo";
5
+ import { getAllBlogSlugs, getAllLegalSlugs } from "@/lib/source";
6
+
7
+ /** Supported locales */
8
+ const locales = ["en", "zh"] as const;
9
+
10
+ /**
11
+ * 动态生成 sitemap.xml
12
+ *
13
+ * 包含所有公开页面的 URL
14
+ * 用于搜索引擎索引
15
+ */
16
+ export default function sitemap(): MetadataRoute.Sitemap {
17
+ const baseUrl = siteConfig.url;
18
+ const now = new Date();
19
+
20
+ // Static pages that exist for each locale
21
+ const staticPaths = [
22
+ "", // homepage
23
+ "/blog",
24
+ "/pseo",
25
+ ];
26
+
27
+ // Generate static routes for each locale
28
+ const staticRoutes = locales.flatMap((locale) =>
29
+ staticPaths.map((path) => ({
30
+ url: `${baseUrl}/${locale}${path}`,
31
+ lastModified: now,
32
+ changeFrequency: "weekly" as const,
33
+ priority: path === "" ? 1 : 0.8,
34
+ }))
35
+ );
36
+
37
+ // Blog posts (dynamic)
38
+ const blogSlugs = getAllBlogSlugs();
39
+ const blogRoutes = blogSlugs.map(
40
+ ({ locale, slug }: { locale: string; slug: string }) => ({
41
+ url: `${baseUrl}/${locale}/blog/${slug}`,
42
+ lastModified: now,
43
+ changeFrequency: "monthly" as const,
44
+ priority: 0.7,
45
+ })
46
+ );
47
+
48
+ // Legal pages (dynamic)
49
+ const legalSlugs = getAllLegalSlugs();
50
+ const legalRoutes = legalSlugs.map(
51
+ ({ locale, slug }: { locale: string; slug: string }) => ({
52
+ url: `${baseUrl}/${locale}/legal/${slug}`,
53
+ lastModified: now,
54
+ changeFrequency: "yearly" as const,
55
+ priority: 0.3,
56
+ })
57
+ );
58
+
59
+ const pseoSlugs = getAllPseoParams();
60
+ const pseoRoutes = pseoSlugs.map(({ locale, slug }) => ({
61
+ url: `${baseUrl}/${locale}/pseo/${slug}`,
62
+ lastModified: now,
63
+ changeFrequency: "weekly" as const,
64
+ priority: 0.6,
65
+ }));
66
+
67
+ return [...staticRoutes, ...blogRoutes, ...legalRoutes, ...pseoRoutes];
68
+ }
@@ -0,0 +1,49 @@
1
+ "use client";
2
+
3
+ import { motion, useReducedMotion } from "framer-motion";
4
+ import type { ReactNode } from "react";
5
+
6
+ /** 全站统一缓动(与 Hero 入场一致) */
7
+ export const EASE: [number, number, number, number] = [0.22, 0.7, 0.16, 1];
8
+
9
+ interface RevealProps {
10
+ children: ReactNode;
11
+ className?: string;
12
+ /** 入场延迟(秒),用于同区块内错峰 */
13
+ delay?: number;
14
+ /** 初始 Y 位移(px) */
15
+ y?: number;
16
+ }
17
+
18
+ /**
19
+ * 滚动进入视口时淡入上浮的一次性入场动画。
20
+ *
21
+ * - once: true —— 只触发一次,滚回去不重复
22
+ * - viewport margin 让动画在元素进入视口 10% 时就触发,更跟手
23
+ * - 自动尊重 prefers-reduced-motion:减弱动效用户直接渲染静态内容,
24
+ * 不做位移/淡入(避免可访问性问题)
25
+ */
26
+ export function Reveal({
27
+ children,
28
+ className,
29
+ delay = 0,
30
+ y = 16,
31
+ }: RevealProps) {
32
+ const reduce = useReducedMotion();
33
+
34
+ if (reduce) {
35
+ return <div className={className}>{children}</div>;
36
+ }
37
+
38
+ return (
39
+ <motion.div
40
+ className={className}
41
+ initial={{ opacity: 0, y }}
42
+ whileInView={{ opacity: 1, y: 0 }}
43
+ viewport={{ once: true, margin: "0px 0px -10% 0px" }}
44
+ transition={{ duration: 0.6, ease: EASE, delay }}
45
+ >
46
+ {children}
47
+ </motion.div>
48
+ );
49
+ }
@@ -0,0 +1,87 @@
1
+ import {
2
+ type ArticleSchemaInput,
3
+ type BreadcrumbItem,
4
+ type FAQItem,
5
+ generateArticleSchema,
6
+ generateBreadcrumbSchema,
7
+ generateFAQSchema,
8
+ generateOrganizationSchema,
9
+ generateSoftwareApplicationSchema,
10
+ generateWebSiteSchema,
11
+ } from "@/lib/seo/json-ld";
12
+
13
+ type LocaleType = "en" | "zh";
14
+
15
+ /**
16
+ * Generic JSON-LD script injector
17
+ */
18
+ function JsonLdScript({ data }: { data: object }) {
19
+ return (
20
+ <script
21
+ type="application/ld+json"
22
+ // biome-ignore lint/security/noDangerouslySetInnerHtml: JSON-LD injection is safe
23
+ dangerouslySetInnerHTML={{ __html: JSON.stringify(data) }}
24
+ />
25
+ );
26
+ }
27
+
28
+ /**
29
+ * WebSite + Organization (typically used in layout)
30
+ */
31
+ export function SiteJsonLd({ locale }: { locale: LocaleType }) {
32
+ return (
33
+ <>
34
+ <JsonLdScript data={generateWebSiteSchema(locale)} />
35
+ <JsonLdScript data={generateOrganizationSchema()} />
36
+ </>
37
+ );
38
+ }
39
+
40
+ /**
41
+ * Article (for blog posts)
42
+ */
43
+ export function ArticleJsonLd(props: ArticleSchemaInput) {
44
+ return <JsonLdScript data={generateArticleSchema(props)} />;
45
+ }
46
+
47
+ /**
48
+ * FAQ Page
49
+ */
50
+ export function FAQJsonLd({ faqs }: { faqs: FAQItem[] }) {
51
+ if (!faqs || faqs.length === 0) return null;
52
+ return <JsonLdScript data={generateFAQSchema(faqs)} />;
53
+ }
54
+
55
+ /**
56
+ * Breadcrumbs
57
+ */
58
+ export function BreadcrumbJsonLd({ items }: { items: BreadcrumbItem[] }) {
59
+ if (!items || items.length === 0) return null;
60
+ return <JsonLdScript data={generateBreadcrumbSchema(items)} />;
61
+ }
62
+
63
+ /**
64
+ * Software Application (for product pages)
65
+ */
66
+ export function SoftwareAppJsonLd({ locale }: { locale: LocaleType }) {
67
+ return <JsonLdScript data={generateSoftwareApplicationSchema(locale)} />;
68
+ }
69
+
70
+ /**
71
+ * Combined schema for homepage
72
+ */
73
+ export function HomePageJsonLd({
74
+ locale,
75
+ faqs,
76
+ }: {
77
+ locale: LocaleType;
78
+ faqs?: FAQItem[];
79
+ }) {
80
+ return (
81
+ <>
82
+ <SiteJsonLd locale={locale} />
83
+ <SoftwareAppJsonLd locale={locale} />
84
+ {faqs && faqs.length > 0 && <FAQJsonLd faqs={faqs} />}
85
+ </>
86
+ );
87
+ }
@@ -0,0 +1,118 @@
1
+ import { ImageResponse } from "next/og";
2
+
3
+ import { siteConfig } from "@/config";
4
+
5
+ export const OG_IMAGE_SIZE = {
6
+ width: 1200,
7
+ height: 630,
8
+ };
9
+
10
+ function getHostname(url: string | undefined): string {
11
+ if (!url) return "example.com";
12
+ try {
13
+ return new URL(url).hostname;
14
+ } catch {
15
+ const cleaned = url.replace(/^https?:\/\//, "");
16
+ return cleaned.split("/")[0] ?? cleaned;
17
+ }
18
+ }
19
+
20
+ export function createOgImageResponse(): ImageResponse {
21
+ const hostname = getHostname(siteConfig.url);
22
+
23
+ return new ImageResponse(
24
+ <div
25
+ style={{
26
+ height: "100%",
27
+ width: "100%",
28
+ display: "flex",
29
+ flexDirection: "column",
30
+ alignItems: "center",
31
+ justifyContent: "center",
32
+ backgroundColor: "#0a0a0a",
33
+ backgroundImage:
34
+ "radial-gradient(circle at 25% 25%, #7c3aed20 0%, transparent 50%), radial-gradient(circle at 75% 75%, #7c3aed10 0%, transparent 50%)",
35
+ }}
36
+ >
37
+ <div
38
+ style={{
39
+ display: "flex",
40
+ alignItems: "center",
41
+ justifyContent: "center",
42
+ marginBottom: 40,
43
+ }}
44
+ >
45
+ <div
46
+ style={{
47
+ width: 80,
48
+ height: 80,
49
+ background: "linear-gradient(135deg, #7c3aed 0%, #a855f7 100%)",
50
+ borderRadius: 20,
51
+ display: "flex",
52
+ alignItems: "center",
53
+ justifyContent: "center",
54
+ marginRight: 24,
55
+ boxShadow: "0 8px 32px rgba(124, 58, 237, 0.4)",
56
+ }}
57
+ >
58
+ {/* biome-ignore lint/a11y/noSvgWithoutTitle: decorative template icon */}
59
+ <svg
60
+ width="48"
61
+ height="48"
62
+ viewBox="0 0 24 24"
63
+ fill="none"
64
+ stroke="white"
65
+ strokeWidth="2"
66
+ strokeLinecap="round"
67
+ strokeLinejoin="round"
68
+ >
69
+ <path d="M12 2L2 7l10 5 10-5-10-5z" />
70
+ <path d="M2 17l10 5 10-5" />
71
+ <path d="M2 12l10 5 10-5" />
72
+ </svg>
73
+ </div>
74
+ <span
75
+ style={{
76
+ fontSize: 64,
77
+ fontWeight: 700,
78
+ background: "linear-gradient(135deg, #ffffff 0%, #a5a5a5 100%)",
79
+ backgroundClip: "text",
80
+ color: "transparent",
81
+ }}
82
+ >
83
+ {siteConfig.name}
84
+ </span>
85
+ </div>
86
+
87
+ <div
88
+ style={{
89
+ fontSize: 28,
90
+ color: "#a1a1aa",
91
+ textAlign: "center",
92
+ maxWidth: 800,
93
+ lineHeight: 1.4,
94
+ padding: "0 40px",
95
+ }}
96
+ >
97
+ {siteConfig.description}
98
+ </div>
99
+
100
+ <div
101
+ style={{
102
+ marginTop: 48,
103
+ display: "flex",
104
+ alignItems: "center",
105
+ padding: "12px 24px",
106
+ background: "rgba(124, 58, 237, 0.1)",
107
+ borderRadius: 50,
108
+ border: "1px solid rgba(124, 58, 237, 0.3)",
109
+ }}
110
+ >
111
+ <span style={{ fontSize: 18, color: "#a78bfa" }}>{hostname}</span>
112
+ </div>
113
+ </div>,
114
+ {
115
+ ...OG_IMAGE_SIZE,
116
+ }
117
+ );
118
+ }
@@ -0,0 +1,66 @@
1
+ "use client";
2
+
3
+ import * as AccordionPrimitive from "@radix-ui/react-accordion";
4
+ import { ChevronDownIcon } from "lucide-react";
5
+ import type * as React from "react";
6
+
7
+ import { cn } from "@/lib/utils";
8
+
9
+ function Accordion({
10
+ ...props
11
+ }: React.ComponentProps<typeof AccordionPrimitive.Root>) {
12
+ return <AccordionPrimitive.Root data-slot="accordion" {...props} />;
13
+ }
14
+
15
+ function AccordionItem({
16
+ className,
17
+ ...props
18
+ }: React.ComponentProps<typeof AccordionPrimitive.Item>) {
19
+ return (
20
+ <AccordionPrimitive.Item
21
+ data-slot="accordion-item"
22
+ className={cn("border-b last:border-b-0", className)}
23
+ {...props}
24
+ />
25
+ );
26
+ }
27
+
28
+ function AccordionTrigger({
29
+ className,
30
+ children,
31
+ ...props
32
+ }: React.ComponentProps<typeof AccordionPrimitive.Trigger>) {
33
+ return (
34
+ <AccordionPrimitive.Header className="flex">
35
+ <AccordionPrimitive.Trigger
36
+ data-slot="accordion-trigger"
37
+ className={cn(
38
+ "focus-visible:border-ring focus-visible:ring-ring/50 flex flex-1 items-start justify-between gap-4 rounded-md py-4 text-left text-sm font-medium transition-all outline-none hover:underline focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 [&[data-state=open]>svg]:rotate-180",
39
+ className
40
+ )}
41
+ {...props}
42
+ >
43
+ {children}
44
+ <ChevronDownIcon className="text-muted-foreground pointer-events-none size-4 shrink-0 translate-y-0.5 transition-transform duration-200" />
45
+ </AccordionPrimitive.Trigger>
46
+ </AccordionPrimitive.Header>
47
+ );
48
+ }
49
+
50
+ function AccordionContent({
51
+ className,
52
+ children,
53
+ ...props
54
+ }: React.ComponentProps<typeof AccordionPrimitive.Content>) {
55
+ return (
56
+ <AccordionPrimitive.Content
57
+ data-slot="accordion-content"
58
+ className="data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down overflow-hidden text-sm"
59
+ {...props}
60
+ >
61
+ <div className={cn("pt-0 pb-4", className)}>{children}</div>
62
+ </AccordionPrimitive.Content>
63
+ );
64
+ }
65
+
66
+ export { Accordion, AccordionContent, AccordionItem, AccordionTrigger };
@@ -0,0 +1,195 @@
1
+ "use client";
2
+
3
+ import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog";
4
+ import type * as React from "react";
5
+ import { Button } from "@/components/ui/button";
6
+ import { cn } from "@/lib/utils";
7
+
8
+ function AlertDialog({
9
+ ...props
10
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Root>) {
11
+ return <AlertDialogPrimitive.Root data-slot="alert-dialog" {...props} />;
12
+ }
13
+
14
+ function AlertDialogTrigger({
15
+ ...props
16
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Trigger>) {
17
+ return (
18
+ <AlertDialogPrimitive.Trigger data-slot="alert-dialog-trigger" {...props} />
19
+ );
20
+ }
21
+
22
+ function AlertDialogPortal({
23
+ ...props
24
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Portal>) {
25
+ return (
26
+ <AlertDialogPrimitive.Portal data-slot="alert-dialog-portal" {...props} />
27
+ );
28
+ }
29
+
30
+ function AlertDialogOverlay({
31
+ className,
32
+ ...props
33
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Overlay>) {
34
+ return (
35
+ <AlertDialogPrimitive.Overlay
36
+ data-slot="alert-dialog-overlay"
37
+ className={cn(
38
+ "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
39
+ className
40
+ )}
41
+ {...props}
42
+ />
43
+ );
44
+ }
45
+
46
+ function AlertDialogContent({
47
+ className,
48
+ size = "default",
49
+ ...props
50
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Content> & {
51
+ size?: "default" | "sm";
52
+ }) {
53
+ return (
54
+ <AlertDialogPortal>
55
+ <AlertDialogOverlay />
56
+ <AlertDialogPrimitive.Content
57
+ data-slot="alert-dialog-content"
58
+ data-size={size}
59
+ className={cn(
60
+ "bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 group/alert-dialog-content fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 data-[size=sm]:max-w-xs data-[size=default]:sm:max-w-lg",
61
+ className
62
+ )}
63
+ {...props}
64
+ />
65
+ </AlertDialogPortal>
66
+ );
67
+ }
68
+
69
+ function AlertDialogHeader({
70
+ className,
71
+ ...props
72
+ }: React.ComponentProps<"div">) {
73
+ return (
74
+ <div
75
+ data-slot="alert-dialog-header"
76
+ className={cn(
77
+ "grid grid-rows-[auto_1fr] place-items-center gap-1.5 text-center has-data-[slot=alert-dialog-media]:grid-rows-[auto_auto_1fr] has-data-[slot=alert-dialog-media]:gap-x-6 sm:group-data-[size=default]/alert-dialog-content:place-items-start sm:group-data-[size=default]/alert-dialog-content:text-left sm:group-data-[size=default]/alert-dialog-content:has-data-[slot=alert-dialog-media]:grid-rows-[auto_1fr]",
78
+ className
79
+ )}
80
+ {...props}
81
+ />
82
+ );
83
+ }
84
+
85
+ function AlertDialogFooter({
86
+ className,
87
+ ...props
88
+ }: React.ComponentProps<"div">) {
89
+ return (
90
+ <div
91
+ data-slot="alert-dialog-footer"
92
+ className={cn(
93
+ "flex flex-col-reverse gap-2 group-data-[size=sm]/alert-dialog-content:grid group-data-[size=sm]/alert-dialog-content:grid-cols-2 sm:flex-row sm:justify-end",
94
+ className
95
+ )}
96
+ {...props}
97
+ />
98
+ );
99
+ }
100
+
101
+ function AlertDialogTitle({
102
+ className,
103
+ ...props
104
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Title>) {
105
+ return (
106
+ <AlertDialogPrimitive.Title
107
+ data-slot="alert-dialog-title"
108
+ className={cn(
109
+ "text-lg font-semibold sm:group-data-[size=default]/alert-dialog-content:group-has-data-[slot=alert-dialog-media]/alert-dialog-content:col-start-2",
110
+ className
111
+ )}
112
+ {...props}
113
+ />
114
+ );
115
+ }
116
+
117
+ function AlertDialogDescription({
118
+ className,
119
+ ...props
120
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Description>) {
121
+ return (
122
+ <AlertDialogPrimitive.Description
123
+ data-slot="alert-dialog-description"
124
+ className={cn("text-muted-foreground text-sm", className)}
125
+ {...props}
126
+ />
127
+ );
128
+ }
129
+
130
+ function AlertDialogMedia({
131
+ className,
132
+ ...props
133
+ }: React.ComponentProps<"div">) {
134
+ return (
135
+ <div
136
+ data-slot="alert-dialog-media"
137
+ className={cn(
138
+ "bg-muted mb-2 inline-flex size-16 items-center justify-center rounded-md sm:group-data-[size=default]/alert-dialog-content:row-span-2 *:[svg:not([class*='size-'])]:size-8",
139
+ className
140
+ )}
141
+ {...props}
142
+ />
143
+ );
144
+ }
145
+
146
+ function AlertDialogAction({
147
+ className,
148
+ variant = "default",
149
+ size = "default",
150
+ ...props
151
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Action> &
152
+ Pick<React.ComponentProps<typeof Button>, "variant" | "size">) {
153
+ return (
154
+ <Button variant={variant} size={size} asChild>
155
+ <AlertDialogPrimitive.Action
156
+ data-slot="alert-dialog-action"
157
+ className={cn(className)}
158
+ {...props}
159
+ />
160
+ </Button>
161
+ );
162
+ }
163
+
164
+ function AlertDialogCancel({
165
+ className,
166
+ variant = "outline",
167
+ size = "default",
168
+ ...props
169
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Cancel> &
170
+ Pick<React.ComponentProps<typeof Button>, "variant" | "size">) {
171
+ return (
172
+ <Button variant={variant} size={size} asChild>
173
+ <AlertDialogPrimitive.Cancel
174
+ data-slot="alert-dialog-cancel"
175
+ className={cn(className)}
176
+ {...props}
177
+ />
178
+ </Button>
179
+ );
180
+ }
181
+
182
+ export {
183
+ AlertDialog,
184
+ AlertDialogAction,
185
+ AlertDialogCancel,
186
+ AlertDialogContent,
187
+ AlertDialogDescription,
188
+ AlertDialogFooter,
189
+ AlertDialogHeader,
190
+ AlertDialogMedia,
191
+ AlertDialogOverlay,
192
+ AlertDialogPortal,
193
+ AlertDialogTitle,
194
+ AlertDialogTrigger,
195
+ };
@@ -0,0 +1,53 @@
1
+ "use client";
2
+
3
+ import * as AvatarPrimitive from "@radix-ui/react-avatar";
4
+ import type * as React from "react";
5
+
6
+ import { cn } from "@/lib/utils";
7
+
8
+ function Avatar({
9
+ className,
10
+ ...props
11
+ }: React.ComponentProps<typeof AvatarPrimitive.Root>) {
12
+ return (
13
+ <AvatarPrimitive.Root
14
+ data-slot="avatar"
15
+ className={cn(
16
+ "relative flex size-8 shrink-0 overflow-hidden rounded-full",
17
+ className
18
+ )}
19
+ {...props}
20
+ />
21
+ );
22
+ }
23
+
24
+ function AvatarImage({
25
+ className,
26
+ ...props
27
+ }: React.ComponentProps<typeof AvatarPrimitive.Image>) {
28
+ return (
29
+ <AvatarPrimitive.Image
30
+ data-slot="avatar-image"
31
+ className={cn("aspect-square size-full", className)}
32
+ {...props}
33
+ />
34
+ );
35
+ }
36
+
37
+ function AvatarFallback({
38
+ className,
39
+ ...props
40
+ }: React.ComponentProps<typeof AvatarPrimitive.Fallback>) {
41
+ return (
42
+ <AvatarPrimitive.Fallback
43
+ data-slot="avatar-fallback"
44
+ className={cn(
45
+ "bg-muted flex size-full items-center justify-center rounded-full",
46
+ className
47
+ )}
48
+ {...props}
49
+ />
50
+ );
51
+ }
52
+
53
+ export { Avatar, AvatarFallback, AvatarImage };
@@ -0,0 +1,46 @@
1
+ import { Slot } from "@radix-ui/react-slot";
2
+ import { cva, type VariantProps } from "class-variance-authority";
3
+ import type * as React from "react";
4
+
5
+ import { cn } from "@/lib/utils";
6
+
7
+ const badgeVariants = cva(
8
+ "inline-flex items-center justify-center rounded-full border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden",
9
+ {
10
+ variants: {
11
+ variant: {
12
+ default:
13
+ "border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90",
14
+ secondary:
15
+ "border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",
16
+ destructive:
17
+ "border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
18
+ outline:
19
+ "text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
20
+ },
21
+ },
22
+ defaultVariants: {
23
+ variant: "default",
24
+ },
25
+ }
26
+ );
27
+
28
+ function Badge({
29
+ className,
30
+ variant,
31
+ asChild = false,
32
+ ...props
33
+ }: React.ComponentProps<"span"> &
34
+ VariantProps<typeof badgeVariants> & { asChild?: boolean }) {
35
+ const Comp = asChild ? Slot : "span";
36
+
37
+ return (
38
+ <Comp
39
+ data-slot="badge"
40
+ className={cn(badgeVariants({ variant }), className)}
41
+ {...props}
42
+ />
43
+ );
44
+ }
45
+
46
+ export { Badge, badgeVariants };