UNCLASSIFIED
Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Open sidebar
Platform One
P
Party Bus
Launchboard
launchboard-fe
Commits
062d988f
Commit
062d988f
authored
Aug 01, 2021
by
luke.glasscock
Browse files
Update LB from code.il2
parent
83f88ca7
Changes
55
Hide whitespace changes
Inline
Side-by-side
Showing
20 changed files
with
719 additions
and
195 deletions
+719
-195
src/components/NavBar.vue
src/components/NavBar.vue
+51
-6
src/components/PersonnelTable.vue
src/components/PersonnelTable.vue
+149
-34
src/components/Section.vue
src/components/Section.vue
+22
-2
src/components/Training/TrainingAttendance.vue
src/components/Training/TrainingAttendance.vue
+135
-15
src/components/UserBanner.vue
src/components/UserBanner.vue
+16
-7
src/components/UserSelect.vue
src/components/UserSelect.vue
+9
-8
src/components/WelcomeSummary.vue
src/components/WelcomeSummary.vue
+2
-1
src/main.js
src/main.js
+1
-1
src/scss/custom.scss
src/scss/custom.scss
+87
-6
src/scss/mixins/breakpoints.scss
src/scss/mixins/breakpoints.scss
+42
-31
src/scss/variables.scss
src/scss/variables.scss
+7
-0
src/store/getter-types.js
src/store/getter-types.js
+3
-0
src/store/index.js
src/store/index.js
+2
-0
src/store/modules/loading.js
src/store/modules/loading.js
+48
-0
src/store/mutation-types.js
src/store/mutation-types.js
+4
-0
src/views/Projects.vue
src/views/Projects.vue
+8
-18
src/views/Resources.vue
src/views/Resources.vue
+10
-24
src/views/Settings.vue
src/views/Settings.vue
+1
-4
src/views/Team.vue
src/views/Team.vue
+20
-15
src/views/TrainingDetails.vue
src/views/TrainingDetails.vue
+102
-23
No files found.
src/components/NavBar.vue
View file @
062d988f
<
template
>
<
template
>
<div>
<div
class=
"lb-nav"
>
<v-app-bar
<v-app-bar
id
=
"app
-b
ar"
v-model
=
"app
B
ar"
app
app
id=
"app-bar"
flat
flat
:hide-on-scroll=
"!menuVisible"
:hide-on-scroll=
"!menuVisible"
height=
"102"
height=
"102"
...
@@ -41,7 +42,13 @@
...
@@ -41,7 +42,13 @@
</router-link>
</router-link>
</div>
</div>
</v-toolbar-items>
</v-toolbar-items>
<v-btn
icon
class=
"no-link nav-item"
id=
"settings-button"
to=
"/settings"
>
<v-btn
icon
class=
"no-link nav-item"
id=
"settings-button"
to=
"/settings"
v-if=
"$vuetify.breakpoint.mdAndUp"
>
<v-icon>
mdi-cog
</v-icon>
<v-icon>
mdi-cog
</v-icon>
</v-btn>
</v-btn>
<v-app-bar-nav-icon
<v-app-bar-nav-icon
...
@@ -84,8 +91,34 @@
...
@@ -84,8 +91,34 @@
>
>
{{
navItem
.
name
}}
{{
navItem
.
name
}}
</router-link>
</router-link>
<div>
<v-btn
icon
class=
"no-link nav-item my-3"
id=
"settings-button"
to=
"/settings"
>
<v-icon>
mdi-cog
</v-icon>
</v-btn>
</div>
</div>
</div>
</v-navigation-drawer>
</v-navigation-drawer>
<div
id=
"nav-space"
v-if=
"appBar"
></div>
<v-alert
dark
v-model=
"betaMessage"
dismissible
class=
"mb-0"
close-icon=
"mdi-close"
color=
"#15283A"
>
The Launchboard Team Management feature will be replaced by Keycloak
groups in the near future. At that time, membership will no longer be set
inside Launchboard. You may continue to use the Launchboard Team
Management feature for now and consider it a beta feature, but those
settings will be overridden by Keycloak once it is used as the source of
truth for Teams in Launchboard
</v-alert>
</div>
</div>
</
template
>
</
template
>
<
script
>
<
script
>
...
@@ -118,15 +151,18 @@ export default {
...
@@ -118,15 +151,18 @@ export default {
components
:
{
Burger
,
TutorialTooltip
},
components
:
{
Burger
,
TutorialTooltip
},
data
:
()
=>
({
data
:
()
=>
({
menuVisible
:
false
,
menuVisible
:
false
,
betaMessage
:
true
,
appBar
:
true
,
}),
}),
computed
:
{
computed
:
{
navItems
()
{
navItems
()
{
const
permission
=
this
.
$store
.
state
.
user
.
user
.
permission
;
const
permission
=
this
.
$store
.
state
.
user
.
user
.
permission
;
if
(
!
permission
)
throw
"
permission should not be empty
"
;
if
(
!
permission
)
throw
new
Error
(
"
permission should not be empty
"
)
;
const
navItems
=
userNav
[
permission
];
const
navItems
=
userNav
[
permission
];
if
(
!
navItems
)
throw
"
nav items are empty
"
;
if
(
!
navItems
)
throw
new
Error
(
"
nav items are empty
"
)
;
return
navItems
;
return
navItems
;
},
},
darkMode
()
{
darkMode
()
{
return
(
return
(
this
.
$store
.
state
.
userPreferences
.
userPreference
&&
this
.
$store
.
state
.
userPreferences
.
userPreference
&&
...
@@ -137,6 +173,15 @@ export default {
...
@@ -137,6 +173,15 @@ export default {
};
};
</
script
>
</
script
>
<
style
lang=
"scss"
>
<
style
lang=
"scss"
>
.lb-nav
{
.v-alert__wrapper
{
align-items
:
baseline
;
}
}
#nav-space
{
height
:
102px
;
width
:
100%
;
}
.theme--light
{
.theme--light
{
#app-bar
{
#app-bar
{
background-color
:
map-get
(
$material-light
,
"background"
);
background-color
:
map-get
(
$material-light
,
"background"
);
...
@@ -199,7 +244,7 @@ export default {
...
@@ -199,7 +244,7 @@ export default {
font-weight
:
600
;
font-weight
:
600
;
.nav-item
{
.nav-item
{
padding
:
8px
6px
;
padding
:
8px
6px
;
margin
:
0
1
6
px
;
margin
:
0
1
4
px
;
line-height
:
16px
;
line-height
:
16px
;
font-size
:
1rem
;
font-size
:
1rem
;
font-weight
:
bold
;
font-weight
:
bold
;
...
...
src/components/PersonnelTable.vue
View file @
062d988f
...
@@ -8,56 +8,138 @@
...
@@ -8,56 +8,138 @@
:confirmationAction="confirmUserPermissionChange"
:confirmationAction="confirmUserPermissionChange"
/>
/>
<div
<div
class=
"d-flex flex-wrap flex-sm-nowrap mb-4 justify-space-between"
>
class=
"d-flex flex-wrap flex-sm-nowrap px-2 px-sm-4 mb-4 justify-space-between"
>
<div
class=
"text-left"
>
<div
class=
"text-left"
>
<h
2
class=
"px-0 mb-2
subhead
"
>
{{
title
}}
</h
2
>
<h
3
class=
"px-0 mb-2"
>
{{
title
}}
</h
3
>
</div>
</div>
</div>
</div>
<v-data-table
<v-data-table
v-model=
"personnel.selectedPersonnel"
v-model=
"personnel.selectedPersonnel"
:headers=
"
personnel.
header
s
"
:headers=
"header"
:items=
"personnelList"
:items=
"personnelList"
show-select
show-select
mobile-breakpoint=
"800"
:show-expand=
"onMobile"
:hide-default-header=
"onMobile"
:options.sync=
"options"
:options.sync=
"options"
:footer-props=
"footerProps"
:footer-props=
"footerProps"
:server-items-length=
"total"
:server-items-length=
"total"
:hide-default-footer=
"personnelList.length === 0"
:hide-default-footer=
"personnelList.length === 0"
class=
"lb-mobile-enabled"
expand-icon=
"mdi-chevron-right"
:mobile-breakpoint=
"mobileBreakpoint"
>
>
<template
v-slot:
[`
item.permission
`
]=
"
{ item }">
<template
v-slot:
[`
item.username
`
]=
"
{ item }">
<a
class=
"primary--text"
:href=
"`https://chat.il2.dso.mil/platform-one/messages/@$
{item.username}`"
target="_blank"
rel="noopener noreferrer"
v-if="item.username"
>
@
{{
item
.
username
}}
</a>
</
template
>
<
template
v-slot:
[`
item.permission
`
]=
"{ item }"
v-if=
"!onMobile"
>
<span
v-if=
"userPermission !== permissionTypes.SUPER_ADMIN"
>
<span
v-if=
"userPermission !== permissionTypes.SUPER_ADMIN"
>
{{
item
.
permission
}}
{{
item
.
permission
}}
</span>
</span>
<v-select
<v-select
light
color=
"primary"
background-color=
"white"
class=
"v-select_selections theme--light pt-5"
v-if=
"userPermission === permissionTypes.SUPER_ADMIN"
v-if=
"userPermission === permissionTypes.SUPER_ADMIN"
v-model=
"item.permission"
v-model=
"item.permission"
:items=
"permissionTypesDropdown"
:items=
"permissionTypesDropdown"
dense
dense
outlined
@
change=
"updateUserPermissions(item)"
@
change=
"updateUserPermissions(item)"
></v-select>
></v-select>
</
template
>
</
template
>
<
template
v-slot:expanded-item=
"{ item }"
v-else
>
<td
class=
"text-start"
>
<div
v-for=
"value in mobileTableKeys"
:key=
"value"
>
<div
v-if=
"item[value]"
class=
"mt-2 ml-10 mt-5 pl-4"
>
<a
class=
"primary--text mattermost"
:href=
"`https://chat.il2.dso.mil/platform-one/messages/@$
{item.username}`"
target="_blank"
rel="noopener noreferrer"
v-if="value === 'username'"
>
@
{{
item
.
username
}}
</a>
<span
v-else-if=
"value !== 'permission'"
>
{{
item
[
value
]
}}
</span>
<span
v-else-if=
"
value === 'permission' &&
userPermission !== permissionTypes.SUPER_ADMIN
"
>
{{
item
.
permission
}}
</span>
<v-select
light
color=
"primary"
background-color=
"white"
class=
"v-select_selections theme--light"
v-else-if=
"
value === 'permission' &&
userPermission === permissionTypes.SUPER_ADMIN
"
v-model=
"item.permission"
:items=
"permissionTypesDropdown"
dense
outlined
@
change=
"updateUserPermissions(item)"
></v-select>
</div>
</div>
</td>
</
template
>
</v-data-table>
</v-data-table>
<div
class=
"d-flex flex-wrap mt-5"
>
<div
class=
"d-flex flex-wrap mt-5 mb-3"
>
<v-btn
<v-tooltip
top
>
@
click=
"removePersonnel"
<
template
v-slot:activator=
"{ on, attrs }"
>
:disabled=
"personnel.selectedPersonnel.length === 0"
<v-btn
class=
"mr-3"
class=
"mr-3 mb-3 mb-sm-0"
color=
"accent"
color=
"accent"
>
:disabled=
"personnel.selectedPersonnel.length === 0"
<v-icon
class=
"mr-2"
>
mdi-trash-can
</v-icon>
:block=
"$vuetify.breakpoint.xs"
Remove Selected
@
click=
"removePersonnel"
</v-btn>
v-bind=
"attrs"
<v-btn
v-on=
"on"
@
click=
"sendEmailToPersonnel"
>
:disabled=
"personnel.selectedPersonnel.length === 0"
<v-icon
class=
"mr-3"
>
mdi-delete
</v-icon>
class=
"mr-3"
Remove Selected
color=
"accent"
</v-btn>
>
</
template
>
<v-icon
class=
"mr-2"
>
mdi-pencil
</v-icon>
<span>
Email Selected
Remove the selected personnel{{
</v-btn>
personnel.selectedPersonnel.length === 1 ? "" : "s"
}}
</span>
</v-tooltip>
<v-tooltip
top
>
<
template
v-slot:activator=
"{ on, attrs }"
>
<v-btn
class=
"mr-3"
color=
"accent"
@
click=
"sendEmailToPersonnel"
:disabled=
"personnel.selectedPersonnel.length === 0"
:block=
"$vuetify.breakpoint.xs"
v-bind=
"attrs"
v-on=
"on"
>
<v-icon
class=
"mr-3"
>
mdi-email
</v-icon>
Email Selected
</v-btn>
</
template
>
<span>
Email Selected
</span>
</v-tooltip>
</div>
</div>
</div>
</div>
</template>
</template>
...
@@ -111,6 +193,7 @@ export default {
...
@@ -111,6 +193,7 @@ export default {
loading
:
true
,
loading
:
true
,
initialLoad
:
true
,
initialLoad
:
true
,
total
:
0
,
total
:
0
,
mobileBreakpoint
:
800
,
footerProps
:
DEFAULT_FOOTER_PROPS
,
footerProps
:
DEFAULT_FOOTER_PROPS
,
params
:
{
params
:
{
...
DEFAULT_PAGINATION_PARAMS
,
...
DEFAULT_PAGINATION_PARAMS
,
...
@@ -126,39 +209,49 @@ export default {
...
@@ -126,39 +209,49 @@ export default {
state
:
{
state
:
{
isLoading
:
true
,
isLoading
:
true
,
},
},
mobileHeaders
:
[
{
text
:
"
Name
"
,
value
:
"
name
"
,
width
:
"
200px
"
},
{
text
:
""
,
value
:
"
data-table-expand
"
,
align
:
"
end
"
},
],
personnel
:
{
personnel
:
{
userSelect
:
{
userSelect
:
{
isAddingBusy
:
false
,
isAddingBusy
:
false
,
isAddingPersonnel
:
false
,
isAddingPersonnel
:
false
,
personnelToAdd
:
[],
personnelToAdd
:
[],
},
},
mobileHeaders
:
[
{
text
:
"
Name
"
,
value
:
"
name
"
,
width
:
"
200px
"
},
{
text
:
""
,
value
:
"
data-table-expand
"
,
align
:
"
end
"
},
],
selectedPersonnel
:
[],
selectedPersonnel
:
[],
listItems
:
[],
listItems
:
[],
headers
:
[
headers
:
[
{
text
:
"
Team
"
,
value
:
"
teamName
"
,
width
:
"
200px
"
,
sortable
:
false
,
},
{
text
:
"
Name
"
,
value
:
"
name
"
,
width
:
"
200px
"
},
{
text
:
"
Name
"
,
value
:
"
name
"
,
width
:
"
200px
"
},
{
{
text
:
"
Role
"
,
text
:
"
Role
"
,
value
:
"
role
"
,
value
:
"
role
"
,
width
:
"
200px
"
,
width
:
"
200px
"
,
},
},
{
text
:
"
Team
"
,
value
:
"
teamName
"
,
width
:
"
200px
"
,
sortable
:
false
,
},
{
text
:
"
Email
"
,
value
:
"
email
"
,
width
:
"
200px
"
},
{
text
:
"
Mattermost
"
,
value
:
"
username
"
,
width
:
"
200px
"
},
{
{
text
:
"
Permission
"
,
text
:
"
Permission
"
,
value
:
"
permission
"
,
value
:
"
permission
"
,
width
:
"
200px
"
,
width
:
"
200px
"
,
},
},
{
text
:
"
Email
"
,
value
:
"
email
"
,
width
:
"
200px
"
},
{
text
:
"
Phone
"
,
value
:
"
phone
"
,
width
:
"
200px
"
},
],
],
},
},
permissionTypes
:
Permission
,
permissionTypes
:
Permission
,
displayUserPermissionDialog
:
false
,
displayUserPermissionDialog
:
false
,
permissionUserData
:
{},
permissionUserData
:
{},
expanded
:
[],
};
};
},
},
async
mounted
()
{
async
mounted
()
{
...
@@ -315,6 +408,28 @@ export default {
...
@@ -315,6 +408,28 @@ export default {
return
{
text
:
label
,
value
:
label
};
return
{
text
:
label
,
value
:
label
};
});
});
},
},
mobileTableKeys
()
{
return
this
.
personnel
.
headers
.
filter
((
item
)
=>
{
return
item
.
value
!==
"
name
"
;
})
.
map
((
filteredObject
)
=>
{
return
filteredObject
.
value
;
});
},
onMobile
()
{
return
this
.
$vuetify
.
breakpoint
.
width
<
this
.
mobileBreakpoint
;
},
header
()
{
return
this
.
onMobile
?
this
.
personnel
.
mobileHeaders
:
this
.
personnel
.
headers
;
},
},
},
};
};
</
script
>
</
script
>
<
style
lang=
"scss"
scoped
>
.v-select_selections
{
width
:
90%
;
}
</
style
>
src/components/Section.vue
View file @
062d988f
<
template
>
<
template
>
<div
class=
"section d-flex flex-column"
>
<div
class=
"section d-flex flex-column"
>
<div
class=
"section-header align-items-center w-100 d-flex pa-3"
>
<div
class=
"section-header align-items-center w-100 d-flex pa-3"
>
<slot
name=
"header"
></slot>
<h4
slot=
"header"
class=
"ma-0 pl-5 text-left"
>
{{
headerTitle
}}
</h4>
<slot
name=
"userinfo"
></slot>
<v-spacer
/>
<v-spacer
/>
<slot
name=
"header-right"
></slot>
<v-tooltip
top
v-if=
"sync"
>
<template
v-slot:activator=
"
{ on, attrs }">
<v-btn
icon
v-bind=
"attrs"
v-on=
"on"
@
click=
"refreshClick"
:loading=
"loading"
>
<v-icon>
mdi-refresh
</v-icon>
</v-btn>
</
template
>
<span>
Refresh
</span>
</v-tooltip>
<v-btn
<v-btn
v-if=
"!hideCollapse && !hideCollapseButton"
v-if=
"!hideCollapse && !hideCollapseButton"
icon
icon
...
@@ -27,6 +43,10 @@ export default {
...
@@ -27,6 +43,10 @@ export default {
hideCollapse
:
Boolean
,
hideCollapse
:
Boolean
,
hideCollapseButton
:
Boolean
,
hideCollapseButton
:
Boolean
,
collapsed
:
Boolean
,
collapsed
:
Boolean
,
sync
:
Boolean
,
loading
:
Boolean
,
refreshClick
:
Function
,
headerTitle
:
String
,
},
},
data
:
()
=>
({
data
:
()
=>
({
isCollapsed
:
false
,
isCollapsed
:
false
,
...
...
src/components/Training/TrainingAttendance.vue
View file @
062d988f
...
@@ -20,33 +20,45 @@
...
@@ -20,33 +20,45 @@
</v-col>
</v-col>
</v-row>
</v-row>
<v-data-table
<v-data-table
id=
"attendance-table"
v-if=
"headers"
v-if=
"headers"
v-model=
"selectedUsers"
v-model=
"selectedUsers"
:headers=
"header
s
"
:headers=
"header"
:items=
"listItems"
:items=
"listItems"
:search=
"search"
:search=
"search"
show-select
show-select
:show-expand=
"onMobile"
:expanded.sync=
"expanded"
:hide-default-header=
"onMobile"
mobile-breakpoint=
"800"
mobile-breakpoint=
"800"
:options.sync=
"options"
:options.sync=
"options"
:footer-props=
"footerProps"
:footer-props=
"footerProps"
:server-items-length=
"total"
:server-items-length=
"total"
:hide-default-footer=
"listItems.length === 0"
:hide-default-footer=
"listItems.length === 0"
expand-icon=
"mdi-chevron-right"
:loading=
"loading"
:loading=
"loading"
class=
"lb-mobile-enabled"
>
>
<template
v-slot:
[`
item.user.name
`
]=
"
{ item }">
<template
v-slot:
[`
item.user.name
`
]=
"
{ item }"
v-if="!onMobile"
>
<v-expansion-panels
accordion
flat
class=
"my-2"
>
<v-expansion-panels
accordion
flat
class=
"my-2"
>
<v-expansion-panel
style=
"background-color: transparent
"
>
<v-expansion-panel
class=
"expansion-background
"
>
<v-expansion-panel-header
style=
"min-height: 48px"
>
{{
<v-expansion-panel-header>
{{
item
.
user
.
name
item
.
user
.
name
}}
</v-expansion-panel-header>
}}
</v-expansion-panel-header>
<v-expansion-panel-content
class=
"text-left"
>
<v-expansion-panel-content
class=
"text-left"
>
<p
class=
"mb-2"
>
{{
item
.
user
.
personnelType
}}
</p>
<p
class=
"mb-2"
>
{{
item
.
user
.
personnelType
}}
</p>
<p
class=
"mb-2"
>
{{
item
.
user
.
organization
}}
</p>
<p
class=
"mb-2"
>
{{
item
.
user
.
organization
}}
</p>
<a
<a
:href=
"'mailto:' + item.user.email"
>
{{
item
.
user
.
email
}}
</a>
:href=
"'mailto:' + item.user.email"
<div>
style=
"color: #bdc931; font-size: 0.875rem"
<a
>
{{
item
.
user
.
email
}}
</a
:href=
"`https://chat.il2.dso.mil/platform-one/messages/@$
{item.user.username}`"
>
target="_blank"
rel="noopener noreferrer"
class="primary--text"
>
@
{{
item
.
user
.
username
}}
</a>
</div>
</v-expansion-panel-content>
</v-expansion-panel-content>
</v-expansion-panel>
</v-expansion-panel>
</v-expansion-panels>
</v-expansion-panels>
...
@@ -65,7 +77,7 @@
...
@@ -65,7 +77,7 @@
v-model=
"item[date].attended"
v-model=
"item[date].attended"
:key=
"date"
:key=
"date"
:ripple=
"false"
:ripple=
"false"
class=
"d-inline-block"
class=
"d-inline-block
pb-1
"
@
change=
"updateAttendance(item, date)"
@
change=
"updateAttendance(item, date)"
></v-checkbox>
></v-checkbox>
</
template
>
</
template
>
...
@@ -80,7 +92,7 @@
...
@@ -80,7 +92,7 @@
<v-checkbox
<v-checkbox
v-else
v-else
v-model=
"item.completed.value"
v-model=
"item.completed.value"
class=
"d-inline-block"
class=
"d-inline-block
pb-1
"
@
change=
"checkboxCompleted(item)"
@
change=
"checkboxCompleted(item)"
:ripple=
"false"
:ripple=
"false"
></v-checkbox>
></v-checkbox>
...
@@ -97,13 +109,100 @@
...
@@ -97,13 +109,100 @@
</v-tooltip>
</v-tooltip>
<span
v-bind:key=
"h.value"
v-else
>
{{ h.text }}
</span>
<span
v-bind:key=
"h.value"
v-else
>
{{ h.text }}
</span>
</template>
</template>
<
template
v-slot:expanded-item=
"{ item }"
v-if=
"onMobile"
>
<div
class=
"mb-7 mt-1"
>
<p
class=
"d-flex flex-wrap mx-0 pl-9 ml-9 mb-4 text-left"
v-if=
"item.user.personnelType"
>
{{
item
.
user
.
personnelType
}}
</p>
<p
class=
"d-flex flex-wrap mx-0 pl-9 ml-9 mb-4 text-left"
v-if=
"item.user.organization"
>
{{
item
.
user
.
organization
}}
</p>
<a
:href=
"'mailto:' + item.user.email"
target=
"_blank"
class=
"d-flex flex-wrap mx-0 pl-9 ml-9 mb-4 text-left"
v-if=
"item.user.email"
>
{{
item
.
user
.
email
}}
</a>
<a
:href=
"`https://chat.il2.dso.mil/platform-one/messages/@$
{item.user.username}`"
target="_blank"
rel="noopener noreferrer"
class="d-flex flex-wrap mx-0 pl-9 ml-9 mb-9 text-left primary--text"
v-if="item.user.username"
>
@
{{
item
.
user
.
username
}}
</a>
</div>
<v-container
v-for=
"(date, index) in courseDates"
:key=
"date"
class=
"d-flex flex-wrap ma-0 pa-0 pl-9 ml-9 pr-9"
>
<v-tooltip
top
v-if=
"dateIsValid(date)"
>
<template
v-slot:activator=
"
{ on }">
<span
v-on=
"on"
>
Day
{{
index
+
1
}}
</span>
<v-icon
class=
"ml-2"
v-if=
"isTodaysDate(date)"
>
mdi-calendar-today
</v-icon
>
</
template
>
<span>
{{ date }}
</span>
</v-tooltip>
<span
v-else
>
Day {{ index + 1 }}
</span>
<v-spacer
/>
<v-progress-circular
indeterminate
:key=
"date"
color=
"primary"
:size=
"20"
:width=
"3"
class=
"d-inline-block ma-0 pa-0 pr-9 mr-9"
v-if=
"item[date] && item[date].loading"
></v-progress-circular>
<v-checkbox
v-else
v-model=
"item[date].attended"
:key=
"date"
:ripple=
"false"
class=
"d-inline-block ma-0 pa-0 pr-9"
@
change=
"updateAttendance(item, date)"
></v-checkbox>
</v-container>
<v-container
class=
"d-flex flex-wrap ma-0 py-0 pl-9 ml-9 pr-9"
>
<p>
Completed
</p>
<v-spacer
/>
<v-progress-circular
indeterminate
color=
"primary"
class=
"d-inline-block ma-0 pa-0 pr-9 mr-9"
:size=
"20"
:width=
"3"
v-if=
"item.completed.loading"
></v-progress-circular>
<v-checkbox
v-else
v-model=
"item.completed.value"
class=
"d-inline-block ma-0 pa-0 pr-9"
@
change=
"checkboxCompleted(item)"
:ripple=
"false"
></v-checkbox>
</v-container>
</template>
</v-data-table>
</v-data-table>
<div
class=
"d-flex flex-wrap mt-5"
>
<div
class=
"d-flex flex-wrap mt-5"
>
<v-btn
<v-btn
@
click=
"removeSelectedStudent"
@
click=
"removeSelectedStudent"
:disabled=
"selectedUsers.length === 0"
:disabled=
"selectedUsers.length === 0"
class=
"mr-3"
class=
"mr-3
mb-3
"
color=
"accent"
color=
"accent"
:block=
"$vuetify.breakpoint.xs"
>
>
<v-icon
class=
"mr-2"
>
mdi-trash-can
</v-icon>
<v-icon
class=
"mr-2"
>
mdi-trash-can
</v-icon>
Remove Selected
Remove Selected
...
@@ -113,6 +212,7 @@
...
@@ -113,6 +212,7 @@
:disabled=
"selectedUsers.length === 0"
:disabled=
"selectedUsers.length === 0"
class=
"mr-3"
class=
"mr-3"
color=
"accent"
color=
"accent"
:block=
"$vuetify.breakpoint.xs"
>
>
<v-icon
class=
"mr-2"
>
mdi-pencil
</v-icon>
<v-icon
class=
"mr-2"
>
mdi-pencil
</v-icon>
Email Selected
Email Selected
...
@@ -152,7 +252,6 @@ export default {
...
@@ -152,7 +252,6 @@ export default {
data
()
{
data
()
{
return
{
return
{
search
:
""
,
search
:
""
,
darkMode
:
null
,
inputRules
,
inputRules
,
loading
:
false
,
loading
:
false
,
total
:
0
,
total
:
0
,
...
@@ -168,12 +267,17 @@ export default {
...
@@ -168,12 +267,17 @@ export default {
},
},
studentToAdd
:
[],
studentToAdd
:
[],
listItems
:
[],
listItems
:
[],
expanded
:
[],
selectedUsers
:
[],
selectedUsers
:
[],
mobileBreakpoint
:
800
,
todaysDate
:
moment
().
format
(
"
YYYY-MM-DD
"
),
todaysDate
:
moment
().
format
(
"
YYYY-MM-DD
"
),
mobileHeader
:
[
{
text
:
"
Name
"
,
value
:
"
user.name
"
,
width
:
"
200px
"
,
sortable
:
false
},
{
value
:
"
data-table-expand
"
,
width
:
"
20px
"
,
align
:
"
right
"
},
],
};
};
},
},
async
mounted
()
{
async
mounted
()
{
this
.
darkMode
=
this
.
$vuetify
.
theme
.
dark
;
await
this
.
fetchData
();
await
this
.
fetchData
();
},
},
methods
:
{
methods
:
{
...
@@ -330,6 +434,12 @@ export default {
...
@@ -330,6 +434,12 @@ export default {
},
},
},
},
computed
:
{
computed
:
{
onMobile
()
{
return
this
.
$vuetify
.
breakpoint
.
width
<
this
.
mobileBreakpoint
;
},
header
()
{
return
this
.
onMobile
?
this
.
mobileHeader
:
this
.
headers
;
},
currentCourseId
()
{
currentCourseId
()
{
return
this
.
trainingCourse
.
id
;
return
this
.
trainingCourse
.
id
;
},
},
...
@@ -386,8 +496,18 @@ export default {
...
@@ -386,8 +496,18 @@ export default {
},
},
};
};
</
script
>
</
script
>
<
style
lang=
"scss"
>
<
style
lang=
"scss"
scoped
>
.search
{
.search
{
width
:
250px
;
width
:
250px
;
}
}
td
:nth-child
(
2
)
.expansion-background
{
background-color
:
transparent
;
}
#attendance-table
{
::v-deep
.v-data-table__wrapper
{
table
>
tbody
>
tr
:nth-child
(
n
)
>
td
:nth-child
(
2
)
>
div
>
div
{
background-color
:
transparent
;
}
}
}
</
style
>
</
style
>
src/components/UserBanner.vue
View file @
062d988f
<
template
>
<
template
>
<Section
class=
"user-banner"
hideCollapseButton
>
<Section
<v-container
slot=
"header"
class=
"pa-0"
fluid
>
class=
"user-banner"
:hideCollapseButton=
"$vuetify.breakpoint.smAndDown"
:hideCollapse=
"$vuetify.breakpoint.mdAndUp"
>
<v-container
slot=
"userinfo"
class=
"pa-0"
fluid
>
<v-skeleton-loader
<v-skeleton-loader
:loading=
"loading"
:loading=
"loading"
height=
"48"
height=
"48"
type=
"list-item-avatar"
type=
"list-item-avatar"
class=
"w-100"
class=
"w-100"
>
>
<div
class=
"d-flex banner-row subhead"
>
<div
class=
"d-flex banner-row subhead
section-header
"
>
<div
<div
class=
"
class=
"
d-flex
d-flex
...
@@ -53,7 +57,7 @@
...
@@ -53,7 +57,7 @@
fluid
fluid
v-if=
"$vuetify.breakpoint.smAndDown"
v-if=
"$vuetify.breakpoint.smAndDown"
>
>
<div
class=
"d-flex banner-row subhead"
>
<div
class=
"d-flex banner-row subhead
section-content
"
>
<div
<div
class=
"
class=
"
d-flex
d-flex
...
@@ -96,6 +100,14 @@ export default {
...
@@ -96,6 +100,14 @@ export default {
</
script
>
</
script
>
<
style
lang=
"scss"
scoped
>
<
style
lang=
"scss"
scoped
>
.theme--light
{
.section-header
{
color
:
white
!
important
;
}
.section-content
{
color
:
black
!
important
;
}
}
.user-banner
{
.user-banner
{
border-radius
:
$border-radius-root
;
border-radius
:
$border-radius-root
;
...
@@ -108,9 +120,6 @@ export default {
...
@@ -108,9 +120,6 @@ export default {
background
:
none
!
important
;
background
:
none
!
important
;
}
}
}
}
&
::v-deep
.subhead
{
color
:
map-get
(
$material-dark
,
"text-color"
)
!
important
;
}
.banner-row
>
*
{
.banner-row
>
*
{
border-right
:
1px
solid
$p1-light-green
;
border-right
:
1px
solid
$p1-light-green
;
...
...
src/components/UserSelect.vue
View file @
062d988f
...
@@ -125,17 +125,13 @@ export default {
...
@@ -125,17 +125,13 @@ export default {
},
},
created
()
{
created
()
{
if
(
this
.
value
)
{
if
(
this
.
value
)
{
this
.
values
=
this
.
value
;
this
.
setValues
(
this
.
value
);
this
.
model
=
this
.
value
;
this
.
items
=
this
.
value
;
}
}
},
},
watch
:
{
watch
:
{
value
()
{
value
(
val
)
{
if
(
this
.
value
)
{
if
(
val
)
{
this
.
values
=
this
.
value
;
this
.
setValues
(
val
);
this
.
model
=
this
.
value
;
this
.
items
=
this
.
value
;
}
}
},
},
search
(
val
)
{
search
(
val
)
{
...
@@ -145,6 +141,11 @@ export default {
...
@@ -145,6 +141,11 @@ export default {
},
},
},
},
methods
:
{
methods
:
{
setValues
(
val
)
{
this
.
values
=
val
;
this
.
model
=
val
;
this
.
items
=
val
;
},
compareSelectedUsers
(
userA
,
userB
)
{
compareSelectedUsers
(
userA
,
userB
)
{
if
(
!
userB
||
!
userA
)
return
false
;
if
(
!
userB
||
!
userA
)
return
false
;
return
userA
===
userB
;
return
userA
===
userB
;
...
...
src/components/WelcomeSummary.vue
View file @
062d988f
<
template
>
<
template
>
<v-container
class=
"welcome-summary px-8
pb-8
"
>
<v-container
class=
"welcome-summary px-8"
>
<div
v-if=
"error"
>
<div
v-if=
"error"
>
<ErrorMessage
v-bind:errorMessage=
"errorMessage"
/>
<ErrorMessage
v-bind:errorMessage=
"errorMessage"
/>
</div>
</div>
...
@@ -17,6 +17,7 @@
...
@@ -17,6 +17,7 @@
class=
"v-input--reverse v-input--expand"
class=
"v-input--reverse v-input--expand"
v-model=
"welcomeSwitch"
v-model=
"welcomeSwitch"
@
change=
"updateUserPreferences()"
@
change=
"updateUserPreferences()"
hide-details
>
>
<template
#label
>
Turn off Welcome Message
</
template
>
<template
#label
>
Turn off Welcome Message
</
template
>
</v-switch>
</v-switch>
...
...
src/main.js
View file @
062d988f
...
@@ -30,7 +30,7 @@ const startLaunchboard = async () => {
...
@@ -30,7 +30,7 @@ const startLaunchboard = async () => {
// store state from results
// store state from results
if
(
!
user
||
!
preferences
)
{
if
(
!
user
||
!
preferences
)
{
throw
"
Unexpected user data from API
"
;
throw
new
Error
(
"
Unexpected user data from API
"
)
;
}
}
store
.
commit
(
SET_USER
,
user
);
store
.
commit
(
SET_USER
,
user
);
...
...
src/scss/custom.scss
View file @
062d988f
...
@@ -176,6 +176,56 @@ body {
...
@@ -176,6 +176,56 @@ body {
margin-bottom
:
0
.5rem
;
margin-bottom
:
0
.5rem
;
}
}
}
}
// custom styles for tables in mobile view
.lb-mobile-enabled
{
&
.lb-mobile-no-checkbox
{
::v-deep
.v-data-table__wrapper
{
.v-data-table__mobile-table-row
{
>
.v-data-table__mobile-row
{
&
:nth-child
(
1
)
{
width
:
100%
!
important
;
padding-left
:
38px
;
}
&
:nth-child
(
2
)
{
width
:
unset
!
important
;
}
}
}
}
}
::v-deep
.v-data-table__wrapper
{
.v-data-table__mobile-table-row
{
display
:
flex
;
>
.v-data-table__mobile-row
{
&
:nth-child
(
2
)
{
width
:
100%
;
}
.v-data-table__mobile-row__cell
{
text-align
:
left
;
// point down on expansion
.v-data-table__expand-icon--active
{
transform
:
rotate
(
90deg
);
}
}
}
}
}
tr
.v-data-table__expanded__content
{
background
:
#010e19
;
color
:
#ffffff
;
td
{
background-color
:
transparent
!
important
;
}
a
{
&
:not
(
.primary--text
)
{
color
:
#ffffff
!
important
;
}
&
.primary--text
{
color
:
var
(
--
v-primary-base
)
!
important
;
}
}
}
}
.flex-card
{
.flex-card
{
display
:
flex
;
display
:
flex
;
...
@@ -196,7 +246,7 @@ body {
...
@@ -196,7 +246,7 @@ body {
font-size
:
12px
;
font-size
:
12px
;
}
}
.v-input__control
{
.v-input__control
{
padding-right
:
8
px
;
padding-right
:
0
px
;
}
}
}
}
...
@@ -224,6 +274,13 @@ body {
...
@@ -224,6 +274,13 @@ body {
}
}
.theme--dark
{
.theme--dark
{
.link-column
a
{
color
:
$p1-light-green
!
important
;
&
:hover
{
color
:
lighten
(
$p1-light-green
,
10%
)
!
important
;
}
}
a
.v-btn.secondary
{
a
.v-btn.secondary
{
span
{
span
{
color
:
black
;
color
:
black
;
...
@@ -337,6 +394,13 @@ body {
...
@@ -337,6 +394,13 @@ body {
}
}
}
}
.theme--light
{
.theme--light
{
.link-column
a
{
color
:
$p1-light-blue
!
important
;
&
:hover
{
color
:
lighten
(
$p1-light-blue
,
10%
)
!
important
;
}
}
a
.v-btn.secondary
{
a
.v-btn.secondary
{
span
{
span
{
color
:
white
;
color
:
white
;
...
@@ -351,10 +415,6 @@ body {
...
@@ -351,10 +415,6 @@ body {
text-decoration
:
underline
;
text-decoration
:
underline
;
}
}
.v-application
a
{
color
:
#021421
!
important
;
}
h1
,
h1
,
h2
,
h2
,
h3
,
h3
,
...
@@ -385,6 +445,7 @@ body {
...
@@ -385,6 +445,7 @@ body {
}
}
}
}
}
}
.v-data-table-header
th
{
.v-data-table-header
th
{
color
:
#000000
!
important
;
color
:
#000000
!
important
;
background-color
:
$table-row-even-bg-light
;
background-color
:
$table-row-even-bg-light
;
...
@@ -517,9 +578,24 @@ body {
...
@@ -517,9 +578,24 @@ body {
.v-data-table__wrapper
{
.v-data-table__wrapper
{
.v-select
{
.v-select
{
font-size
:
0
.875rem
;
font-size
:
14px
;
padding-top
:
5px
;
padding-top
:
5px
;
}
}
td
{
a
{
&
.primary--text
{
color
:
var
(
--
v-primary-base
)
!
important
;
}
}
}
}
}
@media
(
max-width
:
800px
)
{
.v-data-table__wrapper
{
tbody
{
display
:
flex
;
flex-direction
:
column
!
important
;
}
}
}
}
}
...
@@ -562,3 +638,8 @@ body {
...
@@ -562,3 +638,8 @@ body {
padding-top
:
0
!
important
;
padding-top
:
0
!
important
;
margin-bottom
:
0
!
important
;
margin-bottom
:
0
!
important
;
}
}
.
mdi-menu-down
:
:
before
{
content
:
"\F035D"
;
color
:
rgba
(
0
,
0
,
0
,
0
.5
);
}
src/scss/mixins/breakpoints.scss
View file @
062d988f
...
@@ -6,66 +6,77 @@ $xl-breakpoint: 1904px;
...
@@ -6,66 +6,77 @@ $xl-breakpoint: 1904px;
// Extra small devices: on screens that are 599px wide or less
// Extra small devices: on screens that are 599px wide or less
@mixin
xs
{
@mixin
xs
{
@media
only
screen
and
(
max-width
:
$sm-breakpoint
-
1
)
@media
only
screen
and
(
max-width
:
$sm-breakpoint
-
1
)
{
{
@content
;}
@content
;
}
}
}
// Small devices: when the width is between 600px and 959px
// Small devices: when the width is between 600px and 959px
@mixin
sm
{
@mixin
sm
{
@media
screen
and
(
min-width
:
$sm-breakpoint
)
and
(
max-width
:
$md-breakpoint
-
1
)
@media
screen
and
(
min-width
:
$sm-breakpoint
)
and
(
max-width
:
$md-breakpoint
-
1
)
{
{
@content
;}
@content
;
}
}
}
// Medium devices: when the width is between 960px and 1263px
// Medium devices: when the width is between 960px and 1263px
@mixin
md
{
@mixin
md
{
@media
screen
and
(
min-width
:
$md-breakpoint
)
and
(
max-width
:
$lg-breakpoint
-
1
)
@media
screen
and
(
min-width
:
$md-breakpoint
)
and
(
max-width
:
$lg-breakpoint
-
1
)
{
{
@content
;}
@content
;
}
}
}
// Large devices: when the width is between 1264px and 1903px
// Large devices: when the width is between 1264px and 1903px
@mixin
lg
{
@mixin
lg
{
@media
only
screen
and
(
min-width
:
$lg-breakpoint
)
and
(
max-width
:
$xl-breakpoint
-
1
)
@media
only
screen
and
(
min-width
:
$lg-breakpoint
)
and
(
max-width
:
$xl-breakpoint
-
1
)
{
{
@content
;}
@content
;
}
}
}
// Extra large devices: on screens that are 1904px wide or above
// Extra large devices: on screens that are 1904px wide or above
@mixin
xl
{
@mixin
xl
{
@media
only
screen
and
(
min-width
:
$xl-breakpoint
)
@media
only
screen
and
(
min-width
:
$xl-breakpoint
)
{
{
@content
;}
@content
;
}
}
}
// On screens that are 600px wide or above
// On screens that are 600px wide or above
@mixin
sm-up
{
@mixin
sm-up
{
@media
only
screen
and
(
min-width
:
$sm-breakpoint
)
@media
only
screen
and
(
min-width
:
$sm-breakpoint
)
{
{
@content
;}
@content
;
}
}
}
// On screens that are 960px wide or above
// On screens that are 960px wide or above
@mixin
md-up
{
@mixin
md-up
{
@media
only
screen
and
(
min-width
:
$md-breakpoint
)
@media
only
screen
and
(
min-width
:
$md-breakpoint
)
{
{
@content
;}
@content
;
}
}
}
// On screens that are 1264px wide or above
// On screens that are 1264px wide or above
@mixin
lg-up
{
@mixin
lg-up
{
@media
only
screen
and
(
min-width
:
$lg-breakpoint
)
@media
only
screen
and
(
min-width
:
$lg-breakpoint
)
{
{
@content
;}
@content
;
}
}
}
// On screens that are 959px wide or below
// On screens that are 959px wide or below
@mixin
sm-down
{
@mixin
sm-down
{
@media
only
screen
and
(
max-width
:
$md-breakpoint
-
1
)
@media
only
screen
and
(
max-width
:
$md-breakpoint
-
1
)
{
{
@content
;}
@content
;
}
}
}
// On screens that are 1363px wide or above
// On screens that are 1363px wide or above
@mixin
md-down
{
@mixin
md-down
{
@media
only
screen
and
(
max-width
:
$lg-breakpoint
-
1
)
@media
only
screen
and
(
max-width
:
$lg-breakpoint
-
1
)
{
{
@content
;}
@content
;
}
}
}
// On screens that are 1903px wide or above
// On screens that are 1903px wide or above
@mixin
lg-down
{
@mixin
lg-down
{
@media
only
screen
and
(
max-width
:
$xl-breakpoint
-
1
)
@media
only
screen
and
(
max-width
:
$xl-breakpoint
-
1
)
{
{
@content
;}
@content
;
}
}
\ No newline at end of file
}
src/scss/variables.scss
View file @
062d988f
...
@@ -6,6 +6,7 @@ $dark-bg-color: #002743;
...
@@ -6,6 +6,7 @@ $dark-bg-color: #002743;
$gradient-alpha
:
#000000
00
;
$gradient-alpha
:
#000000
00
;
$p1-light-green
:
#bdc931
;
$p1-light-green
:
#bdc931
;
$p1-light-blue
:
#12b5f6
;
$p1-dark-green
:
#8b9638
;
$p1-dark-green
:
#8b9638
;
$p1-orange
:
#f36421
;
$p1-orange
:
#f36421
;
$p1-yellow
:
#f7be16
;
$p1-yellow
:
#f7be16
;
...
@@ -35,6 +36,12 @@ $btn-sizes: (
...
@@ -35,6 +36,12 @@ $btn-sizes: (
"large"
:
70
,
"large"
:
70
,
);
);
// vuetify data table customizations
// https://github.com/vuetifyjs/vuetify/blob/master/packages/vuetify/src/components/VDataTable/_variables.scss
$data-table-regular-header-font-size
:
14px
;
$data-table-regular-row-font-size
:
14px
;
$data-table-expanded-content-box-shadow
:
none
;
$btn-font-sizes
:
(
$btn-font-sizes
:
(
"x-small"
:
10px
,
"x-small"
:
10px
,
"small"
:
12px
,
"small"
:
12px
,
...
...
src/store/getter-types.js
View file @
062d988f
...
@@ -5,3 +5,6 @@ export const GET_INITIAL_LOADING_SUCCESS = "initialLoading/getLoading";
...
@@ -5,3 +5,6 @@ export const GET_INITIAL_LOADING_SUCCESS = "initialLoading/getLoading";
export
const
GET_USER_PREFERENCE
=
"
user_preferences/getUserPreferences
"
;
export
const
GET_USER_PREFERENCE
=
"
user_preferences/getUserPreferences
"
;
export
const
GET_ERROR_MESSAGE
=
"
error/getErrorMessage
"
;
export
const
GET_ERROR_MESSAGE
=
"
error/getErrorMessage
"
;
export
const
GET_TUTORIAL
=
"
tutorial/getTutorial
"
;
export
const
GET_TUTORIAL
=
"
tutorial/getTutorial
"
;
export
const
GET_PAST_METRIC_STATE
=
"
loading/getPastMetricState
"
;
export
const
GET_PRESENT_METRIC_STATE
=
"
loading/getPresentMetricState
"
;
export
const
GET_FUTURE_METRIC_STATE
=
"
loading/getFutureMetricState
"
;
src/store/index.js
View file @
062d988f
...
@@ -5,6 +5,7 @@ import user from "@/store/modules/user";
...
@@ -5,6 +5,7 @@ import user from "@/store/modules/user";
import
initialLoad
from
"
@/store/modules/initial-load
"
;
import
initialLoad
from
"
@/store/modules/initial-load
"
;
import
userPreferences
from
"
@/store/modules/userPreferences.js
"
;
import
userPreferences
from
"
@/store/modules/userPreferences.js
"
;
import
error
from
"
@/store/modules/error.js
"
;
import
error
from
"
@/store/modules/error.js
"
;
import
loading
from
"
@/store/modules/loading.js
"
;
import
tutorial
from
"
@/store/modules/tutorial.js
"
;
import
tutorial
from
"
@/store/modules/tutorial.js
"
;
Vue
.
use
(
Vuex
);
Vue
.
use
(
Vuex
);
...
@@ -19,6 +20,7 @@ export default new Vuex.Store({
...
@@ -19,6 +20,7 @@ export default new Vuex.Store({
userPreferences
,
userPreferences
,
error
,
error
,
tutorial
,
tutorial
,
loading
,
},
},
strict
:
debug
,
strict
:
debug
,
});
});
src/store/modules/loading.js
0 → 100644
View file @
062d988f
import
{
SET_COURSE_LOADING_STATE
,
SET_PAST_METRICS_STATE
,
SET_PRESENT_METRICS_STATE
,
SET_FUTURE_METRICS_STATE
,
}
from
"
@/store/mutation-types
"
;
import
{
GET_PAST_METRIC_STATE
,
GET_PRESENT_METRIC_STATE
,
GET_FUTURE_METRIC_STATE
,
}
from
"
@/store/getter-types
"
;
// initial state
const
state
=
()
=>
({
courseState
:
false
,
pastMetricState
:
false
,
presentMetricState
:
false
,
futureMetricState
:
false
,
});
// getters
const
getters
=
{
[
GET_PAST_METRIC_STATE
]:
(
_state
)
=>
_state
.
pastMetricState
,
[
GET_PRESENT_METRIC_STATE
]:
(
_state
)
=>
_state
.
presentMetricState
,
[
GET_FUTURE_METRIC_STATE
]:
(
_state
)
=>
_state
.
futureMetricState
,
};
// mutations
const
mutations
=
{
[
SET_COURSE_LOADING_STATE
](
_state
,
newState
)
{
_state
.
courseState
=
newState
;
},
[
SET_PAST_METRICS_STATE
](
_state
,
newState
)
{
_state
.
pastMetricState
=
newState
;
},
[
SET_PRESENT_METRICS_STATE
](
_state
,
newState
)
{
_state
.
presentMetricState
=
newState
;
},
[
SET_FUTURE_METRICS_STATE
](
_state
,
newState
)
{
_state
.
futureMetricState
=
newState
;
},
};
export
default
{
state
,
getters
,
mutations
,
};
src/store/mutation-types.js
View file @
062d988f
...
@@ -7,3 +7,7 @@ export const SET_ERROR_MESSAGE = "error/setErrorMessage";
...
@@ -7,3 +7,7 @@ export const SET_ERROR_MESSAGE = "error/setErrorMessage";
export
const
SET_ERROR_DIALOG
=
"
error/setErrorDialog
"
;
export
const
SET_ERROR_DIALOG
=
"
error/setErrorDialog
"
;
export
const
SET_ACTIVE_TUTORIAL
=
"
tutorial/setActiveTutorial
"
;
export
const
SET_ACTIVE_TUTORIAL
=
"
tutorial/setActiveTutorial
"
;
export
const
SET_TOOLTIP_NAME
=
"
tutorial/setTooltipName
"
;
export
const
SET_TOOLTIP_NAME
=
"
tutorial/setTooltipName
"
;
export
const
SET_COURSE_LOADING_STATE
=
"
loading/setCourseState
"
;
export
const
SET_PAST_METRICS_STATE
=
"
loading/setPastMetricState
"
;
export
const
SET_PRESENT_METRICS_STATE
=
"
loading/setPresentMetricState
"
;
export
const
SET_FUTURE_METRICS_STATE
=
"
loading/setFutureMetricState
"
;
src/views/Projects.vue
View file @
062d988f
...
@@ -3,24 +3,14 @@
...
@@ -3,24 +3,14 @@
<v-container
fluid
class=
"px-2 px-md-4 px-lg-8 px-xl-16 pb-0"
>
<v-container
fluid
class=
"px-2 px-md-4 px-lg-8 px-xl-16 pb-0"
>
<v-row>
<v-row>
<v-col
cols=
"12"
class=
"d-flex flex-column pb-0 pb-lg-3"
>
<v-col
cols=
"12"
class=
"d-flex flex-column pb-0 pb-lg-3"
>
<Section
class=
"mt-6"
hideCollapseButton
>
<Section
<h4
slot=
"header"
class=
"ma-0 pl-5 text-left"
>
My Projects
</h4>
class=
"mt-6"
<span
slot=
"header-right"
>
hideCollapseButton
<v-tooltip
top
>
headerTitle=
"My Projects"
<template
v-slot:activator=
"
{ on, attrs }">
sync
<v-btn
:loading=
"loadingProjectData"
icon
:refreshClick=
"refreshProjectData"
v-bind=
"attrs"
>
v-on=
"on"
@
click=
"refreshProjectData"
:loading=
"loadingProjectData"
>
<v-icon>
mdi-refresh
</v-icon>
</v-btn>
</
template
>
<span>
Refresh
</span>
</v-tooltip>
</span>
<ProjectsSummary
<ProjectsSummary
slot=
"content"
slot=
"content"
ref=
"projectSummary"
ref=
"projectSummary"
...
...
src/views/Resources.vue
View file @
062d988f
...
@@ -3,19 +3,19 @@
...
@@ -3,19 +3,19 @@
<v-container
class=
"px-2 px-md-4 px-lg-8 px-xl-16 pb-0"
>
<v-container
class=
"px-2 px-md-4 px-lg-8 px-xl-16 pb-0"
>
<v-row
class=
"justify-content-left"
>
<v-row
class=
"justify-content-left"
>
<v-col
cols=
"12"
lg=
"4"
md=
"6"
sm=
"12"
class=
"py-0 pt-lg-3"
>
<v-col
cols=
"12"
lg=
"4"
md=
"6"
sm=
"12"
class=
"py-0 pt-lg-3"
>
<
Section
hideCollapseButton
>
<
v-row
class=
"mb-2 mt-1"
>
<h
4
slot=
"header"
class=
"mt-4 w-100 text-left"
>
<h
2
class=
"text-lg-left d-inline"
>
INFORMATION
</h2
>
ABMS ADCE Party Bus Survival Guide
</v-row>
</h4
>
<Section
hideCollapseButton
headerTitle=
"Grogu's Guide to DevSecOps"
>
<div
slot=
"content"
>
<div
slot=
"content"
>
<div
class=
"mx-12"
>
<div
class=
"mx-12"
>
Background information about Platform One, Agile
&
UCD
Background information about Platform One, Agile
&
amp;
UCD
philosophies, what the team does, security practices and
philosophies, what the team does, security practices and
processes, etc.
processes, etc.
</div>
</div>
<v-btn
<v-btn
color=
"secondary"
color=
"secondary"
href=
"
./static/docs/(U) Platform One_Party Bus Survival Guide (27 August 2020).pdf
"
href=
"
https://confluence.il2.dso.mil/display/GROGU/7.3+A+Stylized+Exportable+PDF
"
target=
"_blank"
target=
"_blank"
class=
"mx-auto no-link my-4"
class=
"mx-auto no-link my-4"
>
DOWNLOAD GUIDE
>
DOWNLOAD GUIDE
...
@@ -25,12 +25,11 @@
...
@@ -25,12 +25,11 @@
</v-col>
</v-col>
</v-row>
</v-row>
<v-row>
<v-row>
<h2
class=
"subhead"
>
LINKS
</h2>
<h2>
LINKS
</h2>
</v-row>
</v-row>
<v-row>
<v-row>
<v-col>
<v-col>
<Section
hide-collapse-button
>
<Section
hide-collapse-button
headerTitle=
"Training Resources"
>
<h4
slot=
"header"
class=
"text-left"
>
Training Resources
</h4>
<div
slot=
"content"
class=
"d-flex flex-column"
>
<div
slot=
"content"
class=
"d-flex flex-column"
>
<div
class=
"text-left pl-10 link-column"
>
<div
class=
"text-left pl-10 link-column"
>
<a
<a
...
@@ -93,12 +92,11 @@
...
@@ -93,12 +92,11 @@
</v-col>
</v-col>
</v-row>
</v-row>
<v-row>
<v-row>
<h2
class=
"subhead"
>
VIDEOS
</h2>
<h2>
VIDEOS
</h2>
</v-row>
</v-row>
<v-row>
<v-row>
<v-col>
<v-col>
<Section
hide-collapse-button
>
<Section
hide-collapse-button
headerTitle=
"Self-Learning Videos"
>
<h4
slot=
"header"
class=
"text-left"
>
Self-Learning Videos
</h4>
<div
slot=
"content"
class=
"d-flex flex-column"
>
<div
slot=
"content"
class=
"d-flex flex-column"
>
<div
class=
"text-left pl-10 link-column"
>
<div
class=
"text-left pl-10 link-column"
>
Kubernetes -
Kubernetes -
...
@@ -217,12 +215,6 @@ export default {
...
@@ -217,12 +215,6 @@ export default {
}
}
}
}
}
}
.link-column
a
{
color
:
#12b5f6
!
important
;
&
:hover
{
color
:
#12b5f6
!
important
;
}
}
}
}
.theme--dark
{
.theme--dark
{
.section
{
.section
{
...
@@ -238,11 +230,5 @@ export default {
...
@@ -238,11 +230,5 @@ export default {
}
}
}
}
}
}
.link-column
a
{
color
:
#12b5f6
!
important
;
&
:hover
{
color
:
lighten
(
#12b5f6
,
10%
)
!
important
;
}
}
}
}
</
style
>
</
style
>
src/views/Settings.vue
View file @
062d988f
...
@@ -3,10 +3,7 @@
...
@@ -3,10 +3,7 @@
<v-container
fluid
class=
"px-2 px-md-4 px-lg-8 px-xl-16 pb-0"
>
<v-container
fluid
class=
"px-2 px-md-4 px-lg-8 px-xl-16 pb-0"
>
<v-row>
<v-row>
<v-col
cols=
"12"
lg=
"6"
class=
"d-flex flex-column pb-0 pb-lg-3"
>
<v-col
cols=
"12"
lg=
"6"
class=
"d-flex flex-column pb-0 pb-lg-3"
>
<Section>
<Section
headerTitle=
"Launchboard Settings"
>
<h4
slot=
"header"
class=
"ma-0 pl-5 text-left"
>
Launchboard Settings
</h4>
<LaunchboardSettings
slot=
"content"
/>
<LaunchboardSettings
slot=
"content"
/>
</Section>
</Section>
</v-col>
</v-col>
...
...
src/views/Team.vue
View file @
062d988f
...
@@ -36,7 +36,16 @@
...
@@ -36,7 +36,16 @@
/>
/>
<div
<div
id=
"team-table-header"
id=
"team-table-header"
class=
"d-flex flex-column justify-center align-top flex-sm-row mb-6 mt-4 pl-2"
class=
"
d-flex
flex-column
justify-center
align-top
flex-sm-row
mb-6
mt-4
pl-2
"
v-if=
"permission"
v-if=
"permission"
>
>
<div>
<div>
...
@@ -51,7 +60,7 @@
...
@@ -51,7 +60,7 @@
<v-btn
<v-btn
color=
"tertiary"
color=
"tertiary"
min-width=
"200"
min-width=
"200"
:disabled=
"
availableSlots === 0 ||
state.isUndoingDelete"
:disabled=
"state.isUndoingDelete"
@
click=
"state.isAdding = true"
@
click=
"state.isAdding = true"
>
>
Add Team Member
Add Team Member
...
@@ -179,6 +188,7 @@
...
@@ -179,6 +188,7 @@
<
template
v-slot:
[`
item.username
`
]=
"{ item }"
>
<
template
v-slot:
[`
item.username
`
]=
"{ item }"
>
<a
<a
target=
"_blank"
target=
"_blank"
rel=
"noopener"
:href=
"`https://chat.il2.dso.mil/platform-one/messages/@$
{item.username}`"
:href=
"`https://chat.il2.dso.mil/platform-one/messages/@$
{item.username}`"
>
>
@
{{
item
.
username
}}
@
{{
item
.
username
}}
...
@@ -256,15 +266,10 @@
...
@@ -256,15 +266,10 @@
top
top
v-if=
"membersToAdd"
v-if=
"membersToAdd"
v-model=
"snackbars.add"
v-model=
"snackbars.add"
:timeout=
"
5000
"
:timeout=
"
snackbars.timeout
"
>
>
<div
class=
"text-center"
>
<div
class=
"text-center"
>
<div
v-if=
"membersToAdd.length === 1"
>
{{ state.addMemberMessage }}
{{ membersToAdd[0].name }} added to {{ team.name }}
</div>
<div
v-else
>
{{ membersToAdd.length }} members added to {{ team.name }}
</div>
</div>
</div>
</v-snackbar>
</v-snackbar>
</v-skeleton-loader>
</v-skeleton-loader>
...
@@ -306,6 +311,7 @@ export default {
...
@@ -306,6 +311,7 @@ export default {
timeout
:
defaultSnackbarTimeout
,
timeout
:
defaultSnackbarTimeout
,
},
},
state
:
{
state
:
{
addMemberMessage
:
null
,
// edit team details flags
// edit team details flags
showEditDialog
:
false
,
showEditDialog
:
false
,
isEditingTeamDetailsBusy
:
false
,
isEditingTeamDetailsBusy
:
false
,
...
@@ -435,6 +441,11 @@ export default {
...
@@ -435,6 +441,11 @@ export default {
this
.
$route
.
params
.
teamId
this
.
$route
.
params
.
teamId
);
);
this
.
snackbars
.
add
=
true
;
this
.
snackbars
.
add
=
true
;
if
(
this
.
membersToAdd
.
length
===
1
)
{
this
.
state
.
addMemberMessage
=
`
${
this
.
membersToAdd
[
0
].
name
}
added to
${
this
.
team
.
name
}
`
;
}
else
{
this
.
state
.
addMemberMessage
=
`
${
this
.
membersToAdd
.
length
}
added to
${
this
.
team
.
name
}
`
;
}
}
catch
(
e
)
{
}
catch
(
e
)
{
this
.
$store
.
commit
(
SET_ERROR_MESSAGE
,
e
);
this
.
$store
.
commit
(
SET_ERROR_MESSAGE
,
e
);
this
.
$store
.
commit
(
SET_ERROR_DIALOG
,
true
);
this
.
$store
.
commit
(
SET_ERROR_DIALOG
,
true
);
...
@@ -513,12 +524,6 @@ export default {
...
@@ -513,12 +524,6 @@ export default {
// TODO: pull this from user info
// TODO: pull this from user info
return
true
;
return
true
;
},
},
availableSlots
()
{
if
(
this
.
team
&&
this
.
team
.
members
)
{
return
this
.
team
.
capacity
-
this
.
team
.
members
.
length
;
}
return
-
1
;
},
emailSelectedHref
()
{
emailSelectedHref
()
{
if
(
this
.
selectedMembers
.
length
===
0
)
{
if
(
this
.
selectedMembers
.
length
===
0
)
{
return
null
;
return
null
;
...
...
src/views/TrainingDetails.vue
View file @
062d988f
...
@@ -6,13 +6,13 @@
...
@@ -6,13 +6,13 @@
type=
"table-heading,table-thead, table-tbody"
type=
"table-heading,table-thead, table-tbody"
class=
"py-2"
class=
"py-2"
>
>
<v-row
class=
"mb-
4
"
>
<v-row
class=
"mb-
2
"
>
<v-col
cols=
"12"
md=
"6"
>
<v-col
cols=
"12"
md=
"6"
>
<h2
class=
"text-left px-0 my-0"
>
<h2
class=
"text-
center text-md-
left px-0 my-0"
>
{{
trainingCourse
.
name
}}
{{
trainingCourse
.
name
}}
</h2>
</h2>
</v-col>
</v-col>
<v-col
cols=
"12"
md=
"6"
class=
"text-right"
>
<v-col
cols=
"12"
md=
"6"
class=
"text-right"
v-if=
"!onMobile"
>
<v-btn
<v-btn
color=
"primary"
color=
"primary"
@
click=
"state.isAddingStudent = true"
@
click=
"state.isAddingStudent = true"
...
@@ -20,24 +20,30 @@
...
@@ -20,24 +20,30 @@
>
>
Add Student
Add Student
</v-btn>
</v-btn>
<AddStudentsToCourseDialog
ref=
"addStudentsToCourseDialog"
:show-dialog=
"state.isAddingStudent"
:course-id=
"Number.parseInt($route.params.trainingId)"
v-on:close-dialog=
"closeAddStudentDialog"
v-on:update-students=
"fetchDebounced"
/>
</v-col>
</v-col>
</v-row>
</v-row>
<AddStudentsToCourseDialog
ref=
"addStudentsToCourseDialog"
:show-dialog=
"state.isAddingStudent"
:course-id=
"Number.parseInt($route.params.trainingId)"
v-on:close-dialog=
"closeAddStudentDialog"
v-on:update-students=
"fetchDebounced"
/>
<v-data-table
<v-data-table
v-model=
"selectedInstructors"
v-model=
"selectedInstructors"
id=
"training-table"
id=
"training-table"
:headers=
"
pocH
eader
s
"
:headers=
"
h
eader"
:items=
"trainingCourse.instructorsCombined"
:items=
"trainingCourse.instructorsCombined"
hide-default-footer
hide-default-footer
mobile-breakpoint=
"800"
:show-expand=
"onMobile"
:expanded.sync=
"expanded"
:hide-default-header=
"onMobile"
:mobile-breakpoint=
"mobileBreakpoint"
expand-icon=
"mdi-chevron-right"
:disable-sort=
"onMobile"
calculate-widths
calculate-widths
:loading=
"loading"
:loading=
"loading"
class=
"lb-mobile-enabled lb-mobile-no-checkbox"
>
>
<template
v-slot:top
>
<template
v-slot:top
>
<v-dialog
<v-dialog
...
@@ -47,8 +53,14 @@
...
@@ -47,8 +53,14 @@
max-width=
"500px"
max-width=
"500px"
>
>
<v-card>
<v-card>
<v-card-title>
<v-card-title
class=
"d-flex flex-column mb-8"
>
<span
class=
"headline"
>
Edit
{{
trainingCourse
.
name
}}
</span>
<div
class=
"mt-2 mb-1"
>
<img
src=
"@/assets/images/logos/Logo_P1_Yodahead-WH.png"
alt=
"Platform One Smokey"
/>
</div>
<h3>
Edit Course Details
</h3>
</v-card-title>
</v-card-title>
<v-card-text>
<v-card-text>
...
@@ -215,9 +227,7 @@
...
@@ -215,9 +227,7 @@
</v-form>
</v-form>
<!-- END FORM -->
<!-- END FORM -->
</v-card-text>
</v-card-text>
<v-card-actions
class=
"justify-center pb-10"
>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn
color=
"secondary"
@
click=
"cancelUpdateCourse"
>
<v-btn
color=
"secondary"
@
click=
"cancelUpdateCourse"
>
Cancel
Cancel
</v-btn>
</v-btn>
...
@@ -233,6 +243,13 @@
...
@@ -233,6 +243,13 @@
</v-card>
</v-card>
</v-dialog>
</v-dialog>
</template>
</template>
<
template
v-slot:
[`
item.instructor
`]
>
<div
class=
"d-flex align-items-center"
>
<p
class=
"px-0 text-left mb-0"
>
{{
trainingCourse
.
instructor
}}
</p>
</div>
</
template
>
<
template
v-slot:
[`
item.startDate
`]
>
<
template
v-slot:
[`
item.startDate
`]
>
<div
class=
"d-flex align-items-center"
>
<div
class=
"d-flex align-items-center"
>
...
@@ -259,7 +276,7 @@
...
@@ -259,7 +276,7 @@
</
template
>
</
template
>
<
template
v-slot:
[`
item.availability
`]
>
<
template
v-slot:
[`
item.availability
`]
>
<div
class=
"d-flex align-items-center
px-2
"
>
<div
class=
"d-flex align-items-center"
>
<v-progress-linear
<v-progress-linear
class=
"availability-bar"
class=
"availability-bar"
rounded
rounded
...
@@ -281,6 +298,7 @@
...
@@ -281,6 +298,7 @@
</
template
>
</
template
>
<
template
v-slot:footer
>
<
template
v-slot:footer
>
<div
<div
v-if=
"!onMobile"
class=
"d-flex align-items-center"
class=
"d-flex align-items-center"
id=
"alternate-progress-bar-parent"
id=
"alternate-progress-bar-parent"
>
>
...
@@ -297,10 +315,54 @@
...
@@ -297,10 +315,54 @@
></v-progress-linear>
></v-progress-linear>
</div>
</div>
</
template
>
</
template
>
<
template
v-slot:expanded-item
v-if=
"onMobile"
>
<p
class=
"d-flex flex-wrap mx-0 px-0 pl-9 mt-2 mb-5"
>
{{
trainingCourse
.
startDate
}}
to
{{
trainingCourse
.
endDate
}}
</p>
<p
class=
"d-flex flex-wrap text-left mx-0 px-0 pl-9 pr-9 mt-5 mb-5"
>
{{
trainingCourse
.
description
}}
</p>
<div
class=
"d-flex flex-wrap mx-0 px-0"
>
<div
class=
"d-flex align-items-center pl-9 mt-5 mb-5"
light
>
<v-progress-linear
class=
"availability-bar"
rounded
dark
background-color=
"white"
height=
"8px"
:value=
"
(100 * trainingCourse.registrations.length) /
trainingCourse.capacity
"
></v-progress-linear>
<div
class=
"white-space-nowrap ml-2"
v-if=
"!state.isHoveringSeats"
>
{{
trainingCourse
.
registrations
.
length
}}
/
{{
trainingCourse
.
capacity
}}
</div>
</div>
</div>
</
template
>
</v-data-table>
</v-data-table>
<v-btn
v-if=
"onMobile"
color=
"primary"
@
click=
"state.isAddingStudent = true"
class=
"mx-auto my-5 black--text"
block
>
Add Student
</v-btn
>
<div
class=
"d-flex flex-wrap my-5"
>
<div
class=
"d-flex flex-wrap my-5"
>
<v-btn
class=
"mr-3"
@
click=
"btnPushEditPoc"
color=
"accent"
>
<v-btn
class=
"mr-3"
@
click=
"btnPushEditPoc"
:block=
"onMobile"
color=
"accent"
>
<v-icon
class=
"mr-2"
>
mdi-pencil
</v-icon>
<v-icon
class=
"mr-2"
>
mdi-pencil
</v-icon>
Edit Course
Edit Course
</v-btn>
</v-btn>
...
@@ -338,7 +400,9 @@ export default {
...
@@ -338,7 +400,9 @@ export default {
trainingCourseSnapshot
:
{},
trainingCourseSnapshot
:
{},
selectedInstructorsSnapshot
:
[],
selectedInstructorsSnapshot
:
[],
listItems
:
[],
listItems
:
[],
expanded
:
[],
inputRules
,
inputRules
,
mobileBreakpoint
:
800
,
valid
:
true
,
valid
:
true
,
trigger
:
0
,
trigger
:
0
,
studentToAdd
:
[],
studentToAdd
:
[],
...
@@ -373,6 +437,10 @@ export default {
...
@@ -373,6 +437,10 @@ export default {
sortable
:
false
,
sortable
:
false
,
},
},
],
],
mobileHeader
:
[
{
text
:
"
Instructor
"
,
value
:
"
name
"
,
width
:
"
200px
"
,
sortable
:
false
},
{
value
:
"
data-table-expand
"
,
width
:
"
20px
"
,
align
:
"
right
"
},
],
startDateMenu
:
false
,
startDateMenu
:
false
,
endDateMenu
:
false
,
endDateMenu
:
false
,
todaysDate
:
moment
(),
// cannot be formatted if used with diff comparison in courseProgress
todaysDate
:
moment
(),
// cannot be formatted if used with diff comparison in courseProgress
...
@@ -421,6 +489,12 @@ export default {
...
@@ -421,6 +489,12 @@ export default {
return
0
;
return
0
;
}
}
},
},
onMobile
()
{
return
this
.
$vuetify
.
breakpoint
.
width
<
this
.
mobileBreakpoint
;
},
header
()
{
return
this
.
onMobile
?
this
.
mobileHeader
:
this
.
pocHeaders
;
},
},
},
async
mounted
()
{
async
mounted
()
{
this
.
initialLoad
=
true
;
this
.
initialLoad
=
true
;
...
@@ -533,13 +607,18 @@ export default {
...
@@ -533,13 +607,18 @@ export default {
#alternate-progress-bar-parent
{
#alternate-progress-bar-parent
{
height
:
8px
;
height
:
8px
;
}
}
.availability-bar
{
min-width
:
120px
;
width
:
120px
;
}
#alternate-progress-bar-complete
>
div
.primary
{
#alternate-progress-bar-complete
>
div
.primary
{
/* overriding green progress bar */
/* overriding green progress bar */
background-color
:
var
(
--
v-primary-base
)
!
important
;
background-color
:
var
(
--
v-primary-base
)
!
important
;
}
}
#training-table
{
#training-table
.availability-bar
{
.availability-bar
{
width
:
100%
!
important
;
width
:
100%
!
important
;
}
}
}
</
style
>
</
style
>
Prev
1
2
3
Next
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment