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