diff --git a/app/Http/Controllers/Auth/AddressController.php b/app/Http/Controllers/Auth/AddressController.php index c6dc8d1..00c3b46 100644 --- a/app/Http/Controllers/Auth/AddressController.php +++ b/app/Http/Controllers/Auth/AddressController.php @@ -21,6 +21,7 @@ class AddressController extends Controller return [ 'id' => $address->id, 'label' => $address->label, + 'name' => $address->name, 'location' => $address->location, 'address' => $address->address, 'is_primary' => $address->is_primary, @@ -29,6 +30,8 @@ class AddressController extends Controller 'district_id' => $address->district_id, 'subdistrict_id' => $address->subdistrict_id, 'postal_code' => $address->postal_code, + 'latitude' => $address->latitude, + 'longitude' => $address->longitude, ]; }); @@ -82,25 +85,35 @@ class AddressController extends Controller public function update(Request $request, $id) { $request->validate([ + 'label' => 'required|string|max:255', + 'name' => 'required|string|max:255', + 'phone' => 'required|string|max:255', 'province_id' => 'required|exists:provinces,id', 'city_id' => 'required|exists:cities,id', 'district_id' => 'required|exists:districts,id', 'subdistrict_id' => 'required|exists:subdistricts,id', 'postal_code' => 'required|string|max:10', 'address' => 'required|string|max:255', + 'latitude' => 'required|numeric|between:-90,90', + 'longitude' => 'required|numeric|between:-180,180', 'is_primary' => 'boolean' ]); $address = auth()->user()->addresses()->findOrFail($id); $address->update([ + 'label' => $request->label, + 'name' => $request->name, + 'phone' => $request->phone, 'province_id' => $request->province_id, 'city_id' => $request->city_id, 'district_id' => $request->district_id, 'subdistrict_id' => $request->subdistrict_id, 'postal_code' => $request->postal_code, 'address' => $request->address, - 'is_primary' => $request->boolean('is_primary') + 'latitude' => $request->latitude, + 'longitude' => $request->longitude, + 'is_primary' => $request->is_primary ?? $address->is_primary, ]); // Update location names based on selected IDs @@ -133,12 +146,17 @@ class AddressController extends Controller public function store(Request $request) { $request->validate([ + 'label' => 'required|string|max:255', + 'name' => 'required|string|max:255', + 'phone' => 'required|string|max:255', 'province_id' => 'required|exists:provinces,id', 'city_id' => 'required|exists:cities,id', 'district_id' => 'required|exists:districts,id', 'subdistrict_id' => 'required|exists:subdistricts,id', - 'postal_code' => 'required|string|max:10', 'address' => 'required|string|max:255', + 'postal_code' => 'required|string|max:10', + 'latitude' => 'nullable|numeric|between:-90,90', + 'longitude' => 'nullable|numeric|between:-180,180', 'is_primary' => 'boolean' ]); @@ -149,18 +167,18 @@ class AddressController extends Controller $subdistrict = Subdistrict::find($request->subdistrict_id); $address = auth()->user()->addresses()->create([ - 'label' => 'Address ' . (auth()->user()->addresses()->count() + 1), + 'label' => $request->label, + 'name' => $request->name, + 'phone' => $request->phone, 'province_id' => $request->province_id, 'city_id' => $request->city_id, 'district_id' => $request->district_id, 'subdistrict_id' => $request->subdistrict_id, - 'province_name' => $province?->name, - 'regency_name' => $city?->name, - 'district_name' => $district?->name, - 'village_name' => $subdistrict?->name, - 'postal_code' => $request->postal_code, 'address' => $request->address, - 'is_primary' => $request->boolean('is_primary') + 'postal_code' => $request->postal_code, + 'latitude' => $request->latitude, + 'longitude' => $request->longitude, + 'is_primary' => $request->is_primary ?? false, ]); // If set as primary, unset other primary addresses diff --git a/app/Http/Controllers/Auth/ProfileController.php b/app/Http/Controllers/Auth/ProfileController.php index 0671d0d..eca5452 100644 --- a/app/Http/Controllers/Auth/ProfileController.php +++ b/app/Http/Controllers/Auth/ProfileController.php @@ -46,7 +46,7 @@ class ProfileController extends Controller if ($request->hasFile('photo')) { $ext = $request->file('photo')->extension(); $filename = $request->file('photo')->storeAs("profile", $user->id.".".$ext, "public"); - $user->photo = $filename; + $user->photo = asset('storage/' . $filename); } $user->save(); diff --git a/app/Http/Controllers/VoucherEventController.php b/app/Http/Controllers/VoucherEventController.php new file mode 100644 index 0000000..2dc23ce --- /dev/null +++ b/app/Http/Controllers/VoucherEventController.php @@ -0,0 +1,63 @@ +voucherEventRepository = $voucherEventRepository; + } + + /** + * Redeem a voucher event + * + * @param int $voucherEvent + * @param Request $request + * @return JsonResponse + */ + public function redeem($voucherEvent, Request $request): JsonResponse + { + try { + // Get the authenticated user + $user = auth()->user(); + + if (!$user) { + return response()->json([ + 'success' => false, + 'message' => 'User not authenticated' + ], 401); + } + + // Call the repository's redeem method + $result = $this->voucherEventRepository->redeem($voucherEvent, $user); + + if ($result['success']) { + return response()->json([ + 'success' => true, + 'message' => $result['message'] ?? 'Voucher redeemed successfully!', + 'data' => $result['data'] ?? null + ]); + } else { + return response()->json([ + 'success' => false, + 'message' => $result['message'] ?? 'Failed to redeem voucher' + ], 400); + } + + } catch (\Exception $e) { + Log::error($e); + return response()->json([ + 'success' => false, + 'message' => $e->getMessage() + ], 500); + } + } +} diff --git a/app/Models/User.php b/app/Models/User.php index 058b7d4..c8dd1a8 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -57,6 +57,7 @@ class User extends Authenticatable return $this->hasOne(Customer::class); } + public function addresses() { return $this->hasMany(Address::class); @@ -64,6 +65,6 @@ class User extends Authenticatable public function getPhotoUrlAttribute() { - return $this->photo ? env('WMS_ASSET_URL') . '/' . $this->photo : asset('img/photo-placeholder.png'); + return $this->photo ? (str_contains($this->photo, 'http') ? $this->photo : env('WMS_ASSET_URL') . '/' . $this->photo) : asset('img/photo-placeholder.png'); } } diff --git a/app/Repositories/Member/Auth/MemberAuthRepository.php b/app/Repositories/Member/Auth/MemberAuthRepository.php index 2499b69..03b7f3b 100644 --- a/app/Repositories/Member/Auth/MemberAuthRepository.php +++ b/app/Repositories/Member/Auth/MemberAuthRepository.php @@ -451,4 +451,5 @@ class MemberAuthRepository return $user; } + } diff --git a/app/Repositories/Member/Voucher/VoucherRepository.php b/app/Repositories/Member/Voucher/VoucherRepository.php index bc21a62..8950041 100644 --- a/app/Repositories/Member/Voucher/VoucherRepository.php +++ b/app/Repositories/Member/Voucher/VoucherRepository.php @@ -19,7 +19,7 @@ class VoucherRepository $model = Voucher::where('customer_id', (int) @$customer->id) ->where('used_at', null) ->where('expired_at','>=',Carbon::now()) - ->orderBy('created_at','desc')->paginate($request->limit); + ->orderBy('created_at','desc')->paginate($request->limit ?? 100); return $model; } diff --git a/app/Repositories/Member/VoucherEvent/VoucherEventRepository.php b/app/Repositories/Member/VoucherEvent/VoucherEventRepository.php index d1e6583..860914c 100644 --- a/app/Repositories/Member/VoucherEvent/VoucherEventRepository.php +++ b/app/Repositories/Member/VoucherEvent/VoucherEventRepository.php @@ -15,7 +15,7 @@ class VoucherEventRepository { public function getList($request) { - $model = VoucherEvent::where('redeem_point', '>', 0)->orderBy('created_at','desc')->paginate($request->limit); + $model = VoucherEvent::where('redeem_point', '>', 0)->orderBy('created_at','desc')->paginate($request->limit ?? 100); return $model; } @@ -114,7 +114,12 @@ class VoucherEventRepository $nominal = $voucher->nominal; } - $customer = Customer::where('user_id', auth()->user()->id)->firstOrFail(); + $customer = auth()->user()->customer ?? null; + if ($customer == null) { + throw ValidationException::withMessages([ + "customer" => "Lengkapi profil terlebih dahulu" + ]); + } $point = DB::select("SELECT SUM(point) as point FROM customer_points WHERE customer_id = ? ", [$customer->id]); $point = (float) @$point[0]->point; @@ -141,7 +146,7 @@ class VoucherEventRepository 'user_id' => auth()->user()->id ]); - $customer = Customer::where('user_id', auth()->user()->id)->firstOrFail(); + $customer = auth()->user()->customer; $this->redeemPoint($customer->id, $voucher->redeem_point, "Redeem point for ".$model->description, $model); diff --git a/app/View/Components/Checkout/PromoCode.php b/app/View/Components/Checkout/PromoCode.php new file mode 100644 index 0000000..ab8f7e2 --- /dev/null +++ b/app/View/Components/Checkout/PromoCode.php @@ -0,0 +1,39 @@ +voucher_events = $voucherEventRepository->getList([]); + $this->vouchers = $voucherRepository->getList([]); + } + + /** + * Get the view / contents that represent the component. + * + * @return View|string + */ + public function render() + { + return view('components.checkout.promo-code', [ + 'voucher_events' => $this->voucher_events, + 'vouchers' => $this->vouchers + ]); + } +} diff --git a/lang/en/addresses.php b/lang/en/addresses.php index 55e2c6e..7605f79 100644 --- a/lang/en/addresses.php +++ b/lang/en/addresses.php @@ -17,6 +17,14 @@ return [ 'select_village' => 'Select subdistrict...', 'zip_code' => 'ZIP code', 'address' => 'Address', + 'label' => 'Label', + 'name' => 'Name', + 'phone' => 'Phone', + 'please_enter_phone' => 'Please enter your phone number!', + 'latitude' => 'Latitude', + 'longitude' => 'Longitude', + 'please_enter_latitude' => 'Please enter your latitude!', + 'please_enter_longitude' => 'Please enter your longitude!', 'set_as_primary_address' => 'Set as primary address', 'save_changes' => 'Save changes', 'close' => 'Close', @@ -29,6 +37,8 @@ return [ 'please_select_village' => 'Please select your village!', 'please_enter_zip_code' => 'Please enter your ZIP code!', 'please_enter_address' => 'Please enter your address!', + 'please_enter_label' => 'Please enter your label!', + 'please_enter_name' => 'Please enter your name!', 'saving' => 'Saving', 'error_saving_address' => 'Error saving address', 'address_updated_successfully' => 'Address updated successfully', diff --git a/lang/en/checkout.php b/lang/en/checkout.php index d0d527c..fafb736 100644 --- a/lang/en/checkout.php +++ b/lang/en/checkout.php @@ -45,4 +45,15 @@ return [ 'pickup' => 'Pickup', 'pickup_ready' => 'Your order will be ready for pickup at the selected store', 'continue_to_payment' => 'Continue to Payment', + 'apply_promo_code' => 'Apply promo code', + 'enter_promo_code' => 'Enter promo code', + 'enter_valid_promo_code' => 'Enter a valid promo code!', + 'apply' => 'Apply', + 'available_vouchers' => 'Available Vouchers', + 'available_voucher_events' => 'Available Voucher Events', + 'points_required' => 'Points Required', + 'valid_period' => 'Valid Period', + 'terms_and_conditions' => 'Terms and Conditions', + 'close' => 'Close', + 'redeem' => 'Redeem', ]; diff --git a/lang/id/addresses.php b/lang/id/addresses.php index b8ff307..0f1f119 100644 --- a/lang/id/addresses.php +++ b/lang/id/addresses.php @@ -17,7 +17,15 @@ return [ 'select_village' => 'Pilih kelurahan/desa...', 'zip_code' => 'Kode Pos', 'address' => 'Alamat', - 'set_as_primary_address' => 'Jadikan alamat utama', + 'label' => 'Label', + 'name' => 'Nama', + 'phone' => 'Telepon', + 'please_enter_phone' => 'Silakan masukkan nomor telepon Anda!', + 'latitude' => 'Lintang', + 'longitude' => 'Bujur', + 'please_enter_latitude' => 'Silakan masukkan lintang Anda!', + 'please_enter_longitude' => 'Silakan masukkan bujur Anda!', + 'set_as_primary_address' => 'Jadikan sebagai alamat utama', 'save_changes' => 'Simpan perubahan', 'close' => 'Tutup', 'alternative_shipping_address' => 'Alamat Pengiriman Alternatif', @@ -29,6 +37,8 @@ return [ 'please_select_village' => 'Silakan pilih kelurahan/desa Anda!', 'please_enter_zip_code' => 'Silakan masukkan kode pos Anda!', 'please_enter_address' => 'Silakan masukkan alamat Anda!', + 'please_enter_label' => 'Silakan masukkan label Anda!', + 'please_enter_name' => 'Silakan masukkan nama Anda!', 'saving' => 'Menyimpan', 'error_saving_address' => 'Terjadi kesalahan saat menyimpan alamat', 'address_updated_successfully' => 'Alamat berhasil diperbarui', diff --git a/lang/id/checkout.php b/lang/id/checkout.php index 417d5f5..2020e48 100644 --- a/lang/id/checkout.php +++ b/lang/id/checkout.php @@ -45,4 +45,15 @@ return [ 'pickup' => 'Ambil di Toko', 'pickup_ready' => 'Pesanan Anda akan siap diambil di toko yang dipilih', 'continue_to_payment' => 'Lanjut ke Pembayaran', + 'apply_promo_code' => 'Gunakan kode promo', + 'enter_promo_code' => 'Masukkan kode promo', + 'enter_valid_promo_code' => 'Masukkan kode promo yang valid!', + 'apply' => 'Gunakan', + 'available_vouchers' => 'Voucher Tersedia', + 'available_voucher_events' => 'Event Voucher Tersedia', + 'points_required' => 'Poin Diperlukan', + 'valid_period' => 'Periode Berlaku', + 'terms_and_conditions' => 'Syarat dan Ketentuan', + 'close' => 'Tutup', + 'redeem' => 'Tukar', ]; diff --git a/resources/js/components/pwa.js b/resources/js/components/pwa.js index e707ce5..ec8132d 100644 --- a/resources/js/components/pwa.js +++ b/resources/js/components/pwa.js @@ -6,9 +6,18 @@ export default (() => { const htmlElement = document.documentElement - // Check the 'data-pwa' attribute of the HTML element + // Check if Bootstrap is loaded + const checkBootstrapLoaded = () => { + if (typeof bootstrap === 'undefined') { + console.error('Bootstrap is not loaded'); + return false; + } + return true; + }; + + // Check 'data-pwa' attribute of HTML element if (htmlElement.getAttribute('data-pwa') !== 'true') { - // Unregister the service worker if it's registered and 'data-pwa' is not 'true' + // Unregister service worker if it's registered and 'data-pwa' is not 'true' if ('serviceWorker' in navigator) { navigator.serviceWorker.getRegistrations().then((registrations) => { for (let registration of registrations) { @@ -19,6 +28,11 @@ export default (() => { return // Stop further execution to prevent PWA setup when not specified } + // Check Bootstrap before proceeding with any Bootstrap-dependent code + if (!checkBootstrapLoaded()) { + return; + } + // Settings const SETTINGS = { appName: 'AsiaGolf', @@ -171,9 +185,16 @@ export default (() => { // Get prompt instance const promptElement = document.getElementById(promptId) + + // Check if Bootstrap is loaded before using it + if (!checkBootstrapLoaded()) { + console.error('Cannot create PWA prompt: Bootstrap is not loaded'); + return; + } + /* eslint-disable no-undef */ const promptInstance = new bootstrap.Modal(promptElement, { - backdrop: 'static', // Optional: makes prompt not close when clicking outside + backdrop: false, // Disable backdrop to prevent layering issues keyboard: false, // Optional: makes prompt not close when pressing escape key }) /* eslint-enable no-undef */ diff --git a/resources/js/theme.js b/resources/js/theme.js index ba7a96b..a677e44 100644 --- a/resources/js/theme.js +++ b/resources/js/theme.js @@ -6,6 +6,8 @@ * @author Coderthemes * @version 1.0.0 */ +import 'bootstrap' + import 'img-comparison-slider' import 'simplebar' diff --git a/resources/views/account/addresses.blade.php b/resources/views/account/addresses.blade.php index 56831ff..45e9c1c 100644 --- a/resources/views/account/addresses.blade.php +++ b/resources/views/account/addresses.blade.php @@ -101,6 +101,32 @@ +
+
+ + +
{{ __('addresses.please_enter_label') }}
+
+
+ + + +
+
+ + +
{{ __('addresses.please_enter_name') }}
+
+
+ +
+
+ + +
{{ __('addresses.please_enter_phone') }}
+
+
+
@@ -140,17 +166,33 @@
{{ __('addresses.please_select_village') }}
-
+
- - + +
{{ __('addresses.please_enter_zip_code') }}
+ +
+
+ + +
{{ __('addresses.please_enter_latitude') }}
+
+
+ +
+
+ + +
{{ __('addresses.please_enter_longitude') }}
+
+
- +
{{ __('addresses.please_enter_address') }}
@@ -315,6 +357,7 @@
@@ -325,6 +368,32 @@ + +
+
+ + +
{{ __('addresses.please_enter_address') }}
+
+
+ +
+
+ + +
{{ __('addresses.please_enter_name') }}
+
+
+ +
+
+ + +
{{ __('addresses.please_enter_phone') }}
+
+
+ +
@@ -370,25 +439,41 @@
-
+
{{ __('addresses.please_enter_zip_code') }}
+ +
+
+ + +
{{ __('addresses.please_enter_latitude') }}
+
+
+ +
+
+ + +
{{ __('addresses.please_enter_longitude') }}
+
+
- +
{{ __('addresses.please_enter_address') }}
- +
@@ -942,6 +1027,22 @@ // Get CSRF token const tokenInput = form.querySelector('input[name="_token"]'); if (tokenInput) submitData._token = tokenInput.value; + + + // Get Name + const nameInput = isNewAddress ? + form.querySelector('#new-name') : form.querySelector('.edit-name'); + if (nameInput) submitData.name = nameInput.value; + + // Get Phone + const phoneInput = isNewAddress ? + form.querySelector('.new-phone') : form.querySelector('.edit-phone'); + if (phoneInput) submitData.phone = phoneInput.value; + + // Get Label + const labelInput = isNewAddress ? + form.querySelector('#new-label') : form.querySelector('.edit-label'); + if (labelInput) submitData.label = labelInput.value; // Get province ID const provinceSelect = isNewAddress ? @@ -968,9 +1069,19 @@ form.querySelector('#new-zip') : form.querySelector('.postal_code'); if (zipInput) submitData.postal_code = zipInput.value; - // Get address + // Get latitude + const latitudeInput = isNewAddress ? + form.querySelector('.new-latitude') : form.querySelector('.latitude'); + if (latitudeInput) submitData.latitude = latitudeInput.value; + + // Get longitude + const longitudeInput = isNewAddress ? + form.querySelector('.new-longitude') : form.querySelector('.longitude'); + if (longitudeInput) submitData.longitude = longitudeInput.value; + + // Get address const addressInput = isNewAddress ? - form.querySelector('#new-address') : form.querySelector('.zip_code'); + form.querySelector('#new-address') : form.querySelector('.address-input'); if (addressInput) submitData.address = addressInput.value; // Get primary checkbox diff --git a/resources/views/account/info.blade.php b/resources/views/account/info.blade.php index 3466b1b..820ea06 100644 --- a/resources/views/account/info.blade.php +++ b/resources/views/account/info.blade.php @@ -38,8 +38,9 @@
+ @if(auth()->user()->photo) - {{ auth()->user()->name }} + {{ auth()->user()->name }} @else {{ auth()->user()->name }} @endif diff --git a/resources/views/checkout/v1-cart.blade.php b/resources/views/checkout/v1-cart.blade.php index 4a84626..8e9c968 100644 --- a/resources/views/checkout/v1-cart.blade.php +++ b/resources/views/checkout/v1-cart.blade.php @@ -47,8 +47,7 @@ Product - Price + Quantity {{ $cart->name ?? 'Product' }} + width="80" alt="{{ $cart->name ?? 'Product' }}"> + + - - - Rp {{ number_format($cart->itemVariant->display_price ?? 0, 0, ',', '.') }} - +
-
-
-

- -

-
-
-
-
- -
Enter a valid promo - code! -
-
- -
-
-
-
-
+
@endif @@ -251,7 +226,7 @@ -
+ {{--

Trending products

@@ -681,7 +656,7 @@
- + --}} @@ -804,7 +779,7 @@ - @include('layouts.partials/footer') + @include('layouts.partials/footer2') @endsection @section('scripts') diff --git a/resources/views/components/checkout/promo-code.blade.php b/resources/views/components/checkout/promo-code.blade.php new file mode 100644 index 0000000..0f9abd4 --- /dev/null +++ b/resources/views/components/checkout/promo-code.blade.php @@ -0,0 +1,264 @@ +
+
+

+ +

+ +
+ +
+ + {{-- Promo Code Form --}} +
+
+ + +
+ {{ __('checkout.enter_valid_promo_code') }} +
+
+ + +
+ + + {{-- List Voucher --}} + @if($vouchers && count($vouchers) > 0) +
+
+ {{ __('checkout.available_vouchers') }} +
+ +
+ @foreach($vouchers as $voucher) + + {{ $voucher->code }} + + @endforeach +
+
+ @endif + + + {{-- List Voucher Events --}} + @if($voucher_events && count($voucher_events) > 0) +
+
+ {{ __('checkout.available_voucher_events') }} +
+ +
+ @foreach($voucher_events as $voucher_event) + + {{ $voucher_event->name ?? '-' }} + + @endforeach +
+
+ @endif + +
+
+
+
+ + + +{{-- SINGLE REUSABLE MODAL --}} + + + + + \ No newline at end of file diff --git a/resources/views/components/layout/header.blade.php b/resources/views/components/layout/header.blade.php index dea96e2..a9d04b9 100644 --- a/resources/views/components/layout/header.blade.php +++ b/resources/views/components/layout/header.blade.php @@ -312,7 +312,7 @@