|null $areas * @return array|null */ public function handle(Project $project, ?array $areas = null): ?array { $project->loadMissing('projectTemplate'); /** @var ProjectTemplate|null $projectTemplate */ $projectTemplate = $project->projectTemplate; if ($projectTemplate === null) { return null; } $selectedAreas = $areas === null ? DuplicateProjectConfiguration::copyableAreas() : $this->duplicateProjectConfiguration->normalizeCopyAreas($areas); $normalizedTemplateConfiguration = $this->duplicateProjectConfiguration->normalizeConfiguration( $projectTemplate->configuration_snapshot ?? [], ); $templateConfiguration = collect($normalizedTemplateConfiguration) ->only($selectedAreas) ->all(); $currentConfiguration = $this->duplicateProjectConfiguration->export($project, $selectedAreas); $persistedOverrides = $projectTemplate->is_published ? array_values(array_intersect($project->template_overrides ?? [], $selectedAreas)) : []; $overriddenAreas = []; $inheritedAreas = []; foreach ($selectedAreas as $area) { $label = $this->labelForArea($area); $hasTemplateValue = array_key_exists($area, $templateConfiguration); if (! $hasTemplateValue) { continue; } if (! in_array($area, $persistedOverrides, true)) { $inheritedAreas[] = [ 'label' => $label, 'value' => $area, ]; continue; } $overriddenAreas[] = [ 'label' => $label, 'value' => $area, ]; } $overriddenAreaValues = array_flip(array_column($overriddenAreas, 'value')); $inheritedAreaValues = array_flip(array_column($inheritedAreas, 'value')); return [ 'effectiveConfiguration' => $currentConfiguration, 'localConfiguration' => array_diff_key($currentConfiguration, $templateConfiguration), 'localAreas' => collect(array_diff(array_keys($currentConfiguration), array_keys($templateConfiguration)))->map(fn (string $area): array => ['label' => $this->labelForArea($area), 'value' => $area])->values()->all(), 'inheritedAreas' => $inheritedAreas, 'inheritedConfiguration' => array_intersect_key($templateConfiguration, $inheritedAreaValues), 'inheritsFromTemplate' => $projectTemplate->is_published, 'overriddenAreas' => $overriddenAreas, 'overriddenConfiguration' => array_intersect_key($currentConfiguration, $overriddenAreaValues), 'resetRequirements' => $this->resetRequirements($project, $templateConfiguration, $persistedOverrides), 'templateConfiguration' => $templateConfiguration, 'templateId' => $projectTemplate->id, 'templateName' => $projectTemplate->name, 'templateSlug' => $projectTemplate->slug, ]; } /** * @param array $templateConfiguration * @param list $persistedOverrides * @return array */ private function resetRequirements(Project $project, array $templateConfiguration, array $persistedOverrides): array { if (! in_array(DuplicateProjectConfiguration::COPY_TICKET_STATUSES, $persistedOverrides, true)) { return []; } $templateStatuses = $templateConfiguration[DuplicateProjectConfiguration::COPY_TICKET_STATUSES] ?? null; if (! is_array($templateStatuses)) { return []; } $statuses = $project->ticketStatuses()->withCount('tickets')->get(); $templateKeys = array_column($templateStatuses, 'configuration_key'); $replacementOptions = collect($templateStatuses)->map( static fn (array $status): array => ['id' => $status['configuration_key'], 'name' => $status['name']], )->all(); $removedStatuses = $statuses ->filter(static fn ($status): bool => ! in_array($status->configuration_key, $templateKeys, true) && $status->tickets_count > 0) ->map(static fn ($status): array => ['id' => $status->id, 'name' => $status->name, 'ticketCount' => $status->tickets_count]) ->values()->all(); return $removedStatuses === [] ? [] : [ DuplicateProjectConfiguration::COPY_TICKET_STATUSES => [ 'removedStatuses' => $removedStatuses, 'replacementOptions' => $replacementOptions, ], ]; } private function labelForArea(string $area): string { foreach (DuplicateProjectConfiguration::copyAreaOptions() as $option) { if ($option['value'] === $area) { return $option['label']; } } return $area; } }