11<script setup lang="ts">
2+ import { computed , ref , watch } from ' vue'
23import { toast } from ' vue-sonner'
34import { formatDate } from ' ~/lib/utils'
45import { Button } from ' ~/components/ui/button'
56import { CopyIcon , LoaderCircleIcon } from ' lucide-vue-next'
67import { Card , CardContent , CardHeader , CardTitle } from ' @/components/ui/card'
8+ import { Skeleton } from ' @/components/ui/skeleton'
79import { useIntervalFn } from ' @vueuse/core'
810import { useRoute , useRouter } from ' vue-router'
911import { useFetch } from ' #imports'
@@ -14,15 +16,26 @@ const router = useRouter()
1416const {
1517 data : task,
1618 error,
17- refresh
19+ refresh,
20+ pending
1821} = useFetch <TaskSelect >(` /api/tasks/${route .params .id } ` )
1922const copyToClipboard = (value : string ) => {
2023 navigator .clipboard .writeText (value )
2124}
2225
2326useIntervalFn (() => {
2427 refresh ()
25- }, 1500 )
28+ }, 2000 )
29+
30+ const hasLoadedOnce = ref (Boolean (task .value ))
31+
32+ watch (task , (current ) => {
33+ if (current ) {
34+ hasLoadedOnce .value = true
35+ }
36+ })
37+
38+ const isLoading = computed (() => ! hasLoadedOnce .value )
2639
2740const handleCopy = (value : string ) => {
2841 copyToClipboard (value )
@@ -50,80 +63,167 @@ const handleCopy = (value: string) => {
5063 </CardHeader >
5164 <CardContent >
5265 <table >
53- <tbody v-if = " task " >
66+ <tbody >
5467 <tr >
5568 <th >ID</th >
5669 <td class =" flex items-center justify-start gap-2" >
57- <span >{{ task.id }}</span >
58- <Button
59- @click =" handleCopy(task.id)"
60- class =" cursor-pointer"
61- variant =" ghost"
62- ><CopyIcon
63- /></Button >
70+ <template v-if =" ! isLoading " >
71+ <span >{{ task?.id }}</span >
72+ <Button
73+ @click =" task && handleCopy(task.id)"
74+ class =" cursor-pointer"
75+ variant =" ghost"
76+ >
77+ <CopyIcon />
78+ </Button >
79+ </template >
80+ <Skeleton
81+ v-else
82+ class =" h-4 w-50"
83+ />
6484 </td >
6585 </tr >
6686 <tr >
6787 <th >Name</th >
68- <td >{{ task.name }}</td >
88+ <td >
89+ <template v-if =" ! isLoading " >
90+ {{ task?.name }}
91+ </template >
92+ <Skeleton
93+ v-else
94+ class =" h-4 w-80"
95+ />
96+ </td >
6997 </tr >
7098
7199 <tr >
72100 <th >State</th >
73101 <td >
74- <TaskState :state =" task.state" />
102+ <template v-if =" ! isLoading && task " >
103+ <TaskState :state =" task.state" />
104+ </template >
105+ <Skeleton
106+ v-else
107+ class =" h-4 w-24"
108+ />
75109 </td >
76110 </tr >
77111 <tr >
78112 <th >args</th >
79- <td >{{ task.args }}</td >
113+ <td >
114+ <template v-if =" ! isLoading " >
115+ {{ task?.args }}
116+ </template >
117+ <Skeleton
118+ v-else
119+ class =" h-4 w-full max-w-2xl"
120+ />
121+ </td >
80122 </tr >
81123 <tr >
82124 <th >kwargs</th >
83- <td >{{ task.kwargs }}</td >
125+ <td >
126+ <template v-if =" ! isLoading " >
127+ {{ task?.kwargs }}
128+ </template >
129+ <Skeleton
130+ v-else
131+ class =" h-4 w-full max-w-2xl"
132+ />
133+ </td >
84134 </tr >
85135 <tr >
86136 <th >Return Value</th >
87137 <td >
88- {{
89- task.returnValue?.return_value
90- ? task.returnValue?.return_value
91- : 'null'
92- }}
138+ <template v-if =" ! isLoading " >
139+ {{
140+ task?.returnValue?.return_value
141+ ? task?.returnValue?.return_value
142+ : 'null'
143+ }}
144+ </template >
145+ <Skeleton
146+ v-else
147+ class =" h-4 w-80"
148+ />
93149 </td >
94150 </tr >
95151 <tr >
96152 <th >Queued At</th >
97- <td >{{ formatDate(String(task.queuedAt), true) }}</td >
153+ <td >
154+ <template v-if =" ! isLoading " >
155+ {{ formatDate(String(task?.queuedAt), true) }}
156+ </template >
157+ <Skeleton
158+ v-else
159+ class =" h-4 w-40"
160+ />
161+ </td >
98162 </tr >
99163 <tr >
100164 <th >Started At</th >
101- <td v-if =" task.startedAt" >
102- {{ formatDate(String(task.startedAt), true) }}
103- </td >
104- <td v-else >
105- <LoaderCircleIcon class =" animate-spin" />
165+ <td >
166+ <template v-if =" isLoading " >
167+ <Skeleton class =" h-4 w-40" />
168+ </template >
169+ <template v-else-if =" task ?.startedAt " >
170+ {{ formatDate(String(task.startedAt), true) }}
171+ </template >
172+ <template v-else >
173+ <LoaderCircleIcon class =" animate-spin" />
174+ </template >
106175 </td >
107176 </tr >
108177 <tr >
109178 <th >Finished At</th >
110- <td v-if =" task.state === 'running'" >
111- <LoaderCircleIcon class =" animate-spin" />
179+ <td >
180+ <template v-if =" isLoading " >
181+ <Skeleton class =" h-4 w-40" />
182+ </template >
183+ <template v-else-if =" task ?.state === ' running' " >
184+ <LoaderCircleIcon class =" animate-spin" />
185+ </template >
186+ <template v-else-if =" task ?.state === ' abandoned' " ></template >
187+ <template v-else >
188+ {{ formatDate(String(task?.finishedAt), true) }}
189+ </template >
112190 </td >
113- <td v-else-if =" task.state === 'abandoned'" ></td >
114- <td v-else >{{ formatDate(String(task.finishedAt), true) }}</td >
115191 </tr >
116192 <tr >
117193 <th >Error</th >
118- <td >{{ task.error }}</td >
194+ <td >
195+ <template v-if =" ! isLoading " >
196+ {{ task?.error }}
197+ </template >
198+ <Skeleton
199+ v-else
200+ class =" h-4 w-full max-w-2xl"
201+ />
202+ </td >
119203 </tr >
120204 <tr >
121205 <th >Worker</th >
122- <td >{{ task.worker }}</td >
206+ <td >
207+ <template v-if =" ! isLoading " >
208+ {{ task?.worker }}
209+ </template >
210+ <Skeleton
211+ v-else
212+ class =" h-4 w-32"
213+ />
214+ </td >
123215 </tr >
124216 <tr >
125217 <th >Runtime</th >
126- <td >{{ task.executionTime }}</td >
218+ <td >
219+ <template v-if =" ! isLoading " >
220+ {{ task?.executionTime }}
221+ </template >
222+ <Skeleton
223+ v-else
224+ class =" h-4 w-24"
225+ />
226+ </td >
127227 </tr >
128228 </tbody >
129229 </table >
0 commit comments