Adding delay and penalty calculations

This commit is contained in:
2026-07-22 14:10:05 +03:30
parent 28bf82cde9
commit e1f86e6ffe
21 changed files with 1285 additions and 99 deletions
+144 -14
View File
@@ -1077,6 +1077,14 @@ $state = [
}
}
function toDatetimeLocalValue(iso) {
if (!iso) return '';
const d = new Date(iso);
if (Number.isNaN(d.getTime())) return '';
const pad = (n) => String(n).padStart(2, '0');
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}`;
}
function renderTaskDurations(task) {
const list = document.getElementById('modalDurationsList');
if (!list) return;
@@ -1102,7 +1110,9 @@ $state = [
const start = d.start ? new Date(d.start).toLocaleString('fa-IR') : '-';
const end = d.end ? new Date(d.end).toLocaleString('fa-IR') : 'در حال انجام';
const dur = d.duration || (d.end ? '-' : '...');
return `
if (!isAdminLevel1) {
return `
<div class="ui-panel border border-gray-200 rounded-lg p-2">
<div class="text-xs text-gray-500 flex justify-between">
<span>${escapeHtml(uname)}</span>
@@ -1112,9 +1122,92 @@ $state = [
شروع: ${escapeHtml(start)}<br/>
پایان: ${escapeHtml(end)}
</div>
</div>
`;
</div>`;
}
return `
<div class="ui-panel border border-gray-200 rounded-lg p-2" data-duration-id="${d.id}">
<div class="text-xs text-gray-500 flex justify-between mb-2">
<span>${escapeHtml(uname)}</span>
<span class="font-mono duration-value">${escapeHtml(dur)}</span>
</div>
<div class="grid grid-cols-1 gap-2 text-xs">
<label class="flex flex-col gap-1">
<span>شروع</span>
<input type="datetime-local"
class="duration-start ui-input text-sm"
value="${toDatetimeLocalValue(d.start)}">
</label>
<label class="flex flex-col gap-1">
<span>پایان (خالی = در حال انجام)</span>
<input type="datetime-local"
class="duration-end ui-input text-sm"
value="${toDatetimeLocalValue(d.end)}">
</label>
<button type="button"
class="save-duration-btn bg-cyan-600 hover:bg-cyan-700 text-white rounded-lg py-1.5 px-3">
ذخیره زمان
</button>
</div>
</div>`;
}).join('');
if (isAdminLevel1) {
list.querySelectorAll('.save-duration-btn').forEach(btn => {
btn.addEventListener('click', async function () {
const card = this.closest('[data-duration-id]');
if (!card) return;
const durationId = card.getAttribute('data-duration-id');
const startVal = card.querySelector('.duration-start')?.value || '';
const endVal = card.querySelector('.duration-end')?.value || '';
if (!startVal) {
alert('زمان شروع الزامی است.');
return;
}
this.disabled = true;
try {
const res = await fetch("{{ route('task.duration.edit') }}", {
method: 'POST',
headers: {
'X-CSRF-TOKEN': '{{ csrf_token() }}',
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
duration_id: Number(durationId),
start: startVal,
end: endVal || null,
}),
});
const data = await res.json();
if (!res.ok || !data.status) {
alert(data.message || 'خطا در ذخیره زمان');
return;
}
if (currentTaskDetails && Array.isArray(currentTaskDetails.duration)) {
const idx = currentTaskDetails.duration.findIndex(x => String(x.id) === String(durationId));
if (idx >= 0) {
currentTaskDetails.duration[idx] = data.duration;
}
if (data.finished_at !== undefined) {
currentTaskDetails.finished_at = data.finished_at;
}
renderTaskDurations(currentTaskDetails);
updateModalTotalDuration(currentTaskDetails);
}
alert(data.message || 'ذخیره شد');
} catch (e) {
console.error(e);
alert('خطا در ارتباط با سرور');
} finally {
this.disabled = false;
}
});
});
}
}
function switchDetailsTab(tab) {
@@ -1262,6 +1355,7 @@ $state = [
? formatDateTime(task.created_at)
: '-';
document.getElementById('modalTaskAssignees').textContent = formatAssigneeNames(task);
updateTaskDateViews(task);
}
// باز کردن مودال جزئیات تسک
@@ -1280,8 +1374,7 @@ $state = [
// پر کردن فیلدهای مودال
document.getElementById('modalTaskTitle').textContent = task.title || 'بدون عنوان';
document.getElementById('modalTaskDescription').innerHTML = task.description || 'بدون توضیحات';
document.getElementById('modalTaskStartDateView').textContent = task.start_date ? formatJalaliDate(task.start_date) : '-';
document.getElementById('modalTaskTargetDateView').textContent = task.target_date ? formatJalaliDate(task.target_date) : '-';
updateTaskDateViews(task);
updateTaskFinishedAtView(task);
updateModalInfoFields(task);
@@ -1497,8 +1590,8 @@ $state = [
sdEdit.value = currentTaskDetails.start_date ? formatJalaliDate(currentTaskDetails.start_date) : '';
tdEdit.value = currentTaskDetails.target_date ? formatJalaliDate(currentTaskDetails.target_date) : '';
if (sdGreg) sdGreg.value = currentTaskDetails.start_date || '';
if (tdGreg) tdGreg.value = currentTaskDetails.target_date || '';
if (sdGreg) sdGreg.value = normalizeDateValue(currentTaskDetails.start_date);
if (tdGreg) tdGreg.value = normalizeDateValue(currentTaskDetails.target_date);
initModalDatepickers();
}
@@ -1638,15 +1731,52 @@ $state = [
modal.style.display = 'none';
}
// فرمت تاریخ شمسی (ساده)
function formatJalaliDate(dateStr) {
if (!dateStr) return '-';
// فرض می‌کنیم از کتابخانه Jalalian در blade استفاده می‌کنید، اما در js نیز می‌توان پیاده‌سازی کرد
// برای سادگی از Intl یا یک تبدیل ساده استفاده می‌کنیم
const d = new Date(dateStr);
if (isNaN(d)) return dateStr;
// این قسمت می‌تواند با کتابخانه moment-jalaali جایگزین شود
return d.toLocaleDateString('fa-IR'); // نیاز به تنظیم locale مرورگر دارد
const raw = String(dateStr).trim();
if (!raw || raw.startsWith('0000-00-00')) return '-';
if (typeof persianDate !== 'undefined') {
try {
const m = raw.match(/^(\d{4})-(\d{2})-(\d{2})/);
if (m) {
const d = new Date(Number(m[1]), Number(m[2]) - 1, Number(m[3]));
return new persianDate(d)
.toCalendar('persian')
.toLocale('en')
.format('YYYY/MM/DD');
}
const d = new Date(raw);
if (!Number.isNaN(d.getTime())) {
return new persianDate(d)
.toCalendar('persian')
.toLocale('en')
.format('YYYY/MM/DD');
}
} catch (e) {
}
}
const d = new Date(raw.includes('T') || raw.includes(' ') ? raw : (raw + 'T00:00:00'));
if (Number.isNaN(d.getTime())) return raw;
return d.toLocaleDateString('fa-IR');
}
function normalizeDateValue(dateStr) {
if (!dateStr) return '';
const m = String(dateStr).match(/^(\d{4}-\d{2}-\d{2})/);
return m ? m[1] : String(dateStr);
}
function updateTaskDateViews(task) {
const sdView = document.getElementById('modalTaskStartDateView');
const tdView = document.getElementById('modalTaskTargetDateView');
if (sdView) {
sdView.textContent = task.start_date ? formatJalaliDate(task.start_date) : '-';
}
if (tdView) {
tdView.textContent = task.target_date ? formatJalaliDate(task.target_date) : '-';
}
}
function formatDateTime(dateStr) {