Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
fba8725
feat(issue-50): implement voluntary profile page with editable fields…
SebastienDechand Apr 17, 2025
824f1a4
refactor(issue-50): simplify effect and improve understanding of call…
SebastienDechand Aug 21, 2025
662371b
feat(issue-50): implement voluntary profile page with editable fields…
SebastienDechand Aug 21, 2025
5f5824d
refactor(issue-99): reviews fixes
SebastienDechand Aug 21, 2025
a0741bc
feat(issue-99): add auto-focus pin on map to zoom and filter on user …
SebastienDechand Aug 21, 2025
8e01223
feat(issue-99): add auto-focus pin on map to zoom and filter on user …
SebastienDechand Aug 21, 2025
a08674a
feat(issue-101): improve general design for create activity
SebastienDechand Aug 18, 2025
086c25a
feat(issue-101): improve general design for create activity
SebastienDechand Aug 21, 2025
125c74e
add ban user(asso and voluntary), contact user to admin actions (#79)
EmmanuelleBonoli Aug 25, 2025
a034232
Feature/issue 2/docker (#81)
EmmanuelleBonoli Aug 25, 2025
4175f04
Feature/issue 2/docker (#82)
EmmanuelleBonoli Aug 25, 2025
eaa329d
Feature/issue 2/docker (#83)
EmmanuelleBonoli Aug 25, 2025
312a79d
Feature/issue 2/docker (#84)
EmmanuelleBonoli Aug 25, 2025
eb1f84c
Feature/issue 2/docker (#85)
EmmanuelleBonoli Aug 25, 2025
d0d2373
Feature/issue 2/docker (#86)
EmmanuelleBonoli Aug 25, 2025
8de9805
Feature/issue 2/docker (#87)
EmmanuelleBonoli Aug 25, 2025
79fc785
Feature/issue 2/docker (#88)
Irwin-SOLIMAN Aug 25, 2025
901e4b9
Feature/issue 2/docker (#89)
Irwin-SOLIMAN Aug 25, 2025
f939831
Feature/issue 2/docker (#90)
EmmanuelleBonoli Aug 25, 2025
9368a49
Feature/issue 2/docker (#91)
EmmanuelleBonoli Aug 25, 2025
970f16d
Feature/issue 2/docker (#92)
EmmanuelleBonoli Aug 25, 2025
b728204
:construction: wip adding mock for e2e (#93)
Irwin-SOLIMAN Aug 26, 2025
c1072b9
Feature/issue 2/docker (#94)
EmmanuelleBonoli Aug 26, 2025
caf429c
fix dockerfile (#96)
EmmanuelleBonoli Aug 26, 2025
32731f2
fix dockerfile (#98)
EmmanuelleBonoli Aug 26, 2025
a0e13a7
Merge branch 'staging' into development
EmmanuelleBonoli Aug 26, 2025
5bf88f9
fix nginx (#99)
EmmanuelleBonoli Aug 26, 2025
539e07a
Merge branch 'staging' into development
EmmanuelleBonoli Aug 26, 2025
43ece82
fix nginx (#102)
EmmanuelleBonoli Aug 26, 2025
a961e9d
fix nginx (#104)
EmmanuelleBonoli Aug 26, 2025
6813622
Merge branch 'staging' into development
EmmanuelleBonoli Aug 26, 2025
8dfdb09
Feature/issue 103/add cookies rgbd acceptance (#107)
Irwin-SOLIMAN Aug 26, 2025
060665a
feat(issue-72): add scroll for activity description (#95)
SebastienDechand Aug 26, 2025
c9c1f8c
Hotfix/issue 7777/nginx (#108)
EmmanuelleBonoli Aug 26, 2025
df2dcbf
Feature/issue 100/asso can modify an published activity (#110)
Irwin-SOLIMAN Aug 26, 2025
3036586
Hotfix/issue 6666/fix staging (#112)
EmmanuelleBonoli Aug 26, 2025
b0a33cd
Merge branch 'staging' into development
EmmanuelleBonoli Aug 26, 2025
cf10958
transfer login modal (#114)
EmmanuelleBonoli Aug 26, 2025
08d05e6
Development (#111) (#116)
EmmanuelleBonoli Aug 26, 2025
4a32abf
Merge branch 'staging' into development
EmmanuelleBonoli Aug 26, 2025
4fc9325
Merge branch 'staging' into hotfix/issue-666/transfer-login-modal (#117)
EmmanuelleBonoli Aug 26, 2025
b4c8252
Merge branch 'staging' into hotfix/issue-666/transfer-login-modal (#118)
EmmanuelleBonoli Aug 26, 2025
197fec2
maj docker compose production (#120)
EmmanuelleBonoli Aug 26, 2025
2a020a5
Merge branch 'staging' into development
EmmanuelleBonoli Aug 26, 2025
81eda23
feat(issue-180): improve SEO (#123)
SebastienDechand Aug 27, 2025
9f29c6a
fix footer and dark mode (#126)
EmmanuelleBonoli Aug 27, 2025
5c0131e
add documentation with compodoc (#127)
EmmanuelleBonoli Aug 27, 2025
8f4f71a
fix(issue-181): fix SEO
SebastienDechand Aug 27, 2025
d39f3df
feat(issue-92): prevent registration when activity is full or past
SebastienDechand Aug 27, 2025
ee7e52d
Merge branch 'staging' into development
SebastienDechand Aug 27, 2025
40d7c42
fix(issue-181): fix SEO
SebastienDechand Aug 27, 2025
0916ecc
Feature/issue 108/forgotten password (#133)
Irwin-SOLIMAN Aug 28, 2025
312c70d
Merge branch 'staging' into development
SebastienDechand Aug 28, 2025
ba87a69
Feature/issue 108/forgotten password (#135)
Irwin-SOLIMAN Aug 28, 2025
59acbd4
Feature/issue 108/forgotten password (#137)
Irwin-SOLIMAN Aug 28, 2025
aef82ee
feat(issue-104): add image upload cover in profile association
SebastienDechand Aug 28, 2025
3d3214b
:construction: wip fixing css (#139)
SebastienDechand Sep 1, 2025
e0d792a
Merge branch 'staging' into development
SebastienDechand Sep 1, 2025
ded56e0
:construction: wip fixing css (#142)
SebastienDechand Sep 1, 2025
e6320b2
Feature/issue 71/make app responsive
SebastienDechand Sep 22, 2025
ddb226b
authorize admin to delete activities (#145)
EmmanuelleBonoli Sep 22, 2025
dc6ff53
feat(issue-71): make app responsive (#147)
SebastienDechand Sep 22, 2025
e2b4ba0
hotfix(issue-888): past activities now works (#149)
SebastienDechand Sep 22, 2025
18cde1b
Merge branch 'staging' into development
SebastienDechand Sep 22, 2025
10f7c57
Feature/issue 888/hotfix past activities (#150)
SebastienDechand Sep 22, 2025
2cf6e47
:sparkles: added brut force ip ban (#152)
Irwin-SOLIMAN Sep 23, 2025
fba28ec
:sparkles: added brut force ip ban (#154)
EmmanuelleBonoli Sep 24, 2025
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
2 changes: 1 addition & 1 deletion .github/workflows/cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ jobs:
- name: SSH to VPS and deploy
run: |
TAG=${{ needs.build-and-push.outputs.tag }}
ssh -o StrictHostKeyChecking=no -i ~/.ssh/id_ed25519_frontend ${{ secrets.VPS_USER }}@${{ secrets.VPS_HOST }} << EOF
ssh -o StrictHostKeyChecking=no -i ~/.ssh/id_ed25519_frontend -p "${{ secrets.VPS_SSH_PORT }}" ${{ secrets.VPS_USER }}@${{ secrets.VPS_HOST }} << EOF
cd /home/${{ secrets.VPS_USER }}/alasso/frontend/$TAG
docker compose pull
docker compose down
Expand Down
4 changes: 3 additions & 1 deletion cypress.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ import { defineConfig } from 'cypress'
export default defineConfig({

e2e: {
'baseUrl': 'http://localhost:4200'
'baseUrl': 'http://localhost:4200',
viewportWidth: 1920,
viewportHeight: 1080,
},


Expand Down
2 changes: 1 addition & 1 deletion cypress/support/e2e.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
beforeEach(() => {
// the home page's necessary mocks
cy.intercept('GET', '/activities', { fixture: '/common/activities.json' }).as('getActivities');
cy.intercept('GET', '/activities/future', { fixture: '/common/activities.json' }).as('getActivities');
cy.intercept('GET', '/themes', { fixture: '/common/themes.json' }).as('getThemes');
cy.intercept('GET', 'https://api.maptiler.com/maps/**', { statusCode: 200, body: {} }).as('mapTiler');
});
3 changes: 2 additions & 1 deletion src/app/app.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<app-header></app-header>
<p-toast></p-toast>
@if (loading) {
<p-progressBar mode="indeterminate" [style]="{ height: '6px' }" />

Check warning on line 5 in src/app/app.component.html

View workflow job for this annotation

GitHub Actions / 🧪 Lint + Unit Tests

<p-progressBar/> element should not have inline styles via style attribute. Please use classes instead
}

<div class="page">
Expand All @@ -10,4 +10,5 @@
</div>
<div class="spacer"></div>
<app-footer></app-footer>
</div>
<p-confirmDialog></p-confirmDialog>
</div>
3 changes: 2 additions & 1 deletion src/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@ import { HeaderComponent } from './common/components/header/header.component';
import { ProgressBar } from 'primeng/progressbar';
import { AuthFacade } from './features/authentication/services/auth-facade.service';
import { filter } from 'rxjs/operators';
import { ConfirmDialog } from 'primeng/confirmdialog';

@Component({
selector: 'app-root',
standalone: true,
imports: [ButtonModule, RouterOutlet, HeaderComponent, FooterComponent, Toast, ProgressBar],
imports: [ButtonModule, RouterOutlet, HeaderComponent, FooterComponent, Toast, ProgressBar, ConfirmDialog],
templateUrl: './app.component.html',
styleUrl: './app.component.scss',
})
Expand Down
93 changes: 46 additions & 47 deletions src/app/app.routes.ts
Original file line number Diff line number Diff line change
@@ -1,69 +1,55 @@
import { Routes } from '@angular/router';
import { ActivitiesHomeComponent } from './features/activity/pages/activities-home/activities-home.component';
import { ActivityDetailsComponent } from './features/activity/pages/activity-details/activity-details.component';
import { activityDetailsResolver } from './common/resolvers/activity-details.resolver';
import { AssociationDetailsComponent } from './features/association/pages/association-details/association-details.component';
import { associationResolver } from './common/resolvers/association.resolver';
import { ActivityCreationComponent } from './features/activity/pages/activity-creation/activity-creation.component';
import { isLoggedInGuard } from './common/guards/is-logged-in.guard';
import { isAdminGuard } from './common/guards/is-admin.guard';
import { isAssociationGuard } from './common/guards/is-association.guard';
import { ReportHomePageComponent } from './features/report/pages/report-home-page/report-home-page.component';
import { AssociationProfilePageComponent } from './features/profile/pages/association-profile-page/association-profile-page.component';
import { VoluntaryProfilePageComponent } from './features/profile/pages/voluntary-profile-page/voluntary-profile-page.component';
import { isLoggedInGuard } from './common/guards/is-logged-in.guard';
import { isVoluntaryGuard } from './common/guards/is-voluntary.guard';
import { isAdminGuard } from './common/guards/is-admin.guard';
import { CguComponent } from './features/authentication/components/legals/cgu/cgu.component';
import { PolitiqueConfidentialiteComponent } from './features/authentication/components/legals/politique-confidentialite/politique-confidentialite.component';
import { ResetPasswordPageComponent } from './features/authentication/pages/reset-password-page/reset-password-page.component';
import { activityDetailsResolver } from './common/resolvers/activity-details.resolver';
import { associationResolver } from './common/resolvers/association.resolver';
import { ActivitiesHomeComponent } from './features/activity/pages/activities-home/activities-home.component';

export const routes: Routes = [
{
path: '',
component: ActivitiesHomeComponent,
pathMatch: 'full',
component: ActivitiesHomeComponent,
},

{
path: 'activity/creation',
canActivate: [isLoggedInGuard, isAssociationGuard],
component: ActivityCreationComponent,
loadComponent: () => import('./features/activity/pages/activity-creation/activity-creation.component').then(m => m.ActivityCreationComponent),
},
{
path: 'activity/creation/:id',
canActivate: [isLoggedInGuard, isAssociationGuard],
component: ActivityCreationComponent,
},
{
path: 'reports',
canActivate: [isLoggedInGuard, isAdminGuard],
component: ReportHomePageComponent,
loadComponent: () => import('./features/activity/pages/activity-creation/activity-creation.component').then(m => m.ActivityCreationComponent),
},
{
path: 'activity/:id',
resolve: {
activityDetails: activityDetailsResolver,
},
component: ActivityDetailsComponent,
resolve: { activityDetails: activityDetailsResolver },
loadComponent: () => import('./features/activity/pages/activity-details/activity-details.component').then(m => m.ActivityDetailsComponent),
},

{
path: 'association/:id',
component: AssociationDetailsComponent,
resolve: {
association: associationResolver,
},
resolve: { association: associationResolver },
loadComponent: () =>
import('./features/association/pages/association-details/association-details.component').then(m => m.AssociationDetailsComponent),
},
{ path: 'cgu', component: CguComponent },
{ path: 'politique-confidentialite', component: PolitiqueConfidentialiteComponent },
{ path: 'reset-password', component: ResetPasswordPageComponent },

{
path: 'reports',
canActivate: [isLoggedInGuard, isAdminGuard],
loadComponent: () => import('./features/report/pages/report-home-page/report-home-page.component').then(m => m.ReportHomePageComponent),
},

{
path: 'profile/association',
component: AssociationProfilePageComponent,
canActivate: [isLoggedInGuard, isAssociationGuard],
loadComponent: () =>
import('./features/profile/pages/association-profile-page/association-profile-page.component').then(m => m.AssociationProfilePageComponent),
children: [
{
path: '',
redirectTo: 'activities',
pathMatch: 'full',
},
{ path: '', redirectTo: 'activities', pathMatch: 'full' },
{
path: 'activities',
loadComponent: () =>
Expand All @@ -89,16 +75,14 @@ export const routes: Routes = [
},
],
},

{
path: 'profile/voluntary',
component: VoluntaryProfilePageComponent,
canActivate: [isLoggedInGuard, isVoluntaryGuard],
loadComponent: () =>
import('./features/profile/pages/voluntary-profile-page/voluntary-profile-page.component').then(m => m.VoluntaryProfilePageComponent),
children: [
{
path: '',
redirectTo: 'activities',
pathMatch: 'full',
},
{ path: '', redirectTo: 'activities', pathMatch: 'full' },
{
path: 'activities',
loadComponent: () =>
Expand All @@ -124,8 +108,23 @@ export const routes: Routes = [
},
],
},

{
path: 'cgu',
loadComponent: () => import('./features/authentication/components/legals/cgu/cgu.component').then(m => m.CguComponent),
},
{
path: 'politique-confidentialite',
loadComponent: () =>
import('./features/authentication/components/legals/politique-confidentialite/politique-confidentialite.component').then(
m => m.PolitiqueConfidentialiteComponent
),
},
{
path: '**',
redirectTo: '',
path: 'reset-password',
loadComponent: () =>
import('./features/authentication/pages/reset-password-page/reset-password-page.component').then(m => m.ResetPasswordPageComponent),
},

{ path: '**', redirectTo: '' },
];
2 changes: 1 addition & 1 deletion src/app/common/components/header/header.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<div class="right-side-container">
@if (userInfos$ | async; as userInfo) {
@if (userInfo.isConnected) {
@if (userInfo.canPublishActivity && !isOnActivityCreationPage()) {

Check warning on line 10 in src/app/common/components/header/header.component.html

View workflow job for this annotation

GitHub Actions / 🧪 Lint + Unit Tests

Avoid calling expressions in templates
<app-single-button
class="underline-animate"
[type]="'button'"
Expand All @@ -15,7 +15,7 @@
[routerLink]="['/activity', 'creation']"
routerLinkActive="active"
[styleClass]="ButtonStyleClass.addActivityButton"
[label]="'Publier une activité 🚀'"
[label]="'Publier une activité'"
[ariaLabel]="'button de navigation vers la page de creation des activités'">
</app-single-button>
}
Expand Down
2 changes: 1 addition & 1 deletion src/app/common/components/header/header.component.scss
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ header {
}
}

@media screen and (max-width: 575px) {
@media screen and (max-width: 768px) {
header {
.logo {
#header-title {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
[inputId]="inputId"
[disabled]="disabled"
[formControlName]="fieldConfig.name"
appendTo="body"
dateFormat="dd.mm.yy"
[ariaLabel]="fieldConfig.label"
[placeholder]="fieldConfig.placeholder ?? ''">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<form class="input-wrapper" [formGroup]="formGroup">
<form class="input-wrapper" [formGroup]="formGroup" [ngClass]="{ 'row-layout': showSearchButton, 'column-layout': !showSearchButton }">
<div class="search-container" [ngClass]="showSearchButton ? 'with-search' : 'without-search'">
@for (field of inputConfigs; track field.label; let i = $index; let isLast = $last) {
<div class="input-container">
Expand Down Expand Up @@ -49,7 +49,7 @@
</div>

@if (showErrors) {
<app-input-field-error [control]="formGroup.get(field.name)" [fieldName]="field.name" class="input-error-message"> </app-input-field-error>

Check warning on line 52 in src/app/common/components/multiple-input-field/multiple-input-field.component.html

View workflow job for this annotation

GitHub Actions / 🧪 Lint + Unit Tests

Avoid calling expressions in templates
}
@if (!isLast) {
<div class="separator"></div>
Expand All @@ -63,9 +63,17 @@
<i class="fa-solid fa-magnifying-glass"></i>
</button>
}
@if (showSaveButton) {
<button pButton type="button" class="save-button" (click)="save.emit()">
@if (showSaveButton && !isMobile) {
<button pButton type="button" class="save-button" (click)="manualSave()">
<i class="fa-solid fa-floppy-disk"></i>
</button>
}

<div class="field-feedback">
@if (autosave && isMobile) {
<p class="save-status" [class.visible]="saveStatus !== 'idle'" [class.success]="saveStatus === 'saved'">
{{ saveStatus === 'saving' ? 'Enregistrement...' : saveStatus === 'saved' ? 'Enregistré ✅' : '' }}
</p>
}
</div>
</form>
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,15 @@
position: relative;
z-index: 1100;
margin-bottom: 15px;

&.row-layout {
flex-direction: row;
}

&.column-layout {
flex-direction: column;
}

.search-container {
display: flex;
align-items: center;
Expand Down Expand Up @@ -188,3 +197,32 @@
position: absolute;
right: 0;
}

.field-feedback {
margin-top: 4px;
min-height: 22px;
font-size: 14px;
line-height: 1.2;

display: flex;
justify-content: flex-end;
align-items: center;

.save-status {
color: var(--font-color-grey);
opacity: 0;
visibility: hidden;
transition: opacity 0.3s ease;
margin: 0;

&.visible {
opacity: 1;
visibility: visible;
}

&.success {
color: var(--primary-light-color);
font-weight: 500;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ import { IftaLabelModule } from 'primeng/iftalabel';
import { InputGroupModule } from 'primeng/inputgroup';
import { InputGroupAddonModule } from 'primeng/inputgroupaddon';
import { InputTextModule } from 'primeng/inputtext';
import { Subject, debounceTime } from 'rxjs';
import { FormField } from 'src/app/features/authentication/models/form.model';
import { AutosaveFieldComponent } from '../../directives/autosave-field.component';
import { SaveStatus } from '../../models/status';
import { InputFieldErrorComponent } from '../input-field-error/input-field-error.component';

@Component({
Expand All @@ -27,20 +28,21 @@ import { InputFieldErrorComponent } from '../input-field-error/input-field-error
templateUrl: './multiple-input-field.component.html',
styleUrls: ['./multiple-input-field.component.scss'],
})
export class MultipleInputFieldComponent implements OnChanges {
export class MultipleInputFieldComponent extends AutosaveFieldComponent<Record<string, string>> implements OnChanges {
@Output() inputValuesChanged = new EventEmitter<Record<string, string>>();
@Output() save = new EventEmitter<void>();
@Output() save = new EventEmitter<Record<string, string>>();

@Input() fieldConfigs: FormField[] = [];
@Input() showSearchButton: boolean = false;
@Input() showSearchButton = false;
@Input() showSaveButton = false;
@Input() showErrors = false;
@Input() formGroup: FormGroup;
@Input() formGroup!: FormGroup;
@Input() override autosave = false;

focusedIndex: number | null = null;
inputConfigs: { name: string; label: string; placeholder: string; type: string; value: string }[] = [];

private _inputChanges$ = new Subject<Record<string, string>>();
override saveStatus: SaveStatus = 'idle';

get inputClass(): Record<string, boolean> {
return {
Expand All @@ -49,10 +51,8 @@ export class MultipleInputFieldComponent implements OnChanges {
};
}

constructor() {
this._inputChanges$.pipe(debounceTime(500)).subscribe(filters => {
this.inputValuesChanged.emit(filters);
});
override ngOnInit(): void {
super.ngOnInit();
}

ngOnChanges(changes: SimpleChanges): void {
Expand All @@ -70,13 +70,27 @@ export class MultipleInputFieldComponent implements OnChanges {
emitValues(): void {
const filters: Record<string, string> = {};
this.inputConfigs.forEach(config => {
filters[config.name] = config.value;
filters[config.name] = this.formGroup.get(config.name)?.value || '';
});
this._inputChanges$.next(filters);
this.valueChanges$.next(filters);
this.inputValuesChanged.emit(filters);
}

manualSave(): void {
const values: Record<string, string> = {};
this.inputConfigs.forEach(config => {
values[config.name] = this.formGroup.get(config.name)?.value || '';
});
this.startSaving();
this.onSave(values);
}

clearInput(index: number): void {
this.inputConfigs[index].value = '';
this.emitValues();
}

protected override onSave(values: Record<string, string>): void {
this.save.emit(values);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
[cols]="cols"
[formControlName]="fieldConfig.name"
[disabled]="disabled"
class="textarea-field">
</textarea>
class="textarea-field"
[ngClass]="{ 'with-save-button': showSaveButton && !isMobile }"></textarea>
<label [for]="id">{{ fieldConfig.label }}</label>
</p-iftalabel>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,10 +92,10 @@ form {
position: absolute;
right: 0;
top: 0;
height: 70%;
height: 68%;
background-color: var(--primary-light-color);
color: var(--font-color-light);
padding: 19.1px 25px;
padding: 19px 25px;
border: none;
cursor: pointer;
border-radius: 0 10px 10px 0;
Expand Down
Loading
Loading