Merge acquia/main

This commit is contained in:
Oliver Davies 2025-10-02 08:19:35 +01:00
commit ebf1f79584
52 changed files with 15458 additions and 0 deletions

3
acquia/.browserslistrc Normal file
View file

@ -0,0 +1,3 @@
> 1%
last 2 versions
not ie <= 8

5
acquia/.editorconfig Normal file
View file

@ -0,0 +1,5 @@
[*.{js,jsx,ts,tsx,vue}]
indent_style = space
indent_size = 2
trim_trailing_whitespace = true
insert_final_newline = true

1
acquia/.envrc Normal file
View file

@ -0,0 +1 @@
use flake

17
acquia/.eslintrc.js Normal file
View file

@ -0,0 +1,17 @@
module.exports = {
root: true,
env: {
node: true
},
'extends': [
'plugin:vue/essential',
'@vue/standard'
],
rules: {
'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off'
},
parserOptions: {
parser: 'babel-eslint'
}
}

20
acquia/.gitignore vendored Normal file
View file

@ -0,0 +1,20 @@
.DS_Store
node_modules
/dist
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
/.terraform.lock.hcl
/.terraform/
/terraform.tfstate
/terraform.tfstate.*
# Nix
/.direnv/

37
acquia/README.md Normal file
View file

@ -0,0 +1,37 @@
# Rebuilding Acquia
[![Netlify Status](https://api.netlify.com/api/v1/badges/8cc12712-590a-47c7-bb78-d65e9249ac1d/deploy-status)](https://app.netlify.com/sites/rebuilding-acquia/deploys)
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)
[View the site live on Netlify &rarr;](https://rebuilding-acquia.oliverdavies.uk)
## Project setup
```
yarn install
```
### Compiles and hot-reloads for development
```
yarn run serve
```
### Compiles and minifies for production
```
yarn run build
```
### Run your tests
```
yarn run test
```
### Lints and fixes files
```
yarn run lint
```
### Customize configuration
See [Configuration Reference](https://cli.vuejs.org/config/).

12
acquia/babel.config.js Normal file
View file

@ -0,0 +1,12 @@
module.exports = {
presets: [
[
'@babel/preset-env',
{
targets: {
node: 'current'
}
}
]
]
}

27
acquia/flake.lock generated Normal file
View file

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

23
acquia/flake.nix Normal file
View file

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

26
acquia/jest.config.js Normal file
View file

@ -0,0 +1,26 @@
module.exports = {
moduleFileExtensions: [
'js',
'jsx',
'json',
'vue'
],
transform: {
'^.+\\.vue$': 'vue-jest',
'.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$': 'jest-transform-stub',
'^.+\\.jsx?$': 'babel-jest'
},
transformIgnorePatterns: [
'/node_modules/'
],
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1'
},
snapshotSerializers: [
'jest-serializer-vue'
],
testMatch: [
'**/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)'
],
testURL: 'http://localhost/'
}

111
acquia/main.tf Normal file
View file

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

8
acquia/netlify.toml Normal file
View file

@ -0,0 +1,8 @@
[build]
command = """\
yarn lint
yarn test:unit
yarn build
"""
publish = "dist"
environment = { NODE_VERSION = "v12.6.0", YARN_VERSION = "1.13.0" }

40
acquia/package.json Normal file
View file

@ -0,0 +1,40 @@
{
"name": "rebuilding-acquia2",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"test": "jest",
"lint": "vue-cli-service lint",
"test:unit": "vue-cli-service test:unit"
},
"dependencies": {
"postcss-nested": "^4.1.2",
"sugarss": "^2.0.0",
"tailwindcss": "^1.1.2",
"tailwindcss-spaced-items": "^0.1.0",
"tailwindcss-visuallyhidden": "^1.0.2",
"vue": "^2.6.6",
"vue-router": "^3.1.3"
},
"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",
"@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",
"eslint": "^5.8.0",
"eslint-plugin-vue": "^5.0.0",
"jest": "^24.9.0",
"postcss-import": "^12.0.1",
"vue-template-compiler": "^2.5.21"
}
}

16
acquia/postcss.config.js Normal file
View file

@ -0,0 +1,16 @@
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) || []
})
]
}

1
acquia/public/_redirects Normal file
View file

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

BIN
acquia/public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 327 KiB

BIN
acquia/public/img/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

17
acquia/public/index.html Normal file
View file

@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="en" class="h-full">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title>Rebuilding Acquia</title>
</head>
<body>
<noscript>
<strong>We're sorry but rebuilding-acquia doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app" class="flex flex-1 flex-col h-full"></div>
<!-- built files will be auto injected -->
</body>
</html>

BIN
acquia/screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

22
acquia/src/App.vue Normal file
View file

@ -0,0 +1,22 @@
<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>
</template>
<style src="./assets/css/tailwind.css"/>
<script>
import { applications, types } from '@/data'
export default {
data () {
return {
applications,
types
}
}
}
</script>

View file

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

View file

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

View file

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

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

BIN
acquia/src/assets/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

View file

@ -0,0 +1,75 @@
<template>
<div class="py-1 overflow-y-scroll">
<ul class="-ml-3 flex">
<li class="ml-3 flex items-center">
<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>
</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">
<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>
</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">
<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>
</div>
</li>
</ul>
</div>
</template>
<script>
export default {
props: {
application: Object,
environment: {
type: Object,
required: false
}
},
computed: {
selectedOrganisation: function () {
if (!this.application) {
return null
}
return 'Rebuilding Acquia'
},
selectedApplication: function () {
if (!this.application) {
return null
}
return this.application.name
},
selectedEnvironment: function () {
if (!this.application) {
return null
}
if (!this.environment) {
return null
}
return this.environment.name
}
}
}
</script>

View file

@ -0,0 +1,78 @@
<template>
<div>
<div v-if="display == 'grid'" class="bg-white p-4 border-gray-400 rounded-sm border flex-1 shadow">
<div class="flex h-full">
<div class="flex-1 w-5/6 flex flex-col justify-between">
<div>
<div>
<router-link :to="{name: 'environments', params: {id: id}}" class="text-blue-300 no-underline hover:underline focus:underline focus:outline-none"><h2 class="mb-1">{{ application.name }}</h2></router-link>
</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"/>
</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>
</div>
</div>
<div v-if="display == 'list'" class="p-3 bg-white 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>
<div class="px-2 w-2/5">
<a href="#0" class="text-blue-300 no-underline hover:underline focus:underline" v-text="prodUrl"/>
</div>
<div class="px-1 w-1/5">
<php-version :application="application"/>
</div>
</div>
<div class="px-2">
<star-toggle :application="application" :starred="starred" @toggle="starred = !starred"/>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import PhpVersion from '@/components/Application/PhpVersion'
import StarToggle from '@/components/Application/StarToggle'
export default {
components: { PhpVersion, StarToggle },
props: {
application: Object,
display: String,
id: String
},
data () {
return {
starred: false
}
},
computed: {
prodUrl () {
return this.application.environments['prod'].url
}
}
}
</script>

View file

@ -0,0 +1,23 @@
<template>
<div>
<div class="bg-gray-100 rounded overflow-hidden">
<button type="button" class="p-2 focus:outline-none focus:bg-blue-300 focus:text-white" :class="[mode == 'grid' ? 'bg-blue-100 text-white' : 'text-gray-700']" @click="$emit('display-changed', 'grid')">
<svg class="h-5 w-5 fill-current" role="presentation"><use xlink:href="/img/icons.symbol.svg#actions__grid"></use></svg>
<span class="visuallyhidden">Grid</span>
</button>
<button type="button" class="p-2 focus:outline-none focus:bg-blue-300 focus:text-white" :class="[mode == 'list' ? 'bg-blue-100 text-white' : 'text-gray-700']" @click="$emit('display-changed', 'list')">
<svg class="h-5 w-5 fill-current" role="presentation"><use xlink:href="/img/icons.symbol.svg#actions__list"></use></svg>
<span class="visuallyhidden">List</span>
</button>
</div>
</div>
</template>
<script>
export default {
props: {
mode: String
}
}
</script>

View file

@ -0,0 +1,30 @@
<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>
</div>
</template>
<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]
}
}
}
</script>

View file

@ -0,0 +1,24 @@
<template>
<div class="flex items-center text-sm">
<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>
export default {
props: {
application: Object
},
computed: {
phpVersion: function () {
if (this.application.type !== 'drupal') {
return null
}
return this.application.environments['prod'].versions['php']
}
}
}
</script>

View file

@ -0,0 +1,18 @@
<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']">
<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>
</button>
</template>
<script>
export default {
props: {
application: Object,
starred: {
type: Boolean,
default: false
}
}
}
</script>

View file

@ -0,0 +1,44 @@
<template>
<div class="border border-gray-400 rounded shadow bg-white overflow-hidden">
<div class="border-t-4 border-teal">
<div class="border-b border-gray-400">
<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>
<div class="text-xs text-gray-600">{{ description }}</div>
</div>
<div class="mr-3">
<slot name="icon"></slot>
</div>
</div>
</div>
</div>
<div>
<slot name="buttons"></slot>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
title: {
type: String,
required: true
},
description: {
type: String,
required: true
}
}
}
</script>
<style scoped>
svg
@apply h-8 w-8 fill-current text-teal
</style>

View file

@ -0,0 +1,19 @@
<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">
<slot></slot>
<span class="block" v-text="label"></span>
</button>
</template>
<script>
export default {
props: {
label: { type: String }
}
}
</script>
<style scoped>
svg
@apply h-6 w-6 fill-current mb-2
</style>

View file

@ -0,0 +1,97 @@
<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>
</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' ]">
<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">
<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">
<action-card-button label="Switch">
<svg role="presentation"><use xlink:href="/img/icons.symbol.svg#actions__switch"></use></svg>
</action-card-button>
</li>
</ul>
</action-card>
</div>
<div class="px-4 w-full md:block md:w-1/3" :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">
<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">
<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">
<action-card-button label="Restore" :disabled="isProduction" :class="{
'text-blue-300': !isProduction,
'text-gray-500 cursor-not-allowed': isProduction
}">
<svg role="presentation"><use xlink:href="/img/icons.symbol.svg#actions__restore"></use></svg>
</action-card-button>
</li>
</ul>
</action-card>
</div>
<div class="px-4 w-full md:block md:w-1/3" :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">
<action-card-button label="Copy">
<svg role="presentation"><use xlink:href="/img/icons.symbol.svg#actions__copy"></use></svg>
</action-card-button>
</li>
</ul>
</action-card>
</div>
</div>
</div>
</template>
<script>
import ActionCard from '@/components/Environment/ActionCard'
import ActionCardButton from '@/components/Environment/ActionCardButton'
export default {
components: {
ActionCard,
ActionCardButton
},
props: {
environment: Object,
isProduction: Boolean
},
data () {
return {
active: 'code'
}
}
}
</script>

View file

@ -0,0 +1,56 @@
<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-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>
<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="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>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
application: Object,
id: String
}
}
</script>

View file

@ -0,0 +1,71 @@
<template>
<div class="bg-white border-t-10 border-purple" :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>
Quick help
</div>
</div>
<div class="p-4">
<div class="-mb-5 block leading-normal lg:hidden">
<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>
</div>
</div>
<div class="hidden lg:block">
<div class="mb-6 w-full lg:w-auto lg:flex">
<button
v-for="(item, i) in items"
:key="item.subject"
type="button"
class="hover:underline focus:underline mr-6 focus:outline-none"
:class="[ i === selected ? 'text-gray-700' : 'text-blue-300' ]"
@click="selected = i"
>{{ item.subject }}</button>
</div>
<div class="leading-normal">
<p class="text-sm text-gray-600">
{{ items[selected].text }}
<a href="#0" class="text-blue-300 no-underline hover:underline focus:underline">Learn more</a>
</p>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
hidden: Boolean
},
data () {
return {
selected: 0,
items: [
{
subject: 'Deploying code',
text: 'Drag a code element from one environment to another environment, which creates a new tag for the HEAD of that branch and then deploys the tag.'
},
{
subject: 'Copying files',
text: 'Drag a file element from one environment to another environment, which writes new files, overwrites existing files, and ignores files already in place.'
},
{
subject: 'Copying database',
text: 'Drag a database element from one environment to another environment, which overwrites the target database(s). Dragging database elements with more than one database displays a dialog box that allows you to select the databases to deploy'
},
{
subject: 'Task Log',
text: 'The Task Log is displayed below your Acquia Cloud environments and contains information on all of the actions taken against your application, including code deployments, configuration updates and environment specific changes.'
}
]
}
}
}
</script>

View file

@ -0,0 +1,37 @@
<template>
<div>
<div class="pb-4 pt-6 px-4 bg-gray-300">
<h2 class="font-normal text-lg">Task Log</h2>
</div>
<div>
<ul class="list-reset bg-white">
<li v-for="task in reversedTasks" :key="task.text">
<task-log-item :task="task"></task-log-item>
</li>
</ul>
</div>
</div>
</template>
<script>
import reverse from 'lodash/reverse'
import TaskLogItem from '@/components/Environment/TaskLog/TaskLogItem'
export default {
components: {
TaskLogItem
},
props: {
tasks: Array
},
computed: {
reversedTasks: function () {
let tasks = this.tasks
return reverse(tasks)
}
}
}
</script>

View file

@ -0,0 +1,91 @@
<template>
<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 && 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-gray-600 text-2xs">{{ task.times.display }}</div>
</div>
<div>
<button type="button" @click="open = !open">
<svg v-if="!open" class="h-5 w-5 text-gray-600 fill-current" role="presentation"><use xlink:href="/img/icons.symbol.svg#alpha__chevron"></use></svg>
<svg v-else class="h-5 w-5 text-gray-600 fill-current" style="transform: rotate(180deg)" role="presentation"><use xlink:href="/img/icons.symbol.svg#alpha__chevron"></use></svg>
</button>
</div>
</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="px-4 mb-4">
<div class="mb-1 text-xs uppercase">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>
<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>
<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>
<div class="px-4 mb-4">
<div class="mb-1 text-xs uppercase">Status</div>
<div class="text-sm font-bold">{{ status }}</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
task: Object
},
data () {
return {
open: false
}
},
computed: {
status: function () {
if (this.task.loading) {
return 'In progress'
}
if (!this.task.success) {
return 'Failed'
}
return 'Completed'
}
}
}
</script>
<style scoped>
.rotates
animation: rotation 1s infinite linear
@-webkit-keyframes rotation
from
-webkit-transform: rotate(0deg)
to
-webkit-transform: rotate(-359deg)
</style>

View file

@ -0,0 +1,16 @@
<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>
{{ hidden ? 'Show quick help' : 'Hide quick help' }}
</button>
</div>
</template>
<script>
export default {
props: {
hidden: Boolean
}
}
</script>

View file

@ -0,0 +1,38 @@
<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 }">
<button
type="button"
class="text-xs no-underline hover:underline flex flex-col items-center justify-center"
:class="{
'text-gray-400 cursor-not-allowed': link.disabled,
'text-blue-300': !link.disabled,
}"
: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>
{{ link.title }}
</button>
</li>
</ul>
</div>
</template>
<script>
import { omitBy, _ } from 'lodash'
export default {
props: {
links: Array
},
computed: {
filteredLinks: function () {
let links = this.links
return omitBy(links, _.isEmpty)
}
}
}
</script>

View file

@ -0,0 +1,31 @@
<template>
<div class="bg-white">
<div class="mx-1 px-4 lg:px-6 flex items-center leading-none justify-between">
<div class="flex-none md:mr-24">
<router-link to="/"><img src="/img/logo.png" alt="" class="h-10"></router-link>
</div>
<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>
</nav>
</div>
<div class="hidden md:flex md:items-center">
<div><a class="text-sm text-blue-300 no-underline hover:underline uppercase" href="#0">Help</a></div>
<div class="ml-6"><button type="button">
<span class="hidden">Menu</span>
<svg class="fill-current text-blue-300" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" width="20" height="20"><path id="select-product" class="cls-1" d="M.77,0h4A.76.76,0,0,1,5.5.77v4a.76.76,0,0,1-.77.77h-4A.76.76,0,0,1,0,4.73v-4A.76.76,0,0,1,.77,0ZM7.25.77v4A.76.76,0,0,0,8,5.5h4a.76.76,0,0,0,.77-.77v-4A.76.76,0,0,0,12,0H8A.76.76,0,0,0,7.25.77Zm7.25,0v4a.76.76,0,0,0,.77.77h4A.76.76,0,0,0,20,4.73v-4A.76.76,0,0,0,19.23,0h-4A.76.76,0,0,0,14.5.77ZM0,8v4a.76.76,0,0,0,.77.77h4A.76.76,0,0,0,5.5,12V8a.76.76,0,0,0-.77-.77h-4A.76.76,0,0,0,0,8ZM7.25,8v4a.76.76,0,0,0,.77.77h4a.76.76,0,0,0,.77-.77V8A.76.76,0,0,0,12,7.25H8A.76.76,0,0,0,7.25,8ZM14.5,8v4a.76.76,0,0,0,.77.77h4A.76.76,0,0,0,20,12V8a.76.76,0,0,0-.77-.77h-4A.76.76,0,0,0,14.5,8ZM0,15.27v4A.76.76,0,0,0,.77,20h4a.76.76,0,0,0,.77-.77v-4a.76.76,0,0,0-.77-.77h-4A.76.76,0,0,0,0,15.27Zm7.25,0v4A.76.76,0,0,0,8,20h4a.76.76,0,0,0,.77-.77v-4A.76.76,0,0,0,12,14.5H8A.76.76,0,0,0,7.25,15.27Zm7.25,0v4a.76.76,0,0,0,.77.77h4a.76.76,0,0,0,.77-.77v-4a.76.76,0,0,0-.77-.77h-4A.76.76,0,0,0,14.5,15.27Z"></path></svg>
</button></div>
<div class="ml-6"><button type="button">
<span class="hidden">Activity</span>
<svg class="h-6 w-6 text-blue-300 fill-current" role="presentation"><use xlink:href="/img/icons.symbol.svg#objects__notification"></use></svg>
</button></div>
<div class="ml-6">
<img class="w-5 h-5 rounded-full" src="https://avatars1.githubusercontent.com/u/339813?s=460&v=4" alt="">
</div>
</div>
</div>
</div>
</template>

View file

@ -0,0 +1,31 @@
<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>
<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="{
'bg-gray-600 text-white hover:text-white': link.active,
'border-transparent hover:bg-gray-400': !link.active,
'text-gray-500 cursor-not-allowed': link.disabled,
'text-gray-600 hover:text-blue-300': !link.disabled && !link.active,
}"
>
<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>
</a>
</li>
</ul>
</div>
</div>
</template>
<script>
export default {
props: {
links: Array
}
}
</script>

View file

@ -0,0 +1,29 @@
<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">
<app-breadcrumb :application="application" :environment="environment"></app-breadcrumb>
</div>
<slot name="right"></slot>
</div>
</div>
</template>
<script>
import AppBreadcrumb from '@/components/AppBreadcrumb'
export default {
props: {
application: Object,
environment: {
type: Object,
required: false
}
},
components: {
AppBreadcrumb
}
}
</script>

249
acquia/src/data.js Normal file
View file

@ -0,0 +1,249 @@
module.exports = {
types: {
drupal: {
id: 'drupal',
name: 'Drupal'
},
nodejs: {
id: 'nodejs',
name: 'Node.js'
}
},
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: '7.1'
}
},
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.1'
}
},
ra: {
name: 'RA',
url: 'ra.rebuilding-acquia.com',
label: 'tags/WELCOME',
versions: {
php: '7.2'
}
}
},
tasks: [
{
text: 'Commit: fdac923 Merge branch \'update-password-policy\' refs/heads/master',
user: 'system',
times: {
display: 'Dec 19, 2018 3:48:29 PM UTC +0000',
started: 'Dec 19, 2018 3:48:29 PM UTC +0000',
completed: 'Dec 19, 2018 3:48:29 PM UTC +0000'
},
loading: false,
success: true
},
{
text: 'Commit: 9688c41 Update IP address refs/heads/master',
user: 'system',
times: {
display: 'Dec 19, 2018 3:50:31 PM UTC +0000',
started: 'Dec 19, 2018 3:50:31 PM UTC +0000',
completed: 'Dec 19, 2018 3:50:31 PM UTC +0000'
},
loading: false,
success: true
},
{
text: 'Deploy code from test to prod',
user: 'system',
times: {
display: 'Dec 19, 2018 3:55:29 PM UTC +0000',
started: 'Dec 19, 2018 3:55:29 PM UTC +0000',
completed: 'Dec 19, 2018 3:55:29 PM UTC +0000'
},
loading: false,
success: true
},
{
text: 'Deploy the code reference master to dev',
user: 'system',
times: {
display: 'Dec 20, 2018 10:13:48 PM UTC +0000',
started: 'Dec 20, 2018 10:13:48 PM UTC +0000',
completed: 'Dec 20, 2018 10:13:48 PM UTC +0000'
},
loading: false,
success: true
},
{
text: 'Deploy the code reference tags/2018-12-19 to test',
user: 'system',
times: {
display: 'Dec 20, 2018 10:25:07 PM UTC +0000',
started: 'Dec 20, 2018 10:25:07 PM UTC +0000',
completed: 'Dec 20, 2018 10:25:07 PM UTC +0000'
},
loading: false,
success: true
},
{
text: 'Commit: e75c4a3 Merge branch \'voucher-code-bug-34\' refs/heads/master',
user: 'system',
times: {
display: 'Dec 21, 2018 12:55:49 PM UTC +0000',
started: 'Dec 21, 2018 12:55:49 PM UTC +0000',
completed: 'Dec 21, 2018 12:55:49 PM UTC +0000'
},
status: 'Failed',
loading: false,
success: false
},
{
text: 'Commit: 398945c Ensure stage_file_proxy is enabled refs/heads/master',
user: 'system',
times: {
display: 'Dec 21, 2018 1:09:19 PM UTC +0000',
started: 'Dec 21, 2018 1:09:19 PM UTC +0000',
completed: 'Dec 21, 2018 1:09:19 PM UTC +0000'
},
loading: false,
success: true
},
{
text: 'Commit: 96ac151 Fix syntax refs/heads/master',
user: 'system',
times: {
display: 'Dec 21, 2018 1:23:43 PM UTC +0000',
started: 'Dec 21, 2018 1:23:43 PM UTC +0000',
completed: 'Dec 21, 2018 1:23:43 PM UTC +0000'
},
loading: false,
success: true
},
{
text: 'Commit: 8056d51 Add production URLs as settings refs/heads/master',
user: 'system',
times: {
display: 'Dec 21, 2018 1:34:13 PM UTC +0000',
started: 'Dec 21, 2018 1:34:13 PM UTC +0000',
completed: 'Dec 21, 2018 1:34:13 PM UTC +0000'
},
status: 'In progress',
loading: true,
success: true
}
]
},
2: {
id: 2,
name: 'Oliver Davies',
machineName: 'oliverdavies',
type: 'drupal',
level: 'Professional',
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: [
{
text: 'Create database opdaviestest in Dev.',
user: 'system',
times: {
display: 'Dec 23, 2018 11:26:48 PM UTC +0000',
started: 'Dec 23, 2018 11:26:50 PM UTC +0000',
completed: 'Dec 23, 2018 11:26:52 PM UTC +0000'
},
loading: false,
success: true
},
{
text: 'Create database opdaviestest in Stage.',
user: 'system',
times: {
display: 'Dec 23, 2018 11:26:48 PM UTC +0000',
started: 'Dec 23, 2018 11:26:50 PM UTC +0000',
completed: 'Dec 23, 2018 11:26:52 PM UTC +0000'
},
loading: false,
success: true
},
{
text: 'Install Drupal 8 to Dev.',
user: 'oliver@oliverdavies.uk (oliver@oliverdavies.uk)',
times: {
display: 'Dec 23, 2018 11:33:52 PM UTC +0000',
started: 'Dec 23, 2018 11:33:53 PM UTC +0000',
completed: 'Dec 23, 2018 11:37:21 PM UTC +0000'
},
loading: false,
success: true
},
{
text: 'Commit: 9736ef5 Importing site archive refs/heads/master',
user: 'vcs.commit',
times: {
display: 'Dec 23, 2018 11:36:29 PM UTC +0000',
started: 'Dec 23, 2018 11:36:30 PM UTC +0000',
completed: 'Dec 23, 2018 11:37:16 PM UTC +0000'
},
loading: false,
success: true
},
{
text: 'Commit: 0ab620f Initial commit to Acquia Git starter repo. refs/tags/pre-import-2018-12-23',
user: 'vcs.commit',
times: {
display: 'Dec 23, 2018 11:36:30 PM UTC +0000',
started: 'Dec 23, 2018 11:36:32 PM UTC +0000',
completed: 'Dec 23, 2018 11:36:33 PM UTC +0000'
},
loading: false,
success: true
}
]
}
}
}

23
acquia/src/main.js Normal file
View file

@ -0,0 +1,23 @@
import App from './App.vue'
import HeaderButtons from '@/components/HeaderButtons'
import Navbar from '@/components/Navbar'
import routes from './routes'
import Sidebar from '@/components/Sidebar'
import TitleBlock from '@/components/TitleBlock'
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.config.productionTip = false
Vue.component('header-buttons', HeaderButtons)
Vue.component('navbar', Navbar)
Vue.component('sidebar', Sidebar)
Vue.component('title-block', TitleBlock)
Vue.use(VueRouter)
new Vue({
render: h => h(App),
router: new VueRouter(routes)
}).$mount('#app')

25
acquia/src/routes.js Normal file
View file

@ -0,0 +1,25 @@
import Applications from '@/views/Applications'
import Environment from '@/views/Environment'
import Environments from '@/views/Environments'
export default {
routes: [
{
path: '/',
name: 'applications',
component: Applications
},
{
path: '/:id/environments',
name: 'environments',
component: Environments,
props: true
},
{
path: '/:id/environments/:environmentName',
name: 'environment',
component: Environment,
props: true
}
]
}

View file

@ -0,0 +1,104 @@
<template>
<div>
<div class="w-full fixed top-0 z-30">
<navbar></navbar>
<title-block>
<template slot="right">
<header-buttons :links="[
{ title: 'Add Application', icon: 'alpha__new-app', disabled: false },
]"></header-buttons>
</template>
</title-block>
</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="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>
</div>
<application-display-switcher class="mr-3 hidden lg:flex-1 lg:flex lg:justify-end" :mode="display" @display-changed="handleDisplay"></application-display-switcher>
<div class="justify-between items-baseline lg:flex lg:flex-row-reverse">
<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">
</form>
</div>
</div>
</div>
</div>
<div class="-mt-6 -mx-3 flex flex-wrap">
<div
v-for="application in sortedApplications"
:key="application.id"
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>
</div>
</div>
<sidebar :links="[
{ title: 'All', icon: 'alpha__grid', active: true, disabled: false },
{ title: 'Starred', icon: 'state__starred', active: false, disabled: false },
{ title: 'Recents', icon: 'objects__recent', active: false, disabled: false },
]"></sidebar>
</div>
</div>
</div>
</template>
<script>
import sortBy from 'lodash/sortBy'
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
}
},
components: {
ApplicationCard,
ApplicationDisplaySwitcher
},
data () {
return {
display: 'grid'
}
},
computed: {
sortedApplications: function () {
return sortBy(this.applications, [function (a) { return a.name }])
}
},
methods: {
handleDisplay (mode) {
this.display = mode
}
}
}
</script>

View file

@ -0,0 +1,174 @@
<template>
<div>
<div class="fixed top-0 w-full z-30">
<navbar></navbar>
<title-block :application="application" :environment="environment">
<template slot="right">
<header-buttons :links="[
!isProduction ? { title: 'Install Drupal', icon: 'actions__upload', disabled: false } : {},
{ title: 'Clear Varnish', icon: 'alpha__clear-cache', disabled: false },
!isProduction ? { title: 'Live development', icon: 'actions__publish-settings', disabled: false } : { title: 'Production mode', icon: 'state__locked', disabled: false },
{ title: 'Rename', icon: 'actions__edit', disabled: false },
{ title: 'Configure', icon: 'actions__setting', disabled: false },
]"></header-buttons>
</template>
</title-block>
</div>
<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>
<div>
<h1 class="mb-2 text-4xl font-thin">Overview</h1>
<application-tags :application="application" :types="$attrs.types"/>
</div>
<div class="my-10">
<a :href="environment.url" class="flex items-center text-sm text-blue-300 no-underline hover:underline focus:underline">
<svg class="h-6 w-6 fill-current mr-1" role="presentation"><use xlink:href="/img/icons.symbol.svg#alpha__globe"></use></svg>
{{ environment.url }}
</a>
</div>
<div class="spaced-y-10">
<div>
<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>
<p class="text-lg font-hairline text-gray-700">Uptime monitoring</p>
</div>
<div class="mt-6 text-center leading-normal">
<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">
<button type="button" class="btn">Enable Uptime</button>
<button type="button" class="btn is-secondary">Learn more</button>
</div>
</div>
</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="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="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>
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="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>
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="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="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="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="mb-1 text-xs text-gray-600">Live development mode</div>
<div>Off</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<sidebar :links="[
{ title: 'Overview', icon: 'sections__applications', active: true, disabled: false },
{ title: 'Stack Metrics', icon: 'sections__activity', active: false, disabled: false },
{ title: 'Servers', icon: 'alpha__server', active: false, disabled: false },
{ title: 'Databases', icon: 'alpha__database-38', active: false, disabled: false },
{ title: 'Domains', icon: 'alpha__globe', active: false, disabled: false },
{ title: 'Users & Keys', icon: 'locations__keys', active: false, disabled: false },
{ title: 'SSL', icon: 'alpha__ssl', active: false, disabled: false },
{ title: 'Logs', icon: 'alpha__page', active: false, disabled: false },
{ title: 'Insight', icon: 'alpha__insight', active: false, disabled: false },
{ title: 'Scheduled jobs', icon: 'alpha__recent', active: false, disabled: false },
{ title: 'Uptime', icon: 'alpha__grid', active: false, disabled: false },
{ title: 'Variables', icon: 'objects__variable', active: false, disabled: false },
]"></sidebar>
</div>
</div>
</div>
</template>
<script>
import ActionCards from '@/components/Environment/ActionCards'
import ApplicationTags from '@/components/Application/ApplicationTags'
export default {
components: {
ActionCards,
ApplicationTags
},
props: {
id: String,
environmentName: String
},
computed: {
application: function () {
return this.$attrs.applications[this.id]
},
environment: function () {
return this.application.environments[this.environmentName]
},
gitUrl: function () {
return `${this.application.machineName}@svn-1234.prod.hosting.acquia.com:${this.application.machineName}.git`
},
isProduction: function () {
return this.environmentName === 'prod'
},
sshUrl: function () {
return `${this.application.machineName}.${this.environmentName}@staging-1234.prod.hosting.acquia.com`
},
phpVersion: function () {
return this.environment.versions.php
}
}
}
</script>

View file

@ -0,0 +1,105 @@
<template>
<div>
<div class="w-full fixed top-0 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">
Steps to launch
</button>
</div>
<header-buttons class="ml-5" :links="[
{ title: 'Add database', icon: 'actions__new-database', disabled: false },
{ title: 'Git Info', icon: 'actions__info', disabled: false },
{ title: 'Rename', icon: 'actions__edit', disabled: false },
{ title: 'Cancel', icon: 'actions__remove--circle', disabled: false },
]"></header-buttons>
</div>
</template>
</title-block>
</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="mb-6">
<div class="mb-2 lg:flex lg:items-baseline">
<div class="mr-16 mb-4 lg:mb-0">
<h1 class="mb-2 text-4xl font-thin">Environments</h1>
<application-tags :application="application" :types="$attrs.types"/>
</div>
<div class="flex-1 justify-between items-baseline lg:flex lg:flex-row-reverse">
<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">
</form>
</div>
<div class="flex-1">
<toggle-help @toggle="help.hidden = !help.hidden"></toggle-help>
</div>
</div>
</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>
</div>
<sidebar :links="[
{ title: 'Environments', icon: 'alpha__grid', active: true, disabled: false },
{ title: 'Product Keys', icon: 'locations__keys', active: false, disabled: true },
{ title: 'Security', icon: 'alpha__security', active: false, disabled: true },
{ title: 'Acquia Search', icon: 'actions__search', active: false, disabled: false },
]"></sidebar>
</div>
</div>
</div>
</template>
<script>
import ApplicationTags from '@/components/Application/ApplicationTags'
import EnvironmentCards from '@/components/Environment/EnvironmentCards'
import QuickHelp from '@/components/Environment/QuickHelp'
import TaskLog from '@/components/Environment/TaskLog/TaskLog'
import ToggleHelp from '@/components/Environment/ToggleHelp'
export default {
components: {
ApplicationTags,
EnvironmentCards,
QuickHelp,
TaskLog,
ToggleHelp
},
props: {
id: String
},
data () {
return {
help: {
hidden: false
}
}
},
computed: {
application () {
return this.$attrs.applications[this.id]
}
}
}
</script>

65
acquia/tailwind.config.js Normal file
View file

@ -0,0 +1,65 @@
const { fontFamily, spacing } = require('tailwindcss/defaultTheme')
module.exports = {
theme: {
colors: {
inherit: 'inherit',
black: '#22292f',
blue: {
100: '#29aae1',
200: '#018dc7',
300: '#0e68a7',
400: '#004f86'
},
green: '#398002',
gray: {
100: '#f8fafc', // lightest
200: '#eee', // lighter
300: '#ddd', // light
400: '#bbb', // default
500: '#888', // dark
600: '#555', // darker
700: '#333' // darkest
},
orange: {
100: '#fa9903',
200: '#ffb401'
},
purple: '#991faf',
red: '#cc1f1a',
teal: '#3f7b8f',
transparent: 'transparent',
white: '#fff',
yellow: '#faf8df'
},
extend: {
borderWidth: {
3: '3px',
6: '6px',
10: '10px'
},
boxShadow: {
md: '0 0 10px 0 rgba(0,0,0,0.1)'
},
fontFamily: {
sans: ['Proxima Nova', ...fontFamily.sans]
},
fontSize: {
'2xs': '.625rem' // 10px
},
spacing: {
'2px': '2px',
48: '10rem',
56: '14rem',
64: '16rem'
},
width: {
56: '14rem'
}
}
},
plugins: [
require('tailwindcss-spaced-items')({ values: spacing }),
require('tailwindcss-visuallyhidden')()
]
}

11975
acquia/yarn.lock Normal file

File diff suppressed because it is too large Load diff