OneBot: Leantime 改造版源码(BOM/Univer 表格/AI 接管/品牌替换等)
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
{{--
|
||||
Stakeholder Report — Page 4 (Programs & Narrative)
|
||||
|
||||
§5 page 4: Two columns — program rows with RAG + completed count on the
|
||||
left, status narrative from statusUpdates on the right. "Also this period"
|
||||
(secondary closures) at the bottom.
|
||||
|
||||
Vars in:
|
||||
$scope 'strategy' | 'program'
|
||||
$programRows object[] (strategy scope; empty at program scope)
|
||||
$programUpdates array<int,object[]> (byProject at strategy scope)
|
||||
--}}
|
||||
|
||||
|
||||
<div class="p4-two">
|
||||
{{-- ── Programs column (strategy scope only) ─────────────────── --}}
|
||||
<div>
|
||||
<div class="p4-lbl">{{ $scope === 'strategy' ? __('stakeholder.programs.label_programs') : __('stakeholder.programs.label_child_projects') }}</div>
|
||||
@if (count($programRows) === 0)
|
||||
<div class="p4-empty">{{ $scope === 'strategy' ? __('stakeholder.programs.none') : __('stakeholder.programs.none_projects') }}</div>
|
||||
@else
|
||||
@foreach ($programRows as $row)
|
||||
@php
|
||||
$row = (array) $row;
|
||||
// Status → dot color. programRows carries a status field from Marcel's
|
||||
// buildProgramRows (worst-of-children rollup); values: green/yellow/red/null.
|
||||
$status = (string) ($row['status'] ?? '');
|
||||
$dotColor = match ($status) {
|
||||
'green' => 'var(--rd-ok)',
|
||||
'yellow' => 'var(--rd-warn)',
|
||||
'red' => 'var(--rd-danger)',
|
||||
default => 'var(--rd-text-4)',
|
||||
};
|
||||
$statusLabel = match ($status) {
|
||||
'green' => __('stakeholder.programs.status_ontrack'),
|
||||
'yellow' => __('stakeholder.programs.status_atrisk'),
|
||||
'red' => __('stakeholder.programs.status_off'),
|
||||
default => __('stakeholder.programs.status_none'),
|
||||
};
|
||||
$completedCt = (int) ($row['completedCount'] ?? 0);
|
||||
@endphp
|
||||
<div class="p4-prog">
|
||||
<span class="pd" style="background:{{ $dotColor }}"></span>
|
||||
<span class="pn">{{ $row['name'] ?? '' }}</span>
|
||||
<span class="pm">{{ $statusLabel }} · {{ sprintf(__('stakeholder.programs.done_count'), $completedCt) }}</span>
|
||||
</div>
|
||||
@endforeach
|
||||
@endif
|
||||
</div>
|
||||
|
||||
{{-- ── Status narrative column ───────────────────────────────── --}}
|
||||
<div>
|
||||
<div class="p4-lbl">{{ __('stakeholder.programs.label_narrative') }}</div>
|
||||
@php
|
||||
// programUpdates is keyed by projectId. Flatten with a name lookup from
|
||||
// programRows for the bold label per line.
|
||||
$namesByProject = [];
|
||||
foreach ($programRows as $row) {
|
||||
$row = (array) $row;
|
||||
$namesByProject[(int) ($row['id'] ?? 0)] = $row['name'] ?? '';
|
||||
}
|
||||
$flatUpdates = [];
|
||||
foreach ($programUpdates as $pid => $updates) {
|
||||
foreach ($updates as $update) {
|
||||
$update = (object) $update;
|
||||
$update->_projectName = $namesByProject[(int) $pid] ?? '';
|
||||
$flatUpdates[] = $update;
|
||||
}
|
||||
}
|
||||
// Sort newest first — Marcel returns in the same order per project;
|
||||
// for cross-project flat display we re-sort by date desc.
|
||||
usort($flatUpdates, fn ($a, $b) => strcmp((string) ($b->date ?? ''), (string) ($a->date ?? '')));
|
||||
$flatUpdates = array_slice($flatUpdates, 0, 5); // top 5 for the packet view
|
||||
@endphp
|
||||
@if (count($flatUpdates) === 0)
|
||||
<div class="p4-empty">{{ __('stakeholder.programs.no_updates') }}</div>
|
||||
@else
|
||||
@foreach ($flatUpdates as $u)
|
||||
@php
|
||||
$date = ! empty($u->dateParsed) ? $u->dateParsed->setToUserTimezone()->format('M j') : '';
|
||||
$text = trim(strip_tags((string) ($u->text ?? '')));
|
||||
// Truncate long updates — board views want the executive summary.
|
||||
if (mb_strlen($text) > 220) $text = mb_substr($text, 0, 217).'…';
|
||||
@endphp
|
||||
<div class="p4-exec">
|
||||
@if (! empty($u->_projectName))
|
||||
<b>{{ $u->_projectName }}</b> —
|
||||
@endif
|
||||
{{ $text }}
|
||||
@if ($date !== '') <span class="ed">{{ $date }}</span> @endif
|
||||
</div>
|
||||
@endforeach
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- "Also this period" — secondary closures beyond the peak-this-period hero.
|
||||
Nomination surface for closures that could be attached to an outcome.
|
||||
Requires the nomination pass that also feeds the p1 hero; render coming-soon
|
||||
until that lands, to avoid pretending an empty state is a full one. --}}
|
||||
<div class="p4-also">
|
||||
<span class="al-lb">{{ __('stakeholder.programs.also_label') }}</span>
|
||||
<span style="color:var(--rd-text-3);"> — {{ __('stakeholder.programs.also_coming') }}</span>
|
||||
</div>
|
||||
Reference in New Issue
Block a user