9 changed files with 406 additions and 1 deletions
@ -0,0 +1,64 @@ |
|||||
|
class CalendarController < ApplicationController |
||||
|
before_action :authenticate_user! |
||||
|
|
||||
|
def month |
||||
|
@year = params[:year].to_i |
||||
|
@month = params[:month].to_i |
||||
|
|
||||
|
while @month < 1 |
||||
|
@month += 12 |
||||
|
@year -= 1 |
||||
|
end |
||||
|
while @month > 12 |
||||
|
@month -= 12 |
||||
|
@year += 1 |
||||
|
end |
||||
|
|
||||
|
first_of_month = Date.new(@year, @month, 1) |
||||
|
last_of_month = first_of_month.end_of_month |
||||
|
|
||||
|
@filter_typ = params[:typ] |
||||
|
@filter_art = params[:art] |
||||
|
|
||||
|
@entries = current_user.entries |
||||
|
.where(date: first_of_month..last_of_month) |
||||
|
@entries = @entries.where(praktikums_typ: @filter_typ) if @filter_typ.present? |
||||
|
@entries = @entries.where(entry_art: @filter_art) if @filter_art.present? |
||||
|
@entries = @entries.group_by { |e| e.date.to_date } |
||||
|
|
||||
|
|
||||
|
puts @entries |
||||
|
@days = (first_of_month..last_of_month).to_a |
||||
|
end |
||||
|
|
||||
|
def week |
||||
|
@year = params[:year].to_i |
||||
|
@week = params[:week].to_i |
||||
|
|
||||
|
while @month < 1 |
||||
|
@month += 12 |
||||
|
@year -= 1 |
||||
|
end |
||||
|
while @month > 12 |
||||
|
@month -= 12 |
||||
|
@year += 1 |
||||
|
end |
||||
|
|
||||
|
first = Date.commercial(@year, @week, 1) |
||||
|
last = Date.commercial(@year, @week, 7) |
||||
|
|
||||
|
@filter_typ = params[:typ] |
||||
|
@filter_art = params[:art] |
||||
|
|
||||
|
@entries = current_user.entries |
||||
|
.where(date: first..last) |
||||
|
@entries = @entries.where(praktikums_typ: @filter_typ) if @filter_typ.present? |
||||
|
@entries = @entries.where(entry_art: @filter_art) if @filter_art.present? |
||||
|
@entries = @entries.group_by{ |e| e.date.to_date } |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
@days = (first..last).to_a |
||||
|
end |
||||
|
end |
||||
@ -1,2 +1,16 @@ |
|||||
module ApplicationHelper |
module ApplicationHelper |
||||
|
def color_class_for(entry) |
||||
|
case entry.entry_art |
||||
|
when "Praktikum" |
||||
|
"bg-primary text-white" |
||||
|
when "Selbsterfahrung" |
||||
|
"bg-purple text-white" |
||||
|
when "Supervision" |
||||
|
"bg-warning text-dark" |
||||
|
when "Fortbildung" |
||||
|
"bg-success text-white" |
||||
|
else |
||||
|
"bg-secondary text-white" |
||||
|
end |
||||
|
end |
||||
end |
end |
||||
@ -0,0 +1,110 @@ |
|||||
|
<h1 class="mb-3">Kalender – <%= Date::MONTHNAMES[@month] %> <%= @year %></h1> |
||||
|
|
||||
|
<div class="row mb-3"> |
||||
|
<div class="col-md-6"> |
||||
|
<%= link_to "← Vorheriger Monat", calendar_month_path(@year, @month - 1, typ: @filter_typ, art: @filter_art), class: "btn btn-outline-primary" %> |
||||
|
<%= link_to "Nächster Monat →", calendar_month_path(@year, @month + 1, typ: @filter_typ, art: @filter_art), class: "btn btn-outline-primary ms-2" %> |
||||
|
</div> |
||||
|
|
||||
|
<div class="col-md-6 text-end"> |
||||
|
<%= form_with url: calendar_month_path(@year, @month), method: :get, local: true, class: "row g-2 justify-content-end" do %> |
||||
|
<div class="col-auto"> |
||||
|
<%= select_tag :typ, options_for_select(User::PRAKTIKUMSTYPEN, @filter_typ), include_blank: "Alle Typen", class: "form-select" %> |
||||
|
</div> |
||||
|
<div class="col-auto"> |
||||
|
<%= select_tag :art, options_for_select(User::ENTRY_ARTEN, @filter_art), include_blank: "Alle Arten", class: "form-select" %> |
||||
|
</div> |
||||
|
<div class="col-auto"> |
||||
|
<%= submit_tag "Filtern", class: "btn btn-outline-secondary" %> |
||||
|
</div> |
||||
|
<% end %> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<table class="table table-bordered calendar-table"> |
||||
|
<thead class="table-light"> |
||||
|
<tr> |
||||
|
<% Date::ABBR_DAYNAMES.each do |day| %> |
||||
|
<th><%= day %></th> |
||||
|
<% end %> |
||||
|
</tr> |
||||
|
</thead> |
||||
|
<tbody> |
||||
|
<% weeks = @days.in_groups_of(7, nil) %> |
||||
|
<% weeks.each do |week| %> |
||||
|
<tr> |
||||
|
<% week.each do |date| %> |
||||
|
<td class="day-cell |
||||
|
<%= 'bg-light' if date.blank? %> |
||||
|
<%= 'today' if date == Date.current %>" |
||||
|
data-date="<%= date %>" |
||||
|
data-label="<%= date&.strftime('%d.%m.%Y') %>"> |
||||
|
|
||||
|
<% if date %> |
||||
|
<div class="day-box"> |
||||
|
<div class="day-header"> |
||||
|
<span class="day-number"><%= date.day %></span> |
||||
|
|
||||
|
<%= link_to "+", |
||||
|
new_entry_path(date: date), |
||||
|
class: "btn btn-sm btn-success day-add", |
||||
|
title: "Eintrag hinzufügen", |
||||
|
onclick: "event.stopPropagation();" %> |
||||
|
</div> |
||||
|
|
||||
|
<div class="day-entries"> |
||||
|
<% (@entries[date.to_date] || []).each do |entry| %> |
||||
|
<%= link_to edit_entry_path(entry), |
||||
|
class: "day-entry text-decoration-none", |
||||
|
onclick: "event.stopPropagation();" do %> |
||||
|
<span class="badge <%= color_class_for(entry) %>"> |
||||
|
<%= entry.entry_art.capitalize %> |
||||
|
</span> |
||||
|
<span class="entry-time"> |
||||
|
<%= entry.hours %>h <%= entry.minutes %>min |
||||
|
</span> |
||||
|
<% end %> |
||||
|
<% end %> |
||||
|
</div> |
||||
|
</div> |
||||
|
<% end %> |
||||
|
</td> |
||||
|
|
||||
|
<% end %> |
||||
|
</tr> |
||||
|
<% end %> |
||||
|
</tbody> |
||||
|
</table> |
||||
|
|
||||
|
|
||||
|
|
||||
|
<!-- Tagesmodal --> |
||||
|
<div class="modal fade" id="dayEntriesModal" tabindex="-1" aria-hidden="true"> |
||||
|
<div class="modal-dialog modal-lg"> |
||||
|
<div class="modal-content"> |
||||
|
<div class="modal-header"> |
||||
|
<h5 class="modal-title">Einträge für <span id="modal-date-label"></span></h5> |
||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button> |
||||
|
</div> |
||||
|
<div class="modal-body" id="modal-day-entries"></div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<script> |
||||
|
document.addEventListener("DOMContentLoaded", () => { |
||||
|
document.querySelectorAll(".day-cell").forEach(cell => { |
||||
|
cell.addEventListener("click", function () { |
||||
|
const date = this.dataset.date; |
||||
|
const label = this.dataset.label; |
||||
|
const entriesHtml = this.querySelector(".day-entries").innerHTML; |
||||
|
|
||||
|
document.getElementById("modal-date-label").textContent = label; |
||||
|
document.getElementById("modal-day-entries").innerHTML = entriesHtml; |
||||
|
|
||||
|
const modal = new bootstrap.Modal(document.getElementById("dayEntriesModal")); |
||||
|
modal.show(); |
||||
|
}); |
||||
|
}); |
||||
|
}); |
||||
|
</script> |
||||
@ -0,0 +1,75 @@ |
|||||
|
<h1 class="mb-3">Kalender – Woche <%= @week %> / <%= @year %></h1> |
||||
|
|
||||
|
<div class="row mb-3"> |
||||
|
<div class="col-md-6"> |
||||
|
<%= link_to "← Vorherige Woche", calendar_week_path(@year, @week - 1, typ: @filter_typ, art: @filter_art), class: "btn btn-outline-primary" %> |
||||
|
<%= link_to "Nächste Woche →", calendar_week_path(@year, @week + 1, typ: @filter_typ, art: @filter_art), class: "btn btn-outline-primary ms-2" %> |
||||
|
</div> |
||||
|
|
||||
|
<div class="col-md-6 text-end"> |
||||
|
<%= form_with url: calendar_week_path(@year, @week), method: :get, local: true, class: "row g-2 justify-content-end" do %> |
||||
|
<div class="col-auto"> |
||||
|
<%= select_tag :typ, options_for_select(User::PRAKTIKUMSTYPEN, @filter_typ), include_blank: "Alle Typen", class: "form-select" %> |
||||
|
</div> |
||||
|
<div class="col-auto"> |
||||
|
<%= select_tag :art, options_for_select(User::ENTRY_ARTEN, @filter_art), include_blank: "Alle Arten", class: "form-select" %> |
||||
|
</div> |
||||
|
<div class="col-auto"> |
||||
|
<%= submit_tag "Filtern", class: "btn btn-outline-secondary" %> |
||||
|
</div> |
||||
|
<% end %> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div class="row"> |
||||
|
<% @days.each do |date| %> |
||||
|
<div class="col border p-2 day-cell" data-date="<%= date %>" data-label="<%= date.strftime('%A %d.%m.%Y') %>"> |
||||
|
<div class="fw-bold"><%= date.strftime("%a %d.%m.") %></div> |
||||
|
<div class="day-entries d-none"> |
||||
|
<% if @entries[date] %> |
||||
|
<% @entries[date].each do |entry| %> |
||||
|
<div class="mb-2"> |
||||
|
<span class="badge <%= color_class_for(entry) %>"><%= entry.entry_art.capitalize %></span> |
||||
|
<%= "#{entry.hours}h #{entry.minutes}min" %> |
||||
|
<%= link_to "✏️", edit_entry_path(entry), class: "btn btn-sm btn-outline-secondary ms-2" %> |
||||
|
</div> |
||||
|
<% end %> |
||||
|
<% else %> |
||||
|
<p class="text-muted small">Keine Einträge</p> |
||||
|
<% end %> |
||||
|
</div> |
||||
|
<%= link_to "+", new_entry_path(date: date), class: "btn btn-sm btn-success mt-1", onclick: "event.stopPropagation();" %> |
||||
|
</div> |
||||
|
<% end %> |
||||
|
</div> |
||||
|
|
||||
|
<!-- Tagesmodal (wie im Monat) --> |
||||
|
<div class="modal fade" id="dayEntriesModal" tabindex="-1" aria-hidden="true"> |
||||
|
<div class="modal-dialog modal-lg"> |
||||
|
<div class="modal-content"> |
||||
|
<div class="modal-header"> |
||||
|
<h5 class="modal-title">Einträge für <span id="modal-date-label"></span></h5> |
||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button> |
||||
|
</div> |
||||
|
<div class="modal-body" id="modal-day-entries"></div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<script> |
||||
|
document.addEventListener("DOMContentLoaded", () => { |
||||
|
document.querySelectorAll(".day-cell").forEach(cell => { |
||||
|
cell.addEventListener("click", function () { |
||||
|
const date = this.dataset.date; |
||||
|
const label = this.dataset.label; |
||||
|
const entriesHtml = this.querySelector(".day-entries").innerHTML; |
||||
|
|
||||
|
document.getElementById("modal-date-label").textContent = label; |
||||
|
document.getElementById("modal-day-entries").innerHTML = entriesHtml; |
||||
|
|
||||
|
const modal = new bootstrap.Modal(document.getElementById("dayEntriesModal")); |
||||
|
modal.show(); |
||||
|
}); |
||||
|
}); |
||||
|
}); |
||||
|
</script> |
||||
Loading…
Reference in new issue