From 33492bb2390bd9aaecdf11d20480ddb8f9d4aa3b Mon Sep 17 00:00:00 2001 From: Zikil Date: Mon, 3 Mar 2025 20:34:59 +0700 Subject: [PATCH] =?UTF-8?q?=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=B0=20=D0=BA=D0=BE=D1=80=D0=B7=D0=B8=D0=BD=D0=B0,=20?= =?UTF-8?q?=D0=B7=D0=B0=D0=BA=D0=B0=D0=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../__pycache__/order_repo.cpython-310.pyc | Bin 10154 -> 10469 bytes .../__pycache__/user_repo.cpython-310.pyc | Bin 6370 -> 6442 bytes backend/app/repositories/order_repo.py | 179 ++-- backend/app/repositories/user_repo.py | 4 +- .../__pycache__/cart_router.cpython-310.pyc | Bin 1879 -> 1919 bytes backend/app/routers/cart_router.py | 13 +- .../__pycache__/order_schemas.cpython-310.pyc | Bin 4975 -> 4924 bytes .../__pycache__/user_schemas.cpython-310.pyc | Bin 4304 -> 4311 bytes backend/app/schemas/order_schemas.py | 2 - backend/app/schemas/user_schemas.py | 4 +- .../__pycache__/order_service.cpython-310.pyc | Bin 3976 -> 4278 bytes .../__pycache__/user_service.cpython-310.pyc | Bin 5858 -> 5705 bytes backend/app/services/order_service.py | 33 +- backend/app/services/user_service.py | 92 +- .../0beaff9a-bec5-4624-bc08-08fa492ce05c.jpg | Bin 0 -> 22263 bytes .../d62483ea-03fb-4e8b-919e-e1fbf2418ad2.jpg | Bin 0 -> 26699 bytes frontend/components/AddToCartButton.tsx | 92 ++ frontend/components/Footer.tsx | 66 +- frontend/components/Header.tsx | 157 +++- .../admin/ProductVariantManager.tsx | 17 +- frontend/pages/account/orders.tsx | 88 +- frontend/pages/account/orders/[id].tsx | 39 +- frontend/pages/admin/orders/[id].tsx | 197 ++++- frontend/pages/admin/orders/index.tsx | 12 +- frontend/pages/admin/products/[id].tsx | 17 +- frontend/pages/admin/products/index.tsx | 14 +- frontend/pages/all-products.tsx | 12 +- frontend/pages/cart.tsx | 301 +++++++ frontend/pages/checkout.tsx | 822 ++++++++++++++++++ frontend/pages/order-success.tsx | 142 +++ frontend/pages/order-tracking.tsx | 291 +++++++ frontend/pages/product/[slug].tsx | 432 +++++++-- frontend/services/cart.ts | 68 ++ frontend/services/orders.ts | 281 +++++- frontend/services/users.ts | 32 +- 35 files changed, 3032 insertions(+), 375 deletions(-) create mode 100644 backend/uploads/products/9/0beaff9a-bec5-4624-bc08-08fa492ce05c.jpg create mode 100644 backend/uploads/products/9/d62483ea-03fb-4e8b-919e-e1fbf2418ad2.jpg create mode 100644 frontend/components/AddToCartButton.tsx create mode 100644 frontend/pages/cart.tsx create mode 100644 frontend/pages/checkout.tsx create mode 100644 frontend/pages/order-success.tsx create mode 100644 frontend/pages/order-tracking.tsx create mode 100644 frontend/services/cart.ts diff --git a/backend/app/repositories/__pycache__/order_repo.cpython-310.pyc b/backend/app/repositories/__pycache__/order_repo.cpython-310.pyc index 81df5aa00e2c57b6d247ec239e7e0d01eb4bee2f..00f147da0f96d1c09e271b71410db564effac4c7 100644 GIT binary patch delta 2590 zcmah~U1(fI6uxu+_BY%7Y_e&)+1w^+YBW_!snz&{#eZ8AwMCm+(zSPu*(RGfd#}-Q z*G2sgc>nX^-}atO>~AW^F(lpHXvJSuMR63HdFngLyn2E$8mS(u zk35!V{7z4)sE)S4D!QZP4bmOMIB$CDF?B($gcg*N=4r*<@YQJc9mUpVL|sMgkfD`jnC!TL0H?#!W20Ls=X3dh0Wg9(x0ie)pT2LgosHf+Ry7#bB zA~?d83k~xdrC5>b_>g-=vz=BJ4(@2W)v~)MeUYZ{a+|WX|_(t zQ_7VsUW6Dw_v7()EZarN!?(M()8LV;?Xca7Zw4D8i^$WTP##xw#p#)2FTTK(Qj96b zH?G1~>^5hs7bEs7WlY=AyH;D<4!aE3mtvD_Qt3-Q?C!-O@aO%-;AMZ&zvwSp!L+~Z zf9!vV;bs3qaN5FP(fxIQ33oHads8}J4o><@>)Zz{ zH?4HTV9x(AnDUo`X$rU;Ov8T>aX$4gSnf_l5I-lAxBlN!chOSs)}a9{E%=G$FN$xI zH-z|hOuN^MuaaHqMgJ45v*ceMusDOd&*Z)Ma05cT*495*jXUE`c62B^HtrT2w-b7Q z;aVQnkU)N{2k!xq2g&AbWNS4Q!3ivJ$--wPTLeiQYVCOj=6n9j-2W_?3Qqf%WU_); z>;Zxh)?g+$xn^O15mCA*${Wb{lz)M89L!iqU2O}d#4oMZZG8sc2O~~d#WeDOMVcl;b`q|)B&_9iBK%Ot*PifgHc7#*n z>$Z;6fYrB6k}Wv=N%-&#iKN&Q>lKw$vV4$+gCqtZs>U;;Zt>Y_^k|klLnBUZtjJ@e z*G39Md>fh3Bf*`bDST+#9)K~4%t~J%WtD`RL)7* zydMe)q{?#Qf%dNQM^Gfx1T)nri?T3Nuj-n5MK{`*%5*gIYC=~v>ev3z{1J_WS);0J zrq&3b_==Z)#WXNh)2dDy*vH|mnsrZ|nSVw@?JTTqVy2d4VOW}XtT9cRs$`N56-szLkbiXb^APH+CE^I43lVf*QojND6vb0qE~ zalgoBk`3+zvXiS>^oUCOFdGwB)337;G1sxcj)_x=cJWZA$)s3(6fPD!C}uLdSV3IK zbhP6h#_R7X9y_6yR6MPkr{V!|#E+SidyRaIAMhY?`y2TNX^(M$bFJ#AZD2=h~aBj%uqkWNDJ<0p+2w9jYCU?}S z>Qp|ma`^3}H>6Lkdb`9&Mnde^k}+N6ptw&Q*m6@!U)i;58%>21KR>_a6^4}db`P_s z#B1F*#;Epdr|qElqPv$3il4f3>`Ae|=dPU&&B+_-+F6n4U7C=d@=Q+AaGJ!R_@bwO zliXT)_$En71utRq=fzp0MOa%?>^*VI)-$XszTbKrOFrGZ7xGN+Xv;9!j*%#kz?oCT W_HEnQL*mf3d&AabdRJGvYscS#sYD9^ delta 2229 zcmbtVTWl0n7@jk?oz7m^Zg1PN+isU$C>ODkMk78z1R{tSM1n0WW$7$sr`>HiGb@42 zW_yXo1ksw~gJ=j_A51hxieNM-7@rL|`#L`83lHj};X#u&{{PwSMKNmPZvL-l|I2s& z|3CjZy?^MV=R*_0pf14Yha2CFwjH`2da=F~f}a}5(;(K6BnS>M1={WiWnzo-q%7H@ zDWh&ogq7lmW8%D6R_BGHD>MQ{S^DG&Y=rf#jogr6A9d)-bM^bvx#|gunFbqlg%z8+M@MJVc zv$C~h6tui280r2{cw7YHG_zqPu2kpLS+CRreGdiJnT_9n~J)lLwNJ! zLQ%I{&>JM`=!SMlD*7F{-0DERL~s=F152X-p32$%P+A+kkGNY%eNYUn7E-dYlE7ao zxw}#cSk_hon}P-;3exm9+kzIYwT-XY>m9)nXT_rGsMfEwzSgbwHB~zKZ*83;R^v)K z|Glx$SBUKYr_Rn1`b6n}H}`=Zb3{9CwnNilOhGz85{7DfVhpE4v)qAE#SEM+rb-2CY}z z&)i$?oyrWz6x-F@#qQ}5v2v09s3*t*BmVVbp@;4AZD9AB z67Eg+%gUPx_fvNP^k29O?rmUONK~d1`*tV#B)SQA><93aNtzwW6k3twUEI+990G2G zv-@>rwla-zeJbt200jdA_Jo}tA5P~c3VGXWV7Hq#iG_aFueGq|Xk6~=qsQ3J=%eg% zbo~iW8qehE0LDfT__kqOZAYq*k+nvUW|${I2T>X3>*2Iv7A!lL$=fuD0X$hXJvo`l z8x&tG+Q)Lyme_GzrVyS4@RZZz1?vnwfQo%=E*?D@qF5U}ihxykzO-S`jAae^C#aF3 z!&y6%v*-}oPateT*n#jA0wy)2uey3N26&orYM3k8Xlt_cGdQWDPIRIXaY>fMMOhY! zOhlqC$*N3LNhg67TSQs5=}JsQFBz=rQcPK0M^v4Nl3e9j)RnM=9BaKr;Ki@HsF#$8 zq{EKOv>MQ=YiL*3jSta^QZ=eZ_Pbc8iyF}+jf4Q(hz4b;xaNFVkhnx~iuZb2)`D}& zW%D-+hmOW z)ILvg?DbHT4F&`3T8E-mL)mquf!*y0l4+fg=1na1=8Ludn7v<&Y!8RhP|mAPPtrq#h&!zA{Jyc>8DpUhKv^G2rzE4Mek8 z=Vta?=aIId=*wxEO)rn=nz3Z>ckbHZ>Cgfg*j$>Pr)jJZ$Az9k7(w9o24|PPfG`Q* z$@z3H?z3ICikJ;#$x^|X-hSfS?=GQ_%iFA$Gi?R^&Tm)`LP ioE(}*m;mr3%cjiSxD{sNosD~Rp_E9(29mMlV}Af$vp+rn diff --git a/backend/app/repositories/__pycache__/user_repo.cpython-310.pyc b/backend/app/repositories/__pycache__/user_repo.cpython-310.pyc index 362c92bcfa92e1ecce2b5183e734941dfec5aa6a..44a00c629a9fa5905da8674b6c1412ccf8ef8f07 100644 GIT binary patch delta 436 zcmaE4xXOqxpO=@50SKbz9Ze6{n8+u?_+z7XHX~CS+vYipkGUB)Om5(fV3eMGlUK%} zgg=F~hIux_T&8A5Mur-u8s>PG8s>0@U Order: # Проверяем, что адрес доставки существует и принадлежит пользователю, если указан + print(f"Начало создания заказа для пользователя {user_id}") + print(f"Данные заказа: {order}") + if order.shipping_address_id: address = db.query(UserAddress).filter( UserAddress.id == order.shipping_address_id, UserAddress.user_id == user_id ).first() + if not address: + print(f"Адрес доставки {order.shipping_address_id} не найден для пользователя {user_id}") raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, - detail="Адрес доставки не найден или не принадлежит пользователю" + detail="Указанный адрес доставки не найден" ) + print(f"Адрес доставки найден: {address}") - # Получаем элементы заказа - order_items = [] - total_amount = 0 + # Создаем новый заказ + new_order = Order( + user_id=user_id, + status=OrderStatus.PENDING, + shipping_address_id=order.shipping_address_id, + payment_method=order.payment_method, + notes=order.notes, + total_amount=0 # Будет обновлено после добавления товаров + ) - # Если указаны элементы корзины, используем их + db.add(new_order) + db.flush() # Получаем ID заказа + + print(f"Создан заказ с ID: {new_order.id}") + + # Получаем элементы корзины пользователя + cart_items = [] + + # Если указаны конкретные элементы корзины if order.cart_items: + print(f"Используем указанные элементы корзины: {order.cart_items}") cart_items = db.query(CartItem).filter( CartItem.id.in_(order.cart_items), CartItem.user_id == user_id ).all() - - if len(cart_items) != len(order.cart_items): - raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, - detail="Некоторые элементы корзины не найдены или не принадлежат пользователю" - ) - - for cart_item in cart_items: - # Получаем вариант и продукт - variant = db.query(ProductVariant).filter(ProductVariant.id == cart_item.variant_id).first() - if not variant: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, - detail=f"Вариант продукта с ID {cart_item.variant_id} не найден" - ) - - product = db.query(Product).filter(Product.id == variant.product_id).first() - if not product: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, - detail=f"Продукт для варианта с ID {cart_item.variant_id} не найден" - ) - - # Рассчитываем цену - price = product.discount_price if product.discount_price else product.price - price += variant.price_adjustment - - # Создаем элемент заказа - order_item = OrderItem( - variant_id=cart_item.variant_id, - quantity=cart_item.quantity, - price=price - ) - order_items.append(order_item) - - # Обновляем общую сумму - total_amount += price * cart_item.quantity - - # Если указаны прямые элементы заказа, используем их + # Если указаны прямые элементы заказа elif order.items: - for item in order.items: + print(f"Используем прямые элементы заказа: {order.items}") + # Создаем элементы заказа напрямую + for item_data in order.items: # Проверяем, что вариант существует - variant = db.query(ProductVariant).filter(ProductVariant.id == item.variant_id).first() + variant = db.query(ProductVariant).filter(ProductVariant.id == item_data.variant_id).first() if not variant: + db.rollback() raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, - detail=f"Вариант продукта с ID {item.variant_id} не найден" + detail=f"Вариант товара с ID {item_data.variant_id} не найден" ) # Создаем элемент заказа order_item = OrderItem( - variant_id=item.variant_id, - quantity=item.quantity, - price=item.price + order_id=new_order.id, + product_id=variant.product_id, + variant_id=variant.id, + quantity=item_data.quantity, + price=item_data.price ) - order_items.append(order_item) - - # Обновляем общую сумму - total_amount += item.price * item.quantity - + db.add(order_item) + new_order.total_amount += order_item.price * order_item.quantity + # Иначе используем все элементы корзины пользователя else: - # Если не указаны ни элементы корзины, ни прямые элементы заказа, используем всю корзину пользователя - cart_items = get_user_cart(db, user_id) - - if not cart_items: - raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, - detail="Корзина пуста" - ) - + print(f"Используем все элементы корзины пользователя") + cart_items = db.query(CartItem).filter(CartItem.user_id == user_id).all() + + # Если используем элементы корзины + if cart_items: + print(f"Найдено {len(cart_items)} элементов корзины") + # Создаем элементы заказа из элементов корзины for cart_item in cart_items: - # Получаем вариант и продукт + # Получаем вариант товара и его цену variant = db.query(ProductVariant).filter(ProductVariant.id == cart_item.variant_id).first() if not variant: + db.rollback() raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, - detail=f"Вариант продукта с ID {cart_item.variant_id} не найден" + detail=f"Вариант товара с ID {cart_item.variant_id} не найден" ) - product = db.query(Product).filter(Product.id == variant.product_id).first() - if not product: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, - detail=f"Продукт для варианта с ID {cart_item.variant_id} не найден" - ) - - # Рассчитываем цену - price = product.discount_price if product.discount_price else product.price - price += variant.price_adjustment + # Определяем цену (используем скидочную цену, если она есть) + price = variant.discount_price if variant.discount_price else variant.price # Создаем элемент заказа order_item = OrderItem( - variant_id=cart_item.variant_id, + order_id=new_order.id, + variant_id=variant.id, quantity=cart_item.quantity, price=price ) - order_items.append(order_item) + db.add(order_item) - # Обновляем общую сумму - total_amount += price * cart_item.quantity - - # Создаем заказ - db_order = Order( - user_id=user_id, - status=OrderStatus.PENDING, - total_amount=total_amount, - shipping_address_id=order.shipping_address_id, - payment_method=order.payment_method, - notes=order.notes - ) + # Обновляем общую сумму заказа + new_order.total_amount += price * cart_item.quantity + + # Удаляем элемент из корзины + db.delete(cart_item) try: - # Добавляем заказ - db.add(db_order) - db.flush() # Получаем ID заказа, не фиксируя транзакцию - - # Добавляем элементы заказа - for item in order_items: - item.order_id = db_order.id - db.add(item) - - # Очищаем корзину, если заказ создан из корзины - if not order.items: - db.query(CartItem).filter(CartItem.user_id == user_id).delete() - db.commit() - db.refresh(db_order) - return db_order - except IntegrityError: + db.refresh(new_order) + print(f"Заказ успешно создан: {new_order.id}, общая сумма: {new_order.total_amount}") + return new_order + except Exception as e: db.rollback() + print(f"Ошибка при создании заказа: {str(e)}") raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, - detail="Ошибка при создании заказа" + detail=f"Ошибка при создании заказа: {str(e)}" ) @@ -437,14 +397,14 @@ def get_cart_with_product_details(db: Session, user_id: int) -> List[Dict[str, A ).first() # Рассчитываем цену - price = product.discount_price if product.discount_price else product.price - price += variant.price_adjustment + price = variant.discount_price if variant.discount_price else variant.price # Формируем результат result.append({ "id": item.id, "user_id": item.user_id, "variant_id": item.variant_id, + "slug": product.slug, "quantity": item.quantity, "created_at": item.created_at, "updated_at": item.updated_at, @@ -453,7 +413,6 @@ def get_cart_with_product_details(db: Session, user_id: int) -> List[Dict[str, A "product_price": price, "product_image": image.image_url if image else None, "variant_name": variant.name, - "variant_price_adjustment": variant.price_adjustment, "total_price": price * item.quantity }) diff --git a/backend/app/repositories/user_repo.py b/backend/app/repositories/user_repo.py index eb8c092..8cf48f5 100644 --- a/backend/app/repositories/user_repo.py +++ b/backend/app/repositories/user_repo.py @@ -134,7 +134,7 @@ def get_user_addresses(db: Session, user_id: int) -> List[UserAddress]: return db.query(UserAddress).filter(UserAddress.user_id == user_id).all() -def create_address(db: Session, address: AddressCreate, user_id: int) -> UserAddress: +def create_address(db: Session, address: AddressCreate, user_id: int): # Если новый адрес помечен как дефолтный, сбрасываем дефолтный статус у других адресов пользователя if address.is_default: db.query(UserAddress).filter( @@ -157,6 +157,8 @@ def create_address(db: Session, address: AddressCreate, user_id: int) -> UserAdd db.add(db_address) db.commit() db.refresh(db_address) + print(f"Адрес успешно создан: {db_address}") + db_address.user_id = user_id return db_address except Exception: db.rollback() diff --git a/backend/app/routers/__pycache__/cart_router.cpython-310.pyc b/backend/app/routers/__pycache__/cart_router.cpython-310.pyc index 9536858eafbc6a3a6f88bb85fb7220e5cd7977bb..7635dc93218494387969231de41c6603abdd5b75 100644 GIT binary patch delta 273 zcmcc4_n(h9pO=@50SJWqj;0%I~43Iv#n6ee@Bw(+Y1#Xds}=3^>SnLLkGfl+bt zepY!#<;fRWgeQMu72?(cswn~~EYg_F&vsWy4#>R4mXey2T9R6%01{ULi7=<9mK14# zxRM~P{2Vs9`6;D2sdkJ&v0{(O2JE6{Y|LzoEPt68nf@~|G5up==J_ka!Nvj<`-2c; U`pd+^^GAeJjZuU}35eAg0q>DGVgLXD delta 268 zcmey*cb$(npO=@50SIoKJ($kCk#{yT=Vulmm#IO3Y4S$qCPvxGCM-7mLO`Knh`10_ zk^JO(7I&bGC5znTr!11(;y~FV84#g3nU}RK0K|lv!pBsk3grD_)K6vt(l7vGgN!>2 z#6?OVLJUY~G8b_JsaqWBsU`8riA5zv5+Es+$ro9KCx2rV;#LH4)j)*iWMQ_uvhpC+ zY$>TZsU@jJU`dc=x0r$Yfhu^})ihLqdKKzy~|Gn)lf8Ppacq}wX~EL5gj^IlE$Qo6Cr8pqQzWjqKli8 zi9^q*gRZ)0T$?x;SEH+r?z);zp6@_{i;(lZbI(2ZJKwqY{4Sgq;=_1665`*FSD)|g zzmLDM>B94A$MWclU38A-?}SQWns*N9#5s({I584(m8&#;b6%(lUG=k>RgRX}M(%Vw zreeyhQo>5pyKpP0aTsP60X2^2DC53WTlC4@TdZ?%ooxge6LME2=S*GlZg_oFrq^C^ zVU4$UkmpSE(#ot!&V~1U`5Fg}1xNx?fDHgxO#@ICu_)DLn~K#+D<)bOqCb&gehaBW zWW$@WHL8DGXnu&m57mL4#PKhq|S}1URYz2|x=VB|LbdRSrq9 znQC-v%79%!1GRJ8?A~S@`3#W|^iTfW#kOcD#$T3D;(Kg791rgI2M>m)C*z(GI$>Jk zU6Thi`9%86-&F%@`_*a%G078ov)Xvs=?QppHFf&s=Il1F59!>`t2U~uCsebRC>!*h zBZp_lr>DVP`Wcj?LM8=P&`6}i2anlCm#C#LTRbrxi23kPbNMb~+^XC~Ll?mrDQ z=qyoDP5PKv-WST*^Z)<)Kr!p%CYHAVm?skkG^v)%*_e(~I!G2(mmVh{RWaU1<`<)C lFoy?_qEWrUU45Zy9Q&3Mo%J+R{Kr+x*q(;4h-IjM@MI delta 937 zcmZ`%O=}ZT6wOOBlgT7AnS9tZY8%0lSWRNu)TT`vYpE7R2ox8k3klJLQn87=r0T|n z8#f9ru7aSTYd;X_LJ&9pfv#Nn8w3}2=f-<(194;Ep4>O@-g7_R`I`Neb-IpY`}p^C z@7vb1PtIFItBWcHFC{2WzipFJw0}}skfV*e&debzS(XxTeo(5 z{hfBFPgl(i$7LehXDLg0%AybEOI4tU=2)u8-%qpktU*9TMU*a5ERdw@R*0SlXQCy( zg6TA%#CPNLJNQm5&}-{*dYV7Zvh~pHVF^pc{d!vB#zPI2rw5@Ny|Dwa8sf}BTvF+> z#C$ef`VyX@QM*2QjxTip5Cpb8*pn}@QBD;0kkEGP1R2*?$O@Z)7q~Gl~Z#SAd-Q7;Vt|eLWCXk!~R5=6Bk#{ny0DoFher|kXNl8&=QfWzQ@nlDKkE7@l)xW_MP~gy>BcSkYcSLR`C@8BaG&i+oY@#C7?_V$*RJ%kgh7vv}Hn+x$Zz+(?TVr zA*rZk4LEwJ-O`~?u^%Kt!V*}4m1N1Mlo=1zvK|;~CaClZ1&+FAK@6OQ3N0s?876Y% znlh?rN|3rcN|bUE23cTj1$LNRhAq_1mlOuQ`Q$8_RXS56oWO=(lUTpPna@jSAd?lj zEkDS2a$SCwRk`UwvMxWug0~0tt>}>_zW`|~dLTdaLGV_tM@w?E)|sPOuXG%Jp8S(} z(QF{Py@?L;g zpW(UbfG07wC%8Wz_+te;S4?w^JIFhMfPaj)A**T950D~aE8Q5I7w=5?GeNE}9jnEF zcN${eZ1ZDt+Mlia@C=Han&%kvgYjIx7!#kjqhSp?Fv-Bm59H9~GI(p{bvJLXTFp9a48Wt}wfF7b1ib){OGZ$MCV0c_d;9vY|;yN1pO z#aQY)xgdJ%UNRu2?98E>?kq54r!>Rh{H(rl@y0x&4?%rOe6m}~koaLgCjFw|Tm|_z zCvyOMJdL0tXb1!1x6{&$ZrAPlk?WrP5_T_(Zr3BB7;z7iVUc%V=p1j4Xh^p-;RlGd Xn}tH24R*(SS~U3@ zrO#CbRKP zW3-(-moF^C4kTp?Bv3+y2P6s#8nCV+eh`ZpM6iGekSC#`Q3P@)MBW0#0y!p%2jVA) zlZsqH;x?0``4t$8C)e=HGP+Nm#Xq0*7ISe)(PTRTDKSvE-C{|~&(A3W#WTc8_sMwz L&a6T@d}@LK;oNix diff --git a/backend/app/services/__pycache__/user_service.cpython-310.pyc b/backend/app/services/__pycache__/user_service.cpython-310.pyc index b136a1e6e4638f6b7017a3f2628f75d90cc26b5e..93eaf2712af17c064bdc40db493bfb09c54f2f7d 100644 GIT binary patch delta 1439 zcmaJ>O>7%Q6y90S?2qkTukAW%NJU*2m2T)CO&e$nf~rsz0ud#utA?5+WK4*aA9w7i z9M%eL4{!jf(i{-9sP>IYMJl0)1B&T|1Gn|n2PBZVaV5UlC>aivbw1Bkq z1`TL!u+Hj6#pE$gKhdv}fL1KP2I4>hNCFPv0x7_o*EqWwZ!p+tQB?erRw^`%z^GVj zpbU@&wgEX{02lK6<7;oQuFNz zK@f(oiyU5E^Cah@T&t~)U=-d8+u`?}kHhcqUJvhfuF4D6`}B4~9F@oH;;!TH5hoNq zrzivRcZi+JR)>irawKu+pg5}Rf}$}+&nS9U(IG|06!o1XTlQ1Mt}$D!mY2E6W7W4s zS~-m>+70SDy<1Msy;okEjXpeOuhz<9nlH@NyXNd>5Zz?Yh^EC8@)slfjyGSfPe;C0 zvGVT1V);d!SUU~!NK&UnCkn=&)TFTbAI$z?xN$<^_3&dn@SM1A5iu@5O@2;ZlZTxb z#+KFD7AQ$vJz9Mo1Mv!IE(ZGNXTnGVQCTg@`^f>h;h1Z6%3}e&p?#rWL;g?-swfP= z1RhyPoFe}$h&8<^-v(!a5#`(1%=c{~j^KN%dqLs#@LuPG@Y~LneCJx{O85iJkHU?x zJ)Fm=jqKkG?}i)4^Wu4UN0;~0@^@#a-DOp=uv|wuwCsIjTS?5r&o}$BrUDjcKvRk3 z^5yAXTEwfEk)!U@`!B<&sS;5-xrEY(^`*pk5O<;PS-Ilwa3fK%AyRBce&*&cvI-3t zBw~<=K_Ui;7acLWhpgJ)mP-M z)PVdsl}&xAHdByGpv}2;uSrNzw$i6?+uzb>$XD_}X3)3*2XS7GXa2(Ccy`S=uO_Bs zn0<2iKFpd2O2)RT-!&{)eYevzYNUS>pzH^{p?-?p=f1DMv GBi`RHkUOjZ delta 1623 zcmZWpOKclO7~YwEdc9shHZcj5)CyI~Mr}wG8YoQ@PzzL05@^(xx+;tDZe2%Coz6N` zMQbb-L@5YWXb=)8NX`+73$3^zBqW5yk*iODIQK%~gec#vH%A`4lo1^14ZD0 zn&fMnW?PSFcO=rcNZYs}T_nEl8}AyCa$I_cv{N?-EUDO{#TGL%qEw_trf+U4E0UXD zVg&8Dlnvvjr$6KSEC@rF^t4mKoAu!8#w6_gknusMDgw7Z6haPgfbck|CPmVPu{L>M z>Z0{k0#HB(6hH+uKnDyU1(-nEmp0Q|X(BV-ZK^p zM}%`8BsBx))5_!V=gqY&%QxAxV7mbVcG%v)#8i}gk@HQ#7fD~)gg!uIL^%|%6~crGIl z;<*?*bJ^}St^{K*A*N?|o?oxFJP(FvVJK@I7Ed1ySp7Zxw|;g4aZMl;s~C}7g%u#M zqoAJFWPZTHp5D(Ax}$8baEcwX$3f)>E9>i1VLX#?svqSgDm#P#el&HkQ!WhpPX-!< zOgskV?7-2#iCMy$8(5Qj`^y}Mk3AviNkNANO$&NT&|r7*DDVf!3+pVX!yXahpe^>I zP)~x&sdy=3W^A9(bA$SlF3NUNLZWYv`+1S#1wrwjV5j0_!B4{#3d#{{kyunAln7}5 zSehkUvZ5%^cYopg%d|{FE$v<*LuHC+*#2hA{FeDSd6`e97azKYjDHv)i`dkp24Sy2 zVb6j17v_wNM}_}v7K>jh$@56!HN^q0%FkGPAMKAv+Zj*nWTK&R`f_m9OGdzdv1ZcO zMfOvIPVz8q^GY0H+q);VAr(x{HuH>yFdGh2vI;b!5q%rsb#x_ zl(~^VM&|g*{E4y8M1T##xQRh+nW|y|>;g>eJind4kMP@tPWrqsye4Rt|Ef8B*~yZx zxaW+L%AISDNu09qd7vfNVB($5UgDn)xqDuR=&HNg6K{Zirb3+Q?of_=%kK}35}%I_ rf9Ea>f4s8OLOLtxoS?X-ijaIk6Y*)M=w=R&rzTS4sWEljnauqQPhDp` diff --git a/backend/app/services/order_service.py b/backend/app/services/order_service.py index 68a0d79..4d310d3 100644 --- a/backend/app/services/order_service.py +++ b/backend/app/services/order_service.py @@ -66,19 +66,28 @@ def get_cart(db: Session, user_id: int) -> Dict[str, Any]: def create_order(db: Session, user_id: int, order: OrderCreate) -> Dict[str, Any]: from app.schemas.order_schemas import Order as OrderSchema - new_order = order_repo.create_order(db, order, user_id) + print(f"Создание заказа для пользователя {user_id}: {order}") - # Логируем событие создания заказа - log_data = AnalyticsLogCreate( - user_id=user_id, - event_type="order_created", - additional_data={"order_id": new_order.id, "total_amount": new_order.total_amount} - ) - content_repo.log_analytics_event(db, log_data) - - # Преобразуем объект SQLAlchemy в схему Pydantic - order_schema = OrderSchema.model_validate(new_order) - return {"order": order_schema} + try: + new_order = order_repo.create_order(db, order, user_id) + + print(f"Заказ успешно создан: {new_order.id}") + + # Логируем событие создания заказа + log_data = AnalyticsLogCreate( + user_id=user_id, + event_type="order_created", + additional_data={"order_id": new_order.id, "total_amount": new_order.total_amount} + ) + content_repo.log_analytics_event(db, log_data) + + # Получаем заказ с деталями + order_details = order_repo.get_order_with_details(db, new_order.id) + + return {"order": order_details} + except Exception as e: + print(f"Ошибка при создании заказа: {str(e)}") + raise def get_order(db: Session, user_id: int, order_id: int, is_admin: bool = False) -> Dict[str, Any]: diff --git a/backend/app/services/user_service.py b/backend/app/services/user_service.py index 10cf894..f1fc03f 100644 --- a/backend/app/services/user_service.py +++ b/backend/app/services/user_service.py @@ -63,7 +63,6 @@ def login_user(db: Session, email: str, password: str) -> Dict[str, Any]: def get_user_profile(db: Session, user_id: int) -> Dict[str, Any]: from app.schemas.user_schemas import User as UserSchema, Address as AddressSchema - from app.schemas.review_schemas import Review as ReviewSchema user = user_repo.get_user(db, user_id) if not user: @@ -72,16 +71,7 @@ def get_user_profile(db: Session, user_id: int) -> Dict[str, Any]: detail="Пользователь не найден" ) - # Получаем адреса пользователя - addresses = user_repo.get_user_addresses(db, user_id) - - # Получаем заказы пользователя - orders = order_repo.get_user_orders(db, user_id) - - # Получаем отзывы пользователя - reviews = review_repo.get_user_reviews(db, user_id) - - # Преобразуем объекты SQLAlchemy в схемы Pydantic + # Преобразуем объект SQLAlchemy в словарь, а затем в схему Pydantic user_dict = { "id": user.id, "email": user.email, @@ -94,28 +84,27 @@ def get_user_profile(db: Session, user_id: int) -> Dict[str, Any]: "updated_at": user.updated_at, "addresses": [] } - user_schema = UserSchema.model_validate(user_dict) - addresses_schema = [AddressSchema.model_validate({ - "id": address.id, - "user_id": address.user_id, - "address_line1": address.address_line1, - "address_line2": address.address_line2, - "city": address.city, - "state": address.state, - "postal_code": address.postal_code, - "country": address.country, - "is_default": address.is_default, - "created_at": address.created_at, - "updated_at": address.updated_at - }) for address in addresses] - reviews_schema = [ReviewSchema.model_validate(review.__dict__) for review in reviews] - return { - "user": user_schema, - "addresses": addresses_schema, - "orders": orders, # Заказы обрабатываются отдельно в сервисе заказов - "reviews": reviews_schema - } + # Преобразуем адреса пользователя + if user.addresses: + for address in user.addresses: + address_dict = { + "id": address.id, + "user_id": address.user_id, + "address_line1": address.address_line1, + "address_line2": address.address_line2, + "city": address.city, + "state": address.state, + "postal_code": address.postal_code, + "country": address.country, + "is_default": address.is_default, + "created_at": address.created_at, + "updated_at": address.updated_at + } + user_dict["addresses"].append(address_dict) + + user_schema = UserSchema.model_validate(user_dict) + return {"user": user_schema} def update_user_profile(db: Session, user_id: int, user_data: UserUpdate) -> Dict[str, Any]: @@ -131,8 +120,24 @@ def add_user_address(db: Session, user_id: int, address: AddressCreate) -> Dict[ from app.schemas.user_schemas import Address as AddressSchema new_address = user_repo.create_address(db, address, user_id) - # Преобразуем объект SQLAlchemy в схему Pydantic - address_schema = AddressSchema.model_validate(new_address) + print(f"Адрес успешно создан: {new_address}") + + # Преобразуем объект SQLAlchemy в словарь, а затем в схему Pydantic + address_dict = { + "id": new_address.id, + "user_id": new_address.user_id, + "address_line1": new_address.address_line1, + "address_line2": new_address.address_line2, + "city": new_address.city, + "state": new_address.state, + "postal_code": new_address.postal_code, + "country": new_address.country, + "is_default": new_address.is_default, + "created_at": new_address.created_at, + "updated_at": new_address.updated_at + } + + address_schema = AddressSchema.model_validate(address_dict) return {"address": address_schema} @@ -140,8 +145,23 @@ def update_user_address(db: Session, user_id: int, address_id: int, address: Add from app.schemas.user_schemas import Address as AddressSchema updated_address = user_repo.update_address(db, address_id, address, user_id) - # Преобразуем объект SQLAlchemy в схему Pydantic - address_schema = AddressSchema.model_validate(updated_address) + + # Преобразуем объект SQLAlchemy в словарь, а затем в схему Pydantic + address_dict = { + "id": updated_address.id, + "user_id": updated_address.user_id, + "address_line1": updated_address.address_line1, + "address_line2": updated_address.address_line2, + "city": updated_address.city, + "state": updated_address.state, + "postal_code": updated_address.postal_code, + "country": updated_address.country, + "is_default": updated_address.is_default, + "created_at": updated_address.created_at, + "updated_at": updated_address.updated_at + } + + address_schema = AddressSchema.model_validate(address_dict) return {"address": address_schema} diff --git a/backend/uploads/products/9/0beaff9a-bec5-4624-bc08-08fa492ce05c.jpg b/backend/uploads/products/9/0beaff9a-bec5-4624-bc08-08fa492ce05c.jpg new file mode 100644 index 0000000000000000000000000000000000000000..688b48058d05f6f113ac5f7e98ed60198a1ec777 GIT binary patch literal 22263 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�V}obaW_4qI7!`JZyKG7ygqpN- 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 literal 0 HcmV?d00001 diff --git a/frontend/components/AddToCartButton.tsx b/frontend/components/AddToCartButton.tsx new file mode 100644 index 0000000..59f1ef9 --- /dev/null +++ b/frontend/components/AddToCartButton.tsx @@ -0,0 +1,92 @@ +import { useState } from 'react'; +import { useRouter } from 'next/router'; +import { ShoppingCart, Check, AlertCircle } from 'lucide-react'; +import cartService from '../services/cart'; +import authService from '../services/auth'; + +interface AddToCartButtonProps { + variantId: number; + quantity?: number; + className?: string; + onAddToCart?: () => void; +} + +export default function AddToCartButton({ variantId, quantity = 1, className = '', onAddToCart }: AddToCartButtonProps) { + const router = useRouter(); + const [loading, setLoading] = useState(false); + const [success, setSuccess] = useState(false); + const [error, setError] = useState(null); + + const handleAddToCart = async () => { + // Проверяем, авторизован ли пользователь + if (!authService.isAuthenticated()) { + // Сохраняем текущий URL для редиректа после авторизации + const currentPath = router.asPath; + router.push(`/login?redirect=${encodeURIComponent(currentPath)}`); + return; + } + + setLoading(true); + setError(null); + + try { + await cartService.addToCart({ + variant_id: variantId, + quantity: quantity + }); + + setSuccess(true); + + // Вызываем колбэк, если он передан + if (onAddToCart) { + onAddToCart(); + } + + // Сбрасываем состояние успеха через 2 секунды + setTimeout(() => { + setSuccess(false); + }, 2000); + } catch (err) { + console.error('Ошибка при добавлении в корзину:', err); + setError('Не удалось добавить товар в корзину'); + + // Сбрасываем состояние ошибки через 3 секунды + setTimeout(() => { + setError(null); + }, 3000); + } finally { + setLoading(false); + } + }; + + return ( +
+ + + {error && ( +
+ + {error} +
+ )} +
+ ); +} \ No newline at end of file diff --git a/frontend/components/Footer.tsx b/frontend/components/Footer.tsx index df5dfb9..77614f1 100644 --- a/frontend/components/Footer.tsx +++ b/frontend/components/Footer.tsx @@ -1,14 +1,29 @@ import Link from "next/link"; -import { Facebook, Instagram, Twitter, Youtube } from "lucide-react"; +import { Facebook, Instagram, Twitter, Youtube, ChevronDown, ChevronUp } from "lucide-react"; +import { useState } from "react"; export default function Footer() { + // Состояния для отображения/скрытия разделов на мобильных устройствах + const [helpOpen, setHelpOpen] = useState(false); + const [shopOpen, setShopOpen] = useState(false); + const [aboutOpen, setAboutOpen] = useState(false); + return ( -