feat(homepage): update profile preview and payment methods display
This commit is contained in:
130
app/page.tsx
130
app/page.tsx
@@ -1,32 +1,52 @@
|
||||
import Link from "next/link";
|
||||
import QRCode from "qrcode";
|
||||
import { PaymentMethodType } from "@prisma/client";
|
||||
import { auth } from "@/lib/auth";
|
||||
import { buildPaymentUri } from "@/lib/payment-uri";
|
||||
import { ProfileHeader } from "@/components/public/profile-header";
|
||||
import { PaymentMethodCard } from "@/components/public/payment-method-card";
|
||||
import { buttonStyles } from "@/components/ui/button";
|
||||
|
||||
const previewRows = [
|
||||
const previewProfile = {
|
||||
displayName: "Your Name",
|
||||
username: "yourname",
|
||||
bio: "A public page where people can copy your payment details in one place.",
|
||||
avatarUrl: null,
|
||||
};
|
||||
|
||||
const previewMethodsBase: Array<{
|
||||
id: string;
|
||||
type: PaymentMethodType;
|
||||
label: string;
|
||||
value: string;
|
||||
network: string | null;
|
||||
description: string | null;
|
||||
isVisible: boolean;
|
||||
}> = [
|
||||
{
|
||||
title: "PayPal",
|
||||
meta: "paypal.me/alexdev",
|
||||
value: "alexdev",
|
||||
actions: "copy · open"
|
||||
id: "preview-monero",
|
||||
type: "BITCOIN",
|
||||
label: "Bitcoin",
|
||||
value: "bc1qrp8uudvq5rr5l0nuepkxvxayyny2ws2w0m8jz3",
|
||||
network: null,
|
||||
description: null,
|
||||
isVisible: true,
|
||||
},
|
||||
{
|
||||
title: "USDT",
|
||||
meta: "TRC20",
|
||||
value: "TQ6...k2S",
|
||||
actions: "copy · qr"
|
||||
id: "preview-paypal",
|
||||
type: "PAYPAL",
|
||||
label: "PayPal",
|
||||
value: "https://paypal.me/yourname",
|
||||
network: null,
|
||||
description: null,
|
||||
isVisible: true,
|
||||
},
|
||||
{
|
||||
title: "Bank Transfer",
|
||||
meta: "IBAN",
|
||||
value: "DE89 3704 0044 0532 0130 00",
|
||||
actions: "copy"
|
||||
}
|
||||
];
|
||||
|
||||
const howItWorks = [
|
||||
"Create an account and choose your public username.",
|
||||
"Add payment methods and optional social/contact links.",
|
||||
"Share your profile URL so people can copy details or scan QR."
|
||||
"Share your profile URL so people can copy details or scan QR.",
|
||||
];
|
||||
|
||||
const whyPayMe = [
|
||||
@@ -34,11 +54,36 @@ const whyPayMe = [
|
||||
"No processing: PayMe does not execute transactions.",
|
||||
"Self-hosted: run it on your own infrastructure.",
|
||||
"Open source: inspect and modify everything.",
|
||||
"Privacy-friendly: minimal profile data, no tracking layer built in."
|
||||
"Privacy-friendly: minimal profile data, no tracking layer built in.",
|
||||
];
|
||||
|
||||
export default async function HomePage() {
|
||||
const session = await auth();
|
||||
const previewMethods = await Promise.all(
|
||||
previewMethodsBase.map(async (method) => {
|
||||
const qrPayload = buildPaymentUri(
|
||||
method.type,
|
||||
method.value,
|
||||
method.network,
|
||||
);
|
||||
let qrDataUrl: string | null = null;
|
||||
|
||||
try {
|
||||
qrDataUrl = await QRCode.toDataURL(qrPayload, {
|
||||
margin: 1,
|
||||
width: 220,
|
||||
});
|
||||
} catch {
|
||||
qrDataUrl = null;
|
||||
}
|
||||
|
||||
return {
|
||||
...method,
|
||||
qrPayload,
|
||||
qrDataUrl,
|
||||
};
|
||||
}),
|
||||
);
|
||||
|
||||
const primaryHref = session?.user ? "/dashboard" : "/register";
|
||||
const primaryLabel = session?.user ? "Open dashboard" : "Create your profile";
|
||||
@@ -52,8 +97,9 @@ export default async function HomePage() {
|
||||
One public page for every way people can pay you.
|
||||
</h1>
|
||||
<p className="max-w-3xl text-lg leading-relaxed text-muted">
|
||||
Self-hosted payment profile pages for creators, freelancers, and OSS maintainers. No custody. No
|
||||
transaction processing. Just clear payment details and links.
|
||||
Self-hosted payment profile pages for creators, freelancers, and OSS
|
||||
maintainers. No custody. No transaction processing. Just clear
|
||||
payment details and links.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -62,7 +108,8 @@ export default async function HomePage() {
|
||||
href={primaryHref}
|
||||
className={buttonStyles({
|
||||
variant: "primary",
|
||||
className: "relative -top-1 min-h-[3.1rem] min-w-[13rem] justify-center px-6"
|
||||
className:
|
||||
"relative -top-1 min-h-[3.1rem] min-w-[13rem] justify-center px-6",
|
||||
})}
|
||||
>
|
||||
{primaryLabel}
|
||||
@@ -71,7 +118,8 @@ export default async function HomePage() {
|
||||
href="#example"
|
||||
className={buttonStyles({
|
||||
variant: "secondary",
|
||||
className: "relative -top-1 min-h-[3.1rem] min-w-[10.75rem] justify-center px-5"
|
||||
className:
|
||||
"relative -top-1 min-h-[3.1rem] min-w-[10.75rem] justify-center px-5",
|
||||
})}
|
||||
style={{ marginLeft: "14px" }}
|
||||
>
|
||||
@@ -83,31 +131,27 @@ export default async function HomePage() {
|
||||
<section id="example" className="mt-8 space-y-5 md:mt-10">
|
||||
<div className="space-y-2">
|
||||
<h2 className="text-2xl font-bold">Profile preview</h2>
|
||||
<p className="text-sm text-muted">How a public PayMe profile is presented.</p>
|
||||
<p className="text-sm text-muted">
|
||||
How a public PayMe profile is presented.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="terminal-card space-y-5 md:p-6">
|
||||
<div className="space-y-1 border-b border-border/70 pb-4">
|
||||
<p className="terminal-heading">Public profile</p>
|
||||
<p className="pt-1 text-2xl font-bold">Alex Rivera</p>
|
||||
<p className="text-sm text-muted">@alexdev</p>
|
||||
<p className="pt-2 text-base text-muted">Open source maintainer. Donations keep maintenance sustainable.</p>
|
||||
</div>
|
||||
<div className="space-y-8">
|
||||
<ProfileHeader
|
||||
displayName={previewProfile.displayName}
|
||||
username={previewProfile.username}
|
||||
bio={previewProfile.bio}
|
||||
avatarUrl={previewProfile.avatarUrl}
|
||||
/>
|
||||
|
||||
<ul className="list-none space-y-3 p-0">
|
||||
{previewRows.map((row) => (
|
||||
<li key={row.title} className="method-card">
|
||||
<div className="method-card-grid">
|
||||
<div className="method-card-content">
|
||||
<p className="method-card-title">{row.title}</p>
|
||||
<p className="method-card-meta">{row.meta}</p>
|
||||
<p className="method-card-value">{row.value}</p>
|
||||
</div>
|
||||
<p className="text-xs uppercase tracking-[0.12em] text-muted">{row.actions}</p>
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<section className="terminal-section space-y-4">
|
||||
<h3 className="terminal-heading">Payment Methods</h3>
|
||||
<div className="space-y-3">
|
||||
{previewMethods.map((method) => (
|
||||
<PaymentMethodCard key={method.id} method={method} />
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
2
next-env.d.ts
vendored
2
next-env.d.ts
vendored
@@ -1,6 +1,6 @@
|
||||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
import "./.next/dev/types/routes.d.ts";
|
||||
import "./.next/types/routes.d.ts";
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
||||
|
||||
Reference in New Issue
Block a user