opsdash-app/opsdash/lib/Service/DashboardDefaultsService.php
blade34242 a0123eb662
All checks were successful
Nextcloud Server Tests / version-consistency (push) Successful in 50s
Nextcloud Server Tests / matrix-config (push) Successful in 39s
Nextcloud Server Tests / Nextcloud stable31 / PHP 8.2 (stable31, 8.2) (push) Successful in 19m40s
Nextcloud Server Tests / Nextcloud stable31 / PHP 8.3 (stable31, 8.3) (push) Successful in 20m4s
Nextcloud Server Tests / Nextcloud stable32 / PHP 8.2 (stable32, 8.2) (push) Successful in 19m20s
Nextcloud Server Tests / Nextcloud stable32 / PHP 8.3 (stable32, 8.3) (push) Successful in 16m58s
Nextcloud Server Tests / Nextcloud stable33 / PHP 8.2 (stable33, 8.2) (push) Successful in 18m46s
Nextcloud Server Tests / Nextcloud stable33 / PHP 8.3 (stable33, 8.3) (push) Successful in 19m37s
Nextcloud Server Tests / Nextcloud stable34 / PHP 8.2 (stable34, 8.2) (push) Successful in 18m21s
Nextcloud Server Tests / Nextcloud stable34 / PHP 8.3 (stable34, 8.3) (push) Successful in 19m13s
Respect customized dashboard layouts
2026-07-18 14:50:19 +07:00

537 lines
22 KiB
PHP

<?php
declare(strict_types=1);
namespace OCA\Opsdash\Service;
final class DashboardDefaultsService {
/**
* @return array{
* quick: array<int, array<string,mixed>>,
* standard: array<int, array<string,mixed>>,
* pro: array<int, array<string,mixed>>
* }
*/
public function getPresets(): array {
return [
'quick' => $this->buildQuickPreset(),
'standard' => $this->buildStandardPreset(),
'pro' => $this->buildProPreset(),
];
}
/**
* @return array{tabs: array<int, array{id: string, label: string, widgets: array<int, array<string,mixed>>}>, defaultTabId: string}
*/
public function createDefaultTabs(string $mode, string $strategy = ''): array {
if ($mode === 'pro') {
return $this->buildProTabs();
}
$preset = $mode === 'quick'
? $this->buildQuickPreset()
: $this->buildStandardPresetForStrategy($strategy);
return [
'tabs' => [
[
'id' => 'tab-1',
'label' => 'Overview',
'widgets' => $preset,
],
],
'defaultTabId' => 'tab-1',
];
}
/**
* @return array<int, array<string,mixed>>
*/
private function buildStandardPresetForStrategy(string $strategy): array {
$allowedByStrategy = [
'total_only' => ['targets_v2', 'time_summary_overview', 'dayoff_trend'],
'total_plus_categories' => [
'targets_v2', 'time_summary_overview', 'dayoff_trend', 'chart_pie',
'calendar_table', 'chart_stacked',
],
];
$allowed = $allowedByStrategy[$strategy] ?? null;
$preset = $this->buildStandardPreset();
if ($allowed === null) {
return $preset;
}
return array_values(array_filter(
$preset,
static fn (array $widget): bool => in_array((string)($widget['type'] ?? ''), $allowed, true),
));
}
/**
* @return array<int, array<string,mixed>>
*/
private function buildQuickPreset(): array {
// "quick" maps to the empty dashboard mode: one tab with no widgets.
return [];
}
/**
* @return array{tabs: array<int, array{id: string, label: string, widgets: array<int, array<string,mixed>>}>, defaultTabId: string}
*/
private function buildProTabs(): array {
return [
'tabs' => [
[
'id' => 'tab-1',
'label' => 'Overview',
'widgets' => $this->buildProPreset(),
],
[
'id' => 'tab-mjp2ziuq',
'label' => 'Table',
'widgets' => [
[
'id' => 'widget-calendar_table-1768128709761',
'type' => 'calendar_table',
'options' => [
'calendarFilter' => [],
'compact' => false,
'scale' => 'xl',
],
'layout' => ['width' => 'full', 'height' => 'xl', 'order' => 20],
'version' => 1,
],
],
],
[
'id' => 'tab-mk4vuh6c',
'label' => 'Charts',
'widgets' => [
[
'id' => 'widget-chart_pie-112jzy',
'type' => 'chart_pie',
'options' => [
'showLegend' => true,
'showLabels' => true,
'compact' => false,
'filterMode' => 'category',
'filterIds' => [],
],
'layout' => ['width' => 'half', 'height' => 'xl', 'order' => 58],
'version' => 1,
],
[
'id' => 'widget-chart_pie-1768128829168',
'type' => 'chart_pie',
'options' => [
'showLegend' => true,
'showLabels' => true,
'compact' => false,
'filterMode' => 'calendar',
'filterIds' => [],
],
'layout' => ['width' => 'half', 'height' => 'xl', 'order' => 57],
'version' => 1,
],
[
'id' => 'widget-chart_stacked-2ttp7b',
'type' => 'chart_stacked',
'options' => [
'showLegend' => true,
'showLabels' => false,
'compact' => false,
'forecastMode' => 'total',
'filterMode' => 'calendar',
'filterIds' => [],
],
'layout' => ['width' => 'full', 'height' => 'xl', 'order' => 59],
'version' => 1,
],
[
'id' => 'widget-chart_per_day-5tpozm',
'type' => 'chart_per_day',
'options' => [
'showLabels' => false,
'compact' => false,
'reverseOrder' => false,
'forecastMode' => 'total',
'filterMode' => 'calendar',
'filterIds' => [],
],
'layout' => ['width' => 'half', 'height' => 'xl', 'order' => 59.5],
'version' => 1,
],
[
'id' => 'widget-chart_dow-za4squ',
'type' => 'chart_dow',
'options' => [
'showLabels' => true,
'compact' => false,
'reverseOrder' => false,
'forecastMode' => 'total',
'filterMode' => 'calendar',
'filterIds' => [],
],
'layout' => ['width' => 'half', 'height' => 'xl', 'order' => 59.8],
'version' => 1,
],
[
'id' => 'widget-chart_hod-9rg0fn',
'type' => 'chart_hod',
'options' => [
'showHint' => true,
'showLegend' => true,
'lookbackMode' => 'overlay',
'compact' => true,
'reverseOrder' => false,
],
'layout' => ['width' => 'full', 'height' => 'l', 'order' => 60],
'version' => 1,
],
],
],
[
'id' => 'tab-mk4w4rau',
'label' => 'Workspace',
'widgets' => [
[
'id' => 'widget-deck_stats-1768297160410',
'type' => 'deck_stats',
'options' => [
'scope' => 'all', 'mineMode' => 'assignee',
'includeArchived' => true, 'includeCompleted' => true,
'metrics' => ['open_now', 'overdue_now', 'mine_open', 'created_in_range', 'completed_in_range', 'due_in_range'],
],
'layout' => ['width' => 'quarter', 'height' => 'xl', 'order' => 78],
'version' => 1,
],
[
'id' => 'widget-deck_cards-1768297173746',
'type' => 'deck_cards',
'options' => [
'allowMine' => true,
'includeArchived' => true,
'includeCompleted' => true,
'autoScroll' => false,
'intervalSeconds' => 5,
'showCount' => true,
'minFilterCount' => 1,
'maxVisible' => 8,
'autoTagsEnabled' => true,
'compactList' => false,
'customFilters' => [],
'filters' => [
'focus_all',
'focus_mine',
'backlog_all',
'backlog_mine',
'all',
],
'defaultFilter' => 'focus_all',
'mineMode' => 'assignee',
],
'layout' => ['width' => 'half', 'height' => 'xl', 'order' => 89],
'version' => 1,
],
[
'id' => 'widget-calendar_stats-1784281692933',
'type' => 'calendar_stats',
'options' => ['metrics' => ['tracked_hours', 'planned_later', 'event_count', 'average_event_duration']],
'layout' => ['width' => 'quarter', 'height' => 'xl', 'order' => 99],
'version' => 1,
],
],
],
],
'defaultTabId' => 'tab-1',
];
}
/**
* @return array<int, array<string,mixed>>
*/
private function buildStandardPreset(): array {
return [
[
'id' => 'widget-targets_v2-1',
'type' => 'targets_v2',
'options' => [
'showLegend' => true,
'showDelta' => true,
'showForecast' => true,
'showPace' => true,
'neverFinishedMode' => false,
'showToday' => true,
'showTotalDelta' => true,
'showNeedPerDay' => true,
'showCategoryBlocks' => true,
'badges' => true,
'includeWeekendToggle' => true,
'includeZeroDaysInStats' => false,
'useLocalConfig' => false,
],
'layout' => ['width' => 'half', 'height' => 'l', 'order' => 10],
'version' => 1,
],
[
'id' => 'widget-time_summary_overview-2',
'type' => 'time_summary_overview',
'options' => [
'heightMode' => 'auto',
'defaultView' => 'auto',
'showDailyKpis' => true,
'showWeekMiniChart' => true,
'showEmptyLanes' => true,
'maxLanes' => 4,
'showActivityNote' => true,
'showRangeInTitle' => false,
],
'layout' => ['width' => 'half', 'height' => 'l', 'order' => 20],
'version' => 1,
],
[
'id' => 'widget-balance_index-4',
'type' => 'balance_index',
'options' => [
'showConfig' => false,
'showTrend' => true,
'showMessages' => true,
'showCurrent' => true,
'reverseOrder' => false,
'messageDensity' => 'normal',
'indexBasis' => 'category',
'labelMode' => 'period',
'noticeAbove' => 0.15,
'noticeBelow' => 0.15,
'warnAbove' => 0.3,
'warnBelow' => 0.3,
'warnIndex' => 0.6,
'trendColor' => '#2563EB',
],
'layout' => ['width' => 'half', 'height' => 'm', 'order' => 30],
'version' => 1,
],
[
'id' => 'widget-dayoff_trend-5',
'type' => 'dayoff_trend',
'options' => [
'reverseOrder' => false,
'labelMode' => 'period',
'interpretation' => 'more_off_positive',
'toneLowColor' => '#DC2626',
'toneHighColor' => '#16A34A',
],
'layout' => ['width' => 'half', 'height' => 's', 'order' => 40],
'version' => 1,
],
[
'id' => 'widget-chart_pie-7',
'type' => 'chart_pie',
'options' => [
'heightMode' => 'auto',
'showLegend' => true,
'showLabels' => true,
'compact' => false,
'filterMode' => 'calendar',
'filterIds' => [],
],
'layout' => ['width' => 'half', 'height' => 'm', 'order' => 45],
'version' => 1,
],
[
'id' => 'widget-calendar_table-8',
'type' => 'calendar_table',
'options' => [
'calendarFilter' => [],
'compact' => false,
],
'layout' => ['width' => 'full', 'height' => 'l', 'order' => 56],
'version' => 1,
],
[
'id' => 'widget-chart_stacked-9',
'type' => 'chart_stacked',
'options' => [
'heightMode' => 'auto',
'showLegend' => true,
'showLabels' => false,
'compact' => false,
'forecastMode' => 'total',
'filterMode' => 'calendar',
'filterIds' => [],
],
'layout' => ['width' => 'full', 'height' => 'l', 'order' => 57],
'version' => 1,
],
[
'id' => 'widget-chart_per_day-10',
'type' => 'chart_per_day',
'options' => [
'showLabels' => false,
'compact' => false,
'reverseOrder' => false,
'forecastMode' => 'total',
'filterMode' => 'calendar',
'filterIds' => [],
],
'layout' => ['width' => 'half', 'height' => 'xl', 'order' => 58],
'version' => 1,
],
[
'id' => 'widget-chart_dow-11',
'type' => 'chart_dow',
'options' => [
'showLabels' => true,
'compact' => false,
'reverseOrder' => false,
'forecastMode' => 'total',
'filterMode' => 'calendar',
'filterIds' => [],
],
'layout' => ['width' => 'half', 'height' => 'm', 'order' => 58.5],
'version' => 1,
],
[
'id' => 'widget-chart_hod-12',
'type' => 'chart_hod',
'options' => [
'showLegend' => true,
'showHint' => false,
'compact' => false,
'reverseOrder' => false,
'lookbackMode' => 'overlay',
],
'layout' => ['width' => 'full', 'height' => 'l', 'order' => 59],
'version' => 1,
],
[
'id' => 'widget-category_mix_trend-standard',
'type' => 'category_mix_trend',
'options' => [
'density' => 'normal', 'labelMode' => 'period', 'colorMode' => 'hybrid',
'trendIndicator' => 'none', 'squareCells' => false, 'reverseOrder' => false,
'showHeader' => true, 'showBadge' => true, 'shareLowColor' => '#E2E8F0',
'shareHighColor' => '#60A5FA', 'toneLowColor' => '#E11D48', 'toneHighColor' => '#10B981',
],
'layout' => ['width' => 'full', 'height' => 'm', 'order' => 79],
'version' => 1,
],
];
}
/**
* @return array<int, array<string,mixed>>
*/
private function buildProPreset(): array {
return [
[
'id' => 'widget-targets_v2-1',
'type' => 'targets_v2',
'options' => [
'showLegend' => true,
'showDelta' => true,
'showForecast' => true,
'showPace' => true,
'neverFinishedMode' => false,
'showToday' => true,
'showTotalDelta' => true,
'showNeedPerDay' => true,
'showCategoryBlocks' => true,
'badges' => true,
'includeWeekendToggle' => true,
'includeZeroDaysInStats' => false,
'useLocalConfig' => false,
'localConfig' => null,
'scale' => 'md',
'heightMode' => 'auto',
],
'layout' => ['width' => 'half', 'height' => 'xl', 'order' => 10],
'version' => 1,
],
[
'id' => 'widget-time_summary_overview-2',
'type' => 'time_summary_overview',
'options' => [
'defaultView' => 'auto',
'showDailyKpis' => true,
'showWeekMiniChart' => true,
'showEmptyLanes' => false,
'maxLanes' => 4,
'showActivityNote' => true,
'showRangeInTitle' => false,
'scale' => 'md',
'heightMode' => 'auto',
],
'layout' => ['width' => 'half', 'height' => 'xl', 'order' => 20],
'version' => 1,
],
[
'id' => 'widget-balance_index-4',
'type' => 'balance_index',
'options' => [
'showTrend' => true,
'showMessages' => true,
'showConfig' => false,
'indexBasis' => 'both',
'noticeAbove' => 0.15,
'noticeBelow' => 0.15,
'warnAbove' => 0.3,
'warnBelow' => 0.3,
'warnIndex' => 0.6,
'messageDensity' => 'many',
'trendColor' => '#2563EB',
'showCurrent' => true,
'labelMode' => 'period',
'reverseOrder' => false,
'scale' => 'xl',
],
'layout' => ['width' => 'full', 'height' => 'l', 'order' => 40],
'version' => 1,
],
[
'id' => 'widget-dayoff_trend-5',
'type' => 'dayoff_trend',
'options' => [
'reverseOrder' => false,
'labelMode' => 'period',
'interpretation' => 'more_off_positive',
'toneLowColor' => '#DC2626',
'toneHighColor' => '#16A34A',
'ignoreCalendarIds' => [],
'ignoreCategoryIds' => [],
'scale' => 'xl',
'dense' => true,
],
'layout' => ['width' => 'full', 'height' => 'm', 'order' => 60],
'version' => 1,
],
[
'id' => 'widget-category_mix_trend-1784279284521',
'type' => 'category_mix_trend',
'options' => [
'density' => 'normal', 'labelMode' => 'period', 'colorMode' => 'hybrid',
'trendIndicator' => 'none', 'squareCells' => false, 'reverseOrder' => false,
'showHeader' => true, 'showBadge' => true, 'shareLowColor' => '#E2E8F0',
'shareHighColor' => '#60A5FA', 'toneLowColor' => '#E11D48', 'toneHighColor' => '#10B981',
],
'layout' => ['width' => 'full', 'height' => 'm', 'order' => 70],
'version' => 1,
],
];
}
/**
* @param array<string,mixed> $options
* @param array<string,mixed> $layout
* @return array<string,mixed>
*/
private function buildWidget(string $type, array $options, array $layout, int $index): array {
return [
'id' => sprintf('widget-%s-%d', $type, $index),
'type' => $type,
'options' => $options,
'layout' => [
'width' => (string)($layout['width'] ?? 'full'),
'height' => (string)($layout['height'] ?? 'm'),
'order' => is_numeric($layout['order'] ?? null) ? (float)$layout['order'] : 0.0,
],
'version' => 1,
];
}
}