Compare commits
2 commits
2f905d7173
...
36d6fbfad9
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
36d6fbfad9 | ||
|
|
a7ce4d17a1 |
2 changed files with 82 additions and 60 deletions
|
|
@ -21,24 +21,29 @@ import {
|
|||
Shield,
|
||||
Sparkles,
|
||||
Check,
|
||||
ArrowLeft
|
||||
ArrowLeft,
|
||||
Lock
|
||||
} from "lucide-react"
|
||||
import Link from "next/link"
|
||||
|
||||
// Avatar color palette - Jewel Tones (Primary Set)
|
||||
// Avatar color palette - Jewel Tones with XP unlock tiers
|
||||
const PRESET_COLORS = [
|
||||
{ value: "#0F4C81", name: "Sapphire" },
|
||||
{ value: "#9B111E", name: "Ruby" },
|
||||
{ value: "#50C878", name: "Emerald" },
|
||||
{ value: "#9966CC", name: "Amethyst" },
|
||||
{ value: "#0D98BA", name: "Topaz" },
|
||||
{ value: "#E0115F", name: "Rose Quartz" },
|
||||
{ value: "#082567", name: "Lapis" },
|
||||
{ value: "#FF7518", name: "Carnelian" },
|
||||
{ value: "#006B3C", name: "Jade" },
|
||||
{ value: "#1C1C1C", name: "Onyx" },
|
||||
{ value: "#E6E200", name: "Citrine" },
|
||||
{ value: "#702963", name: "Garnet" },
|
||||
// Starter Tier (0 XP) - Always unlocked
|
||||
{ value: "#0F4C81", name: "Sapphire", tier: "Starter", xpRequired: 0 },
|
||||
{ value: "#9B111E", name: "Ruby", tier: "Starter", xpRequired: 0 },
|
||||
{ value: "#50C878", name: "Emerald", tier: "Starter", xpRequired: 0 },
|
||||
{ value: "#9966CC", name: "Amethyst", tier: "Starter", xpRequired: 0 },
|
||||
// Bronze Tier (100 XP)
|
||||
{ value: "#0D98BA", name: "Topaz", tier: "Bronze", xpRequired: 100 },
|
||||
{ value: "#E0115F", name: "Rose Quartz", tier: "Bronze", xpRequired: 100 },
|
||||
{ value: "#082567", name: "Lapis", tier: "Bronze", xpRequired: 100 },
|
||||
{ value: "#FF7518", name: "Carnelian", tier: "Bronze", xpRequired: 100 },
|
||||
// Silver Tier (500 XP)
|
||||
{ value: "#006B3C", name: "Jade", tier: "Silver", xpRequired: 500 },
|
||||
{ value: "#1C1C1C", name: "Onyx", tier: "Silver", xpRequired: 500 },
|
||||
// Gold Tier (1000 XP)
|
||||
{ value: "#E6E200", name: "Citrine", tier: "Gold", xpRequired: 1000 },
|
||||
{ value: "#702963", name: "Garnet", tier: "Gold", xpRequired: 1000 },
|
||||
]
|
||||
|
||||
export default function SettingsPage() {
|
||||
|
|
@ -233,6 +238,7 @@ export default function SettingsPage() {
|
|||
saved={avatarSaved}
|
||||
error={avatarError}
|
||||
onSave={handleSaveAvatar}
|
||||
userXp={(user as any)?.xp || 0}
|
||||
/>
|
||||
|
||||
<Separator />
|
||||
|
|
@ -244,7 +250,7 @@ export default function SettingsPage() {
|
|||
|
||||
<Separator />
|
||||
|
||||
<NotificationsSection />
|
||||
<NotificationsSection preferences={preferences} updatePreferences={updatePreferences} />
|
||||
|
||||
<Separator />
|
||||
|
||||
|
|
@ -296,6 +302,7 @@ export default function SettingsPage() {
|
|||
saved={avatarSaved}
|
||||
error={avatarError}
|
||||
onSave={handleSaveAvatar}
|
||||
userXp={(user as any)?.xp || 0}
|
||||
/>
|
||||
</TabsContent>
|
||||
|
||||
|
|
@ -307,7 +314,7 @@ export default function SettingsPage() {
|
|||
</TabsContent>
|
||||
|
||||
<TabsContent value="notifications">
|
||||
<NotificationsSection />
|
||||
<NotificationsSection preferences={preferences} updatePreferences={updatePreferences} />
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="privacy">
|
||||
|
|
@ -412,7 +419,8 @@ function AppearanceSection({
|
|||
saving,
|
||||
saved,
|
||||
error,
|
||||
onSave
|
||||
onSave,
|
||||
userXp = 0
|
||||
}: any) {
|
||||
return (
|
||||
<Card id="appearance">
|
||||
|
|
@ -422,7 +430,7 @@ function AppearanceSection({
|
|||
Appearance
|
||||
</CardTitle>
|
||||
<CardDescription>
|
||||
Customize how you appear across the site
|
||||
Customize how you appear across the site. Earn XP to unlock more colors!
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-6">
|
||||
|
|
@ -455,24 +463,43 @@ function AppearanceSection({
|
|||
</div>
|
||||
</div>
|
||||
|
||||
{/* Color Grid */}
|
||||
{/* Color Grid with Tiers */}
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<Label>Background Color</Label>
|
||||
<span className="text-xs text-muted-foreground">Your XP: {userXp}</span>
|
||||
</div>
|
||||
<div className="grid grid-cols-6 gap-3">
|
||||
{PRESET_COLORS.map((color) => (
|
||||
{PRESET_COLORS.map((color) => {
|
||||
const isLocked = userXp < color.xpRequired
|
||||
return (
|
||||
<button
|
||||
key={color.value}
|
||||
onClick={() => setAvatarBgColor(color.value)}
|
||||
className="relative aspect-square rounded-xl transition-all hover:scale-110 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-ring"
|
||||
onClick={() => !isLocked && setAvatarBgColor(color.value)}
|
||||
disabled={isLocked}
|
||||
className={`relative aspect-square rounded-xl transition-all focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-ring ${isLocked
|
||||
? 'opacity-40 cursor-not-allowed'
|
||||
: 'hover:scale-110'
|
||||
}`}
|
||||
style={{ backgroundColor: color.value }}
|
||||
title={color.name}
|
||||
title={isLocked
|
||||
? `${color.name} - Unlock at ${color.xpRequired} XP`
|
||||
: `${color.name} (${color.tier})`
|
||||
}
|
||||
>
|
||||
{avatarBgColor === color.value && (
|
||||
{avatarBgColor === color.value && !isLocked && (
|
||||
<Check className="absolute inset-0 m-auto h-5 w-5 text-white drop-shadow-lg" />
|
||||
)}
|
||||
{isLocked && (
|
||||
<Lock className="absolute inset-0 m-auto h-4 w-4 text-white/70 drop-shadow-lg" />
|
||||
)}
|
||||
</button>
|
||||
))}
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Earn XP by attending shows, writing reviews, and rating performances
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{error && (
|
||||
|
|
@ -481,7 +508,7 @@ function AppearanceSection({
|
|||
|
||||
<div className="flex justify-end">
|
||||
<Button onClick={onSave} disabled={saving}>
|
||||
{saving ? "Saving..." : saved ? "Saved ✓" : "Save Avatar"}
|
||||
{saving ? "Saving..." : saved ? "Saved" : "Save Avatar"}
|
||||
</Button>
|
||||
</div>
|
||||
</CardContent>
|
||||
|
|
@ -545,7 +572,7 @@ function DisplaySection({ preferences, updatePreferences }: any) {
|
|||
}
|
||||
|
||||
// Notifications Section
|
||||
function NotificationsSection() {
|
||||
function NotificationsSection({ preferences, updatePreferences }: any) {
|
||||
return (
|
||||
<Card id="notifications">
|
||||
<CardHeader>
|
||||
|
|
@ -554,46 +581,33 @@ function NotificationsSection() {
|
|||
Notifications
|
||||
</CardTitle>
|
||||
<CardDescription>
|
||||
Choose what updates you receive
|
||||
Choose what email notifications you receive
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-6">
|
||||
<SettingRow
|
||||
label="Comment Replies"
|
||||
description="Get notified when someone replies to your comment"
|
||||
checked={true}
|
||||
onChange={() => { }}
|
||||
comingSoon
|
||||
description="Get an email when someone replies to your review or comment"
|
||||
checked={preferences.email_on_reply ?? true}
|
||||
onChange={(checked: boolean) => updatePreferences({ email_on_reply: checked })}
|
||||
/>
|
||||
|
||||
<Separator />
|
||||
|
||||
<SettingRow
|
||||
label="New Show Added"
|
||||
description="Get notified when a new show is added to the archive"
|
||||
checked={true}
|
||||
onChange={() => { }}
|
||||
comingSoon
|
||||
/>
|
||||
|
||||
<Separator />
|
||||
|
||||
<SettingRow
|
||||
label="Chase Song Played"
|
||||
description="Get notified when a song on your chase list gets played"
|
||||
checked={true}
|
||||
onChange={() => { }}
|
||||
comingSoon
|
||||
label="Chase Song Alerts"
|
||||
description="Get an email when a song on your chase list gets played at a show"
|
||||
checked={preferences.email_on_chase ?? true}
|
||||
onChange={(checked: boolean) => updatePreferences({ email_on_chase: checked })}
|
||||
/>
|
||||
|
||||
<Separator />
|
||||
|
||||
<SettingRow
|
||||
label="Weekly Digest"
|
||||
description="Receive a weekly email with site highlights"
|
||||
checked={false}
|
||||
onChange={() => { }}
|
||||
comingSoon
|
||||
description="Receive a weekly email with site highlights and new shows"
|
||||
checked={preferences.email_digest ?? false}
|
||||
onChange={(checked: boolean) => updatePreferences({ email_digest: checked })}
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
|
|
|||
|
|
@ -7,6 +7,10 @@ interface Preferences {
|
|||
wiki_mode: boolean
|
||||
show_ratings: boolean
|
||||
show_comments: boolean
|
||||
theme: string
|
||||
email_on_reply: boolean
|
||||
email_on_chase: boolean
|
||||
email_digest: boolean
|
||||
}
|
||||
|
||||
interface PreferencesContextType {
|
||||
|
|
@ -19,6 +23,10 @@ const defaultPreferences: Preferences = {
|
|||
wiki_mode: false,
|
||||
show_ratings: true,
|
||||
show_comments: true,
|
||||
theme: "system",
|
||||
email_on_reply: true,
|
||||
email_on_chase: true,
|
||||
email_digest: false,
|
||||
}
|
||||
|
||||
const PreferencesContext = createContext<PreferencesContextType>({
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue