Skip to content

Commit

Permalink
feat: little redesign
Browse files Browse the repository at this point in the history
* feat: redesign of stars, course group item, add opinions count

* feat: helping tooltip
  • Loading branch information
qamarq authored Jan 16, 2025
1 parent 5c77215 commit a1ef34c
Show file tree
Hide file tree
Showing 7 changed files with 104 additions and 53 deletions.
1 change: 1 addition & 0 deletions frontend/src/app/plans/edit/[id]/page.client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,7 @@ export function CreateNewPlanPage({
spotsOccupied: g.spotsOccupied,
spotsTotal: g.spotsTotal,
averageRating: g.averageRating,
opinionsCount: g.opinionsCount,
}) satisfies ExtendedGroup,
),
}))
Expand Down
18 changes: 13 additions & 5 deletions frontend/src/components/class-block-stars.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { StarHalfIcon, StarIcon } from "lucide-react";

import { cn } from "@/lib/utils";

function customRound(rating: number): number {
const integerPart = Math.floor(rating);
const decimalPart = rating - integerPart;
Expand All @@ -13,17 +15,23 @@ function customRound(rating: number): number {
}
}

export function StarsRating({ rating }: { rating: number }) {
export function StarsRating({
rating,
hideStars = false,
}: {
rating: number;
hideStars?: boolean;
}) {
const roundedRating = customRound(rating);
const fullPoints = Math.floor(roundedRating);
const isHalfPoint = roundedRating % 1 === 0.5;
return (
<div className="flex gap-1">
<div className={cn("flex gap-1", { hidden: hideStars })}>
{Array.from({ length: fullPoints }).map((_, index) => {
return (
<StarIcon
fill="#9e881c"
color="#9e881c"
fill="#ffe605"
color="#cf9013"
// eslint-disable-next-line react/no-array-index-key
key={index}
width={10}
Expand All @@ -32,7 +40,7 @@ export function StarsRating({ rating }: { rating: number }) {
);
})}
{isHalfPoint ? (
<StarHalfIcon fill="#9e881c" color="#9e881c" width={10} height={10} />
<StarHalfIcon fill="#ffe605" color="#cf9013" width={10} height={10} />
) : null}
</div>
);
Expand Down
126 changes: 79 additions & 47 deletions frontend/src/components/class-block.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,24 @@ import React from "react";
import { StarsRating } from "@/components/class-block-stars";
import { cn } from "@/lib/utils";

export const typeClasses = {
W: "bg-red-300",
L: "bg-blue-300",
C: "bg-green-300",
S: "bg-orange-300",
P: "bg-fuchsia-200",
import { Tooltip, TooltipContent, TooltipTrigger } from "./ui/tooltip";

const typeBgColors = {
W: "bg-red-100",
L: "bg-blue-100",
C: "bg-green-100",
S: "bg-orange-100",
P: "bg-fuchsia-100",
} as const;

const typeBarColors = {
W: "bg-red-500",
L: "bg-blue-500",
C: "bg-green-500",
S: "bg-orange-500",
P: "bg-fuchsia-500",
};

function calculatePosition(startTime: string, endTime: string) {
const [startHour, startMinute] = startTime.split(":").map(Number);
const [endHour, endMinute] = endTime.split(":").map(Number);
Expand Down Expand Up @@ -39,6 +49,7 @@ export function ClassBlock({
spotsOccupied,
spotsTotal,
averageRating,
opinionsCount,
onClick,
isReadonly = false,
}: {
Expand All @@ -54,55 +65,76 @@ export function ClassBlock({
spotsOccupied: number;
spotsTotal: number;
averageRating: number;
opinionsCount: number;
onClick: () => void;
isReadonly?: boolean;
}) {
const position = calculatePosition(startTime, endTime);
const [startGrid, durationSpan] = position;
return (
<button
suppressHydrationWarning={true}
disabled={isDisabled}
onClick={isReadonly ? undefined : onClick}
style={{
gridColumnStart: startGrid,
gridColumnEnd: `span ${durationSpan.toString()}`,
}}
className={cn(
position,
typeClasses[courseType],
`relative flex flex-col truncate rounded-md p-2 shadow-md`,
isChecked
? "cursor-pointer"
: isDisabled
? "opacity-20"
: "cursor-pointer opacity-60",
isReadonly ? "cursor-default" : null,
)}
>
<div className="flex w-full justify-between">
<div className="flex gap-1">
<p>{`${courseType} ${week === "" ? "" : `|${week}`}`}</p>
</div>
<p>{`Grupa ${groupNumber}`}</p>
</div>
<p className="truncate font-bold">{courseName}</p>
<p className="truncate font-semibold">{lecturer}</p>
<p className="mt-2 flex w-full justify-between truncate">
<UsersRoundIcon className="size-3" />
<span
<Tooltip delayDuration={500}>
<TooltipTrigger asChild={true}>
<button
suppressHydrationWarning={true}
disabled={isDisabled}
onClick={isReadonly ? undefined : onClick}
style={{
gridColumnStart: startGrid,
gridColumnEnd: `span ${durationSpan.toString()}`,
}}
className={cn(
"font-bold",
spotsOccupied >= spotsTotal ? "text-red-500" : null,
position,
typeBgColors[courseType],
`border-l-3 relative flex flex-col overflow-hidden truncate rounded-md p-2 shadow-md`,
isChecked
? "cursor-pointer"
: isDisabled
? "opacity-20"
: "cursor-pointer opacity-60",
isReadonly ? "cursor-default" : null,
)}
>
{spotsOccupied}/{spotsTotal}
</span>
</p>
<p className="flex w-full justify-between truncate">
<StarsRating rating={averageRating > 0 ? averageRating : 1} />
<span className="font-bold">{averageRating}</span>
</p>
</button>
<div
className={cn(
"absolute inset-y-0 left-0 top-0 w-[4px]",
typeBarColors[courseType],
)}
></div>
<div className="flex w-full justify-between">
<div className="flex gap-1">
<p>{`${courseType} ${week === "" ? "" : `|${week}`}`}</p>
</div>
<p>{`Grupa ${groupNumber}`}</p>
</div>
<p className="truncate font-bold">{courseName}</p>
<p className="truncate font-semibold">{lecturer}</p>
<p className="mt-2 flex w-full justify-between truncate">
<UsersRoundIcon className="size-3" />
<span
className={cn(
"font-bold",
spotsOccupied >= spotsTotal ? "text-red-500" : null,
)}
>
{spotsOccupied}/{spotsTotal}
</span>
</p>
<div className={"flex w-full justify-between truncate"}>
<StarsRating
rating={averageRating > 0 ? averageRating : 1}
hideStars={durationSpan < 10}
/>
<p className="font-bold">
{averageRating} ({opinionsCount})
</p>
</div>
</button>
</TooltipTrigger>
<TooltipContent>
<p>
{courseName} - {lecturer}
</p>
</TooltipContent>
</Tooltip>
);
}
9 changes: 8 additions & 1 deletion frontend/src/components/groups-accordion.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,17 @@ import {
} from "@/components/ui/accordion";
import { cn } from "@/lib/utils";

import { typeClasses } from "./class-block";
import { Button } from "./ui/button";
import { Checkbox } from "./ui/checkbox";

const typeClasses = {
W: "bg-red-300",
L: "bg-blue-300",
C: "bg-green-300",
S: "bg-orange-300",
P: "bg-fuchsia-200",
} as const;

export function GroupsAccordionItem({
registrationName,
onCourseCheck,
Expand Down
1 change: 1 addition & 0 deletions frontend/src/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export interface ClassBlockProps {
spotsOccupied: number;
spotsTotal: number;
averageRating: number;
opinionsCount: number;
}

export interface Registration {
Expand Down
1 change: 1 addition & 0 deletions frontend/src/lib/utils/update-local-plan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ export const updateLocalPlan = async (
startTime: g.startTime.split(":").slice(0, 2).join(":"),
spotsOccupied: g.spotsOccupied,
spotsTotal: g.spotsTotal,
opinionsCount: g.opinionsCount,
averageRating: g.averageRating,
}));
return {
Expand Down
1 change: 1 addition & 0 deletions frontend/src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ export type CourseType = {
averageRating: number;
createdAt: string;
updatedAt: string;
opinionsCount: number;
}[];
}[];

Expand Down

0 comments on commit a1ef34c

Please sign in to comment.