Create Angular version (#1)
Convert site to Angular; also reworked contents on solution pages
This commit is contained in:
parent
7dc609cf94
commit
cfaf2e84f7
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -14,3 +14,4 @@ selenium-debug.log
|
|||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
.ionide/
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
module.exports = {
|
||||
presets: [
|
||||
'@vue/app'
|
||||
]
|
||||
}
|
13
bit-badger-solutions/.editorconfig
Normal file
13
bit-badger-solutions/.editorconfig
Normal file
|
@ -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
|
46
bit-badger-solutions/.gitignore
vendored
Normal file
46
bit-badger-solutions/.gitignore
vendored
Normal file
|
@ -0,0 +1,46 @@
|
|||
# See http://help.github.com/ignore-files/ for more about ignoring files.
|
||||
|
||||
# compiled output
|
||||
/dist
|
||||
/tmp
|
||||
/out-tsc
|
||||
# Only exists if Bazel was run
|
||||
/bazel-out
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
|
||||
# profiling files
|
||||
chrome-profiler-events*.json
|
||||
speed-measure-plugin*.json
|
||||
|
||||
# IDEs and editors
|
||||
/.idea
|
||||
.project
|
||||
.classpath
|
||||
.c9/
|
||||
*.launch
|
||||
.settings/
|
||||
*.sublime-workspace
|
||||
|
||||
# IDE - VSCode
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
.history/*
|
||||
|
||||
# misc
|
||||
/.sass-cache
|
||||
/connect.lock
|
||||
/coverage
|
||||
/libpeerconnection.log
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
testem.log
|
||||
/typings
|
||||
|
||||
# System Files
|
||||
.DS_Store
|
||||
Thumbs.db
|
129
bit-badger-solutions/angular.json
Normal file
129
bit-badger-solutions/angular.json
Normal file
|
@ -0,0 +1,129 @@
|
|||
{
|
||||
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
||||
"version": 1,
|
||||
"newProjectRoot": "projects",
|
||||
"projects": {
|
||||
"bit-badger-solutions": {
|
||||
"projectType": "application",
|
||||
"schematics": {
|
||||
"@schematics/angular:component": {
|
||||
"style": "sass"
|
||||
}
|
||||
},
|
||||
"root": "",
|
||||
"sourceRoot": "src",
|
||||
"prefix": "app",
|
||||
"architect": {
|
||||
"build": {
|
||||
"builder": "@angular-devkit/build-angular:browser",
|
||||
"options": {
|
||||
"outputPath": "dist/bit-badger-solutions",
|
||||
"index": "src/index.html",
|
||||
"main": "src/main.ts",
|
||||
"polyfills": "src/polyfills.ts",
|
||||
"tsConfig": "tsconfig.app.json",
|
||||
"aot": false,
|
||||
"assets": [
|
||||
"src/favicon.ico",
|
||||
"src/assets"
|
||||
],
|
||||
"styles": [
|
||||
"src/styles.sass"
|
||||
],
|
||||
"scripts": []
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"fileReplacements": [
|
||||
{
|
||||
"replace": "src/environments/environment.ts",
|
||||
"with": "src/environments/environment.prod.ts"
|
||||
}
|
||||
],
|
||||
"optimization": true,
|
||||
"outputHashing": "all",
|
||||
"sourceMap": false,
|
||||
"extractCss": true,
|
||||
"namedChunks": false,
|
||||
"aot": true,
|
||||
"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": "bit-badger-solutions:build"
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"browserTarget": "bit-badger-solutions:build:production"
|
||||
}
|
||||
}
|
||||
},
|
||||
"extract-i18n": {
|
||||
"builder": "@angular-devkit/build-angular:extract-i18n",
|
||||
"options": {
|
||||
"browserTarget": "bit-badger-solutions: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.sass"
|
||||
],
|
||||
"scripts": []
|
||||
}
|
||||
},
|
||||
"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": "bit-badger-solutions:serve"
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"devServerTarget": "bit-badger-solutions:serve:production"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}},
|
||||
"defaultProject": "bit-badger-solutions"
|
||||
}
|
12
bit-badger-solutions/browserslist
Normal file
12
bit-badger-solutions/browserslist
Normal file
|
@ -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'.
|
32
bit-badger-solutions/e2e/protractor.conf.js
Normal file
32
bit-badger-solutions/e2e/protractor.conf.js
Normal file
|
@ -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: 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.json')
|
||||
});
|
||||
jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
|
||||
}
|
||||
};
|
23
bit-badger-solutions/e2e/src/app.e2e-spec.ts
Normal file
23
bit-badger-solutions/e2e/src/app.e2e-spec.ts
Normal file
|
@ -0,0 +1,23 @@
|
|||
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('bit-badger-solutions app is running!');
|
||||
});
|
||||
|
||||
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));
|
||||
});
|
||||
});
|
11
bit-badger-solutions/e2e/src/app.po.ts
Normal file
11
bit-badger-solutions/e2e/src/app.po.ts
Normal file
|
@ -0,0 +1,11 @@
|
|||
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 .content span')).getText() as Promise<string>;
|
||||
}
|
||||
}
|
13
bit-badger-solutions/e2e/tsconfig.json
Normal file
13
bit-badger-solutions/e2e/tsconfig.json
Normal file
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../out-tsc/e2e",
|
||||
"module": "commonjs",
|
||||
"target": "es5",
|
||||
"types": [
|
||||
"jasmine",
|
||||
"jasminewd2",
|
||||
"node"
|
||||
]
|
||||
}
|
||||
}
|
32
bit-badger-solutions/karma.conf.js
Normal file
32
bit-badger-solutions/karma.conf.js
Normal file
|
@ -0,0 +1,32 @@
|
|||
// Karma configuration file, see link for more information
|
||||
// https://karma-runner.github.io/1.0/config/configuration-file.html
|
||||
|
||||
module.exports = function (config) {
|
||||
config.set({
|
||||
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/bit-badger-solutions'),
|
||||
reports: ['html', 'lcovonly', 'text-summary'],
|
||||
fixWebpackSourcePaths: true
|
||||
},
|
||||
reporters: ['progress', 'kjhtml'],
|
||||
port: 9876,
|
||||
colors: true,
|
||||
logLevel: config.LOG_INFO,
|
||||
autoWatch: true,
|
||||
browsers: ['Chrome'],
|
||||
singleRun: false,
|
||||
restartOnFileChange: true
|
||||
});
|
||||
};
|
47
bit-badger-solutions/package.json
Normal file
47
bit-badger-solutions/package.json
Normal file
|
@ -0,0 +1,47 @@
|
|||
{
|
||||
"name": "bit-badger-solutions",
|
||||
"version": "3.0.0",
|
||||
"scripts": {
|
||||
"ng": "ng",
|
||||
"start": "ng serve",
|
||||
"build": "ng build",
|
||||
"test": "ng test",
|
||||
"lint": "ng lint",
|
||||
"e2e": "ng e2e"
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "~8.2.13",
|
||||
"@angular/common": "~8.2.13",
|
||||
"@angular/compiler": "~8.2.13",
|
||||
"@angular/core": "~8.2.13",
|
||||
"@angular/forms": "~8.2.13",
|
||||
"@angular/platform-browser": "~8.2.13",
|
||||
"@angular/platform-browser-dynamic": "~8.2.13",
|
||||
"@angular/router": "~8.2.13",
|
||||
"rxjs": "~6.4.0",
|
||||
"tslib": "^1.10.0",
|
||||
"zone.js": "~0.9.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "~0.803.18",
|
||||
"@angular/cli": "~8.3.18",
|
||||
"@angular/compiler-cli": "~8.2.13",
|
||||
"@angular/language-service": "~8.2.13",
|
||||
"@types/node": "~8.9.4",
|
||||
"@types/jasmine": "~3.3.8",
|
||||
"@types/jasminewd2": "~2.0.3",
|
||||
"codelyzer": "^5.0.0",
|
||||
"jasmine-core": "~3.4.0",
|
||||
"jasmine-spec-reporter": "~4.2.1",
|
||||
"karma": "~4.1.0",
|
||||
"karma-chrome-launcher": "~2.2.0",
|
||||
"karma-coverage-istanbul-reporter": "~2.0.1",
|
||||
"karma-jasmine": "~2.0.1",
|
||||
"karma-jasmine-html-reporter": "^1.4.0",
|
||||
"protractor": "~5.4.0",
|
||||
"ts-node": "~7.0.0",
|
||||
"tslint": "~5.15.0",
|
||||
"typescript": "~3.5.3"
|
||||
}
|
||||
}
|
28
bit-badger-solutions/src/app/app-routing.module.ts
Normal file
28
bit-badger-solutions/src/app/app-routing.module.ts
Normal file
|
@ -0,0 +1,28 @@
|
|||
import { NgModule } from '@angular/core'
|
||||
import { Routes, RouterModule } from '@angular/router'
|
||||
|
||||
import { ApplicationComponent } from './applications/application.component'
|
||||
import { ApplicationListComponent } from './applications/application-list/application-list.component'
|
||||
import { HomeComponent } from './pages/home/home.component'
|
||||
import { InformationPublicizingComponent } from './pages/about/information-publicizing.component'
|
||||
import { LegacyDataComponent } from './pages/about/legacy-data.component'
|
||||
import { ProcessAutomationComponent } from './pages/about/process-automation.component'
|
||||
import { WebServicesComponent } from './pages/about/web-services.component'
|
||||
import { WhyBitBadgerComponent } from './pages/about/why-bit-badger.component'
|
||||
|
||||
const routes: Routes = [
|
||||
{ path: '', component: HomeComponent },
|
||||
{ path: 'about/information-publicizing-solutions', component: InformationPublicizingComponent },
|
||||
{ path: 'about/legacy-data', component: LegacyDataComponent },
|
||||
{ path: 'about/process-automation-solutions', component: ProcessAutomationComponent },
|
||||
{ path: 'about/web-services-solutions', component: WebServicesComponent },
|
||||
{ path: 'about/why-bit-badger', component: WhyBitBadgerComponent },
|
||||
{ path: 'solutions', component: ApplicationListComponent },
|
||||
{ path: 'solutions/:appId', component: ApplicationComponent }
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forRoot(routes, { scrollPositionRestoration: 'enabled' })],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class AppRoutingModule { }
|
5
bit-badger-solutions/src/app/app.component.html
Normal file
5
bit-badger-solutions/src/app/app.component.html
Normal file
|
@ -0,0 +1,5 @@
|
|||
<app-header></app-header>
|
||||
<div id="content">
|
||||
<router-outlet></router-outlet>
|
||||
</div>
|
||||
<app-footer></app-footer>
|
0
bit-badger-solutions/src/app/app.component.sass
Normal file
0
bit-badger-solutions/src/app/app.component.sass
Normal file
35
bit-badger-solutions/src/app/app.component.spec.ts
Normal file
35
bit-badger-solutions/src/app/app.component.spec.ts
Normal file
|
@ -0,0 +1,35 @@
|
|||
import { TestBed, async } from '@angular/core/testing';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { AppComponent } from './app.component';
|
||||
|
||||
describe('AppComponent', () => {
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
RouterTestingModule
|
||||
],
|
||||
declarations: [
|
||||
AppComponent
|
||||
],
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
it('should create the app', () => {
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
const app = fixture.debugElement.componentInstance;
|
||||
expect(app).toBeTruthy();
|
||||
});
|
||||
|
||||
it(`should have as title 'bit-badger-solutions'`, () => {
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
const app = fixture.debugElement.componentInstance;
|
||||
expect(app.title).toEqual('bit-badger-solutions');
|
||||
});
|
||||
|
||||
it('should render title', () => {
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
fixture.detectChanges();
|
||||
const compiled = fixture.debugElement.nativeElement;
|
||||
expect(compiled.querySelector('.content span').textContent).toContain('bit-badger-solutions app is running!');
|
||||
});
|
||||
});
|
10
bit-badger-solutions/src/app/app.component.ts
Normal file
10
bit-badger-solutions/src/app/app.component.ts
Normal file
|
@ -0,0 +1,10 @@
|
|||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
templateUrl: './app.component.html',
|
||||
styleUrls: ['./app.component.sass']
|
||||
})
|
||||
export class AppComponent {
|
||||
title = 'bit-badger-solutions';
|
||||
}
|
39
bit-badger-solutions/src/app/app.module.ts
Normal file
39
bit-badger-solutions/src/app/app.module.ts
Normal file
|
@ -0,0 +1,39 @@
|
|||
import { NgModule } from '@angular/core'
|
||||
import { BrowserModule } from '@angular/platform-browser'
|
||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'
|
||||
|
||||
import { AppRoutingModule } from './app-routing.module'
|
||||
import { ApplicationsModule } from './applications/applications.module'
|
||||
import { SharedModule } from './shared/shared.module'
|
||||
import { SidebarModule } from './sidebar/sidebar.module'
|
||||
|
||||
import { AppComponent } from './app.component'
|
||||
import { HomeComponent } from './pages/home/home.component'
|
||||
import { WhyBitBadgerComponent } from './pages/about/why-bit-badger.component'
|
||||
import { InformationPublicizingComponent } from './pages/about/information-publicizing.component'
|
||||
import { LegacyDataComponent } from './pages/about/legacy-data.component'
|
||||
import { ProcessAutomationComponent } from './pages/about/process-automation.component'
|
||||
import { WebServicesComponent } from './pages/about/web-services.component'
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AppComponent,
|
||||
HomeComponent,
|
||||
WhyBitBadgerComponent,
|
||||
InformationPublicizingComponent,
|
||||
LegacyDataComponent,
|
||||
ProcessAutomationComponent,
|
||||
WebServicesComponent,
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
BrowserAnimationsModule,
|
||||
AppRoutingModule,
|
||||
ApplicationsModule,
|
||||
SharedModule,
|
||||
SidebarModule
|
||||
],
|
||||
providers: [],
|
||||
bootstrap: [AppComponent]
|
||||
})
|
||||
export class AppModule { }
|
|
@ -0,0 +1,13 @@
|
|||
import { Component, OnInit } from '@angular/core'
|
||||
|
||||
@Component({
|
||||
selector: 'app-all-solutions-link',
|
||||
template: `<p><br><a routerLink="/solutions">« Back to All Solutions</a></p>`
|
||||
})
|
||||
export class AllSolutionsLinkComponent implements OnInit {
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit() { }
|
||||
|
||||
}
|
6
bit-badger-solutions/src/app/applications/app-item.ts
Normal file
6
bit-badger-solutions/src/app/applications/app-item.ts
Normal file
|
@ -0,0 +1,6 @@
|
|||
import { Type } from '@angular/core'
|
||||
|
||||
/** An item representing an app */
|
||||
export class AppItem {
|
||||
constructor(public name: string, public component: Type<any>) { }
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
import { Directive, ViewContainerRef } from '@angular/core'
|
||||
|
||||
@Directive({
|
||||
selector: '[app-application-detail]'
|
||||
})
|
||||
export class ApplicationDetailDirective {
|
||||
|
||||
constructor(public viewContainerRef: ViewContainerRef) { }
|
||||
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
<h1>
|
||||
{{ app.name }}<br>
|
||||
<small><small>
|
||||
<a *ngIf="linkToApp" [href]="app.url">{{ app.url }}</a>
|
||||
<span *ngIf="!linkToApp">{{ app.url }}</span>
|
||||
<span *ngIf="linkToArchive">
|
||||
<a [href]="app.archiveUrl"><small>(Archive)</small></a>
|
||||
</span>
|
||||
</small></small>
|
||||
</h1>
|
|
@ -0,0 +1,2 @@
|
|||
h1
|
||||
line-height: 1.6rem
|
|
@ -0,0 +1,28 @@
|
|||
import { Component, OnInit, Input } from '@angular/core'
|
||||
|
||||
import { App } from '../application.types'
|
||||
|
||||
@Component({
|
||||
selector: 'app-application-header',
|
||||
templateUrl: './application-header.component.html',
|
||||
styleUrls: ['./application-header.component.sass']
|
||||
})
|
||||
export class ApplicationHeaderComponent implements OnInit {
|
||||
|
||||
@Input() app: App
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit() { }
|
||||
|
||||
/** Whether to link to the app's URL */
|
||||
get linkToApp () {
|
||||
return this.app.isActive || this.app.linkInactive
|
||||
}
|
||||
|
||||
/** Whether to link to an archive URL */
|
||||
get linkToArchive () {
|
||||
return !this.app.isActive && !this.app.linkInactive && (this.app.archiveUrl > '')
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
<aside><span> </span><img [src]="imageLink" [alt]="imageAlt"></aside>
|
|
@ -0,0 +1,9 @@
|
|||
aside
|
||||
float: right
|
||||
background-color: #FFFAFA
|
||||
aside > span
|
||||
padding-left: .75rem
|
||||
aside > img
|
||||
overflow: hidden
|
||||
border: dotted 1px darkgray
|
||||
border-radius: 10px
|
|
@ -0,0 +1,29 @@
|
|||
import { Component, OnInit, Input } from '@angular/core'
|
||||
|
||||
import { App } from '../application.types'
|
||||
|
||||
@Component({
|
||||
selector: 'app-application-image',
|
||||
templateUrl: './application-image.component.html',
|
||||
styleUrls: ['./application-image.component.sass']
|
||||
})
|
||||
export class ApplicationImageComponent implements OnInit {
|
||||
|
||||
@Input() app: App
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit() {
|
||||
}
|
||||
|
||||
/** The link to the screenshot image */
|
||||
get imageLink () {
|
||||
return `/assets/screenshots/${this.app.id}.png`
|
||||
}
|
||||
|
||||
/** The alt text for the screenshot image */
|
||||
get imageAlt () {
|
||||
return `Screen shot for ${this.app.name}`
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
<p>
|
||||
<span class="app-name" [innerHtml]="app.name"></span> ~ <a routerLink="/solutions/{{ app.id }}">About</a>
|
||||
<span *ngIf="app.isActive"> ~ <a [href]="app.url">Visit</a></span>
|
||||
<span *ngIf="!app.isActive && app.archiveUrl">
|
||||
~ <a [href]="app.archiveUrl">Visit</a><em> (archive)</em>
|
||||
</span>
|
||||
<br>
|
||||
<span [innerHtml]="app.indexText"></span>
|
||||
</p>
|
|
@ -0,0 +1,5 @@
|
|||
.app-name
|
||||
font-family: "Oswald", "Segoe UI", Ubuntu, "DejaVu Sans", "Liberation Sans", Arial, sans-serif
|
||||
font-size: 1.3rem
|
||||
font-weight: bold
|
||||
color: maroon
|
|
@ -0,0 +1,18 @@
|
|||
import { Component, OnInit, Input } from '@angular/core'
|
||||
|
||||
import { App } from '../application.types'
|
||||
|
||||
@Component({
|
||||
selector: 'app-application-list-item',
|
||||
templateUrl: './application-list-item.component.html',
|
||||
styleUrls: ['./application-list-item.component.sass']
|
||||
})
|
||||
export class ApplicationListItemComponent implements OnInit {
|
||||
|
||||
@Input() app: App
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit() { }
|
||||
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
<app-page-title title="All Solutions"></app-page-title>
|
||||
<article class="content auto">
|
||||
<h1>All Solutions</h1>
|
||||
<h2>Active Solutions</h2>
|
||||
<app-application-list-item *ngFor="let app of current" [app]="app"></app-application-list-item>
|
||||
<h2>Past Solutions</h2>
|
||||
<app-application-list-item *ngFor="let app of past" [app]="app"></app-application-list-item>
|
||||
</article>
|
|
@ -0,0 +1,25 @@
|
|||
import { Component, OnInit } from '@angular/core'
|
||||
|
||||
import { ApplicationService } from '../application.service'
|
||||
import { App } from '../application.types'
|
||||
|
||||
@Component({
|
||||
selector: 'app-application-list',
|
||||
templateUrl: './application-list.component.html'
|
||||
})
|
||||
export class ApplicationListComponent implements OnInit {
|
||||
|
||||
current: App[]
|
||||
|
||||
past: App[]
|
||||
|
||||
constructor(private appService: ApplicationService) { }
|
||||
|
||||
ngOnInit() {
|
||||
this.appService.getApps().subscribe(apps => {
|
||||
this.current = apps.filter(app => app.isActive && !app.noAboutLink)
|
||||
this.past = apps.filter(app => !app.isActive && !app.noAboutLink)
|
||||
})
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
import { Component, OnInit, ViewChild, ComponentFactoryResolver } from '@angular/core'
|
||||
import { ActivatedRoute } from '@angular/router'
|
||||
|
||||
import { ApplicationService } from './application.service'
|
||||
import { App } from './application.types'
|
||||
import { AppItem } from './app-item'
|
||||
import { ApplicationDetailDirective } from './application-detail.directive'
|
||||
import { AppDetailComponent } from './solutions/app-detail.component'
|
||||
|
||||
import { BayVistaComponent } from './solutions/bay-vista.component'
|
||||
import { BitBadgerBlogComponent } from './solutions/bit-badger-blog.component'
|
||||
import { CassyFianoComponent } from './solutions/cassy-fiano.component'
|
||||
import { DrMelissaClouthierComponent } from './solutions/dr-melissa-clouthier.component'
|
||||
import { EmeraldMountainChristianSchoolComponent } from './solutions/emerald-mountain-christian-school.component'
|
||||
import { FutilityClosetComponent } from './solutions/futility-closet.component'
|
||||
import { HardCorpsWifeComponent } from './solutions/hard-corps-wife.component'
|
||||
import { LibertyPunditsComponent } from './solutions/liberty-pundits.component'
|
||||
import { MindyMackenzieComponent } from './solutions/mindy-mackenzie.component'
|
||||
import { MyPrayerJournalComponent } from './solutions/my-prayer-journal.component'
|
||||
import { NsxComponent } from './solutions/nsx.component'
|
||||
import { OlivetBaptistComponent } from './solutions/olivet-baptist.component'
|
||||
import { PhotographyByMichelleComponent } from './solutions/photography-by-michelle.component'
|
||||
import { PrayerTrackerComponent } from './solutions/prayer-tracker.component'
|
||||
import { RiehlWorldNewsComponent } from './solutions/riehl-world-news.component'
|
||||
import { SharkTankComponent } from './solutions/shark-tank.component'
|
||||
import { TcmsComponent } from './solutions/tcms.component'
|
||||
import { VirtualPrayerRoomComponent } from './solutions/virtual-prayer-room.component'
|
||||
|
||||
@Component({
|
||||
selector: 'app-application',
|
||||
template: '<ng-template app-application-detail></ng-template>'
|
||||
})
|
||||
export class ApplicationComponent implements OnInit {
|
||||
|
||||
private apps = [
|
||||
new AppItem('bay-vista', BayVistaComponent),
|
||||
new AppItem('cassy-fiano', CassyFianoComponent),
|
||||
new AppItem('dr-melissa-clouthier', DrMelissaClouthierComponent),
|
||||
new AppItem('emerald-mountain-christian-school', EmeraldMountainChristianSchoolComponent),
|
||||
new AppItem('futility-closet', FutilityClosetComponent),
|
||||
new AppItem('hard-corps-wife', HardCorpsWifeComponent),
|
||||
new AppItem('liberty-pundits', LibertyPunditsComponent),
|
||||
new AppItem('mindy-mackenzie', MindyMackenzieComponent),
|
||||
new AppItem('my-prayer-journal', MyPrayerJournalComponent),
|
||||
new AppItem('nsx', NsxComponent),
|
||||
new AppItem('olivet-baptist', OlivetBaptistComponent),
|
||||
new AppItem('photography-by-michelle', PhotographyByMichelleComponent),
|
||||
new AppItem('prayer-tracker', PrayerTrackerComponent),
|
||||
new AppItem('riehl-world-news', RiehlWorldNewsComponent),
|
||||
new AppItem('the-shark-tank', SharkTankComponent),
|
||||
new AppItem('tcms', TcmsComponent),
|
||||
new AppItem('tech-blog', BitBadgerBlogComponent),
|
||||
new AppItem('virtual-prayer-room', VirtualPrayerRoomComponent)
|
||||
]
|
||||
|
||||
@ViewChild(ApplicationDetailDirective, { static: true }) appDetail: ApplicationDetailDirective
|
||||
|
||||
/** The app we're displaying */
|
||||
application: App
|
||||
|
||||
constructor(
|
||||
private componentFactoryResolver: ComponentFactoryResolver,
|
||||
private appService: ApplicationService,
|
||||
private route: ActivatedRoute
|
||||
) { }
|
||||
|
||||
ngOnInit() {
|
||||
this.route.params.subscribe(params => this.displayApp(params['appId']))
|
||||
}
|
||||
|
||||
/** Dynamically load the app-ropriate component */
|
||||
displayApp(appId: string) {
|
||||
const appComponent = this.apps.find(a => a.name === appId)
|
||||
|
||||
const componentFactory = this.componentFactoryResolver.resolveComponentFactory(appComponent.component)
|
||||
const viewContainerRef = this.appDetail.viewContainerRef
|
||||
viewContainerRef.clear()
|
||||
|
||||
const componentRef = viewContainerRef.createComponent(componentFactory)
|
||||
this.appService.getApp(appId)
|
||||
.subscribe(app => {
|
||||
(<AppDetailComponent>componentRef.instance).app = app
|
||||
this.application = app
|
||||
})
|
||||
}
|
||||
|
||||
}
|
323
bit-badger-solutions/src/app/applications/application.data.ts
Normal file
323
bit-badger-solutions/src/app/applications/application.data.ts
Normal file
|
@ -0,0 +1,323 @@
|
|||
import { App, Category, Quote, Technology } from './application.types'
|
||||
|
||||
/** A Word from the Word */
|
||||
const aWordFromTheWord = new App('a-word-from-the-word', 'A Word from the Word', 'https://devotions.summershome.org')
|
||||
aWordFromTheWord.categoryId = Category.PERSONAL
|
||||
aWordFromTheWord.noAboutLink = true
|
||||
aWordFromTheWord.frontPageText = 'Devotions by Daniel'
|
||||
aWordFromTheWord.frontPageOrder = 2
|
||||
|
||||
/** Bay Vista Baptist Church */
|
||||
const bayVista = new App('bay-vista', 'Bay Vista Baptist Church', 'https://bayvista.org')
|
||||
bayVista.categoryId = Category.STATIC
|
||||
bayVista.frontPageText = 'Biloxi, Mississippi'
|
||||
bayVista.frontPageOrder = 1
|
||||
bayVista.indexText = 'Southern Baptist church in Biloxi, Mississippi'
|
||||
bayVista.techStack = [
|
||||
new Technology('Hugo', 'static site generation', true),
|
||||
new Technology('Azure', 'podcast file storage, automated builds, and static site hosting', true),
|
||||
new Technology('GitHub', 'source code control', true),
|
||||
new Technology('Hexo', 'static site generation'),
|
||||
new Technology('Jekyll', 'static site generation'),
|
||||
new Technology('WordPress', 'content management'),
|
||||
new Technology('MySQL', 'data storage')
|
||||
]
|
||||
|
||||
/** The Bit Badger Blog */
|
||||
const techBlog = new App('tech-blog', 'The Bit Badger Blog', 'https://blog.bitbadger.solutions')
|
||||
techBlog.categoryId = Category.STATIC
|
||||
techBlog.frontPageText = 'Technical information (“geek stuff”) from Bit Badger Solutions'
|
||||
techBlog.frontPageOrder = 3
|
||||
techBlog.indexText = 'Geek stuff from Bit Badger Solutions'
|
||||
techBlog.techStack = [
|
||||
new Technology('Hexo', 'static site generation', true),
|
||||
new Technology('Azure', 'static site hosting', true),
|
||||
new Technology('GitHub', 'source code control', true),
|
||||
new Technology('Custom software', 'content management'),
|
||||
new Technology('WordPress', 'content management'),
|
||||
new Technology('BlogEngine.NET', 'content management'),
|
||||
new Technology('Orchard', 'content management'),
|
||||
new Technology('myWebLog', 'content management'),
|
||||
new Technology('Jekyll', 'static site generation'),
|
||||
new Technology('MySQL', 'data storage'),
|
||||
new Technology('SQL Server', 'data storage'),
|
||||
new Technology('RethinkDB', 'data storage')
|
||||
]
|
||||
|
||||
/** Cassy Fiano */
|
||||
const cassyFiano = new App('cassy-fiano', 'Cassy Fiano', 'http://www.cassyfiano.com')
|
||||
cassyFiano.isActive = false
|
||||
cassyFiano.categoryId = Category.WORDPRESS
|
||||
cassyFiano.indexText = 'A “rising star” conservative blogger'
|
||||
cassyFiano.techStack = [
|
||||
new Technology('WordPress', 'blogging (with a custom theme)'),
|
||||
new Technology('MySQL', 'data storage'),
|
||||
new Technology('Rackspace Cloud', 'backup and recovery'),
|
||||
new Technology('Azure', 'backup and recovery')
|
||||
]
|
||||
|
||||
/** Daniel J. Summers */
|
||||
const danielJSummers = new App('daniel-j-summers', 'Daniel J. Summers', 'https://daniel.summershome.org')
|
||||
danielJSummers.categoryId = Category.PERSONAL
|
||||
danielJSummers.noAboutLink = true
|
||||
danielJSummers.frontPageText = 'Daniel’s personal blog',
|
||||
danielJSummers.frontPageOrder = 1
|
||||
|
||||
/** Dr. Melissa Clouthier */
|
||||
const drMelissaClouthier = new App('dr-melissa-clouthier', 'Dr. Melissa Clouthier', 'http://melissablogs.com')
|
||||
drMelissaClouthier.isActive = false
|
||||
drMelissaClouthier.categoryId = Category.WORDPRESS
|
||||
drMelissaClouthier.frontPageText = 'Information Pollination'
|
||||
drMelissaClouthier.frontPageOrder = 1
|
||||
drMelissaClouthier.indexText = 'Politics, health, podcasts and more'
|
||||
drMelissaClouthier.techStack = [
|
||||
new Technology('WordPress', 'blogging (with a custom theme)'),
|
||||
new Technology('MySQL', 'data storage'),
|
||||
new Technology('Rackspace Cloud', 'backup and recovery'),
|
||||
new Technology('Azure', 'backup and recovery')
|
||||
]
|
||||
|
||||
/** Emerald Mountain Christian School */
|
||||
const emcs = new App('emerald-mountain-christian-school', 'Emerald Mountain Christian School',
|
||||
'http://www.emeraldmountainchristianschool.org')
|
||||
emcs.isActive = false
|
||||
emcs.linkInactive = true
|
||||
emcs.indexText = 'Classical, Christ-centered education near Wetumpka, Alabama'
|
||||
emcs.techStack = [
|
||||
new Technology('PHP', 'page generation and interactivity'),
|
||||
new Technology('ASP.NET MVC', 'page generation and interactivity'),
|
||||
new Technology('PostgreSQL', 'data storage'),
|
||||
new Technology('Rackspace Cloud', 'hosting'),
|
||||
new Technology('Azure', 'hosting')
|
||||
]
|
||||
|
||||
/** Futility Closet */
|
||||
const futilityCloset = new App('futility-closet', 'Futility Closet', 'https://www.futilitycloset.com')
|
||||
futilityCloset.categoryId = Category.WORDPRESS
|
||||
futilityCloset.frontPageText = 'An idler’s miscellany of compendious amusements'
|
||||
futilityCloset.frontPageOrder = 2
|
||||
futilityCloset.indexText = 'An idler’s miscellany of compendious amusements'
|
||||
const fcQuote = new Quote('Greg Ross', 'Futility Closet')
|
||||
fcQuote.full =
|
||||
`Bit Badger Solutions has been an absolute godsend for Futility Closet. We have been with them since 2010, initially
|
||||
setting up and maintaining the site on a Rackspace VPS, and then hosting it completely. Daniel’s never failed
|
||||
in being friendly, knowledgeable, thoughtful, and farsighted. I’ve literally lost count of the number of times
|
||||
he’s saved us from one emergency or another, always with diligence and good humor, or recommended an
|
||||
improvement or a protection that saved us later. We would be out of business many times over if it weren’t for
|
||||
his reliability, expertise, and good judgment. And he’s a joy to work with.`
|
||||
fcQuote.pull = [
|
||||
`Daniel’s never failed in being friendly, knowledgeable, thoughtful, and farsighted…`,
|
||||
`We would be out of business many times over if it weren’t for his reliability, expertise, and good
|
||||
judgment…`
|
||||
]
|
||||
futilityCloset.quotes.push(fcQuote)
|
||||
futilityCloset.techStack = [
|
||||
new Technology('WordPress', 'blogging', true),
|
||||
new Technology('nginx', 'the web server', true),
|
||||
new Technology('MySQL', 'data storage', true),
|
||||
new Technology('Digital Ocean', 'web site hosting', true),
|
||||
new Technology('Azure', 'backup and recovery', true),
|
||||
new Technology('Rackspace Cloud', 'web site hosting')
|
||||
]
|
||||
|
||||
/** Hard Corps Wife */
|
||||
const hardCorpsWife = new App('hard-corps-wife', 'Hard Corps Wife', 'http://www.hardcorpswife.com')
|
||||
hardCorpsWife.isActive = false
|
||||
hardCorpsWife.categoryId = Category.WORDPRESS
|
||||
hardCorpsWife.indexText = 'Cassy’s life as a Marine wife'
|
||||
hardCorpsWife.techStack = [
|
||||
new Technology('WordPress', 'blogging'),
|
||||
new Technology('MySQL', 'data storage'),
|
||||
new Technology('Rackspace Cloud', 'web site hosting')
|
||||
]
|
||||
|
||||
/** Liberty Pundits */
|
||||
const libertyPundits = new App('liberty-pundits', 'Liberty Pundits', 'http://libertypundits.net')
|
||||
libertyPundits.isActive = false
|
||||
libertyPundits.categoryId = Category.WORDPRESS
|
||||
libertyPundits.indexText = 'The home for conservatives'
|
||||
libertyPundits.techStack = [
|
||||
new Technology('WordPress', 'blogging'),
|
||||
new Technology('PHP', 'custom data migration software'),
|
||||
new Technology('MySQL', 'data storage')
|
||||
]
|
||||
|
||||
/** Linux Resources */
|
||||
const linuxResources = new App('linux', 'Linux Resources', 'https://blog.bitbadger.solutions/linux/')
|
||||
linuxResources.noAboutLink = true
|
||||
linuxResources.frontPageText = 'Handy information for Linux folks'
|
||||
linuxResources.frontPageOrder = 3
|
||||
|
||||
/** Mindy Mackenzie */
|
||||
const mindyMackenzie = new App('mindy-mackenzie', 'Mindy Mackenzie', 'https://mindymackenzie.com')
|
||||
mindyMackenzie.categoryId = Category.WORDPRESS
|
||||
mindyMackenzie.frontPageText = 'WSJ-best-selling author of The Courage Solution'
|
||||
mindyMackenzie.frontPageOrder = 3
|
||||
mindyMackenzie.indexText = '<em>Wall Street Journal</em> best-selling author and C-suite advisor'
|
||||
const mmQuote = new Quote('Mindy Mackenzie', '')
|
||||
mmQuote.full =
|
||||
`Daniel is the best partner you could hope for in a web designer and for handling web maintenance! He is smart,
|
||||
creative, resourceful and fast. Daniel is able to produce high quality work on short time frames and with minimal
|
||||
creative direction and hit the mark over and over. The best part, is Daniel is a joy to work with. He is smart,
|
||||
customer-centric and trustworthy. If he says he will get it done, he does. After having a poor experience with
|
||||
another firm, I can highly recommend Daniel for all your website design and support needs – he’s terrific!`
|
||||
mmQuote.pull = [
|
||||
'…Daniel is able to produce high quality work on short time frames…',
|
||||
'[Daniel] is smart, customer-centric and trustworthy.'
|
||||
]
|
||||
mindyMackenzie.quotes.push(mmQuote)
|
||||
mindyMackenzie.techStack = [
|
||||
new Technology('WordPress', 'blogging', true),
|
||||
new Technology('nginx', 'the web server', true),
|
||||
new Technology('MySQL', 'data storage', true),
|
||||
new Technology('Digital Ocean', 'web site hosting', true),
|
||||
new Technology('Azure', 'backup and recovery', true),
|
||||
]
|
||||
|
||||
/** myPrayerJournal */
|
||||
const myPrayerJournal = new App('my-prayer-journal', 'myPrayerJournal', 'https://prayerjournal.me')
|
||||
myPrayerJournal.frontPageText = 'Minimalist personal prayer journal'
|
||||
myPrayerJournal.frontPageOrder = 2
|
||||
myPrayerJournal.indexText = 'Minimalist personal prayer journal'
|
||||
myPrayerJournal.techStack = [
|
||||
new Technology('Vue.js', 'the front-end', true),
|
||||
new Technology('Giraffe', 'the back-end data API', true),
|
||||
new Technology('RavenDB', 'data storage', true),
|
||||
new Technology('GitHub', 'source code control', true),
|
||||
new Technology('GitHub Pages', 'documentation', true),
|
||||
new Technology('PostgreSQL', 'data storage')
|
||||
]
|
||||
|
||||
/** Not So Extreme Makeover: Community Edition */
|
||||
const nsx = new App('nsx', 'Not So Extreme Makeover: Community Edition', 'http://notsoextreme.org')
|
||||
nsx.isActive = false
|
||||
nsx.archiveUrl = 'https://nsx.archive.bitbadger.solutions'
|
||||
nsx.indexText =
|
||||
'Public site for the makeover; provides event-driven management of volunteers, donations, and families needing help'
|
||||
nsx.techStack = [
|
||||
new Technology('WordPress', 'content management'),
|
||||
new Technology('PHP', 'NSXapp'),
|
||||
new Technology('MySQL', 'WordPress data storage'),
|
||||
new Technology('PostgreSQL', 'NSXapp data storage')
|
||||
]
|
||||
|
||||
/** Olivet Baptist Church */
|
||||
const olivet = new App('olivet-baptist', 'Olivet Baptist Church', 'https://olivet-baptist.org')
|
||||
olivet.isActive = false
|
||||
olivet.archiveUrl = 'https://olivet.archive.bitbadger.solutions'
|
||||
olivet.categoryId = Category.STATIC
|
||||
olivet.indexText = 'Southern Baptist church in Gulfport, Mississippi'
|
||||
olivet.techStack = [
|
||||
new Technology('Vue.js', 'the user interface for the PWA'),
|
||||
new Technology('Hexo', `generating the site's pages`),
|
||||
new Technology('Azure', 'podcast file storage and archive site hosting'),
|
||||
new Technology('WordPress', 'content management'),
|
||||
new Technology('MySQL', 'data storage')
|
||||
]
|
||||
|
||||
/** Photography by Michelle */
|
||||
const photographyByMichelle = new App('photography-by-michelle', 'Photography by Michelle',
|
||||
'https://www.summershome.org')
|
||||
photographyByMichelle.isActive = false
|
||||
photographyByMichelle.linkInactive = true
|
||||
photographyByMichelle.indexText = 'Photography services in Albuquerque, New Mexico'
|
||||
photographyByMichelle.techStack = [
|
||||
new Technology('ASP.NET MVC', 'content management / gallery creation API'),
|
||||
new Technology('PostgreSQL', 'data storage'),
|
||||
new Technology('C# / Windows Forms', 'desktop gallery application'),
|
||||
new Technology('WordPress', 'content management'),
|
||||
new Technology('MySQL', 'data storage')
|
||||
]
|
||||
|
||||
/** PrayerTracker */
|
||||
const prayerTracker = new App('prayer-tracker', 'PrayerTracker', 'https://prayer.bitbadger.solutions')
|
||||
prayerTracker.frontPageText = 'A prayer request tracking website (Free for any church or Sunday School class!)'
|
||||
prayerTracker.frontPageOrder = 1
|
||||
prayerTracker.indexText = 'Provides an ongoing, centralized prayer list for Sunday School classes and other groups'
|
||||
prayerTracker.techStack = [
|
||||
new Technology('Giraffe', 'server-side logic and dynamic page generation', true),
|
||||
new Technology('PostgreSQL', 'data storage', true),
|
||||
new Technology('GitHub', 'source code control', true),
|
||||
new Technology('GitHub Pages', 'documentation hosting', true),
|
||||
new Technology('MongoDB', 'data storage'),
|
||||
new Technology('ASP.NET MVC', 'dynamic content generation'),
|
||||
new Technology('Database Abstraction', 'data access'),
|
||||
new Technology('MySQL', 'data storage'),
|
||||
new Technology('PHP', 'dynamic content generation')
|
||||
]
|
||||
|
||||
/** Riehl World News */
|
||||
const riehlWorldNews = new App('riehl-world-news', 'Riehl World News', 'http://riehlworldview.com')
|
||||
riehlWorldNews.categoryId = Category.WORDPRESS
|
||||
riehlWorldNews.frontPageText = 'Riehl news for real people'
|
||||
riehlWorldNews.frontPageOrder = 4
|
||||
riehlWorldNews.indexText = 'Riehl news for real people'
|
||||
riehlWorldNews.techStack = [
|
||||
new Technology('WordPress', 'blogging', true),
|
||||
new Technology('MySQL', 'data storage', true),
|
||||
new Technology('F#', 'custom archive static page generation')
|
||||
]
|
||||
|
||||
/** The Shark Tank */
|
||||
const theSharkTank = new App('the-shark-tank', 'The Shark Tank', 'http://shark-tank.net')
|
||||
theSharkTank.isActive = false
|
||||
theSharkTank.categoryId = Category.WORDPRESS
|
||||
theSharkTank.indexText = 'Florida’s political feeding frenzy'
|
||||
theSharkTank.techStack = [ new Technology('WordPress', 'blogging') ]
|
||||
|
||||
/** The Clearinghouse Management System */
|
||||
var tcms = new App('tcms', 'The Clearinghouse Management System', 'http://tcms.us')
|
||||
tcms.isActive = false
|
||||
tcms.indexText =
|
||||
'Assists a needs clearinghouse in connecting people with needs to people that can help meet those needs'
|
||||
tcms.techStack = [
|
||||
new Technology('PHP', 'the TCMS application logic'),
|
||||
new Technology('WordPress', 'publicly-facing pages and authentication'),
|
||||
new Technology('PostgreSQL', 'application data storage'),
|
||||
new Technology('MySQL', 'WordPress data storage')
|
||||
]
|
||||
|
||||
/** Virtual Prayer Room */
|
||||
const vpr = new App('virtual-prayer-room', 'Virtual Prayer Room', 'https://virtualprayerroom.us')
|
||||
vpr.isActive = false
|
||||
vpr.indexText = 'Gives prayer warriors access to requests from wherever they may be, and sends them daily updates'
|
||||
vpr.techStack = [
|
||||
new Technology('PHP', 'the application logic'),
|
||||
new Technology('PostgreSQL', 'data storage')
|
||||
]
|
||||
|
||||
export default {
|
||||
|
||||
/** All categories */
|
||||
categories: [
|
||||
new Category(Category.SITES_APPS, 'Web Sites and Applications'),
|
||||
new Category(Category.WORDPRESS, 'WordPress'),
|
||||
new Category(Category.STATIC, 'Static Sites'),
|
||||
new Category(Category.PERSONAL, 'Personal')
|
||||
],
|
||||
|
||||
/** All apps */
|
||||
apps: [
|
||||
aWordFromTheWord,
|
||||
bayVista,
|
||||
cassyFiano,
|
||||
danielJSummers,
|
||||
drMelissaClouthier,
|
||||
emcs,
|
||||
futilityCloset,
|
||||
hardCorpsWife,
|
||||
libertyPundits,
|
||||
linuxResources,
|
||||
mindyMackenzie,
|
||||
myPrayerJournal,
|
||||
nsx,
|
||||
olivet,
|
||||
photographyByMichelle,
|
||||
prayerTracker,
|
||||
riehlWorldNews,
|
||||
tcms,
|
||||
techBlog,
|
||||
theSharkTank,
|
||||
vpr
|
||||
]
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
import { Injectable } from '@angular/core'
|
||||
import { Observable, of } from 'rxjs'
|
||||
|
||||
import Data from './application.data'
|
||||
import { Category, App } from './application.types'
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class ApplicationService {
|
||||
|
||||
constructor() { }
|
||||
|
||||
/**
|
||||
* Get all categories of apps
|
||||
*/
|
||||
getCategories(): Observable<Category[]> {
|
||||
return of(Data.categories)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all apps
|
||||
*/
|
||||
getApps(): Observable<App[]> {
|
||||
return of(Data.apps)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all applications for the given category ID
|
||||
* @param categoryId The ID of the category for which apps should be retrieved
|
||||
*/
|
||||
getAppsForCategory(categoryId: number): Observable<App[]> {
|
||||
return of(
|
||||
Data.apps
|
||||
.filter(app => app.categoryId === categoryId)
|
||||
.sort((a, b) => a.frontPageOrder - b.frontPageOrder)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific app
|
||||
* @param appId The ID of the app to retrieve
|
||||
*/
|
||||
getApp(appId: string): Observable<App> {
|
||||
return of(Data.apps.find(app => app.id === appId))
|
||||
}
|
||||
|
||||
}
|
|
@ -1,16 +1,3 @@
|
|||
'use strict'
|
||||
|
||||
/** An activity performed for a customer */
|
||||
export class Activity {
|
||||
|
||||
/**
|
||||
* Construct a new instance
|
||||
* @param heading The heading of the activity
|
||||
* @param narrative The description of the activity
|
||||
*/
|
||||
constructor(public heading: string, public narrative: string) { }
|
||||
}
|
||||
|
||||
/** A category of application */
|
||||
export class Category {
|
||||
|
||||
|
@ -61,8 +48,9 @@ export class Technology {
|
|||
* Construct a new instace
|
||||
* @param name The name of the technology
|
||||
* @param usedFor What aspect was addressed by this technology
|
||||
* @param current Whether this technology is currently in use in the solution
|
||||
*/
|
||||
constructor(public name: string, public usedFor: string) { }
|
||||
constructor(public name: string, public usedFor: string, public current: boolean = false) { }
|
||||
}
|
||||
|
||||
/** An application or web site */
|
||||
|
@ -92,15 +80,6 @@ export class App {
|
|||
/** The URL where an archived version of this app may be found */
|
||||
archiveUrl: string = ''
|
||||
|
||||
/** Paragraphs of text that describe the app */
|
||||
paragraphs: string[] = []
|
||||
|
||||
/** Footnotes for the long description */
|
||||
footnotes: string[] = []
|
||||
|
||||
/** Discrete activities performed for this app */
|
||||
activities: Activity[] = []
|
||||
|
||||
/** The technology used for this app */
|
||||
techStack: Technology[] = []
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
import { NgModule } from '@angular/core'
|
||||
import { CommonModule } from '@angular/common'
|
||||
import { RouterModule } from '@angular/router'
|
||||
|
||||
import { AllSolutionsLinkComponent } from './all-solutions-link.component'
|
||||
import { ApplicationComponent } from './application.component'
|
||||
import { ApplicationDetailDirective } from './application-detail.directive'
|
||||
import { ApplicationHeaderComponent } from './application-header/application-header.component'
|
||||
import { ApplicationImageComponent } from './application-image/application-image.component'
|
||||
import { ApplicationListComponent } from './application-list/application-list.component'
|
||||
import { ApplicationListItemComponent } from './application-list-item/application-list-item.component'
|
||||
import { HideSectionComponent } from './hide-section/hide-section.component'
|
||||
import { QuotesComponent } from './quotes/quotes.component';
|
||||
import { SharedModule } from '../shared/shared.module'
|
||||
import { TechnologyComponent } from './technology/technology.component';
|
||||
import { TechStackComponent } from './tech-stack/tech-stack.component';
|
||||
|
||||
import { BayVistaComponent } from './solutions/bay-vista.component'
|
||||
import { BitBadgerBlogComponent } from './solutions/bit-badger-blog.component';
|
||||
import { CassyFianoComponent } from './solutions/cassy-fiano.component'
|
||||
import { DrMelissaClouthierComponent } from './solutions/dr-melissa-clouthier.component';
|
||||
import { EmeraldMountainChristianSchoolComponent } from './solutions/emerald-mountain-christian-school.component';
|
||||
import { FutilityClosetComponent } from './solutions/futility-closet.component';
|
||||
import { HardCorpsWifeComponent } from './solutions/hard-corps-wife.component';
|
||||
import { LibertyPunditsComponent } from './solutions/liberty-pundits.component';
|
||||
import { MindyMackenzieComponent } from './solutions/mindy-mackenzie.component';
|
||||
import { MyPrayerJournalComponent } from './solutions/my-prayer-journal.component';
|
||||
import { NsxComponent } from './solutions/nsx.component';
|
||||
import { OlivetBaptistComponent } from './solutions/olivet-baptist.component';
|
||||
import { PhotographyByMichelleComponent } from './solutions/photography-by-michelle.component';
|
||||
import { PrayerTrackerComponent } from './solutions/prayer-tracker.component'
|
||||
import { RiehlWorldNewsComponent } from './solutions/riehl-world-news.component';
|
||||
import { SharkTankComponent } from './solutions/shark-tank.component';
|
||||
import { TcmsComponent } from './solutions/tcms.component';
|
||||
import { VirtualPrayerRoomComponent } from './solutions/virtual-prayer-room.component'
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AllSolutionsLinkComponent,
|
||||
ApplicationComponent,
|
||||
ApplicationDetailDirective,
|
||||
ApplicationHeaderComponent,
|
||||
ApplicationImageComponent,
|
||||
ApplicationListComponent,
|
||||
ApplicationListItemComponent,
|
||||
BayVistaComponent,
|
||||
BitBadgerBlogComponent,
|
||||
CassyFianoComponent,
|
||||
DrMelissaClouthierComponent,
|
||||
EmeraldMountainChristianSchoolComponent,
|
||||
FutilityClosetComponent,
|
||||
HardCorpsWifeComponent,
|
||||
HideSectionComponent,
|
||||
LibertyPunditsComponent,
|
||||
MindyMackenzieComponent,
|
||||
MyPrayerJournalComponent,
|
||||
NsxComponent,
|
||||
OlivetBaptistComponent,
|
||||
PhotographyByMichelleComponent,
|
||||
PrayerTrackerComponent,
|
||||
QuotesComponent,
|
||||
RiehlWorldNewsComponent,
|
||||
SharkTankComponent,
|
||||
TcmsComponent,
|
||||
TechnologyComponent,
|
||||
TechStackComponent,
|
||||
VirtualPrayerRoomComponent
|
||||
],
|
||||
entryComponents: [
|
||||
BayVistaComponent,
|
||||
BitBadgerBlogComponent,
|
||||
CassyFianoComponent,
|
||||
DrMelissaClouthierComponent,
|
||||
EmeraldMountainChristianSchoolComponent,
|
||||
FutilityClosetComponent,
|
||||
HardCorpsWifeComponent,
|
||||
LibertyPunditsComponent,
|
||||
MindyMackenzieComponent,
|
||||
MyPrayerJournalComponent,
|
||||
NsxComponent,
|
||||
OlivetBaptistComponent,
|
||||
PhotographyByMichelleComponent,
|
||||
PrayerTrackerComponent,
|
||||
RiehlWorldNewsComponent,
|
||||
SharkTankComponent,
|
||||
TcmsComponent,
|
||||
VirtualPrayerRoomComponent
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
RouterModule,
|
||||
SharedModule
|
||||
],
|
||||
exports: [
|
||||
ApplicationComponent,
|
||||
ApplicationListComponent
|
||||
]
|
||||
})
|
||||
export class ApplicationsModule { }
|
|
@ -0,0 +1,4 @@
|
|||
<h3 (click)="toggle()">{{ heading }}<span class="arrow" [innerHtml]="label"></span></h3>
|
||||
<div *ngIf="shown" [@slideInOut]>
|
||||
<ng-content></ng-content>
|
||||
</div>
|
|
@ -0,0 +1,6 @@
|
|||
h3:hover
|
||||
cursor: hand
|
||||
cursor: pointer
|
||||
.arrow
|
||||
font-size: .75rem
|
||||
padding-left: 1rem
|
|
@ -0,0 +1,38 @@
|
|||
import { Component, OnInit, Input } from '@angular/core';
|
||||
import { trigger, transition, style, animate, group } from '@angular/animations';
|
||||
|
||||
@Component({
|
||||
selector: 'app-hide-section',
|
||||
templateUrl: './hide-section.component.html',
|
||||
styleUrls: ['./hide-section.component.sass'],
|
||||
animations: [
|
||||
trigger('slideInOut', [
|
||||
transition(':enter', [
|
||||
style({ opacity: 0 }),
|
||||
animate('500ms ease-in', style({ opacity: 1 }))
|
||||
]),
|
||||
transition(':leave', [
|
||||
style({opacity: 1}),
|
||||
animate('500ms ease-in', style({ opacity: 0 }))
|
||||
])
|
||||
])
|
||||
]
|
||||
})
|
||||
export class HideSectionComponent implements OnInit {
|
||||
|
||||
@Input() heading: string
|
||||
|
||||
label = '▼'
|
||||
|
||||
shown = false
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit() { }
|
||||
|
||||
toggle() {
|
||||
this.shown = !this.shown
|
||||
this.label = this.shown ? '▲' : '▼'
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
<div *ngIf="(quotes || []).length > 0">
|
||||
<h3>The Business Impact</h3>
|
||||
<blockquote *ngFor="let quote of quotes">
|
||||
<p class="quote" [innerHtml]="quote.full"></p>
|
||||
<p class="source">
|
||||
— <strong>{{ quote.name }}</strong>
|
||||
<span *ngIf="quote.from">, {{ quote.from }}</span>
|
||||
</p>
|
||||
</blockquote>
|
||||
</div>
|
|
@ -0,0 +1,9 @@
|
|||
blockquote
|
||||
border-left: solid 1px darkgray
|
||||
margin-left: 25px
|
||||
padding-left: 15px
|
||||
.quote
|
||||
font-style: italic
|
||||
.source
|
||||
text-align: right
|
||||
padding-right: 60px
|
|
@ -0,0 +1,18 @@
|
|||
import { Component, OnInit, Input } from '@angular/core'
|
||||
|
||||
import { Quote } from '../application.types'
|
||||
|
||||
@Component({
|
||||
selector: 'app-quotes',
|
||||
templateUrl: './quotes.component.html',
|
||||
styleUrls: ['./quotes.component.sass']
|
||||
})
|
||||
export class QuotesComponent implements OnInit {
|
||||
|
||||
@Input() quotes: Quote[]
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit() { }
|
||||
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
import { App } from '../application.types'
|
||||
|
||||
/** An inteface implemented by all app detail components */
|
||||
export class AppDetailComponent {
|
||||
|
||||
/** The app to be displayed */
|
||||
app: App
|
||||
|
||||
/** The page title based on this app */
|
||||
get pageTitle () {
|
||||
return `${this.app.name} « Solutions`
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
<app-page-title [title]="pageTitle"></app-page-title>
|
||||
<app-application-header [app]="app"></app-application-header>
|
||||
<div class="app-info">
|
||||
<article class="content">
|
||||
<app-application-image [app]="app"></app-application-image>
|
||||
<h3>The Client</h3>
|
||||
<p>
|
||||
Bay Vista Baptist Church has served the spiritual needs of Mississippi’s Gulf Coast for decades. They
|
||||
emphasize serving their community as well; they were a hub for
|
||||
<abbr title="Federal Emergency Management Agency">FEMA</abbr> during Hurricane Katrina relief and recovery
|
||||
efforts, and they are a relay point for each year’s
|
||||
<a href="https://www.samaritanspurse.org/what-we-do/operation-christmas-child/">Operation Christmas Child</a>
|
||||
campaign.
|
||||
</p>
|
||||
<h3>The Problem</h3>
|
||||
<p>
|
||||
In late 2013, the authors of their current website were no longer around, and no one could get to the site to
|
||||
update it.
|
||||
</p>
|
||||
<h3>The Solution</h3>
|
||||
<p>
|
||||
We developed and continue to maintain a fast, static website that can be updated by multiple trained church
|
||||
members. The site also has a repository for their sermons dating back to January 2014, and a podcast feed that
|
||||
gives their ministry a global reach.
|
||||
</p>
|
||||
<app-hide-section heading="The Process">
|
||||
<p>
|
||||
Initially, we set up a WordPress-based site, where multiple people could have the ability to maintain the site.
|
||||
We manually downloaded all the publically-accessible parts of their old site, and used that content to form the
|
||||
basis for the new side, updating outdated information along the way. We maintained the same look-and-feel, but
|
||||
soon moved to a more mobile-friendly layout.
|
||||
</p>
|
||||
<p>
|
||||
In 2016, we determined that we were the only ones updating the site, so we transformed the site to use a static
|
||||
site generator; this resulted in fast page loads, with automation providing scheduled updates. We also wrote a
|
||||
custom template for the podcast feed, which is also generated as a static file.
|
||||
</p>
|
||||
<p>
|
||||
In 2019, we <a href="https://github.com/bayvistabc/www.bayvista.org">open sourced</a> the site's source code.
|
||||
We also set up Azure Pipelines to automatically build and deploy the site both on demand and on a schedule.
|
||||
Finally, we trained other church members on updating the site's contents and the podcast feed.
|
||||
</p>
|
||||
</app-hide-section>
|
||||
<app-tech-stack [stack]="app.techStack"></app-tech-stack>
|
||||
<app-all-solutions-link></app-all-solutions-link>
|
||||
</article>
|
||||
</div>
|
|
@ -0,0 +1,18 @@
|
|||
import { Component, Input } from '@angular/core'
|
||||
|
||||
import { AppDetailComponent } from './app-detail.component'
|
||||
import { App } from '../application.types'
|
||||
|
||||
@Component({
|
||||
selector: 'app-bay-vista',
|
||||
templateUrl: './bay-vista.component.html'
|
||||
})
|
||||
export class BayVistaComponent extends AppDetailComponent {
|
||||
|
||||
@Input() app: App
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
<app-page-title [title]="pageTitle"></app-page-title>
|
||||
<app-application-header [app]="app"></app-application-header>
|
||||
<div class="app-info">
|
||||
<article class="content">
|
||||
<app-application-image [app]="app"></app-application-image>
|
||||
<h3>The Problem</h3>
|
||||
<p>
|
||||
Daniel needed a place to journal his learning journey with the Linux operating system, and thought that allowing
|
||||
others read this journal would help them learn as well.
|
||||
</p>
|
||||
<h3>The Solution</h3>
|
||||
<p>
|
||||
<em>The Bit Badger Blog</em> contains that journal, plus tech tips and information for many different aspects of
|
||||
technology. It is written, maintained, and hosted by Bit Badger Solutions.
|
||||
</p>
|
||||
<app-hide-section heading="The Process">
|
||||
<p>
|
||||
The initial posts were titled “My Linux Adventure,” and existed as static files that were edited to
|
||||
add each post. Daniel then wrote a rudimentary system that stored the posts in a database, which meant that the
|
||||
entire site did not need manual changes – what a breakthrough! :)
|
||||
</p>
|
||||
<p>
|
||||
Over time, the <em>Bit Badger Blog</em> (and the <em>DJS Consulting Tech Blog</em> before it) has served as a
|
||||
place to support <em>(now inactive)</em> WordPress plug-ins, and go in depth on servers, databases, programming
|
||||
languages, and open-source software. It has also served as a useful live website for learning and
|
||||
experimentation with different content management systems and blogging tools. It has existed in at least 8
|
||||
different tools, with links preserved as systems change.
|
||||
</p>
|
||||
<p>
|
||||
It is currently a statically-generated site, utilizing <a href="https://hexo.io">Hexo</a>, and its code is
|
||||
<a href="https://github.com/bit-badger/blog.bitbadger.solutions">open source</a>. New posts are infrequent,
|
||||
but the information it has is good. It may have more behind-the-scenes posts about future open-source efforts.
|
||||
Stay tuned!
|
||||
</p>
|
||||
</app-hide-section>
|
||||
<app-tech-stack [stack]="app.techStack"></app-tech-stack>
|
||||
<app-all-solutions-link></app-all-solutions-link>
|
||||
</article>
|
||||
</div>
|
|
@ -0,0 +1,18 @@
|
|||
import { Component, Input } from '@angular/core'
|
||||
|
||||
import { AppDetailComponent } from './app-detail.component'
|
||||
import { App } from '../application.types'
|
||||
|
||||
@Component({
|
||||
selector: 'app-bit-badger-blog',
|
||||
templateUrl: './bit-badger-blog.component.html'
|
||||
})
|
||||
export class BitBadgerBlogComponent extends AppDetailComponent {
|
||||
|
||||
@Input() app: App
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
<app-page-title [title]="pageTitle"></app-page-title>
|
||||
<app-application-header [app]="app"></app-application-header>
|
||||
<div class="app-info">
|
||||
<article class="content">
|
||||
<app-application-image [app]="app"></app-application-image>
|
||||
<h3>The Client</h3>
|
||||
<p>
|
||||
Cassy Fiano (now Cassy Chesser) began blogging back in 2007 on Blogger. She worked hard to network with other
|
||||
bloggers, wrote prolifically, and gained a large audience with her coverage of life issues and of Sarah Palin as
|
||||
the first female Republican vice-presidential nominee.
|
||||
</p>
|
||||
<h3>The Problem</h3>
|
||||
<p>
|
||||
With her success, Cassy was quickly outgrowing Blogger. She was interested in moving to a different platform;
|
||||
specifically, Movable Type, as she had some authoring experience with that platform.
|
||||
</p>
|
||||
<h3>The Solution</h3>
|
||||
<p>
|
||||
We migrated her content to a WordPress site, and customized a theme to look very similar to her Blogger theme
|
||||
(which she liked). We maintained the site, and began hosting it a few years later.
|
||||
</p>
|
||||
<h3>The Epilogue</h3>
|
||||
<p>Cassy formally decommissioned this site in early 2014.</p>
|
||||
<app-hide-section heading="The Process">
|
||||
<p>
|
||||
Initially, we assisted her with finding a theme, and customized it. We also modified her old Blogger template
|
||||
to send redirect users to her new blog after displaying a note that the blog had moved. A few years later, we
|
||||
developed an advertising banner to generate income from her writing.
|
||||
</p>
|
||||
<p>
|
||||
In July 2012, we began hosting the site, as we were already hosting her military wife blog
|
||||
<a routerLink="/solutions/hard-corps-wife" title="Hard Corps Wife | Bit Badger Solutions">Hard Corps Wife</a>.
|
||||
When the time came to decommission the site, we backed up the data and ensured she had it.
|
||||
</p>
|
||||
</app-hide-section>
|
||||
<app-tech-stack [stack]="app.techStack"></app-tech-stack>
|
||||
<app-all-solutions-link></app-all-solutions-link>
|
||||
</article>
|
||||
</div>
|
|
@ -0,0 +1,18 @@
|
|||
import { Component, Input } from '@angular/core'
|
||||
|
||||
import { AppDetailComponent } from './app-detail.component'
|
||||
import { App } from '../application.types'
|
||||
|
||||
@Component({
|
||||
selector: 'app-cassy-fiano',
|
||||
templateUrl: './cassy-fiano.component.html'
|
||||
})
|
||||
export class CassyFianoComponent extends AppDetailComponent {
|
||||
|
||||
@Input() app: App
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
<app-page-title [title]="pageTitle"></app-page-title>
|
||||
<app-application-header [app]="app"></app-application-header>
|
||||
<div class="app-info">
|
||||
<article class="content">
|
||||
<app-application-image [app]="app"></app-application-image>
|
||||
<h3>The Client</h3>
|
||||
<p>
|
||||
Dr. Melissa Clouthier (now Mackenzie) blogged from the political right; she also covered health issues and social
|
||||
media techniques and utilization.
|
||||
</p>
|
||||
<h3>The Problem</h3>
|
||||
<p>
|
||||
She had seen our work with <a routerLink="/solutions/cassy-fiano"
|
||||
title="Cassy Fiano | Bit Badger Solutions">Cassy</a>’s site, also wanted to move off Blogger; however, she
|
||||
did not want to lose her years of posts up to that point.
|
||||
</p>
|
||||
<h3>The Solution</h3>
|
||||
<p>
|
||||
We created a custom theme for her site, imported the content into a WordPress site, and created a specialized
|
||||
front-page template. She obtained hosting elsewhere; Bit Badger Solutions maintained it there.
|
||||
</p>
|
||||
<p>
|
||||
<small><em>(NOTE: The thumbnail of the site represents a new skin on the original theme; while the theme is the
|
||||
same, Bit Badger Solutions did not create the graphics.)</em></small>
|
||||
</p>
|
||||
<h3>The Epilogue</h3>
|
||||
<p>Melissa decommissioned this site in 2018; we took final snapshots of the data before shutting it down.</p>
|
||||
<app-hide-section heading="The Process">
|
||||
<p>
|
||||
Initially, we created the theme based off another well-known blogger's site, which had been developed by one of
|
||||
WordPress's core contributors. We also advised on the type of hosting she would need for her site, and moved
|
||||
seveal domains there. We also took care of regular backups of her data.
|
||||
</p>
|
||||
</app-hide-section>
|
||||
<app-tech-stack [stack]="app.techStack"></app-tech-stack>
|
||||
<app-all-solutions-link></app-all-solutions-link>
|
||||
</article>
|
||||
</div>
|
|
@ -0,0 +1,18 @@
|
|||
import { Component, Input } from '@angular/core'
|
||||
|
||||
import { AppDetailComponent } from './app-detail.component'
|
||||
import { App } from '../application.types'
|
||||
|
||||
@Component({
|
||||
selector: 'app-dr-melissa-clouthier',
|
||||
templateUrl: './dr-melissa-clouthier.component.html'
|
||||
})
|
||||
export class DrMelissaClouthierComponent extends AppDetailComponent {
|
||||
|
||||
@Input() app: App
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
<app-page-title [title]="pageTitle"></app-page-title>
|
||||
<app-application-header [app]="app"></app-application-header>
|
||||
<div class="app-info">
|
||||
<article class="content">
|
||||
<app-application-image [app]="app"></app-application-image>
|
||||
<h3>The Client</h3>
|
||||
<p>
|
||||
Emerald Mountain Christian School is a private Christian school founded over 50 years ago. They use the Principle
|
||||
Approach®, which emphasizes research, reasoning, relating, and recording to help students synthesize the
|
||||
information they learn, rather than just requiring rote memorization. More information about the school’s
|
||||
rich history can be found on their site.
|
||||
</p>
|
||||
<h3>The Problem</h3>
|
||||
<p>
|
||||
They had a website with very basic information and very little styling; they also had no way of updating it.
|
||||
</p>
|
||||
<h3>The Solution</h3>
|
||||
<p>
|
||||
In 2004, we developed a theme that brought it in line with the design of their printed materials, adding the
|
||||
school calendar of events and the entirety of their Parent Information Packet, giving prospective families the
|
||||
information the needed to determine if the school was a good fit for their students.
|
||||
</p>
|
||||
<h3>The Epilogue</h3>
|
||||
<p>
|
||||
In 2013, we passed off the content and hosting of the site to a new maintainer. They have since redesigned it;
|
||||
it is accessible via the URL above, and at <a href="http://emcspatriots.org"
|
||||
title="EMCS Patriots">EMCSpatriots.org</a>.
|
||||
</p>
|
||||
<app-hide-section heading="The Process">
|
||||
<p>
|
||||
Initially, we downloaded the content from their old site, and put it into a custom PHP-based framework. We
|
||||
then added a database of events, and a calendar page that read that database, enabling us to display multiple
|
||||
years, as well as future and past years. The design of the online information packet looked like a tabbed
|
||||
notebook, with each page highlighting a different tab.
|
||||
</p>
|
||||
<p>
|
||||
In 2011, we switched the site to use ASP.NET MVC instead of the custom PHP solution, and migrated the data from
|
||||
MySQL to PostgreSQL; these efforts increased the performance of the site.
|
||||
</p>
|
||||
</app-hide-section>
|
||||
<app-tech-stack [stack]="app.techStack"></app-tech-stack>
|
||||
<app-all-solutions-link></app-all-solutions-link>
|
||||
</article>
|
||||
</div>
|
|
@ -0,0 +1,18 @@
|
|||
import { Component, Input } from '@angular/core'
|
||||
|
||||
import { AppDetailComponent } from './app-detail.component'
|
||||
import { App } from '../application.types'
|
||||
|
||||
@Component({
|
||||
selector: 'app-emerald-mountain-christian-school',
|
||||
templateUrl: './emerald-mountain-christian-school.component.html'
|
||||
})
|
||||
export class EmeraldMountainChristianSchoolComponent extends AppDetailComponent {
|
||||
|
||||
@Input() app: App
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
<app-page-title [title]="pageTitle"></app-page-title>
|
||||
<app-application-header [app]="app"></app-application-header>
|
||||
<div class="app-info">
|
||||
<article class="content">
|
||||
<app-application-image [app]="app"></app-application-image>
|
||||
<h3>The Client</h3>
|
||||
<p>
|
||||
Futility Closet exists as a place to give people a break from the dullness of work, by providing puzzles,
|
||||
anecdotes, and more. They also publish a weekly podcast highlighting “forgotten stories from the pages of
|
||||
history,” along with story updates and lateral thinking puzzles.
|
||||
</p>
|
||||
<h3>The Problem</h3>
|
||||
<p>
|
||||
The site was running on a shared host, but was growing too large for that platform. The site had also suffered
|
||||
regular security breaches.
|
||||
</p>
|
||||
<h3>The Solution</h3>
|
||||
<p>
|
||||
We architected an environment that would support a Reddit or Slashdot deluge of requests, and moved the site to
|
||||
an implementation of that environment. We continue to maintain that environment and back up data and files for
|
||||
the over 10,000 posts.
|
||||
</p>
|
||||
<app-quotes [quotes]="app.quotes"></app-quotes>
|
||||
<app-hide-section heading="The Process">
|
||||
<p>
|
||||
In mid-2010, we obtained a backup of the previous site, and looked through it to ensure that none of the
|
||||
breaches had made any permanent changes to the site's structure and data. We also locked down the new server
|
||||
(hosted on Rackspace Cloud) to only required protocols, training the client on SSH so that they could have
|
||||
access. We also stood up nginx as the front-end server, boosting performance significantly while requiring a
|
||||
much smaller server.
|
||||
</p>
|
||||
<p>
|
||||
In 2015, we began hosting Futility Closet (using Digital Ocean).
|
||||
</p>
|
||||
</app-hide-section>
|
||||
<app-tech-stack [stack]="app.techStack"></app-tech-stack>
|
||||
<app-all-solutions-link></app-all-solutions-link>
|
||||
</article>
|
||||
</div>
|
|
@ -0,0 +1,18 @@
|
|||
import { Component, Input } from '@angular/core'
|
||||
|
||||
import { AppDetailComponent } from './app-detail.component'
|
||||
import { App } from '../application.types'
|
||||
|
||||
@Component({
|
||||
selector: 'app-futility-closet',
|
||||
templateUrl: './futility-closet.component.html'
|
||||
})
|
||||
export class FutilityClosetComponent extends AppDetailComponent {
|
||||
|
||||
@Input() app: App
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
<app-page-title [title]="pageTitle"></app-page-title>
|
||||
<app-application-header [app]="app"></app-application-header>
|
||||
<div class="app-info">
|
||||
<article class="content">
|
||||
<app-application-image [app]="app"></app-application-image>
|
||||
<h3>The Client</h3>
|
||||
<p>
|
||||
Our existing client <a routerLink="/solutions/cassy-fiano"
|
||||
title="Cassy Fiano | Bit Badger Solutions">Cassy Fiano</a>
|
||||
</p>
|
||||
<h3>The Problem</h3>
|
||||
<p>Cassy (now Chesser) wanted a separate place from which to chronicle her experience as a military spouse.</p>
|
||||
<h3>The Solution</h3>
|
||||
<p>
|
||||
In mid-2010, we set up her domain name, created a WordPress site, and customized the header and sidebar for her
|
||||
selected theme. We also hosted and maintained the site for the duration of its run.
|
||||
</p>
|
||||
<h3>The Epilogue</h3>
|
||||
<p>In 2013, Cassy shifted priorities and closed this site down.</p>
|
||||
<app-tech-stack [stack]="app.techStack"></app-tech-stack>
|
||||
<app-all-solutions-link></app-all-solutions-link>
|
||||
</article>
|
||||
</div>
|
|
@ -0,0 +1,18 @@
|
|||
import { Component, Input } from '@angular/core'
|
||||
|
||||
import { AppDetailComponent } from './app-detail.component'
|
||||
import { App } from '../application.types'
|
||||
|
||||
@Component({
|
||||
selector: 'app-hard-corps-wife',
|
||||
templateUrl: './hard-corps-wife.component.html'
|
||||
})
|
||||
export class HardCorpsWifeComponent extends AppDetailComponent {
|
||||
|
||||
@Input() app: App
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
<app-page-title [title]="pageTitle"></app-page-title>
|
||||
<app-application-header [app]="app"></app-application-header>
|
||||
<div class="app-info">
|
||||
<article class="content">
|
||||
<app-application-image [app]="app"></app-application-image>
|
||||
<h3>The Client</h3>
|
||||
<p>
|
||||
<a routerLink="/solutions/dr-melissa-clouthier">Melissa Clouthier</a>, Bill Dupray, and Clyde Middleton, all
|
||||
established conservative bloggers, started a joint venture called <em>Liberty Pundits</em>.
|
||||
</p>
|
||||
<h3>The Problem</h3>
|
||||
<p>
|
||||
Bill and Clyde had a significant amount of content on a prior site. As they were starting this with established
|
||||
authors, they needed a site that would handle their expected traffic spikes on popular posts.
|
||||
</p>
|
||||
<h3>The Solution</h3>
|
||||
<p>
|
||||
In early 2010, we migrated their content from a custom solution into WordPress's database; we then set them up on
|
||||
the same host where their podcast was being distributed. However, the combination of theme complexity and traffic
|
||||
overwhelmed that server, so we configured a standalone server with more memory and more efficient software; this
|
||||
allowed them to routinely eclipse 100,000 views per day, most of those coming on posts within the first few
|
||||
hours.
|
||||
</p>
|
||||
<h3>The Epilogue</h3>
|
||||
<p>
|
||||
The site closed in late 2011, as its authors closed their joint venture and moved on to other sites and topics.
|
||||
</p>
|
||||
<app-hide-section heading="The Process">
|
||||
<p>
|
||||
Before we could migrate the data from <em>Patriot Room</em>, Bill and Clyde's prior home, we had to get into
|
||||
the server and determine how data was stored in the custom solution. Once we identified where all the data was,
|
||||
we wrote a custom migration script to shape the data the way WordPress needed it.
|
||||
</p>
|
||||
<p>
|
||||
Bit Badger Solutions maintained the server, keeping it current with performance and security upgrades. We also
|
||||
provided support to the primary 3 bloggers, when they had questions about WordPress or how the site was
|
||||
performing.
|
||||
</p>
|
||||
</app-hide-section>
|
||||
<app-tech-stack [stack]="app.techStack"></app-tech-stack>
|
||||
<app-all-solutions-link></app-all-solutions-link>
|
||||
</article>
|
||||
</div>
|
|
@ -0,0 +1,18 @@
|
|||
import { Component, Input } from '@angular/core'
|
||||
|
||||
import { AppDetailComponent } from './app-detail.component'
|
||||
import { App } from '../application.types'
|
||||
|
||||
@Component({
|
||||
selector: 'app-liberty-pundits',
|
||||
templateUrl: './liberty-pundits.component.html'
|
||||
})
|
||||
export class LibertyPunditsComponent extends AppDetailComponent {
|
||||
|
||||
@Input() app: App
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
<app-page-title [title]="pageTitle"></app-page-title>
|
||||
<app-application-header [app]="app"></app-application-header>
|
||||
<div class="app-info">
|
||||
<article class="content">
|
||||
<app-application-image [app]="app"></app-application-image>
|
||||
<h3>The Client</h3>
|
||||
<p>
|
||||
Mindy Mackenzie, the prior Chief Performance Officer of Beam, Inc., is known as the “Velvet Hammer”
|
||||
for her tough-yet-caring style of leadership. She is a <em>Wall Street Journal</em> best-selling author of the
|
||||
book <em>The Courage Solution: The Power of Truth-Telling with Your Boss, Peers, and Team</em>, and the creator
|
||||
and host of the annual <em>You First Integrative Leadership Summit</em>, equipping women of influence to reach
|
||||
even greater heights.
|
||||
</p>
|
||||
<h3>The Problem</h3>
|
||||
<p>
|
||||
Mindy was dissatisfied with the value she was receiving with her current web designer and host; in advance of
|
||||
her book launch, she needed a more
|
||||
</p>
|
||||
<h3>The Solution</h3>
|
||||
<p>
|
||||
We took over hosting her site, updating it regularly for the book launch, and highlighting her media appearances
|
||||
in conjunction with that launch. We also created and continue to maintain the pages for her <em>You First
|
||||
Integrative Leadership Summit</em>, including online registration.
|
||||
</p>
|
||||
<app-quotes [quotes]="app.quotes"></app-quotes>
|
||||
<app-hide-section heading="The Process">
|
||||
<p>
|
||||
In late 2015, We assumed maintenance of her site several months in advance of the book launch. We created a
|
||||
custom WordPress type to highlight her Media Appearances, automatically ordered from most recent to older. She
|
||||
had a lot of short video content, and we implemented code that displays a different video each week on the
|
||||
front page.
|
||||
</p>
|
||||
<p>
|
||||
In early 2018, we developed the pages for her <em>You First Integrative Leadership Summit</em>, with speaker
|
||||
bios, conference schedule, and an application form. We have continued to maintain these pages across the 2019
|
||||
and 2020 summits.
|
||||
</p>
|
||||
<p>We continue to provide backups, WordPress support, and content updates for Mindy’s site.</p>
|
||||
</app-hide-section>
|
||||
<app-tech-stack [stack]="app.techStack"></app-tech-stack>
|
||||
<app-all-solutions-link></app-all-solutions-link>
|
||||
</article>
|
||||
</div>
|
|
@ -0,0 +1,18 @@
|
|||
import { Component, Input } from '@angular/core'
|
||||
|
||||
import { AppDetailComponent } from './app-detail.component'
|
||||
import { App } from '../application.types'
|
||||
|
||||
@Component({
|
||||
selector: 'app-mindy-mackenzie',
|
||||
templateUrl: './mindy-mackenzie.component.html'
|
||||
})
|
||||
export class MindyMackenzieComponent extends AppDetailComponent {
|
||||
|
||||
@Input() app: App
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
<app-page-title [title]="pageTitle"></app-page-title>
|
||||
<app-application-header [app]="app"></app-application-header>
|
||||
<div class="app-info">
|
||||
<article class="content">
|
||||
<app-application-image [app]="app"></app-application-image>
|
||||
<h3>The Problem</h3>
|
||||
<p>
|
||||
Daniel wanted to maintain a prayer journal, where he could record the prayer requests for which he had prayed,
|
||||
and the answer that eventually came to that request. He didn't want to do that on paper for several reasons
|
||||
– it's easy to lose, a long-running request can run out of space to make notes, etc.
|
||||
</p>
|
||||
<h3>The Solution</h3>
|
||||
<p>
|
||||
We created a site where users can enter requests, pray through lists of these requests, make notes on them, and
|
||||
follow them through until they are answered. The site stores no identifying information, and works well on both
|
||||
desktop and mobile. Bit Badger Solutions hosts and maintains the instance of the site linked above.
|
||||
</p>
|
||||
<app-hide-section heading="The Process">
|
||||
<p>
|
||||
Development of myPrayerJournal began in earnest in early 2017. As we were using this to learn new techniques,
|
||||
we ended up trying a host of different front and back end technologies before settling on Vue.js for the front
|
||||
end and Giraffe for the back end. This combination works well, and we wrote up an 8-post series entitled
|
||||
<a href="https://blog.bitbadger.solutions/2018/a-tour-of-myprayerjournal/introduction.html">"A Tour of
|
||||
myPrayerJournal"</a> over on the <em>Bit Badger Blog</em> that steps through all aspects of version 1 of this
|
||||
application.
|
||||
</p>
|
||||
<p>
|
||||
Version 2 changed to a Material Design interface, and we changed the data store from PostgreSQL to RavenDB, an
|
||||
excellent document database. As this is an open-source project, anyone can review the source code on
|
||||
<a href="https://github.com/bit-badger/myPrayerJournal">GitHub</a>; we also track open issues there.
|
||||
</p>
|
||||
</app-hide-section>
|
||||
<app-tech-stack [stack]="app.techStack"></app-tech-stack>
|
||||
<app-all-solutions-link></app-all-solutions-link>
|
||||
</article>
|
||||
</div>
|
|
@ -0,0 +1,18 @@
|
|||
import { Component, Input } from '@angular/core'
|
||||
|
||||
import { AppDetailComponent } from './app-detail.component'
|
||||
import { App } from '../application.types'
|
||||
|
||||
@Component({
|
||||
selector: 'app-my-prayer-journal',
|
||||
templateUrl: './my-prayer-journal.component.html'
|
||||
})
|
||||
export class MyPrayerJournalComponent extends AppDetailComponent {
|
||||
|
||||
@Input() app: App
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
<app-page-title [title]="pageTitle"></app-page-title>
|
||||
<app-application-header [app]="app"></app-application-header>
|
||||
<div class="app-info">
|
||||
<article class="content">
|
||||
<app-application-image [app]="app"></app-application-image>
|
||||
<h3>The Client</h3>
|
||||
<p>
|
||||
In January 2008, a few members of <a href="http://hoffmantown.org" title="Hoffmantown Church">Hoffmantown
|
||||
Church</a> in Albuquerque, New Mexico had an idea. The ABC show
|
||||
<em><a href="http://abc.go.com/shows/extreme-makeover-home-edition">Extreme Makeover: Home Edition</a></em> had
|
||||
just done <a href="http://abc.go.com/shows/extreme-makeover-home-edition/episode-detail/martinez-family/224884"
|
||||
title="Martinez Family • Extreme Makeover: Home Edition">a build for a pastor in the “war zone”
|
||||
area of town</a>, and this brought attention to Gerald Martinez and the work he had done to help clean up this
|
||||
area of town. Through <a href="http://www.loveincabq.org/" title="Love INC of South Albuquerque">Love INC of
|
||||
South Albuquerque</a>, they learned that there were many other homes in that area that could use the “Ty
|
||||
Pennington touch.” While the goal was not to knock down homes and build new ones, the goal was no less
|
||||
extreme. The goal of the “Not So Extreme Makeover: Community Edition” was to help 50 families in 5
|
||||
days during spring break week in 2008.
|
||||
</p>
|
||||
<h3>The Problem</h3>
|
||||
<p>
|
||||
An effort of this magnitude, happening this quickly, would be unmanageable without software support. It would
|
||||
also require a lot of paperwork, and a lot of people processing that paperwork.
|
||||
</p>
|
||||
<h3>The Solution</h3>
|
||||
<p>
|
||||
We obtained the domain name and stood up the public website quickly using WordPress, which also allowed the
|
||||
coordinators to put content up. We then developed an application (NSXapp) where volunteers could sign up for
|
||||
“X Week”, with over 80 different skill, talent, and ability categories. We then created a way to
|
||||
identify families and their needs, and a place for people with donations to let us know what they would be. From
|
||||
there, we created the ability to begin matching needs with goods (stuff) and abilities (people), organizing the
|
||||
stuff into donated trailers and people into teams. During X Week, NSXapp generated schedules and reports that
|
||||
were used to help guide the teams as they executed their projects.
|
||||
</p>
|
||||
<h3>The Epilogue</h3>
|
||||
<p>
|
||||
From an idea in January, “Not So Extreme Makeover: Community Edition” was able to help 57 families
|
||||
by the end of X Week on March 29th. When Love INC saw how NSXapp worked, they expressed an interest in a version
|
||||
that would allow them to handle these same areas on an ongoing basis; this became
|
||||
<a routerLink="/solutions/tcms" title="The Clearinghouse Management System | Bit Badger Solutions">TCMS</a>.
|
||||
Finally, there is a <a href="https://nsx.archive.bitbadger.solutions">snapshot of the NSX public site</a> that
|
||||
serves as a record of those three months in 2008.
|
||||
</p>
|
||||
<app-tech-stack [stack]="app.techStack"></app-tech-stack>
|
||||
<app-all-solutions-link></app-all-solutions-link>
|
||||
</article>
|
||||
</div>
|
|
@ -0,0 +1,18 @@
|
|||
import { Component, Input } from '@angular/core'
|
||||
|
||||
import { AppDetailComponent } from './app-detail.component'
|
||||
import { App } from '../application.types'
|
||||
|
||||
@Component({
|
||||
selector: 'app-nsx',
|
||||
templateUrl: './nsx.component.html'
|
||||
})
|
||||
export class NsxComponent extends AppDetailComponent {
|
||||
|
||||
@Input() app: App
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
<app-page-title [title]="pageTitle"></app-page-title>
|
||||
<app-application-header [app]="app"></app-application-header>
|
||||
<div class="app-info">
|
||||
<article class="content">
|
||||
<app-application-image [app]="app"></app-application-image>
|
||||
<h3>The Client</h3>
|
||||
<p>
|
||||
Olivet Baptist Church was a Southern Baptist church in Gulfport, Mississippi, who had seen our work with
|
||||
<a routerLink="/solutions/bay-vista" title="Bay Vista Baptist Church | Bit Badger Solutions">Bay Vista</a> and
|
||||
wanted something similar.
|
||||
</p>
|
||||
<h3>The Problem</h3>
|
||||
<p>Olivet had no online presence.</p>
|
||||
<h3>The Solution</h3>
|
||||
<p>
|
||||
Initially, we set up a WordPress site, configured it, and established a podcast feed; we also advised them on how
|
||||
to register that feed in iTunes. A few years later, we converted the site to behave like an app, where it could
|
||||
be installed as an icon, allowing quick access.
|
||||
</p>
|
||||
<h3>The Epilogue</h3>
|
||||
<p>
|
||||
When the church closed its doors on February 24th, 2019, we converted the app-behaving site back to a static web
|
||||
site, set up an archive site, and worked with their personnel to ensure that the podcast links are all still
|
||||
available. We continue to host that archive site and podcast content.
|
||||
</p>
|
||||
<app-hide-section heading="The Process">
|
||||
<p>
|
||||
In 2014, we registered the domain name for the church. They had expressed a desire to do as much of the content
|
||||
of the site themselves, so we supported them as they worked through its initial setup. After the site was
|
||||
originally set up, though, updates were rare (apart from the weekly podcast episodes), so we converted it to be
|
||||
a statically-generated site.
|
||||
<p>
|
||||
In 2018, we modified the site to be a Progressive Web Application (PWA), which allows users to
|
||||
“install” the site, like an app, to their phone’s home screen. The site was also still
|
||||
accessible from the web via a browser. We converted the static content to generate page fragments that the PWA
|
||||
would load, providing the same navigation experience as before.
|
||||
</p>
|
||||
</app-hide-section>
|
||||
<app-tech-stack [stack]="app.techStack"></app-tech-stack>
|
||||
<app-all-solutions-link></app-all-solutions-link>
|
||||
</article>
|
||||
</div>
|
|
@ -0,0 +1,18 @@
|
|||
import { Component, Input } from '@angular/core'
|
||||
|
||||
import { AppDetailComponent } from './app-detail.component'
|
||||
import { App } from '../application.types'
|
||||
|
||||
@Component({
|
||||
selector: 'app-olivet-baptist',
|
||||
templateUrl: './olivet-baptist.component.html'
|
||||
})
|
||||
export class OlivetBaptistComponent extends AppDetailComponent {
|
||||
|
||||
@Input() app: App
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
<app-page-title [title]="pageTitle"></app-page-title>
|
||||
<app-application-header [app]="app"></app-application-header>
|
||||
<div class="app-info">
|
||||
<article class="content">
|
||||
<app-application-image [app]="app"></app-application-image>
|
||||
<h3>The Client</h3>
|
||||
<p>
|
||||
Michelle Summers had been photographing her children for years. When her sons were on sports teams, she was
|
||||
disappointed with the cost of team photography, and felt that she could do a better job at a lower cost. She
|
||||
specialized in outdoor photography of families, children, and sports teams, as well as maternity photography and
|
||||
holiday cards.
|
||||
</p>
|
||||
<h3>The Problem</h3>
|
||||
<p>
|
||||
Michelle needed a site to showcase her previous work, as well as a place to allow her customers to view their
|
||||
proofs before selecting prints.
|
||||
</p>
|
||||
<h3>The Solution</h3>
|
||||
<p>
|
||||
We created a WordPress site with image galleries for her existing work, and utilized a custom plug-in to support
|
||||
online proofs. This site was eventually replaced with one that had a matching Windows application; this
|
||||
application took a set of photos, resized them, applied a watermark, and created the proof gallery without having
|
||||
to even go to the site.
|
||||
</p>
|
||||
<h3>The Epilogue</h3>
|
||||
<p>
|
||||
As Michelle is no longer doing professional photography, the current version of this site is a simple thank-you
|
||||
to her customers from 2007-2014.
|
||||
</p>
|
||||
<app-tech-stack [stack]="app.techStack"></app-tech-stack>
|
||||
<app-all-solutions-link></app-all-solutions-link>
|
||||
</article>
|
||||
</div>
|
|
@ -0,0 +1,18 @@
|
|||
import { Component, Input } from '@angular/core'
|
||||
|
||||
import { AppDetailComponent } from './app-detail.component'
|
||||
import { App } from '../application.types'
|
||||
|
||||
@Component({
|
||||
selector: 'app-photography-by-michelle',
|
||||
templateUrl: './photography-by-michelle.component.html'
|
||||
})
|
||||
export class PhotographyByMichelleComponent extends AppDetailComponent {
|
||||
|
||||
@Input() app: App
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
<app-page-title [title]="pageTitle"></app-page-title>
|
||||
<app-application-header [app]="app"></app-application-header>
|
||||
<div class="app-info">
|
||||
<article class="content">
|
||||
<app-application-image [app]="app"></app-application-image>
|
||||
<h3>The Problem</h3>
|
||||
<p>
|
||||
Back in 2005, Daniel was responsible for keeping up with prayer requests for his Sunday School class. However,
|
||||
simply sending out a mass e-mail has some significant drawbacks - everyone's e-mail address is visible to
|
||||
everyone else; mass e-mails are more likely to be flagged as suspicious; and it is difficult to have a single
|
||||
“latest and greatest” list of members.
|
||||
</p>
|
||||
<h3>The Solution</h3>
|
||||
<p>
|
||||
We wrote a site so we could enter prayer requests and class members; this site would then send individual e-mails
|
||||
to each member. When requests were 15 days old, they would drop off the list. From there, PrayerTracker has grown
|
||||
to support multiple churches and groups within those churches, and the user interface is available in both
|
||||
English <em>y Español</em>. Bit Badger Solutions offers use of this site for free to any church, Sunday School
|
||||
class, or small group that desires a tool to help them establish a continuous list of prayer requests.
|
||||
</p>
|
||||
<app-hide-section heading="The Process">
|
||||
<p>
|
||||
The first reimagining of PrayerTracker occurred in 2011; this was when we moved to a more modern (at the time)
|
||||
framework (ASP MVC 3), building in the multi-church/multi-group security additions, and posturing it for an
|
||||
interface with <a routerLink="/solutions/virtual-prayer-room"
|
||||
title="Virtual Prayer Room | Bit Badger Solutions">Virtual Prayer Room</a>. A year later, a visiting missionary
|
||||
saw the site and liked it, but needed the site (including the online help) in Spanish; we released version 4 a
|
||||
few months later which brought this support.
|
||||
</p>
|
||||
<p>
|
||||
In late 2014, version 5 moved to a MongoDB data store, as we had some problems with columns not being large
|
||||
enough for some requests. In early 2017, we released version 6, which took PrayerTracker into the .NET Core
|
||||
environment; we also moved the data back to PostgreSQL, as it now supported the sizes we needed.
|
||||
</p>
|
||||
<p>
|
||||
Version 7 was released in mid-2018, bringing full mobile accessibility and an upgrade to a modern, ultra-fast
|
||||
web framework (Giraffe). In early 2019, version 7.1 was the first release for PrayerTracker as an
|
||||
<a href="https://github.com/bit-badger/PrayerTracker">open source project</a>. Right on its heels, version 7.2
|
||||
moved the embedded help files to GitHub Pages; this made the web application more streamlined.
|
||||
</p>
|
||||
</app-hide-section>
|
||||
<app-tech-stack [stack]="app.techStack"></app-tech-stack>
|
||||
<app-all-solutions-link></app-all-solutions-link>
|
||||
</article>
|
||||
</div>
|
|
@ -0,0 +1,18 @@
|
|||
import { Component, Input } from '@angular/core'
|
||||
|
||||
import { AppDetailComponent } from './app-detail.component'
|
||||
import { App } from '../application.types'
|
||||
|
||||
@Component({
|
||||
selector: 'app-prayer-tracker',
|
||||
templateUrl: './prayer-tracker.component.html'
|
||||
})
|
||||
export class PrayerTrackerComponent extends AppDetailComponent {
|
||||
|
||||
@Input() app: App
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
<app-page-title [title]="pageTitle"></app-page-title>
|
||||
<app-application-header [app]="app"></app-application-header>
|
||||
<div class="app-info">
|
||||
<article class="content">
|
||||
<app-application-image [app]="app"></app-application-image>
|
||||
<h3>The Client</h3>
|
||||
<p>
|
||||
Dan Riehl began blogging as <em>The Carnivorous Conservative</em> back in 2004, specializing in the areas of
|
||||
crime and politics. He changed to <em>Riehl World View</em> a short time later, and writes both news and opinion
|
||||
pieces. He was a prolific blogger, publishing over 15 posts a day on most days.
|
||||
</p>
|
||||
<h3>The Problem</h3>
|
||||
<p>
|
||||
He wanted to take his blog in a different direction, and was having trouble getting his Movable Type blog do move
|
||||
with him.
|
||||
</p>
|
||||
<h3>The Solution</h3>
|
||||
<p>
|
||||
We stood up a WordPress site on a server he procured. We then assisted him in selecting a theme and customized it
|
||||
to his liking. Finally, we wrote custom migration code to get his past body of work into the new site. In 2018,
|
||||
we generated static files for most of his prior posts, to give him a clean slate for a new direction. We continue
|
||||
to maintain and support <em>Riehl World News</em>.
|
||||
</p>
|
||||
<app-tech-stack [stack]="app.techStack"></app-tech-stack>
|
||||
<app-all-solutions-link></app-all-solutions-link>
|
||||
</article>
|
||||
</div>
|
|
@ -0,0 +1,18 @@
|
|||
import { Component, Input } from '@angular/core'
|
||||
|
||||
import { AppDetailComponent } from './app-detail.component'
|
||||
import { App } from '../application.types'
|
||||
|
||||
@Component({
|
||||
selector: 'app-riehl-world-news',
|
||||
templateUrl: './riehl-world-news.component.html'
|
||||
})
|
||||
export class RiehlWorldNewsComponent extends AppDetailComponent {
|
||||
|
||||
@Input() app: App
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
<app-page-title [title]="pageTitle"></app-page-title>
|
||||
<app-application-header [app]="app"></app-application-header>
|
||||
<div class="app-info">
|
||||
<article class="content">
|
||||
<app-application-image [app]="app"></app-application-image>
|
||||
<h3>The Client</h3>
|
||||
<p>
|
||||
<em>The Shark Tank</em> is a news and opinion site centered on south Florida politics (and the state at large).
|
||||
They provided extensive coverage of Rep. Allen West’s winning campaign in 2010, and continue their focused
|
||||
news and opinion on current political races.
|
||||
</p>
|
||||
<h3>The Problem</h3>
|
||||
<p>
|
||||
They were displeased with their current theme; it was struggling with the amount of content they were producing.
|
||||
</p>
|
||||
<h3>The Solution</h3>
|
||||
<p>
|
||||
They had identified a theme that would better suit their needs. We set it up, ensuring that their content would
|
||||
fit in the new theme’s requirements, and helped them turn off parts that they didn’t need. We also
|
||||
converted the social media connections from their old site to a style that would work nicely in the new theme.
|
||||
</p>
|
||||
<h3>The Epilogue</h3>
|
||||
<p>This was all they needed; they returned their focus to their writing.</p>
|
||||
<app-tech-stack [stack]="app.techStack"></app-tech-stack>
|
||||
<app-all-solutions-link></app-all-solutions-link>
|
||||
</article>
|
||||
</div>
|
|
@ -0,0 +1,18 @@
|
|||
import { Component, Input } from '@angular/core'
|
||||
|
||||
import { AppDetailComponent } from './app-detail.component'
|
||||
import { App } from '../application.types'
|
||||
|
||||
@Component({
|
||||
selector: 'app-shark-tank',
|
||||
templateUrl: './shark-tank.component.html'
|
||||
})
|
||||
export class SharkTankComponent extends AppDetailComponent {
|
||||
|
||||
@Input() app: App
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
<app-page-title [title]="pageTitle"></app-page-title>
|
||||
<app-application-header [app]="app"></app-application-header>
|
||||
<div class="app-info">
|
||||
<article class="content">
|
||||
<app-application-image [app]="app"></app-application-image>
|
||||
<h3>The Client</h3>
|
||||
<p>
|
||||
Love INC of South Albuquerque runs a “needs clearinghouse”; they have volunteers who accept
|
||||
donations, and people contact them with their needs. They are then able to match the person who needs something
|
||||
with that thing, or with someone who can assist them.
|
||||
</p>
|
||||
<h3>The Problem</h3>
|
||||
<p>
|
||||
The files in their offices were multiplying; ensuring people’s needs are not missed, while ensuring that
|
||||
their clients were not taking advantage of their services, required a lot of paper. They were tracking volunteers
|
||||
on a spreadsheet, but their contact info was in yet another file. Having worked with us on the
|
||||
<a routerLink="/solutions/nsx"
|
||||
title="Not So Extreme Makeover: Community Edition | Bit Badger Solutions">“Not So Extreme Makeover:
|
||||
Community Edition”</a>, and thought that the solution we developed for that project would help them.
|
||||
</p>
|
||||
<h3>The Solution</h3>
|
||||
<p>
|
||||
We adapted NSXapp to handle an ongoing stream of people, volunteers, and donations. This enabled them to spend
|
||||
more time with the people who needed help. The WordPress front end also served as their public website, and
|
||||
allowed them to manage the volunteers who were using the system.
|
||||
</p>
|
||||
<h3>The Epilogue</h3>
|
||||
<p>
|
||||
Love INC of South Albuquerque found a SalesForce system that would do things very similar to TCMS, and was able
|
||||
to get in on a program that let them use it at no cost; TCMS was decommissioned in 2014.
|
||||
</p>
|
||||
<app-tech-stack [stack]="app.techStack"></app-tech-stack>
|
||||
<app-all-solutions-link></app-all-solutions-link>
|
||||
</article>
|
||||
</div>
|
|
@ -0,0 +1,18 @@
|
|||
import { Component, Input } from '@angular/core'
|
||||
|
||||
import { AppDetailComponent } from './app-detail.component'
|
||||
import { App } from '../application.types'
|
||||
|
||||
@Component({
|
||||
selector: 'app-tcms',
|
||||
templateUrl: './tcms.component.html'
|
||||
})
|
||||
export class TcmsComponent extends AppDetailComponent {
|
||||
|
||||
@Input() app: App
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
<app-page-title [title]="pageTitle"></app-page-title>
|
||||
<app-application-header [app]="app"></app-application-header>
|
||||
<div class="app-info">
|
||||
<article class="content">
|
||||
<app-application-image [app]="app"></app-application-image>
|
||||
<h3>The Client</h3>
|
||||
<p>
|
||||
Our existing client <a href="http://hoffmantown.org" title="Hoffmantown Church">Hoffmantown Church</a> in
|
||||
Albuquerque, New Mexico, with whom we had worked on the <a routerLink="/solutions/nsx/"
|
||||
title="Not So Extreme Makeover: Community Edition | Bit Badger Solutions">Not So Extreme Makeover: Community
|
||||
Edition</a>
|
||||
</p>
|
||||
<h3>The Problem</h3>
|
||||
<p>
|
||||
Hoffmantown had seen the use of this physical prayer room dwindling over the years. People had become less
|
||||
willing to drive to the church, especially at night, and security became an issue as well; either prayer warriors
|
||||
had to know how to disable the security system, or the church would have to remain unlocked.
|
||||
</p>
|
||||
<h3>The Solution</h3>
|
||||
<p>
|
||||
The development of Virtual Prayer Room extended the prayer room to anywhere a prayer warrior can get an Internet
|
||||
connection! Prayer warriors could enlist right from the site, and had to be approved. Requests and updates were
|
||||
tracked by date/time, and warriors could record when they’ve prayed for a request from the site, or from
|
||||
clicking a link in the daily e-mail they received with requests from their interest areas. As many prayer needs
|
||||
are confidential, security and confidentiality were very important. Virtual Prayer Room ensured these by
|
||||
providing varying security levels for prayer warriors and the ability to mark each request as confidential.
|
||||
</p>
|
||||
<h3>The Epilogue</h3>
|
||||
<p>
|
||||
In 2016, Hoffmantown Church elected to begin using another package for their prayer requests. While a few other
|
||||
churches had expressed interest in it, none ultimately decided to use it; so, in 2017, Virtual Prayer Room was
|
||||
officially decommissioned.
|
||||
</p>
|
||||
<app-tech-stack [stack]="app.techStack"></app-tech-stack>
|
||||
<app-all-solutions-link></app-all-solutions-link>
|
||||
</article>
|
||||
</div>
|
|
@ -0,0 +1,18 @@
|
|||
import { Component, Input } from '@angular/core';
|
||||
|
||||
import { AppDetailComponent } from './app-detail.component';
|
||||
import { App } from '../application.types';
|
||||
|
||||
@Component({
|
||||
selector: 'app-virtual-prayer-room',
|
||||
templateUrl: './virtual-prayer-room.component.html'
|
||||
})
|
||||
export class VirtualPrayerRoomComponent extends AppDetailComponent {
|
||||
|
||||
@Input() app: App
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
<div *ngIf="(stack || []).length > 0">
|
||||
<app-hide-section heading="The Technology Stack">
|
||||
<div *ngIf="currentStack.length > 0">
|
||||
<p><small><strong>Current:</strong></small></p>
|
||||
<ul>
|
||||
<app-technology *ngFor="let tech of currentStack" [tech]="tech"></app-technology>
|
||||
</ul>
|
||||
</div>
|
||||
<div *ngIf="priorStack.length > 0">
|
||||
<p *ngIf="currentStack.length > 0"><small><strong>Previously:</strong></small></p>
|
||||
<ul>
|
||||
<app-technology *ngFor="let tech of priorStack" [tech]="tech"></app-technology>
|
||||
</ul>
|
||||
</div>
|
||||
</app-hide-section>
|
||||
</div>
|
|
@ -0,0 +1,4 @@
|
|||
p
|
||||
margin-bottom: 0
|
||||
ul
|
||||
margin-top: 0
|
|
@ -0,0 +1,28 @@
|
|||
import { Component, OnInit, Input } from '@angular/core'
|
||||
|
||||
import { Technology } from '../application.types'
|
||||
|
||||
@Component({
|
||||
selector: 'app-tech-stack',
|
||||
templateUrl: './tech-stack.component.html',
|
||||
styleUrls: ['./tech-stack.component.sass']
|
||||
})
|
||||
export class TechStackComponent implements OnInit {
|
||||
|
||||
@Input() stack: Technology[]
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit() { }
|
||||
|
||||
/** The currently-used technologies */
|
||||
get currentStack() {
|
||||
return this.stack.filter(p => p.current)
|
||||
}
|
||||
|
||||
/** The previously-used technologies */
|
||||
get priorStack() {
|
||||
return this.stack.filter(p => !p.current)
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
<li>
|
||||
<a *ngIf="hasLink(tech.name)" [href]="techLinks[tech.name]" target="_blank">{{ tech.name }}</a>
|
||||
<span *ngIf="!hasLink(tech.name)">{{ tech.name }}</span>
|
||||
for {{ tech.usedFor }}
|
||||
</li>
|
|
@ -0,0 +1,51 @@
|
|||
import { Component, OnInit, Input } from '@angular/core'
|
||||
|
||||
import { Technology } from '../application.types'
|
||||
|
||||
@Component({
|
||||
selector: 'app-technology',
|
||||
templateUrl: './technology.component.html',
|
||||
styleUrls: ['./technology.component.sass']
|
||||
})
|
||||
export class TechnologyComponent implements OnInit {
|
||||
|
||||
@Input() tech: Technology
|
||||
|
||||
/** External links to technology products */
|
||||
private techLinks = {
|
||||
'ASP.NET MVC': 'https://dotnet.microsoft.com/apps/aspnet/mvc',
|
||||
Azure: 'https://azure.microsoft.com/',
|
||||
'BlogEngine.NET': 'http://www.dotnetblogengine.net/',
|
||||
'Database Abstraction': 'https://github.com/danieljsummers/DatabaseAbstraction',
|
||||
'Digital Ocean': 'https://www.digitalocean.com/',
|
||||
Giraffe: 'https://github.com/giraffe-fsharp/Giraffe',
|
||||
GitHub: 'https://github.com/',
|
||||
'GitHub Pages': 'https://pages.github.com/',
|
||||
Hexo: 'https://hexo.io/',
|
||||
Hugo: 'https://gohugo.io/',
|
||||
Jekyll: 'https://jekyllrb.com/',
|
||||
MongoDB: 'https://www.mongodb.com/',
|
||||
MySQL: 'https://www.mysql.com/',
|
||||
myWebLog: 'https://github.com/bit-badger/myWebLog',
|
||||
nginx: 'http://nginx.org/',
|
||||
Orchard: 'https://orchardproject.net/',
|
||||
PHP: 'https://www.php.net/',
|
||||
PostgreSQL: 'https://www.postgresql.org/',
|
||||
'Rackspace Cloud': 'https://www.rackspace.com/cloud',
|
||||
RavenDB: 'https://ravendb.net/',
|
||||
RethinkDB: 'https://rethinkdb.com/',
|
||||
'SQL Server': 'https://www.microsoft.com/en-us/sql-server/',
|
||||
'Vue.js': 'https://vuejs.org/',
|
||||
WordPress: 'https://wordpress.org'
|
||||
}
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit() { }
|
||||
|
||||
/** Whether there is a link for a given product */
|
||||
hasLink(product: string) {
|
||||
return this.techLinks[product] !== undefined
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
<app-page-title title="Information Publicizing Solutions"></app-page-title>
|
||||
<article class="content auto">
|
||||
<h1>Information Publicizing and Blogging</h1>
|
||||
<p>
|
||||
In the early days of the World Wide Web, it was known as the "information superhighway." From its inception, the
|
||||
web's primary goal is information. The open nature of the Internet allows anyone, anywhere to say anything,
|
||||
provided they can connect a machine to the network. In fact, there are software products to handle everything
|
||||
except creating the content; all you have to bring is the ability to form a coherent thought, and type that
|
||||
thought into a box. <a href="https://wordpress.org" title="WordPress">WordPress</a> is one of the most popular
|
||||
<abbr title="Web Log">blog</abbr>ging platforms in use today; it allows authors to concentrate on the content of
|
||||
their websites, rather than forcing authors to turn into programmers.
|
||||
</p>
|
||||
<h2>Custom-Built Sites</h2>
|
||||
<ul>
|
||||
<li>
|
||||
<p>
|
||||
We developed and maintained the site for <a href="http://www.emeraldmountainchristianschool.org">Emerald
|
||||
Mountain Christian School</a>
|
||||
<small> (<a routerLink="/solutions/emerald-mountain-christian-school" title="Emerald Mountain Christian School | Bit Badger Solutions">about</a>)</small>
|
||||
for 9 years, where they had information about the type of curriculum they teach, the school's 40+-year history,
|
||||
a calendar of events, and how to get more information.
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
We built and maintained the site for <a href="https://www.summershome.org">Photography by Michelle</a>
|
||||
<small> (<a routerLink="/solutions/photography-by-michelle" title="Photography by Michelle | Bit Badger Solutions">about</a>)</small>,
|
||||
which had information, prices, and samples of the photographer's work, as well as the ability for customers to
|
||||
view proofs and make photo selections online.
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
The site for <a href="https://bayvista.org" title="Bay Vista Baptist Church">Bay Vista Baptist Church</a>
|
||||
<small> (<a routerLink="/solutions/bay-vista" title="Bay Vista Baptist Church | Bit Badger Solutions">about</a>)</small>
|
||||
utilizes a "static site generator," where the entire site is generated from source files, then served. It
|
||||
requires no back-end database, which means that the server can send pages as fast as its clients can take them.
|
||||
This site even has a generated podcast feed! Adding content to these types of sites requires a bit more
|
||||
technical knowledge beyond "typing text in a box," but it is a great way to build ultra-fast, scalable web
|
||||
sites.
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
This site is a single-page application (SPA) utilizing the <a href="https://angular.io">Angular</a> JavaScript
|
||||
framework. The application pages are generated based on an internal data set, and the other pages are simple
|
||||
text components. Its bundling means that the initial page is small, and after the initial load, it runs
|
||||
entirely in the browser or on a phone or tablet. Sites that reference external data sets would still need to
|
||||
access the Internet to retrieve data, but this is much more efficient than having to download the entire page
|
||||
every single click. (It's even <a href="https://github.com/bit-badger/bitbadger.solutions">open source</a> if
|
||||
you want to see how we did it.)
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
<h2>WordPress Design, Customization, and Support</h2>
|
||||
<ul>
|
||||
<li>
|
||||
<p>
|
||||
We helped <a routerLink="/solutions/cassy-fiano" title="Cassy Fiano | Bit Badger Solutions">Cassy Fiano</a>
|
||||
and <a routerLink="/solutions/dr-melissa-clouthier" title="Dr. Melissa Clouthier | Bit Badger Solutions">Dr.
|
||||
Melissa Clouthier</a> both move their blogs from Blogspot to their own domains.
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
We migrated <a routerLink="/solutions/liberty-pundits" title="Liberty Pundits">Liberty Pundits</a> from a
|
||||
custom blog platform to WordPress, and set up and maintained their server, which routinely cleared 100,000
|
||||
hits per day in its prime.
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
For <a href="https://www.futilitycloset.com" title="Futility Closet">Futility Closet</a>
|
||||
<small> (<a routerLink="/solutions/futility-closet" title="Futility Closet | Bit Badger Solutions">about</a>)</small>,
|
||||
we moved their site from a shared hosting platform to its own <abbr title="Virtual Private Server">VPS</abbr>,
|
||||
to enable it to handle its ever-increasing traffic.
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
<a routerLink="/solutions/tcms" title="TCMS | Bit Badger Solutions">TCMS</a> and
|
||||
<a routerLink="/solutions/nsx" title="NSXapp | Bit Badger Solutions">NSXapp</a> both used WordPress as their
|
||||
front end, which also provided a public web presence that the customers could update themselves.
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
<p>
|
||||
On <em><a href="https://blog.bitbadger.solutions" title="The Bit Badger Blog">The Bit Badger Blog</a></em> you can
|
||||
browse the
|
||||
<a href="https://blog.bitbadger.solutions/category/wordpress" title="WordPress | The Bit Badger Blog">WordPress</a>
|
||||
category for information on plug-ins, and we have supported theme customizations for nearly all of the WordPress
|
||||
sites linked on the sidebar/footer of the home page.
|
||||
</p>
|
||||
<p><br><a routerLink="/" title="Home">« Home</a></p>
|
||||
</article>
|
|
@ -0,0 +1,13 @@
|
|||
import { Component, OnInit } from '@angular/core'
|
||||
|
||||
@Component({
|
||||
selector: 'app-information-publicizing',
|
||||
templateUrl: './information-publicizing.component.html'
|
||||
})
|
||||
export class InformationPublicizingComponent implements OnInit {
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit() { }
|
||||
|
||||
}
|
|
@ -1,8 +1,7 @@
|
|||
<template lang="pug">
|
||||
article.content.auto
|
||||
page-title(title='Legacy Data Solutions')
|
||||
h1 Legacy Data Sharing
|
||||
p.
|
||||
<app-page-title title="Legacy Data Solutions"></app-page-title>
|
||||
<article class="content auto">
|
||||
<h1>Legacy Data Sharing</h1>
|
||||
<p>
|
||||
Our background in mainframe applications gives us a knowledgeable perspective on retrieving information from
|
||||
older, “legacy” systems. This data can be migrated to a more modern relational or document database,
|
||||
where a web application can retrieve the information; in some cases, the data can even be exposed as a web service
|
||||
|
@ -10,7 +9,6 @@ article.content.auto
|
|||
without having to move their day-to-day system from its current environment. While we currently have no active
|
||||
projects along these lines, our developers have done them in the past for other organizations; sadly, none can be
|
||||
linked publicly.
|
||||
p
|
||||
br
|
||||
router-link(to='/' title='Home') « Home
|
||||
</template>
|
||||
</p>
|
||||
<p><br><a routerLink="/" title="Home">« Home</a></p>
|
||||
</article>
|
|
@ -0,0 +1,13 @@
|
|||
import { Component, OnInit } from '@angular/core'
|
||||
|
||||
@Component({
|
||||
selector: 'app-legacy-data',
|
||||
templateUrl: './legacy-data.component.html'
|
||||
})
|
||||
export class LegacyDataComponent implements OnInit {
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit() { }
|
||||
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
<app-page-title title="Process Automation Solutions"></app-page-title>
|
||||
<article class="content auto">
|
||||
<h1>Process Automation and User Engagement</h1>
|
||||
<p>
|
||||
Computers can be used to augment or automate nearly any process; could you think of generating bank statements,
|
||||
processing mailing lists, or tracking orders without some form of automation? We develop web-based solutions to
|
||||
automate <em>your</em> processes, ensuring that your business constraints are satisfied; these systems can run on
|
||||
the Internet or your private network. For Internet-facing solutions, we engineer solutions that allow them to
|
||||
interact with you securely, presented in an engaging manner. And, by “engagement,” we are not
|
||||
describing intrusive page pop-ups and other <span class="strike">marketing gimmicks</span> web annoyances; we
|
||||
determine an optimal user experience for <em>your</em> customers, and tailor the solution to work for both of you.
|
||||
</p>
|
||||
<p>Several of our solutions fit this description.</p>
|
||||
<ul>
|
||||
<li>
|
||||
<p>
|
||||
<a routerLink="/solutions/virtual-prayer-room" title="Virtual Prayer Room | Bit Badger Solutions">Virtual
|
||||
Prayer Room</a> helped the prayer ministry of
|
||||
<a href="http://www.hoffmantown.org" title="Hoffmantown Church in Albuquerque, NM">Hoffmantown Church</a>
|
||||
enable their prayer warriors to have access to requests wherever they are, even in their inbox once a day!
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
<a routerLink="/solutions/tcms" title="TCMS | Bit Badger Solutions">TCMS</a> was an application that helped
|
||||
organizations such as <a href="http://www.loveincabq.org">Love INC of South Albuquerque</a> connect people with
|
||||
needs to people who can help fulfill those needs. TCMS sprung from the
|
||||
<a href="https://nsx.archive.bitbadger.solutions" title="Not So Extreme Makeover: Community Edition (Archive)">Not
|
||||
So Extreme Makeover: Community Edition</a> in Albuquerque, New Mexico during spring break 2008; we not only
|
||||
developed the public presence, but a private system called
|
||||
<a routerLink="/solutions/nsx" title="NSXapp | Bit Badger Solutions">NSXapp</a> that enabled the management of
|
||||
the volunteers, families, and things for this massive effort.
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
We continue to offer <a href="https://prayer.bitbadger.solutions" title="PrayerTracker">PrayerTracker</a>
|
||||
<small> (<a routerLink="/solutions/prayer-tracker" title="PrayerTracker | Bit Badger Solutions">about</a>)</small>,
|
||||
a free-to-use web application that helps Sunday School classes (or other small groups) generate a prayer
|
||||
request list; it provides a central place for list management and continuity.
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
<p><br><a routerLink="/" title="Home">« Home</a></p>
|
||||
</article>
|
|
@ -0,0 +1,13 @@
|
|||
import { Component, OnInit } from '@angular/core'
|
||||
|
||||
@Component({
|
||||
selector: 'app-process-automation',
|
||||
templateUrl: './process-automation.component.html'
|
||||
})
|
||||
export class ProcessAutomationComponent implements OnInit {
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit() { }
|
||||
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
<app-page-title title="Web Services and API Solutions"></app-page-title>
|
||||
<article class="content auto">
|
||||
<h1>Web Services and APIs</h1>
|
||||
<p>
|
||||
A web service is a way of using the Internet to provide or accept information that makes sense to computers; this
|
||||
allows other sites or applications to consume information from, or provide information to, your service. This
|
||||
enables communication between applications, without having to establish any communication channels other than the
|
||||
ones that web browsers already use. It isn't the best fit for every application, but when it is useful, it is
|
||||
<em>very</em> useful.
|
||||
</p>
|
||||
<p>
|
||||
An <abbr title="Application Programming Interface">API</abbr> can be a synonym for a web service, but it can also
|
||||
be a generally accessible way of providing data. For example, Twitter has a public API, which other applications
|
||||
can use to display tweets on their site.
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
<p>
|
||||
<a routerLink="/solutions/photography-by-michelle" title="Photography by Michelle | Bit Badger Solutions">Photography
|
||||
by Michelle</a> had a private web API that a desktop application utilized to create the online proof sets right
|
||||
from the computer where the images resided.
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
We
|
||||
<a href="https://blog.bitbadger.solutions/2010/4040-web-service.html" title="40/40 Web Service | The Bit Badger Blog">wrote
|
||||
a service</a> for the 2010
|
||||
<a href="http://erlc.com/4040/" title="40/40 Prayer Vigil | Ethics and Religious Liberty Commission of the Southern Baptist Convention">40/40
|
||||
Prayer Vigil</a>, which was utilized by several sites to display the current day's (or hour's) prayer focus, and
|
||||
<a href="https://blog.bitbadger.solutions/2012/4040-web-service-for-2012.html" title="40/40 Web Service for 2012 | The Bit Badger Blog">wrote
|
||||
one for 2012</a> as well. <em>(As the ERLC does not host these any more, this service is no longer active.)</em>
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
<a href="https://prayerjournal.me">myPrayerJournal</a>
|
||||
<small> (<a routerLink="/solutions/my-prayer-journal" title="myPrayerJournal | Bit Badger Solutions">about</a>)</small>
|
||||
is a <abbr title="Single Page Application">SPA</abbr> which only downloads the structure of the site the first
|
||||
time you go there, then utilizes a stateless API to access data from the browser.
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
<p><br><a routerLink="/" title="Home">« Home</a></p>
|
||||
</article>
|
|
@ -0,0 +1,13 @@
|
|||
import { Component, OnInit } from '@angular/core'
|
||||
|
||||
@Component({
|
||||
selector: 'app-web-services',
|
||||
templateUrl: './web-services.component.html'
|
||||
})
|
||||
export class WebServicesComponent implements OnInit {
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit() { }
|
||||
|
||||
}
|
|
@ -1,23 +1,24 @@
|
|||
<template lang="pug">
|
||||
article.content.auto
|
||||
page-title(title='Why Bit Badger?')
|
||||
h1 Why “Bit Badger”?
|
||||
p.
|
||||
<app-page-title title="Why Bit Badger?"></app-page-title>
|
||||
<article class="content auto">
|
||||
<h1>Why “Bit Badger”?</h1>
|
||||
<p>
|
||||
A while back, our primary developer Daniel learned through genetic testing that he had one gene that was not right
|
||||
(technically known as a genetic mutation). He is currently fine #[em (thank you for asking)], but his co-workers
|
||||
(technically known as a genetic mutation). He is currently fine <em>(thank you for asking)</em>, but his co-workers
|
||||
thought of another group of genetic mutants – the X-Men. They wanted to develop the mutant identity for him
|
||||
in that style; since Wolverine is already taken, they wanted something similar, but based on a member of the weasel
|
||||
family (for its normal private life and fierce tenacity, not its morals). They went through several different
|
||||
options, but when “Bit Badger” was mentioned, it was the winner. The Bit Badger's mutant superpower is
|
||||
the ability to shoot 1s and 0s out its nostrils!
|
||||
p.
|
||||
</p>
|
||||
<p>
|
||||
Daniel liked this moniker, and decided to run with it. He had been growing dissatisfied with the name “DJS
|
||||
Consulting,” as he felt that name was passive. He enjoys taking problems and finding creative solutions for
|
||||
them, making our computers work for us instead of the other way around. While he can't actually breathe out 1s and
|
||||
0s, they do flow from his fingers (in groups of 8, of course).
|
||||
p.
|
||||
Do you have a problem that needs a solution? #[a(href='mailto:daniel@bitbadger.solutions') Sic the Bit Badger on it]!
|
||||
p
|
||||
br
|
||||
router-link(to='/' title='Home') « Home
|
||||
</template>
|
||||
</p>
|
||||
<p>
|
||||
Do you have a problem that needs a solution? <a href="mailto:daniel@bitbadger.solutions">Sic the Bit Badger on
|
||||
it</a>!
|
||||
</p>
|
||||
<p><br><a routerLink="/" title="Home">« Home</a></p>
|
||||
</article>
|
|
@ -0,0 +1,13 @@
|
|||
import { Component, OnInit } from '@angular/core'
|
||||
|
||||
@Component({
|
||||
selector: 'app-why-bit-badger',
|
||||
templateUrl: './why-bit-badger.component.html'
|
||||
})
|
||||
export class WhyBitBadgerComponent implements OnInit {
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit() { }
|
||||
|
||||
}
|
60
bit-badger-solutions/src/app/pages/home/home.component.html
Normal file
60
bit-badger-solutions/src/app/pages/home/home.component.html
Normal file
|
@ -0,0 +1,60 @@
|
|||
<app-page-title title="Welcome!"></app-page-title>
|
||||
<div class="home">
|
||||
<article class="content auto">
|
||||
<p class="home-lead">Bit Badger Solutions develops the site you need to enable your success!</p>
|
||||
<p>These solutions can take several different forms.</p>
|
||||
<h2>Process Automation and User Engagement</h2>
|
||||
<p>
|
||||
Do you have a process that requires recording the same thing multiple times? Do you have information in different
|
||||
places, but you need it all together? This solution is for you.
|
||||
<a routerLink="/about/process-automation-solutions" title="Process Automation Solutions">Learn more about how
|
||||
our solutions automate processes and engage users</a>.
|
||||
</p>
|
||||
<h2>Information Publicizing and Blogging</h2>
|
||||
<p>
|
||||
From its inception, the Web has been about information. Do you need to get information out about an upcoming
|
||||
event? Are you wanting to start blogging, or breathe some fresh life into an existing blog? Those are but a few
|
||||
of the problems that this solution solves.
|
||||
<a routerLink="/about/information-publicizing-solutions" title="Information Publicizing Solutions">Find out more
|
||||
about our information publicizing and blogging solutions</a> (including WordPress and statically-generated
|
||||
sites).
|
||||
</p>
|
||||
<h2>Web Services and APIs</h2>
|
||||
<p>
|
||||
Do you have a need for multiple computers to talk to each other? Do you have an interesting data set that you want
|
||||
to make available to the public? A web service or API may be just the solution for you.
|
||||
<a routerLink="/about/web-services-solutions" title="Web Services and API Solutions">Learn about web services,
|
||||
along with examples of current solutions</a>.
|
||||
</p>
|
||||
<h2>Legacy Data Sharing</h2>
|
||||
<p>
|
||||
Do you have data that's old — and by “old,” we aren’t talking “iPhone 6” old,
|
||||
we’re talking “this data
|
||||
<a href="https://en.wikipedia.org/wiki/Age_of_candidacy#United_States" title="Age of Candidacy (United States) | Wikipedia">could
|
||||
run for President</a>” old? Just because the information is in an older “legacy” system
|
||||
doesn’t mean it has to stay there.
|
||||
<a routerLink="/about/legacy-data" title="Legacy Data Sharing Solutions">Learn how our solutions can help get
|
||||
this data where you and your customers can access it more easily</a>.
|
||||
</p>
|
||||
<h2>Why Web-Based?</h2>
|
||||
<p>Web-based solutions have many advantages:</p>
|
||||
<ul>
|
||||
<li><p>They can be used just on a local, private network (an intranet) or on the public Internet.</p></li>
|
||||
<li><p>They are available to any device connected to the network.</p></li>
|
||||
<li><p>They require no special software; every device has a browser - which you're using to read this!)</p></li>
|
||||
<li><p>They can get your most critical needs met first, then evolved and improved over time.</p></li>
|
||||
</ul>
|
||||
<h2>What Is a “Bit Badger”?</h2>
|
||||
<p>
|
||||
<a routerLink="/about/why-bit-badger" title="Why Bit Badger?">Read the Bit Badger’s origin story</a>.
|
||||
</p>
|
||||
<h2>Solutions to Your Problems</h2>
|
||||
<p>
|
||||
We’d be happy to discuss your information technology needs, and which of our solutions are right for you.
|
||||
Just <a href="mailto:daniel@bitbadger.solutions">e-mail us</a> and let us know what we can do for you! You can
|
||||
also <a routerLink="/solutions" title="All Solutions">browse a complete list of our current and previous
|
||||
solutions</a>.
|
||||
</p>
|
||||
</article>
|
||||
<app-sidebar></app-sidebar>
|
||||
</div>
|
|
@ -0,0 +1,9 @@
|
|||
@media all and (min-width: 80rem)
|
||||
.home
|
||||
display: flex
|
||||
flex-flow: row
|
||||
align-items: flex-start
|
||||
justify-content: space-around
|
||||
.home-lead
|
||||
font-size: 1.3rem
|
||||
text-align: center
|
14
bit-badger-solutions/src/app/pages/home/home.component.ts
Normal file
14
bit-badger-solutions/src/app/pages/home/home.component.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
import { Component, OnInit } from '@angular/core'
|
||||
|
||||
@Component({
|
||||
selector: 'app-home',
|
||||
templateUrl: './home.component.html',
|
||||
styleUrls: ['./home.component.sass']
|
||||
})
|
||||
export class HomeComponent implements OnInit {
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit() { }
|
||||
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
<footer>
|
||||
A <strong><a routerLink="/">Bit Badger Solutions</a></strong> original design
|
||||
</footer>
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user