mirror of
https://github.com/logto-io/logto.git
synced 2025-01-20 21:32:31 -05:00
feat(console): app details header (#327)
* feat(console): init app details * feat(console): loading app details from api * fix(console): back to app i18n * refactor(console): add img placeholder alt
This commit is contained in:
parent
00541dda78
commit
71e5da6216
11 changed files with 199 additions and 25 deletions
|
@ -10,6 +10,7 @@ import Sidebar, { getPath, sections } from './components/Sidebar';
|
|||
import Topbar from './components/Topbar';
|
||||
import initI18n from './i18n/init';
|
||||
import ApiResources from './pages/ApiResources';
|
||||
import ApplicationDetails from './pages/ApplicationDetails';
|
||||
import Applications from './pages/Applications';
|
||||
import Connectors from './pages/Connectors';
|
||||
import Connector from './pages/Connectors/Connector';
|
||||
|
@ -38,9 +39,12 @@ const Main = () => {
|
|||
<Content>
|
||||
<Routes>
|
||||
<Route path="api-resources" element={<ApiResources />} />
|
||||
<Route path="applications" element={<Applications />} />
|
||||
<Route path="connectors" element={<Connectors />} />
|
||||
<Route path="connectors/:connectorId" element={<Connector />} />
|
||||
<Route path="applications">
|
||||
<Route index element={<Applications />} />
|
||||
<Route path=":id" element={<ApplicationDetails />} />
|
||||
</Route>
|
||||
</Routes>
|
||||
</Content>
|
||||
</div>
|
||||
|
|
|
@ -1,18 +1,7 @@
|
|||
<svg width="50" height="50" viewBox="0 0 50 50" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect opacity="0.2" width="50" height="50" rx="8" fill="#77B255"/>
|
||||
<g clip-path="url(#clip0_1517_47630)">
|
||||
<path d="M40.1154 29.3335C40.1154 35.5468 33.3996 39.3335 25.1154 39.3335C16.8312 39.3335 10.1154 35.5468 10.1154 29.3335C10.1154 23.1202 16.8312 16.8335 25.1154 16.8335C33.3996 16.8335 40.1154 23.1202 40.1154 29.3335Z" fill="#C6E5B3"/>
|
||||
<path d="M36.5779 21.5632C37.7179 20.6465 38.4487 19.2423 38.4487 17.6665C38.4487 14.9057 36.2096 12.6665 33.4487 12.6665C31.0646 12.6665 29.0737 14.3365 28.5729 16.5707C27.4996 16.1682 26.3371 15.939 25.1154 15.939C23.8946 15.939 22.7312 16.1682 21.6579 16.5707C21.1571 14.3365 19.1662 12.6665 16.7821 12.6665C14.0212 12.6665 11.7821 14.9057 11.7821 17.6665C11.7821 19.2423 12.5129 20.6465 13.6529 21.5632C11.4487 23.7965 10.1154 26.5707 10.1154 29.3332C10.1154 35.5465 16.8312 30.1665 25.1154 30.1665C33.3996 30.1665 40.1154 35.5465 40.1154 29.3332C40.1154 26.5707 38.7821 23.7965 36.5779 21.5632Z" fill="#77B255"/>
|
||||
<path d="M16.3654 20.1668C17.9762 20.1668 19.2821 18.861 19.2821 17.2502C19.2821 15.6393 17.9762 14.3335 16.3654 14.3335C14.7546 14.3335 13.4487 15.6393 13.4487 17.2502C13.4487 18.861 14.7546 20.1668 16.3654 20.1668Z" fill="white"/>
|
||||
<path d="M16.3654 18.5C17.0557 18.5 17.6154 17.9404 17.6154 17.25C17.6154 16.5596 17.0557 16 16.3654 16C15.675 16 15.1154 16.5596 15.1154 17.25C15.1154 17.9404 15.675 18.5 16.3654 18.5Z" fill="#292F33"/>
|
||||
<path d="M33.8654 20.1668C35.4762 20.1668 36.7821 18.861 36.7821 17.2502C36.7821 15.6393 35.4762 14.3335 33.8654 14.3335C32.2546 14.3335 30.9487 15.6393 30.9487 17.2502C30.9487 18.861 32.2546 20.1668 33.8654 20.1668Z" fill="white"/>
|
||||
<path d="M33.8654 18.5C34.5557 18.5 35.1154 17.9404 35.1154 17.25C35.1154 16.5596 34.5557 16 33.8654 16C33.175 16 32.6154 16.5596 32.6154 17.25C32.6154 17.9404 33.175 18.5 33.8654 18.5Z" fill="#292F33"/>
|
||||
<path d="M21.7821 28.5002C22.2423 28.5002 22.6154 28.1271 22.6154 27.6668C22.6154 27.2066 22.2423 26.8335 21.7821 26.8335C21.3218 26.8335 20.9487 27.2066 20.9487 27.6668C20.9487 28.1271 21.3218 28.5002 21.7821 28.5002Z" fill="#5C913B"/>
|
||||
<path d="M28.4487 28.5002C28.909 28.5002 29.2821 28.1271 29.2821 27.6668C29.2821 27.2066 28.909 26.8335 28.4487 26.8335C27.9885 26.8335 27.6154 27.2066 27.6154 27.6668C27.6154 28.1271 27.9885 28.5002 28.4487 28.5002Z" fill="#5C913B"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_1517_47630">
|
||||
<rect width="30" height="30" fill="white" transform="translate(10.1154 11)"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
<svg width="36" height="36" viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M18 36C27.9411 36 36 27.9411 36 18C36 8.05887 27.9411 0 18 0C8.05887 0 0 8.05887 0 18C0 27.9411 8.05887 36 18 36Z" fill="#FFCC4D"/>
|
||||
<path d="M18 21C14.377 21 11.973 20.578 9 20C8.321 19.869 7 20 7 22C7 26 11.595 31 18 31C24.404 31 29 26 29 22C29 20 27.679 19.868 27 20C24.027 20.578 21.623 21 18 21Z" fill="#664500"/>
|
||||
<path d="M9 22C9 22 12 23 18 23C24 23 27 22 27 22C27 22 25 26 18 26C11 26 9 22 9 22Z" fill="white"/>
|
||||
<path d="M12 17C13.3807 17 14.5 15.433 14.5 13.5C14.5 11.567 13.3807 10 12 10C10.6193 10 9.5 11.567 9.5 13.5C9.5 15.433 10.6193 17 12 17Z" fill="#664500"/>
|
||||
<path d="M24 17C25.3807 17 26.5 15.433 26.5 13.5C26.5 11.567 25.3807 10 24 10C22.6193 10 21.5 11.567 21.5 13.5C21.5 15.433 22.6193 17 24 17Z" fill="#664500"/>
|
||||
</svg>
|
||||
|
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 846 B |
|
@ -17,6 +17,7 @@
|
|||
--color-primary-70: #a8a0ff;
|
||||
--color-primary-80: #c6c0ff;
|
||||
--color-primary-90: #e5dfff;
|
||||
--color-primary-95: #f3eeff;
|
||||
--color-on-primary: #fff;
|
||||
--color-error: #ba1b1b;
|
||||
--color-error-30: #930006;
|
||||
|
@ -41,6 +42,7 @@
|
|||
$font-family: 'SF UI Text', 'SF Pro Display', sans-serif;
|
||||
|
||||
.web {
|
||||
--font-title-large: 600 20px/28px #{$font-family};
|
||||
--font-title-medium: 500 16px/24px #{$font-family};
|
||||
--font-heading-small: 600 24px/32px #{$font-family};
|
||||
--font-heading: 600 16px/24px #{$font-family};
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
.placeholder {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
.container {
|
||||
background: rgba(#ffcc4d, 0.3);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
|
|
@ -1,11 +1,23 @@
|
|||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import defaultPlaceholder from '@/assets/images/default-placeholder.svg';
|
||||
|
||||
import * as styles from './index.module.scss';
|
||||
|
||||
const ImagePlaceholder = () => {
|
||||
return <img src={defaultPlaceholder} className={styles.placeholder} />;
|
||||
type Props = {
|
||||
size?: number;
|
||||
borderRadius?: number;
|
||||
};
|
||||
|
||||
const ImagePlaceholder = ({ size = 50, borderRadius = 8 }: Props) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div className={styles.container} style={{ width: size, height: size, borderRadius }}>
|
||||
<img alt={t('general.placeholder')} src={defaultPlaceholder} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ImagePlaceholder;
|
||||
|
|
30
packages/console/src/components/TextButton/index.module.scss
Normal file
30
packages/console/src/components/TextButton/index.module.scss
Normal file
|
@ -0,0 +1,30 @@
|
|||
@use '@/scss/underscore' as _;
|
||||
|
||||
.button {
|
||||
display: inline-block;
|
||||
font: var(--font-caption);
|
||||
color: var(--color-primary);
|
||||
padding: _.unit(0.5) _.unit(1);
|
||||
border-radius: _.unit(1);
|
||||
text-decoration: none;
|
||||
|
||||
svg {
|
||||
fill: var(--color-primary);
|
||||
}
|
||||
|
||||
&:active {
|
||||
background: var(--color-primary-95);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: var(--color-primary-30);
|
||||
|
||||
svg {
|
||||
fill: var(--color-primary-30);
|
||||
}
|
||||
}
|
||||
|
||||
&:focus {
|
||||
outline: 2px solid var(--color-primary-80);
|
||||
}
|
||||
}
|
11
packages/console/src/pages/ApplicationDetails/icons/Back.tsx
Normal file
11
packages/console/src/pages/ApplicationDetails/icons/Back.tsx
Normal file
|
@ -0,0 +1,11 @@
|
|||
import React from 'react';
|
||||
|
||||
const Back = () => {
|
||||
return (
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10.3566 13.6562L4.69995 7.99958L10.3566 2.34292L11.3 3.28558L6.58528 7.99958L11.3 12.7136L10.3566 13.6562Z" />
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export default Back;
|
|
@ -0,0 +1,58 @@
|
|||
@use '@/scss/underscore' as _;
|
||||
|
||||
.container {
|
||||
> *:not(:first-child) {
|
||||
margin-top: _.unit(4);
|
||||
}
|
||||
}
|
||||
|
||||
.button {
|
||||
padding-left: 0;
|
||||
|
||||
.body {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
> *:not(:first-child) {
|
||||
margin-left: _.unit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.container .header {
|
||||
padding: _.unit(8);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
> *:not(:first-child) {
|
||||
margin-left: _.unit(6);
|
||||
}
|
||||
|
||||
.metadata {
|
||||
flex: 1;
|
||||
|
||||
> div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
&:not(:first-child) {
|
||||
margin-top: _.unit(2);
|
||||
}
|
||||
|
||||
> *:not(:first-child) {
|
||||
margin-left: _.unit(2);
|
||||
}
|
||||
}
|
||||
|
||||
.name {
|
||||
font: var(--font-title-large);
|
||||
color: var(--color-component-text);
|
||||
}
|
||||
|
||||
.type {
|
||||
font: var(--font-subhead-2);
|
||||
color: var(--color-component-caption);
|
||||
}
|
||||
}
|
||||
}
|
54
packages/console/src/pages/ApplicationDetails/index.tsx
Normal file
54
packages/console/src/pages/ApplicationDetails/index.tsx
Normal file
|
@ -0,0 +1,54 @@
|
|||
import { Application } from '@logto/schemas';
|
||||
import classNames from 'classnames';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Link, useParams } from 'react-router-dom';
|
||||
import useSWR from 'swr';
|
||||
|
||||
import Card from '@/components/Card';
|
||||
import CopyToClipboard from '@/components/CopyToClipboard';
|
||||
import ImagePlaceholder from '@/components/ImagePlaceholder';
|
||||
import * as buttonStyles from '@/components/TextButton/index.module.scss';
|
||||
import { RequestError } from '@/swr';
|
||||
import { applicationTypeI18nKey } from '@/types/applications';
|
||||
|
||||
import Back from './icons/Back';
|
||||
import * as styles from './index.module.scss';
|
||||
|
||||
const ApplicationDetails = () => {
|
||||
const { id } = useParams();
|
||||
const { data, error } = useSWR<Application, RequestError>(id && `/api/applications/${id}`);
|
||||
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
||||
const isLoading = !data && !error;
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<Link to="/applications" className={classNames(buttonStyles.button, styles.button)}>
|
||||
<div className={styles.body}>
|
||||
<Back />
|
||||
<div>{t('application_details.back_to_applications')}</div>
|
||||
</div>
|
||||
</Link>
|
||||
{isLoading && <div>loading</div>}
|
||||
{error && <div>{`error occurred: ${error.metadata.code}`}</div>}
|
||||
{data && (
|
||||
<Card className={styles.header}>
|
||||
<ImagePlaceholder size={76} borderRadius={16} />
|
||||
<div className={styles.metadata}>
|
||||
<div>
|
||||
<div className={styles.name}>{data.name}</div>
|
||||
<div className={styles.type}>{t(`${applicationTypeI18nKey[data.type]}.title`)}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className={styles.type}>ID</div>
|
||||
<CopyToClipboard value={data.id} />
|
||||
</div>
|
||||
</div>
|
||||
<div>action</div>
|
||||
</Card>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ApplicationDetails;
|
|
@ -1,4 +1,7 @@
|
|||
const translation = {
|
||||
general: {
|
||||
placeholder: 'Placeholder',
|
||||
},
|
||||
sign_in: {
|
||||
action: 'Sign In',
|
||||
loading: 'Signing in...',
|
||||
|
@ -88,6 +91,9 @@ const translation = {
|
|||
social: 'Social',
|
||||
},
|
||||
},
|
||||
application_details: {
|
||||
back_to_applications: 'Back to Applications',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
import en from './en';
|
||||
|
||||
const translation = {
|
||||
general: {
|
||||
placeholder: '占位符',
|
||||
},
|
||||
sign_in: {
|
||||
action: '登录',
|
||||
loading: '登录中...',
|
||||
|
@ -33,7 +36,7 @@ const translation = {
|
|||
tabs: {
|
||||
get_started: '开始使用',
|
||||
dashboard: '仪表盘',
|
||||
applications: '应用',
|
||||
applications: '应用集',
|
||||
api_resources: 'API 资源',
|
||||
sign_in_experience: '登录体验',
|
||||
connectors: '连接器',
|
||||
|
@ -44,7 +47,7 @@ const translation = {
|
|||
settings: '设置',
|
||||
},
|
||||
applications: {
|
||||
title: 'Applications',
|
||||
title: '应用集',
|
||||
subtitle:
|
||||
'Setup a mobile, single page or traditional application to use Logto for authentication.',
|
||||
create: 'Create Application',
|
||||
|
@ -90,6 +93,9 @@ const translation = {
|
|||
social: '社会化登录',
|
||||
},
|
||||
},
|
||||
application_details: {
|
||||
back_to_applications: '返回应用集',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue