From 286716411694b219b09e6240ce525e5357c28636 Mon Sep 17 00:00:00 2001 From: tabarzin Date: Tue, 2 Jun 2026 23:28:40 +0300 Subject: [PATCH 1/8] add linter --- CLAUDE.md | 18 +- README.md | 55 +++-- app/api/og/route.tsx | 74 ++++--- app/api/revalidate/route.ts | 22 +- app/layout.tsx | 60 ++---- app/not-found.tsx | 4 +- app/page.tsx | 6 +- app/pages/[slug]/page.tsx | 16 +- app/pages/page.tsx | 2 +- app/posts/[slug]/page.tsx | 15 +- app/posts/categories/page.tsx | 4 +- app/posts/loading.tsx | 3 +- app/posts/page.tsx | 18 +- app/sitemap.ts | 2 +- components.json | 2 +- components/carousel/news.tsx | 15 +- components/craft.tsx | 16 +- components/nav/mobile-nav.tsx | 42 ++-- components/posts/filter.tsx | 12 +- components/posts/post-card.tsx | 8 +- components/theme/theme-toggle.tsx | 7 +- components/ui/badge.tsx | 22 +- components/ui/button.tsx | 44 ++-- components/ui/card.tsx | 20 +- components/ui/carousel.tsx | 334 ++++++++++++++---------------- components/ui/dropdown-menu.tsx | 99 ++++----- components/ui/form.tsx | 121 +++++------ components/ui/input.tsx | 16 +- components/ui/label.tsx | 29 ++- components/ui/navigation-menu.tsx | 62 +++--- components/ui/pagination.tsx | 81 +++----- components/ui/scroll-area.tsx | 26 ++- components/ui/select.tsx | 21 +- components/ui/separator.tsx | 43 ++-- components/ui/sheet.tsx | 84 +++----- compose-dev.yml | 2 +- compose.yml | 2 +- lib/utils.ts | 21 +- lib/wordpress.ts | 77 ++----- menu.config.ts | 3 +- next.config.ts | 4 +- package.json | 5 +- pnpm-lock.yaml | 254 +++++++++++++++++++++++ pnpm-workspace.yaml | 3 + tsconfig.json | 14 +- 45 files changed, 887 insertions(+), 901 deletions(-) create mode 100644 pnpm-workspace.yaml diff --git a/CLAUDE.md b/CLAUDE.md index c84efc52..5c70842d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -3,9 +3,10 @@ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Build Commands + - `npm run dev` - Start development server with turbo mode - `npm run build` - Build for production -- `npm run start` - Start production server +- `npm run start` - Start production server - `npm run lint` - Run ESLint to check code quality ## Architecture Overview @@ -13,23 +14,27 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co This is a headless WordPress starter using Next.js 15 App Router with TypeScript. Key architectural patterns: ### Data Layer + - All WordPress API interactions go through `lib/wordpress.ts` - Type definitions in `lib/wordpress.d.ts` define Post, Page, Category, Tag, Author, Media interfaces - Error handling uses custom `WordPressAPIError` class - Functions use Next.js cache tags for granular revalidation (e.g., `tags: ['posts', `post-${slug}`]`) ### Routing Structure + - Dynamic routes: `/posts/[slug]`, `/pages/[slug]` - Archive pages: `/posts`, `/posts/authors`, `/posts/categories`, `/posts/tags` - API routes: `/api/revalidate` (webhook), `/api/og` (OG images) ### Component Patterns + - Server Components for data fetching with parallel `Promise.all()` calls - URL-based state management for search and filters - Debounced search (300ms) with `useSearchParams` - Pagination with 9 posts per page default ### Revalidation System + - WordPress plugin sends webhooks on content changes - Next.js endpoint validates webhook secret and calls `revalidateTag()` - Default cache duration: 1 hour (`revalidate: 3600`) @@ -37,34 +42,41 @@ This is a headless WordPress starter using Next.js 15 App Router with TypeScript ## Code Style ### TypeScript + - Use strict typing with interfaces defined in `lib/wordpress.d.ts` - Prefer type annotations over type assertions - Use type inference when the type is obvious ### Naming Conventions + - React components: PascalCase (e.g., `PostCard.tsx`) - Functions and variables: camelCase - Types and interfaces: PascalCase - Constants: UPPERCASE_SNAKE_CASE for true constants ### File Structure + - Page components: `/app/**/*.tsx` -- Reusable UI components: `/components/**/*.tsx` +- Reusable UI components: `/components/**/*.tsx` - API and utility functions: `/lib/**/*.ts` - WordPress data functions must use cache tags for proper revalidation ### Error Handling + - Use `try/catch` blocks for API calls - Utilize `WordPressAPIError` class for consistent API error handling ## Environment Variables + Required environment variables (see `.env.example`): + - `NEXT_PUBLIC_WORDPRESS_URL` - Full URL of WordPress site - `NEXT_WORDPRESS_WEBHOOK_SECRET` - Secret for webhook validation ## Key Dependencies + - Next.js 15.3.3 with React 19.1.0 - TypeScript with strict mode - Tailwind CSS with shadcn/ui components - React Hook Form for form handling -- Lucide React for icons \ No newline at end of file +- Lucide React for icons diff --git a/README.md b/README.md index f024eda6..1979eebd 100644 --- a/README.md +++ b/README.md @@ -6,25 +6,24 @@ This is Next.js application that fetches data from a WordPress site using the Wo ## Make usage - * `make build` - Build the Docker container - * `make build-dev` - Build the development Docker container - * `make up` - Run the production Docker container - * `make up-dev` - Run the development Docker container - * `make build-up` - Build and run the Docker container - * `make build-up-dev` - Build and run the development Docker container - * `make start` - Start the development Docker container" - * `make start-dev` - Start the development Docker container" - * `make stop` - Stop the Docker container" - * `make stop-dev` - Stop the development Docker container" - * `make down` - Stop and remove the development Docker container" - * `make down-dev` - Stop and remove the development Docker container" - * `make restart` - Restart the Docker container" - * `make restart-dev` - Restart the development Docker container" - * `make logs` - Show container logs" - * `make logs-dev` - Show development container logs" - - To start dev server without docker - `npm run dev` or `npm run dev-ssl`(with https) - +- `make build` - Build the Docker container +- `make build-dev` - Build the development Docker container +- `make up` - Run the production Docker container +- `make up-dev` - Run the development Docker container +- `make build-up` - Build and run the Docker container +- `make build-up-dev` - Build and run the development Docker container +- `make start` - Start the development Docker container" +- `make start-dev` - Start the development Docker container" +- `make stop` - Stop the Docker container" +- `make stop-dev` - Stop the development Docker container" +- `make down` - Stop and remove the development Docker container" +- `make down-dev` - Stop and remove the development Docker container" +- `make restart` - Restart the Docker container" +- `make restart-dev` - Restart the development Docker container" +- `make logs` - Show container logs" +- `make logs-dev` - Show development container logs" + +To start dev server without docker - `npm run dev` or `npm run dev-ssl`(with https) ## Table of Contents @@ -212,7 +211,7 @@ Instead of fetching all posts and paginating client-side, the `getPostsPaginated const response = await getPostsPaginated(2, 10, { author: "123", category: "news", - search: "nextjs" + search: "nextjs", }); const { data: posts, headers } = response; @@ -225,10 +224,10 @@ The `getPostsPaginated` function returns a `WordPressResponse` object: ```typescript interface WordPressResponse { - data: T; // The actual posts array + data: T; // The actual posts array headers: { - total: number; // Total number of posts matching the query - totalPages: number; // Total number of pages + total: number; // Total number of posts matching the query + totalPages: number; // Total number of pages }; } ``` @@ -307,7 +306,7 @@ The pagination system includes sophisticated cache tags for optimal performance: ```typescript // Dynamic cache tags based on query parameters -["wordpress", "posts", "posts-page-1", "posts-category-123"] +["wordpress", "posts", "posts-page-1", "posts-category-123"]; ``` This ensures that when content changes, only the relevant pagination pages are revalidated, maintaining excellent performance even with large content sets. @@ -475,13 +474,11 @@ Features: The search system is implemented across several layers: 1. **Client-Side Component** (`search-input.tsx`): - - Uses Next.js App Router's URL handling - Debounced input for better performance - Maintains search state in URL parameters 2. **Server-Side Processing** (`page.tsx`): - - Handles search parameters server-side - Combines search with other filters - Parallel data fetching for better performance @@ -564,15 +561,18 @@ This starter implements an intelligent caching and revalidation system using Nex The WordPress API functions use a sophisticated hierarchical cache tag system for granular revalidation: #### Global Tags + - `wordpress` - Affects all WordPress content #### Content Type Tags + - `posts` - All post content - `categories` - All category content - `tags` - All tag content - `authors` - All author content #### Pagination-Specific Tags + - `posts-page-1`, `posts-page-2`, etc. - Individual pagination pages - `posts-search` - Search result pages - `posts-author-123` - Posts filtered by specific author @@ -580,6 +580,7 @@ The WordPress API functions use a sophisticated hierarchical cache tag system fo - `posts-tag-789` - Posts filtered by specific tag #### Individual Item Tags + - `post-123` - Specific post content - `category-456` - Specific category content - `tag-789` - Specific tag content @@ -590,7 +591,6 @@ This granular system ensures that when content changes, only the relevant cached ### Automatic Revalidation 1. **Install the WordPress Plugin:** - - Navigate to the `/plugin` directory - Use the pre-built `next-revalidate.zip` file or create a ZIP from the `next-revalidate` folder - Install and activate through WordPress admin @@ -598,7 +598,6 @@ This granular system ensures that when content changes, only the relevant cached - Configure your Next.js URL and webhook secret 2. **Configure Next.js:** - - Add `NEXT_WORDPRESS_WEBHOOK_SECRET` to your environment variables (same secret as in WordPress plugin) - The webhook endpoint at `/api/revalidate` is already set up - No additional configuration needed diff --git a/app/api/og/route.tsx b/app/api/og/route.tsx index 36429fbc..bfa71f98 100644 --- a/app/api/og/route.tsx +++ b/app/api/og/route.tsx @@ -12,61 +12,59 @@ export async function GET(request: NextRequest) { const description = searchParams.get("description"); return new ImageResponse( - ( +
+ {title} +
+ {description && (
- {title} + {description}
- {description && ( -
- {description} -
- )} -
- ), + )} + , { width: 1200, height: 630, - } + }, ); } catch (e: any) { console.log(`${e.message}`); diff --git a/app/api/revalidate/route.ts b/app/api/revalidate/route.ts index a801ad79..0fe64fa9 100644 --- a/app/api/revalidate/route.ts +++ b/app/api/revalidate/route.ts @@ -16,35 +16,27 @@ export async function POST(request: NextRequest) { if (secret !== process.env.NEXT_WORDPRESS_WEBHOOK_SECRET) { console.error("Invalid webhook secret"); - return NextResponse.json( - { message: "Invalid webhook secret" }, - { status: 401 } - ); + return NextResponse.json({ message: "Invalid webhook secret" }, { status: 401 }); } const { contentType, contentId, contentSlug } = requestBody; if (!contentType) { - return NextResponse.json( - { message: "Missing content type" }, - { status: 400 } - ); + return NextResponse.json({ message: "Missing content type" }, { status: 400 }); } try { console.log( - `Revalidating content: ${contentType}${ - contentId ? ` (ID: ${contentId})` : "" - }${ + `Revalidating content: ${contentType}${contentId ? ` (ID: ${contentId})` : ""}${ contentSlug ? ` (slug: ${contentSlug})` : "" - }` + }`, ); if (contentType === "post") { revalidateTag("posts"); if (contentId) revalidateTag(`post-${contentId}`); if (contentSlug) revalidateTag(`post-${contentSlug}`); - } else if (contentType === "page") { + } else if (contentType === "page") { revalidateTag("pages"); if (contentSlug) revalidateTag(`page-${contentSlug}`); if (contentId) revalidateTag(`page-${contentId}`); @@ -93,7 +85,7 @@ export async function POST(request: NextRequest) { error: (error as Error).message, timestamp: new Date().toISOString(), }, - { status: 500 } + { status: 500 }, ); } } catch (error) { @@ -104,7 +96,7 @@ export async function POST(request: NextRequest) { error: (error as Error).message, timestamp: new Date().toISOString(), }, - { status: 500 } + { status: 500 }, ); } } diff --git a/app/layout.tsx b/app/layout.tsx index 775ff5ed..6b2780d7 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -25,7 +25,8 @@ const font = FontSans({ }); export const metadata: Metadata = { - title: "Системный Блокъ - Онлайн-журнал о влиянии цифровых технологий на культуру, человека и общество", + title: + "Системный Блокъ - Онлайн-журнал о влиянии цифровых технологий на культуру, человека и общество", description: "Онлайн-журнал о влиянии цифровых технологий на культуру, человека и общество", metadataBase: new URL(siteConfig.site_domain), alternates: { @@ -33,20 +34,12 @@ export const metadata: Metadata = { }, }; -export default function RootLayout({ - children, -}: { - children: React.ReactNode; -}) { +export default function RootLayout({ children }: { children: React.ReactNode }) { return ( - +