better item primary key and cart updates, balance updating WIP

This commit is contained in:
ZareMate 2026-01-29 02:41:31 +01:00
parent 9e52f569f8
commit f5c581000e
12 changed files with 377 additions and 127 deletions

File diff suppressed because one or more lines are too long

View File

@ -167,16 +167,16 @@ exports.Prisma.ShopScalarFieldEnum = {
exports.Prisma.ItemScalarFieldEnum = { exports.Prisma.ItemScalarFieldEnum = {
item_name: 'item_name', item_name: 'item_name',
stock: 'stock', shopId: 'shopId',
shopId: 'shopId' stock: 'stock'
}; };
exports.Prisma.SellableScalarFieldEnum = { exports.Prisma.SellableScalarFieldEnum = {
id: 'id', id: 'id',
item_name: 'item_name', item_name: 'item_name',
shopId: 'shopId',
amount: 'amount', amount: 'amount',
price: 'price', price: 'price',
shopId: 'shopId',
enabled: 'enabled' enabled: 'enabled'
}; };

View File

@ -6756,61 +6756,61 @@ export namespace Prisma {
} }
export type ItemAvgAggregateOutputType = { export type ItemAvgAggregateOutputType = {
stock: number | null
shopId: number | null shopId: number | null
stock: number | null
} }
export type ItemSumAggregateOutputType = { export type ItemSumAggregateOutputType = {
stock: number | null
shopId: number | null shopId: number | null
stock: number | null
} }
export type ItemMinAggregateOutputType = { export type ItemMinAggregateOutputType = {
item_name: string | null item_name: string | null
stock: number | null
shopId: number | null shopId: number | null
stock: number | null
} }
export type ItemMaxAggregateOutputType = { export type ItemMaxAggregateOutputType = {
item_name: string | null item_name: string | null
stock: number | null
shopId: number | null shopId: number | null
stock: number | null
} }
export type ItemCountAggregateOutputType = { export type ItemCountAggregateOutputType = {
item_name: number item_name: number
stock: number
shopId: number shopId: number
stock: number
_all: number _all: number
} }
export type ItemAvgAggregateInputType = { export type ItemAvgAggregateInputType = {
stock?: true
shopId?: true shopId?: true
stock?: true
} }
export type ItemSumAggregateInputType = { export type ItemSumAggregateInputType = {
stock?: true
shopId?: true shopId?: true
stock?: true
} }
export type ItemMinAggregateInputType = { export type ItemMinAggregateInputType = {
item_name?: true item_name?: true
stock?: true
shopId?: true shopId?: true
stock?: true
} }
export type ItemMaxAggregateInputType = { export type ItemMaxAggregateInputType = {
item_name?: true item_name?: true
stock?: true
shopId?: true shopId?: true
stock?: true
} }
export type ItemCountAggregateInputType = { export type ItemCountAggregateInputType = {
item_name?: true item_name?: true
stock?: true
shopId?: true shopId?: true
stock?: true
_all?: true _all?: true
} }
@ -6902,8 +6902,8 @@ export namespace Prisma {
export type ItemGroupByOutputType = { export type ItemGroupByOutputType = {
item_name: string item_name: string
stock: number
shopId: number shopId: number
stock: number
_count: ItemCountAggregateOutputType | null _count: ItemCountAggregateOutputType | null
_avg: ItemAvgAggregateOutputType | null _avg: ItemAvgAggregateOutputType | null
_sum: ItemSumAggregateOutputType | null _sum: ItemSumAggregateOutputType | null
@ -6927,8 +6927,8 @@ export namespace Prisma {
export type ItemSelect<ExtArgs extends $Extensions.InternalArgs = $Extensions.DefaultArgs> = $Extensions.GetSelect<{ export type ItemSelect<ExtArgs extends $Extensions.InternalArgs = $Extensions.DefaultArgs> = $Extensions.GetSelect<{
item_name?: boolean item_name?: boolean
stock?: boolean
shopId?: boolean shopId?: boolean
stock?: boolean
shop?: boolean | ShopDefaultArgs<ExtArgs> shop?: boolean | ShopDefaultArgs<ExtArgs>
sellables?: boolean | Item$sellablesArgs<ExtArgs> sellables?: boolean | Item$sellablesArgs<ExtArgs>
_count?: boolean | ItemCountOutputTypeDefaultArgs<ExtArgs> _count?: boolean | ItemCountOutputTypeDefaultArgs<ExtArgs>
@ -6938,11 +6938,11 @@ export namespace Prisma {
export type ItemSelectScalar = { export type ItemSelectScalar = {
item_name?: boolean item_name?: boolean
stock?: boolean
shopId?: boolean shopId?: boolean
stock?: boolean
} }
export type ItemOmit<ExtArgs extends $Extensions.InternalArgs = $Extensions.DefaultArgs> = $Extensions.GetOmit<"item_name" | "stock" | "shopId", ExtArgs["result"]["item"]> export type ItemOmit<ExtArgs extends $Extensions.InternalArgs = $Extensions.DefaultArgs> = $Extensions.GetOmit<"item_name" | "shopId" | "stock", ExtArgs["result"]["item"]>
export type ItemInclude<ExtArgs extends $Extensions.InternalArgs = $Extensions.DefaultArgs> = { export type ItemInclude<ExtArgs extends $Extensions.InternalArgs = $Extensions.DefaultArgs> = {
shop?: boolean | ShopDefaultArgs<ExtArgs> shop?: boolean | ShopDefaultArgs<ExtArgs>
sellables?: boolean | Item$sellablesArgs<ExtArgs> sellables?: boolean | Item$sellablesArgs<ExtArgs>
@ -6957,8 +6957,8 @@ export namespace Prisma {
} }
scalars: $Extensions.GetPayloadResult<{ scalars: $Extensions.GetPayloadResult<{
item_name: string item_name: string
stock: number
shopId: number shopId: number
stock: number
}, ExtArgs["result"]["item"]> }, ExtArgs["result"]["item"]>
composites: {} composites: {}
} }
@ -7331,8 +7331,8 @@ export namespace Prisma {
*/ */
interface ItemFieldRefs { interface ItemFieldRefs {
readonly item_name: FieldRef<"Item", 'String'> readonly item_name: FieldRef<"Item", 'String'>
readonly stock: FieldRef<"Item", 'Int'>
readonly shopId: FieldRef<"Item", 'Int'> readonly shopId: FieldRef<"Item", 'Int'>
readonly stock: FieldRef<"Item", 'Int'>
} }
@ -7731,82 +7731,82 @@ export namespace Prisma {
} }
export type SellableAvgAggregateOutputType = { export type SellableAvgAggregateOutputType = {
shopId: number | null
amount: number | null amount: number | null
price: number | null price: number | null
shopId: number | null
} }
export type SellableSumAggregateOutputType = { export type SellableSumAggregateOutputType = {
shopId: number | null
amount: number | null amount: number | null
price: number | null price: number | null
shopId: number | null
} }
export type SellableMinAggregateOutputType = { export type SellableMinAggregateOutputType = {
id: string | null id: string | null
item_name: string | null item_name: string | null
shopId: number | null
amount: number | null amount: number | null
price: number | null price: number | null
shopId: number | null
enabled: boolean | null enabled: boolean | null
} }
export type SellableMaxAggregateOutputType = { export type SellableMaxAggregateOutputType = {
id: string | null id: string | null
item_name: string | null item_name: string | null
shopId: number | null
amount: number | null amount: number | null
price: number | null price: number | null
shopId: number | null
enabled: boolean | null enabled: boolean | null
} }
export type SellableCountAggregateOutputType = { export type SellableCountAggregateOutputType = {
id: number id: number
item_name: number item_name: number
shopId: number
amount: number amount: number
price: number price: number
shopId: number
enabled: number enabled: number
_all: number _all: number
} }
export type SellableAvgAggregateInputType = { export type SellableAvgAggregateInputType = {
shopId?: true
amount?: true amount?: true
price?: true price?: true
shopId?: true
} }
export type SellableSumAggregateInputType = { export type SellableSumAggregateInputType = {
shopId?: true
amount?: true amount?: true
price?: true price?: true
shopId?: true
} }
export type SellableMinAggregateInputType = { export type SellableMinAggregateInputType = {
id?: true id?: true
item_name?: true item_name?: true
shopId?: true
amount?: true amount?: true
price?: true price?: true
shopId?: true
enabled?: true enabled?: true
} }
export type SellableMaxAggregateInputType = { export type SellableMaxAggregateInputType = {
id?: true id?: true
item_name?: true item_name?: true
shopId?: true
amount?: true amount?: true
price?: true price?: true
shopId?: true
enabled?: true enabled?: true
} }
export type SellableCountAggregateInputType = { export type SellableCountAggregateInputType = {
id?: true id?: true
item_name?: true item_name?: true
shopId?: true
amount?: true amount?: true
price?: true price?: true
shopId?: true
enabled?: true enabled?: true
_all?: true _all?: true
} }
@ -7900,9 +7900,9 @@ export namespace Prisma {
export type SellableGroupByOutputType = { export type SellableGroupByOutputType = {
id: string id: string
item_name: string item_name: string
shopId: number
amount: number amount: number
price: number price: number
shopId: number
enabled: boolean enabled: boolean
_count: SellableCountAggregateOutputType | null _count: SellableCountAggregateOutputType | null
_avg: SellableAvgAggregateOutputType | null _avg: SellableAvgAggregateOutputType | null
@ -7928,9 +7928,9 @@ export namespace Prisma {
export type SellableSelect<ExtArgs extends $Extensions.InternalArgs = $Extensions.DefaultArgs> = $Extensions.GetSelect<{ export type SellableSelect<ExtArgs extends $Extensions.InternalArgs = $Extensions.DefaultArgs> = $Extensions.GetSelect<{
id?: boolean id?: boolean
item_name?: boolean item_name?: boolean
shopId?: boolean
amount?: boolean amount?: boolean
price?: boolean price?: boolean
shopId?: boolean
enabled?: boolean enabled?: boolean
shop?: boolean | ShopDefaultArgs<ExtArgs> shop?: boolean | ShopDefaultArgs<ExtArgs>
item?: boolean | ItemDefaultArgs<ExtArgs> item?: boolean | ItemDefaultArgs<ExtArgs>
@ -7943,13 +7943,13 @@ export namespace Prisma {
export type SellableSelectScalar = { export type SellableSelectScalar = {
id?: boolean id?: boolean
item_name?: boolean item_name?: boolean
shopId?: boolean
amount?: boolean amount?: boolean
price?: boolean price?: boolean
shopId?: boolean
enabled?: boolean enabled?: boolean
} }
export type SellableOmit<ExtArgs extends $Extensions.InternalArgs = $Extensions.DefaultArgs> = $Extensions.GetOmit<"id" | "item_name" | "amount" | "price" | "shopId" | "enabled", ExtArgs["result"]["sellable"]> export type SellableOmit<ExtArgs extends $Extensions.InternalArgs = $Extensions.DefaultArgs> = $Extensions.GetOmit<"id" | "item_name" | "shopId" | "amount" | "price" | "enabled", ExtArgs["result"]["sellable"]>
export type SellableInclude<ExtArgs extends $Extensions.InternalArgs = $Extensions.DefaultArgs> = { export type SellableInclude<ExtArgs extends $Extensions.InternalArgs = $Extensions.DefaultArgs> = {
shop?: boolean | ShopDefaultArgs<ExtArgs> shop?: boolean | ShopDefaultArgs<ExtArgs>
item?: boolean | ItemDefaultArgs<ExtArgs> item?: boolean | ItemDefaultArgs<ExtArgs>
@ -7967,9 +7967,9 @@ export namespace Prisma {
scalars: $Extensions.GetPayloadResult<{ scalars: $Extensions.GetPayloadResult<{
id: string id: string
item_name: string item_name: string
shopId: number
amount: number amount: number
price: number price: number
shopId: number
enabled: boolean enabled: boolean
}, ExtArgs["result"]["sellable"]> }, ExtArgs["result"]["sellable"]>
composites: {} composites: {}
@ -8345,9 +8345,9 @@ export namespace Prisma {
interface SellableFieldRefs { interface SellableFieldRefs {
readonly id: FieldRef<"Sellable", 'String'> readonly id: FieldRef<"Sellable", 'String'>
readonly item_name: FieldRef<"Sellable", 'String'> readonly item_name: FieldRef<"Sellable", 'String'>
readonly shopId: FieldRef<"Sellable", 'Int'>
readonly amount: FieldRef<"Sellable", 'Int'> readonly amount: FieldRef<"Sellable", 'Int'>
readonly price: FieldRef<"Sellable", 'Float'> readonly price: FieldRef<"Sellable", 'Float'>
readonly shopId: FieldRef<"Sellable", 'Int'>
readonly enabled: FieldRef<"Sellable", 'Boolean'> readonly enabled: FieldRef<"Sellable", 'Boolean'>
} }
@ -11576,8 +11576,8 @@ export namespace Prisma {
export const ItemScalarFieldEnum: { export const ItemScalarFieldEnum: {
item_name: 'item_name', item_name: 'item_name',
stock: 'stock', shopId: 'shopId',
shopId: 'shopId' stock: 'stock'
}; };
export type ItemScalarFieldEnum = (typeof ItemScalarFieldEnum)[keyof typeof ItemScalarFieldEnum] export type ItemScalarFieldEnum = (typeof ItemScalarFieldEnum)[keyof typeof ItemScalarFieldEnum]
@ -11586,9 +11586,9 @@ export namespace Prisma {
export const SellableScalarFieldEnum: { export const SellableScalarFieldEnum: {
id: 'id', id: 'id',
item_name: 'item_name', item_name: 'item_name',
shopId: 'shopId',
amount: 'amount', amount: 'amount',
price: 'price', price: 'price',
shopId: 'shopId',
enabled: 'enabled' enabled: 'enabled'
}; };
@ -12098,36 +12098,37 @@ export namespace Prisma {
OR?: ItemWhereInput[] OR?: ItemWhereInput[]
NOT?: ItemWhereInput | ItemWhereInput[] NOT?: ItemWhereInput | ItemWhereInput[]
item_name?: StringFilter<"Item"> | string item_name?: StringFilter<"Item"> | string
stock?: IntFilter<"Item"> | number
shopId?: IntFilter<"Item"> | number shopId?: IntFilter<"Item"> | number
stock?: IntFilter<"Item"> | number
shop?: XOR<ShopScalarRelationFilter, ShopWhereInput> shop?: XOR<ShopScalarRelationFilter, ShopWhereInput>
sellables?: SellableListRelationFilter sellables?: SellableListRelationFilter
} }
export type ItemOrderByWithRelationInput = { export type ItemOrderByWithRelationInput = {
item_name?: SortOrder item_name?: SortOrder
stock?: SortOrder
shopId?: SortOrder shopId?: SortOrder
stock?: SortOrder
shop?: ShopOrderByWithRelationInput shop?: ShopOrderByWithRelationInput
sellables?: SellableOrderByRelationAggregateInput sellables?: SellableOrderByRelationAggregateInput
_relevance?: ItemOrderByRelevanceInput _relevance?: ItemOrderByRelevanceInput
} }
export type ItemWhereUniqueInput = Prisma.AtLeast<{ export type ItemWhereUniqueInput = Prisma.AtLeast<{
item_name?: string item_name_shopId?: ItemItem_nameShopIdCompoundUniqueInput
AND?: ItemWhereInput | ItemWhereInput[] AND?: ItemWhereInput | ItemWhereInput[]
OR?: ItemWhereInput[] OR?: ItemWhereInput[]
NOT?: ItemWhereInput | ItemWhereInput[] NOT?: ItemWhereInput | ItemWhereInput[]
stock?: IntFilter<"Item"> | number item_name?: StringFilter<"Item"> | string
shopId?: IntFilter<"Item"> | number shopId?: IntFilter<"Item"> | number
stock?: IntFilter<"Item"> | number
shop?: XOR<ShopScalarRelationFilter, ShopWhereInput> shop?: XOR<ShopScalarRelationFilter, ShopWhereInput>
sellables?: SellableListRelationFilter sellables?: SellableListRelationFilter
}, "item_name"> }, "item_name_shopId">
export type ItemOrderByWithAggregationInput = { export type ItemOrderByWithAggregationInput = {
item_name?: SortOrder item_name?: SortOrder
stock?: SortOrder
shopId?: SortOrder shopId?: SortOrder
stock?: SortOrder
_count?: ItemCountOrderByAggregateInput _count?: ItemCountOrderByAggregateInput
_avg?: ItemAvgOrderByAggregateInput _avg?: ItemAvgOrderByAggregateInput
_max?: ItemMaxOrderByAggregateInput _max?: ItemMaxOrderByAggregateInput
@ -12140,8 +12141,8 @@ export namespace Prisma {
OR?: ItemScalarWhereWithAggregatesInput[] OR?: ItemScalarWhereWithAggregatesInput[]
NOT?: ItemScalarWhereWithAggregatesInput | ItemScalarWhereWithAggregatesInput[] NOT?: ItemScalarWhereWithAggregatesInput | ItemScalarWhereWithAggregatesInput[]
item_name?: StringWithAggregatesFilter<"Item"> | string item_name?: StringWithAggregatesFilter<"Item"> | string
stock?: IntWithAggregatesFilter<"Item"> | number
shopId?: IntWithAggregatesFilter<"Item"> | number shopId?: IntWithAggregatesFilter<"Item"> | number
stock?: IntWithAggregatesFilter<"Item"> | number
} }
export type SellableWhereInput = { export type SellableWhereInput = {
@ -12150,9 +12151,9 @@ export namespace Prisma {
NOT?: SellableWhereInput | SellableWhereInput[] NOT?: SellableWhereInput | SellableWhereInput[]
id?: StringFilter<"Sellable"> | string id?: StringFilter<"Sellable"> | string
item_name?: StringFilter<"Sellable"> | string item_name?: StringFilter<"Sellable"> | string
shopId?: IntFilter<"Sellable"> | number
amount?: IntFilter<"Sellable"> | number amount?: IntFilter<"Sellable"> | number
price?: FloatFilter<"Sellable"> | number price?: FloatFilter<"Sellable"> | number
shopId?: IntFilter<"Sellable"> | number
enabled?: BoolFilter<"Sellable"> | boolean enabled?: BoolFilter<"Sellable"> | boolean
shop?: XOR<ShopScalarRelationFilter, ShopWhereInput> shop?: XOR<ShopScalarRelationFilter, ShopWhereInput>
item?: XOR<ItemScalarRelationFilter, ItemWhereInput> item?: XOR<ItemScalarRelationFilter, ItemWhereInput>
@ -12162,9 +12163,9 @@ export namespace Prisma {
export type SellableOrderByWithRelationInput = { export type SellableOrderByWithRelationInput = {
id?: SortOrder id?: SortOrder
item_name?: SortOrder item_name?: SortOrder
shopId?: SortOrder
amount?: SortOrder amount?: SortOrder
price?: SortOrder price?: SortOrder
shopId?: SortOrder
enabled?: SortOrder enabled?: SortOrder
shop?: ShopOrderByWithRelationInput shop?: ShopOrderByWithRelationInput
item?: ItemOrderByWithRelationInput item?: ItemOrderByWithRelationInput
@ -12178,9 +12179,9 @@ export namespace Prisma {
OR?: SellableWhereInput[] OR?: SellableWhereInput[]
NOT?: SellableWhereInput | SellableWhereInput[] NOT?: SellableWhereInput | SellableWhereInput[]
item_name?: StringFilter<"Sellable"> | string item_name?: StringFilter<"Sellable"> | string
shopId?: IntFilter<"Sellable"> | number
amount?: IntFilter<"Sellable"> | number amount?: IntFilter<"Sellable"> | number
price?: FloatFilter<"Sellable"> | number price?: FloatFilter<"Sellable"> | number
shopId?: IntFilter<"Sellable"> | number
enabled?: BoolFilter<"Sellable"> | boolean enabled?: BoolFilter<"Sellable"> | boolean
shop?: XOR<ShopScalarRelationFilter, ShopWhereInput> shop?: XOR<ShopScalarRelationFilter, ShopWhereInput>
item?: XOR<ItemScalarRelationFilter, ItemWhereInput> item?: XOR<ItemScalarRelationFilter, ItemWhereInput>
@ -12190,9 +12191,9 @@ export namespace Prisma {
export type SellableOrderByWithAggregationInput = { export type SellableOrderByWithAggregationInput = {
id?: SortOrder id?: SortOrder
item_name?: SortOrder item_name?: SortOrder
shopId?: SortOrder
amount?: SortOrder amount?: SortOrder
price?: SortOrder price?: SortOrder
shopId?: SortOrder
enabled?: SortOrder enabled?: SortOrder
_count?: SellableCountOrderByAggregateInput _count?: SellableCountOrderByAggregateInput
_avg?: SellableAvgOrderByAggregateInput _avg?: SellableAvgOrderByAggregateInput
@ -12207,9 +12208,9 @@ export namespace Prisma {
NOT?: SellableScalarWhereWithAggregatesInput | SellableScalarWhereWithAggregatesInput[] NOT?: SellableScalarWhereWithAggregatesInput | SellableScalarWhereWithAggregatesInput[]
id?: StringWithAggregatesFilter<"Sellable"> | string id?: StringWithAggregatesFilter<"Sellable"> | string
item_name?: StringWithAggregatesFilter<"Sellable"> | string item_name?: StringWithAggregatesFilter<"Sellable"> | string
shopId?: IntWithAggregatesFilter<"Sellable"> | number
amount?: IntWithAggregatesFilter<"Sellable"> | number amount?: IntWithAggregatesFilter<"Sellable"> | number
price?: FloatWithAggregatesFilter<"Sellable"> | number price?: FloatWithAggregatesFilter<"Sellable"> | number
shopId?: IntWithAggregatesFilter<"Sellable"> | number
enabled?: BoolWithAggregatesFilter<"Sellable"> | boolean enabled?: BoolWithAggregatesFilter<"Sellable"> | boolean
} }
@ -12691,8 +12692,8 @@ export namespace Prisma {
export type ItemUncheckedCreateInput = { export type ItemUncheckedCreateInput = {
item_name: string item_name: string
stock: number
shopId: number shopId: number
stock: number
sellables?: SellableUncheckedCreateNestedManyWithoutItemInput sellables?: SellableUncheckedCreateNestedManyWithoutItemInput
} }
@ -12705,15 +12706,15 @@ export namespace Prisma {
export type ItemUncheckedUpdateInput = { export type ItemUncheckedUpdateInput = {
item_name?: StringFieldUpdateOperationsInput | string item_name?: StringFieldUpdateOperationsInput | string
stock?: IntFieldUpdateOperationsInput | number
shopId?: IntFieldUpdateOperationsInput | number shopId?: IntFieldUpdateOperationsInput | number
stock?: IntFieldUpdateOperationsInput | number
sellables?: SellableUncheckedUpdateManyWithoutItemNestedInput sellables?: SellableUncheckedUpdateManyWithoutItemNestedInput
} }
export type ItemCreateManyInput = { export type ItemCreateManyInput = {
item_name: string item_name: string
stock: number
shopId: number shopId: number
stock: number
} }
export type ItemUpdateManyMutationInput = { export type ItemUpdateManyMutationInput = {
@ -12723,8 +12724,8 @@ export namespace Prisma {
export type ItemUncheckedUpdateManyInput = { export type ItemUncheckedUpdateManyInput = {
item_name?: StringFieldUpdateOperationsInput | string item_name?: StringFieldUpdateOperationsInput | string
stock?: IntFieldUpdateOperationsInput | number
shopId?: IntFieldUpdateOperationsInput | number shopId?: IntFieldUpdateOperationsInput | number
stock?: IntFieldUpdateOperationsInput | number
} }
export type SellableCreateInput = { export type SellableCreateInput = {
@ -12740,9 +12741,9 @@ export namespace Prisma {
export type SellableUncheckedCreateInput = { export type SellableUncheckedCreateInput = {
id?: string id?: string
item_name: string item_name: string
shopId: number
amount: number amount: number
price: number price: number
shopId: number
enabled?: boolean enabled?: boolean
cartItems?: CartItemUncheckedCreateNestedManyWithoutSellableInput cartItems?: CartItemUncheckedCreateNestedManyWithoutSellableInput
} }
@ -12760,9 +12761,9 @@ export namespace Prisma {
export type SellableUncheckedUpdateInput = { export type SellableUncheckedUpdateInput = {
id?: StringFieldUpdateOperationsInput | string id?: StringFieldUpdateOperationsInput | string
item_name?: StringFieldUpdateOperationsInput | string item_name?: StringFieldUpdateOperationsInput | string
shopId?: IntFieldUpdateOperationsInput | number
amount?: IntFieldUpdateOperationsInput | number amount?: IntFieldUpdateOperationsInput | number
price?: FloatFieldUpdateOperationsInput | number price?: FloatFieldUpdateOperationsInput | number
shopId?: IntFieldUpdateOperationsInput | number
enabled?: BoolFieldUpdateOperationsInput | boolean enabled?: BoolFieldUpdateOperationsInput | boolean
cartItems?: CartItemUncheckedUpdateManyWithoutSellableNestedInput cartItems?: CartItemUncheckedUpdateManyWithoutSellableNestedInput
} }
@ -12770,9 +12771,9 @@ export namespace Prisma {
export type SellableCreateManyInput = { export type SellableCreateManyInput = {
id?: string id?: string
item_name: string item_name: string
shopId: number
amount: number amount: number
price: number price: number
shopId: number
enabled?: boolean enabled?: boolean
} }
@ -12786,9 +12787,9 @@ export namespace Prisma {
export type SellableUncheckedUpdateManyInput = { export type SellableUncheckedUpdateManyInput = {
id?: StringFieldUpdateOperationsInput | string id?: StringFieldUpdateOperationsInput | string
item_name?: StringFieldUpdateOperationsInput | string item_name?: StringFieldUpdateOperationsInput | string
shopId?: IntFieldUpdateOperationsInput | number
amount?: IntFieldUpdateOperationsInput | number amount?: IntFieldUpdateOperationsInput | number
price?: FloatFieldUpdateOperationsInput | number price?: FloatFieldUpdateOperationsInput | number
shopId?: IntFieldUpdateOperationsInput | number
enabled?: BoolFieldUpdateOperationsInput | boolean enabled?: BoolFieldUpdateOperationsInput | boolean
} }
@ -13391,32 +13392,37 @@ export namespace Prisma {
search: string search: string
} }
export type ItemItem_nameShopIdCompoundUniqueInput = {
item_name: string
shopId: number
}
export type ItemCountOrderByAggregateInput = { export type ItemCountOrderByAggregateInput = {
item_name?: SortOrder item_name?: SortOrder
stock?: SortOrder
shopId?: SortOrder shopId?: SortOrder
stock?: SortOrder
} }
export type ItemAvgOrderByAggregateInput = { export type ItemAvgOrderByAggregateInput = {
stock?: SortOrder
shopId?: SortOrder shopId?: SortOrder
stock?: SortOrder
} }
export type ItemMaxOrderByAggregateInput = { export type ItemMaxOrderByAggregateInput = {
item_name?: SortOrder item_name?: SortOrder
stock?: SortOrder
shopId?: SortOrder shopId?: SortOrder
stock?: SortOrder
} }
export type ItemMinOrderByAggregateInput = { export type ItemMinOrderByAggregateInput = {
item_name?: SortOrder item_name?: SortOrder
stock?: SortOrder
shopId?: SortOrder shopId?: SortOrder
stock?: SortOrder
} }
export type ItemSumOrderByAggregateInput = { export type ItemSumOrderByAggregateInput = {
stock?: SortOrder
shopId?: SortOrder shopId?: SortOrder
stock?: SortOrder
} }
export type BoolFilter<$PrismaModel = never> = { export type BoolFilter<$PrismaModel = never> = {
@ -13448,40 +13454,40 @@ export namespace Prisma {
export type SellableCountOrderByAggregateInput = { export type SellableCountOrderByAggregateInput = {
id?: SortOrder id?: SortOrder
item_name?: SortOrder item_name?: SortOrder
shopId?: SortOrder
amount?: SortOrder amount?: SortOrder
price?: SortOrder price?: SortOrder
shopId?: SortOrder
enabled?: SortOrder enabled?: SortOrder
} }
export type SellableAvgOrderByAggregateInput = { export type SellableAvgOrderByAggregateInput = {
shopId?: SortOrder
amount?: SortOrder amount?: SortOrder
price?: SortOrder price?: SortOrder
shopId?: SortOrder
} }
export type SellableMaxOrderByAggregateInput = { export type SellableMaxOrderByAggregateInput = {
id?: SortOrder id?: SortOrder
item_name?: SortOrder item_name?: SortOrder
shopId?: SortOrder
amount?: SortOrder amount?: SortOrder
price?: SortOrder price?: SortOrder
shopId?: SortOrder
enabled?: SortOrder enabled?: SortOrder
} }
export type SellableMinOrderByAggregateInput = { export type SellableMinOrderByAggregateInput = {
id?: SortOrder id?: SortOrder
item_name?: SortOrder item_name?: SortOrder
shopId?: SortOrder
amount?: SortOrder amount?: SortOrder
price?: SortOrder price?: SortOrder
shopId?: SortOrder
enabled?: SortOrder enabled?: SortOrder
} }
export type SellableSumOrderByAggregateInput = { export type SellableSumOrderByAggregateInput = {
shopId?: SortOrder
amount?: SortOrder amount?: SortOrder
price?: SortOrder price?: SortOrder
shopId?: SortOrder
} }
export type BoolWithAggregatesFilter<$PrismaModel = never> = { export type BoolWithAggregatesFilter<$PrismaModel = never> = {
@ -14934,8 +14940,8 @@ export namespace Prisma {
OR?: ItemScalarWhereInput[] OR?: ItemScalarWhereInput[]
NOT?: ItemScalarWhereInput | ItemScalarWhereInput[] NOT?: ItemScalarWhereInput | ItemScalarWhereInput[]
item_name?: StringFilter<"Item"> | string item_name?: StringFilter<"Item"> | string
stock?: IntFilter<"Item"> | number
shopId?: IntFilter<"Item"> | number shopId?: IntFilter<"Item"> | number
stock?: IntFilter<"Item"> | number
} }
export type SellableUpsertWithWhereUniqueWithoutShopInput = { export type SellableUpsertWithWhereUniqueWithoutShopInput = {
@ -14960,9 +14966,9 @@ export namespace Prisma {
NOT?: SellableScalarWhereInput | SellableScalarWhereInput[] NOT?: SellableScalarWhereInput | SellableScalarWhereInput[]
id?: StringFilter<"Sellable"> | string id?: StringFilter<"Sellable"> | string
item_name?: StringFilter<"Sellable"> | string item_name?: StringFilter<"Sellable"> | string
shopId?: IntFilter<"Sellable"> | number
amount?: IntFilter<"Sellable"> | number amount?: IntFilter<"Sellable"> | number
price?: FloatFilter<"Sellable"> | number price?: FloatFilter<"Sellable"> | number
shopId?: IntFilter<"Sellable"> | number
enabled?: BoolFilter<"Sellable"> | boolean enabled?: BoolFilter<"Sellable"> | boolean
} }
@ -14998,7 +15004,6 @@ export namespace Prisma {
id?: string id?: string
amount: number amount: number
price: number price: number
shopId: number
enabled?: boolean enabled?: boolean
cartItems?: CartItemUncheckedCreateNestedManyWithoutSellableInput cartItems?: CartItemUncheckedCreateNestedManyWithoutSellableInput
} }
@ -15081,8 +15086,8 @@ export namespace Prisma {
export type ItemUncheckedCreateWithoutSellablesInput = { export type ItemUncheckedCreateWithoutSellablesInput = {
item_name: string item_name: string
stock: number
shopId: number shopId: number
stock: number
} }
export type ItemCreateOrConnectWithoutSellablesInput = { export type ItemCreateOrConnectWithoutSellablesInput = {
@ -15154,8 +15159,8 @@ export namespace Prisma {
export type ItemUncheckedUpdateWithoutSellablesInput = { export type ItemUncheckedUpdateWithoutSellablesInput = {
item_name?: StringFieldUpdateOperationsInput | string item_name?: StringFieldUpdateOperationsInput | string
stock?: IntFieldUpdateOperationsInput | number
shopId?: IntFieldUpdateOperationsInput | number shopId?: IntFieldUpdateOperationsInput | number
stock?: IntFieldUpdateOperationsInput | number
} }
export type CartItemUpsertWithWhereUniqueWithoutSellableInput = { export type CartItemUpsertWithWhereUniqueWithoutSellableInput = {
@ -15312,9 +15317,9 @@ export namespace Prisma {
export type SellableUncheckedCreateWithoutCartItemsInput = { export type SellableUncheckedCreateWithoutCartItemsInput = {
id?: string id?: string
item_name: string item_name: string
shopId: number
amount: number amount: number
price: number price: number
shopId: number
enabled?: boolean enabled?: boolean
} }
@ -15365,9 +15370,9 @@ export namespace Prisma {
export type SellableUncheckedUpdateWithoutCartItemsInput = { export type SellableUncheckedUpdateWithoutCartItemsInput = {
id?: StringFieldUpdateOperationsInput | string id?: StringFieldUpdateOperationsInput | string
item_name?: StringFieldUpdateOperationsInput | string item_name?: StringFieldUpdateOperationsInput | string
shopId?: IntFieldUpdateOperationsInput | number
amount?: IntFieldUpdateOperationsInput | number amount?: IntFieldUpdateOperationsInput | number
price?: FloatFieldUpdateOperationsInput | number price?: FloatFieldUpdateOperationsInput | number
shopId?: IntFieldUpdateOperationsInput | number
enabled?: BoolFieldUpdateOperationsInput | boolean enabled?: BoolFieldUpdateOperationsInput | boolean
} }
@ -15643,7 +15648,6 @@ export namespace Prisma {
id?: string id?: string
amount: number amount: number
price: number price: number
shopId: number
enabled?: boolean enabled?: boolean
} }
@ -15660,7 +15664,6 @@ export namespace Prisma {
id?: StringFieldUpdateOperationsInput | string id?: StringFieldUpdateOperationsInput | string
amount?: IntFieldUpdateOperationsInput | number amount?: IntFieldUpdateOperationsInput | number
price?: FloatFieldUpdateOperationsInput | number price?: FloatFieldUpdateOperationsInput | number
shopId?: IntFieldUpdateOperationsInput | number
enabled?: BoolFieldUpdateOperationsInput | boolean enabled?: BoolFieldUpdateOperationsInput | boolean
cartItems?: CartItemUncheckedUpdateManyWithoutSellableNestedInput cartItems?: CartItemUncheckedUpdateManyWithoutSellableNestedInput
} }
@ -15669,7 +15672,6 @@ export namespace Prisma {
id?: StringFieldUpdateOperationsInput | string id?: StringFieldUpdateOperationsInput | string
amount?: IntFieldUpdateOperationsInput | number amount?: IntFieldUpdateOperationsInput | number
price?: FloatFieldUpdateOperationsInput | number price?: FloatFieldUpdateOperationsInput | number
shopId?: IntFieldUpdateOperationsInput | number
enabled?: BoolFieldUpdateOperationsInput | boolean enabled?: BoolFieldUpdateOperationsInput | boolean
} }

File diff suppressed because one or more lines are too long

View File

@ -1,5 +1,5 @@
{ {
"name": "prisma-client-a0e1bef334afa5b609bdf0c15431f28835b869da62270d0786ce071578cdedb4", "name": "prisma-client-60cecad7f5344b2ab216d0c215477a4a9f0fb3ec9e5201c830e0c4ac3caafb77",
"main": "index.js", "main": "index.js",
"types": "index.d.ts", "types": "index.d.ts",
"browser": "default.js", "browser": "default.js",

View File

@ -81,24 +81,27 @@ model Shop {
} }
model Item { model Item {
item_name String @id item_name String
stock Int
shopId Int shopId Int
stock Int
shop Shop @relation(fields: [shopId], references: [id], onDelete: Cascade, onUpdate: Cascade) shop Shop @relation(fields: [shopId], references: [id], onDelete: Cascade, onUpdate: Cascade)
sellables Sellable[] sellables Sellable[]
@@id([item_name, shopId])
} }
model Sellable { model Sellable {
id String @id @default(cuid()) id String @id @default(cuid())
item_name String item_name String
shopId Int
amount Int amount Int
price Float price Float
shopId Int
enabled Boolean @default(true) enabled Boolean @default(true)
shop Shop @relation(fields: [shopId], references: [id], onDelete: Cascade, onUpdate: Cascade) shop Shop @relation(fields: [shopId], references: [id], onDelete: Cascade)
item Item @relation(fields: [item_name], references: [item_name], onDelete: Cascade, onUpdate: Cascade) item Item @relation(fields: [item_name, shopId], references: [item_name, shopId], onDelete: Cascade)
cartItems CartItem[] cartItems CartItem[]
} }

File diff suppressed because one or more lines are too long

View File

@ -81,24 +81,27 @@ model Shop {
} }
model Item { model Item {
item_name String @id item_name String
stock Int
shopId Int shopId Int
stock Int
shop Shop @relation(fields: [shopId], references: [id], onDelete: Cascade, onUpdate: Cascade) shop Shop @relation(fields: [shopId], references: [id], onDelete: Cascade, onUpdate: Cascade)
sellables Sellable[] sellables Sellable[]
@@id([item_name, shopId])
} }
model Sellable { model Sellable {
id String @id @default(cuid()) id String @id @default(cuid())
item_name String item_name String
shopId Int
amount Int amount Int
price Float price Float
shopId Int
enabled Boolean @default(true) enabled Boolean @default(true)
shop Shop @relation(fields: [shopId], references: [id], onDelete: Cascade, onUpdate: Cascade) shop Shop @relation(fields: [shopId], references: [id], onDelete: Cascade)
item Item @relation(fields: [item_name], references: [item_name], onDelete: Cascade, onUpdate: Cascade) item Item @relation(fields: [item_name, shopId], references: [item_name, shopId], onDelete: Cascade)
cartItems CartItem[] cartItems CartItem[]
} }

View File

@ -0,0 +1,97 @@
import { NextResponse } from "next/server";
import { auth } from "~/server/auth";
import { db } from "~/server/db";
type Order = {
cart: { id: string; quantity: number }[];
address: string;
};
export async function POST(request: Request) {
const { address, cart } = (await request.json()) as Order;
const session = await auth();
if (!session) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
const userId = session.user.id;
try {
if (!cart || cart.length === 0) {
return NextResponse.json({ error: "Cart is empty" }, { status: 400 });
}
// Fetch all cart items with related sellable and item to get shopId
const cartItemsWithShop = await db.cartItem.findMany({
where: { itemId: { in: cart.map((c) => c.id) } },
include: {
sellable: {
include: {
item: true, // include item to get shopId
},
},
},
});
if (cartItemsWithShop.length === 0) {
return NextResponse.json(
{ error: "No valid items in cart" },
{ status: 400 },
);
}
// Group items by shopId
const itemsByShop: Record<number, { id: string; quantity: number }[]> = {};
for (const ci of cartItemsWithShop) {
const shopId = ci.sellable.item.shopId; // get shopId from item
itemsByShop[shopId] ??= []; // initialize if undefined or null
itemsByShop[shopId].push({
id: ci.sellable.item.item_name, // API expects item_name as id
quantity: ci.quantity,
});
}
console.log(itemsByShop);
// Send requests per shop
for (const [shopId, items] of Object.entries(itemsByShop)) {
const body = JSON.stringify({
shopId: Number(shopId),
address,
items,
});
console.log("Sending to shop:", shopId, body);
const response = await fetch(process.env.ITEM_SEND_API!, {
method: "POST",
headers: { "Content-Type": "application/json" },
body,
});
if (!response.ok) {
const errorText = await response.text();
return NextResponse.json(
{ error: `Failed to send items to shop ${shopId}: ${errorText}` },
{ status: response.status },
);
}
// Delete successfully sent items from cart
const itemIds = items.map((i) => i.id);
await db.cartItem.deleteMany({
where: {
cartId: userId,
sellable: { item_name: { in: itemIds } },
},
});
}
return NextResponse.json({ success: true });
} catch (error) {
console.error(error);
return NextResponse.json({ error: "Server error" }, { status: 500 });
}
}

View File

@ -19,6 +19,7 @@ type UserResponse = {
}[]; }[];
}[]; }[];
balance: number; balance: number;
adresses: string[];
}; };
type SellableResponse = { type SellableResponse = {
@ -62,11 +63,6 @@ export default function HomeClient({ session }: Props) {
} }
}, []); }, []);
useEffect(() => {
if (!session) return;
void loadUser();
}, [loadUser, session]);
const loadSellable = useCallback(async () => { const loadSellable = useCallback(async () => {
try { try {
setLoading(true); setLoading(true);
@ -82,8 +78,27 @@ export default function HomeClient({ session }: Props) {
}, []); }, []);
useEffect(() => { useEffect(() => {
if (!session) return;
// Load immediately
void loadUser();
void loadSellable(); void loadSellable();
}, [loadSellable]);
// Set intervals to reload every 30s (30000ms)
const userInterval = setInterval(() => {
void loadUser();
}, 30000);
const sellableInterval = setInterval(() => {
void loadSellable();
}, 30000);
// Clear intervals on unmount
return () => {
clearInterval(userInterval);
clearInterval(sellableInterval);
};
}, [session, loadUser, loadSellable]);
return ( return (
<main className="flex min-h-screen flex-col items-center justify-center bg-linear-to-b from-[#2e026d] to-[#7f3b3b] text-white"> <main className="flex min-h-screen flex-col items-center justify-center bg-linear-to-b from-[#2e026d] to-[#7f3b3b] text-white">

View File

@ -1,17 +1,22 @@
"use client"; "use client";
import type { CartItem } from "generated/prisma";
import { IMAGES_MANIFEST } from "next/dist/shared/lib/constants"; import { IMAGES_MANIFEST } from "next/dist/shared/lib/constants";
import { useEffect, useState, useCallback } from "react"; import { useEffect, useState, useCallback } from "react";
type Cart = {
cartItems: {
itemId: string;
quantity: number;
}[];
};
type UserResponse = { type UserResponse = {
id: string; id: string;
name: string | null; name: string | null;
carts: { carts: Cart[];
cartItems: { adresses: string[];
itemId: string; balance: number;
quantity: number;
}[];
}[];
}; };
type CartViewItem = { type CartViewItem = {
@ -20,6 +25,7 @@ type CartViewItem = {
actualQuantity: number; actualQuantity: number;
itemAmount: number; itemAmount: number;
stock: number; stock: number;
price: number;
}; };
type ApiItem = { type ApiItem = {
@ -64,6 +70,16 @@ type DraggingState = {
rect: DOMRect; rect: DOMRect;
} | null; } | null;
async function buyItems(cart: CartViewItem[], address: string) {
await fetch("/api/buy", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ cart, address }),
});
}
export default function CartButton({ export default function CartButton({
userData, userData,
reloadUser, reloadUser,
@ -72,7 +88,9 @@ export default function CartButton({
const [cartItems, setCartItems] = useState<CartViewItem[]>([]); const [cartItems, setCartItems] = useState<CartViewItem[]>([]);
const [totalQuantity, setTotalQuantity] = useState(0); const [totalQuantity, setTotalQuantity] = useState(0);
const [isOpen, setIsOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
const [selectedAddress, setSelectedAddress] = useState<string>(
userData?.adresses?.[0] ?? "",
);
const [dragging, setDragging] = useState<DraggingState>(null); const [dragging, setDragging] = useState<DraggingState>(null);
const [sliderActive, setSliderActive] = useState<string | null>(null); const [sliderActive, setSliderActive] = useState<string | null>(null);
const [holdTimeout, setHoldTimeout] = useState<NodeJS.Timeout | null>(null); const [holdTimeout, setHoldTimeout] = useState<NodeJS.Timeout | null>(null);
@ -100,10 +118,13 @@ export default function CartButton({
actualQuantity, actualQuantity,
itemAmount: item.amount, itemAmount: item.amount,
stock: item.item.stock, stock: item.item.stock,
price: item.price,
}); });
} }
} }
if (!selectedAddress) {
setSelectedAddress(userData.adresses?.[0] ?? "");
}
setCartItems(collected); setCartItems(collected);
setTotalQuantity(total); setTotalQuantity(total);
}; };
@ -111,6 +132,21 @@ export default function CartButton({
void fetchItems(); void fetchItems();
}, [userData]); }, [userData]);
const getItemSubtotal = (item: CartViewItem) => {
return (item.price * item.actualQuantity) / item.itemAmount;
};
const cartTotalPrice = cartItems.reduce(
(sum, item) => sum + getItemSubtotal(item),
0,
);
const formatPrice = (value: number) =>
value.toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
});
// Remove item function (negative quantity) // Remove item function (negative quantity)
const removeItem = async (id: string, quantity: number) => { const removeItem = async (id: string, quantity: number) => {
try { try {
@ -185,9 +221,21 @@ export default function CartButton({
}; };
}, [dragging, handleMouseMove, handleMouseRelease]); }, [dragging, handleMouseMove, handleMouseRelease]);
const buyDisabled = cartItems.some( const buyDisabled =
(item) => item.stock < item.actualQuantity, cartItems.length === 0 ||
); cartItems.some((item) => item.stock < item.actualQuantity) ||
!userData?.balance ||
cartTotalPrice > userData.balance;
const buyDisabledReason = buyDisabled
? cartItems.some((item) => item.stock === 0)
? "Out of stock"
: cartItems.some((item) => item.stock < item.actualQuantity)
? "Not enough stock"
: !userData?.balance || cartTotalPrice > userData.balance
? "Insufficient balance"
: "No items in cart"
: null;
return ( return (
<> <>
@ -239,7 +287,11 @@ export default function CartButton({
)} )}
</p> </p>
<p className="text-sm text-neutral-400"> <p className="text-sm text-neutral-400">
Quantity: {item.actualQuantity} / Stock: {item.stock} Qty: {item.actualQuantity} · Price: $
{formatPrice(item.price)}
</p>
<p className="text-sm font-semibold text-green-400">
Subtotal: ${formatPrice(getItemSubtotal(item))}
</p> </p>
</div> </div>
</div> </div>
@ -278,13 +330,57 @@ export default function CartButton({
</ul> </ul>
)} )}
<div className="mt-4 flex items-center justify-between rounded-lg bg-white/10 px-4 py-3">
<span className="text-lg font-semibold">Total</span>
<span className="text-lg font-bold text-green-400">
${formatPrice(cartTotalPrice)}
</span>
</div>
{/* ADDRESS SELECT */}
{userData?.adresses && userData.adresses.length > 0 && (
<div className="mt-4">
<label
htmlFor="address"
className="mb-2 block text-sm font-medium text-neutral-300"
>
Select Delivery Address
</label>
<select
id="address"
className="w-full rounded-lg border border-neutral-700 bg-neutral-800 p-2 text-white focus:border-green-500 focus:ring-1 focus:ring-green-500"
value={selectedAddress}
onChange={(e) => setSelectedAddress(e.target.value)}
>
{userData.adresses.map((addr, idx) => (
<option key={idx} value={addr}>
{addr}
</option>
))}
</select>
</div>
)}
<button <button
className={`mt-6 w-full rounded-lg py-3 font-bold text-black ${buyDisabled ? "cursor-not-allowed bg-gray-400" : "bg-green-500 hover:bg-green-600"}`} className={`mt-6 w-full rounded-lg py-3 font-bold text-black ${buyDisabled ? "cursor-not-allowed bg-gray-400" : "bg-green-500 hover:bg-green-600"}`}
onClick={() => alert("Dummy buy action")} onClick={() => {
if (
!selectedAddress ||
selectedAddress === "" ||
cartItems.length === 0
)
return;
void buyItems(cartItems, selectedAddress);
}}
disabled={buyDisabled} disabled={buyDisabled}
> >
Buy Buy
</button> </button>
{buyDisabled && buyDisabledReason && (
<p className="mt-3 text-center text-sm font-medium text-red-400">
{buyDisabledReason}
</p>
)}
</div> </div>
</div> </div>
)} )}

View File

@ -33,6 +33,20 @@ type Props = {
reloadUser: () => Promise<void>; reloadUser: () => Promise<void>;
}; };
const formatName = (name: string) => {
const parts = name.split(":");
if (!parts[1]) return "";
return parts[1]
.split("_")
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
.join(" ");
};
const getModId = (name: string) => {
const [modId] = name.split(":");
return modId ?? "";
};
export default function SellableItemsButton({ export default function SellableItemsButton({
loading, loading,
reloadSellable, reloadSellable,
@ -43,6 +57,7 @@ export default function SellableItemsButton({
const [items, setItems] = useState<ItemFromApi[]>([]); const [items, setItems] = useState<ItemFromApi[]>([]);
const [sellables, setSellables] = useState<SellableFromApi[]>([]); const [sellables, setSellables] = useState<SellableFromApi[]>([]);
const [userShops, setUserShops] = useState<Shop[]>([]); const [userShops, setUserShops] = useState<Shop[]>([]);
const [search, setSearch] = useState("");
const [selectedIndex, setSelectedIndex] = useState<number | null>(null); const [selectedIndex, setSelectedIndex] = useState<number | null>(null);
const [price, setPrice] = useState<number | "">(""); const [price, setPrice] = useState<number | "">("");
@ -201,6 +216,14 @@ export default function SellableItemsButton({
!sellables.some( !sellables.some(
(s) => s.shopId === item.shop.id && s.item_name === item.item_name, (s) => s.shopId === item.shop.id && s.item_name === item.item_name,
), ),
)
.filter(
(item) =>
formatName(item.item_name)
.toLowerCase()
.includes(search.toLowerCase()) ||
getModId(item.item_name).toLowerCase().includes(search.toLowerCase()) ||
item.item_name.toLowerCase().includes(search.toLowerCase()),
); );
// Sellables grouped by user's shops // Sellables grouped by user's shops
@ -353,6 +376,17 @@ export default function SellableItemsButton({
<section> <section>
<h3 className="mb-3 text-lg font-semibold">Add Item to Store</h3> <h3 className="mb-3 text-lg font-semibold">Add Item to Store</h3>
<input
type="text"
value={search}
onChange={(e) => {
setSearch(e.target.value);
setSelectedIndex(null); // reset selection when filtering
}}
placeholder="Search item by name..."
className="mb-3 w-full rounded bg-white/10 px-3 py-2 text-sm placeholder:text-neutral-400"
/>
<ul className="mb-4 max-h-56 space-y-2 overflow-y-auto"> <ul className="mb-4 max-h-56 space-y-2 overflow-y-auto">
{availableItems.map((item, index) => ( {availableItems.map((item, index) => (
<li <li
@ -365,7 +399,7 @@ export default function SellableItemsButton({
}`} }`}
> >
<div className="flex justify-between text-sm"> <div className="flex justify-between text-sm">
<span>{item.item_name}</span> <span>{formatName(item.item_name)}</span>
<span className="text-neutral-400"> <span className="text-neutral-400">
{item.shop.label} {item.shop.label}
</span> </span>