UNCLASSIFIED

Commit 062d988f authored by luke.glasscock's avatar luke.glasscock
Browse files

Update LB from code.il2

parent 83f88ca7
<template> <template>
<div> <div class="lb-nav">
<v-app-bar <v-app-bar
id="app-bar" v-model="appBar"
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 16px; margin: 0 14px;
line-height: 16px; line-height: 16px;
font-size: 1rem; font-size: 1rem;
font-weight: bold; font-weight: bold;
......
...@@ -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">
<h2 class="px-0 mb-2 subhead">{{ title }}</h2> <h3 class="px-0 mb-2">{{ title }}</h3>
</div> </div>
</div> </div>
<v-data-table <v-data-table
v-model="personnel.selectedPersonnel" v-model="personnel.selectedPersonnel"
:headers="personnel.headers" :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>
<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,
......
...@@ -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="headers" :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>
<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;
......
...@@ -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;
......
<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>
......
...@@ -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);
......
...@@ -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: 8px; padding-right: 0px;
} }
} }
...@@ -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);
}
...@@ -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 }
...@@ -6,6 +6,7 @@ $dark-bg-color: #002743; ...@@ -6,6 +6,7 @@ $dark-bg-color: #002743;
$gradient-alpha: #00000000; $gradient-alpha: #00000000;
$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,
......
...@@ -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";
...@@ -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,
}); });
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,
};
...@@ -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";
...@@ -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"
......
...@@ -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">
<h4 slot="header" class="mt-4 w-100 text-left"> <h2 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>
...@@ -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>
......
...@@ -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;
......
...@@ -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="pocHeaders" :headers="header"
: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>
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment