create-nala 3.0.4 → 3.1.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/package.json +1 -1
- package/template/.agents/skills/nala-project/SKILL.md +217 -70
- package/template/AGENTS.md +1 -1
- package/template/CLAUDE.md +27 -0
- package/template/GEMINI.md +27 -0
- package/template/README.md +3 -1
- package/template/package.json +1 -1
- package/template/src/App.vue +1 -1
- package/template/src/assets/fonts/InterVariable.woff2 +0 -0
- package/template/src/assets/fonts/inter-v20-latin-300.woff2 +0 -0
- package/template/src/assets/fonts/inter-v20-latin-500.woff2 +0 -0
- package/template/src/assets/fonts/inter-v20-latin-600.woff2 +0 -0
- package/template/src/assets/fonts/inter-v20-latin-700.woff2 +0 -0
- package/template/src/assets/fonts/inter-v20-latin-regular.woff2 +0 -0
- package/template/src/assets/hero.png +0 -0
- package/template/src/assets/vite.svg +1 -0
- package/template/src/assets/vue.svg +1 -0
- package/template/src/components/ui/sonner/Sonner.vue +43 -13
- package/template/tsconfig.app.json +17 -11
package/package.json
CHANGED
|
@@ -1,14 +1,59 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: nala-project
|
|
3
3
|
description: >
|
|
4
|
-
Skill for the Nala Admin Dashboard Project -
|
|
5
|
-
Vite, TypeScript, Tailwind CSS v4, Reka UI (shadcn-vue), Pinia, and Axios.
|
|
6
|
-
Activates when building views, components, stores, API services, or scaffolding features.
|
|
4
|
+
Skill for the Nala Admin Dashboard Project - a starter SPA template scaffolded with `pnpm create nala`,
|
|
5
|
+
built with Vue 3, Vite, TypeScript, Tailwind CSS v4, Reka UI (shadcn-vue), Unovis charts, Pinia, and Axios.
|
|
6
|
+
Activates when building views, components, stores, API services, or scaffolding new features.
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
-
# Nala Project Skill (Starter Edition)
|
|
9
|
+
# Nala Project Skill (Starter Template Edition)
|
|
10
10
|
|
|
11
|
-
You are an expert frontend engineer building features for this **Nala Admin Dashboard** application
|
|
11
|
+
You are an expert frontend engineer building features for this **Nala Admin Dashboard** application — a clean, minimal starter template scaffolded via `pnpm create nala`. Follow these instructions strictly to ensure clean architecture, design consistency, and zero UI regressions.
|
|
12
|
+
|
|
13
|
+
> **⚠️ Important Context:** This is the **Starter Template**, NOT the full Nala showcase monorepo.
|
|
14
|
+
> There are no showcase views, no ThemeCustomizer, no `CodePreview` component, and no demo-only stores like `notification`, `activity`, `billing`, or `role`. Reference the Nala showcase (`packages/showcase/`) as inspiration only.
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## 🏗️ Project Structure
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
src/
|
|
22
|
+
├── components/
|
|
23
|
+
│ ├── AppLogo.vue ← Auto-imported brand logo component
|
|
24
|
+
│ ├── EmptyState.vue ← Auto-imported zero-data empty state component
|
|
25
|
+
│ ├── PageHeader.vue ← Auto-imported standard page top header
|
|
26
|
+
│ ├── layout/ ← AdminLayout sub-components (Navbar, Sidebar, etc.)
|
|
27
|
+
│ └── ui/ ← 45 Reka UI primitives (all auto-imported)
|
|
28
|
+
├── composables/
|
|
29
|
+
│ ├── useFormatter.ts ← Auto-imported: currency, date, number, relative time formatting
|
|
30
|
+
│ └── useThemeConfig.ts ← Auto-imported: layout mode, border radius, accent color
|
|
31
|
+
├── layouts/
|
|
32
|
+
│ ├── AdminLayout.vue ← Main dashboard shell (sidebar + navbar + content slot)
|
|
33
|
+
│ └── AuthLayout.vue ← Centered auth card layout
|
|
34
|
+
├── lib/
|
|
35
|
+
│ ├── axios.ts ← Pre-configured Axios instance with interceptors
|
|
36
|
+
│ ├── formatters.ts ← Core formatter functions (used by useFormatter)
|
|
37
|
+
│ ├── loading-bar.ts ← Global loading bar singleton
|
|
38
|
+
│ ├── utils.ts ← cn() class merging utility
|
|
39
|
+
│ └── validation.ts ← Shared zod validation helpers
|
|
40
|
+
├── router/
|
|
41
|
+
│ └── index.ts ← Vue Router with auth guard scaffold (commented, ready to enable)
|
|
42
|
+
├── services/
|
|
43
|
+
│ ├── auth.service.ts ← Auth API methods (login, logout, refresh token)
|
|
44
|
+
│ ├── user.service.ts ← User CRUD API methods
|
|
45
|
+
│ ├── types.ts ← Shared API response types (ApiResponse<T>)
|
|
46
|
+
│ └── index.ts ← Service re-exports
|
|
47
|
+
├── stores/
|
|
48
|
+
│ └── auth.ts ← Auth Pinia store (isAuthenticated, currentUser, login/logout)
|
|
49
|
+
└── views/
|
|
50
|
+
├── _starter/
|
|
51
|
+
│ └── BlankView.vue ← Scaffolding starting point for new pages
|
|
52
|
+
├── auth/ ← Full auth suite (Login, Register, Forgot/Reset Password, OTP, etc.)
|
|
53
|
+
├── dashboard/
|
|
54
|
+
│ └── IndexView.vue ← Minimal dashboard with KPI stat cards (your starting point)
|
|
55
|
+
└── errors/ ← 404, 500, 403, Maintenance, Coming Soon pages
|
|
56
|
+
```
|
|
12
57
|
|
|
13
58
|
---
|
|
14
59
|
|
|
@@ -19,12 +64,13 @@ You are an expert frontend engineer building features for this **Nala Admin Dash
|
|
|
19
64
|
| **Framework** | Vue 3 (Composition API) | Always use `<script setup lang="ts">` |
|
|
20
65
|
| **Build Tool** | Vite 8 (`@vitejs/plugin-vue`) | Super-fast HMR & production bundler |
|
|
21
66
|
| **Styling** | Tailwind CSS v4 (`@tailwindcss/vite`) + `tw-animate-css` | OKLCH color tokens in `src/style.css` |
|
|
22
|
-
| **UI Primitives** | `reka-ui` + shadcn-vue (New York style) |
|
|
67
|
+
| **UI Primitives** | `reka-ui` + shadcn-vue (New York style) | 45 primitive components in `src/components/ui/` |
|
|
68
|
+
| **Charts** | `@unovis/ts` + `@unovis/vue` | SVG chart primitives: `AreaChart`, `BarChart`, `LineChart`, `DonutChart` |
|
|
23
69
|
| **Data Table** | `@tanstack/vue-table` | Headless table logic with sorting & pagination |
|
|
24
70
|
| **Form & Validation** | `vee-validate` + `@vee-validate/zod` + `zod` | Strongly-typed validation schemas |
|
|
25
71
|
| **Icons** | `@lucide/vue` | Must be imported explicitly (`import { Plus } from '@lucide/vue'`) |
|
|
26
72
|
| **State Management** | Pinia | Setup Stores pattern (`defineStore('name', () => { ... })`) |
|
|
27
|
-
| **Routing** | Vue Router v5 | HTML5 history mode
|
|
73
|
+
| **Routing** | Vue Router v5 | HTML5 history mode — auth guard scaffold in `src/router/index.ts` (commented) |
|
|
28
74
|
| **API Client** | Axios + Enterprise Interceptors | Silent Token Refresh, Global Loading Bar, Auto Error Toast |
|
|
29
75
|
| **Notifications** | `vue-sonner` | Toast notifications via `toast.success()`, `toast.error()` |
|
|
30
76
|
| **Language & Types** | TypeScript 6 + `vue-tsc` | Strict static typing, zero `any` |
|
|
@@ -35,32 +81,47 @@ You are an expert frontend engineer building features for this **Nala Admin Dash
|
|
|
35
81
|
|
|
36
82
|
1. **Composition API Only:** Never use Options API — always use `<script setup lang="ts">`.
|
|
37
83
|
2. **Strict TypeScript:** No `any` type — define explicit TypeScript interfaces for all props, emits, and API payloads.
|
|
38
|
-
3. **English Standard:** All user-facing UI text, form labels, placeholders, error messages, toast notifications, and in-code comments
|
|
39
|
-
4. **
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
84
|
+
3. **English Standard:** All user-facing UI text, form labels, placeholders, error messages, toast notifications, and in-code comments **MUST be in English**.
|
|
85
|
+
4. **No Build Commands:** NEVER run `pnpm build` or `npm run build` autonomously. Build only on explicit user request.
|
|
86
|
+
5. **No Automatic Tests:** NEVER run `vitest`, `pnpm test`, or `pnpm test:run` autonomously.
|
|
87
|
+
6. **No Git Operations:** NEVER run `git add`, `git commit`, or `git push` autonomously.
|
|
88
|
+
7. **No New Packages Without Confirmation:** Always ask in chat before running `pnpm add`.
|
|
89
|
+
8. **No Scoped CSS:** Use Tailwind utility classes. Do not write `<style scoped>`.
|
|
90
|
+
9. **Dialog Accessibility:** Every `DialogContent` MUST include `DialogTitle` and `DialogDescription` (use `sr-only` if visually hidden) to comply with Reka UI ARIA standards.
|
|
91
|
+
10. **Tailwind CSS v4 Strict Syntax Compliance:**
|
|
92
|
+
- **Gradients:** ALWAYS use `bg-linear-to-r`, `bg-linear-to-b`, `bg-linear-to-tr` (NEVER `bg-gradient-to-*`).
|
|
93
|
+
- **Sizing:** NEVER use arbitrary brackets like `max-w-[170px]`, `w-[300px]`, `p-[1px]`. Use Tailwind v4 scale (`max-w-44`, `w-75`, `p-px`).
|
|
94
|
+
- **Shadows:** Use `shadow-2xs`, `shadow-xs`, `shadow-sm`, `shadow-md`, `shadow-lg`.
|
|
95
|
+
- **Viewport:** Use `min-h-dvh` / `h-dvh` instead of `min-h-screen` / `h-screen`.
|
|
96
|
+
- **Opacity:** Use integer percentage (`bg-primary/50`) NOT decimal (`bg-primary/0.5`).
|
|
45
97
|
|
|
46
98
|
---
|
|
47
99
|
|
|
48
100
|
## ⚡ Auto-Import Guide Matrix
|
|
49
101
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
|
53
|
-
|
|
54
|
-
|
|
|
55
|
-
|
|
|
56
|
-
|
|
|
102
|
+
> The project uses `unplugin-auto-import` and `unplugin-vue-components`. Do NOT add manual imports for items marked ✅ AUTOMATIC.
|
|
103
|
+
|
|
104
|
+
| Category | Auto-Import Status | Usage |
|
|
105
|
+
|---|---|---|
|
|
106
|
+
| **Vue Reactivity** (`ref`, `computed`, `watch`, `onMounted`, `reactive`, etc.) | ✅ **AUTOMATIC** | Use directly: `const count = ref(0)` |
|
|
107
|
+
| **Vue Router** (`useRoute`, `useRouter`) | ✅ **AUTOMATIC** | Use directly: `const router = useRouter()` |
|
|
108
|
+
| **VueUse** (`useColorMode`, `useLocalStorage`, `useDebounceFn`, etc.) | ✅ **AUTOMATIC** | Use directly: `const mode = useColorMode()` |
|
|
109
|
+
| **Pinia** (`defineStore`, `storeToRefs`) | ✅ **AUTOMATIC** | Use directly: `defineStore(...)` |
|
|
110
|
+
| **All UI Components** (`Button`, `Card`, `Dialog`, `Input`, `InputGroup`, `Table`, `Tabs`, etc.) | ✅ **AUTOMATIC** | Use directly in template: `<Card>`, `<Button>`, `<InputGroup>` |
|
|
111
|
+
| **Shared Components** (`PageHeader`, `EmptyState`, `AppLogo`) | ✅ **AUTOMATIC** | Use directly: `<PageHeader>`, `<EmptyState>` |
|
|
112
|
+
| **Custom Composables** (`useFormatter`, `useThemeConfig`) | ✅ **AUTOMATIC** | Use directly: `const fmt = useFormatter()` |
|
|
113
|
+
| **Lucide Icons** (`Plus`, `Search`, `Trash2`, etc.) | ⚠️ **MANUAL IMPORT** | `import { Plus, Search } from '@lucide/vue'` |
|
|
114
|
+
| **Toast Notifications** (`toast`) | ⚠️ **MANUAL IMPORT** | `import { toast } from '@/components/ui/sonner'` |
|
|
115
|
+
| **HTTP API Client** (`apiClient`) | ⚠️ **MANUAL IMPORT** | `import { apiClient } from '@/lib/axios'` |
|
|
116
|
+
| **Class Merging Utility** (`cn`) | ⚠️ **MANUAL IMPORT** | `import { cn } from '@/lib/utils'` |
|
|
117
|
+
| **Form Validation** (`zod`, `toTypedSchema`, `useForm`) | ⚠️ **MANUAL IMPORT** | `import { z } from 'zod'`, `import { useForm } from 'vee-validate'` |
|
|
57
118
|
|
|
58
119
|
---
|
|
59
120
|
|
|
60
|
-
## 🎨 UI Design System
|
|
121
|
+
## 🎨 UI Design System — Key Patterns
|
|
61
122
|
|
|
62
|
-
### 1. Standard Page Header
|
|
63
|
-
Always use
|
|
123
|
+
### 1. Standard Page Header
|
|
124
|
+
Always use `<PageHeader>` at the top of every view — never build a custom header layout:
|
|
64
125
|
```vue
|
|
65
126
|
<PageHeader
|
|
66
127
|
title="Products"
|
|
@@ -75,32 +136,62 @@ Always use the standardized `<PageHeader>` component at the top of every view:
|
|
|
75
136
|
</PageHeader>
|
|
76
137
|
```
|
|
77
138
|
|
|
78
|
-
### 2.
|
|
79
|
-
|
|
139
|
+
### 2. Edge-to-Edge Card Layout (`<Card flush>` + `<CardHeader section>`)
|
|
140
|
+
- Use `<Card flush>` → auto-applies `overflow-hidden py-0 gap-0`
|
|
141
|
+
- Use `<CardHeader section>` → auto-applies `p-6 border-b border-border bg-muted/10`
|
|
142
|
+
- Use `<CardContent class="p-0">` for table content (zero inner padding)
|
|
143
|
+
|
|
144
|
+
```vue
|
|
145
|
+
<Card flush class="shadow-xs border">
|
|
146
|
+
<CardHeader section class="flex items-center justify-between gap-4">
|
|
147
|
+
<div>
|
|
148
|
+
<CardTitle class="text-base font-semibold">Title</CardTitle>
|
|
149
|
+
<CardDescription class="text-xs mt-0.5">Subtitle</CardDescription>
|
|
150
|
+
</div>
|
|
151
|
+
</CardHeader>
|
|
152
|
+
<CardContent class="p-0">
|
|
153
|
+
<!-- Table content here -->
|
|
154
|
+
</CardContent>
|
|
155
|
+
</Card>
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### 3. Input Groups with Icons & Addons
|
|
159
|
+
Never write manual `absolute` positioning for input icons:
|
|
160
|
+
```vue
|
|
161
|
+
<!-- Search icon input -->
|
|
162
|
+
<InputGroup>
|
|
163
|
+
<InputIcon side="left">
|
|
164
|
+
<Search class="h-3.5 w-3.5" />
|
|
165
|
+
</InputIcon>
|
|
166
|
+
<Input v-model="searchQuery" placeholder="Search..." class="pl-8 h-8 text-xs" />
|
|
167
|
+
</InputGroup>
|
|
168
|
+
|
|
169
|
+
<!-- URL prefix/suffix addon -->
|
|
170
|
+
<InputGroup>
|
|
171
|
+
<InputAddon side="left">https://</InputAddon>
|
|
172
|
+
<Input placeholder="my-subdomain" class="rounded-none border-x-0" />
|
|
173
|
+
<InputAddon side="right">.example.com</InputAddon>
|
|
174
|
+
</InputGroup>
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### 4. Standard KPI Stats Cards
|
|
80
178
|
```vue
|
|
81
179
|
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
|
|
82
180
|
<Card
|
|
83
181
|
v-for="stat in stats"
|
|
84
182
|
:key="stat.title"
|
|
85
183
|
flush
|
|
86
|
-
class="highlight-card shadow-xs border-border/80
|
|
184
|
+
class="highlight-card shadow-xs border-border/80 hover:border-border transition-all duration-200"
|
|
87
185
|
>
|
|
88
186
|
<CardContent class="p-5 space-y-2">
|
|
89
|
-
<!-- 1. Top Row: Title + Icon -->
|
|
90
187
|
<div class="flex items-center justify-between">
|
|
91
188
|
<span class="text-xs font-medium text-muted-foreground">{{ stat.title }}</span>
|
|
92
189
|
<component :is="stat.icon" class="h-4 w-4 text-primary" />
|
|
93
190
|
</div>
|
|
94
|
-
|
|
95
|
-
<!-- 2. Value -->
|
|
96
|
-
<div class="text-2xl font-bold tracking-tight text-foreground">{{ stat.value }}</div>
|
|
97
|
-
|
|
98
|
-
<!-- 3. Trend Indicator -->
|
|
191
|
+
<div class="text-2xl font-bold tracking-tight">{{ stat.value }}</div>
|
|
99
192
|
<p class="text-xs text-muted-foreground flex items-center gap-1">
|
|
100
|
-
<span
|
|
101
|
-
class="
|
|
102
|
-
:class="stat.positive ? 'text-emerald-500' : 'text-rose-500'"
|
|
103
|
-
>
|
|
193
|
+
<span class="font-semibold inline-flex items-center gap-0.5"
|
|
194
|
+
:class="stat.positive ? 'text-emerald-500' : 'text-rose-500'">
|
|
104
195
|
<component :is="stat.positive ? ArrowUpRight : ArrowDownRight" class="h-3.5 w-3.5" />
|
|
105
196
|
{{ stat.change }}
|
|
106
197
|
</span>
|
|
@@ -111,23 +202,28 @@ When building summary metrics / stats cards, follow this exact blueprint:
|
|
|
111
202
|
</div>
|
|
112
203
|
```
|
|
113
204
|
|
|
114
|
-
###
|
|
115
|
-
Never write manual absolute positioning for input icons:
|
|
205
|
+
### 5. Empty State (use `<EmptyState>` — don't build inline)
|
|
116
206
|
```vue
|
|
117
|
-
<!--
|
|
118
|
-
<
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
</InputGroup>
|
|
207
|
+
<!-- Inside a table cell or card content when data is empty -->
|
|
208
|
+
<EmptyState
|
|
209
|
+
title="No products found"
|
|
210
|
+
description="Try adjusting your search or add a new product."
|
|
211
|
+
/>
|
|
212
|
+
```
|
|
124
213
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
214
|
+
### 6. `useFormatter()` — Data Formatting
|
|
215
|
+
```ts
|
|
216
|
+
const fmt = useFormatter()
|
|
217
|
+
|
|
218
|
+
fmt.currency(150000, 'IDR') // "Rp 150.000"
|
|
219
|
+
fmt.currency(1999, 'USD') // "$1,999.00"
|
|
220
|
+
fmt.number(1500000) // "1,500,000"
|
|
221
|
+
fmt.compact(1500000) // "1.5M"
|
|
222
|
+
fmt.date(new Date()) // "Sep 3, 2026"
|
|
223
|
+
fmt.dateTime(new Date()) // "Sep 3, 2026, 14:30"
|
|
224
|
+
fmt.relative(pastDate) // "5 minutes ago"
|
|
225
|
+
fmt.bytes(1024 * 1024) // "1 MB"
|
|
226
|
+
fmt.initials('John Doe') // "JD"
|
|
131
227
|
```
|
|
132
228
|
|
|
133
229
|
---
|
|
@@ -473,21 +569,72 @@ const filteredProducts = computed(() => {
|
|
|
473
569
|
</template>
|
|
474
570
|
```
|
|
475
571
|
|
|
476
|
-
### 5. Register
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
572
|
+
### 5. Register Route & Sidebar Entry
|
|
573
|
+
|
|
574
|
+
**`src/router/index.ts`** — Add inside the AdminLayout children array:
|
|
575
|
+
```ts
|
|
576
|
+
{
|
|
577
|
+
path: 'products',
|
|
578
|
+
name: 'products',
|
|
579
|
+
component: () => import('@/views/products/IndexView.vue'),
|
|
580
|
+
},
|
|
581
|
+
```
|
|
582
|
+
|
|
583
|
+
**`src/components/layout/AppSidebar.vue`** — Add to the `mainNav` array:
|
|
584
|
+
```ts
|
|
585
|
+
import { Package } from '@lucide/vue'
|
|
586
|
+
|
|
587
|
+
const mainNav = [
|
|
588
|
+
{ name: 'Dashboard', routeName: 'dashboard', href: '/', icon: LayoutDashboard },
|
|
589
|
+
{ name: 'Products', routeName: 'products', href: '/products', icon: Package },
|
|
590
|
+
]
|
|
591
|
+
```
|
|
592
|
+
|
|
593
|
+
---
|
|
594
|
+
|
|
595
|
+
## 🗺️ Available Routes (Starter Template)
|
|
596
|
+
|
|
597
|
+
| Route | Name | View |
|
|
598
|
+
|---|---|---|
|
|
599
|
+
| `/` | `dashboard` | `views/dashboard/IndexView.vue` |
|
|
600
|
+
| `/_blank` | `starter-blank` | `views/_starter/BlankView.vue` — starting point for new pages |
|
|
601
|
+
| `/auth/login` | `login` | Full login form with demo credentials |
|
|
602
|
+
| `/auth/register` | `register` | Registration with password strength meter |
|
|
603
|
+
| `/auth/forgot-password` | `forgot-password` | Recovery email request |
|
|
604
|
+
| `/auth/reset-password` | `reset-password` | Token-based password reset |
|
|
605
|
+
| `/auth/verify-otp` | `verify-otp` | 6-box OTP input |
|
|
606
|
+
| `/auth/confirm-email` | `confirm-email` | Email confirmation with polling |
|
|
607
|
+
| `/auth/lock-screen` | `lock-screen` | Session lock screen |
|
|
608
|
+
| `/errors/404` | `not-found` | 404 Not Found page |
|
|
609
|
+
| `/errors/500` | `server-error` | 500 Server Error page |
|
|
610
|
+
| `/errors/403` | `unauthorized` | 403 Access Denied page |
|
|
611
|
+
| `/errors/maintenance` | `maintenance` | Maintenance mode with countdown |
|
|
612
|
+
| `/errors/coming-soon` | `coming-soon` | Coming soon with newsletter capture |
|
|
613
|
+
|
|
614
|
+
> **Auth Guard:** The router file (`src/router/index.ts`) has a pre-built auth guard scaffold in comments. Uncomment and configure it to protect your routes.
|
|
615
|
+
|
|
616
|
+
---
|
|
617
|
+
|
|
618
|
+
## 🔧 Tailwind v4 Syntax Reference (Anti-Pattern Table)
|
|
619
|
+
|
|
620
|
+
| Category | ❌ NEVER (v3 / Anti-Pattern) | ✅ ALWAYS (Tailwind v4) |
|
|
621
|
+
|---|---|---|
|
|
622
|
+
| **Gradients** | `bg-gradient-to-r` | `bg-linear-to-r` |
|
|
623
|
+
| **Max Width** | `max-w-[170px]` | `max-w-44` |
|
|
624
|
+
| **Width/Height** | `w-[300px]` / `h-[400px]` | `w-75` / `h-100` |
|
|
625
|
+
| **1px Spacing** | `p-[1px]` / `m-[1px]` | `p-px` / `m-px` |
|
|
626
|
+
| **Shadows** | `shadow-[0_1px_...]` | `shadow-2xs` / `shadow-xs` / `shadow-sm` |
|
|
627
|
+
| **Viewport** | `min-h-screen` / `h-screen` | `min-h-dvh` / `h-dvh` |
|
|
628
|
+
| **Opacity** | `bg-primary/0.5` | `bg-primary/50` |
|
|
629
|
+
|
|
630
|
+
---
|
|
631
|
+
|
|
632
|
+
## 🛠️ Dev Commands
|
|
633
|
+
|
|
634
|
+
| Command | Action |
|
|
635
|
+
|---|---|
|
|
636
|
+
| `pnpm dev` | Start dev server |
|
|
637
|
+
| `pnpm build` | Build for production (run manually only) |
|
|
638
|
+
| `pnpm preview` | Preview production build |
|
|
639
|
+
| `pnpm test` | Run unit tests in watch mode |
|
|
640
|
+
| `pnpm test:run` | Run unit tests once (CI mode) |
|
package/template/AGENTS.md
CHANGED
|
@@ -40,7 +40,7 @@ The following modules are auto-imported via `unplugin-auto-import` and `unplugin
|
|
|
40
40
|
|
|
41
41
|
## 📦 UI Primitives Catalog (`src/components/ui/`)
|
|
42
42
|
|
|
43
|
-
All
|
|
43
|
+
All 45 primitive components in `src/components/ui/` are globally registered and ready to use:
|
|
44
44
|
- `Button`, `Badge`, `Avatar`, `Breadcrumb`, `Card`, `Checkbox`, `Dialog`, `DropdownMenu`
|
|
45
45
|
- `Form` (`FormField`, `FormItem`, `FormLabel`, `FormControl`, `FormMessage`)
|
|
46
46
|
- `Input`, `InputGroup`, `InputIcon`, `InputAddon`, `PinInput`, `Textarea`, `Select`, `Switch`
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# CLAUDE.md — Nala Application AI Guardrails
|
|
2
|
+
|
|
3
|
+
This file defines the strict, high-priority guardrails and behavioral constraints for AI agents working on this **Nala** application.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 🛑 Strict Rules (Enforced on Every Invocation)
|
|
8
|
+
|
|
9
|
+
1. **No Automatic Build:** NEVER run `pnpm build` or `npm run build` after writing or modifying code. Transpilation/build checks are only performed when explicitly requested by the user.
|
|
10
|
+
2. **No Automatic Unit Tests:** NEVER run `vitest`, `pnpm test`, `pnpm test:run`, or `npm test` autonomously after writing or modifying code. Unit tests are strictly executed only upon explicit user instruction.
|
|
11
|
+
3. **No Unauthorized Git Operations:** NEVER run `git add`, `git commit`, or `git push` autonomously. Git commands are strictly executed only upon explicit user instruction.
|
|
12
|
+
4. **English Standard (UI & Code Comments):** All user-facing UI text, form labels, validation/error messages, toast notifications, and in-code comments (JSDoc/inline notes in `.vue` and `.ts` files) MUST be written in **English**.
|
|
13
|
+
5. **Strict Vue 3 & TypeScript Hygiene:** Always use Composition API `<script setup lang="ts">`. Never use Options API. Never use `any` type — define explicit TypeScript interfaces for all props, emits, and API payloads.
|
|
14
|
+
6. **Tailwind CSS v4 Standard & Syntax Compliance:**
|
|
15
|
+
- **Gradients:** ALWAYS use `bg-linear-to-r`, `bg-linear-to-b`, `bg-linear-to-tr` (NEVER use legacy v3 `bg-gradient-to-*`).
|
|
16
|
+
- **Sizing Scale:** NEVER write arbitrary bracket sizing like `max-w-[170px]`, `w-[300px]`, or `p-[1px]`. ALWAYS use Tailwind v4 scale (`max-w-44`, `max-w-28`, `w-75`, `max-h-75`, `p-px`).
|
|
17
|
+
- **Shadows:** Use `shadow-2xs`, `shadow-xs`, `shadow-sm`, `shadow-md`, `shadow-lg`.
|
|
18
|
+
7. **Package Installation Confirmation:** NEVER install new npm packages autonomously. Always ask the user directly in chat for approval before running `pnpm add` or `npm install`.
|
|
19
|
+
8. **No Scoped Styles:** Use Tailwind CSS utility classes instead of `<style scoped>`.
|
|
20
|
+
9. **Dialog / Modal Accessibility Standard:** Every `DialogContent` MUST include `DialogTitle` and `DialogDescription` (use `sr-only` if visually hidden) to comply with Reka UI ARIA standards.
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## 📚 Deep Technical Blueprints & Component Catalog
|
|
25
|
+
|
|
26
|
+
For the complete 45 Reka UI Primitives, Shared Components (`PageHeader`, `EmptyState`, `AppLogo`), Auto-Import Matrix, Axios Service Layer, Pinia Store patterns, and end-to-end CRUD scaffolding blueprints, refer to the **`nala-project` skill** located at:
|
|
27
|
+
👉 [`.agents/skills/nala-project/SKILL.md`](.agents/skills/nala-project/SKILL.md)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# GEMINI.md — Nala Application AI Guardrails
|
|
2
|
+
|
|
3
|
+
This file defines the strict, high-priority guardrails and behavioral constraints for AI agents working on this **Nala** application.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 🛑 Strict Rules (Enforced on Every Invocation)
|
|
8
|
+
|
|
9
|
+
1. **No Automatic Build:** NEVER run `pnpm build` or `npm run build` after writing or modifying code. Transpilation/build checks are only performed when explicitly requested by the user.
|
|
10
|
+
2. **No Automatic Unit Tests:** NEVER run `vitest`, `pnpm test`, `pnpm test:run`, or `npm test` autonomously after writing or modifying code. Unit tests are strictly executed only upon explicit user instruction.
|
|
11
|
+
3. **No Unauthorized Git Operations:** NEVER run `git add`, `git commit`, or `git push` autonomously. Git commands are strictly executed only upon explicit user instruction.
|
|
12
|
+
4. **English Standard (UI & Code Comments):** All user-facing UI text, form labels, validation/error messages, toast notifications, and in-code comments (JSDoc/inline notes in `.vue` and `.ts` files) MUST be written in **English**.
|
|
13
|
+
5. **Strict Vue 3 & TypeScript Hygiene:** Always use Composition API `<script setup lang="ts">`. Never use Options API. Never use `any` type — define explicit TypeScript interfaces for all props, emits, and API payloads.
|
|
14
|
+
6. **Tailwind CSS v4 Standard & Syntax Compliance:**
|
|
15
|
+
- **Gradients:** ALWAYS use `bg-linear-to-r`, `bg-linear-to-b`, `bg-linear-to-tr` (NEVER use legacy v3 `bg-gradient-to-*`).
|
|
16
|
+
- **Sizing Scale:** NEVER write arbitrary bracket sizing like `max-w-[170px]`, `w-[300px]`, or `p-[1px]`. ALWAYS use Tailwind v4 scale (`max-w-44`, `max-w-28`, `w-75`, `max-h-75`, `p-px`).
|
|
17
|
+
- **Shadows:** Use `shadow-2xs`, `shadow-xs`, `shadow-sm`, `shadow-md`, `shadow-lg`.
|
|
18
|
+
7. **Package Installation Confirmation:** NEVER install new npm packages autonomously. Always ask the user directly in chat for approval before running `pnpm add` or `npm install`.
|
|
19
|
+
8. **No Scoped Styles:** Use Tailwind CSS utility classes instead of `<style scoped>`.
|
|
20
|
+
9. **Dialog / Modal Accessibility Standard:** Every `DialogContent` MUST include `DialogTitle` and `DialogDescription` (use `sr-only` if visually hidden) to comply with Reka UI ARIA standards.
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## 📚 Deep Technical Blueprints & Component Catalog
|
|
25
|
+
|
|
26
|
+
For the complete 45 Reka UI Primitives, Shared Components (`PageHeader`, `EmptyState`, `AppLogo`), Auto-Import Matrix, Axios Service Layer, Pinia Store patterns, and end-to-end CRUD scaffolding blueprints, refer to the **`nala-project` skill** located at:
|
|
27
|
+
👉 [`.agents/skills/nala-project/SKILL.md`](.agents/skills/nala-project/SKILL.md)
|
package/template/README.md
CHANGED
|
@@ -57,7 +57,9 @@ src/
|
|
|
57
57
|
├── assets/fonts/ # High-performance local Inter fonts (woff2)
|
|
58
58
|
├── components/
|
|
59
59
|
│ ├── layout/ # Admin shell (AppNavbar, AppSidebar, CommandPalette)
|
|
60
|
-
│ ├── ui/ #
|
|
60
|
+
│ ├── ui/ # 45 accessible UI primitives (Reka UI / shadcn-vue)
|
|
61
|
+
│ ├── AppLogo.vue # Auto-imported brand logo component
|
|
62
|
+
│ ├── EmptyState.vue # Auto-imported empty state component
|
|
61
63
|
│ └── PageHeader.vue # Standardized page header component
|
|
62
64
|
├── composables/
|
|
63
65
|
│ ├── useFormatter.ts # Native Intl data formatting helpers (auto-imported)
|
package/template/package.json
CHANGED
package/template/src/App.vue
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="77" height="47" fill="none" aria-labelledby="vite-logo-title" viewBox="0 0 77 47"><title id="vite-logo-title">Vite</title><style>.parenthesis{fill:#000}@media (prefers-color-scheme:dark){.parenthesis{fill:#fff}}</style><path fill="#9135ff" d="M40.151 45.71c-.663.844-2.02.374-2.02-.699V34.708a2.26 2.26 0 0 0-2.262-2.262H24.493c-.92 0-1.457-1.04-.92-1.788l7.479-10.471c1.07-1.498 0-3.578-1.842-3.578H15.443c-.92 0-1.456-1.04-.92-1.788l9.696-13.576c.213-.297.556-.474.92-.474h28.894c.92 0 1.456 1.04.92 1.788l-7.48 10.472c-1.07 1.497 0 3.578 1.842 3.578h11.376c.944 0 1.474 1.087.89 1.83L40.153 45.712z"/><mask id="a" width="48" height="47" x="14" y="0" maskUnits="userSpaceOnUse" style="mask-type:alpha"><path fill="#000" d="M40.047 45.71c-.663.843-2.02.374-2.02-.699V34.708a2.26 2.26 0 0 0-2.262-2.262H24.389c-.92 0-1.457-1.04-.92-1.788l7.479-10.472c1.07-1.497 0-3.578-1.842-3.578H15.34c-.92 0-1.456-1.04-.92-1.788l9.696-13.575c.213-.297.556-.474.92-.474H53.93c.92 0 1.456 1.04.92 1.788L47.37 13.03c-1.07 1.498 0 3.578 1.842 3.578h11.376c.944 0 1.474 1.088.89 1.831L40.049 45.712z"/></mask><g mask="url(#a)"><g filter="url(#b)"><ellipse cx="5.508" cy="14.704" fill="#eee6ff" rx="5.508" ry="14.704" transform="rotate(269.814 20.96 11.29)scale(-1 1)"/></g><g filter="url(#c)"><ellipse cx="10.399" cy="29.851" fill="#eee6ff" rx="10.399" ry="29.851" transform="rotate(89.814 -16.902 -8.275)scale(1 -1)"/></g><g filter="url(#d)"><ellipse cx="5.508" cy="30.487" fill="#8900ff" rx="5.508" ry="30.487" transform="rotate(89.814 -19.197 -7.127)scale(1 -1)"/></g><g filter="url(#e)"><ellipse cx="5.508" cy="30.599" fill="#8900ff" rx="5.508" ry="30.599" transform="rotate(89.814 -25.928 4.177)scale(1 -1)"/></g><g filter="url(#f)"><ellipse cx="5.508" cy="30.599" fill="#8900ff" rx="5.508" ry="30.599" transform="rotate(89.814 -25.738 5.52)scale(1 -1)"/></g><g filter="url(#g)"><ellipse cx="14.072" cy="22.078" fill="#eee6ff" rx="14.072" ry="22.078" transform="rotate(93.35 31.245 55.578)scale(-1 1)"/></g><g filter="url(#h)"><ellipse cx="3.47" cy="21.501" fill="#8900ff" rx="3.47" ry="21.501" transform="rotate(89.009 35.419 55.202)scale(-1 1)"/></g><g filter="url(#i)"><ellipse cx="3.47" cy="21.501" fill="#8900ff" rx="3.47" ry="21.501" transform="rotate(89.009 35.419 55.202)scale(-1 1)"/></g><g filter="url(#j)"><ellipse cx="14.592" cy="9.743" fill="#8900ff" rx="4.407" ry="29.108" transform="rotate(39.51 14.592 9.743)"/></g><g filter="url(#k)"><ellipse cx="61.728" cy="-5.321" fill="#8900ff" rx="4.407" ry="29.108" transform="rotate(37.892 61.728 -5.32)"/></g><g filter="url(#l)"><ellipse cx="55.618" cy="7.104" fill="#00c2ff" rx="5.971" ry="9.665" transform="rotate(37.892 55.618 7.104)"/></g><g filter="url(#m)"><ellipse cx="12.326" cy="39.103" fill="#8900ff" rx="4.407" ry="29.108" transform="rotate(37.892 12.326 39.103)"/></g><g filter="url(#n)"><ellipse cx="12.326" cy="39.103" fill="#8900ff" rx="4.407" ry="29.108" transform="rotate(37.892 12.326 39.103)"/></g><g filter="url(#o)"><ellipse cx="49.857" cy="30.678" fill="#8900ff" rx="4.407" ry="29.108" transform="rotate(37.892 49.857 30.678)"/></g><g filter="url(#p)"><ellipse cx="52.623" cy="33.171" fill="#00c2ff" rx="5.971" ry="15.297" transform="rotate(37.892 52.623 33.17)"/></g></g><path d="M6.919 0c-9.198 13.166-9.252 33.575 0 46.789h6.215c-9.25-13.214-9.196-33.623 0-46.789zm62.424 0h-6.215c9.198 13.166 9.252 33.575 0 46.789h6.215c9.25-13.214 9.196-33.623 0-46.789" class="parenthesis"/><defs><filter id="b" width="60.045" height="41.654" x="-5.564" y="16.92" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17286" stdDeviation="7.659"/></filter><filter id="c" width="90.34" height="51.437" x="-40.407" y="-6.762" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17286" stdDeviation="7.659"/></filter><filter id="d" width="79.355" height="29.4" x="-35.435" y="2.801" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17286" stdDeviation="4.596"/></filter><filter id="e" width="79.579" height="29.4" x="-30.84" y="20.8" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17286" stdDeviation="4.596"/></filter><filter id="f" width="79.579" height="29.4" x="-29.307" y="21.949" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17286" stdDeviation="4.596"/></filter><filter id="g" width="74.749" height="58.852" x="29.961" y="-17.13" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17286" stdDeviation="7.659"/></filter><filter id="h" width="61.377" height="25.362" x="37.754" y="3.055" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17286" stdDeviation="4.596"/></filter><filter id="i" width="61.377" height="25.362" x="37.754" y="3.055" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17286" stdDeviation="4.596"/></filter><filter id="j" width="56.045" height="63.649" x="-13.43" y="-22.082" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17286" stdDeviation="4.596"/></filter><filter id="k" width="54.814" height="64.646" x="34.321" y="-37.644" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17286" stdDeviation="4.596"/></filter><filter id="l" width="33.541" height="35.313" x="38.847" y="-10.552" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17286" stdDeviation="4.596"/></filter><filter id="m" width="54.814" height="64.646" x="-15.081" y="6.78" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17286" stdDeviation="4.596"/></filter><filter id="n" width="54.814" height="64.646" x="-15.081" y="6.78" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17286" stdDeviation="4.596"/></filter><filter id="o" width="54.814" height="64.646" x="22.45" y="-1.645" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17286" stdDeviation="4.596"/></filter><filter id="p" width="39.409" height="43.623" x="32.919" y="11.36" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17286" stdDeviation="4.596"/></filter></defs></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path></svg>
|
|
@@ -1,14 +1,48 @@
|
|
|
1
1
|
<script lang="ts" setup>
|
|
2
2
|
import type { ToasterProps } from "vue-sonner"
|
|
3
|
-
import {
|
|
3
|
+
import { CheckCircle2, Info, Loader2, AlertCircle, AlertTriangle } from "@lucide/vue"
|
|
4
4
|
import { Toaster as Sonner } from "vue-sonner"
|
|
5
5
|
import { cn } from "@/lib/utils"
|
|
6
6
|
|
|
7
|
-
const props = defineProps<ToasterProps>()
|
|
7
|
+
const props = withDefaults(defineProps<ToasterProps>(), {
|
|
8
|
+
position: "top-right",
|
|
9
|
+
richColors: false,
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
const defaultToastOptions = {
|
|
13
|
+
classes: {
|
|
14
|
+
toast:
|
|
15
|
+
'group toast flex items-center gap-3 w-full bg-card/95 text-foreground border border-border/80 shadow-lg backdrop-blur-md px-4 py-3 rounded-lg text-xs transition-all',
|
|
16
|
+
title: 'text-xs font-semibold text-foreground tracking-tight',
|
|
17
|
+
description: '!text-zinc-600 dark:!text-zinc-300 text-[11.5px] leading-relaxed mt-0.5 font-normal',
|
|
18
|
+
actionButton:
|
|
19
|
+
'bg-primary text-primary-foreground text-[11px] font-medium px-2.5 py-1 rounded shadow-2xs hover:bg-primary/90 transition-colors',
|
|
20
|
+
cancelButton:
|
|
21
|
+
'bg-muted text-muted-foreground text-[11px] font-medium px-2.5 py-1 rounded hover:bg-muted/80 transition-colors',
|
|
22
|
+
success:
|
|
23
|
+
'!border-emerald-500/30 !bg-emerald-500/[0.04] dark:!bg-emerald-950/[0.2] !text-foreground',
|
|
24
|
+
info:
|
|
25
|
+
'!border-blue-500/30 !bg-blue-500/[0.04] dark:!bg-blue-950/[0.2] !text-foreground',
|
|
26
|
+
warning:
|
|
27
|
+
'!border-amber-500/30 !bg-amber-500/[0.04] dark:!bg-amber-950/[0.2] !text-foreground',
|
|
28
|
+
error:
|
|
29
|
+
'!border-rose-500/30 !bg-rose-500/[0.04] dark:!bg-rose-950/[0.2] !text-foreground',
|
|
30
|
+
},
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const computedToastOptions = computed(() => ({
|
|
34
|
+
...defaultToastOptions,
|
|
35
|
+
...props.toastOptions,
|
|
36
|
+
classes: {
|
|
37
|
+
...defaultToastOptions.classes,
|
|
38
|
+
...props.toastOptions?.classes,
|
|
39
|
+
},
|
|
40
|
+
}))
|
|
8
41
|
</script>
|
|
9
42
|
|
|
10
43
|
<template>
|
|
11
44
|
<Sonner
|
|
45
|
+
v-bind="props"
|
|
12
46
|
:class="cn('toaster group', props.class)"
|
|
13
47
|
:style="{
|
|
14
48
|
'--normal-bg': 'var(--popover)',
|
|
@@ -16,27 +50,23 @@ const props = defineProps<ToasterProps>()
|
|
|
16
50
|
'--normal-border': 'var(--border)',
|
|
17
51
|
'--border-radius': 'var(--radius)',
|
|
18
52
|
}"
|
|
19
|
-
|
|
53
|
+
:toast-options="computedToastOptions"
|
|
54
|
+
:close-button="false"
|
|
20
55
|
>
|
|
21
56
|
<template #success-icon>
|
|
22
|
-
<
|
|
57
|
+
<CheckCircle2 class="h-4 w-4 text-emerald-500 shrink-0" />
|
|
23
58
|
</template>
|
|
24
59
|
<template #info-icon>
|
|
25
|
-
<
|
|
60
|
+
<Info class="h-4 w-4 text-blue-500 shrink-0" />
|
|
26
61
|
</template>
|
|
27
62
|
<template #warning-icon>
|
|
28
|
-
<
|
|
63
|
+
<AlertTriangle class="h-4 w-4 text-amber-500 shrink-0" />
|
|
29
64
|
</template>
|
|
30
65
|
<template #error-icon>
|
|
31
|
-
<
|
|
66
|
+
<AlertCircle class="h-4 w-4 text-rose-500 shrink-0" />
|
|
32
67
|
</template>
|
|
33
68
|
<template #loading-icon>
|
|
34
|
-
<
|
|
35
|
-
<Loader2Icon class="size-4 animate-spin" />
|
|
36
|
-
</div>
|
|
37
|
-
</template>
|
|
38
|
-
<template #close-icon>
|
|
39
|
-
<XIcon class="size-4" />
|
|
69
|
+
<Loader2 class="h-4 w-4 text-primary animate-spin shrink-0" />
|
|
40
70
|
</template>
|
|
41
71
|
</Sonner>
|
|
42
72
|
</template>
|
|
@@ -1,21 +1,27 @@
|
|
|
1
1
|
{
|
|
2
|
-
"extends": "@vue/tsconfig/tsconfig.dom.json",
|
|
3
2
|
"compilerOptions": {
|
|
4
3
|
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
"
|
|
10
|
-
/*
|
|
4
|
+
/* Target must match vite's esbuild target */
|
|
5
|
+
"target": "ES2022",
|
|
6
|
+
"module": "ESNext",
|
|
7
|
+
"moduleResolution": "Bundler",
|
|
8
|
+
"useDefineForClassFields": true,
|
|
9
|
+
/* DOM lib for browser APIs */
|
|
10
|
+
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
|
11
|
+
"jsx": "preserve",
|
|
12
|
+
"jsxImportSource": "vue",
|
|
13
|
+
/* Type checking */
|
|
14
|
+
"strict": true,
|
|
15
|
+
"noFallthroughCasesInSwitch": true,
|
|
11
16
|
"noUnusedLocals": true,
|
|
12
17
|
"noUnusedParameters": true,
|
|
13
18
|
"erasableSyntaxOnly": true,
|
|
14
|
-
"
|
|
19
|
+
"allowArbitraryExtensions": true,
|
|
20
|
+
"resolveJsonModule": true,
|
|
21
|
+
"isolatedModules": true,
|
|
22
|
+
"skipLibCheck": true,
|
|
15
23
|
"paths": {
|
|
16
|
-
"@/*": [
|
|
17
|
-
"./src/*"
|
|
18
|
-
]
|
|
24
|
+
"@/*": ["./src/*"]
|
|
19
25
|
}
|
|
20
26
|
},
|
|
21
27
|
"include": [
|