This commit is contained in:
Keiran 2025-09-28 11:38:38 +01:00
parent 34cd43332b
commit 4199f50bac
13 changed files with 930 additions and 36 deletions

View File

@ -1,34 +1,8 @@
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
# ASSCO
## Getting Started
First, run the development server:
# Build
```bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
npm run build
npm start
```
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
## Learn More
To learn more about Next.js, take a look at the following resources:
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
## Deploy on Vercel
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.

View File

@ -0,0 +1,37 @@
import { NextRequest, NextResponse } from "next/server";
export async function POST(request: NextRequest) {
try {
const body = await request.json();
const webhookUrl = process.env.DISCORD_WEBHOOK_URL;
if (!webhookUrl) {
console.error("DISCORD_WEBHOOK_URL environment variable not set");
return NextResponse.json(
{ error: "Webhook not configured" },
{ status: 500 },
);
}
const response = await fetch(webhookUrl, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(body),
});
if (!response.ok) {
throw new Error(`Discord API responded with status ${response.status}`);
}
return NextResponse.json({ success: true });
} catch (error) {
console.error("Webhook error:", error);
return NextResponse.json(
{ error: "Failed to send webhook" },
{ status: 500 },
);
}
}

View File

@ -1 +1,42 @@
@import "tailwindcss";
body {
background-color: #111827;
color: #ffffff;
font-family: system-ui, -apple-system, sans-serif;
}
.container {
max-width: 1200px;
}
input:focus,
select:focus {
outline: none;
border-color: #22c55e;
}
button:disabled {
opacity: 0.6;
cursor: not-allowed;
}
.text-green-400 {
color: #4ade80;
}
.bg-green-600 {
background-color: #16a34a;
}
.bg-green-700 {
background-color: #15803d;
}
.bg-green-800 {
background-color: #166534;
}
.hover\:bg-green-700:hover {
background-color: #15803d;
}

View File

@ -2,8 +2,8 @@ import type { Metadata } from "next";
import "./globals.css";
export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",
title: "ASS AB - Allt vi Skapar Säljer vi",
description: "Beställ Minecraft-föremål från ASS AB",
};
export default function RootLayout({

View File

@ -1,7 +1,107 @@
export default function Home() {
"use client";
import { CartProvider, useCart } from "../contexts/CartContext";
import ProductSelection from "../components/ProductSelection";
import CartView from "../components/CartView";
import DeliveryDetails from "../components/DeliveryDetails";
function OrderApp() {
const { state, dispatch } = useCart();
const steps = ["Lägg till produkter", "Kundvagn", "Leveransuppgifter"];
const nextStep = () => {
if (state.currentStep < 2) {
dispatch({ type: "SET_STEP", payload: state.currentStep + 1 });
}
};
const prevStep = () => {
if (state.currentStep > 0) {
dispatch({ type: "SET_STEP", payload: state.currentStep - 1 });
}
};
const canProceed = () => {
if (state.currentStep === 0) return state.items.length > 0;
if (state.currentStep === 1) return state.items.length > 0;
return true;
};
return (
<main>
<div>Hello world!</div>
</main>
<div className="min-h-screen bg-gray-900">
<header className="bg-green-700 text-white py-6">
<div className="container mx-auto px-4">
<h1 className="text-3xl font-bold text-center">
ASS AB - Allt vi Skapar Säljer vi
</h1>
</div>
</header>
<div className="container mx-auto px-4 py-8">
<div className="mb-8">
<div className="flex justify-center mb-4">
{steps.map((step, index) => (
<div key={index} className="flex items-center">
<div
className={`px-4 py-2 rounded-full ${
index === state.currentStep
? "bg-green-600 text-white"
: index < state.currentStep
? "bg-green-800 text-white"
: "bg-gray-600 text-gray-300"
}`}
>
{step}
</div>
{index < steps.length - 1 && (
<div className="w-8 h-0.5 bg-gray-600 mx-2"></div>
)}
</div>
))}
</div>
</div>
<div className="max-w-4xl mx-auto">
{state.currentStep === 0 && <ProductSelection />}
{state.currentStep === 1 && <CartView />}
{state.currentStep === 2 && <DeliveryDetails />}
<div className="flex justify-between mt-8">
<button
onClick={prevStep}
disabled={state.currentStep === 0}
className="px-6 py-2 bg-gray-600 hover:bg-gray-700 disabled:bg-gray-800 disabled:cursor-not-allowed text-white rounded transition-colors"
>
Tillbaka
</button>
{state.currentStep < 2 && (
<button
onClick={nextStep}
disabled={!canProceed()}
className="px-6 py-2 bg-green-600 hover:bg-green-700 disabled:bg-gray-600 disabled:cursor-not-allowed text-white rounded transition-colors"
>
Nästa
</button>
)}
</div>
</div>
</div>
<footer className="bg-gray-800 text-gray-400 py-6 mt-12">
<div className="container mx-auto px-4 text-center">
<p>© ASS co 2025 - Allt vi Skapar Säljer vi</p>
</div>
</footer>
</div>
);
}
export default function Home() {
return (
<CartProvider>
<OrderApp />
</CartProvider>
);
}

157
src/components/CartView.tsx Normal file
View File

@ -0,0 +1,157 @@
"use client";
import { useState } from "react";
import { useCart } from "../contexts/CartContext";
import { formatDiamonds } from "../utils/formatDiamonds";
import discountCodes from "../data/discountCodes.json";
export default function CartView() {
const { state, dispatch } = useCart();
const [discountCode, setDiscountCode] = useState("");
const calculateItemTotal = (item: (typeof state.items)[0]) => {
let basePrice = item.product.pricePerStack * item.quantity;
if (item.product.id === "cobblestone" && item.variant !== "Cobblestone") {
basePrice = item.quantity / 8;
}
if (item.deliveryOption === "shulker") {
basePrice += 1;
}
return Math.round(basePrice);
};
const subtotal = state.items.reduce(
(sum, item) => sum + calculateItemTotal(item),
0,
);
const discount = Math.round(subtotal * (state.discountPercentage / 100));
const total = subtotal - discount;
const applyDiscountCode = () => {
const code = discountCode.toLowerCase();
const discountInfo = discountCodes[code as keyof typeof discountCodes];
if (discountInfo) {
dispatch({
type: "SET_DISCOUNT",
payload: { code: discountCode, percentage: discountInfo.percentage },
});
} else {
dispatch({ type: "SET_DISCOUNT", payload: { code: "", percentage: 0 } });
}
};
const removeItem = (index: number) => {
dispatch({ type: "REMOVE_ITEM", payload: index });
};
const updateQuantity = (index: number, quantity: number) => {
if (quantity <= 0) {
removeItem(index);
} else {
dispatch({ type: "UPDATE_QUANTITY", payload: { index, quantity } });
}
};
return (
<div className="bg-gray-800 p-6 rounded-lg border border-gray-700">
<h2 className="text-2xl font-bold text-green-400 mb-6">Kundvagn</h2>
{state.items.length === 0 ? (
<p className="text-gray-400">Kundvagnen är tom</p>
) : (
<div className="space-y-4">
{state.items.map((item, index) => (
<div
key={index}
className="bg-gray-700 p-4 rounded border border-gray-600"
>
<div className="flex justify-between items-start">
<div className="flex-1">
<h3 className="text-white font-semibold">
{item.product.name}
{item.variant && ` (${item.variant})`}
</h3>
<p className="text-gray-400 text-sm">
Leverans:{" "}
{item.deliveryOption === "shulker"
? "Shulker (+1 Dia)"
: item.deliveryOption === "pickup"
? "Pickup (-10%)"
: "Kistor"}
</p>
<div className="flex items-center gap-2 mt-2">
<span className="text-gray-300">Antal:</span>
<input
type="number"
value={item.quantity}
onChange={(e) =>
updateQuantity(index, parseInt(e.target.value) || 0)
}
min="0"
className="w-20 bg-gray-600 text-white p-1 rounded border border-gray-500"
/>
</div>
</div>
<div className="text-right">
<p className="text-white font-semibold">
{formatDiamonds(calculateItemTotal(item))}
</p>
<button
onClick={() => removeItem(index)}
className="text-red-400 hover:text-red-300 mt-2 text-sm"
>
Ta bort
</button>
</div>
</div>
</div>
))}
<div className="border-t border-gray-600 pt-4 space-y-2">
<div className="flex justify-between text-gray-300">
<span>Subtotal:</span>
<span>{formatDiamonds(subtotal)}</span>
</div>
{state.discountPercentage > 0 && (
<div className="flex justify-between text-green-400">
<span>Rabatt ({state.discountCode}):</span>
<span>
-{formatDiamonds(discount)} ({state.discountPercentage}%)
</span>
</div>
)}
<div className="flex justify-between text-white font-bold text-lg border-t border-gray-600 pt-2">
<span>Totalt:</span>
<span>{formatDiamonds(total)}</span>
</div>
</div>
<div className="border-t border-gray-600 pt-4">
<label className="block text-gray-300 mb-2">Rabattkod:</label>
<div className="flex gap-2">
<input
type="text"
value={discountCode}
onChange={(e) => setDiscountCode(e.target.value)}
placeholder="Skriv rabattkod"
className="flex-1 bg-gray-700 text-white p-3 rounded border border-gray-600 focus:border-green-400 focus:outline-none"
/>
<button
onClick={applyDiscountCode}
className="bg-green-600 hover:bg-green-700 text-white px-6 py-3 rounded transition-colors"
>
Använd kod
</button>
</div>
</div>
</div>
)}
</div>
);
}

View File

@ -0,0 +1,218 @@
"use client";
import { useState } from "react";
import { useCart } from "../contexts/CartContext";
import { formatDiamonds } from "../utils/formatDiamonds";
export default function DeliveryDetails() {
const { state, dispatch } = useCart();
const [isSubmitting, setIsSubmitting] = useState(false);
const calculateTotal = () => {
let total = 0;
state.items.forEach((item) => {
let itemPrice = item.product.pricePerStack * item.quantity;
if (item.product.id === "cobblestone" && item.variant !== "Cobblestone") {
itemPrice = item.quantity / 8;
}
if (item.deliveryOption === "shulker") {
itemPrice += 1;
}
total += Math.round(itemPrice);
});
const hasPickup = state.items.some(
(item) => item.deliveryOption === "pickup",
);
if (hasPickup) {
total = Math.round(total * 0.9);
}
if (state.discountPercentage > 0) {
total = Math.round(total * (1 - state.discountPercentage / 100));
}
return total;
};
const submitOrder = async () => {
if (!state.playerName || (!hasPickup && !state.coords)) {
alert("Vänligen fyll i alla fält");
return;
}
setIsSubmitting(true);
const finalCoords = hasPickup ? "x:2100 z:1500" : state.coords;
const orderData = {
embeds: [
{
title: "🛒 Ny beställning från ASS AB",
color: 0x22c55e,
fields: [
{
name: "👤 Spelare",
value: state.playerName,
inline: true,
},
{
name: "📍 Koordinater",
value: finalCoords,
inline: true,
},
{
name: "💎 Total kostnad",
value: formatDiamonds(calculateTotal()),
inline: true,
},
{
name: "📦 Beställda varor",
value: state.items
.map((item) => {
const itemTotal = (() => {
let price = item.product.pricePerStack * item.quantity;
if (
item.product.id === "cobblestone" &&
item.variant !== "Cobblestone"
) {
price = item.quantity / 8;
}
if (item.deliveryOption === "shulker") {
price += 1;
}
return Math.round(price);
})();
return `${item.product.name}${item.variant ? ` (${item.variant})` : ""}: ${item.quantity} stacks - ${formatDiamonds(itemTotal)}\n Leverans: ${item.deliveryOption === "shulker" ? "Shulker (+1 Dia)" : item.deliveryOption === "pickup" ? "Pickup (-10%)" : "Kistor"}`;
})
.join("\n"),
inline: false,
},
],
footer: {
text: `Beställning skickad ${new Date().toLocaleString("sv-SE")}`,
},
},
],
};
try {
const response = await fetch("/api/webhook", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(orderData),
});
if (response.ok) {
alert("Beställning skickad! Vi kontaktar dig snart.");
dispatch({ type: "CLEAR_CART" });
dispatch({ type: "SET_STEP", payload: 0 });
dispatch({ type: "SET_PLAYER_NAME", payload: "" });
dispatch({ type: "SET_COORDS", payload: "" });
} else {
alert("Något gick fel. Försök igen.");
}
} catch (error) {
alert("Något gick fel. Försök igen.");
console.error("Error:", error);
} finally {
setIsSubmitting(false);
}
};
const hasPickup = state.items.some(
(item) => item.deliveryOption === "pickup",
);
return (
<div className="bg-gray-800 p-6 rounded-lg border border-gray-700">
<h2 className="text-2xl font-bold text-green-400 mb-6">
Leveransuppgifter
</h2>
<div className="space-y-6">
<div>
<label className="block text-gray-300 mb-2">Namn:</label>
<input
type="text"
value={state.playerName}
onChange={(e) =>
dispatch({ type: "SET_PLAYER_NAME", payload: e.target.value })
}
placeholder="Spelarnamn"
className="w-full bg-gray-700 text-white p-3 rounded border border-gray-600 focus:border-green-400 focus:outline-none"
/>
</div>
<div>
<label className="block text-gray-300 mb-2">
Koordinater {hasPickup && "(pickup på x:2100 z:1500)"}:
</label>
<input
type="text"
value={hasPickup ? "x:2100 z:1500" : state.coords}
onChange={(e) =>
!hasPickup &&
dispatch({ type: "SET_COORDS", payload: e.target.value })
}
placeholder={hasPickup ? "x:2100 z:1500" : "Ex: 123 64 -456"}
disabled={hasPickup}
className={`w-full bg-gray-700 text-white p-3 rounded border border-gray-600 focus:border-green-400 focus:outline-none ${hasPickup ? "opacity-75" : ""}`}
/>
</div>
<div className="bg-gray-700 p-4 rounded border border-gray-600">
<h3 className="text-white font-semibold mb-3">Ordersammanfattning</h3>
<div className="space-y-2 text-sm">
{state.items.map((item, index) => (
<div key={index} className="flex justify-between text-gray-300">
<span>
{item.product.name}
{item.variant ? ` (${item.variant})` : ""} x{item.quantity}
</span>
<span>
{(() => {
let price = item.product.pricePerStack * item.quantity;
if (
item.product.id === "cobblestone" &&
item.variant !== "Cobblestone"
) {
price = item.quantity / 8;
}
if (item.deliveryOption === "shulker") {
price += 1;
}
return formatDiamonds(Math.round(price));
})()}
</span>
</div>
))}
<div className="border-t border-gray-600 pt-2 font-bold text-white">
<div className="flex justify-between">
<span>Total:</span>
<span>{formatDiamonds(calculateTotal())}</span>
</div>
</div>
</div>
</div>
<button
onClick={submitOrder}
disabled={
!state.playerName || (!hasPickup && !state.coords) || isSubmitting
}
className="w-full bg-green-600 hover:bg-green-700 disabled:bg-gray-600 disabled:cursor-not-allowed text-white font-semibold py-3 px-4 rounded transition-colors"
>
{isSubmitting ? "Skickar beställning..." : "Beställ"}
</button>
</div>
</div>
);
}

View File

@ -0,0 +1,126 @@
"use client";
import { useState } from "react";
import { products } from "../data/products";
import { useCart } from "../contexts/CartContext";
import type { Product, CartItem } from "../types/product";
export default function ProductSelection() {
const { dispatch } = useCart();
const [selectedProduct, setSelectedProduct] = useState<Product | null>(null);
const [quantity, setQuantity] = useState(1);
const [deliveryOption, setDeliveryOption] = useState<
"shulker" | "chests" | "pickup"
>("chests");
const [variant, setVariant] = useState("");
const handleAddToCart = () => {
if (!selectedProduct) return;
const item: CartItem = {
product: selectedProduct,
quantity,
unit: "stacks",
deliveryOption,
variant: variant || undefined,
};
dispatch({ type: "ADD_ITEM", payload: item });
setSelectedProduct(null);
setQuantity(1);
setVariant("");
};
const getProductPrice = (product: Product) => {
if (product.defaultStacksPerDia) {
return `${product.defaultStacksPerDia} Stacks = 1 Dia`;
}
return `1 Stack = ${product.pricePerStack} Dia`;
};
return (
<div className="bg-gray-800 p-6 rounded-lg border border-gray-700">
<h2 className="text-2xl font-bold text-green-400 mb-6">
Lägg till produkt
</h2>
<div className="space-y-6">
<div>
<label className="block text-gray-300 mb-2">Produkt:</label>
<select
value={selectedProduct?.id || ""}
onChange={(e) => {
const product = products.find((p) => p.id === e.target.value);
setSelectedProduct(product || null);
setVariant("");
}}
className="w-full bg-gray-700 text-white p-3 rounded border border-gray-600 focus:border-green-400 focus:outline-none"
>
{products.map((product) => (
<option key={product.id} value={product.id}>
{product.name} ({getProductPrice(product)})
</option>
))}
</select>
</div>
{selectedProduct?.variants && (
<div>
<label className="block text-gray-300 mb-2">Variant:</label>
<select
value={variant}
onChange={(e) => setVariant(e.target.value)}
className="w-full bg-gray-700 text-white p-3 rounded border border-gray-600 focus:border-green-400 focus:outline-none"
>
<option value="">Välj variant</option>
{selectedProduct.variants.map((variant) => (
<option key={variant} value={variant}>
{variant}
</option>
))}
</select>
</div>
)}
<div>
<label className="block text-gray-300 mb-2">Antal stackar:</label>
<input
type="number"
value={quantity}
onChange={(e) =>
setQuantity(Math.max(1, parseInt(e.target.value) || 1))
}
min="1"
className="w-full bg-gray-700 text-white p-3 rounded border border-gray-600 focus:border-green-400 focus:outline-none"
/>
</div>
<div>
<label className="block text-gray-300 mb-2">Leveransbehållare:</label>
<select
value={deliveryOption}
onChange={(e) =>
setDeliveryOption(
e.target.value as "shulker" | "chests" | "pickup",
)
}
className="w-full bg-gray-700 text-white p-3 rounded border border-gray-600 focus:border-green-400 focus:outline-none"
>
<option value="chests">Kistor (standard)</option>
<option value="shulker">Shulker (+1 Dia)</option>
<option value="pickup">Pickup (-10% rabatt)</option>
</select>
</div>
<button
onClick={handleAddToCart}
disabled={!selectedProduct || (selectedProduct.variants && !variant)}
className="w-full bg-green-600 hover:bg-green-700 disabled:bg-gray-600 disabled:cursor-not-allowed text-white font-semibold py-3 px-4 rounded transition-colors"
>
Lägg till i kundvagn
</button>
</div>
</div>
);
}

View File

@ -0,0 +1,117 @@
"use client";
import React, { createContext, useContext, useReducer, ReactNode } from "react";
import type { CartItem } from "../types/product";
interface CartState {
items: CartItem[];
discountCode: string;
discountPercentage: number;
playerName: string;
coords: string;
currentStep: number;
}
type CartAction =
| { type: "ADD_ITEM"; payload: CartItem }
| { type: "REMOVE_ITEM"; payload: number }
| { type: "UPDATE_QUANTITY"; payload: { index: number; quantity: number } }
| { type: "CLEAR_CART" }
| { type: "SET_DISCOUNT"; payload: { code: string; percentage: number } }
| { type: "SET_PLAYER_NAME"; payload: string }
| { type: "SET_COORDS"; payload: string }
| { type: "SET_STEP"; payload: number };
const initialState: CartState = {
items: [],
discountCode: "",
discountPercentage: 0,
playerName: "",
coords: "",
currentStep: 0,
};
function cartReducer(state: CartState, action: CartAction): CartState {
switch (action.type) {
case "ADD_ITEM":
const existingIndex = state.items.findIndex(
(item) =>
item.product.id === action.payload.product.id &&
item.variant === action.payload.variant &&
item.unit === action.payload.unit &&
item.deliveryOption === action.payload.deliveryOption,
);
if (existingIndex >= 0) {
const newItems = [...state.items];
newItems[existingIndex] = {
...newItems[existingIndex],
quantity: newItems[existingIndex].quantity + action.payload.quantity,
};
return { ...state, items: newItems };
}
return { ...state, items: [...state.items, action.payload] };
case "REMOVE_ITEM":
return {
...state,
items: state.items.filter((_, index) => index !== action.payload),
};
case "UPDATE_QUANTITY":
const updatedItems = [...state.items];
if (updatedItems[action.payload.index]) {
updatedItems[action.payload.index] = {
...updatedItems[action.payload.index],
quantity: action.payload.quantity,
};
}
return { ...state, items: updatedItems };
case "CLEAR_CART":
return { ...state, items: [] };
case "SET_DISCOUNT":
return {
...state,
discountCode: action.payload.code,
discountPercentage: action.payload.percentage,
};
case "SET_PLAYER_NAME":
return { ...state, playerName: action.payload };
case "SET_COORDS":
return { ...state, coords: action.payload };
case "SET_STEP":
return { ...state, currentStep: action.payload };
default:
return state;
}
}
const CartContext = createContext<{
state: CartState;
dispatch: React.Dispatch<CartAction>;
} | null>(null);
export function CartProvider({ children }: { children: ReactNode }) {
const [state, dispatch] = useReducer(cartReducer, initialState);
return (
<CartContext.Provider value={{ state, dispatch }}>
{children}
</CartContext.Provider>
);
}
export function useCart() {
const context = useContext(CartContext);
if (!context) {
throw new Error("useCart must be used within a CartProvider");
}
return context;
}

View File

@ -0,0 +1 @@
{}

42
src/data/products.ts Normal file
View File

@ -0,0 +1,42 @@
import type { Product } from "../types/product";
export const products: Product[] = [
{
id: "spruce-log",
name: "Spruce Log",
pricePerStack: 1,
},
{
id: "birch-log",
name: "Birch Log",
pricePerStack: 1,
},
{
id: "oak-log",
name: "Oak Log",
pricePerStack: 1,
},
{
id: "jungle-log",
name: "Jungle Log",
pricePerStack: 1,
},
{
id: "cobblestone",
name: "Cobblestone",
pricePerStack: 1 / 16,
defaultStacksPerDia: 16,
variants: ["Cobblestone", "Stone", "Stone Bricks"],
},
{
id: "sand",
name: "Sand",
pricePerStack: 1,
},
{
id: "gunpowder",
name: "Gunpowder",
pricePerStack: 1 / 4,
defaultStacksPerDia: 4,
},
];

65
src/types/product.ts Normal file
View File

@ -0,0 +1,65 @@
export interface Product {
id: string;
name: string;
pricePerStack: number;
variants?: string[];
defaultStacksPerDia?: number;
}
export interface CartItem {
product: Product;
quantity: number;
variant?: string;
unit: "stacks" | "shulkers" | "double-chests";
deliveryOption: "shulker" | "chests" | "pickup";
}
export interface Order {
items: CartItem[];
total: number;
discount: number;
discountCode?: string;
playerName: string;
coords: string;
}
export const products: Product[] = [
{
id: "spruce-log",
name: "Spruce Log",
pricePerStack: 1,
},
{
id: "birch-log",
name: "Birch Log",
pricePerStack: 1,
},
{
id: "oak-log",
name: "Oak Log",
pricePerStack: 1,
},
{
id: "jungle-log",
name: "Jungle Log",
pricePerStack: 1,
},
{
id: "cobblestone",
name: "Cobblestone",
pricePerStack: 1 / 16,
defaultStacksPerDia: 16,
variants: ["Cobblestone", "Stone", "Stone Bricks"],
},
{
id: "sand",
name: "Sand",
pricePerStack: 1,
},
{
id: "gunpowder",
name: "Gunpowder",
pricePerStack: 1 / 4,
defaultStacksPerDia: 4,
},
];

View File

@ -0,0 +1,16 @@
export function formatDiamonds(totalDia: number): string {
const roundedDia = Math.round(totalDia);
if (roundedDia < 64) {
return `${roundedDia} Dia`;
}
const stacks = Math.floor(roundedDia / 64);
const remaining = roundedDia % 64;
if (remaining === 0) {
return `${stacks} ${stacks === 1 ? "stack" : "stacks"} Dia`;
}
return `${stacks} ${stacks === 1 ? "stack" : "stacks"} + ${remaining} Dia`;
}