feat: replace eager in-memory GitHub commit fetching with on-demand retrieval and add a fallback UI for missing commits.

This commit is contained in:
Ümit Tunç
2026-07-01 23:34:42 +03:00
parent 09e92506fd
commit f557d4b91c
@@ -733,6 +733,19 @@
</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 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">
@@ -855,56 +868,55 @@
// 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 = @json($intern->github_repo);
let allGithubCommits = {};
let activeDayIdx = 0;
function fetchAllCommitsInMemory() {
if (!githubRepoUrl) return;
function hideFallbackCommits() {
const container = document.getElementById('git-commits-fallback-container');
if (container) {
container.classList.add('hidden');
}
}
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 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);
function formatDateToIstanbul(d) {
try {
const formatter = new Intl.DateTimeFormat('en-US', {
timeZone: 'Europe/Istanbul',
year: 'numeric',
month: '2-digit',
day: '2-digit'
});
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 appendCommitToQuill(sha, time, msg) {
const escapedMsg = msg.replace(/</g, "&lt;").replace(/>/g, "&gt;");
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) {
@@ -938,8 +950,12 @@
document.getElementById('editor-day-title').textContent = `${dayNum}. Gün`;
document.getElementById('editor-day-date').textContent = dateFormatted;
quill.root.innerHTML = savedContent || '';
quill.setContents([]);
if (savedContent) {
quill.clipboard.dangerouslyPasteHTML(savedContent);
}
document.getElementById('save-status').textContent = '';
hideFallbackCommits();
// Future validation
const dateParts = dateVal.split('-');
@@ -979,34 +995,160 @@
if (!btn) return;
const dateVal = btn.getAttribute('data-date');
const dayCommits = allGithubCommits[dateVal] || [];
const formattedDate = btn.getAttribute('data-formatted-date');
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.");
if (!githubRepoUrl) {
alert("Lütfen önce GitHub depo URL'nizi kaydedin.");
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();
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;
let repoClean = githubRepoUrl.replace(/https?:\/\/(www\.)?github\.com\//i, '');
repoClean = repoClean.replace(/\/$/, '');
const parts = repoClean.split('/');
if (parts.length < 2) {
alert("Geçersiz GitHub URL'si.");
return;
}
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() {