Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

11156 new app status #11364

Merged
merged 41 commits into from
Oct 23, 2023
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
9fe7774
New version of about/overview/administration page
mlqn Sep 29, 2023
2931fb1
Add unit tests for new About page
mlqn Sep 29, 2023
9f34846
Remove extra div
mlqn Sep 29, 2023
a2ce329
Rename Administration to LegacyAdministration and About to Administra…
mlqn Oct 2, 2023
a734949
Fix background image
mlqn Oct 3, 2023
ea806c3
Fix background sizes
mlqn Oct 3, 2023
93f11a9
Replace metadata with appConfig
mlqn Oct 3, 2023
a79b84b
Fix unit tests
mlqn Oct 4, 2023
b540a34
Replace h1 with Heading
mlqn Oct 4, 2023
3b8b996
Merge remote-tracking branch 'origin/master' into 11156-new-app-status
mlqn Oct 11, 2023
7f99009
Add new app status
mlqn Oct 12, 2023
c52835b
Merge remote-tracking branch 'origin/master' into 11156-new-app-status
mlqn Oct 13, 2023
1d5929a
Fix date
mlqn Oct 13, 2023
b3dc46b
Update app status based on new sketches
mlqn Oct 13, 2023
2fa72f8
Update activity based on new sketches
mlqn Oct 13, 2023
e4b84af
Cleanup app status components
mlqn Oct 13, 2023
f7dccfa
Refactoring app status based on new sketches
mlqn Oct 13, 2023
5aa923e
Update app status names
mlqn Oct 13, 2023
3371dab
Merge remote-tracking branch 'origin/master' into 11156-new-app-status
mlqn Oct 14, 2023
b14828c
Revert prettier changes after merging
mlqn Oct 14, 2023
3230e57
Revert prettier changes after merging
mlqn Oct 14, 2023
49c5fdc
Revert prettier changes after merging
mlqn Oct 14, 2023
a810e9d
Add tests for AppStatus
mlqn Oct 14, 2023
724a0e6
Add tests for AppLogs
mlqn Oct 14, 2023
1d7f334
Add environments tests
mlqn Oct 14, 2023
3d50873
Fix block styles
mlqn Oct 15, 2023
a184bcf
Add error handling for app status
mlqn Oct 15, 2023
e53018b
Fix background-image
mlqn Oct 15, 2023
4eeef2d
Replace newAdministration flag with settingsModal
mlqn Oct 15, 2023
aa21d3e
Merge branch 'master' into 11156-new-app-status
framitdavid Oct 20, 2023
bebce28
first PR feedbacks and clean ups
framitdavid Oct 21, 2023
74682b1
removed unused dep
framitdavid Oct 21, 2023
0fd53d1
fixed broken tests
framitdavid Oct 21, 2023
6b535eb
revert prev-commit, let caseing be a css problem not js-problem
framitdavid Oct 21, 2023
431551e
forgotten to remove toUperCase from one test
framitdavid Oct 21, 2023
bd79c5e
PR feedback
framitdavid Oct 21, 2023
7e85b20
add text-key to nb.json
framitdavid Oct 21, 2023
caf8e1f
fixed wrong variant on profileMenu after designsystem update
framitdavid Oct 21, 2023
481cd0a
PR feedback
framitdavid Oct 23, 2023
b96cbdd
changed text
framitdavid Oct 23, 2023
1c43859
Merge branch 'master' into 11156-new-app-status
framitdavid Oct 23, 2023
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
2 changes: 1 addition & 1 deletion development/utils/ensure-dot-env.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ const os = require('os');
const getCommit = require('./git-commit-hash');
const randomPass = () =>
[Math.random().toString(36).substring(2, 5), Math.random().toString(36).substring(2, 5)].join(
'DIG@'
'DIG@',
);

const defaultEnvVars = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react';
import { screen } from '@testing-library/react';
import { screen, waitForElementToBeRemoved } from '@testing-library/react';
Fixed Show fixed Hide fixed
import { Administration } from './Administration';
import { APP_DEVELOPMENT_BASENAME } from 'app-shared/constants';
import { renderWithProviders } from '../../../test/testUtils';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { Heading } from '@digdir/design-system-react';
import { toast } from 'react-toastify';
import { useTranslation } from 'react-i18next';
import { Documentation } from './Documentation';
import { AppEnvironments } from './AppEnvironments';
import { AppLogs } from './AppLogs';

export const Administration = () => {
const { org, app } = useStudioUrlParams();
Expand All @@ -24,7 +26,8 @@ export const Administration = () => {
</div>
<div className={classes.content}>
<main className={classes.main}>
<div className={classes.placeholder}>{/* APP STATUS PLACEHOLDER */}</div>
<AppEnvironments />
<AppLogs />
<hr className={classes.divider} />
<div className={classes.placeholder} style={{ height: '300px' }}>
{/* NAVIGATION PLACEHOLDER */}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
.noEnvironments,
.appEnvironments {
background-color: #f4f5f6;
border-radius: 6px;
padding: var(--fds-spacing-4);
}

.appEnvironments {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: var(--fds-spacing-4);
}

.alert [class*='Paragraph-module_paragraph'] {
gap: var(--fds-spacing-6);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import React from 'react';
import classes from './AppEnvironments.module.css';
import { useStudioUrlParams } from 'app-shared/hooks/useStudioUrlParams';
import { useEnvironmentsQuery, useOrgListQuery } from 'app-development/hooks/queries';
import { AltinnSpinner } from 'app-shared/components';
import { ICreateAppDeploymentEnvObject } from 'app-development/sharedResources/appDeployment/types';
import { DeployEnvironment } from 'app-shared/types/DeployEnvironment';
import { AppStatus } from './AppStatus';
import { Alert, Heading, Paragraph } from '@digdir/design-system-react';
import { Trans, useTranslation } from 'react-i18next';

export const AppEnvironments = () => {
const { org } = useStudioUrlParams();
const { t } = useTranslation();

const { data: environmentList = [], isLoading: envIsLoading } = useEnvironmentsQuery();
const { data: orgs = { orgs: {} }, isLoading: orgsIsLoading } = useOrgListQuery();

if (envIsLoading || orgsIsLoading) return <AltinnSpinner />;

const selectedOrg = orgs.orgs[org];
const hasEnvironments = !(selectedOrg?.environments?.length ?? 0);

if (hasEnvironments) {
return (
<div className={classes.noEnvironments}>
<Alert severity='warning' className={classes.alert}>
<Heading level={2} size='small'>
{t('app_publish.no_env_title')}
</Heading>
<Paragraph>
<Trans i18nKey={'app_publish.no_env_1'}>
<a href='mailto:tjenesteeier@altinn.no' />
</Trans>
</Paragraph>
<Paragraph>
<Trans i18nKey={'app_publish.no_env_2'}>
<a target='_new' rel='noopener noreferrer' />
</Trans>
</Paragraph>
</Alert>
</div>
);
}

const orgEnvironments: ICreateAppDeploymentEnvObject[] = environmentList.filter(
(env: DeployEnvironment) => selectedOrg.environments.includes(env.name),
);

return (
<div className={classes.appEnvironments}>
{orgEnvironments.map((orgEnvironment: DeployEnvironment) => {
return (
<AppStatus
key={orgEnvironment.name}
envName={orgEnvironment.name}
envType={orgEnvironment.type}
/>
);
})}
</div>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
.appLogs {
background-color: #f4f5f6;
border-radius: 6px;

display: flex;
flex-direction: column;
gap: var(--fds-spacing-4);
justify-content: space-between;

padding: var(--fds-spacing-4);

max-height: 275px;
}

.appLogsTitle {
font-weight: unset !important;
}

.logs {
background-color: white;
border-radius: 6px;
display: flex;
flex-direction: column;
gap: var(--fds-spacing-6);
margin: 0;
padding: var(--fds-spacing-4);
padding-left: var(--fds-spacing-8);

overflow: auto;
}

.logTitle {
font-weight: bold;
margin-bottom: var(--fds-spacing-2);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import React from 'react';
import classes from './AppLogs.module.css';
import { useStudioUrlParams } from 'app-shared/hooks/useStudioUrlParams';
import { useAppDeploymentsQuery, useEnvironmentsQuery } from 'app-development/hooks/queries';
import { useTranslation } from 'react-i18next';
import { AltinnSpinner } from 'app-shared/components';
import { DeploymentStatus } from 'app-development/features/appPublish/components/appDeploymentComponent';
import { DeployEnvironment } from 'app-shared/types/DeployEnvironment';
import { IDeployment } from 'app-development/sharedResources/appDeployment/types';
import { Heading } from '@digdir/design-system-react';
import { formatDateTime } from 'app-shared/pure/date-format';

export const AppLogs = () => {
const { org, app } = useStudioUrlParams();
const { t } = useTranslation();

const { data: appDeployments = [], isLoading: deploysAreLoading } = useAppDeploymentsQuery(
org,
app,
);
const { data: environmentList = [], isLoading: envIsLoading } = useEnvironmentsQuery();

if (deploysAreLoading || envIsLoading) return <AltinnSpinner />;

const succeededDeployments = appDeployments.filter(
(deployment: IDeployment) =>
deployment.build.result === DeploymentStatus.succeeded && deployment.build.finished !== null,
);
const hasSucceededDeployments = succeededDeployments.length > 0;

return (
<div className={classes.appLogs}>
<Heading level={2} size='xxsmall' className={classes.appLogsTitle}>
{t('administration.activity')}
</Heading>
<ul className={classes.logs}>
{hasSucceededDeployments ? (
succeededDeployments.map((appDeployment) => {
const environmentType = environmentList
.find((env: DeployEnvironment) => env.name === appDeployment.envName)
?.type.toLowerCase();
return (
<li key={appDeployment.tagName}>
<div className={classes.logTitle}>
{`${t('general.version')} ${appDeployment.tagName} ${t(
`general.${environmentType}_environment`,
)}${environmentType === 'test' && ` ${appDeployment.envName.toUpperCase()}`}`}
</div>
<div>
{`(${appDeployment.createdBy}) ${t('general.date')}: ${formatDateTime(
appDeployment.created,
undefined,
` ${t('general.time_prefix')} `,
)}`}
</div>
</li>
);
})
) : (
<li>{t('administration.no_activity')}</li>
)}
</ul>
</div>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
.alert[class*='Alert-module_alert'] {
border-radius: 6px;
box-shadow: unset !important;
font-size: smaller !important;
display: block !important;
height: 115px !important;
position: relative;
}

.alert [class*='Paragraph-module_paragraph'] {
display: flex;
flex-direction: column;
gap: var(--fds-spacing-3);
font-size: medium !important;
height: 100%;
width: 100%;
}

.alert svg {
position: absolute;
right: var(--fds-spacing-3);
top: var(--fds-spacing-3);
}

.content {
flex: 1;
align-items: stretch;
}

.footer {
font-size: smaller;
color: #68707c;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import React, { useMemo } from 'react';
import classes from './AppStatus.module.css';
import { useStudioUrlParams } from 'app-shared/hooks/useStudioUrlParams';
import { useAppDeploymentsQuery } from 'app-development/hooks/queries';
import { Trans, useTranslation } from 'react-i18next';
import { Alert, Heading } from '@digdir/design-system-react';
import { AltinnSpinner } from 'app-shared/components';
import { DeploymentStatus } from 'app-development/features/appPublish/components/appDeploymentComponent';
import { formatDateTime } from 'app-shared/pure/date-format';
import { IDeployment } from 'app-development/sharedResources/appDeployment/types';
import { getReleaseBuildPipelineLink } from 'app-development/utils/urlHelper';
import { publishPath } from 'app-shared/api/paths';

export type AppStatusProps = {
envName: string;
envType: string;
};

export const AppStatus = ({ envName, envType }: AppStatusProps) => {
const { org, app } = useStudioUrlParams();
const { t } = useTranslation();

const { data: appDeployments = [], isLoading: deploysAreLoading } = useAppDeploymentsQuery(
org,
app,
);

const deployHistory: IDeployment[] = appDeployments.filter((x) => x.envName === envName);

const latestDeploy = deployHistory ? deployHistory[0] : null;
const deploymentInEnv = deployHistory.find((d) => d.deployedInEnv);
const { deployInProgress, deploymentStatus } = useMemo(() => {
if (latestDeploy && latestDeploy.build.finished === null) {
return { deployInProgress: true, deploymentStatus: DeploymentStatus.inProgress };
} else if (latestDeploy && latestDeploy.build.finished && latestDeploy.build.result) {
return { deployInProgress: false, deploymentStatus: latestDeploy.build.result };
} else {
return { deployInProgress: false, deploymentStatus: null };
}
}, [latestDeploy]);

const appDeployedAndReachable = !!deploymentInEnv;
const deployFailed = latestDeploy && deploymentStatus === DeploymentStatus.failed;
const deployedVersionNotReachable =
latestDeploy && !appDeployedAndReachable && deploymentStatus === DeploymentStatus.succeeded;
const noAppDeployed = !latestDeploy || deployInProgress;

if (deploysAreLoading) return <AltinnSpinner />;

const Status = ({
severity,
content,
footer,
}: {
severity: 'success' | 'warning' | 'info';
content: string;
footer: string | JSX.Element;
}) => {
return (
<Alert severity={severity} className={classes.alert}>
<Heading level={2} size='xsmall' className={classes.header}>
{envType.toLowerCase() === 'production' ? t('general.production') : envName.toUpperCase()}
</Heading>
<div className={classes.content}>{content}</div>
<div className={classes.footer}>{footer}</div>
</Alert>
);
};

if (appDeployedAndReachable && !deployInProgress) {
return (
<Status
severity='success'
content={t('administration.success')}
footer={
<Trans
i18nKey={'administration.last_published'}
values={{
lastPublishedDate: formatDateTime(
deploymentInEnv?.created,
undefined,
` ${t('general.time_prefix')} `,
),
}}
/>
}
></Status>
);
}

if (noAppDeployed || (deployFailed && !appDeployedAndReachable)) {
return (
<Status
severity='info'
content={t('administration.no_app')}
footer={
<Trans i18nKey='administration.go_to_publish'>
<a href={publishPath(org, app)} />
</Trans>
}
/>
);
}

if (deployedVersionNotReachable) {
return (
<Status
severity='warning'
content={t('administration.unavailable')}
footer={
<Trans i18nKey='administration.go_to_build_log'>
<a href={getReleaseBuildPipelineLink(deploymentInEnv?.build.id)} />
</Trans>
}
/>
);
}
};
Loading
Loading