From c6f5f8a38b93b1b89cfb08031f4797b7b6360b7e Mon Sep 17 00:00:00 2001 From: Alessandro Dias Batista Date: Mon, 27 Feb 2023 10:42:17 -0300 Subject: [PATCH] add new dashboard page using hotwired --- app/assets/stylesheets/_punches.scss | 3 + app/controllers/punches_controller.rb | 33 ++++++++ app/forms/punches/calendar_form.rb | 44 +++++++++++ app/forms/punches_create_form.rb | 4 +- .../controllers/calendar_controller.js | 39 ++++++++++ app/javascript/packs/calendar.js | 7 ++ app/javascript/packs/hello_react.jsx | 26 ------- app/views/fields/boolean/_index.html.erb | 1 - app/views/fields/boolean/_show.html.erb | 1 - app/views/fields/date_field/_form.html.erb | 6 -- app/views/fields/date_field/_index.html.erb | 1 - app/views/fields/date_field/_show.html.erb | 1 - app/views/layouts/application.html.erb | 1 + app/views/punches/_calendar_form.html.erb | 77 +++++++++++++++++++ app/views/punches/calendar.html.erb | 59 ++++++++++++++ config/application.rb | 1 + config/routes.rb | 10 ++- package.json | 3 + yarn.lock | 15 ++++ 19 files changed, 290 insertions(+), 42 deletions(-) create mode 100644 app/forms/punches/calendar_form.rb create mode 100644 app/javascript/controllers/calendar_controller.js create mode 100644 app/javascript/packs/calendar.js delete mode 100644 app/javascript/packs/hello_react.jsx delete mode 100644 app/views/fields/boolean/_index.html.erb delete mode 100644 app/views/fields/boolean/_show.html.erb delete mode 100644 app/views/fields/date_field/_form.html.erb delete mode 100644 app/views/fields/date_field/_index.html.erb delete mode 100644 app/views/fields/date_field/_show.html.erb create mode 100644 app/views/punches/_calendar_form.html.erb create mode 100644 app/views/punches/calendar.html.erb diff --git a/app/assets/stylesheets/_punches.scss b/app/assets/stylesheets/_punches.scss index b074f6303..a646dc02b 100644 --- a/app/assets/stylesheets/_punches.scss +++ b/app/assets/stylesheets/_punches.scss @@ -47,6 +47,7 @@ $header-text-color: #453b3b; &.out { opacity: .3; + cursor: default; } &.today { @@ -59,6 +60,8 @@ $header-text-color: #453b3b; .weekday-0, .weekday-6, .weekday-holiday { background: $flat-clouds; color: $flat-silver; + cursor: default; + ul { color: $flat-pomegranate; } diff --git a/app/controllers/punches_controller.rb b/app/controllers/punches_controller.rb index 950147bc4..00b7125a1 100644 --- a/app/controllers/punches_controller.rb +++ b/app/controllers/punches_controller.rb @@ -33,6 +33,29 @@ def create end end + def bulk_create + punches_params = params.require(:punches_calendar_form).permit(:project_id, :from1, :from2, :to1, :to2, days: []) + + @punches = Punches::CalendarForm.new(punches_params) + @punches.validate + @punches_of_day = current_user.punches.group_by(&:date) + @current_month_by_weeks = (Date.current.beginning_of_month.beginning_of_week..Date.current.end_of_month.end_of_week).group_by do |date| + date.strftime("%U") + end + + + render :calendar + # @punch = Punch.new(punch_params) + # @punch.user_id = current_user.id + + # if @punch.save + # redirect_to punches_path, notice: I18n.t(:notice, scope: "flash.actions.create", resource_name: "Punch") + # else + # flash_errors('create') + # render :new + # end + end + def update @punch = scopped_punches.find params[:id] @punch.attributes = punch_params @@ -45,6 +68,16 @@ def update end end + def calendar + @selected_month = params[:month].present? && params[:year].present? ? "#{params[:year]}/#{params[:month]}/1".to_date : Date.current.beginning_of_month + + @punches = Punches::CalendarForm.new + @punches_of_day = current_user.punches.group_by(&:date) + @current_month_by_weeks = (@selected_month.beginning_of_week..@selected_month.end_of_month.end_of_week).group_by do |date| + date.strftime("%U") + end + end + def destroy punch = Punch.find(params[:id]) punch.destroy diff --git a/app/forms/punches/calendar_form.rb b/app/forms/punches/calendar_form.rb new file mode 100644 index 000000000..6ed04ac20 --- /dev/null +++ b/app/forms/punches/calendar_form.rb @@ -0,0 +1,44 @@ +class Punches::CalendarForm + include ActiveModel::Model + include ActiveModel::Attributes + + attribute :days, :string + attribute :project_id, :integer + attribute :user_id, :integer + attribute :from1, :string + attribute :to1, :string + attribute :from2, :string + attribute :to2, :string + + validates_presence_of :days, :project_id, :from1, :from2, :to1, :to2 + validate :all_days_valid? + + + def initialize(params={}) + super + @punches = days.to_s.split(",").flat_map do |day| + [ + Punch.new(from_time: from1, to_time: to1, project_id: project_id, when_day: day), + Punch.new(from_time: from2, to_time: to2, project_id: project_id, when_day: day) + ] + end + end + + # def model_name + # ActiveModel::Name.new("CalendarPunches") + # end + + # def save + # return false unless valid? + + # Punch.transaction do + # @punches.each(&:save!) + # end + + # true + # end + + def all_days_valid? + + end +end diff --git a/app/forms/punches_create_form.rb b/app/forms/punches_create_form.rb index 40df9d540..72220213a 100644 --- a/app/forms/punches_create_form.rb +++ b/app/forms/punches_create_form.rb @@ -7,9 +7,7 @@ class PunchesCreateForm validate :punches_validations, :punch_on_same_day_validation def initialize(user, punches) - punches ||= [] - - @punches = punches.map do |punch_params| + @punches = Array(punches).map do |punch_params| Punch.new(**punch_params, user: user) end end diff --git a/app/javascript/controllers/calendar_controller.js b/app/javascript/controllers/calendar_controller.js new file mode 100644 index 000000000..2a6efcab7 --- /dev/null +++ b/app/javascript/controllers/calendar_controller.js @@ -0,0 +1,39 @@ +import { Controller } from "@hotwired/stimulus" + +export default class extends Controller { + static classes = [ "selectDay" ] + static targets = [ "selectedDays", "deactivatable" ] + static values = { selectedDays: Array } + + connect() { + this.disableForm() + } + + selectDay({ currentTarget: dayElement, params: { day } }) { + if (this.selectedDaysValue.some(element => element == day)) { + dayElement.classList.remove(this.selectDayClass) + this.selectedDaysValue = this.selectedDaysValue.filter(element => element != day) + } else { + dayElement.classList.add(this.selectDayClass) + this.selectedDaysValue = [day, ...this.selectedDaysValue] + } + } + + selectedDaysValueChanged() { + this.selectedDaysTarget.value = this.selectedDaysValue + + if (this.selectedDaysValue.length) { + this.enableForm() + } else { + this.disableForm() + } + } + + disableForm() { + this.deactivatableTargets.forEach(element => element.disabled = true) + } + + enableForm() { + this.deactivatableTargets.forEach(element => element.disabled = false) + } +} diff --git a/app/javascript/packs/calendar.js b/app/javascript/packs/calendar.js new file mode 100644 index 000000000..546cc5ec7 --- /dev/null +++ b/app/javascript/packs/calendar.js @@ -0,0 +1,7 @@ +import * as Turbo from "@hotwired/turbo" +import { Application } from "@hotwired/stimulus" +import { definitionsFromContext } from "@hotwired/stimulus-webpack-helpers" + +window.Stimulus = Application.start() +const context = require.context("../controllers", true, /\.js$/) +Stimulus.load(definitionsFromContext(context)) diff --git a/app/javascript/packs/hello_react.jsx b/app/javascript/packs/hello_react.jsx deleted file mode 100644 index 772fc97e0..000000000 --- a/app/javascript/packs/hello_react.jsx +++ /dev/null @@ -1,26 +0,0 @@ -// Run this example by adding <%= javascript_pack_tag 'hello_react' %> to the head of your layout file, -// like app/views/layouts/application.html.erb. All it does is render
Hello React
at the bottom -// of the page. - -import React from 'react' -import ReactDOM from 'react-dom' -import PropTypes from 'prop-types' - -const Hello = props => ( -
Hello {props.name}!
-) - -Hello.defaultProps = { - name: 'David' -} - -Hello.propTypes = { - name: PropTypes.string -} - -document.addEventListener('DOMContentLoaded', () => { - ReactDOM.render( - , - document.body.appendChild(document.createElement('div')), - ) -}) diff --git a/app/views/fields/boolean/_index.html.erb b/app/views/fields/boolean/_index.html.erb deleted file mode 100644 index 8b97161e7..000000000 --- a/app/views/fields/boolean/_index.html.erb +++ /dev/null @@ -1 +0,0 @@ -<%= field.data ? 'Yes' : 'No' %> diff --git a/app/views/fields/boolean/_show.html.erb b/app/views/fields/boolean/_show.html.erb deleted file mode 100644 index 8b97161e7..000000000 --- a/app/views/fields/boolean/_show.html.erb +++ /dev/null @@ -1 +0,0 @@ -<%= field.data ? 'Yes' : 'No' %> diff --git a/app/views/fields/date_field/_form.html.erb b/app/views/fields/date_field/_form.html.erb deleted file mode 100644 index 0728f47df..000000000 --- a/app/views/fields/date_field/_form.html.erb +++ /dev/null @@ -1,6 +0,0 @@ -
- <%= f.label field.attribute %> -
-
- <%= f.text_field field.attribute, class: 'datepicker-input' %> -
diff --git a/app/views/fields/date_field/_index.html.erb b/app/views/fields/date_field/_index.html.erb deleted file mode 100644 index 58630252a..000000000 --- a/app/views/fields/date_field/_index.html.erb +++ /dev/null @@ -1 +0,0 @@ -<%= finish_badge_for(field.resource) %> diff --git a/app/views/fields/date_field/_show.html.erb b/app/views/fields/date_field/_show.html.erb deleted file mode 100644 index 58630252a..000000000 --- a/app/views/fields/date_field/_show.html.erb +++ /dev/null @@ -1 +0,0 @@ -<%= finish_badge_for(field.resource) %> diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 3f30e74ab..67a037e4c 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -29,6 +29,7 @@ <%= stylesheet_link_tag "tailwind" %> <%= stylesheet_link_tag "application" %> <%= csrf_meta_tags %> + <%= yield(:assets) if content_for?(:assets) %> diff --git a/app/views/punches/_calendar_form.html.erb b/app/views/punches/_calendar_form.html.erb new file mode 100644 index 000000000..1e3dc0da3 --- /dev/null +++ b/app/views/punches/_calendar_form.html.erb @@ -0,0 +1,77 @@ + + <%= form_for punches, url: calendar_punches_path do |f| %> + + <%= f.hidden_field :days, name: "punches_calendar_form[days][]", data: {calendar_target: "selectedDays"} %> +
+ +
+
Selecionado (<%= 0 %>)
+
+ + +
+
+ +
+
+ <%= f.select :project_id, Project.all, + include_blank: 'Projeto', + required: true, + data: { calendar_target: "deactivatable" } + %> +
+
+
+ + <%= f.time_field :from1, + class: "form-control form-control-sm w-auto ml-1", + value: "09:00", + required: true, + data: { calendar_target: "deactivatable" } + %> + - + <%= f.time_field :to1, + class: "form-control form-control-sm w-auto", + value: "12:00", + required: true, + data: { calendar_target: "deactivatable" } + %> +
+
+
+
+ + <%= f.time_field :from2, + class: "form-control form-control-sm w-auto ml-1", + value: "13:00", + required: true, + data: { calendar_target: "deactivatable" } + %> + - + <%= f.time_field :to2, + class: "form-control form-control-sm w-auto", + value: "18:00", + required: true, + data: { calendar_target: "deactivatable" } + %> +
+
+
+ <%= f.submit class: "w-100", value: "Salvar", data: { calendar_target: "deactivatable" } %> +
+
+
+ <% end %> +
diff --git a/app/views/punches/calendar.html.erb b/app/views/punches/calendar.html.erb new file mode 100644 index 000000000..61ffb1065 --- /dev/null +++ b/app/views/punches/calendar.html.erb @@ -0,0 +1,59 @@ +<% content_for :assets do %> + <%= javascript_pack_tag 'calendar' %> +<% end %> +
+
+ <%= render "calendar_form", punches: @punches %> +
+
+
+ +
+

+ <%= link_to "❮", + calendar_punches_path(month: @selected_month.prev_month.month, year: @selected_month.prev_month.year), + class: "nav-arrow mr-1" %> + <%= @current_month_by_weeks.values.second.first.strftime("%B") %> + <% unless @current_month_by_weeks.values.second.first.month == Date.current.month %> + <%= link_to "❯", calendar_punches_path(month: @selected_month.next_month.month, year: @selected_month.next_month.year), class: "nav-arrow ml-1" %> + <% end %> +

+
Horas: <%= 0 %>
+
+ + + + + <% Date::DAYNAMES.each do |dayname| %> + + <% end %> + + + + <% @current_month_by_weeks.each do |_week_number, days_of_week| %> + + <% days_of_week.each do |day| %> + <%= tag.td class: "weekday-#{day.wday} #{day.month == @selected_month.month ? "" : "out"}", + data: { + action: day.on_weekday? && day.month == @selected_month.month ? "click->calendar#selectDay" : nil, + calendar_day_param: day } do %> +

+ <%= day.strftime("%d") %><%= "/#{day.strftime('%b')}" if day.mday == 1 %> +

+
    + <% Array(@punches_of_day[day]).each do |punch| %> +
  • + <%= "#{punch.from.strftime("%H:%M")} - #{punch.to.strftime("%H:%M")}" %> +
  • + <% end %> +
+ <% end %> + <% end %> + + <% end %> + +
<%= dayname %>
+
+
+
+
diff --git a/config/application.rb b/config/application.rb index 1ea20ae0a..217f805c6 100644 --- a/config/application.rb +++ b/config/application.rb @@ -30,6 +30,7 @@ class Application < Rails::Application config.i18n.locale = :'pt-BR' config.i18n.default_locale = :'pt-BR' config.time_zone = 'America/Sao_Paulo' + config.beginning_of_week = :sunday # Do not swallow errors in after_commit/after_rollback callbacks. config.active_job.queue_adapter = :sidekiq diff --git a/config/routes.rb b/config/routes.rb index 093e6decc..21691f4e5 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -11,9 +11,6 @@ ActiveAdmin.routes(self) devise_for :users, controllers: { sessions: 'user/sessions' } - resources :punches - resource :user, only: %i[show edit update] - resources :vacations, only: %i[index show new create destroy] if ENV["ENABLE_VACATION"].present? resources :dashboard, only: :index do @@ -29,12 +26,19 @@ end authenticated :user do + resource :user, only: %i[show edit update] root to: 'punches#index', as: :authenticated_user get 'two_factor', to: 'users#two_factor' get 'deactivate_two_factor', to: 'users#deactivate_two_factor' post 'confirm_otp', to: 'users#confirm_otp' get 'backup_codes', to: 'users#backup_codes' post 'deactivate_otp', to: 'users#deactivate_otp' + resources :punches do + collection do + get "calendar(/:year)(/:month)", action: :calendar, as: :calendar + post :calendar, action: :bulk_create + end + end end unauthenticated :user do diff --git a/package.json b/package.json index e6e7eeb09..5abd5b66e 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,9 @@ "dependencies": { "@babel/preset-react": "^7.0.0", "@rails/webpacker": "^5.4.3", + "@hotwired/turbo": "7.2.5", + "@hotwired/stimulus": "3.2.1", + "@hotwired/stimulus-webpack-helpers": "1.0.1", "babel-core": "^6.18.2", "babel-loader": "^8.0.0", "babel-plugin-transform-react-remove-prop-types": "^0.4.24", diff --git a/yarn.lock b/yarn.lock index 912e2d94e..69b79f13f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1106,6 +1106,21 @@ resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6" integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw== +"@hotwired/stimulus-webpack-helpers@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@hotwired/stimulus-webpack-helpers/-/stimulus-webpack-helpers-1.0.1.tgz#4cd74487adeca576c9865ac2b9fe5cb20cef16dd" + integrity sha512-wa/zupVG0eWxRYJjC1IiPBdt3Lruv0RqGN+/DTMmUWUyMAEB27KXmVY6a8YpUVTM7QwVuaLNGW4EqDgrS2upXQ== + +"@hotwired/stimulus@3.2.1": + version "3.2.1" + resolved "https://registry.yarnpkg.com/@hotwired/stimulus/-/stimulus-3.2.1.tgz#e3de23623b0c52c247aba4cd5d530d257008676b" + integrity sha512-HGlzDcf9vv/EQrMJ5ZG6VWNs8Z/xMN+1o2OhV1gKiSG6CqZt5MCBB1gRg5ILiN3U0jEAxuDTNPRfBcnZBDmupQ== + +"@hotwired/turbo@7.2.5": + version "7.2.5" + resolved "https://registry.yarnpkg.com/@hotwired/turbo/-/turbo-7.2.5.tgz#2d9d6bde8a9549c3aea8970445ade16ffd56719a" + integrity sha512-o5PByC/mWkmTe4pWnKrixhPECJUxIT/NHtxKqjq7n9Fj6JlNza1pgxdTCJVIq+PI0j95U+7mA3N4n4A/QYZtZQ== + "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced"