mirror of
https://github.com/Sosokker/B2D-Ventures.git
synced 2025-12-18 13:34:06 +01:00
feature: store meeting log and check for submitting state in calendar
This commit is contained in:
parent
b84dde1044
commit
4ed90281ce
1
.prettierignore
Normal file
1
.prettierignore
Normal file
@ -0,0 +1 @@
|
|||||||
|
/tailwind.config.ts
|
||||||
1147
package-lock.json
generated
1147
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
10
package.json
10
package.json
@ -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",
|
||||||
|
|||||||
@ -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">
|
||||||
|
|||||||
@ -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 };
|
||||||
|
}
|
||||||
|
|||||||
@ -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>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -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
|
||||||
Loading…
Reference in New Issue
Block a user