refactor: simplify internship approval process by removing unit approval and adding supervisor name tracking
This commit is contained in:
@@ -232,30 +232,20 @@ class InternApplicationResource extends Resource
|
|||||||
|
|
||||||
if ($entry && trim($content) !== '') {
|
if ($entry && trim($content) !== '') {
|
||||||
$supApproved = $entry->supervisor_approved;
|
$supApproved = $entry->supervisor_approved;
|
||||||
$unitApproved = $entry->unit_approved;
|
$supName = $entry->supervisor_name;
|
||||||
|
|
||||||
$html .= ' <div style="display:flex; align-items:center; gap:12px; margin-top:12px; padding-top:10px; border-top:1px dashed #e2e8f0; font-size:11px;">';
|
$html .= ' <div style="display:flex; align-items:center; gap:12px; margin-top:12px; padding-top:10px; border-top:1px dashed #e2e8f0; font-size:11px;">';
|
||||||
|
|
||||||
// Supervisor approval
|
// Supervisor approval
|
||||||
$supBg = $supApproved ? '#d1fae5' : '#f1f5f9';
|
$supBg = $supApproved ? '#d1fae5' : '#f1f5f9';
|
||||||
$supColor = $supApproved ? '#065f46' : '#64748b';
|
$supColor = $supApproved ? '#065f46' : '#64748b';
|
||||||
$supText = $supApproved ? 'Sorumlu Onayladı' : 'Sorumlu Onayı Bekliyor';
|
$supText = $supApproved ? 'Sorumlu Onayladı' . ($supName ? ' (Onaylayan: ' . e($supName) . ')' : '') : 'Sorumlu Onayı Bekliyor';
|
||||||
$html .= ' <span id="sup-badge-' . $entry->id . '" style="background:' . $supBg . '; color:' . $supColor . '; padding: 2px 8px; border-radius: 4px; font-weight: 700;">' . $supText . '</span>';
|
$html .= ' <span id="sup-badge-' . $entry->id . '" style="background:' . $supBg . '; color:' . $supColor . '; padding: 2px 8px; border-radius: 4px; font-weight: 700;">' . $supText . '</span>';
|
||||||
|
|
||||||
// Unit approval
|
|
||||||
$unitBg = $unitApproved ? '#d1fae5' : '#f1f5f9';
|
|
||||||
$unitColor = $unitApproved ? '#065f46' : '#64748b';
|
|
||||||
$unitText = $unitApproved ? 'Birim Yetkilisi Onayladı' : 'Birim Yetkilisi Onayı Bekliyor';
|
|
||||||
$html .= ' <span id="unit-badge-' . $entry->id . '" style="background:' . $unitBg . '; color:' . $unitColor . '; padding: 2px 8px; border-radius: 4px; font-weight: 700;">' . $unitText . '</span>';
|
|
||||||
|
|
||||||
// Buttons
|
// Buttons
|
||||||
$supBtnText = $supApproved ? 'Onayı Kaldır' : 'Onayla';
|
$supBtnText = $supApproved ? 'Onayı Kaldır' : 'Onayla';
|
||||||
$supBtnBg = $supApproved ? '#ef4444' : '#2563eb';
|
$supBtnBg = $supApproved ? '#ef4444' : '#2563eb';
|
||||||
$html .= ' <button type="button" onclick="toggleApproval(' . $entry->id . ', \'supervisor\', this)" style="margin-left:auto; background:' . $supBtnBg . '; color:white; border:none; padding:4px 10px; border-radius:6px; font-weight:bold; cursor:pointer; font-size:10px;">Sorumlu ' . $supBtnText . '</button>';
|
$html .= ' <button type="button" onclick="toggleApproval(' . $entry->id . ', this)" style="margin-left:auto; background:' . $supBtnBg . '; color:white; border:none; padding:4px 10px; border-radius:6px; font-weight:bold; cursor:pointer; font-size:10px;">Sorumlu ' . $supBtnText . '</button>';
|
||||||
|
|
||||||
$unitBtnText = $unitApproved ? 'Onayı Kaldır' : 'Onayla';
|
|
||||||
$unitBtnBg = $unitApproved ? '#ef4444' : '#2563eb';
|
|
||||||
$html .= ' <button type="button" onclick="toggleApproval(' . $entry->id . ', \'unit\', this)" style="background:' . $unitBtnBg . '; color:white; border:none; padding:4px 10px; border-radius:6px; font-weight:bold; cursor:pointer; font-size:10px;">Yetkili ' . $unitBtnText . '</button>';
|
|
||||||
|
|
||||||
$html .= ' </div>';
|
$html .= ' </div>';
|
||||||
}
|
}
|
||||||
@@ -268,7 +258,7 @@ class InternApplicationResource extends Resource
|
|||||||
$html .= '
|
$html .= '
|
||||||
<script>
|
<script>
|
||||||
if (typeof window.toggleApproval !== "function") {
|
if (typeof window.toggleApproval !== "function") {
|
||||||
window.toggleApproval = function(entryId, type, btn) {
|
window.toggleApproval = function(entryId, btn) {
|
||||||
btn.disabled = true;
|
btn.disabled = true;
|
||||||
btn.style.opacity = "0.5";
|
btn.style.opacity = "0.5";
|
||||||
|
|
||||||
@@ -279,43 +269,25 @@ class InternApplicationResource extends Resource
|
|||||||
"X-CSRF-TOKEN": "' . csrf_token() . '"
|
"X-CSRF-TOKEN": "' . csrf_token() . '"
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
entry_id: entryId,
|
entry_id: entryId
|
||||||
type: type
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
if (type === "supervisor") {
|
const badge = document.getElementById("sup-badge-" + entryId);
|
||||||
const badge = document.getElementById("sup-badge-" + entryId);
|
if (data.status) {
|
||||||
if (data.status) {
|
badge.style.background = "#d1fae5";
|
||||||
badge.style.background = "#d1fae5";
|
badge.style.color = "#065f46";
|
||||||
badge.style.color = "#065f46";
|
badge.textContent = "Sorumlu Onayladı (Onaylayan: " + data.supervisor_name + ")";
|
||||||
badge.textContent = "Sorumlu Onayladı";
|
btn.textContent = "Sorumlu Onayı Kaldır";
|
||||||
btn.textContent = "Sorumlu Onayı Kaldır";
|
btn.style.background = "#ef4444";
|
||||||
btn.style.background = "#ef4444";
|
|
||||||
} else {
|
|
||||||
badge.style.background = "#f1f5f9";
|
|
||||||
badge.style.color = "#64748b";
|
|
||||||
badge.textContent = "Sorumlu Onayı Bekliyor";
|
|
||||||
btn.textContent = "Sorumlu Onayla";
|
|
||||||
btn.style.background = "#2563eb";
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
const badge = document.getElementById("unit-badge-" + entryId);
|
badge.style.background = "#f1f5f9";
|
||||||
if (data.status) {
|
badge.style.color = "#64748b";
|
||||||
badge.style.background = "#d1fae5";
|
badge.textContent = "Sorumlu Onayı Bekliyor";
|
||||||
badge.style.color = "#065f46";
|
btn.textContent = "Sorumlu Onayla";
|
||||||
badge.textContent = "Birim Yetkilisi Onayladı";
|
btn.style.background = "#2563eb";
|
||||||
btn.textContent = "Yetkili Onayı Kaldır";
|
|
||||||
btn.style.background = "#ef4444";
|
|
||||||
} else {
|
|
||||||
badge.style.background = "#f1f5f9";
|
|
||||||
badge.style.color = "#64748b";
|
|
||||||
badge.textContent = "Birim Yetkilisi Onayı Bekliyor";
|
|
||||||
btn.textContent = "Yetkili Onayla";
|
|
||||||
btn.style.background = "#2563eb";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
alert(data.message || "Bir hata oluştu.");
|
alert(data.message || "Bir hata oluştu.");
|
||||||
@@ -353,13 +325,6 @@ class InternApplicationResource extends Resource
|
|||||||
->label('Staj Sorumlusu Adı / Ünvanı')
|
->label('Staj Sorumlusu Adı / Ünvanı')
|
||||||
->placeholder('Örn: Alperen Trunç')
|
->placeholder('Örn: Alperen Trunç')
|
||||||
->default('Alperen Trunç'),
|
->default('Alperen Trunç'),
|
||||||
\Filament\Forms\Components\Toggle::make('notebook_unit_signed')
|
|
||||||
->label('İlgili Birim Yetkilisi İmzala / Onayla')
|
|
||||||
->live(),
|
|
||||||
TextInput::make('notebook_unit_name')
|
|
||||||
->label('Birim Yetkilisi Adı / Ünvanı')
|
|
||||||
->placeholder('Örn: Yetkili Birim Amiri')
|
|
||||||
->default('Yetkili Birim Amiri'),
|
|
||||||
\Filament\Forms\Components\Toggle::make('notebook_approved')
|
\Filament\Forms\Components\Toggle::make('notebook_approved')
|
||||||
->label('Staj Defterini Genel Olarak Onayla')
|
->label('Staj Defterini Genel Olarak Onayla')
|
||||||
->columnSpanFull(),
|
->columnSpanFull(),
|
||||||
|
|||||||
@@ -530,6 +530,55 @@ class CareerController extends Controller
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getJournalEntry(Request $request)
|
||||||
|
{
|
||||||
|
if (!auth()->check()) {
|
||||||
|
return response()->json(['success' => false, 'message' => 'Yetkisiz işlem.'], 403);
|
||||||
|
}
|
||||||
|
|
||||||
|
$request->validate([
|
||||||
|
'intern_id' => 'required|integer|exists:career_applications,id',
|
||||||
|
'date' => 'required|date',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$intern = \App\Models\CareerApplication::findOrFail($request->intern_id);
|
||||||
|
|
||||||
|
$days = self::getInternshipDates($intern->internship_start_date, $intern->internship_total_days);
|
||||||
|
$dayNum = null;
|
||||||
|
$dateFormatted = null;
|
||||||
|
foreach ($days as $d) {
|
||||||
|
if ($d['date'] === $request->date) {
|
||||||
|
$dayNum = $d['day_number'];
|
||||||
|
$dateFormatted = $d['formatted_date'];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$dayNum) {
|
||||||
|
return response()->json([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'Seçilen tarih staj dönemi dışındadır veya haftasonudur.'
|
||||||
|
], 422);
|
||||||
|
}
|
||||||
|
|
||||||
|
$entry = $intern->journalEntries()->where('date', $request->date)->first();
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'success' => true,
|
||||||
|
'intern_name' => $intern->name,
|
||||||
|
'day_number' => $dayNum,
|
||||||
|
'date' => $request->date,
|
||||||
|
'date_formatted' => $dateFormatted,
|
||||||
|
'entry' => $entry ? [
|
||||||
|
'id' => $entry->id,
|
||||||
|
'content' => $entry->content,
|
||||||
|
'is_retroactive' => $entry->is_retroactive,
|
||||||
|
'supervisor_approved' => $entry->supervisor_approved,
|
||||||
|
'supervisor_name' => $entry->supervisor_name,
|
||||||
|
] : null
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
public function toggleJournalApproval(Request $request)
|
public function toggleJournalApproval(Request $request)
|
||||||
{
|
{
|
||||||
if (!auth()->check()) {
|
if (!auth()->check()) {
|
||||||
@@ -538,28 +587,24 @@ class CareerController extends Controller
|
|||||||
|
|
||||||
$request->validate([
|
$request->validate([
|
||||||
'entry_id' => 'required|integer|exists:internship_journal_entries,id',
|
'entry_id' => 'required|integer|exists:internship_journal_entries,id',
|
||||||
'type' => 'required|string|in:supervisor,unit',
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$entry = \App\Models\InternshipJournalEntry::findOrFail($request->entry_id);
|
$entry = \App\Models\InternshipJournalEntry::findOrFail($request->entry_id);
|
||||||
|
|
||||||
if ($request->type === 'supervisor') {
|
$entry->supervisor_approved = !$entry->supervisor_approved;
|
||||||
$entry->supervisor_approved = !$entry->supervisor_approved;
|
if ($entry->supervisor_approved) {
|
||||||
$entry->save();
|
$entry->supervisor_name = auth()->user()->name;
|
||||||
return response()->json([
|
|
||||||
'success' => true,
|
|
||||||
'status' => $entry->supervisor_approved,
|
|
||||||
'message' => 'Staj sorumlusu onayı güncellendi.'
|
|
||||||
]);
|
|
||||||
} else {
|
} else {
|
||||||
$entry->unit_approved = !$entry->unit_approved;
|
$entry->supervisor_name = null;
|
||||||
$entry->save();
|
|
||||||
return response()->json([
|
|
||||||
'success' => true,
|
|
||||||
'status' => $entry->unit_approved,
|
|
||||||
'message' => 'Birim yetkilisi onayı güncellendi.'
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
$entry->save();
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'success' => true,
|
||||||
|
'status' => $entry->supervisor_approved,
|
||||||
|
'supervisor_name' => $entry->supervisor_name,
|
||||||
|
'message' => 'Staj sorumlusu onayı güncellendi.'
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ class InternshipJournalEntry extends Model
|
|||||||
'is_retroactive',
|
'is_retroactive',
|
||||||
'supervisor_approved',
|
'supervisor_approved',
|
||||||
'unit_approved',
|
'unit_approved',
|
||||||
|
'supervisor_name',
|
||||||
];
|
];
|
||||||
|
|
||||||
protected $casts = [
|
protected $casts = [
|
||||||
|
|||||||
+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->string('supervisor_name')->nullable();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('internship_journal_entries', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('supervisor_name');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -21,6 +21,40 @@
|
|||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Dynamic Journal Modal -->
|
||||||
|
<div id="journal-modal" class="fixed inset-0 z-[9999] hidden items-center justify-center bg-slate-900/60 backdrop-blur-sm transition-opacity duration-300">
|
||||||
|
<div class="bg-white rounded-3xl shadow-2xl border border-slate-100 max-w-2xl w-full mx-4 overflow-hidden transform scale-95 opacity-0 transition-all duration-300 flex flex-col max-h-[90vh]">
|
||||||
|
<!-- Modal Header -->
|
||||||
|
<div class="px-6 py-4 bg-gradient-to-r from-blue-50 to-indigo-50/30 border-b border-slate-100 flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<h4 id="modal-title" class="font-bold text-slate-800 text-lg">Staj Raporu Detayı</h4>
|
||||||
|
<p id="modal-subtitle" class="text-xs text-slate-500 font-semibold mt-0.5"></p>
|
||||||
|
</div>
|
||||||
|
<button type="button" onclick="closeJournalModal()" class="text-slate-400 hover:text-slate-600 transition-colors p-1.5 rounded-full hover:bg-slate-100/80">
|
||||||
|
<svg class="w-5 h-5" fill="none" stroke="currentColor" stroke-width="2.5" viewBox="0 0 24 24"><path d="M6 18L18 6M6 6l12 12"/></svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Modal Body -->
|
||||||
|
<div class="p-6 overflow-y-auto flex-grow prose max-w-none text-slate-700 leading-relaxed text-sm no-scrollbar">
|
||||||
|
<div id="modal-content" class="min-h-[100px] flex flex-col justify-center">
|
||||||
|
<!-- Content gets loaded here -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Modal Footer -->
|
||||||
|
<div class="px-6 py-4 bg-slate-50/50 border-t border-slate-100 flex items-center justify-between">
|
||||||
|
<div id="modal-status-badge">
|
||||||
|
<!-- Status badge -->
|
||||||
|
</div>
|
||||||
|
<div class="flex gap-3">
|
||||||
|
<button type="button" onclick="closeJournalModal()" class="px-4 py-2 border border-slate-200 text-slate-600 hover:bg-slate-50 rounded-xl font-bold text-xs transition-colors">Kapat</button>
|
||||||
|
<button type="button" id="modal-action-btn" onclick="handleModalAction()" class="hidden px-4 py-2 text-white bg-blue-600 hover:bg-blue-700 rounded-xl font-bold text-xs shadow-lg shadow-blue-500/20 transition-all"></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
@push('styles')
|
@push('styles')
|
||||||
<!-- DevExtreme Gantt CSS Dependencies -->
|
<!-- DevExtreme Gantt CSS Dependencies -->
|
||||||
<link rel="stylesheet" href="https://cdn3.devexpress.com/jslib/23.2.5/css/dx.fluent.blue.light.css">
|
<link rel="stylesheet" href="https://cdn3.devexpress.com/jslib/23.2.5/css/dx.fluent.blue.light.css">
|
||||||
@@ -31,6 +65,12 @@
|
|||||||
border-left: 2px dashed #ef4444 !important;
|
border-left: 2px dashed #ef4444 !important;
|
||||||
width: 2px !important;
|
width: 2px !important;
|
||||||
}
|
}
|
||||||
|
.rich-text-content p { margin-bottom: 8px; }
|
||||||
|
.rich-text-content ul { list-style-type: disc !important; padding-left: 20px !important; margin-bottom: 8px !important; }
|
||||||
|
.rich-text-content ol { list-style-type: decimal !important; padding-left: 20px !important; margin-bottom: 8px !important; }
|
||||||
|
.rich-text-content li { margin-bottom: 4px !important; }
|
||||||
|
.no-scrollbar::-webkit-scrollbar { display: none; }
|
||||||
|
.no-scrollbar { -ms-overflow-style: none; scrollbar-width: none; }
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
tailwind = {
|
tailwind = {
|
||||||
@@ -62,6 +102,8 @@
|
|||||||
progress: 0
|
progress: 0
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
let dayColumnsMap = {};
|
||||||
|
|
||||||
$('#gantt').dxGantt({
|
$('#gantt').dxGantt({
|
||||||
tasks: {
|
tasks: {
|
||||||
dataSource: formattedData,
|
dataSource: formattedData,
|
||||||
@@ -105,8 +147,192 @@
|
|||||||
start: new Date(),
|
start: new Date(),
|
||||||
cssClass: 'current-time-line'
|
cssClass: 'current-time-line'
|
||||||
}],
|
}],
|
||||||
|
onScaleCellPrepared: function(e) {
|
||||||
|
if (e.scaleType === 'days') {
|
||||||
|
const $el = $(e.scaleElement || e.element);
|
||||||
|
const left = $el.position().left;
|
||||||
|
const width = $el.outerWidth();
|
||||||
|
const dateStr = e.startDate.toISOString().substring(0, 10);
|
||||||
|
dayColumnsMap[dateStr] = {
|
||||||
|
left: left,
|
||||||
|
right: left + width,
|
||||||
|
date: e.startDate,
|
||||||
|
dateStr: dateStr
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Grid task area cell click listener
|
||||||
|
$(document).on('click', '#gantt', function(e) {
|
||||||
|
const $target = $(e.target);
|
||||||
|
if ($target.closest('.dx-gantt-task-area').length > 0 || $target.closest('.dx-gantt-ts-area').length > 0) {
|
||||||
|
const ganttInstance = $('#gantt').dxGantt('instance');
|
||||||
|
const selectedKey = ganttInstance.option("selectedRowKey");
|
||||||
|
if (!selectedKey) {
|
||||||
|
alert("Lütfen önce sol taraftan stajyeri seçin, ardından tıklamak istediğiniz güne tıklayın.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const $taskArea = $('.dx-gantt-task-area').first();
|
||||||
|
if (!$taskArea.length) return;
|
||||||
|
|
||||||
|
const scrollLeft = ganttInstance._ganttView._taskAreaContainer.scrollLeft;
|
||||||
|
const clickX = e.pageX - $taskArea.offset().left;
|
||||||
|
const totalX = scrollLeft + clickX;
|
||||||
|
|
||||||
|
const cols = Object.values(dayColumnsMap);
|
||||||
|
const clickedCol = cols.find(col => totalX >= col.left && totalX <= col.right);
|
||||||
|
if (clickedCol) {
|
||||||
|
showDailyJournalPopup(selectedKey, clickedCol.dateStr);
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let activeEntryId = null;
|
||||||
|
|
||||||
|
function showDailyJournalPopup(internId, dateStr) {
|
||||||
|
const modal = document.getElementById('journal-modal');
|
||||||
|
const contentDiv = document.getElementById('modal-content');
|
||||||
|
const titleH = document.getElementById('modal-title');
|
||||||
|
const subtitleP = document.getElementById('modal-subtitle');
|
||||||
|
const badgeDiv = document.getElementById('modal-status-badge');
|
||||||
|
const actionBtn = document.getElementById('modal-action-btn');
|
||||||
|
|
||||||
|
modal.classList.remove('hidden');
|
||||||
|
modal.classList.add('flex');
|
||||||
|
setTimeout(() => {
|
||||||
|
modal.firstElementChild.classList.remove('scale-95', 'opacity-0');
|
||||||
|
modal.firstElementChild.classList.add('scale-100', 'opacity-100');
|
||||||
|
}, 50);
|
||||||
|
|
||||||
|
contentDiv.innerHTML = `
|
||||||
|
<div class="flex items-center justify-center p-8">
|
||||||
|
<div class="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
titleH.textContent = "Yükleniyor...";
|
||||||
|
subtitleP.textContent = "";
|
||||||
|
badgeDiv.innerHTML = "";
|
||||||
|
actionBtn.classList.add('hidden');
|
||||||
|
activeEntryId = null;
|
||||||
|
|
||||||
|
fetch(`/stajyer/admin/journal-entry?intern_id=${internId}&date=${dateStr}`)
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(data => {
|
||||||
|
if (!data.success) {
|
||||||
|
contentDiv.innerHTML = `<p class="text-slate-500 italic text-center p-8">${data.message}</p>`;
|
||||||
|
titleH.textContent = "Bilgi";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
titleH.textContent = `${data.intern_name}`;
|
||||||
|
subtitleP.textContent = `${data.day_number}. Gün Raporu — ${data.date_formatted}`;
|
||||||
|
|
||||||
|
if (!data.entry) {
|
||||||
|
contentDiv.innerHTML = `
|
||||||
|
<div class="flex flex-col items-center justify-center p-8 text-center bg-slate-50 rounded-2xl border border-dashed border-slate-200">
|
||||||
|
<i class="uil uil-file-slash text-3xl text-slate-400 mb-2"></i>
|
||||||
|
<p class="text-sm font-medium text-slate-500">Bu gün için henüz staj raporu yazılmamıştır.</p>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
activeEntryId = data.entry.id;
|
||||||
|
const entryContent = data.entry.content || '<p class="text-slate-400 italic">Boş içerik.</p>';
|
||||||
|
|
||||||
|
let contentHtml = '';
|
||||||
|
if (data.entry.is_retroactive) {
|
||||||
|
contentHtml += `
|
||||||
|
<div class="mb-4 inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full bg-rose-50 text-rose-700 text-[10px] font-extrabold uppercase border border-rose-100">
|
||||||
|
<span class="w-1.5 h-1.5 rounded-full bg-rose-500 animate-pulse"></span>
|
||||||
|
Geriye Dönük Kayıt
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
contentHtml += `<div class="rich-text-content prose max-w-none text-slate-700 leading-relaxed text-sm select-text">${entryContent}</div>`;
|
||||||
|
contentDiv.innerHTML = contentHtml;
|
||||||
|
|
||||||
|
updateModalStatus(data.entry.supervisor_approved, data.entry.supervisor_name);
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.error(err);
|
||||||
|
contentDiv.innerHTML = `<p class="text-red-500 text-center p-8 font-semibold">Veriler yüklenirken bir hata oluştu.</p>`;
|
||||||
|
titleH.textContent = "Hata";
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateModalStatus(approved, name) {
|
||||||
|
const badgeDiv = document.getElementById('modal-status-badge');
|
||||||
|
const actionBtn = document.getElementById('modal-action-btn');
|
||||||
|
|
||||||
|
actionBtn.classList.remove('hidden');
|
||||||
|
if (approved) {
|
||||||
|
badgeDiv.innerHTML = `
|
||||||
|
<span class="inline-flex items-center gap-1.5 px-3 py-1 rounded-full bg-emerald-50 text-emerald-700 text-xs font-bold border border-emerald-100">
|
||||||
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="2.5" viewBox="0 0 24 24"><polyline points="20 6 9 17 4 12"/></svg>
|
||||||
|
Sorumlu Onayladı ${name ? `(${name})` : ''}
|
||||||
|
</span>
|
||||||
|
`;
|
||||||
|
actionBtn.textContent = "Onayı Kaldır";
|
||||||
|
actionBtn.className = "px-4 py-2 text-white bg-rose-600 hover:bg-rose-700 rounded-xl font-bold text-xs shadow-lg shadow-rose-500/20 transition-all cursor-pointer";
|
||||||
|
} else {
|
||||||
|
badgeDiv.innerHTML = `
|
||||||
|
<span class="inline-flex items-center gap-1.5 px-3 py-1 rounded-full bg-slate-100 text-slate-600 text-xs font-bold border border-slate-200">
|
||||||
|
Onay Bekliyor
|
||||||
|
</span>
|
||||||
|
`;
|
||||||
|
actionBtn.textContent = "Raporu Onayla";
|
||||||
|
actionBtn.className = "px-4 py-2 text-white bg-emerald-600 hover:bg-emerald-700 rounded-xl font-bold text-xs shadow-lg shadow-emerald-500/20 transition-all cursor-pointer";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleModalAction() {
|
||||||
|
if (!activeEntryId) return;
|
||||||
|
|
||||||
|
const actionBtn = document.getElementById('modal-action-btn');
|
||||||
|
actionBtn.disabled = true;
|
||||||
|
actionBtn.style.opacity = "0.5";
|
||||||
|
|
||||||
|
fetch('/stajyer/admin/toggle-approval', {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"X-CSRF-TOKEN": "{{ csrf_token() }}"
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
entry_id: activeEntryId
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.success) {
|
||||||
|
updateModalStatus(data.status, data.supervisor_name);
|
||||||
|
} else {
|
||||||
|
alert(data.message || "Onay güncellenemedi.");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.error(err);
|
||||||
|
alert("İşlem sırasında bir hata oluştu.");
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
actionBtn.disabled = false;
|
||||||
|
actionBtn.style.opacity = "1";
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeJournalModal() {
|
||||||
|
const modal = document.getElementById('journal-modal');
|
||||||
|
modal.firstElementChild.classList.add('scale-95', 'opacity-0');
|
||||||
|
modal.firstElementChild.classList.remove('scale-100', 'opacity-100');
|
||||||
|
setTimeout(() => {
|
||||||
|
modal.classList.add('hidden');
|
||||||
|
modal.classList.remove('flex');
|
||||||
|
}, 200);
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
@endif
|
@endif
|
||||||
@endpush
|
@endpush
|
||||||
|
|||||||
@@ -425,24 +425,7 @@
|
|||||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" style="color: #059669;"><polyline points="20 6 9 17 4 12"/></svg>
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" style="color: #059669;"><polyline points="20 6 9 17 4 12"/></svg>
|
||||||
<span class="sig-badge">E-ONAYLI</span>
|
<span class="sig-badge">E-ONAYLI</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="sig-name">{{ $intern->notebook_supervisor_name ?: 'Alperen Trunç' }}</div>
|
<div class="sig-name">{{ $entry->supervisor_name ?: ($intern->notebook_supervisor_name ?: 'Alperen Trunç') }}</div>
|
||||||
@else
|
|
||||||
<div class="sig-status waiting">
|
|
||||||
<span class="sig-badge waiting">İMZA BEKLENİYOR</span>
|
|
||||||
</div>
|
|
||||||
<div class="sig-name"> </div>
|
|
||||||
@endif
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Unit Officer -->
|
|
||||||
<div class="signature-box">
|
|
||||||
<div class="sig-title">Birim Yetkilisi</div>
|
|
||||||
@if($entry && $entry->unit_approved)
|
|
||||||
<div class="sig-status">
|
|
||||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" style="color: #059669;"><polyline points="20 6 9 17 4 12"/></svg>
|
|
||||||
<span class="sig-badge">E-ONAYLI</span>
|
|
||||||
</div>
|
|
||||||
<div class="sig-name">{{ $intern->notebook_unit_name ?: 'Yetkili Birim Amiri' }}</div>
|
|
||||||
@else
|
@else
|
||||||
<div class="sig-status waiting">
|
<div class="sig-status waiting">
|
||||||
<span class="sig-badge waiting">İMZA BEKLENİYOR</span>
|
<span class="sig-badge waiting">İMZA BEKLENİYOR</span>
|
||||||
|
|||||||
@@ -165,7 +165,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- ================= PAGE 3: STAJ DEFTERİ (PORTRAIT A4) ================= -->
|
<!-- ================= PAGE 3: STAJ DEFTERİ (PORTRAIT A4) ================= -->
|
||||||
@if($application->notebook_approved || $application->notebook_supervisor_signed || $application->notebook_unit_signed)
|
@if($application->notebook_approved || $application->notebook_supervisor_signed)
|
||||||
<div class="notebook-container-wrapper overflow-hidden w-full flex justify-center items-start mt-8">
|
<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">
|
<div id="notebook-node" class="transcript-page bg-white rounded-[2rem] shadow-2xl p-6 md:p-8 relative overflow-hidden mx-auto">
|
||||||
|
|
||||||
@@ -195,11 +195,6 @@
|
|||||||
Sorumlu: {{ $application->notebook_supervisor_name ?: 'Alperen Trunç' }}
|
Sorumlu: {{ $application->notebook_supervisor_name ?: 'Alperen Trunç' }}
|
||||||
</span>
|
</span>
|
||||||
@endif
|
@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>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -215,10 +210,7 @@
|
|||||||
<span class="text-xs font-extrabold text-slate-800">
|
<span class="text-xs font-extrabold text-slate-800">
|
||||||
{{ $day['day_number'] }}. Gün Raporu
|
{{ $day['day_number'] }}. Gün Raporu
|
||||||
@if($entry->supervisor_approved)
|
@if($entry->supervisor_approved)
|
||||||
<span class="ml-2 inline-flex px-1.5 py-0.5 rounded bg-emerald-100 text-emerald-800 text-[8px] font-extrabold uppercase border border-emerald-200">Sorumlu Onaylı</span>
|
<span class="ml-2 inline-flex px-1.5 py-0.5 rounded bg-emerald-100 text-emerald-800 text-[8px] font-extrabold uppercase border border-emerald-200">Sorumlu Onaylı{{ $entry->supervisor_name ? ' (' . $entry->supervisor_name . ')' : '' }}</span>
|
||||||
@endif
|
|
||||||
@if($entry->unit_approved)
|
|
||||||
<span class="ml-1 inline-flex px-1.5 py-0.5 rounded bg-emerald-100 text-emerald-800 text-[8px] font-extrabold uppercase border border-emerald-200">Yetkili Onaylı</span>
|
|
||||||
@endif
|
@endif
|
||||||
</span>
|
</span>
|
||||||
<span class="text-[10px] text-slate-400 font-bold">{{ $day['formatted_date'] }}</span>
|
<span class="text-[10px] text-slate-400 font-bold">{{ $day['formatted_date'] }}</span>
|
||||||
|
|||||||
@@ -131,6 +131,7 @@ Route::post('/stajyer/cikis', [\App\Http\Controllers\CareerController::class, 'i
|
|||||||
Route::get('/stajyer/admin/giris', [\App\Http\Controllers\CareerController::class, 'internAdminLoginForm'])->name('intern.admin.login');
|
Route::get('/stajyer/admin/giris', [\App\Http\Controllers\CareerController::class, 'internAdminLoginForm'])->name('intern.admin.login');
|
||||||
Route::post('/stajyer/admin/giris', [\App\Http\Controllers\CareerController::class, 'internAdminLogin'])->name('intern.admin.login.submit');
|
Route::post('/stajyer/admin/giris', [\App\Http\Controllers\CareerController::class, 'internAdminLogin'])->name('intern.admin.login.submit');
|
||||||
Route::get('/stajyer/admin/panel', [\App\Http\Controllers\CareerController::class, 'internAdminDashboard'])->name('intern.admin.dashboard');
|
Route::get('/stajyer/admin/panel', [\App\Http\Controllers\CareerController::class, 'internAdminDashboard'])->name('intern.admin.dashboard');
|
||||||
|
Route::get('/stajyer/admin/journal-entry', [\App\Http\Controllers\CareerController::class, 'getJournalEntry'])->name('intern.admin.get-journal-entry');
|
||||||
Route::post('/stajyer/admin/toggle-approval', [\App\Http\Controllers\CareerController::class, 'toggleJournalApproval'])->name('intern.admin.toggle-journal-approval');
|
Route::post('/stajyer/admin/toggle-approval', [\App\Http\Controllers\CareerController::class, 'toggleJournalApproval'])->name('intern.admin.toggle-journal-approval');
|
||||||
Route::post('/stajyer/admin/cikis', [\App\Http\Controllers\CareerController::class, 'internAdminLogout'])->name('intern.admin.logout');
|
Route::post('/stajyer/admin/cikis', [\App\Http\Controllers\CareerController::class, 'internAdminLogout'])->name('intern.admin.logout');
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user