Skip to content

Commit

Permalink
always show job footers for jobs in the proto (#4120)
Browse files Browse the repository at this point in the history
  • Loading branch information
Feroze Mohideen authored Jan 4, 2024
1 parent e0b0a1a commit d359996
Show file tree
Hide file tree
Showing 7 changed files with 124 additions and 58 deletions.
7 changes: 5 additions & 2 deletions dashboard/src/lib/hooks/useAppStatus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -43,14 +45,14 @@ type SerializedServiceStatus = z.infer<typeof serviceStatusValidator>;
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;
Expand Down Expand Up @@ -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);
Expand Down
19 changes: 19 additions & 0 deletions dashboard/src/lib/porter-apps/services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -33,12 +35,15 @@ const Overview: React.FC<Props> = ({ 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,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -38,6 +42,7 @@ type ServiceProps = {
};
clusterIngressIp: string;
showDisableTls: boolean;
existingServiceNames: string[];
};

const ServiceContainer: React.FC<ServiceProps> = ({
Expand All @@ -52,6 +57,7 @@ const ServiceContainer: React.FC<ServiceProps> = ({
internalNetworkingDetails,
clusterIngressIp,
showDisableTls,
existingServiceNames,
}) => {
const renderTabs = (service: ClientService): JSX.Element => {
return match(service)
Expand Down Expand Up @@ -186,13 +192,14 @@ const ServiceContainer: React.FC<ServiceProps> = ({
</StyledSourceBox>
)}
</AnimatePresence>
{status && (
<ServiceStatusFooter
serviceName={service.name.value}
isJob={service.config.type === "job"}
status={status}
/>
{!isClientJobService(service) && status && (
<ServiceStatusFooter status={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) && (
<JobFooter jobName={service.name.value} />
)}
<Spacer y={0.5} />
</>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,7 @@ const ServiceList: React.FC<ServiceListProps> = ({
internalNetworkingDetails={internalNetworkingDetails}
clusterIngressIp={clusterIngressIp}
showDisableTls={loadBalancerType === "ALB"}
existingServiceNames={existingServiceNames}
/>
) : null;
})}
Expand Down
Original file line number Diff line number Diff line change
@@ -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<JobFooterProps> = ({ jobName }) => {
const { latestProto, projectId, clusterId, deploymentTarget, appName } =
useLatestRevision();

return (
<StyledStatusFooter>
<Container row>
<Link to={`/apps/${latestProto.name}/job-history?service=${jobName}`}>
<Button
onClick={() => {}}
height="30px"
width="87px"
color="#ffffff11"
withBorder
>
<I className="material-icons">open_in_new</I>
History
</Button>
</Link>
<Spacer inline x={1} />
<TriggerJobButton
projectId={projectId}
clusterId={clusterId}
appName={appName}
jobName={jobName}
deploymentTargetId={deploymentTarget.id}
/>
</Container>
</StyledStatusFooter>
);
};

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;
}
}
`;
Original file line number Diff line number Diff line change
Expand Up @@ -7,70 +7,25 @@ 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";
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<ServiceStatusFooterProps> = ({
serviceName,
status,
isJob,
}) => {
const [expanded, setExpanded] = useState<boolean>(false);
const {
latestProto,
projectId,
clusterId,
deploymentTarget,
appName,
latestClientNotifications,
tabUrlGenerator,
} = useLatestRevision();
const { latestClientNotifications, tabUrlGenerator } = useLatestRevision();
const [height, setHeight] = useState<Height>(0);

if (isJob) {
return (
<StyledStatusFooter>
<Container row>
<Link
to={`/apps/${latestProto.name}/job-history?service=${serviceName}`}
>
<Button
onClick={() => {}}
height="30px"
width="87px"
color="#ffffff11"
withBorder
>
<I className="material-icons">open_in_new</I>
History
</Button>
</Link>
<Spacer inline x={1} />
<TriggerJobButton
projectId={projectId}
clusterId={clusterId}
appName={appName}
jobName={serviceName}
deploymentTargetId={deploymentTarget.id}
/>
</Container>
</StyledStatusFooter>
);
}

return (
<>
{status.map((versionStatus, i) => {
Expand Down

0 comments on commit d359996

Please sign in to comment.