commit 0143a647359d88bbfea9e08e4634e4be94a1d2cd Author: David Calavera Date: Mon Nov 21 14:52:41 2016 -0800 :sunrise: Signed-off-by: David Calavera diff --git a/.babelrc b/.babelrc new file mode 100644 index 0000000..db848ed --- /dev/null +++ b/.babelrc @@ -0,0 +1,7 @@ +{ + "presets": ["es2015"], + "plugins": [ + "syntax-object-rest-spread", + "transform-object-rest-spread" + ] +} diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..e1b082e --- /dev/null +++ b/.eslintrc @@ -0,0 +1,101 @@ +env: + browser: true + +parser: babel-eslint + +plugins: [ "import" ] + +# enable ECMAScript features +ecmaFeatures: + arrowFunctions: true + binaryLiterals: true + blockBindings: true + classes: true + defaultParams: true + destructuring: true + forOf: true + generators: true + jsx: true + modules: true + objectLiteralShorthandMethods: true + objectLiteralShorthandProperties: true + octalLiterals: true + spread: true + templateStrings: true + +rules: + # Possible Errors + # https://github.com/eslint/eslint/tree/master/docs/rules#possible-errors + no-control-regex: 2 + no-console: 1 + no-debugger: 2 + no-dupe-args: 2 + no-dupe-keys: 2 + no-duplicate-case: 2 + no-empty-character-class: 2 + no-ex-assign: 2 + no-extra-boolean-cast : 2 + no-extra-semi: 2 + no-invalid-regexp: 2 + no-irregular-whitespace: 1 + no-proto: 2 + no-unexpected-multiline: 2 + no-unreachable: 2 + valid-typeof: 2 + + # Best Practices + # https://github.com/eslint/eslint/tree/master/docs/rules#best-practices + no-fallthrough: 2 + no-redeclare: 2 + + # Stylistic Issues + # https://github.com/eslint/eslint/tree/master/docs/rules#stylistic-issues + comma-spacing: 2 + eol-last: 2 + eqeqeq: ["error", "smart"] + indent: [2, 2, {SwitchCase: 1}] + keyword-spacing: 2 + max-len: [1, 160, 2] + new-parens: 2 + no-mixed-spaces-and-tabs: 2 + no-multiple-empty-lines: [2, {max: 2}] + no-trailing-spaces: 2 + object-curly-spacing: [2, "never"] + quotes: [2, "double", "avoid-escape"] + semi: 2 + space-before-blocks: [2, "always"] + space-before-function-paren: [2, "never"] + space-in-parens: [2, "never"] + space-infix-ops: 2 + space-unary-ops: 2 + + # ECMAScript 6 + # http://eslint.org/docs/rules/#ecmascript-6 + arrow-parens: [2, "always"] + arrow-spacing: [2, {"before": true, "after": true}] + no-confusing-arrow: 2 + prefer-const: 2 + + # JSX + jsx-quotes: [2, "prefer-double"] + + # Import + import/no-unresolved: [1, {"commonjs": true, "amd": true}] + import/export: 2 + + # Strict Mode + # https://github.com/eslint/eslint/tree/master/docs/rules#strict-mode + strict: [2, "global"] + + # Variables + # https://github.com/eslint/eslint/tree/master/docs/rules#variables + no-undef: 2 + no-unused-vars: [2, {"args": "none"}] + +# Global scoped method and vars +globals: + __dirname: true + require: true + process: true + ENV: true + module: true diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 0000000..4a02feb --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,27 @@ + + +**- Do you want to request a *feature* or report a *bug*?** + +**- What is the current behavior?** + +**- If the current behavior is a bug, please provide the steps to reproduce.** + +**- What is the expected behavior?** + +**- Please mention your node.js, and operating system version.** diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..9a63fa1 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,35 @@ + + +**- Summary** + + + +**- Test plan** + + + +**- Description for the changelog** + + + +**- A picture of a cute animal (not mandatory but encouraged)** diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c5ea0d4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +node_modules/ +dist/ +npm-debug.log diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..2867c8a --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,74 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of experience, +nationality, personal appearance, race, religion, or sexual identity and +orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or +advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at david@netlify.com. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..e96b919 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,47 @@ +# CONTRIBUTING + +Contributions are always welcome, no matter how large or small. Before contributing, +please read the [code of conduct](CODE_OF_CONDUCT.md). + +## Setup + +> Install yarn on your system: https://yarnpkg.com/en/docs/install + +```sh +$ git clone https://github.com/netlify/netlify-statuskit +$ cd netlify-statuskit +$ yarn +``` + +## Building + +```sh +$ npm run build +``` + +## Testing + +```sh +$ npm run test +``` + +## Runing the server + +```sh +$ npm run start +``` + +## Pull Requests + +We actively welcome your pull requests. + +1. Fork the repo and create your branch from `master`. +2. If you've added code that should be tested, add tests. +3. If you've changed APIs, update the documentation. +4. Ensure the test suite passes. +5. Make sure your code lints. + +## License + +By contributing to Netlify StatusKit, you agree that your contributions will be licensed +under its [MIT license](LICENSE). diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..7936a46 --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +Copyright (c) 2016 Netlify + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..a0cd575 --- /dev/null +++ b/README.md @@ -0,0 +1,58 @@ +# Introduction + +Netlify StatusKit is a template to deploy your own Status pages on Netlify. + +Netlify StatusKit is released under the [MIT License](LICENSE). +Please make sure you understand its [implications and guarantees](https://writing.kemitchell.com/2016/09/21/MIT-License-Line-by-Line.html). + +[![Deploy to Netlify](https://www.netlify.com/img/deploy/button.svg)](https://app.netlify.com/start/deploy?repository=https://github.com/netlify/netlify-statuskit) + +## Initial configuration + +Click in the Deploy to Netlify button above to create your own site directly and push this repository to your own account. +Before creating the site, Netlify will ask you to fill required environment variables listed here: + +- `STATUSKIT_PAGE_TITLE` - Title to show in the browser for your status site. +- `STATUSKIT_COMPANY_LOGO` - URL to your company's logo. +- `STATUSKIT_SUPPORT_CONTACT_LINK` - URL to a support page for your users to talk with you. +- `STATUSKIT_RESOURCES_LINK` - URL to documentation for your users. +- `STATUSKIT_FAVICONS_PATH` - Path where the favicons for different statuses are. By default, we use `/images/favicon/favicon-ok.ico` when there are no incidents, `/images/favicon/favicon-warning.ico` when there are no major incidents, and `/images/favicon/favicon-danger.ico` when there are major incidents. This path can be an external URL, like `https://my-favourite-cdn/images/status-favicons`. The names for the different statuses must be `favicon-ok.ico`, favicon-warning.ico` and `favicon-danger.ico`. + +## Extra configuration + +After the site is created, you can modify the code as much as you want and push it to your GitHub repository. Netlify will pick up changes from there. + +### Reporting systems + +You can add systems you want to report about to your Status page. For instance, you might want to tell your users about a status change in your CDN infrastructure but not in your API. + +Go to `site/config.toml` and change the global `systems` variables. Once that's done, you'll be able to change the status of each one of those systems individually when you open or modify an incident. + +### Full customization + +This template is based in [Netlify's Victor-Hugo](https://github.com/netlify/victor-hugo) boilerplate. +To work on it you'll need NPM installed. To download dependencies type `npm run dependencies`, that will check if you have Hugo installed and will download it for you if you don't. It will also run `npm install` for the first time to download extra dependencies. After that, you can run `npm install` every time you want to install packages. + +## Creating incidents + +Adding incidents to your status page is as simple as adding a new document to the incidents collection. +Create a new incident using Hugo with a command like this: + +``` +hugo -s site new -k incidents incidents/oh-no-something-went-wrong.md +``` + +Hugo will create a new Markdown file for you with title and date based on the file name and a few predefined settings in the header. To learn more about the different states and report, you can see more detailed examples in `site/archetypes/incidents.md`. + +After explaining the current situation in the incident, you can just push the file to GitHub. Netlify will deploy the indicent announcement for you in a matter of seconds. + +## Resolving incidents + +Everything will be operational again when all incidents are marked with `resolved = true`. + +# Development + +Netlify StatusKit uses NPM to manage dependencies. It also bundles a version of Hugo to work out of the box. + +1. Use `npm install` to download dependencies. +2. Use `npm start` to start the development server. diff --git a/bin/hugo_0.17_darwin_amd64 b/bin/hugo_0.17_darwin_amd64 new file mode 100755 index 0000000..963e875 Binary files /dev/null and b/bin/hugo_0.17_darwin_amd64 differ diff --git a/bin/hugo_0.17_linux_amd64 b/bin/hugo_0.17_linux_amd64 new file mode 100755 index 0000000..92cf30b Binary files /dev/null and b/bin/hugo_0.17_linux_amd64 differ diff --git a/bin/hugo_0.17_windows_amd64.exe b/bin/hugo_0.17_windows_amd64.exe new file mode 100755 index 0000000..60a9b21 Binary files /dev/null and b/bin/hugo_0.17_windows_amd64.exe differ diff --git a/gulpfile.babel.js b/gulpfile.babel.js new file mode 100644 index 0000000..b7bb68d --- /dev/null +++ b/gulpfile.babel.js @@ -0,0 +1,65 @@ +import gulp from "gulp"; +import cp from "child_process"; +import gutil from "gulp-util"; +import postcss from "gulp-postcss"; +import cssImport from "postcss-import"; +import cssnext from "postcss-cssnext"; +import BrowserSync from "browser-sync"; +import webpack from "webpack"; +import webpackConfig from "./webpack.conf"; + +const browserSync = BrowserSync.create(); +const hugoBin = `./bin/hugo_0.17_${process.platform}_amd64${process.platform === "windows" ? ".exe" : ""}`; +const defaultArgs = ["-d", "../dist", "-s", "site", "-v"]; + +gulp.task("hugo", (cb) => buildSite(cb)); +gulp.task("hugo-preview", (cb) => buildSite(cb, ["--buildDrafts", "--buildFuture"])); + +gulp.task("build", ["css", "js", "hugo"]); +gulp.task("build-preview", ["css", "js", "hugo-preview"]); + +gulp.task("css", () => ( + gulp.src("./src/css/*.css") + .pipe(postcss([cssnext(), cssImport({from: "./src/css/main.css"})])) + .pipe(gulp.dest("./dist/css")) + .pipe(browserSync.stream()) +)); + +gulp.task("js", (cb) => { + const myConfig = Object.assign({}, webpackConfig); + + webpack(myConfig, (err, stats) => { + if (err) throw new gutil.PluginError("webpack", err); + gutil.log("[webpack]", stats.toString({ + colors: true, + progress: true + })); + browserSync.reload(); + cb(); + }); +}); + +gulp.task("server", ["hugo", "css", "js"], () => { + browserSync.init({ + server: { + baseDir: "./dist" + } + }); + gulp.watch("./src/js/**/*.js", ["js"]); + gulp.watch("./src/css/**/*.css", ["css"]); + gulp.watch("./site/**/*", ["hugo"]); +}); + +function buildSite(cb, options) { + const args = options ? defaultArgs.concat(options) : defaultArgs; + + return cp.spawn(hugoBin, args, {stdio: "inherit"}).on("close", (code) => { + if (code === 0) { + browserSync.reload(); + cb(); + } else { + browserSync.notify("Hugo build failed :("); + cb("Hugo build failed"); + } + }); +} diff --git a/netlify.toml b/netlify.toml new file mode 100644 index 0000000..3a8cc0f --- /dev/null +++ b/netlify.toml @@ -0,0 +1,13 @@ +[build] + command = "npm run build" + publish = "dist" + +[context.deploy-preview] + command = "npm run build-preview" + +[template.environment] + STATUSKIT_PAGE_TITLE = "title for the page" + STATUSKIT_COMPANY_LOGO = "url to your company's logo" + STATUSKIT_SUPPORT_CONTACT_LINK = "url to your support page" + STATUSKIT_RESOURCES_LINK = "url to your knowledge base page" + STATUSKIT_FAVICONS_PATH = "/images/favicons/" diff --git a/package.json b/package.json new file mode 100644 index 0000000..723646c --- /dev/null +++ b/package.json @@ -0,0 +1,47 @@ +{ + "name": "netlify-statusKit", + "version": "1.0.0", + "description": "StatusKit is a template to build Status pages for your business", + "main": "index.js", + "scripts": { + "hugo": "gulp hugo", + "webpack": "gulp webpack", + "build": "gulp build", + "build-preview": "gulp build-preview", + "start": "gulp server", + "lint": "eslint src" + }, + "author": "", + "license": "MIT", + "dependencies": { + "autoprefixer": "^6.3.7", + "babel-eslint": "^6.1.2", + "babel-loader": "^6.2.4", + "babel-plugin-syntax-object-rest-spread": "^6.13.0", + "babel-plugin-transform-class-properties": "^6.10.2", + "babel-plugin-transform-object-assign": "^6.8.0", + "babel-plugin-transform-object-rest-spread": "^6.8.0", + "babel-preset-es2015": "^6.9.0", + "babel-register": "^6.11.6", + "browser-sync": "^2.13.0", + "css-loader": "^0.23.1", + "eslint": "^3.1.1", + "eslint-plugin-import": "^1.11.1", + "exports-loader": "^0.6.3", + "file-loader": "^0.9.0", + "gulp": "^3.9.1", + "gulp-babel": "^6.1.2", + "gulp-postcss": "^6.1.1", + "gulp-util": "^3.0.7", + "imports-loader": "^0.6.5", + "postcss-cssnext": "^2.7.0", + "postcss-import": "^8.1.2", + "postcss-loader": "^0.9.1", + "url-loader": "^0.5.7", + "webpack": "^1.13.1", + "whatwg-fetch": "^1.0.0" + }, + "optionalDependencies": { + "fsevents": "^1.0.15" + } +} diff --git a/site/archetypes/incidents.md b/site/archetypes/incidents.md new file mode 100644 index 0000000..719b635 --- /dev/null +++ b/site/archetypes/incidents.md @@ -0,0 +1,30 @@ ++++ +# default attributes for an incident. +# +# Hugo adds `title` and `date` by default +# when running `hugo new incidents/new-incident.md`, +# so we don't have to specify them here. + +# state: represents the global state of +# your system due to the current incident. +# This is the list of supported states: +# +# - under-maintenance +# - degraded-performance +# - partial-outage +# - major-outage +# +state = "degraded-performance" + +# affectedsystem: is a list of systems affected +# by the incident. +# Example: +# affectedsytems = ["API", "Build servers"] +# +affectedsytems = ["API", "Build servers"] + +# resolved: marks an incident as resolved. +# It can be either true or false. +# +resolved = false ++++ diff --git a/site/config.toml b/site/config.toml new file mode 100644 index 0000000..4a6fc88 --- /dev/null +++ b/site/config.toml @@ -0,0 +1,17 @@ +baseurl = "/" +languageCode = "en-us" +title = "StatusKit" + +[permalinks] + incidents = "/incidents/:year/:month/:day/:slug/" + +[params] + defaultLogo = "https://www.netlify.com/img/press/logos/logomark.svg" + faviconsPath = "/images/favicon/" + +# List of systems monitored in this status page. +# You'll be able to change their status every time +# you open or update an incident. +# Example: +# systems = ["API", "CDN", "DNS", "Site delivery"] +systems = [] diff --git a/site/content/.keep b/site/content/.keep new file mode 100644 index 0000000..e69de29 diff --git a/site/content/incidents/.keep b/site/content/incidents/.keep new file mode 100644 index 0000000..e69de29 diff --git a/site/data/.keep b/site/data/.keep new file mode 100644 index 0000000..e69de29 diff --git a/site/data/states.toml b/site/data/states.toml new file mode 100644 index 0000000..8b69bbc --- /dev/null +++ b/site/data/states.toml @@ -0,0 +1,13 @@ +[descriptions] + ok = "Up and running" + under-maintenance = "Under maintenance" + degraded-performance = "Degraded performance" + partial-outage = "Partial outage" + major-outage = "Major outage" + +[alerts] + ok = "ok" + under-maintenance = "warning" + degraded-performance = "warning" + partial-outage = "warning" + major-outage = "danger" diff --git a/site/layouts/incidents/single.html b/site/layouts/incidents/single.html new file mode 100644 index 0000000..51619ce --- /dev/null +++ b/site/layouts/incidents/single.html @@ -0,0 +1,4 @@ +{{ partial "header" . }} + +{{ partial "incident" . }} + diff --git a/site/layouts/index.html b/site/layouts/index.html new file mode 100644 index 0000000..0b7c045 --- /dev/null +++ b/site/layouts/index.html @@ -0,0 +1,31 @@ +{{ partial "header" . }} + +{{ $incidents := where .Site.Pages.ByDate.Reverse "Section" "incidents" }} +{{ $active := where $incidents "Params.resolved" "!=" true }} + +{{ partial "systems" (dict "content" . "incidents" $active) }} + +{{ with $active }} +
Active Incidents
+ {{ range $active }} + {{ partial "incident" . }} + {{ end }} +{{ else }} + {{ $latest := index $incidents 0 }} + {{ partial "all-clear" $latest }} +{{ end }} + +{{ $recents := first 3 (where $incidents "Params.resolved" "==" true) }} +{{ with $recents }} +
+
Past Incidents
+ {{ range $recents }} + {{ partial "incident" . }} + {{ end }} + See all incidents +
+{{ end }} + +{{ partial "footer" . }} + + diff --git a/site/layouts/partials/all-clear.html b/site/layouts/partials/all-clear.html new file mode 100644 index 0000000..2137007 --- /dev/null +++ b/site/layouts/partials/all-clear.html @@ -0,0 +1,10 @@ +
+ + {{ with .Date }} + {{ $latestDate := dateFormat "2006-01-02T15:04:05" .Date}} +

48+ days since last incident

+ {{ else }} +

No incidents so far, all is good

+ {{ end }} +
+ diff --git a/site/layouts/partials/footer.html b/site/layouts/partials/footer.html new file mode 100644 index 0000000..ed5500d --- /dev/null +++ b/site/layouts/partials/footer.html @@ -0,0 +1,5 @@ + diff --git a/site/layouts/partials/header.html b/site/layouts/partials/header.html new file mode 100644 index 0000000..6da62f1 --- /dev/null +++ b/site/layouts/partials/header.html @@ -0,0 +1,37 @@ + +{{ $logo := getenv "STATUSKIT_COMPANY_LOGO" | default .Site.Params.defaultLogo }} +{{ $title := getenv "STATUSKIT_PAGE_TITLE" | default .Site.Title }} +{{ $resources := getenv "STATUSKIT_RESOURCES_LINK" | default .Site.Params.resourcesLink }} +{{ $contact := getenv "STATUSKIT_SUPPORT_CONTACT_LINK" | default .Site.Params.contactLink }} +{{ $favicons := getenv "STATUSKIT_FAVICONS_PATH" | default .Site.Params.faviconsPath }} + +{{ $incidents := where .Site.Pages.ByDate.Reverse "Section" "incidents" }} +{{ $active := where $incidents "Params.resolved" "!=" true }} +{{ $major := where $active "Params.state" "major-outage" }} + + + + {{ $title }} + + {{ if $major }} + + {{ else if $active }} + + {{ else }} + + {{ end }} + + +
+
+ + {{ $title }} + +
diff --git a/site/layouts/partials/incident.html b/site/layouts/partials/incident.html new file mode 100644 index 0000000..a0e5a7a --- /dev/null +++ b/site/layouts/partials/incident.html @@ -0,0 +1,14 @@ +
+
+

{{ .Title }}

+

{{ dateFormat "02 Jan 2006 15:04 -0700 MST" .Date }}

+
+
+ {{- .Content -}} +
+ {{- $alert := index .Site.Data.states.alerts .Params.state -}} + +

+ {{ index .Site.Data.states.descriptions .Params.state }} +

+
diff --git a/site/layouts/partials/paginator.html b/site/layouts/partials/paginator.html new file mode 100644 index 0000000..bdd7ebb --- /dev/null +++ b/site/layouts/partials/paginator.html @@ -0,0 +1,31 @@ +{{ $pag := $.Paginator }} +{{ if gt $pag.TotalPages 1 }} +
    + {{ if gt $pag.TotalPages 5 }} + {{ with $pag.First }} +
  • + +
  • + {{ end }} + {{ end }} +
  • + +
  • + {{ range $pag.Pagers }} +
  • {{ .PageNumber }}
  • + {{ end }} +
  • + +
  • + {{ if gt $pag.TotalPages 5 }} + {{ with $pag.Last }} +
  • + +
  • + {{ end }} + {{ end }} +
+{{ end }} diff --git a/site/layouts/partials/status-badge.html b/site/layouts/partials/status-badge.html new file mode 100644 index 0000000..efb1f98 --- /dev/null +++ b/site/layouts/partials/status-badge.html @@ -0,0 +1,5 @@ +
+ + + {{ .description }} +
diff --git a/site/layouts/partials/systems.html b/site/layouts/partials/systems.html new file mode 100644 index 0000000..36ebffd --- /dev/null +++ b/site/layouts/partials/systems.html @@ -0,0 +1,29 @@ +{{ $okAlert := index .content.Site.Data.states.alerts "ok"}} +{{ $okDesc := index .content.Site.Data.states.descriptions "ok" }} + +{{ with .content.Site.Params.systems }} +
+ {{ range . }} + {{ $name := . }} +
+
+ {{ $name }} + + {{ if $.incidents }} + {{ range $.incidents }} + {{ if in .Params.affectedsystems $name }} + {{ $alert := index .Site.Data.states.alerts .Params.state }} + {{ $desc := index .Site.Data.states.descriptions .Params.state }} + {{ partial "status-badge" (dict "alert" $alert "description" $desc) }} + {{ else }} + {{ partial "status-badge" (dict "alert" $okAlert "description" $okDesc) }} + {{ end }} + {{ end }} + {{ else }} + {{ partial "status-badge" (dict "alert" $okAlert "description" $okDesc) }} + {{ end }} +
+
+ {{ end }} +
+{{ end }} diff --git a/site/layouts/section/incidents.html b/site/layouts/section/incidents.html new file mode 100644 index 0000000..7f0471f --- /dev/null +++ b/site/layouts/section/incidents.html @@ -0,0 +1,16 @@ +{{ partial "header" . }} + +
+{{ range (.Paginate .Data.Pages.ByPublishDate 15 ).Pages }} +
+ + {{ .Title }} + + +
+{{ end }} + +{{ partial "paginator" . }} +
diff --git a/site/static/.keep b/site/static/.keep new file mode 100644 index 0000000..e69de29 diff --git a/site/static/images/favicon/favicon-danger.ico b/site/static/images/favicon/favicon-danger.ico new file mode 100644 index 0000000..8d8c3a4 Binary files /dev/null and b/site/static/images/favicon/favicon-danger.ico differ diff --git a/site/static/images/favicon/favicon-ok.ico b/site/static/images/favicon/favicon-ok.ico new file mode 100644 index 0000000..fb829a1 Binary files /dev/null and b/site/static/images/favicon/favicon-ok.ico differ diff --git a/site/static/images/favicon/favicon-warning.ico b/site/static/images/favicon/favicon-warning.ico new file mode 100644 index 0000000..8a8b090 Binary files /dev/null and b/site/static/images/favicon/favicon-warning.ico differ diff --git a/site/static/images/icon-danger.svg b/site/static/images/icon-danger.svg new file mode 100644 index 0000000..c309288 --- /dev/null +++ b/site/static/images/icon-danger.svg @@ -0,0 +1,3 @@ + + + diff --git a/site/static/images/icon-large-ok.svg b/site/static/images/icon-large-ok.svg new file mode 100644 index 0000000..6d96538 --- /dev/null +++ b/site/static/images/icon-large-ok.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/site/static/images/icon-ok.svg b/site/static/images/icon-ok.svg new file mode 100644 index 0000000..77bdb62 --- /dev/null +++ b/site/static/images/icon-ok.svg @@ -0,0 +1,3 @@ + + + diff --git a/site/static/images/icon-warning.svg b/site/static/images/icon-warning.svg new file mode 100644 index 0000000..9738fa4 --- /dev/null +++ b/site/static/images/icon-warning.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/css/imports/reset.css b/src/css/imports/reset.css new file mode 100644 index 0000000..be8c05d --- /dev/null +++ b/src/css/imports/reset.css @@ -0,0 +1,4 @@ +body { + font-size: 16px; + margin: 0; +} diff --git a/src/css/main.css b/src/css/main.css new file mode 100644 index 0000000..7b66bab --- /dev/null +++ b/src/css/main.css @@ -0,0 +1,290 @@ +@import "imports/reset.css"; + +body { + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; + color: #202020; + font-size: 16px; +} + +a { + color: #3A71B0; + text-decoration: none; +} + +a:hover, +a:focus, +a:active { + text-decoration: underline; +} + + +.gangsta-wrap { + max-width: 800px; + margin: auto; + padding: 16px; + padding-bottom: 104px; +} + +.header { + margin-bottom: 40px; + padding: 16px 0 24px 0; + border-bottom: 2px solid #F7F7F8; +} + +.logo { + position: relative; + top: 4px; + height: 24px; + margin-right: 8px; +} + +.title { + font-size: 21px; + font-weight: 500; + letter-spacing: 0; + line-height: 24px; +} + +.title a { + color: #202020; +} +.title a:hover, +.title a:focus, { + text-decoration: none; +} + +.right-links { + float: right; +} + +@media (max-width: 450px) { + .right-links { + float: left; + margin-top: 24px; + } + + .header { + padding-bottom: 68px; + } +} + +.button { + position: relative; + top: 4px; + padding: 8px 16px 8px 16px; + font-size: 14px; + font-weight: 600; + color: #202020; + letter-spacing: 0; + line-height: 24px; + text-decoration: none; + border: 2px solid #202020; + border-radius: 4px; + background: #fff; + transition: all 0.3s ease; +} + +.button:hover, +.button:active, +.button:focus { + text-decoration: none; + background: #202020; + color: #fff; +} + +.contact-link { + margin-left: 16px; +} + +.systems { + display: flex; + flex-wrap: wrap; +} + +.card { + display: flex; + flex-direction: column; + justify-content: flex-start; + overflow: hidden; + margin-bottom: 24px; + padding: 16px; + border-radius: 10px; + background: #fff; + box-shadow: 0px 8px 20px 0px rgba(19, 39, 48, .08); + + font-size: 16px; + font-weight: 500; + color: #202020; +} + +.system-operational { + flex-grow: 1; + margin-right: 16px; +} + +.system-operational:last-child { + margin-right: 0; +} + +.system-status-badge { + margin-top: 8px; +} + +.system-status-badge img { + position: relative; + top: 2px; + margin-right: 4px; + height: 16px; +} + +.incident { + color: #202020; +} + +.incident-title { + float: left; + text-transform: capitalize; +} + +.incident-date { + float: right; + font-size: 14px; + opacity: 0.3; +} + +@media (max-width: 450px) { + .incident-date { + width: 100%; + } +} + +.incident-description { + font-size: 14px; + font-weight: 400; +} + +.incident-status { + margin: 24px 0 0 0; +} + +.system-check-ok { + margin: 80px auto 104px auto; + width: 100%; + max-width: 300px; + text-align: center; + font-weight: 500; + color: #009A8D; /*Switch to appropriate color*/ +} + +.system-check-ok img { + margin: auto; + width: 140px; +} + +.section-title { + margin-top: 40px; + margin-bottom: 16px; + padding-left: 16px; + padding-bottom: 16px; + font-size: 16px; + font-weight: 500; + border-bottom: 2px solid #F7F7F8; + color: #bbb; +} + +.see-more { + display: block; + width: 100%; + text-align: center; + margin-top: 40px; + padding-top: 24px; + border-top: 2px solid #F7F7F8; +} + +.history { + margin-top: 104px; +} + +.color-ok { + color: #009A8D; +} + +.color-warning { + color: #DFAA00; +} + +.color-danger { + color: #BD1F1A; +} + +.incident-summary { + margin-bottom: 24px; + overflow: hidden; +} + +.article-title { + width: 70%; + float: left; + text-transform: capitalize; +} + +.article-date { + width: 29%; + float: right; + text-align: right; + font-size: 14px; + opacity: 0.3; +} + +.pagination, +.pagination li { + list-style: none; + display: inline; + -webkit-padding-start: 0px; + margin-right: 8px; +} + +.pagination { + display: block; + margin-top: 40px; + padding-top: 24px; + border-top: 2px solid #F7F7F8; +} + +.pagination .active a { + font-weight: 600; + color: #3A71B0; + opacity: 1; +} + +.pagination li a { + color: #202020; + opacity: 0.5; + transition: all 0.3s ease; +} + +.pagination li a:hover { + color: #3A71B0; + opacity: 1; +} + +.footer { + margin-top: 104px; + padding-top: 24px; + border-top: 2px solid #F7F7F8; +} + +.footer a { + display: inline-block; + width: 100%; + font-size: 14px; + color: #202020; + opacity: 0.3; + transition: all 0.3s ease; + text-align: left; +} + +.footer a:hover { + color: #3A71B0; + opacity: 1; +} diff --git a/src/js/app.js b/src/js/app.js new file mode 100644 index 0000000..6d28ab5 --- /dev/null +++ b/src/js/app.js @@ -0,0 +1,13 @@ +// JS Goes here - ES6 supported +const daysSince = document.getElementById("days-since-latest"); +console.log(daysSince); +if (daysSince) { + const aDay = 1000*60*60*24; + const dateSince = daysSince.getAttribute("data-latest-incident-date"); + + const timeSince = new Date() - new Date(dateSince); + const endDays = Math.floor(timeSince / aDay); + + console.log(endDays); + daysSince.innerHTML = `${endDays} days since last incident` +} diff --git a/webpack.conf.js b/webpack.conf.js new file mode 100644 index 0000000..8cc81c6 --- /dev/null +++ b/webpack.conf.js @@ -0,0 +1,37 @@ +import webpack from "webpack"; +import path from "path"; + +export default { + module: { + loaders: [ + { + test: /\.((png)|(eot)|(woff)|(woff2)|(ttf)|(svg)|(gif))(\?v=\d+\.\d+\.\d+)?$/, + loader: "file?name=/[hash].[ext]" + }, + {test: /\.json$/, loader: "json-loader"}, + { + loader: "babel", + test: /\.js?$/, + exclude: /node_modules/, + query: {cacheDirectory: true} + } + ] + }, + + plugins: [ + new webpack.ProvidePlugin({ + "fetch": "imports?this=>global!exports?global.fetch!whatwg-fetch" + }) + ], + + context: path.join(__dirname, "src"), + entry: { + app: ["./js/app"] + }, + output: { + path: path.join(__dirname, "dist"), + publicPath: "/", + filename: "[name].js" + }, + externals: [/^vendor\/.+\.js$/] +};