feat: add addons and perks to dashboard
This commit is contained in:
+33
-11
@@ -4,7 +4,7 @@ import { useState, useEffect, useMemo } from 'react';
|
|||||||
import { useInventoryStore } from '@/store/useInventoryStore';
|
import { useInventoryStore } from '@/store/useInventoryStore';
|
||||||
import styles from '../styles/Home.module.css';
|
import styles from '../styles/Home.module.css';
|
||||||
import { isNamedDLC } from '@/lib/utils';
|
import { isNamedDLC } from '@/lib/utils';
|
||||||
import { fetchCharacters, fetchItems, fetchOfferings, fetchCustomizations, fetchDLCs } from '@/lib/db';
|
import { fetchCharacters, fetchItems, fetchOfferings, fetchCustomizations, fetchDLCs, fetchAddons, fetchPerks } from '@/lib/db';
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
const store = useInventoryStore();
|
const store = useInventoryStore();
|
||||||
@@ -14,6 +14,8 @@ export default function Home() {
|
|||||||
const [itemsCount, setItemsCount] = useState(0);
|
const [itemsCount, setItemsCount] = useState(0);
|
||||||
const [offeringsCount, setOfferingsCount] = useState(0);
|
const [offeringsCount, setOfferingsCount] = useState(0);
|
||||||
const [dlcsCount, setDlcsCount] = 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('');
|
||||||
|
|
||||||
@@ -23,13 +25,17 @@ export default function Home() {
|
|||||||
fetchItems(),
|
fetchItems(),
|
||||||
fetchOfferings(),
|
fetchOfferings(),
|
||||||
fetchCustomizations(),
|
fetchCustomizations(),
|
||||||
fetchDLCs()
|
fetchDLCs(),
|
||||||
]).then(([chars, items, offerings, customizations, dlcs]) => {
|
fetchAddons(),
|
||||||
|
fetchPerks()
|
||||||
|
]).then(([chars, items, offerings, customizations, dlcs, addons, perks]) => {
|
||||||
setCharCount(chars.length);
|
setCharCount(chars.length);
|
||||||
setItemsCount(items.length);
|
setItemsCount(items.length);
|
||||||
setOfferingsCount(offerings.length);
|
setOfferingsCount(offerings.length);
|
||||||
setCustCount(customizations.length);
|
setCustCount(customizations.length);
|
||||||
setDlcsCount(dlcs.filter(isNamedDLC).length);
|
setDlcsCount(dlcs.filter(isNamedDLC).length);
|
||||||
|
setAddonsCount(addons.length);
|
||||||
|
setPerksCount(perks.length);
|
||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
@@ -55,25 +61,33 @@ export default function Home() {
|
|||||||
unlockedCharacters: store.unlockedCharacters,
|
unlockedCharacters: store.unlockedCharacters,
|
||||||
unlockedCustomizations: store.unlockedCustomizations,
|
unlockedCustomizations: store.unlockedCustomizations,
|
||||||
unlockedDLCs: store.unlockedDLCs,
|
unlockedDLCs: store.unlockedDLCs,
|
||||||
|
unlockedPerks: store.unlockedPerks,
|
||||||
items: store.items,
|
items: store.items,
|
||||||
offerings: store.offerings
|
offerings: store.offerings,
|
||||||
|
addons: store.addons
|
||||||
}, null, 2);
|
}, null, 2);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleImport = async () => {
|
const handleImport = async () => {
|
||||||
|
try {
|
||||||
const parsed = JSON.parse(importText);
|
const parsed = JSON.parse(importText);
|
||||||
store.importProfile(parsed);
|
store.importProfile(parsed);
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Invalid JSON", e);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setImportText(getExportText());
|
setImportText(getExportText());
|
||||||
}, [store.unlockedCharacters, store.unlockedCustomizations, store.unlockedDLCs, store.items, store.offerings]);
|
}, [store.unlockedCharacters, store.unlockedCustomizations, store.unlockedDLCs, store.unlockedPerks, store.items, store.offerings, store.addons]);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
stats
|
stats
|
||||||
*/
|
*/
|
||||||
const unlockedItems = useMemo(() => Object.values(store.items).filter(qty => qty > 0).length, [store.items]);
|
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 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}>
|
return (<div className={styles.container}>
|
||||||
<header className={styles.header}>
|
<header className={styles.header}>
|
||||||
@@ -85,27 +99,35 @@ export default function Home() {
|
|||||||
<section className={styles.statsGrid}>
|
<section className={styles.statsGrid}>
|
||||||
<div className={styles.statCard}>
|
<div className={styles.statCard}>
|
||||||
<div className={styles.statLabel}>Characters</div>
|
<div className={styles.statLabel}>Characters</div>
|
||||||
<div className={styles.statValue}>{store.unlockedCharacters.length} / {charCount || '-'}</div>
|
<div className={styles.statValue}>{store.unlockedCharacters.length} <span className={styles.statTotal}>/ {charCount || '-'}</span></div>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.statCard}>
|
<div className={styles.statCard}>
|
||||||
<div className={styles.statLabel}>Customizations</div>
|
<div className={styles.statLabel}>Customizations</div>
|
||||||
<div className={styles.statValue}>{store.unlockedCustomizations.length} / {custCount || '-'}</div>
|
<div className={styles.statValue}>{store.unlockedCustomizations.length} <span className={styles.statTotal}>/ {custCount || '-'}</span></div>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.statCard}>
|
<div className={styles.statCard}>
|
||||||
<div className={styles.statLabel}>DLCs</div>
|
<div className={styles.statLabel}>DLCs</div>
|
||||||
<div className={styles.statValue}>{store.unlockedDLCs.length} / {dlcsCount || '-'}</div>
|
<div className={styles.statValue}>{store.unlockedDLCs.length} <span className={styles.statTotal}>/ {dlcsCount || '-'}</span></div>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.statCard}>
|
<div className={styles.statCard}>
|
||||||
<div className={styles.statLabel}>Items</div>
|
<div className={styles.statLabel}>Items</div>
|
||||||
<div className={styles.statValue}>{unlockedItems} / {itemsCount || '-'}</div>
|
<div className={styles.statValue}>{unlockedItems} <span className={styles.statTotal}>/ {itemsCount || '-'}</span></div>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.statCard}>
|
<div className={styles.statCard}>
|
||||||
<div className={styles.statLabel}>Offerings</div>
|
<div className={styles.statLabel}>Offerings</div>
|
||||||
<div className={styles.statValue}>{unlockedOfferings} / {offeringsCount || '-'}</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>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* profile import export stuff */}
|
{/* Control panels */}
|
||||||
<section className={styles.panelGrid}>
|
<section className={styles.panelGrid}>
|
||||||
<div className={styles.card}>
|
<div className={styles.card}>
|
||||||
<h3 className={styles.cardTitle}>Profile Import / Export</h3>
|
<h3 className={styles.cardTitle}>Profile Import / Export</h3>
|
||||||
|
|||||||
+103
-25
@@ -29,11 +29,23 @@
|
|||||||
|
|
||||||
.statsGrid {
|
.statsGrid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(5, 1fr);
|
grid-template-columns: repeat(4, 1fr);
|
||||||
gap: 1.5rem;
|
gap: 1.5rem;
|
||||||
margin-bottom: 3rem;
|
margin-bottom: 3rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1000px) {
|
||||||
|
.statsGrid {
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.statsGrid {
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.statCard {
|
.statCard {
|
||||||
background: #0d0d0d;
|
background: #0d0d0d;
|
||||||
border: 1px solid #1a1a1a;
|
border: 1px solid #1a1a1a;
|
||||||
@@ -64,17 +76,30 @@
|
|||||||
line-height: 1;
|
line-height: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.statTotal {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: #555555;
|
||||||
|
}
|
||||||
|
|
||||||
.panelGrid {
|
.panelGrid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr 1.2fr;
|
||||||
gap: 2rem;
|
gap: 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (max-width: 900px) {
|
||||||
|
.panelGrid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.card {
|
.card {
|
||||||
background: #0d0d0d;
|
background: #0d0d0d;
|
||||||
border: 1px solid #1a1a1a;
|
border: 1px solid #1a1a1a;
|
||||||
border-top: 3px solid #630000;
|
border-top: 3px solid #630000;
|
||||||
padding: 2rem;
|
padding: 2rem;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cardTitle {
|
.cardTitle {
|
||||||
@@ -83,12 +108,7 @@
|
|||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
margin: 0 0 1.5rem 0;
|
margin: 0 0 1.5rem 0;
|
||||||
letter-spacing: 0.05em;
|
letter-spacing: 0.05em;
|
||||||
}
|
font-family: 'Oswald', sans-serif;
|
||||||
|
|
||||||
.formatBtnActive {
|
|
||||||
background: #a30000;
|
|
||||||
border-color: #ff0000;
|
|
||||||
color: #ffffff;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.textarea {
|
.textarea {
|
||||||
@@ -149,26 +169,84 @@
|
|||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.toast {
|
.toggleRow {
|
||||||
position: fixed;
|
display: flex;
|
||||||
bottom: 2rem;
|
align-items: center;
|
||||||
right: 2rem;
|
justify-content: space-between;
|
||||||
background: #0d0d0d;
|
padding: 1rem 0;
|
||||||
border: 1px solid #00ff66;
|
border-bottom: 1px solid #1a1a1a;
|
||||||
color: #00ff66;
|
}
|
||||||
padding: 1rem 1.5rem;
|
|
||||||
font-size: 0.8rem;
|
.toggleRow:last-child {
|
||||||
text-transform: uppercase;
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggleInfo {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.2rem;
|
||||||
|
padding-right: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggleLabel {
|
||||||
|
font-size: 0.85rem;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
animation: slideIn 0.3s ease-out forwards;
|
text-transform: uppercase;
|
||||||
|
color: #ffffff;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
font-family: 'Oswald', sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
.toastError {
|
.toggleDesc {
|
||||||
border-color: #ff0000;
|
font-size: 0.68rem;
|
||||||
color: #ff0000;
|
color: #555555;
|
||||||
|
line-height: 1.3;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes slideIn {
|
.switch {
|
||||||
from { transform: translateY(20px); opacity: 0; }
|
position: relative;
|
||||||
to { transform: translateY(0); opacity: 1; }
|
display: inline-block;
|
||||||
|
width: 48px;
|
||||||
|
height: 24px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.switch input {
|
||||||
|
opacity: 0;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider {
|
||||||
|
position: absolute;
|
||||||
|
cursor: pointer;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background-color: #121212;
|
||||||
|
border: 1px solid #333333;
|
||||||
|
transition: 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider:before {
|
||||||
|
position: absolute;
|
||||||
|
content: "";
|
||||||
|
height: 16px;
|
||||||
|
width: 16px;
|
||||||
|
left: 3px;
|
||||||
|
bottom: 3px;
|
||||||
|
background-color: #555555;
|
||||||
|
transition: 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
input:checked + .slider {
|
||||||
|
background-color: #2b0000;
|
||||||
|
border-color: #a30000;
|
||||||
|
}
|
||||||
|
|
||||||
|
input:checked + .slider:before {
|
||||||
|
transform: translateX(24px);
|
||||||
|
background-color: #a30000;
|
||||||
|
box-shadow: 0 0 8px rgba(163, 0, 0, 0.7);
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user