104 lines
5.0 KiB
HTML
104 lines
5.0 KiB
HTML
{% if result.success %}
|
|
<div>
|
|
{% if result.cached %}
|
|
<div class="alert alert-info mb-2" role="status" aria-live="polite">
|
|
<i class="fas fa-info-circle mr-1" aria-hidden="true"></i>
|
|
Using cached rule
|
|
</div>
|
|
{% elif result.fallback %}
|
|
<div class="alert alert-warning mb-2" role="status" aria-live="polite">
|
|
<i class="fas fa-exclamation-triangle mr-1" aria-hidden="true"></i>
|
|
{{ result.message | default('Using fallback rule') }}
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if result.rules %}
|
|
<div class="grid grid-cols-1 gap-2 mb-2" role="list" aria-label="AI-generated rule options">
|
|
{% for rule in result.rules %}
|
|
<div class="bg-white border rounded-lg p-3" role="listitem"
|
|
x-data="{}"
|
|
>
|
|
<div class="flex justify-between items-start mb-2">
|
|
<h4 class="font-medium text-sm">Option {{ loop.index }}</h4>
|
|
<span class="badge {% if rule.quality_score >= 80 %}badge-success{% elif rule.quality_score >= 60 %}badge-warning{% else %}badge-error{% endif %}"
|
|
role="status" aria-live="polite">
|
|
{{ rule.quality_score }}%
|
|
</span>
|
|
</div>
|
|
<p class="text-sm text-gray-700 mb-2 whitespace-pre-line">{{ rule.text }}</p>
|
|
{% if rule.key_criteria %}
|
|
<div class="text-xs text-gray-500 mb-2">
|
|
<i class="fas fa-info-circle mr-1" aria-hidden="true"></i>
|
|
{{ rule.key_criteria }}
|
|
</div>
|
|
{% endif %}
|
|
<div class="flex gap-1">
|
|
<button type="button"
|
|
class="btn btn-xs btn-primary"
|
|
@click="rule_text = $el.getAttribute('data-rule-text'); show_ai_rules=false "
|
|
data-rule-text="{{ rule.text|safe }}"
|
|
aria-label="Use rule option {{ loop.index }}">
|
|
<i class="fas fa-check mr-1" aria-hidden="true"></i>
|
|
Use
|
|
</button>
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
{% endif %}
|
|
|
|
<script>
|
|
// Alpine.js data and methods
|
|
document.addEventListener('alpine:init', () => {
|
|
Alpine.data('aiRuleResult', () => ({
|
|
copyRuleText() {
|
|
const ruleText = document.getElementById('generated-rule-text').textContent;
|
|
navigator.clipboard.writeText(ruleText).then(() => {
|
|
// Show feedback
|
|
const button = event.target.closest('button');
|
|
const originalContent = button.innerHTML;
|
|
button.innerHTML = '<i class="fas fa-check"></i>';
|
|
setTimeout(() => {
|
|
button.innerHTML = originalContent;
|
|
}, 2000);
|
|
// Announce to screen readers
|
|
announceToScreenReader('Rule copied to clipboard');
|
|
}).catch(() => {
|
|
announceToScreenReader('Failed to copy rule to clipboard');
|
|
});
|
|
}
|
|
|
|
|
|
}))
|
|
});
|
|
|
|
function announceToScreenReader(message) {
|
|
const announcement = document.createElement('div');
|
|
announcement.setAttribute('role', 'status');
|
|
announcement.setAttribute('aria-live', 'polite');
|
|
announcement.className = 'sr-only';
|
|
announcement.textContent = message;
|
|
document.body.appendChild(announcement);
|
|
setTimeout(() => {
|
|
document.body.removeChild(announcement);
|
|
}, 1000);
|
|
}
|
|
|
|
// Add keyboard support for buttons
|
|
document.addEventListener('keydown', function(event) {
|
|
if (event.key === 'Enter' || event.key === ' ') {
|
|
const focusedElement = document.activeElement;
|
|
if (focusedElement && focusedElement.getAttribute('role') === 'button') {
|
|
event.preventDefault();
|
|
focusedElement.click();
|
|
}
|
|
}
|
|
});
|
|
</script>
|
|
</div>
|
|
{% else %}
|
|
<div class="alert alert-error mb-2">
|
|
<i class="fas fa-exclamation-circle mr-1"></i>
|
|
{{ result.error | default('Failed to generate rule') }}
|
|
</div>
|
|
{% endif %} |