mirror of
https://github.com/Sosokker/B2D-Ventures.git
synced 2025-12-19 22:14:06 +01:00
feat: add arrange meeting page
This commit is contained in:
parent
0b76ed64da
commit
c79bc4229e
128
src/app/calendar/MeetEventDialog.tsx
Normal file
128
src/app/calendar/MeetEventDialog.tsx
Normal file
@ -0,0 +1,128 @@
|
||||
"use client";
|
||||
|
||||
import React, { useState } from "react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Dialog,
|
||||
DialogClose,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "@/components/ui/dialog";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Clock } from "lucide-react";
|
||||
import { DateTimePicker, TimePicker } from "@/components/ui/datetime-picker";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { createCalendarEvent } from "./actions";
|
||||
import { Session } from "@supabase/supabase-js";
|
||||
|
||||
interface DialogProps {
|
||||
children?: React.ReactNode;
|
||||
open?: boolean;
|
||||
defaultOpen?: boolean;
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
onOpenChange?(open: boolean): void;
|
||||
modal?: boolean;
|
||||
session: Session;
|
||||
projectName: string;
|
||||
}
|
||||
|
||||
export function MeetEventDialog(props: DialogProps) {
|
||||
const [eventDate, setEventDate] = useState<Date | undefined>(undefined);
|
||||
const [startTime, setStartTime] = useState<Date | undefined>(undefined);
|
||||
const [endTime, setEndTime] = useState<Date | undefined>(undefined);
|
||||
const [eventName, setEventName] = useState(`Meet with ${props.projectName}`);
|
||||
const [eventDescription, setEventDescription] = useState(
|
||||
"Meet and gather more information on business in B2DVentures"
|
||||
);
|
||||
const [noteToBusiness, setNoteToBusiness] = useState<string>("");
|
||||
const session = props.session;
|
||||
|
||||
const handleCreateEvent = async () => {
|
||||
if (!session || !eventDate || !startTime || !endTime || !eventName) {
|
||||
alert("Please fill in all event details.");
|
||||
return;
|
||||
}
|
||||
|
||||
const startDate = new Date(eventDate);
|
||||
startDate.setHours(startTime.getHours(), startTime.getMinutes());
|
||||
|
||||
const endDate = new Date(eventDate);
|
||||
endDate.setHours(endTime.getHours(), endTime.getMinutes());
|
||||
await createCalendarEvent(session, startDate, endDate, eventName, eventDescription);
|
||||
props.onOpenChange?.(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog {...props}>
|
||||
<DialogContent className="sm:max-w-md">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="flex gap-2 items-center">
|
||||
<Clock />
|
||||
Arrange Meeting
|
||||
</DialogTitle>
|
||||
<DialogDescription>
|
||||
Arrange a meeting with the business you're interested in for more information.
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="space-y-4 w-[90%]">
|
||||
<div>
|
||||
<Label htmlFor="eventName">Event Name</Label>
|
||||
<Input
|
||||
id="eventName"
|
||||
placeholder="Enter event name"
|
||||
value={eventName}
|
||||
onChange={(e) => setEventName(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label htmlFor="eventDescription">Event Description</Label>
|
||||
<Input
|
||||
id="eventDescription"
|
||||
placeholder="Enter event description"
|
||||
value={eventDescription}
|
||||
onChange={(e) => setEventDescription(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label htmlFor="eventDescription">Event Description</Label>
|
||||
<Input
|
||||
id="note"
|
||||
placeholder="Your note to business"
|
||||
value={noteToBusiness}
|
||||
onChange={(e) => setNoteToBusiness(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label>Date</Label>
|
||||
<DateTimePicker granularity="day" hourCycle={24} value={eventDate} onChange={setEventDate} />
|
||||
</div>
|
||||
<div>
|
||||
<div>
|
||||
<Label>Start Time</Label>
|
||||
<TimePicker date={startTime} onChange={setStartTime} />
|
||||
</div>
|
||||
<div>
|
||||
<Label>End Time</Label>
|
||||
<TimePicker date={endTime} onChange={setEndTime} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<DialogFooter className="sm:justify-start mt-4">
|
||||
<Button type="button" onClick={handleCreateEvent} className="mr-2">
|
||||
Create Event
|
||||
</Button>
|
||||
<DialogClose asChild>
|
||||
<Button type="button" variant="secondary">
|
||||
Close
|
||||
</Button>
|
||||
</DialogClose>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
41
src/app/calendar/actions.ts
Normal file
41
src/app/calendar/actions.ts
Normal file
@ -0,0 +1,41 @@
|
||||
import { Session } from "@supabase/supabase-js";
|
||||
|
||||
export async function createCalendarEvent(
|
||||
session: Session,
|
||||
start: Date,
|
||||
end: Date,
|
||||
eventName: string,
|
||||
eventDescription: string
|
||||
) {
|
||||
console.log("Creating calendar event");
|
||||
|
||||
const event = {
|
||||
summary: eventName,
|
||||
description: eventDescription,
|
||||
start: {
|
||||
dateTime: start.toISOString(),
|
||||
timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
||||
},
|
||||
end: {
|
||||
dateTime: end.toISOString(),
|
||||
timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
||||
},
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await fetch("https://www.googleapis.com/calendar/v3/calendars/primary/events", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: "Bearer " + session.provider_token,
|
||||
},
|
||||
body: JSON.stringify(event),
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
console.log(data);
|
||||
alert("Event created, check your Google Calendar!");
|
||||
} catch (error) {
|
||||
console.error("Error creating calendar event:", error);
|
||||
alert("Failed to create the event.");
|
||||
}
|
||||
}
|
||||
@ -1,21 +1,145 @@
|
||||
"use client";
|
||||
import React, { useState } from "react";
|
||||
import { DateTimePicker } from "@/components/ui/datetime-picker";
|
||||
import { Label } from "@/components/ui/label";
|
||||
|
||||
import React, { useMemo, useState } from "react";
|
||||
import useSession from "@/lib/supabase/useSession";
|
||||
import { MeetEventDialog } from "./MeetEventDialog";
|
||||
import { Clock } from "lucide-react";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCaption,
|
||||
TableCell,
|
||||
TableHeader,
|
||||
TableHead,
|
||||
TableRow,
|
||||
TableFooter,
|
||||
} from "@/components/ui/table";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { useQuery } from "@supabase-cache-helpers/postgrest-react-query";
|
||||
import { getInvestmentByUserId } from "@/lib/data/investmentQuery";
|
||||
import { LegacyLoader } from "@/components/loading/LegacyLoader";
|
||||
import { createSupabaseClient } from "@/lib/supabase/clientComponentClient";
|
||||
|
||||
interface Investment {
|
||||
id: any;
|
||||
project_id: any;
|
||||
project_name: any;
|
||||
project_short_description: any;
|
||||
dataroom_id: any;
|
||||
deal_amount: any;
|
||||
investor_id: any;
|
||||
created_time: any;
|
||||
}
|
||||
|
||||
const DatetimePickerHourCycle = () => {
|
||||
const [date12, setDate12] = useState<Date | undefined>(undefined);
|
||||
const [date24, setDate24] = useState<Date | undefined>(undefined);
|
||||
const supabase = createSupabaseClient();
|
||||
const [showModal, setShowModal] = useState<boolean>(false);
|
||||
const [currentProjectName, setCurrentProjectName] = useState<string>("");
|
||||
const { session, loading } = useSession();
|
||||
|
||||
const {
|
||||
data: investments = [],
|
||||
error: investmentsError,
|
||||
isLoading: isLoadingInvestments,
|
||||
} = useQuery(getInvestmentByUserId(supabase, session?.user.id ?? ""), {
|
||||
enabled: !!session?.user.id,
|
||||
});
|
||||
|
||||
if (investmentsError) {
|
||||
throw "Error load investment data";
|
||||
}
|
||||
|
||||
const projectInvestments = useMemo(() => {
|
||||
if (!investments) return {};
|
||||
|
||||
return investments.reduce((acc: { [key: string]: Investment[] }, investment: Investment) => {
|
||||
const projectId = investment.project_id;
|
||||
if (!acc[projectId]) {
|
||||
acc[projectId] = [];
|
||||
}
|
||||
acc[projectId].push(investment);
|
||||
return acc;
|
||||
}, {});
|
||||
}, [investments]);
|
||||
|
||||
if (loading) {
|
||||
return <LegacyLoader />;
|
||||
}
|
||||
|
||||
if (!session || !session?.user.id) {
|
||||
throw "Can't load session!";
|
||||
}
|
||||
|
||||
if (isLoadingInvestments) {
|
||||
return <LegacyLoader />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-3 lg:flex-row">
|
||||
<div className="flex w-72 flex-col gap-2">
|
||||
<Label>12 Hour</Label>
|
||||
<DateTimePicker hourCycle={12} value={date12} onChange={setDate12} />
|
||||
</div>
|
||||
<div className="flex w-72 flex-col gap-2">
|
||||
<Label>24 Hour</Label>
|
||||
<DateTimePicker hourCycle={24} value={date24} onChange={setDate24} />
|
||||
<div className="container max-w-screen-xl">
|
||||
<span className="flex gap-2 items-center mt-4">
|
||||
<Clock />
|
||||
<p className="text-2xl font-bold">Schedule Meeting</p>
|
||||
</span>
|
||||
<Separator className="my-3" />
|
||||
<div className="space-y-2">
|
||||
{Object.entries(projectInvestments).map(([projectId, projectInvestments]) => (
|
||||
<Card key={projectId}>
|
||||
<CardHeader>
|
||||
<CardTitle className="text-xl">{projectInvestments[0].project_name}</CardTitle>
|
||||
<CardDescription>{projectInvestments[0].project_short_description}</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<Table>
|
||||
<TableCaption>Investments for this project</TableCaption>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead className="w-[100px]">Investment Id</TableHead>
|
||||
<TableHead>Invested At</TableHead>
|
||||
<TableHead className="text-right">Amount</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{projectInvestments.map((investment) => (
|
||||
<TableRow key={investment.id}>
|
||||
<TableCell className="font-medium">{investment.id}</TableCell>
|
||||
<TableCell>{investment.created_time}</TableCell>
|
||||
<TableCell className="text-right">{investment.deal_amount}</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
<TableFooter>
|
||||
<TableRow>
|
||||
<TableCell colSpan={2}>Total</TableCell>
|
||||
<TableCell className="text-right">
|
||||
{projectInvestments
|
||||
.reduce((total, investment) => total + (parseFloat(investment.deal_amount) || 0), 0)
|
||||
.toLocaleString("en-US", { style: "currency", currency: "USD" })}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableFooter>
|
||||
</Table>
|
||||
</CardContent>
|
||||
<CardFooter>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setCurrentProjectName(projectInvestments[0].project_name);
|
||||
setShowModal(true);
|
||||
}}
|
||||
>
|
||||
Schedule Meeting
|
||||
</Button>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
<MeetEventDialog
|
||||
open={showModal}
|
||||
onOpenChange={setShowModal}
|
||||
session={session}
|
||||
projectName={currentProjectName}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@ -12,6 +12,7 @@ export function LoginButton(props: { nextUrl?: string }) {
|
||||
provider: "google",
|
||||
options: {
|
||||
redirectTo: `${location.origin}/auth/callback?next=${props.nextUrl || ""}`,
|
||||
scopes: "https://www.googleapis.com/auth/calendar",
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
@ -12,6 +12,7 @@ export function SignupButton(props: { nextUrl?: string }) {
|
||||
provider: "google",
|
||||
options: {
|
||||
redirectTo: `${location.origin}/auth/callback?next=${props.nextUrl || ""}`,
|
||||
scopes: "https://www.googleapis.com/auth/calendar",
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
Loading…
Reference in New Issue
Block a user