Files
PR-Helper/templates/pages/repo.html
T

290 lines
13 KiB
HTML

<!DOCTYPE html>
<html lang="zh-CN">
<head>
{{template "head" .}}
<link rel="stylesheet" href="/static/lib/diff2html.min.css">
<script src="/static/lib/d3.min.js"></script>
<script src="/static/lib/diff2html.min.js"></script>
<script src="/static/lib/highlight.min.js"></script>
<script src="/static/js/graph.js"></script>
<script src="/static/js/diff-viewer.js"></script>
<style>
.graph-container {
background: #f8fafc;
border-radius: 8px;
min-height: 300px;
overflow: auto;
}
.graph-container svg {
display: block;
}
</style>
</head>
<body class="bg-gray-50 min-h-screen">
{{template "nav" .}}
<main class="max-w-7xl mx-auto px-4 py-8">
<div class="flex items-center justify-between mb-6">
<div>
<h1 class="text-2xl font-bold text-gray-900">仓库详情</h1>
<p id="repo-url" class="text-gray-500 text-sm mt-1"></p>
</div>
<div class="flex gap-2">
<a id="btn-generate" href="#" class="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700">
生成 PR 描述
</a>
<a id="btn-review" href="#" class="px-4 py-2 bg-purple-600 text-white rounded-md hover:bg-purple-700">
AI 代码审查
</a>
</div>
</div>
<!-- Debug Info -->
<div id="debug-info" class="bg-yellow-50 border border-yellow-200 rounded-lg p-4 mb-4 text-sm hidden">
<p><strong>调试信息:</strong></p>
<p>仓库 ID: <span id="debug-id"></span></p>
<p>Refs: <span id="debug-refs"></span></p>
<p>Graph: <span id="debug-graph"></span></p>
</div>
<!-- Loading State -->
<div id="loading" class="bg-white rounded-lg shadow-md p-8 text-center">
<div class="spinner inline-block"></div>
<p class="mt-2 text-gray-500">加载仓库数据...</p>
</div>
<!-- Error State -->
<div id="error" class="bg-red-50 border border-red-200 rounded-lg p-4 hidden">
<p class="text-red-600" id="error-message"></p>
</div>
<!-- Main Content -->
<div id="main-content" class="hidden">
<!-- Tabs -->
<div class="bg-white rounded-lg shadow-md">
<div class="border-b border-gray-200">
<nav class="flex -mb-px">
<button onclick="showTab('graph')" id="tab-graph" class="px-6 py-3 text-sm font-medium border-b-2 border-blue-500 text-blue-600">
Git Graph
</button>
<button onclick="showTab('diff')" id="tab-diff" class="px-6 py-3 text-sm font-medium border-b-2 border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300">
Diff 视图
</button>
</nav>
</div>
<!-- Graph Tab -->
<div id="panel-graph" class="p-4">
<div class="flex items-center gap-4 mb-4">
<div class="flex items-center gap-2">
<label class="text-sm text-gray-600">Base:</label>
<select id="graph-base" class="border rounded px-2 py-1 text-sm">
<option value="">选择分支/标签</option>
</select>
</div>
<div class="flex items-center gap-2">
<label class="text-sm text-gray-600">Head:</label>
<select id="graph-head" class="border rounded px-2 py-1 text-sm">
<option value="">选择分支/标签</option>
</select>
</div>
</div>
<div id="git-graph" class="graph-container p-4"></div>
</div>
<!-- Diff Tab -->
<div id="panel-diff" class="p-4 hidden">
<div class="flex items-center gap-4 mb-4">
<div class="flex items-center gap-2">
<label class="text-sm text-gray-600">Base:</label>
<select id="diff-base" class="border rounded px-2 py-1 text-sm">
<option value="">选择分支/标签</option>
</select>
</div>
<div class="flex items-center gap-2">
<label class="text-sm text-gray-600">Head:</label>
<select id="diff-head" class="border rounded px-2 py-1 text-sm">
<option value="">选择分支/标签</option>
</select>
</div>
<button onclick="loadDiff()" class="px-3 py-1 bg-blue-600 text-white rounded text-sm hover:bg-blue-700">
查看 Diff
</button>
<div class="flex gap-1 ml-auto">
<button onclick="setDiffView('side-by-side')" class="px-2 py-1 text-xs bg-gray-100 rounded hover:bg-gray-200">并排</button>
<button onclick="setDiffView('unified')" class="px-2 py-1 text-xs bg-gray-100 rounded hover:bg-gray-200">统一</button>
</div>
</div>
<div id="diff-container"></div>
</div>
</div>
</div>
</main>
{{template "footer" .}}
<script>
const repoId = '{{.ID}}';
let currentRefs = [];
// Show debug info in development
const showDebug = true;
async function init() {
console.log('Initializing repo page, ID:', repoId);
if (showDebug) {
document.getElementById('debug-info').classList.remove('hidden');
document.getElementById('debug-id').textContent = repoId;
}
try {
// Load repo info first
const reposResp = await fetch('/api/repos');
if (reposResp.ok) {
const repos = await reposResp.json();
console.log('Repos:', repos);
const repo = repos.find(r => r.id == repoId || r.id === parseInt(repoId));
if (repo) {
document.getElementById('repo-url').textContent = repo.url;
document.getElementById('btn-generate').href = `/repo/${repoId}/generate`;
document.getElementById('btn-review').href = `/repo/${repoId}/review`;
} else {
showError('仓库未找到 (ID: ' + repoId + ')');
return;
}
}
// Load refs
console.log('Loading refs...');
const refsResp = await fetch(`/api/repos/${repoId}/refs`);
if (refsResp.ok) {
currentRefs = await refsResp.json();
console.log('Refs loaded:', currentRefs);
if (showDebug) {
document.getElementById('debug-refs').textContent = currentRefs.length + ' refs';
}
populateRefSelects(currentRefs);
} else {
console.error('Failed to load refs:', refsResp.status);
if (showDebug) {
document.getElementById('debug-refs').textContent = 'Error: ' + refsResp.status;
}
}
// Init graph
console.log('Loading graph...');
const graphResp = await fetch(`/api/repos/${repoId}/graph`);
if (graphResp.ok) {
const graphData = await graphResp.json();
console.log('Graph data:', graphData);
if (showDebug) {
document.getElementById('debug-graph').textContent =
(graphData.commits ? graphData.commits.length : 0) + ' commits, ' +
(graphData.refs ? graphData.refs.length : 0) + ' refs';
}
// Render graph
GitGraph.init('git-graph', repoId);
} else {
console.error('Failed to load graph:', graphResp.status);
if (showDebug) {
document.getElementById('debug-graph').textContent = 'Error: ' + graphResp.status;
}
}
// Init diff viewer
DiffViewer.init('diff-container');
// Show main content
document.getElementById('loading').classList.add('hidden');
document.getElementById('main-content').classList.remove('hidden');
} catch (err) {
console.error('Init error:', err);
showError('加载失败: ' + err.message);
}
}
function showError(message) {
document.getElementById('loading').classList.add('hidden');
document.getElementById('error').classList.remove('hidden');
document.getElementById('error-message').textContent = message;
}
function populateRefSelects(refs) {
const selects = ['graph-base', 'graph-head', 'diff-base', 'diff-head'];
const branches = refs.filter(r => !r.is_tag);
const tags = refs.filter(r => r.is_tag);
selects.forEach(selectId => {
const select = document.getElementById(selectId);
select.innerHTML = '<option value="">选择分支/标签</option>';
if (branches.length > 0) {
const group = document.createElement('optgroup');
group.label = '分支';
branches.forEach(ref => {
const opt = document.createElement('option');
opt.value = ref.name;
opt.textContent = ref.name + (ref.is_head ? ' (HEAD)' : '');
group.appendChild(opt);
});
select.appendChild(group);
}
if (tags.length > 0) {
const group = document.createElement('optgroup');
group.label = '标签';
tags.forEach(ref => {
const opt = document.createElement('option');
opt.value = ref.name;
opt.textContent = ref.name;
group.appendChild(opt);
});
select.appendChild(group);
}
});
// Auto-select main/master as base, HEAD as head
const mainBranch = branches.find(b => b.name === 'main' || b.name === 'master');
const headBranch = refs.find(r => r.is_head);
selects.forEach(selectId => {
const select = document.getElementById(selectId);
if (selectId.includes('base') && mainBranch) {
select.value = mainBranch.name;
} else if (selectId.includes('head') && headBranch) {
select.value = headBranch.name;
}
});
}
function showTab(tab) {
document.getElementById('panel-graph').classList.toggle('hidden', tab !== 'graph');
document.getElementById('panel-diff').classList.toggle('hidden', tab !== 'diff');
document.getElementById('tab-graph').classList.toggle('border-blue-500', tab === 'graph');
document.getElementById('tab-graph').classList.toggle('text-blue-600', tab === 'graph');
document.getElementById('tab-diff').classList.toggle('border-blue-500', tab === 'diff');
document.getElementById('tab-diff').classList.toggle('text-blue-600', tab === 'diff');
}
function loadDiff() {
const base = document.getElementById('diff-base').value;
const head = document.getElementById('diff-head').value;
if (!base || !head) {
alert('请选择 Base 和 Head 分支');
return;
}
DiffViewer.loadDiff(repoId, base, head);
}
function setDiffView(mode) {
DiffViewer.setViewMode(mode);
}
init();
</script>
</body>
</html>