Skip to content

Commit ad4c538

Browse files
translations
1 parent bc0a3bf commit ad4c538

File tree

12 files changed

+96
-76
lines changed

12 files changed

+96
-76
lines changed

apps/dashboard

apps/docs/components/home/agnostic-background.tsx

Lines changed: 13 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ function useIsVisible(ref: React.RefObject<HTMLDivElement | null>) {
4242
return isVisible;
4343
}
4444

45-
type BackgroundVariant = 'hero' | 'masked' | 'gradient-only' | 'dithering-only';
45+
type BackgroundVariant = 'hero' | 'masked';
4646

4747
interface AgnosticBackgroundProps {
4848
variant?: BackgroundVariant;
@@ -61,9 +61,13 @@ export function ContentAdoptionBackground(
6161
? ['#0C4A6E', '#1E5A8A', '#2B77E6', '#4A9EF8', '#7DD3FC', '#E0F2FE00']
6262
: ['#56A5F9', '#4A9EF8', '#3B82F6', '#60A5FA', '#7DD3FC', '#93C5FD', '#DBEAFE00']
6363
}
64-
speed={0}
64+
speed={1}
6565
colorBack="#00000000"
6666
shape="sphere"
67+
softness={0.8}
68+
intensity={0.9}
69+
noise={0.4}
70+
minPixelRatio={0.8}
6771
{...props}
6872
/>
6973
);
@@ -79,9 +83,9 @@ export function AgnosticBackground({ variant = 'masked', className }: AgnosticBa
7983
case 'hero':
8084
return (
8185
<>
82-
{/* Hero GrainGradient */}
86+
{/* Hero GrainGradient - fills entire container like Fumadocs */}
8387
<GrainGradient
84-
className="hidden md:block absolute max-w-[1255px] w-full translate-x-2 mx-auto top-[100px] h-[85vh] max-h-[850px] animate-fd-fade-in duration-800 border border-border/50 rounded-sm"
88+
className="absolute inset-0 animate-fd-fade-in duration-800"
8589
colors={
8690
resolvedTheme === 'dark'
8791
? ['#0C4A6E', '#1E5A8A', '#2B77E6', '#4A9EF8', '#7DD3FC', '#E0F2FE00']
@@ -91,13 +95,13 @@ export function AgnosticBackground({ variant = 'masked', className }: AgnosticBa
9195
softness={1}
9296
intensity={0.9}
9397
noise={0.5}
94-
speed={1}
98+
speed={visible ? 1 : 0}
9599
shape="corners"
96100
minPixelRatio={1}
97101
maxPixelCount={1920 * 1080}
98102
/>
99103

100-
{/* Hero Dithering */}
104+
{/* Hero Dithering - positioned like Fumadocs */}
101105
<Dithering
102106
width={720}
103107
height={720}
@@ -109,7 +113,7 @@ export function AgnosticBackground({ variant = 'masked', className }: AgnosticBa
109113
size={3}
110114
speed={0}
111115
frame={5000 * 120}
112-
className="hidden md:block absolute animate-fd-fade-in duration-400 max-lg:bottom-[-30%] max-lg:left-[-200px] lg:top-[-5%] lg:right-0"
116+
className="absolute animate-fd-fade-in duration-400 max-lg:bottom-[-50%] max-lg:left-[-200px] lg:top-[-5%] lg:right-0"
113117
minPixelRatio={1}
114118
/>
115119
</>
@@ -121,47 +125,13 @@ export function AgnosticBackground({ variant = 'masked', className }: AgnosticBa
121125
colorBack="#00000000"
122126
colorFront="#56A5F9"
123127
shape="warp"
124-
type="4x4"
128+
type="8x8"
125129
speed={visible ? 0.4 : 0}
126130
className="size-full"
127-
minPixelRatio={1}
131+
minPixelRatio={0.8}
128132
/>
129133
);
130134

131-
case 'gradient-only':
132-
return (
133-
<GrainGradient
134-
className="absolute inset-0"
135-
colors={
136-
resolvedTheme === 'dark'
137-
? ['#0C4A6E', '#1E5A8A', '#2B77E6', '#4A9EF8', '#7DD3FC', '#E0F2FE00']
138-
: ['#56A5F9', '#4A9EF8', '#3B82F6', '#60A5FA', '#7DD3FC', '#93C5FD', '#DBEAFE00']
139-
}
140-
colorBack="#00000000"
141-
softness={1}
142-
intensity={0.9}
143-
noise={0.5}
144-
speed={1}
145-
shape="corners"
146-
minPixelRatio={1}
147-
maxPixelCount={1920 * 1080}
148-
/>
149-
);
150-
151-
case 'dithering-only':
152-
return (
153-
<Dithering
154-
colorBack="#00000000"
155-
colorFront="#60A5FA"
156-
shape="sphere"
157-
type="4x4"
158-
scale={0.5}
159-
size={3}
160-
speed={0}
161-
className="size-full"
162-
minPixelRatio={1}
163-
/>
164-
);
165135

166136
default:
167137
return null;

apps/docs/components/home/early-member-pricing.tsx

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,15 @@ import { motion, AnimatePresence } from 'framer-motion';
77
import { Button } from '@/components/ui/button';
88
import Link from 'next/link';
99
import { SingleSwirl } from '@/components/preview/swirls';
10+
import { AgnosticBackground } from '@/components/home/agnostic-background';
1011
import { useTranslation } from '@/lib/utils/translation-context';
12+
import { useTheme } from '@/lib/hooks/use-theme';
1113
import { t } from '@/lib/i18n/translations';
1214
import { playClickSound } from '@/lib/utils/sound';
1315

1416
export function EarlyMemberPricing() {
1517
const { currentLanguage } = useTranslation();
18+
const { resolvedTheme } = useTheme();
1619
const [titleNumber, setTitleNumber] = useState(0);
1720
const prices = useMemo(() => ['200 F CFA', '0.30 USD', '0.30 EUR'], []);
1821

@@ -34,8 +37,14 @@ export function EarlyMemberPricing() {
3437
</div>
3538

3639
<section className="overflow-hidden relative bg-transparent dark:bg-background text-foreground dark:text-foreground rounded-sm border p-0 pb-16 pt-16 md:pb-32 md:pt-32 before:absolute before:inset-0 before:bg-linear-to-r before:from-foreground/5 before:via-transparent before:to-foreground/5 before:pointer-events-none dark:before:from-background/20 dark:before:to-background/20 select-none">
37-
<SingleSwirl className="pointer-events-none absolute top-0 bottom-0 left-0" />
38-
<SingleSwirl className="pointer-events-none rotate-180 absolute top-0 bottom-0 right-0 opacity-50" />
40+
{resolvedTheme === 'dark' ? (
41+
<>
42+
<SingleSwirl className="pointer-events-none absolute top-0 bottom-0 left-0" />
43+
<SingleSwirl className="pointer-events-none rotate-180 absolute top-0 bottom-0 right-0 opacity-50" />
44+
</>
45+
) : (
46+
<AgnosticBackground variant="masked" />
47+
)}
3948
<div className="container flex flex-col items-center justify-center">
4049
<div className="text-center mb-6 md:mb-8 px-4">
4150
<div className="mb-6 md:mb-8">

apps/docs/components/home/features-section.tsx

Lines changed: 45 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -520,6 +520,13 @@ export function FeaturesSection() {
520520
currentLanguage,
521521
),
522522
)}
523+
{name === 'Apple Pay' &&
524+
String(
525+
t(
526+
'features.card1.apple_pay_description',
527+
currentLanguage,
528+
),
529+
)}
523530
</p>
524531
</div>
525532
</div>
@@ -648,7 +655,7 @@ export function FeaturesSection() {
648655
<span className="text-xs text-muted-foreground">
649656
{String(t('features.card4.balance', currentLanguage))}
650657
</span>
651-
<div className="flex items-center gap-2 rounded-sm p-1.5 bg-green-50 dark:bg-green-900/30 border w-48 h-8 justify-center">
658+
<div className="flex items-center gap-2 rounded-sm p-1.5 bg-green-50 dark:bg-green-900/30 border-none w-48 h-8 justify-center">
652659
<span className="font-mono text-[11px] font-bold text-green-700 dark:text-green-300">
653660
{(49847392)
654661
.toLocaleString('fr-FR')
@@ -987,16 +994,17 @@ export function FeaturesSection() {
987994
}
988995
};
989996

990-
const providerNames = ['SPI', 'Visa', 'PayPal', 'Google Pay'] as const;
997+
const providerNames = ['SPI', 'Visa', 'Apple Pay', 'Google Pay', 'PayPal'] as const;
991998

992999
// Payment method toggle states
9931000
const [paymentToggles, setPaymentToggles] = React.useState<
9941001
Record<string, boolean>
9951002
>({
9961003
SPI: true,
9971004
Visa: true,
998-
PayPal: false,
999-
'Google Pay': true,
1005+
PayPal: true,
1006+
'Google Pay': false,
1007+
'Apple Pay': true,
10001008
});
10011009

10021010
// Phone numbers hover state - separate for each field
@@ -1055,22 +1063,45 @@ export function FeaturesSection() {
10551063
}));
10561064
};
10571065

1058-
// Visa image rotation effect
1066+
// Helper function to get random interval
1067+
const getRandomInterval = (min: number, max: number) => {
1068+
return Math.random() * (max - min) + min;
1069+
};
1070+
1071+
// Visa image rotation effect - randomize between 3-6 seconds
10591072
React.useEffect(() => {
1060-
const interval = setInterval(() => {
1061-
setVisaImageIndex((prev) => (prev + 1) % visaImages.length);
1062-
}, 4000); // Change every 4 seconds
1073+
let timeoutId: ReturnType<typeof setTimeout>;
1074+
1075+
const scheduleNextRotation = () => {
1076+
timeoutId = setTimeout(() => {
1077+
setVisaImageIndex((prev) => (prev + 1) % visaImages.length);
1078+
scheduleNextRotation(); // Schedule the next rotation
1079+
}, getRandomInterval(3000, 6000));
1080+
};
1081+
1082+
scheduleNextRotation(); // Start the first rotation
10631083

1064-
return () => clearInterval(interval);
1084+
return () => {
1085+
if (timeoutId) clearTimeout(timeoutId);
1086+
};
10651087
}, [visaImages.length]);
10661088

1067-
// SPI image rotation effect
1089+
// SPI image rotation effect - randomize between 6-10 seconds
10681090
React.useEffect(() => {
1069-
const interval = setInterval(() => {
1070-
setSpiImageIndex((prev) => (prev + 1) % spiImages.length);
1071-
}, 8000); // Change every 8 seconds
1091+
let timeoutId: ReturnType<typeof setTimeout>;
1092+
1093+
const scheduleNextRotation = () => {
1094+
timeoutId = setTimeout(() => {
1095+
setSpiImageIndex((prev) => (prev + 1) % spiImages.length);
1096+
scheduleNextRotation(); // Schedule the next rotation
1097+
}, getRandomInterval(6000, 10000));
1098+
};
1099+
1100+
scheduleNextRotation(); // Start the first rotation
10721101

1073-
return () => clearInterval(interval);
1102+
return () => {
1103+
if (timeoutId) clearTimeout(timeoutId);
1104+
};
10741105
}, [spiImages.length]);
10751106

10761107
const productCode = `{

apps/docs/components/home/hero.tsx

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/* @proprietary license */
22

3-
import { Suspense, useState } from 'react';
3+
import { Suspense, useState, useEffect } from 'react';
44
import SimpleImage from '@/components/home/hero-image';
55
import { AgnosticBackground } from '@/components/home/agnostic-background';
66
import { useTranslation } from '@/lib/utils/translation-context';
@@ -21,7 +21,7 @@ interface HeroProps {
2121

2222
// Function to get a random title index from session storage or generate new one
2323
const getSessionTitleIndex = (): number => {
24-
// Check if we're in a browser environment
24+
// Always return 0 on server to avoid hydration mismatch
2525
if (typeof window === 'undefined') {
2626
return 0;
2727
}
@@ -49,8 +49,16 @@ const getSessionTitleIndex = (): number => {
4949

5050
function Hero({ dashboardImage, mobileDashboardImage }: HeroProps) {
5151
const { currentLanguage } = useTranslation();
52-
// Use useState with function initializer - only runs once on mount
53-
const [titleIndex] = useState<number>(() => getSessionTitleIndex());
52+
// Start with index 0 to ensure server/client consistency, then randomize on client
53+
const [titleIndex, setTitleIndex] = useState<number>(0);
54+
55+
// Randomize title on client side after hydration to avoid hydration mismatch
56+
useEffect(() => {
57+
const randomIndex = getSessionTitleIndex();
58+
if (randomIndex !== 0) {
59+
setTitleIndex(randomIndex);
60+
}
61+
}, []);
5462

5563
// Create t function that uses currentLanguage (same pattern as header.tsx)
5664
const t = (key: string) => String(translate(key, currentLanguage));
@@ -67,8 +75,8 @@ function Hero({ dashboardImage, mobileDashboardImage }: HeroProps) {
6775
<AgnosticBackground variant="hero" />
6876

6977
{/* Content overlay */}
70-
<div className="flex flex-col z-10 px-4 size-full md:p-12 max-md:items-center max-md:text-center">
71-
<div className="mt-20 md:mt-12 lg:mt-0">
78+
<div className="flex flex-col z-10 pl-2 pr-4 size-full md:pl-5 md:pr-12 md:py-12 max-md:items-center max-md:text-center">
79+
<div className="mt-28 md:mt-20 lg:mt-8">
7280
<h1 className="text-4xl font-normal tracking-tighter text-left text-zinc-800 dark:text-white md:text-5xl max-w-7xl whitespace-pre-line">
7381
{title as string}
7482
</h1>

apps/docs/components/home/pricing-details.tsx

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -328,23 +328,20 @@ export default function PricingDetails() {
328328

329329
{/* Pricing Table */}
330330
{sub.table && (
331-
<div className="mt-6 md:mt-8 space-y-0 border border-border rounded overflow-hidden">
331+
<div className="mt-6 md:mt-8 border border-border rounded overflow-hidden">
332332
{sub.table.map(
333333
(
334334
row: { label: string; value: string },
335335
rowIdx: number,
336336
) => (
337337
<div
338338
key={rowIdx}
339-
className={`flex flex-col sm:flex-row sm:justify-between sm:items-center px-4 md:px-6 py-3 md:py-4 text-xs md:text-sm gap-1 sm:gap-0 ${rowIdx !== sub.table.length - 1
340-
? 'border-b border-border'
341-
: ''
342-
}`}
339+
className="grid grid-cols-2 border-collapse"
343340
>
344-
<span className="text-muted-foreground">
341+
<span className="text-muted-foreground px-4 py-3 md:py-4 text-xs md:text-sm border border-border">
345342
{row.label}
346343
</span>
347-
<span className="text-foreground font-medium">
344+
<span className="text-foreground font-medium text-right px-4 py-3 md:py-4 text-xs md:text-sm border border-border border-l-0">
348345
{row.value}
349346
</span>
350347
</div>

apps/docs/components/home/pricing-tool.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ export default function PricingTool() {
9696

9797
// Custom ToggleGroup component to ensure no gap
9898
const CurrencyToggle = () => (
99-
<div className="flex w-fit rounded-sm overflow-hidden border border-zinc-200 dark:border-zinc-800">
99+
<div className="flex w-full sm:w-fit rounded-sm overflow-hidden border border-zinc-200 dark:border-zinc-800">
100100
{(
101101
[
102102
{ code: 'XOF', display: 'F CFA' },
@@ -186,7 +186,7 @@ export default function PricingTool() {
186186
}
187187
}}
188188
className={
189-
`px-4 sm:px-6 py-2 transition-colors ${currency === curr
189+
`flex-1 px-4 sm:px-6 py-2 transition-colors ${currency === curr
190190
? 'bg-zinc-100 dark:bg-zinc-800 text-zinc-900 dark:text-white'
191191
: 'bg-transparent hover:bg-zinc-100 dark:hover:bg-zinc-800 text-zinc-600 dark:text-zinc-400'
192192
} ${index < arr.length - 1 ? 'border-r border-zinc-200 dark:border-zinc-800' : ''}` // Add right border except for last

apps/docs/fumadocs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Subproject commit 7a240d7b5b5627320473332b061c0e46687d9622

apps/docs/lib/i18n/locales/en.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,9 +127,10 @@
127127
"crypto_description": "Decentralized payment network",
128128
"description": "Accept local and international payment methods, seamlessly.",
129129
"google_pay_description": "Contactless payment system",
130+
"apple_pay_description": "Apple payment wallet",
130131
"paypal_description": "Digital payment platform",
131132
"spi_description": "Interoperable payment infrastructure",
132-
"title": "All your preferred payment methods",
133+
"title": "Payment methods your customers love",
133134
"visa_description": "Global electronic payment network"
134135
},
135136
"card2": {

apps/docs/lib/i18n/locales/es.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@
127127
"crypto_description": "Pagos con criptomonedas",
128128
"description": "Acepte métodos de pago locales e internacionales, sin esfuerzos.",
129129
"google_pay_description": "Sistema de pago sin contacto",
130+
"apple_pay_description": "Wallet de pago Apple",
130131
"paypal_description": "Plataforma de pago digital",
131132
"spi_description": "Sistema interoperable de pago",
132133
"title": "Todos los métodos de pago preferidos por sus clientes",

0 commit comments

Comments
 (0)