mirror of
https://github.com/logto-io/logto.git
synced 2025-03-24 22:41:28 -05:00
feat(console): app type description
This commit is contained in:
parent
c4dace9a51
commit
3bbfe905bb
12 changed files with 113 additions and 50 deletions
|
@ -34,6 +34,8 @@
|
|||
--color-component-text: #191c1d;
|
||||
--color-outline: #78767f;
|
||||
--color-table-row-selected: rgba(0, 0, 0, 2%);
|
||||
--color-text-default: #202223;
|
||||
--color-foggy: #888;
|
||||
}
|
||||
|
||||
$font-family: 'SF UI Text', 'SF Pro Display', sans-serif;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import classNames from 'classnames';
|
||||
import React, { forwardRef, ReactNode } from 'react';
|
||||
import React, { ReactNode } from 'react';
|
||||
|
||||
import * as styles from './index.module.scss';
|
||||
|
||||
|
@ -15,8 +15,6 @@ const Check = () => (
|
|||
</svg>
|
||||
);
|
||||
|
||||
// https://github.com/yannickcr/eslint-plugin-react/issues/2856
|
||||
/* eslint-disable react/require-default-props */
|
||||
export type Props = {
|
||||
value: string;
|
||||
title: string;
|
||||
|
@ -25,28 +23,18 @@ export type Props = {
|
|||
isChecked?: boolean;
|
||||
onClick?: () => void;
|
||||
};
|
||||
/* eslint-enable react/require-default-props */
|
||||
|
||||
const Radio = forwardRef<HTMLInputElement, Props>(
|
||||
({ value, title, name, children, isChecked, onClick }, reference) => {
|
||||
return (
|
||||
<div className={classNames(styles.radio, isChecked && styles.checked)} onClick={onClick}>
|
||||
<input
|
||||
ref={reference}
|
||||
readOnly
|
||||
type="radio"
|
||||
name={name}
|
||||
value={value}
|
||||
checked={isChecked}
|
||||
/>
|
||||
<div className={classNames(styles.headline, !children && styles.center)}>
|
||||
<div className={styles.title}>{title}</div>
|
||||
<Check />
|
||||
</div>
|
||||
{children}
|
||||
const Radio = ({ value, title, name, children, isChecked, onClick }: Props) => {
|
||||
return (
|
||||
<div className={classNames(styles.radio, isChecked && styles.checked)} onClick={onClick}>
|
||||
<input readOnly type="radio" name={name} value={value} checked={isChecked} />
|
||||
<div className={classNames(styles.headline, !children && styles.center)}>
|
||||
<div className={styles.title}>{title}</div>
|
||||
<Check />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Radio;
|
||||
|
|
|
@ -15,13 +15,14 @@
|
|||
> .radio {
|
||||
position: relative;
|
||||
flex: 1;
|
||||
min-width: 220px;
|
||||
max-width: 220px;
|
||||
padding: _.unit(5);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-radius: _.unit(4);
|
||||
border: 1px solid var(--color-neutral-90);
|
||||
outline: none;
|
||||
user-select: none;
|
||||
cursor: pointer;
|
||||
|
||||
&:not(:first-child) {
|
||||
|
|
12
packages/console/src/icons/Close.tsx
Normal file
12
packages/console/src/icons/Close.tsx
Normal file
|
@ -0,0 +1,12 @@
|
|||
import React, { SVGProps } from 'react';
|
||||
|
||||
const Close = (props: SVGProps<SVGSVGElement>) => (
|
||||
<svg width="32" height="32" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg" {...props}>
|
||||
<path
|
||||
d="M7.05727 7.05703C7.57797 6.53633 8.42219 6.53633 8.94289 7.05703L16.0001 14.1142L23.0573 7.05703C23.578 6.53633 24.4222 6.53633 24.9429 7.05703C25.4636 7.57773 25.4636 8.42195 24.9429 8.94265L17.8857 15.9998L24.9429 23.057C25.4636 23.5777 25.4636 24.4219 24.9429 24.9426C24.4222 25.4633 23.578 25.4633 23.0573 24.9426L16.0001 17.8855L8.94289 24.9426C8.42219 25.4633 7.57797 25.4633 7.05727 24.9426C6.53657 24.4219 6.53657 23.5777 7.05727 23.057L14.1145 15.9998L7.05727 8.94265C6.53657 8.42195 6.53657 7.57773 7.05727 7.05703Z"
|
||||
fill="#333333"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default Close;
|
|
@ -1,23 +1,18 @@
|
|||
import { ApplicationType } from '@logto/schemas';
|
||||
import React, { SVGProps } from 'react';
|
||||
import React from 'react';
|
||||
import { useController, useForm } from 'react-hook-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import Card from '@/components/Card';
|
||||
import CardTitle from '@/components/CardTitle';
|
||||
import FormField from '@/components/FormField';
|
||||
import RadioGroup, { Radio } from '@/components/RadioGroup';
|
||||
import Close from '@/icons/Close';
|
||||
import { applicationTypeI18nKey } from '@/types/applications';
|
||||
|
||||
import TypeDescription from '../TypeDescription';
|
||||
import * as styles from './index.module.scss';
|
||||
|
||||
const Close = (props: SVGProps<SVGSVGElement>) => (
|
||||
<svg width="32" height="32" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg" {...props}>
|
||||
<path
|
||||
d="M7.05727 7.05703C7.57797 6.53633 8.42219 6.53633 8.94289 7.05703L16.0001 14.1142L23.0573 7.05703C23.578 6.53633 24.4222 6.53633 24.9429 7.05703C25.4636 7.57773 25.4636 8.42195 24.9429 8.94265L17.8857 15.9998L24.9429 23.057C25.4636 23.5777 25.4636 24.4219 24.9429 24.9426C24.4222 25.4633 23.578 25.4633 23.0573 24.9426L16.0001 17.8855L8.94289 24.9426C8.42219 25.4633 7.57797 25.4633 7.05727 24.9426C6.53657 24.4219 6.53657 23.5777 7.05727 23.057L14.1145 15.9998L7.05727 8.94265C6.53657 8.42195 6.53657 7.57773 7.05727 7.05703Z"
|
||||
fill="#333333"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
type FormData = {
|
||||
type: ApplicationType;
|
||||
name: string;
|
||||
|
@ -33,6 +28,7 @@ const CreateForm = ({ onClose }: Props) => {
|
|||
const {
|
||||
field: { onChange, value },
|
||||
} = useController({ name: 'type', control });
|
||||
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
||||
|
||||
const onSubmit = handleSubmit((data) => {
|
||||
console.log(data);
|
||||
|
@ -47,9 +43,14 @@ const CreateForm = ({ onClose }: Props) => {
|
|||
<form className={styles.form} onSubmit={onSubmit}>
|
||||
<FormField title="admin_console.applications.select_application_type">
|
||||
<RadioGroup name="application_type" value={value} onChange={onChange}>
|
||||
<Radio title="Native" value={ApplicationType.Native} />
|
||||
<Radio title="Single Page Application" value={ApplicationType.SPA} />
|
||||
<Radio title="Tranditional Web" value={ApplicationType.Traditional} />
|
||||
{Object.values(ApplicationType).map((value) => (
|
||||
<Radio key={value} title={t(`${applicationTypeI18nKey[value]}.title`)} value={value}>
|
||||
<TypeDescription
|
||||
subtitle={t(`${applicationTypeI18nKey[value]}.subtitle`)}
|
||||
description={t(`${applicationTypeI18nKey[value]}.description`)}
|
||||
/>
|
||||
</Radio>
|
||||
))}
|
||||
</RadioGroup>
|
||||
</FormField>
|
||||
<button type="submit">Submit</button>
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
@use '@/scss/underscore' as _;
|
||||
|
||||
.subtitle,
|
||||
.description {
|
||||
margin-top: _.unit(3);
|
||||
flex: 2;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font: var(--font-body);
|
||||
color: var(--color-text-default);
|
||||
}
|
||||
|
||||
.description {
|
||||
font: var(--font-caption);
|
||||
color: var(--color-foggy);
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
import React from 'react';
|
||||
|
||||
import * as styles from './index.module.scss';
|
||||
|
||||
type Props = {
|
||||
subtitle: string;
|
||||
description: string;
|
||||
};
|
||||
|
||||
const TypeDescription = ({ subtitle, description }: Props) => {
|
||||
return (
|
||||
<>
|
||||
<div className={styles.subtitle}>{subtitle}</div>
|
||||
<div className={styles.description}>{description}</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default TypeDescription;
|
|
@ -68,7 +68,7 @@ const Applications = () => {
|
|||
<td>
|
||||
<ItemPreview
|
||||
title={name}
|
||||
subtitle={String(t(applicationTypeI18nKey[type]))}
|
||||
subtitle={t(`${applicationTypeI18nKey[type]}.title`)}
|
||||
icon={<ImagePlaceholder />}
|
||||
/>
|
||||
</td>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translateX(-50%) translateY(-50%);
|
||||
transform: translateX(-50%, -50%);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import { AdminConsoleKey } from '@logto/phrases';
|
||||
import { ApplicationType } from '@logto/schemas';
|
||||
|
||||
export const applicationTypeI18nKey: Record<ApplicationType, AdminConsoleKey> = {
|
||||
export const applicationTypeI18nKey = Object.freeze({
|
||||
[ApplicationType.Native]: 'applications.type.native',
|
||||
[ApplicationType.SPA]: 'applications.type.spa',
|
||||
[ApplicationType.Traditional]: 'applications.type.tranditional',
|
||||
};
|
||||
[ApplicationType.Traditional]: 'applications.type.traditional',
|
||||
} as const);
|
||||
|
|
|
@ -50,9 +50,21 @@ const translation = {
|
|||
select_application_type: 'Select an application type',
|
||||
client_id: 'Client ID',
|
||||
type: {
|
||||
native: 'Native App',
|
||||
spa: 'Single Page App',
|
||||
tranditional: 'Tranditional Web App',
|
||||
native: {
|
||||
title: 'Native',
|
||||
subtitle: 'Mobile, desktop, CLI and smart device apps running natively.',
|
||||
description: 'E.g.: iOS, Electron, Apple TV apps',
|
||||
},
|
||||
spa: {
|
||||
title: 'Single Page App',
|
||||
subtitle: 'A JavaScript front-end app that uses an API.',
|
||||
description: 'E.g.: Angular, React, Vue',
|
||||
},
|
||||
traditional: {
|
||||
title: 'Tranditional Web',
|
||||
subtitle: 'Traditional web app using redirects.',
|
||||
description: 'E.g.: Node.js, Express, ASP.NET, Java, PHP',
|
||||
},
|
||||
},
|
||||
},
|
||||
api_resources: {
|
||||
|
|
|
@ -52,9 +52,21 @@ const translation = {
|
|||
select_application_type: 'Select an application type',
|
||||
client_id: 'Client ID',
|
||||
type: {
|
||||
native: 'Native App',
|
||||
spa: 'Single Page App',
|
||||
tranditional: 'Tranditional Web App',
|
||||
native: {
|
||||
title: 'Native',
|
||||
subtitle: 'Mobile, desktop, CLI and smart device apps running natively.',
|
||||
description: 'E.g.: iOS, Electron, Apple TV apps',
|
||||
},
|
||||
spa: {
|
||||
title: 'Single Page App',
|
||||
subtitle: 'A JavaScript front-end app that uses an API.',
|
||||
description: 'E.g.: Angular, React, Vue',
|
||||
},
|
||||
traditional: {
|
||||
title: 'Tranditional Web',
|
||||
subtitle: 'Traditional web app using redirects.',
|
||||
description: 'E.g.: Node.js, Express, ASP.NET, Java, PHP',
|
||||
},
|
||||
},
|
||||
},
|
||||
api_resources: {
|
||||
|
|
Loading…
Add table
Reference in a new issue