5180 fichiers 38.5 Mo 0 téléchargements 476 vues mis à jour il y a 7 heures ui
nitro-v36-with-renderer-v36 / src / components / housekeeping / views / hotel / HousekeepingReloadView.tsx
HousekeepingReloadView.tsx 7.5 Ko Télécharger le fichier
import { FC, ReactNode, useState } from 'react';
import { FaBook, FaCheck, FaCog, FaCompass, FaCube, FaFilter, FaLanguage, FaSync, FaTimes, FaUserShield } from 'react-icons/fa';
import { formatHousekeepingDateTime, HOUSEKEEPING_RELOAD_TARGETS, HousekeepingReloadTarget, LocalizeText } from '../../../../api';
import { NativeTextTree } from '../../../../common/native-text/NativeTextTree';
import { useHousekeeping, useHousekeepingConfirm } from '../../../../hooks';
import { HousekeepingButton, HousekeepingSection } from '../common/HousekeepingParts';

const RELOAD_ICONS: Record<HousekeepingReloadTarget, ReactNode> = {
    catalog: <FaBook className="text-sky-600" size={11} />,
    texts: <FaLanguage className="text-violet-600" size={11} />,
    permissions: <FaUserShield className="text-rose-600" size={11} />,
    items: <FaCube className="text-amber-600" size={11} />,
    navigator: <FaCompass className="text-emerald-600" size={11} />,
    config: <FaCog className="text-zinc-600" size={11} />,
    wordfilter: <FaFilter className="text-orange-600" size={11} />
};

type ReloadOutcome = { at: number; ok: boolean };

/** The server drops a reload that comes within 2s of the previous one without answering it. */
const RELOAD_SPACING_MS = 2_100;

const wait = (ms: number) => new Promise<void>((resolve) => window.setTimeout(resolve, ms));

/** Hot reload of the server tables, one at a time or all in a row with a report. */
export const HousekeepingReloadView: FC = () => {
    const { isActionPending, reloadHotel } = useHousekeeping();
    const confirm = useHousekeepingConfirm();
    const [outcomes, setOutcomes] = useState<Partial<Record<HousekeepingReloadTarget, ReloadOutcome>>>({});
    const [isReloadingAll, setIsReloadingAll] = useState(false);

    const run = async (target: HousekeepingReloadTarget) => {
        const result = await reloadHotel(target);

        setOutcomes((previous) => ({ ...previous, [target]: { at: Date.now(), ok: !!result?.ok } }));
    };

    const reload = (target: HousekeepingReloadTarget) =>
        confirm(LocalizeText('housekeeping.hotel.reload.confirm', ['target'], [LocalizeText(`housekeeping.hotel.reload.${target}`)]), () => run(target));

    // One after the other, each waiting for its answer and starting at least RELOAD_SPACING_MS
    // after the previous one, so the server's rate limit never drops one.
    const reloadAll = () =>
        confirm(LocalizeText('housekeeping.hotel.reload.all.confirm'), async () => {
            setIsReloadingAll(true);
            setOutcomes({});

            try {
                let lastStart = 0;

                for (const target of HOUSEKEEPING_RELOAD_TARGETS) {
                    const sinceLast = Date.now() - lastStart;

                    if (lastStart && sinceLast < RELOAD_SPACING_MS) await wait(RELOAD_SPACING_MS - sinceLast);

                    lastStart = Date.now();
                    await run(target);
                }
            } finally {
                setIsReloadingAll(false);
            }
        });

    const finished = HOUSEKEEPING_RELOAD_TARGETS.filter((target) => outcomes[target]);
    const failed = finished.filter((target) => !outcomes[target]?.ok);

    return (
        <NativeTextTree>
            <HousekeepingSection
                aside={
                    <HousekeepingButton disabled={isActionPending || isReloadingAll} gap={1} size="sm" variant="primary" onClick={reloadAll}>
                        <FaSync className={isReloadingAll ? 'animate-spin' : ''} size={9} />
                        <span>{LocalizeText('housekeeping.hotel.reload.all')}</span>
                    </HousekeepingButton>
                }
                icon={<FaSync className="text-sky-600" size={9} />}
                title={LocalizeText('housekeeping.hotel.reload.label')}
                tone="accent"
            >
                <div className="text-[10px] text-zinc-500">{LocalizeText('housekeeping.hotel.reload.hint')}</div>
                {finished.length === HOUSEKEEPING_RELOAD_TARGETS.length && !isReloadingAll && (
                    <div
                        className={`rounded border px-2 py-1 text-[11px] ${failed.length ? 'border-rose-300 bg-rose-50 text-rose-800' : 'border-emerald-300 bg-emerald-50 text-emerald-800'}`}
                    >
                        {failed.length
                            ? LocalizeText(
                                  'housekeeping.hotel.reload.all.partial',
                                  ['failed'],
                                  [failed.map((target) => LocalizeText(`housekeeping.hotel.reload.${target}`)).join(', ')]
                              )
                            : LocalizeText('housekeeping.hotel.reload.all.done')}
                    </div>
                )}
                <div className="grid grid-cols-2 gap-1.5">
                    {HOUSEKEEPING_RELOAD_TARGETS.map((target) => {
                        const outcome = outcomes[target];

                        return (
                            <div key={target} className="flex min-w-0 items-center gap-1.5 rounded border border-zinc-200 bg-white px-1.5 py-1">
                                <span className="shrink-0">{RELOAD_ICONS[target]}</span>
                                <div className="flex min-w-0 grow flex-col">
                                    <span className="truncate text-[11px] font-semibold text-zinc-800">
                                        {LocalizeText(`housekeeping.hotel.reload.${target}`)}
                                    </span>
                                    <span
                                        className={`flex items-center gap-1 truncate text-[9px] ${outcome && !outcome.ok ? 'text-rose-600' : 'text-zinc-500'}`}
                                        title={LocalizeText(`housekeeping.hotel.reload.${target}.hint`)}
                                    >
                                        {outcome &&
                                            (outcome.ok ? (
                                                <FaCheck className="shrink-0 text-emerald-600" size={8} />
                                            ) : (
                                                <FaTimes className="shrink-0" size={8} />
                                            ))}
                                        {outcome
                                            ? LocalizeText(
                                                  outcome.ok ? 'housekeeping.hotel.reload.done_at' : 'housekeeping.hotel.reload.failed_at',
                                                  ['time'],
                                                  [formatHousekeepingDateTime(outcome.at)]
                                              )
                                            : LocalizeText(`housekeeping.hotel.reload.${target}.hint`)}
                                    </span>
                                </div>
                                <HousekeepingButton
                                    disabled={isActionPending || isReloadingAll}
                                    gap={1}
                                    size="sm"
                                    variant="primary"
                                    onClick={() => reload(target)}
                                >
                                    <FaSync size={9} />
                                    <span>{LocalizeText('housekeeping.hotel.reload.run')}</span>
                                </HousekeepingButton>
                            </div>
                        );
                    })}
                </div>
            </HousekeepingSection>
        </NativeTextTree>
    );
};
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