diff --git a/Gemfile b/Gemfile index a85fd77..da1c7ab 100644 --- a/Gemfile +++ b/Gemfile @@ -8,9 +8,11 @@ gem "rails", "~> 7.1.3", ">= 7.1.3.2" gem "bootsnap", require: false gem "devise" +gem "devise-i18n" gem "image_processing" gem "importmap-rails" gem "jbuilder" +gem "kaminari" gem "packwerk" gem "pg" gem "puma" diff --git a/Gemfile.lock b/Gemfile.lock index e53dc31..790b5f0 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -120,6 +120,8 @@ GEM railties (>= 4.1.0) responders warden (~> 1.2.3) + devise-i18n (1.12.0) + devise (>= 4.9.0) drb (2.2.1) erubi (1.12.0) ffi (1.16.3) @@ -143,6 +145,18 @@ GEM actionview (>= 5.0.0) activesupport (>= 5.0.0) json (2.7.1) + kaminari (1.2.2) + activesupport (>= 4.1.0) + kaminari-actionview (= 1.2.2) + kaminari-activerecord (= 1.2.2) + kaminari-core (= 1.2.2) + kaminari-actionview (1.2.2) + actionview + kaminari-core (= 1.2.2) + kaminari-activerecord (1.2.2) + activerecord + kaminari-core (= 1.2.2) + kaminari-core (1.2.2) language_server-protocol (3.17.0.3) loofah (2.22.0) crass (~> 1.0.2) @@ -320,10 +334,12 @@ DEPENDENCIES capybara debug devise + devise-i18n foreman image_processing importmap-rails jbuilder + kaminari packwerk pg puma diff --git a/app/assets/images/hoy.png b/app/assets/images/hoy.png new file mode 100644 index 0000000..7233e20 Binary files /dev/null and b/app/assets/images/hoy.png differ diff --git a/app/packages/accounts/views/accounts/passwords/new.html.erb b/app/packages/accounts/views/accounts/passwords/new.html.erb index f4f02aa..658a6b4 100644 --- a/app/packages/accounts/views/accounts/passwords/new.html.erb +++ b/app/packages/accounts/views/accounts/passwords/new.html.erb @@ -1,6 +1,4 @@ -
<%- if controller_name != 'sessions' %>
- <%= link_to "Log in", new_session_path(resource_name) %>
+ <%= link_to I18n.t("devise.shared.login"), new_session_path(resource_name) %>
<% end %>
<%- if devise_mapping.registerable? && controller_name != 'registrations' %>
- <%= link_to "Sign up", new_registration_path(resource_name) %>
+ <%= link_to I18n.t("devise.shared.signup"), new_registration_path(resource_name) %>
<% end %>
<%- if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations' %>
- <%= link_to "Forgot your password?", new_password_path(resource_name) %>
+ <%= link_to I18n.t("devise.shared.forgot_password?"), new_password_path(resource_name) %>
<% end %>
<%- if devise_mapping.confirmable? && controller_name != 'confirmations' %>
- <%= link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name) %>
+ <%= link_to I18n.t("devise.shared.did_not_receive_confirmation?"), new_confirmation_path(resource_name) %>
<% end %>
<%- if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks' %>
- <%= link_to "Didn't receive unlock instructions?", new_unlock_path(resource_name) %>
+ <%= link_to I18n.t("devise.shared.did_not_receive_unlock_instrutions?"), new_unlock_path(resource_name) %>
<% end %>
<%- if devise_mapping.omniauthable? %>
diff --git a/app/packages/planning/controllers/pictos_controller.rb b/app/packages/planning/controllers/pictos_controller.rb
new file mode 100644
index 0000000..32cd42c
--- /dev/null
+++ b/app/packages/planning/controllers/pictos_controller.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+class PictosController < ApplicationController
+ before_action :authenticate_account!
+
+ def index
+ @pictos = Picto::FindAll
+ .call(
+ keyword: params[:q],
+ enabled: !params[:only_disabled].present?,
+ page: params[:page]
+ ).value!
+ end
+
+ def enable
+ Picto::Enable
+ .call(picto_id: params[:id])
+
+ redirect_to pictos_path(q: params[:q], only_disabled: params[:only_disabled])
+ end
+
+ def disable
+ Picto::Disable
+ .call(picto_id: params[:id])
+
+ redirect_to pictos_path(q: params[:q], only_disabled: params[:only_disabled])
+ end
+end
diff --git a/app/packages/planning/controllers/plans/events_controller.rb b/app/packages/planning/controllers/plans/events_controller.rb
new file mode 100644
index 0000000..8692ed6
--- /dev/null
+++ b/app/packages/planning/controllers/plans/events_controller.rb
@@ -0,0 +1,95 @@
+# frozen_string_literal: true
+
+module Plans
+ class EventsController < ApplicationController
+ before_action :authenticate_account!
+ before_action :load_plan
+ before_action :load_event, only: %w(edit update destroy)
+
+ def index
+ @events = Event::FindAll
+ .call(plan_id: @plan.id)
+ .value!
+ end
+
+ def new
+ @event_form = Event::Create::Form.new
+ end
+
+ def create
+ response = Event::Create.call(
+ **input_data_for_create
+ )
+
+ if response.success?
+ flash[:notice] = "Nuevo evento creado!"
+ redirect_to plan_events_path(@plan.id)
+ else
+ flash.now[:alert] = "Algo ha ido mal!"
+ @event = response.value.data
+ render :new
+ end
+ end
+
+ def edit
+ @event_form = Event::Update::Form.new(
+ @event.attributes
+ .slice(:plan_id, :picto_id, :title, :day_of_the_week)
+ .merge(event_id: params[:id])
+ )
+ end
+
+ def update
+ response = Event::Update.call(
+ **input_data_for_update
+ )
+
+ if response.success?
+ flash[:notice] = "Evento editado!"
+ redirect_to plan_events_path(response.value.id)
+ else
+ flash.now[:alert] = "Algo ha ido mal!"
+ @event_form = response.value.data
+ render :edit
+ end
+ end
+
+ def destroy
+ Event::Remove.call(
+ plan_id: @plan.id,
+ event_id: params[:id]
+ )
+
+ redirect_to plan_events_path(@plan.id)
+ end
+
+ private
+
+ def load_event
+ @event = Event::Find.call(
+ plan_id: @plan.id,
+ event_id: params[:id]
+ ).value!
+ end
+
+ def input_data_for_create
+ params
+ .require(:event_create_form)
+ .permit(:picto_id, :title, :day_of_the_week)
+ .merge(plan_id: @plan.id)
+ end
+
+ def input_data_for_update
+ params
+ .require(:event_update_form)
+ .permit(:picto_id, :title, :day_of_the_week)
+ .merge(plan_id: @plan.id, event_id: params[:id])
+ end
+
+ def load_plan
+ @plan = Plan::Find
+ .call(account_id: current_account.id, plan_id: params[:plan_id])
+ .value!
+ end
+ end
+end
diff --git a/app/packages/planning/controllers/plans_controller.rb b/app/packages/planning/controllers/plans_controller.rb
index 22cadc0..59aca8f 100644
--- a/app/packages/planning/controllers/plans_controller.rb
+++ b/app/packages/planning/controllers/plans_controller.rb
@@ -2,7 +2,105 @@
class PlansController < ApplicationController
before_action :authenticate_account!
+ before_action :load_plan, only: %w(edit update destroy)
+
+ def index
+ @plans = Plan::FindAll
+ .call(account_id: current_account.id)
+ .value!
+ end
+
+ def new
+ @plan_form = Plan::Create::Form.new
+ end
+
+ def create
+ response = Plan::Create.call(
+ **input_data_for_create
+ )
+
+ if response.success?
+ flash[:notice] = "Nueva planificación creada!"
+ redirect_to plan_events_path(response.value.id)
+ else
+ flash.now[:alert] = "Algo ha ido mal!"
+ @plan_form = response.value.data
+ render :new
+ end
+ end
+
+ def edit
+ @plan_form = Plan::Update::Form.new(
+ @plan.attributes
+ .slice(:account_id, :name)
+ .merge(plan_id: params[:id])
+ )
+ end
+
+ def update
+ response = Plan::Update.call(
+ **input_data_for_update
+ )
+
+ if response.success?
+ flash[:notice] = "Planificación editada!"
+ redirect_to plan_events_path(response.value.id)
+ else
+ flash.now[:alert] = "Algo ha ido mal!"
+ @plan = response.value.data
+ render :edit
+ end
+ end
+
+ def activate
+ response = Plan::Activate.call(
+ account_id: current_account.id, plan_id: params[:id]
+ )
+
+ if response.success?
+ flash[:notice] = "Nueva planificación creada!"
+ else
+ flash[:alert] = "Algo ha ido mal!"
+ end
+
+ redirect_to plans_path
+ end
def current
+ @plan = Plan::FindCurrent.call(
+ account_id: current_account.id
+ ).value
+ end
+
+ def destroy
+ Plan::Remove.call(
+ account_id: current_account.id,
+ plan_id: @plan.id
+ )
+
+ redirect_to plans_path
+ end
+
+ private
+
+ def load_plan
+ @plan = Plan::Find.call(
+ account_id: current_account.id,
+ plan_id: params[:id]
+ ).value!
+ end
+
+ def input_data_for_create
+ params
+ .require(:plan_create_form)
+ .permit(:name)
+ .merge(account_id: current_account.id)
+ end
+
+ def input_data_for_update
+ params
+ .require(:plan_update_form)
+ .permit(:name)
+ .merge(account_id: current_account.id, plan_id: params[:id])
end
end
diff --git a/app/packages/planning/models/picto.rb b/app/packages/planning/models/picto.rb
index 579dab2..204304b 100644
--- a/app/packages/planning/models/picto.rb
+++ b/app/packages/planning/models/picto.rb
@@ -1,9 +1,14 @@
# frozen_string_literal: true
class Picto < ApplicationRecord
- has_one_attached :image
+ has_one_attached :image do |attachable|
+ attachable.variant :thumb, resize_to_limit: [160, 160]
+ attachable.variant :mini_thumb, resize_to_limit: [100, 100]
+ end
def to_struct
- CustomStruct.new(attributes.merge(enabled?: enabled))
+ CustomStruct.new(
+ attributes.merge(enabled?: enabled, image:)
+ )
end
end
diff --git a/app/packages/planning/operations/event/find.rb b/app/packages/planning/operations/event/find.rb
new file mode 100644
index 0000000..9fe22f1
--- /dev/null
+++ b/app/packages/planning/operations/event/find.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+class Event::Find < CommandHandler::Command
+ class Form
+ include CommandHandler::Form
+
+ attribute :plan_id, :integer
+ attribute :event_id, :integer
+ end
+
+ delegate(*Form.new.attributes.keys, to: :form)
+
+ def call
+ if (event = Event.find_by(plan_id:, id: event_id))
+ Response.success(event.to_struct)
+ else
+ Response.failure(
+ Errors::RecordNotFoundError
+ .build(form:)
+ )
+ end
+ end
+end
diff --git a/app/packages/planning/operations/event/find_all.rb b/app/packages/planning/operations/event/find_all.rb
new file mode 100644
index 0000000..86e555e
--- /dev/null
+++ b/app/packages/planning/operations/event/find_all.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+class Event::FindAll < CommandHandler::Command
+ class Form
+ include CommandHandler::Form
+
+ attribute :plan_id, :integer
+
+ validates :plan_id, presence: true
+ end
+
+ delegate(*Form.new.attributes.keys, to: :form)
+
+ def execute
+ Response.success(
+ Event
+ .where(plan_id:)
+ .order(:day_of_the_week)
+ )
+ end
+end
diff --git a/app/packages/planning/operations/event/remove.rb b/app/packages/planning/operations/event/remove.rb
new file mode 100644
index 0000000..6a5f383
--- /dev/null
+++ b/app/packages/planning/operations/event/remove.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+class Event::Remove < CommandHandler::Command
+ class Form
+ include CommandHandler::Form
+
+ attribute :plan_id, :integer
+ attribute :event_id, :integer
+
+ validates :plan_id, presence: true
+ validates :event_id, presence: true
+ end
+
+ delegate :event_id, :plan_id, to: :form
+
+ def execute
+ Event::Find
+ .call(plan_id:, event_id:)
+ .and_then do |event|
+ Response.success(
+ Event
+ .destroy(event.id)
+ .to_struct
+ )
+ end
+ end
+end
diff --git a/app/packages/planning/operations/event/update.rb b/app/packages/planning/operations/event/update.rb
new file mode 100644
index 0000000..d45b43b
--- /dev/null
+++ b/app/packages/planning/operations/event/update.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+class Event::Update < CommandHandler::Command
+ class Form
+ include CommandHandler::Form
+
+ attribute :plan_id, :integer
+ attribute :event_id, :integer
+ attribute :picto_id, :integer
+ attribute :title, :string
+ attribute :day_of_the_week, :integer
+ end
+
+ delegate(*Form.new.attributes.keys, to: :form)
+
+ def call
+ Event::Find
+ .call(plan_id:, event_id:)
+ .and_then do
+ Event
+ .find_by(id: event_id)
+ .update_with_response(picto_id:, title:, day_of_the_week:)
+ .and_then do |event|
+ Response.success(event.to_struct)
+ end
+ end
+ end
+end
diff --git a/app/packages/planning/operations/picto/find_all.rb b/app/packages/planning/operations/picto/find_all.rb
new file mode 100644
index 0000000..68930eb
--- /dev/null
+++ b/app/packages/planning/operations/picto/find_all.rb
@@ -0,0 +1,42 @@
+# frozen_string_literal: true
+
+class Picto::FindAll < CommandHandler::Command
+ class Form
+ include CommandHandler::Form
+
+ attribute :keyword, :string
+ attribute :enabled, :boolean, default: false
+ attribute :page, :integer
+ attribute :per_page, :integer, default: 25
+ end
+
+ delegate(*Form.new.attributes.keys, to: :form)
+
+ def call
+ pictos = find_all
+
+ Response.success(
+ PaginatedCollection.new(
+ pictos.map(&:to_struct),
+ pictos.current_page,
+ pictos.prev_page,
+ pictos.next_page,
+ pictos.total_pages,
+ per_page
+ )
+ )
+ end
+
+ private
+
+ def find_all
+ pictos = Picto.all
+ pictos = pictos.where("keyword ILIKE ?", "%#{keyword}%") if keyword.present?
+
+ pictos
+ .where(enabled:)
+ .order(:keyword)
+ .page(page)
+ .per(per_page)
+ end
+end
diff --git a/app/packages/planning/operations/plan/find_all.rb b/app/packages/planning/operations/plan/find_all.rb
new file mode 100644
index 0000000..9846dc2
--- /dev/null
+++ b/app/packages/planning/operations/plan/find_all.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+class Plan::FindAll < CommandHandler::Command
+ class Form
+ include CommandHandler::Form
+
+ attribute :account_id, :integer
+
+ validates :account_id, presence: true
+ end
+
+ delegate(*Form.new.attributes.keys, to: :form)
+
+ def execute
+ Response.success(
+ Plan
+ .where(account_id:)
+ .map(&:to_struct)
+ )
+ end
+end
diff --git a/app/packages/planning/operations/plan/find_current.rb b/app/packages/planning/operations/plan/find_current.rb
new file mode 100644
index 0000000..c60f621
--- /dev/null
+++ b/app/packages/planning/operations/plan/find_current.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+class Plan::FindCurrent < CommandHandler::Command
+ class Form
+ include CommandHandler::Form
+
+ attribute :account_id, :integer
+
+ validates :account_id, presence: true
+ end
+
+ delegate :account_id, :plan_id, to: :form
+
+ def execute
+ if (plan = Plan.find_by(account_id:, active: true))
+ Response.success(plan.to_struct)
+ else
+ Response.failure(
+ Errors::RecordNotFoundError
+ .build(form:)
+ )
+ end
+ end
+end
diff --git a/app/packages/planning/views/pictos/index.html.erb b/app/packages/planning/views/pictos/index.html.erb
new file mode 100644
index 0000000..b921d67
--- /dev/null
+++ b/app/packages/planning/views/pictos/index.html.erb
@@ -0,0 +1,37 @@
+<%= form_tag pictos_path, method: :get, class: "mb-4 pl-2" do %>
+
+
Antes de nada hay que crear una planificación.
diff --git a/app/packages/planning/views/plans/edit.html.erb b/app/packages/planning/views/plans/edit.html.erb new file mode 100644 index 0000000..86d2a22 --- /dev/null +++ b/app/packages/planning/views/plans/edit.html.erb @@ -0,0 +1,7 @@ +<%= form_for @plan_form, url: plan_path(@plan.id), method: :put, html: { class: "max-w-sm mx-auto mt-20" } do |f| %> ++ Día de la semana + | ++ Nombre + | ++ Acciones + | +
---|---|---|
+ <%= day_to_name(event.day_of_the_week) %> + | + ++ <%= event.title %> + | ++ <%= link_to "Editar", edit_plan_event_path(@plan.id, event.id), + class: "font-medium text-blue-600 dark:text-blue-500 hover:underline" %> + - + <%= link_to "Eliminar", plan_event_path(@plan.id, event.id), data: { "turbo-method": :delete }, + class: "font-medium text-blue-600 dark:text-blue-500 hover:underline" %> + | +
+ Nombre + | ++ Acciones + | +
---|---|
+ <%= plan.name %> + | ++ <%= link_to "Editar", edit_plan_path(plan.id), + class: "font-medium text-blue-600 dark:text-blue-500 hover:underline" %> + - + <%= link_to "Eliminar", plan_path(plan.id), data: { "turbo-method": :delete }, + class: "font-medium text-blue-600 dark:text-blue-500 hover:underline" %> + - + <%= link_to "Gestionar", plan_events_path(plan.id), + class: "font-medium text-blue-600 dark:text-blue-500 hover:underline" %> + <% unless plan.active? %> + - + <%= link_to "Activar", activate_plan_path(plan.id), data: { "turbo-method": :put }, + class: "font-medium text-blue-600 dark:text-blue-500 hover:underline" %> + <% end %> + | +