From d359996b7af7f6cb219a3a8f7225ae0a64fdabf0 Mon Sep 17 00:00:00 2001 From: Feroze Mohideen Date: Thu, 4 Jan 2024 12:12:37 -0500 Subject: [PATCH] always show job footers for jobs in the proto (#4120) --- dashboard/src/lib/hooks/useAppStatus.ts | 7 +- dashboard/src/lib/porter-apps/services.ts | 19 +++++ .../app-dashboard/app-view/tabs/Overview.tsx | 7 +- .../services-settings/ServiceContainer.tsx | 23 ++++-- .../services-settings/ServiceList.tsx | 1 + .../services-settings/footers/JobFooter.tsx | 76 +++++++++++++++++++ .../{ => footers}/ServiceStatusFooter.tsx | 49 +----------- 7 files changed, 124 insertions(+), 58 deletions(-) create mode 100644 dashboard/src/main/home/app-dashboard/validate-apply/services-settings/footers/JobFooter.tsx rename dashboard/src/main/home/app-dashboard/validate-apply/services-settings/{ => footers}/ServiceStatusFooter.tsx (84%) diff --git a/dashboard/src/lib/hooks/useAppStatus.ts b/dashboard/src/lib/hooks/useAppStatus.ts index 2a09f5b62d0..1ef4a06905a 100644 --- a/dashboard/src/lib/hooks/useAppStatus.ts +++ b/dashboard/src/lib/hooks/useAppStatus.ts @@ -3,6 +3,8 @@ import _ from "lodash"; import pluralize from "pluralize"; import z from "zod"; +import { type ClientService } from "lib/porter-apps/services"; + import api from "shared/api"; import { useWebsockets, @@ -43,14 +45,14 @@ type SerializedServiceStatus = z.infer; export const useAppStatus = ({ projectId, clusterId, - serviceNames, + services, deploymentTargetId, appName, kind = "pod", }: { projectId: number; clusterId: number; - serviceNames: string[]; + services: ClientService[]; deploymentTargetId: string; appName: string; kind?: string; @@ -116,6 +118,7 @@ export const useAppStatus = ({ }; useEffect(() => { + const serviceNames = services.map((s) => s.name.value); void Promise.all(serviceNames.map(updatePods)); for (const serviceName of serviceNames) { setupWebsocket(serviceName); diff --git a/dashboard/src/lib/porter-apps/services.ts b/dashboard/src/lib/porter-apps/services.ts index b1fd3b40b93..d451bbd2392 100644 --- a/dashboard/src/lib/porter-apps/services.ts +++ b/dashboard/src/lib/porter-apps/services.ts @@ -39,6 +39,25 @@ export type DetectedServices = { }; type ClientServiceType = "web" | "worker" | "job" | "predeploy"; +type ClientWebService = ClientService & { config: ClientWebConfig }; +export const isClientWebService = ( + service: ClientService +): service is ClientWebService => { + return service.config.type === "web"; +}; +type ClientWorkerService = ClientService & { config: ClientWorkerConfig }; +export const isClientWorkerService = ( + service: ClientService +): service is ClientWorkerService => { + return service.config.type === "worker"; +}; +type ClientJobService = ClientService & { config: ClientJobConfig }; +export const isClientJobService = ( + service: ClientService +): service is ClientJobService => { + return service.config.type === "job"; +}; + const webConfigValidator = z.object({ type: z.literal("web"), autoscaling: autoscalingValidator.optional(), diff --git a/dashboard/src/main/home/app-dashboard/app-view/tabs/Overview.tsx b/dashboard/src/main/home/app-dashboard/app-view/tabs/Overview.tsx index 8289d7c9003..7d74c8f3133 100644 --- a/dashboard/src/main/home/app-dashboard/app-view/tabs/Overview.tsx +++ b/dashboard/src/main/home/app-dashboard/app-view/tabs/Overview.tsx @@ -9,6 +9,8 @@ import { type PorterAppFormData } from "lib/porter-apps"; import { defaultSerialized, deserializeService, + isClientWebService, + isClientWorkerService, } from "lib/porter-apps/services"; import { useClusterResources } from "shared/ClusterResourcesContext"; @@ -33,12 +35,15 @@ const Overview: React.FC = ({ buttonStatus }) => { projectId, clusterId, deploymentTarget, + latestClientServices, } = useLatestRevision(); const { serviceVersionStatus } = useAppStatus({ projectId, clusterId, - serviceNames: latestProto.serviceList.map((s) => s.name), + services: latestClientServices.filter( + (s) => isClientWebService(s) || isClientWorkerService(s) // we only care about the pod status of web and workers + ), deploymentTargetId: deploymentTarget.id, appName: latestProto.name, }); diff --git a/dashboard/src/main/home/app-dashboard/validate-apply/services-settings/ServiceContainer.tsx b/dashboard/src/main/home/app-dashboard/validate-apply/services-settings/ServiceContainer.tsx index 331c6b3e524..df007aa1761 100644 --- a/dashboard/src/main/home/app-dashboard/validate-apply/services-settings/ServiceContainer.tsx +++ b/dashboard/src/main/home/app-dashboard/validate-apply/services-settings/ServiceContainer.tsx @@ -8,14 +8,18 @@ import { match } from "ts-pattern"; import Spacer from "components/porter/Spacer"; import { type ClientServiceStatus } from "lib/hooks/useAppStatus"; import { type PorterAppFormData } from "lib/porter-apps"; -import { type ClientService } from "lib/porter-apps/services"; +import { + isClientJobService, + type ClientService, +} from "lib/porter-apps/services"; import chip from "assets/computer-chip.svg"; import job from "assets/job.png"; import web from "assets/web.png"; import worker from "assets/worker.png"; -import ServiceStatusFooter from "./ServiceStatusFooter"; +import JobFooter from "./footers/JobFooter"; +import ServiceStatusFooter from "./footers/ServiceStatusFooter"; import JobTabs from "./tabs/JobTabs"; import WebTabs from "./tabs/WebTabs"; import WorkerTabs from "./tabs/WorkerTabs"; @@ -38,6 +42,7 @@ type ServiceProps = { }; clusterIngressIp: string; showDisableTls: boolean; + existingServiceNames: string[]; }; const ServiceContainer: React.FC = ({ @@ -52,6 +57,7 @@ const ServiceContainer: React.FC = ({ internalNetworkingDetails, clusterIngressIp, showDisableTls, + existingServiceNames, }) => { const renderTabs = (service: ClientService): JSX.Element => { return match(service) @@ -186,13 +192,14 @@ const ServiceContainer: React.FC = ({ )} - {status && ( - + {!isClientJobService(service) && status && ( + )} + {isClientJobService(service) && + // make sure that this service is in a created revision before showing the job footer - cannot view history / run jobs that are not deployed + existingServiceNames.includes(service.name.value) && ( + + )} ); diff --git a/dashboard/src/main/home/app-dashboard/validate-apply/services-settings/ServiceList.tsx b/dashboard/src/main/home/app-dashboard/validate-apply/services-settings/ServiceList.tsx index 71dab62f1da..30819d33503 100644 --- a/dashboard/src/main/home/app-dashboard/validate-apply/services-settings/ServiceList.tsx +++ b/dashboard/src/main/home/app-dashboard/validate-apply/services-settings/ServiceList.tsx @@ -238,6 +238,7 @@ const ServiceList: React.FC = ({ internalNetworkingDetails={internalNetworkingDetails} clusterIngressIp={clusterIngressIp} showDisableTls={loadBalancerType === "ALB"} + existingServiceNames={existingServiceNames} /> ) : null; })} diff --git a/dashboard/src/main/home/app-dashboard/validate-apply/services-settings/footers/JobFooter.tsx b/dashboard/src/main/home/app-dashboard/validate-apply/services-settings/footers/JobFooter.tsx new file mode 100644 index 00000000000..8d8a188e5f6 --- /dev/null +++ b/dashboard/src/main/home/app-dashboard/validate-apply/services-settings/footers/JobFooter.tsx @@ -0,0 +1,76 @@ +import React from "react"; +import _ from "lodash"; +import styled from "styled-components"; + +import Button from "components/porter/Button"; +import Container from "components/porter/Container"; +import Link from "components/porter/Link"; +import Spacer from "components/porter/Spacer"; + +import { useLatestRevision } from "../../../app-view/LatestRevisionContext"; +import TriggerJobButton from "../../jobs/TriggerJobButton"; + +type JobFooterProps = { + jobName: string; +}; +const ServiceStatusFooter: React.FC = ({ jobName }) => { + const { latestProto, projectId, clusterId, deploymentTarget, appName } = + useLatestRevision(); + + return ( + + + + + + + + + + ); +}; + +export default ServiceStatusFooter; + +const I = styled.i` + font-size: 14px; + margin-right: 5px; +`; + +const StyledStatusFooter = styled.div` + width: 100%; + padding: 10px 15px; + background: ${(props) => props.theme.fg2}; + border-bottom-left-radius: 5px; + border-bottom-right-radius: 5px; + border: 1px solid #494b4f; + border-top: 0; + overflow: hidden; + display: flex; + align-items: stretch; + flex-direction: row; + animation: fadeIn 0.5s; + @keyframes fadeIn { + from { + opacity: 0; + } + to { + opacity: 1; + } + } +`; diff --git a/dashboard/src/main/home/app-dashboard/validate-apply/services-settings/ServiceStatusFooter.tsx b/dashboard/src/main/home/app-dashboard/validate-apply/services-settings/footers/ServiceStatusFooter.tsx similarity index 84% rename from dashboard/src/main/home/app-dashboard/validate-apply/services-settings/ServiceStatusFooter.tsx rename to dashboard/src/main/home/app-dashboard/validate-apply/services-settings/footers/ServiceStatusFooter.tsx index 68747956371..788047756e7 100644 --- a/dashboard/src/main/home/app-dashboard/validate-apply/services-settings/ServiceStatusFooter.tsx +++ b/dashboard/src/main/home/app-dashboard/validate-apply/services-settings/footers/ServiceStatusFooter.tsx @@ -7,7 +7,6 @@ import { match } from "ts-pattern"; import Button from "components/porter/Button"; import Container from "components/porter/Container"; import Link from "components/porter/Link"; -import Spacer from "components/porter/Spacer"; import Tag from "components/porter/Tag"; import Text from "components/porter/Text"; import { type ClientServiceStatus } from "lib/hooks/useAppStatus"; @@ -15,62 +14,18 @@ import { isClientServiceNotification } from "lib/porter-apps/notification"; import alert from "assets/alert-warning.svg"; -import { useLatestRevision } from "../../app-view/LatestRevisionContext"; -import TriggerJobButton from "../jobs/TriggerJobButton"; +import { useLatestRevision } from "../../../app-view/LatestRevisionContext"; type ServiceStatusFooterProps = { - serviceName: string; status: ClientServiceStatus[]; - isJob: boolean; }; const ServiceStatusFooter: React.FC = ({ - serviceName, status, - isJob, }) => { const [expanded, setExpanded] = useState(false); - const { - latestProto, - projectId, - clusterId, - deploymentTarget, - appName, - latestClientNotifications, - tabUrlGenerator, - } = useLatestRevision(); + const { latestClientNotifications, tabUrlGenerator } = useLatestRevision(); const [height, setHeight] = useState(0); - if (isJob) { - return ( - - - - - - - - - - ); - } - return ( <> {status.map((versionStatus, i) => {