mirror of
https://github.com/opf/openproject.git
synced 2026-06-14 03:30:14 +00:00
Implement editing and adding of non working times
This commit is contained in:
@@ -35,10 +35,15 @@ module Users
|
||||
include OpPrimer::ComponentHelpers
|
||||
|
||||
options non_working_times: [],
|
||||
year: Date.current.year
|
||||
year: Date.current.year,
|
||||
user: nil
|
||||
|
||||
private
|
||||
|
||||
def can_update?
|
||||
user.present? && UserNonWorkingTimes::UpdateContract.can_update?(user: User.current, target_user: user)
|
||||
end
|
||||
|
||||
def wrapper_data
|
||||
{
|
||||
"controller" => "users--non-working-times",
|
||||
@@ -78,8 +83,9 @@ module Users
|
||||
end: (clipped.end_date + 1.day).iso8601,
|
||||
title: event_title(clipped),
|
||||
working_days: clipped.working_days_count,
|
||||
type: "user"
|
||||
}
|
||||
type: "user",
|
||||
edit_url: can_update? ? edit_user_non_working_time_path(user, nwt.id) : nil
|
||||
}.compact
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
<%= component_wrapper do %>
|
||||
<%= render(Primer::Alpha::Dialog.new(id: DIALOG_ID, title:)) do |dialog| %>
|
||||
<% dialog.with_body do %>
|
||||
<%= render(Users::NonWorkingTimes::FormComponent.new(user:, non_working_time:)) %>
|
||||
<% end %>
|
||||
|
||||
<% dialog.with_footer do %>
|
||||
<%= render(Primer::Box.new(display: :flex, justify_content: :space_between, flex: 1)) do %>
|
||||
<%= render(Primer::Box.new) do %>
|
||||
<% if non_working_time.persisted? && can_delete? %>
|
||||
<%= render(
|
||||
Primer::Beta::Button.new(
|
||||
scheme: :danger,
|
||||
tag: :a,
|
||||
href: destroy_url,
|
||||
data: { turbo_method: :delete, turbo_confirm: t(:text_are_you_sure) }
|
||||
)
|
||||
) do %>
|
||||
<%= t(:button_delete) %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<%= render(Primer::Box.new(display: :flex, gap: 2)) do %>
|
||||
<%= render(Primer::Beta::Button.new(data: { "close-dialog-id": DIALOG_ID })) { t(:button_cancel) } %>
|
||||
<%= render(Primer::Beta::Button.new(scheme: :primary, form: Users::NonWorkingTimes::FormComponent::FORM_ID, type: :submit)) { t(:button_confirm) } %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
@@ -0,0 +1,60 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
#-- copyright
|
||||
# OpenProject is an open source project management software.
|
||||
# Copyright (C) the OpenProject GmbH
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License version 3.
|
||||
#
|
||||
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
|
||||
# Copyright (C) 2006-2013 Jean-Philippe Lang
|
||||
# Copyright (C) 2010-2013 the ChiliProject Team
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# See COPYRIGHT and LICENSE files for more details.
|
||||
#++
|
||||
|
||||
module Users
|
||||
module NonWorkingTimes
|
||||
class DialogComponent < ApplicationComponent
|
||||
include OpTurbo::Streamable
|
||||
include OpPrimer::ComponentHelpers
|
||||
|
||||
DIALOG_ID = "non-working-time-dialog"
|
||||
|
||||
attr_reader :user, :non_working_time
|
||||
|
||||
def initialize(user:, non_working_time:, **)
|
||||
super(nil, **)
|
||||
@user = user
|
||||
@non_working_time = non_working_time
|
||||
end
|
||||
|
||||
def title
|
||||
non_working_time.persisted? ? t(:button_edit_non_working_time) : t(:button_add_non_working_time)
|
||||
end
|
||||
|
||||
def can_delete?
|
||||
UserNonWorkingTimes::DeleteContract.can_delete?(user: User.current, target_user: user)
|
||||
end
|
||||
|
||||
def destroy_url
|
||||
user_non_working_time_path(user, non_working_time)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,74 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
#-- copyright
|
||||
# OpenProject is an open source project management software.
|
||||
# Copyright (C) the OpenProject GmbH
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License version 3.
|
||||
#
|
||||
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
|
||||
# Copyright (C) 2006-2013 Jean-Philippe Lang
|
||||
# Copyright (C) 2010-2013 the ChiliProject Team
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# See COPYRIGHT and LICENSE files for more details.
|
||||
#++
|
||||
|
||||
module Users
|
||||
module NonWorkingTimes
|
||||
class Form < ApplicationForm
|
||||
form do |f|
|
||||
f.group(layout: :horizontal) do |g|
|
||||
g.single_date_picker(
|
||||
name: :start_date,
|
||||
label: I18n.t(:label_start_date),
|
||||
required: true,
|
||||
value: model.start_date&.iso8601,
|
||||
datepicker_options: {
|
||||
inDialog: Users::NonWorkingTimes::DialogComponent::DIALOG_ID,
|
||||
data: {
|
||||
action: "change->users--non-working-times-form#previewWorkingDays"
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
g.single_date_picker(
|
||||
name: :end_date,
|
||||
label: I18n.t(:label_end_date),
|
||||
required: true,
|
||||
value: model.end_date&.iso8601,
|
||||
datepicker_options: {
|
||||
inDialog: Users::NonWorkingTimes::DialogComponent::DIALOG_ID,
|
||||
data: {
|
||||
action: "change->users--non-working-times-form#previewWorkingDays"
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
g.text_field(
|
||||
name: :working_days_display,
|
||||
label: I18n.t(:label_working_days),
|
||||
disabled: true,
|
||||
value: model.working_days_count,
|
||||
datepicker_options: { inDialog: Users::NonWorkingTimes::DialogComponent::DIALOG_ID },
|
||||
data: { "users--non-working-times-form-target": "workingDaysInput" }
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,20 @@
|
||||
<%= component_wrapper do %>
|
||||
<%= primer_form_with(
|
||||
model: non_working_time,
|
||||
url: form_url,
|
||||
method: form_method,
|
||||
id: FORM_ID,
|
||||
data: {
|
||||
controller: "users--non-working-times-form",
|
||||
"users--non-working-times-form-preview-url-value" => working_days_preview_url
|
||||
}
|
||||
) do |f| %>
|
||||
<%= render(Users::NonWorkingTimes::Form.new(f)) %>
|
||||
|
||||
<% if non_working_time.errors.any? %>
|
||||
<%= render(Primer::Beta::Flash.new(scheme: :danger, mt: 2)) do %>
|
||||
<%= non_working_time.errors.full_messages.join(", ") %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
@@ -0,0 +1,64 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
#-- copyright
|
||||
# OpenProject is an open source project management software.
|
||||
# Copyright (C) the OpenProject GmbH
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License version 3.
|
||||
#
|
||||
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
|
||||
# Copyright (C) 2006-2013 Jean-Philippe Lang
|
||||
# Copyright (C) 2010-2013 the ChiliProject Team
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# See COPYRIGHT and LICENSE files for more details.
|
||||
#++
|
||||
|
||||
module Users
|
||||
module NonWorkingTimes
|
||||
class FormComponent < ApplicationComponent
|
||||
include OpTurbo::Streamable
|
||||
include OpPrimer::ComponentHelpers
|
||||
|
||||
FORM_ID = "non-working-time-form"
|
||||
|
||||
attr_reader :user, :non_working_time
|
||||
|
||||
def initialize(user:, non_working_time:, **)
|
||||
super(nil, **)
|
||||
@user = user
|
||||
@non_working_time = non_working_time
|
||||
end
|
||||
|
||||
def form_url
|
||||
if non_working_time.persisted?
|
||||
user_non_working_time_path(user, non_working_time)
|
||||
else
|
||||
user_non_working_times_path(user)
|
||||
end
|
||||
end
|
||||
|
||||
def form_method
|
||||
non_working_time.persisted? ? :patch : :post
|
||||
end
|
||||
|
||||
def working_days_preview_url
|
||||
working_days_preview_user_non_working_times_path(user)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -4,8 +4,16 @@
|
||||
<% end %>
|
||||
|
||||
<% user_non_working_times.each do |nwt| %>
|
||||
<%= render(Primer::Box.new(bg: :accent_emphasis, color: :on_emphasis, border_radius: 2, p: 2, mb: 1)) do %>
|
||||
<%= range_label(nwt) %>
|
||||
<% if can_update? %>
|
||||
<%= link_to edit_href(nwt),
|
||||
class: "color-bg-accent-emphasis color-fg-on-emphasis rounded-2 p-2 mb-1 d-block",
|
||||
data: { controller: "async-dialog" } do %>
|
||||
<%= range_label(nwt) %>
|
||||
<% end %>
|
||||
<% else %>
|
||||
<%= render(Primer::Box.new(bg: :accent_emphasis, color: :on_emphasis, border_radius: 2, p: 2, mb: 1)) do %>
|
||||
<%= range_label(nwt) %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
@@ -34,7 +34,8 @@ module Users
|
||||
include OpPrimer::ComponentHelpers
|
||||
|
||||
options non_working_times: [],
|
||||
year: Date.current.year
|
||||
year: Date.current.year,
|
||||
user: nil
|
||||
|
||||
private
|
||||
|
||||
@@ -57,18 +58,28 @@ module Users
|
||||
total_user_days + global_day_count
|
||||
end
|
||||
|
||||
def can_update?
|
||||
user.present? && UserNonWorkingTimes::UpdateContract.can_update?(user: User.current, target_user: user)
|
||||
end
|
||||
|
||||
def can_delete?
|
||||
user.present? && UserNonWorkingTimes::DeleteContract.can_delete?(user: User.current, target_user: user)
|
||||
end
|
||||
|
||||
def edit_href(clipped)
|
||||
edit_user_non_working_time_path(user, clipped.id)
|
||||
end
|
||||
|
||||
def range_label(clipped)
|
||||
date_range = format_date_range(clipped.start_date, clipped.end_date)
|
||||
"#{date_range}: #{I18n.t('label_x_working_days', count: clipped.working_days_count)}"
|
||||
end
|
||||
|
||||
def format_date_range(first, last)
|
||||
if first.month == last.month && first.year == last.year
|
||||
"#{I18n.l(first, format: '%b %d')}-#{last.day}, #{first.year}"
|
||||
elsif first.year == last.year
|
||||
"#{I18n.l(first, format: '%b %d')} - #{I18n.l(last, format: '%b %d')}, #{first.year}"
|
||||
if first.year == last.year
|
||||
"#{I18n.l(first, format: :short)} - #{I18n.l(last, format: :short)}, #{first.year}"
|
||||
else
|
||||
"#{I18n.l(first, format: '%b %d, %Y')} - #{I18n.l(last, format: '%b %d, %Y')}"
|
||||
"#{I18n.l(first, format: :long)} - #{I18n.l(last, format: :long)}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -15,14 +15,16 @@
|
||||
I18n.t(:label_today_capitalized)
|
||||
end
|
||||
|
||||
component.with_action_button(
|
||||
scheme: :primary,
|
||||
leading_icon: :plus,
|
||||
label: I18n.t(:button_add_non_working_time),
|
||||
data: { "turbo-stream" => true },
|
||||
tag: :a,
|
||||
href: "#"
|
||||
) do
|
||||
t(:button_add_non_working_time)
|
||||
if can_create?
|
||||
component.with_action_button(
|
||||
scheme: :primary,
|
||||
leading_icon: :plus,
|
||||
label: I18n.t(:button_add_non_working_time),
|
||||
data: { controller: "async-dialog" },
|
||||
tag: :a,
|
||||
href: new_non_working_time_href
|
||||
) do
|
||||
t(:button_add_non_working_time)
|
||||
end
|
||||
end
|
||||
end %>
|
||||
|
||||
@@ -31,7 +31,15 @@
|
||||
module Users
|
||||
module NonWorkingTimes
|
||||
class SubHeaderComponent < ApplicationComponent
|
||||
options :year
|
||||
options :year, :user
|
||||
|
||||
def can_create?
|
||||
UserNonWorkingTimes::CreateContract.can_create?(user: User.current, target_user: user)
|
||||
end
|
||||
|
||||
def new_non_working_time_href
|
||||
new_user_non_working_time_path(user)
|
||||
end
|
||||
|
||||
def previous_year_attrs
|
||||
{
|
||||
|
||||
@@ -31,23 +31,24 @@
|
||||
module Users
|
||||
module NonWorkingTimes
|
||||
class YearOverviewComponent < ApplicationComponent
|
||||
attr_reader :non_working_times, :year
|
||||
attr_reader :non_working_times, :year, :user
|
||||
|
||||
def initialize(year:, non_working_times:, **)
|
||||
def initialize(year:, non_working_times:, user:, **)
|
||||
super(**)
|
||||
@year = year
|
||||
@non_working_times = non_working_times
|
||||
@user = user
|
||||
end
|
||||
|
||||
def call
|
||||
render(Users::NonWorkingTimes::SubHeaderComponent.new(year: year)) +
|
||||
render(Users::NonWorkingTimes::SubHeaderComponent.new(year:, user:)) +
|
||||
render(Primer::Alpha::Layout.new(classes: "users-non-working-times-year-overview")) do |layout|
|
||||
layout.with_main do
|
||||
render(Users::NonWorkingTimes::CalendarComponent.new(non_working_times: non_working_times, year: year))
|
||||
render(Users::NonWorkingTimes::CalendarComponent.new(non_working_times: non_working_times, year: year, user:))
|
||||
end
|
||||
|
||||
layout.with_sidebar(col_placement: :end) do
|
||||
render(Users::NonWorkingTimes::SidebarComponent.new(non_working_times: non_working_times, year: year))
|
||||
render(Users::NonWorkingTimes::SidebarComponent.new(non_working_times: non_working_times, year: year, user:))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -38,6 +38,11 @@ module UserNonWorkingTimes
|
||||
|
||||
def self.model = ::UserNonWorkingTime
|
||||
|
||||
def self.can_manage?(user:, target_user:)
|
||||
user.allowed_globally?(:manage_working_times) ||
|
||||
(target_user.id == user.id && user.allowed_globally?(:manage_own_working_times))
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def validate_manage_permission
|
||||
|
||||
@@ -30,5 +30,8 @@
|
||||
|
||||
module UserNonWorkingTimes
|
||||
class CreateContract < BaseContract
|
||||
def self.can_create?(user:, target_user:)
|
||||
can_manage?(user:, target_user:)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -34,5 +34,9 @@ module UserNonWorkingTimes
|
||||
user.allowed_globally?(:manage_working_times) ||
|
||||
(model.user_id == user.id && user.allowed_globally?(:manage_own_working_times))
|
||||
}
|
||||
|
||||
def self.can_delete?(user:, target_user:)
|
||||
BaseContract.can_manage?(user:, target_user:)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
#-- copyright
|
||||
# OpenProject is an open source project management software.
|
||||
# Copyright (C) the OpenProject GmbH
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License version 3.
|
||||
#
|
||||
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
|
||||
# Copyright (C) 2006-2013 Jean-Philippe Lang
|
||||
# Copyright (C) 2010-2013 the ChiliProject Team
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# See COPYRIGHT and LICENSE files for more details.
|
||||
#++
|
||||
|
||||
module UserNonWorkingTimes
|
||||
class UpdateContract < BaseContract
|
||||
def self.can_update?(user:, target_user:)
|
||||
can_manage?(user:, target_user:)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -30,16 +30,17 @@
|
||||
|
||||
class Users::NonWorkingTimesController < ApplicationController
|
||||
include WorkingTimesAuthorization
|
||||
include OpTurbo::ComponentStream
|
||||
|
||||
layout "admin"
|
||||
|
||||
before_action :check_working_times_feature_flag_is_active
|
||||
|
||||
authorization_checked! :index, :create, :destroy
|
||||
authorization_checked! :index, :new, :create, :edit, :update, :destroy, :working_days_preview
|
||||
|
||||
before_action :find_user
|
||||
before_action :authorize_manage_working_times
|
||||
before_action :find_non_working_time, only: %i[destroy]
|
||||
before_action :find_non_working_time, only: %i[edit update destroy]
|
||||
|
||||
def index
|
||||
@year = (params[:year].presence || Date.current.year).to_i
|
||||
@@ -48,32 +49,78 @@ class Users::NonWorkingTimesController < ApplicationController
|
||||
render "users/edit"
|
||||
end
|
||||
|
||||
def new
|
||||
@non_working_time = @user.non_working_times.build
|
||||
|
||||
respond_with_dialog(
|
||||
Users::NonWorkingTimes::DialogComponent.new(user: @user, non_working_time: @non_working_time)
|
||||
)
|
||||
end
|
||||
|
||||
def edit
|
||||
respond_with_dialog(
|
||||
Users::NonWorkingTimes::DialogComponent.new(user: @user, non_working_time: @non_working_time)
|
||||
)
|
||||
end
|
||||
|
||||
def create
|
||||
call = UserNonWorkingTimes::CreateService
|
||||
.new(user: current_user)
|
||||
.call(**non_working_time_params, user: @user)
|
||||
|
||||
if call.success?
|
||||
flash[:notice] = I18n.t(:notice_successful_create)
|
||||
close_dialog_via_turbo_stream(Users::NonWorkingTimes::DialogComponent::DIALOG_ID)
|
||||
reload_page_via_turbo_stream
|
||||
else
|
||||
flash[:error] = call.errors.full_messages.join(", ")
|
||||
update_via_turbo_stream(
|
||||
component: Users::NonWorkingTimes::FormComponent.new(user: @user, non_working_time: call.result),
|
||||
status: :unprocessable_entity
|
||||
)
|
||||
end
|
||||
|
||||
redirect_to user_non_working_times_path(@user)
|
||||
respond_with_turbo_streams
|
||||
end
|
||||
|
||||
def update
|
||||
call = UserNonWorkingTimes::UpdateService
|
||||
.new(model: @non_working_time, user: current_user)
|
||||
.call(**non_working_time_params)
|
||||
|
||||
if call.success?
|
||||
close_dialog_via_turbo_stream(Users::NonWorkingTimes::DialogComponent::DIALOG_ID)
|
||||
reload_page_via_turbo_stream
|
||||
else
|
||||
update_via_turbo_stream(
|
||||
component: Users::NonWorkingTimes::FormComponent.new(user: @user, non_working_time: call.result),
|
||||
status: :unprocessable_entity
|
||||
)
|
||||
end
|
||||
|
||||
respond_with_turbo_streams
|
||||
end
|
||||
|
||||
def destroy
|
||||
call = UserNonWorkingTimes::DeleteService
|
||||
.new(model: @user_non_working_time, user: current_user)
|
||||
.new(model: @non_working_time, user: current_user)
|
||||
.call
|
||||
|
||||
if call.success?
|
||||
flash[:notice] = I18n.t(:notice_successful_delete)
|
||||
reload_page_via_turbo_stream
|
||||
else
|
||||
flash[:error] = call.errors.full_messages.join(", ")
|
||||
render_error_flash_message_via_turbo_stream(message: call.errors.full_messages.join(", "))
|
||||
end
|
||||
|
||||
redirect_to user_non_working_times_path(@user)
|
||||
respond_with_turbo_streams
|
||||
end
|
||||
|
||||
def working_days_preview
|
||||
start_date = Date.parse(params[:start_date])
|
||||
end_date = Date.parse(params[:end_date])
|
||||
nwt = @user.non_working_times.build(start_date:, end_date:)
|
||||
|
||||
render json: { working_days: nwt.working_days_count }
|
||||
rescue ArgumentError, TypeError
|
||||
head :bad_request
|
||||
end
|
||||
|
||||
private
|
||||
@@ -85,12 +132,12 @@ class Users::NonWorkingTimesController < ApplicationController
|
||||
end
|
||||
|
||||
def find_non_working_time
|
||||
@user_non_working_time = @user.non_working_times.find(params[:id])
|
||||
@non_working_time = @user.non_working_times.find(params[:id])
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
render_404
|
||||
end
|
||||
|
||||
def non_working_time_params
|
||||
params.expect(non_working_time: [:date])
|
||||
params.expect(user_non_working_time: %i[start_date end_date])
|
||||
end
|
||||
end
|
||||
|
||||
@@ -70,6 +70,8 @@ class UserNonWorkingTime < ApplicationRecord
|
||||
end
|
||||
|
||||
def working_days
|
||||
return [] if start_date.blank? || end_date.blank?
|
||||
|
||||
working_days_in(days)
|
||||
end
|
||||
|
||||
@@ -108,6 +110,7 @@ class UserNonWorkingTime < ApplicationRecord
|
||||
|
||||
def no_overlapping_ranges
|
||||
return unless start_date.present? && end_date.present? && user_id.present?
|
||||
return if end_date < start_date
|
||||
|
||||
errors.add(:start_date, :overlapping_range) if overlapping_range_exists?
|
||||
end
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
#-- copyright
|
||||
# OpenProject is an open source project management software.
|
||||
# Copyright (C) the OpenProject GmbH
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License version 3.
|
||||
#
|
||||
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
|
||||
# Copyright (C) 2006-2013 Jean-Philippe Lang
|
||||
# Copyright (C) 2010-2013 the ChiliProject Team
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# See COPYRIGHT and LICENSE files for more details.
|
||||
#++
|
||||
|
||||
module UserNonWorkingTimes
|
||||
class UpdateService < ::BaseServices::Update
|
||||
end
|
||||
end
|
||||
@@ -1,2 +1,2 @@
|
||||
<%= render(My::WorkingTimesHeaderComponent.new) %>
|
||||
<%= render(Users::NonWorkingTimes::YearOverviewComponent.new(year: @year, non_working_times: @non_working_times)) %>
|
||||
<%= render(Users::NonWorkingTimes::YearOverviewComponent.new(year: @year, non_working_times: @non_working_times, user: @user)) %>
|
||||
|
||||
@@ -27,7 +27,7 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
|
||||
++#%>
|
||||
|
||||
<%= render(Users::NonWorkingTimes::SubHeaderComponent.new(year: @year)) %>
|
||||
<%= render(Users::NonWorkingTimes::SubHeaderComponent.new(year: @year, user: @user)) %>
|
||||
|
||||
<%= render(Primer::OpenProject::FlexLayout.new(align_items: :flex_start, gap: :normal)) do |layout| %>
|
||||
<% layout.with_column(flex: 1) do %>
|
||||
|
||||
Executable
+26
@@ -0,0 +1,26 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Deletes bundled javascript assets and rebuilds them.
|
||||
# Useful for when your frontend doesn't work (jQuery not defined etc.) for seemingly no reason at all.
|
||||
|
||||
die() { yell "$*"; exit 1; }
|
||||
try() { eval "$@" || die "\n\nFailed to run '$*'"; }
|
||||
|
||||
echo "Dropping database"
|
||||
try "bundle exec rake db:drop"
|
||||
|
||||
echo "Deleting structure.sql to recreate a fresh DB from migrations"
|
||||
try "rm -f db/structure.sql"
|
||||
|
||||
echo "Creating database"
|
||||
try "bundle exec rake db:create"
|
||||
|
||||
echo "Migrating database"
|
||||
try "bundle exec rake db:migrate"
|
||||
|
||||
echo "Seeding database"
|
||||
try "bundle exec rake db:seed"
|
||||
|
||||
echo "✔ Done."
|
||||
|
||||
|
||||
@@ -4225,8 +4225,12 @@ en:
|
||||
label_non_working_days: "Availability calendar"
|
||||
label_non_working_days_with_count: "Non-working days (%{count})"
|
||||
label_non_working_days_summary: "Summary"
|
||||
button_add_non_working_time: "Time off"
|
||||
button_edit_non_working_time: "Edit time off"
|
||||
label_continued_from_previous_year: "continued from previous year"
|
||||
label_continues_into_next_year: "continues into next year"
|
||||
label_end_date: "Finish date"
|
||||
label_working_days: "Working days"
|
||||
label_non_working_times_with_count: "%{year} time off (%{count})"
|
||||
label_non_working_times_summary: "%{year} summary"
|
||||
label_total_user_non_working_times: "Personal non-working days"
|
||||
|
||||
+5
-1
@@ -921,7 +921,11 @@ Rails.application.routes.draw do
|
||||
resources :users, constraints: { id: /(\d+|me)/ }, except: :edit do
|
||||
resources :memberships, controller: "users/memberships", only: %i[update create destroy]
|
||||
resources :working_hours, controller: "users/working_hours"
|
||||
resources :non_working_times, controller: "users/non_working_times", only: %i[index create destroy]
|
||||
resources :non_working_times, controller: "users/non_working_times", only: %i[index new create edit update destroy] do
|
||||
collection do
|
||||
get :working_days_preview
|
||||
end
|
||||
end
|
||||
|
||||
collection do
|
||||
get "/invite" => "users/invite#start_dialog"
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* -- copyright
|
||||
* OpenProject is an open source project management software.
|
||||
* Copyright (C) the OpenProject GmbH
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version 3.
|
||||
*
|
||||
* OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
|
||||
* Copyright (C) 2006-2013 Jean-Philippe Lang
|
||||
* Copyright (C) 2010-2013 the ChiliProject Team
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* See COPYRIGHT and LICENSE files for more details.
|
||||
* ++
|
||||
*/
|
||||
|
||||
import { Controller } from '@hotwired/stimulus';
|
||||
|
||||
export default class NonWorkingTimesFormController extends Controller {
|
||||
static targets = [
|
||||
'workingDaysInput',
|
||||
];
|
||||
|
||||
static values = {
|
||||
previewUrl: String,
|
||||
};
|
||||
|
||||
declare readonly workingDaysInputTarget:HTMLInputElement;
|
||||
declare readonly hasWorkingDaysInputTarget:boolean;
|
||||
declare readonly previewUrlValue:string;
|
||||
|
||||
previewWorkingDays() {
|
||||
const startDate = (this.element.querySelector<HTMLInputElement>('#user_non_working_time_start_date')?.value);
|
||||
const endDate = (this.element.querySelector<HTMLInputElement>('#user_non_working_time_end_date')?.value);
|
||||
|
||||
if (!startDate || !endDate) return;
|
||||
|
||||
void fetch(`${this.previewUrlValue}?start_date=${startDate}&end_date=${endDate}`, {
|
||||
headers: { Accept: 'application/json' },
|
||||
})
|
||||
.then((r) => r.json() as Promise<{ working_days:number }>)
|
||||
.then(({ working_days }) => {
|
||||
if (this.hasWorkingDaysInputTarget) {
|
||||
this.workingDaysInputTarget.value = String(working_days);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -32,6 +32,8 @@ import { Controller } from '@hotwired/stimulus';
|
||||
import { Calendar } from '@fullcalendar/core';
|
||||
import multiMonthPlugin from '@fullcalendar/multimonth';
|
||||
import allLocales from '@fullcalendar/core/locales-all';
|
||||
import { renderStreamMessage } from '@hotwired/turbo';
|
||||
import { TurboHelpers } from 'core-turbo/helpers';
|
||||
|
||||
interface NonWorkingDayEvent {
|
||||
date?:string;
|
||||
@@ -40,6 +42,7 @@ interface NonWorkingDayEvent {
|
||||
title:string;
|
||||
type:'global' | 'user';
|
||||
workingDays?:number;
|
||||
edit_url?:string;
|
||||
}
|
||||
|
||||
export default class NonWorkingTimesController extends Controller {
|
||||
@@ -97,11 +100,29 @@ export default class NonWorkingTimesController extends Controller {
|
||||
startTime: '00:00',
|
||||
endTime: '24:00',
|
||||
},
|
||||
eventClick: (info) => {
|
||||
const editUrl = info.event.extendedProps.editUrl as string | undefined;
|
||||
if (editUrl) {
|
||||
info.jsEvent.preventDefault();
|
||||
this.openDialog(editUrl);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
this.calendar.render();
|
||||
}
|
||||
|
||||
private openDialog(url:string):void {
|
||||
TurboHelpers.showProgressBar();
|
||||
|
||||
void fetch(url, {
|
||||
headers: { Accept: 'text/vnd.turbo-stream.html' },
|
||||
})
|
||||
.then((response) => response.text())
|
||||
.then((html) => { renderStreamMessage(html); })
|
||||
.finally(() => { TurboHelpers.hideProgressBar(); });
|
||||
}
|
||||
|
||||
private scrollToToday() {
|
||||
if (this.yearValue !== new Date().getFullYear()) return;
|
||||
|
||||
@@ -126,7 +147,7 @@ export default class NonWorkingTimesController extends Controller {
|
||||
start: event.start,
|
||||
end: event.end,
|
||||
title: event.title,
|
||||
extendedProps: { workingDays: event.workingDays },
|
||||
extendedProps: { workingDays: event.workingDays, editUrl: event.edit_url },
|
||||
classNames: ['non-working-day--user'],
|
||||
allDay: true,
|
||||
};
|
||||
|
||||
@@ -27,6 +27,7 @@ import LazyPageController from './controllers/dynamic/work-packages/activities-t
|
||||
import EditablePageHeaderTitleController from './controllers/dynamic/editable-page-header-title.controller';
|
||||
import WorkingHoursFormController from './controllers/dynamic/users/working-hours-form.controller';
|
||||
import NonWorkingTimesController from './controllers/dynamic/users/non-working-times.controller';
|
||||
import NonWorkingTimesFormController from './controllers/dynamic/users/non-working-times-form.controller';
|
||||
|
||||
import AutoSubmit from '@stimulus-components/auto-submit';
|
||||
import RevealController from '@stimulus-components/reveal';
|
||||
@@ -86,6 +87,7 @@ OpenProjectStimulusApplication.preregister('select-autosize', SelectAutosizeCont
|
||||
OpenProjectStimulusApplication.preregister('editable-page-header-title', EditablePageHeaderTitleController);
|
||||
OpenProjectStimulusApplication.preregister('users--working-hours-form', WorkingHoursFormController);
|
||||
OpenProjectStimulusApplication.preregister('users--non-working-times', NonWorkingTimesController);
|
||||
OpenProjectStimulusApplication.preregister('users--non-working-times-form', NonWorkingTimesFormController);
|
||||
OpenProjectStimulusApplication.preregister('check-all', CheckAllController);
|
||||
OpenProjectStimulusApplication.preregister('checkable', CheckableController);
|
||||
OpenProjectStimulusApplication.preregister('truncation', TruncationController);
|
||||
|
||||
Reference in New Issue
Block a user