diff --git a/packages/frontend/core/src/components/admin-panel/admin-panel-header.tsx b/packages/frontend/core/src/components/admin-panel/admin-panel-header.tsx deleted file mode 100644 index 14b328816..000000000 --- a/packages/frontend/core/src/components/admin-panel/admin-panel-header.tsx +++ /dev/null @@ -1,76 +0,0 @@ -import { Button, useConfirmModal } from '@affine/component'; -import { useNavigateHelper } from '@affine/core/hooks/use-navigate-helper'; -import { ArrowRightBigIcon, Logo1Icon } from '@blocksuite/icons/rc'; -import { useCallback } from 'react'; - -import * as styles from './index.css'; - -export type ModifiedValues = { - id: string; - expiredValue: any; - newValue: any; -}; - -export const AdminPanelHeader = ({ - modifiedValues, - onConfirm, -}: { - modifiedValues: ModifiedValues[]; - onConfirm: () => void; -}) => { - const { openConfirmModal } = useConfirmModal(); - const { jumpToIndex } = useNavigateHelper(); - - const handleJumpToIndex = useCallback(() => jumpToIndex(), [jumpToIndex]); - - return ( -
- -
- After editing, please click the Save button on the right. - -
-
- -
-
- ); -}; diff --git a/packages/frontend/core/src/components/admin-panel/collapsible-item.tsx b/packages/frontend/core/src/components/admin-panel/collapsible-item.tsx deleted file mode 100644 index 452250688..000000000 --- a/packages/frontend/core/src/components/admin-panel/collapsible-item.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import { ArrowDownSmallIcon } from '@blocksuite/icons/rc'; -import * as Collapsible from '@radix-ui/react-collapsible'; -import { useCallback, useState } from 'react'; - -import * as styles from './index.css'; - -export const CollapsibleItem = ({ - items, - initialOpen = false, - title, - currentModule, - changeModule, -}: { - title: string; - items: string[]; - initialOpen?: boolean; - currentModule?: string; - changeModule?: (module: string) => void; -}) => { - const [open, setOpen] = useState(initialOpen); - - const handleClick = useCallback( - (id: string, event?: React.MouseEvent) => { - event?.preventDefault(); - const targetElement = document.getElementById(id); - if (targetElement) { - targetElement.scrollIntoView({ - behavior: 'smooth', - block: 'center', - }); - } - changeModule?.(title); - }, - [changeModule, title] - ); - - return ( - -
- - - - handleClick(title, e)} - > - {title} - -
- -
- {items.map((item, index) => ( - handleClick(item)} - > - - {item} - - - ))} -
-
- ); -}; diff --git a/packages/frontend/core/src/components/admin-panel/index.css.ts b/packages/frontend/core/src/components/admin-panel/index.css.ts deleted file mode 100644 index cba391e98..000000000 --- a/packages/frontend/core/src/components/admin-panel/index.css.ts +++ /dev/null @@ -1,179 +0,0 @@ -import { cssVar } from '@toeverything/theme'; -import { style } from '@vanilla-extract/css'; - -export const header = style({ - width: '100%', - display: 'flex', - justifyContent: 'space-between', - alignItems: 'center', - padding: '12px 16px', - borderBottom: `1px solid ${cssVar('borderColor')}`, - gap: 8, - position: 'sticky', - backgroundColor: cssVar('backgroundPrimaryColor'), - zIndex: 2, -}); - -export const logo = style({ - fontSize: 32, - cursor: 'pointer', -}); - -export const title = style({ - fontSize: cssVar('fontH3'), - fontWeight: 'bold', - display: 'flex', - alignItems: 'center', - gap: 8, - whiteSpace: 'pre-wrap', - textAlign: 'center', - justifyContent: 'center', -}); - -export const outLine = style({ - width: '100%', - display: 'flex', - flexDirection: 'column', - flexWrap: 'wrap', -}); - -export const outLineHeader = style({ - display: 'flex', - width: '100%', - cursor: 'pointer', - alignItems: 'center', - wordBreak: 'break-all', - wordWrap: 'break-word', - fontSize: cssVar('fontBase'), - fontWeight: 'bold', - textTransform: 'capitalize', - ':hover': { - backgroundColor: cssVar('hoverColor'), - }, - selectors: { - '&[data-active=true]': { - background: cssVar('hoverColor'), - }, - }, - color: cssVar('textPrimaryColor'), - borderRadius: 4, - padding: 4, - marginBottom: 4, - ':visited': { - color: cssVar('textPrimaryColor'), - }, -}); - -export const arrowIcon = style({ - transform: 'rotate(-90deg)', - selectors: { - '&[data-open=true]': { - transform: 'rotate(0deg) translateY(3px) translateX(-3px)', - }, - }, - transition: 'transform 0.2s', -}); - -export const collapsibleContainer = style({ - display: 'flex', - width: '100%', - flexDirection: 'column', - wordBreak: 'break-all', - wordWrap: 'break-word', -}); - -export const outLineContent = style({ - fontSize: cssVar('fontSm'), - cursor: 'pointer', - borderRadius: 4, - padding: '4px 18px', - color: cssVar('textPrimaryColor'), - textTransform: 'capitalize', - ':hover': { - backgroundColor: cssVar('hoverColor'), - }, -}); - -export const navText = style({ - width: '100%', - color: cssVar('textPrimaryColor'), - padding: '4px 0px', - ':visited': { - color: cssVar('textPrimaryColor'), - }, -}); - -export const settingItem = style({ - maxWidth: '960px', - minHeight: '100px', - display: 'flex', - gap: 8, - padding: '16px 0', - borderBottom: `0.5px solid ${cssVar('borderColor')}`, -}); - -export const LeftItem = style({ - display: 'flex', - flexDirection: 'column', - gap: 4, - width: '60%', -}); - -export const RightItem = style({ - display: 'flex', - flexDirection: 'column', - gap: 8, - fontSize: cssVar('fontXs'), - color: cssVar('textPrimaryColor'), - width: '40%', - alignItems: 'flex-end', -}); - -export const settingItemTitle = style({ - fontSize: cssVar('fontBase'), - fontWeight: 'bold', - wordBreak: 'break-all', - wordWrap: 'break-word', - marginBottom: 6, -}); - -export const settingItemId = style({ - fontSize: cssVar('fontXs'), - color: cssVar('textSecondaryColor'), - borderRadius: 4, - borderColor: cssVar('borderColor'), - backgroundColor: cssVar('backgroundSecondaryColor'), - padding: '2px 4px', - wordBreak: 'keep-all', -}); - -export const settingItemDescription = style({ - fontSize: cssVar('fontXs'), - color: cssVar('textSecondaryColor'), -}); - -export const changedValues = style({ - background: cssVar('backgroundSecondaryColor'), - display: 'flex', - flexDirection: 'column', - gap: 4, - minHeight: '64px', - borderRadius: 4, - padding: '12px 16px 16px 12px', - marginTop: 8, - overflow: 'hidden', - color: cssVar('textPrimaryColor'), - fontSize: cssVar('fontSm'), -}); - -export const expiredValue = style({ - color: cssVar('textHighlightForegroundRed'), - background: cssVar('textHighlightRed'), - textDecoration: 'line-through', - marginRight: 2, -}); - -export const newValue = style({ - color: cssVar('textHighlightForegroundGreen'), - background: cssVar('textHighlightGreen'), -}); diff --git a/packages/frontend/core/src/components/admin-panel/index.ts b/packages/frontend/core/src/components/admin-panel/index.ts deleted file mode 100644 index e85d9a3f4..000000000 --- a/packages/frontend/core/src/components/admin-panel/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export * from './admin-panel-header'; -export * from './collapsible-item'; -export * from './runtime-setting-row'; -export * from './use-get-server-runtime-config'; -export * from './utils'; diff --git a/packages/frontend/core/src/components/admin-panel/runtime-setting-row.tsx b/packages/frontend/core/src/components/admin-panel/runtime-setting-row.tsx deleted file mode 100644 index 17299bb88..000000000 --- a/packages/frontend/core/src/components/admin-panel/runtime-setting-row.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import { type ReactNode } from 'react'; - -import * as styles from './index.css'; - -export const RuntimeSettingRow = ({ - id, - description, - lastUpdatedTime, - operation, - children, -}: { - id: string; - description: string; - lastUpdatedTime: string; - operation: ReactNode; - children: ReactNode; -}) => { - const formatTime = new Date(lastUpdatedTime).toLocaleString(); - return ( -
-
-
{description}
-
- {id} -
-
- last updated at: {formatTime} -
-
-
- {operation} - {children} -
-
- ); -}; diff --git a/packages/frontend/core/src/components/admin-panel/use-get-server-runtime-config.ts b/packages/frontend/core/src/components/admin-panel/use-get-server-runtime-config.ts deleted file mode 100644 index 333d3604f..000000000 --- a/packages/frontend/core/src/components/admin-panel/use-get-server-runtime-config.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { useQuery } from '@affine/core/hooks/use-query'; -import { getServerRuntimeConfigQuery } from '@affine/graphql'; -import { useMemo } from 'react'; - -export const useGetServerRuntimeConfig = () => { - const { data } = useQuery({ - query: getServerRuntimeConfigQuery, - }); - - const serverRuntimeConfig = useMemo( - () => - data?.serverRuntimeConfig.sort((a, b) => a.id.localeCompare(b.id)) ?? [], - [data] - ); - - // collect all the modules and config keys in each module - const moduleList = useMemo(() => { - const moduleMap: { [key: string]: string[] } = {}; - - serverRuntimeConfig.forEach(config => { - if (!moduleMap[config.module]) { - moduleMap[config.module] = []; - } - moduleMap[config.module].push(config.key); - }); - - return Object.keys(moduleMap) - .sort((a, b) => a.localeCompare(b)) - .map(moduleName => ({ - moduleName, - keys: moduleMap[moduleName].sort((a, b) => a.localeCompare(b)), - })); - }, [serverRuntimeConfig]); - - // group config by module name - const configGroup = useMemo(() => { - const configMap = new Map(); - - serverRuntimeConfig.forEach(config => { - if (!configMap.has(config.module)) { - configMap.set(config.module, []); - } - configMap.get(config.module)?.push(config); - }); - - return Array.from(configMap.entries()).map(([moduleName, configs]) => ({ - moduleName, - configs, - })); - }, [serverRuntimeConfig]); - - return { - serverRuntimeConfig, - moduleList, - configGroup, - }; -}; diff --git a/packages/frontend/core/src/components/admin-panel/use-update-server-runtime-config.ts b/packages/frontend/core/src/components/admin-panel/use-update-server-runtime-config.ts deleted file mode 100644 index ac3ee7f81..000000000 --- a/packages/frontend/core/src/components/admin-panel/use-update-server-runtime-config.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { notify } from '@affine/component'; -import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks'; -import { - useMutateQueryResource, - useMutation, -} from '@affine/core/hooks/use-mutation'; -import { - getServerRuntimeConfigQuery, - updateServerRuntimeConfigsMutation, -} from '@affine/graphql'; - -export const useUpdateServerRuntimeConfigs = () => { - const { trigger, isMutating } = useMutation({ - mutation: updateServerRuntimeConfigsMutation, - }); - const revalidate = useMutateQueryResource(); - - return { - trigger: useAsyncCallback( - async (values: any) => { - try { - await trigger(values); - await revalidate(getServerRuntimeConfigQuery); - notify.success({ - title: 'Saved successfully', - message: 'Runtime configurations have been saved successfully.', - }); - } catch (e) { - notify.error({ - title: 'Failed to save', - message: - 'Failed to save runtime configurations, please try again later.', - }); - console.error(e); - } - }, - [revalidate, trigger] - ), - isMutating, - }; -}; diff --git a/packages/frontend/core/src/components/admin-panel/utils.tsx b/packages/frontend/core/src/components/admin-panel/utils.tsx deleted file mode 100644 index 4a30d115f..000000000 --- a/packages/frontend/core/src/components/admin-panel/utils.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import { Input, Switch } from '@affine/component'; -import type { RuntimeConfigType } from '@affine/graphql'; - -export const renderInput = ( - type: RuntimeConfigType, - value: any, - onChange: (value?: any) => void -) => { - switch (type) { - case 'Boolean': - return ; - case 'String': - return ( - - ); - case 'Number': - return ( -
- -
- ); - // TODO(@JimmFly): add more types - default: - return null; - } -}; - -export const isEqual = (a: any, b: any) => { - if (typeof a !== typeof b) return false; - if (typeof a === 'object') return JSON.stringify(a) === JSON.stringify(b); - return a === b; -}; - -export const formatValue = (value: any) => { - if (typeof value === 'object') return JSON.stringify(value); - return value.toString(); -}; - -export const formatValueForInput = (value: any, type: RuntimeConfigType) => { - let newValue = null; - switch (type) { - case 'Boolean': - newValue = !!value; - break; - case 'String': - newValue = value; - break; - case 'Number': - newValue = Number(value); - break; - case 'Array': - newValue = value.split(','); - break; - case 'Object': - newValue = JSON.parse(value); - break; - default: - break; - } - return newValue; -}; diff --git a/packages/frontend/core/src/pages/admin-panel.css.ts b/packages/frontend/core/src/pages/admin-panel.css.ts deleted file mode 100644 index da9b483bb..000000000 --- a/packages/frontend/core/src/pages/admin-panel.css.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { cssVar } from '@toeverything/theme'; -import { style } from '@vanilla-extract/css'; -export const root = style({ - height: '100vh', - width: '100vw', - display: 'flex', - flexDirection: 'column', - fontSize: cssVar('fontBase'), - position: 'relative', -}); - -export const container = style({ - display: 'flex', - width: '100%', - height: '100%', -}); - -export const sideBar = style({ - width: '300px', - display: 'flex', - flexDirection: 'column', - borderRight: `1px solid ${cssVar('borderColor')}`, - padding: '12px 8px', - height: '100%', - background: cssVar('backgroundPrimaryColor'), - zIndex: 3, -}); - -export const scrollArea = style({ - padding: '24px 0 160px', - background: cssVar('backgroundPrimaryColor'), -}); - -export const main = style({ - display: 'flex', - width: '100%', - flexDirection: 'column', - overflow: 'auto', - alignItems: 'center', -}); - -export const moduleContainer = style({ - width: '100%', - display: 'flex', - flexDirection: 'column', - padding: '16px', - maxWidth: '960px', - margin: 'auto', - gap: 16, -}); - -export const module = style({ - fontSize: cssVar('fontH5'), - fontWeight: 'bold', - marginBottom: 8, - textTransform: 'capitalize', - padding: '16px 0', - borderBottom: `0.5px solid ${cssVar('borderColor')}`, -}); diff --git a/packages/frontend/core/src/pages/admin-panel.tsx b/packages/frontend/core/src/pages/admin-panel.tsx deleted file mode 100644 index 218790491..000000000 --- a/packages/frontend/core/src/pages/admin-panel.tsx +++ /dev/null @@ -1,162 +0,0 @@ -import { Scrollable } from '@affine/component'; -import type { RuntimeConfigType } from '@affine/graphql'; -import { FeatureType, getUserFeaturesQuery } from '@affine/graphql'; -import { useCallback, useMemo, useState } from 'react'; - -import { - AdminPanelHeader, - CollapsibleItem, - formatValue, - formatValueForInput, - isEqual, - type ModifiedValues, - renderInput, - RuntimeSettingRow, - useGetServerRuntimeConfig, -} from '../components/admin-panel'; -import { useUpdateServerRuntimeConfigs } from '../components/admin-panel/use-update-server-runtime-config'; -import { useNavigateHelper } from '../hooks/use-navigate-helper'; -import { useQuery } from '../hooks/use-query'; -import * as styles from './admin-panel.css'; - -export const AdminPanel = () => { - const { serverRuntimeConfig, moduleList, configGroup } = - useGetServerRuntimeConfig(); - - const [currentModule, setCurrentModule] = useState( - moduleList[0].moduleName - ); - - const { trigger } = useUpdateServerRuntimeConfigs(); - - const [configValues, setConfigValues] = useState( - serverRuntimeConfig.reduce( - (acc, config) => { - acc[config.id] = config.value; - return acc; - }, - {} as Record - ) - ); - - const handleInputChange = useCallback( - (key: string, value: any, type: RuntimeConfigType) => { - const newValue = formatValueForInput(value, type); - setConfigValues(prevValues => ({ - ...prevValues, - [key]: newValue, - })); - }, - [] - ); - - const modifiedValues: ModifiedValues[] = useMemo(() => { - return serverRuntimeConfig - .filter(config => !isEqual(config.value, configValues[config.id])) - .map(config => ({ - id: config.id, - key: config.key, - expiredValue: config.value, - newValue: configValues[config.id], - })); - }, [configValues, serverRuntimeConfig]); - - const handleSave = useCallback(() => { - // post value example: { "key1": "newValue1","key2": "newValue2"} - const updates: Record = {}; - - modifiedValues.forEach(item => { - if (item.id && item.newValue !== undefined) { - updates[item.id] = item.newValue; - } - }); - trigger({ updates }); - }, [modifiedValues, trigger]); - - return ( -
-
-
- {moduleList.map(module => ( - - ))} -
-
- - - -
- {configGroup - .filter(group => group.moduleName === currentModule) - .map(group => { - const { moduleName, configs } = group; - return ( -
-
{moduleName}
- {configs?.map(config => { - const { id, type, description, updatedAt } = config; - const isValueEqual = isEqual( - config.value, - configValues[id] - ); - const formatServerValue = formatValue(config.value); - const formatCurrentValue = formatValue( - configValues[id] - ); - return ( - handleInputChange(id, value, type) - )} - > -
- {formatServerValue} => {formatCurrentValue} -
-
- ); - })} -
- ); - })} -
-
- -
-
-
-
- ); -}; - -export const Component = () => { - const { data } = useQuery({ - query: getUserFeaturesQuery, - }); - const { jumpTo404 } = useNavigateHelper(); - const userFeatures = data?.currentUser?.features; - if (!userFeatures || !userFeatures.includes(FeatureType.Admin)) { - jumpTo404(); - return null; - } - - return ; -}; diff --git a/packages/frontend/core/src/router.tsx b/packages/frontend/core/src/router.tsx index f60d4e9c4..37fe7a716 100644 --- a/packages/frontend/core/src/router.tsx +++ b/packages/frontend/core/src/router.tsx @@ -50,10 +50,6 @@ export const topLevelRoutes = [ path: '/404', lazy: () => import('./pages/404'), }, - { - path: '/admin-panel', - lazy: () => import('./pages/admin-panel'), - }, { path: '/expired', lazy: () => import('./pages/expired'),