Skip to content

Commit

Permalink
✨ video section pages updated
Browse files Browse the repository at this point in the history
  • Loading branch information
sinanptm committed Sep 27, 2024
1 parent b5c835d commit 92c7ed6
Show file tree
Hide file tree
Showing 9 changed files with 129 additions and 69 deletions.
12 changes: 12 additions & 0 deletions client/app/(patient)/video-section/[sectionId]/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import PatientVideoLayout from '@/components/page-components/video/PatientVideoLayout';
import { ReactNode } from 'react';

const layout = ({ children }: { children: ReactNode; }) => {
return (
<PatientVideoLayout>
{children}
</PatientVideoLayout>
)
}

export default layout
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ import { VideoIcon, PhoneIcon } from "lucide-react"
import VideoChat from '@/components/page-components/video/VideoChat'
import { ButtonV2 } from '@/components/button/ButtonV2'
import { useState } from "react"
import { useParams } from "next/navigation"

export default function VideoCallPage() {
const [isCalling, setIsCalling] = useState(false)
const [localStream, setLocalStream] = useState<MediaStream | null>(null)

const [isCalling, setIsCalling] = useState(false);
const [localStream, setLocalStream] = useState<MediaStream | null>(null);
const sectionId = useParams().sectionId as string;

const handleStartCall = async () => {
try {
const stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true })
Expand All @@ -25,8 +27,8 @@ export default function VideoCallPage() {
if (localStream) {
localStream.getTracks().forEach(track => track.stop())
}
setLocalStream(null)
setIsCalling(false)
setLocalStream(null);
setIsCalling(false);
}

return (
Expand Down
32 changes: 9 additions & 23 deletions client/components/button/VideoButtonPatient.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,53 +4,39 @@ import React, { useState } from 'react';
import { ButtonV2 } from './ButtonV2';
import Image from 'next/image';
import { Badge } from '@/components/ui/badge'
import VideoCallModal from '../models/VideoCallModal';

type Appointment = {
_id: string;
patientName: string;
startTime: Date;
};
import VideoCallModal from '../models/VideoSectionsModel';

const VideoButtonPatient = () => {
const [isModalOpen, setIsModalOpen] = useState(false);
const [upcomingAppointments, setUpcomingAppointments] = useState<Appointment[]>([]);

const handleClick = () => {
const dummyAppointments: Appointment[] = [
{ _id: '1', patientName: 'John Doe', startTime: new Date(Date.now() + 30 * 60000) },
{ _id: '2', patientName: 'Jane Smith', startTime: new Date(Date.now() + 45 * 60000) },
];
setUpcomingAppointments(dummyAppointments);
setIsModalOpen(true);
console.log('Video button clicked');
}

return (
<>
<ButtonV2 variant="ghost" size="icon" className="relative" onClick={handleClick}>
<Image
{/* <Image
src="/assets/icons/utils/video.svg"
width={20}
height={20}
alt="Video Calls"
className="text-muted-foreground"
/>
{upcomingAppointments.length > 0 && (
{upcomingSections.length > 0 && (
<Badge
variant="destructive"
className="absolute -right-1 -top-1 h-5 w-5 rounded-full p-0 text-xs flex items-center justify-center"
>
{upcomingAppointments.length}
{upcomingSections.length}
</Badge>
)}
)} */}
<span className="sr-only">View Upcoming Video Calls</span>
</ButtonV2>
<VideoCallModal
{/* <VideoCallModal
open={isModalOpen}
setOpen={setIsModalOpen}
appointments={upcomingAppointments}
sections={upcomingSections}
link="/video-call"
/>
/> */}
</>
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,38 +14,28 @@ import { Card, CardContent } from "@/components/ui/card";
import { XIcon, Video } from "lucide-react";
import { ButtonV2 } from "../button/ButtonV2";
import Link from "next/link";
import IVideoSection from "@/types/entities";
import { format } from "date-fns"

type Appointment = {
_id: string;
patientName: string;
startTime: Date;
};

type Props = {
open: boolean;
setOpen: Dispatch<SetStateAction<boolean>>;
appointments: Appointment[];
sections: IVideoSection[];
link: string;
};

export default function VideoCallModal({
open,
setOpen,
appointments,
sections,
link
}: Props) {
const closeModal = () => {
setOpen(false);
};

const formatTimeLeft = (startTime: Date) => {
const now = new Date();
const diff = startTime.getTime() - now.getTime();
const minutes = Math.floor(diff / 60000);
return `${minutes} minute${minutes !== 1 ? 's' : ''} left`;
};

return (
return (
<AlertDialog open={open} onOpenChange={setOpen}>
<AlertDialogContent className="max-w-3xl bg-dark-200">
<AlertDialogHeader>
Expand All @@ -58,17 +48,17 @@ export default function VideoCallModal({
</AlertDialogHeader>
<AlertDialogDescription></AlertDialogDescription>
<div className="space-y-4">
{appointments.length > 0 ? (
{sections.length > 0 ? (
<>
{appointments.map((appointment) => (
<Link key={appointment._id} href={`${link}/${appointment._id}`} onClick={closeModal} >
{sections.map((section) => (
<Link key={section._id} href={`${link}/${section._id}`} onClick={closeModal} >
<Card>
<CardContent className="flex items-center justify-between p-4">
<div className="flex items-center space-x-4">
<Video className="h-6 w-6 text-primary" />
<div>
<h3 className="font-semibold">{appointment.patientName}</h3>
<p className="text-sm text-muted-foreground">{formatTimeLeft(appointment.startTime)}</p>
<h3 className="font-semibold">{section.patientName}</h3>
<p className="text-sm text-muted-foreground">{format(section.startTime!,"hh:mm a")}</p>
</div>
</div>
<ButtonV2
Expand All @@ -92,11 +82,11 @@ export default function VideoCallModal({
src="/assets/icons/emoji/😴.svg"
width={64}
height={64}
alt="No Upcoming Calls"
alt="No Upcoming Sections"
className="opacity-50"
/>
<p className="text-center text-muted-foreground text-sm">
No upcoming video calls in the next hour.
No upcoming video Sections in the next hour.
</p>
</div>
)}
Expand Down
14 changes: 7 additions & 7 deletions client/components/page-components/video/ControlPanel.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Button } from "@/components/ui/button"
import { ButtonV2 } from "@/components/button/ButtonV2"
import { Mic, MicOff, Video, VideoOff, PhoneOff } from 'lucide-react'
import { useState } from "react"

Expand All @@ -23,30 +23,30 @@ export default function ControlPanel({ onEndCall }: ControlPanelProps) {
return (
<div className="bg-gray-800 border-t border-gray-700 p-4">
<div className="flex justify-center space-x-4 max-w-md mx-auto">
<Button
<ButtonV2
onClick={toggleMute}
variant={isMuted ? "destructive" : "secondary"}
size="icon"
className="rounded-full"
>
{isMuted ? <MicOff className="h-5 w-5" /> : <Mic className="h-5 w-5" />}
</Button>
<Button
</ButtonV2>
<ButtonV2
onClick={toggleVideo}
variant={isVideoOff ? "destructive" : "secondary"}
size="icon"
className="rounded-full"
>
{isVideoOff ? <VideoOff className="h-5 w-5" /> : <Video className="h-5 w-5" />}
</Button>
<Button
</ButtonV2>
<ButtonV2
onClick={onEndCall}
variant="destructive"
size="icon"
className="rounded-full"
>
<PhoneOff className="h-5 w-5" />
</Button>
</ButtonV2>
</div>
</div>
)
Expand Down
35 changes: 35 additions & 0 deletions client/components/page-components/video/PatientVideoLayout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
'use client'
import { useAuth } from '@/lib/hooks/useAuth';
import { ReactNode, useEffect, useState } from 'react'
import { notFound } from 'next/navigation';
import VideoCallSkeleton from '@/components/skeletons/VideoSection';

const PatientVideoLayout = ({ children }: { children: ReactNode }) => {
const [isLoading, setLoading] = useState(true);
const { patientToken } = useAuth();
const isPatient = !!patientToken;

useEffect(() => {
if (isPatient) {
const timer = setTimeout(() => {
setLoading(false);
}, 0);
return () => clearTimeout(timer);
} else {
setLoading(false);
}
}, [isPatient]);

if(isLoading){
return <VideoCallSkeleton />
}

if (!isPatient) {
notFound()
}
return (
<div>{children}</div>
)
}

export default PatientVideoLayout
15 changes: 15 additions & 0 deletions client/components/skeletons/VideoSection.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React from 'react'
import { Card } from "@/components/ui/card"
import { Skeleton } from "@/components/ui/skeleton"

export default function VideoCallSkeleton() {
return (
<div className="h-screen flex flex-col bg-gradient-to-b from-gray-900 to-gray-800 text-white">
<Card className="flex flex-col items-center justify-center flex-grow bg-transparent border-0 shadow-none">
<Skeleton className="w-24 h-24 rounded-full mb-8 bg-gray-700" />
<Skeleton className="h-10 w-3/4 max-w-md mb-6 bg-gray-700" />
<Skeleton className="h-12 w-48 rounded-full bg-gray-700" />
</Card>
</div>
)
}
20 changes: 18 additions & 2 deletions client/types/entities.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { AppointmentStatus, AppointmentType, Days, NotificationTypes } from "./enum";
import { AppointmentStatus, AppointmentType, Days, NotificationTypes, VideoSectionStatus } from "./enum";

export interface IPatient {
_id?: string;
Expand Down Expand Up @@ -36,7 +36,7 @@ export interface ISlot {
day?: Days;
startTime?: string;
endTime?: string;
status?: "available" | "booked";
status?: "available" | "booked";
}

export interface IAppointment {
Expand Down Expand Up @@ -95,3 +95,19 @@ export interface IMessage {
readonly isReceived?: boolean;
}

export default interface IVideoSection {
_id?: string;
appointmentId?: string;
doctorName?: string;
patientName?: string;
doctorProfile?: string;
doctorId?: string;
patientProfile?: string;
patientId?: string;
createdAt?: Date;
updatedAt?: Date;
startTime?: Date | string;
endTime?: Date | string;
link?: string;
status?: VideoSectionStatus;
}
24 changes: 14 additions & 10 deletions client/types/enum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export enum Days {
Friday = "friday",
Saturday = "saturday",
Sunday = "sunday"
}export enum AppointmentStatus {
} export enum AppointmentStatus {
// PAYMENT_PENDING = "payment-pending",
PENDING = "pending",
CONFIRMED = "confirmed",
Expand All @@ -29,13 +29,17 @@ export enum NotificationTypes {
APPOINTMENT_REMINDER = 'appointment_reminder'
}
export enum FormFieldType {
INPUT = "input",
TEXTAREA = "textarea",
PHONE_INPUT = "phoneInput",
CHECKBOX = "checkbox",
DATE_PICKER = "datePicker",
SELECT = "select",
SKELETON = "skeleton",
PASSWORD = "password"
INPUT = "input",
TEXTAREA = "textarea",
PHONE_INPUT = "phoneInput",
CHECKBOX = "checkbox",
DATE_PICKER = "datePicker",
SELECT = "select",
SKELETON = "skeleton",
PASSWORD = "password"
}
export enum VideoSectionStatus {
PENDING = 'pending',
IN_PROGRESS = 'in_progress',
COMPLETED = 'completed',
}

1 comment on commit 92c7ed6

@vercel
Copy link

@vercel vercel bot commented on 92c7ed6 Sep 27, 2024

Choose a reason for hiding this comment

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

Please sign in to comment.