From 23e6621b926f228725e299cbcdb8f5857e1dc2d2 Mon Sep 17 00:00:00 2001 From: yamada5131 Date: Mon, 26 Aug 2024 00:00:55 +0700 Subject: [PATCH] [Task#80295] Implement checkout page --- .../app/Http/Controllers/OrderController.php | 61 +- ecommerce-app/app/Models/Address.php | 6 + ecommerce-app/app/Models/OrderDetail.php | 29 +- ecommerce-app/app/Models/OrderItem.php | 2 + ecommerce-app/app/Models/OrderStatus.php | 2 + ecommerce-app/app/Models/ShoppingCartItem.php | 11 + ecommerce-app/app/Models/UserAddress.php | 5 + .../components/orders/confirmation.blade.php | 61 ++ .../views/components/orders/index.blade.php | 125 ++++ .../components/orders/order-item.blade.php | 21 + .../resources/views/orders/index.blade.php | 651 ------------------ ecommerce-app/routes/web.php | 43 +- ecommerce-app/tailwind.config.js | 95 ++- 13 files changed, 377 insertions(+), 735 deletions(-) create mode 100644 ecommerce-app/resources/views/components/orders/confirmation.blade.php create mode 100644 ecommerce-app/resources/views/components/orders/index.blade.php create mode 100644 ecommerce-app/resources/views/components/orders/order-item.blade.php delete mode 100644 ecommerce-app/resources/views/orders/index.blade.php diff --git a/ecommerce-app/app/Http/Controllers/OrderController.php b/ecommerce-app/app/Http/Controllers/OrderController.php index 607edf7..86d405f 100644 --- a/ecommerce-app/app/Http/Controllers/OrderController.php +++ b/ecommerce-app/app/Http/Controllers/OrderController.php @@ -2,12 +2,71 @@ namespace App\Http\Controllers; +use App\Models\OrderDetail; +use App\Models\OrderItem; +use App\Models\OrderStatus; +use App\Models\User; +use Illuminate\Http\Request; +use Illuminate\Support\Facades\DB; use Illuminate\View\View; +use Symfony\Component\HttpFoundation\RedirectResponse; class OrderController extends Controller { public function index(): View { - return view('orders.index'); + // TODO: Replace this with the authenticated user + // $user = Auth::user(); + $user = User::all()->random(); + $orderItems = $user->shoppingCarts->first()->shoppingCartItems; + $defaultAddress = $user->userAddresses->where('is_default', true)->first()->address; + + $totalPrice = $orderItems->sum(function ($item) { + return $item->product->price * $item->qty; + }); + + return view('components.orders.index', compact('user', 'orderItems', 'defaultAddress', 'totalPrice')); + } + + public function store(Request $request): RedirectResponse + { + $orderDetail = null; + + DB::transaction(function () use ($request, &$orderDetail) { + // dd(request()->all()); + $orderStatus = OrderStatus::create([ + 'status' => 'Pending', + ]); + + $orderDetail = OrderDetail::create([ + 'user_id' => $request->user_id, + 'address_id' => $request->address_id, + 'order_date' => $request->order_date, + 'order_total' => $request->order_total, + 'order_status_id' => $orderStatus->id, + ]); + + foreach ($request->order_items as $item) { + OrderItem::create([ + 'order_detail_id' => $orderDetail->id, + 'product_id' => $item['product_id'], + 'qty' => $item['qty'], + 'price' => $item['price'], + ]); + } + }); + + return redirect()->route('orders.confirmation', ['order_id' => $orderDetail->id]); + } + + public function showConfirmation($order_id): View + { + $orderDetail = OrderDetail::with(['user', 'address'])->findOrFail($order_id); + + return view('components.orders.confirmation', [ + 'orderDetail' => $orderDetail, + 'user' => $orderDetail->user, + 'address' => $orderDetail->address, + ]); } } diff --git a/ecommerce-app/app/Models/Address.php b/ecommerce-app/app/Models/Address.php index 216c604..7aef03d 100644 --- a/ecommerce-app/app/Models/Address.php +++ b/ecommerce-app/app/Models/Address.php @@ -4,6 +4,7 @@ use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Relations\HasOne; use Illuminate\Support\Str; class Address extends Model @@ -20,4 +21,9 @@ public static function booted(): void $address->id = Str::uuid(); }); } + + public function country(): HasOne + { + return $this->hasOne(Country::class); + } } diff --git a/ecommerce-app/app/Models/OrderDetail.php b/ecommerce-app/app/Models/OrderDetail.php index 7c58f6b..91f0c2f 100644 --- a/ecommerce-app/app/Models/OrderDetail.php +++ b/ecommerce-app/app/Models/OrderDetail.php @@ -4,9 +4,9 @@ use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; -use Illuminate\Support\Str; +use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; -use Illuminate\Database\Eloquent\Relations\HasManyThrough; +use Illuminate\Support\Str; class OrderDetail extends Model { @@ -16,19 +16,36 @@ class OrderDetail extends Model protected $keyType = 'string'; + protected $guarded = []; + + protected $casts = [ + 'id' => 'string', + ]; + public static function booted(): void { static::creating(function (OrderDetail $orderDetail) { $orderDetail->id = Str::uuid(); }); } - - protected $casts = [ - 'id' => 'string' - ]; public function orderItems(): HasMany { return $this->hasMany(OrderItem::class); } + + public function status(): BelongsTo + { + return $this->belongsTo(OrderStatus::class, 'order_status_id'); + } + + public function user() + { + return $this->belongsTo(User::class); + } + + public function address() + { + return $this->belongsTo(Address::class); + } } diff --git a/ecommerce-app/app/Models/OrderItem.php b/ecommerce-app/app/Models/OrderItem.php index 2537f73..1858192 100644 --- a/ecommerce-app/app/Models/OrderItem.php +++ b/ecommerce-app/app/Models/OrderItem.php @@ -12,6 +12,8 @@ class OrderItem extends Model { use HasFactory; + protected $guarded = []; + public function userReview(): HasOne { return $this->hasOne(UserReview::class); diff --git a/ecommerce-app/app/Models/OrderStatus.php b/ecommerce-app/app/Models/OrderStatus.php index 35cf84f..41c7cbc 100644 --- a/ecommerce-app/app/Models/OrderStatus.php +++ b/ecommerce-app/app/Models/OrderStatus.php @@ -14,6 +14,8 @@ class OrderStatus extends Model protected $keyType = 'string'; + protected $guarded = []; + public static function booted(): void { static::creating(function (OrderStatus $orderStatus) { diff --git a/ecommerce-app/app/Models/ShoppingCartItem.php b/ecommerce-app/app/Models/ShoppingCartItem.php index 94545ff..88bfb7c 100644 --- a/ecommerce-app/app/Models/ShoppingCartItem.php +++ b/ecommerce-app/app/Models/ShoppingCartItem.php @@ -4,6 +4,7 @@ use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Support\Str; class ShoppingCartItem extends Model @@ -16,10 +17,20 @@ class ShoppingCartItem extends Model protected $keyType = 'string'; + public function cart(): BelongsTo + { + return $this->belongsTo(ShoppingCart::class, 'shopping_cart_id'); + } + public static function booted(): void { static::creating(function (ShoppingCartItem $shoppingCartItem) { $shoppingCartItem->id = Str::uuid(); }); } + + public function product(): BelongsTo + { + return $this->belongsTo(Product::class); + } } diff --git a/ecommerce-app/app/Models/UserAddress.php b/ecommerce-app/app/Models/UserAddress.php index 40fc5a7..0d59715 100644 --- a/ecommerce-app/app/Models/UserAddress.php +++ b/ecommerce-app/app/Models/UserAddress.php @@ -28,4 +28,9 @@ public function users(): BelongsTo { return $this->belongsTo(User::class); } + + public function address(): BelongsTo + { + return $this->belongsTo(Address::class); + } } diff --git a/ecommerce-app/resources/views/components/orders/confirmation.blade.php b/ecommerce-app/resources/views/components/orders/confirmation.blade.php new file mode 100644 index 0000000..67997bf --- /dev/null +++ b/ecommerce-app/resources/views/components/orders/confirmation.blade.php @@ -0,0 +1,61 @@ +{{-- TODO: Breaking form into blade component --}} + + + + + + + Document + + @vite(['resources/css/app.css', 'resources/js/app.js']) + + + +
+
+

Thanks for your order!

+

Your order {{ $orderDetail->id }} will be + processed + within 24 hours during working days. We will notify you by email once your order has been shipped.

+
+
+
Date
+
{{ $orderDetail->order_date }} +
+
+
+
Payment Method
+
COD
+
+
+
Name
+
+ {{ $user->first_name . ' ' . $user->last_name }}
+
+
+
Address
+
+ {{ $address->address_line1 . $address->address_line2 }}
+
+
+
Phone
+
+ {{ $user->telephone }}
+
+
+ +
+
+ + + + diff --git a/ecommerce-app/resources/views/components/orders/index.blade.php b/ecommerce-app/resources/views/components/orders/index.blade.php new file mode 100644 index 0000000..4cdbbf8 --- /dev/null +++ b/ecommerce-app/resources/views/components/orders/index.blade.php @@ -0,0 +1,125 @@ +{{-- TODO: Breaking form into blade component --}} + + + + + + + Document + + @vite(['resources/css/app.css', 'resources/js/app.js']) + + + +
+
+ @csrf +
+

Order summary

+ +
+

Billing & Delivery information

+ +
+
+ {{ $user->first_name . ' ' . $user->last_name }} +
+
+ {{ $defaultAddress->address_line1 . ' ' . $defaultAddress->address_line2 . ' ' . $defaultAddress->city . ' ' . $defaultAddress->state . ' ' . $defaultAddress->postal_code }} +
+
+ + +
+ +
+
+ + @foreach ($orderItems as $orderItem) + + @endforeach +
+
+ +
+

Order summary

+ +
+
+
+
Original price
+
${{ $totalPrice }} +
+
+ +
+
Savings
+
-$000.00
+
+ +
+
Store Pickup
+
$00
+
+ +
+
Tax
+
$000
+
+
+ +
+
Total
+
${{ $totalPrice }}
+
+
+ +
+ + +
+ +
+ + + {{-- --}} + + + + + + + @foreach ($orderItems as $item) + + + + @endforeach + +
+
+
+
+
+
+ + + + diff --git a/ecommerce-app/resources/views/components/orders/order-item.blade.php b/ecommerce-app/resources/views/components/orders/order-item.blade.php new file mode 100644 index 0000000..41bd572 --- /dev/null +++ b/ecommerce-app/resources/views/components/orders/order-item.blade.php @@ -0,0 +1,21 @@ + + + +
+ + watch image + + {{ $orderItem->product->name }} +
+ + + + x{{ $orderItem->qty }} + + ${{ $orderItem->product->price * $orderItem->qty }} + + + diff --git a/ecommerce-app/resources/views/orders/index.blade.php b/ecommerce-app/resources/views/orders/index.blade.php deleted file mode 100644 index 86a7162..0000000 --- a/ecommerce-app/resources/views/orders/index.blade.php +++ /dev/null @@ -1,651 +0,0 @@ - - - - - - - Document - - - - -
-
-
-

Order summary

- -
-

Billing & Delivery information

- -
-
Individual
-
Bonnie Green - +1 234 - 567 - 890, San Francisco, California, United States, 3454, Scott Street
-
- - -
- -
-
- - @foreach ($orders as $order) - - - - - - - - - - @endforeach -
- - x2 - $799 -
-
- -
-

Order summary

- -
-
-
-
Original price
-
$6,592.00
-
- -
-
Savings
-
-$299.00
-
- -
-
Store Pickup
-
$99
-
- -
-
Tax
-
$799
-
-
- -
-
Total
-
$7,191.00
-
-
- -
- - -
- -
- - - -
-
-
-
-
-
- - - - - - diff --git a/ecommerce-app/routes/web.php b/ecommerce-app/routes/web.php index f83e49c..c8d2e88 100644 --- a/ecommerce-app/routes/web.php +++ b/ecommerce-app/routes/web.php @@ -1,32 +1,32 @@ name('home.index'); Route::get('/orders', [OrderController::class, 'index'])->name('orders.index'); - +Route::post('/orders', [OrderController::class, 'store'])->name('orders.store'); +Route::get( + '/orders/confirmation/{order_id}', + [OrderController::class, 'showConfirmation'] +)->name('orders.confirmation'); Route::get('/dashboard', [DashboardController::class, 'index'])->name('dashboard.index'); // Profile management routes @@ -37,6 +37,9 @@ Route::get('/order-details', [OrderDetailController::class, 'index'])->name('order-details.index'); Route::get('/order-details/{orderDetail}', [OrderDetailController::class, 'show'])->name('order-details.show'); + Route::get('/cart', [CartController::class, 'show'])->name('cart.show'); + Route::post('/cart/add', [CartController::class, 'update'])->name('cart.update'); + Route::delete('/cart/removeItem', [CartController::class, 'removeItem'])->name('cart.removeItem'); }); Route::middleware(['auth', 'admin'])->group(function () { @@ -88,10 +91,6 @@ Route::get('/cart', [CartController::class, 'show'])->name('cart.show'); Route::get('/products', [CategoryController::class, 'index'])->name('products.index'); Route::get('/products/{product}', [ProductController::class, 'show'])->name('products.show'); +Route::get('/language/{lang}', [LanguageController::class, 'changeLanguage'])->name('locale'); require __DIR__.'/auth.php'; - - - - - diff --git a/ecommerce-app/tailwind.config.js b/ecommerce-app/tailwind.config.js index a230091..dd5aea6 100644 --- a/ecommerce-app/tailwind.config.js +++ b/ecommerce-app/tailwind.config.js @@ -3,7 +3,7 @@ import forms from '@tailwindcss/forms'; /** @type {import('tailwindcss').Config} */ export default { - darkMode: 'selector', + darkMode: 'class', content: [ './vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php', './storage/framework/views/*.php', @@ -14,66 +14,51 @@ export default { theme: { extend: { - colors: { - primary: { - "50": "#eff6ff", - "100": "#dbeafe", - "200": "#bfdbfe", - "300": "#93c5fd", - "400": "#60a5fa", - "500": "#3b82f6", - "600": "#2563eb", - "700": "#1d4ed8", - "800": "#1e40af", - "900": "#1e3a8a", - "950": "#172554" - } - }, - fontFamily: { - sans: [ - 'Figtree', - 'Inter', - 'ui-sans-serif', - 'system-ui', - '-apple-system', - 'system-ui', - 'Segoe UI', - 'Roboto', - 'Helvetica Neue', - 'Arial', - 'Noto Sans', - 'sans-serif', - 'Apple Color Emoji', - 'Segoe UI Emoji', - 'Segoe UI Symbol', - 'Noto Color Emoji', - ...defaultTheme.fontFamily.sans - ], - 'body': [ - 'Inter', - 'ui-sans-serif', - 'system-ui', - '-apple-system', - 'system-ui', - 'Segoe UI', - 'Roboto', - 'Helvetica Neue', - 'Arial', - 'Noto Sans', - 'sans-serif', - 'Apple Color Emoji', - 'Segoe UI Emoji', - 'Segoe UI Symbol', - 'Noto Color Emoji' - ], - }, colors: { primary: {"50":"#eff6ff","100":"#dbeafe","200":"#bfdbfe","300":"#93c5fd","400":"#60a5fa","500":"#3b82f6","600":"#2563eb","700":"#1d4ed8","800":"#1e40af","900":"#1e3a8a","950":"#172554"} } }, + fontFamily: { + 'body': [ + 'Inter', + 'ui-sans-serif', + 'system-ui', + '-apple-system', + 'system-ui', + 'Segoe UI', + 'Roboto', + 'Helvetica Neue', + 'Arial', + 'Noto Sans', + 'sans-serif', + 'Apple Color Emoji', + 'Segoe UI Emoji', + 'Segoe UI Symbol', + 'Noto Color Emoji' + ], + 'sans': [ + 'Inter', + 'ui-sans-serif', + 'system-ui', + '-apple-system', + 'system-ui', + 'Segoe UI', + 'Roboto', + 'Helvetica Neue', + 'Arial', + 'Noto Sans', + 'sans-serif', + 'Apple Color Emoji', + 'Segoe UI Emoji', + 'Segoe UI Symbol', + 'Noto Color Emoji' + ] + } }, - plugins: [forms], + plugins: [ + require('flowbite/plugin'), + ], };