diff --git a/messages/en.json b/messages/en.json
index dfbff26..9e5f377 100644
--- a/messages/en.json
+++ b/messages/en.json
@@ -65,7 +65,8 @@
"categories": "Categories",
"tableOfContents": "Table of Contents",
"all": "All",
- "noPostsFound": "No posts found"
+ "noPostsFound": "No posts found",
+ "allPosts": "All Posts"
},
"mail": {
"common": {
diff --git a/messages/zh.json b/messages/zh.json
index 8fb61c1..6d81209 100644
--- a/messages/zh.json
+++ b/messages/zh.json
@@ -65,7 +65,8 @@
"categories": "分类",
"tableOfContents": "目录",
"all": "全部",
- "noPostsFound": "没有找到文章"
+ "noPostsFound": "没有找到文章",
+ "allPosts": "全部文章"
},
"mail": {
"common": {
diff --git a/public/images/avatars/haitang.png b/public/images/avatars/haitang.png
index 3a0eafb..7fabd8c 100644
Binary files a/public/images/avatars/haitang.png and b/public/images/avatars/haitang.png differ
diff --git a/src/app/[locale]/(marketing)/blog/[...slug]/page.tsx b/src/app/[locale]/(marketing)/blog/[...slug]/page.tsx
index ebd5804..782558c 100644
--- a/src/app/[locale]/(marketing)/blog/[...slug]/page.tsx
+++ b/src/app/[locale]/(marketing)/blog/[...slug]/page.tsx
@@ -1,10 +1,10 @@
import AllPostsButton from '@/components/blog/all-posts-button';
import { BlogToc } from '@/components/blog/blog-toc';
-import { Mdx } from '@/components/marketing/blog/mdx-component';
+import { Mdx } from '@/components/shared/mdx-component';
import { LocaleLink } from '@/i18n/navigation';
import { getTableOfContents } from '@/lib/toc';
import { getBaseUrl } from '@/lib/urls/get-base-url';
-import { getLocaleDate } from '@/lib/utils';
+import { estimateReadingTime, getLocaleDate } from '@/lib/utils';
import type { NextPageProps } from '@/types/next-page-props';
import { allPosts } from 'content-collections';
import type { Metadata } from 'next';
@@ -113,6 +113,12 @@ export default async function BlogPostPage(props: NextPageProps) {
)}
+ {/* blog post date */}
+
+
{date}
+
{estimateReadingTime(post.body.raw)}
+
+
{/* blog post title */}
{post.title}
@@ -137,7 +143,7 @@ export default async function BlogPostPage(props: NextPageProps) {
{t("publisher")}
-
+
{post.author?.avatar && (
)}
-
-
{post.author?.name}
-
-
{date}
-
+
{post.author?.name}
diff --git a/src/app/[locale]/(marketing)/layout.tsx b/src/app/[locale]/(marketing)/layout.tsx
index 4e11e57..c5e7a87 100644
--- a/src/app/[locale]/(marketing)/layout.tsx
+++ b/src/app/[locale]/(marketing)/layout.tsx
@@ -1,6 +1,6 @@
import { marketingConfig } from "@/config/marketing";
import { Footer } from "@/components/layout/footer";
-import { Navbar } from "@/components/marketing/navbar";
+import { Navbar } from "@/components/layout/navbar";
export default function MarketingLayout({ children }: { children: React.ReactNode }) {
return (
diff --git a/src/app/[locale]/layout.tsx b/src/app/[locale]/layout.tsx
index 96eab56..a080bdc 100644
--- a/src/app/[locale]/layout.tsx
+++ b/src/app/[locale]/layout.tsx
@@ -1,8 +1,5 @@
import { fontSourceSans, fontSourceSerif4 } from "@/assets/fonts";
-import { Footer } from '@/components/layout/footer';
-import { Navbar } from '@/components/marketing/navbar';
import { TailwindIndicator } from '@/components/tailwind-indicator';
-import { marketingConfig } from '@/config/marketing';
import { routing } from '@/i18n/routing';
import { cn } from "@/lib/utils";
import { GeistMono } from "geist/font/mono";
diff --git a/src/components/blog/all-posts-button.tsx b/src/components/blog/all-posts-button.tsx
index 5565247..1845b10 100644
--- a/src/components/blog/all-posts-button.tsx
+++ b/src/components/blog/all-posts-button.tsx
@@ -1,10 +1,12 @@
"use client";
import { Button } from "@/components/ui/button";
-import { ArrowLeftIcon } from "lucide-react";
import { LocaleLink } from "@/i18n/navigation";
+import { ArrowLeftIcon } from "lucide-react";
+import { useTranslations } from "next-intl";
export default function AllPostsButton() {
+ const t = useTranslations("BlogPage");
return (
);
diff --git a/src/components/blog/blog-category-list-mobile.tsx b/src/components/blog/blog-category-list-mobile.tsx
index 2bdbec1..5da9aa8 100644
--- a/src/components/blog/blog-category-list-mobile.tsx
+++ b/src/components/blog/blog-category-list-mobile.tsx
@@ -1,12 +1,19 @@
"use client";
+import FilterItemMobile from "@/components/shared/filter-item-mobile";
+import {
+ Drawer,
+ DrawerContent,
+ DrawerOverlay,
+ DrawerPortal,
+ DrawerTitle,
+ DrawerTrigger,
+} from "@/components/ui/drawer";
import { Category } from "content-collections";
import { LayoutListIcon } from "lucide-react";
+import { useTranslations } from "next-intl";
import { useParams } from "next/navigation";
import { useState } from "react";
-import { Drawer } from "vaul";
-import FilterItemMobile from "@/components/shared/filter-item-mobile";
-import { useTranslations } from "next-intl";
export type BlogCategoryListMobileProps = {
categoryList: Category[];
@@ -27,8 +34,8 @@ export function BlogCategoryListMobile({
};
return (
-
-
+ setOpen(true)}
className="flex items-center w-full p-3 border-y text-foreground/90"
>
@@ -41,14 +48,11 @@ export function BlogCategoryListMobile({
{selectedCategory?.name ? `${selectedCategory?.name}` : t("all")}
-
-
-
-
- {t("categories")}
+
+
+
+
+ {t("categories")}
@@ -71,9 +75,8 @@ export function BlogCategoryListMobile({
/>
))}
-
-
-
-
+
+
+
);
}
diff --git a/src/components/blog/blog-post.tsx b/src/components/blog/blog-post.tsx
deleted file mode 100644
index e4f535a..0000000
--- a/src/components/blog/blog-post.tsx
+++ /dev/null
@@ -1,78 +0,0 @@
-import * as React from 'react';
-import Link from 'next/link';
-import { format } from 'date-fns';
-import { Mdx } from '@/components/marketing/blog/mdx-component';
-import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
-import { Separator } from '@/components/ui/separator';
-import { getInitials } from '@/lib/utils';
-import { Post } from 'content-collections';
-
-type BlogPostProps = {
- post: Post;
-};
-
-export function BlogPost({ post }: BlogPostProps): React.JSX.Element {
- console.log(post);
- console.log(post.author);
- return (
-
-
-
-
-
- ←
-
-
All posts
-
-
-
-
- {post.categories.map((c) => c?.name).join(', ')}
-
-
-
-
-
-
- {post.title}
-
-
{post.description}
-
-
-
-
-
- {getInitials(post.author?.name ?? '')}
-
-
-
{post.author?.name ?? ''}
-
-
{estimateReadingTime(post.body.raw)}
-
-
-
-
-
-
-
-
-
- );
-}
-
-function estimateReadingTime(
- text: string,
- wordsPerMinute: number = 250
-): string {
- const words = text.trim().split(/\s+/).length;
- const minutes = Math.ceil(words / wordsPerMinute);
- return minutes === 1 ? '1 minute read' : `${minutes} minutes read`;
-}
diff --git a/src/components/blog/blog-posts.tsx b/src/components/blog/blog-posts.tsx
deleted file mode 100644
index c714b5c..0000000
--- a/src/components/blog/blog-posts.tsx
+++ /dev/null
@@ -1,78 +0,0 @@
-import * as React from 'react';
-import Link from 'next/link';
-import { allPosts } from 'content-collections';
-import { format, isBefore } from 'date-fns';
-import { ArrowRightIcon } from 'lucide-react';
-import { GridSection } from '@/components/marketing/fragments/grid-section';
-import { SiteHeading } from '@/components/marketing/fragments/site-heading';
-import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
-import { FillRemainingSpace } from '@/components/fill-remaining-space';
-import { getBaseUrl } from '@/lib/urls/get-base-url';
-import { getInitials } from '@/lib/utils';
-import { useLocale } from 'next-intl';
-
-export function BlogPosts(): React.JSX.Element {
- const locale = useLocale();
-
- // Filter posts by current locale
- const localizedPosts = allPosts
- .filter(post => post.published && post.locale === locale)
- .slice()
- .sort((a, b) => (isBefore(a.date, b.date) ? 1 : -1));
-
- return (
-
-
-
-
- {localizedPosts.map((post, index) => (
-
-
- {post.categories.map((c) => c?.name).join(', ')}
-
-
-
- {post.title}
-
-
- {post.description}
-
-
-
-
-
-
-
- {getInitials(post.author?.name ?? '')}
-
-
-
{post.author?.name ?? ''}
-
-
-
-
- ))}
-
-
-
- );
-}
diff --git a/src/components/marketing/navbar-mobile.tsx b/src/components/layout/navbar-mobile.tsx
similarity index 72%
rename from src/components/marketing/navbar-mobile.tsx
rename to src/components/layout/navbar-mobile.tsx
index 3be8fd2..88a9c1f 100644
--- a/src/components/marketing/navbar-mobile.tsx
+++ b/src/components/layout/navbar-mobile.tsx
@@ -1,7 +1,7 @@
'use client';
import { Logo } from '@/components/logo';
-import { DOCS_LINKS, MENU_LINKS } from '@/components/marketing/marketing-links';
+import { MENU_LINKS } from '@/config/marketing-links';
import { Button, buttonVariants } from '@/components/ui/button';
import {
Collapsible,
@@ -27,7 +27,6 @@ export function NavbarMobile({
}: React.HTMLAttributes) {
const [open, setOpen] = React.useState(false);
const pathname = usePathname();
- const isDocs = pathname.startsWith('/docs');
React.useEffect(() => {
const handleRouteChangeStart = () => {
@@ -90,11 +89,7 @@ export function NavbarMobile({
{open && (
- {isDocs ? (
-
- ) : (
-
- )}
+
)}
@@ -238,77 +233,3 @@ function MainMobileMenu({ onLinkClicked }: MainMobileMenuProps) {
);
}
-
-interface DocsMobileMenuProps {
- onLinkClicked: () => void;
-};
-
-function DocsMobileMenu({
- onLinkClicked
-}: DocsMobileMenuProps): React.JSX.Element {
- const [expanded, setExpanded] = React.useState>({});
- return (
-
-
-
- {DOCS_LINKS.map((item) => (
- -
-
- setExpanded((prev) => ({
- ...prev,
- [item.title.toLowerCase()]: isOpen
- }))
- }
- >
-
-
-
-
-
- {item.items.map((subItem) => (
- -
-
- {subItem.title}
-
-
- ))}
-
-
-
-
- ))}
-
-
-
-
-
-
-
- );
-}
diff --git a/src/components/layout/navbar.tsx b/src/components/layout/navbar.tsx
index cd3e823..3c7dcc4 100644
--- a/src/components/layout/navbar.tsx
+++ b/src/components/layout/navbar.tsx
@@ -1,11 +1,13 @@
-"use client";
+'use client';
-import { LoginWrapper } from "@/components/auth/login-button";
-import Container from "@/components/container";
-import { Icons } from "@/components/icons/icons";
-import { UserButton } from "@/components/layout/user-button";
-import { Logo } from "@/components/logo";
-import { Button } from "@/components/ui/button";
+import { LoginWrapper } from '@/components/auth/login-button';
+import Container from '@/components/container';
+import { ThemeSwitcher } from '@/components/layout/theme-switcher';
+import { UserButton } from '@/components/layout/user-button';
+import { Logo } from '@/components/logo';
+import { MENU_LINKS } from '@/config/marketing-links';
+import { NavbarMobile } from '@/components/layout/navbar-mobile';
+import { Button } from '@/components/ui/button';
import {
NavigationMenu,
NavigationMenuContent,
@@ -13,274 +15,180 @@ import {
NavigationMenuLink,
NavigationMenuList,
NavigationMenuTrigger,
- navigationMenuTriggerStyle,
-} from "@/components/ui/navigation-menu";
-import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetTrigger } from "@/components/ui/sheet";
-import { siteConfig } from "@/config/site";
+ navigationMenuTriggerStyle
+} from '@/components/ui/navigation-menu';
+import { siteConfig } from '@/config/site';
import { useScroll } from "@/hooks/use-scroll";
-import { authClient } from "@/lib/auth-client";
-import { cn } from "@/lib/utils";
-import type { DashboardConfig, MarketingConfig, NestedNavItem } from "@/types";
-import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "@radix-ui/react-accordion";
-import { MenuIcon } from "lucide-react";
-import Link from "next/link";
-import { usePathname } from "next/navigation";
-import { useEffect, useState } from "react";
+import { authClient } from '@/lib/auth-client';
+import { cn } from '@/lib/utils';
+import { Routes } from '@/routes';
+import { MarketingConfig } from '@/types';
+import Link from 'next/link';
+import { usePathname } from 'next/navigation';
+import { ArrowUpRightIcon } from 'lucide-react';
+import LocaleSelector from '@/components/layout/locale-selector';
+import { LocaleLink } from '@/i18n/navigation';
interface NavBarProps {
scroll?: boolean;
- config: DashboardConfig | MarketingConfig;
+ config: MarketingConfig;
}
-export function Navbar({ scroll = false, config }: NavBarProps) {
+const customNavigationMenuTriggerStyle = cn(
+ navigationMenuTriggerStyle(),
+ "bg-transparent hover:bg-transparent hover:text-primary focus:bg-transparent focus:text-primary data-[active]:bg-transparent data-[active]:text-primary data-[state=open]:bg-transparent data-[state=open]:text-primary relative data-[active]:font-bold dark:text-gray-400 dark:hover:text-gray-300 dark:data-[active]:text-white"
+);
+
+export function Navbar({ scroll, config }: NavBarProps) {
const scrolled = useScroll(50);
const { data: session, error } = authClient.useSession();
const user = session?.user;
- console.log(`Navbar, user:`, user);
+ // console.log(`Navbar, user:`, user);
const pathname = usePathname();
- // console.log(`Navbar, pathname: ${pathname}`);
- const menus = config.menus;
-
- const isMenuActive = (href: string) => {
- if (href === "/") {
- return pathname === "/";
- }
- // console.log(`Navbar, href: ${href}, pathname: ${pathname}`);
- return pathname.startsWith(href);
- };
-
- const [open, setOpen] = useState(false);
- // prevent body scroll when modal is open
- useEffect(() => {
- if (open) {
- document.body.style.overflow = "hidden";
- } else {
- document.body.style.overflow = "auto";
- }
- }, [open]);
return (
-