Skip to content

Commit 840983d

Browse files
authored
Fixed Requirement Section Card Color Inconsistencies (#110)
* Fixed Requirement Section Card Color Inconsistencies * Changed Sub Requirement Properties, fixes DAP-54 as well * Fixes several visual bugs
1 parent ddae1d8 commit 840983d

1 file changed

Lines changed: 77 additions & 69 deletions

File tree

entrypoints/degree-audit/components/requirement-breakdown.tsx

Lines changed: 77 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,18 @@ import {
55
PlannableStatus,
66
Progress,
77
RequirementRule,
8-
Status,
98
} from "@/lib/general-types";
109
import { cn } from "@/lib/utils";
1110
import {
1211
CaretDownIcon,
1312
CaretUpIcon,
14-
PlusCircleIcon,
13+
PlusIcon,
14+
Check,
15+
Minus,
16+
X,
1517
} from "@phosphor-icons/react";
1618
import { CalendarBlankIcon } from "@phosphor-icons/react/dist/ssr";
17-
import { CheckIcon } from "lucide-react";
1819
import { useState } from "react";
19-
import { FramedStatusIcon } from "./gpa-credit-cards";
2020
import { useAuditContext } from "../providers/audit-provider";
2121
import { useCourseModalContext } from "../providers/course-modal-provider";
2222

@@ -35,50 +35,57 @@ const getRequirementCompletionState = (
3535
return "in-progress";
3636
};
3737

38-
const StatusIcon = ({
39-
current,
40-
total,
41-
}: {
42-
current: number;
43-
total: number;
44-
}) => {
38+
const RequirementStatusIcon = ({ current, total }: { current: number; total: number }) => {
4539
const state = getRequirementCompletionState(current, total);
4640

47-
return <FramedStatusIcon state={state} />;
41+
if (state === "completed") {
42+
return (
43+
<div className="flex items-center justify-center w-6 h-6 bg-[#5BA753] rounded shrink-0 mt-0.5">
44+
<Check className="text-white w-4 h-4" weight="bold" />
45+
</div>
46+
);
47+
}
48+
if (state === "not-started") {
49+
return (
50+
<div className="flex items-center justify-center w-6 h-6 bg-[#4A5568] rounded shrink-0 mt-0.5">
51+
<X className="text-white w-4 h-4" weight="bold" />
52+
</div>
53+
);
54+
}
55+
return (
56+
<div className="flex items-center justify-center w-6 h-6 bg-[#9CA3AF] rounded shrink-0 mt-0.5">
57+
<Minus className="text-white w-4 h-4" weight="bold" />
58+
</div>
59+
);
4860
};
4961

5062
// Hours badge component
5163
const HoursBadge = ({ current, total }: { current: number; total: number }) => {
5264
const isComplete = current >= total;
5365
const formatHours = (h: number) => `${h} hour${h === 1 ? '' : 's'}`;
5466
return (
55-
<span className="text-sm text-gray-600 border border-gray-300 rounded-full px-3 py-1">
67+
<span className="text-sm text-gray-900 border border-gray-800 rounded-full px-3 py-0.5 font-medium">
5668
{isComplete ? formatHours(total) : `${current} / ${formatHours(total)}`}
5769
</span>
5870
);
5971
};
6072

6173
const statusIcons = {
6274
Completed: {
63-
icon: (
64-
<CheckIcon className="-ml-1 text-white w-5 h-5 bg-green-500 rounded-full p-1" />
65-
),
66-
color: "bg-[var(--color-course-applied)]",
75+
icon: null,
76+
color: "bg-[#ECF8D0] border-[#9FCA5B]",
6777
},
6878
Planned: {
69-
icon: (
70-
<CalendarBlankIcon className="-ml-1 text-white w-5 h-5 bg-blue-500 rounded-full p-1" />
71-
),
72-
color: "bg-[var(--color-course-applied)]",
79+
icon: null,
80+
color: "bg-[var(--color-course-applied)] border-gray-200",
7381
},
7482
"In Progress": {
7583
icon: null,
76-
color: "bg-[var(--color-course-in-progress)]",
84+
color: "bg-[var(--color-course-in-progress)] border-gray-200",
7785
},
78-
// TODO: make sure this is valid
7986
"Not Started": {
8087
icon: null,
81-
color: "bg-[var(--color-course-unknown)]",
88+
color: "bg-[var(--color-course-unknown)] border-gray-200",
8289
},
8390
} as const satisfies Record<
8491
PlannableStatus,
@@ -93,17 +100,17 @@ const CoursePill = ({ course }: { course: Course }) => {
93100
return (
94101
<div
95102
className={cn(
96-
"flex items-center gap-6 px-4 py-3 rounded-lg text-sm w-[80%]",
103+
"grid grid-cols-[100px_1fr_auto] items-center gap-4 px-4 py-3 rounded-lg text-sm w-full border",
97104
statusIcons[course.status].color,
98105
)}
99106
>
100-
<span className="font-semibold min-w-[80px]">{course.code}</span>
101-
<span className="flex-1">
107+
<span className="font-bold text-gray-900 text-left">{course.code}</span>
108+
<span className="text-left font-medium text-gray-900">
102109
{course.name}
103110
</span>
104-
<span className="text-gray-700">
111+
<span className="text-right text-gray-800">
105112
{isValidSemester ? course.semester : ''}
106-
{isApplied && course.grade && `${isValidSemester ? ' - ' : ''}Grade: ${course.grade}`}
113+
{isApplied && course.grade ? ` - Grade: ${course.grade}` : ''}
107114
</span>
108115
</div>
109116
);
@@ -126,6 +133,8 @@ const parseRequirementCode = (
126133
// Individual requirement row with expandable courses
127134
const RequirementRow = ({ requirement }: { requirement: RequirementRule }) => {
128135
const { getCourseById } = useAuditContext();
136+
const { openModal } = useCourseModalContext();
137+
129138
const courses = requirement.courses.map((courseId) =>
130139
getCourseById(courseId),
131140
);
@@ -136,40 +145,51 @@ const RequirementRow = ({ requirement }: { requirement: RequirementRule }) => {
136145
);
137146

138147
return (
139-
<div className="border-b border-gray-100 last:border-b-0">
148+
<div className="border border-gray-200 rounded-lg mb-3 last:mb-0 overflow-hidden">
140149
{/* Requirement header */}
141150
<button
142-
className="w-full py-3 px-2 flex items-start gap-3 hover:bg-gray-50 transition-colors"
151+
className="w-full py-3 px-3 flex items-start gap-3 hover:bg-gray-50 transition-colors bg-white"
143152
onClick={() => setIsExpanded(!isExpanded)}
144153
>
145-
<StatusIcon
154+
<RequirementStatusIcon
146155
current={requirement.appliedHours}
147156
total={requirement.requiredHours}
148157
/>
149158
<VStack gap={0} className="flex-1 text-left">
150-
<span className="font-bold text-base">{code}</span>
151-
<span className="text-sm text-gray-500">{description}</span>
159+
<span className="font-bold text-base text-gray-900">{code}</span>
160+
<span className="text-sm text-gray-600 mt-0.5">{description}</span>
152161
</VStack>
153-
<HStack y="middle" gap={2}>
162+
<HStack y="middle" gap={3}>
154163
<HoursBadge
155164
current={requirement.appliedHours}
156165
total={requirement.requiredHours}
157166
/>
158167
{isExpanded ? (
159-
<CaretUpIcon className="w-5 h-5 text-gray-400" />
168+
<CaretUpIcon className="w-5 h-5 text-gray-900" weight="bold" />
160169
) : (
161-
<CaretDownIcon className="w-5 h-5 text-gray-400" />
170+
<CaretDownIcon className="w-5 h-5 text-gray-900" weight="bold" />
162171
)}
163172
</HStack>
164173
</button>
165174

166-
{/* Expanded courses */}
167-
{isExpanded && requirement.courses.length > 0 && (
168-
<VStack gap={2} className="pl-11 pr-4 pb-3">
175+
{/* Expanded courses and add button */}
176+
{isExpanded && (
177+
<div className="flex flex-col gap-3 pl-12 pr-4 pb-4 bg-white">
169178
{courses.map((course, idx) => (
170179
<CoursePill key={`${course.code}-${idx}`} course={course} />
171180
))}
172-
</VStack>
181+
182+
<div className="w-full flex justify-center mt-2">
183+
<Button
184+
fill="solid"
185+
className="bg-[var(--color-dap-orange)] hover:opacity-90 text-white border-none w-max px-[24px] h-[40px] rounded-md font-semibold text-base flex items-center justify-center gap-[16px]"
186+
onClick={openModal}
187+
>
188+
<PlusIcon className="w-5 h-5" weight="bold" />
189+
Add Planned Course
190+
</Button>
191+
</div>
192+
</div>
173193
)}
174194
</div>
175195
);
@@ -212,9 +232,14 @@ const ProgressBar = ({
212232
}) => {
213233
const color = CATEGORY_COLORS[colorIndex % CATEGORY_COLORS.length];
214234
const percentage = Math.min((current / total) * 100, 100);
235+
236+
const trackColor = color.rgb.replace("rgb", "rgba").replace(")", ", 0.2)");
215237

216238
return (
217-
<div className="w-40 h-2 bg-gray-200 rounded-full overflow-hidden">
239+
<div
240+
className="w-40 h-2 rounded-full overflow-hidden"
241+
style={{ backgroundColor: trackColor }}
242+
>
218243
<div
219244
className="h-full rounded-full transition-all"
220245
style={{ width: `${percentage}%`, backgroundColor: color.tailwind }}
@@ -230,73 +255,56 @@ type RequirementBreakdownProps = {
230255
colorIndex?: number;
231256
};
232257
const RequirementBreakdown = (props: RequirementBreakdownProps) => {
233-
const { openModal } = useCourseModalContext();
234258
const { title, hours, requirements, colorIndex = 0 } = props;
235259
const [isOpen, setIsOpen] = useState(false);
236260
const borderColor = CATEGORY_COLORS[colorIndex % CATEGORY_COLORS.length];
237261

238262
return (
239263
<div
240-
className="w-full bg-gray-50 rounded-md border border-gray-200 overflow-hidden border-l-8"
264+
className="w-full bg-white rounded-md border border-gray-200 overflow-hidden border-l-4"
241265
style={{ borderLeftColor: borderColor.tailwind }}
242266
>
243267
{/* Main header */}
244268
<button
245-
className={cn(
246-
"w-full p-4 flex items-center justify-between hover:bg-gray-50 transition-colors",
247-
isOpen && "bg-gray-50",
248-
)}
269+
className="w-full p-4 flex items-center justify-between hover:bg-gray-50 transition-colors bg-white"
249270
onClick={() => setIsOpen(!isOpen)}
250271
>
251-
<VStack gap={1}>
252-
<span className="font-bold text-lg">{title}</span>
272+
<VStack gap={2}>
273+
<span className="font-bold text-base text-gray-900">{title}</span>
253274
<ProgressBar
254275
current={hours.current}
255276
total={hours.total}
256277
colorIndex={colorIndex}
257278
/>
258279
</VStack>
259280
<HStack y="middle" gap={2}>
260-
<span className="text-gray-600">
281+
<span className="text-gray-900 font-medium text-sm">
261282
{hours.current.toString().padStart(2, "0")} / {hours.total} hours
262283
</span>
263284
{isOpen ? (
264-
<CaretUpIcon className="w-5 h-5 text-gray-400" />
285+
<CaretUpIcon className="w-5 h-5 text-gray-900" weight="bold" />
265286
) : (
266-
<CaretDownIcon className="w-5 h-5 text-gray-400" />
287+
<CaretDownIcon className="w-5 h-5 text-gray-900" weight="bold" />
267288
)}
268289
</HStack>
269290
</button>
270291

271292
{/* Expanded content */}
272293
{isOpen && (
273-
<div className="border-t border-gray-200">
294+
<div className="bg-white">
274295
{/* Requirement rows */}
275-
<div className="px-4 py-2">
296+
<div className="px-4 py-4">
276297
{requirements.map((requirement, idx) => (
277298
<RequirementRow
278299
key={`${requirement.text.slice(0, 20)}-${idx}`}
279300
requirement={requirement}
280301
/>
281302
))}
282303
</div>
283-
284-
{/* Add course button */}
285-
<div className="px-4 pb-4">
286-
<Button
287-
color="black"
288-
fill="solid"
289-
className="w-full text-base font-semibold py-3 px-6"
290-
onClick={openModal}
291-
>
292-
<PlusCircleIcon className="w-5 h-5" />
293-
Add Hypothetical Course
294-
</Button>
295-
</div>
296304
</div>
297305
)}
298306
</div>
299307
);
300308
};
301309

302-
export default RequirementBreakdown;
310+
export default RequirementBreakdown;

0 commit comments

Comments
 (0)