Feature: Rewamp super admin dashboard (#882)
This commit is contained in:
parent
e49a200ee0
commit
d8d14fc4a4
63 changed files with 2190 additions and 79 deletions
|
@ -177,6 +177,8 @@ linters:
|
||||||
allow_element_with_attribute: false
|
allow_element_with_attribute: false
|
||||||
allow_element_with_class: false
|
allow_element_with_class: false
|
||||||
allow_element_with_id: false
|
allow_element_with_id: false
|
||||||
|
exclude:
|
||||||
|
- 'app/assets/stylesheets/administrate/components/_buttons.scss'
|
||||||
|
|
||||||
SelectorDepth:
|
SelectorDepth:
|
||||||
enabled: true
|
enabled: true
|
||||||
|
@ -279,3 +281,4 @@ linters:
|
||||||
exclude:
|
exclude:
|
||||||
- 'app/javascript/widget/assets/scss/_reset.scss'
|
- 'app/javascript/widget/assets/scss/_reset.scss'
|
||||||
- 'app/javascript/widget/assets/scss/sdk.css'
|
- 'app/javascript/widget/assets/scss/sdk.css'
|
||||||
|
- 'app/assets/stylesheets/administrate/reset/_normalize.scss'
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
//= link_tree ../images
|
//= link_tree ../images
|
||||||
//= link administrate/application.css
|
//= link administrate/application.css
|
||||||
//= link administrate/application.js
|
//= link administrate/application.js
|
||||||
|
//= link dashboardChart.js
|
||||||
|
|
55
app/assets/javascripts/dashboardChart.js
Normal file
55
app/assets/javascripts/dashboardChart.js
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
// eslint-disable-next-line
|
||||||
|
function prepareData(data) {
|
||||||
|
var labels = [];
|
||||||
|
var dataSet = [];
|
||||||
|
data.forEach(item => {
|
||||||
|
labels.push(item[0]);
|
||||||
|
dataSet.push(item[1]);
|
||||||
|
});
|
||||||
|
return { labels, dataSet };
|
||||||
|
}
|
||||||
|
|
||||||
|
function getChartOptions() {
|
||||||
|
var fontFamily =
|
||||||
|
'Inter,-apple-system,system-ui,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif';
|
||||||
|
return {
|
||||||
|
responsive: true,
|
||||||
|
legend: { labels: { fontFamily } },
|
||||||
|
scales: {
|
||||||
|
xAxes: [
|
||||||
|
{
|
||||||
|
barPercentage: 1.26,
|
||||||
|
ticks: { fontFamily },
|
||||||
|
gridLines: { display: false },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
yAxes: [
|
||||||
|
{
|
||||||
|
ticks: { fontFamily },
|
||||||
|
gridLines: { display: false },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line
|
||||||
|
function drawSuperAdminDashboard(data) {
|
||||||
|
var ctx = document.getElementById('dashboard-chart').getContext('2d');
|
||||||
|
var chartData = prepareData(data);
|
||||||
|
// eslint-disable-next-line
|
||||||
|
new Chart(ctx, {
|
||||||
|
type: 'bar',
|
||||||
|
data: {
|
||||||
|
labels: chartData.labels,
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
label: 'Conversations',
|
||||||
|
data: chartData.dataSet,
|
||||||
|
backgroundColor: '#1f93ff',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
options: getChartOptions(),
|
||||||
|
});
|
||||||
|
}
|
32
app/assets/stylesheets/administrate/application.scss
Normal file
32
app/assets/stylesheets/administrate/application.scss
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
@charset 'utf-8';
|
||||||
|
|
||||||
|
@import 'reset/normalize';
|
||||||
|
|
||||||
|
@import 'utilities/variables';
|
||||||
|
@import 'utilities/text-color';
|
||||||
|
|
||||||
|
@import 'selectize';
|
||||||
|
@import 'datetime_picker';
|
||||||
|
|
||||||
|
@import 'library/clearfix';
|
||||||
|
@import 'library/data-label';
|
||||||
|
@import 'library/variables';
|
||||||
|
|
||||||
|
@import 'base/forms';
|
||||||
|
@import 'base/layout';
|
||||||
|
@import 'base/lists';
|
||||||
|
@import 'base/tables';
|
||||||
|
@import 'base/typography';
|
||||||
|
|
||||||
|
@import 'components/app-container';
|
||||||
|
@import 'components/attributes';
|
||||||
|
@import 'components/buttons';
|
||||||
|
@import 'components/cells';
|
||||||
|
@import 'components/field-unit';
|
||||||
|
@import 'components/flashes';
|
||||||
|
@import 'components/form-actions';
|
||||||
|
@import 'components/main-content';
|
||||||
|
@import 'components/navigation';
|
||||||
|
@import 'components/pagination';
|
||||||
|
@import 'components/search';
|
||||||
|
@import 'components/reports';
|
103
app/assets/stylesheets/administrate/base/_forms.scss
Normal file
103
app/assets/stylesheets/administrate/base/_forms.scss
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
fieldset {
|
||||||
|
background-color: transparent;
|
||||||
|
border: 0;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
legend {
|
||||||
|
font-weight: $font-weight-medium;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
display: block;
|
||||||
|
font-weight: $font-weight-medium;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
input,
|
||||||
|
select {
|
||||||
|
display: block;
|
||||||
|
font-family: $base-font-family;
|
||||||
|
font-size: $base-font-size;
|
||||||
|
}
|
||||||
|
|
||||||
|
input,
|
||||||
|
select,
|
||||||
|
textarea {
|
||||||
|
display: block;
|
||||||
|
font-family: $base-font-family;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[type="color"],
|
||||||
|
[type="date"],
|
||||||
|
[type="datetime-local"],
|
||||||
|
[type="email"],
|
||||||
|
[type="month"],
|
||||||
|
[type="number"],
|
||||||
|
[type="password"],
|
||||||
|
[type="search"],
|
||||||
|
[type="tel"],
|
||||||
|
[type="text"],
|
||||||
|
[type="time"],
|
||||||
|
[type="url"],
|
||||||
|
[type="week"],
|
||||||
|
input:not([type]),
|
||||||
|
textarea {
|
||||||
|
appearance: none;
|
||||||
|
background-color: $white;
|
||||||
|
border: $base-border;
|
||||||
|
border-radius: $base-border-radius;
|
||||||
|
padding: 0.5em;
|
||||||
|
transition: border-color $base-duration $base-timing;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: mix($black, $base-border-color, 20%);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
border-color: $action-color;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
background-color: mix($black, $white, 5%);
|
||||||
|
cursor: not-allowed;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border: $base-border;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
resize: vertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
[type="checkbox"],
|
||||||
|
[type="radio"] {
|
||||||
|
display: inline;
|
||||||
|
margin-right: $small-spacing / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
[type="file"] {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
[type="checkbox"],
|
||||||
|
[type="radio"],
|
||||||
|
[type="file"],
|
||||||
|
select {
|
||||||
|
&:focus {
|
||||||
|
outline: $focus-outline;
|
||||||
|
outline-offset: $focus-outline-offset;
|
||||||
|
}
|
||||||
|
}
|
22
app/assets/stylesheets/administrate/base/_layout.scss
Normal file
22
app/assets/stylesheets/administrate/base/_layout.scss
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
html {
|
||||||
|
background-color: $color-white;
|
||||||
|
box-sizing: border-box;
|
||||||
|
font-size: 10px;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
}
|
||||||
|
|
||||||
|
*,
|
||||||
|
*::before,
|
||||||
|
*::after {
|
||||||
|
box-sizing: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
figure {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
img,
|
||||||
|
picture {
|
||||||
|
margin: 0;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
19
app/assets/stylesheets/administrate/base/_lists.scss
Normal file
19
app/assets/stylesheets/administrate/base/_lists.scss
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
ul,
|
||||||
|
ol {
|
||||||
|
list-style-type: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
dl {
|
||||||
|
margin-bottom: $small-spacing;
|
||||||
|
|
||||||
|
dt {
|
||||||
|
font-weight: $font-weight-medium;
|
||||||
|
margin-top: $small-spacing;
|
||||||
|
}
|
||||||
|
|
||||||
|
dd {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
71
app/assets/stylesheets/administrate/base/_tables.scss
Normal file
71
app/assets/stylesheets/administrate/base/_tables.scss
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
font-size: $font-size-default;
|
||||||
|
text-align: left;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tr {
|
||||||
|
border-bottom: $base-border;
|
||||||
|
|
||||||
|
th {
|
||||||
|
font-weight: $font-weight-medium;
|
||||||
|
|
||||||
|
&.cell-label--avatar-field {
|
||||||
|
a {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tbody tr {
|
||||||
|
&:hover {
|
||||||
|
background-color: $base-background-color;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: $focus-outline;
|
||||||
|
outline-offset: -($focus-outline-width);
|
||||||
|
}
|
||||||
|
|
||||||
|
td {
|
||||||
|
&.cell-data--avatar-field {
|
||||||
|
line-height: 1;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
img {
|
||||||
|
border-radius: 50%;
|
||||||
|
height: $space-large;
|
||||||
|
max-height: $space-large;
|
||||||
|
width: $space-large;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
td,
|
||||||
|
th {
|
||||||
|
padding: $space-slab;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
td:first-child,
|
||||||
|
th:first-child {
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
td:last-child,
|
||||||
|
th:last-child {
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
td img {
|
||||||
|
max-height: 2rem;
|
||||||
|
}
|
44
app/assets/stylesheets/administrate/base/_typography.scss
Normal file
44
app/assets/stylesheets/administrate/base/_typography.scss
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
body {
|
||||||
|
color: $base-font-color;
|
||||||
|
font-family: $base-font-family;
|
||||||
|
font-size: $base-font-size;
|
||||||
|
line-height: $base-line-height;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1,
|
||||||
|
h2,
|
||||||
|
h3,
|
||||||
|
h4,
|
||||||
|
h5,
|
||||||
|
h6 {
|
||||||
|
font-family: $heading-font-family;
|
||||||
|
font-size: $base-font-size;
|
||||||
|
line-height: $heading-line-height;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 0 0 $small-spacing;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: $action-color;
|
||||||
|
transition: color $base-duration $base-timing;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: mix($black, $action-color, 25%);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: $focus-outline;
|
||||||
|
outline-offset: $focus-outline-offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
border-bottom: $base-border;
|
||||||
|
border-left: 0;
|
||||||
|
border-right: 0;
|
||||||
|
border-top: 0;
|
||||||
|
margin: $base-spacing 0;
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
.app-container {
|
||||||
|
align-items: stretch;
|
||||||
|
display: flex;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
max-width: 100rem;
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
.attribute-label {
|
||||||
|
@include data-label;
|
||||||
|
clear: left;
|
||||||
|
float: left;
|
||||||
|
margin-bottom: $base-spacing;
|
||||||
|
margin-top: 0.25em;
|
||||||
|
text-align: right;
|
||||||
|
width: calc(15% - 1rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
.preserve-whitespace {
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-wrap: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.attribute-data {
|
||||||
|
float: left;
|
||||||
|
margin-bottom: $base-spacing;
|
||||||
|
margin-left: 2rem;
|
||||||
|
width: calc(85% - 1rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
.attribute--nested {
|
||||||
|
border: $base-border;
|
||||||
|
padding: $small-spacing;
|
||||||
|
}
|
50
app/assets/stylesheets/administrate/components/_buttons.scss
Normal file
50
app/assets/stylesheets/administrate/components/_buttons.scss
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
button,
|
||||||
|
input[type="button"],
|
||||||
|
input[type="reset"],
|
||||||
|
input[type="submit"],
|
||||||
|
.button {
|
||||||
|
appearance: none;
|
||||||
|
background-color: $color-woot;
|
||||||
|
border: 0;
|
||||||
|
border-radius: $base-border-radius;
|
||||||
|
color: $white;
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline-block;
|
||||||
|
font-size: $font-size-default;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
font-weight: $font-weight-medium;
|
||||||
|
line-height: 1;
|
||||||
|
padding: $space-one $space-two;
|
||||||
|
text-decoration: none;
|
||||||
|
transition: background-color $base-duration $base-timing;
|
||||||
|
user-select: none;
|
||||||
|
vertical-align: middle;
|
||||||
|
white-space: nowrap;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: mix($black, $color-woot, 20%);
|
||||||
|
color: $white;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: $focus-outline;
|
||||||
|
outline-offset: $focus-outline-offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
cursor: not-allowed;
|
||||||
|
opacity: 0.5;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: $color-woot;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.button--alt {
|
||||||
|
background-color: transparent;
|
||||||
|
border: $base-border;
|
||||||
|
border-color: $blue;
|
||||||
|
color: $blue;
|
||||||
|
margin-bottom: $base-spacing;
|
||||||
|
}
|
45
app/assets/stylesheets/administrate/components/_cells.scss
Normal file
45
app/assets/stylesheets/administrate/components/_cells.scss
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
.cell-label {
|
||||||
|
&:hover {
|
||||||
|
a {
|
||||||
|
color: $action-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg {
|
||||||
|
fill: $action-color;
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: inherit;
|
||||||
|
display: inline-block;
|
||||||
|
transition: color $base-duration $base-timing;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.cell-label--asc,
|
||||||
|
.cell-label--desc {
|
||||||
|
font-weight: $font-weight-medium;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cell-label__sort-indicator {
|
||||||
|
float: right;
|
||||||
|
margin-left: 5px;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
fill: $hint-grey;
|
||||||
|
height: 13px;
|
||||||
|
transition: transform $base-duration $base-timing;
|
||||||
|
width: 13px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.cell-label__sort-indicator--desc {
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cell-data--number,
|
||||||
|
.cell-label--number {
|
||||||
|
text-align: right;
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
.field-unit {
|
||||||
|
@include administrate-clearfix;
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
margin-bottom: $base-spacing;
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.field-unit__label {
|
||||||
|
float: left;
|
||||||
|
margin-left: 1rem;
|
||||||
|
text-align: right;
|
||||||
|
width: calc(15% - 1rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
.field-unit__field {
|
||||||
|
float: left;
|
||||||
|
margin-left: 2rem;
|
||||||
|
max-width: 50rem;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.field-unit--nested {
|
||||||
|
border: $base-border;
|
||||||
|
margin-left: 7.5%;
|
||||||
|
max-width: 60rem;
|
||||||
|
padding: $small-spacing;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.field-unit__field {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.field-unit__label {
|
||||||
|
width: 10rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.field-unit--required {
|
||||||
|
label::after {
|
||||||
|
color: $red;
|
||||||
|
content: ' *';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.attribute-data--avatar-field {
|
||||||
|
height: $space-larger;
|
||||||
|
width: $space-larger;
|
||||||
|
|
||||||
|
img {
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
}
|
28
app/assets/stylesheets/administrate/components/_flashes.scss
Normal file
28
app/assets/stylesheets/administrate/components/_flashes.scss
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
$base-spacing: 1.5em !default;
|
||||||
|
$flashes: (
|
||||||
|
"alert": #fff6bf,
|
||||||
|
"error": #fbe3e4,
|
||||||
|
"notice": #e5edf8,
|
||||||
|
"success": #e6efc2,
|
||||||
|
) !default;
|
||||||
|
|
||||||
|
@each $flash-type, $color in $flashes {
|
||||||
|
.flash-#{$flash-type} {
|
||||||
|
background-color: $color;
|
||||||
|
color: mix($black, $color, 60%);
|
||||||
|
display: block;
|
||||||
|
margin-bottom: $base-spacing / 2;
|
||||||
|
padding: $base-spacing / 2;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: mix($black, $color, 70%);
|
||||||
|
text-decoration: underline;
|
||||||
|
|
||||||
|
&:focus,
|
||||||
|
&:hover {
|
||||||
|
color: mix($black, $color, 90%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
.form-actions {
|
||||||
|
margin-left: calc(15% + 2rem);
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
.main-content {
|
||||||
|
font-size: $font-size-default;
|
||||||
|
left: 23rem;
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-content__body {
|
||||||
|
padding: $space-two;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-content__header {
|
||||||
|
align-items: center;
|
||||||
|
background-color: $color-white;
|
||||||
|
border-bottom: 1px solid $color-border;
|
||||||
|
display: flex;
|
||||||
|
min-height: 5.6rem;
|
||||||
|
padding: $space-small $space-normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-content__page-title {
|
||||||
|
font-size: $font-size-large;
|
||||||
|
font-weight: $font-weight-medium;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
.logo-brand {
|
||||||
|
margin-bottom: $space-normal;
|
||||||
|
padding: $space-normal $space-smaller;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navigation {
|
||||||
|
background: $white;
|
||||||
|
border-right: 1px solid $color-border;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
font-size: $font-size-default;
|
||||||
|
font-weight: $font-weight-medium;
|
||||||
|
height: 100%;
|
||||||
|
justify-content: flex-start;
|
||||||
|
left: 0;
|
||||||
|
margin: 0;
|
||||||
|
overflow: auto;
|
||||||
|
padding: $space-normal;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
width: 23rem;
|
||||||
|
z-index: 1023;
|
||||||
|
|
||||||
|
li {
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: $color-gray;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
i {
|
||||||
|
min-width: $space-medium;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.navigation__link {
|
||||||
|
background-color: transparent;
|
||||||
|
color: $color-gray;
|
||||||
|
display: block;
|
||||||
|
line-height: 1;
|
||||||
|
margin-bottom: $space-smaller;
|
||||||
|
padding: $space-one;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: $blue;
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: $blue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
&.navigation__link--active {
|
||||||
|
background-color: $color-background;
|
||||||
|
border-radius: $base-border-radius;
|
||||||
|
color: $blue;
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: $blue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.logout {
|
||||||
|
bottom: $space-normal;
|
||||||
|
left: $space-normal;
|
||||||
|
position: fixed;
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
.pagination {
|
||||||
|
font-size: $font-size-default;
|
||||||
|
margin-top: $base-spacing;
|
||||||
|
padding-left: $base-spacing;
|
||||||
|
padding-right: $base-spacing;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
.first,
|
||||||
|
.prev,
|
||||||
|
.page,
|
||||||
|
.next,
|
||||||
|
.last {
|
||||||
|
margin: $small-spacing;
|
||||||
|
}
|
||||||
|
|
||||||
|
.current {
|
||||||
|
font-weight: $font-weight-medium;
|
||||||
|
}
|
||||||
|
}
|
15
app/assets/stylesheets/administrate/components/_reports.scss
Normal file
15
app/assets/stylesheets/administrate/components/_reports.scss
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
.report--list {
|
||||||
|
display: flex;
|
||||||
|
padding: 0 $space-two $space-larger;
|
||||||
|
}
|
||||||
|
|
||||||
|
.report-card {
|
||||||
|
flex: 1;
|
||||||
|
font-size: $font-size-small;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
.metric {
|
||||||
|
font-size: $font-size-bigger;
|
||||||
|
font-weight: 200;
|
||||||
|
}
|
||||||
|
}
|
44
app/assets/stylesheets/administrate/components/_search.scss
Normal file
44
app/assets/stylesheets/administrate/components/_search.scss
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
.search {
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: 2rem;
|
||||||
|
max-width: 24rem;
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search__input {
|
||||||
|
background: $grey-1;
|
||||||
|
padding-left: $space-normal * 2.5;
|
||||||
|
padding-right: $space-normal * 2.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search__eyeglass-icon {
|
||||||
|
fill: $grey-7;
|
||||||
|
height: $space-normal;
|
||||||
|
left: $space-normal;
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
width: $space-normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search__clear-link {
|
||||||
|
height: $space-normal;
|
||||||
|
position: absolute;
|
||||||
|
right: $space-normal * 0.75;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
width: $space-normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search__clear-icon {
|
||||||
|
fill: $grey-5;
|
||||||
|
height: $space-normal;
|
||||||
|
position: absolute;
|
||||||
|
transition: fill $base-duration $base-timing;
|
||||||
|
width: $space-normal;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
fill: $action-color;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
@mixin administrate-clearfix {
|
||||||
|
&::after {
|
||||||
|
clear: both;
|
||||||
|
content: '';
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
@mixin data-label {
|
||||||
|
color: $hint-grey;
|
||||||
|
font-size: 0.8em;
|
||||||
|
font-weight: 400;
|
||||||
|
letter-spacing: 0.0357em;
|
||||||
|
position: relative;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
61
app/assets/stylesheets/administrate/library/_variables.scss
Normal file
61
app/assets/stylesheets/administrate/library/_variables.scss
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
// Typography
|
||||||
|
$base-font-family: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto",
|
||||||
|
"Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
|
||||||
|
sans-serif !default;
|
||||||
|
$heading-font-family: $base-font-family !default;
|
||||||
|
|
||||||
|
$base-font-size: 10px !default;
|
||||||
|
|
||||||
|
$base-line-height: 1.5 !default;
|
||||||
|
$heading-line-height: 1.2 !default;
|
||||||
|
|
||||||
|
// Other Sizes
|
||||||
|
$base-border-radius: 4px !default;
|
||||||
|
$base-spacing: $base-line-height * 1em !default;
|
||||||
|
$small-spacing: $base-spacing / 2 !default;
|
||||||
|
|
||||||
|
// Colors
|
||||||
|
$white: #fff !default;
|
||||||
|
$black: #000 !default;
|
||||||
|
|
||||||
|
$blue: #1f93ff !default;
|
||||||
|
$red: #ff382d !default;
|
||||||
|
$light-yellow: #ffc532 !default;
|
||||||
|
$light-green: #44ce4b !default;
|
||||||
|
|
||||||
|
$grey-0: #f6f7f7 !default;
|
||||||
|
$grey-1: #f0f4f5 !default;
|
||||||
|
$grey-2: #cfd8dc !default;
|
||||||
|
$grey-5: #adb5bd !default;
|
||||||
|
$grey-7: #293f54 !default;
|
||||||
|
|
||||||
|
$hint-grey: #7b808c !default;
|
||||||
|
|
||||||
|
// Font Colors
|
||||||
|
$base-font-color: $grey-7 !default;
|
||||||
|
$action-color: $blue !default;
|
||||||
|
|
||||||
|
// Background Colors
|
||||||
|
$base-background-color: $grey-0 !default;
|
||||||
|
|
||||||
|
// Focus
|
||||||
|
$focus-outline-color: transparentize($action-color, 0.4);
|
||||||
|
$focus-outline-width: 3px;
|
||||||
|
$focus-outline: $focus-outline-width solid $focus-outline-color;
|
||||||
|
$focus-outline-offset: 1px;
|
||||||
|
|
||||||
|
// Flash Colors
|
||||||
|
$flash-colors: (
|
||||||
|
alert: $light-yellow,
|
||||||
|
error: $red,
|
||||||
|
notice: mix($white, $blue, 50%),
|
||||||
|
success: $light-green
|
||||||
|
);
|
||||||
|
|
||||||
|
// Border
|
||||||
|
$base-border-color: $grey-1 !default;
|
||||||
|
$base-border: 1px solid $base-border-color !default;
|
||||||
|
|
||||||
|
// Transitions
|
||||||
|
$base-duration: 250ms !default;
|
||||||
|
$base-timing: ease-in-out !default;
|
447
app/assets/stylesheets/administrate/reset/_normalize.scss
Normal file
447
app/assets/stylesheets/administrate/reset/_normalize.scss
Normal file
|
@ -0,0 +1,447 @@
|
||||||
|
/*! normalize.css v7.0.0 | MIT License | github.com/necolas/normalize.css */
|
||||||
|
|
||||||
|
/* Document
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the line height in all browsers.
|
||||||
|
* 2. Prevent adjustments of font size after orientation changes in
|
||||||
|
* IE on Windows Phone and in iOS.
|
||||||
|
*/
|
||||||
|
|
||||||
|
html {
|
||||||
|
line-height: 1.15; /* 1 */
|
||||||
|
-ms-text-size-adjust: 100%; /* 2 */
|
||||||
|
-webkit-text-size-adjust: 100%; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sections
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the margin in all browsers (opinionated).
|
||||||
|
*/
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct display in IE 9-.
|
||||||
|
*/
|
||||||
|
|
||||||
|
article,
|
||||||
|
aside,
|
||||||
|
footer,
|
||||||
|
header,
|
||||||
|
nav,
|
||||||
|
section {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Correct the font size and margin on `h1` elements within `section` and
|
||||||
|
* `article` contexts in Chrome, Firefox, and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 2em;
|
||||||
|
margin: 0.67em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Grouping content
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct display in IE 9-.
|
||||||
|
* 1. Add the correct display in IE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
figcaption,
|
||||||
|
figure,
|
||||||
|
main { /* 1 */
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct margin in IE 8.
|
||||||
|
*/
|
||||||
|
|
||||||
|
figure {
|
||||||
|
margin: 1em 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Add the correct box sizing in Firefox.
|
||||||
|
* 2. Show the overflow in Edge and IE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
hr {
|
||||||
|
box-sizing: content-box; /* 1 */
|
||||||
|
height: 0; /* 1 */
|
||||||
|
overflow: visible; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the inheritance and scaling of font size in all browsers.
|
||||||
|
* 2. Correct the odd `em` font sizing in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
pre {
|
||||||
|
font-family: monospace, monospace; /* 1 */
|
||||||
|
font-size: 1em; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Text-level semantics
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Remove the gray background on active links in IE 10.
|
||||||
|
* 2. Remove gaps in links underline in iOS 8+ and Safari 8+.
|
||||||
|
*/
|
||||||
|
|
||||||
|
a {
|
||||||
|
background-color: transparent; /* 1 */
|
||||||
|
-webkit-text-decoration-skip: objects; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Remove the bottom border in Chrome 57- and Firefox 39-.
|
||||||
|
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
abbr[title] {
|
||||||
|
border-bottom: none; /* 1 */
|
||||||
|
text-decoration: underline; /* 2 */
|
||||||
|
text-decoration: underline dotted; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prevent the duplicate application of `bolder` by the next rule in Safari 6.
|
||||||
|
*/
|
||||||
|
|
||||||
|
b,
|
||||||
|
strong {
|
||||||
|
font-weight: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct font weight in Chrome, Edge, and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
b,
|
||||||
|
strong {
|
||||||
|
font-weight: bolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the inheritance and scaling of font size in all browsers.
|
||||||
|
* 2. Correct the odd `em` font sizing in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
code,
|
||||||
|
kbd,
|
||||||
|
samp {
|
||||||
|
font-family: monospace, monospace; /* 1 */
|
||||||
|
font-size: 1em; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct font style in Android 4.3-.
|
||||||
|
*/
|
||||||
|
|
||||||
|
dfn {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct background and color in IE 9-.
|
||||||
|
*/
|
||||||
|
|
||||||
|
mark {
|
||||||
|
background-color: #ff0;
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct font size in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
small {
|
||||||
|
font-size: 80%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prevent `sub` and `sup` elements from affecting the line height in
|
||||||
|
* all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
sub,
|
||||||
|
sup {
|
||||||
|
font-size: 75%;
|
||||||
|
line-height: 0;
|
||||||
|
position: relative;
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub {
|
||||||
|
bottom: -0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
sup {
|
||||||
|
top: -0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Embedded content
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct display in IE 9-.
|
||||||
|
*/
|
||||||
|
|
||||||
|
audio,
|
||||||
|
video {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct display in iOS 4-7.
|
||||||
|
*/
|
||||||
|
|
||||||
|
audio:not([controls]) {
|
||||||
|
display: none;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the border on images inside links in IE 10-.
|
||||||
|
*/
|
||||||
|
|
||||||
|
img {
|
||||||
|
border-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hide the overflow in IE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
svg:not(:root) {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Forms
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Change the font styles in all browsers (opinionated).
|
||||||
|
* 2. Remove the margin in Firefox and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button,
|
||||||
|
input,
|
||||||
|
optgroup,
|
||||||
|
select,
|
||||||
|
textarea {
|
||||||
|
font-family: sans-serif; /* 1 */
|
||||||
|
font-size: 100%; /* 1 */
|
||||||
|
line-height: 1.15; /* 1 */
|
||||||
|
margin: 0; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the overflow in IE.
|
||||||
|
* 1. Show the overflow in Edge.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button,
|
||||||
|
input { /* 1 */
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the inheritance of text transform in Edge, Firefox, and IE.
|
||||||
|
* 1. Remove the inheritance of text transform in Firefox.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button,
|
||||||
|
select { /* 1 */
|
||||||
|
text-transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Prevent a WebKit bug where (2) destroys native `audio` and `video`
|
||||||
|
* controls in Android 4.
|
||||||
|
* 2. Correct the inability to style clickable types in iOS and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button,
|
||||||
|
html [type="button"], /* 1 */
|
||||||
|
[type="reset"],
|
||||||
|
[type="submit"] {
|
||||||
|
-webkit-appearance: button; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the inner border and padding in Firefox.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button::-moz-focus-inner,
|
||||||
|
[type="button"]::-moz-focus-inner,
|
||||||
|
[type="reset"]::-moz-focus-inner,
|
||||||
|
[type="submit"]::-moz-focus-inner {
|
||||||
|
border-style: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restore the focus styles unset by the previous rule.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button:-moz-focusring,
|
||||||
|
[type="button"]:-moz-focusring,
|
||||||
|
[type="reset"]:-moz-focusring,
|
||||||
|
[type="submit"]:-moz-focusring {
|
||||||
|
outline: 1px dotted ButtonText;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Correct the padding in Firefox.
|
||||||
|
*/
|
||||||
|
|
||||||
|
fieldset {
|
||||||
|
padding: 0.35em 0.75em 0.625em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the text wrapping in Edge and IE.
|
||||||
|
* 2. Correct the color inheritance from `fieldset` elements in IE.
|
||||||
|
* 3. Remove the padding so developers are not caught out when they zero out
|
||||||
|
* `fieldset` elements in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
legend {
|
||||||
|
box-sizing: border-box; /* 1 */
|
||||||
|
color: inherit; /* 2 */
|
||||||
|
display: table; /* 1 */
|
||||||
|
max-width: 100%; /* 1 */
|
||||||
|
padding: 0; /* 3 */
|
||||||
|
white-space: normal; /* 1 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Add the correct display in IE 9-.
|
||||||
|
* 2. Add the correct vertical alignment in Chrome, Firefox, and Opera.
|
||||||
|
*/
|
||||||
|
|
||||||
|
progress {
|
||||||
|
display: inline-block; /* 1 */
|
||||||
|
vertical-align: baseline; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the default vertical scrollbar in IE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Add the correct box sizing in IE 10-.
|
||||||
|
* 2. Remove the padding in IE 10-.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[type="checkbox"],
|
||||||
|
[type="radio"] {
|
||||||
|
box-sizing: border-box; /* 1 */
|
||||||
|
padding: 0; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Correct the cursor style of increment and decrement buttons in Chrome.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[type="number"]::-webkit-inner-spin-button,
|
||||||
|
[type="number"]::-webkit-outer-spin-button {
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the odd appearance in Chrome and Safari.
|
||||||
|
* 2. Correct the outline style in Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[type="search"] {
|
||||||
|
-webkit-appearance: textfield; /* 1 */
|
||||||
|
outline-offset: -2px; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the inner padding and cancel buttons in Chrome and Safari on macOS.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[type="search"]::-webkit-search-cancel-button,
|
||||||
|
[type="search"]::-webkit-search-decoration {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the inability to style clickable types in iOS and Safari.
|
||||||
|
* 2. Change font properties to `inherit` in Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
::-webkit-file-upload-button {
|
||||||
|
-webkit-appearance: button; /* 1 */
|
||||||
|
font: inherit; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Interactive
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add the correct display in IE 9-.
|
||||||
|
* 1. Add the correct display in Edge, IE, and Firefox.
|
||||||
|
*/
|
||||||
|
|
||||||
|
details, /* 1 */
|
||||||
|
menu {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add the correct display in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
summary {
|
||||||
|
display: list-item;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Scripting
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct display in IE 9-.
|
||||||
|
*/
|
||||||
|
|
||||||
|
canvas {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct display in IE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
template {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hidden
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct display in IE 10-.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[hidden] {
|
||||||
|
display: none;
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
.text-color-red {
|
||||||
|
color: $alert-color;
|
||||||
|
}
|
|
@ -0,0 +1,98 @@
|
||||||
|
// Font sizes
|
||||||
|
$font-size-nano: 0.8rem;
|
||||||
|
$font-size-micro: 1.0rem;
|
||||||
|
$font-size-mini: 1.2rem;
|
||||||
|
$font-size-small: 1.4rem;
|
||||||
|
$font-size-default: 1.6rem;
|
||||||
|
$font-size-medium: 1.8rem;
|
||||||
|
$font-size-large: 2.2rem;
|
||||||
|
$font-size-big: 2.4rem;
|
||||||
|
$font-size-bigger: 3.0rem;
|
||||||
|
$font-size-mega: 3.4rem;
|
||||||
|
$font-size-giga: 4.0rem;
|
||||||
|
|
||||||
|
// spaces
|
||||||
|
$zero: 0;
|
||||||
|
$space-micro: 0.2rem;
|
||||||
|
$space-smaller: 0.4rem;
|
||||||
|
$space-small: 0.8rem;
|
||||||
|
$space-one: 1rem;
|
||||||
|
$space-slab: 1.2rem;
|
||||||
|
$space-normal: 1.6rem;
|
||||||
|
$space-two: 2.0rem;
|
||||||
|
$space-medium: 2.4rem;
|
||||||
|
$space-large: 3.2rem;
|
||||||
|
$space-larger: 4.8rem;
|
||||||
|
$space-jumbo: 6.4rem;
|
||||||
|
$space-mega: 10.0rem;
|
||||||
|
|
||||||
|
// font-weight
|
||||||
|
$font-weight-feather: 100;
|
||||||
|
$font-weight-light: 300;
|
||||||
|
$font-weight-normal: 400;
|
||||||
|
$font-weight-medium: 500;
|
||||||
|
$font-weight-bold: 600;
|
||||||
|
$font-weight-black: 700;
|
||||||
|
|
||||||
|
//Navbar
|
||||||
|
$nav-bar-width: 23rem;
|
||||||
|
$header-height: 5.6rem;
|
||||||
|
|
||||||
|
$woot-logo-padding: $space-large $space-two;
|
||||||
|
|
||||||
|
// Colors
|
||||||
|
$color-woot: #1f93ff;
|
||||||
|
$color-gray: #6e6f73;
|
||||||
|
$color-light-gray: #999a9b;
|
||||||
|
$color-border: #e0e6ed;
|
||||||
|
$color-border-light: #f0f4f5;
|
||||||
|
$color-background: #f4f6fb;
|
||||||
|
$color-border-dark: #cad0d4;
|
||||||
|
$color-background-light: #f9fafc;
|
||||||
|
$color-white: #fff;
|
||||||
|
$color-body: #3c4858;
|
||||||
|
$color-heading: #1f2d3d;
|
||||||
|
$color-extra-light-blue: #f5f7f9;
|
||||||
|
|
||||||
|
$primary-color: $color-woot;
|
||||||
|
$secondary-color: #35c5ff;
|
||||||
|
$success-color: #44ce4b;
|
||||||
|
$warning-color: #ffc532;
|
||||||
|
$alert-color: #ff382d;
|
||||||
|
|
||||||
|
$masked-bg: rgba(0, 0, 0, .4);
|
||||||
|
|
||||||
|
// Color-palettes
|
||||||
|
|
||||||
|
$color-primary-light: #c7e3ff;
|
||||||
|
$color-primary-dark: darken($color-woot, 20%);
|
||||||
|
|
||||||
|
// Thumbnail
|
||||||
|
$thumbnail-radius: 4rem;
|
||||||
|
|
||||||
|
// chat-header
|
||||||
|
$conv-header-height: 4rem;
|
||||||
|
|
||||||
|
// Inbox List
|
||||||
|
|
||||||
|
$inbox-thumb-size: 4.8rem;
|
||||||
|
|
||||||
|
|
||||||
|
// Spinner
|
||||||
|
$spinkit-spinner-color: $color-white !default;
|
||||||
|
$spinkit-spinner-margin: 0 0 0 1.6rem !default;
|
||||||
|
$spinkit-size: 1.6rem !default;
|
||||||
|
|
||||||
|
// Snackbar default
|
||||||
|
$woot-snackbar-bg: #323232;
|
||||||
|
$woot-snackbar-button: #ffeb3b;
|
||||||
|
|
||||||
|
$swift-ease-out-duration: .4s !default;
|
||||||
|
$swift-ease-out-timing-function: cubic-bezier(.25, .8, .25, 1) !default;
|
||||||
|
$swift-ease-out: all $swift-ease-out-duration $swift-ease-out-timing-function !default;
|
||||||
|
|
||||||
|
// Ionicons
|
||||||
|
$ionicons-font-path: '~ionicons/fonts';
|
||||||
|
|
||||||
|
// Transitions
|
||||||
|
$transition-ease-in: all 0.250s ease-in;
|
|
@ -2,10 +2,32 @@ class SuperAdmin::AccountUsersController < SuperAdmin::ApplicationController
|
||||||
# Overwrite any of the RESTful controller actions to implement custom behavior
|
# Overwrite any of the RESTful controller actions to implement custom behavior
|
||||||
# For example, you may want to send an email after a foo is updated.
|
# For example, you may want to send an email after a foo is updated.
|
||||||
#
|
#
|
||||||
# def update
|
def create
|
||||||
# super
|
resource = resource_class.new(resource_params)
|
||||||
# send_foo_updated_email(requested_resource)
|
authorize_resource(resource)
|
||||||
# end
|
|
||||||
|
redirect_resource = params[:redirect_to] == 'user' ? resource.user : resource.account
|
||||||
|
if resource.save
|
||||||
|
redirect_to(
|
||||||
|
[namespace, redirect_resource],
|
||||||
|
notice: translate_with_resource('create.success')
|
||||||
|
)
|
||||||
|
else
|
||||||
|
redirect_to(
|
||||||
|
[namespace, redirect_resource],
|
||||||
|
notice: resource.errors.full_messages.first
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def destroy
|
||||||
|
if requested_resource.destroy
|
||||||
|
flash[:notice] = translate_with_resource('destroy.success')
|
||||||
|
else
|
||||||
|
flash[:error] = requested_resource.errors.full_messages.join('<br/>')
|
||||||
|
end
|
||||||
|
redirect_to([namespace, requested_resource.account])
|
||||||
|
end
|
||||||
|
|
||||||
# Override this method to specify custom lookup behavior.
|
# Override this method to specify custom lookup behavior.
|
||||||
# This will be used to set the resource for the `show`, `edit`, and `update`
|
# This will be used to set the resource for the `show`, `edit`, and `update`
|
||||||
|
|
|
@ -13,4 +13,11 @@ class SuperAdmin::ApplicationController < Administrate::ApplicationController
|
||||||
# def records_per_page
|
# def records_per_page
|
||||||
# params[:per_page] || 20
|
# params[:per_page] || 20
|
||||||
# end
|
# end
|
||||||
|
|
||||||
|
def order
|
||||||
|
@order ||= Administrate::Order.new(
|
||||||
|
params.fetch(resource_name, {}).fetch(:order, 'id'),
|
||||||
|
params.fetch(resource_name, {}).fetch(:direction, 'desc')
|
||||||
|
)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
12
app/controllers/super_admin/dashboard_controller.rb
Normal file
12
app/controllers/super_admin/dashboard_controller.rb
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
class SuperAdmin::DashboardController < SuperAdmin::ApplicationController
|
||||||
|
include ActionView::Helpers::NumberHelper
|
||||||
|
|
||||||
|
def index
|
||||||
|
@data = Conversation.unscoped.group_by_day(:created_at, range: 30.days.ago..2.seconds.ago).count.to_a
|
||||||
|
@accounts_count = number_with_delimiter(Account.all.length)
|
||||||
|
@users_count = number_with_delimiter(User.all.length)
|
||||||
|
@inboxes_count = number_with_delimiter(Inbox.all.length)
|
||||||
|
@conversations_count = number_with_delimiter(Conversation.all.length)
|
||||||
|
@messages_count = number_with_delimiter(Message.all.length)
|
||||||
|
end
|
||||||
|
end
|
|
@ -12,7 +12,10 @@ class AccountDashboard < Administrate::BaseDashboard
|
||||||
name: Field::String,
|
name: Field::String,
|
||||||
created_at: Field::DateTime,
|
created_at: Field::DateTime,
|
||||||
updated_at: Field::DateTime,
|
updated_at: Field::DateTime,
|
||||||
locale: Field::String.with_options(searchable: false)
|
users: CountField,
|
||||||
|
conversations: CountField,
|
||||||
|
locale: Field::Select.with_options(collection: LANGUAGES_CONFIG.map { |_x, y| y[:iso_639_1_code] }),
|
||||||
|
account_users: Field::HasMany
|
||||||
}.freeze
|
}.freeze
|
||||||
|
|
||||||
# COLLECTION_ATTRIBUTES
|
# COLLECTION_ATTRIBUTES
|
||||||
|
@ -21,8 +24,11 @@ class AccountDashboard < Administrate::BaseDashboard
|
||||||
# By default, it's limited to four items to reduce clutter on index pages.
|
# By default, it's limited to four items to reduce clutter on index pages.
|
||||||
# Feel free to add, remove, or rearrange items.
|
# Feel free to add, remove, or rearrange items.
|
||||||
COLLECTION_ATTRIBUTES = %i[
|
COLLECTION_ATTRIBUTES = %i[
|
||||||
|
id
|
||||||
name
|
name
|
||||||
locale
|
locale
|
||||||
|
users
|
||||||
|
conversations
|
||||||
].freeze
|
].freeze
|
||||||
|
|
||||||
# SHOW_PAGE_ATTRIBUTES
|
# SHOW_PAGE_ATTRIBUTES
|
||||||
|
@ -33,6 +39,8 @@ class AccountDashboard < Administrate::BaseDashboard
|
||||||
created_at
|
created_at
|
||||||
updated_at
|
updated_at
|
||||||
locale
|
locale
|
||||||
|
conversations
|
||||||
|
account_users
|
||||||
].freeze
|
].freeze
|
||||||
|
|
||||||
# FORM_ATTRIBUTES
|
# FORM_ATTRIBUTES
|
||||||
|
@ -58,7 +66,7 @@ class AccountDashboard < Administrate::BaseDashboard
|
||||||
# Overwrite this method to customize how accounts are displayed
|
# Overwrite this method to customize how accounts are displayed
|
||||||
# across all pages of the admin dashboard.
|
# across all pages of the admin dashboard.
|
||||||
#
|
#
|
||||||
# def display_resource(account)
|
def display_resource(account)
|
||||||
# "Account ##{account.id}"
|
"##{account.id} #{account.name}"
|
||||||
# end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -8,12 +8,11 @@ class AccountUserDashboard < Administrate::BaseDashboard
|
||||||
# which determines how the attribute is displayed
|
# which determines how the attribute is displayed
|
||||||
# on pages throughout the dashboard.
|
# on pages throughout the dashboard.
|
||||||
ATTRIBUTE_TYPES = {
|
ATTRIBUTE_TYPES = {
|
||||||
account: Field::BelongsTo,
|
account: Field::BelongsTo.with_options(searchable: true, searchable_field: 'name'),
|
||||||
user: Field::BelongsTo,
|
user: Field::BelongsTo.with_options(searchable: true, searchable_field: 'name'),
|
||||||
inviter: Field::BelongsTo.with_options(class_name: 'User'),
|
inviter: Field::BelongsTo.with_options(class_name: 'User', searchable: true, searchable_field: 'name'),
|
||||||
id: Field::Number,
|
id: Field::Number,
|
||||||
role: Field::String.with_options(searchable: false),
|
role: Field::Select.with_options(collection: AccountUser.roles.keys),
|
||||||
inviter_id: Field::Number,
|
|
||||||
created_at: Field::DateTime,
|
created_at: Field::DateTime,
|
||||||
updated_at: Field::DateTime
|
updated_at: Field::DateTime
|
||||||
}.freeze
|
}.freeze
|
||||||
|
@ -27,7 +26,7 @@ class AccountUserDashboard < Administrate::BaseDashboard
|
||||||
account
|
account
|
||||||
user
|
user
|
||||||
inviter
|
inviter
|
||||||
id
|
role
|
||||||
].freeze
|
].freeze
|
||||||
|
|
||||||
# SHOW_PAGE_ATTRIBUTES
|
# SHOW_PAGE_ATTRIBUTES
|
||||||
|
@ -38,7 +37,6 @@ class AccountUserDashboard < Administrate::BaseDashboard
|
||||||
inviter
|
inviter
|
||||||
id
|
id
|
||||||
role
|
role
|
||||||
inviter_id
|
|
||||||
created_at
|
created_at
|
||||||
updated_at
|
updated_at
|
||||||
].freeze
|
].freeze
|
||||||
|
@ -49,9 +47,7 @@ class AccountUserDashboard < Administrate::BaseDashboard
|
||||||
FORM_ATTRIBUTES = %i[
|
FORM_ATTRIBUTES = %i[
|
||||||
account
|
account
|
||||||
user
|
user
|
||||||
inviter
|
|
||||||
role
|
role
|
||||||
inviter_id
|
|
||||||
].freeze
|
].freeze
|
||||||
|
|
||||||
# COLLECTION_FILTERS
|
# COLLECTION_FILTERS
|
||||||
|
@ -69,7 +65,7 @@ class AccountUserDashboard < Administrate::BaseDashboard
|
||||||
# Overwrite this method to customize how account users are displayed
|
# Overwrite this method to customize how account users are displayed
|
||||||
# across all pages of the admin dashboard.
|
# across all pages of the admin dashboard.
|
||||||
#
|
#
|
||||||
# def display_resource(account_user)
|
def display_resource(account_user)
|
||||||
# "AccountUser ##{account_user.id}"
|
"AccountUser ##{account_user.id}"
|
||||||
# end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -10,6 +10,7 @@ class SuperAdminDashboard < Administrate::BaseDashboard
|
||||||
ATTRIBUTE_TYPES = {
|
ATTRIBUTE_TYPES = {
|
||||||
id: Field::Number,
|
id: Field::Number,
|
||||||
email: Field::String,
|
email: Field::String,
|
||||||
|
password: Field::Password,
|
||||||
access_token: Field::HasOne,
|
access_token: Field::HasOne,
|
||||||
remember_created_at: Field::DateTime,
|
remember_created_at: Field::DateTime,
|
||||||
sign_in_count: Field::Number,
|
sign_in_count: Field::Number,
|
||||||
|
@ -52,12 +53,7 @@ class SuperAdminDashboard < Administrate::BaseDashboard
|
||||||
# on the model's form (`new` and `edit`) pages.
|
# on the model's form (`new` and `edit`) pages.
|
||||||
FORM_ATTRIBUTES = %i[
|
FORM_ATTRIBUTES = %i[
|
||||||
email
|
email
|
||||||
remember_created_at
|
password
|
||||||
sign_in_count
|
|
||||||
current_sign_in_at
|
|
||||||
last_sign_in_at
|
|
||||||
current_sign_in_ip
|
|
||||||
last_sign_in_ip
|
|
||||||
].freeze
|
].freeze
|
||||||
|
|
||||||
# COLLECTION_FILTERS
|
# COLLECTION_FILTERS
|
||||||
|
|
|
@ -9,14 +9,11 @@ class UserDashboard < Administrate::BaseDashboard
|
||||||
# on pages throughout the dashboard.
|
# on pages throughout the dashboard.
|
||||||
ATTRIBUTE_TYPES = {
|
ATTRIBUTE_TYPES = {
|
||||||
account_users: Field::HasMany,
|
account_users: Field::HasMany,
|
||||||
accounts: Field::HasMany,
|
|
||||||
invitees: Field::HasMany.with_options(class_name: 'User'),
|
|
||||||
id: Field::Number,
|
id: Field::Number,
|
||||||
|
avatar_url: AvatarField,
|
||||||
provider: Field::String,
|
provider: Field::String,
|
||||||
uid: Field::String,
|
uid: Field::String,
|
||||||
reset_password_token: Field::String,
|
password: Field::Password,
|
||||||
reset_password_sent_at: Field::DateTime,
|
|
||||||
remember_created_at: Field::DateTime,
|
|
||||||
sign_in_count: Field::Number,
|
sign_in_count: Field::Number,
|
||||||
current_sign_in_at: Field::DateTime,
|
current_sign_in_at: Field::DateTime,
|
||||||
last_sign_in_at: Field::DateTime,
|
last_sign_in_at: Field::DateTime,
|
||||||
|
@ -32,7 +29,8 @@ class UserDashboard < Administrate::BaseDashboard
|
||||||
tokens: Field::String.with_options(searchable: false),
|
tokens: Field::String.with_options(searchable: false),
|
||||||
created_at: Field::DateTime,
|
created_at: Field::DateTime,
|
||||||
updated_at: Field::DateTime,
|
updated_at: Field::DateTime,
|
||||||
pubsub_token: Field::String
|
pubsub_token: Field::String,
|
||||||
|
accounts: CountField
|
||||||
}.freeze
|
}.freeze
|
||||||
|
|
||||||
# COLLECTION_ATTRIBUTES
|
# COLLECTION_ATTRIBUTES
|
||||||
|
@ -41,21 +39,25 @@ class UserDashboard < Administrate::BaseDashboard
|
||||||
# By default, it's limited to four items to reduce clutter on index pages.
|
# By default, it's limited to four items to reduce clutter on index pages.
|
||||||
# Feel free to add, remove, or rearrange items.
|
# Feel free to add, remove, or rearrange items.
|
||||||
COLLECTION_ATTRIBUTES = %i[
|
COLLECTION_ATTRIBUTES = %i[
|
||||||
|
id
|
||||||
|
avatar_url
|
||||||
name
|
name
|
||||||
email
|
email
|
||||||
|
accounts
|
||||||
].freeze
|
].freeze
|
||||||
|
|
||||||
# SHOW_PAGE_ATTRIBUTES
|
# SHOW_PAGE_ATTRIBUTES
|
||||||
# an array of attributes that will be displayed on the model's show page.
|
# an array of attributes that will be displayed on the model's show page.
|
||||||
SHOW_PAGE_ATTRIBUTES = %i[
|
SHOW_PAGE_ATTRIBUTES = %i[
|
||||||
accounts
|
|
||||||
id
|
id
|
||||||
|
avatar_url
|
||||||
unconfirmed_email
|
unconfirmed_email
|
||||||
name
|
name
|
||||||
nickname
|
nickname
|
||||||
email
|
email
|
||||||
created_at
|
created_at
|
||||||
updated_at
|
updated_at
|
||||||
|
account_users
|
||||||
].freeze
|
].freeze
|
||||||
|
|
||||||
# FORM_ATTRIBUTES
|
# FORM_ATTRIBUTES
|
||||||
|
@ -65,6 +67,7 @@ class UserDashboard < Administrate::BaseDashboard
|
||||||
name
|
name
|
||||||
nickname
|
nickname
|
||||||
email
|
email
|
||||||
|
password
|
||||||
].freeze
|
].freeze
|
||||||
|
|
||||||
# COLLECTION_FILTERS
|
# COLLECTION_FILTERS
|
||||||
|
@ -82,7 +85,7 @@ class UserDashboard < Administrate::BaseDashboard
|
||||||
# Overwrite this method to customize how users are displayed
|
# Overwrite this method to customize how users are displayed
|
||||||
# across all pages of the admin dashboard.
|
# across all pages of the admin dashboard.
|
||||||
#
|
#
|
||||||
# def display_resource(user)
|
def display_resource(user)
|
||||||
# "User ##{user.id}"
|
"##{user.id} #{user.name}"
|
||||||
# end
|
end
|
||||||
end
|
end
|
||||||
|
|
7
app/fields/avatar_field.rb
Normal file
7
app/fields/avatar_field.rb
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
require 'administrate/field/base'
|
||||||
|
|
||||||
|
class AvatarField < Administrate::Field::Base
|
||||||
|
def avatar_url
|
||||||
|
data.presence || '/admin/avatar.png'
|
||||||
|
end
|
||||||
|
end
|
7
app/fields/count_field.rb
Normal file
7
app/fields/count_field.rb
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
require 'administrate/field/base'
|
||||||
|
|
||||||
|
class CountField < Administrate::Field::Base
|
||||||
|
def to_s
|
||||||
|
data.count
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,13 +1,3 @@
|
||||||
@import 'shared/assets/fonts/inter';
|
@import 'shared/assets/fonts/inter';
|
||||||
@import '../variables';
|
@import '../variables';
|
||||||
|
@import '~ionicons/scss/ionicons';
|
||||||
body {
|
|
||||||
background-color: $color-background;
|
|
||||||
font-family: Inter;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button {
|
|
||||||
background-color: $color-woot;
|
|
||||||
border-radius: 1px solid $color-woot;
|
|
||||||
color: $color-white;
|
|
||||||
}
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ const runSDK = ({ baseUrl, websiteToken }) => {
|
||||||
isOpen: false,
|
isOpen: false,
|
||||||
position: chatwootSettings.position === 'left' ? 'left' : 'right',
|
position: chatwootSettings.position === 'left' ? 'left' : 'right',
|
||||||
websiteToken,
|
websiteToken,
|
||||||
locale: chatwootSettings.locale || 'en',
|
locale: chatwootSettings.locale,
|
||||||
|
|
||||||
toggle() {
|
toggle() {
|
||||||
IFrameHelper.events.toggleBubble();
|
IFrameHelper.events.toggleBubble();
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
import '../dashboard/assets/scss/super_admin/pages.scss';
|
import '../dashboard/assets/scss/super_admin/pages.scss';
|
||||||
|
import 'chart.js';
|
||||||
|
|
|
@ -43,7 +43,7 @@ class AccountUser < ApplicationRecord
|
||||||
end
|
end
|
||||||
|
|
||||||
def destroy_notification_setting
|
def destroy_notification_setting
|
||||||
setting = user.notification_settings.new(account_id: account.id)
|
setting = user.notification_settings.find_by(account_id: account.id)
|
||||||
setting.destroy!
|
setting.destroy!
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
1
app/views/fields/avatar_field/_index.html.erb
Normal file
1
app/views/fields/avatar_field/_index.html.erb
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<%= image_tag field.avatar_url %>
|
1
app/views/fields/avatar_field/_show.html.erb
Normal file
1
app/views/fields/avatar_field/_show.html.erb
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<%= image_tag field.avatar_url %>
|
1
app/views/fields/count_field/_index.html.erb
Normal file
1
app/views/fields/count_field/_index.html.erb
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<%= field.to_s %>
|
1
app/views/fields/count_field/_show.html.erb
Normal file
1
app/views/fields/count_field/_show.html.erb
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<%= field.to_s %>
|
41
app/views/layouts/super_admin/application.html.erb
Normal file
41
app/views/layouts/super_admin/application.html.erb
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
<%#
|
||||||
|
# Application Layout
|
||||||
|
|
||||||
|
This view template is used as the layout
|
||||||
|
for every page that Administrate generates.
|
||||||
|
|
||||||
|
By default, it renders:
|
||||||
|
- Navigation
|
||||||
|
- Content for a search bar
|
||||||
|
(if provided by a `content_for` block in a nested page)
|
||||||
|
- Flashes
|
||||||
|
- Links to stylesheets and JavaScripts
|
||||||
|
%>
|
||||||
|
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="<%= I18n.locale %>">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="ROBOTS" content="NOODP">
|
||||||
|
<meta name="viewport" content="initial-scale=1">
|
||||||
|
<title>
|
||||||
|
<%= content_for(:title) %> - <%= application_title %>
|
||||||
|
</title>
|
||||||
|
<%= render "stylesheet" %>
|
||||||
|
<%= csrf_meta_tags %>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<%= render "icons" %>
|
||||||
|
|
||||||
|
<div class="app-container super-admin">
|
||||||
|
<%= render "navigation" -%>
|
||||||
|
|
||||||
|
<main class="main-content" role="main">
|
||||||
|
<%= render "flashes" -%>
|
||||||
|
<%= yield %>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<%= render "javascript" %>
|
||||||
|
</body>
|
||||||
|
</html>
|
88
app/views/super_admin/accounts/show.html.erb
Normal file
88
app/views/super_admin/accounts/show.html.erb
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
<%#
|
||||||
|
# Show
|
||||||
|
|
||||||
|
This view is the template for the show page.
|
||||||
|
It renders the attributes of a resource,
|
||||||
|
as well as a link to its edit page.
|
||||||
|
|
||||||
|
## Local variables:
|
||||||
|
|
||||||
|
- `page`:
|
||||||
|
An instance of [Administrate::Page::Show][1].
|
||||||
|
Contains methods for accessing the resource to be displayed on the page,
|
||||||
|
as well as helpers for describing how each attribute of the resource
|
||||||
|
should be displayed.
|
||||||
|
|
||||||
|
[1]: http://www.rubydoc.info/gems/administrate/Administrate/Page/Show
|
||||||
|
%>
|
||||||
|
|
||||||
|
<% content_for(:title) { t("administrate.actions.show_resource", name: page.page_title) } %>
|
||||||
|
|
||||||
|
<header class="main-content__header" role="banner">
|
||||||
|
<h1 class="main-content__page-title">
|
||||||
|
<%= content_for(:title) %>
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<%= link_to(
|
||||||
|
t("administrate.actions.edit_resource", name: page.page_title),
|
||||||
|
[:edit, namespace, page.resource],
|
||||||
|
class: "button",
|
||||||
|
) if valid_action?(:edit) && show_action?(:edit, page.resource) %>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<section class="main-content__body">
|
||||||
|
<dl>
|
||||||
|
<% page.attributes.each do |attribute| %>
|
||||||
|
<dt class="attribute-label" id="<%= attribute.name %>">
|
||||||
|
<%= t(
|
||||||
|
"helpers.label.#{resource_name}.#{attribute.name}",
|
||||||
|
default: attribute.name.titleize,
|
||||||
|
) %>
|
||||||
|
</dt>
|
||||||
|
|
||||||
|
<dd class="attribute-data attribute-data--<%=attribute.html_class%>"
|
||||||
|
><%= render_field attribute, page: page %></dd>
|
||||||
|
<% end %>
|
||||||
|
</dl>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="main-content__body">
|
||||||
|
<% account_user_page = Administrate::Page::Form.new(AccountUserDashboard.new, AccountUser.new) %>
|
||||||
|
<%= form_for([namespace, account_user_page.resource], html: { class: "form" }) do |f| %>
|
||||||
|
<% if account_user_page.resource.errors.any? %>
|
||||||
|
<div id="error_explanation">
|
||||||
|
<h2>
|
||||||
|
<%= t(
|
||||||
|
"administrate.form.errors",
|
||||||
|
pluralized_errors: pluralize(account_user_page.resource.errors.count, t("administrate.form.error")),
|
||||||
|
resource_name: display_resource_name(account_user_page.resource_name)
|
||||||
|
) %>
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<% account_user_page.resource.errors.full_messages.each do |message| %>
|
||||||
|
<li class="flash-error"><%= message %></li>
|
||||||
|
<% end %>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<% account_user_page.attributes.each do |attribute| -%>
|
||||||
|
<% if attribute.name == "account" %>
|
||||||
|
<%= f.hidden_field('account_id', value: page.resource.id) %>
|
||||||
|
<%= f.hidden_field('redirect_to', value: 'user') %>
|
||||||
|
<% else %>
|
||||||
|
<div class="field-unit field-unit--<%= attribute.html_class %> field-unit--<%= requireness(attribute) %>">
|
||||||
|
<%= render_field attribute, f: f %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
<% end -%>
|
||||||
|
|
||||||
|
<div class="form-actions">
|
||||||
|
<%= f.submit %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
</section>
|
95
app/views/super_admin/application/_collection.html.erb
Normal file
95
app/views/super_admin/application/_collection.html.erb
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
<%#
|
||||||
|
# Collection
|
||||||
|
|
||||||
|
This partial is used on the `index` and `show` pages
|
||||||
|
to display a collection of resources in an HTML table.
|
||||||
|
|
||||||
|
## Local variables:
|
||||||
|
|
||||||
|
- `collection_presenter`:
|
||||||
|
An instance of [Administrate::Page::Collection][1].
|
||||||
|
The table presenter uses `ResourceDashboard::COLLECTION_ATTRIBUTES` to determine
|
||||||
|
the columns displayed in the table
|
||||||
|
- `resources`:
|
||||||
|
An ActiveModel::Relation collection of resources to be displayed in the table.
|
||||||
|
By default, the number of resources is limited by pagination
|
||||||
|
or by a hard limit to prevent excessive page load times
|
||||||
|
|
||||||
|
[1]: http://www.rubydoc.info/gems/administrate/Administrate/Page/Collection
|
||||||
|
%>
|
||||||
|
|
||||||
|
<table aria-labelledby="<%= table_title %>">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<% collection_presenter.attribute_types.each do |attr_name, attr_type| %>
|
||||||
|
<th class="cell-label
|
||||||
|
cell-label--<%= attr_type.html_class %>
|
||||||
|
cell-label--<%= collection_presenter.ordered_html_class(attr_name) %>"
|
||||||
|
scope="col"
|
||||||
|
role="columnheader"
|
||||||
|
aria-sort="<%= sort_order(collection_presenter.ordered_html_class(attr_name)) %>">
|
||||||
|
<%= link_to(sanitized_order_params(page, collection_field_name).merge(
|
||||||
|
collection_presenter.order_params_for(attr_name, key: collection_field_name)
|
||||||
|
)) do %>
|
||||||
|
<%= t(
|
||||||
|
"helpers.label.#{collection_presenter.resource_name}.#{attr_name}",
|
||||||
|
default: attr_name.to_s,
|
||||||
|
).titleize %>
|
||||||
|
<% if collection_presenter.ordered_by?(attr_name) %>
|
||||||
|
<span class="cell-label__sort-indicator cell-label__sort-indicator--<%= collection_presenter.ordered_html_class(attr_name) %>">
|
||||||
|
<svg aria-hidden="true">
|
||||||
|
<use xlink:href="#icon-up-caret" />
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
<% end %>
|
||||||
|
<% end %>
|
||||||
|
</th>
|
||||||
|
<% end %>
|
||||||
|
<% [valid_action?(:edit, collection_presenter.resource_name),
|
||||||
|
valid_action?(:destroy, collection_presenter.resource_name)].count(true).times do %>
|
||||||
|
<th scope="col"></th>
|
||||||
|
<% end %>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tbody>
|
||||||
|
<% resources.each do |resource| %>
|
||||||
|
<tr class="js-table-row"
|
||||||
|
tabindex="0"
|
||||||
|
<% if valid_action? :show, collection_presenter.resource_name %>
|
||||||
|
<%= %(role=link data-url=#{polymorphic_path([namespace, resource])}) %>
|
||||||
|
<% end %>
|
||||||
|
>
|
||||||
|
<% collection_presenter.attributes_for(resource).each do |attribute| %>
|
||||||
|
<td class="cell-data cell-data--<%= attribute.html_class %>">
|
||||||
|
<% if show_action? :show, resource -%>
|
||||||
|
<a href="<%= polymorphic_path([namespace, resource]) -%>"
|
||||||
|
class="action-show"
|
||||||
|
>
|
||||||
|
<%= render_field attribute %>
|
||||||
|
</a>
|
||||||
|
<% end -%>
|
||||||
|
</td>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<% if valid_action? :edit, collection_presenter.resource_name %>
|
||||||
|
<td><%= link_to(
|
||||||
|
t("administrate.actions.edit"),
|
||||||
|
[:edit, namespace, resource],
|
||||||
|
class: "action-edit",
|
||||||
|
) if show_action? :edit, resource%></td>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<% if valid_action? :destroy, collection_presenter.resource_name %>
|
||||||
|
<td><%= link_to(
|
||||||
|
t("administrate.actions.destroy"),
|
||||||
|
[namespace, resource],
|
||||||
|
class: "text-color-red",
|
||||||
|
method: :delete,
|
||||||
|
data: { confirm: t("administrate.actions.confirm") }
|
||||||
|
) if show_action? :destroy, resource %></td>
|
||||||
|
<% end %>
|
||||||
|
</tr>
|
||||||
|
<% end %>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
20
app/views/super_admin/application/_flashes.html.erb
Normal file
20
app/views/super_admin/application/_flashes.html.erb
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
<%#
|
||||||
|
# Flash Partial
|
||||||
|
|
||||||
|
This partial renders flash messages on every page.
|
||||||
|
|
||||||
|
## Relevant Helpers:
|
||||||
|
|
||||||
|
- `flash`:
|
||||||
|
Returns a hash,
|
||||||
|
where the keys are the type of flash (alert, error, notice, etc)
|
||||||
|
and the values are the message to be displayed.
|
||||||
|
%>
|
||||||
|
|
||||||
|
<% if flash.any? %>
|
||||||
|
<div class="flashes">
|
||||||
|
<% flash.each do |key, value| -%>
|
||||||
|
<div class="flash flash-<%= key %>"><%= value.to_s.html_safe %></div>
|
||||||
|
<% end -%>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
13
app/views/super_admin/application/_icons.html.erb
Normal file
13
app/views/super_admin/application/_icons.html.erb
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
|
||||||
|
<symbol id="icon-cancel" viewBox="0 0 48 48">
|
||||||
|
<path fill-rule="evenodd" d="M24 19.757l-8.485-8.485c-.784-.783-2.047-.782-2.827 0l-1.417 1.416c-.777.777-.78 2.046.002 2.827L19.757 24l-8.485 8.485c-.783.784-.782 2.047 0 2.827l1.416 1.417c.777.777 2.046.78 2.827-.002L24 28.243l8.485 8.485c.784.783 2.047.782 2.827 0l1.417-1.416c.777-.777.78-2.046-.002-2.827L28.243 24l8.485-8.485c.783-.784.782-2.047 0-2.827l-1.416-1.417c-.777-.777-2.046-.78-2.827.002L24 19.757zM24 47c12.703 0 23-10.297 23-23S36.703 1 24 1 1 11.297 1 24s10.297 23 23 23z" />
|
||||||
|
</symbol>
|
||||||
|
|
||||||
|
<symbol id="icon-eyeglass" viewBox="0 0 48 48">
|
||||||
|
<path d="M27.885 32.515c-2.864 1.966-6.333 3.116-10.07 3.116C7.976 35.63 0 27.656 0 17.817 0 7.976 7.976 0 17.816 0S35.63 7.976 35.63 17.816c0 3.736-1.15 7.205-3.115 10.07l14.53 14.53c1.278 1.277 1.275 3.352 0 4.628-1.28 1.278-3.353 1.278-4.63 0l-14.53-14.53zm-10.07-3.736c6.056 0 10.964-4.91 10.964-10.964 0-6.055-4.91-10.964-10.964-10.964-6.055 0-10.964 4.91-10.964 10.964 0 6.055 4.91 10.963 10.964 10.963z" />
|
||||||
|
</symbol>
|
||||||
|
|
||||||
|
<symbol id="icon-up-caret" viewBox="0 0 48 48">
|
||||||
|
<path d="M2.988 33.02c-1.66 0-1.943-.81-.618-1.824l20-15.28c.878-.672 2.31-.67 3.188 0l20.075 15.288c1.316 1.003 1.048 1.816-.62 1.816H2.987z" />
|
||||||
|
</symbol>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.3 KiB |
21
app/views/super_admin/application/_javascript.html.erb
Normal file
21
app/views/super_admin/application/_javascript.html.erb
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
<%#
|
||||||
|
# Javascript Partial
|
||||||
|
|
||||||
|
This partial imports the necessary javascript on each page.
|
||||||
|
By default, it includes the application JS,
|
||||||
|
but each page can define additional JS sources
|
||||||
|
by providing a `content_for(:javascript)` block.
|
||||||
|
%>
|
||||||
|
|
||||||
|
<% Administrate::Engine.javascripts.each do |js_path| %>
|
||||||
|
<%= javascript_include_tag js_path %>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<%= yield :javascript %>
|
||||||
|
|
||||||
|
<% if Rails.env.test? %>
|
||||||
|
<%= javascript_tag do %>
|
||||||
|
$.fx.off = true;
|
||||||
|
$.ajaxSetup({ async: false });
|
||||||
|
<% end %>
|
||||||
|
<% end %>
|
|
@ -10,21 +10,43 @@ as defined by the routes in the `admin/` namespace
|
||||||
<%= javascript_pack_tag 'superadmin_pages' %>
|
<%= javascript_pack_tag 'superadmin_pages' %>
|
||||||
<%= stylesheet_pack_tag 'superadmin_pages' %>
|
<%= stylesheet_pack_tag 'superadmin_pages' %>
|
||||||
|
|
||||||
|
<%
|
||||||
|
sidebar_icons = {
|
||||||
|
accounts: 'ion ion-briefcase',
|
||||||
|
users: 'ion ion-person-stalker',
|
||||||
|
super_admins: 'ion ion-unlocked',
|
||||||
|
access_tokens: 'ion-key'
|
||||||
|
}
|
||||||
|
%>
|
||||||
|
|
||||||
<nav class="navigation" role="navigation">
|
<div class="navigation" role="navigation">
|
||||||
<%= link_to "Dashboard", super_admin_root_url, class: "button button--alt" %>
|
<div class="logo-brand">
|
||||||
|
<%= link_to image_tag('/brand-assets/logo.svg', alt: 'Chatwoot Admin Dashboard'), super_admin_root_url %>
|
||||||
<% Administrate::Namespace.new(namespace).resources.each do |resource| %>
|
|
||||||
<%= link_to(
|
|
||||||
display_resource_name(resource),
|
|
||||||
[namespace, resource_index_route_key(resource)],
|
|
||||||
class: "navigation__link navigation__link--#{nav_link_state(resource)}"
|
|
||||||
) if valid_action? :index, resource %>
|
|
||||||
<% end %>
|
|
||||||
|
|
||||||
<%= link_to "Sidekiq", sidekiq_web_url , class: "button" %>
|
|
||||||
|
|
||||||
<div style="margin-top: 300px;">
|
|
||||||
<%= link_to "Logout", super_admin_logout_url , class: "button button--alt" %>
|
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
|
||||||
|
<ul>
|
||||||
|
<li class="navigation__link">
|
||||||
|
<i class="ion ion-ios-keypad"></i>
|
||||||
|
<%= link_to "Dashboard", super_admin_root_url %>
|
||||||
|
</li>
|
||||||
|
<% Administrate::Namespace.new(namespace).resources.each do |resource| %>
|
||||||
|
<% next if ["account_users", "dashboard", "devise/sessions"].include? resource.resource %>
|
||||||
|
<li class="navigation__link navigation__link--<%= nav_link_state(resource) %>">
|
||||||
|
<i class="<%= sidebar_icons[resource.resource.to_sym] %>"></i>
|
||||||
|
<%= link_to(
|
||||||
|
display_resource_name(resource),
|
||||||
|
[namespace, resource_index_route_key(resource)],
|
||||||
|
) if valid_action? :index, resource %>
|
||||||
|
</li>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<li class="navigation__link">
|
||||||
|
<i class="ion ion ion-network"></i>
|
||||||
|
<%= link_to "Sidekiq", sidekiq_web_url %>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<li class="navigation__link logout">
|
||||||
|
<i class="ion ion-log-out"></i>
|
||||||
|
<%= link_to "Logout", super_admin_logout_url %>
|
||||||
|
</li>
|
||||||
|
</div>
|
||||||
|
|
14
app/views/super_admin/application/_stylesheet.html.erb
Normal file
14
app/views/super_admin/application/_stylesheet.html.erb
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
<%#
|
||||||
|
# Stylesheet Partial
|
||||||
|
|
||||||
|
This partial imports the necessary stylesheets on each page.
|
||||||
|
By default, it includes the application CSS,
|
||||||
|
but each page can define additional CSS sources
|
||||||
|
by providing a `content_for(:stylesheet)` block.
|
||||||
|
%>
|
||||||
|
|
||||||
|
<% Administrate::Engine.stylesheets.each do |css_path| %>
|
||||||
|
<%= stylesheet_link_tag css_path %>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<%= yield :stylesheet %>
|
66
app/views/super_admin/application/index.html.erb
Normal file
66
app/views/super_admin/application/index.html.erb
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
<%#
|
||||||
|
# Index
|
||||||
|
|
||||||
|
This view is the template for the index page.
|
||||||
|
It is responsible for rendering the search bar, header and pagination.
|
||||||
|
It renders the `_table` partial to display details about the resources.
|
||||||
|
|
||||||
|
## Local variables:
|
||||||
|
|
||||||
|
- `page`:
|
||||||
|
An instance of [Administrate::Page::Collection][1].
|
||||||
|
Contains helper methods to help display a table,
|
||||||
|
and knows which attributes should be displayed in the resource's table.
|
||||||
|
- `resources`:
|
||||||
|
An instance of `ActiveRecord::Relation` containing the resources
|
||||||
|
that match the user's search criteria.
|
||||||
|
By default, these resources are passed to the table partial to be displayed.
|
||||||
|
- `search_term`:
|
||||||
|
A string containing the term the user has searched for, if any.
|
||||||
|
- `show_search_bar`:
|
||||||
|
A boolean that determines if the search bar should be shown.
|
||||||
|
|
||||||
|
[1]: http://www.rubydoc.info/gems/administrate/Administrate/Page/Collection
|
||||||
|
%>
|
||||||
|
|
||||||
|
<% content_for(:title) do %>
|
||||||
|
<%= display_resource_name(page.resource_name) %>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<header class="main-content__header" role="banner">
|
||||||
|
<h1 class="main-content__page-title" id="page-title">
|
||||||
|
<%= content_for(:title) %>
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<% if show_search_bar %>
|
||||||
|
<%= render(
|
||||||
|
"search",
|
||||||
|
search_term: search_term,
|
||||||
|
resource_name: display_resource_name(page.resource_name)
|
||||||
|
) %>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<%= link_to(
|
||||||
|
t(
|
||||||
|
"administrate.actions.new_resource",
|
||||||
|
name: page.resource_name.titleize.downcase
|
||||||
|
),
|
||||||
|
[:new, namespace, page.resource_path],
|
||||||
|
class: "button",
|
||||||
|
) if valid_action?(:new) && show_action?(:new, new_resource) %>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<section class="main-content__body main-content__body--flush">
|
||||||
|
<%= render(
|
||||||
|
"collection",
|
||||||
|
collection_presenter: page,
|
||||||
|
collection_field_name: resource_name,
|
||||||
|
page: page,
|
||||||
|
resources: resources,
|
||||||
|
table_title: "page-title"
|
||||||
|
) %>
|
||||||
|
|
||||||
|
<%= paginate resources %>
|
||||||
|
</section>
|
69
app/views/super_admin/dashboard/index.html.erb
Normal file
69
app/views/super_admin/dashboard/index.html.erb
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
<%#
|
||||||
|
# Index
|
||||||
|
|
||||||
|
This view is the template for the index page.
|
||||||
|
It is responsible for rendering the search bar, header and pagination.
|
||||||
|
It renders the `_table` partial to display details about the resources.
|
||||||
|
|
||||||
|
## Local variables:
|
||||||
|
|
||||||
|
- `page`:
|
||||||
|
An instance of [Administrate::Page::Collection][1].
|
||||||
|
Contains helper methods to help display a table,
|
||||||
|
and knows which attributes should be displayed in the resource's table.
|
||||||
|
- `resources`:
|
||||||
|
An instance of `ActiveRecord::Relation` containing the resources
|
||||||
|
that match the user's search criteria.
|
||||||
|
By default, these resources are passed to the table partial to be displayed.
|
||||||
|
- `search_term`:
|
||||||
|
A string containing the term the user has searched for, if any.
|
||||||
|
- `show_search_bar`:
|
||||||
|
A boolean that determines if the search bar should be shown.
|
||||||
|
|
||||||
|
[1]: http://www.rubydoc.info/gems/administrate/Administrate/Page/Collection
|
||||||
|
%>
|
||||||
|
<%= javascript_include_tag "dashboardChart" %>
|
||||||
|
|
||||||
|
<% content_for(:title) do %>
|
||||||
|
Admin Dashboard
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<header class="main-content__header" role="banner">
|
||||||
|
<h1 class="main-content__page-title" id="page-title">
|
||||||
|
<%= content_for(:title) %>
|
||||||
|
</h1>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<section class="main-content__body main-content__body--flush">
|
||||||
|
|
||||||
|
<div class="report--list">
|
||||||
|
<div class="report-card">
|
||||||
|
<div class="metric"><%= @accounts_count %></div>
|
||||||
|
<div>Accounts</div>
|
||||||
|
</div>
|
||||||
|
<div class="report-card">
|
||||||
|
<div class="metric"><%= @users_count %></div>
|
||||||
|
<div>Users</div>
|
||||||
|
</div>
|
||||||
|
<div class="report-card">
|
||||||
|
<div class="metric"><%= @inboxes_count %></div>
|
||||||
|
<div>Inboxes</div>
|
||||||
|
</div>
|
||||||
|
<div class="report-card">
|
||||||
|
<div class="metric"><%= @conversations_count %></div>
|
||||||
|
<div>Conversations</div>
|
||||||
|
</div>
|
||||||
|
<div class="report-card">
|
||||||
|
<div class="metric"><%= @messages_count %></div>
|
||||||
|
<div>Messages</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="chart--container" style="height:500px">
|
||||||
|
<canvas id="dashboard-chart" width="500" height="250"></canvas>
|
||||||
|
</div>
|
||||||
|
<%= javascript_tag do -%>
|
||||||
|
drawSuperAdminDashboard(<%= @data.to_json.html_safe -%>)
|
||||||
|
<% end -%>
|
||||||
|
|
||||||
|
</section>
|
95
app/views/super_admin/users/_collection.html.erb
Normal file
95
app/views/super_admin/users/_collection.html.erb
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
<%#
|
||||||
|
# Collection
|
||||||
|
|
||||||
|
This partial is used on the `index` and `show` pages
|
||||||
|
to display a collection of resources in an HTML table.
|
||||||
|
|
||||||
|
## Local variables:
|
||||||
|
|
||||||
|
- `collection_presenter`:
|
||||||
|
An instance of [Administrate::Page::Collection][1].
|
||||||
|
The table presenter uses `ResourceDashboard::COLLECTION_ATTRIBUTES` to determine
|
||||||
|
the columns displayed in the table
|
||||||
|
- `resources`:
|
||||||
|
An ActiveModel::Relation collection of resources to be displayed in the table.
|
||||||
|
By default, the number of resources is limited by pagination
|
||||||
|
or by a hard limit to prevent excessive page load times
|
||||||
|
|
||||||
|
[1]: http://www.rubydoc.info/gems/administrate/Administrate/Page/Collection
|
||||||
|
%>
|
||||||
|
|
||||||
|
<table aria-labelledby="<%= table_title %>">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<% collection_presenter.attribute_types.each do |attr_name, attr_type| %>
|
||||||
|
<th class="cell-label
|
||||||
|
cell-label--<%= attr_type.html_class %>
|
||||||
|
cell-label--<%= collection_presenter.ordered_html_class(attr_name) %>"
|
||||||
|
scope="col"
|
||||||
|
role="columnheader"
|
||||||
|
aria-sort="<%= sort_order(collection_presenter.ordered_html_class(attr_name)) %>">
|
||||||
|
<%= link_to(sanitized_order_params(page, collection_field_name).merge(
|
||||||
|
collection_presenter.order_params_for(attr_name, key: collection_field_name)
|
||||||
|
)) do %>
|
||||||
|
<%= t(
|
||||||
|
"helpers.label.#{collection_presenter.resource_name}.#{attr_name}",
|
||||||
|
default: attr_name.to_s,
|
||||||
|
).titleize %>
|
||||||
|
<% if collection_presenter.ordered_by?(attr_name) %>
|
||||||
|
<span class="cell-label__sort-indicator cell-label__sort-indicator--<%= collection_presenter.ordered_html_class(attr_name) %>">
|
||||||
|
<svg aria-hidden="true">
|
||||||
|
<use xlink:href="#icon-up-caret" />
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
<% end %>
|
||||||
|
<% end %>
|
||||||
|
</th>
|
||||||
|
<% end %>
|
||||||
|
<% [valid_action?(:edit, collection_presenter.resource_name),
|
||||||
|
valid_action?(:destroy, collection_presenter.resource_name)].count(true).times do %>
|
||||||
|
<th scope="col"></th>
|
||||||
|
<% end %>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tbody>
|
||||||
|
<% resources.each do |resource| %>
|
||||||
|
<tr class="js-table-row"
|
||||||
|
tabindex="0"
|
||||||
|
<% if valid_action? :show, collection_presenter.resource_name %>
|
||||||
|
<%= %(role=link data-url=#{polymorphic_path([namespace, resource])}) %>
|
||||||
|
<% end %>
|
||||||
|
>
|
||||||
|
<% collection_presenter.attributes_for(resource).each do |attribute| %>
|
||||||
|
<td class="cell-data cell-data--<%= attribute.html_class %>">
|
||||||
|
<% if show_action? :show, resource -%>
|
||||||
|
<a href="<%= polymorphic_path([namespace, resource]) -%>"
|
||||||
|
class="action-show"
|
||||||
|
>
|
||||||
|
<%= render_field attribute %>
|
||||||
|
</a>
|
||||||
|
<% end -%>
|
||||||
|
</td>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<% if valid_action? :edit, collection_presenter.resource_name %>
|
||||||
|
<td><%= link_to(
|
||||||
|
t("administrate.actions.edit"),
|
||||||
|
[:edit, namespace, resource],
|
||||||
|
class: "action-edit",
|
||||||
|
) if show_action? :edit, resource%></td>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<% if valid_action? :destroy, collection_presenter.resource_name %>
|
||||||
|
<td><%= link_to(
|
||||||
|
t("administrate.actions.destroy"),
|
||||||
|
[namespace, resource],
|
||||||
|
class: "text-color-red",
|
||||||
|
method: :delete,
|
||||||
|
data: { confirm: t("administrate.actions.confirm") }
|
||||||
|
) if show_action? :destroy, resource %></td>
|
||||||
|
<% end %>
|
||||||
|
</tr>
|
||||||
|
<% end %>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
66
app/views/super_admin/users/index.html.erb
Normal file
66
app/views/super_admin/users/index.html.erb
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
<%#
|
||||||
|
# Index
|
||||||
|
|
||||||
|
This view is the template for the index page.
|
||||||
|
It is responsible for rendering the search bar, header and pagination.
|
||||||
|
It renders the `_table` partial to display details about the resources.
|
||||||
|
|
||||||
|
## Local variables:
|
||||||
|
|
||||||
|
- `page`:
|
||||||
|
An instance of [Administrate::Page::Collection][1].
|
||||||
|
Contains helper methods to help display a table,
|
||||||
|
and knows which attributes should be displayed in the resource's table.
|
||||||
|
- `resources`:
|
||||||
|
An instance of `ActiveRecord::Relation` containing the resources
|
||||||
|
that match the user's search criteria.
|
||||||
|
By default, these resources are passed to the table partial to be displayed.
|
||||||
|
- `search_term`:
|
||||||
|
A string containing the term the user has searched for, if any.
|
||||||
|
- `show_search_bar`:
|
||||||
|
A boolean that determines if the search bar should be shown.
|
||||||
|
|
||||||
|
[1]: http://www.rubydoc.info/gems/administrate/Administrate/Page/Collection
|
||||||
|
%>
|
||||||
|
|
||||||
|
<% content_for(:title) do %>
|
||||||
|
<%= display_resource_name(page.resource_name) %>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<header class="main-content__header" role="banner">
|
||||||
|
<h1 class="main-content__page-title" id="page-title">
|
||||||
|
<%= content_for(:title) %>
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<% if show_search_bar %>
|
||||||
|
<%= render(
|
||||||
|
"search",
|
||||||
|
search_term: search_term,
|
||||||
|
resource_name: display_resource_name(page.resource_name)
|
||||||
|
) %>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<%= link_to(
|
||||||
|
t(
|
||||||
|
"administrate.actions.new_resource",
|
||||||
|
name: page.resource_name.titleize.downcase
|
||||||
|
),
|
||||||
|
[:new, namespace, page.resource_path],
|
||||||
|
class: "button",
|
||||||
|
) if valid_action?(:new) && show_action?(:new, new_resource) %>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<section class="main-content__body main-content__body--flush">
|
||||||
|
<%= render(
|
||||||
|
"collection",
|
||||||
|
collection_presenter: page,
|
||||||
|
collection_field_name: resource_name,
|
||||||
|
page: page,
|
||||||
|
resources: resources,
|
||||||
|
table_title: "page-title"
|
||||||
|
) %>
|
||||||
|
|
||||||
|
<%= paginate resources %>
|
||||||
|
</section>
|
88
app/views/super_admin/users/show.html.erb
Normal file
88
app/views/super_admin/users/show.html.erb
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
<%#
|
||||||
|
# Show
|
||||||
|
|
||||||
|
This view is the template for the show page.
|
||||||
|
It renders the attributes of a resource,
|
||||||
|
as well as a link to its edit page.
|
||||||
|
|
||||||
|
## Local variables:
|
||||||
|
|
||||||
|
- `page`:
|
||||||
|
An instance of [Administrate::Page::Show][1].
|
||||||
|
Contains methods for accessing the resource to be displayed on the page,
|
||||||
|
as well as helpers for describing how each attribute of the resource
|
||||||
|
should be displayed.
|
||||||
|
|
||||||
|
[1]: http://www.rubydoc.info/gems/administrate/Administrate/Page/Show
|
||||||
|
%>
|
||||||
|
|
||||||
|
<% content_for(:title) { t("administrate.actions.show_resource", name: page.page_title) } %>
|
||||||
|
|
||||||
|
<header class="main-content__header" role="banner">
|
||||||
|
<h1 class="main-content__page-title">
|
||||||
|
<%= content_for(:title) %>
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<%= link_to(
|
||||||
|
t("administrate.actions.edit_resource", name: page.page_title),
|
||||||
|
[:edit, namespace, page.resource],
|
||||||
|
class: "button",
|
||||||
|
) if valid_action?(:edit) && show_action?(:edit, page.resource) %>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<section class="main-content__body">
|
||||||
|
<dl>
|
||||||
|
<% page.attributes.each do |attribute| %>
|
||||||
|
<dt class="attribute-label" id="<%= attribute.name %>">
|
||||||
|
<%= t(
|
||||||
|
"helpers.label.#{resource_name}.#{attribute.name}",
|
||||||
|
default: attribute.name.titleize,
|
||||||
|
) %>
|
||||||
|
</dt>
|
||||||
|
|
||||||
|
<dd class="attribute-data attribute-data--<%=attribute.html_class%>"
|
||||||
|
><%= render_field attribute, page: page %></dd>
|
||||||
|
<% end %>
|
||||||
|
</dl>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="main-content__body">
|
||||||
|
<% account_user_page = Administrate::Page::Form.new(AccountUserDashboard.new, AccountUser.new) %>
|
||||||
|
<%= form_for([namespace, account_user_page.resource], html: { class: "form" }) do |f| %>
|
||||||
|
<% if account_user_page.resource.errors.any? %>
|
||||||
|
<div id="error_explanation">
|
||||||
|
<h2>
|
||||||
|
<%= t(
|
||||||
|
"administrate.form.errors",
|
||||||
|
pluralized_errors: pluralize(account_user_page.resource.errors.count, t("administrate.form.error")),
|
||||||
|
resource_name: display_resource_name(account_user_page.resource_name)
|
||||||
|
) %>
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<% account_user_page.resource.errors.full_messages.each do |message| %>
|
||||||
|
<li class="flash-error"><%= message %></li>
|
||||||
|
<% end %>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<% account_user_page.attributes.each do |attribute| -%>
|
||||||
|
<% if attribute.name == "user" %>
|
||||||
|
<%= f.hidden_field('user_id', value: page.resource.id) %>
|
||||||
|
<%= f.hidden_field('redirect_to', value: 'user') %>
|
||||||
|
<% else %>
|
||||||
|
<div class="field-unit field-unit--<%= attribute.html_class %> field-unit--<%= requireness(attribute) %>">
|
||||||
|
<%= render_field attribute, f: f %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
<% end -%>
|
||||||
|
|
||||||
|
<div class="form-actions">
|
||||||
|
<%= f.submit %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
</section>
|
|
@ -172,13 +172,13 @@ Rails.application.routes.draw do
|
||||||
devise_scope :super_admin do
|
devise_scope :super_admin do
|
||||||
get 'super_admin/logout', to: 'super_admin/devise/sessions#destroy'
|
get 'super_admin/logout', to: 'super_admin/devise/sessions#destroy'
|
||||||
namespace :super_admin do
|
namespace :super_admin do
|
||||||
resources :users
|
|
||||||
resources :accounts
|
resources :accounts
|
||||||
resources :account_users
|
resources :account_users, only: [:new, :create, :destroy]
|
||||||
|
resources :users
|
||||||
resources :super_admins
|
resources :super_admins
|
||||||
resources :access_tokens
|
resources :access_tokens, only: [:index, :show]
|
||||||
|
|
||||||
root to: 'users#index'
|
root to: 'dashboard#index'
|
||||||
end
|
end
|
||||||
authenticated :super_admin do
|
authenticated :super_admin do
|
||||||
mount Sidekiq::Web => '/monitoring/sidekiq'
|
mount Sidekiq::Web => '/monitoring/sidekiq'
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
ActiveRecord::Schema.define(version: 2020_05_22_115645) do
|
ActiveRecord::Schema.define(version: 2020_05_22_115645) do
|
||||||
|
|
||||||
# These are extensions that must be enabled in order to support this database
|
# These are extensions that must be enabled in order to support this database
|
||||||
|
enable_extension "pg_stat_statements"
|
||||||
enable_extension "pgcrypto"
|
enable_extension "pgcrypto"
|
||||||
enable_extension "plpgsql"
|
enable_extension "plpgsql"
|
||||||
|
|
||||||
|
|
BIN
public/admin/avatar.png
Normal file
BIN
public/admin/avatar.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 8.3 KiB |
|
@ -16,7 +16,6 @@ RSpec.describe 'Super Admin access tokens API', type: :request do
|
||||||
sign_in super_admin
|
sign_in super_admin
|
||||||
get '/super_admin/access_tokens'
|
get '/super_admin/access_tokens'
|
||||||
expect(response).to have_http_status(:success)
|
expect(response).to have_http_status(:success)
|
||||||
expect(response.body).to include('New access token')
|
|
||||||
expect(response.body).to include(super_admin.access_token.token)
|
expect(response.body).to include(super_admin.access_token.token)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,23 +3,19 @@ require 'rails_helper'
|
||||||
RSpec.describe 'Super Admin Account Users API', type: :request do
|
RSpec.describe 'Super Admin Account Users API', type: :request do
|
||||||
let(:super_admin) { create(:super_admin) }
|
let(:super_admin) { create(:super_admin) }
|
||||||
|
|
||||||
describe 'GET /super_admin/account_users' do
|
describe 'GET /super_admin/account_users/new' do
|
||||||
context 'when it is an unauthenticated super admin' do
|
context 'when it is an unauthenticated super admin' do
|
||||||
it 'returns unauthorized' do
|
it 'returns unauthorized' do
|
||||||
get '/super_admin/account_users'
|
get '/super_admin/account_users/new'
|
||||||
expect(response).to have_http_status(:redirect)
|
expect(response).to have_http_status(:redirect)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when it is an authenticated super admin' do
|
context 'when it is an authenticated super admin' do
|
||||||
let!(:account_user) { create(:account_user) }
|
it 'shows the account user create page' do
|
||||||
|
|
||||||
it 'shows the list of account users' do
|
|
||||||
sign_in super_admin
|
sign_in super_admin
|
||||||
get '/super_admin/account_users'
|
get '/super_admin/account_users/new'
|
||||||
expect(response).to have_http_status(:success)
|
expect(response).to have_http_status(:success)
|
||||||
expect(response.body).to include(account_user.account.id.to_s)
|
|
||||||
expect(response.body).to include(account_user.user.id.to_s)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -15,7 +15,7 @@ RSpec.describe 'Super Admin', type: :request do
|
||||||
sign_in super_admin
|
sign_in super_admin
|
||||||
get '/super_admin'
|
get '/super_admin'
|
||||||
expect(response).to have_http_status(:success)
|
expect(response).to have_http_status(:success)
|
||||||
expect(response.body).to include('New user')
|
expect(response.body).to include('Dashboard')
|
||||||
|
|
||||||
sign_out super_admin
|
sign_out super_admin
|
||||||
get '/super_admin'
|
get '/super_admin'
|
||||||
|
|
Loading…
Reference in a new issue