Add avatar unlock system with XP tiers
This commit is contained in:
parent
a7ce4d17a1
commit
36d6fbfad9
1 changed files with 60 additions and 33 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 />
|
||||
|
|
@ -296,6 +302,7 @@ export default function SettingsPage() {
|
|||
saved={avatarSaved}
|
||||
error={avatarError}
|
||||
onSave={handleSaveAvatar}
|
||||
userXp={(user as any)?.xp || 0}
|
||||
/>
|
||||
</TabsContent>
|
||||
|
||||
|
|
@ -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">
|
||||
<Label>Background Color</Label>
|
||||
<div className="grid grid-cols-6 gap-3">
|
||||
{PRESET_COLORS.map((color) => (
|
||||
<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"
|
||||
style={{ backgroundColor: color.value }}
|
||||
title={color.name}
|
||||
>
|
||||
{avatarBgColor === color.value && (
|
||||
<Check className="absolute inset-0 m-auto h-5 w-5 text-white drop-shadow-lg" />
|
||||
)}
|
||||
</button>
|
||||
))}
|
||||
<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) => {
|
||||
const isLocked = userXp < color.xpRequired
|
||||
return (
|
||||
<button
|
||||
key={color.value}
|
||||
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={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>
|
||||
<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>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue