My profile - edit dimensions, new storage service, hydrate state on refresh
This commit is contained in:
parent
a720e80dd9
commit
b2c8bd766c
14
frontend/package-lock.json
generated
14
frontend/package-lock.json
generated
@ -5163,6 +5163,11 @@
|
||||
"regexp.prototype.flags": "^1.2.0"
|
||||
}
|
||||
},
|
||||
"deepmerge": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-3.3.0.tgz",
|
||||
"integrity": "sha512-GRQOafGHwMHpjPx9iCvTgpu9NojZ49q794EEL94JVEw6VaeA8XTUyBKvAkOOjBX9oJNiV6G3P+T+tihFjo2TqA=="
|
||||
},
|
||||
"default-gateway": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-4.2.0.tgz",
|
||||
@ -8975,6 +8980,15 @@
|
||||
"integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=",
|
||||
"dev": true
|
||||
},
|
||||
"ngrx-store-localstorage": {
|
||||
"version": "10.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ngrx-store-localstorage/-/ngrx-store-localstorage-10.1.1.tgz",
|
||||
"integrity": "sha512-OacpqJMraLrgqr/T3DfyT4T4lqAISZdCPWmYQyHtXWsNgKGid6xL4SgPktJ3vcMbIuuMckOpEFIgIpgBvf8E3g==",
|
||||
"requires": {
|
||||
"deepmerge": "^3.2.0",
|
||||
"tslib": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"nice-try": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
|
||||
|
@ -28,6 +28,7 @@
|
||||
"@types/papaparse": "^5.0.3",
|
||||
"d3": "^5.16.0",
|
||||
"eva-icons": "^1.1.2",
|
||||
"ngrx-store-localstorage": "^10.1.1",
|
||||
"papaparse": "^5.2.0",
|
||||
"rxjs": "~6.5.4",
|
||||
"tslib": "^2.0.0",
|
||||
|
@ -1,12 +1,18 @@
|
||||
import { createAction, props } from '@ngrx/store';
|
||||
import { create } from 'domain';
|
||||
import { Dimension } from '../reducers/dimension.reducers';
|
||||
import { Category, Dimension } from '../reducers/dimension.reducers';
|
||||
|
||||
export const getDimensions = createAction('[Dimension] Get Dimmnsions');
|
||||
export const getDimensionSuccess = createAction(
|
||||
'[Dimension] Get Dimensions Success',
|
||||
props<{ dimensions: Array<Dimension> }>()
|
||||
);
|
||||
export const getSelectedDimension = createAction(
|
||||
'[Dimension] Get Selected Dimension'
|
||||
);
|
||||
export const getSelectedDimensionSuccess = createAction(
|
||||
'[Dimension] Get Selected Dimension Success',
|
||||
props<{ categories: Array<Category> }>()
|
||||
);
|
||||
export const addNewDimension = createAction(
|
||||
'[Dimension] Add Dimension',
|
||||
props<{ dimension: Dimension }>()
|
||||
@ -18,3 +24,10 @@ export const removeDimension = createAction(
|
||||
'[Dimension] Remove Dimension',
|
||||
props<{ id: number }>()
|
||||
);
|
||||
export const setSelectedDimension = createAction(
|
||||
'[Dimension] Set Selected Dimension',
|
||||
props<{ id: number }>()
|
||||
);
|
||||
export const clearSelectedDimension = createAction(
|
||||
'[Dimension] Clear Selected Dimension'
|
||||
);
|
||||
|
@ -1,22 +1,33 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Actions, ofType, createEffect } from '@ngrx/effects';
|
||||
import { State } from '../reducers';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { flatMap, catchError, map } from 'rxjs/operators';
|
||||
import {
|
||||
flatMap,
|
||||
catchError,
|
||||
map,
|
||||
mergeMap,
|
||||
withLatestFrom,
|
||||
concatMap,
|
||||
} from 'rxjs/operators';
|
||||
import { EMPTY, of } from 'rxjs';
|
||||
import {
|
||||
getDimensions,
|
||||
getSelectedDimension,
|
||||
getDimensionSuccess,
|
||||
addNewDimension,
|
||||
removeDimension,
|
||||
getSelectedDimensionSuccess,
|
||||
} from '../actions/dimension.actions';
|
||||
import { DimensionsService } from '../services/dimensions.service';
|
||||
import { addNewDimensionSuccess } from '../actions/dimension.actions';
|
||||
import { State } from '../reducers';
|
||||
import { Store } from '@ngrx/store';
|
||||
|
||||
@Injectable()
|
||||
export class DimmensionEffects {
|
||||
constructor(
|
||||
private actions$: Actions,
|
||||
private dimensionService: DimensionsService
|
||||
private dimensionService: DimensionsService,
|
||||
private store$: Store<State>
|
||||
) {}
|
||||
|
||||
getDimmensions$ = createEffect(() =>
|
||||
@ -25,7 +36,6 @@ export class DimmensionEffects {
|
||||
flatMap(() =>
|
||||
this.dimensionService.getDimensions().pipe(
|
||||
map((result) => {
|
||||
console.log(result);
|
||||
return getDimensionSuccess({ dimensions: result });
|
||||
}),
|
||||
catchError(() => EMPTY)
|
||||
@ -38,11 +48,35 @@ export class DimmensionEffects {
|
||||
this.actions$.pipe(
|
||||
ofType(addNewDimension),
|
||||
flatMap((action) => {
|
||||
console.log(action.dimension);
|
||||
return this.dimensionService
|
||||
.addDimension(action.dimension)
|
||||
.pipe(map(() => addNewDimensionSuccess()));
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
getSelectedDimension$ = createEffect(() =>
|
||||
this.actions$.pipe(
|
||||
ofType(getSelectedDimension),
|
||||
withLatestFrom(
|
||||
this.store$.select((state) => state.dimension.selectedDimension)
|
||||
),
|
||||
concatMap(([_, id]) =>
|
||||
this.dimensionService.getDimension(id!).pipe(
|
||||
map((result) => {
|
||||
return getSelectedDimensionSuccess({ categories: result });
|
||||
})
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
deleteDimension$ = createEffect(
|
||||
() =>
|
||||
this.actions$.pipe(
|
||||
ofType(removeDimension),
|
||||
flatMap((action) => this.dimensionService.removeDimension(action.id))
|
||||
),
|
||||
{ dispatch: false }
|
||||
);
|
||||
}
|
||||
|
@ -0,0 +1,37 @@
|
||||
<div class="editDimension__goBackButtonContainer" (click)="goToMainPage()">
|
||||
<button nbButton status="info">Wróc do strony głównej</button>
|
||||
</div>
|
||||
<form [formGroup]="editDimensionForm" (ngSubmit)="onSubmit()">
|
||||
<nb-layout>
|
||||
<nb-layout-column class="editDimension__leftColumn">
|
||||
<h3>Edytuj wymiar</h3>
|
||||
<input
|
||||
fullWidth
|
||||
nbInput
|
||||
placeholder="Nazwa wymiaru"
|
||||
formControlName="name"
|
||||
class="editDimension__name"
|
||||
/>
|
||||
<textarea nbInput fullWidth placeholder="Opis wymiaru"></textarea>
|
||||
</nb-layout-column>
|
||||
<nb-layout-column>
|
||||
<button nbButton outline status="info" class="editDimension__button">
|
||||
Zapisz
|
||||
</button>
|
||||
<button nbButton outline status="danger" class="editDimension__button">
|
||||
Usuń wymiar
|
||||
</button>
|
||||
<nb-card>
|
||||
<nb-card-header>Kategorie</nb-card-header>
|
||||
<nb-list>
|
||||
<nb-list-item
|
||||
*ngFor="let item of categories.controls; let i = index"
|
||||
formArrayName="categories"
|
||||
>
|
||||
<input nbInput type="text" fullWidth [formControlName]="i" />
|
||||
</nb-list-item>
|
||||
</nb-list>
|
||||
</nb-card>
|
||||
</nb-layout-column>
|
||||
</nb-layout>
|
||||
</form>
|
@ -0,0 +1,13 @@
|
||||
.editDimension {
|
||||
&__leftColumn {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
&__button {
|
||||
margin: 30px 10px 30px 0;
|
||||
}
|
||||
&__goBackButtonContainer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||
import {
|
||||
FormBuilder,
|
||||
FormArray,
|
||||
Validators,
|
||||
FormControl,
|
||||
FormGroup,
|
||||
} from '@angular/forms';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { State } from 'src/app/reducers';
|
||||
import {
|
||||
clearSelectedDimension,
|
||||
getSelectedDimension,
|
||||
} from 'src/app/actions/dimension.actions';
|
||||
import {
|
||||
selectCategories,
|
||||
selectCurrentDimension,
|
||||
} from 'src/app/selectors/dimension.selectors';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
|
||||
@Component({
|
||||
selector: 'edit-dimension',
|
||||
templateUrl: './edit-dimension.component.html',
|
||||
styleUrls: ['./edit-dimension.component.scss'],
|
||||
})
|
||||
export class EditDimensionComponent implements OnInit, OnDestroy {
|
||||
editDimensionForm = this.fb.group({
|
||||
name: ['', Validators.required],
|
||||
categories: this.fb.array([]),
|
||||
});
|
||||
|
||||
constructor(
|
||||
private fb: FormBuilder,
|
||||
private store: Store<State>,
|
||||
private router: Router,
|
||||
private route: ActivatedRoute
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.store.dispatch(getSelectedDimension());
|
||||
this.store
|
||||
.select(selectCurrentDimension)
|
||||
.subscribe((result) =>
|
||||
this.editDimensionForm.patchValue({ name: result?.name })
|
||||
);
|
||||
this.store.select(selectCategories).subscribe((res) => {
|
||||
res.forEach((el) => this.addCategory(el.category));
|
||||
});
|
||||
}
|
||||
|
||||
get name() {
|
||||
return this.editDimensionForm.get('name') as FormControl;
|
||||
}
|
||||
|
||||
get categories() {
|
||||
return this.editDimensionForm.get('categories') as FormArray;
|
||||
}
|
||||
|
||||
addCategory(value: string) {
|
||||
this.categories.push(this.fb.control(value, Validators.required));
|
||||
}
|
||||
|
||||
onSubmit() {}
|
||||
|
||||
goToMainPage() {
|
||||
this.router.navigate(['../'], { relativeTo: this.route });
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.store.dispatch(clearSelectedDimension());
|
||||
this.editDimensionForm.reset();
|
||||
}
|
||||
}
|
@ -6,14 +6,16 @@
|
||||
class="dimension__list"
|
||||
>
|
||||
<div>
|
||||
<span class="subtitle dimension__name">{{ item.name }}</span>
|
||||
<span class="subtitle dimension__name" (click)="editDimension(i + 1)">{{
|
||||
item.name
|
||||
}}</span>
|
||||
</div>
|
||||
<div>
|
||||
<nb-icon
|
||||
icon="close-outline"
|
||||
status="danger"
|
||||
class="dimension__close"
|
||||
(click)="removeDimension(i)"
|
||||
(click)="removeDimension(i + 1)"
|
||||
></nb-icon>
|
||||
</div>
|
||||
</nb-list-item>
|
||||
|
@ -2,13 +2,17 @@ import { Component, OnInit } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { State } from '../../reducers';
|
||||
import { getDimensions } from '../../actions/dimension.actions';
|
||||
import {
|
||||
getDimensions,
|
||||
removeDimension,
|
||||
setSelectedDimension,
|
||||
} from '../../actions/dimension.actions';
|
||||
import { Dimension } from 'src/app/reducers/dimension.reducers';
|
||||
import { removeDimension } from 'src/app/actions/dimension.actions';
|
||||
import { selectDimensions } from '../../selectors/dimension.selectors';
|
||||
import { NbDialogService } from '@nebular/theme';
|
||||
import { AddDimensionDialogComponent } from '../add-dimension-dialog/add-dimension-dialog.component';
|
||||
import { DeleteDialogComponent } from '../delete-dialog/delete-dialog.component';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
|
||||
@Component({
|
||||
selector: 'manage-dimensions',
|
||||
@ -18,13 +22,16 @@ import { DeleteDialogComponent } from '../delete-dialog/delete-dialog.component'
|
||||
export class ManageDimensionsComponent implements OnInit {
|
||||
constructor(
|
||||
private store: Store<State>,
|
||||
private dialogService: NbDialogService
|
||||
private dialogService: NbDialogService,
|
||||
private router: Router,
|
||||
private route: ActivatedRoute
|
||||
) {}
|
||||
|
||||
data$: Observable<Array<Dimension>> = this.store.select(selectDimensions);
|
||||
|
||||
ngOnInit(): void {
|
||||
this.store.dispatch(getDimensions());
|
||||
this.store.select(selectDimensions).subscribe((res) => console.log(res));
|
||||
}
|
||||
|
||||
removeDimension(id: number) {
|
||||
@ -38,4 +45,9 @@ export class ManageDimensionsComponent implements OnInit {
|
||||
openAddDialog(): void {
|
||||
this.dialogService.open(AddDimensionDialogComponent);
|
||||
}
|
||||
|
||||
editDimension(id: number): void {
|
||||
this.store.dispatch(setSelectedDimension({ id }));
|
||||
this.router.navigate([`dimension`], { relativeTo: this.route });
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { Routes, RouterModule } from '@angular/router';
|
||||
import { EditDimensionComponent } from './edit-dimension/edit-dimension.component';
|
||||
import { MainViewComponent } from './main-view/main-view.component';
|
||||
import { MyProfileComponent } from './my-profile.component';
|
||||
|
||||
@ -19,6 +20,10 @@ const routes: Routes = [
|
||||
(m) => m.DiscussionViewerModule
|
||||
),
|
||||
},
|
||||
{
|
||||
path: 'dimension',
|
||||
component: EditDimensionComponent,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
@ -25,6 +25,7 @@ import { MainViewComponent } from './main-view/main-view.component';
|
||||
import { DimensionsService } from '../services/dimensions.service';
|
||||
import { AddDimensionDialogComponent } from './add-dimension-dialog/add-dimension-dialog.component';
|
||||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
import { EditDimensionComponent } from './edit-dimension/edit-dimension.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
@ -34,6 +35,7 @@ import { ReactiveFormsModule } from '@angular/forms';
|
||||
ManageDimensionsComponent,
|
||||
MainViewComponent,
|
||||
AddDimensionDialogComponent,
|
||||
EditDimensionComponent,
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
|
@ -3,22 +3,32 @@ import {
|
||||
getDimensionSuccess,
|
||||
addNewDimension,
|
||||
removeDimension,
|
||||
setSelectedDimension,
|
||||
getSelectedDimensionSuccess,
|
||||
clearSelectedDimension,
|
||||
} from '../actions/dimension.actions';
|
||||
import { createEntityAdapter, EntityAdapter, EntityState } from '@ngrx/entity';
|
||||
|
||||
export interface Dimension {
|
||||
name: string;
|
||||
categories: Array<string>;
|
||||
id: string;
|
||||
}
|
||||
|
||||
export interface Category {
|
||||
id: string;
|
||||
category: string;
|
||||
}
|
||||
|
||||
export interface DimensionsState extends EntityState<Dimension> {
|
||||
selectedDimension: number | null;
|
||||
availableCategories: Array<Category>;
|
||||
}
|
||||
|
||||
export const adapter: EntityAdapter<Dimension> = createEntityAdapter<Dimension>();
|
||||
|
||||
export const initialState: DimensionsState = adapter.getInitialState({
|
||||
selectedDimension: null,
|
||||
availableCategories: [],
|
||||
});
|
||||
|
||||
const _dimensionsReducer = createReducer(
|
||||
@ -31,7 +41,19 @@ const _dimensionsReducer = createReducer(
|
||||
),
|
||||
on(removeDimension, (state, { id }) =>
|
||||
adapter.removeOne(id.toString(), state)
|
||||
)
|
||||
),
|
||||
on(setSelectedDimension, (state, { id }) => ({
|
||||
...state,
|
||||
selectedDimension: id,
|
||||
})),
|
||||
on(getSelectedDimensionSuccess, (state, { categories }) => ({
|
||||
...state,
|
||||
availableCategories: categories,
|
||||
})),
|
||||
on(clearSelectedDimension, (state) => ({
|
||||
...state,
|
||||
availableCategories: [],
|
||||
}))
|
||||
);
|
||||
|
||||
export function dimensionsReducer(
|
||||
|
@ -15,6 +15,7 @@ import {
|
||||
AvailableForumsState,
|
||||
} from './available-forums.reducers';
|
||||
import { DimensionsState, dimensionsReducer } from './dimension.reducers';
|
||||
import { localStorageSync } from 'ngrx-store-localstorage';
|
||||
|
||||
export function debug(reducer: ActionReducer<any>): ActionReducer<any> {
|
||||
return function (state, action) {
|
||||
@ -43,9 +44,18 @@ export const reducers: ActionReducerMap<State> = {
|
||||
dimension: dimensionsReducer,
|
||||
};
|
||||
|
||||
export function localStorageSyncReducer(
|
||||
reducer: ActionReducer<any>
|
||||
): ActionReducer<any> {
|
||||
return localStorageSync({
|
||||
keys: Object.keys(reducers).filter((key) => key !== 'router'),
|
||||
rehydrate: true,
|
||||
})(reducer);
|
||||
}
|
||||
|
||||
export const metaReducers: MetaReducer<State>[] = !environment.production
|
||||
? [debug]
|
||||
: [];
|
||||
? [debug, localStorageSyncReducer]
|
||||
: [localStorageSyncReducer];
|
||||
|
||||
export const selectRouter = createFeatureSelector<
|
||||
State,
|
||||
|
@ -6,6 +6,23 @@ export const selectFeature = createFeatureSelector<State, DimensionsState>(
|
||||
'dimension'
|
||||
);
|
||||
|
||||
const { selectAll } = adapter.getSelectors();
|
||||
const { selectAll, selectEntities } = adapter.getSelectors();
|
||||
|
||||
export const selectDimensions = createSelector(selectFeature, selectAll);
|
||||
export const selectDimensionsEntities = createSelector(
|
||||
selectFeature,
|
||||
selectEntities
|
||||
);
|
||||
export const selectDimension = createSelector(
|
||||
selectFeature,
|
||||
(state) => state.selectedDimension
|
||||
);
|
||||
export const selectCategories = createSelector(
|
||||
selectFeature,
|
||||
(state) => state.availableCategories
|
||||
);
|
||||
export const selectCurrentDimension = createSelector(
|
||||
selectDimensionsEntities,
|
||||
selectDimension,
|
||||
(entities, id) => entities[id!]
|
||||
);
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { EMPTY, Observable } from 'rxjs';
|
||||
import { Dimension } from '../reducers/dimension.reducers';
|
||||
import { Category, Dimension } from '../reducers/dimension.reducers';
|
||||
import { map } from 'rxjs/operators';
|
||||
|
||||
@Injectable()
|
||||
@ -14,10 +14,20 @@ export class DimensionsService {
|
||||
.pipe(map((res) => res.data));
|
||||
}
|
||||
|
||||
getDimension(id: number): Observable<Array<Category>> {
|
||||
return this.http
|
||||
.get<any>(`http://127.0.0.1:8000/dimension/${id}`)
|
||||
.pipe(map((res) => res.data));
|
||||
}
|
||||
|
||||
addDimension(dimension: Dimension): Observable<any> {
|
||||
const body = JSON.stringify(dimension);
|
||||
return this.http.post<any>('http://127.0.0.1:8000/dimension/', body, {
|
||||
responseType: 'json',
|
||||
});
|
||||
}
|
||||
|
||||
removeDimension(id: number): Observable<any> {
|
||||
return this.http.delete<any>('http://127.0.0.1:8000/dimension');
|
||||
}
|
||||
}
|
||||
|
29
frontend/src/app/services/storage.service.ts
Normal file
29
frontend/src/app/services/storage.service.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import { Inject, Injectable, InjectionToken } from '@angular/core';
|
||||
|
||||
export const BROWSER_STORAGE = new InjectionToken<Storage>('Browser Storage', {
|
||||
providedIn: 'root',
|
||||
factory: () => localStorage,
|
||||
});
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class BrowserStorageService {
|
||||
constructor(@Inject(BROWSER_STORAGE) public storage: Storage) {}
|
||||
|
||||
get(key: string) {
|
||||
return this.storage.getItem(key);
|
||||
}
|
||||
|
||||
set(key: string, value: string) {
|
||||
this.storage.setItem(key, value);
|
||||
}
|
||||
|
||||
remove(key: string) {
|
||||
this.storage.removeItem(key);
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.storage.clear();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user