Skip to content

Commit 80b9354

Browse files
author
Rajat Saxena
committed
Analytics queries in mail logic; EmailAnalytics component; EmailAnalytics comp. integration with broadcast mail screen
1 parent aef2fef commit 80b9354

File tree

6 files changed

+521
-170
lines changed

6 files changed

+521
-170
lines changed

apps/web/app/(with-contexts)/dashboard/(sidebar)/mails/broadcast/[id]/page.tsx

Lines changed: 170 additions & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,19 @@ import DashboardContent from "@components/admin/dashboard-content";
44
import { isDateInFuture } from "@/lib/utils";
55
import { AddressContext } from "@components/contexts";
66
import { BROADCASTS } from "@ui-config/strings";
7-
import { useContext } from "react";
7+
import { useContext, useState } from "react";
88
import { PaperPlane, Clock } from "@courselit/icons";
99
import {
1010
Form,
1111
FormField,
1212
Dialog2,
1313
useToast,
14+
Tabbs,
1415
} from "@courselit/components-library";
1516
import {
1617
ChangeEvent,
1718
FormEvent,
1819
useEffect,
19-
useState,
2020
useRef,
2121
useCallback,
2222
useMemo,
@@ -48,6 +48,8 @@ import { useGraphQLFetch } from "@/hooks/use-graphql-fetch";
4848
import FilterContainer from "@components/admin/users/filter-container";
4949
import EmailViewer from "@components/admin/mails/email-viewer";
5050
import { Button } from "@components/ui/button";
51+
import { truncate } from "@courselit/utils";
52+
import EmailAnalytics from "@components/admin/mails/email-analytics";
5153

5254
const breadcrumbs = [
5355
{ label: BROADCASTS, href: "/dashboard/mails?tab=Broadcasts" },
@@ -77,6 +79,7 @@ export default function Page({
7779
const [confirmationDialogOpen, setConfirmationDialogOpen] = useState(false);
7880
const [report, setReport] = useState<SequenceReport>();
7981
const [status, setStatus] = useState<SequenceStatus | null>(null);
82+
const [activeTab, setActiveTab] = useState("Compose");
8083

8184
// Refs to track initial values and prevent saving during load
8285
const initialValues = useRef({
@@ -501,147 +504,181 @@ export default function Page({
501504

502505
return (
503506
<DashboardContent breadcrumbs={breadcrumbs}>
504-
<div className="flex flex-col gap-4">
505-
<div className="flex items-center justify-between">
506-
<h1 className="text-4xl font-semibold mb-4">
507-
{PAGE_HEADER_EDIT_MAIL}
508-
</h1>
509-
</div>
510-
<fieldset>
511-
<label className="mb-1 font-medium">To</label>
512-
{!isInitialLoad.current && (
513-
<FilterContainer
514-
filter={{ aggregator: filtersAggregator, filters }}
515-
onChange={onFilterChange}
516-
disabled={!isEditable}
517-
address={address}
518-
/>
519-
)}
520-
</fieldset>
521-
<Form className="flex flex-col gap-4" onSubmit={onSubmit}>
522-
<FormField
523-
value={subject}
524-
disabled={!isEditable}
525-
label={MAIL_SUBJECT_PLACEHOLDER}
526-
onChange={(e: ChangeEvent<HTMLInputElement>) =>
527-
setSubject(e.target.value)
528-
}
529-
/>
530-
<EmailViewer
531-
content={content}
532-
emailEditorLink={
533-
isEditable
534-
? `/dashboard/mail/sequence/${id}/${emailId}?redirectTo=/dashboard/mails/broadcast/${id}`
535-
: ""
536-
}
537-
/>
538-
{showScheduleInput && (
539-
<FormField
540-
value={new Date(
541-
(delay || new Date().getTime()) -
542-
new Date().getTimezoneOffset() * 60000,
543-
)
544-
.toISOString()
545-
.slice(0, 16)}
546-
type="datetime-local"
547-
label={FORM_MAIL_SCHEDULE_TIME_LABEL}
548-
min={new Date().toISOString().slice(0, 16)}
549-
disabled={!isEditable}
550-
onChange={(e: ChangeEvent<HTMLInputElement>) => {
551-
const selectedDate = new Date(e.target.value);
552-
setDelay(selectedDate.getTime());
553-
}}
554-
/>
555-
)}
556-
{isEditable && (
557-
<div className="flex gap-2">
558-
{!showScheduleInput && (
507+
<div className="flex items-center justify-between mb-4">
508+
<h1 className="text-4xl font-semibold">
509+
{truncate(subject || PAGE_HEADER_EDIT_MAIL, 50)}
510+
</h1>
511+
</div>
512+
<Tabbs
513+
items={["Compose", "Analytics"]}
514+
value={activeTab}
515+
onChange={setActiveTab}
516+
>
517+
<div className="mt-4">
518+
<div className="flex flex-col gap-4">
519+
<fieldset>
520+
<label className="mb-1 font-medium">To</label>
521+
{!isInitialLoad.current && (
522+
<FilterContainer
523+
filter={{
524+
aggregator: filtersAggregator,
525+
filters,
526+
}}
527+
onChange={onFilterChange}
528+
disabled={!isEditable}
529+
address={address}
530+
/>
531+
)}
532+
</fieldset>
533+
<Form
534+
className="flex flex-col gap-4"
535+
onSubmit={onSubmit}
536+
>
537+
<FormField
538+
value={subject}
539+
disabled={!isEditable}
540+
label={MAIL_SUBJECT_PLACEHOLDER}
541+
onChange={(e: ChangeEvent<HTMLInputElement>) =>
542+
setSubject(e.target.value)
543+
}
544+
/>
545+
<EmailViewer
546+
content={content}
547+
emailEditorLink={
548+
isEditable
549+
? `/dashboard/mail/sequence/${id}/${emailId}?redirectTo=/dashboard/mails/broadcast/${id}`
550+
: ""
551+
}
552+
/>
553+
{showScheduleInput && (
554+
<FormField
555+
value={new Date(
556+
(delay || new Date().getTime()) -
557+
new Date().getTimezoneOffset() *
558+
60000,
559+
)
560+
.toISOString()
561+
.slice(0, 16)}
562+
type="datetime-local"
563+
label={FORM_MAIL_SCHEDULE_TIME_LABEL}
564+
min={new Date().toISOString().slice(0, 16)}
565+
disabled={!isEditable}
566+
onChange={(
567+
e: ChangeEvent<HTMLInputElement>,
568+
) => {
569+
const selectedDate = new Date(
570+
e.target.value,
571+
);
572+
setDelay(selectedDate.getTime());
573+
}}
574+
/>
575+
)}
576+
{isEditable && (
559577
<div className="flex gap-2">
560-
<Dialog2
561-
open={confirmationDialogOpen}
562-
onOpenChange={setConfirmationDialogOpen}
563-
title={`${DIALOG_SEND_HEADER} to ${filteredUsersCount} contacts?`}
564-
trigger={
565-
<Button>
566-
<div className="flex items-center gap-2">
567-
<PaperPlane />
568-
{BTN_SEND}
578+
{!showScheduleInput && (
579+
<div className="flex gap-2">
580+
<Dialog2
581+
open={confirmationDialogOpen}
582+
onOpenChange={
583+
setConfirmationDialogOpen
584+
}
585+
title={`${DIALOG_SEND_HEADER} to ${filteredUsersCount} contacts?`}
586+
trigger={
587+
<Button>
588+
<div className="flex items-center gap-2">
589+
<PaperPlane />
590+
{BTN_SEND}
591+
</div>
592+
</Button>
593+
}
594+
onClick={onSubmit}
595+
>
596+
<p>
597+
Are you sure you want to
598+
send this email to{" "}
599+
{filteredUsersCount}{" "}
600+
contacts?
601+
</p>
602+
</Dialog2>
603+
<Button
604+
variant="ghost"
605+
className="gap-2"
606+
onClick={() => {
607+
setShowScheduleInput(true);
608+
}}
609+
>
610+
<Clock />
611+
{BTN_SCHEDULE}
612+
</Button>
613+
</div>
614+
)}
615+
{showScheduleInput && (
616+
<>
617+
<Dialog2
618+
title={`${DIALOG_SEND_HEADER} to ${filteredUsersCount} contacts?`}
619+
open={confirmationDialogOpen}
620+
onOpenChange={
621+
setConfirmationDialogOpen
622+
}
623+
trigger={
624+
<Button>
625+
<div className="flex items-center gap-2">
626+
<Clock />
627+
{BTN_SCHEDULE}
628+
</div>
629+
</Button>
630+
}
631+
onClick={(e) =>
632+
onSubmit(e, true)
633+
}
634+
>
635+
<div className="p-4">
636+
<p>
637+
Are you sure you want to
638+
schedule this email to{" "}
639+
{filteredUsersCount}{" "}
640+
contacts?
641+
</p>
569642
</div>
643+
</Dialog2>
644+
<Button
645+
variant="secondary"
646+
onClick={(e) => {
647+
e.preventDefault();
648+
setShowScheduleInput(false);
649+
setDelay(0);
650+
}}
651+
>
652+
{BUTTON_CANCEL_TEXT}
570653
</Button>
571-
}
572-
onClick={onSubmit}
573-
>
574-
<p>
575-
Are you sure you want to send this
576-
email to {filteredUsersCount}{" "}
577-
contacts?
578-
</p>
579-
</Dialog2>
580-
<Button
581-
variant="ghost"
582-
className="gap-2"
583-
onClick={() => {
584-
setShowScheduleInput(true);
585-
}}
586-
>
587-
<Clock />
588-
{BTN_SCHEDULE}
589-
</Button>
654+
</>
655+
)}
590656
</div>
591657
)}
592-
{showScheduleInput && (
593-
<>
594-
<Dialog2
595-
title={`${DIALOG_SEND_HEADER} to ${filteredUsersCount} contacts?`}
596-
open={confirmationDialogOpen}
597-
onOpenChange={setConfirmationDialogOpen}
598-
trigger={
599-
<Button>
600-
<div className="flex items-center gap-2">
601-
<Clock />
602-
{BTN_SCHEDULE}
603-
</div>
604-
</Button>
605-
}
606-
onClick={(e) => onSubmit(e, true)}
607-
>
608-
<div className="p-4">
609-
<p>
610-
Are you sure you want to
611-
schedule this email to{" "}
612-
{filteredUsersCount} contacts?
613-
</p>
614-
</div>
615-
</Dialog2>
658+
</Form>
659+
{status === "active" &&
660+
isDateInFuture(new Date(delay)) &&
661+
!report?.broadcast?.lockedAt && (
662+
<div>
663+
<p className="flex items-center gap-2 text-sm mb-4 font-semibold text-slate-600">
664+
<Clock /> Scheduled for{" "}
665+
{new Date(delay).toLocaleString()}
666+
</p>
616667
<Button
617668
variant="secondary"
618-
onClick={(e) => {
619-
e.preventDefault();
620-
setShowScheduleInput(false);
621-
setDelay(0);
622-
}}
669+
onClick={cancelSending}
623670
>
624-
{BUTTON_CANCEL_TEXT}
671+
{BUTTON_CANCEL_SCHEDULED_MAIL}
625672
</Button>
626-
</>
673+
</div>
627674
)}
628-
</div>
629-
)}
630-
</Form>
631-
{status === "active" &&
632-
isDateInFuture(new Date(delay)) &&
633-
!report?.broadcast?.lockedAt && (
634-
<div>
635-
<p className="flex items-center gap-2 text-sm mb-4 font-semibold text-slate-600">
636-
<Clock /> Scheduled for{" "}
637-
{new Date(delay).toLocaleString()}
638-
</p>
639-
<Button variant="secondary" onClick={cancelSending}>
640-
{BUTTON_CANCEL_SCHEDULED_MAIL}
641-
</Button>
642-
</div>
643-
)}
644-
</div>
675+
</div>
676+
</div>
677+
<div className="mt-4">
678+
<h2 className="text-2xl font-semibold mb-4">Analytics</h2>
679+
<EmailAnalytics sequence={sequence} report={report} />
680+
</div>
681+
</Tabbs>
645682
</DashboardContent>
646683
);
647684
}

0 commit comments

Comments
 (0)