mirror of
https://github.com/rgrgogu/new_starr.git
synced 2026-09-27 00:12:54 +08:00
99 lines
4.3 KiB
React
99 lines
4.3 KiB
React
import { useState } from "react";
|
|
import { useNavigate } from "react-router-dom";
|
|
import { Send } from "lucide-react";
|
|
import {
|
|
Dialog,
|
|
DialogContent,
|
|
DialogDescription,
|
|
DialogFooter,
|
|
DialogHeader,
|
|
DialogTitle,
|
|
} from "@/components/ui/dialog";
|
|
import { Button } from "@/components/ui/button";
|
|
import { Label } from "@/components/ui/label";
|
|
import { Spinner } from "@/components/ui/spinner";
|
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
|
import { BroadcastTargetPicker } from "@/components/generic/BroadcastTargetPicker";
|
|
import { TARGET_TYPE_OPTIONS, TARGET_TYPE_MAP } from "@/data/notificationBroadcast.data";
|
|
import { useAdminEmailBroadcasts } from "@/contexts/AdminEmailBroadcastContext";
|
|
|
|
// Reused from Notification Broadcasts so admins pick an audience the same way
|
|
// everywhere — same target types, same picker for task list / course / tier plan.
|
|
export function SendEmailBroadcastDialog({ open, onOpenChange, template }) {
|
|
const navigate = useNavigate();
|
|
const { createBroadcast, loading } = useAdminEmailBroadcasts();
|
|
|
|
const [targetType, setTargetType] = useState("");
|
|
const [targetId, setTargetId] = useState(null);
|
|
const [error, setError] = useState("");
|
|
|
|
const needsTarget = TARGET_TYPE_MAP[targetType]?.needsTarget;
|
|
|
|
const reset = () => { setTargetType(""); setTargetId(null); setError(""); };
|
|
|
|
const handleSend = async () => {
|
|
if (!targetType) return setError("Please select an audience.");
|
|
if (needsTarget && !targetId) return setError("Please select a specific target.");
|
|
setError("");
|
|
|
|
const result = await createBroadcast({
|
|
email_template_id: template.email_template_id,
|
|
target_type: targetType,
|
|
target_id: needsTarget ? targetId : null,
|
|
});
|
|
if (result) {
|
|
reset();
|
|
onOpenChange(false);
|
|
navigate("/admin/email-broadcasts");
|
|
}
|
|
};
|
|
|
|
return (
|
|
<Dialog open={open} onOpenChange={(v) => { onOpenChange(v); if (!v) reset(); }}>
|
|
<DialogContent className="sm:max-w-md">
|
|
<DialogHeader>
|
|
<DialogTitle>Send to Recipients</DialogTitle>
|
|
<DialogDescription>
|
|
Send <span className="font-semibold text-foreground">{template?.label}</span> as a real email.
|
|
Delivery is paced in the background — this won't block or time out.
|
|
</DialogDescription>
|
|
</DialogHeader>
|
|
|
|
<div className="space-y-4">
|
|
<div className="space-y-1.5">
|
|
<Label>Audience</Label>
|
|
<Select value={targetType} onValueChange={(v) => { setTargetType(v); setTargetId(null); }}>
|
|
<SelectTrigger><SelectValue placeholder="Select an audience" /></SelectTrigger>
|
|
<SelectContent>
|
|
{TARGET_TYPE_OPTIONS.map((t) => (
|
|
<SelectItem key={t.value} value={t.value}>{t.label}</SelectItem>
|
|
))}
|
|
</SelectContent>
|
|
</Select>
|
|
{targetType && (
|
|
<p className="text-xs text-muted-foreground">{TARGET_TYPE_MAP[targetType]?.description}</p>
|
|
)}
|
|
</div>
|
|
|
|
{needsTarget && (
|
|
<div className="space-y-1.5">
|
|
<Label>Target</Label>
|
|
<BroadcastTargetPicker targetType={targetType} value={targetId} onChange={setTargetId} />
|
|
</div>
|
|
)}
|
|
|
|
{error && <p className="text-xs text-destructive">{error}</p>}
|
|
</div>
|
|
|
|
<DialogFooter>
|
|
<Button variant="outline" onClick={() => onOpenChange(false)} disabled={loading}>Cancel</Button>
|
|
<Button onClick={handleSend} disabled={loading}>
|
|
{loading ? <Spinner className="h-4 w-4 mr-2" /> : <Send className="h-4 w-4 mr-2" />}
|
|
Send
|
|
</Button>
|
|
</DialogFooter>
|
|
</DialogContent>
|
|
</Dialog>
|
|
);
|
|
}
|