mirror of
https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend
synced 2025-12-28 22:42:11 +01:00
Merge with develop
This commit is contained in:
1447
mocks/users.mock.ts
1447
mocks/users.mock.ts
File diff suppressed because it is too large
Load Diff
@@ -21,6 +21,7 @@
|
||||
"@angular/router": "~7.2.0",
|
||||
"@ngxs/store": "^3.3.4",
|
||||
"angular2-signaturepad": "^2.8.0",
|
||||
"@zxing/ngx-scanner": "^1.3.0",
|
||||
"core-js": "^2.5.4",
|
||||
"ngx-infinite-scroll": "^7.0.1",
|
||||
"rxjs": "~6.3.3",
|
||||
|
||||
@@ -22,9 +22,8 @@ import { BreadcrumbsState } from './core/store/state/breadcrumbs.state';
|
||||
import { FilterState } from './core/store/state/filter.state';
|
||||
import { CheckoutComponent } from './components/checkout/checkout.component';
|
||||
import { ModalComponent } from './shared/components/modal/modal.component';
|
||||
import { CustomerSearchComponent } from './components/customer-search/customer-search.component';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { CustomerSearchResultComponent } from './components/customer-search-result/customer-search-result.component';
|
||||
import { CustomerSearchModule } from './components/customer-search/customer-search.module';
|
||||
|
||||
const states = [
|
||||
FeedState,
|
||||
@@ -62,8 +61,6 @@ export function _feedServiceEndpointProviderFactory(conf: ConfigService) {
|
||||
ProductDetailsComponent,
|
||||
CheckoutComponent,
|
||||
ModalComponent,
|
||||
CustomerSearchComponent,
|
||||
CustomerSearchResultComponent,
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
@@ -76,6 +73,7 @@ export function _feedServiceEndpointProviderFactory(conf: ConfigService) {
|
||||
CatServiceModule,
|
||||
FeedServiceModule,
|
||||
FormsModule,
|
||||
CustomerSearchModule
|
||||
],
|
||||
providers: [
|
||||
{ provide: APP_INITIALIZER, useFactory: _configInitializer, multi: true, deps: [ ConfigService ] },
|
||||
|
||||
@@ -19,9 +19,12 @@ export class CheckoutComponent implements OnInit {
|
||||
|
||||
// Step one mock data
|
||||
locations = [
|
||||
{ name: 'Filliale Stachus', date: '18.02.2019'},
|
||||
{ name: 'Filliale Berlin', date: '20.02.2019'},
|
||||
{ name: 'Filliale Frankfurt', date: '22.02.2019'}
|
||||
{ name: 'München Karlsplatz', date: '01.05.2019'},
|
||||
{ name: 'München Marienplatz', date: '01.05.2019'},
|
||||
{ name: 'München Ollenhauerstraße', date: '01.05.2019'},
|
||||
{ name: 'München Pasing Bahnhofsplatz', date: '01.05.2019'},
|
||||
{ name: 'München Theatinerstraße', date: '01.05.2019'}
|
||||
|
||||
];
|
||||
currentLocation = this.locations[0];
|
||||
currentPickUpDate = this.locations[0].date;
|
||||
@@ -127,7 +130,6 @@ export class CheckoutComponent implements OnInit {
|
||||
|
||||
switchSteps(reset: boolean = false) {
|
||||
if (reset) {
|
||||
console.log('Should swtich');
|
||||
this.defaultValues();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
<div class="customer-section-create">
|
||||
<div class="customer-section-create-title">
|
||||
<span>Kundendaten erfassen</span>
|
||||
</div>
|
||||
<div class="align-center customer-section-create-description">
|
||||
<span>Damit wir Ihnen die Bestelldetails zukommen lassen können.</span>
|
||||
</div>
|
||||
<div class="create-wrapper">
|
||||
<form class="form" [formGroup]="userForm">
|
||||
<div class="form-group">
|
||||
<input type="text" id="name" class="input form-control" formControlName="firstName" [ngClass]="{ 'error-visible': submitted && f.firstName.errors }" required autofocus>
|
||||
<label class="placeholder form-control-placeholder" for="name">Vorname</label>
|
||||
<div *ngIf="submitted && f.firstName.errors" class="invalid-feedback">
|
||||
<div>Vorname wird benötigt</div>
|
||||
</div>
|
||||
<div class="line"></div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="text" id="lastName" class="input form-control" formControlName="lastName" [ngClass]="{ 'error-visible': submitted && f.lastName.errors }" required>
|
||||
<label class="placeholder form-control-placeholder" for="lastName">Nachname</label>
|
||||
<div *ngIf="submitted && f.lastName.errors" class="invalid-feedback">
|
||||
<div>Nachname wird benötigt</div>
|
||||
</div>
|
||||
<div class="line"></div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="text" id="date" class="input form-control" formControlName="dateOfBirth" required>
|
||||
<label class="placeholder form-control-placeholder" for="date">Geburtsdatum (TT.MM.JJJJ)</label>
|
||||
<div *ngIf="submitted && f.dateOfBirth.errors" class="invalid-feedback">
|
||||
<div *ngIf="submitted && f.dateOfBirth.errors.required">Geburtsdatum wird benötigt</div>
|
||||
<div *ngIf="submitted && f.dateOfBirth.errors.invalidDate">Geburtsdatum hat ein falsches Format</div>
|
||||
</div>
|
||||
<div class="line"></div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="text" id="address" class="input form-control" formControlName="address" [ngClass]="{ 'error-visible': submitted && f.address.errors }" required>
|
||||
<label class="placeholder form-control-placeholder" for="address">Straße und Hausnummer</label>
|
||||
<div *ngIf="submitted && f.address.errors" class="invalid-feedback">
|
||||
<div>Straße und Hausnummer wird benötigt</div>
|
||||
</div>
|
||||
<div class="line"></div>
|
||||
</div>
|
||||
<div class="form-group form-group-location">
|
||||
<div class="form-group-split">
|
||||
<input type="text" id="zipCode" class="input form-control" formControlName="zipCode" [ngClass]="{ 'error-visible': submitted && f.zipCode.errors }" required>
|
||||
<label class="placeholder form-control-placeholder" for="zipCode">PLZ</label>
|
||||
<div *ngIf="submitted && f.zipCode.errors" class="invalid-feedback invalid-feedback-under invalid-feedback-under-start">
|
||||
<div>PLZ wird benötigt</div>
|
||||
</div>
|
||||
<div class="line"></div>
|
||||
</div>
|
||||
<div class="form-group-split">
|
||||
<input type="text" id="city" class="input form-control" formControlName="city" [ngClass]="{ 'error-visible': submitted && f.city.errors }" required>
|
||||
<label class="placeholder form-control-placeholder city-label" for="city">Ort</label>
|
||||
<div *ngIf="submitted && f.city.errors" class="invalid-feedback invalid-feedback-under">
|
||||
<div>Ort wird benötigt</div>
|
||||
</div>
|
||||
<div class="line"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group btn-container">
|
||||
<a class="btn" (click)="createUser()">Speichern</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,284 @@
|
||||
@import "../../../../../assets/scss/variables";
|
||||
|
||||
.customer-section-create {
|
||||
border-radius: 4px;
|
||||
padding-top: 45px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
padding-bottom: 30px;
|
||||
|
||||
background-image: -webkit-linear-gradient(-180deg, #ffffff 0%, #ffffff 100%);
|
||||
background-image: -moz-linear-gradient(-180deg, #ffffff 0%, #ffffff 100%);
|
||||
background-image: -o-linear-gradient(-180deg, #ffffff 0%, #ffffff 100%);
|
||||
background-image: linear-gradient(-180deg, #ffffff 0%, #ffffff 100%);
|
||||
box-shadow: 0px -2px 24px 0px #dce2e9;
|
||||
}
|
||||
|
||||
.customer-section-create-title {
|
||||
font-weight: bold;
|
||||
font-size: 26px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.customer-section-create-description {
|
||||
font-size: 22px;
|
||||
width: 359px;
|
||||
padding-top: 15px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
// SEARCH
|
||||
.create-wrapper {
|
||||
margin-top: 35px;
|
||||
position: relative;
|
||||
width: 76%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
.error-message {
|
||||
animation: shake 0.3s cubic-bezier(.7,.07,.19,.97) both;
|
||||
transform: translate3d(0, 0, 0);
|
||||
position: absolute;
|
||||
left: 45%;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
color: $hima-error-msg-color;
|
||||
}
|
||||
|
||||
.error-visible {
|
||||
width: 65%;
|
||||
}
|
||||
|
||||
.form-group-location {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.form-group-split {
|
||||
width: 47%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// OLD IPADS
|
||||
@media only screen
|
||||
and (min-device-width : 768px)
|
||||
and (max-device-width : 1024px)
|
||||
and (-webkit-min-device-pixel-ratio: 1){
|
||||
.create-wrapper {
|
||||
|
||||
.error-visible {
|
||||
width: 65%;
|
||||
}
|
||||
|
||||
.invalid-feedback {
|
||||
left: 47%;
|
||||
width: 52%;
|
||||
}
|
||||
|
||||
.city-label {
|
||||
left:294px;
|
||||
}
|
||||
|
||||
.invalid-feedback-under-start {
|
||||
left: -29%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// OLD IPADS LANDSCAPE
|
||||
@media only screen
|
||||
and (min-device-width : 768px)
|
||||
and (max-device-width : 1024px)
|
||||
and (orientation : landscape)
|
||||
and (-webkit-min-device-pixel-ratio: 1) {
|
||||
.create-wrapper {
|
||||
|
||||
.error-message {
|
||||
left: 58%;
|
||||
}
|
||||
|
||||
.invalid-feedback {
|
||||
width: 52%;
|
||||
}
|
||||
|
||||
.city-label {
|
||||
left: 395px;
|
||||
}
|
||||
|
||||
.invalid-feedback-under-start {
|
||||
left: 1%;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// RETINA IPADS LANDSCAPE
|
||||
@media only screen
|
||||
and (min-device-width : 768px)
|
||||
and (max-device-width : 1366px)
|
||||
and (orientation : landscape)
|
||||
and (-webkit-min-device-pixel-ratio: 2) {
|
||||
.create-wrapper {
|
||||
|
||||
.invalid-feedback {
|
||||
width: 49%;
|
||||
}
|
||||
|
||||
.city-label {
|
||||
left: 535px;
|
||||
}
|
||||
|
||||
.invalid-feedback-under-start {
|
||||
left: 1%;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// RETINA IPADS
|
||||
@media only screen
|
||||
and (min-device-width : 768px)
|
||||
and (max-device-width : 1024px)
|
||||
and (-webkit-min-device-pixel-ratio: 2) {
|
||||
.create-wrapper {
|
||||
|
||||
.error-message {
|
||||
left: 56%;
|
||||
}
|
||||
|
||||
.error-visible {
|
||||
width: 65%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen
|
||||
and (max-width: 768px) {
|
||||
.error-message {
|
||||
left: 45% !important;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes shake {
|
||||
10% {
|
||||
transform: translate3d(-1px, 0, 0);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: translate3d(2px, 0, 0);
|
||||
}
|
||||
|
||||
70% {
|
||||
transform: translate3d(-3px, 0, 0);
|
||||
}
|
||||
|
||||
90% {
|
||||
transform: translate3d(3px, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
.invalid-feedback {
|
||||
position: absolute;
|
||||
top: 13px;
|
||||
left: 51%;
|
||||
// width: 52%;
|
||||
text-align: right;
|
||||
animation: shake 0.3s cubic-bezier(.7,.07,.19,.97) both;
|
||||
transform: translate3d(0, 0, 0);
|
||||
position: absolute;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
color: $hima-error-msg-color;
|
||||
}
|
||||
|
||||
.invalid-feedback-under {
|
||||
top: 58px;
|
||||
}
|
||||
|
||||
.btn-container {
|
||||
text-align: center;
|
||||
margin-top: 30px;
|
||||
|
||||
a {
|
||||
background-color: $hima-button-color;
|
||||
border: none;
|
||||
border-radius: 25px;
|
||||
color: #ffffff;
|
||||
padding: 14px 30px;
|
||||
width: 144px;
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.form-group {
|
||||
position: relative;
|
||||
margin-bottom: 1.5rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.form-control-placeholder {
|
||||
position: absolute;
|
||||
top: 18px;
|
||||
padding: 7px 10px 10px;
|
||||
transition: all 200ms;
|
||||
opacity: 0.5;
|
||||
left: 0px;
|
||||
}
|
||||
|
||||
.form-control:focus + .form-control-placeholder,
|
||||
.form-control:valid + .form-control-placeholder {
|
||||
font-size: 75%;
|
||||
transform: translate3d(0, -100%, 0);
|
||||
opacity: 1;
|
||||
border: none;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
|
||||
.form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.line {
|
||||
height: 3px;
|
||||
width: 100%;
|
||||
background-image: url('../../../../../assets/images/Line.svg');
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
|
||||
.input {
|
||||
width: 100%;
|
||||
padding: 15px 10px;
|
||||
caret-color: $hima-button-color;
|
||||
border: none;
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
color: #000000;
|
||||
text-align: left;
|
||||
line-height: 21px;
|
||||
}
|
||||
|
||||
input:focus {
|
||||
outline: none;
|
||||
border:none;
|
||||
}
|
||||
|
||||
label {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
color: #000000;
|
||||
text-align: left;
|
||||
line-height: 21px;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { CreateCustomerCardComponent } from './create-customer-card.component';
|
||||
|
||||
describe('CreateCustomerCardComponent', () => {
|
||||
let component: CreateCustomerCardComponent;
|
||||
let fixture: ComponentFixture<CreateCustomerCardComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ CreateCustomerCardComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(CreateCustomerCardComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,132 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { FormGroup, FormBuilder, Validators, FormControl } from '@angular/forms';
|
||||
import { Store, Select } from '@ngxs/store';
|
||||
import { AddUser, ChangeCurrentRoute, AddProcess } from '../../../../core/store/actions/process.actions';
|
||||
import { User, Address } from '../../../../core/models/user.model';
|
||||
import { Router } from '@angular/router';
|
||||
import { Process } from '../../../../core/models/process.model';
|
||||
import { Breadcrumb } from '../../../../core/models/breadcrumb.model';
|
||||
import { getRandomPic } from '../../../../core/utils/process.util';
|
||||
import { ProcessState } from '../../../../core/store/state/process.state';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
@Component({
|
||||
selector: 'app-create-customer-card',
|
||||
templateUrl: './create-customer-card.component.html',
|
||||
styleUrls: ['./create-customer-card.component.scss']
|
||||
})
|
||||
export class CreateCustomerCardComponent implements OnInit {
|
||||
|
||||
@Select(ProcessState.getProcesses) processes$: Observable<Process[]>;
|
||||
userForm: FormGroup;
|
||||
submitted = false;
|
||||
processes: Process[];
|
||||
|
||||
constructor(
|
||||
private fb: FormBuilder,
|
||||
private store: Store,
|
||||
private router: Router,
|
||||
) { }
|
||||
|
||||
ngOnInit() {
|
||||
this.userForm = this.buildForm(this.fb);
|
||||
}
|
||||
|
||||
createUser() {
|
||||
if (this.userForm.valid) {
|
||||
this.createProcessIfDosntExists();
|
||||
const address = new Address(
|
||||
this.userForm.get('firstName').value + ' ' + this.userForm.get('lastName').value,
|
||||
this.userForm.get('address').value,
|
||||
+this.userForm.get('zipCode').value,
|
||||
this.userForm.get('city').value
|
||||
);
|
||||
const data: User = {
|
||||
id: Date.now() + Math.random(),
|
||||
name: this.userForm.get('firstName').value + ' ' + this.userForm.get('lastName').value,
|
||||
date_of_birth: this.userForm.get('dateOfBirth').value,
|
||||
delivery_addres: address,
|
||||
poossible_addresses: [
|
||||
address
|
||||
],
|
||||
invoice_address: address,
|
||||
email: 'random@gmail.com',
|
||||
shop: (Math.floor(Math.random() * 6) + 1) > 3 ? true : false
|
||||
};
|
||||
this.store.dispatch(new AddUser(data));
|
||||
this.store.dispatch(new ChangeCurrentRoute('customer-search'));
|
||||
this.router.navigate(['customer-search']);
|
||||
this.userForm.reset();
|
||||
} else {
|
||||
this.submitted = true;
|
||||
}
|
||||
}
|
||||
|
||||
loadProcesses() {
|
||||
this.processes$.subscribe(
|
||||
(data: Process[]) => this.processes = data
|
||||
);
|
||||
}
|
||||
|
||||
createProcess() {
|
||||
const newProcess = <Process>{
|
||||
id: 1,
|
||||
name: '# 1',
|
||||
selected: true,
|
||||
icon: getRandomPic(),
|
||||
breadcrumbs: <Breadcrumb[]>[{
|
||||
name: 'Kundendaten erfassen',
|
||||
path: '/customer-search'
|
||||
}],
|
||||
currentRoute: 'customer-search'
|
||||
};
|
||||
|
||||
this.store.dispatch(new AddProcess(newProcess));
|
||||
}
|
||||
|
||||
createProcessIfDosntExists() {
|
||||
this.loadProcesses();
|
||||
if (this.processes.length === 0) {
|
||||
this.createProcess();
|
||||
}
|
||||
}
|
||||
|
||||
get f() { return this.userForm.controls; }
|
||||
|
||||
private buildForm(fb: FormBuilder) {
|
||||
return fb.group({
|
||||
firstName: ['', Validators.required],
|
||||
lastName: ['', Validators.required],
|
||||
dateOfBirth: ['', Validators.required],
|
||||
address: ['', Validators.required],
|
||||
zipCode: ['', Validators.required],
|
||||
city: ['', Validators.required]
|
||||
}, { validators: this.validateAllFormFields });
|
||||
}
|
||||
|
||||
validateAllFormFields(formGroup: FormGroup) {
|
||||
Object.keys(formGroup.controls).forEach(field => {
|
||||
const control = formGroup.get(field);
|
||||
if (control instanceof FormControl) {
|
||||
control.markAsTouched({ onlySelf: true });
|
||||
|
||||
if (field === 'dateOfBirth') {
|
||||
const regexpDate: RegExp = /^\s*(3[01]|[12][0-9]|0?[1-9])\.(1[012]|0?[1-9])\.((?:19|20)\d{2})\s*$/;
|
||||
const validDate = regexpDate.test(control.value);
|
||||
|
||||
if ((control.errors && control.errors.invalidDate) && validDate) {
|
||||
control.setErrors(null);
|
||||
} else if (!control.errors) {
|
||||
const regexpDate: RegExp = /^\s*(3[01]|[12][0-9]|0?[1-9])\.(1[012]|0?[1-9])\.((?:19|20)\d{2})\s*$/;
|
||||
const validDate = regexpDate.test(control.value);
|
||||
if (!validDate) {
|
||||
control.setErrors({ invalidDate: true });
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (control instanceof FormGroup) {
|
||||
this.validateAllFormFields(control);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<div class="customer-search-container">
|
||||
<div class="customer-section" (click)="changeCard()">
|
||||
<ng-container *ngIf="displayUserSearch">
|
||||
<div><span class="customer-section-header">Kundendaten erfassen</span></div>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="!displayUserSearch">
|
||||
<div><span class="customer-section-header">Kundensuche</span></div>
|
||||
</ng-container>
|
||||
</div>
|
||||
<div class="customer-section">
|
||||
<div><span class="customer-section-header">Kundenkarte scannen</span></div>
|
||||
</div>
|
||||
<app-search-customer-card *ngIf="displayUserSearch"></app-search-customer-card>
|
||||
<app-create-customer-card *ngIf="!displayUserSearch"></app-create-customer-card>
|
||||
</div>
|
||||
@@ -0,0 +1,37 @@
|
||||
@import "../../../../../assets/scss/variables";
|
||||
|
||||
.customer-search-container {
|
||||
background-color: white;
|
||||
margin-left: 5px;
|
||||
margin-right: 5px;
|
||||
margin-top: 5px;
|
||||
width: 99%;
|
||||
border-radius: 5px;
|
||||
font-family: 'Open Sans';
|
||||
line-height: 21px;
|
||||
}
|
||||
|
||||
.customer-section {
|
||||
padding: 10px;
|
||||
border-radius: 4px;
|
||||
|
||||
display: grid;
|
||||
grid-template-columns: auto;
|
||||
height: 40px;
|
||||
padding-top: 28px;
|
||||
border-radius: 5px;
|
||||
|
||||
background-image: -webkit-linear-gradient(-180deg, #ffffff 0%, #ffffff 100%);
|
||||
background-image: -moz-linear-gradient(-180deg, #ffffff 0%, #ffffff 100%);
|
||||
background-image: -o-linear-gradient(-180deg, #ffffff 0%, #ffffff 100%);
|
||||
background-image: linear-gradient(-180deg, #ffffff 0%, #ffffff 100%);
|
||||
box-shadow: 0px -2px 24px 0px #dce2e9;
|
||||
}
|
||||
|
||||
.customer-section-header {
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
color: #5a728a;
|
||||
text-align: left;
|
||||
line-height: 21px;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import { Component, OnInit, AfterViewInit } from '@angular/core';
|
||||
import { Store } from '@ngxs/store';
|
||||
@Component({
|
||||
selector: 'app-customer-search',
|
||||
templateUrl: './customer-search.component.html',
|
||||
styleUrls: ['./customer-search.component.scss']
|
||||
})
|
||||
export class CustomerSearchComponent implements OnInit {
|
||||
displayUserSearch = true;
|
||||
animate = false;
|
||||
|
||||
constructor(private store: Store) { }
|
||||
|
||||
ngOnInit() {}
|
||||
|
||||
changeCard() {
|
||||
this.animate = true;
|
||||
this.displayUserSearch = !this.displayUserSearch;
|
||||
this.animate = false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<div class="customer-section-search">
|
||||
<div class="customer-search-title">
|
||||
<span>Kundensuche</span>
|
||||
</div>
|
||||
<div class="customer-search-description">
|
||||
<span>Wie lautet Ihr Name, Ihre E-Mail, Adresse oder Ihre PLZ?</span>
|
||||
</div>
|
||||
<div class="search-wrapper">
|
||||
<input [(ngModel)]="searchParams" (keypress)="keyHandler($event)" class="search-box" placeholder="Name, E-Mail, PLZ" type="text" autofocus [ngClass]="{'error-visible' : searchError}">
|
||||
<span *ngIf="searchError" class="error-message">Ergibt keine Suchergebnisse</span>
|
||||
<img (click)="search()" class="search-icon" src="../../../assets/images/search.svg" alt="search">
|
||||
<img (click)="clear()" *ngIf="searchParams" class="clear-icon" src="../../../assets/images/close.svg" alt="close">
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,80 +1,32 @@
|
||||
@import "../../../assets/scss/variables";
|
||||
|
||||
.customer-search-container {
|
||||
background-color: white;
|
||||
margin-left: 5px;
|
||||
margin-right: 5px;
|
||||
margin-top: 5px;
|
||||
width: 99%;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0px 0px 10px 0px #dce2e9;
|
||||
-moz-box-shadow: 0px 0px 10px 0px #dce2e9;;
|
||||
-webkit-box-shadow: 0px 0px 10px 0px #dce2e9;
|
||||
box-shadow: 0px 0px 10px 0px #dce2e9;
|
||||
font-family: 'Open Sans';
|
||||
line-height: 21px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 485px) {
|
||||
.article-search-container {
|
||||
width: 95%;
|
||||
}
|
||||
}
|
||||
|
||||
.customer-section {
|
||||
padding: 10px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.customer-scan {
|
||||
display: grid;
|
||||
grid-template-columns: auto;
|
||||
height: 40px;
|
||||
padding-top: 28px;
|
||||
}
|
||||
|
||||
.customer-scan-header {
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
color: #5a728a;
|
||||
text-align: left;
|
||||
line-height: 21px;
|
||||
}
|
||||
@import "../../../../../assets/scss/variables";
|
||||
|
||||
.customer-section-search {
|
||||
border-radius: 5px;
|
||||
box-shadow: 0px -2px 24px 0px #dce2e9;
|
||||
border-radius: 4px;
|
||||
padding-top: 45px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
height: 45vh;
|
||||
}
|
||||
|
||||
.customer-scan-content {
|
||||
font-size: 10pt;
|
||||
}
|
||||
|
||||
.customer-scan-content-div {
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.customer-search {
|
||||
display: grid;
|
||||
grid-template-columns: auto;
|
||||
padding-top: 30px;
|
||||
background-image: -webkit-linear-gradient(-180deg, #ffffff 0%, #ffffff 100%);
|
||||
background-image: -moz-linear-gradient(-180deg, #ffffff 0%, #ffffff 100%);
|
||||
background-image: -o-linear-gradient(-180deg, #ffffff 0%, #ffffff 100%);
|
||||
background-image: linear-gradient(-180deg, #ffffff 0%, #ffffff 100%);
|
||||
box-shadow: 0px -2px 24px 0px #dce2e9;
|
||||
}
|
||||
|
||||
.customer-search-title {
|
||||
font-weight: bold;
|
||||
font-size: 26px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.customer-search-description {
|
||||
font-size: 22px;
|
||||
width: 359px;
|
||||
padding-top: 12px;
|
||||
padding-top: 15px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
// SEARCH
|
||||
@@ -85,6 +37,7 @@
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
|
||||
.search-box {
|
||||
width: 100%;
|
||||
@@ -225,13 +178,6 @@ and (max-width: 768px) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.show-all-label {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #f70400;
|
||||
}
|
||||
|
||||
@keyframes shake {
|
||||
10% {
|
||||
transform: translate3d(-1px, 0, 0);
|
||||
@@ -0,0 +1,25 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { SearchCustomerCardComponent } from './search-customer-card.component';
|
||||
|
||||
describe('SearchCustomerCardComponent', () => {
|
||||
let component: SearchCustomerCardComponent;
|
||||
let fixture: ComponentFixture<SearchCustomerCardComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ SearchCustomerCardComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(SearchCustomerCardComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -1,28 +1,27 @@
|
||||
import { Component, OnInit, Input } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import { RecentArticleSearch } from '../../core/models/recent-article-search.model';
|
||||
import { Product } from '../../core/models/product.model';
|
||||
import { Filter } from '../../core/models/filter.model';
|
||||
import { Process } from '../../core/models/process.model';
|
||||
import { RecentArticleSearch } from '../../../../core/models/recent-article-search.model';
|
||||
import { Product } from '../../../../core/models/product.model';
|
||||
import { Filter } from '../../../../core/models/filter.model';
|
||||
import { Process } from '../../../../core/models/process.model';
|
||||
import { Router } from '@angular/router';
|
||||
import { Breadcrumb } from '../../core/models/breadcrumb.model';
|
||||
import { getRandomPic } from '../../core/utils/process.util';
|
||||
import { Breadcrumb } from '../../../../core/models/breadcrumb.model';
|
||||
import { getRandomPic } from '../../../../core/utils/process.util';
|
||||
import { Store, Select } from '@ngxs/store';
|
||||
import { SearchUser, ChangeCurrentRoute, AddProcess } from '../../core/store/actions/process.actions';
|
||||
import { ProcessState } from '../../core/store/state/process.state';
|
||||
import { User } from '../../core/models/user.model';
|
||||
import { SearchUser, ChangeCurrentRoute, AddProcess } from '../../../../core/store/actions/process.actions';
|
||||
import { ProcessState } from '../../../../core/store/state/process.state';
|
||||
import { User } from '../../../../core/models/user.model';
|
||||
|
||||
@Component({
|
||||
selector: 'app-customer-search',
|
||||
templateUrl: './customer-search.component.html',
|
||||
styleUrls: ['./customer-search.component.scss']
|
||||
selector: 'app-search-customer-card',
|
||||
templateUrl: './search-customer-card.component.html',
|
||||
styleUrls: ['./search-customer-card.component.scss']
|
||||
})
|
||||
export class CustomerSearchComponent implements OnInit {
|
||||
|
||||
export class SearchCustomerCardComponent implements OnInit {
|
||||
|
||||
@Select(ProcessState.getProcesses) processes$: Observable<Process[]>;
|
||||
@Select(ProcessState.getUsers) users$: Observable<User[]>;
|
||||
|
||||
|
||||
recentArticles$: Observable<RecentArticleSearch[]>;
|
||||
recentArticles: RecentArticleSearch[];
|
||||
products$: Observable<Product[]>;
|
||||
@@ -40,7 +39,6 @@ export class CustomerSearchComponent implements OnInit {
|
||||
|
||||
ngOnInit() {}
|
||||
|
||||
|
||||
search() {
|
||||
if (!this.searchParams) {
|
||||
this.searchError = false;
|
||||
@@ -5,15 +5,15 @@
|
||||
<h1 class="user-title">{{ user.name }}</h1>
|
||||
<div class="user-location">
|
||||
<span class="user-label">PLZ und Wohnort</span>
|
||||
<span class="user-data">{{ user.name }}</span>
|
||||
<span class="user-data">{{ user.delivery_addres.zip }} {{ user.delivery_addres.city }}</span>
|
||||
</div>
|
||||
<div class="user-email">
|
||||
<span class="user-label">E-Mail</span>
|
||||
<span class="user-data">{{ user.email }}</span>
|
||||
<span class="user-label">E-Mail</span>
|
||||
<span class="user-data">{{ user.email }}</span>
|
||||
</div>
|
||||
<div class="user-shop" *ngIf="user.shop">
|
||||
<span class="user-label">Onlineshop</span>
|
||||
<span class="user-data"><img src="../../../assets/images/Check.svg" alt="check" class="check-icon">Registriert</span>
|
||||
<span class="user-label">Onlineshop</span>
|
||||
<span class="user-data"><img src="../../../assets/images/Check.svg" alt="check" class="check-icon">Registriert</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="user-actions">
|
||||
@@ -23,8 +23,9 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="note">
|
||||
<span class="note-text">
|
||||
Hinweis: Aus Datenschutzgründen werden nur Teilinformationen dargestellt. Tab auf einen Kunden um mehr zu erfahren.
|
||||
</span>
|
||||
<span class="note-text">
|
||||
Hinweis: Aus Datenschutzgründen werden nur Teilinformationen dargestellt. Tab auf einen Kunden um mehr zu erfahren.
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@import "../../../assets/scss/variables";
|
||||
@import "../../../../../assets/scss/variables";
|
||||
|
||||
.customer-search-result-container {
|
||||
font-family: 'Open Sans';
|
||||
@@ -1,20 +1,20 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { CustomerSearchResultComponent } from './customer-search-result.component';
|
||||
import { SearchCustomerResultComponent } from './search-customer-result.component';
|
||||
|
||||
describe('CustomerSearchResultComponent', () => {
|
||||
let component: CustomerSearchResultComponent;
|
||||
let fixture: ComponentFixture<CustomerSearchResultComponent>;
|
||||
describe('SearchCustomerResultComponent', () => {
|
||||
let component: SearchCustomerResultComponent;
|
||||
let fixture: ComponentFixture<SearchCustomerResultComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ CustomerSearchResultComponent ]
|
||||
declarations: [ SearchCustomerResultComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(CustomerSearchResultComponent);
|
||||
fixture = TestBed.createComponent(SearchCustomerResultComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
@@ -1,17 +1,16 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Select, Store } from '@ngxs/store';
|
||||
import { ProcessState } from '../../core/store/state/process.state';
|
||||
import { ProcessState } from '../../../../core/store/state/process.state';
|
||||
import { Observable } from 'rxjs';
|
||||
import { User } from '../../core/models/user.model';
|
||||
import { User } from '../../../../core/models/user.model';
|
||||
import { SethUserName } from 'src/app/core/store/actions/process.actions';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-customer-search-result',
|
||||
templateUrl: './customer-search-result.component.html',
|
||||
styleUrls: ['./customer-search-result.component.scss']
|
||||
selector: 'app-search-customer-result',
|
||||
templateUrl: './search-customer-result.component.html',
|
||||
styleUrls: ['./search-customer-result.component.scss']
|
||||
})
|
||||
export class CustomerSearchResultComponent implements OnInit {
|
||||
export class SearchCustomerResultComponent implements OnInit {
|
||||
|
||||
@Select(ProcessState.getUsers) users$: Observable<User[]>;
|
||||
|
||||
@@ -32,4 +31,5 @@ export class CustomerSearchResultComponent implements OnInit {
|
||||
approve(name: string): void {
|
||||
this.store.dispatch(new SethUserName(name));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
<div class="customer-search-container">
|
||||
<div class="customer-section customer-scan">
|
||||
<div><span class="customer-scan-header">Kundenkarte scannen</span></div>
|
||||
</div>
|
||||
<div class="customer-section-search">
|
||||
<div class="align-center">
|
||||
<span class="customer-search-title">Kundensuche</span>
|
||||
</div>
|
||||
<div class="align-center customer-search-description">
|
||||
<span>Wie lautet Ihr Name, Ihre E-Mail, Adresse oder Ihre PLZ?</span>
|
||||
</div>
|
||||
<div class="align-center search-wrapper">
|
||||
<input [(ngModel)]="searchParams" (keypress)="keyHandler($event)" class="search-box" placeholder="Name, E-Mail, PLZ" type="text" autofocus [ngClass]="{'error-visible' : searchError}">
|
||||
<span *ngIf="searchError" class="error-message">Ergibt keine Suchergebnisse</span>
|
||||
<img (click)="search()" class="search-icon" src="../../../assets/images/search.svg" alt="search">
|
||||
<img (click)="clear()" *ngIf="searchParams" class="clear-icon" src="../../../assets/images/close.svg" alt="close">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
29
src/app/components/customer-search/customer-search.module.ts
Normal file
29
src/app/components/customer-search/customer-search.module.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { SearchCustomerResultComponent } from './components/search-customer-result/search-customer-result.component';
|
||||
import { SearchCustomerCardComponent } from './components/search-customer-card/search-customer-card.component';
|
||||
import { CreateCustomerCardComponent } from './components/create-customer-card/create-customer-card.component';
|
||||
import { CustomerSearchComponent } from './components/customer-search/customer-search.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
SearchCustomerResultComponent,
|
||||
SearchCustomerCardComponent,
|
||||
CreateCustomerCardComponent,
|
||||
CustomerSearchComponent,
|
||||
],
|
||||
exports: [
|
||||
SearchCustomerResultComponent,
|
||||
SearchCustomerCardComponent,
|
||||
CreateCustomerCardComponent,
|
||||
CustomerSearchComponent,
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
],
|
||||
|
||||
})
|
||||
export class CustomerSearchModule {}
|
||||
@@ -14,6 +14,11 @@
|
||||
<span class="process-cart-number">{{ cartCount }}</span>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="!process.cart">
|
||||
<div class="pt-3">
|
||||
<span class=""> </span>
|
||||
</div>
|
||||
</ng-container>
|
||||
<div>
|
||||
<a (click)="deleteProcess(process)">
|
||||
<img class="process-delete-icon" src="/assets/images/close.svg">
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
import { Product } from './product.model';
|
||||
|
||||
export interface User {
|
||||
id: number;
|
||||
name: string;
|
||||
email: string;
|
||||
phone_number: string;
|
||||
delivery_addres: Address;
|
||||
invoice_address: Address;
|
||||
shop: boolean;
|
||||
payement_method: string;
|
||||
poossible_addresses: Address[];
|
||||
date_of_birth: string;
|
||||
email?: string;
|
||||
phone_number?: string;
|
||||
delivery_addres?: Address;
|
||||
invoice_address?: Address;
|
||||
shop?: boolean;
|
||||
payement_method?: string;
|
||||
poossible_addresses?: Address[];
|
||||
}
|
||||
|
||||
export class Address {
|
||||
|
||||
@@ -8,19 +8,39 @@ import { Observable, of } from 'rxjs';
|
||||
})
|
||||
export class UserService {
|
||||
|
||||
constructor() { }
|
||||
constructor() {
|
||||
localStorage.setItem('users', JSON.stringify(usersMock));
|
||||
}
|
||||
|
||||
searchUser(params: string): Observable<User[]> {
|
||||
const filteredUsers = usersMock.filter((user: User) => {
|
||||
// Params should be contained in name, zip or city
|
||||
if (user.name.toLowerCase().indexOf(params.toLowerCase()) >= 0 ||
|
||||
user.delivery_addres.city.toString().toLowerCase().indexOf(params.toLowerCase()) >= 0 ||
|
||||
user.delivery_addres.zip.toString().toLowerCase().indexOf(params.toLowerCase()) >= 0
|
||||
) {
|
||||
return user;
|
||||
}
|
||||
});
|
||||
const mockedUsers = JSON.parse(localStorage.getItem('users'));
|
||||
let foundUsers: User[] = [];
|
||||
const splitedParams = params.split(' ');
|
||||
|
||||
return of(filteredUsers);
|
||||
mockedUsers.forEach((user: User) => {
|
||||
splitedParams.forEach((word: string) => {
|
||||
if (word) {
|
||||
if (user.name.toLowerCase().indexOf(word.toLowerCase()) >= 0 ||
|
||||
user.delivery_addres.city.toString().toLowerCase().indexOf(word.toLowerCase()) >= 0 ||
|
||||
user.delivery_addres.zip.toString().toLowerCase().indexOf(params.toLowerCase()) >= 0
|
||||
) {
|
||||
|
||||
const userExists = foundUsers.filter((userAdded: User) => userAdded.id === user.id);
|
||||
if (userExists.length === 0) {
|
||||
foundUsers.push(user);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return of(foundUsers);
|
||||
}
|
||||
|
||||
addUser(user: User) {
|
||||
const mockedUsers = JSON.parse(localStorage.getItem('users'));
|
||||
localStorage.setItem('users',
|
||||
JSON.stringify([...mockedUsers, user])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Process } from '../../models/process.model';
|
||||
import { Search } from '../../models/search.model';
|
||||
import { ItemDTO } from 'cat-service';
|
||||
import { Breadcrumb } from '../../models/breadcrumb.model';
|
||||
import { User } from '../../models/user.model';
|
||||
|
||||
export const ADD_PROCESS = '[PROCESS] Add';
|
||||
export const DELETE_PROCESS = '[PROCESS] Delete';
|
||||
@@ -13,6 +14,7 @@ export const SET_USER_NAME = '[PROCESS] Set the users name in tab';
|
||||
export const SET_CART = '[PROCESS] Set cart data for user';
|
||||
export const PREVENT_PRODUCT_LOAD = '[POCESS] Prevent product load';
|
||||
export const ALLOW_PRODUCT_LOAD = '[POCESS] Allow product load';
|
||||
export const ADD_USER = '[PROCESS] Add new user to store';
|
||||
|
||||
export class AddProcess {
|
||||
static readonly type = ADD_PROCESS;
|
||||
@@ -69,3 +71,8 @@ export class PreventProductLoad {
|
||||
export class AllowProductLoad {
|
||||
static readonly type = ALLOW_PRODUCT_LOAD;
|
||||
}
|
||||
export class AddUser {
|
||||
static readonly type = ADD_USER;
|
||||
|
||||
constructor(public payload: User) {}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import { ProductService } from '../../services/product.service';
|
||||
import { RecentArticleSearch } from '../../models/recent-article-search.model';
|
||||
import { GetProducts, LoadRecentProducts, AddSelectedProduct } from '../actions/product.actions';
|
||||
import { ItemDTO } from 'dist/cat-service/lib/dtos';
|
||||
import { getCurrentProcess } from '../../utils/process.util';
|
||||
import { getCurrentProcess } from '../../utils/process.util'
|
||||
|
||||
export class ProcessStateModel {
|
||||
processes: Process[];
|
||||
@@ -464,4 +464,9 @@ export class ProcessState {
|
||||
});
|
||||
return newProcessState;
|
||||
}
|
||||
|
||||
@Action(actions.AddUser)
|
||||
addUser(ctx: StateContext<ProcessStateModel>, { payload }: actions.AddUser) {
|
||||
this.usersService.addUser(payload);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
<div class="container">
|
||||
<h1>Artikel scannen</h1>
|
||||
<caption>
|
||||
Scannen Sie den Artikel um Informationen zu erhalten.
|
||||
</caption>
|
||||
<app-barcode-scanner></app-barcode-scanner>
|
||||
</div>
|
||||
34
src/app/modules/barcode-search/barcode-search.component.scss
Normal file
34
src/app/modules/barcode-search/barcode-search.component.scss
Normal file
@@ -0,0 +1,34 @@
|
||||
.container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 36px;
|
||||
flex-direction: column;
|
||||
background-color: rgba(255, 255, 255, 1);
|
||||
border-radius: 5px;
|
||||
box-shadow: 0px -2px 24px 0px rgba(220, 226, 233, 0.8);
|
||||
user-select: none;
|
||||
|
||||
h1 {
|
||||
font-family: 'Open Sans';
|
||||
font-size: 26px;
|
||||
font-weight: 600;
|
||||
color: rgba(0, 0, 0, 1);
|
||||
margin: 0;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
caption {
|
||||
width: 360px;
|
||||
font-family: 'Open Sans';
|
||||
font-size: 22px;
|
||||
color: rgba(0, 0, 0, 1);
|
||||
text-align: center;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
}
|
||||
|
||||
app-barcode-scanner {
|
||||
width: 550px;
|
||||
height: 300px;
|
||||
border: #e1ebf5 2px solid;
|
||||
}
|
||||
12
src/app/modules/barcode-search/barcode-search.component.ts
Normal file
12
src/app/modules/barcode-search/barcode-search.component.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-barcode-search',
|
||||
templateUrl: 'barcode-search.component.html',
|
||||
styleUrls: ['barcode-search.component.scss']
|
||||
})
|
||||
export class BarcodeSearchComponent implements OnInit {
|
||||
constructor() {}
|
||||
|
||||
ngOnInit() {}
|
||||
}
|
||||
14
src/app/modules/barcode-search/barcode-search.module.ts
Normal file
14
src/app/modules/barcode-search/barcode-search.module.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { ZXingScannerModule } from '@zxing/ngx-scanner';
|
||||
|
||||
import { BarcodeSearchComponent } from './barcode-search.component';
|
||||
import { BarcodeScannerComponent } from './components/barcode-scanner/barcode-scanner.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [ZXingScannerModule, CommonModule],
|
||||
exports: [BarcodeSearchComponent],
|
||||
declarations: [BarcodeSearchComponent, BarcodeScannerComponent],
|
||||
providers: []
|
||||
})
|
||||
export class BarcodeSearchModule {}
|
||||
@@ -0,0 +1,51 @@
|
||||
<div class="w-100" [hidden]="!hasDevices">
|
||||
<zxing-scanner
|
||||
#scanner
|
||||
[scannerEnabled]="enabled"
|
||||
(scanError)="scanErrorHandler($event)"
|
||||
(scanFailure)="scanFailureHandler($event)"
|
||||
(camerasFound)="camerasFoundHandler($event)"
|
||||
[formats]="allowedFormats"
|
||||
></zxing-scanner>
|
||||
</div>
|
||||
|
||||
<div class="scanner-controls" *ngIf="!enabled">
|
||||
<div class="result">
|
||||
<span
|
||||
(click)="scanToggle()"
|
||||
class="action"
|
||||
*ngIf="hasDevices && hasPermission"
|
||||
>
|
||||
<i class="icon-placeholder"></i>
|
||||
<span>Scan</span>
|
||||
</span>
|
||||
<span (click)="search()" *ngIf="code" class="action">
|
||||
<i class="icon-placeholder"></i>
|
||||
<span>Suchen</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="scan-result">
|
||||
<span>{{ code }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="debug" *ngIf="!(hasDevices && hasPermission)">
|
||||
<table class="table-scanner-state">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Status</th>
|
||||
<th>Property</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>{{ stateToEmoji(hasDevices) }}</td>
|
||||
<td>Devices</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ stateToEmoji(hasPermission) }}</td>
|
||||
<td>Permissions</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@@ -0,0 +1,52 @@
|
||||
:host {
|
||||
overflow: hidden;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
display: flex;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.scanner-controls {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
.action {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin: 0 20px;
|
||||
}
|
||||
.result {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.scan-result {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
font-weight: 600;
|
||||
margin-bottom: 50px;
|
||||
font-size: 26px;
|
||||
}
|
||||
|
||||
.icon-placeholder {
|
||||
$buttonSize: 30px;
|
||||
border-radius: 60px;
|
||||
border: #e1ebf5 2px solid;
|
||||
background-color: white;
|
||||
width: $buttonSize;
|
||||
height: $buttonSize;
|
||||
display: block;
|
||||
padding: $buttonSize;
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
import { Component, OnInit, ViewChild } from '@angular/core';
|
||||
import { BarcodeFormat, Result } from '@zxing/library';
|
||||
import { ZXingScannerComponent } from '@zxing/ngx-scanner';
|
||||
|
||||
@Component({
|
||||
selector: 'app-barcode-scanner',
|
||||
templateUrl: 'barcode-scanner.component.html',
|
||||
styleUrls: ['barcode-scanner.component.scss']
|
||||
})
|
||||
export class BarcodeScannerComponent implements OnInit {
|
||||
allowedFormats = [
|
||||
BarcodeFormat.QR_CODE,
|
||||
BarcodeFormat.EAN_13,
|
||||
BarcodeFormat.CODE_128,
|
||||
BarcodeFormat.UPC_A
|
||||
];
|
||||
public enabled = false;
|
||||
public debugMode = true;
|
||||
|
||||
public code = '';
|
||||
@ViewChild('scanner')
|
||||
scanner: ZXingScannerComponent;
|
||||
|
||||
hasDevices;
|
||||
availableDevices: MediaDeviceInfo[];
|
||||
currentDevice: MediaDeviceInfo;
|
||||
hasPermission;
|
||||
|
||||
constructor() {}
|
||||
|
||||
ngOnInit() {
|
||||
this.scanner.camerasNotFound.subscribe(() => (this.hasDevices = false));
|
||||
this.scanner.scanComplete.subscribe(result => this.scanSuccess(result));
|
||||
this.scanner.permissionResponse.subscribe((perm: boolean) => {
|
||||
this.hasPermission = perm;
|
||||
});
|
||||
}
|
||||
|
||||
scanSuccess(result: Result) {
|
||||
console.log(event);
|
||||
this.code = result.getText();
|
||||
this.enabled = false;
|
||||
}
|
||||
|
||||
camerasFoundHandler(devices: MediaDeviceInfo[]) {
|
||||
this.hasDevices = true;
|
||||
this.availableDevices = devices;
|
||||
|
||||
this.currentDevice = devices[0];
|
||||
|
||||
for (const device of devices) {
|
||||
if (/back|rear|environment/gi.test(device.label)) {
|
||||
this.scanner.changeDevice(device);
|
||||
this.currentDevice = device;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
scanToggle() {
|
||||
this.enabled = !this.enabled;
|
||||
if (this.enabled) {
|
||||
this.scanner.scan(this.currentDevice.deviceId);
|
||||
}
|
||||
}
|
||||
|
||||
search() {}
|
||||
|
||||
scanErrorHandler($event) {
|
||||
console.error($event);
|
||||
}
|
||||
|
||||
scanFailureHandler($event) {
|
||||
console.error($event);
|
||||
}
|
||||
|
||||
stateToEmoji(state: boolean): string {
|
||||
const states = {
|
||||
// not checked
|
||||
undefined: '❔',
|
||||
// failed to check
|
||||
null: '⭕',
|
||||
// success
|
||||
true: '✔',
|
||||
// can't touch that
|
||||
false: '❌'
|
||||
};
|
||||
|
||||
return states['' + state];
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import { NewsletterSignupModule } from './newsletter-signup/newsletter-signup.module';
|
||||
import { BarcodeSearchModule } from './barcode-search/barcode-search.module';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { HeaderComponent } from 'src/app/components/header/header.component';
|
||||
import { ProcessHeaderComponent } from 'src/app/components/process-header/process-header.component';
|
||||
@@ -41,7 +42,8 @@ import { InfiniteScrollModule } from 'ngx-infinite-scroll';
|
||||
AppRoutingModule,
|
||||
FormsModule,
|
||||
NewsletterSignupModule,
|
||||
InfiniteScrollModule
|
||||
InfiniteScrollModule,
|
||||
BarcodeSearchModule
|
||||
],
|
||||
exports: [
|
||||
HeaderComponent,
|
||||
|
||||
@@ -1,19 +1,21 @@
|
||||
import { NewsletterSignupComponent } from './../../modules/newsletter-signup/newsletter-signup.component';
|
||||
import { BarcodeSearchComponent } from './../../modules/barcode-search/barcode-search.component';
|
||||
import { Routes } from '@angular/router';
|
||||
import { DashboardComponent } from 'src/app/components/dashboard/dashboard.component';
|
||||
import { ArticleSearchComponent } from 'src/app/components/article-search/article-search.component';
|
||||
import { SearchResultsComponent } from 'src/app/components/search-results/search-results.component';
|
||||
import { ProductDetailsComponent } from 'src/app/components/product-details/product-details.component';
|
||||
import { CustomerSearchComponent } from 'src/app/components/customer-search/customer-search.component';
|
||||
import { CustomerSearchResultComponent } from 'src/app/components/customer-search-result/customer-search-result.component';
|
||||
import { NewsletterSignupComponent } from 'src/app/modules/newsletter-signup/newsletter-signup.component';
|
||||
import { DashboardComponent } from '../../components/dashboard/dashboard.component';
|
||||
import { ArticleSearchComponent } from '../../components/article-search/article-search.component';
|
||||
import { SearchResultsComponent } from '../../components/search-results/search-results.component';
|
||||
import { ProductDetailsComponent } from '../../components/product-details/product-details.component';
|
||||
import { CustomerSearchComponent } from '../../components/customer-search/components/customer-search/customer-search.component';
|
||||
import { SearchCustomerResultComponent } from '../../components/customer-search/components/search-customer-result/search-customer-result.component';
|
||||
|
||||
export const routes: Routes = [
|
||||
{ path: '', redirectTo: '/dashboard', pathMatch: 'full' },
|
||||
{ path: 'dashboard', component: DashboardComponent },
|
||||
{ path: 'article-search', component: ArticleSearchComponent },
|
||||
{ path: 'customer-search', component: CustomerSearchComponent },
|
||||
{ path: 'customer-search-result', component: CustomerSearchResultComponent },
|
||||
{ path: 'customer-search-result', component: SearchCustomerResultComponent },
|
||||
{ path: 'search-results#start', component: SearchResultsComponent },
|
||||
{ path: 'product-details/:id', component: ProductDetailsComponent },
|
||||
{ path: 'article-scan', component: BarcodeSearchComponent },
|
||||
{ path: 'newsletter', component: NewsletterSignupComponent }
|
||||
];
|
||||
|
||||
24
yarn.lock
24
yarn.lock
@@ -518,6 +518,21 @@
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz#e77a97fbd345b76d83245edcd17d393b1b41fb31"
|
||||
|
||||
"@zxing/library@0.7.0":
|
||||
version "0.7.0"
|
||||
resolved "https://registry.yarnpkg.com/@zxing/library/-/library-0.7.0.tgz#14969fd2dde384fda06cec829bfcf560bea19a98"
|
||||
dependencies:
|
||||
ts-custom-error "^2.2.1"
|
||||
optionalDependencies:
|
||||
text-encoding "^0.6.4"
|
||||
|
||||
"@zxing/ngx-scanner@^1.3.0":
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/@zxing/ngx-scanner/-/ngx-scanner-1.3.0.tgz#15f2061348ef853a681888df70b4b74b454470e5"
|
||||
dependencies:
|
||||
"@zxing/library" "0.7.0"
|
||||
tslib "^1.9.0"
|
||||
|
||||
JSONStream@^1.3.4:
|
||||
version "1.3.5"
|
||||
resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0"
|
||||
@@ -4382,7 +4397,6 @@ ng-packagr@^4.2.0:
|
||||
ngx-infinite-scroll@^7.0.1:
|
||||
version "7.0.1"
|
||||
resolved "https://registry.yarnpkg.com/ngx-infinite-scroll/-/ngx-infinite-scroll-7.0.1.tgz#59472108f5b6960519c269d6997fe3ae0961be07"
|
||||
integrity sha512-be9DAAuabV7VGI06/JUnS6pXC6mcBOzA4+SBCwOcP9WwJ2r5GjdZyOa34ls9hi1MnCOj3zrXLvPKQ/UDp6csIw==
|
||||
dependencies:
|
||||
opencollective "^1.0.3"
|
||||
|
||||
@@ -6464,6 +6478,10 @@ terser@^3.8.1:
|
||||
source-map "~0.6.1"
|
||||
source-map-support "~0.5.6"
|
||||
|
||||
text-encoding@^0.6.4:
|
||||
version "0.6.4"
|
||||
resolved "https://registry.yarnpkg.com/text-encoding/-/text-encoding-0.6.4.tgz#e399a982257a276dae428bb92845cb71bdc26d19"
|
||||
|
||||
through2@^2.0.0:
|
||||
version "2.0.5"
|
||||
resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd"
|
||||
@@ -6564,6 +6582,10 @@ trim-right@^1.0.1:
|
||||
dependencies:
|
||||
glob "^7.1.2"
|
||||
|
||||
ts-custom-error@^2.2.1:
|
||||
version "2.2.2"
|
||||
resolved "https://registry.yarnpkg.com/ts-custom-error/-/ts-custom-error-2.2.2.tgz#ee769cd6a9cf35dc2e9fedefbb3842f3a2fbceae"
|
||||
|
||||
ts-node@~7.0.0:
|
||||
version "7.0.1"
|
||||
resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-7.0.1.tgz#9562dc2d1e6d248d24bc55f773e3f614337d9baf"
|
||||
|
||||
Reference in New Issue
Block a user