account pages: login, order history, order details, account details, wishlist

This commit is contained in:
elvira
2026-09-24 17:27:58 +03:00
parent 580adac33a
commit cf3681260b
42 changed files with 1972 additions and 15 deletions
@@ -0,0 +1,136 @@
<?php
namespace App\Http\Controllers\Account;
use App\Http\Controllers\Controller;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Validation\Rule;
use Illuminate\View\View;
use Lunar\Models\Country;
use Lunar\Models\State;
use Modules\Core\Customer\Services\CustomerAccountService;
use Modules\Core\Privacy\Services\PrivacyService;
/**
* The profile page: name, invoice details, the one address, and account
* deletion. The customer keeps a single address, flagged as both the
* shipping and billing default, so allowing several later is a UI change
* only (the data is already Lunar's own addresses table).
*/
class AccountController extends Controller
{
// Single-country store, same as CheckoutController::STORE_COUNTRY_ISO3.
private const STORE_COUNTRY_ISO3 = 'GRC';
private const ADDRESS_FIELDS = ['line_one', 'city', 'postcode', 'state', 'contact_phone'];
public function __construct(
private readonly CustomerAccountService $account,
) {}
public function show(string $locale, Request $request): View
{
$user = $request->user();
$customer = $this->account->customer($user);
return view('account.show', [
'user' => $user,
'customer' => $customer,
'address' => $this->defaultAddress($user),
'regions' => State::where('country_id', $this->storeCountry()->id)->orderBy('name')->get(['id', 'name']),
]);
}
public function update(string $locale, Request $request): RedirectResponse
{
$user = $request->user();
$country = $this->storeCountry();
// The address is all-or-nothing: typing any part of it makes the rest
// (and the name, which Lunar requires on every address) required.
$anyAddressField = implode(',', self::ADDRESS_FIELDS);
$data = $request->validate([
'first_name' => ['nullable', 'string', 'max:255', 'required_with:'.$anyAddressField],
'last_name' => ['nullable', 'string', 'max:255', 'required_with:'.$anyAddressField],
'invoice' => ['boolean'],
'company_name' => ['nullable', 'string', 'max:255', 'required_if_accepted:invoice'],
'tax_identifier' => ['nullable', 'digits:9', 'required_if_accepted:invoice'],
'line_one' => ['nullable', 'string', 'max:255', 'required_with:'.$anyAddressField],
'city' => ['nullable', 'string', 'max:255', 'required_with:'.$anyAddressField],
'postcode' => ['nullable', 'regex:/^\d{3}\s?\d{2}$/', 'required_with:'.$anyAddressField],
'state' => [
'nullable',
'string',
'required_with:'.$anyAddressField,
Rule::exists((new State)->getTable(), 'name')->where('country_id', $country->id),
],
'contact_phone' => ['nullable', 'string', 'max:30'],
]);
$invoice = $request->boolean('invoice');
$customer = $this->account->updateProfile($user, [
'first_name' => $data['first_name'] ?? null,
'last_name' => $data['last_name'] ?? null,
'company_name' => $invoice ? $data['company_name'] : null,
]);
// Written directly: core's updateProfile() allowlists `vat_no`, but
// Lunar's column is `tax_identifier`, so it can't go through there yet.
$customer->update(['tax_identifier' => $invoice ? $data['tax_identifier'] : null]);
if (filled($data['line_one'] ?? null)) {
$addressData = [
...collect($data)->only(self::ADDRESS_FIELDS)->all(),
'first_name' => $data['first_name'],
'last_name' => $data['last_name'],
'country_id' => $country->id,
'contact_email' => $user->email,
'shipping_default' => true,
'billing_default' => true,
];
$existing = $this->defaultAddress($user);
$existing
? $this->account->updateAddress($user, $existing->id, $addressData)
: $this->account->createAddress($user, $addressData);
}
return redirect()->route('account')->with('status', __('storefront.account.saved'));
}
/**
* Self-service deletion: opens core's 30-day grace-period erasure request
* (which blocks the login right away) and logs out. Logging back in within
* the grace period cancels it; see core's docs/privacy.md.
*/
public function destroy(string $locale, Request $request, PrivacyService $privacy): RedirectResponse
{
$user = $request->user();
$privacy->requestErasureForUser($user, $user);
Auth::logout();
$request->session()->invalidate();
$request->session()->regenerateToken();
return redirect()->route('login')->with('status', __('storefront.account.deletion_requested'));
}
private function defaultAddress($user)
{
$addresses = collect($this->account->addresses($user));
return $addresses->firstWhere('shipping_default', true) ?? $addresses->first();
}
private function storeCountry(): Country
{
return Country::where('iso3', self::STORE_COUNTRY_ISO3)->firstOrFail();
}
}