diff --git a/app/Http/Controllers/Business/SystemizationTodos.php b/app/Http/Controllers/Business/SystemizationTodos.php index 631dfd1f..68856828 100644 --- a/app/Http/Controllers/Business/SystemizationTodos.php +++ b/app/Http/Controllers/Business/SystemizationTodos.php @@ -170,6 +170,7 @@ class SystemizationTodos extends Controller ]), 'import' => route('business.systemization-todos.import'), 'history' => route('business.systemization-todos.history'), + 'overdue' => route('business.systemization-todos.overdue'), 'historyExport' => route('business.systemization-todos.history.export'), 'columnWidths' => route('business.systemization-todos.column-widths'), 'gridCells' => route('business.systemization-todos.grid-cells'), @@ -1525,6 +1526,45 @@ class SystemizationTodos extends Controller ]); } + public function overdue(): JsonResponse + { + $sequenceMap = $this->hierarchySequenceMap(); + $items = SystemizationTodo::query() + ->where('status', '<>', SystemizationTodo::STATUS_COMPLETED) + ->whereNotNull('reminder_date') + ->whereDate('reminder_date', '<', today()) + ->orderBy('reminder_date') + ->orderBy('sort_order') + ->orderBy('id') + ->get() + ->map(function (SystemizationTodo $todo) use ($sequenceMap): array { + $sequence = $sequenceMap[$todo->id] ?? []; + $reminderDate = $todo->reminder_date?->copy()->startOfDay(); + + return [ + 'id' => (int) $todo->id, + 'type' => (string) ($sequence['category_display'] ?? $todo->category ?? ''), + 'content' => (string) ($sequence['section_display'] ?? $todo->section ?? ''), + 'title' => (string) ($todo->title ?? ''), + 'owner' => implode('、', $this->ownerNames($todo->owner)), + 'assignee' => (string) ($todo->assignee ?? ''), + 'start_date' => $todo->start_date?->format('Y-m-d') ?? '', + 'reminder_date' => $reminderDate?->format('Y-m-d') ?? '', + 'due_date' => $todo->due_date?->format('Y-m-d') ?? (string) ($todo->due_note ?? ''), + 'overdue_days' => $reminderDate === null + ? 0 + : (int) $reminderDate->diffInDays(today()->startOfDay()), + ]; + }) + ->values(); + + return response()->json([ + 'ok' => true, + 'count' => $items->count(), + 'items' => $items, + ]); + } + public function historyExport(Request $request): StreamedResponse { $action = trim((string) $request->query('action', '')); diff --git a/resources/views/business/pages/systemization-todos.blade.php b/resources/views/business/pages/systemization-todos.blade.php index 189bb053..e76eb571 100644 --- a/resources/views/business/pages/systemization-todos.blade.php +++ b/resources/views/business/pages/systemization-todos.blade.php @@ -23,6 +23,9 @@ .sys-todo-summary__value { color:#171717; font-size:1rem; font-weight:700; font-variant-numeric:tabular-nums; } .sys-todo-summary__item.is-warning .sys-todo-summary__value { color:#935f07; } .sys-todo-summary__item.is-success .sys-todo-summary__value { color:#53793e; } + .sys-todo-summary__item.is-action { width:100%; color:inherit; font:inherit; text-align:left; cursor:pointer; } + .sys-todo-summary__item.is-action:hover { border-color:#cbb98e; background:#fff8e8; } + .sys-todo-summary__item.is-action:focus-visible { outline:2px solid var(--sys-gold); outline-offset:2px; } .sys-todo-filter { display:grid; grid-template-columns:repeat(6,minmax(8.5rem,1fr)); gap:.65rem .55rem; align-items:end; margin:0 0 .85rem; padding:.7rem; border:1px solid #e6dfd2; border-radius:.65rem; background:#fcfaf5; } .sys-todo-filter > label { display:grid; grid-column:span 1; gap:.25rem; min-width:0; color:#595959; font-size:.72rem; } .sys-todo-filter > label.sys-todo-filter__keyword { grid-column:span 3; } @@ -349,6 +352,25 @@ .sys-todo-image-dialog__foot a { color:#f0e4bf; font-size:.75rem; } .sys-todo-image-dialog__foot button { min-height:1.9rem; padding:0 .65rem; border:1px solid #8b4c4c; border-radius:.4rem; background:#2a1d1d; color:#ffb4b4; font:inherit; font-size:.72rem; cursor:pointer; } .sys-todo-image-dialog__foot button[hidden] { display:none; } + .sys-todo-overdue-dialog { width:min(76rem,calc(100vw - 2rem)); max-height:90vh; padding:0; border:1px solid var(--sys-border); border-radius:.8rem; background:#fff; color:#171717; overflow:hidden; } + .sys-todo-overdue-dialog::backdrop { background:rgba(23,23,23,.58); } + .sys-todo-overdue-dialog__head { display:flex; align-items:center; gap:.55rem; padding:.8rem 1rem; border-bottom:1px solid #e8e0d2; background:#fff8e8; } + .sys-todo-overdue-dialog__head h2 { margin:0; font-size:.95rem; } + .sys-todo-overdue-dialog__count { color:#935f07; font-size:.78rem; font-weight:600; } + .sys-todo-overdue-dialog__head button { margin-left:auto; width:2rem; height:2rem; border:1px solid var(--sys-border); border-radius:999px; background:#fff; color:#595959; cursor:pointer; } + .sys-todo-overdue-dialog__body { max-height:calc(90vh - 6.5rem); padding:.8rem 1rem; overflow:auto; } + .sys-todo-overdue-dialog__status { padding:1.5rem; color:#666; font-size:.8rem; text-align:center; } + .sys-todo-overdue-dialog__status[hidden], .sys-todo-overdue-dialog__table[hidden], .sys-todo-overdue-dialog__empty[hidden] { display:none; } + .sys-todo-overdue-table { width:100%; min-width:70rem; border-collapse:collapse; font-size:.75rem; } + .sys-todo-overdue-table th { position:sticky; top:0; z-index:1; padding:.55rem .6rem; border-bottom:1px solid #e2d9ca; background:#f6f0e5; color:#51483c; text-align:left; white-space:nowrap; } + .sys-todo-overdue-table td { padding:.6rem; border-bottom:1px solid #eee7da; line-height:1.55; vertical-align:top; } + .sys-todo-overdue-table tbody tr:hover td { background:#fffdf8; } + .sys-todo-overdue-table .is-title { min-width:18rem; font-weight:600; overflow-wrap:anywhere; } + .sys-todo-overdue-table .is-date { white-space:nowrap; font-variant-numeric:tabular-nums; } + .sys-todo-overdue-table .is-danger { color:#b80000; font-weight:600; white-space:nowrap; } + .sys-todo-overdue-dialog__empty { padding:2rem 1rem; color:#666; font-size:.8rem; text-align:center; } + .sys-todo-overdue-dialog__foot { display:flex; justify-content:flex-end; padding:.65rem 1rem; border-top:1px solid #e8e0d2; background:#fcfaf5; } + .sys-todo-overdue-dialog__foot button { min-height:2rem; padding:0 .85rem; border:1px solid var(--sys-border); border-radius:.4rem; background:#fff; color:#51483c; font:inherit; font-size:.76rem; cursor:pointer; } @media (max-width:1100px) { .sys-todo-summary { grid-template-columns:repeat(2,minmax(0,1fr)); } .sys-todo-filter { grid-template-columns:repeat(3,minmax(8.5rem,1fr)); } @@ -396,7 +418,10 @@