Skip to content
Open
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
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
"husky": "^9.1.7",
"openapi-typescript": "^7.8.0",
"postcss": "^8",
"prettier": "^3.6.2",
"prettier-plugin-tailwindcss": "^0.6.8",
"tailwindcss": "^3.4.1",
"tsx": "^4.19.1",
Expand Down
23 changes: 13 additions & 10 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

36 changes: 36 additions & 0 deletions src/app/availability/[slug]/not-found.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import Image from "next/image";
import Link from "next/link";

export default function NotFound() {
return (
<div className="flex min-h-screen items-center justify-center">
<div className="flex h-fit w-fit items-center justify-center gap-2 rounded-md border border-blue-400 bg-blue-100 p-16 shadow-sm">
<div className="flex flex-col items-center gap-3">
<h2 className="text-2xl font-bold">
Oops! Meeting Not Found
</h2>
<p className="text-gray-600">
The Meeting you&apos;re looking for doesn&apos;t exist
or may have been moved
</p>

<div className="p-7">
<Image
src="/ZotMeet_BLACK.png"
alt="ZotMeet Logo"
width={120}
height={120}
/>
</div>

<Link
href="/"
className="rounded-md border border-blue-400 bg-blue-600 p-4 text-white hover:bg-blue-500"
>
Return Home
</Link>
</div>
</div>
</div>
);
}
31 changes: 27 additions & 4 deletions src/app/not-found.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,34 @@
import Image from "next/image";
import Link from "next/link";

export default function NotFound() {
return (
<div>
<h2>Not Found</h2>
<p>Could not find requested resource</p>
<Link href="/">Return Home</Link>
<div className="flex min-h-screen items-center justify-center">
<div className="flex h-fit w-fit items-center justify-center gap-2 rounded-md border border-blue-400 bg-blue-100 p-16 shadow-sm">
<div className="flex flex-col items-center gap-3">
<h2 className="text-2xl font-bold">Oops! Page Not Found</h2>
<p className="text-gray-600">
The Page you&apos;re looking for doesn&apos;t exist or
may have been moved
</p>

<div className="p-7">
<Image
src="/ZotMeet_BLACK.png"
alt="ZotMeet Logo"
width={120}
height={120}
/>
</div>

<Link
href="/"
className="rounded-md border border-blue-400 bg-blue-600 p-4 text-white hover:bg-blue-500"
>
Return Home
</Link>
</div>
</div>
</div>
);
}
21 changes: 18 additions & 3 deletions src/components/availability/group-availability-block.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ interface GroupAvailabilityBlockProps {
onHover?: VoidFunction;
blockColor: string;
hoveredMember?: string | null;
selectedMember: string[];
}

export const GroupAvailabilityBlock = memo(
Expand All @@ -20,15 +21,29 @@ export const GroupAvailabilityBlock = memo(
onHover,
blockColor,
hoveredMember,
selectedMember,
}: GroupAvailabilityBlockProps) => {
const isMemberAvailable =
// Check if hovered member is available (for dimming when hovering)
const isHoveredMemberAvailable =
hoveredMember && block && block.includes(hoveredMember);

// Check if any selected member is available (for dimming when not hovering)
const hasSelectedMemberAvailable =
!hoveredMember &&
selectedMember.length > 0 &&
selectedMember.some((memberId) => block.includes(memberId));

return (
<button
className={cn(
"h-full w-full border-r-[1px] border-gray-medium transition-opacity duration-200",
hoveredMember && !isMemberAvailable && "opacity-30",
"h-full w-full border-r-[1px] border-gray-medium",
// Dim if hovering and hovered member is not available
hoveredMember && !isHoveredMemberAvailable && "opacity-30",
// Dim if not hovering, have selections, but no selected member is available
!hoveredMember &&
selectedMember.length > 0 &&
!hasSelectedMemberAvailable &&
"opacity-30",
tableCellStyles,
className
)}
Expand Down
20 changes: 20 additions & 0 deletions src/components/availability/group-availability-row.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ interface GroupAvailabilityRowProps {
availabilityDates: ZotDate[];
numMembers: number;
hoveredMember: string | null;
selectedMember: string[];
handleCellClick: (params: {
isSelected: boolean;
zotDateIndex: number;
Expand All @@ -38,6 +39,7 @@ export function GroupAvailabilityRow({
availabilityDates,
numMembers,
hoveredMember,
selectedMember,
handleCellClick,
handleCellHover,
}: GroupAvailabilityRowProps) {
Expand Down Expand Up @@ -72,14 +74,31 @@ export function GroupAvailabilityRow({
const block = selectedDate.groupAvailability[timestamp] || [];

// Calculate block color
// Priority: hoveredMember > selectedMember > normal group view
let blockColor = "transparent";
if (hoveredMember) {
if (block.includes(hoveredMember)) {
blockColor = "rgba(55, 124, 251)";
} else {
blockColor = "transparent";
}
} else if (selectedMember.length > 0) {
// When members are selected (checkboxes checked), show their combined schedules
const selectedInBlock = selectedMember.filter((memberId) =>
block.includes(memberId)
);
if (selectedInBlock.length > 0) {
// Show with opacity based on how many selected members are available
const opacity = Math.min(
0.9,
0.5 + selectedInBlock.length * 0.15
);
blockColor = `rgba(55, 124, 251, ${opacity})`;
} else {
blockColor = "transparent";
}
} else if (numMembers > 0) {
// Normal group view (no hover, no selections)
Comment on lines +85 to +101
Copy link
Member

Choose a reason for hiding this comment

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

nit: comments may be redundant here

const opacity = block.length / numMembers;
blockColor = `rgba(55, 124, 251, ${opacity})`;
}
Expand Down Expand Up @@ -115,6 +134,7 @@ export function GroupAvailabilityRow({
blockColor={blockColor}
tableCellStyles={tableCellStyles}
hoveredMember={hoveredMember}
selectedMember={selectedMember}
/>
</td>
);
Expand Down
2 changes: 2 additions & 0 deletions src/components/availability/group-availability.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ export function GroupAvailability({
hoveredMember,
resetSelection,
setIsMobileDrawerOpen,
selectedMember,
} = useGroupSelectionStore();

const updateSelection = useCallback(
Expand Down Expand Up @@ -201,6 +202,7 @@ export function GroupAvailability({
availabilityDates={availabilityDates}
numMembers={members.length}
hoveredMember={hoveredMember}
selectedMember={selectedMember}
handleCellClick={handleCellClick}
handleCellHover={handleCellHover}
/>
Expand Down
71 changes: 63 additions & 8 deletions src/components/availability/group-responses.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ export function GroupResponses({
isMobileDrawerOpen,
setIsMobileDrawerOpen,
setHoveredMember,
selectedMember,
toggleSelectedMember,
} = useGroupSelectionStore();

const [blockInfoString, setBlockInfoString] = useState(
Expand All @@ -38,7 +40,12 @@ export function GroupResponses({
}

const member = members.find((m) => m.memberId === memberId);
setHoveredMember(member ? member.displayName : null);
setHoveredMember(member ? member.memberId : null);
};

const handleCheckboxChange = (memberId: string) => {
// Toggle the member in the selectedMember array
Copy link
Member

Choose a reason for hiding this comment

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

nit: the best comment is no comment.

To elaborate, your code should be written in a way such that it is clear and self-documenting (i.e. does not need a comment to further clarify it). If this holds true, then comments should be reserved for truly complex, difficult to explain code.

toggleSelectedMember(memberId);
};

const { availableMembers, notAvailableMembers } = useMemo(() => {
Expand Down Expand Up @@ -156,8 +163,8 @@ export function GroupResponses({
</span>
</div>
<ul className="h-64 space-y-2 overflow-auto py-2 pl-8">
{availableMembers.length > 0 ? (
availableMembers.map((member) => (
{members.length > 0 ? (
members.map((member) => (
<li
Copy link
Member

Choose a reason for hiding this comment

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

issue: why was this changed? does this affect rendering logic? we still render notAvailableMembers, right? we should clarify what the behavior here should be

key={member.memberId}
className="cursor-pointer text-lg text-gray-800"
Expand All @@ -168,13 +175,36 @@ export function GroupResponses({
handleMemberHover(null)
}
>
{member.displayName}
<div className="flex items-center gap-2">
<input
className="cursor-pointer"
type="checkbox"
Comment on lines +179 to +181
Copy link
Member

Choose a reason for hiding this comment

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

repeat: this should be a shadcn checkbox

checked={selectedMember.includes(
member.memberId
)}
onChange={() =>
handleCheckboxChange(
member.memberId
)
}
onClick={(e) =>
e.stopPropagation()
}
/>
<span
className={cn(
selectedMember.includes(
member.memberId
) && "font-semibold"
)}
>
{member.displayName}
</span>
</div>
</li>
))
) : (
<li className="text-sm italic text-gray-400">
N/A
</li>
<li className="text-sm italic text-gray-400"></li>
Copy link
Member

Choose a reason for hiding this comment

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

question: why are we rendering nothing in the case where we have no members?

suggestion: if it is an improvement to not indicate there are no responders, you should describe why in your PR and show screenshots. Further, you do not need to have an "else" case for this, as list.map(), where list is empty, would simply render nothing anyways

)}
</ul>
</div>
Expand All @@ -198,7 +228,32 @@ export function GroupResponses({
handleMemberHover(null)
}
>
{member.displayName}
<div className="flex items-center gap-2">
<input
Copy link
Member

Choose a reason for hiding this comment

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

suggestion: this probably should be a shadcn checkbox: https://ui.shadcn.com/docs/components/checkbox

className="cursor-pointer"
type="checkbox"
checked={selectedMember.includes(
member.memberId
)}
onChange={() =>
handleCheckboxChange(
member.memberId
)
}
onClick={(e) =>
e.stopPropagation()
}
/>
<span
className={cn(
selectedMember.includes(
member.memberId
) && "font-semibold"
Copy link
Member

Choose a reason for hiding this comment

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

nit: maybe just font-medium?

)}
>
{member.displayName}
</span>
</div>
</li>
))
) : (
Expand Down
Empty file.
Loading