MIF_E31222851/resources/views/admin/indonesian-tts/index.blade.php

537 lines
31 KiB
PHP

@extends('layouts.app')
@section('title', 'Indonesian TTS Settings')
@section('content')
<div class="min-h-screen bg-gray-50">
<!-- Top Navigation -->
<nav class="bg-white shadow-lg sticky top-0 z-40">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex justify-between h-16">
<div class="flex items-center">
<button id="sidebar-toggle" class="lg:hidden text-gray-700 hover:text-primary mr-4">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M4 6h16M4 12h16M4 18h16"></path>
</svg>
</button>
<div class="flex-shrink-0">
<h1 class="text-xl md:text-2xl font-bold text-primary">🏥 Admin Puskesmas</h1>
</div>
</div>
<div class="hidden md:flex items-center space-x-4">
<a href="{{ route('display') }}"
class="text-gray-700 hover:text-primary px-3 py-2 rounded-md text-sm font-medium transition duration-200">Display</a>
<span class="text-gray-700">Selamat datang, Admin</span>
<button onclick="confirmLogout()"
class="bg-red-500 hover:bg-red-600 text-white px-4 py-2 rounded-md text-sm font-medium transition duration-200">
Logout
</button>
</div>
</div>
</div>
</nav>
<div class="flex">
<!-- Sidebar -->
<aside id="sidebar"
class="fixed inset-y-0 left-0 z-50 w-64 bg-white shadow-xl transform -translate-x-full lg:translate-x-0 lg:static lg:inset-0 transition duration-200 ease-in-out">
<div class="flex items-center justify-between h-16 px-6 border-b border-gray-200">
<h2 class="text-lg font-semibold text-gray-900">Menu Admin</h2>
<button id="sidebar-close" class="lg:hidden text-gray-500 hover:text-gray-700">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12">
</path>
</svg>
</button>
</div>
<nav class="px-6 py-6">
<div class="space-y-6">
<!-- Dashboard -->
<div>
<a href="{{ route('admin.dashboard') }}"
class="flex items-center px-4 py-3 rounded-xl {{ request()->routeIs('admin.dashboard') ? 'bg-blue-50 text-blue-700 border border-blue-200' : 'text-gray-700 hover:bg-gray-50' }} transition duration-200">
<svg class="w-5 h-5 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2H5a2 2 0 00-2-2z"></path>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M8 5a2 2 0 012-2h4a2 2 0 012 2v6H8V5z"></path>
</svg>
<span class="font-medium">Dashboard</span>
</a>
</div>
<!-- Daftar Antrian -->
<div>
<div
class="flex items-center px-4 py-2 text-sm font-semibold text-gray-500 uppercase tracking-wider">
<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 5H7a2 2 0 00-2 2v10a2 2 0 002 2h8a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2">
</path>
</svg>
Daftar Antrian
</div>
<div class="mt-3 space-y-1">
<a href="{{ route('admin.poli.umum') }}"
class="flex items-center px-4 py-2 rounded-lg {{ request()->routeIs('admin.poli.umum') ? 'bg-blue-50 text-blue-700' : 'text-gray-600 hover:bg-gray-50' }} transition duration-200">
<div class="w-2 h-2 bg-blue-500 rounded-full mr-3"></div>
<span class="text-sm">Poli Umum</span>
</a>
<a href="{{ route('admin.poli.gigi') }}"
class="flex items-center px-4 py-2 rounded-lg {{ request()->routeIs('admin.poli.gigi') ? 'bg-green-50 text-green-700' : 'text-gray-600 hover:bg-gray-50' }} transition duration-200">
<div class="w-2 h-2 bg-green-500 rounded-full mr-3"></div>
<span class="text-sm">Poli Gigi</span>
</a>
<a href="{{ route('admin.poli.jiwa') }}"
class="flex items-center px-4 py-2 rounded-lg {{ request()->routeIs('admin.poli.jiwa') ? 'bg-purple-50 text-purple-700' : 'text-gray-600 hover:bg-gray-50' }} transition duration-200">
<div class="w-2 h-2 bg-purple-500 rounded-full mr-3"></div>
<span class="text-sm">Poli Jiwa</span>
</a>
<a href="{{ route('admin.poli.tradisional') }}"
class="flex items-center px-4 py-2 rounded-lg {{ request()->routeIs('admin.poli.tradisional') ? 'bg-yellow-50 text-yellow-700' : 'text-gray-600 hover:bg-gray-50' }} transition duration-200">
<div class="w-2 h-2 bg-yellow-500 rounded-full mr-3"></div>
<span class="text-sm">Poli Tradisional</span>
</a>
</div>
</div>
<!-- Kelola User -->
<div>
<a href="{{ route('admin.users.index') }}"
class="flex items-center px-4 py-3 rounded-xl {{ request()->routeIs('admin.users.*') ? 'bg-blue-50 text-blue-700 border border-blue-200' : 'text-gray-700 hover:bg-gray-50' }} transition duration-200">
<svg class="w-5 h-5 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197m13.5-9a2.5 2.5 0 11-5 0 2.5 2.5 0 015 0z">
</path>
</svg>
<span class="font-medium">Kelola User</span>
</a>
</div>
<!-- Laporan -->
<div>
<a href="{{ route('admin.laporan.index') }}"
class="flex items-center px-4 py-3 rounded-xl {{ request()->routeIs('admin.laporan.*') ? 'bg-blue-50 text-blue-700 border border-blue-200' : 'text-gray-700 hover:bg-gray-50' }} transition duration-200">
<svg class="w-5 h-5 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M9 17v-2m3 2v-4m3 4v-6m2 10H7a2 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">
</path>
</svg>
<span class="font-medium">Laporan</span>
</a>
</div>
<!-- Indonesian TTS Settings -->
<div>
<a href="{{ route('admin.indonesian-tts.index') }}"
class="flex items-center px-4 py-3 rounded-xl {{ request()->routeIs('admin.indonesian-tts.*') ? 'bg-green-50 text-green-700 border border-green-200' : 'text-gray-700 hover:bg-gray-50' }} transition duration-200">
<svg class="w-5 h-5 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M19 11a7 7 0 01-7 7m0 0a7 7 0 01-7-7m7 7v4m0 0H8m4 0h4m-4-8a3 3 0 01-3-3V5a3 3 0 116 0v6a3 3 0 01-3 3z">
</path>
</svg>
<span class="font-medium">Indonesian TTS</span>
</a>
</div>
<!-- Display -->
<div>
<a href="{{ route('display') }}"
class="flex items-center px-4 py-3 rounded-xl text-gray-700 hover:bg-gray-50 transition duration-200">
<svg class="w-5 h-5 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M15 10l4.553-2.276A1 1 0 0121 8.618v6.764a1 1 0 01-1.447.894L15 14M5 18h8a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z">
</path>
</svg>
<span class="font-medium">Display</span>
</a>
</div>
</div>
</nav>
</aside>
<!-- Overlay for mobile -->
<div id="sidebar-overlay" class="fixed inset-0 bg-black bg-opacity-50 z-40 lg:hidden hidden"></div>
<!-- Main Content -->
<div class="flex-1 lg:ml-0">
<div class="px-4 sm:px-6 lg:px-8 py-6 md:py-8">
<!-- Header -->
<div class="mb-8 animate-fade-in">
<h1 class="text-3xl md:text-4xl font-bold text-gray-900 mb-2">Indonesian TTS Settings</h1>
<p class="text-gray-600 text-lg">Kelola pengaturan Text-to-Speech Indonesia</p>
</div>
<!-- Status Cards -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
<!-- Indonesian TTS Status -->
<div class="bg-white rounded-xl shadow-lg p-6">
<div class="flex items-center">
<div class="flex-shrink-0">
<div id="indonesian-tts-status"
class="w-8 h-8 rounded-full bg-gray-200 flex items-center justify-center">
<svg class="w-5 h-5 text-gray-500" fill="none" stroke="currentColor"
viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
</div>
</div>
<div class="ml-4">
<h3 class="text-lg font-semibold text-gray-900">Indonesian TTS</h3>
<p id="indonesian-tts-text" class="text-sm text-gray-500">Checking...</p>
</div>
</div>
</div>
<!-- Coqui TTS Status -->
<div class="bg-white rounded-xl shadow-lg p-6">
<div class="flex items-center">
<div class="flex-shrink-0">
<div id="coqui-tts-status"
class="w-8 h-8 rounded-full bg-gray-200 flex items-center justify-center">
<svg class="w-5 h-5 text-gray-500" fill="none" stroke="currentColor"
viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
</div>
</div>
<div class="ml-4">
<h3 class="text-lg font-semibold text-gray-900">Coqui TTS</h3>
<p id="coqui-tts-text" class="text-sm text-gray-500">Checking...</p>
</div>
</div>
</div>
<!-- Model Files Status -->
<div class="bg-white rounded-xl shadow-lg p-6">
<div class="flex items-center">
<div class="flex-shrink-0">
<div id="model-files-status"
class="w-8 h-8 rounded-full bg-gray-200 flex items-center justify-center">
<svg class="w-5 h-5 text-gray-500" fill="none" stroke="currentColor"
viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
</div>
</div>
<div class="ml-4">
<h3 class="text-lg font-semibold text-gray-900">Model Files</h3>
<p id="model-files-text" class="text-sm text-gray-500">Checking...</p>
</div>
</div>
</div>
<!-- Speakers Status -->
<div class="bg-white rounded-xl shadow-lg p-6">
<div class="flex items-center">
<div class="flex-shrink-0">
<div id="speakers-status"
class="w-8 h-8 rounded-full bg-gray-200 flex items-center justify-center">
<svg class="w-5 h-5 text-gray-500" fill="none" stroke="currentColor"
viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
</div>
</div>
<div class="ml-4">
<h3 class="text-lg font-semibold text-gray-900">Available Speakers</h3>
<p id="speakers-text" class="text-sm text-gray-500">Checking...</p>
</div>
</div>
</div>
</div>
<!-- Test TTS Section -->
<div class="bg-white rounded-2xl shadow-xl p-8 mb-8">
<h2 class="text-2xl font-bold text-gray-900 mb-6">Test Indonesian TTS</h2>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<label for="test-text" class="block text-sm font-medium text-gray-700 mb-2">Text untuk
Test</label>
<textarea id="test-text" rows="4"
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
placeholder="Masukkan text untuk test TTS...">Nomor antrian U Lima, silakan menuju ke Poli Umum</textarea>
</div>
<div class="flex items-end">
<button id="test-tts-btn"
class="bg-blue-500 hover:bg-blue-600 text-white px-6 py-2 rounded-md font-medium transition duration-200">
Test TTS
</button>
</div>
</div>
<div id="test-result" class="mt-4 hidden">
<div class="bg-gray-50 rounded-lg p-4">
<h4 class="font-semibold text-gray-900 mb-2">Hasil Test:</h4>
<p id="test-result-text" class="text-sm text-gray-600"></p>
</div>
</div>
</div>
<!-- Installation Instructions -->
<div class="bg-white rounded-2xl shadow-xl p-8 mb-8">
<h2 class="text-2xl font-bold text-gray-900 mb-6">Instalasi Indonesian TTS</h2>
<div id="installation-instructions" class="prose max-w-none">
<div class="bg-yellow-50 border border-yellow-200 rounded-lg p-4 mb-6">
<div class="flex">
<div class="flex-shrink-0">
<svg class="h-5 w-5 text-yellow-400" fill="none" stroke="currentColor"
viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z">
</path>
</svg>
</div>
<div class="ml-3">
<h3 class="text-sm font-medium text-yellow-800">Perhatian</h3>
<div class="mt-2 text-sm text-yellow-700">
<p>Indonesian TTS memerlukan instalasi Coqui TTS dan model files. Ikuti
langkah-langkah di bawah ini.</p>
</div>
</div>
</div>
</div>
<div id="installation-steps" class="space-y-4">
<!-- Installation steps will be loaded here -->
</div>
</div>
</div>
<!-- Download Model Files -->
<div class="bg-white rounded-2xl shadow-xl p-8">
<h2 class="text-2xl font-bold text-gray-900 mb-6">Download Model Files</h2>
<div id="download-info" class="space-y-4">
<!-- Download info will be loaded here -->
</div>
</div>
</div>
</div>
</div>
</div>
@push('scripts')
<script>
// Sidebar toggle for mobile
document.getElementById('sidebar-toggle').addEventListener('click', function() {
document.getElementById('sidebar').classList.remove('-translate-x-full');
document.getElementById('sidebar-overlay').classList.remove('hidden');
});
document.getElementById('sidebar-close').addEventListener('click', function() {
document.getElementById('sidebar').classList.add('-translate-x-full');
document.getElementById('sidebar-overlay').classList.add('hidden');
});
document.getElementById('sidebar-overlay').addEventListener('click', function() {
document.getElementById('sidebar').classList.add('-translate-x-full');
document.getElementById('sidebar-overlay').classList.add('hidden');
});
// Load status on page load
document.addEventListener('DOMContentLoaded', function() {
loadStatus();
loadInstallationInstructions();
loadDownloadInfo();
});
// Load Indonesian TTS status
function loadStatus() {
fetch('{{ route('admin.indonesian-tts.status') }}')
.then(response => response.json())
.then(data => {
updateStatusCard('indonesian-tts', data.indonesian_tts_available, 'Indonesian TTS');
updateStatusCard('coqui-tts', data.coqui_tts_installed, 'Coqui TTS');
updateStatusCard('model-files', data.model_files_exist, 'Model Files');
const speakersCount = Object.keys(data.available_speakers || {}).length;
updateStatusCard('speakers', speakersCount > 0, `${speakersCount} Speakers Available`);
})
.catch(error => {
console.error('Error loading status:', error);
});
}
// Update status card
function updateStatusCard(id, isAvailable, text) {
const statusElement = document.getElementById(`${id}-status`);
const textElement = document.getElementById(`${id}-text`);
if (isAvailable) {
statusElement.className = 'w-8 h-8 rounded-full bg-green-100 flex items-center justify-center';
statusElement.innerHTML =
'<svg class="w-5 h-5 text-green-500" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path></svg>';
textElement.textContent = text;
textElement.className = 'text-sm text-green-600';
} else {
statusElement.className = 'w-8 h-8 rounded-full bg-red-100 flex items-center justify-center';
statusElement.innerHTML =
'<svg class="w-5 h-5 text-red-500" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path></svg>';
textElement.textContent = 'Not Available';
textElement.className = 'text-sm text-red-600';
}
}
// Load installation instructions
function loadInstallationInstructions() {
fetch('{{ route('admin.indonesian-tts.install') }}')
.then(response => response.json())
.then(data => {
const container = document.getElementById('installation-steps');
const steps = data.instructions.steps;
let html = '<ol class="list-decimal list-inside space-y-2">';
steps.forEach(step => {
if (step.trim() === '') {
html += '</ol><ol class="list-decimal list-inside space-y-2 mt-4">';
} else {
html += `<li class="text-sm text-gray-700">${step}</li>`;
}
});
html += '</ol>';
container.innerHTML = html;
})
.catch(error => {
console.error('Error loading installation instructions:', error);
});
}
// Load download info
function loadDownloadInfo() {
fetch('{{ route('admin.indonesian-tts.download') }}')
.then(response => response.json())
.then(data => {
const container = document.getElementById('download-info');
const info = data.download_info;
let html = `
<div class="bg-blue-50 border border-blue-200 rounded-lg p-4 mb-4">
<h3 class="text-lg font-semibold text-blue-900 mb-2">${info.title}</h3>
<p class="text-blue-700">${info.description}</p>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
`;
Object.entries(info.files).forEach(([filename, url]) => {
html += `
<div class="bg-gray-50 rounded-lg p-4">
<h4 class="font-semibold text-gray-900 mb-2">${filename}</h4>
<a href="${url}" target="_blank"
class="inline-flex items-center px-3 py-2 border border-transparent text-sm leading-4 font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">
Download
</a>
</div>
`;
});
html += '</div>';
html += '<div class="bg-gray-50 rounded-lg p-4">';
html += '<h4 class="font-semibold text-gray-900 mb-2">Langkah Manual:</h4>';
html += '<ol class="list-decimal list-inside space-y-1 text-sm text-gray-700">';
info.manual_steps.forEach(step => {
html += `<li>${step}</li>`;
});
html += '</ol></div>';
container.innerHTML = html;
})
.catch(error => {
console.error('Error loading download info:', error);
});
}
// Test TTS functionality
document.getElementById('test-tts-btn').addEventListener('click', function() {
const text = document.getElementById('test-text').value;
const button = this;
const resultDiv = document.getElementById('test-result');
const resultText = document.getElementById('test-result-text');
if (!text.trim()) {
Swal.fire({
icon: 'warning',
title: 'Text Kosong',
text: 'Masukkan text untuk test TTS'
});
return;
}
button.disabled = true;
button.textContent = 'Testing...';
fetch('{{ route('admin.indonesian-tts.test') }}', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content
},
body: JSON.stringify({
text: text
})
})
.then(response => response.json())
.then(data => {
resultDiv.classList.remove('hidden');
if (data.success) {
resultText.textContent = `TTS berhasil dibuat! Type: ${data.tts_type}`;
resultText.className = 'text-sm text-green-600';
if (data.audio_url) {
resultText.innerHTML +=
`<br><audio controls class="mt-2"><source src="${data.audio_url}" type="audio/wav">Your browser does not support the audio element.</audio>`;
}
} else {
resultText.textContent = `TTS gagal: ${data.message}`;
resultText.className = 'text-sm text-red-600';
}
})
.catch(error => {
resultDiv.classList.remove('hidden');
resultText.textContent = `Error: ${error.message}`;
resultText.className = 'text-sm text-red-600';
})
.finally(() => {
button.disabled = false;
button.textContent = 'Test TTS';
});
});
function confirmLogout() {
Swal.fire({
title: 'Konfirmasi Logout',
text: 'Apakah Anda yakin ingin keluar dari sistem?',
icon: 'question',
showCancelButton: true,
confirmButtonText: 'Ya, Logout',
cancelButtonText: 'Batal',
confirmButtonColor: '#EF4444',
cancelButtonColor: '#6B7280'
}).then((result) => {
if (result.isConfirmed) {
const form = document.createElement('form');
form.method = 'POST';
form.action = '{{ route('logout') }}';
const csrfToken = document.createElement('input');
csrfToken.type = 'hidden';
csrfToken.name = '_token';
csrfToken.value = '{{ csrf_token() }}';
form.appendChild(csrfToken);
document.body.appendChild(form);
form.submit();
}
});
}
</script>
@endpush
@endsection