Commit feb3ff57 authored by ThinhNC's avatar ThinhNC

Merge branch 'feat/update-brand-logo' into 'develop'

Feat/update brand logo

See merge request !23
parents 1dfd43b5 2871894d
Pipeline #53123 passed with stage
in 1 minute and 18 seconds
import React from "react";
import Link from "next/link";
import { Bot } from "lucide-react";
import Image from "next/image";
import { ThemeToggle } from "@/components/common/theme-toggle";
import { LanguageToggle } from "@/components/common/language-toggle";
......@@ -19,10 +19,17 @@ export default function AuthLayout({
<header className="flex w-full items-center justify-between">
<Link
href="/"
className="flex items-center gap-2.5 transition-opacity hover:opacity-90"
className="flex items-center gap-2.5 transition-opacity hover:opacity-90 group"
>
<div className="flex h-9 w-9 items-center justify-center rounded-2xl bg-gradient-to-br from-emerald-500 to-teal-700 shadow-md shadow-emerald-500/20 text-white">
<Bot className="h-5 w-5" />
<div className="relative flex h-9 w-9 items-center justify-center rounded-2xl overflow-hidden shadow-md shadow-emerald-500/20 bg-emerald-500/10 border border-emerald-500/20 group-hover:scale-105 transition-transform duration-200">
<Image
src="/logo.png"
alt="DataCrawler Logo"
width={36}
height={36}
className="h-full w-full object-contain p-0.5"
priority
/>
</div>
<span className="font-bold text-base tracking-tight text-foreground">
DataCrawler
......
......@@ -585,11 +585,10 @@ export default function CrawlJobDetailPage({ params }: PageProps) {
id: "pages",
label: t.jobDetail.pagesTab,
icon: FileText,
count: (pagesData?.total && pagesData.total > 0)
? pagesData.total
: isJobRunning && job?.successPages
? job.successPages
: 0,
count:
pagesData?.total !== undefined && pagesData.total > 0
? pagesData.total
: (job?.successPages ?? 0),
},
{ id: "diff", label: t.jobDetail.diffTab, icon: Sparkles, count: diffData?.summary?.changeRate ? `${Math.round(diffData.summary.changeRate * 100)}%` : undefined },
].map((tabItem) => {
......@@ -857,7 +856,7 @@ export default function CrawlJobDetailPage({ params }: PageProps) {
<p className="text-xs text-muted-foreground">{t.jobDetail.pages.subtitle}</p>
</div>
<Badge variant="secondary" className="rounded-full">
{pagesData?.total || 0} {t.jobDetail.pages.pagesExtracted}
{pagesData?.total ?? job?.successPages ?? 0} {t.jobDetail.pages.pagesExtracted}
</Badge>
</div>
......
src/app/favicon.ico

25.3 KB | W: | H:

src/app/favicon.ico

574 KB | W: | H:

src/app/favicon.ico
src/app/favicon.ico
src/app/favicon.ico
src/app/favicon.ico
  • 2-up
  • Swipe
  • Onion skin
......@@ -21,6 +21,13 @@ export const metadata: Metadata = {
},
description:
"Hệ thống quản lý và giám sát tác vụ cào dữ liệu web realtime, tích hợp Next.js, TanStack Query, React Hook Form và Tailwind CSS.",
icons: {
icon: [
{ url: "/logo.png", type: "image/png" },
],
shortcut: "/logo.png",
apple: "/logo.png",
},
};
export default function RootLayout({
......
......@@ -2,8 +2,9 @@
import React from "react";
import Link from "next/link";
import Image from "next/image";
import { usePathname } from "next/navigation";
import { Activity, Bot, Calendar, Download, FileCode2, Globe } from "lucide-react";
import { Activity, Calendar, Download, FileCode2, Globe } from "lucide-react";
import { ThemeToggle } from "./theme-toggle";
import { LanguageToggle } from "./language-toggle";
import { UserMenu } from "./user-menu";
......@@ -23,9 +24,16 @@ export function Navbar() {
<header className="sticky top-0 z-40 w-full border-b border-border/70 bg-card/80 backdrop-blur-md transition-colors duration-200">
<div className="flex h-16 w-full items-center justify-between px-4 sm:px-6 lg:px-8">
{/* Brand */}
<Link href="/" className="flex items-center gap-3 transition-opacity hover:opacity-90">
<div className="flex h-10 w-10 items-center justify-center rounded-2xl bg-gradient-to-br from-emerald-500 to-teal-700 shadow-md shadow-emerald-500/20">
<Bot className="h-6 w-6 text-white" />
<Link href="/" className="flex items-center gap-2.5 transition-opacity hover:opacity-90 group">
<div className="relative flex h-10 w-10 items-center justify-center rounded-2xl overflow-hidden shadow-md shadow-emerald-500/20 bg-emerald-500/10 border border-emerald-500/20 group-hover:scale-105 transition-transform duration-200">
<Image
src="/logo.png"
alt="DataCrawler Logo"
width={40}
height={40}
className="h-full w-full object-contain p-1"
priority
/>
</div>
<span className="font-bold text-lg tracking-tight text-foreground">DataCrawler</span>
</Link>
......
......@@ -2,8 +2,9 @@
import React, { useEffect } from "react";
import Link from "next/link";
import Image from "next/image";
import { usePathname } from "next/navigation";
import { Compass, Home, Bot } from "lucide-react";
import { Compass, Home } from "lucide-react";
import { useLanguage } from "@/providers/language-provider";
import { ThemeToggle } from "@/components/common/theme-toggle";
import { LanguageToggle } from "@/components/common/language-toggle";
......@@ -81,10 +82,17 @@ export function NotFoundClient() {
<header className="flex w-full items-center justify-between">
<Link
href="/"
className="flex items-center gap-2.5 transition-opacity hover:opacity-90"
className="flex items-center gap-2.5 transition-opacity hover:opacity-90 group"
>
<div className="flex h-9 w-9 items-center justify-center rounded-2xl bg-gradient-to-br from-emerald-500 to-teal-700 shadow-md shadow-emerald-500/20 text-white">
<Bot className="h-5 w-5" />
<div className="relative flex h-9 w-9 items-center justify-center rounded-2xl overflow-hidden shadow-md shadow-emerald-500/20 bg-emerald-500/10 border border-emerald-500/20 group-hover:scale-105 transition-transform duration-200">
<Image
src="/logo.png"
alt="DataCrawler Logo"
width={36}
height={36}
className="h-full w-full object-contain p-0.5"
priority
/>
</div>
<span className="font-bold text-base tracking-tight text-foreground">
DataCrawler
......
......@@ -90,7 +90,7 @@ export function notifyJobCompletion({
const notification = new Notification(finalTitle, {
body: finalBody,
icon: "/favicon.ico",
icon: "/logo.png",
tag: `crawl-job-completed-${jobId}`,
});
......
......@@ -219,15 +219,39 @@ export const crawlJobService = {
const response = await apiClient.get<
ApiResponse<{
items: CrawlPagePreview[];
total: number;
page: number;
limit: number;
totalPages: number;
total?: number;
page?: number;
limit?: number;
totalPages?: number;
meta?: {
total?: number;
page?: number;
limit?: number;
totalPages?: number;
};
}>
>(`/crawl-jobs/${id}/pages/preview?${params.toString()}`);
if (response.data?.data) {
return response.data.data;
const raw = response.data?.data;
if (raw) {
const total =
raw.total ??
raw.meta?.total ??
(Array.isArray(raw.items) ? raw.items.length : 0);
const totalPages =
raw.totalPages ??
raw.meta?.totalPages ??
Math.max(1, Math.ceil(total / limit));
const pageNum = raw.page ?? raw.meta?.page ?? page;
const limitNum = raw.limit ?? raw.meta?.limit ?? limit;
return {
items: raw.items || [],
total,
page: pageNum,
limit: limitNum,
totalPages,
};
}
throw new Error("Invalid pages preview response");
},
......
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