🎯 Build Comprehensive User Profile & Settings System
👤 Profile Page Features:
- Complete user profile with avatar, name, username, bio, and email
- Avatar upload with file validation (5MB limit, image types only)
- Avatar generation functionality for custom avatars
- Favorite games selection (MTG, Pokemon, Lorcana)
- Collection statistics display (total cards, collections, decks, value)
- Profile editing with real-time validation
- Member since date and role display
⚙️ Settings Page Features:
- Multi-section tabbed interface (Account, Security, Preferences, Notifications, Display)
- Account settings: email (read-only), collection visibility, preferred currency
- Security settings: password change with validation, 2FA toggle, account deletion
- Preferences: cards per page (25/50/100), default view (grid/list)
- Notifications: email notifications, marketing emails (toggle switches)
- Display settings: theme (light/dark/system), language selection
🗄️ Database Schema Updates:
- Added user profile fields: first_name, last_name, username, bio, avatar_url
- Added preference fields: favorite_games (JSONB), collection_visibility, preferred_currency, cards_per_page, default_view
- Added notification settings: notifications_email, notifications_marketing, two_factor_enabled
- Added display settings: theme, language
- Created user_settings table for complex settings
- Created user_avatars table for avatar management
- Added performance indexes and data validation constraints
📡 API Endpoints Created:
- GET/PUT /api/user/profile - Profile information management
- GET/PUT /api/user/settings - Settings and preferences management
- PUT /api/user/password - Secure password change with bcrypt validation
- GET /api/user/stats - Collection statistics and analytics
🔒 Security & Validation:
- Password change requires current password verification
- Username uniqueness validation
- Input validation for all enum fields (currency, theme, view mode, etc.)
- Proper error handling and user feedback
- Authentication required for all user endpoints
🎨 UI/UX Features:
- Beautiful fire-themed design matching app branding
- Responsive design for mobile and desktop
- Loading states and success/error messages
- Avatar placeholder with user initials
- Tabbed settings interface with icons
- Toggle switches for boolean settings
- Form validation with helpful error messages
✨ Additional Features:
- Collection stats with game/rarity breakdowns
- Recent activity tracking
- Danger zone for account deletion with double confirmation
- Member since display with formatted dates
- Currency formatting for collection values
- Game icons and themed styling throughout
The profile and settings system is now fully functional with comprehensive user management! 👨💻✨
2025-07-26 19:05:55 -04:00
|
|
|
import { useState, useEffect } from 'react';
|
|
|
|
|
import { useRouter } from 'next/router';
|
2025-07-23 22:26:54 -04:00
|
|
|
import Layout from '../components/Layout';
|
|
|
|
|
|
|
|
|
|
export default function Settings() {
|
🎯 Build Comprehensive User Profile & Settings System
👤 Profile Page Features:
- Complete user profile with avatar, name, username, bio, and email
- Avatar upload with file validation (5MB limit, image types only)
- Avatar generation functionality for custom avatars
- Favorite games selection (MTG, Pokemon, Lorcana)
- Collection statistics display (total cards, collections, decks, value)
- Profile editing with real-time validation
- Member since date and role display
⚙️ Settings Page Features:
- Multi-section tabbed interface (Account, Security, Preferences, Notifications, Display)
- Account settings: email (read-only), collection visibility, preferred currency
- Security settings: password change with validation, 2FA toggle, account deletion
- Preferences: cards per page (25/50/100), default view (grid/list)
- Notifications: email notifications, marketing emails (toggle switches)
- Display settings: theme (light/dark/system), language selection
🗄️ Database Schema Updates:
- Added user profile fields: first_name, last_name, username, bio, avatar_url
- Added preference fields: favorite_games (JSONB), collection_visibility, preferred_currency, cards_per_page, default_view
- Added notification settings: notifications_email, notifications_marketing, two_factor_enabled
- Added display settings: theme, language
- Created user_settings table for complex settings
- Created user_avatars table for avatar management
- Added performance indexes and data validation constraints
📡 API Endpoints Created:
- GET/PUT /api/user/profile - Profile information management
- GET/PUT /api/user/settings - Settings and preferences management
- PUT /api/user/password - Secure password change with bcrypt validation
- GET /api/user/stats - Collection statistics and analytics
🔒 Security & Validation:
- Password change requires current password verification
- Username uniqueness validation
- Input validation for all enum fields (currency, theme, view mode, etc.)
- Proper error handling and user feedback
- Authentication required for all user endpoints
🎨 UI/UX Features:
- Beautiful fire-themed design matching app branding
- Responsive design for mobile and desktop
- Loading states and success/error messages
- Avatar placeholder with user initials
- Tabbed settings interface with icons
- Toggle switches for boolean settings
- Form validation with helpful error messages
✨ Additional Features:
- Collection stats with game/rarity breakdowns
- Recent activity tracking
- Danger zone for account deletion with double confirmation
- Member since display with formatted dates
- Currency formatting for collection values
- Game icons and themed styling throughout
The profile and settings system is now fully functional with comprehensive user management! 👨💻✨
2025-07-26 19:05:55 -04:00
|
|
|
const router = useRouter();
|
|
|
|
|
|
|
|
|
|
// User state
|
fix(layout+pages): default user=null + page audit sweep (P0 #7) (#15)
* convoy: scope fix-layout-default-user (P0 #7 — Layout maintainer-email leak)
The last remaining P0 ship-blocker from .convoys/ship-readiness.md.
components/Layout.js line 562 defaults the user prop to a real email
address (me@randallstillwell.com); any page that renders Layout without
passing user explicitly impersonates the maintainer.
Scope: components/Layout.js + audit of 17 pages that import Layout
(grep-confirmed list in convoy file). Single PR likely. Auditor cohort
skipped (no design-system, IA, or browser-smoke surface).
Architect to address:
- Q1: logged-out rendering branch design (navbar, mobile-nav,
auth-only items treatment)
- Q2: page audit triage into always-auth / public-or-auth /
anonymous-allowed buckets
- Q3: brief decomposition (single brief / 2 briefs in 1 PR / fan-out)
- Q4: whether to add vitest coverage for the logged-out branch
(recommend yes — small surface, high regression protection)
Hard out-of-scope: branding (pick-a-name), auth-provider collapse
(single-auth-provider), Layout god-component split (god-component-split).
depends_on: bump-next-js (shipped), fix-auth-bypass (shipped),
drop-public-setup (shipped)
addresses: P0 #7 from .convoys/ship-readiness.md
parent: ship-readiness
Co-authored-by: Cursor <cursoragent@cursor.com>
* architect(fix-layout-default-user): plan + briefs 1-2 (Layout fix + page audit)
2 briefs, single PR. ~12 files net (down from the 18 in the original scope —
10 of the 17 Layout-importing pages already pass user explicitly).
Brief 1: components/Layout.js default user=null + Sign-in CTA branch in
UserProfileDropdown when logged out. Adds first jsdom test in the repo
at test/components/Layout.test.js (Decision D2) with 5 regression-lock
assertions. devDeps: jsdom@^29, @testing-library/react@^16.
Brief 2: page audit sweep — 7 pages need code changes:
- Pass user={user} to Layout: scanner.js, deck-builder.js (×4),
deck/[id].js (×3), decks.js (×3)
- Replace page-level useState({email: 'me@...'}) → useState(null) +
null-guards: profile.js, settings.js
- Replace hardcoded const user = {email: 'me@...'} with useAuth():
card/[id].js
Discovered second anti-pattern: profile.js, settings.js, card/[id].js
seed page-level state with the maintainer email. Folded into Brief 2 since
success metric "no real email address remains in any component default-prop"
reads naturally to include page-level seed values.
Decisions:
A1 — Sign-in CTA replaces avatar+email+dropdown when user===null;
hides auth-only dropdown (Profile/Settings/Logout/Admin);
keeps public + community nav visible
B — Per-page bucket assignment (10 already correct, 7 need fix);
full per-page table with justification in convoy file
C2 — Two briefs in one PR (Brief 1 = Layout + test; Brief 2 = page
sweep depends on Brief 1). C1 buries the conceptual change under
mechanical edits; C3 is over-orchestrated for this scope
D2 — vitest lock-in; first jsdom test in repo; same negative-regression
style as test/lib/permission-middleware.test.js (synthetic-admin
shape). devDeps jsdom + @testing-library/react
Risks tracked R1-R8. Biggest: R2 (useState(null) null-deref in 3 leaky
pages — mitigated by audit-pass mandate + manual smoke).
MobileNavigation deliberately NOT folded in: its user prop is dead code
(never reads user.*); different bug class; cleanup queued separately to
avoid scope expansion.
Flagged-but-deferred:
- 4 pages still import useAuth from lib/auth-context.js
→ single-auth-provider (queued P1 #9)
- Layout headers still render "Deck Hearth" / "DH" branding
→ pick-a-name (queued P1 #12)
- MobileNavigation dead user prop → cleanup-mobile-nav-dead-props
or fold into god-component-split
addresses: P0 #7 from .convoys/ship-readiness.md (last P0 ship-blocker)
parent: ship-readiness
Co-authored-by: Cursor <cursoragent@cursor.com>
* feat(layout): default user=null + Sign-in CTA when logged out (Brief 1 of fix-layout-default-user)
Closes the source-side half of P0 #7 from .convoys/ship-readiness.md.
The page-side sweep (Brief 2) follows in a separate commit.
components/Layout.js:
- Default user prop is now null (was hardcoded to
{ email: 'me@randallstillwell.com', role: 'user' })
- UserProfileDropdown renders a "Sign in" link to /login when
user === null instead of the maintainer's email + auth-only menu
items (Decision A1)
- All user.* accesses guarded with optional chaining or null checks
- useState hook stays above the new null-user early return to satisfy
rules-of-hooks (boot-the-brief caught this on the first try;
see AGENTS.md Gotcha #11.5)
test/components/Layout.test.js (new):
- First jsdom test in the repo (Decision D2)
- 5 regression-lock assertions: no maintainer email ever rendered
(prop omitted, prop=null), Sign-in link exists with href=/login,
supplied email renders when prop is set, no "Guest" placeholder
(locks A1 copy choice)
- Mocks next/link, next/router (prefetch, replace, events, query),
and theme-context.useTheme for jsdom safety under Next 16
package.json + package-lock.json:
- Add jsdom@^29 and @testing-library/react@^16 to devDependencies
- @testing-library/dom@^10 added explicitly (peer auto-install
skipped it under npm 11; brief anticipated this fallback)
vitest.config.js (deviation from brief — see PR description):
- Add esbuild { loader: 'jsx', jsx: 'automatic' } so vitest can
parse JSX in .js files. Required to import any React component
written in the repo's Next.js pages-router .js convention
(AGENTS.md Gotcha #9). The brief said "no change" to this file,
but JSX-in-.js parsing is a hard prerequisite for the new test
to import components/Layout.js — the alternatives (rename test
to .test.jsx; rewrite test in React.createElement) either break
the test glob or still hit the same Layout.js parse failure.
Other tests are unaffected (they import non-JSX modules).
Smoke output: see PR description.
addresses: P0 #7 from .convoys/ship-readiness.md (last P0 ship-blocker)
Co-authored-by: Cursor <cursoragent@cursor.com>
* feat(pages): pass user explicitly + null-guard leaky page seeds (Brief 2 of fix-layout-default-user)
Closes the page-side half of P0 #7 from .convoys/ship-readiness.md.
Brief 1 (commit ddf8fd2) handled the Layout-side fix.
Per the architect's per-page bucket table (Decision B in
.convoys/fix-layout-default-user.md), 7 pages needed code changes;
the other 10 of 17 Layout-importing pages already pass `user` correctly.
Pass user={user} to Layout (4 pages, 11 call sites):
- pages/scanner.js (1 call)
- pages/decks.js (3 calls)
- pages/deck-builder.js (4 calls)
- pages/deck/[id].js (3 calls)
(All four still import useAuth from lib/auth-context.js — that's
intentional and stays as-is until the single-auth-provider convoy
collapses the three parallel auth surfaces.)
Replace leaky page-level seed values with useState(null) + null guards
(2 pages, R2 mitigation):
- pages/profile.js: useState({email: 'me@...', role: 'user', ...})
→ useState(null) + ?. on every sync user.* read
+ early-return guards in getDisplayName/getInitials
+ conditional render around the "Member since" block
so formatDate(undefined) never runs
- pages/settings.js: same pattern (single user.email reader guarded)
Replace hardcoded const with useAuth from lib/use-auth.js (1 page):
- pages/card/[id].js: const user = {email: 'me@...'}
→ const { user } = useAuth() (called unconditionally
at the top of the component; rules-of-hooks safe)
Verification:
- grep 'me@randallstillwell.com' pages/ → 0 hits
- 21/21 vitest tests pass (16 pre-existing + 5 from Brief 1)
- npm run lint matches baseline (128 problems pre, 128 post; verified
via git stash before/after)
- Manual static read-through of every diff; ReadLints clean on the 7
files
- Dev-server smoke: /cards anonymous returned HTTP 200 with 0
'me@randallstillwell' matches before the user's shared dev server
became unresponsive mid-session (same dev-server-shared-by-user
constraint flagged in Brief 1); interactive logged-in smoke is
parent/operator gated
Flagged-but-deferred (untouched per scope):
- 4 pages still import useAuth from lib/auth-context.js
→ single-auth-provider (queued P1 #9)
- components/MobileNavigation.js still receives dead user prop
→ cleanup-mobile-nav-dead-props (or fold into god-component-split)
addresses: P0 #7 from .convoys/ship-readiness.md (last P0 ship-blocker)
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-24 15:31:37 -04:00
|
|
|
const [user, setUser] = useState(null);
|
🎯 Build Comprehensive User Profile & Settings System
👤 Profile Page Features:
- Complete user profile with avatar, name, username, bio, and email
- Avatar upload with file validation (5MB limit, image types only)
- Avatar generation functionality for custom avatars
- Favorite games selection (MTG, Pokemon, Lorcana)
- Collection statistics display (total cards, collections, decks, value)
- Profile editing with real-time validation
- Member since date and role display
⚙️ Settings Page Features:
- Multi-section tabbed interface (Account, Security, Preferences, Notifications, Display)
- Account settings: email (read-only), collection visibility, preferred currency
- Security settings: password change with validation, 2FA toggle, account deletion
- Preferences: cards per page (25/50/100), default view (grid/list)
- Notifications: email notifications, marketing emails (toggle switches)
- Display settings: theme (light/dark/system), language selection
🗄️ Database Schema Updates:
- Added user profile fields: first_name, last_name, username, bio, avatar_url
- Added preference fields: favorite_games (JSONB), collection_visibility, preferred_currency, cards_per_page, default_view
- Added notification settings: notifications_email, notifications_marketing, two_factor_enabled
- Added display settings: theme, language
- Created user_settings table for complex settings
- Created user_avatars table for avatar management
- Added performance indexes and data validation constraints
📡 API Endpoints Created:
- GET/PUT /api/user/profile - Profile information management
- GET/PUT /api/user/settings - Settings and preferences management
- PUT /api/user/password - Secure password change with bcrypt validation
- GET /api/user/stats - Collection statistics and analytics
🔒 Security & Validation:
- Password change requires current password verification
- Username uniqueness validation
- Input validation for all enum fields (currency, theme, view mode, etc.)
- Proper error handling and user feedback
- Authentication required for all user endpoints
🎨 UI/UX Features:
- Beautiful fire-themed design matching app branding
- Responsive design for mobile and desktop
- Loading states and success/error messages
- Avatar placeholder with user initials
- Tabbed settings interface with icons
- Toggle switches for boolean settings
- Form validation with helpful error messages
✨ Additional Features:
- Collection stats with game/rarity breakdowns
- Recent activity tracking
- Danger zone for account deletion with double confirmation
- Member since display with formatted dates
- Currency formatting for collection values
- Game icons and themed styling throughout
The profile and settings system is now fully functional with comprehensive user management! 👨💻✨
2025-07-26 19:05:55 -04:00
|
|
|
|
|
|
|
|
// Settings state
|
|
|
|
|
const [settings, setSettings] = useState({
|
|
|
|
|
// Account Settings
|
|
|
|
|
notifications_email: true,
|
|
|
|
|
notifications_marketing: false,
|
|
|
|
|
collection_visibility: 'private',
|
|
|
|
|
preferred_currency: 'USD',
|
|
|
|
|
cards_per_page: 50,
|
|
|
|
|
default_view: 'grid',
|
|
|
|
|
|
|
|
|
|
// Security Settings
|
|
|
|
|
two_factor_enabled: false,
|
|
|
|
|
|
|
|
|
|
// Display Settings
|
|
|
|
|
theme: 'system', // light, dark, system
|
|
|
|
|
language: 'en'
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Password change state
|
|
|
|
|
const [passwordForm, setPasswordForm] = useState({
|
|
|
|
|
current_password: '',
|
|
|
|
|
new_password: '',
|
|
|
|
|
confirm_password: ''
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// UI state
|
|
|
|
|
const [loading, setLoading] = useState(true);
|
|
|
|
|
const [saving, setSaving] = useState(false);
|
|
|
|
|
const [changingPassword, setChangingPassword] = useState(false);
|
|
|
|
|
const [message, setMessage] = useState({ type: '', text: '' });
|
|
|
|
|
const [activeSection, setActiveSection] = useState('account');
|
|
|
|
|
|
|
|
|
|
const loadSettings = async () => {
|
|
|
|
|
try {
|
|
|
|
|
const token = localStorage.getItem('auth_token');
|
|
|
|
|
if (!token) {
|
|
|
|
|
router.push('/login');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const response = await fetch('/api/user/settings', {
|
|
|
|
|
headers: {
|
|
|
|
|
'Authorization': `Bearer ${token}`
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (response.ok) {
|
|
|
|
|
const data = await response.json();
|
|
|
|
|
setUser(data.user);
|
|
|
|
|
setSettings(data.settings);
|
|
|
|
|
} else if (response.status === 401) {
|
|
|
|
|
router.push('/login');
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('Error loading settings:', error);
|
|
|
|
|
setMessage({ type: 'error', text: 'Failed to load settings' });
|
|
|
|
|
} finally {
|
|
|
|
|
setLoading(false);
|
|
|
|
|
}
|
2026-06-02 02:03:39 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
// eslint-disable-next-line react-hooks/set-state-in-effect -- load settings on mount
|
|
|
|
|
loadSettings();
|
|
|
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps -- mount-only settings load
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
;
|
🎯 Build Comprehensive User Profile & Settings System
👤 Profile Page Features:
- Complete user profile with avatar, name, username, bio, and email
- Avatar upload with file validation (5MB limit, image types only)
- Avatar generation functionality for custom avatars
- Favorite games selection (MTG, Pokemon, Lorcana)
- Collection statistics display (total cards, collections, decks, value)
- Profile editing with real-time validation
- Member since date and role display
⚙️ Settings Page Features:
- Multi-section tabbed interface (Account, Security, Preferences, Notifications, Display)
- Account settings: email (read-only), collection visibility, preferred currency
- Security settings: password change with validation, 2FA toggle, account deletion
- Preferences: cards per page (25/50/100), default view (grid/list)
- Notifications: email notifications, marketing emails (toggle switches)
- Display settings: theme (light/dark/system), language selection
🗄️ Database Schema Updates:
- Added user profile fields: first_name, last_name, username, bio, avatar_url
- Added preference fields: favorite_games (JSONB), collection_visibility, preferred_currency, cards_per_page, default_view
- Added notification settings: notifications_email, notifications_marketing, two_factor_enabled
- Added display settings: theme, language
- Created user_settings table for complex settings
- Created user_avatars table for avatar management
- Added performance indexes and data validation constraints
📡 API Endpoints Created:
- GET/PUT /api/user/profile - Profile information management
- GET/PUT /api/user/settings - Settings and preferences management
- PUT /api/user/password - Secure password change with bcrypt validation
- GET /api/user/stats - Collection statistics and analytics
🔒 Security & Validation:
- Password change requires current password verification
- Username uniqueness validation
- Input validation for all enum fields (currency, theme, view mode, etc.)
- Proper error handling and user feedback
- Authentication required for all user endpoints
🎨 UI/UX Features:
- Beautiful fire-themed design matching app branding
- Responsive design for mobile and desktop
- Loading states and success/error messages
- Avatar placeholder with user initials
- Tabbed settings interface with icons
- Toggle switches for boolean settings
- Form validation with helpful error messages
✨ Additional Features:
- Collection stats with game/rarity breakdowns
- Recent activity tracking
- Danger zone for account deletion with double confirmation
- Member since display with formatted dates
- Currency formatting for collection values
- Game icons and themed styling throughout
The profile and settings system is now fully functional with comprehensive user management! 👨💻✨
2025-07-26 19:05:55 -04:00
|
|
|
|
|
|
|
|
const handleSettingChange = (key, value) => {
|
|
|
|
|
setSettings(prev => ({
|
|
|
|
|
...prev,
|
|
|
|
|
[key]: value
|
|
|
|
|
}));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const saveSettings = async () => {
|
|
|
|
|
setSaving(true);
|
|
|
|
|
setMessage({ type: '', text: '' });
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const token = localStorage.getItem('auth_token');
|
|
|
|
|
const response = await fetch('/api/user/settings', {
|
|
|
|
|
method: 'PUT',
|
|
|
|
|
headers: {
|
|
|
|
|
'Content-Type': 'application/json',
|
|
|
|
|
'Authorization': `Bearer ${token}`
|
|
|
|
|
},
|
|
|
|
|
body: JSON.stringify(settings)
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (response.ok) {
|
|
|
|
|
setMessage({ type: 'success', text: 'Settings saved successfully!' });
|
|
|
|
|
} else {
|
|
|
|
|
const error = await response.json();
|
|
|
|
|
setMessage({ type: 'error', text: error.message || 'Failed to save settings' });
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('Error saving settings:', error);
|
|
|
|
|
setMessage({ type: 'error', text: 'Failed to save settings' });
|
|
|
|
|
} finally {
|
|
|
|
|
setSaving(false);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handlePasswordChange = async (e) => {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
|
|
|
|
|
if (passwordForm.new_password !== passwordForm.confirm_password) {
|
|
|
|
|
setMessage({ type: 'error', text: 'New passwords do not match' });
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (passwordForm.new_password.length < 8) {
|
|
|
|
|
setMessage({ type: 'error', text: 'Password must be at least 8 characters long' });
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
setChangingPassword(true);
|
|
|
|
|
setMessage({ type: '', text: '' });
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const token = localStorage.getItem('auth_token');
|
|
|
|
|
const response = await fetch('/api/user/password', {
|
|
|
|
|
method: 'PUT',
|
|
|
|
|
headers: {
|
|
|
|
|
'Content-Type': 'application/json',
|
|
|
|
|
'Authorization': `Bearer ${token}`
|
|
|
|
|
},
|
|
|
|
|
body: JSON.stringify({
|
|
|
|
|
current_password: passwordForm.current_password,
|
|
|
|
|
new_password: passwordForm.new_password
|
|
|
|
|
})
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (response.ok) {
|
|
|
|
|
setPasswordForm({ current_password: '', new_password: '', confirm_password: '' });
|
|
|
|
|
setMessage({ type: 'success', text: 'Password changed successfully!' });
|
|
|
|
|
} else {
|
|
|
|
|
const error = await response.json();
|
|
|
|
|
setMessage({ type: 'error', text: error.message || 'Failed to change password' });
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('Error changing password:', error);
|
|
|
|
|
setMessage({ type: 'error', text: 'Failed to change password' });
|
|
|
|
|
} finally {
|
|
|
|
|
setChangingPassword(false);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleDeleteAccount = async () => {
|
|
|
|
|
const confirmed = window.confirm(
|
2026-05-29 10:53:40 -04:00
|
|
|
'Are you sure you want to delete your account? This action cannot be undone and will permanently delete all your cards, lists, and decks.'
|
🎯 Build Comprehensive User Profile & Settings System
👤 Profile Page Features:
- Complete user profile with avatar, name, username, bio, and email
- Avatar upload with file validation (5MB limit, image types only)
- Avatar generation functionality for custom avatars
- Favorite games selection (MTG, Pokemon, Lorcana)
- Collection statistics display (total cards, collections, decks, value)
- Profile editing with real-time validation
- Member since date and role display
⚙️ Settings Page Features:
- Multi-section tabbed interface (Account, Security, Preferences, Notifications, Display)
- Account settings: email (read-only), collection visibility, preferred currency
- Security settings: password change with validation, 2FA toggle, account deletion
- Preferences: cards per page (25/50/100), default view (grid/list)
- Notifications: email notifications, marketing emails (toggle switches)
- Display settings: theme (light/dark/system), language selection
🗄️ Database Schema Updates:
- Added user profile fields: first_name, last_name, username, bio, avatar_url
- Added preference fields: favorite_games (JSONB), collection_visibility, preferred_currency, cards_per_page, default_view
- Added notification settings: notifications_email, notifications_marketing, two_factor_enabled
- Added display settings: theme, language
- Created user_settings table for complex settings
- Created user_avatars table for avatar management
- Added performance indexes and data validation constraints
📡 API Endpoints Created:
- GET/PUT /api/user/profile - Profile information management
- GET/PUT /api/user/settings - Settings and preferences management
- PUT /api/user/password - Secure password change with bcrypt validation
- GET /api/user/stats - Collection statistics and analytics
🔒 Security & Validation:
- Password change requires current password verification
- Username uniqueness validation
- Input validation for all enum fields (currency, theme, view mode, etc.)
- Proper error handling and user feedback
- Authentication required for all user endpoints
🎨 UI/UX Features:
- Beautiful fire-themed design matching app branding
- Responsive design for mobile and desktop
- Loading states and success/error messages
- Avatar placeholder with user initials
- Tabbed settings interface with icons
- Toggle switches for boolean settings
- Form validation with helpful error messages
✨ Additional Features:
- Collection stats with game/rarity breakdowns
- Recent activity tracking
- Danger zone for account deletion with double confirmation
- Member since display with formatted dates
- Currency formatting for collection values
- Game icons and themed styling throughout
The profile and settings system is now fully functional with comprehensive user management! 👨💻✨
2025-07-26 19:05:55 -04:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (!confirmed) return;
|
|
|
|
|
|
|
|
|
|
const doubleConfirmed = window.confirm(
|
|
|
|
|
'This is your final warning. Deleting your account will permanently remove all your data. Type "DELETE" to confirm.'
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (!doubleConfirmed) return;
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const token = localStorage.getItem('auth_token');
|
|
|
|
|
const response = await fetch('/api/user/delete', {
|
|
|
|
|
method: 'DELETE',
|
|
|
|
|
headers: {
|
|
|
|
|
'Authorization': `Bearer ${token}`
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (response.ok) {
|
|
|
|
|
localStorage.removeItem('auth_token');
|
|
|
|
|
router.push('/login?message=Account deleted successfully');
|
|
|
|
|
} else {
|
|
|
|
|
const error = await response.json();
|
|
|
|
|
setMessage({ type: 'error', text: error.message || 'Failed to delete account' });
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('Error deleting account:', error);
|
|
|
|
|
setMessage({ type: 'error', text: 'Failed to delete account' });
|
|
|
|
|
}
|
2025-07-23 22:26:54 -04:00
|
|
|
};
|
|
|
|
|
|
🎯 Build Comprehensive User Profile & Settings System
👤 Profile Page Features:
- Complete user profile with avatar, name, username, bio, and email
- Avatar upload with file validation (5MB limit, image types only)
- Avatar generation functionality for custom avatars
- Favorite games selection (MTG, Pokemon, Lorcana)
- Collection statistics display (total cards, collections, decks, value)
- Profile editing with real-time validation
- Member since date and role display
⚙️ Settings Page Features:
- Multi-section tabbed interface (Account, Security, Preferences, Notifications, Display)
- Account settings: email (read-only), collection visibility, preferred currency
- Security settings: password change with validation, 2FA toggle, account deletion
- Preferences: cards per page (25/50/100), default view (grid/list)
- Notifications: email notifications, marketing emails (toggle switches)
- Display settings: theme (light/dark/system), language selection
🗄️ Database Schema Updates:
- Added user profile fields: first_name, last_name, username, bio, avatar_url
- Added preference fields: favorite_games (JSONB), collection_visibility, preferred_currency, cards_per_page, default_view
- Added notification settings: notifications_email, notifications_marketing, two_factor_enabled
- Added display settings: theme, language
- Created user_settings table for complex settings
- Created user_avatars table for avatar management
- Added performance indexes and data validation constraints
📡 API Endpoints Created:
- GET/PUT /api/user/profile - Profile information management
- GET/PUT /api/user/settings - Settings and preferences management
- PUT /api/user/password - Secure password change with bcrypt validation
- GET /api/user/stats - Collection statistics and analytics
🔒 Security & Validation:
- Password change requires current password verification
- Username uniqueness validation
- Input validation for all enum fields (currency, theme, view mode, etc.)
- Proper error handling and user feedback
- Authentication required for all user endpoints
🎨 UI/UX Features:
- Beautiful fire-themed design matching app branding
- Responsive design for mobile and desktop
- Loading states and success/error messages
- Avatar placeholder with user initials
- Tabbed settings interface with icons
- Toggle switches for boolean settings
- Form validation with helpful error messages
✨ Additional Features:
- Collection stats with game/rarity breakdowns
- Recent activity tracking
- Danger zone for account deletion with double confirmation
- Member since display with formatted dates
- Currency formatting for collection values
- Game icons and themed styling throughout
The profile and settings system is now fully functional with comprehensive user management! 👨💻✨
2025-07-26 19:05:55 -04:00
|
|
|
const sections = [
|
|
|
|
|
{ id: 'account', name: 'Account', icon: '👤' },
|
|
|
|
|
{ id: 'security', name: 'Security', icon: '🔒' },
|
|
|
|
|
{ id: 'preferences', name: 'Preferences', icon: '⚙️' },
|
|
|
|
|
{ id: 'notifications', name: 'Notifications', icon: '🔔' },
|
|
|
|
|
{ id: 'display', name: 'Display', icon: '🎨' }
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
const currencyOptions = [
|
|
|
|
|
{ value: 'USD', label: 'US Dollar ($)' },
|
|
|
|
|
{ value: 'EUR', label: 'Euro (€)' },
|
|
|
|
|
{ value: 'GBP', label: 'British Pound (£)' },
|
|
|
|
|
{ value: 'CAD', label: 'Canadian Dollar (C$)' },
|
|
|
|
|
{ value: 'JPY', label: 'Japanese Yen (¥)' }
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
const cardsPerPageOptions = [
|
|
|
|
|
{ value: 25, label: '25 cards' },
|
|
|
|
|
{ value: 50, label: '50 cards' },
|
|
|
|
|
{ value: 100, label: '100 cards' }
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
const viewOptions = [
|
|
|
|
|
{ value: 'grid', label: 'Grid View' },
|
|
|
|
|
{ value: 'list', label: 'List View' }
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
const themeOptions = [
|
|
|
|
|
{ value: 'light', label: 'Light' },
|
|
|
|
|
{ value: 'dark', label: 'Dark' },
|
|
|
|
|
{ value: 'system', label: 'System' }
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
if (loading) {
|
|
|
|
|
return (
|
|
|
|
|
<Layout user={user}>
|
|
|
|
|
<div className="flex items-center justify-center min-h-screen">
|
|
|
|
|
<div className="animate-spin rounded-full h-32 w-32 border-b-2" style={{ borderColor: 'var(--accent-ember)' }}></div>
|
|
|
|
|
</div>
|
|
|
|
|
</Layout>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-23 22:26:54 -04:00
|
|
|
return (
|
|
|
|
|
<Layout user={user}>
|
🎯 Build Comprehensive User Profile & Settings System
👤 Profile Page Features:
- Complete user profile with avatar, name, username, bio, and email
- Avatar upload with file validation (5MB limit, image types only)
- Avatar generation functionality for custom avatars
- Favorite games selection (MTG, Pokemon, Lorcana)
- Collection statistics display (total cards, collections, decks, value)
- Profile editing with real-time validation
- Member since date and role display
⚙️ Settings Page Features:
- Multi-section tabbed interface (Account, Security, Preferences, Notifications, Display)
- Account settings: email (read-only), collection visibility, preferred currency
- Security settings: password change with validation, 2FA toggle, account deletion
- Preferences: cards per page (25/50/100), default view (grid/list)
- Notifications: email notifications, marketing emails (toggle switches)
- Display settings: theme (light/dark/system), language selection
🗄️ Database Schema Updates:
- Added user profile fields: first_name, last_name, username, bio, avatar_url
- Added preference fields: favorite_games (JSONB), collection_visibility, preferred_currency, cards_per_page, default_view
- Added notification settings: notifications_email, notifications_marketing, two_factor_enabled
- Added display settings: theme, language
- Created user_settings table for complex settings
- Created user_avatars table for avatar management
- Added performance indexes and data validation constraints
📡 API Endpoints Created:
- GET/PUT /api/user/profile - Profile information management
- GET/PUT /api/user/settings - Settings and preferences management
- PUT /api/user/password - Secure password change with bcrypt validation
- GET /api/user/stats - Collection statistics and analytics
🔒 Security & Validation:
- Password change requires current password verification
- Username uniqueness validation
- Input validation for all enum fields (currency, theme, view mode, etc.)
- Proper error handling and user feedback
- Authentication required for all user endpoints
🎨 UI/UX Features:
- Beautiful fire-themed design matching app branding
- Responsive design for mobile and desktop
- Loading states and success/error messages
- Avatar placeholder with user initials
- Tabbed settings interface with icons
- Toggle switches for boolean settings
- Form validation with helpful error messages
✨ Additional Features:
- Collection stats with game/rarity breakdowns
- Recent activity tracking
- Danger zone for account deletion with double confirmation
- Member since display with formatted dates
- Currency formatting for collection values
- Game icons and themed styling throughout
The profile and settings system is now fully functional with comprehensive user management! 👨💻✨
2025-07-26 19:05:55 -04:00
|
|
|
<div className="max-w-6xl mx-auto p-6">
|
|
|
|
|
{/* Header */}
|
|
|
|
|
<div className="mb-8">
|
|
|
|
|
<h1 className="text-3xl font-bold mb-2" style={{ color: 'var(--text-primary)' }}>
|
2025-07-23 22:26:54 -04:00
|
|
|
Settings
|
|
|
|
|
</h1>
|
🎯 Build Comprehensive User Profile & Settings System
👤 Profile Page Features:
- Complete user profile with avatar, name, username, bio, and email
- Avatar upload with file validation (5MB limit, image types only)
- Avatar generation functionality for custom avatars
- Favorite games selection (MTG, Pokemon, Lorcana)
- Collection statistics display (total cards, collections, decks, value)
- Profile editing with real-time validation
- Member since date and role display
⚙️ Settings Page Features:
- Multi-section tabbed interface (Account, Security, Preferences, Notifications, Display)
- Account settings: email (read-only), collection visibility, preferred currency
- Security settings: password change with validation, 2FA toggle, account deletion
- Preferences: cards per page (25/50/100), default view (grid/list)
- Notifications: email notifications, marketing emails (toggle switches)
- Display settings: theme (light/dark/system), language selection
🗄️ Database Schema Updates:
- Added user profile fields: first_name, last_name, username, bio, avatar_url
- Added preference fields: favorite_games (JSONB), collection_visibility, preferred_currency, cards_per_page, default_view
- Added notification settings: notifications_email, notifications_marketing, two_factor_enabled
- Added display settings: theme, language
- Created user_settings table for complex settings
- Created user_avatars table for avatar management
- Added performance indexes and data validation constraints
📡 API Endpoints Created:
- GET/PUT /api/user/profile - Profile information management
- GET/PUT /api/user/settings - Settings and preferences management
- PUT /api/user/password - Secure password change with bcrypt validation
- GET /api/user/stats - Collection statistics and analytics
🔒 Security & Validation:
- Password change requires current password verification
- Username uniqueness validation
- Input validation for all enum fields (currency, theme, view mode, etc.)
- Proper error handling and user feedback
- Authentication required for all user endpoints
🎨 UI/UX Features:
- Beautiful fire-themed design matching app branding
- Responsive design for mobile and desktop
- Loading states and success/error messages
- Avatar placeholder with user initials
- Tabbed settings interface with icons
- Toggle switches for boolean settings
- Form validation with helpful error messages
✨ Additional Features:
- Collection stats with game/rarity breakdowns
- Recent activity tracking
- Danger zone for account deletion with double confirmation
- Member since display with formatted dates
- Currency formatting for collection values
- Game icons and themed styling throughout
The profile and settings system is now fully functional with comprehensive user management! 👨💻✨
2025-07-26 19:05:55 -04:00
|
|
|
<p className="text-lg" style={{ color: 'var(--text-secondary)' }}>
|
|
|
|
|
Manage your account preferences and security settings
|
2025-07-23 22:26:54 -04:00
|
|
|
</p>
|
|
|
|
|
</div>
|
🎯 Build Comprehensive User Profile & Settings System
👤 Profile Page Features:
- Complete user profile with avatar, name, username, bio, and email
- Avatar upload with file validation (5MB limit, image types only)
- Avatar generation functionality for custom avatars
- Favorite games selection (MTG, Pokemon, Lorcana)
- Collection statistics display (total cards, collections, decks, value)
- Profile editing with real-time validation
- Member since date and role display
⚙️ Settings Page Features:
- Multi-section tabbed interface (Account, Security, Preferences, Notifications, Display)
- Account settings: email (read-only), collection visibility, preferred currency
- Security settings: password change with validation, 2FA toggle, account deletion
- Preferences: cards per page (25/50/100), default view (grid/list)
- Notifications: email notifications, marketing emails (toggle switches)
- Display settings: theme (light/dark/system), language selection
🗄️ Database Schema Updates:
- Added user profile fields: first_name, last_name, username, bio, avatar_url
- Added preference fields: favorite_games (JSONB), collection_visibility, preferred_currency, cards_per_page, default_view
- Added notification settings: notifications_email, notifications_marketing, two_factor_enabled
- Added display settings: theme, language
- Created user_settings table for complex settings
- Created user_avatars table for avatar management
- Added performance indexes and data validation constraints
📡 API Endpoints Created:
- GET/PUT /api/user/profile - Profile information management
- GET/PUT /api/user/settings - Settings and preferences management
- PUT /api/user/password - Secure password change with bcrypt validation
- GET /api/user/stats - Collection statistics and analytics
🔒 Security & Validation:
- Password change requires current password verification
- Username uniqueness validation
- Input validation for all enum fields (currency, theme, view mode, etc.)
- Proper error handling and user feedback
- Authentication required for all user endpoints
🎨 UI/UX Features:
- Beautiful fire-themed design matching app branding
- Responsive design for mobile and desktop
- Loading states and success/error messages
- Avatar placeholder with user initials
- Tabbed settings interface with icons
- Toggle switches for boolean settings
- Form validation with helpful error messages
✨ Additional Features:
- Collection stats with game/rarity breakdowns
- Recent activity tracking
- Danger zone for account deletion with double confirmation
- Member since display with formatted dates
- Currency formatting for collection values
- Game icons and themed styling throughout
The profile and settings system is now fully functional with comprehensive user management! 👨💻✨
2025-07-26 19:05:55 -04:00
|
|
|
|
|
|
|
|
{/* Message */}
|
|
|
|
|
{message.text && (
|
|
|
|
|
<div className={`mb-6 p-4 rounded-xl ${
|
|
|
|
|
message.type === 'success'
|
|
|
|
|
? 'bg-green-50 dark:bg-green-900/20 text-green-800 dark:text-green-200 border border-green-200 dark:border-green-800'
|
|
|
|
|
: 'bg-red-50 dark:bg-red-900/20 text-red-800 dark:text-red-200 border border-red-200 dark:border-red-800'
|
|
|
|
|
}`}>
|
|
|
|
|
{message.text}
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
<div className="grid grid-cols-1 lg:grid-cols-4 gap-8">
|
|
|
|
|
{/* Sidebar Navigation */}
|
|
|
|
|
<div className="lg:col-span-1">
|
|
|
|
|
<div className="card p-4">
|
|
|
|
|
<nav className="space-y-2">
|
|
|
|
|
{sections.map(section => (
|
|
|
|
|
<button
|
|
|
|
|
key={section.id}
|
|
|
|
|
onClick={() => setActiveSection(section.id)}
|
|
|
|
|
className={`w-full flex items-center gap-3 px-4 py-3 rounded-xl font-medium transition-all duration-200 text-left ${
|
|
|
|
|
activeSection === section.id ? 'shadow-lg' : 'hover:shadow-md'
|
|
|
|
|
}`}
|
|
|
|
|
style={{
|
|
|
|
|
backgroundColor: activeSection === section.id ? 'var(--accent-ember)' : 'transparent',
|
|
|
|
|
color: activeSection === section.id ? 'white' : 'var(--text-primary)'
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<span>{section.icon}</span>
|
|
|
|
|
{section.name}
|
|
|
|
|
</button>
|
|
|
|
|
))}
|
|
|
|
|
</nav>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* Settings Content */}
|
|
|
|
|
<div className="lg:col-span-3">
|
|
|
|
|
<div className="card p-6">
|
|
|
|
|
{/* Account Settings */}
|
|
|
|
|
{activeSection === 'account' && (
|
|
|
|
|
<div>
|
|
|
|
|
<h2 className="text-2xl font-bold mb-6" style={{ color: 'var(--text-primary)' }}>
|
|
|
|
|
Account Settings
|
|
|
|
|
</h2>
|
|
|
|
|
|
|
|
|
|
<div className="space-y-6">
|
|
|
|
|
{/* Email */}
|
|
|
|
|
<div>
|
|
|
|
|
<label className="block text-sm font-medium mb-2" style={{ color: 'var(--text-primary)' }}>
|
|
|
|
|
Email Address
|
|
|
|
|
</label>
|
|
|
|
|
<div className="flex items-center gap-3">
|
|
|
|
|
<input
|
|
|
|
|
type="email"
|
fix(layout+pages): default user=null + page audit sweep (P0 #7) (#15)
* convoy: scope fix-layout-default-user (P0 #7 — Layout maintainer-email leak)
The last remaining P0 ship-blocker from .convoys/ship-readiness.md.
components/Layout.js line 562 defaults the user prop to a real email
address (me@randallstillwell.com); any page that renders Layout without
passing user explicitly impersonates the maintainer.
Scope: components/Layout.js + audit of 17 pages that import Layout
(grep-confirmed list in convoy file). Single PR likely. Auditor cohort
skipped (no design-system, IA, or browser-smoke surface).
Architect to address:
- Q1: logged-out rendering branch design (navbar, mobile-nav,
auth-only items treatment)
- Q2: page audit triage into always-auth / public-or-auth /
anonymous-allowed buckets
- Q3: brief decomposition (single brief / 2 briefs in 1 PR / fan-out)
- Q4: whether to add vitest coverage for the logged-out branch
(recommend yes — small surface, high regression protection)
Hard out-of-scope: branding (pick-a-name), auth-provider collapse
(single-auth-provider), Layout god-component split (god-component-split).
depends_on: bump-next-js (shipped), fix-auth-bypass (shipped),
drop-public-setup (shipped)
addresses: P0 #7 from .convoys/ship-readiness.md
parent: ship-readiness
Co-authored-by: Cursor <cursoragent@cursor.com>
* architect(fix-layout-default-user): plan + briefs 1-2 (Layout fix + page audit)
2 briefs, single PR. ~12 files net (down from the 18 in the original scope —
10 of the 17 Layout-importing pages already pass user explicitly).
Brief 1: components/Layout.js default user=null + Sign-in CTA branch in
UserProfileDropdown when logged out. Adds first jsdom test in the repo
at test/components/Layout.test.js (Decision D2) with 5 regression-lock
assertions. devDeps: jsdom@^29, @testing-library/react@^16.
Brief 2: page audit sweep — 7 pages need code changes:
- Pass user={user} to Layout: scanner.js, deck-builder.js (×4),
deck/[id].js (×3), decks.js (×3)
- Replace page-level useState({email: 'me@...'}) → useState(null) +
null-guards: profile.js, settings.js
- Replace hardcoded const user = {email: 'me@...'} with useAuth():
card/[id].js
Discovered second anti-pattern: profile.js, settings.js, card/[id].js
seed page-level state with the maintainer email. Folded into Brief 2 since
success metric "no real email address remains in any component default-prop"
reads naturally to include page-level seed values.
Decisions:
A1 — Sign-in CTA replaces avatar+email+dropdown when user===null;
hides auth-only dropdown (Profile/Settings/Logout/Admin);
keeps public + community nav visible
B — Per-page bucket assignment (10 already correct, 7 need fix);
full per-page table with justification in convoy file
C2 — Two briefs in one PR (Brief 1 = Layout + test; Brief 2 = page
sweep depends on Brief 1). C1 buries the conceptual change under
mechanical edits; C3 is over-orchestrated for this scope
D2 — vitest lock-in; first jsdom test in repo; same negative-regression
style as test/lib/permission-middleware.test.js (synthetic-admin
shape). devDeps jsdom + @testing-library/react
Risks tracked R1-R8. Biggest: R2 (useState(null) null-deref in 3 leaky
pages — mitigated by audit-pass mandate + manual smoke).
MobileNavigation deliberately NOT folded in: its user prop is dead code
(never reads user.*); different bug class; cleanup queued separately to
avoid scope expansion.
Flagged-but-deferred:
- 4 pages still import useAuth from lib/auth-context.js
→ single-auth-provider (queued P1 #9)
- Layout headers still render "Deck Hearth" / "DH" branding
→ pick-a-name (queued P1 #12)
- MobileNavigation dead user prop → cleanup-mobile-nav-dead-props
or fold into god-component-split
addresses: P0 #7 from .convoys/ship-readiness.md (last P0 ship-blocker)
parent: ship-readiness
Co-authored-by: Cursor <cursoragent@cursor.com>
* feat(layout): default user=null + Sign-in CTA when logged out (Brief 1 of fix-layout-default-user)
Closes the source-side half of P0 #7 from .convoys/ship-readiness.md.
The page-side sweep (Brief 2) follows in a separate commit.
components/Layout.js:
- Default user prop is now null (was hardcoded to
{ email: 'me@randallstillwell.com', role: 'user' })
- UserProfileDropdown renders a "Sign in" link to /login when
user === null instead of the maintainer's email + auth-only menu
items (Decision A1)
- All user.* accesses guarded with optional chaining or null checks
- useState hook stays above the new null-user early return to satisfy
rules-of-hooks (boot-the-brief caught this on the first try;
see AGENTS.md Gotcha #11.5)
test/components/Layout.test.js (new):
- First jsdom test in the repo (Decision D2)
- 5 regression-lock assertions: no maintainer email ever rendered
(prop omitted, prop=null), Sign-in link exists with href=/login,
supplied email renders when prop is set, no "Guest" placeholder
(locks A1 copy choice)
- Mocks next/link, next/router (prefetch, replace, events, query),
and theme-context.useTheme for jsdom safety under Next 16
package.json + package-lock.json:
- Add jsdom@^29 and @testing-library/react@^16 to devDependencies
- @testing-library/dom@^10 added explicitly (peer auto-install
skipped it under npm 11; brief anticipated this fallback)
vitest.config.js (deviation from brief — see PR description):
- Add esbuild { loader: 'jsx', jsx: 'automatic' } so vitest can
parse JSX in .js files. Required to import any React component
written in the repo's Next.js pages-router .js convention
(AGENTS.md Gotcha #9). The brief said "no change" to this file,
but JSX-in-.js parsing is a hard prerequisite for the new test
to import components/Layout.js — the alternatives (rename test
to .test.jsx; rewrite test in React.createElement) either break
the test glob or still hit the same Layout.js parse failure.
Other tests are unaffected (they import non-JSX modules).
Smoke output: see PR description.
addresses: P0 #7 from .convoys/ship-readiness.md (last P0 ship-blocker)
Co-authored-by: Cursor <cursoragent@cursor.com>
* feat(pages): pass user explicitly + null-guard leaky page seeds (Brief 2 of fix-layout-default-user)
Closes the page-side half of P0 #7 from .convoys/ship-readiness.md.
Brief 1 (commit ddf8fd2) handled the Layout-side fix.
Per the architect's per-page bucket table (Decision B in
.convoys/fix-layout-default-user.md), 7 pages needed code changes;
the other 10 of 17 Layout-importing pages already pass `user` correctly.
Pass user={user} to Layout (4 pages, 11 call sites):
- pages/scanner.js (1 call)
- pages/decks.js (3 calls)
- pages/deck-builder.js (4 calls)
- pages/deck/[id].js (3 calls)
(All four still import useAuth from lib/auth-context.js — that's
intentional and stays as-is until the single-auth-provider convoy
collapses the three parallel auth surfaces.)
Replace leaky page-level seed values with useState(null) + null guards
(2 pages, R2 mitigation):
- pages/profile.js: useState({email: 'me@...', role: 'user', ...})
→ useState(null) + ?. on every sync user.* read
+ early-return guards in getDisplayName/getInitials
+ conditional render around the "Member since" block
so formatDate(undefined) never runs
- pages/settings.js: same pattern (single user.email reader guarded)
Replace hardcoded const with useAuth from lib/use-auth.js (1 page):
- pages/card/[id].js: const user = {email: 'me@...'}
→ const { user } = useAuth() (called unconditionally
at the top of the component; rules-of-hooks safe)
Verification:
- grep 'me@randallstillwell.com' pages/ → 0 hits
- 21/21 vitest tests pass (16 pre-existing + 5 from Brief 1)
- npm run lint matches baseline (128 problems pre, 128 post; verified
via git stash before/after)
- Manual static read-through of every diff; ReadLints clean on the 7
files
- Dev-server smoke: /cards anonymous returned HTTP 200 with 0
'me@randallstillwell' matches before the user's shared dev server
became unresponsive mid-session (same dev-server-shared-by-user
constraint flagged in Brief 1); interactive logged-in smoke is
parent/operator gated
Flagged-but-deferred (untouched per scope):
- 4 pages still import useAuth from lib/auth-context.js
→ single-auth-provider (queued P1 #9)
- components/MobileNavigation.js still receives dead user prop
→ cleanup-mobile-nav-dead-props (or fold into god-component-split)
addresses: P0 #7 from .convoys/ship-readiness.md (last P0 ship-blocker)
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-24 15:31:37 -04:00
|
|
|
value={user?.email || ''}
|
🎯 Build Comprehensive User Profile & Settings System
👤 Profile Page Features:
- Complete user profile with avatar, name, username, bio, and email
- Avatar upload with file validation (5MB limit, image types only)
- Avatar generation functionality for custom avatars
- Favorite games selection (MTG, Pokemon, Lorcana)
- Collection statistics display (total cards, collections, decks, value)
- Profile editing with real-time validation
- Member since date and role display
⚙️ Settings Page Features:
- Multi-section tabbed interface (Account, Security, Preferences, Notifications, Display)
- Account settings: email (read-only), collection visibility, preferred currency
- Security settings: password change with validation, 2FA toggle, account deletion
- Preferences: cards per page (25/50/100), default view (grid/list)
- Notifications: email notifications, marketing emails (toggle switches)
- Display settings: theme (light/dark/system), language selection
🗄️ Database Schema Updates:
- Added user profile fields: first_name, last_name, username, bio, avatar_url
- Added preference fields: favorite_games (JSONB), collection_visibility, preferred_currency, cards_per_page, default_view
- Added notification settings: notifications_email, notifications_marketing, two_factor_enabled
- Added display settings: theme, language
- Created user_settings table for complex settings
- Created user_avatars table for avatar management
- Added performance indexes and data validation constraints
📡 API Endpoints Created:
- GET/PUT /api/user/profile - Profile information management
- GET/PUT /api/user/settings - Settings and preferences management
- PUT /api/user/password - Secure password change with bcrypt validation
- GET /api/user/stats - Collection statistics and analytics
🔒 Security & Validation:
- Password change requires current password verification
- Username uniqueness validation
- Input validation for all enum fields (currency, theme, view mode, etc.)
- Proper error handling and user feedback
- Authentication required for all user endpoints
🎨 UI/UX Features:
- Beautiful fire-themed design matching app branding
- Responsive design for mobile and desktop
- Loading states and success/error messages
- Avatar placeholder with user initials
- Tabbed settings interface with icons
- Toggle switches for boolean settings
- Form validation with helpful error messages
✨ Additional Features:
- Collection stats with game/rarity breakdowns
- Recent activity tracking
- Danger zone for account deletion with double confirmation
- Member since display with formatted dates
- Currency formatting for collection values
- Game icons and themed styling throughout
The profile and settings system is now fully functional with comprehensive user management! 👨💻✨
2025-07-26 19:05:55 -04:00
|
|
|
disabled
|
|
|
|
|
className="input-field flex-1 opacity-50 cursor-not-allowed"
|
|
|
|
|
/>
|
|
|
|
|
<span className="px-3 py-1 text-xs rounded-full bg-green-100 dark:bg-green-900/20 text-green-800 dark:text-green-200">
|
|
|
|
|
Verified
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
<p className="text-xs mt-1" style={{ color: 'var(--text-secondary)' }}>
|
|
|
|
|
Contact support to change your email address
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* Collection Visibility */}
|
|
|
|
|
<div>
|
|
|
|
|
<label className="block text-sm font-medium mb-2" style={{ color: 'var(--text-primary)' }}>
|
2026-05-29 10:53:40 -04:00
|
|
|
Default List Visibility
|
🎯 Build Comprehensive User Profile & Settings System
👤 Profile Page Features:
- Complete user profile with avatar, name, username, bio, and email
- Avatar upload with file validation (5MB limit, image types only)
- Avatar generation functionality for custom avatars
- Favorite games selection (MTG, Pokemon, Lorcana)
- Collection statistics display (total cards, collections, decks, value)
- Profile editing with real-time validation
- Member since date and role display
⚙️ Settings Page Features:
- Multi-section tabbed interface (Account, Security, Preferences, Notifications, Display)
- Account settings: email (read-only), collection visibility, preferred currency
- Security settings: password change with validation, 2FA toggle, account deletion
- Preferences: cards per page (25/50/100), default view (grid/list)
- Notifications: email notifications, marketing emails (toggle switches)
- Display settings: theme (light/dark/system), language selection
🗄️ Database Schema Updates:
- Added user profile fields: first_name, last_name, username, bio, avatar_url
- Added preference fields: favorite_games (JSONB), collection_visibility, preferred_currency, cards_per_page, default_view
- Added notification settings: notifications_email, notifications_marketing, two_factor_enabled
- Added display settings: theme, language
- Created user_settings table for complex settings
- Created user_avatars table for avatar management
- Added performance indexes and data validation constraints
📡 API Endpoints Created:
- GET/PUT /api/user/profile - Profile information management
- GET/PUT /api/user/settings - Settings and preferences management
- PUT /api/user/password - Secure password change with bcrypt validation
- GET /api/user/stats - Collection statistics and analytics
🔒 Security & Validation:
- Password change requires current password verification
- Username uniqueness validation
- Input validation for all enum fields (currency, theme, view mode, etc.)
- Proper error handling and user feedback
- Authentication required for all user endpoints
🎨 UI/UX Features:
- Beautiful fire-themed design matching app branding
- Responsive design for mobile and desktop
- Loading states and success/error messages
- Avatar placeholder with user initials
- Tabbed settings interface with icons
- Toggle switches for boolean settings
- Form validation with helpful error messages
✨ Additional Features:
- Collection stats with game/rarity breakdowns
- Recent activity tracking
- Danger zone for account deletion with double confirmation
- Member since display with formatted dates
- Currency formatting for collection values
- Game icons and themed styling throughout
The profile and settings system is now fully functional with comprehensive user management! 👨💻✨
2025-07-26 19:05:55 -04:00
|
|
|
</label>
|
|
|
|
|
<select
|
|
|
|
|
value={settings.collection_visibility}
|
|
|
|
|
onChange={(e) => handleSettingChange('collection_visibility', e.target.value)}
|
|
|
|
|
className="input-field w-full"
|
|
|
|
|
>
|
|
|
|
|
<option value="private">Private</option>
|
|
|
|
|
<option value="public">Public</option>
|
|
|
|
|
<option value="unlisted">Unlisted</option>
|
|
|
|
|
</select>
|
|
|
|
|
<p className="text-xs mt-1" style={{ color: 'var(--text-secondary)' }}>
|
2026-05-29 10:53:40 -04:00
|
|
|
Controls who can see your new lists by default
|
🎯 Build Comprehensive User Profile & Settings System
👤 Profile Page Features:
- Complete user profile with avatar, name, username, bio, and email
- Avatar upload with file validation (5MB limit, image types only)
- Avatar generation functionality for custom avatars
- Favorite games selection (MTG, Pokemon, Lorcana)
- Collection statistics display (total cards, collections, decks, value)
- Profile editing with real-time validation
- Member since date and role display
⚙️ Settings Page Features:
- Multi-section tabbed interface (Account, Security, Preferences, Notifications, Display)
- Account settings: email (read-only), collection visibility, preferred currency
- Security settings: password change with validation, 2FA toggle, account deletion
- Preferences: cards per page (25/50/100), default view (grid/list)
- Notifications: email notifications, marketing emails (toggle switches)
- Display settings: theme (light/dark/system), language selection
🗄️ Database Schema Updates:
- Added user profile fields: first_name, last_name, username, bio, avatar_url
- Added preference fields: favorite_games (JSONB), collection_visibility, preferred_currency, cards_per_page, default_view
- Added notification settings: notifications_email, notifications_marketing, two_factor_enabled
- Added display settings: theme, language
- Created user_settings table for complex settings
- Created user_avatars table for avatar management
- Added performance indexes and data validation constraints
📡 API Endpoints Created:
- GET/PUT /api/user/profile - Profile information management
- GET/PUT /api/user/settings - Settings and preferences management
- PUT /api/user/password - Secure password change with bcrypt validation
- GET /api/user/stats - Collection statistics and analytics
🔒 Security & Validation:
- Password change requires current password verification
- Username uniqueness validation
- Input validation for all enum fields (currency, theme, view mode, etc.)
- Proper error handling and user feedback
- Authentication required for all user endpoints
🎨 UI/UX Features:
- Beautiful fire-themed design matching app branding
- Responsive design for mobile and desktop
- Loading states and success/error messages
- Avatar placeholder with user initials
- Tabbed settings interface with icons
- Toggle switches for boolean settings
- Form validation with helpful error messages
✨ Additional Features:
- Collection stats with game/rarity breakdowns
- Recent activity tracking
- Danger zone for account deletion with double confirmation
- Member since display with formatted dates
- Currency formatting for collection values
- Game icons and themed styling throughout
The profile and settings system is now fully functional with comprehensive user management! 👨💻✨
2025-07-26 19:05:55 -04:00
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* Preferred Currency */}
|
|
|
|
|
<div>
|
|
|
|
|
<label className="block text-sm font-medium mb-2" style={{ color: 'var(--text-primary)' }}>
|
|
|
|
|
Preferred Currency
|
|
|
|
|
</label>
|
|
|
|
|
<select
|
|
|
|
|
value={settings.preferred_currency}
|
|
|
|
|
onChange={(e) => handleSettingChange('preferred_currency', e.target.value)}
|
|
|
|
|
className="input-field w-full"
|
|
|
|
|
>
|
|
|
|
|
{currencyOptions.map(option => (
|
|
|
|
|
<option key={option.value} value={option.value}>
|
|
|
|
|
{option.label}
|
|
|
|
|
</option>
|
|
|
|
|
))}
|
|
|
|
|
</select>
|
|
|
|
|
<p className="text-xs mt-1" style={{ color: 'var(--text-secondary)' }}>
|
|
|
|
|
Currency used for displaying card values
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{/* Security Settings */}
|
|
|
|
|
{activeSection === 'security' && (
|
|
|
|
|
<div>
|
|
|
|
|
<h2 className="text-2xl font-bold mb-6" style={{ color: 'var(--text-primary)' }}>
|
|
|
|
|
Security Settings
|
|
|
|
|
</h2>
|
|
|
|
|
|
|
|
|
|
<div className="space-y-8">
|
|
|
|
|
{/* Change Password */}
|
|
|
|
|
<div>
|
|
|
|
|
<h3 className="text-lg font-semibold mb-4" style={{ color: 'var(--text-primary)' }}>
|
|
|
|
|
Change Password
|
|
|
|
|
</h3>
|
|
|
|
|
<form onSubmit={handlePasswordChange} className="space-y-4">
|
|
|
|
|
<div>
|
|
|
|
|
<label className="block text-sm font-medium mb-2" style={{ color: 'var(--text-primary)' }}>
|
|
|
|
|
Current Password
|
|
|
|
|
</label>
|
|
|
|
|
<input
|
|
|
|
|
type="password"
|
|
|
|
|
value={passwordForm.current_password}
|
|
|
|
|
onChange={(e) => setPasswordForm(prev => ({ ...prev, current_password: e.target.value }))}
|
|
|
|
|
className="input-field w-full"
|
|
|
|
|
required
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="block text-sm font-medium mb-2" style={{ color: 'var(--text-primary)' }}>
|
|
|
|
|
New Password
|
|
|
|
|
</label>
|
|
|
|
|
<input
|
|
|
|
|
type="password"
|
|
|
|
|
value={passwordForm.new_password}
|
|
|
|
|
onChange={(e) => setPasswordForm(prev => ({ ...prev, new_password: e.target.value }))}
|
|
|
|
|
className="input-field w-full"
|
|
|
|
|
minLength={8}
|
|
|
|
|
required
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="block text-sm font-medium mb-2" style={{ color: 'var(--text-primary)' }}>
|
|
|
|
|
Confirm New Password
|
|
|
|
|
</label>
|
|
|
|
|
<input
|
|
|
|
|
type="password"
|
|
|
|
|
value={passwordForm.confirm_password}
|
|
|
|
|
onChange={(e) => setPasswordForm(prev => ({ ...prev, confirm_password: e.target.value }))}
|
|
|
|
|
className="input-field w-full"
|
|
|
|
|
minLength={8}
|
|
|
|
|
required
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<button
|
|
|
|
|
type="submit"
|
|
|
|
|
disabled={changingPassword}
|
|
|
|
|
className="px-6 py-2 rounded-xl font-medium transition-all duration-200 hover:shadow-md"
|
|
|
|
|
style={{
|
|
|
|
|
backgroundColor: 'var(--accent-ember)',
|
|
|
|
|
color: 'white'
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
{changingPassword ? 'Changing Password...' : 'Change Password'}
|
|
|
|
|
</button>
|
|
|
|
|
</form>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* Two-Factor Authentication */}
|
|
|
|
|
<div>
|
|
|
|
|
<h3 className="text-lg font-semibold mb-4" style={{ color: 'var(--text-primary)' }}>
|
|
|
|
|
Two-Factor Authentication
|
|
|
|
|
</h3>
|
feat(design-system): sweep authenticated body-content panels to glass (#97)
PR #95/#96 shipped the Liquid Glass foundation (tokens, primitives, gates)
plus Layout shell, modals, landing, auth pages, and form CTAs — but body-
content panels on authenticated pages (admin Card Editor, admin Card
Import, admin Submissions, dashboard, my-cards, settings, scanner panels,
card detail price cards, popovers) were still rendering as flat
var(--bg-secondary) cards. Result: the admin Tools screen and several
core pages looked unchanged after the redesign.
This sweep adds a `.glass-panel` / `.glass-panel-strong` utility
(<GlassSurface tint=mid/high rim=subtle elevation=ambient/pronounced
blur=mid/high> in class form) and applies it across 18 surfaces:
* Admin Card Editor view, search panel, form (5 sections), preview
* Admin Card Import navigation + 3 body cards + sync panel
* Admin Card Submissions list items
* Dashboard stat cards + empty-state + grid items (5 surfaces)
* My-cards empty-state CTA card
* Settings panels (3)
* Scanner page settings + grid + queue + bulk toolbar + dialog
* Scanner destination picker + camera status banner + disambiguation
* Card detail price cards (Current / TCGPlayer / CardKingdom)
* Permission indicator tooltips
* Collections page header card
* Card detail view price cards
Also migrates the lingering admin Card Editor "Card Editor / Card Import"
nav buttons and the "Save Changes" / "Import Cards" / "Run catalog sync"
CTAs to the <Button> primitive (consistent loading + disabled states).
Page header bands (full-bleed strips with border-bottom on dashboard,
my-cards, cards, collections, community/collections, collection/[id])
are intentionally left solid — they're not card-shaped surfaces and
stacking glass-on-glass directly below the already-glass topbar would
muddy the hierarchy.
Tests: lint clean, vitest 104/104, build green. The visual diff
baseline will need refresh because the homepage spec is unaffected
(it targets the unauthenticated landing page) but the dashboard/
admin/scanner surfaces will diff if/when we add baselines for them.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-03 22:28:52 -04:00
|
|
|
<div className="glass-panel flex items-center justify-between p-4 rounded-xl">
|
🎯 Build Comprehensive User Profile & Settings System
👤 Profile Page Features:
- Complete user profile with avatar, name, username, bio, and email
- Avatar upload with file validation (5MB limit, image types only)
- Avatar generation functionality for custom avatars
- Favorite games selection (MTG, Pokemon, Lorcana)
- Collection statistics display (total cards, collections, decks, value)
- Profile editing with real-time validation
- Member since date and role display
⚙️ Settings Page Features:
- Multi-section tabbed interface (Account, Security, Preferences, Notifications, Display)
- Account settings: email (read-only), collection visibility, preferred currency
- Security settings: password change with validation, 2FA toggle, account deletion
- Preferences: cards per page (25/50/100), default view (grid/list)
- Notifications: email notifications, marketing emails (toggle switches)
- Display settings: theme (light/dark/system), language selection
🗄️ Database Schema Updates:
- Added user profile fields: first_name, last_name, username, bio, avatar_url
- Added preference fields: favorite_games (JSONB), collection_visibility, preferred_currency, cards_per_page, default_view
- Added notification settings: notifications_email, notifications_marketing, two_factor_enabled
- Added display settings: theme, language
- Created user_settings table for complex settings
- Created user_avatars table for avatar management
- Added performance indexes and data validation constraints
📡 API Endpoints Created:
- GET/PUT /api/user/profile - Profile information management
- GET/PUT /api/user/settings - Settings and preferences management
- PUT /api/user/password - Secure password change with bcrypt validation
- GET /api/user/stats - Collection statistics and analytics
🔒 Security & Validation:
- Password change requires current password verification
- Username uniqueness validation
- Input validation for all enum fields (currency, theme, view mode, etc.)
- Proper error handling and user feedback
- Authentication required for all user endpoints
🎨 UI/UX Features:
- Beautiful fire-themed design matching app branding
- Responsive design for mobile and desktop
- Loading states and success/error messages
- Avatar placeholder with user initials
- Tabbed settings interface with icons
- Toggle switches for boolean settings
- Form validation with helpful error messages
✨ Additional Features:
- Collection stats with game/rarity breakdowns
- Recent activity tracking
- Danger zone for account deletion with double confirmation
- Member since display with formatted dates
- Currency formatting for collection values
- Game icons and themed styling throughout
The profile and settings system is now fully functional with comprehensive user management! 👨💻✨
2025-07-26 19:05:55 -04:00
|
|
|
<div>
|
|
|
|
|
<p className="font-medium" style={{ color: 'var(--text-primary)' }}>
|
|
|
|
|
Enable 2FA
|
|
|
|
|
</p>
|
|
|
|
|
<p className="text-sm" style={{ color: 'var(--text-secondary)' }}>
|
|
|
|
|
Add an extra layer of security to your account
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
<button
|
|
|
|
|
onClick={() => handleSettingChange('two_factor_enabled', !settings.two_factor_enabled)}
|
|
|
|
|
className={`relative inline-flex h-6 w-11 items-center rounded-full transition-colors ${
|
|
|
|
|
settings.two_factor_enabled ? 'bg-green-600' : 'bg-gray-300 dark:bg-gray-600'
|
|
|
|
|
}`}
|
|
|
|
|
>
|
|
|
|
|
<span
|
|
|
|
|
className={`inline-block h-4 w-4 transform rounded-full bg-white transition-transform ${
|
|
|
|
|
settings.two_factor_enabled ? 'translate-x-6' : 'translate-x-1'
|
|
|
|
|
}`}
|
|
|
|
|
/>
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
{settings.two_factor_enabled && (
|
|
|
|
|
<p className="text-sm mt-2 p-3 rounded-lg bg-blue-50 dark:bg-blue-900/20 text-blue-800 dark:text-blue-200">
|
|
|
|
|
Two-factor authentication is enabled. Use your authenticator app to log in.
|
|
|
|
|
</p>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* Danger Zone */}
|
|
|
|
|
<div className="border-t pt-8" style={{ borderColor: 'var(--border)' }}>
|
|
|
|
|
<h3 className="text-lg font-semibold mb-4 text-red-600 dark:text-red-400">
|
|
|
|
|
Danger Zone
|
|
|
|
|
</h3>
|
|
|
|
|
<div className="p-4 rounded-xl border border-red-200 dark:border-red-800 bg-red-50 dark:bg-red-900/20">
|
|
|
|
|
<div className="flex items-center justify-between">
|
|
|
|
|
<div>
|
|
|
|
|
<p className="font-medium text-red-800 dark:text-red-200">
|
|
|
|
|
Delete Account
|
|
|
|
|
</p>
|
|
|
|
|
<p className="text-sm text-red-600 dark:text-red-300">
|
|
|
|
|
Permanently delete your account and all associated data
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
<button
|
|
|
|
|
onClick={handleDeleteAccount}
|
|
|
|
|
className="px-4 py-2 bg-red-600 hover:bg-red-700 text-white rounded-xl font-medium transition-colors"
|
|
|
|
|
>
|
|
|
|
|
Delete Account
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{/* Preferences */}
|
|
|
|
|
{activeSection === 'preferences' && (
|
|
|
|
|
<div>
|
|
|
|
|
<h2 className="text-2xl font-bold mb-6" style={{ color: 'var(--text-primary)' }}>
|
|
|
|
|
Preferences
|
|
|
|
|
</h2>
|
|
|
|
|
|
|
|
|
|
<div className="space-y-6">
|
|
|
|
|
{/* Cards Per Page */}
|
|
|
|
|
<div>
|
|
|
|
|
<label className="block text-sm font-medium mb-2" style={{ color: 'var(--text-primary)' }}>
|
|
|
|
|
Cards Per Page
|
|
|
|
|
</label>
|
|
|
|
|
<select
|
|
|
|
|
value={settings.cards_per_page}
|
|
|
|
|
onChange={(e) => handleSettingChange('cards_per_page', parseInt(e.target.value))}
|
|
|
|
|
className="input-field w-full"
|
|
|
|
|
>
|
|
|
|
|
{cardsPerPageOptions.map(option => (
|
|
|
|
|
<option key={option.value} value={option.value}>
|
|
|
|
|
{option.label}
|
|
|
|
|
</option>
|
|
|
|
|
))}
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* Default View */}
|
|
|
|
|
<div>
|
|
|
|
|
<label className="block text-sm font-medium mb-2" style={{ color: 'var(--text-primary)' }}>
|
|
|
|
|
Default View Mode
|
|
|
|
|
</label>
|
|
|
|
|
<select
|
|
|
|
|
value={settings.default_view}
|
|
|
|
|
onChange={(e) => handleSettingChange('default_view', e.target.value)}
|
|
|
|
|
className="input-field w-full"
|
|
|
|
|
>
|
|
|
|
|
{viewOptions.map(option => (
|
|
|
|
|
<option key={option.value} value={option.value}>
|
|
|
|
|
{option.label}
|
|
|
|
|
</option>
|
|
|
|
|
))}
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{/* Notifications */}
|
|
|
|
|
{activeSection === 'notifications' && (
|
|
|
|
|
<div>
|
|
|
|
|
<h2 className="text-2xl font-bold mb-6" style={{ color: 'var(--text-primary)' }}>
|
|
|
|
|
Notification Settings
|
|
|
|
|
</h2>
|
|
|
|
|
|
|
|
|
|
<div className="space-y-6">
|
|
|
|
|
{/* Email Notifications */}
|
feat(design-system): sweep authenticated body-content panels to glass (#97)
PR #95/#96 shipped the Liquid Glass foundation (tokens, primitives, gates)
plus Layout shell, modals, landing, auth pages, and form CTAs — but body-
content panels on authenticated pages (admin Card Editor, admin Card
Import, admin Submissions, dashboard, my-cards, settings, scanner panels,
card detail price cards, popovers) were still rendering as flat
var(--bg-secondary) cards. Result: the admin Tools screen and several
core pages looked unchanged after the redesign.
This sweep adds a `.glass-panel` / `.glass-panel-strong` utility
(<GlassSurface tint=mid/high rim=subtle elevation=ambient/pronounced
blur=mid/high> in class form) and applies it across 18 surfaces:
* Admin Card Editor view, search panel, form (5 sections), preview
* Admin Card Import navigation + 3 body cards + sync panel
* Admin Card Submissions list items
* Dashboard stat cards + empty-state + grid items (5 surfaces)
* My-cards empty-state CTA card
* Settings panels (3)
* Scanner page settings + grid + queue + bulk toolbar + dialog
* Scanner destination picker + camera status banner + disambiguation
* Card detail price cards (Current / TCGPlayer / CardKingdom)
* Permission indicator tooltips
* Collections page header card
* Card detail view price cards
Also migrates the lingering admin Card Editor "Card Editor / Card Import"
nav buttons and the "Save Changes" / "Import Cards" / "Run catalog sync"
CTAs to the <Button> primitive (consistent loading + disabled states).
Page header bands (full-bleed strips with border-bottom on dashboard,
my-cards, cards, collections, community/collections, collection/[id])
are intentionally left solid — they're not card-shaped surfaces and
stacking glass-on-glass directly below the already-glass topbar would
muddy the hierarchy.
Tests: lint clean, vitest 104/104, build green. The visual diff
baseline will need refresh because the homepage spec is unaffected
(it targets the unauthenticated landing page) but the dashboard/
admin/scanner surfaces will diff if/when we add baselines for them.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-03 22:28:52 -04:00
|
|
|
<div className="glass-panel flex items-center justify-between p-4 rounded-xl">
|
🎯 Build Comprehensive User Profile & Settings System
👤 Profile Page Features:
- Complete user profile with avatar, name, username, bio, and email
- Avatar upload with file validation (5MB limit, image types only)
- Avatar generation functionality for custom avatars
- Favorite games selection (MTG, Pokemon, Lorcana)
- Collection statistics display (total cards, collections, decks, value)
- Profile editing with real-time validation
- Member since date and role display
⚙️ Settings Page Features:
- Multi-section tabbed interface (Account, Security, Preferences, Notifications, Display)
- Account settings: email (read-only), collection visibility, preferred currency
- Security settings: password change with validation, 2FA toggle, account deletion
- Preferences: cards per page (25/50/100), default view (grid/list)
- Notifications: email notifications, marketing emails (toggle switches)
- Display settings: theme (light/dark/system), language selection
🗄️ Database Schema Updates:
- Added user profile fields: first_name, last_name, username, bio, avatar_url
- Added preference fields: favorite_games (JSONB), collection_visibility, preferred_currency, cards_per_page, default_view
- Added notification settings: notifications_email, notifications_marketing, two_factor_enabled
- Added display settings: theme, language
- Created user_settings table for complex settings
- Created user_avatars table for avatar management
- Added performance indexes and data validation constraints
📡 API Endpoints Created:
- GET/PUT /api/user/profile - Profile information management
- GET/PUT /api/user/settings - Settings and preferences management
- PUT /api/user/password - Secure password change with bcrypt validation
- GET /api/user/stats - Collection statistics and analytics
🔒 Security & Validation:
- Password change requires current password verification
- Username uniqueness validation
- Input validation for all enum fields (currency, theme, view mode, etc.)
- Proper error handling and user feedback
- Authentication required for all user endpoints
🎨 UI/UX Features:
- Beautiful fire-themed design matching app branding
- Responsive design for mobile and desktop
- Loading states and success/error messages
- Avatar placeholder with user initials
- Tabbed settings interface with icons
- Toggle switches for boolean settings
- Form validation with helpful error messages
✨ Additional Features:
- Collection stats with game/rarity breakdowns
- Recent activity tracking
- Danger zone for account deletion with double confirmation
- Member since display with formatted dates
- Currency formatting for collection values
- Game icons and themed styling throughout
The profile and settings system is now fully functional with comprehensive user management! 👨💻✨
2025-07-26 19:05:55 -04:00
|
|
|
<div>
|
|
|
|
|
<p className="font-medium" style={{ color: 'var(--text-primary)' }}>
|
|
|
|
|
Email Notifications
|
|
|
|
|
</p>
|
|
|
|
|
<p className="text-sm" style={{ color: 'var(--text-secondary)' }}>
|
2026-05-29 10:53:40 -04:00
|
|
|
Receive notifications about your lists and cards
|
🎯 Build Comprehensive User Profile & Settings System
👤 Profile Page Features:
- Complete user profile with avatar, name, username, bio, and email
- Avatar upload with file validation (5MB limit, image types only)
- Avatar generation functionality for custom avatars
- Favorite games selection (MTG, Pokemon, Lorcana)
- Collection statistics display (total cards, collections, decks, value)
- Profile editing with real-time validation
- Member since date and role display
⚙️ Settings Page Features:
- Multi-section tabbed interface (Account, Security, Preferences, Notifications, Display)
- Account settings: email (read-only), collection visibility, preferred currency
- Security settings: password change with validation, 2FA toggle, account deletion
- Preferences: cards per page (25/50/100), default view (grid/list)
- Notifications: email notifications, marketing emails (toggle switches)
- Display settings: theme (light/dark/system), language selection
🗄️ Database Schema Updates:
- Added user profile fields: first_name, last_name, username, bio, avatar_url
- Added preference fields: favorite_games (JSONB), collection_visibility, preferred_currency, cards_per_page, default_view
- Added notification settings: notifications_email, notifications_marketing, two_factor_enabled
- Added display settings: theme, language
- Created user_settings table for complex settings
- Created user_avatars table for avatar management
- Added performance indexes and data validation constraints
📡 API Endpoints Created:
- GET/PUT /api/user/profile - Profile information management
- GET/PUT /api/user/settings - Settings and preferences management
- PUT /api/user/password - Secure password change with bcrypt validation
- GET /api/user/stats - Collection statistics and analytics
🔒 Security & Validation:
- Password change requires current password verification
- Username uniqueness validation
- Input validation for all enum fields (currency, theme, view mode, etc.)
- Proper error handling and user feedback
- Authentication required for all user endpoints
🎨 UI/UX Features:
- Beautiful fire-themed design matching app branding
- Responsive design for mobile and desktop
- Loading states and success/error messages
- Avatar placeholder with user initials
- Tabbed settings interface with icons
- Toggle switches for boolean settings
- Form validation with helpful error messages
✨ Additional Features:
- Collection stats with game/rarity breakdowns
- Recent activity tracking
- Danger zone for account deletion with double confirmation
- Member since display with formatted dates
- Currency formatting for collection values
- Game icons and themed styling throughout
The profile and settings system is now fully functional with comprehensive user management! 👨💻✨
2025-07-26 19:05:55 -04:00
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
<button
|
|
|
|
|
onClick={() => handleSettingChange('notifications_email', !settings.notifications_email)}
|
|
|
|
|
className={`relative inline-flex h-6 w-11 items-center rounded-full transition-colors ${
|
|
|
|
|
settings.notifications_email ? 'bg-green-600' : 'bg-gray-300 dark:bg-gray-600'
|
|
|
|
|
}`}
|
|
|
|
|
>
|
|
|
|
|
<span
|
|
|
|
|
className={`inline-block h-4 w-4 transform rounded-full bg-white transition-transform ${
|
|
|
|
|
settings.notifications_email ? 'translate-x-6' : 'translate-x-1'
|
|
|
|
|
}`}
|
|
|
|
|
/>
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* Marketing Emails */}
|
feat(design-system): sweep authenticated body-content panels to glass (#97)
PR #95/#96 shipped the Liquid Glass foundation (tokens, primitives, gates)
plus Layout shell, modals, landing, auth pages, and form CTAs — but body-
content panels on authenticated pages (admin Card Editor, admin Card
Import, admin Submissions, dashboard, my-cards, settings, scanner panels,
card detail price cards, popovers) were still rendering as flat
var(--bg-secondary) cards. Result: the admin Tools screen and several
core pages looked unchanged after the redesign.
This sweep adds a `.glass-panel` / `.glass-panel-strong` utility
(<GlassSurface tint=mid/high rim=subtle elevation=ambient/pronounced
blur=mid/high> in class form) and applies it across 18 surfaces:
* Admin Card Editor view, search panel, form (5 sections), preview
* Admin Card Import navigation + 3 body cards + sync panel
* Admin Card Submissions list items
* Dashboard stat cards + empty-state + grid items (5 surfaces)
* My-cards empty-state CTA card
* Settings panels (3)
* Scanner page settings + grid + queue + bulk toolbar + dialog
* Scanner destination picker + camera status banner + disambiguation
* Card detail price cards (Current / TCGPlayer / CardKingdom)
* Permission indicator tooltips
* Collections page header card
* Card detail view price cards
Also migrates the lingering admin Card Editor "Card Editor / Card Import"
nav buttons and the "Save Changes" / "Import Cards" / "Run catalog sync"
CTAs to the <Button> primitive (consistent loading + disabled states).
Page header bands (full-bleed strips with border-bottom on dashboard,
my-cards, cards, collections, community/collections, collection/[id])
are intentionally left solid — they're not card-shaped surfaces and
stacking glass-on-glass directly below the already-glass topbar would
muddy the hierarchy.
Tests: lint clean, vitest 104/104, build green. The visual diff
baseline will need refresh because the homepage spec is unaffected
(it targets the unauthenticated landing page) but the dashboard/
admin/scanner surfaces will diff if/when we add baselines for them.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-03 22:28:52 -04:00
|
|
|
<div className="glass-panel flex items-center justify-between p-4 rounded-xl">
|
🎯 Build Comprehensive User Profile & Settings System
👤 Profile Page Features:
- Complete user profile with avatar, name, username, bio, and email
- Avatar upload with file validation (5MB limit, image types only)
- Avatar generation functionality for custom avatars
- Favorite games selection (MTG, Pokemon, Lorcana)
- Collection statistics display (total cards, collections, decks, value)
- Profile editing with real-time validation
- Member since date and role display
⚙️ Settings Page Features:
- Multi-section tabbed interface (Account, Security, Preferences, Notifications, Display)
- Account settings: email (read-only), collection visibility, preferred currency
- Security settings: password change with validation, 2FA toggle, account deletion
- Preferences: cards per page (25/50/100), default view (grid/list)
- Notifications: email notifications, marketing emails (toggle switches)
- Display settings: theme (light/dark/system), language selection
🗄️ Database Schema Updates:
- Added user profile fields: first_name, last_name, username, bio, avatar_url
- Added preference fields: favorite_games (JSONB), collection_visibility, preferred_currency, cards_per_page, default_view
- Added notification settings: notifications_email, notifications_marketing, two_factor_enabled
- Added display settings: theme, language
- Created user_settings table for complex settings
- Created user_avatars table for avatar management
- Added performance indexes and data validation constraints
📡 API Endpoints Created:
- GET/PUT /api/user/profile - Profile information management
- GET/PUT /api/user/settings - Settings and preferences management
- PUT /api/user/password - Secure password change with bcrypt validation
- GET /api/user/stats - Collection statistics and analytics
🔒 Security & Validation:
- Password change requires current password verification
- Username uniqueness validation
- Input validation for all enum fields (currency, theme, view mode, etc.)
- Proper error handling and user feedback
- Authentication required for all user endpoints
🎨 UI/UX Features:
- Beautiful fire-themed design matching app branding
- Responsive design for mobile and desktop
- Loading states and success/error messages
- Avatar placeholder with user initials
- Tabbed settings interface with icons
- Toggle switches for boolean settings
- Form validation with helpful error messages
✨ Additional Features:
- Collection stats with game/rarity breakdowns
- Recent activity tracking
- Danger zone for account deletion with double confirmation
- Member since display with formatted dates
- Currency formatting for collection values
- Game icons and themed styling throughout
The profile and settings system is now fully functional with comprehensive user management! 👨💻✨
2025-07-26 19:05:55 -04:00
|
|
|
<div>
|
|
|
|
|
<p className="font-medium" style={{ color: 'var(--text-primary)' }}>
|
|
|
|
|
Marketing Emails
|
|
|
|
|
</p>
|
|
|
|
|
<p className="text-sm" style={{ color: 'var(--text-secondary)' }}>
|
|
|
|
|
Receive updates about new features and promotions
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
<button
|
|
|
|
|
onClick={() => handleSettingChange('notifications_marketing', !settings.notifications_marketing)}
|
|
|
|
|
className={`relative inline-flex h-6 w-11 items-center rounded-full transition-colors ${
|
|
|
|
|
settings.notifications_marketing ? 'bg-green-600' : 'bg-gray-300 dark:bg-gray-600'
|
|
|
|
|
}`}
|
|
|
|
|
>
|
|
|
|
|
<span
|
|
|
|
|
className={`inline-block h-4 w-4 transform rounded-full bg-white transition-transform ${
|
|
|
|
|
settings.notifications_marketing ? 'translate-x-6' : 'translate-x-1'
|
|
|
|
|
}`}
|
|
|
|
|
/>
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{/* Display Settings */}
|
|
|
|
|
{activeSection === 'display' && (
|
|
|
|
|
<div>
|
|
|
|
|
<h2 className="text-2xl font-bold mb-6" style={{ color: 'var(--text-primary)' }}>
|
|
|
|
|
Display Settings
|
|
|
|
|
</h2>
|
|
|
|
|
|
|
|
|
|
<div className="space-y-6">
|
|
|
|
|
{/* Theme */}
|
|
|
|
|
<div>
|
|
|
|
|
<label className="block text-sm font-medium mb-2" style={{ color: 'var(--text-primary)' }}>
|
|
|
|
|
Theme
|
|
|
|
|
</label>
|
|
|
|
|
<select
|
|
|
|
|
value={settings.theme}
|
|
|
|
|
onChange={(e) => handleSettingChange('theme', e.target.value)}
|
|
|
|
|
className="input-field w-full"
|
|
|
|
|
>
|
|
|
|
|
{themeOptions.map(option => (
|
|
|
|
|
<option key={option.value} value={option.value}>
|
|
|
|
|
{option.label}
|
|
|
|
|
</option>
|
|
|
|
|
))}
|
|
|
|
|
</select>
|
|
|
|
|
<p className="text-xs mt-1" style={{ color: 'var(--text-secondary)' }}>
|
2026-06-02 02:03:39 -04:00
|
|
|
System will follow your device's theme preference
|
🎯 Build Comprehensive User Profile & Settings System
👤 Profile Page Features:
- Complete user profile with avatar, name, username, bio, and email
- Avatar upload with file validation (5MB limit, image types only)
- Avatar generation functionality for custom avatars
- Favorite games selection (MTG, Pokemon, Lorcana)
- Collection statistics display (total cards, collections, decks, value)
- Profile editing with real-time validation
- Member since date and role display
⚙️ Settings Page Features:
- Multi-section tabbed interface (Account, Security, Preferences, Notifications, Display)
- Account settings: email (read-only), collection visibility, preferred currency
- Security settings: password change with validation, 2FA toggle, account deletion
- Preferences: cards per page (25/50/100), default view (grid/list)
- Notifications: email notifications, marketing emails (toggle switches)
- Display settings: theme (light/dark/system), language selection
🗄️ Database Schema Updates:
- Added user profile fields: first_name, last_name, username, bio, avatar_url
- Added preference fields: favorite_games (JSONB), collection_visibility, preferred_currency, cards_per_page, default_view
- Added notification settings: notifications_email, notifications_marketing, two_factor_enabled
- Added display settings: theme, language
- Created user_settings table for complex settings
- Created user_avatars table for avatar management
- Added performance indexes and data validation constraints
📡 API Endpoints Created:
- GET/PUT /api/user/profile - Profile information management
- GET/PUT /api/user/settings - Settings and preferences management
- PUT /api/user/password - Secure password change with bcrypt validation
- GET /api/user/stats - Collection statistics and analytics
🔒 Security & Validation:
- Password change requires current password verification
- Username uniqueness validation
- Input validation for all enum fields (currency, theme, view mode, etc.)
- Proper error handling and user feedback
- Authentication required for all user endpoints
🎨 UI/UX Features:
- Beautiful fire-themed design matching app branding
- Responsive design for mobile and desktop
- Loading states and success/error messages
- Avatar placeholder with user initials
- Tabbed settings interface with icons
- Toggle switches for boolean settings
- Form validation with helpful error messages
✨ Additional Features:
- Collection stats with game/rarity breakdowns
- Recent activity tracking
- Danger zone for account deletion with double confirmation
- Member since display with formatted dates
- Currency formatting for collection values
- Game icons and themed styling throughout
The profile and settings system is now fully functional with comprehensive user management! 👨💻✨
2025-07-26 19:05:55 -04:00
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* Language */}
|
|
|
|
|
<div>
|
|
|
|
|
<label className="block text-sm font-medium mb-2" style={{ color: 'var(--text-primary)' }}>
|
|
|
|
|
Language
|
|
|
|
|
</label>
|
|
|
|
|
<select
|
|
|
|
|
value={settings.language}
|
|
|
|
|
onChange={(e) => handleSettingChange('language', e.target.value)}
|
|
|
|
|
className="input-field w-full"
|
|
|
|
|
>
|
|
|
|
|
<option value="en">English</option>
|
|
|
|
|
<option value="es">Español</option>
|
|
|
|
|
<option value="fr">Français</option>
|
|
|
|
|
<option value="de">Deutsch</option>
|
|
|
|
|
<option value="ja">日本語</option>
|
|
|
|
|
</select>
|
|
|
|
|
<p className="text-xs mt-1" style={{ color: 'var(--text-secondary)' }}>
|
|
|
|
|
Interface language (coming soon)
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{/* Save Button */}
|
|
|
|
|
<div className="flex justify-end pt-8 border-t" style={{ borderColor: 'var(--border)' }}>
|
|
|
|
|
<button
|
|
|
|
|
onClick={saveSettings}
|
|
|
|
|
disabled={saving}
|
|
|
|
|
className="px-6 py-3 rounded-xl font-medium transition-all duration-200 hover:shadow-md"
|
|
|
|
|
style={{
|
|
|
|
|
backgroundColor: 'var(--accent-ember)',
|
|
|
|
|
color: 'white'
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
{saving ? 'Saving...' : 'Save Settings'}
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2025-07-23 22:26:54 -04:00
|
|
|
</div>
|
|
|
|
|
</Layout>
|
|
|
|
|
);
|
|
|
|
|
}
|