5180 fichiers 38.5 Mo 0 téléchargements 479 vues mis à jour il y a 7 heures ui
nitro-v36-with-renderer-v36 / src / components / housekeeping / views / hotel / HousekeepingMaintenanceView.tsx
HousekeepingMaintenanceView.tsx 10.5 Ko Télécharger le fichier
import { HousekeepingMaintenanceStatusEvent } from '@octane/renderer';
import { FC, useEffect, useState } from 'react';
import { FaBan, FaHourglassHalf, FaPlay, FaPowerOff, FaTools } from 'react-icons/fa';
import {
    formatHousekeepingCountdown,
    HOUSEKEEPING_MAINTENANCE_MAX_MINUTES,
    HOUSEKEEPING_MAINTENANCE_MESSAGE_MAX,
    HOUSEKEEPING_MAINTENANCE_PRESETS,
    HousekeepingApi,
    housekeepingFailureKey,
    IHousekeepingMaintenanceStatus,
    LocalizeText,
    readMaintenanceStatus
} from '../../../../api';
import { NativeTextTree } from '../../../../common/native-text/NativeTextTree';
import { useHousekeeping, useHousekeepingConfirm, useHousekeepingDangerConfirm, useMessageEvent } from '../../../../hooks';
import {
    HOUSEKEEPING_INPUT_CLASS,
    HousekeepingButton,
    HousekeepingEmptyState,
    HousekeepingField,
    HousekeepingNumberField,
    HousekeepingPill,
    HousekeepingSection
} from '../common/HousekeepingParts';

const DANGER_WORD = 'MAINTENANCE';

/**
 * Maintenance with a countdown: online users are told the hotel closes in N minutes and
 * reminded as it runs down; at the end everyone below the maintenance rank is disconnected.
 * "Now" skips the countdown. Every change comes back as the current state.
 */
export const HousekeepingMaintenanceView: FC = () => {
    const { isActionPending, startMaintenance, cancelMaintenance, disableMaintenance } = useHousekeeping();
    const confirm = useHousekeepingConfirm();
    const confirmDanger = useHousekeepingDangerConfirm();
    const [status, setStatus] = useState<IHousekeepingMaintenanceStatus | null>(null);
    const [error, setError] = useState<string | null>(null);
    const [minutes, setMinutes] = useState(5);
    const [message, setMessage] = useState('');
    const [now, setNow] = useState(() => Math.floor(Date.now() / 1000));

    useEffect(() => {
        const controller = new AbortController();

        HousekeepingApi.getMaintenanceStatus(controller.signal)
            .then((result) => {
                if (!controller.signal.aborted) setStatus(result);
            })
            .catch((reason) => {
                if (!controller.signal.aborted) setError(housekeepingFailureKey(reason, 'housekeeping.hotel.maintenance.failed'));
            });

        return () => controller.abort();
    }, []);

    // Every change the server makes (ours or another operator's) comes back as the state.
    useMessageEvent<HousekeepingMaintenanceStatusEvent>(HousekeepingMaintenanceStatusEvent, (event) => {
        setStatus(readMaintenanceStatus(event.getParser()));
        setError(null);
    });

    const countdownEndsAt = status?.countdownEndsAt ?? 0;

    useEffect(() => {
        if (!countdownEndsAt) return;

        const timer = window.setInterval(() => setNow(Math.floor(Date.now() / 1000)), 1000);

        return () => window.clearInterval(timer);
    }, [countdownEndsAt]);

    const trimmedMessage = message.trim();
    const validMinutes = minutes >= 1 && minutes <= HOUSEKEEPING_MAINTENANCE_MAX_MINUTES;

    const start = () =>
        confirm(LocalizeText('housekeeping.hotel.maintenance.start.confirm', ['minutes'], [String(minutes)]), () => startMaintenance(minutes, trimmedMessage));

    const startNow = () =>
        confirmDanger(
            LocalizeText('housekeeping.hotel.maintenance.now.confirm', ['word'], [DANGER_WORD]),
            DANGER_WORD,
            () => startMaintenance(0, trimmedMessage),
            LocalizeText('housekeeping.hotel.maintenance.now')
        );

    const disable = () => confirm(LocalizeText('housekeeping.hotel.maintenance.disable.confirm'), () => disableMaintenance());

    return (
        <NativeTextTree>
            <HousekeepingSection
                aside={
                    status && (
                        <HousekeepingPill tone={status.enabled ? 'danger' : countdownEndsAt ? 'warning' : 'success'}>
                            {LocalizeText(
                                status.enabled
                                    ? 'housekeeping.hotel.maintenance.state.on'
                                    : countdownEndsAt
                                      ? 'housekeeping.hotel.maintenance.state.countdown'
                                      : 'housekeeping.hotel.maintenance.state.off'
                            )}
                        </HousekeepingPill>
                    )
                }
                icon={<FaTools className="text-amber-600" size={9} />}
                title={LocalizeText('housekeeping.hotel.maintenance.label')}
                tone="warning"
            >
                {error && <HousekeepingEmptyState>{LocalizeText(error)}</HousekeepingEmptyState>}
                {!error && !status && <HousekeepingEmptyState>{LocalizeText('generic.loading')}</HousekeepingEmptyState>}

                {status && (
                    <>
                        <div className="text-[10px] text-zinc-600">
                            {LocalizeText('housekeeping.hotel.maintenance.rank', ['rank'], [String(status.minRank)])}
                        </div>
                        {status.message && (
                            <div className="rounded border border-zinc-200 bg-white p-1.5 text-[11px] italic text-zinc-700">{status.message}</div>
                        )}

                        {countdownEndsAt > 0 && (
                            <div className="flex items-center gap-2 rounded border border-amber-300 bg-amber-50 px-2 py-1.5">
                                <FaHourglassHalf className="text-amber-600" size={12} />
                                <div className="flex grow flex-col">
                                    <span className="text-[10px] font-semibold uppercase tracking-wide text-amber-800">
                                        {LocalizeText('housekeeping.hotel.maintenance.closes_in')}
                                    </span>
                                    <span className="text-lg font-bold tabular-nums text-amber-900">{formatHousekeepingCountdown(countdownEndsAt - now)}</span>
                                </div>
                                <HousekeepingButton disabled={isActionPending} gap={1} size="sm" variant="secondary" onClick={() => cancelMaintenance()}>
                                    <FaBan size={9} />
                                    <span>{LocalizeText('housekeeping.hotel.maintenance.cancel')}</span>
                                </HousekeepingButton>
                            </div>
                        )}

                        {status.enabled ? (
                            <HousekeepingButton disabled={isActionPending} gap={1} variant="success" onClick={disable}>
                                <FaPowerOff size={10} />
                                <span>{LocalizeText('housekeeping.hotel.maintenance.disable')}</span>
                            </HousekeepingButton>
                        ) : (
                            <>
                                <HousekeepingField
                                    hint={`${trimmedMessage.length}/${HOUSEKEEPING_MAINTENANCE_MESSAGE_MAX}`}
                                    label={LocalizeText('housekeeping.hotel.maintenance.message')}
                                >
                                    <input
                                        className={HOUSEKEEPING_INPUT_CLASS}
                                        maxLength={HOUSEKEEPING_MAINTENANCE_MESSAGE_MAX}
                                        placeholder={LocalizeText('housekeeping.hotel.maintenance.message.placeholder')}
                                        value={message}
                                        onChange={(event) => setMessage(event.target.value)}
                                    />
                                </HousekeepingField>
                                <div className="flex flex-wrap items-center gap-1">
                                    {HOUSEKEEPING_MAINTENANCE_PRESETS.map((preset) => (
                                        <HousekeepingButton
                                            key={preset}
                                            size="sm"
                                            variant={preset === minutes ? 'primary' : 'neutral'}
                                            onClick={() => setMinutes(preset)}
                                        >
                                            <span>{LocalizeText('housekeeping.hotel.maintenance.minutes', ['minutes'], [String(preset)])}</span>
                                        </HousekeepingButton>
                                    ))}
                                    <HousekeepingNumberField
                                        max={HOUSEKEEPING_MAINTENANCE_MAX_MINUTES}
                                        unit={LocalizeText('housekeeping.unit.minutes')}
                                        value={minutes}
                                        widthClass="w-14"
                                        onChange={setMinutes}
                                    />
                                </div>
                                <div className="flex flex-wrap items-center gap-1.5">
                                    <HousekeepingButton disabled={isActionPending || !validMinutes} gap={1} variant="warning" onClick={start}>
                                        <FaPlay size={9} />
                                        <span>
                                            {LocalizeText(
                                                countdownEndsAt ? 'housekeeping.hotel.maintenance.restart' : 'housekeeping.hotel.maintenance.start',
                                                ['minutes'],
                                                [String(minutes)]
                                            )}
                                        </span>
                                    </HousekeepingButton>
                                    <HousekeepingButton classNames={['ml-auto']} disabled={isActionPending} gap={1} variant="danger" onClick={startNow}>
                                        <FaPowerOff size={9} />
                                        <span>{LocalizeText('housekeeping.hotel.maintenance.now')}</span>
                                    </HousekeepingButton>
                                </div>
                                <div className="text-[10px] text-zinc-500">
                                    {LocalizeText('housekeeping.hotel.maintenance.hint', ['rank'], [String(status.minRank)])}
                                </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