Compare commits

..

10 commits

49 changed files with 5467 additions and 5791 deletions

1
.envrc
View file

@ -1 +0,0 @@
use flake

15
.gitignore vendored
View file

@ -11,10 +11,11 @@ npm-debug.log*
yarn-debug.log*
yarn-error.log*
/.terraform.lock.hcl
/.terraform/
/terraform.tfstate
/terraform.tfstate.*
# Nix
/.direnv/
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw*

View file

@ -4,34 +4,28 @@
A clone of [Acquia](https://www.acquia.com)s hosting dashboard, built with [Vue.js](https://vuejs.org) and [Tailwind CSS](https://tailwindcss.com).
![A screenshot of the Rebuilding Acquia website](screenshot.png)
## Icons
[View the site live on Netlify →](https://rebuilding-acquia.oliverdavies.uk)
The icons were downloaded from the live Acquia webite and re-used, but the HTML, CSS and JavaScript code was written from scratch using the above mentioned technologies.
## Project setup
```
yarn install
```
## Screenshots
### Compiles and hot-reloads for development
```
yarn run serve
```
### Viewing all applications
### Compiles and minifies for production
```
yarn run build
```
![A screenshot of the applications page in the list view](docs/images/applications-grid.png)
### Run your tests
```
yarn run test
```
### Viewing all environments for an application
### Lints and fixes files
```
yarn run lint
```
![A screenshot of the applications page in the list view](docs/images/environments-with-help.png)
### Customize configuration
See [Configuration Reference](https://cli.vuejs.org/config/).
### Viewing a single environment
![A screenshot of the applications page in the list view](docs/images/environment-prod.png)
## How it works
TODO: Add blog post link
## Author
[Oliver Davies](https://www.oliverdavies.uk) - Full Stack Developer

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 247 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 242 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 257 KiB

View file

@ -1,27 +0,0 @@
{
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1659446231,
"narHash": "sha256-hekabNdTdgR/iLsgce5TGWmfIDZ86qjPhxDg/8TlzhE=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "eabc38219184cc3e04a974fe31857d8e0eac098d",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-21.11",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}

View file

@ -1,23 +0,0 @@
{
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-21.11";
outputs =
{ nixpkgs, ... }:
let
system = "x86_64-linux";
pkgs = nixpkgs.legacyPackages.${system};
inherit (pkgs) mkShell nodejs;
inherit (pkgs.nodePackages) yarn;
in
{
devShells.${system}.default = mkShell {
buildInputs = [
nodejs
yarn
];
};
formatter.${system} = pkgs.nixfmt-rfc-style;
};
}

111
main.tf
View file

@ -1,111 +0,0 @@
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.0"
}
cloudflare = {
source = "cloudflare/cloudflare"
version = "~> 3.0"
}
}
}
provider "aws" {
region = "eu-west-2"
}
provider "aws" {
alias = "us-east-1"
region = "us-east-1"
}
resource "aws_s3_bucket" "rebuilding-acquia" {
bucket = "rebuilding-acquia"
}
resource "aws_s3_bucket_acl" "rebuilding-acquia" {
acl = "private"
bucket = aws_s3_bucket.rebuilding-acquia.id
}
locals {
s3_origin_id = "rebuilding-acquia"
zone_name = "oliverdavies.uk"
}
resource "aws_cloudfront_origin_access_control" "rebuilding-acquia" {
name = "rebuilding-acquia"
description = "rebuilding-acquia"
origin_access_control_origin_type = "s3"
signing_behavior = "always"
signing_protocol = "sigv4"
}
data "cloudflare_zone" "rebuilding-acquia" {
name = local.zone_name
}
data "aws_acm_certificate" "rebuilding-acquia" {
domain = local.zone_name
provider = aws.us-east-1
statuses = ["ISSUED"]
}
resource "aws_cloudfront_distribution" "s3_distribution" {
origin {
domain_name = aws_s3_bucket.rebuilding-acquia.bucket_regional_domain_name
origin_access_control_id = aws_cloudfront_origin_access_control.rebuilding-acquia.id
origin_id = local.s3_origin_id
}
comment = "Rebuilding Acquia - Tailwind CSS example"
default_root_object = "index.html"
enabled = true
is_ipv6_enabled = true
aliases = ["rebuilding-acquia.${local.zone_name}"]
default_cache_behavior {
allowed_methods = ["GET", "HEAD"]
cached_methods = ["GET", "HEAD"]
target_origin_id = local.s3_origin_id
forwarded_values {
query_string = false
cookies {
forward = "none"
}
}
default_ttl = 3600
max_ttl = 86400
min_ttl = 0
viewer_protocol_policy = "allow-all"
}
price_class = "PriceClass_100"
restrictions {
geo_restriction {
locations = []
restriction_type = "none"
}
}
viewer_certificate {
acm_certificate_arn = data.aws_acm_certificate.rebuilding-acquia.arn
ssl_support_method = "sni-only"
}
}
resource "cloudflare_record" "rebuilding-acquia" {
name = "rebuilding-acquia"
proxied = false
ttl = 0
type = "CNAME"
value = aws_cloudfront_distribution.s3_distribution.domain_name
zone_id = data.cloudflare_zone.rebuilding-acquia.id
}

View file

@ -5,4 +5,4 @@ yarn test:unit
yarn build
"""
publish = "dist"
environment = { NODE_VERSION = "v12.6.0", YARN_VERSION = "1.13.0" }
environment = { YARN_VERSION = "1.13.0" }

View file

@ -11,30 +11,27 @@
},
"dependencies": {
"postcss-nested": "^4.1.2",
"sugarss": "^2.0.0",
"tailwindcss": "^1.1.2",
"tailwindcss": "^1.0.0-beta.3",
"tailwindcss-spaced-items": "^0.1.0",
"tailwindcss-visuallyhidden": "^1.0.2",
"vue": "^2.6.6",
"vue-router": "^3.1.3"
"vue-router": "^3.0.2"
},
"devDependencies": {
"@babel/core": "^7.6.2",
"@babel/preset-env": "^7.6.2",
"@fullhuman/postcss-purgecss": "^1.3.0",
"@vue/cli-plugin-babel": "^3.11.0",
"@vue/cli-plugin-eslint": "^3.11.0",
"@vue/cli-plugin-unit-jest": "^3.11.0",
"@vue/cli-service": "^3.11.0",
"@babel/core": "^7.4.3",
"@babel/preset-env": "^7.4.3",
"@vue/cli-plugin-babel": "^3.5.1",
"@vue/cli-plugin-eslint": "^3.5.1",
"@vue/cli-plugin-unit-jest": "^3.5.3",
"@vue/cli-service": "^3.5.1",
"@vue/eslint-config-standard": "^4.0.0",
"@vue/test-utils": "1.0.0-beta.29",
"babel-core": "7.0.0-bridge.0",
"babel-eslint": "^10.0.3",
"babel-jest": "^24.9.0",
"babel-eslint": "^10.0.1",
"babel-jest": "^24.7.1",
"eslint": "^5.8.0",
"eslint-plugin-vue": "^5.0.0",
"jest": "^24.9.0",
"postcss-import": "^12.0.1",
"jest": "^24.7.1",
"vue-template-compiler": "^2.5.21"
}
}

View file

@ -1,16 +1,7 @@
module.exports = {
parser: 'sugarss',
plugins: [
require('postcss-import'),
require('tailwindcss'),
require('postcss-nested'),
require('autoprefixer'),
process.env.NODE_ENV === 'production' && require('@fullhuman/postcss-purgecss')({
content: [
'./src/**/*.vue',
'./public/index.html',
],
defaultExtractor: content => content.match(/[A-Za-z0-9-_:/]+/g) || []
})
]
plugins: {
tailwindcss: './tailwind.config.js',
'postcss-nested': {},
autoprefixer: {}
}
}

View file

@ -1 +0,0 @@
https://rebuilding-acquia.netlify.com/* https://rebuilding-acquia.oliverdavies.uk/:splat 301!

View file

@ -7,7 +7,7 @@
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title>Rebuilding Acquia</title>
</head>
<body>
<body class="antialiased bg-gray-200 font-sans min-h-full flex flex-col flex-1">
<noscript>
<strong>We're sorry but rebuilding-acquia doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 140 KiB

View file

@ -1,22 +1,44 @@
<template>
<div id="app" class="min-h-screen antialiased bg-gray-200 font-sans flex flex-col flex-1">
<div class="flex flex-col flex-1">
<router-view :applications="applications" :types="types" class="flex flex-col flex-1"/>
</div>
<div id="app" class="flex flex-col flex-1">
<router-view class="flex flex-col flex-1"></router-view>
</div>
</template>
<style src="./assets/css/tailwind.css"/>
<style lang="postcss">
@tailwind base;
<script>
import { applications, types } from '@/data'
html {
line-height: 1.15;
}
export default {
data () {
return {
applications,
types
}
@tailwind components;
h1,
h2 {
@apply font-bold
}
h2 {
@apply text-2xl
}
.btn {
@apply text-sm font-thin antialiased text-white px-4 py-3 no-underline rounded mx-1 bg-blue-300;
&:hover,
&:focus {
@apply bg-blue-400
}
}
</script>
.btn.is-secondary {
@apply bg-gray-500;
&:hover,
&:focus {
@apply bg-gray-600
}
}
@tailwind utilities;
</style>

40
src/api-client.js Normal file
View file

@ -0,0 +1,40 @@
import _ from 'lodash'
let data = require('./data').default
export default {
methods: {
setData (newData) {
data = newData
},
getData () {
return data
},
getTypes () {
return data.types
},
getApplications () {
return data.applications
},
getApplication (applicationId) {
return _(data.applications).get(applicationId)
},
getEnvironment (applicationId, environment) {
return _(this.getApplication(applicationId))
.get('environments')[environment]
},
getApplicationType (application) {
return this.getTypes()[application.type]
},
getVersion (type, environment) {
return _(environment).get(`versions.${type}`)
}
}
}

View file

@ -1,9 +0,0 @@
html
line-height: 1.15
h1,
h2
@apply font-bold
h2
@apply text-2xl

View file

@ -1 +0,0 @@
@import 'components/button'

View file

@ -1,13 +0,0 @@
.btn
@apply text-sm font-thin antialiased text-white px-4 py-3 no-underline rounded mx-1 bg-blue-300
&:hover,
&:focus
@apply bg-blue-400
.btn.is-secondary
@apply bg-gray-500
&:hover,
&:focus
@apply bg-gray-600

View file

@ -1,7 +0,0 @@
@import 'tailwindcss/base'
@import './base.css'
@import 'tailwindcss/components'
@import './components.css'
@import 'tailwindcss/utilities'

View file

@ -1,31 +1,31 @@
<template>
<div class="py-1 overflow-y-scroll">
<ul class="-ml-3 flex">
<li class="ml-3 flex items-center">
<ul class="flex -ml-3">
<li class="flex items-center ml-3">
<div class="flex flex-col-reverse">
<span class="text-gray-700 uppercase text-sm">Organization</span>
<span v-if="!selectedOrganisation" class="mb-1 block font-bold">All</span>
<router-link v-else :to="{ name: 'applications' }" class="mb-1 block font-bold whitespace-no-wrap hover:text-blue-400" v-text="selectedOrganisation"></router-link>
<span v-if="!selectedOrganisation" class="font-bold block mb-1">All</span>
<router-link v-else :to="{ name: 'applications' }" class="font-bold block mb-1 hover:text-blue-400 whitespace-no-wrap" v-text="selectedOrganisation"></router-link>
</div>
<svg class="ml-3" xmlns="http://www.w3.org/2000/svg" width="20" height="20"><path d="M5.94 19.73L15.6 10 5.94.27A.88.88 0 0 0 5.3 0a.89.89 0 0 0-.65 1.51L13.06 10l-8.41 8.49a.89.89 0 0 0 1.29 1.24z"/></svg>
</li>
<li class="ml-3 flex items-center">
<li class="flex items-center ml-3">
<div class="flex flex-col-reverse">
<span class="text-gray-700 uppercase text-sm">Application</span>
<span v-if="!selectedApplication" class="mb-1 block font-bold">All</span>
<span v-else-if="!selectedEnvironment" class="mb-1 block font-bold" v-text="selectedApplication"></span>
<router-link v-else :to="{ name: 'environments' }" class="mb-1 block font-bold whitespace-no-wrap hover:text-blue-400" v-text="selectedApplication"></router-link>
<span v-if="!selectedApplication" class="font-bold block mb-1">All</span>
<span v-else-if="!selectedEnvironment" class="font-bold block mb-1" v-text="selectedApplication"></span>
<router-link v-else :to="{ name: 'environments' }" class="font-bold block mb-1 hover:text-blue-400 whitespace-no-wrap" v-text="selectedApplication"></router-link>
</div>
<svg class="ml-3" xmlns="http://www.w3.org/2000/svg" width="20" height="20"><path d="M5.94 19.73L15.6 10 5.94.27A.88.88 0 0 0 5.3 0a.89.89 0 0 0-.65 1.51L13.06 10l-8.41 8.49a.89.89 0 0 0 1.29 1.24z"/></svg>
</li>
<li class="ml-3 flex items-center">
<li class="flex items-center ml-3">
<div class="flex flex-col-reverse">
<span class="text-gray-700 uppercase text-sm">Environment</span>
<span v-if="!selectedApplication" class="mb-1 block font-bold">---</span>
<span v-else-if="!selectedEnvironment" class="mb-1 block font-bold">All</span>
<span v-else class="mb-1 block font-bold" v-text="selectedEnvironment"></span>
<span v-if="!selectedApplication" class="font-bold block mb-1">---</span>
<span v-else-if="!selectedEnvironment" class="font-bold block mb-1">All</span>
<span v-else class="font-bold block mb-1" v-text="selectedEnvironment"></span>
</div>
</li>
</ul>

View file

@ -9,40 +9,38 @@
</div>
<div class="truncate text-blue-300">
<a href="#0" class="text-inherit no-underline hover:underline focus:underline focus:outline-none" v-text="prodUrl"/>
<a href="#0" class="text-inherit no-underline hover:underline focus:underline focus:outline-none">{{ application.environments['prod'].url }}</a>
</div>
</div>
<php-version class="mt-8" :application="application"></php-version>
</div>
<div class="w-1/6 flex-none text-right">
<star-toggle :application="application" :starred="starred" @toggle="starred = !starred"></star-toggle>
<div class="flex-none w-1/6 text-right">
<star-toggle :application="application"></star-toggle>
</div>
</div>
</div>
<div v-if="display == 'list'" class="p-3 bg-white border-gray-400 border-b">
<div v-if="display == 'list'" class="bg-white p-3 border-gray-400 border-b">
<div class="-mx-2">
<div class="flex flex-row-reverse items-center justify-between">
<div class="-mx-2 flex flex-1 justify-between items-center">
<div class="px-2 flex-1">
<router-link :to="{name: 'environments', params: { id }}" class="text-blue-300 no-underline hover:underline focus:underline">
<h2 class="mb-1 text-base font-normal">{{ application.name }}</h2>
</router-link>
<div class="flex flex-1 justify-between items-center -mx-2">
<div class="flex-1 px-2">
<router-link :to="{name: 'environments', params: {id: id}}" class="text-blue-300 no-underline hover:underline focus:underline"><h2 class="text-base font-normal mb-1">{{ application.name }}</h2></router-link>
</div>
<div class="px-2 w-2/5">
<a href="#0" class="text-blue-300 no-underline hover:underline focus:underline" v-text="prodUrl"/>
<div class="w-2/5 px-2">
<a href="#0" class="text-blue-300 no-underline hover:underline focus:underline">{{ application.environments['prod'].url }}</a>
</div>
<div class="px-1 w-1/5">
<php-version :application="application"/>
<div class="w-1/5 px-2">
<php-version :application="application"></php-version>
</div>
</div>
<div class="px-2">
<star-toggle :application="application" :starred="starred" @toggle="starred = !starred"/>
<star-toggle :application="application"></star-toggle>
</div>
</div>
</div>
@ -51,28 +49,28 @@
</template>
<script>
import ApiClient from '@/api-client.js'
import PhpVersion from '@/components/Application/PhpVersion'
import StarToggle from '@/components/Application/StarToggle'
export default {
components: { PhpVersion, StarToggle },
mixins: [ApiClient],
components: {
PhpVersion,
StarToggle
},
props: {
application: Object,
display: String,
id: String
id: Number
},
data () {
return {
starred: false
}
},
computed: {
prodUrl () {
return this.application.environments['prod'].url
}
}
}
</script>

View file

@ -1,8 +1,8 @@
<template>
<div>
<ul class="list-reset -ml-2 flex flex-wrap">
<li class="ml-2 py-1 px-2 text-2xs rounded uppercase border" :class="typeClasses" v-text="applicationTypeName"/>
<li class="ml-2 py-1 px-2 text-2xs rounded uppercase border border-gray-400 bg-white">{{ application.level }}</li>
<ul class="list-reset flex flex-wrap -mr-2 -mb-2">
<li class="tag" :data-type="type.id">{{ type.name }}</li>
<li class="tag">{{ level }}</li>
</ul>
</div>
</template>
@ -10,21 +10,22 @@
<script>
export default {
props: {
application: Object
},
computed: {
applicationTypeName () {
return this.$attrs.types[this.application.type].name
},
typeClasses () {
return {
'': 'bg-white border-gray-400',
drupal: 'bg-blue-100 border-blue-100 text-white',
nodejs: 'bg-green border-green text-white'
}[this.application.type]
}
level: String,
type: Object
}
}
</script>
<style scoped>
.tag {
@apply text-2xs py-1 px-2 rounded uppercase border border-gray-400 bg-white mr-2 mb-2
}
.tag[data-type="drupal"] {
@apply bg-blue-100 border-blue-100 text-white
}
.tag[data-type="nodejs"] {
@apply bg-green border-green text-white
}
</style>

View file

@ -1,12 +1,16 @@
<template>
<div class="flex items-center text-sm">
<div class="text-sm flex items-center">
<svg class="w-8 h-8" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M1.71 6.86h3.21A2.49 2.49 0 0 1 7 7.67a2.68 2.68 0 0 1 .42 2.19A4.1 4.1 0 0 1 7 11.1a3.55 3.55 0 0 1-.77 1.09A2.6 2.6 0 0 1 5 13a5.92 5.92 0 0 1-1.41.17H2.12l-.45 2.26H0l1.71-8.57m1.41 1.36L2.4 11.8h.31a6.55 6.55 0 0 0 1.91-.22c.52-.17.86-.75 1-1.75.14-.83 0-1.31-.44-1.44a5.52 5.52 0 0 0-1.59-.18zm11.15-1.36h3.21a2.52 2.52 0 0 1 2.05.81A2.75 2.75 0 0 1 20 9.86a4.12 4.12 0 0 1-.38 1.24 3.69 3.69 0 0 1-.76 1.09 2.65 2.65 0 0 1-1.28.79 6.07 6.07 0 0 1-1.42.17h-1.49l-.45 2.26h-1.67l1.72-8.55m1.4 1.36L15 11.8h.31a6.58 6.58 0 0 0 1.92-.22c.51-.17.85-.75 1-1.75.14-.83 0-1.31-.43-1.44a5.64 5.64 0 0 0-1.6-.18zm-7-3.63h1.65l-.44 2.27h1.49a3 3 0 0 1 1.82.51c.41.31.53.92.36 1.81l-.8 4h-1.68l.77-3.79a1.06 1.06 0 0 0-.08-.85c-.12-.17-.4-.25-.82-.25H9.61l-1 4.9H7l1.7-8.6" data-name="Layer 2"/></svg>
<span class="ml-1">{{ phpVersion }}</span>
</div>
</template>
<script>
import ApiClient from '@/api-client.js'
export default {
mixins: [ApiClient],
props: {
application: Object
},
@ -17,7 +21,7 @@ export default {
return null
}
return this.application.environments['prod'].versions['php']
return this.getVersion('php', this.getEnvironment(this.application.id, 'prod'))
}
}
}

View file

@ -1,17 +1,19 @@
<template>
<button type="button" @click="$emit('toggle')" class="focus:outline-none" :class="[starred ? 'text-orange-100 hover:text-orange-200 focus:text-orange-200' : 'text-gray-400 focus:text-orange-200 hover:text-orange-100']">
<button type="button" @click="starred = !starred" class="focus:outline-none" :class="[starred ? 'text-orange-100 hover:text-orange-200 focus:text-orange-200' : 'text-gray-400 focus:text-orange-200 hover:text-orange-100']">
<span class="visuallyhidden">{{ !starred ? 'Star' : 'Unstar' }} {{ application.name }}</span>
<svg class="-mr-1 h-6 w-6 fill-current" role="presentation"><use :xlink:href="`/img/icons.symbol.svg#state__${starred ? 'starred' : 'unstarred'}`"></use></svg>
<svg class="h-6 w-6 fill-current -mr-1" role="presentation"><use :xlink:href="`/img/icons.symbol.svg#state__${starred ? 'starred' : 'unstarred'}`"></use></svg>
</button>
</template>
<script>
export default {
props: {
application: Object,
starred: {
type: Boolean,
default: false
application: Object
},
data () {
return {
starred: false
}
}
}

View file

@ -5,7 +5,7 @@
<div class="px-3 py-4">
<div class="flex flex-row-reverse">
<div class="flex-1">
<h2 class="mb-1 text-gray-600 text-base">{{ title }}</h2>
<h2 class="text-gray-600 text-base mb-1">{{ title }}</h2>
<div class="text-xs text-gray-600">{{ description }}</div>
</div>
@ -39,6 +39,7 @@ export default {
</script>
<style scoped>
svg
svg {
@apply h-8 w-8 fill-current text-teal
}
</style>

View file

@ -1,5 +1,5 @@
<template>
<button type="button" class="py-3 w-full flex flex-col items-center text-blue-300 text-xs font-bold no-underline hover:underline focus:underline hover:bg-gray-200 focus:bg-gray-200 focus:outline-none">
<button type="button" class="w-full flex flex-col items-center py-3 text-blue-300 text-xs font-bold no-underline hover:underline focus:underline hover:bg-gray-200 focus:bg-gray-200 focus:outline-none block">
<slot></slot>
<span class="block" v-text="label"></span>
</button>
@ -14,6 +14,7 @@ export default {
</script>
<style scoped>
svg
@apply h-6 w-6 fill-current mb-2
svg {
@apply h-6 w-6 fill-current mb-2
}
</style>

View file

@ -1,24 +1,24 @@
<template>
<div>
<ul class="flex flex-wrap md:hidden -mx-1 mb-5">
<li class="px-1 w-1/3"><button type="button" class="py-3 w-full text-sm text-white rounded" :class="[ active == 'code' ? 'bg-blue-400' : 'bg-blue-300' ]" @click="active = 'code'">Code</button></li>
<li class="px-1 w-1/3"><button type="button" class="py-3 w-full text-sm text-white rounded" :class="[ active == 'databases' ? 'bg-blue-400' : 'bg-blue-300' ]" @click="active = 'databases'">Databases</button></li>
<li class="px-1 w-1/3"><button type="button" class="py-3 w-full text-sm text-white rounded" :class="[ active == 'files' ? 'bg-blue-400' : 'bg-blue-300' ]" @click="active = 'files'">Files</button></li>
<li class="w-1/3 px-1"><button type="button" class="text-sm text-white w-full py-3 rounded" :class="[ active == 'code' ? 'bg-blue-400' : 'bg-blue-300' ]" @click="active = 'code'">Code</button></li>
<li class="w-1/3 px-1"><button type="button" class="text-sm text-white w-full py-3 rounded" :class="[ active == 'databases' ? 'bg-blue-400' : 'bg-blue-300' ]" @click="active = 'databases'">Databases</button></li>
<li class="w-1/3 px-1"><button type="button" class="text-sm text-white w-full py-3 rounded" :class="[ active == 'files' ? 'bg-blue-400' : 'bg-blue-300' ]" @click="active = 'files'">Files</button></li>
</ul>
<div class="-mx-4 flex flex-wrap">
<div class="px-4 w-full md:w-1/3 md:block" :class="[ active == 'code' ? 'block' : 'hidden' ]">
<div class="flex flex-wrap -mx-4 -mb-4">
<div class="wrapper" :class="[ active == 'code' ? 'block' : 'hidden' ]">
<action-card title="Code" :description="`${ environment.name }: ${environment.label}`">
<svg slot="icon" role="presentation"><use xlink:href="/img/icons.symbol.svg#objects__code"></use></svg>
<ul slot="buttons" class="-ml-px flex">
<li class="w-1/2 flex-1 text-center border-l border-gray-400">
<ul slot="buttons" class="button-list">
<li class="button-list-item">
<action-card-button label="Deploy">
<svg role="presentation"><use xlink:href="/img/icons.symbol.svg#objects__fork"></use></svg>
</action-card-button>
</li>
<li class="w-1/2 flex-1 text-center border-l border-gray-400">
<li class="button-list-item">
<action-card-button label="Switch">
<svg role="presentation"><use xlink:href="/img/icons.symbol.svg#actions__switch"></use></svg>
</action-card-button>
@ -27,24 +27,24 @@
</action-card>
</div>
<div class="px-4 w-full md:block md:w-1/3" :class="[ active == 'databases' ? 'block' : 'hidden' ]">
<div class="wrapper" :class="[ active == 'databases' ? 'block' : 'hidden' ]">
<action-card title="Databases" :description="environment.name">
<svg slot="icon" role="presentation"><use xlink:href="/img/icons.symbol.svg#objects__database"></use></svg>
<ul slot="buttons" class="-ml-px flex">
<li class="w-1/2 flex-1 text-center border-l border-gray-400">
<ul slot="buttons" class="button-list">
<li class="button-list-item">
<action-card-button label="Copy">
<svg role="presentation"><use xlink:href="/img/icons.symbol.svg#actions__copy"></use></svg>
</action-card-button>
</li>
<li class="w-1/2 flex-1 text-center border-l border-gray-400">
<li class="button-list-item">
<action-card-button label="Back up">
<svg role="presentation"><use xlink:href="/img/icons.symbol.svg#actions__backup"></use></svg>
</action-card-button>
</li>
<li class="w-1/2 flex-1 text-center border-l border-gray-400">
<li class="button-list-item">
<action-card-button label="Restore" :disabled="isProduction" :class="{
'text-blue-300': !isProduction,
'text-gray-500 cursor-not-allowed': isProduction
@ -56,12 +56,12 @@
</action-card>
</div>
<div class="px-4 w-full md:block md:w-1/3" :class="[ active == 'files' ? 'block' : 'hidden' ]">
<div class="wrapper" :class="[ active == 'files' ? 'block' : 'hidden' ]">
<action-card title="Files" :description="environment.name">
<svg slot="icon" class="h-8 w-8 fill-current text-teal" role="presentation"><use xlink:href="/img/icons.symbol.svg#objects__environment"></use></svg>
<ul slot="buttons" class="-ml-px flex">
<li class="w-1/2 flex-1 text-center border-l border-gray-400">
<ul slot="buttons" class="button-list">
<li class="button-list-item">
<action-card-button label="Copy">
<svg role="presentation"><use xlink:href="/img/icons.symbol.svg#actions__copy"></use></svg>
</action-card-button>
@ -95,3 +95,21 @@ export default {
}
}
</script>
<style scoped>
.wrapper {
@apply w-full px-4 mb-4;
@screen md {
@apply w-1/3 block
}
}
.button-list {
@apply flex -ml-px
}
.button-list-item {
@apply w-1/2 flex-1 text-center border-l border-gray-400
}
</style>

View file

@ -1,45 +1,47 @@
<template>
<div class="grid gap-6 grid-cols-1 md:grid-cols-2 xl:grid-cols-3">
<div v-for="(environment, key) in application.environments" :key="key">
<div class="border border-gray-400 rounded overflow-hidden shadow">
<div class="bg-white">
<div class="border-t-4 border-teal p-3">
<router-link :to="{ name: 'environment', params: { environmentName: key, id: id }}" class="flex items-baseline no-underline hover:underline focus:underline text-gray-600 hover:text-blue-300 focus:text-blue-300 focus:outline-none mb-1">
<h2>{{ environment.name }}</h2>
<svg class="w-4 h-4 fill-current text-inherit ml-2" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M12.95 10.707l.707-.707L8 4.343 6.586 5.757 10.828 10l-4.242 4.243L8 15.657l4.95-4.95z" fill-rule="evenodd"/></svg>
</router-link>
<div class="mb-6">
<div class="flex flex-wrap -mx-4 -mb-6">
<div v-for="(environment, key) in environments" class="w-full md:w-1/2 xl:w-1/3 px-4 mb-6" :key="key">
<div class="border border-gray-400 rounded overflow-hidden shadow">
<div class="bg-white">
<div class="border-t-4 border-teal p-3">
<router-link :to="{ name: 'environment', params: { environmentName: key, id: id }}" class="flex items-baseline no-underline hover:underline focus:underline text-gray-600 hover:text-blue-300 focus:text-blue-300 focus:outline-none mb-1">
<h2>{{ environment.name }}</h2>
<svg class="w-4 h-4 fill-current text-inherit ml-2" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M12.95 10.707l.707-.707L8 4.343 6.586 5.757 10.828 10l-4.242 4.243L8 15.657l4.95-4.95z" fill-rule="evenodd"/></svg>
</router-link>
<div class="mb-2">
<a href="#0" class="no-underline hover:underline focus:underline text-gray-600 hover:text-blue-300 focus:text-blue-300 focus:outline-none">
{{ environment.url }}
</a>
<div class="mb-2">
<a href="#0" class="no-underline hover:underline focus:underline text-gray-600 hover:text-blue-300 focus:text-blue-300 focus:outline-none">
{{ environment.url }}
</a>
</div>
<div class="text-gray-600">{{ environment.label }}</div>
</div>
<div class="text-gray-600">{{ environment.label }}</div>
</div>
</div>
<div>
<button type="button" class="flex items-center justify-between text-white pl-1 bg-blue-200 hover:bg-blue-400 focus:bg-blue-400 focus:outline-none focus:underline text-sm w-full text-left py-1">
<svg class="h-6 w-5 text-white mr-2 fill-current" role="presentation"><use xlink:href="/img/icons.symbol.svg#objects__grip-handle"></use></svg>
<span class="flex-1">Code</span>
<span class="border-l border-gray-400 p-2">
<svg class="h-6 w-6 text-white fill-current" role="presentation"><use xlink:href="/img/icons.symbol.svg#actions__switch"></use></svg>
</span>
</button>
<div>
<button type="button" class="flex items-center justify-between text-white pl-1 bg-blue-200 hover:bg-blue-400 focus:bg-blue-400 focus:outline-none focus:underline text-sm w-full text-left py-1">
<svg class="h-6 w-5 text-white mr-2 fill-current" role="presentation"><use xlink:href="/img/icons.symbol.svg#objects__grip-handle"></use></svg>
<span class="flex-1">Code</span>
<span class="border-l border-gray-400 p-2">
<svg class="h-6 w-6 text-white fill-current" role="presentation"><use xlink:href="/img/icons.symbol.svg#actions__switch"></use></svg>
</span>
</button>
<button type="button" class="flex items-center justify-between text-white pl-1 bg-blue-200 hover:bg-blue-400 focus:bg-blue-400 focus:outline-none focus:underline text-sm w-full text-left py-1 border-t border-gray-400">
<svg class="h-6 w-5 text-white mr-2 fill-current" role="presentation"><use xlink:href="/img/icons.symbol.svg#objects__grip-handle"></use></svg>
<span class="flex-1">Databases</span>
<span class="border-l border-gray-400 p-2">
<svg class="h-6 w-6 text-white fill-current" role="presentation"><use xlink:href="/img/icons.symbol.svg#actions__backup"></use></svg>
</span>
</button>
<button type="button" class="flex items-center justify-between text-white pl-1 bg-blue-200 hover:bg-blue-400 focus:bg-blue-400 focus:outline-none focus:underline text-sm w-full text-left py-1 border-t border-gray-400">
<svg class="h-6 w-5 text-white mr-2 fill-current" role="presentation"><use xlink:href="/img/icons.symbol.svg#objects__grip-handle"></use></svg>
<span class="flex-1">Databases</span>
<span class="border-l border-gray-400 p-2">
<svg class="h-6 w-6 text-white fill-current" role="presentation"><use xlink:href="/img/icons.symbol.svg#actions__backup"></use></svg>
</span>
</button>
<button type="button" class="px-1 py-3 w-full flex items-center text-white bg-blue-200 text-sm text-left border-t border-gray-400 hover:bg-blue-400 focus:bg-blue-400 focus:outline-none focus:underline">
<svg class="mr-2 h-6 w-5 text-white fill-current" role="presentation"><use xlink:href="/img/icons.symbol.svg#objects__grip-handle"></use></svg>
Files
</button>
<button type="button" class="flex items-center text-white bg-blue-200 hover:bg-blue-400 focus:bg-blue-400 focus:outline-none focus:underline px-1 py-3 text-sm w-full text-left py-1 border-t border-gray-400">
<svg class="h-6 w-5 text-white mr-2 fill-current" role="presentation"><use xlink:href="/img/icons.symbol.svg#objects__grip-handle"></use></svg>
Files
</button>
</div>
</div>
</div>
</div>
@ -49,7 +51,7 @@
<script>
export default {
props: {
application: Object,
environments: Object,
id: String
}
}

View file

@ -1,22 +1,22 @@
<template>
<div class="bg-white border-t-10 border-purple" :class="[hidden ? 'hidden' : 'block lg:flex']">
<div class="bg-white border-t-10 border-purple mb-6" :class="[hidden ? 'hidden' : 'block lg:flex']">
<div class="flex-none py-3 pl-3 pr-8 border-b lg:border-r border-gray-200">
<div class="flex items-center">
<svg class="mr-1 h-6 w-6 text-purple fill-current" role="presentation"><use xlink:href="/img/icons.symbol.svg#actions__info"></use></svg>
<svg class="h-6 w-6 mr-1 text-purple fill-current" role="presentation"><use xlink:href="/img/icons.symbol.svg#actions__info"></use></svg>
Quick help
</div>
</div>
<div class="p-4">
<div class="-mb-5 block leading-normal lg:hidden">
<div class="block lg:hidden leading-normal -mb-5">
<div v-for="item in items" :key="item.subject">
<p class="mb-3 text-sm font-bold">{{ item.subject }}</p>
<p class="mb-5 text-sm">{{ item.text }}</p>
<p class="text-sm mb-3 font-bold">{{ item.subject }}</p>
<p class="text-sm mb-5">{{ item.text }}</p>
</div>
</div>
<div class="hidden lg:block">
<div class="mb-6 w-full lg:w-auto lg:flex">
<div class="w-full lg:w-auto lg:flex mb-6">
<button
v-for="(item, i) in items"
:key="item.subject"

View file

@ -1,6 +1,6 @@
<template>
<div>
<div class="pb-4 pt-6 px-4 bg-gray-300">
<div class="bg-gray-300 pb-4 pt-6 px-4">
<h2 class="font-normal text-lg">Task Log</h2>
</div>
<div>
@ -14,7 +14,7 @@
</template>
<script>
import reverse from 'lodash/reverse'
import _ from 'lodash'
import TaskLogItem from '@/components/Environment/TaskLog/TaskLogItem'
export default {
@ -30,7 +30,7 @@ export default {
reversedTasks: function () {
let tasks = this.tasks
return reverse(tasks)
return _.reverse(tasks)
}
}
}

View file

@ -2,13 +2,13 @@
<div>
<div class="px-3 py-4 border-b border-gray-300 flex" :class="{'bg-yellow-lightest': open}">
<div class="flex-none mr-3">
<svg v-if="task.loading" class="rotates h-5 w-5 text-gray-500 fill-current" role="presentation"><use xlink:href="/img/icons.symbol.svg#feedback__loading"></use></svg>
<svg v-if="task.loading" class="h-5 w-5 text-gray-500 fill-current rotates" role="presentation"><use xlink:href="/img/icons.symbol.svg#feedback__loading"></use></svg>
<svg v-if="!task.loading && task.success" class="h-5 w-5 text-green fill-current" role="presentation"><use xlink:href="/img/icons.symbol.svg#feedback__success-circle"></use></svg>
<svg v-if="!task.loading && !task.success" class="h-5 w-5 text-red fill-current" role="presentation"><use xlink:href="/img/icons.symbol.svg#feedback__warning"></use></svg>
</div>
<div class="flex-1">
<div class="mb-2 text-sm font-bold">{{ task.text }}</div>
<div class="text-sm font-bold mb-2">{{ task.text }}</div>
<div class="text-gray-600 text-2xs">{{ task.times.display }}</div>
</div>
@ -21,29 +21,29 @@
</div>
<div class="p-4 bg-gray-600 text-white antialiased" v-show="open">
<div class="-mx-4 -mb-4 lg:flex lg:flex-wrap">
<div class="lg:flex lg:flex-wrap -mx-4 -mb-4">
<div class="px-4 mb-4">
<div class="mb-1 text-xs uppercase">Task ID</div>
<div class="text-xs uppercase mb-1">Task ID</div>
<div class="text-sm font-bold">12345678</div>
</div>
<div class="mb-4 px-4 lg:w-1/6 lg:flex-none">
<div class="mb-1 text-xs uppercase">User</div>
<div class="truncate text-sm font-bold">{{ task.user }}</div>
<div class="px-4 mb-4 lg:w-1/6 lg:flex-none">
<div class="text-xs uppercase mb-1">User</div>
<div class="text-sm font-bold truncate">{{ task.user }}</div>
</div>
<div class="px-4 mb-4 lg:w-1/4 lg:flex-none">
<div class="mb-1 text-xs uppercase">Started</div>
<div class="truncate text-sm font-bold">{{ task.times.started }}</div>
<div class="text-xs uppercase mb-1">Started</div>
<div class="text-sm font-bold truncate">{{ task.times.started }}</div>
</div>
<div class="px-4 mb-4 lg:w-1/4 lg:flex-none">
<div class="mb-1 text-xs uppercase">Completed</div>
<div class="truncate text-sm font-bold">{{ task.times.completed }}</div>
<div class="text-xs uppercase mb-1">Completed</div>
<div class="text-sm font-bold truncate">{{ task.times.completed }}</div>
</div>
<div class="px-4 mb-4">
<div class="mb-1 text-xs uppercase">Status</div>
<div class="text-xs uppercase mb-1">Status</div>
<div class="text-sm font-bold">{{ status }}</div>
</div>
</div>
@ -80,12 +80,16 @@ export default {
</script>
<style scoped>
.rotates
.rotates {
animation: rotation 1s infinite linear
}
@-webkit-keyframes rotation
from
@-webkit-keyframes rotation {
from {
-webkit-transform: rotate(0deg)
to
}
to {
-webkit-transform: rotate(-359deg)
}
}
</style>

View file

@ -1,7 +1,7 @@
<template>
<div class="hidden lg:block">
<button type="button" class="text-blue-300 text-sm font-hairline flex items-end hover:underline focus:underline focus:outline-none" @click="$emit('toggle')">
<svg class="mr-1 h-5 w-5 text-blue-300 fill-current" role="presentation"><use xlink:href="/img/icons.symbol.svg#actions__info"></use></svg>
<svg class="h-5 w-5 mr-1 text-blue-300 fill-current" role="presentation"><use xlink:href="/img/icons.symbol.svg#actions__info"></use></svg>
{{ hidden ? 'Show quick help' : 'Hide quick help' }}
</button>
</div>

View file

@ -1,7 +1,7 @@
<template>
<div class="hidden lg:block">
<ul class="list-reset flex">
<li v-for="(link, i) in links" :key="link.title" :class="{ 'ml-6': i !== 0 }">
<li v-for="(link, i) in links" :key="link.title" :class="{'ml-6': i !== 0}">
<button
type="button"
class="text-xs no-underline hover:underline flex flex-col items-center justify-center"
@ -11,7 +11,7 @@
}"
:disabled="link.disabled"
>
<svg class="mb-2 h-6 w-6 fill-current" role="presentation"><use :xlink:href="`/img/icons.symbol.svg#${link.icon}`"></use></svg>
<svg class="h-6 w-6 fill-current mb-2" role="presentation"><use :xlink:href="`/img/icons.symbol.svg#${link.icon}`"></use></svg>
{{ link.title }}
</button>
</li>
@ -20,7 +20,7 @@
</template>
<script>
import { omitBy, _ } from 'lodash'
import _ from 'lodash'
export default {
props: {
@ -31,7 +31,7 @@ export default {
filteredLinks: function () {
let links = this.links
return omitBy(links, _.isEmpty)
return _.omitBy(links, _.isEmpty)
}
}
}

View file

@ -7,8 +7,8 @@
<div class="flex flex-col flex-1">
<nav class="border-t-2 border-transparent flex justify-end md:justify-start">
<a href="#0" class="mx-2 px-4 py-5 inline-block text-sm border-b-4 border-blue-300 text-blue-300 uppercase no-underline hover:underline focus:underline">Develop</a>
<a href="#0" class="mx-2 px-4 py-5 inline-block text-sm border-b-4 border-transparent text-blue-300 uppercase no-underline hover:underline focus:underline">Manage</a>
<a href="#0" class="inline-block text-sm border-b-4 border-blue-300 mx-2 px-4 py-5 text-blue-300 uppercase no-underline hover:underline focus:underline">Develop</a>
<a href="#0" class="inline-block text-sm border-b-4 border-transparent mx-2 px-4 py-5 text-blue-300 uppercase no-underline hover:underline focus:underline">Manage</a>
</nav>
</div>

View file

@ -1,11 +1,11 @@
<template>
<div class="fixed left-0 h-full w-auto bg-gray-300 text-gray-400 overflow-y-scroll z-30 antialiased lg:w-56">
<div class="w-auto lg:w-56 bg-gray-300 text-gray-400 antialiased fixed left-0 h-full overflow-y-scroll z-30">
<div>
<ul class="list-reset">
<li v-for="link in links" :key="link.title">
<a
href="#0"
class="px-4 py-2 flex items-center no-underline text-sm focus:underline"
class="flex items-center no-underline focus:underline px-4 py-2 text-sm"
:class="{
'bg-gray-600 text-white hover:text-white': link.active,
'border-transparent hover:bg-gray-400': !link.active,
@ -14,7 +14,7 @@
}"
>
<svg class="h-6 w-6 fill-current" role="presentation"><use :xlink:href="`/img/icons.symbol.svg#${link.icon}`"></use></svg>
<span class="hidden lg:ml-4 lg:block">{{ link.title }}</span>
<span class="hidden lg:block lg:ml-4">{{ link.title }}</span>
</a>
</li>
</ul>

View file

@ -1,7 +1,7 @@
<template>
<div class="border-b-3 border-gray-300">
<div class="px-4 py-5 flex justify-between items-center bg-white border-t border-gray-200 lg:px-6">
<div class="w-full flex flex-col md:w-auto">
<div class="bg-white px-4 lg:px-6 py-5 border-t border-gray-200 flex justify-between items-center">
<div class="w-full md:w-auto flex flex-col">
<app-breadcrumb :application="application" :environment="environment"></app-breadcrumb>
</div>

View file

@ -1,4 +1,4 @@
module.exports = {
export default {
types: {
drupal: {
id: 'drupal',

View file

@ -1,6 +1,6 @@
<template>
<div>
<div class="w-full fixed top-0 z-30">
<div class="fixed top-0 w-full z-30">
<navbar></navbar>
<title-block>
@ -13,41 +13,37 @@
</div>
<div class="mt-48">
<div class="-mt-3 h-full flex flex-row-reverse">
<div class="ml-16 p-4 flex-1 overflow-x-hidden lg:ml-56 lg:p-12">
<div class="-mt-3 flex flex-row-reverse h-full">
<div class="flex-1 p-4 lg:p-12 ml-16 lg:ml-56 overflow-x-hidden">
<div class="mb-6">
<div class="lg:flex lg:items-baseline mb-12">
<div class="mr-16 mb-4 lg:mb-0">
<h1 class="mb-2 text-4xl font-thin">Applications</h1>
<h1 class="text-4xl font-thin mb-2">Applications</h1>
</div>
<application-display-switcher class="mr-3 hidden lg:flex-1 lg:flex lg:justify-end" :mode="display" @display-changed="handleDisplay"></application-display-switcher>
<application-display-switcher class="hidden lg:flex-1 lg:flex lg:justify-end mr-3" :mode="display" @display-changed="handleDisplay"></application-display-switcher>
<div class="justify-between items-baseline lg:flex lg:flex-row-reverse">
<div class="lg:flex lg:flex-row-reverse justify-between items-baseline">
<div class="w-full">
<form action="#">
<label for="applications" class="visuallyhidden">Filter applications</label>
<input id="applications" type="text" placeholder="Filter applications" class="py-2 px-3 w-full border border-gray-600 rounded">
<input id="applications" type="text" placeholder="Filter applications" class="w-full py-2 px-3 border border-gray-600 rounded">
</form>
</div>
</div>
</div>
</div>
<div class="-mt-6 -mx-3 flex flex-wrap">
<div
<div class="flex flex-wrap -mx-3 -mb-6">
<application-card
v-for="application in sortedApplications"
:id="application.id"
:application="application"
:key="application.id"
:display="display"
class="px-3 w-full"
:class="{ 'mt-6 flex flex-col md:w-1/2 lg:w-1/3 xl:w-1/4': display == 'grid' }"
>
<application-card
:application="application"
:display="display"
:id="application.id"
class="flex flex-col flex-1"
></application-card>
</div>
:class="[display == 'grid' ? 'md:w-1/2 lg:w-1/3 xl:w-1/4 mb-6 flex flex-col' : '']"
></application-card>
</div>
</div>
@ -62,21 +58,13 @@
</template>
<script>
import sortBy from 'lodash/sortBy'
import _ from 'lodash'
import ApiClient from '@/api-client.js'
import ApplicationCard from '@/components/Application/ApplicationCard'
import ApplicationDisplaySwitcher from '@/components/Application/ApplicationDisplaySwitcher'
export default {
props: {
applications: {
type: Object,
required: true
},
types: {
type: Object,
required: true
}
},
mixins: [ApiClient],
components: {
ApplicationCard,
@ -85,13 +73,14 @@ export default {
data () {
return {
applications: this.getApplications(),
display: 'grid'
}
},
computed: {
sortedApplications: function () {
return sortBy(this.applications, [function (a) { return a.name }])
return _.sortBy(this.applications, [function (a) { return a.name }])
}
},

View file

@ -18,12 +18,12 @@
<div class="mt-48">
<div class="-mt-3 flex flex-row-reverse h-full">
<div class="ml-16 p-4 flex-1 overflow-x-hidden lg:ml-56 lg:p-12">
<div class="flex-1 p-4 lg:p-12 ml-16 lg:ml-56 overflow-x-hidden">
<div>
<div>
<h1 class="mb-2 text-4xl font-thin">Overview</h1>
<h1 class="text-4xl font-thin mb-2">Overview</h1>
<application-tags :application="application" :types="$attrs.types"/>
<application-tags :type="getApplicationType(application)" :level="application.level"></application-tags>
</div>
<div class="my-10">
@ -38,21 +38,21 @@
<action-cards :environment="environment" :is-production="isProduction"></action-cards>
</div>
<div class="mt-8 shadow-md overflow-hidden">
<div class="p-3 bg-white border-b-2 border-gray-300"><h2 class="text-lg">Site Health</h2></div>
<div class="p-4 bg-white">
<div>
<div class="shadow-md overflow-hidden">
<div class="bg-white border-b-2 border-gray-300 p-3"><h2 class="text-lg">Site Health</h2></div>
<div class="bg-white p-4">
<div class="mb-6">
<p class="text-lg font-hairline text-gray-700">Uptime monitoring</p>
</div>
<div class="mt-6 text-center leading-normal">
<div class="text-center leading-normal mb-5">
<p class="text-gray-600">
Acquia uses a specially tuned uptime monitoring solution to keep track<br class="hidden md:inline">
of whether your Drupal site is really up and running.
</p>
</div>
<div class="mt-5 flex justify-center">
<div class="flex justify-center">
<button type="button" class="btn">Enable Uptime</button>
<button type="button" class="btn is-secondary">Learn more</button>
</div>
@ -60,47 +60,47 @@
</div>
<div class="shadow-md overflow-hidden">
<div class="p-3 bg-white border-b-2 border-gray-300"><h2 class="text-lg">Information</h2></div>
<div class="px-4 py-6 bg-white">
<div class="bg-white border-b-2 border-gray-300 p-3"><h2 class="text-lg">Information</h2></div>
<div class="bg-white px-4 py-6">
<div class="mb-6 relative">
<div class="mb-1 text-xs">Git URL</div>
<div class="p-3 border border-gray-500 rounded truncate">{{ gitUrl }}</div>
<div class="text-xs mb-1">Git URL</div>
<div class="border border-gray-500 rounded p-3 truncate">{{ gitUrl }}</div>
<div class="absolute top-0 right-0">
<button type="button" class="flex items-center text-xs text-blue-300 hover:underline focus:underline">
<svg class="mr-1 h-3 w-3 fill-current" role="presentation"><use xlink:href="/img/icons.symbol.svg#actions__copy"></use></svg>
<svg class="h-3 w-3 fill-current mr-1" role="presentation"><use xlink:href="/img/icons.symbol.svg#actions__copy"></use></svg>
Copy <span class="visuallyhidden">Git URL to your clipboard</span>
</button>
</div>
</div>
<div class="mb-6 relative">
<div class="mb-1 text-xs">SSH URL</div>
<div class="p-3 border border-gray-500 rounded truncate">{{ sshUrl }}</div>
<div class="text-xs mb-1">SSH URL</div>
<div class="border border-gray-500 rounded p-3 truncate">{{ sshUrl }}</div>
<div class="absolute top-0 right-0">
<button type="button" class="flex items-center text-xs text-blue-300 hover:underline focus:underline">
<svg class="mr-1 h-3 w-3 fill-current" role="presentation"><use xlink:href="/img/icons.symbol.svg#actions__copy"></use></svg>
<svg class="h-3 w-3 fill-current mr-1" role="presentation"><use xlink:href="/img/icons.symbol.svg#actions__copy"></use></svg>
Copy <span class="visuallyhidden">SSH URL to your clipboard</span>
</button>
</div>
</div>
<div class="-mx-4 -mb-4 flex flex-wrap lg:-mb-6">
<div class="mb-4 px-4 w-full lg:w-1/2 lg:mb-6">
<div class="flex flex-wrap -mx-4 -mb-4 lg:-mb-6">
<div class="w-full lg:w-1/2 px-4 mb-4 lg:mb-6">
<div class="mb-1 text-xs text-gray-600">IP Address</div>
<div>1.2.3.4</div>
</div>
<div class="mb-4 px-4 w-full lg:w-1/2 lg:mb-6">
<div class="w-full lg:w-1/2 px-4 mb-4 lg:mb-6">
<div class="mb-1 text-xs text-gray-600">Region</div>
<div>eu-west-1</div>
</div>
<div class="mb-4 px-4 w-full lg:w-1/2 lg:mb-6">
<div class="w-full lg:w-1/2 px-4 mb-4 lg:mb-6">
<div class="mb-1 text-xs text-gray-600">PHP version</div>
<div v-text="phpVersion"></div>
</div>
<div class="mb-4 px-4 w-full lg:w-1/2 lg:mb-6">
<div class="w-full lg:w-1/2 px-4 mb-4 lg:mb-6">
<div class="mb-1 text-xs text-gray-600">Live development mode</div>
<div>Off</div>
</div>
@ -132,9 +132,12 @@
<script>
import ActionCards from '@/components/Environment/ActionCards'
import ApiClient from '@/api-client.js'
import ApplicationTags from '@/components/Application/ApplicationTags'
export default {
mixins: [ApiClient],
components: {
ActionCards,
ApplicationTags
@ -147,11 +150,11 @@ export default {
computed: {
application: function () {
return this.$attrs.applications[this.id]
return this.getApplication(this.id)
},
environment: function () {
return this.application.environments[this.environmentName]
return this.getEnvironment(this.id, this.environmentName)
},
gitUrl: function () {
@ -167,7 +170,7 @@ export default {
},
phpVersion: function () {
return this.environment.versions.php
return this.getVersion('php', this.environment)
}
}
}

View file

@ -1,13 +1,13 @@
<template>
<div>
<div class="w-full fixed top-0 z-30">
<div class="fixed top-0 w-full z-30">
<navbar></navbar>
<title-block :application="application">
<template slot="right">
<div class="flex items-center">
<div class="hidden md:block">
<button type="button" class="py-2 px-6 text-sm font-bold text-blue-300 uppercase border border-blue-300 hover:text-blue-400 hover:border-blue-400 focus:border-blue-400">
<button type="button" class="text-sm font-bold text-blue-300 uppercase py-2 px-6 border border-blue-300 hover:text-blue-400 hover:border-blue-400 focus:border-blue-400">
Steps to launch
</button>
</div>
@ -24,20 +24,20 @@
</div>
<div class="mt-48">
<div class="-mt-3 h-full flex flex-row-reverse">
<div class="ml-16 p-4 flex-1 overflow-x-hidden lg:p-12 lg:ml-56">
<div class="-mt-3 flex flex-row-reverse h-full">
<div class="flex-1 p-4 lg:p-12 ml-16 lg:ml-56 overflow-x-hidden">
<div class="mb-6">
<div class="mb-2 lg:flex lg:items-baseline">
<div class="lg:flex lg:items-baseline mb-2">
<div class="mr-16 mb-4 lg:mb-0">
<h1 class="mb-2 text-4xl font-thin">Environments</h1>
<h1 class="text-4xl font-thin mb-2">Environments</h1>
<application-tags :application="application" :types="$attrs.types"/>
<application-tags :type="getApplicationType(application)" :level="application.level"></application-tags>
</div>
<div class="flex-1 justify-between items-baseline lg:flex lg:flex-row-reverse">
<div class="lg:flex lg:flex-row-reverse flex-1 justify-between _bg-blue items-baseline">
<div class="w-full lg:w-1/2 xl:w-1/3">
<form action="#">
<input type="text" placeholder="Filter environments" class="py-2 px-3 w-full border border-gray-600 rounded">
<input type="text" placeholder="Filter environments" class="w-full py-2 px-3 border border-gray-600 rounded">
</form>
</div>
@ -48,13 +48,9 @@
</div>
</div>
<div class="spaced-y-6">
<quick-help :hidden="help.hidden"></quick-help>
<environment-cards :application="application" :id="id"/>
<task-log :tasks="application.tasks" v-if="application.tasks[0]"></task-log>
</div>
<quick-help :hidden="help.hidden"></quick-help>
<environment-cards :environments="application.environments" :id="id"></environment-cards>
<task-log :tasks="application.tasks" v-if="application.tasks[0]"></task-log>
</div>
<sidebar :links="[
@ -69,6 +65,7 @@
</template>
<script>
import ApiClient from '@/api-client.js'
import ApplicationTags from '@/components/Application/ApplicationTags'
import EnvironmentCards from '@/components/Environment/EnvironmentCards'
import QuickHelp from '@/components/Environment/QuickHelp'
@ -76,6 +73,8 @@ import TaskLog from '@/components/Environment/TaskLog/TaskLog'
import ToggleHelp from '@/components/Environment/ToggleHelp'
export default {
mixins: [ApiClient],
components: {
ApplicationTags,
EnvironmentCards,
@ -97,8 +96,8 @@ export default {
},
computed: {
application () {
return this.$attrs.applications[this.id]
application: function () {
return this.getApplication(this.id)
}
}
}

View file

@ -1,4 +1,4 @@
const { fontFamily, spacing } = require('tailwindcss/defaultTheme')
let defaultTheme = require('tailwindcss/defaultTheme')
module.exports = {
theme: {
@ -42,7 +42,7 @@ module.exports = {
md: '0 0 10px 0 rgba(0,0,0,0.1)'
},
fontFamily: {
sans: ['Proxima Nova', ...fontFamily.sans]
sans: ['Proxima Nova', ...defaultTheme.fontFamily.sans]
},
fontSize: {
'2xs': '.625rem' // 10px
@ -59,7 +59,7 @@ module.exports = {
}
},
plugins: [
require('tailwindcss-spaced-items')({ values: spacing }),
require('tailwindcss-spaced-items')({ values: defaultTheme.spacing }),
require('tailwindcss-visuallyhidden')()
]
}

5
tests/unit/.eslintrc.js Normal file
View file

@ -0,0 +1,5 @@
module.exports = {
env: {
jest: true
}
}

View file

@ -0,0 +1,215 @@
import _ from 'lodash'
const apiClient = require('../../src/api-client')
const methods = apiClient.default.methods
test('it can get the application types', () => {
const data = {
types: {
drupal: {
id: 'drupal',
name: 'Drupal'
},
symfony: {
id: 'symfony',
name: 'Symfony'
}
}
}
methods.setData(data)
expect(methods.getTypes()).toBe(data.types)
})
test('it can get the applications', () => {
const data = {
applications: {
1: {
id: 1,
name: 'Rebuilding Acquia',
machineName: 'rebuildingacquia',
type: 'drupal',
level: 'Enterprise',
environments: {},
tasks: {}
},
2: {
id: 2,
name: 'Oliver Davies',
machineName: 'oliverdavies',
type: 'drupal',
level: 'Professional',
environments: {},
tasks: {}
}
}
}
methods.setData(data)
expect(methods.getApplications()).toBe(data.applications)
})
test('it can get a single application', () => {
const data = {
applications: {
1: {
id: 1,
name: 'Rebuilding Acquia',
machineName: 'rebuildingacquia',
type: 'drupal',
level: 'Enterprise',
environments: {},
tasks: {}
},
2: {
id: 2,
name: 'Oliver Davies',
machineName: 'oliverdavies',
type: 'drupal',
level: 'Professional',
environments: {},
tasks: {}
}
}
}
methods.setData(data)
_.forEach([1, 2], (applicationId) => {
expect(methods.getApplication(applicationId))
.toBe(data.applications[applicationId])
})
})
test('it can get an environment for an application', () => {
const data = {
applications: {
1: {
id: 1,
name: 'Rebuilding Acquia',
machineName: 'rebuildingacquia',
type: 'drupal',
level: 'Enterprise',
environments: {
dev: {
name: 'Dev',
url: 'dev.oliverdavies.uk',
label: 'develop',
versions: {
php: '7.2'
}
},
stage: {
name: 'Stage',
url: 'stg.oliverdavies.uk',
label: 'master',
versions: {
php: '7.2'
}
},
prod: {
name: 'Prod',
url: 'oliverdavies.uk',
label: 'tags/2018-12-21',
versions: {
php: '7.2'
}
},
tasks: {}
}
}
}
}
methods.setData(data)
_.forEach(['dev', 'stage', 'prod'], (environmentName) => {
expect(methods.getEnvironment(1, environmentName))
.toBe(data.applications[1].environments[environmentName])
})
})
test('it can get the type of an application', () => {
const data = {
types: {
drupal: {
id: 'drupal',
name: 'Drupal'
},
symfony: {
id: 'symfony',
name: 'Symfony'
}
}
}
methods.setData(data)
const applications = {
drupal: {
type: 'drupal'
},
symfony: {
type: 'symfony'
}
}
_.forEach(applications, (application, expected) => {
expect(methods.getApplicationType(application)).toBe(data.types[expected])
})
})
test('it can get a version from an environment', () => {
const data = {
applications: {
1: {
id: 1,
name: 'Rebuilding Acquia',
machineName: 'rebuildingacquia',
type: 'drupal',
level: 'Enterprise',
environments: {
dev: {
name: 'Dev',
url: 'dev.rebuilding-acquia.com',
label: 'develop',
versions: {
php: '5.6'
}
},
stage: {
name: 'Stage',
url: 'stg.rebuilding-acquia.com',
label: 'master',
versions: {
php: '7.1'
}
},
prod: {
name: 'Prod',
url: 'rebuilding-acquia.com',
label: 'tags/2018-12-21',
versions: {
php: '7.2'
}
}
}
}
}
}
methods.setData(data)
const expected = {
dev: '5.6',
stage: '7.1',
prod: '7.2'
}
_.forEach(expected, (version, environment) => {
expect(methods.getVersion('php', data.applications[1].environments[environment]))
.toBe(version)
})
})

View file

@ -0,0 +1,47 @@
import AppBreadcrumb from '@/components/AppBreadcrumb.vue'
import { RouterLinkStub, shallowMount } from '@vue/test-utils'
const stubs = {
RouterLink: RouterLinkStub
}
test('it returns default values', () => {
const wrapper = shallowMount(AppBreadcrumb, { stubs })
expect(wrapper.vm.selectedOrganisation).toBe(null)
expect(wrapper.vm.selectedApplication).toBe(null)
expect(wrapper.vm.selectedEnvironment).toBe(null)
})
test('it returns the organisation and application names', () => {
const wrapper = shallowMount(AppBreadcrumb, {
propsData: {
application: {
name: 'Oliver Davies'
}
},
stubs
})
expect(wrapper.vm.selectedOrganisation).toBe('Rebuilding Acquia')
expect(wrapper.vm.selectedApplication).toBe('Oliver Davies')
expect(wrapper.vm.selectedEnvironment).toBe(null)
})
test('it returns the environment name', () => {
const wrapper = shallowMount(AppBreadcrumb, {
propsData: {
application: {
name: 'Oliver Davies'
},
environment: {
name: 'Dev'
}
},
stubs
})
expect(wrapper.vm.selectedOrganisation).toBe('Rebuilding Acquia')
expect(wrapper.vm.selectedApplication).toBe('Oliver Davies')
expect(wrapper.vm.selectedEnvironment).toBe('Dev')
})

10119
yarn.lock

File diff suppressed because it is too large Load diff