develop #3
@@ -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
|
||||
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
web: env RUBY_DEBUG_OPEN=true bin/rails server
|
||||
js: yarn build --watch
|
||||
js: yarn build:dev --watch
|
||||
css: yarn build:css --watch
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
/* Import Tailwind using PostCSS */
|
||||
@import "tailwindcss";
|
||||
|
||||
/* Import flash message styles */
|
||||
@import "components/flash";
|
||||
|
||||
/** Default text color */
|
||||
body {
|
||||
color: #555555;
|
||||
|
||||
39
app/assets/stylesheets/components/flash.css
Normal file
39
app/assets/stylesheets/components/flash.css
Normal file
@@ -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;
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
34
app/helpers/flash_messages_helper.rb
Normal file
34
app/helpers/flash_messages_helper.rb
Normal file
@@ -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
|
||||
27
app/javascript/controllers/flash_message_controller.js
Normal file
27
app/javascript/controllers/flash_message_controller.js
Normal file
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<h2>Resend confirmation instructions</h2>
|
||||
|
||||
<%= form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post }) do |f| %>
|
||||
<%= render "devise/shared/error_messages", resource: resource %>
|
||||
|
||||
<div class="field">
|
||||
<%= f.label :email %><br />
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<div class="min-h-screen flex items-center justify-center bg-neutral-50 py-12 px-4 sm:px-6 lg:px-8">
|
||||
<div class="flex items-center justify-center bg-neutral-50 py-12 px-4 sm:px-6 lg:px-8">
|
||||
<div class="max-w-md w-full space-y-8">
|
||||
<div>
|
||||
<%= link_to "/" do %>
|
||||
@@ -13,7 +13,6 @@
|
||||
</div>
|
||||
|
||||
<%= 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 %>
|
||||
|
||||
<div class="space-y-4">
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<div class="min-h-screen flex items-center justify-center bg-neutral-50 py-12 px-4 sm:px-6 lg:px-8">
|
||||
<div class="flex items-center justify-center bg-neutral-50 py-12 px-4 sm:px-6 lg:px-8">
|
||||
<div class="max-w-md w-full space-y-8">
|
||||
<div>
|
||||
<%= link_to "/" do %>
|
||||
@@ -13,7 +13,6 @@
|
||||
</div>
|
||||
|
||||
<%= 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 %>
|
||||
|
||||
<div>
|
||||
<%= f.label :email, class: "block text-sm font-medium text-neutral-700" %>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<div class="min-h-screen flex items-center justify-center bg-neutral-50 py-12 px-4 sm:px-6 lg:px-8">
|
||||
<div class="flex items-center justify-center bg-neutral-50 py-12 px-4 sm:px-6 lg:px-8">
|
||||
<div class="max-w-md w-full space-y-8">
|
||||
<div>
|
||||
<%= link_to "/" do %>
|
||||
@@ -16,7 +16,6 @@
|
||||
</div>
|
||||
|
||||
<%= 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 %>
|
||||
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<div class="min-h-screen flex items-center justify-center bg-neutral-50 py-12 px-4 sm:px-6 lg:px-8">
|
||||
<div class="flex items-center justify-center bg-neutral-50 py-12 px-4 sm:px-6 lg:px-8">
|
||||
<div class="max-w-md w-full space-y-8">
|
||||
<div>
|
||||
<%= link_to "/" do %>
|
||||
@@ -16,7 +16,6 @@
|
||||
</div>
|
||||
|
||||
<%= form_for(resource, as: resource_name, url: session_path(resource_name), html: { class: "mt-8 space-y-6" }) do |f| %>
|
||||
<%= devise_error_messages! %>
|
||||
|
||||
<div class="rounded-md shadow-sm -space-y-px">
|
||||
<div class="field">
|
||||
|
||||
@@ -1,14 +1,5 @@
|
||||
<% if resource.errors.any? %>
|
||||
<div id="error_explanation" data-turbo-cache="false" class="bg-red-50 border border-red-200 rounded-md p-4 mb-4">
|
||||
<h2 class="text-lg font-medium text-red-800 mb-3">
|
||||
<%= I18n.t("errors.messages.not_saved",
|
||||
count: resource.errors.count,
|
||||
resource: resource.class.model_name.human.downcase) %>
|
||||
</h2>
|
||||
<ul class="list-disc list-inside space-y-1">
|
||||
<% resource.errors.full_messages.each do |message| %>
|
||||
<li class="text-sm text-red-700"><%= message %></li>
|
||||
<% flash.now[:error] = message %>
|
||||
<% end %>
|
||||
</ul>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<h2>Resend unlock instructions</h2>
|
||||
|
||||
<%= form_for(resource, as: resource_name, url: unlock_path(resource_name), html: { method: :post }) do |f| %>
|
||||
<%= render "devise/shared/error_messages", resource: resource %>
|
||||
|
||||
<div class="field">
|
||||
<%= f.label :email %><br />
|
||||
|
||||
@@ -7,28 +7,30 @@
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
<%= 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) %>
|
||||
|
||||
<link rel="icon" href="/icon.png" type="image/png">
|
||||
<link rel="icon" href="/icon.svg" type="image/svg+xml">
|
||||
<link rel="apple-touch-icon" href="/icon.png">
|
||||
|
||||
<%# 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" %>
|
||||
</head>
|
||||
|
||||
<body class="h-full font-sans text-neutral-900 antialiased">
|
||||
<div class="min-h-full">
|
||||
|
||||
<div class="">
|
||||
<%= render "components/header" %>
|
||||
|
||||
<main class="container mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||||
<div class="flash">
|
||||
<%= render "shared/flash_messages" %>
|
||||
</div>
|
||||
|
||||
<div class="yield">
|
||||
<%= yield %>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<footer class="bg-neutral-100 text-neutral-600">
|
||||
@@ -36,6 +38,7 @@
|
||||
<%= render "components/footer" %>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
157
app/views/pages/dashboard.html.erb
Normal file
157
app/views/pages/dashboard.html.erb
Normal file
@@ -0,0 +1,157 @@
|
||||
<div class="container mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||||
<!-- Hero section with metrics -->
|
||||
<div class="mb-8">
|
||||
<h1 class="text-3xl font-bold text-slate-900 dark:text-slate-100 mb-6">Tableau de bord</h1>
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
|
||||
<div class="group relative overflow-hidden rounded-2xl bg-white dark:bg-slate-800 border border-neutral-200 dark:border-slate-700 hover:border-purple-300 dark:hover:border-purple-600 transition-all duration-300 p-8">
|
||||
<div class="absolute inset-0 bg-gradient-to-br from-purple-100 to-indigo-100 dark:from-purple-900/20 dark:to-indigo-900/20 opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div>
|
||||
<div class="relative">
|
||||
<div class="text-5xl md:text-3xl font-light bg-gradient-to-r from-purple-600 via-indigo-600 to-pink-600 bg-clip-text text-transparent mb-3">
|
||||
<%= @available_parties %>
|
||||
</div>
|
||||
<p class="text-neutral-700 dark:text-neutral-300 font-mono uppercase tracking-widest text-sm font-medium">
|
||||
Événements disponibles
|
||||
</p>
|
||||
<div class="mt-4 h-1 bg-gradient-to-r from-purple-500 via-indigo-500 to-pink-500 rounded-full w-0 group-hover:w-full transition-all duration-500"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="group relative overflow-hidden rounded-2xl bg-white dark:bg-slate-800 border border-neutral-200 dark:border-slate-700 hover:border-purple-300 dark:hover:border-purple-600 transition-all duration-300 p-8">
|
||||
<div class="absolute inset-0 bg-gradient-to-br from-purple-100 to-indigo-100 dark:from-purple-900/20 dark:to-indigo-900/20 opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div>
|
||||
<div class="relative">
|
||||
<div class="text-5xl md:text-3xl font-light bg-gradient-to-r from-purple-600 via-indigo-600 to-pink-600 bg-clip-text text-transparent mb-3">
|
||||
<%= @events_this_week %>
|
||||
</div>
|
||||
<p class="text-neutral-700 dark:text-neutral-300 font-mono uppercase tracking-widest text-sm font-medium">
|
||||
Événements aujourd'hui
|
||||
</p>
|
||||
<div class="mt-4 h-1 bg-gradient-to-r from-purple-500 via-indigo-500 to-pink-500 rounded-full w-0 group-hover:w-full transition-all duration-500"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="group relative overflow-hidden rounded-2xl bg-white dark:bg-slate-800 border border-neutral-200 dark:border-slate-700 hover:border-purple-300 dark:hover:border-purple-600 transition-all duration-300 p-8">
|
||||
<div class="absolute inset-0 bg-gradient-to-br from-purple-100 to-indigo-100 dark:from-purple-900/20 dark:to-indigo-900/20 opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div>
|
||||
<div class="relative">
|
||||
<div class="text-5xl md:text-3xl font-light bg-gradient-to-r from-purple-600 via-indigo-600 to-pink-600 bg-clip-text text-transparent mb-3">
|
||||
<%= @events_this_week %>
|
||||
</div>
|
||||
<p class="text-neutral-700 dark:text-neutral-300 font-mono uppercase tracking-widest text-sm font-medium">
|
||||
Événements cette semaine
|
||||
</p>
|
||||
<div class="mt-4 h-1 bg-gradient-to-r from-purple-500 via-indigo-500 to-pink-500 rounded-full w-0 group-hover:w-full transition-all duration-500"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Today's parties -->
|
||||
<div class="card hover-lift mb-8">
|
||||
<div class="card-header">
|
||||
<h2 class="text-2xl font-bold text-slate-900 dark:text-slate-100">Évenements du jour</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<% if @today_parties.any? %>
|
||||
<ul class="space-y-4">
|
||||
<% @today_parties.each do |party| %>
|
||||
<li>
|
||||
<%= 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 %>
|
||||
<div class="flex items-center space-x-4">
|
||||
<div class="w-16 h-16 bg-slate-200 dark:bg-slate-700 rounded-lg overflow-hidden flex-shrink-0">
|
||||
<%= image_tag party.image, alt: party.name, class: "w-full h-full object-cover" if party.image.present? %>
|
||||
</div>
|
||||
<div class="flex-1 min-w-0">
|
||||
<h3 class="text-lg font-semibold text-slate-900 dark:text-slate-100 group-hover:text-purple-600 dark:group-hover:text-purple-400 transition-colors duration-200">
|
||||
<%= party.name %>
|
||||
</h3>
|
||||
<p class="text-sm text-slate-600 dark:text-slate-400">
|
||||
<%= l(party.start_time, format: :short) %>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
</li>
|
||||
<% end %>
|
||||
</ul>
|
||||
<% else %>
|
||||
<p class="text-slate-600 dark:text-slate-400">Aucune partie aujourd'hui.</p>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tomorrow's parties -->
|
||||
<div class="card hover-lift mb-8">
|
||||
<div class="card-header">
|
||||
<h2 class="text-2xl font-bold text-slate-900 dark:text-slate-100">Évenements de demain</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<% if @tomorrow_parties.any? %>
|
||||
<ul class="space-y-4">
|
||||
<% @tomorrow_parties.each do |party| %>
|
||||
<li>
|
||||
<%= 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 %>
|
||||
<div class="flex items-center space-x-4">
|
||||
<div class="w-16 h-16 bg-slate-200 dark:bg-slate-700 rounded-lg overflow-hidden flex-shrink-0">
|
||||
<%= image_tag party.image, alt: party.name, class: "w-full h-full object-cover" if party.image.present? %>
|
||||
</div>
|
||||
<div class="flex-1 min-w-0">
|
||||
<h3 class="text-lg font-semibold text-slate-900 dark:text-slate-100 group-hover:text-purple-600 dark:group-hover:text-purple-400 transition-colors duration-200">
|
||||
<%= party.name %>
|
||||
</h3>
|
||||
<p class="text-sm text-slate-600 dark:text-slate-400">
|
||||
<%= l(party.start_time, format: :short) %>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
</li>
|
||||
<% end %>
|
||||
</ul>
|
||||
<% else %>
|
||||
<p class="text-slate-600 dark:text-slate-400">Aucune partie demain.</p>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Other upcoming parties with pagination -->
|
||||
<div class="card hover-lift">
|
||||
<div class="card-header">
|
||||
<h2 class="text-2xl font-bold text-slate-900 dark:text-slate-100">Autres évenements à venir</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<% if @other_parties.any? %>
|
||||
<ul class="space-y-4">
|
||||
<% @other_parties.each do |party| %>
|
||||
<li>
|
||||
<%= 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 %>
|
||||
<div class="flex items-center space-x-4">
|
||||
<div class="w-16 h-16 bg-slate-200 dark:bg-slate-700 rounded-lg overflow-hidden flex-shrink-0">
|
||||
<%= image_tag party.image, alt: party.name, class: "w-full h-full object-cover" if party.image.present? %>
|
||||
</div>
|
||||
<div class="flex-1 min-w-0">
|
||||
<h3 class="text-lg font-semibold text-slate-900 dark:text-slate-100 group-hover:text-purple-600 dark:group-hover:text-purple-400 transition-colors duration-200">
|
||||
<%= party.name %>
|
||||
</h3>
|
||||
<p class="text-sm text-slate-600 dark:text-slate-400">
|
||||
<%= l(party.start_time, format: :short) %>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
</li>
|
||||
<% end %>
|
||||
</ul>
|
||||
|
||||
<!-- Pagination -->
|
||||
<div class="mt-8">
|
||||
<%= paginate @other_parties %>
|
||||
</div>
|
||||
<% else %>
|
||||
<p class="text-slate-600 dark:text-slate-400">Aucune autre partie à venir.</p>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
25
app/views/shared/_flash_messages.html.erb
Normal file
25
app/views/shared/_flash_messages.html.erb
Normal file
@@ -0,0 +1,25 @@
|
||||
<% flash.each do |type, message| %>
|
||||
<% if message.present? %>
|
||||
<div class="rounded-md bg-green-50 border-green-100 p-4 border <%= flash_class(type) %> animate-fade-in" data-controller="flash-message">
|
||||
<div class="flex">
|
||||
<div class="shrink-0">
|
||||
<%= flash_icon(type) %>
|
||||
</div>
|
||||
<div class="ml-3 w-full">
|
||||
<div class="space-y-2">
|
||||
<div class="text-sm text-green-700">
|
||||
<p class="text-sm font-medium"><%= message %></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ml-4 flex-shrink-0 flex">
|
||||
<button data-action="click->flash-message#close" class="inline-flex text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-purple-500">
|
||||
<svg class="h-5 w-5" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
<% end %>
|
||||
225
auth-messages-implementation-plan.md
Normal file
225
auth-messages-implementation-plan.md
Normal file
@@ -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? %>
|
||||
<div class="flash-message <%= flash_class(type) %> animate-fade-in" data-controller="flash-message">
|
||||
<div class="flex items-start">
|
||||
<div class="flex-shrink-0">
|
||||
<%= flash_icon(type) %>
|
||||
</div>
|
||||
<div class="ml-3 flex-1">
|
||||
<p class="text-sm font-medium"><%= message %></p>
|
||||
</div>
|
||||
<div class="ml-4 flex-shrink-0 flex">
|
||||
<button data-action="click->flash-message#close" class="inline-flex text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-purple-500">
|
||||
<svg class="h-5 w-5" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<% 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
|
||||
<body class="h-full font-sans text-neutral-900 antialiased">
|
||||
<div class="min-h-full">
|
||||
<%= render "components/header" %>
|
||||
|
||||
<main class="container mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||||
<%= render "shared/flash_messages" %>
|
||||
<%= yield %>
|
||||
</main>
|
||||
|
||||
<%= render "components/footer" %>
|
||||
</div>
|
||||
</body>
|
||||
```
|
||||
|
||||
### 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
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -12,7 +12,10 @@ Rails.application.routes.draw do
|
||||
# Defines the root path route ("/")
|
||||
root "pages#home"
|
||||
|
||||
# parties page
|
||||
# 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"
|
||||
|
||||
|
||||
@@ -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
|
||||
},
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user