diff --git a/Gemfile b/Gemfile index 4b3f9a1..a0610d6 100644 --- a/Gemfile +++ b/Gemfile @@ -17,6 +17,15 @@ gem 'turbolinks', '~> 5' # Build JSON APIs with ease [https://github.com/rails/jbuilder] gem "jbuilder" gem 'tzinfo-data' + +gem 'jquery-rails' + +# Use jquery-ui for pretty UI +gem 'jquery-ui-rails' + +# Use Sass to process CSS +gem "sassc-rails" + # Use Redis adapter to run Action Cable in production # gem "redis", ">= 4.0.1" diff --git a/Gemfile.lock b/Gemfile.lock index 9099e96..58d1bee 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -110,6 +110,8 @@ GEM erubi (1.13.1) et-orbi (1.4.0) tzinfo + ffi (1.17.2-x64-mingw32) + ffi (1.17.2-x86_64-linux-gnu) fugit (1.12.1) et-orbi (~> 1.4) raabro (~> 1.4) @@ -125,6 +127,12 @@ GEM jbuilder (2.14.1) actionview (>= 7.0.0) activesupport (>= 7.0.0) + jquery-rails (4.6.1) + rails-dom-testing (>= 1, < 3) + railties (>= 4.2.0) + thor (>= 0.14, < 2.0) + jquery-ui-rails (8.0.0) + railties (>= 3.2.16) kaminari (1.2.2) activesupport (>= 4.1.0) kaminari-actionview (= 1.2.2) @@ -230,6 +238,16 @@ GEM railties (>= 7.0) rufus-scheduler (3.9.2) fugit (~> 1.1, >= 1.11.1) + sassc (2.4.0) + ffi (~> 1.9) + sassc (2.4.0-x64-mingw32) + ffi (~> 1.9) + sassc-rails (2.1.2) + railties (>= 4.0.0) + sassc (>= 2.0) + sprockets (> 3.0) + sprockets-rails + tilt securerandom (0.3.2) sprockets (4.2.2) concurrent-ruby (~> 1.0) @@ -241,6 +259,7 @@ GEM sprockets (>= 3.0.0) stringio (3.1.7) thor (1.4.0) + tilt (2.6.1) timeout (0.4.4) tsort (0.2.0) turbolinks (5.2.1) @@ -272,10 +291,13 @@ DEPENDENCIES bootsnap devise jbuilder + jquery-rails + jquery-ui-rails pg (~> 1.1) puma (>= 5.0) rails (~> 7.1.5, >= 7.1.5.2) rufus-scheduler + sassc-rails sprockets-rails turbolinks (~> 5) tzinfo-data diff --git a/app/assets/javascript/application.js b/app/assets/javascript/application.js index 989413b..b7603cc 100644 --- a/app/assets/javascript/application.js +++ b/app/assets/javascript/application.js @@ -4,4 +4,6 @@ import "@hotwired/turbo-rails" import * as ActiveStorage from "@rails/activestorage" import "channels" -console.log("HALLO RAILS JS") \ No newline at end of file +import "jquery" +import "jquery_ujs" +import "./jquery_ui" \ No newline at end of file diff --git a/app/assets/javascript/jquery_ui.js b/app/assets/javascript/jquery_ui.js new file mode 100644 index 0000000..233c435 --- /dev/null +++ b/app/assets/javascript/jquery_ui.js @@ -0,0 +1,3 @@ +import "jquery" +import "jquery_ujs" +import "./jquery_ui" \ No newline at end of file diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css index 288b9ab..96d4125 100644 --- a/app/assets/stylesheets/application.css +++ b/app/assets/stylesheets/application.css @@ -1,3 +1,6 @@ +@import "jquery-ui.css"; + + /* * This is a manifest file that'll be compiled into application.css, which will include all the files * listed below. diff --git a/app/controllers/entries_controller.rb b/app/controllers/entries_controller.rb index 1f4056d..d4bfd79 100644 --- a/app/controllers/entries_controller.rb +++ b/app/controllers/entries_controller.rb @@ -1,10 +1,10 @@ class EntriesController < ApplicationController before_action :authenticate_user! - before_action :set_entry, only: %i[edit update destroy] + before_action :set_entry, only: %i[edit update destroy stop_timer] def index @entries = current_user.entries.order(date: :desc) - + @running_entry = current_user.entries.find_by(end_time: nil, beschreibung: "Timer") # Gesamtzeit in Minuten @total_minutes = @entries.sum { |e| e.hours.to_i * 60 + e.minutes.to_i } @@ -60,6 +60,34 @@ class EntriesController < ApplicationController @entry = current_user.entries.build end + def start_timer + + typ = params[:typ] + art = params[:art] + + @entry = current_user.entries.create!(start_time: Time.current, beschreibung: "Timer" , praktikums_typ:typ, entry_art: art) + redirect_to entries_path, notice: "Timer gestartet" + end + + # neue Aktion: Timer stoppen + def stop_timer + #@entry = current_user.entries.where(end_time: nil).order(start_time: :desc).first + if @entry + @entry.end_time = Time.current + @entry.lunch_break_minutes = params[:lunch_break_minutes].to_i + + total_minutes = @entry.total_minutes_including_break + @entry.hours = total_minutes / 60 + @entry.minutes = total_minutes % 60 + + @entry.save! + notice = "Eintrag gestoppt – Gesamtzeit: #{@entry.hours} h #{@entry.minutes} min" + else + notice = "Kein laufender Eintrag gefunden" + end + redirect_to entries_path, notice: notice + end + def create @entry = current_user.entries.build(entry_params) if @entry.save diff --git a/app/javascript/application.js b/app/javascript/application.js index e69de29..0f50342 100644 --- a/app/javascript/application.js +++ b/app/javascript/application.js @@ -0,0 +1,8 @@ + +import "@hotwired/turbo-rails" +import * as ActiveStorage from "@rails/activestorage" +import "channels" + +import "jquery" +import "jquery_ujs" +import "./jquery_ui" \ No newline at end of file diff --git a/app/javascript/jquery_ui.js b/app/javascript/jquery_ui.js new file mode 100644 index 0000000..6b24672 --- /dev/null +++ b/app/javascript/jquery_ui.js @@ -0,0 +1,5 @@ +import jquery from "jquery"; + +window.jQuery = jquery; + +window.$ = jquery; \ No newline at end of file diff --git a/app/models/entry.rb b/app/models/entry.rb index 7c709c7..128e41d 100644 --- a/app/models/entry.rb +++ b/app/models/entry.rb @@ -4,9 +4,9 @@ class Entry < ApplicationRecord belongs_to :user - validates :date, :hours, :minutes, presence: true - validates :hours, numericality: { only_integer: true, greater_than_or_equal_to: 0 } - validates :minutes, numericality: { only_integer: true, greater_than_or_equal_to: 0, less_than: 60 } + validates :date, presence: true + validates :hours, numericality: { only_integer: true, greater_than_or_equal_to: 0 }, unless: -> { start_time.present? && end_time.nil? } + validates :minutes, numericality: { only_integer: true, greater_than_or_equal_to: 0, less_than: 60 }, unless: -> { start_time.present? && end_time.nil? } before_save :normalize_time @@ -112,9 +112,28 @@ class Entry < ApplicationRecord end end + + def total_minutes_including_break + return nil unless start_time && end_time + minutes = ((end_time - start_time) / 60).to_i + minutes -= lunch_break_minutes.to_i + minutes = 0 if minutes < 0 + minutes + end + + def hours_and_minutes + mins = total_minutes_including_break + return [0, 0] unless mins + [mins / 60, mins % 60] + end + private def normalize_time + + self.minutes ||= 0 + self.hours ||= 0 + self.hours += minutes / 60 self.minutes = minutes % 60 end diff --git a/app/views/entries/_form.html.erb b/app/views/entries/_form.html.erb index d5c1d1b..33b70d0 100644 --- a/app/views/entries/_form.html.erb +++ b/app/views/entries/_form.html.erb @@ -43,12 +43,12 @@
|
<%= link_to 'Bearbeiten', edit_entry_path(entry), class: 'btn btn-sm btn-outline-primary' %>
@@ -240,4 +288,25 @@
});
});
+ document.addEventListener('DOMContentLoaded', function () {
+ const timerElement = document.getElementById('live-timer');
+ if (!timerElement) return;
+
+ <% if @running_entry&.start_time.present? %>
+ const startedAt = new Date("<%= @running_entry.start_time.iso8601 %>");
+ <% end %>
+
+ if (!startedAt) return;
+ const updateTimer = () => {
+ const now = new Date();
+ const diffMs = now - startedAt;
+ const totalMinutes = Math.floor(diffMs / 60000);
+ const hours = Math.floor(totalMinutes / 60);
+ const minutes = totalMinutes % 60;
+ timerElement.textContent = `${hours}h ${minutes}min (${totalMinutes}min)`;
+ };
+
+ updateTimer();
+ setInterval(updateTimer, 60000); // alle Minute aktualisieren
+ });
\ No newline at end of file
diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb
index 845afac..5e15f80 100644
--- a/app/views/layouts/application.html.erb
+++ b/app/views/layouts/application.html.erb
@@ -3,7 +3,8 @@
|