style: run format:apply
This commit is contained in:
+258
-173
@@ -4,202 +4,287 @@ 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';
|
||||
import {
|
||||
fetchCharacters,
|
||||
fetchItems,
|
||||
fetchOfferings,
|
||||
fetchCustomizations,
|
||||
fetchDLCs,
|
||||
fetchAddons,
|
||||
fetchPerks
|
||||
} from '@/lib/db';
|
||||
|
||||
export default function Home() {
|
||||
const store = useInventoryStore();
|
||||
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 [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('');
|
||||
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);
|
||||
});
|
||||
}, []);
|
||||
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 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 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 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);
|
||||
}
|
||||
};
|
||||
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]);
|
||||
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;
|
||||
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>
|
||||
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>
|
||||
{/* 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>
|
||||
{/* 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}>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}>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}>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}>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}>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>
|
||||
<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>
|
||||
|
||||
{/* profile */}
|
||||
<div className={styles.card}>
|
||||
<h3 className={styles.cardTitle}>Profile Import / Export</h3>
|
||||
<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>
|
||||
|
||||
<textarea
|
||||
className={styles.textarea}
|
||||
value={importText}
|
||||
onChange={(e) => setImportText(e.target.value)}
|
||||
/>
|
||||
{/* profile */}
|
||||
<div className={styles.card}>
|
||||
<h3 className={styles.cardTitle}>Profile Import / Export</h3>
|
||||
|
||||
<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>
|
||||
<textarea
|
||||
className={styles.textarea}
|
||||
value={importText}
|
||||
onChange={(e) => setImportText(e.target.value)}
|
||||
/>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
</div>)
|
||||
<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>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user