checkout & create payment
This commit is contained in:
parent
d80b42c297
commit
fa00ee377a
|
|
@ -6,6 +6,7 @@ use App\Models\Address;
|
||||||
use App\Models\Location;
|
use App\Models\Location;
|
||||||
use App\Repositories\Member\Cart\MemberCartRepository;
|
use App\Repositories\Member\Cart\MemberCartRepository;
|
||||||
use App\Repositories\Member\ShippingRepository;
|
use App\Repositories\Member\ShippingRepository;
|
||||||
|
use App\Repositories\Member\Transaction\TransactionRepository;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
class CheckoutController extends Controller
|
class CheckoutController extends Controller
|
||||||
|
|
@ -77,6 +78,12 @@ class CheckoutController extends Controller
|
||||||
|
|
||||||
$location_id = session('location_id', 22);
|
$location_id = session('location_id', 22);
|
||||||
|
|
||||||
|
|
||||||
|
if ($delivery_method == null || $address_id == null || $location_id == null) {
|
||||||
|
|
||||||
|
return redirect()->route('checkout.delivery');
|
||||||
|
}
|
||||||
|
|
||||||
$subtotal = $memberCartRepository->getSubtotal($location_id);
|
$subtotal = $memberCartRepository->getSubtotal($location_id);
|
||||||
$total = $subtotal;
|
$total = $subtotal;
|
||||||
|
|
||||||
|
|
@ -98,11 +105,6 @@ class CheckoutController extends Controller
|
||||||
];
|
];
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return view('checkout.v1-delivery-1-shipping', [
|
return view('checkout.v1-delivery-1-shipping', [
|
||||||
'carts' => $request->user()->carts,
|
'carts' => $request->user()->carts,
|
||||||
'subtotal' => $subtotal,
|
'subtotal' => $subtotal,
|
||||||
|
|
@ -113,8 +115,6 @@ class CheckoutController extends Controller
|
||||||
'shipping_list' => $shipping_list,
|
'shipping_list' => $shipping_list,
|
||||||
]);
|
]);
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
dd($e);
|
|
||||||
|
|
||||||
return redirect()->route('checkout.delivery')->with('error', 'Invalid checkout data');
|
return redirect()->route('checkout.delivery')->with('error', 'Invalid checkout data');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -131,22 +131,73 @@ class CheckoutController extends Controller
|
||||||
|
|
||||||
session(['checkout_courier' => $courier]);
|
session(['checkout_courier' => $courier]);
|
||||||
session(['checkout_service' => $service]);
|
session(['checkout_service' => $service]);
|
||||||
session(['checkout_shipping_cost' => $cost]);
|
// session(['checkout_shipping_cost' => $cost]);
|
||||||
|
|
||||||
return redirect()->route('checkout.payment');
|
return redirect()->route('checkout.payment');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function choosePayment(Request $request)
|
public function choosePayment(Request $request, TransactionRepository $repository, MemberCartRepository $memberCartRepository)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
|
||||||
// proses checkout
|
$address_id = session('checkout_address_id');
|
||||||
|
|
||||||
|
$courier = session('checkout_courier');
|
||||||
|
$service = session('checkout_service');
|
||||||
|
|
||||||
|
$location_id = session('location_id', 22);
|
||||||
|
|
||||||
|
$items = [];
|
||||||
|
|
||||||
|
$request->merge(['location_id' => $location_id]);
|
||||||
|
$carts = $memberCartRepository->getList($request);
|
||||||
|
|
||||||
|
if (count($carts) == 0) {
|
||||||
|
return redirect()->route('checkout.delivery')->with('error', 'No items in cart');
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($carts as $cart) {
|
||||||
|
$items[] = [
|
||||||
|
"item_reference_id" => $cart->item_reference_id,
|
||||||
|
"qty" => $cart->qty
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
"address_id" => $address_id,
|
||||||
|
"note" => "",
|
||||||
|
"courier_company" => $courier,
|
||||||
|
"courier_type" => $service,
|
||||||
|
"location_id" => $location_id,
|
||||||
|
"items" => $items,
|
||||||
|
"vouchers" => [],
|
||||||
|
"use_customer_points" => 0,
|
||||||
|
];
|
||||||
|
|
||||||
|
$item = $repository->create($data);
|
||||||
|
|
||||||
|
$notification = new \App\Notifications\Member\Transaction\OrderWaitPayment($item);
|
||||||
|
$user = auth()->user();
|
||||||
|
$user->notify($notification->delay(now()->addMinutes(1)));
|
||||||
|
|
||||||
|
// return new CheckoutResource($item);
|
||||||
|
|
||||||
// proses payment
|
// proses payment
|
||||||
|
|
||||||
|
|
||||||
|
$payment = $item->payments()->where("method_type",'App\Models\XenditLink')
|
||||||
|
->where("status",'PENDING')
|
||||||
|
->first();
|
||||||
|
$invoice_url = $payment ? @$payment->method->invoice_url: "";
|
||||||
|
|
||||||
|
return redirect()->to($invoice_url);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
|
|
||||||
|
dd($e);
|
||||||
|
|
||||||
return redirect()->route('checkout.delivery')->with('error', 'Invalid checkout data');
|
return redirect()->route('checkout.delivery')->with('error', 'Invalid checkout data');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,9 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Awobaz\Compoships\Compoships;
|
use Awobaz\Compoships\Compoships;
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
use Cache;
|
|
||||||
|
|
||||||
class ItemReference extends Model
|
class ItemReference extends Model
|
||||||
{
|
{
|
||||||
|
|
@ -126,9 +127,7 @@ class ItemReference extends Model
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Log::info([$location_id, $is_consignment]);
|
return $this->hasOne(Discount::class, 'id', 'item_reference_id')
|
||||||
|
|
||||||
return $this->hasOne(Discount::class,DB::raw("discount_items.item_reference_id"))
|
|
||||||
->leftJoin("discount_items","discounts.id","=","discount_id")
|
->leftJoin("discount_items","discounts.id","=","discount_id")
|
||||||
->where("discounts.type","discount")
|
->where("discounts.type","discount")
|
||||||
->orderBy("discounts.created_at","desc")
|
->orderBy("discounts.created_at","desc")
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,66 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Repositories\Auth;
|
||||||
|
|
||||||
|
use App\Models\Permission;
|
||||||
|
use App\Models\Role;
|
||||||
|
use DB;
|
||||||
|
use Hash;
|
||||||
|
|
||||||
|
class RoleRepository
|
||||||
|
{
|
||||||
|
public function getList(array $params = [])
|
||||||
|
{
|
||||||
|
$limit = @$params["limit"] ? (int) @$params["limit"] : 10;
|
||||||
|
$sortColumn = @$params["sort"]["column"] ? $params["sort"]["column"] : "id";
|
||||||
|
$sortDir = @$params["sort"]["dir"] ? $params["sort"]["dir"] : "desc";
|
||||||
|
|
||||||
|
return Role::when(@$params['search'], function($query) use ($params) {
|
||||||
|
return $query->where('name', 'iLIKE', '%' . $params['search'] . '%');
|
||||||
|
})
|
||||||
|
->orderBy($sortColumn, $sortDir)
|
||||||
|
->paginate($limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPermission(array $params = [])
|
||||||
|
{
|
||||||
|
return Permission::orderBy("code")->paginate(1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function create(array $data)
|
||||||
|
{
|
||||||
|
$item = Role::create($data);
|
||||||
|
$item->permissions()->sync($data["permissions"]);
|
||||||
|
$item->save();
|
||||||
|
return $item;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update(Role $item, array $data)
|
||||||
|
{
|
||||||
|
$item->update($data);
|
||||||
|
$item->permissions()->sync($data["permissions"]);
|
||||||
|
$item->save();
|
||||||
|
return $item;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function delete(Role $item)
|
||||||
|
{
|
||||||
|
$item->delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findBy($column, $value)
|
||||||
|
{
|
||||||
|
$item = Role::where($column, $value)->firstOrFail();
|
||||||
|
return $item;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findUserByPermission($code)
|
||||||
|
{
|
||||||
|
$roles = Role::whereHas("permissions", function($query) use ($code){
|
||||||
|
$query->where("code", $code);
|
||||||
|
})->get();
|
||||||
|
return $roles->flatMap(function($role){
|
||||||
|
return $role->users;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Controllers\Member\Transaction;
|
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
|
||||||
use App\Http\Requests\Member\Transaction\TransactionRequest;
|
|
||||||
use App\Http\Resources\Member\Transaction\CheckoutResource;
|
|
||||||
use App\Repositories\Member\Transaction\TransactionRepository;
|
|
||||||
use App\Notifications\Member\Transaction\OrderWaitPayment;
|
|
||||||
use Illuminate\Support\Facades\Notification;
|
|
||||||
|
|
||||||
class CheckoutController extends Controller
|
|
||||||
{
|
|
||||||
public function index(TransactionRequest $request, TransactionRepository $repository)
|
|
||||||
{
|
|
||||||
$data = $request->validated();
|
|
||||||
$item = $repository->create($data);
|
|
||||||
|
|
||||||
$notification = new OrderWaitPayment($item);
|
|
||||||
$user = auth()->user();
|
|
||||||
$user->notify($notification->delay(now()->addMinutes(1)));
|
|
||||||
|
|
||||||
return new CheckoutResource($item);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,149 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Repositories\Member\Voucher;
|
||||||
|
|
||||||
|
use App\Models\Voucher;
|
||||||
|
use App\Models\Customer;
|
||||||
|
use App\Models\User;
|
||||||
|
use App\Models\Cart;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Validation\ValidationException;
|
||||||
|
use Carbon\Carbon;
|
||||||
|
|
||||||
|
class VoucherRepository
|
||||||
|
{
|
||||||
|
public function getList($request)
|
||||||
|
{
|
||||||
|
$user_id = auth()->user()->id;
|
||||||
|
$customer = Customer::where("user_id", $user_id)->first();
|
||||||
|
$model = Voucher::where('customer_id', (int) @$customer->id)
|
||||||
|
->where('used_at', null)
|
||||||
|
->where('expired_at','>=',Carbon::now())
|
||||||
|
->orderBy('created_at','desc')->paginate($request->limit);
|
||||||
|
|
||||||
|
return $model;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function getValidByItems($carts, $customer_id)
|
||||||
|
{
|
||||||
|
$item_reference_ids = $carts->pluck("item_reference_id")->toArray();
|
||||||
|
// collect voucher ids valids
|
||||||
|
if (count($item_reference_ids) == 0){
|
||||||
|
$item_reference_ids[] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
$item_ids = implode(',', $item_reference_ids);
|
||||||
|
$ids = DB::select("SELECT distinct vouchers.id from vouchers
|
||||||
|
left join voucher_events on vouchers.voucher_event_id = voucher_events.id
|
||||||
|
left join voucher_items on voucher_items.voucher_event_id = voucher_events.id
|
||||||
|
where used_at is null and
|
||||||
|
(expired_at > now() or expired_at is null) and
|
||||||
|
customer_id = ? and (
|
||||||
|
redeem_point > 0 or
|
||||||
|
( vouchers.voucher_event_id is null and vouchers.item_reference_id is null) or
|
||||||
|
( vouchers.voucher_event_id is not null and voucher_items.item_reference_id is null) or
|
||||||
|
( vouchers.item_reference_id is not null and vouchers.item_reference_id in ($item_ids)) or
|
||||||
|
( vouchers.voucher_event_id is not null and voucher_items.item_reference_id in ($item_ids))
|
||||||
|
)",[$customer_id]);
|
||||||
|
$ids = collect($ids)->pluck("id")->toArray();
|
||||||
|
return $ids;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSum($params)
|
||||||
|
{
|
||||||
|
$user_id = auth()->user()->id;
|
||||||
|
$customer = Customer::where("user_id", $user_id)->firstOrFail();
|
||||||
|
$voucher_ids = $params["voucher_ids"];
|
||||||
|
// collect item_reference_ids
|
||||||
|
$cart_ids = @$params["cart_ids"] ?? [];
|
||||||
|
$carts = Cart::where("user_id", $user_id)
|
||||||
|
->where(function($query) use ($cart_ids){
|
||||||
|
if (count($cart_ids)){
|
||||||
|
$query->whereIn("id", $cart_ids);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
->get();
|
||||||
|
$ids = $this->getValidByItems($carts, $customer->id);
|
||||||
|
|
||||||
|
return Voucher::where('customer_id', (int) @$customer->id)
|
||||||
|
->where('used_at', null)
|
||||||
|
->whereIn('id', $ids)
|
||||||
|
->whereIn('id', $ids)
|
||||||
|
->orderBy('created_at','desc')
|
||||||
|
->get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getValid($params)
|
||||||
|
{
|
||||||
|
$user_id = auth()->user()->id;
|
||||||
|
$customer = Customer::where("user_id", $user_id)->first();
|
||||||
|
$limit = $params["limit"] ?? 10;
|
||||||
|
|
||||||
|
// collect item_reference_ids
|
||||||
|
$cart_ids = @$params["cart_ids"] ?? [];
|
||||||
|
$carts = Cart::where("user_id", $user_id)
|
||||||
|
->where(function($query) use ($cart_ids){
|
||||||
|
if (count($cart_ids)){
|
||||||
|
$query->whereIn("id", $cart_ids);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
->get();
|
||||||
|
|
||||||
|
$ids = $this->getValidByItems($carts, $customer->id);
|
||||||
|
|
||||||
|
return Voucher::where('customer_id', (int) @$customer->id)
|
||||||
|
->where('used_at', null)
|
||||||
|
->orderBy('created_at','desc')->paginate($limit)
|
||||||
|
->transform(function ($voucher, $key) use ($ids) {
|
||||||
|
$voucher->is_valid = in_array($voucher->id, $ids);
|
||||||
|
return $voucher;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function detail($id)
|
||||||
|
{
|
||||||
|
$model = Voucher::findOrFail($id);
|
||||||
|
return $model;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function send($id, $data)
|
||||||
|
{
|
||||||
|
$model = DB::transaction(function () use ($id, $data) {
|
||||||
|
|
||||||
|
$customer = Customer::where("user_id", $data['customer_id'])->firstOrFail();
|
||||||
|
$model = Voucher::findOrFail($id);
|
||||||
|
|
||||||
|
\Log::info("VOUCHER SEND FROM ".$model->customer_id." TO ".$customer->id);
|
||||||
|
|
||||||
|
$model->customer_id = $customer->id;
|
||||||
|
$model->save();
|
||||||
|
|
||||||
|
return $model;
|
||||||
|
});
|
||||||
|
|
||||||
|
return $model;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function phoneCheck($data)
|
||||||
|
{
|
||||||
|
$model = DB::transaction(function () use ($data) {
|
||||||
|
|
||||||
|
$model = Customer::where('phone', $data['phone'])->first();
|
||||||
|
|
||||||
|
if ($model->user_id == null) {
|
||||||
|
|
||||||
|
throw ValidationException::withMessages([
|
||||||
|
"phone" => "User dari phone number belum terdaftar"
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$model = User::findOrfail($model->user_id);
|
||||||
|
|
||||||
|
return $model;
|
||||||
|
});
|
||||||
|
|
||||||
|
return $model;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\ThirdParty\Xendit;
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use Illuminate\Support\Facades\Http;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
|
||||||
|
class Xendit
|
||||||
|
{
|
||||||
|
public function createPaymentLink($payload){
|
||||||
|
|
||||||
|
$url = "https://api.xendit.co/v2/invoices";
|
||||||
|
$key = env("XENDIT_PRIVATE_KEY");
|
||||||
|
$res = Http::withBasicAuth($key, "")
|
||||||
|
->withBody(json_encode($payload), 'application/json')
|
||||||
|
->post($url);
|
||||||
|
|
||||||
|
if ($res->status() == 200)
|
||||||
|
return $res->json();
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -37,8 +37,6 @@
|
||||||
</h1>
|
</h1>
|
||||||
<form action="{{ route('checkout.shipping.process') }}" method="post">
|
<form action="{{ route('checkout.shipping.process') }}" method="post">
|
||||||
@csrf
|
@csrf
|
||||||
<input type="hidden" name="delivery_method" value="{{ $delivery_method }}">
|
|
||||||
<input type="hidden" name="address_id" value="{{ $address_id }}">
|
|
||||||
|
|
||||||
@if ($delivery_method == 'shipping')
|
@if ($delivery_method == 'shipping')
|
||||||
@foreach ($shipping_list as $shipping)
|
@foreach ($shipping_list as $shipping)
|
||||||
|
|
@ -100,360 +98,137 @@
|
||||||
@section('scripts')
|
@section('scripts')
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
// Delivery method selection
|
// Shipping option selection handler
|
||||||
const deliveryOption = document.getElementById('deliveryOption');
|
const shippingRadios = document.querySelectorAll('input[name="shipping_option"]');
|
||||||
const pickupOption = document.getElementById('pickupOption');
|
console.log('Found shipping radios:', shippingRadios.length);
|
||||||
const deliveryOptions = document.getElementById('deliveryOptions');
|
|
||||||
const pickupOptions = document.getElementById('pickupOptions');
|
|
||||||
const shippingAddress = document.getElementById('shippingAddress');
|
|
||||||
const continueButton = document.getElementById('continueButton');
|
|
||||||
|
|
||||||
// Handle delivery method change
|
shippingRadios.forEach((radio, index) => {
|
||||||
deliveryOption.addEventListener('change', function() {
|
console.log(`Radio ${index}:`, radio.value, radio.checked);
|
||||||
if (this.checked) {
|
|
||||||
deliveryOptions.style.display = 'block';
|
|
||||||
pickupOptions.style.display = 'none';
|
|
||||||
resetPickupSelection();
|
|
||||||
// Update button text for delivery
|
|
||||||
document.getElementById('continueButtonText').textContent =
|
|
||||||
'{{ __('checkout.continue_to_shipping') }}';
|
|
||||||
// Update hidden input values
|
|
||||||
document.getElementById('deliveryMethodInput').value = 'delivery';
|
|
||||||
// Update address ID from selected address
|
|
||||||
const addressSelect = document.getElementById('addressSelect');
|
|
||||||
if (addressSelect && addressSelect.value) {
|
|
||||||
document.getElementById('addressIdInput').value = addressSelect.value;
|
|
||||||
}
|
|
||||||
// Show shipping address step and shipping row
|
|
||||||
const shippingAddressStep = document.getElementById('shippingAddressStep');
|
|
||||||
const shippingRow = document.getElementById('shipping-row');
|
|
||||||
if (shippingAddressStep) {
|
|
||||||
console.log("Showing shipping address step");
|
|
||||||
shippingAddressStep.style.visibility = 'visible';
|
|
||||||
shippingAddressStep.style.height = 'auto';
|
|
||||||
shippingAddressStep.style.overflow = 'visible';
|
|
||||||
shippingAddressStep.style.margin = '';
|
|
||||||
shippingAddressStep.style.padding = '';
|
|
||||||
}
|
|
||||||
if (shippingRow) {
|
|
||||||
console.log("Showing shipping row");
|
|
||||||
shippingRow.style.visibility = 'visible';
|
|
||||||
shippingRow.style.height = 'auto';
|
|
||||||
shippingRow.style.overflow = 'visible';
|
|
||||||
shippingRow.style.margin = '';
|
|
||||||
shippingRow.style.padding = '';
|
|
||||||
} else {
|
|
||||||
console.log("Shipping row not found");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
pickupOption.addEventListener('change', function() {
|
|
||||||
if (this.checked) {
|
|
||||||
deliveryOptions.style.display = 'none';
|
|
||||||
pickupOptions.style.display = 'block';
|
|
||||||
resetDeliverySelection();
|
|
||||||
// Update button text for pickup
|
|
||||||
document.getElementById('continueButtonText').textContent =
|
|
||||||
'{{ __('checkout.continue_to_payment') }}';
|
|
||||||
// Update hidden input values
|
|
||||||
document.getElementById('deliveryMethodInput').value = 'pickup';
|
|
||||||
document.getElementById('addressIdInput').value = '';
|
|
||||||
// Hide shipping address step and shipping row
|
|
||||||
const shippingAddressStep = document.getElementById('shippingAddressStep');
|
|
||||||
const shippingRow = document.getElementById('shipping-row');
|
|
||||||
console.log("Elements found:", shippingAddressStep, shippingRow);
|
|
||||||
if (shippingAddressStep) {
|
|
||||||
console.log("Hiding shipping address step");
|
|
||||||
shippingAddressStep.style.visibility = 'hidden';
|
|
||||||
shippingAddressStep.style.height = '0';
|
|
||||||
shippingAddressStep.style.overflow = 'hidden';
|
|
||||||
shippingAddressStep.style.margin = '0';
|
|
||||||
shippingAddressStep.style.padding = '0';
|
|
||||||
}
|
|
||||||
if (shippingRow) {
|
|
||||||
console.log("Hiding shipping row");
|
|
||||||
shippingRow.style.visibility = 'hidden';
|
|
||||||
shippingRow.style.height = '0';
|
|
||||||
shippingRow.style.overflow = 'hidden';
|
|
||||||
shippingRow.style.margin = '0';
|
|
||||||
shippingRow.style.padding = '0';
|
|
||||||
} else {
|
|
||||||
console.log("Shipping row not found");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Address selection handler
|
|
||||||
const addressSelect = document.getElementById('addressSelect');
|
|
||||||
if (addressSelect) {
|
|
||||||
// Auto-populate form with first selected address on page load
|
|
||||||
function populateAddressForm() {
|
|
||||||
const selectedOption = addressSelect.options[addressSelect.selectedIndex];
|
|
||||||
|
|
||||||
if (selectedOption && selectedOption.value) {
|
|
||||||
// Populate all shipping address form fields
|
|
||||||
document.getElementById('firstName').value = selectedOption.dataset.firstName || '';
|
|
||||||
document.getElementById('lastName').value = selectedOption.dataset.lastName || '';
|
|
||||||
document.getElementById('address').value = selectedOption.dataset.address || '';
|
|
||||||
document.getElementById('city').value = selectedOption.dataset.city || '';
|
|
||||||
document.getElementById('state').value = selectedOption.dataset.state || '';
|
|
||||||
document.getElementById('zip').value = selectedOption.dataset.postcode || '';
|
|
||||||
document.getElementById('phone').value = selectedOption.dataset.phone || '';
|
|
||||||
document.getElementById('postcode').value = selectedOption.dataset.postcode || '';
|
|
||||||
|
|
||||||
// Update order summary with postcode
|
|
||||||
if (selectedOption.dataset.postcode) {
|
|
||||||
updateOrderSummaryWithShipping();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Populate on page load
|
|
||||||
populateAddressForm();
|
|
||||||
// Set initial address ID
|
|
||||||
if (addressSelect.value) {
|
|
||||||
document.getElementById('addressIdInput').value = addressSelect.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
addressSelect.addEventListener('change', function() {
|
|
||||||
const selectedOption = this.options[this.selectedIndex];
|
|
||||||
|
|
||||||
if (this.value === 'new' || this.value === '') {
|
|
||||||
// Clear form fields for new address
|
|
||||||
document.getElementById('firstName').value = '';
|
|
||||||
document.getElementById('lastName').value = '';
|
|
||||||
document.getElementById('address').value = '';
|
|
||||||
document.getElementById('city').value = '';
|
|
||||||
document.getElementById('state').value = '';
|
|
||||||
document.getElementById('zip').value = '';
|
|
||||||
document.getElementById('phone').value = '';
|
|
||||||
document.getElementById('postcode').value = '';
|
|
||||||
// Clear address ID input
|
|
||||||
document.getElementById('addressIdInput').value = '';
|
|
||||||
} else {
|
|
||||||
// Populate form fields with selected address
|
|
||||||
document.getElementById('firstName').value = selectedOption.dataset.firstName || '';
|
|
||||||
document.getElementById('lastName').value = selectedOption.dataset.lastName || '';
|
|
||||||
document.getElementById('address').value = selectedOption.dataset.address || '';
|
|
||||||
document.getElementById('city').value = selectedOption.dataset.city || '';
|
|
||||||
document.getElementById('state').value = selectedOption.dataset.state || '';
|
|
||||||
document.getElementById('zip').value = selectedOption.dataset.postcode || '';
|
|
||||||
document.getElementById('phone').value = selectedOption.dataset.phone || '';
|
|
||||||
document.getElementById('postcode').value = selectedOption.dataset.postcode || '';
|
|
||||||
// Update address ID input
|
|
||||||
document.getElementById('addressIdInput').value = this.value;
|
|
||||||
|
|
||||||
// Update order summary with postcode
|
|
||||||
if (selectedOption.dataset.postcode) {
|
|
||||||
updateOrderSummaryWithShipping();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Auto-update order summary when postcode changes
|
|
||||||
document.getElementById('postcode').addEventListener('input', function() {
|
|
||||||
if (this.value) {
|
|
||||||
updateOrderSummaryWithShipping();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Store selection for pickup
|
|
||||||
const storeRadios = document.querySelectorAll('input[name="store"]');
|
|
||||||
storeRadios.forEach(radio => {
|
|
||||||
radio.addEventListener('change', function() {
|
radio.addEventListener('change', function() {
|
||||||
|
console.log('Shipping option changed:', this.value, this.checked);
|
||||||
if (this.checked) {
|
if (this.checked) {
|
||||||
updateOrderSummaryForPickup();
|
updateOrderSummaryWithShippingCost(this.value);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Form validation before continue
|
function updateOrderSummaryWithShippingCost(shippingValue) {
|
||||||
continueButton.addEventListener('click', function(e) {
|
console.log('updateOrderSummaryWithShippingCost called with:', shippingValue);
|
||||||
const selectedMethod = document.querySelector('input[name="deliveryMethod"]:checked').value;
|
|
||||||
|
|
||||||
if (selectedMethod === 'delivery') {
|
// Parse the shipping value: courier|service|cost
|
||||||
if (!validateDeliveryForm()) {
|
const parts = shippingValue.split('|');
|
||||||
e.preventDefault();
|
console.log('Parsed parts:', parts);
|
||||||
return;
|
if (parts.length !== 3) {
|
||||||
}
|
console.log('Invalid shipping value format');
|
||||||
} else if (selectedMethod === 'pickup') {
|
return;
|
||||||
if (!validatePickupForm()) {
|
|
||||||
e.preventDefault();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
function resetDeliverySelection() {
|
|
||||||
resetShippingCalculation();
|
|
||||||
}
|
|
||||||
|
|
||||||
function resetPickupSelection() {
|
|
||||||
document.querySelectorAll('input[name="store"]').forEach(radio => {
|
|
||||||
radio.checked = false;
|
|
||||||
});
|
|
||||||
resetShippingCalculation();
|
|
||||||
}
|
|
||||||
|
|
||||||
function resetShippingCalculation() {
|
|
||||||
// Reset order summary to original state
|
|
||||||
const shippingElement = document.querySelector('[data-shipping-cost]');
|
|
||||||
if (shippingElement) {
|
|
||||||
shippingElement.style.display = 'none';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const totalElement = document.getElementById('cart-estimated-total');
|
const courier = parts[0];
|
||||||
if (totalElement && window.originalTotal) {
|
const service = parts[1];
|
||||||
totalElement.textContent = `Rp ${window.originalTotal}`;
|
const cost = parseInt(parts[2]);
|
||||||
}
|
console.log('Extracted cost:', cost);
|
||||||
}
|
|
||||||
|
|
||||||
function updateOrderSummaryWithShipping() {
|
|
||||||
// Simulate shipping cost calculation based on postcode
|
|
||||||
const shippingCost = calculateShippingCost(document.getElementById('postcode').value);
|
|
||||||
|
|
||||||
// Update order summary
|
// Update order summary
|
||||||
const subtotalElement = document.getElementById('cart-subtotal');
|
const subtotalElement = document.getElementById('cart-subtotal');
|
||||||
|
console.log('Subtotal element:', subtotalElement);
|
||||||
|
if (!subtotalElement) {
|
||||||
|
console.log('Subtotal element not found');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const currentSubtotal = parseFloat(subtotalElement.textContent.replace(/[^\d]/g, ''));
|
const currentSubtotal = parseFloat(subtotalElement.textContent.replace(/[^\d]/g, ''));
|
||||||
const newTotal = currentSubtotal + shippingCost;
|
console.log('Current subtotal:', currentSubtotal);
|
||||||
|
const newTotal = currentSubtotal + cost;
|
||||||
|
console.log('New total:', newTotal);
|
||||||
|
|
||||||
// Store original total
|
// Store original total
|
||||||
if (!window.originalTotal) {
|
if (!window.originalTotal) {
|
||||||
window.originalTotal = subtotalElement.textContent;
|
window.originalTotal = subtotalElement.textContent;
|
||||||
|
console.log('Stored original total:', window.originalTotal);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update total
|
// Update total
|
||||||
const totalElement = document.getElementById('cart-estimated-total');
|
const totalElement = document.getElementById('cart-estimated-total');
|
||||||
totalElement.textContent = `Rp ${number_format(newTotal, 0, ',', '.')}`;
|
console.log('Total element:', totalElement);
|
||||||
|
if (totalElement) {
|
||||||
|
totalElement.textContent = `Rp ${number_format(newTotal, 0, ',', '.')}`;
|
||||||
|
console.log('Updated total to:', totalElement.textContent);
|
||||||
|
}
|
||||||
|
|
||||||
// Show shipping cost in summary
|
// Show shipping cost in summary
|
||||||
showShippingCost(shippingCost);
|
showShippingCost(cost);
|
||||||
}
|
|
||||||
|
|
||||||
function updateOrderSummaryForPickup() {
|
|
||||||
// Pickup is usually free
|
|
||||||
const subtotalElement = document.getElementById('cart-subtotal');
|
|
||||||
const currentSubtotal = parseFloat(subtotalElement.textContent.replace(/[^\d]/g, ''));
|
|
||||||
|
|
||||||
// Store original total
|
|
||||||
if (!window.originalTotal) {
|
|
||||||
window.originalTotal = subtotalElement.textContent;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update total (same as subtotal for pickup)
|
|
||||||
const totalElement = document.getElementById('cart-estimated-total');
|
|
||||||
totalElement.textContent = subtotalElement.textContent;
|
|
||||||
|
|
||||||
// Show free shipping
|
|
||||||
showShippingCost(0, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
function calculateShippingCost(postcode) {
|
|
||||||
// Simple shipping cost calculation based on postcode
|
|
||||||
// In real implementation, this would call an API
|
|
||||||
const jakartaPostcodes = ['10000', '10110', '10220', '10310', '10410'];
|
|
||||||
const bandungPostcodes = ['40111', '40112', '40113', '40114', '40115'];
|
|
||||||
|
|
||||||
if (jakartaPostcodes.includes(postcode)) {
|
|
||||||
return 15000; // Jakarta: Rp 15,000
|
|
||||||
} else if (bandungPostcodes.includes(postcode)) {
|
|
||||||
return 25000; // Bandung: Rp 25,000
|
|
||||||
} else {
|
|
||||||
return 35000; // Other areas: Rp 35,000
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function showShippingCost(cost, isFree = false) {
|
function showShippingCost(cost, isFree = false) {
|
||||||
// Find or create shipping cost element in order summary
|
console.log('showShippingCost called with:', cost, isFree);
|
||||||
let shippingElement = document.querySelector('[data-shipping-cost]');
|
|
||||||
|
|
||||||
if (!shippingElement) {
|
// Use existing shipping row in order summary
|
||||||
const orderSummary = document.querySelector('.list-unstyled');
|
const shippingRow = document.getElementById('shipping-row');
|
||||||
const shippingLi = document.createElement('li');
|
console.log('Shipping row element:', shippingRow);
|
||||||
shippingLi.className = 'd-flex justify-content-between';
|
if (!shippingRow) {
|
||||||
shippingLi.setAttribute('data-shipping-cost', '');
|
console.log('Shipping row not found');
|
||||||
shippingLi.innerHTML = `
|
return;
|
||||||
<span>{{ __('checkout.shipping') }}:</span>
|
}
|
||||||
<span class="text-dark-emphasis fw-medium" id="shipping-cost">Rp 0</span>
|
|
||||||
`;
|
const costElement = shippingRow.querySelector('span:last-child');
|
||||||
orderSummary.appendChild(shippingLi);
|
console.log('Cost element:', costElement);
|
||||||
shippingElement = shippingLi;
|
if (!costElement) {
|
||||||
|
console.log('Cost element not found');
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const costElement = document.getElementById('shipping-cost');
|
|
||||||
if (isFree) {
|
if (isFree) {
|
||||||
costElement.textContent = '{{ __('checkout.free') }}';
|
costElement.textContent = '{{ __('checkout.free') }}';
|
||||||
costElement.className = 'text-success fw-medium';
|
costElement.className = 'text-success fw-medium';
|
||||||
|
console.log('Set to free shipping');
|
||||||
} else {
|
} else {
|
||||||
costElement.textContent = `Rp ${number_format(cost, 0, ',', '.')}`;
|
costElement.textContent = `Rp ${number_format(cost, 0, ',', '.')}`;
|
||||||
costElement.className = 'text-dark-emphasis fw-medium';
|
costElement.className = 'text-dark-emphasis fw-medium';
|
||||||
|
console.log('Set shipping cost to:', costElement.textContent);
|
||||||
}
|
}
|
||||||
|
|
||||||
shippingElement.style.display = 'flex';
|
// Ensure the shipping row is visible
|
||||||
|
shippingRow.style.display = 'flex';
|
||||||
|
console.log('Made shipping row visible');
|
||||||
}
|
}
|
||||||
|
|
||||||
function validateDeliveryForm() {
|
// Initialize shipping cost on page load for checked option
|
||||||
const postcode = document.getElementById('postcode').value;
|
const checkedShippingRadio = document.querySelector('input[name="shipping_option"]:checked');
|
||||||
const firstName = document.getElementById('firstName').value;
|
console.log('Checked shipping radio on load:', checkedShippingRadio);
|
||||||
const address = document.getElementById('address').value;
|
if (checkedShippingRadio) {
|
||||||
const city = document.getElementById('city').value;
|
console.log('Initializing with value:', checkedShippingRadio.value);
|
||||||
const phone = document.getElementById('phone').value;
|
updateOrderSummaryWithShippingCost(checkedShippingRadio.value);
|
||||||
|
|
||||||
if (!postcode) {
|
|
||||||
alert('{{ __('checkout.enter_postcode') }}');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!firstName || !address || !city || !phone) {
|
|
||||||
alert('{{ __('checkout.fill_required_fields') }}');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function validatePickupForm() {
|
// Number formatting helper
|
||||||
const selectedStore = document.querySelector('input[name="store"]:checked');
|
function number_format(number, decimals, dec_point, thousands_sep) {
|
||||||
|
number = (number + '').replace(/[^0-9+\-Ee.]/g, '');
|
||||||
|
var n = !isFinite(+number) ? 0 : +number;
|
||||||
|
var prec = !isFinite(+decimals) ? 0 : Math.abs(decimals);
|
||||||
|
var sep = (typeof thousands_sep === 'undefined') ? ',' : thousands_sep;
|
||||||
|
var dec = (typeof dec_point === 'undefined') ? '.' : dec_point;
|
||||||
|
|
||||||
if (!selectedStore) {
|
var s = '';
|
||||||
alert('{{ __('checkout.select_store') }}');
|
var toFixedFix = function(n, prec) {
|
||||||
return false;
|
var k = Math.pow(10, prec);
|
||||||
|
return '' + Math.round(n * k) / k;
|
||||||
|
};
|
||||||
|
|
||||||
|
s = (prec ? toFixedFix(n, prec) : '' + Math.round(n)).split('.');
|
||||||
|
if (thousands_sep) {
|
||||||
|
var re = /(-?\d+)(\d{3})/;
|
||||||
|
while (re.test(s[0])) {
|
||||||
|
s[0] = s[0].replace(re, '$1' + sep + '$2');
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Number formatting helper
|
if ((s[1] || '').length < prec) {
|
||||||
function number_format(number, decimals, dec_point, thousands_sep) {
|
s[1] = s[1] || '';
|
||||||
number = (number + '').replace(/[^0-9+\-Ee.]/g, '');
|
s[1] += new Array(prec - s[1].length + 1).join('0');
|
||||||
var n = !isFinite(+number) ? 0 : +number;
|
|
||||||
var prec = !isFinite(+decimals) ? 0 : Math.abs(decimals);
|
|
||||||
var sep = (typeof thousands_sep === 'undefined') ? ',' : thousands_sep;
|
|
||||||
var dec = (typeof dec_point === 'undefined') ? '.' : dec_point;
|
|
||||||
|
|
||||||
var s = '';
|
|
||||||
var toFixedFix = function(n, prec) {
|
|
||||||
var k = Math.pow(10, prec);
|
|
||||||
return '' + Math.round(n * k) / k;
|
|
||||||
};
|
|
||||||
|
|
||||||
s = (prec ? toFixedFix(n, prec) : '' + Math.round(n)).split('.');
|
|
||||||
if (thousands_sep) {
|
|
||||||
var re = /(-?\d+)(\d{3})/;
|
|
||||||
while (re.test(s[0])) {
|
|
||||||
s[0] = s[0].replace(re, '$1' + sep + '$2');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((s[1] || '').length < prec) {
|
|
||||||
s[1] = s[1] || '';
|
|
||||||
s[1] += new Array(prec - s[1].length + 1).join('0');
|
|
||||||
}
|
|
||||||
|
|
||||||
return s.join(dec);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return s.join(dec);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
@endsection
|
@endsection
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,13 @@
|
||||||
<div class="container py-5">
|
<div class="container py-5">
|
||||||
<div class="row pt-1 pt-sm-3 pt-lg-4 pb-2 pb-md-3 pb-lg-4 pb-xl-5">
|
<div class="row pt-1 pt-sm-3 pt-lg-4 pb-2 pb-md-3 pb-lg-4 pb-xl-5">
|
||||||
|
|
||||||
|
{{-- show error message --}}
|
||||||
|
@if(session('error'))
|
||||||
|
<div class="alert alert-danger">
|
||||||
|
{{ session('error') }}
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
<!-- Delivery info (Step 1) -->
|
<!-- Delivery info (Step 1) -->
|
||||||
<div class="col-lg-8 col-xl-7 mb-5 mb-lg-0">
|
<div class="col-lg-8 col-xl-7 mb-5 mb-lg-0">
|
||||||
<div class="d-flex flex-column gap-5 pe-lg-4 pe-xl-0">
|
<div class="d-flex flex-column gap-5 pe-lg-4 pe-xl-0">
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue