Compare commits

...

2 Commits

Author SHA1 Message Date
Bayu Lukman Yusuf b0eda5e389 profile 2026-01-19 14:38:16 +07:00
Bayu Lukman Yusuf b8bc23ee1f profile page 2026-01-19 09:07:01 +07:00
17 changed files with 799 additions and 283 deletions

View File

@ -120,6 +120,8 @@ class LoginEmailController extends Controller
'device' => 'web',
]);
return redirect()->route('home')->with('success', __('otp.login_success'));
} catch (\Illuminate\Validation\ValidationException $e) {

View File

@ -0,0 +1,123 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Helpers\AutoNumbering;
use App\Http\Controllers\Controller;
use App\Models\Customer;
use Exception;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use Intervention\Image\Drivers\Imagick\Driver;
use Intervention\Image\ImageManager;
class ProfileController extends Controller
{
public function index(Request $request)
{
if (! auth()->check()) {
return redirect()->route('login');
}
return view('account.info');
}
public function update(Request $request)
{
try {
$request->validate([
'name' => 'required|string|max:255',
'birth_date' => 'nullable|date',
'email' => 'required|email|max:255',
'phone' => 'required|string|max:255',
'photo' => 'required|image|mimes:jpg,jpeg,png,webp|max:2048',
]);
$user = auth()->user();
$user->name = $request->name;
$user->email = $request->email;
$user->phone = $request->phone;
// Handle avatar upload
if ($request->hasFile('photo')) {
$ext = $request->file('photo')->extension();
$filename = $request->file('photo')->storeAs("profile", $user->id.".".$ext, "public");
$user->photo = $filename;
}
$user->save();
$customer = $user->customer;
if ($user->customer == null) {
$customer = new Customer;
$autoNumbering = new AutoNumbering([
'type' => 'CUST',
'prefix' => 'CAPP',
'location_id' => 0,
'pad' => 9,
]);
do {
$number = $autoNumbering->getCurrent();
$count = Customer::where('number', $number)->count();
} while ($count > 0);
$customer->number = $number;
$customer->user_id = $user->id;
}
if ($request->name){
$customer->name = $request->name;
}
if ($request->email) {
$customer->email = $request->email;
}
if ($request->phone) {
$customer->phone = $request->phone;
}
if ($request->birth_date != null) {
$customer->date_of_birth = $request->birth_date;
}
$customer->save();
return back()->with('success', 'Profile updated successfully!');
} catch (Exception $e) {
Log::error($e);
return back()->with('error', $e->getMessage());
}
}
public function updatePassword(Request $request)
{
try {
$request->validate([
'current_password' => 'required|string',
'password' => 'required|string|min:8|confirmed',
]);
$user = auth()->user();
// Verify current password
if (!Hash::check($request->current_password, $user->password)) {
return back()->with('error', 'Current password is incorrect.');
}
$user->password = bcrypt($request->password);
$user->save();
return back()->with('success', 'Password updated successfully!');
} catch (Exception $e) {
return back()->with('error', $e->getMessage());
}
}
}

View File

@ -25,7 +25,7 @@ class User extends Authenticatable
'photo',
'fcm_token',
'phone',
'phone_verified_at'
'phone_verified_at',
];
/**
@ -50,4 +50,10 @@ class User extends Authenticatable
'password' => 'hashed',
];
}
public function customer()
{
return $this->hasOne(Customer::class);
}
}

39
lang/en/account_info.php Normal file
View File

@ -0,0 +1,39 @@
<?php
return [
'page_title' => 'Account - Personal Info',
'personal_info' => 'Personal info',
'basic_info' => 'Basic info',
'edit' => 'Edit',
'name' => 'Name',
'date_of_birth' => 'Date of birth',
'choose_date' => 'Choose date',
'please_enter_first_name' => 'Please enter your first name!',
'save_changes' => 'Save changes',
'close' => 'Close',
'contact' => 'Contact',
'email_address' => 'Email address',
'please_enter_valid_email' => 'Please enter a valid email address!',
'phone_number' => 'Phone number',
'please_enter_phone_number' => 'Please enter your phone number!',
'password' => 'Password',
'current_password' => 'Current password',
'enter_current_password' => 'Enter your current password',
'new_password' => 'New password',
'create_new_password' => 'Create new password',
'delete_account' => 'Delete account',
'delete_account_description' => 'When you delete your account, your public profile will be deactivated immediately. If you change your mind before the 14 days are up, sign in with your email and password, and we\'ll send you a link to reactivate your account.',
'not_set' => 'Not Set',
'verified' => 'Verified',
'date_format' => 'j F Y',
'language' => 'Language',
'select_language' => 'Select language',
'english' => 'English',
'french' => 'Français',
'german' => 'Deutsch',
'italian' => 'Italiano',
'confirm_password' => 'Confirm password',
'confirm_new_password' => 'Confirm new password',
'profile_photo' => 'Profile Photo',
'photo_requirements' => 'Recommended: Square image, at least 200x200px',
];

View File

@ -0,0 +1,17 @@
<?php
return [
'points' => 'Points',
'orders' => 'Orders',
'wishlist' => 'Wishlist',
'payment_methods' => 'Payment methods',
'my_reviews' => 'My reviews',
'manage_account' => 'Manage account',
'personal_info' => 'Personal info',
'addresses' => 'Addresses',
'notifications' => 'Notifications',
'customer_service' => 'Customer service',
'help_center' => 'Help center',
'terms_and_conditions' => 'Terms and conditions',
'log_out' => 'Log out',
];

6
lang/en/locale.php Normal file
View File

@ -0,0 +1,6 @@
<?php
return [
'en' => 'English',
'id' => 'Indonesia',
];

39
lang/id/account_info.php Normal file
View File

@ -0,0 +1,39 @@
<?php
return [
'page_title' => 'Akun - Info Pribadi',
'personal_info' => 'Info Pribadi',
'basic_info' => 'Info Dasar',
'edit' => 'Edit',
'name' => 'Nama',
'date_of_birth' => 'Tanggal Lahir',
'choose_date' => 'Pilih Tanggal',
'please_enter_first_name' => 'Silakan masukkan nama depan Anda!',
'save_changes' => 'Simpan Perubahan',
'close' => 'Tutup',
'contact' => 'Kontak',
'email_address' => 'Alamat Email',
'please_enter_valid_email' => 'Silakan masukkan alamat email yang valid!',
'phone_number' => 'Nomor Telepon',
'please_enter_phone_number' => 'Silakan masukkan nomor telepon Anda!',
'password' => 'Kata Sandi',
'current_password' => 'Kata Sandi Saat Ini',
'enter_current_password' => 'Masukkan kata sandi saat ini',
'new_password' => 'Kata Sandi Baru',
'create_new_password' => 'Buat kata sandi baru',
'delete_account' => 'Hapus Akun',
'delete_account_description' => 'Ketika Anda menghapus akun, profil publik Anda akan dinonaktifkan segera. Jika Anda berubah pikiran sebelum 14 hari berakhir, masuk dengan email dan kata sandi Anda, dan kami akan mengirimkan tautan untuk mengaktifkan kembali akun Anda.',
'not_set' => 'Belum Diatur',
'verified' => 'Terverifikasi',
'date_format' => 'j F Y',
'language' => 'Bahasa',
'select_language' => 'Pilih Bahasa',
'english' => 'Inggris',
'french' => 'Prancis',
'german' => 'Jerman',
'italian' => 'Italia',
'confirm_password' => 'Konfirmasi Kata Sandi',
'confirm_new_password' => 'Konfirmasi Kata Sandi Baru',
'profile_photo' => 'Foto Profil',
'photo_requirements' => 'Direkomendasikan: Gambar persegi, minimal 200x200px',
];

View File

@ -0,0 +1,17 @@
<?php
return [
'points' => 'Poin',
'orders' => 'Pesanan',
'wishlist' => 'Daftar Keinginan',
'payment_methods' => 'Metode Pembayaran',
'my_reviews' => 'Ulasan Saya',
'manage_account' => 'Kelola Akun',
'personal_info' => 'Info Pribadi',
'addresses' => 'Alamat',
'notifications' => 'Notifikasi',
'customer_service' => 'Layanan Pelanggan',
'help_center' => 'Pusat Bantuan',
'terms_and_conditions' => 'Syarat dan Ketentuan',
'log_out' => 'Keluar',
];

6
lang/id/locale.php Normal file
View File

@ -0,0 +1,6 @@
<?php
return [
'en' => 'English',
'id' => 'Indonesia',
];

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

View File

@ -1,175 +1,230 @@
@extends('layouts.account', ['title' => 'Account - Personal Info'])
@extends('layouts.account', ['title' => __('account_info.page_title')])
@section('content')
<!-- Personal info content -->
<div class="col-lg-9">
<div class="ps-lg-3 ps-xl-0">
<!-- Page title -->
<h1 class="h2 mb-1 mb-sm-2">Personal info</h1>
<h1 class="h2 mb-1 mb-sm-2">{{ __('account_info.personal_info') }}</h1>
<!-- Basic info -->
<div class="border-bottom py-4">
<div class="nav flex-nowrap align-items-center justify-content-between pb-1 mb-3">
<h2 class="h6 mb-0">Basic info</h2>
<h2 class="h6 mb-0">{{ __('account_info.basic_info') }}</h2>
<a aria-controls="basicInfoPreview basicInfoEdit" aria-expanded="false"
class="nav-link hiding-collapse-toggle text-decoration-underline p-0 collapsed"
data-bs-toggle="collapse" href=".basic-info">Edit</a>
data-bs-toggle="collapse" href=".basic-info">{{ __('account_info.edit') }}</a>
</div>
<div class="collapse basic-info show" id="basicInfoPreview">
<div class="collapse basic-info @if(session('error') || $errors->has('name') || $errors->has('email') || $errors->has('phone') || $errors->has('birth_date') || $errors->has('photo')) @else show @endif" id="basicInfoPreview">
<ul class="list-unstyled fs-sm m-0">
<li>Susan Gardner</li>
<li>May 12, 1996</li>
<li>English</li>
<li>{{ auth()->user()->name }}</li>
<li>{{ auth()->user()->email }}</li>
{{-- <li>English</li> --}}
</ul>
</div>
<div class="collapse basic-info" id="basicInfoEdit">
<form class="row g-3 g-sm-4 needs-validation" novalidate="">
<div class="col-sm-6">
<label class="form-label" for="fn">First name</label>
<div class="collapse basic-info @if(session('error') || $errors->has('name') || $errors->has('email') || $errors->has('phone') || $errors->has('birth_date') || $errors->has('photo')) show @endif" id="basicInfoEdit">
<form class="row g-3 g-sm-4 needs-validation" novalidate="" enctype="multipart/form-data" method="POST" action="{{ route('profile.update') }}">
@csrf
@if(session('error'))
<div class="alert alert-danger" role="alert">
{{ session('error') }}
</div>
@endif
<div class="col-sm-12">
<label class="form-label">{{ __('account_info.profile_photo') }}</label>
<div class="d-flex align-items-center gap-4">
<div class="position-relative">
<input class="form-control" id="fn" required="" type="text" value="Susan" />
<div class="invalid-feedback">Please enter your first name!</div>
<div class="avatar avatar-lg" style="aspect-ratio: 1/1; overflow: hidden; max-width: 300px; width: 100%; height: auto;">
@if(auth()->user()->photo)
<img src="{{ asset('storage/' . auth()->user()->photo) }}" alt="{{ auth()->user()->name }}" class="avatar-img rounded-circle" style="width: 100%; height: 100%; object-fit: cover;" onerror="this.src='{{ asset('img/photo-placeholder.png') }}'">
@else
<img src="{{ asset('img/photo-placeholder.png') }}" alt="{{ auth()->user()->name }}" class="avatar-img rounded-circle" style="width: 100%; height: 100%; object-fit: cover;">
@endif
</div>
<label for="photo-upload" class="btn btn-sm btn-primary position-absolute bottom-0 end-0 rounded-circle" style="width: 32px; height: 32px; padding: 0;">
<i class="ci-camera" style="font-size: 14px;"></i>
</label>
<input type="file" id="photo-upload" name="photo" class="d-none" accept="image/*" onchange="previewAvatar(event)">
</div>
<div class="flex-grow-1">
<p class="fs-sm text-muted mb-1">{{ __('account_info.photo_requirements') }}</p>
<p class="fs-sm text-muted mb-0">JPG, PNG or GIF (Max 2MB)</p>
</div>
</div>
</div>
<div class="col-sm-6">
<label class="form-label" for="ln">Last name</label>
<label class="form-label" for="fn">{{ __('account_info.name') }}</label>
<div class="position-relative">
<input class="form-control" id="ln" required="" type="text" value="Gardner" />
<div class="invalid-feedback">Please enter your last name!</div>
<input class="form-control" id="fn" name="name" required="" type="text"
value="{{ auth()->user()->name }}" />
<div class="invalid-feedback">{{ __('account_info.please_enter_first_name') }}</div>
</div>
</div>
<div class="col-sm-6">
<label class="form-label" for="birthdate">Date of birth</label>
<label class="form-label" for="email">{{ __('account_info.email_address') }}</label>
<div class="position-relative">
<input class="form-control" id="email" name="email" required="" type="email"
value="{{ auth()->user()->email ?? __('account_info.not_set') }}" />
<div class="invalid-feedback">{{ __('account_info.please_enter_valid_email') }}</div>
</div>
</div>
<div class="col-sm-6">
<label class="form-label" for="phone">{{ __('account_info.phone_number') }}</label>
<div class="position-relative">
<input class="form-control"
id="phone" name="phone" placeholder="" required="" type="text"
value="{{ auth()->user()->phone ?? __('account_info.not_set') }}" />
<div class="invalid-feedback">{{ __('account_info.please_enter_phone_number') }}</div>
</div>
</div>
<div class="col-sm-6">
<label class="form-label" for="birthdate">{{ __('account_info.date_of_birth') }}</label>
<div class="position-relative">
<input class="form-control form-icon-end"
name="birth_date"
data-datepicker='{
"dateFormat": "F j, Y",
"defaultDate": "May 15, 1996"
"dateFormat": "Y-m-d",
"defaultDate": "{{ auth()->user()->customer->date_of_birth != null ? date('Y-m-d', strtotime(auth()->user()->customer->date_of_birth)) : '' }}"
}'
id="birthdate" placeholder="Choose date" type="text" />
placeholder="{{ __('account_info.choose_date') }}" type="text" />
<i class="ci-calendar position-absolute top-50 end-0 translate-middle-y me-3"></i>
</div>
</div>
<div class="col-sm-6">
<label class="form-label">Language</label>
{{-- <div class="col-sm-6">
<label class="form-label">{{ __('account_info.language') }}</label>
<select class="form-select"
data-select='{
"placeholderValue": "Select language",
"placeholderValue": "{{ __('account_info.select_language') }}",
"choices": [
{
"value": "",
"label": "Select language",
"label": "{{ __('account_info.select_language') }}",
"placeholder": true
},
{
"value": "English",
"label": "&lt;div class=\"d-flex align-items-center\"&gt;&lt;img src=\"/img/flags/en-us.png\" class=\"flex-shrink-0 me-2\" width=\"20\" alt=\"English\"&gt; English&lt;/div&gt;",
"label": "&lt;div class=\"d-flex align-items-center\"&gt;&lt;img src=\"/img/flags/en-us.png\" class=\"flex-shrink-0 me-2\" width=\"20\" alt=\"English\"&gt; {{ __('account_info.english') }}&lt;/div&gt;",
"selected": true
},
{
"value": "Français",
"label": "&lt;div class=\"d-flex align-items-center\"&gt;&lt;img src=\"/img/flags/fr.png\" class=\"flex-shrink-0 me-2\" width=\"20\" alt=\"Français\"&gt; Français&lt;/div&gt;"
"label": "&lt;div class=\"d-flex align-items-center\"&gt;&lt;img src=\"/img/flags/fr.png\" class=\"flex-shrink-0 me-2\" width=\"20\" alt=\"Français\"&gt; {{ __('account_info.french') }}&lt;/div&gt;"
},
{
"value": "Deutsch",
"label": "&lt;div class=\"d-flex align-items-center\"&gt;&lt;img src=\"/img/flags/de.png\" class=\"flex-shrink-0 me-2\" width=\"20\" alt=\"Deutsch\"&gt; Deutsch&lt;/div&gt;"
"label": "&lt;div class=\"d-flex align-items-center\"&gt;&lt;img src=\"/img/flags/de.png\" class=\"flex-shrink-0 me-2\" width=\"20\" alt=\"Deutsch\"&gt; {{ __('account_info.german') }}&lt;/div&gt;"
},
{
"value": "Italiano",
"label": "&lt;div class=\"d-flex align-items-center\"&gt;&lt;img src=\"/img/flags/it.png\" class=\"flex-shrink-0 me-2\" width=\"20\" alt=\"Italiano\"&gt; Italiano&lt;/div&gt;"
"label": "&lt;div class=\"d-flex align-items-center\"&gt;&lt;img src=\"/img/flags/it.png\" class=\"flex-shrink-0 me-2\" width=\"20\" alt=\"Italiano\"&gt; {{ __('account_info.italian') }}&lt;/div&gt;"
}
]
}'
data-select-template="true"></select>
</div>
</div> --}}
<div class="col-12">
<div class="d-flex gap-3 pt-2 pt-sm-0">
<button class="btn btn-primary" type="submit">Save changes</button>
<button class="btn btn-primary" type="submit">{{ __('account_info.save_changes') }}</button>
<button aria-controls="basicInfoPreview basicInfoEdit" aria-expanded="true"
class="btn btn-secondary" data-bs-target=".basic-info" data-bs-toggle="collapse"
type="button">Close</button>
type="button">{{ __('account_info.close') }}</button>
</div>
</div>
</form>
</div>
</div>
<!-- Contact -->
<div class="border-bottom py-4">
{{-- <div class="border-bottom py-4">
<div class="nav flex-nowrap align-items-center justify-content-between pb-1 mb-3">
<div class="d-flex align-items-center gap-3 me-4">
<h2 class="h6 mb-0">Contact</h2>
<h2 class="h6 mb-0">{{ __('account_info.contact') }}</h2>
</div>
<a aria-controls="contactInfoPreview contactInfoEdit" aria-expanded="false"
class="nav-link hiding-collapse-toggle text-decoration-underline p-0 collapsed"
data-bs-toggle="collapse" href=".contact-info">Edit</a>
data-bs-toggle="collapse" href=".contact-info">{{ __('account_info.edit') }}</a>
</div>
<div class="collapse contact-info show" id="contactInfoPreview">
<ul class="list-unstyled fs-sm m-0">
<li class="mb-1">susan.gardner@email.com</li>
<li>+1 (805) 348 95 72 <span class="text-success ms-1">Verified</span></li>
<li class="mb-1">{{ auth()->user()->email }}</li>
<li>{{ auth()->user()->phone ?? __('account_info.not_set') }}<span class="text-success ms-1">{{ __('account_info.verified') }}</span></li>
</ul>
</div>
<div class="collapse contact-info" id="contactInfoEdit">
<form class="row g-3 g-sm-4 needs-validation" novalidate="">
<div class="col-sm-6">
<label class="form-label" for="email">Email address</label>
<div class="position-relative">
<input class="form-control" id="email" required="" type="email"
value="susan.gardner@email.com" />
<div class="invalid-feedback">Please enter a valid email address!</div>
</div>
</div>
<div class="col-sm-6">
<label class="form-label" for="phone">Phone number</label>
<div class="position-relative">
<input class="form-control"
data-input-format='{"numericOnly": true, "delimiters": ["+1 (", ")", " "], "blocks": [0, 3, 0, 3, 2, 2]}'
id="phone" placeholder="+1 (___) ___ __ __" required="" type="text"
value="+1 (805) 348 95 72" />
<div class="invalid-feedback">Please enter your phone number!</div>
</div>
</div>
<div class="col-12">
<div class="d-flex gap-3 pt-2 pt-sm-0">
<button class="btn btn-primary" type="submit">Save changes</button>
<button class="btn btn-primary" type="submit">{{ __('account_info.save_changes') }}</button>
<button aria-controls="contactInfoPreview contactInfoEdit" aria-expanded="true"
class="btn btn-secondary" data-bs-target=".contact-info" data-bs-toggle="collapse"
type="button">Close</button>
type="button">{{ __('account_info.close') }}</button>
</div>
</div>
</form>
</div>
</div>
</div> --}}
<!-- Password -->
<div class="border-bottom py-4">
{{-- <div class="border-bottom py-4">
<div class="nav flex-nowrap align-items-center justify-content-between pb-1 mb-3">
<div class="d-flex align-items-center gap-3 me-4">
<h2 class="h6 mb-0">Password</h2>
<h2 class="h6 mb-0">{{ __('account_info.password') }}</h2>
</div>
<a aria-controls="passChangePreview passChangeEdit" aria-expanded="false"
class="nav-link hiding-collapse-toggle text-decoration-underline p-0 collapsed"
data-bs-toggle="collapse" href=".password-change">Edit</a>
data-bs-toggle="collapse" href=".password-change">{{ __('account_info.edit') }}</a>
</div>
<div class="collapse password-change show" id="passChangePreview">
<div class="collapse password-change @if(session('error') || $errors->has('current_password') || $errors->has('password') || $errors->has('password_confirmation')) @else show @endif" id="passChangePreview">
<ul class="list-unstyled fs-sm m-0">
<li>**************</li>
</ul>
</div>
<div class="collapse password-change" id="passChangeEdit">
<form class="row g-3 g-sm-4 needs-validation" novalidate="">
<div class="collapse password-change @if(session('error') || $errors->has('current_password') || $errors->has('password') || $errors->has('password_confirmation')) show @endif" id="passChangeEdit">
<form class="row g-3 g-sm-4 needs-validation" novalidate="" method="POST" action="{{ route('profile.password.update') }}">
@csrf
@method('PUT')
@if(session('error'))
<div class="alert alert-danger" role="alert">
{{ session('error') }}
</div>
@endif
@if(session('success'))
<div class="alert alert-success" role="alert">
{{ session('success') }}
</div>
@endif
<div class="col-sm-6">
<label class="form-label" for="current-password">Current password</label>
<label class="form-label" for="current-password">{{ __('account_info.current_password') }}</label>
<div class="password-toggle">
<input class="form-control" id="current-password"
placeholder="Enter your current password" required="" type="password" />
<input class="form-control" id="current-password" name="current_password"
placeholder="{{ __('account_info.enter_current_password') }}" required="" type="password" />
<label aria-label="Show/hide password" class="password-toggle-button">
<input class="btn-check" type="checkbox" />
</label>
</div>
</div>
<div class="col-sm-6">
<label class="form-label" for="new-password">New password</label>
<label class="form-label" for="new-password">{{ __('account_info.new_password') }}</label>
<div class="password-toggle">
<input class="form-control" id="new-password" placeholder="Create new password"
<input class="form-control" id="new-password" name="password" placeholder="{{ __('account_info.create_new_password') }}"
required="" type="password" />
<label aria-label="Show/hide password" class="password-toggle-button">
<input class="btn-check" type="checkbox" />
</label>
</div>
</div>
<div class="col-sm-6">
<label class="form-label" for="confirm-password">{{ __('account_info.confirm_password') }}</label>
<div class="password-toggle">
<input class="form-control" id="confirm-password" name="password_confirmation" placeholder="{{ __('account_info.confirm_new_password') }}"
required="" type="password" />
<label aria-label="Show/hide password" class="password-toggle-button">
<input class="btn-check" type="checkbox" />
@ -178,26 +233,53 @@
</div>
<div class="col-12">
<div class="d-flex gap-3 pt-2 pt-sm-0">
<button class="btn btn-primary" type="submit">Save changes</button>
<button class="btn btn-primary" type="submit">{{ __('account_info.save_changes') }}</button>
<button aria-controls="passChangePreview passChangeEdit" aria-expanded="true"
class="btn btn-secondary" data-bs-target=".password-change" data-bs-toggle="collapse"
type="button">Close</button>
type="button">{{ __('account_info.close') }}</button>
</div>
</div>
</form>
</div>
</div>
</div> --}}
<!-- Delete account -->
<div class="pt-3 mt-2 mt-sm-3">
<h2 class="h6">Delete account</h2>
<p class="fs-sm">When you delete your account, your public profile will be deactivated
immediately. If you change your mind before the 14 days are up, sign in with your email and
password, and we'll send you a link to reactivate your account.</p>
<a class="text-danger fs-sm fw-medium" href="#!">Delete account</a>
</div>
{{-- <div class="pt-3 mt-2 mt-sm-3">
<h2 class="h6">{{ __('account_info.delete_account') }}</h2>
<p class="fs-sm">{{ __('account_info.delete_account_description') }}</p>
<a class="text-danger fs-sm fw-medium" href="#!">{{ __('account_info.delete_account') }}</a>
</div> --}}
</div>
</div>
@endsection
@section('scripts')
<script>
function previewAvatar(event) {
const file = event.target.files[0];
if (file) {
// Check file size (2MB limit)
if (file.size > 2 * 1024 * 1024) {
alert('File size must be less than 2MB');
event.target.value = '';
return;
}
// Check file type
if (!file.type.match('image.*')) {
alert('Please select an image file');
event.target.value = '';
return;
}
const reader = new FileReader();
reader.onload = function(e) {
const avatarImg = document.querySelector('.avatar-img');
if (avatarImg) {
avatarImg.src = e.target.result;
}
}
reader.readAsDataURL(file);
}
}
</script>
@endsection

View File

@ -1,8 +1,8 @@
<!-- Language selector for sidebar/offcanvas -->
<div class="dropdown nav">
<a class="nav-link dropdown-toggle py-1 px-0" href="#" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false" aria-label="{{ __('languages.Country select') }}: {{ app()->getLocale() === 'en' ? 'USA' : 'Indonesia' }}">
<a class="nav-link dropdown-toggle py-1 px-0" href="#" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false" aria-label="{{ __('locale.' . app()->getLocale()) }}">
<div class="ratio ratio-1x1" style="width: 20px">
<img src="/img/flags/{{ app()->getLocale() === 'en' ? 'en-us' : 'id' }}.png" alt="{{ app()->getLocale() === 'en' ? 'USA' : 'Indonesia' }}">
<img src="/img/flags/{{ app()->getLocale() === 'en' ? 'en-us' : 'id' }}.png" alt="{{ __('locale.' . app()->getLocale()) }}">
</div>
</a>
<ul class="dropdown-menu fs-sm" style="--cz-dropdown-spacer: .5rem">

View File

@ -1,8 +1,8 @@
<!-- Country selector visible on screens > 768px wide (md breakpoint) -->
<div class="dropdown d-none d-md-block nav">
<a class="nav-link dropdown-toggle py-1 px-0" href="#" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false" aria-label="{{ __('messages.Country select') }}: {{ app()->getLocale() === 'en' ? 'USA' : 'Indonesia' }}">
<a class="nav-link dropdown-toggle py-1 px-0" href="#" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false" aria-label="{{ __('locale.' . session('locale')) }}">
<div class="ratio ratio-1x1" style="width: 20px">
<img src="/img/flags/{{ session('locale') === 'en' ? 'en-us' : 'id' }}.png" alt="{{ session('locale') === 'en' ? 'USA' : 'Indonesia' }}">
<img src="/img/flags/{{ session('locale') === 'en' ? 'en-us' : 'id' }}.png" alt="{{ __('locale.' . session('locale')) }}">
</div>
</a>
<ul class="dropdown-menu fs-sm" style="--cz-dropdown-spacer: .5rem">

View File

@ -235,7 +235,7 @@
<!-- Account button visible on screens > 768px wide (md breakpoint) -->
<a class="btn btn-icon btn-lg fs-lg btn-outline-secondary border-0 rounded-circle animate-shake d-none d-md-inline-flex"
href="{{ route('login') }}">
href="{{ auth()->check() ? route('profile') : route('login') }}">
<i class="ci-user animate-target"></i>
<span class="visually-hidden">{{ __('header.account') }}</span>
</a>

View File

@ -5,13 +5,21 @@
<!-- Header -->
<div class="offcanvas-header d-lg-block py-3 p-lg-0">
<div class="d-flex align-items-center">
<div class="h5 d-flex justify-content-center align-items-center flex-shrink-0 text-primary bg-primary-subtle lh-1 rounded-circle mb-0"
style="width: 3rem; height: 3rem">S</div>
<div class="flex-shrink-0 ms-3">
@if(auth()->user()->photo)
<img src="{{ asset('storage/' . auth()->user()->photo) }}" alt="{{ auth()->user()->name }}"
class="rounded-circle" style="width: 3rem; height: 3rem; object-fit: cover;"
onerror="this.src='{{ asset('img/photo-placeholder.png') }}'">
@else
<div class="h5 d-flex justify-content-center align-items-center text-primary bg-primary-subtle lh-1 rounded-circle mb-0"
style="width: 3rem; height: 3rem;">{{ auth()->user()->name ? substr(auth()->user()->name, 0, 1) : 'S' }}</div>
@endif
</div>
<div class="min-w-0 ps-3">
<h5 class="h6 mb-1">Susan Gardner</h5>
<h5 class="h6 mb-1">{{ auth()->user()->name }}</h5>
<div class="nav flex-nowrap text-nowrap min-w-0">
<a class="nav-link animate-underline text-body p-0" href="#bonusesModal" data-bs-toggle="modal">
<svg class="text-warning flex-shrink-0 me-2" xmlns="http://www.w3.org/2000/svg"
{{-- <svg class="text-warning flex-shrink-0 me-2" xmlns="http://www.w3.org/2000/svg"
width="16" height="16" fill="currentColor">
<path
d="M1.333 9.667H7.5V16h-5c-.64 0-1.167-.527-1.167-1.167V9.667zm13.334 0v5.167c0 .64-.527 1.167-1.167 1.167h-5V9.667h6.167zM0 5.833V7.5c0 .64.527 1.167 1.167 1.167h.167H7.5v-1-3H1.167C.527 4.667 0 5.193 0 5.833zm14.833-1.166H8.5v3 1h6.167.167C15.473 8.667 16 8.14 16 7.5V5.833c0-.64-.527-1.167-1.167-1.167z" />
@ -19,9 +27,11 @@
d="M8 5.363a.5.5 0 0 1-.495-.573C7.752 3.123 9.054-.03 12.219-.03c1.807.001 2.447.977 2.447 1.813 0 1.486-2.069 3.58-6.667 3.58zM12.219.971c-2.388 0-3.295 2.27-3.595 3.377 1.884-.088 3.072-.565 3.756-.971.949-.563 1.287-1.193 1.287-1.595 0-.599-.747-.811-1.447-.811z" />
<path
d="M8.001 5.363c-4.598 0-6.667-2.094-6.667-3.58 0-.836.641-1.812 2.448-1.812 3.165 0 4.467 3.153 4.713 4.819a.5.5 0 0 1-.495.573zM3.782.971c-.7 0-1.448.213-1.448.812 0 .851 1.489 2.403 5.042 2.566C7.076 3.241 6.169.971 3.782.971z" />
</svg>
<span class="animate-target me-1">100 bonuses</span>
<span class="text-body fw-normal text-truncate">available</span>
</svg> --}}
<span
class="animate-target me-1">{{ number_format(auth()->user()->customer->point ?? 0, 0, ',', '.') }}
{{ __('account_sidebar.points') }}</span>
{{-- <span class="text-body fw-normal text-truncate">available</span> --}}
</a>
</div>
</div>
@ -33,59 +43,64 @@
<!-- Body (Navigation) -->
<div class="offcanvas-body d-block pt-2 pt-lg-4 pb-lg-0">
<nav class="list-group list-group-borderless">
<a class="list-group-item list-group-item-action d-flex align-items-center" href="{{ route('second', ['account', 'orders'])}}">
<a class="list-group-item list-group-item-action d-flex align-items-center"
href="{{ route('second', ['account', 'orders']) }}">
<i class="ci-shopping-bag fs-base opacity-75 me-2"></i>
Orders
{{ __('account_sidebar.orders') }}
<span class="badge bg-primary rounded-pill ms-auto">1</span>
</a>
<a class="list-group-item list-group-item-action d-flex align-items-center"
href="{{ route('second', ['account', 'wishlist']) }}">
<i class="ci-heart fs-base opacity-75 me-2"></i>
Wishlist
{{ __('account_sidebar.wishlist') }}
</a>
<a class="list-group-item list-group-item-action d-flex align-items-center" href="{{ route('second', ['account', 'payment'])}}">
<a class="list-group-item list-group-item-action d-flex align-items-center"
href="{{ route('second', ['account', 'payment']) }}">
<i class="ci-credit-card fs-base opacity-75 me-2"></i>
Payment methods
{{ __('account_sidebar.payment_methods') }}
</a>
<a class="list-group-item list-group-item-action d-flex align-items-center" href="{{ route('second', ['account', 'reviews'])}}">
<a class="list-group-item list-group-item-action d-flex align-items-center"
href="{{ route('second', ['account', 'reviews']) }}">
<i class="ci-star fs-base opacity-75 me-2"></i>
My reviews
{{ __('account_sidebar.my_reviews') }}
</a>
</nav>
<h6 class="pt-4 ps-2 ms-1">Manage account</h6>
<h6 class="pt-4 ps-2 ms-1">{{ __('account_sidebar.manage_account') }}</h6>
<nav class="list-group list-group-borderless">
<a class="list-group-item list-group-item-action d-flex align-items-center"
href="{{ route('second', ['account', 'info']) }}">
<i class="ci-user fs-base opacity-75 me-2"></i>
Personal info
{{ __('account_sidebar.personal_info') }}
</a>
<a class="list-group-item list-group-item-action d-flex align-items-center"
href="{{ route('second', ['account', 'addresses']) }}">
<i class="ci-map-pin fs-base opacity-75 me-2"></i>
Addresses
{{ __('account_sidebar.addresses') }}
</a>
<a class="list-group-item list-group-item-action d-flex align-items-center"
href="{{ route('second', ['account', 'notifications']) }}">
<i class="ci-bell fs-base opacity-75 mt-1 me-2"></i>
Notifications
{{ __('account_sidebar.notifications') }}
</a>
</nav>
<h6 class="pt-4 ps-2 ms-1">Customer service</h6>
<h6 class="pt-4 ps-2 ms-1">{{ __('account_sidebar.customer_service') }}</h6>
<nav class="list-group list-group-borderless">
<a class="list-group-item list-group-item-action d-flex align-items-center" href="{{ route('second', ['help', 'topics-v1']) }}">
<a class="list-group-item list-group-item-action d-flex align-items-center"
href="{{ route('second', ['help', 'topics-v1']) }}">
<i class="ci-help-circle fs-base opacity-75 me-2"></i>
Help center
{{ __('account_sidebar.help_center') }}
</a>
<a class="list-group-item list-group-item-action d-flex align-items-center"
href="{{ route('any', 'terms-and-conditions') }}">
<i class="ci-info fs-base opacity-75 me-2"></i>
Terms and conditions
{{ __('account_sidebar.terms_and_conditions') }}
</a>
</nav>
<nav class="list-group list-group-borderless pt-3">
<a class="list-group-item list-group-item-action d-flex align-items-center" href="{{ route('second', ['account', 'signin'])}}">
<a class="list-group-item list-group-item-action d-flex align-items-center"
href="{{ route('second', ['account', 'signin']) }}">
<i class="ci-log-out fs-base opacity-75 me-2"></i>
Log out
{{ __('account_sidebar.log_out') }}
</a>
</nav>
</div>

File diff suppressed because it is too large Load Diff

View File

@ -11,6 +11,7 @@ use App\Http\Controllers\LocaleController;
use App\Http\Controllers\ProductController;
use App\Http\Controllers\SearchController;
use App\Http\Controllers\ComponentController;
use App\Http\Controllers\Auth\ProfileController;
Route::group(['prefix' => '/dummy'], function () {
Route::get('', [RoutingController::class, 'index'])->name('root');
@ -67,3 +68,6 @@ Route::group(['prefix' => '/login/google'], function () {
Route::get('/callback', [App\Http\Controllers\Auth\GoogleController::class, 'handleGoogleCallback'])->name('login.google.callback');
});
Route::get('/profile', [ProfileController::class, 'index'])->name('profile');
Route::post('/profile', [ProfileController::class, 'update'])->name('profile.update');
Route::put('/profile/password', [ProfileController::class, 'updatePassword'])->name('profile.password.update');