Merge branch 'main' of https://git.demonkernel.io.vn/FoodSurf/frontend into fix-minor-issue
Release package / release (pull_request) Successful in 1m46s
Release package / release (pull_request) Successful in 1m46s
This commit is contained in:
@@ -9,8 +9,8 @@ import { useManager } from "@/lib/manager-context";
|
|||||||
import { MenuItemEntity } from "@/lib/types";
|
import { MenuItemEntity } from "@/lib/types";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
|
||||||
const formatPrice = (value: number) =>
|
export const formatPrice = (value?: number) =>
|
||||||
value.toLocaleString("vi-VN", { style: "currency", currency: "VND" });
|
(value ?? 0).toLocaleString("vi-VN", { style: "currency", currency: "VND" });
|
||||||
|
|
||||||
export default function PaymentPage() {
|
export default function PaymentPage() {
|
||||||
const {
|
const {
|
||||||
@@ -42,7 +42,7 @@ export default function PaymentPage() {
|
|||||||
</h1>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{items.length === 0 ? (
|
{items?.length === 0 ? (
|
||||||
<div className="px-4 py-10 text-center text-(--color-text-muted)">
|
<div className="px-4 py-10 text-center text-(--color-text-muted)">
|
||||||
Chưa có sản phẩm nào trong giỏ hàng.
|
Chưa có sản phẩm nào trong giỏ hàng.
|
||||||
</div>
|
</div>
|
||||||
@@ -72,7 +72,7 @@ export default function PaymentPage() {
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{items.map(
|
{items?.map(
|
||||||
({
|
({
|
||||||
productId: id,
|
productId: id,
|
||||||
priceAtTimeOfAdding: price,
|
priceAtTimeOfAdding: price,
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { formatPrice } from "@/app/(main)/payment/page";
|
||||||
import Button from "@/components/atoms/buttons/Button";
|
import Button from "@/components/atoms/buttons/Button";
|
||||||
import { ReviewModal } from "@/components/organisms/modals";
|
import { ReviewModal } from "@/components/organisms/modals";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
@@ -5,9 +6,6 @@ import { useState } from "react";
|
|||||||
|
|
||||||
import type { PaymentSummaryCardProps } from "./Card.types";
|
import type { PaymentSummaryCardProps } from "./Card.types";
|
||||||
|
|
||||||
const formatPrice = (value: number) =>
|
|
||||||
value.toLocaleString("vi-VN", { style: "currency", currency: "VND" });
|
|
||||||
|
|
||||||
export default function PaymentSummaryCard({
|
export default function PaymentSummaryCard({
|
||||||
totalPrice,
|
totalPrice,
|
||||||
isCustomer = false,
|
isCustomer = false,
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ export default function ProductCard({
|
|||||||
</div>
|
</div>
|
||||||
{/* Product image */}
|
{/* Product image */}
|
||||||
<Image
|
<Image
|
||||||
src={image}
|
src={image || "/"}
|
||||||
alt={imageAlt}
|
alt={imageAlt}
|
||||||
fill
|
fill
|
||||||
className="z-1 object-cover"
|
className="z-1 object-cover"
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import { ProductCard } from "@/components/molecules/cards";
|
import { ProductCard } from "@/components/molecules/cards";
|
||||||
import { useCart } from "@/lib/cart-context";
|
import { useCart } from "@/lib/cart-context";
|
||||||
import { MOCK_PRODUCTS } from "@/lib/constants";
|
import { useManager } from "@/lib/manager-context";
|
||||||
|
|
||||||
import type { ProductGridProps } from "./ProductGrid.types";
|
import type { ProductGridProps } from "./ProductGrid.types";
|
||||||
|
|
||||||
@@ -11,8 +11,9 @@ export default function ProductGrid({
|
|||||||
isSidebarOpen = false,
|
isSidebarOpen = false,
|
||||||
}: ProductGridProps) {
|
}: ProductGridProps) {
|
||||||
const { addToCart } = useCart();
|
const { addToCart } = useCart();
|
||||||
|
const { products } = useManager();
|
||||||
|
|
||||||
const filteredProducts = MOCK_PRODUCTS.filter((p) => {
|
const filteredProducts = products.filter((p) => {
|
||||||
const isAvailable = p.available !== false;
|
const isAvailable = p.available !== false;
|
||||||
const matchesSearch =
|
const matchesSearch =
|
||||||
searchQuery.trim() === "" ||
|
searchQuery.trim() === "" ||
|
||||||
@@ -30,17 +31,19 @@ export default function ProductGrid({
|
|||||||
{/* ── Product grid ── */}
|
{/* ── Product grid ── */}
|
||||||
{filteredProducts.length > 0 ? (
|
{filteredProducts.length > 0 ? (
|
||||||
<div className={`grid gap-4 ${gridCols}`}>
|
<div className={`grid gap-4 ${gridCols}`}>
|
||||||
{filteredProducts.map((product) => (
|
{filteredProducts.map(
|
||||||
|
({ id, imageUrl, name, price, description }) => (
|
||||||
<ProductCard
|
<ProductCard
|
||||||
key={product.id}
|
key={id}
|
||||||
image={product.imageUrl}
|
image={imageUrl}
|
||||||
imageAlt={product.name}
|
imageAlt={name}
|
||||||
productName={product.name}
|
productName={name}
|
||||||
price={product.price}
|
price={price}
|
||||||
description={product.description}
|
description={description}
|
||||||
onBuy={() => addToCart(product)}
|
onBuy={() => addToCart({ productId: id!, quantity: 1 })}
|
||||||
/>
|
/>
|
||||||
))}
|
),
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
/* Empty state */
|
/* Empty state */
|
||||||
|
|||||||
+122
-79
@@ -1,23 +1,87 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
|
import { gql } from "@apollo/client";
|
||||||
|
import { useMutation, useQuery } from "@apollo/client/react";
|
||||||
import { createContext, useContext, useEffect, useMemo, useState } from "react";
|
import { createContext, useContext, useEffect, useMemo, useState } from "react";
|
||||||
|
|
||||||
import { CartItemEntity, MenuItemEntity } from "./types";
|
import { cartClient, eateryClient } from "./apollo-clients";
|
||||||
|
import {
|
||||||
|
CartEntity,
|
||||||
|
CartItemEntity,
|
||||||
|
addMenuItemMutation,
|
||||||
|
allEateriesQuery,
|
||||||
|
createCartMutation,
|
||||||
|
getCartQuery,
|
||||||
|
} from "./types";
|
||||||
|
|
||||||
interface CartContextValue {
|
interface CartContextValue {
|
||||||
items: CartItemEntity[];
|
items: CartItemEntity[];
|
||||||
totalItems: number;
|
totalItems: number;
|
||||||
totalPrice: number;
|
totalPrice: number;
|
||||||
addToCart: (product: MenuItemEntity) => void;
|
addToCart: (product: CartItemEntity) => void;
|
||||||
increaseQty: (id: string) => void;
|
increaseQty: (id: string) => void;
|
||||||
decreaseQty: (id: string) => void;
|
decreaseQty: (id: string) => void;
|
||||||
removeFromCart: (id: string) => void;
|
removeFromCart: (id: string) => void;
|
||||||
setQuantity: (id: string, quantity: number) => void;
|
setQuantity: (id: string, quantity: number) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const STORAGE_KEY = "coffee-shop-cart";
|
const CART_ID = "cartId";
|
||||||
const CartContext = createContext<CartContextValue | null>(null);
|
const CartContext = createContext<CartContextValue | null>(null);
|
||||||
|
|
||||||
|
const GET_CART_ITEMS = gql`
|
||||||
|
query getCart($cartId: String!) {
|
||||||
|
getCart(cartId: $cartId) {
|
||||||
|
Id
|
||||||
|
userId
|
||||||
|
eateryId
|
||||||
|
items {
|
||||||
|
productId
|
||||||
|
quantity
|
||||||
|
priceAtTimeOfAdding
|
||||||
|
subTotal
|
||||||
|
}
|
||||||
|
totalAmount
|
||||||
|
paymentQrUrl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const GET_EATERY = gql`
|
||||||
|
query GetEateryMenu {
|
||||||
|
allEateries {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const CREATE_CART = gql`
|
||||||
|
mutation createCart($eateryId: String!) {
|
||||||
|
createCart(eateryId: $eateryId)
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const ADD_ITEM = gql`
|
||||||
|
mutation addItem(
|
||||||
|
$cartId: String!
|
||||||
|
$menuItemId: String!
|
||||||
|
$quantity: BigInteger!
|
||||||
|
) {
|
||||||
|
addItem(cartId: $cartId, menuItemId: $menuItemId, quantity: $quantity) {
|
||||||
|
Id
|
||||||
|
userId
|
||||||
|
eateryId
|
||||||
|
items {
|
||||||
|
productId
|
||||||
|
quantity
|
||||||
|
priceAtTimeOfAdding
|
||||||
|
subTotal
|
||||||
|
}
|
||||||
|
totalAmount
|
||||||
|
paymentQrUrl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
export function CartProvider({ children }: { children: React.ReactNode }) {
|
export function CartProvider({ children }: { children: React.ReactNode }) {
|
||||||
const [cart, setCart] = useState<CartEntity>(null!);
|
const [cart, setCart] = useState<CartEntity>(null!);
|
||||||
const [cartId, setCartId] = useState<string | null>(null);
|
const [cartId, setCartId] = useState<string | null>(null);
|
||||||
@@ -40,110 +104,89 @@ export function CartProvider({ children }: { children: React.ReactNode }) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
const createCartFunc = async () => {
|
||||||
|
if (eateryData && eateryData.allEateries?.length > 0) {
|
||||||
try {
|
try {
|
||||||
const raw = localStorage.getItem(STORAGE_KEY);
|
const firstEateryId = eateryData.allEateries[0].id;
|
||||||
if (!raw) return;
|
const { data: mutationResult } = await createCart({
|
||||||
const parsed = JSON.parse(raw) as CartItemEntity[];
|
variables: { eateryId: firstEateryId },
|
||||||
if (Array.isArray(parsed)) {
|
});
|
||||||
setItems(parsed.filter((i) => i && i.productId && i.quantity > 0));
|
|
||||||
|
const newCartId = mutationResult!.createCart;
|
||||||
|
if (newCartId) {
|
||||||
|
localStorage.setItem(CART_ID, newCartId);
|
||||||
|
setCartId(newCartId);
|
||||||
}
|
}
|
||||||
} catch {
|
} catch (err) {
|
||||||
localStorage.removeItem(STORAGE_KEY);
|
console.error("Lỗi khi tạo giỏ hàng:", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (error) createCartFunc();
|
||||||
|
else {
|
||||||
|
const localCartId = localStorage.getItem(CART_ID);
|
||||||
|
if (localCartId) setCartId(localCartId);
|
||||||
}
|
}
|
||||||
}, [eateryData, createCart, data, loading, error]);
|
}, [eateryData, createCart, data, loading, error]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(items));
|
if (data?.getCart) setCart(data.getCart);
|
||||||
}, [items]);
|
}, [data]);
|
||||||
|
|
||||||
const addToCart = (product: MenuItemEntity) => {
|
const addToCart = async (product: CartItemEntity) => {
|
||||||
setItems((prev) => {
|
if (!cartId) return;
|
||||||
const index = prev.findIndex((i) => i.productId === product.id);
|
|
||||||
if (index === -1) {
|
const { data: result } = await addMenuItem({
|
||||||
return [
|
variables: {
|
||||||
...prev,
|
cartId,
|
||||||
{
|
menuItemId: product.productId!,
|
||||||
productId: product.id!,
|
quantity: product.quantity,
|
||||||
name: product.name,
|
|
||||||
description: product.description,
|
|
||||||
priceAtTimeOfAdding: product.price,
|
|
||||||
quantity: 1,
|
|
||||||
},
|
},
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
const next = [...prev];
|
|
||||||
next[index] = { ...next[index], quantity: next[index].quantity + 1 };
|
|
||||||
return next;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (result) setCart(result.addItem);
|
||||||
};
|
};
|
||||||
|
|
||||||
const increaseQty = (id: string) => {
|
const setQuantity = async (id: string, quantity: number) => {
|
||||||
setItems((prev) =>
|
if (!cartId) return;
|
||||||
prev.map((item) =>
|
|
||||||
item.productId === id ? { ...item, quantity: item.quantity + 1 } : item,
|
const { data: result } = await addMenuItem({
|
||||||
),
|
variables: {
|
||||||
);
|
cartId,
|
||||||
|
menuItemId: id,
|
||||||
|
quantity,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result) setCart(result.addItem);
|
||||||
};
|
};
|
||||||
|
|
||||||
const decreaseQty = (id: string) => {
|
const removeFromCart = (id: string) => setQuantity(id, 0);
|
||||||
setItems((prev) =>
|
|
||||||
prev
|
|
||||||
.map((item) =>
|
|
||||||
item.productId === id
|
|
||||||
? { ...item, quantity: Math.max(0, item.quantity - 1) }
|
|
||||||
: item,
|
|
||||||
)
|
|
||||||
.filter((item) => item.quantity > 0),
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const removeFromCart = (id: string) => {
|
const increaseQty = (id: string) =>
|
||||||
setItems((prev) => prev.filter((item) => item.productId !== id));
|
setQuantity(id, cart.items.find((i) => i.productId == id)!.quantity + 1);
|
||||||
};
|
|
||||||
|
|
||||||
const setQuantity = (id: string, quantity: number) => {
|
const decreaseQty = (id: string) =>
|
||||||
const safeQty = Number.isFinite(quantity)
|
setQuantity(id, cart.items.find((i) => i.productId == id)!.quantity - 1);
|
||||||
? Math.max(0, Math.floor(quantity))
|
|
||||||
: 0;
|
|
||||||
if (safeQty === 0) {
|
|
||||||
removeFromCart(id);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setItems((prev) =>
|
|
||||||
prev.map((item) =>
|
|
||||||
item.productId === id ? { ...item, quantity: safeQty } : item,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const totalItems = useMemo(
|
const totalItems = useMemo(
|
||||||
() => items.reduce((sum, item) => sum + item.quantity, 0),
|
() => cart?.items.reduce((sum, item) => sum + item.quantity, 0),
|
||||||
[items],
|
[cart],
|
||||||
);
|
|
||||||
|
|
||||||
const totalPrice = useMemo(
|
|
||||||
() =>
|
|
||||||
items.reduce(
|
|
||||||
(sum, item) => sum + item.priceAtTimeOfAdding * item.quantity,
|
|
||||||
0,
|
|
||||||
),
|
|
||||||
[items],
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const value = useMemo(
|
const value = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
items,
|
items: cart?.items,
|
||||||
totalItems,
|
totalItems,
|
||||||
totalPrice,
|
totalPrice: cart?.totalAmount,
|
||||||
addToCart,
|
addToCart,
|
||||||
increaseQty,
|
increaseQty,
|
||||||
decreaseQty,
|
decreaseQty,
|
||||||
removeFromCart,
|
removeFromCart,
|
||||||
setQuantity,
|
setQuantity,
|
||||||
}),
|
}),
|
||||||
[items, totalItems, totalPrice],
|
[cart?.items, cart?.totalAmount],
|
||||||
);
|
);
|
||||||
|
|
||||||
return <CartContext.Provider value={value}>{children}</CartContext.Provider>;
|
return <CartContext.Provider value={value}>{children}</CartContext.Provider>;
|
||||||
|
|||||||
@@ -33,189 +33,6 @@ export const SOCIAL_LINKS: SocialLinks = {
|
|||||||
website: "/",
|
website: "/",
|
||||||
};
|
};
|
||||||
|
|
||||||
// ===== MOCK PRODUCTS =====
|
|
||||||
// Placeholder data – replace with real API calls when backend is ready
|
|
||||||
export const MOCK_PRODUCTS: MenuItemEntity[] = [
|
|
||||||
{
|
|
||||||
id: "1",
|
|
||||||
name: "Black Coffee",
|
|
||||||
price: 25000,
|
|
||||||
imageUrl: "/imgs/products/placeholder.jpg",
|
|
||||||
description:
|
|
||||||
"Traditional Vietnamese black coffee, rich and bold, brewed by hand with a phin filter.",
|
|
||||||
available: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "2",
|
|
||||||
name: "Milk Coffee",
|
|
||||||
price: 30000,
|
|
||||||
imageUrl: "/imgs/products/placeholder.jpg",
|
|
||||||
description:
|
|
||||||
"Aromatic condensed milk coffee, rich and creamy — a perfect blend of strong coffee and sweet condensed milk.",
|
|
||||||
available: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "3",
|
|
||||||
name: "White Coffee",
|
|
||||||
|
|
||||||
price: 32000,
|
|
||||||
imageUrl: "/imgs/products/placeholder.jpg",
|
|
||||||
description:
|
|
||||||
"Light and mild, with less coffee and more milk — perfect for those just starting to enjoy coffee.",
|
|
||||||
available: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "4",
|
|
||||||
name: "Egg Coffee",
|
|
||||||
|
|
||||||
price: 45000,
|
|
||||||
imageUrl: "/imgs/products/placeholder.jpg",
|
|
||||||
description:
|
|
||||||
"A Hanoi specialty — smooth, velvety egg cream layered over a bold coffee base.",
|
|
||||||
available: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "5",
|
|
||||||
name: "Peach Orange Lemongrass Tea",
|
|
||||||
|
|
||||||
price: 35000,
|
|
||||||
imageUrl: "/imgs/products/placeholder.jpg",
|
|
||||||
description:
|
|
||||||
"Fragrant peach tea with fresh orange and lemongrass — refreshing and wonderfully cooling.",
|
|
||||||
available: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "6",
|
|
||||||
name: "Matcha Green Tea",
|
|
||||||
|
|
||||||
price: 40000,
|
|
||||||
imageUrl: "/imgs/products/placeholder.jpg",
|
|
||||||
description:
|
|
||||||
"Pure Japanese matcha with a signature light bitterness — aromatic, refreshing, and nutritious.",
|
|
||||||
available: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "7",
|
|
||||||
name: "Lychee Jasmine Tea",
|
|
||||||
|
|
||||||
price: 38000,
|
|
||||||
imageUrl: "/imgs/products/placeholder.jpg",
|
|
||||||
description:
|
|
||||||
"Sweet lychee tea delicately infused with jasmine blossom — a calming and soulful sip.",
|
|
||||||
available: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "8",
|
|
||||||
name: "Yogurt with Tapioca Pearls",
|
|
||||||
|
|
||||||
price: 38000,
|
|
||||||
imageUrl: "/imgs/products/placeholder.jpg",
|
|
||||||
description:
|
|
||||||
"Smooth, creamy yogurt paired with chewy black tapioca pearls — a perfectly balanced sweet and tangy treat.",
|
|
||||||
available: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "9",
|
|
||||||
name: "Strawberry Yogurt",
|
|
||||||
|
|
||||||
price: 40000,
|
|
||||||
imageUrl: "/imgs/products/placeholder.jpg",
|
|
||||||
description:
|
|
||||||
"Chilled yogurt with fresh sweet-tart strawberries, packed with vitamins and minerals.",
|
|
||||||
available: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "10",
|
|
||||||
name: "Fresh Orange Juice",
|
|
||||||
|
|
||||||
price: 35000,
|
|
||||||
imageUrl: "/imgs/products/placeholder.jpg",
|
|
||||||
description:
|
|
||||||
"100% freshly squeezed orange juice, rich in vitamin C and great for your health.",
|
|
||||||
available: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "11",
|
|
||||||
name: "Watermelon Juice",
|
|
||||||
|
|
||||||
price: 30000,
|
|
||||||
imageUrl: "/imgs/products/placeholder.jpg",
|
|
||||||
description:
|
|
||||||
"Ice-cold watermelon juice — instantly refreshing on hot summer days.",
|
|
||||||
available: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "12",
|
|
||||||
name: "Caramel Latte",
|
|
||||||
|
|
||||||
price: 45000,
|
|
||||||
imageUrl: "/imgs/products/placeholder.jpg",
|
|
||||||
description:
|
|
||||||
"Sweet and indulgent caramel latte with a velvety milk foam topping and a drizzle of caramel sauce.",
|
|
||||||
available: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "13",
|
|
||||||
name: "Vanilla Latte",
|
|
||||||
|
|
||||||
price: 45000,
|
|
||||||
imageUrl: "/imgs/products/placeholder.jpg",
|
|
||||||
description:
|
|
||||||
"Smooth and gentle vanilla latte with a delicate natural vanilla fragrance.",
|
|
||||||
available: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "14",
|
|
||||||
name: "Toasted Butter Bread",
|
|
||||||
|
|
||||||
price: 20000,
|
|
||||||
imageUrl: "/imgs/products/placeholder.jpg",
|
|
||||||
description:
|
|
||||||
"Crispy toasted bread spread with fragrant butter and strawberry jam — the perfect coffee companion.",
|
|
||||||
available: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "15",
|
|
||||||
name: "Caramel Flan",
|
|
||||||
|
|
||||||
price: 25000,
|
|
||||||
imageUrl: "/imgs/products/placeholder.jpg",
|
|
||||||
description:
|
|
||||||
"Silky smooth flan with a golden caramel topping that melts in your mouth.",
|
|
||||||
available: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "16",
|
|
||||||
name: "Black Tapioca Pearls",
|
|
||||||
|
|
||||||
price: 10000,
|
|
||||||
imageUrl: "/imgs/products/placeholder.jpg",
|
|
||||||
description:
|
|
||||||
"Chewy black tapioca pearls — add them to any drink for extra flavor and texture.",
|
|
||||||
available: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "17",
|
|
||||||
name: "Coffee Jelly",
|
|
||||||
|
|
||||||
price: 10000,
|
|
||||||
imageUrl: "/imgs/products/placeholder.jpg",
|
|
||||||
description:
|
|
||||||
"Cool coffee jelly that adds a distinctive flavor boost to your drink.",
|
|
||||||
available: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "18",
|
|
||||||
name: "White Tapioca Pearls",
|
|
||||||
|
|
||||||
price: 10000,
|
|
||||||
imageUrl: "/imgs/products/placeholder.jpg",
|
|
||||||
description:
|
|
||||||
"Chewy white tapioca pearls — add them to any drink for extra flavor and texture.",
|
|
||||||
available: true,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
// ===== MOCK SHOPS (for Feed page) =====
|
// ===== MOCK SHOPS (for Feed page) =====
|
||||||
export const MOCK_SHOPS: Shop[] = [
|
export const MOCK_SHOPS: Shop[] = [
|
||||||
{
|
{
|
||||||
|
|||||||
+16
-1
@@ -125,6 +125,7 @@ export interface MenuItemEntity {
|
|||||||
export interface ShiftEntity {}
|
export interface ShiftEntity {}
|
||||||
|
|
||||||
export interface EateryEntity {
|
export interface EateryEntity {
|
||||||
|
id: string;
|
||||||
ownerId: string;
|
ownerId: string;
|
||||||
name: string;
|
name: string;
|
||||||
menuItems: MenuItemEntity[];
|
menuItems: MenuItemEntity[];
|
||||||
@@ -150,10 +151,24 @@ export interface deleteMenuItemMutation {
|
|||||||
export interface CartItemEntity {
|
export interface CartItemEntity {
|
||||||
productId: string;
|
productId: string;
|
||||||
quantity: number;
|
quantity: number;
|
||||||
priceAtTimeOfAdding: number;
|
priceAtTimeOfAdding?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CartEntity {
|
export interface CartEntity {
|
||||||
Id: string;
|
Id: string;
|
||||||
items: CartItemEntity[];
|
items: CartItemEntity[];
|
||||||
|
totalAmount: number;
|
||||||
|
paymentQrUrl: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface getCartQuery {
|
||||||
|
getCart: CartEntity;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface createCartMutation {
|
||||||
|
createCart: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface addMenuItemMutation {
|
||||||
|
addItem: CartEntity;
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user