Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion service/src/models/team.js
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,8 @@ exports.getTeams = async function(options, callback) {
}
}

let baseQuery = Team.find(conditions).sort('_id');
const sortoptions = options.sort ? JSON.parse(options.sort) : { _id: 1 };
let baseQuery = Team.find(conditions).sort(sortoptions);
if (options.populate == null || options.populate == 'true') {
baseQuery = baseQuery.populate('userIds');
}
Expand Down
51 changes: 51 additions & 0 deletions web-app/admin/src/app/admin/admin-teams/admin-teams.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterModule, Routes } from '@angular/router';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatTableModule } from '@angular/material/table';
import { MatPaginatorModule } from '@angular/material/paginator';
import { MatSortModule } from '@angular/material/sort';
import { MatDialogModule } from '@angular/material/dialog';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import { MatButtonModule } from '@angular/material/button';

import { TeamDashboardComponent } from './dashboard/team-dashboard.component';
import { CreateTeamDialogComponent } from './create-team/create-team.component';
import { TeamsService } from './teams-service';
import { CoreModule } from '../../core/core.module';

const routes: Routes = [
{
path: '',
component: TeamDashboardComponent
}
];

@NgModule({
declarations: [
TeamDashboardComponent,
CreateTeamDialogComponent
],
imports: [
CommonModule,
FormsModule,
CoreModule,
ReactiveFormsModule,
MatTableModule,
MatPaginatorModule,
MatSortModule,
MatDialogModule,
MatFormFieldModule,
MatInputModule,
MatButtonModule,
RouterModule.forChild(routes)
],
providers: [
TeamsService
],
entryComponents: [
CreateTeamDialogComponent
]
})
export class AdminTeamsModule { }
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<div class="admin-team-create">
<h1 mat-dialog-title>New Team</h1>
<div mat-dialog-content>
<div *ngIf="errorMessage" class="error-message" style="color: red; margin-bottom: 16px;">
{{ errorMessage }}
</div>
<form [formGroup]="teamForm">
<mat-form-field class="full-width">
<mat-label>Name</mat-label>
<input matInput formControlName="name" required>
<mat-error *ngIf="teamForm.get('name').hasError('required')">
Name is required
</mat-error>
</mat-form-field>
<mat-form-field class="full-width">
<mat-label>Description</mat-label>
<textarea matInput formControlName="description"></textarea>
</mat-form-field>
</form>
</div>
<div mat-dialog-actions>
<button mat-button (click)="cancel()">Cancel</button>
<button mat-button color="primary" (click)="save()" [disabled]="teamForm.invalid">Save</button>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
.full-width {
width: 100%;
}

.admin-team-create {
display: flex;
flex-direction: column;
height: 100%;
justify-content: space-around;
}

.mat-dialog-actions {
display: flex;
justify-content: flex-end;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { Component, Inject } from '@angular/core';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { TeamsService } from '../teams-service';
import { Team } from '../team';

/**
* Dialog component for creating new teams.
* Provides a form interface with validation for team name (required) and description (optional).
*/
@Component({
selector: 'mage-admin-team-create',
templateUrl: './create-team.component.html',
styleUrls: ['./create-team.component.scss']
})
export class CreateTeamDialogComponent {
teamForm: FormGroup;
errorMessage: string = '';

constructor(
public dialogRef: MatDialogRef<CreateTeamDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: { team: Partial<Team> },
private fb: FormBuilder,
private teamsService: TeamsService
) {
this.teamForm = this.fb.group({
name: [data.team.name || '', [Validators.required]],
description: [data.team.description || '']
});
}

/**
* Handles form submission for creating a new team.
* Validates the form, creates the team via the teams service, and closes the dialog on success.
*/
save(): void {
if (this.teamForm.invalid) {
this.errorMessage = 'Please fill in all required fields.';
return;
}

this.errorMessage = '';
const teamData = this.teamForm.value;
this.teamsService.createTeam(teamData).subscribe({
next: (newTeam) => {
this.dialogRef.close(newTeam);
},
error: () => {
this.errorMessage = 'Failed to create team. Please try again.';
}
});
}

/**
* Closes the dialog without saving any data or making any changes.
*/
cancel(): void {
this.dialogRef.close();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<div class="admin-nav-gap">
<div class="container-fluid">
<ol class="breadcrumb">
<li class="active"><i class="fa fa-users"></i> Teams</li>
</ol>
</div>
</div>

<div class="container bottom-gap-l">
<mage-card-navbar [title]="'Teams'" [isSearchable]="true" [searchPlaceholder]="'Search teams...'"
[actionButtons]="actionButtons" (searchTermChanged)="onSearchTermChanged($event)"
(searchCleared)="onSearchCleared()"></mage-card-navbar>

<div class="col-md-12 mat-elevation-z1 w-100">
<table mat-table [class.mat-table]="true" [dataSource]="dataSource" class="mat-table-even-columns">
<ng-container matColumnDef="name">
<th mat-header-cell *matHeaderCellDef>Name</th>
<td mat-cell *matCellDef="let team">
<div class="strong">{{ team.name }}</div>
</td>
</ng-container>

<ng-container matColumnDef="description">
<th mat-header-cell *matHeaderCellDef>Description</th>
<td mat-cell *matCellDef="let team">
<div class="muted description-cell" [title]="team.description">{{ team.description }}</div>
</td>
</ng-container>

<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;" (click)="gotoTeam(row)" class="pointer"></tr>
</table>
<mat-paginator [length]="totalTeams" [pageSize]="pageSize" [pageSizeOptions]="pageSizeOptions"
(page)="onPageChange($event)">
</mat-paginator>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
.mat-table-even-columns {
table-layout: fixed;
width: 100%;

.mat-row {
height: 3.75rem;
}

.mat-cell {
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}

.mat-cell,
.mat-header-cell {
font-size: 1em;
padding: 1rem 0.75rem;
}

.mat-header-cell {
font-weight: 600;
color: rgba(0, 0, 0, 0.87);
}

.description-cell {
display: -webkit-box;
-webkit-line-clamp: 3;
line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
white-space: normal;
line-height: 1.1em;
height: calc(3 * 1.1em);
align-content: center;
}
}

.table-container {
position: relative;
}
Loading