Add reusable project templates

This commit is contained in:
2026-05-25 01:26:08 +00:00
parent 3b17c9ee14
commit 58d2edcacd
24 changed files with 1154 additions and 88 deletions
@@ -7,6 +7,7 @@ use App\Actions\Projects\DuplicateProjectConfiguration;
use App\Models\BoardColumn;
use App\Models\NotificationAction;
use App\Models\Project;
use App\Models\ProjectTemplate;
use App\Models\RepositoryConnection;
use App\Models\Team;
use App\Models\Ticket;
@@ -115,16 +116,24 @@ class ProjectControllerTest extends TestCase
'name' => 'Hidden Source',
'key' => 'SRC2',
]);
$template = ProjectTemplate::factory()->create([
'name' => 'Reusable Setup',
'slug' => 'reusable-setup',
'source_project_id' => $visibleProject->id,
]);
$this->syncProjectPermissions($visibleProject, $user, ['view']);
$this->actingAs($user)
->get(route('projects.create'))
->get(route('projects.create', ['template' => $template->id]))
->assertOk()
->assertInertia(fn (Assert $page) => $page
->component('projects/Create')
->has('copyAreaOptions', count(DuplicateProjectConfiguration::copyableAreas()))
->has('sourceProjects', 1)
->has('sourceTemplates', 1)
->where('sourceProjects.0.id', $visibleProject->id)
->where('sourceTemplates.0.id', $template->id)
->where('selectedSourceTemplateId', $template->id)
->where('copyAreaOptions.0.value', DuplicateProjectConfiguration::COPY_PROJECT_SETTINGS),
);
}
@@ -207,6 +216,66 @@ class ProjectControllerTest extends TestCase
->assertRedirect(route('projects.create'));
}
public function test_authenticated_users_can_create_projects_from_saved_templates(): void
{
$user = User::factory()->create();
$this->syncGlobalPermissions($user, ['project:create']);
$sourceProject = Project::factory()->create([
'key' => 'SRC1',
'name' => 'Source Project',
'show_author' => false,
'show_story_points' => false,
]);
$template = ProjectTemplate::factory()->create([
'source_project_id' => $sourceProject->id,
'name' => 'Kanban Template',
'slug' => 'kanban-template',
'copy_areas' => [
DuplicateProjectConfiguration::COPY_PROJECT_SETTINGS,
DuplicateProjectConfiguration::COPY_TICKET_TYPES,
],
'configuration_snapshot' => [
DuplicateProjectConfiguration::COPY_PROJECT_SETTINGS => [
'show_author' => false,
'show_story_points' => false,
],
DuplicateProjectConfiguration::COPY_TICKET_TYPES => [
[
'name' => 'Bug',
'position' => 1,
'is_parent' => false,
],
],
],
]);
$response = $this->actingAs($user)
->post(route('projects.store'), [
'creation_mode' => 'template',
'name' => 'Template Based Project',
'key' => 'TMP1',
'source_template_id' => $template->id,
]);
$project = Project::query()->where('key', 'TMP1')->firstOrFail();
$response->assertRedirect(route('projects.show', $project));
$this->assertSame(false, $project->fresh()->show_author);
$this->assertSame(false, $project->fresh()->show_story_points);
$this->assertDatabaseHas('ticket_types', [
'project_id' => $project->id,
'name' => 'Bug',
'position' => 1,
'is_parent' => false,
]);
$this->assertDatabaseMissing('tickets', [
'project_id' => $project->id,
]);
}
public function test_authenticated_users_can_duplicate_projects_with_selected_settings_only(): void
{
$user = User::factory()->create();