- app/(mobile)/m/settings/page.tsx — read-only account-info, product-selector (hergebruikt ActivateProductButton + setActiveProductAction met redirectTo /m/products/[id]/solo), QR-pairing-instructie, logout - components/mobile/logout-button.tsx — AlertDialog "Uitloggen?" met bevestig + annuleer; demo-user mag uitloggen (geen demo-block) - Tests: LogoutButton render + open + bevestig (logoutAction) + annuleer Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
92 lines
3.7 KiB
TypeScript
92 lines
3.7 KiB
TypeScript
// PBI-11 / ST-1136: Mobile Settings — read-only account, product-selector,
|
|
// QR-pairing-instructie, logout. Eigenlijke productactivering loopt via de
|
|
// bestaande setActiveProductAction (ActivateProductButton).
|
|
|
|
import Link from 'next/link'
|
|
import { prisma } from '@/lib/prisma'
|
|
import { productAccessFilter } from '@/lib/product-access'
|
|
import { requireSession } from '@/lib/auth-guard'
|
|
import { ActivateProductButton } from '@/components/shared/activate-product-button'
|
|
import { LogoutButton } from '@/components/mobile/logout-button'
|
|
import { Badge } from '@/components/ui/badge'
|
|
|
|
export const metadata = {
|
|
title: 'Settings',
|
|
}
|
|
|
|
export default async function MobileSettingsPage() {
|
|
const session = await requireSession()
|
|
|
|
const [user, products] = await Promise.all([
|
|
prisma.user.findUnique({
|
|
where: { id: session.userId },
|
|
select: { username: true, is_demo: true, active_product_id: true },
|
|
}),
|
|
prisma.product.findMany({
|
|
where: { archived: false, ...productAccessFilter(session.userId) },
|
|
orderBy: { name: 'asc' },
|
|
select: { id: true, name: true },
|
|
}),
|
|
])
|
|
|
|
const isDemo = user?.is_demo ?? false
|
|
|
|
return (
|
|
<div className="px-4 py-6 space-y-6 max-w-md mx-auto w-full">
|
|
<h1 className="text-xl font-semibold">Settings</h1>
|
|
|
|
<section aria-labelledby="account-heading" className="space-y-2">
|
|
<h2 id="account-heading" className="text-sm font-medium text-muted-foreground uppercase tracking-wide">Account</h2>
|
|
<div className="flex items-center gap-2">
|
|
<span className="text-base font-medium">{user?.username ?? '—'}</span>
|
|
{isDemo && (
|
|
<Badge className="bg-status-todo/15 text-status-todo border-status-todo/30">Demo</Badge>
|
|
)}
|
|
</div>
|
|
</section>
|
|
|
|
<section aria-labelledby="product-heading" className="space-y-2">
|
|
<h2 id="product-heading" className="text-sm font-medium text-muted-foreground uppercase tracking-wide">Actief product</h2>
|
|
{products.length === 0 ? (
|
|
<p className="text-sm text-muted-foreground">Geen producten beschikbaar.</p>
|
|
) : (
|
|
<ul className="divide-y divide-border rounded border border-border">
|
|
{products.map((p) => {
|
|
const active = p.id === user?.active_product_id
|
|
return (
|
|
<li key={p.id} className="flex items-center justify-between px-3 py-3">
|
|
<div className="flex items-center gap-2 min-w-0">
|
|
<span className="text-sm truncate">{p.name}</span>
|
|
{active && (
|
|
<Badge className="bg-primary/15 text-primary border-primary/30">Actief</Badge>
|
|
)}
|
|
</div>
|
|
{!active && (
|
|
<ActivateProductButton
|
|
productId={p.id}
|
|
isDemo={isDemo}
|
|
redirectTo={`/m/products/${p.id}/solo`}
|
|
label="Activeer"
|
|
/>
|
|
)}
|
|
</li>
|
|
)
|
|
})}
|
|
</ul>
|
|
)}
|
|
</section>
|
|
|
|
<section aria-labelledby="qr-heading" className="space-y-2">
|
|
<h2 id="qr-heading" className="text-sm font-medium text-muted-foreground uppercase tracking-wide">Inloggen op desktop</h2>
|
|
<p className="text-sm text-muted-foreground">
|
|
Open <Link href="/login" className="text-primary hover:underline">scrum4me.app/login</Link> op je desktop om in te loggen via QR-code. QR-pairing start vanaf de desktop.
|
|
</p>
|
|
</section>
|
|
|
|
<section aria-labelledby="logout-heading" className="space-y-2 pt-2">
|
|
<h2 id="logout-heading" className="sr-only">Uitloggen</h2>
|
|
<LogoutButton />
|
|
</section>
|
|
</div>
|
|
)
|
|
}
|