AshameTheDestroyer commited on
Commit
5f4ae90
·
1 Parent(s): d25fbba

Breadcrumbs.

Browse files
frontend/app/components/breadcrumbs.tsx ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { cn } from "~/lib/utils";
2
+ import { HomeIcon } from "lucide-react";
3
+
4
+ export function Breadcrumbs() {
5
+ const pathname =
6
+ typeof window !== "undefined" ? window.location.pathname : "/";
7
+
8
+ const segments = pathname.split("/").filter(Boolean);
9
+ const crumbs = [
10
+ { name: "/", href: "/", isEllipsis: false },
11
+ ...segments.map((seg, i) => {
12
+ const href = "/" + segments.slice(0, i + 1).join("/");
13
+ const name = decodeURIComponent(seg)
14
+ .replace(/-/g, " ")
15
+ .replace(/\b\w/g, (c) => c.toUpperCase());
16
+ return { name, href, isEllipsis: false };
17
+ }),
18
+ ];
19
+
20
+ const displayCrumbs =
21
+ crumbs.length > 4
22
+ ? [
23
+ crumbs[0],
24
+ { name: "...", href: null, isEllipsis: true },
25
+ crumbs[crumbs.length - 2],
26
+ crumbs[crumbs.length - 1],
27
+ ]
28
+ : crumbs;
29
+
30
+ return (
31
+ <nav aria-label="Breadcrumb" className="flex items-center">
32
+ <ol className="flex items-center gap-2 text-sm">
33
+ {displayCrumbs.map((c, i, array) => (
34
+ <li
35
+ key={c.href ?? `ellipsis-${i}`}
36
+ className="flex items-center gap-2"
37
+ >
38
+ {c.isEllipsis ? (
39
+ <span className="text-muted-foreground">...</span>
40
+ ) : (
41
+ <a
42
+ href={i == 0 || i == array.length -1 ? undefined : c.href!}
43
+ className={cn(
44
+ "text-ellipsis overflow-hidden whitespace-nowrap max-w-36",
45
+ i > 0 && i < array.length - 1
46
+ ? "font-medium hover:underline"
47
+ : "text-muted-foreground"
48
+ )}
49
+ aria-current={
50
+ i > 0 && i < array.length - 1
51
+ ? "page"
52
+ : undefined
53
+ }
54
+ >
55
+ {c.name == "/" ? <HomeIcon className="size-4" /> : c.name}
56
+ </a>
57
+ )}
58
+ {i < displayCrumbs.length - 1 && (
59
+ <span className="text-muted-foreground">/</span>
60
+ )}
61
+ </li>
62
+ ))}
63
+ </ol>
64
+ </nav>
65
+ );
66
+ }
frontend/app/components/header.tsx CHANGED
@@ -1,7 +1,8 @@
1
- "use client"
2
 
3
  import { Button } from "./ui/button";
4
  import { useSidebar } from "./ui/sidebar";
 
5
  import { ThemeToggle } from "./theme-toggle";
6
  import { SidebarCloseIcon, SidebarOpenIcon } from "lucide-react";
7
 
@@ -9,11 +10,16 @@ export function Header() {
9
  const { state, toggleSidebar } = useSidebar();
10
 
11
  return (
12
- <header className="flex place-content-between p-4">
13
  <Button onClick={() => toggleSidebar()} variant="ghost" size="icon">
14
- {state == "collapsed" ? <SidebarOpenIcon /> : <SidebarCloseIcon />}
 
 
 
 
15
  </Button>
 
16
  <ThemeToggle />
17
  </header>
18
  );
19
- }
 
1
+ "use client";
2
 
3
  import { Button } from "./ui/button";
4
  import { useSidebar } from "./ui/sidebar";
5
+ import { Breadcrumbs } from "./breadcrumbs";
6
  import { ThemeToggle } from "./theme-toggle";
7
  import { SidebarCloseIcon, SidebarOpenIcon } from "lucide-react";
8
 
 
10
  const { state, toggleSidebar } = useSidebar();
11
 
12
  return (
13
+ <header className="flex place-content-between gap-2 p-4 [&>nav]:mr-auto place-items-center">
14
  <Button onClick={() => toggleSidebar()} variant="ghost" size="icon">
15
+ {state == "collapsed" ? (
16
+ <SidebarOpenIcon />
17
+ ) : (
18
+ <SidebarCloseIcon />
19
+ )}
20
  </Button>
21
+ <Breadcrumbs />
22
  <ThemeToggle />
23
  </header>
24
  );
25
+ }
frontend/app/components/question-card.tsx CHANGED
@@ -76,7 +76,7 @@ export function QuestionCard({
76
  </div>
77
  ))}
78
  {isStatic && question.correct_options && question.correct_options.some(co => !(answers[question.id] || []).includes(co)) &&
79
- <div className="text-red-600 dark:text-red-400 ml-2">(You did not select all correct answers)</div>}
80
  </div>
81
  ),
82
  }[question.type]}
 
76
  </div>
77
  ))}
78
  {isStatic && question.correct_options && question.correct_options.some(co => !(answers[question.id] || []).includes(co)) &&
79
+ <div className="text-red-600 dark:text-red-400 mt-2">(You did not select all correct answers)</div>}
80
  </div>
81
  ),
82
  }[question.type]}