Compare commits
3 Commits
8cf9e0b191
...
13f8f09524
| Author | SHA1 | Date | |
|---|---|---|---|
| 13f8f09524 | |||
| 351b9064af | |||
| edc843fa1c |
@@ -203,12 +203,22 @@ class InternApplicationResource extends Resource
|
|||||||
foreach ($days as $d) {
|
foreach ($days as $d) {
|
||||||
$dayNum = $d['day_number'];
|
$dayNum = $d['day_number'];
|
||||||
$dateF = $d['formatted_date'];
|
$dateF = $d['formatted_date'];
|
||||||
$content = isset($saved[$dayNum]) ? $saved[$dayNum]->content : '';
|
$entry = $saved->get($dayNum);
|
||||||
|
$content = $entry ? $entry->content : '';
|
||||||
|
$isRetro = $entry ? $entry->is_retroactive : false;
|
||||||
|
$updatedAt = $entry ? $entry->updated_at->format('d.m.Y H:i') : null;
|
||||||
|
|
||||||
$html .= '<div style="margin-bottom: 12px; padding: 12px; border: 1px solid #e2e8f0; border-radius: 8px; background: #f8fafc;">';
|
$html .= '<div style="margin-bottom: 12px; padding: 12px; border: 1px solid #e2e8f0; border-radius: 8px; background: #f8fafc;">';
|
||||||
$html .= ' <div style="display:flex; justify-content:between; font-size:11px; font-weight:700; color:#475569; border-bottom:1px solid #e2e8f0; padding-bottom:6px; margin-bottom:8px;">';
|
$html .= ' <div style="display:flex; justify-content:between; font-size:11px; font-weight:700; color:#475569; border-bottom:1px solid #e2e8f0; padding-bottom:6px; margin-bottom:8px;">';
|
||||||
$html .= ' <span style="font-weight: 800; color: #2563eb;">' . $dayNum . '. Gün Raporu</span>';
|
$html .= ' <span style="font-weight: 800; color: #2563eb;">' . $dayNum . '. Gün Raporu</span>';
|
||||||
|
if ($isRetro) {
|
||||||
|
$html .= ' <span style="margin-left: 10px; background: #fee2e2; color: #991b1b; padding: 1px 6px; border-radius: 4px; font-size: 9px; font-weight: 800;">GERİYE DÖNÜK KAYIT</span>';
|
||||||
|
}
|
||||||
|
if ($updatedAt) {
|
||||||
|
$html .= ' <span style="margin-left: auto;">Son Güncelleme: ' . $updatedAt . '</span>';
|
||||||
|
} else {
|
||||||
$html .= ' <span style="margin-left: auto;">' . $dateF . '</span>';
|
$html .= ' <span style="margin-left: auto;">' . $dateF . '</span>';
|
||||||
|
}
|
||||||
$html .= ' </div>';
|
$html .= ' </div>';
|
||||||
$html .= ' <div style="font-size:12px; color:#1e293b; white-space:pre-wrap; line-height:1.5;">' . ($content ? e($content) : '<em style="color:#94a3b8;">Rapor yazılmamış</em>') . '</div>';
|
$html .= ' <div style="font-size:12px; color:#1e293b; white-space:pre-wrap; line-height:1.5;">' . ($content ? e($content) : '<em style="color:#94a3b8;">Rapor yazılmamış</em>') . '</div>';
|
||||||
$html .= '</div>';
|
$html .= '</div>';
|
||||||
|
|||||||
@@ -472,21 +472,32 @@ class CareerController extends Controller
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
$dateObj = \Carbon\Carbon::parse($request->date);
|
$dateObj = \Carbon\Carbon::parse($request->date);
|
||||||
|
|
||||||
|
// 1. Prevent future entries
|
||||||
|
if ($dateObj->isFuture()) {
|
||||||
|
return response()->json(['success' => false, 'message' => 'İleriye dönük staj günleri için defter doldurulamaz.'], 422);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Prevent weekend entries
|
||||||
if ($dateObj->isWeekend()) {
|
if ($dateObj->isWeekend()) {
|
||||||
return response()->json(['success' => false, 'message' => 'Hafta sonu günlerine staj günlüğü girilemez.'], 422);
|
return response()->json(['success' => false, 'message' => 'Hafta sonu günlerine staj günlüğü girilemez.'], 422);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 3. Determine if retroactive
|
||||||
|
$isRetroactive = $dateObj->lt(\Carbon\Carbon::today());
|
||||||
|
|
||||||
$entry = \App\Models\InternshipJournalEntry::updateOrCreate([
|
$entry = \App\Models\InternshipJournalEntry::updateOrCreate([
|
||||||
'career_application_id' => $intern->id,
|
'career_application_id' => $intern->id,
|
||||||
'day_number' => $request->day_number,
|
'day_number' => $request->day_number,
|
||||||
'date' => $request->date,
|
'date' => $request->date,
|
||||||
], [
|
], [
|
||||||
'content' => $request->content,
|
'content' => $request->content,
|
||||||
|
'is_retroactive' => $isRetroactive,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => true,
|
'success' => true,
|
||||||
'message' => $request->day_number . '. Gün kaydı başarıyla kaydedildi.',
|
'message' => $request->day_number . '. Gün kaydı başarıyla kaydedildi.' . ($isRetroactive ? ' (Geriye Dönük Kayıt)' : ''),
|
||||||
'entry' => $entry
|
'entry' => $entry
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,11 @@ class InternshipJournalEntry extends Model
|
|||||||
'day_number',
|
'day_number',
|
||||||
'date',
|
'date',
|
||||||
'content',
|
'content',
|
||||||
|
'is_retroactive',
|
||||||
|
];
|
||||||
|
|
||||||
|
protected $casts = [
|
||||||
|
'is_retroactive' => 'boolean',
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+28
@@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('internship_journal_entries', function (Blueprint $table) {
|
||||||
|
$table->boolean('is_retroactive')->default(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('internship_journal_entries', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('is_retroactive');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -722,14 +722,20 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="space-y-4">
|
<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>
|
<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>
|
||||||
|
|
||||||
<div class="flex justify-between items-center">
|
<div class="flex justify-between items-center">
|
||||||
<div id="save-status" class="text-xs font-semibold text-slate-400"></div>
|
<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
|
Deftere Kaydet
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -789,10 +795,28 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
@push('styles')
|
@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 src="https://cdn.tailwindcss.com"></script>
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
|
|
||||||
|
let quill;
|
||||||
|
|
||||||
// Tab Switch Logic
|
// Tab Switch Logic
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
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 tabButtons = document.querySelectorAll('.tab-btn');
|
||||||
const tabPanels = document.querySelectorAll('.tab-panel');
|
const tabPanels = document.querySelectorAll('.tab-panel');
|
||||||
|
|
||||||
@@ -906,8 +930,40 @@
|
|||||||
|
|
||||||
document.getElementById('editor-day-title').textContent = `${dayNum}. Gün`;
|
document.getElementById('editor-day-title').textContent = `${dayNum}. Gün`;
|
||||||
document.getElementById('editor-day-date').textContent = dateFormatted;
|
document.getElementById('editor-day-date').textContent = dateFormatted;
|
||||||
document.getElementById('editor-content').value = savedContent;
|
|
||||||
|
quill.root.innerHTML = savedContent || '';
|
||||||
document.getElementById('save-status').textContent = '';
|
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 +979,26 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let commitText = "";
|
let commitHtml = "<ul>";
|
||||||
const sorted = [...dayCommits].reverse();
|
const sorted = [...dayCommits].reverse();
|
||||||
sorted.forEach(c => {
|
sorted.forEach(c => {
|
||||||
const msg = c.commit.message;
|
const msg = c.commit.message;
|
||||||
const sha = c.sha.substring(0, 7);
|
const sha = c.sha.substring(0, 7);
|
||||||
const authorDate = new Date(c.commit.author.date);
|
const authorDate = new Date(c.commit.author.date);
|
||||||
const time = authorDate.toLocaleTimeString('tr-TR', { hour: '2-digit', minute: '2-digit' });
|
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');
|
const currentHtml = quill.root.innerHTML.trim();
|
||||||
if (textarea.value.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.)")) {
|
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 {
|
} else {
|
||||||
textarea.value = commitText.trim();
|
quill.root.innerHTML = commitHtml;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
textarea.value = commitText.trim();
|
quill.root.innerHTML = commitHtml;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -951,7 +1008,10 @@
|
|||||||
|
|
||||||
const dayNum = btn.getAttribute('data-day-num');
|
const dayNum = btn.getAttribute('data-day-num');
|
||||||
const dateVal = btn.getAttribute('data-date');
|
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');
|
const statusText = document.getElementById('save-status');
|
||||||
|
|
||||||
statusText.textContent = "Kaydediliyor...";
|
statusText.textContent = "Kaydediliyor...";
|
||||||
|
|||||||
@@ -207,11 +207,25 @@
|
|||||||
background: #ffffff;
|
background: #ffffff;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
min-height: 250px;
|
min-height: 250px;
|
||||||
white-space: pre-wrap;
|
|
||||||
line-height: 1.6;
|
line-height: 1.6;
|
||||||
color: #334155;
|
color: #334155;
|
||||||
font-size: 13px;
|
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 {
|
.empty-content {
|
||||||
color: #94a3b8;
|
color: #94a3b8;
|
||||||
@@ -383,7 +397,7 @@
|
|||||||
<!-- Content Area -->
|
<!-- Content Area -->
|
||||||
<div class="content-area">
|
<div class="content-area">
|
||||||
@if($entry && trim($entry->content))
|
@if($entry && trim($entry->content))
|
||||||
{!! nl2br(e($entry->content)) !!}
|
{!! $entry->content !!}
|
||||||
@else
|
@else
|
||||||
<div class="empty-content">Bu gün için herhangi bir staj raporu girilmemiştir.</div>
|
<div class="empty-content">Bu gün için herhangi bir staj raporu girilmemiştir.</div>
|
||||||
@endif
|
@endif
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
@section('content')
|
@section('content')
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Cinzel:wght@500;700;800;900&family=Inter:wght@300;400;500;600;700&family=Playfair+Display:ital,wght@0,500;0,700;1,400&display=swap" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css2?family=Cinzel:wght@500;700;800;900&family=Inter:wght@300;400;500;600;700&family=Playfair+Display:ital,wght@0,500;0,700;1,400&display=swap" rel="stylesheet">
|
||||||
|
|
||||||
|
@if($application->status === 'accepted')
|
||||||
<div class="bg-slate-100 py-12 no-print">
|
<div class="bg-slate-100 py-12 no-print">
|
||||||
<div class="container max-w-6xl mx-auto px-4">
|
<div class="container max-w-6xl mx-auto px-4">
|
||||||
<!-- Verification Banner (e-Devlet Style) -->
|
<!-- Verification Banner (e-Devlet Style) -->
|
||||||
@@ -21,88 +22,9 @@
|
|||||||
<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">
|
<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>
|
<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 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>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- ================= PAGE 3: STAJ DEFTERİ (PORTRAIT A4) ================= -->
|
|
||||||
@if($application->notebook_approved || $application->notebook_supervisor_signed || $application->notebook_unit_signed)
|
|
||||||
<div class="notebook-container-wrapper overflow-hidden w-full flex justify-center items-start mt-8">
|
|
||||||
<div id="notebook-node" class="transcript-page bg-white rounded-[2rem] shadow-2xl p-6 md:p-8 relative overflow-hidden mx-auto">
|
|
||||||
|
|
||||||
<!-- Thin border decor for notebook -->
|
|
||||||
<div class="absolute inset-4 border border-slate-100 rounded-[1.5rem] pointer-events-none"></div>
|
|
||||||
|
|
||||||
<div class="relative z-10 flex flex-col justify-between h-full text-slate-700">
|
|
||||||
<div>
|
|
||||||
<!-- Header -->
|
|
||||||
<div class="flex justify-between items-center pb-2 border-b border-slate-100 mb-3">
|
|
||||||
<div>
|
|
||||||
<h2 class="font-serif font-bold text-slate-900 transcript-header-title">ONAYLI STAJ DEFTERİ RAPORU</h2>
|
|
||||||
<p class="text-slate-400 tracking-wider mt-0.5 transcript-header-subtitle">DİJİTAL İMZALI VE ONAYLI STAJ RAPORLARI</p>
|
|
||||||
</div>
|
|
||||||
<img src="{{ asset('logos/truncgil-yatay.svg') }}" alt="Trunçgil Logo" class="h-6 object-contain">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Signatures Information -->
|
|
||||||
<div class="bg-emerald-50 border border-emerald-200 rounded-xl p-3 mb-4 flex justify-between items-center">
|
|
||||||
<div>
|
|
||||||
<span class="text-xs font-extrabold text-emerald-800 uppercase block tracking-wider">Dijital İmza Durumu</span>
|
|
||||||
<span class="text-[10px] text-emerald-600 font-semibold block mt-0.5">Bu staj defteri yetkililer tarafından elektronik olarak imzalanmıştır.</span>
|
|
||||||
</div>
|
|
||||||
<div class="flex gap-2">
|
|
||||||
@if($application->notebook_supervisor_signed)
|
|
||||||
<span class="inline-flex px-2 py-1 rounded bg-emerald-100 text-emerald-800 text-[8px] font-extrabold uppercase border border-emerald-200">
|
|
||||||
Sorumlu: {{ $application->notebook_supervisor_name ?: 'Alperen Trunç' }}
|
|
||||||
</span>
|
|
||||||
@endif
|
|
||||||
@if($application->notebook_unit_signed)
|
|
||||||
<span class="inline-flex px-2 py-1 rounded bg-emerald-100 text-emerald-800 text-[8px] font-extrabold uppercase border border-emerald-200">
|
|
||||||
Yetkili: {{ $application->notebook_unit_name ?: 'Birim Yetkilisi' }}
|
|
||||||
</span>
|
|
||||||
@endif
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Notebook Days content overview -->
|
|
||||||
<div class="overflow-y-auto max-h-[700px] pr-2 no-scrollbar space-y-4">
|
|
||||||
@foreach($days as $day)
|
|
||||||
@php
|
|
||||||
$entry = $savedEntries->get($day['day_number']);
|
|
||||||
@endphp
|
|
||||||
@if($entry && trim($entry->content))
|
|
||||||
<div class="p-3 bg-slate-50 border border-slate-100 rounded-xl">
|
|
||||||
<div class="flex justify-between items-center pb-1.5 border-b border-slate-200/50 mb-1.5">
|
|
||||||
<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>
|
|
||||||
@endif
|
|
||||||
@endforeach
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Footer -->
|
|
||||||
<div class="w-full flex justify-between items-center pt-2 border-t border-slate-100 gap-4 mt-auto">
|
|
||||||
<div class="flex items-center gap-3">
|
|
||||||
<img src="https://api.qrserver.com/v1/create-qr-code/?size=120x120&data={{ urlencode(route('internship.verify', $application->certificate_code)) }}" alt="QR Code" class="w-14 h-14 border border-slate-200 p-0.5 rounded-lg bg-white">
|
|
||||||
<div>
|
|
||||||
<span class="font-bold text-slate-400 tracking-wider block verify-code-label">GÜVENLİ DOĞRULAMA KODU</span>
|
|
||||||
<span class="font-mono font-bold text-slate-800 block verify-code">{{ $application->certificate_code }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="text-right text-slate-400 footer-address">
|
|
||||||
Trunçgil Teknoloji San. ve Tic. Ltd. Şti.<br>
|
|
||||||
Gaziantep Üniversitesi Teknopark No: 4A
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@endif
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -242,8 +164,97 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- ================= PAGE 3: STAJ DEFTERİ (PORTRAIT A4) ================= -->
|
||||||
|
@if($application->notebook_approved || $application->notebook_supervisor_signed || $application->notebook_unit_signed)
|
||||||
|
<div class="notebook-container-wrapper overflow-hidden w-full flex justify-center items-start mt-8">
|
||||||
|
<div id="notebook-node" class="transcript-page bg-white rounded-[2rem] shadow-2xl p-6 md:p-8 relative overflow-hidden mx-auto">
|
||||||
|
|
||||||
|
<!-- Thin border decor for notebook -->
|
||||||
|
<div class="absolute inset-4 border border-slate-100 rounded-[1.5rem] pointer-events-none"></div>
|
||||||
|
|
||||||
|
<div class="relative z-10 flex flex-col justify-between h-full text-slate-700">
|
||||||
|
<div>
|
||||||
|
<!-- Header -->
|
||||||
|
<div class="flex justify-between items-center pb-2 border-b border-slate-100 mb-3">
|
||||||
|
<div>
|
||||||
|
<h2 class="font-serif font-bold text-slate-900 transcript-header-title">ONAYLI STAJ DEFTERİ RAPORU</h2>
|
||||||
|
<p class="text-slate-400 tracking-wider mt-0.5 transcript-header-subtitle">DİJİTAL İMZALI VE ONAYLI STAJ RAPORLARI</p>
|
||||||
|
</div>
|
||||||
|
<img src="{{ asset('logos/truncgil-yatay.svg') }}" alt="Trunçgil Logo" class="h-6 object-contain">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Signatures Information -->
|
||||||
|
<div class="bg-emerald-50 border border-emerald-200 rounded-xl p-3 mb-4 flex justify-between items-center">
|
||||||
|
<div>
|
||||||
|
<span class="text-xs font-extrabold text-emerald-800 uppercase block tracking-wider">Dijital İmza Durumu</span>
|
||||||
|
<span class="text-[10px] text-emerald-600 font-semibold block mt-0.5">Bu staj defteri yetkililer tarafından elektronik olarak imzalanmıştır.</span>
|
||||||
|
</div>
|
||||||
|
<div class="flex gap-2">
|
||||||
|
@if($application->notebook_supervisor_signed)
|
||||||
|
<span class="inline-flex px-2 py-1 rounded bg-emerald-100 text-emerald-800 text-[8px] font-extrabold uppercase border border-emerald-200">
|
||||||
|
Sorumlu: {{ $application->notebook_supervisor_name ?: 'Alperen Trunç' }}
|
||||||
|
</span>
|
||||||
|
@endif
|
||||||
|
@if($application->notebook_unit_signed)
|
||||||
|
<span class="inline-flex px-2 py-1 rounded bg-emerald-100 text-emerald-800 text-[8px] font-extrabold uppercase border border-emerald-200">
|
||||||
|
Yetkili: {{ $application->notebook_unit_name ?: 'Birim Yetkilisi' }}
|
||||||
|
</span>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Notebook Days content overview -->
|
||||||
|
<div class="overflow-y-auto max-h-[700px] pr-2 no-scrollbar space-y-4">
|
||||||
|
@foreach($days as $day)
|
||||||
|
@php
|
||||||
|
$entry = $savedEntries->get($day['day_number']);
|
||||||
|
@endphp
|
||||||
|
@if($entry && trim($entry->content))
|
||||||
|
<div class="p-3 bg-slate-50 border border-slate-100 rounded-xl">
|
||||||
|
<div class="flex justify-between items-center pb-1.5 border-b border-slate-200/50 mb-1.5">
|
||||||
|
<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>
|
||||||
|
<div class="text-xs text-slate-600 leading-relaxed verify-log-content" style="font-size: 11px;">{!! $entry->content !!}</div>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
@endforeach
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Footer -->
|
||||||
|
<div class="w-full flex justify-between items-center pt-2 border-t border-slate-100 gap-4 mt-auto">
|
||||||
|
<div class="flex items-center gap-3">
|
||||||
|
<img src="https://api.qrserver.com/v1/create-qr-code/?size=120x120&data={{ urlencode(route('internship.verify', $application->certificate_code)) }}" alt="QR Code" class="w-14 h-14 border border-slate-200 p-0.5 rounded-lg bg-white">
|
||||||
|
<div>
|
||||||
|
<span class="font-bold text-slate-400 tracking-wider block verify-code-label">GÜVENLİ DOĞRULAMA KODU</span>
|
||||||
|
<span class="font-mono font-bold text-slate-800 block verify-code">{{ $application->certificate_code }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="text-right text-slate-400 footer-address">
|
||||||
|
Trunçgil Teknoloji San. ve Tic. Ltd. Şti.<br>
|
||||||
|
Gaziantep Üniversitesi Teknopark No: 4A
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@else
|
||||||
|
<div class="min-h-screen bg-slate-100 flex items-center justify-center p-6 no-print">
|
||||||
|
<div class="bg-white rounded-3xl p-8 max-w-md w-full shadow-2xl border border-slate-100 text-center">
|
||||||
|
<div class="w-16 h-16 rounded-full bg-red-50 text-[#e31e24] flex items-center justify-center text-3xl mx-auto mb-4">
|
||||||
|
<i class="uil uil-exclamation-triangle"></i>
|
||||||
|
</div>
|
||||||
|
<h1 class="text-xl font-bold text-slate-800">Doğrulanamayan Belge</h1>
|
||||||
|
<p class="text-xs text-slate-500 mt-2 leading-relaxed">Görüntülemeye çalıştığınız staj kaydı henüz onaylanmamıştır veya aktif değildir. Detaylı bilgi için kurum yetkilileri ile iletişime geçebilirsiniz.</p>
|
||||||
|
<a href="/" class="mt-6 inline-flex px-5 py-2.5 bg-slate-800 hover:bg-slate-900 text-white font-bold rounded-xl text-xs transition-all shadow-md">Anasayfaya Dön</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
@push('styles')
|
@push('styles')
|
||||||
<script src="https://cdn.tailwindcss.com"></script>
|
<script src="https://cdn.tailwindcss.com"></script>
|
||||||
@@ -254,6 +265,21 @@
|
|||||||
/* Global scale factor for transcript page fonts. Adjust this easily (e.g. 1.0, 1.10, 1.20) */
|
/* Global scale factor for transcript page fonts. Adjust this easily (e.g. 1.0, 1.10, 1.20) */
|
||||||
--transcript-font-scale: 1.30;
|
--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 {
|
body {
|
||||||
font-family: 'Inter', sans-serif;
|
font-family: 'Inter', sans-serif;
|
||||||
|
|||||||
+383
@@ -0,0 +1,383 @@
|
|||||||
|
|
||||||
|
// Tab Switch Logic
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
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');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
// Fetch Github commits in memory if repo is set
|
||||||
|
fetchAllCommitsInMemory();
|
||||||
|
|
||||||
|
// Select first day of the notebook workspace on load
|
||||||
|
selectNotebookDay(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
const githubRepoUrl = "dummy";
|
||||||
|
let allGithubCommits = {};
|
||||||
|
let activeDayIdx = 0;
|
||||||
|
|
||||||
|
function fetchAllCommitsInMemory() {
|
||||||
|
if (!githubRepoUrl) return;
|
||||||
|
|
||||||
|
let repoClean = githubRepoUrl.replace(/https?:\/\/(www\.)?github\.com\//i, '');
|
||||||
|
repoClean = repoClean.replace(/\/$/, '');
|
||||||
|
const parts = repoClean.split('/');
|
||||||
|
if (parts.length < 2) return;
|
||||||
|
|
||||||
|
const owner = parts[0];
|
||||||
|
const repo = parts[1].replace(/\.git$/i, '');
|
||||||
|
|
||||||
|
fetch(`https://api.github.com/repos/${owner}/${repo}/commits?per_page=100`)
|
||||||
|
.then(response => {
|
||||||
|
if (!response.ok) throw new Error('API Error');
|
||||||
|
return response.json();
|
||||||
|
})
|
||||||
|
.then(commits => {
|
||||||
|
if (!Array.isArray(commits)) return;
|
||||||
|
commits.forEach(item => {
|
||||||
|
const dateStr = item.commit.author.date;
|
||||||
|
if (dateStr) {
|
||||||
|
const dateOnly = dateStr.substring(0, 10);
|
||||||
|
if (!allGithubCommits[dateOnly]) {
|
||||||
|
allGithubCommits[dateOnly] = [];
|
||||||
|
}
|
||||||
|
allGithubCommits[dateOnly].push(item);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.warn('Commits could not be pre-loaded in memory.', err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectNotebookDay(idx) {
|
||||||
|
const oldBtn = document.getElementById(`btn-day-${activeDayIdx}`);
|
||||||
|
if (oldBtn) {
|
||||||
|
oldBtn.classList.remove('border-blue-200', 'bg-blue-50/50');
|
||||||
|
oldBtn.classList.add('border-slate-100', 'bg-white');
|
||||||
|
const oldTitle = oldBtn.querySelector('span.text-xs');
|
||||||
|
if (oldTitle) {
|
||||||
|
oldTitle.classList.remove('text-blue-700');
|
||||||
|
oldTitle.classList.add('text-slate-800');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
activeDayIdx = idx;
|
||||||
|
const newBtn = document.getElementById(`btn-day-${idx}`);
|
||||||
|
if (newBtn) {
|
||||||
|
newBtn.classList.add('border-blue-200', 'bg-blue-50/50');
|
||||||
|
newBtn.classList.remove('border-slate-100', 'bg-white');
|
||||||
|
const newTitle = newBtn.querySelector('span.text-xs');
|
||||||
|
if (newTitle) {
|
||||||
|
newTitle.classList.add('text-blue-700');
|
||||||
|
newTitle.classList.remove('text-slate-800');
|
||||||
|
}
|
||||||
|
|
||||||
|
const dayNum = newBtn.getAttribute('data-day-num');
|
||||||
|
const dateVal = newBtn.getAttribute('data-date');
|
||||||
|
const dateFormatted = newBtn.getAttribute('data-formatted-date');
|
||||||
|
const savedContent = newBtn.getAttribute('data-saved-content');
|
||||||
|
|
||||||
|
document.getElementById('editor-day-title').textContent = `${dayNum}. Gün`;
|
||||||
|
document.getElementById('editor-day-date').textContent = dateFormatted;
|
||||||
|
|
||||||
|
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');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function fillFromGithub() {
|
||||||
|
const btn = document.getElementById(`btn-day-${activeDayIdx}`);
|
||||||
|
if (!btn) return;
|
||||||
|
|
||||||
|
const dateVal = btn.getAttribute('data-date');
|
||||||
|
const dayCommits = allGithubCommits[dateVal] || [];
|
||||||
|
|
||||||
|
if (dayCommits.length === 0) {
|
||||||
|
alert("Bu staj gününe ait GitHub commit'i bulunamadı. Lütfen commit attığınızdan ve tarihin doğru olduğundan emin olun.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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' });
|
||||||
|
commitHtml += `<li><strong>[${time}]</strong> (<em>${sha}</em>) ${msg}</li>`;
|
||||||
|
});
|
||||||
|
commitHtml += "</ul>";
|
||||||
|
|
||||||
|
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.)")) {
|
||||||
|
quill.root.innerHTML = currentHtml + "<br><br>" + commitHtml;
|
||||||
|
} else {
|
||||||
|
quill.root.innerHTML = commitHtml;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
quill.root.innerHTML = commitHtml;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveActiveDay() {
|
||||||
|
const btn = document.getElementById(`btn-day-${activeDayIdx}`);
|
||||||
|
if (!btn) return;
|
||||||
|
|
||||||
|
const dayNum = btn.getAttribute('data-day-num');
|
||||||
|
const dateVal = btn.getAttribute('data-date');
|
||||||
|
let contentVal = quill.root.innerHTML;
|
||||||
|
if (contentVal === '<p><br></p>') {
|
||||||
|
contentVal = '';
|
||||||
|
}
|
||||||
|
const statusText = document.getElementById('save-status');
|
||||||
|
|
||||||
|
statusText.textContent = "Kaydediliyor...";
|
||||||
|
statusText.className = "text-xs font-semibold text-slate-400 animate-pulse";
|
||||||
|
|
||||||
|
fetch(""dummy"", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"X-CSRF-TOKEN": ""dummy""
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
day_number: dayNum,
|
||||||
|
date: dateVal,
|
||||||
|
content: contentVal
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.success) {
|
||||||
|
statusText.textContent = "Başarıyla kaydedildi.";
|
||||||
|
statusText.className = "text-xs font-semibold text-green-600";
|
||||||
|
|
||||||
|
btn.setAttribute('data-saved-content', contentVal);
|
||||||
|
|
||||||
|
const badge = document.getElementById(`badge-day-${activeDayIdx}`);
|
||||||
|
if (badge) {
|
||||||
|
if (contentVal.trim() !== '') {
|
||||||
|
badge.textContent = 'DOLU';
|
||||||
|
badge.className = "inline-flex px-2 py-0.5 rounded-lg text-[9px] font-extrabold uppercase bg-green-50 text-green-700 border border-green-200";
|
||||||
|
} else {
|
||||||
|
badge.textContent = 'BOŞ';
|
||||||
|
badge.className = "inline-flex px-2 py-0.5 rounded-lg text-[9px] font-extrabold uppercase bg-slate-50 text-slate-400 border border-slate-200";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
statusText.textContent = data.message || "Kaydedilemedi.";
|
||||||
|
statusText.className = "text-xs font-semibold text-red-600";
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.error(err);
|
||||||
|
statusText.textContent = "Bağlantı hatası oluştu.";
|
||||||
|
statusText.className = "text-xs font-semibold text-red-600";
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function openPrintModal() {
|
||||||
|
const modal = document.getElementById('print-modal');
|
||||||
|
const card = document.getElementById('print-modal-card');
|
||||||
|
modal.classList.remove('hidden');
|
||||||
|
setTimeout(() => {
|
||||||
|
card.classList.remove('scale-95', 'opacity-0');
|
||||||
|
card.classList.add('scale-100', 'opacity-100');
|
||||||
|
}, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
function closePrintModal() {
|
||||||
|
const modal = document.getElementById('print-modal');
|
||||||
|
const card = document.getElementById('print-modal-card');
|
||||||
|
card.classList.remove('scale-100', 'opacity-100');
|
||||||
|
card.classList.add('scale-95', 'opacity-0');
|
||||||
|
setTimeout(() => {
|
||||||
|
modal.classList.add('hidden');
|
||||||
|
}, 300);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleFileSelected(input) {
|
||||||
|
const fileNameElement = document.getElementById('selected-file-name');
|
||||||
|
const submitBtn = document.getElementById('submit-upload-btn');
|
||||||
|
if (input.files && input.files.length > 0) {
|
||||||
|
const file = input.files[0];
|
||||||
|
const fileSizeMB = file.size / (1024 * 1024);
|
||||||
|
if (fileSizeMB > 50) {
|
||||||
|
alert('Dosya boyutu 50MB\'ı aşamaz.');
|
||||||
|
input.value = '';
|
||||||
|
fileNameElement.textContent = '';
|
||||||
|
submitBtn.disabled = true;
|
||||||
|
submitBtn.classList.add('opacity-50', 'cursor-not-allowed');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fileNameElement.textContent = 'Seçilen dosya: ' + file.name + ' (' + fileSizeMB.toFixed(2) + ' MB)';
|
||||||
|
submitBtn.disabled = false;
|
||||||
|
submitBtn.classList.remove('opacity-50', 'cursor-not-allowed');
|
||||||
|
} else {
|
||||||
|
fileNameElement.textContent = '';
|
||||||
|
submitBtn.disabled = true;
|
||||||
|
submitBtn.classList.add('opacity-50', 'cursor-not-allowed');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function calculateEndDateFrontend() {
|
||||||
|
const startDateVal = document.getElementById('internship_start_date').value;
|
||||||
|
const totalDaysVal = document.getElementById('internship_total_days').value;
|
||||||
|
const endDateInput = document.getElementById('internship_end_date');
|
||||||
|
|
||||||
|
if (!startDateVal || !totalDaysVal || totalDaysVal <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let date = new Date(startDateVal);
|
||||||
|
let daysToAdd = parseInt(totalDaysVal);
|
||||||
|
let count = 0;
|
||||||
|
let endDate = new Date(startDateVal);
|
||||||
|
|
||||||
|
while (count < daysToAdd) {
|
||||||
|
const dayOfWeek = date.getDay();
|
||||||
|
if (dayOfWeek === 6 || dayOfWeek === 0) { // 6 = Saturday, 0 = Sunday
|
||||||
|
date.setDate(date.getDate() + 1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
endDate = new Date(date);
|
||||||
|
date.setDate(date.getDate() + 1);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format to YYYY-MM-DD
|
||||||
|
const yyyy = endDate.getFullYear();
|
||||||
|
const mm = String(endDate.getMonth() + 1).padStart(2, '0');
|
||||||
|
const dd = String(endDate.getDate()).padStart(2, '0');
|
||||||
|
endDateInput.value = `${yyyy}-${mm}-${dd}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function copyToClipboard(elementId, btn) {
|
||||||
|
const text = document.getElementById(elementId).textContent;
|
||||||
|
navigator.clipboard.writeText(text).then(() => {
|
||||||
|
const icon = btn.querySelector('i');
|
||||||
|
const originalClass = icon.className;
|
||||||
|
icon.className = 'uil uil-check text-green-500';
|
||||||
|
setTimeout(() => {
|
||||||
|
icon.className = originalClass;
|
||||||
|
}, 2000);
|
||||||
|
});
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user