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"} %>
+
+ <% 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| %>
+ | <%= 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 %>
+
+
+
+
+
+
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"