Files
unai/docs/DEVELOPMENT_GUIDELINES.md
T
Krikorios aa459f4bd6 🎉 Complete AI-Enhanced University Portal - Ready for Production
 Major Features Added:
- AI Chat with conversation memory and university-specific knowledge base
- Multi-tenant university support with white-label capabilities
- Professional admin interface for knowledge base management
- Advanced database schema with Prisma ORM
- Comprehensive documentation and guides
- Modern Next.js 15 + React 19 architecture
- Bilingual support (English/Arabic)
- Role-based access control
- Real-time chat interface with loading states

🔧 Technical Improvements:
- Fixed all linter errors and TypeScript issues
- Cleaned up codebase and removed legacy files
- Added comprehensive .gitignore
- Updated README with detailed setup instructions
- Optimized database schema and migrations
- Enhanced error handling and user experience

📚 Documentation:
- AI Conversation Memory Guide
- AI Enhancement Summary
- Developer Guide
- User Guide
- Complete setup and deployment instructions

🚀 Ready for GitHub deployment and production use!
2025-07-20 08:26:25 +04:00

19 KiB

Development Guidelines - White-Label University Portal

📋 Overview

This document provides comprehensive development guidelines for the white-label university portal transformation. These guidelines ensure code quality, consistency, and maintainability throughout the development process.

Target Audience: Development team
Scope: White-label transformation and ongoing development
Last Updated: January 2025


🏗️ Architecture Principles

1. Multi-Tenant Design

  • Data Isolation: Strict separation between university data
  • Configuration-Driven: All university-specific content is configurable
  • Scalable: Support for 10+ universities simultaneously
  • Secure: No data leakage between universities

2. Configuration-First Approach

  • Dynamic Content: No hardcoded university-specific content
  • Feature Flags: Enable/disable features per university
  • Branding: Complete visual customization per university
  • Localization: Multi-language support with RTL

3. AI-Enhanced Features

  • Intelligent Chatbot: University-specific AI assistant
  • Dynamic Knowledge Base: Configurable AI responses
  • Accessibility: AI-powered accessibility features
  • Analytics: AI-driven insights and recommendations

📁 File Structure Standards

Directory Organization

src/
├── app/                    # Next.js App Router
│   ├── (university)/       # University-specific routes
│   ├── admin/             # Admin interface
│   ├── api/               # API routes
│   └── globals.css        # Global styles
├── components/            # Reusable components
│   ├── ui/               # Base UI components
│   ├── forms/            # Form components
│   ├── layout/           # Layout components
│   └── features/         # Feature-specific components
├── config/               # Configuration interfaces
├── lib/                  # Utility functions
├── types/                # TypeScript type definitions
└── hooks/                # Custom React hooks

Naming Conventions

  • Files: kebab-case (university-config.ts)
  • Components: PascalCase (UniversityCard.tsx)
  • Functions: camelCase (getUniversityConfig())
  • Constants: UPPER_SNAKE_CASE (DEFAULT_CONFIG)
  • Types: PascalCase (UniversityConfig)

💻 Coding Standards

TypeScript Guidelines

1. Type Definitions

// ✅ Good: Comprehensive type definitions
interface UniversityConfig {
  id: string;
  name: string;
  branding: BrandingConfig;
  features: FeatureConfig;
  ai: AIConfig;
  createdAt: Date;
  updatedAt: Date;
}

// ❌ Bad: Any types or missing types
const config: any = {
  name: "University"
};

2. Function Signatures

// ✅ Good: Clear function signatures with types
async function getUniversityConfig(universityId: string): Promise<UniversityConfig> {
  // Implementation
}

// ❌ Bad: Missing types or unclear signatures
function getConfig(id) {
  // Implementation
}

3. Error Handling

// ✅ Good: Proper error handling with types
try {
  const config = await getUniversityConfig(universityId);
  return config;
} catch (error) {
  if (error instanceof UniversityNotFoundError) {
    throw new Error(`University ${universityId} not found`);
  }
  throw new Error('Failed to fetch university configuration');
}

React Component Guidelines

1. Component Structure

// ✅ Good: Well-structured component
interface UniversityCardProps {
  university: UniversityConfig;
  onEdit?: (id: string) => void;
  onDelete?: (id: string) => void;
}

export default function UniversityCard({ 
  university, 
  onEdit, 
  onDelete 
}: UniversityCardProps) {
  const { name, branding, status } = university;
  
  return (
    <div className="university-card">
      <h3>{name}</h3>
      <img src={branding.logo} alt={`${name} logo`} />
      <div className="actions">
        {onEdit && (
          <button onClick={() => onEdit(university.id)}>
            Edit
          </button>
        )}
        {onDelete && (
          <button onClick={() => onDelete(university.id)}>
            Delete
          </button>
        )}
      </div>
    </div>
  );
}

2. Hooks Usage

// ✅ Good: Custom hooks for reusable logic
export function useUniversity(universityId: string) {
  const [university, setUniversity] = useState<UniversityConfig | null>(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    async function fetchUniversity() {
      try {
        setLoading(true);
        const data = await getUniversityConfig(universityId);
        setUniversity(data);
      } catch (err) {
        setError(err instanceof Error ? err.message : 'Unknown error');
      } finally {
        setLoading(false);
      }
    }

    fetchUniversity();
  }, [universityId]);

  return { university, loading, error };
}

Database Guidelines

1. Schema Design

// ✅ Good: Well-designed schema with relationships
model University {
  id        String   @id @default(uuid())
  slug      String   @unique
  name      String
  branding  Json     // Branding configuration
  features  Json     // Feature flags
  ai        Json     // AI configuration
  status    UniversityStatus @default(SETUP)
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  // Relationships
  programs  AcademicProgram[]
  content   UniversityContent[]
  users     User[]
  knowledgeBase AIKnowledgeBase[]

  @@map("universities")
}

enum UniversityStatus {
  SETUP
  ACTIVE
  INACTIVE
  SUSPENDED
}

2. Query Patterns

// ✅ Good: Efficient queries with proper filtering
export async function getUniversityWithPrograms(universityId: string) {
  return await prisma.university.findUnique({
    where: { id: universityId },
    include: {
      programs: {
        where: { isActive: true },
        orderBy: { createdAt: 'desc' }
      },
      content: {
        where: { isPublished: true },
        orderBy: { updatedAt: 'desc' }
      }
    }
  });
}

🎨 Styling Guidelines

Tailwind CSS Standards

1. Component Styling

// ✅ Good: Consistent component styling
interface ButtonProps {
  variant: 'primary' | 'secondary' | 'danger';
  size: 'sm' | 'md' | 'lg';
  children: React.ReactNode;
}

export function Button({ variant, size, children }: ButtonProps) {
  const baseClasses = "inline-flex items-center justify-center rounded-md font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none ring-offset-background";
  
  const variantClasses = {
    primary: "bg-primary text-primary-foreground hover:bg-primary/90",
    secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
    danger: "bg-destructive text-destructive-foreground hover:bg-destructive/90"
  };
  
  const sizeClasses = {
    sm: "h-9 px-3 text-sm",
    md: "h-10 py-2 px-4",
    lg: "h-11 px-8"
  };

  return (
    <button 
      className={`${baseClasses} ${variantClasses[variant]} ${sizeClasses[size]}`}
    >
      {children}
    </button>
  );
}

2. Dynamic Styling

// ✅ Good: Dynamic styling based on university config
export function UniversityHeader({ university }: { university: UniversityConfig }) {
  const { branding } = university;
  
  return (
    <header 
      className="w-full bg-white shadow-sm"
      style={{
        '--primary-color': branding.colors.primary,
        '--secondary-color': branding.colors.secondary,
      } as React.CSSProperties}
    >
      <div className="container mx-auto px-4 py-4">
        <img 
          src={branding.logo} 
          alt={`${university.name} logo`}
          className="h-12 w-auto"
        />
      </div>
    </header>
  );
}

🔧 Configuration Management

Environment Variables

# ✅ Good: Well-organized environment variables
# Database
DATABASE_URL="postgresql://..."
DATABASE_POOL_SIZE=10

# AI Configuration
OPENROUTER_API_KEY="sk-..."
OLLAMA_URL="http://localhost:11434"
AI_MODEL="gpt-4-turbo"

# University Configuration
DEFAULT_UNIVERSITY_ID="default"
MAX_UNIVERSITIES=100
STORAGE_LIMIT_GB=50

# Security
JWT_SECRET="your-secret-key"
ENCRYPTION_KEY="your-encryption-key"

# Monitoring
SENTRY_DSN="https://..."
ANALYTICS_ID="GA-..."

Configuration Interfaces

// ✅ Good: Comprehensive configuration interfaces
export interface UniversityConfig {
  // Basic Information
  id: string;
  slug: string;
  name: string;
  shortName?: string;
  
  // Branding
  branding: BrandingConfig;
  
  // Features
  features: FeatureConfig;
  
  // AI Configuration
  ai: AIConfig;
  
  // Contact Information
  contact: ContactConfig;
  
  // Status
  status: UniversityStatus;
  
  // Timestamps
  createdAt: Date;
  updatedAt: Date;
}

export interface BrandingConfig {
  logo: {
    primary: string;
    secondary?: string;
    favicon: string;
  };
  colors: {
    primary: string;
    secondary: string;
    accent: string;
    background: string;
    surface: string;
    text: {
      primary: string;
      secondary: string;
    };
  };
  fonts: {
    primary: string;
    secondary?: string;
  };
}

🧪 Testing Guidelines

Unit Testing

// ✅ Good: Comprehensive unit tests
import { describe, it, expect, vi } from 'vitest';
import { render, screen, fireEvent } from '@testing-library/react';
import { UniversityCard } from './UniversityCard';

describe('UniversityCard', () => {
  const mockUniversity = {
    id: '1',
    name: 'Test University',
    branding: {
      logo: '/test-logo.png',
      colors: { primary: '#000000' }
    },
    status: 'ACTIVE' as const
  };

  it('renders university information correctly', () => {
    render(<UniversityCard university={mockUniversity} />);
    
    expect(screen.getByText('Test University')).toBeInTheDocument();
    expect(screen.getByAltText('Test University logo')).toBeInTheDocument();
  });

  it('calls onEdit when edit button is clicked', () => {
    const onEdit = vi.fn();
    render(<UniversityCard university={mockUniversity} onEdit={onEdit} />);
    
    fireEvent.click(screen.getByText('Edit'));
    expect(onEdit).toHaveBeenCalledWith('1');
  });

  it('calls onDelete when delete button is clicked', () => {
    const onDelete = vi.fn();
    render(<UniversityCard university={mockUniversity} onDelete={onDelete} />);
    
    fireEvent.click(screen.getByText('Delete'));
    expect(onDelete).toHaveBeenCalledWith('1');
  });
});

Integration Testing

// ✅ Good: API integration tests
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
import { createServer } from 'http';
import { apiResolver } from 'next/dist/server/api-utils';
import { getUniversityConfig } from '../lib/university';

describe('University API', () => {
  let server: any;

  beforeAll(() => {
    server = createServer(async (req, res) => {
      await apiResolver(req, res, undefined, getUniversityConfig, {
        previewModeId: '',
        previewModeEncryptionKey: '',
        previewModeSigningKey: '',
      }, false);
    });
    server.listen(3001);
  });

  afterAll(() => {
    server.close();
  });

  it('returns university configuration', async () => {
    const response = await fetch('http://localhost:3001/api/universities/1');
    const data = await response.json();
    
    expect(response.status).toBe(200);
    expect(data).toHaveProperty('id');
    expect(data).toHaveProperty('name');
    expect(data).toHaveProperty('branding');
  });
});

🔒 Security Guidelines

Data Validation

// ✅ Good: Comprehensive input validation
import { z } from 'zod';

const UniversityConfigSchema = z.object({
  name: z.string().min(1).max(255),
  slug: z.string().min(1).max(50).regex(/^[a-z0-9-]+$/),
  branding: z.object({
    logo: z.object({
      primary: z.string().url(),
      favicon: z.string().url(),
    }),
    colors: z.object({
      primary: z.string().regex(/^#[0-9A-F]{6}$/i),
      secondary: z.string().regex(/^#[0-9A-F]{6}$/i),
    }),
  }),
  features: z.object({
    aiChatbot: z.boolean(),
    studentPortal: z.boolean(),
    researchPortal: z.boolean(),
  }),
});

export function validateUniversityConfig(data: unknown): UniversityConfig {
  return UniversityConfigSchema.parse(data);
}

Authentication & Authorization

// ✅ Good: Proper authentication middleware
import { NextRequest, NextResponse } from 'next/server';
import { verifyToken } from '../lib/auth';

export async function authMiddleware(request: NextRequest) {
  const token = request.headers.get('authorization')?.replace('Bearer ', '');
  
  if (!token) {
    return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
  }

  try {
    const user = await verifyToken(token);
    const universityId = request.nextUrl.searchParams.get('universityId');
    
    // Check if user has access to this university
    if (universityId && !user.universities.includes(universityId)) {
      return NextResponse.json({ error: 'Forbidden' }, { status: 403 });
    }
    
    // Add user to request context
    request.headers.set('x-user-id', user.id);
    request.headers.set('x-user-role', user.role);
    
    return NextResponse.next();
  } catch (error) {
    return NextResponse.json({ error: 'Invalid token' }, { status: 401 });
  }
}

📊 Performance Guidelines

Code Splitting

// ✅ Good: Proper code splitting
import dynamic from 'next/dynamic';

// Lazy load heavy components
const UniversityAnalytics = dynamic(() => import('./UniversityAnalytics'), {
  loading: () => <div>Loading analytics...</div>,
  ssr: false
});

const AIKnowledgeBase = dynamic(() => import('./AIKnowledgeBase'), {
  loading: () => <div>Loading knowledge base...</div>
});

Database Optimization

// ✅ Good: Optimized database queries
export async function getUniversitiesWithStats() {
  return await prisma.university.findMany({
    select: {
      id: true,
      name: true,
      status: true,
      _count: {
        select: {
          programs: true,
          users: true,
          content: true,
        }
      }
    },
    where: {
      status: 'ACTIVE'
    },
    orderBy: {
      createdAt: 'desc'
    }
  });
}

Caching Strategy

// ✅ Good: Proper caching implementation
import { cache } from 'react';

export const getUniversityConfig = cache(async (universityId: string) => {
  const config = await prisma.university.findUnique({
    where: { id: universityId },
    include: {
      branding: true,
      features: true,
      ai: true,
    }
  });
  
  return config;
});

📝 Documentation Standards

Code Documentation

/**
 * Retrieves university configuration with caching
 * 
 * @param universityId - The unique identifier of the university
 * @param options - Optional configuration options
 * @returns Promise resolving to university configuration
 * 
 * @example
 * ```typescript
 * const config = await getUniversityConfig('university-1', {
 *   includePrograms: true,
 *   includeContent: false
 * });
 * ```
 * 
 * @throws {UniversityNotFoundError} When university is not found
 * @throws {ValidationError} When university ID is invalid
 */
export async function getUniversityConfig(
  universityId: string,
  options: GetUniversityConfigOptions = {}
): Promise<UniversityConfig> {
  // Implementation
}

API Documentation

/**
 * @api {GET} /api/universities/:id Get University Configuration
 * @apiName GetUniversity
 * @apiGroup Universities
 * @apiVersion 1.0.0
 * 
 * @apiParam {String} id University unique identifier
 * 
 * @apiSuccess {String} id University ID
 * @apiSuccess {String} name University name
 * @apiSuccess {Object} branding Branding configuration
 * @apiSuccess {Object} features Feature flags
 * @apiSuccess {Object} ai AI configuration
 * 
 * @apiSuccessExample {json} Success-Response:
 *     HTTP/1.1 200 OK
 *     {
 *       "id": "university-1",
 *       "name": "Example University",
 *       "branding": { ... },
 *       "features": { ... },
 *       "ai": { ... }
 *     }
 * 
 * @apiError {Object} 404 University not found
 * @apiError {Object} 500 Internal server error
 */

🚀 Deployment Guidelines

Environment Configuration

# ✅ Good: Environment-specific configurations
# Development
NODE_ENV=development
DATABASE_URL=postgresql://localhost:5432/university_dev
AI_PROVIDER=mock

# Staging
NODE_ENV=staging
DATABASE_URL=postgresql://staging-db:5432/university_staging
AI_PROVIDER=openrouter

# Production
NODE_ENV=production
DATABASE_URL=postgresql://prod-db:5432/university_prod
AI_PROVIDER=openrouter

Build Optimization

// ✅ Good: Optimized build configuration
// next.config.js
const nextConfig = {
  experimental: {
    optimizeCss: true,
    optimizePackageImports: ['@heroicons/react', 'lucide-react'],
  },
  images: {
    domains: ['university-assets.com'],
    formats: ['image/webp', 'image/avif'],
  },
  compiler: {
    removeConsole: process.env.NODE_ENV === 'production',
  },
  webpack: (config, { dev, isServer }) => {
    if (!dev && !isServer) {
      config.optimization.splitChunks.cacheGroups = {
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors',
          chunks: 'all',
        },
      };
    }
    return config;
  },
};

🎯 Quality Assurance

Code Review Checklist

  • TypeScript: All types properly defined
  • Testing: Unit tests written and passing
  • Performance: No performance regressions
  • Security: Input validation and authentication
  • Accessibility: WCAG 2.1 AA compliance
  • Documentation: Code and API documented
  • Configuration: No hardcoded values
  • Error Handling: Proper error handling
  • Logging: Appropriate logging added
  • Monitoring: Metrics and alerts configured

Performance Benchmarks

  • Page Load Time: <2 seconds
  • API Response Time: <500ms
  • Database Query Time: <100ms
  • Bundle Size: <500KB (gzipped)
  • Lighthouse Score: >90

Security Requirements

  • Authentication: JWT with refresh tokens
  • Authorization: Role-based access control
  • Data Validation: Input sanitization and validation
  • Encryption: Data encrypted at rest and in transit
  • Audit Logging: All actions logged
  • Rate Limiting: API rate limiting implemented
  • CORS: Proper CORS configuration
  • HTTPS: HTTPS enforced in production

Last Updated: January 2025
Next Review: Monthly
Status: Active