diff --git a/FrontEnd/my-app/app/[locale]/quests/[id]/page.tsx b/FrontEnd/my-app/app/[locale]/quests/[id]/page.tsx
index a8d6c9b48..6173cc197 100644
--- a/FrontEnd/my-app/app/[locale]/quests/[id]/page.tsx
+++ b/FrontEnd/my-app/app/[locale]/quests/[id]/page.tsx
@@ -32,7 +32,7 @@ export default function QuestDetailPage() {
setIsLoading(true);
setError(null);
const data = await getQuestById(questId);
- setQuest(data as unknown as Quest);
+ setQuest(data);
trackEvent(ANALYTICS_EVENTS.QUEST_VIEW, { questId });
} catch (err) {
setError(err instanceof Error ? err : new Error('Failed to fetch quest'));
diff --git a/FrontEnd/my-app/components/homepage/FeaturedQuests.test.tsx b/FrontEnd/my-app/components/homepage/FeaturedQuests.test.tsx
index 90be10f48..957f06d36 100644
--- a/FrontEnd/my-app/components/homepage/FeaturedQuests.test.tsx
+++ b/FrontEnd/my-app/components/homepage/FeaturedQuests.test.tsx
@@ -48,7 +48,7 @@ describe('FeaturedQuests - Error Boundary and Timeout Integration', () => {
const mockError = new Error('API request failed');
mockGetQuests
.mockRejectedValueOnce(mockError)
- .mockResolvedValueOnce({ quests: [] });
+ .mockResolvedValueOnce({ data: [] });
render();
@@ -96,7 +96,7 @@ describe('FeaturedQuests - Error Boundary and Timeout Integration', () => {
);
mockGetQuests
.mockRejectedValueOnce(timeoutError)
- .mockResolvedValueOnce({ quests: [] });
+ .mockResolvedValueOnce({ data: [] });
render();
@@ -130,7 +130,7 @@ describe('FeaturedQuests - Error Boundary and Timeout Integration', () => {
);
mockGetQuests
.mockRejectedValueOnce(timeoutError)
- .mockResolvedValueOnce({ quests: [] });
+ .mockResolvedValueOnce({ data: [] });
render();
diff --git a/FrontEnd/my-app/components/homepage/FeaturedQuests.tsx b/FrontEnd/my-app/components/homepage/FeaturedQuests.tsx
index 96bcf2bda..81099e835 100644
--- a/FrontEnd/my-app/components/homepage/FeaturedQuests.tsx
+++ b/FrontEnd/my-app/components/homepage/FeaturedQuests.tsx
@@ -56,14 +56,11 @@ function FeaturedQuestsContent() {
customTimeout,
{
onRevalidate: (fresh) => {
- const freshItems =
- (fresh as any).data ?? (fresh as any).quests ?? [];
- setQuests(freshItems);
+ setQuests(fresh.data);
},
}
);
- const items = (res as any).data ?? (res as any).quests ?? [];
- setQuests(items);
+ setQuests(res.data);
setRetryCount(0); // Reset retry count on success
} catch (err) {
// Don't show error if request was cancelled (e.g., component unmounting)
diff --git a/FrontEnd/my-app/components/quest/QuestHeader.tsx b/FrontEnd/my-app/components/quest/QuestHeader.tsx
index a0952c3be..19df481fd 100644
--- a/FrontEnd/my-app/components/quest/QuestHeader.tsx
+++ b/FrontEnd/my-app/components/quest/QuestHeader.tsx
@@ -130,7 +130,7 @@ export function QuestHeader({
>
{category}
- {difficulty && difficultyInfo && (
+ {difficultyInfo && (
model.
+ */
+export function mapPaginatedQuests(dto: any): PaginatedResponse {
+ if (!dto) {
+ return {
+ data: [],
+ total: 0,
+ page: 1,
+ limit: 12,
+ totalPages: 0,
+ };
+ }
+
+ // Support both backend format (`quests`) and legacy mock server format (`data`)
+ const questsRaw = dto.quests || dto.data || [];
+ const data = Array.isArray(questsRaw) ? questsRaw.map(mapQuest) : [];
+
+ const meta = dto.meta || {};
+ const page = dto.page ?? meta.page ?? 1;
+ const limit = dto.limit ?? meta.limit ?? 12;
+ const total = dto.total ?? meta.total ?? data.length;
+ const totalPages = dto.totalPages ?? meta.totalPages ?? Math.ceil(total / limit);
+
+ return {
+ data,
+ total,
+ page,
+ limit,
+ totalPages,
+ };
+}
+
+/**
+ * Maps a raw UserResponse to a UI UserProfile domain model.
+ */
+export function mapUserProfile(
+ dto: UserResponse,
+ isOwnProfile = false,
+ isFollowing = false
+): UserProfile {
+ return {
+ id: dto.id || '',
+ username: dto.username || '',
+ stellarAddress: dto.stellarAddress || '',
+ avatar: dto.avatarUrl || '',
+ bio: dto.bio || '',
+ level: dto.level || 1,
+ xp: dto.xp || 0,
+ totalEarnings: parseFloat(dto.totalEarned || '0'),
+ questsCompleted: dto.questsCompleted || 0,
+ currentStreak: 0, // default, not in UserResponse
+ joinDate: dto.createdAt || new Date().toISOString(),
+ lastActive: dto.lastActiveAt || dto.updatedAt || new Date().toISOString(),
+ isFollowing: isOwnProfile ? false : isFollowing,
+ followersCount: 0, // default
+ followingCount: 0, // default
+ isOwnProfile,
+ };
+}
+
+/**
+ * Maps a raw UserStatsResponse to a UI UserStats domain model.
+ */
+export function mapUserStats(dto: UserStatsResponse): UserStatsResponse {
+ return {
+ xp: dto.xp || 0,
+ level: dto.level || 1,
+ totalEarned: String(dto.totalEarned || '0'),
+ questsCompleted: dto.questsCompleted || 0,
+ failedQuests: dto.failedQuests || 0,
+ successRate: dto.successRate || 0,
+ badges: dto.badges || [],
+ lastActiveAt: dto.lastActiveAt,
+ };
+}
+
+/**
+ * Maps a raw SubmissionResponse to a UI Submission domain model.
+ */
+export function mapSubmission(dto: SubmissionResponse): Submission {
+ return {
+ id: dto.id || '',
+ questId: dto.questId || '',
+ userId: dto.userId || '',
+ status: dto.status as ApiSubmissionStatus,
+ proof: dto.proof || {},
+ rejectionReason: dto.rejectionReason,
+ approvedAt: dto.approvedAt,
+ approvedBy: dto.approvedBy,
+ rejectedAt: dto.rejectedAt,
+ rejectedBy: dto.rejectedBy,
+ createdAt: dto.createdAt || new Date().toISOString(),
+ updatedAt: dto.updatedAt || new Date().toISOString(),
+ quest: dto.quest ? {
+ id: dto.quest.id,
+ title: dto.quest.title,
+ rewardAmount: dto.quest.rewardAmount,
+ rewardAsset: dto.quest.rewardAsset,
+ } : undefined,
+ };
+}
+
+/**
+ * Helper to map a Badge ID or registry entry to a full Badge.
+ */
+export function mapBadgeIdToBadge(id: string): Badge {
+ const registry: Record> = {
+ 'badge-1': {
+ name: 'Fast Finisher',
+ description: 'Completed 10 quests before deadline.',
+ icon: 'bolt',
+ rarity: 'rare',
+ },
+ 'badge-2': {
+ name: 'Code Guardian',
+ description: 'Delivered multiple high-quality review submissions.',
+ icon: 'shield',
+ rarity: 'epic',
+ },
+ };
+
+ const registered = registry[id] || {
+ name: id
+ .split('-')
+ .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
+ .join(' '),
+ description: 'Earned achievement badge.',
+ icon: 'award',
+ rarity: 'common',
+ };
+
+ return {
+ id,
+ ...registered,
+ earnedAt: new Date().toISOString(),
+ };
+}
diff --git a/FrontEnd/my-app/lib/api/quests.integration.test.ts b/FrontEnd/my-app/lib/api/quests.integration.test.ts
index c3268bc81..951f572c1 100644
--- a/FrontEnd/my-app/lib/api/quests.integration.test.ts
+++ b/FrontEnd/my-app/lib/api/quests.integration.test.ts
@@ -37,23 +37,22 @@ afterEach(() => {
describe('Quests API Integration Tests', () => {
it('should fetch quests successfully with mock data', async () => {
- const response = (await getQuests()) as any;
+ const response = await getQuests();
expect(response).toBeDefined();
expect(response.data).toHaveLength(1);
expect(response.data[0].id).toBe('quest-1');
expect(response.data[0].title).toBe('Test Quest 1');
- expect(response.meta.total).toBe(1);
+ expect(response.total).toBe(1);
});
it('should fetch a single quest by ID successfully', async () => {
- const response = (await getQuestById('quest-123')) as any;
+ const response = await getQuestById('quest-123');
expect(response).toBeDefined();
- expect(response.data).toBeDefined();
- expect(response.data.id).toBe('quest-123');
- expect(response.data.title).toBe('Test Quest quest-123');
- expect(response.data.rewardAmount).toBe(50);
+ expect(response.id).toBe('quest-123');
+ expect(response.title).toBe('Test Quest quest-123');
+ expect(response.rewardAmount).toBe(50);
});
it('should reuse fresh cached quest listings without another request', async () => {
@@ -65,8 +64,8 @@ describe('Quests API Integration Tests', () => {
})
);
- const first = (await getQuests({ limit: 10 })) as any;
- const second = (await getQuests({ limit: 10 })) as any;
+ const first = await getQuests({ limit: 10 });
+ const second = await getQuests({ limit: 10 });
expect(requestCount).toBe(1);
expect(first.data[0].id).toBe('quest-1');
@@ -85,11 +84,11 @@ describe('Quests API Integration Tests', () => {
})
);
- const fresh = (await getQuests()) as any;
+ const fresh = await getQuests();
now = 3 * 60 * 1000 + 1;
- const stale = (await getQuests(undefined, undefined, undefined, {
+ const stale = await getQuests(undefined, undefined, undefined, {
onRevalidate,
- })) as any;
+ });
expect(stale.data[0].id).toBe(fresh.data[0].id);
expect(stale.data[0].id).toBe('quest-1');
@@ -115,9 +114,9 @@ describe('Quests API Integration Tests', () => {
})
);
- const first = (await getQuests()) as any;
+ const first = await getQuests();
now = 13 * 60 * 1000 + 1;
- const second = (await getQuests()) as any;
+ const second = await getQuests();
expect(requestCount).toBe(2);
expect(first.data[0].id).toBe('quest-1');
diff --git a/FrontEnd/my-app/lib/api/quests.serialization.test.ts b/FrontEnd/my-app/lib/api/quests.serialization.test.ts
index 5fca503dd..0019a9442 100644
--- a/FrontEnd/my-app/lib/api/quests.serialization.test.ts
+++ b/FrontEnd/my-app/lib/api/quests.serialization.test.ts
@@ -510,8 +510,8 @@ describe('getQuests – integration via MSW fixture', () => {
const result = await getQuests();
- expect(Array.isArray(result.quests)).toBe(true);
- expect(result.quests).toHaveLength(questPaginatedFixture.quests.length);
+ expect(Array.isArray(result.data)).toBe(true);
+ expect(result.data).toHaveLength(questPaginatedFixture.quests.length);
expect(result.total).toBe(questPaginatedFixture.total);
expect(result.page).toBe(questPaginatedFixture.page);
expect(result.limit).toBe(questPaginatedFixture.limit);
@@ -527,7 +527,7 @@ describe('getQuests – integration via MSW fixture', () => {
const result = await getQuests();
- result.quests.forEach((quest) => {
+ result.data.forEach((quest) => {
assertRequiredQuestFields(quest);
});
});
@@ -541,7 +541,7 @@ describe('getQuests – integration via MSW fixture', () => {
const result = await getQuests();
- expect(result.quests).toHaveLength(0);
+ expect(result.data).toHaveLength(0);
expect(result.total).toBe(0);
});
diff --git a/FrontEnd/my-app/lib/api/quests.ts b/FrontEnd/my-app/lib/api/quests.ts
index 2f6c64981..94b0f3d3a 100644
--- a/FrontEnd/my-app/lib/api/quests.ts
+++ b/FrontEnd/my-app/lib/api/quests.ts
@@ -20,18 +20,18 @@ import {
} from './client';
import { cacheManager } from '@/lib/utils/cache';
import type {
- QuestResponse,
- PaginatedQuestsResponse,
CreateQuestRequest,
UpdateQuestRequest,
QuestQueryParams,
} from '@/lib/types/api.types';
+import type { Quest, PaginatedResponse } from '@/lib/types/quest';
+import { mapQuest, mapPaginatedQuests } from './mappers';
const QUEST_LIST_TTL_MS = 3 * 60 * 1000;
const QUEST_LIST_STALE_TTL_MS = 10 * 60 * 1000;
type QuestListCacheOptions = {
- onRevalidate?: (data: PaginatedQuestsResponse) => void;
+ onRevalidate?: (data: PaginatedResponse) => void;
};
// Re-export legacy types for backward compatibility with existing hooks
@@ -56,20 +56,22 @@ export async function getQuests(
cancelToken?: CancelToken,
timeout?: number,
cacheOptions?: QuestListCacheOptions
-): Promise {
+): Promise> {
const params = buildQuestParams(filters);
const cacheKey = `${generateQuestsCacheKey(params)}${timeout ? `:t-${timeout}` : ''}`;
return cacheManager.getStaleWhileRevalidate(
cacheKey,
- () =>
- withRetry(() =>
- get('/quests', {
+ async () => {
+ const raw = await withRetry(() =>
+ get('/quests', {
params,
signal: cancelToken?.signal,
timeout,
})
- ),
+ );
+ return mapPaginatedQuests(raw);
+ },
{
ttl: QUEST_LIST_TTL_MS,
staleTtl: QUEST_LIST_STALE_TTL_MS,
@@ -89,15 +91,17 @@ export async function getQuests(
export async function getQuestById(
id: string,
cancelToken?: CancelToken
-): Promise {
+): Promise {
return cacheManager.get(
`quest-${id}`,
- () =>
- withRetry(() =>
- get(`/quests/${id}`, {
+ async () => {
+ const raw = await withRetry(() =>
+ get(`/quests/${id}`, {
signal: cancelToken?.signal,
})
- ),
+ );
+ return mapQuest(raw);
+ },
60_000
);
}
@@ -108,11 +112,11 @@ export async function getQuestById(
export async function createQuest(
payload: CreateQuestRequest
-): Promise {
- const result = await post('/quests', payload);
+): Promise {
+ const result = await post('/quests', payload);
// Invalidate list cache (no simple key, so just clear all quest entries)
cacheManager.clear();
- return result;
+ return mapQuest(result);
}
// ---------------------------------------------------------------------------
@@ -122,10 +126,10 @@ export async function createQuest(
export async function updateQuest(
id: string,
payload: UpdateQuestRequest
-): Promise {
- const result = await patch(`/quests/${id}`, payload);
+): Promise {
+ const result = await patch(`/quests/${id}`, payload);
cacheManager.invalidate(`quest-${id}`);
- return result;
+ return mapQuest(result);
}
// ---------------------------------------------------------------------------
diff --git a/FrontEnd/my-app/lib/api/user.ts b/FrontEnd/my-app/lib/api/user.ts
index e41cedfc8..f504de738 100644
--- a/FrontEnd/my-app/lib/api/user.ts
+++ b/FrontEnd/my-app/lib/api/user.ts
@@ -30,6 +30,7 @@ import type {
} from '@/lib/types/api.types';
import type { QuestResponse, SubmissionResponse } from '@/lib/types/api.types';
+import { mapQuest, mapSubmission, mapUserStats, mapBadgeIdToBadge } from './mappers';
// Re-export legacy dashboard types for backward compat
export type {
@@ -211,11 +212,12 @@ export async function fetchUserStats(
address: string,
cancelToken?: CancelToken
): Promise {
- return withRetry(() =>
+ const raw = await withRetry(() =>
get(`/users/${address}/stats`, {
signal: cancelToken?.signal,
})
);
+ return mapUserStats(raw);
}
// ---------------------------------------------------------------------------
@@ -323,15 +325,30 @@ export async function fetchBadges(): Promise {
*/
export async function fetchDashboardData(
address?: string
-): Promise<
- DashboardData | { userProfile: UserResponse; userStats: UserStatsResponse }
-> {
+): Promise {
if (address) {
- const [userProfile, userStats] = await Promise.all([
- fetchUserByAddress(address),
- fetchUserStats(address),
- ]);
- return { userProfile, userStats };
+ const [userStats, activeQuests, recentSubmissions, earningsHistory, badges] =
+ await Promise.all([
+ fetchUserStats(address),
+ fetchActiveQuests(),
+ fetchRecentSubmissions(),
+ fetchEarningsHistory(),
+ fetchBadges(),
+ ]);
+
+ const stats = mapUserStats(userStats);
+
+ const mappedBadges = stats.badges && stats.badges.length > 0
+ ? stats.badges.map(mapBadgeIdToBadge)
+ : badges;
+
+ return {
+ stats,
+ activeQuests: activeQuests.map(mapQuest),
+ recentSubmissions: recentSubmissions.map(mapSubmission),
+ earningsHistory,
+ badges: mappedBadges,
+ };
}
const [activeQuests, recentSubmissions, earningsHistory, badges] =
@@ -343,9 +360,9 @@ export async function fetchDashboardData(
]);
return {
- stats: mockUserStats,
- activeQuests,
- recentSubmissions,
+ stats: mapUserStats(mockUserStats),
+ activeQuests: activeQuests.map(mapQuest),
+ recentSubmissions: recentSubmissions.map(mapSubmission),
earningsHistory,
badges,
};
diff --git a/FrontEnd/my-app/lib/hooks/useAdmin.ts b/FrontEnd/my-app/lib/hooks/useAdmin.ts
index 8b1bd94d7..68fda3ee7 100644
--- a/FrontEnd/my-app/lib/hooks/useAdmin.ts
+++ b/FrontEnd/my-app/lib/hooks/useAdmin.ts
@@ -8,11 +8,11 @@ import {
useContext,
} from 'react';
import type {
- Quest,
+ AdminQuest as Quest,
AdminStats,
AdminUser,
QuestFormData,
- QuestStatus,
+ AdminQuestStatus as QuestStatus,
BulkOperation,
Notification,
} from '../types/admin';
diff --git a/FrontEnd/my-app/lib/hooks/useQuests.ts b/FrontEnd/my-app/lib/hooks/useQuests.ts
index ce9af3c76..e213ef9eb 100644
--- a/FrontEnd/my-app/lib/hooks/useQuests.ts
+++ b/FrontEnd/my-app/lib/hooks/useQuests.ts
@@ -48,7 +48,7 @@ export function useQuests(
...memoizedFilters,
...memoizedPagination,
});
- setQuests(response.quests as any);
+ setQuests(response.data);
setPagination({
page: response.page ?? 1,
limit: response.limit ?? 12,
diff --git a/FrontEnd/my-app/lib/hooks/useUserStats.ts b/FrontEnd/my-app/lib/hooks/useUserStats.ts
index b0d436810..96772d962 100644
--- a/FrontEnd/my-app/lib/hooks/useUserStats.ts
+++ b/FrontEnd/my-app/lib/hooks/useUserStats.ts
@@ -50,9 +50,9 @@ export function useUserStats(): UseUserStatsReturn {
setError(null);
try {
- const data = (await fetchDashboardData(
+ const data = await fetchDashboardData(
user.stellarAddress
- )) as DashboardData;
+ );
setStats(data.stats);
setActiveQuests(data.activeQuests);
setRecentSubmissions(data.recentSubmissions);
@@ -96,7 +96,7 @@ export function useStats() {
return;
}
fetchUserStats(user.stellarAddress)
- .then((data) => setStats(data as any))
+ .then((data) => setStats(data))
.catch((err) => setError(err.message))
.finally(() => setIsLoading(false));
}, [user?.stellarAddress]);
diff --git a/FrontEnd/my-app/lib/types/admin.ts b/FrontEnd/my-app/lib/types/admin.ts
index ef8b22464..bacef0156 100644
--- a/FrontEnd/my-app/lib/types/admin.ts
+++ b/FrontEnd/my-app/lib/types/admin.ts
@@ -1,10 +1,10 @@
-export type QuestStatus =
+export type AdminQuestStatus =
| 'draft'
| 'active'
| 'paused'
| 'completed'
| 'cancelled';
-export type QuestDifficulty =
+export type AdminQuestDifficulty =
| 'beginner'
| 'intermediate'
| 'advanced'
@@ -17,14 +17,14 @@ export type QuestCategory =
| 'Testing'
| 'Community';
-export interface Quest {
+export interface AdminQuest {
id: string;
title: string;
description: string;
shortDescription: string;
category: QuestCategory;
- difficulty?: QuestDifficulty;
- status: QuestStatus;
+ difficulty?: AdminQuestDifficulty;
+ status: AdminQuestStatus;
reward: number;
xpReward: number;
deadline: string;
@@ -42,7 +42,7 @@ export interface QuestFormData {
description: string;
shortDescription: string;
category: QuestCategory;
- difficulty: QuestDifficulty;
+ difficulty: AdminQuestDifficulty;
reward: number;
xpReward: number;
deadline: string;
@@ -82,3 +82,8 @@ export interface Notification {
message: string;
duration?: number;
}
+
+// Backward compatibility exports
+export type QuestStatus = AdminQuestStatus;
+export type QuestDifficulty = AdminQuestDifficulty;
+export type Quest = AdminQuest;
diff --git a/FrontEnd/my-app/lib/types/dashboard.ts b/FrontEnd/my-app/lib/types/dashboard.ts
index facbaeb3b..06bce7148 100644
--- a/FrontEnd/my-app/lib/types/dashboard.ts
+++ b/FrontEnd/my-app/lib/types/dashboard.ts
@@ -1,12 +1,9 @@
-import type {
- QuestResponse,
- SubmissionResponse,
- UserStatsResponse,
-} from './api.types';
+import type { UserStatsResponse } from './api.types';
+import type { Quest } from './quest';
+import type { Submission } from './submission';
+export type { Quest, Submission };
export type UserStats = UserStatsResponse;
-export type Quest = QuestResponse;
-export type Submission = SubmissionResponse;
export interface EarningsData {
date: string;
diff --git a/FrontEnd/my-app/package-lock.json b/FrontEnd/my-app/package-lock.json
index 658671e21..fc996dce6 100644
--- a/FrontEnd/my-app/package-lock.json
+++ b/FrontEnd/my-app/package-lock.json
@@ -21,6 +21,8 @@
"dompurify": "^3.4.1",
"framer-motion": "^12.26.2",
"lucide-react": "^0.562.0",
+ "motion": "^12.42.0",
+ "motion-dom": "^12.42.0",
"next": "15.5.18",
"next-intl": "^3.26.3",
"react": "19.2.5",
@@ -8036,21 +8038,6 @@
"ws": "^7.5.1"
}
},
- "node_modules/@walletconnect/jsonrpc-ws-connection/node_modules/utf-8-validate": {
- "version": "5.0.10",
- "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz",
- "integrity": "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==",
- "hasInstallScript": true,
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "node-gyp-build": "^4.3.0"
- },
- "engines": {
- "node": ">=6.14.2"
- }
- },
"node_modules/@walletconnect/jsonrpc-ws-connection/node_modules/ws": {
"version": "7.5.11",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.11.tgz",
@@ -8173,6 +8160,20 @@
"@types/trusted-types": "^2.0.2"
}
},
+ "node_modules/@walletconnect/modal-ui/node_modules/motion": {
+ "version": "10.16.2",
+ "resolved": "https://registry.npmjs.org/motion/-/motion-10.16.2.tgz",
+ "integrity": "sha512-p+PurYqfUdcJZvtnmAqu5fJgV2kR0uLFQuBKtLeFVTrYEVllI99tiOTSefVNYuip9ELTEkepIIDftNdze76NAQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@motionone/animation": "^10.15.1",
+ "@motionone/dom": "^10.16.2",
+ "@motionone/svelte": "^10.16.2",
+ "@motionone/types": "^10.15.1",
+ "@motionone/utils": "^10.15.1",
+ "@motionone/vue": "^10.16.2"
+ }
+ },
"node_modules/@walletconnect/relay-api": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/@walletconnect/relay-api/-/relay-api-1.0.11.tgz",
@@ -13262,21 +13263,6 @@
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
"license": "MIT"
},
- "node_modules/jayson/node_modules/utf-8-validate": {
- "version": "5.0.10",
- "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz",
- "integrity": "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==",
- "hasInstallScript": true,
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "node-gyp-build": "^4.3.0"
- },
- "engines": {
- "node": ">=6.14.2"
- }
- },
"node_modules/jayson/node_modules/ws": {
"version": "7.5.11",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.11.tgz",
@@ -14253,17 +14239,29 @@
"license": "MIT"
},
"node_modules/motion": {
- "version": "10.16.2",
- "resolved": "https://registry.npmjs.org/motion/-/motion-10.16.2.tgz",
- "integrity": "sha512-p+PurYqfUdcJZvtnmAqu5fJgV2kR0uLFQuBKtLeFVTrYEVllI99tiOTSefVNYuip9ELTEkepIIDftNdze76NAQ==",
+ "version": "12.42.0",
+ "resolved": "https://registry.npmjs.org/motion/-/motion-12.42.0.tgz",
+ "integrity": "sha512-Qhwvu9sVl5/URSq5CNzwMCpSKK8Uhnrwb6VO977kZyj/wOCS7mWebJUnBoHx5cZU1Zv8a9BD5CSICWKAlrLJgA==",
"license": "MIT",
"dependencies": {
- "@motionone/animation": "^10.15.1",
- "@motionone/dom": "^10.16.2",
- "@motionone/svelte": "^10.16.2",
- "@motionone/types": "^10.15.1",
- "@motionone/utils": "^10.15.1",
- "@motionone/vue": "^10.16.2"
+ "framer-motion": "^12.42.0",
+ "tslib": "^2.4.0"
+ },
+ "peerDependencies": {
+ "@emotion/is-prop-valid": "*",
+ "react": "^18.0.0 || ^19.0.0",
+ "react-dom": "^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@emotion/is-prop-valid": {
+ "optional": true
+ },
+ "react": {
+ "optional": true
+ },
+ "react-dom": {
+ "optional": true
+ }
}
},
"node_modules/motion-dom": {
@@ -18645,22 +18643,6 @@
"node": ">= 10"
}
},
- "node_modules/webpack-bundle-analyzer/node_modules/utf-8-validate": {
- "version": "5.0.10",
- "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz",
- "integrity": "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==",
- "dev": true,
- "hasInstallScript": true,
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "node-gyp-build": "^4.3.0"
- },
- "engines": {
- "node": ">=6.14.2"
- }
- },
"node_modules/webpack-bundle-analyzer/node_modules/ws": {
"version": "7.5.11",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.11.tgz",
diff --git a/FrontEnd/my-app/package.json b/FrontEnd/my-app/package.json
index 37acabe30..08ea5c478 100644
--- a/FrontEnd/my-app/package.json
+++ b/FrontEnd/my-app/package.json
@@ -36,6 +36,8 @@
"dompurify": "^3.4.1",
"framer-motion": "^12.26.2",
"lucide-react": "^0.562.0",
+ "motion": "^12.42.0",
+ "motion-dom": "^12.42.0",
"next": "15.5.18",
"next-intl": "^3.26.3",
"react": "19.2.5",
diff --git a/FrontEnd/my-app/vitest.config.ts b/FrontEnd/my-app/vitest.config.ts
index f2cf3c7b9..60488ee3e 100644
--- a/FrontEnd/my-app/vitest.config.ts
+++ b/FrontEnd/my-app/vitest.config.ts
@@ -12,6 +12,11 @@ export default defineConfig({
environment: 'jsdom',
globals: true,
setupFiles: ['./tests/setup.ts'],
+ server: {
+ deps: {
+ inline: ['motion', 'motion-dom'],
+ },
+ },
include: [
'app/**/*.test.{ts,tsx}',
'components/**/*.test.{ts,tsx}',