feat: integrate Quill editor for internship journal entries, add retroactive field support, and enforce future date restrictions
This commit is contained in:
@@ -722,14 +722,20 @@
|
||||
</div>
|
||||
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<div id="future-warning" class="hidden p-4 bg-amber-50/50 border border-amber-200 rounded-2xl text-xs text-amber-700 font-semibold leading-relaxed">
|
||||
<i class="uil uil-exclamation-triangle text-sm mr-1"></i> Bu staj günü henüz gerçekleşmediği için staj günlüğü yazılamaz veya düzenlenemez.
|
||||
</div>
|
||||
|
||||
<div id="editor-wrapper">
|
||||
<label class="block text-xs font-extrabold text-slate-500 uppercase tracking-wider mb-2">Günlük Çalışma Raporu</label>
|
||||
<textarea id="editor-content" rows="10" placeholder="Bu staj gününde yaptığınız çalışmaları detaylıca yazın..." class="w-full px-4 py-3 rounded-xl border border-slate-200 focus:border-blue-400 focus:ring-1 focus:ring-blue-400 focus:outline-none text-sm font-semibold text-slate-700 bg-white transition-all"></textarea>
|
||||
<div id="quill-editor-container" class="rounded-xl border border-slate-200 overflow-hidden">
|
||||
<div id="editor-content-quill" style="height: 250px;"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-between items-center">
|
||||
<div id="save-status" class="text-xs font-semibold text-slate-400"></div>
|
||||
<button type="button" onclick="saveActiveDay()" class="px-5 py-2.5 bg-blue-600 hover:bg-blue-700 text-white font-bold rounded-xl text-xs transition-all shadow-md shadow-blue-500/10">
|
||||
<button type="button" id="save-btn" onclick="saveActiveDay()" class="px-5 py-2.5 bg-blue-600 hover:bg-blue-700 text-white font-bold rounded-xl text-xs transition-all shadow-md shadow-blue-500/10">
|
||||
Deftere Kaydet
|
||||
</button>
|
||||
</div>
|
||||
@@ -789,6 +795,8 @@
|
||||
</div>
|
||||
|
||||
@push('styles')
|
||||
<link href="https://cdn.jsdelivr.net/npm/quill@2.0.2/dist/quill.snow.css" rel="stylesheet" />
|
||||
<script src="https://cdn.jsdelivr.net/npm/quill@2.0.2/dist/quill.js"></script>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<script>
|
||||
// Tab Switch Logic
|
||||
@@ -828,6 +836,57 @@
|
||||
});
|
||||
});
|
||||
|
||||
let quill;
|
||||
|
||||
// Tab Switch Logic
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Initialize Quill editor
|
||||
quill = new Quill('#editor-content-quill', {
|
||||
theme: 'snow',
|
||||
modules: {
|
||||
toolbar: [
|
||||
['bold', 'italic', 'underline', 'strike'], // toggled buttons
|
||||
[{ 'list': 'ordered'}, { 'list': 'bullet' }],
|
||||
['clean'] // remove formatting button
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
const tabButtons = document.querySelectorAll('.tab-btn');
|
||||
const tabPanels = document.querySelectorAll('.tab-panel');
|
||||
|
||||
tabButtons.forEach(button => {
|
||||
button.addEventListener('click', () => {
|
||||
// Deactivate active tab
|
||||
tabButtons.forEach(btn => {
|
||||
btn.classList.remove('active', 'bg-blue-50', 'border-blue-100', 'text-blue-900');
|
||||
btn.classList.add('bg-transparent', 'border-transparent', 'text-slate-600');
|
||||
btn.querySelector('.tab-icon').classList.remove('bg-blue-600', 'text-white');
|
||||
btn.querySelector('.tab-icon').classList.add('bg-slate-50', 'text-slate-500');
|
||||
btn.setAttribute('aria-selected', 'false');
|
||||
});
|
||||
|
||||
// Hide active panels
|
||||
tabPanels.forEach(panel => {
|
||||
panel.classList.add('hidden');
|
||||
});
|
||||
|
||||
// Activate clicked tab
|
||||
button.classList.add('active', 'bg-blue-50', 'border-blue-100', 'text-blue-900');
|
||||
button.classList.remove('bg-transparent', 'border-transparent', 'text-slate-600');
|
||||
button.querySelector('.tab-icon').classList.add('bg-blue-600', 'text-white');
|
||||
button.querySelector('.tab-icon').classList.remove('bg-slate-50', 'text-slate-500');
|
||||
button.setAttribute('aria-selected', 'true');
|
||||
|
||||
// Show targets panel
|
||||
const targetId = button.getAttribute('data-tab-target');
|
||||
const targetPanel = document.getElementById(targetId + '-panel');
|
||||
if (targetPanel) {
|
||||
targetPanel.classList.remove('hidden');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Handle initial state of date logic
|
||||
calculateEndDateFrontend();
|
||||
|
||||
@@ -906,8 +965,40 @@
|
||||
|
||||
document.getElementById('editor-day-title').textContent = `${dayNum}. Gün`;
|
||||
document.getElementById('editor-day-date').textContent = dateFormatted;
|
||||
document.getElementById('editor-content').value = savedContent;
|
||||
|
||||
quill.root.innerHTML = savedContent || '';
|
||||
document.getElementById('save-status').textContent = '';
|
||||
|
||||
// Future validation
|
||||
const dateParts = dateVal.split('-');
|
||||
const selectedDate = new Date(dateParts[0], dateParts[1] - 1, dateParts[2]);
|
||||
const today = new Date();
|
||||
today.setHours(0,0,0,0);
|
||||
|
||||
const isFuture = selectedDate > today;
|
||||
const warningBlock = document.getElementById('future-warning');
|
||||
const saveBtn = document.getElementById('save-btn');
|
||||
const gitBtn = document.querySelector('button[onclick="fillFromGithub()"]');
|
||||
|
||||
if (isFuture) {
|
||||
warningBlock.classList.remove('hidden');
|
||||
quill.enable(false);
|
||||
saveBtn.disabled = true;
|
||||
saveBtn.classList.add('opacity-50', 'cursor-not-allowed');
|
||||
if (gitBtn) {
|
||||
gitBtn.disabled = true;
|
||||
gitBtn.classList.add('opacity-50', 'cursor-not-allowed');
|
||||
}
|
||||
} else {
|
||||
warningBlock.classList.add('hidden');
|
||||
quill.enable(true);
|
||||
saveBtn.disabled = false;
|
||||
saveBtn.classList.remove('opacity-50', 'cursor-not-allowed');
|
||||
if (gitBtn) {
|
||||
gitBtn.disabled = false;
|
||||
gitBtn.classList.remove('opacity-50', 'cursor-not-allowed');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -923,25 +1014,26 @@
|
||||
return;
|
||||
}
|
||||
|
||||
let commitText = "";
|
||||
let commitHtml = "<ul>";
|
||||
const sorted = [...dayCommits].reverse();
|
||||
sorted.forEach(c => {
|
||||
const msg = c.commit.message;
|
||||
const sha = c.sha.substring(0, 7);
|
||||
const authorDate = new Date(c.commit.author.date);
|
||||
const time = authorDate.toLocaleTimeString('tr-TR', { hour: '2-digit', minute: '2-digit' });
|
||||
commitText += `- [${time}] (${sha}) ${msg}\n`;
|
||||
commitHtml += `<li><strong>[${time}]</strong> (<em>${sha}</em>) ${msg}</li>`;
|
||||
});
|
||||
commitHtml += "</ul>";
|
||||
|
||||
const textarea = document.getElementById('editor-content');
|
||||
if (textarea.value.trim() !== "") {
|
||||
const currentHtml = quill.root.innerHTML.trim();
|
||||
if (currentHtml !== "" && currentHtml !== "<p><br></p>") {
|
||||
if (confirm("Mevcut rapor içeriğinizin sonuna bu günün commitlerini eklemek ister misiniz? (Hayır derseniz mevcut içerik silinip commitler yazılır.)")) {
|
||||
textarea.value = textarea.value.trim() + "\n\n" + commitText.trim();
|
||||
quill.root.innerHTML = currentHtml + "<br><br>" + commitHtml;
|
||||
} else {
|
||||
textarea.value = commitText.trim();
|
||||
quill.root.innerHTML = commitHtml;
|
||||
}
|
||||
} else {
|
||||
textarea.value = commitText.trim();
|
||||
quill.root.innerHTML = commitHtml;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -951,7 +1043,10 @@
|
||||
|
||||
const dayNum = btn.getAttribute('data-day-num');
|
||||
const dateVal = btn.getAttribute('data-date');
|
||||
const contentVal = document.getElementById('editor-content').value;
|
||||
let contentVal = quill.root.innerHTML;
|
||||
if (contentVal === '<p><br></p>') {
|
||||
contentVal = '';
|
||||
}
|
||||
const statusText = document.getElementById('save-status');
|
||||
|
||||
statusText.textContent = "Kaydediliyor...";
|
||||
|
||||
@@ -207,11 +207,25 @@
|
||||
background: #ffffff;
|
||||
margin-bottom: 20px;
|
||||
min-height: 250px;
|
||||
white-space: pre-wrap;
|
||||
line-height: 1.6;
|
||||
color: #334155;
|
||||
font-size: 13px;
|
||||
}
|
||||
.content-area ul {
|
||||
list-style-type: disc !important;
|
||||
padding-left: 20px !important;
|
||||
margin-top: 8px !important;
|
||||
margin-bottom: 8px !important;
|
||||
}
|
||||
.content-area ol {
|
||||
list-style-type: decimal !important;
|
||||
padding-left: 20px !important;
|
||||
margin-top: 8px !important;
|
||||
margin-bottom: 8px !important;
|
||||
}
|
||||
.content-area li {
|
||||
margin-bottom: 4px !important;
|
||||
}
|
||||
|
||||
.empty-content {
|
||||
color: #94a3b8;
|
||||
@@ -383,7 +397,7 @@
|
||||
<!-- Content Area -->
|
||||
<div class="content-area">
|
||||
@if($entry && trim($entry->content))
|
||||
{!! nl2br(e($entry->content)) !!}
|
||||
{!! $entry->content !!}
|
||||
@else
|
||||
<div class="empty-content">Bu gün için herhangi bir staj raporu girilmemiştir.</div>
|
||||
@endif
|
||||
|
||||
@@ -22,11 +22,8 @@
|
||||
<button onclick="downloadPDF()" id="pdf-btn" class="px-4 py-2 bg-orange-600 hover:bg-orange-700 text-white text-sm font-bold rounded-lg transition-all shadow-sm flex items-center gap-1.5 whitespace-nowrap">
|
||||
<i class="uil uil-file-download text-base" id="pdf-icon"></i>
|
||||
<span class="spinner-border spinner-border-sm hidden animate-spin w-3.5 h-3.5 border-2 border-white border-t-transparent rounded-full" id="pdf-spinner" role="status"></span>
|
||||
<span id="pdf-btn-text">PDF İndir</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@@ -218,7 +215,7 @@
|
||||
<span class="text-xs font-extrabold text-slate-800">{{ $day['day_number'] }}. Gün Raporu</span>
|
||||
<span class="text-[10px] text-slate-400 font-bold">{{ $day['formatted_date'] }}</span>
|
||||
</div>
|
||||
<p class="text-xs text-slate-600 whitespace-pre-wrap leading-relaxed" style="font-size: 11px;">{{ $entry->content }}</p>
|
||||
<div class="text-xs text-slate-600 leading-relaxed verify-log-content" style="font-size: 11px;">{!! $entry->content !!}</div>
|
||||
</div>
|
||||
@endif
|
||||
@endforeach
|
||||
@@ -268,6 +265,21 @@
|
||||
/* Global scale factor for transcript page fonts. Adjust this easily (e.g. 1.0, 1.10, 1.20) */
|
||||
--transcript-font-scale: 1.30;
|
||||
}
|
||||
.verify-log-content ul {
|
||||
list-style-type: disc !important;
|
||||
padding-left: 18px !important;
|
||||
margin-top: 5px !important;
|
||||
margin-bottom: 5px !important;
|
||||
}
|
||||
.verify-log-content ol {
|
||||
list-style-type: decimal !important;
|
||||
padding-left: 18px !important;
|
||||
margin-top: 5px !important;
|
||||
margin-bottom: 5px !important;
|
||||
}
|
||||
.verify-log-content li {
|
||||
margin-bottom: 3px !important;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Inter', sans-serif;
|
||||
|
||||
Reference in New Issue
Block a user