diff --git a/pages/BF-PRF/AS/DETAIL/[id]/index.vue b/pages/BF-PRF/AS/DETAIL/[id]/index.vue new file mode 100644 index 0000000..81c6af4 --- /dev/null +++ b/pages/BF-PRF/AS/DETAIL/[id]/index.vue @@ -0,0 +1,674 @@ + + + \ No newline at end of file diff --git a/pages/BF-PRF/AS/LIST/index.vue b/pages/BF-PRF/AS/LIST/index.vue new file mode 100644 index 0000000..00a279a --- /dev/null +++ b/pages/BF-PRF/AS/LIST/index.vue @@ -0,0 +1,337 @@ + + + \ No newline at end of file diff --git a/server/api/analyze-asnaf.post.ts b/server/api/analyze-asnaf.post.ts new file mode 100644 index 0000000..2034b0e --- /dev/null +++ b/server/api/analyze-asnaf.post.ts @@ -0,0 +1,124 @@ +import { defineEventHandler, readBody } from 'h3'; + +// Define an interface for the expected request body (subset of AsnafProfile) +interface AsnafAnalysisRequest { + monthlyIncome: string; + otherIncome: string; + totalIncome: string; + occupation: string; + maritalStatus: string; + dependents: Array; // Or a more specific type if you have one for dependents + // Add any other fields you deem necessary for OpenAI to analyze +} + +interface AidSuggestion { + nama: string; + peratusan: string; +} + +// Define an interface for the expected OpenAI response structure (and our API response) +interface AsnafAnalysisResponse { + hadKifayahPercentage: string; + kategoriAsnaf: string; + kategoriKeluarga: string; + cadanganKategori: string; + statusKelayakan: string; + cadanganBantuan: AidSuggestion[]; + ramalanJangkaMasaPulih: string; + rumusan: string; +} + +export default defineEventHandler(async (event): Promise => { + const body = await readBody(event); + + // --- Placeholder for Actual OpenAI API Call --- + // In a real application, you would: + // 1. Retrieve your OpenAI API key securely (e.g., from environment variables) + const openAIApiKey = process.env.OPENAI_API_KEY; + if (!openAIApiKey) { + console.error('OpenAI API key not configured. Please set OPENAI_API_KEY in your .env file.'); + throw createError({ statusCode: 500, statusMessage: 'OpenAI API key not configured' }); + } + + // 2. Construct the prompt for OpenAI using the data from `body`. + // IMPORTANT: Sanitize or carefully construct any data from `body` included in the prompt to prevent prompt injection. + const prompt = `You are an expert Zakat administrator. Based on the following applicant data: monthlyIncome: ${body.monthlyIncome}, totalIncome: ${body.totalIncome}, occupation: ${body.occupation}, maritalStatus: ${body.maritalStatus}, dependents: ${body.dependents.length}. +Return JSON with keys: hadKifayahPercentage, kategoriAsnaf, kategoriKeluarga, cadanganKategori, statusKelayakan, cadanganBantuan, ramalanJangkaMasaPulih, rumusan. +For 'cadanganBantuan', provide a JSON array of objects, where each object has a 'nama' (string, name of the aid) and 'peratusan' (string, e.g., '85%', representing suitability). Suggest 2-3 most relevant aid types. +Example for cadanganBantuan: [{"nama": "Bantuan Kewangan Bulanan", "peratusan": "90%"}, {"nama": "Bantuan Makanan Asas", "peratusan": "75%"}]. +Full JSON Example: {"hadKifayahPercentage": "75%", ..., "cadanganBantuan": [{"nama": "Bantuan Kewangan Bulanan", "peratusan": "90%"}], ...}`; + // Adjust the prompt to be more detailed and specific to your needs and desired JSON output structure. + + // 3. Make the API call to OpenAI + try { + const openAIResponse = await fetch('https://api.openai.com/v1/chat/completions', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${openAIApiKey}`, + }, + body: JSON.stringify({ + model: 'gpt-3.5-turbo', // Or your preferred model like gpt-4 + messages: [{ role: 'user', content: prompt }], + // For more consistent JSON output, consider using a model version that officially supports JSON mode if available + // and set response_format: { type: "json_object" }, (check OpenAI documentation for model compatibility) + }), + }); + + if (!openAIResponse.ok) { + const errorData = await openAIResponse.text(); + console.error('OpenAI API Error details:', errorData); + throw createError({ statusCode: openAIResponse.status, statusMessage: `Failed to get analysis from OpenAI: ${openAIResponse.statusText}` }); + } + + const openAIData = await openAIResponse.json(); + + // Parse the content from the response - structure might vary slightly based on OpenAI model/API version + // It's common for the JSON string to be in openAIData.choices[0].message.content + if (openAIData.choices && openAIData.choices[0] && openAIData.choices[0].message && openAIData.choices[0].message.content) { + const analysisResult = JSON.parse(openAIData.choices[0].message.content) as AsnafAnalysisResponse; + return analysisResult; + } else { + console.error('OpenAI response structure not as expected:', openAIData); + throw createError({ statusCode: 500, statusMessage: 'Unexpected response structure from OpenAI' }); + } + + } catch (error) { + console.error('Error during OpenAI API call or parsing:', error); + // Avoid exposing detailed internal errors to the client if they are not createError objects + if (typeof error === 'object' && error !== null && 'statusCode' in error) { + // We can infer error has statusCode here, but to be super safe with TS: + const e = error as { statusCode: number }; + if (e.statusCode) throw e; + } + throw createError({ statusCode: 500, statusMessage: 'Internal server error during AI analysis' }); + } + // --- End of Actual OpenAI API Call --- + + // The simulated response below this line should be REMOVED once the actual OpenAI call is implemented and working. + /* + console.log('Received for analysis in server route:', body); + await new Promise(resolve => setTimeout(resolve, 2000)); // Simulate API delay + + const totalIncomeNumeric = parseFloat(body.totalIncome); + let percentage = '50%'; + if (totalIncomeNumeric < 1000) percentage = '30%'; + else if (totalIncomeNumeric < 2000) percentage = '65%'; + else if (totalIncomeNumeric < 3000) percentage = '85%'; + else percentage = '110%'; + + return { + hadKifayahPercentage: percentage, + kategoriAsnaf: 'Simulated - Miskin', + kategoriKeluarga: 'Simulated - Miskin (50-100% HK)', + cadanganKategori: 'Simulated - Miskin', + statusKelayakan: 'Simulated - Layak (Miskin)', + cadanganBantuan: [ + { nama: 'Simulated - Bantuan Kewangan Bulanan', peratusan: '80%' }, + { nama: 'Simulated - Bantuan Pendidikan Anak', peratusan: '65%' } + ], + ramalanJangkaMasaPulih: 'Simulated - 6 bulan', + rumusan: 'Simulated - Pemohon memerlukan perhatian segera.' + }; + */ +}); \ No newline at end of file