diff --git a/Dockerfile b/Dockerfile index ff1dd29..335d444 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,7 +11,7 @@ RUN npm install -g pnpm WORKDIR /usr/src/app # Menyalin file penting terlebih dahulu untuk caching -COPY package.json pnpm-lock.yaml ./ +COPY package.json ./ # Menyalin direktori ckeditor5 jika diperlukan COPY vendor/ckeditor5 ./vendor/ckeditor5 diff --git a/app/(admin)/admin/dashboard/page.tsx b/app/(admin)/admin/dashboard/page.tsx index 3be562d..92cce1c 100644 --- a/app/(admin)/admin/dashboard/page.tsx +++ b/app/(admin)/admin/dashboard/page.tsx @@ -1,11 +1,34 @@ +"use client"; + import DashboardContainer from "@/components/main/dashboard/dashboard-container"; +import { motion } from "framer-motion"; +import { useEffect, useState } from "react"; export default function AdminPage() { + const [mounted, setMounted] = useState(false); + + useEffect(() => { + setMounted(true); + }, []); + + if (!mounted) { + return ( +
+
+
+ ); + } + return ( -
-
+ +
-
+ ); } diff --git a/app/globals.css b/app/globals.css index dc98be7..a609acd 100644 --- a/app/globals.css +++ b/app/globals.css @@ -120,3 +120,65 @@ @apply bg-background text-foreground; } } + +/* Custom utility classes */ +@layer utilities { + .line-clamp-1 { + overflow: hidden; + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 1; + } + + .line-clamp-2 { + overflow: hidden; + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + } + + .line-clamp-3 { + overflow: hidden; + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 3; + } + + .text-gradient { + background: linear-gradient(to right, var(--tw-gradient-stops)); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; + } + + .scrollbar-hide { + -ms-overflow-style: none; + scrollbar-width: none; + } + + .scrollbar-hide::-webkit-scrollbar { + display: none; + } + + .scrollbar-thin { + scrollbar-width: thin; + scrollbar-color: rgb(203 213 225) transparent; + } + + .scrollbar-thin::-webkit-scrollbar { + width: 6px; + } + + .scrollbar-thin::-webkit-scrollbar-track { + background: transparent; + } + + .scrollbar-thin::-webkit-scrollbar-thumb { + background-color: rgb(203 213 225); + border-radius: 3px; + } + + .scrollbar-thin::-webkit-scrollbar-thumb:hover { + background-color: rgb(148 163 184); + } +} diff --git a/app/layout.tsx b/app/layout.tsx index 14490a5..63e2178 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,16 +1,16 @@ import type { Metadata } from "next"; -// import { Geist, Geist_Mono } from "next/font/google"; +import { Geist, Geist_Mono } from "next/font/google"; import "./globals.css"; -// const geistSans = Geist({ -// variable: "--font-geist-sans", -// subsets: ["latin"], -// }); +const geistSans = Geist({ + variable: "--font-geist-sans", + subsets: ["latin"], +}); -// const geistMono = Geist_Mono({ -// variable: "--font-geist-mono", -// subsets: ["latin"], -// }); +const geistMono = Geist_Mono({ + variable: "--font-geist-mono", + subsets: ["latin"], +}); export const metadata: Metadata = { title: "Mikul News", @@ -23,10 +23,8 @@ export default function RootLayout({ children: React.ReactNode; }>) { return ( - - + + {children} diff --git a/components/form/login.tsx b/components/form/login.tsx index 5736c86..7eeee15 100644 --- a/components/form/login.tsx +++ b/components/form/login.tsx @@ -55,18 +55,6 @@ export default function Login() { if (!username || !password) { error("Username & Password Wajib Diisi !"); } else { - // let response = await emailValidation(data); - // if (response?.error) { - // error("Username / Password Tidak Sesuai"); - // return false; - // } - - // if (response?.data?.messages[0] === "Continue to setup email") { - // setFirstLogin(true); - // } else { - // setNeedOtp(true); - // } - loading(); const response = await postSignIn(data); if (response?.error) { @@ -93,7 +81,7 @@ export default function Login() { const resActivity = await saveActivity( { activityTypeId: 1, - url: "https://kontenhumas.com/auth", + url: "https://dev.mikulnews.com/auth", userId: profile?.data?.data?.id, }, accessData?.id_token @@ -139,7 +127,6 @@ export default function Login() { close(); } } - // } }; const checkUsername = async () => { @@ -173,90 +160,6 @@ export default function Login() { }); }; - // const submitOtp = async () => { - // loading(); - // const validation = await otpValidationLogin({ - // username: username, - // otpCode: otpValue, - // }); - // if (validation?.error) { - // error("OTP Tidak Sesuai"); - // return false; - // } - - // const response = await postSignIn({ - // username: username, - // password: password, - // }); - - // const resProfile = await getProfile(response?.data?.data?.access_token); - // const profile = resProfile?.data?.data; - - // const dateTime: any = new Date(); - - // const newTime: any = dateTime.getTime() + 10 * 60 * 1000; - - // Cookies.set("access_token", response?.data?.data?.access_token, { - // expires: 1, - // }); - // Cookies.set("refresh_token", response?.data?.data?.refresh_token, { - // expires: 1, - // }); - // Cookies.set("time_refresh", newTime, { - // expires: 1, - // }); - // Cookies.set("is_first_login", "true", { - // secure: true, - // sameSite: "strict", - // }); - // const resActivity = await saveActivity( - // { - // activityTypeId: 1, - // url: "https://kontenhumas.com/auth", - // userId: profile?.data?.data?.id, - // }, - // accessData?.id_token - // ); - // Cookies.set("profile_picture", profile?.profilePictureUrl, { - // expires: 1, - // }); - // Cookies.set("uie", profile?.id, { - // expires: 1, - // }); - // Cookies.set("ufne", profile?.fullname, { - // expires: 1, - // }); - // Cookies.set("ulie", profile?.userLevelGroup, { - // expires: 1, - // }); - // Cookies.set("username", profile?.username, { - // expires: 1, - // }); - // Cookies.set("urie", profile?.roleId, { - // expires: 1, - // }); - // Cookies.set("roleName", profile?.roleName, { - // expires: 1, - // }); - // Cookies.set("masterPoldaId", profile?.masterPoldaId, { - // expires: 1, - // }); - // Cookies.set("ulne", profile?.userLevelId, { - // expires: 1, - // }); - // Cookies.set("urce", profile?.roleCode, { - // expires: 1, - // }); - // Cookies.set("email", profile?.email, { - // expires: 1, - // }); - // router.push("/admin/dashboard"); - // Cookies.set("status", "login", { - // expires: 1, - // }); - // close(); - // }; - const submitCheckEmail = async () => { const req = { oldEmail: oldEmail, @@ -281,190 +184,264 @@ export default function Login() { }; return ( -
-
- - logo - +
+ {/* Left Side - Logo Section */} +
+
+
+
+ +
+ Mikul News Logo +
+ +
+

Portal Mikul News

+

Platform berita terpercaya untuk informasi terkini

+
+
+
+ {/* Decorative elements */} +
+
- {isFirstLogin ? ( -
-

- Setting Account -

- {/*

Email Lama

*/} - {/* */} -
- - setOldEmail(e.target.value)} - /> -
- {/*

Email Baru

*/} - {/* */} -
- - setNewEmail(e.target.value)} - /> -
- -
- ) : needOtp ? ( -
- {/*

Submit OTP

-

OTP

- - -
- - Beranda - -
*/} -
- ) : isResetPassword ? ( -
-

- Reset Password -

- - setCheckUsernameValue(e.target.value.trim())} - onPaste={(e) => setCheckUsernameValue(e.currentTarget.value.trim())} - onCopy={(e) => setCheckUsernameValue(e.currentTarget.value.trim())} - /> - - - -
- ) : ( -
-
-
- Selamat Datang di Portal Mikul News -
-
- Silahkan Login untuk Melihat informasi serta untuk mengetahui - status permintaan informasi dan keberatan yang sudah diajukan. -
- - - setValUsername(e.target.value.trim())} - onPaste={(e) => setValUsername(e.currentTarget.value.trim())} - onCopy={(e) => setValUsername(e.currentTarget.value.trim())} - /> - - -
- setPassword(e.target.value)} + {/* Right Side - Login Form */} +
+
+ {/* Mobile Logo */} +
+ + Mikul News Logo - -
- - - - +
+ + {isFirstLogin ? ( +
+
+
+ + + +
+

Setup Akun

+

Lengkapi informasi email Anda

+
+ +
+
+ + setOldEmail(e.target.value)} + /> +
+ +
+ + setNewEmail(e.target.value)} + /> +
+ + +
+
+ ) : needOtp ? ( +
+
+
+ + + +
+

Verifikasi OTP

+

Masukkan kode OTP yang telah dikirim

+
+
+ ) : isResetPassword ? ( +
+
+
+ + + +
+

Reset Password

+

Masukkan username untuk reset password

+
+ +
+
+ + setCheckUsernameValue(e.target.value.trim())} + onPaste={(e) => setCheckUsernameValue(e.currentTarget.value.trim())} + onCopy={(e) => setCheckUsernameValue(e.currentTarget.value.trim())} + /> +
+ + + +
+ + Beranda + + + +
+
+
+ ) : ( +
+
+
+ + + +
+

Selamat Datang

+

Portal Mikul News - Platform berita terpercaya

+
+ +
+
+ + setValUsername(e.target.value.trim())} + onPaste={(e) => setValUsername(e.currentTarget.value.trim())} + onCopy={(e) => setValUsername(e.currentTarget.value.trim())} + /> +
+ +
+ +
+ setPassword(e.target.value)} + /> + +
+
+ + + +
+ + Beranda + + + +
+
+ + {/*
+
+ + + +
+

Informasi Portal

+

Akses informasi terkini dan status permintaan informasi yang telah diajukan.

+
+
+
*/} +
+ )}
- )} +
); } diff --git a/components/landing-page/option.tsx b/components/landing-page/option.tsx index 42670c5..512232d 100644 --- a/components/landing-page/option.tsx +++ b/components/landing-page/option.tsx @@ -21,35 +21,98 @@ const Option = ({ Icon, title, selected, setSelected, open, notifs, active }: Op onClick={() => setSelected?.(title)} onMouseEnter={() => setHovered(true)} onMouseLeave={() => setHovered(false)} - className={`relative flex h-10 w-full items-center rounded-md transition-colors cursor-pointer ${isActive ? "bg-slate-400 text-black" : "text-black hover:bg-slate-100"}`} + className={`relative flex h-12 w-full px-3 items-center rounded-xl transition-all duration-200 cursor-pointer group ${ + isActive + ? "bg-gradient-to-r from-emerald-500 to-green-500 text-white shadow-lg shadow-emerald-500/25" + : "text-slate-600 hover:bg-gradient-to-r hover:from-slate-100 hover:to-slate-200/50 hover:text-slate-800" + }`} + whileHover={{ scale: 1.02 }} + whileTap={{ scale: 0.98 }} > - - + {/* Active indicator */} + {isActive && ( + + )} + + +
+ +
{open && ( - - {title} - - )} - - {!open && hovered && ( - {title} )} + {/* Tooltip for collapsed state */} + {!open && hovered && ( + +
+ {title} + {/* Tooltip arrow */} +
+
+
+ )} + + {/* Notification badge */} {notifs && open && ( - + {notifs} )} + + {/* Hover effect overlay */} + {hovered && !isActive && ( + + )} ); }; diff --git a/components/landing-page/retracting-sidedar.tsx b/components/landing-page/retracting-sidedar.tsx index dfcbdc1..8353e97 100644 --- a/components/landing-page/retracting-sidedar.tsx +++ b/components/landing-page/retracting-sidedar.tsx @@ -1,6 +1,6 @@ "use client"; -import React, { Dispatch, SetStateAction, useState } from "react"; +import React, { Dispatch, SetStateAction, useState, useEffect } from "react"; import Image from "next/image"; import { Icon } from "@iconify/react"; @@ -8,7 +8,8 @@ import Link from "next/link"; import DashboardContainer from "../main/dashboard/dashboard-container"; import { usePathname } from "next/navigation"; import Option from "./option"; -import { motion } from "framer-motion"; +import { motion, AnimatePresence } from "framer-motion"; +import { useTheme } from "../layout/theme-context"; interface RetractingSidebarProps { sidebarData: boolean; @@ -17,7 +18,7 @@ interface RetractingSidebarProps { const sidebarSections = [ { - title: "DashBoard", + title: "Dashboard", items: [ { title: "Dashboard", @@ -29,15 +30,15 @@ const sidebarSections = [ ], }, { - title: "Apps", + title: "Content Management", items: [ { - title: "Artikel", + title: "Articles", icon: () => , link: "/admin/article", }, { - title: "Kategori", + title: "Categories", icon: () => , link: "/admin/master-category", }, @@ -47,7 +48,7 @@ const sidebarSections = [ // link: "/admin/magazine", // }, { - title: "Advertise", + title: "Advertisements", icon: () => , link: "/admin/advertise", }, @@ -59,15 +60,15 @@ const sidebarSections = [ ], }, { - title: "Master", + title: "System", items: [ { - title: "Master Static Page", + title: "Static Pages", icon: () => , link: "/admin/static-page", }, { - title: "Master User", + title: "User Management", icon: () => , link: "/admin/master-user", }, @@ -80,43 +81,94 @@ export const RetractingSidebar = ({ updateSidebarData, }: RetractingSidebarProps) => { const pathname = usePathname(); + const [mounted, setMounted] = useState(false); + + useEffect(() => { + setMounted(true); + }, []); + + if (!mounted) { + return null; + } return ( <> {/* DESKTOP SIDEBAR */} - - - - - {/* MOBILE SIDEBAR */} - {sidebarData && ( - + - {/* */} - - )} + + + + {/* Desktop Toggle Button - appears when sidebar is collapsed */} + + {!sidebarData && ( + updateSidebarData(true)} + > + + + )} + + + {/* Mobile Toggle Button */} + + {!sidebarData && ( + updateSidebarData(true)} + > + + + )} + + + {/* MOBILE SIDEBAR */} + + {sidebarData && ( + + {/* */} + + + )} + ); }; @@ -130,130 +182,212 @@ const SidebarContent = ({ pathname: string; updateSidebarData: (newData: boolean) => void; }) => { + const { theme, toggleTheme } = useTheme(); return ( - <> - {/* BAGIAN ATAS */} -
- {!open && ( -
- -
- )} - -
- - - - {/* {open && ( - - )} */} - {open && ( - - )} -
- -
- {sidebarSections.map((section) => ( -
-

{section.title}

- {section.items.map((item) => ( - -
- ))} -
-
- - {/* BAGIAN BAWAH */} -
-