mirror of
https://github.com/rgrgogu/new_starr.git
synced 2026-09-27 00:12:54 +08:00
units,lesson as standalone
This commit is contained in:
@@ -11,6 +11,7 @@ import { useAuth } from "@/contexts/AuthContext";
|
||||
import AppBreadcrumb from "@/components/generic/Breadcrumb/AppBreadcrumb";
|
||||
import { BroadcastTargetPicker } from "@/components/generic/BroadcastTargetPicker";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
@@ -26,6 +27,8 @@ const schema = z.object({
|
||||
message: z.string().min(1, "Message is required."),
|
||||
target_type: z.enum(["admin", "user", "both", "task_list", "course", "tier_plan"], { required_error: "Target is required." }),
|
||||
target_id: z.string().nullable().optional(),
|
||||
show_in_sticky: z.boolean().optional(),
|
||||
show_in_notifications: z.boolean().optional(),
|
||||
}).superRefine((data, ctx) => {
|
||||
if (TARGET_TYPE_MAP[data.target_type]?.needsTarget && !data.target_id) {
|
||||
ctx.addIssue({
|
||||
@@ -34,6 +37,14 @@ const schema = z.object({
|
||||
path: ["target_id"],
|
||||
});
|
||||
}
|
||||
|
||||
if (!data.show_in_sticky && !data.show_in_notifications) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: "Select where to show this notification (Sticky or Notifications).",
|
||||
path: ["show_in_sticky"],
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// ─── Helpers ────────────────────────────────────────────────────────────────
|
||||
@@ -77,16 +88,20 @@ export default function AddNotificationBroadcast() {
|
||||
message: "",
|
||||
target_type: undefined,
|
||||
target_id: null,
|
||||
show_in_sticky: false,
|
||||
show_in_notifications: true,
|
||||
},
|
||||
});
|
||||
|
||||
const targetType = watch("target_type");
|
||||
const targetId = watch("target_id");
|
||||
const needsTarget = TARGET_TYPE_MAP[targetType]?.needsTarget;
|
||||
const showInSticky = watch("show_in_sticky");
|
||||
const showInNotifications = watch("show_in_notifications");
|
||||
|
||||
const breadcrumbItems = [
|
||||
{ label: "Home", icon: <House className="size-4" />, to: "/admin" },
|
||||
{ label: "Notifications", to: "/admin/notifications" },
|
||||
{ label: "Announcements", to: "/admin/announcements" },
|
||||
{ label: "New" },
|
||||
];
|
||||
|
||||
@@ -94,11 +109,13 @@ export default function AddNotificationBroadcast() {
|
||||
const payload = {
|
||||
...values,
|
||||
target_id: TARGET_TYPE_MAP[values.target_type]?.needsTarget ? values.target_id : null,
|
||||
show_in_sticky: values.show_in_sticky ?? false,
|
||||
show_in_notifications: values.show_in_notifications ?? true,
|
||||
createdBy: user?.user_id ?? null,
|
||||
};
|
||||
|
||||
const res = await createBroadcast(payload);
|
||||
if (res) navigate("/admin/notifications");
|
||||
if (res) navigate("/admin/announcements");
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -109,7 +126,7 @@ export default function AddNotificationBroadcast() {
|
||||
</div>
|
||||
|
||||
<div className="w-full max-w-2xl pb-10">
|
||||
<h1 className="text-2xl font-semibold tracking-tight mb-1">New notification</h1>
|
||||
<h1 className="text-2xl font-semibold tracking-tight mb-1">New announcement</h1>
|
||||
<p className="text-sm text-muted-foreground mb-6">Compose an announcement. It's saved as a draft until you send it.</p>
|
||||
|
||||
<form onSubmit={handleSubmit(onSubmit)} className="space-y-5">
|
||||
@@ -127,7 +144,7 @@ export default function AddNotificationBroadcast() {
|
||||
</div>
|
||||
</SectionCard>
|
||||
|
||||
<SectionCard title="Target" description="Who receives this notification when it's sent.">
|
||||
<SectionCard title="Target" description="Who receives this announcement when it's sent.">
|
||||
<div>
|
||||
<Select
|
||||
value={targetType}
|
||||
@@ -165,6 +182,32 @@ export default function AddNotificationBroadcast() {
|
||||
)}
|
||||
</SectionCard>
|
||||
|
||||
<SectionCard title="Display" description="Where clients/admins can see this announcement.">
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center gap-3">
|
||||
<Checkbox
|
||||
id="show_in_sticky"
|
||||
checked={showInSticky === true}
|
||||
onCheckedChange={(v) => setValue("show_in_sticky", v === true, { shouldValidate: true })}
|
||||
/>
|
||||
<Label htmlFor="show_in_sticky" className="cursor-pointer">
|
||||
Show in Sticky Announcements
|
||||
</Label>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-3">
|
||||
<Checkbox
|
||||
id="show_in_notifications"
|
||||
checked={showInNotifications === true}
|
||||
onCheckedChange={(v) => setValue("show_in_notifications", v === true, { shouldValidate: true })}
|
||||
/>
|
||||
<Label htmlFor="show_in_notifications" className="cursor-pointer">
|
||||
Show in Notifications
|
||||
</Label>
|
||||
</div>
|
||||
</div>
|
||||
</SectionCard>
|
||||
|
||||
<div className="flex justify-end gap-2 pt-2">
|
||||
<Button type="button" variant="outline" onClick={() => navigate(-1)} disabled={loading}>
|
||||
Cancel
|
||||
|
||||
Reference in New Issue
Block a user