208 lines
9.7 KiB
Python
208 lines
9.7 KiB
Python
|
|
with open('app/modules/sag/templates/detail.html', 'r', encoding='utf-8') as f:
|
||
|
|
text = f.read()
|
||
|
|
|
||
|
|
html_start = text.find('<form id="timeManualFormV1"')
|
||
|
|
html_end = text.find('</form>', html_start) + 7
|
||
|
|
|
||
|
|
new_html = """<form id="timeManualFormV1" class="row g-2 align-items-end" onsubmit="createManualTimeV1(event); return false;">
|
||
|
|
<div class="col-xl-2 col-md-3 col-12">
|
||
|
|
<label class="form-label small mb-1">Medarbejder</label>
|
||
|
|
<select class="form-select form-select-sm" id="timeV1EmployeeId">
|
||
|
|
<option value="">Mig (nuværende bruger)</option>
|
||
|
|
{% for user in assignment_users %}
|
||
|
|
<option value="{{ user.user_id }}">{{ user.display_name }}</option>
|
||
|
|
{% endfor %}
|
||
|
|
</select>
|
||
|
|
</div>
|
||
|
|
<div class="col-xl-2 col-md-3 col-6">
|
||
|
|
<label class="form-label small mb-1">Dato</label>
|
||
|
|
<input type="date" class="form-control form-control-sm" id="timeV1Date">
|
||
|
|
</div>
|
||
|
|
<div class="col-xl-1 col-md-2 col-3">
|
||
|
|
<label class="form-label small mb-1">Start</label>
|
||
|
|
<input type="time" class="form-control form-control-sm" id="timeV1Start">
|
||
|
|
</div>
|
||
|
|
<div class="col-xl-1 col-md-2 col-3">
|
||
|
|
<label class="form-label small mb-1">Slut</label>
|
||
|
|
<input type="time" class="form-control form-control-sm" id="timeV1End">
|
||
|
|
</div>
|
||
|
|
<div class="col-xl-1 col-md-2 col-6">
|
||
|
|
<label class="form-label small mb-1">Minutt.</label>
|
||
|
|
<input type="number" min="1" class="form-control form-control-sm" id="timeV1Minutes" placeholder="45" required>
|
||
|
|
</div>
|
||
|
|
<div class="col-xl-2 col-md-6 col-6">
|
||
|
|
<label class="form-label small mb-1">Beskrivelse</label>
|
||
|
|
<input type="text" class="form-control form-control-sm" id="timeV1Description" placeholder="Hvad er udført?">
|
||
|
|
</div>
|
||
|
|
<div class="col-xl-2 col-md-4 col-12 d-flex gap-1">
|
||
|
|
<div class="w-50">
|
||
|
|
<label class="form-label small mb-1">Type</label>
|
||
|
|
<select class="form-select form-select-sm px-1" id="timeV1Type">
|
||
|
|
<option value="ukendt">Ukendt</option>
|
||
|
|
<option value="manuel" selected>Manuel</option>
|
||
|
|
<option value="opkald">Opkald</option>
|
||
|
|
<option value="mail">Mail</option>
|
||
|
|
<option value="indedesk">IndeDesk</option>
|
||
|
|
</select>
|
||
|
|
</div>
|
||
|
|
<div class="w-50">
|
||
|
|
<label class="form-label small mb-1">Status</label>
|
||
|
|
<select class="form-select form-select-sm px-1" id="timeV1Status">
|
||
|
|
<option value="kladde">Kladde</option>
|
||
|
|
<option value="afventer" selected>Afventer</option>
|
||
|
|
<option value="godkendt">Godkendt</option>
|
||
|
|
</select>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<div class="col-xl-1 col-md-2 col-12 d-grid">
|
||
|
|
<button class="btn btn-sm btn-primary" type="submit" title="Tilføj registrering"><i class="bi bi-plus-lg fs-6"></i></button>
|
||
|
|
</div>
|
||
|
|
</form>"""
|
||
|
|
|
||
|
|
if html_start != -1 and html_end != -1:
|
||
|
|
text = text[:html_start] + new_html + text[html_end:]
|
||
|
|
print("HTML updated.")
|
||
|
|
|
||
|
|
js_start = text.find('async function createManualTimeV1(event) {')
|
||
|
|
js_end = text.find(' document.addEventListener(\'DOMContentLoaded\'', js_start)
|
||
|
|
|
||
|
|
# Notice here the JS checks for start_tid / slut_tid to populate them.
|
||
|
|
new_js = """function bindTimeV1Calculations() {
|
||
|
|
const startIn = document.getElementById('timeV1Start');
|
||
|
|
const endIn = document.getElementById('timeV1End');
|
||
|
|
const minIn = document.getElementById('timeV1Minutes');
|
||
|
|
|
||
|
|
if (!startIn || !endIn || !minIn) return;
|
||
|
|
|
||
|
|
const parseTime = (val) => {
|
||
|
|
if (!val) return null;
|
||
|
|
const [h,m] = val.split(':').map(Number);
|
||
|
|
return (h * 60) + m;
|
||
|
|
};
|
||
|
|
|
||
|
|
const toTimeStr = (totalMins) => {
|
||
|
|
const h = Math.floor(totalMins / 60) % 24;
|
||
|
|
const m = totalMins % 60;
|
||
|
|
return `${h.toString().padStart(2,'0')}:${m.toString().padStart(2,'0')}`;
|
||
|
|
};
|
||
|
|
|
||
|
|
const recalculate = (trigger) => {
|
||
|
|
const s = parseTime(startIn.value);
|
||
|
|
const e = parseTime(endIn.value);
|
||
|
|
const dur = parseInt(minIn.value);
|
||
|
|
|
||
|
|
if (trigger === 'start' || trigger === 'end') {
|
||
|
|
if (s !== null && e !== null) {
|
||
|
|
let diff = e - s;
|
||
|
|
if (diff < 0) diff += 24*60;
|
||
|
|
minIn.value = diff;
|
||
|
|
} else if (s !== null && !isNaN(dur) && dur > 0 && !endIn.value) {
|
||
|
|
endIn.value = toTimeStr(s + dur);
|
||
|
|
} else if (e !== null && !isNaN(dur) && dur > 0 && !startIn.value) {
|
||
|
|
let base = e - dur;
|
||
|
|
while (base < 0) base += 24*60;
|
||
|
|
startIn.value = toTimeStr(base);
|
||
|
|
}
|
||
|
|
} else if (trigger === 'min') {
|
||
|
|
if (s !== null && !isNaN(dur) && dur > 0) {
|
||
|
|
endIn.value = toTimeStr(s + dur);
|
||
|
|
} else if (e !== null && !isNaN(dur) && dur > 0 && !startIn.value) {
|
||
|
|
let base = e - dur;
|
||
|
|
while(base < 0) base+=24*60;
|
||
|
|
startIn.value = toTimeStr(base);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
startIn.addEventListener('change', () => recalculate('start'));
|
||
|
|
endIn.addEventListener('change', () => recalculate('end'));
|
||
|
|
minIn.addEventListener('input', () => recalculate('min'));
|
||
|
|
}
|
||
|
|
|
||
|
|
async function createManualTimeV1(event) {
|
||
|
|
event.preventDefault();
|
||
|
|
const minutes = Number(document.getElementById('timeV1Minutes')?.value || 0);
|
||
|
|
|
||
|
|
if (minutes <= 0) {
|
||
|
|
alert('Indtast minutter over 0');
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
const dateVal = document.getElementById('timeV1Date')?.value || null;
|
||
|
|
const tStart = document.getElementById('timeV1Start')?.value;
|
||
|
|
const tEnd = document.getElementById('timeV1End')?.value;
|
||
|
|
|
||
|
|
let startObj = null;
|
||
|
|
let endObj = null;
|
||
|
|
|
||
|
|
if (dateVal && tStart) {
|
||
|
|
try {
|
||
|
|
const l = new Date(`${dateVal}T${tStart}:00`);
|
||
|
|
startObj = l.toISOString();
|
||
|
|
} catch(e){}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (dateVal && tEnd) {
|
||
|
|
try {
|
||
|
|
const l = new Date(`${dateVal}T${tEnd}:00`);
|
||
|
|
if (startObj && new Date(startObj) > l) {
|
||
|
|
l.setDate(l.getDate() + 1);
|
||
|
|
}
|
||
|
|
endObj = l.toISOString();
|
||
|
|
} catch(e){}
|
||
|
|
}
|
||
|
|
|
||
|
|
const payload = {
|
||
|
|
sag_id: timeCaseId,
|
||
|
|
medarbejder_id: getTimeV1EmployeeId(),
|
||
|
|
faktisk_tid_min: minutes,
|
||
|
|
worked_date: dateVal,
|
||
|
|
entry_type: document.getElementById('timeV1Type')?.value || 'manuel',
|
||
|
|
entry_status: document.getElementById('timeV1Status')?.value || 'afventer',
|
||
|
|
beskrivelse: document.getElementById('timeV1Description')?.value || null,
|
||
|
|
kilde: 'manuel',
|
||
|
|
start_tid: startObj,
|
||
|
|
slut_tid: endObj
|
||
|
|
};
|
||
|
|
|
||
|
|
try {
|
||
|
|
const res = await fetch('/api/v1/timetracking/time/manual', {
|
||
|
|
method: 'POST',
|
||
|
|
headers: { 'Content-Type': 'application/json' },
|
||
|
|
body: JSON.stringify(payload)
|
||
|
|
});
|
||
|
|
if (!res.ok) throw new Error(await res.text());
|
||
|
|
|
||
|
|
const minutesInput = document.getElementById('timeV1Minutes');
|
||
|
|
const descInput = document.getElementById('timeV1Description');
|
||
|
|
const startIn = document.getElementById('timeV1Start');
|
||
|
|
const endIn = document.getElementById('timeV1End');
|
||
|
|
|
||
|
|
if (minutesInput) minutesInput.value = '';
|
||
|
|
if (descInput) descInput.value = '';
|
||
|
|
if (startIn) startIn.value = '';
|
||
|
|
if (endIn) endIn.value = '';
|
||
|
|
|
||
|
|
await loadTimeTrackingTab();
|
||
|
|
} catch (error) {
|
||
|
|
alert('Kunne ikke oprette tidsregistrering: ' + (error.message || 'ukendt fejl'));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
\n"""
|
||
|
|
|
||
|
|
if js_start != -1 and js_end != -1:
|
||
|
|
text = text[:js_start] + new_js + text[js_end:]
|
||
|
|
print("JS updated.")
|
||
|
|
|
||
|
|
dom_start = text.find('document.addEventListener(\'DOMContentLoaded\'')
|
||
|
|
if dom_start != -1:
|
||
|
|
dom_body_start = text.find('{', dom_start) + 1
|
||
|
|
# Check if we already injected it
|
||
|
|
if 'bindTimeV1Calculations();' not in text[dom_start:dom_start+200]:
|
||
|
|
text = text[:dom_body_start] + "\n bindTimeV1Calculations();" + text[dom_body_start:]
|
||
|
|
print("DOMContentLoaded updated.")
|
||
|
|
|
||
|
|
with open('app/modules/sag/templates/detail.html', 'w', encoding='utf-8') as f:
|
||
|
|
f.write(text)
|
||
|
|
print("File saved successfully.")
|