Files
nirvana/web/components/Dashboard/Rooms.tsx

452 lines
13 KiB
TypeScript

import React, { useEffect, useState } from "react";
import { FaAngleDown, FaBell, FaClock, FaPlus, FaLink } from "react-icons/fa";
import { IoTimer } from "react-icons/io5";
import { BsThreeDots } from "react-icons/bs";
import Image from "next/image";
import CreateOrUpdateRoom from "../Modals/CreateOrUpdateRoom";
import {
ShowModalType,
useKeyboardContext,
} from "../../contexts/keyboardContext";
import {
collection,
getFirestore,
onSnapshot,
orderBy,
query,
where,
} from "firebase/firestore";
import { Collections } from "../../services/collections";
import { useTeamDashboardContext } from "../../contexts/teamDashboardContext";
import Room, { RoomStatus, RoomType } from "../../models/room";
import { useAuth } from "../../contexts/authContext";
import { Divider, Dropdown, Menu, Radio, Tooltip } from "antd";
import RoomCard from "../RoomCard";
import toast from "react-hot-toast";
import RoomTypeTag from "../RoomTypeTag";
import moment from "moment";
import { getTime } from "../../helpers/dateTime";
enum RoomTypeFilter {
team = "team",
me = "me",
archived = "archived",
}
enum RoomTimeFilter {
today = "today",
week = "week",
month = "month",
}
const db = getFirestore();
const today = new Date();
const lastWeek = new Date();
lastWeek.setDate(lastWeek.getDate() - 7);
const nextWeek = new Date();
nextWeek.setDate(nextWeek.getDate() + 7);
export default function DashboardRoom() {
const { currUser } = useAuth();
const { handleModalType, showModalType } = useKeyboardContext();
const { team } = useTeamDashboardContext();
const [roomsMap, setRoomsMap] = useState<Map<string, Room>>(
new Map<string, Room>()
);
// get recurring rooms data realtime: ALL of them
useEffect(() => {
/**
* QUERY:
* all recurring rooms, as the created date can be a year ago
*
*/
const q = query(
collection(db, Collections.rooms),
where("teamId", "==", team.id),
where("type", "==", RoomType.recurring)
);
// return unsubscribe
return onSnapshot(q, (snapshot) => {
snapshot.docChanges().forEach(handleDocChange);
});
}, []);
// get scheduled rooms data realtime: the ones with the date range
useEffect(() => {
/**
* QUERY:
* all scheduled rooms in the past 7 days or the next 7 days
*
*/
const q = query(
collection(db, Collections.rooms),
where("teamId", "==", team.id),
where("type", "==", RoomType.scheduled),
where("scheduledDateTime", ">", lastWeek),
where("scheduledDateTime", "<", nextWeek),
orderBy("scheduledDateTime", "asc")
);
// return unsubscribe
return onSnapshot(q, (snapshot) => {
snapshot.docChanges().forEach(handleDocChange);
});
}, []);
// get now rooms data realtime
useEffect(() => {
/**
* QUERY:
* all live/now rooms going on right now
*
*/
const q = query(
collection(db, Collections.rooms),
where("teamId", "==", team.id),
where("type", "==", RoomType.now),
where("createdDate", ">", lastWeek),
orderBy("createdDate", "asc")
);
// return unsubscribe
return onSnapshot(q, (snapshot) => {
snapshot.docChanges().forEach(handleDocChange);
});
}, []);
function handleDocChange(change) {
let updatedRoom = change.doc.data() as Room;
updatedRoom.id = change.doc.id;
if (change.type === "added" || change.type === "modified") {
// update rooms map
setRoomsMap((prevMap) => {
return new Map(prevMap.set(updatedRoom.id, updatedRoom));
});
}
if (change.type === "removed") {
console.log("Removed room: ", updatedRoom);
}
}
// on CTRL + Q, new tab to google meet
// on CTRL + V, show modal to create meeting with the link
const [selectedTabPane, setSelectedTabPane] = useState<string>(
RoomTypeFilter.me
);
// sort this mega array
const allRooms = Array.from(roomsMap.values());
const teamRooms = allRooms.filter(
(room) => room.status != RoomStatus.archived
);
const meRooms = allRooms.filter(
(room) =>
room.members.includes(currUser.uid) && room.status != RoomStatus.archived
);
const archivedRooms = allRooms.filter(
(room) => room.status == RoomStatus.archived
);
// data for different meRooms
const recurring = meRooms.filter(
(room) => room.type == RoomType.recurring && room.status == RoomStatus.empty
);
const now = meRooms.filter(
(room) =>
(room.type == RoomType.now && room.status != RoomStatus.archived) ||
room.status == RoomStatus.live
);
const scheduled = meRooms.filter(
(room) => room.type == RoomType.scheduled && room.status == RoomStatus.empty
);
// sorting array of scheduled rooms
scheduled.sort(function (a, b) {
// Turn your strings into dates, and then subtract them
// to get a value that is either negative, positive, or zero.
if (!b.scheduledDateTime) {
return -1;
}
return (
getTime(a.scheduledDateTime.toDate()) -
getTime(b.scheduledDateTime.toDate())
);
});
var relativeTimeNextMeeting = "";
// go through scheduled and find the first one coming up
if (scheduled && scheduled.length > 0) {
const firstOneInFuture = scheduled.find(
(room) => moment(room.scheduledDateTime.toDate()).diff(today) > 0
);
if (firstOneInFuture) {
const meetingDatetime: Date = firstOneInFuture.scheduledDateTime.toDate();
relativeTimeNextMeeting = moment(meetingDatetime).fromNow();
}
}
function getRoomContent() {
// return data based on the selected filters
switch (selectedTabPane) {
case RoomTypeFilter.team:
// render sections for different types
const recurringTeam = teamRooms.filter(
(room) =>
room.type == RoomType.recurring && room.status == RoomStatus.empty
);
const nowTeam = teamRooms.filter(
(room) =>
(room.type == RoomType.now && room.status != RoomStatus.archived) ||
room.status == RoomStatus.live
);
const scheduledTeam = teamRooms.filter(
(room) =>
room.type == RoomType.scheduled && room.status == RoomStatus.empty
);
// sorting array of scheduled rooms
scheduledTeam.sort(function (a, b) {
// Turn your strings into dates, and then subtract them
// to get a value that is either negative, positive, or zero.
if (!b.scheduledDateTime) {
return -1;
}
return (
getTime(a.scheduledDateTime.toDate()) -
getTime(b.scheduledDateTime.toDate())
);
});
return (
<>
<div className="flex flex-row flex-wrap">
{nowTeam.map((room) => (
<RoomCard
key={room.id}
room={room}
updateRoomHandler={handleUpdateRoom}
/>
))}
{scheduledTeam.map((room) => (
<RoomCard
key={room.id}
room={room}
updateRoomHandler={handleUpdateRoom}
/>
))}
{recurringTeam.map((room) => (
<RoomCard
key={room.id}
room={room}
updateRoomHandler={handleUpdateRoom}
/>
))}
</div>
</>
);
case RoomTypeFilter.me:
return (
<>
<div className="flex flex-row flex-wrap">
{now.map((room) => (
<RoomCard
key={room.id}
room={room}
updateRoomHandler={handleUpdateRoom}
/>
))}
{scheduled.map((room) => (
<RoomCard
key={room.id}
room={room}
updateRoomHandler={handleUpdateRoom}
/>
))}
{recurring.map((room) => (
<RoomCard
key={room.id}
room={room}
updateRoomHandler={handleUpdateRoom}
/>
))}
</div>
</>
);
case RoomTypeFilter.archived:
return (
<span className="flex flex-col space-y-5 px-10">
{archivedRooms.map((room) => {
// if the room is
return (
<RoomCard
key={room.id}
room={room}
updateRoomHandler={handleUpdateRoom}
/>
);
})}
</span>
);
default:
return allRooms.map((room) => {
// if the room is
return (
<RoomCard
key={room.id}
room={room}
updateRoomHandler={handleUpdateRoom}
/>
);
});
}
}
const TimePeriodFilterMenu = (
<Menu>
<Menu.Item key={1} disabled>
Past 24 Hours (Coming Soon)
</Menu.Item>
<Menu.Item key={2} disabled>
This Month (Coming Soon)
</Menu.Item>
</Menu>
);
const [selectedUpdateRoom, setSelectedUpdateRoom] = useState<Room>(null);
async function handleUpdateRoom(roomId: string) {
console.log("going to update room");
// get the room details and pass it into the modal
if (!roomsMap.has(roomId)) {
toast.error("not a valid room to edit");
return;
}
// pass this to the modal for use
setSelectedUpdateRoom(roomsMap.get(roomId));
// call the keyboard context to show the modal
handleModalType(ShowModalType.createRoom);
}
async function handleCloseModal() {
console.log("closing modal");
// to make sure that the next time the props changes, the modal has the updated room object
setSelectedUpdateRoom(null);
// close the modal
handleModalType(ShowModalType.na);
}
return (
<section className="p-5 flex flex-col bg-gray-100 bg-opacity-25 rounded-lg shadow-md flex-1 overflow-auto">
{/* modal for creating room */}
<CreateOrUpdateRoom
show={showModalType == ShowModalType.createRoom}
updateRoom={selectedUpdateRoom}
handleClose={handleCloseModal}
/>
{/* header */}
<Tooltip title={" archive rooms to keep your team focused"}>
<span className="flex flex-row justify-end space-x-3 pb-5 items-center">
<span className="flex flex-col mr-auto">
<span className="text-white ">
ROOMS
<Tooltip
title={"Press q to create an instant room if you have GSuite."}
>
<button
className="right-1 rounded-lg py-1 px-2 ml-1
shadow-md text-center text-white text-sm font-bold"
>
Q
</button>
</Tooltip>
</span>
{relativeTimeNextMeeting ? (
<span className="text-gray-300 text-xs">
{"You have your next scheduled one "}{" "}
<span className="text-orange-500">
{relativeTimeNextMeeting}
{"."}
</span>
</span>
) : (
<></>
)}
</span>
<Radio.Group
value={selectedTabPane}
onChange={(e) => setSelectedTabPane(e.target.value)}
>
<Radio.Button value={RoomTypeFilter.me}>
{RoomTypeFilter.me}{" "}
<span className="text-xs text-orange-500">
{meRooms.length > 0 ? meRooms.length : ""}
</span>
</Radio.Button>
<Radio.Button value={RoomTypeFilter.team}>
{RoomTypeFilter.team}{" "}
<span className="text-xs text-orange-500">
{teamRooms.length}
</span>
</Radio.Button>
<Radio.Button value={RoomTypeFilter.archived}>
{RoomTypeFilter.archived}{" "}
<span className="text-xs text-orange-500">
{archivedRooms.length > 0 ? archivedRooms.length : ""}
</span>
</Radio.Button>
</Radio.Group>
<Dropdown overlay={TimePeriodFilterMenu}>
<span className="text-sm text-gray-300 flex flex-row items-center uppercase hover:cursor-pointer">
Week <FaAngleDown />
</span>
</Dropdown>
<button
onClick={() => {
setSelectedUpdateRoom(null);
handleModalType(ShowModalType.createRoom);
}}
className="bg-gray-300 bg-opacity-25 p-2 rounded hover:bg-opacity-40"
>
<FaPlus className="text-lg text-white" />
</button>
{/* <BsThreeDots className="text-xl text-white" /> */}
</span>
</Tooltip>
{/* all rooms */}
<span className="overflow-auto">{getRoomContent()}</span>
</section>
);
}