Skip to content

Commit

Permalink
refactor(console): improve ux
Browse files Browse the repository at this point in the history
  • Loading branch information
gao-sun committed Jul 27, 2024
1 parent acc5033 commit 3039852
Show file tree
Hide file tree
Showing 25 changed files with 255 additions and 154 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ builder.Services.AddLogtoAuthentication(options =>
{
options.Endpoint = "${props.endpoint}";
options.AppId = "${props.app.id}";
options.AppSecret = "${props.app.secret}";
options.AppSecret = "${props.secrets[0]?.value ?? props.app.secret}";
});
app.UseAuthentication();`}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ Prepare configuration for the Logto client:
const config: LogtoExpressConfig = {
endpoint: '${props.endpoint}',
appId: '${props.app.id}',
appSecret: '${props.app.secret}',
appSecret: '${props.secrets[0]?.value ?? props.app.secret}',
baseUrl: 'http://localhost:3000', // Change to your own base URL
};
`}
Expand Down
2 changes: 1 addition & 1 deletion packages/console/src/assets/docs/guides/web-go/README.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ First, create a Logto config:
logtoConfig := &client.LogtoConfig{
Endpoint: "${props.endpoint}",
AppId: "${props.app.id}",
AppSecret: "${props.app.secret}",
AppSecret: "${props.secrets[0]?.value ?? props.app.secret}",
}
// ...
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ Add the following configuration to your `application.properties` file:
<Code className="language-properties" title="application.properties">
{`spring.security.oauth2.client.registration.logto.client-name=logto
spring.security.oauth2.client.registration.logto.client-id=${props.app.id}
spring.security.oauth2.client.registration.logto.client-secret=${props.app.secret}
spring.security.oauth2.client.registration.logto.client-secret=${props.secrets[0]?.value ?? props.app.secret}
spring.security.oauth2.client.registration.logto.redirect-uri={baseUrl}/login/oauth2/code/{registrationId}
spring.security.oauth2.client.registration.logto.authorization-grant-type=authorization_code
spring.security.oauth2.client.registration.logto.scope=openid,profile,email,offline_access
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ Prepare configuration for the Logto client:
{`export const logtoConfig = {
endpoint: '${props.endpoint}',
appId: '${props.app.id}',
appSecret: '${props.app.secret}',
appSecret: '${props.secrets[0]?.value ?? props.app.secret}',
baseUrl: 'http://localhost:3000', // Change to your own base URL
cookieSecret: '${generateStandardSecret()}', // Auto-generated 32 digit secret
cookieSecure: process.env.NODE_ENV === 'production',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ const handler = NextAuth({
wellKnown: '${props.endpoint}oidc/.well-known/openid-configuration',
authorization: { params: { scope: 'openid offline_access profile email' } },
clientId: '${props.app.id}'',
clientSecret: '${props.app.secret}',
clientSecret: '${props.secrets[0]?.value ?? props.app.secret}',
client: {
id_token_signed_response_alg: 'ES384',
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Import and initialize LogtoClient:
export const logtoClient = new LogtoClient({
endpoint: '${props.endpoint}',
appId: '${props.app.id}',
appSecret: '${props.app.secret}',
appSecret: '${props.secrets[0]?.value ?? props.app.secret}',
baseUrl: '${defaultBaseUrl}', // Change to your own base URL
cookieSecret: '${generateStandardSecret()}', // Auto-generated 32 digit secret
cookieSecure: process.env.NODE_ENV === 'production',
Expand Down
4 changes: 2 additions & 2 deletions packages/console/src/assets/docs/guides/web-nuxt/README.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ In your Nuxt config file, add the Logto module and configure it:
logto: {
endpoint: '${props.endpoint}',
appId: '${props.app.id}',
appSecret: '${props.app.secret}',
appSecret: '${props.secrets[0]?.value ?? props.app.secret}',
cookieEncryptionKey: '${cookieEncryptionKey}', // Random-generated
},
},
Expand All @@ -48,7 +48,7 @@ Since these information are sensitive, it's recommended to use environment varia
<Code title=".env" className="language-bash">
{`NUXT_LOGTO_ENDPOINT=${props.endpoint}
NUXT_LOGTO_APP_ID=${props.app.id}
NUXT_LOGTO_APP_SECRET=${props.app.secret}
NUXT_LOGTO_APP_SECRET=${props.secrets[0]?.value ?? props.app.secret}
NUXT_LOGTO_COOKIE_ENCRYPTION_KEY=${cookieEncryptionKey} # Random-generated
`}
</Code>
Expand Down
2 changes: 1 addition & 1 deletion packages/console/src/assets/docs/guides/web-php/README.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ $client = new LogtoClient(
new LogtoConfig(
endpoint: "${props.endpoint}",
appId: "${props.app.id}",
appSecret: "${props.app.secret}",
appSecret: "${props.secrets[0]?.value ?? props.app.secret}",
),
);`}
</Code>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ client = LogtoClient(
LogtoConfig(
endpoint="${props.endpoint}",
appId="${props.app.id}",
appSecret="${props.app.secret}",
appSecret="${props.secrets[0]?.value ?? props.app.secret}",
)
)`}
</Code>
Expand Down
4 changes: 2 additions & 2 deletions packages/console/src/assets/docs/guides/web-ruby/README.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ In the file where you want to initialize the Logto client (e.g. a base controlle
config: LogtoClient::Config.new(
endpoint: "${props.endpoint}",
app_id: "${props.app.id}",
app_secret: "${props.app.secret}"
app_secret: "${props.secrets[0]?.value ?? props.app.secret}"
),
navigate: ->(uri) { a_redirect_method(uri) },
storage: LogtoClient::SessionStorage.new(the_session_object)
Expand All @@ -64,7 +64,7 @@ class SampleController < ApplicationController
config: LogtoClient::Config.new(
endpoint: "${props.endpoint}",
app_id: "${props.app.id}",
app_secret: "${props.app.secret}"
app_secret: "${props.secrets[0]?.value ?? props.app.secret}"
),
# Allow the client to redirect to other hosts (i.e. your Logto tenant)
navigate: ->(uri) { redirect_to(uri, allow_other_host: true) },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export const handle = handleLogto(
{
endpoint: '${props.endpoint}',
appId: '${props.app.id}',
appSecret: '${props.app.secret}',
appSecret: '${props.secrets[0]?.value ?? props.app.secret}',
},
{ encryptionKey: '${cookieEncryptionKey}' } // Random-generated key
);`}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,36 +28,34 @@
align-items: center;
justify-content: space-between;
cursor: text;
gap: _.unit(2);

.content {
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
display: inline-flex;
align-items: center;
gap: _.unit(2);
margin-right: _.unit(1);
flex-wrap: nowrap;

&.wrapContent {
text-overflow: unset;
word-break: break-all;
}
}

.copyToolTipAnchor {
margin-left: _.unit(2);
}
}

&.default {
.row {
.copyToolTipAnchor {
margin-left: _.unit(3);
}
gap: _.unit(2);
}
}

&.small {
.row {
.copyToolTipAnchor {
margin-left: _.unit(1);
}
gap: _.unit(0.5);

.iconButton {
height: 20px;
Expand All @@ -72,4 +70,13 @@
}
}
}

.dot {
flex-shrink: 0;
display: inline-block;
width: 6px;
height: 6px;
border-radius: 50%;
background: var(--color-text-secondary);
}
}
13 changes: 6 additions & 7 deletions packages/console/src/ds-components/CopyToClipboard/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,10 @@ function CopyToClipboard(
return value;
}

return '•'.repeat(value.length);
return Array.from({ length: Math.max(Math.floor((value.length / 5) * 3), 1) }).map(
// eslint-disable-next-line react/no-array-index-key -- No need to persist the key
(_, index) => <span key={index} className={styles.dot} />
);
}, [hasVisibilityToggle, showHiddenContent, value]);

useEffect(() => {
Expand Down Expand Up @@ -107,7 +110,7 @@ function CopyToClipboard(
{variant !== 'icon' && (
<div
className={classNames(styles.content, isWordWrapAllowed && styles.wrapContent)}
style={valueStyle}
style={{ width: `${value.length}ch`, ...valueStyle }}
>
{displayValue}
</div>
Expand All @@ -124,11 +127,7 @@ function CopyToClipboard(
</IconButton>
</Tooltip>
)}
<Tooltip
isSuccessful={copyState === 'copied'}
anchorClassName={styles.copyToolTipAnchor}
content={t(copyState)}
>
<Tooltip isSuccessful={copyState === 'copied'} content={t(copyState)}>
<IconButton
ref={copyIconReference}
className={styles.iconButton}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ function CreateSecretModal({ appId, isOpen, onClose }: Props) {
formState: { errors, isSubmitting },
handleSubmit,
reset,
} = useForm<FormData>({ defaultValues: { name: '', expiration: String(days[0]) } });
} = useForm<FormData>({ defaultValues: { name: '', expiration: neverExpires } });
const onCloseHandler = useCallback(
(created?: ApplicationSecret) => {
reset();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
@use '@/scss/underscore' as _;

.customEndpointNotes {
margin-top: _.unit(6);
font: var(--font-body-2);
color: var(--color-text-secondary);
}

.fieldButton {
margin-top: _.unit(2);
}

.trailingIcon {
width: 16px;
height: 16px;
}

button.add {
margin-top: _.unit(2);
}

.table {
margin-top: _.unit(2);
word-break: break-word;
}

.empty {
font: var(--font-body-2);
color: var(--color-text-secondary);
margin: _.unit(3) 0;
}

.expired {
color: var(--color-text-secondary);
}

.copyToClipboard {
width: fit-content;
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import {
type ApplicationSecret,
DomainStatus,
type Application,
type SnakeCaseOidcConfig,
Expand All @@ -15,7 +14,6 @@ import CaretDown from '@/assets/icons/caret-down.svg?react';
import CaretUp from '@/assets/icons/caret-up.svg?react';
import CirclePlus from '@/assets/icons/circle-plus.svg?react';
import Plus from '@/assets/icons/plus.svg?react';
import ActionsButton from '@/components/ActionsButton';
import FormCard from '@/components/FormCard';
import { openIdProviderConfigPath, openIdProviderPath } from '@/consts/oidc';
import { AppDataContext } from '@/contexts/AppDataProvider';
Expand All @@ -24,19 +22,18 @@ import CopyToClipboard from '@/ds-components/CopyToClipboard';
import DynamicT from '@/ds-components/DynamicT';
import FormField from '@/ds-components/FormField';
import Table from '@/ds-components/Table';
import { type Column } from '@/ds-components/Table/types';
import TextLink from '@/ds-components/TextLink';
import useApi, { type RequestError } from '@/hooks/use-api';
import { type RequestError } from '@/hooks/use-api';
import useCustomDomain from '@/hooks/use-custom-domain';

import CreateSecretModal from './CreateSecretModal';
import CreateSecretModal from '../CreateSecretModal';

import styles from './index.module.scss';
import { type ApplicationSecretRow, useSecretTableColumns } from './use-secret-table-columns';

const isLegacySecret = (secret: string) => !secret.startsWith(internalPrefix);
export { type ApplicationSecretRow } from './use-secret-table-columns';

type ApplicationSecretRow = Pick<ApplicationSecret, 'name' | 'value' | 'expiresAt'> & {
isLegacy?: boolean;
};
const isLegacySecret = (secret: string) => !secret.startsWith(internalPrefix);

type Props = {
readonly app: Application;
Expand All @@ -55,7 +52,6 @@ function EndpointsAndCredentials({
const { data: customDomain, applyDomain: applyCustomDomain } = useCustomDomain();
const [showCreateSecretModal, setShowCreateSecretModal] = useState(false);
const secrets = useSWR<ApplicationSecretRow[], RequestError>(`api/applications/${id}/secrets`);
const api = useApi();
const shouldShowAppSecrets = hasSecrets(type);

const toggleShowMoreEndpoints = useCallback(() => {
Expand All @@ -80,57 +76,21 @@ function EndpointsAndCredentials({
],
[secret, secrets.data, t]
);
const tableColumns: Array<Column<ApplicationSecretRow>> = useMemo(
() => [
{
title: t('general.name'),
dataIndex: 'name',
colSpan: 3,
render: ({ name }) => <span>{name}</span>,
},
{
title: t('application_details.secrets.value'),
dataIndex: 'value',
colSpan: 6,
render: ({ value }) => (
<CopyToClipboard hasVisibilityToggle displayType="block" value={value} variant="text" />
),
},
{
title: t('application_details.secrets.expires_at'),
dataIndex: 'expiresAt',
colSpan: 3,
render: ({ expiresAt }) => (
<span>
{expiresAt
? new Date(expiresAt).toLocaleString()
: t('application_details.secrets.never')}
</span>
),
},
{
title: '',
dataIndex: 'actions',
render: ({ name, isLegacy }) => (
<ActionsButton
fieldName="application_details.application_secret"
deleteConfirmation="application_details.secrets.delete_confirmation"
onDelete={async () => {
if (isLegacy) {
await api.delete(`api/applications/${id}/legacy-secret`);
onApplicationUpdated();
} else {
await api.delete(`api/applications/${id}/secrets/${encodeURIComponent(name)}`);
void secrets.mutate();
}
}}
/>
),
},
],
[api, id, onApplicationUpdated, secrets, t]
);

const onUpdated = useCallback(
(isLegacy: boolean) => {
if (isLegacy) {
onApplicationUpdated();
} else {
void secrets.mutate();
}
},
[onApplicationUpdated, secrets]
);
const tableColumns = useSecretTableColumns({
appId: id,
onUpdated,
});
return (
<FormCard
title="application_details.endpoints_and_credentials"
Expand Down
Loading

0 comments on commit 3039852

Please sign in to comment.