Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions pkg/app/web/src/components/app-live-state.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import React from "react";
import { Provider } from "react-redux";
import { createStore } from "../../test-utils";
import { dummyApplication } from "../__fixtures__/dummy-application";
import { dummyApplicationLiveState } from "../__fixtures__/dummy-application-live-state";
import { AppLiveState } from "./app-live-state";

export default {
title: "APPLICATION/AppLiveState",
component: AppLiveState,
};

export const overview: React.FC = () => (
<Provider
store={createStore({
applicationLiveState: {
entities: {
[dummyApplicationLiveState.applicationId]: dummyApplicationLiveState,
},
ids: [dummyApplicationLiveState.applicationId],
hasError: {},
loading: {},
},
})}
>
<AppLiveState applicationId={dummyApplication.id} />
</Provider>
);

export const loading: React.FC = () => (
<Provider
store={createStore({
applicationLiveState: {
entities: {},
ids: [],
hasError: {},
loading: {
[dummyApplication.id]: true,
},
},
})}
>
<AppLiveState applicationId={dummyApplication.id} />
</Provider>
);

export const notAvailable: React.FC = () => (
<Provider
store={createStore({
applicationLiveState: {
entities: {},
ids: [],
hasError: {},
loading: {},
},
})}
>
<AppLiveState applicationId={dummyApplication.id} />
</Provider>
);
53 changes: 53 additions & 0 deletions pkg/app/web/src/components/app-live-state.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import React, { FC, memo } from "react";
import { Box, makeStyles, Typography } from "@material-ui/core";
import { useSelector } from "react-redux";
import { AppState } from "../modules";
import {
ApplicationLiveState,
selectById,
selectLoadingById,
} from "../modules/applications-live-state";
import Skeleton from "@material-ui/lab/Skeleton";
import { ApplicationHealthStatusIcon } from "./health-status-icon";
import { APPLICATION_HEALTH_STATUS_TEXT } from "../constants/health-status-text";
import { UI_TEXT_NOT_AVAILABLE_TEXT } from "../constants/ui-text";

const useStyles = makeStyles((theme) => ({
liveStateText: {
marginLeft: theme.spacing(0.5),
},
}));

interface Props {
applicationId: string;
}

export const AppLiveState: FC<Props> = memo(function AppLiveState({
applicationId,
}) {
const classes = useStyles();
const [liveState, liveStateLoading] = useSelector<
AppState,
[ApplicationLiveState | undefined, boolean]
>((state) => [
selectById(state.applicationLiveState, applicationId),
selectLoadingById(state.applicationLiveState, applicationId),
]);

if (liveStateLoading) {
return <Skeleton height={32} width={100} />;
}

return (
<Box display="flex" alignItems="center">
{liveState ? (
<ApplicationHealthStatusIcon health={liveState.healthStatus} />
) : null}
<Typography variant="h6" className={classes.liveStateText}>
{liveState
? APPLICATION_HEALTH_STATUS_TEXT[liveState.healthStatus]
: UI_TEXT_NOT_AVAILABLE_TEXT}
</Typography>
</Box>
);
});
31 changes: 31 additions & 0 deletions pkg/app/web/src/components/application-detail.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ const dummyStore: Partial<AppState> = {
[dummyApplicationLiveState.applicationId]: dummyApplicationLiveState,
},
ids: [dummyApplicationLiveState.applicationId],
loading: {},
hasError: {},
},
pipeds: {
Expand Down Expand Up @@ -91,6 +92,9 @@ export const loadingLiveState: React.FC = () => (
applicationLiveState: {
entities: {},
ids: [],
loading: {
[dummyApplication.id]: true,
},
},
applications: {
adding: false,
Expand All @@ -116,3 +120,30 @@ export const loadingLiveState: React.FC = () => (
<ApplicationDetail applicationId={dummyApplication.id} />
</Provider>
);

export const notAvailable: React.FC = () => (
<Provider
store={createStore({
...dummyStore,
applicationLiveState: {
entities: {},
ids: [],
loading: {},
},
applications: {
adding: false,
disabling: {},
syncing: {},
entities: {
[dummyApplication.id]: {
...dummyApplication,
syncState: undefined,
},
},
ids: [dummyApplication.id],
},
})}
>
<ApplicationDetail applicationId={dummyApplication.id} />
</Provider>
);
1 change: 1 addition & 0 deletions pkg/app/web/src/components/application-detail.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ const baseState: DeepPartial<AppState> = {
entities: {
[dummyApplicationLiveState.applicationId]: dummyApplicationLiveState,
},
loading: {},
hasError: {},
},
environments: {
Expand Down
33 changes: 4 additions & 29 deletions pkg/app/web/src/components/application-detail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import React, { FC, memo } from "react";
import { useDispatch, useSelector } from "react-redux";
import { Link as RouterLink } from "react-router-dom";
import { APPLICATION_KIND_TEXT } from "../constants/application-kind";
import { APPLICATION_HEALTH_STATUS_TEXT } from "../constants/health-status-text";
import { PAGE_PATH_DEPLOYMENTS } from "../constants/path";
import { UI_TEXT_REFRESH } from "../constants/ui-text";
import { AppState } from "../modules";
Expand All @@ -27,19 +26,15 @@ import {
selectById as selectApplicationById,
syncApplication,
} from "../modules/applications";
import {
ApplicationLiveState,
selectById as selectLiveStateById,
} from "../modules/applications-live-state";
import { SyncStrategy } from "../modules/deployments";
import {
Environment,
selectById as selectEnvById,
} from "../modules/environments";
import { Piped, selectById as selectPipeById } from "../modules/pipeds";
import { AppLiveState } from "./app-live-state";
import { AppSyncStatus } from "./app-sync-status";
import { DetailTableRow } from "./detail-table-row";
import { ApplicationHealthStatusIcon } from "./health-status-icon";
import { SplitButton } from "./split-button";
import { SyncStateReason } from "./sync-state-reason";

Expand All @@ -64,9 +59,6 @@ const useStyles = makeStyles((theme) => ({
appSyncState: {
marginRight: theme.spacing(1),
},
liveStateText: {
marginLeft: theme.spacing(0.5),
},
buttonProgress: {
color: theme.palette.primary.main,
position: "absolute",
Expand Down Expand Up @@ -157,16 +149,11 @@ export const ApplicationDetail: FC<Props> = memo(function ApplicationDetail({
const classes = useStyles();
const dispatch = useDispatch();

const [app, liveState, fetchApplicationError] = useSelector<
const [app, fetchApplicationError] = useSelector<
AppState,
[
Application | undefined,
ApplicationLiveState | undefined,
SerializedError | null
]
[Application | undefined, SerializedError | null]
>((state) => [
selectApplicationById(state.applications, applicationId),
selectLiveStateById(state.applicationLiveState, applicationId),
state.applications.fetchApplicationError,
]);

Expand Down Expand Up @@ -242,19 +229,7 @@ export const ApplicationDetail: FC<Props> = memo(function ApplicationDetail({
size="large"
className={classes.appSyncState}
/>

{liveState ? (
<>
<ApplicationHealthStatusIcon
health={liveState.healthStatus}
/>
<Typography variant="h6" className={classes.liveStateText}>
{APPLICATION_HEALTH_STATUS_TEXT[liveState.healthStatus]}
</Typography>
</>
) : (
<Skeleton height={32} width={100} />
)}
<AppLiveState applicationId={applicationId} />
</Box>

{app.syncState && (
Expand Down
14 changes: 12 additions & 2 deletions pkg/app/web/src/modules/applications-live-state.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
const initialState: ApplicationLiveStateState = {
entities: {},
hasError: {},
loading: {},
ids: [],
};

Expand All @@ -29,7 +30,11 @@ describe("applicationLiveStateSlice reducer", () => {
arg: "application-1",
},
})
).toEqual({ ...initialState, hasError: { "application-1": false } });
).toEqual({
...initialState,
hasError: { "application-1": false },
loading: { "application-1": true },
});
});

it(`should handle ${fetchApplicationStateById.rejected.type}`, () => {
Expand All @@ -43,7 +48,11 @@ describe("applicationLiveStateSlice reducer", () => {
},
}
)
).toEqual({ ...initialState, hasError: { "application-1": true } });
).toEqual({
...initialState,
hasError: { "application-1": true },
loading: { "application-1": false },
});
});

it(`should handle ${fetchApplicationStateById.fulfilled.type}`, () => {
Expand All @@ -64,6 +73,7 @@ describe("applicationLiveStateSlice reducer", () => {
},
ids: [dummyApplicationLiveState.applicationId],
hasError: { "application-1": false },
loading: { "application-1": false },
});
});
});
Expand Down
12 changes: 12 additions & 0 deletions pkg/app/web/src/modules/applications-live-state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,10 @@ export const fetchApplicationStateById = createAsyncThunk<
});

const initialState = applicationLiveStateAdapter.getInitialState<{
loading: Record<string, boolean>;
hasError: Record<string, boolean>;
}>({
loading: {},
hasError: {},
});

Expand All @@ -53,22 +55,32 @@ export const selectHasError = (
return state.hasError[applicationId] || false;
};

export const selectLoadingById = (
state: ApplicationLiveStateState,
applicationId: string
): boolean => {
return state.loading[applicationId] || false;
};

export const applicationLiveStateSlice = createSlice({
name: "applicationLiveState",
initialState,
reducers: {},
extraReducers: (builder) => {
builder
.addCase(fetchApplicationStateById.pending, (state, action) => {
state.loading[action.meta.arg] = true;
state.hasError[action.meta.arg] = false;
})
.addCase(fetchApplicationStateById.fulfilled, (state, action) => {
state.loading[action.meta.arg] = false;
state.hasError[action.meta.arg] = false;
if (action.payload) {
applicationLiveStateAdapter.upsertOne(state, action.payload);
}
})
.addCase(fetchApplicationStateById.rejected, (state, action) => {
state.loading[action.meta.arg] = false;
state.hasError[action.meta.arg] = true;
});
},
Expand Down