Hackorda Docs

Development

🚀 Getting Started

Prerequisites

  • Node.js 18+ - Latest LTS version recommended
  • PostgreSQL 14+ - Database server
  • Git - Version control
  • Code Editor - VS Code recommended with extensions

Environment Setup

  1. Clone Repository
git clone <repository-url>
cd hackorda-mvp
  1. Install Dependencies
npm install
  1. Environment Configuration Create .env.local file:
# Database
DATABASE_URL="postgresql://username:password@localhost:5432/hackorda_mvp"

# Clerk Authentication
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY="pk_test_..."
CLERK_SECRET_KEY="sk_test_..."
NEXT_PUBLIC_CLERK_SIGN_IN_URL="/sign-in"
NEXT_PUBLIC_CLERK_SIGN_UP_URL="/sign-up"
NEXT_PUBLIC_CLERK_AFTER_SIGN_IN_URL="/"
NEXT_PUBLIC_CLERK_AFTER_SIGN_UP_URL="/"
  1. Database Setup
# Generate migration files
npm run generate

# Run migrations
npm run migrate

# Seed database (optional)
npm run seed
  1. Start Development Server
npm run dev
# Visit http://localhost:3000

📁 Project Structure

hackorda-mvp/
├── docs/                    # Documentation files
├── public/                  # Static assets
│   ├── blog/               # Markdown blog posts
│   └── images/             # Image assets
├── src/
│   ├── app/                # Next.js App Router
│   │   ├── admin/          # Admin dashboard pages
│   │   ├── api/            # API routes
│   │   ├── auth/           # Authentication pages
│   │   ├── blog/           # Blog pages
│   │   ├── quiz/           # Quiz interface
│   │   └── globals.css     # Global styles
│   ├── components/         # React components
│   │   ├── ui/             # Shadcn/ui components
│   │   ├── admin/          # Admin-specific components
│   │   ├── quiz/           # Quiz components
│   │   └── layout/         # Layout components
│   ├── db/                 # Database configuration
│   │   ├── migrations/     # Drizzle migrations
│   │   ├── schema.ts       # Database schema
│   │   └── seed.ts         # Database seeding
│   ├── hooks/              # Custom React hooks
│   │   ├── admin/          # Admin hooks
│   │   ├── quiz/           # Quiz hooks
│   │   └── user/           # User hooks
│   ├── lib/                # Utility libraries
│   │   ├── auth/           # Authentication utilities
│   │   ├── blog/           # Blog utilities
│   │   └── utils.ts        # General utilities
│   ├── types/              # TypeScript definitions
│   └── middleware.ts       # Next.js middleware
├── tailwind.config.ts      # TailwindCSS configuration
├── drizzle.config.ts       # Drizzle ORM configuration
└── package.json            # Dependencies and scripts

🛠️ Development Workflow

Daily Development

# Start development with fresh build
npm run dev

# Run linting  
npm run lint

# Build for production
npm run build

# Start production server
npm run start

Database Development

# Generate new migration
npm run generate

# Apply migrations
npm run migrate

# Reset and seed database (development only)
npm run seed

# Seed only users
npm run seedusers

🔧 Development Tools

VS Code Extensions

{
  "recommendations": [
    "bradlc.vscode-tailwindcss",
    "ms-vscode.vscode-typescript-next",
    "esbenp.prettier-vscode",
    "ms-vscode.vscode-eslint",
    "drizzle-team.drizzle-vscode"
  ]
}

VS Code Settings

{
  "editor.formatOnSave": true,
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "typescript.preferences.importModuleSpecifier": "relative",
  "tailwindCSS.experimental.classRegex": [
    "cn\\(([^)]*)\\)"
  ]
}

📊 Database Development

Schema Changes Workflow

# 1. Modify src/db/schema.ts
# 2. Generate migration
npm run generate

# 3. Review generated migration in src/db/migrations/
# 4. Apply migration
npm run migrate

# 5. Update TypeScript types (auto-generated)

Common Database Operations

// Creating new table
export const newTable = pgTable("new_table", {
  id: serial("id").primaryKey(),
  name: varchar("name", { length: 255 }).notNull(),
  createdAt: timestamp("created_at").defaultNow().notNull()
});

// Adding relationship
export const relatedTable = pgTable("related_table", {
  id: serial("id").primaryKey(),
  newTableId: integer("new_table_id").references(() => newTable.id)
});

🎯 Component Development

Component Structure

// src/components/example/ExampleComponent.tsx
"use client"; // If client component needed

import { useState } from "react";
import { Button } from "@/components/ui/button";

interface ExampleComponentProps {
  title: string;
  onAction?: () => void;
}

export function ExampleComponent({ title, onAction }: ExampleComponentProps) {
  const [isLoading, setIsLoading] = useState(false);

  const handleClick = () => {
    setIsLoading(true);
    onAction?.();
    setIsLoading(false);
  };

  return (
    <div className="p-4">
      <h2 className="text-xl font-bold">{title}</h2>
      <Button onClick={handleClick} disabled={isLoading}>
        {isLoading ? "Loading..." : "Action"}
      </Button>
    </div>
  );
}

Hook Development Pattern

// src/hooks/example/useExample.ts
"use client";

import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";

export function useExample() {
  const queryClient = useQueryClient();

  const { data, isLoading, error } = useQuery({
    queryKey: ['example'],
    queryFn: async () => {
      const response = await fetch('/api/example');
      if (!response.ok) throw new Error('Failed to fetch');
      return response.json();
    },
    staleTime: 5 * 60 * 1000, // 5 minutes
    gcTime: 10 * 60 * 1000, // 10 minutes
  });

  const mutation = useMutation({
    mutationFn: async (data: ExampleData) => {
      const response = await fetch('/api/example', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(data)
      });
      if (!response.ok) throw new Error('Failed to create');
      return response.json();
    },
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['example'] });
    }
  });

  return {
    data,
    isLoading,
    error,
    create: mutation.mutateAsync,
    isCreating: mutation.isPending
  };
}

🔐 API Development

API Route Pattern

// src/app/api/example/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { db } from '@/db';
import { withAuth } from '@/lib/auth/api-middleware';

export async function GET(request: NextRequest) {
  return withAuth(request, async (req, userId) => {
    try {
      // API logic here
      const data = await db.select().from(table);
      return NextResponse.json(data);
    } catch (error) {
      console.error('API Error:', error);
      return NextResponse.json(
        { error: 'Internal server error' },
        { status: 500 }
      );
    }
  });
}

export async function POST(request: NextRequest) {
  return withAuth(request, async (req, userId) => {
    const body = await request.json();
    
    // Validate input
    if (!body.requiredField) {
      return NextResponse.json(
        { error: 'Required field missing' },
        { status: 400 }
      );
    }

    // Process request
    const result = await db.insert(table).values(body).returning();
    return NextResponse.json(result[0]);
  });
}

🎨 Styling Guidelines

TailwindCSS Best Practices

// Use cn() utility for conditional classes
import { cn } from "@/lib/utils";

export function Component({ variant, className }: Props) {
  return (
    <div className={cn(
      "base classes here",
      variant === "primary" && "primary variant classes",
      variant === "secondary" && "secondary variant classes",
      className
    )}>
      Content
    </div>
  );
}

Component Styling Pattern

// Consistent spacing and sizing
<div className="space-y-6 p-6">
  <div className="flex items-center justify-between">
    <h1 className="text-3xl font-bold tracking-tight">Title</h1>
    <Button>Action</Button>
  </div>
  
  <div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
    {/* Grid items */}
  </div>
</div>

🧪 Testing Guidelines

npm install --save-dev @testing-library/react @testing-library/jest-dom jest jest-environment-jsdom

Test Example

// __tests__/components/ExampleComponent.test.tsx
import { render, screen } from '@testing-library/react';
import { ExampleComponent } from '@/components/ExampleComponent';

describe('ExampleComponent', () => {
  it('renders correctly', () => {
    render(<ExampleComponent title="Test Title" />);
    expect(screen.getByText('Test Title')).toBeInTheDocument();
  });
});

🚀 Deployment Guide

Build Process

# Ensure clean build
npm run lint
npm run build

# Test production build locally
npm run start

Environment Variables for Production

# Database
DATABASE_URL="postgresql://prod-db-url"

# Clerk (Production keys)
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY="pk_live_..."
CLERK_SECRET_KEY="sk_live_..."

Deployment Checklist

  • Environment variables configured
  • Database migrations applied
  • Build passes without errors
  • All tests pass
  • Security headers configured
  • Error monitoring setup

🔍 Debugging Tips

Common Issues

1. Database Connection Errors

# Check PostgreSQL is running
pg_isready -h localhost -p 5432

# Test connection
psql $DATABASE_URL

2. Type Errors After Schema Changes

# Regenerate types
npm run generate

3. Clerk Authentication Issues

# Check environment variables
echo $NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY
echo $CLERK_SECRET_KEY

Debugging Tools

// Add debug logging
console.log('Debug:', { variable, data });

// Use Next.js debugging
export const dynamic = 'force-dynamic'; // In page.tsx

📚 Learning Resources

Development Best Practices

  1. Follow TypeScript strict mode - Enable strict type checking
  2. Use proper error handling - Always handle async errors
  3. Implement loading states - Better user experience
  4. Follow naming conventions - Consistent code style
  5. Write self-documenting code - Clear variable/function names
  6. Keep components small - Single responsibility principle
  7. Use proper database indexes - Performance optimization
  8. Implement proper validation - Input sanitization

🤝 Contributing Guidelines

Code Standards

  • Use TypeScript strict mode
  • Follow Prettier formatting
  • Write descriptive commit messages
  • Add JSDoc comments for complex functions
  • Update documentation for new features

Git Workflow

# Create feature branch
git checkout -b feature/new-feature

# Make changes and commit
git add .
git commit -m "feat: add new feature description"

# Push and create PR
git push origin feature/new-feature

Commit Convention

  • feat: New features
  • fix: Bug fixes
  • docs: Documentation changes
  • style: Code style changes
  • refactor: Code refactoring
  • test: Adding tests
  • chore: Maintenance tasks

On this page