Loading...
- if (error) return Job not found.
+ if (isLoading) return
{Job.Name}
-
+
@@ -61,4 +50,16 @@ const JobDetails = ({ jobId }: { jobId: string }) => {
)
}
+const JobDetailsSkeleton = () => (
+
+)
+
export default JobDetails
diff --git a/webui/components/jobs/details/JobExecutions.tsx b/webui/components/jobs/details/JobExecutions.tsx
index 1aff85fffd..60c761ed16 100644
--- a/webui/components/jobs/details/JobExecutions.tsx
+++ b/webui/components/jobs/details/JobExecutions.tsx
@@ -10,12 +10,11 @@ import {
} from '@/components/ui/table'
import { apimodels_ListJobExecutionsResponse } from '@/lib/api/generated'
import {
- formatTimestamp,
getExecutionDesiredStateLabel,
getExecutionStateLabel,
- getJobStateLabel,
shortID,
} from '@/lib/api/utils'
+import { formatTimestamp } from '@/lib/time'
const JobExecutions = ({
executions,
diff --git a/webui/components/jobs/details/JobHistory.tsx b/webui/components/jobs/details/JobHistory.tsx
index 3154b61f8c..8428b02340 100644
--- a/webui/components/jobs/details/JobHistory.tsx
+++ b/webui/components/jobs/details/JobHistory.tsx
@@ -13,7 +13,8 @@ import { Switch } from '@/components/ui/switch'
import { Button } from '@/components/ui/button'
import { Filter, X } from 'lucide-react'
import { apimodels_ListJobHistoryResponse } from '@/lib/api/generated'
-import { shortID, formatTime } from '@/lib/api/utils'
+import { shortID } from '@/lib/api/utils'
+import { formatTime } from '@/lib/time'
const colors = [
'text-blue-800',
diff --git a/webui/components/jobs/details/JobInformation.tsx b/webui/components/jobs/details/JobInformation.tsx
index 3b514ae9d2..f451216f36 100644
--- a/webui/components/jobs/details/JobInformation.tsx
+++ b/webui/components/jobs/details/JobInformation.tsx
@@ -1,12 +1,12 @@
import React from 'react'
-import { Card, CardHeader, CardContent, CardTitle } from '@/components/ui/card'
-import { Badge } from '@/components/ui/badge'
+import { Card, CardContent } from '@/components/ui/card'
import { models_Job } from '@/lib/api/generated'
-import { formatTimestamp, getJobRunTime } from '@/lib/api/utils'
+import { getJobRunTime } from '@/lib/api/utils'
import JobStatusBadge from '@/components/jobs/JobStatusBadge'
import JobEngineDisplay from '@/components/jobs/JobEngine'
import Labels from '@/components/Labels'
import InfoItem from '@/components/InfoItem'
+import { formatTimestamp } from '@/lib/time'
interface JobInformationProps {
job: models_Job
diff --git a/webui/components/jobs/details/JobLogs.tsx b/webui/components/jobs/details/JobLogs.tsx
index fc3a23b8c3..d86f9b3830 100644
--- a/webui/components/jobs/details/JobLogs.tsx
+++ b/webui/components/jobs/details/JobLogs.tsx
@@ -9,7 +9,7 @@ import {
CheckCircle,
} from 'lucide-react'
import { useApi } from '@/app/providers/ApiProvider'
-import { OpenAPI } from '@/lib/api'
+import { client } from '@/lib/api/generated'
interface LogEntry {
type: number
@@ -43,7 +43,15 @@ const JobLogs = ({ jobId }: { jobId: string | undefined }) => {
setError(null)
setIsStreamEnded(false)
- const wsUrl = `${OpenAPI.BASE.replace(/^http/, 'ws')}/api/v1/orchestrator/jobs/${jobId}/logs?follow=true`
+ const baseUrl = client.getConfig().baseUrl
+ if (!baseUrl) {
+ console.error('Base URL is not set')
+ setError(
+ 'Failed to connect to log stream. Client not configured properly.'
+ )
+ return
+ }
+ const wsUrl = `${baseUrl.replace(/^http/, 'ws')}/api/v1/orchestrator/jobs/${jobId}/logs?follow=true`
console.log('Attempting to connect to:', wsUrl)
const ws = new WebSocket(wsUrl)
diff --git a/webui/components/jobs/list/JobsOverview.tsx b/webui/components/jobs/list/JobsOverview.tsx
index c85de76074..cd7f8aa88b 100644
--- a/webui/components/jobs/list/JobsOverview.tsx
+++ b/webui/components/jobs/list/JobsOverview.tsx
@@ -4,7 +4,7 @@ import React, { useState, useEffect, useCallback } from 'react'
import { JobsTable } from './JobsTable'
import { Input } from '@/components/ui/input'
import { Button } from '@/components/ui/button'
-import { models_Job, OrchestratorService } from '@/lib/api/generated'
+import { Orchestrator, models_Job } from '@/lib/api/generated'
import { useApi } from '@/app/providers/ApiProvider'
import { useRefreshContent } from '@/hooks/useRefreshContent'
import { RefreshCw, Plus } from 'lucide-react'
@@ -15,27 +15,30 @@ export function JobsOverview() {
const { isInitialized } = useApi()
const [pageSize, setPageSize] = useState(10)
const [pageIndex, setPageIndex] = useState(0)
- const [nextToken, setNextToken] = useState
(undefined)
+ const [tokens, setTokens] = useState<(string | undefined)[]>([undefined])
const [isRefreshDisabled, setIsRefreshDisabled] = useState(false)
const fetchJobs = useCallback(async () => {
if (!isInitialized) return
try {
- const response = await OrchestratorService.orchestratorListJobs(
- undefined, // namespace
- pageSize,
- pageIndex === 0 ? undefined : nextToken,
- true, // reverse
- undefined // orderBy
- )
- setJobs(response.Items ?? [])
- setNextToken(response.NextToken)
+ const response = await Orchestrator.listJobs({
+ query: {
+ limit: pageSize,
+ next_token: tokens[pageIndex],
+ reverse: true,
+ },
+ throwOnError: true,
+ })
+ setJobs(response.data.Items ?? [])
+ if (response.data.NextToken && pageIndex === tokens.length - 1) {
+ setTokens([...tokens, response.data.NextToken])
+ }
} catch (error) {
console.error('Error fetching jobs:', error)
setJobs([])
}
- }, [isInitialized, pageSize, pageIndex, nextToken])
+ }, [isInitialized, pageSize, pageIndex, tokens])
useEffect(() => {
fetchJobs()
@@ -44,7 +47,7 @@ export function JobsOverview() {
const handleRefresh = useCallback(() => {
setIsRefreshDisabled(true)
setPageIndex(0)
- setNextToken(undefined)
+ setTokens([undefined])
fetchJobs().then(() => {
// Re-enable the refresh button after a short delay
setTimeout(() => setIsRefreshDisabled(false), 1000)
@@ -67,7 +70,7 @@ export function JobsOverview() {
}
const handleNextPage = () => {
- if (nextToken) {
+ if (pageIndex < tokens.length - 1) {
setPageIndex(pageIndex + 1)
}
}
@@ -75,7 +78,7 @@ export function JobsOverview() {
const handlePageSizeChange = (newSize: number) => {
setPageSize(newSize)
setPageIndex(0)
- setNextToken(undefined)
+ setTokens([undefined])
}
return (
@@ -113,7 +116,7 @@ export function JobsOverview() {
pageIndex={pageIndex}
onPreviousPage={handlePreviousPage}
onNextPage={handleNextPage}
- hasNextPage={!!nextToken}
+ hasNextPage={pageIndex < tokens.length - 1}
/>
)
diff --git a/webui/components/jobs/list/JobsTable.tsx b/webui/components/jobs/list/JobsTable.tsx
index c1e6ae79bd..b633ab6522 100644
--- a/webui/components/jobs/list/JobsTable.tsx
+++ b/webui/components/jobs/list/JobsTable.tsx
@@ -10,7 +10,7 @@ import {
} from '@/components/ui/table'
import TruncatedTextWithTooltip from '@/components/TruncatedTextWithTooltip'
import JobStatusBadge from '@/components/jobs/JobStatusBadge'
-import { formatTimestamp, getJobRunTime } from '@/lib/api/utils'
+import { getJobRunTime } from '@/lib/api/utils'
import JobEngineDisplay from '@/components/jobs/JobEngine'
import { Button } from '@/components/ui/button'
import {
@@ -20,6 +20,7 @@ import {
SelectTrigger,
SelectValue,
} from '@/components/ui/select'
+import { formatTimestamp } from '@/lib/time'
interface JobsTableProps {
jobs: models_Job[]
diff --git a/webui/components/nodes/NodeStatus.tsx b/webui/components/nodes/NodeStatus.tsx
index 55c68c6ef3..edd15989fc 100644
--- a/webui/components/nodes/NodeStatus.tsx
+++ b/webui/components/nodes/NodeStatus.tsx
@@ -1,9 +1,6 @@
import React from 'react'
import { Badge } from '@/components/ui/badge'
-import {
- models_NodeState,
- models_NodeMembershipState,
-} from '@/lib/api/generated'
+import { models_NodeState } from '@/lib/api/generated'
import {
getNodeConnectionStatus,
getNodeMembershipStatus,
diff --git a/webui/components/nodes/details/NodeActions.tsx b/webui/components/nodes/details/NodeActions.tsx
index eca37f5a5d..ed53f5bdc4 100644
--- a/webui/components/nodes/details/NodeActions.tsx
+++ b/webui/components/nodes/details/NodeActions.tsx
@@ -1,5 +1,5 @@
import React from 'react'
-import { models_NodeState, OrchestratorService } from '@/lib/api/generated'
+import { models_NodeState, Orchestrator } from '@/lib/api/generated'
import { Button } from '@/components/ui/button'
import { useToast } from '@/hooks/use-toast'
@@ -11,54 +11,35 @@ interface NodeActionsProps {
const NodeActions: React.FC