Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 117 additions & 0 deletions apps/web/app/(app)/[emailAccountId]/briefs/Onboarding.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
"use client";

import {
Card,
CardContent,
CardHeader,
CardFooter,
CardTitle,
CardDescription,
} from "@/components/ui/card";
import {
SectionHeader,
SectionDescription,
MessageText,
} from "@/components/Typography";
import { ConnectCalendar } from "@/app/(app)/[emailAccountId]/calendars/ConnectCalendar";
import { IconCircle } from "@/app/(app)/[emailAccountId]/onboarding/IconCircle";
import {
User,
Mail,
Lightbulb,
UserIcon,
MailIcon,
LightbulbIcon,
InboxIcon,
} from "lucide-react";
import { Button } from "@/components/ui/button";
import {
Item,
ItemActions,
ItemContent,
ItemDescription,
ItemGroup,
ItemMedia,
ItemTitle,
} from "@/components/ui/item";
Comment on lines +3 to +36
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Remove unused imports.

Several imports are unused and should be removed to comply with the project's coding guidelines:

  • SectionHeader (line 12)
  • SectionDescription (line 13)
  • IconCircle (line 17)
  • User, Mail, Lightbulb (lines 19-21) — the *Icon variants are used instead
  • InboxIcon (line 25)
🔎 Proposed fix
 import {
   Card,
   CardContent,
   CardHeader,
   CardFooter,
   CardTitle,
   CardDescription,
 } from "@/components/ui/card";
 import {
-  SectionHeader,
-  SectionDescription,
   MessageText,
 } from "@/components/Typography";
 import { ConnectCalendar } from "@/app/(app)/[emailAccountId]/calendars/ConnectCalendar";
-import { IconCircle } from "@/app/(app)/[emailAccountId]/onboarding/IconCircle";
 import {
-  User,
-  Mail,
-  Lightbulb,
   UserIcon,
   MailIcon,
   LightbulbIcon,
-  InboxIcon,
 } from "lucide-react";
🤖 Prompt for AI Agents
In apps/web/app/(app)/[emailAccountId]/briefs/Onboarding.tsx around lines 3 to
36, there are several unused imports listed in the review (SectionHeader,
SectionDescription, IconCircle, User, Mail, Lightbulb, InboxIcon); remove those
unused named imports from their respective import statements so only the
actually used symbols remain (keep the *Icon variants and other used components
like Card, Button, Item pieces and ConnectCalendar), update the import lines to
a minimal set and run a quick lint/type check to ensure no other references
remain.


export function BriefsOnboarding({
emailAccountId,
hasCalendarConnected = false,
onEnable,
isEnabling = false,
}: {
emailAccountId: string;
hasCalendarConnected?: boolean;
onEnable?: () => void;
isEnabling?: boolean;
}) {
return (
<div className="mx-4 mt-10 max-w-2xl md:mx-auto">
<CardHeader>
<CardTitle>Meeting Briefs</CardTitle>
<CardDescription>
Receive email briefings before meetings with external guests.
</CardDescription>
</CardHeader>

<CardContent>
<ItemGroup className="grid gap-2">
<Item variant="outline">
<ItemMedia variant="icon" className="bg-blue-50">
<UserIcon className="text-blue-600" />
</ItemMedia>
<ItemContent>
<ItemTitle>Attendee research</ItemTitle>
<ItemDescription>
Who they are, their company, and role
</ItemDescription>
</ItemContent>
</Item>
<Item variant="outline">
<ItemMedia variant="icon" className="bg-blue-50">
<MailIcon className="text-blue-600" />
</ItemMedia>
<ItemContent>
<ItemTitle>Email history</ItemTitle>
<ItemDescription>
Recent conversations with this person
</ItemDescription>
</ItemContent>
</Item>
<Item variant="outline">
<ItemMedia variant="icon" className="bg-blue-50">
<LightbulbIcon className="text-blue-600" />
</ItemMedia>
<ItemContent>
<ItemTitle>Key context</ItemTitle>
<ItemDescription>
Important details from past discussions
</ItemDescription>
</ItemContent>
</Item>
</ItemGroup>
</CardContent>

<CardFooter className="flex flex-col items-center gap-4">
{hasCalendarConnected ? (
<>
<MessageText>
You're all set! Enable meeting briefs to get started:
</MessageText>
<Button onClick={onEnable} loading={isEnabling}>
Enable Meeting Briefs
</Button>
</>
) : (
<>
<MessageText>Connect your calendar to get started:</MessageText>
<ConnectCalendar
onboardingReturnPath={`/${emailAccountId}/briefs`}
/>
</>
)}
</CardFooter>
</div>
);
Comment thread
elie222 marked this conversation as resolved.
}
199 changes: 199 additions & 0 deletions apps/web/app/(app)/[emailAccountId]/briefs/UpcomingMeetings.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
"use client";

import { useCallback, useState } from "react";
import { format, formatDistanceToNow } from "date-fns";
import { CalendarIcon, SendIcon } from "lucide-react";
import { Button } from "@/components/ui/button";
import { toastSuccess, toastError } from "@/components/Toast";
import { LoadingContent } from "@/components/LoadingContent";
import { useAction } from "next-safe-action/hooks";
import { sendBriefAction } from "@/utils/actions/meeting-briefs";
import { useMeetingBriefsHistory } from "@/hooks/useMeetingBriefs";
import { useCalendarUpcomingEvents } from "@/hooks/useCalendarUpcomingEvents";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import {
Item,
ItemContent,
ItemTitle,
ItemDescription,
ItemActions,
ItemGroup,
ItemMedia,
} from "@/components/ui/item";
import { TypographyH3 } from "@/components/Typography";
import { ConfirmDialog } from "@/components/ConfirmDialog";
import { Skeleton } from "@/components/ui/skeleton";

export function UpcomingMeetings({
emailAccountId,
}: {
emailAccountId: string;
}) {
const { data, isLoading, error } = useCalendarUpcomingEvents();
const [sendingEventId, setSendingEventId] = useState<string | null>(null);

const { execute } = useAction(sendBriefAction.bind(null, emailAccountId), {
onSuccess: ({ data: result }) => {
toastSuccess({
description: result.message || "Test brief sent!",
});
},
onError: ({ error }) => {
toastError({
description: error.serverError || "Failed to send brief",
});
},
onSettled: () => {
setSendingEventId(null);
},
});

const handleSendTestBrief = useCallback(
(event: NonNullable<typeof data>["events"][number]) => {
setSendingEventId(event.id);
execute({
event: {
id: event.id,
title: event.title,
description: event.description,
location: event.location,
eventUrl: event.eventUrl,
videoConferenceLink: event.videoConferenceLink,
startTime: new Date(event.startTime).toISOString(),
endTime: new Date(event.endTime).toISOString(),
attendees: event.attendees,
},
});
},
[execute],
);

return (
<>
<TypographyH3>Upcoming Meetings</TypographyH3>

<LoadingContent loading={isLoading} error={error}>
{!data?.events.length ? (
<Item variant="outline" className="mt-4">
<ItemMedia>
<CalendarIcon className="size-4" />
</ItemMedia>
<ItemContent>
<ItemTitle>No upcoming calendar events found</ItemTitle>
</ItemContent>
</Item>
) : (
<>
<ItemGroup className="mt-4 gap-2">
{data?.events.map((event) => (
<Item key={event.id} variant="outline">
<ItemContent>
<ItemTitle>{event.title}</ItemTitle>
<ItemDescription>
{format(
new Date(event.startTime),
"EEE, MMM d 'at' h:mm a",
)}
</ItemDescription>
</ItemContent>
<ItemActions>
<ConfirmDialog
trigger={
<Button
variant="outline"
size="sm"
Icon={SendIcon}
loading={sendingEventId === event.id}
>
Send test brief
</Button>
}
title="Send test brief?"
description="This will send you a briefing email for this meeting now. Use this to verify briefs are working correctly."
confirmText="Send"
onConfirm={() => handleSendTestBrief(event)}
/>
</ItemActions>
</Item>
))}
</ItemGroup>

<div className="mt-4">
<SendHistoryLink />
</div>
</>
)}
</LoadingContent>
</>
);
}

function SendHistoryLink() {
const { data, isLoading, error } = useMeetingBriefsHistory();

return (
<Dialog>
<DialogTrigger asChild>
<Button variant="link" className="h-auto p-0 text-muted-foreground">
View send history →
</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Send History</DialogTitle>
</DialogHeader>

<LoadingContent
loading={isLoading}
error={error}
loadingComponent={<Skeleton className="h-10 w-full" />}
>
{!data?.briefings.length ? (
<Item variant="outline" className="mt-4">
<ItemMedia>
<CalendarIcon className="size-4" />
</ItemMedia>
<ItemContent>
<ItemTitle>No briefings have been sent yet</ItemTitle>
</ItemContent>
</Item>
) : (
<ItemGroup className="mt-2 gap-2">
{data?.briefings.map((briefing) => (
<Item key={briefing.id} variant="outline">
<ItemContent>
<ItemTitle>{briefing.eventTitle}</ItemTitle>
<ItemDescription>
{briefing.guestCount} guest
{briefing.guestCount !== 1 ? "s" : ""} •{" "}
{formatDistanceToNow(new Date(briefing.createdAt), {
addSuffix: true,
})}
</ItemDescription>
</ItemContent>
<ItemActions>
<span
className={`text-xs px-2 py-1 rounded ${
briefing.status === "SENT"
? "bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200"
: "bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200"
}`}
>
{briefing.status}
</span>
</ItemActions>
</Item>
))}
</ItemGroup>
)}
</LoadingContent>
</DialogContent>
</Dialog>
);
}
Loading
Loading