useAchievements
Get all available achievements.Copy
import { useAchievements } from '@zkscore/react';
function AchievementsList() {
const { achievements, loading, error } = useAchievements();
if (loading) return <div>Loading achievements...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<div className="achievements-grid">
{achievements?.map((achievement) => (
<div key={achievement.id} className="achievement-card">
<img src={achievement.icon} alt={achievement.name} />
<h3>{achievement.name}</h3>
<p>{achievement.description}</p>
<span className="points">+{achievement.points} points</span>
</div>
))}
</div>
);
}
useUserAchievements
Get achievements earned by a user.Copy
import { useUserAchievements } from '@zkscore/react';
function UserAchievements({ address }: { address: string }) {
const { achievements, loading } = useUserAchievements(address);
if (loading) return <div>Loading...</div>;
return (
<div className="user-achievements">
<h2>Achievements ({achievements?.length || 0})</h2>
<div className="grid">
{achievements?.map((achievement) => (
<div key={achievement.id} className="earned-achievement">
<img src={achievement.badge} alt={achievement.name} />
<h4>{achievement.name}</h4>
<time>{new Date(achievement.earnedAt).toLocaleDateString()}</time>
</div>
))}
</div>
</div>
);
}
useAchievementProgress
Track progress towards an achievement.Copy
import { useAchievementProgress } from '@zkscore/react';
function AchievementProgress({ address, achievementId }: Props) {
const { progress, loading } = useAchievementProgress(address, achievementId);
if (loading) return <div>Loading progress...</div>;
if (!progress) return null;
return (
<div className="progress-card">
<h3>{progress.achievement.name}</h3>
<div className="progress-bar">
<div
className="fill"
style={{ width: `${progress.percentage}%` }}
/>
</div>
<p>{progress.percentage.toFixed(0)}% complete</p>
<div className="requirements">
{Object.entries(progress.requirements).map(([key, req]) => (
<div key={key} className="requirement">
<span>{req.name}</span>
<span>{req.current} / {req.required}</span>
</div>
))}
</div>
</div>
);
}
useClaimAchievement
Claim an earned achievement.Copy
import { useClaimAchievement } from '@zkscore/react';
function ClaimButton({ achievementId }: { achievementId: string }) {
const { mutate, loading, error, data } = useClaimAchievement({
onSuccess: (achievement) => {
console.log('Achievement claimed:', achievement);
},
});
return (
<div>
<button
onClick={() => mutate({ achievementId })}
disabled={loading}
>
{loading ? 'Claiming...' : 'Claim Achievement'}
</button>
{error && <p className="error">{error.message}</p>}
{data && <p className="success">Achievement claimed!</p>}
</div>
);
}
Advanced Examples
Achievement Showcase
Copy
import { useUserAchievements, useAchievements } from '@zkscore/react';
function AchievementShowcase({ address }: { address: string }) {
const { achievements: all } = useAchievements();
const { achievements: earned } = useUserAchievements(address);
const earnedIds = new Set(earned?.map(a => a.id) || []);
return (
<div className="showcase">
<div className="stats">
<h3>{earned?.length || 0} / {all?.length || 0}</h3>
<p>Achievements Unlocked</p>
</div>
<div className="grid">
{all?.map((achievement) => {
const isEarned = earnedIds.has(achievement.id);
return (
<div
key={achievement.id}
className={`achievement ${isEarned ? 'earned' : 'locked'}`}
>
<img
src={isEarned ? achievement.badge : achievement.lockedIcon}
alt={achievement.name}
/>
<h4>{achievement.name}</h4>
{isEarned && <span className="check">✓</span>}
</div>
);
})}
</div>
</div>
);
}
Progress Tracker
Copy
import { useAchievementProgress } from '@zkscore/react';
function ProgressTracker({ address }: { address: string }) {
const achievementIds = ['defi-expert', 'nft-collector', 'trading-pro'];
return (
<div className="progress-tracker">
<h2>Your Progress</h2>
{achievementIds.map((id) => (
<AchievementProgressItem key={id} address={address} achievementId={id} />
))}
</div>
);
}
function AchievementProgressItem({ address, achievementId }: Props) {
const { progress } = useAchievementProgress(address, achievementId);
if (!progress || progress.completed) return null;
return (
<div className="progress-item">
<div className="header">
<h4>{progress.achievement.name}</h4>
<span>{progress.percentage.toFixed(0)}%</span>
</div>
<progress value={progress.percentage} max={100} />
</div>
);
}
Auto-Claim System
Copy
import { useUserAchievements, useClaimAchievement } from '@zkscore/react';
import { useEffect } from 'react';
function AutoClaim({ address }: { address: string }) {
const { achievements } = useUserAchievements(address);
const { mutate: claim } = useClaimAchievement();
useEffect(() => {
const unclaimedAchievements = achievements?.filter(
(a) => a.eligible && !a.claimed
);
unclaimedAchievements?.forEach((achievement) => {
claim({ achievementId: achievement.id });
});
}, [achievements]);
return null; // Silent auto-claim component
}
Achievement Notifications
Copy
import { useUserAchievements } from '@zkscore/react';
import { useEffect, useState } from 'react';
function AchievementNotifications({ address }: { address: string }) {
const { achievements } = useUserAchievements(address);
const [notification, setNotification] = useState<any>(null);
const [prevCount, setPrevCount] = useState(0);
useEffect(() => {
if (achievements && achievements.length > prevCount) {
const newAchievement = achievements[0]; // Most recent
setNotification(newAchievement);
setTimeout(() => setNotification(null), 5000);
}
setPrevCount(achievements?.length || 0);
}, [achievements]);
if (!notification) return null;
return (
<div className="achievement-notification">
<h3>🎉 Achievement Unlocked!</h3>
<p>{notification.name}</p>
<img src={notification.badge} alt={notification.name} />
</div>
);
}
TypeScript Types
Copy
interface Achievement {
id: string;
name: string;
description: string;
icon: string;
badge: string;
lockedIcon: string;
points: number;
rarity: 'common' | 'rare' | 'epic' | 'legendary';
category: string;
}
interface UserAchievement extends Achievement {
earnedAt: string;
claimed: boolean;
eligible: boolean;
}
interface AchievementProgress {
achievementId: string;
achievement: Achievement;
completed: boolean;
percentage: number;
requirements: Record<string, {
name: string;
current: number;
required: number;
completed: boolean;
}>;
}