Merge all role-dispatcher pages into single files; add FileBrowser with file-type icons
- DashboardPage, Projects, RequestsPage, ProjectDetailPage, RequestDetail: each now handles team/external/client in one file via role flags — removed 10 old role-specific sub-files - Layout: client Company nav link goes directly to /company/:id when user has a single company - FileBrowser: replace emoji icons with colored extension-text badges (square); folder icon stays 📁; Adobe/Figma/design-tool colors for design files - CompaniesPage: merged team Companies + client company routing (single-company redirect, multi-company list) - FileSharing: integrated FileBrowser component - Removed: seafile API + lib, old ServerStatus, TaskDetail, role-split page files Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
import { useState } from 'react';
|
||||
|
||||
export function useSortable(defaultKey = '', defaultDir = 'asc') {
|
||||
const [sortKey, setSortKey] = useState(defaultKey);
|
||||
const [sortDir, setSortDir] = useState(defaultDir);
|
||||
|
||||
const toggle = (key) => {
|
||||
if (sortKey === key) setSortDir(d => d === 'asc' ? 'desc' : 'asc');
|
||||
else { setSortKey(key); setSortDir('asc'); }
|
||||
};
|
||||
|
||||
const sort = (data, getVal) => {
|
||||
if (!sortKey) return data;
|
||||
return [...data].sort((a, b) => {
|
||||
const av = getVal ? getVal(a, sortKey) : a[sortKey];
|
||||
const bv = getVal ? getVal(b, sortKey) : b[sortKey];
|
||||
if (av == null && bv == null) return 0;
|
||||
if (av == null) return 1;
|
||||
if (bv == null) return -1;
|
||||
const cmp = typeof av === 'number' && typeof bv === 'number'
|
||||
? av - bv
|
||||
: String(av).localeCompare(String(bv), undefined, { numeric: true, sensitivity: 'base' });
|
||||
return sortDir === 'asc' ? cmp : -cmp;
|
||||
});
|
||||
};
|
||||
|
||||
return { sortKey, sortDir, toggle, sort };
|
||||
}
|
||||
Reference in New Issue
Block a user