@@ -684,6 +684,7 @@
$dayDate = $d [ 'date' ];
$dayDate = $d [ 'date' ];
$dayDateFormatted = $d [ 'formatted_date' ];
$dayDateFormatted = $d [ 'formatted_date' ];
$hasSaved = isset ( $savedEntries [ $dayNum ]) && trim ( $savedEntries [ $dayNum ] -> content ) !== '' ;
$hasSaved = isset ( $savedEntries [ $dayNum ]) && trim ( $savedEntries [ $dayNum ] -> content ) !== '' ;
$isApproved = isset ( $savedEntries [ $dayNum ]) && $savedEntries [ $dayNum ] -> supervisor_approved ;
@ endphp
@ endphp
< button type = " button "
< button type = " button "
onclick = " selectNotebookDay( { { $idx }}) "
onclick = " selectNotebookDay( { { $idx }}) "
@@ -692,13 +693,19 @@
data - date = " { { $dayDate }} "
data - date = " { { $dayDate }} "
data - formatted - date = " { { $dayDateFormatted }} "
data - formatted - date = " { { $dayDateFormatted }} "
data - saved - content = " { { $savedEntries[$dayNum] ->content ?? '' }} "
data - saved - content = " { { $savedEntries[$dayNum] ->content ?? '' }} "
data - approved = " { { $isApproved ? '1' : '0' }} "
class = " notebook-day-btn w-full flex items-center justify-between p-3 rounded-2xl border text-left transition-all @if( $idx === 0) border-blue-200 bg-blue-50/50 @else border-slate-100 bg-white hover:bg-slate-50 @endif " >
class = " notebook-day-btn w-full flex items-center justify-between p-3 rounded-2xl border text-left transition-all @if( $idx === 0) border-blue-200 bg-blue-50/50 @else border-slate-100 bg-white hover:bg-slate-50 @endif " >
< div >
< div >
< span class = " block text-xs font-extrabold @if( $idx === 0) text-blue-700 @else text-slate-800 @endif " > {{ $dayNum }} . Gün </ span >
< span class = " block text-xs font-extrabold @if( $idx === 0) text-blue-700 @else text-slate-800 @endif " > {{ $dayNum }} . Gün </ span >
< span class = " block text-[10px] text-slate-400 mt-0.5 " > {{ $dayDateFormatted }} </ span >
< span class = " block text-[10px] text-slate-400 mt-0.5 " > {{ $dayDateFormatted }} </ span >
</ div >
</ div >
< span id = " badge-day- { { $idx }} " class = " inline-flex px-2 py-0.5 rounded-lg text-[9px] font-extrabold uppercase @if( $hasSaved ) bg-green-50 text-green-700 border border-green-200 @else bg-slate-50 text-slate-400 border border-slate-200 @endif " >
< span id = " badge-day- { { $idx }} " class = " inline-flex px-2 py-0.5 rounded-lg text-[9px] font-extrabold uppercase
{{ $hasSaved ? 'DOLU' : 'BOŞ' }}
@ if ( $isApproved ) bg - green - 500 text - white border border - green - 600
@ elseif ( $hasSaved ) bg - amber - 50 text - amber - 700 border border - amber - 200
@ else bg - slate - 50 text - slate - 400 border border - slate - 200 @ endif " >
@ if ( $isApproved ) ONAYLANDI
@ elseif ( $hasSaved ) DOLU
@ else BOŞ @ endif
</ span >
</ span >
</ button >
</ button >
@ endforeach
@ endforeach
@@ -708,17 +715,25 @@
< div class = " lg:col-span-8 space-y-4 " >
< div class = " lg:col-span-8 space-y-4 " >
< div class = " bg-white border border-slate-100 rounded-2xl p-5 shadow-sm " >
< div class = " bg-white border border-slate-100 rounded-2xl p-5 shadow-sm " >
< div class = " flex items-center justify-between pb-3 border-b border-slate-100 mb-4 " >
< div class = " flex items-center justify-between pb-3 border-b border-slate-100 mb-4 " >
< div >
< div class = " flex items-center " >
< span id = " editor-day-title " class = " text-sm font-extrabold text-blue-600 bg-blue-50 px-3 py-1 rounded-full " > 1. Gün </ span >
< span id = " editor-day-title " class = " text-sm font-extrabold text-blue-600 bg-blue-50 px-3 py-1 rounded-full " > 1. Gün </ span >
< span id = " editor-day-date " class = " text-xs font-semibold text-slate-400 ml-2 " ></ span >
< span id = " editor-day-date " class = " text-xs font-semibold text-slate-400 ml-2 " ></ span >
< span id = " editor-day-status " class = " ml-2 " ></ span >
</ div >
</ div >
@ if ( $intern -> github_repo )
< div class = " flex items-center gap-2 " >
< button type = " button " onclick = " fillFromGithub() " class = " px-3 py-1.5 bg-slate-800 hover:bg-slate-900 text-white font-extrabold rounded-lg text-[10px] transition-all flex items-center gap-1.5 shadow-sm " >
@ if ( $intern -> github_repo )
< i class = " u il uil-github text-xs " ></ i >
< button type = " button " onclick = " f illFromGithub() " class = " px-3 py-1.5 bg-slate-800 hover:bg-slate-900 text-white font-extrabold rounded-lg text-[10px] transition-all flex items-center gap-1.5 shadow-sm " >
< span > Git Commitlerini Çek </ span >
< i class = " uil uil-github text-xs " > </i >
</ button >
< span > Git Commitlerini Çek </ span >
@ endif
</ button >
@ endif
< button type = " button " onclick = " printActiveDay() " class = " px-3 py-1.5 bg-green-600 hover:bg-green-700 text-white font-extrabold rounded-lg text-[10px] transition-all flex items-center gap-1.5 shadow-sm " >
< i class = " uil uil-print text-xs " ></ i >
< span > Yazdı r </ span >
</ button >
</ div >
</ div >
</ div >
< div class = " space-y-4 " >
< div class = " space-y-4 " >
@@ -733,6 +748,19 @@
</ div >
</ div >
</ div >
</ div >
< div id = " git-commits-fallback-container " class = " hidden p-4 bg-slate-50 border border-slate-200/60 rounded-2xl text-xs space-y-3 " >
< div class = " flex justify-between items-center pb-2 border-b border-slate-200 " >
< span class = " font-extrabold text-slate-700 flex items-center gap-1.5 " >
< i class = " uil uil-info-circle text-blue-600 text-sm " ></ i >
< span > Seçilen güne ait otomatik commit bulunamadı . Son 10 commit ' iniz :</ span >
</ span >
< button type = " button " onclick = " hideFallbackCommits() " class = " text-slate-400 hover:text-slate-600 font-extrabold " > Kapat </ button >
</ div >
< div id = " git-commits-fallback-list " class = " space-y-2 max-h-[150px] overflow-y-auto pr-1 no-scrollbar " >
<!-- items dynamically populated -->
</ 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 " 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 " >
< 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 " >
@@ -855,56 +883,78 @@
// Handle initial state of date logic
// Handle initial state of date logic
calculateEndDateFrontend ();
calculateEndDateFrontend ();
// Fetch Github commits in memory if repo is set
fetchAllCommitsInMemory ();
// Select first day of the notebook workspace on load
// Select first day of the notebook workspace on load
selectNotebookDay ( 0 );
selectNotebookDay ( 0 );
});
});
const githubRepoUrl = @ json ( $intern -> github_repo );
const githubRepoUrl = @ json ( $intern -> github_repo );
let allGithubCommits = {};
let activeDayIdx = 0 ;
let activeDayIdx = 0 ;
function fetchAllCommitsInMemory () {
function hideFallbackCommits () {
if ( ! githubRepoUrl ) return ;
const container = document . getElementById ( 'git-commits-fallback-container' ) ;
if ( container ) {
container . classList . add ( 'hidden' );
}
}
let repoClean = githubRepoUrl . replace ( / https ? : \ / \ / ( www\ . ) ? github\ . com\ //i, '');
function formatDateToIstanbul ( d ) {
repoClean = repoClean . replace ( / \ / $ / , '' );
try {
const parts = repoClean . split ( '/' );
const formatter = new Intl . DateTimeFormat ( 'en-US' , {
if ( parts . length < 2 ) return ;
timeZone : 'Europe/Istanbul' ,
year : 'numeric' ,
const owner = parts [ 0 ];
month : '2-digit' ,
const repo = parts [ 1 ] . replace ( / \ . git $ / i , '' );
day : '2-digit'
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 d = new Date ( dateStr );
const formatter = new Intl . DateTimeFormat ( 'fr-CA' , {
timeZone : 'Europe/Istanbul' ,
year : 'numeric' ,
month : '2-digit' ,
day : '2-digit'
});
const dateOnly = formatter . format ( d );
if ( ! allGithubCommits [ dateOnly ]) {
allGithubCommits [ dateOnly ] = [];
}
allGithubCommits [ dateOnly ] . push ( item );
}
});
})
. catch ( err => {
console . warn ( 'Commits could not be pre-loaded in memory.' , err );
});
});
const parts = formatter . formatToParts ( d );
const year = parts . find ( p => p . type === 'year' ) . value ;
const month = parts . find ( p => p . type === 'month' ) . value ;
const day = parts . find ( p => p . type === 'day' ) . value ;
return `${year}-${month}-${day}` ;
} catch ( e ) {
return d . toISOString () . split ( 'T' )[ 0 ];
}
}
function updateEditorStatusBadge ( isApproved , hasSaved ) {
const statusSpan = document . getElementById ( 'editor-day-status' );
if ( ! statusSpan ) return ;
if ( isApproved ) {
statusSpan . textContent = 'ONAYLANDI' ;
statusSpan . className = 'inline-flex px-2 py-0.5 rounded-full text-[10px] font-extrabold uppercase bg-green-500 text-white border border-green-600 ml-2' ;
} else if ( hasSaved ) {
statusSpan . textContent = 'ONAY BEKLİYOR' ;
statusSpan . className = 'inline-flex px-2 py-0.5 rounded-full text-[10px] font-extrabold uppercase bg-amber-50 text-amber-700 border border-amber-200 ml-2' ;
} else {
statusSpan . textContent = '' ;
statusSpan . className = 'hidden' ;
}
}
function printActiveDay () {
const btn = document . getElementById ( `btn-day-${activeDayIdx}` );
if ( ! btn ) return ;
const dayNum = btn . getAttribute ( 'data-day-num' );
window . open ( `{{ route('intern.print-journal') }}?size=a4&day=${dayNum}` , '_blank' );
}
function appendCommitToQuill ( sha , time , msg ) {
const escapedMsg = msg . replace ( /</ g , " < " ) . replace ( />/ g , " > " );
const commitHtml = `<li><strong>[${time}]</strong> (<em>${sha}</em>) ${escapedMsg}</li>` ;
const currentHtml = quill . root . innerHTML . trim ();
const hasHeader = currentHtml . includes ( " Git Commit Mesajları : " );
const headerHtml = hasHeader ? " " : " <p><strong>Git Commit Mesajları :</strong></p> " ;
let newHtml = " " ;
if ( currentHtml !== " " && currentHtml !== " <p><br></p> " ) {
newHtml = currentHtml + " <br><br> " + headerHtml + " <ul> " + commitHtml + " </ul> " ;
} else {
newHtml = headerHtml + " <ul> " + commitHtml + " </ul> " ;
}
quill . setContents ([]);
quill . clipboard . dangerouslyPasteHTML ( newHtml );
}
}
function selectNotebookDay ( idx ) {
function selectNotebookDay ( idx ) {
@@ -934,12 +984,20 @@
const dateVal = newBtn . getAttribute ( 'data-date' );
const dateVal = newBtn . getAttribute ( 'data-date' );
const dateFormatted = newBtn . getAttribute ( 'data-formatted-date' );
const dateFormatted = newBtn . getAttribute ( 'data-formatted-date' );
const savedContent = newBtn . getAttribute ( 'data-saved-content' );
const savedContent = newBtn . getAttribute ( 'data-saved-content' );
const isApproved = newBtn . getAttribute ( 'data-approved' ) === '1' ;
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 ;
quill . root . innerHTML = savedContent || '' ;
quill . setContents ([]) ;
if ( savedContent ) {
quill . clipboard . dangerouslyPasteHTML ( savedContent );
}
document . getElementById ( 'save-status' ) . textContent = '' ;
document . getElementById ( 'save-status' ) . textContent = '' ;
hideFallbackCommits ();
const hasSaved = savedContent && savedContent . trim () !== '' && savedContent !== '<p><br></p>' ;
updateEditorStatusBadge ( isApproved , hasSaved );
// Future validation
// Future validation
const dateParts = dateVal . split ( '-' );
const dateParts = dateVal . split ( '-' );
@@ -979,34 +1037,160 @@
if ( ! btn ) return ;
if ( ! btn ) return ;
const dateVal = btn . getAttribute ( 'data-date' );
const dateVal = btn . getAttribute ( 'data-date' );
const dayCommits = allGithubCommits [ dateVal ] || [] ;
const formattedDate = btn . getAttribute ( 'data-formatted-date' ) ;
if ( dayCommits . length === 0 ) {
if ( ! githubRepoUrl ) {
alert ( " Bu staj gününe ait GitHub commit'i bulunamadı . Lütfen commit attığını zdan ve tarihin doğru olduğundan emin olu n." );
alert ( " Lütfen önce GitHub depo URL'nizi kaydedi n." );
return ;
return ;
}
}
let commitHtml = " <ul> " ;
let repoClean = githubRepoUrl . replace ( / https ? : \ / \ / ( www\ . ) ? github\ . com\ //i, '');
const sorted = [ ... dayCommits ] . reverse ( );
repoClean = repoClean . replace ( / \ / $ / , '' );
sorted . forEach ( c => {
const parts = repoClean . split ( '/' );
const msg = c . commit . message ;
if ( parts . length < 2 ) {
const sha = c . sha . substring ( 0 , 7 );
alert ( " Geçersiz GitHub URL'si. " );
const authorDate = new Date ( c . commit . author . date ) ;
return ;
const time = authorDate . toLocaleTimeString ( 'tr-TR' , { timeZone : 'Europe/Istanbul' , 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 ;
}
}
const owner = parts [ 0 ];
const repo = parts [ 1 ] . replace ( / \ . git $ / i , '' );
let branch = '' ;
// Parse branch if /tree/{branch} or /commits/{branch} is used
if ( parts . length >= 4 && ( parts [ 2 ] === 'tree' || parts [ 2 ] === 'commits' )) {
branch = parts . slice ( 3 ) . join ( '/' );
}
const gitBtn = document . querySelector ( 'button[onclick="fillFromGithub()"]' );
const originalContent = gitBtn . innerHTML ;
gitBtn . disabled = true ;
gitBtn . innerHTML = `<i class="uil uil-spinner text-xs animate-spin"></i> <span>Çekiliyor...</span>` ;
hideFallbackCommits ();
fetch ( `https://api.github.com/repos/${owner}/${repo}/commits?per_page=100${branch ? ` & sha = $ { branch } ` : ''}` )
. then ( response => {
if ( ! response . ok ) {
if ( response . status === 404 ) {
throw new Error ( " Depo bulunamadı veya herkese açık (public) değil. " );
} else if ( response . status === 403 ) {
throw new Error ( " GitHub API istek limitine ulaşıldı . Lütfen daha sonra tekrar deneyin. " );
} else {
throw new Error ( `GitHub API Hatası (Kod: ${response.status})` );
}
}
return response . json ();
})
. then ( commits => {
if ( ! Array . isArray ( commits )) {
throw new Error ( " Geçersiz API yanı tı . " );
}
// Filter commits by the selected date
const dayCommits = commits . filter ( item => {
const dateStr = item . commit . author . date ;
if ( ! dateStr ) return false ;
const d = new Date ( dateStr );
const dateOnly = formatDateToIstanbul ( d );
return dateOnly === dateVal ;
});
if ( dayCommits . length === 0 ) {
// No commits found for this date. Show fallback list of latest 10 commits.
const fallbackContainer = document . getElementById ( 'git-commits-fallback-container' );
const fallbackList = document . getElementById ( 'git-commits-fallback-list' );
if ( fallbackContainer && fallbackList ) {
fallbackList . innerHTML = '' ;
const latestCommits = commits . slice ( 0 , 10 );
if ( latestCommits . length === 0 ) {
fallbackList . innerHTML = `<p class="text-slate-400 italic">Depoda hiç commit bulunamadı .</p>` ;
} else {
latestCommits . 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' , { timeZone : 'Europe/Istanbul' , hour : '2-digit' , minute : '2-digit' });
const commitDateOnly = formatDateToIstanbul ( authorDate );
// format date to d.m.Y
const [ cy , cm , cd ] = commitDateOnly . split ( '-' );
const formattedCommitDate = `${cd}.${cm}.${cy}` ;
const row = document . createElement ( 'div' );
row . className = " flex items-center justify-between gap-3 p-2 hover:bg-slate-100 rounded-lg border border-slate-100 transition-colors " ;
const infoDiv = document . createElement ( 'div' );
infoDiv . className = " flex-grow min-w-0 " ;
const timeSpan = document . createElement ( 'span' );
timeSpan . className = " font-bold text-slate-700 select-all " ;
timeSpan . textContent = `[${formattedCommitDate} ${time}] ` ;
const shaSpan = document . createElement ( 'span' );
shaSpan . className = " text-slate-500 font-semibold select-all " ;
shaSpan . textContent = `(${sha}) ` ;
const msgSpan = document . createElement ( 'span' );
msgSpan . className = " text-slate-600 truncate block sm:inline-block max-w-full font-medium ml-1 " ;
msgSpan . textContent = msg ;
infoDiv . appendChild ( timeSpan );
infoDiv . appendChild ( shaSpan );
infoDiv . appendChild ( msgSpan );
const addBtn = document . createElement ( 'button' );
addBtn . type = 'button' ;
addBtn . className = " px-2 py-1 bg-blue-50 text-blue-600 hover:bg-blue-600 hover:text-white font-extrabold rounded transition-all flex-shrink-0 text-[10px] " ;
addBtn . textContent = " Ekle " ;
addBtn . addEventListener ( 'click' , () => {
appendCommitToQuill ( sha , `${formattedCommitDate} ${time}` , msg );
});
row . appendChild ( infoDiv );
row . appendChild ( addBtn );
fallbackList . appendChild ( row );
});
}
fallbackContainer . classList . remove ( 'hidden' );
alert ( `Bu staj gününe (${formattedDate}) ait otomatik GitHub commit'i bulunamadı . Son 10 commit'iniz aşağıda listelenmiştir, rapora eklemek istediklerinizi seçebilirsiniz.` );
} else {
alert ( `Bu staj gününe (${formattedDate}) 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' , { timeZone : 'Europe/Istanbul' , hour : '2-digit' , minute : '2-digit' });
commitHtml += `<li><strong>[${time}]</strong> (<em>${sha}</em>) ${msg}</li>` ;
});
commitHtml += " </ul> " ;
const currentHtml = quill . root . innerHTML . trim ();
const headerHtml = " <p><strong>Git Commit Mesajları :</strong></p> " ;
let newHtml = " " ;
if ( currentHtml !== " " && currentHtml !== " <p><br></p> " ) {
newHtml = currentHtml + " <br><br> " + headerHtml + commitHtml ;
} else {
newHtml = headerHtml + commitHtml ;
}
quill . setContents ([]);
quill . clipboard . dangerouslyPasteHTML ( newHtml );
})
. catch ( err => {
alert ( " Commitler çekilirken hata oluştu: " + err . message );
})
. finally (() => {
gitBtn . disabled = false ;
gitBtn . innerHTML = originalContent ;
});
}
}
function saveActiveDay () {
function saveActiveDay () {
@@ -1043,17 +1227,21 @@
statusText . className = " text-xs font-semibold text-green-600 " ;
statusText . className = " text-xs font-semibold text-green-600 " ;
btn . setAttribute ( 'data-saved-content' , contentVal );
btn . setAttribute ( 'data-saved-content' , contentVal );
btn . setAttribute ( 'data-approved' , '0' );
const badge = document . getElementById ( `badge-day-${activeDayIdx}` );
const badge = document . getElementById ( `badge-day-${activeDayIdx}` );
const hasContent = contentVal . trim () !== '' && contentVal !== '<p><br></p>' ;
if ( badge ) {
if ( badge ) {
if ( contentVal . trim () !== '' ) {
if ( hasContent ) {
badge . textContent = 'DOLU' ;
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 " ;
badge . className = " inline-flex px-2 py-0.5 rounded-lg text-[9px] font-extrabold uppercase bg-amber -50 text-amber -700 border border-amber -200 " ;
} else {
} else {
badge . textContent = 'BOŞ' ;
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 " ;
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 " ;
}
}
}
}
updateEditorStatusBadge ( false , hasContent );
} else {
} else {
statusText . textContent = data . message || " Kaydedilemedi. " ;
statusText . textContent = data . message || " Kaydedilemedi. " ;
statusText . className = " text-xs font-semibold text-red-600 " ;
statusText . className = " text-xs font-semibold text-red-600 " ;