Skip to content

Commit 2e5c313

Browse files
committed
✨ mis en place des features subscriptions
1 parent f3db765 commit 2e5c313

14 files changed

+302
-2
lines changed

src/app/modules/dashboard/navigation/admin.menu.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,25 @@ export const adminMenu: Menu[] = [
3939
}
4040
]
4141
},
42+
{
43+
group: 'Plans',
44+
items: [
45+
{
46+
title: 'Plans',
47+
svgPath: [
48+
'M18 18.72a9.094 9.094 0 0 0 3.741-.479 3 3 0 0 0-4.682-2.72m.94 3.198.001.031c0 .225-.012.447-.037.666A11.944 11.944 0 0 1 12 21c-2.17 0-4.207-.576-5.963-1.584A6.062 6.062 0 0 1 6 18.719m12 0a5.971 5.971 0 0 0-.941-3.197m0 0A5.995 5.995 0 0 0 12 12.75a5.995 5.995 0 0 0-5.058 2.772m0 0a3 3 0 0 0-4.681 2.72 8.986 8.986 0 0 0 3.74.477m.94-3.197a5.971 5.971 0 0 0-.94 3.197M15 6.75a3 3 0 1 1-6 0 3 3 0 0 1 6 0Zm6 3a2.25 2.25 0 1 1-4.5 0 2.25 2.25 0 0 1 4.5 0Zm-13.5 0a2.25 2.25 0 1 1-4.5 0 2.25 2.25 0 0 1 4.5 0Z'
49+
],
50+
link: '/plans/create-plan',
51+
},
52+
{
53+
title: 'Souscriptions',
54+
svgPath: [
55+
'M16.5 6v.75m0 3v.75m0 3v.75m0 3V18m-9-5.25h5.25M7.5 15h3M3.375 5.25c-.621 0-1.125.504-1.125 1.125v3.026a2.999 2.999 0 0 1 0 5.198v3.026c0 .621.504 1.125 1.125 1.125h17.25c.621 0 1.125-.504 1.125-1.125v-3.026a2.999 2.999 0 0 1 0-5.198V6.375c0-.621-.504-1.125-1.125-1.125H3.375Z'
56+
],
57+
link: '/plans/subscriptions',
58+
}
59+
]
60+
},
4261
{
4362
group: 'Opérations',
4463
items: [
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export interface Plan {
2+
id?: number;
3+
name: string;
4+
description: string;
5+
price: number;
6+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { Plan } from './plan.interfaces';
2+
3+
export interface SubscriptionState {
4+
plans: Plan[];
5+
loading: boolean;
6+
error: string | null;
7+
message: string | null;
8+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<h2>Formulaire de creation!</h2>
2+
3+
<form [formGroup]="form" (submit)="submit()" class="space-y-6">
4+
<input type="text" formControlName="name" class="block border border-slate-300">
5+
<input type="text" formControlName="description" class="block border border-slate-300">
6+
<input type="number" formControlName="price" class="block border border-slate-300">
7+
8+
<cosna-button-primary type="submit" [loading$]="loading$">
9+
Enregistrer
10+
</cosna-button-primary>
11+
</form>
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { Component, OnInit } from '@angular/core';
2+
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
3+
import { Store } from '@ngrx/store';
4+
import { Observable } from 'rxjs';
5+
import { createPlanAction } from '../../store/subscription.actions';
6+
import { selectError, selectLoading, selectMessage } from '../../store/subscription.selectors';
7+
8+
@Component({
9+
templateUrl: './create-plan.component.html',
10+
})
11+
export class CreatePlanComponent implements OnInit {
12+
13+
public form: FormGroup = this.formBuilder.group({
14+
name: ['', Validators.required],
15+
description: [''],
16+
price: [''],
17+
})
18+
19+
public error$: Observable<string | null> = this.store.select(selectError);
20+
21+
public loading$: Observable<boolean> = this.store.select(selectLoading);
22+
23+
public message$: Observable<string | null> = this.store.select(selectMessage);
24+
25+
constructor(private formBuilder: FormBuilder, private store: Store) { }
26+
27+
ngOnInit(): void { }
28+
29+
submit() {
30+
if (this.form.valid) {
31+
this.store.dispatch(createPlanAction(this.form.getRawValue()))
32+
}
33+
}
34+
35+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { Routes } from '@angular/router';
2+
import { AdminGuard } from '@app/core/guards/admin.guard';
3+
import { AuthGuard } from '@app/core/guards/auth.guard';
4+
import { CpanelComponent } from '@app/shared/themes/layouts/cpanel/cpanel.component';
5+
import { CreatePlanComponent } from '../pages/create-plan/create-plan.component';
6+
7+
export const SUBSCRIPTION_ROUTES: Routes = [
8+
{ path: '', redirectTo: 'create-plan', pathMatch: 'full' },
9+
{
10+
path: '',
11+
canActivateChild: [AuthGuard, AdminGuard],
12+
component: CpanelComponent,
13+
children: [
14+
{ path: 'create-plan', component: CreatePlanComponent }
15+
],
16+
}
17+
];
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { HttpClient } from '@angular/common/http';
2+
import { Injectable } from '@angular/core';
3+
4+
import { environment } from 'environments/environment';
5+
import { Plan } from '../interfaces/plan.interfaces';
6+
7+
@Injectable({
8+
providedIn: 'root'
9+
})
10+
export class PlanService {
11+
12+
constructor(private http: HttpClient) { }
13+
14+
createPlan(plan: Plan) {
15+
return this.http.post<{ plans: Plan[], message: string }>(`${environment.apiUrl}/plans/create`, plan);
16+
}
17+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { Injectable } from '@angular/core';
2+
import { Router } from '@angular/router';
3+
import { Actions, createEffect, ofType } from '@ngrx/effects';
4+
import { catchError, map, of, switchMap } from 'rxjs';
5+
import { Plan } from '../interfaces/plan.interfaces';
6+
7+
import { PlanService } from '../services/plan.service';
8+
import * as SubscriptionActions from './subscription.actions';
9+
10+
@Injectable({
11+
providedIn: 'root'
12+
})
13+
export class PlanEffects {
14+
constructor(
15+
private actions$: Actions,
16+
private planService: PlanService,
17+
private router: Router
18+
) {}
19+
20+
createPlanEffect = createEffect(() => this.actions$.pipe(
21+
ofType(SubscriptionActions.createPlanAction),
22+
switchMap(({ plan }: { plan: Plan }) =>
23+
this.planService.createPlan(plan).pipe(
24+
map(({ plans, message }) => {
25+
return SubscriptionActions.createPlanSuccessAction({ plans, message })
26+
}),
27+
catchError((error) => {
28+
return of(
29+
SubscriptionActions.createPlanFailureAction({
30+
error: error?.message ?? 'Une erreur est survenue'
31+
})
32+
)
33+
})
34+
)
35+
)
36+
)
37+
38+
)
39+
40+
createPlanSuccessEffect() {}
41+
42+
createPlanFailureEffect() {}
43+
44+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { createAction, props } from '@ngrx/store';
2+
3+
import { Plan } from '../interfaces/plan.interfaces';
4+
5+
export const createPlanAction = createAction(
6+
'[Subscription] Create plan',
7+
props<{ plan: Plan }>()
8+
);
9+
10+
export const createPlanSuccessAction = createAction(
11+
'[Subscription] Create plan success',
12+
props<{ plans: Plan[], message: string }>()
13+
);
14+
15+
export const createPlanFailureAction = createAction(
16+
'[Subscription] Create plan failure',
17+
props<{ error: string }>()
18+
);
19+
20+
export const updatePlanAction = createAction(
21+
'[Subscription] Update plan',
22+
props<{ plan: Plan }>()
23+
);
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { Injectable } from '@angular/core';
2+
import { Router } from '@angular/router';
3+
import { Actions } from '@ngrx/effects';
4+
5+
//import { SubscriptionService } from '../services/plan.service';
6+
import * as SubscriptionActions from './subscription.actions';
7+
8+
@Injectable({
9+
providedIn: 'root'
10+
})
11+
export class SubscriptionEffects {
12+
constructor(
13+
private actions$: Actions,
14+
//private subscriptionService: SubscriptionService,
15+
private router: Router
16+
) {}
17+
18+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { createReducer, on } from '@ngrx/store';
2+
3+
import { Plan } from '../interfaces/plan.interfaces';
4+
import { SubscriptionState } from '../interfaces/state.interface';
5+
import * as SubscriptionActions from './subscription.actions';
6+
7+
export const subscriptionState: SubscriptionState = {
8+
plans: [],
9+
loading: false,
10+
error: null,
11+
message: null,
12+
};
13+
14+
export const subscriptionFeatureKey = 'subscription';
15+
16+
export const subscriptionReducer = createReducer(
17+
subscriptionState,
18+
on(
19+
SubscriptionActions.createPlanAction,
20+
(state: SubscriptionState): SubscriptionState => {
21+
return {
22+
...state,
23+
loading: true,
24+
}
25+
}
26+
),
27+
on(
28+
SubscriptionActions.createPlanSuccessAction,
29+
(state: SubscriptionState, { plans, message }: { plans: Plan[], message: string }): SubscriptionState => {
30+
return {
31+
...state,
32+
message,
33+
plans,
34+
loading: false,
35+
error: null,
36+
}
37+
}
38+
),
39+
on(
40+
SubscriptionActions.createPlanFailureAction,
41+
(state: SubscriptionState, { error } : { error: string }): SubscriptionState => {
42+
return {
43+
...state,
44+
error,
45+
loading: false,
46+
message: null,
47+
}
48+
}
49+
)
50+
)
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { createFeatureSelector, createSelector } from '@ngrx/store';
2+
3+
import { SubscriptionState } from '../interfaces/state.interface';
4+
import { subscriptionFeatureKey } from './subscription.reducer';
5+
6+
export const subscriptionFeatureSelector = createFeatureSelector<SubscriptionState>(subscriptionFeatureKey);
7+
8+
export const selectPlans = createSelector(
9+
subscriptionFeatureSelector,
10+
(state: SubscriptionState) => state.plans
11+
);
12+
13+
export const selectLoading = createSelector(
14+
subscriptionFeatureSelector,
15+
(state: SubscriptionState) => state.loading
16+
);
17+
18+
export const selectError = createSelector(
19+
subscriptionFeatureSelector,
20+
(state: SubscriptionState) => state.error
21+
);
22+
23+
export const selectMessage = createSelector(
24+
subscriptionFeatureSelector,
25+
(state: SubscriptionState) => state.message
26+
);
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { NgModule } from '@angular/core';
2+
import { SharedModule } from '@app/shared/shared.module';
3+
import { RouterModule } from '@angular/router';
4+
import { EffectsModule } from '@ngrx/effects';
5+
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
6+
7+
import { SUBSCRIPTION_ROUTES } from './routes/subscription.routes';
8+
import { CreatePlanComponent } from './pages/create-plan/create-plan.component';
9+
import { PlanEffects } from './store/plan.effects';
10+
11+
12+
@NgModule({
13+
declarations: [
14+
CreatePlanComponent
15+
],
16+
imports: [
17+
EffectsModule.forFeature([PlanEffects]),
18+
FormsModule,
19+
ReactiveFormsModule,
20+
RouterModule.forChild(SUBSCRIPTION_ROUTES),
21+
SharedModule,
22+
],
23+
exports: [],
24+
})
25+
export class SubscriptionModule { }

src/app/modules/user/interfaces/user.interface.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
export interface AuthResponse {
2-
message: string;
1+
import { AppHttpResponse } from '@app/shared/interfaces/response.interface';
2+
3+
export interface AuthResponse extends AppHttpResponse {
34
data: {
45
user: User;
56
access_token: string;

0 commit comments

Comments
 (0)