Skip to content

Latest commit

 

History

History
211 lines (162 loc) · 7.49 KB

File metadata and controls

211 lines (162 loc) · 7.49 KB

Implementation Summary: Admin Dashboard and Pinned Posts Feature

Overview

This implementation addresses the requirements to:

  1. Create a proper admin dashboard with meaningful statistics and subscriber information
  2. Add the ability to pin a single post that displays in the public sidebar
  3. Show the pinned post in the public sidebar or an empty state if none exists

Changes Made

1. Database Schema Updates

Migration Script: scripts/migrate-add-is-pinned.js

  • Adds is_pinned boolean column to the blog_posts table
  • Creates an index on is_pinned for better query performance
  • Safe to run multiple times (checks if column exists before adding)
  • Run with: npm run db:migrate-pinned

Database Layer: src/lib/database.ts

New Functions Added:

  • getPinnedPost(): Fetches the currently pinned post (excludes deleted posts)
  • pinPost(id): Pins a post and automatically unpins any previously pinned post (atomic transaction)
  • unpinPost(id): Unpins a specific post
  • getAllSubscribers(): Retrieves all subscribers (both active and inactive) for admin dashboard

Interface Updates:

  • Added is_pinned?: boolean to BlogPost interface
  • Updated table creation to include is_pinned column with default false

2. Type Definitions

src/types/blog.ts

  • Added is_pinned?: boolean field to BlogPost type

3. API Routes

/api/posts/pinned - GET

  • Fetches the currently pinned post
  • Returns 404 if no post is pinned
  • Public endpoint (no authentication required)

/api/posts/[id]/pin - POST & DELETE

  • POST: Pins a post (unpins any other pinned post automatically)
  • DELETE: Unpins a post
  • Requires authentication
  • Validates post exists and is not deleted before pinning

/api/admin/subscribers - GET

  • Fetches all subscribers with statistics
  • Returns: subscribers array, total count, active count, inactive count
  • Requires authentication

4. Admin Dashboard Redesign

src/app/admin/dashboard/page.tsx

Completely redesigned to show a proper dashboard overview:

Statistics Cards:

  • Total Posts: Shows all posts in the system
  • Published Posts: Active, non-deleted posts
  • Deleted Posts: Soft-deleted posts count
  • Your Posts: Posts created by the current admin user

Subscriber Statistics:

  • Total Subscribers: All subscribers
  • Active Subscribers: Currently subscribed users
  • Inactive Subscribers: Unsubscribed users

Subscribers List:

  • Displays recent subscribers (up to 10)
  • Shows email, status (active/inactive), and subscription date
  • Empty state message when no subscribers exist

Quick Actions:

  • Create New Post button
  • Manage Posts button
  • Manage Admins button

Key Improvements:

  • Removed duplicate "Add Admin" functionality (this should be on a separate admin management page)
  • Removed duplicate post management (moved to dedicated Posts page)
  • Clean, focused dashboard showing key metrics at a glance

5. Admin Posts Page Updates

src/app/admin/posts/page.tsx

Added pinning functionality:

New Features:

  • Pin/Unpin button for each post (visible for all non-deleted posts)
  • Visual indicator showing which post is currently pinned (yellow badge with pin icon)
  • Pin button changes color based on pin state:
    • Gray when not pinned
    • Yellow when pinned
    • Filled icon when pinned
  • Automatic unpinning of previous post when a new one is pinned
  • Loading state while pin operation is in progress

Pin Handler:

  • handleTogglePin(): Toggles pin state with proper error handling
  • Updates local state optimistically for better UX
  • Shows error messages if operation fails

6. Public Sidebar Updates

src/components/sidebar.tsx

Updated PinnedSection component:

New Behavior:

  • Fetches pinned post from /api/posts/pinned on component mount
  • Shows loading state (returns null) while fetching
  • Shows nothing (returns null) if no post is pinned - This is the empty state
  • When pinned post exists:
    • Displays post image (if available)
    • Shows post title (truncated to 2 lines)
    • Makes entire card clickable to navigate to post
    • Maintains collapsed state behavior (shows minimal version)

Type Safety:

  • Properly typed pinned post state
  • Fixed TypeScript errors
  • Removed unused imports

7. Package.json Updates

  • Added db:migrate-pinned script for running the migration

How It Works

Pinning Flow:

  1. Admin goes to /admin/posts
  2. Clicks pin button on desired post
  3. API call to POST /api/posts/[id]/pin
  4. Database transaction:
    • Unpins all currently pinned posts
    • Pins the selected post
  5. UI updates to show new pin state
  6. Public sidebar automatically shows the pinned post

Unpinning Flow:

  1. Admin clicks pin button on currently pinned post
  2. API call to DELETE /api/posts/[id]/pin
  3. Database updates post to is_pinned = false
  4. UI updates to show unpinned state
  5. Public sidebar shows nothing (empty state)

Dashboard Data Flow:

  1. On load, dashboard fetches:
    • All posts via /api/posts
    • All subscribers via /api/admin/subscribers
  2. Calculates statistics client-side
  3. Displays overview cards and subscriber list
  4. Provides quick action buttons for common tasks

Migration Instructions

To apply the database schema changes:

npm run db:migrate-pinned

This will add the is_pinned column and create the necessary index.

Testing Checklist

  • Run migration script to add is_pinned column
  • Login to admin dashboard
  • Verify dashboard shows correct statistics
  • Verify subscriber list displays (if any subscribers exist)
  • Go to admin posts page
  • Pin a post - verify it shows as pinned
  • Check public sidebar - verify pinned post appears
  • Pin a different post - verify first one is unpinned automatically
  • Unpin the post - verify sidebar shows nothing
  • Try to pin a deleted post - verify it fails gracefully

File Changes Summary

New Files:

  • scripts/migrate-add-is-pinned.js - Database migration script
  • src/app/api/admin/subscribers/route.ts - Subscribers API for admin
  • src/app/api/posts/[id]/pin/route.ts - Pin/unpin post API
  • src/app/api/posts/pinned/route.ts - Get pinned post API
  • IMPLEMENTATION_PINNED_POSTS_DASHBOARD.md - This documentation

Modified Files:

  • src/lib/database.ts - Added pin functions and subscribers getter
  • src/types/blog.ts - Added is_pinned field
  • src/app/admin/dashboard/page.tsx - Complete redesign
  • src/app/admin/posts/page.tsx - Added pin functionality
  • src/components/sidebar.tsx - Dynamic pinned post loading
  • package.json - Added migration script

Notes

  1. Only One Pinned Post: The implementation ensures only one post can be pinned at a time using a database transaction that unpins all posts before pinning the selected one.

  2. Empty State: When no post is pinned, the sidebar component returns null, effectively showing nothing. This is the empty state as requested.

  3. Soft Delete Compatibility: Pinned posts are automatically filtered to exclude deleted posts. Attempting to pin a deleted post returns an error.

  4. Performance: The pinned post is fetched once on sidebar mount, keeping the public-facing page fast and efficient.

  5. Admin Dashboard: The new dashboard focuses on high-level metrics and recent activity, with quick access to detailed management pages.

Security Considerations

  • All pin/unpin operations require authentication
  • Admin API routes verify user is logged in
  • Public pinned post endpoint is intentionally public (read-only)
  • No sensitive subscriber information exposed through public endpoints