Merge branch 'master' into UC-19

This commit is contained in:
Алексей Дёмин
2022-10-21 20:46:11 +03:00
committed by GitHub
12 changed files with 451 additions and 74 deletions

179
server.js
View File

@@ -248,6 +248,185 @@ export function clientsServer() {
patronymic: el.patronymic, patronymic: el.patronymic,
})); }));
}); });
this.get("/registry/event/", () => ({
count: 4,
next: null,
previous: null,
results: [
{
id: "6faa6bb6-1de6-422c-a401-1b35cd455303",
start: "2022-10-20T20:23:49Z",
end: "2022-10-20T21:23:53Z",
kind: "call",
subkind: null,
description: "",
location: null,
members: [
{
id: "b0509f9a-e824-4bb3-a260-2b32dae9bc81",
person: {
id: "14ff401c-2b7b-4d9a-9159-720c74d8b23c",
last_name: "Гагарин",
first_name: "Юрий",
patronymic: "Алексеевич",
},
role: null,
},
],
employees: [
{
id: "f941a0c6-c750-4f79-92c0-d7a19354e422",
employee: {
id: "db541a67-e836-481f-a653-6325de7c567e",
last_name: "Захарова",
first_name: "Ольга",
patronymic: "Александровна",
},
role: null,
},
{
id: "f941a0c6-c750-4f79-92c0-d7a19354e422",
employee: {
id: "db831a14-e836-481f-a653-6325de7c311e",
last_name: "Жмыхов",
first_name: "Егор",
patronymic: "Сергеевич",
},
role: "owner",
},
],
},
{
id: "7faa7bb7-2de6-422c-a401-1b35cd455303",
start: "2022-10-20T10:00:00Z",
end: "2022-10-20T11:10:00Z",
kind: "call",
subkind: null,
description: "",
location: null,
members: [
{
id: "b0509f9a-e824-4bb3-a260-2b32dae9bc81",
person: {
id: "14ff401c-2b7b-4d9a-9159-720c74d8b23c",
last_name: "Гагарин",
first_name: "Юрий",
patronymic: "Алексеевич",
},
role: null,
},
],
employees: [
{
id: "f941a0c6-c734-4f79-92c2-d7a5674459e422",
employee: {
id: "db831a15-e876-481f-a658-6325de7c311e",
last_name: "Константинопольская",
first_name: "Юлия",
patronymic: "Викторовна",
},
role: null,
},
{
id: "f941a0c6-c750-4f79-92c0-d7a19354e422",
employee: {
id: "db541a67-e836-481f-a653-6325de7c567e",
last_name: "Захарова",
first_name: "Ольга",
patronymic: "Александровна",
},
role: "owner",
},
],
},
{
id: "2faa7bb2-2de6-422c-a401-1b35cd455223",
start: "2022-10-20T10:00:00Z",
end: "2022-10-20T11:10:00Z",
kind: "call",
subkind: null,
description: "",
location: null,
members: [
{
id: "b0509f9a-e824-4bb3-a260-2b32dae9bc81",
person: {
id: "14ff401c-2b7b-4d9a-9159-720c74d8b23c",
last_name: "Гагарин",
first_name: "Юрий",
patronymic: "Алексеевич",
},
role: null,
},
],
employees: [
{
id: "f941a0c6-c734-4f79-92c2-d7a5674459e422",
employee: {
id: "db831a15-e876-481f-a658-6325de7c311e",
last_name: "Константинопольская",
first_name: "Юлия",
patronymic: "Викторовна",
},
role: "owner",
},
{
id: "f941a0c6-c750-4f79-92c0-d7a19354e422",
employee: {
id: "db541a67-e836-481f-a653-6325de7c567e",
last_name: "Захарова",
first_name: "Ольга",
patronymic: "Александровна",
},
role: null,
},
],
},
{
id: "6faa6bb2-1de4-425c-a401-1b35cd455303",
start: "2022-10-20T20:23:49Z",
end: "2022-10-20T21:23:53Z",
kind: "call",
subkind: null,
description: "",
location: null,
members: [
{
id: "b0509f9a-e824-4bb3-a260-2b32dae9bc81",
person: {
id: "14ff401c-2b7b-4d9a-9159-720c74d8b23c",
last_name: "Гагарин",
first_name: "Юрий",
patronymic: "Алексеевич",
},
role: null,
},
],
employees: [
{
id: "f941a0c6-c750-4f79-92c0-d7a19354e422",
employee: {
id: "db541a67-e836-481f-a653-6325de7c567e",
last_name: "Захарова",
first_name: "Ольга",
patronymic: "Александровна",
},
role: null,
},
{
id: "f941a0c6-c750-4f79-92c0-d7a19354e422",
employee: {
id: "db831a14-e836-481f-a653-6325de7c311e",
last_name: "Жмыхов",
first_name: "Егор",
patronymic: "Сергеевич",
},
role: null,
},
],
},
],
}));
this.passthrough("http://45.84.227.122:8080/**"); this.passthrough("http://45.84.227.122:8080/**");
this.get("/api/detail/:id", (schema, request) => { this.get("/api/detail/:id", (schema, request) => {
let id = request.params.id; let id = request.params.id;

View File

@@ -1,5 +1,5 @@
<template lang="pug"> <template lang="pug">
.flex.flex-col.w-full.h-full.gap-y-4 .flex.flex-col.w-full.h-full.gap-y-2
the-header the-header
.flex.flex-auto .flex.flex-auto
the-sidebar the-sidebar

View File

@@ -56,7 +56,7 @@ export default {
.header .header
width: 314px width: 314px
height: 42px height: 40px
position: relative position: relative
background-color: var(--btn-blue-color) background-color: var(--btn-blue-color)
border-radius: 40px border-radius: 40px

View File

@@ -40,6 +40,7 @@ export default {
transform: rotate(180deg) transform: rotate(180deg)
.header-inputs-wrapper .header-inputs-wrapper
height: 40px
background-color: var(--default-white) background-color: var(--default-white)
border-radius: 4px border-radius: 4px
border: 1px solid var(--border-light-grey-color) border: 1px solid var(--border-light-grey-color)

View File

@@ -45,6 +45,7 @@ export default {
.header-wrapper .header-wrapper
width: 100% width: 100%
height: 56px
background-color: var(--default-white) background-color: var(--default-white)
position: relative position: relative
z-index: 2 z-index: 2

View File

@@ -1,8 +1,10 @@
<template lang="pug"> <template lang="pug">
.calendar-container .calendar-container.flex
calendar-sidebar
calendar-schedule( calendar-schedule(
:current-date="currentDate" :current-date="currentDate"
:time-information="timeInformation" :time-information="timeInformation"
:events-data="eventsData"
@previous-date="switchPreviousDate" @previous-date="switchPreviousDate"
@next-date="switchNextDate" @next-date="switchNextDate"
@selected-layout="changeCalendarLayout" @selected-layout="changeCalendarLayout"
@@ -12,9 +14,10 @@
<script> <script>
import * as moment from "moment/moment"; import * as moment from "moment/moment";
import CalendarSchedule from "./components/CalendarSchedule.vue"; import CalendarSchedule from "./components/CalendarSchedule.vue";
import CalendarSidebar from "./components/CalendarSidebar.vue";
export default { export default {
name: "TheCalendar", name: "TheCalendar",
components: { CalendarSchedule }, components: { CalendarSchedule, CalendarSidebar },
data() { data() {
return { return {
calendarLayout: "", calendarLayout: "",
@@ -40,7 +43,7 @@ export default {
this.eventsData = res.results; this.eventsData = res.results;
}, },
fetchEventsData() { fetchEventsData() {
fetch("http://45.84.227.122:8080/registry/event/") fetch("/registry/event/")
.then((res) => res.json()) .then((res) => res.json())
.then((res) => this.saveEventsData(res)); .then((res) => this.saveEventsData(res));
}, },
@@ -53,5 +56,5 @@ export default {
<style lang="sass" scoped> <style lang="sass" scoped>
.calendar-container .calendar-container
width: 100% width: calc(100vw - 80px)
</style> </style>

View File

@@ -1,85 +1,135 @@
<template lang="pug"> <template lang="pug">
.calendar-background-wrapper.flex.flex-col .calendar-background-wrapper.flex.flex-col(
.header.flex.items-center.justify-between.py-2.px-6 ref="backgroundWrapper"
.body.flex.flex-col :class="scrollPresence"
)
calendar-column(
v-for="(owner, index) in filteredOwners"
:key="owner.id"
:owner-data="owner"
:style="calculateColumnPosition(index)"
)
.header(:style="backgroundExtendedWidth")
.body.flex.flex-col(
:style="backgroundExtendedWidth"
)
.line-wrapper .line-wrapper
.line.flex.items-center( .line.flex.items-center(
v-for="hour in hoursArray" v-for="hour in hoursArray"
:key="hour" :key="hour"
) )
.middle-line .middle-line
.time-circle-indicator.-left-6px(
v-if="isShownIndicator"
:style="circleIndicatorLocation"
)
span.time-line-indicator.block(
v-if="isShownIndicator"
:style="lineIndicatorLocation"
)
</template> </template>
<script> <script>
import * as moment from "moment/moment"; import CalendarColumn from "./CalendarColumn.vue";
export default { export default {
name: "CalendarBackground", name: "CalendarBackground",
components: { CalendarColumn },
props: { props: {
hoursArray: Array, hoursArray: Array,
currentTime: String, eventsData: Array,
currentDate: Object,
dayStartTime: Number,
dayEndTime: Number,
}, },
data() { data() {
return { return {
isShownIndicator: true, backgroundWidth: 0,
columnWidth: 0,
defaultColumnWidth: 470,
pixelsPerHour: 62, pixelsPerHour: 62,
}; };
}, },
computed: { computed: {
lineIndicatorLocation() { ownersArrayLength() {
return this.filteredOwners.length;
},
backgroundExtendedWidth() {
if (this.ownersArrayLength > 3) {
return { return {
top: `${this.calculateIndicatorLocation()}px`, width: `${this.defaultColumnWidth * this.ownersArrayLength}px`,
};
}
return {
width: "auto",
}; };
}, },
circleIndicatorLocation() { backgroundHeight() {
return (this.hoursArray.length - 1) * this.pixelsPerHour + 48;
},
scrollPresence() {
return { return {
top: `${this.calculateIndicatorLocation() - 6}px`, scroll: this.ownersArrayLength > 3,
}; };
}, },
scheduleSize() { filteredOwners() {
return (this.dayEndTime - this.dayStartTime) * this.pixelsPerHour; let filteredArray = [];
}, let ownerAbsence = {
pixelsPerMinute() { id: null,
return this.pixelsPerHour / 60; last_name: null,
first_name: null,
patronymic: null,
};
this.eventsData.forEach(({ employees }) => {
let findedElement = employees.find((elem) => elem.role === "owner");
let emptyObjectPresence = this.findObjectInArray(
filteredArray,
ownerAbsence
);
if (!findedElement && !emptyObjectPresence) {
filteredArray.push(ownerAbsence);
}
if (findedElement) {
let ownerPresence = this.findObjectInArray(
filteredArray,
findedElement.employee
);
if (!ownerPresence) {
filteredArray.push(findedElement.employee);
}
}
});
return filteredArray.sort(
(previous, subsequent) => Boolean(subsequent.id) - Boolean(previous.id)
);
}, },
}, },
methods: { methods: {
calculateIndicatorLocation() { calculateColumnPosition(elemIndex) {
let newTime = this.currentTime if (this.ownersArrayLength < 4) {
.split(":") this.columnWidth = this.backgroundWidth / this.ownersArrayLength;
.map((elem) => parseInt(elem, 10)); return {
let result = width: `${this.columnWidth}px`,
(newTime[0] - this.dayStartTime) * this.pixelsPerHour + height: `${this.backgroundHeight}px`,
newTime[1] * this.pixelsPerMinute; left: `${elemIndex * this.columnWidth}px`,
if (result > this.scheduleSize || result < 0) { };
this.isShownIndicator = false;
return 0;
} }
return result; return {
width: `${this.defaultColumnWidth}px`,
height: `${this.backgroundHeight}px`,
left: `${elemIndex * this.defaultColumnWidth}px`,
};
},
calculateBackgroundWidth() {
this.backgroundWidth = this.$refs.backgroundWrapper.offsetWidth;
},
findObjectInArray(array, object) {
return array.find(
(item) => JSON.stringify(item) === JSON.stringify(object)
);
}, },
}, },
watch: { mounted() {
currentDate: function () { this.calculateBackgroundWidth();
this.isShownIndicator =
this.currentDate.format("DD.MM.YYYY") === moment().format("DD.MM.YYYY");
},
}, },
}; };
</script> </script>
<style lang="sass" scoped> <style lang="sass" scoped>
.scroll
overflow-x: scroll
.calendar-background-wrapper .calendar-background-wrapper
width: 100% width: 100%
position: relative
.header .header
height: 48px height: 48px
@@ -99,16 +149,4 @@ export default {
.middle-line .middle-line
border-top: 1px dashed var(--border-light-grey-color) border-top: 1px dashed var(--border-light-grey-color)
width: 100% width: 100%
.time-line-indicator
width: 100%
border-top: 1px solid var(--time-indicator-color)
position: absolute
.time-circle-indicator
width: 12px
height: 12px
background-color: var(--time-indicator-color)
border-radius: 50%
position: absolute
</style> </style>

View File

@@ -1,6 +1,6 @@
<template lang="pug"> <template lang="pug">
.calendar-clock-column.flex.flex-col.items-end.gap-y-43.pt-9.pb-12.px-3 .calendar-clock-column.flex.flex-col.items-end.gap-y-43.pt-9.pb-45px.px-3
span.font-medium.text-base( span.text-base(
v-for="hour in hoursArray" v-for="hour in hoursArray"
:key="hour" :key="hour"
:class="currentHourStyle(hour)" :class="currentHourStyle(hour)"
@@ -42,6 +42,7 @@ export default {
} }
return { return {
"current-time": false, "current-time": false,
"font-medium": true,
}; };
}, },
convertTime(str, startIndex, endIndex) { convertTime(str, startIndex, endIndex) {

View File

@@ -0,0 +1,52 @@
<template lang="pug">
.calendar-column-wrapper.flex.flex-col
.header.flex.items-center.justify-between.py-2.px-6
.flex.items-center
img.avatar-wrapper.mr-2(src="@/assets/images/team-member.svg" alt="Team member")
span.owner-name.font-medium.text-base.mr-6 {{ ownerName }}
img.icon-wrapper.cursor-pointer(src="@/assets/icons/lock.svg")
base-doc-ok-button
div
</template>
<script>
import BaseDocOkButton from "@/components/base/buttons/BaseDocOkButton.vue";
export default {
name: "CalendarColumn",
components: { BaseDocOkButton },
props: {
ownerData: Object,
},
computed: {
ownerName() {
if (this.ownerData.id) {
return `${this.ownerData.last_name} ${this.ownerData.first_name.slice(
0,
1
)}.${this.ownerData.patronymic.slice(0, 1)}.`;
}
return null;
},
},
};
</script>
<style lang="sass" scoped>
.calendar-column-wrapper
position: absolute
border-right: 1px solid var(--border-light-grey-color)
.header
height: 48px
.avatar-wrapper
width: 32px
height: 32px
.icon-wrapper
width: 24px
height: 24px
.owner-name
color: var(--font-dark-blue-color)
</style>

View File

@@ -1,5 +1,5 @@
<template lang="pug"> <template lang="pug">
.schedule.ml-2 .schedule.ml-2.w-full
calendar-header( calendar-header(
:current-date="currentDate" :current-date="currentDate"
:is-current-date="isCurrentDate" :is-current-date="isCurrentDate"
@@ -8,6 +8,7 @@
@selected-layout="selectedLayout" @selected-layout="selectedLayout"
) )
.schedule-body.flex .schedule-body.flex
div
calendar-clock-column( calendar-clock-column(
:hours-array="hoursArray" :hours-array="hoursArray"
:current-time="currentTime" :current-time="currentTime"
@@ -16,10 +17,15 @@
) )
calendar-background( calendar-background(
:hours-array="hoursArray" :hours-array="hoursArray"
:current-time="currentTime" :events-data="eventsData"
:current-date="currentDate" )
:day-start-time="validateStartTime" .time-circle-indicator.left-74px(
:day-end-time="validateEndTime" v-if="isShownIndicator"
:style="circleIndicatorLocation"
)
span.time-line-indicator.block.left-20(
v-if="isShownIndicator"
:style="lineIndicatorLocation"
) )
</template> </template>
@@ -30,7 +36,11 @@ import CalendarBackground from "./CalendarBackground.vue";
import CalendarClockColumn from "./CalendarClockColumn.vue"; import CalendarClockColumn from "./CalendarClockColumn.vue";
export default { export default {
name: "CalendarSchedule", name: "CalendarSchedule",
components: { CalendarHeader, CalendarBackground, CalendarClockColumn }, components: {
CalendarHeader,
CalendarBackground,
CalendarClockColumn,
},
props: { props: {
currentDate: { currentDate: {
type: Object, type: Object,
@@ -44,6 +54,12 @@ export default {
return {}; return {};
}, },
}, },
eventsData: {
type: Array,
default() {
return [];
},
},
}, },
data() { data() {
return { return {
@@ -51,6 +67,9 @@ export default {
hoursArray: [], hoursArray: [],
timer: null, timer: null,
isCurrentDate: true, isCurrentDate: true,
isShownIndicator: true,
pixelsPerHour: 62,
columnHeaderHeight: 48,
}; };
}, },
computed: { computed: {
@@ -69,6 +88,25 @@ export default {
validateEndTime() { validateEndTime() {
return this.verifyTime(this.timeInformation.dayEndTime); return this.verifyTime(this.timeInformation.dayEndTime);
}, },
lineIndicatorLocation() {
return {
top: `${this.calculateIndicatorLocation()}px`,
};
},
circleIndicatorLocation() {
return {
top: `${this.calculateIndicatorLocation() - 6}px`,
};
},
pixelsPerMinute() {
return this.pixelsPerHour / 60;
},
scheduleSize() {
return (
(this.validateEndTime - this.validateStartTime) * this.pixelsPerHour +
this.columnHeaderHeight
);
},
}, },
methods: { methods: {
previousDate() { previousDate() {
@@ -126,6 +164,20 @@ export default {
convertTime(str, startIndex, endIndex) { convertTime(str, startIndex, endIndex) {
return parseInt(str.slice(startIndex, endIndex), 10); return parseInt(str.slice(startIndex, endIndex), 10);
}, },
calculateIndicatorLocation() {
let newTime = this.currentTime
.split(":")
.map((elem) => parseInt(elem, 10));
let result =
(newTime[0] - this.validateStartTime) * this.pixelsPerHour +
newTime[1] * this.pixelsPerMinute +
this.columnHeaderHeight;
if (result > this.scheduleSize || result < 0) {
this.isShownIndicator = false;
return 0;
}
return result;
},
}, },
watch: { watch: {
currentTime() { currentTime() {
@@ -141,6 +193,7 @@ export default {
currentDate: function () { currentDate: function () {
this.isCurrentDate = this.isCurrentDate =
this.currentDate.format("DD.MM.YYYY") === moment().format("DD.MM.YYYY"); this.currentDate.format("DD.MM.YYYY") === moment().format("DD.MM.YYYY");
this.isShownIndicator = this.isCurrentDate;
if (this.timer) { if (this.timer) {
this.stopTimer(); this.stopTimer();
this.hoursArrayInitialization(); this.hoursArrayInitialization();
@@ -166,4 +219,20 @@ export default {
<style lang="sass" scoped> <style lang="sass" scoped>
.schedule .schedule
background-color: var(--default-white) background-color: var(--default-white)
width: calc(100% - 80px)
.time-line-indicator
width: calc(100% - 80px)
border-top: 1px solid var(--time-indicator-color)
position: absolute
.time-circle-indicator
width: 12px
height: 12px
background-color: var(--time-indicator-color)
border-radius: 50%
position: absolute
.schedule-body
position: relative
</style> </style>

View File

@@ -0,0 +1,31 @@
<template lang="pug">
.sidebar.flex.flex-col.bg-white
.sidebar-wrapper.h-full
.sidebar-content.items-center
base-button-plus
</template>
<script>
import BaseButtonPlus from "../../../components/base/buttons/BaseButtonPlus.vue";
export default {
components: {
BaseButtonPlus,
},
};
</script>
<style lang="sass" scoped>
.sidebar
width: 72px
.sidebar-wrapper
border-left: 2px solid var(--btn-blue-color-3)
margin: 13px 0
.sidebar-content
padding: 19px 16px
.button-plus
width: 40px
height: 40px
max-height: 40px
background: var(--btn-blue-color)
color: var(--default-white)
</style>

View File

@@ -27,6 +27,8 @@ module.exports = {
"6px": "6px", "6px": "6px",
"10px": "10px", "10px": "10px",
"3px": "3px", "3px": "3px",
"74px": "74px",
"45px": "45px",
}, },
}, },
}, },