create-better-t-stack 3.12.5 → 3.12.6
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/dist/cli.mjs +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{src-D3EHRs6z.mjs → src-Ci2sRCrb.mjs} +31 -20
- package/package.json +2 -2
- package/templates/auth/better-auth/convex/native/uniwind/components/sign-in.tsx.hbs +32 -50
- package/templates/auth/better-auth/convex/native/uniwind/components/sign-up.tsx.hbs +40 -57
- package/templates/auth/better-auth/native/uniwind/components/sign-in.tsx.hbs +32 -35
- package/templates/auth/better-auth/native/uniwind/components/sign-up.tsx.hbs +49 -37
- package/templates/auth/better-auth/web/nuxt/app/components/SignInForm.vue.hbs +40 -35
- package/templates/auth/better-auth/web/nuxt/app/components/SignUpForm.vue.hbs +47 -40
- package/templates/auth/better-auth/web/nuxt/app/middleware/auth.ts.hbs +9 -7
- package/templates/auth/better-auth/web/nuxt/app/pages/dashboard.vue.hbs +61 -29
- package/templates/auth/better-auth/web/nuxt/app/pages/login.vue.hbs +6 -3
- package/templates/backend/server/elysia/src/index.ts.hbs +8 -3
- package/templates/backend/server/express/src/index.ts.hbs +8 -3
- package/templates/backend/server/fastify/src/index.ts.hbs +8 -3
- package/templates/backend/server/hono/src/index.ts.hbs +16 -6
- package/templates/examples/ai/fullstack/next/src/app/api/ai/route.ts.hbs +8 -3
- package/templates/examples/ai/fullstack/tanstack-start/src/routes/api/ai/$.ts.hbs +8 -3
- package/templates/examples/ai/native/bare/polyfills.js +14 -11
- package/templates/examples/ai/native/uniwind/app/(drawer)/ai.tsx.hbs +104 -124
- package/templates/examples/ai/web/nuxt/app/pages/ai.vue.hbs +22 -22
- package/templates/examples/todo/native/uniwind/app/(drawer)/todos.tsx.hbs +67 -80
- package/templates/examples/todo/web/nuxt/app/pages/todos.vue.hbs +115 -90
- package/templates/frontend/native/uniwind/app/(drawer)/index.tsx.hbs +60 -54
- package/templates/frontend/native/uniwind/app/+not-found.tsx.hbs +16 -21
- package/templates/frontend/native/uniwind/app/modal.tsx.hbs +18 -34
- package/templates/frontend/native/uniwind/package.json.hbs +10 -10
- package/templates/frontend/nuxt/app/components/Header.vue.hbs +25 -29
- package/templates/frontend/nuxt/app/layouts/default.vue.hbs +7 -8
- package/templates/frontend/nuxt/app/pages/index.vue.hbs +58 -39
- package/templates/frontend/nuxt/package.json.hbs +1 -1
- package/templates/frontend/react/web-base/src/components/ui/skeleton.tsx.hbs +5 -5
- package/templates/frontend/nuxt/app/components/Loader.vue.hbs +0 -5
- package/templates/frontend/nuxt/app/components/ModeToggle.vue.hbs +0 -25
|
@@ -1,13 +1,5 @@
|
|
|
1
1
|
import { useState } from "react";
|
|
2
|
-
import {
|
|
3
|
-
View,
|
|
4
|
-
Text,
|
|
5
|
-
TextInput,
|
|
6
|
-
ScrollView,
|
|
7
|
-
ActivityIndicator,
|
|
8
|
-
Alert,
|
|
9
|
-
Pressable,
|
|
10
|
-
} from "react-native";
|
|
2
|
+
import { View, Text, ScrollView, Alert } from "react-native";
|
|
11
3
|
import { Ionicons } from "@expo/vector-icons";
|
|
12
4
|
{{#if (eq backend "convex")}}
|
|
13
5
|
import { useMutation, useQuery } from "convex/react";
|
|
@@ -25,7 +17,7 @@ import { Container } from "@/components/container";
|
|
|
25
17
|
import { trpc } from "@/utils/trpc";
|
|
26
18
|
{{/if}}
|
|
27
19
|
{{/unless}}
|
|
28
|
-
import {
|
|
20
|
+
import { Button, Checkbox, Chip, Spinner, Surface, TextField, useThemeColor } from "heroui-native";
|
|
29
21
|
|
|
30
22
|
export default function TodosScreen() {
|
|
31
23
|
const [newTodoText, setNewTodoText] = useState("");
|
|
@@ -76,7 +68,6 @@ export default function TodosScreen() {
|
|
|
76
68
|
{{/if}}
|
|
77
69
|
|
|
78
70
|
const mutedColor = useThemeColor("muted");
|
|
79
|
-
const accentColor = useThemeColor("accent");
|
|
80
71
|
const dangerColor = useThemeColor("danger");
|
|
81
72
|
const foregroundColor = useThemeColor("foreground");
|
|
82
73
|
|
|
@@ -135,12 +126,10 @@ export default function TodosScreen() {
|
|
|
135
126
|
|
|
136
127
|
return (
|
|
137
128
|
<Container>
|
|
138
|
-
<ScrollView className="flex-1" contentContainerClassName="p-
|
|
139
|
-
<View className="mb-
|
|
140
|
-
<View className="flex-row items-center justify-between
|
|
141
|
-
<Text className="text-
|
|
142
|
-
Todo List
|
|
143
|
-
</Text>
|
|
129
|
+
<ScrollView className="flex-1" contentContainerClassName="p-4">
|
|
130
|
+
<View className="py-4 mb-4">
|
|
131
|
+
<View className="flex-row items-center justify-between">
|
|
132
|
+
<Text className="text-2xl font-semibold text-foreground tracking-tight">Tasks</Text>
|
|
144
133
|
{totalCount > 0 && (
|
|
145
134
|
<Chip variant="secondary" color="accent" size="sm">
|
|
146
135
|
<Chip.Label>
|
|
@@ -151,140 +140,138 @@ export default function TodosScreen() {
|
|
|
151
140
|
</View>
|
|
152
141
|
</View>
|
|
153
142
|
|
|
154
|
-
<
|
|
155
|
-
<View className="flex-row items-center gap-
|
|
143
|
+
<Surface variant="secondary" className="mb-4 p-3 rounded-lg">
|
|
144
|
+
<View className="flex-row items-center gap-2">
|
|
156
145
|
<View className="flex-1">
|
|
157
|
-
<
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
146
|
+
<TextField>
|
|
147
|
+
<TextField.Input
|
|
148
|
+
value={newTodoText}
|
|
149
|
+
onChangeText={setNewTodoText}
|
|
150
|
+
placeholder="Add a new task..."
|
|
151
|
+
{{#unless (eq backend "convex")}}
|
|
152
|
+
editable={!createMutation.isPending}
|
|
153
|
+
{{/unless}}
|
|
154
|
+
onSubmitEditing={handleAddTodo}
|
|
155
|
+
returnKeyType="done"
|
|
156
|
+
/>
|
|
157
|
+
</TextField>
|
|
169
158
|
</View>
|
|
170
|
-
<
|
|
171
|
-
|
|
159
|
+
<Button
|
|
160
|
+
isIconOnly
|
|
172
161
|
{{#if (eq backend "convex")}}
|
|
173
|
-
|
|
174
|
-
|
|
162
|
+
variant={!newTodoText.trim() ? "secondary" : "primary"}
|
|
163
|
+
isDisabled={!newTodoText.trim()}
|
|
175
164
|
{{else}}
|
|
176
|
-
|
|
177
|
-
|
|
165
|
+
variant={createMutation.isPending || !newTodoText.trim() ? "secondary" : "primary"}
|
|
166
|
+
isDisabled={createMutation.isPending || !newTodoText.trim()}
|
|
178
167
|
{{/if}}
|
|
168
|
+
onPress={handleAddTodo}
|
|
169
|
+
size="sm"
|
|
179
170
|
>
|
|
180
171
|
{{#if (eq backend "convex")}}
|
|
181
172
|
<Ionicons
|
|
182
173
|
name="add"
|
|
183
|
-
size={
|
|
174
|
+
size={20}
|
|
184
175
|
color={newTodoText.trim() ? foregroundColor : mutedColor}
|
|
185
176
|
/>
|
|
186
177
|
{{else}}
|
|
187
178
|
{createMutation.isPending ? (
|
|
188
|
-
<
|
|
179
|
+
<Spinner size="sm" color="default" />
|
|
189
180
|
) : (
|
|
190
181
|
<Ionicons
|
|
191
182
|
name="add"
|
|
192
|
-
size={
|
|
183
|
+
size={20}
|
|
193
184
|
color={(createMutation.isPending || !newTodoText.trim()) ? mutedColor : foregroundColor}
|
|
194
185
|
/>
|
|
195
186
|
)}
|
|
196
187
|
{{/if}}
|
|
197
|
-
</
|
|
188
|
+
</Button>
|
|
198
189
|
</View>
|
|
199
|
-
</
|
|
190
|
+
</Surface>
|
|
200
191
|
|
|
201
192
|
{{#if (eq backend "convex")}}
|
|
202
193
|
{isLoading && (
|
|
203
194
|
<View className="items-center justify-center py-12">
|
|
204
|
-
<
|
|
205
|
-
<Text className="text-muted mt-
|
|
195
|
+
<Spinner size="lg" />
|
|
196
|
+
<Text className="text-muted text-sm mt-3">Loading tasks...</Text>
|
|
206
197
|
</View>
|
|
207
198
|
)}
|
|
208
199
|
|
|
209
200
|
{todos && todos.length === 0 && !isLoading && (
|
|
210
|
-
<
|
|
211
|
-
<Ionicons name="checkbox-outline" size={
|
|
212
|
-
<Text className="text-foreground
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
<Text className="text-muted text-center">
|
|
216
|
-
Add your first task to get started!
|
|
217
|
-
</Text>
|
|
218
|
-
</Card>
|
|
201
|
+
<Surface variant="secondary" className="items-center justify-center py-10 rounded-lg">
|
|
202
|
+
<Ionicons name="checkbox-outline" size={40} color={mutedColor} />
|
|
203
|
+
<Text className="text-foreground font-medium mt-3">No tasks yet</Text>
|
|
204
|
+
<Text className="text-muted text-xs mt-1">Add your first task to get started</Text>
|
|
205
|
+
</Surface>
|
|
219
206
|
)}
|
|
220
207
|
|
|
221
208
|
{todos && todos.length > 0 && (
|
|
222
|
-
<View className="gap-
|
|
209
|
+
<View className="gap-2">
|
|
223
210
|
{todos.map((todo) => (
|
|
224
|
-
<
|
|
211
|
+
<Surface key={todo._id} variant="secondary" className="p-3 rounded-lg">
|
|
225
212
|
<View className="flex-row items-center gap-3">
|
|
226
213
|
<Checkbox
|
|
227
214
|
isSelected={todo.completed}
|
|
228
215
|
onSelectedChange={() => handleToggleTodo(todo._id, todo.completed)}
|
|
229
216
|
/>
|
|
230
217
|
<View className="flex-1">
|
|
231
|
-
<Text className={`text-
|
|
218
|
+
<Text className={`text-sm ${todo.completed ? "text-muted line-through" : "text-foreground"}`}>
|
|
232
219
|
{todo.text}
|
|
233
220
|
</Text>
|
|
234
221
|
</View>
|
|
235
|
-
<
|
|
222
|
+
<Button
|
|
223
|
+
isIconOnly
|
|
224
|
+
variant="ghost"
|
|
236
225
|
onPress={() => handleDeleteTodo(todo._id)}
|
|
237
|
-
|
|
226
|
+
size="sm"
|
|
238
227
|
>
|
|
239
|
-
<Ionicons name="trash-outline" size={
|
|
240
|
-
</
|
|
228
|
+
<Ionicons name="trash-outline" size={16} color={dangerColor} />
|
|
229
|
+
</Button>
|
|
241
230
|
</View>
|
|
242
|
-
</
|
|
231
|
+
</Surface>
|
|
243
232
|
))}
|
|
244
233
|
</View>
|
|
245
234
|
)}
|
|
246
235
|
{{else}}
|
|
247
236
|
{isLoading && (
|
|
248
237
|
<View className="items-center justify-center py-12">
|
|
249
|
-
<
|
|
250
|
-
<Text className="text-muted mt-
|
|
238
|
+
<Spinner size="lg" />
|
|
239
|
+
<Text className="text-muted text-sm mt-3">Loading tasks...</Text>
|
|
251
240
|
</View>
|
|
252
241
|
)}
|
|
253
242
|
|
|
254
243
|
{todos?.data && todos.data.length === 0 && !isLoading && (
|
|
255
|
-
<
|
|
256
|
-
<Ionicons name="checkbox-outline" size={
|
|
257
|
-
<Text className="text-foreground
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
<Text className="text-muted text-center">
|
|
261
|
-
Add your first task to get started!
|
|
262
|
-
</Text>
|
|
263
|
-
</Card>
|
|
244
|
+
<Surface variant="secondary" className="items-center justify-center py-10 rounded-lg">
|
|
245
|
+
<Ionicons name="checkbox-outline" size={40} color={mutedColor} />
|
|
246
|
+
<Text className="text-foreground font-medium mt-3">No tasks yet</Text>
|
|
247
|
+
<Text className="text-muted text-xs mt-1">Add your first task to get started</Text>
|
|
248
|
+
</Surface>
|
|
264
249
|
)}
|
|
265
250
|
|
|
266
251
|
{todos?.data && todos.data.length > 0 && (
|
|
267
|
-
<View className="gap-
|
|
252
|
+
<View className="gap-2">
|
|
268
253
|
{todos.data.map((todo) => (
|
|
269
|
-
<
|
|
254
|
+
<Surface key={todo.id} variant="secondary" className="p-3 rounded-lg">
|
|
270
255
|
<View className="flex-row items-center gap-3">
|
|
271
256
|
<Checkbox
|
|
272
257
|
isSelected={todo.completed}
|
|
273
258
|
onSelectedChange={() => handleToggleTodo(todo.id, todo.completed)}
|
|
274
259
|
/>
|
|
275
260
|
<View className="flex-1">
|
|
276
|
-
<Text className={`text-
|
|
261
|
+
<Text className={`text-sm ${todo.completed ? "text-muted line-through" : "text-foreground"}`}>
|
|
277
262
|
{todo.text}
|
|
278
263
|
</Text>
|
|
279
264
|
</View>
|
|
280
|
-
<
|
|
265
|
+
<Button
|
|
266
|
+
isIconOnly
|
|
267
|
+
variant="ghost"
|
|
281
268
|
onPress={() => handleDeleteTodo(todo.id)}
|
|
282
|
-
|
|
269
|
+
size="sm"
|
|
283
270
|
>
|
|
284
|
-
<Ionicons name="trash-outline" size={
|
|
285
|
-
</
|
|
271
|
+
<Ionicons name="trash-outline" size={16} color={dangerColor} />
|
|
272
|
+
</Button>
|
|
286
273
|
</View>
|
|
287
|
-
</
|
|
274
|
+
</Surface>
|
|
288
275
|
))}
|
|
289
276
|
</View>
|
|
290
277
|
)}
|
|
@@ -72,7 +72,7 @@ function handleDeleteTodo(id: number) {
|
|
|
72
72
|
</script>
|
|
73
73
|
|
|
74
74
|
<template>
|
|
75
|
-
<
|
|
75
|
+
<UContainer class="py-8 max-w-md">
|
|
76
76
|
<UCard>
|
|
77
77
|
<template #header>
|
|
78
78
|
<div>
|
|
@@ -80,12 +80,13 @@ function handleDeleteTodo(id: number) {
|
|
|
80
80
|
<div class="text-muted text-sm">Manage your tasks efficiently</div>
|
|
81
81
|
</div>
|
|
82
82
|
</template>
|
|
83
|
+
|
|
83
84
|
<form @submit.prevent="handleAddTodo" class="mb-6 flex items-center gap-2">
|
|
84
85
|
<UInput
|
|
85
86
|
v-model="newTodoText"
|
|
86
87
|
placeholder="Add a new task..."
|
|
87
88
|
autocomplete="off"
|
|
88
|
-
class="
|
|
89
|
+
class="flex-1"
|
|
89
90
|
{{#if (eq backend "convex")}}
|
|
90
91
|
:disabled="isCreatePending"
|
|
91
92
|
{{/if}}
|
|
@@ -93,103 +94,127 @@ function handleDeleteTodo(id: number) {
|
|
|
93
94
|
<UButton
|
|
94
95
|
type="submit"
|
|
95
96
|
{{#if (eq backend "convex")}}
|
|
96
|
-
:
|
|
97
|
-
|
|
98
|
-
>
|
|
99
|
-
{{#if (eq backend "convex")}}
|
|
100
|
-
<span v-if="isCreatePending">
|
|
101
|
-
<UIcon name="i-lucide-loader-2" class="animate-spin" />
|
|
102
|
-
</span>
|
|
103
|
-
<span v-else>Add</span>
|
|
97
|
+
:loading="isCreatePending"
|
|
98
|
+
:disabled="!newTodoText.trim()"
|
|
104
99
|
{{else}}
|
|
105
|
-
|
|
100
|
+
:loading="createMutation.isPending.value"
|
|
106
101
|
{{/if}}
|
|
102
|
+
>
|
|
103
|
+
Add
|
|
107
104
|
</UButton>
|
|
108
105
|
</form>
|
|
109
106
|
|
|
110
107
|
{{#if (eq backend "convex")}}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
@click="handleDeleteTodo(todo._id)"
|
|
146
|
-
aria-label="Delete todo"
|
|
147
|
-
icon="i-lucide-trash-2"
|
|
108
|
+
<!-- Loading State -->
|
|
109
|
+
<div v-if="isPending" class="space-y-2">
|
|
110
|
+
<USkeleton v-for="i in 3" :key="i" class="h-12 w-full" />
|
|
111
|
+
</div>
|
|
112
|
+
|
|
113
|
+
<!-- Error State -->
|
|
114
|
+
<UAlert
|
|
115
|
+
v-else-if="error || deleteError"
|
|
116
|
+
color="error"
|
|
117
|
+
icon="i-lucide-alert-circle"
|
|
118
|
+
title="Error"
|
|
119
|
+
:description="error?.message || deleteError?.message"
|
|
120
|
+
/>
|
|
121
|
+
|
|
122
|
+
<!-- Empty State -->
|
|
123
|
+
<UEmpty
|
|
124
|
+
v-else-if="data?.length === 0"
|
|
125
|
+
icon="i-lucide-clipboard-list"
|
|
126
|
+
title="No todos yet"
|
|
127
|
+
description="Add your first task above!"
|
|
128
|
+
/>
|
|
129
|
+
|
|
130
|
+
<!-- Todo List -->
|
|
131
|
+
<ul v-else-if="data" class="space-y-2">
|
|
132
|
+
<li
|
|
133
|
+
v-for="todo in data"
|
|
134
|
+
:key="todo._id"
|
|
135
|
+
class="flex items-center justify-between rounded-md border p-3"
|
|
136
|
+
>
|
|
137
|
+
<div class="flex items-center gap-3">
|
|
138
|
+
<UCheckbox
|
|
139
|
+
:model-value="todo.completed"
|
|
140
|
+
@update:model-value="() => handleToggleTodo(todo._id, todo.completed)"
|
|
141
|
+
:id="`todo-${todo._id}`"
|
|
148
142
|
/>
|
|
149
|
-
|
|
150
|
-
|
|
143
|
+
<label
|
|
144
|
+
:for="`todo-${todo._id}`"
|
|
145
|
+
:class="{ 'line-through text-muted': todo.completed }"
|
|
146
|
+
class="cursor-pointer"
|
|
147
|
+
>
|
|
148
|
+
\{{ todo.text }}
|
|
149
|
+
</label>
|
|
150
|
+
</div>
|
|
151
|
+
<UButton
|
|
152
|
+
color="error"
|
|
153
|
+
variant="ghost"
|
|
154
|
+
size="sm"
|
|
155
|
+
square
|
|
156
|
+
@click="handleDeleteTodo(todo._id)"
|
|
157
|
+
aria-label="Delete todo"
|
|
158
|
+
icon="i-lucide-trash-2"
|
|
159
|
+
/>
|
|
160
|
+
</li>
|
|
161
|
+
</ul>
|
|
151
162
|
{{else}}
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
@click="handleDeleteTodo(todo.id)"
|
|
187
|
-
aria-label="Delete todo"
|
|
188
|
-
icon="i-lucide-trash-2"
|
|
163
|
+
<!-- Loading State -->
|
|
164
|
+
<div v-if="todos.status.value === 'pending'" class="space-y-2">
|
|
165
|
+
<USkeleton v-for="i in 3" :key="i" class="h-12 w-full" />
|
|
166
|
+
</div>
|
|
167
|
+
|
|
168
|
+
<!-- Error State -->
|
|
169
|
+
<UAlert
|
|
170
|
+
v-else-if="todos.status.value === 'error'"
|
|
171
|
+
color="error"
|
|
172
|
+
icon="i-lucide-alert-circle"
|
|
173
|
+
title="Failed to load todos"
|
|
174
|
+
:description="todos.error.value?.message"
|
|
175
|
+
/>
|
|
176
|
+
|
|
177
|
+
<!-- Empty State -->
|
|
178
|
+
<UEmpty
|
|
179
|
+
v-else-if="todos.data.value?.length === 0"
|
|
180
|
+
icon="i-lucide-clipboard-list"
|
|
181
|
+
title="No todos yet"
|
|
182
|
+
description="Add your first task above!"
|
|
183
|
+
/>
|
|
184
|
+
|
|
185
|
+
<!-- Todo List -->
|
|
186
|
+
<ul v-else class="space-y-2">
|
|
187
|
+
<li
|
|
188
|
+
v-for="todo in todos.data.value"
|
|
189
|
+
:key="todo.id"
|
|
190
|
+
class="flex items-center justify-between rounded-md border p-3"
|
|
191
|
+
>
|
|
192
|
+
<div class="flex items-center gap-3">
|
|
193
|
+
<UCheckbox
|
|
194
|
+
:model-value="todo.completed"
|
|
195
|
+
@update:model-value="() => handleToggleTodo(todo.id, todo.completed)"
|
|
196
|
+
:id="`todo-${todo.id}`"
|
|
189
197
|
/>
|
|
190
|
-
|
|
191
|
-
|
|
198
|
+
<label
|
|
199
|
+
:for="`todo-${todo.id}`"
|
|
200
|
+
:class="{ 'line-through text-muted': todo.completed }"
|
|
201
|
+
class="cursor-pointer"
|
|
202
|
+
>
|
|
203
|
+
\{{ todo.text }}
|
|
204
|
+
</label>
|
|
205
|
+
</div>
|
|
206
|
+
<UButton
|
|
207
|
+
color="error"
|
|
208
|
+
variant="ghost"
|
|
209
|
+
size="sm"
|
|
210
|
+
square
|
|
211
|
+
@click="handleDeleteTodo(todo.id)"
|
|
212
|
+
aria-label="Delete todo"
|
|
213
|
+
icon="i-lucide-trash-2"
|
|
214
|
+
/>
|
|
215
|
+
</li>
|
|
216
|
+
</ul>
|
|
192
217
|
{{/if}}
|
|
193
218
|
</UCard>
|
|
194
|
-
</
|
|
219
|
+
</UContainer>
|
|
195
220
|
</template>
|