🎨 chore(web): apply ESLint and Prettier auto-fixes (baseline)
- Ran: bun run eslint:fix && bun run lint:fix - Inserted AGPL license header via eslint-plugin-header - Enforced no-multiple-empty-lines and other lint rules - Formatted code using Prettier v3 (@so1ve/prettier-config) - No functional changes; formatting-only baseline across JS/JSX files
This commit is contained in:
@@ -31,7 +31,7 @@ export const useContainerWidth = () => {
|
||||
const element = ref.current;
|
||||
if (!element) return;
|
||||
|
||||
const resizeObserver = new ResizeObserver(entries => {
|
||||
const resizeObserver = new ResizeObserver((entries) => {
|
||||
for (let entry of entries) {
|
||||
const { width: newWidth } = entry.contentRect;
|
||||
setWidth(newWidth);
|
||||
|
||||
@@ -109,16 +109,25 @@ export const useHeaderBar = ({ onMobileMenuToggle, drawerOpen }) => {
|
||||
navigate('/login');
|
||||
}, [navigate, t, userDispatch]);
|
||||
|
||||
const handleLanguageChange = useCallback((lang) => {
|
||||
i18n.changeLanguage(lang);
|
||||
}, [i18n]);
|
||||
const handleLanguageChange = useCallback(
|
||||
(lang) => {
|
||||
i18n.changeLanguage(lang);
|
||||
},
|
||||
[i18n],
|
||||
);
|
||||
|
||||
const handleThemeToggle = useCallback((newTheme) => {
|
||||
if (!newTheme || (newTheme !== 'light' && newTheme !== 'dark' && newTheme !== 'auto')) {
|
||||
return;
|
||||
}
|
||||
setTheme(newTheme);
|
||||
}, [setTheme]);
|
||||
const handleThemeToggle = useCallback(
|
||||
(newTheme) => {
|
||||
if (
|
||||
!newTheme ||
|
||||
(newTheme !== 'light' && newTheme !== 'dark' && newTheme !== 'auto')
|
||||
) {
|
||||
return;
|
||||
}
|
||||
setTheme(newTheme);
|
||||
},
|
||||
[setTheme],
|
||||
);
|
||||
|
||||
const handleMobileMenuToggle = useCallback(() => {
|
||||
if (isMobile) {
|
||||
|
||||
@@ -32,4 +32,4 @@ export const useIsMobile = () => {
|
||||
() => window.matchMedia(query).matches,
|
||||
() => false,
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
@@ -47,4 +47,4 @@ export const useMinimumLoadingTime = (loading, minimumTime = 1000) => {
|
||||
}, [loading, minimumTime]);
|
||||
|
||||
return showSkeleton;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -20,38 +20,41 @@ For commercial licensing, please contact support@quantumnous.com
|
||||
import { useMemo } from 'react';
|
||||
|
||||
export const useNavigation = (t, docsLink) => {
|
||||
const mainNavLinks = useMemo(() => [
|
||||
{
|
||||
text: t('首页'),
|
||||
itemKey: 'home',
|
||||
to: '/',
|
||||
},
|
||||
{
|
||||
text: t('控制台'),
|
||||
itemKey: 'console',
|
||||
to: '/console',
|
||||
},
|
||||
{
|
||||
text: t('模型广场'),
|
||||
itemKey: 'pricing',
|
||||
to: '/pricing',
|
||||
},
|
||||
...(docsLink
|
||||
? [
|
||||
{
|
||||
text: t('文档'),
|
||||
itemKey: 'docs',
|
||||
isExternal: true,
|
||||
externalLink: docsLink,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
{
|
||||
text: t('关于'),
|
||||
itemKey: 'about',
|
||||
to: '/about',
|
||||
},
|
||||
], [t, docsLink]);
|
||||
const mainNavLinks = useMemo(
|
||||
() => [
|
||||
{
|
||||
text: t('首页'),
|
||||
itemKey: 'home',
|
||||
to: '/',
|
||||
},
|
||||
{
|
||||
text: t('控制台'),
|
||||
itemKey: 'console',
|
||||
to: '/console',
|
||||
},
|
||||
{
|
||||
text: t('模型广场'),
|
||||
itemKey: 'pricing',
|
||||
to: '/pricing',
|
||||
},
|
||||
...(docsLink
|
||||
? [
|
||||
{
|
||||
text: t('文档'),
|
||||
itemKey: 'docs',
|
||||
isExternal: true,
|
||||
externalLink: docsLink,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
{
|
||||
text: t('关于'),
|
||||
itemKey: 'about',
|
||||
to: '/about',
|
||||
},
|
||||
],
|
||||
[t, docsLink],
|
||||
);
|
||||
|
||||
return {
|
||||
mainNavLinks,
|
||||
|
||||
@@ -26,7 +26,8 @@ export const useNotifications = (statusState) => {
|
||||
const announcements = statusState?.status?.announcements || [];
|
||||
|
||||
// Helper functions
|
||||
const getAnnouncementKey = (a) => `${a?.publishDate || ''}-${(a?.content || '').slice(0, 30)}`;
|
||||
const getAnnouncementKey = (a) =>
|
||||
`${a?.publishDate || ''}-${(a?.content || '').slice(0, 30)}`;
|
||||
|
||||
const calculateUnreadCount = () => {
|
||||
if (!announcements.length) return 0;
|
||||
@@ -37,7 +38,8 @@ export const useNotifications = (statusState) => {
|
||||
readKeys = [];
|
||||
}
|
||||
const readSet = new Set(readKeys);
|
||||
return announcements.filter((a) => !readSet.has(getAnnouncementKey(a))).length;
|
||||
return announcements.filter((a) => !readSet.has(getAnnouncementKey(a)))
|
||||
.length;
|
||||
};
|
||||
|
||||
const getUnreadKeys = () => {
|
||||
@@ -49,7 +51,9 @@ export const useNotifications = (statusState) => {
|
||||
readKeys = [];
|
||||
}
|
||||
const readSet = new Set(readKeys);
|
||||
return announcements.filter((a) => !readSet.has(getAnnouncementKey(a))).map(getAnnouncementKey);
|
||||
return announcements
|
||||
.filter((a) => !readSet.has(getAnnouncementKey(a)))
|
||||
.map(getAnnouncementKey);
|
||||
};
|
||||
|
||||
// Effects
|
||||
@@ -71,7 +75,9 @@ export const useNotifications = (statusState) => {
|
||||
} catch (_) {
|
||||
readKeys = [];
|
||||
}
|
||||
const mergedKeys = Array.from(new Set([...readKeys, ...announcements.map(getAnnouncementKey)]));
|
||||
const mergedKeys = Array.from(
|
||||
new Set([...readKeys, ...announcements.map(getAnnouncementKey)]),
|
||||
);
|
||||
localStorage.setItem('notice_read_keys', JSON.stringify(mergedKeys));
|
||||
}
|
||||
setUnreadCount(0);
|
||||
|
||||
@@ -22,10 +22,12 @@ import { useState, useCallback } from 'react';
|
||||
const KEY = 'default_collapse_sidebar';
|
||||
|
||||
export const useSidebarCollapsed = () => {
|
||||
const [collapsed, setCollapsed] = useState(() => localStorage.getItem(KEY) === 'true');
|
||||
const [collapsed, setCollapsed] = useState(
|
||||
() => localStorage.getItem(KEY) === 'true',
|
||||
);
|
||||
|
||||
const toggle = useCallback(() => {
|
||||
setCollapsed(prev => {
|
||||
setCollapsed((prev) => {
|
||||
const next = !prev;
|
||||
localStorage.setItem(KEY, next.toString());
|
||||
return next;
|
||||
@@ -38,4 +40,4 @@ export const useSidebarCollapsed = () => {
|
||||
}, []);
|
||||
|
||||
return [collapsed, toggle, set];
|
||||
};
|
||||
};
|
||||
|
||||
@@ -27,27 +27,32 @@ import { TABLE_COMPACT_MODES_KEY } from '../../constants';
|
||||
* 内部使用 localStorage 保存状态,并监听 storage 事件保持多标签页同步。
|
||||
*/
|
||||
export function useTableCompactMode(tableKey = 'global') {
|
||||
const [compactMode, setCompactModeState] = useState(() => getTableCompactMode(tableKey));
|
||||
const [compactMode, setCompactModeState] = useState(() =>
|
||||
getTableCompactMode(tableKey),
|
||||
);
|
||||
|
||||
const setCompactMode = useCallback((value) => {
|
||||
setCompactModeState(value);
|
||||
setTableCompactMode(value, tableKey);
|
||||
}, [tableKey]);
|
||||
const setCompactMode = useCallback(
|
||||
(value) => {
|
||||
setCompactModeState(value);
|
||||
setTableCompactMode(value, tableKey);
|
||||
},
|
||||
[tableKey],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const handleStorage = (e) => {
|
||||
if (e.key === TABLE_COMPACT_MODES_KEY) {
|
||||
try {
|
||||
const modes = JSON.parse(e.newValue || '{}');
|
||||
setCompactModeState(!!modes[tableKey]);
|
||||
} catch {
|
||||
// ignore parse error
|
||||
}
|
||||
}
|
||||
};
|
||||
window.addEventListener('storage', handleStorage);
|
||||
return () => window.removeEventListener('storage', handleStorage);
|
||||
}, [tableKey]);
|
||||
useEffect(() => {
|
||||
const handleStorage = (e) => {
|
||||
if (e.key === TABLE_COMPACT_MODES_KEY) {
|
||||
try {
|
||||
const modes = JSON.parse(e.newValue || '{}');
|
||||
setCompactModeState(!!modes[tableKey]);
|
||||
} catch {
|
||||
// ignore parse error
|
||||
}
|
||||
}
|
||||
};
|
||||
window.addEventListener('storage', handleStorage);
|
||||
return () => window.removeEventListener('storage', handleStorage);
|
||||
}, [tableKey]);
|
||||
|
||||
return [compactMode, setCompactMode];
|
||||
}
|
||||
return [compactMode, setCompactMode];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user