@veristone/nuxt-v-app 0.1.0 → 0.2.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/app/components/V/A/Crud/Create.vue +149 -0
- package/app/components/V/A/Crud/Delete.vue +148 -0
- package/app/components/V/A/Crud/Update.vue +171 -0
- package/app/composables/useVCrud.ts +58 -35
- package/app/composables/useVFetch.ts +27 -2
- package/app/layouts/default.vue +76 -68
- package/app/pages/playground/crud.vue +936 -0
- package/app/pages/playground/index.vue +6 -0
- package/app/pages/test-api-auth.vue +223 -0
- package/package.json +1 -1
|
@@ -0,0 +1,936 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
definePageMeta({
|
|
3
|
+
title: "CRUD Components",
|
|
4
|
+
});
|
|
5
|
+
|
|
6
|
+
const toast = useToast();
|
|
7
|
+
|
|
8
|
+
// ===== VACrudCreate Test State =====
|
|
9
|
+
const createBasicResult = ref<any>(null);
|
|
10
|
+
const createCustomResult = ref<any>(null);
|
|
11
|
+
const createInitialResult = ref<any>(null);
|
|
12
|
+
const createProgrammaticOpen = ref(false);
|
|
13
|
+
const createProgrammaticResult = ref<any>(null);
|
|
14
|
+
const createSizeResults = ref<Record<string, any>>({});
|
|
15
|
+
const createErrorResult = ref<any>(null);
|
|
16
|
+
|
|
17
|
+
// ===== VACrudUpdate Test State =====
|
|
18
|
+
const updateBasicResult = ref<any>(null);
|
|
19
|
+
const updateUsers = ref([
|
|
20
|
+
{
|
|
21
|
+
id: 1,
|
|
22
|
+
name: "John Doe",
|
|
23
|
+
email: "john@example.com",
|
|
24
|
+
role: "admin",
|
|
25
|
+
status: "active",
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
id: 2,
|
|
29
|
+
name: "Jane Smith",
|
|
30
|
+
email: "jane@example.com",
|
|
31
|
+
role: "user",
|
|
32
|
+
status: "active",
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
id: 3,
|
|
36
|
+
name: "Bob Wilson",
|
|
37
|
+
email: "bob@example.com",
|
|
38
|
+
role: "user",
|
|
39
|
+
status: "inactive",
|
|
40
|
+
},
|
|
41
|
+
]);
|
|
42
|
+
const updateSelectedUserId = ref(1);
|
|
43
|
+
const updateSelectionResult = ref<any>(null);
|
|
44
|
+
const updateSizeResults = ref<Record<string, any>>({});
|
|
45
|
+
const updateProgrammaticOpen = ref(false);
|
|
46
|
+
const updateProgrammaticResult = ref<any>(null);
|
|
47
|
+
const updateCustomResult = ref<any>(null);
|
|
48
|
+
const updateErrorResult = ref<any>(null);
|
|
49
|
+
|
|
50
|
+
// ===== VACrudDelete Test State =====
|
|
51
|
+
const deleteBasicDeleted = ref(false);
|
|
52
|
+
const deleteMockItems = ref([
|
|
53
|
+
{ id: 1, name: "Project Alpha", description: "Main development project" },
|
|
54
|
+
{ id: 2, name: "Marketing Campaign", description: "Q1 marketing initiative" },
|
|
55
|
+
{ id: 3, name: "Server Migration", description: "AWS to Azure migration" },
|
|
56
|
+
]);
|
|
57
|
+
const deleteSelectedItemId = ref(1);
|
|
58
|
+
const deleteSelectionDeleted = ref(false);
|
|
59
|
+
const deleteCustomDeleted = ref(false);
|
|
60
|
+
const deleteProgrammaticOpen = ref(false);
|
|
61
|
+
const deleteProgrammaticDeleted = ref(false);
|
|
62
|
+
const deleteStyleResults = ref<Record<string, boolean>>({});
|
|
63
|
+
const deleteErrorResult = ref<any>(null);
|
|
64
|
+
|
|
65
|
+
// ===== Event Handlers =====
|
|
66
|
+
// Create handlers
|
|
67
|
+
const handleCreateBasic = (data: any) => {
|
|
68
|
+
createBasicResult.value = data;
|
|
69
|
+
toast.add({
|
|
70
|
+
title: "Created!",
|
|
71
|
+
description: `ID: ${data.id}`,
|
|
72
|
+
color: "success",
|
|
73
|
+
});
|
|
74
|
+
};
|
|
75
|
+
const handleCreateCustom = (data: any) => {
|
|
76
|
+
createCustomResult.value = data;
|
|
77
|
+
toast.add({
|
|
78
|
+
title: "Product Created!",
|
|
79
|
+
description: data.name,
|
|
80
|
+
color: "success",
|
|
81
|
+
});
|
|
82
|
+
};
|
|
83
|
+
const handleCreateInitial = (data: any) => {
|
|
84
|
+
createInitialResult.value = data;
|
|
85
|
+
toast.add({
|
|
86
|
+
title: "Created with Initial Data!",
|
|
87
|
+
description: data.name,
|
|
88
|
+
color: "success",
|
|
89
|
+
});
|
|
90
|
+
};
|
|
91
|
+
const handleCreateSize = (size: string, data: any) => {
|
|
92
|
+
createSizeResults.value[size] = data;
|
|
93
|
+
toast.add({
|
|
94
|
+
title: `${size.toUpperCase()} Modal Created!`,
|
|
95
|
+
description: data.name,
|
|
96
|
+
color: "success",
|
|
97
|
+
});
|
|
98
|
+
};
|
|
99
|
+
const handleCreateProgrammatic = (data: any) => {
|
|
100
|
+
createProgrammaticResult.value = data;
|
|
101
|
+
createProgrammaticOpen.value = false;
|
|
102
|
+
toast.add({
|
|
103
|
+
title: "Programmatic Create!",
|
|
104
|
+
description: data.name,
|
|
105
|
+
color: "success",
|
|
106
|
+
});
|
|
107
|
+
};
|
|
108
|
+
const handleCreateError = (error: any) => {
|
|
109
|
+
createErrorResult.value = error;
|
|
110
|
+
toast.add({
|
|
111
|
+
title: "Create Error",
|
|
112
|
+
description: error.message,
|
|
113
|
+
color: "error",
|
|
114
|
+
});
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
// Update handlers
|
|
118
|
+
const handleUpdateBasic = (data: any) => {
|
|
119
|
+
updateBasicResult.value = data;
|
|
120
|
+
toast.add({ title: "Updated!", description: data.name, color: "success" });
|
|
121
|
+
};
|
|
122
|
+
const handleUpdateSelection = (data: any) => {
|
|
123
|
+
updateSelectionResult.value = data;
|
|
124
|
+
toast.add({
|
|
125
|
+
title: "User Updated!",
|
|
126
|
+
description: data.name,
|
|
127
|
+
color: "success",
|
|
128
|
+
});
|
|
129
|
+
};
|
|
130
|
+
const handleUpdateSize = (size: string, data: any) => {
|
|
131
|
+
updateSizeResults.value[size] = data;
|
|
132
|
+
toast.add({
|
|
133
|
+
title: `${size.toUpperCase()} Update!`,
|
|
134
|
+
description: data.name,
|
|
135
|
+
color: "success",
|
|
136
|
+
});
|
|
137
|
+
};
|
|
138
|
+
const handleUpdateProgrammatic = (data: any) => {
|
|
139
|
+
updateProgrammaticResult.value = data;
|
|
140
|
+
updateProgrammaticOpen.value = false;
|
|
141
|
+
toast.add({
|
|
142
|
+
title: "Programmatic Update!",
|
|
143
|
+
description: data.name,
|
|
144
|
+
color: "success",
|
|
145
|
+
});
|
|
146
|
+
};
|
|
147
|
+
const handleUpdateCustom = (data: any) => {
|
|
148
|
+
updateCustomResult.value = data;
|
|
149
|
+
toast.add({
|
|
150
|
+
title: "Settings Updated!",
|
|
151
|
+
description: data.name,
|
|
152
|
+
color: "success",
|
|
153
|
+
});
|
|
154
|
+
};
|
|
155
|
+
const handleUpdateError = (error: any) => {
|
|
156
|
+
updateErrorResult.value = error;
|
|
157
|
+
toast.add({
|
|
158
|
+
title: "Update Error",
|
|
159
|
+
description: error.message,
|
|
160
|
+
color: "error",
|
|
161
|
+
});
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
// Delete handlers
|
|
165
|
+
const handleDeleteBasic = (id: number) => {
|
|
166
|
+
deleteBasicDeleted.value = true;
|
|
167
|
+
toast.add({
|
|
168
|
+
title: "Deleted!",
|
|
169
|
+
description: `Item #${id}`,
|
|
170
|
+
color: "success",
|
|
171
|
+
});
|
|
172
|
+
};
|
|
173
|
+
const handleDeleteSelection = (id: number) => {
|
|
174
|
+
deleteSelectionDeleted.value = true;
|
|
175
|
+
deleteMockItems.value = deleteMockItems.value.filter(
|
|
176
|
+
(item) => item.id !== id,
|
|
177
|
+
);
|
|
178
|
+
toast.add({
|
|
179
|
+
title: "Item Deleted!",
|
|
180
|
+
description: `ID: ${id}`,
|
|
181
|
+
color: "success",
|
|
182
|
+
});
|
|
183
|
+
if (deleteMockItems.value.length === 0) {
|
|
184
|
+
setTimeout(() => {
|
|
185
|
+
deleteMockItems.value = [
|
|
186
|
+
{ id: 4, name: "New Project A", description: "Fresh start" },
|
|
187
|
+
{ id: 5, name: "New Project B", description: "Another project" },
|
|
188
|
+
];
|
|
189
|
+
deleteSelectionDeleted.value = false;
|
|
190
|
+
}, 2000);
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
const handleDeleteCustom = (id: number) => {
|
|
194
|
+
deleteCustomDeleted.value = true;
|
|
195
|
+
toast.add({
|
|
196
|
+
title: "Permanently Deleted!",
|
|
197
|
+
description: `ID: ${id}`,
|
|
198
|
+
color: "success",
|
|
199
|
+
});
|
|
200
|
+
};
|
|
201
|
+
const handleDeleteProgrammatic = (id: number) => {
|
|
202
|
+
deleteProgrammaticDeleted.value = true;
|
|
203
|
+
deleteProgrammaticOpen.value = false;
|
|
204
|
+
toast.add({
|
|
205
|
+
title: "Programmatic Delete!",
|
|
206
|
+
description: `ID: ${id}`,
|
|
207
|
+
color: "success",
|
|
208
|
+
});
|
|
209
|
+
};
|
|
210
|
+
const handleDeleteStyle = (style: string, id: number) => {
|
|
211
|
+
deleteStyleResults.value[style] = true;
|
|
212
|
+
toast.add({
|
|
213
|
+
title: `${style} Style Deleted!`,
|
|
214
|
+
description: `ID: ${id}`,
|
|
215
|
+
color: "success",
|
|
216
|
+
});
|
|
217
|
+
};
|
|
218
|
+
const handleDeleteError = (error: any) => {
|
|
219
|
+
deleteErrorResult.value = error;
|
|
220
|
+
toast.add({
|
|
221
|
+
title: "Delete Error",
|
|
222
|
+
description: error.message,
|
|
223
|
+
color: "error",
|
|
224
|
+
});
|
|
225
|
+
};
|
|
226
|
+
</script>
|
|
227
|
+
|
|
228
|
+
<template>
|
|
229
|
+
<UDashboardPanel grow>
|
|
230
|
+
<UContainer class="overflow-y-auto max-h-[calc(100vh-4rem)]">
|
|
231
|
+
<div class="py-8">
|
|
232
|
+
<h1 class="text-3xl font-bold mb-4">CRUD Components</h1>
|
|
233
|
+
<p class="text-gray-600 dark:text-gray-400 mb-8">
|
|
234
|
+
Modal-based CRUD operations with VACrudCreate, VACrudUpdate, and
|
|
235
|
+
VACrudDelete.
|
|
236
|
+
</p>
|
|
237
|
+
|
|
238
|
+
<!-- ========================================== -->
|
|
239
|
+
<!-- VACrudCreate Section -->
|
|
240
|
+
<!-- ========================================== -->
|
|
241
|
+
<h2 class="text-2xl font-semibold mb-4 mt-12 text-primary-500">
|
|
242
|
+
VACrudCreate
|
|
243
|
+
</h2>
|
|
244
|
+
<p class="text-gray-600 dark:text-gray-400 mb-6">
|
|
245
|
+
Modal component for creating new records with customizable triggers
|
|
246
|
+
and form slots.
|
|
247
|
+
</p>
|
|
248
|
+
|
|
249
|
+
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-12">
|
|
250
|
+
<!-- Basic Create -->
|
|
251
|
+
<UCard>
|
|
252
|
+
<template #header>
|
|
253
|
+
<h3 class="font-semibold flex items-center gap-2">
|
|
254
|
+
<UIcon name="i-lucide-plus-circle" class="text-primary-500" />
|
|
255
|
+
Basic Usage
|
|
256
|
+
</h3>
|
|
257
|
+
</template>
|
|
258
|
+
<p class="text-sm text-gray-500 mb-4">
|
|
259
|
+
Standard create modal with default trigger.
|
|
260
|
+
</p>
|
|
261
|
+
<VACrudCreate
|
|
262
|
+
endpoint="/api/test-items"
|
|
263
|
+
friendly-name="Item"
|
|
264
|
+
@created="handleCreateBasic"
|
|
265
|
+
>
|
|
266
|
+
<template #default="{ submit, loading }">
|
|
267
|
+
<form
|
|
268
|
+
@submit.prevent="submit({ name: 'New Test Item' })"
|
|
269
|
+
class="space-y-4"
|
|
270
|
+
>
|
|
271
|
+
<UFormField label="Item Name">
|
|
272
|
+
<UInput value="New Test Item" disabled />
|
|
273
|
+
</UFormField>
|
|
274
|
+
<div class="flex justify-end">
|
|
275
|
+
<VABtnSubmit :loading="loading" label="Create Item" />
|
|
276
|
+
</div>
|
|
277
|
+
</form>
|
|
278
|
+
</template>
|
|
279
|
+
</VACrudCreate>
|
|
280
|
+
<UAlert
|
|
281
|
+
v-if="createBasicResult"
|
|
282
|
+
class="mt-4"
|
|
283
|
+
color="success"
|
|
284
|
+
icon="i-lucide-check-circle"
|
|
285
|
+
title="Created!"
|
|
286
|
+
:description="`ID: ${createBasicResult.id}`"
|
|
287
|
+
/>
|
|
288
|
+
</UCard>
|
|
289
|
+
|
|
290
|
+
<!-- Custom Trigger -->
|
|
291
|
+
<UCard>
|
|
292
|
+
<template #header>
|
|
293
|
+
<h3 class="font-semibold flex items-center gap-2">
|
|
294
|
+
<UIcon name="i-lucide-package" class="text-green-500" />
|
|
295
|
+
Custom Trigger
|
|
296
|
+
</h3>
|
|
297
|
+
</template>
|
|
298
|
+
<p class="text-sm text-gray-500 mb-4">
|
|
299
|
+
Custom icon, color, and label.
|
|
300
|
+
</p>
|
|
301
|
+
<VACrudCreate
|
|
302
|
+
endpoint="/api/products"
|
|
303
|
+
friendly-name="Product"
|
|
304
|
+
trigger-label="Add Product"
|
|
305
|
+
trigger-icon="i-lucide-package"
|
|
306
|
+
trigger-color="success"
|
|
307
|
+
@created="handleCreateCustom"
|
|
308
|
+
>
|
|
309
|
+
<template #default="{ submit, loading }">
|
|
310
|
+
<form
|
|
311
|
+
@submit.prevent="submit({ name: 'New Product' })"
|
|
312
|
+
class="space-y-4"
|
|
313
|
+
>
|
|
314
|
+
<UFormField label="Product">
|
|
315
|
+
<UInput value="New Product" disabled />
|
|
316
|
+
</UFormField>
|
|
317
|
+
<div class="flex justify-end">
|
|
318
|
+
<VABtnSubmit
|
|
319
|
+
:loading="loading"
|
|
320
|
+
label="Add Product"
|
|
321
|
+
color="green"
|
|
322
|
+
/>
|
|
323
|
+
</div>
|
|
324
|
+
</form>
|
|
325
|
+
</template>
|
|
326
|
+
</VACrudCreate>
|
|
327
|
+
<UAlert
|
|
328
|
+
v-if="createCustomResult"
|
|
329
|
+
class="mt-4"
|
|
330
|
+
color="success"
|
|
331
|
+
title="Product Created!"
|
|
332
|
+
:description="createCustomResult.name"
|
|
333
|
+
/>
|
|
334
|
+
</UCard>
|
|
335
|
+
|
|
336
|
+
<!-- Initial Data -->
|
|
337
|
+
<UCard>
|
|
338
|
+
<template #header>
|
|
339
|
+
<h3 class="font-semibold flex items-center gap-2">
|
|
340
|
+
<UIcon name="i-lucide-file-edit" class="text-blue-500" />
|
|
341
|
+
With Initial Data
|
|
342
|
+
</h3>
|
|
343
|
+
</template>
|
|
344
|
+
<p class="text-sm text-gray-500 mb-4">Pre-populated form fields.</p>
|
|
345
|
+
<VACrudCreate
|
|
346
|
+
endpoint="/api/users"
|
|
347
|
+
friendly-name="User"
|
|
348
|
+
:initial-data="{ name: 'John Doe', email: 'john@example.com' }"
|
|
349
|
+
@created="handleCreateInitial"
|
|
350
|
+
>
|
|
351
|
+
<template #default="{ submit, loading }">
|
|
352
|
+
<form
|
|
353
|
+
@submit.prevent="
|
|
354
|
+
submit({ name: 'John Doe', email: 'john@example.com' })
|
|
355
|
+
"
|
|
356
|
+
class="space-y-4"
|
|
357
|
+
>
|
|
358
|
+
<UFormField label="Name">
|
|
359
|
+
<UInput value="John Doe" disabled />
|
|
360
|
+
</UFormField>
|
|
361
|
+
<UFormField label="Email">
|
|
362
|
+
<UInput value="john@example.com" disabled />
|
|
363
|
+
</UFormField>
|
|
364
|
+
<div class="flex justify-end">
|
|
365
|
+
<VABtnSubmit :loading="loading" label="Create User" />
|
|
366
|
+
</div>
|
|
367
|
+
</form>
|
|
368
|
+
</template>
|
|
369
|
+
</VACrudCreate>
|
|
370
|
+
<UAlert
|
|
371
|
+
v-if="createInitialResult"
|
|
372
|
+
class="mt-4"
|
|
373
|
+
color="success"
|
|
374
|
+
title="User Created!"
|
|
375
|
+
:description="createInitialResult.name"
|
|
376
|
+
/>
|
|
377
|
+
</UCard>
|
|
378
|
+
|
|
379
|
+
<!-- Programmatic Control -->
|
|
380
|
+
<UCard>
|
|
381
|
+
<template #header>
|
|
382
|
+
<h3 class="font-semibold flex items-center gap-2">
|
|
383
|
+
<UIcon name="i-lucide-code" class="text-orange-500" />
|
|
384
|
+
Programmatic Control
|
|
385
|
+
</h3>
|
|
386
|
+
</template>
|
|
387
|
+
<p class="text-sm text-gray-500 mb-4">Control via v-model:open.</p>
|
|
388
|
+
<div class="flex gap-2 mb-4">
|
|
389
|
+
<UButton
|
|
390
|
+
label="Open"
|
|
391
|
+
color="warning"
|
|
392
|
+
size="sm"
|
|
393
|
+
@click="createProgrammaticOpen = true"
|
|
394
|
+
/>
|
|
395
|
+
</div>
|
|
396
|
+
<VACrudCreate
|
|
397
|
+
v-model:open="createProgrammaticOpen"
|
|
398
|
+
endpoint="/api/items"
|
|
399
|
+
friendly-name="Item"
|
|
400
|
+
@created="handleCreateProgrammatic"
|
|
401
|
+
>
|
|
402
|
+
<template #default="{ submit, loading }">
|
|
403
|
+
<form
|
|
404
|
+
@submit.prevent="submit({ name: 'Programmatic Item' })"
|
|
405
|
+
class="space-y-4"
|
|
406
|
+
>
|
|
407
|
+
<p class="text-sm text-gray-500">Opened programmatically!</p>
|
|
408
|
+
<div class="flex justify-end">
|
|
409
|
+
<VABtnSubmit :loading="loading" label="Create" />
|
|
410
|
+
</div>
|
|
411
|
+
</form>
|
|
412
|
+
</template>
|
|
413
|
+
</VACrudCreate>
|
|
414
|
+
<UAlert
|
|
415
|
+
v-if="createProgrammaticResult"
|
|
416
|
+
class="mt-4"
|
|
417
|
+
color="warning"
|
|
418
|
+
title="Programmatic Success!"
|
|
419
|
+
/>
|
|
420
|
+
</UCard>
|
|
421
|
+
</div>
|
|
422
|
+
|
|
423
|
+
<!-- ========================================== -->
|
|
424
|
+
<!-- VACrudUpdate Section -->
|
|
425
|
+
<!-- ========================================== -->
|
|
426
|
+
<h2 class="text-2xl font-semibold mb-4 mt-12 text-blue-500">
|
|
427
|
+
VACrudUpdate
|
|
428
|
+
</h2>
|
|
429
|
+
<p class="text-gray-600 dark:text-gray-400 mb-6">
|
|
430
|
+
Update modal with data fetching, loading skeleton, and dirty tracking.
|
|
431
|
+
</p>
|
|
432
|
+
|
|
433
|
+
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-12">
|
|
434
|
+
<!-- Basic Update -->
|
|
435
|
+
<UCard>
|
|
436
|
+
<template #header>
|
|
437
|
+
<h3 class="font-semibold flex items-center gap-2">
|
|
438
|
+
<UIcon name="i-lucide-pencil" class="text-blue-500" />
|
|
439
|
+
Basic Update
|
|
440
|
+
</h3>
|
|
441
|
+
</template>
|
|
442
|
+
<p class="text-sm text-gray-500 mb-4">
|
|
443
|
+
Fetches data and tracks changes.
|
|
444
|
+
</p>
|
|
445
|
+
<VACrudUpdate
|
|
446
|
+
endpoint="/api/users"
|
|
447
|
+
:record-id="1"
|
|
448
|
+
friendly-name="User"
|
|
449
|
+
@updated="handleUpdateBasic"
|
|
450
|
+
>
|
|
451
|
+
<template
|
|
452
|
+
#default="{ submit, loading, data, isDirty, isFetching }"
|
|
453
|
+
>
|
|
454
|
+
<div v-if="isFetching" class="space-y-4">
|
|
455
|
+
<USkeleton class="h-4 w-full" />
|
|
456
|
+
<USkeleton class="h-4 w-3/4" />
|
|
457
|
+
<USkeleton class="h-4 w-1/2" />
|
|
458
|
+
</div>
|
|
459
|
+
<form v-else @submit.prevent="submit(data)" class="space-y-4">
|
|
460
|
+
<UFormField label="Name">
|
|
461
|
+
<UInput v-model="data.name" />
|
|
462
|
+
</UFormField>
|
|
463
|
+
<UFormField label="Email">
|
|
464
|
+
<UInput v-model="data.email" />
|
|
465
|
+
</UFormField>
|
|
466
|
+
<div class="flex justify-end">
|
|
467
|
+
<VABtnSubmit :loading="loading" label="Save Changes" />
|
|
468
|
+
</div>
|
|
469
|
+
</form>
|
|
470
|
+
</template>
|
|
471
|
+
</VACrudUpdate>
|
|
472
|
+
<UAlert
|
|
473
|
+
v-if="updateBasicResult"
|
|
474
|
+
class="mt-4"
|
|
475
|
+
color="success"
|
|
476
|
+
title="Updated!"
|
|
477
|
+
:description="updateBasicResult.name"
|
|
478
|
+
/>
|
|
479
|
+
</UCard>
|
|
480
|
+
|
|
481
|
+
<!-- User Selection -->
|
|
482
|
+
<UCard>
|
|
483
|
+
<template #header>
|
|
484
|
+
<h3 class="font-semibold flex items-center gap-2">
|
|
485
|
+
<UIcon name="i-lucide-users" class="text-indigo-500" />
|
|
486
|
+
Dynamic Selection
|
|
487
|
+
</h3>
|
|
488
|
+
</template>
|
|
489
|
+
<p class="text-sm text-gray-500 mb-4">
|
|
490
|
+
Select different users to edit.
|
|
491
|
+
</p>
|
|
492
|
+
|
|
493
|
+
<VACrudUpdate
|
|
494
|
+
:endpoint="'/api/users'"
|
|
495
|
+
:record-id="updateSelectedUserId"
|
|
496
|
+
friendly-name="User"
|
|
497
|
+
@updated="handleUpdateSelection"
|
|
498
|
+
>
|
|
499
|
+
<template
|
|
500
|
+
#default="{ submit, loading, data, isFetching, isDirty }"
|
|
501
|
+
>
|
|
502
|
+
<div v-if="isFetching" class="space-y-4">
|
|
503
|
+
<USkeleton class="h-4 w-full" />
|
|
504
|
+
<USkeleton class="h-4 w-3/4" />
|
|
505
|
+
</div>
|
|
506
|
+
<form v-else @submit.prevent="submit(data)" class="space-y-4">
|
|
507
|
+
<div class="flex items-center gap-2">
|
|
508
|
+
<span class="text-xs text-gray-500"
|
|
509
|
+
>Editing User ID: {{ updateSelectedUserId }}</span
|
|
510
|
+
>
|
|
511
|
+
</div>
|
|
512
|
+
<UFormField label="Name">
|
|
513
|
+
<UInput v-model="data.name" />
|
|
514
|
+
</UFormField>
|
|
515
|
+
<div class="flex justify-end">
|
|
516
|
+
<VABtnSubmit :loading="loading" label="Update" />
|
|
517
|
+
</div>
|
|
518
|
+
</form>
|
|
519
|
+
</template>
|
|
520
|
+
</VACrudUpdate>
|
|
521
|
+
<UAlert
|
|
522
|
+
v-if="updateSelectionResult"
|
|
523
|
+
class="mt-4"
|
|
524
|
+
color="success"
|
|
525
|
+
title="User Updated!"
|
|
526
|
+
/>
|
|
527
|
+
</UCard>
|
|
528
|
+
|
|
529
|
+
<!-- Programmatic Update -->
|
|
530
|
+
<UCard>
|
|
531
|
+
<template #header>
|
|
532
|
+
<h3 class="font-semibold flex items-center gap-2">
|
|
533
|
+
<UIcon name="i-lucide-code" class="text-orange-500" />
|
|
534
|
+
Programmatic Control
|
|
535
|
+
</h3>
|
|
536
|
+
</template>
|
|
537
|
+
<p class="text-sm text-gray-500 mb-4">External modal control.</p>
|
|
538
|
+
<div class="flex gap-2 mb-4">
|
|
539
|
+
<UButton
|
|
540
|
+
label="Open Update"
|
|
541
|
+
color="warning"
|
|
542
|
+
size="sm"
|
|
543
|
+
@click="updateProgrammaticOpen = true"
|
|
544
|
+
/>
|
|
545
|
+
</div>
|
|
546
|
+
<VACrudUpdate
|
|
547
|
+
v-model:open="updateProgrammaticOpen"
|
|
548
|
+
endpoint="/api/users"
|
|
549
|
+
:record-id="2"
|
|
550
|
+
friendly-name="User"
|
|
551
|
+
@updated="handleUpdateProgrammatic"
|
|
552
|
+
>
|
|
553
|
+
<template
|
|
554
|
+
#default="{ submit, loading, data, isFetching, isDirty }"
|
|
555
|
+
>
|
|
556
|
+
<div v-if="isFetching" class="space-y-4">
|
|
557
|
+
<USkeleton class="h-4 w-full" />
|
|
558
|
+
<USkeleton class="h-4 w-3/4" />
|
|
559
|
+
</div>
|
|
560
|
+
<form v-else @submit.prevent="submit(data)" class="space-y-4">
|
|
561
|
+
<UFormField label="Name">
|
|
562
|
+
<UInput v-model="data.name" />
|
|
563
|
+
</UFormField>
|
|
564
|
+
<div class="flex justify-end">
|
|
565
|
+
<VABtnSubmit :loading="loading" label="Update" />
|
|
566
|
+
</div>
|
|
567
|
+
</form>
|
|
568
|
+
</template>
|
|
569
|
+
</VACrudUpdate>
|
|
570
|
+
<UAlert
|
|
571
|
+
v-if="updateProgrammaticResult"
|
|
572
|
+
class="mt-4"
|
|
573
|
+
color="warning"
|
|
574
|
+
title="Programmatic Update!"
|
|
575
|
+
/>
|
|
576
|
+
</UCard>
|
|
577
|
+
|
|
578
|
+
<!-- Custom Style Update -->
|
|
579
|
+
<UCard>
|
|
580
|
+
<template #header>
|
|
581
|
+
<h3 class="font-semibold flex items-center gap-2">
|
|
582
|
+
<UIcon name="i-lucide-settings-2" class="text-green-500" />
|
|
583
|
+
Custom Trigger Style
|
|
584
|
+
</h3>
|
|
585
|
+
</template>
|
|
586
|
+
<p class="text-sm text-gray-500 mb-4">Styled trigger button.</p>
|
|
587
|
+
<VACrudUpdate
|
|
588
|
+
endpoint="/api/users"
|
|
589
|
+
:record-id="3"
|
|
590
|
+
friendly-name="User"
|
|
591
|
+
trigger-label="Edit Settings"
|
|
592
|
+
trigger-icon="i-lucide-settings"
|
|
593
|
+
trigger-color="green"
|
|
594
|
+
@updated="handleUpdateCustom"
|
|
595
|
+
>
|
|
596
|
+
<template #default="{ submit, loading, data, isFetching }">
|
|
597
|
+
<div v-if="isFetching" class="space-y-4">
|
|
598
|
+
<USkeleton class="h-4 w-full" />
|
|
599
|
+
</div>
|
|
600
|
+
<form v-else @submit.prevent="submit(data)" class="space-y-4">
|
|
601
|
+
<UFormField label="Name">
|
|
602
|
+
<UInput v-model="data.name" />
|
|
603
|
+
</UFormField>
|
|
604
|
+
|
|
605
|
+
<div class="flex justify-end">
|
|
606
|
+
<VABtnSubmit
|
|
607
|
+
:loading="loading"
|
|
608
|
+
label="Save Settings"
|
|
609
|
+
color="primary"
|
|
610
|
+
/>
|
|
611
|
+
</div>
|
|
612
|
+
</form>
|
|
613
|
+
</template>
|
|
614
|
+
</VACrudUpdate>
|
|
615
|
+
<UAlert
|
|
616
|
+
v-if="updateCustomResult"
|
|
617
|
+
class="mt-4"
|
|
618
|
+
color="success"
|
|
619
|
+
title="Settings Updated!"
|
|
620
|
+
/>
|
|
621
|
+
</UCard>
|
|
622
|
+
</div>
|
|
623
|
+
|
|
624
|
+
<!-- ========================================== -->
|
|
625
|
+
<!-- VACrudDelete Section -->
|
|
626
|
+
<!-- ========================================== -->
|
|
627
|
+
<h2 class="text-2xl font-semibold mb-4 mt-12 text-red-500">
|
|
628
|
+
VACrudDelete
|
|
629
|
+
</h2>
|
|
630
|
+
<p class="text-gray-600 dark:text-gray-400 mb-6">
|
|
631
|
+
Delete confirmation modal with VAModalConfirm integration.
|
|
632
|
+
</p>
|
|
633
|
+
|
|
634
|
+
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-12">
|
|
635
|
+
<!-- Basic Delete -->
|
|
636
|
+
<UCard>
|
|
637
|
+
<template #header>
|
|
638
|
+
<h3 class="font-semibold flex items-center gap-2">
|
|
639
|
+
<UIcon name="i-lucide-trash-2" class="text-red-500" />
|
|
640
|
+
Basic Delete
|
|
641
|
+
</h3>
|
|
642
|
+
</template>
|
|
643
|
+
<p class="text-sm text-gray-500 mb-4">
|
|
644
|
+
Standard delete confirmation.
|
|
645
|
+
</p>
|
|
646
|
+
<div
|
|
647
|
+
class="flex items-center justify-between p-3 bg-gray-50 dark:bg-gray-800 rounded-lg mb-4"
|
|
648
|
+
>
|
|
649
|
+
<div>
|
|
650
|
+
<p class="font-medium">Project Alpha</p>
|
|
651
|
+
<p class="text-sm text-gray-500">ID: 1</p>
|
|
652
|
+
</div>
|
|
653
|
+
<VACrudDelete
|
|
654
|
+
endpoint="/api/projects"
|
|
655
|
+
:record-id="1"
|
|
656
|
+
item-name="Project Alpha"
|
|
657
|
+
@deleted="handleDeleteBasic"
|
|
658
|
+
/>
|
|
659
|
+
</div>
|
|
660
|
+
<UAlert
|
|
661
|
+
v-if="deleteBasicDeleted"
|
|
662
|
+
color="success"
|
|
663
|
+
icon="i-lucide-check-circle"
|
|
664
|
+
title="Deleted Successfully"
|
|
665
|
+
description="Project Alpha removed"
|
|
666
|
+
/>
|
|
667
|
+
</UCard>
|
|
668
|
+
|
|
669
|
+
<!-- Item List Delete -->
|
|
670
|
+
<UCard>
|
|
671
|
+
<template #header>
|
|
672
|
+
<h3 class="font-semibold flex items-center gap-2">
|
|
673
|
+
<UIcon name="i-lucide-list" class="text-blue-500" />
|
|
674
|
+
Item List with Delete
|
|
675
|
+
</h3>
|
|
676
|
+
</template>
|
|
677
|
+
<p class="text-sm text-gray-500 mb-4">Select and delete items.</p>
|
|
678
|
+
<div
|
|
679
|
+
class="flex items-center justify-between p-3 bg-gray-50 dark:bg-gray-800 rounded-lg mb-4"
|
|
680
|
+
>
|
|
681
|
+
<div>
|
|
682
|
+
<p class="font-medium">
|
|
683
|
+
{{
|
|
684
|
+
deleteMockItems.find((i) => i.id === deleteSelectedItemId)
|
|
685
|
+
?.name
|
|
686
|
+
}}
|
|
687
|
+
</p>
|
|
688
|
+
<p class="text-sm text-gray-500">
|
|
689
|
+
ID: {{ deleteSelectedItemId }}
|
|
690
|
+
</p>
|
|
691
|
+
</div>
|
|
692
|
+
<VACrudDelete
|
|
693
|
+
v-if="
|
|
694
|
+
deleteMockItems.find((i) => i.id === deleteSelectedItemId)
|
|
695
|
+
"
|
|
696
|
+
endpoint="/api/items"
|
|
697
|
+
:record-id="deleteSelectedItemId"
|
|
698
|
+
:item-name="
|
|
699
|
+
deleteMockItems.find((i) => i.id === deleteSelectedItemId)
|
|
700
|
+
?.name
|
|
701
|
+
"
|
|
702
|
+
@deleted="handleDeleteSelection"
|
|
703
|
+
/>
|
|
704
|
+
</div>
|
|
705
|
+
<p class="text-xs text-gray-500 mb-2">
|
|
706
|
+
Remaining: {{ deleteMockItems.length }}
|
|
707
|
+
</p>
|
|
708
|
+
<UAlert
|
|
709
|
+
v-if="deleteSelectionDeleted"
|
|
710
|
+
color="success"
|
|
711
|
+
icon="i-lucide-trash"
|
|
712
|
+
title="Item Deleted"
|
|
713
|
+
description="Removed from list"
|
|
714
|
+
/>
|
|
715
|
+
</UCard>
|
|
716
|
+
|
|
717
|
+
<!-- Custom Confirm Message -->
|
|
718
|
+
<UCard>
|
|
719
|
+
<template #header>
|
|
720
|
+
<h3 class="font-semibold flex items-center gap-2">
|
|
721
|
+
<UIcon name="i-lucide-message-square" class="text-purple-500" />
|
|
722
|
+
Custom Confirm Message
|
|
723
|
+
</h3>
|
|
724
|
+
</template>
|
|
725
|
+
<p class="text-sm text-gray-500 mb-4">Custom confirmation text.</p>
|
|
726
|
+
<div
|
|
727
|
+
class="flex items-center justify-between p-3 bg-gray-50 dark:bg-gray-800 rounded-lg mb-4"
|
|
728
|
+
>
|
|
729
|
+
<div>
|
|
730
|
+
<p class="font-medium">Important Document</p>
|
|
731
|
+
<p class="text-sm text-gray-500">ID: 42</p>
|
|
732
|
+
</div>
|
|
733
|
+
<VACrudDelete
|
|
734
|
+
endpoint="/api/documents"
|
|
735
|
+
:record-id="42"
|
|
736
|
+
item-name="Important Document"
|
|
737
|
+
confirm-title="⚠️ Permanent Deletion"
|
|
738
|
+
confirm-message="Critical data cannot be recovered. Are you sure?"
|
|
739
|
+
trigger-color="warning"
|
|
740
|
+
@deleted="handleDeleteCustom"
|
|
741
|
+
/>
|
|
742
|
+
</div>
|
|
743
|
+
<UAlert
|
|
744
|
+
v-if="deleteCustomDeleted"
|
|
745
|
+
color="warning"
|
|
746
|
+
icon="i-lucide-alert-circle"
|
|
747
|
+
title="Permanently Deleted"
|
|
748
|
+
/>
|
|
749
|
+
</UCard>
|
|
750
|
+
|
|
751
|
+
<!-- Programmatic Delete -->
|
|
752
|
+
<UCard>
|
|
753
|
+
<template #header>
|
|
754
|
+
<h3 class="font-semibold flex items-center gap-2">
|
|
755
|
+
<UIcon name="i-lucide-code" class="text-orange-500" />
|
|
756
|
+
Programmatic Control
|
|
757
|
+
</h3>
|
|
758
|
+
</template>
|
|
759
|
+
<p class="text-sm text-gray-500 mb-4">External modal control.</p>
|
|
760
|
+
<div class="flex gap-2 mb-4">
|
|
761
|
+
<UButton
|
|
762
|
+
label="Open Delete"
|
|
763
|
+
color="error"
|
|
764
|
+
size="sm"
|
|
765
|
+
@click="deleteProgrammaticOpen = true"
|
|
766
|
+
/>
|
|
767
|
+
</div>
|
|
768
|
+
<VACrudDelete
|
|
769
|
+
v-model:open="deleteProgrammaticOpen"
|
|
770
|
+
endpoint="/api/items"
|
|
771
|
+
:record-id="99"
|
|
772
|
+
item-name="Programmatic Item"
|
|
773
|
+
@deleted="handleDeleteProgrammatic"
|
|
774
|
+
/>
|
|
775
|
+
</UCard>
|
|
776
|
+
|
|
777
|
+
<!-- Trigger Style Variants -->
|
|
778
|
+
<UCard class="lg:col-span-2">
|
|
779
|
+
<template #header>
|
|
780
|
+
<h3 class="font-semibold flex items-center gap-2">
|
|
781
|
+
<UIcon name="i-lucide-palette" class="text-green-500" />
|
|
782
|
+
Trigger Style Variants
|
|
783
|
+
</h3>
|
|
784
|
+
</template>
|
|
785
|
+
<p class="text-sm text-gray-500 mb-4">
|
|
786
|
+
Different trigger button styles.
|
|
787
|
+
</p>
|
|
788
|
+
<div class="grid grid-cols-2 md:grid-cols-4 gap-4">
|
|
789
|
+
<div
|
|
790
|
+
class="flex items-center justify-between p-3 bg-gray-50 dark:bg-gray-800 rounded-lg"
|
|
791
|
+
>
|
|
792
|
+
<span class="text-sm">Ghost (Default)</span>
|
|
793
|
+
<VACrudDelete
|
|
794
|
+
endpoint="/api/items"
|
|
795
|
+
:record-id="101"
|
|
796
|
+
item-name="Ghost Item"
|
|
797
|
+
trigger-variant="ghost"
|
|
798
|
+
@deleted="(id) => handleDeleteStyle('ghost', id)"
|
|
799
|
+
/>
|
|
800
|
+
</div>
|
|
801
|
+
<div
|
|
802
|
+
class="flex items-center justify-between p-3 bg-gray-50 dark:bg-gray-800 rounded-lg"
|
|
803
|
+
>
|
|
804
|
+
<span class="text-sm">Solid</span>
|
|
805
|
+
<VACrudDelete
|
|
806
|
+
endpoint="/api/items"
|
|
807
|
+
:record-id="102"
|
|
808
|
+
item-name="Solid Item"
|
|
809
|
+
trigger-variant="solid"
|
|
810
|
+
@deleted="(id) => handleDeleteStyle('solid', id)"
|
|
811
|
+
/>
|
|
812
|
+
</div>
|
|
813
|
+
<div
|
|
814
|
+
class="flex items-center justify-between p-3 bg-gray-50 dark:bg-gray-800 rounded-lg"
|
|
815
|
+
>
|
|
816
|
+
<span class="text-sm">With Label</span>
|
|
817
|
+
<VACrudDelete
|
|
818
|
+
endpoint="/api/items"
|
|
819
|
+
:record-id="103"
|
|
820
|
+
item-name="Labeled Item"
|
|
821
|
+
trigger-label="Remove"
|
|
822
|
+
trigger-icon="i-lucide-x"
|
|
823
|
+
trigger-variant="outline"
|
|
824
|
+
@deleted="(id) => handleDeleteStyle('label', id)"
|
|
825
|
+
/>
|
|
826
|
+
</div>
|
|
827
|
+
<div
|
|
828
|
+
class="flex items-center justify-between p-3 bg-gray-50 dark:bg-gray-800 rounded-lg"
|
|
829
|
+
>
|
|
830
|
+
<span class="text-sm">Large</span>
|
|
831
|
+
<VACrudDelete
|
|
832
|
+
endpoint="/api/items"
|
|
833
|
+
:record-id="104"
|
|
834
|
+
item-name="Large Item"
|
|
835
|
+
trigger-size="md"
|
|
836
|
+
@deleted="(id) => handleDeleteStyle('large', id)"
|
|
837
|
+
/>
|
|
838
|
+
</div>
|
|
839
|
+
</div>
|
|
840
|
+
<div class="mt-4 flex flex-wrap gap-2">
|
|
841
|
+
<UBadge
|
|
842
|
+
v-for="(deleted, style) in deleteStyleResults"
|
|
843
|
+
:key="style"
|
|
844
|
+
color="success"
|
|
845
|
+
>
|
|
846
|
+
{{ style }} deleted
|
|
847
|
+
</UBadge>
|
|
848
|
+
</div>
|
|
849
|
+
</UCard>
|
|
850
|
+
</div>
|
|
851
|
+
|
|
852
|
+
<!-- ========================================== -->
|
|
853
|
+
<!-- API Reference -->
|
|
854
|
+
<!-- ========================================== -->
|
|
855
|
+
<h2 class="text-2xl font-semibold mb-4 mt-12">API Reference</h2>
|
|
856
|
+
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
|
|
857
|
+
<UCard>
|
|
858
|
+
<template #header>
|
|
859
|
+
<h3 class="font-semibold text-primary-500">VACrudCreate</h3>
|
|
860
|
+
</template>
|
|
861
|
+
<div class="space-y-2 text-sm">
|
|
862
|
+
<p><strong>Props:</strong></p>
|
|
863
|
+
<ul class="text-gray-600 dark:text-gray-400 space-y-1 ml-4">
|
|
864
|
+
<li>• endpoint (required)</li>
|
|
865
|
+
<li>• friendlyName (required)</li>
|
|
866
|
+
<li>• triggerLabel, triggerIcon</li>
|
|
867
|
+
<li>• triggerColor, modalSize</li>
|
|
868
|
+
<li>• initialData, open</li>
|
|
869
|
+
</ul>
|
|
870
|
+
<p class="mt-3"><strong>Events:</strong></p>
|
|
871
|
+
<ul class="text-gray-600 dark:text-gray-400 space-y-1 ml-4">
|
|
872
|
+
<li>• @created(data)</li>
|
|
873
|
+
<li>• @error(error)</li>
|
|
874
|
+
<li>• @opened, @closed</li>
|
|
875
|
+
</ul>
|
|
876
|
+
<p class="mt-3">
|
|
877
|
+
<strong>Slot:</strong> { submit, loading, error }
|
|
878
|
+
</p>
|
|
879
|
+
</div>
|
|
880
|
+
</UCard>
|
|
881
|
+
|
|
882
|
+
<UCard>
|
|
883
|
+
<template #header>
|
|
884
|
+
<h3 class="font-semibold text-blue-500">VACrudUpdate</h3>
|
|
885
|
+
</template>
|
|
886
|
+
<div class="space-y-2 text-sm">
|
|
887
|
+
<p><strong>Props:</strong></p>
|
|
888
|
+
<ul class="text-gray-600 dark:text-gray-400 space-y-1 ml-4">
|
|
889
|
+
<li>• endpoint (required)</li>
|
|
890
|
+
<li>• recordId (required)</li>
|
|
891
|
+
<li>• friendlyName</li>
|
|
892
|
+
<li>• triggerLabel, triggerIcon</li>
|
|
893
|
+
<li>• modalSize, open</li>
|
|
894
|
+
</ul>
|
|
895
|
+
<p class="mt-3"><strong>Events:</strong></p>
|
|
896
|
+
<ul class="text-gray-600 dark:text-gray-400 space-y-1 ml-4">
|
|
897
|
+
<li>• @updated(data)</li>
|
|
898
|
+
<li>• @error(error)</li>
|
|
899
|
+
<li>• @opened, @closed</li>
|
|
900
|
+
</ul>
|
|
901
|
+
<p class="mt-3">
|
|
902
|
+
<strong>Slot:</strong> { submit, loading, error, data, isDirty,
|
|
903
|
+
isFetching }
|
|
904
|
+
</p>
|
|
905
|
+
</div>
|
|
906
|
+
</UCard>
|
|
907
|
+
|
|
908
|
+
<UCard>
|
|
909
|
+
<template #header>
|
|
910
|
+
<h3 class="font-semibold text-red-500">VACrudDelete</h3>
|
|
911
|
+
</template>
|
|
912
|
+
<div class="space-y-2 text-sm">
|
|
913
|
+
<p><strong>Props:</strong></p>
|
|
914
|
+
<ul class="text-gray-600 dark:text-gray-400 space-y-1 ml-4">
|
|
915
|
+
<li>• endpoint (required)</li>
|
|
916
|
+
<li>• recordId (required)</li>
|
|
917
|
+
<li>• itemName</li>
|
|
918
|
+
<li>• triggerLabel, triggerIcon</li>
|
|
919
|
+
<li>• confirmTitle, confirmMessage</li>
|
|
920
|
+
</ul>
|
|
921
|
+
<p class="mt-3"><strong>Events:</strong></p>
|
|
922
|
+
<ul class="text-gray-600 dark:text-gray-400 space-y-1 ml-4">
|
|
923
|
+
<li>• @deleted(id)</li>
|
|
924
|
+
<li>• @error(error)</li>
|
|
925
|
+
<li>• @opened, @closed</li>
|
|
926
|
+
</ul>
|
|
927
|
+
<p class="mt-3">
|
|
928
|
+
<strong>Slot:</strong> No slot (uses VAModalConfirm)
|
|
929
|
+
</p>
|
|
930
|
+
</div>
|
|
931
|
+
</UCard>
|
|
932
|
+
</div>
|
|
933
|
+
</div>
|
|
934
|
+
</UContainer>
|
|
935
|
+
</UDashboardPanel>
|
|
936
|
+
</template>
|