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.
- package/LICENSE +21 -0
- package/README.md +58 -0
- package/dist/index.js +227 -0
- package/package.json +48 -0
- package/template/.env.example +45 -0
- package/template/_gitignore +33 -0
- package/template/components.json +24 -0
- package/template/index.html +30 -0
- package/template/package.json +51 -0
- package/template/public/favicon.svg +15 -0
- package/template/public/icons.svg +24 -0
- package/template/public/img/avatar.webp +0 -0
- package/template/public/screenshot.png +0 -0
- package/template/src/App.vue +15 -0
- package/template/src/auto-imports.d.ts +691 -0
- package/template/src/components/AppLogo.vue +159 -0
- package/template/src/components/EmptyState.vue +60 -0
- package/template/src/components/PageHeader.vue +60 -0
- package/template/src/components/layout/AppNavbar.vue +159 -0
- package/template/src/components/layout/AppSidebar.vue +74 -0
- package/template/src/components/layout/CommandSearchDialog.vue +120 -0
- package/template/src/components/ui/accordion/Accordion.vue +15 -0
- package/template/src/components/ui/accordion/AccordionContent.vue +20 -0
- package/template/src/components/ui/accordion/AccordionItem.vue +20 -0
- package/template/src/components/ui/accordion/AccordionTrigger.vue +34 -0
- package/template/src/components/ui/accordion/index.ts +4 -0
- package/template/src/components/ui/alert/Alert.vue +21 -0
- package/template/src/components/ui/alert/AlertDescription.vue +17 -0
- package/template/src/components/ui/alert/AlertTitle.vue +17 -0
- package/template/src/components/ui/alert/index.ts +30 -0
- package/template/src/components/ui/alert-dialog/AlertDialog.vue +15 -0
- package/template/src/components/ui/alert-dialog/AlertDialogAction.vue +18 -0
- package/template/src/components/ui/alert-dialog/AlertDialogCancel.vue +25 -0
- package/template/src/components/ui/alert-dialog/AlertDialogContent.vue +44 -0
- package/template/src/components/ui/alert-dialog/AlertDialogDescription.vue +23 -0
- package/template/src/components/ui/alert-dialog/AlertDialogFooter.vue +22 -0
- package/template/src/components/ui/alert-dialog/AlertDialogHeader.vue +17 -0
- package/template/src/components/ui/alert-dialog/AlertDialogTitle.vue +21 -0
- package/template/src/components/ui/alert-dialog/AlertDialogTrigger.vue +12 -0
- package/template/src/components/ui/alert-dialog/index.ts +9 -0
- package/template/src/components/ui/avatar/Avatar.vue +50 -0
- package/template/src/components/ui/avatar/AvatarFallback.vue +21 -0
- package/template/src/components/ui/avatar/AvatarGroup.vue +69 -0
- package/template/src/components/ui/avatar/AvatarImage.vue +16 -0
- package/template/src/components/ui/avatar/index.ts +4 -0
- package/template/src/components/ui/badge/Badge.vue +40 -0
- package/template/src/components/ui/badge/index.ts +45 -0
- package/template/src/components/ui/breadcrumb/Breadcrumb.vue +17 -0
- package/template/src/components/ui/breadcrumb/BreadcrumbEllipsis.vue +23 -0
- package/template/src/components/ui/breadcrumb/BreadcrumbItem.vue +17 -0
- package/template/src/components/ui/breadcrumb/BreadcrumbLink.vue +21 -0
- package/template/src/components/ui/breadcrumb/BreadcrumbList.vue +17 -0
- package/template/src/components/ui/breadcrumb/BreadcrumbPage.vue +20 -0
- package/template/src/components/ui/breadcrumb/BreadcrumbSeparator.vue +22 -0
- package/template/src/components/ui/breadcrumb/index.ts +7 -0
- package/template/src/components/ui/button/Button.spec.ts +46 -0
- package/template/src/components/ui/button/Button.vue +31 -0
- package/template/src/components/ui/button/index.ts +41 -0
- package/template/src/components/ui/calendar/Calendar.vue +106 -0
- package/template/src/components/ui/calendar/index.ts +1 -0
- package/template/src/components/ui/card/Card.vue +29 -0
- package/template/src/components/ui/card/CardAction.vue +17 -0
- package/template/src/components/ui/card/CardContent.vue +17 -0
- package/template/src/components/ui/card/CardDescription.vue +17 -0
- package/template/src/components/ui/card/CardFooter.vue +17 -0
- package/template/src/components/ui/card/CardHeader.vue +29 -0
- package/template/src/components/ui/card/CardTitle.vue +17 -0
- package/template/src/components/ui/card/index.ts +7 -0
- package/template/src/components/ui/chart/AreaChart.vue +143 -0
- package/template/src/components/ui/chart/BarChart.vue +142 -0
- package/template/src/components/ui/chart/DonutChart.vue +75 -0
- package/template/src/components/ui/chart/LineChart.vue +132 -0
- package/template/src/components/ui/chart/index.ts +4 -0
- package/template/src/components/ui/checkbox/Checkbox.vue +57 -0
- package/template/src/components/ui/checkbox/index.ts +1 -0
- package/template/src/components/ui/collapsible/Collapsible.vue +15 -0
- package/template/src/components/ui/collapsible/CollapsibleContent.vue +18 -0
- package/template/src/components/ui/collapsible/CollapsibleTrigger.vue +12 -0
- package/template/src/components/ui/collapsible/index.ts +3 -0
- package/template/src/components/ui/command/Command.vue +88 -0
- package/template/src/components/ui/command/CommandDialog.vue +31 -0
- package/template/src/components/ui/command/CommandEmpty.vue +27 -0
- package/template/src/components/ui/command/CommandGroup.vue +45 -0
- package/template/src/components/ui/command/CommandInput.vue +39 -0
- package/template/src/components/ui/command/CommandItem.vue +76 -0
- package/template/src/components/ui/command/CommandList.vue +25 -0
- package/template/src/components/ui/command/CommandSeparator.vue +21 -0
- package/template/src/components/ui/command/CommandShortcut.vue +17 -0
- package/template/src/components/ui/command/index.ts +25 -0
- package/template/src/components/ui/context-menu/ContextMenu.vue +15 -0
- package/template/src/components/ui/context-menu/ContextMenuCheckboxItem.vue +36 -0
- package/template/src/components/ui/context-menu/ContextMenuContent.vue +33 -0
- package/template/src/components/ui/context-menu/ContextMenuGroup.vue +12 -0
- package/template/src/components/ui/context-menu/ContextMenuItem.vue +36 -0
- package/template/src/components/ui/context-menu/ContextMenuLabel.vue +25 -0
- package/template/src/components/ui/context-menu/ContextMenuRadioGroup.vue +18 -0
- package/template/src/components/ui/context-menu/ContextMenuRadioItem.vue +36 -0
- package/template/src/components/ui/context-menu/ContextMenuSeparator.vue +18 -0
- package/template/src/components/ui/context-menu/ContextMenuShortcut.vue +17 -0
- package/template/src/components/ui/context-menu/ContextMenuSub.vue +15 -0
- package/template/src/components/ui/context-menu/ContextMenuSubContent.vue +36 -0
- package/template/src/components/ui/context-menu/ContextMenuSubTrigger.vue +32 -0
- package/template/src/components/ui/context-menu/ContextMenuTrigger.vue +13 -0
- package/template/src/components/ui/context-menu/index.ts +14 -0
- package/template/src/components/ui/date-picker/DatePicker.vue +233 -0
- package/template/src/components/ui/date-picker/index.ts +1 -0
- package/template/src/components/ui/dialog/Dialog.vue +19 -0
- package/template/src/components/ui/dialog/DialogClose.vue +15 -0
- package/template/src/components/ui/dialog/DialogContent.vue +53 -0
- package/template/src/components/ui/dialog/DialogDescription.vue +23 -0
- package/template/src/components/ui/dialog/DialogFooter.vue +27 -0
- package/template/src/components/ui/dialog/DialogHeader.vue +17 -0
- package/template/src/components/ui/dialog/DialogOverlay.vue +21 -0
- package/template/src/components/ui/dialog/DialogScrollContent.vue +59 -0
- package/template/src/components/ui/dialog/DialogTitle.vue +23 -0
- package/template/src/components/ui/dialog/DialogTrigger.vue +15 -0
- package/template/src/components/ui/dialog/index.ts +10 -0
- package/template/src/components/ui/dropdown-menu/DropdownMenu.vue +19 -0
- package/template/src/components/ui/dropdown-menu/DropdownMenuCheckboxItem.vue +39 -0
- package/template/src/components/ui/dropdown-menu/DropdownMenuContent.vue +41 -0
- package/template/src/components/ui/dropdown-menu/DropdownMenuGroup.vue +15 -0
- package/template/src/components/ui/dropdown-menu/DropdownMenuItem.vue +31 -0
- package/template/src/components/ui/dropdown-menu/DropdownMenuLabel.vue +23 -0
- package/template/src/components/ui/dropdown-menu/DropdownMenuRadioGroup.vue +21 -0
- package/template/src/components/ui/dropdown-menu/DropdownMenuRadioItem.vue +40 -0
- package/template/src/components/ui/dropdown-menu/DropdownMenuSeparator.vue +23 -0
- package/template/src/components/ui/dropdown-menu/DropdownMenuShortcut.vue +17 -0
- package/template/src/components/ui/dropdown-menu/DropdownMenuSub.vue +18 -0
- package/template/src/components/ui/dropdown-menu/DropdownMenuSubContent.vue +27 -0
- package/template/src/components/ui/dropdown-menu/DropdownMenuSubTrigger.vue +31 -0
- package/template/src/components/ui/dropdown-menu/DropdownMenuTrigger.vue +17 -0
- package/template/src/components/ui/dropdown-menu/index.ts +16 -0
- package/template/src/components/ui/form/FormControl.vue +17 -0
- package/template/src/components/ui/form/FormDescription.vue +21 -0
- package/template/src/components/ui/form/FormItem.vue +23 -0
- package/template/src/components/ui/form/FormLabel.vue +25 -0
- package/template/src/components/ui/form/FormMessage.vue +23 -0
- package/template/src/components/ui/form/index.ts +7 -0
- package/template/src/components/ui/form/injectionKeys.ts +4 -0
- package/template/src/components/ui/form/useFormField.ts +30 -0
- package/template/src/components/ui/hover-card/HoverCard.vue +19 -0
- package/template/src/components/ui/hover-card/HoverCardContent.vue +43 -0
- package/template/src/components/ui/hover-card/HoverCardTrigger.vue +15 -0
- package/template/src/components/ui/hover-card/index.ts +3 -0
- package/template/src/components/ui/index.ts +47 -0
- package/template/src/components/ui/input/Input.vue +33 -0
- package/template/src/components/ui/input/InputAddon.vue +29 -0
- package/template/src/components/ui/input/InputGroup.vue +17 -0
- package/template/src/components/ui/input/InputIcon.vue +29 -0
- package/template/src/components/ui/input/index.ts +4 -0
- package/template/src/components/ui/kbd/Kbd.vue +30 -0
- package/template/src/components/ui/kbd/index.ts +1 -0
- package/template/src/components/ui/label/Label.vue +26 -0
- package/template/src/components/ui/label/index.ts +1 -0
- package/template/src/components/ui/loading-bar/LoadingBar.vue +41 -0
- package/template/src/components/ui/loading-bar/index.ts +2 -0
- package/template/src/components/ui/pagination/Pagination.vue +26 -0
- package/template/src/components/ui/pagination/PaginationContent.vue +22 -0
- package/template/src/components/ui/pagination/PaginationEllipsis.vue +25 -0
- package/template/src/components/ui/pagination/PaginationFirst.vue +33 -0
- package/template/src/components/ui/pagination/PaginationItem.vue +34 -0
- package/template/src/components/ui/pagination/PaginationLast.vue +33 -0
- package/template/src/components/ui/pagination/PaginationNext.vue +33 -0
- package/template/src/components/ui/pagination/PaginationPrevious.vue +33 -0
- package/template/src/components/ui/pagination/index.ts +8 -0
- package/template/src/components/ui/pin-input/PinInput.vue +28 -0
- package/template/src/components/ui/pin-input/PinInputGroup.vue +18 -0
- package/template/src/components/ui/pin-input/PinInputInput.vue +24 -0
- package/template/src/components/ui/pin-input/PinInputSeparator.vue +21 -0
- package/template/src/components/ui/pin-input/index.ts +4 -0
- package/template/src/components/ui/popover/Popover.vue +19 -0
- package/template/src/components/ui/popover/PopoverAnchor.vue +15 -0
- package/template/src/components/ui/popover/PopoverContent.vue +45 -0
- package/template/src/components/ui/popover/PopoverTrigger.vue +15 -0
- package/template/src/components/ui/popover/index.ts +4 -0
- package/template/src/components/ui/progress/Progress.vue +35 -0
- package/template/src/components/ui/progress/index.ts +1 -0
- package/template/src/components/ui/radio-group/RadioGroup.vue +23 -0
- package/template/src/components/ui/radio-group/RadioGroupItem.vue +30 -0
- package/template/src/components/ui/radio-group/index.ts +2 -0
- package/template/src/components/ui/scroll-area/ScrollArea.vue +40 -0
- package/template/src/components/ui/scroll-area/ScrollBar.vue +44 -0
- package/template/src/components/ui/scroll-area/index.ts +2 -0
- package/template/src/components/ui/select/Select.vue +19 -0
- package/template/src/components/ui/select/SelectContent.vue +51 -0
- package/template/src/components/ui/select/SelectGroup.vue +15 -0
- package/template/src/components/ui/select/SelectItem.vue +44 -0
- package/template/src/components/ui/select/SelectItemText.vue +15 -0
- package/template/src/components/ui/select/SelectLabel.vue +17 -0
- package/template/src/components/ui/select/SelectScrollDownButton.vue +26 -0
- package/template/src/components/ui/select/SelectScrollUpButton.vue +26 -0
- package/template/src/components/ui/select/SelectSeparator.vue +19 -0
- package/template/src/components/ui/select/SelectTrigger.vue +33 -0
- package/template/src/components/ui/select/SelectValue.vue +15 -0
- package/template/src/components/ui/select/index.ts +11 -0
- package/template/src/components/ui/separator/Separator.vue +29 -0
- package/template/src/components/ui/separator/index.ts +1 -0
- package/template/src/components/ui/sheet/Sheet.vue +19 -0
- package/template/src/components/ui/sheet/SheetClose.vue +15 -0
- package/template/src/components/ui/sheet/SheetContent.vue +62 -0
- package/template/src/components/ui/sheet/SheetDescription.vue +21 -0
- package/template/src/components/ui/sheet/SheetFooter.vue +16 -0
- package/template/src/components/ui/sheet/SheetHeader.vue +15 -0
- package/template/src/components/ui/sheet/SheetOverlay.vue +21 -0
- package/template/src/components/ui/sheet/SheetTitle.vue +21 -0
- package/template/src/components/ui/sheet/SheetTrigger.vue +15 -0
- package/template/src/components/ui/sheet/index.ts +8 -0
- package/template/src/components/ui/sidebar/Sidebar.vue +113 -0
- package/template/src/components/ui/sidebar/SidebarContent.vue +18 -0
- package/template/src/components/ui/sidebar/SidebarControl.vue +53 -0
- package/template/src/components/ui/sidebar/SidebarFooter.vue +18 -0
- package/template/src/components/ui/sidebar/SidebarGroup.vue +18 -0
- package/template/src/components/ui/sidebar/SidebarGroupAction.vue +27 -0
- package/template/src/components/ui/sidebar/SidebarGroupContent.vue +18 -0
- package/template/src/components/ui/sidebar/SidebarGroupLabel.vue +25 -0
- package/template/src/components/ui/sidebar/SidebarHeader.vue +18 -0
- package/template/src/components/ui/sidebar/SidebarInput.vue +22 -0
- package/template/src/components/ui/sidebar/SidebarInset.vue +21 -0
- package/template/src/components/ui/sidebar/SidebarMenu.vue +18 -0
- package/template/src/components/ui/sidebar/SidebarMenuAction.vue +35 -0
- package/template/src/components/ui/sidebar/SidebarMenuBadge.vue +26 -0
- package/template/src/components/ui/sidebar/SidebarMenuButton.vue +48 -0
- package/template/src/components/ui/sidebar/SidebarMenuButtonChild.vue +36 -0
- package/template/src/components/ui/sidebar/SidebarMenuItem.vue +18 -0
- package/template/src/components/ui/sidebar/SidebarMenuSkeleton.vue +35 -0
- package/template/src/components/ui/sidebar/SidebarMenuSub.vue +22 -0
- package/template/src/components/ui/sidebar/SidebarMenuSubButton.vue +36 -0
- package/template/src/components/ui/sidebar/SidebarMenuSubItem.vue +18 -0
- package/template/src/components/ui/sidebar/SidebarProvider.vue +136 -0
- package/template/src/components/ui/sidebar/SidebarRail.vue +33 -0
- package/template/src/components/ui/sidebar/SidebarSeparator.vue +19 -0
- package/template/src/components/ui/sidebar/SidebarTrigger.vue +27 -0
- package/template/src/components/ui/sidebar/index.ts +61 -0
- package/template/src/components/ui/sidebar/utils.ts +27 -0
- package/template/src/components/ui/skeleton/Skeleton.vue +17 -0
- package/template/src/components/ui/skeleton/index.ts +1 -0
- package/template/src/components/ui/slider/Slider.vue +42 -0
- package/template/src/components/ui/slider/index.ts +1 -0
- package/template/src/components/ui/sonner/Sonner.vue +42 -0
- package/template/src/components/ui/sonner/index.ts +2 -0
- package/template/src/components/ui/stepper/Stepper.vue +102 -0
- package/template/src/components/ui/stepper/index.ts +1 -0
- package/template/src/components/ui/switch/Switch.vue +38 -0
- package/template/src/components/ui/switch/index.ts +1 -0
- package/template/src/components/ui/table/Table.vue +16 -0
- package/template/src/components/ui/table/TableBody.vue +17 -0
- package/template/src/components/ui/table/TableCaption.vue +17 -0
- package/template/src/components/ui/table/TableCell.vue +22 -0
- package/template/src/components/ui/table/TableEmpty.vue +34 -0
- package/template/src/components/ui/table/TableFooter.vue +17 -0
- package/template/src/components/ui/table/TableHead.vue +17 -0
- package/template/src/components/ui/table/TableHeader.vue +17 -0
- package/template/src/components/ui/table/TableRow.vue +17 -0
- package/template/src/components/ui/table/index.ts +9 -0
- package/template/src/components/ui/table/utils.ts +10 -0
- package/template/src/components/ui/tabs/Tabs.vue +24 -0
- package/template/src/components/ui/tabs/TabsContent.vue +21 -0
- package/template/src/components/ui/tabs/TabsList.vue +24 -0
- package/template/src/components/ui/tabs/TabsTrigger.vue +26 -0
- package/template/src/components/ui/tabs/index.ts +4 -0
- package/template/src/components/ui/textarea/Textarea.vue +28 -0
- package/template/src/components/ui/textarea/index.ts +1 -0
- package/template/src/components/ui/timeline/Timeline.vue +73 -0
- package/template/src/components/ui/timeline/index.ts +1 -0
- package/template/src/components/ui/toggle/Toggle.vue +35 -0
- package/template/src/components/ui/toggle/index.ts +28 -0
- package/template/src/components/ui/toggle-group/ToggleGroup.vue +41 -0
- package/template/src/components/ui/toggle-group/ToggleGroupItem.vue +43 -0
- package/template/src/components/ui/toggle-group/index.ts +12 -0
- package/template/src/components/ui/tooltip/Tooltip.vue +19 -0
- package/template/src/components/ui/tooltip/TooltipContent.vue +34 -0
- package/template/src/components/ui/tooltip/TooltipProvider.vue +14 -0
- package/template/src/components/ui/tooltip/TooltipTrigger.vue +15 -0
- package/template/src/components/ui/tooltip/index.ts +4 -0
- package/template/src/components.d.ts +228 -0
- package/template/src/composables/useFormatter.ts +105 -0
- package/template/src/composables/useThemeConfig.ts +252 -0
- package/template/src/layouts/AdminLayout.vue +55 -0
- package/template/src/layouts/AuthLayout.vue +42 -0
- package/template/src/lib/axios.spec.ts +46 -0
- package/template/src/lib/axios.ts +243 -0
- package/template/src/lib/formatters.spec.ts +137 -0
- package/template/src/lib/formatters.ts +332 -0
- package/template/src/lib/loading-bar.ts +103 -0
- package/template/src/lib/utils.spec.ts +26 -0
- package/template/src/lib/utils.ts +7 -0
- package/template/src/lib/validation.spec.ts +78 -0
- package/template/src/main.ts +13 -0
- package/template/src/router/index.ts +147 -0
- package/template/src/services/auth.service.spec.ts +93 -0
- package/template/src/services/auth.service.ts +243 -0
- package/template/src/services/index.ts +3 -0
- package/template/src/services/types.ts +28 -0
- package/template/src/services/user.service.spec.ts +91 -0
- package/template/src/services/user.service.ts +283 -0
- package/template/src/stores/auth.spec.ts +52 -0
- package/template/src/stores/auth.ts +130 -0
- package/template/src/style.css +587 -0
- package/template/src/views/_starter/BlankView.vue +47 -0
- package/template/src/views/auth/ConfirmEmailView.vue +209 -0
- package/template/src/views/auth/ForgotPasswordView.vue +151 -0
- package/template/src/views/auth/LockScreenView.vue +175 -0
- package/template/src/views/auth/LoginView.vue +54 -0
- package/template/src/views/auth/RegisterView.vue +265 -0
- package/template/src/views/auth/ResetPasswordView.vue +260 -0
- package/template/src/views/auth/VerifyOtpView.vue +261 -0
- package/template/src/views/dashboard/IndexView.vue +159 -0
- package/template/src/views/errors/ComingSoonView.vue +241 -0
- package/template/src/views/errors/MaintenanceView.vue +252 -0
- package/template/src/views/errors/NotFoundView.vue +81 -0
- package/template/src/views/errors/ServerErrorView.vue +101 -0
- package/template/src/views/errors/UnauthorizedView.vue +64 -0
- package/template/tsconfig.app.json +27 -0
- package/template/tsconfig.json +19 -0
- package/template/tsconfig.node.json +23 -0
- package/template/vite.config.ts +46 -0
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { ref, onMounted, onUnmounted } from 'vue'
|
|
3
|
+
import { useRouter } from 'vue-router'
|
|
4
|
+
import {
|
|
5
|
+
Wrench,
|
|
6
|
+
RefreshCw,
|
|
7
|
+
Home,
|
|
8
|
+
CheckCircle2,
|
|
9
|
+
Clock,
|
|
10
|
+
Bell,
|
|
11
|
+
Mail,
|
|
12
|
+
Activity,
|
|
13
|
+
} from '@lucide/vue'
|
|
14
|
+
import { Card, CardContent } from '@/components/ui/card'
|
|
15
|
+
import { Button } from '@/components/ui/button'
|
|
16
|
+
import { Input } from '@/components/ui/input'
|
|
17
|
+
import { Progress } from '@/components/ui/progress'
|
|
18
|
+
import AppLogo from '@/components/AppLogo.vue'
|
|
19
|
+
import { toast } from '@/components/ui/sonner'
|
|
20
|
+
|
|
21
|
+
const router = useRouter()
|
|
22
|
+
|
|
23
|
+
// Countdown Timer State (Target: 2 hours 45 minutes from now)
|
|
24
|
+
const hours = ref(2)
|
|
25
|
+
const minutes = ref(45)
|
|
26
|
+
const seconds = ref(18)
|
|
27
|
+
let timerInterval: ReturnType<typeof setInterval> | null = null
|
|
28
|
+
|
|
29
|
+
// Subscription State
|
|
30
|
+
const notifyEmail = ref('')
|
|
31
|
+
const isSubscribed = ref(false)
|
|
32
|
+
const isSubmitting = ref(false)
|
|
33
|
+
const isRefreshing = ref(false)
|
|
34
|
+
|
|
35
|
+
const maintenanceTasks = [
|
|
36
|
+
{ id: 1, title: 'Database Backup & Snapshot', status: 'completed', desc: 'Immutable backup archive verified' },
|
|
37
|
+
{ id: 2, title: 'Core API Cluster Upgrade', status: 'completed', desc: 'Deploying v2.4 kernel patches' },
|
|
38
|
+
{ id: 3, title: 'Search Index Rebuilding', status: 'in-progress', progress: 78, desc: 'Vector indexes processing' },
|
|
39
|
+
{ id: 4, title: 'Edge CDN Cache Warming', status: 'pending', desc: 'Global edge node cache purge' },
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
onMounted(() => {
|
|
43
|
+
timerInterval = setInterval(() => {
|
|
44
|
+
if (seconds.value > 0) {
|
|
45
|
+
seconds.value--
|
|
46
|
+
} else {
|
|
47
|
+
if (minutes.value > 0) {
|
|
48
|
+
minutes.value--
|
|
49
|
+
seconds.value = 59
|
|
50
|
+
} else {
|
|
51
|
+
if (hours.value > 0) {
|
|
52
|
+
hours.value--
|
|
53
|
+
minutes.value = 59
|
|
54
|
+
seconds.value = 59
|
|
55
|
+
} else {
|
|
56
|
+
if (timerInterval) clearInterval(timerInterval)
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}, 1000)
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
onUnmounted(() => {
|
|
64
|
+
if (timerInterval) clearInterval(timerInterval)
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
async function handleSubscribe() {
|
|
68
|
+
if (!notifyEmail.value || !notifyEmail.value.includes('@')) {
|
|
69
|
+
toast.error('Please enter a valid email address.')
|
|
70
|
+
return
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
isSubmitting.value = true
|
|
74
|
+
setTimeout(() => {
|
|
75
|
+
isSubmitting.value = false
|
|
76
|
+
isSubscribed.value = true
|
|
77
|
+
toast.success(`We will notify ${notifyEmail.value} as soon as systems are live!`)
|
|
78
|
+
}, 600)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function handleRefresh() {
|
|
82
|
+
isRefreshing.value = true
|
|
83
|
+
setTimeout(() => {
|
|
84
|
+
isRefreshing.value = false
|
|
85
|
+
toast.info('System status refreshed: Maintenance is 78% complete.')
|
|
86
|
+
}, 750)
|
|
87
|
+
}
|
|
88
|
+
</script>
|
|
89
|
+
|
|
90
|
+
<template>
|
|
91
|
+
<div class="flex min-h-[calc(100vh-6rem)] flex-col items-center justify-center p-4 sm:p-8">
|
|
92
|
+
<div class="w-full max-w-2xl space-y-8 text-center">
|
|
93
|
+
<!-- Brand Logo -->
|
|
94
|
+
<div class="flex justify-center pb-2">
|
|
95
|
+
<router-link to="/" class="inline-flex items-center gap-2.5 hover:opacity-90 transition-opacity">
|
|
96
|
+
<AppLogo class="h-10 w-10" withBackground withText textClass="text-2xl font-bold tracking-tight" />
|
|
97
|
+
</router-link>
|
|
98
|
+
</div>
|
|
99
|
+
|
|
100
|
+
<!-- Icon & Status Badge -->
|
|
101
|
+
<div class="space-y-3">
|
|
102
|
+
<div class="relative mx-auto flex h-20 w-20 items-center justify-center rounded-3xl bg-amber-500/10 text-amber-600 dark:text-amber-400 ring-8 ring-amber-500/5 shadow-xs">
|
|
103
|
+
<Wrench class="h-10 w-10 animate-spin-slow" />
|
|
104
|
+
<span class="absolute -bottom-2 rounded-full bg-amber-500 text-white px-2.5 py-0.5 text-[10px] font-bold tracking-wider uppercase shadow-xs">
|
|
105
|
+
Maintenance
|
|
106
|
+
</span>
|
|
107
|
+
</div>
|
|
108
|
+
|
|
109
|
+
<div class="space-y-2 pt-2">
|
|
110
|
+
<h1 class="text-3xl sm:text-4xl font-extrabold tracking-tight text-foreground">
|
|
111
|
+
System Maintenance in Progress
|
|
112
|
+
</h1>
|
|
113
|
+
<p class="text-sm text-muted-foreground leading-relaxed max-w-lg mx-auto">
|
|
114
|
+
We are performing scheduled core infrastructure updates and database optimizations. All services are expected to resume shortly.
|
|
115
|
+
</p>
|
|
116
|
+
</div>
|
|
117
|
+
</div>
|
|
118
|
+
|
|
119
|
+
<!-- Live Countdown Card -->
|
|
120
|
+
<Card class="border-border/80 bg-card/80 backdrop-blur-xs shadow-md">
|
|
121
|
+
<CardContent class="p-6">
|
|
122
|
+
<p class="text-xs font-semibold uppercase tracking-widest text-muted-foreground mb-4">
|
|
123
|
+
Estimated Completion In
|
|
124
|
+
</p>
|
|
125
|
+
|
|
126
|
+
<div class="grid grid-cols-3 gap-3 sm:gap-6 max-w-sm mx-auto">
|
|
127
|
+
<!-- Hours -->
|
|
128
|
+
<div class="flex flex-col items-center p-3 rounded-xl bg-muted/40 border border-border/60">
|
|
129
|
+
<span class="text-3xl sm:text-4xl font-mono font-extrabold text-foreground">
|
|
130
|
+
{{ String(hours).padStart(2, '0') }}
|
|
131
|
+
</span>
|
|
132
|
+
<span class="text-[11px] font-medium text-muted-foreground uppercase mt-1">Hours</span>
|
|
133
|
+
</div>
|
|
134
|
+
|
|
135
|
+
<!-- Minutes -->
|
|
136
|
+
<div class="flex flex-col items-center p-3 rounded-xl bg-muted/40 border border-border/60">
|
|
137
|
+
<span class="text-3xl sm:text-4xl font-mono font-extrabold text-foreground">
|
|
138
|
+
{{ String(minutes).padStart(2, '0') }}
|
|
139
|
+
</span>
|
|
140
|
+
<span class="text-[11px] font-medium text-muted-foreground uppercase mt-1">Minutes</span>
|
|
141
|
+
</div>
|
|
142
|
+
|
|
143
|
+
<!-- Seconds -->
|
|
144
|
+
<div class="flex flex-col items-center p-3 rounded-xl bg-muted/40 border border-border/60">
|
|
145
|
+
<span class="text-3xl sm:text-4xl font-mono font-extrabold text-primary">
|
|
146
|
+
{{ String(seconds).padStart(2, '0') }}
|
|
147
|
+
</span>
|
|
148
|
+
<span class="text-[11px] font-medium text-muted-foreground uppercase mt-1">Seconds</span>
|
|
149
|
+
</div>
|
|
150
|
+
</div>
|
|
151
|
+
|
|
152
|
+
<!-- Live Progress Tracker -->
|
|
153
|
+
<div class="mt-8 pt-6 border-t border-border/60 text-left space-y-3">
|
|
154
|
+
<div class="flex items-center justify-between text-xs">
|
|
155
|
+
<span class="font-semibold text-foreground flex items-center gap-1.5">
|
|
156
|
+
<Activity class="h-3.5 w-3.5 text-primary animate-pulse" />
|
|
157
|
+
<span>Migration Pipeline Progress</span>
|
|
158
|
+
</span>
|
|
159
|
+
<span class="font-mono font-bold text-primary">78%</span>
|
|
160
|
+
</div>
|
|
161
|
+
<Progress :model-value="78" class="h-2" />
|
|
162
|
+
|
|
163
|
+
<!-- Subtask checklist -->
|
|
164
|
+
<div class="grid grid-cols-1 sm:grid-cols-2 gap-2.5 pt-3">
|
|
165
|
+
<div
|
|
166
|
+
v-for="task in maintenanceTasks"
|
|
167
|
+
:key="task.id"
|
|
168
|
+
class="flex items-start gap-2.5 p-2.5 rounded-lg border border-border/50 bg-background/50 text-xs"
|
|
169
|
+
>
|
|
170
|
+
<CheckCircle2 v-if="task.status === 'completed'" class="h-4 w-4 text-emerald-500 shrink-0 mt-0.5" />
|
|
171
|
+
<RefreshCw v-else-if="task.status === 'in-progress'" class="h-4 w-4 text-primary animate-spin shrink-0 mt-0.5" />
|
|
172
|
+
<Clock v-else class="h-4 w-4 text-muted-foreground/50 shrink-0 mt-0.5" />
|
|
173
|
+
|
|
174
|
+
<div class="space-y-0.5">
|
|
175
|
+
<p class="font-medium text-foreground text-xs leading-none">{{ task.title }}</p>
|
|
176
|
+
<p class="text-[11px] text-muted-foreground">{{ task.desc }}</p>
|
|
177
|
+
</div>
|
|
178
|
+
</div>
|
|
179
|
+
</div>
|
|
180
|
+
</div>
|
|
181
|
+
</CardContent>
|
|
182
|
+
</Card>
|
|
183
|
+
|
|
184
|
+
<!-- Email Notification Capture -->
|
|
185
|
+
<Card class="border-border/80 bg-card/60 shadow-xs max-w-lg mx-auto">
|
|
186
|
+
<CardContent class="p-5 text-center">
|
|
187
|
+
<div v-if="!isSubscribed" class="space-y-3">
|
|
188
|
+
<div class="flex items-center justify-center gap-2 text-xs font-semibold text-foreground">
|
|
189
|
+
<Bell class="h-4 w-4 text-primary" />
|
|
190
|
+
<span>Get notified immediately when we're back online</span>
|
|
191
|
+
</div>
|
|
192
|
+
|
|
193
|
+
<form @submit.prevent="handleSubscribe" class="flex flex-col sm:flex-row gap-2">
|
|
194
|
+
<Input
|
|
195
|
+
v-model="notifyEmail"
|
|
196
|
+
type="email"
|
|
197
|
+
placeholder="Enter your work email"
|
|
198
|
+
class="h-9 text-xs bg-background"
|
|
199
|
+
required
|
|
200
|
+
/>
|
|
201
|
+
<Button type="submit" size="sm" class="h-9 text-xs gap-1.5 shrink-0" :disabled="isSubmitting">
|
|
202
|
+
<Mail class="h-3.5 w-3.5" />
|
|
203
|
+
<span>{{ isSubmitting ? 'Subscribing...' : 'Notify Me' }}</span>
|
|
204
|
+
</Button>
|
|
205
|
+
</form>
|
|
206
|
+
</div>
|
|
207
|
+
|
|
208
|
+
<div v-else class="flex items-center justify-center gap-2 text-xs text-emerald-600 dark:text-emerald-400 font-medium py-1">
|
|
209
|
+
<CheckCircle2 class="h-4 w-4" />
|
|
210
|
+
<span>You're subscribed! We'll email you the moment systems are operational.</span>
|
|
211
|
+
</div>
|
|
212
|
+
</CardContent>
|
|
213
|
+
</Card>
|
|
214
|
+
|
|
215
|
+
<!-- Action Buttons -->
|
|
216
|
+
<div class="flex flex-wrap items-center justify-center gap-3 pt-2">
|
|
217
|
+
<Button
|
|
218
|
+
variant="outline"
|
|
219
|
+
class="gap-2 cursor-pointer text-xs h-9"
|
|
220
|
+
:disabled="isRefreshing"
|
|
221
|
+
@click="handleRefresh"
|
|
222
|
+
>
|
|
223
|
+
<RefreshCw class="h-3.5 w-3.5" :class="{ 'animate-spin': isRefreshing }" />
|
|
224
|
+
<span>{{ isRefreshing ? 'Checking...' : 'Refresh Status' }}</span>
|
|
225
|
+
</Button>
|
|
226
|
+
|
|
227
|
+
<Button
|
|
228
|
+
variant="default"
|
|
229
|
+
class="gap-2 cursor-pointer text-xs h-9"
|
|
230
|
+
@click="router.push('/')"
|
|
231
|
+
>
|
|
232
|
+
<Home class="h-3.5 w-3.5" />
|
|
233
|
+
<span>Dashboard Home</span>
|
|
234
|
+
</Button>
|
|
235
|
+
</div>
|
|
236
|
+
</div>
|
|
237
|
+
</div>
|
|
238
|
+
</template>
|
|
239
|
+
|
|
240
|
+
<style scoped>
|
|
241
|
+
@keyframes spinSlow {
|
|
242
|
+
from {
|
|
243
|
+
transform: rotate(0deg);
|
|
244
|
+
}
|
|
245
|
+
to {
|
|
246
|
+
transform: rotate(360deg);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
.animate-spin-slow {
|
|
250
|
+
animation: spinSlow 12s linear infinite;
|
|
251
|
+
}
|
|
252
|
+
</style>
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import {
|
|
3
|
+
FileQuestion,
|
|
4
|
+
ArrowLeft,
|
|
5
|
+
Home,
|
|
6
|
+
Layers,
|
|
7
|
+
Settings,
|
|
8
|
+
} from '@lucide/vue'
|
|
9
|
+
|
|
10
|
+
const router = useRouter()
|
|
11
|
+
|
|
12
|
+
const goBack = () => {
|
|
13
|
+
if (window.history.length > 1) {
|
|
14
|
+
router.back()
|
|
15
|
+
} else {
|
|
16
|
+
router.push('/')
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const quickLinks = [
|
|
21
|
+
{ label: 'Overview Dashboard', href: '/', icon: Home },
|
|
22
|
+
{ label: 'UI Components', href: '/components/buttons', icon: Layers },
|
|
23
|
+
{ label: 'System Settings', href: '/settings', icon: Settings },
|
|
24
|
+
]
|
|
25
|
+
</script>
|
|
26
|
+
|
|
27
|
+
<template>
|
|
28
|
+
<div class="flex min-h-[calc(100vh-8rem)] flex-col items-center justify-center p-4 text-center sm:p-8">
|
|
29
|
+
<div class="w-full max-w-lg space-y-6">
|
|
30
|
+
<!-- Icon & Status Badge -->
|
|
31
|
+
<div class="relative mx-auto flex h-24 w-24 items-center justify-center rounded-3xl bg-primary/10 text-primary ring-8 ring-primary/5">
|
|
32
|
+
<FileQuestion class="h-12 w-12" />
|
|
33
|
+
<span class="absolute -bottom-2.5 rounded-full bg-primary px-3 py-0.5 text-[11px] font-bold tracking-wider text-primary-foreground uppercase shadow-xs">
|
|
34
|
+
404 Error
|
|
35
|
+
</span>
|
|
36
|
+
</div>
|
|
37
|
+
|
|
38
|
+
<!-- Main Copy -->
|
|
39
|
+
<div class="space-y-2">
|
|
40
|
+
<h1 class="text-3xl font-extrabold tracking-tight sm:text-4xl text-foreground">
|
|
41
|
+
Page Not Found
|
|
42
|
+
</h1>
|
|
43
|
+
<p class="text-sm text-muted-foreground leading-relaxed max-w-md mx-auto">
|
|
44
|
+
The requested URL does not exist or has been moved. Check the URL spelling or choose one of the quick shortcuts below.
|
|
45
|
+
</p>
|
|
46
|
+
</div>
|
|
47
|
+
|
|
48
|
+
<!-- Quick Action Buttons -->
|
|
49
|
+
<div class="flex flex-col gap-2.5 sm:flex-row sm:justify-center pt-2">
|
|
50
|
+
<Button variant="outline" class="gap-2 cursor-pointer" @click="goBack">
|
|
51
|
+
<ArrowLeft class="h-4 w-4" />
|
|
52
|
+
Go Back
|
|
53
|
+
</Button>
|
|
54
|
+
<Button as-child class="gap-2 cursor-pointer">
|
|
55
|
+
<router-link to="/">
|
|
56
|
+
<Home class="h-4 w-4" />
|
|
57
|
+
Back to Dashboard
|
|
58
|
+
</router-link>
|
|
59
|
+
</Button>
|
|
60
|
+
</div>
|
|
61
|
+
|
|
62
|
+
<!-- Quick Helpful Links -->
|
|
63
|
+
<div class="pt-6 border-t border-border/60">
|
|
64
|
+
<p class="text-xs font-semibold text-muted-foreground uppercase tracking-wider mb-3">
|
|
65
|
+
Popular Destinations
|
|
66
|
+
</p>
|
|
67
|
+
<div class="grid grid-cols-1 sm:grid-cols-3 gap-2">
|
|
68
|
+
<router-link
|
|
69
|
+
v-for="link in quickLinks"
|
|
70
|
+
:key="link.href"
|
|
71
|
+
:to="link.href"
|
|
72
|
+
class="flex items-center justify-center gap-2 p-2.5 rounded-lg border border-border bg-card hover:border-primary/50 hover:bg-primary/5 text-xs font-medium text-foreground transition-all shadow-2xs"
|
|
73
|
+
>
|
|
74
|
+
<component :is="link.icon" class="h-3.5 w-3.5 text-muted-foreground" />
|
|
75
|
+
<span>{{ link.label }}</span>
|
|
76
|
+
</router-link>
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
</div>
|
|
81
|
+
</template>
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import {
|
|
3
|
+
ServerCrash,
|
|
4
|
+
RefreshCw,
|
|
5
|
+
Home,
|
|
6
|
+
Activity,
|
|
7
|
+
Copy,
|
|
8
|
+
Check,
|
|
9
|
+
} from '@lucide/vue'
|
|
10
|
+
|
|
11
|
+
const isReloading = ref(false)
|
|
12
|
+
const copiedTrace = ref(false)
|
|
13
|
+
const traceId = ref('req_8f19da240b91_fra')
|
|
14
|
+
|
|
15
|
+
const handleReload = () => {
|
|
16
|
+
isReloading.value = true
|
|
17
|
+
setTimeout(() => {
|
|
18
|
+
window.location.reload()
|
|
19
|
+
}, 600)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const copyTraceId = () => {
|
|
23
|
+
navigator.clipboard.writeText(traceId.value)
|
|
24
|
+
copiedTrace.value = true
|
|
25
|
+
setTimeout(() => {
|
|
26
|
+
copiedTrace.value = false
|
|
27
|
+
}, 2000)
|
|
28
|
+
}
|
|
29
|
+
</script>
|
|
30
|
+
|
|
31
|
+
<template>
|
|
32
|
+
<div class="flex min-h-[calc(100vh-8rem)] flex-col items-center justify-center p-4 text-center sm:p-8">
|
|
33
|
+
<div class="w-full max-w-lg space-y-6">
|
|
34
|
+
<!-- Icon & Status Badge -->
|
|
35
|
+
<div class="relative mx-auto flex h-24 w-24 items-center justify-center rounded-3xl bg-amber-500/10 text-amber-600 dark:text-amber-400 ring-8 ring-amber-500/5">
|
|
36
|
+
<ServerCrash class="h-12 w-12" />
|
|
37
|
+
<span class="absolute -bottom-2.5 rounded-full bg-amber-600 text-white px-3 py-0.5 text-[11px] font-bold tracking-wider uppercase shadow-xs">
|
|
38
|
+
500 Error
|
|
39
|
+
</span>
|
|
40
|
+
</div>
|
|
41
|
+
|
|
42
|
+
<!-- Main Copy -->
|
|
43
|
+
<div class="space-y-2">
|
|
44
|
+
<h1 class="text-3xl font-extrabold tracking-tight sm:text-4xl text-foreground">
|
|
45
|
+
Internal Server Error
|
|
46
|
+
</h1>
|
|
47
|
+
<p class="text-sm text-muted-foreground leading-relaxed max-w-md mx-auto">
|
|
48
|
+
Our gateway encountered an unexpected exception while processing this request. Our engineering team has been automatically notified.
|
|
49
|
+
</p>
|
|
50
|
+
</div>
|
|
51
|
+
|
|
52
|
+
<!-- Diagnostic Trace ID Card -->
|
|
53
|
+
<div class="rounded-xl border border-border bg-card p-3 max-w-sm mx-auto flex items-center justify-between text-xs font-mono shadow-2xs">
|
|
54
|
+
<div class="flex items-center gap-2 text-muted-foreground">
|
|
55
|
+
<span class="h-2 w-2 rounded-full bg-amber-500 animate-pulse"></span>
|
|
56
|
+
<span>Trace:</span>
|
|
57
|
+
<span class="text-foreground font-semibold">{{ traceId }}</span>
|
|
58
|
+
</div>
|
|
59
|
+
<button
|
|
60
|
+
type="button"
|
|
61
|
+
@click="copyTraceId"
|
|
62
|
+
class="text-muted-foreground hover:text-foreground cursor-pointer transition-colors p-1"
|
|
63
|
+
title="Copy Trace ID"
|
|
64
|
+
>
|
|
65
|
+
<Check v-if="copiedTrace" class="h-3.5 w-3.5 text-emerald-500" />
|
|
66
|
+
<Copy v-else class="h-3.5 w-3.5" />
|
|
67
|
+
</button>
|
|
68
|
+
</div>
|
|
69
|
+
|
|
70
|
+
<!-- Quick Action Buttons -->
|
|
71
|
+
<div class="flex flex-col gap-2.5 sm:flex-row sm:justify-center pt-2">
|
|
72
|
+
<Button
|
|
73
|
+
variant="outline"
|
|
74
|
+
class="gap-2 cursor-pointer"
|
|
75
|
+
:disabled="isReloading"
|
|
76
|
+
@click="handleReload"
|
|
77
|
+
>
|
|
78
|
+
<RefreshCw class="h-4 w-4" :class="{ 'animate-spin': isReloading }" />
|
|
79
|
+
{{ isReloading ? 'Reloading...' : 'Try Again' }}
|
|
80
|
+
</Button>
|
|
81
|
+
<Button as-child class="gap-2 cursor-pointer">
|
|
82
|
+
<router-link to="/">
|
|
83
|
+
<Home class="h-4 w-4" />
|
|
84
|
+
Back to Dashboard
|
|
85
|
+
</router-link>
|
|
86
|
+
</Button>
|
|
87
|
+
</div>
|
|
88
|
+
|
|
89
|
+
<!-- System Health Hint -->
|
|
90
|
+
<div class="pt-6 border-t border-border/60">
|
|
91
|
+
<div class="inline-flex items-center gap-2 text-xs text-muted-foreground">
|
|
92
|
+
<Activity class="h-3.5 w-3.5 text-emerald-500" />
|
|
93
|
+
<span>All global systems are operating nominally.</span>
|
|
94
|
+
<router-link to="/components/charts" class="font-medium text-primary hover:underline transition-colors">
|
|
95
|
+
View Status
|
|
96
|
+
</router-link>
|
|
97
|
+
</div>
|
|
98
|
+
</div>
|
|
99
|
+
</div>
|
|
100
|
+
</div>
|
|
101
|
+
</template>
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { useRouter } from 'vue-router'
|
|
3
|
+
import { Button } from '@/components/ui/button'
|
|
4
|
+
import { ShieldAlert, ArrowLeft, Home, HelpCircle } from '@lucide/vue'
|
|
5
|
+
|
|
6
|
+
const router = useRouter()
|
|
7
|
+
|
|
8
|
+
const goBack = () => {
|
|
9
|
+
if (window.history.length > 1) {
|
|
10
|
+
router.back()
|
|
11
|
+
} else {
|
|
12
|
+
router.push('/')
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
</script>
|
|
16
|
+
|
|
17
|
+
<template>
|
|
18
|
+
<div class="flex min-h-[calc(100vh-4rem)] flex-col items-center justify-center p-4 text-center sm:p-8">
|
|
19
|
+
<div class="w-full max-w-md space-y-6">
|
|
20
|
+
<!-- Icon & Status Badge -->
|
|
21
|
+
<div class="relative mx-auto flex h-24 w-24 items-center justify-center rounded-3xl bg-destructive/10 text-destructive ring-8 ring-destructive/5">
|
|
22
|
+
<ShieldAlert class="h-12 w-12" />
|
|
23
|
+
<span class="absolute -bottom-2.5 rounded-full bg-destructive px-2.5 py-0.5 text-[11px] font-bold tracking-wider text-destructive-foreground uppercase shadow-xs">
|
|
24
|
+
403 Error
|
|
25
|
+
</span>
|
|
26
|
+
</div>
|
|
27
|
+
|
|
28
|
+
<!-- Main Copy -->
|
|
29
|
+
<div class="space-y-2">
|
|
30
|
+
<h1 class="text-3xl font-extrabold tracking-tight sm:text-4xl text-foreground">
|
|
31
|
+
Access Denied
|
|
32
|
+
</h1>
|
|
33
|
+
<p class="text-sm text-muted-foreground leading-relaxed">
|
|
34
|
+
You don't have permission to access this page or resource. If you believe this is an error, please contact your administrator.
|
|
35
|
+
</p>
|
|
36
|
+
</div>
|
|
37
|
+
|
|
38
|
+
<!-- Quick Action Buttons -->
|
|
39
|
+
<div class="flex flex-col gap-2.5 sm:flex-row sm:justify-center pt-2">
|
|
40
|
+
<Button variant="outline" class="gap-2 cursor-pointer" @click="goBack">
|
|
41
|
+
<ArrowLeft class="h-4 w-4" />
|
|
42
|
+
Go Back
|
|
43
|
+
</Button>
|
|
44
|
+
<Button as-child class="gap-2 cursor-pointer">
|
|
45
|
+
<router-link to="/">
|
|
46
|
+
<Home class="h-4 w-4" />
|
|
47
|
+
Back to Dashboard
|
|
48
|
+
</router-link>
|
|
49
|
+
</Button>
|
|
50
|
+
</div>
|
|
51
|
+
|
|
52
|
+
<!-- Help / Support Hint -->
|
|
53
|
+
<div class="pt-6 border-t border-border/60">
|
|
54
|
+
<div class="inline-flex items-center gap-1.5 text-xs text-muted-foreground">
|
|
55
|
+
<HelpCircle class="h-3.5 w-3.5" />
|
|
56
|
+
<span>Need elevated permissions?</span>
|
|
57
|
+
<router-link to="/settings" class="font-medium text-primary hover:underline transition-colors">
|
|
58
|
+
Contact Support
|
|
59
|
+
</router-link>
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
</div>
|
|
63
|
+
</div>
|
|
64
|
+
</template>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "@vue/tsconfig/tsconfig.dom.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
|
5
|
+
"types": [
|
|
6
|
+
"vite/client",
|
|
7
|
+
"vitest/globals"
|
|
8
|
+
],
|
|
9
|
+
"allowArbitraryExtensions": true,
|
|
10
|
+
/* Linting */
|
|
11
|
+
"noUnusedLocals": true,
|
|
12
|
+
"noUnusedParameters": true,
|
|
13
|
+
"erasableSyntaxOnly": true,
|
|
14
|
+
"noFallthroughCasesInSwitch": true,
|
|
15
|
+
"paths": {
|
|
16
|
+
"@/*": [
|
|
17
|
+
"./src/*"
|
|
18
|
+
]
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"include": [
|
|
22
|
+
"src/**/*.ts",
|
|
23
|
+
"src/**/*.d.ts",
|
|
24
|
+
"src/**/*.tsx",
|
|
25
|
+
"src/**/*.vue"
|
|
26
|
+
]
|
|
27
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
|
4
|
+
"target": "es2023",
|
|
5
|
+
"lib": ["ES2023"],
|
|
6
|
+
"types": ["node"],
|
|
7
|
+
"skipLibCheck": true,
|
|
8
|
+
|
|
9
|
+
/* Bundler mode */
|
|
10
|
+
"module": "nodenext",
|
|
11
|
+
"allowImportingTsExtensions": true,
|
|
12
|
+
"verbatimModuleSyntax": true,
|
|
13
|
+
"moduleDetection": "force",
|
|
14
|
+
"noEmit": true,
|
|
15
|
+
|
|
16
|
+
/* Linting */
|
|
17
|
+
"noUnusedLocals": true,
|
|
18
|
+
"noUnusedParameters": true,
|
|
19
|
+
"erasableSyntaxOnly": true,
|
|
20
|
+
"noFallthroughCasesInSwitch": true
|
|
21
|
+
},
|
|
22
|
+
"include": ["vite.config.ts"]
|
|
23
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/// <reference types="vitest/config" />
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
import { defineConfig } from 'vite'
|
|
4
|
+
import tailwindcss from '@tailwindcss/vite'
|
|
5
|
+
import vue from '@vitejs/plugin-vue'
|
|
6
|
+
import AutoImport from 'unplugin-auto-import/vite'
|
|
7
|
+
import Components from 'unplugin-vue-components/vite'
|
|
8
|
+
|
|
9
|
+
export default defineConfig({
|
|
10
|
+
test: {
|
|
11
|
+
environment: 'happy-dom',
|
|
12
|
+
globals: true,
|
|
13
|
+
},
|
|
14
|
+
plugins: [
|
|
15
|
+
vue(),
|
|
16
|
+
tailwindcss(),
|
|
17
|
+
AutoImport({
|
|
18
|
+
imports: [
|
|
19
|
+
'vue',
|
|
20
|
+
'vue-router',
|
|
21
|
+
'pinia',
|
|
22
|
+
'@vueuse/core',
|
|
23
|
+
],
|
|
24
|
+
dts: 'src/auto-imports.d.ts',
|
|
25
|
+
dirs: [
|
|
26
|
+
'src/lib',
|
|
27
|
+
'src/composables',
|
|
28
|
+
],
|
|
29
|
+
vueTemplate: true,
|
|
30
|
+
}),
|
|
31
|
+
Components({
|
|
32
|
+
dirs: ['src/components'],
|
|
33
|
+
extensions: ['vue'],
|
|
34
|
+
dts: 'src/components.d.ts',
|
|
35
|
+
deep: true,
|
|
36
|
+
}),
|
|
37
|
+
],
|
|
38
|
+
server: {
|
|
39
|
+
host: true,
|
|
40
|
+
},
|
|
41
|
+
resolve: {
|
|
42
|
+
alias: {
|
|
43
|
+
'@': path.resolve(import.meta.dirname, './src'),
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
})
|