feat: Quests page responsiveness and sidebar enhancements
This commit is contained in:
@ -1,5 +1,4 @@
|
||||
import {
|
||||
Sidebar,
|
||||
SidebarContent,
|
||||
SidebarHeader,
|
||||
SidebarFooter,
|
||||
@ -19,11 +18,12 @@ import {
|
||||
Target,
|
||||
Zap,
|
||||
Trophy,
|
||||
Map,
|
||||
} from "lucide-react";
|
||||
|
||||
import { useState } from "react";
|
||||
import logo from "../assets/ed_logo1.png";
|
||||
import { NavLink, useNavigate } from "react-router-dom";
|
||||
import { NavLink, useNavigate, useLocation } from "react-router-dom";
|
||||
import { useAuthStore } from "../stores/authStore";
|
||||
import { Avatar, AvatarFallback, AvatarImage } from "./ui/avatar";
|
||||
|
||||
@ -31,16 +31,199 @@ export function AppSidebar() {
|
||||
const [open, setOpen] = useState(false);
|
||||
const user = useAuthStore((s) => s.user);
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
const isQuestPage = location.pathname.startsWith("/student/quests");
|
||||
|
||||
const STYLES = `
|
||||
/* ══ DEFAULT sidebar (cream frosted glass) ══ */
|
||||
.as-sidebar-container {
|
||||
position: fixed;
|
||||
top: 0.5rem;
|
||||
bottom: 0.5rem;
|
||||
left: 0.5rem;
|
||||
width: 16rem;
|
||||
z-index: 10;
|
||||
border-radius: 1.75rem;
|
||||
overflow: hidden;
|
||||
pointer-events: auto;
|
||||
background: rgba(255,251,244,0.72);
|
||||
backdrop-filter: blur(24px) saturate(180%);
|
||||
-webkit-backdrop-filter: blur(24px) saturate(180%);
|
||||
border: 1.5px solid rgba(255,255,255,0.7);
|
||||
box-shadow:
|
||||
0 8px 32px rgba(0,0,0,0.12),
|
||||
0 2px 8px rgba(0,0,0,0.06),
|
||||
inset 0 1px 0 rgba(255,255,255,0.8);
|
||||
transition:
|
||||
background 0.4s ease,
|
||||
border-color 0.4s ease,
|
||||
box-shadow 0.4s ease,
|
||||
z-index 0.3s ease;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: calc(100vh - 1rem);
|
||||
}
|
||||
|
||||
/* ══ QUEST mode sidebar (dark navy pirate + gold) ══ */
|
||||
.as-sidebar-container.quest-mode {
|
||||
background: linear-gradient(
|
||||
180deg,
|
||||
rgba(251,191,36,0.12) 0%,
|
||||
rgba(251,191,36,0.08) 50%,
|
||||
rgba(251,191,36,0.1) 100%
|
||||
);
|
||||
background-size: 100% 200%;
|
||||
animation: asSweepDown 3s linear infinite;
|
||||
border-color: rgba(251,191,36,0.28);
|
||||
box-shadow:
|
||||
0 8px 32px rgba(0,0,0,0.25),
|
||||
0 2px 8px rgba(0,0,0,0.15),
|
||||
inset 0 1px 0 rgba(255,255,255,0.08);
|
||||
}
|
||||
|
||||
/* Shimmer animation from top to bottom */
|
||||
@keyframes asSweepDown {
|
||||
0% { background-position: 0% 200%; }
|
||||
100% { background-position: 0% -200%; }
|
||||
}
|
||||
|
||||
/* On quest page, sidebar stays visible above content */
|
||||
.as-sidebar-container.quest-mode {
|
||||
z-index: 40;
|
||||
}
|
||||
|
||||
.as-sidebar-inner {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.as-gradient-overlay {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.as-gradient-overlay.default {
|
||||
background: radial-gradient(
|
||||
circle at top,
|
||||
rgba(168,85,247,0.18),
|
||||
transparent 55%
|
||||
),
|
||||
radial-gradient(
|
||||
circle at bottom,
|
||||
rgba(249,115,22,0.1),
|
||||
transparent 55%
|
||||
);
|
||||
}
|
||||
|
||||
.as-gradient-overlay.quest {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
/* Ensure Sidebar sub-components are transparent */
|
||||
.as-sidebar-inner > * {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.as-sidebar-inner [class*="SidebarHeader"],
|
||||
.as-sidebar-inner [class*="SidebarContent"],
|
||||
.as-sidebar-inner [class*="SidebarFooter"],
|
||||
.as-sidebar-inner [class*="SidebarGroup"],
|
||||
.as-sidebar-inner [class*="SidebarMenu"] {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
/* Quest mode text visibility */
|
||||
.as-sidebar-container.quest-mode [class*="SidebarGroupLabel"] {
|
||||
color: rgba(255, 255, 255, 0.4) !important;
|
||||
}
|
||||
|
||||
.as-sidebar-container.quest-mode a {
|
||||
color: rgba(255, 255, 255, 0.6) !important;
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
.as-sidebar-container.quest-mode a:hover {
|
||||
color: rgba(255, 255, 255, 0.85) !important;
|
||||
background: rgba(255, 255, 255, 0.08) !important;
|
||||
}
|
||||
|
||||
.as-sidebar-container.quest-mode a.active {
|
||||
color: #fbbf24 !important;
|
||||
}
|
||||
|
||||
.as-sidebar-container.quest-mode span {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
/* Quest header text */
|
||||
.as-sidebar-container.quest-mode .text-slate-900 {
|
||||
color: rgba(255, 255, 255, 0.9) !important;
|
||||
}
|
||||
|
||||
.as-sidebar-container.quest-mode .text-slate-400 {
|
||||
color: rgba(255, 255, 255, 0.4) !important;
|
||||
}
|
||||
|
||||
/* Quest mode removes white hover background from menu buttons */
|
||||
.as-sidebar-container.quest-mode [class*="SidebarMenuButton"]:hover {
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
.as-sidebar-container.quest-mode [class*="SidebarMenuButton"] {
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
/* Prevent group-hover from adding white background in quest mode */
|
||||
.as-sidebar-container.quest-mode .group:hover {
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
/* Quest mode footer button styling */
|
||||
.as-sidebar-container.quest-mode [class*="SidebarFooter"] button {
|
||||
background: rgba(255, 255, 255, 0.08) !important;
|
||||
--tw-ring-color: rgba(255, 255, 255, 0.05) !important;
|
||||
border-color: rgba(255, 255, 255, 0.05) !important;
|
||||
}
|
||||
|
||||
.as-sidebar-container.quest-mode [class*="SidebarFooter"] button:hover {
|
||||
background: rgba(255, 255, 255, 0.12) !important;
|
||||
}
|
||||
|
||||
/* Override Tailwind bg-white and ring classes for quest mode */
|
||||
.as-sidebar-container.quest-mode button[class*="bg-white"] {
|
||||
background-color: rgba(255, 255, 255, 0.08) !important;
|
||||
}
|
||||
|
||||
.as-sidebar-container.quest-mode button[class*="ring-white"]:not(:hover) {
|
||||
--tw-ring-color: rgba(255, 255, 255, 0.05) !important;
|
||||
border-color: rgba(255, 255, 255, 0.05) !important;
|
||||
}
|
||||
|
||||
.as-sidebar-container.quest-mode button:hover[class*="hover:bg-white"] {
|
||||
background-color: rgba(255, 255, 255, 0.12) !important;
|
||||
}
|
||||
|
||||
.as-sidebar-container.quest-mode a:hover {
|
||||
--tw-ring-color: rgba(255, 255, 255, 0.05) !important;
|
||||
}
|
||||
`;
|
||||
|
||||
return (
|
||||
<Sidebar
|
||||
variant="floating"
|
||||
className="pointer-events-none border-0 bg-transparent px-0 py-0"
|
||||
>
|
||||
<div className="pointer-events-auto fixed inset-y-2 left-2 flex w-64 flex-col overflow-hidden rounded-[1.75rem] bg-[rgba(249,240,255,0.82)] shadow-[0_18px_45px_rgba(15,23,42,0.16)] transition-colors duration-300">
|
||||
<div className="pointer-events-none absolute inset-0 bg-[radial-gradient(circle_at_top,rgba(168,85,247,0.18),transparent_55%),radial-gradient(circle_at_bottom,rgba(249,115,22,0.1),transparent_55%)]" />
|
||||
<>
|
||||
<style>{STYLES}</style>
|
||||
<div
|
||||
className={`as-sidebar-container${isQuestPage ? " quest-mode" : ""}`}
|
||||
>
|
||||
<div
|
||||
className={`as-gradient-overlay ${isQuestPage ? "quest" : "default"}`}
|
||||
/>
|
||||
|
||||
<div className="relative flex h-full flex-col">
|
||||
<div className="as-sidebar-inner">
|
||||
{/* HEADER */}
|
||||
<SidebarHeader className="px-3 pb-4 pt-1">
|
||||
<div className="flex items-center justify-start gap-2">
|
||||
@ -76,12 +259,12 @@ export function AppSidebar() {
|
||||
<SidebarMenuItem>
|
||||
<SidebarMenuButton
|
||||
asChild
|
||||
className="group cursor-pointer rounded-2xl px-2 py-2.5 transition-colors duration-200 hover:bg-white"
|
||||
className="group cursor-pointer px-2 py-2.5 transition-colors duration-200"
|
||||
>
|
||||
<NavLink
|
||||
to="/student/home"
|
||||
className={({ isActive }) =>
|
||||
`flex items-center gap-2.5 text-sm font-satoshi ${
|
||||
`flex items-center gap-2.5 text-sm font-satoshi rounded-2xl px-2 py-2.5 transition-all duration-200 ${
|
||||
isActive
|
||||
? "text-slate-900"
|
||||
: "text-slate-500 group-hover:text-slate-900"
|
||||
@ -92,11 +275,20 @@ export function AppSidebar() {
|
||||
<>
|
||||
<Home
|
||||
size={18}
|
||||
strokeWidth={3}
|
||||
className={
|
||||
isActive ? "text-orange-400" : "text-slate-400"
|
||||
}
|
||||
/>
|
||||
<span>Home</span>
|
||||
<span
|
||||
className={
|
||||
isActive
|
||||
? "text-orange-400 font-extrabold"
|
||||
: "text-slate-400 font-bold"
|
||||
}
|
||||
>
|
||||
Home
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
</NavLink>
|
||||
@ -109,13 +301,13 @@ export function AppSidebar() {
|
||||
onMouseLeave={() => setOpen(false)}
|
||||
>
|
||||
<SidebarMenuButton
|
||||
className="group cursor-pointer rounded-2xl px-2 py-2.5 transition-colors duration-200 hover:bg-white"
|
||||
className="group cursor-pointer px-2 py-2.5 transition-colors duration-200"
|
||||
asChild
|
||||
>
|
||||
<NavLink
|
||||
to="/student/practice"
|
||||
className={({ isActive }) =>
|
||||
`flex items-center gap-2.5 text-sm font-satoshi ${
|
||||
`flex items-center gap-2.5 text-sm font-satoshi rounded-2xl px-2 py-2.5 transition-all duration-200 ${
|
||||
isActive
|
||||
? "text-slate-900"
|
||||
: "text-slate-500 group-hover:text-slate-900"
|
||||
@ -126,13 +318,23 @@ export function AppSidebar() {
|
||||
<>
|
||||
<BookOpen
|
||||
size={18}
|
||||
strokeWidth={3}
|
||||
className={
|
||||
isActive ? "text-purple-500" : "text-slate-400"
|
||||
}
|
||||
/>
|
||||
<span>Practice</span>
|
||||
<span
|
||||
className={
|
||||
isActive
|
||||
? "text-purple-500 font-extrabold"
|
||||
: "text-slate-400 font-bold"
|
||||
}
|
||||
>
|
||||
Practice
|
||||
</span>
|
||||
<ChevronDown
|
||||
size={16}
|
||||
strokeWidth={3}
|
||||
className={`ml-auto text-slate-400 transition-transform ${
|
||||
open ? "rotate-180" : ""
|
||||
}`}
|
||||
@ -153,7 +355,11 @@ export function AppSidebar() {
|
||||
}`
|
||||
}
|
||||
>
|
||||
<Target size={18} className="text-slate-400" />
|
||||
<Target
|
||||
size={18}
|
||||
strokeWidth={3}
|
||||
className="text-slate-400"
|
||||
/>
|
||||
<span>Targeted Practice</span>
|
||||
</NavLink>
|
||||
<NavLink
|
||||
@ -166,7 +372,11 @@ export function AppSidebar() {
|
||||
}`
|
||||
}
|
||||
>
|
||||
<Zap size={18} className="text-slate-400" />
|
||||
<Zap
|
||||
size={18}
|
||||
strokeWidth={3}
|
||||
className="text-slate-400"
|
||||
/>
|
||||
<span>Drills</span>
|
||||
</NavLink>
|
||||
<NavLink
|
||||
@ -179,23 +389,87 @@ export function AppSidebar() {
|
||||
}`
|
||||
}
|
||||
>
|
||||
<Trophy size={18} className="text-slate-400" />
|
||||
<Trophy
|
||||
size={18}
|
||||
strokeWidth={3}
|
||||
className="text-slate-400"
|
||||
/>
|
||||
<span>Hard Test Modules</span>
|
||||
</NavLink>
|
||||
</SidebarMenuSub>
|
||||
)}
|
||||
</SidebarMenuItem>
|
||||
|
||||
{/* QUESTS */}
|
||||
<SidebarMenuItem>
|
||||
<SidebarMenuButton
|
||||
asChild
|
||||
className="group cursor-pointer px-2 py-2.5 transition-colors duration-200"
|
||||
>
|
||||
<NavLink
|
||||
to="/student/quests"
|
||||
className={({ isActive }) => {
|
||||
if (isActive && isQuestPage) {
|
||||
return "flex items-center gap-2.5 text-sm rounded-2xl px-2 py-2.5 transition-all duration-200";
|
||||
}
|
||||
if (isActive) {
|
||||
return "flex items-center gap-2.5 text-sm font-satoshi rounded-2xl px-2 py-2.5 transition-all duration-200 text-slate-900";
|
||||
}
|
||||
return "flex items-center gap-2.5 text-sm font-satoshi rounded-2xl px-2 py-2.5 transition-all duration-200 text-slate-500 group-hover:text-slate-900";
|
||||
}}
|
||||
>
|
||||
{({ isActive }) => (
|
||||
<>
|
||||
<Map
|
||||
size={18}
|
||||
strokeWidth={3}
|
||||
className={
|
||||
isActive && isQuestPage
|
||||
? "text-amber-400"
|
||||
: isActive
|
||||
? "text-blue-500"
|
||||
: "text-slate-400"
|
||||
}
|
||||
/>
|
||||
<span
|
||||
className={
|
||||
isActive && isQuestPage
|
||||
? ""
|
||||
: isActive
|
||||
? "text-blue-500 font-extrabold"
|
||||
: "text-slate-400 font-bold"
|
||||
}
|
||||
style={
|
||||
isActive && isQuestPage
|
||||
? {
|
||||
fontFamily: "'Sorts Mill Goudy', serif",
|
||||
fontSize: "0.95rem",
|
||||
fontWeight: 900,
|
||||
letterSpacing: "0.05em",
|
||||
color: "#fbbf24",
|
||||
textShadow: "0 0 12px rgba(251,191,36,0.5)",
|
||||
}
|
||||
: {}
|
||||
}
|
||||
>
|
||||
Quests
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
</NavLink>
|
||||
</SidebarMenuButton>
|
||||
</SidebarMenuItem>
|
||||
|
||||
{/* LESSONS */}
|
||||
<SidebarMenuItem>
|
||||
<SidebarMenuButton
|
||||
asChild
|
||||
className="group cursor-pointer rounded-2xl px-2 py-2.5 transition-colors duration-200 hover:bg-white"
|
||||
className="group cursor-pointer px-2 py-2.5 transition-colors duration-200"
|
||||
>
|
||||
<NavLink
|
||||
to="/student/lessons"
|
||||
className={({ isActive }) =>
|
||||
`flex items-center gap-2.5 text-sm font-satoshi ${
|
||||
`flex items-center gap-2.5 text-sm font-satoshi rounded-2xl px-2 py-2.5 transition-all duration-200 ${
|
||||
isActive
|
||||
? "text-slate-900"
|
||||
: "text-slate-500 group-hover:text-slate-900"
|
||||
@ -206,11 +480,20 @@ export function AppSidebar() {
|
||||
<>
|
||||
<Video
|
||||
size={18}
|
||||
strokeWidth={3}
|
||||
className={
|
||||
isActive ? "text-cyan-500" : "text-slate-400"
|
||||
}
|
||||
/>
|
||||
<span>Lessons</span>
|
||||
<span
|
||||
className={
|
||||
isActive
|
||||
? "text-cyan-500 font-extrabold"
|
||||
: "text-slate-400 font-bold"
|
||||
}
|
||||
>
|
||||
Lessons
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
</NavLink>
|
||||
@ -221,12 +504,12 @@ export function AppSidebar() {
|
||||
<SidebarMenuItem>
|
||||
<SidebarMenuButton
|
||||
asChild
|
||||
className="group cursor-pointer rounded-2xl px-2 py-2.5 transition-colors duration-200 hover:bg-white"
|
||||
className="group cursor-pointer px-2 py-2.5 transition-colors duration-200"
|
||||
>
|
||||
<NavLink
|
||||
to="/student/rewards"
|
||||
className={({ isActive }) =>
|
||||
`flex items-center gap-2.5 text-sm font-satoshi ${
|
||||
`flex items-center gap-2.5 text-sm font-satoshi rounded-2xl px-2 py-2.5 transition-all duration-200 ${
|
||||
isActive
|
||||
? "text-slate-900"
|
||||
: "text-slate-500 group-hover:text-slate-900"
|
||||
@ -237,11 +520,20 @@ export function AppSidebar() {
|
||||
<>
|
||||
<Trophy
|
||||
size={18}
|
||||
strokeWidth={3}
|
||||
className={
|
||||
isActive ? "text-emerald-500" : "text-slate-400"
|
||||
}
|
||||
/>
|
||||
<span>Rewards</span>
|
||||
<span
|
||||
className={
|
||||
isActive
|
||||
? "text-emerald-500 font-extrabold"
|
||||
: "text-slate-400 font-bold"
|
||||
}
|
||||
>
|
||||
Rewards
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
</NavLink>
|
||||
@ -268,11 +560,15 @@ export function AppSidebar() {
|
||||
<span className="font-medium text-slate-900">{user?.name}</span>
|
||||
<span className="text-xs text-slate-400">{user?.email}</span>
|
||||
</div>
|
||||
<ChevronDown size={16} className="ml-auto text-slate-400" />
|
||||
<ChevronDown
|
||||
size={16}
|
||||
strokeWidth={3}
|
||||
className="ml-auto text-slate-400"
|
||||
/>
|
||||
</button>
|
||||
</SidebarFooter>
|
||||
</div>
|
||||
</div>
|
||||
</Sidebar>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user