Files
amneziavpnphp/templates/ai/preview_generation.twig
T
2026-01-23 17:55:40 +03:00

244 lines
13 KiB
Twig

{% extends "layout.twig" %}
{% block title %}{{ t('ai.generation_preview') }} - {{ parent() }}{% endblock %}
{% block content %}
<div class="min-h-screen bg-gray-50">
<div class="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<!-- Header -->
<div class="mb-8">
<div class="flex justify-between items-center">
<div>
<h1 class="text-3xl font-bold text-gray-900">{{ t('ai.generation_preview') }}</h1>
<p class="mt-2 text-gray-600">{{ t('ai.generation_preview_description') }}</p>
</div>
<div class="flex space-x-3">
<a href="/settings/protocols" class="inline-flex items-center px-4 py-2 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18"/>
</svg>
{{ t('protocols.back_to_protocols') }}
</a>
<button id="apply-script-btn" class="inline-flex items-center px-4 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-green-600 hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500">
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"/>
</svg>
{{ t('ai.apply_to_protocol') }}
</button>
</div>
</div>
</div>
<!-- Generation Info -->
<div class="bg-white shadow rounded-lg mb-6">
<div class="px-6 py-4 border-b border-gray-200">
<h2 class="text-lg font-medium text-gray-900">{{ t('ai.generation_details') }}</h2>
</div>
<div class="px-6 py-4">
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<label class="block text-sm font-medium text-gray-700">{{ t('ai.model_used') }}</label>
<p class="mt-1 text-sm text-gray-900">{{ generation.model_used }}</p>
</div>
<div>
<label class="block text-sm font-medium text-gray-700">{{ t('ai.generated_at') }}</label>
<p class="mt-1 text-sm text-gray-900">{{ generation.created_at|date('Y-m-d H:i:s') }}</p>
</div>
<div>
<label class="block text-sm font-medium text-gray-700">{{ t('protocols.ubuntu_compatible') }}</label>
<div class="mt-1">
{% if generation.ubuntu_compatible %}
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-green-100 text-green-800">
<svg class="w-3 h-3 mr-1" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"/>
</svg>
{{ t('common.compatible') }}
</span>
{% else %}
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-red-100 text-red-800">
<svg class="w-3 h-3 mr-1" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd"/>
</svg>
{{ t('common.not_compatible') }}
</span>
{% endif %}
</div>
</div>
{% if generation.protocol_name %}
<div>
<label class="block text-sm font-medium text-gray-700">{{ t('ai.associated_protocol') }}</label>
<p class="mt-1 text-sm text-gray-900">{{ generation.protocol_name }}</p>
</div>
{% endif %}
</div>
</div>
</div>
<!-- Prompt -->
<div class="bg-white shadow rounded-lg mb-6">
<div class="px-6 py-4 border-b border-gray-200">
<h2 class="text-lg font-medium text-gray-900">{{ t('ai.user_prompt') }}</h2>
</div>
<div class="px-6 py-4">
<p class="text-sm text-gray-900 whitespace-pre-wrap">{{ generation.prompt }}</p>
</div>
</div>
<!-- Generated Script -->
<div class="bg-white shadow rounded-lg mb-6">
<div class="px-6 py-4 border-b border-gray-200">
<div class="flex justify-between items-center">
<h2 class="text-lg font-medium text-gray-900">{{ t('ai.generated_installation_script') }}</h2>
<button id="copy-script-btn" class="inline-flex items-center px-3 py-1 border border-gray-300 rounded-md text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">
<svg class="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"/>
</svg>
{{ t('ai.copy_script') }}
</button>
</div>
</div>
<div class="px-6 py-4">
<div class="bg-gray-900 text-green-400 p-4 rounded-md overflow-x-auto">
<pre id="script-content" class="text-sm whitespace-pre-wrap">{{ script }}</pre>
</div>
</div>
</div>
<!-- AI Suggestions -->
{% if suggestions %}
<div class="bg-white shadow rounded-lg mb-6">
<div class="px-6 py-4 border-b border-gray-200">
<h2 class="text-lg font-medium text-gray-900">{{ t('ai.suggestions') }}</h2>
</div>
<div class="px-6 py-4">
<ul class="space-y-2">
{% for suggestion in suggestions %}
<li class="flex items-start">
<svg class="w-5 h-5 text-blue-500 mr-2 mt-0.5 flex-shrink-0" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clip-rule="evenodd"/>
</svg>
<span class="text-sm text-gray-900">{{ suggestion }}</span>
</li>
{% endfor %}
</ul>
</div>
</div>
{% endif %}
<!-- Actions -->
<div class="bg-white shadow rounded-lg">
<div class="px-6 py-4 border-b border-gray-200">
<h2 class="text-lg font-medium text-gray-900">{{ t('common.actions') }}</h2>
</div>
<div class="px-6 py-4">
<div class="flex flex-wrap gap-3">
<button id="download-script-btn" class="inline-flex items-center px-4 py-2 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/>
</svg>
{{ t('ai.download_script') }}
</button>
{% if generation.protocol_id %}
<button id="view-protocol-btn" class="inline-flex items-center px-4 py-2 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/>
</svg>
{{ t('protocols.view_protocol') }}
</button>
{% endif %}
<button id="regenerate-btn" class="inline-flex items-center px-4 py-2 border border-purple-300 rounded-md shadow-sm text-sm font-medium text-purple-700 bg-white hover:bg-purple-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-purple-500">
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"/>
</svg>
{{ t('ai.regenerate') }}
</button>
</div>
</div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
const scriptContent = document.getElementById('script-content').textContent;
const generationId = {{ generation.id }};
const protocolId = {{ generation.protocol_id ?: 'null' }};
// Copy script
document.getElementById('copy-script-btn').addEventListener('click', function() {
const textarea = document.createElement('textarea');
textarea.value = scriptContent;
document.body.appendChild(textarea);
textarea.select();
document.execCommand('copy');
document.body.removeChild(textarea);
// Show feedback
const originalText = this.innerHTML;
this.innerHTML = '<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"/></svg>{{ t('common.copied') }}';
setTimeout(() => {
this.innerHTML = originalText;
}, 2000);
});
// Download script
document.getElementById('download-script-btn').addEventListener('click', function() {
const blob = new Blob([scriptContent], { type: 'text/plain' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `install-${generationId}.sh`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
});
// Apply to protocol
document.getElementById('apply-script-btn').addEventListener('click', function() {
if (!protocolId) {
alert('{{ t('ai.no_associated_protocol') }}');
return;
}
if (confirm('{{ t('ai.confirm_apply_script_to_protocol') }}')) {
fetch(`/api/ai/generations/${generationId}/apply`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
}
})
.then(response => response.json())
.then(result => {
if (result.success) {
alert('{{ t('ai.script_applied_successfully') }}');
window.location.href = `/settings/protocols/${protocolId}/edit`;
} else {
alert('{{ t('ai.error_applying_script') }}: ' + result.error);
}
})
.catch(error => {
alert('{{ t('ai.error_applying_script') }}: ' + error.message);
});
}
});
// View protocol
document.getElementById('view-protocol-btn').addEventListener('click', function() {
if (protocolId) {
window.location.href = `/settings/protocols/${protocolId}/edit`;
}
});
// Regenerate
document.getElementById('regenerate-btn').addEventListener('click', function() {
if (confirm('{{ t('ai.confirm_regenerate_script') }}')) {
// Go back to protocols page with AI assistant open
window.location.href = '/settings/protocols';
}
});
});
</script>
{% endblock %}