✨ 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!
19 KiB
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