3july
This commit is contained in:
parent
76cfbf5581
commit
8643657fee
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -12,7 +12,7 @@
|
|||
rel="stylesheet"
|
||||
/>
|
||||
<script src="https://accounts.google.com/gsi/client" async defer></script>
|
||||
<script type="module" crossorigin src="/assets/index-Cq3hylG7.js"></script>
|
||||
<script type="module" crossorigin src="/assets/index-DBI6VQnt.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-B2aqbk3U.css">
|
||||
</head>
|
||||
|
||||
|
|
|
|||
|
|
@ -31,11 +31,6 @@ export function AppShell() {
|
|||
setActiveNav("search");
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!selectedEventId || activeNav !== "agenda") return;
|
||||
setActiveNav("dashboard");
|
||||
}, [selectedEventId, activeNav, setActiveNav]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!selectedEventId) return;
|
||||
if (activeNav === "questions" || activeNav === "quiz") {
|
||||
|
|
|
|||
|
|
@ -146,6 +146,10 @@ export function MainContent() {
|
|||
return <EventAnalyticsPage eventId={selectedEventId} onNavigate={setActiveNav} />;
|
||||
}
|
||||
|
||||
if (activeNav === "agenda") {
|
||||
return <AgendaPage />;
|
||||
}
|
||||
|
||||
if (activeNav === "dashboard") {
|
||||
return (
|
||||
<div style={{ display: "flex", flexDirection: "column", gap: "20px" }}>
|
||||
|
|
@ -205,8 +209,6 @@ export function MainContent() {
|
|||
|
||||
<EventHomeModules />
|
||||
|
||||
<AgendaPage embedInDashboard />
|
||||
|
||||
<div
|
||||
style={{
|
||||
paddingTop: "8px",
|
||||
|
|
@ -222,7 +224,7 @@ export function MainContent() {
|
|||
);
|
||||
}
|
||||
|
||||
return <AgendaPage />;
|
||||
return <HomePage />;
|
||||
};
|
||||
|
||||
return <>{pageContent()}</>;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React, { useRef, useState, useEffect } from "react";
|
||||
import {
|
||||
LayoutDashboard, LineChart, Users,
|
||||
BarChart2, Trophy, ClipboardCheck, Zap,
|
||||
BarChart2, Trophy, ClipboardCheck, Zap, CalendarDays,
|
||||
} from "lucide-react";
|
||||
import { useBreakpoint } from "../hooks/useBreakpoint";
|
||||
|
||||
|
|
@ -17,6 +17,7 @@ interface EventTab {
|
|||
|
||||
const EVENT_TABS: EventTab[] = [
|
||||
{ id: "dashboard", label: "Event Home", shortLabel: "Home", icon: <LayoutDashboard size={13} /> },
|
||||
{ id: "agenda", label: "Agenda", shortLabel: "Agenda", icon: <CalendarDays size={13} /> },
|
||||
{ id: "participants", label: "Participants", shortLabel: "People", icon: <Users size={13} /> },
|
||||
{ id: "attendance", label: "Attendance", shortLabel: "Attend", icon: <ClipboardCheck size={13} /> },
|
||||
{ id: "event-activities", label: "Activities", shortLabel: "Activities", icon: <Zap size={13} /> },
|
||||
|
|
|
|||
|
|
@ -621,7 +621,7 @@ function TrackAccordion({ track, isOpen, virtual, onToggle, onEdit, onDelete, on
|
|||
}
|
||||
|
||||
/* ─────────────── Main Page ─────────────── */
|
||||
export function AgendaPage({ embedInDashboard = false }: { embedInDashboard?: boolean } = {}) {
|
||||
export function AgendaPage() {
|
||||
const { selectedEventId, openAgendaSessionPanelOnce, setOpenAgendaSessionPanelOnce } = useAppNav();
|
||||
const bp = useBreakpoint();
|
||||
const isMobile = bp === "mobile";
|
||||
|
|
@ -933,7 +933,7 @@ export function AgendaPage({ embedInDashboard = false }: { embedInDashboard?: bo
|
|||
{loading ? "…" : eventName}
|
||||
</p>
|
||||
<h1 style={{ fontFamily: "var(--font-inter)", fontSize: isMobile ? "var(--text-xl)" : "var(--text-2xl)", fontWeight: "var(--font-weight-medium)", color: "var(--foreground)", lineHeight: "1.2", marginBottom: "4px" }}>
|
||||
{embedInDashboard ? "Agenda" : "Event Home"}
|
||||
Agenda
|
||||
</h1>
|
||||
<p style={{ fontSize: "var(--text-sm)", fontFamily: "var(--font-noto-sans)", color: "var(--muted)" }}>
|
||||
{loadErr ? <span style={{ color: "var(--destructive)" }}>{loadErr}</span> : `${tracks.filter((t) => t.id !== UNASSIGNED_TRACK_ID).length} tracks · ${total} sessions`}
|
||||
|
|
@ -1005,13 +1005,11 @@ export function AgendaPage({ embedInDashboard = false }: { embedInDashboard?: bo
|
|||
</div>
|
||||
)}
|
||||
|
||||
{!embedInDashboard && (
|
||||
<div style={{ paddingTop: "16px", borderTop: "1px solid var(--border)", textAlign: "center" }}>
|
||||
<p style={{ fontSize: "var(--text-xs)", fontFamily: "var(--font-noto-sans)", color: "var(--muted)" }}>
|
||||
© 2026 Stratawiz Technologies Pvt Ltd · EventBuzz
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
<div style={{ paddingTop: "16px", borderTop: "1px solid var(--border)", textAlign: "center" }}>
|
||||
<p style={{ fontSize: "var(--text-xs)", fontFamily: "var(--font-noto-sans)", color: "var(--muted)" }}>
|
||||
© 2026 Stratawiz Technologies Pvt Ltd · EventBuzz
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{sessionPanel && selectedEventId && (
|
||||
<AgendaSessionPanel
|
||||
|
|
|
|||
|
|
@ -57,10 +57,6 @@ export function EventAnalyticsPage({
|
|||
}, [load]);
|
||||
|
||||
const handleNavigate = (navId: string) => {
|
||||
if (navId === "agenda") {
|
||||
onNavigate?.("dashboard");
|
||||
return;
|
||||
}
|
||||
onNavigate?.(navId);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import React, { useCallback, useEffect, useState } from "react";
|
||||
import { BarChart2, FileText, MessageSquare, ShieldCheck, Tag, Ticket, Trophy, X } from "lucide-react";
|
||||
import { BarChart2, CalendarDays, FileText, Plus, ShieldCheck, Tag, Ticket, Trophy } from "lucide-react";
|
||||
import { useBreakpoint } from "../../hooks/useBreakpoint";
|
||||
import { useAppNav } from "../../contexts/AppNavContext";
|
||||
import { eventsApi } from "../../../services/api/events";
|
||||
|
|
@ -16,18 +16,8 @@ import { AddQuizPollModal, buildQuizPollSettingsPayload } from "../quiz/AddQuizP
|
|||
import type { QuizPollModalForm } from "../quiz/AddQuizPollModal";
|
||||
import type { Question } from "../quiz/AddQuestionModal";
|
||||
import { modalQuestionToApi } from "../quiz/quizQuestionMappers";
|
||||
import { sessionsApi } from "../../../services/api";
|
||||
import { sessionsApi, tracksApi, formsApi } from "../../../services/api";
|
||||
import type { FormWithSubmissionCount } from "../../../services/api/types";
|
||||
import {
|
||||
DEFAULT_REGISTRATION_FORM_NAME,
|
||||
registrationFormDisplayName,
|
||||
resolveEventRegistrationForm,
|
||||
} from "./registrationFormModule";
|
||||
import {
|
||||
DEFAULT_FEEDBACK_FORM_NAME,
|
||||
feedbackFormDisplayName,
|
||||
resolveEventFeedbackForm,
|
||||
} from "./feedbackFormModule";
|
||||
|
||||
function ModuleCard({
|
||||
icon,
|
||||
|
|
@ -139,267 +129,27 @@ function moduleButtonStyle(primary = false): React.CSSProperties {
|
|||
};
|
||||
}
|
||||
|
||||
function DefaultRegistrationResponsesPanel({
|
||||
formName,
|
||||
onClose,
|
||||
}: {
|
||||
formName: string;
|
||||
onClose: () => void;
|
||||
}) {
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
onClick={onClose}
|
||||
role="presentation"
|
||||
style={{ position: "fixed", inset: 0, background: "rgba(0,0,0,0.5)", zIndex: 200 }}
|
||||
/>
|
||||
<div
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-labelledby="default-reg-responses-title"
|
||||
style={{
|
||||
position: "fixed",
|
||||
top: 0,
|
||||
right: 0,
|
||||
height: "100%",
|
||||
width: "min(720px, 100vw)",
|
||||
background: "var(--card)",
|
||||
zIndex: 201,
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
boxShadow: "-4px 0 40px rgba(0,0,0,0.2)",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "flex-start",
|
||||
justifyContent: "space-between",
|
||||
gap: 12,
|
||||
padding: "16px 18px",
|
||||
borderBottom: "1px solid var(--border)",
|
||||
flexShrink: 0,
|
||||
}}
|
||||
>
|
||||
<div style={{ minWidth: 0 }}>
|
||||
<p
|
||||
style={{
|
||||
fontSize: "var(--text-xs)",
|
||||
fontFamily: "var(--font-noto-sans)",
|
||||
color: "var(--muted)",
|
||||
margin: "0 0 4px",
|
||||
}}
|
||||
>
|
||||
Submissions
|
||||
</p>
|
||||
<h2
|
||||
id="default-reg-responses-title"
|
||||
style={{
|
||||
fontFamily: "var(--font-inter)",
|
||||
fontSize: "var(--text-lg)",
|
||||
fontWeight: "var(--font-weight-medium)",
|
||||
color: "var(--foreground)",
|
||||
margin: 0,
|
||||
lineHeight: 1.3,
|
||||
}}
|
||||
>
|
||||
{formName}
|
||||
</h2>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={onClose}
|
||||
aria-label="Close"
|
||||
style={{
|
||||
flexShrink: 0,
|
||||
width: 36,
|
||||
height: 36,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
borderRadius: "var(--radius-button)",
|
||||
border: "1px solid var(--border)",
|
||||
background: "var(--card)",
|
||||
color: "var(--muted)",
|
||||
cursor: "pointer",
|
||||
}}
|
||||
>
|
||||
<X size={18} />
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
flex: 1,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
padding: "32px 24px",
|
||||
textAlign: "center",
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
<p
|
||||
style={{
|
||||
fontFamily: "var(--font-inter)",
|
||||
fontSize: "var(--text-base)",
|
||||
fontWeight: "var(--font-weight-medium)",
|
||||
color: "var(--foreground)",
|
||||
margin: "0 0 8px",
|
||||
}}
|
||||
>
|
||||
No responses yet
|
||||
</p>
|
||||
<p
|
||||
style={{
|
||||
fontSize: "var(--text-sm)",
|
||||
fontFamily: "var(--font-noto-sans)",
|
||||
color: "var(--muted)",
|
||||
margin: 0,
|
||||
maxWidth: "360px",
|
||||
lineHeight: 1.5,
|
||||
}}
|
||||
>
|
||||
Using the default registration form (first name, last name, email). Save the form via Edit to
|
||||
customize it and start collecting responses.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
function formDisplayType(apiType: string): string {
|
||||
if (apiType === "feedback") return "Feedback";
|
||||
if (apiType === "registration") return "Registration";
|
||||
if (apiType === "other") return "Other";
|
||||
if (apiType === "survey" || apiType === "custom") return "Other";
|
||||
return apiType.charAt(0).toUpperCase() + apiType.slice(1);
|
||||
}
|
||||
|
||||
function DefaultFeedbackResponsesPanel({
|
||||
formName,
|
||||
onClose,
|
||||
}: {
|
||||
formName: string;
|
||||
onClose: () => void;
|
||||
}) {
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
onClick={onClose}
|
||||
role="presentation"
|
||||
style={{ position: "fixed", inset: 0, background: "rgba(0,0,0,0.5)", zIndex: 200 }}
|
||||
/>
|
||||
<div
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-labelledby="default-feedback-responses-title"
|
||||
style={{
|
||||
position: "fixed",
|
||||
top: 0,
|
||||
right: 0,
|
||||
height: "100%",
|
||||
width: "min(720px, 100vw)",
|
||||
background: "var(--card)",
|
||||
zIndex: 201,
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
boxShadow: "-4px 0 40px rgba(0,0,0,0.2)",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "flex-start",
|
||||
justifyContent: "space-between",
|
||||
gap: 12,
|
||||
padding: "16px 18px",
|
||||
borderBottom: "1px solid var(--border)",
|
||||
flexShrink: 0,
|
||||
}}
|
||||
>
|
||||
<div style={{ minWidth: 0 }}>
|
||||
<p
|
||||
style={{
|
||||
fontSize: "var(--text-xs)",
|
||||
fontFamily: "var(--font-noto-sans)",
|
||||
color: "var(--muted)",
|
||||
margin: "0 0 4px",
|
||||
}}
|
||||
>
|
||||
Submissions
|
||||
</p>
|
||||
<h2
|
||||
id="default-feedback-responses-title"
|
||||
style={{
|
||||
fontFamily: "var(--font-inter)",
|
||||
fontSize: "var(--text-lg)",
|
||||
fontWeight: "var(--font-weight-medium)",
|
||||
color: "var(--foreground)",
|
||||
margin: 0,
|
||||
lineHeight: 1.3,
|
||||
}}
|
||||
>
|
||||
{formName}
|
||||
</h2>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={onClose}
|
||||
aria-label="Close"
|
||||
style={{
|
||||
flexShrink: 0,
|
||||
width: 36,
|
||||
height: 36,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
borderRadius: "var(--radius-button)",
|
||||
border: "1px solid var(--border)",
|
||||
background: "var(--card)",
|
||||
color: "var(--muted)",
|
||||
cursor: "pointer",
|
||||
}}
|
||||
>
|
||||
<X size={18} />
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
flex: 1,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
padding: "32px 24px",
|
||||
textAlign: "center",
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
<p
|
||||
style={{
|
||||
fontFamily: "var(--font-inter)",
|
||||
fontSize: "var(--text-base)",
|
||||
fontWeight: "var(--font-weight-medium)",
|
||||
color: "var(--foreground)",
|
||||
margin: "0 0 8px",
|
||||
}}
|
||||
>
|
||||
No responses yet
|
||||
</p>
|
||||
<p
|
||||
style={{
|
||||
fontSize: "var(--text-sm)",
|
||||
fontFamily: "var(--font-noto-sans)",
|
||||
color: "var(--muted)",
|
||||
margin: 0,
|
||||
maxWidth: "360px",
|
||||
lineHeight: 1.5,
|
||||
}}
|
||||
>
|
||||
Save a feedback form via Edit to start collecting responses from attendees.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
function formPanelMode(apiType: string): "feedback" | "registration" | "other" {
|
||||
if (apiType === "feedback") return "feedback";
|
||||
if (apiType === "registration") return "registration";
|
||||
return "other";
|
||||
}
|
||||
|
||||
function formMetaLine(form: FormWithSubmissionCount): string {
|
||||
const count = form.submissionCount ?? 0;
|
||||
return `${formDisplayType(form.type)} · ${count.toLocaleString()} response${count === 1 ? "" : "s"}`;
|
||||
}
|
||||
|
||||
export function EventHomeModules() {
|
||||
const { selectedEventId, setActiveNav, setPermissionsOpenAddOnce } = useAppNav();
|
||||
const { selectedEventId, setActiveNav, setPermissionsOpenAddOnce, setOpenAgendaSessionPanelOnce } = useAppNav();
|
||||
const bp = useBreakpoint();
|
||||
const isMobile = bp === "mobile";
|
||||
const isTablet = bp === "tablet";
|
||||
|
|
@ -408,15 +158,10 @@ export function EventHomeModules() {
|
|||
: isTablet
|
||||
? "repeat(2, minmax(0, 1fr))"
|
||||
: "repeat(auto-fill, minmax(240px, 1fr))";
|
||||
const [linkedFormId, setLinkedFormId] = useState<string | undefined>();
|
||||
const [registrationForm, setRegistrationForm] = useState<FormWithSubmissionCount | null>(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [editOpen, setEditOpen] = useState(false);
|
||||
const [responsesOpen, setResponsesOpen] = useState(false);
|
||||
const [feedbackForm, setFeedbackForm] = useState<FormWithSubmissionCount | null>(null);
|
||||
const [feedbackLoading, setFeedbackLoading] = useState(false);
|
||||
const [feedbackEditOpen, setFeedbackEditOpen] = useState(false);
|
||||
const [feedbackResponsesOpen, setFeedbackResponsesOpen] = useState(false);
|
||||
const [eventForms, setEventForms] = useState<FormWithSubmissionCount[]>([]);
|
||||
const [formEditTarget, setFormEditTarget] = useState<FormWithSubmissionCount | null>(null);
|
||||
const [formResponsesTarget, setFormResponsesTarget] = useState<FormWithSubmissionCount | null>(null);
|
||||
const [newFormMode, setNewFormMode] = useState<"registration" | "feedback" | "other" | null>(null);
|
||||
const [pollQuizCounts, setPollQuizCounts] = useState({ polls: 0, quizzes: 0 });
|
||||
const [pollQuizLoading, setPollQuizLoading] = useState(false);
|
||||
const [showAddPoll, setShowAddPoll] = useState(false);
|
||||
|
|
@ -434,17 +179,17 @@ export function EventHomeModules() {
|
|||
const [rewardRulesCount, setRewardRulesCount] = useState(0);
|
||||
const [rewardLoading, setRewardLoading] = useState(false);
|
||||
const [rewardRulesOpen, setRewardRulesOpen] = useState(false);
|
||||
const [agendaCounts, setAgendaCounts] = useState({ tracks: 0, sessions: 0 });
|
||||
const [agendaLoading, setAgendaLoading] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (!selectedEventId) {
|
||||
setLinkedFormId(undefined);
|
||||
setEventName(null);
|
||||
return;
|
||||
}
|
||||
let cancelled = false;
|
||||
void eventsApi.get(selectedEventId).then((res) => {
|
||||
if (cancelled) return;
|
||||
setLinkedFormId(res.data?.website?.registrationFormId);
|
||||
setEventName(res.data?.name ?? null);
|
||||
});
|
||||
return () => {
|
||||
|
|
@ -452,47 +197,25 @@ export function EventHomeModules() {
|
|||
};
|
||||
}, [selectedEventId]);
|
||||
|
||||
const loadRegistrationForm = useCallback(async () => {
|
||||
const loadEventForms = useCallback(async () => {
|
||||
if (!selectedEventId) {
|
||||
setRegistrationForm(null);
|
||||
setLoading(false);
|
||||
setEventForms([]);
|
||||
return;
|
||||
}
|
||||
setLoading(true);
|
||||
try {
|
||||
const form = await resolveEventRegistrationForm(selectedEventId, linkedFormId);
|
||||
setRegistrationForm(form);
|
||||
const forms = await formsApi.listForEvent(selectedEventId);
|
||||
const sorted = [...forms].sort((a, b) =>
|
||||
(a.name || "").localeCompare(b.name || "", undefined, { sensitivity: "base" }),
|
||||
);
|
||||
setEventForms(sorted);
|
||||
} catch {
|
||||
setRegistrationForm(null);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [selectedEventId, linkedFormId]);
|
||||
|
||||
useEffect(() => {
|
||||
void loadRegistrationForm();
|
||||
}, [loadRegistrationForm]);
|
||||
|
||||
const loadFeedbackForm = useCallback(async () => {
|
||||
if (!selectedEventId) {
|
||||
setFeedbackForm(null);
|
||||
setFeedbackLoading(false);
|
||||
return;
|
||||
}
|
||||
setFeedbackLoading(true);
|
||||
try {
|
||||
const form = await resolveEventFeedbackForm(selectedEventId);
|
||||
setFeedbackForm(form);
|
||||
} catch {
|
||||
setFeedbackForm(null);
|
||||
} finally {
|
||||
setFeedbackLoading(false);
|
||||
setEventForms([]);
|
||||
}
|
||||
}, [selectedEventId]);
|
||||
|
||||
useEffect(() => {
|
||||
void loadFeedbackForm();
|
||||
}, [loadFeedbackForm]);
|
||||
void loadEventForms();
|
||||
}, [loadEventForms]);
|
||||
|
||||
const loadPollQuizCounts = useCallback(async () => {
|
||||
if (!selectedEventId) {
|
||||
|
|
@ -610,6 +333,32 @@ export function EventHomeModules() {
|
|||
void loadRewardRulesMeta();
|
||||
}, [loadRewardRulesMeta]);
|
||||
|
||||
const loadAgendaMeta = useCallback(async () => {
|
||||
if (!selectedEventId) {
|
||||
setAgendaCounts({ tracks: 0, sessions: 0 });
|
||||
setAgendaLoading(false);
|
||||
return;
|
||||
}
|
||||
setAgendaLoading(true);
|
||||
try {
|
||||
const [trRes, sesRes] = await Promise.all([
|
||||
tracksApi.list(selectedEventId),
|
||||
sessionsApi.list(selectedEventId),
|
||||
]);
|
||||
const tracks = (trRes.data as { tracks?: unknown[] } | undefined)?.tracks ?? [];
|
||||
const sessions = (sesRes.data as { sessions?: unknown[] } | undefined)?.sessions ?? [];
|
||||
setAgendaCounts({ tracks: tracks.length, sessions: sessions.length });
|
||||
} catch {
|
||||
setAgendaCounts({ tracks: 0, sessions: 0 });
|
||||
} finally {
|
||||
setAgendaLoading(false);
|
||||
}
|
||||
}, [selectedEventId]);
|
||||
|
||||
useEffect(() => {
|
||||
void loadAgendaMeta();
|
||||
}, [loadAgendaMeta]);
|
||||
|
||||
const handleBadgeSuccess = () => {
|
||||
setShowAddBadge(false);
|
||||
setDuplicateBadgeBatchId(null);
|
||||
|
|
@ -627,28 +376,16 @@ export function EventHomeModules() {
|
|||
setShowAddBadge(true);
|
||||
};
|
||||
|
||||
const handleEditSuccess = () => {
|
||||
setEditOpen(false);
|
||||
void loadRegistrationForm();
|
||||
const handleFormEditSuccess = () => {
|
||||
setFormEditTarget(null);
|
||||
void loadEventForms();
|
||||
};
|
||||
|
||||
const handleFeedbackEditSuccess = () => {
|
||||
setFeedbackEditOpen(false);
|
||||
void loadFeedbackForm();
|
||||
const handleNewFormSuccess = () => {
|
||||
setNewFormMode(null);
|
||||
void loadEventForms();
|
||||
};
|
||||
|
||||
const formName = registrationFormDisplayName(registrationForm);
|
||||
const feedbackFormName = feedbackFormDisplayName(feedbackForm);
|
||||
const submissionMeta = !selectedEventId
|
||||
? "Select an event to view responses"
|
||||
: loading
|
||||
? "Loading…"
|
||||
: `${(registrationForm?.submissionCount ?? 0).toLocaleString()} response${(registrationForm?.submissionCount ?? 0) === 1 ? "" : "s"}`;
|
||||
const feedbackSubmissionMeta = !selectedEventId
|
||||
? "Select an event to view responses"
|
||||
: feedbackLoading
|
||||
? "Loading…"
|
||||
: `${(feedbackForm?.submissionCount ?? 0).toLocaleString()} response${(feedbackForm?.submissionCount ?? 0) === 1 ? "" : "s"}`;
|
||||
const actionsDisabled = !selectedEventId;
|
||||
const pollQuizMeta = !selectedEventId
|
||||
? "Select an event to create polls and quizzes"
|
||||
|
|
@ -677,6 +414,11 @@ export function EventHomeModules() {
|
|||
: rewardRulesCount === 0
|
||||
? "No rules configured"
|
||||
: `${rewardRulesCount} rule${rewardRulesCount === 1 ? "" : "s"} configured`;
|
||||
const agendaMeta = !selectedEventId
|
||||
? "Select an event to manage the agenda"
|
||||
: agendaLoading
|
||||
? "Loading…"
|
||||
: `${agendaCounts.tracks} track${agendaCounts.tracks === 1 ? "" : "s"} · ${agendaCounts.sessions} session${agendaCounts.sessions === 1 ? "" : "s"}`;
|
||||
|
||||
const handleSavePollQuiz = async (
|
||||
kind: "poll" | "quiz",
|
||||
|
|
@ -712,9 +454,9 @@ export function EventHomeModules() {
|
|||
}}
|
||||
>
|
||||
<ModuleCard
|
||||
icon={<FileText size={18} />}
|
||||
title={DEFAULT_REGISTRATION_FORM_NAME}
|
||||
meta={submissionMeta}
|
||||
icon={<CalendarDays size={18} />}
|
||||
title="Agenda"
|
||||
meta={agendaMeta}
|
||||
actions={
|
||||
<>
|
||||
<button
|
||||
|
|
@ -725,7 +467,7 @@ export function EventHomeModules() {
|
|||
opacity: actionsDisabled ? 0.55 : 1,
|
||||
cursor: actionsDisabled ? "not-allowed" : "pointer",
|
||||
}}
|
||||
onClick={() => setEditOpen(true)}
|
||||
onClick={() => setActiveNav("agenda")}
|
||||
onMouseEnter={(e) => {
|
||||
if (actionsDisabled) return;
|
||||
(e.currentTarget as HTMLElement).style.borderColor = "var(--primary)";
|
||||
|
|
@ -737,7 +479,7 @@ export function EventHomeModules() {
|
|||
(e.currentTarget as HTMLElement).style.color = "var(--card-foreground)";
|
||||
}}
|
||||
>
|
||||
Edit
|
||||
View Agenda
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
|
|
@ -747,56 +489,62 @@ export function EventHomeModules() {
|
|||
opacity: actionsDisabled ? 0.55 : 1,
|
||||
cursor: actionsDisabled ? "not-allowed" : "pointer",
|
||||
}}
|
||||
onClick={() => setResponsesOpen(true)}
|
||||
onClick={() => {
|
||||
setOpenAgendaSessionPanelOnce(true);
|
||||
setActiveNav("agenda");
|
||||
}}
|
||||
>
|
||||
Responses
|
||||
</button>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
<ModuleCard
|
||||
icon={<MessageSquare size={18} />}
|
||||
title={DEFAULT_FEEDBACK_FORM_NAME}
|
||||
meta={feedbackSubmissionMeta}
|
||||
actions={
|
||||
<>
|
||||
<button
|
||||
type="button"
|
||||
disabled={actionsDisabled}
|
||||
style={{
|
||||
...moduleButtonStyle(),
|
||||
opacity: actionsDisabled ? 0.55 : 1,
|
||||
cursor: actionsDisabled ? "not-allowed" : "pointer",
|
||||
}}
|
||||
onClick={() => setFeedbackEditOpen(true)}
|
||||
onMouseEnter={(e) => {
|
||||
if (actionsDisabled) return;
|
||||
(e.currentTarget as HTMLElement).style.borderColor = "var(--primary)";
|
||||
(e.currentTarget as HTMLElement).style.color = "var(--primary)";
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
if (actionsDisabled) return;
|
||||
(e.currentTarget as HTMLElement).style.borderColor = "var(--border)";
|
||||
(e.currentTarget as HTMLElement).style.color = "var(--card-foreground)";
|
||||
}}
|
||||
>
|
||||
Edit
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
disabled={actionsDisabled}
|
||||
style={{
|
||||
...moduleButtonStyle(true),
|
||||
opacity: actionsDisabled ? 0.55 : 1,
|
||||
cursor: actionsDisabled ? "not-allowed" : "pointer",
|
||||
}}
|
||||
onClick={() => setFeedbackResponsesOpen(true)}
|
||||
>
|
||||
View Responses
|
||||
New Session
|
||||
</button>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
{eventForms.map((form) => (
|
||||
<ModuleCard
|
||||
key={form._id}
|
||||
icon={<FileText size={18} />}
|
||||
title={form.name?.trim() || "Untitled form"}
|
||||
meta={formMetaLine(form)}
|
||||
actions={
|
||||
<>
|
||||
<button
|
||||
type="button"
|
||||
disabled={actionsDisabled}
|
||||
style={{
|
||||
...moduleButtonStyle(),
|
||||
opacity: actionsDisabled ? 0.55 : 1,
|
||||
cursor: actionsDisabled ? "not-allowed" : "pointer",
|
||||
}}
|
||||
onClick={() => setFormEditTarget(form)}
|
||||
onMouseEnter={(e) => {
|
||||
if (actionsDisabled) return;
|
||||
(e.currentTarget as HTMLElement).style.borderColor = "var(--primary)";
|
||||
(e.currentTarget as HTMLElement).style.color = "var(--primary)";
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
if (actionsDisabled) return;
|
||||
(e.currentTarget as HTMLElement).style.borderColor = "var(--border)";
|
||||
(e.currentTarget as HTMLElement).style.color = "var(--card-foreground)";
|
||||
}}
|
||||
>
|
||||
Edit
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
disabled={actionsDisabled}
|
||||
style={{
|
||||
...moduleButtonStyle(true),
|
||||
opacity: actionsDisabled ? 0.55 : 1,
|
||||
cursor: actionsDisabled ? "not-allowed" : "pointer",
|
||||
}}
|
||||
onClick={() => setFormResponsesTarget(form)}
|
||||
>
|
||||
Responses
|
||||
</button>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
))}
|
||||
<ModuleCard
|
||||
icon={<ShieldCheck size={18} />}
|
||||
title="Users & Permissions"
|
||||
|
|
@ -967,54 +715,100 @@ export function EventHomeModules() {
|
|||
</button>
|
||||
}
|
||||
/>
|
||||
<ModuleCard
|
||||
icon={<Plus size={18} />}
|
||||
title="Create New Form"
|
||||
description="Add a form assigned to this event"
|
||||
actions={
|
||||
<>
|
||||
<button
|
||||
type="button"
|
||||
disabled={actionsDisabled}
|
||||
style={{
|
||||
...moduleButtonStyle(),
|
||||
opacity: actionsDisabled ? 0.55 : 1,
|
||||
cursor: actionsDisabled ? "not-allowed" : "pointer",
|
||||
}}
|
||||
onClick={() => setNewFormMode("registration")}
|
||||
onMouseEnter={(e) => {
|
||||
if (actionsDisabled) return;
|
||||
(e.currentTarget as HTMLElement).style.borderColor = "var(--primary)";
|
||||
(e.currentTarget as HTMLElement).style.color = "var(--primary)";
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
if (actionsDisabled) return;
|
||||
(e.currentTarget as HTMLElement).style.borderColor = "var(--border)";
|
||||
(e.currentTarget as HTMLElement).style.color = "var(--card-foreground)";
|
||||
}}
|
||||
>
|
||||
Registration
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
disabled={actionsDisabled}
|
||||
style={{
|
||||
...moduleButtonStyle(),
|
||||
opacity: actionsDisabled ? 0.55 : 1,
|
||||
cursor: actionsDisabled ? "not-allowed" : "pointer",
|
||||
}}
|
||||
onClick={() => setNewFormMode("feedback")}
|
||||
onMouseEnter={(e) => {
|
||||
if (actionsDisabled) return;
|
||||
(e.currentTarget as HTMLElement).style.borderColor = "var(--primary)";
|
||||
(e.currentTarget as HTMLElement).style.color = "var(--primary)";
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
if (actionsDisabled) return;
|
||||
(e.currentTarget as HTMLElement).style.borderColor = "var(--border)";
|
||||
(e.currentTarget as HTMLElement).style.color = "var(--card-foreground)";
|
||||
}}
|
||||
>
|
||||
Feedback
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
disabled={actionsDisabled}
|
||||
style={{
|
||||
...moduleButtonStyle(true),
|
||||
opacity: actionsDisabled ? 0.55 : 1,
|
||||
cursor: actionsDisabled ? "not-allowed" : "pointer",
|
||||
}}
|
||||
onClick={() => setNewFormMode("other")}
|
||||
>
|
||||
Other
|
||||
</button>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</section>
|
||||
|
||||
{editOpen && selectedEventId ? (
|
||||
{formEditTarget && selectedEventId ? (
|
||||
<CreateFormPanel
|
||||
mode="registration"
|
||||
mode={formPanelMode(formEditTarget.type)}
|
||||
eventId={selectedEventId}
|
||||
formId={registrationForm?._id}
|
||||
defaultFormName={registrationForm ? undefined : DEFAULT_REGISTRATION_FORM_NAME}
|
||||
onClose={() => setEditOpen(false)}
|
||||
onSuccess={handleEditSuccess}
|
||||
formId={formEditTarget._id}
|
||||
onClose={() => setFormEditTarget(null)}
|
||||
onSuccess={handleFormEditSuccess}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{responsesOpen && selectedEventId && registrationForm?._id ? (
|
||||
{formResponsesTarget ? (
|
||||
<FormSubmissionsPanel
|
||||
formId={registrationForm._id}
|
||||
formName={formName}
|
||||
onClose={() => setResponsesOpen(false)}
|
||||
formId={formResponsesTarget._id}
|
||||
formName={formResponsesTarget.name?.trim() || "Form"}
|
||||
onClose={() => setFormResponsesTarget(null)}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{responsesOpen && selectedEventId && !registrationForm?._id ? (
|
||||
<DefaultRegistrationResponsesPanel formName={formName} onClose={() => setResponsesOpen(false)} />
|
||||
) : null}
|
||||
|
||||
{feedbackEditOpen && selectedEventId ? (
|
||||
{newFormMode && selectedEventId ? (
|
||||
<CreateFormPanel
|
||||
mode="feedback"
|
||||
mode={newFormMode}
|
||||
eventId={selectedEventId}
|
||||
formId={feedbackForm?._id}
|
||||
defaultFormName={feedbackForm ? undefined : DEFAULT_FEEDBACK_FORM_NAME}
|
||||
onClose={() => setFeedbackEditOpen(false)}
|
||||
onSuccess={handleFeedbackEditSuccess}
|
||||
onClose={() => setNewFormMode(null)}
|
||||
onSuccess={handleNewFormSuccess}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{feedbackResponsesOpen && selectedEventId && feedbackForm?._id ? (
|
||||
<FormSubmissionsPanel
|
||||
formId={feedbackForm._id}
|
||||
formName={feedbackFormName}
|
||||
onClose={() => setFeedbackResponsesOpen(false)}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{feedbackResponsesOpen && selectedEventId && !feedbackForm?._id ? (
|
||||
<DefaultFeedbackResponsesPanel formName={feedbackFormName} onClose={() => setFeedbackResponsesOpen(false)} />
|
||||
) : null}
|
||||
|
||||
{showAddPoll ? (
|
||||
<AddQuizPollModal
|
||||
kind="poll"
|
||||
|
|
|
|||
|
|
@ -1374,7 +1374,7 @@ export function EventsPage({ onNavigateToEvent }: { onNavigateToEvent?: (eventId
|
|||
if (eid) {
|
||||
setSelectedEventId(eid);
|
||||
setOpenAgendaSessionPanelOnce(true);
|
||||
setActiveNav("dashboard");
|
||||
setActiveNav("agenda");
|
||||
} else {
|
||||
setShowCreatePanel(true);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -81,7 +81,8 @@ export const ALL_PAGES: SearchPage[] = [
|
|||
{ id: "p14", navId: "payment", title: "Payment Settings", section: "Portfolio", description: "Configure payment gateways and billing", icon: <CreditCard size={14} /> },
|
||||
{ id: "p15", navId: "user-management", title: "User Management", section: "Portfolio", description: "Manage team members, roles, and permissions", icon: <Shield size={14} /> },
|
||||
{ id: "p16", navId: "settings", title: "Settings", section: "General", description: "Organisation, profile, and platform preferences", icon: <Settings size={14} /> },
|
||||
{ id: "p17", navId: "dashboard", title: "Event Home", section: "Event", description: "Tracks, sessions, and schedule for the event", icon: <LayoutGrid size={14} /> },
|
||||
{ id: "p17", navId: "dashboard", title: "Event Home", section: "Event", description: "Event overview, modules, and quick actions", icon: <LayoutGrid size={14} /> },
|
||||
{ id: "p17a", navId: "agenda", title: "Agenda", section: "Event", description: "Tracks, sessions, and schedule for the event", icon: <Calendar size={14} /> },
|
||||
{ id: "p18", navId: "analytics", title: "Event Analytics", section: "Event", description: "Live stats, charts, and feature overview", icon: <LineChart size={14} /> },
|
||||
{ id: "p19", navId: "participants", title: "Event Participants", section: "Event", description: "Registrations, check-ins, and attendance", icon: <Users size={14} /> },
|
||||
{ id: "p20", navId: "permissions", title: "Users & Permissions", section: "Event", description: "Manage event team members, roles, and session access", icon: <Shield size={14} /> },
|
||||
|
|
|
|||
Loading…
Reference in New Issue