From c226adc36c6c891a6e02a1dd6dae470f719575e6 Mon Sep 17 00:00:00 2001 From: Kevin BATAILLE Date: Tue, 26 Aug 2025 18:29:56 +0200 Subject: [PATCH] feat: implement flash messages system with auto-dismiss notifications - Add flash message helper and styles for consistent notifications - Replace Devise error messages with flash-based notifications - Add dashboard page with event statistics - Configure SMTP settings for development and production - Update authentication controllers to use flash messages - Add JavaScript controller for auto-dismiss functionality --- .env.example | 9 + Procfile.dev | 4 +- .../stylesheets/application.postcss.css | 3 + app/assets/stylesheets/components/flash.css | 39 +++ .../authentications/passwords_controller.rb | 8 +- .../authentications/sessions_controller.rb | 7 +- app/controllers/pages_controller.rb | 11 +- app/helpers/application_helper.rb | 4 + app/helpers/flash_messages_helper.rb | 34 +++ .../controllers/flash_message_controller.js | 27 +++ app/javascript/controllers/index.js | 11 +- app/views/devise/confirmations/new.html.erb | 1 - app/views/devise/passwords/edit.html.erb | 9 +- app/views/devise/passwords/new.html.erb | 9 +- app/views/devise/registrations/new.html.erb | 9 +- app/views/devise/sessions/new.html.erb | 11 +- .../devise/shared/_error_messages.html.erb | 15 +- app/views/devise/unlocks/new.html.erb | 1 - app/views/layouts/application.html.erb | 19 +- app/views/pages/dashboard.html.erb | 157 ++++++++++++ app/views/shared/_flash_messages.html.erb | 25 ++ auth-messages-implementation-plan.md | 225 ++++++++++++++++++ config/environments/development.rb | 7 + config/environments/production.rb | 19 +- config/routes.rb | 9 +- db/seeds.rb | 2 +- 26 files changed, 607 insertions(+), 68 deletions(-) create mode 100644 app/assets/stylesheets/components/flash.css create mode 100644 app/helpers/flash_messages_helper.rb create mode 100644 app/javascript/controllers/flash_message_controller.js create mode 100644 app/views/pages/dashboard.html.erb create mode 100644 app/views/shared/_flash_messages.html.erb create mode 100644 auth-messages-implementation-plan.md diff --git a/.env.example b/.env.example index 2ebb16a..4383235 100644 --- a/.env.example +++ b/.env.example @@ -29,6 +29,15 @@ SMTP_PORT=1025 SMTP_AUTHENTICATION=plain SMTP_ENABLE_STARTTLS=false +# Production SMTP Configuration (set these in .env.production) +# SMTP_ADDRESS=smtp.example.com +# SMTP_PORT=587 +# SMTP_USERNAME=your_smtp_username +# SMTP_PASSWORD=your_smtp_password +# SMTP_AUTHENTICATION=plain +# SMTP_DOMAIN=example.com +# SMTP_STARTTLS=true + # Application variables STRIPE_API_KEY=1337 diff --git a/Procfile.dev b/Procfile.dev index c1cb248..96ee687 100644 --- a/Procfile.dev +++ b/Procfile.dev @@ -1,3 +1,3 @@ -web: env RUBY_DEBUG_OPEN=true bin/rails server -js: yarn build --watch +web: env RUBY_DEBUG_OPEN=true bin/rails server +js: yarn build:dev --watch css: yarn build:css --watch diff --git a/app/assets/stylesheets/application.postcss.css b/app/assets/stylesheets/application.postcss.css index cc9f6b6..39944f4 100644 --- a/app/assets/stylesheets/application.postcss.css +++ b/app/assets/stylesheets/application.postcss.css @@ -3,6 +3,9 @@ /* Import Tailwind using PostCSS */ @import "tailwindcss"; +/* Import flash message styles */ +@import "components/flash"; + /** Default text color */ body { color: #555555; diff --git a/app/assets/stylesheets/components/flash.css b/app/assets/stylesheets/components/flash.css new file mode 100644 index 0000000..d985310 --- /dev/null +++ b/app/assets/stylesheets/components/flash.css @@ -0,0 +1,39 @@ +/* Flash Messages - Theme Integration */ +.flash-message { + @apply max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 mb-4; +} + +/* Base styles for all flash messages */ +.flash-message .flex { + @apply rounded-md p-4 border shadow-md; +} + +/* Success message styles */ +.flash-message-success { + @apply bg-green-50 border-green-100 text-green-800; +} + +/* Error message styles */ +.flash-message-error { + @apply bg-red-50 border-red-100 text-red-800; +} + +/* Warning message styles */ +.flash-message-warning { + @apply bg-yellow-50 border-yellow-100 text-yellow-800; +} + +/* Info message styles */ +.flash-message-info { + @apply bg-blue-50 border-blue-100 text-blue-800; +} + +/* Notice message styles */ +.flash-message-notice { + @apply bg-purple-50 border-purple-100 text-purple-800; +} + +/* Alert message styles */ +.flash-message-alert { + @apply bg-red-50 border-red-100 text-red-800; +} diff --git a/app/controllers/authentications/passwords_controller.rb b/app/controllers/authentications/passwords_controller.rb index edce788..f9f2cf3 100644 --- a/app/controllers/authentications/passwords_controller.rb +++ b/app/controllers/authentications/passwords_controller.rb @@ -23,9 +23,11 @@ class Authentications::PasswordsController < Devise::PasswordsController # protected - # def after_resetting_password_path_for(resource) - # super(resource) - # end + # Override to set a flash message on successful password reset + def after_resetting_password_path_for(resource) + flash[:notice] = "Votre mot de passe a été changé avec succès. Vous êtes maintenant connecté." + super(resource) + end # The path used after sending reset password instructions # def after_sending_reset_password_instructions_path_for(resource_name) diff --git a/app/controllers/authentications/sessions_controller.rb b/app/controllers/authentications/sessions_controller.rb index 6fdf49b..37e6a5f 100644 --- a/app/controllers/authentications/sessions_controller.rb +++ b/app/controllers/authentications/sessions_controller.rb @@ -9,9 +9,10 @@ class Authentications::SessionsController < Devise::SessionsController # end # POST /resource/sign_in - # def create - # super - # end + def create + super + flash[:notice] = "Connexion réussie !" if resource.persisted? + end # DELETE /resource/sign_out # def destroy diff --git a/app/controllers/pages_controller.rb b/app/controllers/pages_controller.rb index 7dce8ce..4a4948c 100644 --- a/app/controllers/pages_controller.rb +++ b/app/controllers/pages_controller.rb @@ -3,17 +3,26 @@ class PagesController < ApplicationController # Skip authentication for public pages # skip_before_action :authenticate_user!, only: [ :home ] + before_action :authenticate_user!, only: [ :dashboard ] # Homepage showing featured parties def home # @parties = Party.published.featured.limit(3) @parties = Party.where(state: :published).order(created_at: :desc) - puts @parties + + if user_signed_in? + return redirect_to(dashboard_path) + end end # User dashboard showing personalized content # Accessible only to authenticated users def dashboard + @available_parties = Party.published.count + @events_this_week = Party.published.where("start_time BETWEEN ? AND ?", Date.current.beginning_of_week, Date.current.end_of_week).count + @today_parties = Party.published.where("DATE(start_time) = ?", Date.current).order(start_time: :asc) + @tomorrow_parties = Party.published.where("DATE(start_time) = ?", Date.current + 1).order(start_time: :asc) + @other_parties = Party.published.upcoming.where.not("DATE(start_time) IN (?)", [Date.current, Date.current + 1]).order(start_time: :asc).page(params[:page]) end # Events page showing all published parties with pagination diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index d551c20..debee41 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -1,5 +1,9 @@ module ApplicationHelper + # Convert prince from cents to float def format_price(cents) (cents.to_f / 100).round(2) end + + # Include flash message helpers + include FlashMessagesHelper end diff --git a/app/helpers/flash_messages_helper.rb b/app/helpers/flash_messages_helper.rb new file mode 100644 index 0000000..4d86a1a --- /dev/null +++ b/app/helpers/flash_messages_helper.rb @@ -0,0 +1,34 @@ +module FlashMessagesHelper + def flash_class(type) + case type.to_s + when 'notice' then 'flash-message-success' + when 'success' then 'flash-message-success' + when 'error' then 'flash-message-error' + when 'alert' then 'flash-message-error' + when 'warning' then 'flash-message-warning' + when 'info' then 'flash-message-info' + else "flash-message-#{type}" + end + end + + def flash_icon(type) + case type.to_s + when 'notice', 'success' + content_tag :svg, class: "h-5 w-5 text-green-400", fill: "currentColor", viewBox: "0 0 20 20" do + content_tag :path, "", "fill-rule": "evenodd", "d": "M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z", "clip-rule": "evenodd" + end + when 'error', 'alert' + content_tag :svg, class: "h-5 w-5 text-red-400", fill: "currentColor", viewBox: "0 0 20 20" do + content_tag :path, "", "fill-rule": "evenodd", "d": "M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z", "clip-rule": "evenodd" + end + when 'warning' + content_tag :svg, class: "h-5 w-5 text-yellow-400", fill: "currentColor", viewBox: "0 0 20 20" do + content_tag :path, "", "fill-rule": "evenodd", "d": "M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z", "clip-rule": "evenodd" + end + else + content_tag :svg, class: "h-5 w-5 text-blue-400", fill: "currentColor", viewBox: "0 0 20 20" do + content_tag :path, "", "fill-rule": "evenodd", "d": "M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z", "clip-rule": "evenodd" + end + end + end +end diff --git a/app/javascript/controllers/flash_message_controller.js b/app/javascript/controllers/flash_message_controller.js new file mode 100644 index 0000000..89e0b2d --- /dev/null +++ b/app/javascript/controllers/flash_message_controller.js @@ -0,0 +1,27 @@ +import { Controller } from "@hotwired/stimulus" + +export default class extends Controller { + static targets = ["message"] + + connect() { + console.log("FlashMessageController mounted", this.element); + + // Auto-dismiss after 5 seconds + this.timeout = setTimeout(() => { + this.close() + }, 5000) + } + + disconnect() { + if (this.timeout) { + clearTimeout(this.timeout) + } + } + + close() { + this.element.classList.add('opacity-0', 'transition-opacity', 'duration-300') + setTimeout(() => { + this.element.remove() + }, 300) + } +} diff --git a/app/javascript/controllers/index.js b/app/javascript/controllers/index.js index 2dd37f0..8847531 100644 --- a/app/javascript/controllers/index.js +++ b/app/javascript/controllers/index.js @@ -5,9 +5,12 @@ import { application } from "./application" import LogoutController from "./logout_controller" -import ShadcnTestController from "./shadcn_test_controller" +import FlashMessage from "./flash_message_controller" import CounterController from "./counter_controller" +import ShadcnTestController from "./shadcn_test_controller" -application.register("logout", LogoutController) -application.register("shadcn-test", ShadcnTestController) -application.register("counter", CounterController) +application.register("logout", LogoutController) // Allow logout using js +application.register("flash-message", FlashMessage) // Dismiss notification after 5 secondes +application.register("counter", CounterController) // Simple counter for homepage + +application.register("shadcn-test", ShadcnTestController) // Test controller for Shadcn diff --git a/app/views/devise/confirmations/new.html.erb b/app/views/devise/confirmations/new.html.erb index b12dd0c..962cb5c 100644 --- a/app/views/devise/confirmations/new.html.erb +++ b/app/views/devise/confirmations/new.html.erb @@ -1,7 +1,6 @@

Resend confirmation instructions

<%= form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post }) do |f| %> - <%= render "devise/shared/error_messages", resource: resource %>
<%= f.label :email %>
diff --git a/app/views/devise/passwords/edit.html.erb b/app/views/devise/passwords/edit.html.erb index 642f4ec..88d71c3 100644 --- a/app/views/devise/passwords/edit.html.erb +++ b/app/views/devise/passwords/edit.html.erb @@ -1,4 +1,4 @@ -
+
<%= link_to "/" do %> @@ -13,7 +13,6 @@
<%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :put, class: "mt-8 space-y-6" }) do |f| %> - <%= render "devise/shared/error_messages", resource: resource %> <%= f.hidden_field :reset_password_token %>
@@ -22,19 +21,19 @@ <% if @minimum_password_length %> (<%= @minimum_password_length %> caractères minimum) <% end %> - <%= f.password_field :password, autofocus: true, autocomplete: "new-password", + <%= f.password_field :password, autofocus: true, autocomplete: "new-password", class: "mt-1 block w-full px-3 py-2 border border-neutral-300 rounded-md shadow-sm placeholder-neutral-400 focus:outline-none focus:ring-purple-500 focus:border-purple-500 sm:text-sm" %>
<%= f.label :password_confirmation, "Confirmer le nouveau mot de passe", class: "block text-sm font-medium text-neutral-700" %> - <%= f.password_field :password_confirmation, autocomplete: "new-password", + <%= f.password_field :password_confirmation, autocomplete: "new-password", class: "mt-1 block w-full px-3 py-2 border border-neutral-300 rounded-md shadow-sm placeholder-neutral-400 focus:outline-none focus:ring-purple-500 focus:border-purple-500 sm:text-sm" %>
- <%= f.submit "Changer mon mot de passe", + <%= f.submit "Changer mon mot de passe", class: "group relative w-full flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-purple-600 hover:bg-purple-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-neutral-50 focus:ring-purple-500" %>
<% end %> diff --git a/app/views/devise/passwords/new.html.erb b/app/views/devise/passwords/new.html.erb index c779699..2a60d75 100644 --- a/app/views/devise/passwords/new.html.erb +++ b/app/views/devise/passwords/new.html.erb @@ -1,4 +1,4 @@ -
+
<%= link_to "/" do %> @@ -13,19 +13,18 @@
<%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :post, class: "mt-8 space-y-6" }) do |f| %> - <%= render "devise/shared/error_messages", resource: resource %>
<%= f.label :email, class: "block text-sm font-medium text-neutral-700" %>
- <%= f.email_field :email, autofocus: true, autocomplete: "email", - class: "appearance-none block w-full px-3 py-2 border border-neutral-300 rounded-md shadow-sm placeholder-neutral-400 focus:outline-none focus:ring-purple-500 focus:border-purple-500 sm:text-sm", + <%= f.email_field :email, autofocus: true, autocomplete: "email", + class: "appearance-none block w-full px-3 py-2 border border-neutral-300 rounded-md shadow-sm placeholder-neutral-400 focus:outline-none focus:ring-purple-500 focus:border-purple-500 sm:text-sm", placeholder: "Adresse email" %>
- <%= f.submit "Envoyer le lien de réinitialisation", + <%= f.submit "Envoyer le lien de réinitialisation", class: "group relative w-full flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-purple-600 hover:bg-purple-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-neutral-50 focus:ring-purple-500" %>
<% end %> diff --git a/app/views/devise/registrations/new.html.erb b/app/views/devise/registrations/new.html.erb index 8e917b2..d6a1b5a 100644 --- a/app/views/devise/registrations/new.html.erb +++ b/app/views/devise/registrations/new.html.erb @@ -1,4 +1,4 @@ -
+
<%= link_to "/" do %> @@ -16,12 +16,11 @@
<%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { class: "mt-8 space-y-6" }) do |f| %> - <%= render "devise/shared/error_messages", resource: resource %>
<%= f.label :email, class: "block text-sm font-medium text-neutral-700" %> - <%= f.email_field :email, autofocus: true, autocomplete: "email", + <%= f.email_field :email, autofocus: true, autocomplete: "email", class: "mt-1 block w-full px-3 py-2 border border-neutral-300 rounded-md shadow-sm placeholder-neutral-500 focus:outline-none focus:ring-purple-500 focus:border-purple-500 sm:text-sm" %>
@@ -30,13 +29,13 @@ <% if @minimum_password_length %> (<%= @minimum_password_length %> caractères minimum) <% end %> - <%= f.password_field :password, autocomplete: "new-password", + <%= f.password_field :password, autocomplete: "new-password", class: "mt-1 block w-full px-3 py-2 border border-neutral-300 rounded-md shadow-sm placeholder-neutral-500 focus:outline-none focus:ring-purple-500 focus:border-purple-500 sm:text-sm" %>
<%= f.label :password_confirmation, class: "block text-sm font-medium text-neutral-700" %> - <%= f.password_field :password_confirmation, autocomplete: "new-password", + <%= f.password_field :password_confirmation, autocomplete: "new-password", class: "mt-1 block w-full px-3 py-2 border border-neutral-300 rounded-md shadow-sm placeholder-neutral-500 focus:outline-none focus:ring-purple-500 focus:border-purple-500 sm:text-sm" %>
diff --git a/app/views/devise/sessions/new.html.erb b/app/views/devise/sessions/new.html.erb index d275a90..6753f2f 100644 --- a/app/views/devise/sessions/new.html.erb +++ b/app/views/devise/sessions/new.html.erb @@ -1,4 +1,4 @@ -
+
<%= link_to "/" do %> @@ -16,20 +16,19 @@
<%= form_for(resource, as: resource_name, url: session_path(resource_name), html: { class: "mt-8 space-y-6" }) do |f| %> - <%= devise_error_messages! %>
<%= f.label :email, class: "sr-only" %> - <%= f.email_field :email, autofocus: true, autocomplete: "email", - class: "appearance-none rounded-none relative block w-full px-3 py-2 border border-neutral-300 placeholder-neutral-500 text-neutral-900 bg-white rounded-t-md focus:outline-none focus:ring-purple-500 focus:border-purple-500 focus:z-10 sm:text-sm", + <%= f.email_field :email, autofocus: true, autocomplete: "email", + class: "appearance-none rounded-none relative block w-full px-3 py-2 border border-neutral-300 placeholder-neutral-500 text-neutral-900 bg-white rounded-t-md focus:outline-none focus:ring-purple-500 focus:border-purple-500 focus:z-10 sm:text-sm", placeholder: "Adresse email" %>
<%= f.label :password, class: "sr-only" %> - <%= f.password_field :password, autocomplete: "current-password", - class: "appearance-none rounded-none relative block w-full px-3 py-2 border border-neutral-300 placeholder-neutral-500 text-neutral-900 bg-white rounded-b-md focus:outline-none focus:ring-purple-500 focus:border-purple-500 focus:z-10 sm:text-sm", + <%= f.password_field :password, autocomplete: "current-password", + class: "appearance-none rounded-none relative block w-full px-3 py-2 border border-neutral-300 placeholder-neutral-500 text-neutral-900 bg-white rounded-b-md focus:outline-none focus:ring-purple-500 focus:border-purple-500 focus:z-10 sm:text-sm", placeholder: "Mot de passe" %>
diff --git a/app/views/devise/shared/_error_messages.html.erb b/app/views/devise/shared/_error_messages.html.erb index 5ba8ac5..833fbad 100644 --- a/app/views/devise/shared/_error_messages.html.erb +++ b/app/views/devise/shared/_error_messages.html.erb @@ -1,14 +1,5 @@ <% if resource.errors.any? %> -
-

- <%= I18n.t("errors.messages.not_saved", - count: resource.errors.count, - resource: resource.class.model_name.human.downcase) %> -

-
    - <% resource.errors.full_messages.each do |message| %> -
  • <%= message %>
  • - <% end %> -
-
+ <% resource.errors.full_messages.each do |message| %> + <% flash.now[:error] = message %> + <% end %> <% end %> diff --git a/app/views/devise/unlocks/new.html.erb b/app/views/devise/unlocks/new.html.erb index ffc34de..39d51da 100644 --- a/app/views/devise/unlocks/new.html.erb +++ b/app/views/devise/unlocks/new.html.erb @@ -1,7 +1,6 @@

Resend unlock instructions

<%= form_for(resource, as: resource_name, url: unlock_path(resource_name), html: { method: :post }) do |f| %> - <%= render "devise/shared/error_messages", resource: resource %>
<%= f.label :email %>
diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index c78f65a..91f7bcf 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -7,28 +7,30 @@ <%= csrf_meta_tags %> <%= csp_meta_tag %> - <%= yield :head %> - <%# Enable PWA manifest for installable apps (make sure to enable in config/routes.rb too!) %> <%#= tag.link rel: "manifest", href: pwa_manifest_path(format: :json) %> - - <%# Includes all stylesheet files in app/assets/stylesheets %> <%= stylesheet_link_tag :app, "data-turbo-track": "reload" %> <%= stylesheet_link_tag "theme", "data-turbo-track": "reload" %> <%= javascript_include_tag "application", "data-turbo-track": "reload", type: "module" %> - -
- <%= render "components/header" %> + +
+ <%= render "components/header" %>
- <%= yield %> +
+ <%= render "shared/flash_messages" %> +
+ +
+ <%= yield %> +
@@ -36,6 +38,7 @@ <%= render "components/footer" %>
+
diff --git a/app/views/pages/dashboard.html.erb b/app/views/pages/dashboard.html.erb new file mode 100644 index 0000000..2e8740b --- /dev/null +++ b/app/views/pages/dashboard.html.erb @@ -0,0 +1,157 @@ +
+ +
+

Tableau de bord

+
+ +
+
+
+
+ <%= @available_parties %> +
+

+ Événements disponibles +

+
+
+
+ +
+
+
+
+ <%= @events_this_week %> +
+

+ Événements aujourd'hui +

+
+
+
+ + + +
+
+
+
+ <%= @events_this_week %> +
+

+ Événements cette semaine +

+
+
+
+ +
+
+ + +
+
+

Évenements du jour

+
+
+ <% if @today_parties.any? %> +
    + <% @today_parties.each do |party| %> +
  • + <%= link_to party_path(party.slug, party), class: "group block p-4 rounded-lg border border-slate-200 dark:border-slate-700 hover:border-purple-300 dark:hover:border-purple-600 hover:shadow-md transition-all duration-200" do %> +
    +
    + <%= image_tag party.image, alt: party.name, class: "w-full h-full object-cover" if party.image.present? %> +
    +
    +

    + <%= party.name %> +

    +

    + <%= l(party.start_time, format: :short) %> +

    +
    +
    + <% end %> +
  • + <% end %> +
+ <% else %> +

Aucune partie aujourd'hui.

+ <% end %> +
+
+ + +
+
+

Évenements de demain

+
+
+ <% if @tomorrow_parties.any? %> +
    + <% @tomorrow_parties.each do |party| %> +
  • + <%= link_to party_path(party.slug, party), class: "group block p-4 rounded-lg border border-slate-200 dark:border-slate-700 hover:border-purple-300 dark:hover:border-purple-600 hover:shadow-md transition-all duration-200" do %> +
    +
    + <%= image_tag party.image, alt: party.name, class: "w-full h-full object-cover" if party.image.present? %> +
    +
    +

    + <%= party.name %> +

    +

    + <%= l(party.start_time, format: :short) %> +

    +
    +
    + <% end %> +
  • + <% end %> +
+ <% else %> +

Aucune partie demain.

+ <% end %> +
+
+ + +
+
+

Autres évenements à venir

+
+
+ <% if @other_parties.any? %> +
    + <% @other_parties.each do |party| %> +
  • + <%= link_to party_path(party.slug, party), class: "group block p-4 rounded-lg border border-slate-200 dark:border-slate-700 hover:border-purple-300 dark:hover:border-purple-600 hover:shadow-md transition-all duration-200" do %> +
    +
    + <%= image_tag party.image, alt: party.name, class: "w-full h-full object-cover" if party.image.present? %> +
    +
    +

    + <%= party.name %> +

    +

    + <%= l(party.start_time, format: :short) %> +

    +
    +
    + <% end %> +
  • + <% end %> +
+ + +
+ <%= paginate @other_parties %> +
+ <% else %> +

Aucune autre partie à venir.

+ <% end %> +
+
+
diff --git a/app/views/shared/_flash_messages.html.erb b/app/views/shared/_flash_messages.html.erb new file mode 100644 index 0000000..2b7ff7d --- /dev/null +++ b/app/views/shared/_flash_messages.html.erb @@ -0,0 +1,25 @@ +<% flash.each do |type, message| %> + <% if message.present? %> +
+
+
+ <%= flash_icon(type) %> +
+
+
+
+

<%= message %>

+
+
+
+
+ +
+
+
+ <% end %> +<% end %> diff --git a/auth-messages-implementation-plan.md b/auth-messages-implementation-plan.md new file mode 100644 index 0000000..9f85b29 --- /dev/null +++ b/auth-messages-implementation-plan.md @@ -0,0 +1,225 @@ +# Authentication Messages Implementation Plan + +## Overview +This document outlines the implementation of error/warn/info messages for login, registration, logout, password reset, and other authentication flows based on the existing purple/pink theme. + +## Current State Analysis +- **Theme**: Purple/pink gradient system with neutral colors +- **Authentication**: Devise with custom controllers +- **Missing**: Flash message display system +- **Existing**: Only form validation errors are displayed + +## Implementation Steps + +### 1. Flash Message Component +Create a reusable flash message component that integrates with the theme. + +### 2. CSS Classes for Message Types +Add theme-consistent styles for different message types: +- Success (green/purple) +- Error (red) +- Warning (yellow/orange) +- Info (blue) + +### 3. JavaScript Enhancement +Add auto-dismiss functionality and animations + +### 4. Integration +Update layouts and views to use the new message system + +## Files to Create/Update + +### A. Flash Message Partial +**File**: `app/views/shared/_flash_messages.html.erb` +```erb +<% flash.each do |type, message| %> + <% if message.present? %> +
+
+
+ <%= flash_icon(type) %> +
+
+

<%= message %>

+
+
+ +
+
+
+ <% end %> +<% end %> +``` + +### B. Flash Message Styling +**File**: `app/assets/stylesheets/components/flash.css` +```css +/* Flash Messages - Theme Integration */ +.flash-message { + @apply max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 mb-4; +} + +.flash-message .flash-container { + @apply rounded-lg p-4 shadow-md border; +} + +.flash-message-success .flash-container { + @apply bg-gradient-to-r from-green-50 to-purple-50 border-green-200 text-green-800; +} + +.flash-message-error .flash-container { + @apply bg-gradient-to-r from-red-50 to-pink-50 border-red-200 text-red-800; +} + +.flash-message-warning .flash-container { + @apply bg-gradient-to-r from-yellow-50 to-orange-50 border-yellow-200 text-yellow-800; +} + +.flash-message-info .flash-container { + @apply bg-gradient-to-r from-blue-50 to-purple-50 border-blue-200 text-blue-800; +} + +.flash-message-notice .flash-container { + @apply bg-gradient-to-r from-purple-50 to-pink-50 border-purple-200 text-purple-800; +} + +.flash-message-alert .flash-container { + @apply bg-gradient-to-r from-red-50 to-pink-50 border-red-200 text-red-800; +} +``` + +### C. Helper Methods +**File**: `app/helpers/flash_messages_helper.rb` +```ruby +module FlashMessagesHelper + def flash_class(type) + case type.to_s + when 'notice' then 'flash-message-success' + when 'success' then 'flash-message-success' + when 'error' then 'flash-message-error' + when 'alert' then 'flash-message-error' + when 'warning' then 'flash-message-warning' + when 'info' then 'flash-message-info' + else "flash-message-#{type}" + end + end + + def flash_icon(type) + case type.to_s + when 'notice', 'success' + content_tag :svg, class: "h-5 w-5 text-green-400", fill: "currentColor", viewBox: "0 0 20 20" do + content_tag :path, "", "fill-rule": "evenodd", "d": "M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z", "clip-rule": "evenodd" + end + when 'error', 'alert' + content_tag :svg, class: "h-5 w-5 text-red-400", fill: "currentColor", viewBox: "0 0 20 20" do + content_tag :path, "", "fill-rule": "evenodd", "d": "M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z", "clip-rule": "evenodd" + end + when 'warning' + content_tag :svg, class: "h-5 w-5 text-yellow-400", fill: "currentColor", viewBox: "0 0 20 20" do + content_tag :path, "", "fill-rule": "evenodd", "d": "M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z", "clip-rule": "evenodd" + end + else + content_tag :svg, class: "h-5 w-5 text-blue-400", fill: "currentColor", viewBox: "0 0 20 20" do + content_tag :path, "", "fill-rule": "evenodd", "d": "M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z", "clip-rule": "evenodd" + end + end + end +end +``` + +### D. JavaScript Controller +**File**: `app/javascript/controllers/flash_message_controller.js` +```javascript +import { Controller } from "@hotwired/stimulus" + +export default class extends Controller { + static targets = ["message"] + + connect() { + // Auto-dismiss after 5 seconds + this.timeout = setTimeout(() => { + this.close() + }, 5000) + } + + disconnect() { + if (this.timeout) { + clearTimeout(this.timeout) + } + } + + close() { + this.element.classList.add('opacity-0', 'transition-opacity', 'duration-300') + setTimeout(() => { + this.element.remove() + }, 300) + } +} +``` + +### E. Update Application Layout +**File**: `app/views/layouts/application.html.erb` (add flash messages) +```erb + +
+ <%= render "components/header" %> + +
+ <%= render "shared/flash_messages" %> + <%= yield %> +
+ + <%= render "components/footer" %> +
+ +``` + +### F. Update Authentication Views +Update all Devise views to remove the old error display and rely on flash messages. + +## Testing Checklist + +### Authentication Flows to Test: +1. **Registration** + - Successful registration + - Registration with validation errors + - Email confirmation + +2. **Login** + - Successful login + - Invalid credentials + - Account locked/unconfirmed + +3. **Password Reset** + - Request reset email + - Reset password success/failure + +4. **Account Management** + - Update profile + - Change password + - Delete account + +### Message Types to Verify: +- [ ] Success messages (green/purple) +- [ ] Error messages (red/pink) +- [ ] Warning messages (yellow/orange) +- [ ] Info messages (blue/purple) + +## Implementation Order +1. Create CSS classes and theme integration +2. Create helper methods +3. Create partial templates +4. Add to application layout +5. Test each authentication flow +6. Add JavaScript enhancements + +## Notes +- All messages use the existing purple/pink theme colors +- Responsive design for mobile/desktop +- Auto-dismiss functionality with manual close option +- Smooth animations and transitions +- Accessibility compliant with focus indicators diff --git a/config/environments/development.rb b/config/environments/development.rb index 4cc21c4..1baad41 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -34,6 +34,13 @@ Rails.application.configure do # Don't care if the mailer can't send. config.action_mailer.raise_delivery_errors = false + # Configure mailer to use localhost:1025 for development + config.action_mailer.delivery_method = :smtp + config.action_mailer.smtp_settings = { + address: "localhost", + port: 1025 + } + # Make template changes take effect immediately. config.action_mailer.perform_caching = false diff --git a/config/environments/production.rb b/config/environments/production.rb index bdcd01d..ddee862 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -60,14 +60,17 @@ Rails.application.configure do # Set host to be used by links generated in mailer templates. config.action_mailer.default_url_options = { host: "example.com" } - # Specify outgoing SMTP server. Remember to add smtp/* credentials via rails credentials:edit. - # config.action_mailer.smtp_settings = { - # user_name: Rails.application.credentials.dig(:smtp, :user_name), - # password: Rails.application.credentials.dig(:smtp, :password), - # address: "smtp.example.com", - # port: 587, - # authentication: :plain - # } + # Configure SMTP settings using environment variables + config.action_mailer.delivery_method = :smtp + config.action_mailer.smtp_settings = { + address: ENV.fetch("SMTP_ADDRESS", "smtp.example.com"), + port: ENV.fetch("SMTP_PORT", 587), + user_name: ENV.fetch("SMTP_USERNAME", ""), + password: ENV.fetch("SMTP_PASSWORD", ""), + authentication: ENV.fetch("SMTP_AUTHENTICATION", "plain"), + domain: ENV.fetch("SMTP_DOMAIN", "example.com"), + enable_starttls_auto: ENV.fetch("SMTP_STARTTLS", true) + } # Enable locale fallbacks for I18n (makes lookups for any locale fall back to # the I18n.default_locale when a translation cannot be found). diff --git a/config/routes.rb b/config/routes.rb index 5a6de5b..7645061 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -12,9 +12,12 @@ Rails.application.routes.draw do # Defines the root path route ("/") root "pages#home" - # parties page - get "parties", to: "parties#index", as: "parties" - get "parties/:slug.:id", to: "parties#show", as: "party" + # Pages + get "dashboard", to: "pages#dashboard", as: "dashboard" + + # Parties + get "parties", to: "parties#index", as: "parties" + get "parties/:slug.:id", to: "parties#show", as: "party" # Routes for devise authentication Gem # Bind devise to user diff --git a/db/seeds.rb b/db/seeds.rb index 942d725..f2320a9 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -44,7 +44,7 @@ parties_data = [ start_time: 1.day.from_now, end_time: 1.day.from_now + 6.hours, featured: true, - image: "https://images.unsplash.com/photo-1506157786151-b84b9d3d78d8?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80", + image: "https://fastly.picsum.photos/id/407/300/200.jpg?hmac=9EhoXMZ1QdwJue90vzxcjBg2YzsZsAWCjJ7oxOhtcU0", user: users.first }, {