Add module ordering feature with drag-and-drop
- Add modules_order setting in backend settings registry - Update ModulesContext with moduleOrder state and save functions - Create configuration tab in Features page with drag-and-drop reordering - Make feature tabs dynamic based on order (updates in real-time) - Sort sidebar modules based on saved order - Add order-cards CSS with vertical layout - Update API client to support string[] type for modules_order 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -18,12 +18,18 @@ export interface ModuleState {
|
||||
user: boolean;
|
||||
}
|
||||
|
||||
// Default order for modules
|
||||
const DEFAULT_MODULE_ORDER: string[] = ['feature1', 'feature2', 'feature3'];
|
||||
|
||||
interface ModulesContextType {
|
||||
moduleStates: Record<ModuleId, ModuleState>;
|
||||
moduleOrder: string[];
|
||||
isModuleEnabled: (moduleId: string) => boolean;
|
||||
isModuleEnabledForUser: (moduleId: string, userPermissions: UserPermissions | undefined, isSuperuser: boolean) => boolean;
|
||||
setModuleEnabled: (moduleId: ModuleId, type: 'admin' | 'user', enabled: boolean) => void;
|
||||
setModuleOrder: (order: string[]) => void;
|
||||
saveModulesToBackend: () => Promise<void>;
|
||||
saveModuleOrder: (order: string[]) => Promise<void>;
|
||||
isLoading: boolean;
|
||||
hasInitialized: boolean;
|
||||
}
|
||||
@@ -42,6 +48,7 @@ const getDefaultStates = (): Record<ModuleId, ModuleState> => {
|
||||
export function ModulesProvider({ children }: { children: ReactNode }) {
|
||||
const { token } = useAuth();
|
||||
const [moduleStates, setModuleStates] = useState<Record<ModuleId, ModuleState>>(getDefaultStates);
|
||||
const [moduleOrder, setModuleOrderState] = useState<string[]>(DEFAULT_MODULE_ORDER);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [hasInitialized, setHasInitialized] = useState(false);
|
||||
|
||||
@@ -88,6 +95,24 @@ export function ModulesProvider({ children }: { children: ReactNode }) {
|
||||
});
|
||||
|
||||
setModuleStates(newStates);
|
||||
|
||||
// Load module order
|
||||
if (settings.modules_order) {
|
||||
let order: string[];
|
||||
if (typeof settings.modules_order === 'string') {
|
||||
try {
|
||||
order = JSON.parse(settings.modules_order);
|
||||
} catch {
|
||||
order = DEFAULT_MODULE_ORDER;
|
||||
}
|
||||
} else if (Array.isArray(settings.modules_order)) {
|
||||
order = settings.modules_order;
|
||||
} else {
|
||||
order = DEFAULT_MODULE_ORDER;
|
||||
}
|
||||
setModuleOrderState(order);
|
||||
}
|
||||
|
||||
setHasInitialized(true);
|
||||
} catch (error) {
|
||||
console.error('Failed to load modules from backend:', error);
|
||||
@@ -112,6 +137,21 @@ export function ModulesProvider({ children }: { children: ReactNode }) {
|
||||
}
|
||||
}, [moduleStates]);
|
||||
|
||||
// Save module order to backend
|
||||
const saveModuleOrder = useCallback(async (order: string[]) => {
|
||||
try {
|
||||
await settingsAPI.updateModules({ modules_order: order });
|
||||
} catch (error) {
|
||||
console.error('Failed to save module order to backend:', error);
|
||||
throw error;
|
||||
}
|
||||
}, []);
|
||||
|
||||
// Set module order (local state only, call saveModuleOrder to persist)
|
||||
const setModuleOrder = useCallback((order: string[]) => {
|
||||
setModuleOrderState(order);
|
||||
}, []);
|
||||
|
||||
// Load modules when token becomes available
|
||||
useEffect(() => {
|
||||
if (token) {
|
||||
@@ -185,10 +225,13 @@ export function ModulesProvider({ children }: { children: ReactNode }) {
|
||||
<ModulesContext.Provider
|
||||
value={{
|
||||
moduleStates,
|
||||
moduleOrder,
|
||||
isModuleEnabled,
|
||||
isModuleEnabledForUser,
|
||||
setModuleEnabled,
|
||||
setModuleOrder,
|
||||
saveModulesToBackend,
|
||||
saveModuleOrder,
|
||||
isLoading,
|
||||
hasInitialized,
|
||||
}}
|
||||
|
||||
Reference in New Issue
Block a user