feat: implement user authentication and license management system
- Added schema for users, licenses, and license hostnames in the database. - Created storage utility for reading and writing JSON files. - Developed user service for user registration, authentication, and retrieval. - Implemented authentication middleware to protect routes. - Built LicenseCard component to display license details. - Created SiteNav component for navigation with user authentication status. - Established AuthContext for managing authentication state and actions. - Developed Home page to display available plugins. - Created LicenseManager page for managing licenses with forms for creation and verification. - Implemented PluginDetail page to show detailed information about a specific plugin. - Added utility functions for date formatting.
This commit is contained in:
125
src/pages/PluginDetail.jsx
Normal file
125
src/pages/PluginDetail.jsx
Normal file
@@ -0,0 +1,125 @@
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { Link, useParams } from "react-router-dom";
|
||||
|
||||
export default function PluginDetail() {
|
||||
const { owner, repo } = useParams();
|
||||
const [data, setData] = useState(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
async function loadDetail() {
|
||||
try {
|
||||
const response = await fetch(`/api/plugins/${owner}/${repo}`);
|
||||
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]);
|
||||
|
||||
const manifest = data?.manifest;
|
||||
const displayName = manifest?.plugin_name || data?.name || repo;
|
||||
const description = manifest?.description || data?.description;
|
||||
const author = manifest?.author || "-";
|
||||
const version = manifest?.version || "-";
|
||||
|
||||
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="cta" href={data.repoUrl} target="_blank" rel="noreferrer">
|
||||
GitHub
|
||||
</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) => (
|
||||
<li key={release.tag}>
|
||||
<a href={release.url} target="_blank" rel="noreferrer">
|
||||
{release.name}
|
||||
</a>
|
||||
<span>
|
||||
{release.publishedAt
|
||||
? new Date(release.publishedAt).toLocaleDateString("nl-NL")
|
||||
: "-"}
|
||||
</span>
|
||||
</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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user