feature: store meeting log and check for submitting state in calendar

This commit is contained in:
Sosokker 2024-11-14 11:22:19 +07:00
parent b84dde1044
commit 4ed90281ce
7 changed files with 1243 additions and 52 deletions

1
.prettierignore Normal file
View File

@ -0,0 +1 @@
/tailwind.config.ts

1147
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -12,6 +12,10 @@
"dependencies": { "dependencies": {
"@hookform/resolvers": "^3.9.0", "@hookform/resolvers": "^3.9.0",
"@mdxeditor/editor": "^3.15.0", "@mdxeditor/editor": "^3.15.0",
"@nextui-org/calendar": "^2.0.12",
"@nextui-org/date-input": "^2.1.4",
"@nextui-org/system": "^2.2.6",
"@nextui-org/theme": "^2.2.11",
"@radix-ui/react-alert-dialog": "^1.1.2", "@radix-ui/react-alert-dialog": "^1.1.2",
"@radix-ui/react-avatar": "^1.1.0", "@radix-ui/react-avatar": "^1.1.0",
"@radix-ui/react-checkbox": "^1.1.2", "@radix-ui/react-checkbox": "^1.1.2",
@ -34,6 +38,7 @@
"@supabase-cache-helpers/postgrest-react-query": "^1.10.1", "@supabase-cache-helpers/postgrest-react-query": "^1.10.1",
"@supabase/ssr": "^0.4.1", "@supabase/ssr": "^0.4.1",
"@supabase/supabase-js": "^2.46.1", "@supabase/supabase-js": "^2.46.1",
"@tailwindcss/line-clamp": "^0.4.4",
"@tanstack/react-query": "^5.59.0", "@tanstack/react-query": "^5.59.0",
"@tanstack/react-query-devtools": "^5.59.0", "@tanstack/react-query-devtools": "^5.59.0",
"b2d-ventures": "file:", "b2d-ventures": "file:",
@ -41,16 +46,17 @@
"class-variance-authority": "^0.7.0", "class-variance-authority": "^0.7.0",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"cmdk": "1.0.0", "cmdk": "1.0.0",
"date-fns": "^4.1.0", "date-fns": "^3.0.0",
"dotenv": "^16.4.5", "dotenv": "^16.4.5",
"embla-carousel-react": "^8.2.0", "embla-carousel-react": "^8.2.0",
"framer-motion": "^11.11.15",
"lucide-react": "^0.428.0", "lucide-react": "^0.428.0",
"next": "^14.2.15", "next": "^14.2.15",
"next-themes": "^0.3.0", "next-themes": "^0.3.0",
"react": "^18.3.1", "react": "^18.3.1",
"react-chartjs-2": "^5.2.0", "react-chartjs-2": "^5.2.0",
"react-countup": "^6.5.3", "react-countup": "^6.5.3",
"react-day-picker": "^9", "react-day-picker": "^8.10.1",
"react-dom": "^18", "react-dom": "^18",
"react-file-icon": "^1.5.0", "react-file-icon": "^1.5.0",
"react-hook-form": "^7.53.0", "react-hook-form": "^7.53.0",

View File

@ -1,6 +1,6 @@
"use client"; "use client";
import React, { useState } from "react"; import React, { useState, useEffect } from "react";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { import {
Dialog, Dialog,
@ -13,10 +13,15 @@ import {
} from "@/components/ui/dialog"; } from "@/components/ui/dialog";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Clock } from "lucide-react"; import { Clock } from "lucide-react";
import { DateTimePicker, TimePicker } from "@/components/ui/datetime-picker";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
import { createCalendarEvent } from "./actions"; import { createCalendarEvent, createMeetingLog } from "./actions";
import { Session } from "@supabase/supabase-js"; import { Session } from "@supabase/supabase-js";
import { createSupabaseClient } from "@/lib/supabase/clientComponentClient";
import { TimeInput } from "@nextui-org/date-input";
import { Calendar } from "@nextui-org/calendar";
import { TimeValue } from "@react-types/datepicker";
import { CalendarDate, getLocalTimeZone, today } from "@internationalized/date";
import toast from "react-hot-toast";
interface DialogProps { interface DialogProps {
children?: React.ReactNode; children?: React.ReactNode;
@ -27,37 +32,75 @@ interface DialogProps {
modal?: boolean; modal?: boolean;
session: Session; session: Session;
projectName: string; projectName: string;
projectId?: number;
} }
export function MeetEventDialog(props: DialogProps) { export function MeetEventDialog(props: DialogProps) {
const [eventDate, setEventDate] = useState<Date | undefined>(undefined); const supabase = createSupabaseClient();
const [startTime, setStartTime] = useState<Date | undefined>(undefined); const timezone = getLocalTimeZone();
const [endTime, setEndTime] = useState<Date | undefined>(undefined); const [eventDate, setEventDate] = useState<CalendarDate | undefined>(undefined);
const [startTime, setStartTime] = useState<TimeValue | undefined>(undefined);
const [endTime, setEndTime] = useState<TimeValue | undefined>(undefined);
const [eventName, setEventName] = useState(`Meet with ${props.projectName}`); const [eventName, setEventName] = useState(`Meet with ${props.projectName}`);
const [eventDescription, setEventDescription] = useState( const [eventDescription, setEventDescription] = useState(
"Meet and gather more information on business in B2DVentures" "Meet and gather more information on business in B2DVentures"
); );
const [isSubmitting, setIsSubmitting] = useState<boolean>(false);
const [noteToBusiness, setNoteToBusiness] = useState<string>(""); const [noteToBusiness, setNoteToBusiness] = useState<string>("");
const session = props.session; const session = props.session;
useEffect(() => {
if (props.projectName) {
setEventName(`Meet with ${props.projectName}`);
}
}, [props.projectName]);
const handleCreateEvent = async () => { const handleCreateEvent = async () => {
if (!session || !eventDate || !startTime || !endTime || !eventName) { if (!session || !eventDate || !startTime || !endTime || !eventName) {
alert("Please fill in all event details."); toast.error("Please fill in all event details.");
return; return;
} }
const startDate = new Date(eventDate); setIsSubmitting(true);
startDate.setHours(startTime.getHours(), startTime.getMinutes());
try {
const startDate = eventDate.toDate(timezone);
startDate.setHours(startTime.hour, startTime.minute);
const endDate = eventDate.toDate(timezone);
endDate.setHours(endTime.hour, startTime.minute);
const endDate = new Date(eventDate);
endDate.setHours(endTime.getHours(), endTime.getMinutes());
await createCalendarEvent(session, startDate, endDate, eventName, eventDescription); await createCalendarEvent(session, startDate, endDate, eventName, eventDescription);
const { status, error } = await createMeetingLog({
client: supabase,
meet_date: eventDate.toString().split("T")[0],
start_time: startTime.toString(),
end_time: endTime.toString(),
note: noteToBusiness,
userId: session.user.id,
projectId: props.projectId!,
});
if (!status) {
console.error("Meeting log error:", error);
toast.error("Failed to log the meeting. Please try again.");
return;
}
toast.success("Meeting event created successfully!");
props.onOpenChange?.(false); props.onOpenChange?.(false);
} catch (error) {
console.error("Error creating event:", error);
toast.error("There was an error creating the event. Please try again.");
} finally {
setIsSubmitting(false);
}
}; };
return ( return (
<Dialog {...props}> <Dialog {...props}>
<DialogContent className="sm:max-w-md"> <DialogContent className="sm:max-w-md overflow-y-auto h-[80%]">
<DialogHeader> <DialogHeader>
<DialogTitle className="flex gap-2 items-center"> <DialogTitle className="flex gap-2 items-center">
<Clock /> <Clock />
@ -98,23 +141,23 @@ export function MeetEventDialog(props: DialogProps) {
</div> </div>
<div> <div>
<Label>Date</Label> <Label>Date</Label>
<DateTimePicker granularity="day" hourCycle={24} value={eventDate} onChange={setEventDate} /> <Calendar value={eventDate} onChange={setEventDate} minValue={today(getLocalTimeZone())} />
</div> </div>
<div> <div>
<div> <div>
<Label>Start Time</Label> <Label>Start Time</Label>
<TimePicker date={startTime} onChange={setStartTime} /> <TimeInput label="Start Time" value={startTime} onChange={setStartTime} />
</div> </div>
<div> <div>
<Label>End Time</Label> <Label>End Time</Label>
<TimePicker date={endTime} onChange={setEndTime} /> <TimeInput label="End Time" value={endTime} onChange={setEndTime} />
</div> </div>
</div> </div>
</div> </div>
<DialogFooter className="sm:justify-start mt-4"> <DialogFooter className="sm:justify-start mt-4">
<Button type="button" onClick={handleCreateEvent} className="mr-2"> <Button type="button" onClick={handleCreateEvent} className="mr-2" disabled={isSubmitting}>
Create Event {isSubmitting ? "Creating..." : "Create Event"}
</Button> </Button>
<DialogClose asChild> <DialogClose asChild>
<Button type="button" variant="secondary"> <Button type="button" variant="secondary">

View File

@ -1,4 +1,4 @@
import { Session } from "@supabase/supabase-js"; import { Session, SupabaseClient } from "@supabase/supabase-js";
export async function createCalendarEvent( export async function createCalendarEvent(
session: Session, session: Session,
@ -33,9 +33,40 @@ export async function createCalendarEvent(
const data = await response.json(); const data = await response.json();
console.log(data); console.log(data);
alert("Event created, check your Google Calendar!");
} catch (error) { } catch (error) {
console.error("Error creating calendar event:", error); console.error("Error creating calendar event:", error);
alert("Failed to create the event.");
} }
} }
interface CreateMeetingLogProps {
client: SupabaseClient;
userId: string;
projectId: number;
meet_date: string;
start_time: string;
end_time: string;
note: string;
}
export async function createMeetingLog({
client,
userId,
projectId,
meet_date,
start_time,
end_time,
note,
}: CreateMeetingLogProps) {
const { error } = await client.from("meeting_log").insert([
{
meet_date: meet_date, // Format date as YYYY-MM-DD
start_time: start_time, // Format time as HH:MM:SS
end_time: end_time, // Format time as HH:MM:SS
note: note, // Text for meeting notes
user_id: userId, // Replace with a valid UUID
project_id: projectId,
},
]);
return error ? { status: false, error } : { status: true, error: null };
}

View File

@ -37,6 +37,7 @@ const DatetimePickerHourCycle = () => {
const supabase = createSupabaseClient(); const supabase = createSupabaseClient();
const [showModal, setShowModal] = useState<boolean>(false); const [showModal, setShowModal] = useState<boolean>(false);
const [currentProjectName, setCurrentProjectName] = useState<string>(""); const [currentProjectName, setCurrentProjectName] = useState<string>("");
const [currentProjectId, setCurrentProjectId] = useState<number | undefined>(undefined);
const { session, loading } = useSession(); const { session, loading } = useSession();
const { const {
@ -125,7 +126,8 @@ const DatetimePickerHourCycle = () => {
<Button <Button
onClick={() => { onClick={() => {
setCurrentProjectName(projectInvestments[0].project_name); setCurrentProjectName(projectInvestments[0].project_name);
setShowModal(true); setCurrentProjectId(projectInvestments[0].project_id);
setTimeout(() => setShowModal(true), 0);
}} }}
> >
Schedule Meeting Schedule Meeting
@ -139,6 +141,7 @@ const DatetimePickerHourCycle = () => {
onOpenChange={setShowModal} onOpenChange={setShowModal}
session={session} session={session}
projectName={currentProjectName} projectName={currentProjectName}
projectId={currentProjectId}
/> />
</div> </div>
); );

View File

@ -1,12 +1,14 @@
import {nextui} from '@nextui-org/theme';
import type { Config } from "tailwindcss" import type { Config } from "tailwindcss"
const config = { const config = {
darkMode: ["class"], darkMode: ["class"],
content: [ content: [
'./pages/**/*.{ts,tsx}', "./pages/**/*.{ts,tsx}",
'./components/**/*.{ts,tsx}', "./components/**/*.{ts,tsx}",
'./app/**/*.{ts,tsx}', "./app/**/*.{ts,tsx}",
'./src/**/*.{ts,tsx}', "./src/**/*.{ts,tsx}",
"./node_modules/@nextui-org/theme/dist/components/(calendar|date-input|button|ripple|spinner).js"
], ],
prefix: "", prefix: "",
theme: { theme: {
@ -74,7 +76,7 @@ const config = {
}, },
}, },
}, },
plugins: [require("tailwindcss-animate"), require('@tailwindcss/typography'),], plugins: [require('tailwindcss-animate'),require('@tailwindcss/typography'),nextui()],
} satisfies Config } satisfies Config
export default config export default config