Skip to content

Commit

Permalink
[Task#80295] Implement checkout page
Browse files Browse the repository at this point in the history
  • Loading branch information
yamada5131 committed Sep 10, 2024
1 parent a1e0382 commit 23e6621
Show file tree
Hide file tree
Showing 13 changed files with 377 additions and 735 deletions.
61 changes: 60 additions & 1 deletion ecommerce-app/app/Http/Controllers/OrderController.php
Original file line number Diff line number Diff line change
Expand Up @@ -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,
]);
}
}
6 changes: 6 additions & 0 deletions ecommerce-app/app/Models/Address.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -20,4 +21,9 @@ public static function booted(): void
$address->id = Str::uuid();
});
}

public function country(): HasOne
{
return $this->hasOne(Country::class);
}
}
29 changes: 23 additions & 6 deletions ecommerce-app/app/Models/OrderDetail.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand All @@ -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);
}
}
2 changes: 2 additions & 0 deletions ecommerce-app/app/Models/OrderItem.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ class OrderItem extends Model
{
use HasFactory;

protected $guarded = [];

public function userReview(): HasOne
{
return $this->hasOne(UserReview::class);
Expand Down
2 changes: 2 additions & 0 deletions ecommerce-app/app/Models/OrderStatus.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ class OrderStatus extends Model

protected $keyType = 'string';

protected $guarded = [];

public static function booted(): void
{
static::creating(function (OrderStatus $orderStatus) {
Expand Down
11 changes: 11 additions & 0 deletions ecommerce-app/app/Models/ShoppingCartItem.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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);
}
}
5 changes: 5 additions & 0 deletions ecommerce-app/app/Models/UserAddress.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,9 @@ public function users(): BelongsTo
{
return $this->belongsTo(User::class);
}

public function address(): BelongsTo
{
return $this->belongsTo(Address::class);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
{{-- TODO: Breaking form into blade component --}}
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/flowbite.min.css" rel="stylesheet" />
@vite(['resources/css/app.css', 'resources/js/app.js'])
</head>

<body>
<section class="bg-white py-8 antialiased dark:bg-gray-900 md:py-16">
<div class="mx-auto max-w-2xl px-4 2xl:px-0">
<h2 class="text-xl font-semibold text-gray-900 dark:text-white sm:text-2xl mb-2">Thanks for your order!</h2>
<p class="text-gray-500 dark:text-gray-400 mb-6 md:mb-8">Your order <a href="#"
class="font-medium text-gray-900 dark:text-white hover:underline">{{ $orderDetail->id }}</a> will be
processed
within 24 hours during working days. We will notify you by email once your order has been shipped.</p>
<div
class="space-y-4 sm:space-y-2 rounded-lg border border-gray-100 bg-gray-50 p-6 dark:border-gray-700 dark:bg-gray-800 mb-6 md:mb-8">
<dl class="sm:flex items-center justify-between gap-4">
<dt class="font-normal mb-1 sm:mb-0 text-gray-500 dark:text-gray-400">Date</dt>
<dd class="font-medium text-gray-900 dark:text-white sm:text-end">{{ $orderDetail->order_date }}
</dd>
</dl>
<dl class="sm:flex items-center justify-between gap-4">
<dt class="font-normal mb-1 sm:mb-0 text-gray-500 dark:text-gray-400">Payment Method</dt>
<dd class="font-medium text-gray-900 dark:text-white sm:text-end">COD</dd>
</dl>
<dl class="sm:flex items-center justify-between gap-4">
<dt class="font-normal mb-1 sm:mb-0 text-gray-500 dark:text-gray-400">Name</dt>
<dd class="font-medium text-gray-900 dark:text-white sm:text-end">
{{ $user->first_name . ' ' . $user->last_name }}</dd>
</dl>
<dl class="sm:flex items-center justify-between gap-4">
<dt class="font-normal mb-1 sm:mb-0 text-gray-500 dark:text-gray-400">Address</dt>
<dd class="font-medium text-gray-900 dark:text-white sm:text-end">
{{ $address->address_line1 . $address->address_line2 }}</dd>
</dl>
<dl class="sm:flex items-center justify-between gap-4">
<dt class="font-normal mb-1 sm:mb-0 text-gray-500 dark:text-gray-400">Phone</dt>
<dd class="font-medium text-gray-900 dark:text-white sm:text-end">
{{ $user->telephone }}</dd>
</dl>
</div>
<div class="flex items-center space-x-4">
<a href="#"
class="text-white bg-primary-700 hover:bg-primary-800 focus:ring-4 focus:ring-primary-300 font-medium rounded-lg text-sm px-5 py-2.5 dark:bg-primary-600 dark:hover:bg-primary-700 focus:outline-none dark:focus:ring-primary-800">Track
your order</a>
<a href="#"
class="py-2.5 px-5 text-sm font-medium text-gray-900 focus:outline-none bg-white rounded-lg border border-gray-200 hover:bg-gray-100 hover:text-primary-700 focus:z-10 focus:ring-4 focus:ring-gray-100 dark:focus:ring-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700">Return
to shopping</a>
</div>
</div>
</section>

</body>

</html>
125 changes: 125 additions & 0 deletions ecommerce-app/resources/views/components/orders/index.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
{{-- TODO: Breaking form into blade component --}}
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/flowbite.min.css" rel="stylesheet" />
@vite(['resources/css/app.css', 'resources/js/app.js'])
</head>

<body>
<section class="bg-white py-8 antialiased dark:bg-gray-900 md:py-16">
<form action="/orders" method="POST" class="mx-auto max-w-screen-xl px-4 2xl:px-0">
@csrf
<div class="mx-auto max-w-3xl">
<h2 class="text-xl font-semibold text-gray-900 dark:text-white sm:text-2xl">Order summary</h2>

<div class="mt-6 space-y-4 border-b border-t border-gray-200 py-8 dark:border-gray-700 sm:mt-8">
<h4 class="text-lg font-semibold text-gray-900 dark:text-white">Billing & Delivery information</h4>

<dl>
<dt class="text-base font-medium text-gray-900 dark:text-white">
{{ $user->first_name . ' ' . $user->last_name }}
</dt>
<dd id="selectedAddress" class="mt-1 text-base font-normal text-gray-500 dark:text-gray-400">
{{ $defaultAddress->address_line1 . ' ' . $defaultAddress->address_line2 . ' ' . $defaultAddress->city . ' ' . $defaultAddress->state . ' ' . $defaultAddress->postal_code }}
</dd>
</dl>

<button type="button" data-modal-target="billingInformationModal"
data-modal-toggle="billingInformationModal"
class="text-base font-medium text-primary-700 hover:underline dark:text-primary-500">Edit</button>
</div>

<div class="mt-6 sm:mt-8">
<div class="relative overflow-x-auto border-b border-gray-200 dark:border-gray-800">
<table class="w-full text-left font-medium text-gray-900 dark:text-white md:table-fixed">
@foreach ($orderItems as $orderItem)
<x-orders.order-item :orderItem="$orderItem" />
@endforeach
</table>
</div>

<div class="mt-4 space-y-6">
<h4 class="text-xl font-semibold text-gray-900 dark:text-white">Order summary</h4>

<div class="space-y-4">
<div class="space-y-2">
<dl class="flex items-center justify-between gap-4">
<dt class="text-gray-500 dark:text-gray-400">Original price</dt>
<dd class="text-base font-medium text-gray-900 dark:text-white">${{ $totalPrice }}
</dd>
</dl>

<dl class="flex items-center justify-between gap-4">
<dt class="text-gray-500 dark:text-gray-400">Savings</dt>
<dd class="text-base font-medium text-green-500">-$000.00</dd>
</dl>

<dl class="flex items-center justify-between gap-4">
<dt class="text-gray-500 dark:text-gray-400">Store Pickup</dt>
<dd class="text-base font-medium text-gray-900 dark:text-white">$00</dd>
</dl>

<dl class="flex items-center justify-between gap-4">
<dt class="text-gray-500 dark:text-gray-400">Tax</dt>
<dd class="text-base font-medium text-gray-900 dark:text-white">$000</dd>
</dl>
</div>

<dl
class="flex items-center justify-between gap-4 border-t border-gray-200 pt-2 dark:border-gray-700">
<dt class="text-lg font-bold text-gray-900 dark:text-white">Total</dt>
<dd class="text-lg font-bold text-gray-900 dark:text-white">${{ $totalPrice }}</dd>
</dl>
</div>

<div class="flex items-start sm:items-center">
<input id="terms-checkbox-2" type="checkbox" name="terms" value="1"
class="h-4 w-4 rounded border-gray-300 bg-gray-100 text-primary-600 focus:ring-2 focus:ring-primary-500 dark:border-gray-600 dark:bg-gray-700 dark:ring-offset-gray-800 dark:focus:ring-primary-600" />
<label for="terms-checkbox-2"
class="ms-2 text-sm font-medium text-gray-900 dark:text-gray-300"> I agree with the <a
href="#" title=""
class="text-primary-700 underline hover:no-underline dark:text-primary-500">Terms
and
Conditions</a> of use of the Flowbite marketplace </label>
</div>

<div class="gap-4 sm:flex sm:items-center">
<button type="button"
class="w-full rounded-lg border border-gray-200 bg-white px-5 py-2.5 text-sm font-medium text-gray-900 hover:bg-gray-100 hover:text-primary-700 focus:z-10 focus:outline-none focus:ring-4 focus:ring-gray-100 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white dark:focus:ring-gray-700">Return
to Shopping
</button>
<!-- Order Details -->
{{-- <input type="hidden" name="user_id" value="{{ Auth::user()->id }}"> --}}
<input type="hidden" name="user_id" value="{{ $user->id }}">
<input type="hidden" name="order_date" value="{{ now() }}">
<input type="hidden" name="address_id" value="{{ $defaultAddress->id }}">
<input type="hidden" name="order_total" value="{{ $totalPrice }}">

<!-- Order Items -->
@foreach ($orderItems as $item)
<input type="hidden" name="order_items[{{ $loop->index }}][product_id]"
value="{{ $item->product_id }}">
<input type="hidden" name="order_items[{{ $loop->index }}][qty]"
value="{{ $item->qty }}">
<input type="hidden" name="order_items[{{ $loop->index }}][price]"
value="{{ $item->product->price }}">
@endforeach
<button type="submit"
class="mt-4 flex w-full items-center justify-center rounded-lg bg-primary-700 px-5 py-2.5 text-sm font-medium text-white hover:bg-primary-800 focus:outline-none focus:ring-4 focus:ring-primary-300 dark:bg-primary-600 dark:hover:bg-primary-700 dark:focus:ring-primary-800 sm:mt-0">Send
the order
</button>
</div>
</div>
</div>
</div>
</form>
</section>

</body>

</html>
Loading

0 comments on commit 23e6621

Please sign in to comment.