( ′∀`)σ≡σ☆))Д′)レ(゚∀゚;)ヘ=З=З=Зε≡(ノ´_ゝ`)ノ
<?php
// config.php - Configuration file
$N8N_BASE_URL = getenv('N8N_BASE_URL') ?: 'https://thebrandai.app.n8n.cloud';
$N8N_AUTH_TOKEN = getenv('N8N_AUTH_TOKEN') ?: '';
$WEBHOOK_START = getenv('WEBHOOK_START') ?: rtrim($N8N_BASE_URL, '/') . '/webhook/proposal/start';
$WEBHOOK_ANSWERS = getenv('WEBHOOK_ANSWERS') ?: rtrim($N8N_BASE_URL, '/') . '/webhook/proposal/answers';
// Helper function to make API calls
function callN8NWebhook($url, $data, $authToken = '') {
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, array_filter([
'Content-Type: application/json',
'Accept: application/json',
$authToken ? ('Authorization: Bearer ' . $authToken) : ''
]));
curl_setopt($ch, CURLOPT_ENCODING, '');
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$ctype = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
$err = curl_error($ch);
curl_close($ch);
if ($httpCode === 200) {
$decoded = json_decode($response, true);
return is_array($decoded) ? $decoded : ['raw' => $response, 'content_type' => $ctype];
}
return [
'error' => 'Upstream error',
'http_code' => $httpCode,
'body' => $response,
'content_type' => $ctype,
'curl_error' => $err,
'upstream' => $url
];
}
// Start session for storing data between requests
session_start();
// Handle form submission
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$action = $_POST['action'] ?? '';
if ($action === 'start') {
// Initial proposal request
$prompt = $_POST['prompt'] ?? '';
$depth = intval($_POST['depth'] ?? 1);
$breadth = intval($_POST['breadth'] ?? 2);
if (empty($prompt)) {
$error = 'Please provide a proposal description';
} else {
$response = callN8NWebhook($WEBHOOK_START, [
// If your workflow expects a specific key for the prompt, include both
'prompt' => $prompt,
'What proposal would you like to create?' => $prompt,
'depth' => $depth,
'breadth' => $breadth
], $N8N_AUTH_TOKEN);
if (isset($response['error'])) {
$errorBody = isset($response['body']) ? (is_string($response['body']) ? $response['body'] : json_encode($response['body'])) : '';
$error = 'Failed to connect to n8n (HTTP ' . ($response['http_code'] ?? '?') . ') ' . ($response['curl_error'] ? ('- ' . $response['curl_error']) : '') . ($errorBody ? ('; body: ' . $errorBody) : '');
} else {
// Store data in session
$reqId = $response['request_id'] ?? ($response['output']['request_id'] ?? null);
$questions = $response['questions'] ?? ($response['output']['questions'] ?? []);
if (!is_array($questions)) { $questions = []; }
$_SESSION['request_id'] = $reqId;
$_SESSION['prompt'] = $prompt;
$_SESSION['depth'] = $depth;
$_SESSION['breadth'] = $breadth;
$_SESSION['questions'] = $questions;
if (empty($questions)) {
$_SESSION['start_error'] = 'No clarification questions returned from n8n. Raw response: ' . json_encode($response);
} else {
$_SESSION['start_error'] = null;
}
// Redirect to questions page
header('Location: ?step=questions');
exit;
}
}
} elseif ($action === 'submit_answers') {
// Submit answers to clarifying questions
$answers = [];
if (isset($_SESSION['questions']) && is_array($_SESSION['questions'])) {
foreach ($_SESSION['questions'] as $index => $question) {
$answer = $_POST["answer_$index"] ?? '';
if (!empty($answer)) {
$answers[] = [
'question' => $question,
'answer' => $answer
];
}
}
}
$response = callN8NWebhook($WEBHOOK_ANSWERS, [
'request_id' => $_SESSION['request_id'] ?? null,
'prompt' => $_SESSION['prompt'] ?? '',
'depth' => $_SESSION['depth'] ?? 1,
'breadth' => $_SESSION['breadth'] ?? 2,
'answers' => $answers
], $N8N_AUTH_TOKEN);
if (isset($response['error'])) {
$errorBody = isset($response['body']) ? (is_string($response['body']) ? $response['body'] : json_encode($response['body'])) : '';
$error = 'Failed to connect to n8n (HTTP ' . ($response['http_code'] ?? '?') . ') ' . ($response['curl_error'] ? ('- ' . $response['curl_error']) : '') . ($errorBody ? ('; body: ' . $errorBody) : '');
} else {
$url = $response['url'] ?? ($response['notion_page']['url'] ?? '');
$_SESSION['notion_page'] = [ 'url' => $url ];
header('Location: ?step=complete');
exit;
}
}
}
$step = $_GET['step'] ?? 'start';
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ProposalX - Brand AI</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Inter', 'Segoe UI', -apple-system, BlinkMacSystemFont, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 2rem;
color: #333;
}
.container {
max-width: 800px;
margin: 0 auto;
}
.card {
background: white;
border-radius: 16px;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
padding: 3rem;
animation: slideUp 0.5s ease-out;
}
@keyframes slideUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.header {
text-align: center;
margin-bottom: 2rem;
}
.logo {
width: 100%;
max-width: 400px;
height: auto;
border-radius: 12px;
margin-bottom: 1.5rem;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
h1 {
font-size: 2rem;
font-weight: 700;
color: #1a202c;
margin-bottom: 0.5rem;
}
.subtitle {
font-size: 1rem;
color: #718096;
line-height: 1.6;
}
.form-group {
margin-bottom: 1.5rem;
}
label {
display: block;
font-weight: 600;
margin-bottom: 0.5rem;
color: #2d3748;
}
.required::after {
content: ' *';
color: #e53e3e;
}
input[type="text"],
textarea,
select {
width: 100%;
padding: 0.875rem 1rem;
border: 2px solid #e2e8f0;
border-radius: 8px;
font-size: 1rem;
transition: all 0.3s;
font-family: inherit;
}
input:focus,
textarea:focus,
select:focus {
outline: none;
border-color: #667eea;
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
}
textarea {
min-height: 120px;
resize: vertical;
}
.range-container {
margin-top: 0.5rem;
}
input[type="range"] {
width: 100%;
height: 6px;
border-radius: 3px;
background: #e2e8f0;
outline: none;
-webkit-appearance: none;
}
input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none;
width: 20px;
height: 20px;
border-radius: 50%;
background: #667eea;
cursor: pointer;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
}
input[type="range"]::-moz-range-thumb {
width: 20px;
height: 20px;
border-radius: 50%;
background: #667eea;
cursor: pointer;
border: none;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
}
.range-labels {
display: flex;
justify-content: space-between;
font-size: 0.75rem;
color: #718096;
margin-top: 0.5rem;
padding: 0 2px;
}
.range-value {
display: inline-block;
margin-left: 0.5rem;
font-weight: 600;
color: #667eea;
}
.description {
font-size: 0.875rem;
color: #718096;
margin-top: 0.25rem;
}
.checkbox-group {
display: flex;
align-items: flex-start;
gap: 0.75rem;
padding: 1rem;
background: #f7fafc;
border-radius: 8px;
border: 1px solid #e2e8f0;
}
input[type="checkbox"] {
margin-top: 0.25rem;
width: 18px;
height: 18px;
cursor: pointer;
}
.checkbox-label {
font-size: 0.875rem;
line-height: 1.5;
color: #2d3748;
font-weight: 400;
}
.btn {
width: 100%;
padding: 1rem 1.5rem;
font-size: 1.125rem;
font-weight: 600;
color: white;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border: none;
border-radius: 8px;
cursor: pointer;
transition: all 0.3s;
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
}
.btn:hover {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(102, 126, 234, 0.6);
}
.btn:active {
transform: translateY(0);
}
.btn:disabled {
opacity: 0.5;
cursor: not-allowed;
transform: none;
}
.error {
background: #fff5f5;
color: #c53030;
padding: 1rem;
border-radius: 8px;
border-left: 4px solid #e53e3e;
margin-bottom: 1.5rem;
}
.success-icon {
text-align: center;
font-size: 4rem;
margin-bottom: 1rem;
animation: scaleIn 0.5s ease-out;
}
@keyframes scaleIn {
from {
transform: scale(0);
}
to {
transform: scale(1);
}
}
.notion-link {
display: inline-block;
margin-top: 1rem;
padding: 0.75rem 1.5rem;
background: #f7fafc;
border: 2px solid #e2e8f0;
border-radius: 8px;
color: #667eea;
text-decoration: none;
font-weight: 600;
transition: all 0.3s;
}
.notion-link:hover {
background: #667eea;
color: white;
border-color: #667eea;
}
.question-number {
display: inline-block;
width: 32px;
height: 32px;
background: #667eea;
color: white;
border-radius: 50%;
text-align: center;
line-height: 32px;
font-weight: 600;
margin-right: 0.75rem;
}
.progress-bar {
height: 6px;
background: #e2e8f0;
border-radius: 3px;
margin-bottom: 2rem;
overflow: hidden;
}
.progress-fill {
height: 100%;
background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
transition: width 0.3s;
}
</style>
</head>
<body>
<div class="container">
<div class="card">
<?php if ($step === 'start'): ?>
<!-- Initial Form -->
<div class="header">
<img src="https://www.thebrand.ai/i/uploads/slider/slider_66dc37268d29a7-88153936-47415963.jpg"
alt="Brand AI" class="logo">
<h1>ProposalX</h1>
<p class="subtitle">AI-powered proposal creation in minutes. Let's create something amazing together.</p>
</div>
<?php if (isset($error)): ?>
<div class="error"><?= htmlspecialchars($error) ?></div>
<?php endif; ?>
<form method="POST">
<input type="hidden" name="action" value="start">
<div class="form-group">
<label class="required">What proposal would you like to create?</label>
<textarea name="prompt" required
placeholder="Example: Create a comprehensive proposal for a digital transformation project targeting Fortune 500 companies..."><?= htmlspecialchars($_POST['prompt'] ?? '') ?></textarea>
</div>
<div class="form-group">
<label for="breadth">Research Breadth <span class="range-value" id="breadth-value">2</span></label>
<p class="description">This value determines how many sources to explore (1-5).</p>
<div class="range-container">
<input type="range" id="breadth" name="breadth" min="1" max="5" value="2"
oninput="document.getElementById('breadth-value').textContent = this.value">
<div class="range-labels">
<span>1</span>
<span>2</span>
<span>3</span>
<span>4</span>
<span>5</span>
</div>
</div>
</div>
<div class="form-group">
<label for="depth">Research Depth <span class="range-value" id="depth-value">1</span></label>
<p class="description">Higher depth means more detailed research (1-3).</p>
<div class="range-container">
<input type="range" id="depth" name="depth" min="1" max="3" value="1"
oninput="document.getElementById('depth-value').textContent = this.value">
<div class="range-labels">
<span>1</span>
<span>2</span>
<span>3</span>
</div>
</div>
</div>
<div class="form-group">
<div class="checkbox-group">
<input type="checkbox" id="acknowledgment" required>
<label for="acknowledgment" class="checkbox-label">
I understand higher depth and breadth values I've selected may incur longer wait times and higher costs. I acknowledge this and wish to proceed with the research request.
</label>
</div>
</div>
<button type="submit" class="btn">Generate Questions →</button>
</form>
<?php elseif ($step === 'questions'): ?>
<!-- Questions Form -->
<div class="progress-bar">
<div class="progress-fill" style="width: 50%;"></div>
</div>
<div class="header">
<img src="https://www.thebrand.ai/i/uploads/slider/slider_66dc37268d29a7-88153936-47415963.jpg"
alt="Brand AI" class="logo">
<h1>Clarification Questions</h1>
<p class="subtitle">Answer these questions to help us better understand your proposal requirements.</p>
</div>
<?php if (!empty($_SESSION['start_error'])): ?>
<div class="error"><?= htmlspecialchars($_SESSION['start_error']) ?></div>
<?php unset($_SESSION['start_error']); ?>
<?php endif; ?>
<form method="POST">
<input type="hidden" name="action" value="submit_answers">
<?php foreach ((is_array($_SESSION['questions'] ?? []) ? $_SESSION['questions'] : []) as $index => $question): ?>
<div class="form-group">
<label class="required">
<span class="question-number"><?= $index + 1 ?></span>
<?= htmlspecialchars($question) ?>
</label>
<textarea name="answer_<?= $index ?>" required
placeholder="Your answer..."><?= htmlspecialchars($_POST["answer_$index"] ?? '') ?></textarea>
</div>
<?php endforeach; ?>
<button type="submit" class="btn">Submit Answers & Start Research →</button>
</form>
<?php elseif ($step === 'complete'): ?>
<!-- Completion Page -->
<div class="progress-bar">
<div class="progress-fill" style="width: 100%;"></div>
</div>
<div class="success-icon">✓</div>
<div class="header">
<h1>Research Initiated Successfully!</h1>
<p class="subtitle">
Your proposal research is now underway. Our AI researcher will work independently to
gather information and compile a comprehensive report.
</p>
</div>
<div style="background: #f7fafc; padding: 1.5rem; border-radius: 8px; margin: 2rem 0;">
<p style="margin-bottom: 1rem; color: #2d3748;">
<strong>Request ID:</strong> <?= htmlspecialchars($_SESSION['request_id'] ?? '') ?>
</p>
<p style="color: #718096; font-size: 0.875rem; line-height: 1.6;">
The research process will take some time depending on the depth and breadth you selected.
You'll receive a notification when your proposal is ready.
</p>
</div>
<?php if (!empty($_SESSION['notion_page']['url'])): ?>
<div style="text-align: center; margin: 2rem 0;">
<p style="margin-bottom: 1rem; color: #2d3748;">Your report will be available at:</p>
<a href="<?= htmlspecialchars($_SESSION['notion_page']['url']) ?>"
class="notion-link" target="_blank">
View Notion Page →
</a>
</div>
<?php endif; ?>
<div style="text-align: center; margin-top: 2rem;">
<a href="?" style="color: #667eea; text-decoration: none; font-weight: 600;">
← Create Another Proposal
</a>
</div>
<?php
// Clear session after showing completion
session_destroy();
?>
<?php endif; ?>
</div>
</div>
<script>
// Add smooth transitions
document.addEventListener('DOMContentLoaded', function() {
const form = document.querySelector('form');
if (form) {
form.addEventListener('submit', function(e) {
const btn = form.querySelector('.btn');
if (btn) {
btn.disabled = true;
btn.innerHTML = 'Processing...';
}
});
}
});
</script>
</body>
</html>