eboshi 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +28 -0
  3. data/Rakefile +8 -0
  4. data/app/assets/config/eboshi_manifest.js +1 -0
  5. data/app/assets/stylesheets/eboshi/application.css +15 -0
  6. data/app/controllers/eboshi/adjustments_controller.rb +77 -0
  7. data/app/controllers/eboshi/application_controller.rb +62 -0
  8. data/app/controllers/eboshi/assignments_controller.rb +31 -0
  9. data/app/controllers/eboshi/budgets_controller.rb +43 -0
  10. data/app/controllers/eboshi/calendar_controller.rb +12 -0
  11. data/app/controllers/eboshi/clients_controller.rb +41 -0
  12. data/app/controllers/eboshi/invoices_controller.rb +69 -0
  13. data/app/controllers/eboshi/line_items_controller.rb +28 -0
  14. data/app/controllers/eboshi/payments_controller.rb +31 -0
  15. data/app/controllers/eboshi/user_sessions_controller.rb +25 -0
  16. data/app/controllers/eboshi/users_controller.rb +41 -0
  17. data/app/controllers/eboshi/works_controller.rb +83 -0
  18. data/app/helpers/eboshi/application_helper.rb +15 -0
  19. data/app/helpers/eboshi/calendar_helper.rb +80 -0
  20. data/app/helpers/eboshi/shallow_route_helper.rb +68 -0
  21. data/app/jobs/eboshi/application_job.rb +4 -0
  22. data/app/mailers/eboshi/application_mailer.rb +6 -0
  23. data/app/models/eboshi/adjustment.rb +37 -0
  24. data/app/models/eboshi/application_record.rb +5 -0
  25. data/app/models/eboshi/assignment.rb +20 -0
  26. data/app/models/eboshi/budget.rb +33 -0
  27. data/app/models/eboshi/client.rb +85 -0
  28. data/app/models/eboshi/invoice.rb +87 -0
  29. data/app/models/eboshi/line_item.rb +94 -0
  30. data/app/models/eboshi/payment.rb +20 -0
  31. data/app/models/eboshi/user.rb +128 -0
  32. data/app/models/eboshi/user_session.rb +6 -0
  33. data/app/models/eboshi/work.rb +66 -0
  34. data/app/views/eboshi/adjustments/_adjustment.html.slim +15 -0
  35. data/app/views/eboshi/adjustments/_form.html.haml +27 -0
  36. data/app/views/eboshi/adjustments/edit.html.haml +7 -0
  37. data/app/views/eboshi/adjustments/new.html.haml +8 -0
  38. data/app/views/eboshi/assignments/new.html.haml +18 -0
  39. data/app/views/eboshi/budgets/form.html.haml +22 -0
  40. data/app/views/eboshi/calendar/index.html.slim +32 -0
  41. data/app/views/eboshi/clients/_form.html.haml +34 -0
  42. data/app/views/eboshi/clients/edit.html.haml +4 -0
  43. data/app/views/eboshi/clients/index.html.haml +19 -0
  44. data/app/views/eboshi/clients/new.html.haml +4 -0
  45. data/app/views/eboshi/invoices/_form.html.slim +68 -0
  46. data/app/views/eboshi/invoices/_full.html.haml +76 -0
  47. data/app/views/eboshi/invoices/_invoice.html.haml +2 -0
  48. data/app/views/eboshi/invoices/_mini.html.haml +21 -0
  49. data/app/views/eboshi/invoices/edit.html.haml +14 -0
  50. data/app/views/eboshi/invoices/index.html.haml +46 -0
  51. data/app/views/eboshi/invoices/new.html.haml +11 -0
  52. data/app/views/eboshi/invoices/show.pdf.haml +118 -0
  53. data/app/views/eboshi/layouts/application.html.haml +120 -0
  54. data/app/views/eboshi/payments/_form.html.haml +10 -0
  55. data/app/views/eboshi/payments/new.html.haml +9 -0
  56. data/app/views/eboshi/user_sessions/new.html.haml +19 -0
  57. data/app/views/eboshi/users/_form.html.haml +65 -0
  58. data/app/views/eboshi/users/edit.html.haml +16 -0
  59. data/app/views/eboshi/users/index.html.haml +11 -0
  60. data/app/views/eboshi/users/new.html.haml +13 -0
  61. data/app/views/eboshi/users/show.html.haml +39 -0
  62. data/app/views/eboshi/works/_form.html.slim +25 -0
  63. data/app/views/eboshi/works/_work.html.slim +27 -0
  64. data/app/views/eboshi/works/edit.html.haml +9 -0
  65. data/app/views/eboshi/works/new.html.haml +8 -0
  66. data/app/views/layouts/eboshi/application.html.haml +120 -0
  67. data/config/routes.rb +35 -0
  68. data/lib/eboshi/engine.rb +15 -0
  69. data/lib/eboshi/version.rb +3 -0
  70. data/lib/eboshi.rb +6 -0
  71. data/lib/tasks/eboshi_tasks.rake +4 -0
  72. metadata +253 -0
@@ -0,0 +1,120 @@
1
+ !!!
2
+ %html
3
+ %head
4
+ %meta(http-equiv="Content-Type" content="text/html; charset=utf-8")
5
+ %title
6
+ Eboshi »
7
+ = yield :title
8
+ = csrf_meta_tags
9
+
10
+ = stylesheet_link_tag "application"
11
+ = yield :css
12
+
13
+ - if current_user
14
+ %style
15
+ :plain
16
+ a:hover, input.button:hover {color: ##{current_user.color};}
17
+ .button:hover, input.button:hover {border-color:##{current_user.color};}
18
+ .line_item_selected {background-color:##{current_user.color};}
19
+
20
+ = javascript_importmap_tags
21
+
22
+ %link{rel: "shortcut icon", href: image_path("/favicon.png"), type: "image/x-icon"}
23
+
24
+ %body
25
+ #flash_notice= flash.notice
26
+ #flash_error= flash.alert
27
+
28
+ #wrapper
29
+ #header_container
30
+ #header
31
+ #login
32
+ #account.right
33
+ - if current_user
34
+ = link_to 'Logout', logout_path, method: :delete, class: "button right"
35
+ - if current_user.admin?
36
+ = link_to 'All Users', users_path, class: "button right"
37
+ = link_to 'My Account', edit_user_path(current_user), class: "button right"
38
+ .user_name.right.sm_pad{style: "color: ##{current_user.color}"}
39
+ %span Welcome, #{current_user.name}!
40
+ .clear
41
+ .clear
42
+ #title
43
+ %h1
44
+ %a(href="/") EBO$HI
45
+ %p a simple rails invoicing application
46
+ .clear
47
+ .clear
48
+
49
+ #breadcrumbs_container
50
+ #breadcrumbs
51
+ %h2= yield :title
52
+ = yield :header
53
+
54
+ #actions= yield :actions
55
+
56
+ #all_content
57
+ #sidebar
58
+ - if current_user
59
+ %h2#summaries_control Activity Summary
60
+ %table#summaries(width="100%" border="0" cellspacing="0" cellpadding="0")
61
+ %col(width="60%")
62
+ %col(width="20%")
63
+ %col(width="20%")
64
+ %tr
65
+ %td Year
66
+ %td= pluralize number_with_precision(current_user.hours_by_year(Date.today), precision: 0), 'hour'
67
+ %td= currency_or_empty current_user.total_by_year(Date.today)
68
+ %tr
69
+ %td Month
70
+ %td= pluralize number_with_precision(current_user.hours_by_month(Date.today), precision: 0), 'hour'
71
+ %td= currency_or_empty current_user.total_by_month(Date.today)
72
+ %tr
73
+ %td Week
74
+ %td= pluralize number_with_precision(current_user.hours_by_week(Date.today), precision: 0), 'hour'
75
+ %td= currency_or_empty current_user.total_by_week(Date.today)
76
+ %tr
77
+ %td Day
78
+ %td= pluralize number_with_precision(current_user.hours_by_date(Date.today), precision: 0), 'hour'
79
+ %td= currency_or_empty current_user.total_by_date(Date.today)
80
+
81
+ %p= link_to "details", "/calendar"
82
+ %br
83
+
84
+ - if @client and !@client.new_record?
85
+ %h2 Collaborators
86
+ %ul#collaborators
87
+ - @client.assignments.each do |assignment|
88
+ - next if assignment.new_record?
89
+ %li
90
+ %span= assignment.user.name
91
+ = link_to "delete", assignment, :method => :delete
92
+ = link_to '+ Invite collaborator', new_assignment_path(@client), :class=>"button right"
93
+ .clear
94
+
95
+ - if current_user
96
+ %h2= link_to "Clients", clients_path
97
+ %table#clients_summary(width="100%" border="0" cellspacing="0" cellpadding="0")
98
+ %col(width="60%")
99
+ %col(width="20%")
100
+ %col(width="20%")
101
+ - current_user.clients.each do |client|
102
+ = cache client do
103
+ %tr
104
+ %td= link_to client.name, [client, :invoices], class: "inline"
105
+ %td= currency_or_empty client.unbilled_balance
106
+ %td.red= currency_or_empty client.overdue_balance
107
+
108
+ = link_to '+ Add Client', new_client_path, :class=>"button right"
109
+ .clear
110
+
111
+
112
+ #content
113
+ = yield
114
+ .clear
115
+ .clear
116
+
117
+ #footer_container
118
+ #footer
119
+ = yield :footer
120
+
@@ -0,0 +1,10 @@
1
+ - f = form
2
+ %p
3
+ = f.label :create_at, "Date"
4
+ = f.date_select :created_at
5
+
6
+ %p
7
+ = f.label :total
8
+ = f.text_field :total
9
+
10
+ %p= f.submit @payment.new_record? ? "Create" : "Update"
@@ -0,0 +1,9 @@
1
+ - content_for :title do
2
+ New payment
3
+
4
+ = error_messages_for :payment
5
+
6
+ = form_for @payment, :url => payments_path(@invoice) do |f|
7
+ = render :partial => 'form', :object => f
8
+
9
+ = link_to 'Cancel', invoices_path(@invoice.client)
@@ -0,0 +1,19 @@
1
+ - content_for :title do
2
+ Login
3
+
4
+ %h2 Login
5
+
6
+ = form_for @user_session, :url => user_session_path do |f|
7
+ = f.error_messages
8
+ %p
9
+ = f.label :email
10
+ = f.text_field :email
11
+ %p
12
+ = f.label :password
13
+ = f.password_field :password
14
+ %p
15
+ = f.label :remember_me
16
+ = f.check_box :remember_me
17
+ %hr
18
+ %p.center_align
19
+ = f.submit "Login", :class=>"button lg"
@@ -0,0 +1,65 @@
1
+ %p
2
+ = form.label :name
3
+ = form.text_field :name
4
+
5
+ %p
6
+ = form.label :email, "Email Address"
7
+ = form.text_field :email
8
+
9
+ %p
10
+ = form.label :password, form.object.new_record? ? nil : "Change password"
11
+ = form.password_field :password
12
+
13
+ %p
14
+ = form.label :password_confirmation
15
+ = form.password_field :password_confirmation
16
+
17
+ %p
18
+ = form.label :rate, "Default Rate"
19
+ == $#{form.text_field :rate}
20
+
21
+ %p
22
+ = form.label :color
23
+ = form.text_field :color
24
+
25
+ %h1 invoice details
26
+
27
+ %p
28
+ = form.label :business_name
29
+ = form.text_field :business_name
30
+
31
+ %p
32
+ = form.label :business_email
33
+ = form.text_field :business_email
34
+
35
+ %p
36
+ = form.label :address
37
+ = form.text_field :address
38
+
39
+ %p
40
+ = form.label :address2, ""
41
+ = form.text_field :address2
42
+
43
+ %p
44
+ = form.label :city
45
+ = form.text_field :city
46
+
47
+ %p
48
+ = form.label :state
49
+ = form.text_field :state
50
+
51
+ %p
52
+ = form.label :zip, "Zipcode"
53
+ = form.text_field :zip
54
+
55
+ %p
56
+ = form.label :logo
57
+ = image_tag @user.logo.url(:pdf) if @user.logo.exists?
58
+ = form.file_field :logo
59
+
60
+ %p
61
+ = form.label :signature
62
+ = image_tag @user.signature.url(:pdf) if @user.signature.exists?
63
+ = form.file_field :signature
64
+
65
+
@@ -0,0 +1,16 @@
1
+ - content_for :title do
2
+ Users » edit
3
+ = @user.name
4
+
5
+ %h2
6
+ .user_name{:style => "color: \##{current_user.color}"}
7
+ %span Users » edit
8
+ = @user.name
9
+
10
+ = form_for @user, :html => { :multipart => true } do |f|
11
+ = f.error_messages
12
+ = render :partial => "form", :object => f
13
+ = f.submit "Update", :class=>"right"
14
+
15
+ = link_to "cancel", current_user.admin? ? users_path : root_path, :class=>"button right"
16
+ .clear
@@ -0,0 +1,11 @@
1
+ - content_for :title do
2
+ users » all
3
+
4
+ %h2 users » all
5
+ %ul
6
+ - @users.each do |user|
7
+ %li.user
8
+ = link_to h(user.name), edit_user_path(user), :style => "color: \##{user.color}"
9
+ %span= number_to_per_hour user.rate
10
+
11
+ .add= link_to '+add user', new_user_path, :class=>"button right clear"
@@ -0,0 +1,13 @@
1
+ - content_for :title do
2
+ Users » new
3
+
4
+ %h2
5
+ Users » new
6
+
7
+ = form_for @user, :html => { :multipart => true } do |f|
8
+ = f.error_messages
9
+ = render :partial => "form", :object => f
10
+ = f.submit "Register"
11
+
12
+ %br
13
+ = link_to "cancel", users_path
@@ -0,0 +1,39 @@
1
+ - content_for :title do
2
+ Users »
3
+ = @user.name
4
+
5
+ %h2
6
+ .user_name{:style => "color: \##{current_user.color}"}
7
+ %span Users »
8
+ = @user.name
9
+
10
+ %p
11
+ %b Email:
12
+ = @user.email
13
+
14
+
15
+ %p
16
+ %b Login count:
17
+ =h @user.login_count
18
+
19
+ %p
20
+ %b Last request at:
21
+ =h @user.last_request_at
22
+
23
+ %p
24
+ %b Last login at:
25
+ =h @user.last_login_at
26
+
27
+ %p
28
+ %b Current login at:
29
+ =h @user.current_login_at
30
+
31
+ %p
32
+ %b Last login ip:
33
+ =h @user.last_login_ip
34
+
35
+ %p
36
+ %b Current login ip:
37
+ =h @user.current_login_ip
38
+
39
+ = link_to 'Edit', edit_user_path, :class=>"button"
@@ -0,0 +1,25 @@
1
+ = error_messages_for :work
2
+
3
+ = form_for [@client, @work] do |form|
4
+ = form.hidden_field :type
5
+
6
+ p
7
+ = form.label :start
8
+ |  
9
+ = form.datetime_field :start
10
+ p
11
+ = form.label :finish, "End"
12
+ |  
13
+ = form.datetime_field :finish
14
+ p
15
+ = form.label :rate
16
+ |  
17
+ | $
18
+ = form.text_field :rate
19
+ p
20
+ = form.label :notes
21
+ |  
22
+ = form.text_area :notes
23
+
24
+ p = form.submit form.object.new_record? ? "Create" : "Update"
25
+
@@ -0,0 +1,27 @@
1
+ tr.line_item.work id="line_item_#{work.id}_tr" class=cycle('even','') data-controller="line-item"
2
+ td = check_box_tag 'line_item_ids[]', work.id, nil, id: "line_item_ids_#{work.id}", data: { line_item_target: "checkbox" }
3
+ td.name.user_name style="color: ##{work.user&.color}" = work.user&.name
4
+ td.time colspan=(work.incomplete? ? 4 : 1)
5
+ = work.start.to_fs(:slash)
6
+ br
7
+ = work.start.to_fs(:time)
8
+ br
9
+ - if work.incomplete?
10
+ = link_to 'Clock Out', clock_out_path(@client, work), class: "clock_out",
11
+ data: { turbo_frame: dom_id(work.invoice || Eboshi::Invoice.new), turbo: true }
12
+ - else
13
+ = work.finish.to_fs(:time)
14
+ - unless work.incomplete?
15
+ td.hours = number_with_precision work.hours, precision: 2
16
+ td.rate = number_to_per_hour work.rate
17
+ td.total = number_to_currency work.total
18
+ td.notes
19
+ textarea.dummy id="notes_#{work.id}" autocomplete="off" data-controller="live-textarea" data-live-textarea-url-value=line_item_path(work)
20
+ = work.notes
21
+
22
+ td
23
+ = link_to image_tag("edit.png", size: "12x13"), edit_work_path(work), title: "Edit"
24
+ = link_to image_tag("delete.png", size: "11x10"), work, class: "delete",
25
+ data: { confirm: "Are you sure you want to delete this line item? This cannot be undone!",
26
+ turbo_frame: dom_id(work.invoice || Eboshi::Invoice.new), turbo_method: "delete", turbo: true }
27
+
@@ -0,0 +1,9 @@
1
+ - content_for :title do
2
+ =h @client.name
3
+ » Editing Time Item
4
+
5
+ = render "form"
6
+
7
+ = link_to 'Back', invoices_path(@client)
8
+
9
+ = button_to "Convert to Flat fee", convert_work_path(@work)
@@ -0,0 +1,8 @@
1
+ - content_for :title do
2
+ =h @client.name
3
+ » New Time Item
4
+
5
+ = render "form"
6
+
7
+ = link_to 'Back', invoices_path(@client)
8
+
@@ -0,0 +1,120 @@
1
+ !!!
2
+ %html
3
+ %head
4
+ %meta(http-equiv="Content-Type" content="text/html; charset=utf-8")
5
+ %title
6
+ Eboshi »
7
+ = yield :title
8
+ = csrf_meta_tags
9
+
10
+ = stylesheet_link_tag "application"
11
+ = yield :css
12
+
13
+ - if current_user
14
+ %style
15
+ :plain
16
+ a:hover, input.button:hover {color: ##{current_user.color};}
17
+ .button:hover, input.button:hover {border-color:##{current_user.color};}
18
+ .line_item_selected {background-color:##{current_user.color};}
19
+
20
+ = javascript_importmap_tags
21
+
22
+ %link{rel: "shortcut icon", href: image_path("/favicon.png"), type: "image/x-icon"}
23
+
24
+ %body
25
+ #flash_notice= flash.notice
26
+ #flash_error= flash.alert
27
+
28
+ #wrapper
29
+ #header_container
30
+ #header
31
+ #login
32
+ #account.right
33
+ - if current_user
34
+ = link_to 'Logout', logout_path, method: :delete, class: "button right"
35
+ - if current_user.admin?
36
+ = link_to 'All Users', users_path, class: "button right"
37
+ = link_to 'My Account', edit_user_path(current_user), class: "button right"
38
+ .user_name.right.sm_pad{style: "color: ##{current_user.color}"}
39
+ %span Welcome, #{current_user.name}!
40
+ .clear
41
+ .clear
42
+ #title
43
+ %h1
44
+ %a(href="/") EBO$HI
45
+ %p a simple rails invoicing application
46
+ .clear
47
+ .clear
48
+
49
+ #breadcrumbs_container
50
+ #breadcrumbs
51
+ %h2= yield :title
52
+ = yield :header
53
+
54
+ #actions= yield :actions
55
+
56
+ #all_content
57
+ #sidebar
58
+ - if current_user
59
+ %h2#summaries_control Activity Summary
60
+ %table#summaries(width="100%" border="0" cellspacing="0" cellpadding="0")
61
+ %col(width="60%")
62
+ %col(width="20%")
63
+ %col(width="20%")
64
+ %tr
65
+ %td Year
66
+ %td= pluralize number_with_precision(current_user.hours_by_year(Date.today), precision: 0), 'hour'
67
+ %td= currency_or_empty current_user.total_by_year(Date.today)
68
+ %tr
69
+ %td Month
70
+ %td= pluralize number_with_precision(current_user.hours_by_month(Date.today), precision: 0), 'hour'
71
+ %td= currency_or_empty current_user.total_by_month(Date.today)
72
+ %tr
73
+ %td Week
74
+ %td= pluralize number_with_precision(current_user.hours_by_week(Date.today), precision: 0), 'hour'
75
+ %td= currency_or_empty current_user.total_by_week(Date.today)
76
+ %tr
77
+ %td Day
78
+ %td= pluralize number_with_precision(current_user.hours_by_date(Date.today), precision: 0), 'hour'
79
+ %td= currency_or_empty current_user.total_by_date(Date.today)
80
+
81
+ %p= link_to "details", "/calendar"
82
+ %br
83
+
84
+ - if @client and !@client.new_record?
85
+ %h2 Collaborators
86
+ %ul#collaborators
87
+ - @client.assignments.each do |assignment|
88
+ - next if assignment.new_record?
89
+ %li
90
+ %span= assignment.user.name
91
+ = link_to "delete", assignment, :method => :delete
92
+ = link_to '+ Invite collaborator', new_assignment_path(@client), :class=>"button right"
93
+ .clear
94
+
95
+ - if current_user
96
+ %h2= link_to "Clients", clients_path
97
+ %table#clients_summary(width="100%" border="0" cellspacing="0" cellpadding="0")
98
+ %col(width="60%")
99
+ %col(width="20%")
100
+ %col(width="20%")
101
+ - current_user.clients.each do |client|
102
+ = cache client do
103
+ %tr
104
+ %td= link_to client.name, [client, :invoices], class: "inline"
105
+ %td= currency_or_empty client.unbilled_balance
106
+ %td.red= currency_or_empty client.overdue_balance
107
+
108
+ = link_to '+ Add Client', new_client_path, :class=>"button right"
109
+ .clear
110
+
111
+
112
+ #content
113
+ = yield
114
+ .clear
115
+ .clear
116
+
117
+ #footer_container
118
+ #footer
119
+ = yield :footer
120
+
data/config/routes.rb ADDED
@@ -0,0 +1,35 @@
1
+ Eboshi::Engine.routes.draw do
2
+ root to: "clients#index"
3
+
4
+ resources :clients do
5
+ resources :invoices do
6
+ resources :payments
7
+ end
8
+ resources :budgets
9
+
10
+ resources :line_items, only: [:update]
11
+ resources :works, except: [:index, :show] do
12
+ post :merge, on: :collection
13
+ post :convert, on: :member
14
+ end
15
+ resources :adjustments, except: [:index, :show]
16
+ resources :assignments, only: [:new, :create, :destroy]
17
+ end
18
+
19
+ resources :payments
20
+ resources :adjustments, except: [:index, :show]
21
+ resources :assignments, only: [:new, :create, :destroy]
22
+ resources :works, except: [:index, :show]
23
+
24
+ match "/clients/:client_id/clock_in(.:format)" => "works#clock_in", :as => "clock_in", :via => [:get, :post]
25
+ match "/clients/:client_id/works/:id/clock_out(.:format)" => "works#clock_out", :as => "clock_out", :via => [:get, :post]
26
+
27
+ get "/calendar(/:year(/:month))" => "calendar#index", :as => "calendar"
28
+
29
+ resources :users
30
+ resource :account, controller: "users"
31
+ resource :user_session
32
+
33
+ get "login" => "user_sessions#new"
34
+ delete "logout" => "user_sessions#destroy"
35
+ end
@@ -0,0 +1,15 @@
1
+ require "authlogic"
2
+ require "scrypt"
3
+ require "validates_email_format_of"
4
+ require "kt-paperclip"
5
+ require "haml-rails"
6
+ require "dynamic_form"
7
+ require "default_value_for"
8
+ require "wicked_pdf"
9
+ # require "wkhtmltopdf-binary"
10
+
11
+ module Eboshi
12
+ class Engine < ::Rails::Engine
13
+ isolate_namespace Eboshi
14
+ end
15
+ end
@@ -0,0 +1,3 @@
1
+ module Eboshi
2
+ VERSION = "0.1.0"
3
+ end
data/lib/eboshi.rb ADDED
@@ -0,0 +1,6 @@
1
+ require "eboshi/version"
2
+ require "eboshi/engine"
3
+
4
+ module Eboshi
5
+ # Your code goes here...
6
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :eboshi do
3
+ # # Task goes here
4
+ # end