@seniorsistemas/components-ai 2.3.0 → 2.4.0-master-bdd92d4e

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.
@@ -3495,6 +3495,225 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
3495
3495
  args: ['scrollContainer', { static: false }]
3496
3496
  }] } });
3497
3497
 
3498
+ class StepsComponent {
3499
+ cdr;
3500
+ /** Lista de steps a serem exibidos */
3501
+ steps = [];
3502
+ /** Índice do step ativo */
3503
+ activeIndex = 0;
3504
+ /** Se permite clicar nos steps para navegar */
3505
+ readonly = false;
3506
+ /** Quantidade máxima de steps visíveis. 'auto' calcula com base no container */
3507
+ maxVisible = 'auto';
3508
+ /** Largura mínima de cada step em pixels (usado no cálculo automático) */
3509
+ stepMinWidth = 140;
3510
+ /** Se mostra animação de transição ao navegar */
3511
+ animated = true;
3512
+ /** Modo de navegação das setas: 'step' altera o activeIndex, 'scroll' apenas desloca a janela visível */
3513
+ navigationMode = 'step';
3514
+ /** Esquema de cores aplicado aos steps. Altera as cores do ativo e completados */
3515
+ colorScheme = 'default';
3516
+ /** Classe CSS customizada para o wrapper */
3517
+ styleClass = '';
3518
+ /** Emite quando o activeIndex muda por clique */
3519
+ activeIndexChange = new EventEmitter();
3520
+ /** Emite o StepItem clicado */
3521
+ stepClick = new EventEmitter();
3522
+ stepsContainer;
3523
+ visibleSteps = [];
3524
+ visibleStartIndex = 0;
3525
+ visibleEndIndex = 0;
3526
+ hasOverflowLeft = false;
3527
+ hasOverflowRight = false;
3528
+ calculatedMaxVisible = 5;
3529
+ resizeObserver = null;
3530
+ constructor(cdr) {
3531
+ this.cdr = cdr;
3532
+ }
3533
+ ngOnChanges(changes) {
3534
+ if (changes['steps'] || changes['activeIndex'] || changes['maxVisible']) {
3535
+ this.recalculate();
3536
+ }
3537
+ }
3538
+ ngAfterViewInit() {
3539
+ this.setupResizeObserver();
3540
+ // Delay para garantir que o container já tem dimensões
3541
+ setTimeout(() => this.recalculate(), 0);
3542
+ }
3543
+ ngOnDestroy() {
3544
+ this.resizeObserver?.disconnect();
3545
+ }
3546
+ onStepClick(step, visibleIndex) {
3547
+ if (this.readonly || step.disabled)
3548
+ return;
3549
+ const realIndex = this.visibleStartIndex + visibleIndex;
3550
+ this.activeIndex = realIndex;
3551
+ this.activeIndexChange.emit(realIndex);
3552
+ this.stepClick.emit(step);
3553
+ this.recalculate();
3554
+ }
3555
+ navigateLeft() {
3556
+ if (this.visibleStartIndex <= 0)
3557
+ return;
3558
+ if (this.navigationMode === 'scroll') {
3559
+ // Apenas desloca a janela sem alterar o activeIndex
3560
+ this.scrollWindow(this.visibleStartIndex - 1);
3561
+ }
3562
+ else {
3563
+ const newActive = Math.max(0, this.activeIndex - 1);
3564
+ this.activeIndex = newActive;
3565
+ this.activeIndexChange.emit(newActive);
3566
+ this.recalculate();
3567
+ }
3568
+ }
3569
+ navigateRight() {
3570
+ if (this.visibleEndIndex >= this.steps.length - 1)
3571
+ return;
3572
+ if (this.navigationMode === 'scroll') {
3573
+ // Apenas desloca a janela sem alterar o activeIndex
3574
+ this.scrollWindow(this.visibleStartIndex + 1);
3575
+ }
3576
+ else {
3577
+ const newActive = Math.min(this.steps.length - 1, this.activeIndex + 1);
3578
+ this.activeIndex = newActive;
3579
+ this.activeIndexChange.emit(newActive);
3580
+ this.recalculate();
3581
+ }
3582
+ }
3583
+ /** Desloca a janela visível para um novo startIndex sem alterar o activeIndex */
3584
+ scrollWindow(newStart) {
3585
+ const total = this.steps.length;
3586
+ const max = this.calculatedMaxVisible;
3587
+ let start = Math.max(0, Math.min(newStart, total - max));
3588
+ let end = start + max - 1;
3589
+ if (end >= total) {
3590
+ end = total - 1;
3591
+ start = end - max + 1;
3592
+ }
3593
+ this.visibleStartIndex = start;
3594
+ this.visibleEndIndex = end;
3595
+ this.hasOverflowLeft = start > 0;
3596
+ this.hasOverflowRight = end < total - 1;
3597
+ this.visibleSteps = this.steps.slice(start, end + 1);
3598
+ this.cdr.markForCheck();
3599
+ }
3600
+ getStepState(visibleIndex) {
3601
+ const realIndex = this.visibleStartIndex + visibleIndex;
3602
+ if (realIndex < this.activeIndex)
3603
+ return 'completed';
3604
+ if (realIndex === this.activeIndex)
3605
+ return 'active';
3606
+ return 'pending';
3607
+ }
3608
+ getRealIndex(visibleIndex) {
3609
+ return this.visibleStartIndex + visibleIndex;
3610
+ }
3611
+ get hiddenLeftCount() {
3612
+ return this.visibleStartIndex;
3613
+ }
3614
+ get hiddenRightCount() {
3615
+ return Math.max(0, this.steps.length - 1 - this.visibleEndIndex);
3616
+ }
3617
+ trackByIndex(index) {
3618
+ return index;
3619
+ }
3620
+ setupResizeObserver() {
3621
+ if (!this.stepsContainer?.nativeElement)
3622
+ return;
3623
+ this.resizeObserver = new ResizeObserver(() => {
3624
+ this.recalculate();
3625
+ });
3626
+ this.resizeObserver.observe(this.stepsContainer.nativeElement);
3627
+ }
3628
+ recalculate() {
3629
+ if (!this.steps.length) {
3630
+ this.visibleSteps = [];
3631
+ this.hasOverflowLeft = false;
3632
+ this.hasOverflowRight = false;
3633
+ this.cdr.markForCheck();
3634
+ return;
3635
+ }
3636
+ this.calculatedMaxVisible = this.getMaxVisible();
3637
+ this.calculateVisibleWindow();
3638
+ this.cdr.markForCheck();
3639
+ }
3640
+ getMaxVisible() {
3641
+ if (this.maxVisible !== 'auto') {
3642
+ return Math.max(1, this.maxVisible);
3643
+ }
3644
+ if (!this.stepsContainer?.nativeElement) {
3645
+ return Math.min(5, this.steps.length);
3646
+ }
3647
+ const containerWidth = this.stepsContainer.nativeElement.offsetWidth;
3648
+ // Reservar espaço para os botões de navegação (40px cada lado)
3649
+ const availableWidth = containerWidth - 80;
3650
+ const maxFit = Math.max(1, Math.floor(availableWidth / this.stepMinWidth));
3651
+ return Math.min(maxFit, this.steps.length);
3652
+ }
3653
+ calculateVisibleWindow() {
3654
+ const total = this.steps.length;
3655
+ const max = this.calculatedMaxVisible;
3656
+ if (total <= max) {
3657
+ // Todos cabem na tela
3658
+ this.visibleStartIndex = 0;
3659
+ this.visibleEndIndex = total - 1;
3660
+ this.hasOverflowLeft = false;
3661
+ this.hasOverflowRight = false;
3662
+ }
3663
+ else {
3664
+ // Centralizar no activeIndex
3665
+ const half = Math.floor(max / 2);
3666
+ let start = this.activeIndex - half;
3667
+ let end = start + max - 1;
3668
+ // Ajustar limites
3669
+ if (start < 0) {
3670
+ start = 0;
3671
+ end = max - 1;
3672
+ }
3673
+ else if (end >= total) {
3674
+ end = total - 1;
3675
+ start = end - max + 1;
3676
+ }
3677
+ this.visibleStartIndex = start;
3678
+ this.visibleEndIndex = end;
3679
+ this.hasOverflowLeft = start > 0;
3680
+ this.hasOverflowRight = end < total - 1;
3681
+ }
3682
+ this.visibleSteps = this.steps.slice(this.visibleStartIndex, this.visibleEndIndex + 1);
3683
+ }
3684
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: StepsComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
3685
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: StepsComponent, isStandalone: true, selector: "sia-steps", inputs: { steps: "steps", activeIndex: "activeIndex", readonly: "readonly", maxVisible: "maxVisible", stepMinWidth: "stepMinWidth", animated: "animated", navigationMode: "navigationMode", colorScheme: "colorScheme", styleClass: "styleClass" }, outputs: { activeIndexChange: "activeIndexChange", stepClick: "stepClick" }, viewQueries: [{ propertyName: "stepsContainer", first: true, predicate: ["stepsContainer"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"sia-steps-wrapper\" [class]=\"styleClass\" [class.animated]=\"animated\"\n [class.theme-success]=\"colorScheme === 'success'\"\n [class.theme-danger]=\"colorScheme === 'danger'\"\n [class.theme-warning]=\"colorScheme === 'warning'\"\n [class.theme-info]=\"colorScheme === 'info'\"\n #stepsContainer>\n <!-- Bot\u00E3o de navega\u00E7\u00E3o esquerda -->\n <button\n *ngIf=\"hasOverflowLeft\"\n class=\"sia-steps-nav sia-steps-nav-left\"\n (click)=\"navigateLeft()\"\n [attr.aria-label]=\"'Anterior (' + hiddenLeftCount + ' ocultos)'\"\n type=\"button\">\n <span class=\"nav-badge\">{{ hiddenLeftCount }}</span>\n <i class=\"pi pi-chevron-left\"></i>\n </button>\n\n <!-- Steps vis\u00EDveis -->\n <div class=\"sia-steps-content\" [class.has-overflow-left]=\"hasOverflowLeft\" [class.has-overflow-right]=\"hasOverflowRight\">\n <div class=\"sia-steps-list\">\n <ng-container *ngFor=\"let step of visibleSteps; let i = index; let first = first; let last = last; trackBy: trackByIndex\">\n <!-- Conector (n\u00E3o aparece antes do primeiro) -->\n <div *ngIf=\"!first\" class=\"sia-step-connector\" [class.completed]=\"getStepState(i) === 'completed' || getStepState(i - 1) === 'completed'\">\n <div class=\"connector-line\"></div>\n </div>\n\n <!-- Step item -->\n <div\n class=\"sia-step-item\"\n [class.active]=\"getStepState(i) === 'active'\"\n [class.completed]=\"getStepState(i) === 'completed'\"\n [class.pending]=\"getStepState(i) === 'pending'\"\n [class.disabled]=\"step.disabled\"\n [class.clickable]=\"!readonly && !step.disabled\"\n [class]=\"step.styleClass || ''\"\n [pTooltip]=\"step.label\"\n tooltipPosition=\"top\"\n (click)=\"onStepClick(step, i)\"\n [attr.role]=\"readonly ? 'listitem' : 'button'\"\n [attr.aria-current]=\"getStepState(i) === 'active' ? 'step' : null\"\n [attr.aria-disabled]=\"step.disabled || readonly\">\n \n <!-- \u00CDcone/n\u00FAmero do step -->\n <div class=\"sia-step-marker\">\n <i *ngIf=\"getStepState(i) === 'completed' && !step.icon\" class=\"pi pi-check\"></i>\n <i *ngIf=\"step.icon && getStepState(i) !== 'completed'\" [class]=\"'pi ' + step.icon\"></i>\n <span *ngIf=\"!step.icon && getStepState(i) !== 'completed'\">{{ getRealIndex(i) + 1 }}</span>\n </div>\n\n <!-- Label do step -->\n <div class=\"sia-step-label\">\n <span class=\"label-text\">{{ step.label }}</span>\n </div>\n </div>\n </ng-container>\n </div>\n </div>\n\n <!-- Bot\u00E3o de navega\u00E7\u00E3o direita -->\n <button\n *ngIf=\"hasOverflowRight\"\n class=\"sia-steps-nav sia-steps-nav-right\"\n (click)=\"navigateRight()\"\n [attr.aria-label]=\"'Pr\u00F3ximo (' + hiddenRightCount + ' ocultos)'\"\n type=\"button\">\n <i class=\"pi pi-chevron-right\"></i>\n <span class=\"nav-badge\">{{ hiddenRightCount }}</span>\n </button>\n</div>\n", styles: [":host{display:block;width:100%}.sia-steps-wrapper{display:flex;align-items:center;width:100%;gap:4px;position:relative}.sia-steps-nav{display:flex;align-items:center;gap:4px;background:var(--p-surface-100, #f1f5f9);border:1px solid var(--p-surface-200, #e2e8f0);border-radius:8px;padding:6px 10px;cursor:pointer;color:var(--p-text-secondary-color, #64748b);font-size:12px;font-weight:500;transition:all .2s ease;flex-shrink:0;min-width:36px;justify-content:center}.sia-steps-nav:hover{background:var(--p-primary-50, #eff6ff);border-color:var(--p-primary-200, #bfdbfe);color:var(--p-primary-600, #2563eb)}.sia-steps-nav:active{transform:scale(.95)}.sia-steps-nav .nav-badge{background:var(--p-primary-100, #dbeafe);color:var(--p-primary-700, #1d4ed8);border-radius:10px;padding:1px 6px;font-size:11px;font-weight:600;line-height:1.4}.sia-steps-nav i{font-size:12px}.sia-steps-content{flex:1;overflow:hidden;min-width:0}.sia-steps-content.has-overflow-left{mask-image:linear-gradient(to right,transparent 0%,black 5%)}.sia-steps-content.has-overflow-right{mask-image:linear-gradient(to left,transparent 0%,black 5%)}.sia-steps-content.has-overflow-left.has-overflow-right{mask-image:linear-gradient(to right,transparent 0%,black 5%,black 95%,transparent 100%)}.sia-steps-list{display:flex;align-items:center;justify-content:center;padding:8px 4px}.sia-step-connector{flex:0 0 auto;width:32px;display:flex;align-items:center;justify-content:center}.sia-step-connector .connector-line{width:100%;height:2px;background:var(--p-surface-300, #cbd5e1);border-radius:1px;transition:background .3s ease}.sia-step-connector.completed .connector-line{background:var(--p-primary-400, #60a5fa)}.sia-step-item{display:flex;flex-direction:column;align-items:center;gap:6px;padding:8px 12px;border-radius:10px;transition:all .25s ease;min-width:80px;max-width:160px;position:relative}.sia-step-item.clickable{cursor:pointer}.sia-step-item.clickable:hover{background:var(--p-surface-50, #f8fafc);transform:translateY(-1px)}.sia-step-item.disabled{opacity:.45;cursor:not-allowed;pointer-events:none}.sia-step-item.pending .sia-step-marker{background:var(--p-surface-100, #f1f5f9);border-color:var(--p-surface-300, #cbd5e1);color:var(--p-text-secondary-color, #64748b)}.sia-step-item.pending .sia-step-label .label-text{color:var(--p-text-secondary-color, #64748b)}.sia-step-item.active .sia-step-marker{background:var(--p-primary-500, #3b82f6);border-color:var(--p-primary-500, #3b82f6);color:#fff;box-shadow:0 0 0 4px var(--p-primary-100, #dbeafe);transform:scale(1.1)}.sia-step-item.active .sia-step-label .label-text{color:var(--p-primary-700, #1d4ed8);font-weight:600}.sia-step-item.completed .sia-step-marker{background:var(--p-primary-100, #dbeafe);border-color:var(--p-primary-300, #93c5fd);color:var(--p-primary-600, #2563eb)}.sia-step-item.completed .sia-step-label .label-text{color:var(--p-text-color, #1e293b)}.sia-step-marker{width:36px;height:36px;border-radius:50%;display:flex;align-items:center;justify-content:center;border:2px solid;font-size:13px;font-weight:600;transition:all .3s ease;flex-shrink:0}.sia-step-marker i{font-size:14px}.sia-step-label{text-align:center;max-width:100%;overflow:hidden}.sia-step-label .label-text{display:block;font-size:12px;font-weight:500;line-height:1.3;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:var(--p-text-secondary-color, #64748b);transition:color .2s ease,font-weight .2s ease}.sia-steps-wrapper.animated .sia-steps-list{transition:transform .3s ease}.sia-steps-wrapper.animated .sia-step-item{animation:stepFadeIn .25s ease forwards}@keyframes stepFadeIn{0%{opacity:.6;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}.sia-steps-wrapper.theme-success .sia-step-connector.completed .connector-line{background:var(--p-green-400, #4ade80)}.sia-steps-wrapper.theme-success .sia-step-item.active .sia-step-marker{background:var(--p-green-500, #22c55e);border-color:var(--p-green-500, #22c55e);box-shadow:0 0 0 4px var(--p-green-100, #dcfce7)}.sia-steps-wrapper.theme-success .sia-step-item.active .sia-step-label .label-text{color:var(--p-green-700, #15803d)}.sia-steps-wrapper.theme-success .sia-step-item.completed .sia-step-marker{background:var(--p-green-100, #dcfce7);border-color:var(--p-green-300, #86efac);color:var(--p-green-600, #16a34a)}.sia-steps-wrapper.theme-danger .sia-step-connector.completed .connector-line{background:var(--p-red-400, #f87171)}.sia-steps-wrapper.theme-danger .sia-step-item.active .sia-step-marker{background:var(--p-red-500, #ef4444);border-color:var(--p-red-500, #ef4444);box-shadow:0 0 0 4px var(--p-red-100, #fee2e2)}.sia-steps-wrapper.theme-danger .sia-step-item.active .sia-step-label .label-text{color:var(--p-red-700, #b91c1c)}.sia-steps-wrapper.theme-danger .sia-step-item.completed .sia-step-marker{background:var(--p-red-100, #fee2e2);border-color:var(--p-red-300, #fca5a5);color:var(--p-red-600, #dc2626)}.sia-steps-wrapper.theme-warning .sia-step-connector.completed .connector-line{background:var(--p-orange-400, #fb923c)}.sia-steps-wrapper.theme-warning .sia-step-item.active .sia-step-marker{background:var(--p-orange-500, #f97316);border-color:var(--p-orange-500, #f97316);box-shadow:0 0 0 4px var(--p-orange-100, #ffedd5)}.sia-steps-wrapper.theme-warning .sia-step-item.active .sia-step-label .label-text{color:var(--p-orange-700, #c2410c)}.sia-steps-wrapper.theme-warning .sia-step-item.completed .sia-step-marker{background:var(--p-orange-100, #ffedd5);border-color:var(--p-orange-300, #fdba74);color:var(--p-orange-600, #ea580c)}.sia-steps-wrapper.theme-info .sia-step-connector.completed .connector-line{background:var(--p-cyan-400, #22d3ee)}.sia-steps-wrapper.theme-info .sia-step-item.active .sia-step-marker{background:var(--p-cyan-500, #06b6d4);border-color:var(--p-cyan-500, #06b6d4);box-shadow:0 0 0 4px var(--p-cyan-100, #cffafe)}.sia-steps-wrapper.theme-info .sia-step-item.active .sia-step-label .label-text{color:var(--p-cyan-700, #0e7490)}.sia-steps-wrapper.theme-info .sia-step-item.completed .sia-step-marker{background:var(--p-cyan-100, #cffafe);border-color:var(--p-cyan-300, #67e8f9);color:var(--p-cyan-600, #0891b2)}@media (max-width: 576px){.sia-step-item{min-width:60px;max-width:100px;padding:6px 8px}.sia-step-marker{width:30px;height:30px;font-size:11px}.sia-step-marker i{font-size:12px}.sia-step-label .label-text{font-size:11px}.sia-step-connector{width:20px}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i7$1.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "appendTo", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3686
+ }
3687
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: StepsComponent, decorators: [{
3688
+ type: Component,
3689
+ args: [{ selector: 'sia-steps', standalone: true, imports: [CommonModule, TooltipModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"sia-steps-wrapper\" [class]=\"styleClass\" [class.animated]=\"animated\"\n [class.theme-success]=\"colorScheme === 'success'\"\n [class.theme-danger]=\"colorScheme === 'danger'\"\n [class.theme-warning]=\"colorScheme === 'warning'\"\n [class.theme-info]=\"colorScheme === 'info'\"\n #stepsContainer>\n <!-- Bot\u00E3o de navega\u00E7\u00E3o esquerda -->\n <button\n *ngIf=\"hasOverflowLeft\"\n class=\"sia-steps-nav sia-steps-nav-left\"\n (click)=\"navigateLeft()\"\n [attr.aria-label]=\"'Anterior (' + hiddenLeftCount + ' ocultos)'\"\n type=\"button\">\n <span class=\"nav-badge\">{{ hiddenLeftCount }}</span>\n <i class=\"pi pi-chevron-left\"></i>\n </button>\n\n <!-- Steps vis\u00EDveis -->\n <div class=\"sia-steps-content\" [class.has-overflow-left]=\"hasOverflowLeft\" [class.has-overflow-right]=\"hasOverflowRight\">\n <div class=\"sia-steps-list\">\n <ng-container *ngFor=\"let step of visibleSteps; let i = index; let first = first; let last = last; trackBy: trackByIndex\">\n <!-- Conector (n\u00E3o aparece antes do primeiro) -->\n <div *ngIf=\"!first\" class=\"sia-step-connector\" [class.completed]=\"getStepState(i) === 'completed' || getStepState(i - 1) === 'completed'\">\n <div class=\"connector-line\"></div>\n </div>\n\n <!-- Step item -->\n <div\n class=\"sia-step-item\"\n [class.active]=\"getStepState(i) === 'active'\"\n [class.completed]=\"getStepState(i) === 'completed'\"\n [class.pending]=\"getStepState(i) === 'pending'\"\n [class.disabled]=\"step.disabled\"\n [class.clickable]=\"!readonly && !step.disabled\"\n [class]=\"step.styleClass || ''\"\n [pTooltip]=\"step.label\"\n tooltipPosition=\"top\"\n (click)=\"onStepClick(step, i)\"\n [attr.role]=\"readonly ? 'listitem' : 'button'\"\n [attr.aria-current]=\"getStepState(i) === 'active' ? 'step' : null\"\n [attr.aria-disabled]=\"step.disabled || readonly\">\n \n <!-- \u00CDcone/n\u00FAmero do step -->\n <div class=\"sia-step-marker\">\n <i *ngIf=\"getStepState(i) === 'completed' && !step.icon\" class=\"pi pi-check\"></i>\n <i *ngIf=\"step.icon && getStepState(i) !== 'completed'\" [class]=\"'pi ' + step.icon\"></i>\n <span *ngIf=\"!step.icon && getStepState(i) !== 'completed'\">{{ getRealIndex(i) + 1 }}</span>\n </div>\n\n <!-- Label do step -->\n <div class=\"sia-step-label\">\n <span class=\"label-text\">{{ step.label }}</span>\n </div>\n </div>\n </ng-container>\n </div>\n </div>\n\n <!-- Bot\u00E3o de navega\u00E7\u00E3o direita -->\n <button\n *ngIf=\"hasOverflowRight\"\n class=\"sia-steps-nav sia-steps-nav-right\"\n (click)=\"navigateRight()\"\n [attr.aria-label]=\"'Pr\u00F3ximo (' + hiddenRightCount + ' ocultos)'\"\n type=\"button\">\n <i class=\"pi pi-chevron-right\"></i>\n <span class=\"nav-badge\">{{ hiddenRightCount }}</span>\n </button>\n</div>\n", styles: [":host{display:block;width:100%}.sia-steps-wrapper{display:flex;align-items:center;width:100%;gap:4px;position:relative}.sia-steps-nav{display:flex;align-items:center;gap:4px;background:var(--p-surface-100, #f1f5f9);border:1px solid var(--p-surface-200, #e2e8f0);border-radius:8px;padding:6px 10px;cursor:pointer;color:var(--p-text-secondary-color, #64748b);font-size:12px;font-weight:500;transition:all .2s ease;flex-shrink:0;min-width:36px;justify-content:center}.sia-steps-nav:hover{background:var(--p-primary-50, #eff6ff);border-color:var(--p-primary-200, #bfdbfe);color:var(--p-primary-600, #2563eb)}.sia-steps-nav:active{transform:scale(.95)}.sia-steps-nav .nav-badge{background:var(--p-primary-100, #dbeafe);color:var(--p-primary-700, #1d4ed8);border-radius:10px;padding:1px 6px;font-size:11px;font-weight:600;line-height:1.4}.sia-steps-nav i{font-size:12px}.sia-steps-content{flex:1;overflow:hidden;min-width:0}.sia-steps-content.has-overflow-left{mask-image:linear-gradient(to right,transparent 0%,black 5%)}.sia-steps-content.has-overflow-right{mask-image:linear-gradient(to left,transparent 0%,black 5%)}.sia-steps-content.has-overflow-left.has-overflow-right{mask-image:linear-gradient(to right,transparent 0%,black 5%,black 95%,transparent 100%)}.sia-steps-list{display:flex;align-items:center;justify-content:center;padding:8px 4px}.sia-step-connector{flex:0 0 auto;width:32px;display:flex;align-items:center;justify-content:center}.sia-step-connector .connector-line{width:100%;height:2px;background:var(--p-surface-300, #cbd5e1);border-radius:1px;transition:background .3s ease}.sia-step-connector.completed .connector-line{background:var(--p-primary-400, #60a5fa)}.sia-step-item{display:flex;flex-direction:column;align-items:center;gap:6px;padding:8px 12px;border-radius:10px;transition:all .25s ease;min-width:80px;max-width:160px;position:relative}.sia-step-item.clickable{cursor:pointer}.sia-step-item.clickable:hover{background:var(--p-surface-50, #f8fafc);transform:translateY(-1px)}.sia-step-item.disabled{opacity:.45;cursor:not-allowed;pointer-events:none}.sia-step-item.pending .sia-step-marker{background:var(--p-surface-100, #f1f5f9);border-color:var(--p-surface-300, #cbd5e1);color:var(--p-text-secondary-color, #64748b)}.sia-step-item.pending .sia-step-label .label-text{color:var(--p-text-secondary-color, #64748b)}.sia-step-item.active .sia-step-marker{background:var(--p-primary-500, #3b82f6);border-color:var(--p-primary-500, #3b82f6);color:#fff;box-shadow:0 0 0 4px var(--p-primary-100, #dbeafe);transform:scale(1.1)}.sia-step-item.active .sia-step-label .label-text{color:var(--p-primary-700, #1d4ed8);font-weight:600}.sia-step-item.completed .sia-step-marker{background:var(--p-primary-100, #dbeafe);border-color:var(--p-primary-300, #93c5fd);color:var(--p-primary-600, #2563eb)}.sia-step-item.completed .sia-step-label .label-text{color:var(--p-text-color, #1e293b)}.sia-step-marker{width:36px;height:36px;border-radius:50%;display:flex;align-items:center;justify-content:center;border:2px solid;font-size:13px;font-weight:600;transition:all .3s ease;flex-shrink:0}.sia-step-marker i{font-size:14px}.sia-step-label{text-align:center;max-width:100%;overflow:hidden}.sia-step-label .label-text{display:block;font-size:12px;font-weight:500;line-height:1.3;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:var(--p-text-secondary-color, #64748b);transition:color .2s ease,font-weight .2s ease}.sia-steps-wrapper.animated .sia-steps-list{transition:transform .3s ease}.sia-steps-wrapper.animated .sia-step-item{animation:stepFadeIn .25s ease forwards}@keyframes stepFadeIn{0%{opacity:.6;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}.sia-steps-wrapper.theme-success .sia-step-connector.completed .connector-line{background:var(--p-green-400, #4ade80)}.sia-steps-wrapper.theme-success .sia-step-item.active .sia-step-marker{background:var(--p-green-500, #22c55e);border-color:var(--p-green-500, #22c55e);box-shadow:0 0 0 4px var(--p-green-100, #dcfce7)}.sia-steps-wrapper.theme-success .sia-step-item.active .sia-step-label .label-text{color:var(--p-green-700, #15803d)}.sia-steps-wrapper.theme-success .sia-step-item.completed .sia-step-marker{background:var(--p-green-100, #dcfce7);border-color:var(--p-green-300, #86efac);color:var(--p-green-600, #16a34a)}.sia-steps-wrapper.theme-danger .sia-step-connector.completed .connector-line{background:var(--p-red-400, #f87171)}.sia-steps-wrapper.theme-danger .sia-step-item.active .sia-step-marker{background:var(--p-red-500, #ef4444);border-color:var(--p-red-500, #ef4444);box-shadow:0 0 0 4px var(--p-red-100, #fee2e2)}.sia-steps-wrapper.theme-danger .sia-step-item.active .sia-step-label .label-text{color:var(--p-red-700, #b91c1c)}.sia-steps-wrapper.theme-danger .sia-step-item.completed .sia-step-marker{background:var(--p-red-100, #fee2e2);border-color:var(--p-red-300, #fca5a5);color:var(--p-red-600, #dc2626)}.sia-steps-wrapper.theme-warning .sia-step-connector.completed .connector-line{background:var(--p-orange-400, #fb923c)}.sia-steps-wrapper.theme-warning .sia-step-item.active .sia-step-marker{background:var(--p-orange-500, #f97316);border-color:var(--p-orange-500, #f97316);box-shadow:0 0 0 4px var(--p-orange-100, #ffedd5)}.sia-steps-wrapper.theme-warning .sia-step-item.active .sia-step-label .label-text{color:var(--p-orange-700, #c2410c)}.sia-steps-wrapper.theme-warning .sia-step-item.completed .sia-step-marker{background:var(--p-orange-100, #ffedd5);border-color:var(--p-orange-300, #fdba74);color:var(--p-orange-600, #ea580c)}.sia-steps-wrapper.theme-info .sia-step-connector.completed .connector-line{background:var(--p-cyan-400, #22d3ee)}.sia-steps-wrapper.theme-info .sia-step-item.active .sia-step-marker{background:var(--p-cyan-500, #06b6d4);border-color:var(--p-cyan-500, #06b6d4);box-shadow:0 0 0 4px var(--p-cyan-100, #cffafe)}.sia-steps-wrapper.theme-info .sia-step-item.active .sia-step-label .label-text{color:var(--p-cyan-700, #0e7490)}.sia-steps-wrapper.theme-info .sia-step-item.completed .sia-step-marker{background:var(--p-cyan-100, #cffafe);border-color:var(--p-cyan-300, #67e8f9);color:var(--p-cyan-600, #0891b2)}@media (max-width: 576px){.sia-step-item{min-width:60px;max-width:100px;padding:6px 8px}.sia-step-marker{width:30px;height:30px;font-size:11px}.sia-step-marker i{font-size:12px}.sia-step-label .label-text{font-size:11px}.sia-step-connector{width:20px}}\n"] }]
3690
+ }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }], propDecorators: { steps: [{
3691
+ type: Input
3692
+ }], activeIndex: [{
3693
+ type: Input
3694
+ }], readonly: [{
3695
+ type: Input
3696
+ }], maxVisible: [{
3697
+ type: Input
3698
+ }], stepMinWidth: [{
3699
+ type: Input
3700
+ }], animated: [{
3701
+ type: Input
3702
+ }], navigationMode: [{
3703
+ type: Input
3704
+ }], colorScheme: [{
3705
+ type: Input
3706
+ }], styleClass: [{
3707
+ type: Input
3708
+ }], activeIndexChange: [{
3709
+ type: Output
3710
+ }], stepClick: [{
3711
+ type: Output
3712
+ }], stepsContainer: [{
3713
+ type: ViewChild,
3714
+ args: ['stepsContainer']
3715
+ }] } });
3716
+
3498
3717
  /**
3499
3718
  * Directive para máscara de código postal
3500
3719
  * Usa o locale do cookie por padrão, ou aceita locale por parâmetro
@@ -4107,6 +4326,13 @@ const DATE_MASK_CONFIGS = {
4107
4326
  'en-US': { mask: '99/99/9999', placeholder: 'mm/dd/yyyy', format: 'mdy' },
4108
4327
  'es-ES': { mask: '99/99/9999', placeholder: 'dd/mm/aaaa', format: 'dmy' }
4109
4328
  };
4329
+ /**
4330
+ * DynamicFieldDateComponent
4331
+ *
4332
+ * O FormControl.value SEMPRE armazena a data no formato ISO: "yyyy-MM-dd" (ex: "2026-05-18").
4333
+ * A variável `displayValue` mantém o valor formatado no locale do usuário para exibição no input.
4334
+ * Isso garante que qualquer componente que acesse form.value receba o valor pronto para o backend.
4335
+ */
4110
4336
  class DynamicFieldDateComponent extends DynamicFieldBaseComponent {
4111
4337
  localeService;
4112
4338
  cdr;
@@ -4114,9 +4340,12 @@ class DynamicFieldDateComponent extends DynamicFieldBaseComponent {
4114
4340
  currentDateFormat = 'dd/mm/yy';
4115
4341
  dateMask = '99/99/9999';
4116
4342
  calendarValue = null;
4343
+ /** Valor de exibição no formato locale (ex: "18/05/2026" para pt-BR) */
4344
+ displayValue = null;
4117
4345
  langSub;
4118
4346
  valueSub;
4119
4347
  currentLocale = 'pt-BR';
4348
+ updatingFromControl = false;
4120
4349
  constructor(translationService, localeService, cdr) {
4121
4350
  super(translationService);
4122
4351
  this.localeService = localeService;
@@ -4125,71 +4354,162 @@ class DynamicFieldDateComponent extends DynamicFieldBaseComponent {
4125
4354
  ngOnInit() {
4126
4355
  this.currentLocale = this.translationService.getCurrentLanguage();
4127
4356
  this.updateDateFormat();
4128
- this.initializeValue();
4129
- this.langSub = this.translationService.currentLanguage$.subscribe((lang) => {
4130
- this.currentLocale = lang;
4131
- this.updateDateFormat();
4132
- this.convertValueToNewLocale();
4133
- });
4134
4357
  const control = this.form.get(this.field.field);
4135
4358
  if (control) {
4359
+ // Inicializar displayValue a partir do valor ISO do control
4360
+ this.syncDisplayFromControl(control.value);
4361
+ // Observar mudanças externas no control (patchValue, setValue)
4136
4362
  this.valueSub = control.valueChanges.subscribe(value => {
4137
- if (value instanceof Date) {
4138
- control.setValue(this.dateToString(value), { emitEvent: false });
4363
+ if (this.updatingFromControl) {
4364
+ return;
4139
4365
  }
4366
+ this.syncDisplayFromControl(value);
4140
4367
  });
4141
4368
  }
4369
+ this.langSub = this.translationService.currentLanguage$.subscribe((lang) => {
4370
+ this.currentLocale = lang;
4371
+ this.updateDateFormat();
4372
+ // Re-formatar o display com o novo locale
4373
+ const ctrl = this.form.get(this.field.field);
4374
+ if (ctrl) {
4375
+ this.syncDisplayFromControl(ctrl.value);
4376
+ }
4377
+ });
4142
4378
  }
4143
4379
  ngOnDestroy() {
4144
4380
  this.langSub?.unsubscribe();
4145
4381
  this.valueSub?.unsubscribe();
4146
4382
  }
4147
- initializeValue() {
4148
- const control = this.form.get(this.field.field);
4149
- if (control?.value instanceof Date) {
4150
- control.setValue(this.dateToString(control.value), { emitEvent: false });
4383
+ /**
4384
+ * Sincroniza o displayValue a partir do valor ISO do FormControl.
4385
+ * Aceita: "yyyy-MM-dd", Date, locale string "dd/MM/yyyy", ou null.
4386
+ */
4387
+ syncDisplayFromControl(value) {
4388
+ if (!value) {
4389
+ this.displayValue = null;
4390
+ return;
4151
4391
  }
4392
+ if (value instanceof Date) {
4393
+ // Converte Date para ISO no control e atualiza display
4394
+ const iso = this.dateToIso(value);
4395
+ this.setControlValue(iso);
4396
+ this.displayValue = this.isoToLocale(iso);
4397
+ return;
4398
+ }
4399
+ const s = String(value);
4400
+ // Já está em formato ISO (yyyy-MM-dd)
4401
+ if (s.match(/^\d{4}-\d{2}-\d{2}/)) {
4402
+ const isoDate = s.substring(0, 10);
4403
+ this.displayValue = this.isoToLocale(isoDate);
4404
+ return;
4405
+ }
4406
+ // Está em formato locale (dd/MM/yyyy ou MM/dd/yyyy)
4407
+ if (s.match(/^\d{2}\/\d{2}\/\d{4}$/)) {
4408
+ const iso = this.localeToIso(s);
4409
+ if (iso) {
4410
+ this.setControlValue(iso);
4411
+ this.displayValue = s;
4412
+ }
4413
+ else {
4414
+ this.displayValue = s;
4415
+ }
4416
+ return;
4417
+ }
4418
+ // Valor incompleto ou com máscara
4419
+ this.displayValue = s.includes('_') ? null : null;
4152
4420
  }
4153
- updateDateFormat() {
4154
- const config = DATE_MASK_CONFIGS[this.currentLocale] || DATE_MASK_CONFIGS['pt-BR'];
4155
- this.dateMask = config.mask;
4156
- this.currentDateFormat = this.field?.dateFormat || this.localeService.getPrimeNGDateFormat();
4421
+ /** Chamado quando o usuário completa a digitação da máscara */
4422
+ onDisplayValueComplete() {
4423
+ this.syncControlFromDisplay();
4424
+ }
4425
+ /** Chamado quando o input perde foco */
4426
+ onDisplayValueBlur() {
4427
+ this.syncControlFromDisplay();
4428
+ }
4429
+ /**
4430
+ * Converte o displayValue (locale) para ISO e seta no FormControl.
4431
+ */
4432
+ syncControlFromDisplay() {
4433
+ const control = this.form.get(this.field.field);
4434
+ if (!control) {
4435
+ return;
4436
+ }
4437
+ if (!this.displayValue || this.displayValue.includes('_')) {
4438
+ this.setControlValue(null);
4439
+ control.markAsDirty();
4440
+ return;
4441
+ }
4442
+ const iso = this.localeToIso(this.displayValue);
4443
+ if (iso) {
4444
+ this.setControlValue(iso);
4445
+ control.markAsDirty();
4446
+ }
4157
4447
  }
4158
- convertValueToNewLocale() {
4448
+ /** Seta o valor no control sem disparar o subscriber interno */
4449
+ setControlValue(value) {
4159
4450
  const control = this.form.get(this.field.field);
4160
- if (control?.value && typeof control.value === 'string' && !control.value.includes('_')) {
4161
- const date = this.stringToDate(control.value);
4162
- if (date) {
4163
- control.setValue(this.dateToString(date), { emitEvent: false });
4164
- }
4451
+ if (!control) {
4452
+ return;
4165
4453
  }
4454
+ this.updatingFromControl = true;
4455
+ control.setValue(value, { emitEvent: true });
4456
+ this.updatingFromControl = false;
4166
4457
  }
4167
- dateToString(date) {
4168
- const day = String(date.getDate()).padStart(2, '0');
4169
- const month = String(date.getMonth() + 1).padStart(2, '0');
4170
- const year = String(date.getFullYear());
4458
+ // ==================== CONVERSÕES ====================
4459
+ /** Converte ISO "yyyy-MM-dd" para locale string */
4460
+ isoToLocale(iso) {
4461
+ const [year, month, day] = iso.split('-');
4171
4462
  const config = DATE_MASK_CONFIGS[this.currentLocale] || DATE_MASK_CONFIGS['pt-BR'];
4172
4463
  return config.format === 'mdy' ? `${month}/${day}/${year}` : `${day}/${month}/${year}`;
4173
4464
  }
4174
- stringToDate(value) {
4175
- if (!value || value.includes('_'))
4176
- return null;
4177
- const parts = value.split('/');
4178
- if (parts.length !== 3)
4465
+ /** Converte locale string para ISO "yyyy-MM-dd" */
4466
+ localeToIso(locale) {
4467
+ const parts = locale.split('/');
4468
+ if (parts.length !== 3) {
4179
4469
  return null;
4470
+ }
4180
4471
  const config = DATE_MASK_CONFIGS[this.currentLocale] || DATE_MASK_CONFIGS['pt-BR'];
4181
- const [p0, p1, p2] = parts.map(p => parseInt(p, 10));
4472
+ const [p0, p1, p2] = parts;
4182
4473
  const [day, month, year] = config.format === 'mdy' ? [p1, p0, p2] : [p0, p1, p2];
4183
- if (isNaN(day) || isNaN(month) || isNaN(year) || month < 1 || month > 12 || day < 1 || day > 31)
4474
+ const d = parseInt(day, 10);
4475
+ const m = parseInt(month, 10);
4476
+ const y = parseInt(year, 10);
4477
+ if (isNaN(d) || isNaN(m) || isNaN(y) || m < 1 || m > 12 || d < 1 || d > 31) {
4184
4478
  return null;
4479
+ }
4480
+ return `${year}-${month}-${day}`;
4481
+ }
4482
+ /** Converte Date para ISO "yyyy-MM-dd" */
4483
+ dateToIso(date) {
4484
+ const day = String(date.getDate()).padStart(2, '0');
4485
+ const month = String(date.getMonth() + 1).padStart(2, '0');
4486
+ const year = String(date.getFullYear());
4487
+ return `${year}-${month}-${day}`;
4488
+ }
4489
+ /** Converte locale string para Date */
4490
+ localeToDate(value) {
4491
+ const iso = this.localeToIso(value);
4492
+ if (!iso) {
4493
+ return null;
4494
+ }
4495
+ const [year, month, day] = iso.split('-').map(p => parseInt(p, 10));
4185
4496
  const date = new Date(year, month - 1, day);
4186
4497
  return isNaN(date.getTime()) ? null : date;
4187
4498
  }
4499
+ // ==================== CALENDAR ====================
4500
+ updateDateFormat() {
4501
+ const config = DATE_MASK_CONFIGS[this.currentLocale] || DATE_MASK_CONFIGS['pt-BR'];
4502
+ this.dateMask = config.mask;
4503
+ this.currentDateFormat = this.field?.dateFormat || this.localeService.getPrimeNGDateFormat();
4504
+ }
4188
4505
  toggleCalendar(event) {
4189
4506
  event.stopPropagation();
4190
- const control = this.form.get(this.field.field);
4191
- this.calendarValue = control?.value ? this.stringToDate(control.value) : new Date();
4192
- // Simular clique no input do calendar para abrir o overlay
4507
+ if (this.displayValue && !this.displayValue.includes('_')) {
4508
+ this.calendarValue = this.localeToDate(this.displayValue);
4509
+ }
4510
+ else {
4511
+ this.calendarValue = new Date();
4512
+ }
4193
4513
  setTimeout(() => {
4194
4514
  if (this.calendarPicker && this.calendarPicker.inputfieldViewChild) {
4195
4515
  this.calendarPicker.inputfieldViewChild.nativeElement.click();
@@ -4197,22 +4517,32 @@ class DynamicFieldDateComponent extends DynamicFieldBaseComponent {
4197
4517
  });
4198
4518
  }
4199
4519
  onCalendarSelect(date) {
4200
- const control = this.form.get(this.field.field);
4201
- if (control && date) {
4202
- control.setValue(this.dateToString(date));
4203
- control.markAsDirty();
4520
+ if (date) {
4521
+ const iso = this.dateToIso(date);
4522
+ this.displayValue = this.isoToLocale(iso);
4523
+ this.setControlValue(iso);
4524
+ const control = this.form.get(this.field.field);
4525
+ if (control) {
4526
+ control.markAsDirty();
4527
+ }
4204
4528
  }
4205
4529
  }
4206
4530
  onCalendarClear() {
4531
+ this.displayValue = null;
4532
+ this.setControlValue(null);
4207
4533
  const control = this.form.get(this.field.field);
4208
4534
  if (control) {
4209
- control.setValue(null);
4210
4535
  control.markAsDirty();
4211
4536
  }
4212
4537
  }
4538
+ isControlDisabled() {
4539
+ const control = this.form.get(this.field.field);
4540
+ return control ? control.disabled : false;
4541
+ }
4213
4542
  getPlaceholder() {
4214
- if (this.field.placeholder)
4543
+ if (this.field.placeholder) {
4215
4544
  return this.field.placeholder;
4545
+ }
4216
4546
  return DATE_MASK_CONFIGS[this.currentLocale]?.placeholder || 'dd/mm/aaaa';
4217
4547
  }
4218
4548
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: DynamicFieldDateComponent, deps: [{ token: TranslationService }, { token: LocaleService }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
@@ -4222,14 +4552,18 @@ class DynamicFieldDateComponent extends DynamicFieldBaseComponent {
4222
4552
  <div class="date-input-group">
4223
4553
  <p-inputmask
4224
4554
  [inputId]="field.field"
4225
- [formControlName]="field.field"
4555
+ [(ngModel)]="displayValue"
4556
+ [ngModelOptions]="{standalone: true}"
4226
4557
  [mask]="dateMask"
4227
4558
  [placeholder]="getPlaceholder()"
4228
4559
  slotChar="_"
4229
4560
  [autoClear]="false"
4230
4561
  styleClass="date-input"
4231
4562
  [class.ng-invalid]="isFieldInvalid()"
4232
- [class.ng-dirty]="isFieldDirty()">
4563
+ [class.ng-dirty]="isFieldDirty()"
4564
+ [disabled]="isControlDisabled()"
4565
+ (onComplete)="onDisplayValueComplete()"
4566
+ (onBlur)="onDisplayValueBlur()">
4233
4567
  </p-inputmask>
4234
4568
  <div class="date-buttons">
4235
4569
  <p-button
@@ -4247,6 +4581,7 @@ class DynamicFieldDateComponent extends DynamicFieldBaseComponent {
4247
4581
  <p-calendar
4248
4582
  #calendarPicker
4249
4583
  [(ngModel)]="calendarValue"
4584
+ [ngModelOptions]="{standalone: true}"
4250
4585
  [dateFormat]="currentDateFormat"
4251
4586
  [showButtonBar]="true"
4252
4587
  [appendTo]="'body'"
@@ -4258,7 +4593,7 @@ class DynamicFieldDateComponent extends DynamicFieldBaseComponent {
4258
4593
  </p-calendar>
4259
4594
  </div>
4260
4595
  </sia-dynamic-field-wrapper>
4261
- `, isInline: true, styles: [".date-field{width:100%;position:relative}.date-input-group{position:relative;width:100%;::ng-deep p-inputmask{width:100%;display:block;input{width:100%;padding-right:2.5rem}}.date-buttons{position:absolute;right:4px;top:50%;transform:translateY(-50%);display:flex;gap:2px;z-index:10}::ng-deep .p-button{width:2rem!important;height:2rem!important;min-width:2rem!important;min-height:2rem!important;padding:0!important;margin:0!important;display:flex!important;align-items:center!important;justify-content:center!important;box-sizing:border-box!important;.p-button-icon{margin:0!important;font-size:.875rem!important}.p-button-label{display:none!important}}}::ng-deep .hidden-calendar-input{position:absolute;width:0;height:0;overflow:hidden;opacity:0;pointer-events:none;input{width:0;height:0;padding:0;border:none}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: InputMaskModule }, { kind: "component", type: i4$2.InputMask, selector: "p-inputmask, p-inputMask, p-input-mask", inputs: ["type", "slotChar", "autoClear", "showClear", "style", "inputId", "styleClass", "placeholder", "size", "maxlength", "tabindex", "title", "variant", "ariaLabel", "ariaLabelledBy", "ariaRequired", "disabled", "readonly", "unmask", "name", "required", "characterPattern", "autofocus", "autoFocus", "autocomplete", "keepBuffer", "mask"], outputs: ["onComplete", "onFocus", "onBlur", "onInput", "onKeydown", "onClear"] }, { kind: "ngmodule", type: CalendarModule }, { kind: "component", type: i5$1.Calendar, selector: "p-calendar", inputs: ["iconDisplay", "style", "styleClass", "inputStyle", "inputId", "name", "inputStyleClass", "placeholder", "ariaLabelledBy", "ariaLabel", "iconAriaLabel", "disabled", "dateFormat", "multipleSeparator", "rangeSeparator", "inline", "showOtherMonths", "selectOtherMonths", "showIcon", "fluid", "icon", "appendTo", "readonlyInput", "shortYearCutoff", "monthNavigator", "yearNavigator", "hourFormat", "timeOnly", "stepHour", "stepMinute", "stepSecond", "showSeconds", "required", "showOnFocus", "showWeek", "startWeekFromFirstDayOfYear", "showClear", "dataType", "selectionMode", "maxDateCount", "showButtonBar", "todayButtonStyleClass", "clearButtonStyleClass", "autofocus", "autoZIndex", "baseZIndex", "panelStyleClass", "panelStyle", "keepInvalid", "hideOnDateTimeSelect", "touchUI", "timeSeparator", "focusTrap", "showTransitionOptions", "hideTransitionOptions", "tabindex", "variant", "minDate", "maxDate", "disabledDates", "disabledDays", "yearRange", "showTime", "responsiveOptions", "numberOfMonths", "firstDayOfWeek", "locale", "view", "defaultDate"], outputs: ["onFocus", "onBlur", "onClose", "onSelect", "onClear", "onInput", "onTodayClick", "onClearClick", "onMonthChange", "onYearChange", "onClickOutside", "onShow"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i6.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "fluid", "buttonProps"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i7$1.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "appendTo", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions"] }, { kind: "component", type: DynamicFieldWrapperComponent, selector: "sia-dynamic-field-wrapper", inputs: ["field", "form", "mode"] }] });
4596
+ `, isInline: true, styles: [".date-field{width:100%;position:relative}.date-input-group{position:relative;width:100%;::ng-deep p-inputmask{width:100%;display:block;input{width:100%;padding-right:2.5rem}}.date-buttons{position:absolute;right:4px;top:50%;transform:translateY(-50%);display:flex;gap:2px;z-index:10}::ng-deep .p-button{width:2rem!important;height:2rem!important;min-width:2rem!important;min-height:2rem!important;padding:0!important;margin:0!important;display:flex!important;align-items:center!important;justify-content:center!important;box-sizing:border-box!important;.p-button-icon{margin:0!important;font-size:.875rem!important}.p-button-label{display:none!important}}}::ng-deep .hidden-calendar-input{position:absolute;width:0;height:0;overflow:hidden;opacity:0;pointer-events:none;input{width:0;height:0;padding:0;border:none}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: InputMaskModule }, { kind: "component", type: i4$2.InputMask, selector: "p-inputmask, p-inputMask, p-input-mask", inputs: ["type", "slotChar", "autoClear", "showClear", "style", "inputId", "styleClass", "placeholder", "size", "maxlength", "tabindex", "title", "variant", "ariaLabel", "ariaLabelledBy", "ariaRequired", "disabled", "readonly", "unmask", "name", "required", "characterPattern", "autofocus", "autoFocus", "autocomplete", "keepBuffer", "mask"], outputs: ["onComplete", "onFocus", "onBlur", "onInput", "onKeydown", "onClear"] }, { kind: "ngmodule", type: CalendarModule }, { kind: "component", type: i5$1.Calendar, selector: "p-calendar", inputs: ["iconDisplay", "style", "styleClass", "inputStyle", "inputId", "name", "inputStyleClass", "placeholder", "ariaLabelledBy", "ariaLabel", "iconAriaLabel", "disabled", "dateFormat", "multipleSeparator", "rangeSeparator", "inline", "showOtherMonths", "selectOtherMonths", "showIcon", "fluid", "icon", "appendTo", "readonlyInput", "shortYearCutoff", "monthNavigator", "yearNavigator", "hourFormat", "timeOnly", "stepHour", "stepMinute", "stepSecond", "showSeconds", "required", "showOnFocus", "showWeek", "startWeekFromFirstDayOfYear", "showClear", "dataType", "selectionMode", "maxDateCount", "showButtonBar", "todayButtonStyleClass", "clearButtonStyleClass", "autofocus", "autoZIndex", "baseZIndex", "panelStyleClass", "panelStyle", "keepInvalid", "hideOnDateTimeSelect", "touchUI", "timeSeparator", "focusTrap", "showTransitionOptions", "hideTransitionOptions", "tabindex", "variant", "minDate", "maxDate", "disabledDates", "disabledDays", "yearRange", "showTime", "responsiveOptions", "numberOfMonths", "firstDayOfWeek", "locale", "view", "defaultDate"], outputs: ["onFocus", "onBlur", "onClose", "onSelect", "onClear", "onInput", "onTodayClick", "onClearClick", "onMonthChange", "onYearChange", "onClickOutside", "onShow"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i6.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "fluid", "buttonProps"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i7$1.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "appendTo", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions"] }, { kind: "component", type: DynamicFieldWrapperComponent, selector: "sia-dynamic-field-wrapper", inputs: ["field", "form", "mode"] }] });
4262
4597
  }
4263
4598
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: DynamicFieldDateComponent, decorators: [{
4264
4599
  type: Component,
@@ -4277,14 +4612,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
4277
4612
  <div class="date-input-group">
4278
4613
  <p-inputmask
4279
4614
  [inputId]="field.field"
4280
- [formControlName]="field.field"
4615
+ [(ngModel)]="displayValue"
4616
+ [ngModelOptions]="{standalone: true}"
4281
4617
  [mask]="dateMask"
4282
4618
  [placeholder]="getPlaceholder()"
4283
4619
  slotChar="_"
4284
4620
  [autoClear]="false"
4285
4621
  styleClass="date-input"
4286
4622
  [class.ng-invalid]="isFieldInvalid()"
4287
- [class.ng-dirty]="isFieldDirty()">
4623
+ [class.ng-dirty]="isFieldDirty()"
4624
+ [disabled]="isControlDisabled()"
4625
+ (onComplete)="onDisplayValueComplete()"
4626
+ (onBlur)="onDisplayValueBlur()">
4288
4627
  </p-inputmask>
4289
4628
  <div class="date-buttons">
4290
4629
  <p-button
@@ -4302,6 +4641,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
4302
4641
  <p-calendar
4303
4642
  #calendarPicker
4304
4643
  [(ngModel)]="calendarValue"
4644
+ [ngModelOptions]="{standalone: true}"
4305
4645
  [dateFormat]="currentDateFormat"
4306
4646
  [showButtonBar]="true"
4307
4647
  [appendTo]="'body'"
@@ -4324,6 +4664,13 @@ const DATETIME_MASK_CONFIGS = {
4324
4664
  'en-US': { mask: '99/99/9999 99:99', placeholder: 'mm/dd/yyyy HH:mm', format: 'mdy' },
4325
4665
  'es-ES': { mask: '99/99/9999 99:99', placeholder: 'dd/mm/aaaa HH:mm', format: 'dmy' }
4326
4666
  };
4667
+ /**
4668
+ * DynamicFieldDatetimeComponent
4669
+ *
4670
+ * O FormControl.value SEMPRE armazena o datetime no formato ISO: "yyyy-MM-ddTHH:mm:ss.sssZ".
4671
+ * A variável `displayValue` mantém o valor formatado no locale do usuário para exibição no input.
4672
+ * Isso garante que qualquer componente que acesse form.value receba o valor pronto para o backend.
4673
+ */
4327
4674
  class DynamicFieldDatetimeComponent extends DynamicFieldBaseComponent {
4328
4675
  localeService;
4329
4676
  cdr;
@@ -4331,9 +4678,12 @@ class DynamicFieldDatetimeComponent extends DynamicFieldBaseComponent {
4331
4678
  currentDateFormat = 'dd/mm/yy';
4332
4679
  datetimeMask = '99/99/9999 99:99';
4333
4680
  calendarValue = null;
4681
+ /** Valor de exibição no formato locale (ex: "18/05/2026 14:30" para pt-BR) */
4682
+ displayValue = null;
4334
4683
  langSub;
4335
4684
  valueSub;
4336
4685
  currentLocale = 'pt-BR';
4686
+ updatingFromControl = false;
4337
4687
  constructor(translationService, localeService, cdr) {
4338
4688
  super(translationService);
4339
4689
  this.localeService = localeService;
@@ -4342,46 +4692,111 @@ class DynamicFieldDatetimeComponent extends DynamicFieldBaseComponent {
4342
4692
  ngOnInit() {
4343
4693
  this.currentLocale = this.translationService.getCurrentLanguage();
4344
4694
  this.updateDatetimeFormat();
4345
- this.initializeValue();
4346
- this.langSub = this.translationService.currentLanguage$.subscribe((lang) => {
4347
- this.currentLocale = lang;
4348
- this.updateDatetimeFormat();
4349
- this.convertValueToNewLocale();
4350
- });
4351
4695
  const control = this.form.get(this.field.field);
4352
4696
  if (control) {
4697
+ // Inicializar displayValue a partir do valor ISO do control
4698
+ this.syncDisplayFromControl(control.value);
4699
+ // Observar mudanças externas no control (patchValue, setValue)
4353
4700
  this.valueSub = control.valueChanges.subscribe(value => {
4354
- if (value instanceof Date) {
4355
- control.setValue(this.dateToString(value), { emitEvent: false });
4701
+ if (this.updatingFromControl) {
4702
+ return;
4356
4703
  }
4704
+ this.syncDisplayFromControl(value);
4357
4705
  });
4358
4706
  }
4707
+ this.langSub = this.translationService.currentLanguage$.subscribe((lang) => {
4708
+ this.currentLocale = lang;
4709
+ this.updateDatetimeFormat();
4710
+ // Re-formatar o display com o novo locale
4711
+ const ctrl = this.form.get(this.field.field);
4712
+ if (ctrl) {
4713
+ this.syncDisplayFromControl(ctrl.value);
4714
+ }
4715
+ });
4359
4716
  }
4360
4717
  ngOnDestroy() {
4361
4718
  this.langSub?.unsubscribe();
4362
4719
  this.valueSub?.unsubscribe();
4363
4720
  }
4364
- initializeValue() {
4365
- const control = this.form.get(this.field.field);
4366
- if (control?.value instanceof Date) {
4367
- control.setValue(this.dateToString(control.value), { emitEvent: false });
4721
+ /**
4722
+ * Sincroniza o displayValue a partir do valor ISO do FormControl.
4723
+ * Aceita: ISO string, Date, locale string, ou null.
4724
+ */
4725
+ syncDisplayFromControl(value) {
4726
+ if (!value) {
4727
+ this.displayValue = null;
4728
+ return;
4368
4729
  }
4730
+ if (value instanceof Date) {
4731
+ const iso = value.toISOString();
4732
+ this.setControlValue(iso);
4733
+ this.displayValue = this.isoToLocale(value);
4734
+ return;
4735
+ }
4736
+ const s = String(value);
4737
+ // ISO datetime string (contém T)
4738
+ if (s.includes('T')) {
4739
+ const d = new Date(s);
4740
+ if (!isNaN(d.getTime())) {
4741
+ this.displayValue = this.isoToLocale(d);
4742
+ }
4743
+ return;
4744
+ }
4745
+ // Formato locale "dd/MM/yyyy HH:mm" ou "MM/dd/yyyy HH:mm"
4746
+ if (s.match(/^\d{2}\/\d{2}\/\d{4} \d{2}:\d{2}$/)) {
4747
+ const iso = this.localeToIso(s);
4748
+ if (iso) {
4749
+ this.setControlValue(iso);
4750
+ this.displayValue = s;
4751
+ }
4752
+ else {
4753
+ this.displayValue = s;
4754
+ }
4755
+ return;
4756
+ }
4757
+ // Valor incompleto
4758
+ this.displayValue = s.includes('_') ? null : null;
4369
4759
  }
4370
- updateDatetimeFormat() {
4371
- const config = DATETIME_MASK_CONFIGS[this.currentLocale] || DATETIME_MASK_CONFIGS['pt-BR'];
4372
- this.datetimeMask = config.mask;
4373
- this.currentDateFormat = this.field?.dateFormat || this.localeService.getPrimeNGDateFormat();
4760
+ /** Chamado quando o usuário completa a digitação da máscara */
4761
+ onDisplayValueComplete() {
4762
+ this.syncControlFromDisplay();
4374
4763
  }
4375
- convertValueToNewLocale() {
4764
+ /** Chamado quando o input perde foco */
4765
+ onDisplayValueBlur() {
4766
+ this.syncControlFromDisplay();
4767
+ }
4768
+ /**
4769
+ * Converte o displayValue (locale) para ISO e seta no FormControl.
4770
+ */
4771
+ syncControlFromDisplay() {
4376
4772
  const control = this.form.get(this.field.field);
4377
- if (control?.value && typeof control.value === 'string' && !control.value.includes('_')) {
4378
- const date = this.stringToDate(control.value);
4379
- if (date) {
4380
- control.setValue(this.dateToString(date), { emitEvent: false });
4381
- }
4773
+ if (!control) {
4774
+ return;
4775
+ }
4776
+ if (!this.displayValue || this.displayValue.includes('_')) {
4777
+ this.setControlValue(null);
4778
+ control.markAsDirty();
4779
+ return;
4780
+ }
4781
+ const iso = this.localeToIso(this.displayValue);
4782
+ if (iso) {
4783
+ this.setControlValue(iso);
4784
+ control.markAsDirty();
4382
4785
  }
4383
4786
  }
4384
- dateToString(date) {
4787
+ /** Seta o valor no control sem disparar o subscriber interno */
4788
+ setControlValue(value) {
4789
+ const control = this.form.get(this.field.field);
4790
+ if (!control) {
4791
+ return;
4792
+ }
4793
+ this.updatingFromControl = true;
4794
+ control.setValue(value, { emitEvent: true });
4795
+ this.updatingFromControl = false;
4796
+ }
4797
+ // ==================== CONVERSÕES ====================
4798
+ /** Converte Date para locale display string */
4799
+ isoToLocale(date) {
4385
4800
  const day = String(date.getDate()).padStart(2, '0');
4386
4801
  const month = String(date.getMonth() + 1).padStart(2, '0');
4387
4802
  const year = String(date.getFullYear());
@@ -4392,33 +4807,53 @@ class DynamicFieldDatetimeComponent extends DynamicFieldBaseComponent {
4392
4807
  ? `${month}/${day}/${year} ${hours}:${minutes}`
4393
4808
  : `${day}/${month}/${year} ${hours}:${minutes}`;
4394
4809
  }
4395
- stringToDate(value) {
4396
- if (!value || value.includes('_'))
4397
- return null;
4398
- const parts = value.split(' ');
4399
- if (parts.length !== 2)
4810
+ /** Converte locale string para ISO datetime string */
4811
+ localeToIso(locale) {
4812
+ const parts = locale.split(' ');
4813
+ if (parts.length !== 2) {
4400
4814
  return null;
4815
+ }
4401
4816
  const dateParts = parts[0].split('/');
4402
4817
  const timeParts = parts[1].split(':');
4403
- if (dateParts.length !== 3 || timeParts.length !== 2)
4818
+ if (dateParts.length !== 3 || timeParts.length !== 2) {
4404
4819
  return null;
4820
+ }
4405
4821
  const config = DATETIME_MASK_CONFIGS[this.currentLocale] || DATETIME_MASK_CONFIGS['pt-BR'];
4406
4822
  const [p0, p1, p2] = dateParts.map(p => parseInt(p, 10));
4407
4823
  const [day, month, year] = config.format === 'mdy' ? [p1, p0, p2] : [p0, p1, p2];
4408
4824
  const hours = parseInt(timeParts[0], 10);
4409
4825
  const minutes = parseInt(timeParts[1], 10);
4410
- if (isNaN(day) || isNaN(month) || isNaN(year) || isNaN(hours) || isNaN(minutes))
4826
+ if (isNaN(day) || isNaN(month) || isNaN(year) || isNaN(hours) || isNaN(minutes)) {
4411
4827
  return null;
4412
- if (month < 1 || month > 12 || day < 1 || day > 31 || hours < 0 || hours > 23 || minutes < 0 || minutes > 59)
4828
+ }
4829
+ if (month < 1 || month > 12 || day < 1 || day > 31 || hours < 0 || hours > 23 || minutes < 0 || minutes > 59) {
4413
4830
  return null;
4831
+ }
4414
4832
  const date = new Date(year, month - 1, day, hours, minutes);
4415
- return isNaN(date.getTime()) ? null : date;
4833
+ return isNaN(date.getTime()) ? null : date.toISOString();
4834
+ }
4835
+ /** Converte locale string para Date */
4836
+ localeToDate(value) {
4837
+ const iso = this.localeToIso(value);
4838
+ if (!iso) {
4839
+ return null;
4840
+ }
4841
+ return new Date(iso);
4842
+ }
4843
+ // ==================== CALENDAR ====================
4844
+ updateDatetimeFormat() {
4845
+ const config = DATETIME_MASK_CONFIGS[this.currentLocale] || DATETIME_MASK_CONFIGS['pt-BR'];
4846
+ this.datetimeMask = config.mask;
4847
+ this.currentDateFormat = this.field?.dateFormat || this.localeService.getPrimeNGDateFormat();
4416
4848
  }
4417
4849
  toggleCalendar(event) {
4418
4850
  event.stopPropagation();
4419
- const control = this.form.get(this.field.field);
4420
- this.calendarValue = control?.value ? this.stringToDate(control.value) : new Date();
4421
- // Simular clique no input do calendar para abrir o overlay
4851
+ if (this.displayValue && !this.displayValue.includes('_')) {
4852
+ this.calendarValue = this.localeToDate(this.displayValue);
4853
+ }
4854
+ else {
4855
+ this.calendarValue = new Date();
4856
+ }
4422
4857
  setTimeout(() => {
4423
4858
  if (this.calendarPicker && this.calendarPicker.inputfieldViewChild) {
4424
4859
  this.calendarPicker.inputfieldViewChild.nativeElement.click();
@@ -4426,22 +4861,32 @@ class DynamicFieldDatetimeComponent extends DynamicFieldBaseComponent {
4426
4861
  });
4427
4862
  }
4428
4863
  onCalendarSelect(date) {
4429
- const control = this.form.get(this.field.field);
4430
- if (control && date) {
4431
- control.setValue(this.dateToString(date));
4432
- control.markAsDirty();
4864
+ if (date) {
4865
+ const iso = date.toISOString();
4866
+ this.displayValue = this.isoToLocale(date);
4867
+ this.setControlValue(iso);
4868
+ const control = this.form.get(this.field.field);
4869
+ if (control) {
4870
+ control.markAsDirty();
4871
+ }
4433
4872
  }
4434
4873
  }
4435
4874
  onCalendarClear() {
4875
+ this.displayValue = null;
4876
+ this.setControlValue(null);
4436
4877
  const control = this.form.get(this.field.field);
4437
4878
  if (control) {
4438
- control.setValue(null);
4439
4879
  control.markAsDirty();
4440
4880
  }
4441
4881
  }
4882
+ isControlDisabled() {
4883
+ const control = this.form.get(this.field.field);
4884
+ return control ? control.disabled : false;
4885
+ }
4442
4886
  getPlaceholder() {
4443
- if (this.field.placeholder)
4887
+ if (this.field.placeholder) {
4444
4888
  return this.field.placeholder;
4889
+ }
4445
4890
  return DATETIME_MASK_CONFIGS[this.currentLocale]?.placeholder || 'dd/mm/aaaa HH:mm';
4446
4891
  }
4447
4892
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: DynamicFieldDatetimeComponent, deps: [{ token: TranslationService }, { token: LocaleService }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
@@ -4451,14 +4896,18 @@ class DynamicFieldDatetimeComponent extends DynamicFieldBaseComponent {
4451
4896
  <div class="datetime-input-group">
4452
4897
  <p-inputmask
4453
4898
  [inputId]="field.field"
4454
- [formControlName]="field.field"
4899
+ [(ngModel)]="displayValue"
4900
+ [ngModelOptions]="{standalone: true}"
4455
4901
  [mask]="datetimeMask"
4456
4902
  [placeholder]="getPlaceholder()"
4457
4903
  slotChar="_"
4458
4904
  [autoClear]="false"
4459
4905
  styleClass="datetime-input"
4460
4906
  [class.ng-invalid]="isFieldInvalid()"
4461
- [class.ng-dirty]="isFieldDirty()">
4907
+ [class.ng-dirty]="isFieldDirty()"
4908
+ [disabled]="isControlDisabled()"
4909
+ (onComplete)="onDisplayValueComplete()"
4910
+ (onBlur)="onDisplayValueBlur()">
4462
4911
  </p-inputmask>
4463
4912
  <div class="datetime-buttons">
4464
4913
  <p-button
@@ -4476,6 +4925,7 @@ class DynamicFieldDatetimeComponent extends DynamicFieldBaseComponent {
4476
4925
  <p-calendar
4477
4926
  #calendarPicker
4478
4927
  [(ngModel)]="calendarValue"
4928
+ [ngModelOptions]="{standalone: true}"
4479
4929
  [dateFormat]="currentDateFormat"
4480
4930
  [showTime]="true"
4481
4931
  [showSeconds]="false"
@@ -4490,7 +4940,7 @@ class DynamicFieldDatetimeComponent extends DynamicFieldBaseComponent {
4490
4940
  </p-calendar>
4491
4941
  </div>
4492
4942
  </sia-dynamic-field-wrapper>
4493
- `, isInline: true, styles: [".datetime-field{width:100%;position:relative}.datetime-input-group{position:relative;width:100%;::ng-deep p-inputmask{width:100%;display:block;input{width:100%;padding-right:2.5rem}}.datetime-buttons{position:absolute;right:4px;top:50%;transform:translateY(-50%);display:flex;gap:2px;z-index:10}::ng-deep .p-button{width:2rem!important;height:2rem!important;min-width:2rem!important;min-height:2rem!important;padding:0!important;margin:0!important;display:flex!important;align-items:center!important;justify-content:center!important;box-sizing:border-box!important;.p-button-icon{margin:0!important;font-size:.875rem!important}.p-button-label{display:none!important}}}::ng-deep .hidden-calendar-input{position:absolute;width:0;height:0;overflow:hidden;opacity:0;pointer-events:none;input{width:0;height:0;padding:0;border:none}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: InputMaskModule }, { kind: "component", type: i4$2.InputMask, selector: "p-inputmask, p-inputMask, p-input-mask", inputs: ["type", "slotChar", "autoClear", "showClear", "style", "inputId", "styleClass", "placeholder", "size", "maxlength", "tabindex", "title", "variant", "ariaLabel", "ariaLabelledBy", "ariaRequired", "disabled", "readonly", "unmask", "name", "required", "characterPattern", "autofocus", "autoFocus", "autocomplete", "keepBuffer", "mask"], outputs: ["onComplete", "onFocus", "onBlur", "onInput", "onKeydown", "onClear"] }, { kind: "ngmodule", type: CalendarModule }, { kind: "component", type: i5$1.Calendar, selector: "p-calendar", inputs: ["iconDisplay", "style", "styleClass", "inputStyle", "inputId", "name", "inputStyleClass", "placeholder", "ariaLabelledBy", "ariaLabel", "iconAriaLabel", "disabled", "dateFormat", "multipleSeparator", "rangeSeparator", "inline", "showOtherMonths", "selectOtherMonths", "showIcon", "fluid", "icon", "appendTo", "readonlyInput", "shortYearCutoff", "monthNavigator", "yearNavigator", "hourFormat", "timeOnly", "stepHour", "stepMinute", "stepSecond", "showSeconds", "required", "showOnFocus", "showWeek", "startWeekFromFirstDayOfYear", "showClear", "dataType", "selectionMode", "maxDateCount", "showButtonBar", "todayButtonStyleClass", "clearButtonStyleClass", "autofocus", "autoZIndex", "baseZIndex", "panelStyleClass", "panelStyle", "keepInvalid", "hideOnDateTimeSelect", "touchUI", "timeSeparator", "focusTrap", "showTransitionOptions", "hideTransitionOptions", "tabindex", "variant", "minDate", "maxDate", "disabledDates", "disabledDays", "yearRange", "showTime", "responsiveOptions", "numberOfMonths", "firstDayOfWeek", "locale", "view", "defaultDate"], outputs: ["onFocus", "onBlur", "onClose", "onSelect", "onClear", "onInput", "onTodayClick", "onClearClick", "onMonthChange", "onYearChange", "onClickOutside", "onShow"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i6.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "fluid", "buttonProps"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i7$1.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "appendTo", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions"] }, { kind: "component", type: DynamicFieldWrapperComponent, selector: "sia-dynamic-field-wrapper", inputs: ["field", "form", "mode"] }] });
4943
+ `, isInline: true, styles: [".datetime-field{width:100%;position:relative}.datetime-input-group{position:relative;width:100%;::ng-deep p-inputmask{width:100%;display:block;input{width:100%;padding-right:2.5rem}}.datetime-buttons{position:absolute;right:4px;top:50%;transform:translateY(-50%);display:flex;gap:2px;z-index:10}::ng-deep .p-button{width:2rem!important;height:2rem!important;min-width:2rem!important;min-height:2rem!important;padding:0!important;margin:0!important;display:flex!important;align-items:center!important;justify-content:center!important;box-sizing:border-box!important;.p-button-icon{margin:0!important;font-size:.875rem!important}.p-button-label{display:none!important}}}::ng-deep .hidden-calendar-input{position:absolute;width:0;height:0;overflow:hidden;opacity:0;pointer-events:none;input{width:0;height:0;padding:0;border:none}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: InputMaskModule }, { kind: "component", type: i4$2.InputMask, selector: "p-inputmask, p-inputMask, p-input-mask", inputs: ["type", "slotChar", "autoClear", "showClear", "style", "inputId", "styleClass", "placeholder", "size", "maxlength", "tabindex", "title", "variant", "ariaLabel", "ariaLabelledBy", "ariaRequired", "disabled", "readonly", "unmask", "name", "required", "characterPattern", "autofocus", "autoFocus", "autocomplete", "keepBuffer", "mask"], outputs: ["onComplete", "onFocus", "onBlur", "onInput", "onKeydown", "onClear"] }, { kind: "ngmodule", type: CalendarModule }, { kind: "component", type: i5$1.Calendar, selector: "p-calendar", inputs: ["iconDisplay", "style", "styleClass", "inputStyle", "inputId", "name", "inputStyleClass", "placeholder", "ariaLabelledBy", "ariaLabel", "iconAriaLabel", "disabled", "dateFormat", "multipleSeparator", "rangeSeparator", "inline", "showOtherMonths", "selectOtherMonths", "showIcon", "fluid", "icon", "appendTo", "readonlyInput", "shortYearCutoff", "monthNavigator", "yearNavigator", "hourFormat", "timeOnly", "stepHour", "stepMinute", "stepSecond", "showSeconds", "required", "showOnFocus", "showWeek", "startWeekFromFirstDayOfYear", "showClear", "dataType", "selectionMode", "maxDateCount", "showButtonBar", "todayButtonStyleClass", "clearButtonStyleClass", "autofocus", "autoZIndex", "baseZIndex", "panelStyleClass", "panelStyle", "keepInvalid", "hideOnDateTimeSelect", "touchUI", "timeSeparator", "focusTrap", "showTransitionOptions", "hideTransitionOptions", "tabindex", "variant", "minDate", "maxDate", "disabledDates", "disabledDays", "yearRange", "showTime", "responsiveOptions", "numberOfMonths", "firstDayOfWeek", "locale", "view", "defaultDate"], outputs: ["onFocus", "onBlur", "onClose", "onSelect", "onClear", "onInput", "onTodayClick", "onClearClick", "onMonthChange", "onYearChange", "onClickOutside", "onShow"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i6.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "fluid", "buttonProps"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i7$1.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "appendTo", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions"] }, { kind: "component", type: DynamicFieldWrapperComponent, selector: "sia-dynamic-field-wrapper", inputs: ["field", "form", "mode"] }] });
4494
4944
  }
4495
4945
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: DynamicFieldDatetimeComponent, decorators: [{
4496
4946
  type: Component,
@@ -4509,14 +4959,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
4509
4959
  <div class="datetime-input-group">
4510
4960
  <p-inputmask
4511
4961
  [inputId]="field.field"
4512
- [formControlName]="field.field"
4962
+ [(ngModel)]="displayValue"
4963
+ [ngModelOptions]="{standalone: true}"
4513
4964
  [mask]="datetimeMask"
4514
4965
  [placeholder]="getPlaceholder()"
4515
4966
  slotChar="_"
4516
4967
  [autoClear]="false"
4517
4968
  styleClass="datetime-input"
4518
4969
  [class.ng-invalid]="isFieldInvalid()"
4519
- [class.ng-dirty]="isFieldDirty()">
4970
+ [class.ng-dirty]="isFieldDirty()"
4971
+ [disabled]="isControlDisabled()"
4972
+ (onComplete)="onDisplayValueComplete()"
4973
+ (onBlur)="onDisplayValueBlur()">
4520
4974
  </p-inputmask>
4521
4975
  <div class="datetime-buttons">
4522
4976
  <p-button
@@ -4534,6 +4988,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
4534
4988
  <p-calendar
4535
4989
  #calendarPicker
4536
4990
  [(ngModel)]="calendarValue"
4991
+ [ngModelOptions]="{standalone: true}"
4537
4992
  [dateFormat]="currentDateFormat"
4538
4993
  [showTime]="true"
4539
4994
  [showSeconds]="false"
@@ -5594,50 +6049,153 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
5594
6049
  `, styles: ["textarea{width:100%;resize:vertical}\n"] }]
5595
6050
  }], ctorParameters: () => [{ type: TranslationService }] });
5596
6051
 
6052
+ /**
6053
+ * DynamicFieldTimeComponent
6054
+ *
6055
+ * O FormControl.value SEMPRE armazena o horário no formato ISO: "HH:mm:ss" (ex: "14:30:00").
6056
+ * A variável `displayValue` mantém o valor formatado "HH:mm" para exibição no input.
6057
+ * Isso garante que qualquer componente que acesse form.value receba o valor pronto para o backend.
6058
+ */
5597
6059
  class DynamicFieldTimeComponent extends DynamicFieldBaseComponent {
5598
6060
  timePicker;
5599
6061
  timeValue = null;
6062
+ /** Valor de exibição no formato "HH:mm" */
6063
+ displayValue = null;
5600
6064
  valueSub;
6065
+ updatingFromControl = false;
5601
6066
  constructor(translationService) {
5602
6067
  super(translationService);
5603
6068
  }
5604
6069
  ngOnInit() {
5605
6070
  const control = this.form.get(this.field.field);
5606
6071
  if (control) {
5607
- if (control.value instanceof Date) {
5608
- control.setValue(this.dateToTimeString(control.value), { emitEvent: false });
5609
- }
6072
+ // Inicializar displayValue a partir do valor ISO do control
6073
+ this.syncDisplayFromControl(control.value);
6074
+ // Observar mudanças externas no control (patchValue, setValue)
5610
6075
  this.valueSub = control.valueChanges.subscribe(value => {
5611
- if (value instanceof Date) {
5612
- control.setValue(this.dateToTimeString(value), { emitEvent: false });
6076
+ if (this.updatingFromControl) {
6077
+ return;
5613
6078
  }
6079
+ this.syncDisplayFromControl(value);
5614
6080
  });
5615
6081
  }
5616
6082
  }
5617
6083
  ngOnDestroy() {
5618
6084
  this.valueSub?.unsubscribe();
5619
6085
  }
5620
- dateToTimeString(date) {
5621
- return `${String(date.getHours()).padStart(2, '0')}:${String(date.getMinutes()).padStart(2, '0')}`;
6086
+ /**
6087
+ * Sincroniza o displayValue a partir do valor ISO do FormControl.
6088
+ * Aceita: "HH:mm:ss", "HH:mm", Date, ou null.
6089
+ */
6090
+ syncDisplayFromControl(value) {
6091
+ if (!value) {
6092
+ this.displayValue = null;
6093
+ return;
6094
+ }
6095
+ if (value instanceof Date) {
6096
+ const iso = this.dateToIso(value);
6097
+ this.setControlValue(iso);
6098
+ this.displayValue = iso.substring(0, 5);
6099
+ return;
6100
+ }
6101
+ const s = String(value);
6102
+ // Formato ISO "HH:mm:ss"
6103
+ if (s.match(/^\d{2}:\d{2}:\d{2}$/)) {
6104
+ this.displayValue = s.substring(0, 5);
6105
+ return;
6106
+ }
6107
+ // Formato curto "HH:mm"
6108
+ if (s.match(/^\d{2}:\d{2}$/)) {
6109
+ // Normalizar para ISO no control
6110
+ this.setControlValue(`${s}:00`);
6111
+ this.displayValue = s;
6112
+ return;
6113
+ }
6114
+ // ISO datetime string com T
6115
+ if (s.includes('T')) {
6116
+ const d = new Date(s);
6117
+ if (!isNaN(d.getTime())) {
6118
+ const iso = this.dateToIso(d);
6119
+ this.setControlValue(iso);
6120
+ this.displayValue = iso.substring(0, 5);
6121
+ }
6122
+ return;
6123
+ }
6124
+ // Valor incompleto
6125
+ this.displayValue = s.includes('_') ? null : null;
6126
+ }
6127
+ /** Chamado quando o usuário completa a digitação da máscara */
6128
+ onDisplayValueComplete() {
6129
+ this.syncControlFromDisplay();
6130
+ }
6131
+ /** Chamado quando o input perde foco */
6132
+ onDisplayValueBlur() {
6133
+ this.syncControlFromDisplay();
6134
+ }
6135
+ /**
6136
+ * Converte o displayValue ("HH:mm") para ISO ("HH:mm:ss") e seta no FormControl.
6137
+ */
6138
+ syncControlFromDisplay() {
6139
+ const control = this.form.get(this.field.field);
6140
+ if (!control) {
6141
+ return;
6142
+ }
6143
+ if (!this.displayValue || this.displayValue.includes('_')) {
6144
+ this.setControlValue(null);
6145
+ control.markAsDirty();
6146
+ return;
6147
+ }
6148
+ // Validar formato HH:mm
6149
+ if (this.displayValue.match(/^\d{2}:\d{2}$/)) {
6150
+ const [h, m] = this.displayValue.split(':').map(p => parseInt(p, 10));
6151
+ if (h >= 0 && h <= 23 && m >= 0 && m <= 59) {
6152
+ this.setControlValue(`${this.displayValue}:00`);
6153
+ control.markAsDirty();
6154
+ }
6155
+ }
6156
+ }
6157
+ /** Seta o valor no control sem disparar o subscriber interno */
6158
+ setControlValue(value) {
6159
+ const control = this.form.get(this.field.field);
6160
+ if (!control) {
6161
+ return;
6162
+ }
6163
+ this.updatingFromControl = true;
6164
+ control.setValue(value, { emitEvent: true });
6165
+ this.updatingFromControl = false;
6166
+ }
6167
+ // ==================== CONVERSÕES ====================
6168
+ /** Converte Date para ISO "HH:mm:ss" */
6169
+ dateToIso(date) {
6170
+ return `${String(date.getHours()).padStart(2, '0')}:${String(date.getMinutes()).padStart(2, '0')}:00`;
5622
6171
  }
5623
- stringToDate(value) {
5624
- if (!value || value.includes('_'))
6172
+ /** Converte display "HH:mm" para Date */
6173
+ displayToDate(value) {
6174
+ if (!value || value.includes('_')) {
5625
6175
  return null;
6176
+ }
5626
6177
  const parts = value.split(':');
5627
- if (parts.length !== 2)
6178
+ if (parts.length !== 2) {
5628
6179
  return null;
6180
+ }
5629
6181
  const hours = parseInt(parts[0], 10);
5630
6182
  const minutes = parseInt(parts[1], 10);
5631
- if (isNaN(hours) || isNaN(minutes) || hours < 0 || hours > 23 || minutes < 0 || minutes > 59)
6183
+ if (isNaN(hours) || isNaN(minutes) || hours < 0 || hours > 23 || minutes < 0 || minutes > 59) {
5632
6184
  return null;
6185
+ }
5633
6186
  const d = new Date();
5634
6187
  d.setHours(hours, minutes, 0, 0);
5635
6188
  return d;
5636
6189
  }
6190
+ // ==================== TIME PICKER ====================
5637
6191
  toggleTimePicker(event) {
5638
6192
  event.stopPropagation();
5639
- const control = this.form.get(this.field.field);
5640
- this.timeValue = control?.value ? this.stringToDate(control.value) : new Date();
6193
+ if (this.displayValue && !this.displayValue.includes('_')) {
6194
+ this.timeValue = this.displayToDate(this.displayValue);
6195
+ }
6196
+ else {
6197
+ this.timeValue = new Date();
6198
+ }
5641
6199
  setTimeout(() => {
5642
6200
  if (this.timePicker) {
5643
6201
  this.timePicker.showOverlay();
@@ -5645,12 +6203,20 @@ class DynamicFieldTimeComponent extends DynamicFieldBaseComponent {
5645
6203
  });
5646
6204
  }
5647
6205
  onTimeSelect(date) {
5648
- const control = this.form.get(this.field.field);
5649
- if (control && date) {
5650
- control.setValue(this.dateToTimeString(date));
5651
- control.markAsDirty();
6206
+ if (date) {
6207
+ const iso = this.dateToIso(date);
6208
+ this.displayValue = iso.substring(0, 5);
6209
+ this.setControlValue(iso);
6210
+ const control = this.form.get(this.field.field);
6211
+ if (control) {
6212
+ control.markAsDirty();
6213
+ }
5652
6214
  }
5653
6215
  }
6216
+ isControlDisabled() {
6217
+ const control = this.form.get(this.field.field);
6218
+ return control ? control.disabled : false;
6219
+ }
5654
6220
  getPlaceholder() {
5655
6221
  return this.field.placeholder || 'HH:mm';
5656
6222
  }
@@ -5661,14 +6227,18 @@ class DynamicFieldTimeComponent extends DynamicFieldBaseComponent {
5661
6227
  <div class="time-input-group">
5662
6228
  <p-inputmask
5663
6229
  [inputId]="field.field"
5664
- [formControlName]="field.field"
6230
+ [(ngModel)]="displayValue"
6231
+ [ngModelOptions]="{standalone: true}"
5665
6232
  mask="99:99"
5666
6233
  [placeholder]="getPlaceholder()"
5667
6234
  slotChar="_"
5668
6235
  [autoClear]="false"
5669
6236
  styleClass="time-input"
5670
6237
  [class.ng-invalid]="isFieldInvalid()"
5671
- [class.ng-dirty]="isFieldDirty()">
6238
+ [class.ng-dirty]="isFieldDirty()"
6239
+ [disabled]="isControlDisabled()"
6240
+ (onComplete)="onDisplayValueComplete()"
6241
+ (onBlur)="onDisplayValueBlur()">
5672
6242
  </p-inputmask>
5673
6243
  <div class="time-buttons">
5674
6244
  <p-button
@@ -5686,6 +6256,7 @@ class DynamicFieldTimeComponent extends DynamicFieldBaseComponent {
5686
6256
  <p-calendar
5687
6257
  #timePicker
5688
6258
  [(ngModel)]="timeValue"
6259
+ [ngModelOptions]="{standalone: true}"
5689
6260
  [timeOnly]="true"
5690
6261
  [showSeconds]="false"
5691
6262
  [hourFormat]="'24'"
@@ -5697,7 +6268,7 @@ class DynamicFieldTimeComponent extends DynamicFieldBaseComponent {
5697
6268
  </p-calendar>
5698
6269
  </div>
5699
6270
  </sia-dynamic-field-wrapper>
5700
- `, isInline: true, styles: [".time-field{width:100%;position:relative}.time-input-group{position:relative;width:100%;::ng-deep p-inputmask{width:100%;display:block;input{width:100%;padding-right:2.5rem}}.time-buttons{position:absolute;right:4px;top:50%;transform:translateY(-50%);display:flex;gap:2px;z-index:10}::ng-deep .p-button{width:2rem!important;height:2rem!important;min-width:2rem!important;min-height:2rem!important;padding:0!important;margin:0!important;display:flex!important;align-items:center!important;justify-content:center!important;box-sizing:border-box!important;.p-button-icon{margin:0!important;font-size:.875rem!important}.p-button-label{display:none!important}}}::ng-deep .hidden-calendar-input{position:absolute;width:0;height:0;overflow:hidden;opacity:0;pointer-events:none;input{width:0;height:0;padding:0;border:none}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: InputMaskModule }, { kind: "component", type: i4$2.InputMask, selector: "p-inputmask, p-inputMask, p-input-mask", inputs: ["type", "slotChar", "autoClear", "showClear", "style", "inputId", "styleClass", "placeholder", "size", "maxlength", "tabindex", "title", "variant", "ariaLabel", "ariaLabelledBy", "ariaRequired", "disabled", "readonly", "unmask", "name", "required", "characterPattern", "autofocus", "autoFocus", "autocomplete", "keepBuffer", "mask"], outputs: ["onComplete", "onFocus", "onBlur", "onInput", "onKeydown", "onClear"] }, { kind: "ngmodule", type: CalendarModule }, { kind: "component", type: i5$1.Calendar, selector: "p-calendar", inputs: ["iconDisplay", "style", "styleClass", "inputStyle", "inputId", "name", "inputStyleClass", "placeholder", "ariaLabelledBy", "ariaLabel", "iconAriaLabel", "disabled", "dateFormat", "multipleSeparator", "rangeSeparator", "inline", "showOtherMonths", "selectOtherMonths", "showIcon", "fluid", "icon", "appendTo", "readonlyInput", "shortYearCutoff", "monthNavigator", "yearNavigator", "hourFormat", "timeOnly", "stepHour", "stepMinute", "stepSecond", "showSeconds", "required", "showOnFocus", "showWeek", "startWeekFromFirstDayOfYear", "showClear", "dataType", "selectionMode", "maxDateCount", "showButtonBar", "todayButtonStyleClass", "clearButtonStyleClass", "autofocus", "autoZIndex", "baseZIndex", "panelStyleClass", "panelStyle", "keepInvalid", "hideOnDateTimeSelect", "touchUI", "timeSeparator", "focusTrap", "showTransitionOptions", "hideTransitionOptions", "tabindex", "variant", "minDate", "maxDate", "disabledDates", "disabledDays", "yearRange", "showTime", "responsiveOptions", "numberOfMonths", "firstDayOfWeek", "locale", "view", "defaultDate"], outputs: ["onFocus", "onBlur", "onClose", "onSelect", "onClear", "onInput", "onTodayClick", "onClearClick", "onMonthChange", "onYearChange", "onClickOutside", "onShow"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i6.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "fluid", "buttonProps"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i7$1.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "appendTo", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions"] }, { kind: "component", type: DynamicFieldWrapperComponent, selector: "sia-dynamic-field-wrapper", inputs: ["field", "form", "mode"] }] });
6271
+ `, isInline: true, styles: [".time-field{width:100%;position:relative}.time-input-group{position:relative;width:100%;::ng-deep p-inputmask{width:100%;display:block;input{width:100%;padding-right:2.5rem}}.time-buttons{position:absolute;right:4px;top:50%;transform:translateY(-50%);display:flex;gap:2px;z-index:10}::ng-deep .p-button{width:2rem!important;height:2rem!important;min-width:2rem!important;min-height:2rem!important;padding:0!important;margin:0!important;display:flex!important;align-items:center!important;justify-content:center!important;box-sizing:border-box!important;.p-button-icon{margin:0!important;font-size:.875rem!important}.p-button-label{display:none!important}}}::ng-deep .hidden-calendar-input{position:absolute;width:0;height:0;overflow:hidden;opacity:0;pointer-events:none;input{width:0;height:0;padding:0;border:none}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: InputMaskModule }, { kind: "component", type: i4$2.InputMask, selector: "p-inputmask, p-inputMask, p-input-mask", inputs: ["type", "slotChar", "autoClear", "showClear", "style", "inputId", "styleClass", "placeholder", "size", "maxlength", "tabindex", "title", "variant", "ariaLabel", "ariaLabelledBy", "ariaRequired", "disabled", "readonly", "unmask", "name", "required", "characterPattern", "autofocus", "autoFocus", "autocomplete", "keepBuffer", "mask"], outputs: ["onComplete", "onFocus", "onBlur", "onInput", "onKeydown", "onClear"] }, { kind: "ngmodule", type: CalendarModule }, { kind: "component", type: i5$1.Calendar, selector: "p-calendar", inputs: ["iconDisplay", "style", "styleClass", "inputStyle", "inputId", "name", "inputStyleClass", "placeholder", "ariaLabelledBy", "ariaLabel", "iconAriaLabel", "disabled", "dateFormat", "multipleSeparator", "rangeSeparator", "inline", "showOtherMonths", "selectOtherMonths", "showIcon", "fluid", "icon", "appendTo", "readonlyInput", "shortYearCutoff", "monthNavigator", "yearNavigator", "hourFormat", "timeOnly", "stepHour", "stepMinute", "stepSecond", "showSeconds", "required", "showOnFocus", "showWeek", "startWeekFromFirstDayOfYear", "showClear", "dataType", "selectionMode", "maxDateCount", "showButtonBar", "todayButtonStyleClass", "clearButtonStyleClass", "autofocus", "autoZIndex", "baseZIndex", "panelStyleClass", "panelStyle", "keepInvalid", "hideOnDateTimeSelect", "touchUI", "timeSeparator", "focusTrap", "showTransitionOptions", "hideTransitionOptions", "tabindex", "variant", "minDate", "maxDate", "disabledDates", "disabledDays", "yearRange", "showTime", "responsiveOptions", "numberOfMonths", "firstDayOfWeek", "locale", "view", "defaultDate"], outputs: ["onFocus", "onBlur", "onClose", "onSelect", "onClear", "onInput", "onTodayClick", "onClearClick", "onMonthChange", "onYearChange", "onClickOutside", "onShow"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i6.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "fluid", "buttonProps"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i7$1.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "appendTo", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions"] }, { kind: "component", type: DynamicFieldWrapperComponent, selector: "sia-dynamic-field-wrapper", inputs: ["field", "form", "mode"] }] });
5701
6272
  }
5702
6273
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: DynamicFieldTimeComponent, decorators: [{
5703
6274
  type: Component,
@@ -5716,14 +6287,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
5716
6287
  <div class="time-input-group">
5717
6288
  <p-inputmask
5718
6289
  [inputId]="field.field"
5719
- [formControlName]="field.field"
6290
+ [(ngModel)]="displayValue"
6291
+ [ngModelOptions]="{standalone: true}"
5720
6292
  mask="99:99"
5721
6293
  [placeholder]="getPlaceholder()"
5722
6294
  slotChar="_"
5723
6295
  [autoClear]="false"
5724
6296
  styleClass="time-input"
5725
6297
  [class.ng-invalid]="isFieldInvalid()"
5726
- [class.ng-dirty]="isFieldDirty()">
6298
+ [class.ng-dirty]="isFieldDirty()"
6299
+ [disabled]="isControlDisabled()"
6300
+ (onComplete)="onDisplayValueComplete()"
6301
+ (onBlur)="onDisplayValueBlur()">
5727
6302
  </p-inputmask>
5728
6303
  <div class="time-buttons">
5729
6304
  <p-button
@@ -5741,6 +6316,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
5741
6316
  <p-calendar
5742
6317
  #timePicker
5743
6318
  [(ngModel)]="timeValue"
6319
+ [ngModelOptions]="{standalone: true}"
5744
6320
  [timeOnly]="true"
5745
6321
  [showSeconds]="false"
5746
6322
  [hourFormat]="'24'"
@@ -6198,55 +6774,42 @@ class DynamicFormComponent {
6198
6774
  processEntityData(entity) {
6199
6775
  const data = { ...entity };
6200
6776
  this.allFields.forEach(field => {
6777
+ // Date: pass ISO string directly — field component handles display conversion
6201
6778
  if (field.type === 'date' && data[field.field]) {
6202
6779
  const v = data[field.field];
6203
- if (typeof v === 'string') {
6204
- // Convert yyyy-MM-dd to locale format string
6205
- const parts = v.substring(0, 10).split('-');
6206
- if (parts.length === 3) {
6207
- const [year, month, day] = parts;
6208
- const locale = this.translationService.getCurrentLanguage();
6209
- data[field.field] = locale === 'en-US'
6210
- ? `${month}/${day}/${year}`
6211
- : `${day}/${month}/${year}`;
6212
- }
6213
- }
6214
- else if (v instanceof Date) {
6215
- const day = String(v.getDate()).padStart(2, '0');
6216
- const month = String(v.getMonth() + 1).padStart(2, '0');
6217
- const year = String(v.getFullYear());
6218
- const locale = this.translationService.getCurrentLanguage();
6219
- data[field.field] = locale === 'en-US'
6220
- ? `${month}/${day}/${year}`
6221
- : `${day}/${month}/${year}`;
6780
+ if (v instanceof Date) {
6781
+ data[field.field] = `${v.getFullYear()}-${String(v.getMonth() + 1).padStart(2, '0')}-${String(v.getDate()).padStart(2, '0')}`;
6222
6782
  }
6783
+ // String values (ISO "yyyy-MM-dd") are passed as-is to the field component
6223
6784
  }
6785
+ // Time: normalize to "HH:mm:ss" — field component handles display conversion
6224
6786
  if (field.type === 'time' && data[field.field]) {
6225
6787
  const v = data[field.field];
6226
- if (typeof v === 'string') {
6788
+ if (v instanceof Date) {
6789
+ data[field.field] = `${String(v.getHours()).padStart(2, '0')}:${String(v.getMinutes()).padStart(2, '0')}:00`;
6790
+ }
6791
+ else if (typeof v === 'string') {
6227
6792
  const parts = v.split(':');
6228
- if (parts.length >= 2) {
6229
- data[field.field] = `${parts[0].padStart(2, '0')}:${parts[1].padStart(2, '0')}`;
6793
+ if (parts.length === 2) {
6794
+ data[field.field] = `${parts[0].padStart(2, '0')}:${parts[1].padStart(2, '0')}:00`;
6230
6795
  }
6231
- }
6232
- else if (v instanceof Date) {
6233
- data[field.field] = `${String(v.getHours()).padStart(2, '0')}:${String(v.getMinutes()).padStart(2, '0')}`;
6796
+ // "HH:mm:ss" is passed as-is
6234
6797
  }
6235
6798
  }
6799
+ // Datetime: normalize to ISO string — field component handles display conversion
6236
6800
  if (field.type === 'datetime' && data[field.field]) {
6237
6801
  const v = data[field.field];
6238
- const d = typeof v === 'string' ? new Date(v) : v;
6239
- if (d instanceof Date && !isNaN(d.getTime())) {
6240
- const day = String(d.getDate()).padStart(2, '0');
6241
- const month = String(d.getMonth() + 1).padStart(2, '0');
6242
- const year = String(d.getFullYear());
6243
- const hours = String(d.getHours()).padStart(2, '0');
6244
- const minutes = String(d.getMinutes()).padStart(2, '0');
6245
- const locale = this.translationService.getCurrentLanguage();
6246
- data[field.field] = locale === 'en-US'
6247
- ? `${month}/${day}/${year} ${hours}:${minutes}`
6248
- : `${day}/${month}/${year} ${hours}:${minutes}`;
6802
+ if (v instanceof Date) {
6803
+ data[field.field] = v.toISOString();
6804
+ }
6805
+ else if (typeof v === 'string' && !v.includes('T')) {
6806
+ // Try to parse non-ISO string
6807
+ const d = new Date(v);
6808
+ if (!isNaN(d.getTime())) {
6809
+ data[field.field] = d.toISOString();
6810
+ }
6249
6811
  }
6812
+ // ISO strings with T are passed as-is
6250
6813
  }
6251
6814
  if (field.type === 'lookup' && data[field.field]) {
6252
6815
  const v = data[field.field];
@@ -6295,49 +6858,20 @@ class DynamicFormComponent {
6295
6858
  out[field.field] = null;
6296
6859
  }
6297
6860
  else if (field.type === 'date') {
6298
- if (typeof v === 'string' && v.match(/^\d{2}\/\d{2}\/\d{4}$/)) {
6299
- // Parse locale string to yyyy-MM-dd
6300
- const parts = v.split('/');
6301
- const locale = this.translationService.getCurrentLanguage();
6302
- const [day, month, year] = locale === 'en-US'
6303
- ? [parts[1], parts[0], parts[2]]
6304
- : [parts[0], parts[1], parts[2]];
6305
- out[field.field] = `${year}-${month}-${day}`;
6306
- }
6307
- else if (v instanceof Date) {
6308
- out[field.field] = `${v.getFullYear()}-${String(v.getMonth() + 1).padStart(2, '0')}-${String(v.getDate()).padStart(2, '0')}`;
6309
- }
6310
- else if (!v || (typeof v === 'string' && v.includes('_'))) {
6861
+ // Value is already in ISO format "yyyy-MM-dd" from the field component
6862
+ if (!v || (typeof v === 'string' && v.includes('_'))) {
6311
6863
  out[field.field] = null;
6312
6864
  }
6313
6865
  }
6314
6866
  else if (field.type === 'time') {
6315
- if (typeof v === 'string' && v.match(/^\d{2}:\d{2}$/)) {
6316
- out[field.field] = `${v}:00`;
6317
- }
6318
- else if (v instanceof Date) {
6319
- out[field.field] = `${String(v.getHours()).padStart(2, '0')}:${String(v.getMinutes()).padStart(2, '0')}:00`;
6320
- }
6321
- else if (!v || (typeof v === 'string' && v.includes('_'))) {
6867
+ // Value is already in ISO format "HH:mm:ss" from the field component
6868
+ if (!v || (typeof v === 'string' && v.includes('_'))) {
6322
6869
  out[field.field] = null;
6323
6870
  }
6324
6871
  }
6325
6872
  else if (field.type === 'datetime') {
6326
- if (typeof v === 'string' && v.match(/^\d{2}\/\d{2}\/\d{4} \d{2}:\d{2}$/)) {
6327
- // Parse locale string to Date
6328
- const [datePart, timePart] = v.split(' ');
6329
- const dateParts = datePart.split('/');
6330
- const locale = this.translationService.getCurrentLanguage();
6331
- const [day, month, year] = locale === 'en-US'
6332
- ? [parseInt(dateParts[1], 10), parseInt(dateParts[0], 10), parseInt(dateParts[2], 10)]
6333
- : [parseInt(dateParts[0], 10), parseInt(dateParts[1], 10), parseInt(dateParts[2], 10)];
6334
- const [hours, minutes] = timePart.split(':').map(p => parseInt(p, 10));
6335
- out[field.field] = new Date(year, month - 1, day, hours, minutes).toISOString();
6336
- }
6337
- else if (v instanceof Date) {
6338
- out[field.field] = v.toISOString();
6339
- }
6340
- else if (!v || (typeof v === 'string' && v.includes('_'))) {
6873
+ // Value is already in ISO format from the field component
6874
+ if (!v || (typeof v === 'string' && v.includes('_'))) {
6341
6875
  out[field.field] = null;
6342
6876
  }
6343
6877
  }
@@ -6497,17 +7031,8 @@ class DynamicFormComponent {
6497
7031
  if (field?.type === 'lookup' && value && typeof value === 'object') {
6498
7032
  control.setValue(value);
6499
7033
  }
6500
- else if (field?.type === 'date' && value && typeof value === 'string') {
6501
- const parts = value.substring(0, 10).split('-');
6502
- if (parts.length === 3) {
6503
- const d = new Date(+parts[0], +parts[1] - 1, +parts[2]);
6504
- control.setValue(!isNaN(d.getTime()) ? d : value);
6505
- }
6506
- else {
6507
- control.setValue(value);
6508
- }
6509
- }
6510
7034
  else {
7035
+ // Date, time, datetime values are already in ISO format in the control
6511
7036
  control.setValue(value);
6512
7037
  }
6513
7038
  }
@@ -6533,9 +7058,7 @@ class DynamicFormComponent {
6533
7058
  if (field?.type === 'lookup' && value && typeof value !== 'object') {
6534
7059
  value = { id: value };
6535
7060
  }
6536
- if (field?.type === 'date' && value instanceof Date) {
6537
- value = `${value.getFullYear()}-${String(value.getMonth() + 1).padStart(2, '0')}-${String(value.getDate()).padStart(2, '0')}`;
6538
- }
7061
+ // Date, time, datetime values are already in ISO format from the field component
6539
7062
  this.fieldSave.emit({ field: fieldName, value });
6540
7063
  }
6541
7064
  cancelInlineEdit() {
@@ -7553,5 +8076,5 @@ const throttle = (func, limit) => {
7553
8076
  * Generated bundle index. Do not edit.
7554
8077
  */
7555
8078
 
7556
- export { AngularComponentsModule, AuthService, BreadcrumbComponent, BulkDeleteDialogComponent, CnpjPipe, CookieService, CpfPipe, DEFAULT_LANGUAGE, DateFormatPipe, DocumentMaskDirective, DocumentPipe, DynamicFieldCheckboxComponent, DynamicFieldDateComponent, DynamicFieldDropdownComponent, DynamicFieldImageComponent, DynamicFieldLookupComponent, DynamicFieldMultiselectComponent, DynamicFieldNumberComponent, DynamicFieldTextComponent, DynamicFieldTextareaComponent, DynamicFieldTimeComponent, DynamicFieldWrapperComponent, DynamicFormComponent, EntityListBaseComponent, EntityService, ExportDialogComponent, FieldType, IassistIconComponent, KanbanBoardComponent, LoadingComponent, LocaleService, MaskService, MoneyMaskDirective, MoneyPipe, PermissionService, PhoneMaskDirective, PhonePipe, PostalCodeMaskDirective, PostalCodePipe, SUPPORTED_LANGUAGES, SeniorPreset, SeniorTokenService, TRANSLATION_CONFIG, TableLoadingDirective, ThemeService, TranslatePipe, TranslationHelper, TranslationService, WebSocketService, apiInterceptor, createFilterString, createFilterTokens, debounce, deepClone, deepEqual, escapeFilterValue, getEnumQuery, getLabelValueRequest, getLanguageInfo, getProp, getSuggestionValue, getTypeInformation, isValidFilter, mapTokenLocaleToLanguage, mergeUnique, provideSeniorPrimeNG, resolveRefs, setProp, throttle };
8079
+ export { AngularComponentsModule, AuthService, BreadcrumbComponent, BulkDeleteDialogComponent, CnpjPipe, CookieService, CpfPipe, DEFAULT_LANGUAGE, DateFormatPipe, DocumentMaskDirective, DocumentPipe, DynamicFieldCheckboxComponent, DynamicFieldDateComponent, DynamicFieldDropdownComponent, DynamicFieldImageComponent, DynamicFieldLookupComponent, DynamicFieldMultiselectComponent, DynamicFieldNumberComponent, DynamicFieldTextComponent, DynamicFieldTextareaComponent, DynamicFieldTimeComponent, DynamicFieldWrapperComponent, DynamicFormComponent, EntityListBaseComponent, EntityService, ExportDialogComponent, FieldType, IassistIconComponent, KanbanBoardComponent, LoadingComponent, LocaleService, MaskService, MoneyMaskDirective, MoneyPipe, PermissionService, PhoneMaskDirective, PhonePipe, PostalCodeMaskDirective, PostalCodePipe, SUPPORTED_LANGUAGES, SeniorPreset, SeniorTokenService, StepsComponent, TRANSLATION_CONFIG, TableLoadingDirective, ThemeService, TranslatePipe, TranslationHelper, TranslationService, WebSocketService, apiInterceptor, createFilterString, createFilterTokens, debounce, deepClone, deepEqual, escapeFilterValue, getEnumQuery, getLabelValueRequest, getLanguageInfo, getProp, getSuggestionValue, getTypeInformation, isValidFilter, mapTokenLocaleToLanguage, mergeUnique, provideSeniorPrimeNG, resolveRefs, setProp, throttle };
7557
8080
  //# sourceMappingURL=seniorsistemas-components-ai.mjs.map