@elasticias/ui 0.0.11 → 0.0.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1563,6 +1563,7 @@ class EfOrderBuilderComponent {
1563
1563
  onFieldChange(item) {
1564
1564
  const updatedItem = OrderLineItemHelper.updateCalculations(item);
1565
1565
  Object.assign(item, updatedItem);
1566
+ this.updateOrderLines([...this.orderLines()]);
1566
1567
  }
1567
1568
  saveEditingRow(item) {
1568
1569
  if (this.readonly())
@@ -1763,7 +1764,7 @@ class EfOrderBuilderComponent {
1763
1764
  return OrderEntityHelper.calculateTotals(this.order());
1764
1765
  }
1765
1766
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfOrderBuilderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1766
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: EfOrderBuilderComponent, isStandalone: true, selector: "ef-order-builder", inputs: { config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null }, order: { classPropertyName: "order", publicName: "order", isSignal: true, isRequired: true, transformFunction: null }, products: { classPropertyName: "products", publicName: "products", isSignal: true, isRequired: true, transformFunction: null }, customers: { classPropertyName: "customers", publicName: "customers", isSignal: true, isRequired: true, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, countGroupLabels: { classPropertyName: "countGroupLabels", publicName: "countGroupLabels", isSignal: true, isRequired: false, transformFunction: null }, productKeyField: { classPropertyName: "productKeyField", publicName: "productKeyField", isSignal: true, isRequired: false, transformFunction: null }, backendErrors: { classPropertyName: "backendErrors", publicName: "backendErrors", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { order: "orderChange", openCatalogue: "openCatalogue", orderChanged: "orderChanged", orderValidated: "orderValidated", productsAdded: "productsAdded" }, ngImport: i0, template: "<!-- Order Header -->\n<p-fieldset [legend]=\"config().headerLabel\">\n <div class=\"grid grid-cols-12 items-baseline gap-2 mb-2\">\n <ef-label [label]=\"config().customerLabel\" for=\"order.customerId\" styleClass=\"col-span-12 md:col-span-6 form-label-left\" required />\n <ef-label [label]=\"config().dateLabel\" for=\"order.orderDate\" styleClass=\"col-span-3 form-label-left\" required />\n </div>\n <div class=\"grid grid-cols-12 items-baseline gap-2 mb-2 mt-4\">\n <div class=\"col-span-12 md:col-span-6\">\n <ef-select\n [ngModel]=\"order().customerId\"\n (ngModelChange)=\"updateCustomer($event)\"\n name=\"order.customerId\"\n inputId=\"order.customerId\"\n [options]=\"customers()\"\n optionValue=\"customerId\"\n optionLabel=\"companyName\"\n [placeholder]=\"config().customerLabel\"\n filter\n showClear\n [disabled]=\"readonly()\"\n required\n />\n </div>\n <div class=\"col-span-3\">\n <ef-datepicker\n [ngModel]=\"order().orderDate\"\n (ngModelChange)=\"updateOrderDate($event)\"\n name=\"order.orderDate\"\n inputId=\"order.orderDate\"\n [disabled]=\"readonly()\"\n required\n />\n </div>\n </div>\n\n @if (config().enableQuickAdd && !readonly()) {\n <div class=\"grid grid-cols-12 items-baseline gap-2 mt-4 mb-2\">\n <ef-label labelKey=\"label.article\" for=\"quickAdd.product\" styleClass=\"col-span-12 md:col-span-6 form-label-left\" />\n <ef-label labelKey=\"label.sale_price\" for=\"quickAdd.price\" styleClass=\"col-span-2 form-label-left\" />\n <ef-label labelKey=\"label.quantity\" for=\"quickAdd.quantity\" styleClass=\"col-span-2 form-label-left\" />\n </div>\n <div class=\"grid grid-cols-12 items-center gap-2\">\n <div class=\"col-span-12 md:col-span-6\">\n <ef-select\n [ngModel]=\"quickAddProduct()\"\n (ngModelChange)=\"onQuickAddProductSelect($event)\"\n name=\"quickAdd.product\"\n inputId=\"quickAdd.product\"\n [options]=\"flattenedProductOptions()\"\n optionLabel=\"_displayLabel\"\n placeholderKey=\"placeholder.select_article\"\n filter\n showClear\n [disabled]=\"readonly()\"\n />\n </div>\n <div class=\"col-span-2\">\n <ef-inputnumber\n id=\"quickAdd.price\"\n name=\"quickAdd.price\"\n [ngModel]=\"quickAddPrice()\"\n (ngModelChange)=\"quickAddPrice.set($event)\"\n mode=\"currency\"\n [currency]=\"currency()\"\n [locale]=\"locale()\"\n [min]=\"0\"\n [disabled]=\"readonly()\"\n size=\"small\"\n />\n </div>\n <div class=\"col-span-2\">\n <ef-inputnumber\n id=\"quickAdd.quantity\"\n name=\"quickAdd.quantity\"\n [ngModel]=\"quickAddQuantity()\"\n (ngModelChange)=\"quickAddQuantity.set($event)\"\n [min]=\"1\"\n [maxFractionDigits]=\"4\"\n [locale]=\"locale()\"\n [disabled]=\"readonly()\"\n size=\"small\"\n />\n </div>\n <div class=\"col-span-2 flex justify-start gap-2\">\n <ef-button\n icon=\"pi pi-plus\"\n [rounded]=\"true\"\n severity=\"primary\"\n tooltipKey=\"tooltip.add_article\"\n [disabled]=\"quickAddDisabled()\"\n (clickEvent)=\"addQuickProduct()\"\n />\n @if (config().showCatalogue) {\n <ef-button\n icon=\"pi pi-search\"\n [rounded]=\"true\"\n severity=\"primary\"\n tooltipKey=\"tooltip.open_catalogue\"\n [disabled]=\"readonly()\"\n (clickEvent)=\"handleOpenCatalogue()\"\n />\n }\n </div>\n </div>\n }\n</p-fieldset>\n\n<div class=\"my-4\"></div>\n\n<!-- Order Line Items -->\n<p-fieldset legend=\"D\u00E9tails\">\n <p-table\n #pTable\n class=\"text-sm\"\n size=\"small\"\n [value]=\"orderLines()\"\n [rowsPerPageOptions]=\"[5, 10, 25, 50]\"\n [resizableColumns]=\"true\"\n [tableStyle]=\"{ 'min-width': '50rem' }\"\n columnResizeMode=\"expand\"\n [showCurrentPageReport]=\"true\"\n stateStorage=\"local\"\n dataKey=\"id\"\n [rowTrackBy]=\"rowTrackBy\"\n >\n <ng-template #header>\n <tr>\n <th>{{ 'label.article' | translate }}</th>\n <th class=\"w-[120px] !text-end\">{{ 'label.sale_price' | translate }}</th>\n <th class=\"w-[120px] !text-end\">{{ 'label.quantity' | translate }}</th>\n @if (config().enableTax) {\n <th class=\"w-[120px] !text-end\">TVA (%)</th>\n }\n @if (config().enableDiscount) {\n <th class=\"w-[120px] !text-end\">\n Remise (%)\n </th>\n }\n <th class=\"w-[120px] !text-end\">Sous-Total</th>\n @if (!readonly()) {\n <th class=\"w-10 !text-end\"></th>\n }\n </tr>\n </ng-template>\n\n <ng-template #body let-item let-rowIndex=\"rowIndex\">\n <!-- Editing mode for new row -->\n @if (item.isEditing) {\n <tr>\n <td>\n <div class=\"align-items-center flex gap-2\">\n <ef-select\n [(ngModel)]=\"item.product\"\n (ngModelChange)=\"onProductSelect(item)\"\n [name]=\"'item.product.' + rowIndex\"\n [inputId]=\"'item.product.' + rowIndex\"\n [options]=\"flattenedProductOptions()\"\n optionLabel=\"_displayLabel\"\n placeholderKey=\"placeholder.select_article\"\n filter\n showClear\n [disabled]=\"readonly()\"\n appendTo=\"body\"\n />\n </div>\n </td>\n <td class=\"w-[120px]\">\n <ef-inputnumber\n [id]=\"'item.productUnitPrice.' + rowIndex\"\n [name]=\"'item.productUnitPrice.' + rowIndex\"\n [(ngModel)]=\"item.productUnitPrice\"\n (ngModelChange)=\"onFieldChange(item)\"\n (onBlur)=\"stopEditField(item)\"\n mode=\"currency\"\n [currency]=\"currency()\"\n [locale]=\"locale()\"\n [disabled]=\"readonly()\"\n size=\"small\"\n />\n </td>\n <td class=\"w-[120px]\">\n <ef-inputnumber\n [id]=\"'item.quantity.' + rowIndex\"\n [name]=\"'item.quantity.' + rowIndex\"\n [(ngModel)]=\"item.quantity\"\n (ngModelChange)=\"onFieldChange(item)\"\n (onBlur)=\"saveEditingRow(item)\"\n [min]=\"1\"\n [maxFractionDigits]=\"4\"\n [locale]=\"locale()\"\n [disabled]=\"readonly()\"\n size=\"small\"\n />\n </td>\n @if (config().enableTax) {\n <td class=\"w-[120px] !text-end\">\n {{ item.taxRate || 0 }} %\n </td>\n }\n @if (config().enableDiscount) {\n <td class=\"w-[120px]\">\n <ef-inputnumber\n [id]=\"'item.discount.' + rowIndex\"\n [name]=\"'item.discount.' + rowIndex\"\n [(ngModel)]=\"item.discount\"\n (ngModelChange)=\"onFieldChange(item)\"\n (onBlur)=\"saveEditingRow(item)\"\n [min]=\"0\"\n [max]=\"100\"\n suffix=\"%\"\n [disabled]=\"readonly()\"\n size=\"small\"\n />\n </td>\n }\n <td class=\"w-[120px] !text-end\">\n {{ calculateSubtotal(item) | currency: currency() }}\n </td>\n @if (!readonly()) {\n <td class=\"w-10 !text-end\">\n <p-button\n icon=\"pi pi-trash\"\n size=\"small\"\n [outlined]=\"true\"\n pTooltip=\"Supprimer\"\n tooltipPosition=\"top\"\n (click)=\"removeOrderLine(item.id)\"\n />\n </td>\n }\n </tr>\n }\n\n <!-- Display mode for saved row -->\n @if (!item.isEditing) {\n <tr>\n <td>\n <div\n [innerHTML]=\"AppUtils.truncate(item.productDescription)\"\n [pTooltip]=\"item.productDescription\"\n tooltipPosition=\"top\"\n ></div>\n </td>\n <td\n class=\"w-[120px]\"\n [class.cursor-pointer]=\"config().allowInlineEdit && !readonly()\"\n (click)=\"startEditField(item, 'productUnitPrice')\"\n >\n @if (\n item.editingField === 'productUnitPrice' &&\n config().allowInlineEdit &&\n !readonly()\n ) {\n <ef-inputnumber\n [id]=\"'item.productUnitPrice.edit.' + rowIndex\"\n [name]=\"'item.productUnitPrice.edit.' + rowIndex\"\n [(ngModel)]=\"item.productUnitPrice\"\n (ngModelChange)=\"onFieldChange(item)\"\n (onBlur)=\"stopEditField(item)\"\n mode=\"currency\"\n [currency]=\"currency()\"\n [locale]=\"locale()\"\n size=\"small\"\n class=\"!text-end\"\n />\n } @else {\n <span\n [class]=\"\n config().allowInlineEdit && !readonly()\n ? 'block cursor-pointer rounded px-2 py-1 text-end hover:bg-surface-100 dark:hover:bg-surface-700'\n : 'block px-2 py-1 text-end'\n \"\n >\n {{ item.productUnitPrice | currency: currency() }}\n </span>\n }\n </td>\n <td\n class=\"w-[120px]\"\n [class.cursor-pointer]=\"config().allowInlineEdit && !readonly()\"\n (click)=\"startEditField(item, 'quantity')\"\n >\n @if (\n item.editingField === 'quantity' &&\n config().allowInlineEdit &&\n !readonly()\n ) {\n <ef-inputnumber\n [id]=\"'item.quantity.edit.' + rowIndex\"\n [name]=\"'item.quantity.edit.' + rowIndex\"\n [(ngModel)]=\"item.quantity\"\n (ngModelChange)=\"onFieldChange(item)\"\n (onBlur)=\"stopEditField(item)\"\n [min]=\"1\"\n [maxFractionDigits]=\"4\"\n [locale]=\"locale()\"\n size=\"small\"\n class=\"!text-end\"\n />\n } @else {\n <span\n [class]=\"\n config().allowInlineEdit && !readonly()\n ? 'block cursor-pointer rounded px-2 py-1 text-end hover:bg-surface-100 dark:hover:bg-surface-700'\n : 'block px-2 py-1 text-end'\n \"\n >\n {{ item.quantity | number: '1.4-4' : locale() }}\n </span>\n }\n </td>\n @if (config().enableTax) {\n <td class=\"w-[120px] !text-end\">\n {{ item.taxRate || 0 }} %\n </td>\n }\n @if (config().enableDiscount) {\n <td\n class=\"w-[120px]\"\n [class.cursor-pointer]=\"config().allowInlineEdit && !readonly()\"\n (click)=\"startEditField(item, 'discount')\"\n >\n @if (\n item.editingField === 'discount' &&\n config().allowInlineEdit &&\n !readonly()\n ) {\n <ef-inputnumber\n [id]=\"'item.discount.edit.' + rowIndex\"\n [name]=\"'item.discount.edit.' + rowIndex\"\n [(ngModel)]=\"item.discount\"\n (ngModelChange)=\"onFieldChange(item)\"\n (onBlur)=\"stopEditField(item)\"\n [min]=\"0\"\n [max]=\"100\"\n suffix=\"%\"\n size=\"small\"\n class=\"!text-end\"\n />\n } @else {\n <span\n [class]=\"\n config().allowInlineEdit && !readonly()\n ? 'block cursor-pointer rounded px-2 py-1 text-end hover:bg-surface-100 dark:hover:bg-surface-700'\n : 'block px-2 py-1 text-end'\n \"\n >\n {{ item.discount || 0 }}%\n </span>\n }\n </td>\n }\n <td class=\"w-[120px] !text-end\">\n {{ item.grossAmount | currency: currency() }}\n </td>\n @if (!readonly()) {\n <td class=\"w-10 !text-end\">\n <p-button\n icon=\"pi pi-trash\"\n size=\"small\"\n [outlined]=\"true\"\n tooltipPosition=\"top\"\n pTooltip=\"Supprimer\"\n (click)=\"removeOrderLine(item.id)\"\n />\n </td>\n }\n </tr>\n }\n </ng-template>\n\n <ng-template #footer>\n @if (!readonly()) {\n <tr>\n <td\n [attr.colspan]=\"\n config().enableTax && config().enableDiscount\n ? 7\n : config().enableTax || config().enableDiscount\n ? 6\n : 5\n \"\n >\n <a\n href=\"javascript:void(0)\"\n class=\"inline-flex items-center gap-2 text-sm font-medium text-primary-500 hover:text-primary-700\"\n [class.opacity-50]=\"hasEditingRow()\"\n [class.pointer-events-none]=\"hasEditingRow()\"\n (click)=\"addNewProductRow()\"\n >\n <i class=\"pi pi-plus text-xs\"></i>\n Ajouter un article\n </a>\n @if (config().showCatalogue) {\n <ng-container>\n &nbsp; | &nbsp;\n <a\n href=\"javascript:void(0)\"\n class=\"inline-flex items-center gap-2 text-sm font-medium text-primary-500 hover:text-primary-700\"\n (click)=\"handleOpenCatalogue()\"\n >\n <i class=\"pi pi-search text-xs\"></i>\n Ouvrir le catalogue des articles\n </a>\n </ng-container>\n }\n </td>\n </tr>\n }\n\n <!-- Totals Section -->\n <tr class=\"border-t\">\n <td\n [attr.colspan]=\"\n config().enableTax && config().enableDiscount\n ? 5\n : config().enableTax || config().enableDiscount\n ? 4\n : 3\n \"\n >\n @if (config().showProductsSummary) {\n <div class=\"w-[500px]\">\n <div class=\"my-4\"></div>\n <ef-order-summary\n [orderLines]=\"orderLines()\"\n [countGroupLabels]=\"countGroupLabels()\"\n legend=\"R\u00E9capitulatif\"\n />\n </div>\n }\n </td>\n <td [attr.colspan]=\"readonly() ? 2 : 3\" class=\"!text-end\">\n <p-fieldset [legend]=\"'Totaux'\">\n <div class=\"flex flex-col gap-2\">\n <div class=\"flex items-end justify-between gap-4\">\n <span\n class=\"font-semibold text-surface-700 dark:text-surface-300\"\n >\n Montant brut :\n </span>\n <span class=\"font-semibold\">\n {{ grossTotal() | currency: currency() }}\n </span>\n </div>\n @if (config().enableDiscount) {\n <div class=\"flex justify-between gap-4\">\n <span\n class=\"font-semibold text-surface-700 dark:text-surface-300\"\n >\n Remise :\n </span>\n <span class=\"font-semibold\">\n {{ discountTotal() | currency: currency() }}\n </span>\n </div>\n }\n @if (config().enableTax) {\n <div class=\"flex justify-between gap-4\">\n <span\n class=\"font-semibold text-surface-700 dark:text-surface-300\"\n >\n TVA :\n </span>\n <span class=\"font-semibold\">\n {{ tvaTotal() | currency: currency() }}\n </span>\n </div>\n }\n <p-divider class=\"!my-1\" />\n <div class=\"flex justify-between gap-4\">\n <span class=\"text-lg font-bold text-primary\">\n Montant Total :\n </span>\n <span class=\"text-lg font-bold text-primary\">\n {{ finalTotal() | currency: currency() }}\n </span>\n </div>\n </div>\n </p-fieldset>\n </td>\n </tr>\n </ng-template>\n </p-table>\n</p-fieldset>\n\n<!-- Optional Notes Section -->\n@if (config().showNotes) {\n <p-fieldset legend=\"Notes\">\n <textarea\n pTextarea\n id=\"order.notes\"\n name=\"order.notes\"\n [ngModel]=\"order().notes\"\n (ngModelChange)=\"updateNotes($event)\"\n [disabled]=\"readonly()\"\n [placeholder]=\"'common.notes_placeholder' | translate\"\n rows=\"5\"\n class=\"w-full\"\n ></textarea>\n </p-fieldset>\n}\n\n<!-- Validation Messages -->\n@if (hasErrors()) {\n <div\n class=\"my-4 rounded border border-red-200 bg-red-50 p-4 dark:bg-red-900/20\"\n >\n <div class=\"flex items-start gap-2\">\n <i\n class=\"pi pi-exclamation-triangle mt-1 text-red-600 dark:text-red-400\"\n ></i>\n <div class=\"flex-1\">\n <h4 class=\"mb-2 font-semibold text-red-800 dark:text-red-200\">\n Erreurs de validation\n </h4>\n <ul class=\"list-inside list-disc text-sm text-red-700 dark:text-red-300\">\n @for (error of combinedErrors() | keyvalue; track error.key) {\n <li>\n {{ error.value[0] }}\n </li>\n }\n </ul>\n </div>\n </div>\n </div>\n}\n\n<!-- Change Indicator -->\n@if (hasChanges()) {\n <div\n class=\"my-4 flex items-center gap-2 rounded border border-orange-200 bg-orange-50 p-3 dark:bg-orange-900/20\"\n >\n <i class=\"pi pi-info-circle text-orange-600 dark:text-orange-400\"></i>\n <span class=\"text-sm text-orange-800 dark:text-orange-200\">\n Modifications non sauvegard\u00E9es d\u00E9tect\u00E9es\n </span>\n </div>\n}\n", styles: [".inline-edit-cell{position:relative}.inline-edit-cell .edit-indicator{position:absolute;top:2px;right:2px;font-size:.65rem;opacity:0;transition:opacity .2s ease}.inline-edit-cell:hover .edit-indicator{opacity:.5}.cursor-pointer{cursor:pointer;transition:background-color .2s ease}.cursor-pointer:hover{background-color:#0000000d}:host-context(.dark-mode) .cursor-pointer:hover{background-color:#ffffff0d}.cursor-pointer:active{transform:scale(.98)}::ng-deep .p-datatable .p-datatable-thead>tr>th{font-weight:600;text-transform:uppercase;font-size:.75rem;letter-spacing:.05em}::ng-deep .p-datatable .editing-row{background-color:rgba(var(--primary-500),.05);border-left:3px solid var(--primary-500)}::ng-deep .p-datatable .w-\\[120px\\]{width:120px;min-width:120px;max-width:120px}::ng-deep .p-datatable .w-20{width:5rem;min-width:5rem;max-width:5rem}::ng-deep .p-datatable .\\!text-end{text-align:right!important;justify-content:flex-end}.totals-section{background-color:rgba(var(--surface-0),.5);border-top:2px solid var(--surface-border);padding:1rem}.totals-section .totals-row{display:flex;justify-content:space-between;align-items:center;padding:.5rem 0;gap:1rem}.totals-section .totals-row.total-final{border-top:2px solid var(--primary-500);padding-top:.75rem;margin-top:.5rem;font-size:1.125rem;font-weight:700;color:var(--primary-500)}.totals-section .totals-label{color:var(--text-color-secondary);font-weight:600}.totals-section .totals-value{font-weight:600;font-variant-numeric:tabular-nums}.add-product-link{display:inline-flex;align-items:center;gap:.5rem;font-size:.875rem;font-weight:500;color:var(--primary-500);text-decoration:none;transition:color .2s ease}.add-product-link:hover{color:var(--primary-700)}.add-product-link.disabled{opacity:.5;pointer-events:none;cursor:not-allowed}.add-product-link i{font-size:.75rem}.validation-message{padding:1rem;margin-bottom:1rem;border-radius:4px}.validation-message.error{background-color:rgba(var(--red-50),1);border:1px solid rgba(var(--red-200),1);color:var(--red-800)}:host-context(.dark-mode) .validation-message.error{background-color:rgba(var(--red-900),.2);color:var(--red-200)}.validation-message.error .icon{color:var(--red-600)}:host-context(.dark-mode) .validation-message.error .icon{color:var(--red-400)}.validation-message.warning{background-color:rgba(var(--orange-50),1);border:1px solid rgba(var(--orange-200),1);color:var(--orange-800)}:host-context(.dark-mode) .validation-message.warning{background-color:rgba(var(--orange-900),.2);color:var(--orange-200)}.validation-message.warning .icon{color:var(--orange-600)}:host-context(.dark-mode) .validation-message.warning .icon{color:var(--orange-400)}.validation-message.info{background-color:rgba(var(--blue-50),1);border:1px solid rgba(var(--blue-200),1);color:var(--blue-800)}:host-context(.dark-mode) .validation-message.info{background-color:rgba(var(--blue-900),.2);color:var(--blue-200)}.validation-message.info .icon{color:var(--blue-600)}:host-context(.dark-mode) .validation-message.info .icon{color:var(--blue-400)}::ng-deep .p-inputnumber-input.currency-input{text-align:right}::ng-deep .p-inputnumber.discount-input .p-inputnumber-input{text-align:right}@media(max-width:768px){::ng-deep .p-datatable{font-size:.875rem}::ng-deep .p-datatable .w-\\[120px\\]{width:100px;min-width:100px;max-width:100px}.totals-section{font-size:.875rem}.totals-section .totals-row.total-final{font-size:1rem}}@media(max-width:576px){::ng-deep .p-datatable .p-datatable-thead>tr>th{font-size:.7rem;padding:.5rem .25rem}::ng-deep .p-datatable .p-datatable-tbody>tr>td{padding:.5rem .25rem}}:focus-visible{outline:2px solid var(--primary-500);outline-offset:2px;border-radius:4px}@media(prefers-contrast:high){.cursor-pointer:hover{background-color:#0000001a}.validation-message{border-width:2px}}@media(prefers-reduced-motion:reduce){*{transition-duration:.01ms!important;animation-duration:.01ms!important}}@media print{.add-product-link,.p-button,.inline-edit-cell:hover,.cursor-pointer{display:none!important}.validation-message{display:none}::ng-deep .p-datatable .p-datatable-thead>tr>th,::ng-deep .p-datatable .p-datatable-tbody>tr>td{border:1px solid #000}}.loading-overlay{position:absolute;inset:0;background-color:#fffc;display:flex;align-items:center;justify-content:center;z-index:1000}:host-context(.dark-mode) .loading-overlay{background-color:#000c}.loading-overlay .spinner{width:3rem;height:3rem;border:3px solid var(--surface-border);border-top-color:var(--primary-500);border-radius:50%;animation:spin 1s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.empty-state{text-align:center;padding:3rem 1rem;color:var(--text-color-secondary)}.empty-state .empty-icon{font-size:3rem;margin-bottom:1rem;opacity:.3}.empty-state .empty-title{font-size:1.125rem;font-weight:600;margin-bottom:.5rem}.empty-state .empty-message{font-size:.875rem;margin-bottom:1.5rem}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "ngmodule", type: TextareaModule }, { kind: "directive", type: i2$2.Textarea, selector: "[pTextarea], [pInputTextarea]", inputs: ["pTextareaPT", "pTextareaUnstyled", "autoResize", "pSize", "variant", "fluid", "invalid"], outputs: ["onResize"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: TableModule }, { kind: "component", type: i2$3.Table, selector: "p-table", inputs: ["frozenColumns", "frozenValue", "styleClass", "tableStyle", "tableStyleClass", "paginator", "pageLinks", "rowsPerPageOptions", "alwaysShowPaginator", "paginatorPosition", "paginatorStyleClass", "paginatorDropdownAppendTo", "paginatorDropdownScrollHeight", "currentPageReportTemplate", "showCurrentPageReport", "showJumpToPageDropdown", "showJumpToPageInput", "showFirstLastIcon", "showPageLinks", "defaultSortOrder", "sortMode", "resetPageOnSort", "selectionMode", "selectionPageOnly", "contextMenuSelection", "contextMenuSelectionMode", "dataKey", "metaKeySelection", "rowSelectable", "rowTrackBy", "lazy", "lazyLoadOnInit", "compareSelectionBy", "csvSeparator", "exportFilename", "filters", "globalFilterFields", "filterDelay", "filterLocale", "expandedRowKeys", "editingRowKeys", "rowExpandMode", "scrollable", "rowGroupMode", "scrollHeight", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "virtualScrollDelay", "frozenWidth", "contextMenu", "resizableColumns", "columnResizeMode", "reorderableColumns", "loading", "loadingIcon", "showLoader", "rowHover", "customSort", "showInitialSortBadge", "exportFunction", "exportHeader", "stateKey", "stateStorage", "editMode", "groupRowsBy", "size", "showGridlines", "stripedRows", "groupRowsByOrder", "responsiveLayout", "breakpoint", "paginatorLocale", "value", "columns", "first", "rows", "totalRecords", "sortField", "sortOrder", "multiSortMeta", "selection", "selectAll"], outputs: ["contextMenuSelectionChange", "selectAllChange", "selectionChange", "onRowSelect", "onRowUnselect", "onPage", "onSort", "onFilter", "onLazyLoad", "onRowExpand", "onRowCollapse", "onContextMenuSelect", "onColResize", "onColReorder", "onRowReorder", "onEditInit", "onEditComplete", "onEditCancel", "onHeaderCheckboxToggle", "sortFunction", "firstChange", "rowsChange", "onStateSave", "onStateRestore"] }, { kind: "ngmodule", type: FieldsetModule }, { kind: "component", type: i1$1.Fieldset, selector: "p-fieldset", inputs: ["legend", "toggleable", "style", "styleClass", "transitionOptions", "motionOptions", "collapsed"], outputs: ["collapsedChange", "onBeforeToggle", "onAfterToggle"] }, { kind: "ngmodule", type: DividerModule }, { kind: "component", type: i6.Divider, selector: "p-divider", inputs: ["styleClass", "layout", "type", "align"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i2$1.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "showOnEllipsis", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo", "ptTooltip", "pTooltipPT", "pTooltipUnstyled"] }, { kind: "component", type: EfOrderSummaryComponent, selector: "ef-order-summary", inputs: ["orderLines", "countGroupLabels", "legend"] }, { kind: "component", type: EfSelectComponent, selector: "ef-select", inputs: ["label", "labelKey", "inputId", "name", "options", "optionLabel", "optionValue", "placeholder", "placeholderKey", "size", "required", "fluid", "inline", "filter", "showClear", "disabled", "appendTo", "filterByKeys"], outputs: ["selectionChangeEvent"] }, { kind: "component", type: EfDatepickerComponent, selector: "ef-datepicker", inputs: ["label", "labelKey", "inputId", "name", "placeholder", "placeholderKey", "size", "required", "fluid", "inline", "disabled", "showIcon", "iconDisplay", "selectionMode", "dateFormat", "appendTo", "showTime", "showButtonBar", "minDate", "maxDate"], outputs: ["dateSelectEvent"] }, { kind: "component", type: EfInputNumberComponent, selector: "ef-inputnumber", inputs: ["label", "labelKey", "required", "value", "invalid", "disabled", "readonly", "format", "showButtons", "buttonLayout", "incrementButtonClass", "decrementButtonClass", "incrementButtonIcon", "decrementButtonIcon", "prefix", "suffix", "currency", "currencyDisplay", "locale", "localeMatcher", "mode", "useGrouping", "minFractionDigits", "maxFractionDigits", "min", "max", "step", "allowEmpty", "inputId", "name", "placeholder", "placeholderKey", "inputSize", "size", "maxlength", "minlength", "pattern", "tabindex", "title", "ariaLabel", "ariaLabelledBy", "ariaDescribedBy", "ariaRequired", "inputStyle", "inputStyleClass", "style", "styleClass", "showClear", "variant", "autofocus", "autocomplete", "fluid", "inline", "pt", "ptOptions", "dt", "unstyled"], outputs: ["inputEvent", "focusEvent", "blurEvent", "keyDownEvent", "clearEvent"] }, { kind: "component", type: EfLabelComponent, selector: "ef-label", inputs: ["label", "labelKey", "for", "styleClass", "required"] }, { kind: "component", type: EfButtonComponent, selector: "ef-button", inputs: ["label", "labelKey", "icon", "severity", "size", "disabled", "rounded", "outlined", "raised", "text", "loading", "styleClass", "type", "badge", "badgeSeverity", "tooltip", "tooltipKey", "tooltipPosition"], outputs: ["clickEvent"] }, { kind: "pipe", type: i8.DecimalPipe, name: "number" }, { kind: "pipe", type: i8.CurrencyPipe, name: "currency" }, { kind: "pipe", type: i8.KeyValuePipe, name: "keyvalue" }, { kind: "pipe", type: i3.TranslatePipe, name: "translate" }] });
1767
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: EfOrderBuilderComponent, isStandalone: true, selector: "ef-order-builder", inputs: { config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null }, order: { classPropertyName: "order", publicName: "order", isSignal: true, isRequired: true, transformFunction: null }, products: { classPropertyName: "products", publicName: "products", isSignal: true, isRequired: true, transformFunction: null }, customers: { classPropertyName: "customers", publicName: "customers", isSignal: true, isRequired: true, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, countGroupLabels: { classPropertyName: "countGroupLabels", publicName: "countGroupLabels", isSignal: true, isRequired: false, transformFunction: null }, productKeyField: { classPropertyName: "productKeyField", publicName: "productKeyField", isSignal: true, isRequired: false, transformFunction: null }, backendErrors: { classPropertyName: "backendErrors", publicName: "backendErrors", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { order: "orderChange", openCatalogue: "openCatalogue", orderChanged: "orderChanged", orderValidated: "orderValidated", productsAdded: "productsAdded" }, ngImport: i0, template: "<!-- Order Header -->\n<p-fieldset [legend]=\"config().headerLabel\">\n <div class=\"grid grid-cols-12 items-baseline gap-2 mb-2\">\n <ef-label [label]=\"config().customerLabel\" for=\"order.customerId\" styleClass=\"col-span-12 md:col-span-6 form-label-left\" required />\n <ef-label [label]=\"config().dateLabel\" for=\"order.orderDate\" styleClass=\"col-span-3 form-label-left\" required />\n </div>\n <div class=\"grid grid-cols-12 items-baseline gap-2 mb-2 mt-4\">\n <div class=\"col-span-12 md:col-span-6\">\n <ef-select\n [ngModel]=\"order().customerId\"\n (ngModelChange)=\"updateCustomer($event)\"\n name=\"order.customerId\"\n inputId=\"order.customerId\"\n [options]=\"customers()\"\n optionValue=\"customerId\"\n optionLabel=\"companyName\"\n [placeholder]=\"config().customerLabel\"\n filter\n showClear\n [disabled]=\"readonly()\"\n required\n />\n </div>\n <div class=\"col-span-3\">\n <ef-datepicker\n [ngModel]=\"order().orderDate\"\n (ngModelChange)=\"updateOrderDate($event)\"\n name=\"order.orderDate\"\n inputId=\"order.orderDate\"\n [disabled]=\"readonly()\"\n required\n />\n </div>\n </div>\n\n @if (config().enableQuickAdd && !readonly()) {\n <div class=\"grid grid-cols-12 items-baseline gap-2 mt-4 mb-2\">\n <ef-label labelKey=\"label.article\" for=\"quickAdd.product\" styleClass=\"col-span-12 md:col-span-6 form-label-left\" />\n <ef-label labelKey=\"label.sale_price\" for=\"quickAdd.price\" styleClass=\"col-span-2 form-label-left\" />\n <ef-label labelKey=\"label.quantity\" for=\"quickAdd.quantity\" styleClass=\"col-span-2 form-label-left\" />\n </div>\n <div class=\"grid grid-cols-12 items-center gap-2\">\n <div class=\"col-span-12 md:col-span-6\">\n <ef-select\n [ngModel]=\"quickAddProduct()\"\n (ngModelChange)=\"onQuickAddProductSelect($event)\"\n name=\"quickAdd.product\"\n inputId=\"quickAdd.product\"\n [options]=\"flattenedProductOptions()\"\n optionLabel=\"_displayLabel\"\n placeholderKey=\"placeholder.select_article\"\n filter\n showClear\n [disabled]=\"readonly()\"\n />\n </div>\n <div class=\"col-span-2\">\n <ef-inputnumber\n id=\"quickAdd.price\"\n name=\"quickAdd.price\"\n [ngModel]=\"quickAddPrice()\"\n (ngModelChange)=\"quickAddPrice.set($event)\"\n mode=\"currency\"\n [currency]=\"currency()\"\n [locale]=\"locale()\"\n [min]=\"0\"\n [disabled]=\"readonly()\"\n size=\"small\"\n />\n </div>\n <div class=\"col-span-2\">\n <ef-inputnumber\n id=\"quickAdd.quantity\"\n name=\"quickAdd.quantity\"\n [ngModel]=\"quickAddQuantity()\"\n (ngModelChange)=\"quickAddQuantity.set($event)\"\n [min]=\"0\"\n [maxFractionDigits]=\"4\"\n [locale]=\"locale()\"\n [disabled]=\"readonly()\"\n size=\"small\"\n />\n </div>\n <div class=\"col-span-2 flex justify-start gap-2\">\n <ef-button\n icon=\"pi pi-plus\"\n [rounded]=\"true\"\n severity=\"primary\"\n tooltipKey=\"tooltip.add_article\"\n [disabled]=\"quickAddDisabled()\"\n (clickEvent)=\"addQuickProduct()\"\n />\n @if (config().showCatalogue) {\n <ef-button\n icon=\"pi pi-search\"\n [rounded]=\"true\"\n severity=\"primary\"\n tooltipKey=\"tooltip.open_catalogue\"\n [disabled]=\"readonly()\"\n (clickEvent)=\"handleOpenCatalogue()\"\n />\n }\n </div>\n </div>\n }\n</p-fieldset>\n\n<div class=\"my-4\"></div>\n\n<!-- Order Line Items -->\n<p-fieldset legend=\"D\u00E9tails\">\n <p-table\n #pTable\n class=\"text-sm\"\n size=\"small\"\n [value]=\"orderLines()\"\n [rowsPerPageOptions]=\"[5, 10, 25, 50]\"\n [resizableColumns]=\"true\"\n [tableStyle]=\"{ 'min-width': '50rem' }\"\n columnResizeMode=\"expand\"\n [showCurrentPageReport]=\"true\"\n stateStorage=\"local\"\n dataKey=\"id\"\n [rowTrackBy]=\"rowTrackBy\"\n >\n <ng-template #header>\n <tr>\n <th>{{ 'label.article' | translate }}</th>\n <th class=\"w-[120px] !text-end\">{{ 'label.sale_price' | translate }}</th>\n <th class=\"w-[120px] !text-end\">{{ 'label.quantity' | translate }}</th>\n @if (config().enableTax) {\n <th class=\"w-[120px] !text-end\">TVA (%)</th>\n }\n @if (config().enableDiscount) {\n <th class=\"w-[120px] !text-end\">\n Remise (%)\n </th>\n }\n <th class=\"w-[120px] !text-end\">Sous-Total</th>\n @if (!readonly()) {\n <th class=\"w-10 !text-end\"></th>\n }\n </tr>\n </ng-template>\n\n <ng-template #body let-item let-rowIndex=\"rowIndex\">\n <!-- Editing mode for new row -->\n @if (item.isEditing) {\n <tr>\n <td>\n <div class=\"align-items-center flex gap-2\">\n <ef-select\n [(ngModel)]=\"item.product\"\n (ngModelChange)=\"onProductSelect(item)\"\n [name]=\"'item.product.' + rowIndex\"\n [inputId]=\"'item.product.' + rowIndex\"\n [options]=\"flattenedProductOptions()\"\n optionLabel=\"_displayLabel\"\n placeholderKey=\"placeholder.select_article\"\n filter\n showClear\n [disabled]=\"readonly()\"\n appendTo=\"body\"\n />\n </div>\n </td>\n <td class=\"w-[120px]\">\n <ef-inputnumber\n [id]=\"'item.productUnitPrice.' + rowIndex\"\n [name]=\"'item.productUnitPrice.' + rowIndex\"\n [(ngModel)]=\"item.productUnitPrice\"\n (ngModelChange)=\"onFieldChange(item)\"\n (onBlur)=\"stopEditField(item)\"\n mode=\"currency\"\n [currency]=\"currency()\"\n [locale]=\"locale()\"\n [disabled]=\"readonly()\"\n size=\"small\"\n />\n </td>\n <td class=\"w-[120px]\">\n <ef-inputnumber\n [id]=\"'item.quantity.' + rowIndex\"\n [name]=\"'item.quantity.' + rowIndex\"\n [(ngModel)]=\"item.quantity\"\n (ngModelChange)=\"onFieldChange(item)\"\n (onBlur)=\"saveEditingRow(item)\"\n [min]=\"0\"\n [maxFractionDigits]=\"4\"\n [locale]=\"locale()\"\n [disabled]=\"readonly()\"\n size=\"small\"\n />\n </td>\n @if (config().enableTax) {\n <td class=\"w-[120px] !text-end\">\n {{ item.taxRate || 0 }} %\n </td>\n }\n @if (config().enableDiscount) {\n <td class=\"w-[120px]\">\n <ef-inputnumber\n [id]=\"'item.discount.' + rowIndex\"\n [name]=\"'item.discount.' + rowIndex\"\n [(ngModel)]=\"item.discount\"\n (ngModelChange)=\"onFieldChange(item)\"\n (onBlur)=\"saveEditingRow(item)\"\n [min]=\"0\"\n [max]=\"100\"\n suffix=\"%\"\n [disabled]=\"readonly()\"\n size=\"small\"\n />\n </td>\n }\n <td class=\"w-[120px] !text-end\">\n {{ calculateSubtotal(item) | currency: currency() }}\n </td>\n @if (!readonly()) {\n <td class=\"w-10 !text-end\">\n <p-button\n icon=\"pi pi-trash\"\n size=\"small\"\n [outlined]=\"true\"\n pTooltip=\"Supprimer\"\n tooltipPosition=\"top\"\n (click)=\"removeOrderLine(item.id)\"\n />\n </td>\n }\n </tr>\n }\n\n <!-- Display mode for saved row -->\n @if (!item.isEditing) {\n <tr>\n <td>\n <div\n [innerHTML]=\"AppUtils.truncate(item.productDescription)\"\n [pTooltip]=\"item.productDescription\"\n tooltipPosition=\"top\"\n ></div>\n </td>\n <td\n class=\"w-[120px]\"\n [class.cursor-pointer]=\"config().allowInlineEdit && !readonly()\"\n (click)=\"startEditField(item, 'productUnitPrice')\"\n >\n @if (\n item.editingField === 'productUnitPrice' &&\n config().allowInlineEdit &&\n !readonly()\n ) {\n <ef-inputnumber\n [id]=\"'item.productUnitPrice.edit.' + rowIndex\"\n [name]=\"'item.productUnitPrice.edit.' + rowIndex\"\n [(ngModel)]=\"item.productUnitPrice\"\n (ngModelChange)=\"onFieldChange(item)\"\n (onBlur)=\"stopEditField(item)\"\n mode=\"currency\"\n [currency]=\"currency()\"\n [locale]=\"locale()\"\n size=\"small\"\n class=\"!text-end\"\n />\n } @else {\n <span\n [class]=\"\n config().allowInlineEdit && !readonly()\n ? 'block cursor-pointer rounded px-2 py-1 text-end hover:bg-surface-100 dark:hover:bg-surface-700'\n : 'block px-2 py-1 text-end'\n \"\n >\n {{ item.productUnitPrice | currency: currency() }}\n </span>\n }\n </td>\n <td\n class=\"w-[120px]\"\n [class.cursor-pointer]=\"config().allowInlineEdit && !readonly()\"\n (click)=\"startEditField(item, 'quantity')\"\n >\n @if (\n item.editingField === 'quantity' &&\n config().allowInlineEdit &&\n !readonly()\n ) {\n <ef-inputnumber\n [id]=\"'item.quantity.edit.' + rowIndex\"\n [name]=\"'item.quantity.edit.' + rowIndex\"\n [(ngModel)]=\"item.quantity\"\n (ngModelChange)=\"onFieldChange(item)\"\n (onBlur)=\"stopEditField(item)\"\n [min]=\"0\"\n [maxFractionDigits]=\"4\"\n [locale]=\"locale()\"\n size=\"small\"\n class=\"!text-end\"\n />\n } @else {\n <span\n [class]=\"\n config().allowInlineEdit && !readonly()\n ? 'block cursor-pointer rounded px-2 py-1 text-end hover:bg-surface-100 dark:hover:bg-surface-700'\n : 'block px-2 py-1 text-end'\n \"\n >\n {{ item.quantity | number: '1.4-4' : locale() }}\n </span>\n }\n </td>\n @if (config().enableTax) {\n <td class=\"w-[120px] !text-end\">\n {{ item.taxRate || 0 }} %\n </td>\n }\n @if (config().enableDiscount) {\n <td\n class=\"w-[120px]\"\n [class.cursor-pointer]=\"config().allowInlineEdit && !readonly()\"\n (click)=\"startEditField(item, 'discount')\"\n >\n @if (\n item.editingField === 'discount' &&\n config().allowInlineEdit &&\n !readonly()\n ) {\n <ef-inputnumber\n [id]=\"'item.discount.edit.' + rowIndex\"\n [name]=\"'item.discount.edit.' + rowIndex\"\n [(ngModel)]=\"item.discount\"\n (ngModelChange)=\"onFieldChange(item)\"\n (onBlur)=\"stopEditField(item)\"\n [min]=\"0\"\n [max]=\"100\"\n suffix=\"%\"\n size=\"small\"\n class=\"!text-end\"\n />\n } @else {\n <span\n [class]=\"\n config().allowInlineEdit && !readonly()\n ? 'block cursor-pointer rounded px-2 py-1 text-end hover:bg-surface-100 dark:hover:bg-surface-700'\n : 'block px-2 py-1 text-end'\n \"\n >\n {{ item.discount || 0 }}%\n </span>\n }\n </td>\n }\n <td class=\"w-[120px] !text-end\">\n {{ item.grossAmount | currency: currency() }}\n </td>\n @if (!readonly()) {\n <td class=\"w-10 !text-end\">\n <p-button\n icon=\"pi pi-trash\"\n size=\"small\"\n [outlined]=\"true\"\n tooltipPosition=\"top\"\n pTooltip=\"Supprimer\"\n (click)=\"removeOrderLine(item.id)\"\n />\n </td>\n }\n </tr>\n }\n </ng-template>\n\n <ng-template #footer>\n @if (!readonly()) {\n <tr>\n <td\n [attr.colspan]=\"\n config().enableTax && config().enableDiscount\n ? 7\n : config().enableTax || config().enableDiscount\n ? 6\n : 5\n \"\n >\n <a\n href=\"javascript:void(0)\"\n class=\"inline-flex items-center gap-2 text-sm font-medium text-primary-500 hover:text-primary-700\"\n [class.opacity-50]=\"hasEditingRow()\"\n [class.pointer-events-none]=\"hasEditingRow()\"\n (click)=\"addNewProductRow()\"\n >\n <i class=\"pi pi-plus text-xs\"></i>\n Ajouter un article\n </a>\n @if (config().showCatalogue) {\n <ng-container>\n &nbsp; | &nbsp;\n <a\n href=\"javascript:void(0)\"\n class=\"inline-flex items-center gap-2 text-sm font-medium text-primary-500 hover:text-primary-700\"\n (click)=\"handleOpenCatalogue()\"\n >\n <i class=\"pi pi-search text-xs\"></i>\n Ouvrir le catalogue des articles\n </a>\n </ng-container>\n }\n </td>\n </tr>\n }\n\n <!-- Totals Section -->\n <tr class=\"border-t\">\n <td\n [attr.colspan]=\"\n config().enableTax && config().enableDiscount\n ? 5\n : config().enableTax || config().enableDiscount\n ? 4\n : 3\n \"\n >\n @if (config().showProductsSummary) {\n <div class=\"w-[500px]\">\n <div class=\"my-4\"></div>\n <ef-order-summary\n [orderLines]=\"orderLines()\"\n [countGroupLabels]=\"countGroupLabels()\"\n legend=\"R\u00E9capitulatif\"\n />\n </div>\n }\n </td>\n <td [attr.colspan]=\"readonly() ? 2 : 3\" class=\"!text-end\">\n <p-fieldset [legend]=\"'Totaux'\">\n <div class=\"flex flex-col gap-2\">\n <div class=\"flex items-end justify-between gap-4\">\n <span\n class=\"font-semibold text-surface-700 dark:text-surface-300\"\n >\n Montant brut :\n </span>\n <span class=\"font-semibold\">\n {{ grossTotal() | currency: currency() }}\n </span>\n </div>\n @if (config().enableDiscount) {\n <div class=\"flex justify-between gap-4\">\n <span\n class=\"font-semibold text-surface-700 dark:text-surface-300\"\n >\n Remise :\n </span>\n <span class=\"font-semibold\">\n {{ discountTotal() | currency: currency() }}\n </span>\n </div>\n }\n @if (config().enableTax) {\n <div class=\"flex justify-between gap-4\">\n <span\n class=\"font-semibold text-surface-700 dark:text-surface-300\"\n >\n TVA :\n </span>\n <span class=\"font-semibold\">\n {{ tvaTotal() | currency: currency() }}\n </span>\n </div>\n }\n <p-divider class=\"!my-1\" />\n <div class=\"flex justify-between gap-4\">\n <span class=\"text-lg font-bold text-primary\">\n Montant Total :\n </span>\n <span class=\"text-lg font-bold text-primary\">\n {{ finalTotal() | currency: currency() }}\n </span>\n </div>\n </div>\n </p-fieldset>\n </td>\n </tr>\n </ng-template>\n </p-table>\n</p-fieldset>\n\n<!-- Optional Notes Section -->\n@if (config().showNotes) {\n <p-fieldset legend=\"Notes\">\n <textarea\n pTextarea\n id=\"order.notes\"\n name=\"order.notes\"\n [ngModel]=\"order().notes\"\n (ngModelChange)=\"updateNotes($event)\"\n [disabled]=\"readonly()\"\n [placeholder]=\"'common.notes_placeholder' | translate\"\n rows=\"5\"\n class=\"w-full\"\n ></textarea>\n </p-fieldset>\n}\n\n<!-- Validation Messages -->\n@if (hasErrors()) {\n <div\n class=\"my-4 rounded border border-red-200 bg-red-50 p-4 dark:bg-red-900/20\"\n >\n <div class=\"flex items-start gap-2\">\n <i\n class=\"pi pi-exclamation-triangle mt-1 text-red-600 dark:text-red-400\"\n ></i>\n <div class=\"flex-1\">\n <h4 class=\"mb-2 font-semibold text-red-800 dark:text-red-200\">\n Erreurs de validation\n </h4>\n <ul class=\"list-inside list-disc text-sm text-red-700 dark:text-red-300\">\n @for (error of combinedErrors() | keyvalue; track error.key) {\n <li>\n {{ error.value[0] }}\n </li>\n }\n </ul>\n </div>\n </div>\n </div>\n}\n\n<!-- Change Indicator -->\n@if (hasChanges()) {\n <div\n class=\"my-4 flex items-center gap-2 rounded border border-orange-200 bg-orange-50 p-3 dark:bg-orange-900/20\"\n >\n <i class=\"pi pi-info-circle text-orange-600 dark:text-orange-400\"></i>\n <span class=\"text-sm text-orange-800 dark:text-orange-200\">\n Modifications non sauvegard\u00E9es d\u00E9tect\u00E9es\n </span>\n </div>\n}\n", styles: [".inline-edit-cell{position:relative}.inline-edit-cell .edit-indicator{position:absolute;top:2px;right:2px;font-size:.65rem;opacity:0;transition:opacity .2s ease}.inline-edit-cell:hover .edit-indicator{opacity:.5}.cursor-pointer{cursor:pointer;transition:background-color .2s ease}.cursor-pointer:hover{background-color:#0000000d}:host-context(.dark-mode) .cursor-pointer:hover{background-color:#ffffff0d}.cursor-pointer:active{transform:scale(.98)}::ng-deep .p-datatable .p-datatable-thead>tr>th{font-weight:600;text-transform:uppercase;font-size:.75rem;letter-spacing:.05em}::ng-deep .p-datatable .editing-row{background-color:rgba(var(--primary-500),.05);border-left:3px solid var(--primary-500)}::ng-deep .p-datatable .w-\\[120px\\]{width:120px;min-width:120px;max-width:120px}::ng-deep .p-datatable .w-20{width:5rem;min-width:5rem;max-width:5rem}::ng-deep .p-datatable .\\!text-end{text-align:right!important;justify-content:flex-end}.totals-section{background-color:rgba(var(--surface-0),.5);border-top:2px solid var(--surface-border);padding:1rem}.totals-section .totals-row{display:flex;justify-content:space-between;align-items:center;padding:.5rem 0;gap:1rem}.totals-section .totals-row.total-final{border-top:2px solid var(--primary-500);padding-top:.75rem;margin-top:.5rem;font-size:1.125rem;font-weight:700;color:var(--primary-500)}.totals-section .totals-label{color:var(--text-color-secondary);font-weight:600}.totals-section .totals-value{font-weight:600;font-variant-numeric:tabular-nums}.add-product-link{display:inline-flex;align-items:center;gap:.5rem;font-size:.875rem;font-weight:500;color:var(--primary-500);text-decoration:none;transition:color .2s ease}.add-product-link:hover{color:var(--primary-700)}.add-product-link.disabled{opacity:.5;pointer-events:none;cursor:not-allowed}.add-product-link i{font-size:.75rem}.validation-message{padding:1rem;margin-bottom:1rem;border-radius:4px}.validation-message.error{background-color:rgba(var(--red-50),1);border:1px solid rgba(var(--red-200),1);color:var(--red-800)}:host-context(.dark-mode) .validation-message.error{background-color:rgba(var(--red-900),.2);color:var(--red-200)}.validation-message.error .icon{color:var(--red-600)}:host-context(.dark-mode) .validation-message.error .icon{color:var(--red-400)}.validation-message.warning{background-color:rgba(var(--orange-50),1);border:1px solid rgba(var(--orange-200),1);color:var(--orange-800)}:host-context(.dark-mode) .validation-message.warning{background-color:rgba(var(--orange-900),.2);color:var(--orange-200)}.validation-message.warning .icon{color:var(--orange-600)}:host-context(.dark-mode) .validation-message.warning .icon{color:var(--orange-400)}.validation-message.info{background-color:rgba(var(--blue-50),1);border:1px solid rgba(var(--blue-200),1);color:var(--blue-800)}:host-context(.dark-mode) .validation-message.info{background-color:rgba(var(--blue-900),.2);color:var(--blue-200)}.validation-message.info .icon{color:var(--blue-600)}:host-context(.dark-mode) .validation-message.info .icon{color:var(--blue-400)}::ng-deep .p-inputnumber-input.currency-input{text-align:right}::ng-deep .p-inputnumber.discount-input .p-inputnumber-input{text-align:right}@media(max-width:768px){::ng-deep .p-datatable{font-size:.875rem}::ng-deep .p-datatable .w-\\[120px\\]{width:100px;min-width:100px;max-width:100px}.totals-section{font-size:.875rem}.totals-section .totals-row.total-final{font-size:1rem}}@media(max-width:576px){::ng-deep .p-datatable .p-datatable-thead>tr>th{font-size:.7rem;padding:.5rem .25rem}::ng-deep .p-datatable .p-datatable-tbody>tr>td{padding:.5rem .25rem}}:focus-visible{outline:2px solid var(--primary-500);outline-offset:2px;border-radius:4px}@media(prefers-contrast:high){.cursor-pointer:hover{background-color:#0000001a}.validation-message{border-width:2px}}@media(prefers-reduced-motion:reduce){*{transition-duration:.01ms!important;animation-duration:.01ms!important}}@media print{.add-product-link,.p-button,.inline-edit-cell:hover,.cursor-pointer{display:none!important}.validation-message{display:none}::ng-deep .p-datatable .p-datatable-thead>tr>th,::ng-deep .p-datatable .p-datatable-tbody>tr>td{border:1px solid #000}}.loading-overlay{position:absolute;inset:0;background-color:#fffc;display:flex;align-items:center;justify-content:center;z-index:1000}:host-context(.dark-mode) .loading-overlay{background-color:#000c}.loading-overlay .spinner{width:3rem;height:3rem;border:3px solid var(--surface-border);border-top-color:var(--primary-500);border-radius:50%;animation:spin 1s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.empty-state{text-align:center;padding:3rem 1rem;color:var(--text-color-secondary)}.empty-state .empty-icon{font-size:3rem;margin-bottom:1rem;opacity:.3}.empty-state .empty-title{font-size:1.125rem;font-weight:600;margin-bottom:.5rem}.empty-state .empty-message{font-size:.875rem;margin-bottom:1.5rem}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "ngmodule", type: TextareaModule }, { kind: "directive", type: i2$2.Textarea, selector: "[pTextarea], [pInputTextarea]", inputs: ["pTextareaPT", "pTextareaUnstyled", "autoResize", "pSize", "variant", "fluid", "invalid"], outputs: ["onResize"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: TableModule }, { kind: "component", type: i2$3.Table, selector: "p-table", inputs: ["frozenColumns", "frozenValue", "styleClass", "tableStyle", "tableStyleClass", "paginator", "pageLinks", "rowsPerPageOptions", "alwaysShowPaginator", "paginatorPosition", "paginatorStyleClass", "paginatorDropdownAppendTo", "paginatorDropdownScrollHeight", "currentPageReportTemplate", "showCurrentPageReport", "showJumpToPageDropdown", "showJumpToPageInput", "showFirstLastIcon", "showPageLinks", "defaultSortOrder", "sortMode", "resetPageOnSort", "selectionMode", "selectionPageOnly", "contextMenuSelection", "contextMenuSelectionMode", "dataKey", "metaKeySelection", "rowSelectable", "rowTrackBy", "lazy", "lazyLoadOnInit", "compareSelectionBy", "csvSeparator", "exportFilename", "filters", "globalFilterFields", "filterDelay", "filterLocale", "expandedRowKeys", "editingRowKeys", "rowExpandMode", "scrollable", "rowGroupMode", "scrollHeight", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "virtualScrollDelay", "frozenWidth", "contextMenu", "resizableColumns", "columnResizeMode", "reorderableColumns", "loading", "loadingIcon", "showLoader", "rowHover", "customSort", "showInitialSortBadge", "exportFunction", "exportHeader", "stateKey", "stateStorage", "editMode", "groupRowsBy", "size", "showGridlines", "stripedRows", "groupRowsByOrder", "responsiveLayout", "breakpoint", "paginatorLocale", "value", "columns", "first", "rows", "totalRecords", "sortField", "sortOrder", "multiSortMeta", "selection", "selectAll"], outputs: ["contextMenuSelectionChange", "selectAllChange", "selectionChange", "onRowSelect", "onRowUnselect", "onPage", "onSort", "onFilter", "onLazyLoad", "onRowExpand", "onRowCollapse", "onContextMenuSelect", "onColResize", "onColReorder", "onRowReorder", "onEditInit", "onEditComplete", "onEditCancel", "onHeaderCheckboxToggle", "sortFunction", "firstChange", "rowsChange", "onStateSave", "onStateRestore"] }, { kind: "ngmodule", type: FieldsetModule }, { kind: "component", type: i1$1.Fieldset, selector: "p-fieldset", inputs: ["legend", "toggleable", "style", "styleClass", "transitionOptions", "motionOptions", "collapsed"], outputs: ["collapsedChange", "onBeforeToggle", "onAfterToggle"] }, { kind: "ngmodule", type: DividerModule }, { kind: "component", type: i6.Divider, selector: "p-divider", inputs: ["styleClass", "layout", "type", "align"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i2$1.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "showOnEllipsis", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo", "ptTooltip", "pTooltipPT", "pTooltipUnstyled"] }, { kind: "component", type: EfOrderSummaryComponent, selector: "ef-order-summary", inputs: ["orderLines", "countGroupLabels", "legend"] }, { kind: "component", type: EfSelectComponent, selector: "ef-select", inputs: ["label", "labelKey", "inputId", "name", "options", "optionLabel", "optionValue", "placeholder", "placeholderKey", "size", "required", "fluid", "inline", "filter", "showClear", "disabled", "appendTo", "filterByKeys"], outputs: ["selectionChangeEvent"] }, { kind: "component", type: EfDatepickerComponent, selector: "ef-datepicker", inputs: ["label", "labelKey", "inputId", "name", "placeholder", "placeholderKey", "size", "required", "fluid", "inline", "disabled", "showIcon", "iconDisplay", "selectionMode", "dateFormat", "appendTo", "showTime", "showButtonBar", "minDate", "maxDate"], outputs: ["dateSelectEvent"] }, { kind: "component", type: EfInputNumberComponent, selector: "ef-inputnumber", inputs: ["label", "labelKey", "required", "value", "invalid", "disabled", "readonly", "format", "showButtons", "buttonLayout", "incrementButtonClass", "decrementButtonClass", "incrementButtonIcon", "decrementButtonIcon", "prefix", "suffix", "currency", "currencyDisplay", "locale", "localeMatcher", "mode", "useGrouping", "minFractionDigits", "maxFractionDigits", "min", "max", "step", "allowEmpty", "inputId", "name", "placeholder", "placeholderKey", "inputSize", "size", "maxlength", "minlength", "pattern", "tabindex", "title", "ariaLabel", "ariaLabelledBy", "ariaDescribedBy", "ariaRequired", "inputStyle", "inputStyleClass", "style", "styleClass", "showClear", "variant", "autofocus", "autocomplete", "fluid", "inline", "pt", "ptOptions", "dt", "unstyled"], outputs: ["inputEvent", "focusEvent", "blurEvent", "keyDownEvent", "clearEvent"] }, { kind: "component", type: EfLabelComponent, selector: "ef-label", inputs: ["label", "labelKey", "for", "styleClass", "required"] }, { kind: "component", type: EfButtonComponent, selector: "ef-button", inputs: ["label", "labelKey", "icon", "severity", "size", "disabled", "rounded", "outlined", "raised", "text", "loading", "styleClass", "type", "badge", "badgeSeverity", "tooltip", "tooltipKey", "tooltipPosition"], outputs: ["clickEvent"] }, { kind: "pipe", type: i8.DecimalPipe, name: "number" }, { kind: "pipe", type: i8.CurrencyPipe, name: "currency" }, { kind: "pipe", type: i8.KeyValuePipe, name: "keyvalue" }, { kind: "pipe", type: i3.TranslatePipe, name: "translate" }] });
1767
1768
  }
1768
1769
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfOrderBuilderComponent, decorators: [{
1769
1770
  type: Component,
@@ -1784,7 +1785,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImpo
1784
1785
  EfLabelComponent,
1785
1786
  EfFieldsetComponent,
1786
1787
  EfButtonComponent,
1787
- ], template: "<!-- Order Header -->\n<p-fieldset [legend]=\"config().headerLabel\">\n <div class=\"grid grid-cols-12 items-baseline gap-2 mb-2\">\n <ef-label [label]=\"config().customerLabel\" for=\"order.customerId\" styleClass=\"col-span-12 md:col-span-6 form-label-left\" required />\n <ef-label [label]=\"config().dateLabel\" for=\"order.orderDate\" styleClass=\"col-span-3 form-label-left\" required />\n </div>\n <div class=\"grid grid-cols-12 items-baseline gap-2 mb-2 mt-4\">\n <div class=\"col-span-12 md:col-span-6\">\n <ef-select\n [ngModel]=\"order().customerId\"\n (ngModelChange)=\"updateCustomer($event)\"\n name=\"order.customerId\"\n inputId=\"order.customerId\"\n [options]=\"customers()\"\n optionValue=\"customerId\"\n optionLabel=\"companyName\"\n [placeholder]=\"config().customerLabel\"\n filter\n showClear\n [disabled]=\"readonly()\"\n required\n />\n </div>\n <div class=\"col-span-3\">\n <ef-datepicker\n [ngModel]=\"order().orderDate\"\n (ngModelChange)=\"updateOrderDate($event)\"\n name=\"order.orderDate\"\n inputId=\"order.orderDate\"\n [disabled]=\"readonly()\"\n required\n />\n </div>\n </div>\n\n @if (config().enableQuickAdd && !readonly()) {\n <div class=\"grid grid-cols-12 items-baseline gap-2 mt-4 mb-2\">\n <ef-label labelKey=\"label.article\" for=\"quickAdd.product\" styleClass=\"col-span-12 md:col-span-6 form-label-left\" />\n <ef-label labelKey=\"label.sale_price\" for=\"quickAdd.price\" styleClass=\"col-span-2 form-label-left\" />\n <ef-label labelKey=\"label.quantity\" for=\"quickAdd.quantity\" styleClass=\"col-span-2 form-label-left\" />\n </div>\n <div class=\"grid grid-cols-12 items-center gap-2\">\n <div class=\"col-span-12 md:col-span-6\">\n <ef-select\n [ngModel]=\"quickAddProduct()\"\n (ngModelChange)=\"onQuickAddProductSelect($event)\"\n name=\"quickAdd.product\"\n inputId=\"quickAdd.product\"\n [options]=\"flattenedProductOptions()\"\n optionLabel=\"_displayLabel\"\n placeholderKey=\"placeholder.select_article\"\n filter\n showClear\n [disabled]=\"readonly()\"\n />\n </div>\n <div class=\"col-span-2\">\n <ef-inputnumber\n id=\"quickAdd.price\"\n name=\"quickAdd.price\"\n [ngModel]=\"quickAddPrice()\"\n (ngModelChange)=\"quickAddPrice.set($event)\"\n mode=\"currency\"\n [currency]=\"currency()\"\n [locale]=\"locale()\"\n [min]=\"0\"\n [disabled]=\"readonly()\"\n size=\"small\"\n />\n </div>\n <div class=\"col-span-2\">\n <ef-inputnumber\n id=\"quickAdd.quantity\"\n name=\"quickAdd.quantity\"\n [ngModel]=\"quickAddQuantity()\"\n (ngModelChange)=\"quickAddQuantity.set($event)\"\n [min]=\"1\"\n [maxFractionDigits]=\"4\"\n [locale]=\"locale()\"\n [disabled]=\"readonly()\"\n size=\"small\"\n />\n </div>\n <div class=\"col-span-2 flex justify-start gap-2\">\n <ef-button\n icon=\"pi pi-plus\"\n [rounded]=\"true\"\n severity=\"primary\"\n tooltipKey=\"tooltip.add_article\"\n [disabled]=\"quickAddDisabled()\"\n (clickEvent)=\"addQuickProduct()\"\n />\n @if (config().showCatalogue) {\n <ef-button\n icon=\"pi pi-search\"\n [rounded]=\"true\"\n severity=\"primary\"\n tooltipKey=\"tooltip.open_catalogue\"\n [disabled]=\"readonly()\"\n (clickEvent)=\"handleOpenCatalogue()\"\n />\n }\n </div>\n </div>\n }\n</p-fieldset>\n\n<div class=\"my-4\"></div>\n\n<!-- Order Line Items -->\n<p-fieldset legend=\"D\u00E9tails\">\n <p-table\n #pTable\n class=\"text-sm\"\n size=\"small\"\n [value]=\"orderLines()\"\n [rowsPerPageOptions]=\"[5, 10, 25, 50]\"\n [resizableColumns]=\"true\"\n [tableStyle]=\"{ 'min-width': '50rem' }\"\n columnResizeMode=\"expand\"\n [showCurrentPageReport]=\"true\"\n stateStorage=\"local\"\n dataKey=\"id\"\n [rowTrackBy]=\"rowTrackBy\"\n >\n <ng-template #header>\n <tr>\n <th>{{ 'label.article' | translate }}</th>\n <th class=\"w-[120px] !text-end\">{{ 'label.sale_price' | translate }}</th>\n <th class=\"w-[120px] !text-end\">{{ 'label.quantity' | translate }}</th>\n @if (config().enableTax) {\n <th class=\"w-[120px] !text-end\">TVA (%)</th>\n }\n @if (config().enableDiscount) {\n <th class=\"w-[120px] !text-end\">\n Remise (%)\n </th>\n }\n <th class=\"w-[120px] !text-end\">Sous-Total</th>\n @if (!readonly()) {\n <th class=\"w-10 !text-end\"></th>\n }\n </tr>\n </ng-template>\n\n <ng-template #body let-item let-rowIndex=\"rowIndex\">\n <!-- Editing mode for new row -->\n @if (item.isEditing) {\n <tr>\n <td>\n <div class=\"align-items-center flex gap-2\">\n <ef-select\n [(ngModel)]=\"item.product\"\n (ngModelChange)=\"onProductSelect(item)\"\n [name]=\"'item.product.' + rowIndex\"\n [inputId]=\"'item.product.' + rowIndex\"\n [options]=\"flattenedProductOptions()\"\n optionLabel=\"_displayLabel\"\n placeholderKey=\"placeholder.select_article\"\n filter\n showClear\n [disabled]=\"readonly()\"\n appendTo=\"body\"\n />\n </div>\n </td>\n <td class=\"w-[120px]\">\n <ef-inputnumber\n [id]=\"'item.productUnitPrice.' + rowIndex\"\n [name]=\"'item.productUnitPrice.' + rowIndex\"\n [(ngModel)]=\"item.productUnitPrice\"\n (ngModelChange)=\"onFieldChange(item)\"\n (onBlur)=\"stopEditField(item)\"\n mode=\"currency\"\n [currency]=\"currency()\"\n [locale]=\"locale()\"\n [disabled]=\"readonly()\"\n size=\"small\"\n />\n </td>\n <td class=\"w-[120px]\">\n <ef-inputnumber\n [id]=\"'item.quantity.' + rowIndex\"\n [name]=\"'item.quantity.' + rowIndex\"\n [(ngModel)]=\"item.quantity\"\n (ngModelChange)=\"onFieldChange(item)\"\n (onBlur)=\"saveEditingRow(item)\"\n [min]=\"1\"\n [maxFractionDigits]=\"4\"\n [locale]=\"locale()\"\n [disabled]=\"readonly()\"\n size=\"small\"\n />\n </td>\n @if (config().enableTax) {\n <td class=\"w-[120px] !text-end\">\n {{ item.taxRate || 0 }} %\n </td>\n }\n @if (config().enableDiscount) {\n <td class=\"w-[120px]\">\n <ef-inputnumber\n [id]=\"'item.discount.' + rowIndex\"\n [name]=\"'item.discount.' + rowIndex\"\n [(ngModel)]=\"item.discount\"\n (ngModelChange)=\"onFieldChange(item)\"\n (onBlur)=\"saveEditingRow(item)\"\n [min]=\"0\"\n [max]=\"100\"\n suffix=\"%\"\n [disabled]=\"readonly()\"\n size=\"small\"\n />\n </td>\n }\n <td class=\"w-[120px] !text-end\">\n {{ calculateSubtotal(item) | currency: currency() }}\n </td>\n @if (!readonly()) {\n <td class=\"w-10 !text-end\">\n <p-button\n icon=\"pi pi-trash\"\n size=\"small\"\n [outlined]=\"true\"\n pTooltip=\"Supprimer\"\n tooltipPosition=\"top\"\n (click)=\"removeOrderLine(item.id)\"\n />\n </td>\n }\n </tr>\n }\n\n <!-- Display mode for saved row -->\n @if (!item.isEditing) {\n <tr>\n <td>\n <div\n [innerHTML]=\"AppUtils.truncate(item.productDescription)\"\n [pTooltip]=\"item.productDescription\"\n tooltipPosition=\"top\"\n ></div>\n </td>\n <td\n class=\"w-[120px]\"\n [class.cursor-pointer]=\"config().allowInlineEdit && !readonly()\"\n (click)=\"startEditField(item, 'productUnitPrice')\"\n >\n @if (\n item.editingField === 'productUnitPrice' &&\n config().allowInlineEdit &&\n !readonly()\n ) {\n <ef-inputnumber\n [id]=\"'item.productUnitPrice.edit.' + rowIndex\"\n [name]=\"'item.productUnitPrice.edit.' + rowIndex\"\n [(ngModel)]=\"item.productUnitPrice\"\n (ngModelChange)=\"onFieldChange(item)\"\n (onBlur)=\"stopEditField(item)\"\n mode=\"currency\"\n [currency]=\"currency()\"\n [locale]=\"locale()\"\n size=\"small\"\n class=\"!text-end\"\n />\n } @else {\n <span\n [class]=\"\n config().allowInlineEdit && !readonly()\n ? 'block cursor-pointer rounded px-2 py-1 text-end hover:bg-surface-100 dark:hover:bg-surface-700'\n : 'block px-2 py-1 text-end'\n \"\n >\n {{ item.productUnitPrice | currency: currency() }}\n </span>\n }\n </td>\n <td\n class=\"w-[120px]\"\n [class.cursor-pointer]=\"config().allowInlineEdit && !readonly()\"\n (click)=\"startEditField(item, 'quantity')\"\n >\n @if (\n item.editingField === 'quantity' &&\n config().allowInlineEdit &&\n !readonly()\n ) {\n <ef-inputnumber\n [id]=\"'item.quantity.edit.' + rowIndex\"\n [name]=\"'item.quantity.edit.' + rowIndex\"\n [(ngModel)]=\"item.quantity\"\n (ngModelChange)=\"onFieldChange(item)\"\n (onBlur)=\"stopEditField(item)\"\n [min]=\"1\"\n [maxFractionDigits]=\"4\"\n [locale]=\"locale()\"\n size=\"small\"\n class=\"!text-end\"\n />\n } @else {\n <span\n [class]=\"\n config().allowInlineEdit && !readonly()\n ? 'block cursor-pointer rounded px-2 py-1 text-end hover:bg-surface-100 dark:hover:bg-surface-700'\n : 'block px-2 py-1 text-end'\n \"\n >\n {{ item.quantity | number: '1.4-4' : locale() }}\n </span>\n }\n </td>\n @if (config().enableTax) {\n <td class=\"w-[120px] !text-end\">\n {{ item.taxRate || 0 }} %\n </td>\n }\n @if (config().enableDiscount) {\n <td\n class=\"w-[120px]\"\n [class.cursor-pointer]=\"config().allowInlineEdit && !readonly()\"\n (click)=\"startEditField(item, 'discount')\"\n >\n @if (\n item.editingField === 'discount' &&\n config().allowInlineEdit &&\n !readonly()\n ) {\n <ef-inputnumber\n [id]=\"'item.discount.edit.' + rowIndex\"\n [name]=\"'item.discount.edit.' + rowIndex\"\n [(ngModel)]=\"item.discount\"\n (ngModelChange)=\"onFieldChange(item)\"\n (onBlur)=\"stopEditField(item)\"\n [min]=\"0\"\n [max]=\"100\"\n suffix=\"%\"\n size=\"small\"\n class=\"!text-end\"\n />\n } @else {\n <span\n [class]=\"\n config().allowInlineEdit && !readonly()\n ? 'block cursor-pointer rounded px-2 py-1 text-end hover:bg-surface-100 dark:hover:bg-surface-700'\n : 'block px-2 py-1 text-end'\n \"\n >\n {{ item.discount || 0 }}%\n </span>\n }\n </td>\n }\n <td class=\"w-[120px] !text-end\">\n {{ item.grossAmount | currency: currency() }}\n </td>\n @if (!readonly()) {\n <td class=\"w-10 !text-end\">\n <p-button\n icon=\"pi pi-trash\"\n size=\"small\"\n [outlined]=\"true\"\n tooltipPosition=\"top\"\n pTooltip=\"Supprimer\"\n (click)=\"removeOrderLine(item.id)\"\n />\n </td>\n }\n </tr>\n }\n </ng-template>\n\n <ng-template #footer>\n @if (!readonly()) {\n <tr>\n <td\n [attr.colspan]=\"\n config().enableTax && config().enableDiscount\n ? 7\n : config().enableTax || config().enableDiscount\n ? 6\n : 5\n \"\n >\n <a\n href=\"javascript:void(0)\"\n class=\"inline-flex items-center gap-2 text-sm font-medium text-primary-500 hover:text-primary-700\"\n [class.opacity-50]=\"hasEditingRow()\"\n [class.pointer-events-none]=\"hasEditingRow()\"\n (click)=\"addNewProductRow()\"\n >\n <i class=\"pi pi-plus text-xs\"></i>\n Ajouter un article\n </a>\n @if (config().showCatalogue) {\n <ng-container>\n &nbsp; | &nbsp;\n <a\n href=\"javascript:void(0)\"\n class=\"inline-flex items-center gap-2 text-sm font-medium text-primary-500 hover:text-primary-700\"\n (click)=\"handleOpenCatalogue()\"\n >\n <i class=\"pi pi-search text-xs\"></i>\n Ouvrir le catalogue des articles\n </a>\n </ng-container>\n }\n </td>\n </tr>\n }\n\n <!-- Totals Section -->\n <tr class=\"border-t\">\n <td\n [attr.colspan]=\"\n config().enableTax && config().enableDiscount\n ? 5\n : config().enableTax || config().enableDiscount\n ? 4\n : 3\n \"\n >\n @if (config().showProductsSummary) {\n <div class=\"w-[500px]\">\n <div class=\"my-4\"></div>\n <ef-order-summary\n [orderLines]=\"orderLines()\"\n [countGroupLabels]=\"countGroupLabels()\"\n legend=\"R\u00E9capitulatif\"\n />\n </div>\n }\n </td>\n <td [attr.colspan]=\"readonly() ? 2 : 3\" class=\"!text-end\">\n <p-fieldset [legend]=\"'Totaux'\">\n <div class=\"flex flex-col gap-2\">\n <div class=\"flex items-end justify-between gap-4\">\n <span\n class=\"font-semibold text-surface-700 dark:text-surface-300\"\n >\n Montant brut :\n </span>\n <span class=\"font-semibold\">\n {{ grossTotal() | currency: currency() }}\n </span>\n </div>\n @if (config().enableDiscount) {\n <div class=\"flex justify-between gap-4\">\n <span\n class=\"font-semibold text-surface-700 dark:text-surface-300\"\n >\n Remise :\n </span>\n <span class=\"font-semibold\">\n {{ discountTotal() | currency: currency() }}\n </span>\n </div>\n }\n @if (config().enableTax) {\n <div class=\"flex justify-between gap-4\">\n <span\n class=\"font-semibold text-surface-700 dark:text-surface-300\"\n >\n TVA :\n </span>\n <span class=\"font-semibold\">\n {{ tvaTotal() | currency: currency() }}\n </span>\n </div>\n }\n <p-divider class=\"!my-1\" />\n <div class=\"flex justify-between gap-4\">\n <span class=\"text-lg font-bold text-primary\">\n Montant Total :\n </span>\n <span class=\"text-lg font-bold text-primary\">\n {{ finalTotal() | currency: currency() }}\n </span>\n </div>\n </div>\n </p-fieldset>\n </td>\n </tr>\n </ng-template>\n </p-table>\n</p-fieldset>\n\n<!-- Optional Notes Section -->\n@if (config().showNotes) {\n <p-fieldset legend=\"Notes\">\n <textarea\n pTextarea\n id=\"order.notes\"\n name=\"order.notes\"\n [ngModel]=\"order().notes\"\n (ngModelChange)=\"updateNotes($event)\"\n [disabled]=\"readonly()\"\n [placeholder]=\"'common.notes_placeholder' | translate\"\n rows=\"5\"\n class=\"w-full\"\n ></textarea>\n </p-fieldset>\n}\n\n<!-- Validation Messages -->\n@if (hasErrors()) {\n <div\n class=\"my-4 rounded border border-red-200 bg-red-50 p-4 dark:bg-red-900/20\"\n >\n <div class=\"flex items-start gap-2\">\n <i\n class=\"pi pi-exclamation-triangle mt-1 text-red-600 dark:text-red-400\"\n ></i>\n <div class=\"flex-1\">\n <h4 class=\"mb-2 font-semibold text-red-800 dark:text-red-200\">\n Erreurs de validation\n </h4>\n <ul class=\"list-inside list-disc text-sm text-red-700 dark:text-red-300\">\n @for (error of combinedErrors() | keyvalue; track error.key) {\n <li>\n {{ error.value[0] }}\n </li>\n }\n </ul>\n </div>\n </div>\n </div>\n}\n\n<!-- Change Indicator -->\n@if (hasChanges()) {\n <div\n class=\"my-4 flex items-center gap-2 rounded border border-orange-200 bg-orange-50 p-3 dark:bg-orange-900/20\"\n >\n <i class=\"pi pi-info-circle text-orange-600 dark:text-orange-400\"></i>\n <span class=\"text-sm text-orange-800 dark:text-orange-200\">\n Modifications non sauvegard\u00E9es d\u00E9tect\u00E9es\n </span>\n </div>\n}\n", styles: [".inline-edit-cell{position:relative}.inline-edit-cell .edit-indicator{position:absolute;top:2px;right:2px;font-size:.65rem;opacity:0;transition:opacity .2s ease}.inline-edit-cell:hover .edit-indicator{opacity:.5}.cursor-pointer{cursor:pointer;transition:background-color .2s ease}.cursor-pointer:hover{background-color:#0000000d}:host-context(.dark-mode) .cursor-pointer:hover{background-color:#ffffff0d}.cursor-pointer:active{transform:scale(.98)}::ng-deep .p-datatable .p-datatable-thead>tr>th{font-weight:600;text-transform:uppercase;font-size:.75rem;letter-spacing:.05em}::ng-deep .p-datatable .editing-row{background-color:rgba(var(--primary-500),.05);border-left:3px solid var(--primary-500)}::ng-deep .p-datatable .w-\\[120px\\]{width:120px;min-width:120px;max-width:120px}::ng-deep .p-datatable .w-20{width:5rem;min-width:5rem;max-width:5rem}::ng-deep .p-datatable .\\!text-end{text-align:right!important;justify-content:flex-end}.totals-section{background-color:rgba(var(--surface-0),.5);border-top:2px solid var(--surface-border);padding:1rem}.totals-section .totals-row{display:flex;justify-content:space-between;align-items:center;padding:.5rem 0;gap:1rem}.totals-section .totals-row.total-final{border-top:2px solid var(--primary-500);padding-top:.75rem;margin-top:.5rem;font-size:1.125rem;font-weight:700;color:var(--primary-500)}.totals-section .totals-label{color:var(--text-color-secondary);font-weight:600}.totals-section .totals-value{font-weight:600;font-variant-numeric:tabular-nums}.add-product-link{display:inline-flex;align-items:center;gap:.5rem;font-size:.875rem;font-weight:500;color:var(--primary-500);text-decoration:none;transition:color .2s ease}.add-product-link:hover{color:var(--primary-700)}.add-product-link.disabled{opacity:.5;pointer-events:none;cursor:not-allowed}.add-product-link i{font-size:.75rem}.validation-message{padding:1rem;margin-bottom:1rem;border-radius:4px}.validation-message.error{background-color:rgba(var(--red-50),1);border:1px solid rgba(var(--red-200),1);color:var(--red-800)}:host-context(.dark-mode) .validation-message.error{background-color:rgba(var(--red-900),.2);color:var(--red-200)}.validation-message.error .icon{color:var(--red-600)}:host-context(.dark-mode) .validation-message.error .icon{color:var(--red-400)}.validation-message.warning{background-color:rgba(var(--orange-50),1);border:1px solid rgba(var(--orange-200),1);color:var(--orange-800)}:host-context(.dark-mode) .validation-message.warning{background-color:rgba(var(--orange-900),.2);color:var(--orange-200)}.validation-message.warning .icon{color:var(--orange-600)}:host-context(.dark-mode) .validation-message.warning .icon{color:var(--orange-400)}.validation-message.info{background-color:rgba(var(--blue-50),1);border:1px solid rgba(var(--blue-200),1);color:var(--blue-800)}:host-context(.dark-mode) .validation-message.info{background-color:rgba(var(--blue-900),.2);color:var(--blue-200)}.validation-message.info .icon{color:var(--blue-600)}:host-context(.dark-mode) .validation-message.info .icon{color:var(--blue-400)}::ng-deep .p-inputnumber-input.currency-input{text-align:right}::ng-deep .p-inputnumber.discount-input .p-inputnumber-input{text-align:right}@media(max-width:768px){::ng-deep .p-datatable{font-size:.875rem}::ng-deep .p-datatable .w-\\[120px\\]{width:100px;min-width:100px;max-width:100px}.totals-section{font-size:.875rem}.totals-section .totals-row.total-final{font-size:1rem}}@media(max-width:576px){::ng-deep .p-datatable .p-datatable-thead>tr>th{font-size:.7rem;padding:.5rem .25rem}::ng-deep .p-datatable .p-datatable-tbody>tr>td{padding:.5rem .25rem}}:focus-visible{outline:2px solid var(--primary-500);outline-offset:2px;border-radius:4px}@media(prefers-contrast:high){.cursor-pointer:hover{background-color:#0000001a}.validation-message{border-width:2px}}@media(prefers-reduced-motion:reduce){*{transition-duration:.01ms!important;animation-duration:.01ms!important}}@media print{.add-product-link,.p-button,.inline-edit-cell:hover,.cursor-pointer{display:none!important}.validation-message{display:none}::ng-deep .p-datatable .p-datatable-thead>tr>th,::ng-deep .p-datatable .p-datatable-tbody>tr>td{border:1px solid #000}}.loading-overlay{position:absolute;inset:0;background-color:#fffc;display:flex;align-items:center;justify-content:center;z-index:1000}:host-context(.dark-mode) .loading-overlay{background-color:#000c}.loading-overlay .spinner{width:3rem;height:3rem;border:3px solid var(--surface-border);border-top-color:var(--primary-500);border-radius:50%;animation:spin 1s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.empty-state{text-align:center;padding:3rem 1rem;color:var(--text-color-secondary)}.empty-state .empty-icon{font-size:3rem;margin-bottom:1rem;opacity:.3}.empty-state .empty-title{font-size:1.125rem;font-weight:600;margin-bottom:.5rem}.empty-state .empty-message{font-size:.875rem;margin-bottom:1.5rem}\n"] }]
1788
+ ], template: "<!-- Order Header -->\n<p-fieldset [legend]=\"config().headerLabel\">\n <div class=\"grid grid-cols-12 items-baseline gap-2 mb-2\">\n <ef-label [label]=\"config().customerLabel\" for=\"order.customerId\" styleClass=\"col-span-12 md:col-span-6 form-label-left\" required />\n <ef-label [label]=\"config().dateLabel\" for=\"order.orderDate\" styleClass=\"col-span-3 form-label-left\" required />\n </div>\n <div class=\"grid grid-cols-12 items-baseline gap-2 mb-2 mt-4\">\n <div class=\"col-span-12 md:col-span-6\">\n <ef-select\n [ngModel]=\"order().customerId\"\n (ngModelChange)=\"updateCustomer($event)\"\n name=\"order.customerId\"\n inputId=\"order.customerId\"\n [options]=\"customers()\"\n optionValue=\"customerId\"\n optionLabel=\"companyName\"\n [placeholder]=\"config().customerLabel\"\n filter\n showClear\n [disabled]=\"readonly()\"\n required\n />\n </div>\n <div class=\"col-span-3\">\n <ef-datepicker\n [ngModel]=\"order().orderDate\"\n (ngModelChange)=\"updateOrderDate($event)\"\n name=\"order.orderDate\"\n inputId=\"order.orderDate\"\n [disabled]=\"readonly()\"\n required\n />\n </div>\n </div>\n\n @if (config().enableQuickAdd && !readonly()) {\n <div class=\"grid grid-cols-12 items-baseline gap-2 mt-4 mb-2\">\n <ef-label labelKey=\"label.article\" for=\"quickAdd.product\" styleClass=\"col-span-12 md:col-span-6 form-label-left\" />\n <ef-label labelKey=\"label.sale_price\" for=\"quickAdd.price\" styleClass=\"col-span-2 form-label-left\" />\n <ef-label labelKey=\"label.quantity\" for=\"quickAdd.quantity\" styleClass=\"col-span-2 form-label-left\" />\n </div>\n <div class=\"grid grid-cols-12 items-center gap-2\">\n <div class=\"col-span-12 md:col-span-6\">\n <ef-select\n [ngModel]=\"quickAddProduct()\"\n (ngModelChange)=\"onQuickAddProductSelect($event)\"\n name=\"quickAdd.product\"\n inputId=\"quickAdd.product\"\n [options]=\"flattenedProductOptions()\"\n optionLabel=\"_displayLabel\"\n placeholderKey=\"placeholder.select_article\"\n filter\n showClear\n [disabled]=\"readonly()\"\n />\n </div>\n <div class=\"col-span-2\">\n <ef-inputnumber\n id=\"quickAdd.price\"\n name=\"quickAdd.price\"\n [ngModel]=\"quickAddPrice()\"\n (ngModelChange)=\"quickAddPrice.set($event)\"\n mode=\"currency\"\n [currency]=\"currency()\"\n [locale]=\"locale()\"\n [min]=\"0\"\n [disabled]=\"readonly()\"\n size=\"small\"\n />\n </div>\n <div class=\"col-span-2\">\n <ef-inputnumber\n id=\"quickAdd.quantity\"\n name=\"quickAdd.quantity\"\n [ngModel]=\"quickAddQuantity()\"\n (ngModelChange)=\"quickAddQuantity.set($event)\"\n [min]=\"0\"\n [maxFractionDigits]=\"4\"\n [locale]=\"locale()\"\n [disabled]=\"readonly()\"\n size=\"small\"\n />\n </div>\n <div class=\"col-span-2 flex justify-start gap-2\">\n <ef-button\n icon=\"pi pi-plus\"\n [rounded]=\"true\"\n severity=\"primary\"\n tooltipKey=\"tooltip.add_article\"\n [disabled]=\"quickAddDisabled()\"\n (clickEvent)=\"addQuickProduct()\"\n />\n @if (config().showCatalogue) {\n <ef-button\n icon=\"pi pi-search\"\n [rounded]=\"true\"\n severity=\"primary\"\n tooltipKey=\"tooltip.open_catalogue\"\n [disabled]=\"readonly()\"\n (clickEvent)=\"handleOpenCatalogue()\"\n />\n }\n </div>\n </div>\n }\n</p-fieldset>\n\n<div class=\"my-4\"></div>\n\n<!-- Order Line Items -->\n<p-fieldset legend=\"D\u00E9tails\">\n <p-table\n #pTable\n class=\"text-sm\"\n size=\"small\"\n [value]=\"orderLines()\"\n [rowsPerPageOptions]=\"[5, 10, 25, 50]\"\n [resizableColumns]=\"true\"\n [tableStyle]=\"{ 'min-width': '50rem' }\"\n columnResizeMode=\"expand\"\n [showCurrentPageReport]=\"true\"\n stateStorage=\"local\"\n dataKey=\"id\"\n [rowTrackBy]=\"rowTrackBy\"\n >\n <ng-template #header>\n <tr>\n <th>{{ 'label.article' | translate }}</th>\n <th class=\"w-[120px] !text-end\">{{ 'label.sale_price' | translate }}</th>\n <th class=\"w-[120px] !text-end\">{{ 'label.quantity' | translate }}</th>\n @if (config().enableTax) {\n <th class=\"w-[120px] !text-end\">TVA (%)</th>\n }\n @if (config().enableDiscount) {\n <th class=\"w-[120px] !text-end\">\n Remise (%)\n </th>\n }\n <th class=\"w-[120px] !text-end\">Sous-Total</th>\n @if (!readonly()) {\n <th class=\"w-10 !text-end\"></th>\n }\n </tr>\n </ng-template>\n\n <ng-template #body let-item let-rowIndex=\"rowIndex\">\n <!-- Editing mode for new row -->\n @if (item.isEditing) {\n <tr>\n <td>\n <div class=\"align-items-center flex gap-2\">\n <ef-select\n [(ngModel)]=\"item.product\"\n (ngModelChange)=\"onProductSelect(item)\"\n [name]=\"'item.product.' + rowIndex\"\n [inputId]=\"'item.product.' + rowIndex\"\n [options]=\"flattenedProductOptions()\"\n optionLabel=\"_displayLabel\"\n placeholderKey=\"placeholder.select_article\"\n filter\n showClear\n [disabled]=\"readonly()\"\n appendTo=\"body\"\n />\n </div>\n </td>\n <td class=\"w-[120px]\">\n <ef-inputnumber\n [id]=\"'item.productUnitPrice.' + rowIndex\"\n [name]=\"'item.productUnitPrice.' + rowIndex\"\n [(ngModel)]=\"item.productUnitPrice\"\n (ngModelChange)=\"onFieldChange(item)\"\n (onBlur)=\"stopEditField(item)\"\n mode=\"currency\"\n [currency]=\"currency()\"\n [locale]=\"locale()\"\n [disabled]=\"readonly()\"\n size=\"small\"\n />\n </td>\n <td class=\"w-[120px]\">\n <ef-inputnumber\n [id]=\"'item.quantity.' + rowIndex\"\n [name]=\"'item.quantity.' + rowIndex\"\n [(ngModel)]=\"item.quantity\"\n (ngModelChange)=\"onFieldChange(item)\"\n (onBlur)=\"saveEditingRow(item)\"\n [min]=\"0\"\n [maxFractionDigits]=\"4\"\n [locale]=\"locale()\"\n [disabled]=\"readonly()\"\n size=\"small\"\n />\n </td>\n @if (config().enableTax) {\n <td class=\"w-[120px] !text-end\">\n {{ item.taxRate || 0 }} %\n </td>\n }\n @if (config().enableDiscount) {\n <td class=\"w-[120px]\">\n <ef-inputnumber\n [id]=\"'item.discount.' + rowIndex\"\n [name]=\"'item.discount.' + rowIndex\"\n [(ngModel)]=\"item.discount\"\n (ngModelChange)=\"onFieldChange(item)\"\n (onBlur)=\"saveEditingRow(item)\"\n [min]=\"0\"\n [max]=\"100\"\n suffix=\"%\"\n [disabled]=\"readonly()\"\n size=\"small\"\n />\n </td>\n }\n <td class=\"w-[120px] !text-end\">\n {{ calculateSubtotal(item) | currency: currency() }}\n </td>\n @if (!readonly()) {\n <td class=\"w-10 !text-end\">\n <p-button\n icon=\"pi pi-trash\"\n size=\"small\"\n [outlined]=\"true\"\n pTooltip=\"Supprimer\"\n tooltipPosition=\"top\"\n (click)=\"removeOrderLine(item.id)\"\n />\n </td>\n }\n </tr>\n }\n\n <!-- Display mode for saved row -->\n @if (!item.isEditing) {\n <tr>\n <td>\n <div\n [innerHTML]=\"AppUtils.truncate(item.productDescription)\"\n [pTooltip]=\"item.productDescription\"\n tooltipPosition=\"top\"\n ></div>\n </td>\n <td\n class=\"w-[120px]\"\n [class.cursor-pointer]=\"config().allowInlineEdit && !readonly()\"\n (click)=\"startEditField(item, 'productUnitPrice')\"\n >\n @if (\n item.editingField === 'productUnitPrice' &&\n config().allowInlineEdit &&\n !readonly()\n ) {\n <ef-inputnumber\n [id]=\"'item.productUnitPrice.edit.' + rowIndex\"\n [name]=\"'item.productUnitPrice.edit.' + rowIndex\"\n [(ngModel)]=\"item.productUnitPrice\"\n (ngModelChange)=\"onFieldChange(item)\"\n (onBlur)=\"stopEditField(item)\"\n mode=\"currency\"\n [currency]=\"currency()\"\n [locale]=\"locale()\"\n size=\"small\"\n class=\"!text-end\"\n />\n } @else {\n <span\n [class]=\"\n config().allowInlineEdit && !readonly()\n ? 'block cursor-pointer rounded px-2 py-1 text-end hover:bg-surface-100 dark:hover:bg-surface-700'\n : 'block px-2 py-1 text-end'\n \"\n >\n {{ item.productUnitPrice | currency: currency() }}\n </span>\n }\n </td>\n <td\n class=\"w-[120px]\"\n [class.cursor-pointer]=\"config().allowInlineEdit && !readonly()\"\n (click)=\"startEditField(item, 'quantity')\"\n >\n @if (\n item.editingField === 'quantity' &&\n config().allowInlineEdit &&\n !readonly()\n ) {\n <ef-inputnumber\n [id]=\"'item.quantity.edit.' + rowIndex\"\n [name]=\"'item.quantity.edit.' + rowIndex\"\n [(ngModel)]=\"item.quantity\"\n (ngModelChange)=\"onFieldChange(item)\"\n (onBlur)=\"stopEditField(item)\"\n [min]=\"0\"\n [maxFractionDigits]=\"4\"\n [locale]=\"locale()\"\n size=\"small\"\n class=\"!text-end\"\n />\n } @else {\n <span\n [class]=\"\n config().allowInlineEdit && !readonly()\n ? 'block cursor-pointer rounded px-2 py-1 text-end hover:bg-surface-100 dark:hover:bg-surface-700'\n : 'block px-2 py-1 text-end'\n \"\n >\n {{ item.quantity | number: '1.4-4' : locale() }}\n </span>\n }\n </td>\n @if (config().enableTax) {\n <td class=\"w-[120px] !text-end\">\n {{ item.taxRate || 0 }} %\n </td>\n }\n @if (config().enableDiscount) {\n <td\n class=\"w-[120px]\"\n [class.cursor-pointer]=\"config().allowInlineEdit && !readonly()\"\n (click)=\"startEditField(item, 'discount')\"\n >\n @if (\n item.editingField === 'discount' &&\n config().allowInlineEdit &&\n !readonly()\n ) {\n <ef-inputnumber\n [id]=\"'item.discount.edit.' + rowIndex\"\n [name]=\"'item.discount.edit.' + rowIndex\"\n [(ngModel)]=\"item.discount\"\n (ngModelChange)=\"onFieldChange(item)\"\n (onBlur)=\"stopEditField(item)\"\n [min]=\"0\"\n [max]=\"100\"\n suffix=\"%\"\n size=\"small\"\n class=\"!text-end\"\n />\n } @else {\n <span\n [class]=\"\n config().allowInlineEdit && !readonly()\n ? 'block cursor-pointer rounded px-2 py-1 text-end hover:bg-surface-100 dark:hover:bg-surface-700'\n : 'block px-2 py-1 text-end'\n \"\n >\n {{ item.discount || 0 }}%\n </span>\n }\n </td>\n }\n <td class=\"w-[120px] !text-end\">\n {{ item.grossAmount | currency: currency() }}\n </td>\n @if (!readonly()) {\n <td class=\"w-10 !text-end\">\n <p-button\n icon=\"pi pi-trash\"\n size=\"small\"\n [outlined]=\"true\"\n tooltipPosition=\"top\"\n pTooltip=\"Supprimer\"\n (click)=\"removeOrderLine(item.id)\"\n />\n </td>\n }\n </tr>\n }\n </ng-template>\n\n <ng-template #footer>\n @if (!readonly()) {\n <tr>\n <td\n [attr.colspan]=\"\n config().enableTax && config().enableDiscount\n ? 7\n : config().enableTax || config().enableDiscount\n ? 6\n : 5\n \"\n >\n <a\n href=\"javascript:void(0)\"\n class=\"inline-flex items-center gap-2 text-sm font-medium text-primary-500 hover:text-primary-700\"\n [class.opacity-50]=\"hasEditingRow()\"\n [class.pointer-events-none]=\"hasEditingRow()\"\n (click)=\"addNewProductRow()\"\n >\n <i class=\"pi pi-plus text-xs\"></i>\n Ajouter un article\n </a>\n @if (config().showCatalogue) {\n <ng-container>\n &nbsp; | &nbsp;\n <a\n href=\"javascript:void(0)\"\n class=\"inline-flex items-center gap-2 text-sm font-medium text-primary-500 hover:text-primary-700\"\n (click)=\"handleOpenCatalogue()\"\n >\n <i class=\"pi pi-search text-xs\"></i>\n Ouvrir le catalogue des articles\n </a>\n </ng-container>\n }\n </td>\n </tr>\n }\n\n <!-- Totals Section -->\n <tr class=\"border-t\">\n <td\n [attr.colspan]=\"\n config().enableTax && config().enableDiscount\n ? 5\n : config().enableTax || config().enableDiscount\n ? 4\n : 3\n \"\n >\n @if (config().showProductsSummary) {\n <div class=\"w-[500px]\">\n <div class=\"my-4\"></div>\n <ef-order-summary\n [orderLines]=\"orderLines()\"\n [countGroupLabels]=\"countGroupLabels()\"\n legend=\"R\u00E9capitulatif\"\n />\n </div>\n }\n </td>\n <td [attr.colspan]=\"readonly() ? 2 : 3\" class=\"!text-end\">\n <p-fieldset [legend]=\"'Totaux'\">\n <div class=\"flex flex-col gap-2\">\n <div class=\"flex items-end justify-between gap-4\">\n <span\n class=\"font-semibold text-surface-700 dark:text-surface-300\"\n >\n Montant brut :\n </span>\n <span class=\"font-semibold\">\n {{ grossTotal() | currency: currency() }}\n </span>\n </div>\n @if (config().enableDiscount) {\n <div class=\"flex justify-between gap-4\">\n <span\n class=\"font-semibold text-surface-700 dark:text-surface-300\"\n >\n Remise :\n </span>\n <span class=\"font-semibold\">\n {{ discountTotal() | currency: currency() }}\n </span>\n </div>\n }\n @if (config().enableTax) {\n <div class=\"flex justify-between gap-4\">\n <span\n class=\"font-semibold text-surface-700 dark:text-surface-300\"\n >\n TVA :\n </span>\n <span class=\"font-semibold\">\n {{ tvaTotal() | currency: currency() }}\n </span>\n </div>\n }\n <p-divider class=\"!my-1\" />\n <div class=\"flex justify-between gap-4\">\n <span class=\"text-lg font-bold text-primary\">\n Montant Total :\n </span>\n <span class=\"text-lg font-bold text-primary\">\n {{ finalTotal() | currency: currency() }}\n </span>\n </div>\n </div>\n </p-fieldset>\n </td>\n </tr>\n </ng-template>\n </p-table>\n</p-fieldset>\n\n<!-- Optional Notes Section -->\n@if (config().showNotes) {\n <p-fieldset legend=\"Notes\">\n <textarea\n pTextarea\n id=\"order.notes\"\n name=\"order.notes\"\n [ngModel]=\"order().notes\"\n (ngModelChange)=\"updateNotes($event)\"\n [disabled]=\"readonly()\"\n [placeholder]=\"'common.notes_placeholder' | translate\"\n rows=\"5\"\n class=\"w-full\"\n ></textarea>\n </p-fieldset>\n}\n\n<!-- Validation Messages -->\n@if (hasErrors()) {\n <div\n class=\"my-4 rounded border border-red-200 bg-red-50 p-4 dark:bg-red-900/20\"\n >\n <div class=\"flex items-start gap-2\">\n <i\n class=\"pi pi-exclamation-triangle mt-1 text-red-600 dark:text-red-400\"\n ></i>\n <div class=\"flex-1\">\n <h4 class=\"mb-2 font-semibold text-red-800 dark:text-red-200\">\n Erreurs de validation\n </h4>\n <ul class=\"list-inside list-disc text-sm text-red-700 dark:text-red-300\">\n @for (error of combinedErrors() | keyvalue; track error.key) {\n <li>\n {{ error.value[0] }}\n </li>\n }\n </ul>\n </div>\n </div>\n </div>\n}\n\n<!-- Change Indicator -->\n@if (hasChanges()) {\n <div\n class=\"my-4 flex items-center gap-2 rounded border border-orange-200 bg-orange-50 p-3 dark:bg-orange-900/20\"\n >\n <i class=\"pi pi-info-circle text-orange-600 dark:text-orange-400\"></i>\n <span class=\"text-sm text-orange-800 dark:text-orange-200\">\n Modifications non sauvegard\u00E9es d\u00E9tect\u00E9es\n </span>\n </div>\n}\n", styles: [".inline-edit-cell{position:relative}.inline-edit-cell .edit-indicator{position:absolute;top:2px;right:2px;font-size:.65rem;opacity:0;transition:opacity .2s ease}.inline-edit-cell:hover .edit-indicator{opacity:.5}.cursor-pointer{cursor:pointer;transition:background-color .2s ease}.cursor-pointer:hover{background-color:#0000000d}:host-context(.dark-mode) .cursor-pointer:hover{background-color:#ffffff0d}.cursor-pointer:active{transform:scale(.98)}::ng-deep .p-datatable .p-datatable-thead>tr>th{font-weight:600;text-transform:uppercase;font-size:.75rem;letter-spacing:.05em}::ng-deep .p-datatable .editing-row{background-color:rgba(var(--primary-500),.05);border-left:3px solid var(--primary-500)}::ng-deep .p-datatable .w-\\[120px\\]{width:120px;min-width:120px;max-width:120px}::ng-deep .p-datatable .w-20{width:5rem;min-width:5rem;max-width:5rem}::ng-deep .p-datatable .\\!text-end{text-align:right!important;justify-content:flex-end}.totals-section{background-color:rgba(var(--surface-0),.5);border-top:2px solid var(--surface-border);padding:1rem}.totals-section .totals-row{display:flex;justify-content:space-between;align-items:center;padding:.5rem 0;gap:1rem}.totals-section .totals-row.total-final{border-top:2px solid var(--primary-500);padding-top:.75rem;margin-top:.5rem;font-size:1.125rem;font-weight:700;color:var(--primary-500)}.totals-section .totals-label{color:var(--text-color-secondary);font-weight:600}.totals-section .totals-value{font-weight:600;font-variant-numeric:tabular-nums}.add-product-link{display:inline-flex;align-items:center;gap:.5rem;font-size:.875rem;font-weight:500;color:var(--primary-500);text-decoration:none;transition:color .2s ease}.add-product-link:hover{color:var(--primary-700)}.add-product-link.disabled{opacity:.5;pointer-events:none;cursor:not-allowed}.add-product-link i{font-size:.75rem}.validation-message{padding:1rem;margin-bottom:1rem;border-radius:4px}.validation-message.error{background-color:rgba(var(--red-50),1);border:1px solid rgba(var(--red-200),1);color:var(--red-800)}:host-context(.dark-mode) .validation-message.error{background-color:rgba(var(--red-900),.2);color:var(--red-200)}.validation-message.error .icon{color:var(--red-600)}:host-context(.dark-mode) .validation-message.error .icon{color:var(--red-400)}.validation-message.warning{background-color:rgba(var(--orange-50),1);border:1px solid rgba(var(--orange-200),1);color:var(--orange-800)}:host-context(.dark-mode) .validation-message.warning{background-color:rgba(var(--orange-900),.2);color:var(--orange-200)}.validation-message.warning .icon{color:var(--orange-600)}:host-context(.dark-mode) .validation-message.warning .icon{color:var(--orange-400)}.validation-message.info{background-color:rgba(var(--blue-50),1);border:1px solid rgba(var(--blue-200),1);color:var(--blue-800)}:host-context(.dark-mode) .validation-message.info{background-color:rgba(var(--blue-900),.2);color:var(--blue-200)}.validation-message.info .icon{color:var(--blue-600)}:host-context(.dark-mode) .validation-message.info .icon{color:var(--blue-400)}::ng-deep .p-inputnumber-input.currency-input{text-align:right}::ng-deep .p-inputnumber.discount-input .p-inputnumber-input{text-align:right}@media(max-width:768px){::ng-deep .p-datatable{font-size:.875rem}::ng-deep .p-datatable .w-\\[120px\\]{width:100px;min-width:100px;max-width:100px}.totals-section{font-size:.875rem}.totals-section .totals-row.total-final{font-size:1rem}}@media(max-width:576px){::ng-deep .p-datatable .p-datatable-thead>tr>th{font-size:.7rem;padding:.5rem .25rem}::ng-deep .p-datatable .p-datatable-tbody>tr>td{padding:.5rem .25rem}}:focus-visible{outline:2px solid var(--primary-500);outline-offset:2px;border-radius:4px}@media(prefers-contrast:high){.cursor-pointer:hover{background-color:#0000001a}.validation-message{border-width:2px}}@media(prefers-reduced-motion:reduce){*{transition-duration:.01ms!important;animation-duration:.01ms!important}}@media print{.add-product-link,.p-button,.inline-edit-cell:hover,.cursor-pointer{display:none!important}.validation-message{display:none}::ng-deep .p-datatable .p-datatable-thead>tr>th,::ng-deep .p-datatable .p-datatable-tbody>tr>td{border:1px solid #000}}.loading-overlay{position:absolute;inset:0;background-color:#fffc;display:flex;align-items:center;justify-content:center;z-index:1000}:host-context(.dark-mode) .loading-overlay{background-color:#000c}.loading-overlay .spinner{width:3rem;height:3rem;border:3px solid var(--surface-border);border-top-color:var(--primary-500);border-radius:50%;animation:spin 1s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.empty-state{text-align:center;padding:3rem 1rem;color:var(--text-color-secondary)}.empty-state .empty-icon{font-size:3rem;margin-bottom:1rem;opacity:.3}.empty-state .empty-title{font-size:1.125rem;font-weight:600;margin-bottom:.5rem}.empty-state .empty-message{font-size:.875rem;margin-bottom:1.5rem}\n"] }]
1788
1789
  }], ctorParameters: () => [], propDecorators: { config: [{ type: i0.Input, args: [{ isSignal: true, alias: "config", required: false }] }], order: [{ type: i0.Input, args: [{ isSignal: true, alias: "order", required: true }] }, { type: i0.Output, args: ["orderChange"] }], products: [{ type: i0.Input, args: [{ isSignal: true, alias: "products", required: true }] }], customers: [{ type: i0.Input, args: [{ isSignal: true, alias: "customers", required: true }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], countGroupLabels: [{ type: i0.Input, args: [{ isSignal: true, alias: "countGroupLabels", required: false }] }], productKeyField: [{ type: i0.Input, args: [{ isSignal: true, alias: "productKeyField", required: false }] }], backendErrors: [{ type: i0.Input, args: [{ isSignal: true, alias: "backendErrors", required: false }] }], openCatalogue: [{ type: i0.Output, args: ["openCatalogue"] }], orderChanged: [{ type: i0.Output, args: ["orderChanged"] }], orderValidated: [{ type: i0.Output, args: ["orderValidated"] }], productsAdded: [{ type: i0.Output, args: ["productsAdded"] }] } });
1789
1790
 
1790
1791
  class EfProductCatalogueFilterComponent {