Skip to content

Commit 3a258b1

Browse files
Audit Card Selected State Matching Figma (#70)
* Updated audit card to be more like Figma * Updated audit card to be more like Figma * Updated audit card styling to match Figma design
1 parent 903ef7e commit 3a258b1

4 files changed

Lines changed: 171 additions & 116 deletions

File tree

entrypoints/components/audit-card.tsx

Lines changed: 29 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -18,78 +18,33 @@ const DegreeAuditCard: React.FC<DegreeAuditCardProps> = ({
1818
}) => {
1919
return (
2020
<div
21-
className={`!rounded-lg px-4 py-3 w-full transition-all duration-200 cursor-pointer ${
21+
className={`!rounded px-3 py-[12px] w-full transition-all duration-200 cursor-pointer ${
2222
isSelected
23-
? "bg-[#FFF7ED] border-2 border-[#F9E7D5]"
24-
: "bg-[var(--color-dap-stone-50)] border-2 border-[var(--color-dap-border)]"
23+
? "bg-[var(--color-dap-orange)] border border-[var(--color-dap-orange)]"
24+
: "bg-[#FAFAF9] border border-[var(--color-dap-border)]"
2525
}`}
2626
onClick={onToggle}
2727
>
28-
<div className="flex flex-col gap-2">
29-
{/* Header Row: Caret + Title + Menu + Percentage */}
30-
<div className="flex items-center justify-between">
31-
<div className="flex items-center gap-2">
32-
{isExpanded ? (
33-
<CaretUp
34-
size={16}
35-
weight="bold"
36-
className="text-[var(--color-dap-dark)]"
37-
/>
38-
) : (
39-
<CaretDown
40-
size={16}
41-
weight="bold"
42-
className="text-[var(--color-dap-dark)]"
43-
/>
44-
)}
45-
<span className="font-bold text-[18px] leading-[20px] text-[var(--color-dap-primary)] tracking-[0.13px] overflow-hidden text-ellipsis whitespace-nowrap">
46-
{title}
47-
</span>
48-
</div>
49-
<div className="flex items-center gap-2">
50-
<button
51-
onClick={(e) => {
52-
e.stopPropagation();
53-
onMenuClick?.();
54-
}}
55-
className="p-1 hover:bg-black/5 rounded"
56-
>
57-
<DotsThree
58-
size={20}
59-
weight="bold"
60-
className="text-[var(--color-dap-dark)]"
61-
/>
62-
</button>
63-
<div className="bg-[var(--color-dap-orange)] px-2 py-1 rounded h-[26px] flex items-center justify-center">
64-
<span className="text-white text-sm font-bold leading-[18px]">
65-
{percentage}%
66-
</span>
67-
</div>
68-
</div>
28+
<div className="flex items-center justify-between">
29+
{/* Title */}
30+
<div className={`font-bold text-[18px] leading-tight ${
31+
isSelected ? "text-white" : "text-[var(--color-dap-orange)]"
32+
}`}>
33+
{title}
6934
</div>
7035

71-
{/* Tags Section - Only visible when expanded */}
72-
{isExpanded && (
73-
<div className="flex flex-col gap-2 ml-6">
74-
{/* Majors */}
75-
{majors && majors.length > 0 && (
76-
<div className="flex flex-wrap gap-1">
77-
{majors.map((major, index) => (
78-
<Tag key={index} index={index} major={major} type="major" />
79-
))}
80-
</div>
81-
)}
82-
83-
{/* Minors/Certs */}
84-
{minors && minors.length > 0 && (
85-
<div className="flex flex-wrap gap-1">
86-
{minors.map((minor, index) => (
87-
<Tag key={index} index={index} major={minor} type="minor" />
88-
))}
89-
</div>
90-
)}
91-
</div>
92-
)}
36+
{/* Percentage Badge */}
37+
<div className={`rounded-md px-3 py-2 flex items-center justify-center ${
38+
isSelected
39+
? "bg-white"
40+
: "bg-[var(--color-dap-orange)]"
41+
}`}>
42+
<span className={`text-base font-bold leading-tight ${
43+
isSelected ? "text-[var(--color-dap-orange)]" : "text-white"
44+
}`}>
45+
{percentage}%
46+
</span>
47+
</div>
9348
</div>
9449
</div>
9550
);
@@ -105,49 +60,18 @@ export const DegreeAuditCardPopup: React.FC<DegreeAuditCardProps> = ({
10560
percentage = 90,
10661
}) => {
10762
return (
108-
<div className="bg-[var(--color-dap-stone-50)] rounded-sm border-2 border-[var(--color-dap-border)] px-4 py-3 w-full transition-all duration-200 hover:bg-[#FFF7ED] hover:border-2 hover:border-[#F9E7D5] cursor-pointer">
109-
<div className="flex flex-col gap-3">
63+
<div className="bg-[#FAFAF9] rounded border border-[var(--color-dap-border)] px-3 py-[12px] w-full transition-all duration-200 cursor-pointer">
64+
<div className="flex items-center justify-between">
11065
{/* Title */}
111-
<div className="font-bold text-[18px] leading-[20px] text-[var(--color-dap-primary)] tracking-[0.13px] overflow-hidden text-ellipsis whitespace-nowrap">
66+
<div className="font-bold text-[18px] leading-tight text-[var(--color-dap-orange)]">
11267
{title}
11368
</div>
114-
<div className="flex items-start justify-between">
115-
<div className="flex flex-col gap-2">
116-
{/* Majors */}
117-
{majors && majors.length > 0 && (
118-
<div className="flex gap-2 items-center">
119-
<span className="text-[11px] leading-[12px] text-black">
120-
Major:
121-
</span>
122-
<div className="flex gap-1 flex-wrap">
123-
{majors.map((major, index) => (
124-
<Tag key={index} index={index} major={major} type="major" />
125-
))}
126-
</div>
127-
</div>
128-
)}
129-
130-
{/* Minors/Certs */}
131-
{minors && minors.length > 0 && (
132-
<div className="flex gap-2 items-center">
133-
<span className="text-[11px] leading-[12px] text-black">
134-
Minor/cert:
135-
</span>
136-
<div className="flex gap-1 flex-wrap">
137-
{minors.map((minor, index) => (
138-
<Tag key={index} index={index} major={minor} type="minor" />
139-
))}
140-
</div>
141-
</div>
142-
)}
143-
</div>
14469

145-
{/* Percentage */}
146-
<div className="bg-[var(--color-dap-orange)] rounded-md px-2 py-1 h-[30px] flex items-center justify-center">
147-
<span className="text-white text-base font-bold leading-[22px]">
148-
{percentage}%
149-
</span>
150-
</div>
70+
{/* Percentage Badge */}
71+
<div className="bg-[var(--color-dap-orange)] rounded-md px-3 py-2 flex items-center justify-center">
72+
<span className="text-white text-base font-bold leading-tight">
73+
{percentage}%
74+
</span>
15175
</div>
15276
</div>
15377
</div>

entrypoints/popup-app/main.tsx

Lines changed: 113 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import Button from "../components/common/button";
55
import { DegreeAuditCardPopup } from "../components/audit-card";
66
import "./style.css";
77
import DAPLogo from "@/assets/svgs/dap-circle-logo";
8+
import logo from "../../public/logo.png";
89
import { PlusIcon } from "@phosphor-icons/react";
910
import { getAuditHistory } from "@/lib/storage";
1011
import type { DegreeAuditCardProps } from "@/lib/general-types";
@@ -133,13 +134,121 @@ export default function App() {
133134
// setHypotheticalCourses((prev) => [...prev, course]);
134135
// // TODO: Store in browser storage and send to degree audit page
135136
// };
137+
const Pill = ({ text }: { text: string }) => (
138+
<span className="inline-flex items-center rounded-full bg-[var(--color-dap-primary)] px-2.5 py-[2px] text-[12px] font-extrabold leading-none text-white">
139+
{text}
140+
</span>
141+
);
142+
143+
const AuditCardDetailed = ({ audit }: { audit: DegreeAuditCardProps }) => (
144+
<div
145+
className="
146+
group
147+
w-full
148+
rounded-[12px]
149+
border
150+
border-gray-200
151+
bg-white
152+
px-5
153+
py-4
154+
hover:cursor-pointer
155+
hover:bg-[var(--color-dap-primary)]
156+
transition-colors
157+
"
158+
onClick={(e) => {
159+
e.stopPropagation();
160+
handleOpenDegreeAuditPage(audit?.auditId);
161+
}}
162+
>
163+
<div className="flex items-start justify-between gap-4">
164+
{/* Left */}
165+
<div className="min-w-0 flex-1">
166+
<div
167+
className="
168+
text-[20px]
169+
font-extrabold
170+
text-[var(--color-dap-primary)]
171+
leading-tight
172+
transition-colors
173+
group-hover:text-white
174+
"
175+
>
176+
{audit.title}
177+
</div>
178+
179+
<div className="mt-3 flex items-center gap-3">
180+
<span
181+
className="
182+
text-[13px]
183+
font-medium
184+
tracking-wide
185+
text-black
186+
transition-colors
187+
hover:text-white
188+
"
189+
>
190+
MAJOR
191+
</span>
192+
193+
{(audit.majors ?? []).map((m, i) => (
194+
<span
195+
key={`${m}-${i}`}
196+
className="
197+
inline-flex items-center
198+
rounded-[6px]
199+
bg-[#068b5e]
200+
px-3 py-[3px]
201+
text-[12px]
202+
leading-none
203+
text-white
204+
"
205+
>
206+
{m}
207+
</span>
208+
))}
209+
</div>
210+
211+
{/* Optional MINOR/CERT row if you want parity with the detailed card */}
212+
{(audit.minors ?? []).length > 0 && (
213+
<div className="mt-2 flex items-center gap-4">
214+
<div className="text-[13px] font-extrabold tracking-wide text-[var(--color-ut-charcoal)]">
215+
MINOR/CERT
216+
</div>
217+
<div className="flex flex-wrap gap-2">
218+
{(audit.minors ?? []).map((m, i) => (
219+
<span
220+
key={`${m}-${i}`}
221+
className="inline-flex items-center rounded-full bg-gray-100 px-2.5 py-[2px] text-[12px] font-bold leading-none text-[var(--color-ut-charcoal)]"
222+
>
223+
{m}
224+
</span>
225+
))}
226+
</div>
227+
</div>
228+
)}
229+
</div>
230+
231+
{/* Right badge */}
232+
<div className="relative top-6">
233+
<div className="rounded-[10px] bg-[var(--color-dap-primary)] px-3.5 py-2 text-white text-[16px] font-extrabold leading-none">
234+
{audit.percentage}%
235+
</div>
236+
</div>
237+
</div>
238+
</div>
239+
);
240+
136241

137242
return (
138243
<div className="w-[438px] h-full min-h-[300px] max-h-[600px] bg-white font-sans overflow-hidden flex flex-col border border-gray-100">
139244
<header className="flex justify-between items-center p-3 border-b border-gray-200 flex-shrink-0">
140245
<div className="flex items-center space-x-2">
141-
<DAPLogo />
142-
<span className="font-bold text-lg text-dap-primary leading-tight">
246+
<img
247+
src={logo}
248+
alt="Logo"
249+
style={{ width: "70px", height: "auto" }}
250+
/>
251+
<span className="font-bold text-lg text-dap-primary leading-tight">
143252
Degree Audit
144253
<br />
145254
Plus
@@ -170,7 +279,7 @@ export default function App() {
170279
style={{ fontFamily: "Roboto Flex" }}
171280
className="text-[25.63px] font-bold text-[var(--color-dap-dark)]"
172281
>
173-
Current Audits
282+
{audits.length} AUDITS
174283
</h1>
175284
{isSyncing && (
176285
<div className="flex items-center gap-1.5 text-[var(--color-dap-gray-light)]">
@@ -230,6 +339,7 @@ export default function App() {
230339
minors={audit.minors}
231340
percentage={audit.percentage}
232341
/>
342+
233343
</div>
234344
))}
235345
</div>

lib/audit-history-scraper.ts

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,22 +9,43 @@ function parseMajor(programText: string): string {
99
// Clean up whitespace first (normalize spaces and newlines)
1010
const cleanText = programText.replace(/\s+/g, " ").trim();
1111

12+
// NEW: strip leading punctuation and an optional "major" label
13+
function cleanExtractedMajor(raw: string): string {
14+
return raw
15+
.replace(/^\s*[:,-]+\s*/g, "") // leading ": , -"
16+
.replace(/^\s*major\s*[:,-]?\s*/i, "") // leading "major", "Major:", etc.
17+
.trim();
18+
}
19+
20+
// Accepts: "B S", "BS", "B.S.", "B A", "BA", "B.A."
21+
const BSBA_PREFIX = /B\.?\s*[SA]\.?\s*/;
22+
1223
// Pattern 1: "B S in Communication and Leadership" (with "in")
13-
const withInMatch = cleanText.match(/B [SA] in (.+?)(?:\(|$)/);
14-
if (withInMatch) return withInMatch[1].trim();
24+
const withInMatch = cleanText.match(
25+
new RegExp(`${BSBA_PREFIX.source}in\\s+(.+?)(?:\\(|$)`, "i"),
26+
);
27+
if (withInMatch) return cleanExtractedMajor(withInMatch[1]);
28+
29+
// Pattern 2: "B S Computer Science, CS" or "B A Economics" or "B S Mathematics"
30+
const spacedMatch = cleanText.match(
31+
new RegExp(`${BSBA_PREFIX.source}(.+?)(?:,|\\(|$)`, "i"),
32+
);
33+
if (spacedMatch) return cleanExtractedMajor(spacedMatch[1]);
1534

16-
// Pattern 2: "B S Computer Science, CS" or "B A Economics"
17-
const spacedMatch = cleanText.match(/B [SA] (.+?)(?:,|\()/);
18-
if (spacedMatch) return spacedMatch[1].trim();
35+
// Other degree letters (keep your behavior, just allow dots/no-space and end-of-string)
36+
const degreeMatch = cleanText.match(/B\.?\s*[A-Z]\.?\s*(.+?)(?:,|\(|$)/i);
37+
if (degreeMatch) return cleanExtractedMajor(degreeMatch[1]);
1938

2039
// Pattern 3: "BSGS, Climate System Science" or "DEGREE_CODE, Major Name"
21-
// Handles degrees where code comes first, then major name after comma
2240
const codeFirstMatch = cleanText.match(/^[A-Z]+,\s*(.+?)(?:\(|$)/);
23-
if (codeFirstMatch) return codeFirstMatch[1].trim();
41+
if (codeFirstMatch) return cleanExtractedMajor(codeFirstMatch[1]);
2442

25-
// Fallback: first line
43+
// Fallback: first token
2644
return cleanText.split(" ")[0];
2745
}
46+
47+
48+
2849

2950
function parseCredential(programText: string): string | null {
3051
const match = programText.match(/- Credential:\s*(.+?)\s*\(/);

public/logo.png

2.54 KB
Loading

0 commit comments

Comments
 (0)