feat: add GitHub username field and implement commit filtering by author in intern dashboard

This commit is contained in:
Ümit Tunç
2026-07-01 23:46:12 +03:00
parent 26670fcca5
commit d0292af20c
4 changed files with 257 additions and 28 deletions
+15 -2
View File
@@ -208,22 +208,35 @@ class CareerController extends Controller
$request->validate([
'github_repo' => 'required|string|url|max:255',
'github_username' => 'nullable|string|max:100',
], [
'github_repo.required' => 'Lütfen Github depo URL\'sini girin.',
'github_repo.url' => 'Geçerli bir URL girilmelidir.',
'github_repo.max' => 'Github depo URL\'si en fazla 255 karakter olabilir.',
'github_username.max' => 'Github kullanıcı adı en fazla 100 karakter olabilir.',
]);
$intern = CareerApplication::findOrFail(session('intern_id'));
// Sanitize & format github URL
$repoUrl = trim($request->github_repo);
$username = trim($request->github_username);
$intern->update([
'github_repo' => $repoUrl
'github_repo' => $repoUrl,
'github_username' => $username ?: null,
]);
return redirect()->back()->with('success', 'Github deposu başarıyla güncellendi.');
if ($request->expectsJson() || $request->ajax()) {
return response()->json([
'success' => true,
'message' => 'Github bilgileri başarıyla güncellendi.',
'github_repo' => $repoUrl,
'github_username' => $username ?: null,
]);
}
return redirect()->back()->with('success', 'Github bilgileri başarıyla güncellendi.');
}
public function downloadMarkdown()
+1
View File
@@ -30,6 +30,7 @@ class CareerApplication extends Model
'internship_end_date',
'internship_total_days',
'github_repo',
'github_username',
'certificate_code',
'transcript_markdown',
'notebook_supervisor_signed',
@@ -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('career_applications', function (Blueprint $table) {
$table->string('github_username')->nullable()->after('github_repo');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('career_applications', function (Blueprint $table) {
$table->dropColumn('github_username');
});
}
};
@@ -655,15 +655,23 @@
<div class="p-5 rounded-2xl border border-slate-100 bg-slate-50/50 mb-6">
<form action="{{ route('intern.save-github') }}" method="POST" class="space-y-4">
@csrf
<div>
<label for="github_repo" class="block text-xs font-extrabold text-slate-500 uppercase tracking-wider mb-2">GitHub Public Depo URL'si</label>
<div class="flex flex-col sm:flex-row gap-3">
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label for="github_repo" class="block text-xs font-extrabold text-slate-500 uppercase tracking-wider mb-2">GitHub Public Depo URL'si</label>
<input type="url" name="github_repo" id="github_repo" placeholder="https://github.com/kullaniciadi/depo-adi" value="{{ $intern->github_repo }}" required 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" />
<button type="submit" class="px-5 py-3 bg-blue-600 hover:bg-blue-700 text-white font-bold rounded-xl text-sm transition-all shadow-md shadow-blue-500/10 flex-shrink-0">
{{ $intern->github_repo ? 'Güncelle' : 'Kaydet' }}
</button>
</div>
<span class="block text-xs text-slate-400 mt-2"><i class="uil uil-info-circle text-blue-600"></i> Deponuzun <strong>public (herkese açık)</strong> olması gerekmektedir.</span>
<div>
<label for="github_username" class="block text-xs font-extrabold text-slate-500 uppercase tracking-wider mb-2">GitHub Kullanıcı Adınız (Opsiyonel)</label>
<input type="text" name="github_username" id="github_username" placeholder="GitHub kullanıcı adınız (örn: Emresatil)" value="{{ $intern->github_username }}" 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" />
</div>
</div>
<div class="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4 pt-2 border-t border-slate-100">
<span class="text-xs text-slate-400 leading-normal max-w-xl">
<i class="uil uil-info-circle text-blue-600 text-sm"></i> Deponuzun <strong>public</strong> olması gerekir. Birden fazla kişi aynı projede çalışıyorsa, kendi commitlerinizi süzmek için kullanıcı adınızı yazmanız önerilir.
</span>
<button type="submit" class="px-5 py-3 bg-blue-600 hover:bg-blue-700 text-white font-bold rounded-xl text-sm transition-all shadow-md shadow-blue-500/10 flex-shrink-0 w-full sm:w-auto">
Bilgileri Kaydet
</button>
</div>
</form>
</div>
@@ -822,6 +830,39 @@
</div>
</div>
<!-- Author Selection Modal -->
<div id="author-modal" class="fixed inset-0 bg-slate-900/60 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
<div class="bg-white rounded-3xl p-6 max-w-md w-full shadow-2xl border border-slate-100/50 transform scale-95 opacity-0 transition-all duration-300" id="author-modal-card">
<div class="flex justify-between items-start mb-4">
<div>
<h3 class="text-lg font-bold text-slate-800 flex items-center gap-2">
<i class="uil uil-users-alt text-blue-600"></i>
<span>GitHub Yazar Seçimi</span>
</h3>
<p class="text-xs text-slate-400 mt-1">Bu günün commitlerinde birden fazla yazar tespit edildi. Hangisi sizsiniz?</p>
</div>
<button onclick="closeAuthorModal()" class="text-slate-400 hover:text-slate-600">
<i class="uil uil-multiply text-xl"></i>
</button>
</div>
<div id="author-list-container" class="space-y-2 mb-4 max-h-[220px] overflow-y-auto pr-1 no-scrollbar">
<!-- Dynamically populated author options -->
</div>
<div class="flex items-center gap-2 mb-5 bg-slate-50 p-3 rounded-xl border border-slate-200/60">
<input type="checkbox" id="save-selected-author-pref" class="rounded text-blue-600 focus:ring-blue-500 w-4 h-4" checked />
<label for="save-selected-author-pref" class="text-xs text-slate-600 font-bold select-none cursor-pointer">
Seçtiğim kullanıcı adını profilime kaydet ve varsayılan yap
</label>
</div>
<div class="flex justify-end gap-2">
<button type="button" onclick="closeAuthorModal()" class="px-4 py-2 bg-slate-100 hover:bg-slate-200 text-slate-600 font-bold rounded-xl text-xs transition-all">İptal</button>
</div>
</div>
</div>
@push('styles')
<link href="https://cdn.jsdelivr.net/npm/quill@2.0.2/dist/quill.snow.css" rel="stylesheet" />
<script src="https://cdn.jsdelivr.net/npm/quill@2.0.2/dist/quill.js"></script>
@@ -888,8 +929,148 @@
});
const githubRepoUrl = @json($intern->github_repo);
let githubUsername = @json($intern->github_username);
let activeDayIdx = 0;
let activeCommitsToProcess = [];
let activeCommitsDateVal = '';
function openAuthorModal(uniqueAuthors) {
const modal = document.getElementById('author-modal');
const card = document.getElementById('author-modal-card');
const container = document.getElementById('author-list-container');
if (!modal || !card || !container) return;
container.innerHTML = '';
// 1. Add "Hepsi" (All commits) option
const allBtn = document.createElement('button');
allBtn.type = 'button';
allBtn.className = "w-full p-3 rounded-xl border border-slate-200 hover:border-blue-400 hover:bg-blue-50/20 text-left transition-all font-extrabold text-xs text-slate-700 flex items-center justify-between";
allBtn.innerHTML = `
<span>Tüm Commitler (Filtreleme Yapma)</span>
<i class="uil uil-angle-right text-base text-slate-400"></i>
`;
allBtn.addEventListener('click', () => {
insertCommitsForAuthor(null);
});
container.appendChild(allBtn);
// 2. Add each unique author option
uniqueAuthors.forEach(author => {
const btn = document.createElement('button');
btn.type = 'button';
btn.className = "w-full p-3 rounded-xl border border-slate-200 hover:border-blue-400 hover:bg-blue-50/20 text-left transition-all font-extrabold text-xs text-slate-700 flex items-center justify-between mt-2";
btn.innerHTML = `
<span>${author}</span>
<i class="uil uil-angle-right text-base text-slate-400"></i>
`;
btn.addEventListener('click', () => {
insertCommitsForAuthor(author);
});
container.appendChild(btn);
});
modal.classList.remove('hidden');
setTimeout(() => {
card.classList.remove('scale-95', 'opacity-0');
card.classList.add('scale-100', 'opacity-100');
}, 10);
}
function closeAuthorModal() {
const modal = document.getElementById('author-modal');
const card = document.getElementById('author-modal-card');
if (!modal || !card) return;
card.classList.remove('scale-100', 'opacity-100');
card.classList.add('scale-95', 'opacity-0');
setTimeout(() => {
modal.classList.add('hidden');
}, 300);
}
function insertCommitsForAuthor(author) {
closeAuthorModal();
const savePref = document.getElementById('save-selected-author-pref');
if (author && savePref && savePref.checked) {
saveGithubUsernamePreference(author);
}
let dayCommits = activeCommitsToProcess;
if (author) {
dayCommits = activeCommitsToProcess.filter(item => {
const commitAuthorName = item.commit.author.name;
const commitAuthorLogin = item.author ? item.author.login : null;
return (commitAuthorLogin && commitAuthorLogin.toLowerCase() === author.toLowerCase()) ||
(commitAuthorName && commitAuthorName.toLowerCase() === author.toLowerCase());
});
}
renderCommitsToEditor(dayCommits);
}
function saveGithubUsernamePreference(username) {
if (!githubRepoUrl) return;
fetch("{{ route('intern.save-github') }}", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-CSRF-TOKEN": "{{ csrf_token() }}"
},
body: JSON.stringify({
github_repo: githubRepoUrl,
github_username: username
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
githubUsername = username;
const usernameInput = document.getElementById('github_username');
if (usernameInput) {
usernameInput.value = username;
}
}
})
.catch(err => {
console.warn("Kullanıcı adı tercihi kaydedilemedi.", err);
});
}
function renderCommitsToEditor(commitsList) {
if (commitsList.length === 0) {
alert("Bu yazar için seçilen güne ait commit bulunamadı.");
return;
}
let commitHtml = "<ul>";
const sorted = [...commitsList].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);
}
function hideFallbackCommits() {
const container = document.getElementById('git-commits-fallback-container');
if (container) {
@@ -1160,29 +1341,35 @@
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>`;
// Store these commits in memory for processing
activeCommitsToProcess = dayCommits;
activeCommitsDateVal = dateVal;
// Get unique authors
const authorsSet = new Set();
dayCommits.forEach(item => {
const login = item.author ? item.author.login : null;
const name = item.commit.author.name;
if (login) authorsSet.add(login);
if (name) authorsSet.add(name);
});
commitHtml += "</ul>";
const uniqueAuthors = Array.from(authorsSet);
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;
// If there are multiple authors
if (uniqueAuthors.length > 1) {
// If we have a saved githubUsername that matches one of the unique authors
const matchedAuthor = uniqueAuthors.find(a => githubUsername && a.toLowerCase() === githubUsername.toLowerCase());
if (matchedAuthor) {
// Automatically filter and insert without modal
insertCommitsForAuthor(matchedAuthor);
} else {
// Show modal to choose
openAuthorModal(uniqueAuthors);
}
} else {
newHtml = headerHtml + commitHtml;
// Only 1 author, insert directly
renderCommitsToEditor(dayCommits);
}
quill.setContents([]);
quill.clipboard.dangerouslyPasteHTML(newHtml);
})
.catch(err => {
alert("Commitler çekilirken hata oluştu: " + err.message);