diff --git a/dashboard/src/components/entity-card.tsx b/dashboard/src/components/entity-card.tsx index d49e08a754..7c113b2a54 100644 --- a/dashboard/src/components/entity-card.tsx +++ b/dashboard/src/components/entity-card.tsx @@ -10,7 +10,7 @@ import React, { ReactNode } from "react" import styled from "@emotion/styled" import { Entity } from "../containers/overview" import { colors } from "../styles/variables" -import { Facebook } from "react-content-loader" +import { Facebook as ContentLoader } from "react-content-loader" interface EntityCardProps { type: EntityType @@ -19,10 +19,11 @@ const EntityCard = styled.div` max-height: 13rem; background-color: ${props => (props && props.type && colors.cardTypes[props.type] || "white")}; margin-right: 1rem; - box-shadow: 0px 0px 16px rgba(0, 0, 0, 0.14); + box-shadow: 2px 2px 9px rgba(0,0,0,0.14); border-radius: 4px; width: 100%; margin-top: 1rem; + padding: .75rem; &:first-of-type { margin-top: 0; @@ -32,43 +33,41 @@ const EntityCard = styled.div` margin-right: 0; } ` + const Header = styled.div` width: 100%; - padding: .6rem .75rem; - height: 3rem; + display:flex; + justify-content: space-between; ` const Content = styled.div` width: 100%; - padding: 0rem .75rem .75rem .75rem; position: relative; max-height: 10rem; + padding-top: .75rem; &:empty -{ - display:none; -} + { + display:none; + } ` -type StateProps = { +type StateContainerProps = { state: string, } -const State = styled.div` +const StateContainer = styled.div` padding: 0 .5rem; margin-left: auto; - background-color: ${props => (props && props.state && colors.state[props.state] || colors.gardenGrayLight)}; - display: ${props => (props && props.state && colors.state[props.state] && "flex" || "none")}; + background-color: ${props => (props && props.state ? colors.state[props.state] : colors.gardenGrayLight)}; + display: ${props => (props && props.state && colors.state[props.state] ? "flex" : "none")}; align-items: center; - margin-top: -0.5rem; - -border-radius: 4px; - -font-weight: 500; -font-size: 11px; -line-height: 16px; -text-align: center; -letter-spacing: 0.02em; - -color: #FFFFFF; + border-radius: 0.25rem; + font-weight: 500; + font-size: 0.6875rem; + line-height: 1rem; + text-align: center; + letter-spacing: 0.02em; + color: #FFFFFF; + height: 1rem; ` const Tag = styled.div` @@ -81,17 +80,16 @@ const Tag = styled.div` letter-spacing: 0.01em; color: #90A0B7; ` + const Name = styled.div` - height: 1rem; font-size: 0.9375rem; + font-weight: 500; color: rgba(0, 0, 0, .87); + padding-top: 0.125rem; ` -const Row = styled.div` - display: flex; - align-items: center; -` type EntityType = "service" | "test" | "task" + interface Props { type: EntityType children: ReactNode @@ -107,19 +105,19 @@ export default ({ return (
- {type.toUpperCase()} - +
+ {type.toUpperCase()} {name} - {state && ( - - {state} - - )} - +
+ {state && ( + + {state} + + )}
{isLoading && ( - + )} {!isLoading && children} diff --git a/dashboard/src/components/entity-result.tsx b/dashboard/src/components/entity-result.tsx index 58ef994c59..eb6eb19662 100644 --- a/dashboard/src/components/entity-result.tsx +++ b/dashboard/src/components/entity-result.tsx @@ -156,7 +156,7 @@ export default ({ `, )} > -
+
diff --git a/dashboard/src/components/ingresses.tsx b/dashboard/src/components/ingresses.tsx index b399a5b1cd..a4eed8f243 100644 --- a/dashboard/src/components/ingresses.tsx +++ b/dashboard/src/components/ingresses.tsx @@ -34,7 +34,7 @@ const Ingresses = styled.div` } ` const LinkContainer = styled.div` - padding-bottom: 1rem; + padding-top: .25rem; font-size: .75rem; &:last-of-type { @@ -69,7 +69,7 @@ export default ({ ingresses }: IngressesProp) => { return ( - {(ingresses || []).map((ingress, index) => { + {(ingresses || []).map((ingress) => { const url = getIngressUrl(ingress) return ( @@ -83,9 +83,6 @@ export default ({ ingresses }: IngressesProp) => { {truncateMiddle(url)}
- {ingresses && (index < ingresses.length - 1) && -
- } ) })} diff --git a/dashboard/src/components/module.tsx b/dashboard/src/components/module.tsx index a0576b5ce9..fb10867d82 100644 --- a/dashboard/src/components/module.tsx +++ b/dashboard/src/components/module.tsx @@ -8,6 +8,7 @@ import React, { useState, useContext } from "react" import styled from "@emotion/styled" +import { css } from "emotion" import moment from "moment" import { ModuleModel } from "../containers/overview" import EntityCard from "./entity-card" @@ -18,10 +19,10 @@ import { TertiaryButton } from "./button" const Module = styled.div` padding: 1.2rem; background: white; - box-shadow: 0px 6px 18px rgba(0, 0, 0, 0.06); - border-radius: 4px; + box-shadow: 0rem 0.375rem 1.125rem rgba(0, 0, 0, 0.06); + border-radius: 0.25rem; margin: 0 1.3rem 1.3rem 0; - min-width: 17rem; + min-width: 17.5rem; flex: 1 1; max-width: 20rem; ` @@ -30,7 +31,7 @@ type EntityCardsProps = { visible: boolean, } const EntityCards = styled.div` - padding-top: .75rem; + padding-top: 1rem; display: flex; flex-wrap: wrap; align-items: middle; @@ -47,22 +48,12 @@ const EntityCards = styled.div` } ` -const Header = styled.div` - display: flex; - align-items: center; - align-self: flex-start; -` - type FieldsProps = { visible: boolean, } const Fields = styled.div` display: ${props => (props.visible ? `block` : "none")}; - animation: fadein .5s ; - - &:first-of-type{ - padding-top:0; - } + animation: fadein .5s; @keyframes fadein { from { opacity: 0; @@ -73,78 +64,81 @@ const Fields = styled.div` } ` -type FieldProps = { - inline?: boolean, -} -const Field = styled.div` - display: ${props => (props.inline ? "inline" : "block")}; - padding-bottom: .5rem; +const Header = styled.div` + line-height: 1rem; + display: flex; + align-items: baseline; + align-self: flex-start; + justify-content: space-between; +` - &:last-of-type{ - padding-bottom: 0; - } +const Name = styled.div` + font-weight: 500; + font-size: 0.9375rem; + letter-spacing: 0.01em; + color: #323C47; ` const Tag = styled.div` - display: inline-block; + padding-left: .5rem; font-weight: 500; - font-size: 10px; + font-size: 0.625rem; letter-spacing: 0.01em; color: #90A0B7; - padding-left: .25rem; ` -const Name = styled.div` - padding-right: .5rem; - font-weight: 500; - font-size: 15px; - letter-spacing: 0.01em; - color: #323C47; + +type FieldProps = { + inline?: boolean, + visible: boolean, +} +const Field = styled.div` + display: ${props => (props.visible ? (props.inline ? "flex" : "block") : "none")}; + flex-direction: row; +` + +type FieldGroupProps = { + visible: boolean, +} +const FieldGroup = styled.div` + display: ${props => (props.visible ? "flex" : "none")}; + flex-direction: row; + padding-top: .25rem; ` -const Key = styled.span` +const Key = styled.div` padding-right: .25rem; - font-size: 13px; - line-height: 19px; + font-size: 0.8125rem; + line-height: 1.1875rem; letter-spacing: 0.01em; color: #4C5862; opacity: 0.5; ` -const Value = styled.span` + +const Value = styled.div` padding-right: .5rem; - font-size: 13px; - line-height: 19px; + font-size: 0.8125rem; + line-height: 1.1875rem; letter-spacing: 0.01em; +` + +const Description = styled(Field)` color: #4C5862; + opacity: 0.5; + padding-top: .25rem; ` -const UrlFull = styled(Value)` - overflow-wrap: break-word; - word-wrap: break-word; - -ms-word-break: break-all; - word-break: break-all; - word-break: break-word; - -ms-hyphens: auto; - -moz-hyphens: auto; - -webkit-hyphens: auto; - hyphens: auto; +const Full = styled(Value)` cursor: pointer; ` -const Description = styled(Field)` - padding-top: 0.25rem; -` -const UrlShort = styled(Value)` - padding-right: .5rem; - font-size: 13px; - line-height: 19px; - letter-spacing: 0.01em; - color: #4C5862; - cursor: pointer; - display: -webkit-box; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; - overflow: hidden; +const Short = styled(Value)` + cursor: pointer; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; ` + interface ModuleProp { module: ModuleModel } @@ -156,9 +150,8 @@ export default ({ actions: { selectEntity }, } = useContext(UiStateContext) - const [showFullDescription, setDescriptionState] = useState(false) - const toggleDescriptionState = () => (setDescriptionState(!showFullDescription)) - + const [isValueExpended, setValueExpendedState] = useState(false) + const toggleValueExpendedState = () => (setValueExpendedState(!isValueExpended)) const handleSelectEntity = ( { moduleName, @@ -182,21 +175,20 @@ export default ({ return (
- {name} {type && type.toUpperCase()} MODULE + {name} + {type && type.toUpperCase()} MODULE
- {description && ( - - {!showFullDescription && ( - {description} - )} - {showFullDescription && ( - {description} - )} - - )} + + {!isValueExpended && ( + {description} + )} + {isValueExpended && ( + {description} + )} + - + 0}> {services.map(service => ( - {service.dependencies.length > 0 && ( - - Depends on: - {service.dependencies.join(", ")} - - )} - + 0}> + Depends on: + {service.dependencies.join(", ")} + + 0}> ))} - + 0}> {tests.map(test => ( - {test.dependencies.length > 0 && ( - - Depends on: - {test.dependencies.join(", ")} - - )} -
- + 0}> + Depends on: + {test.dependencies.join(", ")} + + + Ran: {moment(test.startedAt).fromNow()} - {test.state === "succeeded" && - - Took: - {test.duration} - - } -
+ + Took: + {test.duration} + +
))} - + 0}> {tasks.map(task => ( - {task.dependencies.length && ( - - Depends on: - {task.dependencies.join(", ")} - - )} -
- + 0}> + Depends on: + {task.dependencies.join(", ")} + + + Ran: {moment(task.startedAt).fromNow()} - {task.state === "succeeded" && - - Took: - {task.duration} - - } -
+ + Took: + {task.duration} + +
{ const handleClick = () => onClick({ entityName, moduleName, entityType }) return ( - + Show result ) diff --git a/dashboard/src/containers/overview.tsx b/dashboard/src/containers/overview.tsx index a0d13767e5..799ed16ae6 100644 --- a/dashboard/src/containers/overview.tsx +++ b/dashboard/src/containers/overview.tsx @@ -41,7 +41,8 @@ const Modules = styled.div` export type ModuleModel = { name: string; type: string; - path: string; + path?: string; + repositoryUrl?: string; description?: string; services: Service[]; tests: Test[]; @@ -109,7 +110,11 @@ export default () => { modules = config.data.moduleConfigs.map(moduleConfig => ({ name: moduleConfig.name, type: moduleConfig.type, - path: moduleConfig.path, + path: config.data && + config.data.projectRoot && + config.data.projectRoot.split("/").pop() + + moduleConfig.path.replace(config.data.projectRoot, ""), + repositoryUrl: moduleConfig.repositoryUrl, description: moduleConfig.description, services: moduleConfig.serviceConfigs.map(service => ({ name: service.name, diff --git a/garden-service/src/garden.ts b/garden-service/src/garden.ts index 3fe05368bf..bb67e1a904 100644 --- a/garden-service/src/garden.ts +++ b/garden-service/src/garden.ts @@ -704,6 +704,7 @@ export class Garden { providers: await this.resolveProviders(), variables: this.variables, moduleConfigs: sortBy(await this.resolveModuleConfigs(), "name"), + projectRoot: this.projectRoot, } } @@ -715,4 +716,5 @@ export interface ConfigDump { providers: Provider[] variables: PrimitiveMap moduleConfigs: ModuleConfig[] + projectRoot: string } diff --git a/garden-service/test/unit/src/commands/get/get-config.ts b/garden-service/test/unit/src/commands/get/get-config.ts index e18bf986e4..d98f403566 100644 --- a/garden-service/test/unit/src/commands/get/get-config.ts +++ b/garden-service/test/unit/src/commands/get/get-config.ts @@ -28,6 +28,7 @@ describe("GetConfigCommand", () => { providers, variables: garden.variables, moduleConfigs: sortBy(await garden.resolveModuleConfigs(), "name"), + projectRoot: garden.projectRoot, } expect(config).to.deep.equal(res.result)