Skip to content

stvgo/stvCmsNextJS

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

45 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

STV CMS - Next.js Content Management System

A modern, performant CMS built with Next.js 15, TypeScript, and Tailwind CSS.

πŸš€ Features

Performance Optimizations

  • βœ… React Query for efficient data fetching and caching
  • βœ… Next/Image for optimized image loading
  • βœ… Suspense boundaries for better loading states
  • βœ… Error boundaries for graceful error handling
  • βœ… Code splitting and lazy loading
  • βœ… Production-safe logging (auto-strips in production)

Type Safety

  • βœ… TypeScript strict mode enabled
  • βœ… Zod validators for all API payloads
  • βœ… Type-safe API client with proper error handling
  • βœ… ESLint rules enforcing best practices

Developer Experience

  • βœ… Custom hooks for all data operations
  • βœ… Centralized configuration management
  • βœ… Environment validation at startup
  • βœ… React Query Devtools for debugging
  • βœ… Comprehensive error handling

UI/UX

  • βœ… Loading skeletons for all async data
  • βœ… Toast notifications (ready to implement)
  • βœ… Responsive design with Tailwind CSS
  • βœ… Dark/B&W themes with proper CSS variables
  • βœ… Accessible components (Radix UI)

πŸ“ Project Structure

stv-cms-nextjs/
β”œβ”€β”€ app/                      # Next.js App Router
β”‚   β”œβ”€β”€ layout.tsx           # Root layout with providers
β”‚   β”œβ”€β”€ page.tsx             # Dashboard page
β”‚   └── post/                # Post-related routes
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ components/          # React components
β”‚   β”‚   β”œβ”€β”€ ui/             # shadcn/ui components
β”‚   β”‚   β”œβ”€β”€ providers/      # Context providers
β”‚   β”‚   β”œβ”€β”€ error-boundary.tsx
β”‚   β”‚   β”œβ”€β”€ loading-skeletons.tsx
β”‚   β”‚   └── cms-image.tsx
β”‚   β”œβ”€β”€ config/             # App configuration
β”‚   β”‚   └── index.ts
β”‚   β”œβ”€β”€ hooks/              # Custom React hooks
β”‚   β”‚   β”œβ”€β”€ use-posts.ts
β”‚   β”‚   β”œβ”€β”€ use-post-queries.ts
β”‚   β”‚   β”œβ”€β”€ use-image-upload.ts
β”‚   β”‚   └── use-utils.ts
β”‚   β”œβ”€β”€ lib/                # Utilities and API
β”‚   β”‚   β”œβ”€β”€ api.ts
β”‚   β”‚   β”œβ”€β”€ api-errors.ts
β”‚   β”‚   β”œβ”€β”€ logger.ts
β”‚   β”‚   β”œβ”€β”€ env.ts
β”‚   β”‚   β”œβ”€β”€ query-keys.ts
β”‚   β”‚   └── utils.ts
β”‚   β”œβ”€β”€ types/              # TypeScript definitions
β”‚   β”‚   └── post.ts
β”‚   └── validators/         # Zod schemas
β”‚       └── post.ts
└── public/                 # Static assets

πŸ› οΈ Getting Started

Prerequisites

  • Node.js 18+
  • npm or yarn
  • Backend API running on port 8080 (or configure in .env)

Installation

  1. Clone and install dependencies
npm install
  1. Set up environment variables
cp .env.example .env
# Edit .env with your configuration
  1. Run development server
npm run dev
  1. Build for production
npm run build
npm start

πŸ“ Usage

Fetching Posts

Server Component (Recommended for initial load)

import { getPosts } from '@/lib/api';

export default async function PostsPage() {
  const posts = await getPosts();
  return <PostsList posts={posts} />;
}

Client Component (with React Query)

'use client';
import { usePosts } from '@/hooks/use-post-queries';
import { PostCardSkeleton } from '@/components/loading-skeletons';

export default function PostsPage() {
  const { data: posts, isLoading, error } = usePosts();
  
  if (isLoading) return <PostCardSkeleton />;
  if (error) return <ErrorDisplay error={error} />;
  
  return <PostsList posts={posts} />;
}

Creating a Post

'use client';
import { useCreatePost } from '@/hooks/use-post-queries';
import { useRouter } from 'next/navigation';

export default function CreatePostForm() {
  const mutation = useCreatePost();
  const router = useRouter();

  const handleSubmit = async (data: FormData) => {
    try {
      await mutation.mutateAsync({
        title: data.get('title') as string,
        user_id: 'Stiven Valeriano',
        content_blocks: [...],
      });
      router.push('/');
    } catch (error) {
      console.error('Failed to create post:', error);
    }
  };

  return <form onSubmit={handleSubmit}>...</form>;
}

Image Upload

'use client';
import { useImageUpload } from '@/hooks/use-image-upload';
import { CMSImage } from '@/components/cms-image';

export default function ImageUploader() {
  const { upload, loading, previewUrl, filename } = useImageUpload();

  return (
    <div>
      <input type="file" onChange={(e) => upload(e.target.files[0])} />
      {loading && <p>Uploading...</p>}
      {previewUrl && <img src={previewUrl} alt="Preview" />}
      {filename && <CMSImage src={filename} alt="Uploaded" width={400} height={300} />}
    </div>
  );
}

πŸ”§ Configuration

Environment Variables

Variable Description Default
API_URL Backend API URL (server-side) http://localhost:8080
NEXT_PUBLIC_IMAGE_URL Image base URL http://localhost:8080
API_TIMEOUT Request timeout (ms) 10000
API_RETRY_ATTEMPTS Retry attempts 3
NEXT_PUBLIC_APP_NAME App name STV CMS
DEBUG Enable debug logging false

Tailwind Configuration

Edit tailwind.config.ts to customize:

  • Colors and design tokens
  • Breakpoints
  • Animations
  • Component variants

🎨 Theming

The app supports multiple themes via CSS variables:

  • Dark (default): Modern dark theme
  • B&W: Black and white minimal theme

Switch themes using the theme provider or UI toggle.

πŸ“Š Performance Tips

  1. Use Server Components for initial data fetching
  2. Use React Query hooks for client-side interactions
  3. Enable image optimization with CMSImage component
  4. Implement route prefetching with Link component
  5. Use Suspense boundaries for better loading UX

πŸ§ͺ Testing

# Run linter
npm run lint

# Type check
npx tsc --noEmit

# Build
npm run build

πŸ“š Best Practices

DO βœ…

  • Use Server Components for data fetching
  • Use React Query for client-side mutations
  • Implement loading skeletons
  • Handle errors with ErrorBoundary
  • Use TypeScript strictly
  • Validate inputs with Zod

DON'T ❌

  • Don't use console.log (use logger)
  • Don't skip error handling
  • Don't use any type
  • Don't hardcode URLs
  • Don't ignore loading states
  • Don't bypass TypeScript

🚨 Error Handling

All API errors are properly typed:

import { isApiError, getErrorMessage } from '@/lib/api-errors';

try {
  await createPost(data);
} catch (error) {
  if (isApiError(error)) {
    // Handle API-specific errors
    console.error('API Error:', error.message, error.status);
  } else {
    // Handle other errors
    console.error('Unexpected error:', getErrorMessage(error));
  }
}

🀝 Contributing

  1. Follow the existing code structure
  2. Use TypeScript strictly
  3. Add proper error handling
  4. Update documentation
  5. Test before committing

πŸ“„ License

MIT License

🎯 Roadmap

  • Add unit tests
  • Implement form validation with react-hook-form
  • Add optimistic updates
  • Implement search with debounce
  • Add pagination component
  • Implement drag-and-drop for images
  • Add keyboard shortcuts
  • Improve accessibility (ARIA labels)
  • Add analytics tracking
  • Implement PWA features

Built with ❀️ using Next.js 15, TypeScript, and Tailwind CSS

About

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages