fix calendar again
This commit is contained in:
@@ -86,7 +86,7 @@ func UpdateDefaultHours(w http.ResponseWriter, r *http.Request) {
|
|||||||
w.WriteHeader(http.StatusNoContent)
|
w.WriteHeader(http.StatusNoContent)
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- GetWorkingHours (merged default + applied exceptions) ---
|
// --- GetWorkingHours (merged default + applied exceptions, UK-local) ---
|
||||||
func GetWorkingHours(w http.ResponseWriter, r *http.Request) {
|
func GetWorkingHours(w http.ResponseWriter, r *http.Request) {
|
||||||
startStr := r.URL.Query().Get("start")
|
startStr := r.URL.Query().Get("start")
|
||||||
endStr := r.URL.Query().Get("end")
|
endStr := r.URL.Query().Get("end")
|
||||||
@@ -94,17 +94,23 @@ func GetWorkingHours(w http.ResponseWriter, r *http.Request) {
|
|||||||
http.Error(w, "start and end query params required", http.StatusBadRequest)
|
http.Error(w, "start and end query params required", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
start, err := time.Parse("2006-01-02", startStr)
|
|
||||||
|
ukLocation, _ := time.LoadLocation("Europe/London")
|
||||||
|
start, err := time.ParseInLocation("2006-01-02", startStr, ukLocation)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, "invalid start date", http.StatusBadRequest)
|
http.Error(w, "invalid start date", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
end, err := time.Parse("2006-01-02", endStr)
|
end, err := time.ParseInLocation("2006-01-02", endStr, ukLocation)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, "invalid end date", http.StatusBadRequest)
|
http.Error(w, "invalid end date", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set to local start/end of day
|
||||||
|
start = time.Date(start.Year(), start.Month(), start.Day(), 0, 0, 0, 0, ukLocation)
|
||||||
|
end = time.Date(end.Year(), end.Month(), end.Day(), 23, 59, 59, 999999999, ukLocation)
|
||||||
|
|
||||||
// Load default hours
|
// Load default hours
|
||||||
defaultMap := map[int]DefaultHours{}
|
defaultMap := map[int]DefaultHours{}
|
||||||
defRows, _ := db.DB.Query(r.Context(), `
|
defRows, _ := db.DB.Query(r.Context(), `
|
||||||
@@ -119,35 +125,31 @@ func GetWorkingHours(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
defRows.Close()
|
defRows.Close()
|
||||||
|
|
||||||
// Load exceptional applications in range
|
// Load exceptional applications for Mondays in range
|
||||||
appRows, _ := db.DB.Query(r.Context(), `
|
appRows, _ := db.DB.Query(r.Context(), `
|
||||||
SELECT a.group_id, a.week_start
|
SELECT group_id, week_start
|
||||||
FROM exceptional_group_applications a
|
FROM exceptional_group_applications
|
||||||
WHERE a.week_start <= $1 AND a.week_start >= $2 - INTERVAL '6 days'
|
WHERE week_start BETWEEN $1 AND $2
|
||||||
`, end, start) // any week overlapping the range
|
`, start, end)
|
||||||
type appEntry struct {
|
type appEntry struct {
|
||||||
GroupID int
|
GroupID int
|
||||||
WeekStart time.Time
|
WeekStart time.Time
|
||||||
}
|
}
|
||||||
var apps []appEntry
|
apps := []appEntry{}
|
||||||
|
groupIDs := []int{}
|
||||||
for appRows.Next() {
|
for appRows.Next() {
|
||||||
var e appEntry
|
var a appEntry
|
||||||
if err := appRows.Scan(&e.GroupID, &e.WeekStart); err == nil {
|
if err := appRows.Scan(&a.GroupID, &a.WeekStart); err == nil {
|
||||||
apps = append(apps, e)
|
a.WeekStart = time.Date(a.WeekStart.Year(), a.WeekStart.Month(), a.WeekStart.Day(), 0, 0, 0, 0, ukLocation)
|
||||||
|
apps = append(apps, a)
|
||||||
|
groupIDs = append(groupIDs, a.GroupID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
appRows.Close()
|
appRows.Close()
|
||||||
|
|
||||||
// Load exceptional
|
exHoursMap := map[int]map[int]ExceptionalHours{}
|
||||||
// Load exceptional hours for all relevant groups
|
|
||||||
groupIDs := []int{}
|
|
||||||
for _, a := range apps {
|
|
||||||
groupIDs = append(groupIDs, a.GroupID)
|
|
||||||
}
|
|
||||||
|
|
||||||
exHoursMap := map[int]map[int]ExceptionalHours{} // groupID -> weekday -> hours
|
|
||||||
if len(groupIDs) > 0 {
|
if len(groupIDs) > 0 {
|
||||||
query, args, _ := sqlIn("SELECT group_id, weekday, start_time::text, end_time::text, is_open FROM exceptional_working_hours WHERE group_id IN (?)", groupIDs)
|
query, args, _ := sqlIn("SELECT group_id, weekday, start_time::text, end_time::text, is_open FROM exceptional_working_hours WHERE group_id IN (%s)", groupIDs)
|
||||||
rows, _ := db.DB.Query(r.Context(), query, args...)
|
rows, _ := db.DB.Query(r.Context(), query, args...)
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var h ExceptionalHours
|
var h ExceptionalHours
|
||||||
@@ -168,14 +170,22 @@ func GetWorkingHours(w http.ResponseWriter, r *http.Request) {
|
|||||||
if weekday == 0 {
|
if weekday == 0 {
|
||||||
weekday = 6 // Go Sunday=0 -> our Sunday=6
|
weekday = 6 // Go Sunday=0 -> our Sunday=6
|
||||||
} else {
|
} else {
|
||||||
weekday -= 1 // shift Monday=0 ... Sunday=6
|
weekday -= 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// find applied group for this week
|
// Calculate the Monday of this week
|
||||||
|
daysSinceMonday := int(d.Weekday()) - 1
|
||||||
|
if daysSinceMonday < 0 {
|
||||||
|
daysSinceMonday = 6 // Sunday
|
||||||
|
}
|
||||||
|
weekStart := d.AddDate(0, 0, -daysSinceMonday)
|
||||||
|
weekStart = time.Date(weekStart.Year(), weekStart.Month(), weekStart.Day(), 0, 0, 0, 0, ukLocation)
|
||||||
|
|
||||||
var applied *ExceptionalHours
|
var applied *ExceptionalHours
|
||||||
weekStart := d.AddDate(0, 0, -weekday) // Monday of current week
|
weekStartStr := weekStart.Format("2006-01-02")
|
||||||
for _, a := range apps {
|
for _, a := range apps {
|
||||||
if a.WeekStart.Equal(weekStart) {
|
appWeekStartStr := a.WeekStart.Format("2006-01-02")
|
||||||
|
if appWeekStartStr == weekStartStr {
|
||||||
if dayHours, ok := exHoursMap[a.GroupID][weekday]; ok {
|
if dayHours, ok := exHoursMap[a.GroupID][weekday]; ok {
|
||||||
applied = &dayHours
|
applied = &dayHours
|
||||||
}
|
}
|
||||||
@@ -240,7 +250,7 @@ type DayAvailableHours struct {
|
|||||||
Source string `json:"source"`
|
Source string `json:"source"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- GetAvailableHours (working hours with gaps for lunch, bookings, etc.) ---
|
// --- GetAvailableHours (with bookings, UK-local) ---
|
||||||
func GetAvailableHours(w http.ResponseWriter, r *http.Request) {
|
func GetAvailableHours(w http.ResponseWriter, r *http.Request) {
|
||||||
startStr := r.URL.Query().Get("start")
|
startStr := r.URL.Query().Get("start")
|
||||||
endStr := r.URL.Query().Get("end")
|
endStr := r.URL.Query().Get("end")
|
||||||
@@ -249,35 +259,17 @@ func GetAvailableHours(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load UK timezone
|
ukLocation, _ := time.LoadLocation("Europe/London")
|
||||||
ukLocation, err := time.LoadLocation("Europe/London")
|
start, _ := time.ParseInLocation("2006-01-02", startStr, ukLocation)
|
||||||
if err != nil {
|
end, _ := time.ParseInLocation("2006-01-02", endStr, ukLocation)
|
||||||
http.Error(w, "failed to load timezone", http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse dates in UK timezone
|
// set start/end of day
|
||||||
start, err := time.ParseInLocation("2006-01-02", startStr, ukLocation)
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, "invalid start date", http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
end, err := time.ParseInLocation("2006-01-02", endStr, ukLocation)
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, "invalid end date", http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set to start of day (00:00:00) and end of day (23:59:59)
|
|
||||||
start = time.Date(start.Year(), start.Month(), start.Day(), 0, 0, 0, 0, ukLocation)
|
start = time.Date(start.Year(), start.Month(), start.Day(), 0, 0, 0, 0, ukLocation)
|
||||||
end = time.Date(end.Year(), end.Month(), end.Day(), 23, 59, 59, 999999999, ukLocation)
|
end = time.Date(end.Year(), end.Month(), end.Day(), 23, 59, 59, 999999999, ukLocation)
|
||||||
|
|
||||||
// Load default hours
|
// Load default hours
|
||||||
defaultMap := map[int]DefaultHours{}
|
defaultMap := map[int]DefaultHours{}
|
||||||
defRows, _ := db.DB.Query(r.Context(), `
|
defRows, _ := db.DB.Query(r.Context(), `SELECT weekday, start_time::text, end_time::text, is_open FROM working_hours`)
|
||||||
SELECT weekday, start_time::text, end_time::text, is_open
|
|
||||||
FROM working_hours
|
|
||||||
`)
|
|
||||||
for defRows.Next() {
|
for defRows.Next() {
|
||||||
var d DefaultHours
|
var d DefaultHours
|
||||||
if err := defRows.Scan(&d.Weekday, &d.StartTime, &d.EndTime, &d.IsOpen); err == nil {
|
if err := defRows.Scan(&d.Weekday, &d.StartTime, &d.EndTime, &d.IsOpen); err == nil {
|
||||||
@@ -286,34 +278,31 @@ func GetAvailableHours(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
defRows.Close()
|
defRows.Close()
|
||||||
|
|
||||||
// Load exceptional applications in range
|
// Load exceptional applications for Mondays in range
|
||||||
appRows, _ := db.DB.Query(r.Context(), `
|
appRows, _ := db.DB.Query(r.Context(), `
|
||||||
SELECT a.group_id, a.week_start
|
SELECT group_id, week_start
|
||||||
FROM exceptional_group_applications a
|
FROM exceptional_group_applications
|
||||||
WHERE a.week_start <= $1 AND a.week_start >= $2 - INTERVAL '6 days'
|
WHERE week_start BETWEEN $1 AND $2
|
||||||
`, end, start)
|
`, start, end)
|
||||||
type appEntry struct {
|
type appEntry struct {
|
||||||
GroupID int
|
GroupID int
|
||||||
WeekStart time.Time
|
WeekStart time.Time
|
||||||
}
|
}
|
||||||
var apps []appEntry
|
apps := []appEntry{}
|
||||||
|
groupIDs := []int{}
|
||||||
for appRows.Next() {
|
for appRows.Next() {
|
||||||
var e appEntry
|
var a appEntry
|
||||||
if err := appRows.Scan(&e.GroupID, &e.WeekStart); err == nil {
|
if err := appRows.Scan(&a.GroupID, &a.WeekStart); err == nil {
|
||||||
apps = append(apps, e)
|
a.WeekStart = time.Date(a.WeekStart.Year(), a.WeekStart.Month(), a.WeekStart.Day(), 0, 0, 0, 0, ukLocation)
|
||||||
|
apps = append(apps, a)
|
||||||
|
groupIDs = append(groupIDs, a.GroupID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
appRows.Close()
|
appRows.Close()
|
||||||
|
|
||||||
// Load exceptional hours for all relevant groups
|
exHoursMap := map[int]map[int]ExceptionalHours{}
|
||||||
groupIDs := []int{}
|
|
||||||
for _, a := range apps {
|
|
||||||
groupIDs = append(groupIDs, a.GroupID)
|
|
||||||
}
|
|
||||||
|
|
||||||
exHoursMap := map[int]map[int]ExceptionalHours{} // groupID -> weekday -> hours
|
|
||||||
if len(groupIDs) > 0 {
|
if len(groupIDs) > 0 {
|
||||||
query, args, _ := sqlIn("SELECT group_id, weekday, start_time::text, end_time::text, is_open FROM exceptional_working_hours WHERE group_id IN (?)", groupIDs)
|
query, args, _ := sqlIn("SELECT group_id, weekday, start_time::text, end_time::text, is_open FROM exceptional_working_hours WHERE group_id IN (%s)", groupIDs)
|
||||||
rows, _ := db.DB.Query(r.Context(), query, args...)
|
rows, _ := db.DB.Query(r.Context(), query, args...)
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var h ExceptionalHours
|
var h ExceptionalHours
|
||||||
@@ -327,58 +316,56 @@ func GetAvailableHours(w http.ResponseWriter, r *http.Request) {
|
|||||||
rows.Close()
|
rows.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load bookings with their total duration (sum of all services)
|
// Load bookings
|
||||||
bookingRows, err := db.DB.Query(r.Context(), `
|
bookingRows, _ := db.DB.Query(r.Context(), `
|
||||||
SELECT
|
SELECT b.start_time, COALESCE(SUM(s.duration_minutes),0) as total_duration
|
||||||
b.start_time,
|
|
||||||
COALESCE(SUM(s.duration_minutes), 0) as total_duration
|
|
||||||
FROM bookings b
|
FROM bookings b
|
||||||
LEFT JOIN booking_services bs ON b.id = bs.booking_id
|
LEFT JOIN booking_services bs ON b.id = bs.booking_id
|
||||||
LEFT JOIN services s ON bs.service_id = s.id
|
LEFT JOIN services s ON bs.service_id = s.id
|
||||||
WHERE b.start_time >= $1
|
WHERE b.start_time >= $1 AND b.start_time <= $2
|
||||||
AND b.start_time <= $2
|
|
||||||
GROUP BY b.id, b.start_time
|
GROUP BY b.id, b.start_time
|
||||||
ORDER BY b.start_time
|
ORDER BY b.start_time
|
||||||
`, start, end)
|
`, start, end)
|
||||||
|
|
||||||
if err != nil {
|
bookings := map[string][]TimeSlot{}
|
||||||
http.Error(w, "failed to query bookings", http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
bookings := map[string][]TimeSlot{} // date -> booked slots
|
|
||||||
for bookingRows.Next() {
|
for bookingRows.Next() {
|
||||||
var startTime time.Time
|
var t time.Time
|
||||||
var durationMinutes int
|
var dur int
|
||||||
if err := bookingRows.Scan(&startTime, &durationMinutes); err == nil {
|
if err := bookingRows.Scan(&t, &dur); err == nil {
|
||||||
// Convert to UK timezone for date formatting
|
t = t.In(ukLocation)
|
||||||
startTimeUK := startTime.In(ukLocation)
|
dateStr := t.Format("2006-01-02")
|
||||||
dateStr := startTimeUK.Format("2006-01-02")
|
endTime := t.Add(time.Duration(dur) * time.Minute)
|
||||||
endTime := startTimeUK.Add(time.Duration(durationMinutes) * time.Minute)
|
|
||||||
|
|
||||||
bookings[dateStr] = append(bookings[dateStr], TimeSlot{
|
bookings[dateStr] = append(bookings[dateStr], TimeSlot{
|
||||||
StartTime: startTimeUK.Format("15:04"),
|
StartTime: t.Format("15:04"),
|
||||||
EndTime: endTime.Format("15:04"),
|
EndTime: endTime.Format("15:04"),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
bookingRows.Close()
|
bookingRows.Close()
|
||||||
|
|
||||||
// Generate final result per day with gaps
|
// Generate available slots per day
|
||||||
var results []DayAvailableHours
|
var results []DayAvailableHours
|
||||||
for d := start; !d.After(end); d = d.AddDate(0, 0, 1) {
|
for d := start; !d.After(end); d = d.AddDate(0, 0, 1) {
|
||||||
weekday := int(d.Weekday())
|
weekday := int(d.Weekday())
|
||||||
if weekday == 0 {
|
if weekday == 0 {
|
||||||
weekday = 6 // Go Sunday=0 -> our Sunday=6
|
weekday = 6
|
||||||
} else {
|
} else {
|
||||||
weekday -= 1 // shift Monday=0 ... Sunday=6
|
weekday -= 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find applied group for this week
|
// Calculate the Monday of this week
|
||||||
|
daysSinceMonday := int(d.Weekday()) - 1
|
||||||
|
if daysSinceMonday < 0 {
|
||||||
|
daysSinceMonday = 6 // Sunday
|
||||||
|
}
|
||||||
|
weekStart := d.AddDate(0, 0, -daysSinceMonday)
|
||||||
|
weekStart = time.Date(weekStart.Year(), weekStart.Month(), weekStart.Day(), 0, 0, 0, 0, ukLocation)
|
||||||
|
|
||||||
var applied *ExceptionalHours
|
var applied *ExceptionalHours
|
||||||
weekStart := d.AddDate(0, 0, -weekday) // Monday of current week
|
weekStartStr := weekStart.Format("2006-01-02")
|
||||||
for _, a := range apps {
|
for _, a := range apps {
|
||||||
if a.WeekStart.Equal(weekStart) {
|
appWeekStartStr := a.WeekStart.Format("2006-01-02")
|
||||||
|
if appWeekStartStr == weekStartStr {
|
||||||
if dayHours, ok := exHoursMap[a.GroupID][weekday]; ok {
|
if dayHours, ok := exHoursMap[a.GroupID][weekday]; ok {
|
||||||
applied = &dayHours
|
applied = &dayHours
|
||||||
}
|
}
|
||||||
@@ -390,7 +377,6 @@ func GetAvailableHours(w http.ResponseWriter, r *http.Request) {
|
|||||||
day.Date = d.Format("2006-01-02")
|
day.Date = d.Format("2006-01-02")
|
||||||
day.Weekday = weekday
|
day.Weekday = weekday
|
||||||
|
|
||||||
// Determine base working hours and source
|
|
||||||
var baseStart, baseEnd string
|
var baseStart, baseEnd string
|
||||||
var isOpen bool
|
var isOpen bool
|
||||||
if applied != nil {
|
if applied != nil {
|
||||||
@@ -411,18 +397,12 @@ func GetAvailableHours(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
day.IsOpen = isOpen
|
day.IsOpen = isOpen
|
||||||
|
|
||||||
if isOpen {
|
if isOpen {
|
||||||
// Start with the full working hours as one slot
|
slots := []TimeSlot{{StartTime: baseStart, EndTime: baseEnd}}
|
||||||
allSlots := []TimeSlot{{StartTime: baseStart, EndTime: baseEnd}}
|
if booked, ok := bookings[day.Date]; ok {
|
||||||
|
slots = subtractTimeSlots(slots, booked)
|
||||||
// Apply gaps from bookings
|
|
||||||
dateStr := day.Date
|
|
||||||
if booked, ok := bookings[dateStr]; ok {
|
|
||||||
allSlots = subtractTimeSlots(allSlots, booked)
|
|
||||||
}
|
}
|
||||||
|
day.Slots = slots
|
||||||
day.Slots = allSlots
|
|
||||||
} else {
|
} else {
|
||||||
day.Slots = []TimeSlot{}
|
day.Slots = []TimeSlot{}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -142,8 +142,9 @@ func CreateExceptionalGroup(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var parsedWeeks []time.Time
|
var parsedWeeks []time.Time
|
||||||
|
ukLocation, _ := time.LoadLocation("Europe/London")
|
||||||
for _, ws := range g.WeekStarts {
|
for _, ws := range g.WeekStarts {
|
||||||
weekStart, err := time.Parse("2006-01-02", ws)
|
weekStart, err := time.ParseInLocation("2006-01-02", ws, ukLocation)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, "invalid week_start format, expected YYYY-MM-DD", http.StatusBadRequest)
|
http.Error(w, "invalid week_start format, expected YYYY-MM-DD", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
@@ -152,6 +153,8 @@ func CreateExceptionalGroup(w http.ResponseWriter, r *http.Request) {
|
|||||||
http.Error(w, "week_start must be a Monday", http.StatusBadRequest)
|
http.Error(w, "week_start must be a Monday", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// Normalize to UK midnight
|
||||||
|
weekStart = time.Date(weekStart.Year(), weekStart.Month(), weekStart.Day(), 0, 0, 0, 0, ukLocation)
|
||||||
parsedWeeks = append(parsedWeeks, weekStart)
|
parsedWeeks = append(parsedWeeks, weekStart)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -262,8 +265,9 @@ func UpdateExceptionalApplications(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
// Validate and parse weeks
|
// Validate and parse weeks
|
||||||
var parsedWeeks []time.Time
|
var parsedWeeks []time.Time
|
||||||
|
ukLocation, _ := time.LoadLocation("Europe/London")
|
||||||
for _, ws := range req.WeekStarts {
|
for _, ws := range req.WeekStarts {
|
||||||
weekStart, err := time.Parse("2006-01-02", ws)
|
weekStart, err := time.ParseInLocation("2006-01-02", ws, ukLocation)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, "invalid week_start format, expected YYYY-MM-DD", http.StatusBadRequest)
|
http.Error(w, "invalid week_start format, expected YYYY-MM-DD", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
@@ -272,6 +276,8 @@ func UpdateExceptionalApplications(w http.ResponseWriter, r *http.Request) {
|
|||||||
http.Error(w, "week_start must be a Monday", http.StatusBadRequest)
|
http.Error(w, "week_start must be a Monday", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// Normalize to UK midnight
|
||||||
|
weekStart = time.Date(weekStart.Year(), weekStart.Month(), weekStart.Day(), 0, 0, 0, 0, ukLocation)
|
||||||
parsedWeeks = append(parsedWeeks, weekStart)
|
parsedWeeks = append(parsedWeeks, weekStart)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user