0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2025-01-27 21:39:16 -05:00

fix(console): adjust styles on table (#476)

This commit is contained in:
Wang Sijie 2022-04-01 16:17:54 +08:00 committed by GitHub
parent 36cfdf4b16
commit 039f3d0cbb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 246 additions and 231 deletions

View file

@ -2,7 +2,7 @@
.container {
display: inline-block;
padding: _.unit(3);
padding: _.unit(2) _.unit(3);
border-radius: _.unit(2);
background: var(--color-inverse-on-surface);
color: var(--color-on-secondary-container);

View file

@ -10,12 +10,16 @@ type Props = {
borderRadius?: number;
};
const ImagePlaceholder = ({ size = 50, borderRadius = 8 }: Props) => {
const ImagePlaceholder = ({ size = 40, borderRadius = 8 }: Props) => {
const { t } = useTranslation();
return (
<div className={styles.container} style={{ width: size, height: size, borderRadius }}>
<img alt={t('general.placeholder')} src={defaultPlaceholder} />
<img
alt={t('general.placeholder')}
src={defaultPlaceholder}
style={{ width: size, height: size }}
/>
</div>
);
};

View file

@ -23,4 +23,15 @@
font: var(--font-body-small);
color: var(--color-outline);
}
&.compact {
.content {
display: flex;
align-items: baseline;
.title {
margin-right: _.unit(1);
}
}
}
}

View file

@ -1,3 +1,4 @@
import classNames from 'classnames';
import React, { ReactNode } from 'react';
import { Link, To } from 'react-router-dom';
@ -8,13 +9,14 @@ type Props = {
subtitle?: string;
icon?: ReactNode;
to?: To;
size?: 'default' | 'compact';
};
const ItemPreview = ({ title, subtitle, icon, to }: Props) => {
const ItemPreview = ({ title, subtitle, icon, to, size = 'default' }: Props) => {
return (
<div className={styles.item}>
<div className={classNames(styles.item, styles[size])}>
{icon && <div className={styles.icon}>{icon}</div>}
<div>
<div className={styles.content}>
{to && (
<Link
className={styles.title}

View file

@ -3,12 +3,12 @@
td > div.loading {
display: flex;
background: none;
height: 50px;
height: 40px;
border-radius: unset;
.avatar {
width: 50px;
height: 50px;
width: 40px;
height: 40px;
border-radius: _.unit(2);
margin-right: _.unit(4);
background: var(--color-neutral-95);
@ -20,14 +20,15 @@ td > div.loading {
.title {
background: var(--color-neutral-95);
border-radius: _.unit(2);
height: 16px;
margin: _.unit(2) 0;
height: 14px;
margin: _.unit(1) 0;
}
.subTitle {
background: var(--color-neutral-95);
border-radius: _.unit(2);
height: 12px;
margin-top: _.unit(2);
}
}
}

View file

@ -1,5 +1,9 @@
@use '@/scss/underscore' as _;
.card {
@include _.flex-column;
}
.headline {
display: flex;
justify-content: space-between;
@ -7,18 +11,7 @@
.table {
margin-top: _.unit(4);
tbody {
max-height: calc(100vh - _.unit(64));
tr.clickable {
cursor: pointer;
&:hover {
background: var(--color-table-row-selected);
}
}
}
flex: 1;
}
.apiResourceName {

View file

@ -1,5 +1,6 @@
import { Resource } from '@logto/schemas';
import { conditional } from '@silverhand/essentials/lib/utilities/conditional.js';
import classNames from 'classnames';
import React, { useState } from 'react';
import { toast } from 'react-hot-toast';
import { useTranslation } from 'react-i18next';
@ -18,6 +19,7 @@ import TableError from '@/components/Table/TableError';
import TableLoading from '@/components/Table/TableLoading';
import { RequestError } from '@/hooks/use-api';
import * as modalStyles from '@/scss/modal.module.scss';
import * as tableStyles from '@/scss/table.module.scss';
import CreateForm from './components/CreateForm';
import * as styles from './index.module.scss';
@ -32,7 +34,7 @@ const ApiResources = () => {
const navigate = useNavigate();
return (
<Card>
<Card className={styles.card}>
<div className={styles.headline}>
<CardTitle title="api_resources.title" subtitle="api_resources.subtitle" />
<Button
@ -62,55 +64,57 @@ const ApiResources = () => {
/>
</Modal>
</div>
<table className={styles.table}>
<colgroup>
<col className={styles.apiResourceName} />
<col />
</colgroup>
<thead>
<tr>
<th>{t('api_resources.api_name')}</th>
<th>{t('api_resources.api_identifier')}</th>
</tr>
</thead>
<tbody>
{error && (
<TableError
columns={2}
content={error.body.message}
onRetry={async () => mutate(undefined, true)}
/>
)}
{isLoading && <TableLoading columns={2} />}
{data?.length === 0 && (
<TableEmpty columns={2}>
<Button
title="admin_console.api_resources.create"
type="outline"
onClick={() => {
setIsCreateFormOpen(true);
}}
/>
</TableEmpty>
)}
{data?.map(({ id, name, indicator }) => (
<tr
key={id}
className={styles.clickable}
onClick={() => {
navigate(buildDetailsLink(id));
}}
>
<td>
<ItemPreview title={name} icon={<ImagePlaceholder />} to={buildDetailsLink(id)} />
</td>
<td>
<CopyToClipboard value={indicator} />
</td>
<div className={classNames(styles.table, tableStyles.scrollable)}>
<table>
<colgroup>
<col className={styles.apiResourceName} />
<col />
</colgroup>
<thead>
<tr>
<th>{t('api_resources.api_name')}</th>
<th>{t('api_resources.api_identifier')}</th>
</tr>
))}
</tbody>
</table>
</thead>
<tbody>
{error && (
<TableError
columns={2}
content={error.body.message}
onRetry={async () => mutate(undefined, true)}
/>
)}
{isLoading && <TableLoading columns={2} />}
{data?.length === 0 && (
<TableEmpty columns={2}>
<Button
title="admin_console.api_resources.create"
type="outline"
onClick={() => {
setIsCreateFormOpen(true);
}}
/>
</TableEmpty>
)}
{data?.map(({ id, name, indicator }) => (
<tr
key={id}
className={tableStyles.clickable}
onClick={() => {
navigate(buildDetailsLink(id));
}}
>
<td>
<ItemPreview title={name} icon={<ImagePlaceholder />} to={buildDetailsLink(id)} />
</td>
<td>
<CopyToClipboard value={indicator} />
</td>
</tr>
))}
</tbody>
</table>
</div>
</Card>
);
};

View file

@ -1,5 +1,9 @@
@use '@/scss/underscore' as _;
.card {
@include _.flex-column;
}
.headline {
display: flex;
justify-content: space-between;
@ -7,18 +11,7 @@
.table {
margin-top: _.unit(4);
tbody {
max-height: calc(100vh - _.unit(64));
tr.clickable {
cursor: pointer;
&:hover {
background: var(--color-table-row-selected);
}
}
}
flex: 1;
}
.applicationName {

View file

@ -1,5 +1,6 @@
import { Application } from '@logto/schemas';
import { conditional } from '@silverhand/essentials/lib/utilities/conditional.js';
import classNames from 'classnames';
import React, { useState } from 'react';
import { toast } from 'react-hot-toast';
import { useTranslation } from 'react-i18next';
@ -18,6 +19,7 @@ import TableError from '@/components/Table/TableError';
import TableLoading from '@/components/Table/TableLoading';
import { RequestError } from '@/hooks/use-api';
import * as modalStyles from '@/scss/modal.module.scss';
import * as tableStyles from '@/scss/table.module.scss';
import { applicationTypeI18nKey } from '@/types/applications';
import CreateForm from './components/CreateForm';
@ -31,7 +33,7 @@ const Applications = () => {
const navigate = useNavigate();
return (
<Card>
<Card className={styles.card}>
<div className={styles.headline}>
<CardTitle title="applications.title" subtitle="applications.subtitle" />
<Button
@ -60,60 +62,62 @@ const Applications = () => {
/>
</Modal>
</div>
<table className={styles.table}>
<colgroup>
<col className={styles.applicationName} />
<col />
</colgroup>
<thead>
<tr>
<th>{t('applications.application_name')}</th>
<th>{t('applications.client_id')}</th>
</tr>
</thead>
<tbody>
{error && (
<TableError
columns={2}
content={error.body.message}
onRetry={async () => mutate(undefined, true)}
/>
)}
{isLoading && <TableLoading columns={2} />}
{data?.length === 0 && (
<TableEmpty columns={2}>
<Button
title="admin_console.applications.create"
type="outline"
onClick={() => {
setIsCreateFormOpen(true);
}}
/>
</TableEmpty>
)}
{data?.map(({ id, name, type }) => (
<tr
key={id}
className={styles.clickable}
onClick={() => {
navigate(`/applications/${id}`);
}}
>
<td>
<ItemPreview
title={name}
subtitle={t(`${applicationTypeI18nKey[type]}.title`)}
icon={<ImagePlaceholder />}
to={`/applications/${id}`}
/>
</td>
<td>
<CopyToClipboard value={id} />
</td>
<div className={classNames(styles.table, tableStyles.scrollable)}>
<table>
<colgroup>
<col className={styles.applicationName} />
<col />
</colgroup>
<thead>
<tr>
<th>{t('applications.application_name')}</th>
<th>{t('applications.client_id')}</th>
</tr>
))}
</tbody>
</table>
</thead>
<tbody>
{error && (
<TableError
columns={2}
content={error.body.message}
onRetry={async () => mutate(undefined, true)}
/>
)}
{isLoading && <TableLoading columns={2} />}
{data?.length === 0 && (
<TableEmpty columns={2}>
<Button
title="admin_console.applications.create"
type="outline"
onClick={() => {
setIsCreateFormOpen(true);
}}
/>
</TableEmpty>
)}
{data?.map(({ id, name, type }) => (
<tr
key={id}
className={tableStyles.clickable}
onClick={() => {
navigate(`/applications/${id}`);
}}
>
<td>
<ItemPreview
title={name}
subtitle={t(`${applicationTypeI18nKey[type]}.title`)}
icon={<ImagePlaceholder />}
to={`/applications/${id}`}
/>
</td>
<td>
<CopyToClipboard value={id} />
</td>
</tr>
))}
</tbody>
</table>
</div>
</Card>
);
};

View file

@ -1,5 +1,9 @@
@use '@/scss/underscore' as _;
.card {
@include _.flex-column;
}
.headline {
display: flex;
justify-content: space-between;
@ -11,18 +15,7 @@
.table {
margin-top: _.unit(4);
tbody {
max-height: calc(100vh - _.unit(74));
tr.clickable {
cursor: pointer;
&:hover {
background: var(--color-table-row-selected);
}
}
}
flex: 1;
}
.connectorName {

View file

@ -1,4 +1,5 @@
import { ConnectorDTO, ConnectorType } from '@logto/schemas';
import classNames from 'classnames';
import React, { useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useLocation } from 'react-router-dom';
@ -12,6 +13,7 @@ import TableEmpty from '@/components/Table/TableEmpty';
import TableError from '@/components/Table/TableError';
import TableLoading from '@/components/Table/TableLoading';
import { RequestError } from '@/hooks/use-api';
import * as tableStyles from '@/scss/table.module.scss';
import ConnectorRow from './components/ConnectorRow';
import SetupModal from './components/SetupModal';
@ -49,7 +51,7 @@ const Connectors = () => {
return (
<>
<Card>
<Card className={styles.card}>
<div className={styles.headline}>
<CardTitle title="connectors.title" subtitle="connectors.subtitle" />
{isSocial && (
@ -66,54 +68,60 @@ const Connectors = () => {
<TabNavLink href="/connectors">{t('connectors.tab_email_sms')}</TabNavLink>
<TabNavLink href="/connectors/social">{t('connectors.tab_social')}</TabNavLink>
</TabNav>
<table className={styles.table}>
<colgroup>
<col className={styles.connectorName} />
<col />
<col />
</colgroup>
<thead>
<tr>
<th>{t('connectors.connector_name')}</th>
<th>{t('connectors.connector_type')}</th>
<th>{t('connectors.connector_status')}</th>
</tr>
</thead>
<tbody>
{error && (
<TableError
columns={3}
content={error.body.message}
onRetry={async () => mutate(undefined, true)}
/>
)}
{isLoading && <TableLoading columns={3} />}
{socialConnectors?.length === 0 && (
<TableEmpty
columns={3}
title={t('connectors.type.social')}
content={t('connectors.social_connector_eg')}
>
<Button title="admin_console.connectors.create" type="outline" />
</TableEmpty>
)}
{!isLoading && !isSocial && (
<ConnectorRow
connector={emailConnector}
type={ConnectorType.Email}
onClickSetup={() => {
setCreateType(ConnectorType.Email);
}}
/>
)}
{!isLoading && !isSocial && (
<ConnectorRow connector={smsConnector} type={ConnectorType.SMS} />
)}
{socialConnectors?.map((connector) => (
<ConnectorRow key={connector.id} connector={connector} type={ConnectorType.Social} />
))}
</tbody>
</table>
<div className={classNames(styles.table, tableStyles.scrollable)}>
<table>
<colgroup>
<col className={styles.connectorName} />
<col />
<col />
</colgroup>
<thead>
<tr>
<th>{t('connectors.connector_name')}</th>
<th>{t('connectors.connector_type')}</th>
<th>{t('connectors.connector_status')}</th>
</tr>
</thead>
<tbody>
{error && (
<TableError
columns={3}
content={error.body.message}
onRetry={async () => mutate(undefined, true)}
/>
)}
{isLoading && <TableLoading columns={3} />}
{socialConnectors?.length === 0 && (
<TableEmpty
columns={3}
title={t('connectors.type.social')}
content={t('connectors.social_connector_eg')}
>
<Button title="admin_console.connectors.create" type="outline" />
</TableEmpty>
)}
{!isLoading && !isSocial && (
<ConnectorRow
connector={emailConnector}
type={ConnectorType.Email}
onClickSetup={() => {
setCreateType(ConnectorType.Email);
}}
/>
)}
{!isLoading && !isSocial && (
<ConnectorRow connector={smsConnector} type={ConnectorType.SMS} />
)}
{socialConnectors?.map((connector) => (
<ConnectorRow
key={connector.id}
connector={connector}
type={ConnectorType.Social}
/>
))}
</tbody>
</table>
</div>
</Card>
{data && (
<SetupModal

View file

@ -1,9 +1,7 @@
@use '@/scss/underscore' as _;
.card {
display: flex;
flex-direction: column;
height: 100%;
@include _.flex-column;
}
.headline {
@ -17,14 +15,6 @@
.table {
flex: 1;
tr.clickable {
cursor: pointer;
&:hover {
background: var(--color-table-row-selected);
}
}
}
.pagination {

View file

@ -107,7 +107,7 @@ const Users = () => {
{users?.map(({ id, name, username }) => (
<tr
key={id}
className={styles.clickable}
className={tableStyles.clickable}
onClick={() => {
navigate(`/users/${id}`);
}}
@ -116,8 +116,9 @@ const Users = () => {
<ItemPreview
title={name ?? '-'}
subtitle={username ?? '-'}
icon={<ImagePlaceholder />}
icon={<ImagePlaceholder size={24} />}
to={`/users/${id}`}
size="compact"
/>
</td>
<td>Application</td>

View file

@ -83,6 +83,7 @@
--color-code-comment: #66bb6a;
--color-surface-3: #e3dff5;
--color-surface-5: #dfd9f5;
--shadow-light-s1: 0 2px 8px rgba(0, 0, 0, 8%);
--shadow-light-s2: 0 4px 12px rgba(0, 0, 0, 12%);
}

View file

@ -7,8 +7,8 @@
@mixin flex-column {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100%;
}
@mixin text-ellipsis {

View file

@ -30,30 +30,27 @@ table {
th {
font: var(--font-subhead-2);
color: var(--color-component-caption);
padding: _.unit(3) _.unit(4);
padding: _.unit(2);
border-bottom: 1px solid var(--color-neutral-90);
text-align: left;
&:first-child,
&:last-child {
padding: _.unit(2) _.unit(8);
}
}
}
tbody {
td {
font: var(--font-body-medium);
border-bottom: 1px solid var(--color-neutral-90);
padding: _.unit(3) _.unit(2);
&:first-child,
&:last-child {
padding: _.unit(3) _.unit(8);
}
}
}
tbody {
td {
border-bottom: 1px solid var(--color-neutral-90);
padding: _.unit(5) _.unit(4);
&:first-child,
&:last-child {
padding: _.unit(5) _.unit(8);
}
}
}
tr:last-child td {
border-bottom: none;
}
}

View file

@ -1,13 +1,26 @@
@use '@/scss/underscore' as _;
.base {
box-shadow: var(--shadow-light-s1);
}
tr.clickable {
cursor: pointer;
&:hover {
background: var(--color-table-row-selected);
}
}
.scrollable {
margin-top: _.unit(4);
overflow-y: auto;
border: 1px solid var(--color-neutral-90);
border-radius: _.unit(2);
box-shadow: var(--shadow-light-s1);
table {
border: none;
box-shadow: none;
thead tr {
position: sticky;