115 lines
4.6 KiB
Vue
115 lines
4.6 KiB
Vue
<template>
|
|
<div class="flex flex-col gap-4 h-full">
|
|
|
|
<!-- Header Stats -->
|
|
<div class="flex justify-between items-center bg-[#231533] p-3 border-2 border-[#4a3b5e]">
|
|
<div class="flex gap-4">
|
|
<span class="text-[#99e550] tracking-widest uppercase">Unlocked: {{ unlocked }}/{{ total }}</span>
|
|
</div>
|
|
<div class="w-1/3 flex items-center gap-2">
|
|
<span class="text-[#99e550] tracking-widest uppercase text-sm whitespace-nowrap">Progress: {{ percentage }}%</span>
|
|
<RetroProgressBar :progress="percentage" color="#99e550" />
|
|
</div>
|
|
</div>
|
|
|
|
<div class="bg-[#0f0816] p-2 border-l-4 border-[#99e550] mb-2">
|
|
<h3 class="text-[#9fd75b] text-lg font-bold flex items-center gap-2">
|
|
<span class="text-xl">▼</span> ACHIEVEMENT LIST ({{ unlocked }}/{{ total }})
|
|
</h3>
|
|
</div>
|
|
|
|
<!-- Grid Layout for Achievements -->
|
|
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-3 overflow-y-auto pb-4">
|
|
<div
|
|
v-for="achievement in achievements"
|
|
:key="achievement.id"
|
|
class="relative border-2 p-3 flex flex-col gap-2 min-h-[140px] transition-all group hover:bg-[#231533]"
|
|
:style="{ borderColor: achievement.unlocked ? '#99e550' : '#4a3b5e', backgroundColor: achievement.unlocked ? '#150c1f' : '#0f0816' }"
|
|
>
|
|
<!-- Header: Icon + Title -->
|
|
<div class="flex items-start gap-3">
|
|
<div class="p-2 rounded-sm border-2" :class="achievement.unlocked ? 'border-[#99e550] bg-[#4b692f]/20' : 'border-[#4a3b5e] bg-[#2b193f]'">
|
|
<component :is="ICON_MAP[achievement.icon] || Trophy" :size="24" :color="achievement.unlocked ? achievement.color || '#ffe762' : '#8f80a0'" />
|
|
</div>
|
|
<div class="flex flex-col">
|
|
<h4 class="font-bold tracking-wide leading-none mb-1" :class="achievement.unlocked ? 'text-[#2ce8f4]' : 'text-[#8f80a0]'">
|
|
{{ achievement.title }}
|
|
</h4>
|
|
<span v-if="achievement.unlocked" class="text-[10px] text-[#99e550] uppercase tracking-widest">Completed</span>
|
|
<span v-else class="text-[10px] text-[#8f80a0] uppercase tracking-widest flex items-center gap-1">
|
|
<Lock :size="10" /> Locked
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Description -->
|
|
<p class="text-xs text-[#e0d8f0] flex-grow leading-tight">
|
|
{{ achievement.description }}
|
|
</p>
|
|
|
|
<!-- Reward Section (if exists) -->
|
|
<div v-if="achievement.reward" class="text-[10px] text-[#99e550]">
|
|
<span class="text-[#99e550] opacity-70">Reward: </span>
|
|
{{ achievement.reward }}
|
|
</div>
|
|
|
|
<!-- Progress Bar (if incomplete) -->
|
|
<div v-if="!achievement.unlocked && achievement.maxValue" class="mt-auto">
|
|
<div class="flex justify-between text-[9px] text-[#8f80a0] mb-0.5">
|
|
<span>{{ achievement.currentValue }} / {{ achievement.maxValue }}</span>
|
|
<span>{{ achievement.progress }}%</span>
|
|
</div>
|
|
<div class="h-1 bg-[#2b193f] w-full">
|
|
<div class="h-full bg-[#4a3b5e]" :style="{ width: `${achievement.progress}%` }" />
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Decorative corner if unlocked -->
|
|
<div v-if="achievement.unlocked" class="absolute top-0 right-0 w-4 h-4 overflow-hidden">
|
|
<div class="absolute top-0 right-0 w-2 h-2 bg-[#99e550]" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { computed } from 'vue';
|
|
import { CheckCircle2, Lock, Trophy, Baby, CalendarDays, Egg, Sprout, Cake, Star, Diamond, Milk, Utensils, Gamepad2, Sparkles, BookOpen, Search, Leaf, Dumbbell, Brush, Pill } from 'lucide-vue-next';
|
|
import PixelFrame from './PixelFrame.vue';
|
|
import RetroProgressBar from './RetroProgressBar.vue';
|
|
|
|
type Achievement = any;
|
|
|
|
interface Props {
|
|
achievements: Achievement[];
|
|
}
|
|
|
|
const props = defineProps<Props>();
|
|
|
|
const ICON_MAP: Record<string, any> = {
|
|
baby: Baby,
|
|
calendar: CalendarDays,
|
|
egg: Egg,
|
|
sprout: Sprout,
|
|
cake: Cake,
|
|
star: Star,
|
|
diamond: Diamond,
|
|
milk: Milk,
|
|
utensils: Utensils,
|
|
gamepad: Gamepad2,
|
|
sparkles: Sparkles,
|
|
book: BookOpen,
|
|
search: Search,
|
|
leaf: Leaf,
|
|
dumbbell: Dumbbell,
|
|
brush: Brush,
|
|
pill: Pill,
|
|
trophy: Trophy,
|
|
};
|
|
|
|
const total = computed(() => props.achievements.length);
|
|
const unlocked = computed(() => props.achievements.filter(a => a.unlocked).length);
|
|
const percentage = computed(() => Math.round((unlocked.value / total.value) * 100));
|
|
</script>
|