nextworks 0.0.1 → 0.1.0-alpha.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 (277) hide show
  1. package/README.md +209 -30
  2. package/dist/.gitkeep +0 -0
  3. package/dist/cli_manifests/auth_manifest.json +86 -0
  4. package/dist/cli_manifests/blocks_manifest.json +185 -0
  5. package/dist/cli_manifests/data_manifest.json +51 -0
  6. package/dist/cli_manifests/forms_manifest.json +61 -0
  7. package/dist/commands/admin-posts.d.ts +2 -0
  8. package/dist/commands/admin-posts.d.ts.map +1 -0
  9. package/dist/commands/admin-posts.js +15 -0
  10. package/dist/commands/admin-posts.js.map +1 -0
  11. package/dist/commands/admin-users.d.ts +2 -0
  12. package/dist/commands/admin-users.d.ts.map +1 -0
  13. package/dist/commands/admin-users.js +15 -0
  14. package/dist/commands/admin-users.js.map +1 -0
  15. package/dist/commands/auth-core.d.ts +2 -0
  16. package/dist/commands/auth-core.d.ts.map +1 -0
  17. package/dist/commands/auth-core.js +83 -0
  18. package/dist/commands/auth-core.js.map +1 -0
  19. package/dist/commands/auth-forms.d.ts +2 -0
  20. package/dist/commands/auth-forms.d.ts.map +1 -0
  21. package/dist/commands/auth-forms.js +15 -0
  22. package/dist/commands/auth-forms.js.map +1 -0
  23. package/dist/commands/blocks-options.d.ts +7 -0
  24. package/dist/commands/blocks-options.d.ts.map +1 -0
  25. package/dist/commands/blocks-options.js +19 -0
  26. package/dist/commands/blocks-options.js.map +1 -0
  27. package/dist/commands/blocks.d.ts +7 -0
  28. package/dist/commands/blocks.d.ts.map +1 -0
  29. package/dist/commands/blocks.js +140 -0
  30. package/dist/commands/blocks.js.map +1 -0
  31. package/dist/commands/data.d.ts +3 -0
  32. package/dist/commands/data.d.ts.map +1 -0
  33. package/dist/commands/data.js +88 -0
  34. package/dist/commands/data.js.map +1 -0
  35. package/dist/commands/forms.d.ts +6 -0
  36. package/dist/commands/forms.d.ts.map +1 -0
  37. package/dist/commands/forms.js +107 -0
  38. package/dist/commands/forms.js.map +1 -0
  39. package/dist/commands/remove-auth-core.d.ts +2 -0
  40. package/dist/commands/remove-auth-core.d.ts.map +1 -0
  41. package/dist/commands/remove-auth-core.js +69 -0
  42. package/dist/commands/remove-auth-core.js.map +1 -0
  43. package/dist/commands/remove-blocks.d.ts +2 -0
  44. package/dist/commands/remove-blocks.d.ts.map +1 -0
  45. package/dist/commands/remove-blocks.js +36 -0
  46. package/dist/commands/remove-blocks.js.map +1 -0
  47. package/dist/index.d.ts +3 -0
  48. package/dist/index.d.ts.map +1 -0
  49. package/dist/index.js +109 -0
  50. package/dist/index.js.map +1 -0
  51. package/dist/kits/auth-core/README.md +82 -0
  52. package/dist/kits/auth-core/app/(protected)/dashboard/page.tsx +8 -0
  53. package/dist/kits/auth-core/app/(protected)/layout.tsx +18 -0
  54. package/dist/kits/auth-core/app/(protected)/settings/profile/page.tsx +15 -0
  55. package/dist/kits/auth-core/app/(protected)/settings/profile/profile-form.tsx +114 -0
  56. package/dist/kits/auth-core/app/api/auth/[...nextauth]/route.ts +1 -0
  57. package/dist/kits/auth-core/app/api/auth/forgot-password/route.ts +114 -0
  58. package/dist/kits/auth-core/app/api/auth/providers/route.ts +6 -0
  59. package/dist/kits/auth-core/app/api/auth/reset-password/route.ts +63 -0
  60. package/dist/kits/auth-core/app/api/auth/send-verify-email/route.ts +6 -0
  61. package/dist/kits/auth-core/app/api/signup/route.ts +41 -0
  62. package/dist/kits/auth-core/app/auth/forgot-password/page.tsx +21 -0
  63. package/dist/kits/auth-core/app/auth/login/page.tsx +5 -0
  64. package/dist/kits/auth-core/app/auth/reset-password/page.tsx +187 -0
  65. package/dist/kits/auth-core/app/auth/signup/page.tsx +5 -0
  66. package/dist/kits/auth-core/app/auth/verify-email/page.tsx +11 -0
  67. package/dist/kits/auth-core/components/admin/admin-header.tsx +57 -0
  68. package/dist/kits/auth-core/components/auth/dashboard.tsx +237 -0
  69. package/dist/kits/auth-core/components/auth/forgot-password-form.tsx +90 -0
  70. package/dist/kits/auth-core/components/auth/login-form.tsx +467 -0
  71. package/dist/kits/auth-core/components/auth/logout-button.tsx +50 -0
  72. package/dist/kits/auth-core/components/auth/minimal-logout-button.tsx +40 -0
  73. package/dist/kits/auth-core/components/auth/signup-form.tsx +468 -0
  74. package/dist/kits/auth-core/components/require-auth.tsx +59 -0
  75. package/dist/kits/auth-core/components/session-provider.tsx +11 -0
  76. package/dist/kits/auth-core/components/ui/README.txt +1 -0
  77. package/dist/kits/auth-core/components/ui/button.tsx +55 -0
  78. package/dist/kits/auth-core/components/ui/input.tsx +25 -0
  79. package/dist/kits/auth-core/components/ui/label.tsx +23 -0
  80. package/dist/kits/auth-core/lib/api/errors.ts +14 -0
  81. package/dist/kits/auth-core/lib/auth-helpers.ts +29 -0
  82. package/dist/kits/auth-core/lib/auth.ts +142 -0
  83. package/dist/kits/auth-core/lib/email/dev-transport.ts +42 -0
  84. package/dist/kits/auth-core/lib/email/index.ts +28 -0
  85. package/dist/kits/auth-core/lib/email/provider-smtp.ts +36 -0
  86. package/dist/kits/auth-core/lib/forms/map-errors.ts +11 -0
  87. package/dist/kits/auth-core/lib/hash.ts +6 -0
  88. package/dist/kits/auth-core/lib/prisma.ts +15 -0
  89. package/dist/kits/auth-core/lib/server/result.ts +45 -0
  90. package/dist/kits/auth-core/lib/utils.ts +6 -0
  91. package/dist/kits/auth-core/lib/validation/forms.ts +88 -0
  92. package/dist/kits/auth-core/package-deps.json +19 -0
  93. package/dist/kits/auth-core/prisma/auth-models.prisma +81 -0
  94. package/dist/kits/auth-core/prisma/schema.prisma +81 -0
  95. package/dist/kits/auth-core/scripts/populate-tokenhash.mjs +26 -0
  96. package/dist/kits/auth-core/scripts/promote-admin.mjs +33 -0
  97. package/dist/kits/auth-core/scripts/seed-demo.mjs +40 -0
  98. package/dist/kits/auth-core/types/next-auth.d.ts +25 -0
  99. package/dist/kits/blocks/README.md +53 -0
  100. package/dist/kits/blocks/app/globals.css +175 -0
  101. package/dist/kits/blocks/app/templates/digitalagency/PresetThemeVars.tsx +80 -0
  102. package/dist/kits/blocks/app/templates/digitalagency/README.md +36 -0
  103. package/dist/kits/blocks/app/templates/digitalagency/components/About.tsx +99 -0
  104. package/dist/kits/blocks/app/templates/digitalagency/components/CTA.tsx +74 -0
  105. package/dist/kits/blocks/app/templates/digitalagency/components/Contact.tsx +227 -0
  106. package/dist/kits/blocks/app/templates/digitalagency/components/Footer.tsx +89 -0
  107. package/dist/kits/blocks/app/templates/digitalagency/components/Hero.tsx +90 -0
  108. package/dist/kits/blocks/app/templates/digitalagency/components/Navbar.tsx +168 -0
  109. package/dist/kits/blocks/app/templates/digitalagency/components/NetworkPattern.tsx +297 -0
  110. package/dist/kits/blocks/app/templates/digitalagency/components/Portfolio.tsx +157 -0
  111. package/dist/kits/blocks/app/templates/digitalagency/components/Pricing.tsx +114 -0
  112. package/dist/kits/blocks/app/templates/digitalagency/components/Process.tsx +59 -0
  113. package/dist/kits/blocks/app/templates/digitalagency/components/Services.tsx +55 -0
  114. package/dist/kits/blocks/app/templates/digitalagency/components/Team.tsx +28 -0
  115. package/dist/kits/blocks/app/templates/digitalagency/components/Testimonials.tsx +65 -0
  116. package/dist/kits/blocks/app/templates/digitalagency/page.tsx +38 -0
  117. package/dist/kits/blocks/app/templates/gallery/PresetThemeVars.tsx +85 -0
  118. package/dist/kits/blocks/app/templates/gallery/page.tsx +303 -0
  119. package/dist/kits/blocks/app/templates/productlaunch/PresetThemeVars.tsx +74 -0
  120. package/dist/kits/blocks/app/templates/productlaunch/README.md +55 -0
  121. package/dist/kits/blocks/app/templates/productlaunch/components/About.tsx +178 -0
  122. package/dist/kits/blocks/app/templates/productlaunch/components/CTA.tsx +93 -0
  123. package/dist/kits/blocks/app/templates/productlaunch/components/Contact.tsx +231 -0
  124. package/dist/kits/blocks/app/templates/productlaunch/components/FAQ.tsx +93 -0
  125. package/dist/kits/blocks/app/templates/productlaunch/components/Features.tsx +84 -0
  126. package/dist/kits/blocks/app/templates/productlaunch/components/Footer.tsx +132 -0
  127. package/dist/kits/blocks/app/templates/productlaunch/components/Hero.tsx +89 -0
  128. package/dist/kits/blocks/app/templates/productlaunch/components/Navbar.tsx +162 -0
  129. package/dist/kits/blocks/app/templates/productlaunch/components/Pricing.tsx +106 -0
  130. package/dist/kits/blocks/app/templates/productlaunch/components/ProcessTimeline.tsx +110 -0
  131. package/dist/kits/blocks/app/templates/productlaunch/components/ServicesGrid.tsx +68 -0
  132. package/dist/kits/blocks/app/templates/productlaunch/components/Team.tsx +104 -0
  133. package/dist/kits/blocks/app/templates/productlaunch/components/Testimonials.tsx +89 -0
  134. package/dist/kits/blocks/app/templates/productlaunch/components/TrustBadges.tsx +76 -0
  135. package/dist/kits/blocks/app/templates/productlaunch/page.tsx +45 -0
  136. package/dist/kits/blocks/app/templates/saasdashboard/PresetThemeVars.tsx +80 -0
  137. package/dist/kits/blocks/app/templates/saasdashboard/README.md +38 -0
  138. package/dist/kits/blocks/app/templates/saasdashboard/components/Contact.tsx +176 -0
  139. package/dist/kits/blocks/app/templates/saasdashboard/components/Dashboard.tsx +293 -0
  140. package/dist/kits/blocks/app/templates/saasdashboard/components/FAQ.tsx +55 -0
  141. package/dist/kits/blocks/app/templates/saasdashboard/components/Features.tsx +91 -0
  142. package/dist/kits/blocks/app/templates/saasdashboard/components/Footer.tsx +77 -0
  143. package/dist/kits/blocks/app/templates/saasdashboard/components/Hero.tsx +105 -0
  144. package/dist/kits/blocks/app/templates/saasdashboard/components/Hero_mask.tsx +127 -0
  145. package/dist/kits/blocks/app/templates/saasdashboard/components/Navbar.tsx +159 -0
  146. package/dist/kits/blocks/app/templates/saasdashboard/components/Pricing.tsx +90 -0
  147. package/dist/kits/blocks/app/templates/saasdashboard/components/SmoothScroll.tsx +97 -0
  148. package/dist/kits/blocks/app/templates/saasdashboard/components/Testimonials.tsx +72 -0
  149. package/dist/kits/blocks/app/templates/saasdashboard/components/TrustBadges.tsx +53 -0
  150. package/dist/kits/blocks/app/templates/saasdashboard/page.tsx +39 -0
  151. package/dist/kits/blocks/components/app-providers.tsx +1 -0
  152. package/dist/kits/blocks/components/enhanced-theme-provider.tsx +195 -0
  153. package/dist/kits/blocks/components/sections/About.tsx +291 -0
  154. package/dist/kits/blocks/components/sections/CTA.tsx +258 -0
  155. package/dist/kits/blocks/components/sections/Contact.tsx +267 -0
  156. package/dist/kits/blocks/components/sections/FAQ.tsx +226 -0
  157. package/dist/kits/blocks/components/sections/Features.tsx +269 -0
  158. package/dist/kits/blocks/components/sections/Footer.tsx +302 -0
  159. package/dist/kits/blocks/components/sections/HeroMotion.tsx +307 -0
  160. package/dist/kits/blocks/components/sections/HeroOverlay.tsx +358 -0
  161. package/dist/kits/blocks/components/sections/HeroSplit.tsx +352 -0
  162. package/dist/kits/blocks/components/sections/Navbar.tsx +353 -0
  163. package/dist/kits/blocks/components/sections/Newsletter.tsx +156 -0
  164. package/dist/kits/blocks/components/sections/PortfolioSimple.tsx +550 -0
  165. package/dist/kits/blocks/components/sections/Pricing.tsx +264 -0
  166. package/dist/kits/blocks/components/sections/ProcessTimeline.tsx +325 -0
  167. package/dist/kits/blocks/components/sections/ServicesGrid.tsx +210 -0
  168. package/dist/kits/blocks/components/sections/Team.tsx +309 -0
  169. package/dist/kits/blocks/components/sections/Testimonials.tsx +158 -0
  170. package/dist/kits/blocks/components/sections/TrustBadges.tsx +162 -0
  171. package/dist/kits/blocks/components/theme-provider.tsx +34 -0
  172. package/dist/kits/blocks/components/ui/alert-dialog.tsx +134 -0
  173. package/dist/kits/blocks/components/ui/brand-node.tsx +121 -0
  174. package/dist/kits/blocks/components/ui/button.tsx +122 -0
  175. package/dist/kits/blocks/components/ui/button_bck.tsx +93 -0
  176. package/dist/kits/blocks/components/ui/card.tsx +95 -0
  177. package/dist/kits/blocks/components/ui/checkbox.tsx +30 -0
  178. package/dist/kits/blocks/components/ui/cta-button.tsx +125 -0
  179. package/dist/kits/blocks/components/ui/dropdown-menu.tsx +201 -0
  180. package/dist/kits/blocks/components/ui/feature-card.tsx +91 -0
  181. package/dist/kits/blocks/components/ui/input.tsx +27 -0
  182. package/dist/kits/blocks/components/ui/label.tsx +29 -0
  183. package/dist/kits/blocks/components/ui/pricing-card.tsx +120 -0
  184. package/dist/kits/blocks/components/ui/select.tsx +25 -0
  185. package/dist/kits/blocks/components/ui/skeleton.tsx +13 -0
  186. package/dist/kits/blocks/components/ui/switch.tsx +78 -0
  187. package/dist/kits/blocks/components/ui/table.tsx +98 -0
  188. package/dist/kits/blocks/components/ui/testimonial-card.tsx +108 -0
  189. package/dist/kits/blocks/components/ui/textarea.tsx +26 -0
  190. package/dist/kits/blocks/components/ui/theme-selector.tsx +247 -0
  191. package/dist/kits/blocks/components/ui/theme-toggle.tsx +74 -0
  192. package/dist/kits/blocks/components/ui/toaster.tsx +7 -0
  193. package/dist/kits/blocks/lib/themes.ts +399 -0
  194. package/dist/kits/blocks/lib/themes_old.ts +37 -0
  195. package/dist/kits/blocks/lib/utils.ts +9 -0
  196. package/dist/kits/blocks/next.config.ts +11 -0
  197. package/dist/kits/blocks/notes/THEME_GUIDE.md +29 -0
  198. package/dist/kits/blocks/notes/THEMING_CONVERSION_SUMMARY.md +14 -0
  199. package/dist/kits/blocks/package-deps.json +22 -0
  200. package/dist/kits/blocks/public/placeholders/gallery/hero-pexels-broken-9945014.avif +0 -0
  201. package/dist/kits/blocks/public/placeholders/gallery/pexels-googledeepmind-25626431.jpg +0 -0
  202. package/dist/kits/blocks/public/placeholders/gallery/pexels-googledeepmind-25626432.jpg +0 -0
  203. package/dist/kits/blocks/public/placeholders/gallery/pexels-googledeepmind-25626434.jpg +0 -0
  204. package/dist/kits/blocks/public/placeholders/gallery/pexels-googledeepmind-25626436.jpg +0 -0
  205. package/dist/kits/blocks/public/placeholders/product_launch/feature_1.png +0 -0
  206. package/dist/kits/blocks/public/placeholders/product_launch/feature_2.png +0 -0
  207. package/dist/kits/blocks/public/placeholders/product_launch/feature_3.png +0 -0
  208. package/dist/kits/blocks/public/placeholders/product_launch/feature_4.png +0 -0
  209. package/dist/kits/blocks/public/placeholders/product_launch/hero.png +0 -0
  210. package/dist/kits/blocks/public/placeholders/saas_dashboard/analytics.png +0 -0
  211. package/dist/kits/blocks/public/placeholders/saas_dashboard/chat.png +0 -0
  212. package/dist/kits/blocks/public/placeholders/saas_dashboard/projectBoard.png +0 -0
  213. package/dist/kits/data/.gitkeep +0 -0
  214. package/dist/kits/data/README.md +80 -0
  215. package/dist/kits/data/app/(protected)/admin/posts/page.tsx +5 -0
  216. package/dist/kits/data/app/(protected)/admin/users/page.tsx +5 -0
  217. package/dist/kits/data/app/api/posts/[id]/route.ts +83 -0
  218. package/dist/kits/data/app/api/posts/route.ts +138 -0
  219. package/dist/kits/data/app/api/seed-demo/route.ts +45 -0
  220. package/dist/kits/data/app/api/users/[id]/route.ts +127 -0
  221. package/dist/kits/data/app/api/users/check-email/route.ts +18 -0
  222. package/dist/kits/data/app/api/users/check-unique/route.ts +27 -0
  223. package/dist/kits/data/app/api/users/route.ts +79 -0
  224. package/dist/kits/data/app/examples/demo/README.md +4 -0
  225. package/dist/kits/data/app/examples/demo/create-post-form.tsx +106 -0
  226. package/dist/kits/data/app/examples/demo/page.tsx +118 -0
  227. package/dist/kits/data/app/examples/demo/seed-demo-button.tsx +37 -0
  228. package/dist/kits/data/components/admin/posts-manager.tsx +719 -0
  229. package/dist/kits/data/components/admin/users-manager.tsx +432 -0
  230. package/dist/kits/data/lib/prisma.ts +15 -0
  231. package/dist/kits/data/lib/server/result.ts +90 -0
  232. package/dist/kits/data/package-deps.json +11 -0
  233. package/dist/kits/data/scripts/seed-demo.mjs +41 -0
  234. package/dist/kits/forms/.gitkeep +0 -0
  235. package/dist/kits/forms/README.md +49 -0
  236. package/dist/kits/forms/app/.gitkeep +0 -0
  237. package/dist/kits/forms/app/api/wizard/route.ts +71 -0
  238. package/dist/kits/forms/app/examples/forms/basic/page.tsx +124 -0
  239. package/dist/kits/forms/app/examples/forms/server-action/form-client.tsx +28 -0
  240. package/dist/kits/forms/app/examples/forms/server-action/page.tsx +71 -0
  241. package/dist/kits/forms/app/examples/forms/wizard/page.tsx +15 -0
  242. package/dist/kits/forms/app/examples/forms/wizard/wizard-client.tsx +2 -0
  243. package/dist/kits/forms/components/.gitkeep +0 -0
  244. package/dist/kits/forms/components/examples/wizard-client.tsx +231 -0
  245. package/dist/kits/forms/components/hooks/useCheckUnique.ts +79 -0
  246. package/dist/kits/forms/components/ui/button.tsx +122 -0
  247. package/dist/kits/forms/components/ui/checkbox.tsx +30 -0
  248. package/dist/kits/forms/components/ui/form/context.ts +33 -0
  249. package/dist/kits/forms/components/ui/form/form-control.tsx +28 -0
  250. package/dist/kits/forms/components/ui/form/form-description.tsx +22 -0
  251. package/dist/kits/forms/components/ui/form/form-field.tsx +36 -0
  252. package/dist/kits/forms/components/ui/form/form-item.tsx +21 -0
  253. package/dist/kits/forms/components/ui/form/form-label.tsx +24 -0
  254. package/dist/kits/forms/components/ui/form/form-message.tsx +29 -0
  255. package/dist/kits/forms/components/ui/form/form.tsx +26 -0
  256. package/dist/kits/forms/components/ui/input.tsx +27 -0
  257. package/dist/kits/forms/components/ui/label.tsx +29 -0
  258. package/dist/kits/forms/components/ui/select.tsx +25 -0
  259. package/dist/kits/forms/components/ui/switch.tsx +78 -0
  260. package/dist/kits/forms/components/ui/textarea.tsx +26 -0
  261. package/dist/kits/forms/lib/.gitkeep +0 -0
  262. package/dist/kits/forms/lib/forms/map-errors.ts +29 -0
  263. package/dist/kits/forms/lib/prisma.ts +16 -0
  264. package/dist/kits/forms/lib/utils.ts +9 -0
  265. package/dist/kits/forms/lib/validation/forms.ts +88 -0
  266. package/dist/kits/forms/lib/validation/wizard.ts +32 -0
  267. package/dist/kits/forms/package-deps.json +17 -0
  268. package/dist/utils/file-operations.d.ts +18 -0
  269. package/dist/utils/file-operations.d.ts.map +1 -0
  270. package/dist/utils/file-operations.js +327 -0
  271. package/dist/utils/file-operations.js.map +1 -0
  272. package/dist/utils/installation-tracker.d.ts +26 -0
  273. package/dist/utils/installation-tracker.d.ts.map +1 -0
  274. package/dist/utils/installation-tracker.js +98 -0
  275. package/dist/utils/installation-tracker.js.map +1 -0
  276. package/package.json +51 -21
  277. package/index.js +0 -1
@@ -0,0 +1,195 @@
1
+ export { EnhancedThemeProvider, useThemeVariant } from "@nextworks/blocks-core";
2
+
3
+ // "use client";
4
+
5
+ // import * as React from "react";
6
+ // import { ThemeProvider as NextThemesProvider } from "next-themes";
7
+ // import type { ThemeProviderProps as NextThemesProviderProps } from "next-themes";
8
+ // import {
9
+ // ThemeVariant,
10
+ // themes,
11
+ // darkThemes,
12
+ // type ThemeConfig,
13
+ // } from "@/lib/themes";
14
+
15
+ // type ColorTokens = ThemeConfig["colors"];
16
+ // const CUSTOM_STORAGE_KEY = "nxw-theme-custom";
17
+
18
+ // interface EnhancedThemeProviderProps {
19
+ // children: React.ReactNode;
20
+ // attribute?: NextThemesProviderProps["attribute"];
21
+ // defaultTheme?: NextThemesProviderProps["defaultTheme"];
22
+ // enableSystem?: NextThemesProviderProps["enableSystem"];
23
+ // disableTransitionOnChange?: NextThemesProviderProps["disableTransitionOnChange"];
24
+ // defaultThemeVariant?: ThemeVariant;
25
+ // // Optional DX: provide default custom tokens declaratively (e.g. brand colors)
26
+ // defaultCustomTokens?: Partial<ColorTokens> | null;
27
+ // }
28
+
29
+ // interface ThemeContextType {
30
+ // themeVariant: ThemeVariant;
31
+ // setThemeVariant: (variant: ThemeVariant) => void;
32
+
33
+ // customTheme: Partial<ColorTokens> | null;
34
+ // setCustomTheme: (tokens: Partial<ColorTokens> | null) => void;
35
+ // setCustomBrandColors: (tokens: Partial<ColorTokens>) => void;
36
+
37
+ // applyTheme: (variant: ThemeVariant, isDark?: boolean) => void;
38
+ // }
39
+
40
+ // const ThemeContext = React.createContext<ThemeContextType | undefined>(
41
+ // undefined,
42
+ // );
43
+
44
+ // export function useThemeVariant() {
45
+ // const context = React.useContext(ThemeContext);
46
+ // if (context === undefined) {
47
+ // throw new Error(
48
+ // "useThemeVariant must be used within an EnhancedThemeProvider",
49
+ // );
50
+ // }
51
+ // return context;
52
+ // }
53
+
54
+ // export function EnhancedThemeProvider({
55
+ // children,
56
+ // attribute = "class",
57
+ // defaultTheme = "system",
58
+ // enableSystem = true,
59
+ // disableTransitionOnChange = false,
60
+ // defaultThemeVariant = "monochrome",
61
+ // defaultCustomTokens = null,
62
+ // }: EnhancedThemeProviderProps) {
63
+ // const [themeVariant, setThemeVariant] =
64
+ // React.useState<ThemeVariant>(defaultThemeVariant);
65
+ // const [customTheme, setCustomThemeState] =
66
+ // React.useState<Partial<ColorTokens> | null>(defaultCustomTokens);
67
+
68
+ // const writeCustomCookies = React.useCallback(
69
+ // (tokens: Partial<ColorTokens> | null) => {
70
+ // if (tokens) {
71
+ // document.cookie = `theme-variant=custom; Path=/; Max-Age=31536000; SameSite=Lax`;
72
+ // document.cookie = `theme-custom=${encodeURIComponent(JSON.stringify(tokens))}; Path=/; Max-Age=31536000; SameSite=Lax`;
73
+ // } else {
74
+ // document.cookie = `theme-custom=; Path=/; Max-Age=0; SameSite=Lax`;
75
+ // }
76
+ // },
77
+ // [],
78
+ // );
79
+
80
+ // const setCustomTheme = React.useCallback(
81
+ // (tokens: Partial<ColorTokens> | null) => {
82
+ // setCustomThemeState(tokens);
83
+ // try {
84
+ // if (tokens) {
85
+ // localStorage.setItem(CUSTOM_STORAGE_KEY, JSON.stringify(tokens));
86
+ // } else {
87
+ // localStorage.removeItem(CUSTOM_STORAGE_KEY);
88
+ // }
89
+ // } catch {}
90
+ // writeCustomCookies(tokens ?? null);
91
+ // },
92
+ // [writeCustomCookies],
93
+ // );
94
+
95
+ // const setCustomBrandColors = React.useCallback(
96
+ // (tokens: Partial<ColorTokens>) => {
97
+ // setThemeVariant("custom");
98
+ // setCustomTheme(tokens);
99
+ // },
100
+ // [setCustomTheme],
101
+ // );
102
+
103
+ // const applyTheme = React.useCallback(
104
+ // (variant: ThemeVariant, isDark = false) => {
105
+ // const base = isDark ? darkThemes[variant].colors : themes[variant].colors;
106
+ // const merged =
107
+ // variant === "custom" && customTheme
108
+ // ? { ...base, ...customTheme }
109
+ // : base;
110
+
111
+ // // Apply CSS custom properties to the document root
112
+ // const root = document.documentElement;
113
+
114
+ // // Reflect the current variant for CSS selectors
115
+ // root.setAttribute("data-theme-variant", variant);
116
+
117
+ // Object.entries(merged).forEach(([key, value]) => {
118
+ // const cssVar = `--${key.replace(/([A-Z])/g, "-$1").toLowerCase()}`;
119
+ // root.style.setProperty(cssVar, value as string);
120
+ // });
121
+ // },
122
+ // [customTheme],
123
+ // );
124
+
125
+ // // Apply theme when variant or custom tokens change
126
+ // React.useEffect(() => {
127
+ // const isDark = document.documentElement.classList.contains("dark");
128
+ // applyTheme(themeVariant, isDark);
129
+ // }, [themeVariant, customTheme, applyTheme]);
130
+
131
+ // // Load custom tokens from localStorage on mount (overrides defaultCustomTokens if present)
132
+ // React.useEffect(() => {
133
+ // try {
134
+ // const raw = localStorage.getItem(CUSTOM_STORAGE_KEY);
135
+ // if (raw) {
136
+ // const parsed = JSON.parse(raw) as Partial<ColorTokens>;
137
+ // setCustomThemeState(parsed);
138
+ // }
139
+ // } catch {}
140
+ // }, []);
141
+
142
+ // // Listen for theme changes from next-themes
143
+
144
+ // React.useEffect(() => {
145
+ // const observer = new MutationObserver((mutations) => {
146
+ // mutations.forEach((mutation) => {
147
+ // if (
148
+ // mutation.type === "attributes" &&
149
+ // mutation.attributeName === "class"
150
+ // ) {
151
+ // const isDark = document.documentElement.classList.contains("dark");
152
+ // applyTheme(themeVariant, isDark);
153
+ // }
154
+ // });
155
+ // });
156
+
157
+ // observer.observe(document.documentElement, {
158
+ // attributes: true,
159
+ // attributeFilter: ["class"],
160
+ // });
161
+
162
+ // return () => observer.disconnect();
163
+ // }, [themeVariant, applyTheme]);
164
+
165
+ // const contextValue = React.useMemo(
166
+ // () => ({
167
+ // themeVariant,
168
+ // setThemeVariant,
169
+ // customTheme,
170
+ // setCustomTheme,
171
+ // setCustomBrandColors,
172
+ // applyTheme,
173
+ // }),
174
+ // [
175
+ // themeVariant,
176
+ // customTheme,
177
+ // setCustomTheme,
178
+ // setCustomBrandColors,
179
+ // applyTheme,
180
+ // ],
181
+ // );
182
+
183
+ // return (
184
+ // <NextThemesProvider
185
+ // attribute={attribute}
186
+ // defaultTheme={defaultTheme}
187
+ // enableSystem={enableSystem}
188
+ // disableTransitionOnChange={disableTransitionOnChange}
189
+ // >
190
+ // <ThemeContext.Provider value={contextValue}>
191
+ // {children}
192
+ // </ThemeContext.Provider>
193
+ // </NextThemesProvider>
194
+ // );
195
+ // }
@@ -0,0 +1,291 @@
1
+ "use client";
2
+
3
+ import React from "react";
4
+ import { cn } from "@/lib/utils";
5
+
6
+ /**
7
+ * Data used to render a single statistic in the About section.
8
+ *
9
+ * @remarks
10
+ * Typical examples include counts like projects completed or years of
11
+ * experience. The suffix lets you append symbols such as "+", "%", or "/7".
12
+ *
13
+ * @public
14
+ */
15
+ export interface AboutStatData {
16
+ /** The main numeric/text value (e.g., "50") */
17
+ value?: string;
18
+ /** Short label describing the stat (e.g., "Successful Projects") */
19
+ label?: string;
20
+ /** Optional suffix appended to the value (e.g., "+") */
21
+ suffix?: string;
22
+ }
23
+
24
+ /**
25
+ * Props for the About section component.
26
+ *
27
+ * @remarks
28
+ * - Styling: exposes slot-style className overrides (section, container,
29
+ * inner, contentContainer, contentStack, subheading, heading, content,
30
+ * statsSection, statsGrid, statItem, statNumber, statLabel). Consumer
31
+ * classes are merged after defaults via cn().
32
+ * - Layout: text alignment can be set via aboutTextAlign.
33
+ * - Motion: animateStats enables a count-up animation for numeric stats on first viewport visibility.
34
+ * - Accessibility: rendered as a semantic <section> with aria-label.
35
+ */
36
+ interface AboutProps {
37
+ /** Main heading text displayed in the section header. @defaultValue "Your Success Is Our Mission" */
38
+ aboutHeadingText?: string;
39
+ /** Optional subheading text displayed above the heading. */
40
+ aboutSubheadingText?: string;
41
+ /** Primary paragraph/content body. @defaultValue a short marketing blurb */
42
+ aboutContentText?: string;
43
+ /** Optional list of statistics to render below the content. @defaultValue defaultStatsData */
44
+ aboutStats?: AboutStatData[];
45
+
46
+ /** Optional top-level class to override the section root */
47
+ className?: string;
48
+
49
+ /** Slot-style overrides */
50
+ section?: { className?: string };
51
+ container?: { className?: string };
52
+ inner?: { className?: string };
53
+ contentContainer?: { className?: string };
54
+ contentStack?: { className?: string };
55
+ subheading?: { className?: string };
56
+ heading?: { className?: string };
57
+ content?: { className?: string };
58
+ statsSection?: { className?: string };
59
+ statsGrid?: { className?: string };
60
+ statItem?: { className?: string };
61
+ statNumber?: { className?: string };
62
+ statLabel?: { className?: string };
63
+
64
+ /** Controls text alignment at various breakpoints. @defaultValue "center" */
65
+ aboutTextAlign?: "left" | "center" | "right";
66
+ /** Whether to render the stats block at the bottom. @defaultValue true */
67
+ showStats?: boolean;
68
+ /** Enable count-up animation for numeric stats when they enter the viewport. @defaultValue false */
69
+ animateStats?: boolean;
70
+
71
+ /** ARIA label for the section. @defaultValue "About section" */
72
+ ariaLabel?: string;
73
+ }
74
+
75
+ /**
76
+ * Default statistics displayed if no aboutStats are provided.
77
+ */
78
+ const defaultStatsData: AboutStatData[] = [
79
+ { value: "50", label: "Successful Projects", suffix: "+" },
80
+ { value: "5", label: "Years Experience", suffix: "+" },
81
+ { value: "100", label: "Happy Clients", suffix: "%" },
82
+ { value: "24", label: "Support Hours", suffix: "/7" },
83
+ ];
84
+
85
+ /**
86
+ * About section with a heading, optional subheading/content, and an optional
87
+ * stats block.
88
+ *
89
+ * @remarks
90
+ * - Styling: exposes slot-style className overrides. Consumer classes are
91
+ * merged after defaults via cn().
92
+ * - Layout: control text alignment with aboutTextAlign.
93
+ * - Accessibility: rendered as a semantic <section> with aria-label.
94
+ *
95
+ * @example
96
+ * <About
97
+ * aboutHeadingText="Your Success Is Our Mission"
98
+ * aboutContentText="We build digital products that convert."
99
+ * aboutStats={[{ value: "50", label: "Successful Projects", suffix: "+" }]}
100
+ * />
101
+ */
102
+ /**
103
+ * About section with a heading, optional subheading/content, and an optional
104
+ * stats block.
105
+ *
106
+ * @remarks
107
+ * - Styling: exposes slot-style className overrides. Consumer classes are
108
+ * merged after defaults via cn().
109
+ * - Layout: control text alignment with aboutTextAlign.
110
+ * - Accessibility: rendered as a semantic <section> with aria-label.
111
+ *
112
+ * @example
113
+ * <About
114
+ * aboutHeadingText="Your Success Is Our Mission"
115
+ * aboutContentText="We build digital products that convert."
116
+ * aboutStats={[{ value: "50", label: "Successful Projects", suffix: "+" }]}
117
+ * />
118
+ */
119
+ /**
120
+ * Animated count-up for integers using requestAnimationFrame.
121
+ * Eases out and runs once when shouldStart becomes true.
122
+ */
123
+ function AnimatedCount({
124
+ end,
125
+ shouldStart,
126
+ duration = 1500,
127
+ }: {
128
+ end: number;
129
+ shouldStart: boolean;
130
+ duration?: number;
131
+ }) {
132
+ const [value, setValue] = React.useState(0);
133
+ const [done, setDone] = React.useState(false);
134
+
135
+ const prefersReduced = React.useMemo(() => {
136
+ if (typeof window === "undefined") return false;
137
+ return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
138
+ }, []);
139
+
140
+ React.useEffect(() => {
141
+ if (prefersReduced) return; // show end value immediately below
142
+ if (!shouldStart || done) return;
143
+ let raf: number;
144
+ const startTs = performance.now();
145
+
146
+ const tick = (now: number) => {
147
+ const progress = Math.min((now - startTs) / duration, 1);
148
+ const eased = 1 - Math.pow(1 - progress, 3);
149
+ const next = Math.round(eased * end);
150
+ setValue(next);
151
+ if (progress < 1) {
152
+ raf = requestAnimationFrame(tick);
153
+ } else {
154
+ setDone(true);
155
+ }
156
+ };
157
+
158
+ raf = requestAnimationFrame(tick);
159
+ return () => cancelAnimationFrame(raf);
160
+ }, [shouldStart, end, duration, done, prefersReduced]);
161
+
162
+ if (prefersReduced) return <>{end}</>;
163
+ return <>{value}</>;
164
+ }
165
+
166
+ export function About({
167
+ aboutHeadingText = "Your Success Is Our Mission",
168
+ aboutSubheadingText,
169
+ aboutContentText = "With 50+ successful projects and 5 years of experience, we specialize in creating digital solutions that drive real business growth.",
170
+ aboutStats = defaultStatsData,
171
+ className,
172
+ section = { className: "py-20 bg-muted text-foreground" },
173
+ container = { className: "max-w-7xl mx-auto px-6" },
174
+ inner = { className: "flex flex-col gap-12" },
175
+ contentContainer = { className: "max-w-4xl mx-auto" },
176
+ contentStack = { className: "flex flex-col gap-6" },
177
+ subheading = {
178
+ className:
179
+ "text-sm font-semibold font-poppins text-primary uppercase tracking-wider",
180
+ },
181
+ heading = {
182
+ className: "text-3xl font-bold font-poppins text-foreground leading-tight",
183
+ },
184
+ content = {
185
+ className:
186
+ "text-lg font-inter text-foreground leading-relaxed opacity-90 max-w-3xl",
187
+ },
188
+ statsSection = {
189
+ className:
190
+ "bg-card p-8 rounded-xl shadow-lg mx-auto max-w-5xl w-full border border-border",
191
+ },
192
+ statsGrid = {
193
+ className: "grid grid-cols-2 md:grid-cols-4 gap-8 justify-items-center",
194
+ },
195
+ statItem = { className: "flex flex-col items-center gap-2" },
196
+ statNumber = {
197
+ className: "text-4xl font-bold font-poppins text-foreground leading-none",
198
+ },
199
+ statLabel = {
200
+ className:
201
+ "text-sm font-medium font-inter text-foreground text-center opacity-80",
202
+ },
203
+ aboutTextAlign = "center",
204
+ showStats = true,
205
+ animateStats = false,
206
+ ariaLabel = "About section",
207
+ }: AboutProps) {
208
+ const textAlignClasses = {
209
+ left: "text-left",
210
+ right: "text-right",
211
+ center: "text-center",
212
+ } as const;
213
+
214
+ const statsSectionRef = React.useRef<HTMLDivElement | null>(null);
215
+ const [shouldStartCount, setShouldStartCount] = React.useState(false);
216
+
217
+ React.useEffect(() => {
218
+ if (!animateStats) return;
219
+ if (!statsSectionRef.current) return;
220
+
221
+ const observer = new IntersectionObserver(
222
+ (entries) => {
223
+ for (const entry of entries) {
224
+ if (entry.isIntersecting) {
225
+ setShouldStartCount(true);
226
+ observer.disconnect();
227
+ break;
228
+ }
229
+ }
230
+ },
231
+ { threshold: 0.3 },
232
+ );
233
+
234
+ observer.observe(statsSectionRef.current);
235
+
236
+ return () => observer.disconnect();
237
+ }, [animateStats]);
238
+
239
+ return (
240
+ <section
241
+ className={cn(section.className, className)}
242
+ aria-label={ariaLabel}
243
+ >
244
+ <div className={cn(container.className)}>
245
+ <div className={cn(inner.className)}>
246
+ <div
247
+ className={cn(
248
+ contentContainer.className,
249
+ textAlignClasses[aboutTextAlign],
250
+ )}
251
+ >
252
+ <div className={cn(contentStack.className)}>
253
+ {aboutSubheadingText && (
254
+ <div className={cn(subheading.className)}>
255
+ {aboutSubheadingText}
256
+ </div>
257
+ )}
258
+ <h2 className={cn(heading.className)}>{aboutHeadingText}</h2>
259
+ <p className={cn(content.className)}>{aboutContentText}</p>
260
+ </div>
261
+ </div>
262
+ {showStats && aboutStats?.length ? (
263
+ <div ref={statsSectionRef} className={cn(statsSection.className)}>
264
+ <div className={cn(statsGrid.className)}>
265
+ {aboutStats.map((s, i) => (
266
+ <div
267
+ key={`${s.label ?? "stat"}-${i}`}
268
+ className={cn(statItem.className)}
269
+ >
270
+ <div className={cn(statNumber.className)}>
271
+ {animateStats && Number.isFinite(Number(s.value)) ? (
272
+ <AnimatedCount
273
+ end={Number(s.value)}
274
+ shouldStart={shouldStartCount}
275
+ />
276
+ ) : (
277
+ s.value
278
+ )}
279
+ {s.suffix}
280
+ </div>
281
+ <div className={cn(statLabel.className)}>{s.label}</div>
282
+ </div>
283
+ ))}
284
+ </div>
285
+ </div>
286
+ ) : null}
287
+ </div>
288
+ </div>
289
+ </section>
290
+ );
291
+ }