edit filter time

This commit is contained in:
2026-06-02 22:17:47 +03:30
parent 300575930f
commit 41d1036300
7 changed files with 844 additions and 250 deletions
+99 -115
View File
@@ -22,127 +22,126 @@ if (isset($task)) {
<div class="w-full mt-[70px] justify-center container flex">
<div class="ui-card">
<div class="ui-card-header">{{isset($task) ? "ویرایش وظیفه" : "ایجاد وظیفه جدید"}}</div>
<form action="{{isset($task) ? Route('task.edit') : Route('task.create')}}" method="POST" class="ui-card-body grid grid-cols-2 gap-4 items-center">
<form action="{{isset($task) ? Route('task.edit') : Route('task.create')}}" method="POST" class="ui-card-body ui-form-stack">
@csrf
@if(isset($task))
<input type="hidden" id="id" name="id" value="{{$task->id}}">
@endif
<label for="title" class="font-medium text-sm"><span class="text-red-500">*</span>عنوان:</label>
<input class="rounded-lg border border-gray-300 p-2 w-full" value="{{$task->title ?? null}}" type="text"
id="title" name="title" required>
<label for="description" class="font-medium text-sm">توضیحات:</label>
<textarea id="description" name="description"
class="rounded-lg border border-gray-300 p-2 w-full">{{$task->description ?? null}}</textarea>
<div class="ui-form-field">
<label for="title"><span class="text-red-500">*</span> عنوان</label>
<input class="ui-input" value="{{$task->title ?? null}}" type="text"
id="title" name="title" required>
</div>
<label for="priority" class="font-medium text-sm">اولویت:</label>
<select id="priority" name="priority" class="rounded-lg border border-gray-300 p-2 w-full">
<option value="none" {{ old('priority', $task->priority ?? '') == 'none' ? 'selected' : '' }}>ندارد</option>
<option value="low" {{ old('priority', $task->priority ?? '') == 'low' ? 'selected' : '' }}>کم</option>
<option value="medium" {{ old('priority', $task->priority ?? '') == 'medium' ? 'selected' : '' }}>عادی</option>
<option value="urgent" {{ old('priority', $task->priority ?? '') == 'urgent' ? 'selected' : '' }}>فوری</option>
<option value="high" {{ old('priority', $task->priority ?? '') == 'high' ? 'selected' : '' }}>زیاد</option>
</select>
<div class="ui-form-field">
<label for="description">توضیحات</label>
<textarea id="description" name="description" class="ui-input">{{$task->description ?? null}}</textarea>
</div>
<!-- تاریخ شروع -->
<div class="p-2">
<label for="start_date" class="font-medium text-sm">تاریخ شروع <span
class="text-red-500">*</span></label>
<input type="text" id="start_date" name="start_date_display"
class="rounded-lg border border-gray-300 p-2 " value="{{$task->start_date ?? null}}"
<div class="ui-form-field">
<label for="priority">اولویت</label>
<select id="priority" name="priority" class="ui-input">
<option value="none" {{ old('priority', $task->priority ?? '') == 'none' ? 'selected' : '' }}>ندارد</option>
<option value="low" {{ old('priority', $task->priority ?? '') == 'low' ? 'selected' : '' }}>کم</option>
<option value="medium" {{ old('priority', $task->priority ?? '') == 'medium' ? 'selected' : '' }}>عادی</option>
<option value="urgent" {{ old('priority', $task->priority ?? '') == 'urgent' ? 'selected' : '' }}>فوری</option>
<option value="high" {{ old('priority', $task->priority ?? '') == 'high' ? 'selected' : '' }}>زیاد</option>
</select>
</div>
<div class="ui-form-field">
<label for="start_date">تاریخ شروع <span class="text-red-500">*</span></label>
<input type="text" id="start_date" name="start_date_display" class="ui-input"
value="{{$task->start_date ?? null}}"
placeholder="تاریخ شروع" autocomplete="off" readonly required>
<input type="hidden" name="start_date" id="start_date_gregorian">
</div>
<!-- تاریخ هدف -->
<label for="target_date" class="font-medium text-sm">تاریخ هدف</label>
{{-- <div class="relative">--}}
<input type="text" id="target_date" name="target_date_display"
class="rounded-lg border border-gray-300 p-2 pwt-datepicker-input-element mt-2"
value="{{$task->target_date ?? null}}"
placeholder="تاریخ هدف" autocomplete="off" readonly>
<button type="button" id="clear_target_date"
class="absolute left-2 top-1/2 transform -translate-y-1/2 text-gray-400 hover:text-red-500">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M6 18L18 6M6 6l12 12"></path>
</svg>
</button>
{{-- </div>--}}
<div class="ui-form-field">
<label for="target_date">تاریخ هدف</label>
<div class="ui-input-wrap">
<input type="text" id="target_date" name="target_date_display" class="ui-input"
value="{{$task->target_date ?? null}}"
placeholder="تاریخ هدف" autocomplete="off" readonly>
<button type="button" id="clear_target_date" class="ui-input-clear" aria-label="پاک کردن تاریخ هدف">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M6 18L18 6M6 6l12 12"></path>
</svg>
</button>
</div>
<input type="hidden" name="target_date" id="target_date_gregorian">
</div>
<!-- وضعیت -->
<label for="state" class="font-medium text-sm"><span class="text-red-500">*</span>وضعیت:</label>
<select id="state" name="state" class="rounded-lg border border-gray-300 p-2 w-full" required>
<option value="1" {{ old('state', $task->state ?? '') == 1 ? 'selected' : '' }}>در انتظار</option>
<option value="2" {{ old('state', $task->state ?? '') == 2 ? 'selected' : '' }}>در صف انجام</option>
<option value="3" {{ old('state', $task->state ?? '') == 3 ? 'selected' : '' }}>درحال انجام</option>
<option value="4" {{ old('state', $task->state ?? '') == 4 ? 'selected' : '' }}>انجام‌شده</option>
</select>
<div class="ui-form-field">
<label for="state"><span class="text-red-500">*</span> وضعیت</label>
<select id="state" name="state" class="ui-input" required>
<option value="1" {{ old('state', $task->state ?? '') == 1 ? 'selected' : '' }}>در انتظار</option>
<option value="2" {{ old('state', $task->state ?? '') == 2 ? 'selected' : '' }}>در صف انجام</option>
<option value="3" {{ old('state', $task->state ?? '') == 3 ? 'selected' : '' }}>درحال انجام</option>
<option value="4" {{ old('state', $task->state ?? '') == 4 ? 'selected' : '' }}>انجام‌شده</option>
</select>
</div>
<!-- کاربران مسئول -->
<label for="assignees" class="font-medium text-sm">انتصاب
کاربران:</label>
<div class="relative" id="dropdown-container">
{{-- دکمه --}}
<button type="button" id="dropdown-btn"
class="ui-input text-right flex justify-between items-center">
<span id="selected-count">انتخاب کاربران</span>
<svg class="w-4 h-4 transition-transform" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M19 9l-7 7-7-7"></path>
</svg>
</button>
<div class="ui-form-field">
<label for="dropdown-btn">انتصاب کاربران</label>
<div class="relative" id="dropdown-container">
<button type="button" id="dropdown-btn"
class="ui-input text-right flex justify-between items-center">
<span id="selected-count">انتخاب کاربران</span>
<svg class="w-4 h-4 transition-transform" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M19 9l-7 7-7-7"></path>
</svg>
</button>
<div id="dropdown-list"
class="hidden absolute z-20 w-full ui-panel border border-gray-300 rounded-lg shadow-lg">
<div id="dropdown-list"
class="hidden absolute z-20 w-full ui-panel border border-gray-300 rounded-lg shadow-lg mt-1">
<div class="p-2 border-b">
<input type="text" id="search-input"
class="w-full px-3 py-1 border border-gray-200 rounded-lg text-sm"
placeholder="جستجوی کاربر...">
</div>
<div class="p-2 border-b">
<input type="text" id="search-input"
class="w-full px-3 py-1 border border-gray-200 rounded-lg text-sm"
placeholder="جستجوی کاربر...">
</div>
{{-- لیست کاربران --}}
<div id="users-list" class="max-h-48 overflow-y-auto space-y-1 ">
@foreach($users as $user)
<label class="user-item flex items-center gap-2 p-1 hover:bg-gray-50 rounded cursor-pointer"
data-name="{{ $user->name }}">
<input type="checkbox"
name="assignees[]"
value="{{ $user->id }}"
class="user-checkbox rounded text-cyan-600"
{{ isset($task) && (in_array($user->id, $selectedAssignees)) ? 'checked' : '' }}>
<div id="users-list" class="max-h-48 overflow-y-auto space-y-1 p-1">
@foreach($users as $user)
<label class="user-item flex items-center gap-2 p-1 hover:bg-gray-50 rounded cursor-pointer"
data-name="{{ $user->name }}">
<input type="checkbox"
name="assignees[]"
value="{{ $user->id }}"
class="user-checkbox rounded text-cyan-600"
{{ isset($task) && (in_array($user->id, $selectedAssignees)) ? 'checked' : '' }}>
<span>{{ $user->name }}</span>
</label>
@endforeach
<span>{{ $user->name }}</span>
</label>
@endforeach
</div>
</div>
</div>
</div>
{{-- <!-- برچسب‌ها -->--}}
{{-- <label for="labels" class="font-medium text-sm">برچسب‌ها:</label>--}}
{{-- <input class="rounded-lg border border-gray-300 p-2 w-full"--}}
{{-- type="text" id="labels" name="labels[]" placeholder="UUID1, UUID2">--}}
<div class="ui-form-field">
<label for="app">نام اپ</label>
<select id="app" name="app" class="ui-input">
<option value="">-- انتخاب اپ --</option>
@if(!empty($apps) && count($apps) > 0)
@foreach($apps as $app)
<option value="{{ $app->id }}" {{ old('app', $task->app ?? '') == $app->id ? 'selected' : '' }}>
{{ $app->name_fa }} {{ $app->type_fa }}
</option>
@endforeach
@endif
</select>
</div>
<!-- اپ -->
<label for="app" class="font-medium text-sm">نام اپ:</label>
<select id="app" name="app" class="rounded-lg z-20 border border-gray-300 p-2 w-full">
<option value="">-- انتخاب اپ --</option>
@if(!empty($apps) && count($apps) > 0)
@foreach($apps as $app)
<option value="{{ $app->id }}" {{ old('app', $task->app ?? '') == $app->id ? 'selected' : '' }}>
{{ $app->name_fa }} {{ $app->type_fa }}
</option>
@endforeach
@endif
</select>
<div class="inline-block flex pt-3 justify-center">
<button type="submit"
class="bg-cyan-600 text-white py-2 px-4 rounded-lg hover:bg-cyan-700 transition <!--mr-24 mt-2.5-->">
{{isset($task) ? "ویرایش" : "ایجاد وظیفه جدید"}}</button>
<div class="ui-form-actions">
<button type="submit"
class="bg-cyan-600 text-white py-2 px-6 rounded-lg hover:bg-cyan-700 transition">
{{isset($task) ? "ویرایش" : "ایجاد وظیفه جدید"}}
</button>
</div>
</form>
@@ -156,7 +155,6 @@ if (isset($task)) {
const usersList = document.getElementById('users-list');
const selectedCountSpan = document.getElementById('selected-count');
// باز و بسته کردن
dropdownBtn.addEventListener('click', (e) => {
e.stopPropagation();
dropdownList.classList.toggle('hidden');
@@ -165,29 +163,22 @@ if (isset($task)) {
}
});
// بستن با کلیک بیرون
document.addEventListener('click', (e) => {
if (!dropdownBtn.contains(e.target) && !dropdownList.contains(e.target)) {
dropdownList.classList.add('hidden');
}
});
// قابلیت جستجو
searchInput.addEventListener('input', function () {
const searchText = this.value.toLowerCase();
const items = usersList.querySelectorAll('.user-item');
items.forEach(item => {
const name = item.getAttribute('data-name').toLowerCase();
if (name.includes(searchText)) {
item.style.display = 'flex';
} else {
item.style.display = 'none';
}
item.style.display = name.includes(searchText) ? 'flex' : 'none';
});
});
// آپدیت تعداد
function updateSelectedCount() {
const selected = document.querySelectorAll('.user-checkbox:checked');
const count = selected.length;
@@ -201,7 +192,6 @@ if (isset($task)) {
updateSelectedCount();
$(document).ready(function () {
// تاریخ شروع با ساعت جاری
$('#start_date').persianDatepicker({
format: 'YYYY/MM/DD',
autoClose: true,
@@ -210,15 +200,12 @@ if (isset($task)) {
todayButton: {enabled: true, text: {fa: 'امروز'}}
},
onSelect: function (unix) {
// تبدیل به میلادی
let gregorian = new persianDate(unix).toCalendar('gregorian').toLocale('en');
let dateStr = gregorian.format('YYYY-MM-DD');
$('#start_date_gregorian').val(dateStr);
console.log('Start Gregorian:', dateStr);
}
});
// تاریخ هدف
$('#target_date').persianDatepicker({
format: 'YYYY/MM/DD',
autoClose: true,
@@ -229,13 +216,10 @@ if (isset($task)) {
onSelect: function (unix) {
let gregorianDate = new persianDate(unix).toCalendar('gregorian').toLocale('en');
let dateStrTarget = gregorianDate.format('YYYY-MM-DD');
// let dateTimeStr = `${dateStr} 23:59:59`;
$('#target_date_gregorian').val(dateStrTarget);
console.log('Target DateTime (Gregorian):', dateStrTarget);
}
});
// دکمه پاک کردن تاریخ هدف
$('#clear_target_date').on('click', function () {
$('#target_date').val('');
$('#target_date_gregorian').val('');
@@ -243,4 +227,4 @@ if (isset($task)) {
});
</script>
@endsection
@endsection
+275 -41
View File
@@ -93,7 +93,54 @@ $state = [
box-shadow: var(--shadow-soft);
}
.tasks-filter select {
.tasks-filters {
display: flex;
flex-direction: column;
gap: 0.75rem;
margin-bottom: 0.75rem;
}
.tasks-filters .tasks-filter {
margin-bottom: 0;
}
.tasks-filter-date {
flex-direction: column;
align-items: stretch;
gap: 0.65rem;
}
.tasks-filter-date-head {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0.5rem;
}
.tasks-filter-date-head select {
flex: 1;
min-width: 140px;
}
.tasks-filter-date-row {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0.5rem;
margin-top: 0.45rem;
}
.tasks-filter-date-controls {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0.5rem;
flex: 1;
min-width: 0;
}
.tasks-filter select,
.tasks-filter input[type="text"] {
border-radius: 0.875rem;
border: 1px solid var(--border-input);
background: var(--surface-input);
@@ -101,7 +148,38 @@ $state = [
transition: border-color 0.2s ease, box-shadow 0.2s ease;
}
.tasks-filter select:focus {
.tasks-filter-date-field {
flex: 1 1 7rem;
min-width: 7rem;
padding: 0.5rem 0.75rem;
font-size: 0.875rem;
}
.tasks-filter-date-type {
min-width: 140px;
padding: 0.5rem 0.75rem;
font-size: 0.875rem;
}
.tasks-filter-clear {
padding: 0.5rem 0.75rem;
font-size: 0.8125rem;
border-radius: 0.875rem;
border: 1px solid var(--border-input);
background: var(--surface-tabs);
color: var(--text-secondary);
white-space: nowrap;
cursor: pointer;
transition: background-color 0.2s ease, color 0.2s ease;
}
.tasks-filter-clear:hover {
background: var(--surface-tab-hover);
color: var(--text-primary);
}
.tasks-filter select:focus,
.tasks-filter input[type="text"]:focus {
outline: none;
border-color: color-mix(in srgb, var(--primary-color) 55%, white);
box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary-color) 22%, transparent);
@@ -344,19 +422,45 @@ $state = [
});
}
function filterTasksByAssignee(assigneeId) {
function applyTaskFilters() {
const assigneeId = document.getElementById('assigneeFilter')?.value || '';
const dateField = document.getElementById('dateFilterField')?.value || 'target_date';
const fromDate = document.getElementById('dateFilterFromGregorian')?.value || '';
const toDate = document.getElementById('dateFilterToGregorian')?.value || '';
const hasDateFilter = Boolean(fromDate || toDate);
const dateAttrMap = {
target_date: 'targetDate',
start_date: 'startDate',
finished_at: 'finishedDate',
created_at: 'createdDate',
};
const dateAttr = dateAttrMap[dateField] || 'targetDate';
const hasAnyFilter = Boolean(assigneeId || hasDateFilter);
document.querySelectorAll('.task-row').forEach(row => {
if (!assigneeId) {
row.classList.remove('hidden');
return;
let visible = true;
if (assigneeId) {
let ids = [];
try {
ids = JSON.parse(row.dataset.assigneeIds || '[]');
} catch (e) {
ids = [];
}
visible = ids.includes(String(assigneeId));
}
let ids = [];
try {
ids = JSON.parse(row.dataset.assigneeIds || '[]');
} catch (e) {
ids = [];
if (visible && hasDateFilter) {
const taskDate = row.dataset[dateAttr] || '';
if (!taskDate) {
visible = false;
} else {
if (fromDate && taskDate < fromDate) visible = false;
if (toDate && taskDate > toDate) visible = false;
}
}
row.classList.toggle('hidden', !ids.includes(String(assigneeId)));
row.classList.toggle('hidden', !visible);
});
document.querySelectorAll('.task-table-container').forEach(panel => {
@@ -364,32 +468,90 @@ $state = [
const visibleRows = panel.querySelectorAll('.task-row:not(.hidden)').length;
const hasRows = panel.querySelectorAll('.task-row').length > 0;
if (emptyMsg) {
emptyMsg.classList.toggle('hidden', !assigneeId || !hasRows || visibleRows > 0);
emptyMsg.classList.toggle('hidden', !hasAnyFilter || !hasRows || visibleRows > 0);
}
});
updateTaskTabCounts();
}
function filterTasksByAssignee() {
applyTaskFilters();
}
function clearTaskDateFilter() {
const fromInput = document.getElementById('dateFilterFrom');
const toInput = document.getElementById('dateFilterTo');
const fromHidden = document.getElementById('dateFilterFromGregorian');
const toHidden = document.getElementById('dateFilterToGregorian');
if (fromInput) fromInput.value = '';
if (toInput) toInput.value = '';
if (fromHidden) fromHidden.value = '';
if (toHidden) toHidden.value = '';
applyTaskFilters();
}
</script>
<div class="container tasks-page w-full flex justify-center items-center flex-col">
@if($count > 0)
<div class="w-full max-w-6xl mx-auto mt-4">
@if(!empty($assigneeFilterOptions))
<div class="tasks-filter flex flex-wrap items-center gap-3">
<label for="assigneeFilter" class="text-sm font-medium text-gray-700 whitespace-nowrap">
فیلتر بر اساس مسئول:
</label>
<select id="assigneeFilter"
onchange="filterTasksByAssignee(this.value)"
class="flex-1 min-w-[180px] px-3 py-2 text-sm text-gray-800">
<option value="">همه مسئولین</option>
@foreach($assigneeFilterOptions as $option)
<option value="{{ $option['id'] }}">{{ $option['name'] }}</option>
@endforeach
</select>
<div class="tasks-filters">
@if(!empty($assigneeFilterOptions))
<div class="tasks-filter flex flex-wrap items-center gap-3">
<label for="assigneeFilter" class="text-sm font-medium text-gray-700 whitespace-nowrap">
فیلتر بر اساس مسئول:
</label>
<select id="assigneeFilter"
onchange="applyTaskFilters()"
class="flex-1 min-w-[180px] px-3 py-2 text-sm text-gray-800">
<option value="">همه مسئولین</option>
@foreach($assigneeFilterOptions as $option)
<option value="{{ $option['id'] }}">{{ $option['name'] }}</option>
@endforeach
</select>
</div>
@endif
<div class="tasks-filter tasks-filter-date">
<div class="tasks-filter-date-head">
<label for="dateFilterField" class="text-sm font-medium text-gray-700 whitespace-nowrap">
فیلتر بر اساس تاریخ:
</label>
<select id="dateFilterField"
onchange="applyTaskFilters()"
class="tasks-filter-date-type">
<option value="target_date">مهلت</option>
<option value="start_date">تاریخ شروع</option>
<option value="finished_at">تاریخ اتمام</option>
<option value="created_at">تاریخ ایجاد</option>
</select>
</div>
<div class="tasks-filter-date-row">
<input type="text"
id="dateFilterFrom"
class="tasks-filter-date-field"
placeholder="از تاریخ"
autocomplete="off"
readonly>
<input type="text"
id="dateFilterTo"
class="tasks-filter-date-field"
placeholder="تا تاریخ"
autocomplete="off"
readonly>
<button type="button"
id="dateFilterClear"
class="tasks-filter-clear"
onclick="clearTaskDateFilter()">
پاک کردن
</button>
<input type="hidden" id="dateFilterFromGregorian">
<input type="hidden" id="dateFilterToGregorian">
</div>
</div>
@endif
</div>
<div class="tasks-card mt-3">
<div class="tasks-tabs">
@@ -422,6 +584,10 @@ $state = [
<article class="task-card task-row {{ $task->active ? 'is-active' : '' }}"
data-task-id="{{ $task->id }}"
data-assignee-ids='@json($task->assignee_ids ?? [])'
data-start-date="{{ $task->start_date ? \Carbon\Carbon::parse($task->start_date)->format('Y-m-d') : '' }}"
data-target-date="{{ $task->target_date ? \Carbon\Carbon::parse($task->target_date)->format('Y-m-d') : '' }}"
data-finished-date="{{ $task->finished_at ? \Carbon\Carbon::parse($task->finished_at)->format('Y-m-d') : '' }}"
data-created-date="{{ $task->created_at ? \Carbon\Carbon::parse($task->created_at)->format('Y-m-d') : '' }}"
onclick="openTaskDetailsModal({{ $task->id }})"
role="button"
tabindex="0"
@@ -475,7 +641,7 @@ $state = [
@endforeach
</div>
<div class="task-filter-empty hidden tasks-empty">
تسکی برای این مسئول در این وضعیت وجود ندارد.
وظیفه‌ای با این فیلترها در این وضعیت وجود ندارد.
</div>
@else
<div class="tasks-empty">
@@ -584,6 +750,10 @@ $state = [
placeholder="مهلت" autocomplete="off" readonly>
<input id="modalTaskTargetDateGregorian" type="hidden">
</div>
<div>
<h3 class="text-sm font-medium text-gray-500 mb-1">تاریخ اتمام</h3>
<p class="text-gray-800 text-sm" id="modalTaskFinishedAtView">-</p>
</div>
<div>
<h3 class="text-sm font-medium text-gray-500 mb-1">زمان صرف شده</h3>
<p class="text-gray-800 text-sm font-mono" id="modalTaskDuration">00:00:00</p>
@@ -804,6 +974,7 @@ $state = [
window.tasksById = tasksById;
updateModalInfoFields(currentTaskDetails);
updateTaskFinishedAtView(currentTaskDetails);
renderTaskComments(currentTaskDetails);
renderTaskDurations(currentTaskDetails);
updateModalTotalDuration(currentTaskDetails);
@@ -1056,6 +1227,30 @@ $state = [
});
}
function resolveFinishedAtFromTask(task) {
if (task?.finished_at) {
return task.finished_at;
}
const durs = Array.isArray(task?.duration) ? task.duration : [];
const ends = durs
.map(d => d.end)
.filter(Boolean)
.sort((a, b) => new Date(b) - new Date(a));
return ends[0] || null;
}
function updateTaskFinishedAtView(task) {
const finishedView = document.getElementById('modalTaskFinishedAtView');
if (!finishedView) {
return;
}
const finishedAt = resolveFinishedAtFromTask(task);
finishedView.textContent = finishedAt ? formatDateTime(finishedAt) : '-';
}
function formatAssigneeNames(task) {
const names = Array.isArray(task?.assignee_names) ? task.assignee_names.filter(Boolean) : [];
return names.length ? names.join('، ') : '-';
@@ -1087,6 +1282,7 @@ $state = [
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) : '-';
updateTaskFinishedAtView(task);
updateModalInfoFields(task);
// اپ
@@ -1173,11 +1369,22 @@ $state = [
break;
}
}
currentTaskDetails.finished_at = data.finished_at || data.end || currentTaskDetails.finished_at;
}
currentTaskDetails.duration = durs;
renderTaskDurations(currentTaskDetails);
updateModalTotalDuration(currentTaskDetails);
updateTaskFinishedAtView(currentTaskDetails);
const row = document.querySelector(`.task-row[data-task-id="${currentTaskDetails.id}"]`);
if (row && currentTaskDetails.finished_at) {
row.dataset.finishedDate = String(currentTaskDetails.finished_at).slice(0, 10);
}
tasksById[currentTaskDetails.id] = currentTaskDetails;
tasksById[String(currentTaskDetails.id)] = currentTaskDetails;
window.tasksById = tasksById;
const nextAction = currentTaskDetails.active ? 'end' : 'start';
durationBtn.textContent = nextAction === 'start' ? 'شروع' : 'پایان';
@@ -1301,18 +1508,22 @@ $state = [
const sdEdit = $('#modalTaskStartDateEdit');
const tdEdit = $('#modalTaskTargetDateEdit');
const pickerOptions = {
format: 'YYYY/MM/DD',
autoClose: true,
toolbox: {
calendarSwitch: {enabled: false},
todayButton: {enabled: true, text: {fa: 'امروز'}}
}
};
if (sdEdit.length) {
try {
sdEdit.persianDatepicker('destroy');
} catch (e) {
}
sdEdit.persianDatepicker({
format: 'YYYY/MM/DD',
autoClose: true,
toolbox: {
calendarSwitch: {enabled: false},
todayButton: {enabled: true, text: {fa: 'امروز'}}
},
...pickerOptions,
onSelect: function (unix) {
let gregorian = new persianDate(unix).toCalendar('gregorian').toLocale('en');
let dateStr = gregorian.format('YYYY-MM-DD');
@@ -1328,12 +1539,7 @@ $state = [
} catch (e) {
}
tdEdit.persianDatepicker({
format: 'YYYY/MM/DD',
autoClose: true,
toolbox: {
calendarSwitch: {enabled: false},
todayButton: {enabled: true, text: {fa: 'امروز'}}
},
...pickerOptions,
onSelect: function (unix) {
let gregorian = new persianDate(unix).toCalendar('gregorian').toLocale('en');
let dateStr = gregorian.format('YYYY-MM-DD');
@@ -1376,7 +1582,6 @@ $state = [
currentTaskDetails.start_date = sdGreg?.value || null;
currentTaskDetails.target_date = tdGreg?.value || null;
// refresh view bits (بدون بازنشانی مودال)
const sdView = document.getElementById('modalTaskStartDateView');
const tdView = document.getElementById('modalTaskTargetDateView');
if (sdView) sdView.textContent = currentTaskDetails.start_date ? formatJalaliDate(currentTaskDetails.start_date) : '-';
@@ -1452,6 +1657,35 @@ $state = [
// NOTE: مدیریت زمان از مودال جزئیات حذف شده و فقط در تب "زمان‌ها" انجام می‌شود.
$(document).ready(function () {
const datePickerOptions = {
format: 'YYYY/MM/DD',
autoClose: true,
toolbox: {
calendarSwitch: {enabled: false},
todayButton: {enabled: true, text: {fa: 'امروز'}}
}
};
$('#dateFilterFrom').persianDatepicker({
...datePickerOptions,
onSelect: function (unix) {
const gregorian = new persianDate(unix).toCalendar('gregorian').toLocale('en');
$('#dateFilterFromGregorian').val(gregorian.format('YYYY-MM-DD'));
applyTaskFilters();
}
});
$('#dateFilterTo').persianDatepicker({
...datePickerOptions,
onSelect: function (unix) {
const gregorian = new persianDate(unix).toCalendar('gregorian').toLocale('en');
$('#dateFilterToGregorian').val(gregorian.format('YYYY-MM-DD'));
applyTaskFilters();
}
});
});
</script>
@endsection