5180 fichiers 38.5 Mo 0 téléchargements 366 vues mis à jour il y a 7 heures ui
nitro-v36-with-renderer-v36 / src / components / housekeeping / views / users / HousekeepingUserAccountView.tsx
HousekeepingUserAccountView.tsx 10.4 Ko Télécharger le fichier
import { FC, useEffect, useState } from 'react';
import { FaHourglassHalf, FaKey, FaStop, FaUserShield } from 'react-icons/fa';
import {
    formatHousekeepingDateTime,
    HK_MAX_RANK,
    HOUSEKEEPING_TEMP_RANK_DAYS,
    HOUSEKEEPING_TEMP_RANK_MAX_DAYS,
    HousekeepingApi,
    housekeepingTempRankSeconds,
    IHousekeepingList,
    IHousekeepingUser,
    LocalizeText
} from '../../../../api';
import { NativeTextTree } from '../../../../common/native-text/NativeTextTree';
import { useHasPermission, useHousekeeping, useHousekeepingDangerConfirm } from '../../../../hooks';
import { HousekeepingButton, HousekeepingNumberField, HousekeepingSection } from '../common/HousekeepingParts';

const TEMP_RANK_LIST = 'user.temp_rank';

interface TemporaryRank {
    rank: string;
    previousRankId: number;
    previous: string;
    expires: number;
    staff: string;
}

const readTemporaryRank = (list: IHousekeepingList | null): TemporaryRank | null => {
    const row = list?.rows[0];

    if (!list || !row) return null;

    const cell = (name: string) => row[list.columns.indexOf(name)] ?? '';
    const named = (name: string, id: string) => (cell(name) ? `${cell(name)} (${cell(id)})` : `#${cell(id)}`);

    return {
        rank: named('rank_name', 'rank'),
        previousRankId: Number(cell('previous_rank')) || 1,
        previous: named('previous_rank_name', 'previous_rank'),
        expires: Number(cell('expires')) || 0,
        staff: cell('staff')
    };
};

/** Rank (lasting or for a number of days) and password of the selected user. */
export const HousekeepingUserAccountView: FC<{ user: IHousekeepingUser }> = ({ user }) => {
    const { isActionPending, setUserRank, resetUserPassword } = useHousekeeping();
    const confirmDanger = useHousekeepingDangerConfirm();
    // Rank changes belong to the permissions area; without it the page keeps the password only.
    const canChangeRank = useHasPermission('acc_hk_permissions');
    const [rankDraft, setRankDraft] = useState<number>(user.rank || 1);
    // 0 = a lasting rank; otherwise the rank lasts this many days, then the previous one comes back.
    const [days, setDays] = useState(0);
    const [temporaryRank, setTemporaryRank] = useState<TemporaryRank | null>(null);
    const [reload, setReload] = useState(0);

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

        HousekeepingApi.requestList(TEMP_RANK_LIST, user.id, controller.signal)
            .then((result) => {
                if (!controller.signal.aborted) setTemporaryRank(result.ok ? readTemporaryRank(result) : null);
            })
            .catch(() => undefined);

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

    const durationSeconds = housekeepingTempRankSeconds(days);
    const isTemporary = durationSeconds > 0;
    const unchanged = rankDraft === user.rank && !isTemporary;

    const giveRank = () =>
        confirmDanger(
            isTemporary
                ? LocalizeText('housekeeping.user.account.rank_temp_confirm', ['rank', 'days'], [String(rankDraft), String(days)])
                : LocalizeText('housekeeping.user.account.rank_confirm', ['rank'], [String(rankDraft)]),
            user.username,
            async () => {
                const result = await setUserRank(user.id, rankDraft, durationSeconds);

                if (result?.ok) setReload((value) => value + 1);
            },
            LocalizeText('housekeeping.action.set_rank')
        );

    // Ending early is a lasting change back to the previous rank, which also stops the timer.
    const endTemporaryRank = (rank: TemporaryRank) =>
        confirmDanger(
            LocalizeText('housekeeping.user.account.rank_temp_end_confirm', ['rank'], [rank.previous]),
            user.username,
            async () => {
                const result = await setUserRank(user.id, rank.previousRankId);

                if (result?.ok) {
                    setRankDraft(rank.previousRankId);
                    setReload((value) => value + 1);
                }
            },
            LocalizeText('housekeeping.user.account.rank_temp_end')
        );

    return (
        <NativeTextTree>
            <div className="flex flex-col gap-2">
                {canChangeRank && (
                    <HousekeepingSection
                        icon={<FaUserShield className="text-violet-500" size={9} />}
                        title={LocalizeText('housekeeping.user.account.rank')}
                        tone="accent"
                    >
                        {temporaryRank && (
                            <div className="flex items-center gap-2 rounded border border-amber-300 bg-amber-50 px-2 py-1 text-[11px] text-amber-900">
                                <FaHourglassHalf className="shrink-0 text-amber-600" size={11} />
                                <span className="grow">
                                    {LocalizeText(
                                        'housekeeping.user.account.rank_temp_running',
                                        ['rank', 'until', 'previous', 'staff'],
                                        [
                                            temporaryRank.rank,
                                            formatHousekeepingDateTime(temporaryRank.expires * 1000),
                                            temporaryRank.previous,
                                            temporaryRank.staff || '-'
                                        ]
                                    )}
                                </span>
                                <HousekeepingButton
                                    disabled={isActionPending}
                                    gap={1}
                                    size="sm"
                                    variant="secondary"
                                    onClick={() => endTemporaryRank(temporaryRank)}
                                >
                                    <FaStop size={8} />
                                    <span>{LocalizeText('housekeeping.user.account.rank_temp_end')}</span>
                                </HousekeepingButton>
                            </div>
                        )}
                        <div className="flex items-center gap-1.5">
                            <HousekeepingNumberField
                                label={LocalizeText('housekeeping.field.rank')}
                                max={HK_MAX_RANK}
                                value={rankDraft}
                                onChange={setRankDraft}
                            />
                            <span className="text-[10px] text-zinc-500">
                                {LocalizeText('housekeeping.user.account.rank_current', ['rank'], [`${user.rankName} (${user.rank})`])}
                            </span>
                        </div>
                        <div className="flex flex-wrap items-center gap-1">
                            <span className="text-[10px] font-semibold uppercase tracking-wide text-zinc-600">
                                {LocalizeText('housekeeping.user.account.rank_for')}
                            </span>
                            <HousekeepingButton size="sm" variant={days === 0 ? 'primary' : 'neutral'} onClick={() => setDays(0)}>
                                <span>{LocalizeText('housekeeping.user.account.rank_lasting')}</span>
                            </HousekeepingButton>
                            {HOUSEKEEPING_TEMP_RANK_DAYS.map((preset) => (
                                <HousekeepingButton key={preset} size="sm" variant={days === preset ? 'primary' : 'neutral'} onClick={() => setDays(preset)}>
                                    <span>{LocalizeText('housekeeping.user.account.rank_days', ['days'], [String(preset)])}</span>
                                </HousekeepingButton>
                            ))}
                            <HousekeepingNumberField
                                max={HOUSEKEEPING_TEMP_RANK_MAX_DAYS}
                                unit={LocalizeText('housekeeping.unit.days')}
                                value={days}
                                widthClass="w-14"
                                onChange={setDays}
                            />
                        </div>
                        <div className="flex items-center gap-1.5">
                            <span className="text-[10px] text-zinc-500">
                                {isTemporary
                                    ? LocalizeText('housekeeping.user.account.rank_temp_hint', ['days'], [String(days)])
                                    : LocalizeText('housekeeping.user.account.rank_lasting_hint')}
                            </span>
                            <HousekeepingButton classNames={['ml-auto']} disabled={isActionPending || unchanged} gap={1} variant="primary" onClick={giveRank}>
                                <FaUserShield size={10} />
                                <span>{LocalizeText('housekeeping.action.set_rank')}</span>
                            </HousekeepingButton>
                        </div>
                    </HousekeepingSection>
                )}
                <HousekeepingSection icon={<FaKey className="text-zinc-500" size={9} />} title={LocalizeText('housekeeping.user.account.password')}>
                    <div className="flex items-center gap-1.5">
                        <span className="text-[11px] text-zinc-600">{LocalizeText('housekeeping.user.account.password_hint')}</span>
                        <HousekeepingButton
                            classNames={['ml-auto', 'shrink-0']}
                            disabled={isActionPending}
                            gap={1}
                            variant="secondary"
                            onClick={() =>
                                confirmDanger(
                                    LocalizeText('housekeeping.action.reset_password.confirm'),
                                    user.username,
                                    () => resetUserPassword(user.id),
                                    LocalizeText('housekeeping.action.reset_password')
                                )
                            }
                        >
                            <FaKey size={10} />
                            <span>{LocalizeText('housekeeping.action.reset_password')}</span>
                        </HousekeepingButton>
                    </div>
                </HousekeepingSection>
            </div>
        </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