Skip to content

Commit

Permalink
feat: Locking and Deployment information changes (#276)
Browse files Browse the repository at this point in the history
  • Loading branch information
TurkerKoc authored Feb 3, 2025
1 parent 0f549e7 commit 26ca4f2
Show file tree
Hide file tree
Showing 51 changed files with 1,186 additions and 535 deletions.
1 change: 1 addition & 0 deletions .vscode/extensions.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@
"josevseb.google-java-format-for-vs-code",
"codacy-app.codacy",
"cweijan.dbclient-jdbc",
"cweijan.vscode-postgresql-client2",
]
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
<div class="flex-col items-start mb-2">
<span class="text-xs uppercase tracking-tighter font-bold text-gray-500">Latest deployment</span>
<span class="text-xs uppercase tracking-tighter font-bold text-gray-500">Latest Commit</span>
@if (commit(); as commit) {
<div class="flex gap-4 items-center mb-4 mt-2">
<span class="font-semibold text-lg">{{ commit.message }}</span>
<div class="flex-grow"></div>
<div class="flex gap-1 items-center">
<span>{{ deployment().ref }}</span>
<i-tabler name="git-branch" class="w-6 h-6 text-gray-500" />
</div>
</div>
<div class="flex gap-2 items-center text-sm">
<div class="flex gap-1 items-center">
Expand All @@ -26,13 +22,6 @@
<span [pTooltip]="deployment().sha">{{ deployment().sha | slice: 0 : 8 }}</span>
</div>
<div class="flex flex-grow"></div>
<div>
<div class="flex gap-2">
@for (installedApp of installedApps(); track installedApp) {
<p-tag class="bg-gray-300 gap-0">{{ installedApp }}</p-tag>
}
</div>
</div>
</div>
} @else {
<div class="font-semibold text-lg">Commit Information Could Not Be Loaded</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ export class EnvironmentDeploymentInfoComponent {

repositoryId = input.required<number>();
deployment = input.required<EnvironmentDeployment>();
installedApps = input.required<string[]>();

commitQuery = injectQuery(() => ({
...getCommitByRepositoryIdAndNameOptions({ path: { repoId: this.repositoryId(), sha: this.deployment()?.sha || '' } }),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,72 +1,70 @@
@switch (state()) {
@case ('SUCCESS') {
<!-- Success State -->
<p-tag severity="success" [rounded]="true">
<i-tabler name="check" class="!h-4 !w-4 mr-0.5"></i-tabler>
Latest deployment successful
<p-tag severity="success" [rounded]="true" [pTooltip]="'Latest Deployment Successful'">
<i-tabler name="check" class="!size-5"></i-tabler>
</p-tag>
}

@case ('WAITING') {
<!-- In Progress State -->
<p-tag severity="warn" [rounded]="true" [pTooltip]="'Waiting deployment'">
<i-tabler name="progress" class="!size-5 animate-spin"></i-tabler>
</p-tag>
}

@case ('PENDING') {
<!-- Pending State -->
<p-tag severity="info" [rounded]="true">
<i-tabler name="progress" class="!h-4 !w-4 mr-0.5"></i-tabler>
Deployment pending
<p-tag severity="warn" [rounded]="true" [pTooltip]="'Deployment pending'">
<i-tabler name="progress" class="!size-5 animate-spin"></i-tabler>
</p-tag>
}

@case ('IN_PROGRESS') {
<!-- In Progress State -->
<p-tag severity="info" [rounded]="true">
<i-tabler name="progress" class="!h-4 !w-4 mr-0.5 animate-spin"></i-tabler>
Deployment in progress
<p-tag severity="info" [rounded]="true" [pTooltip]="'Deployment in progress'">
<i-tabler name="progress" class="!size-5 animate-spin"></i-tabler>
</p-tag>
}

@case ('QUEUED') {
<!-- Queued State -->
<p-tag severity="warn" [rounded]="true">
<i-tabler name="progress" class="!h-4 !w-4 mr-0.5"></i-tabler>
Deployment queued
<p-tag severity="info" [rounded]="true" [pTooltip]="'Deployment queued'">
<i-tabler name="progress" class="!size-5 animate-spin"></i-tabler>
</p-tag>
}

@case ('ERROR') {
<!-- Error State -->
<p-tag severity="danger" [rounded]="true">
<i-tabler name="exclamation-circle" class="!h-4 !w-4 mr-0.5"></i-tabler>
Latest deployment failed
<p-tag severity="danger" [rounded]="true" [pTooltip]="'Latest deployment failed'">
<i-tabler name="exclamation-circle" class="!size-5"></i-tabler>
</p-tag>
}

@case ('FAILURE') {
<!-- Failure State -->
<p-tag severity="danger" [rounded]="true">
<i-tabler name="exclamation-mark" class="!h-4 !w-4 mr-0.5"></i-tabler>
Latest deployment failed
<p-tag severity="danger" [rounded]="true" [pTooltip]="'Latest deployment failed'">
<i-tabler name="exclamation-mark" class="!size-5"></i-tabler>
</p-tag>
}

@case ('INACTIVE') {
<!-- Inactive State -->
<p-tag severity="secondary" [rounded]="true">
<i-tabler name="time-duration-off" class="!h-4 !w-4 mr-0.5"></i-tabler>
Deployment inactive
<p-tag severity="secondary" [rounded]="true" [pTooltip]="'Deployment inactive'">
<i-tabler name="time-duration-off" class="!size-5"></i-tabler>
</p-tag>
}

@case ('UNKNOWN') {
<!-- Unknown State -->
<p-tag severity="secondary" [rounded]="true">
<i-tabler name="question-mark" class="!h-4 !w-4 mr-0.5"></i-tabler>
Deployment state unknown
<p-tag severity="secondary" [rounded]="true" [pTooltip]="'Deployment state unknown'">
<i-tabler name="question-mark" class="!size-5"></i-tabler>
</p-tag>
}

@default {
<p-tag severity="secondary" [rounded]="true">
<i-tabler name="question-mark" class="!h-4 !w-4 mr-0.5"></i-tabler>
Unrecognized State
<p-tag severity="secondary" [rounded]="true" [pTooltip]="'Unrecognized State'">
<i-tabler name="question-mark" class="!size-5"></i-tabler>
</p-tag>
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { Component, input } from '@angular/core';
import { IconsModule } from 'icons.module';
import { TagModule } from 'primeng/tag';
import { TooltipModule } from 'primeng/tooltip';

@Component({
selector: 'app-deployment-state-tag',
imports: [TagModule, IconsModule],
imports: [TagModule, IconsModule, TooltipModule],
providers: [],
templateUrl: './deployment-state-tag.component.html',
})
export class DeploymentStateTagComponent {
state = input.required<string>();
state = input.required<string | undefined>();
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<div class="flex items-center justify-between mb-3">
<input pInputText id="commit-hash" (input)="onSearch($event)" [value]="searchInput()" type="text" placeholder="Search for installed systems" class="w-1/3" />
@if (!hideLinkToList()) {
<p-button [routerLink]="'../../../environment'" class="self-end">{{ permissionService.isAdmin() ? 'Manage Environments' : 'Go to Environments' }}</p-button>
<p-button [routerLink]="'../../../environment'" class="self-end">{{ isAdmin() ? 'Manage Environments' : 'Go to Environments' }}</p-button>
}
</div>

Expand All @@ -18,29 +18,48 @@
<p-accordion-panel [value]="environment.id">
<p-accordion-header>
<div class="flex gap-2 items-center w-full">
<span>{{ environment.name }}</span>
<div class="flex flex-col gap-1">
<div class="flex gap-1 items-center mr-3">
<span [pTooltip]="'Open Environment'" class="cursor-pointer hover:bg-gray-200 px-2 py-1 rounded" (click)="openExternalLink($event, environment)">
{{ environment.name }}
</span>

<app-lock-tag [isLocked]="!!environment.locked"></app-lock-tag>
<app-lock-tag [isLocked]="!!environment.locked"></app-lock-tag>

@if (environment.latestDeployment; as deployment) {
<app-deployment-state-tag [state]="deployment.state || 'IN_PROGRESS'" />
}

@if (environment.latestStatus; as status) {
<app-environment-status-tag [status]="status" />
}
@if (environment.latestStatus; as status) {
<app-environment-status-tag [status]="status" />
}
</div>
@if (environment.latestDeployment; as deployment) {
<div class="flex gap-1 items-center text-sm mt-2">
<app-user-avatar [user]="environment?.latestDeployment?.user" />
@if (environment.latestDeployment.user?.name) {
{{ environment.latestDeployment.user?.name }} deployed
}
@if (environment.latestDeployment.updatedAt) {
<span [pTooltip]="getDeploymentTime(environment) || ''">{{ environment.latestDeployment.updatedAt || '' | timeAgo }}</span>
}
<app-deployment-state-tag [state]="deployment.state" />
<p-tag severity="secondary" rounded="true" class="max-w-[350px] flex items-center" [pTooltip]="tooltipTemplate" tooltipPosition="top">
<i-tabler name="git-branch" class="!size-5 mr-0.5 flex-shrink-0"></i-tabler>
<span class="ml-1 truncate flex-1">{{ deployment.ref }}</span>
<ng-template #tooltipTemplate>
<div class="flex items-center">{{ deployment.ref }}</div>
</ng-template>
</p-tag>
</div>
}
</div>

<div class="flex-grow"></div>

@if (environment.locked) {
<div class="flex gap-1 items-center">
<app-user-avatar [user]="environment.lockedBy" />
</div>
@if (environment.lockedAt) {
<app-lock-time [lockedAt]="environment.lockedAt"></app-lock-time>
}
@if (environment.serverUrl) {
<a [href]="getFullUrl(environment.serverUrl)" target="_blank" class="p-button p-button-secondary p-2" (click)="$event.stopPropagation()"
><i-tabler name="external-link" class="mr-1" />Open</a
>
}
@if ((isLoggedIn() && isCurrentUserLocked(environment)) || hasUnlockPermissions()) {
<button (click)="onUnlockEnvironment($event, environment)" class="p-button p-button-danger p-2"><i-tabler name="lock-open" class="mr-1" />Unlock</button>
}
Expand All @@ -65,22 +84,22 @@
</div>
</p-accordion-header>
<p-accordion-content>
<div class="flex justify-between max-w-6xl">
@if (environment.latestDeployment; as deployment) {
<app-environment-deployment-info
class="flex-grow max-w-2xl"
[deployment]="deployment"
[repositoryId]="environment.repository?.id || 0"
[installedApps]="environment.installedApps || []"
/>
}

@if (environment.latestStatus; as status) {
<app-environment-status-info class="flex-grow max-w-xs mt-2 ml-14" [status]="status" />
}
<div class="flex w-full">
<div class="flex flex-col gap-1">
@if (environment.latestDeployment; as deployment) {
<app-environment-deployment-info class="max-w-2xl" [deployment]="deployment" [repositoryId]="environment.repository?.id || 0" />
}
</div>
<div class="flex-grow"></div>
<div class="flex flex-col gap-1">
@if (environment.latestStatus; as status) {
<app-environment-status-info class="max-w-xs mt-2 ml-14" [status]="status" />
}
</div>
<span class="w-2"></span>
</div>

<div class="flex gap-4 items-center justify-between">
<div class="flex gap-4 mt-3 items-center justify-between">
<div class="flex gap-1 mt-2 items-center">
<a
icon
Expand All @@ -89,6 +108,12 @@
><i-tabler class="mr-1" name="history" />View Activity History</a
>
</div>
<div class="flex flex-grow"></div>
<div class="flex gap-2">
@for (installedApp of environment.installedApps; track installedApp) {
<p-tag class="bg-gray-300 gap-0">{{ installedApp }}</p-tag>
}
</div>
</div>
</p-accordion-content>
</p-accordion-panel>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Component, computed, inject, input, output, signal } from '@angular/core';
import { AccordionModule } from 'primeng/accordion';

import { CommonModule } from '@angular/common';
import { DatePipe, CommonModule } from '@angular/common';
import { RouterLink } from '@angular/router';
import { EnvironmentDto } from '@app/core/modules/openapi';
import {
Expand All @@ -25,6 +25,10 @@ import { DeploymentStateTagComponent } from '../deployment-state-tag/deployment-
import { LockTagComponent } from '../lock-tag/lock-tag.component';
import { LockTimeComponent } from '../lock-time/lock-time.component';
import { KeycloakService } from '@app/core/services/keycloak/keycloak.service';
import { AvatarModule } from 'primeng/avatar';
import { TooltipModule } from 'primeng/tooltip';
import { TimeAgoPipe } from '@app/pipes/time-ago.pipe';
import { UserAvatarComponent } from '@app/components/user-avatar/user-avatar.component';
import { EnvironmentStatusInfoComponent } from '../environment-status-info/environment-status-info.component';
import { EnvironmentStatusTagComponent } from '../environment-status-tag/environment-status-tag.component';

Expand All @@ -38,27 +42,34 @@ import { EnvironmentStatusTagComponent } from '../environment-status-tag/environ
TagModule,
IconsModule,
ButtonModule,
TooltipModule,
DeploymentStateTagComponent,
EnvironmentStatusTagComponent,
EnvironmentDeploymentInfoComponent,
EnvironmentStatusInfoComponent,
LockTimeComponent,
AvatarModule,
ConfirmDialogModule,
CommonModule,
TimeAgoPipe,
UserAvatarComponent,
],
providers: [DatePipe],
templateUrl: './environment-list-view.component.html',
})
export class EnvironmentListViewComponent {
private queryClient = inject(QueryClient);
private keycloakService = inject(KeycloakService);
private confirmationService = inject(ConfirmationService);
permissionService = inject(PermissionService);
private keycloakService = inject(KeycloakService);
private datePipe = inject(DatePipe);
private permissionService = inject(PermissionService);

editable = input<boolean | undefined>();
deployable = input<boolean | undefined>();
hideLinkToList = input<boolean | undefined>();

isLoggedIn = computed(() => this.keycloakService.isLoggedIn());
isAdmin = computed(() => this.permissionService.isAdmin());
hasUnlockPermissions = computed(() => this.permissionService.isAtLeastMaintainer());
hasDeployPermissions = computed(() => this.permissionService.hasWritePermission());
hasEditEnvironmentPermissions = computed(() => this.permissionService.isAdmin());
Expand All @@ -68,7 +79,10 @@ export class EnvironmentListViewComponent {
searchInput = signal<string>('');

canViewAllEnvironments = computed(() => this.isLoggedIn() && this.editable() && this.hasEditEnvironmentPermissions());
queryFunction = computed(() => (this.canViewAllEnvironments() ? getAllEnvironmentsOptions() : getAllEnabledEnvironmentsOptions()));
queryFunction = computed(() => {
const options = this.canViewAllEnvironments() ? getAllEnvironmentsOptions() : getAllEnabledEnvironmentsOptions();
return { ...options, refetchInterval: 3000 };
});
queryKey = computed(() => (this.canViewAllEnvironments() ? getAllEnvironmentsQueryKey() : getAllEnabledEnvironmentsQueryKey()));

environmentQuery = injectQuery(() => this.queryFunction());
Expand All @@ -83,7 +97,9 @@ export class EnvironmentListViewComponent {
}));

isCurrentUserLocked = (environment: EnvironmentDto) => {
return environment.lockedBy === this.keycloakService.getPreferredUsername();
const currentUserGithubId = Number(this.keycloakService.getUserGithubId());
const environmentLockedById = Number(environment.lockedBy?.id);
return environmentLockedById === currentUserGithubId;
};

onUnlockEnvironment(event: Event, environment: EnvironmentDto) {
Expand Down Expand Up @@ -125,4 +141,19 @@ export class EnvironmentListViewComponent {
}
return url;
}

openExternalLink(event: MouseEvent, environment: EnvironmentDto): void {
// Prevent the click event from propagating
event.stopPropagation();

// Only proceed if the server URL is available
if (environment.serverUrl) {
window.open(this.getFullUrl(environment.serverUrl), '_blank');
}
}

getDeploymentTime(environment: EnvironmentDto) {
const date = environment.latestDeployment?.updatedAt;
return date ? this.datePipe.transform(date, 'd MMMM y, h:mm a') : null; // Format date
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@
@if (status.success) {
<p-tag severity="success" [rounded]="true">
<i-tabler name="check" class="!h-4 !w-4 mr-0.5"></i-tabler>
Status check successful
Server Status
</p-tag>
} @else {
<!-- Error State -->
<p-tag severity="danger" [rounded]="true">
<i-tabler name="exclamation-circle" class="!h-4 !w-4 mr-0.5"></i-tabler>
Status check failed
Server Status
</p-tag>
}
}
Loading

0 comments on commit 26ca4f2

Please sign in to comment.