feat: Add environment configuration, ESLint setup, GitHub Actions deployment workflow, comprehensive API documentation, Jest testing configuration, and optimized Vite setup; implement AI model lazy loading and SEO components for improved performance and user experience
This commit is contained in:
28
.env.public.example
Normal file
28
.env.public.example
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
# Public Environment Variables Only
|
||||||
|
# This file can be committed to version control
|
||||||
|
|
||||||
|
# Application Info
|
||||||
|
VITE_APP_NAME="Miracles In Motion"
|
||||||
|
VITE_APP_VERSION="3.0.0"
|
||||||
|
VITE_APP_ENV="production"
|
||||||
|
|
||||||
|
# Public URLs
|
||||||
|
VITE_API_BASE_URL="https://api.miraclesinmotion.org"
|
||||||
|
VITE_CDN_URL="https://cdn.miraclesinmotion.org"
|
||||||
|
|
||||||
|
# Feature Flags
|
||||||
|
VITE_ENABLE_AI_ASSISTANCE="true"
|
||||||
|
VITE_ENABLE_ANALYTICS="true"
|
||||||
|
VITE_ENABLE_MOBILE_APP="true"
|
||||||
|
VITE_ENABLE_TRAINING="true"
|
||||||
|
|
||||||
|
# Performance Settings
|
||||||
|
VITE_AI_BATCH_SIZE="5"
|
||||||
|
VITE_CACHE_TIMEOUT="300000"
|
||||||
|
VITE_REQUEST_TIMEOUT="30000"
|
||||||
|
|
||||||
|
# Note: Sensitive data should be managed through:
|
||||||
|
# - Server-side environment variables
|
||||||
|
# - Secure credential management systems
|
||||||
|
# - Runtime configuration APIs
|
||||||
|
# Never commit API keys, passwords, or tokens to version control
|
||||||
32
.eslintrc.recommended.json
Normal file
32
.eslintrc.recommended.json
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"root": true,
|
||||||
|
"env": { "browser": true, "es2020": true },
|
||||||
|
"extends": [
|
||||||
|
"eslint:recommended",
|
||||||
|
"@typescript-eslint/recommended",
|
||||||
|
"plugin:react-hooks/recommended",
|
||||||
|
"plugin:react/recommended",
|
||||||
|
"plugin:jsx-a11y/recommended"
|
||||||
|
],
|
||||||
|
"ignorePatterns": ["dist", ".eslintrc.cjs"],
|
||||||
|
"parser": "@typescript-eslint/parser",
|
||||||
|
"plugins": ["react-refresh", "jsx-a11y"],
|
||||||
|
"rules": {
|
||||||
|
"react-refresh/only-export-components": [
|
||||||
|
"warn",
|
||||||
|
{ "allowConstantExport": true }
|
||||||
|
],
|
||||||
|
"react/react-in-jsx-scope": "off",
|
||||||
|
"react/prop-types": "off",
|
||||||
|
"@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }],
|
||||||
|
"@typescript-eslint/explicit-function-return-type": "warn",
|
||||||
|
"jsx-a11y/anchor-is-valid": "error",
|
||||||
|
"jsx-a11y/alt-text": "error",
|
||||||
|
"no-console": "warn"
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"react": {
|
||||||
|
"version": "detect"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
87
.github/workflows/deploy.yml
vendored
Normal file
87
.github/workflows/deploy.yml
vendored
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
name: Build and Deploy
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ main ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ main ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
node-version: '18'
|
||||||
|
cache: 'npm'
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: npm ci
|
||||||
|
|
||||||
|
- name: Run type checking
|
||||||
|
run: npm run type-check
|
||||||
|
|
||||||
|
- name: Run linting
|
||||||
|
run: npm run lint
|
||||||
|
|
||||||
|
- name: Run tests
|
||||||
|
run: npm run test:ci
|
||||||
|
|
||||||
|
- name: Security audit
|
||||||
|
run: npm audit --audit-level moderate
|
||||||
|
|
||||||
|
build:
|
||||||
|
needs: test
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
node-version: '18'
|
||||||
|
cache: 'npm'
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: npm ci
|
||||||
|
|
||||||
|
- name: Build application
|
||||||
|
run: npm run build
|
||||||
|
env:
|
||||||
|
VITE_APP_VERSION: ${{ github.sha }}
|
||||||
|
VITE_BUILD_TIME: ${{ github.event.head_commit.timestamp }}
|
||||||
|
|
||||||
|
- name: Analyze bundle size
|
||||||
|
run: npx bundlesize
|
||||||
|
|
||||||
|
- name: Upload build artifacts
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: dist
|
||||||
|
path: dist/
|
||||||
|
|
||||||
|
deploy:
|
||||||
|
needs: build
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: github.ref == 'refs/heads/main'
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Download build artifacts
|
||||||
|
uses: actions/download-artifact@v3
|
||||||
|
with:
|
||||||
|
name: dist
|
||||||
|
path: dist/
|
||||||
|
|
||||||
|
- name: Deploy to GitHub Pages
|
||||||
|
uses: peaceiris/actions-gh-pages@v3
|
||||||
|
with:
|
||||||
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
publish_dir: ./dist
|
||||||
|
cname: miraclesinmotion.org
|
||||||
76
docs/API.md
Normal file
76
docs/API.md
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
# API Documentation
|
||||||
|
|
||||||
|
## Student Assistance AI API
|
||||||
|
|
||||||
|
### Core Endpoints
|
||||||
|
|
||||||
|
#### `POST /api/student-requests`
|
||||||
|
Process new student assistance requests through AI matching engine.
|
||||||
|
|
||||||
|
**Request Body:**
|
||||||
|
```typescript
|
||||||
|
{
|
||||||
|
studentId: string
|
||||||
|
description: string
|
||||||
|
category: 'clothing' | 'supplies' | 'food' | 'transportation' | 'emergency'
|
||||||
|
urgency: 'low' | 'medium' | 'high' | 'critical'
|
||||||
|
constraints: {
|
||||||
|
maxBudget?: number
|
||||||
|
timeframe: string
|
||||||
|
geographic?: {
|
||||||
|
maxDistance: number
|
||||||
|
preferredAreas?: string[]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
```typescript
|
||||||
|
{
|
||||||
|
requestId: string
|
||||||
|
status: 'pending' | 'processing' | 'matched' | 'completed'
|
||||||
|
matches: MatchResult[]
|
||||||
|
estimatedCompletion: string
|
||||||
|
aiConfidence: number
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `GET /api/requests/{requestId}/status`
|
||||||
|
Get real-time status of a student request.
|
||||||
|
|
||||||
|
#### `POST /api/ai/feedback`
|
||||||
|
Submit feedback for AI model improvement.
|
||||||
|
|
||||||
|
**Request Body:**
|
||||||
|
```typescript
|
||||||
|
{
|
||||||
|
requestId: string
|
||||||
|
matchId: string
|
||||||
|
outcome: 'successful' | 'partial' | 'failed'
|
||||||
|
feedback: {
|
||||||
|
satisfactionScore: number (1-5)
|
||||||
|
issues?: string[]
|
||||||
|
improvements?: string[]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Error Handling
|
||||||
|
|
||||||
|
All API endpoints return errors in the following format:
|
||||||
|
```typescript
|
||||||
|
{
|
||||||
|
error: {
|
||||||
|
code: string
|
||||||
|
message: string
|
||||||
|
details?: any
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Common error codes:
|
||||||
|
- `INVALID_REQUEST`: Request format is incorrect
|
||||||
|
- `AI_MODEL_UNAVAILABLE`: AI service is temporarily unavailable
|
||||||
|
- `INSUFFICIENT_RESOURCES`: No matching resources found
|
||||||
|
- `RATE_LIMIT_EXCEEDED`: Too many requests from client
|
||||||
28
jest.config.js
Normal file
28
jest.config.js
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
// Jest Configuration for Testing
|
||||||
|
module.exports = {
|
||||||
|
preset: 'ts-jest',
|
||||||
|
testEnvironment: 'jsdom',
|
||||||
|
setupFilesAfterEnv: ['<rootDir>/src/test/setup.ts'],
|
||||||
|
moduleNameMapping: {
|
||||||
|
'^@/(.*)$': '<rootDir>/src/$1',
|
||||||
|
'\\.(css|less|scss)$': 'identity-obj-proxy'
|
||||||
|
},
|
||||||
|
collectCoverageFrom: [
|
||||||
|
'src/**/*.{ts,tsx}',
|
||||||
|
'!src/**/*.d.ts',
|
||||||
|
'!src/test/**/*',
|
||||||
|
'!src/main.tsx'
|
||||||
|
],
|
||||||
|
coverageThreshold: {
|
||||||
|
global: {
|
||||||
|
branches: 70,
|
||||||
|
functions: 70,
|
||||||
|
lines: 70,
|
||||||
|
statements: 70
|
||||||
|
}
|
||||||
|
},
|
||||||
|
testMatch: [
|
||||||
|
'<rootDir>/src/**/__tests__/**/*.{ts,tsx}',
|
||||||
|
'<rootDir>/src/**/*.{test,spec}.{ts,tsx}'
|
||||||
|
]
|
||||||
|
}
|
||||||
91
package.recommended.json
Normal file
91
package.recommended.json
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
{
|
||||||
|
"name": "miracles-in-motion-web",
|
||||||
|
"private": true,
|
||||||
|
"version": "1.0.0",
|
||||||
|
"type": "module",
|
||||||
|
"description": "Public website for Miracles In Motion 501(c)3 non-profit organization",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "tsc && vite build",
|
||||||
|
"build:analyze": "npm run build && npx vite-bundle-analyzer dist",
|
||||||
|
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
||||||
|
"lint:fix": "eslint . --ext ts,tsx --fix",
|
||||||
|
"type-check": "tsc --noEmit",
|
||||||
|
"preview": "vite preview",
|
||||||
|
"test": "jest",
|
||||||
|
"test:watch": "jest --watch",
|
||||||
|
"test:ci": "jest --ci --coverage --watchAll=false",
|
||||||
|
"deploy": "npm run build && gh-pages -d dist",
|
||||||
|
"audit:security": "npm audit --audit-level moderate",
|
||||||
|
"audit:bundle": "npx bundlesize"
|
||||||
|
},
|
||||||
|
"bundlesize": [
|
||||||
|
{
|
||||||
|
"path": "./dist/assets/*.js",
|
||||||
|
"maxSize": "500kb"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"keywords": [
|
||||||
|
"non-profit",
|
||||||
|
"charity",
|
||||||
|
"501c3",
|
||||||
|
"miracles-in-motion",
|
||||||
|
"community",
|
||||||
|
"donations",
|
||||||
|
"volunteers",
|
||||||
|
"react",
|
||||||
|
"vite",
|
||||||
|
"tailwind"
|
||||||
|
],
|
||||||
|
"author": "Miracles In Motion",
|
||||||
|
"license": "MIT",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/Miracles-In-Motion/public-web.git"
|
||||||
|
},
|
||||||
|
"homepage": "https://miraclesinmotion.org",
|
||||||
|
"dependencies": {
|
||||||
|
"@tensorflow/tfjs": "^4.22.0",
|
||||||
|
"bull": "^4.16.5",
|
||||||
|
"compromise": "^14.14.4",
|
||||||
|
"date-fns": "^4.1.0",
|
||||||
|
"framer-motion": "^10.16.16",
|
||||||
|
"ioredis": "^5.8.0",
|
||||||
|
"lucide-react": "^0.290.0",
|
||||||
|
"ml-matrix": "^6.12.1",
|
||||||
|
"natural": "^8.1.0",
|
||||||
|
"react": "^18.2.0",
|
||||||
|
"react-dom": "^18.2.0",
|
||||||
|
"redis": "^5.8.3",
|
||||||
|
"socket.io-client": "^4.8.1",
|
||||||
|
"uuid": "^13.0.0",
|
||||||
|
"ws": "^8.18.3",
|
||||||
|
"react-helmet-async": "^1.3.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@tailwindcss/typography": "^0.5.10",
|
||||||
|
"@types/react": "^18.2.37",
|
||||||
|
"@types/react-dom": "^18.2.15",
|
||||||
|
"@types/jest": "^29.5.7",
|
||||||
|
"@testing-library/react": "^13.4.0",
|
||||||
|
"@testing-library/jest-dom": "^6.1.4",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^6.10.0",
|
||||||
|
"@typescript-eslint/parser": "^6.10.0",
|
||||||
|
"@vitejs/plugin-react": "^4.1.0",
|
||||||
|
"autoprefixer": "^10.4.16",
|
||||||
|
"bundlesize": "^0.18.1",
|
||||||
|
"eslint": "^8.53.0",
|
||||||
|
"eslint-plugin-react-hooks": "^4.6.0",
|
||||||
|
"eslint-plugin-react-refresh": "^0.4.4",
|
||||||
|
"eslint-plugin-jsx-a11y": "^6.8.0",
|
||||||
|
"gh-pages": "^6.0.0",
|
||||||
|
"jest": "^29.7.0",
|
||||||
|
"jest-environment-jsdom": "^29.7.0",
|
||||||
|
"postcss": "^8.4.31",
|
||||||
|
"tailwindcss": "^3.3.5",
|
||||||
|
"ts-jest": "^29.1.1",
|
||||||
|
"typescript": "^5.2.2",
|
||||||
|
"vite": "^4.5.0",
|
||||||
|
"vite-bundle-analyzer": "^0.7.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
69
src/ai/OptimizedStudentAssistanceAI.ts
Normal file
69
src/ai/OptimizedStudentAssistanceAI.ts
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
// AI Model Lazy Loading Implementation
|
||||||
|
export class OptimizedStudentAssistanceAI {
|
||||||
|
private static models: Map<string, any> = new Map()
|
||||||
|
private static modelUrls = {
|
||||||
|
'text-vectorization': '/models/text-vectorizer.json',
|
||||||
|
'matching-engine': '/models/matcher.json',
|
||||||
|
'priority-classifier': '/models/priority.json'
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lazy load models on demand
|
||||||
|
private static async loadModel(modelType: string) {
|
||||||
|
if (this.models.has(modelType)) {
|
||||||
|
return this.models.get(modelType)
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
console.log(`🤖 Loading AI model: ${modelType}`)
|
||||||
|
const model = await tf.loadLayersModel(this.modelUrls[modelType])
|
||||||
|
this.models.set(modelType, model)
|
||||||
|
console.log(`✅ Model ${modelType} loaded successfully`)
|
||||||
|
return model
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`❌ Failed to load model ${modelType}:`, error)
|
||||||
|
// Fallback to rule-based system
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Preload critical models in background
|
||||||
|
static async preloadCriticalModels() {
|
||||||
|
try {
|
||||||
|
// Load text vectorization model first (most commonly used)
|
||||||
|
await this.loadModel('text-vectorization')
|
||||||
|
|
||||||
|
// Load others in background
|
||||||
|
setTimeout(() => this.loadModel('matching-engine'), 2000)
|
||||||
|
setTimeout(() => this.loadModel('priority-classifier'), 4000)
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('Background model preloading failed:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async processRequest(request: StudentRequest): Promise<MatchResult[]> {
|
||||||
|
// Load models as needed
|
||||||
|
const textModel = await OptimizedStudentAssistanceAI.loadModel('text-vectorization')
|
||||||
|
const matchingModel = await OptimizedStudentAssistanceAI.loadModel('matching-engine')
|
||||||
|
|
||||||
|
// Process with loaded models or fallback to rule-based
|
||||||
|
if (textModel && matchingModel) {
|
||||||
|
return this.aiBasedMatching(request, textModel, matchingModel)
|
||||||
|
} else {
|
||||||
|
return this.ruleBasedMatching(request)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async aiBasedMatching(request: StudentRequest, textModel: any, matchingModel: any): Promise<MatchResult[]> {
|
||||||
|
// AI-powered matching logic
|
||||||
|
console.log('🤖 Using AI-powered matching')
|
||||||
|
// ... implementation
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
private async ruleBasedMatching(request: StudentRequest): Promise<MatchResult[]> {
|
||||||
|
// Fallback rule-based matching
|
||||||
|
console.log('📏 Using rule-based matching (fallback)')
|
||||||
|
// ... implementation
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
}
|
||||||
69
src/components/SEO/index.tsx
Normal file
69
src/components/SEO/index.tsx
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { Helmet } from 'react-helmet-async'
|
||||||
|
|
||||||
|
interface SEOProps {
|
||||||
|
title?: string
|
||||||
|
description?: string
|
||||||
|
keywords?: string[]
|
||||||
|
image?: string
|
||||||
|
url?: string
|
||||||
|
type?: 'website' | 'article' | 'organization'
|
||||||
|
}
|
||||||
|
|
||||||
|
export const SEO: React.FC<SEOProps> = ({
|
||||||
|
title = 'Miracles In Motion - Supporting Students in Need',
|
||||||
|
description = 'A 501(c)3 non-profit providing school supplies, clothing, and emergency assistance to students and families in need.',
|
||||||
|
keywords = ['nonprofit', 'charity', '501c3', 'student assistance', 'school supplies', 'donations'],
|
||||||
|
image = 'https://miraclesinmotion.org/og-image.png',
|
||||||
|
url = 'https://miraclesinmotion.org',
|
||||||
|
type = 'website'
|
||||||
|
}) => {
|
||||||
|
const structuredData = {
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@type": "Organization",
|
||||||
|
"name": "Miracles In Motion",
|
||||||
|
"description": description,
|
||||||
|
"url": url,
|
||||||
|
"logo": "https://miraclesinmotion.org/logo.png",
|
||||||
|
"contactPoint": {
|
||||||
|
"@type": "ContactPoint",
|
||||||
|
"telephone": "+1-555-123-4567",
|
||||||
|
"contactType": "Customer Service"
|
||||||
|
},
|
||||||
|
"sameAs": [
|
||||||
|
"https://facebook.com/miraclesinmotion",
|
||||||
|
"https://instagram.com/miraclesinmotion"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Helmet>
|
||||||
|
<title>{title}</title>
|
||||||
|
<meta name="description" content={description} />
|
||||||
|
<meta name="keywords" content={keywords.join(', ')} />
|
||||||
|
|
||||||
|
{/* Open Graph */}
|
||||||
|
<meta property="og:title" content={title} />
|
||||||
|
<meta property="og:description" content={description} />
|
||||||
|
<meta property="og:image" content={image} />
|
||||||
|
<meta property="og:url" content={url} />
|
||||||
|
<meta property="og:type" content={type} />
|
||||||
|
|
||||||
|
{/* Twitter Card */}
|
||||||
|
<meta name="twitter:card" content="summary_large_image" />
|
||||||
|
<meta name="twitter:title" content={title} />
|
||||||
|
<meta name="twitter:description" content={description} />
|
||||||
|
<meta name="twitter:image" content={image} />
|
||||||
|
|
||||||
|
{/* Structured Data */}
|
||||||
|
<script type="application/ld+json">
|
||||||
|
{JSON.stringify(structuredData)}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{/* Additional Meta Tags */}
|
||||||
|
<meta name="robots" content="index, follow" />
|
||||||
|
<meta name="author" content="Miracles In Motion" />
|
||||||
|
<link rel="canonical" href={url} />
|
||||||
|
</Helmet>
|
||||||
|
)
|
||||||
|
}
|
||||||
42
src/pages/DonatePage/index.tsx
Normal file
42
src/pages/DonatePage/index.tsx
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
// Example: Modular Page Structure
|
||||||
|
import React from 'react'
|
||||||
|
import { SEOHead } from '@/components/SEO'
|
||||||
|
import { PageShell } from '@/components/Layout'
|
||||||
|
import { DonationForm } from './DonationForm'
|
||||||
|
import { ImpactCalculator } from './ImpactCalculator'
|
||||||
|
import { DonationTiers } from './DonationTiers'
|
||||||
|
import { Heart } from 'lucide-react'
|
||||||
|
|
||||||
|
interface DonatePageProps {
|
||||||
|
className?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DonatePage: React.FC<DonatePageProps> = ({ className }) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<SEOHead
|
||||||
|
title="Donate - Support Students in Need"
|
||||||
|
description="Your donation directly supports students with school supplies, clothing, and emergency assistance."
|
||||||
|
/>
|
||||||
|
|
||||||
|
<PageShell
|
||||||
|
title="Make a Difference Today"
|
||||||
|
icon={Heart}
|
||||||
|
eyebrow="Every dollar counts"
|
||||||
|
className={className}
|
||||||
|
>
|
||||||
|
<div className="grid gap-8 lg:grid-cols-3">
|
||||||
|
<div className="lg:col-span-2 space-y-8">
|
||||||
|
<DonationTiers />
|
||||||
|
<DonationForm />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-6">
|
||||||
|
<ImpactCalculator />
|
||||||
|
{/* Add trust badges, testimonials, etc. */}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</PageShell>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
61
vite.config.optimized.ts
Normal file
61
vite.config.optimized.ts
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
import { defineConfig } from 'vite'
|
||||||
|
import react from '@vitejs/plugin-react'
|
||||||
|
import { resolve } from 'path'
|
||||||
|
|
||||||
|
// Optimized Vite Configuration
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [react()],
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
'@': resolve(__dirname, './src'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
server: {
|
||||||
|
port: 3000,
|
||||||
|
open: true,
|
||||||
|
},
|
||||||
|
build: {
|
||||||
|
outDir: 'dist',
|
||||||
|
sourcemap: true,
|
||||||
|
rollupOptions: {
|
||||||
|
output: {
|
||||||
|
manualChunks: {
|
||||||
|
// Core React libraries
|
||||||
|
vendor: ['react', 'react-dom'],
|
||||||
|
|
||||||
|
// UI and animations
|
||||||
|
ui: ['framer-motion', 'lucide-react'],
|
||||||
|
|
||||||
|
// AI and ML libraries (largest chunk)
|
||||||
|
ai: ['@tensorflow/tfjs', 'natural', 'ml-matrix', 'compromise'],
|
||||||
|
|
||||||
|
// Real-time and networking
|
||||||
|
realtime: ['socket.io-client', 'ws', 'ioredis', 'redis'],
|
||||||
|
|
||||||
|
// Queue management
|
||||||
|
queue: ['bull', 'uuid'],
|
||||||
|
|
||||||
|
// Date utilities
|
||||||
|
utils: ['date-fns']
|
||||||
|
},
|
||||||
|
// Optimize chunk sizes
|
||||||
|
chunkFileNames: (chunkInfo) => {
|
||||||
|
const facadeModuleId = chunkInfo.facadeModuleId
|
||||||
|
? chunkInfo.facadeModuleId.split('/').pop()
|
||||||
|
: 'chunk'
|
||||||
|
return `js/${facadeModuleId}-[hash].js`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// Enable compression
|
||||||
|
minify: 'terser',
|
||||||
|
terserOptions: {
|
||||||
|
compress: {
|
||||||
|
drop_console: true,
|
||||||
|
drop_debugger: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// Optimize chunks
|
||||||
|
chunkSizeWarningLimit: 500,
|
||||||
|
},
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user