diff --git a/public/ajax.php b/public/ajax.php
index 49ae8cc..0af3aaf 100644
--- a/public/ajax.php
+++ b/public/ajax.php
@@ -64,6 +64,7 @@ try {
// Router
$projectActions = new \App\Actions\ProjectActions();
+ $taskActions = new \App\Actions\TaskActions();
$consentService = new \App\Services\ConsentService();
switch ($action) {
@@ -130,6 +131,14 @@ try {
sendResponse(true, $result);
break;
+ case 'generateWebsite':
+ $projectId = $data['project_id'] ?? null;
+ if (!$projectId) {
+ sendResponse(false, ['code' => 'MISSING_PROJECT_ID', 'message' => 'Project ID is required.'], 400);
+ }
+ sendResponse(true, $taskActions->generateWebsite($userId, $projectId));
+ break;
+
default:
sendResponse(false, ['code' => 'UNKNOWN_ACTION', 'message' => "Action '$action' is not defined."], 404);
break;
diff --git a/public/css/wizard.css b/public/css/wizard.css
index 2ee7cb8..b5b1276 100644
--- a/public/css/wizard.css
+++ b/public/css/wizard.css
@@ -482,6 +482,33 @@ textarea:focus, input[type="text"]:focus, input[type="email"]:focus, input[type=
padding: 0;
}
+/* Generation Loader */
+.generation-loader {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 1.5rem;
+ padding: 3rem 0;
+}
+
+.spinner {
+ width: 3rem;
+ height: 3rem;
+ border: 4px solid var(--border-color);
+ border-top-color: var(--primary-color);
+ border-radius: 50%;
+ animation: spin 1s linear infinite;
+}
+
+@keyframes spin {
+ to { transform: rotate(360deg); }
+}
+
+#generation-status {
+ font-weight: 500;
+ color: var(--primary-color);
+}
+
button:disabled {
opacity: 0.5;
cursor: not-allowed;
diff --git a/public/index.html b/public/index.html
index 1299a5f..1601de3 100644
--- a/public/index.html
+++ b/public/index.html
@@ -307,9 +307,11 @@
Generovanie obsahu
-
Naša AI teraz pripravuje váš textový obsah.
-
diff --git a/public/js/wizard.js b/public/js/wizard.js
index fd9c74b..bfde63b 100644
--- a/public/js/wizard.js
+++ b/public/js/wizard.js
@@ -50,6 +50,13 @@ const App = {
this.state.currentStep = this.state.project.current_step || 1;
console.log('Loaded existing project:', this.state.project.project_id);
this.syncSelectionWithProject();
+
+ // Resume polling if generating
+ const pollingStatuses = ['queued', 'generating', 'rendering'];
+ if (pollingStatuses.includes(this.state.project.status)) {
+ this.state.currentStep = 6;
+ this.startPolling();
+ }
}
} else {
await this.createProject();
@@ -708,9 +715,66 @@ const App = {
if (this.state.currentStep < this.state.totalSteps) {
this.showStep(this.state.currentStep + 1);
+
+ // Trigger generation if entering step 6
+ if (this.state.currentStep === 6) {
+ this.startWebsiteGeneration();
+ }
}
},
+ async startWebsiteGeneration() {
+ try {
+ const result = await this.apiCall('generateWebsite');
+ if (result.success) {
+ console.log('Generation started:', result.data.task_id);
+ this.startPolling();
+ }
+ } catch (error) {
+ console.error('Failed to start generation:', error);
+ document.getElementById('generation-status').textContent = 'Chyba: ' + error.message;
+ }
+ },
+
+ startPolling() {
+ if (this.pollingInterval) clearInterval(this.pollingInterval);
+
+ this.pollingInterval = setInterval(async () => {
+ try {
+ const response = await this.apiCall('getProjectStatus');
+ if (response.success) {
+ const project = response.data;
+ this.state.project = project;
+ this.updateGenerationStatus(project.status);
+
+ if (project.status === 'ready' || project.status === 'failed') {
+ clearInterval(this.pollingInterval);
+ if (project.status === 'ready') {
+ // Move to preview step
+ this.showStep(7);
+ } else {
+ document.getElementById('generation-status').textContent = 'Generovanie zlyhalo. Skúste to neskôr.';
+ }
+ }
+ }
+ } catch (error) {
+ console.error('Polling error:', error);
+ }
+ }, 3000);
+ },
+
+ updateGenerationStatus(status) {
+ const statusEl = document.getElementById('generation-status');
+ const messages = {
+ 'queued': 'Zaradené do fronty, čakám na AI...',
+ 'generating': 'AI práve píše texty pre váš web...',
+ 'rendering': 'Skladáme výslednú stránku...',
+ 'ready': 'Hotovo! Pripravujem náhľad...',
+ 'failed': 'Nastal problém pri generovaní.'
+ };
+ statusEl.textContent = messages[status] || 'Spracovávam...';
+ },
+
prevStep() {
if (this.state.currentStep > 1) {
this.showStep(this.state.currentStep - 1);
@@ -749,6 +813,9 @@ const App = {
btnNext.disabled = nextDisabled;
+ // Hide footer in generating step
+ document.querySelector('.wizard-footer').classList.toggle('hidden', this.state.currentStep === 6);
+
if (this.state.currentStep === this.state.totalSteps) {
btnNext.textContent = 'Dokončiť';
} else {
diff --git a/src/Actions/TaskActions.php b/src/Actions/TaskActions.php
new file mode 100644
index 0000000..afbef3d
--- /dev/null
+++ b/src/Actions/TaskActions.php
@@ -0,0 +1,66 @@
+storage = new FileStorage();
+ $this->projectActions = new ProjectActions();
+ }
+
+ /**
+ * Creates a new AI generation task for a project.
+ */
+ public function generateWebsite(string $userId, string $projectId): array
+ {
+ // 1. Verify project ownership and status
+ $projectData = $this->projectActions->getProjectStatus($userId, $projectId);
+
+ // 2. Prevent duplicate queuing
+ if ($projectData['status'] === 'queued' || $projectData['status'] === 'generating') {
+ return ['message' => 'Generation already in progress.', 'status' => $projectData['status']];
+ }
+
+ // 3. Verify GDPR consent
+ $consentService = new ConsentService();
+ if (!$consentService->hasConsent($projectId)) {
+ throw new Exception("GDPR consent is required for AI generation.", 403);
+ }
+
+ // 4. Create Task
+ $taskId = 't_' . bin2hex(random_bytes(8));
+ $taskData = [
+ 'task_id' => $taskId,
+ 'project_id' => $projectId,
+ 'status' => 'queued',
+ 'attempt_count' => 0,
+ 'max_attempts' => 3,
+ 'created_at' => date('c'),
+ 'wizard_data' => $projectData['wizard_data']
+ ];
+
+ $this->storage->put("llm/{$taskId}.json", $taskData);
+
+ // 5. Update Project Status
+ $projectData['status'] = 'queued';
+ $projectData['updated_at'] = date('c');
+ $this->storage->put("projects/{$projectId}.json", $projectData);
+
+ return [
+ 'task_id' => $taskId,
+ 'status' => 'queued',
+ 'message' => 'Project successfully queued for generation.'
+ ];
+ }
+}