Nimbin[12]?Web & Tools / dienstplancreator / js/roster.js

dienstplancreator git · main

Dienstplan (roster) creator, PHP/JS web app

php javascript roster web · first commit 2023-07-13 · last commit 2023-09-14 (3 years ago) · synced 3 days ago · upstream: github.com/nimbin2/dienstplancreator

JavaScript 50% PHP 40.5% CSS 8.5%
git clone https://git.christianimmanuel.de/web-tools/dienstplancreator.gitwget https://git.christianimmanuel.de/web-tools/dienstplancreator/archive/dienstplancreator.tar.gz
js/roster.js 83.5 KB · 1868 lines raw
// get Roster Data ROSTER
getActiveRosterData = async (year_week) => {
    if (year_week === DATE_YEAR_WEEK) {
        return ROSTER
    } else if (year_week === DATE_YEAR_WEEK_TABLE_FROM) {
        return ROSTER_TABLE_FROM
    } else if (year_week === DATE_YEAR_WEEK_TABLE_TO) {
        return ROSTER_TABLE_TO
    } else {
        return getDataRosterToRoster(year_week)
    }
}

getDataRosterToRoster = async (year_week) => {
    let roster = ROSTERS.find(ro => ro.year_week === year_week)?.roster
    pushR = (r) => {
        ROSTERS.push({year_week: year_week, roster: [...r]})
    }
    if (typeof roster !== "undefined") {
        return roster
    } else if (ROSTERS_YEARWEEK.indexOf(year_week) === -1) {
        return createDataA(year_week).then(() => {
            return dbGet_roster_active(year_week).then(r => {pushR(r); return r})
        })
    } else {
        return dbGet_roster_active(year_week).then(r => {pushR(r); return r})
    }
}

getRosterBefore = async (d) => {
    let yws = ROSTERS_YEARWEEK
    let r_id = yws.indexOf(DATE_YEAR_WEEK)+d
    if (r_id && r_id < yws.length) {return await getDataRosterToRoster(yws[r_id]) }
}

// create new Roster
createDataA = async (year_week) => {
    if (!year_week) {year_week = DATE_YEAR_WEEK}
    createR = (changes) => {
        return dbSet_roster(year_week, TIME_STEP, [DAYBREAK_1, DAYBREAK_2]).then(() => {
            if (year_week < DATE_YEAR_WEEK_OLDEST) { 
                changes.forEach(c => dbSet_roster_change_where_yearweek_and_day(year_week, c.day_id, c.start, c.end, c.amount))
            }

            return dbGet_rosters_yearweek().then((yws) => {
                ROSTERS_YEARWEEK = yws
                if (year_week === DATE_YEAR_WEEK) {
                    DATE_YEAR_WEEK_NEWEST = ROSTERS_YEARWEEK[0]
                    DATE_YEAR_WEEK_OLDEST = ROSTERS_YEARWEEK[ROSTERS_YEARWEEK.length-1] 
                }
                return yws
            })
        })
    }
    if (year_week < DATE_YEAR_WEEK_OLDEST) { 
        return dbGet_roster_changes_where_yearweek(DATE_YEAR_WEEK_OLDEST).then((changes) => {
            return createR(changes)
        })
    } else {
        return createR()
    }
}

// get Active Roster
getActiveRoster = async (reload) => {
    showLoading("getActiveRoster")
    EDITABLE_ROWS_TOP = await dbGet_roster_editable_rows_top(DATE_YEAR_WEEK)
    EDITABLE_ROWS_BOTTOM = await dbGet_roster_editable_rows_bottom(DATE_YEAR_WEEK)
    EDITABLE_CELLS_RIGHT = await dbGet_roster_editable_cells_right(DATE_YEAR_WEEK)
    ROSTER_PERSON_INFOS = await dbGet_roster_info_where_yearweek(DATE_YEAR_WEEK)
    let yws = await dbGet_rosters_yearweek()
    let roster_data
    if (yws.indexOf(DATE_YEAR_WEEK) > -1) {
        roster_data = await dbGet_roster_where_yearweek(DATE_YEAR_WEEK)
    } else {
        roster_data = await dbGet_roster_where_yearweek(yws[0])
    }
    DAYBREAK_1 = roster_data.break_60
    DAYBREAK_2 = roster_data.break_90
    TIME_STEP  = roster_data.time_step

    callback_1 = () => {
        return getSettings().then(() => {
            if ((EDITABLE_ROW) || (EDITABLE_ROWS_TOP.length > 0)) {
                setEditableRowTrue()
            } else {document.querySelector("#Roster-week-add-editable-row button.row-top").classList.remove("active")}
            if ((EDITABLE_ROW_BOTTOM) ||(EDITABLE_ROWS_BOTTOM.length > 0)) {
                setEditableRowBottomTrue()
            } else {document.querySelector("#Roster-week-add-editable-row button.row-bottom").classList.remove("active")}
            if ((EDITABLE_CELLS_RIGHT.length > 0) || EDITABLE_CELL) {
                EDITABLE_CELL = true; document.querySelector("#Roster-week-add-editable-cell button").classList.add("active")
            } else if (DISPLAY_EDITABLE_CELL.v) {
                setEditableCellTrue()
            } else {document.querySelector("#Roster-week-add-editable-cell button").classList.remove("active")}

            renderPerson()
            let person_list_buttons = document.querySelectorAll("#Person-list-buttons button")
            for (let i=0; i<person_list_buttons.length; i++) {person_list_buttons[i].classList.remove("active")}
            if (person_list_buttons.length > 0) person_list_buttons[0].classList.add("active")

            loadRosterTableWeek()
            return loadRosterTable().then(() => {
                hideLoading("getActiveRoster")
                setZoomLevel(SETTINGS.zoom_web)
                return
            })
        })
    }
    callback_0 = () => {
        if (ROSTERS_YEARWEEK.indexOf(DATE_YEAR_WEEK) === -1) {
            return createDataA().then(() => {return callback_1()})
        } else {
            return callback_1()
        }
    }
    return callback_0()
}

getActiveRosterWeek = async (reload) => {
    showLoading("getActiveRoster")
    EDITABLE_ROWS_TOP = await dbGet_roster_editable_rows_top(DATE_YEAR_WEEK)
    EDITABLE_ROWS_BOTTOM = await dbGet_roster_editable_rows_bottom(DATE_YEAR_WEEK)
    EDITABLE_CELLS_RIGHT = await dbGet_roster_editable_cells_right(DATE_YEAR_WEEK)
    ROSTER_PERSON_INFOS = await dbGet_roster_info_where_yearweek(DATE_YEAR_WEEK)
    let yws = await dbGet_rosters_yearweek()
    let roster_data
    if (yws.indexOf(DATE_YEAR_WEEK) > -1) {
        roster_data = await dbGet_roster_where_yearweek(DATE_YEAR_WEEK)
    } else {
        roster_data = await dbGet_roster_where_yearweek(yws[0])
    }
    DAYBREAK_1 = roster_data.break_60
    DAYBREAK_2 = roster_data.break_90
    TIME_STEP  = roster_data.time_step
    NOWEEKEDIT = true

    callback_1 = () => {
        return getSettings().then(() => {
            if ((DISPLAY_EDITABLE_ROW.v) || (EDITABLE_ROWS_TOP.length > 0)) {
                EDITABLE_ROW = true
            } else {EDITABLE_ROW = false }
            if ((DISPLAY_EDITABLE_ROW_BOTTOM.v) ||(EDITABLE_ROWS_BOTTOM.length > 0)) {
                EDITABLE_ROW_BOTTOM = true
            } else {EDITABLE_ROW_BOTTOM = false}
            if (EDITABLE_CELLS_RIGHT.length > 0) {
                EDITABLE_CELL = true
            } else if (DISPLAY_EDITABLE_CELL.v) {
                EDITABLE_CELL = true
            } else {EDITABLE_CELL = false}

            loadRosterTableWeek()
            hideLoading("getActiveRoster")
        })
    }
    callback_0 = () => {
        if (ROSTERS_YEARWEEK.indexOf(DATE_YEAR_WEEK) === -1) {
            hideLoading("getActiveRoster")
            return
        } else {
            return callback_1()
        }
    }
    return callback_0()
}

// Roster Amount per day
changeTimeAmount = async (el, day_id, direction, ael) => {
    let roster = ROSTER[day_id]
    let blocks_multiple = document.getElementById(`Table-times-thead-tr-${day_id}`).querySelectorAll(".th-amount-container.active")
    let block_this_active = document.getElementById(`Table-times-thead-tr-${day_id}`).getElementsByClassName("th-amount-container")[ael]
    if (!block_this_active.classList.contains("active")) {
        if (blocks_multiple.length > 0) {
            for (i=0; i< blocks_multiple.length; i++) {
                blocks_multiple[i].classList.remove("active")
            }
            blocks_multiple = []
        }
    }

    if (blocks_multiple.length > 0) {
        for (i=0; i< blocks_multiple.length; i++) {
            let block = blocks_multiple[i]
            let block_time = block.parentNode.parentNode
            let time = parseFloat(block_time.getAttribute("time"))
            let t_id = roster.d.map(a => {return a.t}).indexOf(time)
            let amount = roster.d[t_id].a
            let newAmount = amount
            if (direction === "up") {
                if (amount < PERSONS.length) newAmount = amount+1
            } else {
                if (amount > 0) newAmount = amount-1
            }

            ROSTER[day_id].d[t_id].a = newAmount
            block_time.getElementsByClassName("time-amount-max")[0].innerHTML = newAmount

        }
        setTimeAmountTh(day_id)
        let change_times = structuredClone(ROSTER[day_id].d)
        change_times.forEach(data => {data.time = data.t; data.amount = data.a; delete data.t; delete data.a})
        setDataAC(DATE_YEAR_WEEK, day_id, change_times)
        return
    }
    let amount = roster.d[ael].a
    let newAmount = amount
    if (direction === "up") {
        if (amount < PERSONS.length) newAmount = amount+1
    } else {
        if (amount > 0) newAmount = amount-1
    }

    if (amount === newAmount) return

    ROSTER[day_id].d[ael].a = newAmount
    let change_times = structuredClone(ROSTER[day_id].d)
    change_times.forEach(data => {data.time = data.t; data.amount = data.a; delete data.t; delete data.a})
    setDataAC(DATE_YEAR_WEEK, day_id, change_times)
    document.getElementById("Roster-"+day_id).getElementsByClassName("time-amount-max")[ael].innerHTML = newAmount
    setTimeAmountTh(day_id)
}

getPersonAmountForDay = (day_id) => {
	let amount_day = []
	ROSTER[day_id].d.forEach(time => amount_day.push({ time: time.t, is: getPersonAmountForDayTime(day_id, time.t).length, max:  time.a }) )
	return amount_day
}
getPersonAmountForDayTime = (day_id, time) => {
    let shifts = PERSONS.map(p => p.shifts).flat(1).filter(s => s.day_id === day_id)
    let shifts_in_time = []
    shifts.forEach(shift => {
        if (shift.start <= time && shift.end >= time) shifts_in_time.push(shift)
    })
    return shifts_in_time
}

setTimeAmountTh = async (day_id) => {
    for (let id=0; id<ROSTER[day_id].d.length; id++) {
        let el = ROSTER[day_id].d[id]
        let amount = getPersonAmountForDayTime(day_id, el.t).length
        let color_id = Math.round((amount*colors_red_orange_green.length)/ROSTER[day_id].d[id].a)
        let color = colors_red_orange_green[color_id]
        if (color_id >= colors_red_orange_green.length) color = colors_red_orange_green[colors_red_orange_green.length-1]
        if (amount > ROSTER[day_id].d[id].a) color = "#458caf"
        else if (amount === ROSTER[day_id].d[id].a) color = "#61a64d"

        let block_th = document.getElementById("Table-times-thead-tr-"+day_id).querySelector('[time="'+el.t+'"]')

        block_th.setAttribute("style", `background-color: ${color}; border-color: ${color}87;`)
        block_th.getElementsByClassName("time-amount-is")[0].innerHTML = amount+"/"
    }
}


setTimeAmount = async (person_id) => {
    let person = PERSONS.find(p => p.id === person_id)
    for (let day_id=0; day_id<ROSTER.length; day_id++) {
        let blocks_name = document.querySelectorAll(`#Roster-days-container .tr-name-${person_id}`)

        let hours_breaks = await getPersonDayHoursAndBreaks(person_id, day_id)
		let hours = hours_breaks[0]
		let breaks = hours_breaks[1]

		let hours_breaks_a  = await getPersonHoursAndBreaks(person_id)
		let hours_a = hours_breaks_a[0]
		let breaks_a = hours_breaks_a[1]

        let hours_total  = new Promise((res, rej) => {res(getPersonHoursWithFreedays(person_id))})
        hours_total = await hours_total 
        hours_total = parseFloat(hours_total.toFixed(2))

        for (var i = 0; i < blocks_name.length; i++) {
            blocks_name[i].getElementsByClassName("hours-given")[0].innerHTML = hours_a+"/"+hours_total
        }

        document.querySelector(`#Tr-${person_id}-day-${day_id} .hours-today`).innerHTML = hours-person.mpa[day_id]
        document.querySelector(`#Tr-${person_id}-day-${day_id} .break-today`).innerHTML = breaks
        let block_overtime = document.querySelector(`#Tr-${person_id}-day-${day_id} .td-overtime`)
        let overtime = person.overtime
        if (overtime === 0) {block_overtime.classList.add("green"); block_overtime.classList.remove("orange", "red")}
        else if (overtime > 0) {overtime = `+${overtime}`; block_overtime.classList.add("red"); block_overtime.classList.remove("green", "orange")}
        else if (overtime < 0) {block_overtime.classList.add("orange"); block_overtime.classList.remove("green", "red")}
        block_overtime.innerHTML = overtime

        document.querySelector(`#Person-table-tr-${person_id} .td-overtime`).innerHTML = overtime
        setTimeAmountTh(day_id)
    }
}

getBlockTimeTd = (person_id, day_id, time) => { 
    let block_table_row = document.getElementById(`Tr-${person_id}-day-${day_id}`)
    if (!block_table_row) return
    let block_td = block_table_row.querySelector('[time="'+time+'"]')
    return block_td
}

removePersonFromShiftDay = async (person_id, day_id, year_week, noreload) => {
    if (!year_week) { year_week = DATE_YEAR_WEEK}
    if (day_id >= DAYS.length) return
    if (ROSTERS_YEARWEEK.indexOf(year_week) === -1) return
    let shifts = []
    let year_week_set = false
    return dbRemove_shift_where_person_and_year_week_and_day(person_id, year_week, day_id).then(() => {
        if (year_week !== DATE_YEAR_WEEK) return
        if (!noreload) {
            return setActivePerson(person_id).then((person) => {
                return reloadPersonDayData(person_id, day_id, year_week)
            })
        }
        return
    })
}

// get auto shift
getBestShiftStarts = async (person_id, day_id) => {
    let amount_sortet = getPersonAmountForDay(day_id).sort((a, b) => {return (b.max-b.is)-(a.max-a.is) })
    let average = new Promise((res, rej) => {res(getPersonAverageHours(person_id, day_id))})
    average = await average 
    let best = []
    let max_diff = amount_sortet.map(da => (da.max-da.is))[0]
    getMax = (max, is) => {
        let re = max-is
        if (re < 0) {re = (re*max_diff/100)*max_diff}
        else {re = (re*max_diff/100) }
        return re
    }
    ROSTER[day_id].d.forEach(d => {
        max_diff = amount_sortet.map(da => (da.max-da.is))[0]
        let rank = amount_sortet.filter(da => { return da.time >= d.t  && da.time < d.t+average}).map(da => ((getMax(da.max, da.is)*100)/da.max)).reduce((a, b) => a + b, 0)
        best.push({time: d.t, rank: rank})
    })

    return best.sort((a,b) => b.rank-a.rank)
} 
getAutoTimeStart = async (person_id, day_id) => {
    let average = await getPersonAverageHours(person_id, day_id)
    let start = await getBestShiftStarts(person_id, day_id)
    start = start[0].time
    if (start+average > ROSTER[day_id].d[ROSTER[day_id].d.length-1].t) {start = ROSTER[day_id].d[ROSTER[day_id].d.length-1].t-average}
    if (start < ROSTER[day_id].d[0].t) {start = ROSTER[day_id].d[0].t}
    return start
}
/*
autoAddShifts = async (rerun) => {
    showLoading("autoAddShifts")
    let person_without = new Promise((res, rej) => {res(getPersonWithoutShifts())})
    person_without = await person_without 
    if (person_without.length === 0) return
    while (person_without.length > 0) {
        let max_id = person_without.map(p => p[2] === person_without[0][2]).indexOf(false)-1
        if (!max_id >= 0) {max_id = 0}
        let person_without_id = Math.floor(Math.random() * max_id)
        let person = person_without[person_without_id]
        let person_id = person[0]
        let days = person[1]
        for (let di=0; di<days.length;di++) {
            let day_id = days[di]
            let as = new Promise((res, rej) => {res(getAutoTimeStart(person_id, day_id))})
            as = await as
            addNewShiftToRoster(person_id, day_id, as, undefined, undefined, true)
        }
        person_without.splice(person_without_id, 1)
    }
    getActiveRoster().then(() => hideLoading("autoAddShifts"))
}
        */
addExtrashiftToRoster = (person_id, day_id, time_start, start, end) => {
    let prev_duration
    let average
    getD = async () => {
        prev_duration = await getPersonDayHoursAndBreaks(person_id, day_id)
        prev_duration = prev_duration [0]
        average = new Promise((res, rej) => {res(getPersonAverageHours(person_id, day_id))})
        average = await average 
    }
    setD = () => {
        let duration = average-prev_duration-TIME_STEP
        if (duration < 0) {duration = 2}

        let time_end = time_start+duration
        if (time_start < start) {
            if (time_end > start-(TIME_STEP*2)) {time_end = start-(TIME_STEP*2)}
        }
        if (time_start > end) {
            if (time_end > ROSTER[day_id].d[ROSTER[day_id].d.length-1].t) {time_end = ROSTER[day_id].d[ROSTER[day_id].d.length-1].t}
        }
        if (time_end < time_start) { time_end = time_start}
        
        addShift(DATE_YEAR_WEEK, day_id, person_id, time_start, time_end)
    }
    getD().then(() => setD())
}
addShiftToNextRoster = (person_id, day_id, time_start, time_end, should_delete) => {
    let next_yw = getDateToRosterDate(new Date(getDateOfISOWeek(DATE_YEAR_WEEK).getTime() + 7 * 24 * 60 * 60 * 1000))[0]
    ROSTERS_YEARWEEK.indexOf(next_yw === -1) &&
    createDataA(next_yw)
    should_delete && 
    dbRemove_shift_where_person_and_year_week_and_day(person_id, next_yw, day_id).then(() => {
        dbAdd_shift(next_yw, day_id, person_id, time_start, time_end)
    })
}
getShiftEnd = async (person_id, day_id, time_start, year_week) => {
    if (typeof year_week === "undefined") {year_week = DATE_YEAR_WEEK}
    let roster = await getActiveRosterData(year_week)
    let person_average_hours = await getPersonAverageHours(person_id, day_id, year_week)
    if (person_average_hours > 6) person_average_hours = person_average_hours+DAYBREAK_1
    if (person_average_hours > 9) person_average_hours = person_average_hours+DAYBREAK_2
    let time_end = time_start+person_average_hours-TIME_STEP

    if (time_start === roster[day_id].d[0].t && roster[day_id].d[1].t-roster[day_id].d[0].t !== TIME_STEP) {
        time_end = time_end-TIME_STEPS[TIME_STEPS.indexOf(TIME_STEP)-1]
    }
    if (time_end > roster[day_id].d[roster[day_id].d.length-1].t+TIME_STEP) time_end = roster[day_id].d[roster[day_id].d.length-1].t+TIME_STEP

    return time_end
}
setShift = async (year_week, day_id, person_id, shift_id) => {
    showLoading(`setShift-${shift_id}`)
    if (year_week === DATE_YEAR_WEEK) {
        setActivePerson(person_id, year_week).then((persons) => {
            reloadPersonDayData(person_id, day_id)
            hideLoading(`setShift-${shift_id}`)
        })
    }
    if (document.getElementById("Roster-week-table-to")) {
        setActivePerson(person_id, year_week).then((persons) => {
            reloadPersonDayData(person_id, day_id, year_week, "Roster-week-table-to").then(() => {
                hideLoading(`setShift-${shift_id}`)
            })
        })
    }
}
addShift = async (year_week, day_id, person_id, start, end, noreload) => {
    return dbAdd_shift(year_week, day_id, person_id, start, end).then(shift => {
            if ((year_week === DATE_YEAR_WEEK) || (year_week === DATE_YEAR_WEEK_TABLE_TO)) {
            if (!noreload) {
                return setShift(year_week, day_id, person_id, shift.id)
            }
        }
        return shift.shift_id
    })
}
updateShift = async (shift, start, end, noreload) => {
    dbSet_shift_where_id(shift.id, start, end).then(() => {
        if (!noreload) {
            setShift(shift.yearweek, shift.day_id, shift.person_id, shift.id)
        }
    })
}
addNewShiftToRoster = async (person_id, day_id, time_start, time_end, year_week, noreload) => {
    let person = await getActivePersonById(person_id)
    let year_week_set = false
    if (typeof year_week === "undefined") {year_week = DATE_YEAR_WEEK; year_week_set = true}
    let cif = await checkIfPersonDayIsFree(person_id, day_id, year_week)
    if (cif !== false) return

    let roster = await getActiveRosterData(year_week)
    if (typeof time_end === "undefined") {
        time_end = await getShiftEnd(person_id, day_id, time_start, year_week)
    }
    add = () => {
        ROSTERS_YEARWEEK.indexOf(year_week) === -1 && createDataA(year_week)
        return removePersonFromShiftDay(person.id, day_id, year_week, true).then(() => {
            return addShift(year_week, day_id, person_id, time_start, time_end, noreload)
        })
    }
    return add()
}

splitShift = (person_id, day_id, shift_id, time, start, end) => {
    dbRemove_shift_where_id(shift_id).then(() => {
        addShift(DATE_YEAR_WEEK, day_id, person_id, start, time)
        addShift(DATE_YEAR_WEEK, day_id, person_id, time+TIME_STEP, end)
    })
}

modifyShift = (year_week, day_id, shift_id, person_id, time, direction) => {
	showLoading("modifyShift") 
    getActivePersonById(person_id, year_week).then(person => {
        let shifts = person.shifts.filter(s => s.day_id === day_id)
        let shift = shifts.find(s => s.id === shift_id)
        let shift_b = shifts.find(s => s.start !== shift.start)


        if (time < ROSTER[day_id].d[0].t) {time = ROSTER[day_id].d[0].t}
        else if (time > ROSTER[day_id].d[ROSTER[day_id].d.length-1].t) {time = ROSTER[day_id].d[ROSTER[day_id].d.length-1].t}
        if (direction !== 1 && time > shift.end) {
            addNewShiftToRoster(person_id, day_id, time, undefined, year_week)
            return
        }

        if ((typeof shift_b !== "undefined" && time-TIME_STEP === shift_b.end) || (typeof shift_b !== "undefined" && time+TIME_STEP === shift_b.start)) {
            let start
            let end
            let new_shift
            if (time-TIME_STEP === shift_b.end) {
                start = shift_b.start
                end = shift.end
            } else if (time+TIME_STEP === shift_b.start) {
                start = shift.start
                end = shift_b.end
            }
            addNewShiftToRoster(person_id, day_id, start, end, year_week)
        } else if (direction === 1) {
            updateShift(shift, shift.start, time+TIME_STEP)
        } else {
            updateShift(shift, time, shift.end)
        }
        hideLoading("modifyShift")
    })
}

timeHasShift = (time, shifts) => {
    let has_shift = false
    shifts.forEach(shift => {
        if (shift.start <= time && shift.end > time) has_shift = true
    })
    return has_shift
}

createBlockExtraShift = (person, day_id, time, start, end) => {
    let person_id = person.id
    let block_add = document.createElement("button")
    block_add.innerHTML = "+"
    block_add.classList.add("roster-add-extra-shift")
    block_add.setAttribute("title", `${person.name}\nExtra Schicht hinzufügen\n${timeToHHMM(time)}`)
    block_add.setAttribute("onClick", `addExtrashiftToRoster(${person_id}, ${day_id}, ${time}, ${start}, ${end})`)
    return block_add
}

// load editable Roster
addRosterButtonInnerShiftExtendShort = (person_id, day_id, shift_id, time) => {
    getActivePersonById(person_id).then(person => {
        let shift = person.shifts.find(s => s.id === shift_id)
        let start = shift.start
        let end   = shift.end
        if (end-start <= TIME_STEP) return
        let block_td = getBlockTimeTd(person_id, day_id, time) 
        let block_td_container = block_td.getElementsByClassName("container")[0]

        let block_container = document.createElement("div")
        let block_button_shorten = document.createElement("button")
        let block_button_extend = document.createElement("button")

        let block_container_remove = block_td_container.getElementsByClassName("shiftmodify-buttons-container-inner")[0]
        let block_extend_remove = block_td_container.getElementsByClassName("roster-extend-shift")[0]
        if (typeof block_container_remove !== "undefined") block_container_remove.remove()
        if (typeof block_extend_remove !== "undefined") block_extend_remove.remove()

        block_container.classList.add("shiftmodify-buttons-container-inner")

        if (time !== start) {
            block_button_extend.classList.add("chevron-right")
            block_button_extend.setAttribute("onclick", `modifyShift("${DATE_YEAR_WEEK}", ${day_id}, ${shift_id}, ${person_id}, ${time}, 0)`)
            block_button_extend.setAttribute("title", `${person.name}\nSchichtstart verschieben\n${timeToHHMM(time)}`)
            block_container.appendChild(block_button_extend)
        }

        if (time !== end) {
            block_button_shorten.classList.add("chevron-left")
            block_button_shorten.setAttribute("onclick", `modifyShift("${DATE_YEAR_WEEK}", ${day_id}, ${shift_id}, ${person_id}, ${time}, 1)`)
            block_button_shorten.setAttribute("title", `${person.name}\nSchichtende verschieben\n${timeToHHMM(time+TIME_STEP)}`)
            block_container.appendChild(block_button_shorten)
        }

        block_td_container.appendChild(block_container)


        if (time === start) {
            block_button_shorten.setAttribute("style", "width: 100%;")
        } else if (time === end) {
            block_button_extend.setAttribute("style", "width: 100%;")
        }
    })
}

addRosterButtonExtendShift = (person, day_id, shift_id, time, direction) => {
    let person_id = person.id
    let block_td = getBlockTimeTd(person_id, day_id, time) 
    let block_container = block_td.getElementsByClassName("container")[0]
    let block_container_mod = block_container.getElementsByClassName("shiftmodify-buttons-container")[0]

    if (typeof block_container_mod === "undefined") return
    let block_button = document.createElement("button")
    let chevron = "chevron-left"
    let click = `modifyShift("${DATE_YEAR_WEEK}", ${day_id}, ${shift_id}, ${person_id}, ${time}, 0)`
    let title_text = `${person.name}\nSchichtstart verschieben\n${timeToHHMM(time)}`
    if (direction === 1) {
        chevron = "chevron-right"
        click = `modifyShift("${DATE_YEAR_WEEK}", ${day_id}, ${shift_id}, ${person_id}, ${time}, 1)`
        title_text = `${person.name}\nSchichtende verschieben\n${timeToHHMM(time+TIME_STEP)}`
    }

    block_button.classList.add("roster-extend-shift", chevron)
    block_button.setAttribute("title", title_text)
    block_button.setAttribute("onClick", click)
    block_container_mod.appendChild(block_button)
}

addRosterButtonSplitShift = (person, day_id, shift_id, time, start, end) => {
    let person_id = person.id
    let block_td = getBlockTimeTd(person_id, day_id, time)
    let block_td_container = block_td.getElementsByClassName("container")[0]
    let block_remove_button = block_td.getElementsByClassName("container")[0].getElementsByClassName("shift-split")[0]
    if (block_remove_button) block_td_container.removeChild(block_remove_button)

    let block_button = document.createElement("button")
    block_button.classList.add("shift-split", "button-x", "shiftmodify-button-header")

    block_button.setAttribute("title", `${person.name}\nSchicht teilen\n${timeToHHMM(time)}`)
    block_button.setAttribute("onClick", `splitShift(${person_id}, ${day_id}, ${shift_id}, ${time}, ${start}, ${end})`)
    block_td_container.appendChild(block_button)
}

loadEditablePersonDayShift = (person_id, day_id, noWReload) => {
    showLoading("loadEditablePersonDayShift")
    let person = PERSONS.find(p => p.id === person_id)
    let shifts = person.shifts.filter(s => s.day_id === day_id)
	shifts.sort((a, b) => a.start-b.start)
	let shift  = shifts[0]
    if (shift) {
        // remove extra shift buttons
        let block_tr = document.getElementById(`Tr-${person_id}-day-${day_id}`)
        let remove_splits = block_tr.querySelectorAll(".shift-split")
        let remove_adds = block_tr.querySelectorAll(".roster-add-extra-shift")
        let remove_extend = block_tr.querySelectorAll(".roster-extend-shift")
        let removes_mod_inner = block_tr.querySelectorAll(".shiftmodify-buttons-container-inner")
        let removes = [...remove_splits, ...remove_adds, ...removes_mod_inner, ...remove_extend ]
        for (i=0; i<removes.length; i++) {
            removes[i].parentNode.removeChild(removes[i])
        }

        let is_before	= true
        let is_after	= false
        let is_during	= false
        ROSTER[day_id].d.forEach(data => {
            let time = data.t
            let block_td = getBlockTimeTd(person_id, day_id, time)
            let block_container = block_td.querySelector(".container")
            block_td.classList.remove("assigned")
            let has_shift = timeHasShift(time, shifts)
            if (has_shift) {
                is_during   = true
                is_before	= false
                if (is_after) {is_after = false; shift = shifts[1]}
                block_td.classList.add("assigned")
            } else if (is_during) {
                is_during   = false
                is_after    = true
            }
            if (has_shift) {
                block_td.classList.add("assigned")
                addRosterButtonInnerShiftExtendShort(person_id, day_id, shift.id, time)
                if (shifts.length === 1 && time !== shift.start && time !== shift.end-TIME_STEP)
                    addRosterButtonSplitShift(person, day_id, shifts[0].id, time, shifts[0].start, shifts[0].end)
            } else if (is_before) {
                if (time !== shift.start-TIME_STEP && shifts.length === 1) block_container.appendChild(createBlockExtraShift(person, day_id, time, shift.start, shift.end))
                addRosterButtonExtendShift(person, day_id, shift.id, time)
            } else if (is_after) {
                if (time !== shift.end && shifts.length === 1) block_container.appendChild(createBlockExtraShift(person, day_id, time, shift.start, shift.end))
                addRosterButtonExtendShift(person, day_id, shift.id, time, 1)
            } 
        })
    }
    checkIfPersonDayIsFree(person_id, day_id).then(check_if_free => {
        let h_mpa = check_if_free ? 0 : person.mpa[day_id]
        if (h_mpa === 0) {h_mpa = ""}
        let block_mpa = document.querySelector(`#Tr-${person_id}-day-${day_id} .hours-mpa`)
        if (block_mpa) { block_mpa.innerHTML = `<div>${h_mpa}</div>`}
    })
    hideLoading("loadEditablePersonDayShift")
}

loadEditableDataIntoRosterTable = (el, day_id) => {
    showLoading("loadEditableDataIntoRosterTable")
    window.onmousemove = (e) => eventListenerEditRosterDay(e, day_id)
    el.classList.add("d-none")
    let block = document.querySelector(`#Roster-${day_id}`)
    if (block.classList.contains("editable")) {
        hideLoading("loadEditableDataIntoRosterTable")
        return
    }
    block.classList.add("editable")

    PERSONS.forEach(person => {
        loadEditablePersonDayShift(person.id, day_id)
    })
    hideLoading("loadEditableDataIntoRosterTable")
}

getShiftTimeText = (shift, day_id, table_id) => {
    if (typeof table_id === "undefined") {table_id = "Roster-week-table"}
    let shift_start = shift.start
    let shift_end = shift.end

    let text_start = timeToHHMM(shift_start)
    if (text_start.length === 4) {text_start = " "+text_start}
    let text_end = timeToHHMM(shift_end)
    if (text_end.length === 4) {text_end = " "+text_end}
    if (EDITABLE_TABLES.indexOf(table_id) > -1) {
        return `<input type="time" title="Schichtstart ändern" required onfocusout='modifyShift("${shift.yearweek}", ${day_id}, ${shift.id}, ${shift.person_id}, hhMMToTime(this.value), 0)' value="${timeToFullHHMM(shift_start)}" class="start" time="${shift_start}"><p> - </p><input type="time" title="Schichtende ändern" required onfocusout='modifyShift("${shift.yearweek}", ${day_id}, ${shift.id}, ${shift.person_id} , hhMMToTime(this.value)-TIME_STEP, 1)' value="${timeToFullHHMM(shift_end)}" class="end" time="${shift_end}">`
    } else {
        return `<p class="start" time="${shift_start}">${text_start}</p><p> - </p><p class="end" time="${shift_end}">${text_end}</p>`
    }
}


// reload Person data
reloadPersonData = (person_id, year_week, table_id) => {
    showLoading(`reloadPersonData${person_id}`)
    DAYS.forEach((day, day_id) => {
        reloadPersonDayData(person_id, day_id, year_week, table_id).then(() => {
            hideLoading(`reloadPersonData${person_id}`)
        })
    })
}

reloadPersonDayData = async (person_id, day_id, year_week, table_id) => {
    showLoading(`reloadPersonDayData-${person_id}-${day_id}`)
    if (!year_week) {year_week = DATE_YEAR_WEEK}
    if (!table_id) {table_id = "Roster-week-table"}
    getActivePersonById(person_id, year_week).then(person => {
    // set week
        if (year_week === DATE_YEAR_WEEK && table_id !== "Roster-week-table") {
            renderRosterTableWeekPersonDay(person, day_id, year_week, document.querySelector(`#Roster-week-table .Roster-week-td-name-${person_id}-day-${day_id}`), table_id)
        }
        renderRosterTableWeekPersonDay(person, day_id, year_week, document.querySelector(`#${table_id} .Roster-week-td-name-${person_id}-day-${day_id}`), table_id).then(() => {
    // set week hours and breaks
            let block_name = document.querySelector(`#${table_id} .Roster-week-tr-${person_id} .Roster-week-td-name`)
            if (!block_name) {hideLoading(`reloadPersonDayData-${person_id}-${day_id}`); return}
            let block_td_hours = block_name.querySelector(".hours")
            let block_td_breaks = block_name.querySelector(".breaks")
            getPersonHoursAndBreaks(person_id, year_week).then(hours_breaks_all => {
                let breaks_all = hours_breaks_all[1]
                getPersonHoursWithFreedays(person_id, year_week).then(hours_total => {
                    hours_total = parseFloat(hours_total.toFixed(2))
                    block_td_hours.innerHTML = hours_breaks_all[0]+"/"+hours_total
                    block_td_breaks.innerHTML = hours_breaks_all[1]
                })
            })
        })
        if (year_week === DATE_YEAR_WEEK) {
    // set Person Table row
            renderPersonTableRow(person_id)
    // set roster
            createBlockRosterTablePersonRow(person, ROSTER[day_id], day_id, document.querySelector(`#Tr-${person_id}-day-${day_id}`)).then(() => {
// set roster hours and breaks
                setTimeAmount(person_id)
                loadEditablePersonDayShift(person_id, day_id, true)
                hideLoading(`reloadPersonDayData-${person_id}-${day_id}`)
            })
        } else {
            hideLoading(`reloadPersonDayData-${person_id}-${day_id}`)
        }
    })
}

loadTableWeekPersonData = async (person_id, year_week, table_id) => {
    showLoading(`loadTableWeekPersonData-${person_id}`)
    if (typeof year_week === "undefined") { year_week = DATE_YEAR_WEEK}
    if (typeof table_id === "undefined") { table_id = "Roster-week-table"}

    let person = await getActivePersonById(person_id, year_week)
    let block_table = document.querySelector(`#${table_id}`)
    if (EDITABLE_TABLES.indexOf(table_id) > -1)
        block_table.classList.add("Roster-week-editable")

    for (let day_id=0; day_id<DAYS.length; day_id++) {
        renderRosterTableWeekPersonDay(person, day_id, year_week, document.querySelector(`#${table_id} .Roster-week-td-name-${person_id}-day-${day_id}`), table_id).then(() => {
            if (day_id === DAYS.length-1) hideLoading(`loadTableWeekPersonData-${person_id}`)
        })
    }
}

rosterWeekDisplayOvertime = (el) => {
    if (DISPLAY_OVERTIME) {
        el.classList.remove("active")
        dbSet_settings_overtime_default(false)
        DISPLAY_OVERTIME = false
    } else {
        el.classList.add("active")
        dbSet_settings_overtime_default(true)
        DISPLAY_OVERTIME = true
    }
    loadRosterTableWeek()
}
rosterWeekAddEditableRow = (el) => {
    if (EDITABLE_ROW) {
        el.classList.remove("active")
        EDITABLE_ROW = false
        dbRemove_roster_editable_rows_top(DATE_YEAR_WEEK)
        EDITABLE_ROWS_TOP = []
    } else {
        el.classList.add("active")
        EDITABLE_ROW = true
        EDITABLE_ROWS_TOP = []
        //for (let i=0; i<6; i++) {EDITABLE_ROWS_TOP.push({id: i.toString(), yearweek: DATE_YEAR_WEEK, text: ''})}
    }
    loadRosterTableWeek()
}
rosterWeekAddEditableRowBottom = (el) => {
    if (EDITABLE_ROW_BOTTOM) {
        el.classList.remove("active")
        EDITABLE_ROW_BOTTOM = false
        dbRemove_roster_editable_rows_bottom(DATE_YEAR_WEEK)
        EDITABLE_ROWS_BOTTOM = []
    } else {
        el.classList.add("active")
        EDITABLE_ROW_BOTTOM = true
        EDITABLE_ROWS_BOTTOM = []
        //for (let i=0; i<6; i++) {EDITABLE_ROWS_BOTTOM.push({id: i.toString(), yearweek: DATE_YEAR_WEEK, text: ''})}
    }
    
    loadRosterTableWeek()
}
rosterWeekAddEditableCell = (el) => {
    if (EDITABLE_CELL) {
        el.classList.remove("active")
        EDITABLE_CELL = false
        dbRemove_roster_editable_cells_right(DATE_YEAR_WEEK)
        EDITABLE_CELLS_RIGHT = []
        loadRosterTableWeek()
    } else {
        setEditableCellTrue()
        dbGet_roster_editable_cells_right(DATE_YEAR_WEEK).then(ec => {
            EDITABLE_CELLS_RIGHT = ec
            loadRosterTableWeek()
        })
        /*EDITABLE_CELLS_RIGHT = []
        EDITABLE_CELLS_RIGHT.push({id: "i", yearweek: DATE_YEAR_WEEK, text: ""})
        PERSONS.forEach(person => {
            EDITABLE_CELLS_RIGHT.push({id: person.id, yearweek: DATE_YEAR_WEEK, text: ""})
        })*/
    }
}
setEditableCell = (cell, id, text) => {
    id = id.toString()
    if      (cell === "row_top")    dbSet_roster_editable_rows_top_where_key(DATE_YEAR_WEEK, id, text)
    else if (cell === "row_bottom") dbSet_roster_editable_rows_bottom_where_key(DATE_YEAR_WEEK, id, text)
    else if (cell === "cell_right") dbSet_roster_editable_cells_right_where_key(DATE_YEAR_WEEK, id, text)
}
renderRosterTableWeekPersonDay = async (person, day_id, year_week, block_td, table_id) => {
    if (!block_td) {return}
    block_td.innerHTML = ""
    block_td.classList.remove("v-bottom", "free", "v-bottom")
    let shifts = person.shifts.filter(s => s.day_id === day_id) 
    let person_id = person.id
// set info
    let info_set = false
    setInfo = async (person_id, day_id, block_td) => {
        if (info_set) return
        block_info = document.createElement("div")
        block_info.classList.add("info")
        let info
        if (year_week === DATE_YEAR_WEEK) {
            info = ROSTER_PERSON_INFOS.find(i => i.day_id === day_id && i.id === person_id.toString())?.text
        } else if (year_week === DATE_YEAR_WEEK_TABLE_FROM) {
            info = ROSTER_PERSON_INFOS_TABLE_FROM.find(i => i.day_id === day_id && i.id === person_id.toString())?.text
        } else if (year_week === DATE_YEAR_WEEK_TABLE_TO) {
            info = ROSTER_PERSON_INFOS_TABLE_TO.find(i => i.day_id === day_id && i.id === person_id.toString())?.text
        } else {
            info = await dbGet_roster_info_where_yearweek_and_day_and_person(year_week, day_id, person_id)
        }
        if (typeof info !== "undefined") {
            let block_info_text = document.createElement("div")
            block_info_text.innerHTML = info
            block_info.appendChild(block_info_text)
            block_td.appendChild(block_info) 
        } else {
            block_td.classList.remove("v-bottom")
        }
        info_set = true
    }
// set free
    let ch_free = await checkIfPersonDayIsFree(person_id, day_id, year_week)
    if (ch_free) {
        block_td.classList.add("free")
        let block_free = document.createElement("div")
        block_free.classList.add("free")
        let block_time = document.createElement("p")

        block_time.classList.add("time")
        block_time.innerHTML = ch_free
        block_free.appendChild(block_time)
        block_td.appendChild(block_free)
        if (ch_free === dbGet_settings_label_cut(2)) {
            let block_duration = document.createElement("p")
            block_duration.classList.add("duration")
            //let duration = await getPersonDayHoursAndBreaks(person_id, day_id, year_week)[0]
            block_td.appendChild(block_duration)
        }
        setInfo(person_id, day_id, block_td)
        return
    } else {
        block_td.classList.remove("free")
    }
// set empty shift
    if (shifts.length === 0) {
        block_td.classList.add("no-shift")
        block_td.classList.add("v-bottom")
        setInfo(person_id, day_id, block_td)
        return
    }
// set shift
    let block_td_container = document.createElement("div")
    block_td.appendChild(block_td_container)
    let block_time_container = document.createElement("div")
    if (EDITABLE_TABLES.indexOf(table_id) > -1) {
        getAutoTimeStart(person_id, day_id).then(auto_start => {
            if (shifts.length === 0) block_time_container.setAttribute("onClick", `addNewShiftToRoster(${person_id}, ${day_id}, ${auto_start}, undefined, "${year_week}")`)
        })
    }
    block_time_container.classList.add("time")
    block_td_container.classList.add("container")
    shifts.sort((a, b) => {
        return a.start - b.start;
    });

    let title_shift_text = person.name
    shifts.forEach((shift, shift_id) => {
        let block_time = document.createElement("div")
        let shift_text = getShiftTimeText(shift, day_id, table_id)
        block_time.innerHTML = shift_text
        block_time_container.appendChild(block_time)
    })
    block_td_container.appendChild(block_time_container)
// set hours
    getPersonDayHoursAndBreaks(person_id, day_id, year_week).then(hours_breaks => {
        let duration = hours_breaks[0].toString()
        duration = duration-person.mpa[day_id]
        if (Math.floor(hours_breaks[0]).toString().length === 1) {duration = " "+duration}
        let block_duration = document.createElement("p")
        block_duration.classList.add("duration")
        block_duration.setAttribute("title", "Stunden")
        block_duration.innerHTML = parseFloat(duration) === 0 ? "" : duration
        block_td_container.appendChild(block_duration)
// set break
        let block_break = document.createElement("p")
        let break_day = hours_breaks[1]
        block_break.classList.add("break")
        block_break.setAttribute("title", "Pause")
        if (break_day < 10) {break_day = ` ${break_day} `}
        block_break.innerHTML = parseFloat(duration) === 0 ? "" : break_day
        block_td_container.appendChild(block_break)
    })

// set mpa
    let block_mpa = document.createElement("div")
    let block_mpa_title = document.createElement("div")
    let block_mpa_value = document.createElement("div")
    if (person.mpa[day_id] > 0) {
        if (EDITABLE_TABLES.indexOf(table_id) > -1) {
            block_mpa_value = document.createElement("input")
            block_mpa_value.setAttribute("value", person.mpa[day_id])
            block_mpa_value.setAttribute("title", `${getRenames("m").a} ändern.`)
            block_mpa_value.setAttribute("type", `number`)
            block_mpa_value.setAttribute("step", `0.25`)
            block_mpa_value.setAttribute("onchange", `addMpaToday(${person_id}, ${day_id}, this.value)`)
        } else {
            block_mpa_value.innerHTML = person.mpa[day_id]
        }
        block_mpa_title.innerHTML = getRenames("m").a
    } else {
        if (EDITABLE_TABLES.indexOf(table_id) > -1) {
            block_mpa.setAttribute("onClick", `showLoading("addMpaToday"); window.setTimeout(() => {addMpaToday(${person_id}, ${day_id}, 1); hideLoading("addMpaToday")},50)`)
            block_mpa.setAttribute("title", `${getRenames("m").a} mit 1h für diesen Tag hinzufügen.`)
        }
    }
    block_mpa.classList.add("mpa")
    block_mpa.appendChild(block_mpa_title)
    block_mpa.appendChild(block_mpa_value)
    block_td_container.appendChild(block_mpa)
// set shift label
    let label_sl = person.sl[day_id]
    if (label_sl !== false) {
        block_td_container.style = 'grid-template-areas: "time time time time" "break mpa label duration";'
        let block_sl = document.createElement("div")
        block_sl.classList.add("label")
        block_sl.innerHTML = dbGet_shift_label_cut(label_sl)
        block_td_container.appendChild(block_sl)
    }

    setInfo(person_id, day_id, block_td)
}

createBlockRosterTableWeek = async (year_week, table_id) => {
    if (typeof table_id === "undefined") { 
        table_id = "Roster-week-table"
    }
    if (typeof year_week === "undefined") { 
        year_week = DATE_YEAR_WEEK
    }
    let date = getDateOfISOWeek(year_week)

    let block_table = document.createElement("table")
    block_table.setAttribute("id", table_id)
    if (EDITABLE_TABLES.indexOf(table_id) > -1) block_table.classList.add("Roster-week-editable")

    let block_thead = document.createElement("thead")
    let block_thead_row = document.createElement("tr")
    let block_th = document.createElement("th")
    block_th.classList.add("Roster-week-th-name")
    let block_th_name = document.createElement("p")
    block_th_name.innerHTML = "Name"
    block_th.appendChild(block_th_name)
    block_thead_row.appendChild(block_th)

    let roster = await getActiveRosterData(year_week)
    roster.forEach((roster, day_id) => {
        let day_date = new Date(date)
        day_date.setDate(day_date.getDate() - (day_date.getDay() + 6) % 7 + day_id)

        let block_th = document.createElement("th")
        let block_th_day = document.createElement("p")
        let block_th_day_date = document.createElement("p")

        block_th.classList.add("Roster-week-th")
        block_th.setAttribute("day_id", day_id)
        block_th_day.innerHTML = roster.n
        block_th_day_date.innerHTML = day_date.toLocaleDateString('de', {day: '2-digit', month: '2-digit', year: '2-digit'})
        block_th.appendChild(block_th_day)
        block_th.appendChild(block_th_day_date)
        block_thead_row.appendChild(block_th)
    })
    if (EDITABLE_CELL) {
        let block_th_cell = document.createElement("th")
        block_th_cell.classList.add("td-editable-cell", `Roster-week-td-editable-cell-i`)
        if (!NOWEEKEDIT) block_th_cell.setAttribute("contenteditable", "true")
        block_th_cell.addEventListener('focusout', (e) => setEditableCell("cell_right", "i", e.target.innerHTML))
        if (EDITABLE_ROW) block_th_cell.setAttribute("rowspan", "2")
        block_th_cell.innerHTML = EDITABLE_CELLS_RIGHT.find(c => c.id === "i")?.text || ""
        block_thead_row.appendChild(block_th_cell)
    }

    block_thead.appendChild(block_thead_row)

    let block_tbody = document.createElement("tbody")
    addEmptyRow = () => {
        let block_tbody_row = document.createElement("tr")
        block_tbody_row.classList.add("tr-empty")
        let block_td = document.createElement("td")
        if (EDITABLE_CELL) {
            block_td.setAttribute("colspan", DAYS.length+2)
        } else {
            block_td.setAttribute("colspan", DAYS.length+1)
        }
        block_tbody_row.appendChild(block_td)
        block_tbody.appendChild(block_tbody_row)
    }
    if (table_id === "Roster-week-table" && EDITABLE_ROW) {
        let block_thead_row_1 = document.createElement("tr")
        block_thead_row_1.classList.add("tr-editable")
        for (let i=0; i<6; i++) {
            let block_th_er = document.createElement("th")
            block_th_er.setAttribute("id", `Roster-week-th-editable-${i}`)
            if (!NOWEEKEDIT) block_th_er.setAttribute("contenteditable", `true`)
            block_th_er.addEventListener('focusout', (e) => setEditableCell("row_top", i, e.target.innerHTML))
            block_th_er.innerHTML = EDITABLE_ROWS_TOP.find(c => c.id === i.toString())?.text || ""
            block_thead_row_1.appendChild(block_th_er)
        }
        block_thead.appendChild(block_thead_row_1)
        addEmptyRow()
    }
    block_table.appendChild(block_thead)


    let sort_d = false
    if ((SETTINGS.sort_week.indexOf("d-d") > -1) || (SETTINGS.sort_week.indexOf("d-u") > -1)) {sort_d = true}
    let person_week
    if (year_week === DATE_YEAR_WEEK) {
        person_week = PERSONS_WEEK
    } else if (year_week === DATE_YEAR_WEEK_TABLE_FROM) {
        person_week = PERSONS_TABLE_FROM
    }  else if (year_week === DATE_YEAR_WEEK_TABLE_TO) {
        person_week = PERSONS_TABLE_TO
    } else {
        person_week = await getSortedPersonRosterWeek(1, year_week)
    }
    let sort_d_c = person_week[0].department
    if (typeof sort_d_c === "undefined") return
    for (let pw=0; pw<person_week.length; pw++) {
        let person = person_week[pw]
        if (sort_d && sort_d_c !== person.department) {addEmptyRow(); sort_d_c = person.department}
        let person_id = person.id
        let block_tbody_row = document.createElement("tr")
        let block_td = document.createElement("td")
        let block_td_container = document.createElement("div")
        let block_td_name = document.createElement("p")
        let block_td_overtime = document.createElement("p")
        let block_td_hours = document.createElement("p")
        let block_td_breaks = document.createElement("p")
        block_td.classList.add("Roster-week-td-name")
        block_td.setAttribute("person_id", person_id)
        block_td_name.innerHTML = person.name
        block_td_overtime.classList.add("overtime", "font-number")

        let overtime_t = getOvertimeTitleText(person.overtime, person.name)
        block_td_overtime.setAttribute("title", overtime_t)
        block_td_hours.classList.add("hours", "font-number")
        block_td_hours.setAttribute("title", "Stunden insgesamt")
        block_td_breaks.classList.add("breaks", "font-number")
        block_td_breaks.setAttribute("title", "Pause insgesamt")

        block_td_container.classList.add("td-name")
        block_td_container.appendChild(block_td_name)
        block_td_container.appendChild(block_td_overtime)
        block_td_container.appendChild(block_td_hours)
        block_td_container.appendChild(block_td_breaks)
        block_td.appendChild(block_td_container)
        block_tbody_row.appendChild(block_td)
        block_tbody_row.classList.add("Roster-week-tr-"+person_id)
        block_tbody_row.setAttribute("person_id", person_id)

// set hours and breaks
        let hours_breaks_all = await getPersonHoursAndBreaks(person_id, year_week)
		let breaks_all = hours_breaks_all[1]
		let hours_total = new Promise((res, rej) => {res(getPersonHoursWithFreedays(person_id, year_week))})
		hours_total = await hours_total 
		hours_total = parseFloat(hours_total.toFixed(2))
		block_td_hours.innerHTML = hours_breaks_all[0]+"/"+hours_total
		block_td_breaks.innerHTML = hours_breaks_all[1]

// set overtime
		if (DISPLAY_OVERTIME) {
			block_td_overtime.innerHTML = person.overtime
		}   

        for (let day_id=0; day_id<DAYS.length; day_id++) {
            block_td = document.createElement("td")
            block_td.classList.add("Roster-week-td", `Roster-week-td-name-${person_id}-day-${day_id}`)
            block_td.setAttribute("person_id", person_id)
            block_td.setAttribute("day_id", day_id)
            block_tbody_row.appendChild(block_td)
            renderRosterTableWeekPersonDay(person, day_id, year_week, block_td, table_id)

        }
        if (table_id === "Roster-week-table" && EDITABLE_CELL) {
            let block_td_cell = document.createElement("td")
            block_td_cell.classList.add("td-editable-cell", `Roster-week-td-editable-cell-${person_id}`)
            if (!NOWEEKEDIT) block_td_cell.setAttribute("contenteditable", "true")
            block_td_cell.addEventListener('focusout', (e) => setEditableCell("cell_right", person_id, e.target.innerHTML))
            block_td_cell.innerHTML = EDITABLE_CELLS_RIGHT.find(c => c.id === person_id.toString())?.text || ""
            block_tbody_row.appendChild(block_td_cell)
        }
        block_tbody.appendChild(block_tbody_row)
    }

    if (EDITABLE_ROW_BOTTOM) {
        addEmptyRow()
        let block_tr_editable = document.createElement("tr")
        block_tr_editable.classList.add("tr-editable")
        for (let i=0; i<6; i++) {
            let block_td_ediatable = document.createElement("td")
            block_td_ediatable.setAttribute("id", `Roster-week-td-bottom-editable-${i}`)
            if (!NOWEEKEDIT) block_td_ediatable.setAttribute("contenteditable", `true`)
            block_td_ediatable.addEventListener('focusout', (e) => setEditableCell("row_bottom", i, e.target.innerHTML))
            block_td_ediatable.innerHTML = EDITABLE_ROWS_BOTTOM.find(c => c.id === i.toString())?.text || ""
            block_tr_editable.appendChild(block_td_ediatable)
        }
        block_tbody.appendChild(block_tr_editable)
    }
    block_table.appendChild(block_tbody)

    return block_table
}

loadRosterTableWeekData = async (year_week, table_id) => {
    if (PERSONS.length === 0) return
    showLoading("loadRosterTableWeekData")
    if (typeof year_week === "undefined") { year_week = DATE_YEAR_WEEK }
    if (typeof table_id === "undefined") {
        table_id = "Roster-week-table"
    }
    let person_all
    if (year_week === DATE_YEAR_WEEK) {
        person_all = PERSONS
    } else if (year_week === DATE_YEAR_WEEK_TABLE_TO) {
        person_all = PERSONS_TABLE_TO
    }  else if (year_week === DATE_YEAR_WEEK_TABLE_FROM) {
        person_all = PERSONS_TABLE_FROM
    } else {
        person_all = dbGet_persons_active_where_yearweek(year_week)
    }
    for (let i=0; i<person_all.length; i++) {
        let person_id = person_all[i].id
        loadTableWeekPersonData(person_id, year_week, table_id).then(() => {
            if (i === person_all.length-1) {
                hideLoading("loadRosterTableWeekData")
            }
        })
    }
    DAYS.forEach((day, day_id) => {
        let day_date = new Date(getIsoStringFromDay(day_id, year_week))
        document.querySelectorAll(`#${table_id} .Roster-week-th`)[day_id].querySelectorAll("p")[1].innerHTML = day_date.toLocaleDateString('de', {day: '2-digit', month: '2-digit', year: '2-digit'})
    })
}

loadRosterTableWeek = () => {
    if (PERSONS.length === 0) return
    let block_roster_container = document.getElementById("Roster-week-container")
    let block_header = document.createElement("h2")
    let block_print = document.createElement("button")
    block_roster_container.innerHTML = ""

    block_header.innerHTML = "Woche "+DATE_WEEK
    block_print.innerHTML = "Drucken"
    block_print.classList.add("button-print")
    block_print.setAttribute("onClick", `printTable("Roster-week-container", ${DATE_YEAR_WEEK})`)
    block_roster_container.appendChild(block_header)
    block_roster_container.appendChild(block_print)


    createB = async () => {
        let create_block = new Promise((res, rej) => {res(createBlockRosterTableWeek())})
        create_block = await create_block 
        block_roster_container.appendChild(create_block )
    } 
    createB().then(() => {
        let block_week_info = document.createElement("div")
        block_week_info.setAttribute("id", "Roster-week-info")
        block_week_info.innerHTML = ""
        let info = ROSTER_PERSON_INFOS.find(i => i.day_id === 999 && i.id === "all")?.text
        if (typeof info !== "undefined") { 
            let block_week_info_text = document.createElement("div")
            block_week_info_text.innerHTML = info
            block_week_info.appendChild(block_week_info_text)
            document.querySelector("#Roster-week-week-info").classList.add("active")
            document.getElementById("Set-week-info-container").querySelector("textarea").value = info
            document.getElementById("Set-week-info-container").classList.remove("d-none")
        }
        block_roster_container.appendChild(block_week_info)
    })
}


eventListenerEditRosterDay = (event, day_id) => {
    let block_container = document.getElementById(`Roster-${day_id}`)
    if (!block_container.contains(event.target)) {
        let button = block_container.querySelector(".button-edit-table")
        button.classList.remove("d-none")
        window.onmousemove = null
    }

}


createDaysOption = (day_id) => {
    let blocks = []
    DAYS.forEach((day, d_id) => {
        if (day_id === d_id) return
        let block = document.createElement("option")
        block.value = d_id
        block.innerHTML = day
        blocks.push(block)
    })
    return blocks
}

copyRosterDay = async (day_id, from_id) => {
    showLoading("copyRosterDay")
    setS = async (shifts, did, yw) => {
        let noreload = shifts.length === 1 ? false : true
        if (yw !== DATE_YEAR_WEEK) {noreload = true}
        if (shifts.length === 0) return
        return addNewShiftToRoster(shifts[0].person_id, did, shifts[0].start, shifts[0].end, yw, noreload).then(() => {
            if (shifts.length > 1) { return addShift(yw, did, shifts[1].person_id, shifts[1].start, shifts[1].end) }
            return
        })
    }
	asyncMain = () => {
		let new_roster_data 
		let year_week
		let year_week_to = DATE_YEAR_WEEK
		if (day_id === "all") {
			year_week = DATE_YEAR_WEEK
			from_id = parseFloat(from_id)
            setAs = async() => { 
                for (let p = 0; p<PERSONS.length; p++) {
                    let person = PERSONS[p]
                    let person_id = person.id
                    let shifts = person.shifts.filter(s => s.day_id === from_id)
                    for (let d_id=0; d_id<DAYS.length;d_id++) {
                        if (d_id === from_id) { 
                            if (p === PERSONS.length-1) { hideLoading("copyRosterDay")}
                            continue 
                        }
                        checkIfPersonDayIsFree(person_id, d_id).then(cf => {
                            if (cf === false) {
                                setS(shifts, d_id, year_week_to).then(() => {
                                    if (p === PERSONS.length-1) { hideLoading("copyRosterDay")}
                                })
                            } else if (p === PERSONS.length-1) { hideLoading("copyRosterDay")}
                        })
                    }
                }
            }
            setAs()
        } else {
			if (from_id === "week_before") {
				year_week = DATE_YEAR_WEEK_BEFORE
				from_id = day_id
                day_id = parseFloat(day_id)
			} else if (from_id === "week_before_week") {
				year_week = DATE_YEAR_WEEK_BEFORE
				from_id = day_id
                day_id = parseFloat(day_id)
			} else if (day_id === "next"){
				year_week = DATE_YEAR_WEEK
                year_week_to = DATE_YEAR_WEEK_AFTER
                from_id = parseFloat(from_id)
                day_id = from_id 
            } else {
				year_week = DATE_YEAR_WEEK
				from_id = parseFloat(from_id)
                day_id = parseFloat(day_id)
			}


            loopP = (persons_yw) => {
                for (let p=0; p<persons_yw.length; p++) {
                    let shifts = persons_yw[p].shifts.filter(s => s.day_id === from_id)
                    checkIfPersonDayIsFree(persons_yw[p].id, day_id, year_week_to).then(cf => {
                        if (cf === false) {
                            setS(shifts, day_id, year_week_to).then(() => {
                                if (p === persons_yw.length-1) { hideLoading("copyRosterDay")}
                            })
                        } else if (p === persons_yw.length-1) { hideLoading("copyRosterDay")}
                    })
                }
            }
            if (ROSTERS_YEARWEEK.indexOf(year_week_to) === -1) {
                createDataA(year_week_to).then(() => {
                    loopP(PERSONS)
                })
            } else if (DATE_YEAR_WEEK === year_week) {
                loopP(PERSONS)
            } else {
                dbGet_persons_active_where_yearweek(year_week).then(persons_yw => loopP(persons_yw))
            }
		}
	}
    asyncMain()
}

removeDayShifts = (day_id) => {
    showLoading("removeDayShifts")
    checkElementInView("main")
    dbRemove_shift_where_year_week_and_day(DATE_YEAR_WEEK, day_id).then(() => {
        setPerson().then(() => {
            getActiveRoster().then(() => {
                if (INVIEW.length > 0) document.querySelector(INVIEW)?.scrollIntoView()
                hideLoading("removeDayShifts")
            })
        })
    })
}

requestRemoveDayShifts = (day_id) => {
    document.body.classList.add("noscroll")
    document.getElementById("Popup").classList.add("active", "request-remove")
    let block = document.getElementById("Popup-container")
    block.style.width = "300px"

    let block_text = document.createElement("p")
    block_text.innerHTML = "Sicher dass du alle Schichten für <b>"+DAYS[day_id]+" löschen</b> möchtest?"
    block.appendChild(block_text)

    let block_button_cancel = document.createElement("button")
    block_button_cancel.setAttribute("onClick", "closePopup()")
    block_button_cancel.innerHTML = "abbrechen"
    block.appendChild(block_button_cancel)

    let block_button_remove = document.createElement("button")
    block_button_remove.setAttribute("onClick", "removeDayShifts("+day_id+");closePopup()")
    block_button_remove.innerHTML = "löschen"
    block.appendChild(block_button_remove)
    
    document.getElementById("Popup").onclick = (e) => eventListenerClosePopup(e)
}

scrollTimeIntoView = (day_id, time) => {
    document.getElementById("Table-times-thead-tr-"+day_id).querySelector(`[time="${time}"]`).scrollIntoView({behavior: "smooth", inline: "center", block: "nearest"})
}

addRosterPersonDayInfo = (person_id, day_id, text) => {
    while (text.slice(-1) === "\n") {text = text.substring(0, text.length-1)}

	let info = ROSTER_PERSON_INFOS.find(i => i.day_id === day_id && i.id === person_id.toString())
	if (typeof info !== "undefined") {info.text = text}
	else ROSTER_PERSON_INFOS.push({id: person_id.toString(), yearweek: DATE_YEAR_WEEK, day_id: day_id, text: text})
    dbSet_roster_info_where_yearweek_and_day_and_person(DATE_YEAR_WEEK, day_id, person_id, text)
}

removeRosterPersonDayInfo = (person_id, day_id) => {
    ROSTER_PERSON_INFOS = ROSTER_PERSON_INFOS.filter(i => `${i.id}-${i.day_id}` !== `${person_id}-${day_id}`)
    dbRemove_roster_info_where_yearweek_and_day_and_person(DATE_YEAR_WEEK, day_id, person_id)
}

closePersonDayInfoInput = (person_id, day_id, block) => {
    let block_text = block.querySelector("textarea")
    let text = block_text.value
    if (text.length === 0) {
        removeRosterPersonDayInfo(person_id, day_id)
        block.querySelector("button").classList.remove("set")
    } else if (text.length > 0) {
        block.querySelector("button").classList.add("set")
        addRosterPersonDayInfo(person_id, day_id, text)
    }

    block.removeChild(block_text)
    document.getElementById(`Td-name-${person_id}-day-${day_id}`).classList.remove("active")
    document.body.onclick = null
    reloadPersonDayData(person_id, day_id)
}

eventListenerClosePersonDayInfoInput = (evt, person_id, day_id, block) => {
	let targetEl = evt.target
	let clickEl = block
	do {
		if (targetEl == clickEl) return
		targetEl = targetEl.parentNode;
	} while (targetEl);
	closePersonDayInfoInput(person_id, day_id, block)
}

openPersonDayInfoInput = (person_id, day_id, el) => {
    let block = el.parentNode
    let block_input = document.createElement("textarea")
    document.getElementById(`Td-name-${person_id}-day-${day_id}`).classList.add("active")
    let info = ROSTER_PERSON_INFOS.find(i => i.day_id === day_id && i.id === person_id.toString())?.text
    if (typeof info !== "undefined") block_input.value = info
        block.appendChild(block_input)
    window.setTimeout(() => {
        document.body.onclick = (e) => eventListenerClosePersonDayInfoInput(e, person_id, day_id, block)
    }, 100)
}

createBlockRosterTablePersonRow = async (person, roster, day_id, block_tbody_row) => {
    document.querySelector(`#Roster-${day_id}`).classList.remove("editable")
    let year_week = DATE_YEAR_WEEK
    let person_id = person.id
    let shifts_all = person.shifts
    let shifts = shifts_all.filter(s => s.day_id === day_id)

    block_tbody_row.innerHTML = ""

    // create table name
    let block_name_td = document.createElement("td")
    let block_td_name_hours_container = document.createElement("div")
    block_td_name_hours_container.classList.add("td-name-hours-container")
    let block_name_td_container = document.createElement("div")
    let d_color = DEPARTMENT_COLORS[DEPARTMENTS.map(d => d.id).indexOf(person.department)]
    block_name_td.style.background = `linear-gradient(to right, black 1px, ${d_color} 1px, ${d_color} calc(100% - 1px), black 1px)`
    block_name_td_container.classList.add("td-name-container")
    block_name_td.setAttribute("id", "Td-name-"+person_id+"-day-"+day_id)
    block_name_td.classList.add(`name-${person_id}`, "td-name", "td-name-hours", "td-sticky")

    let block_name_hours = document.createElement("div")
    block_name_hours.classList.add("hours-amount")

    let block_name_td_container_hours = document.createElement("div")
    block_name_td_container_hours.classList.add("hours-container")
    let block_name_td_name = document.createElement("div")
    block_name_td_name.classList.add("name")
    block_name_td_name.innerHTML = person.name
    block_name_td_container.appendChild(block_name_td_name)

    let block_name_td_overtime = document.createElement("div")
    block_name_td_overtime.classList.add("td-overtime")
    let overtime = person.overtime
    let overtime_t = getOvertimeTitleText(overtime, person.name)
    if (overtime === 0) {block_name_td_overtime.classList.add("green"); block_name_td_overtime.classList.remove("orange", "red")}
    else if (overtime > 0) {overtime = `+${overtime}`; block_name_td_overtime.classList.add("red"); block_name_td_overtime.classList.remove("green", "orange")}
    else if (overtime < 0) {block_name_td_overtime.classList.add("orange"); block_name_td_overtime.classList.remove("green", "red")}
    block_name_td_overtime.innerHTML = overtime
    block_name_td_overtime.setAttribute("title", overtime_t)
    block_name_td_container.appendChild(block_name_td_overtime)


    let hours_breaks = await getPersonDayHoursAndBreaks(person_id, day_id, year_week)
    let hours = hours_breaks[0]
    let breaks = hours_breaks[1]
    let hours_total = await getPersonHoursWithFreedays(person_id)
    hours_total = parseFloat(hours_total.toFixed(2))
    let hours_breaks_all = await getPersonHoursAndBreaks(person_id, year_week)
    let hours_a = hours_breaks_all[0]
	let breaks_a = hours_breaks_all[1]
    let block_name_hours_vs_given = document.createElement("div")
    block_name_hours_vs_given.setAttribute("title", "Stunden vergeben/Stunden max")
    block_name_hours_vs_given.classList.add("hours-given")
    block_name_hours_vs_given.innerHTML = hours_a+"/"+hours_total
    block_name_hours.appendChild(block_name_hours_vs_given)


    block_name_td_container_hours.appendChild(block_name_hours)

    block_name_td_container.appendChild(block_name_td_container_hours)
    block_td_name_hours_container.appendChild(block_name_td_container)
    block_name_td.appendChild(block_td_name_hours_container)


    // create table name hours

    // top
    let block_td_top_bottom = document.createElement("div")
    block_td_top_bottom.classList.add("td-top-bottom")
    let block_td_top = document.createElement("div")
    block_td_top.classList.add("td-top")

    let block_name_mpa = document.createElement("div")
    block_name_mpa.setAttribute("title", getRenames("m").a)
    block_name_mpa.classList.add("hours-mpa")
    let mpa = person.mpa[day_id]
    let check_if_free = await checkIfPersonDayIsFree(person_id, day_id)
    if (check_if_free) {mpa = 0}
    let check_betterment = check_if_free === getRenames("b").a ? true : false

    let block_name_mpa_value = document.createElement("div")
    block_name_mpa_value.innerHTML = mpa === 0 ? "" : person.mpa[day_id]
    block_name_mpa.style.zIndex = mpa === 0 ? "-1" : "0"
    block_name_mpa.appendChild(block_name_mpa_value)
    block_td_top.appendChild(block_name_mpa)

    let block_name_td_info = document.createElement("div")
    block_name_td_info.classList.add("td-info")
    let block_name_td_info_button = document.createElement("button")

    let info = ROSTER_PERSON_INFOS.find(i => i.day_id === day_id && i.id === person_id.toString())?.text

    info && info.length > 0 && block_name_td_info_button.classList.add("set")
    block_name_td_info_button.classList.add("button-info")
    block_name_td_info_button.setAttribute("onClick", `openPersonDayInfoInput(${person_id}, ${day_id}, this)`)
    block_name_td_info_button.setAttribute("title", "Informationen zu der Schicht hinzufügen")
    block_name_td_info_button.innerHTML = "i"
    block_name_td_info.appendChild(block_name_td_info_button)
    block_td_top.appendChild(block_name_td_info)

    let block_name_td_menu = document.createElement("div")
    let block_name_td_button = document.createElement("button")
    block_name_td_button.classList.add("button-menu", "hamburger")
    block_name_td_button.innerHTML = "<span></span><span></span><span></span>"
    block_name_td_button.setAttribute("onClick", "openRosterPersonMenu("+person_id+", "+day_id+")")
    block_name_td_menu.classList.add("menu", "menu-"+person_id+"-day-"+day_id)
    block_td_top.appendChild(block_name_td_button)
    if (check_if_free) {
        block_name_td_button.setAttribute("disabled", "true")
    }
    block_td_top.appendChild(block_name_td_menu)

    block_td_top_bottom.appendChild(block_td_top)

    // bottom
    let block_td_bottom = document.createElement("div")
    block_td_bottom.classList.add("td-bottom")

    let block_name_break = document.createElement("div")
    block_name_break.setAttribute("title", "Pause heute")
    block_name_break.classList.add("break-today")
    block_name_break.innerHTML = breaks
    block_td_bottom.appendChild(block_name_break)


    let block_name_hours_today = document.createElement("div")
    block_name_hours_today.setAttribute("title", "Schichtstunden heute ohne MPA")
    block_name_hours_today.classList.add("hours-today")
    block_name_hours_today.innerHTML = check_betterment ? hours : hours-person.mpa[day_id]
    block_td_bottom.appendChild(block_name_hours_today)

    block_td_top_bottom .appendChild(block_td_bottom)
    block_td_name_hours_container.appendChild(block_td_top_bottom)

    // create table time
    block_tbody_row.setAttribute("onmouseover", `this.classList.add("hover");`)
    block_tbody_row.setAttribute("onmouseout", `this.classList.remove("hover");`)

    block_tbody_row.setAttribute("id", `Tr-${person_id}-day-${day_id}`)
    block_tbody_row.classList.add("tr-name-"+person_id)

    block_tbody_row.appendChild(block_name_td)

    // create table td

    callback_r = (name) => {
        let block_td_after = document.createElement("td")
        block_td_after.classList.add("td-name-after")
        block_td_after.innerHTML = name
        return block_td_after
    }

    let label = person.sl[day_id]
    roster.d.forEach((data, time_id) => {
        let time = data.t
        let block_times_td = document.createElement("td")
        let block_times_container = document.createElement("div")
        block_times_container.classList.add("container")
        let block_times_content
        block_times_td.setAttribute("person", person.name)
        block_times_td.setAttribute("time", time)
        block_times_td.classList.add("td-day-time")
        if (check_if_free === false) {
            block_times_content = document.createElement("div")
            block_times_content.classList.add("shiftmodify-buttons-container")
            let block_times_button_shift = document.createElement("button")
            block_times_button_shift.classList.add("roster-add-shift")
            if (label !== false) {
                block_times_button_shift.innerHTML = dbGet_shift_label_cut(label)
            } else {
                block_times_button_shift.innerHTML = ""
            }
            block_times_button_shift.setAttribute("title", `${person.name}\nNeue Schicht\n${timeToHHMM(time)}`)
            block_times_button_shift.setAttribute("onClick", "addNewShiftToRoster("+person_id+", "+day_id+", "+time+")")

            block_times_content.appendChild(block_times_button_shift)
            block_times_container.appendChild(block_times_content)
        } else {
            block_times_td.classList.add("free")
            block_times_content = document.createElement("p")
            block_times_content.innerHTML = check_if_free
            block_times_container.innerHTML = "" 
            block_times_container.appendChild(block_times_content)
        }
        block_times_td.appendChild(block_times_container)
        block_tbody_row.appendChild(block_times_td)
        if (time_id === roster.d.length-1) block_tbody_row.appendChild(callback_r(person.name))
        if (shifts.find(s => s.start <= time && s.end >time)) {
            block_times_td.classList.add("assigned")
        }
    })
}

renderRosterSort = () => {
    let block_roster_container = document.getElementById("Roster-days-container")
    let block = document.createElement("div")
    let block_remove = document.querySelector("#Sort-roster-by")
    block.setAttribute("id", "Sort-roster-by")
    block.classList.add("settings-box")
    let block_box = document.createElement("div")
    block_box.classList.add("sort-box")
    let block_name = document.createElement("button")
    block_name.setAttribute("id", "Sort-roster-by-name")
    block_name.classList.add("sort-roster-by", "sort")
    block_name.setAttribute("name", "n")
    block_name.setAttribute("onClick", "sortRosterBy('days', this)")
    block_name.innerHTML = "Name"
    let block_dep = document.createElement("button")
    block_dep.setAttribute("id", "Sort-roster-by-department")
    block_dep.classList.add("sort-roster-by", "sort")
    block_dep.setAttribute("name", "d")
    block_dep.setAttribute("onClick", "sortRosterBy('days', this)")
    block_dep.innerHTML = "Bereich"
    let block_hou = document.createElement("button")
    block_hou.setAttribute("id", "Sort-roster-by-hours")
    block_hou.classList.add("sort-roster-by", "sort")
    block_hou.setAttribute("name", "h")
    block_hou.setAttribute("onClick", "sortRosterBy('days', this)")
    block_hou.innerHTML = "Stunden"
    if (block_remove) block_roster_container.removeChild(block_remove)
    block_box.appendChild(block_name)
    block_box.appendChild(block_dep)
    block_box.appendChild(block_hou)
    block.appendChild(block_box)
    block_roster_container.appendChild(block)
    setRosterSortButtons(0)
}
loadRosterTable = async () => {
    let block_roster_container = document.getElementById("Roster-days-container")
    block_roster_container.innerHTML = ""
    renderRosterSort()
    let day = structuredClone(DATE)
    day.setDate(day.getDate() - 1)
    for (let day_id=0; day_id<ROSTER.length; day_id++) {
        let roster = ROSTER[day_id]
        day.setDate(day.getDate() + 1)
        let block_container_roster = document.createElement("div")
        let block_container_roster_head = document.createElement("div")
        block_container_roster_head.classList.add("Roster-container-head")

        let block_header_day = document.createElement("h2")
        block_header_day.setAttribute("id", `Roster-${day_id}-title`)

        block_container_roster.setAttribute("id","Roster-"+day_id)
        block_container_roster.classList.add("Roster-container")
        block_container_roster.appendChild(block_header_day)
        addDragToScroll(block_container_roster)


        let block_container_copy_from_to = document.createElement("div")
        block_container_copy_from_to.classList.add("container-copy-from-to")

        let block_button_copy_from = document.createElement("select")
        block_button_copy_from.classList.add("select-copy-from", "select-copy")
        block_button_copy_from.setAttribute("title", "Den gesamten Plan von einem Wochentag, oder selben Tag der Vorwoche kopieren.\nDies löscht alle vorhandenen Schichten des Tages")
        block_button_copy_from.setAttribute("onchange", "copyRosterDay("+day_id+", this.value)")
        block_button_copy_from.innerHTML = '<option disabled="disabled" selected="selected">Übertragen von</option>'

        let block_option_wb = document.createElement("option")
        block_option_wb.value = "week_before"
        block_option_wb.innerHTML = "Letzte Woche"
        block_button_copy_from.appendChild(block_option_wb)
        let block_option_wbw = document.createElement("option")
        block_option_wbw.value = "week_before_week"
        block_option_wbw.innerHTML = "Vorletze Woche"
        block_button_copy_from.appendChild(block_option_wbw)
        createDaysOption(day_id).forEach(block => block_button_copy_from.appendChild(block))
        block_container_copy_from_to.appendChild(block_button_copy_from)

        let block_button_copy_to = document.createElement("select")
        block_button_copy_to.classList.add("select-copy-to", "select-copy")
        block_button_copy_to.setAttribute("title", "Den gesamten Plan dieses Tages, auf einen Anderen Tag übertragen.\nDies löscht alle vorhandenen Schichten des zieltages.")
        block_button_copy_to.setAttribute("onchange", "copyRosterDay(this.value, "+day_id+")")
        block_button_copy_to.innerHTML = '<option disabled="disabled" selected="selected">Übertragen nach</option><option value="next">Nächste Woche</option>'
        createDaysOption(day_id).forEach(block => block_button_copy_to.appendChild(block))
        let block_copy_to_all = document.createElement("option")
        block_copy_to_all.setAttribute("value", "all")
        block_copy_to_all.innerHTML = "Alle"
        block_button_copy_to.appendChild(block_copy_to_all)
        block_container_copy_from_to.appendChild(block_button_copy_to)

        block_container_roster_head.appendChild(block_container_copy_from_to)

        let block_table_container = document.createElement("div")
        block_table_container.setAttribute("id", "Table-"+day_id+"-container")
        block_table_container.classList.add("tables-container")

        let block_button_edit_table = document.createElement("button")
        block_button_edit_table.setAttribute("onClick", `loadEditableDataIntoRosterTable(this, ${day_id})`)
        block_button_edit_table.classList.add("button-edit-table")
        let block_button_edit_table_text = document.createElement("div")
        block_button_edit_table_text.innerHTML = "Bearbeiten"
        block_button_edit_table.appendChild(block_button_edit_table_text)
        block_table_container.appendChild(block_button_edit_table)

        let block_table = document.createElement("table")
        block_table.classList.add("table-times")
        let block_thead = document.createElement("thead")
        let block_tbody = document.createElement("tbody")
        let block_thead_row = document.createElement("tr")
        block_thead_row.setAttribute("id", "Table-times-thead-tr-"+day_id)

        let block_time_scroll = document.createElement("div")
        block_time_scroll.classList.add("time-scroll-container")
        block_container_roster_head.appendChild(block_time_scroll)

        block_container_roster.appendChild(block_container_roster_head)
        let block_button_remove = document.createElement("button")
        block_button_remove.innerHTML = "löschen"
        block_button_remove.classList.add("button-remove-day-shifts", "button-remove")
        block_button_remove.setAttribute("onClick", "requestRemoveDayShifts("+day_id+")")
        block_container_roster_head.appendChild(block_button_remove)
        let block_th_name = document.createElement("th") 
        let block_th_name_day = document.createElement("div") 
        block_th_name_day.innerHTML = DAYS[day_id]
        block_th_name.appendChild(block_th_name_day)
        block_thead_row.appendChild(block_th_name)


        block_thead.appendChild(block_thead_row)
        block_table.appendChild(block_thead)
        block_table.appendChild(block_tbody)
        block_table_container.appendChild(block_table)
        block_container_roster.appendChild(block_table_container)
        block_roster_container.appendChild(block_container_roster)
        for (let time_id=0; time_id<roster.d.length; time_id++) {
            let time = roster.d[time_id]
            if (checkNaturalNumber(time.t)) {
                let button = document.createElement("button")
                button.setAttribute("onClick", `scrollTimeIntoView(${day_id}, ${time.t})`)
                button.setAttribute("title", `Zur Uhrzeit ${time.t}:00 scrollen`)
                button.innerHTML = time.t
                block_time_scroll.appendChild(button)
            }

            let block_th = document.createElement("th") 
            let block_th_time = document.createElement("p")  
            let block_th_amount = document.createElement("div")  
            let block_th_amount_container = document.createElement("div")  
            let block_th_amount_max = document.createElement("p")  
            let block_th_amount_is = document.createElement("p")  
            let block_th_amount_buttons = document.createElement("div")  
            let block_th_amount_button_up = document.createElement("button")  
            let block_th_amount_button_down = document.createElement("button")  
            block_th.setAttribute("time", time.t)
            block_th.classList.add("th-day-time", "font-number")
            block_th.setAttribute("style", "background-color: "+colors_red_orange_green[0]+";")
            block_th_time.classList.add("time", "font-number")
            let time_text = timeToHHMM(time.t)
            if (time_text.length === 4) time_text = ` ${time_text}`
            block_th_time.innerHTML = time_text
            block_th_amount_buttons.classList.add("th-amount-buttons")
            block_th_amount_button_up.classList.add("chevron-up")
            block_th_amount_button_up.setAttribute("onClick", "changeTimeAmount(this, "+day_id+", 'up', "+time_id+")")
            block_th_amount_button_down.classList.add("chevron-down")
            block_th_amount_button_down.setAttribute("onClick", "changeTimeAmount(this, "+day_id+", 'down', "+time_id+")")

            block_th_amount_buttons.appendChild(block_th_amount_button_up)
            block_th_amount_buttons.appendChild(block_th_amount_button_down)

            block_th_amount_container.setAttribute("onClick", "this.classList.toggle('active')")
            block_th_amount_container.setAttribute("title", "vergebene Schichten/Gewünschte Anzahl\nUm mehrere zu erhöhen auf mehrere dieser Zahlen drücken.\nUm alle Markierungen zu entfernen eine unmarkierte Zahl verändern.")
            block_th_amount_container.classList.add("th-amount-container")
            block_th_amount_max.innerHTML = time.a
            block_th_amount_max.classList.add("time-amount-max")
            block_th_amount_is.innerHTML = "0/"
            block_th_amount_is.classList.add("time-amount-is")

            block_th_amount_container.appendChild(block_th_amount_is)
            block_th_amount_container.appendChild(block_th_amount_max)
            block_th_amount.appendChild(block_th_amount_container)
            block_th_amount.appendChild(block_th_amount_buttons)
            block_th_amount.classList.add("time-amount")

            block_th.appendChild(block_th_amount)
            block_th.appendChild(block_th_time)

            block_thead_row.appendChild(block_th)

            let amount = await getPersonAmountForDayTime(day_id, time.t).length
            let color_id = Math.round((amount*colors_red_orange_green.length)/time.a)
            let color = colors_red_orange_green[color_id]
            if (color_id >= colors_red_orange_green.length) color = colors_red_orange_green[colors_red_orange_green.length-1]
            if (amount > time.a) color = "#458caf"
            else if (amount === time.a) color = "#61a64d"


            block_th.setAttribute("style", `background-color: ${color}; border-color: ${color}87;`)
            block_th_amount_is.innerHTML = amount+"/"
        }
        let time_last = timeToHHMM(ROSTER[day_id].d[ROSTER[day_id].d.length-1].t+TIME_STEP)
        let block_th = document.createElement("th") 
        block_th.setAttribute("colspan", "2")
        let block_th_p = document.createElement("p") 

        block_th_p.innerHTML = time_last
        block_th_p.classList.add("time")
        block_th.classList.add("th-day-time")
        block_th.appendChild(block_th_p)
        block_thead_row.appendChild(block_th)

        let log_date = performance.now()
        for (let pr=0; pr<PERSONS_ROSTER.length;pr++) {
            log_date = performance.now()
            let person = PERSONS_ROSTER[pr]
            let block_tbody_row = document.createElement("tr")
            block_tbody.appendChild(block_tbody_row)
            createBlockRosterTablePersonRow(person, roster, day_id, block_tbody_row)
        }

        let day_date = structuredClone(DATE)
        day_date.setDate(day_date.getDate() - (day_date.getDay() + 6) % 7 + day_id)
        block_header_day.innerHTML = roster.n+" "+day_date.toLocaleDateString('de', {day: '2-digit', month: '2-digit', year: '2-digit'})
        addDragToScroll(block_container_roster, day_id)
    }
}

toggleTableWeekEditeable = (table_name) => {
    showLoading("toggleTableWeekEditeable")
    let year_week = DATE_YEAR_WEEK
    let table_id = EDITABLE_TABLES.indexOf(table_name)
    if (table_id > -1) { EDITABLE_TABLES.splice(table_id, 1) } 
    else { 
        EDITABLE_TABLES.push(table_name)
    }
    if (document.querySelector("#Popup .item-table-to .select-roster")) {
        year_week = document.querySelector("#Popup .item-table-to .select-roster").value
    }
    loadRosterTableWeekData(year_week, table_name).then(() => {
        hideLoading("toggleTableWeekEditeable")
    })
}