5180 fichiers 38.5 Mo 0 téléchargements 398 vues mis à jour il y a 7 heures ui
nitro-v36-with-renderer-v36 / src / components / housekeeping / views / support / HousekeepingTicketEscalationView.tsx
HousekeepingTicketEscalationView.tsx 5.7 Ko Télécharger le fichier
import { IssueMessageData } from '@octane/renderer';
import { FC, useEffect, useMemo, useState } from 'react';
import { FaGavel } from 'react-icons/fa';
import {
    getHousekeepingEscalationSteps,
    getHousekeepingSanctionTemplates,
    HousekeepingApi,
    HousekeepingSanctionTemplate,
    HousekeepingSanctionType,
    LocalizeText,
    localizeSanctionTemplate,
    suggestHousekeepingSanction
} from '../../../../api';
import { NativeTextTree } from '../../../../common/native-text/NativeTextTree';
import { useHousekeeping, useHousekeepingConfirm } from '../../../../hooks';
import { HousekeepingButton, HousekeepingPill } from '../common/HousekeepingParts';

/**
 * The sanction the escalation suggests for the reported user, from how many sanctions they
 * already have, with a one-click apply after a confirm. The reason names the ticket. The
 * history loads on request: the list request is rate-limited, and several tickets asking at
 * once would lose all but the first.
 */
export const HousekeepingTicketEscalationView: FC<{ ticket: IssueMessageData }> = ({ ticket }) => {
    const { isActionPending, banUser, muteUser, kickUser, tradeLockUser } = useHousekeeping();
    const confirm = useHousekeepingConfirm();
    const templates = useMemo(() => getHousekeepingSanctionTemplates(), []);
    const steps = useMemo(() => getHousekeepingEscalationSteps(), []);
    const [pastSanctions, setPastSanctions] = useState<number | null>(null);
    const [requested, setRequested] = useState(false);
    const [failed, setFailed] = useState(false);
    const [applied, setApplied] = useState(false);

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

        const controller = new AbortController();

        setFailed(false);

        HousekeepingApi.requestList('user.sanctions', ticket.reportedUserId, controller.signal)
            .then((result) => {
                if (controller.signal.aborted) return;
                if (result.ok) setPastSanctions(result.rows.length);
                else setFailed(true);
            })
            .catch(() => {
                if (!controller.signal.aborted) setFailed(true);
            })
            .finally(() => {
                if (!controller.signal.aborted) setRequested(false);
            });

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

    if (pastSanctions === null) {
        return (
            <NativeTextTree>
                <div className="flex items-center gap-1.5 text-[11px]">
                    <HousekeepingButton disabled={requested} gap={1} size="sm" variant="secondary" onClick={() => setRequested(true)}>
                        <FaGavel size={9} />
                        <span>{LocalizeText('housekeeping.support.escalation.check', ['user'], [ticket.reportedUserName])}</span>
                    </HousekeepingButton>
                    {failed && <span className="text-[10px] text-rose-600">{LocalizeText('housekeeping.list.failed')}</span>}
                </div>
            </NativeTextTree>
        );
    }

    const suggestion = suggestHousekeepingSanction(pastSanctions, steps, templates);

    const apply = (template: HousekeepingSanctionTemplate) => {
        const { name, reason } = localizeSanctionTemplate(template);
        const fullReason = `${reason} (#${ticket.issueId})`;
        const userId = ticket.reportedUserId;

        confirm(LocalizeText('housekeeping.support.escalation.confirm', ['sanction', 'user'], [name, ticket.reportedUserName]), async () => {
            const result = await (template.type === HousekeepingSanctionType.BAN
                ? banUser(userId, fullReason, template.durationValue)
                : template.type === HousekeepingSanctionType.MUTE
                  ? muteUser(userId, fullReason, template.durationValue)
                  : template.type === HousekeepingSanctionType.TRADE_LOCK
                    ? tradeLockUser(userId, template.durationValue, fullReason)
                    : kickUser(userId, fullReason));

            if (result?.ok) setApplied(true);
        });
    };

    return (
        <NativeTextTree>
            <div className="flex flex-wrap items-center gap-1.5 rounded border border-rose-200 bg-rose-50/40 px-1.5 py-1 text-[11px]">
                <FaGavel className="shrink-0 text-rose-600" size={9} />
                <span className="text-zinc-700">
                    {LocalizeText('housekeeping.support.escalation.history', ['user', 'count'], [ticket.reportedUserName, String(pastSanctions)])}
                </span>
                {suggestion ? (
                    <>
                        <HousekeepingPill tone="danger">{localizeSanctionTemplate(suggestion).name}</HousekeepingPill>
                        {applied ? (
                            <span className="ml-auto text-[10px] text-emerald-700">{LocalizeText('housekeeping.support.escalation.applied')}</span>
                        ) : (
                            <HousekeepingButton
                                classNames={['ml-auto']}
                                disabled={isActionPending}
                                gap={1}
                                size="sm"
                                variant="danger"
                                onClick={() => apply(suggestion)}
                            >
                                <FaGavel size={9} />
                                <span>{LocalizeText('housekeeping.support.escalation.apply')}</span>
                            </HousekeepingButton>
                        )}
                    </>
                ) : (
                    <span className="text-zinc-500">{LocalizeText('housekeeping.support.escalation.none')}</span>
                )}
            </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