Skip to content

Commit

Permalink
Merge pull request #79 from pkorchak/add-save-button
Browse files Browse the repository at this point in the history
feat: Add form save button
  • Loading branch information
pkorchak committed Oct 22, 2023
2 parents cf976bc + 7849145 commit 8722903
Show file tree
Hide file tree
Showing 15 changed files with 147 additions and 41 deletions.
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"version": "0.0.1",
"scripts": {
"ng": "ng",
"start": "set NODE_OPTIONS=--openssl-legacy-provider && ng serve --proxy-config=proxy.config.json",
"start": "ng serve --proxy-config=proxy.config.json",
"build": "ng build --configuration production",
"lint": "ng lint",
"test": "ng test",
Expand Down
4 changes: 2 additions & 2 deletions proxy.config.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"/api/*": {
"target": " https://03z6w06wwd.execute-api.us-east-1.amazonaws.com/Stage",
"secure": false,
"target": "https://aq4isfk378.execute-api.us-east-1.amazonaws.com/Prod",
"secure": true,
"changeOrigin": true,
"pathRewrite": {
"^/api": ""
Expand Down
16 changes: 12 additions & 4 deletions src/app/app-routing.module.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { RouterModule, Routes } from '@angular/router';
import { AuthComponent } from '@widgets/auth/auth.component';

const routes: Routes = [
{path: '', pathMatch: 'full', redirectTo: '/create'},
{path: 'pre-built', loadChildren: () => import('./pages/pre-built-forms/pre-built-forms.module').then(m => m.PreBuiltFormsModule)},
{path: 'create', loadChildren: () => import('./pages/create-form/create-form.module').then(m => m.CreateFormModule)}
{ path: '', pathMatch: 'full', redirectTo: '/create' },
{ path: 'login', component: AuthComponent },
{
path: 'pre-built',
loadChildren: () => import('./pages/pre-built-forms/pre-built-forms.module').then(m => m.PreBuiltFormsModule)
},
{
path: 'create',
loadChildren: () => import('./pages/create-form/create-form.module').then(m => m.CreateFormModule)
}
];

@NgModule({
Expand Down
7 changes: 7 additions & 0 deletions src/app/model/dto/rq/form-create-rq-dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { FormElement } from '@model/form-elements/abstract-form-element';

export interface FormCreateRqDto {
name: string;
columnsNum: number;
elements: FormElement[];
}
4 changes: 4 additions & 0 deletions src/app/model/dto/rs/form-create-rs-dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export interface FormCreateRsDto {
uuid?: string;
message: string;
}
13 changes: 13 additions & 0 deletions src/app/services/api/forms-http.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { HttpService } from './http.service';
import { FormCreateRqDto } from '@model/dto/rq/form-create-rq-dto';
import { FormCreateRsDto } from '@model/dto/rs/form-create-rs-dto';

@Injectable({ providedIn: 'root' })
export class FormsHttpService extends HttpService {

public create(user: FormCreateRqDto): Observable<FormCreateRsDto> {
return this.post('/api/forms', user) as Observable<FormCreateRsDto>;
}
}
20 changes: 13 additions & 7 deletions src/app/services/api/http.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,23 @@ import { Observable } from 'rxjs';
import { Router } from '@angular/router';
import { catchError } from 'rxjs/operators';

@Injectable({providedIn: 'root'})
@Injectable({ providedIn: 'root' })
export class HttpService {

private headers: HttpHeaders = new HttpHeaders({'Content-Type': 'application/json'});

constructor(private http: HttpClient,
private router: Router) {
}

public post(url: string, body: unknown, params?: HttpParams): Observable<unknown> {
return this.handleResponse(this.http.post(url, body, {headers: this.headers, ...params}));
return this.handleResponse(this.http.post(url, body, { headers: this.buildHeaders(), ...params }));
}

public get(url: string, params?: HttpParams): Observable<unknown> {
return this.handleResponse(this.http.get(url, {headers: this.headers, ...params}));
return this.handleResponse(this.http.get(url, { headers: this.buildHeaders(), ...params }));
}

public patch(url: string, body: unknown, params?: HttpParams): Observable<unknown> {
return this.handleResponse(this.http.patch(url, body, {headers: this.headers, ...params}));
return this.handleResponse(this.http.patch(url, body, { headers: this.buildHeaders(), ...params }));
}

private handleResponse(response: Observable<unknown>): Observable<unknown> {
Expand All @@ -34,6 +32,14 @@ export class HttpService {
this.router.navigateByUrl('/login');
}
// TODO Show notification with error text for the user
throw new Error(`${error.statusText}\n${error.message}`);
throw new Error(`${ error.statusText }\n${ error.message }`);
}

private buildHeaders() {
const headers = {};
const token = localStorage.getItem('token');
if (token) headers['Authorization'] = token;

return headers;
}
}
6 changes: 4 additions & 2 deletions src/app/widgets/auth/auth.component.less
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
@import '~app/styles/global/_colors.less';

:host {
position: absolute;
position: fixed;
top: 0;
left: 0;
display: flex;
align-items: center;
z-index: 100;
Expand All @@ -15,7 +17,7 @@
height: fit-content;
width: 360px;
padding: 30px;
border-radius: 6px;
border-radius: 6px;
border: 1px solid #e8e8e8;
box-shadow: 0 3px 3px 3px rgba(0, 0, 0, 0.05);
}
Expand Down
55 changes: 34 additions & 21 deletions src/app/widgets/auth/auth.component.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { SocialAuthService, SocialUser } from '@abacritt/angularx-social-login';
import { Component, EventEmitter, OnInit, Output } from '@angular/core';
import { Component, DestroyRef, EventEmitter, inject, OnInit, Output } from '@angular/core';
import { IdentityProvider, RegisterUserRqDto } from '@model/dto/rq/register-user-rq-dto';
import { RegisterUserRsDto } from '@model/dto/rs/register-user-rs-dto';
import { UsersHttpService } from '@services/api/users-http.service';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { ActivatedRoute, Router } from '@angular/router';

@Component({
selector: 'app-auth',
Expand All @@ -13,9 +15,13 @@ export class AuthComponent implements OnInit {

@Output() signedIn = new EventEmitter();

private destroyRef = inject(DestroyRef);

constructor(
private socialAuthService: SocialAuthService,
private usersHttpService: UsersHttpService) {
private usersHttpService: UsersHttpService,
private route: ActivatedRoute,
private router: Router) {
}

ngOnInit() {
Expand All @@ -24,27 +30,34 @@ export class AuthComponent implements OnInit {
email: ['', Validators.required],
password: ['', Validators.required],
});*/
this.socialAuthService.authState.subscribe((user: SocialUser) => {
if (user && user.idToken != localStorage.getItem('token')) {
localStorage.setItem('token', user.idToken);

this.usersHttpService.register({
firstName: user.firstName,
lastName: user.lastName,
email: user.email,
photoUrl: user.photoUrl,
provider: user.provider as IdentityProvider
} as RegisterUserRqDto)
.subscribe((rs: RegisterUserRsDto) => {
localStorage.setItem('userId', rs.id || '');
this.signedIn.emit();
});
}
});
this.socialAuthService.authState
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((user: SocialUser) => {
if (user && user.idToken != localStorage.getItem('token')) {
localStorage.setItem('token', user.idToken);

this.usersHttpService.register({
firstName: user.firstName,
lastName: user.lastName,
email: user.email,
photoUrl: user.photoUrl,
provider: user.provider as IdentityProvider
} as RegisterUserRqDto)
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((rs: RegisterUserRsDto) => {
localStorage.setItem('userId', rs.id || '');
this.signedIn.emit();

if (this.route.snapshot.url.toString().includes('login')) {
this.router.navigateByUrl('/');
}
});
}
});
}

signOut(): void {
this.socialAuthService.signOut();
localStorage.removeItem('token');
this.socialAuthService.signOut()
.then(() => localStorage.removeItem('token'));
}
}
11 changes: 10 additions & 1 deletion src/app/widgets/form-editor/form-editor.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,21 @@

<button nz-button
class="final-action-btn"
nzType="primary"
nzType="secondary"
nzShape="round"
[disabled]="form.invalid"
(click)="showFormPreviewClick.emit(form.value)">
<i nz-icon nzType="eye"></i> Show form preview
</button>

<button nz-button
class="final-action-btn save-btn"
nzType="primary"
nzShape="round"
[disabled]="form.invalid"
(click)="save()">
<i nz-icon nzType="save"></i> Save
</button>
</app-form-container>

<app-settings-drawer>
Expand Down
5 changes: 5 additions & 0 deletions src/app/widgets/form-editor/form-editor.component.less
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,8 @@
font-size: 13px;
margin-bottom: 6px;
}

.save-btn {
float: right;
font-size: 1rem;
}
4 changes: 3 additions & 1 deletion src/app/widgets/form-editor/form-editor.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { MockComponents } from 'ng-mocks';
import { By } from '@angular/platform-browser';
import { ChangeDetectionStrategy, DebugElement } from '@angular/core';
import { AbstractFormElement } from '@model/form-elements/abstract-form-element';
import { HttpClientTestingModule } from '@angular/common/http/testing';

describe('FormEditorComponent', () => {
let component: FormEditorComponent;
Expand Down Expand Up @@ -42,7 +43,8 @@ describe('FormEditorComponent', () => {
],
imports: [
ReactiveFormsModule,
NzInputNumberModule
NzInputNumberModule,
HttpClientTestingModule
]
})
.overrideComponent(FormEditorComponent, {
Expand Down
38 changes: 37 additions & 1 deletion src/app/widgets/form-editor/form-editor.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
DestroyRef,
EventEmitter,
inject,
Input,
OnChanges,
OnInit,
Expand All @@ -17,6 +19,10 @@ import {
EditableFormElementComponent,
FormElementControls
} from '@widgets/form-editor/element/editable-form-element.component';
import { FormsHttpService } from '@services/api/forms-http.service';
import { FormCreateRqDto } from '@model/dto/rq/form-create-rq-dto';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { FormElement } from '@model/form-elements/abstract-form-element';

@Component({
selector: 'app-form-editor',
Expand All @@ -42,8 +48,11 @@ export class FormEditorComponent implements OnInit, OnChanges {
columnWidth: string;
formElements = new FormArray<FormGroup<FormElementControls>>([]);

private destroyRef = inject(DestroyRef);

constructor(private fb: FormBuilder,
private cdr: ChangeDetectorRef) {
private cdr: ChangeDetectorRef,
private formsHttpService: FormsHttpService) {
}

ngOnInit(): void {
Expand Down Expand Up @@ -92,6 +101,33 @@ export class FormEditorComponent implements OnInit, OnChanges {
deleteElement(index: number): void {
this.formElements.removeAt(index);
}

save(): void {
this.formsHttpService.create(this.mapFormCreateRqDto())
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(() => {
// TODO Show notification with save result
});
}

private mapFormCreateRqDto(): FormCreateRqDto {
return {
name: this.formName.value,
columnsNum: this.columnsNum.value,
elements: [
...this.formElements.controls.map(element => this.mapFormElement(element))
]
} as FormCreateRqDto
}

private mapFormElement(element: FormGroup<FormElementControls>): FormElement {
return {
label: element.get('label')?.value,
type: element.get('type')?.value,
required: element.get('required')?.value || undefined,
placeholder: element.get('placeholder')?.value || undefined,
} as FormElement;
}
}

interface FormEditorControls {
Expand Down
1 change: 1 addition & 0 deletions src/assets/outline/save.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 8722903

Please sign in to comment.