diff --git a/api/config.php b/api/config.php new file mode 100644 index 0000000..b715582 --- /dev/null +++ b/api/config.php @@ -0,0 +1,6 @@ + 'Method Not Allowed']); + exit; +} + +$input = json_decode(file_get_contents('php://input'), true); +$audience = $input['audience'] ?? ''; +$idea = $input['idea'] ?? ''; + +if (empty($audience) || empty($idea)) { + http_response_code(400); + echo json_encode(['error' => 'Audience and Idea are required.']); + exit; +} + +if (ANTHROPIC_API_KEY === 'YOUR_ANTHROPIC_API_KEY_HERE') { + http_response_code(500); + echo json_encode(['error' => 'Anthropic API key is not configured. Please add it to api/config.php.']); + exit; +} + +$prompt = "Based on the following business idea and target audience in Egypt/MENA, create 3 distinct user personas. + +Target Audience: "$audience" +Business Idea: "$idea" + +For each persona, provide: +- A culturally relevant Egyptian/MENA name. +- Age and a realistic occupation in the Egyptian market. +- 2-3 key personality traits. +- Main concerns and priorities relevant to the Egyptian/MENA context. +- A typical communication style. + +Please return the response as a JSON object with a single key 'personas' which is an array of the 3 persona objects. Do not include any other text or explanation outside of the JSON object. +Each persona object in the array should have the following keys: 'name', 'age', 'occupation', 'traits', 'concerns', 'style'."; + +$data = [ + 'model' => 'claude-3-sonnet-20240229', + 'max_tokens' => 2048, + 'messages' => [ + [ + 'role' => 'user', + 'content' => $prompt, + ], + ], +]; + +$ch = curl_init('https://api.anthropic.com/v1/messages'); +curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); +curl_setopt($ch, CURLOPT_POST, true); +curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data)); +curl_setopt($ch, CURLOPT_HTTPHEADER, [ + 'x-api-key: ' . ANTHROPIC_API_KEY, + 'anthropic-version: 2023-06-01', + 'content-type: application/json', +]); + +$response = curl_exec($ch); +$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); +curl_close($ch); + +if ($http_code !== 200) { + http_response_code(502); + // Forwarding a sanitized error is better for debugging without exposing too much. + echo json_encode(['error' => 'Failed to communicate with AI service.', 'details' => json_decode($response)]); + exit; +} + +$result = json_decode($response, true); + +// The response from Claude is inside content block. +$content_json = $result['content'][0]['text'] ?? null; + +if (!$content_json) { + http_response_code(500); + echo json_encode(['error' => 'Invalid response format from AI service.']); + exit; +} + +// The actual persona data is expected to be a JSON string. +// We need to decode it one more time. +$personas_data = json_decode($content_json, true); + +if (json_last_error() !== JSON_ERROR_NONE || !isset($personas_data['personas'])) { + // Fallback if the AI didn't return perfect JSON + http_response_code(500); + echo json_encode(['error' => 'Could not parse personas from AI response.', 'raw_content' => $content_json]); + exit; +} + +echo json_encode($personas_data); diff --git a/assets/css/custom.css b/assets/css/custom.css new file mode 100644 index 0000000..169ebb2 --- /dev/null +++ b/assets/css/custom.css @@ -0,0 +1,110 @@ +/* Screen Test - Custom Styles */ + +body { + font-family: 'Inter', sans-serif; + background: linear-gradient(to bottom, #F9FAFB, #F3F4F6); +} + +.main-title { + font-weight: 800; /* Extra Bold */ + color: #111827; /* gray-900 */ +} + +.form-card { + width: 100%; + max-width: 700px; + background-color: rgba(255, 255, 255, 0.7); + backdrop-filter: blur(10px); + -webkit-backdrop-filter: blur(10px); + border: 1px solid rgba(255, 255, 255, 0.3); +} + +.form-control-lg { + padding: 1rem 1.25rem; + font-size: 1.1rem; + background-color: #F9FAFB; + border: 1px solid #E5E7EB; +} + +.form-control-lg:focus { + background-color: #fff; + border-color: #A855F7; + box-shadow: 0 0 0 0.25rem rgba(168, 85, 247, 0.2); +} + +.primary-gradient-btn { + background: linear-gradient(45deg, #D946EF, #A855F7, #6366F1); + border: none; + padding: 0.8rem 1.5rem; + transition: all 0.3s ease; +} + +.primary-gradient-btn:hover { + transform: translateY(-2px); + box-shadow: 0 10px 20px rgba(0,0,0,0.1) !important; +} + +/* Shadow Utilities */ +.shadow-xl { + box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04); +} + +.shadow-2xl { + box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25); +} + +/* Rounded Utilities */ +.rounded-4 { + border-radius: 1rem !important; +} + +.rounded-5 { + border-radius: 1.5rem !important; +} + +/* Persona Card Styles */ +.persona-card { + color: #fff; + transition: transform 0.3s ease, box-shadow 0.3s ease; +} + +.persona-card:hover { + transform: translateY(-5px); + box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25) !important; +} + +.persona-card .card-title { + color: #fff; +} + +.persona-card .card-subtitle { + color: rgba(255, 255, 255, 0.8); +} + +.persona-card h6 { + color: rgba(255, 255, 255, 0.9); + text-transform: uppercase; + font-size: 0.8rem; + letter-spacing: 0.5px; +} + +.persona-card p { + color: rgba(255, 255, 255, 0.95); +} + +/* Persona Gradients */ +.persona-gradient-1 { + background: linear-gradient(45deg, #22d3ee, #06b6d4); /* Cyan */ +} + +.persona-gradient-2 { + background: linear-gradient(45deg, #34d399, #059669); /* Emerald */ +} + +.persona-gradient-3 { + background: linear-gradient(45deg, #a78bfa, #7c3aed); /* Violet */ +} + +.text-white-75 { + color: rgba(255, 255, 255, 0.75) !important; +} diff --git a/assets/js/main.js b/assets/js/main.js new file mode 100644 index 0000000..97ecd10 --- /dev/null +++ b/assets/js/main.js @@ -0,0 +1,96 @@ +// Screen Test - Main JS + +document.addEventListener('DOMContentLoaded', function () { + const form = document.getElementById('idea-form'); + const submitButton = form.querySelector('button[type="submit"]'); + const personasContainer = document.getElementById('personas-container'); + const personasGrid = document.getElementById('personas-grid'); + + if (form) { + form.addEventListener('submit', function (e) { + e.preventDefault(); + + const audience = document.getElementById('target-audience').value; + const idea = document.getElementById('business-idea').value; + + if (!audience || !idea) { + alert('Please fill out both Target Audience and Business Idea fields.'); + return; + } + + // --- Loading State --- + const originalButtonText = submitButton.innerHTML; + submitButton.disabled = true; + submitButton.innerHTML = ` + + Generating... + `; + personasContainer.classList.add('d-none'); + personasGrid.innerHTML = ''; + + + // --- API Call --- + fetch('api/generate_personas.php', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ audience, idea }), + }) + .then(response => { + if (!response.ok) { + return response.json().then(err => { throw new Error(err.error || 'An unknown error occurred.') }); + } + return response.json(); + }) + .then(data => { + // --- Render Personas --- + if (data.personas && data.personas.length > 0) { + renderPersonas(data.personas); + personasContainer.classList.remove('d-none'); + setTimeout(() => { + personasContainer.scrollIntoView({ behavior: 'smooth', block: 'start' }); + }, 100); + } else { + throw new Error('The AI did not return any personas. Please try refining your inputs.'); + } + }) + .catch(error => { + console.error('Error:', error); + alert(`An error occurred: ${error.message}`); + }) + .finally(() => { + // --- Reset Button --- + submitButton.disabled = false; + submitButton.innerHTML = originalButtonText; + }); + }); + } + + function renderPersonas(personas) { + const personaGradients = [ + 'persona-gradient-1', + 'persona-gradient-2', + 'persona-gradient-3' + ]; + + personasGrid.innerHTML = personas.map((persona, index) => ` +
+
+
+

${persona.name}

+

${persona.age}, ${persona.occupation}

+
+
Traits:
+

${persona.traits}

+
Concerns:
+

${persona.concerns}

+
Style:
+

${persona.style}

+
+
+
+
+ `).join(''); + } +}); \ No newline at end of file diff --git a/index.php b/index.php index 7205f3d..51ee228 100644 --- a/index.php +++ b/index.php @@ -1,150 +1,82 @@ - - + - - - New Style - - - - - - - - - - - - - - - - - - - + + + + + Screen Test + + + + + + + + + + + + + + + + + + + + + -
-
-

Analyzing your requirements and generating your website…

-
- Loading… -
-

AI is collecting your requirements and applying the first changes.

-

This page will update automatically as the plan is implemented.

-

Runtime: PHP — UTC

-
-
- + +
+ +
+

Screen Test

+

Validate your business idea with AI-powered personas for the Egyptian market.

+
+ +
+
+ +
+
+ + +
+ +
+ + +
+ +
+ +
+
+ +
+
+ +
+

Generated Personas

+
+ +
+
+ + + +
+ + + + + - + \ No newline at end of file