5180 fichiers 38.5 Mo 0 téléchargements 352 vues mis à jour il y a 7 heures ui
nitro-v36-with-renderer-v36 / src / components / catalog / views / admin / useCatalogAdminSmartSave.ts
useCatalogAdminSmartSave.ts 5.7 Ko Télécharger le fichier
import { useCallback, useEffect, useReducer, useRef } from 'react';
import {
    catalogAdminSmartSaveReducer,
    createCatalogAdminSmartSaveState,
    isCatalogAdminFormDirty,
    mergeCatalogAdminCommittedForm
} from './CatalogAdminSmartSaveState';

export interface CatalogAdminSaveAcknowledgement
{
    operationId: string;
    success: boolean;
    code: string;
    message: string;
    entity: unknown;
    fieldErrors: Record<string, string>;
    acknowledgedAt: number;
}

interface UseCatalogAdminSmartSaveOptions<T extends object>
{
    initial: T;
    acknowledgement: CatalogAdminSaveAcknowledgement | null;
    submit: (draft: T) => string | null;
    canSubmit?: (draft: T) => boolean;
    toCommitted: (acknowledgement: CatalogAdminSaveAcknowledgement) => T | null;
    onClose?: () => void;
    confirmClose?: () => boolean;
}

export const useCatalogAdminSmartSave = <T extends object>({
    initial,
    acknowledgement,
    submit,
    canSubmit = () => true,
    toCommitted,
    onClose,
    confirmClose = () => window.confirm('Discard unsaved changes?')
}: UseCatalogAdminSmartSaveOptions<T>) =>
{
    const [state, dispatch] = useReducer(catalogAdminSmartSaveReducer<T>, initial, createCatalogAdminSmartSaveState<T>);
    const stateRef = useRef(state);
    const submitRef = useRef(submit);
    const canSubmitRef = useRef(canSubmit);
    const closeRef = useRef(onClose);
    const toCommittedRef = useRef(toCommitted);
    stateRef.current = state;
    submitRef.current = submit;
    canSubmitRef.current = canSubmit;
    closeRef.current = onClose;
    toCommittedRef.current = toCommitted;

    const patch = useCallback((value: Partial<T>) => dispatch({ type: 'patch', patch: value }), []);
    const hydrate = useCallback((value: T) => dispatch({ type: 'hydrate', value }), []);
    const reset = useCallback(() => dispatch({ type: 'reset' }), []);

    const save = useCallback((closeAfter = false) =>
    {
        const current = stateRef.current;
        if(current.inFlight)
        {
            dispatch({ type: 'queue', closeAfter });
            return current.inFlight.operationId;
        }
        if(!isCatalogAdminFormDirty(current.baseline, current.draft))
        {
            if(closeAfter) closeRef.current?.();
            return null;
        }
        if(!canSubmitRef.current(current.draft)) return null;

        const operationId = submitRef.current(current.draft);
        if(!operationId) return null;
        dispatch({ type: 'submit', operationId, submitted: current.draft, closeAfter });
        return operationId;
    }, []);

    const requestClose = useCallback(() =>
    {
        const current = stateRef.current;
        if((current.inFlight || isCatalogAdminFormDirty(current.baseline, current.draft)) && !confirmClose()) return;
        closeRef.current?.();
    }, [confirmClose]);

    useEffect(() =>
    {
        if(!acknowledgement) return;
        const current = stateRef.current;
        const inFlight = current.inFlight;
        if(!inFlight || inFlight.operationId !== acknowledgement.operationId) return;

        if(!acknowledgement.success)
        {
            if(acknowledgement.code === 'STALE_REVISION')
                dispatch({ type: 'conflict', operationId: acknowledgement.operationId, message: acknowledgement.message });
            else
                dispatch({
                    type: 'failure',
                    operationId: acknowledgement.operationId,
                    message: acknowledgement.message,
                    fieldErrors: acknowledgement.fieldErrors
                });
            return;
        }

        const committed = toCommittedRef.current(acknowledgement);
        if(!committed)
        {
            dispatch({
                type: 'failure', operationId: acknowledgement.operationId,
                message: acknowledgement.message || 'The server returned an incomplete save result.', fieldErrors: {}
            });
            return;
        }

        const merged = mergeCatalogAdminCommittedForm(inFlight.submitted, current.draft, committed);
        const shouldClose = inFlight.closeAfter && !current.queued && !isCatalogAdminFormDirty(committed, merged);
        dispatch({
            type: 'success', operationId: acknowledgement.operationId, committed,
            savedAt: acknowledgement.acknowledgedAt, message: acknowledgement.message
        });
        if(shouldClose) closeRef.current?.();
    }, [acknowledgement]);

    useEffect(() =>
    {
        if(state.inFlight || !state.queued) return;
        if(!isCatalogAdminFormDirty(state.baseline, state.draft))
        {
            const closeAfter = state.queued.closeAfter;
            dispatch({ type: 'reset' });
            if(closeAfter) closeRef.current?.();
            return;
        }

        if(!canSubmitRef.current(state.draft)) return;
        const operationId = submitRef.current(state.draft);
        if(!operationId) return;
        dispatch({ type: 'submit', operationId, submitted: state.draft, closeAfter: state.queued.closeAfter });
    }, [state.baseline, state.draft, state.inFlight, state.queued]);

    useEffect(() =>
    {
        const handleKeyboardSave = (event: KeyboardEvent) =>
        {
            if(!(event.ctrlKey || event.metaKey) || event.key.toLowerCase() !== 's') return;
            event.preventDefault();
            save(false);
        };
        window.addEventListener('keydown', handleKeyboardSave);
        return () => window.removeEventListener('keydown', handleKeyboardSave);
    }, [save]);

    return {
        ...state,
        draft: state.draft,
        isDirty: isCatalogAdminFormDirty(state.baseline, state.draft),
        patch,
        hydrate,
        reset,
        save,
        requestClose
    };
};
15Sujets
1Dépôts
20Membres
3En ligne
Profile
Inscrit
Dernière visite
Messages
0
Votes positifs
0
Votes négatifs
0

Nous utilisons des cookies pour assurer le bon fonctionnement du site et améliorer votre expérience. en savoir plus

Autoriser