From 5106cdd414b05d1e81689e3fb8efb5fda78b4d01 Mon Sep 17 00:00:00 2001
From: f <f@sutty.nl>
Date: Wed, 19 Jun 2024 18:16:49 -0300
Subject: [PATCH 1/5] =?UTF-8?q?fix:=20m=C3=A1s=20arreglos=20tema=20oscuro?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 app/assets/stylesheets/application.scss | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss
index ce0b0924..32530ce5 100644
--- a/app/assets/stylesheets/application.scss
+++ b/app/assets/stylesheets/application.scss
@@ -26,6 +26,10 @@ $card-bg: var(--background);
 $card-border-color: var(--card-border-color);
 $input-bg: var(--background);
 $input-color: var(--foreground);
+$btn-bg-color: var(--btn-bg-color);
+$btn-color: var(--btn-color);
+$input-group-addon-bg: var(--btn-bg-color);
+$custom-file-color: var(--btn-color);
 
 $spacers: (
   2-plus: 0.75rem

From 6f30727a7b7d0026c24c64d28e59294316f96b03 Mon Sep 17 00:00:00 2001
From: f <f@sutty.nl>
Date: Wed, 19 Jun 2024 18:16:58 -0300
Subject: [PATCH 2/5] fix: traducir campo de imagen

---
 app/assets/stylesheets/application.scss | 14 ++++++++------
 app/views/posts/attributes/_file.haml   |  1 +
 app/views/posts/attributes/_image.haml  |  1 +
 3 files changed, 10 insertions(+), 6 deletions(-)

diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss
index 32530ce5..d8fe8c9f 100644
--- a/app/assets/stylesheets/application.scss
+++ b/app/assets/stylesheets/application.scss
@@ -11,6 +11,14 @@ $colors: (
   "magenta": $magenta
 );
 
+// TODO: Encontrar la forma de generar esto desde los locales de Rails
+$custom-file-text: (
+  en: "Browse",
+  es: "Buscar archivo",
+  pt: "Buscar arquivo",
+  pt-BR: "Buscar arquivo"
+);
+
 // Redefinir variables de Bootstrap
 $primary: $magenta;
 $secondary: $black;
@@ -111,12 +119,6 @@ $sizes: (
   }
 }
 
-// TODO: Encontrar la forma de generar esto desde los locales de Rails
-$custom-file-text: (
-  en: 'Browse',
-  es: 'Buscar archivo'
-);
-
 @font-face {
   font-family: 'Saira';
   font-style: normal;
diff --git a/app/views/posts/attributes/_file.haml b/app/views/posts/attributes/_file.haml
index 20c27399..007f6b9f 100644
--- a/app/views/posts/attributes/_file.haml
+++ b/app/views/posts/attributes/_file.haml
@@ -26,6 +26,7 @@
     = file_field(*field_name_for(base, attribute, :path),
       **field_options(attribute, metadata, required: (metadata.required && !metadata.path?)),
       class: "custom-file-input #{invalid(post, attribute)}",
+      lang: locale,
       data: { target: 'file-preview.input', action: 'file-preview#update' })
     = label_tag "#{base}_#{attribute}_path",
       post_label_t(attribute, :path, post: post), class: 'custom-file-label'
diff --git a/app/views/posts/attributes/_image.haml b/app/views/posts/attributes/_image.haml
index 241a78e8..03d9d15f 100644
--- a/app/views/posts/attributes/_image.haml
+++ b/app/views/posts/attributes/_image.haml
@@ -23,6 +23,7 @@
       **field_options(attribute, metadata, required: (metadata.required && !metadata.path?)),
       class: "custom-file-input #{invalid(post, attribute)}",
       accept: ActiveStorage.web_image_content_types.join(','),
+      lang: locale,
       data: { target: 'file-preview.input', action: 'file-preview#update' })
     = label_tag "#{base}_#{attribute}_path",
       post_label_t(attribute, :path, post: post), class: 'custom-file-label'

From e2af1f215ac389e6ae87e1d4f3c5546992adfc8f Mon Sep 17 00:00:00 2001
From: f <f@sutty.nl>
Date: Wed, 19 Jun 2024 18:19:07 -0300
Subject: [PATCH 3/5] feat: las notificaciones se manejan con eventos

---
 .../controllers/form_validation_controller.js | 37 +++++++---------
 .../controllers/notification_controller.js    | 43 +++++++++++++++++++
 app/views/posts/_form.haml                    | 14 ++++--
 app/views/posts/_submit.haml                  | 12 +-----
 app/views/posts/_validation.haml              | 16 +++++++
 config/locales/en.yml                         |  5 ++-
 config/locales/es.yml                         |  7 ++-
 7 files changed, 96 insertions(+), 38 deletions(-)
 create mode 100644 app/javascript/controllers/notification_controller.js
 create mode 100644 app/views/posts/_validation.haml

diff --git a/app/javascript/controllers/form_validation_controller.js b/app/javascript/controllers/form_validation_controller.js
index c9817e27..85e7bb86 100644
--- a/app/javascript/controllers/form_validation_controller.js
+++ b/app/javascript/controllers/form_validation_controller.js
@@ -3,6 +3,16 @@ import { Controller } from "stimulus";
 export default class extends Controller {
   static targets = ["invalid", "submitting"];
 
+  // @todo Stimulus >1
+  get submittingIdValue() {
+    return this.element.dataset?.formValidationSubmittingIdValue;
+  }
+
+  // @todo Stimulus >1
+  get invalidIdValue() {
+    return this.element.dataset?.formValidationInvalidIdValue;
+  }
+
   connect() {
     this.element.setAttribute("novalidate", true);
 
@@ -19,34 +29,19 @@ export default class extends Controller {
 
   submit(event = undefined) {
     event?.preventDefault();
-    event?.stopPropagation();
 
     if (this.element.reportValidity()) {
       this.element.classList.remove("was-validated");
-      this.element.submit();
 
-      this.show(this.submittingTargets);
-      this.hide(this.invalidTargets);
+      if (!this.element.hasAttribute("hx-post")) this.element.submit();
+
+      window.dispatchEvent(new CustomEvent("notification:show", { detail: { id: this.submittingIdValue } }));
     } else {
+      event?.stopPropagation();
+
       this.element.classList.add("was-validated");
-      this.hide(this.submittingTargets);
-      this.show(this.invalidTargets);
-    }
-  }
 
-  show(elements) {
-    for (const element of elements) {
-      element.classList.remove("d-none");
-
-      setTimeout(() => element.classList.add("show"), 1);
-    }
-  }
-
-  hide(elements) {
-    for (const element of elements) {
-      element.classList.remove("show");
-
-      setTimeout(() => element.classList.add("d-none"), 2000);
+      window.dispatchEvent(new CustomEvent("notification:show", { detail: { id: this.invalidIdValue } }));
     }
   }
 }
diff --git a/app/javascript/controllers/notification_controller.js b/app/javascript/controllers/notification_controller.js
new file mode 100644
index 00000000..7fbe3b5a
--- /dev/null
+++ b/app/javascript/controllers/notification_controller.js
@@ -0,0 +1,43 @@
+import { Controller } from "stimulus";
+
+/*
+ * Solo se puede mostrar una notificación a la vez
+ */
+export default class extends Controller {
+  // @todo Stimulus >1
+  get showClasses() {
+    return (this.element.dataset?.notificationShowClass || "").split(" ").filter(x => x);
+  }
+
+  // @todo Stimulus >1
+  get hideClasses() {
+    return (this.element.dataset?.notificationHideClass || "").split(" ").filter(x => x);
+  }
+
+  /*
+   * Al recibir el evento de mostrar, si no está dirigido al elemento
+   * actual, se oculta.
+   */
+  show(event = undefined) {
+    if (event?.detail?.id !== this.element.id) {
+      this.hide({ detail: { id: this.element.id } });
+      return;
+    }
+
+    this.element.classList.remove("d-none");
+
+    setTimeout(() => {
+      this.element.classList.remove(...this.hideClasses);
+      this.element.classList.add(...this.showClasses);
+    }, 1);
+  }
+
+  hide(event = undefined) {
+    if (event?.detail?.id !== this.element.id) return;
+
+    this.element.classList.remove(...this.showClasses);
+    this.element.classList.add(...this.hideClasses);
+
+    setTimeout(() => this.element.classList.add("d-none"), 150);
+  }
+}
diff --git a/app/views/posts/_form.haml b/app/views/posts/_form.haml
index aa8f3d1d..3e09cb72 100644
--- a/app/views/posts/_form.haml
+++ b/app/views/posts/_form.haml
@@ -31,11 +31,19 @@
   end
 
 - dir = t("locales.#{@locale}.dir")
+- submitting_id = random_id
+- invalid_id = random_id
+- data = {}
+- data[:controller] = 'unsaved-changes form-validation'
+- data[:action] = 'unsaved-changes#submit form-validation#submit beforeunload@window->unsaved-changes#unsaved turbolinks:before-visit@window->unsaved-changes#unsavedTurbolinks'
+- data[:'unsaved-changes-confirm-value'] = t('.confirm')
+- data[:'form-validation-submitting-id-value'] = submitting_id
+- data[:'form-validation-invalid-id-value'] = invalid_id
 
 -# Comienza el formulario
-= form_tag url, method: method, class: "form post #{extra_class}", multipart: true, data: { controller: 'unsaved-changes form-validation', action: 'unsaved-changes#submit form-validation#submit beforeunload@window->unsaved-changes#unsaved turbolinks:before-visit@window->unsaved-changes#unsavedTurbolinks', 'unsaved-changes-confirm-value': t('.confirm') } do
+= form_tag url, method: method, class: "form post #{extra_class}", multipart: true, data: data do
   -# Botones de guardado
-  = render 'posts/submit', site: site, post: post
+  = render 'posts/submit', site: site, post: post, invalid: invalid_id, submitting: submitting_id
 
   = hidden_field_tag 'post[layout]', post.layout.name
 
@@ -43,7 +51,7 @@
   = render 'posts/attributes', site: site, post: post, dir: dir, base: 'post', locale: @locale
 
   -# Botones de guardado
-  = render 'posts/submit', site: site, post: post
+  = render 'posts/submit', site: site, post: post, invalid: invalid_id, submitting: submitting_id
 
 -# Formularios usados por los modales
 = yield(:post_form)
diff --git a/app/views/posts/_submit.haml b/app/views/posts/_submit.haml
index c3064c53..41d6f420 100644
--- a/app/views/posts/_submit.haml
+++ b/app/views/posts/_submit.haml
@@ -1,11 +1,3 @@
-- invalid_help = site.config.fetch('invalid_help', t('.invalid_help'))
-- submitting_help = site.config.fetch('submitting_help', t('.submitting_help'))
-
 .d-flex.flex-column.flex-md-row.align-items-start.mb-3
-  %div
-    = submit_tag t('.save'), class: 'btn btn-secondary submit-post'
-  .d-flex.flex-column.position-relative
-    = render 'bootstrap/alert', class: 'm-0 d-none fade', data: { target: 'form-validation.invalid' } do
-      = invalid_help
-    = render 'bootstrap/alert', class: 'm-0 d-none fade position-absolute top-0 left-0', data: { target: 'form-validation.submitting' } do
-      = submitting_help
+  %div= submit_tag t('.save'), class: 'btn btn-secondary submit-post'
+  = render 'posts/validation', site: site, submitting: { id: submitting }, invalid: { id: invalid }
diff --git a/app/views/posts/_validation.haml b/app/views/posts/_validation.haml
new file mode 100644
index 00000000..c28a743a
--- /dev/null
+++ b/app/views/posts/_validation.haml
@@ -0,0 +1,16 @@
+- invalid = site.config.fetch('invalid', t('.invalid'))
+- submitting = site.config.fetch('submitting', t('.submitting'))
+- %i[invalid submitting].each do |key|
+  - local_assigns[key] ||= {}
+  - local_assigns[key][:data] ||= {}
+  - local_assigns[key][:data][:target] ||= "form-validation.#{key}"
+  - local_assigns[key][:data][:action] ||= 'notification:show@window->notification#show'
+  - local_assigns[key][:data][:controller] ||= 'notification'
+  - local_assigns[key][:data][:'notification-hide-class'] ||= 'hide'
+  - local_assigns[key][:data][:'notification-show-class'] ||= 'show'
+
+.d-flex.flex-column
+  = render 'bootstrap/alert', class: 'm-0 d-none fade', **local_assigns[:invalid] do
+    = invalid
+  = render 'bootstrap/alert', class: 'm-0 d-none fade', **local_assigns[:submitting] do
+    = submitting
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 3815f5de..c0ad36d2 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -719,8 +719,9 @@ en:
     submit:
       save: 'Save'
       save_draft: 'Save as draft'
-      invalid_help: "Some fields need attention! Please search for the fields marked as not valid."
-      submitting_help: "Saving changes, please wait..."
+    validation:
+      invalid: "Some fields need attention! Please search for the fields marked as not valid."
+      submitting: "Saving changes, please wait..."
     new_array:
       remove: "Remove"
     attributes:
diff --git a/config/locales/es.yml b/config/locales/es.yml
index 7b981b1d..1fd397e4 100644
--- a/config/locales/es.yml
+++ b/config/locales/es.yml
@@ -727,8 +727,9 @@ es:
     submit:
       save: 'Guardar'
       save_draft: 'Guardar como borrador'
-      invalid_help: "¡Te falta completar algunos campos! Busca los que estén marcados como no válidos."
-      submitting_help: "Guardando, por favor espera..."
+    validation:
+      invalid: "¡Te falta completar algunos campos! Busca los que estén marcados como no válidos."
+      submitting: "Guardando, por favor espera..."
     new_array:
       remove: "Eliminar"
     attributes:
@@ -770,6 +771,8 @@ es:
         edit: "Editar"
       new_predefined_array:
         edit: "Editar"
+      new_predefined_value:
+        edit: "Editar"
       new_array:
         edit: "Editar"
         required: "Seleccioná al menos una opción."

From 798bb992dc41423b0241510d4614fb39ae7a609f Mon Sep 17 00:00:00 2001
From: f <f@sutty.nl>
Date: Wed, 19 Jun 2024 18:20:05 -0300
Subject: [PATCH 4/5] feat: validar formularios htmx

---
 app/views/posts/_htmx_form.haml | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/app/views/posts/_htmx_form.haml b/app/views/posts/_htmx_form.haml
index f4f4a845..7a23445b 100644
--- a/app/views/posts/_htmx_form.haml
+++ b/app/views/posts/_htmx_form.haml
@@ -27,7 +27,12 @@
     multipart: true,
     class: 'form post ',
     'hx-swap': params.require(:swap),
-    'hx-target': "##{params.require(:target)}"
+    'hx-target': "##{params.require(:target)}",
+    'hx-validate': true,
+    data: {
+      controller: 'form-validation',
+      action: 'form-validation#submit'
+    }
   }
 
   if post.new?

From 34aa8822f29254e21bf316f092fe578a665367b0 Mon Sep 17 00:00:00 2001
From: f <f@sutty.nl>
Date: Wed, 19 Jun 2024 18:21:44 -0300
Subject: [PATCH 5/5] feat: avisar que se guardaron los cambios

---
 app/controllers/posts_controller.rb          | 8 ++++++--
 app/views/posts/_htmx_form.haml              | 6 +++++-
 app/views/posts/attributes/_new_has_one.haml | 8 +++++++-
 config/locales/en.yml                        | 2 ++
 config/locales/es.yml                        | 2 ++
 5 files changed, 22 insertions(+), 4 deletions(-)

diff --git a/app/controllers/posts_controller.rb b/app/controllers/posts_controller.rb
index 0f3c24d5..70ba2e54 100644
--- a/app/controllers/posts_controller.rb
+++ b/app/controllers/posts_controller.rb
@@ -128,7 +128,9 @@ class PostsController < ApplicationController
     # condiciones.
     if htmx?
       if post.persisted?
-        swap_modals
+        triggers = { 'notification:show' => { 'id' => params.permit(:saved).values.first } }
+
+        swap_modals(triggers)
 
         @value = post.title.value
         @uuid = post.uuid.value
@@ -169,7 +171,9 @@ class PostsController < ApplicationController
 
     if htmx?
       if post.persisted?
-        swap_modals
+        triggers = { 'notification:show' => params.permit(:saved).values.first }
+
+        swap_modals(triggers)
 
         @value = post.title.value
         @uuid = post.uuid.value
diff --git a/app/views/posts/_htmx_form.haml b/app/views/posts/_htmx_form.haml
index 7a23445b..1a8c0597 100644
--- a/app/views/posts/_htmx_form.haml
+++ b/app/views/posts/_htmx_form.haml
@@ -31,7 +31,9 @@
     'hx-validate': true,
     data: {
       controller: 'form-validation',
-      action: 'form-validation#submit'
+      action: 'form-validation#submit',
+      'form-validation-submitting-id-value': params.permit(:submitting).values.first,
+      'form-validation-invalid-id-value': params.permit(:invalid).values.first,
     }
   }
 
@@ -78,6 +80,8 @@
   %input{ type: 'hidden', name: 'swap', value: params.require(:swap) }
   - if params[:inverse].present?
     %input{ type: 'hidden', name: 'inverse', value: params.require(:inverse) }
+  - if params[:saved].present?
+    %input{ type: 'hidden', name: 'saved', value: params.require(:saved) }
 
   = hidden_field_tag "#{base}[layout]", post.layout.name
 
diff --git a/app/views/posts/attributes/_new_has_one.haml b/app/views/posts/attributes/_new_has_one.haml
index 58e098b6..87ef0440 100644
--- a/app/views/posts/attributes/_new_has_one.haml
+++ b/app/views/posts/attributes/_new_has_one.haml
@@ -19,6 +19,9 @@
   post_form_loaded_id = random_id
   value_list_id = random_id
   layout = metadata.filter[:layout]
+  invalid_id = random_id
+  submitting_id = random_id
+  saved_id = random_id
 
 %div{ data: { controller: 'modal'  }}
   .form-group
@@ -43,10 +46,13 @@
       = render 'bootstrap/modal', id: id, modal_content_attributes: { class: 'h-100' } do
         - content_for :"#{id}_body" do
           -# @todo ocultar el modal después de guardar
-          .placeholder-glow{ 'hx-get': site_posts_form_path(site, layout: layout, base: id, name: name, form: form_id, swap: 'innerHTML', target: target_id, attribute: 'new_has_one', hide: modal_id, uuid: metadata.value), 'hx-trigger': 'load' }
+          .placeholder-glow{ 'hx-get': site_posts_form_path(site, layout: layout, base: id, name: name, form: form_id, swap: 'innerHTML', target: target_id, attribute: 'new_has_one', hide: modal_id, uuid: metadata.value, invalid: invalid_id, submitting: submitting_id, saved: saved_id), 'hx-trigger': 'load' }
             %span.placeholder.w-100.h-100
 
         - content_for :"#{id}_footer" do
+          = render 'posts/validation', site: site, invalid: { id: invalid_id }, submitting: { id: submitting_id }
+          = render 'bootstrap/alert', class: 'm-0 d-none fade', id: saved_id, data: { controller: 'notification', action: 'notification:show@window->notification#show', 'notification-hide-class': 'hide', 'notification-show-class': 'show' } do
+            = t('.saved')
           = render 'bootstrap/btn', form: form_id, content: t('.save'), type: 'submit', class: 'm-0 mt-1 mr-1'
           = render 'bootstrap/btn', content: t('.close'), action: 'modal#hide', class: 'm-0 mt-1 mr-1'
 
diff --git a/config/locales/en.yml b/config/locales/en.yml
index c0ad36d2..52f002e6 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -965,3 +965,5 @@ en:
       save: "Save"
     card:
       edit: "Edit"
+    alert:
+      saved: "Changes were saved!"
diff --git a/config/locales/es.yml b/config/locales/es.yml
index 1fd397e4..8fe09758 100644
--- a/config/locales/es.yml
+++ b/config/locales/es.yml
@@ -975,3 +975,5 @@ es:
       save: "Guardar"
     card:
       edit: "Editar"
+    alert:
+      saved: "¡Cambios guardados!"