{{-- A notification link's ?notif=/?kbsync=/?push=/?import= opened its modal on arrival and is now spent. Drop it from the address bar so reloading doesn't re-open the modal. --}} @if ($oneShotCleanUrl) @endif {{-- Shrink the live Jira-status pill next to the story key so it aligns with the key. --}}
@if (session('wk_toast'))
@endif

Weekly Tasks · {{ $quarter }} · {{ $totalCount }} {{ $showAll ? 'tasks (all members)' : 'tasks' }}

@if ($showAll || count($members))
@if ($isLead) @endif
@endif
@if ($quarterHasCurrentWeek) @endif @include('partials.weekly-export', [ 'wxQuarter' => $quarter, 'wxIsLead' => $isLead, 'wxWeeks' => $weeks, 'wxCurrentWeek' => $currentWeek, ]) @if (! empty($lastKanboardSync)) Kanboard synced {{ \Illuminate\Support\Carbon::parse($lastKanboardSync)->diffForHumans() }} @endif
{{-- Moved-in-Jira panels: one per project, always visible (independent of the bell). --}} @once @endonce
@php $movedTotal = array_sum($movedCounts ?? []); @endphp
@foreach ($movedProjects as $proj) @endforeach
@foreach ($movedProjects as $proj) @php $rows = $movedStories[$proj] ?? []; $items = $movedItems[$proj] ?? []; @endphp
@forelse ($items as $it) @if ($it['kind'] === 'row') @include('partials.weekly-moved-node', [ 'n' => $it['node'], 'display' => null, 'member' => false, 'xShow' => "visible('".$proj."', '".$it['key']."')", ]) @else {{-- Stories sharing a summary prefix, stated once and collapsed under it. --}}
@if ($it['untracked'] === 0) all in Weekly @else {{ $it['untracked'] }} to add {{-- One task for the whole group: title = the shared prefix, covering every story below. --}} @endif
@foreach ($it['members'] as $mem) @include('partials.weekly-moved-node', [ 'n' => $mem['node'], 'display' => $mem['short'], 'member' => true, 'xShow' => "memVisible('".$proj."', '".$it['key']."', '".$mem['node']['move']->jira_key."')", ]) @endforeach @endif @empty

No new movements to add.

@endforelse @if (count($rows))

No matches for “”.

@endif
@endforeach
@foreach ($weeks as $week) @php $wtasks = $grouped[$week['start']] ?? []; $isCur = $week['start'] === $currentWeek; $isClosed = in_array($week['start'], $closedWeeks ?? [], true); $wcount = array_sum(array_map('count', $wtasks)); // A week is hidable when it's before the current week (only within the quarter // that contains today) — driven by the "Hide previous weeks" toggle. $hidable = $quarterHasCurrentWeek && $week['start'] < $currentWeek; @endphp
{{ $week['label'] }} {{ $wcount }} @if ($isCur)Current@endif @if ($isClosed)Closed@endif @if ($isLead && $isCur) @endif @if ($isLead && $isClosed) @endif
@foreach ($modules as $module) @php $rows = $wtasks[$module] ?? []; @endphp @foreach ($storyRows[$week['start']][$module] ?? [] as $unit) @php $t = $unit['task']; @endphp @php $statusCanon = collect($statuses)->firstWhere(fn ($s) => $s[0] === $t->status)[2] ?? $t->status; $sc = \App\Services\StatusService::statusColor(null, $statusCanon); // Returned gets its own maroon (its canonical is in_progress, so it'd // otherwise share the blue). if ($t->status === 'returned') { $sc = ['bg' => '#7f1d1d', 'fg' => '#ffffff']; } $isBug = strtolower((string) $t->source_type) === 'bug' || str_contains(strtolower((string) $t->task_type), 'bug'); // Task-type colour coding (mirrors the status pill). $typeColors = [ 'Test Planning' => ['bg' => '#eab308', 'fg' => '#1f2937'], // yellow 'Sanity Testing' => ['bg' => '#16a34a', 'fg' => '#ffffff'], // green 'Regression Testing' => ['bg' => '#16a34a', 'fg' => '#ffffff'], // green 'Functional Testing' => ['bg' => '#16a34a', 'fg' => '#ffffff'], // green 'New Bug Filed' => ['bg' => '#dc2626', 'fg' => '#ffffff'], // red 'Bug Retest' => ['bg' => '#dc2626', 'fg' => '#ffffff'], // red 'Meeting' => ['bg' => '#2563eb', 'fg' => '#ffffff'], // blue 'Admin' => ['bg' => '#2563eb', 'fg' => '#ffffff'], // blue 'Other Tasks' => ['bg' => '#6b7280', 'fg' => '#ffffff'], // grey ]; $tc = $typeColors[$t->task_type] ?? ['bg' => 'transparent', 'fg' => 'var(--ink)']; @endphp jira_key) data-jira="{{ $t->jira_key }}" @endif class="{{ $unit['role'] === 'child' ? 'wk-child-row' : ($unit['role'] === 'parent' ? 'wk-parent-row' : '') }}" @if ($unit['role'] === 'child') x-show="modOpen('{{ $module }}') && grpOpen('{{ $unit['parentId'] }}', {{ $unit['groupOpen'] ? 'true' : 'false' }})" x-cloak @else x-show="modOpen('{{ $module }}')" @endif> @endforeach @endforeach
TargetDue DateActuals
TaskTask TypeDate AccomplishedStatus
{{ $module }} {{ count($rows) }} @unless ($showAll) @endunless
@if ($unit['role'] === 'parent') 🔗 {{ $unit['childCount'] }} @endif @if ($t->jira_key) @if ($jiraBase) {{ $t->jira_key }} @else {{ $t->jira_key }} @endif @php $hist = $history[$t->jira_key] ?? null; @endphp
Status movement · {{ $t->jira_key }}
@if ($hist === null)
Loading…
@elseif (empty($hist))
No status history available.
@else
    @foreach ($hist as $e)
  1. {{ $e['status'] }} {{ $e['at'] }}
  2. @endforeach
@endif
@if (! empty($jiraStatuses[$t->jira_key]['status'])) @endif @if (! empty($returnCounts[$t->jira_key])) ↩ Returned ×{{ $returnCounts[$t->jira_key] }} @elseif (str_contains(strtolower((string) $t->task_type), 'retest')) {{-- The re-test line generated when a bug is marked Returned in Weekly (manual returns have no Jira return-move, so the ×N badge above won't show). --}} ↩ Returned @endif @if ($isBug) {{-- Linked stories already added → show their Jira IDs; keep a compact "+" to add any not-yet-added links (the picker fetches live). --}} @php $linkedTracked = array_values(array_filter($bugLinkedStories[$t->jira_key] ?? [], fn ($k) => isset($trackedByOwnerWeek[$t->owner_email.'|'.$t->week_start?->toDateString().'|'.$k]))); @endphp @foreach ($linkedTracked as $lk) @if ($jiraBase)🔗 {{ $lk }}@else🔗 {{ $lk }}@endif @endforeach @if (isset($bugsWithLinks[$t->jira_key])) @endif @elseif (! empty($storyBugCount[$t->jira_key])) 🐞 {{ $storyBugCount[$t->jira_key] }} bug{{ $storyBugCount[$t->jira_key] === 1 ? '' : 's' }} @endif @if (! empty($coveredCounts[$t->id]) && $coveredCounts[$t->id] > 1) {{-- Added from a prefix group: this one row stands in for N stories. --}} ▣ covers {{ $coveredCounts[$t->id] }} stories @endif @endif @if ($showAll){{ $names[$t->owner_email] ?? $t->owner_email }}@endif
@endforeach
{{-- Import modal --}} @if ($showImport)
Import Jira Stories @if ($showAll) @endif

Your QA tasks — drop a story onto a module

@foreach ($modules as $module) @php $stagedHere = collect($staged)->where('module', $module)->values(); @endphp
{{ $module }} drop here
@foreach ($stagedHere as $s)
{{ $s['key'] }}
@endforeach
@endforeach @if (count($staged))

{{ count($staged) }} staged — click Save to add {{ count($staged) === 1 ? 'it' : 'them' }} to Weekly Tasks.

@endif

Jira stories

@unless ($hasImportDashboard)

No dashboard uploaded for {{ $quarter }} yet — showing all stories for the quarter. Import one in Settings → Import Dashboard to prioritise project targets.

@endunless
@if (empty($importList))

No matching Jira stories.

@else @foreach ($importList as $it) @endforeach
StoryProjectTypeStatus
@if ($it['is_target'])TARGET@elseif ($it['prio'])@endif @if ($it['is_reported'])REPORTED@endif @if ($it['is_automation'])AUTOMATION@endif {{ $it['title'] }} {{ $it['key'] }}
@php $ownerName = ($it['owner_email'] ?? null) ? ($names[$it['owner_email']] ?? $it['owner_email']) : null; $sameOwner = $ownerName && $it['creator'] && $ownerName === $it['creator']; @endphp @if ($it['creator'] || $ownerName)
@if ($it['creator'])Reporter: {{ $it['creator'] }}@endif @if ($ownerName && ! $sameOwner)@if ($it['creator']) · @endif Owner: {{ $ownerName }}@endif
@endif
{{ $it['project'] }} @if ($it['status'])@else @endif
@endif
@endif {{-- Notification guidance modal (arrived from the bell; guidance only, no action) --}} @if ($notifModal) @php $k = $notifKey ?: 'this item'; $notifCopy = [ 'newbug' => ['New bug filed by QA', "New bug {$k} was filed by QA this week. Make sure it's logged in your Weekly Tasks and push it to Kanboard when you're ready."], 'bug_to_qa' => ['Bug moved to In QA', "Bug {$k} moved In Dev → In QA. Pick it up for testing and update its status in your Weekly Task."], 'bug_returned' => ['Bug returned to In Dev', "Bug {$k} was returned In QA → In Dev. Mark it Returned on its Weekly Task."], 'bug_done' => ['Bug completed', "Bug {$k} moved In QA → Done. Confirm the fix and mark the Weekly Task Done."], 'story_to_qa' => ['Story moved to In QA', "Story {$k} moved In Dev → In QA. Add it to your Weekly Tasks to begin testing."], 'story_done' => ['Story completed', "Story {$k} moved In QA → Done. Update its status in your Weekly Tasks."], ]; [$notifTitle, $notifBody] = $notifCopy[$notifKind] ?? ['Notification', '']; @endphp
{{ $notifTitle }}

{!! $notifBody !!}

This is a reminder — no changes were made automatically. Update the task below yourself.

@endif {{-- Kanboard status sync modal --}} @if ($showSync)
Sync from Kanboard — {{ $quarter }}{{ $showAll ? '' : ' · ' . ($names[$member] ?? $member) }}
@if (count($syncNew))

{{ count($syncNew) }} Kanboard ticket{{ count($syncNew) === 1 ? '' : 's' }} not yet in Weekly Tasks (created or moved this week). Reflect adds {{ count($syncNew) === 1 ? 'it' : 'them' }}.

@if ($showAll)@endif @foreach ($syncNew as $it) @if ($showAll)@endif @endforeach
New ticketMemberModuleKanboard column
{{ $it['title'] }} {{ ($it['reason'] ?? 'new') === 'moved' ? 'moved' : 'new' }}{{ $names[$it['owner']] ?? $it['owner'] }}{{ $it['module'] }} {{ $it['column'] ?: '—' }}
@endif @if (empty($syncUpdates) && empty($syncNew))

Every Kanboard-linked task already matches Kanboard. 🎉

@elseif (! empty($syncUpdates))

{{ count($syncUpdates) }} task{{ count($syncUpdates) === 1 ? '' : 's' }} changed in Kanboard. Review the changes, then Reflect to update Weekly Tasks.

@if ($showAll)@endif @foreach ($syncUpdates as $d) @if ($showAll)@endif @endforeach
TaskWeekMemberChanges from Kanboard
{{ $d['title'] }} {{ $d['week'] ? \Illuminate\Support\Carbon::parse($d['week'])->format('M j') : '—' }}{{ $names[$d['owner']] ?? $d['owner'] }}@foreach ($d['changes'] as $c){{ $c }}@endforeach
@endif
@php $syncTotal = count($syncUpdates) + count($syncNew); @endphp
@endif {{-- Apply Kanboard changes modal (opened from a notification) --}} @if ($showKbSync) @php $kbTotal = count($kbNew) + count($kbMoved); @endphp
Kanboard changes this week{{ $showAll ? '' : ' · ' . ($names[$member] ?? $member) }}
@if (! $kbTotal)

Nothing new to apply — you're up to date for this week. 🎉

@else

Review the {{ $kbTotal }} change{{ $kbTotal === 1 ? '' : 's' }} from Kanboard, then Accept to apply them to Weekly Tasks.

@if (count($kbNew))

New tickets to import {{ count($kbNew) }}

@if ($showAll)@endif @foreach ($kbNew as $it) @if ($showAll)@endif @endforeach
TicketModuleMemberCreated
{{ $it['title'] }} #{{ $it['source_id'] }} {{ $it['module'] }}{{ $names[$it['owner']] ?? $it['owner'] }}{{ $it['created'] ? \Illuminate\Support\Carbon::parse($it['created'])->format('M j') : '—' }}
@endif @if (count($kbMoved))

Status movements to reflect {{ count($kbMoved) }}

@if ($showAll)@endif @foreach ($kbMoved as $it) @if ($showAll)@endif @endforeach
TaskMemberWeekly statusKanboard status
{{ $it['title'] }}{{ $names[$it['owner']] ?? $it['owner'] }}{{ $it['current_label'] }} {{ $it['kanboard_label'] }}
@endif @endif
@endif {{-- Push to Kanboard modal --}} @if ($showPush) @once @endonce
Push to Kanboard — {{ $quarter }}{{ $showAll ? '' : ' · '.($names[$member] ?? $member) }}
{{-- Each scope carries its count of un-pushed tasks, so rows waiting in another week (carry-overs and returns land in NEXT week) can't go unnoticed. --}}
@foreach ([['current','This week'], ['previous','Previous week'], ['next','Next week'], ['all','All open weeks']] as [$sk, $slabel]) @endforeach
Target project @if ($pushProject && count($projectChoices) <= 1) {{ $pushProjectName }} @else @endif
@if (empty($pushRows)) @php $scopeWord = ['previous' => 'for the previous week', 'next' => 'for next week', 'all' => 'in any open week'][$pushScope] ?? 'this week'; // Where the remaining un-pushed rows are — never claim "all done" while another scope still has some. $elsewhere = collect(['current' => 'This week', 'previous' => 'Previous week', 'next' => 'Next week']) ->filter(fn ($lbl, $k) => $k !== $pushScope && ($pushCounts[$k] ?? 0) > 0) ->map(fn ($lbl, $k) => ['label' => $lbl, 'n' => (int) $pushCounts[$k]]); @endphp

No new tasks to push {{ $scopeWord }}@if ($elsewhere->isNotEmpty()) — but {{ $elsewhere->map(fn ($e) => $e['n'].' in “'.$e['label'].'”')->implode(' and ') }} still {{ $elsewhere->sum('n') === 1 ? 'has' : 'have' }} no Kanboard ticket. @else — everything here is already in Kanboard. 🎉 @endif

@elseif (! $pushProject)

Pick the target Kanboard project to continue.

@else @php $weeks = collect($pushRows)->unique('week')->values(); $mods = collect($pushRows)->pluck('category')->filter()->unique()->values(); @endphp
Swimlane per week
@foreach ($weeks as $w)
{{ \Illuminate\Support\Carbon::parse($w['week'])->format('M j') }} · {{ $w['swimlane_name'] ?: 'no week bucket' }}
@endforeach @if ($mods->count())
Category per module
@foreach ($mods as $mod)
{{ $mod }}
@endforeach @endif

Click rows to include/exclude · status maps to the matching column.

@if ($showAll)@endif @foreach ($pushRows as $r) @if ($showAll)@endif @endforeach
TaskOwnerWeekModuleStatus
{{ $r['title'] }} @unless ($r['owner_linked'])⚠ unassigned@endunless {{ $names[$r['owner']] ?? $r['owner'] }}{{ \Illuminate\Support\Carbon::parse($r['week'])->format('M j') }} {{ $r['module'] }} {{ ucfirst(str_replace('_',' ',$r['status'])) }}
@endif @if (! empty($pushEdits))
Edited tasks already in Kanboard — will be updated

Click rows to include/exclude. Your Weekly edits overwrite the linked Kanboard ticket (title, status→column, due, type→tag, owner). Category is left as-is.

@if ($showAll)@endif @foreach ($pushEdits as $e) @if ($showAll)@endif @endforeach
TaskOwnerChanges to push
{{ $e['title'] }}{{ $names[$e['owner']] ?? $e['owner'] }}@foreach ($e['changes'] as $c){{ $c }}@endforeach
@endif
@endif {{-- Push result popup — replaces the toast on success; auto-dismisses or close manually. --}}

Closing automatically in s…

{{-- Move-to-next-week confirmation --}} @if ($moveConfirmId)
Move to next week

Are you sure you want to move this task to next week? It will be relocated to the following week, and when you push to Kanboard its ticket moves to next week's swimlane.

@endif {{-- Linked-issues picker --}} @if (!empty($linked))
{{ $linkedDirect ? 'Add linked stories' : 'Add linked issues' }}
{{ count($linked) }} {{ $linkedDirect ? 'stor'.(count($linked) === 1 ? 'y is' : 'ies are') : 'issues are' }} linked to {{ $linkedContext['bug'] ?? 'the bug you added' }}. Pick which to add to {{ $linkedContext['module'] ?? '' }}.
@foreach ($linked as $lx) @endforeach
@endif {{-- Arriving from the bug notification (?focus=KEY): scroll to + flash that bug's row. --}} @if ($focusKey)
@endif {{-- Delete confirmation. The Delete button animates the specific row in JS (app.js wkDeleteRow) and only then calls the server to remove it. --}} @if ($pendingDelete)
Delete task?
This removes the task from the weekly log. This can't be undone.
@endif {{-- Toast --}}