Files
HexUnlockedWeb/app/page.tsx
T
2026-06-19 04:42:59 -03:00

291 lines
8.0 KiB
TypeScript

'use client';
import { useState, useEffect, useMemo } from 'react';
import { useInventoryStore } from '@/store/useInventoryStore';
import styles from '../styles/Home.module.css';
import { isNamedDLC } from '@/lib/utils';
import {
fetchCharacters,
fetchItems,
fetchOfferings,
fetchCustomizations,
fetchDLCs,
fetchAddons,
fetchPerks
} from '@/lib/db';
export default function Home() {
const store = useInventoryStore();
const [charCount, setCharCount] = useState(0);
const [custCount, setCustCount] = useState(0);
const [itemsCount, setItemsCount] = useState(0);
const [offeringsCount, setOfferingsCount] = useState(0);
const [dlcsCount, setDlcsCount] = useState(0);
const [addonsCount, setAddonsCount] = useState(0);
const [perksCount, setPerksCount] = useState(0);
const [importText, setImportText] = useState('');
useEffect(() => {
Promise.all([
fetchCharacters(),
fetchItems(),
fetchOfferings(),
fetchCustomizations(),
fetchDLCs(),
fetchAddons(),
fetchPerks()
]).then(
([chars, items, offerings, customizations, dlcs, addons, perks]) => {
setCharCount(chars.length);
setItemsCount(items.length);
setOfferingsCount(offerings.length);
setCustCount(customizations.length);
setDlcsCount(dlcs.filter(isNamedDLC).length);
setAddonsCount(addons.length);
setPerksCount(perks.length);
}
);
}, []);
/*
profile handling
*/
const handleDownload = () => {
const blob = new Blob([importText], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = 'profile.json';
link.click();
URL.revokeObjectURL(url);
};
const handleCopy = () => {
navigator.clipboard.writeText(importText);
};
const getExportText = () => {
return JSON.stringify(
{
unlockedCharacters: store.unlockedCharacters,
unlockedCustomizations: store.unlockedCustomizations,
unlockedDLCs: store.unlockedDLCs,
unlockedPerks: store.unlockedPerks,
items: store.items,
offerings: store.offerings,
addons: store.addons
},
null,
2
);
};
const handleImport = async () => {
try {
const parsed = JSON.parse(importText);
store.importProfile(parsed);
} catch (e) {
console.error('Invalid JSON', e);
}
};
useEffect(() => {
setImportText(getExportText());
}, [
store.unlockedCharacters,
store.unlockedCustomizations,
store.unlockedDLCs,
store.unlockedPerks,
store.items,
store.offerings,
store.addons
]);
/*
stats
*/
const unlockedItems = useMemo(
() => Object.values(store.items).filter((qty) => qty > 0).length,
[store.items]
);
const unlockedOfferings = useMemo(
() => Object.values(store.offerings).filter((qty) => qty > 0).length,
[store.offerings]
);
const unlockedAddons = useMemo(
() => Object.values(store.addons).filter((qty) => qty > 0).length,
[store.addons]
);
const unlockedPerks = store.unlockedPerks.length;
return (
<div className={styles.container}>
<header className={styles.header}>
<h1 className={styles.title}>Dashboard</h1>
<p className={styles.subtitle}>
Status overview and profile management
</p>
</header>
{/* stats cards */}
<section className={styles.statsGrid}>
<div className={styles.statCard}>
<div className={styles.statLabel}>Characters</div>
<div className={styles.statValue}>
{store.unlockedCharacters.length}{' '}
<span className={styles.statTotal}>/ {charCount || '-'}</span>
</div>
</div>
<div className={styles.statCard}>
<div className={styles.statLabel}>Customizations</div>
<div className={styles.statValue}>
{store.unlockedCustomizations.length}{' '}
<span className={styles.statTotal}>/ {custCount || '-'}</span>
</div>
</div>
<div className={styles.statCard}>
<div className={styles.statLabel}>DLCs</div>
<div className={styles.statValue}>
{store.unlockedDLCs.length}{' '}
<span className={styles.statTotal}>/ {dlcsCount || '-'}</span>
</div>
</div>
<div className={styles.statCard}>
<div className={styles.statLabel}>Items</div>
<div className={styles.statValue}>
{unlockedItems}{' '}
<span className={styles.statTotal}>/ {itemsCount || '-'}</span>
</div>
</div>
<div className={styles.statCard}>
<div className={styles.statLabel}>Offerings</div>
<div className={styles.statValue}>
{unlockedOfferings}{' '}
<span className={styles.statTotal}>/ {offeringsCount || '-'}</span>
</div>
</div>
<div className={styles.statCard}>
<div className={styles.statLabel}>Addons</div>
<div className={styles.statValue}>
{unlockedAddons}{' '}
<span className={styles.statTotal}>/ {addonsCount || '-'}</span>
</div>
</div>
<div className={styles.statCard}>
<div className={styles.statLabel}>Perks</div>
<div className={styles.statValue}>
{unlockedPerks}{' '}
<span className={styles.statTotal}>/ {perksCount || '-'}</span>
</div>
</div>
</section>
{/* toggles */}
<section className={styles.panelGrid}>
<div className={styles.card}>
<h3 className={styles.cardTitle}>Spoofer Toggles</h3>
<div className={styles.toggleRow}>
<div className={styles.toggleInfo}>
<span className={styles.toggleLabel}>Item Spoofing</span>
<span className={styles.toggleDesc}>
Spoof inventory items, addons, and offerings
</span>
</div>
<label className={styles.switch}>
<input
type='checkbox'
checked={store.spoofItems}
onChange={(e) =>
store.setToggle('spoofItems', e.target.checked)
}
/>
<span className={styles.slider}></span>
</label>
</div>
<div className={styles.toggleRow}>
<div className={styles.toggleInfo}>
<span className={styles.toggleLabel}>Perk Spoofing</span>
<span className={styles.toggleDesc}>
Unlock all perk slots and custom builds
</span>
</div>
<label className={styles.switch}>
<input
type='checkbox'
checked={store.spoofPerks}
onChange={(e) =>
store.setToggle('spoofPerks', e.target.checked)
}
/>
<span className={styles.slider}></span>
</label>
</div>
<div className={styles.toggleRow}>
<div className={styles.toggleInfo}>
<span className={styles.toggleLabel}>Catalog Spoofing</span>
<span className={styles.toggleDesc}>
Unlock cosmetics, characters, and outfits
</span>
</div>
<label className={styles.switch}>
<input
type='checkbox'
checked={store.spoofCatalog}
onChange={(e) =>
store.setToggle('spoofCatalog', e.target.checked)
}
/>
<span className={styles.slider}></span>
</label>
</div>
<div className={styles.toggleRow}>
<div className={styles.toggleInfo}>
<span className={styles.toggleLabel}>DLC Spoofing</span>
<span className={styles.toggleDesc}>
Bypass Steam, Epic, and Xbox DLC ownership
</span>
</div>
<label className={styles.switch}>
<input
type='checkbox'
checked={store.spoofDLCs}
onChange={(e) => store.setToggle('spoofDLCs', e.target.checked)}
/>
<span className={styles.slider}></span>
</label>
</div>
</div>
{/* profile */}
<div className={styles.card}>
<h3 className={styles.cardTitle}>Profile Import / Export</h3>
<textarea
className={styles.textarea}
value={importText}
onChange={(e) => setImportText(e.target.value)}
/>
<div className={styles.btnRow}>
<button className={styles.primaryBtn} onClick={handleImport}>
Validate & Import
</button>
<button className={styles.secondaryBtn} onClick={handleCopy}>
Copy to Clipboard
</button>
<button className={styles.secondaryBtn} onClick={handleDownload}>
Download file
</button>
</div>
</div>
</section>
</div>
);
}