Skip to content
Snippets Groups Projects
Commit d1328818 authored by Pierre Ecarlat's avatar Pierre Ecarlat
Browse files

Merge branch 'change/navbar' into 'dev'

fix(navbar): Use a single navbar component everywhere

See merge request !895
parents bb07c08e 8b82992f
No related branches found
No related tags found
2 merge requests!907V3.2.0,!895fix(navbar): Use a single navbar component everywhere
Showing
with 132 additions and 286 deletions
......@@ -3,6 +3,8 @@ import { NgModule } from '@angular/core';
import { AgGridModule } from 'ag-grid-angular';
import { SharedModule } from '../shared/shared.module';
import { AdminRoutingModule } from './admin-routing.module';
import { AdminHeaderComponent } from './components/admin-header/admin-header.component';
import { ButtonCellRendererComponent } from './components/button-cell-renderer/button-cell-renderer.component';
import { ClaimStructureComponent } from './components/claim-structure/claim-structure.component';
import { DeletedStructuresComponent } from './components/deleted-structures/deleted-structures.component';
import { EspaceCoopCNFSComponent } from './components/espace-coop-cnfs/espace-coop-cnfs.component';
......@@ -21,9 +23,7 @@ import { DeleteUserComponent } from './components/manage-users/delete-user/delet
import { EmployerRendererComponent } from './components/manage-users/employer-renderer/employer-renderer.component';
import { JobRendererComponent } from './components/manage-users/job-renderer/job-renderer.component';
import { ManageUsersComponent } from './components/manage-users/manage-users.component';
import { NavBarComponent } from './components/nav-bar/nav-bar.component';
import { AdminStructuresListComponent } from './components/structures-list/admin-structures-list.component';
import { ButtonCellRendererComponent } from './components/button-cell-renderer/button-cell-renderer.component';
@NgModule({
declarations: [
......@@ -45,7 +45,7 @@ import { ButtonCellRendererComponent } from './components/button-cell-renderer/b
ManageJobsComponent,
ManageEmployersComponent,
DeletedStructuresComponent,
NavBarComponent,
AdminHeaderComponent,
EspaceCoopCNFSComponent,
ButtonCellRendererComponent,
],
......
@import 'color';
.adminLayout {
display: flex;
flex-direction: column;
align-items: start;
gap: 1rem;
display: block;
margin: auto;
padding-bottom: 1rem;
padding: 2rem 0;
width: 80%;
}
h3,
h2 {
margin: 1rem 0;
}
h3.inline {
display: flex;
flex-direction: row;
align-items: center;
gap: 1rem;
}
.incomplete {
......
<div class="header">
<div class="title-and-ghost">
<h1>Administration</h1>
<app-button [variant]="'secondary'" [label]="'Ghost'" [size]="'small'" (action)="openGhost()" />
</div>
<app-nav-bar
[tabs]="getTabsNames()"
[currentTab]="currentTab"
[ariaLabel]="'Sélectionner le panneau admin'"
(clickedTab)="navigateTo($event)"
/>
</div>
@import 'typography';
.header {
display: flex;
flex-direction: column;
width: 100%;
.title-and-ghost {
display: flex;
justify-content: space-between;
width: 100%;
h1 {
justify-content: start;
@include font-regular-24;
}
}
app-nav-bar {
margin: 1.5rem 0;
}
}
import { Component, OnInit } from '@angular/core';
import { Router, NavigationEnd } from '@angular/router';
import { Router } from '@angular/router';
import { ConfigService } from '../../../services/config.service';
import { AdminRoutes } from '../../admin-routing.module';
import { filter } from 'rxjs/operators';
@Component({
selector: 'app-admin-nav-bar',
templateUrl: './nav-bar.component.html',
styleUrls: ['./nav-bar.component.scss'],
selector: 'app-admin-header',
templateUrl: './admin-header.component.html',
styleUrls: ['./admin-header.component.scss'],
})
export class NavBarComponent implements OnInit {
public routes = AdminRoutes;
export class AdminHeaderComponent implements OnInit {
private ghostLink: string;
public currentRoute: string;
public currentTab = 0;
public routes = AdminRoutes;
public buttons = [
public tabs = [
{ label: 'Revendication structure', route: this.routes.pendingStructures.link },
{ label: 'Liste structures', route: this.routes.structuresList.link },
{ label: 'Structures supprimées', route: this.routes.deletedStructures.link },
......@@ -35,22 +34,19 @@ export class NavBarComponent implements OnInit {
this.ghostLink = config.ghostAdminUrl;
});
this.router.events.pipe(filter((event) => event instanceof NavigationEnd)).subscribe((event: NavigationEnd) => {
this.currentRoute = event.urlAfterRedirects || event.url;
});
this.currentRoute = this.router.url.substring(1);
// We navigateTo distinct routes, so we need to recover the currentTab after rerouting
this.currentTab = this.tabs.findIndex((tab) => tab.route === this.router.url.substring(1));
}
public openGhost(): void {
window.open(this.ghostLink);
public getTabsNames(): string[] {
return this.tabs.map((tab) => tab.label);
}
public navigateTo(route: string): void {
this.router.navigateByUrl(route);
public navigateTo(tabIndex: number): void {
this.router.navigateByUrl(this.tabs[tabIndex].route);
}
public isActive(route: string): string {
return this.currentRoute === route ? 'active' : '';
public openGhost(): void {
window.open(this.ghostLink);
}
}
<app-admin-nav-bar />
<div class="adminLayout">
<app-admin-header />
<h2>Revendication structure</h2>
<ag-grid-angular
*ngIf="demandsAttachment?.length"
......
<app-admin-nav-bar />
<div *ngIf="isLoading" class="loader" aria-busy="true">
<img class="loader-gif" src="/assets/gif/loader_circle.gif" alt />
</div>
<div *ngIf="!isLoading" class="adminLayout">
<app-admin-header />
<h2>Liste des structures supprimées</h2>
<ag-grid-angular
*ngIf="deletedStructures"
......
<app-admin-nav-bar />
<div class="adminLayout">
<app-admin-header />
<h2>CNFS présents dans Espace Coop sans compte Rés'in ({{ unregisteredCNFS.length }})</h2>
<ag-grid-angular
*ngIf="unregisteredCNFS.length"
......
<app-admin-nav-bar />
<div class="adminLayout">
<app-admin-header />
<h2>
Gestion des employeurs
<span *ngIf="unvalidatedEmployers && validatedEmployers">
({{ unvalidatedEmployers.length + validatedEmployers.length }})
</span>
</h2>
</div>
<div class="adminLayout">
<h3 *ngIf="unvalidatedEmployers" class="title">Employeurs non validés ({{ unvalidatedEmployers.length }})</h3>
<ag-grid-angular
*ngIf="unvalidatedEmployers"
......
<app-admin-nav-bar />
<div class="adminLayout">
<app-admin-header />
<h2>
Gestion des fonctions
<span *ngIf="unvalidatedJobs && validatedJobs"> ({{ unvalidatedJobs.length + validatedJobs.length }}) </span>
</h2>
</div>
<div class="adminLayout">
<h3 *ngIf="unvalidatedJobs" class="title">Fonctions non validées ({{ unvalidatedJobs.length }})</h3>
<ag-grid-angular
*ngIf="unvalidatedJobs"
......
<app-admin-nav-bar />
<div class="adminLayout">
<app-admin-header />
<h2>
Gestion des utilisateurs
<span *ngIf="unVerifiedUsers && unAttachedUsers && attachedUsers">
({{ unVerifiedUsers.length + unAttachedUsers.length + attachedUsers.length }})
</span>
</h2>
</div>
<div class="adminLayout">
<h3 *ngIf="unVerifiedUsers" class="title">Utilisateurs non vérifiés ({{ unVerifiedUsers.length }})</h3>
<ag-grid-angular
*ngIf="validatedJobs && validatedEmployers"
......
<div class="header">
<div class="title-and-ghost">
<h1>Administration</h1>
<app-button
[variant]="'secondary'"
[label]="'Ghost'"
[type]="'button'"
(action)="openGhost()"
/>
</div>
<nav>
<ng-container *ngFor="let button of buttons">
<div [class]="isActive(button.route)">
<app-button
[variant]="'tertiary'"
[label]="button.label"
[type]="'button'"
(action)="navigateTo(button.route)"
/>
</div>
</ng-container>
</nav>
</div>
.header {
display: flex;
flex-direction: column;
align-items: center;
.title-and-ghost {
margin-top: 2rem;
display: flex;
flex-direction: row;
justify-content: space-between;
width: 80%;
height: 2rem;
margin-bottom: 1.5rem;
}
h1 {
justify-content: start;
font-weight: 400;
}
nav {
margin-top: 0.5rem;
display: flex;
justify-content: space-between;
border-bottom: 1px solid #ccc;
margin-bottom: 1.5rem;
width: 80%;
overflow-x: auto;
}
app-button {
height: 3.75rem;
position: relative;
}
.active {
border-bottom: 3px solid black;
padding-bottom: 0.5rem;
}
}
<app-admin-nav-bar />
<div class="adminLayout">
<app-admin-header />
<div class="filters-containers">
<div *ngFor="let filterOption of filterOptions">
<div class="filter">
<app-radio-option
class="filter-option"
[id]="filterOption.value"
[label]="filterOption.label"
[value]="filterOption.value"
[selected]="filter === filterOption.value"
(click)="setFilter(filterOption.value)"
/>
<div class="filters-containers">
<div *ngFor="let filterOption of filterOptions">
<div class="filter">
<app-radio-option
class="filter-option"
[id]="filterOption.value"
[label]="filterOption.label"
[value]="filterOption.value"
[selected]="filter === filterOption.value"
(click)="setFilter(filterOption.value)"
/>
</div>
</div>
</div>
</div>
<div *ngIf="isLoading" class="loader" aria-busy="true">
<img class="loader-gif" src="/assets/gif/loader_circle.gif" alt="" />
</div>
<div *ngIf="!isLoading" class="adminLayout">
<div *ngFor="let section of filteredSections" class="section">
<h3>{{ section.title }} ({{ section.data?.length || 0 }})</h3>
<ag-grid-angular
*ngIf="section.data?.length"
class="ag-theme-alpine"
domLayout="autoHeight"
style="width: 100%"
[gridOptions]="getGridContext(section.filter)"
[rowData]="section.data"
[columnDefs]="columnDefs"
[rowHeight]="rowHeight"
[ngClass]="section.cssClass"
/>
<div *ngIf="!section.data?.length" class="no-structures">Aucun renseignement</div>
<div *ngIf="isLoading" class="loader" aria-busy="true">
<img class="loader-gif" src="/assets/gif/loader_circle.gif" alt="" />
</div>
<div *ngIf="!isLoading">
<div *ngFor="let section of filteredSections" class="section">
<h3>{{ section.title }} ({{ section.data?.length || 0 }})</h3>
<ag-grid-angular
*ngIf="section.data?.length"
class="ag-theme-alpine"
domLayout="autoHeight"
style="width: 100%"
[gridOptions]="getGridContext(section.filter)"
[rowData]="section.data"
[columnDefs]="columnDefs"
[rowHeight]="rowHeight"
[ngClass]="section.cssClass"
/>
<div *ngIf="!section.data?.length" class="no-structures">Aucun renseignement</div>
</div>
</div>
</div>
......@@ -19,12 +19,8 @@
}
.filters-containers {
width: 80%;
display: flex;
margin-left: 10%;
flex-direction: row;
align-items: center;
margin-bottom: 1rem;
margin-bottom: 2rem;
gap: 0.5rem;
}
......
......@@ -8,23 +8,10 @@
</div>
</div>
<nav>
<div role="tablist" class="navigation" aria-label="Filtrer les actualités">
<div
*ngFor="let tag of tags.others"
class="tag"
role="tab"
tabindex="0"
aria-controls="posts"
[attr.aria-selected]="tag.slug === mainActiveTag.slug ? true : false"
[ngClass]="{ active: tag.slug === mainActiveTag.slug || tag.name === mainActiveTag.slug }"
(click)="activateTag(tag)"
(keydown.enter)="activateTag(tag)"
>
<span>
{{ tag.name }}
</span>
</div>
</div>
</nav>
<app-nav-bar
ariaLabel="Sélectionner une catégorie d'actualités"
[tabs]="getTabsNames()"
[currentTab]="currentTab"
(clickedTab)="activateTag($event)"
/>
</div>
......@@ -13,41 +13,3 @@
color: $grey-3;
}
}
nav {
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid $grey-4;
.navigation {
display: flex;
overflow-x: auto;
white-space: nowrap;
.tag {
cursor: pointer;
user-select: none;
@include font-regular-16;
box-sizing: border-box;
padding: 0 16px;
height: 60px;
display: flex;
align-items: center;
border-bottom: 3px solid transparent;
transition: all 0.3s ease-in-out;
outline-offset: -2px; // Fixes the focus display issue in Firefox where it was not visible due to "overflow-x" on .navigation
&.active {
@include font-bold-16;
border-color: $grey-1;
}
&:hover {
border-color: $grey-4;
}
&:focus {
outline-color: $red;
}
}
}
}
......@@ -11,8 +11,8 @@ import { parseSlugToTag } from '../utils/NewsUtils';
})
export class PostHeaderComponent implements OnInit {
public tags: TagWithMeta;
public mainActiveTag: Tag = new Tag({ slug: TagEnum.aLaUne });
public tagEnum = TagEnum;
public currentTab = 0;
public checkedPublicTags: Tag[] = [];
public checkedLocationTags: Tag[] = [];
......@@ -33,8 +33,9 @@ export class PostHeaderComponent implements OnInit {
});
this.route.queryParams.subscribe((queryParams) => {
// We navigateTo distinct routes, so we need to recover the currentTab after rerouting
if (queryParams.mainTag) {
this.mainActiveTag = new Tag({ slug: queryParams.mainTag });
this.currentTab = this.tags.others.findIndex((tag) => tag.slug === queryParams.mainTag);
}
if (queryParams.publicTags) {
this.checkedPublicTags = parseSlugToTag(queryParams.publicTags);
......@@ -45,6 +46,10 @@ export class PostHeaderComponent implements OnInit {
});
}
public getTabsNames(): string[] {
return this.tags.others.map((tab) => tab.name);
}
private removeTagBySlug(slugToRemove: string): void {
this.tags.others = this.tags.others.filter((tag) => tag.slug !== slugToRemove);
}
......@@ -67,16 +72,11 @@ export class PostHeaderComponent implements OnInit {
return nameTagA.localeCompare(nameTagB);
}
public activateTag(tag: Tag): void {
this.mainActiveTag = tag;
this.setQueryParam();
}
private setQueryParam(): void {
public activateTag(tabIndex: number): void {
this.router.navigate(['/actualites'], {
relativeTo: this.route,
queryParams: {
mainTag: this.getMainTag(),
mainTag: this.getMainTag(this.tags.others[tabIndex]),
publicTags: this.checkedPublicTags.map((tag) => tag.slug),
locationTags: this.checkedLocationTags.map((tag) => tag.slug),
},
......@@ -84,10 +84,10 @@ export class PostHeaderComponent implements OnInit {
});
}
public getMainTag(): string {
if (this.mainActiveTag.slug === TagEnum.aLaUne) {
public getMainTag(tag: Tag): string {
if (tag.slug === TagEnum.aLaUne) {
return '';
}
return this.mainActiveTag.slug === this.tagEnum.etudes ? this.mainActiveTag.name : this.mainActiveTag.slug;
return tag.slug === this.tagEnum.etudes ? tag.name : tag.slug;
}
}
......@@ -24,51 +24,18 @@
/>
</div>
<!-- Navigation -->
<div class="navigation" role="menu">
<div
class="tab"
tabindex="0"
role="menuitem"
[ngClass]="{ selected: currentTab === tabsEnum.details }"
(click)="navigateTo(tabsEnum.details)"
(keyup.enter)="navigateTo(tabsEnum.details)"
>
Coordonnées
</div>
<div
class="tab"
tabindex="0"
role="menuitem"
[ngClass]="{ selected: currentTab === tabsEnum.credentials }"
(click)="navigateTo(tabsEnum.credentials)"
(keyup.enter)="navigateTo(tabsEnum.credentials)"
>
Email et mot de passe
</div>
<div
class="tab"
tabindex="0"
role="menuitem"
[ngClass]="{ selected: currentTab === tabsEnum.employer }"
(click)="navigateTo(tabsEnum.employer)"
(keyup.enter)="navigateTo(tabsEnum.employer)"
>
Employeur et fonction
</div>
<div
class="tab"
tabindex="0"
role="menuitem"
[ngClass]="{ selected: currentTab === tabsEnum.description }"
(click)="navigateTo(tabsEnum.description)"
(keyup.enter)="navigateTo(tabsEnum.description)"
>
Description
</div>
</div>
<app-nav-bar
[tabs]="getTabsNames()"
[currentTab]="currentTab"
[shouldExpand]="true"
[ariaLabel]="'Sélectionner quelle section de votre profil éditer'"
[ariaControls]="getTabsIds()"
(clickedTab)="changeTab($event)"
/>
<!-- Content of tabs -->
<div class="content">
<div *ngIf="currentTab === tabsEnum.details" class="detailsTab">
<div *ngIf="currentTab === tabsEnum.details" id="detailsTab" class="detailsTab">
<app-input
[id]="'name'"
[label]="'Prénom'"
......@@ -92,7 +59,7 @@
/>
</div>
<div *ngIf="currentTab === tabsEnum.credentials" class="credentialsTab">
<div *ngIf="currentTab === tabsEnum.credentials" id="credentialsTab" class="credentialsTab">
<div class="credentials">
<div class="inline">
<app-svg-icon [folder]="'tags'" [icon]="'mail'" [iconClass]="'icon-20'" />
......@@ -123,7 +90,7 @@
</div>
</div>
<div *ngIf="currentTab === tabsEnum.employer" class="employerJob">
<div *ngIf="currentTab === tabsEnum.employer" id="employerJob" class="employerJob">
<app-select-or-create
[autocompleteFunction]="profileService.getEmployers.bind(profileService)"
[name]="'Employeur'"
......@@ -149,7 +116,7 @@
/>
</div>
<div *ngIf="currentTab === tabsEnum.description" class="descriptionTab">
<div *ngIf="currentTab === tabsEnum.description" id="descriptionTab" class="descriptionTab">
<app-textarea
[id]="'description'"
[label]="'Description'"
......
......@@ -53,37 +53,6 @@
}
}
.navigation {
justify-content: flex-start;
overflow-x: auto;
white-space: nowrap;
border-bottom: 1px solid $grey-5;
.tab {
cursor: pointer;
user-select: none;
@include font-regular-16;
box-sizing: border-box;
padding: 0 16px;
height: 60px;
display: flex;
justify-content: center;
align-items: center;
flex: 1;
border-bottom: 3px solid transparent;
transition: all 0.3s ease-in-out;
&:hover {
border-color: $grey-4;
}
&.selected {
font-weight: bold;
border-color: $grey-1;
}
}
}
.content {
flex: 1;
max-width: 600px;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment