cool prestige update
This commit is contained in:
@@ -11,6 +11,8 @@ const PAGE_SIZE = 50;
|
||||
export default function Dashboard() {
|
||||
const { user, loading, patchUser } = useAuth();
|
||||
const [inventory, setInventory] = useState([]);
|
||||
const [vault, setVault] = useState(null);
|
||||
const [vaultMessage, setVaultMessage] = useState('');
|
||||
const [error, setError] = useState('');
|
||||
const [sort, setSort] = useState('value');
|
||||
const [page, setPage] = useState(1);
|
||||
@@ -37,6 +39,7 @@ export default function Dashboard() {
|
||||
const load = async () => {
|
||||
const inv = await api.inventory();
|
||||
setInventory(inv.inventory);
|
||||
setVault(inv.vault || null);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
@@ -109,6 +112,7 @@ export default function Dashboard() {
|
||||
return inventory.filter(
|
||||
(inv) =>
|
||||
!inv.favorite &&
|
||||
!inv.staked &&
|
||||
(inv.valueCents ?? inv.item?.valueCents ?? 0) < thresholdCents
|
||||
);
|
||||
}, [inventory, thresholdCents]);
|
||||
@@ -118,10 +122,17 @@ export default function Dashboard() {
|
||||
[inventory, selected]
|
||||
);
|
||||
|
||||
const stakedItems = useMemo(() => inventory.filter((inv) => inv.staked), [inventory]);
|
||||
|
||||
const selectedHasFavorite = selectedItems.some((inv) => inv.favorite);
|
||||
const selectedAllFavorite =
|
||||
selectedItems.length > 0 && selectedItems.every((inv) => inv.favorite);
|
||||
const selectedCanSell = selectedItems.some((inv) => !inv.favorite);
|
||||
const selectedCanSell = selectedItems.some((inv) => !inv.favorite && !inv.staked);
|
||||
const selectedStaked = selectedItems.filter((inv) => inv.staked);
|
||||
const selectedUnstaked = selectedItems.filter((inv) => !inv.staked);
|
||||
const vaultFree = Math.max(0, (vault?.slots ?? 3) - (vault?.used ?? 0));
|
||||
const canStake = selectedUnstaked.length > 0 && selectedUnstaked.length <= vaultFree;
|
||||
const canUnstake = selectedStaked.length > 0;
|
||||
|
||||
const belowValue = useMemo(
|
||||
() =>
|
||||
@@ -155,11 +166,78 @@ export default function Dashboard() {
|
||||
};
|
||||
|
||||
const sell = async () => {
|
||||
const ids = selectedItems.filter((inv) => !inv.favorite).map((inv) => inv.id);
|
||||
const ids = selectedItems
|
||||
.filter((inv) => !inv.favorite && !inv.staked)
|
||||
.map((inv) => inv.id);
|
||||
if (!ids.length) return;
|
||||
await sellIds(ids);
|
||||
};
|
||||
|
||||
const stakeSelected = async () => {
|
||||
const ids = selectedUnstaked.map((inv) => inv.id);
|
||||
if (!ids.length) return;
|
||||
setBusy(true);
|
||||
setError('');
|
||||
setVaultMessage('');
|
||||
try {
|
||||
const data = await api.vaultStake(ids);
|
||||
if (data.user) patchUser({ balance: data.user.balance });
|
||||
setSelected([]);
|
||||
setVaultMessage(`Staked ${ids.length} item${ids.length === 1 ? '' : 's'} in vault`);
|
||||
await load();
|
||||
} catch (err) {
|
||||
setError(err.message);
|
||||
} finally {
|
||||
setBusy(false);
|
||||
}
|
||||
};
|
||||
|
||||
const unstakeSelected = async () => {
|
||||
const ids = selectedStaked.map((inv) => inv.id);
|
||||
if (!ids.length) return;
|
||||
setBusy(true);
|
||||
setError('');
|
||||
setVaultMessage('');
|
||||
try {
|
||||
const data = await api.vaultUnstake(ids);
|
||||
if (data.user) patchUser({ balance: data.user.balance });
|
||||
setSelected([]);
|
||||
const yieldPart =
|
||||
data.yieldCredited > 0
|
||||
? ` · +${formatCredits(data.yieldCredited)} cr yield`
|
||||
: '';
|
||||
setVaultMessage(
|
||||
`Unstaked ${ids.length} item${ids.length === 1 ? '' : 's'}${yieldPart}`
|
||||
);
|
||||
await load();
|
||||
} catch (err) {
|
||||
setError(err.message);
|
||||
} finally {
|
||||
setBusy(false);
|
||||
}
|
||||
};
|
||||
|
||||
const claimVault = async () => {
|
||||
setBusy(true);
|
||||
setError('');
|
||||
setVaultMessage('');
|
||||
try {
|
||||
const data = await api.vaultClaim();
|
||||
if (data.user) patchUser({ balance: data.user.balance });
|
||||
if (data.vault) setVault(data.vault);
|
||||
setVaultMessage(
|
||||
data.credited > 0
|
||||
? `Vault yield +${formatCredits(data.credited)} cr`
|
||||
: 'No vault yield ready yet'
|
||||
);
|
||||
await load();
|
||||
} catch (err) {
|
||||
setError(err.message);
|
||||
} finally {
|
||||
setBusy(false);
|
||||
}
|
||||
};
|
||||
|
||||
const setFavorite = async (favorite) => {
|
||||
if (!selected.length) return;
|
||||
setBusy(true);
|
||||
@@ -303,10 +381,34 @@ export default function Dashboard() {
|
||||
className="btn"
|
||||
onClick={sell}
|
||||
disabled={busy || !selectedCanSell}
|
||||
title={selectedHasFavorite ? 'Favorites cannot be sold' : undefined}
|
||||
title={
|
||||
selectedHasFavorite || selectedStaked.length
|
||||
? 'Favorites and vault items cannot be sold'
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
Sell
|
||||
</button>
|
||||
{canStake && (
|
||||
<button
|
||||
type="button"
|
||||
className="btn secondary"
|
||||
onClick={stakeSelected}
|
||||
disabled={busy}
|
||||
>
|
||||
Stake ({selectedUnstaked.length})
|
||||
</button>
|
||||
)}
|
||||
{canUnstake && (
|
||||
<button
|
||||
type="button"
|
||||
className="btn secondary"
|
||||
onClick={unstakeSelected}
|
||||
disabled={busy}
|
||||
>
|
||||
Unstake ({selectedStaked.length})
|
||||
</button>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
@@ -381,6 +483,57 @@ export default function Dashboard() {
|
||||
</div>
|
||||
|
||||
{error && <div className="error">{error}</div>}
|
||||
{vaultMessage && <div className="ok-msg">{vaultMessage}</div>}
|
||||
|
||||
<div className="vault-panel admin-block">
|
||||
<div className="section-head">
|
||||
<div>
|
||||
<h2>Vault</h2>
|
||||
<p className="muted">
|
||||
Stake up to {vault?.slots ?? 3} items. Online: 10% / 10 min · Offline: 1% / 10 min
|
||||
(scales with wear & rarity).
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
className="btn secondary"
|
||||
onClick={claimVault}
|
||||
disabled={busy || !(vault?.used > 0)}
|
||||
>
|
||||
Claim yield
|
||||
</button>
|
||||
</div>
|
||||
<div className="vault-stats">
|
||||
<span>
|
||||
Slots <strong>{vault?.used ?? 0}/{vault?.slots ?? 3}</strong>
|
||||
</span>
|
||||
<span>
|
||||
Online / tick{' '}
|
||||
<strong className="gold">{formatCredits(vault?.onlinePerTick ?? 0)} cr</strong>
|
||||
</span>
|
||||
<span>
|
||||
Offline / tick{' '}
|
||||
<strong>{formatCredits(vault?.offlinePerTick ?? 0)} cr</strong>
|
||||
</span>
|
||||
</div>
|
||||
{stakedItems.length > 0 ? (
|
||||
<div className="grid vault-grid">
|
||||
{stakedItems.map((inv) => (
|
||||
<ItemTile
|
||||
key={`vault-${inv.id}`}
|
||||
item={inv.item}
|
||||
favorite={inv.favorite}
|
||||
staked
|
||||
selected={selected.includes(inv.id)}
|
||||
onClick={() => toggle(inv.id)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<p className="muted">No items staked — select items below and Stake.</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{inventory.length === 0 ? (
|
||||
<div className="empty">
|
||||
Empty for now —{' '}
|
||||
@@ -397,6 +550,7 @@ export default function Dashboard() {
|
||||
key={inv.id}
|
||||
item={inv.item}
|
||||
favorite={inv.favorite}
|
||||
staked={inv.staked}
|
||||
selected={selected.includes(inv.id)}
|
||||
onClick={() => toggle(inv.id)}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user