Merge branch '2.0' into 2.0-docs

* 2.0:
  [Fixtures] Remove incorrect cap photo
  [Fixtures] Refresh user avatars
  [Fixtures] Remove old unused photos
  [UI] Reduce the margin on the product show page
  Add Sylius logo for e-mails
  [UI] Add missing asterix
  Fix mobile product attributes form
  Fix missing payment request breadcrumb
  Fix missing taxon header
  Fix mobile shipping methods form
  [Admin] Fix deleting customer account
  [UI] Add top margin to flash messages
  Fix mobile order header in admin panel
  Fix order payments and shipments tables in admin panel
  Remove obsolete catalog promotions validation adr
  Fix mobile admin navbar
This commit is contained in:
Grzegorz Sadowski 2024-11-10 14:38:08 +01:00
commit ebe1f5fca0
No known key found for this signature in database
GPG key ID: EF87FF4E6E3BD364
31 changed files with 54 additions and 110 deletions

View file

@ -1,66 +0,0 @@
# Catalog promotions validation
* Status: accepted
* Date: 2022-02-09
## Context and Problem Statement
Catalog Promotions, as the new feature in Sylius 1.11, is developed in API-first manner, but of course provides
UI functionality as well. The challenge, that we faced during the implementation was the validation, and either it should
be unified for API and UI or separated.
## Decision Drivers
Catalog Promotions' validation should:
* be easy to customize and extend
* work on both API and UI
* validate both business scenarios and syntactical correctness
## Considered Options
### Unified validation in custom validators
This is the first approach to validation we had. The concept was, to have
[custom validator for each action and scope](https://github.com/Sylius/Sylius/blob/b5458fa31aadaf699e39b0cc106d5efd25144823/src/Sylius/Bundle/CoreBundle/Validator/CatalogPromotionAction/FixedDiscountActionValidator.php).
The correct validation is chosen based on the key provided in `sylius.catalog_promotion.action_validator` tag. These custom
validators should contain both business and syntactical validation (as the latter is not configured anywhere else).
* Good, because custom validators are unified for API and UI, which results in the lack of duplications
* Bad, because business and syntactical validation is mixed, even though these are different type of validations
* Bad, because it resulted in some problems in validation messages display (Catalog Promotions' forms are quite complicated)
### Unified business validation and separated syntactical validation
Variation of the previous option, which still uses custom validators but only for business (or functional) validation.
Syntactical validation is delivered separately for UI and API.
* Good, because syntactical and business validation is separated, as it should be
* Good, because UI validation on forms is less problematic still being extendable and customizable
* Semi-bad, because it results in _apparent_ duplication of logic
* Bad, because there is no easy way to syntactically validate requests in ApiPlatform, so it needs to be done in validators anyway
## Decision Outcome
Chosen option: **"Unified business validation and separated syntactical validation"**
Syntactical and functional validation are two different types of validation. They should be separated, especially as they
can be approached differently (from the technical point of view) regarding the interface used (UI or API). Moreover, this
separation gives us better control over how requests/forms are validated (e.g. some values passed in forms does not have
to be validated in Sylius, as they're already protected in used Symfony types). Output of this decisions results in following
structure of validators:
* `src/Sylius/Bundle/PromotionBundle/Validator/CatalogPromotionAction` - base business validation of catalog promotion actions
not related to the concepts from other bundles (currently empty, only interface)
* `src/Sylius/Bundle/CoreBundle/Validator/CatalogPromotionAction` - business validation of catalog promotion actions related
to the concepts from other bundles (e.g. product variants)
* `src/Sylius/Bundle/CoreBundle/Validator/CatalogPromotionScope` - business validation of catalog promotion scopes related
to the concepts from other bundles (e.g. taxons)
* `src/Sylius/Bundle/ApiBundle/Validator/CatalogPromotion` - syntactical validation of API requests; validators here usually
decorate validators from the **CoreBundle**
Syntactical validation of forms data shall be done on each specific form (e.g. those from `src/Sylius/Bundle/CoreBundle/Form/Type/CatalogPromotionScope`).
## References
* [1st approach](https://github.com/Sylius/Sylius/pull/13174)
* [Chosen approach](https://github.com/Sylius/Sylius/pull/13620)

View file

@ -9,12 +9,19 @@ Feature: Deleting the customer account
And there is a user "theodore@example.com" identified by "pswd"
And I am logged in as an administrator
@api @ui
@api @no-ui
Scenario: Deleting account should not delete customer details
When I delete the account of "theodore@example.com" user
Then the user account should be deleted
But the customer with this email should still exist
@no-api @ui
Scenario: Deleting account should not delete customer details
When I delete the account of "theodore@example.com" user
Then I should be notified that it has been successfully deleted
And the customer should have no account
But the customer with this email should still exist
@api @ui
Scenario: A customer with no user cannot be deleted
Given the account of "theodore@example.com" was deleted

View file

@ -54,12 +54,11 @@ final class UserContext implements Context
}
/**
* @Then the user account should be deleted
* @Then the customer should have no account
*/
public function accountShouldBeDeleted()
public function theCustomerShouldHaveNoAccount(): void
{
$deletedUser = $this->sharedStorage->get('deleted_user');
$this->customerShowPage->open(['id' => $deletedUser->getCustomer()->getId()]);
Assert::false($this->customerShowPage->hasAccount());

View file

@ -1,7 +1,8 @@
{% set user = hookable_metadata.context.resource.user %}
{% set customer = hookable_metadata.context.resource %}
{% set user = customer.user %}
{% if user != null %}
<form action="{{ path('sylius_admin_shop_user_delete', {'id': user.id}) }}" method="POST">
<form action="{{ path('sylius_admin_shop_user_delete', {'id': user.id, 'customerId': customer.id}) }}" method="POST">
<input type="hidden" name="_method" value="DELETE" />
<input type="hidden" name="_csrf_token" value="{{ csrf_token(user.id) }}" />
<button type="submit" class="dropdown-item" {{ sylius_test_html_attribute('customer-actions-delete') }}>

View file

@ -3,7 +3,7 @@
{% set order = hookable_metadata.context.resource %}
<div class="col-12 col-md-6">
<div class="d-md-flex gap-2 align-items-center">
<div class="d-flex gap-2 align-items-center">
<h1 class="page-title">
{{ 'sylius.ui.order'|trans }}
#{{ order.number }}

View file

@ -2,15 +2,17 @@
<div class="py-3">
{% if order.hasPayments %}
<table class="table table-vcenter table-borderless table-sm card-table">
<tbody>
{% for payment in order.payments %}
<tr {{ sylius_test_html_attribute('payment') }}>
{% hook 'item' with { resource: order, payment } %}
</tr>
{% endfor %}
</tbody>
</table>
<div class="table-responsive">
<table class="table table-vcenter table-borderless table-sm card-table">
<tbody>
{% for payment in order.payments %}
<tr {{ sylius_test_html_attribute('payment') }}>
{% hook 'item' with { resource: order, payment } %}
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<span class="px-3 text-secondary italic" {{ sylius_test_html_attribute('no-payments') }}>{{ 'sylius.ui.no_payments'|trans }}</span>
{% endif %}

View file

@ -1,4 +1,4 @@
<td class="w-1">
<td class="w-1 px-2">
<div class="d-flex justify-content-end">
{% hook 'actions' %}
</div>

View file

@ -1,6 +1,6 @@
{% set shipment = hookable_metadata.context.shipment %}
<td class="px-3 py-2">
<td class="px-3 py-2 text-nowrap">
<div class="py-2">
<strong {{ sylius_test_html_attribute('shipment-method') }}>{{ shipment.method.name }}</strong>
<div class="text-secondary">{{ ux_icon('tabler:world') }} {{ shipment.method.zone }}</div>

View file

@ -2,15 +2,17 @@
<div class="py-3">
{% if order.hasShipments %}
<table class="table table-vcenter table-borderless table-sm card-table">
<tbody>
{% for shipment in order.shipments %}
<tr {{ sylius_test_html_attribute('shipment') }}>
{% hook 'item' with { shipment } %}
</tr>
{% endfor %}
</tbody>
</table>
<div class="table-responsive">
<table class="table table-vcenter table-borderless table-sm card-table">
<tbody>
{% for shipment in order.shipments %}
<tr {{ sylius_test_html_attribute('shipment') }}>
{% hook 'item' with { shipment } %}
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<span class="px-3 text-secondary italic">{{ 'sylius.ui.there_are_no_shipments_to_display'|trans }}</span>
{% endif %}

View file

@ -5,6 +5,6 @@
{{ breadcrumbs([
{ name: 'sylius.ui.dashboard', url: path('sylius_admin_dashboard'), active: false },
{ name: 'sylius.ui.payments'|trans, url: path('sylius_admin_payment_index'), active: false },
{ name: configuration.request.get('id'), active: false },
{ name: configuration.request.get('paymentId'), active: true },
{ name: 'sylius.ui.payment_requests'|trans, active: true },
]) }}

View file

@ -1,3 +1,3 @@
<div class="col-6">
<div class="col-12 col-md-6">
{{ form_row(hookable_metadata.context.form.code, sylius_test_form_attribute('code')) }}
</div>

View file

@ -1,5 +1,5 @@
{% if hookable_metadata.context.form.position is defined %}
<div class="col-6">
<div class="col-12 col-md-6">
{{ form_row(hookable_metadata.context.form.position, sylius_test_form_attribute('position')) }}
</div>
{% endif %}

View file

@ -1,3 +1,3 @@
<div class="col-6">
<div class="col-12 col-md-6">
{{ form_row(hookable_metadata.context.form.translatable, sylius_test_form_attribute('translatable')) }}
</div>

View file

@ -1,3 +1,3 @@
<div class="col-6">
<div class="col-12 col-md-6">
{{ form_row(hookable_metadata.context.form.type, sylius_test_form_attribute('type')) }}
</div>

View file

@ -1,3 +1,3 @@
<div class="row mt-3">
<div class="row mt-3 row-gap-2">
{% hook 'title_block' %}
</div>

View file

@ -1,5 +1,5 @@
<header class="navbar d-print-none">
<div class="container-xl d-flex">
<div class="container-xl d-flex gap-2 flex-nowrap">
{% hook 'sylius_admin.common.component.navbar' with { _prefixes: [] } %}
</div>
</header>

View file

@ -1,3 +1,3 @@
<div class="nav align-items-center gap-3">
<div class="nav align-items-center gap-3 flex-nowrap flex-shrink-0">
{% hook 'items' %}
</div>

View file

@ -1,3 +1,3 @@
<div class="d-flex flex-wrap sm-align-items-center gap-3" id="navbar-menu">
<div class="d-flex flex-wrap gap-3 flex-shrink-1" id="navbar-menu">
{% hook 'menu' %}
</div>

View file

@ -14,10 +14,10 @@
{% hook 'form' with { form } %}
</div>
<div class="row">
<div class="col-6">
<div class="col-12 col-md-6">
{% hook 'form#left' with { form } %}
</div>
<div class="col-6">
<div class="col-12 col-md-6">
{% hook 'form#right' with { form } %}
</div>
</div>

View file

@ -270,7 +270,6 @@ sylius_fixtures:
images:
- { path: '@SyliusCoreBundle/Resources/fixtures/caps/cap_13_1.jpeg', type: 'main' }
- { path: '@SyliusCoreBundle/Resources/fixtures/caps/cap_13_2.jpeg' }
- { path: '@SyliusCoreBundle/Resources/fixtures/caps/cap_13_3.jpeg' }
- name: 'Adventurous Aurora Cap'
tax_category: 'other'

Binary file not shown.

Before

Width:  |  Height:  |  Size: 100 KiB

After

Width:  |  Height:  |  Size: 161 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 214 KiB

After

Width:  |  Height:  |  Size: 171 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 263 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 245 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 382 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

View file

@ -1,4 +1,5 @@
.form-label.required::after {
.form-label.required::after,
.col-form-label.required::after {
content: "*";
padding-left: 3px;
color: $danger;

View file

@ -9,6 +9,7 @@
.sylius-messages:has(*) {
margin-top: 1.5rem;
margin-bottom: 1.5rem;
}
.alert {

View file

@ -1,3 +1,3 @@
<div class="mb-4">
{% hook 'sylius_shop.product.index.page.body.main.header' with { taxon } %}
{% hook 'sylius_shop.product.index.content.body.main.header' with { taxon } %}
</div>

View file

@ -3,7 +3,5 @@
{% block title %}{{ product.name }} | {{ parent() }}{% endblock %}
{% block content %}
<div class="container mt-4 mb-5">
{% hook 'sylius_shop.product.show' with { product, configuration } %}
</div>
{% hook 'sylius_shop.product.show' with { product, configuration } %}
{% endblock %}