mirror of
https://github.com/logto-io/logto.git
synced 2025-02-17 22:04:19 -05:00
fix(console): fix details page state (#3569)
This commit is contained in:
parent
540c41ff64
commit
9c17de0906
15 changed files with 161 additions and 229 deletions
|
@ -0,0 +1,17 @@
|
|||
@use '@/scss/underscore' as _;
|
||||
|
||||
.container {
|
||||
min-height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-width: min-content;
|
||||
|
||||
> *:not(:first-child) {
|
||||
margin-top: _.unit(4);
|
||||
}
|
||||
}
|
||||
|
||||
.backLink {
|
||||
margin: _.unit(1) 0 0 _.unit(1);
|
||||
user-select: none;
|
||||
}
|
52
packages/console/src/components/DetailsPage/index.tsx
Normal file
52
packages/console/src/components/DetailsPage/index.tsx
Normal file
|
@ -0,0 +1,52 @@
|
|||
import type { AdminConsoleKey } from '@logto/phrases';
|
||||
import classNames from 'classnames';
|
||||
import type { ReactElement, ReactNode } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import Back from '@/assets/images/back.svg';
|
||||
import type { RequestError } from '@/hooks/use-api';
|
||||
|
||||
import type DangerousRaw from '../DangerousRaw';
|
||||
import DetailsSkeleton from '../DetailsSkeleton';
|
||||
import RequestDataError from '../RequestDataError';
|
||||
import TextLink from '../TextLink';
|
||||
import * as styles from './index.module.scss';
|
||||
|
||||
type Props = {
|
||||
backLink: string;
|
||||
backLinkTitle?: AdminConsoleKey | ReactElement<typeof DangerousRaw>;
|
||||
isLoading?: boolean;
|
||||
error?: RequestError;
|
||||
onRetry?: () => void;
|
||||
children: ReactNode;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
function DetailsPage({
|
||||
backLink,
|
||||
backLinkTitle,
|
||||
isLoading,
|
||||
error,
|
||||
onRetry,
|
||||
children,
|
||||
className,
|
||||
}: Props) {
|
||||
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
||||
|
||||
return (
|
||||
<div className={classNames(styles.container, className)}>
|
||||
<TextLink to={backLink} icon={<Back />} className={styles.backLink}>
|
||||
{typeof backLinkTitle === 'string' ? t(backLinkTitle) : backLinkTitle}
|
||||
</TextLink>
|
||||
{isLoading ? (
|
||||
<DetailsSkeleton />
|
||||
) : error ? (
|
||||
<RequestDataError error={error} onRetry={onRetry} />
|
||||
) : (
|
||||
children
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default DetailsPage;
|
|
@ -4,11 +4,6 @@
|
|||
height: 100%;
|
||||
}
|
||||
|
||||
.backLink {
|
||||
margin: _.unit(1) 0 0 _.unit(1);
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.deleteConfirm {
|
||||
> :not(:first-child) {
|
||||
margin-top: _.unit(6);
|
||||
|
|
|
@ -9,22 +9,18 @@ import useSWR from 'swr';
|
|||
|
||||
import ApiResourceDark from '@/assets/images/api-resource-dark.svg';
|
||||
import ApiResource from '@/assets/images/api-resource.svg';
|
||||
import Back from '@/assets/images/back.svg';
|
||||
import Delete from '@/assets/images/delete.svg';
|
||||
import More from '@/assets/images/more.svg';
|
||||
import ActionMenu, { ActionMenuItem } from '@/components/ActionMenu';
|
||||
import Card from '@/components/Card';
|
||||
import CopyToClipboard from '@/components/CopyToClipboard';
|
||||
import DeleteConfirmModal from '@/components/DeleteConfirmModal';
|
||||
import DetailsSkeleton from '@/components/DetailsSkeleton';
|
||||
import RequestDataError from '@/components/RequestDataError';
|
||||
import DetailsPage from '@/components/DetailsPage';
|
||||
import TabNav, { TabNavItem } from '@/components/TabNav';
|
||||
import TextLink from '@/components/TextLink';
|
||||
import { ApiResourceDetailsTabs } from '@/consts/page-tabs';
|
||||
import type { RequestError } from '@/hooks/use-api';
|
||||
import useApi from '@/hooks/use-api';
|
||||
import useTheme from '@/hooks/use-theme';
|
||||
import * as detailsStyles from '@/scss/details.module.scss';
|
||||
import { withAppInsights } from '@/utils/app-insights';
|
||||
|
||||
import * as styles from './index.module.scss';
|
||||
|
@ -70,21 +66,14 @@ function ApiResourceDetails() {
|
|||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames(detailsStyles.container, isOnPermissionPage && styles.permissionPage)}
|
||||
<DetailsPage
|
||||
backLink="/api-resources"
|
||||
backLinkTitle="api_resource_details.back_to_api_resources"
|
||||
isLoading={isLoading}
|
||||
error={error}
|
||||
className={classNames(isOnPermissionPage && styles.permissionPage)}
|
||||
onRetry={mutate}
|
||||
>
|
||||
<TextLink to="/api-resources" icon={<Back />} className={styles.backLink}>
|
||||
{t('api_resource_details.back_to_api_resources')}
|
||||
</TextLink>
|
||||
{isLoading && <DetailsSkeleton />}
|
||||
{error && (
|
||||
<RequestDataError
|
||||
error={error}
|
||||
onRetry={() => {
|
||||
void mutate();
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{data && (
|
||||
<>
|
||||
<Card className={styles.header}>
|
||||
|
@ -153,7 +142,7 @@ function ApiResourceDetails() {
|
|||
/>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</DetailsPage>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,5 @@
|
|||
@use '@/scss/underscore' as _;
|
||||
|
||||
.backLink {
|
||||
margin: _.unit(1) 0 0 _.unit(1);
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.deleteConfirm {
|
||||
> :not(:first-child) {
|
||||
margin-top: _.unit(6);
|
||||
|
|
|
@ -7,7 +7,6 @@ import { Trans, useTranslation } from 'react-i18next';
|
|||
import { useLocation, useNavigate, useParams } from 'react-router-dom';
|
||||
import useSWR from 'swr';
|
||||
|
||||
import Back from '@/assets/images/back.svg';
|
||||
import Delete from '@/assets/images/delete.svg';
|
||||
import More from '@/assets/images/more.svg';
|
||||
import ActionMenu, { ActionMenuItem } from '@/components/ActionMenu';
|
||||
|
@ -17,16 +16,13 @@ import Card from '@/components/Card';
|
|||
import CopyToClipboard from '@/components/CopyToClipboard';
|
||||
import DeleteConfirmModal from '@/components/DeleteConfirmModal';
|
||||
import DetailsForm from '@/components/DetailsForm';
|
||||
import DetailsSkeleton from '@/components/DetailsSkeleton';
|
||||
import DetailsPage from '@/components/DetailsPage';
|
||||
import Drawer from '@/components/Drawer';
|
||||
import RequestDataError from '@/components/RequestDataError';
|
||||
import TabNav, { TabNavItem } from '@/components/TabNav';
|
||||
import TextLink from '@/components/TextLink';
|
||||
import UnsavedChangesAlertModal from '@/components/UnsavedChangesAlertModal';
|
||||
import type { RequestError } from '@/hooks/use-api';
|
||||
import useApi from '@/hooks/use-api';
|
||||
import useDocumentationUrl from '@/hooks/use-documentation-url';
|
||||
import * as detailsStyles from '@/scss/details.module.scss';
|
||||
import { applicationTypeI18nKey } from '@/types/applications';
|
||||
import { withAppInsights } from '@/utils/app-insights';
|
||||
|
||||
|
@ -131,20 +127,16 @@ function ApplicationDetails() {
|
|||
};
|
||||
|
||||
return (
|
||||
<div className={detailsStyles.container}>
|
||||
<TextLink to="/applications" icon={<Back />} className={styles.backLink}>
|
||||
{t('application_details.back_to_applications')}
|
||||
</TextLink>
|
||||
{isLoading && <DetailsSkeleton />}
|
||||
{requestError && (
|
||||
<RequestDataError
|
||||
error={requestError}
|
||||
onRetry={() => {
|
||||
void mutate();
|
||||
void mutateOidcConfig();
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<DetailsPage
|
||||
backLink="/applications"
|
||||
backLinkTitle="application_details.back_to_applications"
|
||||
isLoading={isLoading}
|
||||
error={requestError}
|
||||
onRetry={() => {
|
||||
void mutate();
|
||||
void mutateOidcConfig();
|
||||
}}
|
||||
>
|
||||
{data && oidcConfig && (
|
||||
<>
|
||||
<Card className={styles.header}>
|
||||
|
@ -236,7 +228,7 @@ function ApplicationDetails() {
|
|||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</DetailsPage>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,5 @@
|
|||
@use '@/scss/underscore' as _;
|
||||
|
||||
.backLink {
|
||||
margin: _.unit(1) 0 0 _.unit(1);
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.header {
|
||||
padding: _.unit(6);
|
||||
display: flex;
|
||||
|
@ -47,6 +42,10 @@
|
|||
}
|
||||
|
||||
.body {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-bottom: 0;
|
||||
margin-bottom: _.unit(6);
|
||||
|
||||
> :not(:first-child) {
|
||||
|
|
|
@ -1,23 +1,19 @@
|
|||
import type { User, Log } from '@logto/schemas';
|
||||
import { demoAppApplicationId } from '@logto/schemas';
|
||||
import classNames from 'classnames';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useLocation, useParams } from 'react-router-dom';
|
||||
import useSWR from 'swr';
|
||||
|
||||
import Back from '@/assets/images/back.svg';
|
||||
import ApplicationName from '@/components/ApplicationName';
|
||||
import Card from '@/components/Card';
|
||||
import CodeEditor from '@/components/CodeEditor';
|
||||
import DetailsSkeleton from '@/components/DetailsSkeleton';
|
||||
import DangerousRaw from '@/components/DangerousRaw';
|
||||
import DetailsPage from '@/components/DetailsPage';
|
||||
import FormField from '@/components/FormField';
|
||||
import RequestDataError from '@/components/RequestDataError';
|
||||
import TabNav, { TabNavItem } from '@/components/TabNav';
|
||||
import TextLink from '@/components/TextLink';
|
||||
import UserName from '@/components/UserName';
|
||||
import { logEventTitle } from '@/consts/logs';
|
||||
import type { RequestError } from '@/hooks/use-api';
|
||||
import * as detailsStyles from '@/scss/details.module.scss';
|
||||
import { withAppInsights } from '@/utils/app-insights';
|
||||
|
||||
import EventIcon from './components/EventIcon';
|
||||
|
@ -48,19 +44,13 @@ function AuditLogDetails() {
|
|||
}
|
||||
|
||||
return (
|
||||
<div className={detailsStyles.container}>
|
||||
<TextLink to={backLink} icon={<Back />} className={styles.backLink}>
|
||||
{backLinkTitle}
|
||||
</TextLink>
|
||||
{isLoading && <DetailsSkeleton />}
|
||||
{error && (
|
||||
<RequestDataError
|
||||
error={error}
|
||||
onRetry={() => {
|
||||
void mutate();
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<DetailsPage
|
||||
backLink={backLink}
|
||||
backLinkTitle={<DangerousRaw>{backLinkTitle}</DangerousRaw>}
|
||||
isLoading={isLoading}
|
||||
error={error}
|
||||
onRetry={mutate}
|
||||
>
|
||||
{data && (
|
||||
<>
|
||||
<Card className={styles.header}>
|
||||
|
@ -117,7 +107,7 @@ function AuditLogDetails() {
|
|||
{t('log_details.tab_details')}
|
||||
</TabNavItem>
|
||||
</TabNav>
|
||||
<Card className={classNames(styles.body, detailsStyles.body)}>
|
||||
<Card className={styles.body}>
|
||||
<div className={styles.main}>
|
||||
<FormField title="log_details.raw_data">
|
||||
<CodeEditor language="json" value={JSON.stringify(data.payload, null, 2)} />
|
||||
|
@ -126,7 +116,7 @@ function AuditLogDetails() {
|
|||
</Card>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</DetailsPage>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,5 @@
|
|||
@use '@/scss/underscore' as _;
|
||||
|
||||
.backLink {
|
||||
margin: _.unit(1) 0 0 _.unit(1);
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.header {
|
||||
padding: _.unit(6) _.unit(8);
|
||||
display: flex;
|
||||
|
|
|
@ -6,7 +6,6 @@ import { useTranslation } from 'react-i18next';
|
|||
import { useLocation, useNavigate, useParams } from 'react-router-dom';
|
||||
import useSWR, { useSWRConfig } from 'swr';
|
||||
|
||||
import Back from '@/assets/images/back.svg';
|
||||
import Delete from '@/assets/images/delete.svg';
|
||||
import More from '@/assets/images/more.svg';
|
||||
import Reset from '@/assets/images/reset.svg';
|
||||
|
@ -15,19 +14,16 @@ import Button from '@/components/Button';
|
|||
import Card from '@/components/Card';
|
||||
import ConnectorLogo from '@/components/ConnectorLogo';
|
||||
import CopyToClipboard from '@/components/CopyToClipboard';
|
||||
import DetailsSkeleton from '@/components/DetailsSkeleton';
|
||||
import DetailsPage from '@/components/DetailsPage';
|
||||
import Drawer from '@/components/Drawer';
|
||||
import Markdown from '@/components/Markdown';
|
||||
import RequestDataError from '@/components/RequestDataError';
|
||||
import Status from '@/components/Status';
|
||||
import TabNav, { TabNavItem } from '@/components/TabNav';
|
||||
import TextLink from '@/components/TextLink';
|
||||
import UnnamedTrans from '@/components/UnnamedTrans';
|
||||
import { ConnectorsTabs } from '@/consts/page-tabs';
|
||||
import type { RequestError } from '@/hooks/use-api';
|
||||
import useApi from '@/hooks/use-api';
|
||||
import useConnectorInUse from '@/hooks/use-connector-in-use';
|
||||
import * as detailsStyles from '@/scss/details.module.scss';
|
||||
import { withAppInsights } from '@/utils/app-insights';
|
||||
|
||||
import CreateForm from '../Connectors/components/CreateForm';
|
||||
|
@ -108,22 +104,18 @@ function ConnectorDetails() {
|
|||
}
|
||||
|
||||
return (
|
||||
<div className={detailsStyles.container}>
|
||||
<TextLink to={getConnectorsPathname(isSocial)} icon={<Back />} className={styles.backLink}>
|
||||
{t('connector_details.back_to_connectors')}
|
||||
</TextLink>
|
||||
{requestError && (
|
||||
<RequestDataError
|
||||
error={requestError}
|
||||
onRetry={() => {
|
||||
void mutate();
|
||||
void mutateConnectorFactory();
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{isLoading && !requestError && <DetailsSkeleton />}
|
||||
<DetailsPage
|
||||
backLink={getConnectorsPathname(isSocial)}
|
||||
backLinkTitle="connector_details.back_to_connectors"
|
||||
isLoading={isLoading}
|
||||
error={requestError}
|
||||
onRetry={() => {
|
||||
void mutate();
|
||||
void mutateConnectorFactory();
|
||||
}}
|
||||
>
|
||||
{isSocial && <ConnectorTabs target={data.target} connectorId={data.id} />}
|
||||
{!requestError && data && (
|
||||
{data && (
|
||||
<>
|
||||
<Card className={styles.header}>
|
||||
<ConnectorLogo data={data} size="large" />
|
||||
|
@ -232,7 +224,7 @@ function ConnectorDetails() {
|
|||
/>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</DetailsPage>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -4,39 +4,31 @@
|
|||
height: 100%;
|
||||
}
|
||||
|
||||
.container {
|
||||
.backLink {
|
||||
margin: _.unit(1) 0 0 _.unit(1);
|
||||
user-select: none;
|
||||
.header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: _.unit(6) _.unit(8);
|
||||
|
||||
.info {
|
||||
.name {
|
||||
font: var(--font-title-1);
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.meta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.idText {
|
||||
font: var(--font-label-2);
|
||||
color: var(--color-text-secondary);
|
||||
margin-right: _.unit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: _.unit(6) _.unit(8);
|
||||
|
||||
.info {
|
||||
.name {
|
||||
font: var(--font-title-1);
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.meta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.idText {
|
||||
font: var(--font-label-2);
|
||||
color: var(--color-text-secondary);
|
||||
margin-right: _.unit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.moreIcon {
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
.moreIcon {
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,21 +6,17 @@ import { useTranslation } from 'react-i18next';
|
|||
import { Outlet, useLocation, useNavigate, useParams } from 'react-router-dom';
|
||||
import useSWR, { useSWRConfig } from 'swr';
|
||||
|
||||
import Back from '@/assets/images/back.svg';
|
||||
import Delete from '@/assets/images/delete.svg';
|
||||
import More from '@/assets/images/more.svg';
|
||||
import ActionMenu, { ActionMenuItem } from '@/components/ActionMenu';
|
||||
import Card from '@/components/Card';
|
||||
import ConfirmModal from '@/components/ConfirmModal';
|
||||
import CopyToClipboard from '@/components/CopyToClipboard';
|
||||
import DetailsSkeleton from '@/components/DetailsSkeleton';
|
||||
import RequestDataError from '@/components/RequestDataError';
|
||||
import DetailsPage from '@/components/DetailsPage';
|
||||
import TabNav, { TabNavItem } from '@/components/TabNav';
|
||||
import TextLink from '@/components/TextLink';
|
||||
import { RoleDetailsTabs } from '@/consts/page-tabs';
|
||||
import type { RequestError } from '@/hooks/use-api';
|
||||
import useApi from '@/hooks/use-api';
|
||||
import * as detailsStyles from '@/scss/details.module.scss';
|
||||
import { withAppInsights } from '@/utils/app-insights';
|
||||
|
||||
import * as styles from './index.module.scss';
|
||||
|
@ -67,25 +63,14 @@ function RoleDetails() {
|
|||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
detailsStyles.container,
|
||||
styles.container,
|
||||
isPageHasTable && styles.withTable
|
||||
)}
|
||||
<DetailsPage
|
||||
backLink="/roles"
|
||||
backLinkTitle="role_details.back_to_roles"
|
||||
isLoading={isLoading}
|
||||
error={error}
|
||||
className={classNames(isPageHasTable && styles.withTable)}
|
||||
onRetry={mutate}
|
||||
>
|
||||
<TextLink to="/roles" icon={<Back />} className={styles.backLink}>
|
||||
{t('role_details.back_to_roles')}
|
||||
</TextLink>
|
||||
{isLoading && <DetailsSkeleton />}
|
||||
{error && (
|
||||
<RequestDataError
|
||||
error={error}
|
||||
onRetry={() => {
|
||||
void mutate();
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{data && (
|
||||
<>
|
||||
<Card className={styles.header}>
|
||||
|
@ -146,7 +131,7 @@ function RoleDetails() {
|
|||
/>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</DetailsPage>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,5 @@
|
|||
@use '@/scss/underscore' as _;
|
||||
|
||||
.backLink {
|
||||
margin: _.unit(1) 0 0 _.unit(1);
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.resourceLayout {
|
||||
height: 100%;
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@ import ReactModal from 'react-modal';
|
|||
import { Outlet, useLocation, useNavigate, useParams } from 'react-router-dom';
|
||||
import useSWR from 'swr';
|
||||
|
||||
import Back from '@/assets/images/back.svg';
|
||||
import Delete from '@/assets/images/delete.svg';
|
||||
import More from '@/assets/images/more.svg';
|
||||
import Reset from '@/assets/images/reset.svg';
|
||||
|
@ -15,15 +14,12 @@ import ActionMenu, { ActionMenuItem } from '@/components/ActionMenu';
|
|||
import Card from '@/components/Card';
|
||||
import CopyToClipboard from '@/components/CopyToClipboard';
|
||||
import DeleteConfirmModal from '@/components/DeleteConfirmModal';
|
||||
import DetailsSkeleton from '@/components/DetailsSkeleton';
|
||||
import RequestDataError from '@/components/RequestDataError';
|
||||
import DetailsPage from '@/components/DetailsPage';
|
||||
import TabNav, { TabNavItem } from '@/components/TabNav';
|
||||
import TextLink from '@/components/TextLink';
|
||||
import UserAvatar from '@/components/UserAvatar';
|
||||
import { UserDetailsTabs } from '@/consts/page-tabs';
|
||||
import type { RequestError } from '@/hooks/use-api';
|
||||
import useApi from '@/hooks/use-api';
|
||||
import * as detailsStyles from '@/scss/details.module.scss';
|
||||
import * as modalStyles from '@/scss/modal.module.scss';
|
||||
import { withAppInsights } from '@/utils/app-insights';
|
||||
|
||||
|
@ -70,19 +66,14 @@ function UserDetails() {
|
|||
};
|
||||
|
||||
return (
|
||||
<div className={classNames(detailsStyles.container, isPageHasTable && styles.resourceLayout)}>
|
||||
<TextLink to="/users" icon={<Back />} className={styles.backLink}>
|
||||
{t('user_details.back_to_users')}
|
||||
</TextLink>
|
||||
{isLoading && <DetailsSkeleton />}
|
||||
{error && (
|
||||
<RequestDataError
|
||||
error={error}
|
||||
onRetry={() => {
|
||||
void mutate();
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<DetailsPage
|
||||
backLink="/users"
|
||||
backLinkTitle="user_details.back_to_users"
|
||||
isLoading={isLoading}
|
||||
error={error}
|
||||
className={classNames(isPageHasTable && styles.resourceLayout)}
|
||||
onRetry={mutate}
|
||||
>
|
||||
{data && (
|
||||
<>
|
||||
<Card className={styles.header}>
|
||||
|
@ -194,7 +185,7 @@ function UserDetails() {
|
|||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</DetailsPage>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,47 +0,0 @@
|
|||
@use '@/scss/underscore' as _;
|
||||
|
||||
.container {
|
||||
min-height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-width: min-content;
|
||||
|
||||
> *:not(:first-child) {
|
||||
margin-top: _.unit(4);
|
||||
}
|
||||
|
||||
.body {
|
||||
position: relative;
|
||||
padding-bottom: 0;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.footer {
|
||||
position: sticky;
|
||||
bottom: 0;
|
||||
margin: 0 _.unit(-6);
|
||||
// Use the same color with app's background to cover card body
|
||||
// simulate the always-on border-radius
|
||||
background: var(--color-base);
|
||||
|
||||
.footerMain {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
border-bottom-left-radius: 16px;
|
||||
border-bottom-right-radius: 16px;
|
||||
background: var(--color-layer-1);
|
||||
padding: _.unit(6);
|
||||
}
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
background: var(--color-border);
|
||||
opacity: 50%;
|
||||
height: 1px;
|
||||
display: block;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue