This commit is contained in:
BoHung Chiu 2022-04-18 09:01:57 +08:00
parent 054c7a215e
commit 542c6a06aa
44 changed files with 2750 additions and 185 deletions

View File

@ -1,38 +1,105 @@
class Admin::RulingTimersController < OrbitMemberController class Admin::RulingTimersController < OrbitMemberController
include Admin::RulingTimersHelper include Admin::RulingTimersHelper
before_action :set_weekdays before_action :check_permissions, :set_weekdays, :set_setting
before_action :set_projects , :only => [:project_management,:add_task,:edit_task]
before_action :set_tags, :only => [:index, :task_management, :project_management, :add_task,:edit_task]
def check_permissions
@is_manager = false
OrbitHelper.set_params(params,current_user)
access_level = OrbitHelper.user_access_level?
if (access_level.nil? || access_level == "user")
@setting = RulingTimerSetting.first
unless @setting.authorize?(current_user)
render_401
end
else
@is_manager = true
end
end
def set_type_params
all_types = ["all_tasks","my_tasks","my_assisted_tasks","my_observed_tasks"]
type = params[:type]
if !all_types.include?(type)
if @is_manager
type = all_types[0]
else
type = all_types[1]
end
end
case type
when "all_tasks"
@tasks = @tasks
when "my_tasks"
@tasks = @tasks.where(:owner=>current_user)
when "my_assisted_tasks"
@tasks = @tasks.where(:helper_ids=>current_user.id.to_s)
when "my_observed_tasks"
@tasks = @tasks.where(:observer_ids=>current_user.id.to_s)
end
end
def initialize def initialize
super super
@app_title = "ruling_timer" @app_title = "ruling_timer"
end end
def index def index
user = current_user user = current_user
@ruling_timer_temp = RulingTimerTemp.where(:user=>user).first get_user_timer_data(user)
@ruling_timer_history = []
all_count = 0
per = 10
page = params[:page].to_i
if @ruling_timer_temp
@ruling_timer_history = RulingTimerHistory.where(:user=>user).desc(:created_at)
all_count += (1 + @ruling_timer_history.count)
if page <= 1
@ruling_timer_history = @ruling_timer_history.page(0).per(per - 1)
else
@ruling_timer_history = @ruling_timer_history.limit(per).skip(per * page - 1)
@ruling_timer_temp = nil
end
end
@ruling_pager = RulingPager.new(:count=>all_count,:page=>page,:per=>per)
end end
def timer_management def timer_management
@timer_temps = RulingTimerTemp.all.page(params[:page]).per(15) @timer_temps = RulingTimerTemp.all.page(params[:page]).per(15)
@active_users = @timer_temps.map{|t| t.user} @active_users = @timer_temps.map{|t| t.user}
end end
def task_management def task_management
@tasks = RulingTimerTask.all.page(params[:page]).per(10) @tasks = RulingTimerTask.all.desc(:created_at).page(params[:page]).per(10)
set_type_params
end
def project_management
end end
def work_history def work_history
user = User.find(params[:id]) rescue nil user = User.find(params[:id]) rescue nil
get_user_timer_data(user,true)
render "index"
end
def set_projects
@categories = current_user.approved_categories.select{|c| c.module_app_id == @module_app.id} rescue []
@task = nil
@private_project_id = nil
if params[:action] == "edit_task"
@task = RulingTimerTask.find(params[:id])
@private_project_id = RulingTimerProject.where(:is_private=>true,:creator=>@task.creator).pluck(:id).first #私人專案
else
@private_project_id = RulingTimerProject.where(:is_private=>true,:creator=>current_user).pluck(:id).first #私人專案
end
@projects = RulingTimerProject.any_of({:is_hidden=>false,:category_id.in=>@categories.collect(&:id)},{:id=>@private_project_id}).desc(:created_at)
if params[:action] == "project_management"
@projects = @projects.page(params[:page]).per(10)
else
@projects = @projects.to_a
if @private_project_id.nil?
@projects = [RulingTimerProject.new] + @projects
end
end
end
def get_user_timer_data(user, display_name = false)
time_now = DateTime.now.utc.new_offset("+8")
now_year_month = time_now.strftime("%Y/%m")
@start_year_month = params[:start_year_month].present? ? DateTime.parse(params[:start_year_month]).utc : time_now
@end_year_month = params[:end_year_month].present? ? DateTime.parse(params[:end_year_month]).utc : time_now
@all_year_months = []
tmp = @start_year_month.clone
while tmp <= @end_year_month do
@all_year_months << tmp.strftime("%Y/%m")
tmp += 1.month
end
@all_year_months_data = @all_year_months.map do |ym|
tmp = RulingTimerTemp.where(:user=>user,:date=>/#{ym.sub("/","\\/")}/).first
all_infos = []
if tmp
all_infos << {:date=>tmp.date.split(" ")[0],:seconds=>tmp.calc("work_times"),:work_time_str=>tmp.transform_second_to_time(tmp.all_work_times_seconds)}
end
all_infos += RulingTimerHistory.where(:user=>user,:date=>/#{ym.sub("/","\\/")}/).desc(:created_at).pluck(:date,:all_work_times_seconds,:work_time_str).map{|d,t,str| {:date=> d.split(" ")[0],:seconds=>t,:work_time_str=>str}}
[ym, all_infos.inject(0){|sum,h| sum + h[:seconds] } , all_infos]
end
@ruling_timer_temp = RulingTimerTemp.where(:user=>user).first @ruling_timer_temp = RulingTimerTemp.where(:user=>user).first
@ruling_timer_history = [] @ruling_timer_history = []
all_count = 0 all_count = 0
@ -49,8 +116,7 @@ class Admin::RulingTimersController < OrbitMemberController
end end
end end
@ruling_pager = RulingPager.new(:count=>all_count,:page=>page,:per=>per) @ruling_pager = RulingPager.new(:count=>all_count,:page=>page,:per=>per)
@user_name = user.name @user_name = user.name if display_name
render "index"
end end
def edit_temp_timer def edit_temp_timer
@timer = RulingTimerTemp.find(params[:id]) rescue nil @timer = RulingTimerTemp.find(params[:id]) rescue nil
@ -86,20 +152,36 @@ class Admin::RulingTimersController < OrbitMemberController
redirect_to params[:referer_url] redirect_to params[:referer_url]
end end
def add_task def add_task
@task = RulingTimerTask.new @no_comment_save_btn = true
@users = User.all.where(:user_name.ne=>"rulingcom") @task = RulingTimerTask.new(:owner=>current_user,:ruling_timer_project_id=>@private_project_id)
if params[:project_id]
@task.ruling_timer_project_id = params[:project_id]
end
@project = @task.ruling_timer_project
@users = User.where(:member_profile_id.in=>MemberProfile.where(:role_ids.in=>@setting.role_ids).pluck(:id))
end end
def edit_task def edit_task
@task = RulingTimerTask.find(params[:id]) @no_comment_save_btn = true
@users = User.all.where(:user_name.ne=>"rulingcom") @users = User.where(:member_profile_id.in=>MemberProfile.where(:role_ids.in=>@setting.role_ids).pluck(:id))
@project = nil
if params[:project_id]
@project = RulingTimerProject.find(params[:project_id])
else
@project = @task.ruling_timer_project
end
end end
def create_task def create_task
@task = RulingTimerTask.create(task_params) @task = RulingTimerTask.new(task_params)
@task.creator = current_user
@task.update_user_id = current_user.id
@task.save
redirect_to params[:referer_url] redirect_to params[:referer_url]
end end
def update_task def update_task
@task = RulingTimerTask.find(params[:id]) @task = RulingTimerTask.find(params[:id])
@task.update_user_id = current_user.id
@task.update_attributes(task_params) @task.update_attributes(task_params)
@task.save
redirect_to params[:referer_url] redirect_to params[:referer_url]
end end
def delete_task def delete_task
@ -109,7 +191,12 @@ class Admin::RulingTimersController < OrbitMemberController
end end
end end
def view_task def view_task
@task = RulingTimerTask.find(params[:id]) @task = nil
if params[:type] == "sub_task"
@task = RulingTimerSubTask.find(params[:id]).ruling_timer_task
else
@task = RulingTimerTask.find(params[:id])
end
end end
def delete_history def delete_history
if params[:confirm_delete] if params[:confirm_delete]
@ -131,6 +218,38 @@ class Admin::RulingTimersController < OrbitMemberController
end end
@history = RulingTimerHistory.new(:user_id=>params[:id]) @history = RulingTimerHistory.new(:user_id=>params[:id])
end end
def add_project
@project = RulingTimerProject.new(:project_manager=>current_user)
@categories = current_user.approved_categories.select{|c| c.module_app_id == @module_app.id} rescue []
@users = User.where(:member_profile_id.in=>MemberProfile.where(:role_ids.in=>@setting.role_ids).pluck(:id))
end
def edit_project
@project = RulingTimerProject.find(params[:id])
@categories = current_user.approved_categories.select{|c| c.module_app_id == @module_app.id} rescue []
@users = User.where(:member_profile_id.in=>MemberProfile.where(:role_ids.in=>@setting.role_ids).pluck(:id))
end
def create_project
@project = RulingTimerProject.new(project_params.merge({:creator=>current_user}))
@project.save
redirect_to params[:referer_url]
end
def update_project
@project = RulingTimerProject.find(params[:id])
@project.update_attributes(project_params)
redirect_to params[:referer_url]
end
def delete_project
if params[:confirm_delete]
RulingTimerProject.where(:id=>params[:id]).destroy
render :json => {:success=>true}
end
end
def view_project
@project = RulingTimerProject.find(params[:id])
@tasks = @project.ruling_timer_tasks.desc(:created_at).page(params[:page]).per(10)
set_type_params
render "task_management"
end
def create_history def create_history
@history_params = history_params @history_params = history_params
user = User.find(@history_params[:user_id]) user = User.find(@history_params[:user_id])
@ -149,6 +268,72 @@ class Admin::RulingTimersController < OrbitMemberController
end end
redirect_to params[:referer_url] redirect_to params[:referer_url]
end end
def section_management
@project = RulingTimerProject.find(params[:id])
@sections = @project.ruling_timer_sections.page(params[:page]).per(10)
if request.xhr?
render :partial => "sections_content"
end
end
def add_section
@section = RulingTimerSection.new(:ruling_timer_project_id=>params[:id])
render :layout => false
end
def edit_section
@section = RulingTimerSection.find(params[:id])
render :layout => false
end
def update_section
@section = RulingTimerSection.find(params[:id])
@section.update_attributes(section_params)
render :json => {:success => true}
end
def create_section
@section = RulingTimerSection.create(section_params)
render :json => {:success => true}
end
def delete_section
if params[:confirm_delete]
RulingTimerSection.where(:id=>params[:id]).destroy
render :json => {:success=>true}
end
end
def save_comment
if params["task_id"]
@comment = nil
@task = RulingTimerTask.find(params["task_id"])
i = 0
if params["id"]
@comment = RulingTimerComment.where(:id=>params["id"]).first
end
if @comment.nil?
@comment = RulingTimerComment.new(:ruling_timer_task_id=>params["task_id"])
i = @task.ruling_timer_comments.count
else
i = @task.ruling_timer_comments.find_index{|c| c.id.to_s == params["id"]}
end
@comment.content = params["content"]
@comment.save
html = nil
f = RulingFormHelper.new
f.fields_for :task,@task do |f|
f.fields_for :ruling_timer_comments, @comment do |f|
html = render_to_string(:partial=>"form_comment", :object => @comment ,:locals=>{:f=>f,:i=>i})
end
end
render :json => {:success=>true,:id=>@comment.id.to_s, :html => html}
end
end
def delete_comment
if params["id"]
RulingTimerComment.where(:id=>params["id"]).destroy
end
puts params
render :json => {:success=>true}
end
def section_params
section_params = params.require(:ruling_timer_section).permit!
end
def history_params def history_params
history_params = params.require(:ruling_timer_history).permit! history_params = params.require(:ruling_timer_history).permit!
date = history_params[:date] date = history_params[:date]
@ -169,8 +354,11 @@ class Admin::RulingTimersController < OrbitMemberController
end end
def task_params def task_params
task_params = params.require(:ruling_timer_task).permit! task_params = params.require(:ruling_timer_task).permit!
task_params[:user_ids] = [] if task_params[:user_ids].nil? end
task_params def project_params
project_params = params.require(:ruling_timer_project).permit!
project_params[:all_user_ids] = [] if project_params[:all_user_ids].nil?
project_params
end end
def timer_params def timer_params
if params[:type] == "temp" if params[:type] == "temp"
@ -183,6 +371,26 @@ class Admin::RulingTimersController < OrbitMemberController
@weekdays = ["sunday","monday","tuesday","wednesday","thursday","friday","saturday"] @weekdays = ["sunday","monday","tuesday","wednesday","thursday","friday","saturday"]
@weekdays.map!{|d| I18n.t("ruling_timer.week_days.#{d}")} @weekdays.map!{|d| I18n.t("ruling_timer.week_days.#{d}")}
end end
def settings
@roles = Role.all
end
def update_setting
@setting.update_attributes(setting_params)
redirect_to settings_admin_ruling_timers_path and return
end
def set_setting
@module_app = ModuleApp.where(:key=>@app_title).first
@setting = RulingTimerSetting.first
if @setting.nil?
@setting = RulingTimerSetting.create
end
end
def set_tags
@tags = @module_app.tags
end
def setting_params
params.require(:ruling_timer_setting).permit!
end
class RulingPager class RulingPager
attr_accessor :count,:page,:per attr_accessor :count,:page,:per
def initialize(attrs) def initialize(attrs)
@ -202,4 +410,8 @@ class Admin::RulingTimersController < OrbitMemberController
(self.count / self.per).ceil (self.count / self.per).ceil
end end
end end
class RulingFormHelper
include ActionView::Helpers::FormHelper
include ActionView::Context
end
end end

View File

@ -30,7 +30,7 @@ class RulingTimersController < ApplicationController
def add_task def add_task
begin begin
@ruling_timer_temp.tasks << params[:task] @ruling_timer_temp.tasks << params[:task]
task = RulingTimerTask.create(:task_name=>params[:task],:user_ids=>[current_user.id.to_s]) task = RulingTimerTask.create(:task_name=>params[:task],:user_ids=>[current_user.id.to_s],:creator=>current_user,:owner=>current_user,:update_user_id=>current_user.id)
sub_task = task.ruling_timer_sub_tasks.first sub_task = task.ruling_timer_sub_tasks.first
@ruling_timer_temp.sub_task_ids << sub_task.id.to_s @ruling_timer_temp.sub_task_ids << sub_task.id.to_s
@ruling_timer_temp.save! @ruling_timer_temp.save!

View File

@ -0,0 +1,56 @@
class RulingTimerComment
include Mongoid::Document
include Mongoid::Timestamps
field :content, type: String, default: ""
belongs_to :uploader, :class_name=>"User", :foreign_key => :uploader_id #上傳者
belongs_to :ruling_timer_task
has_many :ruling_timer_notifies, :autosave => true, :dependent => :destroy
before_create do
self.uploader_id = self.ruling_timer_task.update_user_id rescue nil
end
after_save do
all_user_ids = self.ruling_timer_task.get_all_user_ids rescue []
ruling_timer_temps = RulingTimerTemp.where(:user_id.in=>all_user_ids).to_a
ruling_timer_temps.each do |temp|
notify = self.ruling_timer_notifies.where(:user=>temp.user).first
if notify.nil?
notify = self.ruling_timer_notifies.new(:type=>0,:user=>temp.user)
end
notify.is_hidden = false
notify.ruling_timer_task = self.ruling_timer_task
notify.update_user_id = self.uploader_id
notify.content = self.content
notify.save
end
end
def convert_datetime(time)
if time.class == Time
return time.to_datetime
elsif time.class == DateTime
return time
elsif time.class == String
return DateTime.parse(time)
else
return Time.at(time).to_datetime #time is seconds
end
end
def display_updated_at
update_time = convert_datetime(self.updated_at)
time_now = DateTime.now
formate_date1 = I18n.t("ruling_timer.formate_date1")
formate_date2 = I18n.t("ruling_timer.formate_date2")
formate_date3 = I18n.t("ruling_timer.formate_date3")
formate_date4 = I18n.t("ruling_timer.formate_date4")
diff = time_now.to_time - update_time.to_time
diff_time = Time.at(diff)
if diff < 3600
diff_time.strftime(formate_date4).gsub(/0+(\d)/){|f| $1}
elsif diff < 86400
diff_time.strftime(formate_date3)
elsif time_now.year == update_time.year
update_time.strftime(formate_date2).gsub(/am|pm/){|p| I18n.t("ruling_timer.#{p}")}
else
update_time.strftime(formate_date1).gsub(/am|pm/){|p| I18n.t("ruling_timer.#{p}")}
end
end
end

View File

@ -0,0 +1,60 @@
class RulingTimerFile
include Mongoid::Document
include Mongoid::Timestamps
mount_uploader :file, AssetUploader
field :time_offset, type: String, default: "+8"
field :description, type: String, default: ""
belongs_to :uploader, :class_name=>"User", :foreign_key => :uploader_id #上傳者
belongs_to :ruling_timer_task
has_many :ruling_timer_notifies, :autosave => true, :dependent => :destroy
before_create do
self.uploader_id = self.ruling_timer_task.update_user_id rescue nil
end
after_save do
if self[:file].present?
all_user_ids = self.ruling_timer_task.get_all_user_ids rescue []
ruling_timer_temps = RulingTimerTemp.where(:user_id.in=>all_user_ids).to_a
ruling_timer_temps.each do |temp|
notify = self.ruling_timer_notifies.where(:user=>temp.user).first
if notify.nil?
notify = self.ruling_timer_notifies.new(:type=>1,:user=>temp.user)
end
notify.is_hidden = false
notify.ruling_timer_task = self.ruling_timer_task
notify.update_user_id = self.uploader_id
notify.content = "<a href=\"#{self.file.url}\" title=\"#{self[:file]}\">#{self[:file]}</a>"
notify.save
end
end
end
def convert_datetime(time)
if time.class == Time
return time.to_datetime
elsif time.class == DateTime
return time
elsif time.class == String
return DateTime.parse(time)
else
return Time.at(time).to_datetime #time is seconds
end
end
def display_updated_at
update_time = convert_datetime(self.updated_at)
time_now = DateTime.now
formate_date1 = I18n.t("ruling_timer.formate_date1")
formate_date2 = I18n.t("ruling_timer.formate_date2")
formate_date3 = I18n.t("ruling_timer.formate_date3")
formate_date4 = I18n.t("ruling_timer.formate_date4")
diff = time_now.to_time - update_time.to_time
diff_time = Time.at(diff)
if diff < 3600
diff_time.strftime(formate_date4).gsub(/0+(\d)/){|f| $1}
elsif diff < 86400
diff_time.strftime(formate_date3)
elsif time_now.year == update_time.year
update_time.strftime(formate_date2).gsub(/am|pm/){|p| I18n.t("ruling_timer.#{p}")}
else
update_time.strftime(formate_date1).gsub(/am|pm/){|p| I18n.t("ruling_timer.#{p}")}
end
end
end

View File

@ -0,0 +1,49 @@
class RulingTimerNotify
include Mongoid::Document
include Mongoid::Timestamps
field :time_offset, type: String, default: "+8"
field :content, type: String, default: "" #store comment 、 file name 、 section name
field :type, type: Integer, default: 0 # 0 => comments, 1 => file, 2 => section(列表)
field :data # store section change or other data
field :is_hidden, type: Boolean, default: false
belongs_to :update_user, :class_name=>"User", :foreign_key => :update_user_id #更新提醒的使用者
belongs_to :user
belongs_to :ruling_timer_task
belongs_to :ruling_timer_comment
belongs_to :ruling_timer_file
before_create do
if self.update_user_id.nil?
self.update_user_id = self.ruling_timer_task.update_user_id rescue nil
end
end
def convert_datetime(time)
if time.class == Time
return time.to_datetime
elsif time.class == DateTime
return time
elsif time.class == String
return DateTime.parse(time)
else
return Time.at(time).to_datetime #time is seconds
end
end
def display_updated_at
update_time = convert_datetime(self.updated_at)
time_now = DateTime.now
formate_date1 = I18n.t("ruling_timer.formate_date1")
formate_date2 = I18n.t("ruling_timer.formate_date2")
formate_date3 = I18n.t("ruling_timer.formate_date3")
formate_date4 = I18n.t("ruling_timer.formate_date4")
diff = time_now.to_time - update_time.to_time
diff_time = Time.at(diff)
if diff < 3600
diff_time.strftime(formate_date4).gsub(/0+(\d)/){|f| $1}
elsif diff < 86400
diff_time.strftime(formate_date3)
elsif time_now.year == update_time.year
update_time.strftime(formate_date2).gsub(/am|pm/){|p| I18n.t("ruling_timer.#{p}")}
else
update_time.strftime(formate_date1).gsub(/am|pm/){|p| I18n.t("ruling_timer.#{p}")}
end
end
end

View File

@ -0,0 +1,84 @@
class RulingTimerProject
include Mongoid::Document
include Mongoid::Timestamps
include OrbitTag::Taggable
include OrbitCategory::Categorizable
field :project_no, type: String, default: ""
field :project_name, type: String, default: ""
field :objectives, type: String, default: "" #專案⽬標
field :status, type: Integer, default: 0 # 0 =>規劃中, 1 => 已啟動, 2 => 已結案
has_many :ruling_timer_tasks, :autosave => true
field :is_finished, type: Boolean, default: false
field :time_offset, type: String, default: "+8"
field :privacy, type: Integer, default: 0 #私密等級 0 => 所有團隊成員Open, 1 => 專案成員Project, 2 => 優先, 3 => 任務成員 Task
field :priority, type: Integer, default: 0 #優先度 0 => 無, 1 => 普通, 2 => 優先, 3 => 緊急
belongs_to :creator , :class_name=>"User", :foreign_key => :creator_id #建立者
belongs_to :project_manager , :class_name=>"User", :foreign_key => :project_manager_id #專案經理
field :all_user_ids, type: Array, default: [] #專案成員
field :is_hidden, type: Boolean, default: true
field :is_private, type: Boolean, default: true
field :task_count, type: Integer, default: 0
field :sort_number, type: Integer, default: 0
has_many :ruling_timer_sections, :autosave => true, :dependent => :destroy #任務列表
after_initialize do |record|
unless record.new_record?
save_flag = false
if record.category_id.nil?
record.category_id = RulingTimerSetting.first.personal_category_id
save_flag = true
end
if record.project_manager_id.nil?
record.project_manager_id = record.creator_id
save_flag = true
end
record.save if save_flag
end
end
before_create do |record|
time_now = record.created_at || DateTime.now.utc.new_offset(record.time_offset)
today_start = DateTime.parse("#{time_now.strftime("%Y/%m/%d")} 00:00:00#{record.time_offset}").utc
today_end = DateTime.parse("#{(time_now + 1.day).strftime("%Y/%m/%d")} 00:00:00#{record.time_offset}").utc
today_project_count = record.class.where(:created_at.gte=>today_start,:created_at.lt=>today_end).count
record.project_no = "P"+time_now.strftime("%Y%m%d") + ("0000" + (today_project_count + 1).to_s)[-4..-1]
end
before_save do |record|
end
def all_users
User.where(:id.in=>self.all_user_ids)
end
def convert_datetime(time)
if time.class == Time
return time.to_datetime
elsif time.class == DateTime
return time
elsif time.class == String
return DateTime.parse(time)
else
return Time.at(time).to_datetime #time is seconds
end
end
def create_time
convert_datetime(self.created_at.utc).new_offset(self.time_offset).strftime("%Y/%m/%d %H:%m")
end
def users
User.where(:id.in=>self.user_ids)
end
def get_project_name
self.is_private ? I18n.t("ruling_timer.private_tasks") : self.project_name
end
def get_status
I18n.t("ruling_timer.status_opts.#{self.status}")
end
after_save do |record|
# if record.user_ids_changed?
# delete_user_ids = record.user_ids_was.to_a - record.user_ids.to_a
# add_user_ids = record.user_ids.to_a - record.user_ids_was.to_a
# delete_user_ids.each do |user|
# self.ruling_timer_sub_tasks.where(:user=>user).destroy
# end
# add_user_ids.each do |user|
# self.ruling_timer_sub_tasks.create(:user=>user,:task_name=>task_name)
# end
# end
end
end

View File

@ -0,0 +1,11 @@
class RulingTimerSection
include Mongoid::Document
include Mongoid::Timestamps
field :section_name, type: String, default: ""
belongs_to :ruling_timer_project
has_many :ruling_timer_task
field :task_count, type: Integer, default: 0
after_destroy do |record|
RulingTimerTask.where(:ruling_timer_section_id=>record.id).update_all(:ruling_timer_section_id=>nil)
end
end

View File

@ -0,0 +1,12 @@
class RulingTimerSetting
include Mongoid::Document
include Mongoid::Timestamps
field :role_ids, type: Array, default: []
field :personal_category_id
def roles
Role.where(:id.in=>self.role_ids)
end
def authorize?(user)
return ((user.member_profile.role_ids.map{|id| id.to_s} & self.role_ids).count != 0)
end
end

View File

@ -1,13 +1,160 @@
class RulingTimerTask class RulingTimerTask
include Mongoid::Document include Mongoid::Document
include Mongoid::Timestamps include Mongoid::Timestamps
include OrbitTag::Taggable
field :ticket_no, type: String, default: ""
field :task_name, type: String, default: "" field :task_name, type: String, default: ""
has_many :ruling_timer_sub_tasks, :autosave => true, :dependent => :destroy has_many :ruling_timer_sub_tasks, :autosave => true, :dependent => :destroy
field :user_ids, type: Array, default: [] field :user_ids, type: Array, default: [] #負責⼈、協助人
field :is_finished, type: Boolean, default: false field :is_finished, type: Boolean, default: false
field :time_offset, type: String, default: "+8"
field :privacy, type: Integer, default: 0 #私密等級 0 => 所有團隊成員Open, 1 => 專案成員Project, 2 => 優先, 3 => 任務成員 Task
field :priority, type: Integer, default: 0 #優先度 0 => 無, 1 => 普通, 2 => 優先, 3 => 緊急
belongs_to :creator , :class_name=>"User", :foreign_key => :creator_id #建立者
belongs_to :owner , :class_name=>"User", :foreign_key => :owner_id #負責⼈
belongs_to :ruling_timer_project
field :helper_ids, type: Array, default: [] #協助人
field :observer_ids, type: Array, default: [] #觀察者
field :details, type: String, default: ""
field :progress, type: Integer, default: 0 #進度 0 ~ 100
field :update_user_id
has_many :ruling_timer_files, :autosave => true, :dependent => :destroy
accepts_nested_attributes_for :ruling_timer_files, :allow_destroy => true
has_many :ruling_timer_comments, :autosave => true, :dependent => :destroy
accepts_nested_attributes_for :ruling_timer_comments, :allow_destroy => true
belongs_to :ruling_timer_section
has_many :ruling_timer_notifies, :autosave => true, :dependent => :destroy
after_initialize do
unless self.new_record?
save_flag = false
if self.owner.nil? && self.user_ids.present?
self.owner_id = self.user_ids.first
save_flag = true
end
if self.creator.nil? && self.user_ids.present?
self.creator_id = self.user_ids.first
save_flag = true
end
if self.ruling_timer_project_id.nil?
project = RulingTimerProject.where(:creator=>self.creator).first
if project.nil?
project = RulingTimerProject.create(:created_at=>self.created_at,:creator=>self.creator,:time_offset=>self.time_offset)
end
self.ruling_timer_project = project
save_flag = true
end
self.save if save_flag
end
end
before_create do |record|
time_now = record.created_at || DateTime.now.utc.new_offset(record.time_offset)
today_start = DateTime.parse("#{time_now.strftime("%Y/%m/%d")} 00:00:00#{record.time_offset}").utc
today_end = DateTime.parse("#{(time_now + 1.day).strftime("%Y/%m/%d")} 00:00:00#{record.time_offset}").utc
today_task_count = record.class.where(:created_at.gte=>today_start,:created_at.lt=>today_end).count
record.ticket_no = "T"+time_now.strftime("%Y%m%d") + ("0000" + (today_task_count + 1).to_s)[-4..-1]
end
before_save do |record|
unless @already_save
record.user_ids = [record.owner_id.to_s] + record.helper_ids
record.user_ids = record.user_ids.select{|id| id.present?}
if self.ruling_timer_project_id == "private_task"
time_now = DateTime.now.utc
project = RulingTimerProject.where(:creator=>self.creator).first
if project.nil?
project = RulingTimerProject.create(:created_at=>time_now,:creator=>self.creator,:time_offset=>self.time_offset)
end
self.ruling_timer_project = project
end
end
end
after_save do
if self.ruling_timer_project_id_change && !@already_save
if self.ruling_timer_project_id_change[0]
old_project = RulingTimerProject.find(self.ruling_timer_project_id_change[0]) rescue nil
if old_project
old_project.task_count -= 1
@already_save = true
old_project.save(:validate=>false)
end
end
new_project = self.ruling_timer_project
new_project.task_count += 1
new_project.all_user_ids += self.get_all_user_ids
new_project.all_user_ids.uniq
@already_save = true
new_project.save(:validate=>false)
end
if self.ruling_timer_section_id_change && !@already_save
need_create_notify = true
if self.ruling_timer_section_id_change[0]
if self.ruling_timer_notifies.where(:type=>2,:data=>self.ruling_timer_section_id_change.reverse,:updated_at.lte=>DateTime.now - 1.hour).destroy != 0
need_create_notify = false
end
old_section = RulingTimerSection.find(self.ruling_timer_section_id_change[0]) rescue nil
if old_section
old_section.task_count -= 1 if old_section.task_count > 0
@already_save = true
old_section.save(:validate=>false)
end
end
new_section = self.ruling_timer_section
new_section.task_count += 1
@already_save = true
new_section.save(:validate=>false)
if need_create_notify
all_user_ids = self.get_all_user_ids rescue []
ruling_timer_temps = RulingTimerTemp.where(:user_id.in=>all_user_ids).to_a
ruling_timer_temps.each do |temp|
notify = self.ruling_timer_notifies.new(:type=>2,:user=>temp.user,:data=>self.ruling_timer_section_id_change)
notify.is_hidden = false
notify.ruling_timer_task = self.ruling_timer_task
notify.update_user_id = self.uploader_id
notify.content = self.content
notify.save
end
end
end
end
after_destroy do
@already_save = true
if self.ruling_timer_project
project = self.ruling_timer_project
project.task_count -= 1
project.save(:validate=>false)
end
end
def get_all_user_ids
all_user_ids = []
all_user_ids << self.owner_id.to_s if self.owner_id.present?
all_user_ids += (self.helper_ids + self.observer_ids)
all_user_ids.uniq
end
def helpers
User.where(:id.in=>self.helper_ids)
end
def observers
User.where(:id.in=>self.observer_ids)
end
def convert_datetime(time)
if time.class == Time
return time.to_datetime
elsif time.class == DateTime
return time
elsif time.class == String
return DateTime.parse(time)
else
return Time.at(time).to_datetime #time is seconds
end
end
def create_time
convert_datetime(self.created_at.utc).new_offset(self.time_offset).strftime("%Y/%m/%d %H:%m")
end
def users def users
User.where(:id.in=>self.user_ids) User.where(:id.in=>self.user_ids)
end end
def get_priority
I18n.t("ruling_timer.priority_opts.#{self.priority}")
end
after_save do |record| after_save do |record|
if record.user_ids_changed? if record.user_ids_changed?
delete_user_ids = record.user_ids_was.to_a - record.user_ids.to_a delete_user_ids = record.user_ids_was.to_a - record.user_ids.to_a

View File

@ -148,13 +148,10 @@ class RulingTimerTemp
def check_and_store def check_and_store
unless self.new_record? unless self.new_record?
time_now = DateTime.now.utc time_now = DateTime.now.utc
if time_now.new_offset(self.time_offset).strftime('%Y/%m/%d') != self.get_last_work_time.strftime('%Y/%m/%d')
self.stop
self.store
end
date_str = time_now.new_offset(self.time_offset).strftime("%Y/%m/%d %w") date_str = time_now.new_offset(self.time_offset).strftime("%Y/%m/%d %w")
if self.date != date_str if self.date != date_str
self.save self.stop
self.store
end end
end end
end end
@ -207,6 +204,9 @@ class RulingTimerTemp
if self.work_times.count % 2 == 1 if self.work_times.count % 2 == 1
self.work_times.push(time_now) self.work_times.push(time_now)
end end
if self.rest_times.count % 2 == 1
self.rest_times.push(time_now)
end
self.status = "stop" self.status = "stop"
self.calc("work_times",false) self.calc("work_times",false)
self.work_time_str = transform_second_to_time(self.all_work_times_seconds) self.work_time_str = transform_second_to_time(self.all_work_times_seconds)
@ -268,6 +268,19 @@ class RulingTimerTemp
def getPaddedComp(comp) def getPaddedComp(comp)
return ((comp.to_i < 10) ? ('0' + comp.to_s) : comp.to_s) return ((comp.to_i < 10) ? ('0' + comp.to_s) : comp.to_s)
end end
def self.getPaddedComp(comp)
return ((comp.to_i < 10) ? ('0' + comp.to_s) : comp.to_s)
end
def self.transform_second_to_time(seconds)
seconds = 0 if seconds.nil?
hour = 3600
minute = 60
total_hour = getPaddedComp(seconds / hour)
rest_seconds = seconds % hour
total_minute = getPaddedComp(rest_seconds / minute)
total_second = getPaddedComp(rest_seconds % minute)
return (total_hour + ":" + total_minute + ":" + total_second)
end
def transform_second_to_time(seconds) def transform_second_to_time(seconds)
seconds = 0 if seconds.nil? seconds = 0 if seconds.nil?
hour = 3600 hour = 3600

View File

@ -0,0 +1,42 @@
<div class="comment-block" <%= "data-id=#{form_comment.id}" unless form_comment.new_record? %>>
<% unless form_comment.new_record? %>
<div>
<span class="user_name"><%= form_comment.uploader.name rescue "" %></span>
<span class="update_time"><%= form_comment.display_updated_at rescue "" %></span>
<p class="content_text markdown_text"><%= form_comment.content %></p>
<%= f.text_area :content, :class => "hide content_textarea", :placeholder => t("ruling_timer.comments"), :title => t("ruling_timer.comments") %>
</div>
<% end %>
<div class="input-block">
<% if form_comment.new_record? %>
<div>
<span class="user_name"><%= @current_user.name rescue "" %></span>
<%= f.text_area :content, :class => "content_textarea", :placeholder => t("ruling_timer.comments"), :title => t("ruling_timer.comments") %>
</div>
<% end %>
<% if form_comment.new_record? %>
<span class="delete_comment add-on btn" title="<%= t(:delete_) %>">
<a class="icon-trash"></a>
</span>
<% else %>
<% if @current_user.id == form_comment.uploader.id %>
<span class="add-on btn edit_comment"><a class="icon-edit" title="<%= t(:edit) %>"></a></span>
<% unless @no_comment_save_btn %>
<span class="cancel_edit_comment add-on btn hide" title="<%= t("ruling_timer.cancel_edit") %>">
<a class="icon-remove"></a>
</span>
<% end %>
<span class="remove_existing_record add-on btn" title="<%= t(:remove) %>">
<%= f.hidden_field :id %>
<a class="icon-remove"></a>
<%= f.hidden_field :_destroy, :value => nil, :class => 'should_destroy' %>
</span>
<% end %>
<% end %>
<% unless @no_comment_save_btn %>
<span class="save_comment btn <%= 'hide' if !form_comment.new_record? %>" title="<%= t("ruling_timer.save") %>">
<a class="icon-save"></a>
</span>
<% end %>
</div>
</div>

View File

@ -0,0 +1,43 @@
<% if form_file.new_record? %>
<div class="fileupload fileupload-new start-line" data-provides="fileupload">
<% else %>
<div class="fileupload fileupload-exists start-line" data-provides="fileupload">
<% if form_file.file.blank? %>
<%= t(:no_file) %>
<% else %>
<span class="file_block">
<span class="uploaded_file"><%= link_to content_tag(:i) + form_file.file_identifier, form_file.file.url, {:class => 'file-link file-type', :target => '_blank', :title => form_file.file_identifier} %></span>
<div></div>
<span class="uploader_time"><%= form_file.display_updated_at %> (<span class="user_name"><%= form_file.uploader.name %></span>) </span>
</span>
<% end %>
<% end %>
<div class="input-prepend input-append">
<label>
<span class="add-on btn btn-file" title='<%= t(:file_) %>'>
<i class="icons-paperclip"></i>
<%= f.file_field :file %>
</span>
<div class="uneditable-input input-medium">
<i class="icon-file fileupload-exists"></i>
<span class="fileupload-preview"><%= (form_file.new_record? || form_file.file.blank?) ? t(:select_file) : t(:change_file) %></span>
</div>
</label>
<span class="add-on icons-pencil" title='<%= t(:description) %>'></span>
<span class="tab-content">
<%= f.text_field :description, :class => "input-medium", placeholder: t(:description) %>
</span>
</span>
<% if form_file.new_record? %>
<span class="delete_file add-on btn" title="<%= t(:delete_) %>">
<a class="icon-trash"></a>
</span>
<% else %>
<span class="remove_existing_record add-on btn" title="<%= t(:remove) %>">
<%= f.hidden_field :id %>
<a class="icon-remove"></a>
<%= f.hidden_field :_destroy, :value => nil, :class => 'should_destroy' %>
</span>
<% end %>
</div>
</div>

View File

@ -0,0 +1,16 @@
<style>
.inline-radio{
float: left;
margin-right: 1em;
}
</style>
<% base_name = defined?(base_name) ? base_name + "." : "" %>
<div class="control-group">
<label class="control-label muted"><%= t("#{base_name}#{field_name}") %></label>
<div class="controls">
<% trans = t("#{base_name}#{field_name}_opts") %>
<% trans.each do |k,v| %>
<label class="inline-radio"><%= f.radio_button field_name, k %><%=v%></label>
<% end %>
</div>
</div>

View File

@ -0,0 +1,46 @@
<div id="timer_select_block">
<form method="get">
<%= fields_for "date_pick" do |f| %>
<%= f.datetime_picker :start_year_month, {:picker_type=>"date",:format=>"yyyy/MM",:label=>t("ruling_timer.start_year_month"), :value => @start_year_month} %><%= f.datetime_picker :end_year_month, {:picker_type=>"date",:format=>"yyyy/MM",:label=>t("ruling_timer.end_year_month"), :value => @end_year_month} %>
<%= f.hidden_field_tag :type, "statistic" %>
<%= f.submit t('submit'), class: 'btn btn-primary' %>
<% end %>
</form>
</div>
<style>
.timer_table{
max-height: 13em;
overflow-y: scroll;
}
hr.new_line{
margin: 1em 0;
border: 1px solid #333;
}
</style>
<div class="statistic_data">
<% all_count = @all_year_months_data.count %>
<% @all_year_months_data.each_with_index do |(year_month,total_seconds,all_infos), i| %>
<h4><%= year_month %></h4>
<h5><%= t("ruling_timer.total") %>: <%= RulingTimerTemp.transform_second_to_time(total_seconds) %></h5>
<div class="timer_table">
<table class="table main-list">
<thead>
<th><%= t("ruling_timer.date") %></th>
<th><%= t("ruling_timer.work_time") %></th>
</thead>
<tbody>
<% all_infos.each do |info| %>
<tr>
<td><%= info[:date] %></td>
<td><%= info[:work_time_str] %></td>
</tr>
<% end %>
</tbody>
</table>
</div>
<% if i < all_count - 1 %>
<hr class="new_line">
<% end %>
<% end %>
</div>

View File

@ -0,0 +1,78 @@
<table class="table main-list">
<thead>
<tr>
<th><%=t("ruling_timer.date")%></th>
<th><%=t("ruling_timer.summary")%></th>
<th><%=t("ruling_timer.work_time")%></th>
<th><%=t("action")%></th>
</tr>
</thead>
<tbody>
<% if @ruling_timer_temp %>
<%= render :partial => "timer_work_row", :locals=>{:timer=>@ruling_timer_temp} %>
<% end %>
<% @ruling_timer_history.each do |timer_history| %>
<%= render :partial => "timer_work_row", :locals=>{:timer=>timer_history} %>
<% end %>
</tbody>
</table>
<%=
content_tag :div, class: "bottomnav clearfix" do
content_tag(:div, paginate(@ruling_pager), class: "pagination pagination-centered") +
link_to(t("ruling_timer.add_history"),add_history_admin_ruling_timer_path(:id=>(params[:id].nil? ? "current_user" : params[:id])),:class=>"btn btn-primary pull-right")
end
%>
<script>
$('.delete_history').click(function(){
var item = $(this);
var item_row = $(this).parents("tr").eq(0);
var history_id = $(item).data("id");
var type = $(item).data("type");
if(window.confirm("<%=t("ruling_timer.delete_history_hint")%>")){
if(window.confirm("<%=t("ruling_timer.delete_history_hint")%>")){
if($("#dialog-confirm").length == 0){
$("#main-wrap").before("<div id='dialog-confirm' title='<%=t("ruling_timer.delete_task")%>'>"+
"<div style='clear:both;'></div><div id='info_texts'>"+'<%=t("ruling_timer.delete_history_hint1")%>'.replace('{{history}}',item.data('history-date'))+"</div>"+"<input id=\"confirm_input\"/ placeholder=\"<%= t('ruling_timer.please_input_confirm_delete') %>\" style=\"width: 17em;\">"+"<div id='msg_end' style='height:0px; overflow:hidden'></div>"+
"</div>");
}else{
$("#info_texts").html('<%=t("ruling_timer.delete_history_hint1")%>'.replace('{{history}}',item.data('history-date')));
$('#confirm_input').css('display','block');
$('#confirm_input').val('');
}
$( "#dialog-confirm" ).dialog({
resizable: true,
minHeight: 200,
maxHeight: 400,
modal: true,
width: '80%',
open: function(){
$('#confirm_input').blur();
},
close: function(){
$('#confirm_input').css('display','none');
$( this ).dialog( "close" );
},
buttons: {
"<%= t('ruling_timer.confirm') %>": function(){
var _this = $(this);
if($('#confirm_input').val().match(/<%= t('ruling_timer.confirm_delete') %>/gi)){
$('#confirm_input').css('display','none');
$.post(decodeURIComponent("<%=delete_history_admin_ruling_timer_path(:id=>"{{history_id}}")%>").replace("{{history_id}}",history_id),{'confirm_delete': true,'type': type}).done(function(data){
item_row.remove();
_this.dialog( "close" );
});
}else{
alert("<%= t('client_management.please_input_confirm_delete').html_safe %>");
$('#confirm_input').focus();
}
},
"<%= t('ruling_timer.cancel') %>": function(){
$('#confirm_input').css('display','none');
$( this ).dialog( "close" );
}
}
});
}
}
});
</script>

View File

@ -0,0 +1,155 @@
<% content_for :page_specific_css do %>
<%= stylesheet_link_tag "lib/main-forms" %>
<%= stylesheet_link_tag "lib/fileupload" %>
<%= stylesheet_link_tag "lib/main-list" %>
<%= stylesheet_link_tag "select2/select2" %>
<% end %>
<% content_for :page_specific_javascript do %>
<%= javascript_include_tag "select2/select2.min" %>
<% end %>
<style type="text/css">
.reach_limit{
background: #a90c0c;
color: white;
padding: 4px 12px;
font-family: 'Varela Round';
letter-spacing: -.4px;
cursor: default;
display: inline-block;
}
.add-on.btn-group .dropdown-menu{
margin: 0;
}
.main-forms fieldset .input-area .controls .input-prepend a:hover{
color: #fff;
}
.main-forms fieldset .input-area {
padding: 0;
}
</style>
<% content_for :page_specific_javascript do %>
<%= javascript_include_tag "lib/bootstrap-fileupload" %>
<%= javascript_include_tag "lib/bootstrap-datetimepicker" %>
<%= javascript_include_tag "lib/datetimepicker/datetimepicker.js" %>
<%= javascript_include_tag "lib/file-type" %>
<%= javascript_include_tag "lib/module-area" %>
<%= javascript_include_tag "form" %>
<% end %>
<div class="control-group">
<label class="control-label">*<%= t("ruling_timer.project_name") %></label>
<div class="controls">
<%= f.text_field :project_name, :required => "required" %>
</div>
</div>
<div class="control-group">
<label class="control-label muted"><%= t("ruling_timer.category") %></label>
<span class="input-area">
<div class="controls">
<%= select_category(f, @module_app) %>
</div>
</span>
</div>
<div class="control-group">
<label class="control-label muted"><%= t(:tags) %></label>
<span class="input-area">
<%= select_tags(f, @module_app) %>
</span>
</div>
<div class="control-group">
<label class="control-label muted"><%= t("ruling_timer.objectives") %></label>
<div class="controls">
<%= f.text_area :objectives %>
</div>
</div>
<%= render :partial => "form_opts", :locals => {:base_name=>"ruling_timer",:field_name=>"status",:f=>f} %>
<%= render :partial => "form_opts", :locals => {:base_name=>"ruling_timer",:field_name=>"privacy",:f=>f} %>
<div class="control-group">
<label class="control-label muted"><%= t("ruling_timer.project_manager") %></label>
<div class="controls">
<div id="project_manager_append_area" class="checkbox-card">
<%= @selected_project_managers.join("\n").html_safe %>
</div>
<div class="clearfix"></div>
<button id="add_user_btn" type="button" class="btn btn-primary" data-modal="#project_manager_modal"><%=t("ruling_timer.change")%></button>
</div>
</div>
<div class="control-group">
<label class="control-label muted"><%= t("ruling_timer.project_member") %></label>
<div class="controls">
<div id="all_user_append_area" class="checkbox-card">
<%= @selected_all_users.join("\n").html_safe %>
</div>
<div class="clearfix"></div>
<button id="add_user_btn" type="button" class="btn btn-primary" data-modal="#all_user_modal"><%=t("ruling_timer.add_member")%></button>
</div>
</div>
<!-- Form Actions -->
<div class="form-actions">
<%= f.hidden_field :is_hidden, :value => "0" %>
<%= f.hidden_field :is_private, :value => "0" %>
<%= f.hidden_field :time_offset, :id=>"task_timeoffset" %>
<% referer = request.referer rescue nil %>
<% referer = get_referer_url if referer.blank? || request.host != URI.parse(URI.encode(referer)).host %>
<%= f.submit t('submit'), class: 'btn btn-primary' %>
<input type="hidden" name="referer_url" value="<%= referer %>">
<%= link_to t('cancel'), referer, :class=>"btn" %>
</div>
<script>
if (!Array.prototype.includes) {
Object.defineProperty(Array.prototype, "includes", {
enumerable: false,
value: function(obj) {
var newArr = this.filter(function(el) {
return el == obj;
});
return newArr.length > 0;
}
});
}
function matchCustom(params, data) {
var all_options = this.element[0].all_options;
if(params == undefined || params.trim() == ''){
return all_options.includes(data);
}else{
return (data.toLowerCase().search(params.toLowerCase()) != -1)
}
}
var select2 = $(".select2_selection_box");
select2.each(function(i,v){
var select2_item = $(v);
var all_options = select2_item.find("option").map(function(j,option){
return $(option).text();
}).toArray();
select2_item[0].all_options = all_options;
select2_item.select2({
placeholder: select2_item.data("placeholder"),
matcher: matchCustom,
width: 'resolve'
});
})
window.timer_offset = (new Date().getTimezoneOffset() / -60).toString();
if(window.timer_offset[0] != "-"){
window.timer_offset = "+" + window.timer_offset;
}
$(document).ready(function(){
$(".slider").each(function(i,slider){
var $slider = $(slider);
var $slider_value = $slider.parent().find(".slider_value");
$slider.slider({
min: parseInt($slider.attr("min")),
max: parseInt($slider.attr("max")),
slide: function(e, ui) {
$slider_value.html(ui.value);
}
})
$slider.on("change",function(){
$slider_value.text($(this).val());
})
})
$("#hslider")
$("#task_timeoffset").val(window.timer_offset);
$("[data-modal]").click(function(){
$($(this).data("modal")).modal("show");
})
})
</script>

View File

@ -0,0 +1,7 @@
<% checked = false if defined?(checked).nil? %>
<li class="check-item <%=checked ? 'active' : ''%>">
<label>
<span class="user-name"><%= role.title rescue 'noname' %></span>
</label>
<%= check_box_tag "#{@role_params_name}", (role.id rescue nil) , (checked ? true : false) , :id => "role_ids_#{(role.id rescue '')}"%>
</li>

View File

@ -0,0 +1,61 @@
<%= stylesheet_link_tag "member_select" %>
<%= stylesheet_link_tag "lib/checkbox-card" %>
<style>
.checkbox-card li .member-pic {
float: left;
margin-right: 5px;
width: 40px;
height: 40px;
}
</style>
<div class="modal hide fade" tabindex="-1" role="dialog" id="role_modal">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title"><%=t(:role)%></h5>
</div>
<div class="modal-body">
<div class="member-filter-result nano">
<div>
<ul class="checkbox-card clearfix">
<% @selected_role_ids = @selected_role_ids.to_a %>
<% @selected_roles = [] %>
<% @roles.each do |role| %>
<% if @selected_role_ids.include?(role.id.to_s) %>
<% @selected_roles << render(:partial => "role_card",:locals=>{:role=>role,:checked=>true}) %>
<% else %>
<%= render :partial => "role_card",:locals=>{:role=>role} %>
<% end %>
<% end %>
</ul>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary save_button">Save changes</button>
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<script>
$(document).ready(function(){
function set_modal_max_height(){
var modal_body = $(this).find(".modal-body");
modal_body.css("max-height","auto");
var window_height = $(window).height();
var max_height = window_height - $(".modal-header").height() - $(".modal-footer").height() - 20 - window_height * 0.2 - 15 * 2;
modal_body.css("max-height",max_height);
}
$(".modal").on("shown.bs.modal",set_modal_max_height);
$(window).resize(function(){
set_modal_max_height.call($(".modal"));
})
$(".save_button").off("click").on("click",function(){
var modal = $(this).parents(".modal");
var li_selected = modal.find(".check-item input:checked").parents("li");
$("<%=@target%>").append(li_selected);
modal.modal('hide');
})
})
</script>

View File

@ -0,0 +1,18 @@
<% content_for :page_specific_css do %>
<%= stylesheet_link_tag "lib/main-forms" %>
<% end %>
<div class="control-group">
<label class="control-label">*<%= t("ruling_timer.section_name") %></label>
<div class="controls">
<%= f.text_field :section_name, :required => "required" %>
</div>
</div>
<!-- Form Actions -->
<div class="form-actions">
<%= f.hidden_field :ruling_timer_project_id %>
<% if request.xhr? %>
<%= link_to t('submit'), "#", class: 'btn btn-primary modal_link', "data-target" => "#section-form", style: "color: #fff;" %>
<% else %>
<%= f.submit t('submit'), class: 'btn btn-primary modal-link', "data-target" => "#section-form" %>
<% end %>
</div>

View File

@ -0,0 +1,10 @@
<% @sections.each_with_index do |section,i| %>
<tr>
<td class="section_name_td"><%=section.section_name%></td>
<td class="tasl_count_td"><%=section.task_count %></td>
<td>
<a href="#" data-href="<%=edit_section_admin_ruling_timer_path(:id=>section.id)%>" class="btn btn-primary modal_link"><%=t("ruling_timer.edit_section")%></a>
<a href="#" class="btn btn-danger delete_section" data-section="<%=section.section_name%>" data-section-id="<%=section.id.to_s%>"><%=t("ruling_timer.delete_section")%></a>
</td>
</tr>
<% end %>

View File

@ -1,21 +1,190 @@
<% @current_user = current_user %>
<% content_for :page_specific_css do %>
<%= stylesheet_link_tag "lib/main-forms" %>
<%= stylesheet_link_tag "lib/fileupload" %>
<%= stylesheet_link_tag "lib/main-list" %>
<%= stylesheet_link_tag "select2/select2" %>
<% end %>
<% content_for :page_specific_javascript do %>
<%= javascript_include_tag "select2/select2.min" %>
<% end %>
<style type="text/css">
.file_block{
display: inline-block;
}
.reach_limit{
background: #a90c0c;
color: white;
padding: 4px 12px;
font-family: 'Varela Round';
letter-spacing: -.4px;
cursor: default;
display: inline-block;
}
.add-on.btn-group .dropdown-menu{
margin: 0;
}
.main-forms fieldset .input-area .controls .input-prepend a:hover{
color: #fff;
}
.main-forms fieldset .input-area {
padding: 0;
}
.comment-block .content_textarea{
display: block;
width: 80%;
min-height: 100px;
}
.comment-block .content_textarea.hide{
display: none;
}
.comment-block .user_name{
font-size: 1.5em;
}
.comment-block p.content_text{
background-color: #fff;
border-radius: 3px;
box-shadow: 0 1px 2px -1px rgb(9 30 66 / 25%), 0 0 0 1px rgb(9 30 66 / 8%);
box-sizing: border-box;
padding: 0.5em;
overflow: hidden;
text-overflow: ellipsis;
max-width: 80%;
}
</style>
<% content_for :page_specific_javascript do %>
<%= javascript_include_tag "lib/bootstrap-fileupload" %>
<%= javascript_include_tag "lib/bootstrap-datetimepicker" %>
<%= javascript_include_tag "lib/datetimepicker/datetimepicker.js" %>
<%= javascript_include_tag "lib/file-type" %>
<%= javascript_include_tag "lib/module-area" %>
<%= javascript_include_tag "form" %>
<% end %>
<div class="control-group"> <div class="control-group">
<label class="control-label muted"><%= t("ruling_timer.task_name") %></label> <label class="control-label">*<%= t("ruling_timer.task_name") %></label>
<div class="controls"> <div class="controls">
<%= f.text_field :task_name %> <%= f.text_field :task_name, :required => "required" %>
</div> </div>
</div> </div>
<div class="control-group"> <div class="control-group">
<label class="control-label muted"><%= t("ruling_timer.task_performer") %></label> <label class="control-label muted"><%= t("ruling_timer.project") %></label>
<div class="controls"> <div class="controls">
<div id="user_append_area" class="checkbox-card"> <% if params[:project_id] %>
<%= @selected_users.join("\n").html_safe %> <%= @project.get_project_name %>
<% else %>
<%= f.select :ruling_timer_project_id, options_for_select(@projects.collect{|pj| [pj.get_project_name,(pj.new_record? ? "private_task" : pj.id.to_s)]},f.object.ruling_timer_project_id.to_s), {}, {:id=>"ruling_timer_project_id",:class=>"select2_selection_box"} %>
<% end %>
</div>
</div>
<div class="control-group">
<label class="control-label muted"><%= t("ruling_timer.sections") %></label>
<div class="controls">
<%= f.select :ruling_timer_section_id, options_for_select(@project.ruling_timer_sections.collect{|section| [section.section_name,section.id.to_s]},f.object.ruling_timer_section_id.to_s), {:include_blank => true}, {:id=>"ruling_timer_section_id",:class=>"select2_selection_box"} %>
</div>
</div>
<div class="control-group">
<label class="control-label muted"><%= t(:tags) %></label>
<span class="input-area">
<%= select_tags(f, @module_app) %>
</span>
</div>
<%= render :partial => "form_opts", :locals => {:base_name=>"ruling_timer",:field_name=>"privacy",:f=>f} %>
<%= render :partial => "form_opts", :locals => {:base_name=>"ruling_timer",:field_name=>"priority",:f=>f} %>
<div class="control-group">
<label class="control-label muted"><%= t("ruling_timer.owner") %></label>
<div class="controls">
<div id="owner_append_area" class="checkbox-card">
<%= @selected_owners.join("\n").html_safe %>
</div> </div>
<div class="clearfix"></div> <div class="clearfix"></div>
<button id="add_user_btn" type="button" class="btn btn-primary"><%=t("ruling_timer.add_member")%></button> <button id="add_user_btn" type="button" class="btn btn-primary" data-modal="#owner_modal"><%=t("ruling_timer.change")%></button>
</div>
</div>
<div class="control-group">
<label class="control-label muted"><%= t("ruling_timer.helper") %></label>
<div class="controls">
<div id="helper_append_area" class="checkbox-card">
<%= @selected_helpers.join("\n").html_safe %>
</div>
<div class="clearfix"></div>
<button id="add_user_btn" type="button" class="btn btn-primary" data-modal="#helper_modal"><%=t("ruling_timer.add_member")%></button>
</div>
</div>
<div class="control-group">
<label class="control-label muted"><%= t("ruling_timer.observer") %></label>
<div class="controls">
<div id="observer_append_area" class="checkbox-card">
<%= @selected_observers.join("\n").html_safe %>
</div>
<div class="clearfix"></div>
<button id="add_user_btn" type="button" class="btn btn-primary" data-modal="#observer_modal"><%=t("ruling_timer.add_member")%></button>
</div>
</div>
<div class="control-group">
<label class="control-label muted"><%= t("ruling_timer.progress") %></label>
<div class="controls">
<input type="range" min="0" max="100" value="<%=f.object.progress%>" class="slider" id="task_progress" name="<%=f.object_name%>[progress]"><span><span class="slider_value"><%=f.object.progress%></span>%</span>
</div>
</div>
<div class="control-group">
<label class="control-label muted"><%= t("ruling_timer.details") %></label>
<div class="controls">
<%= f.text_area :details, :class=>"ckeditor" %>
</div>
</div>
<!-- Comments -->
<div class="control-group">
<label class="control-label muted"><%= t("ruling_timer.comments") %></label>
<div class="controls">
<p class="add-btn">
<%= hidden_field_tag 'ruling_timer_comment_field_count', f.object.ruling_timer_comments.count %>
<a id="add_comment" class="trigger btn btn-small btn-primary"><i class="icons-plus"></i> <%= t(:add) %></a>
</p>
<!-- Add -->
<div class="add-target">
</div>
<!-- Exist -->
<% if f.object && !f.object.ruling_timer_comments.blank? %>
<div class="exist">
<% f.object.ruling_timer_comments.desc(:created_at).each_with_index do |ruling_timer_comment, i| %>
<%= f.fields_for :ruling_timer_comments, ruling_timer_comment do |f| %>
<%= render :partial => 'form_comment', :object => ruling_timer_comment, :locals => {:f => f, :i => i} %>
<% end %>
<% end %>
<hr>
</div>
<% end %>
</div>
</div>
<!-- File -->
<div class="control-group">
<label class="control-label muted"><%= t(:file_) %></label>
<div class="controls">
<!-- Exist -->
<% if f.object && !f.object.ruling_timer_files.blank? %>
<div class="exist">
<% f.object.ruling_timer_files.each_with_index do |ruling_timer_file, i| %>
<%= f.fields_for :ruling_timer_files, ruling_timer_file do |f| %>
<%= render :partial => 'form_file', :object => ruling_timer_file, :locals => {:f => f, :i => i} %>
<% end %>
<% end %>
<hr>
</div>
<% end %>
<!-- Add -->
<div class="add-target">
</div>
<p class="add-btn">
<%= hidden_field_tag 'ruling_timer_file_field_count', f.object.ruling_timer_files.count %>
<a id="add_file" class="trigger btn btn-small btn-primary"><i class="icons-plus"></i> <%= t(:add) %></a>
</p>
</div> </div>
</div> </div>
<!-- Form Actions --> <!-- Form Actions -->
<div class="form-actions"> <div class="form-actions">
<%= f.hidden_field :time_offset, :id=>"task_timeoffset" %>
<% referer = request.referer rescue nil %> <% referer = request.referer rescue nil %>
<% referer = get_referer_url if referer.blank? || request.host != URI.parse(URI.encode(referer)).host %> <% referer = get_referer_url if referer.blank? || request.host != URI.parse(URI.encode(referer)).host %>
<%= f.submit t('submit'), class: 'btn btn-primary' %> <%= f.submit t('submit'), class: 'btn btn-primary' %>
@ -23,9 +192,119 @@
<%= link_to t('cancel'), referer, :class=>"btn" %> <%= link_to t('cancel'), referer, :class=>"btn" %>
</div> </div>
<script> <script>
if (!Array.prototype.includes) {
Object.defineProperty(Array.prototype, "includes", {
enumerable: false,
value: function(obj) {
var newArr = this.filter(function(el) {
return el == obj;
});
return newArr.length > 0;
}
});
}
function matchCustom(params, data) {
var all_options = this.element[0].all_options;
if(params == undefined || params.trim() == ''){
return all_options.includes(data);
}else{
return (data.toLowerCase().search(params.toLowerCase()) != -1)
}
}
var select2 = $(".select2_selection_box");
var allow_section_relations = <%= @projects.map{|p| [p.id.to_s, p.ruling_timer_sections.map{|s| [s.section_name,s.id.to_s]}]}.to_h.to_json.html_safe %>;
select2.each(function(i,v){
var select2_item = $(v);
var all_options = [];
var all_values = [];
select2_item.find("option").each(function(j,option){
all_options.push($(option).text());
all_values.push($(option).val());
});
select2_item[0].all_options = all_options;
select2_item[0].all_values = all_values;
var custom_match = $(v).data("custom-match");
var match_func = (custom_match ? window[custom_match] : matchCustom );
select2_item.select2({
placeholder: select2_item.data("placeholder"),
matcher: match_func,
width: 'resolve'
});
})
$("#ruling_timer_project_id").on("change select2:select",function(){
$("#ruling_timer_section_id").empty();
var all_options = [""];
$("#ruling_timer_section_id").html(allow_section_relations[$(this).val()].map(function(v){
all_options.push(v[0]);
return (new Option(v[0],v[1])).outerHTML;
}).join("")).prepend("<option value=\"\"></option>");
var opts = $("#ruling_timer_section_id").data("select2").opts;
var custom_match = $(this).data("custom-match");
var match_func = (custom_match ? window[custom_match] : matchCustom );
$("#ruling_timer_section_id").select2("destroy").select2({
placeholder: $(this).data("placeholder"),
matcher: match_func,
width: 'resolve'
});
})
window.timer_offset = (new Date().getTimezoneOffset() / -60).toString();
if(window.timer_offset[0] != "-"){
window.timer_offset = "+" + window.timer_offset;
}
$(document).ready(function(){ $(document).ready(function(){
$("#add_user_btn").click(function(){ $(".slider").each(function(i,slider){
$("#user_modal").modal("show"); var $slider = $(slider);
var $slider_value = $slider.parent().find(".slider_value");
$slider.slider({
min: parseInt($slider.attr("min")),
max: parseInt($slider.attr("max")),
slide: function(e, ui) {
$slider_value.html(ui.value);
}
})
$slider.on("change",function(){
$slider_value.text($(this).val());
})
})
$("#task_timeoffset").val(window.timer_offset);
$("[data-modal]").click(function(){
$($(this).data("modal")).modal("show");
})
$(document).on('click', '#add_file', function(){
var new_id = $(this).prev().attr('value');
var old_id = new RegExp("new_ruling_timer_files", "g");
var on = $('.language-nav li.active').index();
var le = $(this).parent('.add-btn').prev('.add-target').children('.start-line').length;
$(this).prev().attr('value', parseInt(new_id) + 1);
$(this).parent().siblings('.add-target').append(("<%= escape_javascript(add_attribute 'form_file', f, :ruling_timer_files) %>").replace(old_id, new_id));
$(this).parent('.add-btn').prev('.add-target').children('.start-line').eq(le).children('.input-append').find('.tab-content').each(function() {
$(this).children('.tab-pane').eq(on).addClass('in active').siblings().removeClass('in active');
});
formTip();
});
$(document).on('click', '.delete_file, .delete_comment', function(){
$(this).parents('.input-prepend,.input-block').remove();
});
$(document).on('click', '.remove_existing_record', function(){
if(confirm("<%= I18n.t(:sure?)%>")){
$(this).children('.should_destroy').attr('value', 1);
$(this).parents('.start-line,.comment-block').hide();
}
});
$(document).on('click', '#add_comment', function(){
var new_id = $(this).prev().attr('value');
var old_id = new RegExp("new_ruling_timer_files", "g");
var on = $('.language-nav li.active').index();
var le = $(this).parent('.add-btn').prev('.add-target').children('.start-line').length;
$(this).prev().attr('value', parseInt(new_id) + 1);
$(this).parent().siblings('.add-target').prepend(("<%= escape_javascript(add_attribute 'form_comment', f, :ruling_timer_comments) %>").replace(old_id, new_id));
formTip();
});
$(".edit_comment").click(function(){
var comment_block = $(this).parents(".comment-block");
comment_block.find(".content_text").addClass("hide");
comment_block.find(".content_textarea").removeClass("hide").focus();
$(this).addClass("hide");
}) })
}) })
</script> </script>

View File

@ -0,0 +1,48 @@
<table class="table main-list">
<thead>
<tr>
<th><%= t("ruling_timer.ticket_no") %></th>
<th><%= t("ruling_timer.priority") %></th>
<th><%= t("ruling_timer.project") %></th>
<th><%= t("ruling_timer.task_name") %></th>
<th><%= t(:tags) %></th>
<th><%= t("ruling_timer.owner") %></th>
<th><%= t("ruling_timer.progress") %></th>
<th><%= t("ruling_timer.task_status") %></th>
<th><%=t("action")%></th>
</tr>
</thead>
<tbody>
<% tasks.each_with_index do |task,i| %>
<tr>
<td class="ticket_no_td"><%=task.ticket_no%></td>
<td class="priority_td"><%=task.get_priority%></td>
<td class="project_td"><%= task.ruling_timer_project.get_project_name rescue "" %></td>
<td class="task_name_td"><%=task.task_name%></td>
<td class="tags_td"></td>
<td class="owner_td"><%=task.owner.name rescue "" %></td>
<td class="progress_td"><%=task.progress%>%</td>
<td class="task_performer_td">
<% if task.user_ids %>
<% user_ids = task.user_ids
user_bson_ids = user_ids.map{|id| BSON::ObjectId(id)}
%>
<% users = User.find(user_ids).index_by(&:id).slice(*user_bson_ids).values %>
<% users.each do |user| %>
<% sub_task = task.ruling_timer_sub_tasks.where(:user=>user).first %>
<% next if sub_task.nil? %>
<% klass = (sub_task.status == 'working' ? 'fa-play' : (sub_task.status == 'finish' ? 'fa-flag-checkered' : 'fa-pause') rescue 'fa-pause') %>
<span><%=user.name%>( <i class="fa <%=klass%> <%=sub_task.status%>_label"></i> <%=sub_task.get_infos["work"] rescue "00:00:00" %> )</span>
<br>
<% end %>
<% end %>
</td>
<td>
<a href="<%=view_task_admin_ruling_timer_path(:id=>task.id)%>" class="btn btn-primary"><%=t("view")%></a>
<a href="<%=edit_task_admin_ruling_timer_path(:id=>task.id,:project_id=>(params[:id].present? ? params[:id] : nil)) %>" class="btn btn-primary"><%=t("edit")%></a>
<a href="#" class="btn btn-danger delete_task" data-task="<%=task.task_name%>" data-task-id="<%=task.id.to_s%>"><%=t("ruling_timer.delete_task")%></a>
</td>
</tr>
<% end %>
</tbody>
</table>

View File

@ -8,7 +8,7 @@
height: 40px; height: 40px;
} }
</style> </style>
<div class="modal hide fade" tabindex="-1" role="dialog" id="user_modal"> <div class="modal hide fade" tabindex="-1" role="dialog" id="<%=name%>_modal">
<div class="modal-dialog" role="document"> <div class="modal-dialog" role="document">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
@ -18,11 +18,13 @@
<div class="member-filter-result nano"> <div class="member-filter-result nano">
<div> <div>
<ul class="checkbox-card clearfix"> <ul class="checkbox-card clearfix">
<% @selected_user_ids = @selected_user_ids.to_a %> <% @selected_user_ids = @selected_user_ids.to_a.map{|id| id.to_s} %>
<% @selected_users = [] %> <% instance_variable_set("@selected_#{name}s",[]) %>
<% tmp = instance_variable_get("@selected_#{name}s") %>
<% @users.each do |user| %> <% @users.each do |user| %>
<% if @selected_user_ids.include?(user.id.to_s) %> <% if @selected_user_ids.include?(user.id.to_s) %>
<% @selected_users << render(:partial => "user_card",:locals=>{:user=>user,:checked=>true}) %> <% tmp << render(:partial => "user_card",:locals=>{:user=>user,:checked=>true}) %>
<%= tmp[-1] if @display_selected %>
<% else %> <% else %>
<%= render :partial => "user_card",:locals=>{:user=>user} %> <%= render :partial => "user_card",:locals=>{:user=>user} %>
<% end %> <% end %>
@ -47,14 +49,31 @@
var max_height = window_height - $(".modal-header").height() - $(".modal-footer").height() - 20 - window_height * 0.2 - 15 * 2; var max_height = window_height - $(".modal-header").height() - $(".modal-footer").height() - 20 - window_height * 0.2 - 15 * 2;
modal_body.css("max-height",max_height); modal_body.css("max-height",max_height);
} }
$(".modal").on("shown.bs.modal",set_modal_max_height); $("#<%=name%>_modal").on("shown.bs.modal",set_modal_max_height);
$(window).resize(function(){ $(window).resize(function(){
set_modal_max_height.call($(".modal")); set_modal_max_height.call($(".modal"));
}) })
$(".save_button").off("click").on("click",function(){ $("#<%=name%>_modal .save_button").off("click").on("click",function(){
var modal = $(this).parents(".modal"); var modal = $(this).parents(".modal");
var li_selected = modal.find(".check-item input:checked").parents("li"); var li_selected = modal.find(".check-item input:checked").parents("li");
<% if @user_params_name[-2..-1] != "[]" %>
if(li_selected.length > 1){
alert("<%=t('ruling_timer.can_only_choose_one')%>");
li_selected[0].scrollIntoView()
return false;
}
<% if @must_select %>
else if(li_selected.length == 0){
alert("<%=t('ruling_timer.must_select_one')%>");
}
<% end %>
<% end %>
<% if @display_selected %>
$("<%=@target%>").empty();
$("<%=@target%>").append(li_selected.clone());
<% else %>
$("<%=@target%>").append(li_selected); $("<%=@target%>").append(li_selected);
<% end %>
modal.modal('hide'); modal.modal('hide');
}) })
}) })

View File

@ -0,0 +1,7 @@
<%= render :partial => "user_select_modal",:locals=>{:@users=>@users,:@selected_user_ids=>[@project.project_manager_id],:@target=>"#project_manager_append_area",:@user_params_name=>"#{@project.class.to_s.underscore}[project_manager_id]",:name => "project_manager",:@display_selected => true } %>
<%= render :partial => "user_select_modal",:locals=>{:@users=>@users,:@selected_user_ids=>@project.all_user_ids,:@target=>"#all_user_append_area",:@user_params_name=>"#{@project.class.to_s.underscore}[all_user_ids][]",:name => "all_user" } %>
<%= form_for @project, url: create_project_admin_ruling_timers_path(@project), html: {class: "form-horizontal main-forms previewable"} do |f| %>
<fieldset>
<%= render :partial => "project_form",:locals=>{:f=>f} %>
</fieldset>
<% end %>

View File

@ -0,0 +1,5 @@
<%= form_for @section, url: create_section_admin_ruling_timers_path(@section), html: {class: "form-horizontal main-forms previewable", id: "section-form"} do |f| %>
<fieldset>
<%= render :partial => "section_form",:locals=>{:f=>f} %>
</fieldset>
<% end %>

View File

@ -1,4 +1,6 @@
<%= render :partial => "user_select_modal",:locals=>{:@users=>@users,:@selected_user_ids=>@task.user_ids,:@target=>"#user_append_area",:@user_params_name=>"#{@task.class.to_s.underscore}[user_ids][]"} %> <%= render :partial => "user_select_modal",:locals=>{:@users=>@users,:@selected_user_ids=>[@task.owner_id],:@target=>"#owner_append_area",:@user_params_name=>"#{@task.class.to_s.underscore}[owner_id]",:name => "owner",:@display_selected => true } %>
<%= render :partial => "user_select_modal",:locals=>{:@users=>@users,:@selected_user_ids=>@task.helper_ids,:@target=>"#helper_append_area",:@user_params_name=>"#{@task.class.to_s.underscore}[helper_ids][]",:name => "helper" } %>
<%= render :partial => "user_select_modal",:locals=>{:@users=>@users,:@selected_user_ids=>@task.observer_ids,:@target=>"#observer_append_area",:@user_params_name=>"#{@task.class.to_s.underscore}[observer_ids][]",:name => "observer" } %>
<%= form_for @task, url: create_task_admin_ruling_timers_path(@task), html: {class: "form-horizontal main-forms previewable"} do |f| %> <%= form_for @task, url: create_task_admin_ruling_timers_path(@task), html: {class: "form-horizontal main-forms previewable"} do |f| %>
<fieldset> <fieldset>
<%= render :partial => "task_form",:locals=>{:f=>f} %> <%= render :partial => "task_form",:locals=>{:f=>f} %>

View File

@ -0,0 +1,7 @@
<%= render :partial => "user_select_modal",:locals=>{:@users=>@users,:@selected_user_ids=>[@project.project_manager_id],:@target=>"#project_manager_append_area",:@user_params_name=>"#{@project.class.to_s.underscore}[project_manager_id]",:name => "project_manager",:@display_selected => true } %>
<%= render :partial => "user_select_modal",:locals=>{:@users=>@users,:@selected_user_ids=>@project.all_user_ids,:@target=>"#all_user_append_area",:@user_params_name=>"#{@project.class.to_s.underscore}[all_user_ids][]",:name => "all_user" } %>
<%= form_for @project, url: update_project_admin_ruling_timer_path(@project), html: {class: "form-horizontal main-forms previewable"} do |f| %>
<fieldset>
<%= render :partial => "project_form",:locals=>{:f=>f} %>
</fieldset>
<% end %>

View File

@ -0,0 +1,5 @@
<%= form_for @section, url: update_section_admin_ruling_timer_path(@section), html: {class: "form-horizontal main-forms previewable", id: "section-form"} do |f| %>
<fieldset>
<%= render :partial => "section_form",:locals=>{:f=>f} %>
</fieldset>
<% end %>

View File

@ -1,4 +1,6 @@
<%= render :partial => "user_select_modal",:locals=>{:@users=>@users,:@selected_user_ids=>@task.user_ids,:@target=>"#user_append_area",:@user_params_name=>"#{@task.class.to_s.underscore}[user_ids][]"} %> <%= render :partial => "user_select_modal",:locals=>{:@users=>@users,:@selected_user_ids=>[@task.owner_id],:@target=>"#owner_append_area",:@user_params_name=>"#{@task.class.to_s.underscore}[owner_id]",:name => "owner",:@display_selected => true } %>
<%= render :partial => "user_select_modal",:locals=>{:@users=>@users,:@selected_user_ids=>@task.helper_ids,:@target=>"#helper_append_area",:@user_params_name=>"#{@task.class.to_s.underscore}[helper_ids][]",:name => "helper" } %>
<%= render :partial => "user_select_modal",:locals=>{:@users=>@users,:@selected_user_ids=>@task.observer_ids,:@target=>"#observer_append_area",:@user_params_name=>"#{@task.class.to_s.underscore}[observer_ids][]",:name => "observer" } %>
<%= form_for @task, url: update_task_admin_ruling_timer_path(@task), html: {class: "form-horizontal main-forms previewable"} do |f| %> <%= form_for @task, url: update_task_admin_ruling_timer_path(@task), html: {class: "form-horizontal main-forms previewable"} do |f| %>
<fieldset> <fieldset>
<%= render :partial => "task_form",:locals=>{:f=>f} %> <%= render :partial => "task_form",:locals=>{:f=>f} %>

View File

@ -5,81 +5,12 @@
<%=t("ruling_timer.my_work_history")%> <%=t("ruling_timer.my_work_history")%>
<% end %> <% end %>
</h3> </h3>
<table class="table main-list"> <div class="pull-right">
<thead> <a href="?type=" class="btn <%= 'active' if params[:type] != 'statistic' %>"><%=t("ruling_timer.list")%></a>
<tr> <a href="?type=statistic" class="btn <%= 'active' if params[:type] == 'statistic' %>"><%=t("ruling_timer.statistic")%></a>
<th><%=t("ruling_timer.date")%></th> </div>
<th><%=t("ruling_timer.summary")%></th> <% if params[:type] != "statistic" %>
<th><%=t("ruling_timer.work_time")%></th> <%= render :partial => "index_table" %>
<th><%=t("action")%></th> <% else %>
</tr> <%= render :partial => "index_statistic" %>
</thead> <% end %>
<tbody>
<% if @ruling_timer_temp %>
<%= render :partial => "timer_work_row", :locals=>{:timer=>@ruling_timer_temp} %>
<% end %>
<% @ruling_timer_history.each do |timer_history| %>
<%= render :partial => "timer_work_row", :locals=>{:timer=>timer_history} %>
<% end %>
</tbody>
</table>
<%=
content_tag :div, class: "bottomnav clearfix" do
content_tag(:div, paginate(@ruling_pager), class: "pagination pagination-centered") +
link_to(t("ruling_timer.add_history"),add_history_admin_ruling_timer_path(:id=>(params[:id].nil? ? "current_user" : params[:id])),:class=>"btn btn-primary pull-right")
end
%>
<script>
$('.delete_history').click(function(){
var item = $(this);
var item_row = $(this).parents("tr").eq(0);
var history_id = $(item).data("id");
var type = $(item).data("type");
if(window.confirm("<%=t("ruling_timer.delete_history_hint")%>")){
if(window.confirm("<%=t("ruling_timer.delete_history_hint")%>")){
if($("#dialog-confirm").length == 0){
$("#main-wrap").before("<div id='dialog-confirm' title='<%=t("ruling_timer.delete_task")%>'>"+
"<div style='clear:both;'></div><div id='info_texts'>"+'<%=t("ruling_timer.delete_history_hint1")%>'.replace('{{history}}',item.data('history-date'))+"</div>"+"<input id=\"confirm_input\"/ placeholder=\"<%= t('ruling_timer.please_input_confirm_delete') %>\" style=\"width: 17em;\">"+"<div id='msg_end' style='height:0px; overflow:hidden'></div>"+
"</div>");
}else{
$("#info_texts").html('<%=t("ruling_timer.delete_history_hint1")%>'.replace('{{history}}',item.data('history-date')));
$('#confirm_input').css('display','block');
$('#confirm_input').val('');
}
$( "#dialog-confirm" ).dialog({
resizable: true,
minHeight: 200,
maxHeight: 400,
modal: true,
width: '80%',
open: function(){
$('#confirm_input').blur();
},
close: function(){
$('#confirm_input').css('display','none');
$( this ).dialog( "close" );
},
buttons: {
"<%= t('ruling_timer.confirm') %>": function(){
var _this = $(this);
if($('#confirm_input').val().match(/<%= t('ruling_timer.confirm_delete') %>/gi)){
$('#confirm_input').css('display','none');
$.post(decodeURIComponent("<%=delete_history_admin_ruling_timer_path(:id=>"{{history_id}}")%>").replace("{{history_id}}",history_id),{'confirm_delete': true,'type': type}).done(function(data){
item_row.remove();
_this.dialog( "close" );
});
}else{
alert("<%= t('client_management.please_input_confirm_delete').html_safe %>");
$('#confirm_input').focus();
}
},
"<%= t('ruling_timer.cancel') %>": function(){
$('#confirm_input').css('display','none');
$( this ).dialog( "close" );
}
}
});
}
}
});
</script>

View File

@ -0,0 +1,130 @@
<style type="text/css">
.span20-percent{
width: 20%;
float: left;
text-align: center;
font-size: 1.5em;
margin: 0;
border: 2px solid #333333;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
.span20-percent img{
width: 100%;
}
.status-tags > *{
font-weight: bold;
}
.task_name{
font-size: 1.5em;
}
.working_label{
color: #5a5a5a;
font-weight: bold;
}
.finish_label{
color: #3ec700;
font-weight: bold;
}
.stop_label{
color: #cd1643;
font-weight: bold;
}
</style>
<h3><%= t("ruling_timer.projects") %></h3>
<table class="table main-list">
<thead>
<tr>
<th><%= t("ruling_timer.project_no") %></th>
<th><%= t("ruling_timer.project_name") %></th>
<th><%= t("ruling_timer.status") %></th>
<th><%= t("ruling_timer.project_manager") %></th>
<th><%= t("ruling_timer.project_member") %></th>
<th><%= t("ruling_timer.task_count") %></th>
<th><%=t("action")%></th>
</tr>
</thead>
<tbody>
<% @projects.each_with_index do |project,i| %>
<tr>
<td class="project_no_td"><%=project.project_no%></td>
<td class="project_name_td"><%=project.get_project_name%></td>
<td class="status_td"><%=project.get_status %></td>
<td class="project_manager_td"><%=project.project_manager.name rescue "" %></td>
<td class="all_user_ids_td"><%=project.all_user_ids.count %></td>
<td class="task_count_td">
<%= project.task_count %>
</td>
<td>
<a href="<%=view_project_admin_ruling_timer_path(:id=>project.id)%>" class="btn btn-primary"><%=t("view")%></a>
<a href="<%=section_management_admin_ruling_timer_path(:id=>project.id)%>" class="btn btn-primary"><%=t("ruling_timer.section_management")%></a>
<% unless project.is_private %>
<a href="<%=edit_project_admin_ruling_timer_path(:id=>project.id)%>" class="btn btn-primary"><%=t("edit")%></a>
<a href="#" class="btn btn-danger delete_project" data-project="<%=project.get_project_name%>" data-project-id="<%=project.id.to_s%>"><%=t("ruling_timer.delete_project")%></a>
<% end %>
</td>
</tr>
<% end %>
</tbody>
</table>
<%=
content_tag :div, class: "bottomnav clearfix" do
content_tag(:div, paginate(@projects), class: "pagination pagination-centered") +
content_tag(:div, link_to(t("ruling_timer.add_project"), add_project_admin_ruling_timers_path, :class=>"btn btn-primary"), class: "pull-right")
end %>
<script>
<% ["working","stop","finish"].each do |status| %>
$(".<%=status%>_label").attr("title",'<%=t("ruling_timer.#{status}")%>');
<% end %>
$('.delete_project').click(function(){
var item = $(this);
var item_row = $(this).parents("tr").eq(0);
var project_id = $(item).data("project-id")
if(window.confirm("<%=t("ruling_timer.delete_project_hint")%>")){
if(window.confirm("<%=t("ruling_timer.delete_project_hint")%>")){
if($("#dialog-confirm").length == 0){
$("#main-wrap").before("<div id='dialog-confirm' title='<%=t("ruling_timer.delete_task")%>'>"+
"<div style='clear:both;'></div><div id='info_texts'>"+'<%=t("ruling_timer.delete_project_hint1")%>'.replace('{{project}}',item.data('project'))+"</div>"+"<input id=\"confirm_input\"/ placeholder=\"<%= t('ruling_timer.please_input_confirm_delete') %>\" style=\"width: 17em;\">"+"<div id='msg_end' style='height:0px; overflow:hidden'></div>"+
"</div>");
}else{
$("#info_texts").html('<%=t("ruling_timer.delete_project_hint1")%>'.replace('{{project}}',item.data('project')));
$('#confirm_input').css('display','block');
$('#confirm_input').val('');
}
$( "#dialog-confirm" ).dialog({
resizable: true,
minHeight: 200,
maxHeight: 400,
modal: true,
width: '80%',
open: function(){
$('#confirm_input').blur();
},
close: function(){
$('#confirm_input').css('display','none');
$( this ).dialog( "close" );
},
buttons: {
"<%= t('ruling_timer.confirm') %>": function(){
var _this = $(this);
if($('#confirm_input').val().match(/<%= t('ruling_timer.confirm_delete') %>/gi)){
$('#confirm_input').css('display','none');
$.post(decodeURIComponent("<%=delete_project_admin_ruling_timer_path(:id=>"{{project_id}}")%>").replace("{{project_id}}",project_id),{'confirm_delete': true}).done(function(data){
item_row.remove();
_this.dialog( "close" );
});
}else{
alert("<%= t('ruling_timer.please_input_confirm_delete').html_safe %>");
$('#confirm_input').focus();
}
},
"<%= t('ruling_timer.cancel') %>": function(){
$('#confirm_input').css('display','none');
$( this ).dialog( "close" );
}
}
});
}
}
});
</script>

View File

@ -0,0 +1,157 @@
<style type="text/css">
.span20-percent{
width: 20%;
float: left;
text-align: center;
font-size: 1.5em;
margin: 0;
border: 2px solid #333333;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
.span20-percent img{
width: 100%;
}
.status-tags > *{
font-weight: bold;
}
.task_name{
font-size: 1.5em;
}
.working_label{
color: #5a5a5a;
font-weight: bold;
}
.finish_label{
color: #3ec700;
font-weight: bold;
}
.stop_label{
color: #cd1643;
font-weight: bold;
}
</style>
<div id="dialog-modal" title="">
<div id="modal_body_content"></div>
</div>
<h3><%= @project.get_project_name %>-<%=t("ruling_timer.section_management")%></h3>
<table class="table main-list">
<thead>
<tr>
<th><%= t("ruling_timer.section_name") %></th>
<th><%= t("ruling_timer.task_count") %></th>
<th><%=t("action")%></th>
</tr>
</thead>
<tbody id="sections_content">
<%= render :partial => "sections_content" %>
</tbody>
</table>
<%=
content_tag :div, class: "bottomnav clearfix" do
content_tag(:div, paginate(@sections), class: "pagination pagination-centered") +
content_tag(:div, link_to(t("ruling_timer.add_section"), "#" , :class=>"btn btn-primary modal_link", "data-href" => add_section_admin_ruling_timer_path(@project) ), class: "pull-right")
end %>
<script>
$(document).on("click",'.modal_link',function(){
$(this).attr("disabled","disabled");
var link = $(this).data("href");
var method = ($(this).data("method") || "get").toUpperCase();
var target = $(this).data("target");
var data = null;
$("#dialog-modal").attr("title",$(this).text());
if(target){
var $target = $(target);
if($target.is("form")){
link = $target.attr("action");
method = $target.attr("method");
data = $target.serializeArray()
}
}
$.ajax({
url: link,
method: method,
data: data,
async: false
}).done(function(data){
if(typeof(data) == "object"){
if(data["success"]){
$.get(window.location.href).done(function(html){
$("#sections_content").html(html);
$("#dialog-modal").dialog( "close" )
})
}
}else{
$("#modal_body_content").html(data);
$( "#dialog-modal" ).dialog({
resizable: true,
minHeight: 200,
maxHeight: 400,
modal: true,
width: '80%',
close: function(){
$( this ).dialog( "close" );
},
buttons: {
"<%= t('ruling_timer.close') %>": function(){
$( this ).dialog( "close" );
}
}
});
}
})
$(this).attr("disabled","");
return false;
})
$(document).on("click",'.delete_section',function(){
var item = $(this);
var item_row = $(this).parents("tr").eq(0);
var section_id = $(item).data("section-id")
if(window.confirm("<%=t("ruling_timer.delete_section_hint")%>")){
if(window.confirm("<%=t("ruling_timer.delete_section_hint")%>")){
if($("#dialog-confirm").length == 0){
$("#main-wrap").before("<div id='dialog-confirm' title='<%=t("ruling_timer.delete_task")%>'>"+
"<div style='clear:both;'></div><div id='info_texts'>"+'<%=t("ruling_timer.delete_section_hint1")%>'.replace('{{section}}',item.data('section'))+"</div>"+"<input id=\"confirm_input\"/ placeholder=\"<%= t('ruling_timer.please_input_confirm_delete') %>\" style=\"width: 17em;\">"+"<div id='msg_end' style='height:0px; overflow:hidden'></div>"+
"</div>");
}else{
$("#info_texts").html('<%=t("ruling_timer.delete_section_hint1")%>'.replace('{{section}}',item.data('section')));
$('#confirm_input').css('display','block');
$('#confirm_input').val('');
}
$( "#dialog-confirm" ).dialog({
resizable: true,
minHeight: 200,
maxHeight: 400,
modal: true,
width: '80%',
open: function(){
$('#confirm_input').blur();
},
close: function(){
$('#confirm_input').css('display','none');
$( this ).dialog( "close" );
},
buttons: {
"<%= t('ruling_timer.confirm') %>": function(){
var _this = $(this);
if($('#confirm_input').val().match(/<%= t('ruling_timer.confirm_delete') %>/gi)){
$('#confirm_input').css('display','none');
$.post(decodeURIComponent("<%=delete_section_admin_ruling_timer_path(:id=>"{{section_id}}")%>").replace("{{section_id}}",section_id),{'confirm_delete': true}).done(function(data){
item_row.remove();
_this.dialog( "close" );
});
}else{
alert("<%= t('ruling_timer.please_input_confirm_delete').html_safe %>");
$('#confirm_input').focus();
}
},
"<%= t('ruling_timer.cancel') %>": function(){
$('#confirm_input').css('display','none');
$( this ).dialog( "close" );
}
}
});
}
}
});
</script>

View File

@ -0,0 +1,26 @@
<%= render :partial => "role_select_modal",:locals=>{:@users=>@users,:@selected_role_ids=>@setting.role_ids,:@target=>"#role_append_area",:@role_params_name=>"#{@setting.class.to_s.underscore}[role_ids][]"} %>
<%= form_for @setting, url: update_setting_admin_ruling_timers_path(@ruling_weather_setting), html: {class: "form-horizontal main-forms previewable"} do |f| %>
<fieldset>
<div class="control-group">
<label class="control-label muted" for="authorized_user_role"><%= t("ruling_timer.authorized_user_role") %></label>
<div class="controls">
<div id="role_append_area" class="checkbox-card">
<%= @selected_roles.join("\n").html_safe %>
</div>
<div class="clearfix"></div>
<button id="add_role_btn" type="button" class="btn btn-primary"><%=t("ruling_timer.add_role")%></button>
</div>
</div>
<!-- Form Actions -->
<div class="form-actions">
<%= f.submit t('ruling_timer.save'), class: 'btn btn-primary' %>
</div>
</fieldset>
<% end %>
<script>
$(document).ready(function(){
$("#add_role_btn").click(function(){
$("#role_modal").modal("show");
})
})
</script>

View File

@ -30,47 +30,47 @@
color: #cd1643; color: #cd1643;
font-weight: bold; font-weight: bold;
} }
.black_hr{
border-color: #333333;
}
.types_link{
margin-bottom: 0.5em;
}
</style> </style>
<h3><%= t("ruling_timer.task") %></h3> <h3><%= "#{@project.get_project_name} - " if @project%><%= t("ruling_timer.task") %></h3>
<table class="table main-list"> <div class="pull-right types_link">
<thead> <%if @is_manager
<tr> all_types = ["all_tasks","my_tasks","my_assisted_tasks","my_observed_tasks"]
<th><%= t("ruling_timer.task_name") %></th> else
<th><%= t("ruling_timer.task_performer") %></th> all_types = ["my_tasks","my_assisted_tasks","my_observed_tasks"]
<th><%=t("action")%></th> end
</tr> %>
</thead> <% all_types.each_with_index do |type,i| %>
<tbody> <% active = false %>
<% @tasks.each_with_index do |task,i| %> <% if i == 0 && !(all_types.include?(params[:type]))
<tr> active = true
<td class="task_name"><%=task.task_name%></td> else
<td class="task_performer"> active = (type == params[:type])
<% if task.user_ids %> end
<% user_ids = task.user_ids %>
user_bson_ids = user_ids.map{|id| BSON::ObjectId(id)} <a href="?type=<%=type%>" class="btn <%= 'active' if active %>"><%=t("ruling_timer.#{type}")%></a>
%> <% end %>
<% users = User.find(user_ids).index_by(&:id).slice(*user_bson_ids).values %> </div>
<% users.each do |user| %> <% if params[:id].present? %>
<% sub_task = task.ruling_timer_sub_tasks.where(:user=>user).first %> <% task_group = @tasks.group_by(&:ruling_timer_section_id) %>
<% klass = (sub_task.status == 'working' ? 'fa-play' : (sub_task.status == 'finish' ? 'fa-flag-checkered' : 'fa-pause') rescue 'fa-pause') %> <% task_group_count = task_group.count %>
<span><%=user.name%>( <i class="fa <%=klass%> <%=sub_task.status%>_label"></i> <%=sub_task.get_infos["work"] rescue "00:00:00" %> )</span> <% task_group.each_with_index do |(ruling_timer_section_id, tasks), i| %>
<br> <h4><%= tasks[0].ruling_timer_section.section_name rescue I18n.t("ruling_timer.none") %></h4>
<% end %> <%= render :partial => "task_table", :locals =>{:tasks => tasks} %>
<% end %> <%= '<hr class="black_hr">'.html_safe if i < task_group_count - 1 %>
</td> <% end %>
<td> <% else %>
<a href="<%=view_task_admin_ruling_timer_path(:id=>task.id)%>" class="btn btn-primary"><%=t("view")%></a> <%= render :partial => "task_table", :locals =>{:tasks => @tasks} %>
<a href="<%=edit_task_admin_ruling_timer_path(:id=>task.id)%>" class="btn btn-primary"><%=t("edit")%></a> <% end %>
<a href="#" class="btn btn-danger delete_task" data-task="<%=task.task_name%>" data-task-id="<%=task.id.to_s%>"><%=t("ruling_timer.delete_task")%></a>
</td>
</tr>
<% end %>
</tbody>
</table>
<%= <%=
content_tag :div, class: "bottomnav clearfix" do content_tag :div, class: "bottomnav clearfix" do
content_tag(:div, paginate(@tasks), class: "pagination pagination-centered") + content_tag(:div, paginate(@tasks), class: "pagination pagination-centered") +
content_tag(:div, link_to(t("ruling_timer.add_task"), add_task_admin_ruling_timers_path, :class=>"btn btn-primary"), class: "pull-right") content_tag(:div, link_to(t("ruling_timer.add_task"), add_task_admin_ruling_timers_path + (@project ? "?project_id=#{@project.id}" : ""), :class=>"btn btn-primary"), class: "pull-right")
end %> end %>
<script> <script>
<% ["working","stop","finish"].each do |status| %> <% ["working","stop","finish"].each do |status| %>
@ -114,7 +114,7 @@
_this.dialog( "close" ); _this.dialog( "close" );
}); });
}else{ }else{
alert("<%= t('client_management.please_input_confirm_delete').html_safe %>"); alert("<%= t('ruling_timer.please_input_confirm_delete').html_safe %>");
$('#confirm_input').focus(); $('#confirm_input').focus();
} }
}, },

View File

@ -1,3 +1,9 @@
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<style type="text/css">
.markdown_text:not(.ready){
display: none;
}
</style>
<style type="text/css"> <style type="text/css">
.working_label{ .working_label{
color: #5a5a5a; color: #5a5a5a;
@ -11,9 +17,116 @@
color: #cd1643; color: #cd1643;
font-weight: bold; font-weight: bold;
} }
.details_block {
font-size: 1.3em;
}
.main_fields{
font-size: 1.3em;
}
.custom_hr{
border-top: 0.125em solid #000000;
margin: 1em 0;
border-bottom: 0;
}
</style> </style>
<fieldset> <style>
.comment-block .content_textarea{
display: block;
width: 80%;
min-height: 100px;
}
.comment-block .content_textarea.hide{
display: none;
}
.comment-block .user_name{
font-size: 1.5em;
}
.comment-block p.content_text{
background-color: #fff;
border-radius: 3px;
box-shadow: 0 1px 2px -1px rgb(9 30 66 / 25%), 0 0 0 1px rgb(9 30 66 / 8%);
box-sizing: border-box;
padding: 0.5em;
overflow: hidden;
text-overflow: ellipsis;
max-width: 80%;
}
.file_block .preview_img{
position: relative;
color: #fff;
min-height: 3em;
background: gray;
}
.file_block .file_image{
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%,-50%);
}
.file_block .controls{
padding-left: 1em;
}
</style>
<%= fields_for :task, @task do |f| %>
<fieldset class="form-horizontal main_fields">
<legend><h3><%= @task.task_name%></h3></legend> <legend><h3><%= @task.task_name%></h3></legend>
<h4><%= t("ruling_timer.details") %></h4>
<div class="details_block">
<%= @task.details.html_safe %>
</div>
<hr class="custom_hr">
<div class="control-group">
<span><%= t("ruling_timer.progress") %>: <%= @task.progress %> %</span>
</div>
<hr class="custom_hr">
<h4><%= t(:file_) %></h4>
<div class="file_blocks">
<% @task.ruling_timer_files.desc(:updated_at).each do |file| %>
<% file_url = file.file.url rescue "" %>
<% next if file_url.blank? %>
<% file_name = File.basename(file_url) %>
<% ext_name = File.extname(file_name).split(".").last %>
<% ext_name = "File" if ext_name.blank? %>
<div class="file_block control-group">
<div class="control-label preview_img">
<% if ext_name.match(/(png|jpg|gif)/i) %>
<img alt="<%= file_name %>" title="<%= file_name %>" src="<%= file_url %>">
<% else %>
<div class="file_image"><%=ext_name.upcase%></div>
<% end %>
</div>
<div class="controls">
<div><a title="<%= file_name %>" href="<%=file_url%>" target="_blank" class="file_name"><%= file_name %></a></div>
<div class="description"><%=file.description%></div>
<div><span class="updated_at"><%=file.display_updated_at%></span>( <span class="user_name"><%=file.uploader.name%></span> )</div>
</div>
</div>
<% end %>
</div>
<hr class="custom_hr">
<h4><%= t("ruling_timer.comments_text") %></h4>
<div class="comments_blocks">
<p class="add-btn">
<%= hidden_field_tag 'ruling_timer_comment_field_count', f.object.ruling_timer_comments.count %>
<a id="add_comment" class="trigger btn btn-small btn-primary"><i class="icons-plus"></i> <%= t(:add) %></a>
</p>
<!-- Add -->
<div class="add-target">
</div>
<!-- Exist -->
<% if f.object && !f.object.ruling_timer_comments.blank? %>
<div class="exist">
<% f.object.ruling_timer_comments.desc(:created_at).each_with_index do |ruling_timer_comment, i| %>
<%= f.fields_for :ruling_timer_comments, ruling_timer_comment do |f| %>
<%= render :partial => 'form_comment', :object => ruling_timer_comment, :locals => {:f => f, :i => i} %>
<% end %>
<% end %>
<hr>
</div>
<% end %>
</div>
<hr class="custom_hr">
<div class="field_body"> <div class="field_body">
<table class="table main-list"> <table class="table main-list">
<thead> <thead>
@ -57,6 +170,7 @@
content_tag :div, class: "bottomnav clearfix" do content_tag :div, class: "bottomnav clearfix" do
referer = request.referer rescue nil referer = request.referer rescue nil
referer = task_management_admin_ruling_timers_path if referer.blank? || request.host != URI.parse(URI.encode(referer)).host referer = task_management_admin_ruling_timers_path if referer.blank? || request.host != URI.parse(URI.encode(referer)).host
link_to(t('edit'), edit_task_admin_ruling_timer_path , :class=>"btn") +
link_to(t('ruling_timer.back'), referer, :class=>"btn") link_to(t('ruling_timer.back'), referer, :class=>"btn")
end %> end %>
<style> <style>
@ -64,3 +178,79 @@
text-align: center; text-align: center;
} }
</style> </style>
<script>
$(document).on('click', '#add_comment', function(){
var new_id = $(this).prev().attr('value');
var old_id = new RegExp("new_ruling_timer_files", "g");
var on = $('.language-nav li.active').index();
var le = $(this).parent('.add-btn').prev('.add-target').children('.start-line').length;
$(this).prev().attr('value', parseInt(new_id) + 1);
$(this).parent().siblings('.add-target').prepend(("<%= escape_javascript(add_attribute 'form_comment', f, :ruling_timer_comments) %>").replace(old_id, new_id));
formTip();
});
$(document).on("click",".edit_comment",function(){
var comment_block = $(this).parents(".comment-block");
comment_block.find(".content_text").addClass("hide");
comment_block.find(".content_textarea").removeClass("hide").focus();
comment_block.find(".content_textarea").data("content",comment_block.find(".content_textarea").val());
$(this).addClass("hide");
$(this).siblings(".save_comment").removeClass("hide");
$(this).siblings(".cancel_edit_comment").removeClass("hide");
$(this).siblings(".remove_existing_record").addClass("hide");
})
$(document).on("click",".cancel_edit_comment",function(){
var _this = $(this);
var comment_block = _this.parents(".comment-block");
comment_block.find(".content_textarea").val(comment_block.find(".content_textarea").data("content"));
_this.addClass("hide");
_this.siblings(".save_comment").addClass("hide");
_this.siblings(".edit_comment").removeClass("hide");
_this.siblings(".remove_existing_record").removeClass("hide");
comment_block.find(".content_text").removeClass("hide");
comment_block.find(".content_textarea").addClass("hide");
})
$(document).on('click', '.delete_file, .delete_comment', function(){
$(this).parents('.input-prepend,.input-block').remove();
});
$(document).on('click', '.remove_existing_record', function(){
var _this = $(this);
if(confirm("<%= I18n.t(:sure?)%>")){
_this.children('.should_destroy').attr('value', 1);
var comment_block = _this.parents('.comment-block');
if( comment_block.length != 0 ){
var id = comment_block.data("id");
$.post("<%=delete_comment_admin_ruling_timers_path %>",{"id": id}).done(function(){
_this.parents('.start-line,.comment-block').remove();
})
}
}
});
$(".markdown_text:not(.ready)").each(function(i,v){
$(v).html(marked($(v).html()));
$(v).addClass("ready");
})
$(document).on("click",".save_comment",function(){
var _this = $(this);
var comment_block = _this.parents(".comment-block");
var id = comment_block.data("id");
var task_id = "<%= @task.id.to_s %>";
var content = comment_block.find(".content_textarea").val();
$.post("<%=save_comment_admin_ruling_timers_path %>",{"id": id,"content": content,"task_id": task_id}).done(function(data){
if(data["id"]){
comment_block.data("id",data["id"]);
_this.addClass("hide");
_this.siblings(".cancel_edit_comment").addClass("hide");
_this.siblings(".edit_comment").removeClass("hide");
_this.siblings(".remove_existing_record").removeClass("hide");
if(data["html"]){
comment_block.replaceWith(data["html"]);
$(".markdown_text:not(.ready)").each(function(i,v){
$(v).html(marked($(v).html()));
$(v).addClass("ready");
})
}
}
})
})
</script>
<% end %>

View File

@ -0,0 +1,122 @@
<% if current_user %>
<div class="timeman-container timeman-container-tc" id="timeman-container">
<div class="timeman-wrap">
<span id="timeman-block" class="timeman-block">
<span class="bx-time" id="timeman-timer">
<span class="current_time"><%=DateTime.now.new_offset('+08:00').strftime('%H:%M')%></span>
</span>
<span class="timeman-right-side" id="timeman-right">
<span class="timeman-beginning-but" id="timeman-status-block">
<i class="fa fa-stop-circle"></i>
<span id="timeman-status" class="timeman-status">工作中</span>
</span>
</span>
</span>
</div>
</div>
<style>
.timeman-container{
float: left;
background: <%=current_site.orbit_bar_background_color%>;
color: <%=current_site.orbit_bar_text_color%>;
height: 100%;
}
.timeman-wrap{
height: 100%;
}
.current_time{
font-size: 1.3em;
}
.timeman-block {
cursor: pointer;
display: inline-block;
height: 100%;
text-align: left;
white-space: normal;
display: flex;
}
.timeman-right-side{
display: inline-block;
font-size: 0.9em;
margin-left: 0.5em;
vertical-align: middle;
line-height: 2;
}
.timeman-beginning-but{
vertical-align: middle;
}
</style>
<script>
$(document).ready(function(){
var time_tracker = {};
var is_chinese = ( I18n && I18n.locale.indexOf('zh') != -1 );
time_tracker.datetime_format = is_chinese ? 'y M d h:m b' : 'd M, y h:m b';
time_tracker.date_format = is_chinese ? 'y M d' : 'd M, y';
time_tracker.time_format = "h:m b";
time_tracker.date_time_str_format = 'y/MM/d H:m';
time_tracker.short_day = (is_chinese ? "d (w)" : "w d")
time_tracker.short_date = (is_chinese ? "M d (w)" : "w d, M")
time_tracker.getDateString = function(date, format,is_chinese) {
var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
var week_days = ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'];
if(is_chinese){
months = [];
for(var i=0;i<12;i++){
months.push((i+1)+"月");
}
week_days = ["週日","週一","週二","週三","週四","週五","週六"]
}
var getPaddedComp = function(comp) {
return ((parseInt(comp) < 10) ? ('0' + comp) : comp)
},
formattedDate = format,
o = {
"y+": date.getFullYear() + (is_chinese ? "年" : ""), // year
"MM+": getPaddedComp(date.getMonth() + 1), //raw month
"M+": months[date.getMonth()], //month
"d+": (is_chinese ? (date.getDate() + "日") : getPaddedComp(date.getDate())), //day
"w+": week_days[date.getDay()], //weekday
"h+": getPaddedComp((date.getHours() > 12) ? date.getHours() % 12 : date.getHours()), //hour
"H+": getPaddedComp(date.getHours()), //hour
"m+": getPaddedComp(date.getMinutes()), //minute
"s+": getPaddedComp(date.getSeconds()), //second
"S+": getPaddedComp(date.getMilliseconds()), //millisecond,
"b+": (date.getHours() >= 12) ? 'PM' : 'AM'
};
for (var k in o) {
if (new RegExp("(" + k + ")").test(format)) {
formattedDate = formattedDate.replace(RegExp.$1, o[k]);
}
}
return formattedDate;
};
var update_interval = 60 * 1000;
var time_tracker_timeout_id;
function update_time(){
$(".current_time").html(time_tracker.getDateString(new Date,time_tracker.time_format))
var update_interval = 60 * 1000;
if(time_tracker_timeout_id)
window.clearTimeout(time_tracker_timeout_id);
time_tracker_timeout_id = window.setTimeout(update_time,update_interval)
}
update_time();
$("#timeman-container").off("click").on("click",function(){
$("#timeman_main").css("display","");
if($("#timeman_main").hasClass("active_popup"))
$("#timeman_main").removeClass("active_popup");
else{
$("#timeman_main").addClass("active_popup");
$('#timeman_main .nav-pills').scrollingTabs('refresh');
}
})
$(document).on("click",":not(.timeman-wrap,#timeman_main)",function(event){
var target = $(event.target);
if(target.is(":not(.timeman-wrap,#timeman_main)")){
if(target.parents(".timeman-wrap,#timeman_main").length == 0)
$("#timeman_main").css("display","none");
}
})
})
</script>
<% end %>

View File

@ -0,0 +1,276 @@
<% if (current_user rescue false)%>
<div class="popup-window" id="timeman_main" style="display: none; position: fixed; left: 0; z-index: 1400 !important;">
<div id="popup-window-content-timeman_main" class="popup-window-content">
<div class="tm-popup-content">
<div class="tm-popup-notice">
<span class="tm-popup-notice-text">工作日期間: </span>
<span class="tm-popup-notice-time">00:00:00</span>
<span class="tm-popup-notice-pencil"><i class="fa fa-pencil" aria-hidden="true"></i></span>
<span class="tm-popup-notice-right"></span>
</div>
<div class="tm-popup-timeman tm-popup-timeman-buttons-mode tm-popup-timeman-change-time-mode">
<div class="tm-popup-timeman-pause" style="display: none;">
<span class="tm-popup-timeman-pause-timer-caption">休息時數:</span>
<span class="tm-popup-timeman-pause-time"></span>
</div>
<table cellspacing="0" class="tm-popup-timeman-layout">
<tbody>
<tr class="timer_button">
<td class="tm-popup-timeman-layout-time">
<button class="btn btn-secondary tm-btn-pause">
<span class="text-start" id="resume_timer"><i class="fa fa-play" style="margin-right: 0.5em;"></i>繼續</span>
<span class="text-pause" id="pause_timer"><i class="fa fa-pause" style="margin-right: 0.5em;"></i>休息時數</span>
</button>
</td>
<td class="tm-popup-timeman-layout-button">
<div class="tm-popup-button-handler">
<button class="btn btn-danger" id="stop_timer">
<span style="margin-right: 0.5em;">&#9632;</span>打卡下班
</button>
</div>
<span class="tm-popup-change-time-link">變更下班打卡時間</span>
</td>
</tr>
</tbody>
</table>
</div>
<div style="min-height: 0px;">
<div class="tm-info-bar" style="display: none;">
<span title="開始時間追蹤器" class="tm-info-bar-btn tm-info-bar-btn-play">
</span>
<span title="暫停我的計時器" class="tm-info-bar-btn tm-info-bar-btn-pause"></span>
<span title="完成任務並停止時間追蹤器" class="tm-info-bar-btn tm-info-bar-btn-flag"></span>
<span class="tm-info-bar-time"></span>
<span class="tm-info-bar-text">
<span class="tm-info-bar-text-inner"></span>
</span>
</div>
</div>
<div class="tm-tabs-box">
<ul class="nav nav-pills tm-tabs">
<li class="tm-tab active">
<a href="#tm_plan" data-toggle="tab" aria-expanded="true">每日計劃</a>
</li>
<li class="tm-tab">
<a href="#tm_summary" data-toggle="tab" aria-expanded="true">每日摘要</a>
</li>
</ul>
<div class="tab-content tm-tabs-content">
<div class="tm-tab-content tab-pane fade active in" id="tm_plan">
<div class="bx-planner-content">
<div>
<div class="tm-popup-section tm-popup-section-tasks"><span class="tm-popup-section-text">今日任務</span><span class="tm-popup-section-right-link">從清單中選取</span>
</div>
<div class="tm-popup-tasks">
<!-- <div class="tm-task-list">
<div id="tm-task-item-19" class="tm-task-item "><input class="tm-task-checkbox" type="checkbox">
<a href="/company/personal/user/9/tasks/task/view/19/" class="tm-task-name tm-task-no-timer">修改研討會版型</a>
<span class="tm-task-item-menu"></span>
</div>
</div> -->
<div class="tm-popup-task-form tm-popup-task-form-disabled">
<input type="text" class="tm-popup-task-form-textbox" placeholder="輸入新任務">
<span class="tm-popup-task-form-submit"></span>
</div>
</div>
</div>
<div class="tm-popup-events-empty">
<div class="tm-popup-section tm-popup-section-events">
<span class="tm-popup-section-text">事項</span>
</div>
<div class="tm-popup-events">
<div class="tm-popup-event-list"></div>
<div class="tm-popup-event-form tm-popup-event-form-disabled">
<input type="text" class="tm-popup-event-start-time-textbox" value="13:00">
<input type="text" class="tm-popup-event-end-time-textbox" value="14:00">
<input type="text" class="tm-popup-event-form-textbox" value="新活動">
<span class="tm-popup-event-form-submit"></span>
<div class="tm-popup-event-form-options">
<input type="checkbox" class="checkbox" id="bx_tm_absence_0.4349872404840247">
<label for="bx_tm_absence_0.4349872404840247">不在辦公室</label>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="tm-tab-content tab-pane fade" id="tm_summary">
<div class="tm-popup-report">
<div class="tm-popup-report-text">
<textarea class="tm-popup-report-textarea" placeholder="撰寫您工作的簡報"></textarea>
</div>
<div class="tm-popup-report-buttons">
<button type="button" class="btn btn-success" disabled="true">儲存</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="popup-window-angly popup-window-angly-top" style="left: 130px; margin-left: 0px;"></div>
</div>
<style>
.tm-popup-notice-pencil{
cursor: pointer;
}
.popup-window{
background: #ffffff;
top: 48px;
padding: 10px;
-webkit-box-shadow: 0 7px 21px rgb(83 92 105 / 12%), 0 -1px 6px 0 rgb(83 92 105 / 6%);
box-shadow: 0 7px 21px rgb(83 92 105 / 12%), 0 -1px 6px 0 rgb(83 92 105 / 6%);
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
justify-content: stretch;
display: none;
}
.popup-window.active_popup{
display: flex;
}
.tm-popup-timeman-pause-timer-caption{
background: #868d95;
color: #ffffff;
}
.timer_button{
vertical-align: top;
}
.tm-tabs-box ul{
width: 100%;
}
.popup-window-angly {
height: 22px;
position: absolute;
overflow: hidden;
width: 33px;
}
.popup-window-angly-top {
display: block;
left: 10px;
margin: 0;
top: -22px;
}
.popup-window-angly:before {
background-color: #fff;
-webkit-box-shadow: 0 0 21px rgb(83 92 105 / 13%);
box-shadow: 0 0 21px rgb(83 92 105 / 13%);
content: '';
height: 15px;
position: absolute;
left: 9px;
top: 16px;
-webkit-transform: rotate(45deg);
-ms-transform: rotate(45deg);
transform: rotate(45deg);
-webkit-transform-origin: 50% 50%;
-ms-transform-origin: 50% 50%;
transform-origin: 50% 50%;
width: 15px;
}
.text-pause,.text-start{
display: none;
}
.tm-btn-pause .text-pause{
display: initial;
}
.tm-btn-start .text-start{
display: initial;
}
.btn.btn-secondary{
background: #868d95;
color: #fff;
}
.btn.btn-secondary:hover{
background: #5b6573;
}
.tm-popup-report-buttons {
padding: 10px 0 0 0;
text-align: center;
}
.tm-tab-content {
background-color: #fff;
border: 1px solid #c6cdd3;
padding: 10px;
}
.tm-popup-report-textarea{
width: 100%;
min-height: 130px;
}
.tm-popup-section {
border-radius: 0;
height: 39px;
}
.tm-popup-section {
white-space: nowrap;
position: relative;
background: #e8e8e8;
font: normal normal normal 12px/23px Arial,Helvetica,sans-serif;
border-radius: 2px;
}
.tm-popup-section-tasks {
background: #b9e9fa;
}
.tm-popup-section-right-link {
color: #3a8090;
font-size: 12px;
line-height: 14px;
opacity: .6;
margin-top: 12px;
text-decoration: none;
border-bottom: 1px dashed #3a8090;
-webkit-transition: opacity .2s linear;
transition: opacity .2s linear;
}
.tm-popup-section-right-link {
position: absolute;
right: 11px;
top: 0;
font-size: 11px;
line-height: 21px;
color: #949494;
text-decoration: underline;
cursor: pointer;
}
.tm-popup-section-left, .tm-popup-section-text, .tm-popup-section-right {
display: inline-block;
height: 23px;
vertical-align: top;
}
.tm-popup-section-text {
color: #535c69;
height: 39px;
font: bold 14px/39px "Helvetica Neue",Helvetica,Arial,sans-serif;
padding-left: 10px;
vertical-align: middle;
}
.tm-popup-section-text {
padding: 0 8px;
color: #000;
}
.tm-popup-section-events {
background: #ffe75e;
}
.tm-popup-event-form-options {
width: auto;
padding: 2px 0 0 110px;
text-align: left;
color: #555;
}
.tm-popup-event-form .tm-popup-event-start-time-textbox, .tm-popup-event-form .tm-popup-event-end-time-textbox {
padding: 0 4px !important;
text-align: center;
width: 55px;
}
.tm-popup-event-form-options input {
vertical-align: middle;
margin: 0;
display: inline;
}
.tm-popup-tasks {
margin: 15px 0;
}
.tm-popup-event-form {
margin: 10px 0 0 0;
}
</style>
<% end %>

View File

@ -1,4 +1,5 @@
<% if current_user %> <% @allow_ruling_timer = (current_user && (current_user.is_admin? || RulingTimerSetting.first.authorize?(current_user) rescue false)) %>
<% if @allow_ruling_timer %>
<%= csrf_meta_tags %> <%= csrf_meta_tags %>
<% @ruling_timer_temp = RulingTimerTemp.where(:user=>current_user).first %> <% @ruling_timer_temp = RulingTimerTemp.where(:user=>current_user).first %>
<% @ruling_timer_temp = RulingTimerTemp.new if @ruling_timer_temp.nil? %> <% @ruling_timer_temp = RulingTimerTemp.new if @ruling_timer_temp.nil? %>
@ -89,6 +90,7 @@
} }
window.create_notify = function(title,body,idx){ window.create_notify = function(title,body,idx){
if(window.can_notify){ if(window.can_notify){
console.log("create")
body = body || ""; body = body || "";
var notify = new Notification(title,{"body": body,"icon": window.notify_icon_url,"tag": ("timer_notify_"+idx)}); var notify = new Notification(title,{"body": body,"icon": window.notify_icon_url,"tag": ("timer_notify_"+idx)});
if(idx != undefined){ if(idx != undefined){

View File

@ -1,4 +1,4 @@
<% if (current_user rescue false)%> <% if @allow_ruling_timer %>
<% @ruling_timer_temp.reset_all %> <% @ruling_timer_temp.reset_all %>
<% @timer_infos = @ruling_timer_temp.get_infos %> <% @timer_infos = @ruling_timer_temp.get_infos %>
<div class="popup-window" id="timeman_main" style="display: none; position: fixed; left: 0; z-index: 1400 !important;"> <div class="popup-window" id="timeman_main" style="display: none; position: fixed; left: 0; z-index: 1400 !important;">
@ -72,7 +72,7 @@
<% checked = @ruling_timer_temp.tasks_finished.include?(i) %> <% checked = @ruling_timer_temp.tasks_finished.include?(i) %>
<div class="tm-task-item" data-task-id="<%=@ruling_timer_temp.sub_task_ids[i]%>"> <div class="tm-task-item" data-task-id="<%=@ruling_timer_temp.sub_task_ids[i]%>">
<input class="tm-task-checkbox" type="checkbox" <%="checked=\"checked\"" if checked%>> <input class="tm-task-checkbox" type="checkbox" <%="checked=\"checked\"" if checked%>>
<span class="tm-task-name <%="task-finished" if checked %>"><%=task%></span> <a href="<%=view_task_admin_ruling_timer_path(:id=>@ruling_timer_temp.sub_task_ids[i],:type=>"sub_task")%>" class="tm-task-name <%="task-finished" if checked %>" title="<%=task%>" target="_blank"><%=task%></a>
<span class="tm-task-item-menu"><i class="fa fa-caret-down"></i></span> <span class="tm-task-item-menu"><i class="fa fa-caret-down"></i></span>
</div> </div>
<% end %> <% end %>
@ -150,7 +150,7 @@
<% else %> <% else %>
<% @new_sub_tasks.each_with_index do |sub_task,i| %> <% @new_sub_tasks.each_with_index do |sub_task,i| %>
<div class="tm-task-new-item" data-task-id="<%=sub_task.ruling_timer_task_id.to_s%>"> <div class="tm-task-new-item" data-task-id="<%=sub_task.ruling_timer_task_id.to_s%>">
<%=sub_task.task_name%> <a href="<%=view_task_admin_ruling_timer_path(:id=>sub_task.ruling_timer_task_id.to_s)%>" title="<%=sub_task.task_name%>" target="_blank"><%=sub_task.task_name%></a>
<button type="button" title="添加任務" class="add_btn add_task_from_list">+</button> <button type="button" title="添加任務" class="add_btn add_task_from_list">+</button>
</div> </div>
<% end %> <% end %>
@ -494,6 +494,7 @@
$(".stop_btn_groups").addClass("hide"); $(".stop_btn_groups").addClass("hide");
$(".working_btn_groups").removeClass("hide"); $(".working_btn_groups").removeClass("hide");
_this.parent().removeClass("tm-btn-start").addClass("tm-btn-pause"); _this.parent().removeClass("tm-btn-start").addClass("tm-btn-pause");
time_tracker.stop_timer();
time_tracker.set_data(data); time_tracker.set_data(data);
time_tracker.start_timer("work_seconds"); time_tracker.start_timer("work_seconds");
$(".tm-popup-timeman-pause").css("display","none"); $(".tm-popup-timeman-pause").css("display","none");
@ -530,6 +531,7 @@
$(".stop_btn_groups").addClass("hide"); $(".stop_btn_groups").addClass("hide");
$(".working_btn_groups").removeClass("hide"); $(".working_btn_groups").removeClass("hide");
_this.parent().addClass("tm-btn-start").removeClass("tm-btn-pause"); _this.parent().addClass("tm-btn-start").removeClass("tm-btn-pause");
time_tracker.stop_timer();
time_tracker.set_data(data); time_tracker.set_data(data);
time_tracker.start_timer("rest_seconds"); time_tracker.start_timer("rest_seconds");
$(".tm-popup-timeman-pause").css("display",""); $(".tm-popup-timeman-pause").css("display","");

View File

@ -1,5 +1,6 @@
en: en:
restful_actions: restful_actions:
project_management: Project Management
task_management: Task Management task_management: Task Management
timer_management: Timer Management timer_management: Timer Management
work_history: Work history work_history: Work history
@ -8,9 +9,43 @@ en:
add_task: Add task add_task: Add task
edit_task: Edit task edit_task: Edit task
add_history: "Add history" add_history: "Add history"
add_project: Add project
view_task: View Task
module_name: module_name:
ruling_timer: Ruling timer ruling_timer: Teamwork
ruling_timer: ruling_timer:
cancel_edit: Cancel Edit
save: Save
task_performer: "Owner , Helper"
none: None
must_select_one: "Must select one!"
can_only_choose_one: "Can only choose one!"
change: Change
privacy: Privacy
privacy_opts:
"0": Open
"1": Project
"2": Task
priority: Priority
priority_opts:
"3": Urgent
"2": First
"1": Normal
"0": None
save: Save
settings: Settings
authorized_user_role: Authorized User Role
add_role: Add role
ticket_no: "Ticket No."
project_no: "Project No."
create_time: Create Time
scheduled_completion_time: Scheduled Completion Time
estimated_working_hours: Estimated Working Hours
actual_completion_time: Actual Completion Time
start_year_month: Start year month
end_year_month: End year month
list: List
statistic: Statistic
please_input_larger_than_value: "Please input larger than {{value}}!" please_input_larger_than_value: "Please input larger than {{value}}!"
please_input_smaller_than_value: "Please input smaller than {{value}}!" please_input_smaller_than_value: "Please input smaller than {{value}}!"
view_history: "View history" view_history: "View history"
@ -25,13 +60,25 @@ en:
delete_history_hint: "Do you really want to delete this history?" delete_history_hint: "Do you really want to delete this history?"
delete_task_hint1: "Do you really want to delete this task({{task}})?" delete_task_hint1: "Do you really want to delete this task({{task}})?"
delete_task_hint: "Do you really want to delete this task?" delete_task_hint: "Do you really want to delete this task?"
delete_project_hint1: "Do you really want to delete this project({{project}})?"
delete_project_hint: "Do you really want to delete this project?"
delete_section_hint1: "Do you really want to delete this section({{section}})?"
delete_section_hint: "Do you really want to delete this section?"
new_tasks_hint: "New tasks can be added!" new_tasks_hint: "New tasks can be added!"
add_member: Add Member add_member: Add Member
delete_task: "Delete task" delete_task: "Delete task"
add_task: Add task add_task: Add task
task: Task task: Task
task_performer: Task performer creator: Creator
task_name: Task name owner: Owner
helper: Helper
observer: Observer
progress: Progress
details: Details
comments_text: Comments
comments: Comments
file: File
task_name: Task Title
finish: Finish finish: Finish
stop: Stop stop: Stop
working: Working working: Working
@ -39,7 +86,7 @@ en:
offline: Offline offline: Offline
member: Member member: Member
total: Total total: Total
ruling_timer: Ruling timer ruling_timer: Teamwork
user_work_history: "%{user}'s Work History" user_work_history: "%{user}'s Work History"
my_work_history: My Work History my_work_history: My Work History
summary: Summary summary: Summary
@ -53,3 +100,38 @@ en:
thursday: Thursday thursday: Thursday
friday: Friday friday: Friday
saturday: Saturday saturday: Saturday
task_status: Status
projects: Project
project: Project
project_name: Project Title
project_member: Project Member
category: Category
objectives: Objectives
status: Status
status_opts:
"0": Planning
"1": Started
"2": Closed
approval_procedures: Approval Procedures
private_tasks: Private Tasks
project_manager: Project Manager
task_count: Task count
add_project: Add project
personal_project: Personal
am: "AM"
pm: "PM"
formate_date1: "%Y/%m/%d %l:%M %P"
formate_date2: "%m/%d %l:%M %P"
formate_date3: "%k hour ago"
formate_date4: "%M minute ago"
section_management: "Sections Management"
sections: Sections
section_name: Section Name
add_section: Add Section
edit_section: Edit Section
delete_section: Delete Section
close: Close
all_tasks: All Tasks
my_tasks: My Tasks
my_assisted_tasks: My Assisted Tasks
my_observed_tasks: My Observed Tasks

View File

@ -1,5 +1,6 @@
zh_tw: zh_tw:
restful_actions: restful_actions:
project_management: 專案管理
task_management: 任務管理 task_management: 任務管理
timer_management: 計時管理 timer_management: 計時管理
work_history: 工作紀錄 work_history: 工作紀錄
@ -8,9 +9,43 @@ zh_tw:
add_task: 新增任務 add_task: 新增任務
edit_task: 編輯任務 edit_task: 編輯任務
add_history: 新增紀錄 add_history: 新增紀錄
add_project: 新增專案
view_task: 查看任務
module_name: module_name:
ruling_timer: 計時器模組 ruling_timer: Teamwork
ruling_timer: ruling_timer:
cancel_edit: 取消編輯
save: 儲存
task_performer: "負責人、協助者"
none:
must_select_one: "請選擇1個!"
can_only_choose_one: "只能選擇1個!"
change: 更換
privacy: 私密等級
privacy_opts:
"0": 所有團隊成員
"1": 專案成員
"2": 任務成員
priority: 優先度
priority_opts:
"3": 緊急
"2": 優先
"1": 普通
"0":
save: 儲存
settings: 設定
authorized_user_role: 授權使用身分
add_role: 添加身分
ticket_no: 單號
project_no: 專案編號
create_time: 建立時間
scheduled_completion_time: 預定完成時間
estimated_working_hours: 預估⼯時
actual_completion_time: 實際完成時間
start_year_month: 開始年月
end_year_month: 結束年月
list: 列表
statistic: 統計
please_input_larger_than_value: "請輸入大於{{value}}的值!" please_input_larger_than_value: "請輸入大於{{value}}的值!"
please_input_smaller_than_value: "請輸入小於{{value}}的值!" please_input_smaller_than_value: "請輸入小於{{value}}的值!"
view_history: 查看歷史紀錄 view_history: 查看歷史紀錄
@ -25,12 +60,24 @@ zh_tw:
delete_history_hint: "您真的想刪除此紀錄嗎?" delete_history_hint: "您真的想刪除此紀錄嗎?"
delete_task_hint1: "您真的想刪除此任務({{task}})嗎?" delete_task_hint1: "您真的想刪除此任務({{task}})嗎?"
delete_task_hint: "您真的想刪除此任務嗎?" delete_task_hint: "您真的想刪除此任務嗎?"
delete_project_hint1: "您真的想刪除此專案({{project}})嗎?"
delete_project_hint: "您真的想刪除此專案嗎?"
delete_section_hint1: "您真的想刪除此任務列表({{section}})嗎?"
delete_section_hint: "您真的想刪除此任務列表嗎?"
new_tasks_hint: 有新的任務可添加 new_tasks_hint: 有新的任務可添加
add_member: 添加成員 add_member: 添加成員
delete_task: 刪除任務 delete_task: 刪除任務
add_task: 新增任務 add_task: 新增任務
task: 任務 task: 任務
task_performer: 任務執行者 creator: 建立者
owner: 負責⼈
helper: 協助者
observer: 觀察者
progress: 進度
details: 說明
comments_text: 評論
comments: 撰寫評論
file: 附件
task_name: 任務名稱 task_name: 任務名稱
finish: 已完成 finish: 已完成
stop: 暫停 stop: 暫停
@ -39,7 +86,7 @@ zh_tw:
offline: 離線 offline: 離線
member: 成員 member: 成員
total: 共計 total: 共計
ruling_timer: 計時器模組 ruling_timer: Teamwork
user_work_history: "%{user}的工作紀錄" user_work_history: "%{user}的工作紀錄"
my_work_history: 我的工作紀錄 my_work_history: 我的工作紀錄
summary: 摘要 summary: 摘要
@ -53,3 +100,38 @@ zh_tw:
thursday: 週四 thursday: 週四
friday: 週五 friday: 週五
saturday: 週六 saturday: 週六
task_status: 執⾏狀態
projects: 專案
project: 所屬專案
project_name: 專案名稱
project_member: 專案成員
category: 專案類別
objectives: 專案⽬標
status: 專案狀態
status_opts:
"0": 規劃中
"1": 已啟動
"2": 已結案
approval_procedures: 簽核流程
private_tasks: 私⼈任務
project_manager: 專案經理
task_count: 任務數量
add_project: 新增專案
personal_project: 個⼈專案
am: "上午"
pm: "下午"
formate_date1: "%Y年%m月%d日 %P%l點%M分"
formate_date2: "%m月%d日 %P%l點%M分"
formate_date3: "%k小時前"
formate_date4: "%M分鐘前"
sections: 任務列表
section_management: "任務列表管理"
section_name: 列表名稱
add_section: 新增列表
edit_section: 編輯列表
delete_section: 刪除列表
close: 關閉
all_tasks: 所有任務
my_tasks: 我的任務
my_assisted_tasks: 我協助的任務
my_observed_tasks: 我觀察的任務

View File

@ -12,6 +12,28 @@ Rails.application.routes.draw do
save_flag = true save_flag = true
end end
s.save if save_flag s.save if save_flag
@module_app = ModuleApp.where(:key=>"ruling_timer").first
if @module_app.categories.count == 0
category = @module_app.categories.new
category.title_translations = I18n.available_locales.map{|l| [l.to_s,I18n.with_locale(l){I18n.t("ruling_timer.personal_project")}]}.to_h
category.save
setting = RulingTimerSetting.first
setting = RulingTimerSetting.create if setting.nil?
setting.personal_category_id = category.id
setting.save
else
setting = RulingTimerSetting.first
setting = RulingTimerSetting.create if setting.nil?
if setting.personal_category_id.nil?
category = @module_app.categories.first
setting.personal_category_id = category.id
setting.save
else
category = Category.find(setting.personal_category_id)
end
category.title_translations = I18n.available_locales.map{|l| [l.to_s,I18n.with_locale(l){I18n.t("ruling_timer.personal_project")}]}.to_h
category.save
end
rescue => e rescue => e
puts e.to_s puts e.to_s
end end
@ -28,6 +50,15 @@ Rails.application.routes.draw do
get "edit_task" get "edit_task"
post "delete_task" post "delete_task"
get "view_task" get "view_task"
patch "update_project"
get "edit_project"
post "delete_project"
get "view_project"
get "section_management"
get "edit_section"
patch "update_section"
post "delete_section"
get "add_section"
end end
collection do collection do
post "create_history" post "create_history"
@ -37,6 +68,17 @@ Rails.application.routes.draw do
get "add_task" get "add_task"
post "create_task" post "create_task"
patch "create_task" patch "create_task"
get "settings"
post "update_setting"
patch "update_setting"
get "project_management"
get "add_project"
post "create_project"
patch "create_project"
post "create_section"
patch "create_section"
post "save_comment"
post "delete_comment"
end end
end end
end end

View File

@ -6,29 +6,58 @@ module RulingTimer
module_label "ruling_timer.ruling_timer" module_label "ruling_timer.ruling_timer"
base_url File.expand_path File.dirname(__FILE__) base_url File.expand_path File.dirname(__FILE__)
authorizable authorizable
categorizable
taggable "RulingTimerTask"
side_bar do side_bar do
head_label_i18n 'ruling_timer.ruling_timer', icon_class: "icons-clock" head_label_i18n 'ruling_timer.ruling_timer', icon_class: "icons-clock"
available_for "users" available_for "users"
active_for_controllers (['admin/ruling_timers']) active_for_controllers (['admin/ruling_timers'])
head_link_path "admin_ruling_timers_path" head_link_path "admin_ruling_timers_path"
context_link 'ruling_timer.settings',
:link_path=>"settings_admin_ruling_timers_path" ,
:priority=>1,
:active_for_action=>{'admin/ruling_timers'=>'settings'},
:available_for => 'managers'
context_link 'restful_actions.timer_management', context_link 'restful_actions.timer_management',
:link_path=>"timer_management_admin_ruling_timers_path" , :link_path=>"timer_management_admin_ruling_timers_path" ,
:priority=>1, :priority=>2,
:active_for_action=>{'admin/ruling_timers'=>'timer_management'}, :active_for_action=>{'admin/ruling_timers'=>'timer_management'},
:available_for => 'managers' :available_for => 'managers'
context_link 'restful_actions.task_management', context_link 'restful_actions.task_management',
:link_path=>"task_management_admin_ruling_timers_path" , :link_path=>"task_management_admin_ruling_timers_path" ,
:priority=>2, :priority=>3,
:active_for_action=>{'admin/ruling_timers'=>'task_management'}, :active_for_action=>{'admin/ruling_timers'=>'task_management'},
:available_for => 'managers' :available_for => 'managers'
context_link 'restful_actions.project_management',
:link_path=>"project_management_admin_ruling_timers_path" ,
:priority=>3,
:active_for_action=>{'admin/ruling_timers'=>'project_management'},
:available_for => 'managers'
context_link 'categories',
:link_path=>"admin_module_app_categories_path" ,
:link_arg=>"{:module_app_id=>ModuleApp.find_by(:key=>'ruling_timer').id}",
:priority=>4,
:active_for_action=>{'admin/ruling_timers'=>'categories'},
:active_for_category => 'RulingTimer',
:available_for => 'managers'
context_link 'tags',
:link_path=>"admin_module_app_tags_path" ,
:link_arg=>"{:module_app_id=>ModuleApp.find_by(:key=>'ruling_timer').id}",
:priority=>5,
:active_for_action=>{'admin/ruling_timers'=>'tags'},
:active_for_tag => 'RulingTimer',
:available_for => 'managers'
end end
end end
rescue => e rescue => e
puts e.to_s
puts e.backtrace
OrbitApp.registration "RulingTimer", :type => "ModuleApp" do OrbitApp.registration "RulingTimer", :type => "ModuleApp" do
module_label "ruling_timer.ruling_timer" module_label "ruling_timer.ruling_timer"
base_url File.expand_path File.dirname(__FILE__) base_url File.expand_path File.dirname(__FILE__)
authorizable authorizable
categorizable
taggable "RulingTimerTask"
side_bar do side_bar do
head_label_i18n 'ruling_timer.ruling_timer', icon_class: "icons-clock" head_label_i18n 'ruling_timer.ruling_timer', icon_class: "icons-clock"
available_for "users" available_for "users"

0
lib/tasks/.keep Normal file
View File