Session 13: The Slate, Africa geo-restriction, OAuth providers, PropRow + GameCard (1311 tests)
This commit is contained in:
@@ -3,8 +3,7 @@
|
||||
import { useState } from 'react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useAuth } from '@/contexts/AuthContext';
|
||||
import { useT, useLocale } from '@/contexts/LocaleContext';
|
||||
import { AFRICA_LOCALES } from '@/lib/locales';
|
||||
import { useT, useRegion } from '@/contexts/LocaleContext';
|
||||
|
||||
type TierId = 'free' | 'africa' | 'analyst' | 'desk';
|
||||
|
||||
@@ -113,21 +112,28 @@ const TIERS: TierConfig[] = [
|
||||
export default function Pricing() {
|
||||
const router = useRouter();
|
||||
const { session, loading: authLoading } = useAuth();
|
||||
const { locale } = useLocale();
|
||||
const { inAfrica } = useRegion();
|
||||
const t = useT();
|
||||
const [pending, setPending] = useState<TierId | null>(null);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
// Session 12 — Africa-language users see VYNDR Africa first. The
|
||||
// tier order is stable per locale (no flicker between renders).
|
||||
// Browser region (NG / KE / ZA / GH) isn't available server-side
|
||||
// without IP geolocation, so we use the locale as a proxy. Users
|
||||
// outside the locale set can still pick the Africa tier; it just
|
||||
// doesn't lead the card grid for them.
|
||||
const orderedTiers = AFRICA_LOCALES.has(locale)
|
||||
// Session 13 — Africa tier visibility + order is now driven by
|
||||
// REAL IP geolocation via Cloudflare's CF-IPCountry header (stamped
|
||||
// onto x-vyndr-country by the middleware). The previous locale-
|
||||
// based proxy (Swahili speakers everywhere) was both too narrow
|
||||
// (most African users browse in English/French) and too broad
|
||||
// (Swahili speakers outside Africa got the discount).
|
||||
//
|
||||
// Inside Africa: VYNDR Africa renders first, then Free, then Analyst, Desk.
|
||||
// Outside Africa: the Africa tier card is filtered out of the render
|
||||
// entirely — no path for non-African users to even
|
||||
// see the $4.99 option.
|
||||
// Unknown country (local dev, non-Cloudflare): degrades closed →
|
||||
// Africa tier hidden (same as outside Africa).
|
||||
const orderedTiers = inAfrica
|
||||
? [TIERS.find((x) => x.id === 'africa')!, TIERS.find((x) => x.id === 'free')!,
|
||||
TIERS.find((x) => x.id === 'analyst')!, TIERS.find((x) => x.id === 'desk')!]
|
||||
: TIERS;
|
||||
: TIERS.filter((x) => x.id !== 'africa');
|
||||
|
||||
async function startCheckout(tier: TierId) {
|
||||
setError(null);
|
||||
@@ -218,7 +224,18 @@ export default function Pricing() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="pricing-grid" style={{ display: 'grid', gap: 24 }}>
|
||||
<div
|
||||
className="pricing-grid"
|
||||
style={{
|
||||
display: 'grid',
|
||||
gap: 24,
|
||||
// The desktop column count tracks the visible tier count
|
||||
// (3 outside Africa, 4 inside). styled-jsx's `:global()`
|
||||
// doesn't handle attribute selectors cleanly, so we pin
|
||||
// the value via a CSS custom property on the grid root.
|
||||
['--pricing-cols' as keyof React.CSSProperties]: String(orderedTiers.length),
|
||||
} as React.CSSProperties}
|
||||
>
|
||||
{orderedTiers.map((tier, i) => {
|
||||
const isPending = pending === tier.id;
|
||||
const isDisabled = authLoading || (pending !== null && !isPending);
|
||||
@@ -318,15 +335,15 @@ export default function Pricing() {
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
:global(.pricing-grid) {
|
||||
/* Session 12 — Africa tier brings the count to 4. On
|
||||
tablet we stay 2-up so cards don't squeeze; desktop
|
||||
unfolds to 4-up at >=1100px. */
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
}
|
||||
@media (min-width: 1100px) {
|
||||
:global(.pricing-grid) {
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
/* --pricing-cols is set by the React render (3 outside
|
||||
Africa, 4 inside) so the desktop layout tracks the
|
||||
visible tier count without an attribute selector. */
|
||||
grid-template-columns: repeat(var(--pricing-cols, 3), 1fr);
|
||||
}
|
||||
}
|
||||
`}</style>
|
||||
|
||||
Reference in New Issue
Block a user