Ship the full Communication portal (real SMS API, feature-locked future channels), BFF proxy, docs, snapshot, and phase handover. Co-authored-by: Cursor <cursoragent@cursor.com>
63 lines
2.7 KiB
TypeScript
63 lines
2.7 KiB
TypeScript
"use client";
|
||
|
||
import { useState } from "react";
|
||
import { useMutation } from "@tanstack/react-query";
|
||
import { toast } from "sonner";
|
||
import { communicationApi } from "@/modules/communication/services/communication-api";
|
||
import { useTenantId } from "@/hooks/useTenantId";
|
||
import { CommunicationPageLoader } from "@/modules/communication/pages/shared";
|
||
import { PageHeader, Card, CardContent, Button, FormField, Input } from "@/components/ds";
|
||
import { CommunicationBreadcrumbs } from "@/modules/communication/components/CommunicationBreadcrumbs";
|
||
import { useCommunicationPermissions } from "@/modules/communication/hooks/useCommunicationPermissions";
|
||
import { PermissionDeniedState } from "@/src/shared/ui";
|
||
|
||
export function OtpPage() {
|
||
const { tenantId } = useTenantId();
|
||
const perms = useCommunicationPermissions();
|
||
const [dest, setDest] = useState("");
|
||
const [code, setCode] = useState("");
|
||
const [challengeId, setChallengeId] = useState<string | null>(null);
|
||
|
||
const requestM = useMutation({
|
||
mutationFn: () =>
|
||
communicationApi.otp.request(tenantId!, { destination: dest, channel: "sms", purpose: "business" }),
|
||
onSuccess: (r) => {
|
||
toast.success("OTP درخواست شد");
|
||
setChallengeId(String(r.challenge_id));
|
||
if (r.debug_code) toast.message(`Debug: ${r.debug_code}`);
|
||
},
|
||
onError: (e: Error) => toast.error(e.message),
|
||
});
|
||
|
||
const verifyM = useMutation({
|
||
mutationFn: () =>
|
||
communicationApi.otp.verify(tenantId!, {
|
||
destination: dest,
|
||
code,
|
||
challenge_id: challengeId,
|
||
}),
|
||
onSuccess: (r) => toast.success(r.verified ? "تأیید شد" : "ناموفق"),
|
||
onError: (e: Error) => toast.error(e.message),
|
||
});
|
||
|
||
if (!tenantId) return <CommunicationPageLoader />;
|
||
if (!perms.can("communication.otp.request")) return <PermissionDeniedState />;
|
||
|
||
return (
|
||
<div className="mx-auto max-w-md space-y-6">
|
||
<CommunicationBreadcrumbs current="OTP" />
|
||
<PageHeader title="OTP کسبوکار" description="غیر auth — Core OTP جداگانه" />
|
||
<Card>
|
||
<CardContent className="space-y-4 p-4">
|
||
<FormField label="موبایل"><Input value={dest} onChange={(e) => setDest(e.target.value)} dir="ltr" /></FormField>
|
||
<Button onClick={() => requestM.mutate()} loading={requestM.isPending} disabled={!dest}>درخواست OTP</Button>
|
||
<FormField label="کد"><Input value={code} onChange={(e) => setCode(e.target.value)} dir="ltr" /></FormField>
|
||
<Button variant="outline" onClick={() => verifyM.mutate()} loading={verifyM.isPending} disabled={!code}>تأیید</Button>
|
||
</CardContent>
|
||
</Card>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
export default OtpPage;
|