Skip to content

Commit

Permalink
feat: improve cart, add label
Browse files Browse the repository at this point in the history
  • Loading branch information
rolznz committed Jan 7, 2024
1 parent ddf1849 commit da844a2
Show file tree
Hide file tree
Showing 6 changed files with 158 additions and 56 deletions.
10 changes: 7 additions & 3 deletions src/components/Navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@ import {
import { localStorageKeys } from "../constants";
import useStore from "../state/store";

export function Navbar() {
type NavbarProps = {
onOpenCart?: () => void;
};

export function Navbar({ onOpenCart }: NavbarProps) {
const { cart } = useStore();
return (
<div className="navbar bg-base-100">
Expand Down Expand Up @@ -62,12 +66,12 @@ export function Navbar() {
<BuzzPay className="w-24 h-6 mt-2" />
</div>
<div className="flex-none">
<Link to="../items">
<Link to="../items" onClick={onOpenCart}>
<button className="btn btn-square btn-ghost relative">
<PopiconsCartDuotone className="w-6 h-6" />
{cart.length > 0 && (
<div className="badge badge-info absolute -top-2 -right-2">
{cart.length}
{cart.map((item) => item.quantity).reduce((a, b) => a + b)}
</div>
)}
</button>
Expand Down
59 changes: 40 additions & 19 deletions src/pages/wallet/Items.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ import { Item } from "../../types";
import useStore from "../../state/store";

export function Items() {
const { amount, setAmount, cart, addItemToCart, startNewPurchase } =
useStore();
const { cart, addItemToCart, removeItemFromCart, clearCart } = useStore();
const [itemName, setItemName] = React.useState("");
const [itemPrice, setItemPrice] = React.useState("");
const [isSaving, setSaving] = React.useState(false);
Expand Down Expand Up @@ -57,35 +56,57 @@ export function Items() {
<Backbar />
<div className="flex flex-1 flex-col w-full h-full justify-center items-center gap-4">
<h1 className="text-lg">Cart</h1>
{itemsData.items.map((item) => (
<button
{cart.map((item) => (
<div
key={item.name}
onClick={() => {
setAmount((parseInt(amount || "0") + item.price).toString());
addItemToCart(item);
}}
className="btn btn-primary"
className="flex justify-center items-center gap-2"
>
{item.name} - {item.price} sats (
{
cart.filter(
(cartItem) => JSON.stringify(cartItem) === JSON.stringify(item)
).length
}
)
</button>
<p className="flex-1">
{item.name} - {item.price} sats ({item.quantity})
</p>
<button
onClick={() => {
addItemToCart(item);
}}
className="btn btn-success"
>
+
</button>
<button
onClick={() => {
removeItemFromCart(item);
}}
className="btn btn-error"
>
-
</button>
</div>
))}

{cart.length > 0 && (
<button
onClick={() => {
startNewPurchase();
clearCart();
}}
className="btn btn-error"
>
Clear cart
</button>
)}
<h1 className="text-lg">Add Items</h1>
{itemsData.items.map((item) => (
<button
key={item.name}
onClick={() => {
addItemToCart(item);
}}
className="btn btn-primary"
>
{item.name} - {item.price} sats (
{cart.find((cartItem) => cartItem.name === item.name)?.quantity ||
0}
)
</button>
))}

<form
onSubmit={onSubmit}
Expand Down
68 changes: 54 additions & 14 deletions src/pages/wallet/New.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,32 +5,57 @@ import useStore from "../../state/store";
import { MAX_MEMO_LENGTH } from "../../constants";

export function New() {
const { amount, setAmount, cart } = useStore();
const [amount, setAmount] = React.useState("");
const [label, setLabel] = React.useState("");
const { cart, addItemToCart } = useStore();
const [isLoading, setLoading] = React.useState(false);
const navigate = useNavigate();
const provider = useStore((store) => store.provider);

function convertCurrentEntryToCartItem() {
if (amount) {
const findFreeLabel = () => {
const labelPrefix = "Item ";
let index = 0;
let freeLabel: string;
do {
index++;
freeLabel = `${labelPrefix}${index}`;
} while (cart.some((item) => item.name === freeLabel));
return freeLabel;
};

addItemToCart({
name: label || findFreeLabel(),
price: parseInt(amount),
});
}
}

async function onSubmit(e: FormEvent) {
e.preventDefault();
try {
setLoading(true);
const amountSats = parseInt(amount);
if (isNaN(amountSats) || amountSats < 1) {
throw new Error("Invalid amount: " + amountSats);
}
convertCurrentEntryToCartItem();

const finalCart = useStore.getState().cart;
if (!finalCart.length) {
throw new Error("Empty cart");
}
let memo = "";

if (cart.length) {
// TODO: group cart items
memo += cart.map((cart) => cart.name).join(", ");
memo += " - ";
}
// TODO: group cart items
memo += finalCart.map((item) => item.name).join(", ");
memo += " - ";

memo += "Alby PoS";

const totalAmount = finalCart
.map((cart) => cart.price * cart.quantity)
.reduce((a, b) => a + b);

const invoice = await provider?.makeInvoice({
amount: amountSats,
amount: totalAmount,
defaultMemo: memo.substring(0, MAX_MEMO_LENGTH),
});
navigate(`../pay/${invoice.paymentRequest}`);
Expand All @@ -43,7 +68,7 @@ export function New() {

return (
<>
<Navbar />
<Navbar onOpenCart={convertCurrentEntryToCartItem} />
<div className="flex h-full w-full flex-1 flex-col items-center justify-center">
<form
onSubmit={onSubmit}
Expand All @@ -62,13 +87,28 @@ export function New() {
setAmount(e.target.value);
}}
></input>
<button
type="button"
onClick={() => {
const newLabel = prompt("Label", label);
if (newLabel) {
setLabel(newLabel);
}
}}
>
{label || "+ Add label"}
</button>
</div>
<button
className="btn btn-primary w-full"
type="submit"
disabled={isLoading || !amount}
disabled={isLoading || (!amount && !cart.length)}
>
Charge
Charge{" "}
{cart
.map((cart) => cart.price * cart.quantity)
.reduce((a, b) => a + b, parseInt(amount || "0"))}{" "}
sats
{isLoading && <span className="loading loading-spinner"></span>}
</button>
</form>
Expand Down
2 changes: 1 addition & 1 deletion src/pages/wallet/Pay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export function Pay() {
paymentRequest: invoice,
});
if (response.paid) {
useStore.getState().startNewPurchase();
useStore.getState().clearCart();
navigate("../paid");
}
}, 3000);
Expand Down
69 changes: 50 additions & 19 deletions src/state/store.ts
Original file line number Diff line number Diff line change
@@ -1,40 +1,71 @@
import { webln } from "@getalby/sdk";
import { create } from "zustand";
import { Item } from "../types";
import { CartItem, Item } from "../types";

interface Store {
readonly provider: webln.NostrWebLNProvider | undefined;
readonly amount: string;
readonly cart: Item[];
readonly cart: CartItem[];

setProvider(provider: webln.NostrWebLNProvider | undefined): void;
setAmount(amount: string): void;
addItemToCart(item: Item): void;
startNewPurchase(): void;
removeItemFromCart(item: Item): void;
clearCart(): void;
}

const useStore = create<Store>((set, get) => ({
provider: undefined,
...newPurchase(),
setAmount: (amount) => {
set({ amount });
},
cart: [],
setProvider: (provider) => {
set({ provider });
},
addItemToCart: (item) => {
set({ cart: [...get().cart, item] });
const currentCart = get().cart;
const existingItem = currentCart.find(
(existing) => existing.name === item.name
);
if (existingItem) {
const existingItemIndex = currentCart.indexOf(existingItem);
set({
cart: [
...currentCart.slice(0, existingItemIndex),
{ ...existingItem, quantity: existingItem.quantity + 1 },
...currentCart.slice(existingItemIndex + 1),
],
});
} else {
set({
cart: [...currentCart, { ...item, quantity: 1 }],
});
}
},
removeItemFromCart: (item) => {
const currentCart = get().cart;
const existingItem = currentCart.find(
(existing) => existing.name === item.name
);
if (!existingItem) {
return;
}
if (existingItem.quantity > 1) {
const existingItemIndex = currentCart.indexOf(existingItem);
set({
cart: [
...currentCart.slice(0, existingItemIndex),
{ ...existingItem, quantity: existingItem.quantity - 1 },
...currentCart.slice(existingItemIndex + 1),
],
});
} else {
set({
cart: currentCart.filter((item) => item !== existingItem),
});
}
},
startNewPurchase: () => {
set(newPurchase());
clearCart: () => {
set({
cart: [],
});
},
}));

function newPurchase() {
return {
amount: "",
cart: [],
};
}

export default useStore;
6 changes: 6 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,9 @@ export type Item = {
name: string;
price: number;
};

export type CartItem = {
name: string;
price: number;
quantity: number;
};

0 comments on commit da844a2

Please sign in to comment.