Create Nuxt version

This commit is contained in:
Daniel J. Summers 2020-08-08 20:08:44 -05:00
parent 9e80e528c9
commit 128c554ce5
194 changed files with 15155 additions and 12891 deletions

View File

@ -1,12 +0,0 @@
# 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'.

View File

@ -1,13 +1,13 @@
# Editor configuration, see https://editorconfig.org # editorconfig.org
root = true root = true
[*] [*]
charset = utf-8
indent_style = space indent_style = space
indent_size = 2 indent_size = 2
insert_final_newline = true end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true trim_trailing_whitespace = true
insert_final_newline = true
[*.md] [*.md]
max_line_length = off
trim_trailing_whitespace = false trim_trailing_whitespace = false

View File

@ -0,0 +1,15 @@
module.exports = {
root: true,
env: {
browser: true,
node: true
},
extends: [
'@nuxtjs/eslint-config-typescript',
'plugin:nuxt/recommended'
],
plugins: [
],
// add your custom rules here
rules: {}
}

View File

@ -1,46 +1,90 @@
# See http://help.github.com/ignore-files/ for more about ignoring files. # Created by .ignore support plugin (hsz.mobi)
### Node template
# Logs
/logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# compiled output # Runtime data
/dist pids
/tmp *.pid
/out-tsc *.seed
# Only exists if Bazel was run *.pid.lock
/bazel-out
# dependencies # Directory for instrumented libs generated by jscoverage/JSCover
/node_modules lib-cov
# profiling files # Coverage directory used by tools like istanbul
chrome-profiler-events*.json coverage
speed-measure-plugin*.json
# IDEs and editors # nyc test coverage
/.idea .nyc_output
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.vscode/* .grunt
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.history/*
# misc # Bower dependency directory (https://bower.io/)
/.sass-cache bower_components
/connect.lock
/coverage
/libpeerconnection.log
npm-debug.log
yarn-error.log
testem.log
/typings
# System Files # node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# TypeScript v1 declaration files
typings/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
# parcel-bundler cache (https://parceljs.org/)
.cache
# next.js build output
.next
# nuxt.js build output
.nuxt
# Nuxt generate
dist
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless
# IDE / Editor
.idea
# Service worker
sw.*
# macOS
.DS_Store .DS_Store
Thumbs.db
# Vim swap files
*.swp

View File

@ -0,0 +1,20 @@
# bit-badger-solutions
## Build Setup
```bash
# install dependencies
$ npm install
# serve with hot reload at localhost:3000
$ npm run dev
# build for production and launch server
$ npm run build
$ npm run start
# generate static project
$ npm run generate
```
For detailed explanation on how things work, check out [Nuxt.js docs](https://nuxtjs.org).

View File

@ -1,132 +0,0 @@
{
"$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": true,
"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,
"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",
"cli": {
"analytics": "bfe12c8e-b3a2-47cb-abee-7380fe1d1fed"
}
}

View File

@ -0,0 +1,29 @@
<template>
<svg class="NuxtLogo" width="245" height="180" viewBox="0 0 452 342" xmlns="http://www.w3.org/2000/svg">
<path
d="M139 330l-1-2c-2-4-2-8-1-13H29L189 31l67 121 22-16-67-121c-1-2-9-14-22-14-6 0-15 2-22 15L5 303c-1 3-8 16-2 27 4 6 10 12 24 12h136c-14 0-21-6-24-12z"
fill="#00C58E"
/>
<path
d="M447 304L317 70c-2-2-9-15-22-15-6 0-15 3-22 15l-17 28v54l39-67 129 230h-49a23 23 0 0 1-2 14l-1 1c-6 11-21 12-23 12h76c3 0 17-1 24-12 3-5 5-14-2-26z"
fill="#108775"
/>
<path
d="M376 330v-1l1-2c1-4 2-8 1-12l-4-12-102-178-15-27h-1l-15 27-102 178-4 12a24 24 0 0 0 2 15c4 6 10 12 24 12h190c3 0 18-1 25-12zM256 152l93 163H163l93-163z"
fill="#2F495E"
/>
</svg>
</template>
<style>
.NuxtLogo {
animation: 1s appear;
margin: auto;
}
@keyframes appear {
0% {
opacity: 0;
}
}
</style>

View File

@ -0,0 +1,19 @@
<template>
<footer>
A <strong><nuxt-link to="/">Bit Badger Solutions</nuxt-link></strong> original design
</footer>
</template>
<style scoped>
footer {
padding: 20px 15px 10px 15px;
text-align: right;
font-size: 1rem;
color: black;
clear: both;
background-image: linear-gradient(to bottom, #FFFAFA, lightgray);
}
footer a:link, footer a:visited {
color: black;
}
</style>

View File

@ -0,0 +1,70 @@
<template>
<header class="site-header">
<div class="header-logo">
<nuxt-link to="/">
<img
src="/bitbadger.png"
alt="A cartoon badger looking at a computer screen, with his paw on a mouse"
title="Bit Badger Solutions">
</nuxt-link>
</div>
<div class="header-title">
<nuxt-link to="/">Bit Badger Solutions</nuxt-link>
</div>
<div class="header-spacer">
&nbsp;
</div>
<div class="header-social">
<a href="https://twitter.com/Bit_Badger" title="Bit_Badger on Twitter" target="_blank">
<img src="/twitter.png" alt="Twitter">
</a>&nbsp; &nbsp;<a href="https://www.facebook.com/bitbadger.solutions" title="Bit Badger Solutions on Facebook" _target="_blank">
<img src="/facebook.png" alt="Facebook">
</a>
</div>
</header>
</template>
<style scoped>
.site-header {
height: 100px;
display: flex;
flex-direction: row;
justify-content: space-between;
background-image: linear-gradient(to bottom, lightgray, #FFFAFA);
}
.site-header a, .site-header a:visited {
color: black;
}
.site-header a:hover {
border-bottom: none;
}
.header-title {
font-size: 3rem;
font-weight: bold;
line-height: 100px;
text-align: center;
}
.header-spacer {
flex-grow: 3;
}
.header-social {
padding: 25px .8rem 0 0;
}
.header-social img {
width: 50px;
height: 50px;
}
@media all and (max-width:40rem) {
.site-header {
height: auto;
flex-direction: column;
align-items: center;
}
.header-title {
line-height: 3rem;
}
.header-spacer {
display: none;
}
}
</style>

View File

@ -0,0 +1,41 @@
<template>
<section>
<h3 @click="toggle()">
{{ heading }}<span class="arrow" v-html="label"></span>
</h3>
<slot v-if="isVisible"></slot>
</section>
</template>
<script lang="ts">
import Vue from 'vue'
export default Vue.extend({
name: 'hidden-section',
props: [
'heading'
],
data () {
return {
isVisible: false,
label: '&#x25BC;'
}
},
methods: {
toggle () {
this.isVisible = !this.isVisible
this.label = this.isVisible ? '&#x25B2;' : '&#x25BC;'
}
}
})
</script>
<style scoped>
h3:hover {
cursor: hand;
cursor: pointer;
}
.arrow {
font-size: .75rem;
padding-left: 1rem;
}
</style>

View File

@ -0,0 +1,108 @@
<template>
<aside class="app-sidebar">
<div v-for="cat in sortedCats" :key="cat">
<div class="app-sidebar-head" v-text="cat" />
<div v-for="sln in displayForCategegory(cat)" :key="sln.title">
<p class="app-sidebar-name">
<strong>{{ sln.title }}</strong><br>
<span v-if="!sln.noAboutLink">
<nuxt-link :to="`/solutions/${sln.slug}`" :title="aboutTitle(sln.title)">About</nuxt-link> &bull;
</span>
<a :href="sln.url" :title="sln.title" target="_blank">Visit</a>
</p>
<p class="app-sidebar-description" v-html="sln.frontPage.text" />
</div>
</div>
</aside>
</template>
<script lang="ts">
import Vue from 'vue'
/**
* Sort the categories in a specific order; any not addressed will appear at the bottom.
*/
const categoryOrder = (x: string, y: string) => {
if (x === 'Web Sites and Applications') { return -1 }
if (x === 'WordPress' && y !== 'Web Sites and Applications') { return -1 }
if (x === 'Static Sites' && y !== 'Web Sites and Applications' && y !== 'WordPress') { return -1 }
return 1
}
export default Vue.extend({
name: 'home-sidebar',
props: ['catNames', 'solutions'],
computed: {
sortedCats (): string[] {
return [...this.catNames].sort(categoryOrder)
}
},
methods: {
displayForCategegory (category: string): any[] {
return this.solutions
.filter((x: any) => x.category === category)
.sort((x: any, y: any) => x.frontPage.order - y.frontPage.order)
},
aboutTitle (name: string): string {
return `About ${name} | Bit Badger Solutions`
}
}
})
</script>
<style scoped>
.app-sidebar {
text-align: center;
border-top: dotted 1px lightgray;
padding-top: 1rem;
font-size: .9rem;
display: flex;
flex-flow: row wrap;
justify-content: space-around;
}
.app-sidebar > div {
width: 20rem;
padding-bottom: 1rem;
}
@media all and (min-width: 68rem) {
.app-sidebar {
width: 66rem;
margin: auto;
}
}
@media all and (min-width: 80rem) {
.app-sidebar {
width: 12rem;
border-top: none;
border-left: dotted 1px lightgray;
padding-top: 0;
padding-left: 2rem;
flex-direction: column;
}
.app-sidebar > div {
width: auto;
}
}
.app-sidebar a {
font-size: 10pt;
font-family: sans-serif;
}
.app-sidebar-head {
font-family: "Oswald", "Segoe UI", Ubuntu, "DejaVu Sans", "Liberation Sans", Arial, sans-serif;
font-weight: bold;
color: maroon;
margin-bottom: .8rem;
padding: 3px 12px;
border-bottom: solid 2px lightgray;
font-size: 1rem;
}
.app-sidebar-name, .app-sidebar-description {
margin: 0;
padding: 0;
}
.app-sidebar-description {
font-style: italic;
color: #555555;
padding-bottom: .6rem;
}
</style>

View File

@ -0,0 +1,29 @@
<template>
<p>
<span class="app-name" v-html="solution.title"></span>
~ <nuxt-link :to="'/solutions/' + solution.slug">About</nuxt-link>
<span v-if="!solution.isInactive">~ <a :href="solution.url" target="_blank">Visit</a></span>
<span v-if="solution.isInactive && solution.archiveUrl">
~&nbsp;<a :href="solution.archiveUrl" target="_blank">Visit</a><em> (archive)</em>
</span>
<br>
<span v-html="solution.summary"></span>
</p>
</template>
<script lang="ts">
import Vue from 'vue'
export default Vue.extend({
name: 'solution-item',
props: ['solution']
})
</script>
<style scoped>
.app-name {
font-family: "Oswald", "Segoe UI", Ubuntu, "DejaVu Sans", "Liberation Sans", Arial, sans-serif;
font-size: 1.3rem;
font-weight: bold;
color: maroon;
}
</style>

View File

@ -0,0 +1,58 @@
<template>
<li>
<a v-if="hasLink(tech[0])" :href="techLinks[tech[0]]" target="_blank">{{ tech[0] }}</a>
<template v-else>{{ tech[0] }}</template>
for {{ tech[1] }}
</li>
</template>
<script lang="ts">
import Vue from 'vue'
/** Links to various technologies */
const techLinks: any = {
'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'
}
export default Vue.extend({
name: 'technology-item',
props: [ 'tech' ],
data () {
return { techLinks }
},
methods: {
/**
* Does the given technology have a known link?
*
* @param tech The technology to be checked
* @returns True if there is a link, false if not
*/
hasLink (tech: string): boolean {
return techLinks[tech] !== undefined
}
}
})
</script>

View File

@ -0,0 +1,21 @@
---
title: Information Publicizing and Blogging
---
In the early days of the World Wide Web, it was known as the &ldquo;information superhighway.&rdquo; From its inception, the web&rsquo;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. [WordPress](https://wordpress.org "WordPress") 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.
## Custom-Built Sites
- We developed and maintained the site for [Emerald Mountain Christian School](http://www.emeraldmountainchristianschool.org)<small> (<nuxt-link to="/solutions/emerald-mountain-christian-school" title="Emerald Mountain Christian School &bull; Bit Badger Solutions">about</nuxt-link>)</small> for 9 years, where they had information about the type of curriculum they teach, the school&rsquo;s 40+-year history, a calendar of events, and how to get more information.
- We built and maintained the site for [Photography by Michelle](https://www.summershome.org)<small> (<nuxt-link to="/solutions/photography-by-michelle" title="Photography by Michelle &bull; Bit Badger Solutions">about</nuxt-link>)</small>, which had information, prices, and samples of the photographer&rsquo;s work, as well as the ability for customers to view proofs and make photo selections online.
- The site for [Bay Vista Baptist Church](https://bayvista.org "Bay Vista Baptist Church")<small> (<nuxt-link to="/solutions/bay-vista" title="Bay Vista Baptist Church &bull; Bit Badger Solutions">about</nuxt-link>)</small> utilizes a &ldquo;static site generator,&rdquo; 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 &ldquo;typing text in a box,&rdquo; but it is a great way to build ultra-fast, scalable web sites.
- This site is also a statically-generated site, utilizing the [Nuxt](https://nuxtjs.org) project for the [Vue.js](https://vuejs.org) JavaScript framework. Using Nuxt&rsquo;s content plugin, the majority of pages are written in Markdown (a very writing-friendly format). We can include data at the top of each file that controls how it is displayed; this gives us both the flexibility of a database and the speed of not actually having one! (It&rsquo;s [open source](https://github.com/bit-badger/bitbadger.solutions) if you want to see how we did it.)
## WordPress Design, Customization, and Support
- For [Futility Closet](https://www.futilitycloset.com "Futility Closet")<small> (<nuxt-link to="/solutions/futility-closet" title="Futility Closet &bull; Bit Badger Solutions">about</nuxt-link>)</small>, we moved their site from a shared hosting platform to its own <abbr title="Virtual Private Server">VPS</abbr>, securing it from intrusion attempts and enabling it to handle its ever-increasing traffic.
- We took over [Mindy Mackenzie](https://mindymackenzie.com "Mindy Mackenzie")<small> (<nuxt-link to="/solutions/mindy-mackenzie" title="Mindy Mackenzie &bull; Bit Badger Solutions">about</nuxt-link>)</small> in advance of the release of her eventual _<abbr title="Wall Street Journal">WSJ</abbr>_ best-selling book _The Courage Solution_, and continue to support her as she hosts the annual _You First Integrative Leadership Summit_.
- We helped <nuxt-link to="/solutions/cassy-fiano" title="Cassy Fiano &bull; Bit Badger Solutions">Cassy Fiano</nuxt-link> and <nuxt-link to="/solutions/dr-melissa-clouthier" title="Dr. Melissa Clouthier &bull; Bit Badger Solutions">Dr. Melissa Clouthier</nuxt-link> both move their blogs from Blogspot to their own domains.
- We migrated <nuxt-link to="/solutions/liberty-pundits" title="Liberty Pundits &bull; Bit Badger Solutions">Liberty Pundits</nuxt-link> 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.
- <nuxt-link to="/solutions/tcms" title="The Clearinghouse Management System (TCMS) &bull; Bit Badger Solutions">TCMS</nuxt-link> and <nuxt-link to="/solutions/nsx" title="NSXapp &bull; Bit Badger Solutions">NSXapp</nuxt-link> both used WordPress as their front end, which also provided a public web presence that the customers could update themselves.
On _[The Bit Badger Blog](https://blog.bitbadger.solutions "The Bit Badger Blog")_ you can browse the [WordPress](https://blog.bitbadger.solutions/category/wordpress "WordPress &bull; The Bit Badger Blog") 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.

View File

@ -0,0 +1,4 @@
---
title: Legacy Data Solutions
---
Our background in mainframe applications gives us a knowledgeable perspective on retrieving information from older, &ldquo;legacy&rdquo; 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 in place. These types of systems are often a great way for companies to expose their data to their customers, without having to move their day-to-day system from its current environment. While we currently have no active projects along these lines, we have done them in the past for other organizations; sadly, none can be linked publicly.

View File

@ -0,0 +1,10 @@
---
title: Process Automation and User Engagement
---
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 _your_ 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 &ldquo;engagement,&rdquo; 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 _your_ customers, and tailor the solution to work for both of you.
Several of our solutions fit this description.
- <nuxt-link to="/solutions/virtual-prayer-room" title="Virtual Prayer Room &bull; Bit Badger Solutions">Virtual Prayer Room</nuxt-link> helped the prayer ministry of [Hoffmantown Church](https://www.hoffmantownchurch.org "Hoffmantown Church &bull; Albuquerque, New Mexico") enable their prayer warriors to have access to requests wherever they are, even in their inbox once a day!
- <nuxt-link to="/solutions/tcms" title="The Clearinghouse Management System (TCMS) &bull; Bit Badger Solutions">TCMS</nuxt-link> was an application that helped organizations such as [Love INC of South Albuquerque](http://www.loveincabq.org) connect people with needs to people who can help fulfill those needs. TCMS sprung from the [Not So Extreme Makeover: Community Edition](https://nsx.archive.bitbadger.solutions "Not So Extreme Makeover: Community Edition (Archive)") in Albuquerque, New Mexico during spring break 2008; we not only developed the public presence, but a private system called <nuxt-link to="/solutions/nsx" title="NSXapp &bull; Bit Badger Solutions">NSXapp</nuxt-link> that enabled the management of the volunteers, families, and things for this massive effort.
- We continue to offer [PrayerTracker](https://prayer.bitbadger.solutions "PrayerTracker")<small> (<nuxt-link to="/solutions/prayer-tracker" title="PrayerTracker &bull; Bit Badger Solutions">about</nuxt-link>)</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.

View File

@ -0,0 +1,10 @@
---
title: Web Services Solutions
---
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&rsquo;t the best fit for every application, but when it is useful, it is _very_ useful.
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.
- [myPrayerJournal](https://prayerjournal.me)<small> (<nuxt-link to="/solutions/my-prayer-journal" title="myPrayerJournal &bull; Bit Badger Solutions">about</nuxt-link>)</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.
- <nuxt-link to="/solutions/photography-by-michelle" title="Photography by Michelle &bull; Bit Badger Solutions">Photography by Michelle</nuxt-link> had a private web API that a desktop application utilized to create the online proof sets right from the computer where the images resided.
- We [wrote a service](https://blog.bitbadger.solutions/2010/4040-web-service.html "40/40 Web Service &bull; The Bit Badger Blog") for the 2010 [40/40 Prayer Vigil](http://erlc.com/4040/ "40/40 Prayer Vigil &bull; Ethics and Religious Liberty Commission of the Southern Baptist Convention"), which was utilized by several sites to display the current day&rsquo;s (or hour&rsquo;s) prayer focus, and [wrote one for 2012](https://blog.bitbadger.solutions/2012/4040-web-service-for-2012.html "40/40 Web Service for 2012 &bull; The Bit Badger Blog") as well. _(As the ERLC does not host these any more, this service is no longer active.)_

View File

@ -0,0 +1,8 @@
---
title: Why &ldquo;Bit Badger&rdquo;?
---
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 _(thank you for asking)_, but his co-workers thought of another group of genetic mutants &ndash; 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 &ldquo;Bit Badger&rdquo; was mentioned, it was the winner. The Bit Badger's mutant superpower is the ability to shoot 1s and 0s out its nostrils!
Daniel liked this moniker, and decided to run with it. He had been growing dissatisfied with the name &ldquo;DJS Consulting,&rdquo; 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).
Do you have a problem that needs a solution? [Sic the Bit Badger on it](mailto:daniel@bitbadger.solutions)!

View File

@ -0,0 +1,45 @@
<p class="home-lead">Bit Badger Solutions develops the site you need to enable your success!</p>
These solutions can take several different forms.
## Process Automation and User Engagement
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. [Learn more about how our solutions automate processes and engage users][automation].
## Information Publicizing and Blogging
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. [Find out more about our information publicizing and blogging solutions][information] (including WordPress and statically-generated sites).
## Web Services and APIs
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. [Learn about web services, along with examples of current solutions][services].
## Legacy Data Sharing
Do you have data that's old &mdash; and by &ldquo;old,&rdquo; we aren&rsquo;t talking &ldquo;iPhone 6&rdquo; old, we&rsquo;re talking &ldquo;this data [could run for President][old]&rdquo; old? Just because the information is in an older &ldquo;legacy&rdquo; system doesn&rsquo;t mean it has to stay there. [Learn how our solutions can help get this data where you and your customers can access it more easily][legacy].
## Why Web-Based?
Web-based solutions have many advantages:
- They can be used just on a local, private network (an intranet) or on the public Internet.
- They are available to any device connected to the network.
- They require no special software; every device has a browser - which you're using to read this!)
- They can get your most critical needs met first, then evolved and improved over time.
## What Is a &ldquo;Bit Badger&rdquo;?
[Read the Bit Badger&rsquo;s origin story][origin].
## Solutions to Your Problems
We&rsquo;d be happy to discuss your information technology needs, and which of our solutions are right for you. Just [e-mail us](mailto:daniel@bitbadger.solutions) and let us know what we can do for you! You can also [browse a complete list of our current and previous solutions][solutions].
[automation]: /about/process-automation-solutions "Process Automation Solutions"
[information]: /about/information-publicizing-solutions "Information Publicizing Solutions"
[services]: /about/web-services-solutions "Web Services and API Solutions"
[old]: https://en.wikipedia.org/wiki/Age_of_candidacy#United_States "Age of Candidacy (United States) | Wikipedia"
[legacy]: /about/legacy-data "Legacy Data Sharing Solutions"
[origin]: /about/why-bit-badger "Why Bit Badger?"
[solutions]: /solutions "All Solutions"

View File

@ -0,0 +1,10 @@
---
title: A Word from the Word
url: https://devotions.summershome.org
category: Personal
noAboutLink: true
frontPage:
display: true
order: 2
text: Devotions by Daniel
---

View File

@ -0,0 +1,49 @@
---
title: Bay Vista Baptist Church
url: https://bayvista.org
summary: Southern Baptist church in Biloxi, Mississippi
category: Static Sites
frontPage:
display: true
order: 1
text: Biloxi, Mississippi
technologies:
- Hugo:
for: static site generation
isCurrent: true
- Azure:
for: podcast file storage, automated builds, and static site hosting
isCurrent: true
- GitHub:
for: source code control
isCurrent: true
- Hexo:
for: static site generation
- Jekyll:
for: static site generation
- WordPress:
for: content management
- MySQL:
for: data storage
---
### The Client
Bay Vista Baptist Church has served the spiritual needs of Mississippi&rsquo;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&rsquo;s [Operation Christmas Child](https://www.samaritanspurse.org/what-we-do/operation-christmas-child/) campaign.
### The Problem
In late 2013, the authors of their current website were no longer around, and no one could get to the site to update it.
### The Solution
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.
<hidden-section heading="The Process">
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.
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.
In 2019, we [open sourced](https://github.com/bayvistabc/www.bayvista.org) the site&rsquo;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&rsquo;s contents and the podcast feed. Although we continue to host the site, the church is now maintaining it themselves.
</hidden-section>

View File

@ -0,0 +1,42 @@
---
title: Cassy Fiano
url: http://www.cassyfiano.com
summary: A &ldquo;rising star&rdquo; conservative blogger
category: WordPress
isInactive: true
noLink: true
frontPage:
display: false
technologies:
- WordPress:
for: blogging (with a custom theme)
- MySQL:
for: data storage
- Rackspace Cloud:
for: backup and recovery
- Azure:
for: backup and recovery
---
### The Client
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.
### The Problem
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.
### The Solution
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.
### The Epilogue
Cassy formally decommissioned this site in early 2014.
<hidden-section heading="The Process">
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.
In July 2012, we began hosting the site, as we were already hosting her military wife blog <nuxt-link to="/solutions/hard-corps-wife" title="Hard Corps Wife &bull; Bit Badger Solutions">Hard Corps Wife</nuxt-link>. When the time came to decommission the site, we backed up the data and ensured she had it.
</hidden-section>

View File

@ -0,0 +1,10 @@
---
title: Daniel J. Summers
url: https://daniel.summershome.org
category: Personal
noAboutLink: true
frontPage:
display: true
order: 1
text: Daniel&rsquo;s personal blog
---

View File

@ -0,0 +1,42 @@
---
title: Dr. Melissa Clouthier
url: http://melissablogs.com
summary: Politics, health, podcasts and more
category: WordPress
isInactive: true
noLink: true
frontPage:
display: false
technologies:
- WordPress:
for: blogging (with a custom theme)
- MySQL:
for: data storage
- Rackspace Cloud:
for: backup and recovery
- Azure:
for: backup and recovery
---
### The Client
Dr. Melissa Clouthier (now Mackenzie) blogged from the political right; she also covered health issues and social media techniques and utilization.
### The Problem
She had seen our work with <nuxt-link to="/solutions/cassy-fiano" title="Cassy Fiano &bull; Bit Badger Solutions">Cassy</nuxt-link>&rsquo;s site, also wanted to move off Blogger; however, she did not want to lose her years of posts up to that point.
### The Solution
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.
<small>_(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.)_</small>
### The Epilogue
Melissa decommissioned this site in 2018; we took final snapshots of the data before shutting it down.
<hidden-section heading="The Process">
Initially, we created the theme based off another well-known blogger&rsquo;s site, which had been developed by one of WordPress&rsquo;s core contributors. We also advised on the type of hosting she would need for her site, and moved several domains there. We also took care of regular backups of her data.
</hidden-section>

View File

@ -0,0 +1,43 @@
---
title: Emerald Mountain Christian School
url: http://www.emeraldmountainchristianschool.org
summary: Classical, Christ-centered education near Wetumpka, Alabama
category: Web Sites and Applications
isInactive: true
frontPage:
display: false
technologies:
- PHP:
for: page generation and interactivity
- ASP.NET MVC:
for: page generation and interactivity
- PostgreSQL:
for: data storage
- Rackspace Cloud:
for: hosting
- Azure:
for: hosting
---
### The Client
Emerald Mountain Christian School is a private Christian school founded over 50 years ago. They use the Principle Approach&reg;, 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&rsquo;s rich history can be found on their site.
### The Problem
They had a website with very basic information and very little styling; they also had no way of updating it.
### The Solution
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.
### The Epilogue
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 [EMCSpatriots.org](http://emcspatriots.org "EMCS Patriots").
<hidden-section heading="The Process">
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.
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.
</app-hide-section>

View File

@ -0,0 +1,53 @@
---
title: Futility Closet
url: https://www.futilitycloset.com
summary: An idler&rsquo;s miscellany of compendious amusements
category: WordPress
frontPage:
display: true
order: 1
text: An idler&rsquo;s miscellany of compendious amusements
technologies:
- WordPress:
for: blogging
isCurrent: true
- nginx:
for: the web server
isCurrent: true
- MySQL:
for: data storage
isCurrent: true
- Digital Ocean:
for: web site hosting
isCurrent: true
- Azure:
for: backup and recovery
isCurrent: true
- Rackspace Cloud:
for: web site hosting
---
### The Client
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 &ldquo;forgotten stories from the pages of history,&rdquo; along with story updates and lateral thinking puzzles.
### The Problem
The site was running on a shared host, but was growing too large for that platform. The site had also suffered regular security breaches.
### The Solution
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.
### The Business Impact
> <p class="quote">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&rsquo;s never failed in being friendly, knowledgeable, thoughtful, and farsighted. I&rsquo;ve literally lost count of the number of times he&rsquo;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&rsquo;t for his reliability, expertise, and good judgment. And he&rsquo;s a joy to work with.</p>
>
> <p class="source"> &mdash; <strong>Greg Ross</strong>, Futility Closet</p>
<hidden-section heading="The Process">
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&rsquo;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.
In 2015, we began hosting Futility Closet (using Digital Ocean).
</app-hide-section>

View File

@ -0,0 +1,32 @@
---
title: Hard Corps Wife
url: http://www.hardcorpswife.com
summary: Cassy&rsquo;s life as a Marine wife
category: WordPress
isInactive: true
noLink: true
frontPage:
display: false
technologies:
- WordPress:
for: blogging
- MySQL:
for: data storage
- Rackspace Cloud:
for: web site hosting
---
### The Client
Our existing client <nuxt-link to="/solutions/cassy-fiano" title="Cassy Fiano &bull; Bit Badger Solutions">Cassy Fiano</nuxt-link>
### The Problem
Cassy (now Chesser) wanted a separate place from which to chronicle her experience as a military spouse.
### The Solution
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.
### The Epilogue
In 2013, Cassy shifted priorities and closed this site down.

View File

@ -0,0 +1,40 @@
---
title: Liberty Pundits
url: http://libertypundits.net
summary: The home for conservatives
category: WordPress
isInactive: true
noLink: true
frontPage:
display: false
technologies:
- WordPress:
for: blogging
- PHP:
for: custom data migration software
- MySQL:
for: data storage
---
### The Client
<nuxt-link to="/solutions/dr-melissa-clouthier" title="Dr. Melissa Clouthier &bull; Bit Badger Solutions">Melissa Clouthier</nuxt-link>, Bill Dupray, and Clyde Middleton, all established conservative bloggers, started a joint venture called _Liberty Pundits_.
### The Problem
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.
### The Solution
In early 2010, we migrated their content from a custom solution into WordPress&rsquo;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.
### The Epilogue
The site closed in late 2011, as its authors closed their joint venture and moved on to other sites and topics.
<hidden-section heading="The Process">
Before we could migrate the data from _Patriot Room_, Bill and Clyde&rsquo;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.
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.
</hidden-section>

View File

@ -0,0 +1,10 @@
---
title: Linux Resources
url: https://blog.bitbadger.solutions/linux/
category: Web Sites and Applications
noAboutLink: true
frontPage:
display: true
order: 3
text: Handy information for Linux folks
---

View File

@ -0,0 +1,53 @@
---
title: Mindy Mackenzie
url: https://mindymackenzie.com
summary: <em>Wall Street Journal</em> best-selling author and C-suite advisor
category: WordPress
frontPage:
display: true
order: 2
text: WSJ-best-selling author of The Courage Solution
technologies:
- WordPress:
for: blogging and content management
isCurrent: true
- nginx:
for: the web server
isCurrent: true
- MySQL:
for: data storage
isCurrent: true
- Digital Ocean:
for: web site hosting
isCurrent: true
- Azure:
for: backup and recovery
isCurrent: true
---
### The Client
Mindy Mackenzie, the prior Chief Performance Officer of Beam, Inc., is known as the &ldquo;Velvet Hammer&rdquo; for her tough-yet-caring style of leadership. She is a _Wall Street Journal_ best-selling author of the book _The Courage Solution: The Power of Truth-Telling with Your Boss, Peers, and Team_, and the creator and host of the annual _You First Integrative Leadership Summit_, equipping women of influence to reach even greater heights.
### The Problem
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 responsive site that could easily be updated.
### The Solution
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 _You First Integrative Leadership Summit_, including online registration.
### The Business Impact
> <p class="quote">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&rsquo;s terrific!</p>
>
> <p class="source"> &mdash; <strong>Mindy Mackenzie</strong></p>
<hidden-section heading="The Process">
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.
In early 2018, we developed the pages for her _You First Integrative Leadership Summit_, with speaker bios, conference schedule, and an application form. We have continued to maintain these pages across the 2019 and 2020 summits.
We continue to provide backups, WordPress support, and content updates for Mindy&rsquo;s site.
</app-hide-section>

View File

@ -0,0 +1,43 @@
---
title: myPrayerJournal
url: https://prayerjournal.me
summary: Minimalist personal prayer journal
category: Web Sites and Applications
frontPage:
display: true
order: 2
text: Minimalist personal prayer journal
technologies:
- Vue.js:
for: the front-end
isCurrent: true
- Giraffe:
for: the back-end data API
isCurrent: true
- RavenDB:
for: data storage
isCurrent: true
- GitHub:
for: source code control
isCurrent: true
- GitHub Pages:
for: documentation
isCurrent: true
- PostgreSQL:
for: data storage
---
### The Problem
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&rsquo;t want to do that on paper for several reasons &ndash; it's easy to lose, a long-running request can run out of space to make notes, etc.
### The Solution
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.
<hidden-section heading="The Process">
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 Tour of myPrayerJournal"](https://blog.bitbadger.solutions/2018/a-tour-of-myprayerjournal/introduction.html "A Tour of myPrayerJournal: Introduction | The Bit Badger Blog") over on the _Bit Badger Blog_ that steps through all aspects of version 1 of this application.
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 [GitHub](https://github.com/bit-badger/myPrayerJournal); we also track open issues there.
</app-hide-section>

View File

@ -0,0 +1,36 @@
---
title: "Not So Extreme Makeover: Community Edition"
url: http://notsoextreme.org
summary: Public site for the makeover; provides event-driven management of volunteers, donations, and families needing help
category: Web Sites and Applications
isInactive: true
noLink: true
linkToArchive: true
archiveUrl: https://nsx.archive.bitbadger.solutions
frontPage:
display: false
technologies:
- WordPress:
for: content management
- PHP:
for: NSXapp
- MySQL:
for: WordPress data storage
- PostgreSQL:
for: NSXapp data storage
---
### The Client
In January 2008, a few members of [Hoffmantown Church](https://www.hoffmantownchurch.org "Hoffmantown Church &bull; Albuquerque, New Mexico") in Albuquerque, New Mexico had an idea. The ABC show _[Extreme Makeover: Home Edition](http://abc.go.com/shows/extreme-makeover-home-edition)_ had just done [a build for a pastor in the &ldquo;war zone&rdquo; area of town](http://abc.go.com/shows/extreme-makeover-home-edition/episode-detail/martinez-family/224884 "Martinez Family &bull; Extreme Makeover: Home Edition"), and this brought attention to Gerald Martinez and the work he had done to help clean up this area of town. Through [Love INC of South Albuquerque](http://www.loveincabq.org/ "Love INC of South Albuquerque"), they learned that there were many other homes in that area that could use the &ldquo;Ty Pennington touch.&rdquo; While the goal was not to knock down homes and build new ones, the goal was no less extreme. The goal of the &ldquo;Not So Extreme Makeover: Community Edition&rdquo; was to help 50 families in 5 days during spring break week in 2008.
### The Problem
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.
### The Solution
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 &ldquo;X Week&rdquo;, 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.
### The Epilogue
From an idea in January, &ldquo;Not So Extreme Makeover: Community Edition&rdquo; 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 <nuxt-link to="/solutions/tcms" title="The Clearinghouse Management System (TCMS) &bull; Bit Badger Solutions">TCMS</nuxt-link>. Finally, there is a [snapshot of the NSX public site](https://nsx.archive.bitbadger.solutions) that serves as a record of those three months in 2008.

View File

@ -0,0 +1,46 @@
---
title: Olivet Baptist Church
url: https://olivet-baptist.org
summary: Southern Baptist church in Gulfport, Mississippi
category: Static Sites
isInactive: true
noLink: true
linkToArchive: true
archiveUrl: https://olivet.archive.bitbadger.solutions
frontPage:
display: false
technologies:
- Vue.js:
for: the user interface for the PWA
- Hexo:
for: generating the site's pages
- Azure:
for: podcast file storage and archive site hosting
- WordPress:
for: content management
- MySQL:
for: data storage
---
### The Client
Olivet Baptist Church was a Southern Baptist church in Gulfport, Mississippi, who had seen our work with <nuxt-link to="/solutions/bay-vista" title="Bay Vista Baptist Church &bull; Bit Badger Solutions">Bay Vista</nuxt-link> and wanted something similar.
### The Problem
Olivet had no online presence.
### The Solution
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.
### The Epilogue
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.
<hidden-section heading="The Process">
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.
In 2018, we modified the site to be a Progressive Web Application (PWA), which allows users to &ldquo;install&rdquo; the site, like an app, to their phone&rsquo;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.
</hidden-section>

View File

@ -0,0 +1,35 @@
---
title: Photography by Michelle
url: https://www.summershome.org
summary: Photography services in Albuquerque, New Mexico
category: Web Sites and Applications
isInactive: true
frontPage:
display: false
technologies:
- ASP.NET MVC:
for: content management / gallery creation API
- PostgreSQL:
for: data storage
- C# / Windows Forms:
for: desktop gallery application
- WordPress:
for: content management
- MySQL:
for: data storage
---
### The Client
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.
### The Problem
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.
### The Solution
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.
### The Epilogue
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.

View File

@ -0,0 +1,50 @@
---
title: PrayerTracker
url: https://prayer.bitbadger.solutions
summary: Provides an ongoing, centralized prayer list for Sunday School classes and other groups
category: Web Sites and Applications
frontPage:
display: true
order: 1
text: A prayer request tracking website (Free for any church or Sunday School class!)
technologies:
- Giraffe:
for: server-side logic and dynamic page generation
isCurrent: true
- PostgreSQL:
for: data storage
isCurrent: true
- GitHub:
for: source code control
isCurrent: true
- GitHub Pages:
for: documentation hosting
isCurrent: true
- MongoDB:
for: data storage
- ASP.NET MVC:
for: dynamic content generation
- Database Abstraction:
for: data access
- MySQL:
for: data storage
- PHP:
for: dynamic content generation
---
### The Problem
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&rsquo;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 &ldquo;latest and greatest&rdquo; list of members.
### The Solution
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 _y Español_. 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.
<hidden-section heading="The Process">
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 <nuxt-link to="/solutions/virtual-prayer-room" title="Virtual Prayer Room &bull; Bit Badger Solutions">Virtual Prayer Room</nuxt-link>. 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.
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.
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 [open source project](https://github.com/bit-badger/PrayerTracker). Right on its heels, version 7.2 moved the embedded help files to GitHub Pages; this made the web application more streamlined.
</hidden-section>

View File

@ -0,0 +1,30 @@
---
title: Riehl World News
url: http://riehlworldview.com
summary: Riehl news for real people
category: WordPress
frontPage:
display: true
order: 3
text: Riehl news for real people
technologies:
- WordPress:
for: blogging
isCurrent: true
- MySQL:
for: data storage
isCurrent: true
- F#:
for: custom archive static page generation
---
### The Client
Dan Riehl began blogging as _The Carnivorous Conservative_ back in 2004, specializing in the areas of crime and politics. He changed to _Riehl World View_ 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.
### The Problem
He wanted to take his blog in a different direction, and was having trouble getting his Movable Type blog to move with him.
### The Solution
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 _Riehl World News_.

View File

@ -0,0 +1,34 @@
---
title: The Clearinghouse Management System
url: http://tcms.us
summary: Assists a needs clearinghouse in connecting people with needs to people that can help meet those needs
category: Web Sites and Applications
isInactive: true
noLink: true
frontPage:
display: false
technologies:
- PHP:
for: the TCMS application logic
- WordPress:
for: publicly-facing pages and authentication
- PostgreSQL:
for: application data storage
- MySQL:
for: WordPress data storage
---
### The Client
Love INC of South Albuquerque runs a &ldquo;needs clearinghouse&rdquo;; 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.
### The Problem
The files in their offices were multiplying; ensuring people&rsquo;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 <nuxt-link to="/solutions/nsx" title="Not So Extreme Makeover: Community Edition &bull; Bit Badger Solutions">&ldquo;Not So Extreme Makeover: Community Edition&rdquo;</nuxt-link>, and thought that the solution we developed for that project would help them.
### The Solution
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.
### The Epilogue
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.

View File

@ -0,0 +1,55 @@
---
title: The Bit Badger Blog
url: https://blog.bitbadger.solutions
summary: Geek stuff from Bit Badger Solutions
category: Static Sites
frontPage:
display: true
order: 3
text: Technical information (&ldquo;geek stuff&rdquo;) from Bit Badger Solutions
technologies:
- Hexo:
for: static site generation
isCurrent: true
- Azure:
for: static site hosting
isCurrent: true
- GitHub:
for: source code control
isCurrent: true
- Custom software:
for: content management
- WordPress:
for: content management
- BlogEngine.NET:
for: content management
- Orchard:
for: content management
- myWebLog:
for: content management
- Jekyll:
for: static site generation
- MySQL:
for: data storage
- SQL Server:
for: data storage
- RethinkDB:
for: data storage
---
### The Problem
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.
### The Solution
_The Bit Badger Blog_ contains that journal, plus tech tips and information for many different aspects of technology. It is written, maintained, and hosted by Bit Badger Solutions.
<hidden-section heading="The Process">
The initial posts were titled &ldquo;My Linux Adventure,&rdquo; 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 &ndash; what a breakthrough! :)
Over time, the _Bit Badger Blog_ (and the _DJS Consulting Tech Blog_ before it) has served as a place to support _(now inactive)_ 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.
It is currently a statically-generated site, utilizing [Hexo](https://hexo.io), and its code is [open source](https://github.com/bit-badger/blog.bitbadger.solutions). 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!
</hidden-section>

View File

@ -0,0 +1,28 @@
---
title: The Shark Tank
url: http://shark-tank.net
summary: Floridas political feeding frenzy
category: WordPress
isInactive: true
noLink: true
frontPage:
display: false
technologies:
- WordPress:
for: blogging
---
### The Client
_The Shark Tank_ is a news and opinion site centered on south Florida politics (and the state at large). They provided extensive coverage of Rep. Allen West&rsquo;s winning campaign in 2010, and continue their focused news and opinion on current political races.
### The Problem
They were displeased with their current theme; it was struggling with the amount of content they were producing.
### The Solution
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&rsquo;s requirements, and helped them turn off parts that they didn&rsquo;t need. We also converted the social media connections from their old site to a style that would work nicely in the new theme.
### The Epilogue
This was all they needed; they returned their focus to their writing.

View File

@ -0,0 +1,30 @@
---
title: Virtual Prayer Room
url: https://virtualprayerroom.us
summary: Gives prayer warriors access to requests from wherever they may be, and sends them daily updates
category: Web Sites and Applications
isInactive: true
noLink: true
frontPage:
display: false
technologies:
- PHP:
for: the application logic
- PostgreSQL:
for: data storage
---
### The Client
Our existing client [Hoffmantown Church](http://hoffmantown.org "Hoffmantown Church &bull; Albuquerque, New Mexico") in Albuquerque, New Mexico, with whom we had worked on the <nuxt-link to="/solutions/nsx/" title="Not So Extreme Makeover: Community Edition &bull; Bit Badger Solutions">Not So Extreme Makeover: Community Edition</nuxt-link>
### The Problem
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.
### The Solution
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&rsquo;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.
### The Epilogue
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.

View File

@ -1,32 +0,0 @@
// @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 } }));
}
};

View File

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

View File

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

View File

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

View File

@ -1,32 +0,0 @@
// 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
});
};

View File

@ -0,0 +1,111 @@
<template>
<div>
<PageHeader />
<Nuxt />
<PageFooter />
</div>
</template>
<script lang="ts">
import Vue from 'vue'
export default Vue.extend({
head () {
return {
link: [
{ rel: 'stylesheet',
href: 'https://fonts.googleapis.com/css?family=Oswald|Raleway'
}
]
}
}
})
</script>
<style>
html {
background-color: lightgray;
}
body {
margin: 0px;
font-family: "Raleway", "Segoe UI", Ubuntu, Tahoma, "DejaVu Sans", "Liberation Sans", Arial, sans-serif;
background-color: #FFFAFA;
}
a {
color: navy;
text-decoration: none;
}
a:hover {
border-bottom: dotted 1px navy;
}
a img {
border: 0;
}
acronym {
border-bottom: dotted 1px black;
}
header, h1, h2, h3, footer a {
font-family: "Oswald", "Segoe UI", Ubuntu, "DejaVu Sans", "Liberation Sans", Arial, sans-serif;
}
h1 {
text-align: center;
margin: 1.4rem 0;
font-size: 2rem;
}
h2 {
margin: 1.2rem 0;
}
h3 {
margin: 1rem 0;
}
h2, h3 {
border-bottom: solid 2px navy;
}
@media all and (min-width:40rem) {
h2, h3 {
width: 80%;
}
}
p {
margin: 1rem 0;
}
#content {
margin: 0 1rem;
}
.content {
font-size: 1.1rem;
}
.auto {
margin: 0 auto;
}
@media all and (min-width: 68rem) {
.content {
width: 66rem;
}
}
.hdr {
font-size: 14pt;
font-weight: bold;
}
.strike {
text-decoration: line-through;
}
.alignleft {
float: left;
padding-right: 5px;
}
ul {
padding-left: 40px;
}
li {
list-style-type: disc;
}
.app-info {
display: flex;
flex-flow: row-reverse wrap;
justify-content: center;
}
abbr[title] {
text-decoration: none;
border-bottom: dotted 1px rgba(0, 0, 0, .5)
}
</style>

View File

@ -0,0 +1,71 @@
export default {
/*
** Nuxt rendering mode
** See https://nuxtjs.org/api/configuration-mode
*/
mode: 'universal',
/*
** Nuxt target
** See https://nuxtjs.org/api/configuration-target
*/
target: 'static',
/*
** Headers of the page
** See https://nuxtjs.org/api/configuration-head
*/
head: {
title: process.env.npm_package_name || '',
meta: [
{ charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
{ hid: 'description', name: 'description', content: process.env.npm_package_description || '' }
],
link: [
{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }
]
},
/*
** Global CSS
*/
css: [
],
/*
** Plugins to load before mounting the App
** https://nuxtjs.org/guide/plugins
*/
plugins: [
],
/*
** Auto import components
** See https://nuxtjs.org/api/configuration-components
*/
components: true,
/*
** Nuxt.js dev-modules
*/
buildModules: [
'@nuxt/typescript-build'
],
/*
** Nuxt.js modules
*/
modules: [
// Doc: https://github.com/nuxt/content
'@nuxt/content'
],
/*
** Content module configuration
** See https://content.nuxtjs.org/configuration
*/
content: {
liveEdit: false,
nestedProperties: ['frontPage.display']
},
/*
** Build configuration
** See https://nuxtjs.org/api/configuration-build/
*/
build: {
}
}

13323
bit-badger-solutions/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,48 +1,28 @@
{ {
"name": "bit-badger-solutions", "name": "bit-badger-solutions",
"version": "3.0.0", "version": "3.0.0",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
},
"private": true, "private": true,
"scripts": {
"dev": "nuxt-ts",
"build": "nuxt-ts build",
"start": "nuxt-ts start",
"generate": "nuxt-ts generate",
"lint:js": "eslint --ext .js,.vue --ignore-path .gitignore .",
"lint": "npm run lint:js"
},
"dependencies": { "dependencies": {
"@angular/animations": "~10.0.5", "@nuxt/content": "^1.5.0",
"@angular/common": "~10.0.5", "@nuxt/typescript-runtime": "^1.0.0",
"@angular/compiler": "~10.0.5", "nuxt": "^2.14.0"
"@angular/core": "~10.0.5",
"@angular/forms": "~10.0.5",
"@angular/platform-browser": "~10.0.5",
"@angular/platform-browser-dynamic": "~10.0.5",
"@angular/router": "~10.0.5",
"rxjs": "~6.5.5",
"tslib": "^2.0.0",
"zone.js": "~0.10.2"
}, },
"devDependencies": { "devDependencies": {
"@angular-devkit/build-angular": "~0.1000.4", "@nuxt/types": "^2.14.0",
"@angular-devkit/schematics": "^10.0.4", "@nuxt/typescript-build": "^2.0.2",
"@angular/cli": "~10.0.4", "@nuxtjs/eslint-config": "^3.1.0",
"@angular/compiler-cli": "~10.0.5", "@nuxtjs/eslint-config-typescript": "^3.0.0",
"@angular/language-service": "~10.0.5", "@nuxtjs/eslint-module": "^2.0.0",
"@types/jasmine": "~3.3.8", "babel-eslint": "^10.1.0",
"@types/jasminewd2": "~2.0.3", "eslint": "^7.5.0",
"@types/node": "^12.11.1", "eslint-plugin-nuxt": "^1.0.0"
"codelyzer": "^6.0.0",
"jasmine-core": "~3.5.0",
"jasmine-spec-reporter": "~5.0.0",
"karma": "~5.0.0",
"karma-chrome-launcher": "~3.1.0",
"karma-coverage-istanbul-reporter": "~3.0.2",
"karma-jasmine": "~3.3.0",
"karma-jasmine-html-reporter": "^1.5.0",
"protractor": "~7.0.0",
"ts-node": "~7.0.0",
"tslint": "~6.1.0",
"typescript": "~3.9.7"
} }
} }

View File

@ -0,0 +1,28 @@
<template>
<article class="content auto">
<h1 v-html="page.title" />
<nuxt-content :document="page" />
<p><br><nuxt-link to="/" title="Home">&laquo; Home</nuxt-link></p>
</article>
</template>
<script lang="ts">
import Vue from 'vue'
import { siteTitle } from '../index.vue'
export default Vue.extend({
async asyncData ({ $content, params }) {
const page = await $content(`about/${params.slug}`).fetch()
return { page }
},
head () {
return { title: siteTitle((this as any).page.title) }
}
})
</script>
<style scoped>
li {
margin-bottom: .8rem;
}
</style>

View File

@ -0,0 +1,53 @@
<template>
<div class="home">
<article class="content auto">
<nuxt-content :document="home" />
</article>
<home-sidebar :cat-names="catNames" :solutions="slns" />
</div>
</template>
<script lang="ts">
import Vue from 'vue'
export default Vue.extend({
async asyncData ({ $content }) {
const home = await $content('home').fetch()
const cats: {category: string}[] = await $content('solutions').only(['category']).fetch()
const catNames = [...new Set(cats.map(x => x.category))]
const slns = await $content('solutions').where({ 'frontPage.display': true }).fetch()
return { home, catNames, slns }
},
head () {
return { title: siteTitle('Welcome!') }
}
})
/**
* Construct a title for the site
*
* @param pageTitle The title of the page
* @returns The page title, with the site title appended
*/
export function siteTitle (pageTitle: string) {
return `${pageTitle} « Bit Badger Solutions`
}
</script>
<style>
@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;
}
</style>

View File

@ -0,0 +1,139 @@
<template>
<section>
<h1>
{{ solution.title }}<br>
<small><small>
<a v-if="!solution.noLink" :href="solution.url" target="_blank">{{ solution.url }}</a>
<span v-else>{{ solution.url }}</span>
<span v-if="solution.linkToArchive"> &nbsp;&nbsp;
<a :href="solution.archiveUrl"><small>(Archive)</small></a>
</span>
</small></small>
</h1>
<div class="app-info">
<article class="content">
<aside>
<span>&nbsp;</span>
<img :src="`/screenshots/${solution.slug}.png`" :alt="`Screen shot of ${solution.title}`">
</aside>
<nuxt-content :document="solution" />
<hidden-section v-if="hasTechStack" heading="The Technology Stack" class="tech-stack">
<template v-if="hasCurrent">
<p v-if="hasCurrent && hasPrevious">
<small><strong>Current:</strong></small>
</p>
<ul>
<technology-item v-for="tech in currentTech" :key="tech[0]" :tech="tech" />
</ul>
</template>
<template v-if="hasPrevious">
<p v-if="hasCurrent && hasPrevious">
<small><strong>Previously:</strong></small>
</p>
<ul>
<technology-item v-for="tech in previousTech" :key="tech[0]" :tech="tech" />
</ul>
</template>
</hidden-section>
<p><br><nuxt-link to="/solutions">&laquo; Back to All Solutions</nuxt-link></p>
</article>
</div>
</section>
</template>
<script lang="ts">
import Vue from 'vue'
import { siteTitle } from '../../index.vue'
export default Vue.extend({
async asyncData ({ $content, params }) {
const solution = await $content(`solutions/${params.id}`).fetch()
return { solution }
},
head () {
return { title: siteTitle((this as any).solution.title + ' « Solution') }
},
computed: {
/**
* Does this solution have a technology stack defined?
*
* @returns True if there are technologies defined, false if not
*/
hasTechStack (): boolean {
return ((this as any).solution.technologies || []).length > 0
},
/**
* Does this solution have any technology marked as current?
*
* @returns True if there are technologies marked as current, false if not
*/
hasCurrent (): boolean {
return (this as any).solution.technologies.filter((x: any) => x[Object.keys(x)[0]].isCurrent).length > 0
},
/**
* Retrieve the current technologies for this solution.
*
* @returns The current technologies for this solution
*/
currentTech (): [] {
return (this as any).solution.technologies
.filter((x: any) => x[Object.keys(x)[0]].isCurrent)
.map((x: any) => [ Object.keys(x)[0], x[Object.keys(x)[0]].for ])
},
/**
* Does this solutio have any technology not marked as current?
*
* @returns True if there are technologies not marked as current, false if not
*/
hasPrevious (): boolean {
return (this as any).solution.technologies.filter((x: any) => !x[Object.keys(x)[0]].isCurrent).length > 0
},
/**
* Retrieve the non-current technologies for this solution.
*
* @returns The non-current technologies for this solution.
*/
previousTech (): [] {
return (this as any).solution.technologies
.filter((x: any) => !x[Object.keys(x)[0]].isCurrent)
.map((x: any) => [ Object.keys(x)[0], x[Object.keys(x)[0]].for ])
}
}
})
</script>
<style scoped>
h1 {
line-height: 1.6rem;
}
aside {
float: right;
background-color: #FFFAFA;
}
aside > span {
padding-left: .75rem;
}
aside > img {
overflow: hidden;
border: dotted 1px darkgray;
border-radius: 10px;
}
.tech-stack p {
margin-bottom: 0;
}
.tech-stack ul {
margin-top: 0;
}
blockquote {
border-left: solid 1px darkgray;
margin-left: 25px;
padding-left: 15px;
}
.quote {
font-style: italic;
}
.source {
text-align: right;
padding-right: 60px;
}
</style>

View File

@ -0,0 +1,32 @@
<template>
<article class="content auto">
<h1>All Solutions</h1>
<h2>Active Solutions</h2>
<solution-item v-for="sln in active" :key="sln.slug" :solution="sln" />
<h2>Past Solutions</h2>
<solution-item v-for="sln in inactive" :key="sln.slug" :solution="sln" />
</article>
</template>
<script lang="ts">
import Vue from 'vue'
import { siteTitle } from '../index.vue'
const sortByName = (x: any, y: any): number =>
x.title.toLowerCase() < y.title.toLowerCase() ? -1 : x.title.toLowerCase() > y.title.toLowerCase() ? 1 : 0
export default Vue.extend({
async asyncData ({ $content }) {
const activeSlns = await $content('solutions')
.where({ isInactive: { $ne: true }, noAboutLink: { $ne: true } }).fetch()
const inactiveSlns = await $content('solutions')
.where({ isInactive: true, noAboutLink: { $ne: true } }).fetch()
const active = activeSlns.sort(sortByName)
const inactive = inactiveSlns.sort(sortByName)
return { active, inactive }
},
head () {
return { title: siteTitle('All Solutions') }
}
})
</script>

View File

@ -1,28 +0,0 @@
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 { }

View File

@ -1,5 +0,0 @@
<app-header></app-header>
<div id="content">
<router-outlet></router-outlet>
</div>
<app-footer></app-footer>

View File

@ -1,35 +0,0 @@
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!');
});
});

View File

@ -1,10 +0,0 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.sass']
})
export class AppComponent {
title = 'bit-badger-solutions';
}

View File

@ -1,39 +0,0 @@
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 { }

View File

@ -1,13 +0,0 @@
import { Component, OnInit } from '@angular/core'
@Component({
selector: 'app-all-solutions-link',
template: `<p><br><a routerLink="/solutions">&laquo; Back to All Solutions</a></p>`
})
export class AllSolutionsLinkComponent implements OnInit {
constructor() { }
ngOnInit() { }
}

View File

@ -1,6 +0,0 @@
import { Type } from '@angular/core'
/** An item representing an app */
export class AppItem {
constructor(public name: string, public component: Type<any>) { }
}

View File

@ -1,10 +0,0 @@
import { Directive, ViewContainerRef } from '@angular/core'
@Directive({
selector: '[app-application-detail]'
})
export class ApplicationDetailDirective {
constructor(public viewContainerRef: ViewContainerRef) { }
}

View File

@ -1,10 +0,0 @@
<h1>
{{ app.name }}<br>
<small><small>
<a *ngIf="linkToApp" [href]="app.url">{{ app.url }}</a>
<span *ngIf="!linkToApp">{{ app.url }}</span>
<span *ngIf="linkToArchive">&nbsp; &nbsp;
<a [href]="app.archiveUrl"><small>(Archive)</small></a>
</span>
</small></small>
</h1>

View File

@ -1,28 +0,0 @@
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 > '')
}
}

View File

@ -1 +0,0 @@
<aside><span>&nbsp;</span><img [src]="imageLink" [alt]="imageAlt"></aside>

View File

@ -1,9 +0,0 @@
aside
float: right
background-color: #FFFAFA
aside > span
padding-left: .75rem
aside > img
overflow: hidden
border: dotted 1px darkgray
border-radius: 10px

View File

@ -1,29 +0,0 @@
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}`
}
}

View File

@ -1,9 +0,0 @@
<p>
<span class="app-name" [innerHtml]="app.name"></span>&nbsp;~&nbsp;<a routerLink="/solutions/{{ app.id }}">About</a>
<span *ngIf="app.isActive">&nbsp;~&nbsp;<a [href]="app.url">Visit</a></span>
<span *ngIf="!app.isActive && app.archiveUrl">
~&nbsp;<a [href]="app.archiveUrl">Visit</a><em> (archive)</em>
</span>
<br>
<span [innerHtml]="app.indexText"></span>
</p>

View File

@ -1,5 +0,0 @@
.app-name
font-family: "Oswald", "Segoe UI", Ubuntu, "DejaVu Sans", "Liberation Sans", Arial, sans-serif
font-size: 1.3rem
font-weight: bold
color: maroon

View File

@ -1,18 +0,0 @@
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() { }
}

View File

@ -1,8 +0,0 @@
<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>

View File

@ -1,25 +0,0 @@
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)
})
}
}

View File

@ -1,87 +0,0 @@
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
})
}
}

View File

@ -1,323 +0,0 @@
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 (&ldquo;geek stuff&rdquo;) 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 &ldquo;rising star&rdquo; 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&rsquo;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&rsquo;s miscellany of compendious amusements'
futilityCloset.frontPageOrder = 2
futilityCloset.indexText = 'An idler&rsquo;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&rsquo;s never failed
in being friendly, knowledgeable, thoughtful, and farsighted. I&rsquo;ve literally lost count of the number of times
he&rsquo;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&rsquo;t for
his reliability, expertise, and good judgment. And he&rsquo;s a joy to work with.`
fcQuote.pull = [
`Daniel&rsquo;s never failed in being friendly, knowledgeable, thoughtful, and farsighted&hellip;`,
`We would be out of business many times over if it weren&rsquo;t for his reliability, expertise, and good
judgment&hellip;`
]
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&rsquo;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&rsquo;s terrific!`
mmQuote.pull = [
'&hellip;Daniel is able to produce high quality work on short time frames&hellip;',
'[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 = 'Floridas 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
]
}

View File

@ -1,48 +0,0 @@
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))
}
}

View File

@ -1,96 +0,0 @@
/** A category of application */
export class Category {
/**
* Construct a new instance
* @param id The ID of the category
* @param name The name of the category
*/
constructor(public id: number, public name: string) {
this.id = id
this.name = name
}
/** Sites/applications not otherwise specified */
static SITES_APPS = 1
/** WordPress sites */
static WORDPRESS = 2
/** Statically generated sites */
static STATIC = 3
/** Daniel's personal sites */
static PERSONAL = 99
}
/** A quote from an app */
export class Quote {
/** The full text of the quote */
full: string = ''
/** Shorter portions of the full quote */
pull: string[] = []
/**
* Construct a new instance
* @param name The name of the person who provided the quote
* @param from What organization the person who provided the quote represents
*/
constructor(public name: string, public from: string) { }
}
/** A description of a part of the technology stack used */
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, public current: boolean = false) { }
}
/** An application or web site */
export class App {
/** Whether this app is active (default true) */
isActive: boolean = true
/** The ID of the category to which this app belongs (default "Web Sites and Applictions") */
categoryId: number = Category.SITES_APPS
/** Whether to skip displaying an About link on the front page */
noAboutLink: boolean = false
/** Whether the generate a link for an inactive app (defaults to false) */
linkInactive: boolean = false
/** The text to use for the short description in the front page sidebar */
frontPageText: string = ''
/** The order (within category) in which this app should be displayed */
frontPageOrder: number = 0
/** The text to display for the application on this index page */
indexText: string = ''
/** The URL where an archived version of this app may be found */
archiveUrl: string = ''
/** The technology used for this app */
techStack: Technology[] = []
/** Customer quotes */
quotes: Quote[] = []
/**
* Construct a new instance
* @param id The ID of the app
* @param name The name of the app
* @param url The URL of the app
*/
constructor(public id: string, public name: string, public url: string) { }
}

View File

@ -1,99 +0,0 @@
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 { }

View File

@ -1,4 +0,0 @@
<h3 (click)="toggle()">{{ heading }}<span class="arrow" [innerHtml]="label"></span></h3>
<div *ngIf="shown" [@slideInOut]>
<ng-content></ng-content>
</div>

View File

@ -1,6 +0,0 @@
h3:hover
cursor: hand
cursor: pointer
.arrow
font-size: .75rem
padding-left: 1rem

View File

@ -1,38 +0,0 @@
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 = '&#x25BC;'
shown = false
constructor() { }
ngOnInit() { }
toggle() {
this.shown = !this.shown
this.label = this.shown ? '&#x25B2;' : '&#x25BC;'
}
}

View File

@ -1,10 +0,0 @@
<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">
&mdash; <strong>{{ quote.name }}</strong>
<span *ngIf="quote.from">, {{ quote.from }}</span>
</p>
</blockquote>
</div>

View File

@ -1,9 +0,0 @@
blockquote
border-left: solid 1px darkgray
margin-left: 25px
padding-left: 15px
.quote
font-style: italic
.source
text-align: right
padding-right: 60px

View File

@ -1,18 +0,0 @@
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() { }
}

View File

@ -1,14 +0,0 @@
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`
}
}

View File

@ -1,47 +0,0 @@
<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&rsquo;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&rsquo;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>

View File

@ -1,18 +0,0 @@
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()
}
}

View File

@ -1,39 +0,0 @@
<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 &ldquo;My Linux Adventure,&rdquo; 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 &ndash; 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>

View File

@ -1,18 +0,0 @@
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()
}
}

View File

@ -1,39 +0,0 @@
<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>

View File

@ -1,18 +0,0 @@
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()
}
}

View File

@ -1,38 +0,0 @@
<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>&rsquo;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>

View File

@ -1,18 +0,0 @@
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()
}
}

View File

@ -1,44 +0,0 @@
<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&reg;, 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&rsquo;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>

View File

@ -1,18 +0,0 @@
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()
}
}

View File

@ -1,39 +0,0 @@
<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 &ldquo;forgotten stories from the pages of
history,&rdquo; 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>

View File

@ -1,18 +0,0 @@
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()
}
}

View File

@ -1,23 +0,0 @@
<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>

View File

@ -1,18 +0,0 @@
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()
}
}

View File

@ -1,43 +0,0 @@
<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>

Some files were not shown because too many files have changed in this diff Show More