refactor(ui): fix layout issues and temporarily disable auth features

- app & navbar: comment out login, register, and logout features.
- scan-page: align info cards vertically and reduce upload area padding.
- mobile-nav: implement React Portal to fix overlapping menu issue.
This commit is contained in:
seriouselly
2026-06-06 03:01:07 +07:00
parent 9f9098b91b
commit 450b2f9d97
4 changed files with 71 additions and 70 deletions
+8 -10
View File
@@ -5,7 +5,7 @@ import {
Navigate, Navigate,
} from "react-router-dom"; } from "react-router-dom";
import { AuthInitializer } from "@/components/auth-initializer"; import { AuthInitializer } from "@/components/auth-initializer";
import { AuthGuard } from "@/components/auth-guard"; // import { AuthGuard } from "@/components/auth-guard";
import { DashboardPage } from "@/pages/dashboard-page"; import { DashboardPage } from "@/pages/dashboard-page";
import { ScanPage } from "@/pages/scan-page"; import { ScanPage } from "@/pages/scan-page";
import { LibraryPage } from "@/pages/library-page"; import { LibraryPage } from "@/pages/library-page";
@@ -14,16 +14,16 @@ import { DiseaseDetailPage } from "@/pages/disease-detail-page";
import { DiagnosisDetailPage } from "@/pages/diagnosis-detail-page"; import { DiagnosisDetailPage } from "@/pages/diagnosis-detail-page";
import { ExpertReviewsPage } from "@/pages/expert-reviews-page"; import { ExpertReviewsPage } from "@/pages/expert-reviews-page";
import { DiagnosesPage } from "@/pages/diagnoses-page"; import { DiagnosesPage } from "@/pages/diagnoses-page";
import { LoginPage } from "@/pages/login-page"; // import { LoginPage } from "@/pages/login-page";
import { RegisterPage } from "@/pages/register-page"; // import { RegisterPage } from "@/pages/register-page";
import { MainLayout } from "@/components/layout/main-layout"; import { MainLayout } from "@/components/layout/main-layout";
const queryClient = new QueryClient(); const queryClient = new QueryClient();
const router = createBrowserRouter([ const router = createBrowserRouter([
{ path: "/", element: <Navigate to="/dashboard" replace /> }, { path: "/", element: <Navigate to="/dashboard" replace /> },
{ path: "/login", element: <LoginPage /> }, // { path: "/login", element: <LoginPage /> },
{ path: "/register", element: <RegisterPage /> }, // { path: "/register", element: <RegisterPage /> },
{ {
path: "/dashboard", path: "/dashboard",
element: ( element: (
@@ -67,11 +67,9 @@ const router = createBrowserRouter([
{ {
path: "/expert/reviews", path: "/expert/reviews",
element: ( element: (
<AuthGuard requireExpert> <MainLayout>
<MainLayout> <ExpertReviewsPage />
<ExpertReviewsPage /> </MainLayout>
</MainLayout>
</AuthGuard>
), ),
}, },
{ {
+40 -28
View File
@@ -1,38 +1,58 @@
import { createPortal } from "react-dom";
import { Link, useLocation } from "react-router-dom"; import { Link, useLocation } from "react-router-dom";
import { X, Leaf } from "lucide-react"; import { X, Leaf } from "lucide-react";
type Props = { type Props = {
open: boolean; open: boolean;
onClose: () => void; onClose: () => void;
userRole?: string | null;
}; };
const NAV_ITEMS = [ const NAV_ITEMS = [
{ path: "/dashboard", label: "Dashboard" }, { path: "/dashboard", label: "Dashboard" },
{ path: "/scan", label: "Scan Tanaman" }, { path: "/scan", label: "Scan Tanaman" },
{ path: "/diagnoses", label: "Diagnosa" }, { path: "/diagnoses", label: "Diagnosa" },
{ path: "/catalog", label: "Pustaka" }, { path: "/catalog", label: "Pustaka", altPath: "/library" },
]; ];
export function MobileNav({ open, onClose, userRole }: Props) { export function MobileNav({ open, onClose }: Props) {
const location = useLocation(); const location = useLocation();
if (!open) return null;
const isActive = (path: string) => location.pathname === path; const isActive = (path: string) => location.pathname === path;
return ( const navContent = (
<div className="fixed inset-0 z-50 lg:hidden"> <div
// 2. Ubah z-50 menjadi z-[999] agar levelnya mentok paling atas
className={`fixed inset-0 z-[999] lg:hidden transition-opacity duration-300 ease-in-out ${
open ? "opacity-100" : "opacity-0 pointer-events-none"
}`}
aria-hidden={!open}
>
{/* Backdrop */} {/* Backdrop */}
<div className="fixed inset-0 bg-black/40" onClick={onClose} /> <div
className="fixed inset-0 bg-black/60 backdrop-blur-sm"
onClick={onClose}
/>
{/* Panel */} {/* Panel */}
<div className="fixed inset-y-0 right-0 z-50 w-full max-w-sm bg-[#306D29] shadow-xl"> <div
<div className="flex items-center justify-between px-6 h-20 border-b border-white/10"> className={`fixed inset-y-0 right-0 z-[999] flex w-full max-w-sm flex-col bg-[#306D29] shadow-2xl transition-transform duration-300 ease-in-out ${
open ? "translate-x-0" : "translate-x-full"
}`}
>
{/* Header */}
<div className="flex items-center justify-between px-6 h-20 border-b border-white/10 shrink-0">
<div className="flex items-center gap-3 font-semibold text-white"> <div className="flex items-center gap-3 font-semibold text-white">
<div className="flex h-10 w-10 items-center justify-center rounded-2xl bg-[#48A111]"> <div className="flex h-10 w-10 items-center justify-center rounded-2xl bg-[#48A111]">
<Leaf className="h-5 w-5" /> <Link to="/dashboard" onClick={onClose}>
<Leaf className="h-5 w-5" />
</Link>
</div>
<div className="leading-tight">
<div className="text-lg font-bold">ZeaVis Edu</div>
<div className="text-[13px] font-normal text-[#9AD872]">
Smart AI for Corn Disease Detection
</div>
</div> </div>
<span className="text-lg font-bold">ZeaVis Edu</span>
</div> </div>
<button <button
onClick={onClose} onClick={onClose}
@@ -43,9 +63,11 @@ export function MobileNav({ open, onClose, userRole }: Props) {
</button> </button>
</div> </div>
<nav className="flex flex-col gap-2 px-4 pt-6"> {/* Nav items */}
<nav className="flex flex-col gap-2 overflow-y-auto px-4 py-6 flex-1">
{NAV_ITEMS.map((item) => { {NAV_ITEMS.map((item) => {
const active = isActive(item.path); const active =
isActive(item.path) || (item.altPath && isActive(item.altPath));
return ( return (
<Link <Link
key={item.path} key={item.path}
@@ -61,22 +83,12 @@ export function MobileNav({ open, onClose, userRole }: Props) {
</Link> </Link>
); );
})} })}
{userRole === "expert" && (
<Link
to="/expert/reviews"
onClick={onClose}
className={`rounded-full px-4 py-3 text-[16px] font-medium transition-colors ${
isActive("/expert/reviews")
? "bg-[#48A111] text-white shadow-sm"
: "text-white/85 hover:bg-white/10 hover:text-white"
}`}
>
Review
</Link>
)}
</nav> </nav>
</div> </div>
</div> </div>
); );
return typeof document !== "undefined"
? createPortal(navContent, document.body)
: navContent;
} }
+22 -31
View File
@@ -1,19 +1,12 @@
import { useState } from "react"; import { useState } from "react";
import { Link, useLocation, useNavigate } from "react-router-dom"; import { Link, useLocation } from "react-router-dom";
import { Leaf, Menu } from "lucide-react"; import { Leaf, Menu } from "lucide-react";
import { MobileNav } from "./mobile-nav"; import { MobileNav } from "./mobile-nav";
import { Button } from "@/components/ui/button";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { useAuthStore } from "@/store/auth-store";
import { apiClient } from "@/lib/api-client";
export function Navbar() { export function Navbar() {
const [mobileMenuOpen, setMobileMenuOpen] = useState(false); const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
const location = useLocation(); const location = useLocation();
const navigate = useNavigate(); //
const queryClient = useQueryClient();
const setUser = useAuthStore((state) => state.setUser);
const user = useAuthStore((state) => state.user);
const isActive = (path: string) => location.pathname === path; const isActive = (path: string) => location.pathname === path;
@@ -25,17 +18,6 @@ export function Navbar() {
: "text-white/85 hover:bg-white/10 hover:text-white", : "text-white/85 hover:bg-white/10 hover:text-white",
].join(" "); ].join(" ");
const logoutMutation = useMutation({
mutationFn: async () => {
await apiClient.logout();
},
onSuccess: () => {
setUser(null);
queryClient.clear();
navigate("/login");
},
});
return ( return (
<header className="sticky top-0 z-40 bg-[#306D29] text-white shadow-sm backdrop-blur"> <header className="sticky top-0 z-40 bg-[#306D29] text-white shadow-sm backdrop-blur">
<div className="mx-auto flex h-20 max-w-6xl items-center justify-between px-6"> <div className="mx-auto flex h-20 max-w-6xl items-center justify-between px-6">
@@ -54,26 +36,35 @@ export function Navbar() {
</div> </div>
<nav className="hidden lg:flex items-center gap-2"> <nav className="hidden lg:flex items-center gap-2">
<Link to="/dashboard" className={navLinkClassName(isActive("/dashboard"))}> <Link
to="/dashboard"
className={navLinkClassName(isActive("/dashboard"))}
>
Dashboard Dashboard
</Link> </Link>
<Link to="/scan" className={navLinkClassName(isActive("/scan"))}> <Link to="/scan" className={navLinkClassName(isActive("/scan"))}>
Scan Tanaman Scan Tanaman
</Link> </Link>
<Link to="/diagnoses" className={navLinkClassName(isActive("/diagnoses"))}> <Link
to="/diagnoses"
className={navLinkClassName(isActive("/diagnoses"))}
>
Diagnosa Diagnosa
</Link> </Link>
<Link to="/catalog" className={navLinkClassName(isActive("/catalog") || isActive("/library"))}> <Link
to="/catalog"
className={navLinkClassName(
isActive("/catalog") || isActive("/library"),
)}
>
Pustaka Pustaka
</Link> </Link>
{user?.role === "expert" && ( <Link
<Link to="/expert/reviews"
to="/expert/reviews" className={navLinkClassName(isActive("/expert/reviews"))}
className={navLinkClassName(isActive("/expert/reviews"))} >
> Review
Review </Link>
</Link>
)}
</nav> </nav>
<button <button
+1 -1
View File
@@ -103,7 +103,7 @@ export function ScanPage() {
{!previewUrl ? ( {!previewUrl ? (
<div className="space-y-3"> <div className="space-y-3">
<div <div
className="w-full border-2 border-dashed border-green-300 rounded-md p-12 h-60 text-center cursor-pointer" className="w-full border-2 border-dashed border-green-300 rounded-md p-10 h-60 text-center cursor-pointer"
onClick={() => inputRef.current?.click()} onClick={() => inputRef.current?.click()}
> >
<Upload className="mx-auto text-green-500 mb-3" size={48} /> <Upload className="mx-auto text-green-500 mb-3" size={48} />