0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2025-03-17 22:31:28 -05:00

fix(console): new ui in save changes footer (#661)

This commit is contained in:
Wang Sijie 2022-04-27 14:11:17 +08:00 committed by GitHub
parent 5e251bdc08
commit 19b9db809a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 154 additions and 103 deletions

View file

@ -44,8 +44,6 @@
}
.body {
padding-bottom: 0;
> :first-child {
margin-top: 0;
}

View file

@ -1,4 +1,5 @@
import { Resource } from '@logto/schemas';
import classNames from 'classnames';
import React, { useEffect, useState } from 'react';
import { useForm } from 'react-hook-form';
import { toast } from 'react-hot-toast';
@ -141,11 +142,11 @@ const ApiResourceDetails = () => {
</Modal>
</div>
</Card>
<Card className={styles.body}>
<Card className={classNames(styles.body, detailsStyles.body)}>
<TabNav>
<TabNavLink href={location.pathname}>{t('api_resource_details.settings')}</TabNavLink>
</TabNav>
<form className={styles.form} onSubmit={onSubmit}>
<form className={classNames(styles.form, detailsStyles.body)} onSubmit={onSubmit}>
<div className={styles.fields}>
<FormField
isRequired
@ -164,7 +165,9 @@ const ApiResourceDetails = () => {
/>
</FormField>
</div>
<div className={detailsStyles.footer}>
</form>
<div className={detailsStyles.footer}>
<div className={detailsStyles.footerMain}>
<Button
isLoading={isSubmitting}
htmlType="submit"
@ -173,7 +176,7 @@ const ApiResourceDetails = () => {
size="large"
/>
</div>
</form>
</div>
</Card>
</>
)}

View file

@ -5,8 +5,6 @@
}
.body {
padding-bottom: 0;
> :first-child {
margin-top: 0;
}

View file

@ -1,4 +1,5 @@
import { Application, SnakeCaseOidcConfig } from '@logto/schemas';
import classNames from 'classnames';
import React, { useEffect, useState } from 'react';
import { Controller, useForm } from 'react-hook-form';
import { toast } from 'react-hot-toast';
@ -246,7 +247,7 @@ const ApplicationDetails = () => {
</Modal>
</div>
</Card>
<Card className={styles.body}>
<Card className={classNames(styles.body, detailsStyles.body)}>
<TabNav>
<TabNavLink href={`/applications/${data.id}/settings`}>
{t('application_details.settings')}
@ -260,13 +261,15 @@ const ApplicationDetails = () => {
{isAdvancedSettings ? AdvancedSettingsPage : SettingsPage}
</div>
<div className={detailsStyles.footer}>
<Button
isLoading={isSubmitting}
htmlType="submit"
type="primary"
size="large"
title="admin_console.application_details.save_changes"
/>
<div className={detailsStyles.footerMain}>
<Button
isLoading={isSubmitting}
htmlType="submit"
type="primary"
size="large"
title="admin_console.application_details.save_changes"
/>
</div>
</div>
</form>
</Card>

View file

@ -1,4 +1,5 @@
import { ConnectorDTO, ConnectorType } from '@logto/schemas';
import classNames from 'classnames';
import React, { useEffect, useState } from 'react';
import { toast } from 'react-hot-toast';
import { useTranslation } from 'react-i18next';
@ -179,7 +180,7 @@ const ConnectorDetails = () => {
</Card>
)}
{data && (
<Card className={styles.body}>
<Card className={classNames(styles.body, detailsStyles.body)}>
<TabNav>
<TabNavLink href={`/connectors/${connectorId ?? ''}`}>
{t('connector_details.tab_settings')}
@ -197,12 +198,14 @@ const ConnectorDetails = () => {
)}
{saveError && <div>{saveError}</div>}
<div className={detailsStyles.footer}>
<Button
type="primary"
title="admin_console.connector_details.save_changes"
isLoading={isSubmitting}
onClick={handleSave}
/>
<div className={detailsStyles.footerMain}>
<Button
type="primary"
title="admin_console.connector_details.save_changes"
isLoading={isSubmitting}
onClick={handleSave}
/>
</div>
</div>
</Card>
)}

View file

@ -78,7 +78,12 @@ const CreateForm = ({ onClose, isOpen: isFormOpen, type }: Props) => {
{isLoading && 'Loading...'}
{error && error}
{connectors && (
<RadioGroup name="connector" value={activeConnectorId} onChange={setActiveConnectorId}>
<RadioGroup
name="connector"
value={activeConnectorId}
type="card"
onChange={setActiveConnectorId}
>
{connectors.map(({ id, metadata: { name, logo, description } }) => (
<Radio key={id} value={id} className={styles.connector}>
<div className={styles.logo}>

View file

@ -0,0 +1,3 @@
.container {
padding-bottom: 0;
}

View file

@ -1,5 +1,6 @@
import { Language } from '@logto/phrases';
import { AppearanceMode, Setting } from '@logto/schemas';
import classNames from 'classnames';
import React, { useEffect } from 'react';
import { Controller, useForm } from 'react-hook-form';
import { toast } from 'react-hot-toast';
@ -16,6 +17,8 @@ import TextInput from '@/components/TextInput';
import useApi, { RequestError } from '@/hooks/use-api';
import * as detailsStyles from '@/scss/details.module.scss';
import * as styles from './index.module.scss';
const Settings = () => {
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
const { data, error, mutate } = useSWR<Setting, RequestError>('/api/settings');
@ -51,7 +54,7 @@ const Settings = () => {
});
return (
<Card className={detailsStyles.container}>
<Card className={classNames(detailsStyles.container, styles.container)}>
<CardTitle title="settings.title" subtitle="settings.description" />
<TabNav>
<TabNavLink href="/settings">{t('settings.tabs.general')}</TabNavLink>
@ -59,67 +62,71 @@ const Settings = () => {
{!data && !error && <div>loading</div>}
{error && <div>{`error occurred: ${error.body.message}`}</div>}
{data && (
<form onSubmit={onSubmit}>
<FormField title="admin_console.settings.custom_domain">
<TextInput {...register('customDomain')} />
</FormField>
<FormField isRequired title="admin_console.settings.language">
<Controller
name="adminConsole.language"
control={control}
render={({ field: { value, onChange } }) => (
<Select
value={value}
options={[
{
value: Language.English,
title: t('settings.language_english'),
},
{
value: Language.Chinese,
title: t('settings.language_chinese'),
},
]}
onChange={onChange}
/>
)}
/>
</FormField>
<FormField isRequired title="admin_console.settings.appearance">
<Controller
name="adminConsole.appearanceMode"
control={control}
render={({ field: { value, onChange } }) => (
<Select
value={value}
options={[
{
value: AppearanceMode.SyncWithSystem,
title: t('settings.appearance_system'),
},
{
value: AppearanceMode.LightMode,
title: t('settings.appearance_light'),
},
{
value: AppearanceMode.DarkMode,
title: t('settings.appearance_dark'),
},
]}
onChange={onChange}
/>
)}
/>
</FormField>
<>
<form className={detailsStyles.body} onSubmit={onSubmit}>
<FormField title="admin_console.settings.custom_domain">
<TextInput {...register('customDomain')} />
</FormField>
<FormField isRequired title="admin_console.settings.language">
<Controller
name="adminConsole.language"
control={control}
render={({ field: { value, onChange } }) => (
<Select
value={value}
options={[
{
value: Language.English,
title: t('settings.language_english'),
},
{
value: Language.Chinese,
title: t('settings.language_chinese'),
},
]}
onChange={onChange}
/>
)}
/>
</FormField>
<FormField isRequired title="admin_console.settings.appearance">
<Controller
name="adminConsole.appearanceMode"
control={control}
render={({ field: { value, onChange } }) => (
<Select
value={value}
options={[
{
value: AppearanceMode.SyncWithSystem,
title: t('settings.appearance_system'),
},
{
value: AppearanceMode.LightMode,
title: t('settings.appearance_light'),
},
{
value: AppearanceMode.DarkMode,
title: t('settings.appearance_dark'),
},
]}
onChange={onChange}
/>
)}
/>
</FormField>
</form>
<div className={detailsStyles.footer}>
<Button
isLoading={isSubmitting}
type="primary"
htmlType="submit"
title="general.save_changes"
/>
<div className={detailsStyles.footerMain}>
<Button
isLoading={isSubmitting}
type="primary"
htmlType="submit"
title="general.save_changes"
/>
</div>
</div>
</form>
</>
)}
</Card>
);

View file

@ -13,6 +13,14 @@
.tabs {
padding-top: _.unit(2);
}
.card {
padding-bottom: 0;
}
.form {
padding-bottom: _.unit(8);
}
}
.preview {

View file

@ -1,4 +1,5 @@
import { SignInExperience as SignInExperienceType } from '@logto/schemas';
import classNames from 'classnames';
import React, { useEffect } from 'react';
import { FormProvider, useForm } from 'react-hook-form';
import { toast } from 'react-hot-toast';
@ -55,8 +56,8 @@ const SignInExperience = () => {
return (
<div className={styles.wrapper}>
<div className={styles.setup}>
<Card className={detailsStyles.container}>
<div className={classNames(styles.setup, detailsStyles.container)}>
<Card className={styles.card}>
<CardTitle title="sign_in_exp.title" subtitle="sign_in_exp.description" />
<TabNav className={styles.tabs}>
<TabNavLink href="/sign-in-experience/experience">
@ -73,7 +74,7 @@ const SignInExperience = () => {
{error && <div>{`error occurred: ${error.body.message}`}</div>}
{data && (
<FormProvider {...methods}>
<form onSubmit={onSubmit}>
<form className={classNames(detailsStyles.body, styles.form)} onSubmit={onSubmit}>
{tab === 'experience' && (
<>
<BrandingForm />
@ -82,7 +83,9 @@ const SignInExperience = () => {
)}
{tab === 'methods' && <SignInMethodsForm />}
{tab === 'others' && <LanguagesForm />}
<div className={detailsStyles.footer}>
</form>
<div className={detailsStyles.footer}>
<div className={detailsStyles.footerMain}>
<Button
isLoading={isSubmitting}
type="primary"
@ -90,7 +93,7 @@ const SignInExperience = () => {
title="general.save_changes"
/>
</div>
</form>
</div>
</FormProvider>
)}
</Card>

View file

@ -57,8 +57,6 @@
}
.body {
padding-bottom: 0;
> :first-child {
margin-top: 0;
}

View file

@ -1,5 +1,6 @@
import { User } from '@logto/schemas';
import { Nullable } from '@silverhand/essentials';
import classNames from 'classnames';
import React, { useEffect, useState } from 'react';
import { Controller, useController, useForm } from 'react-hook-form';
import { toast } from 'react-hot-toast';
@ -176,7 +177,7 @@ const UserDetails = () => {
</ReactModal>
</div>
</Card>
<Card className={styles.body}>
<Card className={classNames(styles.body, detailsStyles.body)}>
<TabNav>
<TabNavLink href={`/users/${id}`}>{t('user_details.tab_settings')}</TabNavLink>
<TabNavLink href={`/users/${id}/logs`}>{t('user_details.tab_logs')}</TabNavLink>
@ -252,13 +253,15 @@ const UserDetails = () => {
</FormField>
</div>
<div className={detailsStyles.footer}>
<Button
isLoading={isSubmitting}
htmlType="submit"
type="primary"
title="admin_console.user_details.save_changes"
size="large"
/>
<div className={detailsStyles.footerMain}>
<Button
isLoading={isSubmitting}
htmlType="submit"
type="primary"
title="admin_console.user_details.save_changes"
size="large"
/>
</div>
</div>
</form>
</Card>

View file

@ -1,18 +1,37 @@
@use '@/scss/underscore' as _;
.container {
position: relative;
height: 100%;
display: flex;
flex-direction: column;
> *:not(:first-child) {
margin-top: _.unit(4);
}
.body {
position: relative;
padding-bottom: 0;
flex: 1;
display: flex;
flex-direction: column;
}
.footer {
padding: 0 _.unit(6) _.unit(6);
text-align: right;
position: sticky;
bottom: 0;
background: var(--color-layer-1);
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 {
border-bottom-left-radius: 16px;
border-bottom-right-radius: 16px;
background: var(--color-layer-1);
padding: _.unit(6);
}
&::before {
content: '';
@ -20,7 +39,7 @@
opacity: 50%;
height: 1px;
display: block;
margin: _.unit(6) _.unit(-6);
margin: 0;
}
}
}