You've already forked wc-bootstrap
Polish My Account templates with Bootstrap 5 patterns
Redesign navigation with endpoint icons, offcanvas-lg responsive pattern, and sticky sidebar. Replace flat dashboard with card-based welcome greeting (avatar) and quick-action grid. Wrap all forms (edit-account, edit-address, lost/reset-password) in card sections with icon headers. Restructure view-order with summary card and status badge component. Override WooCommerce's float-based layout and max-width constraint to let Bootstrap flex grid handle sizing. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
32
CLAUDE.md
32
CLAUDE.md
@@ -299,7 +299,11 @@ The child theme inherits from `wp-bootstrap` via WordPress `Template: wp-bootstr
|
||||
|
||||
## Architecture Decisions
|
||||
|
||||
- **Edit forms** use 3+9 column layout: `col-lg-3` sticky sidebar (progress indicator, section nav) + `col-lg-9` card-based sections.
|
||||
- **My Account layout** uses `col-lg-auto` (nav) + `col-lg` (content) so the nav auto-sizes to its content and the main area fills remaining space. Requires CSS resets for WooCommerce's float-based layout (`float: none; width: auto/100%`) and `max-width: none` on `.woocommerce-account main .woocommerce` to override the plugin's `max-width: 1000px`.
|
||||
- **My Account navigation** uses `offcanvas-lg offcanvas-start` responsive pattern: full sidebar with icons on desktop (sticky via `position-sticky`), offcanvas slide-in on mobile with toggle button. Icons are mapped per endpoint via a Twig hash.
|
||||
- **My Account dashboard** uses card-based quick actions (`row row-cols-1 row-cols-md-2 row-cols-xl-3 g-3`) with `bg-primary-subtle` icon containers that adapt to dark mode. Each action card is an `<a class="card">` for full-card clickability.
|
||||
- **My Account forms** (edit-account, edit-address, lost-password, reset-password) are wrapped in `card shadow-sm` sections with icon headers for visual consistency.
|
||||
- **Edit forms** use card-based section grouping (e.g., "Personal information" + "Password change" as separate cards) instead of `<fieldset>/<legend>`.
|
||||
- **Detail pages** use 8+4 column layout with sticky sidebar.
|
||||
- **Cards** use `<article class="card h-100">` with `stretched-link`.
|
||||
- **Form layout** uses centered `col-lg-8 col-xl-7` with card + shadow for auth forms.
|
||||
@@ -313,4 +317,28 @@ Current version: **v0.0.1**
|
||||
|
||||
## Session History
|
||||
|
||||
<!-- AI assistants: document key learnings and session outcomes here -->
|
||||
### 2026-02-28 — My Account Bootstrap 5 Polish
|
||||
|
||||
**Scope:** Redesigned 8 my-account Twig templates + CSS overrides to feel like a polished Bootstrap 5 application.
|
||||
|
||||
**Files changed (10):**
|
||||
|
||||
- `templates/myaccount/navigation.html.twig` — Added endpoint icon map, `offcanvas-lg` responsive pattern, sticky sidebar
|
||||
- `templates/myaccount/dashboard.html.twig` — Replaced plain `<p>` tags with welcome card (avatar + greeting) and 5 quick-action cards in responsive grid
|
||||
- `templates/myaccount/view-order.html.twig` — Replaced `<mark>` tags with summary card using `list-group-flush` and `components/status-badge.html.twig`; notes wrapped in card
|
||||
- `templates/myaccount/form-edit-account.html.twig` — Wrapped in two card sections (Personal info + Password change) with icons
|
||||
- `templates/myaccount/form-edit-address.html.twig` — Wrapped in card with dynamic icon (`bi-receipt` billing / `bi-truck` shipping)
|
||||
- `templates/myaccount/form-lost-password.html.twig` — Wrapped in card with `bi-key` icon
|
||||
- `templates/myaccount/form-reset-password.html.twig` — Wrapped in card with `bi-shield-lock` icon
|
||||
- `templates/myaccount/lost-password-confirmation.html.twig` — Added `text-body-secondary` styling and "Back to login" button
|
||||
- `templates/myaccount/my-account.html.twig` — Changed grid from `col-lg-3`/`col-lg-9` to `col-lg-auto`/`col-lg`
|
||||
- `assets/css/wc-bootstrap.css` — Reset WooCommerce float layout, override `max-width: 1000px`, avatar rounding, card hover lift
|
||||
|
||||
**Design decisions:**
|
||||
|
||||
- **Nav column auto-sizing (`col-lg-auto`)**: Fixed-width columns (e.g., `col-lg-3` = 285px) caused label overflow with icons. Auto-sizing lets the nav take exactly the space it needs across locales while the content fills the rest.
|
||||
- **WooCommerce layout overrides require matching specificity**: The plugin uses `.woocommerce-account .woocommerce-MyAccount-navigation` (specificity `0,2,0`) — single-class selectors don't override. Also `.woocommerce-account main .woocommerce` sets `max-width: 1000px` (specificity `0,2,1`) which must be reset to `none`.
|
||||
- **`offcanvas-lg` over `d-none d-lg-block`**: Bootstrap's responsive offcanvas natively handles the desktop/mobile switch without duplicating nav markup. The toggle button uses `d-lg-none` visibility.
|
||||
- **`bg-primary-subtle` for icon containers**: These Bootstrap 5.3 contextual utilities automatically adapt to dark mode, unlike hardcoded colors.
|
||||
- **Welcome message restructured**: Separated greeting from logout link instead of using WooCommerce's default inline-linked `__()` string. This gives full control over card layout and avoids translated strings containing HTML structure assumptions.
|
||||
- **Templates NOT changed** (already well-done): `orders.html.twig`, `my-address.html.twig`, `form-login.html.twig`, `payment-methods.html.twig`, `form-add-payment-method.html.twig`, `downloads.html.twig`
|
||||
|
||||
@@ -280,16 +280,47 @@ header.sticky-top.is-stuck {
|
||||
/* ==========================================================================
|
||||
My Account
|
||||
Navigation and layout for the My Account area.
|
||||
Reset WooCommerce's float-based layout to let Bootstrap flex grid handle it.
|
||||
========================================================================== */
|
||||
|
||||
.woocommerce-account main .woocommerce,
|
||||
.woocommerce-cart main .woocommerce,
|
||||
.woocommerce-checkout main .woocommerce {
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
.woocommerce-account .woocommerce-MyAccount-navigation {
|
||||
float: none;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.woocommerce-account .woocommerce-MyAccount-content {
|
||||
float: none;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.woocommerce-MyAccount-navigation .list-group-item {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.woocommerce-MyAccount-navigation .list-group-item.active {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* Order status marks */
|
||||
.woocommerce-order-details mark {
|
||||
background: none;
|
||||
font-weight: 600;
|
||||
/* Dashboard: avatar rounding */
|
||||
.woocommerce-MyAccount-content .avatar {
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
/* Dashboard: quick action card hover lift */
|
||||
.woocommerce-MyAccount-content a.card {
|
||||
transition: transform 0.15s ease, box-shadow 0.15s ease;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.woocommerce-MyAccount-content a.card:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: var(--bs-box-shadow) !important;
|
||||
}
|
||||
|
||||
/* View-order notes */
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
{#
|
||||
# My Account Dashboard (Bootstrap 5 Override)
|
||||
#
|
||||
# Shows the welcome screen on the account dashboard.
|
||||
# Shows a card-based dashboard with welcome greeting and
|
||||
# quick action links to each account section.
|
||||
#
|
||||
# Expected context:
|
||||
# current_user - WP_User object
|
||||
@@ -12,28 +13,54 @@
|
||||
# @since 0.1.0
|
||||
#}
|
||||
|
||||
<p>
|
||||
{{ __('Hello %1$s (not %1$s? <a href="%2$s">Log out</a>)')|format(
|
||||
'<strong>' ~ current_user.display_name|esc_html ~ '</strong>',
|
||||
wc_logout_url()|esc_url
|
||||
)|wp_kses_post }}
|
||||
</p>
|
||||
{% set quick_actions = [
|
||||
{ endpoint: 'orders', icon: 'bi-bag', label: __('Orders'), desc: __('View and track your orders') },
|
||||
{ endpoint: 'edit-address', icon: 'bi-geo-alt', label: __('Addresses'), desc: __('Manage billing & shipping') },
|
||||
{ endpoint: 'edit-account', icon: 'bi-person-gear', label: __('Account details'), desc: __('Update name, email & password') },
|
||||
{ endpoint: 'downloads', icon: 'bi-download', label: __('Downloads'), desc: __('Access purchased files') },
|
||||
{ endpoint: 'payment-methods', icon: 'bi-credit-card', label: __('Payment methods'), desc: __('Manage saved cards') },
|
||||
] %}
|
||||
|
||||
<p>
|
||||
{% if wc_shipping_enabled() %}
|
||||
{{ __('From your account dashboard you can view your <a href="%1$s">recent orders</a>, manage your <a href="%2$s">shipping and billing addresses</a>, and <a href="%3$s">edit your password and account details</a>.')|format(
|
||||
wc_get_endpoint_url('orders')|esc_url,
|
||||
wc_get_endpoint_url('edit-address')|esc_url,
|
||||
wc_get_endpoint_url('edit-account')|esc_url
|
||||
)|wp_kses_post }}
|
||||
{% else %}
|
||||
{{ __('From your account dashboard you can view your <a href="%1$s">recent orders</a>, manage your <a href="%2$s">billing address</a>, and <a href="%3$s">edit your password and account details</a>.')|format(
|
||||
wc_get_endpoint_url('orders')|esc_url,
|
||||
wc_get_endpoint_url('edit-address')|esc_url,
|
||||
wc_get_endpoint_url('edit-account')|esc_url
|
||||
)|wp_kses_post }}
|
||||
{% endif %}
|
||||
</p>
|
||||
<div class="card shadow-sm mb-4">
|
||||
<div class="card-body d-flex align-items-center gap-3">
|
||||
<div class="flex-shrink-0">
|
||||
{{ get_avatar(current_user.ID, 64)|raw }}
|
||||
</div>
|
||||
<div class="flex-grow-1">
|
||||
<h2 class="h5 mb-1">
|
||||
{{ __('Hello, %s!')|format(current_user.display_name|esc_html) }}
|
||||
</h2>
|
||||
<p class="text-body-secondary mb-0">
|
||||
{{ __('Not %s?')|format(current_user.display_name|esc_html) }}
|
||||
<a href="{{ wc_logout_url()|esc_url }}">{{ __('Log out') }}</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row row-cols-1 row-cols-md-2 row-cols-xl-3 g-3 mb-4">
|
||||
{% for action in quick_actions %}
|
||||
<div class="col">
|
||||
<a href="{{ wc_get_endpoint_url(action.endpoint)|esc_url }}"
|
||||
class="card h-100 text-decoration-none shadow-sm">
|
||||
<div class="card-body d-flex align-items-start gap-3">
|
||||
<div class="flex-shrink-0">
|
||||
<span class="d-inline-flex align-items-center justify-content-center
|
||||
bg-primary-subtle text-primary rounded-3"
|
||||
style="width: 3rem; height: 3rem;"
|
||||
aria-hidden="true">
|
||||
<i class="bi {{ action.icon }} fs-4"></i>
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="h6 mb-1 text-body">{{ action.label }}</h3>
|
||||
<p class="text-body-secondary small mb-0">{{ action.desc }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
{{ do_action('woocommerce_account_dashboard') }}
|
||||
{{ do_action('woocommerce_before_my_account') }}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
{#
|
||||
# Edit Account Form (Bootstrap 5 Override)
|
||||
#
|
||||
# Account details editing form with Bootstrap form styling.
|
||||
# Account details editing form with card-based sections
|
||||
# for personal information and password change.
|
||||
#
|
||||
# Expected context:
|
||||
# user - WP_User object
|
||||
@@ -18,109 +19,125 @@
|
||||
|
||||
{{ do_action('woocommerce_edit_account_form_start') }}
|
||||
|
||||
<div class="row g-3 mb-3">
|
||||
<div class="col-sm-6">
|
||||
<label for="account_first_name" class="form-label">
|
||||
{{ __('First name') }} <span class="text-danger" aria-hidden="true">*</span>
|
||||
<span class="visually-hidden">{{ __('Required') }}</span>
|
||||
</label>
|
||||
<input type="text"
|
||||
class="form-control"
|
||||
name="account_first_name"
|
||||
id="account_first_name"
|
||||
autocomplete="given-name"
|
||||
value="{{ user.first_name|esc_attr }}"
|
||||
required
|
||||
aria-required="true" />
|
||||
<div class="card shadow-sm mb-4">
|
||||
<div class="card-header">
|
||||
<h2 class="h5 mb-0">
|
||||
<i class="bi bi-person me-1" aria-hidden="true"></i>
|
||||
{{ __('Personal information') }}
|
||||
</h2>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<label for="account_last_name" class="form-label">
|
||||
{{ __('Last name') }} <span class="text-danger" aria-hidden="true">*</span>
|
||||
<span class="visually-hidden">{{ __('Required') }}</span>
|
||||
</label>
|
||||
<input type="text"
|
||||
class="form-control"
|
||||
name="account_last_name"
|
||||
id="account_last_name"
|
||||
autocomplete="family-name"
|
||||
value="{{ user.last_name|esc_attr }}"
|
||||
required
|
||||
aria-required="true" />
|
||||
<div class="card-body">
|
||||
<div class="row g-3 mb-3">
|
||||
<div class="col-sm-6">
|
||||
<label for="account_first_name" class="form-label">
|
||||
{{ __('First name') }} <span class="text-danger" aria-hidden="true">*</span>
|
||||
<span class="visually-hidden">{{ __('Required') }}</span>
|
||||
</label>
|
||||
<input type="text"
|
||||
class="form-control"
|
||||
name="account_first_name"
|
||||
id="account_first_name"
|
||||
autocomplete="given-name"
|
||||
value="{{ user.first_name|esc_attr }}"
|
||||
required
|
||||
aria-required="true" />
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<label for="account_last_name" class="form-label">
|
||||
{{ __('Last name') }} <span class="text-danger" aria-hidden="true">*</span>
|
||||
<span class="visually-hidden">{{ __('Required') }}</span>
|
||||
</label>
|
||||
<input type="text"
|
||||
class="form-control"
|
||||
name="account_last_name"
|
||||
id="account_last_name"
|
||||
autocomplete="family-name"
|
||||
value="{{ user.last_name|esc_attr }}"
|
||||
required
|
||||
aria-required="true" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="account_display_name" class="form-label">
|
||||
{{ __('Display name') }} <span class="text-danger" aria-hidden="true">*</span>
|
||||
<span class="visually-hidden">{{ __('Required') }}</span>
|
||||
</label>
|
||||
<input type="text"
|
||||
class="form-control"
|
||||
name="account_display_name"
|
||||
id="account_display_name"
|
||||
aria-describedby="account_display_name_description"
|
||||
value="{{ user.display_name|esc_attr }}"
|
||||
required
|
||||
aria-required="true" />
|
||||
<div id="account_display_name_description" class="form-text">
|
||||
{{ __('This will be how your name will be displayed in the account section and in reviews') }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-0">
|
||||
<label for="account_email" class="form-label">
|
||||
{{ __('Email address') }} <span class="text-danger" aria-hidden="true">*</span>
|
||||
<span class="visually-hidden">{{ __('Required') }}</span>
|
||||
</label>
|
||||
<input type="email"
|
||||
class="form-control"
|
||||
name="account_email"
|
||||
id="account_email"
|
||||
autocomplete="email"
|
||||
value="{{ user.user_email|esc_attr }}"
|
||||
required
|
||||
aria-required="true" />
|
||||
</div>
|
||||
|
||||
{{ do_action('woocommerce_edit_account_form_fields') }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="account_display_name" class="form-label">
|
||||
{{ __('Display name') }} <span class="text-danger" aria-hidden="true">*</span>
|
||||
<span class="visually-hidden">{{ __('Required') }}</span>
|
||||
</label>
|
||||
<input type="text"
|
||||
class="form-control"
|
||||
name="account_display_name"
|
||||
id="account_display_name"
|
||||
aria-describedby="account_display_name_description"
|
||||
value="{{ user.display_name|esc_attr }}"
|
||||
required
|
||||
aria-required="true" />
|
||||
<div id="account_display_name_description" class="form-text">
|
||||
{{ __('This will be how your name will be displayed in the account section and in reviews') }}
|
||||
<div class="card shadow-sm mb-4">
|
||||
<div class="card-header">
|
||||
<h2 class="h5 mb-0">
|
||||
<i class="bi bi-shield-lock me-1" aria-hidden="true"></i>
|
||||
{{ __('Password change') }}
|
||||
</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="mb-3">
|
||||
<label for="password_current" class="form-label">
|
||||
{{ __('Current password (leave blank to leave unchanged)') }}
|
||||
</label>
|
||||
<input type="password"
|
||||
class="form-control"
|
||||
name="password_current"
|
||||
id="password_current"
|
||||
autocomplete="off" />
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="password_1" class="form-label">
|
||||
{{ __('New password (leave blank to leave unchanged)') }}
|
||||
</label>
|
||||
<input type="password"
|
||||
class="form-control"
|
||||
name="password_1"
|
||||
id="password_1"
|
||||
autocomplete="off" />
|
||||
</div>
|
||||
|
||||
<div class="mb-0">
|
||||
<label for="password_2" class="form-label">
|
||||
{{ __('Confirm new password') }}
|
||||
</label>
|
||||
<input type="password"
|
||||
class="form-control"
|
||||
name="password_2"
|
||||
id="password_2"
|
||||
autocomplete="off" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="account_email" class="form-label">
|
||||
{{ __('Email address') }} <span class="text-danger" aria-hidden="true">*</span>
|
||||
<span class="visually-hidden">{{ __('Required') }}</span>
|
||||
</label>
|
||||
<input type="email"
|
||||
class="form-control"
|
||||
name="account_email"
|
||||
id="account_email"
|
||||
autocomplete="email"
|
||||
value="{{ user.user_email|esc_attr }}"
|
||||
required
|
||||
aria-required="true" />
|
||||
</div>
|
||||
|
||||
{{ do_action('woocommerce_edit_account_form_fields') }}
|
||||
|
||||
<fieldset class="mb-3">
|
||||
<legend class="h5 border-bottom pb-2 mb-3">{{ __('Password change') }}</legend>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="password_current" class="form-label">
|
||||
{{ __('Current password (leave blank to leave unchanged)') }}
|
||||
</label>
|
||||
<input type="password"
|
||||
class="form-control"
|
||||
name="password_current"
|
||||
id="password_current"
|
||||
autocomplete="off" />
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="password_1" class="form-label">
|
||||
{{ __('New password (leave blank to leave unchanged)') }}
|
||||
</label>
|
||||
<input type="password"
|
||||
class="form-control"
|
||||
name="password_1"
|
||||
id="password_1"
|
||||
autocomplete="off" />
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="password_2" class="form-label">
|
||||
{{ __('Confirm new password') }}
|
||||
</label>
|
||||
<input type="password"
|
||||
class="form-control"
|
||||
name="password_2"
|
||||
id="password_2"
|
||||
autocomplete="off" />
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
{{ do_action('woocommerce_edit_account_form') }}
|
||||
|
||||
<div class="mt-3">
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
{#
|
||||
# Edit Address Form (Bootstrap 5 Override)
|
||||
#
|
||||
# Address editing form with Bootstrap form styling.
|
||||
# Address editing form wrapped in a Bootstrap card with
|
||||
# contextual icon for billing/shipping.
|
||||
#
|
||||
# Expected context:
|
||||
# load_address - Address type ('billing' or 'shipping'), or empty
|
||||
@@ -14,36 +15,45 @@
|
||||
#}
|
||||
|
||||
{% set page_title = load_address == 'billing' ? __('Billing address') : __('Shipping address') %}
|
||||
{% set address_icon = load_address == 'billing' ? 'bi-receipt' : 'bi-truck' %}
|
||||
|
||||
{{ do_action('woocommerce_before_edit_account_address_form') }}
|
||||
|
||||
{% if not load_address %}
|
||||
{% include 'myaccount/my-address.html.twig' %}
|
||||
{% else %}
|
||||
<form method="post" novalidate>
|
||||
<h2 class="h4 mb-4">{{ apply_filters('woocommerce_my_account_edit_address_title', page_title, load_address) }}</h2>
|
||||
|
||||
<div class="woocommerce-address-fields">
|
||||
{{ do_action('woocommerce_before_edit_address_form_' ~ load_address) }}
|
||||
|
||||
<div class="woocommerce-address-fields__field-wrapper">
|
||||
{% for key, field in address %}
|
||||
{{ woocommerce_form_field(key, field, wc_get_post_data_by_key(key, field.value)) }}
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
{{ do_action('woocommerce_after_edit_address_form_' ~ load_address) }}
|
||||
|
||||
<div class="mt-3">
|
||||
<button type="submit" class="btn btn-primary" name="save_address" value="{{ __('Save address') }}">
|
||||
<i class="bi bi-check-lg me-1" aria-hidden="true"></i>
|
||||
{{ __('Save address') }}
|
||||
</button>
|
||||
{{ wp_nonce_field('woocommerce-edit_address', 'woocommerce-edit-address-nonce') }}
|
||||
<input type="hidden" name="action" value="edit_address" />
|
||||
</div>
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-header">
|
||||
<h2 class="h5 mb-0">
|
||||
<i class="bi {{ address_icon }} me-1" aria-hidden="true"></i>
|
||||
{{ apply_filters('woocommerce_my_account_edit_address_title', page_title, load_address) }}
|
||||
</h2>
|
||||
</div>
|
||||
</form>
|
||||
<div class="card-body">
|
||||
<form method="post" novalidate>
|
||||
<div class="woocommerce-address-fields">
|
||||
{{ do_action('woocommerce_before_edit_address_form_' ~ load_address) }}
|
||||
|
||||
<div class="woocommerce-address-fields__field-wrapper">
|
||||
{% for key, field in address %}
|
||||
{{ woocommerce_form_field(key, field, wc_get_post_data_by_key(key, field.value)) }}
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
{{ do_action('woocommerce_after_edit_address_form_' ~ load_address) }}
|
||||
|
||||
<div class="mt-3">
|
||||
<button type="submit" class="btn btn-primary" name="save_address" value="{{ __('Save address') }}">
|
||||
<i class="bi bi-check-lg me-1" aria-hidden="true"></i>
|
||||
{{ __('Save address') }}
|
||||
</button>
|
||||
{{ wp_nonce_field('woocommerce-edit_address', 'woocommerce-edit-address-nonce') }}
|
||||
<input type="hidden" name="action" value="edit_address" />
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{{ do_action('woocommerce_after_edit_account_address_form') }}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
{#
|
||||
# Lost Password Form (Bootstrap 5 Override)
|
||||
#
|
||||
# Form to request a password reset email.
|
||||
# Form to request a password reset email, wrapped in a
|
||||
# Bootstrap card for visual consistency.
|
||||
#
|
||||
# WooCommerce PHP equivalent: myaccount/form-lost-password.php
|
||||
#
|
||||
@@ -11,37 +12,47 @@
|
||||
|
||||
{{ do_action('woocommerce_before_lost_password_form') }}
|
||||
|
||||
<form method="post" class="woocommerce-ResetPassword lost_reset_password">
|
||||
|
||||
<p class="mb-3">
|
||||
{{ apply_filters('woocommerce_lost_password_message', __('Lost your password? Please enter your username or email address. You will receive a link to create a new password via email.')) }}
|
||||
</p>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="user_login" class="form-label">
|
||||
{{ __('Username or email') }} <span class="text-danger" aria-hidden="true">*</span>
|
||||
<span class="visually-hidden">{{ __('Required') }}</span>
|
||||
</label>
|
||||
<input type="text"
|
||||
class="form-control"
|
||||
name="user_login"
|
||||
id="user_login"
|
||||
autocomplete="username"
|
||||
required
|
||||
aria-required="true" />
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-header">
|
||||
<h2 class="h5 mb-0">
|
||||
<i class="bi bi-key me-1" aria-hidden="true"></i>
|
||||
{{ __('Lost your password?') }}
|
||||
</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="post" class="woocommerce-ResetPassword lost_reset_password">
|
||||
|
||||
{{ do_action('woocommerce_lostpassword_form') }}
|
||||
<p class="text-body-secondary mb-3">
|
||||
{{ apply_filters('woocommerce_lost_password_message', __('Lost your password? Please enter your username or email address. You will receive a link to create a new password via email.')) }}
|
||||
</p>
|
||||
|
||||
<div class="mt-3">
|
||||
<input type="hidden" name="wc_reset_password" value="true" />
|
||||
<button type="submit" class="btn btn-primary" value="{{ __('Reset password') }}">
|
||||
{{ __('Reset password') }}
|
||||
</button>
|
||||
<div class="mb-3">
|
||||
<label for="user_login" class="form-label">
|
||||
{{ __('Username or email') }} <span class="text-danger" aria-hidden="true">*</span>
|
||||
<span class="visually-hidden">{{ __('Required') }}</span>
|
||||
</label>
|
||||
<input type="text"
|
||||
class="form-control"
|
||||
name="user_login"
|
||||
id="user_login"
|
||||
autocomplete="username"
|
||||
required
|
||||
aria-required="true" />
|
||||
</div>
|
||||
|
||||
{{ do_action('woocommerce_lostpassword_form') }}
|
||||
|
||||
<div class="mt-3">
|
||||
<input type="hidden" name="wc_reset_password" value="true" />
|
||||
<button type="submit" class="btn btn-primary" value="{{ __('Reset password') }}">
|
||||
{{ __('Reset password') }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{{ wp_nonce_field('lost_password', 'woocommerce-lost-password-nonce') }}
|
||||
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{{ wp_nonce_field('lost_password', 'woocommerce-lost-password-nonce') }}
|
||||
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{{ do_action('woocommerce_after_lost_password_form') }}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
{#
|
||||
# Reset Password Form (Bootstrap 5 Override)
|
||||
#
|
||||
# Form to set a new password after clicking the reset link.
|
||||
# Form to set a new password after clicking the reset link,
|
||||
# wrapped in a Bootstrap card for visual consistency.
|
||||
#
|
||||
# Expected context:
|
||||
# args - Array with 'key' and 'login' values
|
||||
@@ -14,55 +15,65 @@
|
||||
|
||||
{{ do_action('woocommerce_before_reset_password_form') }}
|
||||
|
||||
<form method="post" class="woocommerce-ResetPassword lost_reset_password">
|
||||
|
||||
<p class="mb-3">
|
||||
{{ apply_filters('woocommerce_reset_password_message', __('Enter a new password below.')) }}
|
||||
</p>
|
||||
|
||||
<div class="row g-3 mb-3">
|
||||
<div class="col-sm-6">
|
||||
<label for="password_1" class="form-label">
|
||||
{{ __('New password') }} <span class="text-danger" aria-hidden="true">*</span>
|
||||
<span class="visually-hidden">{{ __('Required') }}</span>
|
||||
</label>
|
||||
<input type="password"
|
||||
class="form-control"
|
||||
name="password_1"
|
||||
id="password_1"
|
||||
autocomplete="new-password"
|
||||
required
|
||||
aria-required="true" />
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<label for="password_2" class="form-label">
|
||||
{{ __('Re-enter new password') }} <span class="text-danger" aria-hidden="true">*</span>
|
||||
<span class="visually-hidden">{{ __('Required') }}</span>
|
||||
</label>
|
||||
<input type="password"
|
||||
class="form-control"
|
||||
name="password_2"
|
||||
id="password_2"
|
||||
autocomplete="new-password"
|
||||
required
|
||||
aria-required="true" />
|
||||
</div>
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-header">
|
||||
<h2 class="h5 mb-0">
|
||||
<i class="bi bi-shield-lock me-1" aria-hidden="true"></i>
|
||||
{{ __('Set new password') }}
|
||||
</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="post" class="woocommerce-ResetPassword lost_reset_password">
|
||||
|
||||
<input type="hidden" name="reset_key" value="{{ args.key|esc_attr }}" />
|
||||
<input type="hidden" name="reset_login" value="{{ args.login|esc_attr }}" />
|
||||
<p class="text-body-secondary mb-3">
|
||||
{{ apply_filters('woocommerce_reset_password_message', __('Enter a new password below.')) }}
|
||||
</p>
|
||||
|
||||
{{ do_action('woocommerce_resetpassword_form') }}
|
||||
<div class="row g-3 mb-3">
|
||||
<div class="col-sm-6">
|
||||
<label for="password_1" class="form-label">
|
||||
{{ __('New password') }} <span class="text-danger" aria-hidden="true">*</span>
|
||||
<span class="visually-hidden">{{ __('Required') }}</span>
|
||||
</label>
|
||||
<input type="password"
|
||||
class="form-control"
|
||||
name="password_1"
|
||||
id="password_1"
|
||||
autocomplete="new-password"
|
||||
required
|
||||
aria-required="true" />
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<label for="password_2" class="form-label">
|
||||
{{ __('Re-enter new password') }} <span class="text-danger" aria-hidden="true">*</span>
|
||||
<span class="visually-hidden">{{ __('Required') }}</span>
|
||||
</label>
|
||||
<input type="password"
|
||||
class="form-control"
|
||||
name="password_2"
|
||||
id="password_2"
|
||||
autocomplete="new-password"
|
||||
required
|
||||
aria-required="true" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<input type="hidden" name="wc_reset_password" value="true" />
|
||||
<button type="submit" class="btn btn-primary" value="{{ __('Save') }}">
|
||||
{{ __('Save') }}
|
||||
</button>
|
||||
<input type="hidden" name="reset_key" value="{{ args.key|esc_attr }}" />
|
||||
<input type="hidden" name="reset_login" value="{{ args.login|esc_attr }}" />
|
||||
|
||||
{{ do_action('woocommerce_resetpassword_form') }}
|
||||
|
||||
<div class="mt-3">
|
||||
<input type="hidden" name="wc_reset_password" value="true" />
|
||||
<button type="submit" class="btn btn-primary" value="{{ __('Save') }}">
|
||||
{{ __('Save') }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{{ wp_nonce_field('reset_password', 'woocommerce-reset-password-nonce') }}
|
||||
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{{ wp_nonce_field('reset_password', 'woocommerce-reset-password-nonce') }}
|
||||
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{{ do_action('woocommerce_after_reset_password_form') }}
|
||||
|
||||
@@ -16,8 +16,13 @@
|
||||
|
||||
{{ do_action('woocommerce_before_lost_password_confirmation_message') }}
|
||||
|
||||
<p>
|
||||
<p class="text-body-secondary">
|
||||
{{ apply_filters('woocommerce_lost_password_confirmation_message', __('A password reset email has been sent to the email address on file for your account, but may take several minutes to show up in your inbox. Please wait at least 10 minutes before attempting another reset.')) }}
|
||||
</p>
|
||||
|
||||
<a href="{{ wc_get_page_permalink('myaccount')|esc_url }}" class="btn btn-outline-primary">
|
||||
<i class="bi bi-arrow-left me-1" aria-hidden="true"></i>
|
||||
{{ __('Back to login') }}
|
||||
</a>
|
||||
|
||||
{{ do_action('woocommerce_after_lost_password_confirmation_message') }}
|
||||
|
||||
@@ -13,11 +13,11 @@
|
||||
{{ do_action('woocommerce_before_my_account') }}
|
||||
|
||||
<div class="row g-4">
|
||||
<div class="col-lg-3">
|
||||
<div class="col-lg-auto">
|
||||
{% include 'myaccount/navigation.html.twig' %}
|
||||
</div>
|
||||
|
||||
<div class="col-lg-9">
|
||||
<div class="col-lg">
|
||||
<div class="woocommerce-MyAccount-content">
|
||||
{{ do_action('woocommerce_account_content') }}
|
||||
</div>
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
# My Account Navigation (Bootstrap 5 Override)
|
||||
#
|
||||
# Renders the sidebar navigation for the My Account area
|
||||
# using Bootstrap list-group component.
|
||||
# using Bootstrap list-group component with icons.
|
||||
# Responsive: offcanvas on mobile, sticky sidebar on desktop.
|
||||
#
|
||||
# WooCommerce PHP equivalent: myaccount/navigation.php
|
||||
#
|
||||
@@ -10,14 +11,53 @@
|
||||
# @since 0.1.0
|
||||
#}
|
||||
|
||||
<nav class="woocommerce-MyAccount-navigation" aria-label="{{ __('Account navigation') }}">
|
||||
<div class="list-group">
|
||||
{% for endpoint, label in wc_get_account_menu_items() %}
|
||||
<a href="{{ wc_get_account_endpoint_url(endpoint)|esc_url }}"
|
||||
class="list-group-item list-group-item-action{% if wc_get_account_menu_item_classes(endpoint) matches '/is-active/' %} active{% endif %}"
|
||||
{% if wc_get_account_menu_item_classes(endpoint) matches '/is-active/' %}aria-current="page"{% endif %}>
|
||||
{{ label|esc_html }}
|
||||
</a>
|
||||
{% endfor %}
|
||||
{% set endpoint_icons = {
|
||||
'dashboard': 'bi-speedometer2',
|
||||
'orders': 'bi-bag',
|
||||
'downloads': 'bi-download',
|
||||
'edit-address': 'bi-geo-alt',
|
||||
'payment-methods': 'bi-credit-card',
|
||||
'edit-account': 'bi-person-gear',
|
||||
'customer-logout': 'bi-box-arrow-right',
|
||||
} %}
|
||||
|
||||
{{ do_action('woocommerce_before_account_navigation') }}
|
||||
|
||||
<button class="btn btn-outline-secondary w-100 mb-3 d-lg-none"
|
||||
type="button"
|
||||
data-bs-toggle="offcanvas"
|
||||
data-bs-target="#accountNav"
|
||||
aria-controls="accountNav">
|
||||
<i class="bi bi-list me-1" aria-hidden="true"></i>
|
||||
{{ __('Account menu') }}
|
||||
</button>
|
||||
|
||||
<div class="offcanvas-lg offcanvas-start" id="accountNav" tabindex="-1"
|
||||
aria-labelledby="accountNavLabel">
|
||||
<div class="offcanvas-header d-lg-none">
|
||||
<h5 class="offcanvas-title" id="accountNavLabel">{{ __('Account menu') }}</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="offcanvas"
|
||||
data-bs-target="#accountNav" aria-label="{{ __('Close') }}"></button>
|
||||
</div>
|
||||
</nav>
|
||||
<div class="offcanvas-body p-0">
|
||||
<nav class="woocommerce-MyAccount-navigation position-sticky"
|
||||
style="top: 5rem;"
|
||||
aria-label="{{ __('Account navigation') }}">
|
||||
<div class="list-group">
|
||||
{% for endpoint, label in wc_get_account_menu_items() %}
|
||||
{% set is_active = wc_get_account_menu_item_classes(endpoint) matches '/is-active/' %}
|
||||
<a href="{{ wc_get_account_endpoint_url(endpoint)|esc_url }}"
|
||||
class="list-group-item list-group-item-action d-flex align-items-center{% if is_active %} active{% endif %}"
|
||||
{% if is_active %}aria-current="page"{% endif %}>
|
||||
{% if endpoint_icons[endpoint] is defined %}
|
||||
<i class="bi {{ endpoint_icons[endpoint] }} me-2" aria-hidden="true"></i>
|
||||
{% endif %}
|
||||
{{ label|esc_html }}
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{ do_action('woocommerce_after_account_navigation') }}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
{#
|
||||
# View Order (Bootstrap 5 Override)
|
||||
#
|
||||
# Shows details of a specific order on the account page.
|
||||
# Shows details of a specific order on the account page
|
||||
# with summary card, status badge, and order notes.
|
||||
# HPOS compatible: uses WC_Order methods only.
|
||||
#
|
||||
# Expected context:
|
||||
@@ -16,29 +17,60 @@
|
||||
|
||||
{% set notes = order.get_customer_order_notes() %}
|
||||
|
||||
<p>
|
||||
{{ __('Order #%1$s was placed on %2$s and is currently %3$s.')|format(
|
||||
'<mark class="order-number">' ~ order.get_order_number() ~ '</mark>',
|
||||
'<mark class="order-date">' ~ wc_format_datetime(order.get_date_created()) ~ '</mark>',
|
||||
'<mark class="order-status">' ~ wc_get_order_status_name(order.get_status()) ~ '</mark>'
|
||||
)|wp_kses_post }}
|
||||
</p>
|
||||
<div class="card shadow-sm mb-4">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<h2 class="h5 mb-0">
|
||||
{{ __('Order #%s')|format(order.get_order_number()) }}
|
||||
</h2>
|
||||
{% include 'components/status-badge.html.twig' with { status: order.get_status() } %}
|
||||
</div>
|
||||
<ul class="list-group list-group-flush">
|
||||
<li class="list-group-item d-flex justify-content-between">
|
||||
<span class="text-body-secondary">{{ __('Date') }}</span>
|
||||
<time datetime="{{ order.get_date_created().date('c')|esc_attr }}">
|
||||
{{ wc_format_datetime(order.get_date_created())|esc_html }}
|
||||
</time>
|
||||
</li>
|
||||
<li class="list-group-item d-flex justify-content-between">
|
||||
<span class="text-body-secondary">{{ __('Status') }}</span>
|
||||
{% include 'components/status-badge.html.twig' with { status: order.get_status() } %}
|
||||
</li>
|
||||
<li class="list-group-item d-flex justify-content-between">
|
||||
<span class="text-body-secondary">{{ __('Total') }}</span>
|
||||
<strong>{{ order.get_formatted_order_total()|wp_kses_post }}</strong>
|
||||
</li>
|
||||
{% if order.get_payment_method_title() %}
|
||||
<li class="list-group-item d-flex justify-content-between">
|
||||
<span class="text-body-secondary">{{ __('Payment method') }}</span>
|
||||
<span>{{ order.get_payment_method_title()|esc_html }}</span>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{% if notes %}
|
||||
<h2 class="h5 mt-4 mb-3">{{ __('Order updates') }}</h2>
|
||||
<div class="list-group mb-4">
|
||||
{% for note in notes %}
|
||||
<div class="list-group-item">
|
||||
<div class="d-flex justify-content-between align-items-center mb-1">
|
||||
<small class="text-body-secondary">
|
||||
{{ date_i18n(__('l jS \\o\\f F Y, h:ia'), strtotime(note.comment_date)) }}
|
||||
</small>
|
||||
<div class="card shadow-sm mb-4">
|
||||
<div class="card-header">
|
||||
<h3 class="h6 mb-0">
|
||||
<i class="bi bi-chat-left-text me-1" aria-hidden="true"></i>
|
||||
{{ __('Order updates') }}
|
||||
</h3>
|
||||
</div>
|
||||
<div class="list-group list-group-flush">
|
||||
{% for note in notes %}
|
||||
<div class="list-group-item">
|
||||
<div class="d-flex justify-content-between align-items-center mb-1">
|
||||
<small class="text-body-secondary">
|
||||
<i class="bi bi-clock me-1" aria-hidden="true"></i>
|
||||
{{ date_i18n(__('l jS \\o\\f F Y, h:ia'), strtotime(note.comment_date)) }}
|
||||
</small>
|
||||
</div>
|
||||
<div class="mb-0">
|
||||
{{ wpautop(wptexturize(note.comment_content))|raw }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-0">
|
||||
{{ wpautop(wptexturize(note.comment_content))|raw }}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user