refactor resourceStore

This commit is contained in:
Emil Nielsen 2023-04-07 14:29:29 +07:00
parent b84b8eadb5
commit e20775068f
21 changed files with 669 additions and 570 deletions

View File

@ -0,0 +1,123 @@
"use client";
import React, { useState } from "react";
import { IResourceAccount, IConversionPair } from "typings";
import { resourceToFc } from "../../../utils/helpers";
import ResourceModal from "./ResourceModal";
const ResourceAccount = (props: {
account: IResourceAccount;
sellResource: (pairs: IConversionPair[]) => void;
}) => {
const [showModal, setShowModal] = useState(false);
const [conversionPair, setConversionPair] = useState<
IConversionPair | undefined
>(undefined);
const [conversionRate, setConversionRate] = useState<Number>(0.1);
const handleConversionPair = (amount: number, resourceType: string) => {
const updatedPair = {
resourceType: resourceType,
resourceAmount: amount,
moneyAmount: amount * conversionRate.valueOf(),
};
setConversionPair(updatedPair);
};
console.log(conversionPair);
return (
<>
{showModal && (
<div className="z-10 fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center">
<div className="p-8 relative w-96">
<button
onClick={() => {
setShowModal(!showModal);
}}
className="text-white absolute top-0"
>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth={2}
stroke="currentColor"
className="w-6 h-6"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M6 18L18 6M6 6l12 12"
/>
</svg>
</button>
<div className="md:h-auto max-w-md max-h-full overflow-y-auto">
<ResourceModal
resourceAccount={props.account}
conversionPair={conversionPair}
handleConversionPair={handleConversionPair}
/>
{
<div className="mt-2 bg-white rounded-lg p-3">
<p className="mb-0 text-2xl font-bold text-black">
Grand total
</p>
<p className="mb-2 text-2xl font-bold underline text-green-700">
${" "}
{conversionPair
? conversionPair.moneyAmount.toLocaleString("en-US", {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})
: 0}
</p>
{conversionPair && (
<button
onClick={() => props.sellResource([conversionPair])}
className="bg-green-700 py-2 text-slate-900 rounded-lg font-bold w-28 text-center text-white"
>
Sell Now
</button>
)}
</div>
}
</div>
</div>
</div>
)}
<div className="flex-1 md:mb-0 mb-2 bg-gradient-to-tr from-purple-600 via-blue-600 to-indigo-700 h-auto p-1 rounded-xl">
<div className="relative bg-slate-900 text-white px-8 py-4 rounded-xl">
<span
className={resourceToFc(props.account.resourceType) + " font-bold"}
>
{props.account.resourceType}
</span>
<h3 className="text-2xl font-bold">
{props.account.balance.toLocaleString("en-US", {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}{" "}
kg
</h3>
<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 absolute right-0 top-0 mt-4 mr-4"
onClick={() => {
setShowModal(!showModal);
}}
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M2.25 18.75a60.07 60.07 0 0115.797 2.101c.727.198 1.453-.342 1.453-1.096V18.75M3.75 4.5v.75A.75.75 0 013 6h-.75m0 0v-.375c0-.621.504-1.125 1.125-1.125H20.25M2.25 6v9m18-10.5v.75c0 .414.336.75.75.75h.75m-1.5-1.5h.375c.621 0 1.125.504 1.125 1.125v9.75c0 .621-.504 1.125-1.125 1.125h-.375m1.5-1.5H21a.75.75 0 00-.75.75v.75m0 0H3.75m0 0h-.375a1.125 1.125 0 01-1.125-1.125V15m1.5 1.5v-.75A.75.75 0 003 15h-.75M15 10.5a3 3 0 11-6 0 3 3 0 016 0zm3 0h.008v.008H18V10.5zm-12 0h.008v.008H6V10.5z"
/>
</svg>
</div>
</div>
</>
);
};
export default ResourceAccount;

View File

@ -1,12 +1,12 @@
"use client"; "use client";
import React from "react"; import React, { useState, useEffect } from "react";
import { IBankAccount } from "typings"; import { IBankAccount, IConversionPair } from "typings";
import ResourceAccount from "./ResourceAccount"; import Account from "./Account";
import { BiLoaderAlt } from "react-icons/bi"; import { BiLoaderAlt } from "react-icons/bi";
const BankAccountsView = (props: { const BankAccountsView = (props: {
bankAccount: IBankAccount | undefined; bankAccount: IBankAccount | undefined;
setLightBoxIsActive: () => void; sellResource: (pairs: IConversionPair[]) => void;
}) => { }) => {
if (props.bankAccount === undefined) { if (props.bankAccount === undefined) {
return ( return (
@ -15,10 +15,11 @@ const BankAccountsView = (props: {
</div> </div>
); );
} }
return ( return (
<div className="p-4"> <div className="p-4">
<div className="flex items-center gap-8"> <div className="md:flex items-center gap-8">
<div className="flex-1 bg-gradient-to-tr from-purple-600 via-blue-600 to-indigo-700 h-auto p-1 rounded-xl"> <div className="flex-1 md:mb-0 mb-2 bg-gradient-to-tr from-purple-600 via-blue-600 to-indigo-700 h-auto p-1 rounded-xl">
<div className="text-white bg-slate-900 px-8 py-4 rounded-xl"> <div className="text-white bg-slate-900 px-8 py-4 rounded-xl">
<span className="text-green-600 font-bold">Moonbucks</span> <span className="text-green-600 font-bold">Moonbucks</span>
<h3 className="text-2xl font-bold"> <h3 className="text-2xl font-bold">
@ -33,7 +34,13 @@ const BankAccountsView = (props: {
{props.bankAccount && {props.bankAccount &&
props.bankAccount?.resourceAccounts.length > 0 && props.bankAccount?.resourceAccounts.length > 0 &&
props.bankAccount.resourceAccounts.map((account, id) => { props.bankAccount.resourceAccounts.map((account, id) => {
return <ResourceAccount key={id} account={account} />; return (
<Account
key={id}
account={account}
sellResource={props.sellResource}
/>
);
})} })}
</div> </div>
</div> </div>

View File

@ -0,0 +1,110 @@
import React, { useState, useEffect } from "react";
import ResourceStoreItem from "../ResourceModal";
import { sumValues } from "../../../../utils/helpers";
import { IBankAccount, IConversionPair } from "typings";
const ResourceStore = (props: {
bankAccount: IBankAccount | undefined;
sellResource: (pairs: IConversionPair[]) => void;
}) => {
const [conversionRate, setConversionRate] = useState<Number>(0.1);
const [conversionPairs, setConversionPairs] = useState<
IConversionPair[] | null
>(null);
useEffect(() => {
if (props.bankAccount) {
const pairs = props.bankAccount.resourceAccounts.map((resource) => ({
resourceType: resource.resourceType,
resourceAmount: 0,
moneyAmount: 0,
}));
setConversionPairs(pairs);
}
}, []);
const handleConversionPairs = (amount: number, resourceType: string) => {
if (!conversionPairs) return;
const existingObj = conversionPairs.find(
(obj) => obj.resourceType === resourceType
);
const updatedPairs = conversionPairs.map((pair) => {
if (pair.resourceType === existingObj?.resourceType) {
return {
...pair,
resourceAmount: amount,
moneyAmount: amount * conversionRate.valueOf(),
};
} else {
return pair;
}
});
setConversionPairs(updatedPairs);
};
const getConversionPair = (resourceType: string) => {
return conversionPairs?.find((obj) => obj.resourceType === resourceType);
};
const handleSellResources = async () => {
if (conversionPairs) {
const response = props.sellResource(conversionPairs);
// if ok clear pairs
// setConversionPairs(null);
}
};
return (
<div className="bg-gradient-to-tr from-purple-600 via-blue-600 to-indigo-700 h-auto p-1 rounded-xl rounded-xl col-span-2">
<div className="bg-slate-900 text-white p-4 md:p-8 rounded-xl">
<h1 className="text-white text-3xl font-bold">Sell Resources</h1>
<div className="flex gap-4 mt-4">
<div className="flex-1">
{props.bankAccount &&
props.bankAccount.resourceAccounts.map((resourceAccount, id) => (
<div key={id} className="flex mb-4 gap-4">
<div className="flex-1">
<ResourceStoreItem
resourceAccount={resourceAccount}
conversionPair={getConversionPair(
resourceAccount.resourceType
)}
handleConversionPairs={handleConversionPairs}
/>
</div>
</div>
))}
{conversionPairs && (
<div className="bg-slate-200 rounded-lg p-3">
<p className="mb-0 text-2xl font-bold text-black">
Grand total
</p>
<p className="mb-3 text-2xl font-bold underline text-green-700">
${" "}
{sumValues(conversionPairs, "moneyAmount").toLocaleString(
"en-US",
{
minimumFractionDigits: 2,
maximumFractionDigits: 2,
}
)}
</p>
<button
onClick={handleSellResources}
className="bg-green-700 py-2 text-slate-900 rounded-lg font-bold w-28 text-center text-white"
>
Sell Now
</button>
</div>
)}
</div>
</div>
</div>
</div>
);
};
export default ResourceStore;

View File

@ -0,0 +1,121 @@
"use client";
import React, { useEffect, useState } from "react";
import { IResourceAccount, IConversionPair } from "typings";
import { resourceToBg, resourceToFc } from "../../../utils/helpers";
const ResourceModal = (props: {
resourceAccount: IResourceAccount;
conversionPair: IConversionPair | undefined;
handleConversionPair: (amount: number, resourceType: string) => void;
}) => {
const [error, setError] = useState("");
const handleAmountChange = (
e: React.ChangeEvent<HTMLInputElement>,
resourceType: string
) => {
console.log("Changing amount");
const amount = Number(e.target.value);
if (amount <= props.resourceAccount.balance) {
props.handleConversionPair(amount, resourceType);
if (error) setError("");
} else {
setError("You don't have enough");
}
};
return (
<div
className={
resourceToBg(props.resourceAccount.resourceType) +
" bg-gradient-to-br hover:bg-gradient-to-tr rounded-lg p-3"
}
>
<div className="text-white mr-4">
<span
className={
resourceToFc(props.resourceAccount.resourceType) + " font-bold"
}
>
{props.resourceAccount.resourceType}
</span>
<h3 className="md:text-2xl font-bold">
{props.resourceAccount.balance.toLocaleString("en-US", {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}{" "}
kg
</h3>
</div>
<div className="text-white mt-4">
<div className="flex">
<span>
<span
className={
resourceToFc(props.resourceAccount.resourceType) + " font-bold"
}
>
Amount
</span>
<h3 className="md:text-2xl font-bold">
{props.conversionPair
? props.conversionPair.resourceAmount.toLocaleString("en-US", {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})
: 0}{" "}
kg.
</h3>
</span>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth="2.5"
stroke="currentColor"
className="w-6 h-6 mt-4 mx-4"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M17.25 8.25L21 12m0 0l-3.75 3.75M21 12H3"
/>
</svg>
<span>
<span
className={
resourceToFc(props.resourceAccount.resourceType) + " font-bold"
}
>
Moonbucks
</span>
<h3 className="md:text-2xl font-bold">
$
{props.conversionPair
? props.conversionPair.moneyAmount.toLocaleString("en-US", {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})
: 0}
</h3>
</span>
</div>
</div>
<div className="flex flex-col">
{error && (
<span className="text-red-500 font-bold mb-1 text-sm">{error}</span>
)}
<input
type="text"
className="bg-white px-4 py-2 mr-2 mt-4 rounded-lg h-full"
placeholder="0"
onChange={(e) =>
handleAmountChange(e, props.resourceAccount.resourceType)
}
/>
</div>
</div>
);
};
export default ResourceModal;

View File

@ -1,6 +1,6 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import { IInventoryItem, IStake } from "typings"; import { IInventoryItem, IStake } from "typings";
import CardLayout from "../Layouts/CardLayout"; import CardLayout from "../../Layouts/CardLayout";
import Image from "next/image"; import Image from "next/image";
const InventoryItem = (props: { const InventoryItem = (props: {
inventoryItem: IInventoryItem; inventoryItem: IInventoryItem;
@ -35,7 +35,7 @@ const InventoryItem = (props: {
}; };
return ( return (
<CardLayout> <CardLayout>
<div className="flex gap-4"> <div className="md:flex gap-4">
<div className="flex-1"> <div className="flex-1">
<h3 className="text-2xl font-bold mb-2"> <h3 className="text-2xl font-bold mb-2">
{props.inventoryItem.storeItem.name} {props.inventoryItem.storeItem.name}
@ -95,7 +95,7 @@ const InventoryItem = (props: {
<span className="ml-1">({getNextUpgradePrice()})</span> <span className="ml-1">({getNextUpgradePrice()})</span>
</div> </div>
</div> </div>
<div className="relative object-contain w-48 h-48"> <div className="relative object-contain w-48 h-48 mt-4 md:mt-0">
<Image <Image
src={props.inventoryItem.storeItem.image} src={props.inventoryItem.storeItem.image}
alt={props.inventoryItem.storeItem.name} alt={props.inventoryItem.storeItem.name}

View File

@ -1,5 +1,5 @@
import React from "react"; import React from "react";
import { IInventoryItem, IStakingSource, IStake } from "typings"; import { IInventoryItem, IStake } from "typings";
import InventoryItem from "./InventoryItem"; import InventoryItem from "./InventoryItem";
const InventoryItemView = (props: { const InventoryItemView = (props: {
@ -11,7 +11,7 @@ const InventoryItemView = (props: {
<div className="bg-gradient-to-tr from-purple-600 via-blue-600 to-indigo-700 h-auto p-1 rounded-xl rounded-xl col-span-5"> <div className="bg-gradient-to-tr from-purple-600 via-blue-600 to-indigo-700 h-auto p-1 rounded-xl rounded-xl col-span-5">
<div className="bg-slate-900 text-white p-8 rounded-xl"> <div className="bg-slate-900 text-white p-8 rounded-xl">
<h2 className="text-3xl font-bold mb-4">Your Inventory</h2> <h2 className="text-3xl font-bold mb-4">Your Inventory</h2>
<div className="grid grid-cols-3 gap-8"> <div className="grid grid-cols-1 md:grid-cols-3 gap-8">
{props.inventoryItems && {props.inventoryItems &&
props.inventoryItems.length > 0 && props.inventoryItems.length > 0 &&
props.inventoryItems.map((inventoryItem, id) => ( props.inventoryItems.map((inventoryItem, id) => (

View File

@ -88,7 +88,7 @@ export default function Navbar({
<nav className="text-white flex justify-between mt-4"> <nav className="text-white flex justify-between mt-4">
<button <button
onClick={() => router.push("/")} onClick={() => router.push("/")}
className="p-2 m-4 text-3xl font-bold" className="p-2 m-4 text-xl md:text-3xl font-bold"
> >
Moon Miners Moon Miners
</button> </button>
@ -109,8 +109,8 @@ export default function Navbar({
)} )}
</button> </button>
) : ( ) : (
<div className="flex items-stretch"> <div className="flex">
<button className="p-2 m-4 bg-slate-800 rounded-lg"> <button className="w-24 md:w-32 p-2 m-4 bg-slate-800 rounded-lg truncate">
{walletAddress} {walletAddress}
</button> </button>
<button onClick={logout} className="p-2 m-4 bg-slate-800 rounded-lg"> <button onClick={logout} className="p-2 m-4 bg-slate-800 rounded-lg">

View File

@ -9,7 +9,6 @@ const menuItems: MenuItem[] = [
{ name: "Moons", componentKey: "stakingsourcesview" }, { name: "Moons", componentKey: "stakingsourcesview" },
{ name: "Inventory", componentKey: "inventoryitemview" }, { name: "Inventory", componentKey: "inventoryitemview" },
{ name: "Buy Items", componentKey: "storeitemview" }, { name: "Buy Items", componentKey: "storeitemview" },
{ name: "Sell Resources", componentKey: "resourcestore" },
]; ];
interface NavbarProps { interface NavbarProps {
@ -22,7 +21,7 @@ const NavbarVertical: React.FC<NavbarProps> = ({
activeItem, activeItem,
}) => { }) => {
return ( return (
<div className=""> <div className="w-full col-span-5 md:col-span-1">
<nav className="bg-gradient-to-tr from-purple-600 via-blue-600 to-indigo-700 h-auto p-1 rounded-xl"> <nav className="bg-gradient-to-tr from-purple-600 via-blue-600 to-indigo-700 h-auto p-1 rounded-xl">
<ul className="bg-slate-900 p-8 rounded-xl"> <ul className="bg-slate-900 p-8 rounded-xl">
{menuItems.map((item) => ( {menuItems.map((item) => (

View File

@ -1,27 +0,0 @@
"use client";
import React from "react";
import { IResourceAccount } from "typings";
import { resourceToBg, resourceToFc } from "../../utils/helpers";
const ResourceAccount = (props: { account: IResourceAccount }) => {
return (
<div className="flex-1 bg-gradient-to-tr from-purple-600 via-blue-600 to-indigo-700 h-auto p-1 rounded-xl">
<div className="bg-slate-900 text-white px-8 py-4 rounded-xl">
<span
className={resourceToFc(props.account.resourceType) + " font-bold"}
>
{props.account.resourceType}
</span>
<h3 className="text-2xl font-bold">
{props.account.balance.toLocaleString("en-US", {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}{" "}
kg
</h3>
</div>
</div>
);
};
export default ResourceAccount;

View File

@ -1,100 +0,0 @@
import React, { useState, useEffect } from "react";
import ResourceStoreItem from "./ResourceStoreItem";
import { sumValues } from "../../utils/helpers";
import { IBankAccount, IConversionPair } from "typings";
const ResourceStore = (props: { bankAccount: IBankAccount | undefined }) => {
const [conversionRate, setConversionRate] = useState<Number>(0.1);
const [conversionPairs, setConversionPairs] = useState<
IConversionPair[] | null
>(null);
useEffect(() => {
if (props.bankAccount) {
const pairs = props.bankAccount.resourceAccounts.map((resource) => ({
resourceType: resource.resourceType,
resourceAmount: 0,
moneyAmount: 0,
}));
setConversionPairs(pairs);
}
}, []);
const handleConversionPairs = (amount: number, resourceType: string) => {
if (!conversionPairs) return;
const existingObj = conversionPairs.find(
(obj) => obj.resourceType === resourceType
);
const updatedPairs = conversionPairs.map((pair) => {
if (pair.resourceType === existingObj?.resourceType) {
return {
...pair,
resourceAmount: amount,
moneyAmount: amount * conversionRate.valueOf(),
};
} else {
return pair;
}
});
setConversionPairs(updatedPairs);
};
const getConversionPair = (resourceType: string) => {
return conversionPairs?.find((obj) => obj.resourceType === resourceType);
};
const handleSellResources = () => {
// Do something..
console.log(conversionPairs);
};
return (
<div className="bg-slate-800 rounded-lg p-4 col-span-2">
<h1 className="text-white text-3xl font-bold">Sell Resources</h1>
<div className="flex gap-4 mt-4">
<div className="flex-1">
{props.bankAccount &&
props.bankAccount.resourceAccounts.map((resourceAccount) => (
<div className="flex mb-4 gap-4">
<div className="flex-1">
<ResourceStoreItem
resourceAccount={resourceAccount}
conversionPair={getConversionPair(
resourceAccount.resourceType
)}
handleConversionPairs={handleConversionPairs}
/>
</div>
</div>
))}
{conversionPairs && (
<div className="bg-slate-200 rounded-lg p-3">
<p className="mb-0 text-2xl font-bold text-black">Grand total</p>
<p className="mb-3 text-2xl font-bold underline text-green-700">
${" "}
{sumValues(conversionPairs, "moneyAmount").toLocaleString(
"en-US",
{
minimumFractionDigits: 2,
maximumFractionDigits: 2,
}
)}
</p>
<button
onClick={handleSellResources}
className="bg-green-700 py-2 text-slate-900 rounded-lg font-bold w-28 text-center text-white"
>
Sell Now
</button>
</div>
)}
</div>
</div>
</div>
);
};
export default ResourceStore;

View File

@ -1,122 +0,0 @@
"use client";
import React, { useEffect, useState } from "react";
import { IResourceAccount, IConversionPair } from "typings";
import { resourceToBg, resourceToFc } from "../../utils/helpers";
const ResourceStoreItem = (props: {
resourceAccount: IResourceAccount;
conversionPair: IConversionPair | undefined;
handleConversionPairs: (amount: number, resourceType: string) => void;
}) => {
const [error, setError] = useState("");
const handleAmountChange = (
e: React.ChangeEvent<HTMLInputElement>,
resourceType: string
) => {
const amount = Number(e.target.value);
if (amount <= props.resourceAccount.balance) {
props.handleConversionPairs(amount, resourceType);
if (error) setError("");
} else {
setError("You don't have enough");
}
};
return (
<div
className={
resourceToBg(props.resourceAccount.resourceType) +
" bg-gradient-to-br hover:bg-gradient-to-tr rounded-lg p-3 flex flex-row"
}
>
<div className="text-white mr-4 flex-none">
<span
className={
resourceToFc(props.resourceAccount.resourceType) + " font-bold"
}
>
{props.resourceAccount.resourceType}
</span>
<h3 className="text-2xl font-bold">
{props.resourceAccount.balance.toLocaleString("en-US", {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}{" "}
kg
</h3>
</div>
{props.conversionPair ? (
<div className={"text-white flex-1 border-l border-white/25 pl-4"}>
<div className="flex">
<span>
<span
className={
resourceToFc(props.resourceAccount.resourceType) +
" font-bold"
}
>
{props.conversionPair.resourceType}
</span>
<h3 className="text-2xl font-bold">
{props.conversionPair.resourceAmount.toLocaleString("en-US", {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}{" "}
kg.
</h3>
</span>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth="2.5"
stroke="currentColor"
className="w-6 h-6 mt-4 mx-4"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M17.25 8.25L21 12m0 0l-3.75 3.75M21 12H3"
/>
</svg>
<span>
<span
className={
resourceToFc(props.resourceAccount.resourceType) +
" font-bold"
}
>
Moonbucks
</span>
<h3 className="text-2xl font-bold">
$
{props.conversionPair.moneyAmount.toLocaleString("en-US", {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
</h3>
</span>
</div>
</div>
) : (
<div className="flex-1"></div>
)}
<div className="flex flex-col">
{error && (
<span className="text-red-500 font-bold mb-1 text-sm">{error}</span>
)}
<input
type="text"
className="bg-white px-4 py-2 mr-2 rounded-lg h-full"
placeholder="0"
onChange={(e) =>
handleAmountChange(e, props.resourceAccount.resourceType)
}
/>
</div>
</div>
);
};
export default ResourceStoreItem;

View File

@ -0,0 +1,237 @@
import React, { useState, useEffect } from "react";
import { IInventoryItem, IStakingSource, IStake, IOption } from "typings";
import {
calculateRemainingTime,
getObjectFromArray,
} from "../../../utils/helpers";
import SelectDropdown from "../SelectDropdown";
import Image from "next/image";
const StakingSource = (props: {
stakingSource: IStakingSource;
inventoryItems: IInventoryItem[] | null | undefined;
claimStake: (stakingEventId: number) => void;
startStake: (
inventoryItemId: number,
storeItemId: string,
wellId: number
) => void;
}) => {
const [activeStakes, setActiveStakes] = useState<IStake[]>([]);
const [selectedItemId, setSelectedItemId] = useState<number | null>(null);
const [selectedStoreItemId, setSelectedStoreItemId] = useState<string | null>(
null
);
const [selectedWellId, setSelectedWellId] = useState<number | null>(null);
const [showInfo, setShowInfo] = useState(false);
// Check if claimable every second
useEffect(() => {
const updatedActiveStakes = props.stakingSource.activeStakes.map(
(stake) => {
const remainingTime = calculateRemainingTime(
stake.startTime,
stake.durationInMins
);
const obj = {
...stake,
remainingTime: remainingTime,
};
return obj;
}
);
const intervalId = setInterval(() => {
setActiveStakes(updatedActiveStakes);
}, 1000);
return () => {
clearInterval(intervalId);
};
});
const handleStartMining = () => {
if (selectedItemId && selectedWellId && selectedStoreItemId) {
props.startStake(selectedItemId, selectedStoreItemId, selectedWellId);
}
};
const handleClaim = (stakingEventId: number) => {
props.claimStake(stakingEventId);
};
const handleSelectChange = (
wellId: string,
itemId: number,
storeitemId: string
) => {
setSelectedWellId(Number(wellId));
setSelectedStoreItemId(storeitemId);
setSelectedItemId(itemId);
};
const Countdown = (props: { remainingTime: number }) => {
const hours = Math.floor((props.remainingTime / (1000 * 60 * 60)) % 24)
.toString()
.padStart(2, "0");
const minutes = Math.floor((props.remainingTime / (1000 * 60)) % 60)
.toString()
.padStart(2, "0");
const seconds = Math.floor((props.remainingTime / 1000) % 60)
.toString()
.padStart(2, "0");
return (
<span className="font-bold">
{hours} : {minutes} : {seconds}
</span>
);
};
const RenderButtonOrCountdown = (props: { stake: IStake }) => {
if (!props.stake.remainingTime) return <p>Error</p>;
if (props.stake.remainingTime <= 0) {
return (
<button
onClick={() => handleClaim(props.stake.id)}
className="bg-slate-100 text-slate-900 px-4 py-2 rounded-lg font-bold text-center"
>
Claim
</button>
);
} else {
return <Countdown remainingTime={props.stake.remainingTime} />;
}
};
const isActive = (item: IInventoryItem): boolean => {
return props.stakingSource.activeStakes.some(
(obj) => obj.inventoryItemId === item.id
);
};
return (
<div className="flex justify-center items-center">
<div className="relative group">
<Image
src="/assets/moon_1.png"
alt="Moon"
width={props.stakingSource.size}
height={props.stakingSource.size}
onClick={() => setShowInfo(!showInfo)}
className="hover:cursor-pointer"
/>
{showInfo && (
<div className="z-10 fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center">
<div className="w-full h-full md:h-auto max-w-md max-h-full overflow-y-auto">
<div className="relative h-full md:h-auto text-white bg-gradient-to-tr from-purple-600 via-blue-600 to-indigo-700 h-auto md:rounded-xl h-auto p-1">
<div className="h-full md:h-auto bg-slate-900 md:rounded-xl p-4">
<button
onClick={() => {
setShowInfo(!showInfo);
}}
className="text-white absolute -right-0 mr-4"
>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth={2}
stroke="currentColor"
className="w-6 h-6"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M6 18L18 6M6 6l12 12"
/>
</svg>
</button>
<h3 className="text-3xl font-bold mb-2">
{props.stakingSource.name}
</h3>
<p className="text-sm">{props.stakingSource.description}</p>
<div className="flex-1">
<p className="font-bold mt-4 mb-2">Active Drills</p>
{activeStakes &&
activeStakes.map((stake, id) => (
<div
key={id}
className="border border-white rounded-xl p-3 mb-2 bg-black/20"
>
<p>
<span className="font-bold">Drill: </span>
{props.inventoryItems &&
getObjectFromArray(
props.inventoryItems,
"id",
stake.inventoryItemId
)?.storeItem.name}
</p>
<p>
<span className="font-bold">Resource: </span>
{stake.resourceType}
</p>
<p className="mb-2">
<span className="font-bold">Yield: </span>
{stake.stakeAmount}
</p>
<RenderButtonOrCountdown stake={stake} />
</div>
))}
</div>
<p className="font-bold mt-4 mb-2">Inactive Drills</p>
{props.inventoryItems &&
props.inventoryItems.map(
(item, id) =>
!isActive(item) && (
<div
key={id}
className="border border-white rounded-xl p-3 mb-2 bg-black/20"
>
<p className="font-bold">{item.storeItem.name}</p>
<p className="mt-2">Select Resource</p>
<div className="flex">
<SelectDropdown
options={props.stakingSource.resourceWells.map(
(well): IOption => ({
value: well.id,
label: well.resourceType,
})
)}
onChange={(value) =>
handleSelectChange(
value,
item.id,
item.storeItem.id
)
}
isActive={selectedItemId === item.id}
/>
{selectedItemId === item.id ? (
<button
onClick={handleStartMining}
className="bg-slate-100 text-slate-900 px-4 py-2 rounded-lg font-bold w-28 text-center ml-2"
>
Activate
</button>
) : (
<></>
)}
</div>
</div>
)
)}
</div>
</div>
</div>
</div>
)}
</div>
</div>
);
};
export default StakingSource;

View File

@ -43,7 +43,7 @@ const moons = [
interface RowProps { interface RowProps {
stakingSources: IStakingSource[]; stakingSources: IStakingSource[];
inventoryItems: IInventoryItem[]; inventoryItems: IInventoryItem[] | undefined | null;
claimStake: (stakingEventId: number) => void; claimStake: (stakingEventId: number) => void;
startStake: ( startStake: (
inventoryItemId: number, inventoryItemId: number,
@ -59,7 +59,7 @@ const Row: React.FC<RowProps> = ({
startStake, startStake,
}) => { }) => {
return ( return (
<div className="flex justify-between items-center w-full mb-8 mt-8"> <div className="md:flex md:justify-between md:items-center w-full mb-8 mt-8">
{stakingSources.map((stakingSource, index) => ( {stakingSources.map((stakingSource, index) => (
<div key={index} className="w-full"> <div key={index} className="w-full">
<StakingSource <StakingSource

View File

@ -1,237 +0,0 @@
import React, { useState, useEffect } from "react";
import { IInventoryItem, IStakingSource, IStake, IOption } from "typings";
import CardLayout from "../Layouts/CardLayout";
import {
calculateRemainingTime,
getObjectFromArray,
} from "../../utils/helpers";
import SelectDropdown from "./SelectDropdown";
import Image from "next/image";
const StakingSource = (props: {
stakingSource: IStakingSource;
inventoryItems: IInventoryItem[] | null | undefined;
claimStake: (stakingEventId: number) => void;
startStake: (
inventoryItemId: number,
storeItemId: string,
wellId: number
) => void;
}) => {
const [activeStakes, setActiveStakes] = useState<IStake[]>([]);
const [selectedItemId, setSelectedItemId] = useState<number | null>(null);
const [selectedStoreItemId, setSelectedStoreItemId] = useState<string | null>(
null
);
const [selectedWellId, setSelectedWellId] = useState<number | null>(null);
const [showInfo, setShowInfo] = useState(false);
// Check if claimable every second
useEffect(() => {
const updatedActiveStakes = props.stakingSource.activeStakes.map(
(stake) => {
const remainingTime = calculateRemainingTime(
stake.startTime,
stake.durationInMins
);
const obj = {
...stake,
remainingTime: remainingTime,
};
return obj;
}
);
const intervalId = setInterval(() => {
setActiveStakes(updatedActiveStakes);
}, 1000);
return () => {
clearInterval(intervalId);
};
});
const handleStartMining = () => {
if (selectedItemId && selectedWellId && selectedStoreItemId) {
props.startStake(selectedItemId, selectedStoreItemId, selectedWellId);
}
};
const handleClaim = (stakingEventId: number) => {
props.claimStake(stakingEventId);
};
const handleSelectChange = (
wellId: string,
itemId: number,
storeitemId: string
) => {
setSelectedWellId(Number(wellId));
setSelectedStoreItemId(storeitemId);
setSelectedItemId(itemId);
};
const Countdown = (props: { remainingTime: number }) => {
const hours = Math.floor((props.remainingTime / (1000 * 60 * 60)) % 24)
.toString()
.padStart(2, "0");
const minutes = Math.floor((props.remainingTime / (1000 * 60)) % 60)
.toString()
.padStart(2, "0");
const seconds = Math.floor((props.remainingTime / 1000) % 60)
.toString()
.padStart(2, "0");
return (
<span className="font-bold">
{hours} : {minutes} : {seconds}
</span>
);
};
const RenderButtonOrCountdown = (props: { stake: IStake }) => {
if (!props.stake.remainingTime) return <p>Error</p>;
if (props.stake.remainingTime <= 0) {
return (
<button
onClick={() => handleClaim(props.stake.id)}
className="bg-slate-100 text-slate-900 px-4 py-2 rounded-lg font-bold text-center"
>
Claim
</button>
);
} else {
return (
// I'll fix it
<Countdown remainingTime={props.stake.remainingTime} />
);
}
};
const isActive = (item: IInventoryItem): boolean => {
return props.stakingSource.activeStakes.some(
(obj) => obj.inventoryItemId === item.id
);
};
return (
<div className="flex justify-center items-center">
<div className="relative group">
<Image
src="/assets/moon_1.png"
alt="Moon"
width={props.stakingSource.size}
height={props.stakingSource.size}
onClick={() => setShowInfo(!showInfo)}
className="hover:cursor-pointer"
/>
{showInfo && (
<div className="absolute -right-0 top-1/2 transform -translate-y-1/2 w-96">
<div className="bg-slate-500 border-2 border-white rounded-xl p-4 text-white">
<button
onClick={() => {
setShowInfo(!showInfo);
}}
className="text-white absolute -right-0 mr-4"
>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth={2}
stroke="currentColor"
className="w-6 h-6"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M6 18L18 6M6 6l12 12"
/>
</svg>
</button>
<h3 className="text-xl font-bold mb-2">
{props.stakingSource.name}
</h3>
<p className="text-sm">{props.stakingSource.description}</p>
<div className="flex-1">
<p className="font-bold mt-4 mb-2">Active Drills</p>
{activeStakes &&
activeStakes.map((stake, id) => (
<div
key={id}
className="border border-white rounded-xl p-3 mb-2 bg-black/20"
>
<p>
<span className="font-bold">Drill: </span>
{props.inventoryItems &&
getObjectFromArray(
props.inventoryItems,
"id",
stake.inventoryItemId
)?.storeItem.name}
</p>
<p>
<span className="font-bold">Resource: </span>
{stake.resourceType}
</p>
<p className="mb-2">
<span className="font-bold">Yield: </span>
{stake.stakeAmount}
</p>
<RenderButtonOrCountdown stake={stake} />
</div>
))}
</div>
<p className="font-bold mt-4 mb-2">Inactive Drills</p>
{props.inventoryItems &&
props.inventoryItems.map(
(item, id) =>
!isActive(item) && (
<div
key={id}
className="border border-white rounded-xl p-3 mb-2 bg-black/20"
>
<p className="font-bold">{item.storeItem.name}</p>
<p className="mt-2">Select Resource</p>
<div className="flex">
<SelectDropdown
options={props.stakingSource.resourceWells.map(
(well): IOption => ({
value: well.id,
label: well.resourceType,
})
)}
onChange={(value) =>
handleSelectChange(
value,
item.id,
item.storeItem.id
)
}
isActive={selectedItemId === item.id}
/>
{selectedItemId === item.id ? (
<button
onClick={handleStartMining}
className="bg-slate-100 text-slate-900 px-4 py-2 rounded-lg font-bold w-28 text-center ml-2"
>
Activate
</button>
) : (
<></>
)}
</div>
</div>
)
)}
</div>
</div>
)}
</div>
</div>
);
};
export default StakingSource;

View File

@ -1,7 +1,7 @@
"use client"; "use client";
import React, { useState } from "react"; import React from "react";
import { IStoreItem } from "typings"; import { IStoreItem } from "typings";
import CardLayout from "../Layouts/CardLayout"; import CardLayout from "../../Layouts/CardLayout";
import Image from "next/image"; import Image from "next/image";
const StoreItem = (props: { const StoreItem = (props: {
@ -10,7 +10,7 @@ const StoreItem = (props: {
}) => { }) => {
return ( return (
<CardLayout> <CardLayout>
<div className="flex "> <div className="md:flex">
<div className="flex-1 pr-4 mr-4"> <div className="flex-1 pr-4 mr-4">
<h3 className="text-2xl font-bold mb-2">{props.storeItem.name}</h3> <h3 className="text-2xl font-bold mb-2">{props.storeItem.name}</h3>
<p className="text-sm mb-3">{props.storeItem.description}</p> <p className="text-sm mb-3">{props.storeItem.description}</p>
@ -42,7 +42,7 @@ const StoreItem = (props: {
src={props.storeItem.image} src={props.storeItem.image}
alt={props.storeItem.name} alt={props.storeItem.name}
fill fill
className="rounded-lg border border-white/20 shadow-lg object-contain" className="rounded-lg border border-white/20 shadow-lg object-contain mt-4 md:mt-0"
/> />
</div> </div>
</div> </div>

View File

@ -0,0 +1,32 @@
import React from "react";
import { IInventoryItem, IStoreItem } from "typings";
import StoreItem from "./StoreItem";
const StoreItemView = (props: {
inventoryItems: IInventoryItem[] | null | undefined;
storeItems: IStoreItem[];
buyStoreItem: (itemId: string) => void;
}) => {
return (
<div className="bg-gradient-to-tr from-purple-600 via-blue-600 to-indigo-700 h-auto p-1 rounded-xl rounded-xl col-span-5">
<div className="bg-slate-900 text-white p-8 rounded-xl">
<h2 className="text-3xl font-bold mb-4 text-white">Store</h2>
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
{props.storeItems &&
props.storeItems.length > 0 &&
props.storeItems
.filter((item) => !item.isOwned)
.map((storeItem, id) => (
<StoreItem
key={id}
storeItem={storeItem}
buyStoreItem={props.buyStoreItem}
/>
))}
</div>
</div>
</div>
);
};
export default StoreItemView;

View File

@ -1,30 +0,0 @@
import React, { useState } from "react";
import { IInventoryItem, IStakingSource, IStoreItem } from "typings";
import StoreItem from "./StoreItem";
const StoreItemView = (props: {
inventoryItems: IInventoryItem[] | null | undefined;
storeItems: IStoreItem[];
buyStoreItem: (itemId: string) => void;
}) => {
return (
<div className="border-2 border-white p-8 rounded-lg col-span-5">
<h2 className="text-3xl font-bold mb-4 text-white">Store</h2>
<div className="grid grid-cols-3 gap-8">
{props.storeItems &&
props.storeItems.length > 0 &&
props.storeItems
.filter((item) => !item.isOwned)
.map((storeItem, id) => (
<StoreItem
key={id}
storeItem={storeItem}
buyStoreItem={props.buyStoreItem}
/>
))}
</div>
</div>
);
};
export default StoreItemView;

View File

@ -1,10 +1,10 @@
"use client"; "use client";
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import InventoryItemView from "./Components/InventoryItemView"; import InventoryItemView from "./Components/Inventory/InventoryItemView";
import StakingSourcesView from "./Components/StakingSourcesView"; import StakingSourcesView from "./Components/Staking/StakingSourcesView";
import BankAccountsView from "./Components/BankAccountsView"; import BankAccountsView from "./Components/Accounts/AccountsView";
import StoreItemView from "./Components/StoreItemView"; import StoreItemView from "./Components/Store/StoreItemView";
import ResourceStore from "./Components/ResourceStore"; import ResourceStore from "./Components/Accounts/Old/ResourceStore";
import NotificationPopover from "./Components/NotificationPopover"; import NotificationPopover from "./Components/NotificationPopover";
import { gameConfig } from "@/utils/gameLogic"; import { gameConfig } from "@/utils/gameLogic";
import { import {
@ -12,10 +12,11 @@ import {
IStakingSource, IStakingSource,
IBankAccount, IBankAccount,
IStake, IStake,
IConversionPair,
Notification, Notification,
} from "typings"; } from "typings";
import Navbar from "./Components/Navbar"; import Navbar from "./Components/Navigation/Navbar";
import NavbarVertical from "./Components/NavbarVertical"; import NavbarVertical from "./Components/Navigation/NavbarVertical";
export default function Home() { export default function Home() {
const [inventoryItems, setInventoryItems] = useState< const [inventoryItems, setInventoryItems] = useState<
@ -170,13 +171,14 @@ export default function Home() {
} }
}; };
// Which object is it? // TODO Joe create sql
const sellResource = async (obj: any) => { const sellResource = async (pairs: IConversionPair[]) => {
console.log("Selling resource");
const response = await fetch(`/api/user/${userId}/bank-account`, { const response = await fetch(`/api/user/${userId}/bank-account`, {
method: "PUT", method: "PUT",
headers: { "Content-Type": "application/json" }, headers: { "Content-Type": "application/json" },
body: JSON.stringify({ body: JSON.stringify({
obj: obj, pairs: pairs,
}), }),
}); });
return await response.json(); return await response.json();
@ -261,7 +263,6 @@ export default function Home() {
}; };
const createStakingSource = async () => { const createStakingSource = async () => {
console.log("Minting...");
try { try {
const response = await fetch(`/api/user/${userId}/staking-sources`, { const response = await fetch(`/api/user/${userId}/staking-sources`, {
method: "POST", method: "POST",
@ -315,7 +316,6 @@ export default function Home() {
return <p>Loading...</p>; return <p>Loading...</p>;
} }
//console.log(stakingSources);
return ( return (
<> <>
<Navbar setUserId={setUserId} /> <Navbar setUserId={setUserId} />
@ -325,11 +325,8 @@ export default function Home() {
onClose={handleCloseNotification} onClose={handleCloseNotification}
/> />
)} )}
<BankAccountsView <BankAccountsView bankAccount={bankAccount} sellResource={sellResource} />
bankAccount={bankAccount} <div className="grid grid-cols-1 md:grid-cols-6 p-4 gap-8">
setLightBoxIsActive={handleSetLightBox}
/>
<div className="grid grid-cols-6 p-4 gap-8">
<NavbarVertical <NavbarVertical
onMenuItemClick={handleMenuItemClick} onMenuItemClick={handleMenuItemClick}
activeItem={activeComponent} activeItem={activeComponent}
@ -357,9 +354,6 @@ export default function Home() {
buyStoreItem={buyStoreItem} buyStoreItem={buyStoreItem}
/> />
)} )}
{activeComponent === "resourcestore" && (
<ResourceStore bankAccount={bankAccount} />
)}
</div> </div>
</> </>
); );

View File

@ -19,14 +19,6 @@ export default async function handler(
const result = await db.query("SELECT * FROM get_accounts($1)", [userId]); const result = await db.query("SELECT * FROM get_accounts($1)", [userId]);
if (result.rowCount > 0) { if (result.rowCount > 0) {
// Make the result to the type
/* const updatedResult: IBankAccount = {
id: result.rows[0].id,
primaryBalance: result.rows[0].primarybalance,
resourceAccounts: result.rows[0].resourceaccounts
} */
return res.status(200).json(result.rows[0]); return res.status(200).json(result.rows[0]);
} else { } else {
return res.status(400).json({error: "Could not find accounts for user"}); return res.status(400).json({error: "Could not find accounts for user"});
@ -37,9 +29,9 @@ export default async function handler(
// sell resource // sell resource
// payload userId, key:resourceName/value:amount // payload userId, key:resourceName/value:amount
const { userId } = req.query; const { userId } = req.query;
const { obj } = req.body; // an array of IConversionPairs
// make sure they have each resource they say they have const { pairs } = req.body;
// calculate conversion rates // check they have appropriate amount of each resource
// increment balance // increment balance
// decrement resources // decrement resources
} }