Feature/resource yields (#3)
* Add handleSetUpgrade function and other related stuff * Fix select drill to upgrade * Small refactor - apply drill on upgrade * Refactor types - WIP * Add handleBuy function, sort storeItems by owned * Add tiers to storeItems * Show equipment on moon, add radiobutton to select * Modify types * Cleanup some code and comments etc. * Add claim function to increment user balance and decrement well * Add API to get data * Set inventoryItem and activeTier in state * Update stakingSource whenever inventoryItems are updated * Disable upgrade button when mining is active * Add initial implementation of lightbox --------- Co-authored-by: Nico Li <nilindenau@gmail.com>
This commit is contained in:
parent
0c1f15464f
commit
801532759d
33
src/app/Components/BankAccount.tsx
Normal file
33
src/app/Components/BankAccount.tsx
Normal file
@ -0,0 +1,33 @@
|
||||
"use client";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { IBankAccount } from "typings";
|
||||
|
||||
const BankAccount = (props: { bankAccount: IBankAccount }) => {
|
||||
return (
|
||||
<div
|
||||
className={
|
||||
props.bankAccount.resourceType.bgColorClass +
|
||||
" bg-gradient-to-br hover:bg-gradient-to-tr rounded-lg p-3"
|
||||
}
|
||||
>
|
||||
<div className="text-white">
|
||||
<span
|
||||
className={
|
||||
props.bankAccount.resourceType.fontColorClass + " font-bold"
|
||||
}
|
||||
>
|
||||
{props.bankAccount.resourceType.name}
|
||||
</span>
|
||||
<h3 className="text-2xl font-bold">
|
||||
{props.bankAccount.balance.toLocaleString("en-US", {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
})}{" "}
|
||||
kg
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default BankAccount;
|
33
src/app/Components/BankAccountsView.tsx
Normal file
33
src/app/Components/BankAccountsView.tsx
Normal file
@ -0,0 +1,33 @@
|
||||
"use client";
|
||||
import React from "react";
|
||||
import { IBankAccount } from "typings";
|
||||
import BankAccount from "./BankAccount";
|
||||
|
||||
const BankAccountsView = (props: {
|
||||
bankAccounts: IBankAccount[];
|
||||
setLightBoxIsActive: () => void;
|
||||
}) => {
|
||||
return (
|
||||
<div className="p-4">
|
||||
<div className="grid grid-cols-5 gap-8">
|
||||
<div className="bg-green-800 rounded-lg p-3">
|
||||
<div className="text-white">
|
||||
<span className="text-green-400">Moonbucks</span>
|
||||
<h3 className="text-2xl font-bold">$5,342.23</h3>
|
||||
<button
|
||||
onClick={() => props.setLightBoxIsActive()}
|
||||
className="px-2 text-sm mt-1 rounded-lg font-bold bg-green-400 text-green-800"
|
||||
>
|
||||
Sell Resources
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{props.bankAccounts.map((bankAccount, id) => {
|
||||
return <BankAccount key={id} bankAccount={bankAccount} />;
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default BankAccountsView;
|
51
src/app/Components/InventoryItem.tsx
Normal file
51
src/app/Components/InventoryItem.tsx
Normal file
@ -0,0 +1,51 @@
|
||||
import React from "react";
|
||||
import { IInventoryItem, IStoreItem } from "typings";
|
||||
import CardLayout from "../Layouts/CardLayout";
|
||||
|
||||
const InventoryItem = (props: {
|
||||
inventoryItem: IInventoryItem;
|
||||
inUse: boolean | undefined;
|
||||
handleIncrementTier: (inventoryItem: IInventoryItem) => void;
|
||||
}) => {
|
||||
const getCurrentTier = (index: number) => {
|
||||
return props.inventoryItem.storeItem.tiers[index];
|
||||
};
|
||||
|
||||
return (
|
||||
<CardLayout>
|
||||
<h3 className="text-xl font-bold mb-2">
|
||||
{props.inventoryItem.storeItem.name}{" "}
|
||||
<span className="bg-green-600 rounded-full px-2">
|
||||
{props.inventoryItem.currentTierIndex + 1}
|
||||
</span>
|
||||
</h3>
|
||||
<p className="text-sm">{props.inventoryItem.storeItem.description}</p>
|
||||
<div className="flex gap-4">
|
||||
<div className="flex-1">
|
||||
<p className="font-bold mt-4">Yield</p>
|
||||
<ul className="list-none">
|
||||
{getCurrentTier(props.inventoryItem.currentTierIndex).tier}
|
||||
</ul>
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<div className="flex">
|
||||
{props.inUse ? (
|
||||
<button className="bg-slate-400 text-slate-600 px-4 py-2 rounded-lg font-bold w-28 text-center cursor-not-allowed">
|
||||
Upgrade
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
onClick={() => props.handleIncrementTier(props.inventoryItem)}
|
||||
className="bg-slate-100 text-slate-900 px-4 py-2 rounded-lg font-bold w-28 text-center"
|
||||
>
|
||||
Upgrade
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardLayout>
|
||||
);
|
||||
};
|
||||
|
||||
export default InventoryItem;
|
33
src/app/Components/InventoryItemView.tsx
Normal file
33
src/app/Components/InventoryItemView.tsx
Normal file
@ -0,0 +1,33 @@
|
||||
import React from "react";
|
||||
import { IInventoryItem, IStakingSource } from "typings";
|
||||
import InventoryItem from "./InventoryItem";
|
||||
|
||||
const InventoryItemView = (props: {
|
||||
stakingSources: IStakingSource[] | null;
|
||||
inventoryItems: IInventoryItem[] | null | undefined;
|
||||
handleIncrementTier: (inventoryItem: IInventoryItem) => void;
|
||||
}) => {
|
||||
const inUse = (inventoryItemId: number) => {
|
||||
return props.stakingSources?.some((source) => {
|
||||
if (!source.inventoryItem) return false;
|
||||
return source.inventoryItem.id == inventoryItemId;
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="bg-slate-800 text-white p-4 rounded-lg">
|
||||
<h2 className="text-2xl font-bold mb-4">Your Inventory</h2>
|
||||
{props.inventoryItems &&
|
||||
props.inventoryItems.map((inventoryItem, id) => (
|
||||
<InventoryItem
|
||||
key={id}
|
||||
inventoryItem={inventoryItem}
|
||||
handleIncrementTier={props.handleIncrementTier}
|
||||
inUse={inUse(inventoryItem.id)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default InventoryItemView;
|
12
src/app/Components/LightBox.tsx
Normal file
12
src/app/Components/LightBox.tsx
Normal file
@ -0,0 +1,12 @@
|
||||
import React from "react";
|
||||
import { IBankAccount } from "typings";
|
||||
|
||||
const LightBox = (props: { bankAccounts: IBankAccount[] }) => {
|
||||
return (
|
||||
<div className="flex-1">
|
||||
<h1>Sell Your Resources</h1>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default LightBox;
|
@ -1,52 +0,0 @@
|
||||
import React from "react";
|
||||
import { Drill } from "typings";
|
||||
import CardLayout from "../Layouts/CardLayout";
|
||||
|
||||
const MiningItem = (props: { drill: Drill }) => {
|
||||
return (
|
||||
<CardLayout>
|
||||
<h3 className="text-xl font-bold mb-2">{props.drill.name}</h3>
|
||||
<p className="text-sm">{props.drill.description}</p>
|
||||
<div className="flex gap-4">
|
||||
<div className="flex-1">
|
||||
<p className="font-bold mt-4">Base Yield</p>
|
||||
<ul className="list-none">
|
||||
{props.drill.yield.map(
|
||||
(x, id) =>
|
||||
x.resource && (
|
||||
<li key={id}>
|
||||
<span>{x.baseYield}</span>{" "}
|
||||
<span className="text-teal-500 font-bold">
|
||||
{x.resource.name}
|
||||
</span>
|
||||
</li>
|
||||
)
|
||||
)}
|
||||
</ul>
|
||||
<p className="font-bold mt-4">Upgrades</p>
|
||||
<ul className="list-none">
|
||||
<li>
|
||||
<span className="">SkyMiner MK-III</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<p className="font-bold mt-4">Status</p>
|
||||
<ul className="list-none">
|
||||
<li>
|
||||
<span className="">Active Mining on Moon 1</span>
|
||||
</li>
|
||||
</ul>
|
||||
<p className="font-bold mt-4">Moon</p>
|
||||
<select className="block bg-white px-4 py-2 pr-8 rounded shadow text-black cursor-pointer">
|
||||
<option>Moon 1</option>
|
||||
<option>Moon 2</option>
|
||||
<option>Moon 3</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</CardLayout>
|
||||
);
|
||||
};
|
||||
|
||||
export default MiningItem;
|
@ -1,16 +0,0 @@
|
||||
import React from "react";
|
||||
import { Drill } from "typings";
|
||||
import MiningItem from "./MiningItem";
|
||||
|
||||
const MiningView = (props: { drills: Drill[] }) => {
|
||||
return (
|
||||
<div className="bg-slate-800 text-white p-4 rounded-lg">
|
||||
<h2 className="text-2xl font-bold mb-4">Drills</h2>
|
||||
{props.drills.map((drill, id) => (
|
||||
<MiningItem key={id} drill={drill} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default MiningView;
|
@ -1,57 +0,0 @@
|
||||
import React from "react";
|
||||
import { Moon } from "typings";
|
||||
import CardLayout from "../Layouts/CardLayout";
|
||||
|
||||
const MoonItem = (props: { moon: Moon }) => {
|
||||
return (
|
||||
<CardLayout>
|
||||
<h3 className="text-xl font-bold mb-2">{props.moon.name}</h3>
|
||||
<p className="text-sm">{props.moon.description}</p>
|
||||
<div className="flex gap-4">
|
||||
<div className="flex-1">
|
||||
<p className="font-bold mt-4">Mineral Composition</p>
|
||||
<ul className="list-none">
|
||||
{props.moon.resources.map((resource, id) => (
|
||||
<li key={id}>
|
||||
<span>{resource.name}</span>{" "}
|
||||
<span className="text-teal-500 font-bold">80%</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<p className="font-bold mt-4">Status</p>
|
||||
<ul className="list-none">
|
||||
<li className="flex">
|
||||
<span className="text-green-600 mr-1">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
strokeWidth="1.5"
|
||||
stroke="currentColor"
|
||||
className="w-6 h-6"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M9 12.75L11.25 15 15 9.75M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
Operational
|
||||
</li>
|
||||
</ul>
|
||||
<p className="font-bold mt-4">Drill</p>
|
||||
<ul className="list-none">
|
||||
<li>
|
||||
<span className="">Eclipse Drill</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</CardLayout>
|
||||
);
|
||||
};
|
||||
|
||||
export default MoonItem;
|
@ -1,76 +0,0 @@
|
||||
"use client";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Moon } from "typings";
|
||||
import MoonItem from "./MoonItem";
|
||||
|
||||
const MoonsView = (props: { moons: Moon[] }) => {
|
||||
const [isMining, setIsMining] = useState(false);
|
||||
const [miningTime, setMiningTime] = useState(3);
|
||||
const [farmedResources, setFarmedResources] = useState<string[]>([]);
|
||||
|
||||
const resources = ["Moonstone", "Lunarite", "Selenite", "Heliogem"];
|
||||
|
||||
useEffect(() => {
|
||||
if (isMining) {
|
||||
const intervalId = setInterval(() => {
|
||||
setMiningTime((prev) => prev - 1);
|
||||
}, 1000);
|
||||
|
||||
if (miningTime === 0) {
|
||||
setIsMining(false);
|
||||
setFarmedResources((prev) => [
|
||||
...prev,
|
||||
resources[Math.floor(Math.random() * (resources.length - 1 - 1))],
|
||||
]);
|
||||
clearInterval(intervalId);
|
||||
setMiningTime(3);
|
||||
}
|
||||
|
||||
return () => {
|
||||
clearInterval(intervalId);
|
||||
};
|
||||
}
|
||||
}, [isMining, miningTime]);
|
||||
|
||||
return (
|
||||
<div className="bg-slate-800 p-4 rounded-lg text-white">
|
||||
<h2 className="text-2xl mb-4 font-bold">Moons</h2>
|
||||
{props.moons.map((moon, id) => (
|
||||
<MoonItem key={id} moon={moon} />
|
||||
))}
|
||||
<div className="flex mt-5">
|
||||
<button
|
||||
className="bg-slate-100 text-slate-900 p-1 rounded-lg"
|
||||
onClick={() => setIsMining(true)}
|
||||
>
|
||||
Start mining
|
||||
</button>
|
||||
{isMining && <div className="p-1 ml-5">Mining time: {miningTime}</div>}
|
||||
</div>
|
||||
<div className="mt-5"> Here you will see your mined resources</div>
|
||||
{farmedResources.length <= 0 ? (
|
||||
<div>Start mining to get resources</div>
|
||||
) : (
|
||||
<>
|
||||
{farmedResources.map((farmedResource, index) => (
|
||||
<div className="my-2" key={index}>
|
||||
{farmedResource}
|
||||
<button
|
||||
className="bg-slate-100 text-slate-900 p-1 mx-2 rounded-lg"
|
||||
onClick={() =>
|
||||
setFarmedResources((prev) =>
|
||||
prev.filter((_, idx) => idx !== index)
|
||||
)
|
||||
}
|
||||
>
|
||||
Claim
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default MoonsView;
|
@ -1,51 +0,0 @@
|
||||
"use client";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { IResourceCardProps } from "typings";
|
||||
|
||||
const ResourceItem = (props: { resourceCardProps: IResourceCardProps }) => {
|
||||
const [totalYield, settotalYield] = useState(100);
|
||||
const [isMining, setIsMining] = useState(props.resourceCardProps.isMining);
|
||||
|
||||
useEffect(() => {
|
||||
if (isMining) {
|
||||
const intervalId = setInterval(() => {
|
||||
settotalYield((prev) => prev + props.resourceCardProps.yieldPerSecond);
|
||||
}, 1000);
|
||||
|
||||
return () => {
|
||||
clearInterval(intervalId);
|
||||
};
|
||||
}
|
||||
}, [totalYield]);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={
|
||||
props.resourceCardProps.resource.bgColorClass +
|
||||
" bg-gradient-to-br hover:bg-gradient-to-tr rounded-lg p-3"
|
||||
}
|
||||
>
|
||||
<div className="text-white">
|
||||
<span
|
||||
className={
|
||||
props.resourceCardProps.resource.fontColorClass + " font-bold"
|
||||
}
|
||||
>
|
||||
{props.resourceCardProps.resource.name}
|
||||
</span>
|
||||
<h3 className="text-2xl font-bold">
|
||||
{totalYield.toLocaleString("en-US", {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
})}{" "}
|
||||
kg
|
||||
</h3>
|
||||
<span className="text-sm">
|
||||
{props.resourceCardProps.yieldPerSecond * 3600} / hour
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ResourceItem;
|
@ -1,34 +0,0 @@
|
||||
"use client";
|
||||
import React from "react";
|
||||
import { Resource } from "typings";
|
||||
import ResourceItem from "./ResourceItem";
|
||||
|
||||
const ResourceView = (props: { resources: Resource[] }) => {
|
||||
return (
|
||||
<div className="p-4">
|
||||
<div className="grid grid-cols-5 gap-8">
|
||||
<div className="bg-green-800 rounded-lg p-3">
|
||||
<div className="text-white">
|
||||
<span className="text-green-400">Moonbucks</span>
|
||||
<h3 className="text-2xl font-bold">$5,342.23</h3>
|
||||
<button className="px-2 text-sm mt-1 rounded-lg font-bold bg-green-400 text-green-800">
|
||||
Sell Resources
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{props.resources.map((resource, id) => {
|
||||
const resourceCardProps = {
|
||||
resource: resource,
|
||||
isMining: true,
|
||||
yieldPerSecond: 0.15,
|
||||
};
|
||||
return (
|
||||
<ResourceItem key={id} resourceCardProps={resourceCardProps} />
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ResourceView;
|
187
src/app/Components/StakingSource.tsx
Normal file
187
src/app/Components/StakingSource.tsx
Normal file
@ -0,0 +1,187 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { IInventoryItem, IStakingSource, IClaimableResource } from "typings";
|
||||
import CardLayout from "../Layouts/CardLayout";
|
||||
|
||||
const StakingSource = (props: {
|
||||
stakingSource: IStakingSource;
|
||||
inventoryItems: IInventoryItem[] | null | undefined;
|
||||
handleAddItem: (
|
||||
inventoryItem: IInventoryItem,
|
||||
stakingSource: IStakingSource
|
||||
) => void;
|
||||
claimResource: (
|
||||
stakingSource: IStakingSource,
|
||||
claimedResource: IClaimableResource
|
||||
) => boolean;
|
||||
}) => {
|
||||
const [isMining, setIsMining] = useState(false);
|
||||
const [miningTime, setMiningTime] = useState(0);
|
||||
const [claimableResource, setClaimableResource] =
|
||||
useState<IClaimableResource | null>(null);
|
||||
const [activeInventoryItem, setActiveInventoryItem] =
|
||||
useState<IInventoryItem | null>(null);
|
||||
const [activeTier, setActiveTier] = useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
if (isMining && activeInventoryItem) {
|
||||
const intervalId = setInterval(() => {
|
||||
setMiningTime((prev) => prev - 1);
|
||||
}, 1000);
|
||||
|
||||
if (miningTime === 0) {
|
||||
setIsMining(false);
|
||||
|
||||
// Get a random resource from available wells
|
||||
let randResource =
|
||||
props.stakingSource.resourceWells[
|
||||
Math.floor(Math.random() * props.stakingSource.resourceWells.length)
|
||||
];
|
||||
|
||||
// Get yield based on the tier
|
||||
const totalYield = activeInventoryItem.storeItem.tiers[activeTier].tier;
|
||||
|
||||
// Construct the claimableResource
|
||||
const claimableResource: IClaimableResource = {
|
||||
resourceType: randResource.resourceType,
|
||||
balance: totalYield,
|
||||
};
|
||||
|
||||
setClaimableResource(claimableResource);
|
||||
clearInterval(intervalId);
|
||||
setMiningTime(activeInventoryItem.storeItem.timeToClaim);
|
||||
}
|
||||
|
||||
return () => {
|
||||
clearInterval(intervalId);
|
||||
};
|
||||
}
|
||||
}, [isMining, miningTime]);
|
||||
|
||||
const isChecked = (item: IInventoryItem) => {
|
||||
if (!props.stakingSource.inventoryItem) return false;
|
||||
return item.id === props.stakingSource.inventoryItem.id;
|
||||
};
|
||||
|
||||
const handleStartMining = () => {
|
||||
if (props.stakingSource.inventoryItem) {
|
||||
setActiveInventoryItem(props.stakingSource.inventoryItem);
|
||||
setMiningTime(props.stakingSource.inventoryItem.storeItem.timeToClaim);
|
||||
setActiveTier(props.stakingSource.inventoryItem.currentTierIndex);
|
||||
setIsMining(true);
|
||||
}
|
||||
};
|
||||
|
||||
const handleClaim = () => {
|
||||
if (!claimableResource) return;
|
||||
if (props.claimResource(props.stakingSource, claimableResource)) {
|
||||
setClaimableResource(null);
|
||||
}
|
||||
};
|
||||
|
||||
const renderButton = () => {
|
||||
if (props.stakingSource.inventoryItem) {
|
||||
if (!claimableResource && !isMining) {
|
||||
return (
|
||||
<button
|
||||
onClick={() => handleStartMining()}
|
||||
className="bg-slate-100 text-slate-900 px-4 py-2 rounded-lg font-bold text-center"
|
||||
>
|
||||
Start Mining
|
||||
</button>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (isMining)
|
||||
return (
|
||||
<button className="bg-slate-400 text-slate-600 px-4 py-2 rounded-lg font-bold w-28 text-center cursor-not-allowed">
|
||||
{miningTime}
|
||||
</button>
|
||||
);
|
||||
|
||||
if (claimableResource) {
|
||||
return (
|
||||
<button
|
||||
onClick={() => handleClaim()}
|
||||
className="bg-slate-100 text-slate-900 px-4 py-2 rounded-lg font-bold text-center"
|
||||
>
|
||||
Claim
|
||||
</button>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<CardLayout>
|
||||
<h3 className="text-xl font-bold mb-2">{props.stakingSource.name}</h3>
|
||||
<p className="text-sm">{props.stakingSource.description}</p>
|
||||
<div className="flex gap-4">
|
||||
<div className="flex-1">
|
||||
<p className="font-bold mt-4">Resources</p>
|
||||
<ul className="list-none">
|
||||
{props.stakingSource.resourceWells.map((resourceWell, id) => (
|
||||
<li key={id}>
|
||||
<span>{resourceWell.resourceType.name}</span>{" "}
|
||||
<span className="text-teal-500 font-bold">
|
||||
{resourceWell.supply}
|
||||
</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<p className="font-bold mt-4">Status</p>
|
||||
<ul className="list-none">
|
||||
<li className="flex">
|
||||
<span className="text-green-600 mr-1">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
strokeWidth="1.5"
|
||||
stroke="currentColor"
|
||||
className="w-6 h-6"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M9 12.75L11.25 15 15 9.75M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
Operational
|
||||
</li>
|
||||
</ul>
|
||||
<p className="font-bold mt-4">Equipment</p>
|
||||
{!isMining
|
||||
? props.inventoryItems &&
|
||||
props.inventoryItems.map((item, id) => (
|
||||
<div key={id}>
|
||||
<input
|
||||
type="radio"
|
||||
name="inventoryItem"
|
||||
value={item.id}
|
||||
checked={isChecked(item)}
|
||||
onChange={() =>
|
||||
props.handleAddItem(item, props.stakingSource)
|
||||
}
|
||||
/>
|
||||
<label htmlFor={item.id.toString()}>
|
||||
{item.storeItem.name}{" "}
|
||||
<span className="bg-white text-black rounded-full px-2">
|
||||
{item.currentTierIndex + 1}
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
))
|
||||
: props.stakingSource.inventoryItem && (
|
||||
<p>{props.stakingSource.inventoryItem.storeItem.name}</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{renderButton()}
|
||||
</CardLayout>
|
||||
);
|
||||
};
|
||||
|
||||
export default StakingSource;
|
35
src/app/Components/StakingSourcesView.tsx
Normal file
35
src/app/Components/StakingSourcesView.tsx
Normal file
@ -0,0 +1,35 @@
|
||||
"use client";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { IInventoryItem, IStakingSource, IClaimableResource } from "typings";
|
||||
import StakingSource from "./StakingSource";
|
||||
|
||||
const StakingSourcesView = (props: {
|
||||
stakingSources: IStakingSource[] | null;
|
||||
inventoryItems: IInventoryItem[] | null | undefined;
|
||||
handleAddItem: (
|
||||
inventoryItem: IInventoryItem,
|
||||
stakingSource: IStakingSource
|
||||
) => void;
|
||||
claimResource: (
|
||||
stakingSource: IStakingSource,
|
||||
claimedResource: IClaimableResource
|
||||
) => boolean;
|
||||
}) => {
|
||||
return (
|
||||
<div className="bg-slate-800 p-4 rounded-lg text-white">
|
||||
<h2 className="text-2xl mb-4 font-bold">Your Moons</h2>
|
||||
{props.stakingSources &&
|
||||
props.stakingSources.map((stakingSource, id) => (
|
||||
<StakingSource
|
||||
key={id}
|
||||
stakingSource={stakingSource}
|
||||
inventoryItems={props.inventoryItems}
|
||||
handleAddItem={props.handleAddItem}
|
||||
claimResource={props.claimResource}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default StakingSourcesView;
|
60
src/app/Components/StoreItem.tsx
Normal file
60
src/app/Components/StoreItem.tsx
Normal file
@ -0,0 +1,60 @@
|
||||
"use client";
|
||||
import React, { useState } from "react";
|
||||
import { IStoreItem, IInventoryItem } from "typings";
|
||||
import CommonCardLayout from "../Layouts/CommonCardLayout";
|
||||
|
||||
const StoreItem = (props: {
|
||||
storeItem: IStoreItem;
|
||||
handleBuy: (storeItem: IStoreItem) => void;
|
||||
owned: boolean | undefined;
|
||||
}) => {
|
||||
return (
|
||||
<CommonCardLayout>
|
||||
<div className="flex gap-4">
|
||||
<div className="flex-1">
|
||||
{props.owned ? (
|
||||
<span className="text-green-600 font-bold">Owned</span>
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
<h3 className="text-xl font-bold mb-2">{props.storeItem.name}</h3>
|
||||
<p className="text-sm mb-3">{props.storeItem.description}</p>
|
||||
<p className="text-lg font-bold">$ {props.storeItem.price}</p>
|
||||
<p className="font-bold mt-3">Tier Upgrades</p>
|
||||
<table className="table-auto text-center">
|
||||
<tr>
|
||||
<th className="border border-white py-1 px-3 w-18">Tier</th>
|
||||
<th className="border border-white py-1 px-3 w-28">Price</th>
|
||||
</tr>
|
||||
{props.storeItem.tiers.map((tier) => (
|
||||
<tr>
|
||||
<td className="border border-white py-1 px-3 w-18">
|
||||
{tier.tier}
|
||||
</td>
|
||||
<td className="border border-white py-1 px-3 w-28">
|
||||
${tier.price}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</table>
|
||||
</div>
|
||||
<div className="flex items-center h-100">
|
||||
{props.owned ? (
|
||||
<button className="bg-slate-400 text-slate-600 px-4 py-2 rounded-lg font-bold w-28 text-center cursor-not-allowed">
|
||||
Owned
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
onClick={() => props.handleBuy(props.storeItem)}
|
||||
className="bg-slate-100 text-slate-900 px-4 py-2 rounded-lg font-bold w-28 text-center"
|
||||
>
|
||||
Buy
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</CommonCardLayout>
|
||||
);
|
||||
};
|
||||
|
||||
export default StoreItem;
|
41
src/app/Components/StoreItemView.tsx
Normal file
41
src/app/Components/StoreItemView.tsx
Normal file
@ -0,0 +1,41 @@
|
||||
import React from "react";
|
||||
import { IInventoryItem, IStoreItem } from "typings";
|
||||
import StoreItem from "./StoreItem";
|
||||
|
||||
const StoreItemView = (props: {
|
||||
storeItems: IStoreItem[] | null;
|
||||
inventoryItems: IInventoryItem[] | null | undefined;
|
||||
handleBuy: (storeItem: IStoreItem) => void;
|
||||
}) => {
|
||||
const isOwned = (storeItemId: number) => {
|
||||
return props.inventoryItems?.some(
|
||||
(item) => item.storeItem.id == storeItemId
|
||||
);
|
||||
};
|
||||
|
||||
const storeItemsToRender = props.storeItems?.map((storeItem) => {
|
||||
return {
|
||||
...storeItem,
|
||||
owned: isOwned(storeItem.id),
|
||||
};
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="bg-slate-800 p-4 rounded-lg">
|
||||
<h2 className="text-2xl font-bold mb-4 text-white">Store</h2>
|
||||
{storeItemsToRender &&
|
||||
storeItemsToRender
|
||||
.sort((a, b) => (a.owned === b.owned ? 0 : a.owned ? 1 : -1))
|
||||
.map((storeItem, id) => (
|
||||
<StoreItem
|
||||
key={id}
|
||||
storeItem={storeItem}
|
||||
handleBuy={props.handleBuy}
|
||||
owned={storeItem.owned}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default StoreItemView;
|
@ -1,57 +0,0 @@
|
||||
import React from "react";
|
||||
import { Upgrade } from "typings";
|
||||
import CardLayout from "../Layouts/CardLayout";
|
||||
import RareCardLayout from "../Layouts/RareCardLayout";
|
||||
import EpicCardLayout from "../Layouts/EpicCardLayout";
|
||||
import PrestineCardLayout from "../Layouts/PrestineCardLayout";
|
||||
import CommonCardLayout from "../Layouts/CommonCardLayout";
|
||||
|
||||
const UpgradeItem = (props: { upgrade: Upgrade }) => {
|
||||
return (
|
||||
<CommonCardLayout>
|
||||
<div className="flex gap-4">
|
||||
<div className="flex-1">
|
||||
{props.upgrade.owned && (
|
||||
<span className="text-green-600 font-bold">Owned</span>
|
||||
)}
|
||||
<h3 className="text-xl font-bold mb-2">{props.upgrade.name}</h3>
|
||||
<p className="text-sm mb-3">{props.upgrade.description}</p>
|
||||
{!props.upgrade.owned && (
|
||||
<p className="text-lg font-bold">${props.upgrade.price}</p>
|
||||
)}
|
||||
<p className="font-bold mt-3">Modifies</p>
|
||||
<ul className="list-none">
|
||||
{props.upgrade.modifiers.map((modifier, id) => (
|
||||
<li key={id}>
|
||||
<span>{modifier.yield}</span>{" "}
|
||||
<span
|
||||
className={modifier.resource?.fontColorClass + " font-bold"}
|
||||
>
|
||||
{modifier.resource && modifier.resource.name}
|
||||
</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
{!props.upgrade.owned ? (
|
||||
<div className="flex items-center h-100">
|
||||
<button className=" bg-slate-100 text-slate-900 px-4 py-2 rounded-lg font-bold w-28 text-center">
|
||||
Buy
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex-none items-center h-100">
|
||||
<p className="font-bold mt-4">Drill</p>
|
||||
<select className="block bg-white px-4 py-2 pr-8 rounded shadow text-black cursor-pointer">
|
||||
<option>Drill 1</option>
|
||||
<option>Drill 2</option>
|
||||
<option>Drill 3</option>
|
||||
</select>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</CommonCardLayout>
|
||||
);
|
||||
};
|
||||
|
||||
export default UpgradeItem;
|
@ -1,16 +0,0 @@
|
||||
import React from "react";
|
||||
import { Upgrade } from "typings";
|
||||
import UpgradeItem from "./UpgradeItem";
|
||||
|
||||
const UpgradeView = (props: { upgrades: Upgrade[] }) => {
|
||||
return (
|
||||
<div className="bg-slate-800 p-4 rounded-lg">
|
||||
<h2 className="text-2xl font-bold mb-4 text-white">Upgrades</h2>
|
||||
{props.upgrades.map((upgrade, id) => (
|
||||
<UpgradeItem key={id} upgrade={upgrade} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default UpgradeView;
|
375
src/app/page.tsx
375
src/app/page.tsx
@ -1,97 +1,382 @@
|
||||
import MiningView from "./Components/MiningView";
|
||||
import MoonsView from "./Components/MoonsView";
|
||||
import ResourceView from "./Components/ResourceView";
|
||||
import UpgradeView from "./Components/UpgradeView";
|
||||
import { Resource, Upgrade, Drill, Moon } from "typings";
|
||||
"use client";
|
||||
import { useState, useEffect } from "react";
|
||||
|
||||
import getObjectFromArray from "@/utils/helpers";
|
||||
import InventoryItemView from "./Components/InventoryItemView";
|
||||
import StakingSourcesView from "./Components/StakingSourcesView";
|
||||
import BankAccountsView from "./Components/BankAccountsView";
|
||||
import StoreItemView from "./Components/StoreItemView";
|
||||
import LightBox from "./Components/LightBox";
|
||||
import {
|
||||
IResourceWell,
|
||||
IResourceType,
|
||||
IStoreItem,
|
||||
IInventoryItem,
|
||||
IStakingSource,
|
||||
IBankAccount,
|
||||
IClaimableResource,
|
||||
} from "typings";
|
||||
|
||||
const resources: Resource[] = [
|
||||
import { getObjectFromArray } from "../utils/helpers";
|
||||
|
||||
const DBresourceTypes: IResourceType[] = [
|
||||
{
|
||||
id: 1,
|
||||
name: "Moonstone",
|
||||
fontColorClass: "text-teal-400",
|
||||
bgColorClass: "from-teal-800 to-teal-900",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: "Lunarite",
|
||||
fontColorClass: "text-cyan-400",
|
||||
bgColorClass: "from-cyan-800 to-cyan-900",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: "Selenite",
|
||||
fontColorClass: "text-purple-300",
|
||||
bgColorClass: "from-purple-800 to-purple-900",
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: "Heliogem",
|
||||
fontColorClass: "text-rose-300",
|
||||
bgColorClass: "from-rose-800 to-rose-900",
|
||||
},
|
||||
];
|
||||
|
||||
const upgrades: Upgrade[] = [
|
||||
const DBresourceWells: IResourceWell[] = [
|
||||
{
|
||||
name: "SkyMiner MK-III",
|
||||
description:
|
||||
"A cutting-edge mining robot equipped with advanced sensors, capable of identifying and extracting even the rarest minerals on the moon's surface.",
|
||||
price: 500,
|
||||
modifiers: [
|
||||
{
|
||||
resource: getObjectFromArray(resources, "name", "Moonstone"),
|
||||
yield: 1,
|
||||
},
|
||||
],
|
||||
owned: false,
|
||||
},
|
||||
{
|
||||
name: "Astro-Excavator 5000",
|
||||
description:
|
||||
"A state-of-the-art excavation machine, capable of digging deeper and faster than any other equipment on the market.",
|
||||
price: 250,
|
||||
modifiers: [
|
||||
{
|
||||
resource: getObjectFromArray(resources, "name", "Selenite"),
|
||||
yield: 1,
|
||||
},
|
||||
],
|
||||
owned: true,
|
||||
id: 1,
|
||||
resourceType: DBresourceTypes[0],
|
||||
supply: 10000,
|
||||
},
|
||||
];
|
||||
|
||||
const drills: Drill[] = [
|
||||
const DBstoreItems: IStoreItem[] = [
|
||||
{
|
||||
id: 1,
|
||||
name: "Eclipse Drill",
|
||||
description:
|
||||
"A compact and lightweight drill designed for use in tight and narrow mining tunnels.",
|
||||
price: 1500,
|
||||
yield: [
|
||||
price: 225,
|
||||
timeToClaim: 3,
|
||||
tiers: [
|
||||
{
|
||||
resource: getObjectFromArray(resources, "name", "Lunarite"),
|
||||
baseYield: 250,
|
||||
tier: 500,
|
||||
price: 50,
|
||||
},
|
||||
{
|
||||
tier: 600,
|
||||
price: 75,
|
||||
},
|
||||
{
|
||||
tier: 700,
|
||||
price: 100,
|
||||
},
|
||||
{
|
||||
tier: 800,
|
||||
price: 150,
|
||||
},
|
||||
{
|
||||
tier: 900,
|
||||
price: 200,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: "Moon Saw 2000",
|
||||
description:
|
||||
"A compact and lightweight drill designed for use in tight and narrow mining tunnels.",
|
||||
price: 100,
|
||||
timeToClaim: 3,
|
||||
tiers: [
|
||||
{
|
||||
tier: 500,
|
||||
price: 50,
|
||||
},
|
||||
{
|
||||
tier: 550,
|
||||
price: 75,
|
||||
},
|
||||
{
|
||||
tier: 600,
|
||||
price: 100,
|
||||
},
|
||||
{
|
||||
tier: 650,
|
||||
price: 150,
|
||||
},
|
||||
{
|
||||
tier: 700,
|
||||
price: 200,
|
||||
},
|
||||
],
|
||||
upgrades: null,
|
||||
},
|
||||
];
|
||||
|
||||
const moons: Moon[] = [
|
||||
const DBinventoryItems: IInventoryItem[] = [];
|
||||
|
||||
const DBstakingSources: IStakingSource[] = [
|
||||
{
|
||||
id: 1,
|
||||
name: "Selene's Eye",
|
||||
description:
|
||||
"Selene's Eye is a large and mysterious moon, named for its distinctive appearance - a bright, glowing eye that seems to stare out from the void of space",
|
||||
resources: resources,
|
||||
drill: null,
|
||||
resourceWells: DBresourceWells,
|
||||
inventoryItem: null,
|
||||
},
|
||||
];
|
||||
|
||||
const DBbankAccounts: IBankAccount[] = [
|
||||
{
|
||||
id: 1,
|
||||
resourceType: DBresourceTypes[0],
|
||||
balance: 0,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
resourceType: DBresourceTypes[1],
|
||||
balance: 0,
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
resourceType: DBresourceTypes[2],
|
||||
balance: 0,
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
resourceType: DBresourceTypes[3],
|
||||
balance: 0,
|
||||
},
|
||||
];
|
||||
|
||||
export default function Home() {
|
||||
const [inventoryItems, setInventoryItems] = useState<
|
||||
IInventoryItem[] | null | undefined
|
||||
>([]);
|
||||
const [stakingSources, setStakingSources] = useState<IStakingSource[] | null>(
|
||||
[]
|
||||
);
|
||||
const [bankAccounts, setBankAccounts] = useState<IBankAccount[]>([]);
|
||||
const [storeItems, setStoreItems] = useState<IStoreItem[]>([]);
|
||||
const [lightBoxIsActive, setLightBoxIsActive] = useState(false);
|
||||
|
||||
// Connect to DB here
|
||||
useEffect(() => {
|
||||
// get the user who is currently logged in
|
||||
const loggedInUser = 1;
|
||||
|
||||
const fetchInventoryItems = async () => {
|
||||
// old
|
||||
setInventoryItems(DBinventoryItems);
|
||||
// new
|
||||
// const response = await fetch(`/api/user/${loggedInUser}/inventory-items`);
|
||||
// const DBInventoryItems = await response.json();
|
||||
// setInventoryItems(DBInventoryItems.message);
|
||||
};
|
||||
|
||||
const fetchStakingSources = async () => {
|
||||
// old
|
||||
setStakingSources(DBstakingSources);
|
||||
// new
|
||||
// const response = await fetch(`/api/user/${loggedInUser}/staking-sources`);
|
||||
// const DBStakingSources = await response.json();
|
||||
// setStakingSources(DBStakingSources);
|
||||
};
|
||||
|
||||
const fetchBankAccounts = async () => {
|
||||
// old
|
||||
setBankAccounts(DBbankAccounts);
|
||||
// new
|
||||
// const response = await fetch(`/api/user/${loggedInUser}/bank-accounts`);
|
||||
// const DBBankAccounts = await response.json();
|
||||
// setStakingSources(DBBankAccounts.message);
|
||||
};
|
||||
|
||||
const fetchStoreItems = async () => {
|
||||
// old
|
||||
setStoreItems(DBstoreItems);
|
||||
// new
|
||||
// const response = await fetch(`/api/store/items`);
|
||||
// const DBStoreItems = await response.json();
|
||||
// setStoreItems(DBStoreItems.message);
|
||||
};
|
||||
|
||||
fetchBankAccounts();
|
||||
fetchStakingSources();
|
||||
fetchInventoryItems();
|
||||
fetchStoreItems();
|
||||
}, []);
|
||||
|
||||
// Use effect to update the items on staking sources when inventoryItems are updated
|
||||
useEffect(() => {
|
||||
const updateItemsOnStakingSources = () => {
|
||||
const updatedStakingSources = stakingSources?.map((source) => {
|
||||
const item = inventoryItems?.find(
|
||||
(item) => source.inventoryItem?.id === item.id
|
||||
);
|
||||
if (item) {
|
||||
return { ...source, inventoryItem: item };
|
||||
} else {
|
||||
return source;
|
||||
}
|
||||
});
|
||||
updatedStakingSources && setStakingSources(updatedStakingSources);
|
||||
};
|
||||
if (stakingSources && stakingSources?.length > 0) {
|
||||
updateItemsOnStakingSources();
|
||||
}
|
||||
}, [inventoryItems]);
|
||||
|
||||
const handleAddItem = (
|
||||
inventoryItem: IInventoryItem,
|
||||
stakingSource: IStakingSource
|
||||
) => {
|
||||
const newStakingSources = stakingSources?.map((source) => {
|
||||
if (source.id === stakingSource.id) {
|
||||
return { ...source, inventoryItem: inventoryItem };
|
||||
} else {
|
||||
return source;
|
||||
}
|
||||
});
|
||||
|
||||
if (newStakingSources) {
|
||||
setStakingSources(newStakingSources);
|
||||
}
|
||||
};
|
||||
|
||||
const handleBuy = (storeItem: IStoreItem) => {
|
||||
const hasItem = inventoryItems?.some((item) => {
|
||||
item.storeItem.id === storeItem.id;
|
||||
});
|
||||
|
||||
if (hasItem) return;
|
||||
|
||||
const getNewIndex = () => {
|
||||
if (!inventoryItems) return 0;
|
||||
return inventoryItems.length;
|
||||
};
|
||||
|
||||
const newInventoryItem = {
|
||||
id: getNewIndex(),
|
||||
stakingSource: null,
|
||||
storeItem: storeItem,
|
||||
currentTierIndex: 0,
|
||||
};
|
||||
|
||||
if (inventoryItems && inventoryItems !== undefined) {
|
||||
setInventoryItems([...inventoryItems, newInventoryItem]);
|
||||
}
|
||||
};
|
||||
|
||||
const claimResource = (
|
||||
stakingSource: IStakingSource,
|
||||
claimedResource: IClaimableResource
|
||||
): boolean => {
|
||||
// Known bug: If a Inventory is already selected, and then upgraded, claiming will be from old tier
|
||||
const bankAccount = getBankAccount(claimedResource.resourceType);
|
||||
if (!bankAccount) return false;
|
||||
|
||||
decrementResourceWell(stakingSource, claimedResource);
|
||||
incrementBalance(bankAccount, claimedResource.balance);
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
const decrementResourceWell = (
|
||||
stakingSource: IStakingSource,
|
||||
claimedResource: IClaimableResource
|
||||
) => {
|
||||
const updatedResourceWells = stakingSource.resourceWells.map((well) => {
|
||||
if (well.resourceType.name === claimedResource.resourceType.name) {
|
||||
return { ...well, supply: well.supply - claimedResource.balance };
|
||||
} else {
|
||||
return well;
|
||||
}
|
||||
});
|
||||
|
||||
const updatedStakingSources = stakingSources?.map((source) => {
|
||||
if (source.id === stakingSource.id) {
|
||||
return { ...source, resourceWells: updatedResourceWells };
|
||||
} else {
|
||||
return source;
|
||||
}
|
||||
});
|
||||
|
||||
updatedStakingSources && setStakingSources(updatedStakingSources);
|
||||
};
|
||||
|
||||
const decrementBalance = () => {};
|
||||
|
||||
const incrementBalance = (bankAccount: IBankAccount, amount: number) => {
|
||||
const updatedBankAccounts = bankAccounts.map((account) => {
|
||||
if (account.id === bankAccount.id) {
|
||||
return { ...account, balance: account.balance + amount };
|
||||
} else {
|
||||
return account;
|
||||
}
|
||||
});
|
||||
setBankAccounts(updatedBankAccounts);
|
||||
};
|
||||
|
||||
const getStoreItemConfiguration = () => {};
|
||||
|
||||
const getBankAccount = (resourceType: IResourceType) => {
|
||||
return getObjectFromArray(bankAccounts, "resourceType", resourceType);
|
||||
};
|
||||
|
||||
const handleIncrementTier = (inventoryItem: IInventoryItem) => {
|
||||
console.log("Incrementing Tier");
|
||||
// Check user has balance
|
||||
// Decrement user balance
|
||||
|
||||
if (inventoryItem.currentTierIndex === 4) return;
|
||||
|
||||
const updatedInventoryItems = inventoryItems?.map((item) => {
|
||||
if (item.id === inventoryItem.id) {
|
||||
return { ...item, currentTierIndex: item.currentTierIndex + 1 };
|
||||
} else {
|
||||
return item;
|
||||
}
|
||||
});
|
||||
|
||||
if (updatedInventoryItems !== undefined)
|
||||
setInventoryItems(updatedInventoryItems);
|
||||
};
|
||||
|
||||
const handleSetLightBox = () => {
|
||||
setLightBoxIsActive(!lightBoxIsActive);
|
||||
};
|
||||
|
||||
const renderLightBox = () => {
|
||||
if (lightBoxIsActive) return <LightBox bankAccounts={bankAccounts} />;
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<ResourceView resources={resources} />
|
||||
<BankAccountsView
|
||||
bankAccounts={bankAccounts}
|
||||
setLightBoxIsActive={handleSetLightBox}
|
||||
/>
|
||||
<div className="grid grid-cols-3 gap-8 p-4">
|
||||
<MoonsView moons={moons} />
|
||||
<MiningView drills={drills} />
|
||||
<UpgradeView upgrades={upgrades} />
|
||||
<StakingSourcesView
|
||||
stakingSources={stakingSources}
|
||||
inventoryItems={inventoryItems}
|
||||
handleAddItem={handleAddItem}
|
||||
claimResource={claimResource}
|
||||
/>
|
||||
<InventoryItemView
|
||||
stakingSources={stakingSources}
|
||||
inventoryItems={inventoryItems}
|
||||
handleIncrementTier={handleIncrementTier}
|
||||
/>
|
||||
<StoreItemView
|
||||
storeItems={storeItems}
|
||||
inventoryItems={inventoryItems}
|
||||
handleBuy={handleBuy}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
@ -1,12 +0,0 @@
|
||||
import type { NextApiRequest, NextApiResponse } from "next";
|
||||
|
||||
type Data = {
|
||||
name: string;
|
||||
};
|
||||
|
||||
export default function handler(
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse<Data>
|
||||
) {
|
||||
res.status(200).json({ name: "Moon Miners!" });
|
||||
}
|
80
src/pages/api/store/items.ts
Normal file
80
src/pages/api/store/items.ts
Normal file
@ -0,0 +1,80 @@
|
||||
import type { NextApiRequest, NextApiResponse } from "next";
|
||||
import { IStoreItem } from "typings";
|
||||
|
||||
export default function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
if (req.method === "GET") {
|
||||
// query all store items
|
||||
const storeItems: IStoreItem[] = [
|
||||
{
|
||||
id: 1,
|
||||
name: "Eclipse Drill",
|
||||
description:
|
||||
"A compact and lightweight drill designed for use in tight and narrow mining tunnels.",
|
||||
price: 225,
|
||||
timeToClaim: 3,
|
||||
tiers: [
|
||||
{
|
||||
tier: 500,
|
||||
price: 50,
|
||||
},
|
||||
{
|
||||
tier: 600,
|
||||
price: 75,
|
||||
},
|
||||
{
|
||||
tier: 700,
|
||||
price: 100,
|
||||
},
|
||||
{
|
||||
tier: 800,
|
||||
price: 150,
|
||||
},
|
||||
{
|
||||
tier: 900,
|
||||
price: 200,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: "Moon Saw 2000",
|
||||
description:
|
||||
"A compact and lightweight drill designed for use in tight and narrow mining tunnels.",
|
||||
price: 100,
|
||||
timeToClaim: 3,
|
||||
tiers: [
|
||||
{
|
||||
tier: 500,
|
||||
price: 50,
|
||||
},
|
||||
{
|
||||
tier: 550,
|
||||
price: 75,
|
||||
},
|
||||
{
|
||||
tier: 600,
|
||||
price: 100,
|
||||
},
|
||||
{
|
||||
tier: 650,
|
||||
price: 150,
|
||||
},
|
||||
{
|
||||
tier: 700,
|
||||
price: 200,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
// if no store items send empty array
|
||||
if (!storeItems) return res.status(200).json({ message: [] });
|
||||
|
||||
// if store items found send store items
|
||||
return res.status(200).json({ message: storeItems });
|
||||
}
|
||||
} catch (error) {
|
||||
res.status(500).json({ message: "Unexpexted server error" });
|
||||
}
|
||||
}
|
73
src/pages/api/user/[userId]/bank-accounts.ts
Normal file
73
src/pages/api/user/[userId]/bank-accounts.ts
Normal file
@ -0,0 +1,73 @@
|
||||
import type { NextApiRequest, NextApiResponse } from "next";
|
||||
import { IBankAccount, IResourceType } from "typings";
|
||||
|
||||
export default function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
if (req.method === "GET") {
|
||||
const { userId } = req.query;
|
||||
|
||||
// query db for user ID
|
||||
const user = true;
|
||||
// if user not found send error
|
||||
if (!user) return res.status(404).json({ message: "User not found" });
|
||||
|
||||
// if user found query all bank accounts attached to this user
|
||||
const DBresourceTypes: IResourceType[] = [
|
||||
{
|
||||
id: 1,
|
||||
name: "Moonstone",
|
||||
fontColorClass: "text-teal-400",
|
||||
bgColorClass: "from-teal-800 to-teal-900",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: "Lunarite",
|
||||
fontColorClass: "text-cyan-400",
|
||||
bgColorClass: "from-cyan-800 to-cyan-900",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: "Selenite",
|
||||
fontColorClass: "text-purple-300",
|
||||
bgColorClass: "from-purple-800 to-purple-900",
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: "Heliogem",
|
||||
fontColorClass: "text-rose-300",
|
||||
bgColorClass: "from-rose-800 to-rose-900",
|
||||
},
|
||||
];
|
||||
const bankAccounts: IBankAccount[] = [
|
||||
{
|
||||
id: 1,
|
||||
resourceType: DBresourceTypes[0],
|
||||
balance: 0,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
resourceType: DBresourceTypes[1],
|
||||
balance: 0,
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
resourceType: DBresourceTypes[2],
|
||||
balance: 0,
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
resourceType: DBresourceTypes[3],
|
||||
balance: 0,
|
||||
},
|
||||
];
|
||||
|
||||
// if bank accounts not found send empty array
|
||||
if (!bankAccounts) return res.status(200).json({ message: [] });
|
||||
|
||||
// if bank accounts found send bank accounts
|
||||
return res.status(200).json({ message: bankAccounts });
|
||||
}
|
||||
} catch (error) {
|
||||
res.status(500).json({ message: "Unexpexted server error" });
|
||||
}
|
||||
}
|
32
src/pages/api/user/[userId]/inventory-items.ts
Normal file
32
src/pages/api/user/[userId]/inventory-items.ts
Normal file
@ -0,0 +1,32 @@
|
||||
import type { NextApiRequest, NextApiResponse } from "next";
|
||||
import { IInventoryItem } from "typings";
|
||||
|
||||
export default function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
if (req.method === "GET") {
|
||||
const { userId } = req.query;
|
||||
|
||||
// query db for user ID
|
||||
const user = true;
|
||||
// if user not found send error
|
||||
if (!user) return res.status(404).json({ message: "User not found" });
|
||||
|
||||
// if user found query all inventory items attached to this user
|
||||
const inventoryItems: IInventoryItem[] = [
|
||||
{
|
||||
id: 123,
|
||||
storeItem: "Example StoreItem",
|
||||
currentTierIndex: 0,
|
||||
},
|
||||
];
|
||||
|
||||
// if inventory items not found send empty array
|
||||
if (!inventoryItems) return res.status(200).json({ message: [] });
|
||||
|
||||
// if inventory items found send inventory items
|
||||
return res.status(200).json({ message: inventoryItems });
|
||||
}
|
||||
} catch (error) {
|
||||
res.status(500).json({ message: "Unexpexted server error" });
|
||||
}
|
||||
}
|
35
src/pages/api/user/[userId]/staking-sources.ts
Normal file
35
src/pages/api/user/[userId]/staking-sources.ts
Normal file
@ -0,0 +1,35 @@
|
||||
import type { NextApiRequest, NextApiResponse } from "next";
|
||||
import { IStakingSource } from "typings";
|
||||
|
||||
export default function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
if (req.method === "GET") {
|
||||
const { userId } = req.query;
|
||||
|
||||
// query db for user ID
|
||||
const user = true;
|
||||
// if user not found send error
|
||||
if (!user) return res.status(404).json({ message: "User not found" });
|
||||
|
||||
// if user found query all staking sources attached to this user
|
||||
const stakingSources: IStakingSource[] = [
|
||||
{
|
||||
id: 1,
|
||||
name: "Selene's Eye",
|
||||
description:
|
||||
"Selene's Eye is a large and mysterious moon, named for its distinctive appearance - a bright, glowing eye that seems to stare out from the void of space",
|
||||
resourceWells: "Example DBresourceWells",
|
||||
inventoryItem: null,
|
||||
},
|
||||
];
|
||||
|
||||
// if staking sources not found send empty array
|
||||
if (!stakingSources) return res.status(200).json({ message: [] });
|
||||
|
||||
// if staking sources found send staking sources
|
||||
return res.status(200).json({ message: stakingSources });
|
||||
}
|
||||
} catch (error) {
|
||||
res.status(500).json({ message: "Unexpexted server error" });
|
||||
}
|
||||
}
|
@ -1,3 +1,9 @@
|
||||
export default function getObjectFromArray<T>(array: T[], key: keyof T, value: any): T | undefined {
|
||||
export function getObjectFromArray<T>(array: T[], key: keyof T, value: any): T | undefined {
|
||||
return array.find(obj => obj[key] === value);
|
||||
}
|
||||
|
||||
export function pushElementToArray<T>(array: T[], element: T): T[] {
|
||||
const newArray = [...array]; // create a new array
|
||||
newArray.push(element); // add the new element to the array
|
||||
return newArray; // return the new array
|
||||
}
|
@ -1,9 +1,12 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noEmit": true,
|
||||
@ -21,9 +24,19 @@
|
||||
],
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
"@/*": [
|
||||
"./src/*"
|
||||
]
|
||||
},
|
||||
"skipLibCheck": true
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||
"exclude": ["node_modules"]
|
||||
"include": [
|
||||
"next-env.d.ts",
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
".next/types/**/*.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
}
|
||||
|
73
typings.d.ts
vendored
73
typings.d.ts
vendored
@ -1,45 +1,54 @@
|
||||
export type User = {
|
||||
id: number;
|
||||
inventoryItems: IInventoryItem[];
|
||||
};
|
||||
|
||||
export type Resource = {
|
||||
name: string,
|
||||
fontColorClass: string,
|
||||
bgColorClass: string
|
||||
export interface IResourceType {
|
||||
id: number;
|
||||
name: string;
|
||||
fontColorClass: string;
|
||||
bgColorClass: string;
|
||||
}
|
||||
|
||||
export interface IResourceCardProps {
|
||||
resource: Resource,
|
||||
isMining: boolean,
|
||||
yieldPerSecond: number
|
||||
export interface IResourceWell {
|
||||
id: number;
|
||||
resourceType: IResourceType;
|
||||
supply: number;
|
||||
}
|
||||
|
||||
export type Modifier = {
|
||||
resource: Resource | undefined,
|
||||
yield: number
|
||||
export interface IStakingSource {
|
||||
id: number;
|
||||
name: string;
|
||||
description: string;
|
||||
resourceWells: IResourceWell[];
|
||||
inventoryItem: IInventoryItem | null;
|
||||
}
|
||||
|
||||
export type Yield = {
|
||||
resource: Resource | undefined,
|
||||
baseYield: number
|
||||
export interface IInventoryItem {
|
||||
id: number;
|
||||
storeItem: IStoreItem;
|
||||
currentTierIndex: number;
|
||||
}
|
||||
|
||||
export type Moon = {
|
||||
name: string,
|
||||
description: string,
|
||||
resources: Resource[]
|
||||
drill: Drill | null
|
||||
export interface IStoreItem {
|
||||
id: number;
|
||||
name: string;
|
||||
description: string;
|
||||
price: number;
|
||||
timeToClaim: number;
|
||||
tiers: {
|
||||
tier: number;
|
||||
price: number;
|
||||
}[];
|
||||
}
|
||||
|
||||
export type Drill = {
|
||||
name: string,
|
||||
description: string,
|
||||
price: number,
|
||||
yield: Yield[]
|
||||
upgrades: Upgrade[] | null
|
||||
export interface IBankAccount {
|
||||
id: number;
|
||||
resourceType: IResourceType;
|
||||
balance: number;
|
||||
}
|
||||
|
||||
export type Upgrade = {
|
||||
name: string,
|
||||
description: string,
|
||||
price: number,
|
||||
modifiers: Modifier[]
|
||||
owned: boolean
|
||||
}
|
||||
export interface IClaimableResource {
|
||||
resourceType: IResourceType;
|
||||
balance: number;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user