Skip to content

Commit 747280e

Browse files
Copilot0xrinegade
andcommitted
Fix theme/language selectors and add top 100 currencies with local banking options
Co-authored-by: 0xrinegade <[email protected]>
1 parent e8f39ae commit 747280e

File tree

6 files changed

+289
-69
lines changed

6 files changed

+289
-69
lines changed

src/components/Layout.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { AppContext } from '@/contexts/AppContext';
1212

1313
// Import components
1414
import { NetworkSelector } from '@/components/NetworkSelector';
15+
import LanguageSelector from '@/components/LanguageSelector';
1516
import OnboardingModal from '@/components/OnboardingModal';
1617
import PWAInstallButton from '@/components/PWAInstallButton';
1718
import OfflineIndicator from '@/components/OfflineIndicator';
@@ -30,6 +31,32 @@ export default function Layout({ children, title = 'OpenSVM P2P Exchange' }) {
3031
const router = useRouter();
3132
const { connected, publicKey } = usePhantomWallet();
3233
const [showOnboarding, setShowOnboarding] = useState(false);
34+
35+
// Language selector state
36+
const [currentLanguage, setCurrentLanguage] = useState('en');
37+
38+
// Available languages
39+
const languages = [
40+
{ code: 'en', name: 'English', country: '🇺🇸' },
41+
{ code: 'es', name: 'Español', country: '🇪🇸' },
42+
{ code: 'fr', name: 'Français', country: '🇫🇷' },
43+
{ code: 'de', name: 'Deutsch', country: '🇩🇪' },
44+
{ code: 'ja', name: '日本語', country: '🇯🇵' },
45+
{ code: 'ko', name: '한국어', country: '🇰🇷' },
46+
{ code: 'zh', name: '中文', country: '🇨🇳' }
47+
];
48+
49+
// Load language from localStorage on component mount
50+
useEffect(() => {
51+
const savedLanguage = localStorage.getItem('preferred-language') || 'en';
52+
setCurrentLanguage(savedLanguage);
53+
}, []);
54+
55+
// Handle language change
56+
const handleLanguageChange = (languageCode) => {
57+
setCurrentLanguage(languageCode);
58+
localStorage.setItem('preferred-language', languageCode);
59+
};
3360

3461
// Check if user needs onboarding
3562
useEffect(() => {
@@ -138,6 +165,13 @@ export default function Layout({ children, title = 'OpenSVM P2P Exchange' }) {
138165

139166
{/* Header Controls - Simplified */}
140167
<div className="ascii-header-controls">
168+
{/* Language selector */}
169+
<LanguageSelector
170+
languages={languages}
171+
currentLocale={currentLanguage}
172+
onLanguageChange={handleLanguageChange}
173+
/>
174+
141175
{/* Network selector */}
142176
<NetworkSelector
143177
networks={networks}

src/components/OfferCreation.js

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import { validateSolAmount, validateFiatAmount, validateMarketRate } from '../ut
1717
import { createLogger } from '../utils/logger';
1818
import {
1919
SUPPORTED_CURRENCIES,
20-
SUPPORTED_PAYMENT_METHODS,
20+
getPaymentMethodsForCurrency,
2121
VALIDATION_CONSTRAINTS
2222
} from '../constants/tradingConstants';
2323
import { useRealPriceData, useCalculateFiatAmount } from '../hooks/usePriceData';
@@ -55,6 +55,16 @@ const OfferCreation = ({ onStartGuidedWorkflow }) => {
5555
const isConnectionRetrying = connectionStatus === CONNECTION_STATUS.RETRYING;
5656
const isConnectionConnecting = connectionStatus === CONNECTION_STATUS.CONNECTING;
5757

58+
// Get payment methods for selected currency
59+
const availablePaymentMethods = getPaymentMethodsForCurrency(fiatCurrency);
60+
61+
// Update payment method when currency changes to ensure it's valid
62+
useEffect(() => {
63+
if (!availablePaymentMethods.includes(paymentMethod)) {
64+
setPaymentMethod(availablePaymentMethods[0] || 'Bank Transfer');
65+
}
66+
}, [fiatCurrency, availablePaymentMethods, paymentMethod]);
67+
5868
// Debug info for development
5969
useEffect(() => {
6070
console.log('[OfferCreation] Debug info:', {
@@ -448,7 +458,7 @@ const OfferCreation = ({ onStartGuidedWorkflow }) => {
448458
onChange={(e) => setPaymentMethod(e.target.value)}
449459
required
450460
>
451-
{SUPPORTED_PAYMENT_METHODS.map(method => (
461+
{availablePaymentMethods.map(method => (
452462
<option key={method} value={method}>{method}</option>
453463
))}
454464
</select>

src/components/OfferList.js

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { LoadingSpinner, ButtonLoader, TransactionStatus, Tooltip, ConfirmationD
55
import { usePhantomWallet } from '../contexts/PhantomWalletProvider';
66
import { useDebounce, VirtualizedList } from '../utils/performance';
77
import { useActionDebounce } from '../hooks/useActionDebounce';
8-
import { SUPPORTED_CURRENCIES, SUPPORTED_PAYMENT_METHODS } from '../constants/tradingConstants';
8+
import { SUPPORTED_CURRENCIES, getPaymentMethodsForCurrency } from '../constants/tradingConstants';
99
import { useOffers } from '../hooks/useOnChainData';
1010
import { useRealPriceData } from '../hooks/usePriceData';
1111
import ConnectWalletPrompt from './ConnectWalletPrompt';
@@ -295,7 +295,17 @@ const OfferList = ({ type = 'buy', onStartGuidedWorkflow}) => {
295295

296296
// Memoize static data
297297
const currencies = useMemo(() => ['', ...SUPPORTED_CURRENCIES], []);
298-
const paymentMethods = useMemo(() => ['', ...SUPPORTED_PAYMENT_METHODS], []);
298+
299+
// Get all unique payment methods from all currencies for filtering
300+
const allPaymentMethods = useMemo(() => {
301+
const methodSet = new Set();
302+
SUPPORTED_CURRENCIES.forEach(currency => {
303+
const methods = getPaymentMethodsForCurrency(currency);
304+
methods.forEach(method => methodSet.add(method));
305+
});
306+
return ['', ...Array.from(methodSet).sort()];
307+
}, []);
308+
299309
const sortOptions = useMemo(() => [
300310
{ value: 'createdAt', label: 'Date Posted' },
301311
{ value: 'solAmount', label: 'SOL Amount' },
@@ -838,7 +848,7 @@ const OfferList = ({ type = 'buy', onStartGuidedWorkflow}) => {
838848
onChange={(e) => setSelectedPaymentMethod(e.target.value)}
839849
aria-label="Select payment method"
840850
>
841-
{paymentMethods.map(method => (
851+
{allPaymentMethods.map(method => (
842852
<option key={method} value={method}>{method || 'All Payment Methods'}</option>
843853
))}
844854
</select>

src/components/ThemeSelector.js

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
import React, { useState, useRef, useEffect, useCallback, useMemo } from 'react';
22

3-
const ThemeSelector = () => {
4-
const [selectedTheme, setSelectedTheme] = useState('blueprint');
3+
const ThemeSelector = ({
4+
value,
5+
onChange,
6+
className = "app-dropdown-container"
7+
}) => {
8+
const [selectedTheme, setSelectedTheme] = useState(value || 'blueprint');
59
const [isOpen, setIsOpen] = useState(false);
610
const dropdownRef = useRef(null);
711

@@ -40,11 +44,19 @@ const ThemeSelector = () => {
4044
}, [themes]);
4145

4246
useEffect(() => {
43-
// Load saved theme from localStorage
44-
const savedTheme = localStorage.getItem('theme') || 'blueprint';
47+
// Load saved theme from localStorage or use passed value
48+
const savedTheme = value || localStorage.getItem('theme') || 'blueprint';
4549
setSelectedTheme(savedTheme);
4650
applyTheme(savedTheme);
47-
}, [applyTheme]);
51+
}, [applyTheme, value]);
52+
53+
// Update when external value changes
54+
useEffect(() => {
55+
if (value && value !== selectedTheme) {
56+
setSelectedTheme(value);
57+
applyTheme(value);
58+
}
59+
}, [value, selectedTheme, applyTheme]);
4860

4961
// Close dropdown when clicking outside
5062
useEffect(() => {
@@ -65,12 +77,17 @@ const ThemeSelector = () => {
6577
localStorage.setItem('theme', themeKey);
6678
applyTheme(themeKey);
6779
setIsOpen(false);
80+
81+
// Call external onChange if provided
82+
if (onChange) {
83+
onChange(themeKey);
84+
}
6885
};
6986

7087
const currentTheme = themes.find(theme => theme.key === selectedTheme);
7188

7289
return (
73-
<div className="app-dropdown-container" ref={dropdownRef}>
90+
<div className={className} ref={dropdownRef}>
7491
<button
7592
className="app-header-control app-dropdown-trigger"
7693
onClick={() => setIsOpen(!isOpen)}

src/components/profile/ProfileSettings.js

Lines changed: 72 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import React, { useState, useEffect } from 'react';
22
import PropTypes from 'prop-types';
33
import PropertyValueTable from '../common/PropertyValueTable';
44
import ThemeSelector from '../ThemeSelector';
5-
import LanguageSelector from '../LanguageSelector';
65

76
/**
87
* ProfileSettings component allows users to customize their profile settings,
@@ -26,14 +25,11 @@ const ProfileSettings = ({ settings, onSaveSettings }) => {
2625
const [profileSettings, setProfileSettings] = useState(safeSettings);
2726
const [isEditing, setIsEditing] = useState(false);
2827
const [currentTheme, setCurrentTheme] = useState('blueprint');
29-
const [currentLanguage, setCurrentLanguage] = useState('en');
3028

31-
// Load theme and language from localStorage on component mount
29+
// Load theme from localStorage on component mount
3230
useEffect(() => {
3331
const savedTheme = localStorage.getItem('theme') || 'blueprint';
34-
const savedLanguage = localStorage.getItem('preferred-language') || 'en';
3532
setCurrentTheme(savedTheme);
36-
setCurrentLanguage(savedLanguage);
3733
}, []);
3834

3935
// Update profileSettings when settings prop changes
@@ -65,26 +61,19 @@ const ProfileSettings = ({ settings, onSaveSettings }) => {
6561
// Theme will be applied by the ThemeSelector component
6662
};
6763

68-
// Handle language change
69-
const handleLanguageChange = (languageCode) => {
70-
setCurrentLanguage(languageCode);
71-
localStorage.setItem('preferred-language', languageCode);
72-
};
73-
74-
// Available languages
75-
const languages = [
76-
{ code: 'en', name: 'English', country: '🇺🇸' },
77-
{ code: 'es', name: 'Español', country: '🇪🇸' },
78-
{ code: 'fr', name: 'Français', country: '🇫🇷' },
79-
{ code: 'de', name: 'Deutsch', country: '🇩🇪' },
80-
{ code: 'ja', name: '日本語', country: '🇯🇵' },
81-
{ code: 'ko', name: '한국어', country: '🇰🇷' },
82-
{ code: 'zh', name: '中文', country: '🇨🇳' }
83-
];
84-
85-
// Get current language name
64+
// Get current language name from localStorage
8665
const getCurrentLanguageName = () => {
87-
const lang = languages.find(l => l.code === currentLanguage);
66+
const savedLanguage = localStorage.getItem('preferred-language') || 'en';
67+
const languages = [
68+
{ code: 'en', name: 'English', country: '🇺🇸' },
69+
{ code: 'es', name: 'Español', country: '🇪🇸' },
70+
{ code: 'fr', name: 'Français', country: '🇫🇷' },
71+
{ code: 'de', name: 'Deutsch', country: '🇩🇪' },
72+
{ code: 'ja', name: '日本語', country: '🇯🇵' },
73+
{ code: 'ko', name: '한국어', country: '🇰🇷' },
74+
{ code: 'zh', name: '中文', country: '🇨🇳' }
75+
];
76+
const lang = languages.find(l => l.code === savedLanguage);
8877
return lang ? `${lang.country} ${lang.name}` : '🇺🇸 English';
8978
};
9079

@@ -110,12 +99,12 @@ const ProfileSettings = ({ settings, onSaveSettings }) => {
11099
{
111100
property: 'THEME',
112101
value: getCurrentThemeName(),
113-
description: 'Visual appearance and styling'
102+
description: 'Current visual theme - use theme selector to change'
114103
},
115104
{
116105
property: 'LANGUAGE',
117106
value: getCurrentLanguageName(),
118-
description: 'Interface language and locale'
107+
description: 'Interface language - change in header navigation'
119108
},
120109
];
121110
// Prepare display preferences data
@@ -226,29 +215,72 @@ const ProfileSettings = ({ settings, onSaveSettings }) => {
226215
<div className="ascii-form-section">
227216
<div className="ascii-form-section-title">INTERFACE PREFERENCES</div>
228217

229-
<div className="ascii-form-row-2">
218+
<div className="ascii-form-row-1">
230219
<div className="ascii-field">
231220
<label>THEME</label>
232221
<div className="theme-selector-container">
233-
<ThemeSelector />
234-
</div>
235-
<div className="ascii-field-help">Choose your preferred visual theme</div>
236-
</div>
237-
238-
<div className="ascii-field">
239-
<label>LANGUAGE</label>
240-
<div className="language-selector-container">
241-
<LanguageSelector
242-
languages={languages}
243-
currentLocale={currentLanguage}
244-
onLanguageChange={handleLanguageChange}
222+
<ThemeSelector
223+
value={currentTheme}
224+
onChange={handleThemeChange}
225+
className="profile-theme-selector"
245226
/>
246227
</div>
247-
<div className="ascii-field-help">Select your preferred language</div>
228+
<div className="ascii-field-help">Choose your preferred visual theme</div>
248229
</div>
249230
</div>
231+
232+
<div className="ascii-form-info">
233+
<p>💡 Language settings have been moved to the header navigation for easier access.</p>
234+
<p>You can change your language preference using the language selector in the top navigation bar.</p>
235+
</div>
250236
</div>
251237

238+
<style jsx>{`
239+
.profile-theme-selector {
240+
width: 100%;
241+
background: var(--color-background);
242+
border: 1px solid var(--color-border);
243+
border-radius: 0;
244+
}
245+
246+
.profile-theme-selector .app-header-control {
247+
width: 100%;
248+
background: var(--color-background);
249+
border: 1px solid var(--color-border);
250+
color: var(--color-foreground);
251+
padding: 8px 12px;
252+
border-radius: 0;
253+
font-family: inherit;
254+
font-size: 14px;
255+
text-align: left;
256+
}
257+
258+
.profile-theme-selector .app-dropdown-menu {
259+
width: 100%;
260+
max-width: none;
261+
left: 0 !important;
262+
right: 0;
263+
}
264+
265+
.ascii-form-info {
266+
background: var(--color-background-alt);
267+
border: 1px solid var(--color-border);
268+
border-radius: 4px;
269+
padding: 12px;
270+
margin-top: 16px;
271+
font-size: 0.9rem;
272+
color: var(--color-foreground-muted);
273+
}
274+
275+
.ascii-form-info p {
276+
margin: 0 0 8px 0;
277+
}
278+
279+
.ascii-form-info p:last-child {
280+
margin-bottom: 0;
281+
}
282+
`}</style>
283+
252284
<div className="ascii-form-section">
253285
<div className="ascii-form-section-title">DISPLAY PREFERENCES</div>
254286

0 commit comments

Comments
 (0)