mirror of
https://github.com/rgrgogu/new_starr.git
synced 2026-09-27 00:12:54 +08:00
@@ -1,4 +1,4 @@
|
||||
import { Users, GitFork, FolderOpen, BookText, BookCheck, FileText, ListCheck, ShieldCheck, Megaphone, Bell, Trophy, Cog } from "lucide-react";
|
||||
import { Users, GitFork, FolderOpen, BookText, BookCheck, FileText, ListCheck, ShieldCheck, Megaphone, Bell, Cog } from "lucide-react";
|
||||
|
||||
export const ADMIN_SECTIONS = [
|
||||
{
|
||||
@@ -43,7 +43,6 @@ export const ADMIN_SECTIONS = [
|
||||
tiles: [
|
||||
{ key: "advertisements", label: "Advertisements", icon: Megaphone, link: "/admin/advertisements" },
|
||||
{ key: "notifications", label: "Announcements", icon: Bell, link: "/admin/announcements" },
|
||||
{ key: "achievements", label: "Achievements", icon: Trophy, link: "/admin/achievements" },
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,40 +1,36 @@
|
||||
// data/advertisements.data.js
|
||||
import { Megaphone, Image, BellRing, PanelRight } from "lucide-react";
|
||||
import { Megaphone, Image } from "lucide-react";
|
||||
|
||||
// ─── Types ──────────────────────────────────────────────────────────────────
|
||||
// Drives: filter dropdown options, type badge label/icon on each card,
|
||||
// and which fields the Add/Edit form shows (hero needs headline/description/ctas,
|
||||
// banner/popup/sidebar are closer to image-only).
|
||||
// banner is closer to image-only).
|
||||
|
||||
// TODO(ads-1): Once placements are re-categorized (Hero -> Dashboard, Banner ->
|
||||
// Tier Plans) and popup/sidebar placements are removed, drop the "popup" and
|
||||
// "sidebar" entries here too — see data/placement.data.js.
|
||||
export const ADVERTISEMENT_TYPES = [
|
||||
{ value: "hero", label: "Hero", icon: Megaphone, description: "Large featured banner with headline, description, and CTAs" },
|
||||
{ value: "banner", label: "Banner", icon: Image, description: "Simple image banner" },
|
||||
{ value: "popup", label: "Popup", icon: BellRing, description: "Modal-style popup shown on page load" },
|
||||
{ value: "sidebar", label: "Sidebar", icon: PanelRight, description: "Compact image placed in a sidebar slot" },
|
||||
];
|
||||
|
||||
export const ADVERTISEMENT_TYPE_MAP = Object.fromEntries(
|
||||
ADVERTISEMENT_TYPES.map((t) => [t.value, t])
|
||||
);
|
||||
|
||||
// Types that show the rich content fields (headline, description, CTAs) in the form
|
||||
export const RICH_CONTENT_TYPES = ["hero"];
|
||||
// ─── Content modes ──────────────────────────────────────────────────────────
|
||||
// Whether an ad is image-only or carries badge/headline/description/CTAs
|
||||
// alongside the image — an explicit admin choice, decoupled from placement/format.
|
||||
export const CONTENT_MODES = [
|
||||
{ value: "image", label: "Full image" },
|
||||
{ value: "content", label: "Content + image" },
|
||||
];
|
||||
|
||||
// ─── Statuses ───────────────────────────────────────────────────────────────
|
||||
// Drives: filter dropdown options, status badge color/label on each card.
|
||||
|
||||
// TODO(ads-4): Remove "archived" from this list — it should no longer show up
|
||||
// in the "All statuses" filter on AdvertisementList.jsx (archived ads live in
|
||||
// their own separate Archived list/table, not mixed into the active filter).
|
||||
export const ADVERTISEMENT_STATUSES = [
|
||||
{ value: "draft", label: "Draft", badgeClass: "bg-muted text-muted-foreground" },
|
||||
{ value: "active", label: "Active", badgeClass: "bg-green-100 text-green-700 dark:bg-green-900/40 dark:text-green-400" },
|
||||
{ value: "scheduled", label: "Scheduled", badgeClass: "bg-blue-100 text-blue-700 dark:bg-blue-900/40 dark:text-blue-400" },
|
||||
{ value: "expired", label: "Expired", badgeClass: "bg-muted text-muted-foreground" },
|
||||
{ value: "archived", label: "Archived", badgeClass: "bg-muted text-muted-foreground" },
|
||||
];
|
||||
|
||||
export const ADVERTISEMENT_STATUS_MAP = Object.fromEntries(
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
// Reference-only registry of {{placeholder}} tokens available per system
|
||||
// notification type. Purely informational for the admin editor — the backend
|
||||
// derives the real substitution data from wherever renderNotification({ type,
|
||||
// data }) is called in code, this just tells the admin what's actually
|
||||
// available to reference. Mirrors emailTemplatePlaceholders.data.js.
|
||||
export const NOTIFICATION_TEMPLATE_PLACEHOLDERS = {
|
||||
task_overdue: ["count", "task_word"],
|
||||
user_registration: ["groupName", "groupCode", "userEmail"],
|
||||
nogrp_user_registered: ["userEmail", "regType"],
|
||||
task_requirements_updated: ["taskName", "taskListId", "groupId"],
|
||||
user_task_overdue: ["count", "task_label", "task_list_ids"],
|
||||
task_reminder: ["taskName", "deadline", "taskListId", "groupId"],
|
||||
course_unlocked: ["courseTitle", "courseUuid"],
|
||||
course_completed: ["courseTitle", "courseUuid"],
|
||||
certificate_issued: ["courseTitle", "courseUuid"],
|
||||
welcome: ["greeting", "group_suffix", "groupName", "groupCode", "accType"],
|
||||
nogrp_welcome: [],
|
||||
assessment_updated: ["assessmentTitle", "courseTitle", "courseUuid"],
|
||||
tier_expired: ["planLabel", "tier", "label", "planId"],
|
||||
};
|
||||
@@ -1,20 +0,0 @@
|
||||
import { ListChecks, GraduationCap, Megaphone, ClipboardCheck, CreditCard, UserPlus, Users } from "lucide-react";
|
||||
|
||||
// Groups notification templates by the `notify_type` written into the
|
||||
// delivered notification row — every row here is is_system, so a category
|
||||
// axis (like email templates' announcement/advertisement/system/other) would
|
||||
// always resolve to a single value and add nothing. This is the axis that
|
||||
// actually varies. Mirrors the shape of emailTemplateCategories.data.js.
|
||||
export const NOTIFICATION_TEMPLATE_TYPES = [
|
||||
{ value: "task_overdue", label: "Tasks (Admin)", icon: ListChecks },
|
||||
{ value: "task", label: "Tasks (User)", icon: ListChecks },
|
||||
{ value: "course", label: "Courses", icon: GraduationCap },
|
||||
{ value: "announcement", label: "Announcements", icon: Megaphone },
|
||||
{ value: "assessment", label: "Assessments", icon: ClipboardCheck },
|
||||
{ value: "tier_expired", label: "Subscriptions", icon: CreditCard },
|
||||
{ value: "user_registration", label: "New Registrations", icon: UserPlus },
|
||||
{ value: "nogrp_user_registered", label: "Unaffiliated Users", icon: Users },
|
||||
];
|
||||
|
||||
export const getNotificationTemplateType = (value) =>
|
||||
NOTIFICATION_TEMPLATE_TYPES.find((t) => t.value === value) ?? null;
|
||||
@@ -6,17 +6,10 @@
|
||||
// backend registry when adding a new placement — same pattern as ADVERTISEMENT_TYPES already
|
||||
// mirroring the backend type ENUM.
|
||||
|
||||
// TODO(ads-1): Re-categorize placements — Hero -> Dashboard, Banner -> Tier Plans.
|
||||
// Remove the "popup" and "sidebar" formats entirely (dashboard.popup,
|
||||
// course_details.sidebar). Keep in sync with the backend registry at
|
||||
// new_starr/models/advertisements/advertisements.placements.js.
|
||||
export const PLACEMENTS = [
|
||||
{ key: "dashboard.hero", format: "hero", page: "dashboard", pageLabel: "Dashboard", slotLabel: "Hero (top of page)" },
|
||||
{ key: "dashboard.popup", format: "popup", page: "dashboard", pageLabel: "Dashboard", slotLabel: "Popup (on load)" },
|
||||
{ key: "course_list.banner", format: "banner", page: "course_list", pageLabel: "Courses", slotLabel: "Banner (above course grid)" },
|
||||
{ key: "tier_plans.banner", format: "banner", page: "tier_plans", pageLabel: "Tier Plans", slotLabel: "Banner (above plan cards)" },
|
||||
{ key: "course_details.banner", format: "banner", page: "course_details", pageLabel: "Course Details", slotLabel: "Banner (below hero)" },
|
||||
{ key: "course_details.sidebar", format: "sidebar", page: "course_details", pageLabel: "Course Details", slotLabel: "Sidebar (beside course content)" },
|
||||
{ key: "plans.banner", format: "banner", page: "plans", pageLabel: "Plans", slotLabel: "Banner (above plan cards)" },
|
||||
];
|
||||
|
||||
export const PLACEMENT_MAP = Object.fromEntries(PLACEMENTS.map((p) => [p.key, p]));
|
||||
|
||||
@@ -26,46 +26,15 @@ export const PLACEMENT_LAYOUTS = {
|
||||
{ kind: "grid", label: "Courses" },
|
||||
],
|
||||
},
|
||||
"dashboard.popup": {
|
||||
blocks: [
|
||||
{ kind: "nav" },
|
||||
{ kind: "bar", size: "xl" },
|
||||
{ kind: "list", label: "My Groups" },
|
||||
{ kind: "grid", label: "Courses" },
|
||||
],
|
||||
overlay: { label: "Popup" },
|
||||
},
|
||||
"course_list.banner": {
|
||||
blocks: [
|
||||
{ kind: "nav" },
|
||||
{ kind: "filters" },
|
||||
{ kind: "bar", size: "md", highlight: true, label: "Banner" },
|
||||
{ kind: "grid", label: "Course cards" },
|
||||
],
|
||||
},
|
||||
"course_details.banner": {
|
||||
blocks: [
|
||||
{ kind: "nav" },
|
||||
{ kind: "bar", size: "lg", label: "Course hero" },
|
||||
{ kind: "bar", size: "sm", highlight: true, label: "Banner" },
|
||||
{ kind: "row", columns: [
|
||||
{ label: "Course content", width: "flex-1" },
|
||||
{ label: "Sidebar", width: "w-1/4" },
|
||||
] },
|
||||
{ kind: "list", label: "Course content" },
|
||||
],
|
||||
},
|
||||
"course_details.sidebar": {
|
||||
blocks: [
|
||||
{ kind: "nav" },
|
||||
{ kind: "bar", size: "lg", label: "Course hero" },
|
||||
{ kind: "bar", size: "sm", label: "Banner" },
|
||||
{ kind: "row", columns: [
|
||||
{ label: "Course content", width: "flex-1" },
|
||||
{ label: "Sidebar", width: "w-1/4", highlight: true },
|
||||
] },
|
||||
],
|
||||
},
|
||||
"plans.banner": {
|
||||
"tier_plans.banner": {
|
||||
blocks: [
|
||||
{ kind: "nav" },
|
||||
{ kind: "bar", size: "md", highlight: true, label: "Banner" },
|
||||
|
||||
Reference in New Issue
Block a user