refactor: html output custom style
parent
a14a72f212
commit
a27d993b13
|
@ -4,16 +4,28 @@
|
|||
<title>Driftctl scan report</title>
|
||||
<meta charset="UTF-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<link rel="shortcut icon"
|
||||
href="https://raw.githubusercontent.com/cloudskiff/driftctl-docs/main/static/img/favicon.ico"/>
|
||||
<style>{{.Stylesheet}}</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="heading d-flex justify-space-between align-center">
|
||||
<div>
|
||||
<h1 class="heading-title">Driftctl scan report</h1>
|
||||
<h1 class="heading-title mb-1">Driftctl scan report</h1>
|
||||
<span class="heading-subtitle">Coverage {{.Coverage}}%</span>
|
||||
</div>
|
||||
<h2 class="heading-date">{{ .ScanDate }}</h2>
|
||||
<div class="text--right">
|
||||
<h2 class="heading-date mb-1">{{ .ScanDate }}</h2>
|
||||
<span class="heading-subtitle">Scan duration {{.ScanDuration}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card d-flex justify-space-around mb-2 text--grey">
|
||||
<span>Total resources: {{.Summary.TotalResources}}</span>
|
||||
<span>Managed: {{rate .Summary.TotalManaged}}%</span>
|
||||
<span>Changed: {{.Summary.TotalDrifted}}/{{.Summary.TotalManaged}}</span>
|
||||
<span>Unmanaged: {{rate .Summary.TotalUnmanaged}}%</span>
|
||||
<span>Missing: {{rate .Summary.TotalDeleted}}%</span>
|
||||
</div>
|
||||
<div class="app-content">
|
||||
{{ if (lt .Coverage 100) }}
|
||||
|
@ -56,16 +68,24 @@
|
|||
<div class="panels">
|
||||
{{ if (gt (len .Unmanaged) 0) }}
|
||||
<div class="panel" id="one-panel">
|
||||
<div class="d-flex justify-space-between text--grey pa-1">
|
||||
<span>Resource id</span>
|
||||
<span>Resource type</span>
|
||||
</div>
|
||||
{{range $res := .Unmanaged}}
|
||||
<div class="resource-item resource-item-unmanaged d-flex justify-space-between">
|
||||
<span class="resource-item-id">{{$res.TerraformId}}</span>
|
||||
<span class="resource-item-type text--bold">{{$res.TerraformType}}</span>
|
||||
<span class="resource-item-type">{{$res.TerraformType}}</span>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
{{ if (gt (len .Differences) 0) }}
|
||||
<div class="panel" id="two-panel">
|
||||
<div class="d-flex justify-space-between text--grey pa-1">
|
||||
<span>Resource id</span>
|
||||
<span>Resource type</span>
|
||||
</div>
|
||||
{{range $diff := .Differences}}
|
||||
<div class="resource-item resource-item-changed d-flex justify-space-between">
|
||||
<span class="resource-item-id">{{$diff.Res.TerraformId}}</span>
|
||||
|
@ -74,17 +94,21 @@
|
|||
<div>{{ formatChange $change }}</div>
|
||||
{{end}}
|
||||
</div>
|
||||
<span class="resource-item-type text--bold">{{$diff.Res.TerraformType}}</span>
|
||||
<span class="resource-item-type">{{$diff.Res.TerraformType}}</span>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
{{ if (gt (len .Deleted) 0) }}
|
||||
<div class="panel" id="three-panel">
|
||||
<div class="d-flex justify-space-between text--grey pa-1">
|
||||
<span>Resource id</span>
|
||||
<span>Resource type</span>
|
||||
</div>
|
||||
{{range $res := .Deleted}}
|
||||
<div class="resource-item resource-item-deleted d-flex justify-space-between">
|
||||
<span class="resource-item-id">{{$res.TerraformId}}</span>
|
||||
<span class="resource-item-type text--bold">{{$res.TerraformType}}</span>
|
||||
<span class="resource-item-type">{{$res.TerraformType}}</span>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
|
@ -94,8 +118,8 @@
|
|||
{{range $type, $messages := .Alerts}}
|
||||
{{range $el := $messages}}
|
||||
<div class="resource-item resource-item-alerts">
|
||||
<span class="resource-item-type text--bold">{{ $type }}</span>
|
||||
<span>- {{ $el.Message }}</span>
|
||||
<span class="resource-item-type">{{ $type }}</span>
|
||||
<span>{{ $el.Message }}</span>
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
@import url('https://fonts.googleapis.com/css2?family=Source+Sans+Pro:wght@300;400;600&display=swap');
|
||||
|
||||
/* http://meyerweb.com/eric/tools/css/reset/
|
||||
v2.0 | 20110126
|
||||
License: none (public domain)
|
||||
|
@ -20,7 +18,6 @@ time, mark, audio, video {
|
|||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
font-size: 100%;
|
||||
font: inherit;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
@ -45,7 +42,6 @@ blockquote, q {
|
|||
|
||||
blockquote:before, blockquote:after,
|
||||
q:before, q:after {
|
||||
content: '';
|
||||
content: none;
|
||||
}
|
||||
|
||||
|
@ -62,7 +58,7 @@ table {
|
|||
}
|
||||
|
||||
body {
|
||||
font-family: 'Source Sans Pro', sans-serif;
|
||||
font-family: "Helvetica", sans-serif;
|
||||
color: #1C1E21;
|
||||
background-color: #F7F7F9;
|
||||
padding-bottom: 50px;
|
||||
|
@ -127,6 +123,26 @@ div.container {
|
|||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.pa-1 {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.pl-1 {
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
.pr-1 {
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
.pt-1 {
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
.pb-1 {
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
input[type="text"],
|
||||
select {
|
||||
padding: 8px;
|
||||
|
@ -144,6 +160,7 @@ select:focus {
|
|||
|
||||
input[name="resource-id-filter"], select[name="resource-type-filter"] {
|
||||
width: 300px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
|
@ -157,21 +174,32 @@ h2 {
|
|||
}
|
||||
|
||||
.heading-title {
|
||||
margin-bottom: 10px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.heading-subtitle {
|
||||
font-size: 14px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.app-content {
|
||||
padding: 25px;
|
||||
border-top: 3px solid #71B2C3;
|
||||
background-color: #ffffff;
|
||||
border-radius: 0 0 10px 10px;
|
||||
box-shadow: 0 0 5px #0000000a;
|
||||
}
|
||||
|
||||
.resource-item {
|
||||
border: 1px solid #ececec;
|
||||
padding: 10px;
|
||||
margin-top: 10px;
|
||||
border-top: 1px solid #ececec;
|
||||
border-left: 1px solid #ececec;
|
||||
border-right: 1px solid #ececec;
|
||||
padding: 15px;
|
||||
color: #6e7071;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.resource-item:last-child {
|
||||
border-bottom: 1px solid #ececec;
|
||||
}
|
||||
|
||||
.resource-item:hover {
|
||||
|
@ -182,6 +210,22 @@ h2 {
|
|||
font-weight: bold;
|
||||
}
|
||||
|
||||
.text--grey {
|
||||
color: #747578;
|
||||
}
|
||||
|
||||
.text--right {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.card {
|
||||
padding: 15px;
|
||||
font-size: 15px;
|
||||
background: #ffffff;
|
||||
box-shadow: 0 0 5px #0000000a;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.reset-filter-btn {
|
||||
border: none;
|
||||
padding: 8px;
|
||||
|
@ -206,6 +250,7 @@ h2 {
|
|||
display: inline-block;
|
||||
color: #747578;
|
||||
transition: background-color 200ms;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.tab:hover {
|
||||
|
@ -251,9 +296,8 @@ h2 {
|
|||
#two:checked ~ .tabs #two-tab,
|
||||
#three:checked ~ .tabs #three-tab,
|
||||
#four:checked ~ .tabs #four-tab {
|
||||
background: transparent;
|
||||
color: #747578;
|
||||
border-bottom: 2px solid #71b2c3;
|
||||
background: #71b2c3;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.empty-message-container {
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"embed"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"math"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
@ -26,14 +27,15 @@ type HTML struct {
|
|||
}
|
||||
|
||||
type HTMLTemplateParams struct {
|
||||
ScanDate string
|
||||
Coverage int
|
||||
Summary analyser.Summary
|
||||
Unmanaged []resource.Resource
|
||||
Differences []analyser.Difference
|
||||
Deleted []resource.Resource
|
||||
Alerts alerter.Alerts
|
||||
Stylesheet template.CSS
|
||||
ScanDate string
|
||||
Coverage int
|
||||
Summary analyser.Summary
|
||||
Unmanaged []resource.Resource
|
||||
Differences []analyser.Difference
|
||||
Deleted []resource.Resource
|
||||
Alerts alerter.Alerts
|
||||
Stylesheet template.CSS
|
||||
ScanDuration string
|
||||
}
|
||||
|
||||
func NewHTML(path string) *HTML {
|
||||
|
@ -92,6 +94,12 @@ func (c *HTML) Write(analysis *analyser.Analysis) error {
|
|||
|
||||
return fmt.Sprintf("%s %s: %s => %s %s", prefix, strings.Join(ch.Path, "."), prettify(ch.From), prettify(ch.To), suffix)
|
||||
},
|
||||
"rate": func(count int) float64 {
|
||||
if analysis.Summary().TotalResources == 0 {
|
||||
return 0
|
||||
}
|
||||
return math.Round(100 * float64(count) / float64(analysis.Summary().TotalResources))
|
||||
},
|
||||
}
|
||||
|
||||
tmpl, err := template.New("main").Funcs(funcMap).Parse(string(tmplFile))
|
||||
|
@ -100,14 +108,15 @@ func (c *HTML) Write(analysis *analyser.Analysis) error {
|
|||
}
|
||||
|
||||
data := &HTMLTemplateParams{
|
||||
ScanDate: time.Now().Format("Jan 02, 2006"),
|
||||
Summary: analysis.Summary(),
|
||||
Coverage: analysis.Coverage(),
|
||||
Unmanaged: analysis.Unmanaged(),
|
||||
Differences: analysis.Differences(),
|
||||
Deleted: analysis.Deleted(),
|
||||
Alerts: analysis.Alerts(),
|
||||
Stylesheet: template.CSS(styleFile),
|
||||
ScanDate: time.Now().Format("Jan 02, 2006"),
|
||||
Summary: analysis.Summary(),
|
||||
Coverage: analysis.Coverage(),
|
||||
Unmanaged: analysis.Unmanaged(),
|
||||
Differences: analysis.Differences(),
|
||||
Deleted: analysis.Deleted(),
|
||||
Alerts: analysis.Alerts(),
|
||||
Stylesheet: template.CSS(styleFile),
|
||||
ScanDuration: analysis.Duration.Round(time.Second).String(),
|
||||
}
|
||||
|
||||
err = tmpl.Execute(file, data)
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"io/ioutil"
|
||||
"path"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/cloudskiff/driftctl/pkg/resource"
|
||||
testresource "github.com/cloudskiff/driftctl/test/resource"
|
||||
|
@ -21,12 +22,38 @@ func TestHTML_Write(t *testing.T) {
|
|||
analysis func() *analyser.Analysis
|
||||
err error
|
||||
}{
|
||||
{
|
||||
name: "test html output when there's no resources",
|
||||
goldenfile: "output_empty.html",
|
||||
analysis: func() *analyser.Analysis {
|
||||
a := &analyser.Analysis{}
|
||||
return a
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
name: "test html output when infrastructure is in sync",
|
||||
goldenfile: "output_sync.html",
|
||||
analysis: func() *analyser.Analysis {
|
||||
a := &analyser.Analysis{}
|
||||
a.Duration = 72 * time.Second
|
||||
a.AddManaged(
|
||||
&testresource.FakeResource{
|
||||
Id: "deleted-id-3",
|
||||
Type: "aws_deleted_resource",
|
||||
},
|
||||
)
|
||||
return a
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
name: "test html output",
|
||||
goldenfile: "output.html",
|
||||
|
||||
analysis: func() *analyser.Analysis {
|
||||
a := fakeAnalysisWithAlerts()
|
||||
a.Duration = 91 * time.Second
|
||||
a.AddDeleted(
|
||||
&testresource.FakeResource{
|
||||
Id: "deleted-id-3",
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
<title>Driftctl scan report</title>
|
||||
<meta charset="UTF-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<style>@import url('https://fonts.googleapis.com/css2?family=Source+Sans+Pro:wght@300;400;600&display=swap');
|
||||
|
||||
/* http://meyerweb.com/eric/tools/css/reset/
|
||||
<link rel="shortcut icon"
|
||||
href="https://raw.githubusercontent.com/cloudskiff/driftctl-docs/main/static/img/favicon.ico"/>
|
||||
<style>/* http://meyerweb.com/eric/tools/css/reset/
|
||||
v2.0 | 20110126
|
||||
License: none (public domain)
|
||||
*/
|
||||
|
@ -26,7 +26,6 @@ time, mark, audio, video {
|
|||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
font-size: 100%;
|
||||
font: inherit;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
@ -51,7 +50,6 @@ blockquote, q {
|
|||
|
||||
blockquote:before, blockquote:after,
|
||||
q:before, q:after {
|
||||
content: '';
|
||||
content: none;
|
||||
}
|
||||
|
||||
|
@ -68,7 +66,7 @@ table {
|
|||
}
|
||||
|
||||
body {
|
||||
font-family: 'Source Sans Pro', sans-serif;
|
||||
font-family: "Helvetica", sans-serif;
|
||||
color: #1C1E21;
|
||||
background-color: #F7F7F9;
|
||||
padding-bottom: 50px;
|
||||
|
@ -133,6 +131,26 @@ div.container {
|
|||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.pa-1 {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.pl-1 {
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
.pr-1 {
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
.pt-1 {
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
.pb-1 {
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
input[type="text"],
|
||||
select {
|
||||
padding: 8px;
|
||||
|
@ -150,6 +168,7 @@ select:focus {
|
|||
|
||||
input[name="resource-id-filter"], select[name="resource-type-filter"] {
|
||||
width: 300px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
|
@ -163,21 +182,32 @@ h2 {
|
|||
}
|
||||
|
||||
.heading-title {
|
||||
margin-bottom: 10px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.heading-subtitle {
|
||||
font-size: 14px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.app-content {
|
||||
padding: 25px;
|
||||
border-top: 3px solid #71B2C3;
|
||||
background-color: #ffffff;
|
||||
border-radius: 0 0 10px 10px;
|
||||
box-shadow: 0 0 5px #0000000a;
|
||||
}
|
||||
|
||||
.resource-item {
|
||||
border: 1px solid #ececec;
|
||||
padding: 10px;
|
||||
margin-top: 10px;
|
||||
border-top: 1px solid #ececec;
|
||||
border-left: 1px solid #ececec;
|
||||
border-right: 1px solid #ececec;
|
||||
padding: 15px;
|
||||
color: #6e7071;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.resource-item:last-child {
|
||||
border-bottom: 1px solid #ececec;
|
||||
}
|
||||
|
||||
.resource-item:hover {
|
||||
|
@ -188,6 +218,22 @@ h2 {
|
|||
font-weight: bold;
|
||||
}
|
||||
|
||||
.text--grey {
|
||||
color: #747578;
|
||||
}
|
||||
|
||||
.text--right {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.card {
|
||||
padding: 15px;
|
||||
font-size: 15px;
|
||||
background: #ffffff;
|
||||
box-shadow: 0 0 5px #0000000a;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.reset-filter-btn {
|
||||
border: none;
|
||||
padding: 8px;
|
||||
|
@ -212,6 +258,7 @@ h2 {
|
|||
display: inline-block;
|
||||
color: #747578;
|
||||
transition: background-color 200ms;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.tab:hover {
|
||||
|
@ -257,9 +304,8 @@ h2 {
|
|||
#two:checked ~ .tabs #two-tab,
|
||||
#three:checked ~ .tabs #three-tab,
|
||||
#four:checked ~ .tabs #four-tab {
|
||||
background: transparent;
|
||||
color: #747578;
|
||||
border-bottom: 2px solid #71b2c3;
|
||||
background: #71b2c3;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.empty-message-container {
|
||||
|
@ -270,10 +316,20 @@ h2 {
|
|||
<div class="container">
|
||||
<div class="heading d-flex justify-space-between align-center">
|
||||
<div>
|
||||
<h1 class="heading-title">Driftctl scan report</h1>
|
||||
<h1 class="heading-title mb-1">Driftctl scan report</h1>
|
||||
<span class="heading-subtitle">Coverage 15%</span>
|
||||
</div>
|
||||
<h2 class="heading-date">Jun 07, 2021</h2>
|
||||
<div class="text--right">
|
||||
<h2 class="heading-date mb-1">Jun 08, 2021</h2>
|
||||
<span class="heading-subtitle">Scan duration 1m31s</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card d-flex justify-space-around mb-2 text--grey">
|
||||
<span>Total resources: 13</span>
|
||||
<span>Managed: 15%</span>
|
||||
<span>Changed: 2/2</span>
|
||||
<span>Unmanaged: 38%</span>
|
||||
<span>Missing: 46%</span>
|
||||
</div>
|
||||
<div class="app-content">
|
||||
|
||||
|
@ -320,36 +376,44 @@ h2 {
|
|||
<div class="panels">
|
||||
|
||||
<div class="panel" id="one-panel">
|
||||
<div class="d-flex justify-space-between text--grey pa-1">
|
||||
<span>Resource id</span>
|
||||
<span>Resource type</span>
|
||||
</div>
|
||||
|
||||
<div class="resource-item resource-item-unmanaged d-flex justify-space-between">
|
||||
<span class="resource-item-id">unmanaged-id-1</span>
|
||||
<span class="resource-item-type text--bold">aws_unmanaged_resource</span>
|
||||
<span class="resource-item-type">aws_unmanaged_resource</span>
|
||||
</div>
|
||||
|
||||
<div class="resource-item resource-item-unmanaged d-flex justify-space-between">
|
||||
<span class="resource-item-id">unmanaged-id-2</span>
|
||||
<span class="resource-item-type text--bold">aws_unmanaged_resource</span>
|
||||
<span class="resource-item-type">aws_unmanaged_resource</span>
|
||||
</div>
|
||||
|
||||
<div class="resource-item resource-item-unmanaged d-flex justify-space-between">
|
||||
<span class="resource-item-id">unmanaged-id-3</span>
|
||||
<span class="resource-item-type text--bold">aws_unmanaged_resource</span>
|
||||
<span class="resource-item-type">aws_unmanaged_resource</span>
|
||||
</div>
|
||||
|
||||
<div class="resource-item resource-item-unmanaged d-flex justify-space-between">
|
||||
<span class="resource-item-id">unmanaged-id-4</span>
|
||||
<span class="resource-item-type text--bold">aws_unmanaged_resource</span>
|
||||
<span class="resource-item-type">aws_unmanaged_resource</span>
|
||||
</div>
|
||||
|
||||
<div class="resource-item resource-item-unmanaged d-flex justify-space-between">
|
||||
<span class="resource-item-id">unmanaged-id-5</span>
|
||||
<span class="resource-item-type text--bold">aws_unmanaged_resource</span>
|
||||
<span class="resource-item-type">aws_unmanaged_resource</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div class="panel" id="two-panel">
|
||||
<div class="d-flex justify-space-between text--grey pa-1">
|
||||
<span>Resource id</span>
|
||||
<span>Resource type</span>
|
||||
</div>
|
||||
|
||||
<div class="resource-item resource-item-changed d-flex justify-space-between">
|
||||
<span class="resource-item-id">diff-id-1</span>
|
||||
|
@ -362,7 +426,7 @@ h2 {
|
|||
<div>- a: "oldValue" => <nil> </div>
|
||||
|
||||
</div>
|
||||
<span class="resource-item-type text--bold">aws_diff_resource</span>
|
||||
<span class="resource-item-type">aws_diff_resource</span>
|
||||
</div>
|
||||
|
||||
<div class="resource-item resource-item-changed d-flex justify-space-between">
|
||||
|
@ -372,42 +436,46 @@ h2 {
|
|||
<div>- path.to.field: <nil> => ["value"] </div>
|
||||
|
||||
</div>
|
||||
<span class="resource-item-type text--bold">aws_diff_resource</span>
|
||||
<span class="resource-item-type">aws_diff_resource</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div class="panel" id="three-panel">
|
||||
<div class="d-flex justify-space-between text--grey pa-1">
|
||||
<span>Resource id</span>
|
||||
<span>Resource type</span>
|
||||
</div>
|
||||
|
||||
<div class="resource-item resource-item-deleted d-flex justify-space-between">
|
||||
<span class="resource-item-id">deleted-id-1</span>
|
||||
<span class="resource-item-type text--bold">aws_deleted_resource</span>
|
||||
<span class="resource-item-type">aws_deleted_resource</span>
|
||||
</div>
|
||||
|
||||
<div class="resource-item resource-item-deleted d-flex justify-space-between">
|
||||
<span class="resource-item-id">deleted-id-2</span>
|
||||
<span class="resource-item-type text--bold">aws_deleted_resource</span>
|
||||
<span class="resource-item-type">aws_deleted_resource</span>
|
||||
</div>
|
||||
|
||||
<div class="resource-item resource-item-deleted d-flex justify-space-between">
|
||||
<span class="resource-item-id">deleted-id-3</span>
|
||||
<span class="resource-item-type text--bold">aws_deleted_resource</span>
|
||||
<span class="resource-item-type">aws_deleted_resource</span>
|
||||
</div>
|
||||
|
||||
<div class="resource-item resource-item-deleted d-flex justify-space-between">
|
||||
<span class="resource-item-id">deleted-id-4</span>
|
||||
<span class="resource-item-type text--bold">aws_deleted_resource</span>
|
||||
<span class="resource-item-type">aws_deleted_resource</span>
|
||||
</div>
|
||||
|
||||
<div class="resource-item resource-item-deleted d-flex justify-space-between">
|
||||
<span class="resource-item-id">deleted-id-5</span>
|
||||
<span class="resource-item-type text--bold">aws_deleted_resource</span>
|
||||
<span class="resource-item-type">aws_deleted_resource</span>
|
||||
</div>
|
||||
|
||||
<div class="resource-item resource-item-deleted d-flex justify-space-between">
|
||||
<span class="resource-item-id">deleted-id-6</span>
|
||||
<span class="resource-item-type text--bold">aws_deleted_resource</span>
|
||||
<span class="resource-item-type">aws_deleted_resource</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
@ -417,18 +485,18 @@ h2 {
|
|||
|
||||
|
||||
<div class="resource-item resource-item-alerts">
|
||||
<span class="resource-item-type text--bold"></span>
|
||||
<span>- Ignoring aws_vpc from drift calculation: Listing aws_vpc is forbidden.</span>
|
||||
<span class="resource-item-type"></span>
|
||||
<span>Ignoring aws_vpc from drift calculation: Listing aws_vpc is forbidden.</span>
|
||||
</div>
|
||||
|
||||
<div class="resource-item resource-item-alerts">
|
||||
<span class="resource-item-type text--bold"></span>
|
||||
<span>- Ignoring aws_sqs from drift calculation: Listing aws_sqs is forbidden.</span>
|
||||
<span class="resource-item-type"></span>
|
||||
<span>Ignoring aws_sqs from drift calculation: Listing aws_sqs is forbidden.</span>
|
||||
</div>
|
||||
|
||||
<div class="resource-item resource-item-alerts">
|
||||
<span class="resource-item-type text--bold"></span>
|
||||
<span>- Ignoring aws_sns from drift calculation: Listing aws_sns is forbidden.</span>
|
||||
<span class="resource-item-type"></span>
|
||||
<span>Ignoring aws_sns from drift calculation: Listing aws_sns is forbidden.</span>
|
||||
</div>
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,458 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Driftctl scan report</title>
|
||||
<meta charset="UTF-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<link rel="shortcut icon"
|
||||
href="https://raw.githubusercontent.com/cloudskiff/driftctl-docs/main/static/img/favicon.ico"/>
|
||||
<style>/* http://meyerweb.com/eric/tools/css/reset/
|
||||
v2.0 | 20110126
|
||||
License: none (public domain)
|
||||
*/
|
||||
html, body, div, span, applet, object, iframe,
|
||||
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
|
||||
a, abbr, acronym, address, big, cite, code,
|
||||
del, dfn, em, img, ins, kbd, q, s, samp,
|
||||
small, strike, strong, sub, sup, tt, var,
|
||||
b, u, i, center,
|
||||
dl, dt, dd, ol, ul, li,
|
||||
fieldset, form, label, legend,
|
||||
table, caption, tbody, tfoot, thead, tr, th, td,
|
||||
article, aside, canvas, details, embed,
|
||||
figure, figcaption, footer, header, hgroup,
|
||||
menu, nav, output, ruby, section, summary,
|
||||
time, mark, audio, video {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
font: inherit;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
/* HTML5 display-role reset for older browsers */
|
||||
article, aside, details, figcaption, figure,
|
||||
footer, header, hgroup, menu, nav, section {
|
||||
display: block;
|
||||
}
|
||||
|
||||
body {
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
ol, ul {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
blockquote, q {
|
||||
quotes: none;
|
||||
}
|
||||
|
||||
blockquote:before, blockquote:after,
|
||||
q:before, q:after {
|
||||
content: none;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Custom style
|
||||
*/
|
||||
:root {
|
||||
--transitionDuration: 400ms;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: "Helvetica", sans-serif;
|
||||
color: #1C1E21;
|
||||
background-color: #F7F7F9;
|
||||
padding-bottom: 50px;
|
||||
}
|
||||
|
||||
div.container {
|
||||
max-width: 100%;
|
||||
width: 1280px;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.heading {
|
||||
height: 130px;
|
||||
}
|
||||
|
||||
.hide {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.d-flex {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.justify-center {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.justify-space-around {
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
.justify-space-between {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.align-center {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.mt-5 {
|
||||
margin-top: 50px;
|
||||
}
|
||||
|
||||
.mb-5 {
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
|
||||
.mt-2 {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.mb-2 {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.mt-1 {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.mb-1 {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.pa-1 {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.pl-1 {
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
.pr-1 {
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
.pt-1 {
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
.pb-1 {
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
input[type="text"],
|
||||
select {
|
||||
padding: 8px;
|
||||
font-size: 14px;
|
||||
border: 1px solid #ececec;
|
||||
outline: none;
|
||||
transition: var(--transitionDuration);
|
||||
color: #6e7071;
|
||||
}
|
||||
|
||||
input[type="text"]:focus,
|
||||
select:focus {
|
||||
border: 1px solid #71b2c3;
|
||||
}
|
||||
|
||||
input[name="resource-id-filter"], select[name="resource-type-filter"] {
|
||||
width: 300px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.heading-title {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.heading-subtitle {
|
||||
font-size: 14px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.app-content {
|
||||
padding: 25px;
|
||||
border-top: 3px solid #71B2C3;
|
||||
background-color: #ffffff;
|
||||
box-shadow: 0 0 5px #0000000a;
|
||||
}
|
||||
|
||||
.resource-item {
|
||||
border-top: 1px solid #ececec;
|
||||
border-left: 1px solid #ececec;
|
||||
border-right: 1px solid #ececec;
|
||||
padding: 15px;
|
||||
color: #6e7071;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.resource-item:last-child {
|
||||
border-bottom: 1px solid #ececec;
|
||||
}
|
||||
|
||||
.resource-item:hover {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
|
||||
.text--bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.text--grey {
|
||||
color: #747578;
|
||||
}
|
||||
|
||||
.text--right {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.card {
|
||||
padding: 15px;
|
||||
font-size: 15px;
|
||||
background: #ffffff;
|
||||
box-shadow: 0 0 5px #0000000a;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.reset-filter-btn {
|
||||
border: none;
|
||||
padding: 8px;
|
||||
font-size: 14px;
|
||||
background-color: transparent;
|
||||
color: #5faabd;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* tabs style */
|
||||
.tabs-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.tab {
|
||||
cursor: pointer;
|
||||
padding: 10px 20px;
|
||||
margin: 0 2px;
|
||||
background: transparent;
|
||||
display: inline-block;
|
||||
color: #747578;
|
||||
transition: background-color 200ms;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.tab:hover {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
|
||||
.panels {
|
||||
width: 100%;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.panel {
|
||||
display: none;
|
||||
animation: fadein .8s;
|
||||
}
|
||||
|
||||
@keyframes fadein {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.panel-title {
|
||||
font-size: 1.5em;
|
||||
font-weight: bold
|
||||
}
|
||||
|
||||
.radio {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#one:checked ~ .panels #one-panel,
|
||||
#two:checked ~ .panels #two-panel,
|
||||
#three:checked ~ .panels #three-panel,
|
||||
#four:checked ~ .panels #four-panel {
|
||||
display: block
|
||||
}
|
||||
|
||||
#one:checked ~ .tabs #one-tab,
|
||||
#two:checked ~ .tabs #two-tab,
|
||||
#three:checked ~ .tabs #three-tab,
|
||||
#four:checked ~ .tabs #four-tab {
|
||||
background: #71b2c3;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.empty-message-container {
|
||||
color: #747578;
|
||||
}</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="heading d-flex justify-space-between align-center">
|
||||
<div>
|
||||
<h1 class="heading-title mb-1">Driftctl scan report</h1>
|
||||
<span class="heading-subtitle">Coverage 0%</span>
|
||||
</div>
|
||||
<div class="text--right">
|
||||
<h2 class="heading-date mb-1">Jun 08, 2021</h2>
|
||||
<span class="heading-subtitle">Scan duration 0s</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card d-flex justify-space-around mb-2 text--grey">
|
||||
<span>Total resources: 0</span>
|
||||
<span>Managed: 0%</span>
|
||||
<span>Changed: 0/0</span>
|
||||
<span>Unmanaged: 0%</span>
|
||||
<span>Missing: 0%</span>
|
||||
</div>
|
||||
<div class="app-content">
|
||||
|
||||
<div class="d-flex justify-center mb-2">
|
||||
<form id="filter-form" action="#">
|
||||
<input type="text" name="resource-id-filter" placeholder="Search resources..." onkeyup="refreshState()">
|
||||
<select name="resource-type-filter" onchange="refreshState()">
|
||||
<option value="">Resource type</option>
|
||||
|
||||
</select>
|
||||
<button type="button" onclick="resetFilters()" class="reset-filter-btn">Reset filters</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="tabs-wrapper">
|
||||
<input class="radio" id="one" name="group" type="radio" checked>
|
||||
<input class="radio" id="two" name="group" type="radio">
|
||||
<input class="radio" id="three" name="group" type="radio">
|
||||
<input class="radio" id="four" name="group" type="radio">
|
||||
<div class="tabs">
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
<div class="panels">
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="empty-message-container mt-5 d-flex justify-center hide">
|
||||
<p>There's nothing to see there...</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
<script lang="js">
|
||||
const resources = document.querySelectorAll('.resource-item')
|
||||
|
||||
function hideResource(res) {
|
||||
res.classList.add('hide')
|
||||
}
|
||||
|
||||
function displayResource(res) {
|
||||
res.classList.remove('hide')
|
||||
}
|
||||
|
||||
function resourceIdContains(res, query) {
|
||||
const el = res.querySelector('.resource-item-id')
|
||||
if (!el) {
|
||||
return false
|
||||
}
|
||||
return el.innerText.toLowerCase().includes(query.toLowerCase())
|
||||
}
|
||||
|
||||
function resourceTypeEqual(res, type) {
|
||||
const el = res.querySelector('.resource-item-type')
|
||||
if (!el) {
|
||||
return false
|
||||
}
|
||||
return el.innerText === type
|
||||
}
|
||||
|
||||
function refreshCounters() {
|
||||
const counterClassMapping = {
|
||||
'.resource-item-unmanaged': '.resource-count-unmanaged',
|
||||
'.resource-item-changed': '.resource-count-changed',
|
||||
'.resource-item-deleted': '.resource-count-deleted',
|
||||
'.resource-item-alerts': '.resource-count-alerts',
|
||||
}
|
||||
|
||||
for (const resClass in counterClassMapping) {
|
||||
const countEl = document.querySelector(counterClassMapping[resClass])
|
||||
if (!countEl) {
|
||||
continue
|
||||
}
|
||||
countEl.textContent = Array.from(document.querySelectorAll(resClass)).filter(el => !el.classList.contains('hide')).length
|
||||
}
|
||||
}
|
||||
|
||||
function computeEmptyMessage() {
|
||||
const msgEl = document.querySelector('.empty-message-container')
|
||||
const wrapperEl = document.querySelector('.tabs-wrapper')
|
||||
const count = Array.from(resources).filter(el => !el.classList.contains('hide')).length
|
||||
if (count === 0) {
|
||||
msgEl.classList.remove('hide')
|
||||
wrapperEl.classList.add('hide')
|
||||
return
|
||||
}
|
||||
msgEl.classList.add('hide')
|
||||
wrapperEl.classList.remove('hide')
|
||||
}
|
||||
|
||||
function refreshState() {
|
||||
const queryFilterInput = document.querySelector('input[name=resource-id-filter]').value
|
||||
const typeFilterInput = document.querySelector('select[name=resource-type-filter]').value
|
||||
|
||||
for (const res of resources) {
|
||||
const matchId = !queryFilterInput.length || resourceIdContains(res, queryFilterInput)
|
||||
const matchType = !typeFilterInput.length || resourceTypeEqual(res, typeFilterInput)
|
||||
|
||||
if (matchId && matchType) {
|
||||
displayResource(res)
|
||||
continue
|
||||
}
|
||||
|
||||
hideResource(res)
|
||||
}
|
||||
|
||||
refreshCounters()
|
||||
computeEmptyMessage()
|
||||
}
|
||||
|
||||
function resetFilters() {
|
||||
document.querySelector('input[name=resource-id-filter]').value = ""
|
||||
document.querySelector('select[name=resource-type-filter]').value = ""
|
||||
refreshState()
|
||||
}
|
||||
|
||||
refreshState()
|
||||
</script>
|
||||
</html>
|
|
@ -0,0 +1,428 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Driftctl scan report</title>
|
||||
<meta charset="UTF-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<link rel="shortcut icon"
|
||||
href="https://raw.githubusercontent.com/cloudskiff/driftctl-docs/main/static/img/favicon.ico"/>
|
||||
<style>/* http://meyerweb.com/eric/tools/css/reset/
|
||||
v2.0 | 20110126
|
||||
License: none (public domain)
|
||||
*/
|
||||
html, body, div, span, applet, object, iframe,
|
||||
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
|
||||
a, abbr, acronym, address, big, cite, code,
|
||||
del, dfn, em, img, ins, kbd, q, s, samp,
|
||||
small, strike, strong, sub, sup, tt, var,
|
||||
b, u, i, center,
|
||||
dl, dt, dd, ol, ul, li,
|
||||
fieldset, form, label, legend,
|
||||
table, caption, tbody, tfoot, thead, tr, th, td,
|
||||
article, aside, canvas, details, embed,
|
||||
figure, figcaption, footer, header, hgroup,
|
||||
menu, nav, output, ruby, section, summary,
|
||||
time, mark, audio, video {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
font: inherit;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
/* HTML5 display-role reset for older browsers */
|
||||
article, aside, details, figcaption, figure,
|
||||
footer, header, hgroup, menu, nav, section {
|
||||
display: block;
|
||||
}
|
||||
|
||||
body {
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
ol, ul {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
blockquote, q {
|
||||
quotes: none;
|
||||
}
|
||||
|
||||
blockquote:before, blockquote:after,
|
||||
q:before, q:after {
|
||||
content: none;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Custom style
|
||||
*/
|
||||
:root {
|
||||
--transitionDuration: 400ms;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: "Helvetica", sans-serif;
|
||||
color: #1C1E21;
|
||||
background-color: #F7F7F9;
|
||||
padding-bottom: 50px;
|
||||
}
|
||||
|
||||
div.container {
|
||||
max-width: 100%;
|
||||
width: 1280px;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.heading {
|
||||
height: 130px;
|
||||
}
|
||||
|
||||
.hide {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.d-flex {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.justify-center {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.justify-space-around {
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
.justify-space-between {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.align-center {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.mt-5 {
|
||||
margin-top: 50px;
|
||||
}
|
||||
|
||||
.mb-5 {
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
|
||||
.mt-2 {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.mb-2 {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.mt-1 {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.mb-1 {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.pa-1 {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.pl-1 {
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
.pr-1 {
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
.pt-1 {
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
.pb-1 {
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
input[type="text"],
|
||||
select {
|
||||
padding: 8px;
|
||||
font-size: 14px;
|
||||
border: 1px solid #ececec;
|
||||
outline: none;
|
||||
transition: var(--transitionDuration);
|
||||
color: #6e7071;
|
||||
}
|
||||
|
||||
input[type="text"]:focus,
|
||||
select:focus {
|
||||
border: 1px solid #71b2c3;
|
||||
}
|
||||
|
||||
input[name="resource-id-filter"], select[name="resource-type-filter"] {
|
||||
width: 300px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.heading-title {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.heading-subtitle {
|
||||
font-size: 14px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.app-content {
|
||||
padding: 25px;
|
||||
border-top: 3px solid #71B2C3;
|
||||
background-color: #ffffff;
|
||||
box-shadow: 0 0 5px #0000000a;
|
||||
}
|
||||
|
||||
.resource-item {
|
||||
border-top: 1px solid #ececec;
|
||||
border-left: 1px solid #ececec;
|
||||
border-right: 1px solid #ececec;
|
||||
padding: 15px;
|
||||
color: #6e7071;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.resource-item:last-child {
|
||||
border-bottom: 1px solid #ececec;
|
||||
}
|
||||
|
||||
.resource-item:hover {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
|
||||
.text--bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.text--grey {
|
||||
color: #747578;
|
||||
}
|
||||
|
||||
.text--right {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.card {
|
||||
padding: 15px;
|
||||
font-size: 15px;
|
||||
background: #ffffff;
|
||||
box-shadow: 0 0 5px #0000000a;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.reset-filter-btn {
|
||||
border: none;
|
||||
padding: 8px;
|
||||
font-size: 14px;
|
||||
background-color: transparent;
|
||||
color: #5faabd;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* tabs style */
|
||||
.tabs-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.tab {
|
||||
cursor: pointer;
|
||||
padding: 10px 20px;
|
||||
margin: 0 2px;
|
||||
background: transparent;
|
||||
display: inline-block;
|
||||
color: #747578;
|
||||
transition: background-color 200ms;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.tab:hover {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
|
||||
.panels {
|
||||
width: 100%;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.panel {
|
||||
display: none;
|
||||
animation: fadein .8s;
|
||||
}
|
||||
|
||||
@keyframes fadein {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.panel-title {
|
||||
font-size: 1.5em;
|
||||
font-weight: bold
|
||||
}
|
||||
|
||||
.radio {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#one:checked ~ .panels #one-panel,
|
||||
#two:checked ~ .panels #two-panel,
|
||||
#three:checked ~ .panels #three-panel,
|
||||
#four:checked ~ .panels #four-panel {
|
||||
display: block
|
||||
}
|
||||
|
||||
#one:checked ~ .tabs #one-tab,
|
||||
#two:checked ~ .tabs #two-tab,
|
||||
#three:checked ~ .tabs #three-tab,
|
||||
#four:checked ~ .tabs #four-tab {
|
||||
background: #71b2c3;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.empty-message-container {
|
||||
color: #747578;
|
||||
}</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="heading d-flex justify-space-between align-center">
|
||||
<div>
|
||||
<h1 class="heading-title mb-1">Driftctl scan report</h1>
|
||||
<span class="heading-subtitle">Coverage 100%</span>
|
||||
</div>
|
||||
<div class="text--right">
|
||||
<h2 class="heading-date mb-1">Jun 08, 2021</h2>
|
||||
<span class="heading-subtitle">Scan duration 1m12s</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card d-flex justify-space-around mb-2 text--grey">
|
||||
<span>Total resources: 1</span>
|
||||
<span>Managed: 100%</span>
|
||||
<span>Changed: 0/1</span>
|
||||
<span>Unmanaged: 0%</span>
|
||||
<span>Missing: 0%</span>
|
||||
</div>
|
||||
<div class="app-content">
|
||||
|
||||
<div class="d-flex justify-center mt-5 mb-5">
|
||||
<h1>Congrats! Your infrastructure is in sync</h1>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
<script lang="js">
|
||||
const resources = document.querySelectorAll('.resource-item')
|
||||
|
||||
function hideResource(res) {
|
||||
res.classList.add('hide')
|
||||
}
|
||||
|
||||
function displayResource(res) {
|
||||
res.classList.remove('hide')
|
||||
}
|
||||
|
||||
function resourceIdContains(res, query) {
|
||||
const el = res.querySelector('.resource-item-id')
|
||||
if (!el) {
|
||||
return false
|
||||
}
|
||||
return el.innerText.toLowerCase().includes(query.toLowerCase())
|
||||
}
|
||||
|
||||
function resourceTypeEqual(res, type) {
|
||||
const el = res.querySelector('.resource-item-type')
|
||||
if (!el) {
|
||||
return false
|
||||
}
|
||||
return el.innerText === type
|
||||
}
|
||||
|
||||
function refreshCounters() {
|
||||
const counterClassMapping = {
|
||||
'.resource-item-unmanaged': '.resource-count-unmanaged',
|
||||
'.resource-item-changed': '.resource-count-changed',
|
||||
'.resource-item-deleted': '.resource-count-deleted',
|
||||
'.resource-item-alerts': '.resource-count-alerts',
|
||||
}
|
||||
|
||||
for (const resClass in counterClassMapping) {
|
||||
const countEl = document.querySelector(counterClassMapping[resClass])
|
||||
if (!countEl) {
|
||||
continue
|
||||
}
|
||||
countEl.textContent = Array.from(document.querySelectorAll(resClass)).filter(el => !el.classList.contains('hide')).length
|
||||
}
|
||||
}
|
||||
|
||||
function computeEmptyMessage() {
|
||||
const msgEl = document.querySelector('.empty-message-container')
|
||||
const wrapperEl = document.querySelector('.tabs-wrapper')
|
||||
const count = Array.from(resources).filter(el => !el.classList.contains('hide')).length
|
||||
if (count === 0) {
|
||||
msgEl.classList.remove('hide')
|
||||
wrapperEl.classList.add('hide')
|
||||
return
|
||||
}
|
||||
msgEl.classList.add('hide')
|
||||
wrapperEl.classList.remove('hide')
|
||||
}
|
||||
|
||||
function refreshState() {
|
||||
const queryFilterInput = document.querySelector('input[name=resource-id-filter]').value
|
||||
const typeFilterInput = document.querySelector('select[name=resource-type-filter]').value
|
||||
|
||||
for (const res of resources) {
|
||||
const matchId = !queryFilterInput.length || resourceIdContains(res, queryFilterInput)
|
||||
const matchType = !typeFilterInput.length || resourceTypeEqual(res, typeFilterInput)
|
||||
|
||||
if (matchId && matchType) {
|
||||
displayResource(res)
|
||||
continue
|
||||
}
|
||||
|
||||
hideResource(res)
|
||||
}
|
||||
|
||||
refreshCounters()
|
||||
computeEmptyMessage()
|
||||
}
|
||||
|
||||
function resetFilters() {
|
||||
document.querySelector('input[name=resource-id-filter]').value = ""
|
||||
document.querySelector('select[name=resource-type-filter]').value = ""
|
||||
refreshState()
|
||||
}
|
||||
|
||||
refreshState()
|
||||
</script>
|
||||
</html>
|
Loading…
Reference in New Issue