Skip to content

Commit 35945be

Browse files
committed
update
1 parent aeb342d commit 35945be

File tree

8 files changed

+351
-90
lines changed

8 files changed

+351
-90
lines changed

app/app.vue

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747

4848
<template>
4949
<UApp>
50-
<UHeader v-if="route.path !== '/auth'">
50+
<UHeader v-if="route.path !== '/auth'" :toggle="false">
5151
<UNavigationMenu :items="items" variant="pill" />
5252

5353
<template #title>
@@ -68,10 +68,6 @@
6868
/>
6969
</UDropdownMenu>
7070
</template>
71-
72-
<template #body>
73-
<UNavigationMenu :items="items" orientation="vertical" class="-mx-2.5" />
74-
</template>
7571
</UHeader>
7672

7773
<UMain>

app/components/Table.vue

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,11 @@
137137
}"
138138
@contextmenu="onContextmenu"
139139
@select="(event, row) => $emit('select', event, row)"
140-
/>
140+
>
141+
<template v-for="(_, name) in $slots" #[name]="slotData">
142+
<slot :name="name" v-bind="slotData" />
143+
</template>
144+
</UTable>
141145
</UContextMenu>
142146
<div
143147
v-if="(total || 0) > 0"
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
<script setup lang="ts">
2+
import { refDebounced } from '@vueuse/core'
3+
import type { TableColumn } from '@nuxt/ui'
4+
5+
const props = defineProps<{
6+
onboardingTasks: any[]
7+
}>()
8+
9+
const emit = defineEmits<{
10+
(e: 'toggle', row: any, checked: boolean): void
11+
}>()
12+
13+
const page = ref(1)
14+
const limit = ref(5)
15+
const search = ref('')
16+
const debouncedSearch = refDebounced(search, 300)
17+
18+
// Reset page on search
19+
watch(debouncedSearch, () => {
20+
page.value = 1
21+
})
22+
23+
const filteredData = computed(() => {
24+
let data = props.onboardingTasks
25+
if (debouncedSearch.value) {
26+
const s = debouncedSearch.value.toLowerCase()
27+
data = data.filter((ot: any) =>
28+
ot.user.name.toLowerCase().includes(s) ||
29+
ot.user.email.toLowerCase().includes(s)
30+
)
31+
}
32+
return data
33+
})
34+
35+
const paginatedData = computed(() => {
36+
const start = (page.value - 1) * limit.value
37+
return filteredData.value.slice(start, start + limit.value)
38+
})
39+
40+
const columns: TableColumn<any>[] = [
41+
{
42+
accessorKey: 'completed',
43+
header: 'Status',
44+
},
45+
{
46+
accessorKey: 'user.name',
47+
header: 'Employee',
48+
},
49+
{
50+
accessorKey: 'user.email',
51+
header: 'Email',
52+
}
53+
]
54+
</script>
55+
56+
<template>
57+
<Table
58+
v-model:page="page"
59+
v-model:items-per-page="limit"
60+
v-model:search="search"
61+
:columns="columns"
62+
:data="paginatedData"
63+
:total="filteredData.length"
64+
>
65+
<template #completed-cell="{ row }">
66+
<div class="flex items-center gap-2">
67+
<UCheckbox
68+
:model-value="row.original.completed"
69+
@update:model-value="(val) => emit('toggle', row.original, val)"
70+
/>
71+
<span :class="['text-xs', row.original.completed ? 'text-green-600' : 'text-gray-500']">
72+
{{ row.original.completed ? 'Done' : 'Pending' }}
73+
</span>
74+
</div>
75+
</template>
76+
</Table>
77+
</template>

app/middleware/auth.global.ts

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,22 @@ export default defineNuxtRouteMiddleware(async (to) => {
1212
console.log({ session: session.value })
1313
// User is logged in
1414
const role = session?.value?.user?.role
15-
//if (role === 'VOLUNTEER') {
16-
// // Volunteers can only access /rides and its sub-paths, and /settings
17-
// if (!to.path.startsWith('/rides') && to.path !== '/settings') {
18-
// return navigateTo('/rides')
19-
// }
20-
//}
15+
const userId = session?.value?.user?.id
16+
17+
if (role === 'SUPERVISOR') {
18+
const targetPath = `/supervisor/${userId}`
19+
if (to.path !== targetPath) {
20+
return navigateTo(targetPath)
21+
}
22+
}
23+
24+
if (role === 'ONBOARDING') {
25+
const targetPath = `/onboarding/${userId}`
26+
if (to.path !== targetPath) {
27+
return navigateTo(targetPath)
28+
}
29+
}
30+
2131
if (to.path === '/auth') {
2232
return navigateTo('/')
2333
}

app/pages/onboarding/[id].vue

Lines changed: 49 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
11
<script setup lang="ts">
22
import type { TableColumn } from '@nuxt/ui'
33
import { refDebounced } from '@vueuse/core'
4+
import { h, resolveComponent } from 'vue'
5+
import { authClient } from '~/utils/auth-client'
46
57
const route = useRoute()
68
const userId = route.params.id as string
9+
const UCheckbox = resolveComponent('UCheckbox')
10+
11+
// Fetch Current Session
12+
const { data: session } = await authClient.useSession(useFetch)
13+
const isOnboardingUser = computed(() => session.value?.user?.role === 'ONBOARDING')
714
815
// Fetch User Info
916
const { data: user } = await useFetch(`/api/get/users/byId/${userId}`)
@@ -67,58 +74,60 @@
6774
return filteredData.value.slice(start, start + limit.value)
6875
})
6976
70-
const columns: TableColumn<any>[] = [
71-
{
72-
accessorKey: 'task.desc',
73-
header: 'Task',
74-
},
75-
{
76-
accessorKey: 'task.supervisor.name',
77-
header: 'Supervisor',
78-
cell: ({ row }) => row.original.task.supervisor?.name || 'Unassigned',
79-
},
80-
{
81-
accessorKey: 'completed',
82-
header: 'Status',
83-
cell: ({ row }) => {
84-
const completed = row.original.completed
85-
return h(
86-
'div',
87-
{ class: 'flex items-center' },
88-
h(
89-
'span',
90-
{
91-
class: [
92-
'inline-flex items-center px-2 py-0.5 rounded text-xs font-medium',
93-
completed
94-
? 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200'
95-
: 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200',
96-
],
97-
},
98-
completed ? 'Completed' : 'Pending'
99-
)
100-
)
77+
const columns: TableColumn<any>[] = [
78+
{
79+
accessorKey: 'completed',
80+
header: 'Status',
81+
cell: ({ row }) => {
82+
return h('div', { class: 'flex items-center gap-2' }, [
83+
h(UCheckbox, {
84+
modelValue: row.original.completed,
85+
disabled: isOnboardingUser.value,
86+
'onUpdate:modelValue': (val: boolean) => toggleStatus(row.original, val),
87+
}),
88+
h('span', {
89+
class: ['text-xs font-medium', row.original.completed ? 'text-green-600' : 'text-gray-500']
90+
}, row.original.completed ? 'Completed' : 'Pending')
91+
])
92+
}
10193
},
102-
},
103-
]
104-
</script>
105-
94+
{
95+
accessorKey: 'task.desc',
96+
header: 'Task',
97+
},
98+
{
99+
accessorKey: 'task.supervisor.name',
100+
header: 'Supervisor',
101+
cell: ({ row }) => row.original.task.supervisor?.name || 'Unassigned',
102+
},
103+
]
104+
105+
async function toggleStatus(row: any, checked: boolean) {
106+
try {
107+
await $fetch(`/api/put/onboarding-tasks/${row.id}`, {
108+
method: 'PUT',
109+
body: { completed: checked },
110+
})
111+
await refresh()
112+
} catch (e) {
113+
console.error(e)
114+
}
115+
}
116+
</script>
106117
<template>
107118
<div class="flex flex-col gap-6 p-6">
108119
<div class="flex items-center gap-4">
109120
<UButton
121+
v-if="!isOnboardingUser"
110122
icon="i-heroicons-arrow-left"
111123
color="neutral"
112124
variant="ghost"
113125
to="/"
114126
/>
115127
<div>
116-
<h1 class="text-2xl font-bold text-gray-900 dark:text-white">
128+
<h1 v-if="!isOnboardingUser" class="text-2xl font-bold text-gray-900 dark:text-white">
117129
{{ user?.name }}
118130
</h1>
119-
<p class="text-sm text-gray-500 dark:text-gray-400">
120-
{{ user?.role }} • {{ user?.department?.name || 'No Department' }}
121-
</p>
122131
</div>
123132
</div>
124133

@@ -140,4 +149,4 @@
140149
</p>
141150
</div>
142151
</div>
143-
</template>
152+
</template>

0 commit comments

Comments
 (0)