KAN-44: UI Rework 29: Repository-Einstellungen des Projekts #143
@@ -583,6 +583,8 @@ export const en = {
|
|||||||
'profile.delete.confirmAction': 'Permanently delete account',
|
'profile.delete.confirmAction': 'Permanently delete account',
|
||||||
'common.cancel': 'Cancel',
|
'common.cancel': 'Cancel',
|
||||||
'common.close': 'Close',
|
'common.close': 'Close',
|
||||||
|
'common.edit': 'Edit',
|
||||||
|
'common.remove': 'Remove',
|
||||||
'common.retry': 'Try again',
|
'common.retry': 'Try again',
|
||||||
'feedback.empty': 'Nothing here yet',
|
'feedback.empty': 'Nothing here yet',
|
||||||
'feedback.saved': 'Changes saved',
|
'feedback.saved': 'Changes saved',
|
||||||
@@ -874,6 +876,81 @@ export const en = {
|
|||||||
'inheritance.area.ticketLinkTypes': 'Ticket link types',
|
'inheritance.area.ticketLinkTypes': 'Ticket link types',
|
||||||
'inheritance.area.repositoryConnections': 'Repository connections',
|
'inheritance.area.repositoryConnections': 'Repository connections',
|
||||||
'inheritance.area.notificationActions': 'Notification actions',
|
'inheritance.area.notificationActions': 'Notification actions',
|
||||||
|
'repositorySettings.pageTitle': '{project} repository settings',
|
||||||
|
'repositorySettings.title': 'Project repositories',
|
||||||
|
'repositorySettings.description':
|
||||||
|
'Choose the repositories this project uses for ticket branches and pull requests.',
|
||||||
|
'repositorySettings.projectScope':
|
||||||
|
'Repository links belong to the project. Every team member keeps using the same linked repositories, while access is granted through their own personal Git connections.',
|
||||||
|
'repositorySettings.linkedRepositories': 'Linked repositories',
|
||||||
|
'repositorySettings.linkAction': 'Link repository',
|
||||||
|
'repositorySettings.empty': 'No project repositories yet',
|
||||||
|
'repositorySettings.emptyDescription':
|
||||||
|
'Link a repository from one of your personal Git connections to make it available to project tickets.',
|
||||||
|
'repositorySettings.personalAccessReady': 'Personal access ready',
|
||||||
|
'repositorySettings.personalAccessMissing': 'Personal access needed',
|
||||||
|
'repositorySettings.ticketUsage':
|
||||||
|
'Tickets use this project link to discover matching branches and pull requests.',
|
||||||
|
'repositorySettings.openRepository':
|
||||||
|
'Open {repository} in the Git provider (opens in a new tab)',
|
||||||
|
'repositorySettings.open': 'Open repository',
|
||||||
|
'repositorySettings.connectAccess': 'Connect Git access',
|
||||||
|
'repositorySettings.editRepository':
|
||||||
|
'Change project repository {repository}',
|
||||||
|
'repositorySettings.removeRepository':
|
||||||
|
'Remove project repository {repository}',
|
||||||
|
'repositorySettings.personalSources': 'Personal Git access',
|
||||||
|
'repositorySettings.personalSourcesDescription':
|
||||||
|
'These account connections are private to you and are only used to discover repositories you can link to this project.',
|
||||||
|
'repositorySettings.manageConnections': 'Manage Git connections',
|
||||||
|
'repositorySettings.refresh': 'Refresh repositories',
|
||||||
|
'repositorySettings.refreshing': 'Loading repositories…',
|
||||||
|
'repositorySettings.refreshingDescription':
|
||||||
|
'The latest repositories are being requested from your Git providers.',
|
||||||
|
'repositorySettings.noConnections': 'No personal Git connection',
|
||||||
|
'repositorySettings.noConnectionsDescription':
|
||||||
|
'Add a personal Git connection before linking or changing a project repository.',
|
||||||
|
'repositorySettings.detectedSources': 'Detected personal sources',
|
||||||
|
'repositorySettings.unavailable': 'Provider unavailable',
|
||||||
|
'repositorySettings.repositoryCount': '{count} repositories',
|
||||||
|
'repositorySettings.linkTitle': 'Link a project repository',
|
||||||
|
'repositorySettings.linkDescription':
|
||||||
|
'Choose a personal connection, find a repository, and add it to this project.',
|
||||||
|
'repositorySettings.editTitle': 'Change project repository',
|
||||||
|
'repositorySettings.editDescription':
|
||||||
|
'Replace this project link with another repository. Tickets will use the updated link after saving.',
|
||||||
|
'repositorySettings.removeTitle': 'Remove repository',
|
||||||
|
'repositorySettings.removeDescription':
|
||||||
|
'Remove this repository from the project? Tickets will no longer discover its branches or pull requests.',
|
||||||
|
'repositorySettings.linked': 'Repository linked to the project.',
|
||||||
|
'repositorySettings.updated': 'Project repository updated.',
|
||||||
|
'repositorySettings.removed': 'Repository removed from the project.',
|
||||||
|
'repositorySettings.matchingConnectionMissing':
|
||||||
|
'Matching personal access is missing',
|
||||||
|
'repositorySettings.matchingConnectionMissingDescription':
|
||||||
|
'The project link remains available to the team, but this account cannot query it until you add a matching provider connection. You can still choose a different accessible repository below.',
|
||||||
|
'repositorySettings.personalConnection': 'Personal Git connection',
|
||||||
|
'repositorySettings.personalConnectionHelp':
|
||||||
|
'Used for discovery only; it is not shared with the project.',
|
||||||
|
'repositorySettings.providerError':
|
||||||
|
'Repositories could not be loaded from this provider',
|
||||||
|
'repositorySettings.searchLabel': 'Find a repository',
|
||||||
|
'repositorySettings.searchPlaceholder':
|
||||||
|
'Search by repository, owner, or namespace',
|
||||||
|
'repositorySettings.noRepositories': 'No repositories available',
|
||||||
|
'repositorySettings.noRepositoriesDescription':
|
||||||
|
'This connection is working, but the provider returned no repositories you can access.',
|
||||||
|
'repositorySettings.noSearchResults': 'No matching repositories',
|
||||||
|
'repositorySettings.noSearchResultsDescription':
|
||||||
|
'No repository matches “{search}”. Try a different name or clear the search.',
|
||||||
|
'repositorySettings.repositoryLabel': 'Available repositories',
|
||||||
|
'repositorySettings.forbidden': 'Project access denied',
|
||||||
|
'repositorySettings.forbiddenDescription':
|
||||||
|
'You no longer have permission to manage repositories for this project. Close this dialog and refresh the page.',
|
||||||
|
'repositorySettings.linking': 'Linking repository…',
|
||||||
|
'repositorySettings.saving': 'Saving repository…',
|
||||||
|
'repositorySettings.save': 'Save repository',
|
||||||
|
'repositorySettings.removing': 'Removing repository…',
|
||||||
'gitConnections.description':
|
'gitConnections.description':
|
||||||
'Connect your personal Git account to browse repositories, branches, and pull requests you can access.',
|
'Connect your personal Git account to browse repositories, branches, and pull requests you can access.',
|
||||||
'gitConnections.privacy':
|
'gitConnections.privacy':
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -63,6 +63,58 @@ class ProjectRepositorySettingsControllerTest extends TestCase
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensure repository discovery distinguishes empty provider results from provider failures.
|
||||||
|
*/
|
||||||
|
public function test_repository_settings_expose_personal_access_and_provider_states(): void
|
||||||
|
{
|
||||||
|
$user = User::factory()->create();
|
||||||
|
$project = Project::factory()->create();
|
||||||
|
$emptyGitConnection = UserGitConnection::factory()->for($user)->create([
|
||||||
|
'account_login' => 'empty-account',
|
||||||
|
'base_url' => 'https://empty.git.test',
|
||||||
|
'provider' => GitProviderKey::Gitea,
|
||||||
|
]);
|
||||||
|
UserGitConnection::factory()->for($user)->create([
|
||||||
|
'account_login' => 'broken-account',
|
||||||
|
'base_url' => 'https://broken.git.test',
|
||||||
|
'provider' => GitProviderKey::Gitea,
|
||||||
|
]);
|
||||||
|
RepositoryConnection::factory()->for($project)->create([
|
||||||
|
'base_url' => $emptyGitConnection->base_url,
|
||||||
|
'provider' => GitProviderKey::Gitea,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->syncProjectPermissions($project, $user, ['edit']);
|
||||||
|
|
||||||
|
Http::preventStrayRequests();
|
||||||
|
Http::fake([
|
||||||
|
'https://empty.git.test/api/v1/user/repos*' => Http::response([]),
|
||||||
|
'https://broken.git.test/api/v1/user/repos*' => Http::response([], 502),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->actingAs($user)
|
||||||
|
->get(route('projects.settings.repositories', $project))
|
||||||
|
->assertOk()
|
||||||
|
->assertInertia(fn (Assert $page) => $page
|
||||||
|
->component('projects/settings/Repositories')
|
||||||
|
->has('availableRepositorySources', 2)
|
||||||
|
->where('availableRepositorySources', function ($sources): bool {
|
||||||
|
$sourcesByAccount = collect($sources)->keyBy('account_login');
|
||||||
|
$emptySource = $sourcesByAccount->get('empty-account');
|
||||||
|
$brokenSource = $sourcesByAccount->get('broken-account');
|
||||||
|
|
||||||
|
return data_get($emptySource, 'provider') === GitProviderKey::Gitea->value
|
||||||
|
&& data_get($emptySource, 'provider_label') === GitProviderKey::Gitea->label()
|
||||||
|
&& data_get($emptySource, 'repositories') === []
|
||||||
|
&& data_get($emptySource, 'error') === null
|
||||||
|
&& data_get($brokenSource, 'repositories') === []
|
||||||
|
&& data_get($brokenSource, 'error') === config('app.name').' could not load repositories from this Git connection right now.';
|
||||||
|
})
|
||||||
|
->where('repositoryConnections.0.has_personal_access', true),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ensure users without edit permission cannot manage repository settings.
|
* Ensure users without edit permission cannot manage repository settings.
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user