From 0c9591f08e7ad013bdda58193b3fa929917823b3 Mon Sep 17 00:00:00 2001 From: JimmFly Date: Thu, 13 Mar 2025 10:46:26 +0000 Subject: [PATCH] feat(core): add an entry for admin panel (#10813) ![CleanShot 2025-03-13 at 13 02 25@2x](https://github.com/user-attachments/assets/82f50a5b-f079-4c64-a3fa-6554735bea82) --- .../components/root-app-sidebar/user-info.tsx | 23 ++++++- .../modules/cloud/entities/user-feature.ts | 5 ++ .../i18n/src/i18n-completenesses.json | 2 +- packages/frontend/i18n/src/i18n.gen.ts | 65 ++++++++++++++----- packages/frontend/i18n/src/resources/en.json | 8 ++- 5 files changed, 81 insertions(+), 22 deletions(-) diff --git a/packages/frontend/core/src/components/root-app-sidebar/user-info.tsx b/packages/frontend/core/src/components/root-app-sidebar/user-info.tsx index 36190de6a5..b376ecd0d9 100644 --- a/packages/frontend/core/src/components/root-app-sidebar/user-info.tsx +++ b/packages/frontend/core/src/components/root-app-sidebar/user-info.tsx @@ -14,7 +14,7 @@ import { } from '@affine/core/modules/dialogs'; import { useI18n } from '@affine/i18n'; import { track } from '@affine/track'; -import { AccountIcon, SignOutIcon } from '@blocksuite/icons/rc'; +import { AccountIcon, AdminIcon, SignOutIcon } from '@blocksuite/icons/rc'; import { useLiveData, useService } from '@toeverything/infra'; import { cssVar } from '@toeverything/theme'; import { assignInlineVars } from '@vanilla-extract/dynamic'; @@ -27,6 +27,7 @@ import { ServerService, SubscriptionService, UserCopilotQuotaService, + UserFeatureService, UserQuotaService, } from '../../modules/cloud'; import { UserPlanButton } from '../affine/auth/user-plan-button'; @@ -80,6 +81,9 @@ const UnauthorizedUserInfo = () => { const AccountMenu = () => { const workspaceDialogService = useService(WorkspaceDialogService); const openSignOutModal = useSignOut(); + const serverService = useService(ServerService); + const userFeatureService = useService(UserFeatureService); + const isAdmin = useLiveData(userFeatureService.userFeature.isAdmin$); const onOpenAccountSetting = useCallback(() => { track.$.navigationPanel.profileAndBadge.openSettings({ to: 'account' }); @@ -88,8 +92,16 @@ const AccountMenu = () => { }); }, [workspaceDialogService]); + const onOpenAdminPanel = useCallback(() => { + window.open(`${serverService.server.baseUrl}/admin`, '_blank'); + }, [serverService.server.baseUrl]); + const t = useI18n(); + useEffect(() => { + userFeatureService.userFeature.revalidate(); + }, [userFeatureService]); + return ( <> { > {t['com.affine.workspace.cloud.account.settings']()} + {isAdmin ? ( + } + data-testid="workspace-modal-account-admin-option" + onClick={onOpenAdminPanel} + > + {t['com.affine.workspace.cloud.account.admin']()} + + ) : null} } data-testid="workspace-modal-sign-out-option" diff --git a/packages/frontend/core/src/modules/cloud/entities/user-feature.ts b/packages/frontend/core/src/modules/cloud/entities/user-feature.ts index 967ab4449a..d1e44126d5 100644 --- a/packages/frontend/core/src/modules/cloud/entities/user-feature.ts +++ b/packages/frontend/core/src/modules/cloud/entities/user-feature.ts @@ -18,6 +18,11 @@ import type { UserFeatureStore } from '../stores/user-feature'; export class UserFeature extends Entity { // undefined means no user, null means loading features$ = new LiveData(null); + + isAdmin$ = this.features$.map(features => + features === null ? null : features?.some(f => f === FeatureType.Admin) + ); + isEarlyAccess$ = this.features$.map(features => features === null ? null diff --git a/packages/frontend/i18n/src/i18n-completenesses.json b/packages/frontend/i18n/src/i18n-completenesses.json index 51ac8b71d1..39b698a733 100644 --- a/packages/frontend/i18n/src/i18n-completenesses.json +++ b/packages/frontend/i18n/src/i18n-completenesses.json @@ -14,7 +14,7 @@ "it-IT": 99, "it": 1, "ja": 99, - "ko": 63, + "ko": 62, "pl": 99, "pt-BR": 99, "ru": 99, diff --git a/packages/frontend/i18n/src/i18n.gen.ts b/packages/frontend/i18n/src/i18n.gen.ts index 65a0c12367..383cb725e8 100644 --- a/packages/frontend/i18n/src/i18n.gen.ts +++ b/packages/frontend/i18n/src/i18n.gen.ts @@ -5404,8 +5404,8 @@ export function useAFFiNEI18N(): { */ ["com.affine.settings.workspace.experimental-features.enable-callout.description"](): string; /** - * Embed Iframe Block - */ + * `Embed Iframe Block` + */ ["com.affine.settings.workspace.experimental-features.enable-embed-iframe-block.name"](): string; /** * `Enables Embed Iframe Block.` @@ -6465,6 +6465,10 @@ export function useAFFiNEI18N(): { * `Account settings` */ ["com.affine.workspace.cloud.account.settings"](): string; + /** + * `Admin panel` + */ + ["com.affine.workspace.cloud.account.admin"](): string; /** * `Sign up/ Sign in` */ @@ -6549,6 +6553,18 @@ export function useAFFiNEI18N(): { * `Yesterday` */ ["com.affine.yesterday"](): string; + /** + * `Inactive` + */ + ["com.affine.inactive"](): string; + /** + * `Inactive member` + */ + ["com.affine.inactive-member"](): string; + /** + * `Inactive workspace` + */ + ["com.affine.inactive-workspace"](): string; /** * `core` */ @@ -6992,12 +7008,9 @@ export function useAFFiNEI18N(): { */ ["com.affine.notification.unsupported"](): string; /** - * `{{username}} mentioned you in {{docTitle}}` + * `No new notifications` */ - ["com.affine.notification.mention"](options: Readonly<{ - username: string; - docTitle: string; - }>): string; + ["com.affine.notification.empty"](): string; /** * `Tips` */ @@ -7006,6 +7019,10 @@ export function useAFFiNEI18N(): { * `Template` */ Template(): string; + /** + * `Delete Template` + */ + ["com.affine.template-list.delete"](): string; /** * `No template` */ @@ -7119,6 +7136,12 @@ export function useAFFiNEI18N(): { ["error.QUERY_TOO_LONG"](options: { readonly max: string; }): string; + /** + * `Validation error, errors: {{errors}}` + */ + ["error.VALIDATION_ERROR"](options: { + readonly errors: string; + }): string; /** * `User not found.` */ @@ -7162,6 +7185,10 @@ export function useAFFiNEI18N(): { status: string; body: string; }>): string; + /** + * `Invalid auth state. You might start the auth progress from another device.` + */ + ["error.INVALID_AUTH_STATE"](): string; /** * `Missing query parameter `{{name}}`.` */ @@ -7622,21 +7649,15 @@ export function useAFFiNEI18N(): { */ ["error.NOTIFICATION_NOT_FOUND"](): string; /** - * `Mention user do not have permission to access space {{spaceId}}.` + * `Mentioned user can not access doc {{docId}}.` */ - ["error.MENTION_USER_SPACE_ACCESS_DENIED"](options: { - readonly spaceId: string; + ["error.MENTION_USER_DOC_ACCESS_DENIED"](options: { + readonly docId: string; }): string; /** - * `You cannot mention yourself.` + * `You can not mention yourself.` */ ["error.MENTION_USER_ONESELF_DENIED"](): string; - /** - * `You do not have permission to access notification {{notificationId}}.` - */ - ["error.NOTIFICATION_ACCESS_DENIED"](options: { - readonly notificationId: string; - }): string; } { const { t } = useTranslation(); return useMemo(() => createProxy((key) => t.bind(null, key)), [t]); } function createComponent(i18nKey: string) { return (props) => createElement(Trans, { i18nKey, shouldUnescape: true, ...props }); @@ -8061,6 +8082,16 @@ export const TypedTrans: { }, { ["1"]: JSX.Element; }>>; + /** + * `<1>{{username}} mentioned you in <2>{{docTitle}}` + */ + ["com.affine.notification.mention"]: ComponentType, { + ["1"]: JSX.Element; + ["2"]: JSX.Element; + }>>; /** * `Unable to join <1/> <2>{{workspaceName}} due to insufficient seats available.` */ diff --git a/packages/frontend/i18n/src/resources/en.json b/packages/frontend/i18n/src/resources/en.json index b423cb351a..de968f15da 100644 --- a/packages/frontend/i18n/src/resources/en.json +++ b/packages/frontend/i18n/src/resources/en.json @@ -1610,6 +1610,7 @@ "com.affine.workspace.cloud": "Cloud workspaces", "com.affine.workspace.cloud.account.logout": "Sign out", "com.affine.workspace.cloud.account.settings": "Account settings", + "com.affine.workspace.cloud.account.admin": "Admin panel", "com.affine.workspace.cloud.auth": "Sign up/ Sign in", "com.affine.workspace.cloud.description": "Sync with AFFiNE Cloud", "com.affine.workspace.cloud.join": "Join workspace", @@ -1774,6 +1775,7 @@ "error.BAD_REQUEST": "Bad request.", "error.GRAPHQL_BAD_REQUEST": "GraphQL bad request, code: {{code}}, {{message}}", "error.QUERY_TOO_LONG": "Query is too long, max length is {{max}}.", + "error.VALIDATION_ERROR": "Validation error, errors: {{errors}}", "error.USER_NOT_FOUND": "User not found.", "error.USER_AVATAR_NOT_FOUND": "User avatar not found.", "error.EMAIL_ALREADY_USED": "This email has already been registered.", @@ -1783,6 +1785,7 @@ "error.OAUTH_STATE_EXPIRED": "OAuth state expired, please try again.", "error.INVALID_OAUTH_CALLBACK_STATE": "Invalid callback state parameter.", "error.INVALID_OAUTH_CALLBACK_CODE": "Invalid callback code parameter, provider response status: {{status}} and body: {{body}}.", + "error.INVALID_AUTH_STATE": "Invalid auth state. You might start the auth progress from another device.", "error.MISSING_OAUTH_QUERY_PARAMETER": "Missing query parameter `{{name}}`.", "error.OAUTH_ACCOUNT_ALREADY_CONNECTED": "The third-party account has already been connected to another user.", "error.INVALID_EMAIL": "An invalid email provided: {{email}}", @@ -1875,7 +1878,6 @@ "error.WORKSPACE_MEMBERS_EXCEED_LIMIT_TO_DOWNGRADE": "You cannot downgrade the workspace from team workspace because there are more than {{limit}} members that are currently active.", "error.UNSUPPORTED_CLIENT_VERSION": "Unsupported client with version [{{clientVersion}}], required version is [{{requiredVersion}}].", "error.NOTIFICATION_NOT_FOUND": "Notification not found.", - "error.MENTION_USER_SPACE_ACCESS_DENIED": "Mention user do not have permission to access space {{spaceId}}.", - "error.MENTION_USER_ONESELF_DENIED": "You cannot mention yourself.", - "error.NOTIFICATION_ACCESS_DENIED": "You do not have permission to access notification {{notificationId}}." + "error.MENTION_USER_DOC_ACCESS_DENIED": "Mentioned user can not access doc {{docId}}.", + "error.MENTION_USER_ONESELF_DENIED": "You can not mention yourself." }