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"
},
"dependencies": {
"@capacitor/storage": "^1.2.5",
"@capacitor/core": "^4.5.0",
"@capacitor/preferences": "^4.0.2",
"@ionic/vue": "^6.3.6",
"@mdi/font": "5.9.55",
"@vuelidate/core": "^2.0.0",
@@ -19,16 +20,18 @@
"mitt": "^2.1.0",
"moment": "^2.29.4",
"node-polyfill-webpack-plugin": "^2.0.1",
"pug": "^3.0.2",
"pug-plain-loader": "^1.1.0",
"roboto-fontface": "*",
"vue": "^3.2.13",
"vue-i18n": "^9.0.0",
"vue-i18n-extract": "^1.2.3",
"vue-loading-overlay": "^5.0.3",
"vue-router": "^4.0.3",
"vue-toast-notification": "^3.0.4",
"vuetify": "^3.0.0-beta.0",
"vuex": "^4.1.0",
"webfontloader": "^1.0.0",
"vue-toast-notification": "^3.0.4"
"webfontloader": "^1.0.0"
},
"devDependencies": {
"@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-app>
</template>
<script>
export default {
name: 'App'
}
</script>
+27 -13
View File
@@ -1,15 +1,18 @@
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import { IonicVue } from '@ionic/vue'
import vuetify from './vuetify'
import { loadFonts } from './webfontloader'
import loader from 'vue-loading-overlay'
import { createApp } from 'vue'
import vuetify from './vuetify'
import { loadFonts } from './webfontloader'
import loader from 'vue-loading-overlay'
import App from './App.vue'
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/normalize.css'
@@ -25,6 +28,8 @@ import '@ionic/vue/css/display.css'
import '@/plugins/app/_theme/index.sass'
import 'vue-loading-overlay/dist/vue-loading.css'
// vue cal
// import VueCal from '@/plugins/app@vendor/vue-cal/index'
loadFonts()
@@ -37,8 +42,15 @@ const options: Options = {
},
i18n: {
disabled: true,
languages: [{ title: 'English', flag: 'en', value: 'en' }],
defaultLanguage: 'en'
languages: [
{
title: 'English', flag: 'en', value: 'en'
},
{
title: "Slovenský", flag: "sk", value: "sk"
}
],
defaultLanguage: 'sk'
},
moxios: {
routes: {
@@ -83,6 +95,8 @@ createApp(App)
.use(IonicVue)
.use(vuetify)
.use(wToast)
.use(i18n, options)
// .use(VueCal)
.use(loader, {
color: '#6200EE'
})
@@ -2,40 +2,190 @@
<v-app-bar class="header">
<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">
<h1>{{ $route.name }}</h1>
<div class="d-flex align-center justify-space-between w-100 responsive-position">
<h1 class="mr-md-4 route-name mb-5">{{ $route.name }}</h1>
<div class="d-flex justify-center align-center">
<img class="mx-3" :src="require('../_img/search-icon.svg')" alt="search-icon" />
<input class="mx-2" type="text" placeholder="Vyhľadávanie" />
<v-btn class="create-button">+ Vytvoriť</v-btn>
<div class="d-flex justify-center align-center pos-col">
<img
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>
</v-app-bar>
</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>
.route-name
font-size: 22px
.header
position: relative !important
box-shadow: none !important
border-bottom: 1px solid rgba(0, 0, 0, 0.2)
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)
.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
.menu-icon
display: none
padding: 75px 0
.dropdown-hover
cursor: pointer !important
&:hover
background: #F2E7FE
font-weight: bolder
</style>
@@ -6,7 +6,7 @@
<ion-list lines="none" id="labels-list">
<ion-item v-for="page in pages" :key="page.pageName" :class="{active: isActive(page.pageName)}"
@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-list>
</ion-content>
@@ -96,4 +96,12 @@ ion-item
ion-menu
width: 300px
--width: 300px
.unfinished-tasks
color: #fff
background: #B00020
border-radius: 50%
padding: 0 4px
margin-bottom: 15px
width: fit-content
</style>
@@ -33,7 +33,4 @@ export default {
.split-pane-right-side
width: calc(100vw - 300px) !important
max-width: calc(100vw - 300px) !important
.main-content
padding: 20px 15px
</style>
@@ -1,7 +1,7 @@
<template>
<header class="login-header-border ">
<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>
</header>
</template>
+20 -4
View File
@@ -1,5 +1,21 @@
span.highlight
color: #7F39FB
.task-action
.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
cursor: pointer
th
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"
></v-select>
<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
class="subheader-input disabled"
:items="items"
@@ -17,7 +17,7 @@
variant="outlined"
append-inner-icon="mdi-calendar-blank-outline"
></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>
<v-text-field
class="subheader-input"
@@ -57,7 +57,13 @@ export default {
width: 56px
min-width: auto !important
padding: 0
margin: 0 10px
margin: 0
&.prev
margin-right: 10px
&.next
margin-left: 10px
&:deep(.v-btn__append)
margin: 0
@@ -75,7 +81,4 @@ export default {
.date-wrapper
flex: 0 1 402px
</style>
@@ -1,326 +1,220 @@
<template>
<div>
<v-sheet tile height="54" color="grey lighten-3" class="d-flex">
<v-btn icon class="ma-2" @click="$refs.calendar.prev()">
<v-icon>mdi-chevron-left</v-icon>
</v-btn>
<v-select v-model="type" :items="types" dense outlined hide-details class="ma-2" label="type"></v-select>
<v-select v-model="mode" :items="modes" dense outlined hide-details label="event-overlap-mode" class="ma-2"></v-select>
<v-select v-model="weekday" :items="weekdays" dense outlined hide-details label="weekdays" class="ma-2"></v-select>
<v-spacer></v-spacer>
<v-btn icon class="ma-2" @click="$refs.calendar.next()">
<v-icon>mdi-chevron-right</v-icon>
</v-btn>
</v-sheet>
<v-sheet height="600">
<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">
<template #event="{ event, timed }">
<div class="pl-1" v-html="getEventHTML(event, timed)"></div>
<div v-if="timed" class="v-event-drag-bottom" @mousedown.stop="extendBottom(event)"></div>
</template>
<template #day-body="{ date, week }">
<div class="v-current-time" :class="{ first: date === week[0].date }" :style="{ top: nowY }">
<div class="v-current-time-time">
{{ nowTime }}
</div>
</div>
</template>
&nbsp;
</v-calendar>
<!-- {{ lastEvent }} -->
</v-sheet>
<div class="calendar-wrapper">
<Vue-cal
:hide-view-selector="true"
:hide-title-bar="true"
:disable-views="['years', 'year', 'month', 'day']"
:time-from="8 * 60"
:time-to="24 * 60"
:hide-weekends="true"
:selected-date="new Date()"
:onEventClick="taskClicked"
:editable-events="vueCalConfig.editing"
:events="events"
:min-event-width="90"
:overlaps-per-time-step="true"
:time-cell-height="200"
:min-cell-width="300"
sticky-split-labels
>
<template v-slot:weekday-heading="{ heading }">
<span class="d-flex align-center">
<img v-if="getHeadingDate(heading.date) == 'Dnes'" :src="require('./_img/home-icon.svg')" class="mr-2">
<p>{{ getHeadingDate(heading.date) }}</p>
</span>
</template>
<template v-slot:time-cell="{ hours, minutes }">
<span class="d-flex align-center justify-center">
<p>{{hours}}:{{String(minutes).padStart(2, '0')}}</p>
<div class="time-column-line"></div>
</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>
</template>
<script>
// import VueCal from 'vue-cal'
// import 'vue-cal/dist/vuecal.css'
export default {
components: {
// VueCal,
},
data() {
return {
type: 'day',
types: ['month', 'week', 'day', '4day'],
mode: 'stack',
modes: ['stack', 'column'],
weekday: [0, 1, 2, 3, 4, 5, 6],
weekdays: [
{ text: 'Sun - Sat', value: [0, 1, 2, 3, 4, 5, 6] },
{ text: 'Mon - Sun', value: [1, 2, 3, 4, 5, 6, 0] },
{ text: 'Mon - Fri', value: [1, 2, 3, 4, 5] },
{ text: 'Mon, Wed, Fri', value: [1, 3, 5] }
events: [
{
start: '2022-11-21 8:00',
end: '2022-11-21 13:30',
title: 'Running Marathon',
content: '<i class="icon material-icons">directions_run</i>',
class: 'red',
id: 1
},
{
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,
dragStart: null,
lastEvent: '',
createEvent: null,
createStart: null,
extendOriginal: null,
vueCalConfig: {
editing: {
title: false,
drag: true,
resize: false,
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: {
getEvents({ start, end }) {
const events = []
getHeadingDate(value) {
const date = new Date(value)
const min = new Date(`${start.date}T00:00:00`)
const max = new Date(`${end.date}T23:59:59`)
const days = (max.getTime() - min.getTime()) / 86400000
const eventCount = this.rnd(days, days + 20)
const today = new Date()
const yesterday = new Date((new Date()).setDate(today.getDate() - 1))
const tomorrow = new Date((new Date()).setDate(today.getDate() + 1))
for (let i = 0; i < eventCount; i++) {
const allDay = this.rnd(0, 3) === 0
const firstTimestamp = this.rnd(min.getTime(), max.getTime())
const first = new Date(firstTimestamp - (firstTimestamp % 900000))
const secondTimestamp = this.rnd(2, allDay ? 288 : 8) * 900000
const second = new Date(first.getTime() + secondTimestamp)
if (today.toLocaleDateString('sk-SK') === date.toLocaleDateString('sk-SK')) return "Dnes"
if (yesterday.toLocaleDateString('sk-SK') === date.toLocaleDateString('sk-SK')) return "Včera"
if (tomorrow.toLocaleDateString('sk-SK') === date.toLocaleDateString('sk-SK')) return "Zajtra"
events.push({
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)]
})
}
return date.toLocaleDateString('sk-SK')
},
this.events = events
},
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()}`
taskClicked(task) {
console.log("task", task)
}
}
}
</script>
<style lang="sass" scoped>
.v-calendar
user-select: none
-webkit-user-select: none
.calendar-wrapper
width: 100%
overflow-x: scroll
padding-top: 50px
.v-event-drag-bottom
position: absolute
left: 0
right: 0
bottom: 4px
height: 4px
cursor: ns-resize
.vuecal
transform: scale(1)
.v-event-drag-bottom::after
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-deep
.v-event-timed:hover .v-event-drag-bottom::after
display: block
.vuecal__cells
-ms-overflow-style: none
scrollbar-width: none
.v-current-time
height: 1px
background-color: red
position: absolute
left: -1px
right: 0
pointer-events: none
&::-webkit-scrollbar
display: none
&.first::after
content: ''
position: absolute
background-color: red
width: 7px
height: 7px
border-radius: 3.5px
margin-top: -3px
margin-left: -3px
.vuecal__body
max-height: 100%
overflow-y: scroll
position: relative !important
-ms-overflow-style: none
scrollbar-width: none
&::-webkit-scrollbar
display: none
.v-current-time-time
display: none
.weekday-label
justify-content: start !important
font-size: 24px
font-weight: 700
.vuecal__weekdays-headings,
.vuecal__cell:before
border: none
&.first .v-current-time-time
display: block
position: absolute
left: -60px
color: red
font-size: 10px
padding: 1px
background-color: white
width: 50px
height: 20px
text-align: right
top: -9px
.vuecal__time-column
width: 150px
box-shadow: none !important
.time-column-line
width: 40px
height: 0
border: 1px solid rgba(33, 33, 33, 0.08)
margin-left: 10px
.vuecal__flex
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>
@@ -39,23 +39,9 @@ export default {
</script>
<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
border: thin solid rgba(var(--v-border-color), var(--v-border-opacity))
@media screen and (max-width: 496px)
.task-action
div
@@ -65,4 +51,5 @@ v-table, th, td
.pencil-icon, .trash-icon
border-radius: 50%
margin: 6px
padding: 6px
</style>
@@ -37,24 +37,10 @@ export default {
</script>
<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
border: thin solid rgba(var(--v-border-color), var(--v-border-opacity))
@media screen and (max-width: 496px)
@media screen and (max-width: 379px)
.task-action
div
display: flex
@@ -63,4 +49,5 @@ v-table, th, td
.pencil-icon, .trash-icon
border-radius: 50%
margin: 6px
padding: 6px
</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>
<div></div>
<W-loadable :content="teams">
<A-teams-table :teams="teams" />
</W-loadable>
</template>
<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>
+3 -3
View File
@@ -2,7 +2,7 @@ import { Language, Options } from "../types"
import { Ref, ref } from 'vue'
import axios from 'axios'
import { createI18n, I18n as I18nType } from 'vue-i18n'
import { Storage } from '@capacitor/storage'
import { Preferences } from '@capacitor/preferences'
export class I18n {
@@ -30,7 +30,7 @@ export class I18n {
}
public async initCurrentLanguage() {
const { value } = await Storage.get({ key: this.languageKey })
const { value } = await Preferences.get({ key: this.languageKey })
if (value) {
this.currentLanguage.value = JSON.parse(value)
@@ -58,7 +58,7 @@ export class I18n {
this.currentLanguage.value = language
this._addAxiosHeaders()
Storage.set({
Preferences.set({
key: this.languageKey,
value: JSON.stringify(language)
})