- Implemented repoService for database interactions including count, list, get, create, update, and delete operations. - Created RepoManager component for managing repositories with a user interface. - Added forms for creating and editing repositories, including validation and error handling. - Integrated API calls for fetching, creating, updating, and deleting repositories. - Enhanced user experience with loading states and action feedback messages.
160 lines
6.9 KiB
JavaScript
160 lines
6.9 KiB
JavaScript
import { useEffect, useMemo, useState } from "react";
|
|
import { Link, useParams, useSearchParams } from "react-router-dom";
|
|
|
|
export default function PluginDetail() {
|
|
const { owner, repo } = useParams();
|
|
const [searchParams] = useSearchParams();
|
|
const provider = (searchParams.get("provider") || "github").toLowerCase();
|
|
const baseUrl = searchParams.get("baseUrl") || "";
|
|
const repoId = searchParams.get("repoId") || "";
|
|
const [data, setData] = useState(null);
|
|
const [loading, setLoading] = useState(true);
|
|
const [error, setError] = useState(null);
|
|
|
|
useEffect(() => {
|
|
async function loadDetail() {
|
|
try {
|
|
const query = new URLSearchParams();
|
|
if (provider) {
|
|
query.set("provider", provider);
|
|
}
|
|
if (baseUrl) {
|
|
query.set("baseUrl", baseUrl);
|
|
}
|
|
if (repoId) {
|
|
query.set("repoId", repoId);
|
|
}
|
|
const search = query.toString();
|
|
const response = await fetch(
|
|
`/api/plugins/${owner}/${repo}${search.length > 0 ? `?${search}` : ""}`
|
|
);
|
|
if (!response.ok) {
|
|
throw new Error("Kon details niet laden");
|
|
}
|
|
const detail = await response.json();
|
|
setData(detail);
|
|
} catch (err) {
|
|
setError("Laden van plugin details is mislukt.");
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
}
|
|
|
|
loadDetail();
|
|
}, [owner, repo, provider, baseUrl]);
|
|
|
|
const manifest = data?.manifest;
|
|
const displayName = data?.label || manifest?.plugin_name || data?.name || repo;
|
|
const description = manifest?.description || data?.description;
|
|
const author = manifest?.author || "-";
|
|
const version = manifest?.version || "-";
|
|
const repositoryLabel = data?.provider === "gitea" ? "Gitea" : "GitHub";
|
|
const latestDownload = data?.download;
|
|
|
|
const releases = useMemo(() => data?.releases || [], [data]);
|
|
const commits = useMemo(() => data?.commits || [], [data]);
|
|
|
|
return (
|
|
<div className="page">
|
|
<header className="detail-hero">
|
|
<div>
|
|
<p className="eyebrow">Plugin details</p>
|
|
<h1>{displayName}</h1>
|
|
<p className="subtitle">{description}</p>
|
|
</div>
|
|
<div className="detail-actions">
|
|
<Link className="ghost" to="/">← Terug</Link>
|
|
{data?.repoUrl && (
|
|
<a className="ghost" href={data.repoUrl} target="_blank" rel="noreferrer">
|
|
{repositoryLabel}
|
|
</a>
|
|
)}
|
|
{latestDownload?.url && (
|
|
<a className="cta" href={latestDownload.url} target="_blank" rel="noreferrer">
|
|
Download {latestDownload.version}
|
|
</a>
|
|
)}
|
|
</div>
|
|
</header>
|
|
|
|
{loading && <div className="state">Bezig met laden…</div>}
|
|
{error && <div className="state error">{error}</div>}
|
|
|
|
{!loading && !error && data && (
|
|
<section className="detail-grid">
|
|
<div className="card">
|
|
<h2>Manifest</h2>
|
|
<div className="detail-list">
|
|
<div>
|
|
<span>Naam</span>
|
|
<strong>{displayName}</strong>
|
|
</div>
|
|
<div>
|
|
<span>Versie</span>
|
|
<strong>{version}</strong>
|
|
</div>
|
|
<div>
|
|
<span>Auteur</span>
|
|
<strong>{author}</strong>
|
|
</div>
|
|
<div>
|
|
<span>Repository</span>
|
|
<strong>{data.fullName}</strong>
|
|
</div>
|
|
</div>
|
|
{manifest?.author_url && (
|
|
<a className="link" href={manifest.author_url} target="_blank" rel="noreferrer">
|
|
Auteur website →
|
|
</a>
|
|
)}
|
|
</div>
|
|
|
|
<div className="card">
|
|
<h2>Releases</h2>
|
|
{releases.length === 0 && <p>Geen releases gevonden.</p>}
|
|
<ul className="list">
|
|
{releases.map((release) => {
|
|
const key = release.tag || release.name || release.url;
|
|
return (
|
|
<li key={key}>
|
|
<div>
|
|
<a href={release.url} target="_blank" rel="noreferrer">
|
|
{release.name}
|
|
</a>
|
|
<span>
|
|
{release.publishedAt
|
|
? new Date(release.publishedAt).toLocaleDateString("nl-NL")
|
|
: "-"}
|
|
</span>
|
|
</div>
|
|
{release.downloadUrl && (
|
|
<a className="ghost" href={release.downloadUrl} target="_blank" rel="noreferrer">
|
|
Download
|
|
</a>
|
|
)}
|
|
</li>
|
|
);
|
|
})}
|
|
</ul>
|
|
</div>
|
|
|
|
<div className="card">
|
|
<h2>Recente commits</h2>
|
|
{commits.length === 0 && <p>Geen commits gevonden.</p>}
|
|
<ul className="list">
|
|
{commits.map((commit) => (
|
|
<li key={commit.sha}>
|
|
<a href={commit.url} target="_blank" rel="noreferrer">
|
|
{commit.message?.split("\n")[0] || commit.sha.slice(0, 7)}
|
|
</a>
|
|
<span>{commit.author || "-"}</span>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
</div>
|
|
</section>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|