This commit is contained in:
Zakary Timson 2022-05-02 21:10:15 -04:00
parent 200574099b
commit 8e818b179b
60 changed files with 617 additions and 879 deletions

View File

@ -1,67 +0,0 @@
version: 2
jobs:
build:
docker:
- image: circleci/node:10.4-browsers
working_directory: ~/repo
steps:
- checkout
- restore_cache:
keys:
- v1-dependencies-{{ checksum "package.json" }}
- v1-dependencies-
- run:
name: Install Dependancies
command: yarn
- save_cache:
key: v1-dependencies-{{ checksum "package.json" }}
paths:
- node_modules
- run:
name: Build
command: yarn build
- persist_to_workspace:
root: ./
paths:
- node_modules
- dist/ZaksCode
- store_artifacts:
path: ./dist/ZaksCode
deploy:
docker:
- image: circleci/node:10.4-browsers
working_directory: ~/repo
steps:
- checkout
- attach_workspace:
at: ./
- run:
name: Deploy
command: yarn firebase deploy --token "${FIREBASE_TOKEN}"
workflows:
version: 2
build_and_deply:
jobs:
- build
- deploy:
requires:
- build
filters:
branches:
only: master

View File

@ -3,7 +3,7 @@ root = true
[*] [*]
charset = utf-8 charset = utf-8
indent_style = space indent_style = tab
indent_size = 4 indent_size = 4
insert_final_newline = true insert_final_newline = true
trim_trailing_whitespace = true trim_trailing_whitespace = true
@ -11,3 +11,7 @@ trim_trailing_whitespace = true
[*.md] [*.md]
max_line_length = off max_line_length = off
trim_trailing_whitespace = false trim_trailing_whitespace = false
[*.yml]
indent_style = space
indent_size = 2

View File

@ -1,5 +0,0 @@
{
"projects": {
"default": "zaks-code"
}
}

1
.gitignore vendored
View File

@ -44,3 +44,4 @@ testem.log
# System Files # System Files
.DS_Store .DS_Store
Thumbs.db Thumbs.db
/package-lock.json

27
Dockerfile Normal file
View File

@ -0,0 +1,27 @@
FROM node:16 as build
# Variables
ARG NODE_ENV=prod
ARG NODE_OPTIONS=""
ENV NG_CLI_ANALYTICS=ci \
NODE_ENV=${NODE_ENV} \
NODE_OPTIONS=${NODE_OPTIONS}
# Setup
RUN npm config set unsafe-perm true && \
mkdir /app
WORKDIR /app
COPY . .
# Install
RUN if [ ! -d "dist" ] && [ ! -d "node_modules" ]; then npm install; fi
# Build
if [ ! -d "dist" ]; then npm run "build:$NODE_ENV"; fi
# Use Nginx to serve
FROM nginx:1.20-alpine
COPY --from=build /app/dist /usr/share/nginx/html
COPY docker/robots.txt /usr/share/nginx/html/robots.txt
COPY docker/nginx.conf /etc/nginx/nginx.conf
EXPOSE 80

View File

@ -1,3 +1,27 @@
# ZaksCode # Zakscode
WIP: An Angular + Firebase replacement for my old personal site writen in Django. This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 13.3.4.
## Development server
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application will automatically reload if you change any of the source files.
## 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.
## 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 a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities.
## Further help
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.

View File

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/dist" />
<excludeFolder url="file://$MODULE_DIR$/tmp" />
</content>
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View File

@ -3,73 +3,87 @@
"version": 1, "version": 1,
"newProjectRoot": "projects", "newProjectRoot": "projects",
"projects": { "projects": {
"ZaksCode": { "zakscode": {
"projectType": "application",
"schematics": {
"@schematics/angular:component": {
"style": "scss"
},
"@schematics/angular:application": {
"strict": true
}
},
"root": "", "root": "",
"sourceRoot": "src", "sourceRoot": "src",
"projectType": "application",
"prefix": "app", "prefix": "app",
"schematics": {},
"architect": { "architect": {
"build": { "build": {
"builder": "@angular-devkit/build-angular:browser", "builder": "@angular-devkit/build-angular:browser",
"options": { "options": {
"outputPath": "dist/ZaksCode", "outputPath": "dist/zakscode",
"index": "src/index.html", "index": "src/index.html",
"main": "src/main.ts", "main": "src/main.ts",
"polyfills": "src/polyfills.ts", "polyfills": "src/polyfills.ts",
"tsConfig": "src/tsconfig.app.json", "tsConfig": "tsconfig.app.json",
"inlineStyleLanguage": "scss",
"assets": [ "assets": [
"src/favicon.ico",
"src/assets" "src/assets"
], ],
"styles": [ "styles": [
"src/custom-theme.scss",
"src/styles.scss" "src/styles.scss"
], ],
"scripts": [], "scripts": []
"es5BrowserSupport": true
}, },
"configurations": { "configurations": {
"production": { "production": {
"budgets": [
{
"type": "initial",
"maximumWarning": "500kb",
"maximumError": "1mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "2kb",
"maximumError": "4kb"
}
],
"fileReplacements": [ "fileReplacements": [
{ {
"replace": "src/environments/environment.ts", "replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts" "with": "src/environments/environment.prod.ts"
} }
], ],
"optimization": true, "outputHashing": "all"
"outputHashing": "all", },
"sourceMap": false, "development": {
"extractCss": true, "buildOptimizer": false,
"namedChunks": false, "optimization": false,
"aot": true, "vendorChunk": true,
"extractLicenses": true, "extractLicenses": false,
"vendorChunk": false, "sourceMap": true,
"buildOptimizer": true, "namedChunks": true
"budgets": [
{
"type": "initial",
"maximumWarning": "2mb",
"maximumError": "5mb"
}
]
} }
} },
"defaultConfiguration": "production"
}, },
"serve": { "serve": {
"builder": "@angular-devkit/build-angular:dev-server", "builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "ZaksCode:build"
},
"configurations": { "configurations": {
"production": { "production": {
"browserTarget": "ZaksCode:build:production" "browserTarget": "zakscode:build:production"
},
"development": {
"browserTarget": "zakscode:build:development"
} }
} },
"defaultConfiguration": "development"
}, },
"extract-i18n": { "extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n", "builder": "@angular-devkit/build-angular:extract-i18n",
"options": { "options": {
"browserTarget": "ZaksCode:build" "browserTarget": "zakscode:build"
} }
}, },
"test": { "test": {
@ -77,60 +91,21 @@
"options": { "options": {
"main": "src/test.ts", "main": "src/test.ts",
"polyfills": "src/polyfills.ts", "polyfills": "src/polyfills.ts",
"tsConfig": "src/tsconfig.spec.json", "tsConfig": "tsconfig.spec.json",
"karmaConfig": "src/karma.conf.js", "karmaConfig": "karma.conf.js",
"inlineStyleLanguage": "scss",
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [ "styles": [
"src/custom-theme.scss",
"src/styles.scss" "src/styles.scss"
], ],
"scripts": [], "scripts": []
"assets": [
"src/assets"
]
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"src/tsconfig.app.json",
"src/tsconfig.spec.json"
],
"exclude": [
"**/node_modules/**"
]
}
}
}
},
"ZaksCode-e2e": {
"root": "e2e/",
"projectType": "application",
"prefix": "",
"architect": {
"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "e2e/protractor.conf.js",
"devServerTarget": "ZaksCode:serve"
},
"configurations": {
"production": {
"devServerTarget": "ZaksCode:serve:production"
}
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": "e2e/tsconfig.e2e.json",
"exclude": [
"**/node_modules/**"
]
} }
} }
} }
} }
}, },
"defaultProject": "ZaksCode" "defaultProject": "zakscode"
} }

31
docker/nginx.conf Normal file
View File

@ -0,0 +1,31 @@
worker_processes auto;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
gzip on;
gzip_proxied any;
gzip_types text/plain text/css application/xml application/xhtml+xml application/rss+xml application/javascript application/x-javascript application/json application/x-font-woff;
gzip_vary on;
gzip_disable "MSIE [1-6]\.(?!.*SV1)";
sendfile off;
keepalive_timeout 65;
server {
listen 80;
index index.html;
root /usr/share/nginx/html;
autoindex off;
location / {
try_files $uri$args $uri$args/ /index.html;
}
}
}

2
docker/robots.txt Normal file
View File

@ -0,0 +1,2 @@
User-Agent: *
Allow: /

View File

@ -1,28 +0,0 @@
// Protractor configuration file, see link for more information
// https://github.com/angular/protractor/blob/master/lib/config.ts
const { SpecReporter } = require('jasmine-spec-reporter');
exports.config = {
allScriptsTimeout: 11000,
specs: [
'./src/**/*.e2e-spec.ts'
],
capabilities: {
'browserName': 'chrome'
},
directConnect: true,
baseUrl: 'http://localhost:4200/',
framework: 'jasmine',
jasmineNodeOpts: {
showColors: true,
defaultTimeoutInterval: 30000,
print: function() {}
},
onPrepare() {
require('ts-node').register({
project: require('path').join(__dirname, './tsconfig.e2e.json')
});
jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
}
};

View File

@ -1,23 +0,0 @@
import { AppPage } from './app.po';
import { browser, logging } from 'protractor';
describe('workspace-project App', () => {
let page: AppPage;
beforeEach(() => {
page = new AppPage();
});
it('should display welcome message', () => {
page.navigateTo();
expect(page.getTitleText()).toEqual('Welcome to ZaksCode!');
});
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));
});
});

View File

@ -1,11 +0,0 @@
import { browser, by, element } from 'protractor';
export class AppPage {
navigateTo() {
return browser.get(browser.baseUrl) as Promise<any>;
}
getTitleText() {
return element(by.css('app-root h1')).getText() as Promise<string>;
}
}

View File

@ -1,13 +0,0 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/app",
"module": "commonjs",
"target": "es5",
"types": [
"jasmine",
"jasminewd2",
"node"
]
}
}

View File

@ -1,23 +0,0 @@
{
"firestore": {
"rules": "firestore.rules",
"indexes": "firestore.indexes.json"
},
"hosting": {
"public": "dist/ZaksCode",
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
],
"rewrites": [
{
"source": "**",
"destination": "/index.html"
}
]
},
"storage": {
"rules": "storage.rules"
}
}

View File

@ -1,4 +0,0 @@
{
"indexes": [],
"fieldOverrides": []
}

View File

@ -1,8 +0,0 @@
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read;
allow write: if request.auth != null;
}
}
}

108
gitlab/.gitlab-ci.yml Normal file
View File

@ -0,0 +1,108 @@
image: node:16
stages:
- build
- test
- publish
- deploy
npm:
stage: build
artifacts:
paths:
- dist
expire_in: 1 week
cache:
- key:
files:
- package.json
paths:
- node_modules
- package-lock.json
policy: pull-push
- key: $CI_PIPELINE_ID
paths:
- dist
policy: push
script:
- npm i
- npm run build:prod
rules:
- if: $CI_COMMIT_BRANCH
audit:
stage: test
cache:
- key:
files:
- package.json
paths:
- node_modules
policy: pull
script:
- echo "vulnerabilities_high $(npm audit | grep -oE '[0-9]+ high' | grep -oE '[0-9]+' || echo 0)" > metrics.txt
- echo "vulnerabilities_medium $(npm audit | grep -oE '[0-9]+ moderate' | grep -oE '[0-9]+' || echo 0)" >> metrics.txt
- echo "vulnerabilities_low $(npm audit | grep -oE '[0-9]+ low' | grep -oE '[0-9]+' || echo 0)" >> metrics.txt
artifacts:
reports:
metrics: metrics.txt
rules:
- if: $CI_COMMIT_BRANCH
eslint:
stage: test
cache:
- key:
files:
- package.json
paths:
- node_modules
policy: pull
script:
- npm run lint
rules:
- if: $CI_COMMIT_BRANCH
registry:
stage: publish
image: docker
cache:
- key: $CI_PIPELINE_ID
paths:
- dist
policy: pull
before_script:
- docker login -u $CI_REGISTRY_USER -p $CI_JOB_TOKEN $CI_REGISTRY
script:
- TAG=$(echo "$CI_COMMIT_BRANCH" | sed -E "s/[_/]/-/g")
- docker build --no-cache -t "$CI_REGISTRY_IMAGE:$TAG" .
- docker push "$CI_REGISTRY_IMAGE:$TAG"
rules:
- if: $CI_COMMIT_BRANCH
tag:
stage: publish
image:
name: alpine/git
entrypoint: [ "" ]
cache: [ ]
before_script:
- git remote set-url origin "http://gitlab-ci-token:$DEPLOY_TOKEN@$CI_SERVER_HOST/$CI_PROJECT_PATH.git"
script:
- VERSION=$(cat package.json | grep version | grep -Eo ':.+' | grep -Eo '[[:alnum:]\.\/\-]+')
- git tag -f $VERSION $CI_COMMIT_SHA
- git push -f origin $VERSION
rules:
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
production:
stage: deploy
image: node:16
cache: [ ]
script:
- echo "Make curl request to portainer"
environment:
name: Production
url: https://zakscode.com
rules:
- if: '$CI_COMMIT_BRANCH == "production" && $CI_DEPLOY_FREEZE == null'

View File

@ -9,16 +9,28 @@ module.exports = function (config) {
require('karma-jasmine'), require('karma-jasmine'),
require('karma-chrome-launcher'), require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'), require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'), require('karma-coverage'),
require('@angular-devkit/build-angular/plugins/karma') require('@angular-devkit/build-angular/plugins/karma')
], ],
client: { client: {
jasmine: {
// you can add configuration options for Jasmine here
// the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html
// for example, you can disable the random execution with `random: false`
// or set a specific seed with `seed: 4321`
},
clearContext: false // leave Jasmine Spec Runner output visible in browser clearContext: false // leave Jasmine Spec Runner output visible in browser
}, },
coverageIstanbulReporter: { jasmineHtmlReporter: {
dir: require('path').join(__dirname, '../coverage/ZaksCode'), suppressAll: true // removes the duplicated traces
reports: ['html', 'lcovonly', 'text-summary'], },
fixWebpackSourcePaths: true coverageReporter: {
dir: require('path').join(__dirname, './coverage/zakscode'),
subdir: '.',
reporters: [
{ type: 'html' },
{ type: 'text-summary' }
]
}, },
reporters: ['progress', 'kjhtml'], reporters: ['progress', 'kjhtml'],
port: 9876, port: 9876,

View File

@ -1,55 +1,41 @@
{ {
"name": "zaks-code", "name": "zakscode",
"version": "1.0.0", "version": "0.0.0",
"scripts": { "scripts": {
"ng": "ng", "ng": "ng",
"start": "ng serve", "start": "ng serve",
"build": "ng build", "build": "ng build",
"test": "ng test", "watch": "ng build --watch --configuration development",
"lint": "ng lint", "test": "ng test"
"e2e": "ng e2e" },
}, "private": true,
"private": true, "dependencies": {
"dependencies": { "@angular/animations": "~13.3.0",
"@angular/animations": "~7.2.0", "@angular/cdk": "^13.3.5",
"@angular/cdk": "~7.3.7", "@angular/common": "~13.3.0",
"@angular/common": "~7.2.0", "@angular/compiler": "~13.3.0",
"@angular/compiler": "~7.2.0", "@angular/core": "~13.3.0",
"@angular/core": "~7.2.0", "@angular/forms": "~13.3.0",
"@angular/fire": "~5.1.2", "@angular/material": "^13.3.5",
"@angular/forms": "~7.2.0", "@angular/platform-browser": "~13.3.0",
"@angular/material": "^7.3.7", "@angular/platform-browser-dynamic": "~13.3.0",
"@angular/platform-browser": "~7.2.0", "@angular/router": "~13.3.0",
"@angular/platform-browser-dynamic": "~7.2.0", "rxjs": "~7.5.0",
"@angular/router": "~7.2.0", "tslib": "^2.3.0",
"@thisissoon/angular-inviewport": "^4.2.2", "zone.js": "~0.11.4"
"core-js": "^2.5.4", },
"firebase": "^5.1.0", "devDependencies": {
"hammerjs": "^2.0.8", "@angular-devkit/build-angular": "~13.3.4",
"rxjs": "~6.3.3", "@angular/cli": "~13.3.4",
"tslib": "^1.9.0", "@angular/compiler-cli": "~13.3.0",
"zone.js": "~0.8.26" "@types/jasmine": "~3.10.0",
}, "@types/node": "^12.11.1",
"devDependencies": { "jasmine-core": "~4.0.0",
"@angular-devkit/build-angular": "^0.13.9", "karma": "~6.3.0",
"@angular/cli": "~7.3.8", "karma-chrome-launcher": "~3.1.0",
"@angular/compiler-cli": "~7.2.0", "karma-coverage": "~2.1.0",
"@angular/language-service": "~7.2.0", "karma-jasmine": "~4.0.0",
"@types/jasmine": "~2.8.8", "karma-jasmine-html-reporter": "~1.7.0",
"@types/jasminewd2": "~2.0.3", "typescript": "~4.6.2"
"@types/node": "~8.9.4", }
"codelyzer": "~4.5.0",
"firebase-tools": "^6.0.1",
"jasmine-core": "~2.99.1",
"jasmine-spec-reporter": "~4.2.1",
"karma": "~4.0.0",
"karma-chrome-launcher": "~2.2.0",
"karma-coverage-istanbul-reporter": "~2.0.1",
"karma-jasmine": "~1.1.2",
"karma-jasmine-html-reporter": "^0.2.2",
"protractor": "~5.4.0",
"ts-node": "~7.0.0",
"tslint": "~5.11.0",
"typescript": "~3.2.2"
}
} }

View File

@ -1,58 +0,0 @@
<div class="w-100">
<div class="container d-flex align-items-center" style="height: 300px">
<div class="w-100 text-center">
<typewriter text="Admin Panel" [delay]="500"></typewriter>
</div>
</div>
<div class="container mb-md-5 p-0 bg-white">
<div class="p-4">
<h3>Users</h3>
<table class="table mt-3">
<thead>
<tr>
<th>First Name</th>
<th>Last Name</th>
<th>Email</th>
<th class="text-center">Admin</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let user of adminService.users | async">
<td>{{user.firstName}}</td>
<td>{{user.lastName}}</td>
<td>{{user.email}}</td>
<td class="text-center">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="adminCheck" [checked]="user.admin"
disabled>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<div class="p-4">
<h3>Quotes</h3>
<table class="table mt-3">
<thead>
<tr>
<th>Quote</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let quote of store.quotes | async">
<td>{{quote.text}}</td>
</tr>
<tr>
<td>
<a routerLink="">+ Add Quote</a>
</td>
</tr>
</tbody>
</table>
</div>
<footer class="p-1 bg-dark text-center" style="color: grey">
© 2019 ZaksCode
</footer>
</div>
</div>

View File

@ -1,11 +0,0 @@
import {Component} from '@angular/core';
import {AdminService} from './admin.service';
import {AppStore} from '../app.store';
@Component({
selector: 'admin',
templateUrl: 'admin.component.html'
})
export class AdminComponent {
constructor(public adminService: AdminService, public store: AppStore) {}
}

View File

@ -1,15 +0,0 @@
import {Observable} from 'rxjs';
import {DbUser} from '../models/dbUser';
import {AngularFirestore} from '@angular/fire/firestore';
import {Injectable} from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class AdminService {
users: Observable<DbUser[]>;
constructor(private firestore: AngularFirestore) {
this.users = this.firestore.collection<DbUser>('users').valueChanges();
}
}

View File

@ -1,10 +0,0 @@
<div style="position: absolute; top: 1em; right: 1em">
<div *ngIf="loggedIn">
<a routerLink="/admin" class="mr-3">Admin Panel</a>
<a routerLink="" (click)="authService.logout()">Logout</a>
</div>
<div *ngIf="!loggedIn">
<a routerLink="" (click)="authService.googleLogin()">Login</a>
</div>
</div>
<router-outlet></router-outlet>

View File

@ -1,20 +0,0 @@
import {Component} from '@angular/core';
import {AuthService} from './auth.service';
import {ActivatedRoute} from '@angular/router';
@Component({
selector: 'app-root',
templateUrl: 'app.component.html'
})
export class AppComponent {
loggedIn = false;
set title(title: string) {
document.getElementsByTagName('title')[0].innerHTML = `Zaks Code${title ? ` - ${title}` : ''}`;
}
constructor(private route: ActivatedRoute, public authService: AuthService) {
this.route.url.subscribe(() => this.title = ''); // Clear the title on nav event
this.authService.user.subscribe(user => this.loggedIn = !!user);
}
}

View File

@ -1,24 +1,17 @@
import {BrowserModule} from '@angular/platform-browser'; import {BrowserModule} from '@angular/platform-browser';
import {NgModule} from '@angular/core'; import {NgModule} from '@angular/core';
import {AngularFireModule} from '@angular/fire'; import {HomeComponent} from './views/home/home.component';
import {AngularFirestoreModule, FirestoreSettingsToken} from '@angular/fire/firestore';
import {HomeComponent} from './home/home.component';
import {BrowserAnimationsModule} from '@angular/platform-browser/animations'; import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
import {MaterialModule} from './material.module'; import {MaterialModule} from './material.module';
import {FormsModule} from '@angular/forms'; import {FormsModule} from '@angular/forms';
import {TypewriterComponent} from './components/typewriter/typewriter.component'; import {TypewriterComponent} from './components/typewriter/typewriter.component';
import {SlideShowComponent} from './components/slideShow/slideShow.component'; import {SlideShowComponent} from './components/slideShow/slideShow.component';
import {environment} from '../environments/environment'; import {AppComponent} from './views/app/app.component';
import {AppComponent} from './app.component';
import {AppRouting} from './app.routing'; import {AppRouting} from './app.routing';
import {AdminComponent} from './admin/admin.component';
import {AngularFireAuthModule} from '@angular/fire/auth';
import {ConsoleComponent} from './components/console/console.component'; import {ConsoleComponent} from './components/console/console.component';
@NgModule({ @NgModule({
declarations: [ declarations: [
AdminComponent,
AppComponent, AppComponent,
ConsoleComponent, ConsoleComponent,
HomeComponent, HomeComponent,
@ -26,18 +19,12 @@ import {ConsoleComponent} from './components/console/console.component';
TypewriterComponent TypewriterComponent
], ],
imports: [ imports: [
AngularFireAuthModule,
AngularFireModule.initializeApp(environment.firebase),
AngularFirestoreModule,
AppRouting, AppRouting,
BrowserModule, BrowserModule,
BrowserAnimationsModule, BrowserAnimationsModule,
FormsModule, FormsModule,
MaterialModule, MaterialModule,
], ],
providers: [
{ provide: FirestoreSettingsToken, useValue: {} }
],
bootstrap: [AppComponent] bootstrap: [AppComponent]
}) })
export class AppModule { } export class AppModule { }

View File

@ -1,14 +1,11 @@
import {NgModule} from '@angular/core'; import {NgModule} from '@angular/core';
import {RouterModule} from '@angular/router'; import {RouterModule} from '@angular/router';
import {HomeComponent} from './home/home.component'; import {HomeComponent} from './views/home/home.component';
import {AdminComponent} from './admin/admin.component';
import {AuthService} from './auth.service';
@NgModule({ @NgModule({
imports: [ imports: [
RouterModule.forRoot([ RouterModule.forRoot([
{path: '', component: HomeComponent}, {path: '', component: HomeComponent},
{path: 'admin', component: AdminComponent, canActivate: [AuthService]},
{path: '**', redirectTo: ''} {path: '**', redirectTo: ''}
]) ])
], ],

View File

@ -1,25 +0,0 @@
import {Injectable} from '@angular/core';
import {BehaviorSubject, Observable} from 'rxjs';
import {AngularFirestore} from '@angular/fire/firestore';
import {Quote} from './models/quote';
import {DbUser} from './models/dbUser';
@Injectable({
providedIn: 'root'
})
export class AppStore {
quotes: Observable<Quote[]>;
constructor(private firestore: AngularFirestore) {
this.quotes = this.firestore.collection<Quote>('quotes').valueChanges();
}
addQuote(text: string) {
return this.firestore.collection<Quote>('quotes').add({text: text});
}
async getUser(uid: string) {
let user = await this.firestore.collection<DbUser>('users').doc(uid).get().toPromise();
return user.data();
}
}

View File

@ -1,41 +0,0 @@
import {Injectable} from '@angular/core';
import {AngularFireAuth} from '@angular/fire/auth';
import {CanActivate, Router} from '@angular/router';
import {BehaviorSubject, Observable} from 'rxjs';
import { auth } from 'firebase';
import {map} from 'rxjs/operators';
import {User} from './models/user';
import {AppStore} from './app.store';
import {tap} from 'rxjs/internal/operators/tap';
import {filter} from 'rxjs/internal/operators/filter';
@Injectable({
providedIn: 'root'
})
export class AuthService implements CanActivate {
user = new BehaviorSubject<User>(null);
constructor(private store: AppStore, private afAuth: AngularFireAuth, private router: Router) {
this.afAuth.user.subscribe(async user => {
this.user.next(user ? <User>Object.assign(await this.store.getUser(user.uid), user): null);
});
}
canActivate(): Observable<boolean> {
return this.user.pipe(
filter(user => !!user),
map(user => !user.isAnonymous),
tap(user => user ? null : this.googleLogin())
);
}
googleLogin() {
return this.afAuth.auth.signInWithPopup(new auth.GoogleAuthProvider());
}
async logout() {
await this.afAuth.auth.signOut();
this.router.navigate(['/']);
}
}

View File

@ -1,8 +1,10 @@
<div clas="p-1" style="background-color: #353535; color: #fff;"> <div class="p-3 d-flex flex-column justify-content-end console overflow-hidden" [style.height]="height">
<div class="pl-2 pt-2" style="height: 10rem;"> <div>
<h4 *ngFor="let o of output">{{o}}</h4> <h4 class="m-0" *ngFor="let o of output">{{o}}</h4>
</div> </div>
<div class="pl-2 pb-2"> <div class="mt-2">
<h4 class="d-inline mr-2" style="color: #22ff22">></h4><typewriter [text]="input" (done)="enter()"></typewriter> <h4 class="m-0">
{{prompt}} <typewriter class="pl-2" [text]="input" (done)="done()"></typewriter>
</h4>
</div> </div>
</div> </div>

View File

@ -1,32 +1,35 @@
import {Component} from '@angular/core'; import {Component, Input, ViewChild} from '@angular/core';
import {AppStore} from '../../app.store'; import { take } from 'rxjs';
import {map} from 'rxjs/operators'; import {TypewriterComponent} from '../typewriter/typewriter.component';
export type ConsoleConfig = {
input: string,
output: () => string
}
@Component({ @Component({
selector: 'console', selector: 'console',
templateUrl: './console.component.html' templateUrl: './console.component.html',
styleUrls: ['./console.component.scss']
}) })
export class ConsoleComponent { export class ConsoleComponent {
private done = false; input = '';
output: string[] = [];
prompt = '>'
input = './motd.sh'; done = () => {};
output = [];
quote;
constructor(private store: AppStore) { @Input() height: string = 'auto';
store.quotes.subscribe(quotes => {
const quote = quotes[Math.floor(Math.random() * quotes.length)];
this.quote = quote.text;
});
}
enter() { @ViewChild(TypewriterComponent) typewriter!: TypewriterComponent;
if(this.done) return;
setTimeout(() => { exec(input: string, output: () => string) {
this.output.push(this.input); this.done = () => {
this.input = ''; this.output.push(`${this.prompt} ${input}`);
setTimeout(() => this.output.push(this.quote), 500); console.log(output());
}, 1500); this.output.push(output());
this.done = true; this.input = '';
} };
this.input = input;
}
} }

View File

@ -1,10 +1,15 @@
import {Component, Input} from '@angular/core'; import {Component, Input} from '@angular/core';
import {Slide} from '../../models/slide';
export type Slide = {
title: string;
description: string;
image: string;
}
@Component({ @Component({
selector: 'slideshow', selector: 'slideshow',
templateUrl: 'slideShow.component.html' templateUrl: 'slideShow.component.html'
}) })
export class SlideShowComponent { export class SlideShowComponent {
@Input() slides: Slide[]; @Input() slides: Slide[] = [];
} }

View File

@ -1,27 +1,30 @@
import {Component, EventEmitter, Input, Output} from '@angular/core'; import {Component, EventEmitter, Input, Output} from '@angular/core';
import {Observable, timer} from 'rxjs'; import {Observable, of, timer} from 'rxjs';
import {filter, map, tap} from 'rxjs/operators'; import {filter, map, tap} from 'rxjs/operators';
@Component({ @Component({
selector: 'typewriter', selector: 'typewriter',
template: ` template: `<span class="typewriter">{{output | async}}</span>`,
<div class="d-inline typewriter"><h4 class="d-inline">{{output | async}}</h4></div>`,
styleUrls: ['typewriter.component.scss'] styleUrls: ['typewriter.component.scss']
}) })
export class TypewriterComponent { export class TypewriterComponent {
output?: Observable<string>;
@Input() delay = 1500; @Input() delay = 1500;
@Input() speed = 100; @Input() speed = 100;
@Input() set text(text: string) { @Input() set text(text: string) {
this.output = timer(this.delay, this.speed).pipe( if(!text) {
filter(n => n <= (text.length || 0)), this.output = of('');
tap(n => { } else {
if(n == text.length) this.done.emit(); this.output = timer(this.delay, this.speed).pipe(
}), filter(n => n <= (text.length || 0)),
map(n => text.slice(0, n)) tap(n => {
); if(n == text.length) this.done.emit();
}),
map(n => text.slice(0, n))
);
}
} }
@Output() done = new EventEmitter<void>(); @Output() done = new EventEmitter<void>();
output: Observable<string>;
} }

View File

@ -1,67 +0,0 @@
<div class="w-100">
<div class="w-100" style="height: calc(100vh - 140px)"></div>
<div class="container p-0 bg-white">
<div class="px-4" style="background-color: #732222">
<mat-card style="max-width: 600px; transform: translateY(-33%)">
<mat-card-content class="d-flex p-3">
<div class="d-none d-md-block pr-3">
<img src="assets/img/portrait.jpg" width="150px" height="150px" style="border-radius: 50%" alt="Zakary Timson">
</div>
<div>
<h1 class="mb-0">Zakary Timson</h1>
<h5 class="text-muted">FULL STACK SOFTWARE ENGINEER</h5>
<div class="mt-3">
<div><i class="mr-2 fa fa-map-marker-alt"></i> London Ontario, Canada</div>
<div><i class="mr-2 fa fa-envelope"></i> <a href="mailto:zaktimson@gmail.com">zaktimson@gmail.com</a>
</div>
<div><i class="mr-2 fab fa-github"></i> <a href="https://github.com/ztimson" target="_blank">github.com/ztimson</a></div>
</div>
</div>
</mat-card-content>
</mat-card>
</div>
<div class="p-4">
<console></console>
</div>
<div class="p-4">
<h3>About Me</h3>
<div class="d-flex align-items-center">
<div class="flex-grow-1">
<p>
Zak was born with a keyboard in hand and was learning his first programming language by thirteen. Nearly
entirely self taught, Zak challenged his programming courses through both highschool and college while
working in the industry to gain professional experience. He is very passionate about technology and as a
lifelong learner it has opened the door to many other hobbies like robotics, space and physics. Some of his
personal projects include a full sized arcade machine, home automation and a power wall for a home solar
system.
</p>
</div>
<div class="flex-shrink-1 p-3">
<img src="assets/img/keyboard-in-hand.jpg" height="150px" width="auto" style="border-radius: 50%">
</div>
</div>
</div>
<!-- <div class="p-4 text-white" style="background-color: #1c7fc1">
<h3>Projects</h3>
<div style="height: 400px">
<slideshow></slideshow>
</div>
</div> -->
<div class="p-4">
<h3>Resume & References</h3>
<a class="btn btn-outline-primary" href="https://docs.google.com/document/d/1xP6HASPerXKMJM_x6-PhHVvoYgq-Hym5IRO7g47EX8o/edit?usp=sharing" target="_blank">Resume</a>
<div class="ml-3 btn-group" role="group" aria-label="Basic example">
<a class="btn btn-outline-info" href="https://drive.google.com/file/d/0B_iz0vkzXmAyNWw0UDFzT0ZTeVU/view?usp=sharing" target="_blank">Manager</a>
<a class="btn btn-outline-info" href="https://drive.google.com/file/d/0B_iz0vkzXmAyaFBhcXBEaGp6YWc/view?usp=sharing" target="_blank">Contractor</a>
<a class="btn btn-outline-info" href="https://drive.google.com/file/d/0B_iz0vkzXmAyM0YtTWcxQzk0dEE/view?usp=sharing" target="_blank">Teacher</a>
<a class="btn btn-outline-info" href="https://drive.google.com/file/d/0B_iz0vkzXmAyX2owd0xURjh3RlE/view?usp=sharing" target="_blank">Principle</a>
<a class="btn btn-outline-info" href="https://drive.google.com/file/d/0B_iz0vkzXmAyMHdaM1BjZ1MwbWxva2lOY290NElwanN4b2JV/view?usp=sharing" target="_blank">CD Project Red</a>
</div>
</div>
<footer class="p-1 bg-dark text-center" style="color: grey">
Copyright © Zakary Timson 2019 | All Rights Reserved
</footer>
<div class="skirt"></div>
</div>
</div>

View File

@ -1,7 +0,0 @@
import { Component } from '@angular/core';
@Component({
selector: 'home',
templateUrl: './home.component.html'
})
export class HomeComponent { }

View File

@ -1,5 +1,5 @@
import {NgModule} from '@angular/core'; import {NgModule} from '@angular/core';
import {MatCardModule} from '@angular/material'; import {MatCardModule} from '@angular/material/card';
const MODULES = [ const MODULES = [
MatCardModule MatCardModule

View File

@ -1,6 +0,0 @@
export interface DbUser {
admin: boolean;
email: string;
firstName: string;
lastName: string;
}

View File

@ -1,3 +0,0 @@
export interface Quote {
text: string;
}

View File

@ -1,7 +0,0 @@
import {SafeUrl} from '@angular/platform-browser';
export interface Slide {
title: string
description: string
image: string | SafeUrl
}

View File

@ -1,4 +0,0 @@
import {User as FirebaseUser} from 'firebase';
import {DbUser} from './dbUser';
export interface User extends FirebaseUser, DbUser { }

View File

@ -0,0 +1 @@
<router-outlet></router-outlet>

View File

@ -0,0 +1,17 @@
import {Component} from '@angular/core';
import {Title} from '@angular/platform-browser';
import {ActivatedRoute} from '@angular/router';
@Component({
selector: 'app-root',
templateUrl: 'app.component.html'
})
export class AppComponent {
constructor(private route: ActivatedRoute, private title: Title) {
this.route.url.subscribe(() => this.setTitle(''));
}
setTitle(title: string) {
this.title.setTitle(`Zak's Code${title ? ` - ${title}` : ''}`);
}
}

View File

@ -0,0 +1,69 @@
<div class="w-100">
<div class="w-100" style="height: calc(100vh - 140px)"></div>
<div class="container p-0 bg-white">
<div class="px-3" style="background-color: #732222">
<mat-card style="max-width: 600px; transform: translateY(-33%)">
<mat-card-content class="d-flex p-3">
<div class="d-none d-md-block pr-3">
<img src="assets/img/portrait.jpg" width="150px" height="150px" style="border-radius: 50%" alt="Zakary Timson">
</div>
<div>
<h1 class="mb-0">Zakary Timson</h1>
<h5 class="text-muted">DEVOPS & SOFTWARE ENGINEER</h5>
<div class="mt-3">
<div><i class="mr-2 fa fa-map-marker-alt"></i> London Ontario, Canada</div>
<div><i class="mr-2 fa fa-envelope"></i> <a href="mailto:zaktimson@gmail.com">zaktimson@gmail.com</a>
</div>
<div><i class="mr-2 fab fa-github"></i> <a href="https://github.com/ztimson" target="_blank">github.com/ztimson</a></div>
</div>
</div>
</mat-card-content>
</mat-card>
</div>
<div class="p-3">
<console height="12rem"></console>
</div>
<!-- <div class="p-3">-->
<!-- <h3>Projects</h3>-->
<!-- <iframe class="border-0" src="https://gitlab.zakscode.com/explore" width="100%" height="75vh">-->
<!-- </iframe>-->
<!-- </div>-->
<div class="p-3">
<h3>About Me</h3>
<div>
<img alt="Childhood" class="float-right m-3 m-md-0 ml-md-3" src="assets/img/keyboard-in-hand.jpg" height="150px" width="auto" style="border-radius: 50%">
<p>
Zak was born with a keyboard in hand and was learning his first programming language by thirteen. Nearly
entirely self taught, Zak challenged his programming courses through both high-school and college while
working in the industry to gain professional experience. He is very passionate about technology and as a
lifelong learner it has opened the door to many other hobbies like robotics, space and physics. Some of his
personal projects include a full sized arcade machine, home automation and a power wall for a home solar
system.
</p>
</div>
</div>
<div class="p-3 overflow-hidden">
<h3>Resume & References</h3>
<a class="mb-3 btn btn-outline-primary" href="https://docs.google.com/document/d/1xP6HASPerXKMJM_x6-PhHVvoYgq-Hym5IRO7g47EX8o/edit?usp=sharing" target="_blank">Resume</a>
<ul class="d-md-none list-group">
<a class="list-group-item list-group-item-action border-info text-info" href="https://docs.google.com/document/d/1xP6HASPerXKMJM_x6-PhHVvoYgq-Hym5IRO7g47EX8o/edit?usp=sharing" target="_blank">Resume</a>
<a class="list-group-item list-group-item-action border-info text-info" href="https://drive.google.com/file/d/0B_iz0vkzXmAyNWw0UDFzT0ZTeVU/view?usp=sharing" target="_blank">Manager</a>
<a class="list-group-item list-group-item-action border-info text-info" href="https://drive.google.com/file/d/0B_iz0vkzXmAyaFBhcXBEaGp6YWc/view?usp=sharing" target="_blank">Contractor</a>
<a class="list-group-item list-group-item-action border-info text-info" href="https://drive.google.com/file/d/0B_iz0vkzXmAyM0YtTWcxQzk0dEE/view?usp=sharing" target="_blank">Teacher</a>
<a class="list-group-item list-group-item-action border-info text-info" href="https://drive.google.com/file/d/0B_iz0vkzXmAyX2owd0xURjh3RlE/view?usp=sharing" target="_blank">Principle</a>
<a class="list-group-item list-group-item-action border-info text-info" href="https://drive.google.com/file/d/0B_iz0vkzXmAyMHdaM1BjZ1MwbWxva2lOY290NElwanN4b2JV/view?usp=sharing" target="_blank">CD Projekt Red</a>
</ul>
<div class="d-none d-md-block btn-group" role="group" aria-label="Basic example">
<a class="btn btn-outline-info" href="https://drive.google.com/file/d/0B_iz0vkzXmAyNWw0UDFzT0ZTeVU/view?usp=sharing" target="_blank">Manager</a>
<a class="btn btn-outline-info" href="https://drive.google.com/file/d/0B_iz0vkzXmAyaFBhcXBEaGp6YWc/view?usp=sharing" target="_blank">Contractor</a>
<a class="btn btn-outline-info" href="https://drive.google.com/file/d/0B_iz0vkzXmAyM0YtTWcxQzk0dEE/view?usp=sharing" target="_blank">Teacher</a>
<a class="btn btn-outline-info" href="https://drive.google.com/file/d/0B_iz0vkzXmAyX2owd0xURjh3RlE/view?usp=sharing" target="_blank">Principle</a>
<a class="btn btn-outline-info" href="https://drive.google.com/file/d/0B_iz0vkzXmAyMHdaM1BjZ1MwbWxva2lOY290NElwanN4b2JV/view?usp=sharing" target="_blank">CD Projekt Red</a>
</div>
</div>
<footer class="p-1 bg-dark text-center" style="color: grey">
Copyright © Zakary Timson 2019 | All Rights Reserved
</footer>
<div class="d-none d-sm-block skirt"></div>
</div>
</div>

View File

@ -0,0 +1,15 @@
import {Component, ViewChild} from '@angular/core';
import {ConsoleComponent, ConsoleConfig} from '../../components/console/console.component';
import {QuoteService} from '../../services/quote.service';
@Component({
selector: 'home',
templateUrl: './home.component.html'
})
export class HomeComponent {
@ViewChild(ConsoleComponent) console!: ConsoleComponent;
constructor(private quotes: QuoteService) {
setTimeout(() => this.console.exec('bash ./random-thought.sh', () => quotes.random()), 3000);
}
}

View File

@ -1,11 +0,0 @@
# This file is currently used by autoprefixer to adjust CSS to support the below specified browsers
# For additional information regarding the format and rule options, please see:
# https://github.com/browserslist/browserslist#queries
#
# For IE 9-11 support, please remove 'not' from the last line of the file and adjust as needed
> 0.5%
last 2 versions
Firefox ESR
not dead
not IE 9-11

View File

@ -1,11 +1,3 @@
export const environment = { export const environment = {
production: true, production: true
firebase: {
apiKey: "AIzaSyC1vKmDDzRR85DnOowPZvAkZEyzJ1EmjGI",
authDomain: "zaks-code.firebaseapp.com",
databaseURL: "https://zaks-code.firebaseio.com",
projectId: "zaks-code",
storageBucket: "zaks-code.appspot.com",
messagingSenderId: "430075133237"
}
}; };

View File

@ -1,11 +1,16 @@
// This file can be replaced during build by using the `fileReplacements` array.
// `ng build` replaces `environment.ts` with `environment.prod.ts`.
// The list of file replacements can be found in `angular.json`.
export const environment = { export const environment = {
production: false, production: false
firebase: {
apiKey: 'AIzaSyC1vKmDDzRR85DnOowPZvAkZEyzJ1EmjGI',
authDomain: 'zaks-code.firebaseapp.com',
databaseURL: 'https://zaks-code.firebaseio.com',
projectId: 'zaks-code',
storageBucket: 'zaks-code.appspot.com',
messagingSenderId: '430075133237'
}
}; };
/*
* For easier debugging in development mode, you can import the following file
* to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
*
* This import should be commented out in production mode because it will have a negative impact
* on performance if an error is thrown.
*/
// import 'zone.js/plugins/zone-error'; // Included with Angular CLI.

View File

@ -1,25 +1,28 @@
<!doctype html> <!doctype html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<title>Zaks Code</title> <title>Zaks Code</title>
<base href="/"> <base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<meta name=”robots” content=”index,nofollow” /> <meta name=”robots” content=”index,nofollow” />
<meta property=”og:type” content=”website” /> <meta property=”og:type” content=”website” />
<meta property=”og:title” content=”Zakary Timson /> <meta property=”og:title” content=”Zakary Timson />
<meta property=”og:description” content=”Cloud Architecture & Software Engineering /> <meta property=”og:description” content=”Cloud Architecture & Software Engineering />
<meta property=”og:image” content=”https://zakscode.com/assets/img/ZaksCode.png” /> <meta property=”og:image” content=”https://zakscode.com/assets/img/ZaksCode.png” />
<meta property=”og:url” content=”https://zakscode.com” /> <meta property=”og:url” content=”https://zakscode.com” />
<meta property=”og:site_name” content=”ZaksCode” /> <meta property=”og:site_name” content=”ZaksCode” />
<meta name=”twitter:title” content=”Zakary Timson /> <meta name=”twitter:title” content=”Zakary Timson />
<meta name=”twitter:description” content=”Cloud Architecture & Software Engineering /> <meta name=”twitter:description” content=”Cloud Architecture & Software Engineering />
<meta name=”twitter:image” content=”https://zakscode.com/assets/img/ZaksCode.png” /> <meta name=”twitter:image” content=”https://zakscode.com/assets/img/ZaksCode.png” />
<link rel="icon" type="image/png" href="assets/img/logo.png"> <link rel="icon" type="image/png" href="assets/img/logo.png">
<link rel="preconnect" href="https://fonts.gstatic.com">
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
</head> </head>
<body> <body>
<app-root></app-root> <app-root></app-root>
</body> </body>
</html> </html>

View File

@ -1,4 +1,3 @@
import 'hammerjs';
import { enableProdMode } from '@angular/core'; import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

View File

@ -8,8 +8,8 @@
* file. * file.
* *
* The current setup is for so-called "evergreen" browsers; the last versions of browsers that * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
* automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), * automatically update themselves. This includes recent versions of Safari, Chrome (including
* Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. * Opera), Edge on the desktop, and iOS and Chrome on mobile.
* *
* Learn more in https://angular.io/guide/browser-support * Learn more in https://angular.io/guide/browser-support
*/ */
@ -18,16 +18,6 @@
* BROWSER POLYFILLS * BROWSER POLYFILLS
*/ */
/** IE10 and IE11 requires the following for NgClass support on SVG elements */
// import 'classlist.js'; // Run `npm install --save classlist.js`.
/**
* Web Animations `@angular/platform-browser/animations`
* Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
* Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
*/
// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
/** /**
* By default, zone.js will patch all possible macroTask and DomEvents * By default, zone.js will patch all possible macroTask and DomEvents
* user can disable parts of macroTask/DomEvents patch by setting following flags * user can disable parts of macroTask/DomEvents patch by setting following flags
@ -35,7 +25,7 @@
* will put import in the top of bundle, so user need to create a separate file * will put import in the top of bundle, so user need to create a separate file
* in this directory (for example: zone-flags.ts), and put the following flags * in this directory (for example: zone-flags.ts), and put the following flags
* into that file, and then add the following code before importing zone.js. * into that file, and then add the following code before importing zone.js.
* import './zone-flags.ts'; * import './zone-flags';
* *
* The flags allowed in zone-flags.ts are listed here. * The flags allowed in zone-flags.ts are listed here.
* *
@ -43,7 +33,7 @@
* *
* (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
* (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
* (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
* *
* in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
* with the following flag, it will bypass `zone.js` patch for IE/Edge * with the following flag, it will bypass `zone.js` patch for IE/Edge
@ -55,7 +45,7 @@
/*************************************************************************************************** /***************************************************************************************************
* Zone JS is required by default for Angular itself. * Zone JS is required by default for Angular itself.
*/ */
import 'zone.js/dist/zone'; // Included with Angular CLI. import 'zone.js'; // Included with Angular CLI.
/*************************************************************************************************** /***************************************************************************************************

View File

@ -1,3 +1,38 @@
// Custom Theming for Angular Material
// For more information: https://material.angular.io/guide/theming
@use '@angular/material' as mat;
// Plus imports for other components in your app.
// Include the common styles for Angular Material. We include this here so that you only
// have to load a single css file for Angular Material in your app.
// Be sure that you only ever include this mixin once!
@include mat.core();
// Define the palettes for your theme using the Material Design palettes available in palette.scss
// (imported above). For each palette, you can optionally specify a default, lighter, and darker
// hue. Available color palettes: https://material.io/design/color/
$zakscode-primary: mat.define-palette(mat.$indigo-palette);
$zakscode-accent: mat.define-palette(mat.$pink-palette, A200, A100, A400);
// The warn palette is optional (defaults to red).
$zakscode-warn: mat.define-palette(mat.$red-palette);
// Create the theme object. A theme consists of configurations for individual
// theming systems such as "color" or "typography".
$zakscode-theme: mat.define-light-theme((
color: (
primary: $zakscode-primary,
accent: $zakscode-accent,
warn: $zakscode-warn,
)
));
// Include theme styles for core and each component used in your app.
// Alternatively, you can import and @include the theme mixins for each component
// that you are using.
@include mat.all-component-themes($zakscode-theme);
@import url("https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.3.1/css/bootstrap.min.css"); @import url("https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.3.1/css/bootstrap.min.css");
@import url("https://use.fontawesome.com/releases/v5.8.1/css/all.css"); @import url("https://use.fontawesome.com/releases/v5.8.1/css/all.css");
@import url("https://fonts.googleapis.com/icon?family=Material+Icons|Roboto:300,400,500"); @import url("https://fonts.googleapis.com/icon?family=Material+Icons|Roboto:300,400,500");
@ -18,17 +53,20 @@
} }
html, body { html, body {
height: 100%; height: 100%;
} }
body { body {
margin: 0; margin: 0;
font-family: Roboto, sans-serif; font-family: Roboto, sans-serif;
background: #354B72 url("/assets/img/ZaksCode.gif") no-repeat fixed center; background: #354B72 url("/assets/img/ZaksCode.gif") no-repeat fixed center;
} }
.skirt { .skirt {
height: 3rem; height: 3rem;
width: 100%; width: 100%;
background-color: #354B72; background-color: #354B72;
} }
html, body { height: 100%; }
body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; }

View File

@ -1,19 +1,25 @@
// This file is required by karma.conf.js and loads recursively all the .spec and framework files // This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'zone.js/dist/zone-testing'; import 'zone.js/testing';
import { getTestBed } from '@angular/core/testing'; import { getTestBed } from '@angular/core/testing';
import { import {
BrowserDynamicTestingModule, BrowserDynamicTestingModule,
platformBrowserDynamicTesting platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing'; } from '@angular/platform-browser-dynamic/testing';
declare const require: any; declare const require: {
context(path: string, deep?: boolean, filter?: RegExp): {
<T>(id: string): T;
keys(): string[];
};
};
// First, initialize the Angular testing environment. // First, initialize the Angular testing environment.
getTestBed().initTestEnvironment( getTestBed().initTestEnvironment(
BrowserDynamicTestingModule, BrowserDynamicTestingModule,
platformBrowserDynamicTesting() platformBrowserDynamicTesting(),
); );
// Then we find all the tests. // Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/); const context = require.context('./', true, /\.spec\.ts$/);
// And load the modules. // And load the modules.

View File

@ -1,11 +0,0 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/app",
"types": []
},
"exclude": [
"test.ts",
"**/*.spec.ts"
]
}

View File

@ -1,18 +0,0 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/spec",
"types": [
"jasmine",
"node"
]
},
"files": [
"test.ts",
"polyfills.ts"
],
"include": [
"**/*.spec.ts",
"**/*.d.ts"
]
}

View File

@ -1,17 +0,0 @@
{
"extends": "../tslint.json",
"rules": {
"directive-selector": [
true,
"attribute",
"app",
"camelCase"
],
"component-selector": [
true,
"element",
"app",
"kebab-case"
]
}
}

View File

@ -1,8 +0,0 @@
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read;
allow write: if request.auth != null;
}
}
}

15
tsconfig.app.json Normal file
View File

@ -0,0 +1,15 @@
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./out-tsc/app",
"types": []
},
"files": [
"src/main.ts",
"src/polyfills.ts"
],
"include": [
"src/**/*.d.ts"
]
}

View File

@ -1,22 +1,32 @@
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{ {
"compileOnSave": false, "compileOnSave": false,
"compilerOptions": { "compilerOptions": {
"baseUrl": "./", "baseUrl": "./",
"outDir": "./dist/out-tsc", "outDir": "./dist/out-tsc",
"forceConsistentCasingInFileNames": true,
"strict": true,
"noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"sourceMap": true, "sourceMap": true,
"declaration": false, "declaration": false,
"module": "es2015", "downlevelIteration": true,
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true, "experimentalDecorators": true,
"moduleResolution": "node",
"importHelpers": true, "importHelpers": true,
"target": "es5", "target": "es2017",
"typeRoots": [ "module": "es2020",
"node_modules/@types"
],
"lib": [ "lib": [
"es2018", "es2020",
"dom" "dom"
] ]
},
"angularCompilerOptions": {
"enableI18nLegacyMessageIdFormat": false,
"strictInjectionParameters": true,
"strictInputAccessModifiers": true,
"strictTemplates": true
} }
} }

18
tsconfig.spec.json Normal file
View File

@ -0,0 +1,18 @@
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./out-tsc/spec",
"types": [
"jasmine"
]
},
"files": [
"src/test.ts",
"src/polyfills.ts"
],
"include": [
"src/**/*.spec.ts",
"src/**/*.d.ts"
]
}

View File

@ -1,75 +0,0 @@
{
"extends": "tslint:recommended",
"rulesDirectory": [
"codelyzer"
],
"rules": {
"array-type": false,
"arrow-parens": false,
"deprecation": {
"severity": "warn"
},
"import-blacklist": [
true,
"rxjs/Rx"
],
"interface-name": false,
"max-classes-per-file": false,
"max-line-length": [
true,
140
],
"member-access": false,
"member-ordering": [
true,
{
"order": [
"static-field",
"instance-field",
"static-method",
"instance-method"
]
}
],
"no-consecutive-blank-lines": false,
"no-console": [
true,
"debug",
"info",
"time",
"timeEnd",
"trace"
],
"no-empty": false,
"no-inferrable-types": [
true,
"ignore-params"
],
"no-non-null-assertion": true,
"no-redundant-jsdoc": true,
"no-switch-case-fall-through": true,
"no-use-before-declare": true,
"no-var-requires": false,
"object-literal-key-quotes": [
true,
"as-needed"
],
"object-literal-sort-keys": false,
"ordered-imports": false,
"quotemark": [
true,
"single"
],
"trailing-comma": false,
"no-output-on-prefix": true,
"use-input-property-decorator": true,
"use-output-property-decorator": true,
"use-host-property-decorator": true,
"no-input-rename": true,
"no-output-rename": true,
"use-life-cycle-interface": true,
"use-pipe-transform-interface": true,
"component-class-suffix": true,
"directive-class-suffix": true
}
}