import React, { createContext, useContext, useReducer } from 'react'; import { NotifAlert } from '../components/Global/ToastNotif'; // Initial state const initialState = { brandId: null, routeBrandId: null, errorCodeId: null, currentStep: 1, brandInfo: { brand_name: '', brand_type: '', brand_manufacture: '', brand_model: '', is_active: true }, tempErrorCodes: [], existingErrorCodes: [], currentErrorCode: null, isLoading: false, error: null, lastSaved: null }; // Action types const SET_BRAND_INFO = 'SET_BRAND_INFO'; const SET_BRAND_ID = 'SET_BRAND_ID'; const SET_ROUTE_BRAND_ID = 'SET_ROUTE_BRAND_ID'; const SET_ERROR_CODE_ID = 'SET_ERROR_CODE_ID'; const SET_CURRENT_STEP = 'SET_CURRENT_STEP'; const UPDATE_BRAND_FIELD = 'UPDATE_BRAND_FIELD'; const ADD_ERROR_CODE = 'ADD_ERROR_CODE'; const UPDATE_ERROR_CODE = 'UPDATE_ERROR_CODE'; const DELETE_ERROR_CODE = 'DELETE_ERROR_CODE'; const MARK_AS_DELETED = 'MARK_AS_DELETED'; const SET_TEMP_ERROR_CODES = 'SET_TEMP_ERROR_CODES'; const SET_EXISTING_ERROR_CODES = 'SET_EXISTING_ERROR_CODES'; const MERGE_ERROR_CODES = 'MERGE_ERROR_CODES'; const SET_CURRENT_ERROR_CODE = 'SET_CURRENT_ERROR_CODE'; const SET_LOADING = 'SET_LOADING'; const SET_ERROR = 'SET_ERROR'; const RESET_FORM = 'RESET_FORM'; const SET_LAST_SAVED = 'SET_LAST_SAVED'; // Reducer function const brandFormReducer = (state, action) => { switch (action.type) { case SET_BRAND_INFO: return { ...state, brandInfo: action.payload }; case SET_BRAND_ID: return { ...state, brandId: action.payload }; case SET_ROUTE_BRAND_ID: return { ...state, routeBrandId: action.payload }; case SET_ERROR_CODE_ID: return { ...state, errorCodeId: action.payload }; case SET_CURRENT_STEP: return { ...state, currentStep: action.payload }; case UPDATE_BRAND_FIELD: return { ...state, brandInfo: { ...state.brandInfo, [action.payload.field]: action.payload.value } }; case ADD_ERROR_CODE: const newErrorCode = { tempId: Date.now(), error_code: '', error_code_name: '', error_code_description: '', error_code_color: '#000000ff', path_icon: '', is_active: true, solutions: [], spareparts: [], status: 'new', created_at: new Date().toISOString() }; return { ...state, tempErrorCodes: [...state.tempErrorCodes, newErrorCode] }; case UPDATE_ERROR_CODE: const { tempId, data } = action.payload; if (tempId.startsWith('existing_')) { return { ...state, existingErrorCodes: state.existingErrorCodes.map(ec => `existing_${ec.error_code_id}` === tempId ? { ...ec, ...data, status: 'modified', updated_at: new Date().toISOString() } : ec ) }; } else { // Update in tempErrorCodes return { ...state, tempErrorCodes: state.tempErrorCodes.map(ec => ec.tempId === tempId ? { ...ec, ...data, status: ec.status === 'new' ? 'new' : 'modified', updated_at: new Date().toISOString() } : ec ) }; } case DELETE_ERROR_CODE: return { ...state, tempErrorCodes: state.tempErrorCodes.filter(ec => ec.tempId !== action.payload) }; case MARK_AS_DELETED: const deleteTempId = action.payload; // Check if it's an existing error code (format: existing_${error_code_id}) if (deleteTempId.startsWith('existing_')) { return { ...state, existingErrorCodes: state.existingErrorCodes.map(ec => `existing_${ec.error_code_id}` === deleteTempId ? { ...ec, status: 'deleted', is_active: false, deleted_at: new Date().toISOString() } : ec ) }; } else { return { ...state, tempErrorCodes: state.tempErrorCodes.map(ec => ec.tempId === deleteTempId ? { ...ec, status: 'deleted', is_active: false, deleted_at: new Date().toISOString() } : ec ) }; } case SET_TEMP_ERROR_CODES: return { ...state, tempErrorCodes: action.payload }; case SET_EXISTING_ERROR_CODES: return { ...state, existingErrorCodes: action.payload }; case MERGE_ERROR_CODES: return { ...state, existingErrorCodes: action.payload.existing || [], tempErrorCodes: action.payload.temporary || [] }; case SET_LOADING: return { ...state, isLoading: action.payload }; case SET_ERROR: return { ...state, error: action.payload }; case RESET_FORM: return { ...initialState, lastSaved: state.lastSaved }; case SET_CURRENT_ERROR_CODE: return { ...state, currentErrorCode: action.payload }; case SET_LAST_SAVED: return { ...state, lastSaved: action.payload }; default: return state; } }; // Create context const BrandFormContext = createContext(); export const BrandFormProvider = ({ children }) => { const [state, dispatch] = useReducer(brandFormReducer, initialState); // Actions const actions = { setBrandId: (brandId) => { dispatch({ type: SET_BRAND_ID, payload: brandId }); }, setRouteBrandId: (routeBrandId) => { dispatch({ type: SET_ROUTE_BRAND_ID, payload: routeBrandId }); }, setErrorCodeId: (errorCodeId) => { dispatch({ type: SET_ERROR_CODE_ID, payload: errorCodeId }); }, setCurrentStep: (step) => { dispatch({ type: SET_CURRENT_STEP, payload: step }); }, getCurrentBrandId: () => { return state.brandId || state.routeBrandId; }, setBrandInfo: (brandInfo) => { dispatch({ type: SET_BRAND_INFO, payload: brandInfo }); }, updateBrandField: (field, value) => { dispatch({ type: UPDATE_BRAND_FIELD, payload: { field, value } }); }, addErrorCode: (errorCodeData = {}) => { const defaultErrorCode = { tempId: Date.now(), error_code: '', error_code_name: '', error_code_description: '', error_code_color: '#000000ff', path_icon: '', is_active: true, solutions: [], spareparts: [], status: 'new', created_at: new Date().toISOString() }; const newErrorCode = { ...defaultErrorCode, ...errorCodeData }; dispatch({ type: ADD_ERROR_CODE, payload: newErrorCode }); }, updateErrorCode: (tempId, data) => { dispatch({ type: UPDATE_ERROR_CODE, payload: { tempId, data } }); }, deleteErrorCode: (tempId, isPermanent = false) => { if (isPermanent) { dispatch({ type: DELETE_ERROR_CODE, payload: tempId }); } else { dispatch({ type: MARK_AS_DELETED, payload: tempId }); } }, markAsDeleted: (tempId) => { dispatch({ type: MARK_AS_DELETED, payload: tempId }); }, setTempErrorCodes: (errorCodes) => { dispatch({ type: SET_TEMP_ERROR_CODES, payload: errorCodes }); }, setExistingErrorCodes: (errorCodes) => { dispatch({ type: SET_EXISTING_ERROR_CODES, payload: errorCodes }); }, mergeErrorCodes: (existing, temporary) => { dispatch({ type: MERGE_ERROR_CODES, payload: { existing, temporary } }); }, setLoading: (isLoading) => { dispatch({ type: SET_LOADING, payload: isLoading }); }, setError: (error) => { dispatch({ type: SET_ERROR, payload: error }); }, resetForm: () => { dispatch({ type: RESET_FORM }); }, setLastSaved: (timestamp) => { dispatch({ type: SET_LAST_SAVED, payload: timestamp }); }, setCurrentErrorCode: (errorCode) => { dispatch({ type: SET_CURRENT_ERROR_CODE, payload: errorCode }); }, // Initialize context with route parameters initializeFromRoute: (routeBrandId, errorCodeId = null) => { dispatch({ type: SET_ROUTE_BRAND_ID, payload: routeBrandId }); if (errorCodeId) { dispatch({ type: SET_ERROR_CODE_ID, payload: errorCodeId }); } }, // Navigate to specific step with parameter handling navigateToStep: (step, brandId = null, errorCodeId = null) => { dispatch({ type: SET_CURRENT_STEP, payload: step }); if (brandId) { dispatch({ type: SET_BRAND_ID, payload: brandId }); } if (errorCodeId) { dispatch({ type: SET_ERROR_CODE_ID, payload: errorCodeId }); } }, // Utility functions getErrorCodeByTempId: (tempId) => { return state.tempErrorCodes.find(ec => ec.tempId === tempId); }, getActiveErrorCodes: () => { return state.tempErrorCodes.filter(ec => ec.status !== 'deleted'); }, getModifiedErrorCodes: () => { return state.tempErrorCodes.filter(ec => ec.status === 'new' || ec.status === 'modified' || ec.status === 'deleted' ); }, hasChanges: () => { return state.tempErrorCodes.some(ec => ec.status === 'new' || ec.status === 'modified' || ec.status === 'deleted' ); }, validateForm: () => { const errors = {}; // Validate brand info if (!state.brandInfo.brand_name?.trim()) { errors.brand = 'Brand name is required'; NotifAlert({ icon: 'warning', title: 'Validasi Gagal', message: 'Brand name wajib diisi!', }); return { isValid: false, errors }; } if (!state.brandInfo.brand_manufacture?.trim()) { errors.brand_manufacture = 'Brand manufacture is required'; NotifAlert({ icon: 'warning', title: 'Validasi Gagal', message: 'Brand manufacture wajib diisi!', }); return { isValid: false, errors }; } const allActiveErrorCodes = [ ...state.existingErrorCodes, ...state.tempErrorCodes.filter(ec => ec.status !== 'deleted') ]; if (allActiveErrorCodes.length === 0) { errors.errorCodes = 'At least one error code is required'; NotifAlert({ icon: 'warning', title: 'Validasi Gagal', message: 'Brand harus memiliki minimal 1 error code!', }); return { isValid: false, errors }; } // Validate each error code allActiveErrorCodes.forEach((ec, index) => { if (!ec.error_code?.trim()) { errors[`errorCode_${ec.tempId || ec.error_code_id}`] = 'Error code is required'; } if (!ec.error_code_name?.trim()) { errors[`errorCodeName_${ec.tempId || ec.error_code_id}`] = 'Error code name is required'; } let solutionsToCheck = []; if (ec.solution && Array.isArray(ec.solution)) { solutionsToCheck = ec.solution; } else if (ec.solutions && Array.isArray(ec.solutions)) { solutionsToCheck = ec.solutions; } if (solutionsToCheck.length > 0) { const activeSolutions = solutionsToCheck.filter(sol => sol.status !== 'deleted'); if (activeSolutions.length === 0) { errors[`solutions_${ec.tempId || ec.error_code_id}`] = 'Setiap error code harus memiliki minimal 1 solution'; } } else { errors[`solutions_${ec.tempId || ec.error_code_id}`] = 'Setiap error code harus memiliki minimal 1 solution'; } if (ec.spareparts && Array.isArray(ec.spareparts)) { ec.spareparts.forEach((sp, spIndex) => { if (sp === undefined || sp === null || sp === '') { console.error(`❌ Error Code ${index}, Sparepart ${spIndex}: sparepart_id is undefined, null, or empty`); } }); } }); if (Object.keys(errors).length > 0) { NotifAlert({ icon: 'warning', title: 'Validasi Gagal', message: 'Perbaiki error yang ada sebelum melanjutkan!', }); } return { isValid: Object.keys(errors).length === 0, errors }; }, prepareSubmissionData: (userId) => { const allErrorCodes = [ ...state.existingErrorCodes.map(ec => ({ ...ec, tempId: `existing_${ec.error_code_id}`, status: 'existing' })), ...state.tempErrorCodes ]; const finalErrorCodes = allErrorCodes .filter(ec => ec.status !== 'deleted') .map(ec => { const cleanedCode = { error_code: ec.error_code || '', error_code_name: ec.error_code_name || '', error_code_description: ec.error_code_description || '', error_code_color: ec.error_code_color || '#000000', path_icon: ec.path_icon || '', is_active: ec.is_active === true ? 1 : 0, solution: [], spareparts: [] }; if (ec.error_code_id && ec.status === 'existing') { cleanedCode.error_code_id = parseInt(ec.error_code_id); } // Handle both solution and solutions fields for compatibility let solutions = []; if (ec.solution && Array.isArray(ec.solution)) { solutions = ec.solution; } else if (ec.solutions && Array.isArray(ec.solutions)) { solutions = ec.solutions; } if (solutions.length > 0) { cleanedCode.solution = solutions .filter(sol => sol && sol.solution_name) .map(sol => ({ solution_name: sol.solution_name || '', type_solution: sol.type_solution || 'text', text_solution: sol.text_solution || '', path_solution: sol.path_solution || '', is_active: sol.is_active === true ? 1 : 0 })); } if (ec.spareparts && Array.isArray(ec.spareparts)) { cleanedCode.spareparts = ec.spareparts .filter(sp => sp !== undefined && sp !== null && sp !== '' && sp !== 0) .map(sp => { let sparepartId = 0; if (typeof sp === 'object' && sp !== null) { sparepartId = sp.sparepart_id || sp.id || sp.sparepartId || 0; } else if (typeof sp === 'string' || typeof sp === 'number') { sparepartId = parseInt(sp) || 0; } return parseInt(sparepartId) || 0; }) .filter(id => id > 0); } return cleanedCode; }); const submissionData = { brand_name: state.brandInfo.brand_name || '', brand_type: state.brandInfo.brand_type || '', brand_manufacture: state.brandInfo.brand_manufacture || '', brand_model: state.brandInfo.brand_model || '', is_active: state.brandInfo.is_active === true ? 1 : 0, error_code: finalErrorCodes, updated_by: parseInt(userId) || 1 }; // console.log(' Prepared flat submission data:', JSON.stringify(submissionData, null, 2)); return submissionData; }, getAllErrorCodes: () => { return [ ...state.existingErrorCodes.map(ec => ({ ...ec, tempId: `existing_${ec.error_code_id}`, status: 'existing' })), ...state.tempErrorCodes ]; }, getErrorCodeById: (id) => { const existingCode = state.existingErrorCodes.find(ec => ec.error_code_id == id); if (existingCode) { return { ...existingCode, tempId: `existing_${existingCode.error_code_id}`, status: 'existing' }; } return state.tempErrorCodes.find(ec => ec.tempId == id); }, // Enhanced error code management loadErrorCodesForBrand: async (brandId) => { dispatch({ type: SET_LOADING, payload: true }); try { const { getErrorCodesByBrandId } = await import('../api/master-brand'); const response = await getErrorCodesByBrandId(brandId); if (response && response.data) { dispatch({ type: SET_EXISTING_ERROR_CODES, payload: response.data }); } return response; } catch (error) { dispatch({ type: SET_ERROR, payload: error.message }); throw error; } finally { dispatch({ type: SET_LOADING, payload: false }); } }, // Smart navigation helper navigateToErrorCodes: (brandId) => { const currentId = brandId || state.brandId || state.routeBrandId; if (currentId) { dispatch({ type: SET_BRAND_ID, payload: currentId }); dispatch({ type: SET_CURRENT_STEP, payload: 2 }); return currentId; } return null; }, editErrorCode: (errorCodeId, brandId) => { const currentBrandId = brandId || state.brandId || state.routeBrandId; if (currentBrandId && errorCodeId) { dispatch({ type: SET_BRAND_ID, payload: currentBrandId }); dispatch({ type: SET_ERROR_CODE_ID, payload: errorCodeId }); dispatch({ type: SET_CURRENT_STEP, payload: 3 }); return { brandId: currentBrandId, errorCodeId }; } return null; } }; const value = { ...state, ...actions }; return ( {children} ); }; export const useBrandForm = () => { const context = useContext(BrandFormContext); if (!context) { throw new Error('useBrandForm must be used within a BrandFormProvider'); } return context; }; export default BrandFormContext;