feat(01-04): implement WizardState types and pure reducer
- Create src/store/types.ts: WizardState interface, WizardAction union type, INITIAL_STATE constant - Create src/store/reducer.ts: pure wizardReducer handling SET_STEP, SET_BACKEND_TYPE, SET_REMOTE_NAME, SET_REMOTE_PARAMS, SET_DEPLOYMENT, RESET - All 8 reducer tests GREEN - No localStorage/sessionStorage access (SECU-03 compliant)
This commit is contained in:
@@ -0,0 +1,44 @@
|
|||||||
|
// src/store/reducer.ts
|
||||||
|
// Pure reducer — no side effects, no localStorage, no async operations.
|
||||||
|
|
||||||
|
import { WizardState, WizardAction, INITIAL_STATE } from './types';
|
||||||
|
|
||||||
|
export function wizardReducer(
|
||||||
|
state: WizardState = INITIAL_STATE,
|
||||||
|
action: WizardAction
|
||||||
|
): WizardState {
|
||||||
|
switch (action.type) {
|
||||||
|
case 'SET_STEP':
|
||||||
|
return { ...state, currentStep: action.payload };
|
||||||
|
|
||||||
|
case 'SET_BACKEND_TYPE':
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
remote: { ...state.remote, backendType: action.payload },
|
||||||
|
};
|
||||||
|
|
||||||
|
case 'SET_REMOTE_NAME':
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
remote: { ...state.remote, name: action.payload },
|
||||||
|
};
|
||||||
|
|
||||||
|
case 'SET_REMOTE_PARAMS':
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
remote: { ...state.remote, params: action.payload },
|
||||||
|
};
|
||||||
|
|
||||||
|
case 'SET_DEPLOYMENT':
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
deployment: { ...state.deployment, ...action.payload },
|
||||||
|
};
|
||||||
|
|
||||||
|
case 'RESET':
|
||||||
|
return INITIAL_STATE;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
// src/store/types.ts
|
||||||
|
// WizardState shape, action union type, and initial state.
|
||||||
|
// SECURITY: This state is exclusively in-memory.
|
||||||
|
// NEVER add localStorage.setItem, sessionStorage.setItem, or IndexedDB here.
|
||||||
|
// Closing the browser tab is the intended "clear credentials" operation (SECU-03).
|
||||||
|
|
||||||
|
import type { BackendType } from '../schemas/registry';
|
||||||
|
|
||||||
|
// Re-export BackendType so store consumers import from one place
|
||||||
|
export type { BackendType };
|
||||||
|
|
||||||
|
export interface WizardState {
|
||||||
|
currentStep: number;
|
||||||
|
remote: {
|
||||||
|
name: string;
|
||||||
|
backendType: BackendType | null;
|
||||||
|
params: Record<string, string>; // backend-specific key/value pairs (rclone config keys)
|
||||||
|
};
|
||||||
|
deployment: {
|
||||||
|
includeInstall: boolean;
|
||||||
|
configPath: 'machine-wide' | 'user-profile'; // machine-wide = C:\ProgramData\rclone\
|
||||||
|
scriptTargets: ('intune' | 'rmm')[];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export type WizardAction =
|
||||||
|
| { type: 'SET_STEP'; payload: number }
|
||||||
|
| { type: 'SET_BACKEND_TYPE'; payload: BackendType }
|
||||||
|
| { type: 'SET_REMOTE_NAME'; payload: string }
|
||||||
|
| { type: 'SET_REMOTE_PARAMS'; payload: Record<string, string> }
|
||||||
|
| { type: 'SET_DEPLOYMENT'; payload: Partial<WizardState['deployment']> }
|
||||||
|
| { type: 'RESET' };
|
||||||
|
|
||||||
|
export const INITIAL_STATE: WizardState = {
|
||||||
|
currentStep: 0,
|
||||||
|
remote: {
|
||||||
|
name: '',
|
||||||
|
backendType: null,
|
||||||
|
params: {},
|
||||||
|
},
|
||||||
|
deployment: {
|
||||||
|
includeInstall: false,
|
||||||
|
configPath: 'machine-wide',
|
||||||
|
scriptTargets: ['intune', 'rmm'],
|
||||||
|
},
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user