205 lines
5.7 KiB
TypeScript
205 lines
5.7 KiB
TypeScript
import { useState, useEffect, useCallback } from 'react';
|
|
import workflowService, {
|
|
type WorkflowTransition,
|
|
type WorkflowInfo,
|
|
getWorkflowStateStyle,
|
|
getActionButtonStyle,
|
|
getActionIcon
|
|
} from '../services/workflowService';
|
|
|
|
interface UseWorkflowOptions {
|
|
doctype: string;
|
|
docname: string | null;
|
|
workflowState?: string;
|
|
enabled?: boolean;
|
|
docData?: Record<string, any>; // Added: Document data for condition evaluation
|
|
}
|
|
|
|
interface UseWorkflowReturn {
|
|
// State
|
|
transitions: WorkflowTransition[];
|
|
workflowInfo: WorkflowInfo | null;
|
|
userRoles: string[];
|
|
currentUser: string;
|
|
isSystemManager: boolean;
|
|
loading: boolean;
|
|
actionLoading: boolean;
|
|
error: string | null;
|
|
canEdit: boolean;
|
|
|
|
// Actions
|
|
applyAction: (action: string, nextState?: string) => Promise<boolean>;
|
|
refreshTransitions: () => Promise<void>;
|
|
|
|
// Helpers
|
|
getStateStyle: (state: string) => { bg: string; text: string; border: string };
|
|
getButtonStyle: (action: string) => string;
|
|
getIcon: (action: string) => string;
|
|
}
|
|
|
|
export const useWorkflow = ({
|
|
doctype,
|
|
docname,
|
|
workflowState,
|
|
enabled = true,
|
|
docData, // Added: Document data for condition evaluation
|
|
}: UseWorkflowOptions): UseWorkflowReturn => {
|
|
const [transitions, setTransitions] = useState<WorkflowTransition[]>([]);
|
|
const [workflowInfo, setWorkflowInfo] = useState<WorkflowInfo | null>(null);
|
|
const [userRoles, setUserRoles] = useState<string[]>([]);
|
|
const [currentUser, setCurrentUser] = useState<string>('');
|
|
const [isSystemManagerUser, setIsSystemManagerUser] = useState(false);
|
|
const [loading, setLoading] = useState(false);
|
|
const [actionLoading, setActionLoading] = useState(false);
|
|
const [error, setError] = useState<string | null>(null);
|
|
const [canEdit, setCanEdit] = useState(true);
|
|
|
|
// Fetch workflow info on mount
|
|
useEffect(() => {
|
|
if (!enabled) return;
|
|
|
|
const fetchWorkflowInfo = async () => {
|
|
try {
|
|
const info = await workflowService.getWorkflowInfo(doctype);
|
|
setWorkflowInfo(info);
|
|
} catch (err) {
|
|
console.error('Error fetching workflow info:', err);
|
|
}
|
|
};
|
|
|
|
fetchWorkflowInfo();
|
|
}, [doctype, enabled]);
|
|
|
|
// Fetch user roles, current user, and check System Manager
|
|
useEffect(() => {
|
|
if (!enabled) return;
|
|
|
|
const fetchUserInfo = async () => {
|
|
try {
|
|
const [roles, user, isSysManager] = await Promise.all([
|
|
workflowService.getCurrentUserRoles(),
|
|
workflowService.getCurrentUser(),
|
|
workflowService.isSystemManager(),
|
|
]);
|
|
setUserRoles(roles);
|
|
setCurrentUser(user);
|
|
setIsSystemManagerUser(isSysManager);
|
|
|
|
// System Manager can always edit
|
|
if (isSysManager) {
|
|
setCanEdit(true);
|
|
}
|
|
} catch (err) {
|
|
console.error('Error fetching user info:', err);
|
|
}
|
|
};
|
|
|
|
fetchUserInfo();
|
|
}, [enabled]);
|
|
|
|
// Fetch available transitions when docname, workflowState, or docData changes
|
|
const refreshTransitions = useCallback(async () => {
|
|
if (!docname || !enabled) {
|
|
setTransitions([]);
|
|
return;
|
|
}
|
|
|
|
setLoading(true);
|
|
setError(null);
|
|
|
|
try {
|
|
// Pass document data for condition evaluation
|
|
const availableTransitions = await workflowService.getWorkflowTransitions(
|
|
doctype,
|
|
docname,
|
|
workflowState,
|
|
docData // Pass document data
|
|
);
|
|
|
|
console.log('[useWorkflow] Available transitions:', availableTransitions);
|
|
setTransitions(availableTransitions);
|
|
|
|
// Check if user can edit (System Manager always can)
|
|
if (workflowState) {
|
|
const canUserEdit = await workflowService.canUserEditDocument(doctype, docname, workflowState);
|
|
setCanEdit(canUserEdit);
|
|
}
|
|
} catch (err) {
|
|
console.error('Error fetching transitions:', err);
|
|
setError('Failed to load workflow actions');
|
|
setTransitions([]);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
}, [doctype, docname, workflowState, enabled, docData]);
|
|
|
|
useEffect(() => {
|
|
refreshTransitions();
|
|
}, [refreshTransitions]);
|
|
|
|
// Apply workflow action
|
|
const applyAction = useCallback(async (action: string, nextState?: string): Promise<boolean> => {
|
|
if (!docname) {
|
|
setError('Document not saved yet');
|
|
return false;
|
|
}
|
|
|
|
setActionLoading(true);
|
|
setError(null);
|
|
|
|
try {
|
|
// Pass nextState for System Manager force update if needed
|
|
await workflowService.applyWorkflowAction(doctype, docname, action, nextState);
|
|
|
|
// Refresh transitions after action
|
|
await refreshTransitions();
|
|
|
|
return true;
|
|
} catch (err: any) {
|
|
console.error('Error applying workflow action:', err);
|
|
|
|
// Extract error message
|
|
let errorMessage = 'Failed to apply action';
|
|
if (err.message) {
|
|
errorMessage = err.message;
|
|
} else if (err._server_messages) {
|
|
try {
|
|
const serverMessages = JSON.parse(err._server_messages);
|
|
errorMessage = serverMessages.map((m: string) => {
|
|
try {
|
|
return JSON.parse(m).message;
|
|
} catch {
|
|
return m;
|
|
}
|
|
}).join('\n');
|
|
} catch {
|
|
errorMessage = err._server_messages;
|
|
}
|
|
}
|
|
|
|
setError(errorMessage);
|
|
return false;
|
|
} finally {
|
|
setActionLoading(false);
|
|
}
|
|
}, [doctype, docname, refreshTransitions]);
|
|
|
|
return {
|
|
transitions,
|
|
workflowInfo,
|
|
userRoles,
|
|
currentUser,
|
|
isSystemManager: isSystemManagerUser,
|
|
loading,
|
|
actionLoading,
|
|
error,
|
|
canEdit,
|
|
applyAction,
|
|
refreshTransitions,
|
|
getStateStyle: getWorkflowStateStyle,
|
|
getButtonStyle: getActionButtonStyle,
|
|
getIcon: getActionIcon,
|
|
};
|
|
};
|
|
|
|
export default useWorkflow; |