From dadaaeea7781cb62811256b512003e5c4d6ad95e Mon Sep 17 00:00:00 2001
From: Paul Nothaft
Date: Fri, 3 Jul 2026 12:29:58 +0200
Subject: [PATCH] feat(setup): final community/thank-you step (#732); fix
create-admin button overflow (#730)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
#730 — the account step's Create-admin button shared a flex row with Back;
its label + loading spinner exceeded the card width, so the button
overflowed the card outline while submitting (and was fragile for longer
i18n labels). Stack both buttons full-width — the primary always has room
for the spinner now, matching every other wizard step.
#732 — add a final 'community' step, shown once on first-run after
config / no-config, before entering the app. Mission line + four link
cards (report a bug, request a feature, star/share, Buy Me a Coffee),
all target=_blank rel=noopener, and a Finish → Dashboard button. Fully
i18n (en + de). Restore keeps its reload flow (a restored instance is no
longer first-run, so it never reaches this step). Adds .github/FUNDING.yml
so GitHub renders a Sponsor button too.
Verified live: drove the real first-run wizard end to end — stacked
account buttons render inside the card, community step shows the mission
+ all four links, Finish lands on the dashboard.
---
.github/FUNDING.yml | 4 ++
frontend/src/i18n/locales/de.json | 13 +++++
frontend/src/i18n/locales/en.json | 13 +++++
frontend/src/pages/SetupPage.tsx | 83 +++++++++++++++++++++++++++----
4 files changed, 102 insertions(+), 11 deletions(-)
create mode 100644 .github/FUNDING.yml
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 00000000..80eedc0b
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1,4 @@
+# These are supported funding model platforms
+# https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/displaying-a-sponsor-button-in-your-repository
+
+buy_me_a_coffee: theluap
diff --git a/frontend/src/i18n/locales/de.json b/frontend/src/i18n/locales/de.json
index 633af451..871607d3 100644
--- a/frontend/src/i18n/locales/de.json
+++ b/frontend/src/i18n/locales/de.json
@@ -3572,6 +3572,19 @@
"finish": "Einrichtung abschließen",
"saveFailed": "Einige Einstellungen konnten nicht gespeichert werden — Sie können sie in den Einstellungen abschließen."
},
+ "community": {
+ "subtitle": "Alles bereit",
+ "mission": "PicPeak gibt es, damit Fotografinnen und Fotografen ihre Galerien und Kundendaten selbst besitzen — auf dem eigenen Server, ohne monatliche SaaS-Gebühren. Danke, dass du es ausprobierst.",
+ "bugTitle": "Fehler gefunden?",
+ "bugDesc": "Melde ihn und wir sehen ihn uns an.",
+ "featureTitle": "Wunschfunktion?",
+ "featureDesc": "Sag uns, was PicPeak für dich besser machen würde.",
+ "starTitle": "Gefällt es dir? Erzähl davon",
+ "starDesc": "Empfiehl es anderen Fotografen, teile es und gib dem Projekt einen Stern auf GitHub.",
+ "supportTitle": "Entwicklung unterstützen",
+ "supportDesc": "Optional — spendier mir einen Kaffee, um PicPeak am Leben zu halten.",
+ "finish": "Fertig → Dashboard"
+ },
"stepOf": "Schritt {{current}} von {{total}}",
"continue": "Weiter",
"back": "Zurück",
diff --git a/frontend/src/i18n/locales/en.json b/frontend/src/i18n/locales/en.json
index fbb30759..f6f85e94 100644
--- a/frontend/src/i18n/locales/en.json
+++ b/frontend/src/i18n/locales/en.json
@@ -3468,6 +3468,19 @@
"finish": "Finish setup",
"saveFailed": "Some settings could not be saved — you can finish them in Settings."
},
+ "community": {
+ "subtitle": "You're all set",
+ "mission": "PicPeak exists so photographers can own their galleries and client data — on their own server, without monthly SaaS fees. Thanks for giving it a try.",
+ "bugTitle": "Found a bug?",
+ "bugDesc": "Open an issue and we'll take a look.",
+ "featureTitle": "Want a feature?",
+ "featureDesc": "Tell us what would make PicPeak better for you.",
+ "starTitle": "Like it? Spread the word",
+ "starDesc": "Tell other photographers, share it, and star the project on GitHub.",
+ "supportTitle": "Support development",
+ "supportDesc": "Optional — buy me a coffee to help keep PicPeak going.",
+ "finish": "Finish → Dashboard"
+ },
"stepOf": "Step {{current}} of {{total}}",
"continue": "Continue",
"back": "Back",
diff --git a/frontend/src/pages/SetupPage.tsx b/frontend/src/pages/SetupPage.tsx
index 790c01e7..12af1f46 100644
--- a/frontend/src/pages/SetupPage.tsx
+++ b/frontend/src/pages/SetupPage.tsx
@@ -1,7 +1,7 @@
import React, { useState } from 'react';
import { Navigate, useNavigate } from 'react-router-dom';
import { useQuery } from '@tanstack/react-query';
-import { Key, Mail, Lock, Eye, EyeOff, AlertCircle, ArrowLeft, ArrowRight, Copy, Check, ExternalLink } from 'lucide-react';
+import { Key, Mail, Lock, Eye, EyeOff, AlertCircle, ArrowLeft, ArrowRight, Copy, Check, ExternalLink, Bug, Lightbulb, Star, Coffee } from 'lucide-react';
import { toast } from 'react-toastify';
import { useTranslation } from 'react-i18next';
@@ -19,6 +19,20 @@ import type { AdminUser } from '../types';
const SETUP_DOCS_URL =
'https://github.com/PicPeak/picpeak/blob/main/README.md#first-run--create-your-admin-account';
+// Final "own your data" thank-you step (#732). Link cards shown once, on
+// first-run only, before entering the app. Every link is optional and opens
+// in a new tab; nothing here makes an external call.
+const COMMUNITY_LINKS: {
+ key: string;
+ href: string;
+ icon: React.ComponentType<{ className?: string }>;
+}[] = [
+ { key: 'bug', href: 'https://github.com/PicPeak/picpeak/issues/new?template=bug_report.md', icon: Bug },
+ { key: 'feature', href: 'https://github.com/PicPeak/picpeak/issues/new?template=feature_request.md', icon: Lightbulb },
+ { key: 'star', href: 'https://github.com/PicPeak/picpeak', icon: Star },
+ { key: 'support', href: 'https://www.buymeacoffee.com/theluap', icon: Coffee },
+];
+
// "How will you use PicPeak?" — the opt-in feature groups shown after the admin
// account is created. galleries/analytics/userManagement are always on and not
// listed. Labels/descriptions reuse the existing Settings→Features i18n keys
@@ -53,7 +67,7 @@ export const SetupPage: React.FC = () => {
staleTime: Infinity,
});
- const [step, setStep] = useState<'token' | 'account' | 'usage' | 'restore' | 'config'>('token');
+ const [step, setStep] = useState<'token' | 'account' | 'usage' | 'restore' | 'config' | 'community'>('token');
const [form, setForm] = useState({ token: '', email: '', password: '', confirm: '' });
const [showPassword, setShowPassword] = useState(false);
const [isSubmitting, setIsSubmitting] = useState(false);
@@ -234,8 +248,9 @@ export const SetupPage: React.FC = () => {
selectedFeatures.has('reminderEmails') ||
selectedFeatures.has('incomingMail') ||
selectedFeatures.has('whatsapp');
- if (needsConfig) setStep('config');
- else navigate('/admin/dashboard', { replace: true });
+ // Both the config branch and the no-config path end on the final
+ // community/thank-you step (#732), whose Finish button enters the app.
+ setStep(needsConfig ? 'config' : 'community');
}
};
@@ -267,7 +282,9 @@ export const SetupPage: React.FC = () => {
? t('setup.restoreStepSubtitle')
: step === 'config'
? t('setup.config.subtitle')
- : t('setup.usageSubtitle')}
+ : step === 'community'
+ ? t('setup.community.subtitle')
+ : t('setup.usageSubtitle')}
+ {/* Stacked full-width (not a side-by-side flex row): the primary
+ label + loading spinner exceeded the card width when squeezed
+ next to Back, so the button overflowed the card outline while
+ submitting (#730). Full-width also keeps longer translations
+ (e.g. German) inside the button. Matches every other step. */}
+