Files
citrus/resources/views/livewire/intern-journal-timeline.blade.php
T

264 lines
15 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<div
x-data="{
githubRepoUrl: @js($this->githubRepo),
commitDays: [],
activeDayIndex: 0,
loading: false,
errorMsg: '',
isEmpty: false,
init() {
this.loadCommits();
},
loadCommits() {
if (!this.githubRepoUrl) {
this.isEmpty = true;
return;
}
this.loading = true;
this.errorMsg = '';
this.isEmpty = false;
this.commitDays = [];
let repoClean = this.githubRepoUrl.replace(/https?:\/\/(www\.)?github\.com\//i, '');
repoClean = repoClean.replace(/\/$/, '');
const parts = repoClean.split('/');
if (parts.length < 2) {
this.loading = false;
this.errorMsg = 'Github depo URL\'si çözümlenemedi. Geçerli format: https://github.com/kullanici/depo';
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) {
if (response.status === 404) throw new Error('Depo bulunamadı veya private (gizli). Deponuzun public (herkese açık) olduğundan emin olun.');
else if (response.status === 403) throw new Error('GitHub API istek limiti aşıldı. Lütfen daha sonra tekrar deneyin.');
else throw new Error('GitHub API hatası: ' + response.statusText);
}
return response.json();
})
.then(commits => {
this.loading = false;
if (!Array.isArray(commits) || commits.length === 0) {
this.isEmpty = true;
return;
}
commits.reverse();
const grouped = {};
commits.forEach(item => {
const dateStr = item.commit.author.date;
if (dateStr) {
const dateOnly = dateStr.substring(0, 10);
if (!grouped[dateOnly]) grouped[dateOnly] = [];
grouped[dateOnly].push(item);
}
});
const sortedDates = Object.keys(grouped).sort();
this.commitDays = sortedDates.map((date, index) => {
const dp = date.split('-');
return {
date: date,
dayNum: index + 1,
formattedDate: dp[2] + '.' + dp[1] + '.' + dp[0],
commits: grouped[date].map(c => ({
message: c.commit.message,
time: new Date(c.commit.author.date).toLocaleTimeString('tr-TR', { hour: '2-digit', minute: '2-digit' }),
sha: c.sha.substring(0, 7),
url: c.html_url
}))
};
});
if (this.commitDays.length === 0) {
this.isEmpty = true;
return;
}
this.activeDayIndex = 0;
})
.catch(err => {
this.loading = false;
this.errorMsg = err.message || 'Bir hata oluştu.';
});
},
get activeDay() {
return this.commitDays[this.activeDayIndex] || null;
},
navigateDay(dir) {
const n = this.activeDayIndex + dir;
if (n >= 0 && n < this.commitDays.length) this.activeDayIndex = n;
},
selectDay(i) {
this.activeDayIndex = i;
}
}"
style="margin-top: 1rem; font-family: 'Inter', 'Segoe UI', system-ui, -apple-system, sans-serif;"
>
<style>
.jt-day-btn {
transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
cursor: pointer;
}
.jt-day-btn:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.15);
}
.jt-arrow-btn {
transition: all 0.2s ease;
}
.jt-arrow-btn:hover:not(:disabled) {
transform: scale(1.08);
background: #eff6ff;
border-color: #93c5fd;
}
.jt-arrow-btn:disabled {
opacity: 0.35;
cursor: not-allowed;
}
.jt-event-card {
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.jt-event-card:hover {
transform: translateY(-2px) translateX(3px);
box-shadow: 0 12px 24px -8px rgba(59, 130, 246, 0.12);
border-color: #bfdbfe !important;
background: #ffffff !important;
}
.jt-timeline-dot {
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.jt-event-row:hover .jt-timeline-dot {
transform: scale(1.2);
background-color: #3b82f6 !important;
box-shadow: 0 0 0 5px rgba(59, 130, 246, 0.18);
}
.jt-event-row:hover .jt-timeline-dot-inner {
background-color: #ffffff !important;
}
.jt-sha-link {
transition: all 0.25s ease;
}
.jt-sha-link:hover {
background: #3b82f6 !important;
color: #ffffff !important;
border-color: #3b82f6 !important;
}
.jt-paginator-scroll::-webkit-scrollbar {
height: 4px;
}
.jt-paginator-scroll::-webkit-scrollbar-track {
background: transparent;
}
.jt-paginator-scroll::-webkit-scrollbar-thumb {
background: #cbd5e1;
border-radius: 9999px;
}
.jt-paginator-scroll {
scrollbar-width: thin;
scrollbar-color: #cbd5e1 transparent;
}
@keyframes jt-spin {
to { transform: rotate(360deg); }
}
@keyframes jt-fadeIn {
from { opacity: 0; transform: translateY(8px); }
to { opacity: 1; transform: translateY(0); }
}
.jt-fade-in {
animation: jt-fadeIn 0.4s ease-out;
}
</style>
{{-- Loader --}}
<div x-show="loading" x-cloak style="padding: 3rem 0; display: flex; flex-direction: column; align-items: center; justify-content: center;">
<div style="width: 2.5rem; height: 2.5rem; border: 2px solid #e2e8f0; border-top-color: #3b82f6; border-radius: 50%; animation: jt-spin 0.8s linear infinite; margin-bottom: 0.75rem;"></div>
<span style="font-size: 0.75rem; color: #94a3b8; font-weight: 600; letter-spacing: 0.025em;">Commit geçmişi yükleniyor...</span>
</div>
{{-- Error --}}
<div x-show="errorMsg" x-cloak x-text="errorMsg" style="padding: 1rem 1.25rem; border-radius: 1rem; background: #fef2f2; border: 1px solid #fecaca; color: #dc2626; font-size: 0.75rem; font-weight: 700;"></div>
{{-- Empty --}}
<div x-show="isEmpty && !loading" x-cloak style="padding: 2rem; border-radius: 1.5rem; border: 1px solid #f1f5f9; background: #ffffff; text-align: center; color: #94a3b8; font-style: italic; font-size: 0.875rem;">
Henüz hiçbir commit verisi bulunamadı veya depo adresi yapılandırılmadı.
</div>
{{-- Day Paginator --}}
<div x-show="commitDays.length > 0" x-cloak style="display: flex; align-items: center; justify-content: space-between; gap: 1rem; padding: 0.75rem; background: rgba(255,255,255,0.8); backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px); border: 1px solid #e2e8f0; border-radius: 1.5rem; margin-bottom: 1.75rem; user-select: none; box-shadow: 0 1px 3px rgba(0,0,0,0.04);">
<button type="button" @click="navigateDay(-1)" :disabled="activeDayIndex === 0" class="jt-arrow-btn" style="width: 2.75rem; height: 2.75rem; border-radius: 0.875rem; background: #ffffff; color: #64748b; border: 1px solid #e2e8f0; display: flex; align-items: center; justify-content: center; font-size: 1.25rem; box-shadow: 0 1px 2px rgba(0,0,0,0.04);">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M15 18l-6-6 6-6"/></svg>
</button>
<div class="jt-paginator-scroll" style="flex: 1; overflow-x: auto; padding: 0.375rem 0;">
<div style="display: flex; gap: 0.625rem; justify-content: center; min-width: max-content; padding: 0 0.5rem;">
<template x-for="(day, index) in commitDays" :key="day.date">
<button type="button" @click="selectDay(index)" class="jt-day-btn" :style="index === activeDayIndex
? 'padding: 0.5rem 1.25rem; border-radius: 0.875rem; display: flex; flex-direction: column; align-items: center; justify-content: center; border: 1.5px solid #93c5fd; background: linear-gradient(135deg, #eff6ff, #dbeafe); color: #1d4ed8; font-weight: 800; box-shadow: 0 2px 8px rgba(59, 130, 246, 0.15);'
: 'padding: 0.5rem 1.25rem; border-radius: 0.875rem; display: flex; flex-direction: column; align-items: center; justify-content: center; border: 1px solid #e2e8f0; background: #ffffff; color: #64748b; font-weight: 600;'">
<span style="font-size: 0.75rem; line-height: 1.2;" x-text="day.dayNum + '. Gün'"></span>
<span style="font-size: 0.6rem; margin-top: 0.125rem; opacity: 0.7; font-weight: 500;" x-text="day.formattedDate"></span>
</button>
</template>
</div>
</div>
<button type="button" @click="navigateDay(1)" :disabled="activeDayIndex === commitDays.length - 1" class="jt-arrow-btn" style="width: 2.75rem; height: 2.75rem; border-radius: 0.875rem; background: #ffffff; color: #64748b; border: 1px solid #e2e8f0; display: flex; align-items: center; justify-content: center; font-size: 1.25rem; box-shadow: 0 1px 2px rgba(0,0,0,0.04);">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M9 18l6-6-6-6"/></svg>
</button>
</div>
{{-- Active Day Card --}}
<div x-show="activeDay" x-cloak class="jt-fade-in" :key="activeDayIndex" style="background: #ffffff; border: 1px solid #f1f5f9; border-radius: 1.5rem; padding: 1.75rem 2rem; box-shadow: 0 4px 20px -4px rgba(15, 23, 42, 0.06);">
{{-- Day Header --}}
<div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 1.75rem; padding-bottom: 1.25rem; border-bottom: 1px solid #f1f5f9;">
<div style="display: flex; align-items: center; gap: 0.875rem;">
<span style="font-size: 0.875rem; font-weight: 800; color: #2563eb; background: linear-gradient(135deg, #eff6ff, #dbeafe); padding: 0.5rem 1.125rem; border-radius: 1rem; box-shadow: 0 1px 3px rgba(37, 99, 235, 0.1);" x-text="activeDay ? activeDay.dayNum + '. Gün' : ''"></span>
<span style="font-size: 0.8125rem; font-weight: 600; color: #94a3b8; display: flex; align-items: center; gap: 0.375rem;">
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="color: #60a5fa;"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/></svg>
<span x-text="activeDay ? activeDay.formattedDate : ''"></span>
</span>
</div>
<span style="font-size: 0.6875rem; font-weight: 700; color: #94a3b8; background: #f8fafc; border: 1px solid #f1f5f9; padding: 0.375rem 0.875rem; border-radius: 0.625rem;" x-text="activeDay ? activeDay.commits.length + ' Commit' : ''"></span>
</div>
{{-- Timeline --}}
<div style="position: relative; padding-left: 2.25rem;">
{{-- Gradient Timeline Line --}}
<div style="position: absolute; left: 11px; top: 8px; bottom: 8px; width: 2px; background: linear-gradient(180deg, #3b82f6 0%, #818cf8 40%, #c7d2fe 100%); border-radius: 2px;"></div>
<div style="display: flex; flex-direction: column; gap: 1.25rem;">
<template x-for="(commit, ci) in (activeDay ? activeDay.commits : [])" :key="ci">
<div class="jt-event-row" style="position: relative;">
{{-- Timeline Dot --}}
<div class="jt-timeline-dot" style="position: absolute; left: -32px; top: 6px; width: 24px; height: 24px; border-radius: 50%; background: #ffffff; border: 2.5px solid #3b82f6; display: flex; align-items: center; justify-content: center; z-index: 1;">
<div class="jt-timeline-dot-inner" style="width: 8px; height: 8px; border-radius: 50%; background: #3b82f6;"></div>
</div>
{{-- Event Card --}}
<div class="jt-event-card" style="padding: 1.125rem 1.25rem; background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 1rem; box-shadow: 0 1px 3px rgba(0,0,0,0.03);">
<div style="display: flex; align-items: center; justify-content: space-between; gap: 1rem; margin-bottom: 0.625rem;">
<span style="font-size: 0.75rem; font-weight: 700; color: #94a3b8; display: flex; align-items: center; gap: 0.375rem;">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#60a5fa" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>
<span x-text="commit.time"></span>
</span>
<a :href="commit.url" target="_blank" class="jt-sha-link" x-text="commit.sha" style="font-size: 0.6875rem; font-family: 'SF Mono', 'Fira Code', monospace; font-weight: 700; color: #2563eb; background: #eff6ff; padding: 0.25rem 0.75rem; border-radius: 0.5rem; border: 1px solid #bfdbfe; text-decoration: none;"></a>
</div>
<p style="font-size: 0.8125rem; font-weight: 600; color: #334155; line-height: 1.6; white-space: pre-line; margin: 0;" x-text="commit.message"></p>
</div>
</div>
</template>
</div>
</div>
</div>
</div>