feat: Quests page responsiveness and sidebar enhancements

This commit is contained in:
2026-03-04 03:22:22 +06:00
parent c35f328e30
commit 437c7a517f
37 changed files with 398 additions and 65 deletions

View File

@ -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>
</>
);
}