Skip to content
This repository was archived by the owner on Dec 12, 2024. It is now read-only.

Commit 74650c1

Browse files
Feat/todo (#199)
* feat:dwa-starter-vue-app-hello-word * feat:dwa-starter-vue-app-hello-word * feat:dwa-starter-vue-app-hello-word * added tailwind css * added tailwind css * added tbd example json for ci integration * added tbd example json for ci integration * replaced npm with pnpm in ci/cd file * fix:pnpm build error in ci/cd * fix:pnpm build error in ci/cd * fix:pnpm build error in ci/cd * break the commands into seperate items * break the commands into seperate items * Setup routes for Home, About, Settings and 404 handling * Setup routes for Home, About, Settings and 404 handling * remove vite config timestamp file * remove vite config timestamp file * initial commit * initial commit * fix:layout * fix:layout * fix:layout * fix:layout * responsive nav menu * responsive nav menu * backdrop blur * vite plugin pwa * remove unused navigation menu component * pwa assets generation and settings page form * pwa assets generation and settings page form * web5 connection setup * web5 connection setup * test case fix * test unit specs update * test unit specs update * feat:todo list * ci fix * ci fix * ci fix * fix * fix todo list functions * delete todo unused store * conflict fix * conflict fix * conflict fix * seperate web5 connect options button loading state * seperate web5 connect options button loading state * revert web5 composable to resemble that of the react vite version * revert to importing react linb utilties * update * update * update * connect task to web5 * connect task to web5 * conflict fix * conflict fix * edit button on todo item
1 parent fe2977e commit 74650c1

File tree

16 files changed

+390
-11
lines changed

16 files changed

+390
-11
lines changed
Lines changed: 178 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,181 @@
1-
<script setup lang="ts"></script>
1+
<script setup lang="ts">
2+
import { useWeb5, type Task } from '@/composables/web5'
3+
import { onBeforeMount, ref } from 'vue'
4+
import { Input } from '@/components/ui/input'
5+
import { Button } from '@/components/ui/button'
6+
import { TrashIcon, Pencil2Icon } from '@radix-icons/vue'
7+
import {
8+
Table,
9+
TableBody,
10+
TableCaption,
11+
TableCell,
12+
TableHead,
13+
TableHeader,
14+
TableRow
15+
} from '@/components/ui/table'
16+
import { Checkbox } from '@/components/ui/checkbox'
17+
import { toast } from '@/components/ui/toast/use-toast'
18+
19+
const task = ref('')
20+
const tasks = ref<Task[]>([])
21+
22+
const { listTasks, createTask, updateTask, deleteTask } = useWeb5()
23+
24+
onBeforeMount(() => {
25+
findTasks()
26+
})
27+
28+
const findTasks = async () => {
29+
tasks.value = await listTasks()
30+
}
31+
32+
async function addTodo() {
33+
try {
34+
if (!task.value) {
35+
toast({
36+
title: 'Error',
37+
description: 'task cant be null'
38+
})
39+
return
40+
}
41+
const data = {
42+
completed: false,
43+
title: task.value
44+
}
45+
46+
await createTask(data)
47+
await findTasks()
48+
task.value = ''
49+
toast({
50+
description: 'Todo added successfully'
51+
})
52+
} catch (err: any) {
53+
toast({
54+
description: `Failed to add todo: ${err.message || 'Unknown error'}`,
55+
title: 'Error'
56+
})
57+
}
58+
}
59+
60+
async function deleteTodo(id: string) {
61+
try {
62+
await deleteTask(id)
63+
await findTasks()
64+
toast({
65+
description: 'Todo deleted successfully'
66+
})
67+
} catch (err: any) {
68+
toast({
69+
description: `Failed to delete todo: ${err.message || 'Unknown error'}`,
70+
title: 'Error'
71+
})
72+
}
73+
}
74+
75+
async function toggleTodoStatus(task: Task) {
76+
try {
77+
await updateTask(task)
78+
await findTasks()
79+
toast({
80+
description: `Todo status updated to ${task.completed ? 'completed' : 'incomplete'}`
81+
})
82+
} catch (err: any) {
83+
toast({
84+
description: `Failed to update todo: ${err.message || 'Unknown error'}`,
85+
title: 'Error'
86+
})
87+
}
88+
}
89+
90+
function startEditing(id?: string) {
91+
const task = tasks.value.find((i) => i.id === id)
92+
if (task) task.isEditing = true
93+
}
94+
95+
function stopEditing(id?: string) {
96+
const task = tasks.value.find((i) => i.id === id)
97+
if (task) task.isEditing = false
98+
}
99+
100+
async function updateTodoTitle(task: Task) {
101+
try {
102+
await updateTask(task)
103+
await findTasks()
104+
toast({
105+
description: 'Todo title updated successfully'
106+
})
107+
} catch (err: any) {
108+
toast({
109+
description: `Failed to update todo title: ${err.message || 'Unknown error'}`,
110+
title: 'Error'
111+
})
112+
} finally {
113+
stopEditing(task.id)
114+
}
115+
}
116+
</script>
2117

3118
<template>
4-
<h1>Todo</h1>
5-
<p>coming soon</p>
119+
<div class="flex flex-col gap-4">
120+
<h1>Todos</h1>
121+
122+
<div class="lg:w-1/3 flex items-center gap-2">
123+
<label for="add-todo" class="sr-only">Add a todo</label>
124+
<Input
125+
type="text"
126+
id="add-todo"
127+
v-model="task"
128+
@keydown.enter.exact.prevent="addTodo"
129+
placeholder="what are you working on?"
130+
/>
131+
<Button type="button" @click="addTodo"> Add </Button>
132+
</div>
133+
<div v-if="!tasks.length">
134+
<h2>no todos created yet</h2>
135+
</div>
136+
137+
<Table v-else class="lg:w-1/3">
138+
<TableCaption>Todos.</TableCaption>
139+
<TableHeader>
140+
<TableRow>
141+
<TableHead class="w-[100px]"> Completed? </TableHead>
142+
<TableHead>Title</TableHead>
143+
<TableHead class="text-right"> </TableHead>
144+
<TableHead class="text-right"> </TableHead>
145+
</TableRow>
146+
</TableHeader>
147+
<TableBody>
148+
<TableRow v-for="todo in tasks" :key="todo.id">
149+
<TableCell>
150+
<Checkbox
151+
:checked="todo.completed"
152+
@click="toggleTodoStatus({ ...todo, completed: !todo.completed })"
153+
/>
154+
</TableCell>
155+
<TableCell class="cursor-pointer">
156+
<div v-if="todo.isEditing">
157+
<Input
158+
v-model="todo.title"
159+
@keydown.enter="updateTodoTitle(todo)"
160+
@blur="updateTodoTitle(todo)"
161+
/>
162+
</div>
163+
<div v-else @click="startEditing(todo.id)">
164+
{{ todo.title }}
165+
</div>
166+
</TableCell>
167+
<TableCell class="text-right">
168+
<Button variant="ghost" @click="startEditing(todo.id)">
169+
<Pencil2Icon class="w-4 h-4" />
170+
</Button>
171+
</TableCell>
172+
<TableCell class="text-right">
173+
<Button variant="ghost" @click="deleteTodo(todo.id as string)">
174+
<TrashIcon class="w-4 h-4" />
175+
</Button>
176+
</TableCell>
177+
</TableRow>
178+
</TableBody>
179+
</Table>
180+
</div>
6181
</template>
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<script setup lang="ts">
2+
import { type HTMLAttributes, computed } from 'vue'
3+
import type { CheckboxRootEmits, CheckboxRootProps } from 'radix-vue'
4+
import { CheckboxIndicator, CheckboxRoot, useForwardPropsEmits } from 'radix-vue'
5+
import { CheckIcon } from '@radix-icons/vue'
6+
import { cn } from '@/lib/utils'
7+
8+
const props = defineProps<CheckboxRootProps & { class?: HTMLAttributes['class'] }>()
9+
const emits = defineEmits<CheckboxRootEmits>()
10+
11+
const delegatedProps = computed(() => {
12+
const { class: _, ...delegated } = props
13+
14+
return delegated
15+
})
16+
17+
const forwarded = useForwardPropsEmits(delegatedProps, emits)
18+
</script>
19+
20+
<template>
21+
<CheckboxRoot
22+
v-bind="forwarded"
23+
:class="
24+
cn(
25+
'peer h-4 w-4 shrink-0 rounded-sm border border-slate-200 border-slate-900 shadow focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-slate-950 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-slate-900 data-[state=checked]:text-slate-50 dark:border-slate-800 dark:border-slate-50 dark:focus-visible:ring-slate-300 dark:data-[state=checked]:bg-slate-50 dark:data-[state=checked]:text-slate-900',
26+
props.class
27+
)
28+
"
29+
>
30+
<CheckboxIndicator class="flex h-full w-full items-center justify-center text-current">
31+
<slot>
32+
<CheckIcon class="h-4 w-4" />
33+
</slot>
34+
</CheckboxIndicator>
35+
</CheckboxRoot>
36+
</template>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default as Checkbox } from './Checkbox.vue'
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<script setup lang="ts">
2+
import type { HTMLAttributes } from 'vue'
3+
import { cn } from '@/lib/utils'
4+
5+
const props = defineProps<{
6+
class?: HTMLAttributes['class']
7+
}>()
8+
</script>
9+
10+
<template>
11+
<div class="w-full overflow-auto">
12+
<table :class="cn('w-full caption-bottom text-sm', props.class)">
13+
<slot />
14+
</table>
15+
</div>
16+
</template>
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<script setup lang="ts">
2+
import type { HTMLAttributes } from 'vue'
3+
import { cn } from '@/lib/utils'
4+
5+
const props = defineProps<{
6+
class?: HTMLAttributes['class']
7+
}>()
8+
</script>
9+
10+
<template>
11+
<tbody :class="cn('[&_tr:last-child]:border-0', props.class)">
12+
<slot />
13+
</tbody>
14+
</template>
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<script setup lang="ts">
2+
import type { HTMLAttributes } from 'vue'
3+
import { cn } from '@/lib/utils'
4+
5+
const props = defineProps<{
6+
class?: HTMLAttributes['class']
7+
}>()
8+
</script>
9+
10+
<template>
11+
<caption :class="cn('mt-4 text-sm text-slate-500 dark:text-slate-400', props.class)">
12+
<slot />
13+
</caption>
14+
</template>
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<script setup lang="ts">
2+
import type { HTMLAttributes } from 'vue'
3+
import { cn } from '@/lib/utils'
4+
5+
const props = defineProps<{
6+
class?: HTMLAttributes['class']
7+
}>()
8+
</script>
9+
10+
<template>
11+
<td
12+
:class="
13+
cn(
14+
'p-2 align-middle [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-0.5',
15+
props.class,
16+
)
17+
"
18+
>
19+
<slot />
20+
</td>
21+
</template>
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<script setup lang="ts">
2+
import { type HTMLAttributes, computed } from 'vue'
3+
import TableRow from './TableRow.vue'
4+
import TableCell from './TableCell.vue'
5+
import { cn } from '@/lib/utils'
6+
7+
const props = withDefaults(defineProps<{
8+
class?: HTMLAttributes['class']
9+
colspan?: number
10+
}>(), {
11+
colspan: 1,
12+
})
13+
14+
const delegatedProps = computed(() => {
15+
const { class: _, ...delegated } = props
16+
17+
return delegated
18+
})
19+
</script>
20+
21+
<template>
22+
<TableRow>
23+
<TableCell
24+
:class="
25+
cn(
26+
'p-4 whitespace-nowrap align-middle text-sm text-slate-950 dark:text-slate-50',
27+
props.class,
28+
)
29+
"
30+
v-bind="delegatedProps"
31+
>
32+
<div class="flex items-center justify-center py-10">
33+
<slot />
34+
</div>
35+
</TableCell>
36+
</TableRow>
37+
</template>
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<script setup lang="ts">
2+
import type { HTMLAttributes } from 'vue'
3+
import { cn } from '@/lib/utils'
4+
5+
const props = defineProps<{
6+
class?: HTMLAttributes['class']
7+
}>()
8+
</script>
9+
10+
<template>
11+
<tfoot :class="cn('border-t bg-slate-100/50 font-medium [&>tr]:last:border-b-0 dark:bg-slate-800/50', props.class)">
12+
<slot />
13+
</tfoot>
14+
</template>
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<script setup lang="ts">
2+
import type { HTMLAttributes } from 'vue'
3+
import { cn } from '@/lib/utils'
4+
5+
const props = defineProps<{
6+
class?: HTMLAttributes['class']
7+
}>()
8+
</script>
9+
10+
<template>
11+
<th :class="cn('h-10 px-2 text-left align-middle font-medium text-slate-500 [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-0.5 dark:text-slate-400', props.class)">
12+
<slot />
13+
</th>
14+
</template>

0 commit comments

Comments
 (0)