mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-03 08:53:36 +00:00
refactor(ui): migrate settings view to React Query
- Migrate use-cursor-permissions to query and mutation hooks - Migrate use-cursor-status to React Query - Migrate use-skills-settings to useUpdateGlobalSettings mutation - Migrate use-subagents-settings to mutation hooks - Migrate use-subagents to useDiscoveredAgents query - Migrate opencode-settings-tab to React Query hooks - Migrate worktrees-section to query hooks - Migrate codex/claude usage sections to query hooks - Remove manual useState for loading/error states Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,103 +1,52 @@
|
||||
import { useState, useCallback } from 'react';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
import { toast } from 'sonner';
|
||||
import { useState, useCallback, useEffect } from 'react';
|
||||
import { useCursorPermissionsQuery, type CursorPermissionsData } from '@/hooks/queries';
|
||||
import { useApplyCursorProfile, useCopyCursorConfig } from '@/hooks/mutations';
|
||||
|
||||
const logger = createLogger('CursorPermissions');
|
||||
import { getHttpApiClient } from '@/lib/http-api-client';
|
||||
import type { CursorPermissionProfile } from '@automaker/types';
|
||||
|
||||
export interface PermissionsData {
|
||||
activeProfile: CursorPermissionProfile | null;
|
||||
effectivePermissions: { allow: string[]; deny: string[] } | null;
|
||||
hasProjectConfig: boolean;
|
||||
availableProfiles: Array<{
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
permissions: { allow: string[]; deny: string[] };
|
||||
}>;
|
||||
}
|
||||
// Re-export for backward compatibility
|
||||
export type PermissionsData = CursorPermissionsData;
|
||||
|
||||
/**
|
||||
* Custom hook for managing Cursor CLI permissions
|
||||
* Handles loading permissions data, applying profiles, and copying configs
|
||||
*/
|
||||
export function useCursorPermissions(projectPath?: string) {
|
||||
const [permissions, setPermissions] = useState<PermissionsData | null>(null);
|
||||
const [isLoadingPermissions, setIsLoadingPermissions] = useState(false);
|
||||
const [isSavingPermissions, setIsSavingPermissions] = useState(false);
|
||||
const [copiedConfig, setCopiedConfig] = useState(false);
|
||||
|
||||
// Load permissions data
|
||||
const loadPermissions = useCallback(async () => {
|
||||
setIsLoadingPermissions(true);
|
||||
try {
|
||||
const api = getHttpApiClient();
|
||||
const result = await api.setup.getCursorPermissions(projectPath);
|
||||
|
||||
if (result.success) {
|
||||
setPermissions({
|
||||
activeProfile: result.activeProfile || null,
|
||||
effectivePermissions: result.effectivePermissions || null,
|
||||
hasProjectConfig: result.hasProjectConfig || false,
|
||||
availableProfiles: result.availableProfiles || [],
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Failed to load Cursor permissions:', error);
|
||||
} finally {
|
||||
setIsLoadingPermissions(false);
|
||||
}
|
||||
}, [projectPath]);
|
||||
// React Query hooks
|
||||
const permissionsQuery = useCursorPermissionsQuery(projectPath);
|
||||
const applyProfileMutation = useApplyCursorProfile(projectPath);
|
||||
const copyConfigMutation = useCopyCursorConfig();
|
||||
|
||||
// Apply a permission profile
|
||||
const applyProfile = useCallback(
|
||||
async (profileId: 'strict' | 'development', scope: 'global' | 'project') => {
|
||||
setIsSavingPermissions(true);
|
||||
try {
|
||||
const api = getHttpApiClient();
|
||||
const result = await api.setup.applyCursorPermissionProfile(
|
||||
profileId,
|
||||
scope,
|
||||
scope === 'project' ? projectPath : undefined
|
||||
);
|
||||
|
||||
if (result.success) {
|
||||
toast.success(result.message || `Applied ${profileId} profile`);
|
||||
await loadPermissions();
|
||||
} else {
|
||||
toast.error(result.error || 'Failed to apply profile');
|
||||
}
|
||||
} catch (error) {
|
||||
toast.error('Failed to apply profile');
|
||||
} finally {
|
||||
setIsSavingPermissions(false);
|
||||
}
|
||||
(profileId: 'strict' | 'development', scope: 'global' | 'project') => {
|
||||
applyProfileMutation.mutate({ profileId, scope });
|
||||
},
|
||||
[projectPath, loadPermissions]
|
||||
[applyProfileMutation]
|
||||
);
|
||||
|
||||
// Copy example config to clipboard
|
||||
const copyConfig = useCallback(async (profileId: 'strict' | 'development') => {
|
||||
try {
|
||||
const api = getHttpApiClient();
|
||||
const result = await api.setup.getCursorExampleConfig(profileId);
|
||||
const copyConfig = useCallback(
|
||||
(profileId: 'strict' | 'development') => {
|
||||
copyConfigMutation.mutate(profileId, {
|
||||
onSuccess: () => {
|
||||
setCopiedConfig(true);
|
||||
setTimeout(() => setCopiedConfig(false), 2000);
|
||||
},
|
||||
});
|
||||
},
|
||||
[copyConfigMutation]
|
||||
);
|
||||
|
||||
if (result.success && result.config) {
|
||||
await navigator.clipboard.writeText(result.config);
|
||||
setCopiedConfig(true);
|
||||
toast.success('Config copied to clipboard');
|
||||
setTimeout(() => setCopiedConfig(false), 2000);
|
||||
}
|
||||
} catch (error) {
|
||||
toast.error('Failed to copy config');
|
||||
}
|
||||
}, []);
|
||||
// Load permissions (refetch)
|
||||
const loadPermissions = useCallback(() => {
|
||||
permissionsQuery.refetch();
|
||||
}, [permissionsQuery]);
|
||||
|
||||
return {
|
||||
permissions,
|
||||
isLoadingPermissions,
|
||||
isSavingPermissions,
|
||||
permissions: permissionsQuery.data ?? null,
|
||||
isLoadingPermissions: permissionsQuery.isLoading,
|
||||
isSavingPermissions: applyProfileMutation.isPending,
|
||||
copiedConfig,
|
||||
loadPermissions,
|
||||
applyProfile,
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
const logger = createLogger('CursorStatus');
|
||||
import { getHttpApiClient } from '@/lib/http-api-client';
|
||||
import { useEffect, useMemo, useCallback } from 'react';
|
||||
import { useCursorCliStatus } from '@/hooks/queries';
|
||||
import { useSetupStore } from '@/store/setup-store';
|
||||
|
||||
export interface CursorStatus {
|
||||
@@ -15,52 +11,42 @@ export interface CursorStatus {
|
||||
|
||||
/**
|
||||
* Custom hook for managing Cursor CLI status
|
||||
* Handles checking CLI installation, authentication, and refresh functionality
|
||||
* Uses React Query for data fetching with automatic caching.
|
||||
*/
|
||||
export function useCursorStatus() {
|
||||
const { setCursorCliStatus } = useSetupStore();
|
||||
const { data: result, isLoading, refetch } = useCursorCliStatus();
|
||||
|
||||
const [status, setStatus] = useState<CursorStatus | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
|
||||
const loadData = useCallback(async () => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
const api = getHttpApiClient();
|
||||
const statusResult = await api.setup.getCursorStatus();
|
||||
|
||||
if (statusResult.success) {
|
||||
const newStatus = {
|
||||
installed: statusResult.installed ?? false,
|
||||
version: statusResult.version ?? undefined,
|
||||
authenticated: statusResult.auth?.authenticated ?? false,
|
||||
method: statusResult.auth?.method,
|
||||
};
|
||||
setStatus(newStatus);
|
||||
|
||||
// Also update the global setup store so other components can access the status
|
||||
setCursorCliStatus({
|
||||
installed: newStatus.installed,
|
||||
version: newStatus.version,
|
||||
auth: newStatus.authenticated
|
||||
? {
|
||||
authenticated: true,
|
||||
method: newStatus.method || 'unknown',
|
||||
}
|
||||
: undefined,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Failed to load Cursor settings:', error);
|
||||
toast.error('Failed to load Cursor settings');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [setCursorCliStatus]);
|
||||
// Transform the API result into the local CursorStatus shape
|
||||
const status = useMemo((): CursorStatus | null => {
|
||||
if (!result) return null;
|
||||
return {
|
||||
installed: result.installed ?? false,
|
||||
version: result.version ?? undefined,
|
||||
authenticated: result.auth?.authenticated ?? false,
|
||||
method: result.auth?.method,
|
||||
};
|
||||
}, [result]);
|
||||
|
||||
// Keep the global setup store in sync with query data
|
||||
useEffect(() => {
|
||||
loadData();
|
||||
}, [loadData]);
|
||||
if (status) {
|
||||
setCursorCliStatus({
|
||||
installed: status.installed,
|
||||
version: status.version,
|
||||
auth: status.authenticated
|
||||
? {
|
||||
authenticated: true,
|
||||
method: status.method || 'unknown',
|
||||
}
|
||||
: undefined,
|
||||
});
|
||||
}
|
||||
}, [status, setCursorCliStatus]);
|
||||
|
||||
const loadData = useCallback(() => {
|
||||
refetch();
|
||||
}, [refetch]);
|
||||
|
||||
return {
|
||||
status,
|
||||
|
||||
Reference in New Issue
Block a user