diff --git a/frontend/package-lock.json b/frontend/package-lock.json index fe4e379..606bccd 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -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", diff --git a/frontend/package.json b/frontend/package.json index c5f13ea..418ddeb 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -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", diff --git a/frontend/src/app/actions/dimension.actions.ts b/frontend/src/app/actions/dimension.actions.ts index f4948f8..42a8149 100644 --- a/frontend/src/app/actions/dimension.actions.ts +++ b/frontend/src/app/actions/dimension.actions.ts @@ -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 }>() ); +export const getSelectedDimension = createAction( + '[Dimension] Get Selected Dimension' +); +export const getSelectedDimensionSuccess = createAction( + '[Dimension] Get Selected Dimension Success', + props<{ categories: Array }>() +); 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' +); diff --git a/frontend/src/app/effects/dimmension.effects.ts b/frontend/src/app/effects/dimmension.effects.ts index 620f306..600599a 100644 --- a/frontend/src/app/effects/dimmension.effects.ts +++ b/frontend/src/app/effects/dimmension.effects.ts @@ -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 ) {} 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 } + ); } diff --git a/frontend/src/app/my-profile/edit-dimension/edit-dimension.component.html b/frontend/src/app/my-profile/edit-dimension/edit-dimension.component.html new file mode 100644 index 0000000..c30d987 --- /dev/null +++ b/frontend/src/app/my-profile/edit-dimension/edit-dimension.component.html @@ -0,0 +1,37 @@ +
+ +
+
+ + +

Edytuj wymiar

+ + +
+ + + + + Kategorie + + + + + + + +
+
diff --git a/frontend/src/app/my-profile/edit-dimension/edit-dimension.component.scss b/frontend/src/app/my-profile/edit-dimension/edit-dimension.component.scss new file mode 100644 index 0000000..6c104c7 --- /dev/null +++ b/frontend/src/app/my-profile/edit-dimension/edit-dimension.component.scss @@ -0,0 +1,13 @@ +.editDimension { + &__leftColumn { + display: flex; + flex-direction: column; + } + &__button { + margin: 30px 10px 30px 0; + } + &__goBackButtonContainer { + display: flex; + justify-content: flex-end; + } +} diff --git a/frontend/src/app/my-profile/edit-dimension/edit-dimension.component.ts b/frontend/src/app/my-profile/edit-dimension/edit-dimension.component.ts new file mode 100644 index 0000000..f52f91c --- /dev/null +++ b/frontend/src/app/my-profile/edit-dimension/edit-dimension.component.ts @@ -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, + 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(); + } +} diff --git a/frontend/src/app/my-profile/manage-dimensions/manage-dimensions.component.html b/frontend/src/app/my-profile/manage-dimensions/manage-dimensions.component.html index 7cbec8a..a1caad4 100644 --- a/frontend/src/app/my-profile/manage-dimensions/manage-dimensions.component.html +++ b/frontend/src/app/my-profile/manage-dimensions/manage-dimensions.component.html @@ -6,14 +6,16 @@ class="dimension__list" >
- {{ item.name }} + {{ + item.name + }}
diff --git a/frontend/src/app/my-profile/manage-dimensions/manage-dimensions.component.ts b/frontend/src/app/my-profile/manage-dimensions/manage-dimensions.component.ts index 92254f6..98a1bf8 100644 --- a/frontend/src/app/my-profile/manage-dimensions/manage-dimensions.component.ts +++ b/frontend/src/app/my-profile/manage-dimensions/manage-dimensions.component.ts @@ -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, - private dialogService: NbDialogService + private dialogService: NbDialogService, + private router: Router, + private route: ActivatedRoute ) {} data$: Observable> = 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 }); + } } diff --git a/frontend/src/app/my-profile/my-profile-routing.module.ts b/frontend/src/app/my-profile/my-profile-routing.module.ts index 56d482e..532664c 100644 --- a/frontend/src/app/my-profile/my-profile-routing.module.ts +++ b/frontend/src/app/my-profile/my-profile-routing.module.ts @@ -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, + }, ], }, ]; diff --git a/frontend/src/app/my-profile/my-profile.module.ts b/frontend/src/app/my-profile/my-profile.module.ts index fff1961..0838323 100644 --- a/frontend/src/app/my-profile/my-profile.module.ts +++ b/frontend/src/app/my-profile/my-profile.module.ts @@ -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, diff --git a/frontend/src/app/reducers/dimension.reducers.ts b/frontend/src/app/reducers/dimension.reducers.ts index aa2f694..857f7b0 100644 --- a/frontend/src/app/reducers/dimension.reducers.ts +++ b/frontend/src/app/reducers/dimension.reducers.ts @@ -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; + id: string; +} + +export interface Category { + id: string; + category: string; } export interface DimensionsState extends EntityState { selectedDimension: number | null; + availableCategories: Array; } export const adapter: EntityAdapter = createEntityAdapter(); 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( diff --git a/frontend/src/app/reducers/index.ts b/frontend/src/app/reducers/index.ts index aa79443..d22ae2a 100644 --- a/frontend/src/app/reducers/index.ts +++ b/frontend/src/app/reducers/index.ts @@ -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): ActionReducer { return function (state, action) { @@ -43,9 +44,18 @@ export const reducers: ActionReducerMap = { dimension: dimensionsReducer, }; +export function localStorageSyncReducer( + reducer: ActionReducer +): ActionReducer { + return localStorageSync({ + keys: Object.keys(reducers).filter((key) => key !== 'router'), + rehydrate: true, + })(reducer); +} + export const metaReducers: MetaReducer[] = !environment.production - ? [debug] - : []; + ? [debug, localStorageSyncReducer] + : [localStorageSyncReducer]; export const selectRouter = createFeatureSelector< State, diff --git a/frontend/src/app/selectors/dimension.selectors.ts b/frontend/src/app/selectors/dimension.selectors.ts index 4cdc773..958e724 100644 --- a/frontend/src/app/selectors/dimension.selectors.ts +++ b/frontend/src/app/selectors/dimension.selectors.ts @@ -6,6 +6,23 @@ export const selectFeature = createFeatureSelector( '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!] +); diff --git a/frontend/src/app/services/dimensions.service.ts b/frontend/src/app/services/dimensions.service.ts index b2fe901..a1dd444 100644 --- a/frontend/src/app/services/dimensions.service.ts +++ b/frontend/src/app/services/dimensions.service.ts @@ -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> { + return this.http + .get(`http://127.0.0.1:8000/dimension/${id}`) + .pipe(map((res) => res.data)); + } + addDimension(dimension: Dimension): Observable { const body = JSON.stringify(dimension); return this.http.post('http://127.0.0.1:8000/dimension/', body, { responseType: 'json', }); } + + removeDimension(id: number): Observable { + return this.http.delete('http://127.0.0.1:8000/dimension'); + } } diff --git a/frontend/src/app/services/storage.service.ts b/frontend/src/app/services/storage.service.ts new file mode 100644 index 0000000..6f28d6b --- /dev/null +++ b/frontend/src/app/services/storage.service.ts @@ -0,0 +1,29 @@ +import { Inject, Injectable, InjectionToken } from '@angular/core'; + +export const BROWSER_STORAGE = new InjectionToken('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(); + } +}