Compare commits

...

48 Commits

Author SHA1 Message Date
b8fdc3f98a Made headline sticky 2025-08-24 01:27:34 +02:00
a588f79b37 Updated to rails 8.0.2.1 2025-08-20 00:01:35 +02:00
624b046db4 Fixed view#jobs and edit#jobs with jobs without pdf 2025-08-05 00:51:05 +02:00
b534e45792 Formated fix 2025-07-25 03:00:54 +02:00
b5557670ba Fixed job number link in jobs#index 2025-07-25 02:59:54 +02:00
a01ac2203e Fixed job number to work with broadcast in jobs#index 2025-07-23 16:09:11 +02:00
e1c6a90830 Bundle update 2025-07-23 16:08:54 +02:00
d856267e84 Updated to rails 8.0.0.2 2025-07-17 12:21:59 +02:00
b0666533ec Updated to rails 8.0.0.1 2024-12-11 06:11:53 +01:00
0f03e4de59 Migrate to rails 8 2024-12-09 11:14:46 +01:00
3803d6657b Analyze PDF only if present 2024-10-07 10:35:46 +02:00
25b40f99d5 PDF not required if operator created job 2024-10-07 10:05:45 +02:00
350cc2dc78 Added 10 last customer jobs in profile/show 2024-10-03 18:37:02 +02:00
88b5568b2f Display customer jobs in admin/user#index 2024-10-03 18:36:36 +02:00
1dcac5b6f4 Changed stats in admin/dashboard 2024-10-03 18:15:00 +02:00
89b2763a7b Fixed role status info in jobs 2024-10-03 16:49:47 +02:00
6ce1b80360 Fixed seed created_at column 2024-10-03 16:18:52 +02:00
a2269cb6b9 Bundle update 2024-10-03 16:05:30 +02:00
d28f9ce305 Added operate? admin? policy for navbar 2024-10-02 14:34:31 +02:00
b3c79e095b Show admin navbar only for admins 2024-10-02 14:24:35 +02:00
def3fb7ee2 Changed turbo load bar style 2024-10-02 14:21:59 +02:00
c625d61c0d Fixed link_to if customer is not set 2024-10-02 13:53:17 +02:00
337ff9b9d9 Fixed typo 2024-10-02 13:51:15 +02:00
23749c80b8 Fixed operator/job#show without customer 2024-10-02 13:35:31 +02:00
1fd6ba27d5 Changed description of listed items sum 2024-10-02 12:02:27 +02:00
8e111dea0d Changed order of admin/job#index 2024-10-02 12:01:51 +02:00
5c7c079c22 Added some jobs created by operator without costumer id 2024-10-02 12:01:19 +02:00
4ed1cbb528 Added setting infos for canceled role 2024-10-02 11:32:48 +02:00
e2cf5bb19e Renamed job associations for operator and costumer 2024-10-02 09:04:17 +02:00
5237ab9af6 Bugfix admin/user#show 2024-10-02 08:33:42 +02:00
f5eece52ee Bugfix job role info allocation 2024-10-02 08:32:47 +02:00
5b60eb64ad Added has_many assoziations for creator and cashier to jobs, fixed all views according to that changes. Implemented allocation of roles infos when status changes in job model. 2024-10-01 17:45:52 +02:00
de3aa07259 Added navbar hover 2024-09-25 15:39:47 +02:00
d81d44ce9f Switch between calendar and pagy of admin/jobs#index 2024-09-25 15:35:25 +02:00
f7bf922f44 Calendar view in admin/jobs#index optional 2024-09-25 13:43:57 +02:00
3b1a3ab27e Fixed policy 2024-09-24 13:02:25 +02:00
4afcb818f3 Added link to job in jobs#index 2024-09-24 13:01:04 +02:00
2039f00896 Rename policy to match purpose 2024-09-24 12:23:16 +02:00
40481faab8 Added links to users for amin 2024-09-24 12:13:38 +02:00
3af0f6f563 Dashboard link to specific user role 2024-09-24 11:06:33 +02:00
0452c4538b Added more jobs in the past to seeds 2024-09-23 15:18:06 +02:00
82f91c7111 Added sort function and pagy calendar to admin/jobs index 2024-09-23 15:17:37 +02:00
084613bbf5 Refactored user roles 2024-09-23 12:32:45 +02:00
0f877456a4 Added sort function in admin/jobs index 2024-09-23 10:16:16 +02:00
feb37a8a6b Small updates in admin/users index 2024-09-23 09:16:15 +02:00
87a91471d9 Small update in admin/users index 2024-09-23 08:04:50 +02:00
fd30d51f17 Added opacity to back to top link 2024-09-21 01:31:48 +02:00
af8b91cf9c Removed test form field 2024-09-19 14:31:04 +02:00
40 changed files with 850 additions and 445 deletions

19
Gemfile
View File

@@ -1,11 +1,11 @@
source "https://rubygems.org" source "https://rubygems.org"
# Bundle edge Rails instead: gem "rails", github: "rails/rails", branch: "main" # Bundle edge Rails instead: gem "rails", github: "rails/rails", branch: "main"
gem "rails", "~> 7.2.0", ">= 7.2.0" gem "rails", "~> 8.0.0"
# The original asset pipeline for Rails [https://github.com/rails/sprockets-rails] # The original asset pipeline for Rails [https://github.com/rails/sprockets-rails]
gem "sprockets-rails" gem "sprockets-rails"
# Use sqlite3 as the database for Active Record # Use sqlite3 as the database for Active Record
gem "sqlite3", "~> 1.4" gem "sqlite3", ">= 2.1"
# Use the Puma web server [https://github.com/puma/puma] # Use the Puma web server [https://github.com/puma/puma]
gem "puma", ">= 5.0" gem "puma", ">= 5.0"
# Use JavaScript with ESM import maps [https://github.com/rails/importmap-rails] # Use JavaScript with ESM import maps [https://github.com/rails/importmap-rails]
@@ -15,7 +15,7 @@ gem "turbo-rails"
# Hotwire's modest JavaScript framework [https://stimulus.hotwired.dev] # Hotwire's modest JavaScript framework [https://stimulus.hotwired.dev]
gem "stimulus-rails" gem "stimulus-rails"
# Use Tailwind CSS [https://github.com/rails/tailwindcss-rails] # Use Tailwind CSS [https://github.com/rails/tailwindcss-rails]
gem "tailwindcss-rails" gem "tailwindcss-rails", "~> 3.3.1"
# Build JSON APIs with ease [https://github.com/rails/jbuilder] # Build JSON APIs with ease [https://github.com/rails/jbuilder]
gem "jbuilder" gem "jbuilder"
# Use Redis adapter to run Action Cable in production # Use Redis adapter to run Action Cable in production
@@ -30,9 +30,20 @@ gem "bcrypt", "~> 3.1.7"
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem # Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem "tzinfo-data", platforms: %i[windows jruby] gem "tzinfo-data", platforms: %i[windows jruby]
# Use the database-backed adapters for Rails.cache, Active Job, and Action Cable
gem "solid_cache"
gem "solid_queue"
gem "solid_cable"
# Reduces boot times through caching; required in config/boot.rb # Reduces boot times through caching; required in config/boot.rb
gem "bootsnap", require: false gem "bootsnap", require: false
# Deploy this application anywhere as a Docker container [https://kamal-deploy.org]
gem "kamal", require: false
# Add HTTP asset caching/compression and X-Sendfile acceleration to Puma [https://github.com/basecamp/thruster/]
gem "thruster", require: false
# Use Active Storage variants [https://guides.rubyonrails.org/active_storage_overview.html#transforming-images] # Use Active Storage variants [https://guides.rubyonrails.org/active_storage_overview.html#transforming-images]
gem "image_processing", "~> 1.2" gem "image_processing", "~> 1.2"
@@ -89,6 +100,6 @@ gem "csv", "~> 3.3"
# Centralization of locale data collection for Ruby on Rails. # Centralization of locale data collection for Ruby on Rails.
# URL: https://github.com/svenfuchs/rails-i18n # URL: https://github.com/svenfuchs/rails-i18n
gem "rails-i18n", "~> 7.0" gem "rails-i18n", "~> 8.0"
gem "ransack", "~> 4.2" gem "ransack", "~> 4.2"

View File

@@ -1,70 +1,70 @@
GEM GEM
remote: https://rubygems.org/ remote: https://rubygems.org/
specs: specs:
Ascii85 (1.1.1) Ascii85 (2.0.1)
action_policy (0.7.1) action_policy (0.7.5)
ruby-next-core (>= 1.0) ruby-next-core (>= 1.0)
actioncable (7.2.1) actioncable (8.0.2.1)
actionpack (= 7.2.1) actionpack (= 8.0.2.1)
activesupport (= 7.2.1) activesupport (= 8.0.2.1)
nio4r (~> 2.0) nio4r (~> 2.0)
websocket-driver (>= 0.6.1) websocket-driver (>= 0.6.1)
zeitwerk (~> 2.6) zeitwerk (~> 2.6)
actionmailbox (7.2.1) actionmailbox (8.0.2.1)
actionpack (= 7.2.1) actionpack (= 8.0.2.1)
activejob (= 7.2.1) activejob (= 8.0.2.1)
activerecord (= 7.2.1) activerecord (= 8.0.2.1)
activestorage (= 7.2.1) activestorage (= 8.0.2.1)
activesupport (= 7.2.1) activesupport (= 8.0.2.1)
mail (>= 2.8.0) mail (>= 2.8.0)
actionmailer (7.2.1) actionmailer (8.0.2.1)
actionpack (= 7.2.1) actionpack (= 8.0.2.1)
actionview (= 7.2.1) actionview (= 8.0.2.1)
activejob (= 7.2.1) activejob (= 8.0.2.1)
activesupport (= 7.2.1) activesupport (= 8.0.2.1)
mail (>= 2.8.0) mail (>= 2.8.0)
rails-dom-testing (~> 2.2) rails-dom-testing (~> 2.2)
actionpack (7.2.1) actionpack (8.0.2.1)
actionview (= 7.2.1) actionview (= 8.0.2.1)
activesupport (= 7.2.1) activesupport (= 8.0.2.1)
nokogiri (>= 1.8.5) nokogiri (>= 1.8.5)
racc rack (>= 2.2.4)
rack (>= 2.2.4, < 3.2)
rack-session (>= 1.0.1) rack-session (>= 1.0.1)
rack-test (>= 0.6.3) rack-test (>= 0.6.3)
rails-dom-testing (~> 2.2) rails-dom-testing (~> 2.2)
rails-html-sanitizer (~> 1.6) rails-html-sanitizer (~> 1.6)
useragent (~> 0.16) useragent (~> 0.16)
actiontext (7.2.1) actiontext (8.0.2.1)
actionpack (= 7.2.1) actionpack (= 8.0.2.1)
activerecord (= 7.2.1) activerecord (= 8.0.2.1)
activestorage (= 7.2.1) activestorage (= 8.0.2.1)
activesupport (= 7.2.1) activesupport (= 8.0.2.1)
globalid (>= 0.6.0) globalid (>= 0.6.0)
nokogiri (>= 1.8.5) nokogiri (>= 1.8.5)
actionview (7.2.1) actionview (8.0.2.1)
activesupport (= 7.2.1) activesupport (= 8.0.2.1)
builder (~> 3.1) builder (~> 3.1)
erubi (~> 1.11) erubi (~> 1.11)
rails-dom-testing (~> 2.2) rails-dom-testing (~> 2.2)
rails-html-sanitizer (~> 1.6) rails-html-sanitizer (~> 1.6)
activejob (7.2.1) activejob (8.0.2.1)
activesupport (= 7.2.1) activesupport (= 8.0.2.1)
globalid (>= 0.3.6) globalid (>= 0.3.6)
activemodel (7.2.1) activemodel (8.0.2.1)
activesupport (= 7.2.1) activesupport (= 8.0.2.1)
activerecord (7.2.1) activerecord (8.0.2.1)
activemodel (= 7.2.1) activemodel (= 8.0.2.1)
activesupport (= 7.2.1) activesupport (= 8.0.2.1)
timeout (>= 0.4.0) timeout (>= 0.4.0)
activestorage (7.2.1) activestorage (8.0.2.1)
actionpack (= 7.2.1) actionpack (= 8.0.2.1)
activejob (= 7.2.1) activejob (= 8.0.2.1)
activerecord (= 7.2.1) activerecord (= 8.0.2.1)
activesupport (= 7.2.1) activesupport (= 8.0.2.1)
marcel (~> 1.0) marcel (~> 1.0)
activesupport (7.2.1) activesupport (8.0.2.1)
base64 base64
benchmark (>= 0.3)
bigdecimal bigdecimal
concurrent-ruby (~> 1.0, >= 1.3.1) concurrent-ruby (~> 1.0, >= 1.3.1)
connection_pool (>= 2.2.5) connection_pool (>= 2.2.5)
@@ -74,18 +74,23 @@ GEM
minitest (>= 5.1) minitest (>= 5.1)
securerandom (>= 0.3) securerandom (>= 0.3)
tzinfo (~> 2.0, >= 2.0.5) tzinfo (~> 2.0, >= 2.0.5)
uri (>= 0.13.1)
addressable (2.8.7) addressable (2.8.7)
public_suffix (>= 2.0.2, < 7.0) public_suffix (>= 2.0.2, < 7.0)
afm (0.2.2) afm (1.0.0)
ast (2.4.2) ast (2.4.3)
authentication-zero (3.0.2) authentication-zero (3.0.2)
base64 (0.2.0) base64 (0.3.0)
bcrypt (3.1.20) bcrypt (3.1.20)
bigdecimal (3.1.8) bcrypt_pbkdf (1.1.1)
bcrypt_pbkdf (1.1.1-arm64-darwin)
bcrypt_pbkdf (1.1.1-x86_64-darwin)
benchmark (0.4.1)
bigdecimal (3.2.2)
bindex (0.8.1) bindex (0.8.1)
bootsnap (1.18.4) bootsnap (1.18.6)
msgpack (~> 1.2) msgpack (~> 1.2)
brakeman (6.2.1) brakeman (7.1.0)
racc racc
builder (3.3.0) builder (3.3.0)
capybara (3.40.0) capybara (3.40.0)
@@ -99,59 +104,81 @@ GEM
xpath (~> 3.2) xpath (~> 3.2)
childprocess (5.1.0) childprocess (5.1.0)
logger (~> 1.5) logger (~> 1.5)
concurrent-ruby (1.3.4) concurrent-ruby (1.3.5)
connection_pool (2.4.1) connection_pool (2.5.3)
crass (1.0.6) crass (1.0.6)
csv (3.3.0) csv (3.3.5)
date (3.3.4) date (3.4.1)
debug (1.9.2) debug (1.11.0)
irb (~> 1.10) irb (~> 1.10)
reline (>= 0.3.8) reline (>= 0.3.8)
drb (2.2.1) dotenv (3.1.8)
erubi (1.13.0) drb (2.2.3)
faker (3.4.2) ed25519 (1.4.0)
erb (5.0.2)
erubi (1.13.1)
et-orbi (1.3.0)
tzinfo
faker (3.5.2)
i18n (>= 1.8.11, < 2) i18n (>= 1.8.11, < 2)
ffi (1.17.0-aarch64-linux-gnu) ffi (1.17.2-aarch64-linux-gnu)
ffi (1.17.0-arm-linux-gnu) ffi (1.17.2-arm-linux-gnu)
ffi (1.17.0-arm64-darwin) ffi (1.17.2-arm64-darwin)
ffi (1.17.0-x86-linux-gnu) ffi (1.17.2-x86-linux-gnu)
ffi (1.17.0-x86_64-darwin) ffi (1.17.2-x86_64-darwin)
ffi (1.17.0-x86_64-linux-gnu) ffi (1.17.2-x86_64-linux-gnu)
fugit (1.11.1)
et-orbi (~> 1, >= 1.2.11)
raabro (~> 1.4)
globalid (1.2.1) globalid (1.2.1)
activesupport (>= 6.1) activesupport (>= 6.1)
hashery (2.1.2) hashery (2.1.2)
i18n (1.14.5) i18n (1.14.7)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
image_processing (1.13.0) image_processing (1.14.0)
mini_magick (>= 4.9.5, < 5) mini_magick (>= 4.9.5, < 6)
ruby-vips (>= 2.0.17, < 3) ruby-vips (>= 2.0.17, < 3)
importmap-rails (2.0.1) importmap-rails (2.2.2)
actionpack (>= 6.0.0) actionpack (>= 6.0.0)
activesupport (>= 6.0.0) activesupport (>= 6.0.0)
railties (>= 6.0.0) railties (>= 6.0.0)
inline_svg (1.10.0) inline_svg (1.10.0)
activesupport (>= 3.0) activesupport (>= 3.0)
nokogiri (>= 1.6) nokogiri (>= 1.6)
io-console (0.7.2) io-console (0.8.1)
irb (1.14.0) irb (1.15.2)
pp (>= 0.6.0)
rdoc (>= 4.0.0) rdoc (>= 4.0.0)
reline (>= 0.4.2) reline (>= 0.4.2)
jbuilder (2.12.0) jbuilder (2.14.1)
actionview (>= 5.0.0) actionview (>= 7.0.0)
activesupport (>= 5.0.0) activesupport (>= 7.0.0)
json (2.7.2) json (2.13.2)
kredis (1.7.0) kamal (2.7.0)
activesupport (>= 7.0)
base64 (~> 0.2)
bcrypt_pbkdf (~> 1.0)
concurrent-ruby (~> 1.2)
dotenv (~> 3.1)
ed25519 (~> 1.4)
net-ssh (~> 7.3)
sshkit (>= 1.23.0, < 2.0)
thor (~> 1.3)
zeitwerk (>= 2.6.18, < 3.0)
kredis (1.8.0)
activemodel (>= 6.0.0) activemodel (>= 6.0.0)
activesupport (>= 6.0.0) activesupport (>= 6.0.0)
redis (>= 4.2, < 6) redis (>= 4.2, < 6)
language_server-protocol (3.17.0.3) language_server-protocol (3.17.0.5)
launchy (3.0.1) launchy (3.1.1)
addressable (~> 2.8) addressable (~> 2.8)
childprocess (~> 5.0) childprocess (~> 5.0)
logger (~> 1.6)
letter_opener (1.10.0) letter_opener (1.10.0)
launchy (>= 2.2, < 4) launchy (>= 2.2, < 4)
logger (1.6.1) lint_roller (1.1.0)
loofah (2.22.0) logger (1.7.0)
loofah (2.24.1)
crass (~> 1.0.2) crass (~> 1.0.2)
nokogiri (>= 1.12.0) nokogiri (>= 1.12.0)
mail (2.8.1) mail (2.8.1)
@@ -160,202 +187,246 @@ GEM
net-pop net-pop
net-smtp net-smtp
marcel (1.0.4) marcel (1.0.4)
matrix (0.4.2) matrix (0.4.3)
mini_magick (4.13.2) mini_magick (5.3.1)
logger
mini_mime (1.1.5) mini_mime (1.1.5)
minitest (5.25.1) mini_portile2 (2.8.9)
msgpack (1.7.2) minitest (5.25.5)
net-imap (0.4.16) msgpack (1.8.0)
net-imap (0.5.9)
date date
net-protocol net-protocol
net-pop (0.1.2) net-pop (0.1.2)
net-protocol net-protocol
net-protocol (0.2.2) net-protocol (0.2.2)
timeout timeout
net-smtp (0.5.0) net-scp (4.1.0)
net-ssh (>= 2.6.5, < 8.0.0)
net-sftp (4.0.0)
net-ssh (>= 5.0.0, < 8.0.0)
net-smtp (0.5.1)
net-protocol net-protocol
nio4r (2.7.3) net-ssh (7.3.0)
nokogiri (1.16.7-aarch64-linux) nio4r (2.7.4)
nokogiri (1.18.9)
mini_portile2 (~> 2.8.2)
racc (~> 1.4) racc (~> 1.4)
nokogiri (1.16.7-arm-linux) nokogiri (1.18.9-aarch64-linux-gnu)
racc (~> 1.4) racc (~> 1.4)
nokogiri (1.16.7-arm64-darwin) nokogiri (1.18.9-arm-linux-gnu)
racc (~> 1.4) racc (~> 1.4)
nokogiri (1.16.7-x86-linux) nokogiri (1.18.9-arm64-darwin)
racc (~> 1.4) racc (~> 1.4)
nokogiri (1.16.7-x86_64-darwin) nokogiri (1.18.9-x86_64-darwin)
racc (~> 1.4) racc (~> 1.4)
nokogiri (1.16.7-x86_64-linux) nokogiri (1.18.9-x86_64-linux-gnu)
racc (~> 1.4) racc (~> 1.4)
pagy (9.0.9) ostruct (0.6.3)
parallel (1.26.3) pagy (9.4.0)
parser (3.3.5.0) parallel (1.27.0)
parser (3.3.9.0)
ast (~> 2.4.1) ast (~> 2.4.1)
racc racc
pdf-reader (2.12.0) pdf-reader (2.15.0)
Ascii85 (~> 1.0) Ascii85 (>= 1.0, < 3.0, != 2.0.0)
afm (~> 0.2.1) afm (>= 0.2.1, < 2)
hashery (~> 2.0) hashery (~> 2.0)
ruby-rc4 ruby-rc4
ttfunk ttfunk
psych (5.1.2) pp (0.6.2)
prettyprint
prettyprint (0.2.0)
prism (1.4.0)
psych (5.2.6)
date
stringio stringio
public_suffix (6.0.1) public_suffix (6.0.2)
puma (6.4.2) puma (6.6.1)
nio4r (~> 2.0) nio4r (~> 2.0)
pwned (2.4.1) pwned (2.4.1)
raabro (1.4.0)
racc (1.8.1) racc (1.8.1)
rack (3.1.7) rack (3.2.0)
rack-session (2.0.0) rack-session (2.1.1)
base64 (>= 0.1.0)
rack (>= 3.0.0) rack (>= 3.0.0)
rack-test (2.1.0) rack-test (2.2.0)
rack (>= 1.3) rack (>= 1.3)
rackup (2.1.0) rackup (2.2.1)
rack (>= 3) rack (>= 3)
webrick (~> 1.8) rails (8.0.2.1)
rails (7.2.1) actioncable (= 8.0.2.1)
actioncable (= 7.2.1) actionmailbox (= 8.0.2.1)
actionmailbox (= 7.2.1) actionmailer (= 8.0.2.1)
actionmailer (= 7.2.1) actionpack (= 8.0.2.1)
actionpack (= 7.2.1) actiontext (= 8.0.2.1)
actiontext (= 7.2.1) actionview (= 8.0.2.1)
actionview (= 7.2.1) activejob (= 8.0.2.1)
activejob (= 7.2.1) activemodel (= 8.0.2.1)
activemodel (= 7.2.1) activerecord (= 8.0.2.1)
activerecord (= 7.2.1) activestorage (= 8.0.2.1)
activestorage (= 7.2.1) activesupport (= 8.0.2.1)
activesupport (= 7.2.1)
bundler (>= 1.15.0) bundler (>= 1.15.0)
railties (= 7.2.1) railties (= 8.0.2.1)
rails-dom-testing (2.2.0) rails-dom-testing (2.3.0)
activesupport (>= 5.0.0) activesupport (>= 5.0.0)
minitest minitest
nokogiri (>= 1.6) nokogiri (>= 1.6)
rails-html-sanitizer (1.6.0) rails-html-sanitizer (1.6.2)
loofah (~> 2.21) loofah (~> 2.21)
nokogiri (~> 1.14) nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0)
rails-i18n (7.0.9) rails-i18n (8.0.2)
i18n (>= 0.7, < 2) i18n (>= 0.7, < 2)
railties (>= 6.0.0, < 8) railties (>= 8.0.0, < 9)
railties (7.2.1) railties (8.0.2.1)
actionpack (= 7.2.1) actionpack (= 8.0.2.1)
activesupport (= 7.2.1) activesupport (= 8.0.2.1)
irb (~> 1.13) irb (~> 1.13)
rackup (>= 1.0.0) rackup (>= 1.0.0)
rake (>= 12.2) rake (>= 12.2)
thor (~> 1.0, >= 1.2.2) thor (~> 1.0, >= 1.2.2)
zeitwerk (~> 2.6) zeitwerk (~> 2.6)
rainbow (3.1.1) rainbow (3.1.1)
rake (13.2.1) rake (13.3.0)
ransack (4.2.1) ransack (4.3.0)
activerecord (>= 6.1.5) activerecord (>= 6.1.5)
activesupport (>= 6.1.5) activesupport (>= 6.1.5)
i18n i18n
rdoc (6.7.0) rdoc (6.14.2)
erb
psych (>= 4.0.0) psych (>= 4.0.0)
redis (5.3.0) redis (5.4.1)
redis-client (>= 0.22.0) redis-client (>= 0.22.0)
redis-client (0.22.2) redis-client (0.25.2)
connection_pool connection_pool
regexp_parser (2.9.2) regexp_parser (2.11.2)
reline (0.5.10) reline (0.6.2)
io-console (~> 0.5) io-console (~> 0.5)
rexml (3.3.7) rexml (3.4.1)
rubocop (1.66.1) rubocop (1.79.2)
json (~> 2.3) json (~> 2.3)
language_server-protocol (>= 3.17.0) language_server-protocol (~> 3.17.0.2)
lint_roller (~> 1.1.0)
parallel (~> 1.10) parallel (~> 1.10)
parser (>= 3.3.0.2) parser (>= 3.3.0.2)
rainbow (>= 2.2.2, < 4.0) rainbow (>= 2.2.2, < 4.0)
regexp_parser (>= 2.4, < 3.0) regexp_parser (>= 2.9.3, < 3.0)
rubocop-ast (>= 1.32.2, < 2.0) rubocop-ast (>= 1.46.0, < 2.0)
ruby-progressbar (~> 1.7) ruby-progressbar (~> 1.7)
unicode-display_width (>= 2.4.0, < 3.0) unicode-display_width (>= 2.4.0, < 4.0)
rubocop-ast (1.32.3) rubocop-ast (1.46.0)
parser (>= 3.3.1.0) parser (>= 3.3.7.2)
rubocop-minitest (0.36.0) prism (~> 1.4)
rubocop (>= 1.61, < 2.0) rubocop-performance (1.25.0)
rubocop-ast (>= 1.31.1, < 2.0) lint_roller (~> 1.1)
rubocop-performance (1.21.1) rubocop (>= 1.75.0, < 2.0)
rubocop (>= 1.48.1, < 2.0) rubocop-ast (>= 1.38.0, < 2.0)
rubocop-ast (>= 1.31.1, < 2.0) rubocop-rails (2.33.3)
rubocop-rails (2.26.1)
activesupport (>= 4.2.0) activesupport (>= 4.2.0)
lint_roller (~> 1.1)
rack (>= 1.1) rack (>= 1.1)
rubocop (>= 1.52.0, < 2.0) rubocop (>= 1.75.0, < 2.0)
rubocop-ast (>= 1.31.1, < 2.0) rubocop-ast (>= 1.44.0, < 2.0)
rubocop-rails-omakase (1.0.0) rubocop-rails-omakase (1.1.0)
rubocop rubocop (>= 1.72)
rubocop-minitest rubocop-performance (>= 1.24)
rubocop-performance rubocop-rails (>= 2.30)
rubocop-rails ruby-next-core (1.1.2)
ruby-next-core (1.0.3)
ruby-progressbar (1.13.0) ruby-progressbar (1.13.0)
ruby-rc4 (0.1.5) ruby-rc4 (0.1.5)
ruby-vips (2.2.2) ruby-vips (2.2.4)
ffi (~> 1.12) ffi (~> 1.12)
logger logger
rubyzip (2.3.2) rubyzip (3.0.1)
securerandom (0.3.1) securerandom (0.4.1)
selenium-webdriver (4.24.0) selenium-webdriver (4.35.0)
base64 (~> 0.2) base64 (~> 0.2)
logger (~> 1.4) logger (~> 1.4)
rexml (~> 3.2, >= 3.2.5) rexml (~> 3.2, >= 3.2.5)
rubyzip (>= 1.2.2, < 3.0) rubyzip (>= 1.2.2, < 4.0)
websocket (~> 1.0) websocket (~> 1.0)
sprockets (4.2.1) solid_cable (3.0.12)
actioncable (>= 7.2)
activejob (>= 7.2)
activerecord (>= 7.2)
railties (>= 7.2)
solid_cache (1.0.7)
activejob (>= 7.2)
activerecord (>= 7.2)
railties (>= 7.2)
solid_queue (1.2.1)
activejob (>= 7.1)
activerecord (>= 7.1)
concurrent-ruby (>= 1.3.1)
fugit (~> 1.11.0)
railties (>= 7.1)
thor (>= 1.3.1)
sprockets (4.2.2)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
logger
rack (>= 2.2.4, < 4) rack (>= 2.2.4, < 4)
sprockets-rails (3.5.2) sprockets-rails (3.5.2)
actionpack (>= 6.1) actionpack (>= 6.1)
activesupport (>= 6.1) activesupport (>= 6.1)
sprockets (>= 3.0.0) sprockets (>= 3.0.0)
sqlite3 (1.7.3-aarch64-linux) sqlite3 (2.7.3-aarch64-linux-gnu)
sqlite3 (1.7.3-arm-linux) sqlite3 (2.7.3-arm-linux-gnu)
sqlite3 (1.7.3-arm64-darwin) sqlite3 (2.7.3-arm64-darwin)
sqlite3 (1.7.3-x86-linux) sqlite3 (2.7.3-x86-linux-gnu)
sqlite3 (1.7.3-x86_64-darwin) sqlite3 (2.7.3-x86_64-darwin)
sqlite3 (1.7.3-x86_64-linux) sqlite3 (2.7.3-x86_64-linux-gnu)
sshkit (1.24.0)
base64
logger
net-scp (>= 1.1.2)
net-sftp (>= 2.1.2)
net-ssh (>= 2.8.0)
ostruct
stimulus-rails (1.3.4) stimulus-rails (1.3.4)
railties (>= 6.0.0) railties (>= 6.0.0)
stringio (3.1.1) stringio (3.1.7)
tailwindcss-rails (2.7.3) tailwindcss-rails (3.3.2)
railties (>= 7.0.0) railties (>= 7.0.0)
tailwindcss-rails (2.7.3-aarch64-linux) tailwindcss-ruby (~> 3.0)
railties (>= 7.0.0) tailwindcss-ruby (3.4.17)
tailwindcss-rails (2.7.3-arm-linux) tailwindcss-ruby (3.4.17-aarch64-linux)
railties (>= 7.0.0) tailwindcss-ruby (3.4.17-arm-linux)
tailwindcss-rails (2.7.3-arm64-darwin) tailwindcss-ruby (3.4.17-arm64-darwin)
railties (>= 7.0.0) tailwindcss-ruby (3.4.17-x86_64-darwin)
tailwindcss-rails (2.7.3-x86_64-darwin) tailwindcss-ruby (3.4.17-x86_64-linux)
railties (>= 7.0.0) thor (1.4.0)
tailwindcss-rails (2.7.3-x86_64-linux) thruster (0.1.15)
railties (>= 7.0.0) thruster (0.1.15-aarch64-linux)
thor (1.3.2) thruster (0.1.15-arm64-darwin)
timeout (0.4.1) thruster (0.1.15-x86_64-darwin)
thruster (0.1.15-x86_64-linux)
timeout (0.4.3)
ttfunk (1.8.0) ttfunk (1.8.0)
bigdecimal (~> 3.1) bigdecimal (~> 3.1)
turbo-rails (2.0.6) turbo-rails (2.0.16)
actionpack (>= 6.0.0) actionpack (>= 7.1.0)
activejob (>= 6.0.0) railties (>= 7.1.0)
railties (>= 6.0.0)
tzinfo (2.0.6) tzinfo (2.0.6)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
unicode-display_width (2.5.0) unicode-display_width (3.1.5)
useragent (0.16.10) unicode-emoji (~> 4.0, >= 4.0.4)
unicode-emoji (4.0.4)
uri (1.0.3)
useragent (0.16.11)
web-console (4.2.1) web-console (4.2.1)
actionview (>= 6.0.0) actionview (>= 6.0.0)
activemodel (>= 6.0.0) activemodel (>= 6.0.0)
bindex (>= 0.4.0) bindex (>= 0.4.0)
railties (>= 6.0.0) railties (>= 6.0.0)
webrick (1.8.1)
websocket (1.2.11) websocket (1.2.11)
websocket-driver (0.7.6) websocket-driver (0.8.0)
base64
websocket-extensions (>= 0.1.0) websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.5) websocket-extensions (0.1.5)
xpath (3.2.0) xpath (3.2.0)
nokogiri (~> 1.8) nokogiri (~> 1.8)
zeitwerk (2.6.18) zeitwerk (2.7.3)
PLATFORMS PLATFORMS
aarch64-linux aarch64-linux
@@ -379,25 +450,30 @@ DEPENDENCIES
importmap-rails importmap-rails
inline_svg (~> 1.9) inline_svg (~> 1.9)
jbuilder jbuilder
kamal
kredis kredis
letter_opener letter_opener
pagy (~> 9.0) pagy (~> 9.0)
pdf-reader (~> 2.12) pdf-reader (~> 2.12)
puma (>= 5.0) puma (>= 5.0)
pwned pwned
rails (~> 7.2.0, >= 7.2.0) rails (~> 8.0.0)
rails-i18n (~> 7.0) rails-i18n (~> 8.0)
ransack (~> 4.2) ransack (~> 4.2)
redis (>= 4.0.1) redis (>= 4.0.1)
rubocop-rails-omakase rubocop-rails-omakase
selenium-webdriver selenium-webdriver
solid_cable
solid_cache
solid_queue
sprockets-rails sprockets-rails
sqlite3 (~> 1.4) sqlite3 (>= 2.1)
stimulus-rails stimulus-rails
tailwindcss-rails tailwindcss-rails (~> 3.3.1)
thruster
turbo-rails turbo-rails
tzinfo-data tzinfo-data
web-console web-console
BUNDLED WITH BUNDLED WITH
2.5.17 2.7.1

View File

@@ -14,6 +14,11 @@
*= require_self *= require_self
*/ */
.turbo-progress-bar {
height: 5px;
background-color: red;
}
.animate-job-in { .animate-job-in {
animation: fade-in 0.25s ease-out, slide-in 0.25s ease-out; animation: fade-in 0.25s ease-out, slide-in 0.25s ease-out;
} }

View File

@@ -2,7 +2,7 @@ class Admin::DashboardsController < ApplicationController
before_action :authorize! before_action :authorize!
def show def show
@user = User.all @user = User.all
@jobs = Job.currently_working_on @jobs = Job.all
end end
def authorize! def authorize!

View File

@@ -3,7 +3,17 @@ class Admin::JobsController < ApplicationController
before_action :authorize! before_action :authorize!
def index def index
@jobs = Job.all @q = Job.ransack(params[:q])
@pagy, @records = pagy(@jobs, limit: 20) @q.sorts = "id desc" if @q.sorts.empty?
# @pagy, @records = pagy(@q.result(distinct: true), limit: 20)
@calendar, @pagy, @records = pagy_calendar(@q.result, year: {}, month: {}, pagy: {}, active: params[:calendar])
end
def pagy_calendar_period(collection)
[ collection.minimum(:created_at).in_time_zone, collection.maximum(:created_at).in_time_zone ] if collection
end
def pagy_calendar_filter(collection, from, to)
collection.where(created_at: from...to)
end end
end end

View File

@@ -1,7 +1,7 @@
class JobsController < ApplicationController class JobsController < ApplicationController
skip_before_action :authenticate_user!, only: [ :index, :cancel_button ] skip_before_action :authenticate_user!, only: [ :index, :cancel_button, :badge ]
skip_before_action :verified_user!, only: [ :index ] skip_before_action :verified_user!, only: [ :index ]
skip_verify_authorized only: [ :index, :new, :create, :cancel_button ] skip_verify_authorized only: [ :index, :new, :create, :cancel_button, :badge]
# GET /jobs or /jobs.json # GET /jobs or /jobs.json
def index def index
@@ -11,13 +11,13 @@ class JobsController < ApplicationController
# GET /jobs/new # GET /jobs/new
def new def new
@job = Job.new(costumer_firstname: current_user.firstname, costumer_lastname: current_user.lastname) @job = Job.new(customer_firstname: current_user.firstname, customer_lastname: current_user.lastname)
end end
# POST /jobs or /jobs.json # POST /jobs or /jobs.json
def create def create
@job = Job.new(job_params) @job = Job.new(job_params)
@job.costumer = current_user @job.customer = current_user
respond_to do |format| respond_to do |format|
if @job.save if @job.save
@@ -53,6 +53,12 @@ class JobsController < ApplicationController
render partial: "jobs/cancel_button", locals: { job: @job } render partial: "jobs/cancel_button", locals: { job: @job }
end end
def badge
@job = Job.find(params[:id])
render partial: "jobs/badge", locals: { job: @job }
end
private private
def broadcast_update_status_cards_and_start_next_job_button def broadcast_update_status_cards_and_start_next_job_button
@@ -95,6 +101,6 @@ class JobsController < ApplicationController
# Only allow a list of trusted parameters through. # Only allow a list of trusted parameters through.
def job_params def job_params
params.require(:job).permit(:costumer_id, :costumer_firstname, :costumer_lastname, :privacy_policy, :pdf) params.require(:job).permit(:customer_id, :customer_firstname, :customer_lastname, :privacy_policy, :pdf)
end end
end end

View File

@@ -22,8 +22,7 @@ class Operator::JobsController < ApplicationController
def create def create
@job = Job.new(job_params) @job = Job.new(job_params)
@job.created_by_operator = true @job.created_by_operator = true
# TODO: rename costumer to creater? When created by operator the operator is referenced instead of costumer. @job.creator = current_user
@job.costumer = current_user
respond_to do |format| respond_to do |format|
if @job.save if @job.save
@@ -40,6 +39,8 @@ class Operator::JobsController < ApplicationController
def update def update
@job.assign_attributes(job_params) @job.assign_attributes(job_params)
@status_changed = @job.status_changed? @status_changed = @job.status_changed?
@job.current_user = current_user if @status_changed
respond_to do |format| respond_to do |format|
if @job.save if @job.save
broadcast_update_job broadcast_update_job
@@ -113,7 +114,7 @@ class Operator::JobsController < ApplicationController
# Only allow a list of trusted parameters through. # Only allow a list of trusted parameters through.
def job_params def job_params
params.require(:job).permit(:pdf, :operator_id, :costumer_id, :operator_firstname, :operator_lastname, :costumer_firstname, :costumer_lastname, :status, :privacy_policy, :intern, :cost_center, :number_of_plans_a0, :number_of_plans_a1, :number_of_plans_a2, :number_of_plans_a3, :costum_qm_plan) params.require(:job).permit(:pdf, :operator_id, :customer_id, :operator_firstname, :operator_lastname, :customer_firstname, :customer_lastname, :status, :privacy_policy, :intern, :cost_center, :number_of_plans_a0, :number_of_plans_a1, :number_of_plans_a2, :number_of_plans_a3, :costum_qm_plan)
end end
# FIXME: Move broadcast to model though i don't think view logic belongs in the model # FIXME: Move broadcast to model though i don't think view logic belongs in the model

View File

@@ -1,22 +1,28 @@
class Job < ApplicationRecord class Job < ApplicationRecord
belongs_to :operator, class_name: "User", optional: true, counter_cache: :jobs_as_operator_count attr_accessor :current_user
belongs_to :costumer, class_name: "User", optional: true, counter_cache: :jobs_as_costumer_count
belongs_to :customer, class_name: "User", optional: true, counter_cache: :customer_jobs_count, inverse_of: :customer_jobs
belongs_to :operator, class_name: "User", optional: true, counter_cache: :operator_jobs_count, inverse_of: :operator_jobs
belongs_to :creator, class_name: "User", optional: true, counter_cache: :created_jobs_count, inverse_of: :created_jobs
belongs_to :cashier, class_name: "User", optional: true, counter_cache: :cashed_jobs_count, inverse_of: :cashed_jobs
has_one_attached :pdf, dependent: :purge has_one_attached :pdf, dependent: :purge
validates_presence_of :costumer_firstname, :costumer_lastname, :pdf validates_presence_of :customer_firstname, :customer_lastname
validates_presence_of :cost_center, if: :intern validates_presence_of :cost_center, if: :intern
validates_presence_of :pdf, unless: :created_by_operator?
validates :privacy_policy, acceptance: true, unless: :created_by_operator? validates :privacy_policy, acceptance: true, unless: :created_by_operator?
validates :number_of_plans_a0, :number_of_plans_a1, :number_of_plans_a2, :number_of_plans_a3, :costum_qm_plan, numericality: { greater_than_or_equal_to: 0 } validates :number_of_plans_a0, :number_of_plans_a1, :number_of_plans_a2, :number_of_plans_a3, :costum_qm_plan, numericality: { greater_than_or_equal_to: 0 }
validate :acceptable_pdf, unless: :created_by_operator?
validate :acceptable_pdf
before_save :update_printed_at, if: :will_save_change_to_status?
before_save :update_paid_at, if: :will_save_change_to_status?
before_save :update_status_changed_at, if: :will_save_change_to_status? before_save :update_status_changed_at, if: :will_save_change_to_status?
before_save :update_user_status_infos, if: -> { will_save_change_to_status? || new_record? }
before_save :set_cost_qm before_save :set_cost_qm
before_save :calc_cost, if: :printed_pages_changes? before_save :calc_cost, if: :printed_pages_changes?
before_validation :set_costumer_infos, unless: :created_by_operator?, on: :create
before_validation :set_customer_infos, unless: :created_by_operator?, on: :create
# TODO: works only when job is created. Should move analyzer to activestorage : # TODO: works only when job is created. Should move analyzer to activestorage :
# https://discuss.rubyonrails.org/t/active-storage-in-production-lessons-learned-and-in-depth-look-at-how-it-works/83289 # https://discuss.rubyonrails.org/t/active-storage-in-production-lessons-learned-and-in-depth-look-at-how-it-works/83289
@@ -36,6 +42,10 @@ class Job < ApplicationRecord
AVAILABLE_PAGE_FORMATS = [ :a0, :a1, :a2, :a3 ] AVAILABLE_PAGE_FORMATS = [ :a0, :a1, :a2, :a3 ]
# scope :created_as_operator, -> { where created_as_operator: true }
# scope :created_as_customer, -> { where created_as_operator: false }
scope :not_canceled, -> { !canceled }
# NOTE: only named status are returned because of WHERE/IN clause for the enum values # NOTE: only named status are returned because of WHERE/IN clause for the enum values
scope :in_status_order, -> { in_order_of(:status, %w[open printing pickup paid canceled]) } scope :in_status_order, -> { in_order_of(:status, %w[open printing pickup paid canceled]) }
@@ -51,6 +61,8 @@ class Job < ApplicationRecord
scope :status_changed_on_day, lambda { |date| scope :status_changed_on_day, lambda { |date|
where("status_changed_at >= ? AND status_changed_at <= ?", date.beginning_of_day, date.end_of_day) where("status_changed_at >= ? AND status_changed_at <= ?", date.beginning_of_day, date.end_of_day)
} }
scope :created_by_customer, -> { not(:created_by_operator) }
# Returns all jobs with status: open print pickup and jobs from today with status: paid canceled # Returns all jobs with status: open print pickup and jobs from today with status: paid canceled
# paid: only updated_at today # paid: only updated_at today
# canceled: only updated_at today # canceled: only updated_at today
@@ -61,7 +73,7 @@ class Job < ApplicationRecord
.where("status_changed_at >= ?", Time.now.beginning_of_day)) .where("status_changed_at >= ?", Time.now.beginning_of_day))
# .in_status_order # .in_status_order
.order(created_at: :asc) .order(created_at: :asc)
# .order(:costumer_firstname, :costumer_lastname) # .order(:customer_firstname, :customer_lastname)
.with_attached_pdf # scope from activestorage for .includes(pdf_attachment: :blob) .with_attached_pdf # scope from activestorage for .includes(pdf_attachment: :blob)
# .references(:pdf_attachment, :blob) # creates big join table # .references(:pdf_attachment, :blob) # creates big join table
end end
@@ -70,8 +82,8 @@ class Job < ApplicationRecord
Job.where(status: %i[paid canceled]) Job.where(status: %i[paid canceled])
end end
def costumer_fullname def customer_fullname
[ costumer_firstname, " ", costumer_lastname ].join [ customer_firstname, " ", customer_lastname ].join
end end
def acceptable_pdf def acceptable_pdf
@@ -111,31 +123,30 @@ class Job < ApplicationRecord
end end
def self.report_to_csv(jobs) def self.report_to_csv(jobs)
columns = [ "id", "costumer_firstname", "costumer_lastname", "paid_at", "cost" ] columns_readable = [ "ID", "Kunde Vorname", "Kunde Nachname", "Kassierer Vorname", "Kassierer Nachname", "bezahlt am", "Betrag" ]
columns_readable = [ "ID", "Name", "Nachname", "bezahlt am", "Betrag" ]
CSV.generate(col_sep: ";") do |csv| CSV.generate(col_sep: ";") do |csv|
csv << columns_readable csv << columns_readable
jobs.each do |job| jobs.each do |job|
# csv << job.attributes.values_at(*columns) # csv << job.attributes.values_at(*columns)
csv << [ job.id, job.costumer_firstname, job.costumer_lastname, job.paid_at.localtime.strftime("%Y-%m-%d"), job.cost.to_s + "" ] csv << [ job.id, job.customer_firstname, job.customer_lastname, job.cashier_firstname, job.cashier_lastname, job.paid_at.localtime.strftime("%Y-%m-%d"), job.cost.to_s + "" ]
end end
end end
end end
def self.ransackable_attributes(auth_object = nil)
[ "created_at", "id", "customer_firstname", "customer_lastname", "pdf.", "created_by_operator", "number_of_plans_a0", "number_of_plans_a1", "number_of_plans_a2", "number_of_plans_a3", "costum_qm_plan", "cost", "status" ]
end
def self.ransackable_associations(auth_object = nil)
[ "pdf_blob" ]
end
private private
def printed_pages_changes? def printed_pages_changes?
costum_qm_plan_changed? || number_of_plans_a0_changed? || number_of_plans_a1_changed? || number_of_plans_a2_changed? || number_of_plans_a3_changed? costum_qm_plan_changed? || number_of_plans_a0_changed? || number_of_plans_a1_changed? || number_of_plans_a2_changed? || number_of_plans_a3_changed?
end end
def update_printed_at
self.printed_at = Time.now if pickup? || (paid? && printed_at.nil?)
end
def update_paid_at
self.paid_at = Time.now if paid?
end
def update_status_changed_at def update_status_changed_at
self.status_changed_at = Time.now self.status_changed_at = Time.now
end end
@@ -162,6 +173,7 @@ class Job < ApplicationRecord
# TODO: add any check if attachment has changed # TODO: add any check if attachment has changed
# pdfs.each do |pdf| # pdfs.each do |pdf|
if pdf.blob
pdf.blob.open do |file| pdf.blob.open do |file|
# file = ActiveStorage::Blob.service.path_for(pdf.key).to_s # file = ActiveStorage::Blob.service.path_for(pdf.key).to_s
pdf_analyzer = Services::PdfAnalyzer.new(file) pdf_analyzer = Services::PdfAnalyzer.new(file)
@@ -172,13 +184,81 @@ class Job < ApplicationRecord
self.number_of_plans_a3 += pdf_analyzer.pages_a3 self.number_of_plans_a3 += pdf_analyzer.pages_a3
self.costum_qm_plan += pdf_analyzer.costum_qm self.costum_qm_plan += pdf_analyzer.costum_qm
end end
end
# end # end
calc_cost calc_cost
save save
end end
def set_costumer_infos def set_customer_infos
self.costumer_firstname = costumer.firstname self.customer = current_user unless self.customer
self.costumer_lastname = costumer.lastname self.customer_firstname = customer.firstname
self.customer_lastname = customer.lastname
end
def set_operator_infos
self.operator = current_user unless self.operator
self.operator_firstname = operator.firstname
self.operator_lastname = operator.lastname
end
def clear_operator_infos
self.operator = nil
self.operator_firstname = nil
self.operator_lastname = nil
end
def operator_infos_set?
self.operator && self.operator_firstname && self.operator_lastname
end
def set_cashier_infos
self.paid_at = Time.now
self.cashier = current_user unless self.cashier
self.cashier_firstname = cashier.firstname
self.cashier_lastname = cashier.lastname
end
def clear_cashier_infos
self.paid_at = nil
self.cashier = nil
self.cashier_firstname = nil
self.cashier_lastname = nil
end
def reset_operator_and_cashier_infos
clear_operator_infos
clear_cashier_infos
end
def update_user_status_infos
case status.to_sym
when :open
reset_operator_and_cashier_infos
self.printed_at = nil
self.paid_at = nil
when :printing
set_operator_infos
clear_cashier_infos
self.printed_at = nil
when :pickup
set_operator_infos unless operator_infos_set?
clear_cashier_infos
self.printed_at = Time.now unless self.printed_at
when :canceled
clear_operator_infos
clear_cashier_infos
self.printed_at = nil
when :paid
set_operator_infos unless operator_infos_set?
self.printed_at = Time.now unless self.printed_at
set_cashier_infos
self.paid_at = Time.now
end
end
def reset_to_status_open?
status_changed? && open?
end end
end end

View File

@@ -1,7 +1,10 @@
class User < ApplicationRecord class User < ApplicationRecord
has_secure_password has_secure_password
has_many :jobs_as_costumer, foreign_key: :costumer_id, class_name: "Job" # has_many :jobs
has_many :jobs_as_operator, foreign_key: :operator_id, class_name: "Job" has_many :customer_jobs, foreign_key: :customer_id, class_name: "Job"
has_many :operator_jobs, foreign_key: :operator_id, class_name: "Job"
has_many :created_jobs, foreign_key: :creator_id, class_name: "Job"
has_many :cashed_jobs, foreign_key: :cashier_id, class_name: "Job"
generates_token_for :email_verification, expires_in: 2.days do generates_token_for :email_verification, expires_in: 2.days do
email email
@@ -23,8 +26,6 @@ class User < ApplicationRecord
normalizes :email, with: -> { _1.strip.downcase } normalizes :email, with: -> { _1.strip.downcase }
AVAILABLE_ROLES = [ :user, :operator, :admin ]
enum :role, { enum :role, {
user: "user", user: "user",
operator: "operator", operator: "operator",
@@ -44,7 +45,7 @@ class User < ApplicationRecord
end end
def self.ransackable_attributes(auth_object = nil) def self.ransackable_attributes(auth_object = nil)
[ "created_at", "email", "firstname", "id", "jobs_as_costumer_count", "jobs_as_operator_count", "lastname", "role", "verified", "name" ] [ "created_at", "email", "firstname", "id", "customer_jobs_count", "operator_jobs_count", "lastname", "role", "verified", "name" ]
end end
def self.ransackable_associations(auth_object = nil) def self.ransackable_associations(auth_object = nil)

View File

@@ -1,6 +1,6 @@
# Base class for application policies # Base class for application policies
class ApplicationPolicy < ActionPolicy::Base class ApplicationPolicy < ActionPolicy::Base
pre_check :allow_admins, :deny_verified_users pre_check :allow_admins, :deny_unverified_users
# admin is good! :) # admin is good! :)
def allow_admins def allow_admins
@@ -8,10 +8,18 @@ class ApplicationPolicy < ActionPolicy::Base
end end
# no email verification no rights # no email verification no rights
def deny_verified_users def deny_unverified_users
deny! unless user.verified? deny! unless user.verified?
end end
def operate?
allow! if user.operator? || user.admin?
end
def admin?
allow! if user.admin?
end
# Configure additional authorization contexts here # Configure additional authorization contexts here
# (`user` is added by default). # (`user` is added by default).
# #

View File

@@ -2,7 +2,7 @@ class JobPolicy < ApplicationPolicy
skip_pre_check :allow_admins, only: :cancel? skip_pre_check :allow_admins, only: :cancel?
def cancel? def cancel?
record.open? && (user == record.costumer || user.operator? || user.admin?) record.open? && (user == record.customer || user.operator? || user.admin?)
end end
# See https://actionpolicy.evilmartians.io/#/writing_policies # See https://actionpolicy.evilmartians.io/#/writing_policies

View File

@@ -15,13 +15,13 @@
<p class="size-28"><%= @user.size %></p> <p class="size-28"><%= @user.size %></p>
</div> </div>
<% end %> <% end %>
<%= link_to admin_users_path() do %> <%= link_to admin_users_path(q: { role_eq: "operator"}) do %>
<div class="p-4 shadow bg-gray-50"> <div class="p-4 shadow bg-gray-50">
<h2 class="text-xl">Tutoren:</h2> <h2 class="text-xl">Operatoren:</h2>
<p class="size-28"><%= @user.operator.size %></p> <p class="size-28"><%= @user.operator.size %></p>
</div> </div>
<% end %> <% end %>
<%= link_to admin_users_path() do %> <%= link_to admin_users_path(q: { role_eq: "admin"}) do %>
<div class="p-4 shadow bg-gray-50"> <div class="p-4 shadow bg-gray-50">
<h2 class="text-xl">Admins:</h2> <h2 class="text-xl">Admins:</h2>
<p class="size-28"><%= @user.admin.size %></p> <p class="size-28"><%= @user.admin.size %></p>
@@ -29,10 +29,15 @@
<% end %> <% end %>
</div> </div>
<div class="flex space-x-2"> <div class="flex space-x-2">
<h2>Aktueller Monat</h2> <%= link_to operator_jobs_path() do %>
<div class="p-4 shadow bg-gray-50">
<h2 class="text-xl">Heutige Druckaufträge:</h2>
<p class="size-28"><%= @jobs.currently_working_on.size %></p>
</div>
<% end %>
<%= link_to admin_jobs_path() do %> <%= link_to admin_jobs_path() do %>
<div class="p-4 shadow bg-gray-50"> <div class="p-4 shadow bg-gray-50">
<h2 class="text-xl">aktuelle Druckaufträge:</h2> <h2 class="text-xl">Alle Druckaufträge:</h2>
<p class="size-28"><%= @jobs.size %></p> <p class="size-28"><%= @jobs.size %></p>
</div> </div>
<% end %> <% end %>

View File

@@ -6,22 +6,21 @@
</span> </span>
<% end %> <% end %>
</td> </td>
<td class="p-2 py-3 text-center"> <td class="p-2 py-3">
<% if job.pdf.attached? && job.pdf.previewable? %> <%= link_to_if job.customer, job.customer_firstname, ( job.customer ? admin_user_path(job.customer) : ""), target: "_top" %>
<%= link_to job.pdf, target: "_blank", target: "_top" do %>
<span class="badge badge-hover">
<%= icon("eye", class:"icon") %>
</span>
<% end %>
<% end %>
</td> </td>
<td class="p-2 py-3"> <td class="p-2 py-3">
<%= link_to job.costumer_fullname, admin_user_path(job.costumer), target: "_top" %> <%= link_to_if job.customer, job.customer_lastname, ( job.customer ? admin_user_path(job.customer) : ""), target: "_top" %>
</td> </td>
<td class="p-2 py-3"> <td class="p-2 py-3">
<% if job.pdf.attached? %> <% if job.pdf.attached? %>
<%#= link_to job.pdf.filename, rails_blob_path(job.pdf, disposition: "attachment") %> <%#= link_to job.pdf.filename, rails_blob_path(job.pdf, disposition: "attachment") %>
<%= link_to truncate(job.pdf.filename.to_s, length: 45), job.pdf, download:true, target: "_top" %> <%= link_to truncate(job.pdf.filename.to_s, length: 45), job.pdf, download:true, target: "_top" %>
<% if job.pdf.previewable? %>
<%= link_to job.pdf, target: "_blank", target: "_top" do %>
<span class="badge badge-hover mr-1"><%= icon("eye", class:"icon text-hsrm-gray size-6 inline") %></span>
<% end %>
<% end %>
<%= link_to job.pdf, download:true, target: "_top" do %> <%= link_to job.pdf, download:true, target: "_top" do %>
<span class="badge badge-hover"> <span class="badge badge-hover">
<%= icon("document-arrow-down", class: "text-hsrm-gray size-6 inline", title: "Download") %> <%= icon("document-arrow-down", class: "text-hsrm-gray size-6 inline", title: "Download") %>
@@ -32,24 +31,33 @@
</td> </td>
<% Job::AVAILABLE_PAGE_FORMATS.each do |din| %> <% Job::AVAILABLE_PAGE_FORMATS.each do |din| %>
<td class="p-1 py-3"> <td class="p-1 py-3">
<span class="badge">
<%= job.public_send("number_of_plans_#{din}") if job.respond_to? "number_of_plans_#{din}" %> <%= job.public_send("number_of_plans_#{din}") if job.respond_to? "number_of_plans_#{din}" %>
</span>
</td> </td>
<% end %> <% end %>
<td class="p-2 py-3 text-right"> <td class="p-2 py-3 text-right">
<span class="badge">
<%= number_with_delimiter job.costum_qm_plan.round(2) %> m² <%= number_with_delimiter job.costum_qm_plan.round(2) %> m²
</span>
</td> </td>
<td class="w-24 p-2 py-3 text-right"> <td class="w-24 p-2 py-3 text-right">
<span class="badge">
<%= number_to_currency job.cost, locale: :de %> <%= number_to_currency job.cost, locale: :de %>
</span>
</td> </td>
<td class="p-2 py-3"> <td class="p-2 py-3 text-center">
<%= l job.created_at.localtime.to_date %>
</td>
<td class="p-2 py-3 text-center">
<% if job.created_by_operator %>
<%= link_to admin_user_path(job.creator) do %>
<span class="badge badge-status">Operator</span>
<% end %>
<% else %>
<%= link_to admin_user_path(job.creator) do %>
<span class="badge badge-status badge-hover">Kunden</span>
<% end %>
<% end %>
</span>
</td>
<td class="p-2 py-3">
<span class="badge badge-status text-status-<%= job.status.to_sym %> bg-status-<%= job.status %>-light"> <span class="badge badge-status text-status-<%= job.status.to_sym %> bg-status-<%= job.status %>-light">
<%= job.status %> <%= job.status %>
</span> </span>
</td> </td>
</tr> </tr>

View File

@@ -1,22 +1,49 @@
<%= turbo_frame_tag "admin_jobs" do %> <% content_for :title, "Benutzerliste" %>
<div> <h1 class="font-bold text-4xl py-4 text-hsrm-gray">Alle Druckaufträge</h1>
<h1 class="font-bold text-4xl py-4 text-hsrm-gray">Alle Druckaufträge</h1> <% if @calendar %>
<%= link_to icon("magnifying-glass", class: "icon size-5 mr-3") + 'Filter', admin_jobs_path(), class: "px-4 py-2 hover:bg-gray-100 hover:text-hsrm-red border-b-4 hover:border-hsrm-red-light" %>
<%= content_tag :span, icon("calendar", class: "icon size-5 mr-3") + 'Kalender', class: "px-4 py-2 border-b-4 border-hsrm-red-light" %>
<div class="pt-2">
<%== pagy_nav(@calendar[:year]) %>
<%== pagy_nav(@calendar[:month]) if @calendar[:month] %>
</div>
<% else %>
<%= content_tag :span, icon("magnifying-glass", class: "icon size-5 mr-3") + 'Filter', class: "px-4 py-2 border-b-4 border-hsrm-red-light" %>
<%= link_to icon("calendar", class: "icon size-5 mr-3") + 'Kalender', admin_jobs_path(calendar: true), class: "px-4 py-2 hover:bg-gray-100 hover:text-hsrm-red border-b-4 hover:border-hsrm-red-light" %>
<div class="flex items-center justify-between py-4">
<%= search_form_for @q, data: { turbo_frame: :admin_jobs, turbo_action: 'advance' }, url: admin_jobs_path() do |f| %>
<%= f.search_field :customer_firstname_or_customer_lastname_or_pdf_blob_filename_cont, placeholder: "Suchen", oninput: 'this.form.requestSubmit();' %>
<%= f.label :status_eq, "Status:" %>
<%= f.select :status_eq, Job.statuses.keys, {include_blank: "alle"}, onchange: 'this.form.requestSubmit();' %>
<%= f.label :created_by_operator_eq, "Erstellt vom:" %>
<%= f.select :created_by_operator_eq, [["Operator", true],["Kunden", false]], {include_blank: "alle"}, onchange: 'this.form.requestSubmit();' %>
<%= f.submit "Filter anwenden", class: "py-2 px-3 bg-hsrm-red hover:bg-hsrm-red-light shadow-lg text-white inline-block font-medium cursor-pointer" %>
</div>
<% end %>
<% end %>
<div>
<%= turbo_frame_tag "admin_jobs", data: { turbo_action: 'advance'} do %>
<div class="flex justify-between items-center">
<%== pagy_nav(@pagy) %> <%== pagy_nav(@pagy) %>
<%== pagy_info(@pagy) %>
</div>
<div class="min-w-full overflow-auto shadow-lg pt-2"> <div class="min-w-full overflow-auto shadow-lg pt-2">
<table class="w-full py-8 table-auto"> <table class="w-full py-8 table-auto">
<thead class="font-semibold tracking-wide bg-gray-200 border-b-2 border-gray-300 text text-hsrm-gray"> <thead class="font-semibold tracking-wide bg-gray-200 border-b-2 border-gray-300 text text-hsrm-gray">
<tr> <tr>
<th class="w-1 p-2 py-3 text-center"> ID </th> <th class="w-1 p-2 py-3 text-center text-nowrap"><%= sort_link(@q, :id, "ID", ) %></th>
<th class="w-1 p-2 py-3 text-center"> Vorschau </th> <th class="min-w-24 p-2 py-3 text-left"><%= sort_link(@q, :customer_firstname, "Vorname") %></th>
<th class="w-1 p-2 py-3 text-left"> Auftraggeber </th> <th class="min-w-24 p-2 py-3 text-left"><%= sort_link(@q, :customer_lastname, "Nachname") %></th>
<th class="p-2 py-3 text-left"> PDF </th> <th class="p-2 py-3 text-left"><%= sort_link(@q, :pdf_blob_filename, "PDF") %></th>
<th class="w-1 p-1 py-3 text-left"> A0 </th> <th class="w-1 p-1 py-3 text-left text-nowrap"><%= sort_link(@q, :number_of_plans_a0, "A0") %></th>
<th class="w-1 p-1 py-3 text-left"> A1 </th> <th class="w-1 p-1 py-3 text-left text-nowrap"><%= sort_link(@q, :number_of_plans_a1, "A1") %></th>
<th class="w-1 p-1 py-3 text-left"> A2 </th> <th class="w-1 p-1 py-3 text-left text-nowrap"><%= sort_link(@q, :number_of_plans_a2, "A2") %></th>
<th class="w-1 p-1 py-3 text-left"> A3 </th> <th class="w-1 p-1 py-3 text-left text-nowrap"><%= sort_link(@q, :number_of_plans_a3, "A3") %></th>
<th class="w-1 p-2 py-3 text-center text-nowrap"> no DIN </th> <th class="w-1 p-2 py-3 text-center text-nowrap"><%= sort_link(@q, :costum_qm_plan, "no DIN") %></th>
<th class="w-1 p-2 py-3 text-center"> Kosten </th> <th class="w-1 p-2 py-3 text-center"><%= sort_link(@q, :cost, "Kosten") %></th>
<th class="w-1 p-2 py-3 text-center"> Status </th> <th class="w-1 p-2 py-3 text-center text-nowrap"><%= sort_link(@q, :created_at, "Erstellt am") %></th>
<th class="w-1 p-2 py-3 text-center text-nowrap"><%= sort_link(@q, :created_by_operator, "Erstellt von") %></th>
<th class="w-1 p-2 py-3 text-center"><%= sort_link(@q, :status, "Status") %></th>
</tr> </tr>
</thead> </thead>
<tbody id='jobs' class="divide-y divivde-gray-300"> <tbody id='jobs' class="divide-y divivde-gray-300">
@@ -24,6 +51,8 @@
</tbody> </tbody>
</table> </table>
</div> </div>
<div class="flex justify-between items-center">
<%== pagy_nav(@pagy) %> <%== pagy_nav(@pagy) %>
<%== pagy_info(@pagy, item_name: "Users") %>
</div> </div>
<% end %> <% end %>

View File

@@ -6,6 +6,10 @@
</p> </p>
<p><%= user.email %></p> <p><%= user.email %></p>
<p>E-Mail Verifiziert: <p>E-Mail Verifiziert:
<%= icon bool_icon(user.verified), class: "icon #{user.verified ? "text-green-600" : "text-red-600"}" %> <%= icon bool_icon(user.verified), class: "icon #{user.verified ? "text-green-600" : "text-red-600"}" %></p>
<p><%= user.created_at %></p> <p><%= user.created_at %></p>
</div> <p>Druckaufträge als Kunde: <%= @user.customer_jobs.size %></p>
<p>davon abgebrochen: <%= @user.customer_jobs.canceled.size %></p>
<p>Druckaufträge als Operator: <%= @user.operator_jobs.size %></p>
<p>Druckaufträge kassiert: <%= @user.cashed_jobs.size %></p>
</div>

View File

@@ -18,14 +18,14 @@
<%= highlight user.email, [params.dig(:q, :firstname_or_lastname_or_email_cont).to_s, params.dig(:q, :email_start).to_s] %> <%= highlight user.email, [params.dig(:q, :firstname_or_lastname_or_email_cont).to_s, params.dig(:q, :email_start).to_s] %>
</td> </td>
<td class="p-2 py-3 text-right"> <td class="p-2 py-3 text-right">
<%= user.jobs_as_costumer.where.not(status: :canceled).size %> <%= user.customer_jobs.not_canceled.size %>
</td> </td>
<td class="p-2 py-3 text-center"> <td class="p-2 py-3 text-center">
<%= l user.created_at.localtime.to_date %> <%= l user.created_at.localtime.to_date %>
</td> </td>
<td class="p-2 py-3 text-nowrap"> <td class="p-2 py-3 text-nowrap">
<span class="inline-flex -space-x-px overflow-hidden rounded-md bg-white shadow divide-x divide-gray-300"> <span class="inline-flex -space-x-px overflow-hidden bg-white divide-x divide-gray-300 rounded-md shadow">
<% User::AVAILABLE_ROLES.each do |role| %> <% User.roles.keys.each do |role| %>
<% if allowed_to? :change_role?, user %> <% if allowed_to? :change_role?, user %>
<% if user.role == role.to_s %> <% if user.role == role.to_s %>
<%= button_to role, admin_user_path(user), method: :patch, params: { user: { role: role }}, form_class: "inline", class: "inline-block px-4 py-2 text-sm uppercase font-medium focus:relative bg-role-#{role}-light text-#{role}" %> <%= button_to role, admin_user_path(user), method: :patch, params: { user: { role: role }}, form_class: "inline", class: "inline-block px-4 py-2 text-sm uppercase font-medium focus:relative bg-role-#{role}-light text-#{role}" %>
@@ -36,7 +36,7 @@
<% if user.role == role.to_s %> <% if user.role == role.to_s %>
<span class="inline-block px-4 py-2 text-sm uppercase font-medium text-<%= role %> bg-role-<%= role %>-light focus:relative"><%= role %></span> <span class="inline-block px-4 py-2 text-sm uppercase font-medium text-<%= role %> bg-role-<%= role %>-light focus:relative"><%= role %></span>
<% else %> <% else %>
<span class="inline-block px-4 py-2 text-sm uppercase font-medium text-gray-400 focus:relative"><%= role %></span> <span class="inline-block px-4 py-2 text-sm font-medium text-gray-400 uppercase focus:relative"><%= role %></span>
<% end %> <% end %>
<% end %> <% end %>
<% end %> <% end %>

View File

@@ -1,8 +1,7 @@
<div class="w-full"> <% content_for :title, "Benutzerliste" %>
<% content_for :title, "Current Print Jobs" %> <h1 class="text-4xl font-bold text-hsrm-gray">Benutzerliste</h1>
<h1 class="text-4xl font-bold text-hsrm-gray">Benutzerliste</h1> <div class="flex items-center justify-between py-4">
<div class="flex items-center justify-between py-4"> <!-- <div>
<div>
<div class="mt-6 md:flex md:items-center md:justify-between"> <div class="mt-6 md:flex md:items-center md:justify-between">
<div class="relative flex items-center mt-4 md:mt-0"> <div class="relative flex items-center mt-4 md:mt-0">
<span class="absolute"> <span class="absolute">
@@ -14,6 +13,12 @@
</div> </div>
<fieldset class="inline-flex -space-x-px overflow-hidden rounded-md border bg-white shadow-sm"> <fieldset class="inline-flex -space-x-px overflow-hidden rounded-md border bg-white shadow-sm">
<legend class="sr-only">Color</legend> <legend class="sr-only">Color</legend>
<div>
<label for="ColorBlack" class="inline-block selecet:bg-green-500 px-4 py-2 text-sm font-medium uppercase text-gray-800 hover:bg-green-300 focus:relative">
<input type="radio" name="ColorOption" value="ColorBlack" id="ColorBlack" class="sr-only" checked />
<p class="text-sm font-medium">Alle</p>
</label>
</div>
<div> <div>
<label for="ColorBlack" class="inline-block selecet:bg-green-500 px-4 py-2 text-sm font-medium uppercase text-gray-800 hover:bg-green-300 focus:relative"> <label for="ColorBlack" class="inline-block selecet:bg-green-500 px-4 py-2 text-sm font-medium uppercase text-gray-800 hover:bg-green-300 focus:relative">
<input type="radio" name="ColorOption" value="ColorBlack" id="ColorBlack" class="sr-only" checked /> <input type="radio" name="ColorOption" value="ColorBlack" id="ColorBlack" class="sr-only" checked />
@@ -33,7 +38,7 @@
</label> </label>
</div> </div>
</fieldset> </fieldset>
</div> </div> -->
<%= search_form_for @q, data: { turbo_frame: :admin_users, turbo_action: 'advance' }, url: admin_users_path do |f| %> <%= search_form_for @q, data: { turbo_frame: :admin_users, turbo_action: 'advance' }, url: admin_users_path do |f| %>
<%#= f.label :firstname_cont, "Vorname:" %> <%#= f.label :firstname_cont, "Vorname:" %>
<%#= f.search_field :firstname_cont, oninput: 'this.form.requestSubmit();' %> <%#= f.search_field :firstname_cont, oninput: 'this.form.requestSubmit();' %>
@@ -41,24 +46,26 @@
<%#= f.search_field :lastname_cont, oninput: 'this.form.requestSubmit();' %> <%#= f.search_field :lastname_cont, oninput: 'this.form.requestSubmit();' %>
<%#= f.label :email_start, "E-Mail:" %> <%#= f.label :email_start, "E-Mail:" %>
<%#= f.search_field :email_start, oninput: 'this.form.requestSubmit();' %> <%#= f.search_field :email_start, oninput: 'this.form.requestSubmit();' %>
<%#= f.label :name_or_email_cont, "Name oder E-Mail:" %> <%#= f.label :firstname_or_lastname_or_email_cont, "Suchen:" %>
<%= f.search_field :firstname_or_lastname_or_email_cont, oninput: 'this.form.requestSubmit();' %> <%= f.search_field :firstname_or_lastname_or_email_cont, placeholder: "Suchen", oninput: 'this.form.requestSubmit();' %>
<%= f.label :created_at_dategteq, "Erstellt von:" %> <%= f.label :created_at_dategteq, "Registriert von:" %>
<%= f.date_field :created_at_dategteq, onchange: 'this.form.requestSubmit();' %> <%= f.date_field :created_at_dategteq, onchange: 'this.form.requestSubmit();' %>
<%= f.label :created_at_datelteq, "bis:" %> <%= f.label :created_at_datelteq, "bis:" %>
<%= f.date_field :created_at_datelteq, onchange: 'this.form.requestSubmit();' %> <%= f.date_field :created_at_datelteq, onchange: 'this.form.requestSubmit();' %>
<%= f.label :verified_eq, "E-Mail validiert:" %> <%= f.label :verified_eq, "E-Mail validiert:" %>
<%= f.select :verified_eq, [true,false], {include_blank: true}, onchange: 'this.form.requestSubmit();' %> <%= f.select :verified_eq, [true,false], {include_blank: "alle"}, onchange: 'this.form.requestSubmit();' %>
<%= f.label :role_eq, "Rolle:" %> <%= f.label :role_eq, "Rolle:" %>
<%#= f.collection_radio_buttons :role_eq, User::AVAILABLE_ROLES, 0,0 %> <%#= f.collection_radio_buttons :role_eq, User::AVAILABLE_ROLES, 0,0 %>
<%= f.select :role_eq, User::AVAILABLE_ROLES, {include_blank: true}, onchange: 'this.form.requestSubmit();' %> <%= f.select :role_eq, User.roles.keys, {include_blank: "alle"}, onchange: 'this.form.requestSubmit();' %>
<%#= f. %> <%#= f. %>
<%= f.submit "Filter anwenden", class: "py-2 px-3 bg-hsrm-red hover:bg-hsrm-red-light shadow-lg text-white inline-block font-medium cursor-pointer" %> <%= f.submit "Filter anwenden", class: "py-2 px-3 bg-hsrm-red hover:bg-hsrm-red-light shadow-lg text-white inline-block font-medium cursor-pointer" %>
<% end %> <% end %>
</div> </div>
</div> <%= turbo_frame_tag "admin_users", data: { turbo_action: 'advance'} do %>
<%= turbo_frame_tag "admin_users", data: { turbo_action: 'advance'} do %> <div class="flex justify-between items-center">
<%== pagy_nav(@pagy) %> <%== pagy_nav(@pagy) %>
<%== pagy_info(@pagy) %>
</div>
<div class="min-w-full overflow-auto shadow-lg pt-2"> <div class="min-w-full overflow-auto shadow-lg pt-2">
<table class="w-full py-8 table-auto"> <table class="w-full py-8 table-auto">
<thead class="font-semibold tracking-wide bg-gray-200 border-b-2 border-gray-300 text text-hsrm-gray"> <thead class="font-semibold tracking-wide bg-gray-200 border-b-2 border-gray-300 text text-hsrm-gray">
@@ -67,7 +74,7 @@
<th class="min-w-24 p-2 py-3 text-left"><%= sort_link(@q, :firstname, "Vorname") %></th> <th class="min-w-24 p-2 py-3 text-left"><%= sort_link(@q, :firstname, "Vorname") %></th>
<th class="min-w-24 p-2 py-3 text-left"><%= sort_link(@q, :lastname, "Nachname") %></th> <th class="min-w-24 p-2 py-3 text-left"><%= sort_link(@q, :lastname, "Nachname") %></th>
<th class="p-2 py-3 text-left"><%= sort_link(@q, :email, "E-Mail-Adresse") %></th> <th class="p-2 py-3 text-left"><%= sort_link(@q, :email, "E-Mail-Adresse") %></th>
<th class="w-1 p-2 py-3 text-right text-nowrap"><%= sort_link(@q, :jobs_as_costumer_count, "# Jobs") %></th> <th class="w-1 p-2 py-3 text-right text-nowrap"><%= sort_link(@q, :customer_jobs_count, "# Jobs") %></th>
<th class="w-1 p-2 py-3 text-center text-nowrap"><%= sort_link(@q, :created_at, "Registriert am") %></th> <th class="w-1 p-2 py-3 text-center text-nowrap"><%= sort_link(@q, :created_at, "Registriert am") %></th>
<th class="w-1 p-2 py-3 text-center"><%= sort_link(@q, :role, "Rolle") %></th> <th class="w-1 p-2 py-3 text-center"><%= sort_link(@q, :role, "Rolle") %></th>
</tr> </tr>
@@ -77,6 +84,9 @@
</tbody> </tbody>
</table> </table>
</div> </div>
<div class="flex justify-between items-center">
<%== pagy_nav(@pagy) %> <%== pagy_nav(@pagy) %>
<%== pagy_info(@pagy, item_name: "Users") %>
</div> </div>
</div>
<% end %> <% end %>

View File

@@ -1,14 +1,8 @@
<div> <div>
<h1 class="text-4xl font-bold">Benutzer Details</h1> <h1 class="text-4xl font-bold text-hsrm-gray py-4">Benutzer Details</h1>
<%= render partial: 'user', locals: { user: @user } %> <%= render partial: 'user', locals: { user: @user } %>
</div> </div>
<p>Some Stats: <h1 class="py-4 text-4xl font-bold text-hsrm-gray">Die letzten 10 Druckaufträge</h1>
<ul>
<li>Druckaufträge insgesammt: <%= @user.jobs_as_costumer.size %></li>
<li>davon abgebrochen: <%= @user.jobs_as_costumer.canceled.size %></li>
<li>letzten 5 Druckaufträge</li>
</ul>
</p>
<div class="min-w-full overflow-auto shadow-lg"> <div class="min-w-full overflow-auto shadow-lg">
<table class="w-full py-8 table-auto"> <table class="w-full py-8 table-auto">
<thead class="font-semibold tracking-wide bg-gray-200 border-b-2 border-gray-300 text text-hsrm-gray"> <thead class="font-semibold tracking-wide bg-gray-200 border-b-2 border-gray-300 text text-hsrm-gray">
@@ -27,7 +21,7 @@
</tr> </tr>
</thead> </thead>
<tbody id='jobs' class="divide-y divivde-gray-300"> <tbody id='jobs' class="divide-y divivde-gray-300">
<%= render partial: "jobs/job_tr", collection: @user.jobs_as_costumer.limit(5), as: :job, locals: { no_actions: true } %> <%= render partial: "jobs/job_tr", collection: @user.customer_jobs.order(created_at: :desc).limit(10), as: :job, locals: { no_actions: true } %>
</tbody> </tbody>
</table> </table>
</div> </div>

View File

@@ -0,0 +1,3 @@
<%= turbo_frame_tag dom_id(job, :badge) do %>
<%= link_to_if user_signed_in? && allowed_to?(:show, job, namespace: :Operator), job.id, operator_job_path(job), target: "_blank" %>
<% end %>

View File

@@ -11,12 +11,12 @@
</div> </div>
<% end %> <% end %>
<div class="my-5"> <div class="my-5">
<%= form.label :costumer_firstname, 'Vorname' %> <%= form.label :customer_firstname, 'Vorname' %>
<%= form.text_field :costumer_firstname, disabled: true ,class: "block shadow-lg rounded-md border border-hsrm-gray outline-none px-3 py-2 mt-2 w-full" %> <%= form.text_field :customer_firstname, disabled: true ,class: "block shadow-lg rounded-md border border-hsrm-gray outline-none px-3 py-2 mt-2 w-full" %>
</div> </div>
<div class="my-5"> <div class="my-5">
<%= form.label :costumer_lastname, 'Nachname' %> <%= form.label :customer_lastname, 'Nachname' %>
<%= form.text_field :costumer_lastname, disabled: true ,class: "block shadow-lg rounded-md border border-hsrm-gray outline-none px-3 py-2 mt-2 w-full" %> <%= form.text_field :customer_lastname, disabled: true ,class: "block shadow-lg rounded-md border border-hsrm-gray outline-none px-3 py-2 mt-2 w-full" %>
</div> </div>
<div> <div>
<%= form.label :pdf, "Plan auswählen (PDF-Format)" %> <%= form.label :pdf, "Plan auswählen (PDF-Format)" %>

View File

@@ -1,17 +1,26 @@
<tr id="<%= dom_id job %>" class="bg-status-<%= job.status %>-light odd:bg-opacity-25 even:bg-opacity-15 text-hsrm-gray whitespace-nowrap hover:bg-opacity-30" data-stream-enter-class="animate-flash-increase"> <tr id="<%= dom_id job %>" class="bg-status-<%= job.status %>-light odd:bg-opacity-25 even:bg-opacity-15 text-hsrm-gray whitespace-nowrap hover:bg-opacity-30" data-stream-enter-class="animate-flash-increase">
<td class="p-2 py-3 text-center"> <td class="p-2 py-3 text-center">
<span class="badge badge-xl text-status-<%= job.status %> bg-status-<%= job.status %>-light rounded-lg shadow"> <span class="badge badge-xl text-status-<%= job.status %> bg-status-<%= job.status %>-light rounded-lg shadow">
<%= job.id %> <% # TODO: Refactor to helper function %>
<% if defined?(no_turbo_stream) && no_turbo_stream %>
<%= turbo_frame_tag dom_id(job, :badge) do %>
<%= render partial: "jobs/badge", locals: { job: job } %>
<% end %>
<% else %>
<%= turbo_frame_tag dom_id(job, :badge), src: badge_job_path(job), loading: 'lazy' do %>
<%= icon("ellipsis-horizontal-circle", class: "icon icon-disabled size-10", title: "Loading...") %>
<% end %>
<% end %>
</span> </span>
</td> </td>
<td class="p-2 py-3 text-center"> <td class="p-2 py-3 text-center">
<% if job.pdf.attached? && job.pdf.previewable? %> <% if job.pdf.attached? && job.pdf.previewable? %>
<%= image_tag(url_for(job.pdf.blob.preview(resize_to_limit: [100, 100])), class: "shadow") %> <%= image_tag(url_for(job.pdf.blob.preview(resize_to_limit: [100, 100])), class: "shadow") %>
<%#= image_tag job.pdf.preview(resize_to_limit: [50, 50]), class: "mx-auto" %> <%# = image_tag job.pdf.preview(resize_to_limit: [50, 50]), class: "mx-auto" %>
<% end %> <% end %>
</td> </td>
<td class="p-2 py-3"> <td class="p-2 py-3">
<%= job.costumer_fullname %> <%= job.customer_fullname %>
</td> </td>
<td class="p-2 py-3"> <td class="p-2 py-3">
<% if job.pdf.attached? %> <% if job.pdf.attached? %>

View File

@@ -23,7 +23,7 @@
<th class="w-1 p-2 py-3 text-center">Action</th> <th class="w-1 p-2 py-3 text-center">Action</th>
</tr> </tr>
</thead> </thead>
<tbody id='jobs' class="divide-y divivde-gray-300"> <tbody id="jobs" class="divide-y divivde-gray-300">
<%= render partial: "job_tr", collection: @jobs, as: :job, locals: { no_turbo_stream: @no_turbo_stream } %> <%= render partial: "job_tr", collection: @jobs, as: :job, locals: { no_turbo_stream: @no_turbo_stream } %>
</tbody> </tbody>
</table> </table>

View File

@@ -1,5 +1,5 @@
<!-- Back to top button --> <!-- Back to top button -->
<%= link_to "#" do %> <%= link_to "#", class: "opacity-80" do %>
<button <button
type="button" type="button"
data-twe-ripple-init data-twe-ripple-init

View File

@@ -1,26 +1,28 @@
<header class="container flex items-center justify-between px-4 py-6 mx-auto border-b-2 border-gray-300"> <header class="container bg-white sticky z-10 top-0 flex items-center justify-between px-4 py-6 mx-auto border-b-2 border-gray-300">
<a href="/" class="text-4xl font-bold text-hsrm-gray">Plottservice FAB</a> <a href="/" class="text-4xl font-bold text-hsrm-gray">Plottservice FAB</a>
<nav> <nav>
<ul class="flex justify-center space-x-2 font-semibold items-color"> <ul class="flex justify-center space-x-2 font-semibold items-color">
<li class="relative"> <li class="relative">
<button class="px-4 py-2 cursor-default hover:text-hsrm-red border-b-4 <%= root_tab? && 'border-hsrm-red-light' %>"> <button class="px-4 py-2 hover:text-hsrm-red border-b-4 <%= root_tab? && 'border-hsrm-red-light' || 'hover:border-hsrm-red-light hover:bg-gray-100' %>">
<%= link_to "Aktuelle Druckaufträge", root_path %> <%= link_to "Aktuelle Druckaufträge", root_path %>
</button> </button>
</li> </li>
<% if user_signed_in? %> <% if user_signed_in? %>
<li class="relative"> <li class="relative">
<button class="px-4 py-2 cursor-default hover:text-hsrm-red border-b-4 <%= profile_tab? && 'border-hsrm-red-light' %>"> <button class="px-4 py-2 hover:text-hsrm-red border-b-4 <%= profile_tab? && 'border-hsrm-red-light' || 'hover:border-hsrm-red-light hover:bg-gray-100' %>">
<%= link_to "Profil", profile_path %> <%= link_to "Profil", profile_path %>
</button> </button>
</li> </li>
<% if (current_user.admin? || current_user.operator?) %> <% if allowed_to? :admin?, with: ApplicationPolicy %>
<li class="relative"> <li class="relative">
<button class="px-4 py-2 cursor-default <%= current_user.admin? && 'hover:text-hsrm-red' || 'text-hsrm-gray-light' %> border-b-4 <%= admin_tab? && 'border-hsrm-red-light' %>"> <button class="px-4 py-2 <%= current_user.admin? && 'hover:text-hsrm-red' || 'text-hsrm-gray-light' %> border-b-4 <%= admin_tab? && 'border-hsrm-red-light' || 'hover:border-hsrm-red-light hover:bg-gray-100' %>">
<%= link_to_if current_user.admin?, "Admin", admin_dashboard_path %> <%= link_to_if current_user.admin?, "Admin", admin_dashboard_path %>
</button> </button>
</li> </li>
<% end %>
<% if allowed_to? :operate?, with: ApplicationPolicy %>
<li class="relative"> <li class="relative">
<button class="px-4 py-2 cursor-default hover:text-hsrm-red border-b-4 <%= operator_tab? && 'border-hsrm-red-light' %>"> <button class="px-4 py-2 hover:text-hsrm-red border-b-4 <%= operator_tab? && 'border-hsrm-red-light' || 'hover:border-hsrm-red-light hover:bg-gray-100' %>">
<%= link_to 'Operator', operator_jobs_path %> <%= link_to 'Operator', operator_jobs_path %>
</button> </button>
</li> </li>

View File

@@ -11,12 +11,12 @@
</div> </div>
<% end %> <% end %>
<div class="my-5"> <div class="my-5">
<%= form.label :costumer_firstname, 'Vorname' %> <%= form.label :customer_firstname, 'Vorname' %>
<%= form.text_field :costumer_firstname, class: "block shadow-lg rounded-md border border-hsrm-gray outline-none px-3 py-2 mt-2 w-full" %> <%= form.text_field :customer_firstname, class: "block shadow-lg rounded-md border border-hsrm-gray outline-none px-3 py-2 mt-2 w-full" %>
</div> </div>
<div class="my-5"> <div class="my-5">
<%= form.label :costumer_lastname, 'Nachname' %> <%= form.label :customer_lastname, 'Nachname' %>
<%= form.text_field :costumer_lastname, class: "block shadow-lg rounded-md border border-hsrm-gray outline-none px-3 py-2 mt-2 w-full" %> <%= form.text_field :customer_lastname, class: "block shadow-lg rounded-md border border-hsrm-gray outline-none px-3 py-2 mt-2 w-full" %>
</div> </div>
<div class="my-5 inline"> <div class="my-5 inline">
<%= form.check_box :intern, class: "pr-2 h-5 w-5" %> <%= form.check_box :intern, class: "pr-2 h-5 w-5" %>

View File

@@ -1,35 +1,63 @@
<div id="<%= dom_id job %>" class="flex flex-row space-x-4 shadow"> <div id="<%= dom_id job %>" class="flex flex-row space-x-4 shadow">
<div> <div>
<% if @job.pdf.attached? && job.pdf.previewable? %>
<embed type="application/pdf" src="<%= url_for(@job.pdf) %>" width="600px" height="800px" class="" /> <embed type="application/pdf" src="<%= url_for(@job.pdf) %>" width="600px" height="800px" class="" />
<% end %>
</div> </div>
<div> <div>
<p class="my-3">
<strong class="mb-1 font-medium">customer ID:</strong>
<% if job.customer %>
<%= link_to_if allowed_to?(:show?, job.customer, namespace: :Admin), "#{job.customer_id} - #{job.customer.name} (#{job.customer.email})", admin_user_path(job.customer) %>
<% else %>
-
<% end %>
</p>
<p class="my-3">
<strong class="mb-1 font-medium">Creator ID:</strong>
<% if job.creator %>
<%= link_to_if allowed_to?(:show?, job.creator, namespace: :Admin, ), "#{job.creator_id} - #{job.creator.name} (#{job.creator.email})", admin_user_path(job.creator) %>
<% else %>
-
<% end %>
</p>
<p class="my-3"> <p class="my-3">
<strong class="mb-1 font-medium">Operator ID:</strong> <strong class="mb-1 font-medium">Operator ID:</strong>
<% if job.operator %> <% if job.operator %>
<%= job.operator_id %> - <%= job.operator.name %> (<%= job.operator.email %>) <%= link_to_if allowed_to?(:show?, job.operator, namespace: :Admin), "#{job.operator_id} - #{job.operator.name} (#{job.operator.email})", admin_user_path(job.operator) %>
<% else %> <% else %>
- -
<% end %> <% end %>
</p> </p>
<p class="my-3"> <p class="my-3">
<strong class="mb-1 font-medium">Costumer ID:</strong> <strong class="mb-1 font-medium">Cashier ID:</strong>
<% if job.costumer %> <% if job.cashier %>
<%= job.costumer_id %> - <%= job.costumer.name %> (<%= job.costumer.email %>) <%= link_to_if allowed_to?(:show?, job.cashier, namespace: :Admin), "#{job.cashier_id} - #{job.cashier.name} (#{job.cashier.email})", admin_user_path(job.cashier) %>
<% else %> <% else %>
- -
<% end %> <% end %>
</p> </p>
<p class="my-3"> <p class="my-3">
<strong class="mb-1 font-medium">Betreiber:</strong> <strong class="mb-1 font-medium">Kunde:</strong>
<%= "#{job.operator_firstname} #{job.operator_lastname}" %> <%= link_to_if job.customer && allowed_to?(:show? , job.customer, namespace: :Admin), "#{job.customer_firstname} #{job.customer_lastname}", ( job.customer ? admin_user_path(job.customer) : "" ) %>
</p> </p>
<p class="my-3"> <p class="my-3">
<strong class="mb-1 font-medium">Auftraggeber:</strong> <strong class="mb-1 font-medium">Operator:</strong>
<%= "#{job.costumer_firstname} #{job.costumer_lastname}" %> <%= link_to_if job.operator && allowed_to?(:show, job.operator, namespace: :Admin), "#{job.operator_firstname} #{job.operator_lastname}", ( job.operator ? admin_users_path(job.operator) : "" )%>
</p>
<p class="my-3">
<strong class="mb-1 font-medium">Kassierer:</strong>
<%= link_to_if job.cashier && allowed_to?(:show, job.cashier, namespace: :Admin), "#{job.cashier_firstname} #{job.cashier_lastname}", ( job.cashier ? admin_users_path(job.cashier) : "" ) %>
</p>
<p class="my-3">
<strong class="mb-1 font-medium">Erstellt durch Operator:</strong>
<%= icon bool_icon(job.created_by_operator), class: "icon #{job.created_by_operator ? "text-green-600" : "text-red-600"}" %>
</p> </p>
<p class="my-3"> <p class="my-3">
<strong class="mb-1 font-medium">Aktueller Status:</strong> <strong class="mb-1 font-medium">Aktueller Status:</strong>
<%= %> <span class="badge badge-status inline text-status-<%= job.status.to_sym %> bg-status-<%= job.status %>-light">
<%= job.status %>
</span>
</p> </p>
<p class="my-3"> <p class="my-3">
<strong class="mb-1 font-medium">Paid at:</strong> <strong class="mb-1 font-medium">Paid at:</strong>

View File

@@ -13,17 +13,17 @@
<% end %> <% end %>
</td> </td>
<td class="p-2 py-3"> <td class="p-2 py-3">
<%= job.costumer_fullname %> <%= job.customer_fullname %>
</td> </td>
<td class="p-2 py-3"> <td class="p-2 py-3">
<% if job.pdf.attached? %> <% if job.pdf.attached? %>
<%#= link_to job.pdf.filename, rails_blob_path(job.pdf, disposition: "attachment") %> <% #= link_to job.pdf.filename, rails_blob_path(job.pdf, disposition: "attachment") %>
<%= link_to_if job.printing?, truncate(job.pdf.filename.to_s, length: 45), job.pdf, download:true %> <%= link_to_if job.printing?, truncate(job.pdf.filename.to_s, length: 45), job.pdf, download:true %>
<% if job.printing? %> <% if job.printing? %>
<%= link_to job.pdf, download:true do %> <%= link_to job.pdf, download:true do %>
<span class="badge badge-hover"> <span class="badge badge-hover">
<%= icon("document-arrow-down", class: "text-hsrm-gray size-6 inline", title: "Download") %> <%= icon("document-arrow-down", class: "text-hsrm-gray size-6 inline", title: "Download") %>
<%=number_to_human_size job.pdf.blob.byte_size%> <%= number_to_human_size job.pdf.blob.byte_size %>
</span> </span>
<% end %> <% end %>
<% end %> <% end %>

View File

@@ -1,6 +1,14 @@
<div id="<%= dom_id @job %>" class="flex flex-row space-x-4 shadow"> <div id="<%= dom_id @job %>" class="flex flex-row space-x-4 shadow">
<div> <div>
<embed type="application/pdf" src="<%= url_for(@job.pdf) %>#view=Fit" width="700px" height="900px" class="" /> <% if @job.pdf.attached? && @job.pdf.previewable? %>
<embed
type="application/pdf"
src="<%= url_for(@job.pdf) %> #view=Fit"
width="700px"
height="900px"
class=""
/>
<% end %>
</div> </div>
<div> <div>
<%= render "form", job: @job %> <%= render "form", job: @job %>

View File

@@ -22,17 +22,17 @@
<h2 class="p-1 text-lg font-bold border-b-2 border-hsrm-red">Aufgegebene Druckaufträge</h2> <h2 class="p-1 text-lg font-bold border-b-2 border-hsrm-red">Aufgegebene Druckaufträge</h2>
<p> <p>
Aufgegebene Druckaufträge Aufgegebene Druckaufträge
<%= current_user.jobs_as_costumer.size %> <%= current_user.customer_jobs.size %>
</p> </p>
<p> <p>
Abgebrochene Druckaufgräge Abgebrochene Druckaufgräge
<%= current_user.jobs_as_costumer.canceled.size %> <%= current_user.customer_jobs.canceled.size %>
</p> </p>
<% if is_admin_or_operator? %> <% if is_admin_or_operator? %>
<h2 class="p-1 text-lg font-bold border-b-2 border-hsrm-red">Bearbeitete Druckaufträge</h2> <h2 class="p-1 text-lg font-bold border-b-2 border-hsrm-red">Bearbeitete Druckaufträge</h2>
<p> <p>
Gedruckte Druckaufträge Gedruckte Druckaufträge
<%= current_user.jobs_as_operator.paid.size %> <%= current_user.operator_jobs.paid.size %>
</p> </p>
<% end %> <% end %>
<h2 class="p-1 text-lg font-bold border-b-2 border-hsrm-red">Actions</h2> <h2 class="p-1 text-lg font-bold border-b-2 border-hsrm-red">Actions</h2>
@@ -49,3 +49,26 @@
<br> <br>
<%= button_to "Log out", Current.session, method: :delete, class: "btn btn-primary" %> <%= button_to "Log out", Current.session, method: :delete, class: "btn btn-primary" %>
</div> </div>
<h1 class="py-4 text-4xl font-bold text-hsrm-gray">Die letzten 10 Druckaufträge</h1>
<div class="min-w-full overflow-auto shadow-lg">
<table class="w-full py-8 table-auto">
<thead class="font-semibold tracking-wide bg-gray-200 border-b-2 border-gray-300 text text-hsrm-gray">
<tr>
<th class="w-1 p-2 py-3 text-center"> ID </th>
<th class="w-1 p-2 py-3 text-center"> Vorschau </th>
<th class="w-1 p-2 py-3 text-left"> Auftraggeber </th>
<th class="p-2 py-3 text-left"> PDF </th>
<th class="w-1 p-1 py-3 text-left"> A0 </th>
<th class="w-1 p-1 py-3 text-left"> A1 </th>
<th class="w-1 p-1 py-3 text-left"> A2 </th>
<th class="w-1 p-1 py-3 text-left"> A3 </th>
<th class="w-1 p-2 py-3 text-center text-nowrap"> no DIN </th>
<th class="w-1 p-2 py-3 text-center"> Kosten </th>
<th class="w-1 p-2 py-3 text-center"> Status </th>
</tr>
</thead>
<tbody id='jobs' class="divide-y divivde-gray-300">
<%= render partial: "jobs/job_tr", collection: current_user.customer_jobs.order(created_at: :desc).limit(10), as: :job, locals: { no_actions: true } %>
</tbody>
</table>
</div>

View File

@@ -0,0 +1,5 @@
ActiveSupport.on_load(:active_storage_blob) do
def self.ransackable_attributes(auth_object = nil)
%w[filename]
end
end

View File

@@ -41,7 +41,7 @@
# Calendar extra: Add pagination filtering by calendar time unit (year, quarter, month, week, day) # Calendar extra: Add pagination filtering by calendar time unit (year, quarter, month, week, day)
# See https://ddnexus.github.io/pagy/docs/extras/calendar # See https://ddnexus.github.io/pagy/docs/extras/calendar
# require 'pagy/extras/calendar' require "pagy/extras/calendar"
# Default for each calendar unit class in IRB: # Default for each calendar unit class in IRB:
# >> Pagy::Calendar::Year::DEFAULT # >> Pagy::Calendar::Year::DEFAULT
# >> Pagy::Calendar::Quarter::DEFAULT # >> Pagy::Calendar::Quarter::DEFAULT

View File

@@ -33,8 +33,8 @@ de:
job: job:
pdf: "Plan (PDF Format)" pdf: "Plan (PDF Format)"
privacy_policy: "Datenschutzerklärung" privacy_policy: "Datenschutzerklärung"
costumer_firstname: "Vorname" customer_firstname: "Vorname"
costumer_lastname: "Nachname" customer_lastname: "Nachname"
errors: errors:
models: models:
job: job:

View File

@@ -14,6 +14,7 @@ Rails.application.routes.draw do
member do member do
patch "cancel" patch "cancel"
get "cancel_button" get "cancel_button"
get "badge"
end end
end end
resource :profile, only: [ :show, :edit, :destroy ] resource :profile, only: [ :show, :edit, :destroy ]

View File

@@ -1,17 +1,19 @@
class CreateJobs < ActiveRecord::Migration[7.1] class CreateJobs < ActiveRecord::Migration[7.1]
def change def change
create_table :jobs do |t| create_table :jobs do |t|
t.references :customer, null: true
t.references :creator, null: true
t.references :cashier, null: true
t.references :operator, null: true t.references :operator, null: true
t.references :costumer, null: true t.string :customer_firstname
t.string :customer_lastname
t.string :operator_firstname t.string :operator_firstname
t.string :operator_lastname t.string :operator_lastname
t.string :costumer_firstname t.string :cashier_firstname
t.string :costumer_lastname t.string :cashier_lastname
t.boolean :printed, default: false
t.boolean :paid, default: false
t.datetime :printed_at t.datetime :printed_at
t.datetime :status_changed_at
t.datetime :paid_at t.datetime :paid_at
t.datetime :status_changed_at
t.boolean :intern, default: false t.boolean :intern, default: false
t.string :cost_center t.string :cost_center
t.string :status, default: "open", index: true t.string :status, default: "open", index: true

View File

@@ -10,12 +10,16 @@ class CreateUsers < ActiveRecord::Migration[7.2]
t.boolean :verified, null: false, default: false t.boolean :verified, null: false, default: false
t.integer :jobs_as_costumer_count, default: 0 t.integer :customer_jobs_count, default: 0
t.integer :jobs_as_operator_count, default: 0 t.integer :operator_jobs_count, default: 0
t.integer :created_jobs_count, default: 0
t.integer :cashed_jobs_count, default: 0
t.timestamps t.timestamps
end end
add_foreign_key :jobs, :users, column: :customer_id
add_foreign_key :jobs, :users, column: :operator_id add_foreign_key :jobs, :users, column: :operator_id
add_foreign_key :jobs, :users, column: :costumer_id add_foreign_key :jobs, :users, column: :creator_id
add_foreign_key :jobs, :users, column: :cashier_id
end end
end end

30
db/schema.rb generated
View File

@@ -10,7 +10,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[7.2].define(version: 2024_08_26_144016) do ActiveRecord::Schema[8.0].define(version: 2024_08_26_144016) do
create_table "active_storage_attachments", force: :cascade do |t| create_table "active_storage_attachments", force: :cascade do |t|
t.string "name", null: false t.string "name", null: false
t.string "record_type", null: false t.string "record_type", null: false
@@ -40,17 +40,19 @@ ActiveRecord::Schema[7.2].define(version: 2024_08_26_144016) do
end end
create_table "jobs", force: :cascade do |t| create_table "jobs", force: :cascade do |t|
t.integer "customer_id"
t.integer "creator_id"
t.integer "cashier_id"
t.integer "operator_id" t.integer "operator_id"
t.integer "costumer_id" t.string "customer_firstname"
t.string "customer_lastname"
t.string "operator_firstname" t.string "operator_firstname"
t.string "operator_lastname" t.string "operator_lastname"
t.string "costumer_firstname" t.string "cashier_firstname"
t.string "costumer_lastname" t.string "cashier_lastname"
t.boolean "printed", default: false
t.boolean "paid", default: false
t.datetime "printed_at" t.datetime "printed_at"
t.datetime "status_changed_at"
t.datetime "paid_at" t.datetime "paid_at"
t.datetime "status_changed_at"
t.boolean "intern", default: false t.boolean "intern", default: false
t.string "cost_center" t.string "cost_center"
t.string "status", default: "open" t.string "status", default: "open"
@@ -65,7 +67,9 @@ ActiveRecord::Schema[7.2].define(version: 2024_08_26_144016) do
t.boolean "created_by_operator", default: false t.boolean "created_by_operator", default: false
t.datetime "created_at", null: false t.datetime "created_at", null: false
t.datetime "updated_at", null: false t.datetime "updated_at", null: false
t.index ["costumer_id"], name: "index_jobs_on_costumer_id" t.index ["cashier_id"], name: "index_jobs_on_cashier_id"
t.index ["creator_id"], name: "index_jobs_on_creator_id"
t.index ["customer_id"], name: "index_jobs_on_customer_id"
t.index ["operator_id"], name: "index_jobs_on_operator_id" t.index ["operator_id"], name: "index_jobs_on_operator_id"
t.index ["status"], name: "index_jobs_on_status" t.index ["status"], name: "index_jobs_on_status"
end end
@@ -86,8 +90,10 @@ ActiveRecord::Schema[7.2].define(version: 2024_08_26_144016) do
t.string "lastname" t.string "lastname"
t.string "role", default: "user" t.string "role", default: "user"
t.boolean "verified", default: false, null: false t.boolean "verified", default: false, null: false
t.integer "jobs_as_costumer_count", default: 0 t.integer "customer_jobs_count", default: 0
t.integer "jobs_as_operator_count", default: 0 t.integer "operator_jobs_count", default: 0
t.integer "created_jobs_count", default: 0
t.integer "cashed_jobs_count", default: 0
t.datetime "created_at", null: false t.datetime "created_at", null: false
t.datetime "updated_at", null: false t.datetime "updated_at", null: false
t.index ["email"], name: "index_users_on_email", unique: true t.index ["email"], name: "index_users_on_email", unique: true
@@ -96,7 +102,9 @@ ActiveRecord::Schema[7.2].define(version: 2024_08_26_144016) do
add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id" add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id"
add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id" add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id"
add_foreign_key "jobs", "users", column: "costumer_id" add_foreign_key "jobs", "users", column: "cashier_id"
add_foreign_key "jobs", "users", column: "creator_id"
add_foreign_key "jobs", "users", column: "customer_id"
add_foreign_key "jobs", "users", column: "operator_id" add_foreign_key "jobs", "users", column: "operator_id"
add_foreign_key "sessions", "users" add_foreign_key "sessions", "users"
end end

View File

@@ -15,8 +15,9 @@ User.create!(email: "david.boehm@hs-rm.de", firstname: "David", lastname: "Böhm
User.create!(email: "maximilian.lasser@hs-rm.de", firstname: "Max", lastname: "Lasser", role: :admin, password_digest: BCrypt::Password.create("admin"), verified: true) User.create!(email: "maximilian.lasser@hs-rm.de", firstname: "Max", lastname: "Lasser", role: :admin, password_digest: BCrypt::Password.create("admin"), verified: true)
# Operators # Operators
User.create!(email: "tutor.operator@hs-rm.de", firstname: "Tutor", lastname: "Operator", role: :operator, password_digest: BCrypt::Password.create("operator"), verified: true) operators = []
User.create!(email: "tutor2.operator@hs-rm.de", firstname: "Tutor2", lastname: "Operator", role: :operator, password_digest: BCrypt::Password.create("operator"), verified: true) operators << User.create!(email: "tutor.operator@hs-rm.de", firstname: "Tutor", lastname: "Operator", role: :operator, password_digest: BCrypt::Password.create("operator"), verified: true)
operators << User.create!(email: "tutor2.operator@hs-rm.de", firstname: "Tutor2", lastname: "Operator", role: :operator, password_digest: BCrypt::Password.create("operator"), verified: true)
# Students # Students
User.create!(email: "stud.student@student.hs-rm.de", firstname: "Student", lastname: "Student", password_digest: BCrypt::Password.create("stud"), verified: true) User.create!(email: "stud.student@student.hs-rm.de", firstname: "Student", lastname: "Student", password_digest: BCrypt::Password.create("stud"), verified: true)
@@ -29,8 +30,8 @@ students = []
lastname = Faker::Name.unique.last_name lastname = Faker::Name.unique.last_name
# created_at = Faker::Time.backward(days: 60, period: :day) # created_at = Faker::Time.backward(days: 60, period: :day)
created_at = Faker::Time.between_dates(from: Date.today - 60, to: Date.today - 30, period: :day) created_at = Faker::Time.between_dates(from: Date.today - 60, to: Date.today - 30, period: :day)
email="#{firstname}.#{lastname}@student.hs-rm.de".downcase.gsub('ö', 'oe').gsub('ä', 'ae').gsub('ü', 'ue').gsub('ß', 'ss') email=I18n.transliterate "#{firstname}.#{lastname}@student.hs-rm.de"
email.delete(" ") puts "Create Student: " + email
students << User.new(email: email, firstname: firstname, lastname: lastname, password_digest: BCrypt::Password.create("password"), verified: true, created_at: created_at) students << User.new(email: email, firstname: firstname, lastname: lastname, password_digest: BCrypt::Password.create("password"), verified: true, created_at: created_at)
students.last.save! students.last.save!
end end
@@ -41,8 +42,8 @@ end
lastname = Faker::Name.unique.last_name lastname = Faker::Name.unique.last_name
created_at = Faker::Time.backward(days: 60, period: :day) created_at = Faker::Time.backward(days: 60, period: :day)
# created_at = Faker::Time.between_dates(from: Date.today - 60, to: Date.today, period: :day) # created_at = Faker::Time.between_dates(from: Date.today - 60, to: Date.today, period: :day)
email="#{firstname}.#{lastname}@student.hs-rm.de".downcase.gsub('ö', 'oe').gsub('ä', 'ae').gsub('ü', 'ue').gsub('ß', 'ss') email=I18n.transliterate "#{firstname}.#{lastname}@student.hs-rm.de"
email.delete(" ") puts "Create Student: " + email
User.new(email: email, firstname: firstname, lastname: lastname, password_digest: BCrypt::Password.create("password"), verified: true, created_at: created_at).save! User.new(email: email, firstname: firstname, lastname: lastname, password_digest: BCrypt::Password.create("password"), verified: true, created_at: created_at).save!
end end
@@ -52,11 +53,46 @@ end
lastname = Faker::Name.unique.last_name lastname = Faker::Name.unique.last_name
created_at = Faker::Time.backward(days: 60, period: :day) created_at = Faker::Time.backward(days: 60, period: :day)
# created_at = Faker::Time.between_dates(from: Date.today - 60, to: Date.today, period: :day) # created_at = Faker::Time.between_dates(from: Date.today - 60, to: Date.today, period: :day)
email="#{firstname}.#{lastname}@student.hs-rm.de".downcase.gsub('ö', 'oe').gsub('ä', 'ae').gsub('ü', 'ue').gsub('ß', 'ss') email=I18n.transliterate "#{firstname}.#{lastname}@student.hs-rm.de"
email.delete(" ") puts "Create Student: " + email
user=User.new(email: email, firstname: firstname, lastname: lastname, password_digest: BCrypt::Password.create("password"), verified: false, created_at: created_at).save! User.new(email: email, firstname: firstname, lastname: lastname, password_digest: BCrypt::Password.create("password"), verified: false, created_at: created_at).save!
end end
# Jobs paid (and some canceled) in the far past
10.times do
[ 'GanzWichtig.pdf', 'IchBinIn5MinDran.pdf', 'DerPlanDerImmerProblemeMacht.pdf',
'DieFarbenGefallenMirNicht.pdf', 'MachHinIchHabsEilig.pdf', 'WarumDauertDasSoLange.pdf',
'DenPlanBezahleIchNicht.pdf', 'IchWarAlsErstesDran.pdf', 'WarumIstDerPlotterDefekt.pdf',
'DasNächsteMalGeheIchWoAndersHin.pdf' ].shuffle.each do |pdf|
status = %i[paid paid paid paid paid paid paid canceled].sample
created_at = Faker::Time.backward(days: 1.year.in_days, period: :day)
if status == :paid
printed_at = created_at + 30.minutes
paid_at = created_at + 45.minutes
status_changed_at = paid_at
updated_at = status_changed_at
else
updated_at = created_at + rand(4..44).minutes
status_changed_at = updated_at
end
job = Job.new(status:, privacy_policy: true)
job.pdf = File.open(Rails.root.join('db/pdfs/', pdf))
student = students[rand(0..9)]
job.customer = student
job.creator = student
operator = operators[rand(0...1)]
job.operator = operator if status != :open
job.cashier = operator if status == :paid
job.save!
job.update_column :created_at, created_at # write with update_column to avoid before_save action
job.update_column :printed_at, printed_at # write with update_column to avoid before_save action
job.update_column :status_changed_at, status_changed_at # write with update_column to avoid before_save action
job.update_column :paid_at, paid_at if status == :paid # write with update_column to avoid before_save action
job.update_column :updated_at, updated_at # write with update_column to avoid before_save action
end
end
# Jobs paid (and some canceled) in the past # Jobs paid (and some canceled) in the past
2.times do 2.times do
[ 'GanzWichtig.pdf', 'IchBinIn5MinDran.pdf', 'DerPlanDerImmerProblemeMacht.pdf', [ 'GanzWichtig.pdf', 'IchBinIn5MinDran.pdf', 'DerPlanDerImmerProblemeMacht.pdf',
@@ -74,10 +110,16 @@ end
updated_at = created_at + rand(4..44).minutes updated_at = created_at + rand(4..44).minutes
status_changed_at = updated_at status_changed_at = updated_at
end end
job = Job.new(status:, privacy_policy: true, created_at: created_at) job = Job.new(status:, privacy_policy: true)
job.pdf = File.open(Rails.root.join('db/pdfs/', pdf)) job.pdf = File.open(Rails.root.join('db/pdfs/', pdf))
job.costumer = students[rand(0...9)] student = students[rand(0...9)]
job.customer = student
job.creator = student
operator = operators[rand(0...1)]
job.operator = operator if status == :paid
job.cashier = operator if status == :paid
job.save! job.save!
job.update_column :created_at, created_at # write with update_column to avoid before_save action
job.update_column :printed_at, printed_at # write with update_column to avoid before_save action job.update_column :printed_at, printed_at # write with update_column to avoid before_save action
job.update_column :status_changed_at, status_changed_at # write with update_column to avoid before_save action job.update_column :status_changed_at, status_changed_at # write with update_column to avoid before_save action
job.update_column :paid_at, paid_at if status == :paid # write with update_column to avoid before_save action job.update_column :paid_at, paid_at if status == :paid # write with update_column to avoid before_save action
@@ -96,7 +138,29 @@ end
status = :open if i > 0 status = :open if i > 0
job = Job.new(status:, privacy_policy: true) job = Job.new(status:, privacy_policy: true)
job.pdf = File.open(Rails.root.join('db/pdfs/', pdf)) job.pdf = File.open(Rails.root.join('db/pdfs/', pdf))
job.costumer = students[rand(0...4)] student = students[rand(0...4)]
job.customer = student
job.creator = student
operator = operators[rand(0...1)]
job.operator = operator if status != :open
job.cashier = operator if status == :paid
job.save! job.save!
end end
end end
# Jobs created from operator
[ 'GanzWichtig.pdf', 'IchBinIn5MinDran.pdf', 'DerPlanDerImmerProblemeMacht.pdf',
'DieFarbenGefallenMirNicht.pdf', 'MachHinIchHabsEilig.pdf', 'WarumDauertDasSoLange.pdf',
'DenPlanBezahleIchNicht.pdf', 'IchWarAlsErstesDran.pdf', 'WarumIstDerPlotterDefekt.pdf',
'DasNächsteMalGeheIchWoAndersHin.pdf' ].shuffle.each do |pdf|
job = Job.new(privacy_policy: true)
job.pdf = File.open(Rails.root.join('db/pdfs/', pdf))
student = students[rand(0...9)]
job.customer = [ student, student, false ].sample
job.customer_firstname = student.firstname
job.customer_lastname = student.lastname
job.creator = operators[rand(0...1)]
job.created_by_operator = true
job.inspect
job.save!
end

View File

@@ -17,7 +17,7 @@ class JobsControllerTest < ActionDispatch::IntegrationTest
test "should create job" do test "should create job" do
assert_difference("Job.count") do assert_difference("Job.count") do
post jobs_url, params: { job: { cost_center: @job.cost_center, costum_qm_plan: @job.costum_qm_plan, costumer_firstname: @job.costumer_firstname, costumer_id_id: @job.costumer_id_id, costumer_lastname: @job.costumer_lastname, intern: @job.intern, number_of_plans_a0: @job.number_of_plans_a0, number_of_plans_a1: @job.number_of_plans_a1, number_of_plans_a2: @job.number_of_plans_a2, number_of_plans_a3: @job.number_of_plans_a3, operator_firstname: @job.operator_firstname, operator_id_id: @job.operator_id_id, operator_lastname: @job.operator_lastname, paid: @job.paid, printed_at: @job.printed_at } } post jobs_url, params: { job: { cost_center: @job.cost_center, costum_qm_plan: @job.costum_qm_plan, customer_firstname: @job.customer_firstname, customer_id_id: @job.customer_id_id, customer_lastname: @job.customer_lastname, intern: @job.intern, number_of_plans_a0: @job.number_of_plans_a0, number_of_plans_a1: @job.number_of_plans_a1, number_of_plans_a2: @job.number_of_plans_a2, number_of_plans_a3: @job.number_of_plans_a3, operator_firstname: @job.operator_firstname, operator_id_id: @job.operator_id_id, operator_lastname: @job.operator_lastname, paid: @job.paid, printed_at: @job.printed_at } }
end end
assert_redirected_to job_url(Job.last) assert_redirected_to job_url(Job.last)
@@ -34,7 +34,7 @@ class JobsControllerTest < ActionDispatch::IntegrationTest
end end
test "should update job" do test "should update job" do
patch job_url(@job), params: { job: { cost_center: @job.cost_center, costum_qm_plan: @job.costum_qm_plan, costumer_firstname: @job.costumer_firstname, costumer_id_id: @job.costumer_id_id, costumer_lastname: @job.costumer_lastname, intern: @job.intern, number_of_plans_a0: @job.number_of_plans_a0, number_of_plans_a1: @job.number_of_plans_a1, number_of_plans_a2: @job.number_of_plans_a2, number_of_plans_a3: @job.number_of_plans_a3, operator_firstname: @job.operator_firstname, operator_id_id: @job.operator_id_id, operator_lastname: @job.operator_lastname, paid: @job.paid, printed_at: @job.printed_at } } patch job_url(@job), params: { job: { cost_center: @job.cost_center, costum_qm_plan: @job.costum_qm_plan, customer_firstname: @job.customer_firstname, customer_id_id: @job.customer_id_id, customer_lastname: @job.customer_lastname, intern: @job.intern, number_of_plans_a0: @job.number_of_plans_a0, number_of_plans_a1: @job.number_of_plans_a1, number_of_plans_a2: @job.number_of_plans_a2, number_of_plans_a3: @job.number_of_plans_a3, operator_firstname: @job.operator_firstname, operator_id_id: @job.operator_id_id, operator_lastname: @job.operator_lastname, paid: @job.paid, printed_at: @job.printed_at } }
assert_redirected_to job_url(@job) assert_redirected_to job_url(@job)
end end

View File

@@ -2,11 +2,11 @@
one: one:
operator_id: one operator_id: one
costumer_id: one customer_id: one
operator_firstname: MyString operator_firstname: MyString
operator_lastname: MyString operator_lastname: MyString
costumer_firstname: MyString customer_firstname: MyString
costumer_lastname: MyString customer_lastname: MyString
paid: false paid: false
printed_at: 2024-07-27 12:13:47 printed_at: 2024-07-27 12:13:47
intern: false intern: false
@@ -19,11 +19,11 @@ one:
two: two:
operator_id: two operator_id: two
costumer_id: two customer_id: two
operator_firstname: MyString operator_firstname: MyString
operator_lastname: MyString operator_lastname: MyString
costumer_firstname: MyString customer_firstname: MyString
costumer_lastname: MyString customer_lastname: MyString
paid: false paid: false
printed_at: 2024-07-27 12:13:47 printed_at: 2024-07-27 12:13:47
intern: false intern: false

View File

@@ -16,9 +16,9 @@ class JobsTest < ApplicationSystemTestCase
fill_in "Cost center", with: @job.cost_center fill_in "Cost center", with: @job.cost_center
fill_in "Costum qm plan", with: @job.costum_qm_plan fill_in "Costum qm plan", with: @job.costum_qm_plan
fill_in "Costumer firstname", with: @job.costumer_firstname fill_in "customer firstname", with: @job.customer_firstname
fill_in "Costumer id", with: @job.costumer_id_id fill_in "customer id", with: @job.customer_id_id
fill_in "Costumer lastname", with: @job.costumer_lastname fill_in "customer lastname", with: @job.customer_lastname
check "Intern" if @job.intern check "Intern" if @job.intern
fill_in "Number of plans a0", with: @job.number_of_plans_a0 fill_in "Number of plans a0", with: @job.number_of_plans_a0
fill_in "Number of plans a1", with: @job.number_of_plans_a1 fill_in "Number of plans a1", with: @job.number_of_plans_a1
@@ -41,9 +41,9 @@ class JobsTest < ApplicationSystemTestCase
fill_in "Cost center", with: @job.cost_center fill_in "Cost center", with: @job.cost_center
fill_in "Costum qm plan", with: @job.costum_qm_plan fill_in "Costum qm plan", with: @job.costum_qm_plan
fill_in "Costumer firstname", with: @job.costumer_firstname fill_in "customer firstname", with: @job.customer_firstname
fill_in "Costumer id", with: @job.costumer_id_id fill_in "customer id", with: @job.customer_id_id
fill_in "Costumer lastname", with: @job.costumer_lastname fill_in "customer lastname", with: @job.customer_lastname
check "Intern" if @job.intern check "Intern" if @job.intern
fill_in "Number of plans a0", with: @job.number_of_plans_a0 fill_in "Number of plans a0", with: @job.number_of_plans_a0
fill_in "Number of plans a1", with: @job.number_of_plans_a1 fill_in "Number of plans a1", with: @job.number_of_plans_a1