New formula manager

This commit is contained in:
Zakary Timson 2018-11-04 17:30:43 -05:00
parent b57e3cc2cc
commit 80a59878f1
12 changed files with 243 additions and 226 deletions

View File

@ -1,6 +1,6 @@
{ {
"name": "fhsons", "name": "fhsons",
"version": "0.0.0", "version": "1.0.0",
"main": "main.js", "main": "main.js",
"scripts": { "scripts": {
"ng": "ng", "ng": "ng",

View File

@ -1,17 +1,19 @@
<div class="container"> <div class="container-fluid bg-white">
<div class="row"> <div class="container">
<div class="col-12 py-4"> <div class="row">
<img src="/assets/img/Mariamorgis.jpg" class="float-left img-fluid mb-4 mr-4"> <div class="col-12 py-4">
<h2 class="mb-4 roboto">40 Years of Excellence</h2> <img src="/assets/img/Mariamorgis.jpg" class="float-left img-fluid mb-4 mr-4">
<hr class="ml-4"> <h2 class="mb-4 roboto">40 Years of Excellence</h2>
<strong>FH &amp; Sons</strong> has been in business for 40 years. We have been ISO 9001:2000 certified since May 2000. Our <hr class="ml-4">
company manufactures plastisol silk screen inks, plastisols for moulding and dipping applications, as well as bulk <strong>FH &amp; Sons</strong> has been in business for 40 years. We have been ISO 9001:2000 certified since May 2000. Our
pigment dispersions. We also distribute for several top end equipment and chemical manufacturers. We strive to provide company manufactures plastisol silk screen inks, plastisols for moulding and dipping applications, as well as bulk
value in everything we sell, coupled with unmatched customer service and guidance. pigment dispersions. We also distribute for several top end equipment and chemical manufacturers. We strive to provide
value in everything we sell, coupled with unmatched customer service and guidance.
</div>
</div> </div>
</div> </div>
</div> </div>
<div style="background-color: #53709f;"> <div style="background: #53709f;">
<div class="container"> <div class="container">
<div class="row pt-4"> <div class="row pt-4">
<div class="col-12"> <div class="col-12">

View File

@ -1,54 +1,52 @@
<!-- Nav -->
<div class="wrapper">
<nav class="navbar navbar-expand-lg fixed-top navbar-light bg-light">
<div class="container">
<a class="navbar-brand roboto" [routerLink]="['/']">
<span class="fh-fade">fh</span>
<span class="rainbow"> & </span>
<span class="sons-fade">sons</span>
</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbar" aria-expanded="false">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbar"> <nav class="navbar navbar-expand-lg fixed-top navbar-light bg-light">
<ul class="navbar-nav mr-auto"> <div class="container">
<li class="nav-item"> <a class="navbar-brand roboto" [routerLink]="['/']">
<a class="nav-link text-dark" [routerLink]="['/about']">About</a> <span class="fh-fade">fh</span>
</li> <span class="rainbow"> & </span>
<li class="nav-item"> <span class="sons-fade">sons</span>
<a class="nav-link text-dark" [routerLink]="['/store']">Store</a> </a>
</li> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbar" aria-expanded="false">
<li class="nav-item"> <span class="navbar-toggler-icon"></span>
<a class="nav-link text-dark" [routerLink]="['/formulaManager']">Formula Manager</a> </button>
</li>
<li class="nav-item"> <div class="collapse navbar-collapse" id="navbar">
<a class="nav-link text-dark" target="_blank" href="/assets/Chromatex_MSDS.pdf">MSDS</a> <ul class="navbar-nav mr-auto">
</li> <li class="nav-item">
</ul> <a class="nav-link text-dark" [routerLink]="['/about']">About</a>
<div class="ml-auto"> </li>
<button *ngIf="!store.user" mat-stroked-button color="primary" (click)="login()">Login</button> <li class="nav-item">
<div *ngIf="store.user" class="d-inline"> <a class="nav-link text-dark" [routerLink]="['/store']">Store</a>
<span class="text-muted mr-3">{{store.user.email}}</span> </li>
<button mat-stroked-button color="accent" (click)="afAuth.auth.signOut()">Logout</button> <li class="nav-item">
</div> <a class="nav-link text-dark" [routerLink]="['/formulaManager']">Formula Manager</a>
<button *ngIf="cart.length" mat-icon-button class="ml-3" [routerLink]="['/cart']"> </li>
<mat-icon style="transform: translateY(6px)" [matBadge]="cartCount()" matBadgeColor="warn"> <li class="nav-item">
shopping_cart <a class="nav-link text-dark" target="_blank" href="/assets/Chromatex_MSDS.pdf">MSDS</a>
</mat-icon> </li>
</button> </ul>
<div class="ml-auto">
<button *ngIf="!store.user" mat-stroked-button color="primary" (click)="login()">Login</button>
<div *ngIf="store.user" class="d-inline">
<span class="text-muted mr-3">{{store.user.email}}</span>
<button mat-stroked-button color="accent" (click)="afAuth.auth.signOut()">Logout</button>
</div> </div>
<button *ngIf="cart.length" mat-icon-button class="ml-3" [routerLink]="['/cart']">
<mat-icon style="transform: translateY(6px)" [matBadge]="cartCount()" matBadgeColor="warn">
shopping_cart
</mat-icon>
</button>
</div> </div>
</div> </div>
</nav>
<!-- Content -->
<div>
<div style="height: 56px"></div>
<router-outlet></router-outlet>
</div> </div>
</nav>
<div class="push"></div> <!-- Push content down from under nav-->
<div style="height: 56px"></div>
<!-- Content -->
<div>
<router-outlet></router-outlet>
</div> </div>
<!-- Footer --> <!-- Footer -->

View File

@ -26,10 +26,10 @@ import { ProductsComponent } from './store/products/products.component';
import { CartComponent } from './store/cart/cart.component'; import { CartComponent } from './store/cart/cart.component';
import { ViewComponents } from './formulaManager/viewComponents/viewComponents.component'; import { ViewComponents } from './formulaManager/viewComponents/viewComponents.component';
import { NewComponentComponent } from './formulaManager/newComponent/newComponent.component'; import { NewComponentComponent } from './formulaManager/newComponent/newComponent.component';
import { HttpModule } from '@angular/http';
import { NewFormulaComponent } from './formulaManager/newFormula/newFormula.component'; import { NewFormulaComponent } from './formulaManager/newFormula/newFormula.component';
import { AppStore } from './app.store'; import { AppStore } from './app.store';
import { SlideshowModule } from 'ng-simple-slideshow'; import { SlideshowModule } from 'ng-simple-slideshow';
import {HttpClientModule} from '@angular/common/http';
@NgModule({ @NgModule({
declarations: [ declarations: [
@ -60,7 +60,7 @@ import { SlideshowModule } from 'ng-simple-slideshow';
BrowserAnimationsModule, BrowserAnimationsModule,
BrowserModule, BrowserModule,
FormsModule, FormsModule,
HttpModule, HttpClientModule,
NgxElectronModule, NgxElectronModule,
ReactiveFormsModule, ReactiveFormsModule,
RouterModule.forRoot([ RouterModule.forRoot([

View File

@ -1,129 +1,115 @@
<div class="container"> <mat-drawer-container class="flex-grow-1">
<div *ngIf="!electron.isElectronApp" class="row mt-3 d-none d-lg-block"> <mat-drawer #drawer [mode]="mobile ? 'over' : 'side'" [opened]="!mobile" [disableClose]="!mobile" [autoFocus]="false">
<div class="col-12"> <mat-form-field appearance="outline" class="mx-3 mt-3 pb-0">
<div class="jumbotron text-white" style=" <mat-label>Search</mat-label>
background-color: #000; <input #search matInput>
background-image: url('../../assets/img/formula.jpg'); <mat-icon matSuffix>search</mat-icon>
background-size: cover; </mat-form-field>
"> <mat-divider></mat-divider>
<h1 class="display-4">Standalone</h1> <mat-list>
<p class="lead text-white-50">Download Formula Manager 2.0</p> <ng-container *ngFor="let f of formulas | async; let i = index">
<hr class="my-4"> <mat-divider *ngIf="f.name.toLowerCase().indexOf(search.value.toLowerCase()) != -1 && i > 0"></mat-divider>
<p>Tired of using your browser? Try downloading Formula Manager 2.0!</p> <mat-list-item *ngIf="f.name.toLowerCase().indexOf(search.value.toLowerCase()) != -1" (click)="displayFormula(f)" [ngClass]="{'active': f.id == formula?.id}">
<a class="btn btn-primary btn-lg mb-3 mr-3" href="#" download> <mat-icon *ngIf="!f.approved" class="mr-3 text-danger">remove_circle</mat-icon>
<i class="fab fa-windows"></i> Windows</a> {{f.name}}
<a class="btn btn-primary btn-lg mb-3 mr-3" href="#" download> </mat-list-item>
<i class="fab fa-apple"></i> MacOS</a> </ng-container>
<a class="btn btn-primary btn-lg mb-3 mr-3" href="#" download> </mat-list>
<i class="fab fa-linux"></i> Linux</a> <div *ngIf="store.user" class="fixed-bottom w-100">
<button *ngIf="installPrompt" class="btn btn-primary btn-lg mb-3" (click)="prompt()"> <mat-divider></mat-divider>
<i class="fas fa-mobile-alt"></i> Mobile</button> <button mat-button class="m-1 float-right" (click)="newFormula()">
</div> <mat-icon>add</mat-icon> New
</button>
</div> </div>
</div> </mat-drawer>
<div *ngIf="store.user" class="row mt-3"> <mat-drawer-content class="p-3 bg-white" style="min-height: 450px; background: url('assets/img/splatter.jpg') center; background-size: cover;">
<div class="col-12"> <div>
<div class="float-right"> <button *ngIf="mobile" mat-raised-button (click)="drawer.open()">
<button mat-raised-button class="mr-3" (click)="openComponents()"> <mat-icon>opacity</mat-icon> Formulas
<mat-icon>list</mat-icon> Components </button>
</button>
<button mat-raised-button (click)="newFormula()">
<mat-icon>add</mat-icon> Formula
</button>
</div>
</div> </div>
</div> <div *ngIf="(electron.isElectronApp || mobile) && !formula">
<div class="row mt-3"> <img src="assets/img/starthere.png" class="m-4">
<div class="col-12 col-lg-3 mb-5">
<mat-form-field class="w-100">
<input #search matInput placeholder="Search">
<mat-icon matSuffix>search</mat-icon>
</mat-form-field>
<mat-list style="max-height: 500px; overflow-y: scroll">
<ng-container *ngFor="let f of formulas | async; let i = index">
<mat-divider *ngIf="f.name.toLowerCase().indexOf(search.value.toLowerCase()) != -1 && i > 0"></mat-divider>
<mat-list-item *ngIf="f.name.toLowerCase().indexOf(search.value.toLowerCase()) != -1" (click)="displayFormula(f)" [ngClass]="{'active': f.id == formula?.id}">
<mat-icon *ngIf="!f.approved" class="mr-3 text-danger">remove_circle</mat-icon>
{{f.name}}
</mat-list-item>
</ng-container>
</mat-list>
</div> </div>
<div *ngIf="!formula" class="d-none d-lg-block col-lg-9"> <mat-card *ngIf="!electron.isElectronApp && !mobile && !formula" class="mx-auto" style="max-width: 600px">
<div class="w-100"> <mat-card-header>
<div class="col-12 p-5"> <div class="d-flex align-items-center" style="color: #53709f">
<mat-card class="mx-auto text-center"> <mat-icon style="transform: scale(2.5)">opacity</mat-icon>
<mat-card-content> <h1 class="ml-3 display-4">Formula Manager</h1>
Pick a formula from the left hand side to see its components!
</mat-card-content>
</mat-card>
</div> </div>
</div> </mat-card-header>
</div> <mat-card-content>
<div *ngIf="formula" class="col-12 col-lg-9 mb-5"> <p class="lead text-white-50">Tired of using your browser? Download the standalone version of the Formula Manager.</p>
<div> <button mat-button class="button-fix"><i class="fab fa-windows mr-1 mb-1"></i> Windows</button>
<div class="float-left"> <button mat-button class="button-fix"><i class="fab fa-apple mr-1 mb-1"></i> MacOS</button>
<h3 class="mb-0">{{formula.name}}</h3> <button mat-button class="button-fix"><i class="fab fa-linux mr-1 mb-1"></i> Linux</button>
<span class="text-muted">Created: {{formula.created | date}}</span> </mat-card-content>
</div> </mat-card>
<div *ngIf="store.user" class="float-right"> <mat-card *ngIf="formula" class="my-4 mx-auto" style="max-width: 600px">
<h5>Approved: <mat-card-header>
<mat-card-title class="mb-0"><h4>{{formula.name}}</h4></mat-card-title>
<mat-card-subtitle>{{formula.created | date}}</mat-card-subtitle>
<div *ngIf="store.user" class="ml-auto">
<h4>Approved:
<mat-icon *ngIf="formula.approved" class="text-success" style="transform: translateY(20%)">check_circle</mat-icon> <mat-icon *ngIf="formula.approved" class="text-success" style="transform: translateY(20%)">check_circle</mat-icon>
<mat-icon *ngIf="!formula.approved" class="text-danger" style="transform: translateY(20%)">remove_circle</mat-icon> <mat-icon *ngIf="!formula.approved" class="text-danger" style="transform: translateY(20%)">remove_circle</mat-icon>
</h5> </h4>
</div> </div>
</div> </mat-card-header>
<table class="w-100 mt-4 table"> <mat-card-content>
<thead> <table class="w-100 mt-4 table">
<thead>
<tr> <tr>
<th style="width: 80%">Name</th> <th style="width: 80%">Name</th>
<th style="width: 10%">Quantity</th> <th class="text-right" style="width: 10%">Quantity</th>
<th style="width: 10%">Cost</th> <th class="text-right" style="width: 10%">Cost</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr *ngFor="let c of formula.components"> <tr *ngFor="let c of formula.components">
<td style="width: 80%">{{c.component?.name}}</td> <td style="width: 80%">{{c.component?.name}}</td>
<td style="width: 10%">{{c.quantity | scale: formula.total : _newTotal | convertFromG: unit}} {{unit}}</td> <td class="text-right" style="width: 10%">{{c.quantity | scale: formula.total : _newTotal | convertFromG: unit}} {{unit}}</td>
<td style="width: 10%">{{c.quantity | scale: formula.total : _newTotal / 1000 * c.component.cost | currency}}</td> <td class="text-right" style="width: 10%">{{c.quantity | scale: formula.total : _newTotal / 1000 * c.component.cost | currency}}</td>
</tbody> </tbody>
</table> </table>
<div class="col-12 d-lg-block mb-3" style="height: 4px; background:black; width: 100%;"></div> <div class="col-12 d-lg-block mb-3" style="height: 4px; background:black; width: 100%;"></div>
<div class="d-flex"> <div class="d-flex">
<div class="d-inline ml-auto"> <div class="d-inline ml-auto">
<div class="d-inline-block mr-3"> <div class="d-inline-block mr-3">
<mat-form-field style="width: 75px"> <mat-form-field style="width: 75px">
<input matInput type="number" placeholder="Yield" [(ngModel)]="newTotal"> <input matInput type="number" placeholder="Yield" [(ngModel)]="newTotal">
</mat-form-field> </mat-form-field>
<mat-form-field style="width: 40px"> <mat-form-field style="width: 40px">
<mat-select placeholder="Unit" [(ngModel)]="unit"> <mat-select placeholder="Unit" [(ngModel)]="unit">
<mat-option value="g">g</mat-option> <mat-option value="g">g</mat-option>
<mat-option value="oz">oz</mat-option> <mat-option value="oz">oz</mat-option>
<mat-option value="kg">kg</mat-option> <mat-option value="kg">kg</mat-option>
<mat-option value="lb">lb</mat-option> <mat-option value="lb">lb</mat-option>
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
</div> </div>
<div class="d-inline-block"> <div class="d-inline-block">
<mat-form-field id="totalCost" style="width: 100px"> <mat-form-field id="totalCost" style="width: 100px">
<input matInput placeholder="Total Cost" [value]="cost() | currency" [readonly]="true" style="text-align: right;"> <input matInput placeholder="Total Cost" [value]="cost() | currency" [readonly]="true" style="text-align: right;">
</mat-form-field> </mat-form-field>
</div>
</div> </div>
</div> </div>
</div> </mat-card-content>
<div *ngIf="store.user" class="w-100 mt-4"> <mat-card-actions *ngIf="store.user" style="overflow: hidden">
<div class="float-right"> <mat-divider></mat-divider>
<button *ngIf="!formula.approved" mat-raised-button class="mr-3" (click)="approve(formula)"> <button *ngIf="!formula.approved" mat-button class="m-1 mt-2" (click)="approve(formula)">
<mat-icon>check</mat-icon>Approve <mat-icon>check</mat-icon>Approve
</button> </button>
<button mat-raised-button class="mr-3" (click)="edit(formula)"> <div class="float-right m-1 mt-2">
<button mat-button class="mr-3" (click)="edit(formula)">
<mat-icon>edit</mat-icon>Edit <mat-icon>edit</mat-icon>Edit
</button> </button>
<button mat-raised-button (click)="delete(formula)"> <button mat-button (click)="delete(formula)">
<mat-icon>delete</mat-icon>Delete <mat-icon>delete</mat-icon>Delete
</button> </button>
</div> </div>
</div> </mat-card-actions>
</div> </mat-card>
</div> </mat-drawer-content>
</div> </mat-drawer-container>

View File

@ -1,4 +1,4 @@
import {Component, ElementRef, ViewChildren, HostListener} from '@angular/core'; import {Component} from '@angular/core';
import {ConvertFromGPipe, ConvertToGPipe} from './units.pipe'; import {ConvertFromGPipe, ConvertToGPipe} from './units.pipe';
import {ElectronService} from 'ngx-electron'; import {ElectronService} from 'ngx-electron';
import {LocalStorage} from 'webstorage-decorators'; import {LocalStorage} from 'webstorage-decorators';
@ -8,68 +8,74 @@ import {NewFormulaComponent} from './newFormula/newFormula.component';
import {AppStore} from '../app.store'; import {AppStore} from '../app.store';
import {map} from 'rxjs/operators'; import {map} from 'rxjs/operators';
import {DeleteComponent} from '../delete/delete.component'; import {DeleteComponent} from '../delete/delete.component';
import {BreakpointObserver, Breakpoints} from '@angular/cdk/layout';
@Component({ @Component({
selector: 'formula-manager', selector: 'formula-manager',
templateUrl: './formulaManager.component.html', templateUrl: './formulaManager.component.html',
styles: [ styles: [
` `
.active { .active {
background-color: #f0f0f0; background-color: #f0f0f0;
} }
` `
] ]
}) })
export class FormulaManagerComponent { export class FormulaManagerComponent {
formula; formula;
formulas; formulas;
@LocalStorage({defaultValue: 'g'}) @LocalStorage({defaultValue: 'g'})
unit; unit;
mobile = false;
_newTotal: number = 0; _newTotal: number = 0;
get newTotal() { get newTotal() {
return new ConvertFromGPipe().transform(this._newTotal, this.unit); return new ConvertFromGPipe().transform(this._newTotal, this.unit);
} }
set newTotal(total) {
this._newTotal = new ConvertToGPipe().transform(total, this.unit);
}
constructor(public electron: ElectronService, private dialog: MatDialog, public store: AppStore) { set newTotal(total) {
this.formulas = this.store.formulas.pipe(map(rows => rows.filter(row => this.store.user || row.approved))); this._newTotal = new ConvertToGPipe().transform(total, this.unit);
} }
approve(formula) { constructor(public electron: ElectronService, private dialog: MatDialog, private $breakpoint: BreakpointObserver, public store: AppStore) {
formula.approved = true; this.formulas = this.store.formulas.pipe(map(rows => rows.filter(row => this.store.user || row.approved)));
formula.ref.update({approved: true});
}
cost() { // Handle switching between mobile and desktop
let cost = 0; this.$breakpoint.observe(Breakpoints.Handset).subscribe(e => this.mobile = e.matches);
this.formula.components.forEach( }
row => (cost += (((row.quantity / this.formula.total) * this._newTotal) / 1000) * row.component.cost)
);
return cost;
}
edit(formula) { approve(formula) {
this.dialog.open(NewFormulaComponent, {data: formula}); formula.approved = true;
} formula.ref.update({approved: true});
}
delete(formula) { cost() {
this.dialog.open(DeleteComponent, {data: formula}); let cost = 0;
} this.formula.components.forEach(
row => (cost += (((row.quantity / this.formula.total) * this._newTotal) / 1000) * row.component.cost)
);
return cost;
}
displayFormula(formula) { edit(formula) {
formula.total = formula.components.reduce((acc, row) => (acc += row.quantity), 0); this.dialog.open(NewFormulaComponent, {data: formula});
this.newTotal = new ConvertFromGPipe().transform(formula.total, this.unit); }
this.formula = formula;
}
newFormula() { delete(formula) {
this.dialog.open(NewFormulaComponent); this.dialog.open(DeleteComponent, {data: formula});
} }
openComponents() { displayFormula(formula) {
this.dialog.open(ViewComponents, {height: '500px'}); formula.total = formula.components.reduce((acc, row) => (acc += row.quantity), 0);
} this.newTotal = new ConvertFromGPipe().transform(formula.total, this.unit);
this.formula = formula;
}
newFormula() {
this.dialog.open(NewFormulaComponent);
}
openComponents() {
this.dialog.open(ViewComponents, {height: '500px'});
}
} }

View File

@ -36,7 +36,7 @@
</div> </div>
</div> </div>
</div> </div>
<div class="container-fluid pb-5"> <div class="container-fluid bg-white pb-5">
<div class="row"> <div class="row">
<div class="col d-none d-md-inline p-0" style="position: relative"> <div class="col d-none d-md-inline p-0" style="position: relative">
<div class="d-inline-block" style="position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%)"> <div class="d-inline-block" style="position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%)">

View File

@ -1,8 +1,21 @@
@import '~@angular/material/prebuilt-themes/indigo-pink.css'; @import '~@angular/material/prebuilt-themes/indigo-pink.css';
@import url('https://use.fontawesome.com/releases/v5.1.0/css/all.css'); @import url('https://use.fontawesome.com/releases/v5.1.0/css/all.css');
@import url('https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css'); @import url('https://fonts.googleapis.com/css?family=Material+Icons|Roboto+Condensed');
@import url('https://fonts.googleapis.com/css?family=Roboto+Condensed');
@import url('https://fonts.googleapis.com/icon?family=Material+Icons'); ::-webkit-scrollbar-track {
border-radius: 10px;
background-color: rgba(255, 255, 255, 0);
}
::-webkit-scrollbar {
width: 8px;
background-color: #F5F5F5;
}
::-webkit-scrollbar-thumb {
border-radius: 10px;
background-color: #555;
}
html, html,
body { body {
@ -10,6 +23,11 @@ body {
width: 100%; width: 100%;
margin: 0; margin: 0;
padding: 0; padding: 0;
background-color: #292929;
}
.button-fix {
font-size: 1.2rem;
} }
a, a,

7
src/assets/css/bootstrap.min.css vendored Normal file

File diff suppressed because one or more lines are too long

BIN
src/assets/img/splatter.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 703 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

View File

@ -16,7 +16,7 @@
<link rel="icon" type="image/png" href="assets/img/logo.png"> <link rel="icon" type="image/png" href="assets/img/logo.png">
<link rel="manifest" href="manifest.json"> <link rel="manifest" href="manifest.json">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.1.1/css/all.css"> <link rel="stylesheet" href="assets/css/bootstrap.min.css">
<script src="https://www.paypalobjects.com/api/checkout.js"></script> <script src="https://www.paypalobjects.com/api/checkout.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>