Commit e7743761 authored by Lê Đức Huy's avatar Lê Đức Huy

feat: implement admin sidebar, layout shell, and login page components

parent 91a2bc40
......@@ -109,6 +109,7 @@ function Footer() {
const contactInfo = {
name:
primaryBranch?.branch_name ??
siteInformation?.website_name ??
"LIÊN ĐOÀN THƯƠNG MẠI & CÔNG NGHIỆP VIỆT NAM - CHI NHÁNH KHU VỰC THÀNH PHỐ HỒ CHÍ MINH",
address:
......@@ -285,7 +286,7 @@ function Footer() {
{extraBranches.length > 0 ? (
<div className="pt-4">
<p className="text-[14px] font-semibold uppercase text-[#dce7ff]">
Chi nhánh khác
Các chi nhánh khác
</p>
<div className="mt-3 space-y-3 text-[14px] text-[#c7d8ff]">
{extraBranches.map((branch) => (
......@@ -295,32 +296,6 @@ function Footer() {
{branch.branch_name}
</p>
) : null}
{branch.address ? (
<div className="flex items-start gap-2">
<MapPin className="mt-0.5 h-3.5 w-3.5 shrink-0 text-[#f7b500]" />
<span>{branch.address}</span>
</div>
) : null}
{branch.telephone || branch.hotline ? (
<div className="flex items-center gap-2">
<Phone className="h-3.5 w-3.5 shrink-0 text-[#f7b500]" />
<span>{branch.telephone || branch.hotline}</span>
</div>
) : null}
{branch.fax ? (
<div className="flex items-center gap-2">
<Printer className="h-3.5 w-3.5 shrink-0 text-[#f7b500]" />
<span>{branch.fax}</span>
</div>
) : null}
{branch.email ? (
<div className="flex items-center gap-2">
<Mail className="h-3.5 w-3.5 shrink-0 text-[#f7b500]" />
<a href={`mailto:${branch.email}`}>
{branch.email}
</a>
</div>
) : null}
</div>
))}
</div>
......
......@@ -7,8 +7,9 @@ import { useQuery } from "@tanstack/react-query";
import Image from "next/image";
import Link from "next/link";
import logo from "@/assets/VCCI-HCM-logo-VN-2025.png";
import { getLogo } from "@/api/endpoints/logo";
import { useGetLogo } from "@/api/endpoints/logo";
import { getSiteInformation } from "@/api/endpoints/site-information";
import { resolveUploadUrl } from "@/links";
import type { Logo } from "@/api/models/logo";
import type {
SiteInformationData,
......@@ -176,20 +177,23 @@ function Header() {
staleTime: 5 * 60 * 1000,
});
const { data: currentLogo = null } = useQuery({
queryKey: ["header-logo"],
queryFn: () =>
getLogo({
const { data: currentLogo = null } = useGetLogo(
{
page: 1,
pageSize: 1,
sortField: "updated_at",
sortOrder: "desc",
}).catch(() => null),
select: (response) =>
(response as LogoListEnvelope | null)?.data?.responseData?.rows?.[0] ??
null,
},
{
query: {
select: (response: any) => {
const responseData = response?.responseData ?? response?.data?.responseData;
return (responseData?.rows?.[0] as Logo | undefined) ?? null;
},
staleTime: 5 * 60 * 1000,
});
},
}
);
const { data: siteInformationResponse } =
useQuery<ApiEnvelope<SiteInformationData> | null>({
......@@ -252,8 +256,7 @@ function Header() {
return (
<header className="sticky top-0 z-50 shadow-[0_1px_0_rgba(15,23,42,0.05)]">
<div
className={`hidden w-full items-center justify-center overflow-hidden bg-[#25439a] ${
isTopBarHidden ? "lg:hidden" : "h-10 lg:flex"
className={`hidden w-full items-center justify-center overflow-hidden bg-[#25439a] ${isTopBarHidden ? "lg:hidden" : "h-10 lg:flex"
}`}
>
<div className="mx-auto flex h-full w-full max-w-[1460px] items-center justify-between gap-6 px-6 xl:px-8">
......@@ -321,8 +324,8 @@ function Header() {
<Image
width={108}
height={40}
className="h-auto w-[108px] object-contain"
src={currentLogo?.logo_url || logo}
className="h-auto max-h-10 w-[108px] object-contain"
src={currentLogo?.logo_url ? resolveUploadUrl(currentLogo.logo_url) : logo}
alt={currentLogo?.logo_name || "VCCI-HCM"}
priority
/>
......@@ -360,8 +363,7 @@ function Header() {
</div>
<div
className={`fixed inset-0 z-60 bg-white transition-all duration-300 lg:hidden ${
toggleMenu
className={`fixed inset-0 z-60 bg-white transition-all duration-300 lg:hidden ${toggleMenu
? "pointer-events-auto translate-y-0 opacity-100"
: "pointer-events-none -translate-y-2 opacity-0"
}`}
......@@ -376,8 +378,8 @@ function Header() {
<Image
width={108}
height={40}
className="h-auto w-[108px] object-contain"
src={currentLogo?.logo_url || logo}
className="h-auto max-h-10 w-[108px] object-contain"
src={currentLogo?.logo_url ? resolveUploadUrl(currentLogo.logo_url) : logo}
alt={currentLogo?.logo_name || "VCCI-HCM"}
priority
/>
......
......@@ -15,14 +15,14 @@ import {
} from "lucide-react";
import { toast } from "sonner";
import { useQuery } from "@tanstack/react-query";
import { getLogo } from "@/api/endpoints/logo";
import { useGetLogo } from "@/api/endpoints/logo";
import type { Logo } from "@/api/models/logo";
import { Button } from "@/components/ui/button";
import { Checkbox } from "@/components/ui/checkbox";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import logo from "@/assets/VCCI-HCM-logo-VN-2025.png";
import links from "@/links";
import links, { resolveUploadUrl } from "@/links";
import { loginAdmin } from "@/lib/auth/admin-auth";
import useAuthStore from "@/store/useAuthStore";
......@@ -131,12 +131,22 @@ function AuthShell({
mode: AuthMode;
children: React.ReactNode;
}) {
const { data: logoData } = useQuery({
queryKey: ["logo", { page: 1, pageSize: 1, sortOrder: "desc" }],
queryFn: () => getLogo({ page: 1, pageSize: 1, sortOrder: "desc" }),
select: (response) =>
(response as LogoListEnvelope)?.data?.responseData?.rows?.[0],
});
const { data: logoData } = useGetLogo(
{
page: 1,
pageSize: 1,
sortField: "updated_at",
sortOrder: "desc",
},
{
query: {
select: (response: any) => {
const responseData = response?.responseData ?? response?.data?.responseData;
return (responseData?.rows?.[0] as Logo | undefined) ?? null;
},
},
}
);
const title =
mode === "login"
......@@ -163,7 +173,7 @@ function AuthShell({
<div className="flex items-center gap-4">
<div className="flex h-16 w-16 items-center justify-center rounded-2xl border border-[#063e8e]/10 bg-white shadow-sm">
<Image
src={logoData?.logo_url || logo}
src={logoData?.logo_url ? resolveUploadUrl(logoData.logo_url) : logo}
alt={logoData?.logo_name || "VCCI HCM"}
width={48}
height={48}
......@@ -217,7 +227,7 @@ function AuthShell({
<div className="mb-8 flex items-center gap-3 lg:hidden">
<div className="flex h-12 w-12 items-center justify-center rounded-2xl border border-[#063e8e]/10 bg-[#f8fbff]">
<Image
src={logoData?.logo_url || logo}
src={logoData?.logo_url ? resolveUploadUrl(logoData.logo_url) : logo}
alt={logoData?.logo_name || "VCCI HCM"}
width={36}
height={36}
......
......@@ -17,9 +17,10 @@ import {
Video,
} from "lucide-react";
import { useQuery } from "@tanstack/react-query";
import { getLogo } from "@/api/endpoints/logo";
import { useGetLogo } from "@/api/endpoints/logo";
import type { Logo } from "@/api/models/logo";
import logo from "@/assets/VCCI-HCM-logo-VN-2025.png";
import { resolveUploadUrl } from "@/links";
type LogoListEnvelope = {
data?: {
......@@ -89,12 +90,22 @@ export function AdminSidebar() {
Record<string, boolean>
>({});
const { data: logoData } = useQuery({
queryKey: ["logo", { page: 1, pageSize: 1, sortOrder: "desc" }],
queryFn: () => getLogo({ page: 1, pageSize: 1, sortOrder: "desc" }),
select: (response) =>
(response as LogoListEnvelope)?.data?.responseData?.rows?.[0],
});
const { data: logoData } = useGetLogo(
{
page: 1,
pageSize: 1,
sortField: "updated_at",
sortOrder: "desc",
},
{
query: {
select: (response: any) => {
const responseData = response?.responseData ?? response?.data?.responseData;
return (responseData?.rows?.[0] as Logo | undefined) ?? null;
},
},
}
);
const isItemActive = React.useCallback(
(href: string) => {
......@@ -146,7 +157,7 @@ export function AdminSidebar() {
>
<div className="flex h-14 w-14 shrink-0 items-center justify-center rounded-2xl border border-[#063e8e]/10 bg-[#f8fbff] shadow-sm">
<Image
src={logoData?.logo_url || logo}
src={logoData?.logo_url ? resolveUploadUrl(logoData.logo_url) : logo}
alt={logoData?.logo_name || "VCCI HCM"}
width={40}
height={40}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment