feat: recreate Prestige tab with insight upgrades, memories, pacts, and loop reset
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m19s
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m19s
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
# Circular Dependencies
|
# Circular Dependencies
|
||||||
Generated: 2026-05-19T13:55:24.489Z
|
Generated: 2026-05-19T16:29:33.501Z
|
||||||
Found: 3 circular chain(s) — these MUST be fixed before modifying involved files.
|
Found: 3 circular chain(s) — these MUST be fixed before modifying involved files.
|
||||||
|
|
||||||
1. Processed 121 files (1.3s) (4 warnings)
|
1. Processed 121 files (1.3s) (4 warnings)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"_meta": {
|
"_meta": {
|
||||||
"generated": "2026-05-19T13:55:23.066Z",
|
"generated": "2026-05-19T16:29:32.045Z",
|
||||||
"description": "Import dependency graph for src/lib/game. Keys are files, values are arrays of files they import.",
|
"description": "Import dependency graph for src/lib/game. Keys are files, values are arrays of files they import.",
|
||||||
"usage": "To find what a file affects, search for its path in the VALUES. To find what a file depends on, look at its KEY entry."
|
"usage": "To find what a file affects, search for its path in the VALUES. To find what a file depends on, look at its KEY entry."
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -111,6 +111,8 @@ Mana-Loop/
|
|||||||
│ │ │ │ ├── DebugTab.test.ts
|
│ │ │ │ ├── DebugTab.test.ts
|
||||||
│ │ │ │ ├── DebugTab.tsx
|
│ │ │ │ ├── DebugTab.tsx
|
||||||
│ │ │ │ ├── DisciplinesTab.tsx
|
│ │ │ │ ├── DisciplinesTab.tsx
|
||||||
|
│ │ │ │ ├── PrestigeTab.test.ts
|
||||||
|
│ │ │ │ ├── PrestigeTab.tsx
|
||||||
│ │ │ │ ├── SpellsTab.tsx
|
│ │ │ │ ├── SpellsTab.tsx
|
||||||
│ │ │ │ ├── StatsTab.tsx
|
│ │ │ │ ├── StatsTab.tsx
|
||||||
│ │ │ │ └── index.ts
|
│ │ │ │ └── index.ts
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ const StatsTab = lazy(() => import('@/components/game/tabs').then(module => ({ d
|
|||||||
const DebugTab = lazy(() => import('@/components/game/tabs').then(module => ({ default: module.DebugTab })));
|
const DebugTab = lazy(() => import('@/components/game/tabs').then(module => ({ default: module.DebugTab })));
|
||||||
const AchievementsTab = lazy(() => import('@/components/game/tabs').then(module => ({ default: module.AchievementsTab })));
|
const AchievementsTab = lazy(() => import('@/components/game/tabs').then(module => ({ default: module.AchievementsTab })));
|
||||||
const AttunementsTab = lazy(() => import('@/components/game/tabs').then(module => ({ default: module.AttunementsTab })));
|
const AttunementsTab = lazy(() => import('@/components/game/tabs').then(module => ({ default: module.AttunementsTab })));
|
||||||
|
const PrestigeTab = lazy(() => import('@/components/game/tabs').then(module => ({ default: module.PrestigeTab })));
|
||||||
|
|
||||||
const TabLoadingFallback = () => <div className="p-4 text-center text-gray-400">Loading...</div>;
|
const TabLoadingFallback = () => <div className="p-4 text-center text-gray-400">Loading...</div>;
|
||||||
|
|
||||||
@@ -239,6 +240,7 @@ export default function ManaLoopGame() {
|
|||||||
<TabsTrigger value="debug" className="text-xs px-2 py-1">🐛 Debug</TabsTrigger>
|
<TabsTrigger value="debug" className="text-xs px-2 py-1">🐛 Debug</TabsTrigger>
|
||||||
<TabsTrigger value="attunements" className="text-xs px-2 py-1">⚗️ Attunements</TabsTrigger>
|
<TabsTrigger value="attunements" className="text-xs px-2 py-1">⚗️ Attunements</TabsTrigger>
|
||||||
<TabsTrigger value="achievements" className="text-xs px-2 py-1">🏆 Achievements</TabsTrigger>
|
<TabsTrigger value="achievements" className="text-xs px-2 py-1">🏆 Achievements</TabsTrigger>
|
||||||
|
<TabsTrigger value="prestige" className="text-xs px-2 py-1">✨ Prestige</TabsTrigger>
|
||||||
</TabsList>
|
</TabsList>
|
||||||
|
|
||||||
<TabsContent value="spells">
|
<TabsContent value="spells">
|
||||||
@@ -292,6 +294,14 @@ export default function ManaLoopGame() {
|
|||||||
</Suspense>
|
</Suspense>
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
||||||
|
<TabsContent value="prestige">
|
||||||
|
<ErrorBoundary fallback={<div className="p-4 text-red-400">prestige tab failed to load.</div>}>
|
||||||
|
<Suspense fallback={<TabLoadingFallback />}>
|
||||||
|
<PrestigeTab />
|
||||||
|
</Suspense>
|
||||||
|
</ErrorBoundary>
|
||||||
|
</TabsContent>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
@@ -0,0 +1,97 @@
|
|||||||
|
import { describe, it, expect } from 'vitest';
|
||||||
|
|
||||||
|
// ─── Test: PrestigeTab barrel export ───────────────────────────────────────────
|
||||||
|
|
||||||
|
describe('PrestigeTab module structure', () => {
|
||||||
|
it('exports PrestigeTab from barrel index', async () => {
|
||||||
|
const mod = await import('./PrestigeTab');
|
||||||
|
expect(mod.PrestigeTab).toBeDefined();
|
||||||
|
expect(typeof mod.PrestigeTab).toBe('function');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('PrestigeTab has correct displayName', async () => {
|
||||||
|
const { PrestigeTab } = await import('./PrestigeTab');
|
||||||
|
expect(PrestigeTab.displayName).toBe('PrestigeTab');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// ─── Test: Barrel export includes PrestigeTab ──────────────────────────────────
|
||||||
|
|
||||||
|
describe('Tab barrel export', () => {
|
||||||
|
it('includes PrestigeTab in the tabs index', async () => {
|
||||||
|
const mod = await import('@/components/game/tabs');
|
||||||
|
expect(mod.PrestigeTab).toBeDefined();
|
||||||
|
expect(typeof mod.PrestigeTab).toBe('function');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// ─── Test: Prestige upgrade definitions ────────────────────────────────────────
|
||||||
|
|
||||||
|
describe('Prestige upgrade definitions', () => {
|
||||||
|
it('has exactly 14 prestige upgrades', async () => {
|
||||||
|
const { PRESTIGE_DEF } = await import('@/lib/game/constants/prestige');
|
||||||
|
expect(Object.keys(PRESTIGE_DEF).length).toBe(14);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('all upgrades have required fields', async () => {
|
||||||
|
const { PRESTIGE_DEF } = await import('@/lib/game/constants/prestige');
|
||||||
|
for (const [id, def] of Object.entries(PRESTIGE_DEF)) {
|
||||||
|
expect(def.name).toBeTruthy();
|
||||||
|
expect(def.desc).toBeTruthy();
|
||||||
|
expect(def.max).toBeGreaterThan(0);
|
||||||
|
expect(def.cost).toBeGreaterThan(0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('all 14 expected upgrade IDs are present', async () => {
|
||||||
|
const { PRESTIGE_DEF } = await import('@/lib/game/constants/prestige');
|
||||||
|
const expectedIds = [
|
||||||
|
'manaWell', 'manaFlow', 'deepMemory', 'insightAmp', 'spireKey',
|
||||||
|
'temporalEcho', 'steadyHand', 'ancientKnowledge', 'elementalAttune',
|
||||||
|
'spellMemory', 'guardianPact', 'quickStart', 'elemStart',
|
||||||
|
'unlockedManaTypeCapacity',
|
||||||
|
];
|
||||||
|
for (const id of expectedIds) {
|
||||||
|
expect(PRESTIGE_DEF[id]).toBeDefined();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('upgrade costs are positive integers', async () => {
|
||||||
|
const { PRESTIGE_DEF } = await import('@/lib/game/constants/prestige');
|
||||||
|
for (const def of Object.values(PRESTIGE_DEF)) {
|
||||||
|
expect(Number.isInteger(def.cost)).toBe(true);
|
||||||
|
expect(def.cost).toBeGreaterThan(0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('upgrade max levels are positive integers', async () => {
|
||||||
|
const { PRESTIGE_DEF } = await import('@/lib/game/constants/prestige');
|
||||||
|
for (const def of Object.values(PRESTIGE_DEF)) {
|
||||||
|
expect(Number.isInteger(def.max)).toBe(true);
|
||||||
|
expect(def.max).toBeGreaterThan(0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// ─── Test: Prestige store shape ────────────────────────────────────────────────
|
||||||
|
|
||||||
|
describe('Prestige store', () => {
|
||||||
|
it('usePrestigeStore is importable', async () => {
|
||||||
|
const mod = await import('@/lib/game/stores');
|
||||||
|
expect(mod.usePrestigeStore).toBeDefined();
|
||||||
|
expect(typeof mod.usePrestigeStore).toBe('function');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// ─── Test: File size limit ─────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
describe('File size limits (400 lines max)', () => {
|
||||||
|
it('PrestigeTab.tsx is under 400 lines', async () => {
|
||||||
|
const fs = await import('fs');
|
||||||
|
const path = await import('path');
|
||||||
|
const filePath = path.join(__dirname, 'PrestigeTab.tsx');
|
||||||
|
const content = fs.readFileSync(filePath, 'utf-8');
|
||||||
|
const lines = content.split('\n').length;
|
||||||
|
expect(lines).toBeLessThan(400);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,272 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { useState, useEffect, useCallback } from 'react';
|
||||||
|
import { useShallow } from 'zustand/react/shallow';
|
||||||
|
import { usePrestigeStore, useGameStore } from '@/lib/game/stores';
|
||||||
|
import { PRESTIGE_DEF } from '@/lib/game/constants/prestige';
|
||||||
|
import { Card, CardContent } from '@/components/ui/card';
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
|
import { Badge } from '@/components/ui/badge';
|
||||||
|
import { ScrollArea } from '@/components/ui/scroll-area';
|
||||||
|
import { SectionHeader } from '@/components/ui/section-header';
|
||||||
|
import { DebugName } from '@/components/game/debug/debug-context';
|
||||||
|
import { fmt } from '@/lib/game/stores';
|
||||||
|
import {
|
||||||
|
AlertDialog,
|
||||||
|
AlertDialogAction,
|
||||||
|
AlertDialogCancel,
|
||||||
|
AlertDialogContent,
|
||||||
|
AlertDialogDescription,
|
||||||
|
AlertDialogFooter,
|
||||||
|
AlertDialogHeader,
|
||||||
|
AlertDialogTitle,
|
||||||
|
AlertDialogTrigger,
|
||||||
|
} from '@/components/ui/alert-dialog';
|
||||||
|
|
||||||
|
// ─── Upgrade Card ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
interface UpgradeCardProps {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
desc: string;
|
||||||
|
max: number;
|
||||||
|
cost: number;
|
||||||
|
currentLevel: number;
|
||||||
|
insight: number;
|
||||||
|
onPurchase: (id: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
function UpgradeCard({ id, name, desc, max, cost, currentLevel, insight, onPurchase }: UpgradeCardProps) {
|
||||||
|
const isMaxed = currentLevel >= max;
|
||||||
|
const canAfford = insight >= cost;
|
||||||
|
const disabled = isMaxed || !canAfford;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card className={`bg-gray-900/60 ${isMaxed ? 'border-amber-700/50' : 'border-gray-700'}`}>
|
||||||
|
<CardContent className="p-4 space-y-2">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<span className="font-medium text-sm text-gray-100">{name}</span>
|
||||||
|
<Badge
|
||||||
|
variant="outline"
|
||||||
|
className={isMaxed ? 'border-amber-600 text-amber-400' : 'border-gray-600 text-gray-400'}
|
||||||
|
>
|
||||||
|
{currentLevel}/{max}
|
||||||
|
</Badge>
|
||||||
|
</div>
|
||||||
|
<p className="text-xs text-gray-400">{desc}</p>
|
||||||
|
<div className="flex items-center justify-between pt-1">
|
||||||
|
<span className="text-xs text-gray-500">
|
||||||
|
Cost: <span className={canAfford ? 'text-amber-400' : 'text-red-400'}>{fmt(cost)}</span> insight
|
||||||
|
</span>
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
disabled={disabled}
|
||||||
|
onClick={() => onPurchase(id)}
|
||||||
|
className="h-7 px-3 text-xs"
|
||||||
|
>
|
||||||
|
{isMaxed ? 'Maxed' : 'Buy'}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Main Component ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
export function PrestigeTab() {
|
||||||
|
const [mounted, setMounted] = useState(false);
|
||||||
|
|
||||||
|
const {
|
||||||
|
insight,
|
||||||
|
totalInsight,
|
||||||
|
loopInsight,
|
||||||
|
loopCount,
|
||||||
|
prestigeUpgrades,
|
||||||
|
memorySlots,
|
||||||
|
memories,
|
||||||
|
pactSlots,
|
||||||
|
signedPacts,
|
||||||
|
defeatedGuardians,
|
||||||
|
doPrestige,
|
||||||
|
} = usePrestigeStore(useShallow((s) => ({
|
||||||
|
insight: s.insight,
|
||||||
|
totalInsight: s.totalInsight,
|
||||||
|
loopInsight: s.loopInsight,
|
||||||
|
loopCount: s.loopCount,
|
||||||
|
prestigeUpgrades: s.prestigeUpgrades,
|
||||||
|
memorySlots: s.memorySlots,
|
||||||
|
memories: s.memories,
|
||||||
|
pactSlots: s.pactSlots,
|
||||||
|
signedPacts: s.signedPacts,
|
||||||
|
defeatedGuardians: s.defeatedGuardians,
|
||||||
|
doPrestige: s.doPrestige,
|
||||||
|
})));
|
||||||
|
|
||||||
|
const startNewLoop = useGameStore((s) => s.startNewLoop);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setMounted(true);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handlePurchase = useCallback((id: string) => {
|
||||||
|
doPrestige(id);
|
||||||
|
}, [doPrestige]);
|
||||||
|
|
||||||
|
const handleResetLoop = useCallback(() => {
|
||||||
|
startNewLoop();
|
||||||
|
}, [startNewLoop]);
|
||||||
|
|
||||||
|
if (!mounted) {
|
||||||
|
return (
|
||||||
|
<div className="flex items-center justify-center p-8 text-gray-500">
|
||||||
|
Loading prestige…
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const upgradeEntries = Object.entries(PRESTIGE_DEF);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DebugName name="PrestigeTab">
|
||||||
|
<div className="space-y-4">
|
||||||
|
{/* Insight & Loop Summary */}
|
||||||
|
<Card className="bg-gray-900/60 border-gray-700">
|
||||||
|
<CardContent className="py-4">
|
||||||
|
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
|
||||||
|
<div className="text-center">
|
||||||
|
<div className="text-2xl font-bold text-amber-400">{fmt(insight)}</div>
|
||||||
|
<div className="text-xs text-gray-400 mt-0.5">Insight Available</div>
|
||||||
|
</div>
|
||||||
|
<div className="text-center">
|
||||||
|
<div className="text-2xl font-bold text-gray-200">{fmt(totalInsight)}</div>
|
||||||
|
<div className="text-xs text-gray-400 mt-0.5">Total Insight Earned</div>
|
||||||
|
</div>
|
||||||
|
<div className="text-center">
|
||||||
|
<div className="text-2xl font-bold text-gray-200">{loopCount}</div>
|
||||||
|
<div className="text-xs text-gray-400 mt-0.5">Loops Completed</div>
|
||||||
|
</div>
|
||||||
|
<div className="text-center">
|
||||||
|
<div className="text-2xl font-bold text-purple-400">{fmt(loopInsight)}</div>
|
||||||
|
<div className="text-xs text-gray-400 mt-0.5">This Loop's Insight</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
{/* Memories & Pacts */}
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
|
<Card className="bg-gray-900/60 border-gray-700">
|
||||||
|
<SectionHeader title="🧠 Memories" />
|
||||||
|
<CardContent className="pt-0">
|
||||||
|
<p className="text-xs text-gray-400 mb-2">
|
||||||
|
Skills carried between loops. Slots: {memories.length}/{memorySlots}
|
||||||
|
</p>
|
||||||
|
{memories.length === 0 ? (
|
||||||
|
<p className="text-xs text-gray-500 italic">No memories stored yet.</p>
|
||||||
|
) : (
|
||||||
|
<div className="space-y-1">
|
||||||
|
{memories.map((m) => (
|
||||||
|
<div key={m.skillId} className="text-xs text-gray-300 flex justify-between">
|
||||||
|
<span>{m.skillId}</span>
|
||||||
|
<span className="text-gray-500">Lv.{m.level} T{m.tier}</span>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Card className="bg-gray-900/60 border-gray-700">
|
||||||
|
<SectionHeader title="📜 Pacts" />
|
||||||
|
<CardContent className="pt-0">
|
||||||
|
<p className="text-xs text-gray-400 mb-2">
|
||||||
|
Guardian pacts signed. Slots: {signedPacts.length}/{pactSlots}
|
||||||
|
</p>
|
||||||
|
{defeatedGuardians.length > 0 && (
|
||||||
|
<p className="text-xs text-gray-500 mb-1">
|
||||||
|
Defeated guardians: {defeatedGuardians.map((f) => `F${f}`).join(', ')}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
{signedPacts.length === 0 ? (
|
||||||
|
<p className="text-xs text-gray-500 italic">No pacts signed yet.</p>
|
||||||
|
) : (
|
||||||
|
<div className="space-y-1">
|
||||||
|
{signedPacts.map((f) => (
|
||||||
|
<div key={f} className="text-xs text-green-400">
|
||||||
|
✓ Floor {f} — Pact signed
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Prestige Upgrades */}
|
||||||
|
<Card className="bg-gray-900/60 border-gray-700">
|
||||||
|
<SectionHeader title="⬆️ Prestige Upgrades" />
|
||||||
|
<CardContent className="pt-0">
|
||||||
|
<ScrollArea className="h-[400px] pr-2">
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-3">
|
||||||
|
{upgradeEntries.map(([id, def]) => (
|
||||||
|
<UpgradeCard
|
||||||
|
key={id}
|
||||||
|
id={id}
|
||||||
|
name={def.name}
|
||||||
|
desc={def.desc}
|
||||||
|
max={def.max}
|
||||||
|
cost={def.cost}
|
||||||
|
currentLevel={prestigeUpgrades[id] || 0}
|
||||||
|
insight={insight}
|
||||||
|
onPurchase={handlePurchase}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</ScrollArea>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
{/* Reset Loop */}
|
||||||
|
<Card className="bg-gray-900/60 border-red-900/50">
|
||||||
|
<CardContent className="py-4">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<h3 className="text-lg font-semibold text-red-400">Reset Loop</h3>
|
||||||
|
<p className="text-xs text-gray-400 mt-1">
|
||||||
|
End the current loop and gain {fmt(loopInsight)} insight. Your prestige upgrades, memories, and pacts are preserved.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<AlertDialog>
|
||||||
|
<AlertDialogTrigger asChild>
|
||||||
|
<Button variant="destructive" size="sm" className="flex-shrink-0 ml-4">
|
||||||
|
Reset Loop
|
||||||
|
</Button>
|
||||||
|
</AlertDialogTrigger>
|
||||||
|
<AlertDialogContent>
|
||||||
|
<AlertDialogHeader>
|
||||||
|
<AlertDialogTitle>Reset the Loop?</AlertDialogTitle>
|
||||||
|
<AlertDialogDescription>
|
||||||
|
This will end your current loop and award you <strong className="text-amber-400">{fmt(loopInsight)} insight</strong>.
|
||||||
|
Your prestige upgrades, memories, and pacts will be preserved.
|
||||||
|
<br /><br />
|
||||||
|
Day, hour, mana, floor progress, and combat state will be reset.
|
||||||
|
</AlertDialogDescription>
|
||||||
|
</AlertDialogHeader>
|
||||||
|
<AlertDialogFooter>
|
||||||
|
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
||||||
|
<AlertDialogAction onClick={handleResetLoop}>
|
||||||
|
Confirm Reset
|
||||||
|
</AlertDialogAction>
|
||||||
|
</AlertDialogFooter>
|
||||||
|
</AlertDialogContent>
|
||||||
|
</AlertDialog>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
</DebugName>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
PrestigeTab.displayName = 'PrestigeTab';
|
||||||
@@ -7,3 +7,4 @@ export { StatsTab } from './StatsTab';
|
|||||||
export { DebugTab } from './DebugTab';
|
export { DebugTab } from './DebugTab';
|
||||||
export { AchievementsTab } from './AchievementsTab';
|
export { AchievementsTab } from './AchievementsTab';
|
||||||
export { AttunementsTab } from './AttunementsTab';
|
export { AttunementsTab } from './AttunementsTab';
|
||||||
|
export { PrestigeTab } from './PrestigeTab';
|
||||||
|
|||||||
Reference in New Issue
Block a user