inicios del FrontEnd
This commit is contained in:
34
APP/components/Badge.vue
Normal file
34
APP/components/Badge.vue
Normal file
@@ -0,0 +1,34 @@
|
||||
<template>
|
||||
<component :is="tag" class="badge" :class="`badge-${type}`">
|
||||
<slot></slot>
|
||||
</component>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'badge',
|
||||
props: {
|
||||
tag: {
|
||||
type: String,
|
||||
default: 'span',
|
||||
description: 'Badge tag'
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: 'default',
|
||||
validator: value => {
|
||||
let acceptedValues = [
|
||||
'primary',
|
||||
'info',
|
||||
'success',
|
||||
'warning',
|
||||
'danger',
|
||||
'default'
|
||||
];
|
||||
return acceptedValues.indexOf(value) !== -1;
|
||||
},
|
||||
description: 'Badge type (primary|info|success|warning|danger|default)'
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style></style>
|
||||
69
APP/components/BaseAlert.vue
Normal file
69
APP/components/BaseAlert.vue
Normal file
@@ -0,0 +1,69 @@
|
||||
<template>
|
||||
<fade-transition>
|
||||
<div
|
||||
v-if="visible"
|
||||
class="alert"
|
||||
:class="[`alert-${type}`, { 'alert-with-icon': icon }]"
|
||||
role="alert"
|
||||
>
|
||||
<slot v-if="!dismissible"></slot>
|
||||
<template v-else>
|
||||
<slot name="dismiss-icon">
|
||||
<button
|
||||
type="button"
|
||||
class="close"
|
||||
aria-label="Close"
|
||||
@click="dismissAlert"
|
||||
>
|
||||
<i class="tim-icons icon-simple-remove"></i>
|
||||
</button>
|
||||
</slot>
|
||||
|
||||
<template v-if="icon || $slots.icon">
|
||||
<slot name="icon">
|
||||
<span data-notify="icon" :class="icon"></span>
|
||||
</slot>
|
||||
</template>
|
||||
|
||||
<span data-notify="message"> <slot></slot> </span>
|
||||
</template>
|
||||
</div>
|
||||
</fade-transition>
|
||||
</template>
|
||||
<script>
|
||||
import { FadeTransition } from 'vue2-transitions';
|
||||
|
||||
export default {
|
||||
name: 'base-alert',
|
||||
components: {
|
||||
FadeTransition
|
||||
},
|
||||
props: {
|
||||
type: {
|
||||
type: String,
|
||||
default: 'default',
|
||||
description: 'Alert type'
|
||||
},
|
||||
dismissible: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
description: 'Whether alert is dismissible (closeable)'
|
||||
},
|
||||
icon: {
|
||||
type: String,
|
||||
default: '',
|
||||
description: 'Alert icon to display'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
visible: true
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
dismissAlert() {
|
||||
this.visible = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
81
APP/components/BaseButton.vue
Normal file
81
APP/components/BaseButton.vue
Normal file
@@ -0,0 +1,81 @@
|
||||
<template>
|
||||
<component
|
||||
:is="tag"
|
||||
:type="tag === 'button' ? nativeType : ''"
|
||||
:disabled="disabled || loading"
|
||||
@click="handleClick"
|
||||
class="btn"
|
||||
:class="[
|
||||
{ 'btn-round': round },
|
||||
{ 'btn-block': block },
|
||||
{ 'btn-wd': wide },
|
||||
{ 'btn-icon btn-fab': icon },
|
||||
{ [`btn-${type}`]: type },
|
||||
{ [`btn-${size}`]: size },
|
||||
{ 'btn-simple': simple },
|
||||
{ 'btn-link': link },
|
||||
{ disabled: disabled && tag !== 'button' }
|
||||
]"
|
||||
>
|
||||
<slot name="loading">
|
||||
<i v-if="loading" class="fas fa-spinner fa-spin"></i>
|
||||
</slot>
|
||||
<slot></slot>
|
||||
</component>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'base-button',
|
||||
props: {
|
||||
tag: {
|
||||
type: String,
|
||||
default: 'button',
|
||||
description: 'Button html tag'
|
||||
},
|
||||
round: Boolean,
|
||||
icon: Boolean,
|
||||
block: Boolean,
|
||||
loading: Boolean,
|
||||
wide: Boolean,
|
||||
disabled: Boolean,
|
||||
type: {
|
||||
type: String,
|
||||
default: 'default',
|
||||
description: 'Button type (primary|secondary|danger etc)'
|
||||
},
|
||||
nativeType: {
|
||||
type: String,
|
||||
default: 'button',
|
||||
description: 'Button native type (e.g button, input etc)'
|
||||
},
|
||||
size: {
|
||||
type: String,
|
||||
default: '',
|
||||
description: 'Button size (sm|lg)'
|
||||
},
|
||||
simple: {
|
||||
type: Boolean,
|
||||
description: 'Whether button is simple (outlined)'
|
||||
},
|
||||
link: {
|
||||
type: Boolean,
|
||||
description: 'Whether button is a link (no borders or background)'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleClick(evt) {
|
||||
this.$emit('click', evt);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
/deep/ i {
|
||||
padding: 0 3px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
97
APP/components/BaseDropdown.vue
Normal file
97
APP/components/BaseDropdown.vue
Normal file
@@ -0,0 +1,97 @@
|
||||
<template>
|
||||
<component
|
||||
:is="tag"
|
||||
class="dropdown"
|
||||
:class="[{ show: isOpen }, `drop${direction}`]"
|
||||
@click="toggleDropDown"
|
||||
v-click-outside="closeDropDown"
|
||||
>
|
||||
<slot name="title-container" :is-open="isOpen">
|
||||
<component
|
||||
:is="titleTag"
|
||||
class="dropdown-toggle no-caret"
|
||||
:class="titleClasses"
|
||||
:aria-label="title || 'dropdown'"
|
||||
:aria-expanded="isOpen"
|
||||
data-toggle="dropdown"
|
||||
>
|
||||
<slot name="title" :is-open="isOpen">
|
||||
<i :class="icon"></i> {{ title }}
|
||||
</slot>
|
||||
</component>
|
||||
</slot>
|
||||
<ul
|
||||
class="dropdown-menu"
|
||||
:class="[
|
||||
{ show: isOpen },
|
||||
{ 'dropdown-menu-right': menuOnRight },
|
||||
menuClasses
|
||||
]"
|
||||
>
|
||||
<slot></slot>
|
||||
</ul>
|
||||
</component>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'base-dropdown',
|
||||
props: {
|
||||
tag: {
|
||||
type: String,
|
||||
default: 'div',
|
||||
description: 'Dropdown html tag (e.g div, ul etc)'
|
||||
},
|
||||
titleTag: {
|
||||
type: String,
|
||||
default: 'button',
|
||||
description: 'Dropdown title (toggle) html tag'
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
description: 'Dropdown title'
|
||||
},
|
||||
direction: {
|
||||
type: String,
|
||||
default: 'down', // up | down
|
||||
description: 'Dropdown menu direction (up|down)'
|
||||
},
|
||||
icon: {
|
||||
type: String,
|
||||
description: 'Dropdown icon'
|
||||
},
|
||||
titleClasses: {
|
||||
type: [String, Object, Array],
|
||||
description: 'Title css classes'
|
||||
},
|
||||
menuClasses: {
|
||||
type: [String, Object],
|
||||
description: 'Menu css classes'
|
||||
},
|
||||
menuOnRight: {
|
||||
type: Boolean,
|
||||
description: 'Whether menu should appear on the right'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isOpen: false
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
toggleDropDown() {
|
||||
this.isOpen = !this.isOpen;
|
||||
this.$emit('change', this.isOpen);
|
||||
},
|
||||
closeDropDown() {
|
||||
this.isOpen = false;
|
||||
this.$emit('change', false);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.dropdown {
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
</style>
|
||||
148
APP/components/BasePagination.vue
Executable file
148
APP/components/BasePagination.vue
Executable file
@@ -0,0 +1,148 @@
|
||||
<template>
|
||||
<ul class="pagination" :class="paginationClass">
|
||||
<li
|
||||
class="page-item prev-page"
|
||||
v-if="showArrows"
|
||||
:class="{ disabled: value === 1 }"
|
||||
>
|
||||
<a class="page-link" aria-label="Previous" @click="prevPage">
|
||||
<i class="tim-icons icon-double-left" aria-hidden="true"></i>
|
||||
</a>
|
||||
</li>
|
||||
<li
|
||||
class="page-item"
|
||||
v-for="item in range(minPage, maxPage)"
|
||||
:key="item"
|
||||
:class="{ active: value === item }"
|
||||
>
|
||||
<a class="page-link" @click="changePage(item)">{{ item }}</a>
|
||||
</li>
|
||||
<li
|
||||
v-if="showArrows"
|
||||
class="page-item page-pre next-page"
|
||||
:class="{ disabled: value === totalPages }"
|
||||
>
|
||||
<a class="page-link" aria-label="Next" @click="nextPage">
|
||||
<i class="tim-icons icon-double-right" aria-hidden="true"></i>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'base-pagination',
|
||||
props: {
|
||||
type: {
|
||||
type: String,
|
||||
default: 'primary',
|
||||
validator: value => {
|
||||
return [
|
||||
'default',
|
||||
'primary',
|
||||
'danger',
|
||||
'success',
|
||||
'warning',
|
||||
'info'
|
||||
].includes(value);
|
||||
}
|
||||
},
|
||||
pageCount: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
perPage: {
|
||||
type: Number,
|
||||
default: 10
|
||||
},
|
||||
showArrows: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
total: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
value: {
|
||||
type: Number,
|
||||
default: 1
|
||||
},
|
||||
pagesToDisplay: {
|
||||
type: Number,
|
||||
default: 5
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
paginationClass() {
|
||||
return `pagination-${this.type}`;
|
||||
},
|
||||
totalPages() {
|
||||
if (this.pageCount > 0) return this.pageCount;
|
||||
if (this.total > 0) {
|
||||
return Math.ceil(this.total / this.perPage);
|
||||
}
|
||||
return 1;
|
||||
},
|
||||
defaultPagesToDisplay() {
|
||||
if (this.totalPages > 0 && this.totalPages < this.pagesToDisplay) {
|
||||
return this.totalPages;
|
||||
}
|
||||
return this.pagesToDisplay;
|
||||
},
|
||||
minPage() {
|
||||
if (this.value >= this.defaultPagesToDisplay) {
|
||||
const pagesToAdd = Math.floor(this.defaultPagesToDisplay / 2);
|
||||
const newMaxPage = pagesToAdd + this.value;
|
||||
if (newMaxPage > this.totalPages) {
|
||||
return this.totalPages - this.defaultPagesToDisplay + 1;
|
||||
}
|
||||
return this.value - pagesToAdd;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
},
|
||||
maxPage() {
|
||||
if (this.value >= this.defaultPagesToDisplay) {
|
||||
const pagesToAdd = Math.floor(this.defaultPagesToDisplay / 2);
|
||||
const newMaxPage = pagesToAdd + this.value;
|
||||
if (newMaxPage < this.totalPages) {
|
||||
return newMaxPage;
|
||||
} else {
|
||||
return this.totalPages;
|
||||
}
|
||||
} else {
|
||||
return this.defaultPagesToDisplay;
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
range(min, max) {
|
||||
let arr = [];
|
||||
for (let i = min; i <= max; i++) {
|
||||
arr.push(i);
|
||||
}
|
||||
return arr;
|
||||
},
|
||||
changePage(item) {
|
||||
this.$emit('input', item);
|
||||
},
|
||||
nextPage() {
|
||||
if (this.value < this.totalPages) {
|
||||
this.$emit('input', this.value + 1);
|
||||
}
|
||||
},
|
||||
prevPage() {
|
||||
if (this.value > 1) {
|
||||
this.$emit('input', this.value - 1);
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
perPage() {
|
||||
this.$emit('input', 1);
|
||||
},
|
||||
total() {
|
||||
this.$emit('input', 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
79
APP/components/BaseProgress.vue
Executable file
79
APP/components/BaseProgress.vue
Executable file
@@ -0,0 +1,79 @@
|
||||
<template>
|
||||
<div
|
||||
class="progress-container"
|
||||
:class="{
|
||||
[`progress-${type}`]: type,
|
||||
[`progress-${size}`]: size
|
||||
}"
|
||||
>
|
||||
<span class="progress-badge" v-if="label">{{ label }}</span>
|
||||
<div class="progress">
|
||||
<span class="progress-value" v-if="showValue && valuePosition === 'left'"
|
||||
>{{ value }}%</span
|
||||
>
|
||||
<div
|
||||
class="progress-bar"
|
||||
:class="computedClasses"
|
||||
role="progressbar"
|
||||
:aria-valuenow="value"
|
||||
aria-valuemin="0"
|
||||
aria-valuemax="100"
|
||||
:style="`width: ${value}%;`"
|
||||
>
|
||||
<slot>
|
||||
<span
|
||||
v-if="showValue && valuePosition === 'right'"
|
||||
class="progress-value"
|
||||
>{{ value }}%</span
|
||||
>
|
||||
</slot>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'base-progress',
|
||||
props: {
|
||||
striped: Boolean,
|
||||
showValue: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
animated: Boolean,
|
||||
label: String,
|
||||
valuePosition: {
|
||||
type: String,
|
||||
default: 'left' // left | right
|
||||
},
|
||||
height: {
|
||||
type: Number,
|
||||
default: 1
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: 'default'
|
||||
},
|
||||
size: {
|
||||
type: String,
|
||||
default: 'sm'
|
||||
},
|
||||
value: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
validator: value => {
|
||||
return value >= 0 && value <= 100;
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
computedClasses() {
|
||||
return [
|
||||
{ 'progress-bar-striped': this.striped },
|
||||
{ 'progress-bar-animated': this.animated }
|
||||
];
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style></style>
|
||||
48
APP/components/BaseSwitch.vue
Executable file
48
APP/components/BaseSwitch.vue
Executable file
@@ -0,0 +1,48 @@
|
||||
<template>
|
||||
<div
|
||||
class="bootstrap-switch bootstrap-switch-wrapper bootstrap-switch-animate"
|
||||
:class="switchClass"
|
||||
>
|
||||
<div class="bootstrap-switch-container" @click="triggerToggle()">
|
||||
<span class="bootstrap-switch-handle-on ">
|
||||
<slot name="on"> {{ onText }} </slot>
|
||||
</span>
|
||||
<span class="bootstrap-switch-label"></span>
|
||||
<span class="bootstrap-switch-handle-off bootstrap-switch-default">
|
||||
<slot name="off"> {{ offText }} </slot>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'base-switch',
|
||||
props: {
|
||||
value: [Array, Boolean],
|
||||
onText: String,
|
||||
offText: String
|
||||
},
|
||||
computed: {
|
||||
switchClass() {
|
||||
let base = 'bootstrap-switch-';
|
||||
let state = this.model ? 'on' : 'off';
|
||||
let classes = base + state;
|
||||
return classes;
|
||||
},
|
||||
model: {
|
||||
get() {
|
||||
return this.value;
|
||||
},
|
||||
set(value) {
|
||||
this.$emit('input', value);
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
triggerToggle() {
|
||||
this.model = !this.model;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style></style>
|
||||
70
APP/components/BaseTable.vue
Normal file
70
APP/components/BaseTable.vue
Normal file
@@ -0,0 +1,70 @@
|
||||
<template>
|
||||
<table class="table tablesorter" :class="tableClass">
|
||||
<thead :class="theadClasses">
|
||||
<tr>
|
||||
<slot name="columns" :columns="columns">
|
||||
<th v-for="column in columns" :key="column">{{ column }}</th>
|
||||
</slot>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody :class="tbodyClasses">
|
||||
<tr v-for="(item, index) in data" :key="index">
|
||||
<slot :row="item" :index="index">
|
||||
<td
|
||||
v-for="(column, index) in columns"
|
||||
:key="index"
|
||||
v-if="hasValue(item, column)"
|
||||
>
|
||||
{{ itemValue(item, column) }}
|
||||
</td>
|
||||
</slot>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'base-table',
|
||||
props: {
|
||||
columns: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
description: 'Table columns'
|
||||
},
|
||||
data: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
description: 'Table data'
|
||||
},
|
||||
type: {
|
||||
type: String, // striped | hover
|
||||
default: '',
|
||||
description: 'Whether table is striped or hover type'
|
||||
},
|
||||
theadClasses: {
|
||||
type: String,
|
||||
default: '',
|
||||
description: '<thead> css classes'
|
||||
},
|
||||
tbodyClasses: {
|
||||
type: String,
|
||||
default: '',
|
||||
description: '<tbody> css classes'
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
tableClass() {
|
||||
return this.type && `table-${this.type}`;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
hasValue(item, column) {
|
||||
return item[column.toLowerCase()] !== 'undefined';
|
||||
},
|
||||
itemValue(item, column) {
|
||||
return item[column.toLowerCase()];
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style></style>
|
||||
17
APP/components/Breadcrumb/Breadcrumb.vue
Executable file
17
APP/components/Breadcrumb/Breadcrumb.vue
Executable file
@@ -0,0 +1,17 @@
|
||||
<template>
|
||||
<ul class="breadcrumb" :class="{ 'bg-transparent': transparent }">
|
||||
<slot></slot>
|
||||
</ul>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'breadcrumb',
|
||||
props: {
|
||||
transparent: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style></style>
|
||||
16
APP/components/Breadcrumb/BreadcrumbItem.vue
Executable file
16
APP/components/Breadcrumb/BreadcrumbItem.vue
Executable file
@@ -0,0 +1,16 @@
|
||||
<template>
|
||||
<li class="breadcrumb-item" :class="{ active: active }"><slot></slot></li>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'breadcrumb-item',
|
||||
props: {
|
||||
active: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
description: 'Whether breadcrumb item is active'
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style></style>
|
||||
48
APP/components/Breadcrumb/RouteBreadcrumb.vue
Normal file
48
APP/components/Breadcrumb/RouteBreadcrumb.vue
Normal file
@@ -0,0 +1,48 @@
|
||||
<template>
|
||||
<bread-crumb>
|
||||
<BreadCrumbItem
|
||||
v-for="(route, index) in $route.matched.slice()"
|
||||
:key="route.name"
|
||||
style="display:inline-block"
|
||||
>
|
||||
<nuxt-link
|
||||
:to="{ name: route.name }"
|
||||
v-if="index < $route.matched.length - 1"
|
||||
class="breadcrumb-link"
|
||||
>
|
||||
{{ routeName }}
|
||||
</nuxt-link>
|
||||
<span v-else class="breadcrumb-current">{{ routeName }}</span>
|
||||
</BreadCrumbItem>
|
||||
</bread-crumb>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import BreadCrumb from './Breadcrumb';
|
||||
import BreadCrumbItem from './BreadcrumbItem';
|
||||
|
||||
export default {
|
||||
name: 'route-breadcrumb',
|
||||
components: {
|
||||
BreadCrumb,
|
||||
BreadCrumbItem
|
||||
},
|
||||
computed: {
|
||||
routeName() {
|
||||
const { path } = this.$route;
|
||||
let parts = path.split('/')
|
||||
return parts.map(p => this.capitalizeFirstLetter(p)).join(' ');
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
capitalizeFirstLetter(string) {
|
||||
if (!string || typeof string !== 'string') {
|
||||
return ''
|
||||
}
|
||||
return string.charAt(0).toUpperCase() + string.slice(1);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
62
APP/components/Cards/Card.vue
Normal file
62
APP/components/Cards/Card.vue
Normal file
@@ -0,0 +1,62 @@
|
||||
<template>
|
||||
<div class="card" :class="[type && `card-${type}`]">
|
||||
<div class="card-image" v-if="$slots.image"><slot name="image"></slot></div>
|
||||
<div
|
||||
class="card-header"
|
||||
v-if="$slots.header || title"
|
||||
:class="headerClasses"
|
||||
>
|
||||
<slot name="header">
|
||||
<h4 class="card-title">{{ title }}</h4>
|
||||
<p class="card-category" v-if="subTitle">{{ subTitle }}</p>
|
||||
</slot>
|
||||
</div>
|
||||
<div class="card-body" v-if="$slots.default" :class="bodyClasses">
|
||||
<slot></slot>
|
||||
</div>
|
||||
<div class="card-image" v-if="$slots['image-bottom']">
|
||||
<slot name="image-bottom"></slot>
|
||||
</div>
|
||||
<slot name="raw-content"></slot>
|
||||
<div class="card-footer" :class="footerClasses" v-if="$slots.footer">
|
||||
<hr v-if="showFooterLine" />
|
||||
<slot name="footer"></slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'card',
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
description: 'Card title'
|
||||
},
|
||||
subTitle: {
|
||||
type: String,
|
||||
description: 'Card subtitle'
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
description: 'Card type (e.g primary/danger etc)'
|
||||
},
|
||||
showFooterLine: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
headerClasses: {
|
||||
type: [String, Object, Array],
|
||||
description: 'Card header css classes'
|
||||
},
|
||||
bodyClasses: {
|
||||
type: [String, Object, Array],
|
||||
description: 'Card body css classes'
|
||||
},
|
||||
footerClasses: {
|
||||
type: [String, Object, Array],
|
||||
description: 'Card footer css classes'
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style></style>
|
||||
63
APP/components/Charts/BarChart.js
Normal file
63
APP/components/Charts/BarChart.js
Normal file
@@ -0,0 +1,63 @@
|
||||
import { Bar, mixins } from 'vue-chartjs';
|
||||
|
||||
export default {
|
||||
name: 'bar-chart',
|
||||
extends: Bar,
|
||||
mixins: [mixins.reactiveProp],
|
||||
props: {
|
||||
extraOptions: Object,
|
||||
gradientColors: {
|
||||
type: Array,
|
||||
default: () => [
|
||||
'rgba(72,72,176,0.2)',
|
||||
'rgba(72,72,176,0.0)',
|
||||
'rgba(119,52,169,0)'
|
||||
],
|
||||
validator: val => {
|
||||
return val.length > 1;
|
||||
}
|
||||
},
|
||||
gradientStops: {
|
||||
type: Array,
|
||||
default: () => [1, 0.4, 0],
|
||||
validator: val => {
|
||||
return val.length > 1;
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
ctx: null
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
updateGradients(chartData) {
|
||||
if (!chartData) return;
|
||||
const ctx =
|
||||
this.ctx || document.getElementById(this.chartId).getContext('2d');
|
||||
const gradientStroke = ctx.createLinearGradient(0, 230, 0, 50);
|
||||
|
||||
this.gradientStops.forEach((stop, index) => {
|
||||
gradientStroke.addColorStop(stop, this.gradientColors[index]);
|
||||
});
|
||||
|
||||
chartData.datasets.forEach(set => {
|
||||
if (!set.backgroundColor) {
|
||||
set.backgroundColor = gradientStroke;
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$watch(
|
||||
'chartData',
|
||||
(newVal, oldVal) => {
|
||||
this.updateGradients(newVal);
|
||||
if (!oldVal) {
|
||||
this.renderChart(this.chartData, this.extraOptions);
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
}
|
||||
};
|
||||
62
APP/components/Charts/LineChart.js
Normal file
62
APP/components/Charts/LineChart.js
Normal file
@@ -0,0 +1,62 @@
|
||||
import { Line, mixins } from 'vue-chartjs';
|
||||
|
||||
export default {
|
||||
name: 'line-chart',
|
||||
extends: Line,
|
||||
mixins: [mixins.reactiveProp],
|
||||
props: {
|
||||
extraOptions: Object,
|
||||
gradientColors: {
|
||||
type: Array,
|
||||
default: () => [
|
||||
'rgba(72,72,176,0.2)',
|
||||
'rgba(72,72,176,0.0)',
|
||||
'rgba(119,52,169,0)'
|
||||
],
|
||||
validator: val => {
|
||||
return val.length > 1;
|
||||
}
|
||||
},
|
||||
gradientStops: {
|
||||
type: Array,
|
||||
default: () => [1, 0.4, 0],
|
||||
validator: val => {
|
||||
return val.length > 1;
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
ctx: null
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
updateGradients(chartData) {
|
||||
if (!chartData) return;
|
||||
const ctx =
|
||||
this.ctx || document.getElementById(this.chartId).getContext('2d');
|
||||
const gradientStroke = ctx.createLinearGradient(0, 230, 0, 50);
|
||||
|
||||
this.gradientStops.forEach((stop, index) => {
|
||||
gradientStroke.addColorStop(stop, this.gradientColors[index]);
|
||||
});
|
||||
chartData.datasets.forEach(set => {
|
||||
if (!set.backgroundColor) {
|
||||
set.backgroundColor = gradientStroke;
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$watch(
|
||||
'chartData',
|
||||
(newVal, oldVal) => {
|
||||
this.updateGradients(this.chartData);
|
||||
if (!oldVal) {
|
||||
this.renderChart(this.chartData, this.extraOptions);
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
}
|
||||
};
|
||||
377
APP/components/Charts/config.js
Normal file
377
APP/components/Charts/config.js
Normal file
@@ -0,0 +1,377 @@
|
||||
export const basicOptions = {
|
||||
maintainAspectRatio: false,
|
||||
legend: {
|
||||
display: false
|
||||
},
|
||||
responsive: true
|
||||
};
|
||||
export let blueChartOptions = {
|
||||
...basicOptions,
|
||||
tooltips: {
|
||||
backgroundColor: '#f5f5f5',
|
||||
titleFontColor: '#333',
|
||||
bodyFontColor: '#666',
|
||||
bodySpacing: 4,
|
||||
xPadding: 12,
|
||||
mode: 'nearest',
|
||||
intersect: 0,
|
||||
position: 'nearest'
|
||||
},
|
||||
scales: {
|
||||
yAxes: [
|
||||
{
|
||||
barPercentage: 1.6,
|
||||
gridLines: {
|
||||
drawBorder: false,
|
||||
color: 'rgba(29,140,248,0.0)',
|
||||
zeroLineColor: 'transparent'
|
||||
},
|
||||
ticks: {
|
||||
suggestedMin: 60,
|
||||
suggestedMax: 125,
|
||||
padding: 20,
|
||||
fontColor: '#2380f7'
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
xAxes: [
|
||||
{
|
||||
barPercentage: 1.6,
|
||||
gridLines: {
|
||||
drawBorder: false,
|
||||
color: 'rgba(29,140,248,0.1)',
|
||||
zeroLineColor: 'transparent'
|
||||
},
|
||||
ticks: {
|
||||
padding: 20,
|
||||
fontColor: '#2380f7'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
export let lineChartOptionsBlue = {
|
||||
...basicOptions,
|
||||
tooltips: {
|
||||
backgroundColor: '#f5f5f5',
|
||||
titleFontColor: '#333',
|
||||
bodyFontColor: '#666',
|
||||
bodySpacing: 4,
|
||||
xPadding: 12,
|
||||
mode: 'nearest',
|
||||
intersect: 0,
|
||||
position: 'nearest'
|
||||
},
|
||||
responsive: true,
|
||||
scales: {
|
||||
yAxes: [
|
||||
{
|
||||
barPercentage: 1.6,
|
||||
gridLines: {
|
||||
drawBorder: false,
|
||||
color: 'rgba(29,140,248,0.0)',
|
||||
zeroLineColor: 'transparent'
|
||||
},
|
||||
ticks: {
|
||||
suggestedMin: 60,
|
||||
suggestedMax: 125,
|
||||
padding: 20,
|
||||
fontColor: '#9e9e9e'
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
xAxes: [
|
||||
{
|
||||
barPercentage: 1.6,
|
||||
gridLines: {
|
||||
drawBorder: false,
|
||||
color: 'rgba(29,140,248,0.1)',
|
||||
zeroLineColor: 'transparent'
|
||||
},
|
||||
ticks: {
|
||||
padding: 20,
|
||||
fontColor: '#9e9e9e'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
export let barChartOptionsGradient = {
|
||||
...basicOptions,
|
||||
tooltips: {
|
||||
backgroundColor: '#f5f5f5',
|
||||
titleFontColor: '#333',
|
||||
bodyFontColor: '#666',
|
||||
bodySpacing: 4,
|
||||
xPadding: 12,
|
||||
mode: 'nearest',
|
||||
intersect: 0,
|
||||
position: 'nearest'
|
||||
},
|
||||
responsive: true,
|
||||
scales: {
|
||||
yAxes: [
|
||||
{
|
||||
gridLines: {
|
||||
drawBorder: false,
|
||||
color: 'rgba(253,93,147,0.1)',
|
||||
zeroLineColor: 'transparent'
|
||||
},
|
||||
ticks: {
|
||||
suggestedMin: 60,
|
||||
suggestedMax: 125,
|
||||
padding: 20,
|
||||
fontColor: '#9e9e9e'
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
xAxes: [
|
||||
{
|
||||
gridLines: {
|
||||
drawBorder: false,
|
||||
color: 'rgba(253,93,147,0.1)',
|
||||
zeroLineColor: 'transparent'
|
||||
},
|
||||
ticks: {
|
||||
padding: 20,
|
||||
fontColor: '#9e9e9e'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
export let pieChartOptions = {
|
||||
...basicOptions,
|
||||
cutoutPercentage: 70,
|
||||
tooltips: {
|
||||
backgroundColor: '#f5f5f5',
|
||||
titleFontColor: '#333',
|
||||
bodyFontColor: '#666',
|
||||
bodySpacing: 4,
|
||||
xPadding: 12,
|
||||
mode: 'nearest',
|
||||
intersect: 0,
|
||||
position: 'nearest'
|
||||
},
|
||||
|
||||
scales: {
|
||||
yAxes: [
|
||||
{
|
||||
display: 0,
|
||||
ticks: {
|
||||
display: false
|
||||
},
|
||||
gridLines: {
|
||||
drawBorder: false,
|
||||
zeroLineColor: 'transparent',
|
||||
color: 'rgba(255,255,255,0.05)'
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
xAxes: [
|
||||
{
|
||||
display: 0,
|
||||
barPercentage: 1.6,
|
||||
gridLines: {
|
||||
drawBorder: false,
|
||||
color: 'rgba(255,255,255,0.1)',
|
||||
zeroLineColor: 'transparent'
|
||||
},
|
||||
ticks: {
|
||||
display: false
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
export let purpleChartOptions = {
|
||||
...basicOptions,
|
||||
tooltips: {
|
||||
backgroundColor: '#f5f5f5',
|
||||
titleFontColor: '#333',
|
||||
bodyFontColor: '#666',
|
||||
bodySpacing: 4,
|
||||
xPadding: 12,
|
||||
mode: 'nearest',
|
||||
intersect: 0,
|
||||
position: 'nearest'
|
||||
},
|
||||
scales: {
|
||||
yAxes: [
|
||||
{
|
||||
barPercentage: 1.6,
|
||||
gridLines: {
|
||||
drawBorder: false,
|
||||
color: 'rgba(29,140,248,0.0)',
|
||||
zeroLineColor: 'transparent'
|
||||
},
|
||||
ticks: {
|
||||
suggestedMin: 60,
|
||||
suggestedMax: 125,
|
||||
padding: 20,
|
||||
fontColor: '#9a9a9a'
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
xAxes: [
|
||||
{
|
||||
barPercentage: 1.6,
|
||||
gridLines: {
|
||||
drawBorder: false,
|
||||
color: 'rgba(225,78,202,0.1)',
|
||||
zeroLineColor: 'transparent'
|
||||
},
|
||||
ticks: {
|
||||
padding: 20,
|
||||
fontColor: '#9a9a9a'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
export let orangeChartOptions = {
|
||||
...basicOptions,
|
||||
tooltips: {
|
||||
backgroundColor: '#f5f5f5',
|
||||
titleFontColor: '#333',
|
||||
bodyFontColor: '#666',
|
||||
bodySpacing: 4,
|
||||
xPadding: 12,
|
||||
mode: 'nearest',
|
||||
intersect: 0,
|
||||
position: 'nearest'
|
||||
},
|
||||
scales: {
|
||||
yAxes: [
|
||||
{
|
||||
barPercentage: 1.6,
|
||||
gridLines: {
|
||||
drawBorder: false,
|
||||
color: 'rgba(29,140,248,0.0)',
|
||||
zeroLineColor: 'transparent'
|
||||
},
|
||||
ticks: {
|
||||
suggestedMin: 50,
|
||||
suggestedMax: 110,
|
||||
padding: 20,
|
||||
fontColor: '#ff8a76'
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
xAxes: [
|
||||
{
|
||||
barPercentage: 1.6,
|
||||
gridLines: {
|
||||
drawBorder: false,
|
||||
color: 'rgba(220,53,69,0.1)',
|
||||
zeroLineColor: 'transparent'
|
||||
},
|
||||
ticks: {
|
||||
padding: 20,
|
||||
fontColor: '#ff8a76'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
export let greenChartOptions = {
|
||||
...basicOptions,
|
||||
tooltips: {
|
||||
backgroundColor: '#f5f5f5',
|
||||
titleFontColor: '#333',
|
||||
bodyFontColor: '#666',
|
||||
bodySpacing: 4,
|
||||
xPadding: 12,
|
||||
mode: 'nearest',
|
||||
intersect: 0,
|
||||
position: 'nearest'
|
||||
},
|
||||
scales: {
|
||||
yAxes: [
|
||||
{
|
||||
barPercentage: 1.6,
|
||||
gridLines: {
|
||||
drawBorder: false,
|
||||
color: 'rgba(29,140,248,0.0)',
|
||||
zeroLineColor: 'transparent'
|
||||
},
|
||||
ticks: {
|
||||
suggestedMin: 50,
|
||||
suggestedMax: 125,
|
||||
padding: 20,
|
||||
fontColor: '#9e9e9e'
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
xAxes: [
|
||||
{
|
||||
barPercentage: 1.6,
|
||||
gridLines: {
|
||||
drawBorder: false,
|
||||
color: 'rgba(0,242,195,0.1)',
|
||||
zeroLineColor: 'transparent'
|
||||
},
|
||||
ticks: {
|
||||
padding: 20,
|
||||
fontColor: '#9e9e9e'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
export let barChartOptions = {
|
||||
...basicOptions,
|
||||
tooltips: {
|
||||
backgroundColor: '#f5f5f5',
|
||||
titleFontColor: '#333',
|
||||
bodyFontColor: '#666',
|
||||
bodySpacing: 4,
|
||||
xPadding: 12,
|
||||
mode: 'nearest',
|
||||
intersect: 0,
|
||||
position: 'nearest'
|
||||
},
|
||||
scales: {
|
||||
yAxes: [
|
||||
{
|
||||
gridLines: {
|
||||
drawBorder: false,
|
||||
color: 'rgba(29,140,248,0.1)',
|
||||
zeroLineColor: 'transparent'
|
||||
},
|
||||
ticks: {
|
||||
suggestedMin: 60,
|
||||
suggestedMax: 120,
|
||||
padding: 20,
|
||||
fontColor: '#9e9e9e'
|
||||
}
|
||||
}
|
||||
],
|
||||
xAxes: [
|
||||
{
|
||||
gridLines: {
|
||||
drawBorder: false,
|
||||
color: 'rgba(29,140,248,0.1)',
|
||||
zeroLineColor: 'transparent'
|
||||
},
|
||||
ticks: {
|
||||
padding: 20,
|
||||
fontColor: '#9e9e9e'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
11
APP/components/Charts/utils.js
Normal file
11
APP/components/Charts/utils.js
Normal file
@@ -0,0 +1,11 @@
|
||||
export function hexToRGB(hex, alpha) {
|
||||
const r = parseInt(hex.slice(1, 3), 16),
|
||||
g = parseInt(hex.slice(3, 5), 16),
|
||||
b = parseInt(hex.slice(5, 7), 16);
|
||||
|
||||
if (alpha) {
|
||||
return `rgba(${r},${g},${b}, ${alpha})`;
|
||||
} else {
|
||||
return `rgb(${r},${g},${b})`;
|
||||
}
|
||||
}
|
||||
35
APP/components/CloseButton.vue
Executable file
35
APP/components/CloseButton.vue
Executable file
@@ -0,0 +1,35 @@
|
||||
<template>
|
||||
<button
|
||||
type="button"
|
||||
class="navbar-toggler"
|
||||
data-toggle="collapse"
|
||||
@click="handleClick"
|
||||
:data-target="`#${target}`"
|
||||
:aria-controls="target"
|
||||
:aria-expanded="expanded"
|
||||
aria-label="Toggle navigation"
|
||||
>
|
||||
<span></span> <span></span>
|
||||
</button>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'close-button',
|
||||
props: {
|
||||
target: {
|
||||
type: [String, Number],
|
||||
description: 'Close button target element'
|
||||
},
|
||||
expanded: {
|
||||
type: Boolean,
|
||||
description: 'Whether button is expanded (aria-expanded attribute)'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleClick(evt) {
|
||||
this.$emit('click', evt);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style></style>
|
||||
72
APP/components/Dashboard/TaskList.vue
Normal file
72
APP/components/Dashboard/TaskList.vue
Normal file
@@ -0,0 +1,72 @@
|
||||
<template>
|
||||
<base-table :data="tableData" thead-classes="text-primary">
|
||||
<template slot-scope="{ row }">
|
||||
<td><base-checkbox v-model="row.done"></base-checkbox></td>
|
||||
<td>
|
||||
<p class="title">{{ row.title }}</p>
|
||||
<p class="text-muted">{{ row.description }}</p>
|
||||
</td>
|
||||
<td class="td-actions text-right">
|
||||
<el-tooltip
|
||||
content="Edit task"
|
||||
effect="light"
|
||||
:open-delay="300"
|
||||
placement="top"
|
||||
>
|
||||
<base-button type="link">
|
||||
<i class="tim-icons icon-pencil"></i>
|
||||
</base-button>
|
||||
</el-tooltip>
|
||||
</td>
|
||||
</template>
|
||||
</base-table>
|
||||
</template>
|
||||
<script>
|
||||
import { BaseTable } from '@/components';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
BaseTable
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
tableData: [
|
||||
{
|
||||
title: 'Update the Documentation',
|
||||
description: 'Dwuamish Head, Seattle, WA 8:47 AM',
|
||||
done: false
|
||||
},
|
||||
{
|
||||
title: 'GDPR Compliance',
|
||||
description:
|
||||
'The GDPR is a regulation that requires businesses to protect the personal data and privacy of Europe citizens for transactions that occur within EU member states.',
|
||||
done: true
|
||||
},
|
||||
{
|
||||
title: 'Solve the issues',
|
||||
description:
|
||||
'Fifty percent of all respondents said they would be more likely to shop at a company',
|
||||
done: false
|
||||
},
|
||||
{
|
||||
title: 'Release v2.0.0',
|
||||
description: 'Ra Ave SW, Seattle, WA 98116, SUA 11:19 AM',
|
||||
done: false
|
||||
},
|
||||
{
|
||||
title: 'Export the processed files',
|
||||
description:
|
||||
'The report also shows that consumers will not easily forgive a company once a breach exposing their personal data occurs.',
|
||||
done: false
|
||||
},
|
||||
{
|
||||
title: 'Arival at export process',
|
||||
description: 'Capitol Hill, Seattle, WA 12:34 AM',
|
||||
done: false
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style></style>
|
||||
119
APP/components/Dashboard/UserTable.vue
Normal file
119
APP/components/Dashboard/UserTable.vue
Normal file
@@ -0,0 +1,119 @@
|
||||
<template>
|
||||
<base-table :data="table" thead-classes="text-primary">
|
||||
<template slot="columns" slot-scope="{ columns }">
|
||||
<th>#</th>
|
||||
<th>Name</th>
|
||||
<th>Job Position</th>
|
||||
<th>Salary</th>
|
||||
<th class="text-right">Milestone</th>
|
||||
<th class="text-right">Actions</th>
|
||||
</template>
|
||||
|
||||
<template slot-scope="{ row, index }">
|
||||
<td class="text-center">
|
||||
<div class="photo"><img :src="row.img" alt="photo" /></div>
|
||||
</td>
|
||||
<td>{{ row.name }}</td>
|
||||
<td>{{ row.job }}</td>
|
||||
<td class="text-center"><base-progress :value="row.progress" /></td>
|
||||
<td class="text-right">€ 99,225</td>
|
||||
<td class="text-right">
|
||||
<el-tooltip
|
||||
content="Refresh"
|
||||
effect="light"
|
||||
:open-delay="300"
|
||||
placement="top"
|
||||
>
|
||||
<base-button
|
||||
:type="index > 2 ? 'success' : 'neutral'"
|
||||
icon
|
||||
size="sm"
|
||||
class="btn-link"
|
||||
>
|
||||
<i class="tim-icons icon-refresh-01"></i>
|
||||
</base-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip
|
||||
content="Delete"
|
||||
effect="light"
|
||||
:open-delay="300"
|
||||
placement="top"
|
||||
>
|
||||
<base-button
|
||||
:type="index > 2 ? 'danger' : 'neutral'"
|
||||
icon
|
||||
size="sm"
|
||||
class="btn-link"
|
||||
>
|
||||
<i class="tim-icons icon-simple-remove"></i>
|
||||
</base-button>
|
||||
</el-tooltip>
|
||||
</td>
|
||||
</template>
|
||||
</base-table>
|
||||
</template>
|
||||
<script>
|
||||
import { BaseTable, BaseProgress } from '@/components';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
BaseTable,
|
||||
BaseProgress
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
table: [
|
||||
{
|
||||
id: 1,
|
||||
img: 'img/tania.jpg',
|
||||
name: 'Tania Mike',
|
||||
job: 'Develop',
|
||||
progress: 25,
|
||||
salary: '€ 99,225'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
img: 'img/robi.jpg',
|
||||
name: 'John Doe',
|
||||
job: 'CEO',
|
||||
progress: 77,
|
||||
salary: '€ 89,241'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
img: 'img/lora.jpg',
|
||||
name: 'Alexa Mike',
|
||||
job: 'Design',
|
||||
progress: 41,
|
||||
salary: '€ 92,144'
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
img: 'img/jana.jpg',
|
||||
name: 'Jana Monday',
|
||||
job: 'Marketing',
|
||||
progress: 50,
|
||||
salary: '€ 49,990'
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
img: 'img/mike.jpg',
|
||||
name: 'Paul Dickens',
|
||||
job: 'Develop',
|
||||
progress: 100,
|
||||
salary: '€ 69,201'
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
img: 'img/emilyz.jpg',
|
||||
name: 'Manuela Rico',
|
||||
job: 'Manager',
|
||||
progress: 15,
|
||||
salary: '€ 99,201'
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style></style>
|
||||
58
APP/components/Inputs/BaseCheckbox.vue
Executable file
58
APP/components/Inputs/BaseCheckbox.vue
Executable file
@@ -0,0 +1,58 @@
|
||||
<template>
|
||||
<div class="form-check" :class="[{ disabled: disabled }, inlineClass]">
|
||||
<label :for="cbId" class="form-check-label">
|
||||
<input
|
||||
:id="cbId"
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
:disabled="disabled"
|
||||
v-model="model"
|
||||
/>
|
||||
<span class="form-check-sign"></span>
|
||||
<slot> <span v-if="inline"> </span> </slot>
|
||||
</label>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'base-checkbox',
|
||||
model: {
|
||||
prop: 'checked'
|
||||
},
|
||||
props: {
|
||||
checked: [Array, Boolean],
|
||||
disabled: Boolean,
|
||||
inline: Boolean,
|
||||
hasError: Boolean
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
cbId: '',
|
||||
touched: false
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
model: {
|
||||
get() {
|
||||
return this.checked;
|
||||
},
|
||||
set(check) {
|
||||
if (!this.touched) {
|
||||
this.touched = true;
|
||||
}
|
||||
this.$emit('input', check);
|
||||
}
|
||||
},
|
||||
inlineClass() {
|
||||
if (this.inline) {
|
||||
return `form-check-inline`;
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.cbId = Math.random()
|
||||
.toString(16)
|
||||
.slice(2);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
126
APP/components/Inputs/BaseInput.vue
Normal file
126
APP/components/Inputs/BaseInput.vue
Normal file
@@ -0,0 +1,126 @@
|
||||
<template>
|
||||
<div
|
||||
class="form-group"
|
||||
:class="{
|
||||
'input-group-focus': focused,
|
||||
'has-danger': error,
|
||||
'has-success': !error && touched,
|
||||
'has-label': label,
|
||||
'has-icon': hasIcon,
|
||||
}"
|
||||
>
|
||||
<slot name="label">
|
||||
<label v-if="label"> {{ label }} {{ required ? '*' : '' }} </label>
|
||||
</slot>
|
||||
<div class="mb-0" :class="{'input-group': hasIcon}">
|
||||
<slot name="addonLeft">
|
||||
<span v-if="addonLeftIcon" class="input-group-prepend">
|
||||
<div class="input-group-text"><i :class="addonLeftIcon"></i></div>
|
||||
</span>
|
||||
</slot>
|
||||
<slot>
|
||||
<input
|
||||
:value="value"
|
||||
v-bind="$attrs"
|
||||
v-on="listeners"
|
||||
class="form-control"
|
||||
aria-describedby="addon-right addon-left"
|
||||
/>
|
||||
</slot>
|
||||
<slot name="addonRight">
|
||||
<span v-if="addonRightIcon" class="input-group-append">
|
||||
<div class="input-group-text"><i :class="addonRightIcon"></i></div>
|
||||
</span>
|
||||
</slot>
|
||||
</div>
|
||||
|
||||
<slot name="error" v-if="error || $slots.error">
|
||||
<label class="error">{{ error }}</label>
|
||||
</slot>
|
||||
<slot name="helperText"></slot>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
inheritAttrs: false,
|
||||
name: 'base-input',
|
||||
props: {
|
||||
required: Boolean,
|
||||
label: {
|
||||
type: String,
|
||||
description: 'Input label'
|
||||
},
|
||||
error: {
|
||||
type: String,
|
||||
description: 'Input error',
|
||||
default: ''
|
||||
},
|
||||
value: {
|
||||
type: [String, Number],
|
||||
description: 'Input value'
|
||||
},
|
||||
addonRightIcon: {
|
||||
type: String,
|
||||
description: 'Input icon on the right'
|
||||
},
|
||||
addonLeftIcon: {
|
||||
type: String,
|
||||
description: 'Input icon on the left'
|
||||
}
|
||||
},
|
||||
model: {
|
||||
prop: 'value',
|
||||
event: 'input'
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
focused: false,
|
||||
touched: false
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
hasIcon() {
|
||||
return this.hasLeftAddon || this.hasRightAddon
|
||||
},
|
||||
hasLeftAddon() {
|
||||
const { addonLeft } = this.$slots;
|
||||
return (
|
||||
addonLeft !== undefined ||
|
||||
this.addonLeftIcon !== undefined
|
||||
);
|
||||
},
|
||||
hasRightAddon() {
|
||||
const { addonRight } = this.$slots;
|
||||
return (
|
||||
addonRight !== undefined ||
|
||||
this.addonRightIcon !== undefined
|
||||
);
|
||||
},
|
||||
listeners() {
|
||||
return {
|
||||
...this.$listeners,
|
||||
input: this.onInput,
|
||||
blur: this.onBlur,
|
||||
focus: this.onFocus
|
||||
};
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onInput(evt) {
|
||||
if (!this.touched) {
|
||||
this.touched = true;
|
||||
}
|
||||
this.$emit('input', evt.target.value);
|
||||
},
|
||||
onFocus(evt) {
|
||||
this.focused = true;
|
||||
this.$emit('focus', evt)
|
||||
},
|
||||
onBlur(evt) {
|
||||
this.focused = false;
|
||||
this.$emit('blur', evt)
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style></style>
|
||||
67
APP/components/Inputs/BaseRadio.vue
Executable file
67
APP/components/Inputs/BaseRadio.vue
Executable file
@@ -0,0 +1,67 @@
|
||||
<template>
|
||||
<div
|
||||
class="form-check form-check-radio"
|
||||
:class="[inlineClass, { disabled: disabled }]"
|
||||
>
|
||||
<label :for="cbId" class="form-check-label">
|
||||
<input
|
||||
:id="cbId"
|
||||
class="form-check-input"
|
||||
type="radio"
|
||||
:disabled="disabled"
|
||||
:value="name"
|
||||
v-model="model"
|
||||
/>
|
||||
<slot></slot> <span class="form-check-sign"></span>
|
||||
</label>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'base-radio',
|
||||
props: {
|
||||
name: {
|
||||
type: [String, Number],
|
||||
description: 'Radio label'
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
description: 'Whether radio is disabled'
|
||||
},
|
||||
value: {
|
||||
type: [String, Boolean],
|
||||
description: 'Radio value'
|
||||
},
|
||||
inline: {
|
||||
type: Boolean,
|
||||
description: 'Whether radio is inline'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
cbId: ''
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
model: {
|
||||
get() {
|
||||
return this.value;
|
||||
},
|
||||
set(value) {
|
||||
this.$emit('input', value);
|
||||
}
|
||||
},
|
||||
inlineClass() {
|
||||
if (this.inline) {
|
||||
return `form-check-inline`;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.cbId = Math.random()
|
||||
.toString(16)
|
||||
.slice(2);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
45
APP/components/Inputs/IconCheckbox.vue
Normal file
45
APP/components/Inputs/IconCheckbox.vue
Normal file
@@ -0,0 +1,45 @@
|
||||
<template>
|
||||
<div
|
||||
class="choice"
|
||||
:class="{ active: checked }"
|
||||
data-toggle="wizard-checkbox"
|
||||
@click="updateValue"
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
:name="name"
|
||||
:disabled="disabled"
|
||||
:checked="checked"
|
||||
/>
|
||||
<div class="icon">
|
||||
<slot name="icon"> <i :class="icon"></i> </slot>
|
||||
</div>
|
||||
<slot name="title">
|
||||
<h6>{{ title }}</h6>
|
||||
</slot>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'icon-checkbox',
|
||||
model: {
|
||||
prop: 'checked'
|
||||
},
|
||||
props: {
|
||||
checked: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
name: String,
|
||||
title: String,
|
||||
icon: String,
|
||||
disabled: Boolean
|
||||
},
|
||||
methods: {
|
||||
updateValue() {
|
||||
this.$emit('input', !this.checked);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style></style>
|
||||
17
APP/components/Layout/Content.vue
Executable file
17
APP/components/Layout/Content.vue
Executable file
@@ -0,0 +1,17 @@
|
||||
<template>
|
||||
<div class="content">
|
||||
<FadeTransition :duration="200" mode="out-in">
|
||||
<!-- your content here -->
|
||||
<nuxt></nuxt>
|
||||
</FadeTransition>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { FadeTransition } from 'vue2-transitions';
|
||||
export default {
|
||||
components: {
|
||||
FadeTransition
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style></style>
|
||||
40
APP/components/Layout/ContentFooter.vue
Executable file
40
APP/components/Layout/ContentFooter.vue
Executable file
@@ -0,0 +1,40 @@
|
||||
<template>
|
||||
<footer class="footer">
|
||||
<div class="container-fluid">
|
||||
<ul class="nav">
|
||||
|
||||
<li class="nav-item">
|
||||
<a
|
||||
href="#"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
class="nav-link"
|
||||
>
|
||||
Blog
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="copyright">
|
||||
© {{ year }}, hecho con <i class="tim-icons icon-heart-2"></i> por
|
||||
|
||||
<a
|
||||
href="#"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>mdchaparror</a
|
||||
>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
year: new Date().getFullYear()
|
||||
};
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style></style>
|
||||
293
APP/components/Layout/DashboardLayout.vue
Executable file
293
APP/components/Layout/DashboardLayout.vue
Executable file
@@ -0,0 +1,293 @@
|
||||
<template>
|
||||
<div class="wrapper" :class="{ 'nav-open': $sidebar.showSidebar }">
|
||||
<notifications></notifications>
|
||||
<side-bar
|
||||
:background-color="sidebarBackground"
|
||||
:short-title="$t('sidebar.shortTitle')"
|
||||
:title="$t('sidebar.title')"
|
||||
>
|
||||
<template slot-scope="props" slot="links">
|
||||
<sidebar-item
|
||||
:link="{
|
||||
name: $t('sidebar.dashboard'),
|
||||
icon: 'tim-icons icon-chart-pie-36',
|
||||
path: '/dashboard'
|
||||
}"
|
||||
>
|
||||
</sidebar-item>
|
||||
<sidebar-item
|
||||
:link="{ name: $t('sidebar.pages'), icon: 'tim-icons icon-image-02' }"
|
||||
>
|
||||
<sidebar-item
|
||||
:link="{ name: $t('sidebar.pricing'), path: '/pricing' }"
|
||||
></sidebar-item>
|
||||
<sidebar-item
|
||||
:link="{ name: $t('sidebar.rtl'), path: '/pages/rtl' }"
|
||||
></sidebar-item>
|
||||
<sidebar-item
|
||||
:link="{ name: $t('sidebar.timeline'), path: '/pages/timeline' }"
|
||||
></sidebar-item>
|
||||
<sidebar-item
|
||||
:link="{ name: $t('sidebar.login'), path: '/login' }"
|
||||
></sidebar-item>
|
||||
<sidebar-item
|
||||
:link="{ name: $t('sidebar.register'), path: '/register' }"
|
||||
></sidebar-item>
|
||||
<sidebar-item
|
||||
:link="{ name: $t('sidebar.lock'), path: '/lock' }"
|
||||
></sidebar-item>
|
||||
<sidebar-item
|
||||
:link="{ name: $t('sidebar.userProfile'), path: '/pages/user' }"
|
||||
></sidebar-item>
|
||||
</sidebar-item>
|
||||
<sidebar-item
|
||||
:link="{
|
||||
name: $t('sidebar.components'),
|
||||
icon: 'tim-icons icon-molecule-40'
|
||||
}"
|
||||
>
|
||||
<sidebar-item :link="{ name: $t('sidebar.multiLevelCollapse') }">
|
||||
<sidebar-item
|
||||
:link="{
|
||||
name: $t('sidebar.example'),
|
||||
isRoute: false,
|
||||
path: 'https://google.com',
|
||||
target: '_blank'
|
||||
}"
|
||||
></sidebar-item>
|
||||
</sidebar-item>
|
||||
|
||||
<sidebar-item
|
||||
:link="{ name: $t('sidebar.buttons'), path: '/components/buttons' }"
|
||||
></sidebar-item>
|
||||
<sidebar-item
|
||||
:link="{
|
||||
name: $t('sidebar.gridSystem'),
|
||||
path: '/components/grid-system'
|
||||
}"
|
||||
></sidebar-item>
|
||||
<sidebar-item
|
||||
:link="{ name: $t('sidebar.panels'), path: '/components/panels' }"
|
||||
></sidebar-item>
|
||||
<sidebar-item
|
||||
:link="{
|
||||
name: $t('sidebar.sweetAlert'),
|
||||
path: '/components/sweet-alert'
|
||||
}"
|
||||
></sidebar-item>
|
||||
<sidebar-item
|
||||
:link="{
|
||||
name: $t('sidebar.notifications'),
|
||||
path: '/components/notifications'
|
||||
}"
|
||||
></sidebar-item>
|
||||
<sidebar-item
|
||||
:link="{ name: $t('sidebar.icons'), path: '/components/icons' }"
|
||||
></sidebar-item>
|
||||
<sidebar-item
|
||||
:link="{
|
||||
name: $t('sidebar.typography'),
|
||||
path: '/components/typography'
|
||||
}"
|
||||
></sidebar-item>
|
||||
</sidebar-item>
|
||||
<sidebar-item
|
||||
:link="{ name: $t('sidebar.forms'), icon: 'tim-icons icon-notes' }"
|
||||
>
|
||||
<sidebar-item
|
||||
:link="{ name: $t('sidebar.regularForms'), path: '/forms/regular' }"
|
||||
></sidebar-item>
|
||||
<sidebar-item
|
||||
:link="{
|
||||
name: $t('sidebar.extendedForms'),
|
||||
path: '/forms/extended'
|
||||
}"
|
||||
></sidebar-item>
|
||||
<sidebar-item
|
||||
:link="{
|
||||
name: $t('sidebar.validationForms'),
|
||||
path: '/forms/validation'
|
||||
}"
|
||||
></sidebar-item>
|
||||
<sidebar-item
|
||||
:link="{ name: $t('sidebar.wizard'), path: '/forms/wizard' }"
|
||||
></sidebar-item>
|
||||
</sidebar-item>
|
||||
<sidebar-item
|
||||
:link="{
|
||||
name: $t('sidebar.tables'),
|
||||
icon: 'tim-icons icon-puzzle-10'
|
||||
}"
|
||||
>
|
||||
<sidebar-item
|
||||
:link="{
|
||||
name: $t('sidebar.regularTables'),
|
||||
path: '/table-list/regular'
|
||||
}"
|
||||
></sidebar-item>
|
||||
<sidebar-item
|
||||
:link="{
|
||||
name: $t('sidebar.extendedTables'),
|
||||
path: '/table-list/extended'
|
||||
}"
|
||||
></sidebar-item>
|
||||
<sidebar-item
|
||||
:link="{
|
||||
name: $t('sidebar.paginatedTables'),
|
||||
path: '/table-list/paginated'
|
||||
}"
|
||||
></sidebar-item>
|
||||
</sidebar-item>
|
||||
<sidebar-item
|
||||
:link="{ name: $t('sidebar.maps'), icon: 'tim-icons icon-pin' }"
|
||||
>
|
||||
<sidebar-item
|
||||
:link="{ name: $t('sidebar.googleMaps'), path: '/maps/google' }"
|
||||
></sidebar-item>
|
||||
<sidebar-item
|
||||
:link="{
|
||||
name: $t('sidebar.fullScreenMaps'),
|
||||
path: '/maps/full-screen'
|
||||
}"
|
||||
></sidebar-item>
|
||||
<sidebar-item
|
||||
:link="{ name: $t('sidebar.vectorMaps'), path: '/maps/vector-map' }"
|
||||
></sidebar-item>
|
||||
</sidebar-item>
|
||||
<sidebar-item
|
||||
:link="{
|
||||
name: $t('sidebar.widgets'),
|
||||
icon: 'tim-icons icon-settings',
|
||||
path: '/widgets'
|
||||
}"
|
||||
></sidebar-item>
|
||||
<sidebar-item
|
||||
:link="{
|
||||
name: $t('sidebar.charts'),
|
||||
icon: 'tim-icons icon-chart-bar-32',
|
||||
path: '/charts'
|
||||
}"
|
||||
></sidebar-item>
|
||||
<sidebar-item
|
||||
:link="{
|
||||
name: $t('sidebar.calendar'),
|
||||
icon: 'tim-icons icon-time-alarm',
|
||||
path: '/calendar'
|
||||
}"
|
||||
></sidebar-item>
|
||||
</template>
|
||||
</side-bar>
|
||||
<!--Share plugin (for demo purposes). You can remove it if don't plan on using it-->
|
||||
<sidebar-share :background-color.sync="sidebarBackground"> </sidebar-share>
|
||||
<div class="main-panel" :data="sidebarBackground">
|
||||
<dashboard-navbar></dashboard-navbar>
|
||||
<router-view name="header"></router-view>
|
||||
|
||||
<div
|
||||
:class="{ content: !$route.meta.hideContent }"
|
||||
@click="toggleSidebar"
|
||||
>
|
||||
<zoom-center-transition :duration="200" mode="out-in">
|
||||
<!-- your content here -->
|
||||
<nuxt></nuxt>
|
||||
</zoom-center-transition>
|
||||
</div>
|
||||
<content-footer v-if="!$route.meta.hideFooter"></content-footer>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
/* eslint-disable no-new */
|
||||
import PerfectScrollbar from 'perfect-scrollbar';
|
||||
import 'perfect-scrollbar/css/perfect-scrollbar.css';
|
||||
import SidebarShare from './SidebarSharePlugin';
|
||||
function hasElement(className) {
|
||||
return document.getElementsByClassName(className).length > 0;
|
||||
}
|
||||
|
||||
function initScrollbar(className) {
|
||||
if (hasElement(className)) {
|
||||
new PerfectScrollbar(`.${className}`);
|
||||
} else {
|
||||
// try to init it later in case this component is loaded async
|
||||
setTimeout(() => {
|
||||
initScrollbar(className);
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
|
||||
import DashboardNavbar from './DashboardNavbar.vue';
|
||||
import ContentFooter from './ContentFooter.vue';
|
||||
import DashboardContent from './Content.vue';
|
||||
import { SlideYDownTransition, ZoomCenterTransition } from 'vue2-transitions';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
DashboardNavbar,
|
||||
ContentFooter,
|
||||
DashboardContent,
|
||||
SlideYDownTransition,
|
||||
ZoomCenterTransition,
|
||||
SidebarShare
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
sidebarBackground: 'vue' //vue|blue|orange|green|red|primary
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
toggleSidebar() {
|
||||
if (this.$sidebar.showSidebar) {
|
||||
this.$sidebar.displaySidebar(false);
|
||||
}
|
||||
},
|
||||
initScrollbar() {
|
||||
let docClasses = document.body.classList;
|
||||
let isWindows = navigator.platform.startsWith('Win');
|
||||
if (isWindows) {
|
||||
// if we are on windows OS we activate the perfectScrollbar function
|
||||
initScrollbar('sidebar');
|
||||
initScrollbar('main-panel');
|
||||
initScrollbar('sidebar-wrapper');
|
||||
|
||||
docClasses.add('perfect-scrollbar-on');
|
||||
} else {
|
||||
docClasses.add('perfect-scrollbar-off');
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.initScrollbar();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style lang="scss">
|
||||
$scaleSize: 0.95;
|
||||
@keyframes zoomIn95 {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: scale3d($scaleSize, $scaleSize, $scaleSize);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.main-panel .zoomIn {
|
||||
animation-name: zoomIn95;
|
||||
}
|
||||
|
||||
@keyframes zoomOut95 {
|
||||
from {
|
||||
opacity: 1;
|
||||
}
|
||||
to {
|
||||
opacity: 0;
|
||||
transform: scale3d($scaleSize, $scaleSize, $scaleSize);
|
||||
}
|
||||
}
|
||||
|
||||
.main-panel .zoomOut {
|
||||
animation-name: zoomOut95;
|
||||
}
|
||||
</style>
|
||||
133
APP/components/Layout/DashboardNavbar.vue
Executable file
133
APP/components/Layout/DashboardNavbar.vue
Executable file
@@ -0,0 +1,133 @@
|
||||
<template>
|
||||
<base-nav
|
||||
v-model="showMenu"
|
||||
class="navbar-absolute top-navbar"
|
||||
type="white"
|
||||
:transparent="true"
|
||||
>
|
||||
<div slot="brand" class="navbar-wrapper">
|
||||
<div
|
||||
class="navbar-toggle d-inline"
|
||||
:class="{ toggled: $sidebar.showSidebar }"
|
||||
>
|
||||
<button type="button" class="navbar-toggler" @click="toggleSidebar">
|
||||
<span class="navbar-toggler-bar bar1"></span>
|
||||
<span class="navbar-toggler-bar bar2"></span>
|
||||
<span class="navbar-toggler-bar bar3"></span>
|
||||
</button>
|
||||
</div>
|
||||
<a class="navbar-brand ml-xl-3 ml-5" href="#pablo">{{ routeName }}</a>
|
||||
</div>
|
||||
|
||||
<ul class="navbar-nav" :class="$rtl.isRTL ? 'mr-auto' : 'ml-auto'">
|
||||
<div class="search-bar input-group" @click="searchModalVisible = true">
|
||||
<button
|
||||
class="btn btn-link"
|
||||
id="search-button"
|
||||
data-toggle="modal"
|
||||
data-target="#searchModal"
|
||||
>
|
||||
<i class="tim-icons icon-zoom-split"></i>
|
||||
</button>
|
||||
<!-- You can choose types of search input -->
|
||||
</div>
|
||||
<modal
|
||||
:show.sync="searchModalVisible"
|
||||
class="modal-search"
|
||||
id="searchModal"
|
||||
:centered="false"
|
||||
:show-close="true"
|
||||
>
|
||||
<input
|
||||
slot="header"
|
||||
v-model="searchQuery"
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="inlineFormInputGroup"
|
||||
placeholder="SEARCH"
|
||||
/>
|
||||
</modal>
|
||||
<base-dropdown
|
||||
tag="li"
|
||||
:menu-on-right="!$rtl.isRTL"
|
||||
title-tag="a"
|
||||
title-classes="nav-link"
|
||||
class="nav-item"
|
||||
>
|
||||
|
||||
<template
|
||||
slot="title"
|
||||
>
|
||||
<div class="photo"><img src="img/mdchaparror.png" /></div>
|
||||
<b class="caret d-none d-lg-block d-xl-block"></b>
|
||||
<p class="d-lg-none">Log out</p>
|
||||
</template>
|
||||
<li class="nav-link">
|
||||
<a href="#" class="nav-item dropdown-item">Profile</a>
|
||||
</li>
|
||||
<li class="nav-link">
|
||||
<a href="#" class="nav-item dropdown-item">Settings</a>
|
||||
</li>
|
||||
<div class="dropdown-divider"></div>
|
||||
<li class="nav-link">
|
||||
<a href="#" class="nav-item dropdown-item">Log out</a>
|
||||
</li>
|
||||
</base-dropdown>
|
||||
</ul>
|
||||
</base-nav>
|
||||
</template>
|
||||
<script>
|
||||
import { CollapseTransition } from 'vue2-transitions';
|
||||
import { BaseNav, Modal } from '@/components';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
CollapseTransition,
|
||||
BaseNav,
|
||||
Modal
|
||||
},
|
||||
computed: {
|
||||
routeName() {
|
||||
const { path } = this.$route;
|
||||
let parts = path.split('/')
|
||||
if(parts == ','){
|
||||
return 'Dashboard';
|
||||
}
|
||||
return parts.map(p => this.capitalizeFirstLetter(p)).join(' ');
|
||||
},
|
||||
isRTL() {
|
||||
return this.$rtl.isRTL;
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
activeNotifications: false,
|
||||
showMenu: false,
|
||||
searchModalVisible: false,
|
||||
searchQuery: ''
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
capitalizeFirstLetter(string) {
|
||||
if (!string || typeof string !== 'string') {
|
||||
return ''
|
||||
}
|
||||
return string.charAt(0).toUpperCase() + string.slice(1);
|
||||
},
|
||||
closeDropDown() {
|
||||
this.activeNotifications = false;
|
||||
},
|
||||
toggleSidebar() {
|
||||
this.$sidebar.displaySidebar(!this.$sidebar.showSidebar);
|
||||
},
|
||||
toggleMenu() {
|
||||
this.showMenu = !this.showMenu;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style scoped>
|
||||
.top-navbar {
|
||||
top: 0px;
|
||||
}
|
||||
</style>
|
||||
25
APP/components/Layout/LoadingMainPanel.vue
Executable file
25
APP/components/Layout/LoadingMainPanel.vue
Executable file
@@ -0,0 +1,25 @@
|
||||
<template>
|
||||
<div class="row" v-loading="true" id="loading"></div>
|
||||
</template>
|
||||
<script>
|
||||
import Vue from 'vue';
|
||||
import { Loading } from 'element-ui';
|
||||
|
||||
Vue.use(Loading.directive);
|
||||
export default {};
|
||||
</script>
|
||||
<style>
|
||||
#loading {
|
||||
min-height: 200px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.el-loading-spinner .path {
|
||||
stroke: #66615b !important;
|
||||
}
|
||||
|
||||
.el-loading-mask {
|
||||
background: transparent !important;
|
||||
}
|
||||
</style>
|
||||
106
APP/components/Layout/SidebarSharePlugin.vue
Normal file
106
APP/components/Layout/SidebarSharePlugin.vue
Normal file
@@ -0,0 +1,106 @@
|
||||
<template>
|
||||
<div class="fixed-plugin" v-click-outside="closeDropDown">
|
||||
<div class="dropdown show-dropdown" :class="{ show: isOpen }">
|
||||
<a data-toggle="dropdown" class="settings-icon">
|
||||
<i class="fa fa-cog fa-2x" @click="toggleDropDown"> </i>
|
||||
</a>
|
||||
<ul class="dropdown-menu" :class="{ show: isOpen }">
|
||||
<li class="header-title">Sidebar Background</li>
|
||||
<li class="adjustments-line">
|
||||
<a class="switch-trigger background-color">
|
||||
<div class="badge-colors text-center">
|
||||
<span
|
||||
v-for="item in sidebarColors"
|
||||
:key="item.color"
|
||||
class="badge filter"
|
||||
:class="[`badge-${item.color}`, { active: item.active }]"
|
||||
:data-color="item.color"
|
||||
@click="changeSidebarBackground(item);"
|
||||
></span>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="header-title">Sidebar Mini</li>
|
||||
<li class="adjustments-line">
|
||||
<div class="togglebutton switch-change-color mt-3">
|
||||
<span class="label-switch">LIGHT MODE</span>
|
||||
<base-switch v-model="darkMode" @input="toggleMode"></base-switch>
|
||||
<span class="label-switch label-right">DARK MODE</span>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { BaseSwitch } from '@/components';
|
||||
|
||||
export default {
|
||||
name: 'sidebar-share',
|
||||
components: {
|
||||
BaseSwitch
|
||||
},
|
||||
props: {
|
||||
backgroundColor: String
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
sidebarMini: true,
|
||||
darkMode: false,
|
||||
isOpen: false,
|
||||
sidebarColors: [
|
||||
{ color: 'primary', active: false, value: 'primary' },
|
||||
{ color: 'vue', active: true, value: 'vue' },
|
||||
{ color: 'info', active: false, value: 'blue' },
|
||||
{ color: 'success', active: false, value: 'green' }
|
||||
]
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
toggleDropDown() {
|
||||
this.isOpen = !this.isOpen;
|
||||
},
|
||||
closeDropDown() {
|
||||
this.isOpen = false;
|
||||
},
|
||||
toggleList(list, itemToActivate) {
|
||||
list.forEach(listItem => {
|
||||
listItem.active = false;
|
||||
});
|
||||
itemToActivate.active = true;
|
||||
},
|
||||
changeSidebarBackground(item) {
|
||||
this.$emit('update:backgroundColor', item.value);
|
||||
this.toggleList(this.sidebarColors, item);
|
||||
},
|
||||
toggleMode(type) {
|
||||
let docClasses = document.body.classList;
|
||||
if (type) {
|
||||
docClasses.remove('white-content');
|
||||
} else {
|
||||
docClasses.add('white-content');
|
||||
}
|
||||
},
|
||||
minimizeSidebar() {
|
||||
this.$sidebar.toggleMinimize();
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
@import '~@/assets/sass/dashboard/custom/variables';
|
||||
|
||||
.settings-icon {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.badge-vue {
|
||||
background-color: $vue;
|
||||
}
|
||||
</style>
|
||||
49
APP/components/Layout/starter/SampleFooter.vue
Normal file
49
APP/components/Layout/starter/SampleFooter.vue
Normal file
@@ -0,0 +1,49 @@
|
||||
<template>
|
||||
<footer class="footer">
|
||||
<div class="container-fluid">
|
||||
<ul class="nav">
|
||||
<li class="nav-item">
|
||||
<a
|
||||
href="https://example.com"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
class="nav-link"
|
||||
>
|
||||
Link
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a
|
||||
href="https://example.com"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
class="nav-link"
|
||||
>
|
||||
Another Link
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="copyright">
|
||||
© {{ year }}, made with <i class="tim-icons icon-heart-2"></i> by
|
||||
|
||||
<a
|
||||
href="https://www.creative-tim.com/?ref=pdf-vuejs"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>Creative Tim</a
|
||||
>
|
||||
for a better web.
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
year: new Date().getFullYear()
|
||||
};
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style></style>
|
||||
137
APP/components/Layout/starter/SampleNavbar.vue
Normal file
137
APP/components/Layout/starter/SampleNavbar.vue
Normal file
@@ -0,0 +1,137 @@
|
||||
<template>
|
||||
<base-nav
|
||||
v-model="showMenu"
|
||||
class="navbar-absolute top-navbar"
|
||||
type="white"
|
||||
:transparent="true"
|
||||
>
|
||||
<div slot="brand" class="navbar-wrapper">
|
||||
<div
|
||||
class="navbar-toggle d-inline"
|
||||
:class="{ toggled: $sidebar.showSidebar }"
|
||||
>
|
||||
<button type="button" class="navbar-toggler" @click="toggleSidebar">
|
||||
<span class="navbar-toggler-bar bar1"></span>
|
||||
<span class="navbar-toggler-bar bar2"></span>
|
||||
<span class="navbar-toggler-bar bar3"></span>
|
||||
</button>
|
||||
</div>
|
||||
<a class="navbar-brand" href="#pablo">{{ routeName }}</a>
|
||||
</div>
|
||||
|
||||
<ul class="navbar-nav" :class="$rtl.isRTL ? 'mr-auto' : 'ml-auto'">
|
||||
<div class="search-bar input-group" @click="searchModalVisible = true">
|
||||
<!--
|
||||
<input type="text" class="form-control" placeholder="Search...">
|
||||
<div class="input-group-addon"><i class="tim-icons icon-zoom-split"></i></div>
|
||||
-->
|
||||
<button
|
||||
class="btn btn-link"
|
||||
id="search-button"
|
||||
data-toggle="modal"
|
||||
data-target="#searchModal"
|
||||
>
|
||||
<i class="tim-icons icon-zoom-split"></i>
|
||||
</button>
|
||||
<!-- You can choose types of search input -->
|
||||
</div>
|
||||
<modal
|
||||
:show.sync="searchModalVisible"
|
||||
class="modal-search"
|
||||
id="searchModal"
|
||||
:centered="false"
|
||||
:show-close="true"
|
||||
>
|
||||
<input
|
||||
slot="header"
|
||||
v-model="searchQuery"
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="inlineFormInputGroup"
|
||||
placeholder="SEARCH"
|
||||
/>
|
||||
</modal>
|
||||
<base-dropdown
|
||||
tag="li"
|
||||
:menu-on-right="!$rtl.isRTL"
|
||||
title-tag="a"
|
||||
class="nav-item"
|
||||
title-classes="nav-link"
|
||||
menu-classes="dropdown-navbar"
|
||||
>
|
||||
<template
|
||||
slot="title"
|
||||
>
|
||||
<div class="photo"><img src="img/mike.jpg" /></div>
|
||||
<b class="caret d-none d-lg-block d-xl-block"></b>
|
||||
<p class="d-lg-none">Log out</p>
|
||||
</template>
|
||||
<li class="nav-link">
|
||||
<a href="#" class="nav-item dropdown-item">Profile</a>
|
||||
</li>
|
||||
<li class="nav-link">
|
||||
<a href="#" class="nav-item dropdown-item">Settings</a>
|
||||
</li>
|
||||
<div class="dropdown-divider"></div>
|
||||
<li class="nav-link">
|
||||
<a href="#" class="nav-item dropdown-item">Log out</a>
|
||||
</li>
|
||||
</base-dropdown>
|
||||
</ul>
|
||||
</base-nav>
|
||||
</template>
|
||||
<script>
|
||||
import { CollapseTransition } from 'vue2-transitions';
|
||||
import { BaseNav, Modal } from '@/components';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
CollapseTransition,
|
||||
BaseNav,
|
||||
Modal
|
||||
},
|
||||
computed: {
|
||||
routeName() {
|
||||
const { path } = this.$route;
|
||||
let parts = path.split('/')
|
||||
return parts.map(p => this.capitalizeFirstLetter(p)).join(' ');
|
||||
},
|
||||
isRTL() {
|
||||
return this.$rtl.isRTL;
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
activeNotifications: false,
|
||||
showMenu: false,
|
||||
searchModalVisible: false,
|
||||
searchQuery: ''
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
capitalizeFirstLetter(string) {
|
||||
return string.charAt(0).toUpperCase() + string.slice(1);
|
||||
},
|
||||
toggleNotificationDropDown() {
|
||||
this.activeNotifications = !this.activeNotifications;
|
||||
},
|
||||
closeDropDown() {
|
||||
this.activeNotifications = false;
|
||||
},
|
||||
toggleSidebar() {
|
||||
this.$sidebar.displaySidebar(!this.$sidebar.showSidebar);
|
||||
},
|
||||
hideSidebar() {
|
||||
this.$sidebar.displaySidebar(false);
|
||||
},
|
||||
toggleMenu() {
|
||||
this.showMenu = !this.showMenu;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style scoped>
|
||||
.top-navbar {
|
||||
top: 0px;
|
||||
}
|
||||
</style>
|
||||
25
APP/components/LoadingPanel.vue
Executable file
25
APP/components/LoadingPanel.vue
Executable file
@@ -0,0 +1,25 @@
|
||||
<template>
|
||||
<div class="row" v-loading="true" id="loading"></div>
|
||||
</template>
|
||||
<script>
|
||||
import Vue from 'vue';
|
||||
import { Loading } from 'element-ui';
|
||||
|
||||
Vue.use(Loading.directive);
|
||||
export default {};
|
||||
</script>
|
||||
<style>
|
||||
#loading {
|
||||
min-height: 200px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.el-loading-spinner .path {
|
||||
stroke: #66615b !important;
|
||||
}
|
||||
|
||||
.el-loading-mask {
|
||||
background: transparent !important;
|
||||
}
|
||||
</style>
|
||||
170
APP/components/Modal.vue
Normal file
170
APP/components/Modal.vue
Normal file
@@ -0,0 +1,170 @@
|
||||
<template>
|
||||
<SlideYUpTransition :duration="animationDuration">
|
||||
<div
|
||||
ref="modal"
|
||||
class="modal fade"
|
||||
@click.self="closeModal"
|
||||
:class="[
|
||||
{ 'show d-block': show },
|
||||
{ 'd-none': !show },
|
||||
{ 'modal-mini': type === 'mini' }
|
||||
]"
|
||||
v-show="show"
|
||||
tabindex="-1"
|
||||
role="dialog"
|
||||
:aria-hidden="!show"
|
||||
>
|
||||
<div
|
||||
class="modal-dialog"
|
||||
role="document"
|
||||
:class="[
|
||||
{ 'modal-notice': type === 'notice' },
|
||||
{ 'modal-dialog-centered': centered },
|
||||
modalClasses
|
||||
]"
|
||||
>
|
||||
<div
|
||||
class="modal-content"
|
||||
:class="[
|
||||
gradient ? `bg-gradient-${gradient}` : '',
|
||||
modalContentClasses
|
||||
]"
|
||||
>
|
||||
<div
|
||||
class="modal-header"
|
||||
:class="[headerClasses]"
|
||||
v-if="$slots.header"
|
||||
>
|
||||
<slot name="header"></slot>
|
||||
<slot name="close-button">
|
||||
<button
|
||||
type="button"
|
||||
class="close"
|
||||
v-if="showClose"
|
||||
@click="closeModal"
|
||||
data-dismiss="modal"
|
||||
aria-label="Close"
|
||||
>
|
||||
<i class="tim-icons icon-simple-remove"></i>
|
||||
</button>
|
||||
</slot>
|
||||
</div>
|
||||
|
||||
<div v-if="$slots.default" class="modal-body" :class="bodyClasses">
|
||||
<slot></slot>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer" :class="footerClasses" v-if="$slots.footer">
|
||||
<slot name="footer"></slot>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</SlideYUpTransition>
|
||||
</template>
|
||||
<script>
|
||||
import { SlideYUpTransition } from 'vue2-transitions';
|
||||
|
||||
export default {
|
||||
name: 'modal',
|
||||
components: {
|
||||
SlideYUpTransition
|
||||
},
|
||||
props: {
|
||||
show: Boolean,
|
||||
showClose: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
centered: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
appendToBody: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
description: 'Whether modal should be appended to document body'
|
||||
},
|
||||
scrollToBottom: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
description: "Whether modal should scroll to it's bottom automatically"
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: '',
|
||||
validator(value) {
|
||||
let acceptedValues = ['', 'notice', 'mini'];
|
||||
return acceptedValues.indexOf(value) !== -1;
|
||||
},
|
||||
description: 'Modal type (notice|mini|"") '
|
||||
},
|
||||
modalClasses: {
|
||||
type: [Object, String],
|
||||
description: 'Modal dialog css classes'
|
||||
},
|
||||
modalContentClasses: {
|
||||
type: [Object, String],
|
||||
description: 'Modal dialog content css classes'
|
||||
},
|
||||
gradient: {
|
||||
type: String,
|
||||
description: 'Modal gradient type (danger, primary etc)'
|
||||
},
|
||||
headerClasses: {
|
||||
type: [Object, String],
|
||||
description: 'Modal Header css classes'
|
||||
},
|
||||
bodyClasses: {
|
||||
type: [Object, String],
|
||||
description: 'Modal Body css classes'
|
||||
},
|
||||
footerClasses: {
|
||||
type: [Object, String],
|
||||
description: 'Modal Footer css classes'
|
||||
},
|
||||
animationDuration: {
|
||||
type: Number,
|
||||
default: 500,
|
||||
description: 'Modal transition duration'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
closeModal() {
|
||||
this.$emit('update:show', false);
|
||||
this.$emit('close');
|
||||
},
|
||||
scrollModalToBottom() {
|
||||
if (!this.scrollToBottom) return;
|
||||
let elm = this.$refs.modal;
|
||||
elm.scrollTop = elm.scrollHeight - elm.clientHeight;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
if (this.appendToBody) {
|
||||
document.body.appendChild(this.$el);
|
||||
}
|
||||
},
|
||||
destroyed() {
|
||||
if (this.appendToBody && this.$el && this.$el.parentNode) {
|
||||
this.$el.parentNode.removeChild(this.$el);
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
show(val) {
|
||||
let documentClasses = document.body.classList;
|
||||
if (val) {
|
||||
documentClasses.add('modal-open');
|
||||
this.$nextTick(this.scrollModalToBottom);
|
||||
} else {
|
||||
documentClasses.remove('modal-open');
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
.modal.show {
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
</style>
|
||||
136
APP/components/Navbar/BaseNav.vue
Normal file
136
APP/components/Navbar/BaseNav.vue
Normal file
@@ -0,0 +1,136 @@
|
||||
<template>
|
||||
<nav :class="classes" class="navbar">
|
||||
<div :class="containerClasses">
|
||||
<slot name="brand"></slot>
|
||||
|
||||
<slot name="toggle-button">
|
||||
<button
|
||||
class="navbar-toggler collapsed"
|
||||
v-if="hasMenu"
|
||||
type="button"
|
||||
@click="toggleMenu"
|
||||
aria-expanded="false"
|
||||
aria-label="Toggle navigation"
|
||||
>
|
||||
<span class="navbar-toggler-bar navbar-kebab"></span>
|
||||
<span class="navbar-toggler-bar navbar-kebab"></span>
|
||||
<span class="navbar-toggler-bar navbar-kebab"></span>
|
||||
</button>
|
||||
</slot>
|
||||
|
||||
<CollapseTransition
|
||||
@after-leave="onTransitionEnd"
|
||||
@before-enter="onTransitionStart"
|
||||
>
|
||||
<div
|
||||
class="collapse navbar-collapse show"
|
||||
:class="menuClasses"
|
||||
v-show="show"
|
||||
>
|
||||
<slot></slot>
|
||||
</div>
|
||||
</CollapseTransition>
|
||||
</div>
|
||||
</nav>
|
||||
</template>
|
||||
<script>
|
||||
import { CollapseTransition } from 'vue2-transitions';
|
||||
|
||||
export default {
|
||||
name: 'base-nav',
|
||||
props: {
|
||||
show: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
description:
|
||||
'Whether navbar menu is shown (valid for viewports < specified by `expand` prop)'
|
||||
},
|
||||
transparent: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
description: 'Whether navbar is transparent'
|
||||
},
|
||||
expand: {
|
||||
type: String,
|
||||
default: 'lg',
|
||||
description: 'Breakpoint where nav should expand'
|
||||
},
|
||||
menuClasses: {
|
||||
type: [String, Object, Array],
|
||||
default: '',
|
||||
description:
|
||||
'Navbar menu (items) classes. Can be used to align menu items to the right/left'
|
||||
},
|
||||
containerClasses: {
|
||||
type: [String, Object, Array],
|
||||
default: 'container-fluid',
|
||||
description:
|
||||
'Container classes. Can be used to control container classes (contains both navbar brand and menu items)'
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: 'white',
|
||||
validator(value) {
|
||||
return [
|
||||
'dark',
|
||||
'success',
|
||||
'danger',
|
||||
'warning',
|
||||
'white',
|
||||
'primary',
|
||||
'info',
|
||||
'vue'
|
||||
].includes(value);
|
||||
},
|
||||
description: 'Navbar color type'
|
||||
}
|
||||
},
|
||||
model: {
|
||||
prop: 'show',
|
||||
event: 'change'
|
||||
},
|
||||
components: {
|
||||
CollapseTransition
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
transitionFinished: true
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
classes() {
|
||||
let color = `bg-${this.type}`;
|
||||
let classes = [
|
||||
{ 'navbar-transparent': !this.show && this.transparent },
|
||||
{ [`navbar-expand-${this.expand}`]: this.expand }
|
||||
];
|
||||
if (this.position) {
|
||||
classes.push(`navbar-${this.position}`);
|
||||
}
|
||||
if (
|
||||
!this.transparent ||
|
||||
(this.show && !this.transitionFinished) ||
|
||||
(!this.show && !this.transitionFinished)
|
||||
) {
|
||||
classes.push(color);
|
||||
}
|
||||
return classes;
|
||||
},
|
||||
hasMenu() {
|
||||
return this.$slots.default;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
toggleMenu() {
|
||||
this.$emit('change', !this.show);
|
||||
},
|
||||
onTransitionStart() {
|
||||
this.transitionFinished = false;
|
||||
},
|
||||
onTransitionEnd() {
|
||||
this.transitionFinished = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style></style>
|
||||
21
APP/components/Navbar/NavbarToggleButton.vue
Normal file
21
APP/components/Navbar/NavbarToggleButton.vue
Normal file
@@ -0,0 +1,21 @@
|
||||
<template>
|
||||
<button
|
||||
type="button"
|
||||
class="navbar-toggler collapsed"
|
||||
data-toggle="collapse"
|
||||
data-target="#navbar"
|
||||
aria-controls="navbarSupportedContent"
|
||||
aria-expanded="false"
|
||||
aria-label="Toggle navigation"
|
||||
>
|
||||
<span class="navbar-toggler-bar bar1"></span>
|
||||
<span class="navbar-toggler-bar bar2"></span>
|
||||
<span class="navbar-toggler-bar bar3"></span>
|
||||
</button>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'navbar-toggle-button'
|
||||
};
|
||||
</script>
|
||||
<style></style>
|
||||
186
APP/components/NotificationPlugin/Notification.vue
Normal file
186
APP/components/NotificationPlugin/Notification.vue
Normal file
@@ -0,0 +1,186 @@
|
||||
<template>
|
||||
<div
|
||||
@click="tryClose"
|
||||
data-notify="container"
|
||||
class="alert open"
|
||||
:class="[
|
||||
{ 'alert-with-icon': icon },
|
||||
verticalAlign,
|
||||
horizontalAlign,
|
||||
alertType
|
||||
]"
|
||||
role="alert"
|
||||
:style="customPosition"
|
||||
data-notify-position="top-center"
|
||||
>
|
||||
<button
|
||||
v-if="showClose"
|
||||
type="button"
|
||||
aria-hidden="true"
|
||||
class="close col-xs-1"
|
||||
data-notify="dismiss"
|
||||
@click="close"
|
||||
>
|
||||
<i class="tim-icons icon-simple-remove"></i>
|
||||
</button>
|
||||
|
||||
<span v-if="icon" data-notify="icon" :class="['alert-icon', icon]"></span>
|
||||
<span data-notify="message">
|
||||
<span v-if="title" class="title"
|
||||
><b>{{ title }}<br /></b
|
||||
></span>
|
||||
<span v-if="message" v-html="message"></span>
|
||||
<content-render
|
||||
v-if="!message && component"
|
||||
:component="component"
|
||||
></content-render>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'notification',
|
||||
components: {
|
||||
contentRender: {
|
||||
props: ['component'],
|
||||
render: h => h(this.component)
|
||||
}
|
||||
},
|
||||
props: {
|
||||
message: String,
|
||||
title: String,
|
||||
icon: String,
|
||||
verticalAlign: {
|
||||
type: String,
|
||||
default: 'top',
|
||||
validator: value => {
|
||||
let acceptedValues = ['top', 'bottom'];
|
||||
return acceptedValues.indexOf(value) !== -1;
|
||||
}
|
||||
},
|
||||
horizontalAlign: {
|
||||
type: String,
|
||||
default: 'right',
|
||||
validator: value => {
|
||||
let acceptedValues = ['left', 'center', 'right'];
|
||||
return acceptedValues.indexOf(value) !== -1;
|
||||
}
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: 'info',
|
||||
validator: value => {
|
||||
let acceptedValues = [
|
||||
'info',
|
||||
'primary',
|
||||
'danger',
|
||||
'warning',
|
||||
'success'
|
||||
];
|
||||
return acceptedValues.indexOf(value) !== -1;
|
||||
}
|
||||
},
|
||||
timeout: {
|
||||
type: Number,
|
||||
default: 5000,
|
||||
validator: value => {
|
||||
return value >= 0;
|
||||
}
|
||||
},
|
||||
timestamp: {
|
||||
type: Date,
|
||||
default: () => new Date()
|
||||
},
|
||||
component: {
|
||||
type: [Object, Function]
|
||||
},
|
||||
showClose: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
closeOnClick: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
clickHandler: Function
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
elmHeight: 0
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
hasIcon() {
|
||||
return this.icon && this.icon.length > 0;
|
||||
},
|
||||
alertType() {
|
||||
return `alert-${this.type}`;
|
||||
},
|
||||
customPosition() {
|
||||
let initialMargin = 20;
|
||||
let alertHeight = this.elmHeight + 10;
|
||||
let sameAlerts = this.$notifications.state.filter(alert => {
|
||||
return (
|
||||
alert.horizontalAlign === this.horizontalAlign &&
|
||||
alert.verticalAlign === this.verticalAlign
|
||||
);
|
||||
});
|
||||
let sameAlertsCount = 1
|
||||
let currentIndex = sameAlerts.findIndex(n => n.timestamp === this.timestamp)
|
||||
if (this.$notifications.settings.overlap) {
|
||||
sameAlertsCount = 1;
|
||||
}
|
||||
if (currentIndex !== -1) {
|
||||
sameAlertsCount = currentIndex + 1
|
||||
}
|
||||
let pixels = (sameAlertsCount - 1) * alertHeight + initialMargin;
|
||||
let styles = {};
|
||||
if (this.verticalAlign === 'top') {
|
||||
styles.top = `${pixels}px`;
|
||||
} else {
|
||||
styles.bottom = `${pixels}px`;
|
||||
}
|
||||
return styles;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
close() {
|
||||
this.$emit('close', this.timestamp);
|
||||
},
|
||||
tryClose(evt) {
|
||||
if (this.clickHandler) {
|
||||
this.clickHandler(evt, this);
|
||||
}
|
||||
if (this.closeOnClick) {
|
||||
this.close();
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.elmHeight = this.$el.clientHeight;
|
||||
if (this.timeout) {
|
||||
setTimeout(this.close, this.timeout);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.notifications .alert {
|
||||
position: fixed;
|
||||
z-index: 10000;
|
||||
|
||||
&[data-notify='container'] {
|
||||
width: 400px;
|
||||
}
|
||||
|
||||
&.center {
|
||||
margin: 0 auto;
|
||||
}
|
||||
&.left {
|
||||
left: 20px;
|
||||
}
|
||||
&.right {
|
||||
right: 20px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
80
APP/components/NotificationPlugin/Notifications.vue
Normal file
80
APP/components/NotificationPlugin/Notifications.vue
Normal file
@@ -0,0 +1,80 @@
|
||||
<template>
|
||||
<div class="notifications">
|
||||
<transition-group :name="transitionName" :mode="transitionMode">
|
||||
<notification
|
||||
v-for="notification in notifications"
|
||||
v-bind="notification"
|
||||
:clickHandler="notification.onClick"
|
||||
:key="notification.timestamp.getTime()"
|
||||
@close="removeNotification"
|
||||
>
|
||||
</notification>
|
||||
</transition-group>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import Notification from './Notification.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Notification
|
||||
},
|
||||
props: {
|
||||
transitionName: {
|
||||
type: String,
|
||||
default: 'list'
|
||||
},
|
||||
transitionMode: {
|
||||
type: String,
|
||||
default: 'in-out'
|
||||
},
|
||||
overlap: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
notifications: this.$notifications.state
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
removeNotification(timestamp) {
|
||||
this.$notifications.removeNotification(timestamp);
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.$notifications.settings.overlap = this.overlap;
|
||||
},
|
||||
watch: {
|
||||
overlap: function(newVal) {
|
||||
this.$notifications.settings.overlap = newVal;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.notifications {
|
||||
.list-move {
|
||||
transition: transform 0.3s, opacity 0.4s;
|
||||
}
|
||||
.list-item {
|
||||
display: inline-block;
|
||||
}
|
||||
.list-enter-active {
|
||||
transition: transform 0.2s ease-in, opacity 0.4s ease-in;
|
||||
}
|
||||
.list-leave-active {
|
||||
transition: transform 1s ease-out, opacity 0.4s ease-out;
|
||||
}
|
||||
|
||||
.list-enter {
|
||||
opacity: 0;
|
||||
transform: scale(1.1);
|
||||
}
|
||||
.list-leave-to {
|
||||
opacity: 0;
|
||||
transform: scale(1.2, 0.7);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
71
APP/components/NotificationPlugin/index.js
Normal file
71
APP/components/NotificationPlugin/index.js
Normal file
@@ -0,0 +1,71 @@
|
||||
import Notifications from './Notifications.vue';
|
||||
|
||||
const NotificationStore = {
|
||||
state: [], // here the notifications will be added
|
||||
settings: {
|
||||
overlap: false,
|
||||
verticalAlign: 'top',
|
||||
horizontalAlign: 'right',
|
||||
type: 'info',
|
||||
timeout: 5000,
|
||||
closeOnClick: true,
|
||||
showClose: true,
|
||||
order: 'normal' // normal | reverse (When reverse, each notification will be added on top)
|
||||
},
|
||||
setOptions(options) {
|
||||
this.settings = Object.assign(this.settings, options);
|
||||
},
|
||||
removeNotification(timestamp) {
|
||||
const indexToDelete = this.state.findIndex(n => n.timestamp === timestamp);
|
||||
if (indexToDelete !== -1) {
|
||||
this.state.splice(indexToDelete, 1);
|
||||
}
|
||||
},
|
||||
addNotification(notification) {
|
||||
if (typeof notification === 'string' || notification instanceof String) {
|
||||
notification = { message: notification };
|
||||
}
|
||||
notification.timestamp = new Date();
|
||||
notification.timestamp.setMilliseconds(
|
||||
notification.timestamp.getMilliseconds() + this.state.length
|
||||
);
|
||||
notification = Object.assign({}, this.settings, notification);
|
||||
if (this.settings.order === 'reverse') {
|
||||
this.state.unshift(notification)
|
||||
} else {
|
||||
this.state.push(notification)
|
||||
}
|
||||
},
|
||||
notify(notification) {
|
||||
if (Array.isArray(notification)) {
|
||||
notification.forEach(notificationInstance => {
|
||||
this.addNotification(notificationInstance);
|
||||
});
|
||||
} else {
|
||||
this.addNotification(notification);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const NotificationsPlugin = {
|
||||
install(Vue, options) {
|
||||
let app = new Vue({
|
||||
data: {
|
||||
notificationStore: NotificationStore
|
||||
},
|
||||
methods: {
|
||||
notify(notification) {
|
||||
this.notificationStore.notify(notification);
|
||||
}
|
||||
}
|
||||
});
|
||||
Vue.prototype.$notify = app.notify;
|
||||
Vue.prototype.$notifications = app.notificationStore;
|
||||
Vue.component('Notifications', Notifications);
|
||||
if (options) {
|
||||
NotificationStore.setOptions(options);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default NotificationsPlugin;
|
||||
7
APP/components/README.md
Normal file
7
APP/components/README.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# COMPONENTS
|
||||
|
||||
**This directory is not required, you can delete it if you don't want to use it.**
|
||||
|
||||
The components directory contains your Vue.js Components.
|
||||
|
||||
_Nuxt.js doesn't supercharge these components._
|
||||
90
APP/components/SidebarPlugin/SideBar.vue
Executable file
90
APP/components/SidebarPlugin/SideBar.vue
Executable file
@@ -0,0 +1,90 @@
|
||||
<template>
|
||||
<div class="sidebar" :data="backgroundColor">
|
||||
<div class="sidebar-wrapper" ref="sidebarScrollArea">
|
||||
<div class="logo">
|
||||
<span class="simple-text logo-normal">
|
||||
{{ title }}
|
||||
</span>
|
||||
</div>
|
||||
<slot></slot>
|
||||
<ul class="nav">
|
||||
<slot name="links">
|
||||
<sidebar-item
|
||||
v-for="(link, index) in sidebarLinks"
|
||||
:key="link.name + index"
|
||||
:link="link"
|
||||
>
|
||||
</sidebar-item>
|
||||
</slot>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'sidebar',
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
default: 'Creative Tim',
|
||||
description: 'Sidebar title'
|
||||
},
|
||||
shortTitle: {
|
||||
type: String,
|
||||
default: 'CT',
|
||||
description: 'Sidebar short title'
|
||||
},
|
||||
logo: {
|
||||
type: String,
|
||||
default: 'http://demos.creative-tim.com/nuxt-black-dashboard-pro/img/icon-nuxt.svg',
|
||||
description: 'Sidebar app logo'
|
||||
},
|
||||
backgroundColor: {
|
||||
type: String,
|
||||
default: 'vue',
|
||||
validator: value => {
|
||||
let acceptedValues = [
|
||||
'',
|
||||
'vue',
|
||||
'blue',
|
||||
'green',
|
||||
'primary'
|
||||
];
|
||||
return acceptedValues.indexOf(value) !== -1;
|
||||
},
|
||||
description:
|
||||
'Sidebar background color (vue|blue|green|orange|red|primary)'
|
||||
},
|
||||
sidebarLinks: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
description:
|
||||
"List of sidebar links as an array if you don't want to use components for these."
|
||||
},
|
||||
autoClose: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
description:
|
||||
'Whether sidebar should autoclose on mobile when clicking an item'
|
||||
}
|
||||
},
|
||||
provide() {
|
||||
return {
|
||||
autoClose: this.autoClose
|
||||
};
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (this.$sidebar.showSidebar) {
|
||||
this.$sidebar.showSidebar = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
@media (min-width: 992px) {
|
||||
.navbar-search-form-mobile,
|
||||
.nav-mobile-menu {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
178
APP/components/SidebarPlugin/SidebarItem.vue
Executable file
178
APP/components/SidebarPlugin/SidebarItem.vue
Executable file
@@ -0,0 +1,178 @@
|
||||
<template>
|
||||
<component
|
||||
:is="baseComponent"
|
||||
:to="link.path ? link.path : '/'"
|
||||
:class="{ active: isActive }"
|
||||
tag="li"
|
||||
>
|
||||
<a
|
||||
v-if="isMenu"
|
||||
class="sidebar-menu-item"
|
||||
:aria-expanded="!collapsed"
|
||||
data-toggle="collapse"
|
||||
@click.prevent="collapseMenu"
|
||||
>
|
||||
|
||||
</a>
|
||||
|
||||
<slot
|
||||
name="title"
|
||||
v-if="children.length === 0 && !$slots.default && link.path"
|
||||
>
|
||||
<component
|
||||
:to="link.path"
|
||||
@click.native="linkClick"
|
||||
:is="elementType(link, false)"
|
||||
:class="{ active: link.active }"
|
||||
:target="link.target"
|
||||
:href="link.path"
|
||||
>
|
||||
<template v-if="addLink">
|
||||
<span class="sidebar-normal">{{ link.name }}</span>
|
||||
</template>
|
||||
<template v-else>
|
||||
<i :class="link.icon"></i>
|
||||
<p>{{ link.name }}</p>
|
||||
</template>
|
||||
</component>
|
||||
</slot>
|
||||
</component>
|
||||
</template>
|
||||
<script>
|
||||
import { CollapseTransition } from 'vue2-transitions';
|
||||
|
||||
export default {
|
||||
name: 'sidebar-item',
|
||||
components: {
|
||||
CollapseTransition
|
||||
},
|
||||
props: {
|
||||
menu: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
description:
|
||||
"Whether the item is a menu. Most of the item it's not used and should be used only if you want to override the default behavior."
|
||||
},
|
||||
link: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {
|
||||
name: '',
|
||||
path: '',
|
||||
children: []
|
||||
};
|
||||
},
|
||||
description:
|
||||
'Sidebar link. Can contain name, path, icon and other attributes. See examples for more info'
|
||||
}
|
||||
},
|
||||
provide() {
|
||||
return {
|
||||
addLink: this.addChild,
|
||||
removeLink: this.removeChild
|
||||
};
|
||||
},
|
||||
inject: {
|
||||
addLink: { default: null },
|
||||
removeLink: { default: null },
|
||||
autoClose: {
|
||||
default: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
children: [],
|
||||
collapsed: true
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
baseComponent() {
|
||||
return this.isMenu || this.link.isRoute ? 'li' : 'nuxt-link';
|
||||
},
|
||||
linkPrefix() {
|
||||
if (this.link.name) {
|
||||
let words = this.link.name.split(' ');
|
||||
return words.map(word => word.substring(0, 1)).join('');
|
||||
}
|
||||
},
|
||||
isMenu() {
|
||||
if (!this.$slots.default) {
|
||||
return false
|
||||
}
|
||||
return this.$slots.default.some(item => item.tag.endsWith('sidebar-item'))
|
||||
},
|
||||
isActive() {
|
||||
if (this.$route && this.$route.path) {
|
||||
let matchingRoute = this.children.find(c =>
|
||||
this.$route.path.startsWith(c.link.path)
|
||||
);
|
||||
if (matchingRoute !== undefined) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
addChild(item) {
|
||||
const index = this.$slots.default.indexOf(item.$vnode);
|
||||
this.children.splice(index, 0, item);
|
||||
},
|
||||
removeChild(item) {
|
||||
const tabs = this.children;
|
||||
const index = tabs.indexOf(item);
|
||||
tabs.splice(index, 1);
|
||||
},
|
||||
elementType(link, isParent = true) {
|
||||
if (link.isRoute === false) {
|
||||
return isParent ? 'li' : 'a';
|
||||
} else {
|
||||
return 'nuxt-link';
|
||||
}
|
||||
},
|
||||
linkAbbreviation(name) {
|
||||
const matches = name.match(/\b(\w)/g);
|
||||
return matches.join('');
|
||||
},
|
||||
linkClick() {
|
||||
if (
|
||||
this.autoClose &&
|
||||
this.$sidebar &&
|
||||
this.$sidebar.showSidebar === true
|
||||
) {
|
||||
this.$sidebar.displaySidebar(false);
|
||||
}
|
||||
},
|
||||
collapseMenu() {
|
||||
this.collapsed = !this.collapsed;
|
||||
},
|
||||
collapseSubMenu(link) {
|
||||
link.collapsed = !link.collapsed;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
if (this.addLink) {
|
||||
this.addLink(this);
|
||||
}
|
||||
if (this.link.collapsed !== undefined) {
|
||||
this.collapsed = this.link.collapsed;
|
||||
}
|
||||
if (this.isActive && this.isMenu) {
|
||||
this.collapsed = false;
|
||||
}
|
||||
},
|
||||
destroyed() {
|
||||
if (this.$el && this.$el.parentNode) {
|
||||
this.$el.parentNode.removeChild(this.$el);
|
||||
}
|
||||
if (this.removeLink) {
|
||||
this.removeLink(this);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
.sidebar-menu-item {
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
28
APP/components/SidebarPlugin/index.js
Executable file
28
APP/components/SidebarPlugin/index.js
Executable file
@@ -0,0 +1,28 @@
|
||||
import Sidebar from './SideBar.vue';
|
||||
import SidebarItem from './SidebarItem.vue';
|
||||
|
||||
const SidebarStore = {
|
||||
showSidebar: false,
|
||||
sidebarLinks: [],
|
||||
displaySidebar(value) {
|
||||
this.showSidebar = value;
|
||||
},
|
||||
};
|
||||
|
||||
const SidebarPlugin = {
|
||||
install(Vue, options) {
|
||||
if (options && options.sidebarLinks) {
|
||||
SidebarStore.sidebarLinks = options.sidebarLinks;
|
||||
}
|
||||
let app = new Vue({
|
||||
data: {
|
||||
sidebarStore: SidebarStore
|
||||
}
|
||||
});
|
||||
Vue.prototype.$sidebar = app.sidebarStore;
|
||||
Vue.component('side-bar', Sidebar);
|
||||
Vue.component('sidebar-item', SidebarItem);
|
||||
}
|
||||
};
|
||||
|
||||
export default SidebarPlugin;
|
||||
142
APP/components/UserProfile/EditProfileForm.vue
Executable file
142
APP/components/UserProfile/EditProfileForm.vue
Executable file
@@ -0,0 +1,142 @@
|
||||
<template>
|
||||
<card>
|
||||
<h5 slot="header" class="title">Edit Profile</h5>
|
||||
<form @submit.prevent="updateProfile">
|
||||
<div class="row">
|
||||
<div class="col-md-5">
|
||||
<base-input
|
||||
type="text"
|
||||
label="Company"
|
||||
:disabled="true"
|
||||
placeholder="Company"
|
||||
v-model="user.company"
|
||||
>
|
||||
</base-input>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<base-input
|
||||
type="text"
|
||||
label="Username"
|
||||
placeholder="Username"
|
||||
v-model="user.username"
|
||||
>
|
||||
</base-input>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<base-input
|
||||
type="email"
|
||||
label="Email address"
|
||||
placeholder="mike@email.com"
|
||||
v-model="user.email"
|
||||
>
|
||||
</base-input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<base-input
|
||||
type="text"
|
||||
label="First Name"
|
||||
placeholder="First Name"
|
||||
v-model="user.firstName"
|
||||
>
|
||||
</base-input>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<base-input
|
||||
type="text"
|
||||
label="Last Name"
|
||||
placeholder="Last Name"
|
||||
v-model="user.lastName"
|
||||
>
|
||||
</base-input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<base-input
|
||||
type="text"
|
||||
label="Address"
|
||||
placeholder="Home Address"
|
||||
v-model="user.address"
|
||||
>
|
||||
</base-input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<base-input
|
||||
type="text"
|
||||
label="City"
|
||||
placeholder="City"
|
||||
v-model="user.city"
|
||||
>
|
||||
</base-input>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<base-input
|
||||
type="text"
|
||||
label="Country"
|
||||
placeholder="Country"
|
||||
v-model="user.country"
|
||||
>
|
||||
</base-input>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<base-input
|
||||
label="Postal Code"
|
||||
placeholder="ZIP Code"
|
||||
v-model="user.postalCode"
|
||||
>
|
||||
</base-input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<base-input label="About Me">
|
||||
<textarea
|
||||
class="form-control"
|
||||
placeholder="ZIP Code"
|
||||
v-model="user.aboutMe"
|
||||
>
|
||||
</textarea>
|
||||
</base-input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<base-button native-type="submit" type="primary" class="btn-fill">
|
||||
Save
|
||||
</base-button>
|
||||
</form>
|
||||
</card>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
user: {
|
||||
company: 'Creative Code Inc.',
|
||||
username: 'michael23',
|
||||
email: '',
|
||||
firstName: 'Mike',
|
||||
lastName: 'Andrew',
|
||||
address: 'Bld Mihail Kogalniceanu, nr. 8 Bl 1, Sc 1, Ap 09',
|
||||
city: 'New York',
|
||||
country: 'USA',
|
||||
postalCode: '',
|
||||
aboutMe: `Lamborghini Mercy, Your chick she so thirsty, I'm in that two seat Lambo.`
|
||||
}
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
updateProfile() {
|
||||
alert('Your data: ' + JSON.stringify(this.user));
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style></style>
|
||||
38
APP/components/UserProfile/UserCard.vue
Executable file
38
APP/components/UserProfile/UserCard.vue
Executable file
@@ -0,0 +1,38 @@
|
||||
<template>
|
||||
<card class="card-user">
|
||||
<p class="card-text"></p>
|
||||
<div class="author">
|
||||
<div class="block block-one"></div>
|
||||
<div class="block block-two"></div>
|
||||
<div class="block block-three"></div>
|
||||
<div class="block block-four"></div>
|
||||
<a href="javascript:void(0)">
|
||||
<img class="avatar" src="img/emilyz.jpg" alt="..." />
|
||||
<h5 class="title">Mike Andrew</h5>
|
||||
</a>
|
||||
<p class="description">Ceo/Co-Founder</p>
|
||||
</div>
|
||||
<p></p>
|
||||
<div class="card-description">
|
||||
Do not be scared of the truth because we need to restart the human
|
||||
foundation in truth And I love you like Kanye loves Kanye I love Rick
|
||||
Owens’ bed design but the back is...
|
||||
</div>
|
||||
|
||||
<div slot="footer" class="button-container">
|
||||
<base-button class="btn-facebook" icon round>
|
||||
<i class="fab fa-facebook"></i>
|
||||
</base-button>
|
||||
<base-button class="btn-twitter" icon round>
|
||||
<i class="fab fa-twitter"></i>
|
||||
</base-button>
|
||||
<base-button class="btn-google" icon round>
|
||||
<i class="fab fa-google-plus"></i>
|
||||
</base-button>
|
||||
</div>
|
||||
</card>
|
||||
</template>
|
||||
<script>
|
||||
export default {};
|
||||
</script>
|
||||
<style></style>
|
||||
51
APP/components/index.js
Executable file
51
APP/components/index.js
Executable file
@@ -0,0 +1,51 @@
|
||||
import BaseCheckbox from './Inputs/BaseCheckbox.vue';
|
||||
import BaseAlert from './BaseAlert.vue';
|
||||
import IconCheckbox from './Inputs/IconCheckbox.vue';
|
||||
import BaseRadio from './Inputs/BaseRadio.vue';
|
||||
import BaseInput from './Inputs/BaseInput.vue';
|
||||
import BaseSwitch from './BaseSwitch.vue';
|
||||
import Badge from './Badge';
|
||||
import BaseProgress from './BaseProgress.vue';
|
||||
import BaseButton from './BaseButton.vue';
|
||||
|
||||
import BaseDropdown from './BaseDropdown.vue';
|
||||
import BaseTable from './BaseTable.vue';
|
||||
|
||||
import Card from './Cards/Card.vue';
|
||||
import BaseNav from './Navbar/BaseNav';
|
||||
import NavbarToggleButton from './Navbar/NavbarToggleButton';
|
||||
|
||||
import Breadcrumb from './Breadcrumb/Breadcrumb.vue';
|
||||
import BreadcrumbItem from './Breadcrumb/BreadcrumbItem.vue';
|
||||
import RouteBreadCrumb from './Breadcrumb/RouteBreadcrumb.vue';
|
||||
import Modal from './Modal.vue';
|
||||
import LoadingPanel from './LoadingPanel.vue';
|
||||
|
||||
import BasePagination from './BasePagination.vue';
|
||||
|
||||
import SidebarPlugin from './SidebarPlugin';
|
||||
|
||||
|
||||
export {
|
||||
BaseCheckbox,
|
||||
IconCheckbox,
|
||||
BaseSwitch,
|
||||
Badge,
|
||||
BaseAlert,
|
||||
BaseProgress,
|
||||
BasePagination,
|
||||
BaseRadio,
|
||||
BaseInput,
|
||||
Card,
|
||||
BaseTable,
|
||||
BaseDropdown,
|
||||
SidebarPlugin,
|
||||
BaseNav,
|
||||
NavbarToggleButton,
|
||||
Breadcrumb,
|
||||
BreadcrumbItem,
|
||||
RouteBreadCrumb,
|
||||
Modal,
|
||||
BaseButton,
|
||||
LoadingPanel
|
||||
};
|
||||
Reference in New Issue
Block a user