team table, create team modal, delete team modal

This commit is contained in:
romankko2 committed 2022-11-25 23:15:34 +01:00
1 parent 0e33d86c98
commit f6d0764969
22 files changed
+1741 -877

No files matched your search

+960 -500
View File
File diff suppressed because it is too large. Load diff
+6 -3
View File
@@ -8,7 +8,8 @@
"lint": "vue-cli-service lint" "lint": "vue-cli-service lint"
}, },
"dependencies": { "dependencies": {
"@capacitor/storage": "^1.2.5", "@capacitor/core": "^4.5.0",
"@capacitor/preferences": "^4.0.2",
"@ionic/vue": "^6.3.6", "@ionic/vue": "^6.3.6",
"@mdi/font": "5.9.55", "@mdi/font": "5.9.55",
"@vuelidate/core": "^2.0.0", "@vuelidate/core": "^2.0.0",
@@ -19,16 +20,18 @@
"mitt": "^2.1.0", "mitt": "^2.1.0",
"moment": "^2.29.4", "moment": "^2.29.4",
"node-polyfill-webpack-plugin": "^2.0.1", "node-polyfill-webpack-plugin": "^2.0.1",
"pug": "^3.0.2",
"pug-plain-loader": "^1.1.0",
"roboto-fontface": "*", "roboto-fontface": "*",
"vue": "^3.2.13", "vue": "^3.2.13",
"vue-i18n": "^9.0.0", "vue-i18n": "^9.0.0",
"vue-i18n-extract": "^1.2.3", "vue-i18n-extract": "^1.2.3",
"vue-loading-overlay": "^5.0.3", "vue-loading-overlay": "^5.0.3",
"vue-router": "^4.0.3", "vue-router": "^4.0.3",
"vue-toast-notification": "^3.0.4",
"vuetify": "^3.0.0-beta.0", "vuetify": "^3.0.0-beta.0",
"vuex": "^4.1.0", "vuex": "^4.1.0",
"webfontloader": "^1.0.0", "webfontloader": "^1.0.0"
"vue-toast-notification": "^3.0.4"
}, },
"devDependencies": { "devDependencies": {
"@types/webfontloader": "^1.0.0", "@types/webfontloader": "^1.0.0",
File renamed without changes.
+4 -1
View File
@@ -1 +1,4 @@
{} {
"Chyba": "",
"Zadali ste neplatný email.": ""
}
+4
View File
@@ -0,0 +1,4 @@
{
"Value is not a valid email address": "Zadali ste neplatný email.",
"error": "chyba"
}
@@ -0,0 +1,69 @@
<template>
<v-dialog v-model="show" max-width="650">
<v-card>
<div class="d-flex justify-space-between align-center">
<h1>Ste si istý, že chcete zmazať tento tím?</h1>
<v-icon
@click.stop="show = false"
icon="mdi-close"
size="large"
></v-icon>
</div>
<v-card-actions class="mt-10 d-flex justify-start align-center">
<v-btn class="create-btn" variant="flat" @click="deleteTeam()">ÁNO</v-btn>
<v-btn class="ml-7 cancel-btn" @click.stop="show = false">ZRUŠIŤ</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</template>
<script>
export default {
props: [
'visible'
],
data() {
return {
selectedEmployee: "",
selectedEmployees: [],
employees: null,
};
},
methods: {
async deleteTeam(id) {
try{
await this.$axios.delete(`v1/teams/${id}`)
this.$router.push({ name: 'Overview - Teams' })
this.show = false
} catch (error) {
this.$toast.error(error.response.data.error)
}
}
},
computed: {
show: {
get() {
return this.visible;
},
set(value) {
if (!value) this.$emit('close')
},
},
}
}
</script>
<style lang="sass" scoped>
.cancel-btn
border: thin solid rgba(var(--v-border-color), var(--v-border-opacity))
color: #7f39fb
.create-btn
background: #7f39fb
border-radius: 4px
color: #fff
</style>
@@ -0,0 +1,175 @@
<template>
<v-dialog v-model="show" max-width="650" max-height="90%" height="700px">
<W-loadable :content="employees" class="h-100">
<v-card v-if="employees" height="100%" class="d-flex flex-column">
<div class="d-flex justify-space-between align-center modal-header">
<h1>Vytvoriť tím</h1>
<v-icon
@click.stop="show = false"
icon="mdi-close"
size="large"
></v-icon>
</div>
<v-card-actions class="d-flex flex-column modal-content">
<v-text-field
class="modal-input"
label="Názov tímu"
variant="outlined"
></v-text-field>
<div class="employee-position w-100" v-for="(employee, index) in selectedEmployees" :key="employee.id">
<v-text-field
class="modal-input"
disabled
label="Zamestanec"
variant="outlined"
v-model="selectedEmployees[index].surname"
>
</v-text-field>
<v-icon
class="remove-input"
@click="removeInput(index)"
icon="mdi-close"
size="small"
></v-icon
>
</div>
<!-- <v-btn class="add-btn">
<v-icon icon="mdi-plus" size="large"></v-icon>PRIDAŤ
ZAMESTNANCA</v-btn
> -->
<v-autocomplete
class="modal-input"
label="Vyhladat"
variant="outlined"
:items="employeesOptions"
v-model="selectedEmployee"
></v-autocomplete>
</v-card-actions>
<v-btn class="create-btn" @click="createTeam()">VYTVORIŤ</v-btn>
</v-card>
</W-loadable>
</v-dialog>
</template>
<script>
export default {
props: [
'visible'
],
data() {
return {
selectedEmployee: "",
selectedEmployees: [],
employees: null,
};
},
async created() {
try {
this.employees = await this.$axios.get_auth_data("v1/employees");
} catch (error) {
console.log(error)
}
},
methods: {
removeInput(index) {
this.selectedEmployees.splice(index, 1)
},
async createTeam() {
try{
await this.$axios.post_auth('v1/teams')
this.$router.push({ name: 'Overview - Teams' })
this.show = false
} catch (error) {
this.$toast.error(error.response.data.error)
}
}
},
computed: {
employeesOptions() {
const selectedEmployeesIds = this.selectedEmployees.map(
(employee) => employee.id
);
const employees = this.employees.filter(
(employee) => !selectedEmployeesIds.includes(employee.id)
);
return employees.map(
(employee) => `${employee.name} ${employee.surname}`
);
},
show: {
get() {
return this.visible;
},
set(value) {
if (!value) this.$emit("close")
},
},
},
watch: {
selectedEmployee() {
const selectedEmployee = this.selectedEmployee.split(" ");
const employee = this.employees.find(
(e) =>
e.name == selectedEmployee[0] &&
e.surname == selectedEmployee[1]
);
this.selectedEmployees.push(JSON.parse(JSON.stringify(employee)));
// this.selectedEmployee = ''
},
},
};
</script>
<style lang="sass" scoped>
.modal-input
width: 100%
flex: 0 0 auto
// .add-btn
// color: #6200EE
// background: #fff
// border: thin solid rgba(var(--v-border-color), var(--v-border-opacity))
// height: 50px
// width: 100%
.modal-header
flex: 0 0 auto
.modal-content
flex: 1 1 100%
margin: 10px 0
overflow-y: scroll
.create-btn
height: 50px
width: 100%
flex: 0 0 auto
background: #7f39fb
border-radius: 4px
color: #fff
.remove-input
pointer-events: auto
opacity: 2
position: absolute
left: 92%
top: 25%
.employee-position
position: relative
</style>
+6
View File
@@ -5,3 +5,9 @@
</v-main> </v-main>
</v-app> </v-app>
</template> </template>
<script>
export default {
name: 'App'
}
</script>
+27 -13
View File
@@ -1,15 +1,18 @@
import { createApp } from 'vue' import { createApp } from 'vue'
import App from './App.vue' import vuetify from './vuetify'
import router from './router' import { loadFonts } from './webfontloader'
import { IonicVue } from '@ionic/vue' import loader from 'vue-loading-overlay'
import vuetify from './vuetify' import App from './App.vue'
import { loadFonts } from './webfontloader'
import loader from 'vue-loading-overlay' import { IonicVue } from '@ionic/vue'
import router from './router'
import store from '../../../store'
import wToast from '@/plugins/w/toast'
import i18n from '@/plugins/w/i18n'
import w from '@/plugins/w/w'
import { Options } from '@/plugins/w/w/types'
import store from '@/store'
import wToast from '@/plugins/w/toast'
import w from '@/plugins/w/w'
import { Options } from '@/plugins/w/w/types'
import '@ionic/vue/css/core.css' import '@ionic/vue/css/core.css'
import '@ionic/vue/css/normalize.css' import '@ionic/vue/css/normalize.css'
@@ -25,6 +28,8 @@ import '@ionic/vue/css/display.css'
import '@/plugins/app/_theme/index.sass' import '@/plugins/app/_theme/index.sass'
import 'vue-loading-overlay/dist/vue-loading.css' import 'vue-loading-overlay/dist/vue-loading.css'
// vue cal
// import VueCal from '@/plugins/app@vendor/vue-cal/index'
loadFonts() loadFonts()
@@ -37,8 +42,15 @@ const options: Options = {
}, },
i18n: { i18n: {
disabled: true, disabled: true,
languages: [{ title: 'English', flag: 'en', value: 'en' }], languages: [
defaultLanguage: 'en' {
title: 'English', flag: 'en', value: 'en'
},
{
title: "Slovenský", flag: "sk", value: "sk"
}
],
defaultLanguage: 'sk'
}, },
moxios: { moxios: {
routes: { routes: {
@@ -83,6 +95,8 @@ createApp(App)
.use(IonicVue) .use(IonicVue)
.use(vuetify) .use(vuetify)
.use(wToast) .use(wToast)
.use(i18n, options)
// .use(VueCal)
.use(loader, { .use(loader, {
color: '#6200EE' color: '#6200EE'
}) })
@@ -2,40 +2,190 @@
<v-app-bar class="header"> <v-app-bar class="header">
<v-app-bar-nav-icon autoHide @click="eventBus.emit('toggle-menu')" class="menu-icon"></v-app-bar-nav-icon> <v-app-bar-nav-icon autoHide @click="eventBus.emit('toggle-menu')" class="menu-icon"></v-app-bar-nav-icon>
<div class="d-flex align-center justify-space-between w-100"> <div class="d-flex align-center justify-space-between w-100 responsive-position">
<h1>{{ $route.name }}</h1> <h1 class="mr-md-4 route-name mb-5">{{ $route.name }}</h1>
<div class="d-flex justify-center align-center"> <div class="d-flex justify-center align-center">
<img class="mx-3" :src="require('../_img/search-icon.svg')" alt="search-icon" /> <div class="d-flex justify-center align-center pos-col">
<input class="mx-2" type="text" placeholder="Vyhľadávanie" /> <img
<v-btn class="create-button">+ Vytvoriť</v-btn> class="search-icon mx-3"
:src="
require('@/plugins/app/_layout/app/_img/search-icon.svg')
"
alt="search-icon"
/>
<input
class="search-input mx-2"
type="text"
placeholder="Vyhľadávanie"
/>
<v-menu open-on-hover>
<template v-slot:activator="{ props }">
<v-btn v-bind="props" class="create-btn"
>+<span class="responsive-btn"
>Vytvoriť</span
></v-btn
>
</template>
<v-list class="text-left">
<v-list-item class="dropdown-hover">
MASTER TASK
</v-list-item>
<v-list-item class="dropdown-hover">
SUB TASK
</v-list-item>
<v-list-item class="dropdown-hover" @click.stop="dialog=true" >
TÍM
<A-team-modal :visible="dialog" @close="dialog=false" />
</v-list-item>
<v-list-item class="dropdown-hover">
BLOKER
</v-list-item>
</v-list>
</v-menu>
<div
class="d-flex justify-center align-center employee-section mx-1"
>
<p v-if="user" class="employee-select">
{{
user.user.name.charAt(0) +
user.user.surname.charAt(0)
}}
</p>
<div class="text-center">
<v-menu open-on-hover>
<template v-slot:activator="{ props }">
<v-btn
class="text-transform"
v-if="user"
v-bind="props"
>
{{
`${user.user.name} ${user.user.surname}`
}}
<v-icon
class="pl-5"
icon="mdi-chevron-down"
size="x-small"
></v-icon>
</v-btn>
</template>
<v-list class="text-center">
<v-list-item class="dropdown-hover">
Working hours
</v-list-item>
<v-list-item>
<v-btn class="logout-btn"
>Odhlásiť sa</v-btn
>
</v-list-item>
</v-list>
</v-menu>
</div>
</div>
</div>
</div> </div>
</div> </div>
</v-app-bar> </v-app-bar>
</template> </template>
<script>
import ATeamModal from '../../../_components/a-team-modal.vue'
export default {
components: {
ATeamModal
},
data() {
return {
user: null,
dialog: false
}
},
async created() {
try {
this.user = await this.$axios.get_auth_data("v1/auth/info");
} catch (error) {
console.log(error)
}
}
}
</script>
<style lang="sass" scoped> <style lang="sass" scoped>
.route-name
font-size: 22px
.header .header
position: relative !important position: relative !important
box-shadow: none !important box-shadow: none !important
border-bottom: 1px solid rgba(0, 0, 0, 0.2) border-bottom: 1px solid rgba(0, 0, 0, 0.2)
padding: 25px 15px padding: 25px 15px
img
border: 2px solid #e0e0e0
padding: 6px
border-radius: 6px
input
outline: none
.create-button
background: #7f39fb
border-radius: 4px
color: #fff
@media (min-width: 992px) @media (min-width: 992px)
.menu-icon
display: none
.create-btn
background: #7f39fb
border-radius: 4px
color: #fff
.logout-btn
background: #7f39fb
border-radius: 4px
color: #fff
.text-transform
text-transform: capitalize
.search-input
outline: none
.search-icon
border: 2px solid #e0e0e0
padding: 6px
border-radius: 6px
.employee-section
border: 1px solid rgba(33, 33, 33, 0.08)
border-radius: 16px
height: 70px
.employee-select
background: #6200EE
color: #fff
border-radius: 50%
padding: 13px
margin-left: 10px
@media screen and (max-width: 772px)
.route-name, .search-icon
display: none
@media screen and (max-width: 617px)
.responsive-position
justify-content: center !important
.pos-col
display: flex
flex-direction: column-reverse
justify-content: center
margin: auto
.employee-section, .create-btn, .search-input
margin-top: 20px
.header .header
.menu-icon padding: 75px 0
display: none
.dropdown-hover
cursor: pointer !important
&:hover
background: #F2E7FE
font-weight: bolder
</style> </style>
@@ -6,7 +6,7 @@
<ion-list lines="none" id="labels-list"> <ion-list lines="none" id="labels-list">
<ion-item v-for="page in pages" :key="page.pageName" :class="{active: isActive(page.pageName)}" <ion-item v-for="page in pages" :key="page.pageName" :class="{active: isActive(page.pageName)}"
@click="$router.push({name: page.pageName}); closeMenu()"> @click="$router.push({name: page.pageName}); closeMenu()">
{{ page.title }} {{ page.title }}<span v-if="page.pageName == 'Overview - Unscheduled tasks'" class="mx-1 unfinished-tasks">13</span>
</ion-item> </ion-item>
</ion-list> </ion-list>
</ion-content> </ion-content>
@@ -96,4 +96,12 @@ ion-item
ion-menu ion-menu
width: 300px width: 300px
--width: 300px --width: 300px
.unfinished-tasks
color: #fff
background: #B00020
border-radius: 50%
padding: 0 4px
margin-bottom: 15px
width: fit-content
</style> </style>
@@ -33,7 +33,4 @@ export default {
.split-pane-right-side .split-pane-right-side
width: calc(100vw - 300px) !important width: calc(100vw - 300px) !important
max-width: calc(100vw - 300px) !important max-width: calc(100vw - 300px) !important
.main-content
padding: 20px 15px
</style> </style>
@@ -1,7 +1,7 @@
<template> <template>
<header class="login-header-border "> <header class="login-header-border ">
<v-container> <v-container>
<img :src="require(`@/plugins/app/_layout/_img/jandl-logo.svg`)" alt="jandl-logo"> <img :src="require('@/plugins/app/_layout/_img/jandl-logo.svg')" alt="jandl-logo">
</v-container> </v-container>
</header> </header>
</template> </template>
+20 -4
View File
@@ -1,5 +1,21 @@
span.highlight .task-action
color: #7F39FB .trash-icon
padding: 10px
background: rgba(0, 0, 0, 0.04)
margin-left: 12px
border-radius: 50%
cursor: pointer
.pencil-icon
padding: 10px
background: rgba(0, 0, 0, 0.04)
border-radius: 50%
cursor: pointer
.cursor-pointer:hover th
cursor: pointer font-size: 16px !important
.task-action
width: 50%
.v-card
padding: 35px !important
@@ -0,0 +1,4 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M2 20H22" stroke="#14181F" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M19.6585 9.70128L12.6585 3.57628C12.2815 3.24638 11.7185 3.24638 11.3415 3.57628L4.3415 9.70128C4.12448 9.89117 4 10.1655 4 10.4539V19.0001C4 19.5524 4.44772 20.0001 5 20.0001H9C9.55228 20.0001 10 19.5524 10 19.0001V15.0001C10 14.4478 10.4477 14.0001 11 14.0001H13C13.5523 14.0001 14 14.4478 14 15.0001V19.0001C14 19.5524 14.4477 20.0001 15 20.0001H19C19.5523 20.0001 20 19.5524 20 19.0001V10.4539C20 10.1655 19.8755 9.89117 19.6585 9.70128Z" stroke="#14181F" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 740 B

@@ -8,7 +8,7 @@
variant="outlined" variant="outlined"
></v-select> ></v-select>
<div class="d-flex date-wrapper"> <div class="d-flex date-wrapper">
<v-btn class="subheader-icon-button " variant="outlined"><img :src="require('./_img/icon-prev.svg')" alt=""></v-btn> <v-btn class="subheader-icon-button prev" variant="outlined"><img :src="require('./_img/icon-prev.svg')" alt=""></v-btn>
<v-text-field <v-text-field
class="subheader-input disabled" class="subheader-input disabled"
:items="items" :items="items"
@@ -17,7 +17,7 @@
variant="outlined" variant="outlined"
append-inner-icon="mdi-calendar-blank-outline" append-inner-icon="mdi-calendar-blank-outline"
></v-text-field> ></v-text-field>
<v-btn class="subheader-icon-button " variant="outlined"><img :src="require('./_img/icon-next.svg')" alt=""></v-btn> <v-btn class="subheader-icon-button next" variant="outlined"><img :src="require('./_img/icon-next.svg')" alt=""></v-btn>
</div> </div>
<v-text-field <v-text-field
class="subheader-input" class="subheader-input"
@@ -57,7 +57,13 @@ export default {
width: 56px width: 56px
min-width: auto !important min-width: auto !important
padding: 0 padding: 0
margin: 0 10px margin: 0
&.prev
margin-right: 10px
&.next
margin-left: 10px
&:deep(.v-btn__append) &:deep(.v-btn__append)
margin: 0 margin: 0
@@ -75,7 +81,4 @@ export default {
.date-wrapper .date-wrapper
flex: 0 1 402px flex: 0 1 402px
</style> </style>
@@ -1,326 +1,220 @@
<template> <template>
<div> <div class="calendar-wrapper">
<v-sheet tile height="54" color="grey lighten-3" class="d-flex"> <Vue-cal
<v-btn icon class="ma-2" @click="$refs.calendar.prev()"> :hide-view-selector="true"
<v-icon>mdi-chevron-left</v-icon> :hide-title-bar="true"
</v-btn> :disable-views="['years', 'year', 'month', 'day']"
<v-select v-model="type" :items="types" dense outlined hide-details class="ma-2" label="type"></v-select> :time-from="8 * 60"
<v-select v-model="mode" :items="modes" dense outlined hide-details label="event-overlap-mode" class="ma-2"></v-select> :time-to="24 * 60"
<v-select v-model="weekday" :items="weekdays" dense outlined hide-details label="weekdays" class="ma-2"></v-select> :hide-weekends="true"
<v-spacer></v-spacer> :selected-date="new Date()"
<v-btn icon class="ma-2" @click="$refs.calendar.next()"> :onEventClick="taskClicked"
<v-icon>mdi-chevron-right</v-icon> :editable-events="vueCalConfig.editing"
</v-btn> :events="events"
</v-sheet> :min-event-width="90"
<v-sheet height="600"> :overlaps-per-time-step="true"
<v-calendar ref="calendar" v-model="value" :weekdays="weekday" :type="type" :events="events" :event-overlap-mode="mode" :event-overlap-threshold="60" :event-color="getEventColor" :event-ripple="false" @change="getEvents" @mousedown:event="startDrag" @mousedown:time="startTime" @mousemove:time="mouseMove" @mouseup:time="endDrag" @mouseleave="cancelDrag"> :time-cell-height="200"
<template #event="{ event, timed }"> :min-cell-width="300"
<div class="pl-1" v-html="getEventHTML(event, timed)"></div> sticky-split-labels
<div v-if="timed" class="v-event-drag-bottom" @mousedown.stop="extendBottom(event)"></div> >
</template> <template v-slot:weekday-heading="{ heading }">
<template #day-body="{ date, week }"> <span class="d-flex align-center">
<div class="v-current-time" :class="{ first: date === week[0].date }" :style="{ top: nowY }"> <img v-if="getHeadingDate(heading.date) == 'Dnes'" :src="require('./_img/home-icon.svg')" class="mr-2">
<div class="v-current-time-time"> <p>{{ getHeadingDate(heading.date) }}</p>
{{ nowTime }} </span>
</div> </template>
</div>
</template> <template v-slot:time-cell="{ hours, minutes }">
&nbsp; <span class="d-flex align-center justify-center">
</v-calendar> <p>{{hours}}:{{String(minutes).padStart(2, '0')}}</p>
<!-- {{ lastEvent }} --> <div class="time-column-line"></div>
</v-sheet> </span>
</template>
<template v-slot:event="{ event }">
<div class="vuecal__event-title vuecal__event-title--edit"
contenteditable
@blur="event.title = $event.target.innerHTML"
v-html="event.title" />
<small class="vuecal__event-time">
<strong>Event start:</strong> <span>{{ event.start.formatTime("h O'clock") }}</span><br/>
<strong>Event end:</strong> <span>{{ event.end.formatTime("h O'clock") }}</span>
</small>
</template>
</Vue-cal>
</div> </div>
</template> </template>
<script> <script>
// import VueCal from 'vue-cal'
// import 'vue-cal/dist/vuecal.css'
export default { export default {
components: {
// VueCal,
},
data() { data() {
return { return {
type: 'day', events: [
types: ['month', 'week', 'day', '4day'], {
mode: 'stack', start: '2022-11-21 8:00',
modes: ['stack', 'column'], end: '2022-11-21 13:30',
weekday: [0, 1, 2, 3, 4, 5, 6], title: 'Running Marathon',
weekdays: [ content: '<i class="icon material-icons">directions_run</i>',
{ text: 'Sun - Sat', value: [0, 1, 2, 3, 4, 5, 6] }, class: 'red',
{ text: 'Mon - Sun', value: [1, 2, 3, 4, 5, 6, 0] }, id: 1
{ text: 'Mon - Fri', value: [1, 2, 3, 4, 5] }, },
{ text: 'Mon, Wed, Fri', value: [1, 3, 5] } {
start: '2022-11-21 9:00',
end: '2022-11-21 11:00',
title: 'Drink water!',
content: '<i class="icon material-icons">local_drink</i>',
class: 'green',
id: 2
},
{
start: '2022-11-21 10:00',
end: '2022-11-21 12:00',
title: 'Trip to India',
content: '<i class="icon material-icons">flight</i>',
class: 'blue',
id: 3
},
{
start: '2022-11-21 13:30',
end: '2022-11-21 16:00',
title: 'Trip to India',
content: '<i class="icon material-icons">flight</i>',
class: 'blue',
id: 3
},
{
start: '2022-11-22 11:00',
end: '2022-11-22 13:00',
title: 'Trip to India',
content: '<i class="icon material-icons">flight</i>',
class: 'yellow',
id: 3
}
], ],
value: '',
events: [],
colors: ['#2196F3', '#3F51B5', '#673AB7', '#00BCD4', '#4CAF50', '#FF9800', '#757575'],
names: ['Meeting', 'Holiday', 'PTO', 'Travel', 'Event', 'Birthday', 'Conference', 'Party'],
dragEvent: null, vueCalConfig: {
dragStart: null, editing: {
lastEvent: '', title: false,
createEvent: null, drag: true,
createStart: null, resize: false,
extendOriginal: null, delete: false,
create: false
}
},
isMounted: false
} }
}, },
computed: {
nowY() {
const cal = this.$refs.calendar
if (!cal && !this.isMounted) {
return -1
}
return cal.timeToY(cal.times.now) + 'px'
},
nowTime() {
const cal = this.$refs.calendar
if (!cal && !this.isMounted) {
return -1
}
return cal.formatTime(cal.times.now)
}
},
mounted() {
const cal = this.$refs.calendar
this.isMounted = true
// scroll to the current time
const minutes = cal.times.now.hour * 60 + cal.times.now.minute
const firstTime = Math.max(0, minutes - (minutes % 30) - 30)
cal.scrollToTime(firstTime)
// every minute update the current time bar
setInterval(() => cal.updateTimes(), 60 * 1000)
},
methods: { methods: {
getEvents({ start, end }) { getHeadingDate(value) {
const events = [] const date = new Date(value)
const min = new Date(`${start.date}T00:00:00`) const today = new Date()
const max = new Date(`${end.date}T23:59:59`) const yesterday = new Date((new Date()).setDate(today.getDate() - 1))
const days = (max.getTime() - min.getTime()) / 86400000 const tomorrow = new Date((new Date()).setDate(today.getDate() + 1))
const eventCount = this.rnd(days, days + 20)
for (let i = 0; i < eventCount; i++) { if (today.toLocaleDateString('sk-SK') === date.toLocaleDateString('sk-SK')) return "Dnes"
const allDay = this.rnd(0, 3) === 0 if (yesterday.toLocaleDateString('sk-SK') === date.toLocaleDateString('sk-SK')) return "Včera"
const firstTimestamp = this.rnd(min.getTime(), max.getTime()) if (tomorrow.toLocaleDateString('sk-SK') === date.toLocaleDateString('sk-SK')) return "Zajtra"
const first = new Date(firstTimestamp - (firstTimestamp % 900000))
const secondTimestamp = this.rnd(2, allDay ? 288 : 8) * 900000
const second = new Date(first.getTime() + secondTimestamp)
events.push({ return date.toLocaleDateString('sk-SK')
name: this.names[this.rnd(0, this.names.length - 1)], },
start: this.formatDate(first, !allDay),
end: this.formatDate(second, !allDay),
color: this.colors[this.rnd(0, this.colors.length - 1)]
})
}
this.events = events taskClicked(task) {
}, console.log("task", task)
getEventColor(event) {
const rgb = parseInt(event.color.substring(1), 16)
const r = (rgb >> 16) & 0xff
const g = (rgb >> 8) & 0xff
const b = (rgb >> 0) & 0xff
return event === this.dragEvent ? `rgba(${r}, ${g}, ${b}, 0.5)` : event === this.createEvent ? `rgba(${r}, ${g}, ${b}, 0.5)` : event.color
},
getEventHTML(event, timed) {
const cal = this.$refs.calendar
let name = event.name
if (event.start.hasTime) {
if (timed) {
const showStart = event.start.hour < 12 && event.end.hour >= 12
const start = cal.formatTime(event.start, showStart)
const end = cal.formatTime(event.end, true)
// const singline = diffMinutes(event.start, event.end) <= this.parsedEventOverlapThreshold
const singline = event.end - event.start <= this.parsedEventOverlapThreshold
const separator = singline ? ', ' : '<br>'
return `<strong>${name}</strong>${separator}${start} - ${end}`
} else {
const time = this.formatTime(event.start, true)
return `<strong>${time}</strong> ${name}`
}
}
return name
},
rnd(a, b) {
return Math.floor((b - a + 1) * Math.random()) + a
},
formatDate(a, withTime) {
return withTime ? `${a.getFullYear()}-${a.getMonth() + 1}-${a.getDate()} ${a.getHours()}:${a.getMinutes()}` : `${a.getFullYear()}-${a.getMonth() + 1}-${a.getDate()}`
},
eventMove(e) {
console.log('eventMove', e)
},
startDrag(e) {
if (e.event && e.timed) {
this.dragEvent = e.event
this.dragTime = null
this.extendOriginal = null
}
this.lastEvent = 'startDrag'
},
startTime(e) {
const mouse = this.toDate(e)
if (this.dragEvent && this.dragTime === null) {
const start = this.toDate(this.dragEvent.start)
this.dragTime = mouse.getTime() - start.getTime()
} else {
this.createStart = this.roundTime(mouse.getTime())
this.createEvent = {
name: '(no title)',
start: this.toTimestamp(new Date(this.createStart)),
end: this.toTimestamp(new Date(this.createStart)),
color: this.colors[this.rnd(0, this.colors.length - 1)]
}
this.events.push(this.createEvent)
}
this.lastEvent = 'startTime'
},
extendBottom(event) {
this.createEvent = event
this.createStart = this.toDate(event.start).getTime()
this.extendOriginal = event.end
},
mouseMoveEvent(e) {
console.log('mouseMoveEvent', e)
},
mouseMove(e) {
if (this.dragEvent && this.dragTime !== null) {
const start = this.toDate(this.dragEvent.start)
const end = this.toDate(this.dragEvent.end)
const duration = end.getTime() - start.getTime()
const mouse = this.toDate(e)
const newStartTime = mouse.getTime() - this.dragTime
const newStart = new Date(this.roundTime(newStartTime))
const newEnd = new Date(newStart.getTime() + duration)
this.dragEvent.start = this.toTimestamp(newStart)
this.dragEvent.end = this.toTimestamp(newEnd)
} else if (this.createEvent && this.createStart !== null) {
const mouse = this.toDate(e).getTime()
const mouseRounded = this.roundTime(mouse, false)
const min = Math.min(mouseRounded, this.createStart)
const max = Math.max(mouseRounded, this.createStart)
this.createEvent.start = this.toTimestamp(new Date(min))
this.createEvent.end = this.toTimestamp(new Date(max))
}
},
endDrag(e) {
this.dragTime = null
this.dragEvent = null
this.createEvent = null
this.createStart = null
this.extendOriginal = null
this.lastEvent = 'endDrag'
},
cancelDrag(e) {
if (this.createEvent) {
if (this.extendOriginal) {
this.createEvent.end = this.extendOriginal
} else {
const i = this.events.indexOf(this.createEvent)
if (i !== -1) {
this.events.splice(i, 1)
}
}
}
this.createEvent = null
this.createStart = null
this.dragTime = null
this.dragEvent = null
this.lastEvent = 'cancelDrag'
},
roundTime(time, down = true) {
const roundDownTime = 15 * 60 * 1000 // 15 minutes
return down ? time - (time % roundDownTime) : time + (roundDownTime - (time % roundDownTime))
},
toDate(tms) {
return typeof tms === 'string' ? new Date(tms) : new Date(tms.year, tms.month - 1, tms.day, tms.hour, tms.minute)
},
toTimestamp(date) {
return `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()} ${date.getHours()}:${date.getMinutes()}`
} }
} }
} }
</script> </script>
<style lang="sass" scoped> <style lang="sass" scoped>
.v-calendar .calendar-wrapper
user-select: none width: 100%
-webkit-user-select: none overflow-x: scroll
padding-top: 50px
.v-event-drag-bottom .vuecal
position: absolute transform: scale(1)
left: 0
right: 0
bottom: 4px
height: 4px
cursor: ns-resize
.v-event-drag-bottom::after ::v-deep
display: none
position: absolute
left: 50%
height: 4px
border-top: 1px solid white
border-bottom: 1px solid white
width: 20px
margin-left: -10px
opacity: 0.8
content: ''
.v-event-timed:hover .v-event-drag-bottom::after .vuecal__cells
display: block -ms-overflow-style: none
scrollbar-width: none
.v-current-time &::-webkit-scrollbar
height: 1px display: none
background-color: red
position: absolute
left: -1px
right: 0
pointer-events: none
&.first::after .vuecal__body
content: '' max-height: 100%
position: absolute overflow-y: scroll
background-color: red position: relative !important
width: 7px -ms-overflow-style: none
height: 7px scrollbar-width: none
border-radius: 3.5px
margin-top: -3px
margin-left: -3px
&::-webkit-scrollbar
display: none
.v-current-time-time .weekday-label
display: none justify-content: start !important
font-size: 24px
font-weight: 700
.vuecal__weekdays-headings,
.vuecal__cell:before
border: none
&.first .v-current-time-time .vuecal__time-column
display: block width: 150px
position: absolute box-shadow: none !important
left: -60px
color: red .time-column-line
font-size: 10px width: 40px
padding: 1px height: 0
background-color: white border: 1px solid rgba(33, 33, 33, 0.08)
width: 50px margin-left: 10px
height: 20px
text-align: right .vuecal__flex
top: -9px flex-wrap: nowrap !important
.vuecal__cell
padding: 10px 10px 10px 0px
.vuecal__cell--today
background-color: rgba(0, 0, 0, .02) !important
.vuecal:not(.vuecal--day-view) .vuecal__cell--selected:not(.vuecal__cell--today) ,
.vuecal__cell--current:not(.vuecal__cell--today)
background-color: white
.vuecal__event
border-radius: 16px
padding: 20px 10px
text-align: left
.vuecal__event-title
text-align: left
&:hover
z-index: 10
&.red
background: #FFC9C9 !important
&.green
background: #E9FFE5 !important
&.blue
background: #DADEF8 !important
&.yellow
background: #F6ECD3 !important
</style> </style>
@@ -39,23 +39,9 @@ export default {
</script> </script>
<style lang="sass" scoped> <style lang="sass" scoped>
.task-action
.trash-icon
height: 25px
background: rgba(0, 0, 0, 0.04)
margin-left: 12px
border-radius: 50%
cursor: pointer
.pencil-icon
height: 25px
background: rgba(0, 0, 0, 0.04)
border-radius: 50%
cursor: pointer
v-table, th, td v-table, th, td
border: thin solid rgba(var(--v-border-color), var(--v-border-opacity)) border: thin solid rgba(var(--v-border-color), var(--v-border-opacity))
@media screen and (max-width: 496px) @media screen and (max-width: 496px)
.task-action .task-action
div div
@@ -65,4 +51,5 @@ v-table, th, td
.pencil-icon, .trash-icon .pencil-icon, .trash-icon
border-radius: 50% border-radius: 50%
margin: 6px margin: 6px
padding: 6px
</style> </style>
@@ -37,24 +37,10 @@ export default {
</script> </script>
<style lang="sass" scoped> <style lang="sass" scoped>
.task-action
.trash-icon
height: 25px
background: rgba(0, 0, 0, 0.04)
margin-left: 12px
border-radius: 50%
cursor: pointer
.pencil-icon
height: 25px
background: rgba(0, 0, 0, 0.04)
border-radius: 50%
cursor: pointer
v-table, th, td v-table, th, td
border: thin solid rgba(var(--v-border-color), var(--v-border-opacity)) border: thin solid rgba(var(--v-border-color), var(--v-border-opacity))
@media screen and (max-width: 379px)
@media screen and (max-width: 496px)
.task-action .task-action
div div
display: flex display: flex
@@ -63,4 +49,5 @@ v-table, th, td
.pencil-icon, .trash-icon .pencil-icon, .trash-icon
border-radius: 50% border-radius: 50%
margin: 6px margin: 6px
padding: 6px
</style> </style>
@@ -0,0 +1,64 @@
<template>
<v-table>
<thead>
<tr>
<th class="text-left">Názov</th>
<th class="text-left">Akcia</th>
</tr>
</thead>
<tbody>
<tr v-for="team in teams" :key="team.id">
<td>{{ team.name }}</td>
<td class="task-action">
<div class="d-flex justify-start">
<img
class="pencil-icon"
:src="
require('@/plugins/tm@overview/_img/pencil-icon.svg')
"
alt="pencil-icon"
/>
<img
class="trash-icon"
:src="
require('@/plugins/tm@overview/_img/trash-icon.svg')
"
alt="trash-icon"
@click.stop="dialog=true"
/>
<A-delete-team-modal :visible="dialog" @close="dialog=false" />
</div>
</td>
</tr>
</tbody>
</v-table>
</template>
<script>
import ADeleteTeamModal from '../../../app/_components/a-delete-team-modal.vue'
export default {
components: {
ADeleteTeamModal
},
data() {
return {
dialog: false
}
},
props: {
teams: {
type: Array,
required: true,
},
}
}
</script>
<style lang="sass" scoped>
v-table, th, td
border: thin solid rgba(var(--v-border-color), var(--v-border-opacity))
</style>
+22 -2
View File
@@ -1,9 +1,29 @@
<template> <template>
<div></div> <W-loadable :content="teams">
<A-teams-table :teams="teams" />
</W-loadable>
</template> </template>
<script> <script>
export default { import ATeamsTable from './_components/a-teams-table.vue'
export default {
components: {
ATeamsTable
},
data() {
return {
teams: null
}
},
async created() {
try {
this.teams = await this.$axios.get_auth_data('v1/teams')
} catch (error) {
console.error(error)
}
}
} }
</script> </script>
+3 -3
View File
@@ -2,7 +2,7 @@ import { Language, Options } from "../types"
import { Ref, ref } from 'vue' import { Ref, ref } from 'vue'
import axios from 'axios' import axios from 'axios'
import { createI18n, I18n as I18nType } from 'vue-i18n' import { createI18n, I18n as I18nType } from 'vue-i18n'
import { Storage } from '@capacitor/storage' import { Preferences } from '@capacitor/preferences'
export class I18n { export class I18n {
@@ -30,7 +30,7 @@ export class I18n {
} }
public async initCurrentLanguage() { public async initCurrentLanguage() {
const { value } = await Storage.get({ key: this.languageKey }) const { value } = await Preferences.get({ key: this.languageKey })
if (value) { if (value) {
this.currentLanguage.value = JSON.parse(value) this.currentLanguage.value = JSON.parse(value)
@@ -58,7 +58,7 @@ export class I18n {
this.currentLanguage.value = language this.currentLanguage.value = language
this._addAxiosHeaders() this._addAxiosHeaders()
Storage.set({ Preferences.set({
key: this.languageKey, key: this.languageKey,
value: JSON.stringify(language) value: JSON.stringify(language)
}) })