8000 feat(ui): see icons and descriptions on Bookmarks page (#3760) (#3765) · ovh/cds@50a3fa7 · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content {"props":{"docsUrl":"https://docs.github.com/get-started/accessibility/keyboard-shortcuts"}}

Commit 50a3fa7

Browse files
authored
feat(ui): see icons and descriptions on Bookmarks page (#3760) (#3765)
Signed-off-by: Benjamin Coenen <benjamin.coenen@corp.ovh.com> close #3760
1 parent 3d0f311 commit 50a3fa7

File tree

10 files changed

+155
-31
lines changed

10 files changed

+155
-31
lines changed

engine/api/api_routes.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,9 @@ func (api *API) InitRouter() {
107107
r.Handle("/import/{permProjectKey}/{uuid}", r.GET(api.getImportAsCodeHandler))
108108
r.Handle("/import/{permProjectKey}/{uuid}/perform", r.POST(api.postPerformImportAsCodeHandler))
109109

110+
// Bookmarks
111+
r.Handle("/bookmarks", r.GET(api.getBookmarksHandler))
112+
110113
// Project
111114
r.Handle("/project", r.GET(api.getProjectsHandler, AllowProvider(true), EnableTracing()), r.POST(api.addProjectHandler))
112115
r.Handle("/project/{permProjectKey}", r.GET(api.getProjectHandler), r.PUT(api.updateProjectHandler), r.DELETE(api.deleteProjectHandler))

engine/api/bookmark.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package api
2+
3+
import (
4+
"context"
5+
"net/http"
6+
7+
"github.com/ovh/cds/engine/api/bookmark"
8+
"github.com/ovh/cds/engine/service"
9+
)
10+
11+
func (api *API) getBookmarksHandler() service.Handler {
12+
return func(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
13+
data, err := bookmark.LoadAll(api.mustDB(), getUser(ctx))
14+
if err != nil {
15+
return err
16+
}
17+
return service.WriteJSON(w, data, http.StatusOK)
18+
}
19+
}

engine/api/bookmark/bookmark.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package bookmark
2+
3+
import (
4+
"database/sql"
5+
6+
"github.com/go-gorp/gorp"
7+
8+
"github.com/ovh/cds/sdk"
9+
)
10+
11+
// LoadAll returns all bookmarks with icons and their description
12+
func LoadAll(db gorp.SqlExecutor, u *sdk.User) ([]sdk.Bookmark, error) {
13+
var data []sdk.Bookmark
14+
query := `
15+
(
16+
SELECT DISTINCT
17+
project.projectkey AS key, project.name AS project_name, project.description, '' AS workflow_name, project.description, project.icon,
18+
true AS favorite,
19+
'project' AS type
20+
FROM project
21+
JOIN project_favorite ON project.id = project_favorite.project_id AND project_favorite.user_id = $1
22+
ORDER BY project.name
23+
)
24+
UNION
25+
(
26+
SELECT DISTINCT
27+
project.projectkey AS key, project.name AS project_name, workflow.description, workflow.name AS workflow_name, workflow.description, workflow.icon,
28+
true AS favorite,
29+
'workflow' AS type
30+
FROM project
31+
JOIN workflow ON workflow.project_id = project.id
32+
JOIN workflow_favorite ON workflow.id = workflow_favorite.workflow_id AND workflow_favorite.user_id = $1
33+
ORDER BY project.name
34+
)
35+
`
36+
if u == nil {
37+
u = &sdk.User{}
38+
}
39+
40+
if _, err := db.Select(&data, query, u.ID); err != nil {
41+
if err == sql.ErrNoRows {
42+
return nil, nil
43+
}
44+
return nil, sdk.WrapError(err, "cannot load bookmarks as admin")
45+
}
46+
47+
return data, nil
48+
}

sdk/bookmark.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package sdk
2+
3+
// Bookmark represents the type for a bookmark with his icon and description
4+
type Bookmark struct {
5+
Icon string `json:"icon" db:"icon"`
6+
Description string `json:"description" db:"description"`
7+
NavbarProjectData
8+
}

ui/src/app/model/bookmark.model.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
export class Bookmark {
2+
key: string;
3+
name: string;
4+
description: string;
5+
workflow_name: string;
6+
type: string;
7+
favorite: boolean;
8+
icon: string;
9+
}

ui/src/app/service/user/user.service.ts

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11

2-
import {HttpClient, HttpHeaders} from '@angular/common/http';
3-
import {Injectable} from '@angular/core';
4-
import {Observable} from 'rxjs';
5-
import {map} from 'rxjs/operators';
6-
import {Groups} from '../../model/group.model';
7-
import {Token} from '../../model/token.model';
8-
import {User} from '../../model/user.model';
9-
import {AuthentificationStore} from '../auth/authentification.store';
2+
import { HttpClient, HttpHeaders } from '@angular/common/http';
3+
import { Injectable } from '@angular/core';
4+
import { Observable } from 'rxjs';
5+
import { map } from 'rxjs/operators';
6+
import { Bookmark } from '../../model/bookmark.model';
7+
import { Groups } from '../../model/group.model';
8+
import { Token } from '../../model/token.model';
9+
import { User } from '../../model/user.model';
10+
import { AuthentificationStore } from '../auth/authentification.store';
1011

1112
@Injectable()
1213
export class UserService {
@@ -134,4 +135,12 @@ export class UserService {
134135
deleteUser(username: string): Observable<Response> {
135136
return this._http.delete<Response>('/user/' + username);
136137
}
138+
139+
/**
140+
* Get bookmarks for current user
141+
* @returns {Observable<Bookmark>}
142+
*/
143+
getBookmarks(): Observable<Bookmark[]> {
144+
return this._http.get<Bookmark[]>('/bookmarks');
145+
}
137146
}

ui/src/app/shared/favorite-cards/favorite-cards.component.html

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -58,26 +58,34 @@
5858
<div class="content">
5959
<ng-container [ngSwitch]="favorite.type">
6060
<ng-container *ngSwitchCase="'workflow'">
61-
<div class="right floated">
61+
<div class="right floated" *ngIf="!favorite.icon">
6262
<i class="share alternate icon"></i>
6363
</div>
64+
<img class="right floated mini ui image" [src]="favorite.icon" *ngIf="favorite.icon">
6465
<a class="header" sm-item href="#" [routerLink]="['/project', favorite.key, 'workflow', favorite.workflow_name]">
6566
{{favorite.workflow_name}}
6667
</a>
67-
<div class="description">
68+
<div class="meta">
6869
{{'navbar_workflow_in' | translate}} <a class="projectLink" href="" [routerLink]="['/project', favorite.key]">{{favorite.key}}</a>
6970
</div>
71+
<div class="description">
72+
{{favorite.description}}
73+
</div>
7074
</ng-container>
7175
<ng-container *ngSwitchDefault>
72-
<div class="right floated">
76+
<div class="right floated" *ngIf="!favorite.icon">
7377
<i class="browser icon"></i>
7478
</div>
79+
<img class="right floated mini ui image" [src]="favorite.icon" *ngIf="favorite.icon">
7580
<a class="header" sm-item href="#" [routerLink]="['/project', favorite.key]">
7681
{{favorite.name}}
7782
</a>
78-
<div class="description">
83+
<div class="meta">
7984
{{'common_project' | translate}}
8085
</div>
86+
<div class="description">
87+
{{favorite.description}}
88+
</div>
8189
</ng-container>
8290
</ng-container>
8391
</div>
@@ -87,8 +95,6 @@
8795
<div class="ui active small inline loader" *ngIf="loading[favorite.key + favorite.workflow_name]"></div>
8896
<i class="trash icon" *ngIf="!loading[favorite.key + favorite.workflow_name]"></i>
8997
</div>
90-
91-
9298
</div>
9399
</div>
94100
</ng-container>

ui/src/app/shared/favorite-cards/favorite-cards.component.ts

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
import {Component, Input} from '@angular/core';
2-
import {finalize} from 'rxjs/operators';
3-
import {NavbarProjectData} from '../../model/navbar.model';
4-
import {ProjectStore} from '../../service/project/project.store';
5-
import {WorkflowStore} from '../../service/workflow/workflow.store';
1+
import { Component, EventEmitter, Input, Output } from '@angular/core';
2+
import { finalize } from 'rxjs/operators';
3+
import { Bookmark } from '../../model/bookmark.model';
4+
import { NavbarProjectData } from '../../model/navbar.model';
5+
import { ProjectStore } from '../../service/project/project.store';
6+
import { WorkflowStore } from '../../service/workflow/workflow.store';
67

78
@Component({
89
selector: 'app-favorite-cards',
@@ -11,7 +12,7 @@ import {WorkflowStore} from '../../service/workflow/workflow.store';
1112
})
1213
export class FavoriteCardsComponent {
1314

14-
@Input() favorites: Array<NavbarProjectData>;
15+
@Input() favorites: Array<Bookmark>;
1516
@Input() centered = true;
1617
@Input('projects')
1718
set projects(projects: Array<NavbarProjectData>) {
@@ -25,6 +26,8 @@ export class FavoriteCardsComponent {
2526
}
2627
@Input() workflows: Array<NavbarProjectData>;
2728

29+
@Output() updated: EventEmitter<NavbarProjectData> = new EventEmitter<NavbarProjectData>();
30+
2831
loading = {};
2932
newFav = new NavbarProjectData();
3033
filteredProjects: Array<NavbarProjectData> = [];
@@ -61,7 +64,7 @@ export class FavoriteCardsComponent {
6164
this.newFav = new NavbarProjectData();
6265
this.projectKeySelected = null;
6366
}))
64-
.subscribe();
67+
.subscribe(() => this.updated.emit(fav));
6568
break;
6669
case 'workflow':
6770
this._workflowStore.updateFavorite(fav.key, fav.workflow_name)
@@ -70,7 +73,7 @@ export class FavoriteCardsComponent {
7073
this.newFav = new NavbarProjectData();
7174
this.projectKeySelected = null;
7275
}))
73-
.subscribe();
76+
.subscribe(() => this.updated.emit(fav));
7477
break;
7578
default:
7679
this.newFav = new NavbarProjectData();

ui/src/app/views/favorite/favorite.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ <h1 class="ui center aligned header">{{ 'favorite_title' | translate }}</h1>
55
<div class="two wide column">
66
</div>
77
<div class="twelve wide column">
8-
<app-favorite-cards [favorites]="favorites" [projects]="projects" [workflows]="workflows"></app-favorite-cards>
8+
<app-favorite-cards [favorites]="favorites" [projects]="projects" [workflows]="workflows" (updated)="favoriteUpdated()"></app-favorite-cards>
99
</div>
1010
</div>
1111
</div>
Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1-
import {Component} from '@angular/core';
2-
import {NavbarProjectData} from 'app/model/navbar.model';
3-
import {Subscription} from 'rxjs';
4-
import {NavbarService} from '../../service/navbar/navbar.service';
5-
import {AutoUnsubscribe} from '../../shared/decorator/autoUnsubscribe';
1+
import { Component } from '@angular/core';
2+
import { Bookmark } from 'app/model/bookmark.model';
3+
import { NavbarProjectData } from 'app/model/navbar.model';
4+
import { UserService } from 'app/service/services.module';
5+
import { Subscription } from 'rxjs';
6+
import { finalize, first } from 'rxjs/operators';
7+
import { NavbarService } from '../../service/navbar/navbar.service';
8+
import { AutoUnsubscribe } from '../../shared/decorator/autoUnsubscribe';
69

710
@Component({
811
selector: 'app-favorite',
@@ -12,27 +15,43 @@ import {AutoUnsubscribe} from '../../shared/decorator/autoUnsubscribe';
1215
@AutoUnsubscribe()
1316
export class FavoriteComponent {
1417

15-
favorites: Array<NavbarProjectData> = [];
18+
favorites: Array<Bookmark> = [];
1619
projects: Array<NavbarProjectData> = [];
1720
workflows: Array<NavbarProjectData> = [];
1821
loading = true;
1922

2023
_navbarSub: Subscription;
2124

2225
constructor(
26+
private _userService: UserService,
2327
private _navbarService: NavbarService
2428
) {
29+
this.loadBookmarks();
30+
2531
this._navbarSub = this._navbarService.getData(true)
2632
.subscribe((data) => {
2733
this.loading = false;
2834
if (Array.isArray(data)) {
29-
this.favorites = data.filter((fav) => fav.favorite);
35+
let favorites = data.filter((fav) => fav.favorite);
3036
this.projects = data.filter((elt) => elt.type === 'project');
3137
this.workflows = data.filter((elt) => {
3238
return elt.type === 'workflow' &&
33-
!this.favorites.find((fav) => fav.type === 'workflow' && fav.workflow_name === elt.workflow_name);
39+
!favorites.find((fav) => fav.type === 'workflow' && fav.workflow_name === elt.workflow_name);
3440
});
3541
}
3642
});
3743
}
44+
45+
loadBookmarks() {
46+
this.loading = true;
47+
this._userService.getBookmarks()
48+
.pipe(
49+
first(),
50+
finalize(() => this.loading = false)
51+
).subscribe((bookmarks) => this.favorites = bookmarks);
52+
}
53+
54+
favoriteUpdated() {
55+
this.loadBookmarks();
56+
}
3857
}

0 commit comments

Comments
 (0)
0