create-nala 3.0.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 (316) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +58 -0
  3. package/dist/index.js +227 -0
  4. package/package.json +48 -0
  5. package/template/.env.example +45 -0
  6. package/template/_gitignore +33 -0
  7. package/template/components.json +24 -0
  8. package/template/index.html +30 -0
  9. package/template/package.json +51 -0
  10. package/template/public/favicon.svg +15 -0
  11. package/template/public/icons.svg +24 -0
  12. package/template/public/img/avatar.webp +0 -0
  13. package/template/public/screenshot.png +0 -0
  14. package/template/src/App.vue +15 -0
  15. package/template/src/auto-imports.d.ts +691 -0
  16. package/template/src/components/AppLogo.vue +159 -0
  17. package/template/src/components/EmptyState.vue +60 -0
  18. package/template/src/components/PageHeader.vue +60 -0
  19. package/template/src/components/layout/AppNavbar.vue +159 -0
  20. package/template/src/components/layout/AppSidebar.vue +74 -0
  21. package/template/src/components/layout/CommandSearchDialog.vue +120 -0
  22. package/template/src/components/ui/accordion/Accordion.vue +15 -0
  23. package/template/src/components/ui/accordion/AccordionContent.vue +20 -0
  24. package/template/src/components/ui/accordion/AccordionItem.vue +20 -0
  25. package/template/src/components/ui/accordion/AccordionTrigger.vue +34 -0
  26. package/template/src/components/ui/accordion/index.ts +4 -0
  27. package/template/src/components/ui/alert/Alert.vue +21 -0
  28. package/template/src/components/ui/alert/AlertDescription.vue +17 -0
  29. package/template/src/components/ui/alert/AlertTitle.vue +17 -0
  30. package/template/src/components/ui/alert/index.ts +30 -0
  31. package/template/src/components/ui/alert-dialog/AlertDialog.vue +15 -0
  32. package/template/src/components/ui/alert-dialog/AlertDialogAction.vue +18 -0
  33. package/template/src/components/ui/alert-dialog/AlertDialogCancel.vue +25 -0
  34. package/template/src/components/ui/alert-dialog/AlertDialogContent.vue +44 -0
  35. package/template/src/components/ui/alert-dialog/AlertDialogDescription.vue +23 -0
  36. package/template/src/components/ui/alert-dialog/AlertDialogFooter.vue +22 -0
  37. package/template/src/components/ui/alert-dialog/AlertDialogHeader.vue +17 -0
  38. package/template/src/components/ui/alert-dialog/AlertDialogTitle.vue +21 -0
  39. package/template/src/components/ui/alert-dialog/AlertDialogTrigger.vue +12 -0
  40. package/template/src/components/ui/alert-dialog/index.ts +9 -0
  41. package/template/src/components/ui/avatar/Avatar.vue +50 -0
  42. package/template/src/components/ui/avatar/AvatarFallback.vue +21 -0
  43. package/template/src/components/ui/avatar/AvatarGroup.vue +69 -0
  44. package/template/src/components/ui/avatar/AvatarImage.vue +16 -0
  45. package/template/src/components/ui/avatar/index.ts +4 -0
  46. package/template/src/components/ui/badge/Badge.vue +40 -0
  47. package/template/src/components/ui/badge/index.ts +45 -0
  48. package/template/src/components/ui/breadcrumb/Breadcrumb.vue +17 -0
  49. package/template/src/components/ui/breadcrumb/BreadcrumbEllipsis.vue +23 -0
  50. package/template/src/components/ui/breadcrumb/BreadcrumbItem.vue +17 -0
  51. package/template/src/components/ui/breadcrumb/BreadcrumbLink.vue +21 -0
  52. package/template/src/components/ui/breadcrumb/BreadcrumbList.vue +17 -0
  53. package/template/src/components/ui/breadcrumb/BreadcrumbPage.vue +20 -0
  54. package/template/src/components/ui/breadcrumb/BreadcrumbSeparator.vue +22 -0
  55. package/template/src/components/ui/breadcrumb/index.ts +7 -0
  56. package/template/src/components/ui/button/Button.spec.ts +46 -0
  57. package/template/src/components/ui/button/Button.vue +31 -0
  58. package/template/src/components/ui/button/index.ts +41 -0
  59. package/template/src/components/ui/calendar/Calendar.vue +106 -0
  60. package/template/src/components/ui/calendar/index.ts +1 -0
  61. package/template/src/components/ui/card/Card.vue +29 -0
  62. package/template/src/components/ui/card/CardAction.vue +17 -0
  63. package/template/src/components/ui/card/CardContent.vue +17 -0
  64. package/template/src/components/ui/card/CardDescription.vue +17 -0
  65. package/template/src/components/ui/card/CardFooter.vue +17 -0
  66. package/template/src/components/ui/card/CardHeader.vue +29 -0
  67. package/template/src/components/ui/card/CardTitle.vue +17 -0
  68. package/template/src/components/ui/card/index.ts +7 -0
  69. package/template/src/components/ui/chart/AreaChart.vue +143 -0
  70. package/template/src/components/ui/chart/BarChart.vue +142 -0
  71. package/template/src/components/ui/chart/DonutChart.vue +75 -0
  72. package/template/src/components/ui/chart/LineChart.vue +132 -0
  73. package/template/src/components/ui/chart/index.ts +4 -0
  74. package/template/src/components/ui/checkbox/Checkbox.vue +57 -0
  75. package/template/src/components/ui/checkbox/index.ts +1 -0
  76. package/template/src/components/ui/collapsible/Collapsible.vue +15 -0
  77. package/template/src/components/ui/collapsible/CollapsibleContent.vue +18 -0
  78. package/template/src/components/ui/collapsible/CollapsibleTrigger.vue +12 -0
  79. package/template/src/components/ui/collapsible/index.ts +3 -0
  80. package/template/src/components/ui/command/Command.vue +88 -0
  81. package/template/src/components/ui/command/CommandDialog.vue +31 -0
  82. package/template/src/components/ui/command/CommandEmpty.vue +27 -0
  83. package/template/src/components/ui/command/CommandGroup.vue +45 -0
  84. package/template/src/components/ui/command/CommandInput.vue +39 -0
  85. package/template/src/components/ui/command/CommandItem.vue +76 -0
  86. package/template/src/components/ui/command/CommandList.vue +25 -0
  87. package/template/src/components/ui/command/CommandSeparator.vue +21 -0
  88. package/template/src/components/ui/command/CommandShortcut.vue +17 -0
  89. package/template/src/components/ui/command/index.ts +25 -0
  90. package/template/src/components/ui/context-menu/ContextMenu.vue +15 -0
  91. package/template/src/components/ui/context-menu/ContextMenuCheckboxItem.vue +36 -0
  92. package/template/src/components/ui/context-menu/ContextMenuContent.vue +33 -0
  93. package/template/src/components/ui/context-menu/ContextMenuGroup.vue +12 -0
  94. package/template/src/components/ui/context-menu/ContextMenuItem.vue +36 -0
  95. package/template/src/components/ui/context-menu/ContextMenuLabel.vue +25 -0
  96. package/template/src/components/ui/context-menu/ContextMenuRadioGroup.vue +18 -0
  97. package/template/src/components/ui/context-menu/ContextMenuRadioItem.vue +36 -0
  98. package/template/src/components/ui/context-menu/ContextMenuSeparator.vue +18 -0
  99. package/template/src/components/ui/context-menu/ContextMenuShortcut.vue +17 -0
  100. package/template/src/components/ui/context-menu/ContextMenuSub.vue +15 -0
  101. package/template/src/components/ui/context-menu/ContextMenuSubContent.vue +36 -0
  102. package/template/src/components/ui/context-menu/ContextMenuSubTrigger.vue +32 -0
  103. package/template/src/components/ui/context-menu/ContextMenuTrigger.vue +13 -0
  104. package/template/src/components/ui/context-menu/index.ts +14 -0
  105. package/template/src/components/ui/date-picker/DatePicker.vue +233 -0
  106. package/template/src/components/ui/date-picker/index.ts +1 -0
  107. package/template/src/components/ui/dialog/Dialog.vue +19 -0
  108. package/template/src/components/ui/dialog/DialogClose.vue +15 -0
  109. package/template/src/components/ui/dialog/DialogContent.vue +53 -0
  110. package/template/src/components/ui/dialog/DialogDescription.vue +23 -0
  111. package/template/src/components/ui/dialog/DialogFooter.vue +27 -0
  112. package/template/src/components/ui/dialog/DialogHeader.vue +17 -0
  113. package/template/src/components/ui/dialog/DialogOverlay.vue +21 -0
  114. package/template/src/components/ui/dialog/DialogScrollContent.vue +59 -0
  115. package/template/src/components/ui/dialog/DialogTitle.vue +23 -0
  116. package/template/src/components/ui/dialog/DialogTrigger.vue +15 -0
  117. package/template/src/components/ui/dialog/index.ts +10 -0
  118. package/template/src/components/ui/dropdown-menu/DropdownMenu.vue +19 -0
  119. package/template/src/components/ui/dropdown-menu/DropdownMenuCheckboxItem.vue +39 -0
  120. package/template/src/components/ui/dropdown-menu/DropdownMenuContent.vue +41 -0
  121. package/template/src/components/ui/dropdown-menu/DropdownMenuGroup.vue +15 -0
  122. package/template/src/components/ui/dropdown-menu/DropdownMenuItem.vue +31 -0
  123. package/template/src/components/ui/dropdown-menu/DropdownMenuLabel.vue +23 -0
  124. package/template/src/components/ui/dropdown-menu/DropdownMenuRadioGroup.vue +21 -0
  125. package/template/src/components/ui/dropdown-menu/DropdownMenuRadioItem.vue +40 -0
  126. package/template/src/components/ui/dropdown-menu/DropdownMenuSeparator.vue +23 -0
  127. package/template/src/components/ui/dropdown-menu/DropdownMenuShortcut.vue +17 -0
  128. package/template/src/components/ui/dropdown-menu/DropdownMenuSub.vue +18 -0
  129. package/template/src/components/ui/dropdown-menu/DropdownMenuSubContent.vue +27 -0
  130. package/template/src/components/ui/dropdown-menu/DropdownMenuSubTrigger.vue +31 -0
  131. package/template/src/components/ui/dropdown-menu/DropdownMenuTrigger.vue +17 -0
  132. package/template/src/components/ui/dropdown-menu/index.ts +16 -0
  133. package/template/src/components/ui/form/FormControl.vue +17 -0
  134. package/template/src/components/ui/form/FormDescription.vue +21 -0
  135. package/template/src/components/ui/form/FormItem.vue +23 -0
  136. package/template/src/components/ui/form/FormLabel.vue +25 -0
  137. package/template/src/components/ui/form/FormMessage.vue +23 -0
  138. package/template/src/components/ui/form/index.ts +7 -0
  139. package/template/src/components/ui/form/injectionKeys.ts +4 -0
  140. package/template/src/components/ui/form/useFormField.ts +30 -0
  141. package/template/src/components/ui/hover-card/HoverCard.vue +19 -0
  142. package/template/src/components/ui/hover-card/HoverCardContent.vue +43 -0
  143. package/template/src/components/ui/hover-card/HoverCardTrigger.vue +15 -0
  144. package/template/src/components/ui/hover-card/index.ts +3 -0
  145. package/template/src/components/ui/index.ts +47 -0
  146. package/template/src/components/ui/input/Input.vue +33 -0
  147. package/template/src/components/ui/input/InputAddon.vue +29 -0
  148. package/template/src/components/ui/input/InputGroup.vue +17 -0
  149. package/template/src/components/ui/input/InputIcon.vue +29 -0
  150. package/template/src/components/ui/input/index.ts +4 -0
  151. package/template/src/components/ui/kbd/Kbd.vue +30 -0
  152. package/template/src/components/ui/kbd/index.ts +1 -0
  153. package/template/src/components/ui/label/Label.vue +26 -0
  154. package/template/src/components/ui/label/index.ts +1 -0
  155. package/template/src/components/ui/loading-bar/LoadingBar.vue +41 -0
  156. package/template/src/components/ui/loading-bar/index.ts +2 -0
  157. package/template/src/components/ui/pagination/Pagination.vue +26 -0
  158. package/template/src/components/ui/pagination/PaginationContent.vue +22 -0
  159. package/template/src/components/ui/pagination/PaginationEllipsis.vue +25 -0
  160. package/template/src/components/ui/pagination/PaginationFirst.vue +33 -0
  161. package/template/src/components/ui/pagination/PaginationItem.vue +34 -0
  162. package/template/src/components/ui/pagination/PaginationLast.vue +33 -0
  163. package/template/src/components/ui/pagination/PaginationNext.vue +33 -0
  164. package/template/src/components/ui/pagination/PaginationPrevious.vue +33 -0
  165. package/template/src/components/ui/pagination/index.ts +8 -0
  166. package/template/src/components/ui/pin-input/PinInput.vue +28 -0
  167. package/template/src/components/ui/pin-input/PinInputGroup.vue +18 -0
  168. package/template/src/components/ui/pin-input/PinInputInput.vue +24 -0
  169. package/template/src/components/ui/pin-input/PinInputSeparator.vue +21 -0
  170. package/template/src/components/ui/pin-input/index.ts +4 -0
  171. package/template/src/components/ui/popover/Popover.vue +19 -0
  172. package/template/src/components/ui/popover/PopoverAnchor.vue +15 -0
  173. package/template/src/components/ui/popover/PopoverContent.vue +45 -0
  174. package/template/src/components/ui/popover/PopoverTrigger.vue +15 -0
  175. package/template/src/components/ui/popover/index.ts +4 -0
  176. package/template/src/components/ui/progress/Progress.vue +35 -0
  177. package/template/src/components/ui/progress/index.ts +1 -0
  178. package/template/src/components/ui/radio-group/RadioGroup.vue +23 -0
  179. package/template/src/components/ui/radio-group/RadioGroupItem.vue +30 -0
  180. package/template/src/components/ui/radio-group/index.ts +2 -0
  181. package/template/src/components/ui/scroll-area/ScrollArea.vue +40 -0
  182. package/template/src/components/ui/scroll-area/ScrollBar.vue +44 -0
  183. package/template/src/components/ui/scroll-area/index.ts +2 -0
  184. package/template/src/components/ui/select/Select.vue +19 -0
  185. package/template/src/components/ui/select/SelectContent.vue +51 -0
  186. package/template/src/components/ui/select/SelectGroup.vue +15 -0
  187. package/template/src/components/ui/select/SelectItem.vue +44 -0
  188. package/template/src/components/ui/select/SelectItemText.vue +15 -0
  189. package/template/src/components/ui/select/SelectLabel.vue +17 -0
  190. package/template/src/components/ui/select/SelectScrollDownButton.vue +26 -0
  191. package/template/src/components/ui/select/SelectScrollUpButton.vue +26 -0
  192. package/template/src/components/ui/select/SelectSeparator.vue +19 -0
  193. package/template/src/components/ui/select/SelectTrigger.vue +33 -0
  194. package/template/src/components/ui/select/SelectValue.vue +15 -0
  195. package/template/src/components/ui/select/index.ts +11 -0
  196. package/template/src/components/ui/separator/Separator.vue +29 -0
  197. package/template/src/components/ui/separator/index.ts +1 -0
  198. package/template/src/components/ui/sheet/Sheet.vue +19 -0
  199. package/template/src/components/ui/sheet/SheetClose.vue +15 -0
  200. package/template/src/components/ui/sheet/SheetContent.vue +62 -0
  201. package/template/src/components/ui/sheet/SheetDescription.vue +21 -0
  202. package/template/src/components/ui/sheet/SheetFooter.vue +16 -0
  203. package/template/src/components/ui/sheet/SheetHeader.vue +15 -0
  204. package/template/src/components/ui/sheet/SheetOverlay.vue +21 -0
  205. package/template/src/components/ui/sheet/SheetTitle.vue +21 -0
  206. package/template/src/components/ui/sheet/SheetTrigger.vue +15 -0
  207. package/template/src/components/ui/sheet/index.ts +8 -0
  208. package/template/src/components/ui/sidebar/Sidebar.vue +113 -0
  209. package/template/src/components/ui/sidebar/SidebarContent.vue +18 -0
  210. package/template/src/components/ui/sidebar/SidebarControl.vue +53 -0
  211. package/template/src/components/ui/sidebar/SidebarFooter.vue +18 -0
  212. package/template/src/components/ui/sidebar/SidebarGroup.vue +18 -0
  213. package/template/src/components/ui/sidebar/SidebarGroupAction.vue +27 -0
  214. package/template/src/components/ui/sidebar/SidebarGroupContent.vue +18 -0
  215. package/template/src/components/ui/sidebar/SidebarGroupLabel.vue +25 -0
  216. package/template/src/components/ui/sidebar/SidebarHeader.vue +18 -0
  217. package/template/src/components/ui/sidebar/SidebarInput.vue +22 -0
  218. package/template/src/components/ui/sidebar/SidebarInset.vue +21 -0
  219. package/template/src/components/ui/sidebar/SidebarMenu.vue +18 -0
  220. package/template/src/components/ui/sidebar/SidebarMenuAction.vue +35 -0
  221. package/template/src/components/ui/sidebar/SidebarMenuBadge.vue +26 -0
  222. package/template/src/components/ui/sidebar/SidebarMenuButton.vue +48 -0
  223. package/template/src/components/ui/sidebar/SidebarMenuButtonChild.vue +36 -0
  224. package/template/src/components/ui/sidebar/SidebarMenuItem.vue +18 -0
  225. package/template/src/components/ui/sidebar/SidebarMenuSkeleton.vue +35 -0
  226. package/template/src/components/ui/sidebar/SidebarMenuSub.vue +22 -0
  227. package/template/src/components/ui/sidebar/SidebarMenuSubButton.vue +36 -0
  228. package/template/src/components/ui/sidebar/SidebarMenuSubItem.vue +18 -0
  229. package/template/src/components/ui/sidebar/SidebarProvider.vue +136 -0
  230. package/template/src/components/ui/sidebar/SidebarRail.vue +33 -0
  231. package/template/src/components/ui/sidebar/SidebarSeparator.vue +19 -0
  232. package/template/src/components/ui/sidebar/SidebarTrigger.vue +27 -0
  233. package/template/src/components/ui/sidebar/index.ts +61 -0
  234. package/template/src/components/ui/sidebar/utils.ts +27 -0
  235. package/template/src/components/ui/skeleton/Skeleton.vue +17 -0
  236. package/template/src/components/ui/skeleton/index.ts +1 -0
  237. package/template/src/components/ui/slider/Slider.vue +42 -0
  238. package/template/src/components/ui/slider/index.ts +1 -0
  239. package/template/src/components/ui/sonner/Sonner.vue +42 -0
  240. package/template/src/components/ui/sonner/index.ts +2 -0
  241. package/template/src/components/ui/stepper/Stepper.vue +102 -0
  242. package/template/src/components/ui/stepper/index.ts +1 -0
  243. package/template/src/components/ui/switch/Switch.vue +38 -0
  244. package/template/src/components/ui/switch/index.ts +1 -0
  245. package/template/src/components/ui/table/Table.vue +16 -0
  246. package/template/src/components/ui/table/TableBody.vue +17 -0
  247. package/template/src/components/ui/table/TableCaption.vue +17 -0
  248. package/template/src/components/ui/table/TableCell.vue +22 -0
  249. package/template/src/components/ui/table/TableEmpty.vue +34 -0
  250. package/template/src/components/ui/table/TableFooter.vue +17 -0
  251. package/template/src/components/ui/table/TableHead.vue +17 -0
  252. package/template/src/components/ui/table/TableHeader.vue +17 -0
  253. package/template/src/components/ui/table/TableRow.vue +17 -0
  254. package/template/src/components/ui/table/index.ts +9 -0
  255. package/template/src/components/ui/table/utils.ts +10 -0
  256. package/template/src/components/ui/tabs/Tabs.vue +24 -0
  257. package/template/src/components/ui/tabs/TabsContent.vue +21 -0
  258. package/template/src/components/ui/tabs/TabsList.vue +24 -0
  259. package/template/src/components/ui/tabs/TabsTrigger.vue +26 -0
  260. package/template/src/components/ui/tabs/index.ts +4 -0
  261. package/template/src/components/ui/textarea/Textarea.vue +28 -0
  262. package/template/src/components/ui/textarea/index.ts +1 -0
  263. package/template/src/components/ui/timeline/Timeline.vue +73 -0
  264. package/template/src/components/ui/timeline/index.ts +1 -0
  265. package/template/src/components/ui/toggle/Toggle.vue +35 -0
  266. package/template/src/components/ui/toggle/index.ts +28 -0
  267. package/template/src/components/ui/toggle-group/ToggleGroup.vue +41 -0
  268. package/template/src/components/ui/toggle-group/ToggleGroupItem.vue +43 -0
  269. package/template/src/components/ui/toggle-group/index.ts +12 -0
  270. package/template/src/components/ui/tooltip/Tooltip.vue +19 -0
  271. package/template/src/components/ui/tooltip/TooltipContent.vue +34 -0
  272. package/template/src/components/ui/tooltip/TooltipProvider.vue +14 -0
  273. package/template/src/components/ui/tooltip/TooltipTrigger.vue +15 -0
  274. package/template/src/components/ui/tooltip/index.ts +4 -0
  275. package/template/src/components.d.ts +228 -0
  276. package/template/src/composables/useFormatter.ts +105 -0
  277. package/template/src/composables/useThemeConfig.ts +252 -0
  278. package/template/src/layouts/AdminLayout.vue +55 -0
  279. package/template/src/layouts/AuthLayout.vue +42 -0
  280. package/template/src/lib/axios.spec.ts +46 -0
  281. package/template/src/lib/axios.ts +243 -0
  282. package/template/src/lib/formatters.spec.ts +137 -0
  283. package/template/src/lib/formatters.ts +332 -0
  284. package/template/src/lib/loading-bar.ts +103 -0
  285. package/template/src/lib/utils.spec.ts +26 -0
  286. package/template/src/lib/utils.ts +7 -0
  287. package/template/src/lib/validation.spec.ts +78 -0
  288. package/template/src/main.ts +13 -0
  289. package/template/src/router/index.ts +147 -0
  290. package/template/src/services/auth.service.spec.ts +93 -0
  291. package/template/src/services/auth.service.ts +243 -0
  292. package/template/src/services/index.ts +3 -0
  293. package/template/src/services/types.ts +28 -0
  294. package/template/src/services/user.service.spec.ts +91 -0
  295. package/template/src/services/user.service.ts +283 -0
  296. package/template/src/stores/auth.spec.ts +52 -0
  297. package/template/src/stores/auth.ts +130 -0
  298. package/template/src/style.css +587 -0
  299. package/template/src/views/_starter/BlankView.vue +47 -0
  300. package/template/src/views/auth/ConfirmEmailView.vue +209 -0
  301. package/template/src/views/auth/ForgotPasswordView.vue +151 -0
  302. package/template/src/views/auth/LockScreenView.vue +175 -0
  303. package/template/src/views/auth/LoginView.vue +54 -0
  304. package/template/src/views/auth/RegisterView.vue +265 -0
  305. package/template/src/views/auth/ResetPasswordView.vue +260 -0
  306. package/template/src/views/auth/VerifyOtpView.vue +261 -0
  307. package/template/src/views/dashboard/IndexView.vue +159 -0
  308. package/template/src/views/errors/ComingSoonView.vue +241 -0
  309. package/template/src/views/errors/MaintenanceView.vue +252 -0
  310. package/template/src/views/errors/NotFoundView.vue +81 -0
  311. package/template/src/views/errors/ServerErrorView.vue +101 -0
  312. package/template/src/views/errors/UnauthorizedView.vue +64 -0
  313. package/template/tsconfig.app.json +27 -0
  314. package/template/tsconfig.json +19 -0
  315. package/template/tsconfig.node.json +23 -0
  316. package/template/vite.config.ts +46 -0
@@ -0,0 +1,261 @@
1
+ <script setup lang="ts">
2
+ import { ref, computed, onMounted, onUnmounted } from 'vue'
3
+ import { useRouter } from 'vue-router'
4
+ import { Card, CardContent, CardDescription, CardHeader, CardTitle, CardFooter } from '@/components/ui/card'
5
+ import { Button } from '@/components/ui/button'
6
+ import { Input } from '@/components/ui/input'
7
+ import { Label } from '@/components/ui/label'
8
+ import { ShieldCheck, CheckCircle2, ArrowRight, ArrowLeft, RotateCw, KeyRound, Smartphone } from '@lucide/vue'
9
+
10
+ const router = useRouter()
11
+ const isRecoveryMode = ref(false)
12
+ const recoveryCode = ref('')
13
+ const digits = ref<string[]>(['', '', '', '', '', ''])
14
+ const inputRefs = ref<HTMLInputElement[]>([])
15
+ const isVerifying = ref(false)
16
+ const isVerified = ref(false)
17
+ const errorMessage = ref('')
18
+ const countdown = ref(60)
19
+ const isResending = ref(false)
20
+ let timer: ReturnType<typeof setInterval> | null = null
21
+
22
+ const isCodeComplete = computed(() => {
23
+ if (isRecoveryMode.value) {
24
+ return recoveryCode.value.trim().length >= 8
25
+ }
26
+ return digits.value.every((d) => d.length === 1)
27
+ })
28
+
29
+ const startCountdown = () => {
30
+ countdown.value = 60
31
+ if (timer) clearInterval(timer)
32
+ timer = setInterval(() => {
33
+ if (countdown.value > 0) {
34
+ countdown.value--
35
+ } else {
36
+ if (timer) clearInterval(timer)
37
+ }
38
+ }, 1000)
39
+ }
40
+
41
+ onMounted(() => {
42
+ startCountdown()
43
+ setTimeout(() => {
44
+ inputRefs.value[0]?.focus()
45
+ }, 100)
46
+ })
47
+
48
+ onUnmounted(() => {
49
+ if (timer) clearInterval(timer)
50
+ })
51
+
52
+ const handleInput = (index: number, event: Event) => {
53
+ errorMessage.value = ''
54
+ const target = event.target as HTMLInputElement
55
+ const value = target.value.replace(/\D/g, '')
56
+
57
+ digits.value[index] = value ? value.slice(-1) : ''
58
+
59
+ if (value && index < 5) {
60
+ inputRefs.value[index + 1]?.focus()
61
+ }
62
+ }
63
+
64
+ const handleKeyDown = (index: number, event: KeyboardEvent) => {
65
+ if (event.key === 'Backspace' && !digits.value[index] && index > 0) {
66
+ inputRefs.value[index - 1]?.focus()
67
+ }
68
+ }
69
+
70
+ const handlePaste = (event: ClipboardEvent) => {
71
+ event.preventDefault()
72
+ const pastedData = event.clipboardData?.getData('text') || ''
73
+ const numbers = pastedData.replace(/\D/g, '').slice(0, 6)
74
+
75
+ if (numbers.length > 0) {
76
+ for (let i = 0; i < 6; i++) {
77
+ digits.value[i] = numbers[i] || ''
78
+ }
79
+ const nextIndex = Math.min(numbers.length, 5)
80
+ inputRefs.value[nextIndex]?.focus()
81
+ }
82
+ }
83
+
84
+ const handleVerify = () => {
85
+ if (!isCodeComplete.value) return
86
+ isVerifying.value = true
87
+ errorMessage.value = ''
88
+
89
+ setTimeout(() => {
90
+ isVerifying.value = false
91
+ const enteredCode = isRecoveryMode.value ? recoveryCode.value.trim() : digits.value.join('')
92
+
93
+ if (enteredCode === '000000') {
94
+ errorMessage.value = 'Invalid OTP code. Please check and try again.'
95
+ } else {
96
+ isVerified.value = true
97
+ }
98
+ }, 1000)
99
+ }
100
+
101
+ const handleResend = () => {
102
+ if (countdown.value > 0 || isResending.value) return
103
+ isResending.value = true
104
+ errorMessage.value = ''
105
+
106
+ setTimeout(() => {
107
+ isResending.value = false
108
+ digits.value = ['', '', '', '', '', '']
109
+ inputRefs.value[0]?.focus()
110
+ startCountdown()
111
+ }, 800)
112
+ }
113
+
114
+ const toggleRecoveryMode = () => {
115
+ isRecoveryMode.value = !isRecoveryMode.value
116
+ errorMessage.value = ''
117
+ }
118
+
119
+ const handleProceed = () => {
120
+ router.push('/')
121
+ }
122
+ </script>
123
+
124
+ <template>
125
+ <Card flush class="w-full max-w-md shadow-xl border-border/80 bg-card">
126
+ <!-- Success State -->
127
+ <template v-if="isVerified">
128
+ <CardHeader class="pt-8 pb-6 px-6 sm:px-8 space-y-2 text-center">
129
+ <div class="mx-auto mb-2 flex h-12 w-12 items-center justify-center rounded-xl bg-emerald-500/10 text-emerald-600 dark:text-emerald-400">
130
+ <CheckCircle2 class="h-6 w-6" />
131
+ </div>
132
+ <CardTitle class="text-2xl font-bold tracking-tight">Authentication verified</CardTitle>
133
+ <CardDescription class="text-sm">
134
+ Two-factor authentication verified successfully. Redirecting you to your account...
135
+ </CardDescription>
136
+ </CardHeader>
137
+
138
+ <CardContent class="px-6 sm:px-8 pb-8">
139
+ <Button class="w-full font-medium cursor-pointer" @click="handleProceed">
140
+ <span class="flex items-center justify-center gap-2">
141
+ Continue to Dashboard
142
+ <ArrowRight class="h-4 w-4" />
143
+ </span>
144
+ </Button>
145
+ </CardContent>
146
+ </template>
147
+
148
+ <!-- OTP Form State -->
149
+ <template v-else>
150
+ <CardHeader class="pt-8 pb-6 px-6 sm:px-8 space-y-1.5 text-center">
151
+ <div class="mx-auto mb-2 flex h-12 w-12 items-center justify-center rounded-xl bg-primary/10 text-primary">
152
+ <component :is="isRecoveryMode ? KeyRound : ShieldCheck" class="h-6 w-6" />
153
+ </div>
154
+ <CardTitle class="text-2xl font-bold tracking-tight">
155
+ {{ isRecoveryMode ? 'Enter recovery code' : 'Two-Factor Authentication' }}
156
+ </CardTitle>
157
+ <CardDescription class="text-sm">
158
+ {{
159
+ isRecoveryMode
160
+ ? 'Enter one of your emergency recovery codes to access your account.'
161
+ : 'Enter the 6-digit verification code generated by your authenticator app or sent to your phone.'
162
+ }}
163
+ </CardDescription>
164
+ </CardHeader>
165
+
166
+ <CardContent class="px-6 sm:px-8 pb-6 space-y-5">
167
+ <!-- Recovery Code Input -->
168
+ <div v-if="isRecoveryMode" class="space-y-2">
169
+ <Label for="recoveryCode" class="text-xs font-semibold">Recovery Code</Label>
170
+ <InputGroup>
171
+ <InputIcon>
172
+ <KeyRound />
173
+ </InputIcon>
174
+ <Input
175
+ id="recoveryCode"
176
+ v-model="recoveryCode"
177
+ type="text"
178
+ placeholder="e.g. ABCD-1234-EFGH"
179
+ class="pl-9 font-mono uppercase"
180
+ autocomplete="off"
181
+ />
182
+ </InputGroup>
183
+ </div>
184
+
185
+ <!-- 6-Digit OTP Inputs -->
186
+ <div v-else class="flex items-center justify-center gap-2 sm:gap-3" @paste="handlePaste">
187
+ <input
188
+ v-for="(_, index) in digits"
189
+ :key="index"
190
+ ref="inputRefs"
191
+ type="text"
192
+ inputmode="numeric"
193
+ pattern="[0-9]*"
194
+ maxlength="1"
195
+ :value="digits[index]"
196
+ class="h-12 w-11 sm:h-13 sm:w-12 rounded-lg border border-input bg-background text-center text-lg font-semibold shadow-xs outline-none transition-all focus:border-primary focus:ring-2 focus:ring-primary/20 disabled:cursor-not-allowed disabled:opacity-50"
197
+ @input="handleInput(index, $event)"
198
+ @keydown="handleKeyDown(index, $event)"
199
+ />
200
+ </div>
201
+
202
+ <!-- Error Message -->
203
+ <p v-if="errorMessage" class="text-xs font-medium text-destructive text-center">
204
+ {{ errorMessage }}
205
+ </p>
206
+
207
+ <!-- Submit Button -->
208
+ <Button
209
+ type="button"
210
+ class="w-full font-medium cursor-pointer"
211
+ :disabled="!isCodeComplete || isVerifying"
212
+ @click="handleVerify"
213
+ >
214
+ <span v-if="isVerifying">Verifying...</span>
215
+ <span v-else class="flex items-center justify-center gap-2">
216
+ Verify Code
217
+ <ArrowRight class="h-4 w-4" />
218
+ </span>
219
+ </Button>
220
+
221
+ <!-- Method Alternatives & Resend Options -->
222
+ <div class="space-y-3 text-center text-xs text-muted-foreground">
223
+ <div v-if="!isRecoveryMode">
224
+ <span>Didn't get a code? </span>
225
+ <button
226
+ type="button"
227
+ class="font-medium text-primary hover:underline transition-colors cursor-pointer disabled:cursor-not-allowed disabled:opacity-50 disabled:hover:no-underline inline-flex items-center gap-1"
228
+ :disabled="countdown > 0 || isResending"
229
+ @click="handleResend"
230
+ >
231
+ <RotateCw v-if="isResending" class="h-3 w-3 animate-spin" />
232
+ <span v-if="countdown > 0">Resend code in {{ countdown }}s</span>
233
+ <span v-else>Resend code</span>
234
+ </button>
235
+ </div>
236
+
237
+ <div>
238
+ <button
239
+ type="button"
240
+ class="inline-flex items-center gap-1.5 font-medium text-muted-foreground hover:text-foreground transition-colors cursor-pointer"
241
+ @click="toggleRecoveryMode"
242
+ >
243
+ <component :is="isRecoveryMode ? Smartphone : KeyRound" class="h-3.5 w-3.5" />
244
+ {{ isRecoveryMode ? 'Use authenticator code instead' : 'Use a backup recovery code' }}
245
+ </button>
246
+ </div>
247
+ </div>
248
+ </CardContent>
249
+
250
+ <CardFooter class="border-t border-border/60 bg-muted/30 px-6 py-4 flex flex-col items-center justify-center text-center">
251
+ <router-link
252
+ to="/auth/login"
253
+ class="inline-flex items-center gap-1.5 text-xs font-medium text-muted-foreground hover:text-foreground transition-colors"
254
+ >
255
+ <ArrowLeft class="h-3.5 w-3.5" />
256
+ Back to sign in
257
+ </router-link>
258
+ </CardFooter>
259
+ </template>
260
+ </Card>
261
+ </template>
@@ -0,0 +1,159 @@
1
+ <script setup lang="ts">
2
+ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
3
+ import { Button } from '@/components/ui/button'
4
+ import PageHeader from '@/components/PageHeader.vue'
5
+ import {
6
+ Users,
7
+ DollarSign,
8
+ UserPlus,
9
+ Activity,
10
+ ArrowUpRight,
11
+ Sparkles,
12
+ Layers,
13
+ Code2,
14
+ } from '@lucide/vue'
15
+
16
+ // ─── KPI Stats ────────────────────────────────────────────────────────────────
17
+ const stats = [
18
+ {
19
+ title: 'Total Revenue',
20
+ value: '$48,295',
21
+ change: '+12.5%',
22
+ changeLabel: 'vs last month',
23
+ positive: true,
24
+ icon: DollarSign,
25
+ color: 'text-emerald-500',
26
+ },
27
+ {
28
+ title: 'Active Users',
29
+ value: '14,832',
30
+ change: '+8.2%',
31
+ changeLabel: 'vs last month',
32
+ positive: true,
33
+ icon: Users,
34
+ color: 'text-primary',
35
+ },
36
+ {
37
+ title: 'New Signups',
38
+ value: '2,741',
39
+ change: '+23.1%',
40
+ changeLabel: 'this week',
41
+ positive: true,
42
+ icon: UserPlus,
43
+ color: 'text-violet-500',
44
+ },
45
+ {
46
+ title: 'System Health',
47
+ value: '99.98%',
48
+ change: 'Operational',
49
+ changeLabel: 'all systems nominal',
50
+ positive: true,
51
+ icon: Activity,
52
+ color: 'text-emerald-500',
53
+ },
54
+ ]
55
+ </script>
56
+
57
+ <template>
58
+ <div class="space-y-6">
59
+ <!-- Page Header -->
60
+ <PageHeader
61
+ title="Dashboard"
62
+ description="Welcome back to your Nala application overview."
63
+ >
64
+ <template #actions>
65
+ <Button size="sm" class="gap-1.5 cursor-pointer">
66
+ <Sparkles class="h-4 w-4" />
67
+ <span>New Action</span>
68
+ </Button>
69
+ </template>
70
+ </PageHeader>
71
+
72
+ <!-- KPI Metric Cards Grid -->
73
+ <div class="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4">
74
+ <Card
75
+ v-for="stat in stats"
76
+ :key="stat.title"
77
+ class="relative overflow-hidden transition-all duration-200 hover:shadow-md"
78
+ >
79
+ <CardHeader class="flex flex-row items-center justify-between pb-2 space-y-0">
80
+ <CardTitle class="text-xs font-medium tracking-wide uppercase text-muted-foreground">
81
+ {{ stat.title }}
82
+ </CardTitle>
83
+ <div class="flex h-8 w-8 items-center justify-center rounded-lg bg-muted/60">
84
+ <component :is="stat.icon" class="h-4 w-4" :class="stat.color" />
85
+ </div>
86
+ </CardHeader>
87
+ <CardContent>
88
+ <div class="text-2xl font-bold tracking-tight text-foreground">
89
+ {{ stat.value }}
90
+ </div>
91
+ <div class="mt-1 flex items-center text-xs text-muted-foreground">
92
+ <span
93
+ class="inline-flex items-center font-medium mr-1"
94
+ :class="stat.positive ? 'text-emerald-500' : 'text-amber-500'"
95
+ >
96
+ <ArrowUpRight class="h-3 w-3 mr-0.5" />
97
+ {{ stat.change }}
98
+ </span>
99
+ <span>{{ stat.changeLabel }}</span>
100
+ </div>
101
+ </CardContent>
102
+ </Card>
103
+ </div>
104
+
105
+ <!-- Quick Starter Cards -->
106
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
107
+ <Card>
108
+ <CardHeader>
109
+ <div class="flex items-center gap-2">
110
+ <div class="flex h-8 w-8 items-center justify-center rounded-lg bg-primary/10 text-primary">
111
+ <Layers class="h-4 w-4" />
112
+ </div>
113
+ <div>
114
+ <CardTitle class="text-base">Ready-to-use UI Primitives</CardTitle>
115
+ <CardDescription>Over 34+ accessible components preconfigured.</CardDescription>
116
+ </div>
117
+ </div>
118
+ </CardHeader>
119
+ <CardContent class="text-sm text-muted-foreground space-y-3">
120
+ <p>
121
+ Build your pages with standardized Reka UI primitives styled with Tailwind CSS v4 in
122
+ <code class="text-xs bg-muted px-1.5 py-0.5 rounded font-mono text-foreground">src/components/ui/</code>.
123
+ </p>
124
+ <div class="pt-2">
125
+ <router-link to="/_blank">
126
+ <Button variant="outline" size="sm" class="gap-1.5">
127
+ <Code2 class="h-4 w-4" />
128
+ <span>Open Blank Template</span>
129
+ </Button>
130
+ </router-link>
131
+ </div>
132
+ </CardContent>
133
+ </Card>
134
+
135
+ <Card>
136
+ <CardHeader>
137
+ <div class="flex items-center gap-2">
138
+ <div class="flex h-8 w-8 items-center justify-center rounded-lg bg-emerald-500/10 text-emerald-500">
139
+ <Sparkles class="h-4 w-4" />
140
+ </div>
141
+ <div>
142
+ <CardTitle class="text-base">Enterprise Service Layer</CardTitle>
143
+ <CardDescription>Axios client with typed request interceptors.</CardDescription>
144
+ </div>
145
+ </div>
146
+ </CardHeader>
147
+ <CardContent class="text-sm text-muted-foreground space-y-3">
148
+ <p>
149
+ Easily connect your real REST or GraphQL APIs using the modular architecture in
150
+ <code class="text-xs bg-muted px-1.5 py-0.5 rounded font-mono text-foreground">src/services/</code>.
151
+ </p>
152
+ <p class="text-xs text-muted-foreground">
153
+ Includes automatic token injection, global error toast handling, and full TypeScript typing.
154
+ </p>
155
+ </CardContent>
156
+ </Card>
157
+ </div>
158
+ </div>
159
+ </template>
@@ -0,0 +1,241 @@
1
+ <script setup lang="ts">
2
+ import { ref, onMounted, onUnmounted } from 'vue'
3
+ import { useRouter } from 'vue-router'
4
+ import {
5
+ Rocket,
6
+ Sparkles,
7
+ Bot,
8
+ ShieldCheck,
9
+ Zap,
10
+ CheckCircle2,
11
+ Home,
12
+ ArrowRight,
13
+ Users,
14
+ } from '@lucide/vue'
15
+ import { Card, CardContent } from '@/components/ui/card'
16
+ import { Button } from '@/components/ui/button'
17
+ import { Input } from '@/components/ui/input'
18
+ import AppLogo from '@/components/AppLogo.vue'
19
+ import { toast } from '@/components/ui/sonner'
20
+
21
+ const router = useRouter()
22
+
23
+ // Countdown state (Target: 14 days, 8 hours, 32 mins, 45 secs)
24
+ const days = ref(14)
25
+ const hours = ref(8)
26
+ const minutes = ref(32)
27
+ const seconds = ref(45)
28
+ let timer: ReturnType<typeof setInterval> | null = null
29
+
30
+ // Waitlist State
31
+ const email = ref('')
32
+ const isSubmitted = ref(false)
33
+ const isSubmitting = ref(false)
34
+
35
+ const upcomingFeatures = [
36
+ {
37
+ icon: Bot,
38
+ title: 'Autonomous AI Orchestration',
39
+ desc: 'Self-healing workflow pipelines with multi-agent collaborative tools.',
40
+ },
41
+ {
42
+ icon: Zap,
43
+ title: 'Sub-Millisecond Edge Sync',
44
+ desc: 'Distributed global replication powered by modern event streaming.',
45
+ },
46
+ {
47
+ icon: ShieldCheck,
48
+ title: 'Zero-Trust Audit Framework',
49
+ desc: 'Cryptographically immutable compliance logs and role policies.',
50
+ },
51
+ ]
52
+
53
+ onMounted(() => {
54
+ timer = setInterval(() => {
55
+ if (seconds.value > 0) {
56
+ seconds.value--
57
+ } else {
58
+ seconds.value = 59
59
+ if (minutes.value > 0) {
60
+ minutes.value--
61
+ } else {
62
+ minutes.value = 59
63
+ if (hours.value > 0) {
64
+ hours.value--
65
+ } else {
66
+ hours.value = 23
67
+ if (days.value > 0) {
68
+ days.value--
69
+ }
70
+ }
71
+ }
72
+ }
73
+ }, 1000)
74
+ })
75
+
76
+ onUnmounted(() => {
77
+ if (timer) clearInterval(timer)
78
+ })
79
+
80
+ async function handleJoinWaitlist() {
81
+ if (!email.value || !email.value.includes('@')) {
82
+ toast.error('Please enter a valid work email.')
83
+ return
84
+ }
85
+
86
+ isSubmitting.value = true
87
+ setTimeout(() => {
88
+ isSubmitting.value = false
89
+ isSubmitted.value = true
90
+ toast.success("You're on the early access waitlist! Check your inbox shortly.")
91
+ }, 600)
92
+ }
93
+ </script>
94
+
95
+ <template>
96
+ <div class="flex min-h-[calc(100vh-6rem)] flex-col items-center justify-center p-4 sm:p-8">
97
+ <div class="w-full max-w-3xl space-y-10 text-center">
98
+ <!-- Brand Logo Header -->
99
+ <div class="flex justify-center pb-2">
100
+ <router-link to="/" class="inline-flex items-center gap-2.5 hover:opacity-90 transition-opacity">
101
+ <AppLogo class="h-10 w-10" withBackground withText textClass="text-2xl font-bold tracking-tight" />
102
+ </router-link>
103
+ </div>
104
+
105
+ <!-- Hero Teaser Header -->
106
+ <div class="space-y-4">
107
+ <div class="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-primary/10 border border-primary/20 text-primary text-xs font-semibold shadow-xs">
108
+ <Sparkles class="h-3.5 w-3.5" />
109
+ <span>Next-Gen Enterprise Engine • v2.0 Release</span>
110
+ </div>
111
+
112
+ <h1 class="text-3xl sm:text-5xl font-black tracking-tight text-foreground max-w-2xl mx-auto leading-tight">
113
+ Something Extraordinary is in the Works
114
+ </h1>
115
+
116
+ <p class="text-sm sm:text-base text-muted-foreground leading-relaxed max-w-xl mx-auto">
117
+ We are architecting the ultimate workspace intelligence suite — combining real-time edge streaming, AI orchestration, and enterprise compliance.
118
+ </p>
119
+ </div>
120
+
121
+ <!-- Countdown Clock Cards -->
122
+ <div class="grid grid-cols-2 sm:grid-cols-4 gap-3 sm:gap-4 max-w-lg mx-auto">
123
+ <!-- Days -->
124
+ <Card class="border-border/80 bg-card/80 backdrop-blur-xs shadow-xs">
125
+ <CardContent class="p-4 flex flex-col items-center justify-center">
126
+ <span class="text-3xl sm:text-4xl font-mono font-black text-foreground">
127
+ {{ String(days).padStart(2, '0') }}
128
+ </span>
129
+ <span class="text-[11px] font-semibold text-muted-foreground uppercase mt-1">Days</span>
130
+ </CardContent>
131
+ </Card>
132
+
133
+ <!-- Hours -->
134
+ <Card class="border-border/80 bg-card/80 backdrop-blur-xs shadow-xs">
135
+ <CardContent class="p-4 flex flex-col items-center justify-center">
136
+ <span class="text-3xl sm:text-4xl font-mono font-black text-foreground">
137
+ {{ String(hours).padStart(2, '0') }}
138
+ </span>
139
+ <span class="text-[11px] font-semibold text-muted-foreground uppercase mt-1">Hours</span>
140
+ </CardContent>
141
+ </Card>
142
+
143
+ <!-- Minutes -->
144
+ <Card class="border-border/80 bg-card/80 backdrop-blur-xs shadow-xs">
145
+ <CardContent class="p-4 flex flex-col items-center justify-center">
146
+ <span class="text-3xl sm:text-4xl font-mono font-black text-foreground">
147
+ {{ String(minutes).padStart(2, '0') }}
148
+ </span>
149
+ <span class="text-[11px] font-semibold text-muted-foreground uppercase mt-1">Minutes</span>
150
+ </CardContent>
151
+ </Card>
152
+
153
+ <!-- Seconds -->
154
+ <Card class="border-border/80 bg-card/80 backdrop-blur-xs shadow-xs">
155
+ <CardContent class="p-4 flex flex-col items-center justify-center">
156
+ <span class="text-3xl sm:text-4xl font-mono font-black text-primary">
157
+ {{ String(seconds).padStart(2, '0') }}
158
+ </span>
159
+ <span class="text-[11px] font-semibold text-muted-foreground uppercase mt-1">Seconds</span>
160
+ </CardContent>
161
+ </Card>
162
+ </div>
163
+
164
+ <!-- Early Access VIP Waitlist Form -->
165
+ <Card class="border-border/80 bg-card/60 backdrop-blur-xs shadow-md max-w-md mx-auto overflow-hidden">
166
+ <CardContent class="p-6 space-y-4">
167
+ <div v-if="!isSubmitted" class="space-y-3">
168
+ <div class="text-left space-y-1">
169
+ <h2 class="text-sm font-bold text-foreground">Request Early Access</h2>
170
+ <p class="text-xs text-muted-foreground">
171
+ Join our private beta pool and receive priority onboarding invitations.
172
+ </p>
173
+ </div>
174
+
175
+ <form @submit.prevent="handleJoinWaitlist" class="flex flex-col sm:flex-row gap-2">
176
+ <Input
177
+ v-model="email"
178
+ type="email"
179
+ placeholder="name@company.com"
180
+ class="h-10 text-xs bg-background"
181
+ required
182
+ />
183
+ <Button type="submit" class="h-10 text-xs font-semibold gap-1.5 shrink-0" :disabled="isSubmitting">
184
+ <Rocket class="h-3.5 w-3.5" />
185
+ <span>{{ isSubmitting ? 'Joining...' : 'Join Waitlist' }}</span>
186
+ </Button>
187
+ </form>
188
+ </div>
189
+
190
+ <!-- Success Confirmation -->
191
+ <div v-else class="py-2 text-center space-y-2">
192
+ <div class="inline-flex items-center justify-center h-10 w-10 rounded-full bg-emerald-500/10 text-emerald-600 dark:text-emerald-400 mb-1">
193
+ <CheckCircle2 class="h-5 w-5" />
194
+ </div>
195
+ <p class="text-sm font-bold text-foreground">You're on the VIP Waitlist!</p>
196
+ <p class="text-xs text-muted-foreground">
197
+ We've reserved spot <strong class="text-primary font-mono">#1,428</strong> for <span class="text-foreground font-medium">{{ email }}</span>.
198
+ </p>
199
+ </div>
200
+
201
+ <div class="pt-2 flex items-center justify-center gap-4 text-[11px] text-muted-foreground border-t border-border/40">
202
+ <span class="flex items-center gap-1">
203
+ <Users class="h-3 w-3 text-primary" />
204
+ <span>1,427 devs waiting</span>
205
+ </span>
206
+ <span>•</span>
207
+ <span>No spam, unsubscribe anytime</span>
208
+ </div>
209
+ </CardContent>
210
+ </Card>
211
+
212
+ <!-- Teaser Features Grid -->
213
+ <div class="grid grid-cols-1 sm:grid-cols-3 gap-4 pt-4 text-left">
214
+ <div
215
+ v-for="feat in upcomingFeatures"
216
+ :key="feat.title"
217
+ class="p-4 rounded-xl border border-border/60 bg-card/40 hover:border-primary/40 hover:bg-card/70 transition-all space-y-2 shadow-2xs"
218
+ >
219
+ <div class="inline-flex items-center justify-center p-2 rounded-lg bg-primary/10 text-primary">
220
+ <component :is="feat.icon" class="h-4 w-4" />
221
+ </div>
222
+ <h3 class="text-xs font-bold text-foreground">{{ feat.title }}</h3>
223
+ <p class="text-[11px] text-muted-foreground leading-relaxed">{{ feat.desc }}</p>
224
+ </div>
225
+ </div>
226
+
227
+ <!-- Action Navigation -->
228
+ <div class="pt-4 flex items-center justify-center gap-3">
229
+ <Button variant="outline" size="sm" class="gap-1.5 text-xs h-9" @click="router.push('/')">
230
+ <Home class="h-3.5 w-3.5" />
231
+ <span>Back to Dashboard</span>
232
+ </Button>
233
+
234
+ <Button variant="ghost" size="sm" class="gap-1.5 text-xs h-9 text-muted-foreground hover:text-foreground" @click="router.push('/components/buttons')">
235
+ <span>Explore UI Components</span>
236
+ <ArrowRight class="h-3.5 w-3.5" />
237
+ </Button>
238
+ </div>
239
+ </div>
240
+ </div>
241
+ </template>