1
1
import { API } from "api/api" ;
2
2
import type * as TypesGen from "api/typesGenerated" ;
3
+ import { Badge } from "components/Badge/Badge" ;
3
4
import { Button } from "components/Button/Button" ;
4
5
import { ExternalImage } from "components/ExternalImage/ExternalImage" ;
5
6
import { CoderIcon } from "components/Icons/CoderIcon" ;
6
7
import type { ProxyContextValue } from "contexts/ProxyContext" ;
7
8
import { useWebpushNotifications } from "contexts/useWebpushNotifications" ;
9
+ import { useAuthenticated } from "hooks" ;
8
10
import { useEmbeddedMetadata } from "hooks/useEmbeddedMetadata" ;
9
11
import { NotificationsInbox } from "modules/notifications/NotificationsInbox/NotificationsInbox" ;
12
+ import { data } from "pages/TasksPage/TasksPage" ;
10
13
import type { FC } from "react" ;
14
+ import { useQuery } from "react-query" ;
11
15
import { NavLink , useLocation } from "react-router" ;
12
16
import { cn } from "utils/cn" ;
13
17
import { DeploymentDropdown } from "./DeploymentDropdown" ;
@@ -144,7 +148,6 @@ interface NavItemsProps {
144
148
145
149
const NavItems :FC < NavItemsProps > = ( { className} ) => {
146
150
const location = useLocation ( ) ;
147
- const { metadata} = useEmbeddedMetadata ( ) ;
148
151
149
152
return (
150
153
< nav className = { cn ( "flex items-center gap-4 h-full" , className ) } >
@@ -167,16 +170,56 @@ const NavItems: FC<NavItemsProps> = ({ className }) => {
167
170
>
168
171
Templates
169
172
</ NavLink >
170
- { metadata [ "tasks-tab-visible" ] . value && (
171
- < NavLink
172
- className = { ( { isActive} ) => {
173
- return cn ( linkStyles . default , isActive ?linkStyles . active :"" ) ;
174
- } }
175
- to = "/tasks"
173
+ < TasksNavItem />
174
+ </ nav >
175
+ ) ;
176
+ } ;
177
+
178
+ const TasksNavItem :FC = ( ) => {
179
+ const { metadata} = useEmbeddedMetadata ( ) ;
180
+ const canSeeTasks =
181
+ ! ! metadata [ "tasks-tab-visible" ] . value ||
182
+ process . env . NODE_ENV === "development" ;
183
+ const { user} = useAuthenticated ( ) ;
184
+ const filter = {
185
+ user :{
186
+ label :user . username ,
187
+ value :user . username ,
188
+ avatarUrl :user . avatar_url ,
189
+ } ,
190
+ } ;
191
+ const { data :idleTasks } = useQuery ( {
192
+ queryKey :[ "tasks" , filter ] ,
193
+ queryFn :( ) => data . fetchTasks ( filter ) ,
194
+ refetchInterval :10_000 ,
195
+ enabled :canSeeTasks ,
196
+ refetchOnWindowFocus :true ,
197
+ select :( data ) =>
198
+ data . filter ( ( task ) => task . workspace . latest_app_status ?. state === "idle" ) ,
199
+ } ) ;
200
+
201
+ if ( ! canSeeTasks ) {
202
+ return null ;
203
+ }
204
+
205
+ return (
206
+ < NavLink
207
+ className = { ( { isActive} ) => {
208
+ return cn ( linkStyles . default , isActive ?linkStyles . active :"" ) ;
209
+ } }
210
+ to = "/tasks"
211
+ >
212
+ Tasks
213
+ { idleTasks && idleTasks . length > 0 && (
214
+ < Badge
215
+ variant = "info"
216
+ size = "xs"
217
+ className = "ml-2"
218
+ aria-label = { `You have${ idleTasks . length } tasks waiting for input` }
176
219
>
177
- Tasks
178
- </ NavLink >
220
+ { idleTasks . length }
221
+ </ Badge >
179
222
) }
180
- </ nav >
223
+ </ NavLink >
181
224
) ;
182
225
} ;