Function bodies 182 total
POST function · typescript · L4-L37 (34 LOC)app/api/ai/auto-fill/route.ts
export async function POST(request: NextRequest) {
try {
const body = await request.json()
const { applicationData, documents, locale = 'en' } = body
// Simulate AI processing delay
await new Promise(resolve => setTimeout(resolve, 1200))
const isArabic = locale === 'ar'
// Generate suggestions based on documents and partial data
const suggestions = generateSuggestions(applicationData, documents, isArabic)
const hasDocuments = documents && documents.length > 0
return NextResponse.json({
success: true,
suggestions,
message: isArabic
? hasDocuments
? 'تم تحليل المستندات وإنشاء الاقتراحات بنجاح'
: 'تم إنشاء اقتراحات بناءً على معلومات القطاع والمؤسسة'
: hasDocuments
? 'Documents analyzed and suggestions generated successfully'
: 'Suggestions generated based on sector and organization information'
})
} catch (error) {
console.error('Auto-fill API error:', error)
generateSuggestions function · typescript · L39-L122 (84 LOC)app/api/ai/auto-fill/route.ts
function generateSuggestions(
applicationData: Record<string, unknown>,
documents: Array<{ type: string; fileName: string; ocrText?: string }>,
isArabic: boolean
) {
// Mock suggestions - in production, this would use AI to extract from documents
const hasPolicy = documents?.some(d =>
d.type === 'ESG_POLICY' || d.fileName?.toLowerCase().includes('policy')
)
const hasReport = documents?.some(d =>
d.type === 'REPORT' || d.fileName?.toLowerCase().includes('report')
)
const hasTradeLicense = documents?.some(d =>
d.fileName?.toLowerCase().includes('license') || d.fileName?.toLowerCase().includes('trade')
)
const suggestions: Record<string, string> = {}
// Only suggest values for empty fields
if (!applicationData?.applicantName) {
suggestions.applicantName = isArabic ? 'أحمد محمد العلي' : 'Ahmed Mohamed Al-Ali'
}
if (!applicationData?.organizationName) {
suggestions.organizationName = isArabic
? 'شركة الحلول المستدامة ذ.م.م'
: POST function · typescript · L9-L38 (30 LOC)app/api/ai/chat/route.ts
export async function POST(request: NextRequest) {
try {
const body = await request.json()
const { message, conversationHistory = [], locale = 'en' } = body
if (!message) {
return NextResponse.json(
{ error: 'message is required' },
{ status: 400 }
)
}
// Simulate AI processing delay
await new Promise(resolve => setTimeout(resolve, 800))
const response = generateAIResponse(message.toLowerCase(), locale, conversationHistory)
return NextResponse.json({
success: true,
response,
suggestedActions: getSuggestedActions(message.toLowerCase(), locale),
})
} catch (error) {
console.error('Chat API error:', error)
return NextResponse.json(
{ error: 'Failed to process chat message' },
{ status: 500 }
)
}
}generateAIResponse function · typescript · L40-L263 (224 LOC)app/api/ai/chat/route.ts
function generateAIResponse(message: string, locale: string, history: ChatMessage[]): string {
const isArabic = locale === 'ar'
// ESG Certificate related queries
if (message.includes('esg') || message.includes('certificate') || message.includes('شهادة') || message.includes('استدامة')) {
if (message.includes('how') || message.includes('apply') || message.includes('كيف') || message.includes('تقديم')) {
return isArabic
? `للحصول على شهادة ESG، اتبع الخطوات التالية:
1. **انتقل إلى صفحة الخدمات** وابحث عن "شهادة ESG"
2. **انقر على "بدء الطلب"** لفتح نموذج الطلب
3. **أكمل الخطوات الأربع:**
- معلومات مقدم الطلب والمؤسسة
- ملف ESG (البيئة، الاجتماعية، الحوكمة)
- تحميل المستندات الداعمة
- مراجعة وإرسال الطلب
هل تريد أن أوجهك إلى صفحة تقديم الطلب؟`
: `To obtain an ESG Certificate, follow these steps:
1. **Go to the Services page** and search for "ESG Certificate"
2. **Click "Start Application"** to open the application form
3. **Complete the 4 stePOST function · typescript · L3-L49 (47 LOC)app/api/ai/comment/route.ts
export async function POST(request: NextRequest) {
try {
const body = await request.json()
const { description, sector, organizationName, type } = body
// Simulate AI processing time
await new Promise((resolve) => setTimeout(resolve, 1000))
// type can be 'corrections', 'approval', or 'rejection'
let comment = ''
if (type === 'corrections') {
const correctionComments = [
`Thank you for your ESG certification application. After careful review, we require the following additional information before proceeding:\n\n1. Please provide specific quantitative data for your environmental impact metrics (e.g., carbon emissions in tonnes CO2e, water usage in liters, waste diversion rates).\n\n2. Include documentation or evidence supporting your sustainability claims.\n\n3. Elaborate on your governance structure and ESG oversight mechanisms.\n\n4. Add details about stakeholder engagement and reporting practices.\n\nPlease update your application with tPOST function · typescript · L4-L39 (36 LOC)app/api/ai/compliance-score/route.ts
export async function POST(request: NextRequest) {
try {
const body = await request.json()
const {
environmentalProfile,
socialProfile,
governanceProfile,
sector,
organizationName,
locale = 'en'
} = body
// Simulate AI processing delay
await new Promise(resolve => setTimeout(resolve, 800))
const isArabic = locale === 'ar'
const result = calculateComplianceScore(
environmentalProfile,
socialProfile,
governanceProfile,
sector,
isArabic
)
return NextResponse.json({
success: true,
...result
})
} catch (error) {
console.error('Compliance score API error:', error)
return NextResponse.json(
{ error: 'Failed to calculate compliance score' },
{ status: 500 }
)
}
}calculateComplianceScore function · typescript · L41-L169 (129 LOC)app/api/ai/compliance-score/route.ts
function calculateComplianceScore(
environmentalProfile: string | null,
socialProfile: string | null,
governanceProfile: string | null,
sector: string | null,
isArabic: boolean
) {
let score = 0
const hints: string[] = []
const strengths: string[] = []
// Parse profiles
let envData: Record<string, string> = {}
let socData: Record<string, string> = {}
let govData: Record<string, string> = {}
try {
if (environmentalProfile) envData = JSON.parse(environmentalProfile)
if (socialProfile) socData = JSON.parse(socialProfile)
if (governanceProfile) govData = JSON.parse(governanceProfile)
} catch {
// Invalid JSON, continue with empty objects
}
// Environmental scoring (max 35 points)
let envScore = 0
if (envData.description && envData.description.length > 50) {
envScore += 15
strengths.push(isArabic ? 'وصف بيئي شامل' : 'Comprehensive environmental description')
} else {
hints.push(isArabic
? 'أضف وصفاً مفصلاً للمبادرات Repobility's GitHub App fixes findings like these · https://github.com/apps/repobility-bot
POST function · typescript · L4-L26 (23 LOC)app/api/ai/document-classifier/route.ts
export async function POST(request: NextRequest) {
try {
const body = await request.json()
const { fileName, ocrText, userSelectedType, locale = 'en', documentIndex = 0 } = body
// Simulate AI processing delay
await new Promise(resolve => setTimeout(resolve, 600))
const isArabic = locale === 'ar'
const result = classifyDocument(fileName, ocrText, isArabic, documentIndex)
return NextResponse.json({
success: true,
...result
})
} catch (error) {
console.error('Document classifier API error:', error)
return NextResponse.json(
{ error: 'Failed to classify document' },
{ status: 500 }
)
}
}classifyDocument function · typescript · L35-L110 (76 LOC)app/api/ai/document-classifier/route.ts
function classifyDocument(
fileName: string,
ocrText: string | null,
isArabic: boolean,
documentIndex: number
): ClassificationResult {
// Define document types to cycle through for demo purposes
const documentTypes = [
{
type: 'ESG_POLICY',
labelEn: 'ESG Policy',
labelAr: 'سياسة ESG',
confidence: 0.90,
reasoningEn: 'Document contains organizational sustainability policies and commitments',
reasoningAr: 'يحتوي المستند على سياسات الاستدامة والالتزامات المؤسسية'
},
{
type: 'SUSTAINABILITY_REPORT',
labelEn: 'Sustainability Report',
labelAr: 'تقرير الاستدامة',
confidence: 0.88,
reasoningEn: 'Annual sustainability performance metrics and achievements identified',
reasoningAr: 'تم تحديد مقاييس الأداء السنوية للاستدامة والإنجازات'
},
{
type: 'GOVERNANCE_POLICY',
labelEn: 'Governance Policy',
labelAr: 'سياسة الحوكمة',
confidence: 0.92,
reasoningEn: 'Corporate goPOST function · typescript · L4-L31 (28 LOC)app/api/ai/draft-certificate/route.ts
export async function POST(request: NextRequest) {
try {
const body = await request.json()
const {
applicationData,
complianceScore,
locale = 'en'
} = body
// Simulate AI processing delay
await new Promise(resolve => setTimeout(resolve, 800))
const isArabic = locale === 'ar'
const certificateText = generateDraftCertificate(applicationData, complianceScore, isArabic)
return NextResponse.json({
success: true,
certificateText,
generatedAt: new Date().toISOString()
})
} catch (error) {
console.error('Draft certificate API error:', error)
return NextResponse.json(
{ error: 'Failed to generate draft certificate' },
{ status: 500 }
)
}
}generateDraftCertificate function · typescript · L33-L156 (124 LOC)app/api/ai/draft-certificate/route.ts
function generateDraftCertificate(
applicationData: Record<string, unknown>,
complianceScore: number | null,
isArabic: boolean
): string {
const orgName = applicationData?.organizationName || (isArabic ? 'المؤسسة' : 'Organization')
const sector = applicationData?.sector || (isArabic ? 'القطاع' : 'Sector')
const applicantName = applicationData?.applicantName || (isArabic ? 'مقدم الطلب' : 'Applicant')
const currentDate = new Date()
const validUntil = new Date(currentDate)
validUntil.setFullYear(validUntil.getFullYear() + 1)
const formatDate = (date: Date) => {
if (isArabic) {
return date.toLocaleDateString('ar-AE', { year: 'numeric', month: 'long', day: 'numeric' })
}
return date.toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' })
}
// Determine ESG level based on score
let esgLevel: string
if (complianceScore && complianceScore >= 80) {
esgLevel = isArabic ? 'المستوى الذهبي' : 'Gold Level'
} else if (compPOST function · typescript · L4-L31 (28 LOC)app/api/ai/draft-report/route.ts
export async function POST(request: NextRequest) {
try {
const body = await request.json()
const {
applicationData,
documents,
locale = 'en'
} = body
// Simulate AI processing delay
await new Promise(resolve => setTimeout(resolve, 1500))
const isArabic = locale === 'ar'
const report = generateDraftReport(applicationData, documents, isArabic)
return NextResponse.json({
success: true,
report,
generatedAt: new Date().toISOString()
})
} catch (error) {
console.error('Draft report API error:', error)
return NextResponse.json(
{ error: 'Failed to generate draft report' },
{ status: 500 }
)
}
}generateDraftReport function · typescript · L33-L191 (159 LOC)app/api/ai/draft-report/route.ts
function generateDraftReport(
applicationData: Record<string, unknown>,
documents: Array<{ type: string; fileName: string }>,
isArabic: boolean
): string {
const orgName = applicationData?.organizationName || (isArabic ? 'المؤسسة' : 'Organization')
const sector = applicationData?.sector || (isArabic ? 'القطاع' : 'Sector')
// Parse ESG profiles
let envData: Record<string, string> = {}
let socData: Record<string, string> = {}
let govData: Record<string, string> = {}
try {
if (applicationData?.environmentalProfile) {
envData = JSON.parse(applicationData.environmentalProfile as string)
}
if (applicationData?.socialProfile) {
socData = JSON.parse(applicationData.socialProfile as string)
}
if (applicationData?.governanceProfile) {
govData = JSON.parse(applicationData.governanceProfile as string)
}
} catch {
// Continue with empty objects
}
const documentTypes = documents?.map(d => d.type).join(', ') || 'N/A'
if (isPOST function · typescript · L4-L33 (30 LOC)app/api/ai/esg-hints/route.ts
export async function POST(request: NextRequest) {
try {
const body = await request.json()
const { profile, type } = body // type: 'environmental' | 'social' | 'governance'
if (!type) {
return NextResponse.json(
{ error: 'type is required (environmental, social, or governance)' },
{ status: 400 }
)
}
// Simulate AI processing delay
await new Promise(resolve => setTimeout(resolve, 1200))
const hints = generateHints(type, profile)
return NextResponse.json({
success: true,
type,
hints,
})
} catch (error) {
console.error('ESG Hints error:', error)
return NextResponse.json(
{ error: 'Failed to generate ESG hints' },
{ status: 500 }
)
}
}generateHints function · typescript · L35-L40 (6 LOC)app/api/ai/esg-hints/route.ts
function generateHints(type: string, profile?: { description?: string }): {
suggestions: string[]
missingAreas: string[]
improvementTips: string[]
sampleKpis: string[]
} {Want this analysis on your repo? https://repobility.com/scan/
POST function · typescript · L4-L200 (197 LOC)app/api/ai/ocr-review/route.ts
export async function POST(request: NextRequest) {
try {
const body = await request.json()
const { fileName, documentType } = body
if (!fileName || !documentType) {
return NextResponse.json(
{ error: 'fileName and documentType are required' },
{ status: 400 }
)
}
// Simulate OCR processing delay
await new Promise(resolve => setTimeout(resolve, 1500))
// Generate mock OCR text based on document type
const ocrTexts: Record<string, string> = {
ESG_POLICY: `ENVIRONMENTAL, SOCIAL & GOVERNANCE POLICY
Effective Date: January 2024
Organization: [Extracted Organization Name]
1. ENVIRONMENTAL COMMITMENT
We are committed to reducing our environmental footprint through:
- Carbon neutrality targets by 2030
- 100% renewable energy usage
- Zero-waste manufacturing processes
2. SOCIAL RESPONSIBILITY
Our social initiatives include:
- Diversity and inclusion programs
- Community engagement partnerships
- Employee wellness programsgetSuggestedTags function · typescript · L202-L213 (12 LOC)app/api/ai/ocr-review/route.ts
function getSuggestedTags(documentType: string): string[] {
const tags: Record<string, string[]> = {
ESG_POLICY: ['policy', 'commitment', 'strategy', 'targets'],
SUSTAINABILITY_REPORT: ['annual-report', 'gri', 'metrics', 'disclosure'],
CARBON_AUDIT: ['emissions', 'carbon', 'audit', 'ghg'],
ISO_CERTIFICATE: ['iso', 'certification', 'standards', 'compliance'],
FINANCIAL_REPORT: ['financial', 'investment', 'esg-disclosure'],
GOVERNANCE_CHARTER: ['governance', 'board', 'committee', 'independence'],
OTHER: ['supporting', 'documentation'],
}
return tags[documentType] || tags.OTHER
}POST function · typescript · L12-L53 (42 LOC)app/api/ai/precheck/route.ts
export async function POST(request: NextRequest) {
try {
const body = await request.json()
const { description, sector, organizationName } = body
// Simulate AI processing time
await new Promise((resolve) => setTimeout(resolve, 1500))
// Generate a contextual response
let response = mockResponses[Math.floor(Math.random() * mockResponses.length)]
// Add sector-specific suggestions
if (sector) {
const sectorSuggestions: Record<string, string> = {
Energy: " For the energy sector, consider highlighting renewable energy adoption rates and grid decarbonization efforts.",
Manufacturing: " As a manufacturing company, include details on circular economy practices and sustainable sourcing.",
Construction: " For construction, emphasize green building certifications (LEED, BREEAM) and sustainable materials usage.",
Agriculture: " Agricultural operations should detail organic certification status and water conservation measuPOST function · typescript · L4-L31 (28 LOC)app/api/ai/renewal-forecast/route.ts
export async function POST(request: NextRequest) {
try {
const body = await request.json()
const {
sector,
organizationSize,
complianceScore,
locale = 'en'
} = body
// Simulate AI processing delay
await new Promise(resolve => setTimeout(resolve, 500))
const isArabic = locale === 'ar'
const forecast = calculateRenewalForecast(sector, organizationSize, complianceScore, isArabic)
return NextResponse.json({
success: true,
...forecast
})
} catch (error) {
console.error('Renewal forecast API error:', error)
return NextResponse.json(
{ error: 'Failed to calculate renewal forecast' },
{ status: 500 }
)
}
}calculateRenewalForecast function · typescript · L33-L108 (76 LOC)app/api/ai/renewal-forecast/route.ts
function calculateRenewalForecast(
sector: string | null,
organizationSize: string | null,
complianceScore: number | null,
isArabic: boolean
) {
const now = new Date()
// Base validity: 12 months
let validityMonths = 12
// Adjust based on compliance score
if (complianceScore && complianceScore >= 85) {
validityMonths = 18 // High performers get extended validity
} else if (complianceScore && complianceScore < 50) {
validityMonths = 6 // Low performers need earlier re-evaluation
}
// Calculate dates
const validUntil = new Date(now)
validUntil.setMonth(validUntil.getMonth() + validityMonths)
const renewalSuggestedAt = new Date(validUntil)
renewalSuggestedAt.setDate(renewalSuggestedAt.getDate() - 90) // 90 days before expiry
const nextReviewAt = new Date(now)
nextReviewAt.setMonth(nextReviewAt.getMonth() + Math.floor(validityMonths / 2)) // Mid-term review
// Sector-specific recommendations
const sectorRecommendations: Record<string, {POST function · typescript · L5-L51 (47 LOC)app/api/ai/reviewer-assist/route.ts
export async function POST(request: NextRequest) {
try {
const body = await request.json()
const { applicationId, locale = 'en' } = body
if (!applicationId) {
return NextResponse.json(
{ error: 'Application ID is required' },
{ status: 400 }
)
}
// Fetch application with documents
const application = await prisma.application.findUnique({
where: { id: applicationId },
include: {
documents: true,
reviewNotes: true
}
})
if (!application) {
return NextResponse.json(
{ error: 'Application not found' },
{ status: 404 }
)
}
// Simulate AI processing delay
await new Promise(resolve => setTimeout(resolve, 1000))
const isArabic = locale === 'ar'
const analysis = analyzeApplication(application, isArabic)
return NextResponse.json({
success: true,
applicationId,
...analysis
})
} catch (error) {
console.error('Reviewer aanalyzeApplication function · typescript · L63-L229 (167 LOC)app/api/ai/reviewer-assist/route.ts
function analyzeApplication(
application: {
applicantName: string
organizationName: string
sector: string
description: string
environmentalProfile: string | null
socialProfile: string | null
governanceProfile: string | null
documents: Array<{ type: string; fileName: string; ocrText: string | null }>
reviewNotes: Array<{ note: string; authorType: string }>
},
isArabic: boolean
): ReviewerAnalysis {
const redFlags: string[] = []
const strengths: string[] = []
const extraDocumentsSuggested: string[] = []
// Parse ESG profiles
let envData: Record<string, string> = {}
let socData: Record<string, string> = {}
let govData: Record<string, string> = {}
try {
if (application.environmentalProfile) {
envData = JSON.parse(application.environmentalProfile)
}
if (application.socialProfile) {
socData = JSON.parse(application.socialProfile)
}
if (application.governanceProfile) {
govData = JSON.parse(applicgenerateDetailedNotes function · typescript · L231-L270 (40 LOC)app/api/ai/reviewer-assist/route.ts
function generateDetailedNotes(
application: { organizationName: string; sector: string },
redFlags: string[],
strengths: string[],
isArabic: boolean
): string {
if (isArabic) {
return `
## ملخص تحليل الطلب
**المؤسسة:** ${application.organizationName}
**القطاع:** ${application.sector}
### نقاط القوة (${strengths.length})
${strengths.map(s => `• ${s}`).join('\n') || '• لم يتم تحديد نقاط قوة محددة'}
### المخاوف (${redFlags.length})
${redFlags.map(r => `• ${r}`).join('\n') || '• لم يتم تحديد مخاوف'}
### ملاحظات المراجع
يرجى مراجعة الطلب بعناية والتحقق من جميع المستندات المقدمة. تأكد من أن جميع مؤشرات الأداء الرئيسية قابلة للقياس والتحقق منها.
`.trim()
}
return `
## Application Analysis Summary
**Organization:** ${application.organizationName}
**Sector:** ${application.sector}
### Strengths (${strengths.length})
${strengths.map(s => `• ${s}`).join('\n') || '• No specific strengths identified'}
### Concerns (${redFlags.length})
${redFlags.map(r => `• ${r}`).joiProvenance: Repobility (https://repobility.com) — every score reproducible from /scan/
POST function · typescript · L4-L34 (31 LOC)app/api/ai/sector-template/route.ts
export async function POST(request: NextRequest) {
try {
const body = await request.json()
const { sector, locale = 'en' } = body
if (!sector) {
return NextResponse.json(
{ error: 'Sector is required' },
{ status: 400 }
)
}
// Simulate AI processing delay
await new Promise(resolve => setTimeout(resolve, 400))
const isArabic = locale === 'ar'
const template = getSectorTemplate(sector, isArabic)
return NextResponse.json({
success: true,
sector,
...template
})
} catch (error) {
console.error('Sector template API error:', error)
return NextResponse.json(
{ error: 'Failed to fetch sector template' },
{ status: 500 }
)
}
}getSectorTemplate function · typescript · L46-L303 (258 LOC)app/api/ai/sector-template/route.ts
function getSectorTemplate(sector: string, isArabic: boolean): SectorTemplate {
const templates: Record<string, { en: SectorTemplate; ar: SectorTemplate }> = {
'Manufacturing': {
en: {
environmentalTemplate: 'For manufacturing, focus on emissions reduction, waste management, and resource efficiency. Key areas include energy consumption per unit, water usage optimization, and circular economy practices.',
environmentalExamples: [
'Reduce carbon emissions by 25% through energy-efficient machinery',
'Implement zero-waste-to-landfill program',
'Use 50% recycled materials in production',
'Install solar panels for 30% of energy needs'
],
socialTemplate: 'Manufacturing social responsibility centers on worker safety, fair labor practices, and community impact. Focus on workplace conditions and supply chain ethics.',
socialExamples: [
'Achieve zero workplace injuries through safety training',
matchServices function · typescript · L20-L118 (99 LOC)app/api/ai/service-match/route.ts
function matchServices(
query: string,
services: Array<{
id: number
name: string
nameAr: string
description: string
descriptionAr: string
tags: string | null
tagsAr: string | null
}>,
lang: 'en' | 'ar'
): MatchedService[] {
const queryLower = query.toLowerCase()
const queryWords = queryLower.split(/\s+/).filter(w => w.length > 2)
const scores: Array<{ serviceId: number; score: number }> = []
for (const service of services) {
let score = 0
const name = lang === 'ar' ? service.nameAr : service.name
const description = lang === 'ar' ? service.descriptionAr : service.description
const tagsStr = lang === 'ar' ? service.tagsAr : service.tags
const nameLower = name.toLowerCase()
const descLower = description.toLowerCase()
// Parse tags
let tags: string[] = []
if (tagsStr) {
try {
tags = JSON.parse(tagsStr)
} catch {
tags = []
}
}
// Exact name match (high score)
igenerateReasoningSummary function · typescript · L121-L137 (17 LOC)app/api/ai/service-match/route.ts
function generateReasoningSummary(
query: string,
matchedCount: number,
lang: 'en' | 'ar'
): string {
if (lang === 'ar') {
if (matchedCount === 0) {
return `لم أتمكن من العثور على خدمات تتطابق مع "${query}". يرجى المحاولة بكلمات مختلفة أو تصفح دليل الخدمات.`
}
return `بناءً على استفسارك "${query}"، قمت بتحليل الكلمات الرئيسية والسياق لتحديد الخدمات الأكثر صلة. وجدت ${matchedCount} خدمة/خدمات قد تلبي احتياجاتك. يتم ترتيب النتائج حسب درجة الثقة بناءً على مدى تطابقها مع طلبك.`
}
if (matchedCount === 0) {
return `I couldn't find any services matching "${query}". Please try different keywords or browse the service directory.`
}
return `Based on your query "${query}", I analyzed the keywords and context to identify the most relevant services. I found ${matchedCount} service(s) that may meet your needs. Results are ranked by confidence score based on how closely they match your request.`
}POST function · typescript · L139-L181 (43 LOC)app/api/ai/service-match/route.ts
export async function POST(request: NextRequest) {
try {
const body: ServiceMatchRequest = await request.json()
const { query, lang = 'en' } = body
if (!query || query.trim().length === 0) {
return NextResponse.json(
{ error: 'Query is required' },
{ status: 400 }
)
}
// Fetch all services
const services = await prisma.service.findMany({
select: {
id: true,
name: true,
nameAr: true,
description: true,
descriptionAr: true,
tags: true,
tagsAr: true,
},
})
// Match services
const matchedServices = matchServices(query, services, lang)
// Generate response
const response: ServiceMatchResponse = {
reasoningSummary: generateReasoningSummary(query, matchedServices.length, lang),
matchedServices,
}
return NextResponse.json(response)
} catch (error) {
console.error('Service match error:', error)
return NextResponse.json(
POST function · typescript · L5-L46 (42 LOC)app/api/ai/service-recommendations/route.ts
export async function POST(request: NextRequest) {
try {
const body = await request.json()
const {
sector,
organizationSize,
hasCompletedESG,
locale = 'en'
} = body
// Simulate AI processing delay
await new Promise(resolve => setTimeout(resolve, 600))
const isArabic = locale === 'ar'
// Get all services from database
const allServices = await prisma.service.findMany()
// Generate recommendations based on context
const recommendations = generateRecommendations(
allServices,
sector,
organizationSize,
hasCompletedESG,
isArabic
)
return NextResponse.json({
success: true,
recommendations,
reasoning: isArabic
? 'بناءً على قطاعك وحجم مؤسستك، نوصي بالخدمات التالية'
: 'Based on your sector and organization profile, we recommend these services'
})
} catch (error) {
console.error('Service recommendations API error:', error)
return NextResponsgenerateRecommendations function · typescript · L60-L184 (125 LOC)app/api/ai/service-recommendations/route.ts
function generateRecommendations(
services: Array<{
id: number
name: string
nameAr: string
description: string
descriptionAr: string
platform: string
dept: string
channelType: string
tags: string | null
}>,
sector: string | null,
organizationSize: string | null,
hasCompletedESG: boolean,
isArabic: boolean
): ServiceRecommendation[] {
const recommendations: ServiceRecommendation[] = []
// Define recommendation rules
const recommendationRules = [
{
namePattern: /certificate.*origin/i,
reason: 'Essential for international trade and export activities',
reasonAr: 'ضروري للتجارة الدولية وأنشطة التصدير',
relevance: 85,
sectors: ['Manufacturing', 'Trade', 'Logistics']
},
{
namePattern: /business.*matchmaking/i,
reason: 'Connect with potential partners and investors in your sector',
reasonAr: 'تواصل مع شركاء ومستثمرين محتملين في قطاعك',
relevance: 80,
sectors: null // APOST function · typescript · L3-L47 (45 LOC)app/api/ai/summary/route.ts
export async function POST(request: NextRequest) {
try {
const body = await request.json()
const { description, sector, organizationName, applicantName } = body
// Simulate AI processing time
await new Promise((resolve) => setTimeout(resolve, 2000))
const riskLevel = Math.random() > 0.7 ? 'HIGH' : Math.random() > 0.4 ? 'MEDIUM' : 'LOW'
const completenessScore = Math.floor(Math.random() * 30) + 70 // 70-100
// Generate summary based on input
const summaries = [
`**Application Summary for ${organizationName}**\n\nThis application from ${applicantName} in the ${sector} sector demonstrates a commitment to ESG principles. The organization has outlined various sustainability initiatives with measurable targets.\n\n**Key Strengths:**\n- Clear environmental objectives\n- Documented social responsibility programs\n- Established governance framework\n\n**Areas for Improvement:**\n- More specific quantitative metrics needed\n- Long-term sustainability roAll rows scored by the Repobility analyzer (https://repobility.com)
POST function · typescript · L4-L29 (26 LOC)app/api/applications/[id]/notes/route.ts
export async function POST(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const { id } = await params
const body = await request.json()
const { note, authorType = 'STAFF' } = body
const reviewNote = await prisma.reviewNote.create({
data: {
applicationId: id,
note,
authorType,
},
})
return NextResponse.json({ success: true, reviewNote })
} catch (error) {
console.error('Error creating note:', error)
return NextResponse.json(
{ success: false, error: 'Failed to create note' },
{ status: 500 }
)
}
}GET function · typescript · L4-L36 (33 LOC)app/api/applications/[id]/route.ts
export async function GET(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const { id } = await params
const application = await prisma.application.findUnique({
where: { id },
include: {
reviewNotes: {
orderBy: { createdAt: 'asc' },
},
certificate: true,
},
})
if (!application) {
return NextResponse.json(
{ success: false, error: 'Application not found' },
{ status: 404 }
)
}
return NextResponse.json({ success: true, application })
} catch (error) {
console.error('Error fetching application:', error)
return NextResponse.json(
{ success: false, error: 'Failed to fetch application' },
{ status: 500 }
)
}
}PUT function · typescript · L38-L76 (39 LOC)app/api/applications/[id]/route.ts
export async function PUT(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const { id } = await params
const body = await request.json()
const { description, aiPrecheckResult } = body
const application = await prisma.application.update({
where: { id },
data: {
description,
aiPrecheckResult,
status: 'SUBMITTED',
reviewNotes: {
create: {
authorType: 'SYSTEM',
note: 'Customer resubmitted application after corrections.',
},
},
},
include: {
reviewNotes: {
orderBy: { createdAt: 'asc' },
},
certificate: true,
},
})
return NextResponse.json({ success: true, application })
} catch (error) {
console.error('Error updating application:', error)
return NextResponse.json(
{ success: false, error: 'Failed to update application' },
{ status: 500 }
)
}
}generateCertificateNumber function · typescript · L4-L8 (5 LOC)app/api/applications/[id]/status/route.ts
function generateCertificateNumber(): string {
const year = new Date().getFullYear()
const random = Math.floor(Math.random() * 10000).toString().padStart(4, '0')
return `ESG-${year}-${random}`
}PUT function · typescript · L10-L70 (61 LOC)app/api/applications/[id]/status/route.ts
export async function PUT(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const { id } = await params
const body = await request.json()
const { status, note } = body
// Start with updating the application status
const updateData: {
status: string
reviewNotes?: { create: { authorType: string; note: string } }
certificate?: { create: { certificateNumber: string } }
} = {
status,
}
// Add a system note if provided
if (note) {
updateData.reviewNotes = {
create: {
authorType: 'STAFF',
note,
},
}
}
// If approving, create a certificate
if (status === 'APPROVED') {
updateData.certificate = {
create: {
certificateNumber: generateCertificateNumber(),
},
}
}
const application = await prisma.application.update({
where: { id },
data: updateData,
include: {
reviewNoteGET function · typescript · L4-L34 (31 LOC)app/api/applications/route.ts
export async function GET(request: NextRequest) {
try {
const { searchParams } = new URL(request.url)
const status = searchParams.get('status')
const sortBy = searchParams.get('sortBy') || 'createdAt'
const sortOrder = searchParams.get('sortOrder') || 'desc'
const where = status && status !== 'all' ? { status } : {}
const applications = await prisma.application.findMany({
where,
include: {
reviewNotes: {
orderBy: { createdAt: 'desc' },
},
certificate: true,
},
orderBy: {
[sortBy]: sortOrder,
},
})
return NextResponse.json({ success: true, applications })
} catch (error) {
console.error('Error fetching applications:', error)
return NextResponse.json(
{ success: false, error: 'Failed to fetch applications' },
{ status: 500 }
)
}
}POST function · typescript · L36-L70 (35 LOC)app/api/applications/route.ts
export async function POST(request: NextRequest) {
try {
const body = await request.json()
const { applicantName, organizationName, email, sector, description, aiPrecheckResult } = body
const application = await prisma.application.create({
data: {
applicantName,
organizationName,
email,
sector,
description,
aiPrecheckResult,
status: 'SUBMITTED',
reviewNotes: {
create: {
authorType: 'SYSTEM',
note: 'Application submitted for ESG certification review.',
},
},
},
include: {
reviewNotes: true,
},
})
return NextResponse.json({ success: true, application })
} catch (error) {
console.error('Error creating application:', error)
return NextResponse.json(
{ success: false, error: 'Failed to create application' },
{ status: 500 }
)
}
}POST function · typescript · L5-L171 (167 LOC)app/api/market-directory/setup-advisor/route.ts
export async function POST(request: NextRequest) {
try {
const body = await request.json()
const {
sector,
subSector,
companySize,
legalStructure,
budget,
priorities = [],
additionalContext,
locale = 'en',
} = body as {
sector: string
subSector?: string
companySize?: string
legalStructure?: string
budget?: string
priorities?: string[]
additionalContext?: string
locale?: string
}
// Simulate AI processing delay (2-3 seconds)
await new Promise((resolve) => setTimeout(resolve, 2200))
const isArabic = locale === 'ar'
// Find sector stats
const sectorStat = sectorStats.find((s) => s.sector === sector)
// Score each area based on input criteria
const scoredAreas = areaStats.map((area) => {
let score = 50 // base
// Sector match — if area's top sectors include the requested sector
if (area.topSectors.includes(sector)) {
scRepobility's GitHub App fixes findings like these · https://github.com/apps/repobility-bot
generateRationale function · typescript · L173-L185 (13 LOC)app/api/market-directory/setup-advisor/route.ts
function generateRationale(area: AreaStat, sector: string, isArabic: boolean): string {
const hasSector = area.topSectors.includes(sector)
if (isArabic) {
if (hasSector) {
return `${area.areaAr} تضم بالفعل مجتمعاً نشطاً في قطاع ${sector}، مما يوفر فرص تواصل وتعاون. مستوى التشبع ${area.saturationLevel === 'Low' ? 'المنخفض' : area.saturationLevel === 'Medium' ? 'المتوسط' : 'المرتفع'} يعني مساحة للنمو.`
}
return `${area.areaAr} تقدم بيئة أعمال متنوعة مع بنية تحتية قوية. اتجاه النمو ${area.growthTrend === 'Growing' ? 'التصاعدي' : 'المستقر'} يشير إلى فرص واعدة.`
}
if (hasSector) {
return `${area.area} already hosts an active ${sector} community, providing networking and collaboration opportunities. The ${area.saturationLevel.toLowerCase()} saturation level means room for growth.`
}
return `${area.area} offers a diverse business environment with strong infrastructure. The ${area.growthTrend.toLowerCase()} growth trend indicates promising opportunities.`
}generateCompetitiveLandscape function · typescript · L187-L192 (6 LOC)app/api/market-directory/setup-advisor/route.ts
function generateCompetitiveLandscape(area: AreaStat, sector: string, isArabic: boolean): string {
if (isArabic) {
return `${area.totalCompanies} شركة مسجلة في المنطقة. القطاعات الرئيسية: ${area.topSectorsAr.join('، ')}. متوسط الإيجار: ${area.averageRent}.`
}
return `${area.totalCompanies} registered companies in the area. Key sectors: ${area.topSectors.join(', ')}. Average rent: ${area.averageRent}.`
}generateAdvantages function · typescript · L194-L209 (16 LOC)app/api/market-directory/setup-advisor/route.ts
function generateAdvantages(area: AreaStat, isArabic: boolean): string[] {
const infra = isArabic ? area.keyInfrastructureAr : area.keyInfrastructure
const advantages = infra.slice(0, 3)
if (area.growthTrend === 'Growing') {
advantages.push(
isArabic ? 'سوق نامي مع فرص متزايدة' : 'Growing market with increasing opportunities'
)
}
if (area.saturationLevel === 'Low' || area.saturationLevel === 'Medium') {
advantages.push(
isArabic ? 'منافسة معتدلة — مساحة للداخلين الجدد' : 'Moderate competition — room for new entrants'
)
}
return advantages
}generateChallenges function · typescript · L211-L231 (21 LOC)app/api/market-directory/setup-advisor/route.ts
function generateChallenges(area: AreaStat, isArabic: boolean): string[] {
const challenges: string[] = []
const rentNum = parseInt(area.averageRent.replace(/[^0-9]/g, ''))
if (rentNum > 250) {
challenges.push(
isArabic ? 'تكاليف إيجار مرتفعة نسبياً' : 'Relatively high rental costs'
)
}
if (area.saturationLevel === 'High' || area.saturationLevel === 'Very High') {
challenges.push(
isArabic ? 'سوق مشبع — يتطلب تمايز قوي' : 'Saturated market — requires strong differentiation'
)
}
if (challenges.length === 0) {
challenges.push(
isArabic ? 'قد يتطلب بناء شبكة علاقات محلية' : 'May require building local network from scratch'
)
}
return challenges
}GET function · typescript · L4-L38 (35 LOC)app/api/services/[id]/route.ts
export async function GET(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const { id } = await params
const serviceId = parseInt(id, 10)
if (isNaN(serviceId)) {
return NextResponse.json(
{ error: 'Invalid service ID' },
{ status: 400 }
)
}
const service = await prisma.service.findUnique({
where: { id: serviceId },
})
if (!service) {
return NextResponse.json(
{ error: 'Service not found' },
{ status: 404 }
)
}
return NextResponse.json(service)
} catch (error) {
console.error('Error fetching service:', error)
return NextResponse.json(
{ error: 'Failed to fetch service' },
{ status: 500 }
)
}
}GET function · typescript · L4-L55 (52 LOC)app/api/services/route.ts
export async function GET(request: NextRequest) {
try {
const { searchParams } = new URL(request.url)
const platform = searchParams.get('platform')
const department = searchParams.get('department')
const search = searchParams.get('search')
// Build where clause
const where: Record<string, unknown> = {}
if (platform) {
where.platform = platform
}
if (department) {
where.dept = department
}
if (search) {
where.OR = [
{ name: { contains: search } },
{ nameAr: { contains: search } },
{ description: { contains: search } },
{ descriptionAr: { contains: search } },
]
}
const services = await prisma.service.findMany({
where,
orderBy: { name: 'asc' },
})
// Get unique departments and platforms for filters
const allServices = await prisma.service.findMany({
select: { dept: true, platform: true },
})
const departments = [...new Set(allServices.GET function · typescript · L4-L41 (38 LOC)app/api/staff/activity/route.ts
export async function GET(request: NextRequest) {
try {
const { searchParams } = new URL(request.url)
const page = parseInt(searchParams.get('page') || '1', 10)
const pageSize = parseInt(searchParams.get('pageSize') || '15', 10)
const skip = (page - 1) * pageSize
const [entries, totalCount] = await Promise.all([
prisma.activityLog.findMany({
orderBy: { performedAt: 'desc' },
skip,
take: pageSize,
include: {
application: {
select: {
id: true,
serviceType: true,
submittedBy: true,
},
},
},
}),
prisma.activityLog.count(),
])
return NextResponse.json({
success: true,
entries,
totalCount,
page,
pageSize,
totalPages: Math.ceil(totalCount / pageSize),
})
} catch (error) {
console.error('Error fetching activity log:', error)
return NextResponse.json({ success: false, erroGET function · typescript · L6-L35 (30 LOC)app/api/staff/applications/[id]/route.ts
export async function GET(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const { id } = await params
const application = await prisma.baseApplication.findUnique({
where: { id },
include: {
assignedTo: {
select: { id: true, name: true, nameAr: true },
},
activityLogs: {
orderBy: { performedAt: 'desc' },
take: 50,
},
},
})
if (!application) {
return NextResponse.json({ success: false, error: 'Application not found' }, { status: 404 })
}
return NextResponse.json({ success: true, application })
} catch (error) {
console.error('Error fetching application:', error)
return NextResponse.json({ success: false, error: 'Failed to fetch application' }, { status: 500 })
}
}Want this analysis on your repo? https://repobility.com/scan/
PATCH function · typescript · L37-L110 (74 LOC)app/api/staff/applications/[id]/route.ts
export async function PATCH(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const { id } = await params
const body = await request.json()
// Fetch current application
const current = await prisma.baseApplication.findUnique({ where: { id } })
if (!current) {
return NextResponse.json({ success: false, error: 'Application not found' }, { status: 404 })
}
// Build update data
const updateData: Record<string, unknown> = {}
const actions: string[] = []
if (body.status && body.status !== current.status) {
updateData.status = body.status as StaffApplicationStatus
actions.push(`Status changed from ${current.status} to ${body.status}`)
// Set reviewedAt if moving to a terminal status
const terminalStatuses = ['APPROVED', 'REJECTED', 'CLOSED']
if (terminalStatuses.includes(body.status) && !current.reviewedAt) {
updateData.reviewedAt = new Date()
updateData.revieweGET function · typescript · L5-L81 (77 LOC)app/api/staff/applications/route.ts
export async function GET(request: NextRequest) {
try {
const { searchParams } = new URL(request.url)
// Return staff list for assignment dropdown
if (searchParams.get('staffList') === 'true') {
const staffList = await prisma.staffUser.findMany({
select: { id: true, name: true, nameAr: true },
orderBy: { name: 'asc' },
})
return NextResponse.json({ success: true, staffList })
}
// Parse query params
const serviceType = searchParams.get('serviceType') || ''
const status = searchParams.get('status') || ''
const assignedToId = searchParams.get('assignedToId') || ''
const search = searchParams.get('search') || ''
const page = parseInt(searchParams.get('page') || '1', 10)
const pageSize = parseInt(searchParams.get('pageSize') || '10', 10)
const sortBy = searchParams.get('sortBy') || 'submittedAt'
const sortOrder = (searchParams.get('sortOrder') || 'desc') as 'asc' | 'desc'
const skip = (page - 1) *GET function · typescript · L4-L101 (98 LOC)app/api/staff/stats/route.ts
export async function GET() {
try {
const now = new Date()
const todayStart = new Date(now.getFullYear(), now.getMonth(), now.getDate())
const thirtyDaysAgo = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000)
const [totalOpen, pendingReview, resolvedToday, avgResolution, countsByService, oldestPending] = await Promise.all([
// Total open applications (not CLOSED, not REJECTED, not APPROVED)
prisma.baseApplication.count({
where: {
status: { in: ['SUBMITTED', 'UNDER_REVIEW', 'PENDING_INFO'] },
},
}),
// Pending review (SUBMITTED or UNDER_REVIEW)
prisma.baseApplication.count({
where: {
status: { in: ['SUBMITTED', 'UNDER_REVIEW'] },
},
}),
// Resolved today
prisma.baseApplication.count({
where: {
reviewedAt: { gte: todayStart },
status: { in: ['APPROVED', 'REJECTED', 'CLOSED'] },
},
}),
// Average resolution time (last 30page 1 / 4next ›