Sets up basic Angular app
|
@ -0,0 +1,13 @@
|
|||
# Editor configuration, see https://editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.md]
|
||||
max_line_length = off
|
||||
trim_trailing_whitespace = false
|
|
@ -1,88 +1,49 @@
|
|||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
# See http://help.github.com/ignore-files/ for more about ignoring files.
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
# compiled output
|
||||
/dist
|
||||
/tmp
|
||||
/out-tsc
|
||||
# Only exists if Bazel was run
|
||||
/bazel-out
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
# dependencies
|
||||
/node_modules
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
# profiling files
|
||||
chrome-profiler-events*.json
|
||||
speed-measure-plugin*.json
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
*.lcov
|
||||
# IDEs and editors
|
||||
/.idea
|
||||
.project
|
||||
.classpath
|
||||
.c9/
|
||||
*.launch
|
||||
.settings/
|
||||
*.sublime-workspace
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
# IDE - VSCode
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
.history/*
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
# misc
|
||||
/.sass-cache
|
||||
/connect.lock
|
||||
/coverage
|
||||
/libpeerconnection.log
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
testem.log
|
||||
/typings
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
# System Files
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# TypeScript v1 declaration files
|
||||
typings/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variables file
|
||||
.env
|
||||
.env.test
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
|
||||
# next.js build output
|
||||
.next
|
||||
|
||||
# nuxt.js build output
|
||||
.nuxt
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
.dynamodb/
|
||||
# SonarCloud
|
||||
/.scannerwork/
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
sonar.organization=sartography
|
||||
sonar.projectKey=sartography_cr-connect-frontend
|
||||
sonar.host.url=https://sonarcloud.io
|
||||
sonar.javascript.lcov.reportPaths=coverage/lcov.info
|
||||
sonar.typescript.tsconfigPath=tsconfig.json
|
||||
sonar.coverage.exclusions=**/*.mocks.ts, **/*.spec.ts, e2e/**, src/assets/**, src/main.ts, src/test.ts, src/polyfills.ts, karma.conf.js, docker/**
|
||||
sonar.cpd.exclusions=**/*.mocks.ts, **/*.spec.ts, e2e/**, src/assets/**, src/main.ts, src/test.ts, src/polyfills.ts, karma.conf.js, docker/**
|
||||
sonar.exclusions=src/assets/**
|
|
@ -0,0 +1,55 @@
|
|||
sudo: required
|
||||
|
||||
dist: bionic
|
||||
|
||||
language: node_js
|
||||
|
||||
node_js:
|
||||
- 12
|
||||
|
||||
services:
|
||||
- docker
|
||||
- xvfb
|
||||
|
||||
before_install:
|
||||
- |
|
||||
if [[ $TRAVIS_BRANCH =~ ^(feature\/.*)$ ]]; then
|
||||
export E2E_TAG="dev"
|
||||
elif [[ $TRAVIS_BRANCH =~ ^(dev|testing|demo|training|staging|rrt\/.*)$ ]]; then
|
||||
export E2E_TAG="${TRAVIS_BRANCH//\//_}"
|
||||
else
|
||||
export E2E_TAG="latest"
|
||||
fi
|
||||
echo "E2E_TAG = $E2E_TAG"
|
||||
|
||||
install:
|
||||
- npm install
|
||||
|
||||
addons:
|
||||
chrome: stable
|
||||
|
||||
env:
|
||||
global:
|
||||
- API_URL=http://localhost:5000/v1.0
|
||||
- BASE_HREF=/
|
||||
- DEPLOY_URL=/
|
||||
- HOME_ROUTE=home
|
||||
- IRB_URL=http://localhost:5001/
|
||||
- PORT0=4200
|
||||
- PRODUCTION=false
|
||||
script:
|
||||
- npm run ci
|
||||
|
||||
deploy:
|
||||
provider: script
|
||||
script: bash ./deploy.sh sartography/cr-connect-frontend
|
||||
on:
|
||||
all_branches: true
|
||||
condition: $TRAVIS_BRANCH =~ ^(dev|testing|demo|training|staging|master|rrt\/.*)$
|
||||
|
||||
notifications:
|
||||
email:
|
||||
on_success: change
|
||||
on_failure: always
|
||||
recipients:
|
||||
- dan@sartography.com
|
|
@ -0,0 +1,31 @@
|
|||
### STAGE 1: Build ###
|
||||
FROM sartography/cr-connect-angular-base AS builder
|
||||
|
||||
COPY . /app/
|
||||
|
||||
ARG build_config=prod
|
||||
RUN npm install && \
|
||||
npm run build:$build_config
|
||||
|
||||
### STAGE 2: Run ###
|
||||
FROM nginx:alpine
|
||||
RUN set -x && apk add --update --no-cache bash libintl gettext curl
|
||||
|
||||
COPY --from=builder /app/dist/* /etc/nginx/html/
|
||||
COPY --from=builder /app/nginx.conf /etc/nginx/conf.d/default.conf
|
||||
|
||||
# Script for substituting environment variables
|
||||
COPY ./docker/substitute-env-variables.sh ./entrypoint.sh
|
||||
RUN chmod +x ./entrypoint.sh
|
||||
|
||||
# Fix for Angular routing
|
||||
RUN echo "pushstate: enabled" > /etc/nginx/html/Staticfile
|
||||
|
||||
# The entrypoint.sh script will run after the container finishes starting.
|
||||
# Substitutes environment variables in nginx configuration and index.html,
|
||||
# then starts/reloads nginx.
|
||||
ENTRYPOINT ["./entrypoint.sh", \
|
||||
"/etc/nginx/html/index.html,/etc/nginx/conf.d/default.conf", \
|
||||
"PRODUCTION,API_URL,IRB_URL,HOME_ROUTE,BASE_HREF,DEPLOY_URL,PORT0,GOOGLE_ANALYTICS_KEY,SENTRY_KEY,TITLE", \
|
||||
"/etc/nginx/html", \
|
||||
"true"]
|
|
@ -1,7 +1,6 @@
|
|||
MIT License
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2020 Aaron Louie
|
||||
Copyright (c) 2019 Sartography
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
45
README.md
|
@ -1 +1,44 @@
|
|||
covid19-testing-kiosk
|
||||
# sartography/cr-connect-frontend
|
||||
[![Build Status](https://travis-ci.com/sartography/cr-connect-frontend.svg?branch=master)](https://travis-ci.com/sartography/cr-connect-frontend)
|
||||
[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=sartography_cr-connect-frontend&metric=coverage)](https://sonarcloud.io/dashboard?id=sartography_cr-connect-frontend)
|
||||
|
||||
# CR Connect Frontend
|
||||
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 8.3.15.
|
||||
|
||||
## Development server
|
||||
|
||||
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
|
||||
|
||||
## Local Development with Sartogprahy Libraries
|
||||
If you are making changes to the Sartography Libraries dependency,
|
||||
you can replace that line in the package.json file with something akin to this, where you supply the full path to the dist folder, remember
|
||||
to run 'npm build' in that directory for your local changes to take effect, and be sure to change this back to the proper value before
|
||||
committing your code
|
||||
```
|
||||
"sartography-workflow-lib": "/home/dan/code/workflow/sartography-libraries/dist/sartography-workflow-lib",
|
||||
```
|
||||
Also note that you need to add
|
||||
```json
|
||||
"preserveSymlinks": true
|
||||
```
|
||||
to your angular.json file in build/options.
|
||||
|
||||
## Code scaffolding
|
||||
|
||||
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
|
||||
|
||||
## Build
|
||||
|
||||
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.
|
||||
|
||||
## Running unit tests
|
||||
|
||||
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
|
||||
|
||||
## Running end-to-end tests
|
||||
|
||||
Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
|
||||
|
||||
## Further help
|
||||
|
||||
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
|
||||
|
|
|
@ -0,0 +1,176 @@
|
|||
{
|
||||
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
||||
"version": 1,
|
||||
"newProjectRoot": "projects",
|
||||
"projects": {
|
||||
"cr-connect-frontend": {
|
||||
"projectType": "application",
|
||||
"schematics": {
|
||||
"@schematics/angular:component": {
|
||||
"style": "scss"
|
||||
}
|
||||
},
|
||||
"root": "",
|
||||
"sourceRoot": "src",
|
||||
"prefix": "app",
|
||||
"architect": {
|
||||
"build": {
|
||||
"builder": "@angular-devkit/build-angular:browser",
|
||||
"options": {
|
||||
"outputPath": "dist/cr-connect-frontend",
|
||||
"index": "src/index.html",
|
||||
"main": "src/main.ts",
|
||||
"polyfills": "src/polyfills.ts",
|
||||
"tsConfig": "tsconfig.app.json",
|
||||
"aot": true,
|
||||
"assets": [
|
||||
"src/favicon.ico",
|
||||
"src/assets"
|
||||
],
|
||||
"styles": [
|
||||
"src/styles.scss"
|
||||
]
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"optimization": true,
|
||||
"outputHashing": "all",
|
||||
"sourceMap": false,
|
||||
"extractCss": true,
|
||||
"namedChunks": false,
|
||||
"extractLicenses": true,
|
||||
"vendorChunk": false,
|
||||
"buildOptimizer": true,
|
||||
"budgets": [
|
||||
{
|
||||
"type": "initial",
|
||||
"maximumWarning": "2mb",
|
||||
"maximumError": "5mb"
|
||||
},
|
||||
{
|
||||
"type": "anyComponentStyle",
|
||||
"maximumWarning": "6kb",
|
||||
"maximumError": "10kb"
|
||||
}
|
||||
]
|
||||
},
|
||||
"staging": {
|
||||
"optimization": true,
|
||||
"outputHashing": "all",
|
||||
"sourceMap": false,
|
||||
"extractCss": true,
|
||||
"namedChunks": false,
|
||||
"extractLicenses": true,
|
||||
"vendorChunk": false,
|
||||
"buildOptimizer": true,
|
||||
"budgets": [
|
||||
{
|
||||
"type": "initial",
|
||||
"maximumWarning": "2mb",
|
||||
"maximumError": "5mb"
|
||||
},
|
||||
{
|
||||
"type": "anyComponentStyle",
|
||||
"maximumWarning": "6kb",
|
||||
"maximumError": "10kb"
|
||||
}
|
||||
]
|
||||
},
|
||||
"test": {
|
||||
"optimization": true,
|
||||
"outputHashing": "all",
|
||||
"sourceMap": false,
|
||||
"extractCss": true,
|
||||
"namedChunks": false,
|
||||
"extractLicenses": true,
|
||||
"vendorChunk": false,
|
||||
"buildOptimizer": true,
|
||||
"budgets": [
|
||||
{
|
||||
"type": "initial",
|
||||
"maximumWarning": "2mb",
|
||||
"maximumError": "5mb"
|
||||
},
|
||||
{
|
||||
"type": "anyComponentStyle",
|
||||
"maximumWarning": "6kb",
|
||||
"maximumError": "10kb"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"serve": {
|
||||
"builder": "@angular-devkit/build-angular:dev-server",
|
||||
"options": {
|
||||
"browserTarget": "cr-connect-frontend:build"
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"browserTarget": "cr-connect-frontend:build:production"
|
||||
}
|
||||
}
|
||||
},
|
||||
"extract-i18n": {
|
||||
"builder": "@angular-devkit/build-angular:extract-i18n",
|
||||
"options": {
|
||||
"browserTarget": "cr-connect-frontend:build"
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
"builder": "@angular-devkit/build-angular:karma",
|
||||
"options": {
|
||||
"main": "src/test.ts",
|
||||
"polyfills": "src/polyfills.ts",
|
||||
"tsConfig": "tsconfig.spec.json",
|
||||
"karmaConfig": "karma.conf.js",
|
||||
"assets": [
|
||||
"src/favicon.ico",
|
||||
"src/assets"
|
||||
],
|
||||
"styles": [
|
||||
"src/styles.scss"
|
||||
],
|
||||
"scripts": [
|
||||
"node_modules/marked/lib/marked.js"
|
||||
],
|
||||
"codeCoverageExclude": [
|
||||
"src/polyfills.ts",
|
||||
"src/test.ts",
|
||||
"docker/**/*"
|
||||
]
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"builder": "@angular-devkit/build-angular:tslint",
|
||||
"options": {
|
||||
"tsConfig": [
|
||||
"tsconfig.app.json",
|
||||
"tsconfig.spec.json",
|
||||
"e2e/tsconfig.json"
|
||||
],
|
||||
"exclude": [
|
||||
"**/node_modules/**"
|
||||
]
|
||||
}
|
||||
},
|
||||
"e2e": {
|
||||
"builder": "@angular-devkit/build-angular:protractor",
|
||||
"options": {
|
||||
"protractorConfig": "e2e/protractor.conf.js",
|
||||
"devServerTarget": "cr-connect-frontend:serve"
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"devServerTarget": "cr-connect-frontend:serve:production"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"defaultProject": "cr-connect-frontend",
|
||||
"cli": {
|
||||
"analytics": "39bf12a6-4921-4c8d-bd17-8df6300cf98a"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
|
||||
# For additional information regarding the format and rule options, please see:
|
||||
# https://github.com/browserslist/browserslist#queries
|
||||
|
||||
# You can see what browsers were selected by your queries by running:
|
||||
# npx browserslist
|
||||
|
||||
> 0.5%
|
||||
last 2 versions
|
||||
Firefox ESR
|
||||
not dead
|
||||
not IE 9-11 # For IE 9-11 support, remove 'not'.
|
|
@ -0,0 +1,45 @@
|
|||
#!/bin/bash
|
||||
|
||||
#########################################################################
|
||||
# Builds the Docker image for the current git branch on Travis CI and
|
||||
# publishes it to Docker Hub.
|
||||
#
|
||||
# Parameters:
|
||||
# $1: Docker Hub repository to publish to
|
||||
#
|
||||
# Required environment variables (place in Settings menu on Travis CI):
|
||||
# $DOCKER_USERNAME: Docker Hub username
|
||||
# $DOCKER_TOKEN: Docker Hub access token
|
||||
#########################################################################
|
||||
|
||||
echo 'Building Docker image...'
|
||||
DOCKER_REPO="$1"
|
||||
|
||||
function branch_to_tag () {
|
||||
if [ "$1" == "master" ]; then echo "latest"; else echo "$1" ; fi
|
||||
}
|
||||
|
||||
function branch_to_deploy_group() {
|
||||
if [[ $1 =~ ^(rrt\/.*)$ ]]; then echo "rrt"; else echo "crconnect" ; fi
|
||||
}
|
||||
|
||||
DOCKER_TAG=$(branch_to_tag "$TRAVIS_BRANCH")
|
||||
|
||||
DEPLOY_GROUP=$(branch_to_deploy_group "$TRAVIS_BRANCH")
|
||||
|
||||
if [ "$DEPLOY_GROUP" == "rrt" ]; then
|
||||
IFS='/' read -ra ARR <<< "$TRAVIS_BRANCH" # Split branch on '/' character
|
||||
DOCKER_TAG=$(branch_to_tag "rrt_${ARR[1]}")
|
||||
fi
|
||||
|
||||
echo "DOCKER_REPO = $DOCKER_REPO"
|
||||
echo "DOCKER_TAG = $DOCKER_TAG"
|
||||
|
||||
echo "$DOCKER_TOKEN" | docker login -u "$DOCKER_USERNAME" --password-stdin || exit 1
|
||||
docker build -f Dockerfile -t "$DOCKER_REPO:$DOCKER_TAG" . || exit 1
|
||||
|
||||
|
||||
# Push Docker image to Docker Hub
|
||||
echo "Publishing to Docker Hub..."
|
||||
docker push "$DOCKER_REPO" || exit 1
|
||||
echo "Done."
|
|
@ -0,0 +1,95 @@
|
|||
version: "3.3"
|
||||
services:
|
||||
db:
|
||||
container_name: db
|
||||
image: sartography/cr-connect-db:$E2E_TAG
|
||||
ports:
|
||||
- "5432:5432"
|
||||
environment:
|
||||
- POSTGRES_USER=crc_user
|
||||
- POSTGRES_PASSWORD=crc_pass
|
||||
- POSTGRES_MULTIPLE_DATABASES=crc_test,pb_test
|
||||
healthcheck:
|
||||
test: ["CMD", "pg_isready"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
|
||||
pb:
|
||||
container_name: pb
|
||||
depends_on:
|
||||
- db
|
||||
image: sartography/protocol-builder-mock:$E2E_TAG
|
||||
environment:
|
||||
- APPLICATION_ROOT=/
|
||||
- CORS_ALLOW_ORIGINS=localhost:5000,backend:5000,localhost:5002,bpmn:5002,localhost:4200,frontend:4200
|
||||
- DB_HOST=db
|
||||
- DB_NAME=pb_test
|
||||
- DB_PASSWORD=crc_pass
|
||||
- DB_PORT=5432
|
||||
- DB_USER=crc_user
|
||||
- PORT0=5001
|
||||
- UPGRADE_DB=true
|
||||
ports:
|
||||
- "5001:5001"
|
||||
command: ./wait-for-it.sh db:5432 -t 0 -- ./docker_run.sh
|
||||
|
||||
backend:
|
||||
container_name: backend
|
||||
depends_on:
|
||||
- db
|
||||
- pb
|
||||
image: sartography/cr-connect-workflow:$E2E_TAG
|
||||
environment:
|
||||
- APPLICATION_ROOT=/
|
||||
- CORS_ALLOW_ORIGINS=localhost:5002,bpmn:5002,localhost:4200,frontend:4200
|
||||
- DB_HOST=db
|
||||
- DB_NAME=crc_test
|
||||
- DB_PASSWORD=crc_pass
|
||||
- DB_PORT=5432
|
||||
- DB_USER=crc_user
|
||||
- DEVELOPMENT=true
|
||||
- LDAP_URL=mock
|
||||
- PB_BASE_URL=http://pb:5001/v2.0/
|
||||
- PB_ENABLED=true
|
||||
- PORT0=5000
|
||||
- PRODUCTION=false
|
||||
- RESET_DB=true
|
||||
- TESTING=false
|
||||
- UPGRADE_DB=true
|
||||
ports:
|
||||
- "5000:5000"
|
||||
command: ./wait-for-it.sh pb:5001 -t 0 -- ./docker_run.sh
|
||||
|
||||
|
||||
# bpmn:
|
||||
# container_name: bpmn
|
||||
# depends_on:
|
||||
# - db
|
||||
# - backend
|
||||
# image: sartography/cr-connect-bpmn:dev
|
||||
# environment:
|
||||
# - API_URL=http://localhost:5000/api/v1.0
|
||||
# - BASE_HREF=/bpmn/
|
||||
# - DEPLOY_URL=/bpmn/
|
||||
# - HOME_ROUTE=home
|
||||
# - PORT0=5002
|
||||
# - PRODUCTION=false
|
||||
# ports:
|
||||
# - "5002:5002"
|
||||
#
|
||||
# frontend:
|
||||
# container_name: frontend
|
||||
# depends_on:
|
||||
# - db
|
||||
# - backend
|
||||
# image: sartography/cr-connect-frontend:dev
|
||||
# environment:
|
||||
# - API_URL=http://localhost:5000/api/v1.0
|
||||
# - BASE_HREF=/app/
|
||||
# - DEPLOY_URL=/app/
|
||||
# - HOME_ROUTE=home
|
||||
# - PORT0=4200
|
||||
# - PRODUCTION=false
|
||||
# ports:
|
||||
# - "4200:4200"
|
|
@ -0,0 +1,123 @@
|
|||
#!/bin/bash
|
||||
|
||||
#####################################################################
|
||||
# Substitutes the given environment variables in the given files.
|
||||
# Parameters:
|
||||
# $1: Comma-delimited list of file paths
|
||||
# $2: Comma-delimited list of environment variables
|
||||
# $3: Absolute path to nginx html directory (optional)
|
||||
# $4: Should restart nginx (optional)
|
||||
#####################################################################
|
||||
|
||||
echo 'Substituting environment variables...'
|
||||
num_args=0
|
||||
|
||||
# The first parameter is a comma-delimited list of paths to files which should be substituted
|
||||
if [[ -z $1 ]]; then
|
||||
echo 'ERROR: No target files given.'
|
||||
exit 1
|
||||
else
|
||||
num_args=1
|
||||
fi
|
||||
|
||||
# The second parameter is a comma-delimited list of environment variable names
|
||||
if [[ -z $2 ]]; then
|
||||
echo 'ERROR: No environment variables given.'
|
||||
exit 1
|
||||
else
|
||||
num_args=2
|
||||
fi
|
||||
|
||||
# The third parameter is the absolute path to the nginx html directory
|
||||
if [[ -z $3 ]]; then
|
||||
echo '' # It's optional. Don't print anything.
|
||||
else
|
||||
num_args=3
|
||||
fi
|
||||
|
||||
# The fourth parameter, if 'true', is whether we should reload nginx
|
||||
if [[ -z $4 ]]; then
|
||||
echo '' # It's optional. Don't print anything.
|
||||
else
|
||||
num_args=4
|
||||
fi
|
||||
|
||||
# Find & replace BASE_HREF in all files in the nginx html directory
|
||||
if [[ "$2" == *"BASE_HREF"* ]] && [[ "$2" == *"DEPLOY_URL"* ]]; then
|
||||
# Add trailing slash to $BASE_HREF if needed
|
||||
length=${#BASE_HREF}
|
||||
last_char=${BASE_HREF:length-1:1}
|
||||
[[ $last_char != "/" ]] && BASE_HREF="$BASE_HREF/"; :
|
||||
|
||||
# Add trailing slash to $DEPLOY_URL if needed
|
||||
length=${#DEPLOY_URL}
|
||||
last_char=${DEPLOY_URL:length-1:1}
|
||||
[[ $last_char != "/" ]] && DEPLOY_URL="$DEPLOY_URL/"; :
|
||||
|
||||
# The third parameter is the absolute path to the nginx html directory
|
||||
if [[ $num_args -ge 3 ]]; then
|
||||
# Replace all instances of __REPLACE_ME_WITH_BASE_HREF__ with $BASE_HREF
|
||||
find "$3" \( -type d -name .git -prune \) -o -type f -print0 | \
|
||||
xargs -0 sed -i 's@__REPLACE_ME_WITH_BASE_HREF__@'"$BASE_HREF"'@g'
|
||||
|
||||
echo 'Replacing base href...'
|
||||
# Wait a few seconds in case find | sed needs more time
|
||||
sleep 3
|
||||
|
||||
# Replace all instances of __REPLACE_ME_WITH_DEPLOY_URL__ with $DEPLOY_URL
|
||||
find "$3" \( -type d -name .git -prune \) -o -type f -print0 | \
|
||||
xargs -0 sed -i 's@__REPLACE_ME_WITH_DEPLOY_URL__@'"$DEPLOY_URL"'@g'
|
||||
|
||||
echo 'Replacing deploy url...'
|
||||
# Wait a few seconds in case find | sed needs more time
|
||||
sleep 3
|
||||
fi
|
||||
fi
|
||||
|
||||
# Convert "VAR1,VAR2,VAR3,..." to "\$VAR1 \$VAR2 \$VAR3 ..."
|
||||
env_list="\\\$${2//,/ \\\$}" # "\" and "$" are escaped as "\\" and "\$"
|
||||
for file_path in ${1//,/ }
|
||||
do
|
||||
echo "replacing environment variables in $file_path"
|
||||
|
||||
# Replace strings in the given file(s) in env_list
|
||||
envsubst "$env_list" < "$file_path" > "$file_path".tmp && mv "$file_path".tmp "$file_path"
|
||||
|
||||
echo '...'
|
||||
# Wait a second in case envsubst needs more time
|
||||
sleep 1
|
||||
|
||||
# If this is the nginx default.conf file, replace double slashes with single slashes
|
||||
if [[ $file_path == *"/default.conf"* ]]; then
|
||||
sed -i -e 's@//@/@g' "$file_path"
|
||||
fi
|
||||
done
|
||||
|
||||
echo 'Finished substituting environment variables.'
|
||||
for env_var in ${2//,/ }
|
||||
do
|
||||
echo "$env_var = ${!env_var}"
|
||||
done
|
||||
|
||||
# Reload nginx
|
||||
if [ $num_args -ge 4 ] && [ "$4" == "true" ]; then
|
||||
# Check to see if nginx command is available
|
||||
if hash nginx 2> /dev/null; then
|
||||
# Check to see if nginx is already running
|
||||
if [ -e /var/run/nginx.pid ]; then
|
||||
echo "nginx is currently running. Reloading nginx..."
|
||||
exec nginx -s reload
|
||||
echo "nginx reloaded."
|
||||
else
|
||||
echo "nginx is not yet running. Starting nginx..."
|
||||
exec nginx -g 'daemon off;'
|
||||
echo "nginx started."
|
||||
fi
|
||||
else
|
||||
echo "nginx command not found on this system."
|
||||
fi
|
||||
fi
|
||||
|
||||
# Execute all other commands with parameters
|
||||
num_args=$((num_args + 1))
|
||||
exec "${@:num_args}"
|
|
@ -0,0 +1,32 @@
|
|||
// @ts-check
|
||||
// Protractor configuration file, see link for more information
|
||||
// https://github.com/angular/protractor/blob/master/lib/config.ts
|
||||
|
||||
const { SpecReporter } = require('jasmine-spec-reporter');
|
||||
|
||||
/**
|
||||
* @type { import("protractor").Config }
|
||||
*/
|
||||
exports.config = {
|
||||
allScriptsTimeout: 60000,
|
||||
specs: [
|
||||
'./src/**/*.e2e-spec.ts'
|
||||
],
|
||||
capabilities: {
|
||||
browserName: 'chrome'
|
||||
},
|
||||
directConnect: true,
|
||||
baseUrl: 'http://localhost:4200/',
|
||||
framework: 'jasmine',
|
||||
jasmineNodeOpts: {
|
||||
showColors: true,
|
||||
defaultTimeoutInterval: 60000,
|
||||
print: function() {}
|
||||
},
|
||||
onPrepare() {
|
||||
require('ts-node').register({
|
||||
project: require('path').join(__dirname, './tsconfig.json')
|
||||
});
|
||||
jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
|
||||
}
|
||||
};
|
|
@ -0,0 +1,125 @@
|
|||
import {HttpClient} from 'protractor-http-client';
|
||||
import {AppPage} from './app.po';
|
||||
|
||||
describe('Clinical Research Coordinator App', () => {
|
||||
let page: AppPage;
|
||||
let http: HttpClient;
|
||||
|
||||
beforeEach(() => {
|
||||
page = new AppPage();
|
||||
http = new HttpClient('http://localhost:5001');
|
||||
});
|
||||
|
||||
it('should automatically sign-in and redirect to home screen', () => {
|
||||
page.navigateTo();
|
||||
expect(page.getRoute()).toEqual('/home');
|
||||
expect(page.getElements('#cta_protocol_builder').count()).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it('should navigate to help screen', () => {
|
||||
page.clickAndExpectRoute('#nav_help', '/help');
|
||||
});
|
||||
|
||||
it('should navigate to profile', () => {
|
||||
page.clickElement('#nav_account');
|
||||
page.clickAndExpectRoute('#nav_profile', '/profile');
|
||||
});
|
||||
|
||||
it('should navigate to notifications', () => {
|
||||
page.clickElement('#nav_account');
|
||||
page.clickAndExpectRoute('#nav_notifications', '/notifications');
|
||||
});
|
||||
|
||||
it('should navigate back to home screen', () => {
|
||||
page.setLocalStorageVar('numstudy', '1');
|
||||
expect(page.getLocalStorageVar('numstudy')).toEqual('1');
|
||||
page.clickAndExpectRoute('#nav_home', '/home');
|
||||
expect(page.getElements('#cta_protocol_builder').count()).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it('should open Protocol Builder in new window', async () => {
|
||||
expect(page.getElements('#cta_protocol_builder').count()).toEqual(1);
|
||||
expect(page.getElements('#cta_reload_studies').count()).toEqual(1);
|
||||
|
||||
// Open Protocol Builder in new tab.
|
||||
const numTabsBefore = await page.getNumTabs();
|
||||
page.clickElement('#cta_protocol_builder');
|
||||
const numTabsAfter = await page.getNumTabs();
|
||||
expect(numTabsAfter).toBeGreaterThan(numTabsBefore);
|
||||
|
||||
// Close Protocol Builder tab.
|
||||
await page.switchFocusToTab(1);
|
||||
await page.closeTab();
|
||||
await page.switchFocusToTab(0);
|
||||
});
|
||||
|
||||
it('should load new study from Protocol Builder', async () => {
|
||||
const numStudiesBefore = await page.getElements('.study-row').count();
|
||||
|
||||
// Add a new study to Protocol Builder.
|
||||
http.post('/new_study', '' +
|
||||
`STUDYID=${Math.floor(Math.random() * 100000)}&` +
|
||||
`TITLE=${encodeURIComponent('New study title')}&` +
|
||||
`NETBADGEID=dhf8r&` +
|
||||
`DATE_MODIFIED=${encodeURIComponent(new Date().toISOString())}&` +
|
||||
`requirements=9&` +
|
||||
`requirements=21&` +
|
||||
`requirements=40&` +
|
||||
`requirements=44&` +
|
||||
`requirements=52&` +
|
||||
`requirements=53&` +
|
||||
`Q_COMPLETE=y`,
|
||||
{'Content-Type': 'application/x-www-form-urlencoded'}
|
||||
).catch(error => {
|
||||
console.error(error);
|
||||
});
|
||||
http.failOnHttpError = false;
|
||||
|
||||
// Reload the list of studies.
|
||||
await page.clickElement('#cta_reload_studies');
|
||||
await page.waitForNotVisible('.loading');
|
||||
await page.waitForClickable('.study-row');
|
||||
|
||||
const numStudiesAfter = await page.getElements('.study-row').count();
|
||||
expect(numStudiesAfter).toBeGreaterThan(numStudiesBefore);
|
||||
});
|
||||
|
||||
it('should navigate to a study', async () => {
|
||||
const studyRow = page.getElement('.study-row');
|
||||
const studyId = await studyRow.getAttribute('data-study-id');
|
||||
await expect(studyId).not.toBeUndefined();
|
||||
await expect(studyId).not.toBeNull();
|
||||
page.clickAndExpectRoute('.study-row', '/study/' + studyId);
|
||||
});
|
||||
|
||||
it('should display workflow spec categories in tiles', async () => {
|
||||
const numTiles = await page.getElements('.workflow-list-item').count();
|
||||
expect(numTiles).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it('should navigate to a workflow', async () => {
|
||||
const wfSelector = '.workflow-list-item .workflow-action';
|
||||
expect(page.getElements(wfSelector).count()).toBeGreaterThan(0);
|
||||
const workflow = await page.getElement(wfSelector);
|
||||
const studyId = await workflow.getAttribute('data-study-id');
|
||||
const catId = await workflow.getAttribute('data-category-id');
|
||||
const workflowId = await workflow.getAttribute('data-workflow-id');
|
||||
|
||||
console.log('studyId', studyId);
|
||||
console.log('catId', catId);
|
||||
console.log('workflowId', workflowId);
|
||||
const expectedRoute = `/study/${studyId}?category=${catId}&workflow=${workflowId}`;
|
||||
await page.clickElement(wfSelector);
|
||||
const newRoute = await page.getRoute();
|
||||
expect(newRoute.slice(0, expectedRoute.length)).toEqual(expectedRoute);
|
||||
});
|
||||
|
||||
// TODO: CATCH 401/403 ERRORS AND VERIFY THAT THEY REDIRECT TO LOGIN
|
||||
// afterEach(async () => {
|
||||
// // Assert that there are no errors emitted from the browser
|
||||
// const logs = await browser.manage().logs().get(logging.Type.BROWSER);
|
||||
// expect(logs).not.toContain(jasmine.objectContaining({
|
||||
// level: logging.Level.SEVERE,
|
||||
// } as logging.Entry));
|
||||
// });
|
||||
});
|
|
@ -0,0 +1,103 @@
|
|||
import {browser, by, element, ElementArrayFinder, ElementFinder, ExpectedConditions} from 'protractor';
|
||||
|
||||
export class AppPage {
|
||||
navigateTo() {
|
||||
return browser.get(browser.baseUrl) as Promise<any>;
|
||||
}
|
||||
|
||||
clickAndExpectRoute(clickSelector: string, expectedRoute: string | RegExp) {
|
||||
this.waitForClickable(clickSelector);
|
||||
this.clickElement(clickSelector);
|
||||
if (typeof expectedRoute === 'string') {
|
||||
expect(this.getRoute()).toEqual(expectedRoute);
|
||||
} else {
|
||||
expect(this.getRoute()).toMatch(expectedRoute);
|
||||
}
|
||||
}
|
||||
|
||||
clickElement(selector: string) {
|
||||
this.waitForClickable(selector);
|
||||
this.scrollTo(selector);
|
||||
this.focus(selector);
|
||||
return this.getElement(selector).click();
|
||||
}
|
||||
|
||||
closeTab() {
|
||||
return browser.close();
|
||||
}
|
||||
|
||||
focus(selector: string) {
|
||||
return browser.controlFlow().execute(() => {
|
||||
return browser.executeScript('arguments[0].focus()', this.getElement(selector).getWebElement());
|
||||
});
|
||||
}
|
||||
|
||||
getElement(selector: string): ElementFinder {
|
||||
return element.all(by.css(selector)).first();
|
||||
}
|
||||
|
||||
getElements(selector: string): ElementArrayFinder {
|
||||
return element.all(by.css(selector));
|
||||
}
|
||||
|
||||
getLocalStorageVar(name: string) {
|
||||
return browser.executeScript(`return window.localStorage.getItem('${name}');`);
|
||||
}
|
||||
|
||||
getNumTabs() {
|
||||
return browser.getAllWindowHandles().then(wh => {
|
||||
return wh.length;
|
||||
});
|
||||
}
|
||||
|
||||
async getRoute() {
|
||||
const url = await this.getUrl();
|
||||
return '/' + url.split(browser.baseUrl)[1];
|
||||
}
|
||||
|
||||
getText(selector: string) {
|
||||
return element(by.css(selector)).getText() as Promise<string>;
|
||||
}
|
||||
|
||||
getUrl() {
|
||||
return browser.getCurrentUrl();
|
||||
}
|
||||
|
||||
scrollTo(selector: string) {
|
||||
browser.controlFlow().execute(() => {
|
||||
browser.executeScript('arguments[0].scrollIntoView(false)', this.getElement(selector).getWebElement());
|
||||
});
|
||||
}
|
||||
|
||||
setLocalStorageVar(name: string, value: string) {
|
||||
return browser.executeScript(`return window.localStorage.setItem('${name}','${value}');`);
|
||||
}
|
||||
|
||||
switchFocusToTab(tabIndex: number) {
|
||||
return browser.getAllWindowHandles().then(wh => {
|
||||
return wh.forEach((h, i) => {
|
||||
if (i === tabIndex) { return browser.switchTo().window(h); }
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
waitFor(t: number) {
|
||||
return browser.sleep(t);
|
||||
}
|
||||
|
||||
waitForClickable(selector: string) {
|
||||
const e = this.getElement(selector);
|
||||
return browser.wait(ExpectedConditions.elementToBeClickable(e), 5000);
|
||||
}
|
||||
|
||||
waitForNotVisible(selector: string) {
|
||||
const e = this.getElement(selector);
|
||||
return browser.wait(ExpectedConditions.invisibilityOf(e), 5000);
|
||||
}
|
||||
|
||||
waitForVisible(selector: string) {
|
||||
const e = this.getElement(selector);
|
||||
return browser.wait(ExpectedConditions.visibilityOf(e), 5000);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../out-tsc/e2e",
|
||||
"module": "commonjs",
|
||||
"target": "es5",
|
||||
"types": [
|
||||
"jasmine",
|
||||
"jasminewd2",
|
||||
"node"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
// Karma configuration file, see link for more information
|
||||
// https://karma-runner.github.io/1.0/config/configuration-file.html
|
||||
|
||||
module.exports = function (config) {
|
||||
|
||||
if (config.browsers.indexOf('ChromeHeadlessCI') !== -1) {
|
||||
process.env.CHROME_BIN = require('puppeteer').executablePath();
|
||||
}
|
||||
|
||||
config.set({
|
||||
files: ['karma.globals.js'],
|
||||
basePath: '',
|
||||
frameworks: ['jasmine', '@angular-devkit/build-angular'],
|
||||
plugins: [
|
||||
require('karma-jasmine'),
|
||||
require('karma-chrome-launcher'),
|
||||
require('karma-jasmine-html-reporter'),
|
||||
require('karma-coverage-istanbul-reporter'),
|
||||
require('@angular-devkit/build-angular/plugins/karma')
|
||||
],
|
||||
client: {
|
||||
clearContext: false // leave Jasmine Spec Runner output visible in browser
|
||||
},
|
||||
coverageIstanbulReporter: {
|
||||
dir: require('path').join(__dirname, './coverage'),
|
||||
reports: ['lcovonly'],
|
||||
fixWebpackSourcePaths: true
|
||||
},
|
||||
customLaunchers: {
|
||||
ChromeHeadlessCI: {
|
||||
base: 'ChromeHeadless',
|
||||
flags: ['--no-sandbox', '--disable-gpu' ]
|
||||
}
|
||||
},
|
||||
reporters: ['progress', 'kjhtml'],
|
||||
port: 9876,
|
||||
colors: true,
|
||||
logLevel: config.LOG_INFO,
|
||||
autoWatch: true,
|
||||
browsers: ['Chrome'],
|
||||
singleRun: false,
|
||||
restartOnFileChange: true
|
||||
});
|
||||
};
|
|
@ -0,0 +1,6 @@
|
|||
var ENV = {
|
||||
production: 'false',
|
||||
api: 'apiRoot',
|
||||
irbUrl: 'irbUrl',
|
||||
homeRoute: 'home',
|
||||
};
|
|
@ -0,0 +1,12 @@
|
|||
server {
|
||||
listen $PORT0;
|
||||
|
||||
port_in_redirect off;
|
||||
|
||||
location $BASE_HREF/ {
|
||||
alias /etc/nginx/html/;
|
||||
index index.html index.htm;
|
||||
try_files $uri$args $uri $BASE_HREF/index.html;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
{
|
||||
"name": "cr-connect-frontend",
|
||||
"version": "0.0.0",
|
||||
"scripts": {
|
||||
"ng": "ng",
|
||||
"start": "ng serve",
|
||||
"build": "ng build",
|
||||
"build:prod": "ng build --configuration=production --prod --base-href=__REPLACE_ME_WITH_BASE_HREF__ --deploy-url=__REPLACE_ME_WITH_DEPLOY_URL__",
|
||||
"build:staging": "ng build --configuration=staging --prod --base-href=__REPLACE_ME_WITH_BASE_HREF__ --deploy-url=__REPLACE_ME_WITH_DEPLOY_URL__",
|
||||
"build:test": "ng build --configuration=test",
|
||||
"test": "ng test",
|
||||
"test:coverage": "ng test --codeCoverage=true --watch=false --browsers=ChromeHeadless",
|
||||
"lint": "ng lint",
|
||||
"e2e": "./node_modules/protractor/bin/webdriver-manager update && ng e2e",
|
||||
"e2e:with-backend": "npm run backend && ng e2e && npm run backend:stop",
|
||||
"backend:stop": "cd docker && docker-compose down && cd ..",
|
||||
"backend:build": "cd docker && docker-compose pull && docker-compose build && cd ..",
|
||||
"backend:start": "cd docker && docker-compose up -d --force-recreate && cd ..",
|
||||
"backend": "npm run backend:stop && npm run backend:build && npm run backend:start",
|
||||
"env": "chmod +x ./docker/substitute-env-variables.sh && ./docker/substitute-env-variables.sh src/index.html PRODUCTION,API_URL,IRB_URL,HOME_ROUTE,BASE_HREF,DEPLOY_URL,PORT0,GOOGLE_ANALYTICS_KEY,SENTRY_KEY,TITLE",
|
||||
"ci": "npm run lint && npm run test:coverage && sonar-scanner && npm run env && npm run backend && npm run e2e"
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "~9.1.11",
|
||||
"@angular/cdk": "^9.2.4",
|
||||
"@angular/common": "~9.1.11",
|
||||
"@angular/compiler": "~9.1.11",
|
||||
"@angular/core": "~9.1.11",
|
||||
"@angular/flex-layout": "^9.0.0-beta.31",
|
||||
"@angular/forms": "~9.1.11",
|
||||
"@angular/material": "^9.2.4",
|
||||
"@angular/platform-browser": "~9.1.11",
|
||||
"@angular/platform-browser-dynamic": "~9.1.11",
|
||||
"@angular/router": "~9.1.11",
|
||||
"@ngx-formly/core": "^5.8.0",
|
||||
"@ngx-formly/material": "^5.8.0",
|
||||
"lodash.isequal": "^4.5.0",
|
||||
"rfdc": "^1.1.4",
|
||||
"rxjs": "~6.5.4",
|
||||
"tslib": "^1.13.0",
|
||||
"zone.js": "~0.10.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "^0.901.10",
|
||||
"@angular/cli": "^9.1.10",
|
||||
"@angular/compiler-cli": "~9.1.11",
|
||||
"@angular/language-service": "~9.1.11",
|
||||
"@types/jasmine": "^3.5.11",
|
||||
"@types/jasminewd2": "~2.0.3",
|
||||
"@types/node": "^12.12.47",
|
||||
"codelyzer": "^5.1.2",
|
||||
"jasmine-core": "~3.5.0",
|
||||
"jasmine-spec-reporter": "~4.2.1",
|
||||
"karma": "^5.1.0",
|
||||
"karma-chrome-launcher": "~3.1.0",
|
||||
"karma-coverage-istanbul-reporter": "~2.1.1",
|
||||
"karma-jasmine": "~3.1.1",
|
||||
"karma-jasmine-html-reporter": "^1.5.4",
|
||||
"lodash.get": "^4.4.2",
|
||||
"lodash.set": "^4.3.2",
|
||||
"protractor": "^7.0.0",
|
||||
"protractor-http-client": "^1.0.4",
|
||||
"sonar-scanner": "^3.1.0",
|
||||
"ts-node": "~8.6.2",
|
||||
"tslint": "~6.0.0",
|
||||
"typescript": "~3.7.5"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
sonar.organization=sartography
|
||||
sonar.projectKey=sartography_cr-connect-frontend
|
||||
sonar.host.url=https://sonarcloud.io
|
||||
sonar.javascript.lcov.reportPaths=coverage/lcov.info
|
||||
sonar.typescript.tsconfigPath=tsconfig.json
|
||||
sonar.coverage.exclusions=**/*.mocks.ts, **/*.spec.ts, e2e/**, src/assets/**, src/main.ts, src/test.ts, src/polyfills.ts, karma.conf.js, docker/**
|
||||
sonar.cpd.exclusions=**/*.mocks.ts, **/*.spec.ts, e2e/**, src/assets/**, src/main.ts, src/test.ts, src/polyfills.ts, karma.conf.js, docker/**
|
||||
sonar.exclusions=src/assets/**
|
|
@ -0,0 +1,65 @@
|
|||
// GLOBAL SCSS VARIABLES
|
||||
$body-font-family: 'franklin-gothic-urw', sans-serif;
|
||||
$heading-font-family: 'franklin-gothic-urw-cond', sans-serif;
|
||||
|
||||
// COLOR PALETTE
|
||||
|
||||
// gray
|
||||
$brand-gray: #4e4e4e;
|
||||
$brand-gray-tint-1: #666666;
|
||||
$brand-gray-tint-2: #DADADA;
|
||||
$brand-gray-tint-3: #F1F1EF;
|
||||
$brand-gray-tint-4: scale-color($brand-gray, $lightness: 90%);
|
||||
$body-color: $brand-gray;
|
||||
$brand-gray-shade-1: scale-color($brand-gray, $lightness: -20%);
|
||||
$brand-gray-shade-2: scale-color($brand-gray, $lightness: -40%);
|
||||
$brand-gray-shade-3: scale-color($brand-gray, $lightness: -60%);
|
||||
$brand-gray-shade-4: scale-color($brand-gray, $lightness: -80%);
|
||||
$brand-gray-shade-5: scale-color($brand-gray, $lightness: -100%);
|
||||
$body-color-muted: $brand-gray-tint-1;
|
||||
$body-color-light: $brand-gray-tint-4;
|
||||
$brand-gray-muted: $brand-gray-tint-1;
|
||||
$brand-gray-light: $brand-gray-tint-4;
|
||||
|
||||
// primary (UVA "Jefferson Blue")
|
||||
$brand-primary: #232D4B;
|
||||
$brand-primary-tint-1: #394E79;
|
||||
$brand-primary-tint-2: #6C799C;
|
||||
$brand-primary-tint-3: #A9AFC7;
|
||||
$brand-primary-tint-4: scale-color($brand-primary, $lightness: 90%);
|
||||
$brand-primary-shade-1: #092255;
|
||||
$brand-primary-shade-2: #041D4F;
|
||||
$brand-primary-shade-3: #02194A;
|
||||
$brand-primary-shade-4: #021745;
|
||||
$brand-primary-shade-5: #03143E;
|
||||
$brand-primary-muted: $brand-primary-tint-1;
|
||||
$brand-primary-light: $brand-primary-tint-4;
|
||||
|
||||
// accent (UVA "Rotunda Orange")
|
||||
$brand-accent: #E57200;
|
||||
$brand-accent-tint-1: #F69350;
|
||||
$brand-accent-tint-2: #FAB584;
|
||||
$brand-accent-tint-3: #FDD8BB;
|
||||
$brand-accent-tint-4: scale-color($brand-accent, $lightness: 90%);
|
||||
$brand-accent-shade-1: #E76E25;
|
||||
$brand-accent-shade-2: #DD6923;
|
||||
$brand-accent-shade-3: #D36421;
|
||||
$brand-accent-shade-4: #C8601F;
|
||||
$brand-accent-shade-5: #C05B1D;
|
||||
$brand-accent-muted: $brand-accent-tint-1;
|
||||
$brand-accent-light: $brand-accent-tint-4;
|
||||
|
||||
// warning (UVA "Emergency Red")
|
||||
$brand-warning: #DF1E43;
|
||||
$brand-warning-muted: desaturate(scale-color($brand-warning, $lightness: 30%), 20%);
|
||||
$brand-warning-light: scale-color($brand-warning, $lightness: 90%);
|
||||
|
||||
// Green
|
||||
$brand-green: #64B343;
|
||||
$brand-green-muted: #8EC774;
|
||||
$brand-green-light: #B5D9A3;
|
||||
|
||||
// Dimensions
|
||||
$easeDuration: 300ms;
|
||||
$animationDuration: 500ms;
|
||||
$header-height: 84px;
|
|
@ -0,0 +1,92 @@
|
|||
@import 'config';
|
||||
@import '../node_modules/@angular/material/theming';
|
||||
|
||||
// Define a custom typography config that overrides the font-family
|
||||
$custom-typography: mat-typography-config(
|
||||
$font-family: $body-font-family,
|
||||
$display-4: mat-typography-level(3.75rem, 1.0925, 700, $body-font-family),
|
||||
$display-3: mat-typography-level(2.4917rem, 1.1037, 700, $body-font-family),
|
||||
$display-2: mat-typography-level(2.2148rem, 1.1287, 700, $body-font-family),
|
||||
$display-1: mat-typography-level(1.9688rem, 1.1429, 700, $body-font-family),
|
||||
$headline: mat-typography-level(1.7500rem, 1.1429, 700, $body-font-family), // h1
|
||||
$title: mat-typography-level(1.5625rem, 1.1200, 500, $body-font-family), // h2
|
||||
$subheading-2: mat-typography-level(1.3750rem, 1.0000, 700, $heading-font-family), // h3
|
||||
$subheading-1: mat-typography-level(1.2500rem, 1.1000, 700, $body-font-family), // h4
|
||||
$body-2: mat-typography-level(1.1250rem, 1.1111, 500, $body-font-family), // h5
|
||||
$body-1: mat-typography-level(1.0000rem, 1.1250, 500, $body-font-family), // p
|
||||
$caption: mat-typography-level(0.8750rem, 0.7143, 500, $body-font-family), // small
|
||||
$button: mat-typography-level(1.0000rem, 1.1250, 500, $body-font-family),
|
||||
$input: mat-typography-level(1.0000rem, 1.1250, 500, $body-font-family)
|
||||
);
|
||||
|
||||
$mat-blue: (
|
||||
50: #f1f5f7,
|
||||
100: #b3c1d3,
|
||||
200: $brand-primary-tint-3,
|
||||
300: $brand-primary-tint-2,
|
||||
400: $brand-primary-tint-1,
|
||||
500: $brand-primary,
|
||||
600: $brand-primary-shade-1,
|
||||
700: $brand-primary-shade-2,
|
||||
800: $brand-primary-shade-3,
|
||||
900: $brand-primary-shade-4,
|
||||
A100: #b3c1d3,
|
||||
A200: $brand-primary-tint-3,
|
||||
A400: $brand-primary-tint-2,
|
||||
A700: $brand-primary-tint-1,
|
||||
contrast: (
|
||||
50: $body-color,
|
||||
100: $body-color,
|
||||
200: $body-color,
|
||||
300: #ffffff,
|
||||
400: #ffffff,
|
||||
500: #ffffff,
|
||||
600: #ffffff,
|
||||
700: #ffffff,
|
||||
800: #ffffff,
|
||||
900: #ffffff,
|
||||
A100: $body-color,
|
||||
A200: #ffffff,
|
||||
A400: #ffffff,
|
||||
A700: #ffffff,
|
||||
)
|
||||
);
|
||||
|
||||
$mat-orange: (
|
||||
50: #fceee0,
|
||||
100: $brand-accent-tint-3,
|
||||
200: $brand-accent-tint-2,
|
||||
300: $brand-accent-tint-1,
|
||||
400: $brand-accent,
|
||||
500: $brand-accent-shade-1,
|
||||
600: $brand-accent-shade-2,
|
||||
700: $brand-accent-shade-3,
|
||||
800: $brand-accent-shade-4,
|
||||
900: $brand-accent-shade-5,
|
||||
A100: #fceee0,
|
||||
A200: $brand-accent-tint-3,
|
||||
A400: $brand-accent-tint-2,
|
||||
A700: $brand-accent-tint-1,
|
||||
contrast: (
|
||||
50: $body-color,
|
||||
100: $body-color,
|
||||
200: $body-color,
|
||||
300: $body-color,
|
||||
400: $body-color,
|
||||
500: #ffffff,
|
||||
600: #ffffff,
|
||||
700: #ffffff,
|
||||
800: #ffffff,
|
||||
900: #ffffff,
|
||||
A100: $body-color,
|
||||
A200: $body-color,
|
||||
A400: $body-color,
|
||||
A700: $body-color,
|
||||
)
|
||||
);
|
||||
|
||||
$cr-connect-primary: mat-palette($mat-blue);
|
||||
$cr-connect-accent: mat-palette($mat-orange);
|
||||
$cr-connect-warn: mat-palette($mat-red);
|
||||
$cr-connect-theme: mat-light-theme($cr-connect-primary, $cr-connect-accent, $cr-connect-warn);
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
import {APP_BASE_HREF, Location} from '@angular/common';
|
||||
import {HttpClient} from '@angular/common/http';
|
||||
import {HttpClientTestingModule} from '@angular/common/http/testing';
|
||||
import {TestBed} from '@angular/core/testing';
|
||||
import {FormsModule} from '@angular/forms';
|
||||
import {MatButtonModule} from '@angular/material/button';
|
||||
import {MatCardModule} from '@angular/material/card';
|
||||
import {MatFormFieldModule} from '@angular/material/form-field';
|
||||
import {MatIconModule} from '@angular/material/icon';
|
||||
import {MatListModule} from '@angular/material/list';
|
||||
import {MatMenuModule} from '@angular/material/menu';
|
||||
import {MatProgressBarModule} from '@angular/material/progress-bar';
|
||||
import {MatProgressSpinnerModule} from '@angular/material/progress-spinner';
|
||||
import {MatSelectModule} from '@angular/material/select';
|
||||
import {MatSidenavModule} from '@angular/material/sidenav';
|
||||
import {MatToolbarModule} from '@angular/material/toolbar';
|
||||
import {BrowserAnimationsModule, NoopAnimationsModule} from '@angular/platform-browser/animations';
|
||||
import {Router} from '@angular/router';
|
||||
import {RouterTestingModule} from '@angular/router/testing';
|
||||
import {FormlyModule} from '@ngx-formly/core';
|
||||
import {FormlyMaterialModule} from '@ngx-formly/material';
|
||||
import {ChartsModule} from 'ng2-charts';
|
||||
import {MarkdownModule} from 'ngx-markdown';
|
||||
import {ApiService, MockEnvironment, SessionRedirectComponent, ToFormlyPipe} from 'sartography-workflow-lib';
|
||||
import {routes} from './app-routing.module';
|
||||
import {AppComponent} from './app.component';
|
||||
import {DashboardComponent} from './dashboard/dashboard.component';
|
||||
import {FooterComponent} from './footer/footer.component';
|
||||
import {HelpComponent} from './help/help.component';
|
||||
import {HomeComponent} from './home/home.component';
|
||||
import {InboxComponent} from './inbox/inbox.component';
|
||||
import {NavbarComponent} from './navbar/navbar.component';
|
||||
import {NotificationsComponent} from './notifications/notifications.component';
|
||||
import {ProfileComponent} from './profile/profile.component';
|
||||
import {StudiesComponent} from './studies/studies.component';
|
||||
import {StudyComponent} from './study/study.component';
|
||||
import {WorkflowFilesComponent} from './workflow-files/workflow-files.component';
|
||||
import {WorkflowFormComponent} from './workflow-form/workflow-form.component';
|
||||
import {WorkflowStepsMenuListComponent} from './workflow-steps-menu-list/workflow-steps-menu-list.component';
|
||||
import {WorkflowComponent} from './workflow/workflow.component';
|
||||
|
||||
|
||||
describe('Router: App', () => {
|
||||
let location: Location;
|
||||
let router: Router;
|
||||
let fixture;
|
||||
const mockEnvironment = new MockEnvironment();
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [
|
||||
AppComponent,
|
||||
DashboardComponent,
|
||||
DashboardComponent,
|
||||
FooterComponent,
|
||||
HelpComponent,
|
||||
HomeComponent,
|
||||
InboxComponent,
|
||||
NavbarComponent,
|
||||
NotificationsComponent,
|
||||
ProfileComponent,
|
||||
SessionRedirectComponent,
|
||||
StudiesComponent,
|
||||
StudyComponent,
|
||||
ToFormlyPipe,
|
||||
WorkflowComponent,
|
||||
WorkflowFilesComponent,
|
||||
WorkflowFormComponent,
|
||||
WorkflowStepsMenuListComponent,
|
||||
],
|
||||
imports: [
|
||||
BrowserAnimationsModule,
|
||||
ChartsModule,
|
||||
FormlyMaterialModule,
|
||||
FormlyModule,
|
||||
FormsModule,
|
||||
HttpClientTestingModule,
|
||||
MarkdownModule,
|
||||
MatButtonModule,
|
||||
MatCardModule,
|
||||
MatFormFieldModule,
|
||||
MatIconModule,
|
||||
MatListModule,
|
||||
MatMenuModule,
|
||||
MatProgressBarModule,
|
||||
MatProgressSpinnerModule,
|
||||
MatSelectModule,
|
||||
MatSidenavModule,
|
||||
MatToolbarModule,
|
||||
NoopAnimationsModule,
|
||||
RouterTestingModule.withRoutes(routes),
|
||||
],
|
||||
providers: [
|
||||
HttpClient,
|
||||
ApiService,
|
||||
{provide: 'APP_ENVIRONMENT', useValue: mockEnvironment},
|
||||
{provide: APP_BASE_HREF, useValue: '/'},
|
||||
]
|
||||
});
|
||||
|
||||
router = TestBed.inject(Router);
|
||||
location = TestBed.inject(Location);
|
||||
fixture = TestBed.createComponent(AppComponent);
|
||||
fixture.ngZone.run(() => router.initialNavigation());
|
||||
});
|
||||
|
||||
it('navigate to "" redirects you to /', async () => {
|
||||
console.log('mockEnvironment', mockEnvironment);
|
||||
const success = await fixture.ngZone.run(() => router.navigate(['']));
|
||||
expect(success).toBeTruthy();
|
||||
expect(location.path()).toBe('/home');
|
||||
});
|
||||
});
|
|
@ -0,0 +1,28 @@
|
|||
import {NgModule} from '@angular/core';
|
||||
import {RouterModule, Routes} from '@angular/router';
|
||||
import {ThisEnvironment} from '../environments/environment.injectable';
|
||||
import {HomeComponent} from './home/home.component';
|
||||
|
||||
export const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
pathMatch: 'full',
|
||||
component: HomeComponent
|
||||
},
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
RouterModule.forRoot(routes, {
|
||||
scrollPositionRestoration: 'enabled',
|
||||
anchorScrolling: 'enabled',
|
||||
scrollOffset: [0, 84],
|
||||
})
|
||||
],
|
||||
exports: [RouterModule],
|
||||
providers: [
|
||||
{provide: 'APP_ENVIRONMENT', useClass: ThisEnvironment},
|
||||
]
|
||||
})
|
||||
export class AppRoutingModule {
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
<div class="mat-typography">
|
||||
<app-navbar (userChanged)="reload()"></app-navbar>
|
||||
<router-outlet *ngIf="!loading; else loadingMessage"></router-outlet>
|
||||
<app-footer></app-footer>
|
||||
</div>
|
||||
|
||||
|
||||
<ng-template #loadingMessage>
|
||||
<app-loading></app-loading>
|
||||
</ng-template>
|
|
@ -0,0 +1,58 @@
|
|||
import {APP_BASE_HREF} from '@angular/common';
|
||||
import {HttpClient} from '@angular/common/http';
|
||||
import {HttpClientTestingModule} from '@angular/common/http/testing';
|
||||
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
|
||||
import {MatIconModule} from '@angular/material/icon';
|
||||
import {FakeMatIconRegistry} from '@angular/material/icon/testing';
|
||||
import {MatMenuModule} from '@angular/material/menu';
|
||||
import {RouterTestingModule} from '@angular/router/testing';
|
||||
import {AppComponent} from './app.component';
|
||||
import {FooterComponent} from './footer/footer.component';
|
||||
import {NavbarComponent} from './navbar/navbar.component';
|
||||
import {ApiService} from './services/api.service';
|
||||
import {MockEnvironment} from './testing/environment.mock';
|
||||
|
||||
describe('AppComponent', () => {
|
||||
let component: AppComponent;
|
||||
let fixture: ComponentFixture<AppComponent>;
|
||||
const mockEnvironment = new MockEnvironment();
|
||||
const mockTitle = `'Once,' said the Mock Title at last, with a deep sigh, 'I was a real Title.'`;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [
|
||||
AppComponent,
|
||||
FooterComponent,
|
||||
NavbarComponent,
|
||||
],
|
||||
imports: [
|
||||
HttpClientTestingModule,
|
||||
MatIconModule,
|
||||
MatMenuModule,
|
||||
RouterTestingModule,
|
||||
],
|
||||
providers: [
|
||||
HttpClient,
|
||||
FakeMatIconRegistry,
|
||||
ApiService,
|
||||
{provide: 'APP_ENVIRONMENT', useValue: mockEnvironment},
|
||||
{provide: APP_BASE_HREF, useValue: '/'},
|
||||
]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
mockEnvironment.title = mockTitle;
|
||||
fixture = TestBed.createComponent(AppComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create the app', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
it(`should set the page title to match environment variable`, () => {
|
||||
expect((component as any).titleService.getTitle()).toEqual(mockTitle);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,27 @@
|
|||
import {Component, Inject} from '@angular/core';
|
||||
import {MatIconRegistry} from '@angular/material/icon';
|
||||
import {DomSanitizer, Title} from '@angular/platform-browser';
|
||||
import {Router} from '@angular/router';
|
||||
import {AppEnvironment} from './interfaces/appEnvironment.interface';
|
||||
import {GoogleAnalyticsService} from './services/google-analytics.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
templateUrl: './app.component.html',
|
||||
styleUrls: ['./app.component.scss']
|
||||
})
|
||||
export class AppComponent {
|
||||
loading: boolean;
|
||||
|
||||
constructor(
|
||||
@Inject('APP_ENVIRONMENT') private environment: AppEnvironment,
|
||||
private titleService: Title,
|
||||
) {
|
||||
this.titleService.setTitle(this.environment.title);
|
||||
}
|
||||
|
||||
reload() {
|
||||
this.loading = true;
|
||||
setTimeout(() => this.loading = false, 300);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
import {APP_BASE_HREF, PlatformLocation} from '@angular/common';
|
||||
import {HttpClientModule} from '@angular/common/http';
|
||||
import {NgModule} from '@angular/core';
|
||||
import {FlexLayoutModule} from '@angular/flex-layout';
|
||||
import {FormsModule, ReactiveFormsModule} from '@angular/forms';
|
||||
import {MAT_FORM_FIELD_DEFAULT_OPTIONS} from '@angular/material/form-field';
|
||||
import {MatProgressSpinnerModule} from '@angular/material/progress-spinner';
|
||||
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
|
||||
import {FormlyModule} from '@ngx-formly/core';
|
||||
import {ThisEnvironment} from '../environments/environment.injectable';
|
||||
import {AppRoutingModule} from './app-routing.module';
|
||||
import {AppComponent} from './app.component';
|
||||
import {FooterComponent} from './footer/footer.component';
|
||||
import {HomeComponent} from './home/home.component';
|
||||
import {LoadingComponent} from './loading/loading.component';
|
||||
import {NavbarComponent} from './navbar/navbar.component';
|
||||
import {ApiService} from './services/api.service';
|
||||
|
||||
/**
|
||||
* This function is used internal to get a string instance of the `<base href="" />` value from `index.html`.
|
||||
* This is an exported function, instead of a private function or inline lambda, to prevent this error:
|
||||
*
|
||||
* `Error encountered resolving symbol values statically.`
|
||||
* `Function calls are not supported.`
|
||||
* `Consider replacing the function or lambda with a reference to an exported function.`
|
||||
*
|
||||
* @param platformLocation an Angular service used to interact with a browser's URL
|
||||
* @return a string instance of the `<base href="" />` value from `index.html`
|
||||
*/
|
||||
export function getBaseHref(platformLocation: PlatformLocation): string {
|
||||
return platformLocation.getBaseHrefFromDOM();
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AppComponent,
|
||||
LoadingComponent,
|
||||
FooterComponent,
|
||||
NavbarComponent,
|
||||
HomeComponent,
|
||||
],
|
||||
imports: [
|
||||
BrowserAnimationsModule,
|
||||
FlexLayoutModule,
|
||||
FormlyModule,
|
||||
FormsModule,
|
||||
HttpClientModule,
|
||||
MatProgressSpinnerModule,
|
||||
ReactiveFormsModule,
|
||||
AppRoutingModule // <-- This line MUST be last (https://angular.io/guide/router#module-import-order-matters)
|
||||
],
|
||||
providers: [
|
||||
{provide: MAT_FORM_FIELD_DEFAULT_OPTIONS, useValue: {appearance: 'outline'}},
|
||||
ApiService,
|
||||
{provide: 'APP_ENVIRONMENT', useClass: ThisEnvironment},
|
||||
{provide: APP_BASE_HREF, useFactory: getBaseHref, deps: [PlatformLocation]},
|
||||
],
|
||||
bootstrap: [AppComponent],
|
||||
entryComponents: []
|
||||
})
|
||||
export class AppModule {
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
<p>footer works!</p>
|
|
@ -0,0 +1,25 @@
|
|||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { FooterComponent } from './footer.component';
|
||||
|
||||
describe('FooterComponent', () => {
|
||||
let component: FooterComponent;
|
||||
let fixture: ComponentFixture<FooterComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ FooterComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(FooterComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,15 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-footer',
|
||||
templateUrl: './footer.component.html',
|
||||
styleUrls: ['./footer.component.scss']
|
||||
})
|
||||
export class FooterComponent implements OnInit {
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
<p>home works!</p>
|
|
@ -0,0 +1,25 @@
|
|||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { HomeComponent } from './home.component';
|
||||
|
||||
describe('HomeComponent', () => {
|
||||
let component: HomeComponent;
|
||||
let fixture: ComponentFixture<HomeComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ HomeComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(HomeComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,15 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-home',
|
||||
templateUrl: './home.component.html',
|
||||
styleUrls: ['./home.component.scss']
|
||||
})
|
||||
export class HomeComponent implements OnInit {
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
export interface ApiError {
|
||||
status_code: number;
|
||||
code: string;
|
||||
message: string;
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
export interface AppEnvironment {
|
||||
production: boolean;
|
||||
api: string;
|
||||
title: string;
|
||||
googleAnalyticsKey: string;
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
export interface Sample {
|
||||
id: string;
|
||||
barCodeId: string;
|
||||
locationId: string;
|
||||
createdAd: string;
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
<div *ngIf="showSpinner" class="loading" fxLayoutAlign="center center">
|
||||
{{message || ''}}
|
||||
<mat-spinner [diameter]="diameter"></mat-spinner>
|
||||
</div>
|
||||
<span *ngIf="!showSpinner">{{message || '...'}}</span>
|
|
@ -0,0 +1,29 @@
|
|||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import {MatProgressSpinnerModule} from '@angular/material/progress-spinner';
|
||||
|
||||
import { LoadingComponent } from './loading.component';
|
||||
|
||||
describe('LoadingComponent', () => {
|
||||
let component: LoadingComponent;
|
||||
let fixture: ComponentFixture<LoadingComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ LoadingComponent ],
|
||||
imports: [
|
||||
MatProgressSpinnerModule
|
||||
]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(LoadingComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,34 @@
|
|||
import {Component, Input, OnInit} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-loading',
|
||||
templateUrl: './loading.component.html',
|
||||
styleUrls: ['./loading.component.scss']
|
||||
})
|
||||
export class LoadingComponent implements OnInit {
|
||||
@Input() showSpinner = true;
|
||||
@Input() message: string;
|
||||
@Input() size = 'lg';
|
||||
@Input() baseSize = 24;
|
||||
|
||||
get diameter(): number {
|
||||
switch (this.size) {
|
||||
case 'xl':
|
||||
return this.baseSize * 4;
|
||||
case 'lg':
|
||||
return this.baseSize * 3;
|
||||
case 'med':
|
||||
return this.baseSize * 2;
|
||||
case 'sm':
|
||||
return this.baseSize;
|
||||
default:
|
||||
return this.baseSize * 3;
|
||||
}
|
||||
}
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
<p>navbar works!</p>
|
|
@ -0,0 +1,25 @@
|
|||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { NavbarComponent } from './navbar.component';
|
||||
|
||||
describe('NavbarComponent', () => {
|
||||
let component: NavbarComponent;
|
||||
let fixture: ComponentFixture<NavbarComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ NavbarComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(NavbarComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,15 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-navbar',
|
||||
templateUrl: './navbar.component.html',
|
||||
styleUrls: ['./navbar.component.scss']
|
||||
})
|
||||
export class NavbarComponent implements OnInit {
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
import {APP_BASE_HREF} from '@angular/common';
|
||||
import {HttpClientTestingModule, HttpTestingController} from '@angular/common/http/testing';
|
||||
import {TestBed} from '@angular/core/testing';
|
||||
import {MatBottomSheetModule} from '@angular/material/bottom-sheet';
|
||||
import {Router} from '@angular/router';
|
||||
import {RouterTestingModule} from '@angular/router/testing';
|
||||
import {MockEnvironment} from '../testing/environment.mock';
|
||||
import {ApiService} from './api.service';
|
||||
|
||||
describe('ApiService', () => {
|
||||
let httpMock: HttpTestingController;
|
||||
let location: Location;
|
||||
let service: ApiService;
|
||||
const mockEnvironment = new MockEnvironment();
|
||||
const mockRouter = {
|
||||
createUrlTree: jasmine.createSpy('createUrlTree'),
|
||||
navigate: jasmine.createSpy('navigate')
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
HttpClientTestingModule,
|
||||
MatBottomSheetModule,
|
||||
RouterTestingModule.withRoutes([]),
|
||||
],
|
||||
providers: [
|
||||
ApiService,
|
||||
{provide: 'APP_ENVIRONMENT', useValue: mockEnvironment},
|
||||
{provide: APP_BASE_HREF, useValue: '/'},
|
||||
{provide: Router, useValue: mockRouter},
|
||||
{provide: Location, useValue: location},
|
||||
]
|
||||
});
|
||||
|
||||
httpMock = TestBed.inject(HttpTestingController);
|
||||
service = TestBed.inject(ApiService);
|
||||
location = TestBed.inject(Location);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
httpMock.verify();
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,47 @@
|
|||
import {APP_BASE_HREF} from '@angular/common';
|
||||
import {HttpClient} from '@angular/common/http';
|
||||
import {Inject, Injectable} from '@angular/core';
|
||||
import {Router} from '@angular/router';
|
||||
import {Observable, throwError} from 'rxjs';
|
||||
import {catchError} from 'rxjs/operators';
|
||||
import {ApiError} from '../interfaces/apiError.interface';
|
||||
import {AppEnvironment} from '../interfaces/appEnvironment.interface';
|
||||
import {Sample} from '../interfaces/sample.interface';
|
||||
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class ApiService {
|
||||
apiRoot: string;
|
||||
|
||||
constructor(
|
||||
@Inject('APP_ENVIRONMENT') private environment: AppEnvironment,
|
||||
@Inject(APP_BASE_HREF) public baseHref: string,
|
||||
private httpClient: HttpClient,
|
||||
private router: Router,
|
||||
private location: Location,
|
||||
) {
|
||||
this.apiRoot = environment.api;
|
||||
}
|
||||
|
||||
/** Get the string value from a given URL */
|
||||
getStringFromUrl(url: string): Observable<string> {
|
||||
return this.httpClient
|
||||
.get(url, {responseType: 'text'})
|
||||
.pipe(catchError(err => this._handleError(err)));
|
||||
}
|
||||
|
||||
/** Add new sample */
|
||||
addSample(sample: Sample): Observable<Sample> {
|
||||
const url = this.apiRoot;
|
||||
|
||||
return this.httpClient
|
||||
.put<Sample>(url, sample)
|
||||
.pipe(catchError(err => this._handleError(err)));
|
||||
}
|
||||
|
||||
private _handleError(error: ApiError): Observable<never> {
|
||||
return throwError(error.message || 'Could not complete your request; please try again later.');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
import {APP_BASE_HREF} from '@angular/common';
|
||||
import {HttpClientTestingModule} from '@angular/common/http/testing';
|
||||
import {TestBed} from '@angular/core/testing';
|
||||
import {Router} from '@angular/router';
|
||||
import {RouterTestingModule} from '@angular/router/testing';
|
||||
import {SessionRedirectComponent} from '../components/session-redirect/session-redirect.component';
|
||||
import {MockEnvironment} from '../testing/mocks/environment.mocks';
|
||||
import {GoogleAnalyticsService} from './google-analytics.service';
|
||||
|
||||
describe('GoogleAnalyticsService', () => {
|
||||
let service: GoogleAnalyticsService;
|
||||
const mockEnvironment = new MockEnvironment();
|
||||
const mockRouter = {
|
||||
createUrlTree: jasmine.createSpy('createUrlTree'),
|
||||
navigate: jasmine.createSpy('navigate'),
|
||||
events: jasmine.createSpyObj('events', ['subscribe']),
|
||||
};
|
||||
|
||||
beforeEach(() => TestBed.configureTestingModule({
|
||||
declarations: [SessionRedirectComponent],
|
||||
imports: [
|
||||
HttpClientTestingModule,
|
||||
RouterTestingModule.withRoutes([
|
||||
{
|
||||
path: 'session/:token',
|
||||
component: SessionRedirectComponent
|
||||
}
|
||||
])
|
||||
],
|
||||
providers: [
|
||||
GoogleAnalyticsService,
|
||||
{provide: 'APP_ENVIRONMENT', useValue: mockEnvironment},
|
||||
{provide: APP_BASE_HREF, useValue: '/'},
|
||||
{provide: Router, useValue: mockRouter},
|
||||
{provide: Location, useValue: location},
|
||||
],
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
service = TestBed.inject(GoogleAnalyticsService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
expect(service.analyticsKey).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should set a new analytics key', () => {
|
||||
service.init('new_key');
|
||||
expect(service.analyticsKey).toEqual('new_key');
|
||||
});
|
||||
});
|
|
@ -0,0 +1,82 @@
|
|||
import {APP_BASE_HREF} from '@angular/common';
|
||||
import {HttpRequest} from '@angular/common/http';
|
||||
import {Inject, Injectable} from '@angular/core';
|
||||
import {NavigationEnd, Router} from '@angular/router';
|
||||
import {ApiError} from '../interfaces/apiError.interface';
|
||||
import {AppEnvironment} from '../interfaces/appEnvironment.interface';
|
||||
|
||||
declare var gtag;
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class GoogleAnalyticsService {
|
||||
analyticsKey: string;
|
||||
|
||||
constructor(
|
||||
@Inject('APP_ENVIRONMENT') private environment: AppEnvironment,
|
||||
@Inject(APP_BASE_HREF) public baseHref: string,
|
||||
private router: Router,
|
||||
) {
|
||||
this.analyticsKey = this.environment.googleAnalyticsKey;
|
||||
}
|
||||
|
||||
public authEvent(req: HttpRequest<any>) {
|
||||
this.event('login', 'authentication', req.url)
|
||||
}
|
||||
|
||||
public errorEvent(error: ApiError) {
|
||||
this.event(error.code, 'error_messages', error.message);
|
||||
}
|
||||
|
||||
public setUser(uid) {
|
||||
if (gtag) {
|
||||
gtag('set', {user_id: uid}); // Set the user ID using signed-in user_id.
|
||||
this.event('user-id available', 'authentication', uid)
|
||||
}
|
||||
}
|
||||
|
||||
public init(analyticsKey) {
|
||||
this.analyticsKey = analyticsKey || this.analyticsKey;
|
||||
this.listenForRouteChanges();
|
||||
|
||||
try {
|
||||
const script1 = document.createElement('script');
|
||||
script1.async = true;
|
||||
script1.src = 'https://www.googletagmanager.com/gtag/js?id=' + this.analyticsKey;
|
||||
document.head.appendChild(script1);
|
||||
|
||||
const script2 = document.createElement('script');
|
||||
script2.innerHTML = `
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
function gtag(){dataLayer.push(arguments);}
|
||||
gtag('js', new Date());
|
||||
gtag('config', '` + this.analyticsKey + `', {'send_page_view': false});
|
||||
`;
|
||||
document.head.appendChild(script2);
|
||||
} catch (ex) {
|
||||
console.error('Error appending google analytics');
|
||||
console.error(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private event(action: string, category: string, label: string) {
|
||||
if (gtag) {
|
||||
gtag('event', action, {
|
||||
event_category: category,
|
||||
event_label: label
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private listenForRouteChanges() {
|
||||
const analyticsKey = this.environment.googleAnalyticsKey;
|
||||
this.router.events.subscribe(event => {
|
||||
if (gtag && event instanceof NavigationEnd) {
|
||||
gtag('config', analyticsKey, {
|
||||
page_path: event.urlAfterRedirects,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
import {Injectable} from '@angular/core';
|
||||
import {AppEnvironment} from '../interfaces/appEnvironment.interface';
|
||||
|
||||
@Injectable()
|
||||
export class MockEnvironment implements AppEnvironment {
|
||||
production = false;
|
||||
api = 'apiRoot';
|
||||
title = 'Mock Title';
|
||||
googleAnalyticsKey = '';
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M13,9H18.5L13,3.5V9M6,2H14L20,8V20A2,2 0 0,1 18,22H6C4.89,22 4,21.1 4,20V4C4,2.89 4.89,2 6,2M6,20H15L18,20V12L14,16L12,14L6,20M8,9A2,2 0 0,0 6,11A2,2 0 0,0 8,13A2,2 0 0,0 10,11A2,2 0 0,0 8,9Z" /></svg>
|
After Width: | Height: | Size: 486 B |
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M13,9H18.5L13,3.5V9M6,2H14L20,8V20A2,2 0 0,1 18,22H6C4.89,22 4,21.1 4,20V4C4,2.89 4.89,2 6,2M9,16A2,2 0 0,0 7,18A2,2 0 0,0 9,20A2,2 0 0,0 11,18V13H14V11H10V16.27C9.71,16.1 9.36,16 9,16Z" /></svg>
|
After Width: | Height: | Size: 480 B |
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M13,9H18.5L13,3.5V9M6,2H14L20,8V20A2,2 0 0,1 18,22H6C4.89,22 4,21.1 4,20V4C4,2.89 4.89,2 6,2M17,19V13L14,15.2V13H7V19H14V16.8L17,19Z" /></svg>
|
After Width: | Height: | Size: 427 B |
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M13,9H18.5L13,3.5V9M6,2H14L20,8V20A2,2 0 0,1 18,22H6C4.89,22 4,21.1 4,20V4C4,2.89 4.89,2 6,2M6.12,15.5L9.86,19.24L11.28,17.83L8.95,15.5L11.28,13.17L9.86,11.76L6.12,15.5M17.28,15.5L13.54,11.76L12.12,13.17L14.45,15.5L12.12,17.83L13.54,19.24L17.28,15.5Z" /></svg>
|
After Width: | Height: | Size: 545 B |
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M6,2H14L20,8V20A2,2 0 0,1 18,22H6A2,2 0 0,1 4,20V4A2,2 0 0,1 6,2M13,3.5V9H18.5L13,3.5M17,11H13V13H14L12,14.67L10,13H11V11H7V13H8L11,15.5L8,18H7V20H11V18H10L12,16.33L14,18H13V20H17V18H16L13,15.5L16,13H17V11Z" /></svg>
|
After Width: | Height: | Size: 501 B |
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M6,2H18A2,2 0 0,1 20,4V20A2,2 0 0,1 18,22H6A2,2 0 0,1 4,20V4A2,2 0 0,1 6,2M12,4A6,6 0 0,0 6,10C6,13.31 8.69,16 12.1,16L11.22,13.77C10.95,13.29 11.11,12.68 11.59,12.4L12.45,11.9C12.93,11.63 13.54,11.79 13.82,12.27L15.74,14.69C17.12,13.59 18,11.9 18,10A6,6 0 0,0 12,4M12,9A1,1 0 0,1 13,10A1,1 0 0,1 12,11A1,1 0 0,1 11,10A1,1 0 0,1 12,9M7,18A1,1 0 0,0 6,19A1,1 0 0,0 7,20A1,1 0 0,0 8,19A1,1 0 0,0 7,18M12.09,13.27L14.58,19.58L17.17,18.08L12.95,12.77L12.09,13.27Z" /></svg>
|
After Width: | Height: | Size: 754 B |
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M13,9H18.5L13,3.5V9M6,2H14L20,8V20A2,2 0 0,1 18,22H6C4.89,22 4,21.1 4,20V4C4,2.89 4.89,2 6,2M6.12,15.5L9.86,19.24L11.28,17.83L8.95,15.5L11.28,13.17L9.86,11.76L6.12,15.5M17.28,15.5L13.54,11.76L12.12,13.17L14.45,15.5L12.12,17.83L13.54,19.24L17.28,15.5Z" /></svg>
|
After Width: | Height: | Size: 545 B |
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M6,2H14L20,8V20A2,2 0 0,1 18,22H6A2,2 0 0,1 4,20V4A2,2 0 0,1 6,2M13,3.5V9H18.5L13,3.5M7,13L8.5,20H10.5L12,17L13.5,20H15.5L17,13H18V11H14V13H15L14.1,17.2L13,15V15H11V15L9.9,17.2L9,13H10V11H6V13H7Z" /></svg>
|
After Width: | Height: | Size: 490 B |
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M6,2H14L20,8V20A2,2 0 0,1 18,22H6A2,2 0 0,1 4,20V4A2,2 0 0,1 6,2M13,3.5V9H18.5L13,3.5M7,13L8.5,20H10.5L12,17L13.5,20H15.5L17,13H18V11H14V13H15L14.1,17.2L13,15V15H11V15L9.9,17.2L9,13H10V11H6V13H7Z" /></svg>
|
After Width: | Height: | Size: 490 B |
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M13,9H18.5L13,3.5V9M6,2H14L20,8V20A2,2 0 0,1 18,22H6C4.89,22 4,21.1 4,20V4C4,2.89 4.89,2 6,2M6,20H15L18,20V12L14,16L12,14L6,20M8,9A2,2 0 0,0 6,11A2,2 0 0,0 8,13A2,2 0 0,0 10,11A2,2 0 0,0 8,9Z" /></svg>
|
After Width: | Height: | Size: 486 B |
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M13,9H18.5L13,3.5V9M6,2H14L20,8V20A2,2 0 0,1 18,22H6C4.89,22 4,21.1 4,20V4C4,2.89 4.89,2 6,2M6.12,15.5L9.86,19.24L11.28,17.83L8.95,15.5L11.28,13.17L9.86,11.76L6.12,15.5M17.28,15.5L13.54,11.76L12.12,13.17L14.45,15.5L12.12,17.83L13.54,19.24L17.28,15.5Z" /></svg>
|
After Width: | Height: | Size: 545 B |
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M6,2C4.89,2 4,2.89 4,4V20A2,2 0 0,0 6,22H18A2,2 0 0,0 20,20V8L14,2H6M13,3.5L18.5,9H13V3.5M12,11A3,3 0 0,1 15,14C15,15.88 12.75,16.06 12.75,17.75H11.25C11.25,15.31 13.5,15.5 13.5,14A1.5,1.5 0 0,0 12,12.5A1.5,1.5 0 0,0 10.5,14H9A3,3 0 0,1 12,11M11.25,18.5H12.75V20H11.25V18.5Z" /></svg>
|
After Width: | Height: | Size: 569 B |
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M6,2H18A2,2 0 0,1 20,4V20A2,2 0 0,1 18,22H6A2,2 0 0,1 4,20V4A2,2 0 0,1 6,2M12,4A6,6 0 0,0 6,10C6,13.31 8.69,16 12.1,16L11.22,13.77C10.95,13.29 11.11,12.68 11.59,12.4L12.45,11.9C12.93,11.63 13.54,11.79 13.82,12.27L15.74,14.69C17.12,13.59 18,11.9 18,10A6,6 0 0,0 12,4M12,9A1,1 0 0,1 13,10A1,1 0 0,1 12,11A1,1 0 0,1 11,10A1,1 0 0,1 12,9M7,18A1,1 0 0,0 6,19A1,1 0 0,0 7,20A1,1 0 0,0 8,19A1,1 0 0,0 7,18M12.09,13.27L14.58,19.58L17.17,18.08L12.95,12.77L12.09,13.27Z" /></svg>
|
After Width: | Height: | Size: 754 B |
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M13,9H18.5L13,3.5V9M6,2H14L20,8V20A2,2 0 0,1 18,22H6C4.89,22 4,21.1 4,20V4C4,2.89 4.89,2 6,2M6.12,15.5L9.86,19.24L11.28,17.83L8.95,15.5L11.28,13.17L9.86,11.76L6.12,15.5M17.28,15.5L13.54,11.76L12.12,13.17L14.45,15.5L12.12,17.83L13.54,19.24L17.28,15.5Z" /></svg>
|
After Width: | Height: | Size: 545 B |
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M13,9H18.5L13,3.5V9M6,2H14L20,8V20A2,2 0 0,1 18,22H6C4.89,22 4,21.1 4,20V4C4,2.89 4.89,2 6,2M6,20H15L18,20V12L14,16L12,14L6,20M8,9A2,2 0 0,0 6,11A2,2 0 0,0 8,13A2,2 0 0,0 10,11A2,2 0 0,0 8,9Z" /></svg>
|
After Width: | Height: | Size: 486 B |
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M13,9H18.5L13,3.5V9M6,2H14L20,8V20A2,2 0 0,1 18,22H6C4.89,22 4,21.1 4,20V4C4,2.89 4.89,2 6,2M6,20H15L18,20V12L14,16L12,14L6,20M8,9A2,2 0 0,0 6,11A2,2 0 0,0 8,13A2,2 0 0,0 10,11A2,2 0 0,0 8,9Z" /></svg>
|
After Width: | Height: | Size: 486 B |
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M13,9H18.5L13,3.5V9M6,2H14L20,8V20A2,2 0 0,1 18,22H6C4.89,22 4,21.1 4,20V4C4,2.89 4.89,2 6,2M6.12,15.5L9.86,19.24L11.28,17.83L8.95,15.5L11.28,13.17L9.86,11.76L6.12,15.5M17.28,15.5L13.54,11.76L12.12,13.17L14.45,15.5L12.12,17.83L13.54,19.24L17.28,15.5Z" /></svg>
|
After Width: | Height: | Size: 545 B |
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M6,2H14L20,8V20A2,2 0 0,1 18,22H6A2,2 0 0,1 4,20V4A2,2 0 0,1 6,2M13,3.5V9H18.5L13,3.5M7,13L8.5,20H10.5L12,17L13.5,20H15.5L17,13H18V11H14V13H15L14.1,17.2L13,15V15H11V15L9.9,17.2L9,13H10V11H6V13H7Z" /></svg>
|
After Width: | Height: | Size: 490 B |
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M13,9H18.5L13,3.5V9M6,2H14L20,8V20A2,2 0 0,1 18,22H6C4.89,22 4,21.1 4,20V4C4,2.89 4.89,2 6,2M17,19V13L14,15.2V13H7V19H14V16.8L17,19Z" /></svg>
|
After Width: | Height: | Size: 427 B |
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M13,9H18.5L13,3.5V9M6,2H14L20,8V20A2,2 0 0,1 18,22H6C4.89,22 4,21.1 4,20V4C4,2.89 4.89,2 6,2M9,16A2,2 0 0,0 7,18A2,2 0 0,0 9,20A2,2 0 0,0 11,18V13H14V11H10V16.27C9.71,16.1 9.36,16 9,16Z" /></svg>
|
After Width: | Height: | Size: 480 B |
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M13,9H18.5L13,3.5V9M6,2H14L20,8V20A2,2 0 0,1 18,22H6C4.89,22 4,21.1 4,20V4C4,2.89 4.89,2 6,2M17,19V13L14,15.2V13H7V19H14V16.8L17,19Z" /></svg>
|
After Width: | Height: | Size: 427 B |
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M13,9H18.5L13,3.5V9M6,2H14L20,8V20A2,2 0 0,1 18,22H6C4.89,22 4,21.1 4,20V4C4,2.89 4.89,2 6,2M17,19V13L14,15.2V13H7V19H14V16.8L17,19Z" /></svg>
|
After Width: | Height: | Size: 427 B |
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M6,2H18A2,2 0 0,1 20,4V20A2,2 0 0,1 18,22H6A2,2 0 0,1 4,20V4A2,2 0 0,1 6,2M12,4A6,6 0 0,0 6,10C6,13.31 8.69,16 12.1,16L11.22,13.77C10.95,13.29 11.11,12.68 11.59,12.4L12.45,11.9C12.93,11.63 13.54,11.79 13.82,12.27L15.74,14.69C17.12,13.59 18,11.9 18,10A6,6 0 0,0 12,4M12,9A1,1 0 0,1 13,10A1,1 0 0,1 12,11A1,1 0 0,1 11,10A1,1 0 0,1 12,9M7,18A1,1 0 0,0 6,19A1,1 0 0,0 7,20A1,1 0 0,0 8,19A1,1 0 0,0 7,18M12.09,13.27L14.58,19.58L17.17,18.08L12.95,12.77L12.09,13.27Z" /></svg>
|
After Width: | Height: | Size: 754 B |
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M13,9H18.5L13,3.5V9M6,2H14L20,8V20A2,2 0 0,1 18,22H6C4.89,22 4,21.1 4,20V4C4,2.89 4.89,2 6,2M9,16A2,2 0 0,0 7,18A2,2 0 0,0 9,20A2,2 0 0,0 11,18V13H14V11H10V16.27C9.71,16.1 9.36,16 9,16Z" /></svg>
|
After Width: | Height: | Size: 480 B |
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M14,9H19.5L14,3.5V9M7,2H15L21,8V20A2,2 0 0,1 19,22H7C5.89,22 5,21.1 5,20V4A2,2 0 0,1 7,2M11.93,12.44C12.34,13.34 12.86,14.08 13.46,14.59L13.87,14.91C13,15.07 11.8,15.35 10.53,15.84V15.84L10.42,15.88L10.92,14.84C11.37,13.97 11.7,13.18 11.93,12.44M18.41,16.25C18.59,16.07 18.68,15.84 18.69,15.59C18.72,15.39 18.67,15.2 18.57,15.04C18.28,14.57 17.53,14.35 16.29,14.35L15,14.42L14.13,13.84C13.5,13.32 12.93,12.41 12.53,11.28L12.57,11.14C12.9,9.81 13.21,8.2 12.55,7.54C12.39,7.38 12.17,7.3 11.94,7.3H11.7C11.33,7.3 11,7.69 10.91,8.07C10.54,9.4 10.76,10.13 11.13,11.34V11.35C10.88,12.23 10.56,13.25 10.05,14.28L9.09,16.08L8.2,16.57C7,17.32 6.43,18.16 6.32,18.69C6.28,18.88 6.3,19.05 6.37,19.23L6.4,19.28L6.88,19.59L7.32,19.7C8.13,19.7 9.05,18.75 10.29,16.63L10.47,16.56C11.5,16.23 12.78,16 14.5,15.81C15.53,16.32 16.74,16.55 17.5,16.55C17.94,16.55 18.24,16.44 18.41,16.25M18,15.54L18.09,15.65C18.08,15.75 18.05,15.76 18,15.78H17.96L17.77,15.8C17.31,15.8 16.6,15.61 15.87,15.29C15.96,15.19 16,15.19 16.1,15.19C17.5,15.19 17.9,15.44 18,15.54M8.83,17C8.18,18.19 7.59,18.85 7.14,19C7.19,18.62 7.64,17.96 8.35,17.31L8.83,17M11.85,10.09C11.62,9.19 11.61,8.46 11.78,8.04L11.85,7.92L12,7.97C12.17,8.21 12.19,8.53 12.09,9.07L12.06,9.23L11.9,10.05L11.85,10.09Z" /></svg>
|
After Width: | Height: | Size: 1.5 KiB |
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M13,9H18.5L13,3.5V9M6,2H14L20,8V20A2,2 0 0,1 18,22H6C4.89,22 4,21.1 4,20V4C4,2.89 4.89,2 6,2M6.12,15.5L9.86,19.24L11.28,17.83L8.95,15.5L11.28,13.17L9.86,11.76L6.12,15.5M17.28,15.5L13.54,11.76L12.12,13.17L14.45,15.5L12.12,17.83L13.54,19.24L17.28,15.5Z" /></svg>
|
After Width: | Height: | Size: 545 B |
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M6,2H18A2,2 0 0,1 20,4V20A2,2 0 0,1 18,22H6A2,2 0 0,1 4,20V4A2,2 0 0,1 6,2M12,4A6,6 0 0,0 6,10C6,13.31 8.69,16 12.1,16L11.22,13.77C10.95,13.29 11.11,12.68 11.59,12.4L12.45,11.9C12.93,11.63 13.54,11.79 13.82,12.27L15.74,14.69C17.12,13.59 18,11.9 18,10A6,6 0 0,0 12,4M12,9A1,1 0 0,1 13,10A1,1 0 0,1 12,11A1,1 0 0,1 11,10A1,1 0 0,1 12,9M7,18A1,1 0 0,0 6,19A1,1 0 0,0 7,20A1,1 0 0,0 8,19A1,1 0 0,0 7,18M12.09,13.27L14.58,19.58L17.17,18.08L12.95,12.77L12.09,13.27Z" /></svg>
|
After Width: | Height: | Size: 754 B |
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M6,2H14L20,8V20A2,2 0 0,1 18,22H6A2,2 0 0,1 4,20V4A2,2 0 0,1 6,2M13,3.5V9H18.5L13,3.5M7,13L8.5,20H10.5L12,17L13.5,20H15.5L17,13H18V11H14V13H15L14.1,17.2L13,15V15H11V15L9.9,17.2L9,13H10V11H6V13H7Z" /></svg>
|
After Width: | Height: | Size: 490 B |
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M13,9H18.5L13,3.5V9M6,2H14L20,8V20A2,2 0 0,1 18,22H6C4.89,22 4,21.1 4,20V4C4,2.89 4.89,2 6,2M6.12,15.5L9.86,19.24L11.28,17.83L8.95,15.5L11.28,13.17L9.86,11.76L6.12,15.5M17.28,15.5L13.54,11.76L12.12,13.17L14.45,15.5L12.12,17.83L13.54,19.24L17.28,15.5Z" /></svg>
|
After Width: | Height: | Size: 545 B |
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M13,9H18.5L13,3.5V9M6,2H14L20,8V20A2,2 0 0,1 18,22H6C4.89,22 4,21.1 4,20V4C4,2.89 4.89,2 6,2M6,20H15L18,20V12L14,16L12,14L6,20M8,9A2,2 0 0,0 6,11A2,2 0 0,0 8,13A2,2 0 0,0 10,11A2,2 0 0,0 8,9Z" /></svg>
|
After Width: | Height: | Size: 486 B |
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M13,9H18.5L13,3.5V9M6,2H14L20,8V20A2,2 0 0,1 18,22H6C4.89,22 4,21.1 4,20V4C4,2.89 4.89,2 6,2M6,20H15L18,20V12L14,16L12,14L6,20M8,9A2,2 0 0,0 6,11A2,2 0 0,0 8,13A2,2 0 0,0 10,11A2,2 0 0,0 8,9Z" /></svg>
|
After Width: | Height: | Size: 486 B |
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M6,2H14L20,8V20A2,2 0 0,1 18,22H6A2,2 0 0,1 4,20V4A2,2 0 0,1 6,2M13,3.5V9H18.5L13,3.5M8,11V13H9V19H8V20H12V19H11V17H13A3,3 0 0,0 16,14A3,3 0 0,0 13,11H8M13,13A1,1 0 0,1 14,14A1,1 0 0,1 13,15H11V13H13Z" /></svg>
|
After Width: | Height: | Size: 495 B |
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M6,2H14L20,8V20A2,2 0 0,1 18,22H6A2,2 0 0,1 4,20V4A2,2 0 0,1 6,2M13,3.5V9H18.5L13,3.5M8,11V13H9V19H8V20H12V19H11V17H13A3,3 0 0,0 16,14A3,3 0 0,0 13,11H8M13,13A1,1 0 0,1 14,14A1,1 0 0,1 13,15H11V13H13Z" /></svg>
|
After Width: | Height: | Size: 495 B |
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M13,9H18.5L13,3.5V9M6,2H14L20,8V20A2,2 0 0,1 18,22H6C4.89,22 4,21.1 4,20V4C4,2.89 4.89,2 6,2M6,20H15L18,20V12L14,16L12,14L6,20M8,9A2,2 0 0,0 6,11A2,2 0 0,0 8,13A2,2 0 0,0 10,11A2,2 0 0,0 8,9Z" /></svg>
|
After Width: | Height: | Size: 486 B |
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M13,9H18.5L13,3.5V9M6,2H14L20,8V20A2,2 0 0,1 18,22H6C4.89,22 4,21.1 4,20V4C4,2.89 4.89,2 6,2M6.12,15.5L9.86,19.24L11.28,17.83L8.95,15.5L11.28,13.17L9.86,11.76L6.12,15.5M17.28,15.5L13.54,11.76L12.12,13.17L14.45,15.5L12.12,17.83L13.54,19.24L17.28,15.5Z" /></svg>
|
After Width: | Height: | Size: 545 B |
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M6,2H14L20,8V20A2,2 0 0,1 18,22H6A2,2 0 0,1 4,20V4A2,2 0 0,1 6,2M13,3.5V9H18.5L13,3.5M7,13L8.5,20H10.5L12,17L13.5,20H15.5L17,13H18V11H14V13H15L14.1,17.2L13,15V15H11V15L9.9,17.2L9,13H10V11H6V13H7Z" /></svg>
|
After Width: | Height: | Size: 490 B |
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M13,9H18.5L13,3.5V9M6,2H14L20,8V20A2,2 0 0,1 18,22H6C4.89,22 4,21.1 4,20V4C4,2.89 4.89,2 6,2M6.12,15.5L9.86,19.24L11.28,17.83L8.95,15.5L11.28,13.17L9.86,11.76L6.12,15.5M17.28,15.5L13.54,11.76L12.12,13.17L14.45,15.5L12.12,17.83L13.54,19.24L17.28,15.5Z" /></svg>
|
After Width: | Height: | Size: 545 B |
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M13,9H18.5L13,3.5V9M6,2H14L20,8V20A2,2 0 0,1 18,22H6C4.89,22 4,21.1 4,20V4C4,2.89 4.89,2 6,2M6,20H15L18,20V12L14,16L12,14L6,20M8,9A2,2 0 0,0 6,11A2,2 0 0,0 8,13A2,2 0 0,0 10,11A2,2 0 0,0 8,9Z" /></svg>
|
After Width: | Height: | Size: 486 B |
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M13,9H18.5L13,3.5V9M6,2H14L20,8V20A2,2 0 0,1 18,22H6C4.89,22 4,21.1 4,20V4C4,2.89 4.89,2 6,2M6,20H15L18,20V12L14,16L12,14L6,20M8,9A2,2 0 0,0 6,11A2,2 0 0,0 8,13A2,2 0 0,0 10,11A2,2 0 0,0 8,9Z" /></svg>
|
After Width: | Height: | Size: 486 B |
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M6,2H18A2,2 0 0,1 20,4V20A2,2 0 0,1 18,22H6A2,2 0 0,1 4,20V4A2,2 0 0,1 6,2M12,4A6,6 0 0,0 6,10C6,13.31 8.69,16 12.1,16L11.22,13.77C10.95,13.29 11.11,12.68 11.59,12.4L12.45,11.9C12.93,11.63 13.54,11.79 13.82,12.27L15.74,14.69C17.12,13.59 18,11.9 18,10A6,6 0 0,0 12,4M12,9A1,1 0 0,1 13,10A1,1 0 0,1 12,11A1,1 0 0,1 11,10A1,1 0 0,1 12,9M7,18A1,1 0 0,0 6,19A1,1 0 0,0 7,20A1,1 0 0,0 8,19A1,1 0 0,0 7,18M12.09,13.27L14.58,19.58L17.17,18.08L12.95,12.77L12.09,13.27Z" /></svg>
|
After Width: | Height: | Size: 754 B |
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M13,9H18.5L13,3.5V9M6,2H14L20,8V20A2,2 0 0,1 18,22H6C4.89,22 4,21.1 4,20V4C4,2.89 4.89,2 6,2M6,20H15L18,20V12L14,16L12,14L6,20M8,9A2,2 0 0,0 6,11A2,2 0 0,0 8,13A2,2 0 0,0 10,11A2,2 0 0,0 8,9Z" /></svg>
|
After Width: | Height: | Size: 486 B |
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M6,2H14L20,8V20A2,2 0 0,1 18,22H6A2,2 0 0,1 4,20V4A2,2 0 0,1 6,2M13,3.5V9H18.5L13,3.5M7,13L8.5,20H10.5L12,17L13.5,20H15.5L17,13H18V11H14V13H15L14.1,17.2L13,15V15H11V15L9.9,17.2L9,13H10V11H6V13H7Z" /></svg>
|
After Width: | Height: | Size: 490 B |
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M6,2C4.89,2 4,2.89 4,4V20A2,2 0 0,0 6,22H18A2,2 0 0,0 20,20V8L14,2H6M13,3.5L18.5,9H13V3.5M12,11A3,3 0 0,1 15,14C15,15.88 12.75,16.06 12.75,17.75H11.25C11.25,15.31 13.5,15.5 13.5,14A1.5,1.5 0 0,0 12,12.5A1.5,1.5 0 0,0 10.5,14H9A3,3 0 0,1 12,11M11.25,18.5H12.75V20H11.25V18.5Z" /></svg>
|
After Width: | Height: | Size: 569 B |
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M13,9H18.5L13,3.5V9M6,2H14L20,8V20A2,2 0 0,1 18,22H6C4.89,22 4,21.1 4,20V4C4,2.89 4.89,2 6,2M9,16A2,2 0 0,0 7,18A2,2 0 0,0 9,20A2,2 0 0,0 11,18V13H14V11H10V16.27C9.71,16.1 9.36,16 9,16Z" /></svg>
|
After Width: | Height: | Size: 480 B |