first commit
This commit is contained in:
157
src/App.css
Normal file
157
src/App.css
Normal file
@@ -0,0 +1,157 @@
|
||||
:root {
|
||||
color-scheme: light;
|
||||
}
|
||||
|
||||
.app {
|
||||
min-height: 100vh;
|
||||
background: radial-gradient(circle at top, #f5f7ff, #ffffff 45%);
|
||||
color: #0f172a;
|
||||
font-family: "Inter", system-ui, sans-serif;
|
||||
padding: 48px 8vw 64px;
|
||||
}
|
||||
|
||||
.hero {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 24px;
|
||||
margin-bottom: 48px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.eyebrow {
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.2em;
|
||||
font-size: 12px;
|
||||
color: #6366f1;
|
||||
font-weight: 600;
|
||||
margin: 0 0 8px;
|
||||
}
|
||||
|
||||
.hero h1 {
|
||||
font-size: clamp(2.4rem, 4vw, 3.4rem);
|
||||
margin: 0 0 8px;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
margin: 0;
|
||||
font-size: 1.1rem;
|
||||
color: #475569;
|
||||
max-width: 520px;
|
||||
}
|
||||
|
||||
.cta {
|
||||
background: #4f46e5;
|
||||
color: #fff;
|
||||
padding: 12px 20px;
|
||||
border-radius: 999px;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
box-shadow: 0 8px 24px rgba(79, 70, 229, 0.2);
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
}
|
||||
|
||||
.cta:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 12px 30px rgba(79, 70, 229, 0.3);
|
||||
}
|
||||
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.card {
|
||||
background: #fff;
|
||||
border-radius: 20px;
|
||||
padding: 24px;
|
||||
box-shadow: 0 12px 30px rgba(15, 23, 42, 0.08);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.card h2 {
|
||||
margin: 0;
|
||||
font-size: 1.3rem;
|
||||
}
|
||||
|
||||
.card p {
|
||||
margin: 0;
|
||||
color: #475569;
|
||||
}
|
||||
|
||||
.pill {
|
||||
background: #eef2ff;
|
||||
color: #4338ca;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
padding: 4px 10px;
|
||||
border-radius: 999px;
|
||||
}
|
||||
|
||||
.meta {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
font-size: 0.9rem;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.topics {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.topic {
|
||||
background: #f1f5f9;
|
||||
color: #475569;
|
||||
font-size: 0.75rem;
|
||||
padding: 4px 8px;
|
||||
border-radius: 999px;
|
||||
}
|
||||
|
||||
.link {
|
||||
margin-top: auto;
|
||||
color: #4f46e5;
|
||||
font-weight: 600;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.state {
|
||||
background: #fff;
|
||||
border-radius: 16px;
|
||||
padding: 20px;
|
||||
box-shadow: 0 10px 20px rgba(15, 23, 42, 0.08);
|
||||
color: #475569;
|
||||
}
|
||||
|
||||
.state.error {
|
||||
background: #fee2e2;
|
||||
color: #b91c1c;
|
||||
}
|
||||
|
||||
.footer {
|
||||
margin-top: 48px;
|
||||
color: #94a3b8;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.app {
|
||||
padding: 40px 6vw 56px;
|
||||
}
|
||||
|
||||
.hero {
|
||||
align-items: flex-start;
|
||||
}
|
||||
}
|
||||
80
src/App.jsx
Normal file
80
src/App.jsx
Normal file
@@ -0,0 +1,80 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import "./App.css";
|
||||
|
||||
export default function App() {
|
||||
const [plugins, setPlugins] = useState([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState(null);
|
||||
const [lastSync, setLastSync] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
async function loadPlugins() {
|
||||
try {
|
||||
const response = await fetch("/api/plugins");
|
||||
if (!response.ok) {
|
||||
throw new Error("Kon plugins niet laden");
|
||||
}
|
||||
const data = await response.json();
|
||||
setPlugins(data.items || []);
|
||||
setLastSync(data.updatedAt);
|
||||
} catch (err) {
|
||||
setError("Laden van GitHub data is mislukt.");
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
loadPlugins();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="app">
|
||||
<header className="hero">
|
||||
<div>
|
||||
<p className="eyebrow">WordPress plugin overzicht</p>
|
||||
</div>
|
||||
<a className="cta" href="https://github.com/SitiWeb" target="_blank" rel="noreferrer">
|
||||
GitHub SitiWeb
|
||||
</a>
|
||||
</header>
|
||||
|
||||
<section className="grid">
|
||||
{loading && <div className="state">Bezig met laden…</div>}
|
||||
{error && <div className="state error">{error}</div>}
|
||||
{!loading && !error && plugins.length === 0 && (
|
||||
<div className="state">Geen repositories gevonden.</div>
|
||||
)}
|
||||
{plugins.map((plugin) => (
|
||||
<article className="card" key={plugin.fullName}>
|
||||
<div className="card-header">
|
||||
<h2>{plugin.name}</h2>
|
||||
<span className="pill">{plugin.fullName}</span>
|
||||
</div>
|
||||
<p>{plugin.description}</p>
|
||||
<div className="meta">
|
||||
<span>★ {plugin.stars}</span>
|
||||
<span>Forks {plugin.forks}</span>
|
||||
<span>Issues {plugin.issues}</span>
|
||||
</div>
|
||||
{plugin.topics.length > 0 && (
|
||||
<div className="topics">
|
||||
{plugin.topics.slice(0, 4).map((topic) => (
|
||||
<span className="topic" key={topic}>{topic}</span>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
<a className="link" href={plugin.repoUrl} target="_blank" rel="noreferrer">
|
||||
Bekijk op GitHub →
|
||||
</a>
|
||||
</article>
|
||||
))}
|
||||
</section>
|
||||
|
||||
<footer className="footer">
|
||||
<span>
|
||||
Laatste sync: {lastSync ? new Date(lastSync).toLocaleString("nl-NL") : "-"}
|
||||
</span>
|
||||
</footer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
13
src/index.css
Normal file
13
src/index.css
Normal file
@@ -0,0 +1,13 @@
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
display: block;
|
||||
}
|
||||
10
src/main.jsx
Normal file
10
src/main.jsx
Normal file
@@ -0,0 +1,10 @@
|
||||
import React from "react";
|
||||
import { createRoot } from "react-dom/client";
|
||||
import App from "./App.jsx";
|
||||
import "./index.css";
|
||||
|
||||
createRoot(document.getElementById("root")).render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>
|
||||
);
|
||||
Reference in New Issue
Block a user