Refactor issue detail editing: streamline input handling and remove editing mode logic
This commit is contained in:
@@ -4,24 +4,20 @@
|
|||||||
<p>Informations de creation et de suivi de l'issue.</p>
|
<p>Informations de creation et de suivi de l'issue.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-meta">
|
<div class="header-meta">
|
||||||
@if (!isEditing) {
|
<div class="status-inline">
|
||||||
<div class="status-inline">
|
<span class="status-label">Status</span>
|
||||||
<span class="status-label">Status</span>
|
<select
|
||||||
<select
|
class="status-select"
|
||||||
class="status-select"
|
[ngModel]="issue.status"
|
||||||
[ngModel]="issue.status"
|
(ngModelChange)="updateStatus($event)"
|
||||||
(ngModelChange)="updateStatus($event)"
|
>
|
||||||
>
|
@for (status of statusOptions; track status) {
|
||||||
@for (status of statusOptions; track status) {
|
<option [value]="status">{{ status }}</option>
|
||||||
<option [value]="status">{{ status }}</option>
|
}
|
||||||
}
|
</select>
|
||||||
</select>
|
</div>
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
<div class="header-actions">
|
<div class="header-actions">
|
||||||
@if (!isEditing) {
|
|
||||||
<button type="button" class="edit-button" (click)="startEdit()">Editer l'issue</button>
|
|
||||||
<div class="more-wrapper">
|
<div class="more-wrapper">
|
||||||
<button type="button" class="more-button" (click)="toggleMoreMenu()">More ▾</button>
|
<button type="button" class="more-button" (click)="toggleMoreMenu()">More ▾</button>
|
||||||
|
|
||||||
@@ -33,7 +29,6 @@
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
@@ -48,125 +43,79 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<th>Nom</th>
|
<th>Nom</th>
|
||||||
<td>
|
<td>
|
||||||
@if (isEditing) {
|
<input type="text" [(ngModel)]="issue.name" (blur)="saveIssue()" />
|
||||||
<input type="text" [(ngModel)]="issue.name" />
|
|
||||||
} @else {
|
|
||||||
{{ issue.name || '-' }}
|
|
||||||
}
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Type</th>
|
<th>Type</th>
|
||||||
<td>
|
<td>
|
||||||
@if (isEditing) {
|
<select [(ngModel)]="issueTypeValue" (change)="saveIssue()">
|
||||||
<select [(ngModel)]="issueTypeValue">
|
@for (type of typeOptions; track type) {
|
||||||
@for (type of typeOptions; track type) {
|
<option [value]="type">{{ type }}</option>
|
||||||
<option [value]="type">{{ type }}</option>
|
}
|
||||||
}
|
</select>
|
||||||
</select>
|
|
||||||
} @else {
|
|
||||||
{{ issueTypeValue }}
|
|
||||||
}
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Epic</th>
|
<th>Epic</th>
|
||||||
<td>
|
<td>
|
||||||
@if (isEditing) {
|
<input type="text" [(ngModel)]="issue.epic" (blur)="saveIssue()" />
|
||||||
<input type="text" [(ngModel)]="issue.epic" />
|
|
||||||
} @else {
|
|
||||||
{{ issue.epic || '-' }}
|
|
||||||
}
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Depend de</th>
|
<th>Depend de</th>
|
||||||
<td>
|
<td>
|
||||||
@if (isEditing) {
|
<select multiple [(ngModel)]="dependencyIds" class="dependency-multiselect" (change)="saveIssue()">
|
||||||
<select multiple [(ngModel)]="dependencyIds" class="dependency-multiselect">
|
@for (candidate of dependencyCandidates; track candidate.id) {
|
||||||
@for (candidate of dependencyCandidates; track candidate.id) {
|
<option [ngValue]="candidate.id">
|
||||||
<option [ngValue]="candidate.id">
|
#{{ candidate.id }} - {{ candidate.name || 'Sans nom' }}
|
||||||
#{{ candidate.id }} - {{ candidate.name || 'Sans nom' }}
|
</option>
|
||||||
</option>
|
}
|
||||||
}
|
</select>
|
||||||
</select>
|
|
||||||
} @else {
|
|
||||||
{{ resolveDependencyLabels(dependencyIds) }}
|
|
||||||
}
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Assignee</th>
|
<th>Assignee</th>
|
||||||
<td>
|
<td>
|
||||||
@if (isEditing) {
|
<input type="text" [(ngModel)]="issue.assignee" (blur)="saveIssue()" />
|
||||||
<input type="text" [(ngModel)]="issue.assignee" />
|
|
||||||
} @else {
|
|
||||||
{{ issue.assignee || '-' }}
|
|
||||||
}
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Date d'echeance</th>
|
<th>Date d'echeance</th>
|
||||||
<td>
|
<td>
|
||||||
@if (isEditing) {
|
<input type="date" [(ngModel)]="issue.dueDate" (blur)="saveIssue()" />
|
||||||
<input type="date" [(ngModel)]="issue.dueDate" />
|
|
||||||
} @else {
|
|
||||||
{{ issue.dueDate || '-' }}
|
|
||||||
}
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Temps estimé</th>
|
<th>Temps estimé</th>
|
||||||
<td>
|
<td>
|
||||||
@if (isEditing) {
|
<input type="number" min="0" step="0.5" [(ngModel)]="estimatedTimeValue" (blur)="saveIssue()" />
|
||||||
<input type="number" min="0" step="0.5" [(ngModel)]="estimatedTimeValue" />
|
|
||||||
} @else {
|
|
||||||
{{ estimatedTimeValue !== null ? estimatedTimeValue + ' h' : '-' }}
|
|
||||||
}
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Description</th>
|
<th>Description</th>
|
||||||
<td>
|
<td>
|
||||||
@if (isEditing) {
|
<textarea rows="4" [(ngModel)]="issue.description" (blur)="saveIssue()"></textarea>
|
||||||
<textarea rows="4" [(ngModel)]="issue.description"></textarea>
|
|
||||||
} @else {
|
|
||||||
{{ issue.description || '-' }}
|
|
||||||
}
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Priorite</th>
|
<th>Priorite</th>
|
||||||
<td>
|
<td>
|
||||||
@if (isEditing) {
|
<select [(ngModel)]="issue.priority" (change)="saveIssue()">
|
||||||
<select [(ngModel)]="issue.priority">
|
<option value="Basse">Basse</option>
|
||||||
<option value="Basse">Basse</option>
|
<option value="Moyenne">Moyenne</option>
|
||||||
<option value="Moyenne">Moyenne</option>
|
<option value="Haute">Haute</option>
|
||||||
<option value="Haute">Haute</option>
|
</select>
|
||||||
</select>
|
|
||||||
} @else {
|
|
||||||
{{ issue.priority }}
|
|
||||||
}
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Progression</th>
|
<th>Progression</th>
|
||||||
<td>
|
<td>
|
||||||
@if (isEditing) {
|
<input type="number" min="0" max="100" [(ngModel)]="issue.progress" (blur)="saveIssue()" />
|
||||||
<input type="number" min="0" max="100" [(ngModel)]="issue.progress" />
|
|
||||||
} @else {
|
|
||||||
{{ issue.progress }}%
|
|
||||||
}
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
@if (isEditing) {
|
|
||||||
<div class="form-actions">
|
|
||||||
<button type="button" class="cancel-button" (click)="cancelEdit()">Annuler</button>
|
|
||||||
<button type="button" class="save-button" (click)="saveIssue()">Enregistrer</button>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
@@ -13,10 +13,9 @@ export class IssueDetail {
|
|||||||
private readonly route = inject(ActivatedRoute);
|
private readonly route = inject(ActivatedRoute);
|
||||||
private readonly router = inject(Router);
|
private readonly router = inject(Router);
|
||||||
private readonly issuesStore = inject(IssuesStore);
|
private readonly issuesStore = inject(IssuesStore);
|
||||||
|
private readonly isNewIssueRoute = this.route.snapshot.routeConfig?.path === 'issues/new';
|
||||||
|
|
||||||
protected issue: IssueEntity = this.buildIssue();
|
protected issue: IssueEntity = this.buildIssue();
|
||||||
protected isEditing = this.route.snapshot.queryParamMap.get('mode') === 'edit';
|
|
||||||
private issueBeforeEdit: IssueEntity | null = null;
|
|
||||||
protected readonly issues = this.issuesStore.issues;
|
protected readonly issues = this.issuesStore.issues;
|
||||||
protected moreMenuOpen = false;
|
protected moreMenuOpen = false;
|
||||||
|
|
||||||
@@ -60,30 +59,11 @@ export class IssueDetail {
|
|||||||
this.issue.type = value;
|
this.issue.type = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor() {
|
|
||||||
if (this.isEditing) {
|
|
||||||
this.issueBeforeEdit = this.cloneIssue(this.issue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected startEdit(): void {
|
|
||||||
this.issueBeforeEdit = this.cloneIssue(this.issue);
|
|
||||||
this.isEditing = true;
|
|
||||||
this.closeMoreMenu();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected cancelEdit(): void {
|
|
||||||
if (this.issueBeforeEdit) {
|
|
||||||
this.issue = this.cloneIssue(this.issueBeforeEdit);
|
|
||||||
}
|
|
||||||
this.isEditing = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected saveIssue(): void {
|
protected saveIssue(): void {
|
||||||
this.issuesStore.upsert(this.issue);
|
this.issuesStore.upsert(this.issue);
|
||||||
this.issueBeforeEdit = this.cloneIssue(this.issue);
|
if (this.isNewIssueRoute) {
|
||||||
this.isEditing = false;
|
this.router.navigate(['/issues', this.issue.id]);
|
||||||
this.router.navigate(['/issues', this.issue.id]);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected deleteIssue(): void {
|
protected deleteIssue(): void {
|
||||||
@@ -120,9 +100,6 @@ export class IssueDetail {
|
|||||||
return this.issues().filter((issue) => issue.id !== this.issue.id);
|
return this.issues().filter((issue) => issue.id !== this.issue.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
private cloneIssue(issue: IssueEntity): IssueEntity {
|
|
||||||
return { ...issue };
|
|
||||||
}
|
|
||||||
|
|
||||||
private buildIssue(): IssueEntity {
|
private buildIssue(): IssueEntity {
|
||||||
const idParam = this.route.snapshot.paramMap.get('id');
|
const idParam = this.route.snapshot.paramMap.get('id');
|
||||||
|
|||||||
Reference in New Issue
Block a user