Skip to content

Commit a97ec01

Browse files
committed
Sync member info
1 parent bf73251 commit a97ec01

File tree

9 files changed

+576
-3
lines changed

9 files changed

+576
-3
lines changed

.env.example

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,17 @@ DATABASE_URL="postgresql://postgres:password@localhost:5432/go-roborrego"
2929
NEXT_PUBLIC_SUPABASE_URL="https://<project-ref>.supabase.co"
3030
NEXT_PUBLIC_SUPABASE_ANON_KEY=""
3131
SUPABASE_SERVICE_ROLE_KEY=""
32+
33+
# GitHub App — for exporting member data to roborregos-web as a PR
34+
# Create a GitHub App at https://github.com/settings/apps with:
35+
# - Repository permissions: Contents (read/write), Pull Requests (read/write)
36+
# - Install the app on RoBorregos/roborregos-web and note the Installation ID
37+
# App ID from the app's "General" settings page
38+
GITHUB_APP_ID=""
39+
# Private key — generate a .pem from the app settings, then inline with \n as newlines
40+
GITHUB_APP_PRIVATE_KEY=""
41+
# Installation ID — found in the URL when viewing the app installation:
42+
# https://github.com/organizations/<org>/settings/installations/<id>
43+
GITHUB_APP_INSTALLATION_ID=""
44+
# Override if forking to a different repo (default: RoBorregos/roborregos-web)
45+
# GITHUB_WEB_REPO="RoBorregos/roborregos-web"

package-lock.json

Lines changed: 191 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
"@dnd-kit/sortable": "^10.0.0",
2828
"@dnd-kit/utilities": "^3.2.2",
2929
"@modelcontextprotocol/sdk": "^1.27.1",
30+
"@octokit/auth-app": "^8.2.0",
3031
"@prisma/client": "^6.6.0",
3132
"@supabase/supabase-js": "^2.99.1",
3233
"@t3-oss/env-nextjs": "^0.12.0",

prisma/schema.prisma

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,13 @@ model User {
119119
role Role @default(VIEWER)
120120
status MemberStatus @default(ACTIVE)
121121
lastLoginAt DateTime?
122+
// Web export
123+
webId Int? @unique
124+
lastname String?
125+
subtitle String?
126+
semesters Int?
127+
tags String?
128+
excludeFromExport Boolean @default(false)
122129
123130
// NextAuth relations
124131
accounts Account[]

src/app/dashboard/admin/_components/AdminDashboardClient.tsx

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"use client";
22

3+
import { useState } from "react";
34
import Link from "next/link";
45
import { api, type RouterOutputs } from "~/trpc/react";
56

@@ -175,6 +176,89 @@ export function AdminDashboardClient() {
175176
))}
176177
</div>
177178
</Section>
179+
180+
{/* Web export */}
181+
<WebExportPanel />
182+
</div>
183+
</div>
184+
);
185+
}
186+
187+
function WebExportPanel() {
188+
const [result, setResult] = useState<{ prUrl?: string; prNumber?: number; memberCount?: number; imagesUploaded?: number; newIdAssignments?: number } | null>(null);
189+
const [error, setError] = useState<string | null>(null);
190+
191+
const exportMutation = api.admin.exportToWebRepo.useMutation({
192+
onSuccess: (data) => {
193+
if (!data.dryRun) setResult(data);
194+
},
195+
onError: (e) => setError(e.message),
196+
});
197+
198+
const dryRunMutation = api.admin.exportToWebRepo.useMutation({
199+
onSuccess: (data) => {
200+
if (data.dryRun) {
201+
const preview = data as { dryRun: true; memberCount: number; newIdAssignments: number };
202+
setResult({ memberCount: preview.memberCount, newIdAssignments: preview.newIdAssignments });
203+
}
204+
},
205+
onError: (e) => setError(e.message),
206+
});
207+
208+
const isBusy = exportMutation.isPending || dryRunMutation.isPending;
209+
210+
return (
211+
<div className="bg-white rounded-xl border border-gray-200 shadow-sm overflow-hidden lg:col-span-2">
212+
<div className="flex items-center justify-between px-4 py-3 border-b border-gray-100">
213+
<h2 className="text-sm font-semibold text-gray-900">Export Members to Website</h2>
214+
<span className="text-xs text-gray-400">roborregos-web</span>
215+
</div>
216+
<div className="px-4 py-4 space-y-3">
217+
<p className="text-xs text-gray-500">
218+
Creates a pull request on <span className="font-mono">RoBorregos/roborregos-web</span> with
219+
updated <span className="font-mono">members.json</span> and member images.
220+
Members marked &ldquo;Exclude from export&rdquo; are skipped. Web IDs are auto-assigned if missing.
221+
</p>
222+
223+
{error && (
224+
<p className="text-xs text-red-600 bg-red-50 rounded-lg px-3 py-2">{error}</p>
225+
)}
226+
227+
{result?.prUrl && (
228+
<div className="text-xs bg-green-50 border border-green-200 rounded-lg px-3 py-2 space-y-1">
229+
<p className="font-medium text-green-800">PR created successfully</p>
230+
<p className="text-green-700">
231+
{result.memberCount} members · {result.imagesUploaded} images · {result.newIdAssignments} new IDs assigned
232+
</p>
233+
<a href={result.prUrl} target="_blank" rel="noreferrer" className="text-blue-600 hover:underline font-mono">
234+
PR #{result.prNumber}
235+
</a>
236+
</div>
237+
)}
238+
239+
{result && !result.prUrl && (
240+
<div className="text-xs bg-blue-50 border border-blue-200 rounded-lg px-3 py-2">
241+
<p className="font-medium text-blue-800">Dry run complete</p>
242+
<p className="text-blue-700">{result.memberCount} members to export · {result.newIdAssignments} would get new IDs</p>
243+
</div>
244+
)}
245+
246+
<div className="flex gap-2">
247+
<button
248+
onClick={() => { setError(null); setResult(null); dryRunMutation.mutate({ dryRun: true }); }}
249+
disabled={isBusy}
250+
className="px-3 py-1.5 text-xs border border-gray-300 text-gray-700 rounded-lg hover:bg-gray-50 disabled:opacity-50 transition-colors"
251+
>
252+
{dryRunMutation.isPending ? "Running…" : "Dry Run"}
253+
</button>
254+
<button
255+
onClick={() => { setError(null); setResult(null); exportMutation.mutate({ dryRun: false }); }}
256+
disabled={isBusy}
257+
className="px-3 py-1.5 text-xs bg-[#1a2744] text-white rounded-lg hover:bg-[#243660] disabled:opacity-50 transition-colors"
258+
>
259+
{exportMutation.isPending ? "Creating PR…" : "Create PR"}
260+
</button>
261+
</div>
178262
</div>
179263
</div>
180264
);

0 commit comments

Comments
 (0)