# 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 ```typescript // โœ… 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 ```typescript // โœ… Good: Clear function signatures with types async function getUniversityConfig(universityId: string): Promise { // Implementation } // โŒ Bad: Missing types or unclear signatures function getConfig(id) { // Implementation } ``` #### 3. Error Handling ```typescript // โœ… 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 ```typescript // โœ… 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 (

{name}

{`${name}
{onEdit && ( )} {onDelete && ( )}
); } ``` #### 2. Hooks Usage ```typescript // โœ… Good: Custom hooks for reusable logic export function useUniversity(universityId: string) { const [university, setUniversity] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(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 ```prisma // โœ… 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 ```typescript // โœ… 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 ```typescript // โœ… 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 ( ); } ``` #### 2. Dynamic Styling ```typescript // โœ… Good: Dynamic styling based on university config export function UniversityHeader({ university }: { university: UniversityConfig }) { const { branding } = university; return (
{`${university.name}
); } ``` --- ## ๐Ÿ”ง Configuration Management ### Environment Variables ```bash # โœ… 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 ```typescript // โœ… 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 ```typescript // โœ… 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(); 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(); fireEvent.click(screen.getByText('Edit')); expect(onEdit).toHaveBeenCalledWith('1'); }); it('calls onDelete when delete button is clicked', () => { const onDelete = vi.fn(); render(); fireEvent.click(screen.getByText('Delete')); expect(onDelete).toHaveBeenCalledWith('1'); }); }); ``` ### Integration Testing ```typescript // โœ… 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 ```typescript // โœ… 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 ```typescript // โœ… 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 ```typescript // โœ… Good: Proper code splitting import dynamic from 'next/dynamic'; // Lazy load heavy components const UniversityAnalytics = dynamic(() => import('./UniversityAnalytics'), { loading: () =>
Loading analytics...
, ssr: false }); const AIKnowledgeBase = dynamic(() => import('./AIKnowledgeBase'), { loading: () =>
Loading knowledge base...
}); ``` ### Database Optimization ```typescript // โœ… 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 ```typescript // โœ… 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 ```typescript /** * 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 { // Implementation } ``` ### API Documentation ```typescript /** * @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 ```bash # โœ… 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 ```typescript // โœ… 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