fix: fix wrong date calculation

This commit is contained in:
Patryk Hegenberg 2025-11-05 08:58:05 +01:00
parent 4514ce44a2
commit c8b7666971
4 changed files with 815 additions and 62 deletions

View file

@ -9,6 +9,7 @@ import Json.Decode as Decode exposing (Decoder, field, int, string, bool, list,
import Json.Encode as Encode
import Task
import Time
import Dict exposing (Dict)
-- PORTS
@ -45,12 +46,37 @@ type alias Model =
, selectedEntries : List SelectedEntry
, currentWeek : Int
, currentYear : Int
, weekDates : Maybe WeekDates -- NEU: Backend liefert Daten
, currentTime : Time.Posix
, zone : Time.Zone
, newSchedule : NewSchedule
, newUser : NewUser
, error : Maybe String
, weekEditMode : Bool
, hasEntriesForCurrentWeek : Bool
}
-- type alias Model =
-- { page : Page
-- , activeTab : AdminTab
-- , username : String
-- , password : String
-- , token : Maybe String
-- , isAdmin : Bool
-- , schedules : List Schedule
-- , users : List User
-- , timeEntries : List TimeEntry
-- , weeklyHours : List WeeklyHours
-- , selectedEntries : List SelectedEntry
-- , currentWeek : Int
-- , currentYear : Int
-- , currentTime : Time.Posix
-- , zone : Time.Zone
-- , newSchedule : NewSchedule
-- , newUser : NewUser
-- , error : Maybe String
-- , weekEditMode : Bool -- NEU: Edit-Modus für die Woche
-- , hasEntriesForCurrentWeek : Bool -- NEU: Hat die aktuelle Woche bereits Einträge?
-- }
type Page
= LoginPage
@ -115,6 +141,13 @@ type alias NewUser =
, isAdmin : Bool
}
type alias WeekDates =
{ year : Int
, week : Int
, dates : List (String, String) -- [(dayOfWeek, date)]
, range : String
}
init : Maybe String -> (Model, Cmd Msg)
init storedToken =
let
@ -137,19 +170,34 @@ init storedToken =
, newSchedule = NewSchedule "" "" "" "lesson" ""
, newUser = NewUser "" "" False
, error = Nothing
, weekEditMode = False
, hasEntriesForCurrentWeek = False
, weekDates = Nothing -- NEU
}
cmd =
case storedToken of
Just token ->
Cmd.batch
[ Task.perform SetTime Time.now
[ Task.perform SetTime Time.now -- Dies lädt dann automatisch Daten
, fetchSchedules (Just token)
]
Nothing ->
Task.perform SetTime Time.now
in
(model, cmd)
-- cmd =
-- case storedToken of
-- Just token ->
-- Cmd.batch
-- [ Task.perform SetTime Time.now
-- , fetchSchedules (Just token)
-- , fetchMyTimeEntries token
-- ]
-- Nothing ->
-- Task.perform SetTime Time.now
-- in
-- (model, cmd)
-- UPDATE
@ -168,6 +216,10 @@ type Msg
| TimeEntriesSaved (Result Http.Error ())
| PreviousWeek
| NextWeek
| EnableEditMode -- NEU
| DisableEditMode -- NEU
| DeleteWeekEntries -- NEU
| WeekEntriesDeleted (Result Http.Error ()) -- NEU
| SwitchTab AdminTab
| UpdateNewScheduleDay String
| UpdateNewScheduleStart String
@ -187,10 +239,16 @@ type Msg
| UserDeleted (Result Http.Error ())
| FetchUsers
| UsersReceived (Result Http.Error (List User))
| FetchMyTimeEntries -- NEU
| MyTimeEntriesReceived (Result Http.Error (List TimeEntry)) -- NEU
| FetchAllTimeEntries
| AllTimeEntriesReceived (Result Http.Error (List TimeEntry))
| FetchWeeklyHours
| WeeklyHoursReceived (Result Http.Error (List WeeklyHours))
| FetchWeekDates
| WeekDatesReceived (Result Http.Error WeekDates)
| CheckWeekHasEntries
| WeekHasEntriesReceived (Result Http.Error Bool)
update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
@ -210,12 +268,14 @@ update msg model =
in
({ model
| token = Just result.token
, username = result.username
, isAdmin = result.isAdmin
, page = newPage
, error = Nothing
}, Cmd.batch
[ saveToken result.token
, fetchSchedules (Just result.token)
, if not result.isAdmin then fetchMyTimeEntries result.token else Cmd.none
])
LoginResponse (Err _) ->
@ -230,15 +290,15 @@ update msg model =
, password = ""
}, removeToken ())
SetTime time ->
let
(year, week) = getISOWeekFromPosix time
in
({ model
| currentTime = time
, currentWeek = week
, currentYear = year
}, Cmd.none)
-- SetTime time ->
-- let
-- (year, week) = getISOWeekFromPosix time
-- in
-- ({ model
-- | currentTime = time
-- , currentWeek = week
-- , currentYear = year
-- }, Cmd.none)
FetchSchedules ->
(model, fetchSchedules model.token)
@ -250,15 +310,18 @@ update msg model =
({ model | error = Just "Fehler beim Laden des Stundenplans" }, Cmd.none)
ToggleScheduleSelection scheduleId dayOfWeek ->
let
entry = { scheduleId = scheduleId, dayOfWeek = dayOfWeek }
newSelected =
if List.any (\e -> e.scheduleId == scheduleId && e.dayOfWeek == dayOfWeek) model.selectedEntries then
List.filter (\e -> not (e.scheduleId == scheduleId && e.dayOfWeek == dayOfWeek)) model.selectedEntries
else
entry :: model.selectedEntries
in
({ model | selectedEntries = newSelected }, Cmd.none)
if model.weekEditMode then
let
entry = { scheduleId = scheduleId, dayOfWeek = dayOfWeek }
newSelected =
if List.any (\e -> e.scheduleId == scheduleId && e.dayOfWeek == dayOfWeek) model.selectedEntries then
List.filter (\e -> not (e.scheduleId == scheduleId && e.dayOfWeek == dayOfWeek)) model.selectedEntries
else
entry :: model.selectedEntries
in
({ model | selectedEntries = newSelected }, Cmd.none)
else
(model, Cmd.none)
SaveTimeEntries ->
case model.token of
@ -268,7 +331,16 @@ update msg model =
(model, Cmd.none)
TimeEntriesSaved (Ok _) ->
({ model | selectedEntries = [], error = Nothing }, Cmd.none)
case model.token of
Just token ->
({ model
| selectedEntries = []
, error = Nothing
, weekEditMode = False
, hasEntriesForCurrentWeek = True
}, fetchMyTimeEntries token)
Nothing ->
(model, Cmd.none)
TimeEntriesSaved (Err _) ->
({ model | error = Just "Fehler beim Speichern" }, Cmd.none)
@ -277,13 +349,198 @@ update msg model =
let
(newYear, newWeek) = previousWeek model.currentYear model.currentWeek
in
({ model | currentWeek = newWeek, currentYear = newYear, selectedEntries = [] }, Cmd.none)
({ model
| currentWeek = newWeek
, currentYear = newYear
, selectedEntries = []
, weekEditMode = False
}, case model.token of
Just token ->
Cmd.batch
[ fetchWeekDates token newYear newWeek
, checkWeekHasEntries token newYear newWeek
]
Nothing ->
Cmd.none
)
NextWeek ->
let
(newYear, newWeek) = nextWeek model.currentYear model.currentWeek
in
({ model | currentWeek = newWeek, currentYear = newYear, selectedEntries = [] }, Cmd.none)
({ model
| currentWeek = newWeek
, currentYear = newYear
, selectedEntries = []
, weekEditMode = False
}, case model.token of
Just token ->
Cmd.batch
[ fetchWeekDates token newYear newWeek
, checkWeekHasEntries token newYear newWeek
]
Nothing ->
Cmd.none
)
FetchWeekDates ->
case model.token of
Just token ->
(model, fetchWeekDates token model.currentYear model.currentWeek)
Nothing ->
(model, Cmd.none)
WeekDatesReceived (Ok weekDates) ->
({ model | weekDates = Just weekDates }, Cmd.none)
WeekDatesReceived (Err _) ->
({ model | error = Just "Fehler beim Laden der Wochendaten" }, Cmd.none)
CheckWeekHasEntries ->
case model.token of
Just token ->
(model, checkWeekHasEntries token model.currentYear model.currentWeek)
Nothing ->
(model, Cmd.none)
WeekHasEntriesReceived (Ok hasEntries) ->
({ model | hasEntriesForCurrentWeek = hasEntries }, Cmd.none)
WeekHasEntriesReceived (Err _) ->
(model, Cmd.none)
SetTime time ->
let
(year, week) = getISOWeekFromPosix time
in
({ model
| currentTime = time
, currentWeek = week
, currentYear = year
}, case model.token of
Just token ->
Cmd.batch
[ fetchWeekDates token year week
, checkWeekHasEntries token year week
]
Nothing ->
Cmd.none
)
-- PreviousWeek ->
-- let
-- (newYear, newWeek) = previousWeek model.currentYear model.currentWeek
-- in
-- ({ model
-- | currentWeek = newWeek
-- , currentYear = newYear
-- , selectedEntries = []
-- , weekEditMode = False
-- , hasEntriesForCurrentWeek = False -- WICHTIG: Zurücksetzen!
-- }, case model.token of
-- Just token -> fetchMyTimeEntries token
-- Nothing -> Cmd.none
-- )
-- NextWeek ->
-- let
-- (newYear, newWeek) = nextWeek model.currentYear model.currentWeek
-- in
-- ({ model
-- | currentWeek = newWeek
-- , currentYear = newYear
-- , selectedEntries = []
-- , weekEditMode = False
-- , hasEntriesForCurrentWeek = False -- WICHTIG: Zurücksetzen!
-- }, case model.token of
-- Just token -> fetchMyTimeEntries token
-- Nothing -> Cmd.none
-- )
-- PreviousWeek ->
-- let
-- (newYear, newWeek) = previousWeek model.currentYear model.currentWeek
-- in
-- ({ model
-- | currentWeek = newWeek
-- , currentYear = newYear
-- , selectedEntries = []
-- , weekEditMode = False
-- }, case model.token of
-- Just token -> fetchMyTimeEntries token
-- Nothing -> Cmd.none
-- )
-- NextWeek ->
-- let
-- (newYear, newWeek) = nextWeek model.currentYear model.currentWeek
-- in
-- ({ model
-- | currentWeek = newWeek
-- , currentYear = newYear
-- , selectedEntries = []
-- , weekEditMode = False
-- }, case model.token of
-- Just token -> fetchMyTimeEntries token
-- Nothing -> Cmd.none
-- )
EnableEditMode ->
let
-- Lade bestehende Einträge in selectedEntries
currentWeekEntries = List.filter
(\e ->
let
(entryYear, entryWeek) = getYearWeekFromDate e.date
in
entryWeek == model.currentWeek && entryYear == model.currentYear
)
model.timeEntries
preSelectedEntries = List.map
(\entry ->
-- Finde den dayOfWeek aus dem Datum
let
parts = String.split "-" entry.date
year = parts |> List.head |> Maybe.andThen String.toInt |> Maybe.withDefault 2025
month = parts |> List.drop 1 |> List.head |> Maybe.andThen String.toInt |> Maybe.withDefault 1
day = parts |> List.drop 2 |> List.head |> Maybe.andThen String.toInt |> Maybe.withDefault 1
dayOfWeek = (getDayOfWeek year month day)
in
{ scheduleId = entry.scheduleId, dayOfWeek = dayOfWeek }
)
currentWeekEntries
in
({ model
| weekEditMode = True
, selectedEntries = preSelectedEntries
}, Cmd.none)
DisableEditMode ->
({ model
| weekEditMode = False
, selectedEntries = []
}, Cmd.none)
DeleteWeekEntries ->
case model.token of
Just token ->
(model, deleteWeekEntries token model.currentYear model.currentWeek)
Nothing ->
(model, Cmd.none)
WeekEntriesDeleted (Ok _) ->
case model.token of
Just token ->
({ model
| weekEditMode = True
, selectedEntries = []
, hasEntriesForCurrentWeek = False
}, fetchMyTimeEntries token)
Nothing ->
(model, Cmd.none)
WeekEntriesDeleted (Err _) ->
({ model | error = Just "Fehler beim Löschen" }, Cmd.none)
SwitchTab tab ->
let
@ -294,7 +551,6 @@ update msg model =
fetchUsers token
Nothing ->
Cmd.none
-- fetchUsers model.token
TimeEntriesTab ->
case model.token of
Just token ->
@ -410,7 +666,6 @@ update msg model =
({ model | newUser = emptyUser }, fetchUsers token)
Nothing ->
(model, Cmd.none)
-- ({ model | newUser = emptyUser }, fetchUsers model.token)
UserCreated (Err _) ->
({ model | error = Just "Fehler beim Erstellen des Benutzers" }, Cmd.none)
@ -428,7 +683,6 @@ update msg model =
(model, fetchUsers token)
Nothing ->
(model, Cmd.none)
-- (model, fetchUsers model.token)
UserDeleted (Err _) ->
({ model | error = Just "Fehler beim Löschen des Benutzers" }, Cmd.none)
@ -446,6 +700,33 @@ update msg model =
UsersReceived (Err _) ->
({ model | error = Just "Fehler beim Laden der Benutzer" }, Cmd.none)
FetchMyTimeEntries ->
case model.token of
Just token ->
(model, fetchMyTimeEntries token)
Nothing ->
(model, Cmd.none)
MyTimeEntriesReceived (Ok entries) ->
let
hasEntries = List.any
(\e ->
let
(entryYear, entryWeek) = getYearWeekFromDate e.date
in
entryWeek == model.currentWeek && entryYear == model.currentYear
)
entries
in
({ model
| timeEntries = entries
, hasEntriesForCurrentWeek = hasEntries
, weekEditMode = False
}, Cmd.none)
MyTimeEntriesReceived (Err _) ->
({ model | error = Just "Fehler beim Laden der Einträge" }, Cmd.none)
FetchAllTimeEntries ->
case model.token of
Just token ->
@ -511,11 +792,56 @@ getISOWeek : Int -> Int -> Int -> Int
getISOWeek year month day =
let
dayOfYear = getDayOfYear year month day
jan1DayOfWeek = getDayOfWeek year 1 1
weekDay = modBy 7 (jan1DayOfWeek + dayOfYear - 1)
weekNumber = ((dayOfYear + jan1DayOfWeek - 1) // 7) + 1
-- Wochentag des 4. Januar (definiert ISO Woche 1)
jan4DayOfWeek = getDayOfWeek year 1 4
-- Tag des Jahres für den Montag von Woche 1
-- Der 4. Januar ist immer in Woche 1
mondayOfWeek1DayOfYear = 4 - jan4DayOfWeek
-- Berechne die Wochennummer
weekNum = ((dayOfYear - mondayOfWeek1DayOfYear) // 7) + 1
in
if weekNumber > 52 then 52 else if weekNumber < 1 then 1 else weekNumber
if weekNum < 1 then
-- Gehört zur letzten Woche des Vorjahres
52 -- Vereinfachung: könnte auch 53 sein
else if weekNum > 52 then
let
-- Prüfe ob Jahr 53 Wochen hat
dec31DayOfWeek = getDayOfWeek year 12 31
jan1DayOfWeek = getDayOfWeek year 1 1
in
-- Jahr hat 53 Wochen wenn 1. Januar ein Donnerstag ist
-- oder 31. Dezember ein Donnerstag ist (bei Schaltjahren)
if jan1DayOfWeek == 3 || (isLeapYear year && jan1DayOfWeek == 2) then
weekNum
else
1
else
weekNum
-- -- Korrigierte ISO-8601 Wochenberechnung
-- getISOWeek : Int -> Int -> Int -> Int
-- getISOWeek year month day =
-- let
-- dayOfYear = getDayOfYear year month day
-- jan1DayOfWeek = getDayOfWeek year 1 1
-- -- ISO 8601: Woche beginnt Montag (0), Jahr beginnt mit der Woche die den 4. Januar enthält
-- correction = (jan1DayOfWeek + 6) |> modBy 7 -- Montag = 0
-- weekNumber = (dayOfYear + correction - 1) // 7
-- in
-- if weekNumber == 0 then
-- -- Gehört zur letzten Woche des Vorjahres
-- getISOWeek (year - 1) 12 31
-- else if weekNumber > 52 then
-- let
-- dec31DayOfWeek = getDayOfWeek year 12 31
-- in
-- -- Prüfe ob es Woche 53 ist oder schon Woche 1 des nächsten Jahres
-- if dec31DayOfWeek < 3 then 1 else weekNumber
-- else
-- weekNumber
getDayOfYear : Int -> Int -> Int -> Int
getDayOfYear year month day =
@ -529,6 +855,7 @@ isLeapYear : Int -> Bool
isLeapYear year =
(modBy 4 year == 0) && ((modBy 100 year /= 0) || (modBy 400 year == 0))
-- Korrigierter getDayOfWeek: Montag = 0, Sonntag = 6 (ISO 8601)
getDayOfWeek : Int -> Int -> Int -> Int
getDayOfWeek year month day =
let
@ -540,25 +867,56 @@ getDayOfWeek year month day =
j = adjustedYear // 100
h = (q + ((13 * (m + 1)) // 5) + k + (k // 4) + (j // 4) - (2 * j)) |> modBy 7
in
-- Konvertiere: Zeller gibt Samstag=0, Sonntag=1, ... Freitag=6
-- ISO 8601 will: Montag=0, ..., Sonntag=6
(h + 5) |> modBy 7
-- Korrigiertes getDateForWeekDay
getDateForWeekDay : Int -> Int -> Int -> String
getDateForWeekDay year week dayOfWeek =
let
-- Finde den 4. Januar (immer in Woche 1 nach ISO 8601)
jan4DayOfWeek = getDayOfWeek year 1 4
daysToMonday = jan4DayOfWeek
firstMondayOfYear = 4 - daysToMonday
daysFromFirstMonday = (week - 1) * 7 + dayOfWeek
totalDays = firstMondayOfYear + daysFromFirstMonday
(finalYear, finalMonth, finalDay) = addDaysToDate year 1 1 totalDays
-- Montag von Woche 1
-- Wenn der 4. Januar z.B. ein Mittwoch ist (dayOfWeek=2),
-- dann ist Montag 2 Tage früher, also der 2. Januar
mondayOfWeek1Date = 4 - jan4DayOfWeek
-- Berechne den Tag: Montag Woche 1 + (Woche - 1) * 7 Tage + Wochentag
targetDayOfYear = mondayOfWeek1Date + ((week - 1) * 7) + dayOfWeek
(finalYear, finalMonth, finalDay) =
if targetDayOfYear < 1 then
-- Datum liegt im Vorjahr
addDaysToDate (year - 1) 12 31 (targetDayOfYear)
else
addDaysToDate year 1 targetDayOfYear 0
in
String.fromInt finalYear ++ "-" ++
String.padLeft 2 '0' (String.fromInt finalMonth) ++ "-" ++
String.padLeft 2 '0' (String.fromInt finalDay)
-- getDateForWeekDay : Int -> Int -> Int -> String
-- getDateForWeekDay year week dayOfWeek =
-- let
-- -- Finde den ersten Montag der ersten ISO-Woche
-- jan4 = { year = year, month = 1, day = 4 }
-- jan4DayOfWeek = getDayOfWeek year 1 4
-- -- Montag der Woche 1 (die Woche mit dem 4. Januar)
-- mondayOfWeek1 = 4 - jan4DayOfWeek
-- -- Berechne Tage vom Jahresbeginn
-- daysFromJan1 = mondayOfWeek1 + (week - 1) * 7 + dayOfWeek
-- (finalYear, finalMonth, finalDay) = addDaysToDate year 1 1 daysFromJan1
-- in
-- String.fromInt finalYear ++ "-" ++
-- String.padLeft 2 '0' (String.fromInt finalMonth) ++ "-" ++
-- String.padLeft 2 '0' (String.fromInt finalDay)
addDaysToDate : Int -> Int -> Int -> Int -> (Int, Int, Int)
addDaysToDate year month day daysToAdd =
addDaysToDate startYear startMonth startDay daysToAdd =
let
daysInMonth m y =
case m of
@ -577,21 +935,71 @@ addDaysToDate year month day daysToAdd =
_ -> 0
helper y m d remaining =
if remaining <= 0 then
if remaining == 0 then
(y, m, d)
else
else if remaining > 0 then
-- Vorwärts zählen
let
daysInCurrentMonth = daysInMonth m y
daysLeftInMonth = daysInCurrentMonth - d + 1
daysLeftInMonth = daysInCurrentMonth - d
in
if remaining < daysLeftInMonth then
if remaining <= daysLeftInMonth then
(y, m, d + remaining)
else if m == 12 then
helper (y + 1) 1 1 (remaining - daysLeftInMonth)
helper (y + 1) 1 1 (remaining - daysLeftInMonth - 1)
else
helper y (m + 1) 1 (remaining - daysLeftInMonth)
helper y (m + 1) 1 (remaining - daysLeftInMonth - 1)
else
-- Rückwärts zählen
if d + remaining >= 1 then
(y, m, d + remaining)
else if m == 1 then
let
prevMonthDays = daysInMonth 12 (y - 1)
in
helper (y - 1) 12 prevMonthDays (remaining + d)
else
let
prevMonthDays = daysInMonth (m - 1) y
in
helper y (m - 1) prevMonthDays (remaining + d)
in
helper year month day daysToAdd
helper startYear startMonth startDay daysToAdd
-- addDaysToDate : Int -> Int -> Int -> Int -> (Int, Int, Int)
-- addDaysToDate year month day daysToAdd =
-- let
-- daysInMonth m y =
-- case m of
-- 1 -> 31
-- 2 -> if isLeapYear y then 29 else 28
-- 3 -> 31
-- 4 -> 30
-- 5 -> 31
-- 6 -> 30
-- 7 -> 31
-- 8 -> 31
-- 9 -> 30
-- 10 -> 31
-- 11 -> 30
-- 12 -> 31
-- _ -> 0
-- helper y m d remaining =
-- if remaining <= 0 then
-- (y, m, d)
-- else
-- let
-- daysInCurrentMonth = daysInMonth m y
-- daysLeftInMonth = daysInCurrentMonth - d + 1
-- in
-- if remaining < daysLeftInMonth then
-- (y, m, d + remaining)
-- else if m == 12 then
-- helper (y + 1) 1 1 (remaining - daysLeftInMonth)
-- else
-- helper y (m + 1) 1 (remaining - daysLeftInMonth)
-- in
-- helper year month day daysToAdd
previousWeek : Int -> Int -> (Int, Int)
previousWeek year week =
@ -602,7 +1010,7 @@ previousWeek year week =
nextWeek : Int -> Int -> (Int, Int)
nextWeek year week =
if week == 52 then
if week >= 52 then
(year + 1, 1)
else
(year, week + 1)
@ -615,6 +1023,16 @@ getWeekDateRange year week =
in
mondayDate ++ " bis " ++ fridayDate
getYearWeekFromDate : String -> (Int, Int)
getYearWeekFromDate dateStr =
let
parts = String.split "-" dateStr
year = parts |> List.head |> Maybe.andThen String.toInt |> Maybe.withDefault 2025
month = parts |> List.drop 1 |> List.head |> Maybe.andThen String.toInt |> Maybe.withDefault 1
day = parts |> List.drop 2 |> List.head |> Maybe.andThen String.toInt |> Maybe.withDefault 1
in
(year, getISOWeek year month day)
calculateHours : String -> String -> Float
calculateHours startTime endTime =
let
@ -724,16 +1142,71 @@ viewUserDashboard model =
[ div [ class "container" ]
[ viewWeekNavigation model
, h2 [ class "title" ] [ text "Stundenplan" ]
, viewScheduleGridWithWeek model
, div [ class "field mt-4" ]
[ div [ class "control" ]
[ button
[ class "button is-primary is-large is-fullwidth"
, onClick SaveTimeEntries
, disabled (List.isEmpty model.selectedEntries)
] [ text "Speichern" ]
-- Status-Anzeige und Bearbeiten-Button
, if model.hasEntriesForCurrentWeek && not model.weekEditMode then
div [ class "notification is-success" ]
[ div [ class "level" ]
[ div [ class "level-left" ]
[ div [ class "level-item" ]
[ span [ class "icon" ]
[ i [ class "fas fa-check-circle" ] [] ]
, span [] [ text "Diese Woche wurde bereits erfasst" ]
]
]
, div [ class "level-right" ]
[ div [ class "level-item" ]
[ button
[ class "button is-warning"
, onClick EnableEditMode
] [ text "Bearbeiten" ]
]
]
]
]
]
else if model.weekEditMode then
div [ class "notification is-warning" ]
[ div [ class "level" ]
[ div [ class "level-left" ]
[ div [ class "level-item" ]
[ span [ class "icon" ]
[ i [ class "fas fa-edit" ] [] ]
, span [] [ text "Bearbeitungsmodus aktiv" ]
]
]
, div [ class "level-right" ]
[ div [ class "level-item" ]
[ button
[ class "button is-danger is-small mr-2"
, onClick DeleteWeekEntries
] [ text "Einträge löschen" ]
, button
[ class "button is-light is-small"
, onClick DisableEditMode
] [ text "Abbrechen" ]
]
]
]
]
else
div [ class "notification is-info is-light" ]
[ text "Wählen Sie die Zeiten aus, die Sie in dieser Woche gearbeitet haben." ]
, viewScheduleGridWithWeek model
, if model.weekEditMode || not model.hasEntriesForCurrentWeek then
div [ class "field mt-4" ]
[ div [ class "control" ]
[ button
[ class "button is-primary is-large is-fullwidth"
, onClick SaveTimeEntries
, disabled (List.isEmpty model.selectedEntries)
] [ text (if model.weekEditMode then "Änderungen speichern" else "Speichern") ]
]
]
else
text ""
, case model.error of
Just err ->
div [ class "notification is-danger mt-4" ] [ text err ]
@ -812,6 +1285,12 @@ viewTimeEntriesTab model =
viewWeekNavigation : Model -> Html Msg
viewWeekNavigation model =
let
dateRange =
case model.weekDates of
Just wd -> wd.range
Nothing -> "Laden..."
in
div [ class "box" ]
[ nav [ class "level" ]
[ div [ class "level-left" ]
@ -829,7 +1308,7 @@ viewWeekNavigation model =
, p [ class "title" ]
[ text ("KW " ++ String.fromInt model.currentWeek ++ " / " ++ String.fromInt model.currentYear) ]
, p [ class "subtitle is-6" ]
[ text (getWeekDateRange model.currentYear model.currentWeek) ]
[ text dateRange ]
]
]
, div [ class "level-right" ]
@ -843,6 +1322,39 @@ viewWeekNavigation model =
]
]
]
-- viewWeekNavigation : Model -> Html Msg
-- viewWeekNavigation model =
-- div [ class "box" ]
-- [ nav [ class "level" ]
-- [ div [ class "level-left" ]
-- [ div [ class "level-item" ]
-- [ button
-- [ class "button is-primary"
-- , onClick PreviousWeek
-- ]
-- [ text "← Vorherige Woche" ]
-- ]
-- ]
-- , div [ class "level-item has-text-centered" ]
-- [ div []
-- [ p [ class "heading" ] [ text "Kalenderwoche" ]
-- , p [ class "title" ]
-- [ text ("KW " ++ String.fromInt model.currentWeek ++ " / " ++ String.fromInt model.currentYear) ]
-- , p [ class "subtitle is-6" ]
-- [ text (getWeekDateRange model.currentYear model.currentWeek) ]
-- ]
-- ]
-- , div [ class "level-right" ]
-- [ div [ class "level-item" ]
-- [ button
-- [ class "button is-primary"
-- , onClick NextWeek
-- ]
-- [ text "Nächste Woche →" ]
-- ]
-- ]
-- ]
-- ]
viewScheduleGridWithWeek : Model -> Html Msg
viewScheduleGridWithWeek model =
@ -869,27 +1381,61 @@ viewScheduleGridWithWeek model =
viewDayColumnWithWeek : Model -> (Int, List Schedule) -> Html Msg
viewDayColumnWithWeek model (dayOfWeek, schedules) =
let
dateForDay = getDateForWeekDay model.currentYear model.currentWeek dayOfWeek
dateForDay =
case model.weekDates of
Just wd ->
wd.dates
|> List.filter (\(day, _) -> day == String.fromInt dayOfWeek)
|> List.head
|> Maybe.map Tuple.second
|> Maybe.withDefault "N/A"
Nothing ->
"Laden..."
in
td [ class "has-background-light", style "vertical-align" "top", style "min-width" "150px" ]
[ p [ class "has-text-centered has-text-weight-bold is-size-7 mb-2" ]
[ text dateForDay ]
, div [] (List.map (viewScheduleItemWithDay model dayOfWeek) schedules)
]
-- viewDayColumnWithWeek : Model -> (Int, List Schedule) -> Html Msg
-- viewDayColumnWithWeek model (dayOfWeek, schedules) =
-- let
-- dateForDay = getDateForWeekDay model.currentYear model.currentWeek dayOfWeek
-- in
-- td [ class "has-background-light", style "vertical-align" "top", style "min-width" "150px" ]
-- [ p [ class "has-text-centered has-text-weight-bold is-size-7 mb-2" ]
-- [ text dateForDay ]
-- , div [] (List.map (viewScheduleItemWithDay model dayOfWeek) schedules)
-- ]
viewScheduleItemWithDay : Model -> Int -> Schedule -> Html Msg
viewScheduleItemWithDay model dayOfWeek schedule =
let
isSelected = List.any (\e -> e.scheduleId == schedule.id && e.dayOfWeek == dayOfWeek) model.selectedEntries
boxClass = if isSelected then "box has-background-success-light" else "box has-background-white"
-- Prüfe ob dieser Eintrag bereits in der DB ist (nur relevant wenn Edit-Mode aktiv)
isLocked = model.hasEntriesForCurrentWeek && not model.weekEditMode
boxClass =
if isLocked then
if isSelected then "box has-background-success-light" else "box has-background-white"
else if isSelected then
"box has-background-success-light"
else
"box has-background-white"
typeText = if schedule.scheduleType == "break" then " (Pause)" else ""
cursorStyle = if isLocked then "not-allowed" else "pointer"
opacity = if isLocked && not isSelected then "0.6" else "1"
in
div
[ class boxClass
, onClick (ToggleScheduleSelection schedule.id dayOfWeek)
, style "cursor" "pointer"
, onClick (if isLocked then Logout else ToggleScheduleSelection schedule.id dayOfWeek) -- Dummy onClick wenn locked
, style "cursor" cursorStyle
, style "margin-bottom" "0.5rem"
, style "padding" "0.75rem"
, style "opacity" opacity
]
[ p [ class "has-text-weight-bold is-size-7" ]
[ text (schedule.startTime ++ " - " ++ schedule.endTime) ]
@ -897,6 +1443,9 @@ viewScheduleItemWithDay model dayOfWeek schedule =
[ text (schedule.title ++ typeText) ]
]
-- (Rest der View-Funktionen bleiben gleich wie in deiner Version)
-- viewScheduleForm, viewScheduleList, viewUserForm, viewUserList, viewWeeklyHoursSummary, viewTimeEntriesList
viewScheduleForm : Model -> Html Msg
viewScheduleForm model =
div [ class "box" ]
@ -1159,11 +1708,7 @@ viewTimeEntriesList model =
filteredEntries = List.filter
(\e ->
let
parts = String.split "-" e.date
entryYear = parts |> List.head |> Maybe.andThen String.toInt |> Maybe.withDefault 0
entryMonth = parts |> List.drop 1 |> List.head |> Maybe.andThen String.toInt |> Maybe.withDefault 1
entryDay = parts |> List.drop 2 |> List.head |> Maybe.andThen String.toInt |> Maybe.withDefault 1
entryWeek = getISOWeek entryYear entryMonth entryDay
(entryYear, entryWeek) = getYearWeekFromDate e.date
in
entryWeek == model.currentWeek && entryYear == model.currentYear
)
@ -1255,6 +1800,18 @@ scheduleDecoder =
(field "type" string)
(field "title" string)
fetchMyTimeEntries : String -> Cmd Msg
fetchMyTimeEntries token =
Http.request
{ method = "GET"
, headers = [ Http.header "Authorization" ("Bearer " ++ token) ]
, url = "/api/my-time-entries"
, body = Http.emptyBody
, expect = Http.expectJson MyTimeEntriesReceived (Decode.list timeEntryDecoder)
, timeout = Nothing
, tracker = Nothing
}
saveTimeEntriesForWeek : String -> List SelectedEntry -> Int -> Int -> List Schedule -> Cmd Msg
saveTimeEntriesForWeek token selectedEntries year week schedules =
let
@ -1292,6 +1849,18 @@ saveTimeEntriesForWeek token selectedEntries year week schedules =
Just cmd -> cmd
Nothing -> Cmd.none
deleteWeekEntries : String -> Int -> Int -> Cmd Msg
deleteWeekEntries token year week =
Http.request
{ method = "DELETE"
, headers = [ Http.header "Authorization" ("Bearer " ++ token) ]
, url = "/api/my-time-entries/week?year=" ++ String.fromInt year ++ "&week=" ++ String.fromInt week
, body = Http.emptyBody
, expect = Http.expectWhatever WeekEntriesDeleted
, timeout = Nothing
, tracker = Nothing
}
createSchedule : String -> NewSchedule -> Cmd Msg
createSchedule token schedule =
case String.toInt schedule.dayOfWeek of
@ -1419,3 +1988,36 @@ weeklyHoursDecoder =
(field "week" int)
(field "year" int)
(field "total_hours" float)
fetchWeekDates : String -> Int -> Int -> Cmd Msg
fetchWeekDates token year week =
Http.request
{ method = "GET"
, headers = [ Http.header "Authorization" ("Bearer " ++ token) ]
, url = "/api/week-dates?year=" ++ String.fromInt year ++ "&week=" ++ String.fromInt week
, body = Http.emptyBody
, expect = Http.expectJson WeekDatesReceived weekDatesDecoder
, timeout = Nothing
, tracker = Nothing
}
weekDatesDecoder : Decoder WeekDates
weekDatesDecoder =
Decode.map4 WeekDates
(field "year" int)
(field "week" int)
(field "dates" (Decode.dict string) |> Decode.map Dict.toList)
(field "range" string)
checkWeekHasEntries : String -> Int -> Int -> Cmd Msg
checkWeekHasEntries token year week =
Http.request
{ method = "GET"
, headers = [ Http.header "Authorization" ("Bearer " ++ token) ]
, url = "/api/week-has-entries?year=" ++ String.fromInt year ++ "&week=" ++ String.fromInt week
, body = Http.emptyBody
, expect = Http.expectJson WeekHasEntriesReceived (field "has_entries" bool)
, timeout = Nothing
, tracker = Nothing
}