Skip to content
Draft
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
3 changes: 2 additions & 1 deletion src/app/api/reservations/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const authToken = process.env.TWILIO_AUTH_TOKEN;

const client = require('twilio')(accountSid, authToken);

// TODO: Update to follow proper REST api standards.
// get all reservations assigned to a worker, and the task associated with each reservation
export async function GET(req: NextRequest) {
const workerSid = req.nextUrl.searchParams.get('workerSid');
Expand Down Expand Up @@ -39,9 +40,9 @@ export async function GET(req: NextRequest) {
}
}

// TODO: Update to follow proper REST api standards.
// Update reservation status
export async function POST(req: NextRequest) {

try {
const task = req.nextUrl.searchParams.get('taskSid');
const status = req.nextUrl.searchParams.get('status');
Expand Down
1 change: 1 addition & 0 deletions src/app/api/tasks/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const authToken = process.env.TWILIO_AUTH_TOKEN;
const workspaceSid = process.env.TWILIO_WORKSPACE_SID || '';
const client = require('twilio')(accountSid, authToken);

// TODO: Update to follow proper REST api standards.
// Dequeue a reservation to connect the call
export async function POST(req: NextRequest) {
try {
Expand Down
18 changes: 15 additions & 3 deletions src/app/dashboard/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,26 @@ import Appbar from "../../components/appbar";
import Sidebar from "../../components/sidebar";

import "../../styles/globals.css";


import useCalls from '@/lib/hooks/useCalls';
import CallsContext from '@/contexts/CallsContext';
export default function Layout({
children,
}: {
children: React.ReactNode;
}) {
const { data: session, status } = useSession();

const calls = useCalls({
email: session?.user?.email || "",
workerSid: session?.employeeNumber || "",
friendlyName: session?.user?.name || "",
});

if (status === 'loading') {
return <React.Fragment>Loading...</React.Fragment>;
} else if (session) {
const isProgramManager = true; // TODO

const isProgramManager = true; // TODO: Just check session token groups property and see if its contains 'admin' (name not yet determined)
let initials = "AA";
if (session.user?.name) {
const parts = session.user.name.split(' ');
Expand All @@ -27,8 +35,10 @@ export default function Layout({
initials = parts[0][0].toUpperCase();
}
}

return (
<div className="h-screen w-screen">
<CallsContext.Provider value={calls}>
<Appbar initials={initials} isProgramManager={isProgramManager} />
<div className="h-full flex flex-row">
<Sidebar
Expand All @@ -39,6 +49,8 @@ export default function Layout({
{children}
</div>
</div>
</CallsContext.Provider>

</div>
);
} else {
Expand Down
59 changes: 15 additions & 44 deletions src/app/dashboard/tasks/page.tsx
Original file line number Diff line number Diff line change
@@ -1,56 +1,27 @@
"use client"

import { useState, useEffect, useCallback } from 'react';
import { useState, useEffect, useCallback, useContext } from 'react';
import { useSession } from "next-auth/react";
import { Button } from '../../../components/ui/button';
import React from 'react';
import { formatPhoneNumber, formatTimeWithUnits } from '../../../lib/utils';

import CallsContext from '@/contexts/CallsContext';

export default function Tasks() {
const [tasks, setTasks] = useState([]);
const [activeTasks, setActiveTasks] = useState([]);
const { data: session } = useSession();

const fetchTasks = useCallback(() => {
fetch('/api/reservations?workerSid=' + session?.employeeNumber)
.then(response => response.json())
.then(data => {
setTasks(data);
setActiveTasks(data.filter((task: any) => task.reservation.reservationStatus === 'accepted' || task.reservation.reservationStatus === 'pending'));
});
}, [session]);

const updateReservation = (reservation: any, status: string) => {
try {
fetch(`/api/reservations?taskSid=${reservation.taskSid}&status=${status}&reservationSid=${reservation.sid}`, {
method: 'POST'
})
} catch (error) {
console.error("Error updating reservation", error)
}
fetchTasks()
}

const dequeueTask = (task: any, number: string) => {
try {
fetch(`/api/tasks?taskSid=${task.reservation.taskSid}&reservationSid=${task.reservation.sid}&number=${number}`, {
method: 'POST'
})
} catch (error) {
console.error("Error dequeing reservation", error)
}
fetchTasks()
}

useEffect(() => {
// Fetch tasks immediately and then every 2 seconds
fetchTasks();
const intervalId = setInterval(fetchTasks, 2000);

// Clear interval on component unmount
return () => clearInterval(intervalId);
}, [fetchTasks]);
const {
inCall,
number,
makeCall,
setNumber,
endCall,
incomingCall,
acceptCall,
rejectCall,
activeTasks,
updateReservation,
dequeueTask
} = useContext(CallsContext);

return (
<main>
Expand Down
10 changes: 9 additions & 1 deletion src/components/appbar/Logo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,20 @@ import Image from 'next/image';
export default function Logo() {
return (
<div className="flex flex-row">
<Image src="/connie-logo.png" alt="connie logo" className="h-24 sm:h-32" />
<Image
src="/connie-logo.png"
alt="connie logo"
className="h-24 sm:h-32"
width={128}
height={128}
/>
<div className="self-center w-0.5 h-6 mx-2 bg-slate-400" />
<Image
src="/asa-logo.png"
alt="asa logo"
className="h-6 sm:h-8 self-center"
width={100}
height={32}
/>
</div>
);
Expand Down
34 changes: 8 additions & 26 deletions src/components/appbar/NotificationsCard.tsx
Original file line number Diff line number Diff line change
@@ -1,40 +1,19 @@
import React, { useCallback, useEffect } from "react";
import { formatPhoneNumber, formatTimeWithUnits } from "../../lib/utils";
import { useSession } from "next-auth/react";
import { MessageSquare, PhoneIncoming } from "lucide-react";


interface NotificationsCardProps {
activeTasks: any;
setActiveTasks: (tasks: any) => void;
}

export default function NotificationsCard({ activeTasks, setActiveTasks }: NotificationsCardProps) {
const { data: session } = useSession();

const fetchTasks = useCallback(() => {
fetch('/api/reservations?workerSid=' + session?.employeeNumber)
.then(response => response.json())
.then(data => {
setActiveTasks(data.filter((task: any) => task.reservation.reservationStatus === 'accepted' || task.reservation.reservationStatus === 'pending'));
});
}, [session, setActiveTasks]);

useEffect(() => {
// Fetch tasks immediately and then every 2 seconds
fetchTasks();
const intervalId = setInterval(fetchTasks, 2000);

// Clear interval on component unmount
return () => clearInterval(intervalId);
}, [fetchTasks]);
export default function NotificationsCard({ activeTasks }: NotificationsCardProps) {

return (

<div>
<h1 className="text-lg">Notifications</h1>
{activeTasks && Array.isArray(activeTasks) && activeTasks
.map((task: any) => (
<h2>Notifications</h2>
{activeTasks?.length > 0 ? (
activeTasks.map((task: any) => (
<div key={task.task.sid} className="border-t border-gray-300 w-full p-1 flex items-center">
<div className="mr-3">
{task.task.taskChannelUniqueName === 'voice' ? <PhoneIncoming /> : <MessageSquare />}
Expand All @@ -54,7 +33,10 @@ export default function NotificationsCard({ activeTasks, setActiveTasks }: Notif
<h3 className="text-sm font-light">{formatTimeWithUnits(task.task.age)}</h3>
</div>
</div>
))}
))
) : (
<p className="text-sm italic mt-2">No Notifications</p>
)}
</div>
)
}
21 changes: 9 additions & 12 deletions src/components/appbar/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use client";
import Link from "next/link";
import { useState } from "react";
import { useState, useContext } from "react";
import { cn } from "../../lib/utils";

import { Menubar } from "../../components/ui/menubar";
Expand Down Expand Up @@ -43,6 +43,7 @@ import AgentStatus from "./AgentStatus";
import ClientOnly from "../ClientOnly";
import { useSession } from "next-auth/react";
import IncomingCallModal from "../(dashboard)/tasks/IncomingCallModal";
import CallsContext from "@/contexts/CallsContext";

interface AppbarProps extends React.HTMLAttributes<HTMLDivElement> {
initials: string;
Expand All @@ -64,14 +65,11 @@ export default function Appbar({
endCall,
incomingCall,
acceptCall,
rejectCall
} = useCalls({
email: session?.user?.email || '',
workerSid: session?.employeeNumber || '',
friendlyName: session?.user?.name || '',
});
rejectCall,
activeTasks
} = useContext(CallsContext);

const [activeTasks, setActiveTasks] = useState([]);
// TODO: move this to useCalls hook

return (
<Menubar
Expand All @@ -81,13 +79,13 @@ export default function Appbar({
<Logo />
</Link>
<div className="flex space-x-8">
{/* TODO replace with Availability status toggle component */}
{/* TODO : Link status component? I dont think it was linked */}
<div className="self-center">
<ClientOnly>
<AgentStatus />
</ClientOnly>
</div>
<div className="flex flex-row space-x-4">
<div className="flex flex-row space-x-4 items-center">
<Popover>
<PopoverTrigger asChild>
<Button variant="ghost" size="icon">
Expand Down Expand Up @@ -117,14 +115,13 @@ export default function Appbar({
<Popover>
<PopoverTrigger asChild>
<Button variant="ghost" size="icon">
{activeTasks.length > 0 ? <BellDot color="#08B3E5" /> :
{activeTasks?.length > 0 ? <BellDot color="#08B3E5" /> :
<Bell color="#D3D3D3" />}
</Button>
</PopoverTrigger>
<PopoverContent align="end">
<NotificationsCard
activeTasks={activeTasks}
setActiveTasks={setActiveTasks}
/>
</PopoverContent>
</Popover>
Expand Down
32 changes: 32 additions & 0 deletions src/contexts/CallsContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React from 'react';

export type CallsContextType = {
inCall: boolean;
number: string;
makeCall: (number: string) => Promise<void>;
setNumber: React.Dispatch<React.SetStateAction<string>>;
endCall: () => void;
incomingCall: boolean;
acceptCall: () => void;
rejectCall: () => void;
updateReservation: (reservation: any, status: string) => void;
activeTasks: any[];
dequeueTask: (task: any, number: string) => void;
};


const CallsContext = React.createContext<CallsContextType>({
inCall: false,
number: "",
makeCall: async () => {},
setNumber: () => {},
endCall: () => {},
incomingCall: false,
acceptCall: () => {},
rejectCall: () => {},
updateReservation: () => {},
activeTasks: [],
dequeueTask: () => {}
});

export default CallsContext;
Loading