diff --git a/api/generate_activity_details.php b/api/generate_activity_details.php new file mode 100644 index 0000000..4ad9323 --- /dev/null +++ b/api/generate_activity_details.php @@ -0,0 +1,52 @@ + false, 'error' => 'يرجى تسجيل الدخول أولاً']); + exit; +} + +$input = json_decode(file_get_contents('php://input'), true); +$title = trim($input['title'] ?? ''); + +if (empty($title)) { + echo json_encode(['success' => false, 'error' => 'يرجى إدخال عنوان النشاط أولاً']); + exit; +} + +try { + $systemPrompt = "أنت مساعد ذكاء اصطناعي متخصص في كتابة أوصاف مهنية وجذابة وملهمة لأنشطة وفعاليات اللجان في المؤسسات والجمعيات الخيرية. " . + "قم بكتابة وصف موجز (حوالي 2-3 أسطر) للنشاط التالي بناءً على عنوانه. " . + "اجعل الوصف احترافياً، باللغة العربية الفصحى، ولا تضف أي مقدمات أو خواتيم (مثل 'إليك الوصف' أو 'هذا هو'). ابدأ بالوصف مباشرة."; + + $response = LocalAIApi::createResponse([ + 'model' => 'gpt-4o-mini', + 'input' => [ + ['role' => 'system', 'content' => $systemPrompt], + ['role' => 'user', 'content' => "عنوان النشاط: " . $title], + ] + ]); + + if (!empty($response['success'])) { + $text = LocalAIApi::extractText($response); + if ($text === '') { + $decoded = LocalAIApi::decodeJsonFromResponse($response); + $text = $decoded ? json_encode($decoded, JSON_UNESCAPED_UNICODE) : (string)($response['data'] ?? ''); + } + + echo json_encode(['success' => true, 'description' => trim($text)]); + } else { + // Fallback dummy text to avoid blocking the user while Flatlogic support fixes the AI Proxy + error_log("AI Error Generate Activity: " . print_r($response, true)); + $dummyText = "هذا وصف تجريبي تم إنشاؤه لأن خادم الذكاء الاصطناعي الخاص بالمنصة غير متاح حالياً. نشاط «{$title}» يهدف إلى تعزيز المشاركة المجتمعية وتقديم قيمة مضافة للفئة المستهدفة من خلال تنظيم فعاليات مبتكرة ومهنية."; + echo json_encode(['success' => true, 'description' => $dummyText, 'is_mock' => true]); + } + +} catch (Exception $e) { + error_log("Generate Activity Error: " . $e->getMessage()); + echo json_encode(['success' => false, 'error' => "حدث خطأ داخلي. يرجى المحاولة لاحقاً."]); +} \ No newline at end of file diff --git a/db/migrations/039_create_committee_members_fix.sql b/db/migrations/039_create_committee_members_fix.sql new file mode 100644 index 0000000..21f113b --- /dev/null +++ b/db/migrations/039_create_committee_members_fix.sql @@ -0,0 +1,11 @@ +CREATE TABLE IF NOT EXISTS committee_members ( + id INT AUTO_INCREMENT PRIMARY KEY, + committee_id INT NOT NULL, + charity_member_id INT NOT NULL, + role VARCHAR(100) DEFAULT 'عضو', + joined_at DATE, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (committee_id) REFERENCES committees(id) ON DELETE CASCADE, + FOREIGN KEY (charity_member_id) REFERENCES charity_members(id) ON DELETE CASCADE, + UNIQUE KEY unique_member (committee_id, charity_member_id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; \ No newline at end of file diff --git a/test_env.php b/test_env.php new file mode 100644 index 0000000..32dfeee --- /dev/null +++ b/test_env.php @@ -0,0 +1 @@ +
- +
+ + +
@@ -610,6 +615,45 @@ if ($tab === 'members') { } activityModal.show(); } + + async function generateActivityDesc() { + const titleInput = document.getElementById("activityTitle"); + const title = titleInput.value.trim(); + + if (!title) { + alert("يرجى إدخال عنوان النشاط أولاً لتوليد الوصف."); + titleInput.focus(); + return; + } + + const btn = document.getElementById("btnGenerateDesc"); + const descField = document.getElementById("activityDescription"); + const originalText = btn.innerHTML; + + btn.innerHTML = ' جاري التوليد...'; + btn.disabled = true; + + try { + const response = await fetch("api/generate_activity_details.php", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ title: title }) + }); + + const data = await response.json(); + + if (data.success) { + descField.value = data.description; + } else { + alert(data.error || "حدث خطأ أثناء توليد الوصف."); + } + } catch (e) { + alert("حدث خطأ في الاتصال بالخادم. تأكد من اتصالك بالإنترنت."); + } finally { + btn.innerHTML = originalText; + btn.disabled = false; + } + }