diff --git a/libs/domains/services/feature/src/index.ts b/libs/domains/services/feature/src/index.ts index a6c78c963e8..1115f99e231 100644 --- a/libs/domains/services/feature/src/index.ts +++ b/libs/domains/services/feature/src/index.ts @@ -23,6 +23,9 @@ export * from './lib/hooks/use-service-statuses/use-service-statuses' export * from './lib/hooks/use-service-type/use-service-type' export * from './lib/hooks/use-service/use-service' export * from './lib/hooks/use-services/use-services' +export * from './lib/hooks/use-service-deployment-and-running-statuses/use-service-deployment-and-running-statuses' +export * from './lib/hooks/use-services-list/use-services-list' +export * from './lib/hooks/use-services-list-context/use-services-list-context' export * from './lib/hooks/use-delete-all-services/use-delete-all-services' export * from './lib/hooks/use-delete-service/use-delete-service' export * from './lib/hooks/use-edit-service/use-edit-service' diff --git a/libs/domains/services/feature/src/lib/hooks/use-service-deployment-and-running-statuses/use-service-deployment-and-running-statuses.ts b/libs/domains/services/feature/src/lib/hooks/use-service-deployment-and-running-statuses/use-service-deployment-and-running-statuses.ts new file mode 100644 index 00000000000..15293334432 --- /dev/null +++ b/libs/domains/services/feature/src/lib/hooks/use-service-deployment-and-running-statuses/use-service-deployment-and-running-statuses.ts @@ -0,0 +1,72 @@ +import { useQuery } from '@tanstack/react-query' +import { P, match } from 'ts-pattern' +import { type AnyService } from '@qovery/domains/services/data-access' +import { type ServiceRunningStatus, type ServiceStatuses } from '@qovery/shared/interfaces' +import { upperCaseFirstLetter } from '@qovery/shared/util-js' +import { queries } from '@qovery/state/util-queries' + +export interface UseDeploymentFullStatusProps { + environmentId?: string + service?: AnyService +} + +export function useServiceDeploymentAndRunningStatuses({ + environmentId: envId, + service, +}: UseDeploymentFullStatusProps) { + const serviceId = service?.id ?? '' + const environmentId = envId ?? '' + const { data: runningStatus } = useQuery({ + ...queries.services.runningStatus(environmentId, serviceId), + }) + + const { data: deploymentStatus } = useQuery({ + ...queries.services.deploymentStatus(environmentId, serviceId), + }) + + const deploymentStatusLabel = upperCaseFirstLetter( + (deploymentStatus?.state === 'READY' ? 'NEVER_DEPLOYED' : deploymentStatus?.state)?.replace('_', ' ') ?? 'STOPPED' + ) + const runningStatusLabel = upperCaseFirstLetter(runningStatus?.state.replace('_', ' ') ?? 'STOPPED') + const isManagedDb = service?.serviceType === 'DATABASE' && service.mode === 'MANAGED' + const runningStatusOverride: ServiceRunningStatus = match({ runningStatus, isManagedDb }) + .with({ runningStatus: P.any, isManagedDb: true }, () => ({ + triggered_action: undefined, + ...deploymentStatus, + state: match(deploymentStatus?.state) + .with('DEPLOYED', () => 'RUNNING' as const) + .otherwise(() => 'UNKNOWN' as const), + stateLabel: match(deploymentStatus?.state) + .with('DEPLOYED', () => 'Running') + .otherwise(() => 'Unknown'), + })) + .with({ runningStatus: P.nullish, isManagedDb: false }, () => ({ + state: undefined, + stateLabel: undefined, + triggered_action: undefined, + })) + .with({ runningStatus: P.not(P.nullish) }, ({ runningStatus }) => ({ + triggered_action: undefined, // will be unpacked from runningStatus if present + ...runningStatus, + stateLabel: runningStatusLabel, + })) + .exhaustive() + + const data: ServiceStatuses = { + runningStatus: runningStatusOverride, + ...(deploymentStatus + ? { + deploymentStatus: { + ...deploymentStatus, + stateLabel: deploymentStatusLabel, + }, + } + : {}), + } + + return { + data, + } +} + +export default useServiceDeploymentAndRunningStatuses diff --git a/libs/domains/services/feature/src/lib/hooks/use-services-list-context/use-services-list-context.tsx b/libs/domains/services/feature/src/lib/hooks/use-services-list-context/use-services-list-context.tsx new file mode 100644 index 00000000000..7d45a426be4 --- /dev/null +++ b/libs/domains/services/feature/src/lib/hooks/use-services-list-context/use-services-list-context.tsx @@ -0,0 +1,68 @@ +import { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react' +import { type AnyService } from '@qovery/domains/services/data-access' +import { type ServiceStatuses } from '@qovery/shared/interfaces' +import { useServicesList } from '../use-services-list/use-services-list' + +type ServiceWithStatus = AnyService & ServiceStatuses + +interface ServicesListContextType { + statuses: Record + services: ServiceWithStatus[] + addStatusForService: (serviceId: string, status: ServiceStatuses) => void +} + +export function ServicesListProvider({ + children, + environmentId, +}: { + children: React.ReactNode + environmentId: string +}) { + const [servicesList, setServicesList] = useState([]) + const [statuses, setStatuses] = useState({}) + + const { data: servicesResponse = [] } = useServicesList({ environmentId, suspense: true }) + + useEffect(() => { + setServicesList(servicesResponse) + }, [servicesResponse]) + + const addStatusForService = useCallback((serviceId: string, status: ServiceStatuses) => { + setStatuses((prevStatuses) => ({ + ...prevStatuses, + [serviceId]: { + ...prevStatuses[serviceId], + ...status, + }, + })) + }, []) + + const services = useMemo((): ServiceWithStatus[] => { + return servicesList.map((service) => ({ + ...service, + runningStatus: statuses[service.id]?.runningStatus, + deploymentStatus: statuses[service.id]?.deploymentStatus, + })) + }, [servicesList, statuses]) + + const value = useMemo( + () => ({ + statuses, + services, + addStatusForService, + }), + [services, statuses, addStatusForService] + ) + + return {children} +} + +export const ServicesListContext = createContext(undefined) + +export function useServicesListContext() { + const context = useContext(ServicesListContext) + if (context === undefined) { + throw new Error('useServiceLogsContext must be used within a ServicesListProvider') + } + return context +} diff --git a/libs/domains/services/feature/src/lib/hooks/use-services-list/use-services-list.ts b/libs/domains/services/feature/src/lib/hooks/use-services-list/use-services-list.ts new file mode 100644 index 00000000000..1a366ca734d --- /dev/null +++ b/libs/domains/services/feature/src/lib/hooks/use-services-list/use-services-list.ts @@ -0,0 +1,21 @@ +import { useQuery } from '@tanstack/react-query' +import { queries } from '@qovery/state/util-queries' + +export interface UseServicesListProps { + environmentId?: string + suspense?: boolean +} + +export function useServicesList({ environmentId, suspense = false }: UseServicesListProps) { + return useQuery({ + ...queries.services.list(environmentId!), + select(services) { + services.sort(({ name: nameA }, { name: nameB }) => nameA.localeCompare(nameB)) + return services + }, + enabled: Boolean(environmentId), + suspense, + }) +} + +export default useServicesList diff --git a/libs/domains/services/feature/src/lib/last-version/last-version.tsx b/libs/domains/services/feature/src/lib/last-version/last-version.tsx index 0e82b58af4f..804ec94c265 100644 --- a/libs/domains/services/feature/src/lib/last-version/last-version.tsx +++ b/libs/domains/services/feature/src/lib/last-version/last-version.tsx @@ -117,7 +117,7 @@ export function LastVersion({ organizationId, projectId, service, version }: Las return ( - + @@ -125,7 +125,7 @@ export function LastVersion({ organizationId, projectId, service, version }: Las