diff --git a/src/Services/LLMpool.php b/src/Services/LLMpool.php
index ff1991e..f83e3d3 100644
--- a/src/Services/LLMpool.php
+++ b/src/Services/LLMpool.php
@@ -14,12 +14,14 @@ class LLMpool
private FileStorage $storage;
private DAIClient $daiClient;
private ContentPrompt $promptGenerator;
+ private Renderer $renderer;
public function __construct()
{
$this->storage = new FileStorage();
$this->daiClient = new DAIClient();
$this->promptGenerator = new ContentPrompt();
+ $this->renderer = new Renderer();
}
/**
@@ -91,12 +93,21 @@ class LLMpool
}
$projectData['content']['generated'] = $content;
+ $this->storage->put("projects/{$projectId}.json", $projectData);
+
+ // 5. Render static site
+ if (!$this->renderer->render($projectId)) {
+ throw new Exception("Rendering failed.");
+ }
+
+ // 6. Update Project Status
+ $projectData = $this->storage->get("projects/{$projectId}.json");
$projectData['status'] = 'ready'; // Transition to ready
$projectData['current_step'] = 7; // Automatically ready for preview
$projectData['updated_at'] = date('c');
$this->storage->put("projects/{$projectId}.json", $projectData);
- // 6. Finish Task (Delete or Archive)
+ // 7. Finish Task (Delete or Archive)
$this->storage->delete($taskPath);
error_log("LLMpool: Task $taskFilename processed successfully for project $projectId.");
diff --git a/src/Services/Renderer.php b/src/Services/Renderer.php
new file mode 100644
index 0000000..fd1829c
--- /dev/null
+++ b/src/Services/Renderer.php
@@ -0,0 +1,69 @@
+storage = new FileStorage();
+ $this->exportsPath = realpath(__DIR__ . '/../../exports');
+ $this->templatesPath = realpath(__DIR__ . '/../Templates');
+ }
+
+ /**
+ * Renders a complete static website for a project.
+ */
+ public function render(string $projectId): bool
+ {
+ $projectData = $this->storage->get("projects/{$projectId}.json");
+ if (!$projectData || empty($projectData['content']['generated'])) {
+ return false;
+ }
+
+ $projectExportDir = $this->exportsPath . DIRECTORY_SEPARATOR . $projectId;
+ $assetsDir = $projectExportDir . DIRECTORY_SEPARATOR . 'assets' . DIRECTORY_SEPARATOR . 'css';
+
+ // 1. Create directory structure
+ if (!is_dir($assetsDir)) {
+ mkdir($assetsDir, 0777, true);
+ }
+
+ // 2. Copy static assets
+ $siteCssSource = $this->templatesPath . DIRECTORY_SEPARATOR . 'css' . DIRECTORY_SEPARATOR . 'site.css';
+ copy($siteCssSource, $assetsDir . DIRECTORY_SEPARATOR . 'site.css');
+
+ // 3. Render HTML
+ $html = $this->renderTemplate('base', [
+ 'project' => $projectData,
+ 'content' => ''
+ ]);
+
+ // 4. Save to exports
+ return file_put_contents($projectExportDir . DIRECTORY_SEPARATOR . 'index.html', $html) !== false;
+ }
+
+ /**
+ * Internal helper to render a PHP template with variables.
+ */
+ private function renderTemplate(string $templateName, array $vars = []): string
+ {
+ $templateFile = $this->templatesPath . DIRECTORY_SEPARATOR . $templateName . '.php';
+ if (!file_exists($templateFile)) {
+ throw new Exception("Template not found: $templateName");
+ }
+
+ extract($vars);
+ ob_start();
+ include $templateFile;
+ return ob_get_clean();
+ }
+}
diff --git a/src/Templates/base.php b/src/Templates/base.php
new file mode 100644
index 0000000..5085581
--- /dev/null
+++ b/src/Templates/base.php
@@ -0,0 +1,87 @@
+
+
+
+
+
+
+
+ = e($seo['title'] ?? $identity['business_name'] ?? 'Môj Web') ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ = $content ?>
+
+
+
+
+
+
diff --git a/src/Templates/css/site.css b/src/Templates/css/site.css
new file mode 100644
index 0000000..f636772
--- /dev/null
+++ b/src/Templates/css/site.css
@@ -0,0 +1,83 @@
+/* Reset & Base Styles */
+:root {
+ --primary-color: #2563eb;
+ --text-color: #1f2937;
+ --bg-color: #ffffff;
+ --section-padding: 5rem 1.5rem;
+}
+
+* {
+ box-sizing: border-box;
+ margin: 0;
+ padding: 0;
+}
+
+body {
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
+ color: var(--text-color);
+ background-color: var(--bg-color);
+ line-height: 1.6;
+}
+
+h1, h2, h3 {
+ line-height: 1.2;
+ margin-bottom: 1.5rem;
+}
+
+p {
+ margin-bottom: 1rem;
+}
+
+.container {
+ max-width: 1200px;
+ margin: 0 auto;
+ padding: 0 1.5rem;
+}
+
+section {
+ padding: var(--section-padding);
+}
+
+/* Common Components */
+.btn {
+ display: inline-block;
+ padding: 0.75rem 1.5rem;
+ border-radius: 0.375rem;
+ font-weight: 600;
+ text-decoration: none;
+ transition: all 0.2s;
+ cursor: pointer;
+}
+
+.btn-primary {
+ background-color: var(--primary-color);
+ color: white;
+}
+
+.btn-primary:hover {
+ opacity: 0.9;
+}
+
+/* Header & Nav */
+header {
+ padding: 1.5rem 0;
+ border-bottom: 1px solid #e5e7eb;
+}
+
+nav .container {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.logo img {
+ height: 2.5rem;
+}
+
+/* Footer */
+footer {
+ background-color: #111827;
+ color: white;
+ padding: 4rem 0;
+ margin-top: 2rem;
+}
diff --git a/tests/test_llmpool.php b/tests/test_llmpool.php
deleted file mode 100644
index f82582c..0000000
--- a/tests/test_llmpool.php
+++ /dev/null
@@ -1,72 +0,0 @@
- $userId,
- 'created_at' => date('c'),
- 'projects' => []
-];
-$storage->put("users/{$userId}.json", $userData);
-
-$project = $projectActions->createProject($userId);
-$projectId = $project['project_id'];
-
-// Mock some wizard data
-$project['wizard_data'] = [
- 'business_category' => ['group' => 'gastro', 'subcategory' => 'cafe'],
- 'identity' => ['business_name' => 'Kaviareň u Robota', 'tagline' => 'Káva s dušou'],
- 'services' => ['items' => [['name' => 'Espresso', 'description' => 'Silná káva', 'price_from' => '2']]],
- 'smart_answers' => ['terrace' => true]
-];
-$storage->put("projects/{$projectId}.json", $project);
-
-// 2. Create a dummy task
-$taskId = 't_test_worker';
-$taskData = [
- 'task_id' => $taskId,
- 'project_id' => $projectId,
- 'status' => 'queued',
- 'attempt_count' => 0,
- 'max_attempts' => 3,
- 'created_at' => date('c'),
- 'wizard_data' => $project['wizard_data']
-];
-$storage->put("llm/{$taskId}.json", $taskData);
-
-echo "[INFO] Task created: $taskId for project $projectId" . PHP_EOL;
-
-// 3. Run Worker
-$worker = new LLMpool();
-echo "[PROCESS] Running worker for task $taskId.json..." . PHP_EOL;
-
-// Since we call real AI, it might take time
-$success = $worker->processTask($taskId . '.json');
-
-if ($success) {
- echo "[SUCCESS] Worker finished successfully." . PHP_EOL;
- $updatedProject = $storage->get("projects/{$projectId}.json");
- echo "[INFO] Project status: " . $updatedProject['status'] . PHP_EOL;
- echo "[INFO] Generated content keys: " . implode(', ', array_keys($updatedProject['content']['generated'])) . PHP_EOL;
-} else {
- echo "[FAIL] Worker failed to process task." . PHP_EOL;
- $task = $storage->get("llm/{$taskId}.json");
- if ($task) {
- echo "[DEBUG] Task status: " . $task['status'] . PHP_EOL;
- echo "[DEBUG] Last error: " . ($task['last_error'] ?? 'N/A') . PHP_EOL;
- }
-}