autocomit
This commit is contained in:
+81
@@ -0,0 +1,81 @@
|
||||
<div class="dependencies-container">
|
||||
<div class="header">
|
||||
<h3>External Dependencies</h3>
|
||||
<button (click)="createNew()" class="btn-primary">Add Dependency</button>
|
||||
</div>
|
||||
|
||||
<div class="filters">
|
||||
<div class="filter-group">
|
||||
<label>Filter by Type:</label>
|
||||
<select [(ngModel)]="selectedTypeId" (ngModelChange)="onFilterChange()" class="filter-select">
|
||||
<option value="">All Types</option>
|
||||
<option *ngFor="let type of dependencyTypes" [value]="type.id">
|
||||
{{ type.typeName }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="filter-group">
|
||||
<label>Filter by Status:</label>
|
||||
<select [(ngModel)]="selectedStatus" (ngModelChange)="onFilterChange()" class="filter-select">
|
||||
<option *ngFor="let opt of statusOptions" [value]="opt.value">
|
||||
{{ opt.label }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="loading" class="loading">Loading dependencies...</div>
|
||||
<div *ngIf="error" class="error">{{ error }}</div>
|
||||
|
||||
<div *ngIf="!loading && dependencies.length > 0" class="dependencies-table">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Type</th>
|
||||
<th>Status</th>
|
||||
<th>Validity Period</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *ngFor="let dep of dependencies">
|
||||
<td><strong>{{ dep.name }}</strong></td>
|
||||
<td>{{ dep.dependencyType.typeName }}</td>
|
||||
<td>
|
||||
<span class="status-badge" [ngClass]="getStatusClass(dep.status)">
|
||||
{{ getStatusLabel(dep.status) }}
|
||||
<span *ngIf="dep.daysUntilExpiration !== null && dep.daysUntilExpiration !== undefined">
|
||||
({{ dep.daysUntilExpiration }} days)
|
||||
</span>
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<div *ngIf="dep.validityStartDate || dep.validityEndDate">
|
||||
{{ dep.validityStartDate ? (dep.validityStartDate | date:'mediumDate') : '-' }}
|
||||
→
|
||||
{{ dep.validityEndDate ? (dep.validityEndDate | date:'mediumDate') : 'Indefinite' }}
|
||||
</div>
|
||||
<div *ngIf="!dep.validityStartDate && !dep.validityEndDate">-</div>
|
||||
</td>
|
||||
<td class="actions">
|
||||
<button (click)="viewDetails(dep.id)" class="btn-sm">View</button>
|
||||
<button (click)="edit(dep.id)" class="btn-sm">Edit</button>
|
||||
<button (click)="delete(dep.id)" class="btn-sm btn-danger">Delete</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div *ngIf="!loading && dependencies.length === 0" class="empty">
|
||||
No external dependencies found for this application.
|
||||
</div>
|
||||
|
||||
<div *ngIf="totalPages > 1" class="pagination">
|
||||
<button (click)="previousPage()" [disabled]="page === 0">Previous</button>
|
||||
<span>Page {{ page + 1 }} of {{ totalPages }}</span>
|
||||
<button (click)="nextPage()" [disabled]="page >= totalPages - 1">Next</button>
|
||||
</div>
|
||||
</div>
|
||||
+181
@@ -0,0 +1,181 @@
|
||||
.dependencies-container {
|
||||
.header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 1.5rem;
|
||||
|
||||
h3 {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background-color: #3f51b5;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 0.75rem 1.5rem;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background-color: #303f9f;
|
||||
}
|
||||
}
|
||||
|
||||
.filters {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
margin-bottom: 1.5rem;
|
||||
padding: 1rem;
|
||||
background: #f9f9f9;
|
||||
border-radius: 8px;
|
||||
|
||||
.filter-group {
|
||||
flex: 1;
|
||||
min-width: 200px;
|
||||
|
||||
label {
|
||||
display: block;
|
||||
margin-bottom: 0.5rem;
|
||||
font-weight: 500;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.filter-select {
|
||||
width: 100%;
|
||||
padding: 0.5rem;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
font-size: 0.95rem;
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
border-color: #3f51b5;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.loading, .error, .empty {
|
||||
text-align: center;
|
||||
padding: 2rem;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.error {
|
||||
color: #f44336;
|
||||
}
|
||||
|
||||
.dependencies-table {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
|
||||
th, td {
|
||||
padding: 1rem;
|
||||
text-align: left;
|
||||
border-bottom: 1px solid #f5f5f5;
|
||||
}
|
||||
|
||||
th {
|
||||
background-color: #f9f9f9;
|
||||
font-weight: 600;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
tbody tr:hover {
|
||||
background-color: #fafafa;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.status-badge {
|
||||
padding: 0.25rem 0.75rem;
|
||||
border-radius: 12px;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
display: inline-block;
|
||||
|
||||
&.status-active {
|
||||
background-color: #e8f5e9;
|
||||
color: #388e3c;
|
||||
}
|
||||
|
||||
&.status-expiring {
|
||||
background-color: #fff3e0;
|
||||
color: #f57c00;
|
||||
}
|
||||
|
||||
&.status-expired {
|
||||
background-color: #ffebee;
|
||||
color: #c62828;
|
||||
}
|
||||
|
||||
&.status-not-valid {
|
||||
background-color: #f5f5f5;
|
||||
color: #616161;
|
||||
}
|
||||
}
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.btn-sm {
|
||||
padding: 0.5rem 1rem;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
background-color: #2196f3;
|
||||
color: white;
|
||||
font-size: 0.875rem;
|
||||
|
||||
&:hover {
|
||||
background-color: #1976d2;
|
||||
}
|
||||
|
||||
&.btn-danger {
|
||||
background-color: #f44336;
|
||||
|
||||
&:hover {
|
||||
background-color: #d32f2f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.pagination {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
margin-top: 1.5rem;
|
||||
|
||||
button {
|
||||
padding: 0.5rem 1rem;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
background: white;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover:not(:disabled) {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
|
||||
span {
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
+152
@@ -0,0 +1,152 @@
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { Router } from '@angular/router';
|
||||
import { DependencyService } from '../../dependencies/dependency.service';
|
||||
import { ExternalDependency, DependencyType } from '../../../shared/models/dependency.model';
|
||||
import { Page } from '../../../shared/models/environment.model';
|
||||
|
||||
@Component({
|
||||
selector: 'app-application-dependencies',
|
||||
standalone: true,
|
||||
imports: [CommonModule, FormsModule],
|
||||
templateUrl: './application-dependencies.component.html',
|
||||
styleUrls: ['./application-dependencies.component.scss']
|
||||
})
|
||||
export class ApplicationDependenciesComponent implements OnInit {
|
||||
@Input() applicationId!: string;
|
||||
@Input() applicationName!: string;
|
||||
|
||||
dependencies: ExternalDependency[] = [];
|
||||
dependencyTypes: DependencyType[] = [];
|
||||
loading = false;
|
||||
error = '';
|
||||
|
||||
page = 0;
|
||||
size = 10;
|
||||
totalPages = 0;
|
||||
|
||||
selectedTypeId = '';
|
||||
selectedStatus = '';
|
||||
|
||||
statusOptions = [
|
||||
{ value: '', label: 'All Statuses' },
|
||||
{ value: 'ACTIVE', label: 'Active' },
|
||||
{ value: 'EXPIRING', label: 'Expiring Soon' },
|
||||
{ value: 'EXPIRED', label: 'Expired' },
|
||||
{ value: 'NOT_YET_VALID', label: 'Not Yet Valid' }
|
||||
];
|
||||
|
||||
constructor(
|
||||
private dependencyService: DependencyService,
|
||||
private router: Router
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
if (this.applicationId) {
|
||||
this.loadDependencyTypes();
|
||||
this.loadDependencies();
|
||||
}
|
||||
}
|
||||
|
||||
loadDependencyTypes(): void {
|
||||
this.dependencyService.getDependencyTypes().subscribe({
|
||||
next: (types) => {
|
||||
this.dependencyTypes = types;
|
||||
},
|
||||
error: (err) => {
|
||||
console.error('Failed to load dependency types', err);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
loadDependencies(): void {
|
||||
this.loading = true;
|
||||
const filters: any = { applicationId: this.applicationId };
|
||||
|
||||
if (this.selectedTypeId) {
|
||||
filters.dependencyTypeId = this.selectedTypeId;
|
||||
}
|
||||
if (this.selectedStatus) {
|
||||
filters.status = this.selectedStatus;
|
||||
}
|
||||
|
||||
this.dependencyService.getDependencies(filters, this.page, this.size).subscribe({
|
||||
next: (data: Page<ExternalDependency>) => {
|
||||
this.dependencies = data.content;
|
||||
this.totalPages = data.totalPages;
|
||||
this.loading = false;
|
||||
},
|
||||
error: (err) => {
|
||||
this.error = 'Failed to load dependencies';
|
||||
this.loading = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onFilterChange(): void {
|
||||
this.page = 0;
|
||||
this.loadDependencies();
|
||||
}
|
||||
|
||||
createNew(): void {
|
||||
this.router.navigate(['/dependencies/new'], {
|
||||
queryParams: { applicationId: this.applicationId }
|
||||
});
|
||||
}
|
||||
|
||||
viewDetails(id: string): void {
|
||||
this.router.navigate(['/dependencies', id]);
|
||||
}
|
||||
|
||||
edit(id: string): void {
|
||||
this.router.navigate(['/dependencies', id, 'edit']);
|
||||
}
|
||||
|
||||
delete(id: string): void {
|
||||
if (confirm('Are you sure you want to delete this dependency?')) {
|
||||
this.dependencyService.deleteDependency(id).subscribe({
|
||||
next: () => {
|
||||
this.loadDependencies();
|
||||
},
|
||||
error: (err) => {
|
||||
this.error = 'Failed to delete dependency';
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
getStatusClass(status: string): string {
|
||||
const classes: Record<string, string> = {
|
||||
'ACTIVE': 'status-active',
|
||||
'EXPIRING': 'status-expiring',
|
||||
'EXPIRED': 'status-expired',
|
||||
'NOT_YET_VALID': 'status-not-valid'
|
||||
};
|
||||
return classes[status] || '';
|
||||
}
|
||||
|
||||
getStatusLabel(status: string): string {
|
||||
const labels: Record<string, string> = {
|
||||
'ACTIVE': 'Active',
|
||||
'EXPIRING': 'Expiring Soon',
|
||||
'EXPIRED': 'Expired',
|
||||
'NOT_YET_VALID': 'Not Yet Valid'
|
||||
};
|
||||
return labels[status] || status;
|
||||
}
|
||||
|
||||
nextPage(): void {
|
||||
if (this.page < this.totalPages - 1) {
|
||||
this.page++;
|
||||
this.loadDependencies();
|
||||
}
|
||||
}
|
||||
|
||||
previousPage(): void {
|
||||
if (this.page > 0) {
|
||||
this.page--;
|
||||
this.loadDependencies();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { HttpClient, HttpParams } from '@angular/common/http';
|
||||
import { Observable } from 'rxjs';
|
||||
import {
|
||||
ExternalDependency,
|
||||
CreateExternalDependencyRequest,
|
||||
UpdateExternalDependencyRequest,
|
||||
DependencyType,
|
||||
CreateDependencyTypeRequest,
|
||||
UpdateDependencyTypeRequest
|
||||
} from '../../shared/models/dependency.model';
|
||||
import { Page } from '../../shared/models/environment.model';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class DependencyService {
|
||||
private readonly API_URL = '/api/dependencies';
|
||||
private readonly TYPE_API_URL = '/api/dependency-types';
|
||||
|
||||
constructor(private http: HttpClient) {}
|
||||
|
||||
// Dependency Types
|
||||
getDependencyTypes(): Observable<DependencyType[]> {
|
||||
return this.http.get<DependencyType[]>(this.TYPE_API_URL);
|
||||
}
|
||||
|
||||
getDependencyType(id: string): Observable<DependencyType> {
|
||||
return this.http.get<DependencyType>(`${this.TYPE_API_URL}/${id}`);
|
||||
}
|
||||
|
||||
createDependencyType(data: CreateDependencyTypeRequest): Observable<DependencyType> {
|
||||
return this.http.post<DependencyType>(this.TYPE_API_URL, data);
|
||||
}
|
||||
|
||||
updateDependencyType(id: string, data: UpdateDependencyTypeRequest): Observable<DependencyType> {
|
||||
return this.http.put<DependencyType>(`${this.TYPE_API_URL}/${id}`, data);
|
||||
}
|
||||
|
||||
deleteDependencyType(id: string): Observable<void> {
|
||||
return this.http.delete<void>(`${this.TYPE_API_URL}/${id}`);
|
||||
}
|
||||
|
||||
// External Dependencies
|
||||
getDependencies(
|
||||
filters?: {
|
||||
applicationId?: string;
|
||||
dependencyTypeId?: string;
|
||||
status?: string;
|
||||
},
|
||||
page: number = 0,
|
||||
size: number = 20,
|
||||
sortBy: string = 'name',
|
||||
sortDirection: string = 'asc'
|
||||
): Observable<Page<ExternalDependency>> {
|
||||
let params = new HttpParams()
|
||||
.set('page', page.toString())
|
||||
.set('size', size.toString())
|
||||
.set('sortBy', sortBy)
|
||||
.set('sortDirection', sortDirection);
|
||||
|
||||
if (filters?.applicationId) {
|
||||
params = params.set('applicationId', filters.applicationId);
|
||||
}
|
||||
if (filters?.dependencyTypeId) {
|
||||
params = params.set('dependencyTypeId', filters.dependencyTypeId);
|
||||
}
|
||||
if (filters?.status) {
|
||||
params = params.set('status', filters.status);
|
||||
}
|
||||
|
||||
return this.http.get<Page<ExternalDependency>>(this.API_URL, { params });
|
||||
}
|
||||
|
||||
getDependency(id: string): Observable<ExternalDependency> {
|
||||
return this.http.get<ExternalDependency>(`${this.API_URL}/${id}`);
|
||||
}
|
||||
|
||||
getDependenciesByApplication(
|
||||
applicationId: string,
|
||||
page: number = 0,
|
||||
size: number = 20
|
||||
): Observable<Page<ExternalDependency>> {
|
||||
const params = new HttpParams()
|
||||
.set('page', page.toString())
|
||||
.set('size', size.toString());
|
||||
|
||||
return this.http.get<Page<ExternalDependency>>(
|
||||
`${this.API_URL}/by-application/${applicationId}`,
|
||||
{ params }
|
||||
);
|
||||
}
|
||||
|
||||
createDependency(
|
||||
applicationId: string,
|
||||
data: CreateExternalDependencyRequest
|
||||
): Observable<ExternalDependency> {
|
||||
return this.http.post<ExternalDependency>(
|
||||
`${this.API_URL}/for-application/${applicationId}`,
|
||||
data
|
||||
);
|
||||
}
|
||||
|
||||
updateDependency(
|
||||
id: string,
|
||||
data: UpdateExternalDependencyRequest
|
||||
): Observable<ExternalDependency> {
|
||||
return this.http.put<ExternalDependency>(`${this.API_URL}/${id}`, data);
|
||||
}
|
||||
|
||||
deleteDependency(id: string): Observable<void> {
|
||||
return this.http.delete<void>(`${this.API_URL}/${id}`);
|
||||
}
|
||||
|
||||
getExpiringDependencies(days: number = 30): Observable<ExternalDependency[]> {
|
||||
const params = new HttpParams().set('days', days.toString());
|
||||
return this.http.get<ExternalDependency[]>(`${this.API_URL}/expiring`, { params });
|
||||
}
|
||||
|
||||
getExpiredDependencies(): Observable<ExternalDependency[]> {
|
||||
return this.http.get<ExternalDependency[]>(`${this.API_URL}/expired`);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
export interface DependencyType {
|
||||
id: string;
|
||||
typeName: string;
|
||||
description?: string;
|
||||
isCustom: boolean;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
export interface ExternalDependency {
|
||||
id: string;
|
||||
application: {
|
||||
id: string;
|
||||
name: string;
|
||||
};
|
||||
dependencyType: DependencyType;
|
||||
name: string;
|
||||
description?: string;
|
||||
technicalDocumentation?: string;
|
||||
validityStartDate?: Date;
|
||||
validityEndDate?: Date;
|
||||
isActive: boolean;
|
||||
daysUntilExpiration?: number;
|
||||
status: 'ACTIVE' | 'EXPIRING' | 'EXPIRED' | 'NOT_YET_VALID';
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
export interface CreateDependencyTypeRequest {
|
||||
typeName: string;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
export interface UpdateDependencyTypeRequest {
|
||||
typeName?: string;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
export interface CreateExternalDependencyRequest {
|
||||
dependencyTypeId: string;
|
||||
name: string;
|
||||
description?: string;
|
||||
technicalDocumentation?: string;
|
||||
validityStartDate?: Date;
|
||||
validityEndDate?: Date;
|
||||
}
|
||||
|
||||
export interface UpdateExternalDependencyRequest {
|
||||
dependencyTypeId?: string;
|
||||
name?: string;
|
||||
description?: string;
|
||||
technicalDocumentation?: string;
|
||||
validityStartDate?: Date;
|
||||
validityEndDate?: Date;
|
||||
}
|
||||
Reference in New Issue
Block a user