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:
+8
-10
@@ -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>
|
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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} />
|
||||||
|
|||||||
Reference in New Issue
Block a user