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/LoginWaController.php b/app/Http/Controllers/Auth/LoginWaController.php index 3ea7de7..cf28252 100644 --- a/app/Http/Controllers/Auth/LoginWaController.php +++ b/app/Http/Controllers/Auth/LoginWaController.php @@ -62,6 +62,7 @@ class LoginWaController extends Controller return response()->json([ 'success' => true, 'message' => __('otp.sent'), + 'redirect' => route('login-phone.otp.view', ['identity' => $identity]), ]); } catch (\Exception $e) { @@ -78,6 +79,7 @@ class LoginWaController extends Controller { return view('account.otp', [ 'identity' => $identity, + 'type' => 'phone', ]); } 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/ContactController.php b/app/Http/Controllers/ContactController.php new file mode 100644 index 0000000..922559f --- /dev/null +++ b/app/Http/Controllers/ContactController.php @@ -0,0 +1,13 @@ +getList([]); + + // Render brand links HTML with images as swiper slides + $brandHtml = ''; + $currentBrandId = $request->input('current_brand'); + + foreach ($brands as $brand) { + $isActive = $currentBrandId == $brand->id; + // Only show brands that have images + if ($brand->image_url) { + $brandHtml .= ''; + $brandHtml .= ''.$brand->name.''; + $brandHtml .= ''; + } + } + + return response()->json([ + 'success' => true, + 'brands' => $brandHtml, + ]); + } + public function announcements(Request $request) { // Static announcements for now - can be moved to database later @@ -388,6 +413,70 @@ class ProductController extends Controller ]); } + public function populersJson(Request $request) + { + $type = $request->input('type', 'new'); + $limit = 6; + + $user = auth()->user(); + $userId = $user ? $user->id : 0; + [$location_id, $is_consignment] = Cache::remember('employee_user_'.$userId, 60 * 60 * 24, function () use ($user) { + + if ($user == null) { + return [10, false]; + } + + $employee = @$user->employee; + $location_id = @$employee->location_id; + $location = @$employee->location; + $is_consignment = (bool) @$location->is_consignment; + + return [$location_id, $is_consignment]; + + }); + + $productRepository = new ProductRepository; + + // Set parameters based on type + $params = [ + 'limit' => $limit, + 'location_id' => $location_id, + 'is_consignment' => $is_consignment, + ]; + + switch ($type) { + case 'new': + $params['sort'] = 'new'; + break; + case 'best_sellers': + $params['sort'] = 'best_sellers'; + break; + case 'special-offer': + $params['event'] = 'special-offer'; + break; + case 'top_rated': + $params['sort'] = 'random'; + break; + default: + $params['sort'] = 'new'; + break; + } + + $params['limit'] = 10; + + $products = $productRepository->getList($params); + + + $p = $products->map(function($row){ + $row->image_url = $row->image_url; + return $row; + }); + + return response()->json([ + 'data' =>$p, + ]); + } + public function detail($slug, Request $request, ProductRepository $productRepository) { 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/Catalog/BrandRepository.php b/app/Repositories/Catalog/BrandRepository.php index 7e8f0fc..320805d 100644 --- a/app/Repositories/Catalog/BrandRepository.php +++ b/app/Repositories/Catalog/BrandRepository.php @@ -27,7 +27,9 @@ class BrandRepository return Brand::whereIn('name', $ids)->orderBy('priority', 'desc') ->where('priority', '>', 0) - ->orderBy('name', 'asc')->get(); + ->orderBy('name', 'asc') + + ->get(); } return Brand::orderBy('priority', 'desc')->orderBy('name', 'asc') 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/app/View/Components/FooterBrands.php b/app/View/Components/FooterBrands.php new file mode 100644 index 0000000..16b6afb --- /dev/null +++ b/app/View/Components/FooterBrands.php @@ -0,0 +1,29 @@ +brands = $brandRepository->getList([]); + } + + /** + * Get the view / contents that represent the component. + */ + public function render(): View|Closure|string + { + return view('components.footer-brands', ['brands' => $this->brands]); + } +} diff --git a/app/View/Components/Layout/NavbarCategory.php b/app/View/Components/Layout/NavbarCategory.php index dba898f..1642d48 100644 --- a/app/View/Components/Layout/NavbarCategory.php +++ b/app/View/Components/Layout/NavbarCategory.php @@ -4,6 +4,8 @@ namespace App\View\Components\Layout; use App\Repositories\Catalog\CategoryRepository; use App\Repositories\Catalog\GenderRepository; +use App\Repositories\Catalog\BrandRepository; + use Closure; use Illuminate\Contracts\View\View; use Illuminate\View\Component; @@ -13,6 +15,7 @@ class NavbarCategory extends Component public $genders = []; public $categories = []; + public $brands = []; /** * Create a new component instance. */ @@ -22,13 +25,18 @@ class NavbarCategory extends Component $genderRepository = new GenderRepository(); $categoryRepository = new CategoryRepository(); + $brandsRepository = new BrandRepository(); $this->genders = $genderRepository->getList([]); + $this->genders = collect($this->genders)->chunk(7); + $this->categories = $categoryRepository->getList([]); // chunk $this->categories = collect($this->categories)->chunk(7); - + + $this->brands = $brandsRepository->getList([]); + $this->brands = collect($this->brands)->chunk(7); } /** 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/en/contact.php b/lang/en/contact.php new file mode 100644 index 0000000..02e305e --- /dev/null +++ b/lang/en/contact.php @@ -0,0 +1,64 @@ + 'Contact us', + 'subtitle' => 'Feel free to contact us and we will be happy to help you!', + + // Contact details section + 'location_title' => 'Location', + 'location_address' => 'Jalan Pintu Air Raya No.11 B,D,E,F,G RT.8/RW.1, Ps. Baru, Kecamatan Sawah Besar, Kota Jakarta Pusat, Daerah Khusus Ibukota Jakarta 10710', + 'location_link' => 'Get directions', + + 'phone_title' => 'Phone', + 'phone_number' => '(021) 3500703', + 'phone_whatsapp' => '0818-4343-01', + + 'email_title' => 'Email', + 'email_address' => 'sales@asiagolf.id', + + // Contact form + 'form_title' => 'Send us a message', + 'form_subtitle' => 'We usually respond within 24 hours', + + 'name_label' => 'Name', + 'name_placeholder' => 'Your name', + + 'email_label' => 'Email', + 'email_placeholder' => 'your.email@example.com', + + 'subject_label' => 'Subject', + 'subject_placeholder' => 'What is this about?', + + 'message_label' => 'Message', + 'message_placeholder' => 'Tell us more about your inquiry...', + + 'submit_button' => 'Send message', + + // Social media + 'social_title' => 'Follow us', + 'social_instagram' => 'Instagram', + 'social_facebook' => 'Facebook', + 'social_twitter' => 'Twitter', + + // Success messages + 'success_title' => 'Thank you for your message!', + 'success_message' => 'We have received your message and will get back to you soon.', + + // Error messages + 'error_title' => 'Something went wrong', + 'error_message' => 'Please try again later or contact us directly.', + + // Working hours + 'working_hours_title' => 'Working hours', + 'working_hours_mon_fri' => 'Mon - Fri 8:00 - 18:00', + 'working_hours_sat_sun' => 'Sat - Sun 10:00 - 16:00', + + // Support section + 'support_title' => 'Looking for support?', + 'support_message' => 'We might already have what you\'re looking for. See our FAQs or head to our dedicated Help Center.', + 'help_center_button' => 'Help center', + + // FAQ + 'faq_delivery_question' => 'How long will delivery take?', + 'faq_delivery_answer' => 'Delivery times vary based on your location and chosen shipping method. Generally, our standard delivery takes up to 5 days, while our Express Delivery ensures your order reaches you within 1 day. Please note that these times may be subject to occasional variations due to unforeseen circumstances, but we do our best to meet these estimates.', +]; \ No newline at end of file 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/lang/id/contact.php b/lang/id/contact.php new file mode 100644 index 0000000..69826bd --- /dev/null +++ b/lang/id/contact.php @@ -0,0 +1,64 @@ + 'Hubungi Kami', + 'subtitle' => 'Jangan ragu untuk menghubungi kami dan kami akan dengan senang hati membantu Anda!', + + // Contact details section + 'location_title' => 'Lokasi', + 'location_address' => 'Jalan Pintu Air Raya No.11 B,D,E,F,G RT.8/RW.1, Ps. Baru, Kecamatan Sawah Besar, Kota Jakarta Pusat, Daerah Khusus Ibukota Jakarta 10710', + 'location_link' => 'Petunjuk arah', + + 'phone_title' => 'Telepon', + 'phone_number' => '(021) 3500703', + 'phone_whatsapp' => '0818-4343-01', + + 'email_title' => 'Email', + 'email_address' => 'sales@asiagolf.id', + + // Contact form + 'form_title' => 'Kirim pesan', + 'form_subtitle' => 'Kami biasanya merespons dalam 24 jam', + + 'name_label' => 'Nama', + 'name_placeholder' => 'Nama Anda', + + 'email_label' => 'Email', + 'email_placeholder' => 'email.anda@example.com', + + 'subject_label' => 'Subjek', + 'subject_placeholder' => 'Tentang apa ini?', + + 'message_label' => 'Pesan', + 'message_placeholder' => 'Ceritakan lebih lanjut tentang pertanyaan Anda...', + + 'submit_button' => 'Kirim pesan', + + // Social media + 'social_title' => 'Ikuti kami', + 'social_instagram' => 'Instagram', + 'social_facebook' => 'Facebook', + 'social_twitter' => 'Twitter', + + // Success messages + 'success_title' => 'Terima kasih atas pesan Anda!', + 'success_message' => 'Kami telah menerima pesan Anda dan akan segera menghubungi Anda kembali.', + + // Error messages + 'error_title' => 'Terjadi kesalahan', + 'error_message' => 'Silakan coba lagi nanti atau hubungi kami langsung.', + + // Working hours + 'working_hours_title' => 'Jam operasional', + 'working_hours_mon_fri' => 'Sen - Jum 08:00 - 18:00', + 'working_hours_sat_sun' => 'Sab - Min 10:00 - 16:00', + + // Support section + 'support_title' => 'Mencari bantuan?', + 'support_message' => 'Kami mungkin sudah memiliki apa yang Anda cari. Lihat FAQ kami atau kunjungi ke Pusat Bantuan kami.', + 'help_center_button' => 'Pusat bantuan', + + // FAQ + 'faq_delivery_question' => 'Berapa lama pengiriman akan berlangsung?', + 'faq_delivery_answer' => 'Waktu pengiriman bervariasi tergantung pada lokasi Anda dan metode pengiriman yang dipilih. Umumnya, pengiriman standar kami memakan waktu hingga 5 hari, sementara Pengiriman Ekspres memastikan pesanan Anda sampai dalam 1 hari. Harap dicatat bahwa waktu ini dapat berubah karena keadaan yang tidak terduga, tetapi kami melakukan yang terbaik untuk memenuhi perkiraan ini.', +]; diff --git a/public/ig/607467049_3991986004432775_3154614208824352862_n.jpg b/public/ig/607467049_3991986004432775_3154614208824352862_n.jpg new file mode 100644 index 0000000..74c98c4 Binary files /dev/null and b/public/ig/607467049_3991986004432775_3154614208824352862_n.jpg differ diff --git a/public/ig/627126351_868515585958098_5234946979031253180_n.jpg b/public/ig/627126351_868515585958098_5234946979031253180_n.jpg new file mode 100644 index 0000000..9ab8f4f Binary files /dev/null and b/public/ig/627126351_868515585958098_5234946979031253180_n.jpg differ diff --git a/public/ig/637072850_18450267685100713_2841227512989064742_n.jpg b/public/ig/637072850_18450267685100713_2841227512989064742_n.jpg new file mode 100644 index 0000000..390ffc7 Binary files /dev/null and b/public/ig/637072850_18450267685100713_2841227512989064742_n.jpg differ diff --git a/public/ig/637270426_18450238369100713_2695956505063029899_n.jpg b/public/ig/637270426_18450238369100713_2695956505063029899_n.jpg new file mode 100644 index 0000000..7feaf66 Binary files /dev/null and b/public/ig/637270426_18450238369100713_2695956505063029899_n.jpg differ diff --git a/public/ig/639478537_1665736817945005_7744756753154487865_n.jpg b/public/ig/639478537_1665736817945005_7744756753154487865_n.jpg new file mode 100644 index 0000000..38c57de Binary files /dev/null and b/public/ig/639478537_1665736817945005_7744756753154487865_n.jpg differ 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/account/otp.blade.php b/resources/views/account/otp.blade.php index 960cc32..bd2722e 100644 --- a/resources/views/account/otp.blade.php +++ b/resources/views/account/otp.blade.php @@ -37,7 +37,7 @@
-
{{ __('otp.invalid_code') }}
diff --git a/resources/views/checkout/v1-cart.blade.php b/resources/views/checkout/v1-cart.blade.php index 4a84626..29d094a 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,11 +656,11 @@
- + --}} -
+ {{--
@@ -753,7 +728,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/footer-brands.blade.php b/resources/views/components/footer-brands.blade.php new file mode 100644 index 0000000..a8fffe8 --- /dev/null +++ b/resources/views/components/footer-brands.blade.php @@ -0,0 +1,13 @@ +@if($brands && $brands->count() > 0) +
+@endif \ No newline at end of file diff --git a/resources/views/components/home/brand-home-fashion-v1.blade.php b/resources/views/components/home/brand-home-fashion-v1.blade.php index ee921c6..28c8ed4 100644 --- a/resources/views/components/home/brand-home-fashion-v1.blade.php +++ b/resources/views/components/home/brand-home-fashion-v1.blade.php @@ -71,8 +71,8 @@ document.addEventListener('DOMContentLoaded', function() { // Show loading spinner loadingSpinner.classList.remove('d-none'); - // Make AJAX request for brands - fetch('{{ route("product.ajax.brands") }}') + // Make AJAX request for brands with images + fetch('{{ route("product.ajax.brands-with-images") }}') .then(response => response.json()) .then(data => { if (data.success && data.brands) { diff --git a/resources/views/components/home/home-popular-products.blade.php b/resources/views/components/home/home-popular-products.blade.php index d157481..0582e9f 100644 --- a/resources/views/components/home/home-popular-products.blade.php +++ b/resources/views/components/home/home-popular-products.blade.php @@ -113,16 +113,12 @@ document.addEventListener('DOMContentLoaded', function() { } // Make AJAX request for popular products - fetch('{{ route("product.ajax.populers") }}?type=best_sellers&limit=6') + fetch('{{ route("product.ajax.populers-json") }}?type=best_sellers&limit=6') .then(response => response.json()) .then(data => { - if (data.success && data.products) { - // Parse the HTML to extract individual product cards - const tempDiv = document.createElement('div'); - tempDiv.innerHTML = data.products; - - const productCards = tempDiv.querySelectorAll('.col'); - const productsArray = Array.from(productCards); + if ( data.data) { + // Handle JSON response directly + const productsArray = data.data; // Split products into two slides (3 products each) const slide1 = productsArray.slice(0, 3); @@ -139,16 +135,16 @@ document.addEventListener('DOMContentLoaded', function() { listDiv.className = 'd-flex flex-column gap-3 gap-lg-4'; slide.forEach(product => { - // Convert product card to list item format - const productImg = product.querySelector('img'); - const productLink = product.querySelector('a'); - const productPrice = product.querySelector('.h6, .price'); + // Convert product object to list item format + const productImg = product.image_url; + const productLink = product.name; + const productPrice = product.unit_price; const listItem = document.createElement('div'); listItem.className = 'd-flex align-items-center position-relative bg-body-tertiary rounded overflow-hidden animate-underline'; const img = document.createElement('img'); - img.src = productImg ? productImg.src : '/img/shop/fashion/thumbs/0' + (index * 3 + slide.indexOf(product) + 1) + '.png'; + img.src = productImg ? productImg : '/img/shop/fashion/thumbs/0' + (index * 3 + slide.indexOf(product) + 1) + '.png'; img.width = 110; img.alt = 'Thumbnail'; @@ -157,23 +153,22 @@ document.addEventListener('DOMContentLoaded', function() { const link = document.createElement('a'); link.className = 'nav-link text-dark-emphasis stretched-link w-100 min-w-0 p-0'; - link.href = productLink ? productLink.href : '#'; + link.href = product.slug ? '/product/' + product.slug : '#'; const span = document.createElement('span'); span.className = 'animate-target text-truncate'; - span.textContent = productLink ? productLink.textContent.trim() : 'Product Name'; + span.textContent = productLink ? productLink : 'Product Name'; const price = document.createElement('div'); price.className = 'h6 mb-0'; - price.textContent = productPrice ? productPrice.textContent : '$0.00'; + price.textContent = productPrice ? Number(productPrice).toLocaleString('id-ID', { style: 'currency', currency: 'IDR', minimumFractionDigits: 0 }) : '$0.00'; const name = document.createElement('div'); name.className = 'text-truncate mb-1'; - name.textContent = productLink ? productLink.textContent.trim() : 'Product Name'; + name.textContent = productLink ? productLink : 'Product Name'; link.appendChild(span); navDiv.appendChild(link); - navDiv.appendChild(name); navDiv.appendChild(price); listItem.appendChild(img); listItem.appendChild(navDiv); diff --git a/resources/views/components/home/instagram-feed.blade.php b/resources/views/components/home/instagram-feed.blade.php index ecea69e..b72f190 100644 --- a/resources/views/components/home/instagram-feed.blade.php +++ b/resources/views/components/home/instagram-feed.blade.php @@ -11,53 +11,53 @@
+ href="https://www.instagram.com/asiagolfindonesia/">
- Instagram image + Instagram image
+ href="https://www.instagram.com/asiagolfindonesia/">
- Instagram image + Instagram image
+ href="https://www.instagram.com/asiagolfindonesia/">
- Instagram image + Instagram image
+ href="https://www.instagram.com/asiagolfindonesia/">
- Instagram image + Instagram image
+ href="https://www.instagram.com/asiagolfindonesia/">
- Instagram image + Instagram image
diff --git a/resources/views/components/home/product-card.blade.php b/resources/views/components/home/product-card.blade.php index a53a00c..dee9213 100644 --- a/resources/views/components/home/product-card.blade.php +++ b/resources/views/components/home/product-card.blade.php @@ -16,20 +16,6 @@
- {{--
-
- XS - S - M - L - -
-
--}} - + - + --}} -
-
-

- - #AsiaGolf - -

-

Find more inspiration on our Instagram

-
-
- -
-
+ @include('layouts.partials.footer3') diff --git a/resources/views/layouts/base.blade.php b/resources/views/layouts/base.blade.php index 45f0b66..0d4c2b9 100644 --- a/resources/views/layouts/base.blade.php +++ b/resources/views/layouts/base.blade.php @@ -5,6 +5,7 @@ @include('layouts.partials/title-meta') @vite(['resources/js/theme-switcher.js']) + @include('layouts.partials/head-css') diff --git a/resources/views/layouts/partials/account-sidebar.blade.php b/resources/views/layouts/partials/account-sidebar.blade.php index 9fc14c3..ad3e980 100644 --- a/resources/views/layouts/partials/account-sidebar.blade.php +++ b/resources/views/layouts/partials/account-sidebar.blade.php @@ -1,22 +1,23 @@ diff --git a/resources/views/layouts/partials/footer2.blade.php b/resources/views/layouts/partials/footer2.blade.php index 83e2474..b7d8f9e 100644 --- a/resources/views/layouts/partials/footer2.blade.php +++ b/resources/views/layouts/partials/footer2.blade.php @@ -2,7 +2,7 @@
-
+ {{--
@@ -26,7 +26,7 @@

Spend less time searching and more time creating.

-
+
--}}
@@ -75,31 +75,14 @@
diff --git a/resources/views/layouts/partials/head-css.blade.php b/resources/views/layouts/partials/head-css.blade.php index 82e8ccd..b6f29ff 100644 --- a/resources/views/layouts/partials/head-css.blade.php +++ b/resources/views/layouts/partials/head-css.blade.php @@ -1,5 +1,4 @@ -@vite(['node_modules/choices.js/public/assets/styles/choices.min.css', 'node_modules/swiper/swiper-bundle.min.css', 'node_modules/glightbox/dist/css/glightbox.min.css', 'node_modules/simplebar/dist/simplebar.min.css', 'node_modules/flatpickr/dist/flatpickr.min.css', 'node_modules/nouislider/dist/nouislider.min.css', 'node_modules/img-comparison-slider/dist/styles.css']) +@vite(['node_modules/choices.js/public/assets/styles/choices.min.css', 'node_modules/swiper/swiper-bundle.min.css', 'node_modules/glightbox/dist/css/glightbox.min.css', 'node_modules/simplebar/dist/simplebar.min.css', 'node_modules/flatpickr/dist/flatpickr.min.css', 'node_modules/nouislider/dist/nouislider.min.css', 'node_modules/img-comparison-slider/dist/styles.css', 'node_modules/bootstrap/dist/css/bootstrap.min.css']) @vite(['resources/icons/cartzilla-icons.min.css']) @vite(['resources/scss/theme.scss']) - diff --git a/resources/views/shop/catalog-fashion.blade.php b/resources/views/shop/catalog-fashion.blade.php index d023add..247bc38 100644 --- a/resources/views/shop/catalog-fashion.blade.php +++ b/resources/views/shop/catalog-fashion.blade.php @@ -97,7 +97,7 @@
-
+ {{--

- + --}} {{--
diff --git a/resources/views/terms-and-conditions.blade.php b/resources/views/terms-and-conditions.blade.php index 52889aa..2f4f572 100644 --- a/resources/views/terms-and-conditions.blade.php +++ b/resources/views/terms-and-conditions.blade.php @@ -52,25 +52,41 @@

{{ __('tnc.contact_content') }}

-

{{ __('tnc.help_text') }} {{ __('tnc.help_link') }}

+

{{ __('tnc.help_text') }} {{ __('tnc.help_link') }}


diff --git a/routes/web.php b/routes/web.php index 1fb037e..0ef1c3b 100644 --- a/routes/web.php +++ b/routes/web.php @@ -17,6 +17,9 @@ use App\Http\Controllers\RoutingController; use App\Http\Controllers\SearchController; use App\Http\Controllers\TncController; use App\Http\Controllers\HelpController; +use App\Http\Controllers\VoucherEventController; +use App\Http\Controllers\ContactController; + use Illuminate\Support\Facades\Route; Route::group(['prefix' => '/dummy'], function () { @@ -38,8 +41,10 @@ Route::get('/', [HomeController::class, 'index'])->name('home'); Route::get('/products', [ProductController::class, 'index'])->name('product.index'); Route::get('/products/ajax', [ProductController::class, 'ajax'])->name('product.ajax'); Route::get('/products/ajax/populers', [ProductController::class, 'populers'])->name('product.ajax.populers'); +Route::get('/products/ajax/populers-json', [ProductController::class, 'populersJson'])->name('product.ajax.populers-json'); Route::get('/products/ajax/highlights', [ProductController::class, 'highlights'])->name('product.ajax.highlights'); Route::get('/products/ajax/brands', [ProductController::class, 'brands'])->name('product.ajax.brands'); +Route::get('/products/ajax/brands-with-images', [ProductController::class, 'brandsWithImages'])->name('product.ajax.brands-with-images'); Route::get('/products/ajax/categories', [ProductController::class, 'categories'])->name('product.ajax.categories'); Route::get('/products/ajax/genders', [ProductController::class, 'genders'])->name('product.ajax.genders'); Route::get('/products/ajax/announcements', [ProductController::class, 'announcements'])->name('product.ajax.announcements'); @@ -111,6 +116,10 @@ Route::middleware(['auth'])->prefix('/checkout')->group(function () { }); +Route::middleware(['auth'])->prefix('/voucher-events')->group(function () { + Route::post('/{voucherEvent}/redeem', [VoucherEventController::class, 'redeem'])->name('voucher-events.redeem'); +}); + Route::middleware(['auth'])->prefix('/orders')->group(function () { Route::get('/', [OrderController::class, 'index'])->name('orders'); @@ -118,3 +127,4 @@ Route::middleware(['auth'])->prefix('/orders')->group(function () { }); Route::get('/terms-and-conditions', [TncController::class, 'index'])->name('terms-and-conditions'); Route::get('/help', [HelpController::class, 'index'])->name('help'); +Route::get('/contact', [ContactController::class, 'index'])->name('contact'); diff --git a/vite.config.js b/vite.config.js index cc136f5..cb5de26 100644 --- a/vite.config.js +++ b/vite.config.js @@ -15,6 +15,7 @@ export default defineConfig({ "node_modules/simplebar/dist/simplebar.min.css", 'node_modules/flatpickr/dist/flatpickr.min.css', "node_modules/nouislider/dist/nouislider.min.css", + "node_modules/bootstrap/dist/css/bootstrap.min.css", //js "resources/js/theme-switcher.js",