Client - init calendar on Dashboard
This commit is contained in:
parent
a85860581f
commit
2ae2cf04d5
@ -14,6 +14,7 @@
|
|||||||
"chart.js": "^3.5.1",
|
"chart.js": "^3.5.1",
|
||||||
"core-js": "^3.6.5",
|
"core-js": "^3.6.5",
|
||||||
"date-fns": "^2.23.0",
|
"date-fns": "^2.23.0",
|
||||||
|
"date-fns-tz": "^1.1.6",
|
||||||
"register-service-worker": "^1.7.1",
|
"register-service-worker": "^1.7.1",
|
||||||
"vue": "^3.0.0",
|
"vue": "^3.0.0",
|
||||||
"vue-chart-3": "^0.5.8",
|
"vue-chart-3": "^0.5.8",
|
||||||
|
BIN
fittrackee_client/public/img/sports/cycling-sport.png
Normal file
BIN
fittrackee_client/public/img/sports/cycling-sport.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.4 KiB |
BIN
fittrackee_client/public/img/sports/cycling-transport.png
Normal file
BIN
fittrackee_client/public/img/sports/cycling-transport.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.5 KiB |
BIN
fittrackee_client/public/img/sports/hiking.png
Normal file
BIN
fittrackee_client/public/img/sports/hiking.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.0 KiB |
BIN
fittrackee_client/public/img/sports/mountain-biking.png
Normal file
BIN
fittrackee_client/public/img/sports/mountain-biking.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.3 KiB |
BIN
fittrackee_client/public/img/sports/running.png
Normal file
BIN
fittrackee_client/public/img/sports/running.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.0 KiB |
BIN
fittrackee_client/public/img/sports/walking.png
Normal file
BIN
fittrackee_client/public/img/sports/walking.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.5 KiB |
@ -1,3 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div>Calendar</div>
|
|
||||||
</template>
|
|
@ -0,0 +1,151 @@
|
|||||||
|
<template>
|
||||||
|
<div class="calendar-cells">
|
||||||
|
<div class="calendar-row" v-for="(row, index) in rows" :key="index">
|
||||||
|
<div
|
||||||
|
class="calendar-cell"
|
||||||
|
:class="{
|
||||||
|
'disabled-cell': !isSameMonth(day, currentDay),
|
||||||
|
'week-end': isWeekEnd(i),
|
||||||
|
today: isToday(day),
|
||||||
|
}"
|
||||||
|
v-for="(day, i) in row"
|
||||||
|
:key="i"
|
||||||
|
>
|
||||||
|
<CalendarWorkouts
|
||||||
|
:workouts="filterWorkouts(day, workouts)"
|
||||||
|
:sports="sports"
|
||||||
|
/>
|
||||||
|
<div class="calendar-cell-day">
|
||||||
|
{{ format(day, 'd') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { addDays, format, isSameDay, isSameMonth, isToday } from 'date-fns'
|
||||||
|
import { defineComponent, PropType, toRefs } from 'vue'
|
||||||
|
|
||||||
|
import CalendarWorkouts from '@/components/Dashboard/UserCalendar/CalendarWorkouts.vue'
|
||||||
|
import { ISport } from '@/types/sports'
|
||||||
|
import { IWorkout } from '@/types/workouts'
|
||||||
|
import { getDateWithTZ } from '@/utils/dates'
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'CalendarCells',
|
||||||
|
components: {
|
||||||
|
CalendarWorkouts,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
currentDay: {
|
||||||
|
type: Date,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
endDate: {
|
||||||
|
type: Date,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
sports: {
|
||||||
|
type: Object as PropType<ISport[]>,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
startDate: {
|
||||||
|
type: Date,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
timezone: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
weekStartingMonday: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
workouts: {
|
||||||
|
type: Object as PropType<IWorkout[]>,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
setup(props) {
|
||||||
|
const rows = []
|
||||||
|
let { startDate, endDate, weekStartingMonday } = toRefs(props)
|
||||||
|
let day = startDate.value
|
||||||
|
while (day <= endDate.value) {
|
||||||
|
const days = []
|
||||||
|
for (let i = 0; i < 7; i++) {
|
||||||
|
days.push(day)
|
||||||
|
day = addDays(day, 1)
|
||||||
|
}
|
||||||
|
rows.push(days)
|
||||||
|
}
|
||||||
|
|
||||||
|
function isWeekEnd(day: number): boolean {
|
||||||
|
return weekStartingMonday ? [0, 6].includes(day) : [5, 6].includes(day)
|
||||||
|
}
|
||||||
|
|
||||||
|
function filterWorkouts(day: Date, workouts: IWorkout[]) {
|
||||||
|
if (workouts) {
|
||||||
|
return workouts
|
||||||
|
.filter((workout) =>
|
||||||
|
isSameDay(
|
||||||
|
getDateWithTZ(workout.workout_date, props.timezone),
|
||||||
|
day
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.reverse()
|
||||||
|
}
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
return { rows, format, isSameMonth, isToday, isWeekEnd, filterWorkouts }
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
@import '~@/scss/base';
|
||||||
|
.calendar-cells {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 100%;
|
||||||
|
flex-grow: 1;
|
||||||
|
|
||||||
|
.calendar-row {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
border-top: solid 1px var(--calendar-border-color);
|
||||||
|
|
||||||
|
.calendar-cell {
|
||||||
|
border-right: solid 1px var(--calendar-border-color);
|
||||||
|
height: 3em;
|
||||||
|
flex-grow: 1;
|
||||||
|
flex-basis: 10%;
|
||||||
|
padding: $default-padding * 0.5;
|
||||||
|
width: 10%;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.calendar-cell-day {
|
||||||
|
position: absolute;
|
||||||
|
font-size: 0.8em;
|
||||||
|
line-height: 1;
|
||||||
|
top: 0.5em;
|
||||||
|
right: 0.5em;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.calendar-cell:last-child {
|
||||||
|
border-right: 0;
|
||||||
|
}
|
||||||
|
.disabled-cell {
|
||||||
|
color: var(--app-color-light);
|
||||||
|
}
|
||||||
|
.week-end {
|
||||||
|
background: var(--calendar-week-end-color);
|
||||||
|
}
|
||||||
|
.today {
|
||||||
|
background: var(--calendar-today-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,50 @@
|
|||||||
|
<template>
|
||||||
|
<div class="calendar-days">
|
||||||
|
<div class="calendar-day" v-for="(day, index) in days" :key="index">
|
||||||
|
{{ format(day, 'EEE', localeOptions) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { format, addDays } from 'date-fns'
|
||||||
|
import { defineComponent } from 'vue'
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'CalendarDays',
|
||||||
|
props: {
|
||||||
|
startDate: {
|
||||||
|
type: Date,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
localeOptions: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
setup(props) {
|
||||||
|
const days = []
|
||||||
|
for (let i = 0; i < 7; i++) {
|
||||||
|
days.push(addDays(props.startDate, i))
|
||||||
|
}
|
||||||
|
return { days, addDays, format }
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
@import '~@/scss/base';
|
||||||
|
.calendar-days {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
border-top: solid 1px var(--calendar-border-color);
|
||||||
|
|
||||||
|
.calendar-day {
|
||||||
|
flex-grow: 1;
|
||||||
|
padding: $default-padding * 0.5;
|
||||||
|
text-align: center;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--app-color-light);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,61 @@
|
|||||||
|
<template>
|
||||||
|
<div class="calendar-header">
|
||||||
|
<div class="calendar-arrow calendar-arrow-left">
|
||||||
|
<i class="fa fa-chevron-left" aria-hidden="true" />
|
||||||
|
</div>
|
||||||
|
<div class="calendar-month">
|
||||||
|
<span>
|
||||||
|
{{ format(day, 'MMM yyyy', localeOptions) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="calendar-arrow calendar-arrow-right">
|
||||||
|
<i class="fa fa-chevron-right" aria-hidden="true" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { format } from 'date-fns'
|
||||||
|
import { defineComponent } from 'vue'
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'CalendarHeader',
|
||||||
|
props: {
|
||||||
|
day: {
|
||||||
|
type: Date,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
localeOptions: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
setup() {
|
||||||
|
return { format }
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
@import '~@/scss/base';
|
||||||
|
.calendar-header {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
|
||||||
|
.calendar-arrow,
|
||||||
|
.calendar-month {
|
||||||
|
flex-grow: 1;
|
||||||
|
padding: $default-padding;
|
||||||
|
}
|
||||||
|
.calendar-arrow-left {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
.calendar-arrow-right {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
.calendar-month {
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: center;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,34 @@
|
|||||||
|
<template>
|
||||||
|
<div class="calendar-workout">
|
||||||
|
<img alt="workout sport logo" :src="sportImg" :title="workout.title" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent, PropType } from 'vue'
|
||||||
|
|
||||||
|
import { IWorkout } from '@/types/workouts'
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'CalendarWorkouts',
|
||||||
|
props: {
|
||||||
|
workout: {
|
||||||
|
type: Object as PropType<IWorkout>,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
sportImg: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.calendar-workout {
|
||||||
|
img {
|
||||||
|
max-width: 18px;
|
||||||
|
max-height: 18px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,45 @@
|
|||||||
|
<template>
|
||||||
|
<div class="calendar-workouts">
|
||||||
|
<div v-for="(workout, index) in workouts" :key="index">
|
||||||
|
<CalendarWorkout
|
||||||
|
:workout="workout"
|
||||||
|
:sportImg="getSportImg(workout, sports)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent, PropType } from 'vue'
|
||||||
|
|
||||||
|
import CalendarWorkout from '@/components/Dashboard/UserCalendar/CalendarWorkout.vue'
|
||||||
|
import { ISport } from '@/types/sports'
|
||||||
|
import { IWorkout } from '@/types/workouts'
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'CalendarWorkouts',
|
||||||
|
components: {
|
||||||
|
CalendarWorkout,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
workouts: {
|
||||||
|
type: Object as PropType<IWorkout[]>,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
sports: {
|
||||||
|
type: Object as PropType<ISport[]>,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
setup() {
|
||||||
|
function getSportImg(workout: IWorkout, sports: ISport[]): string {
|
||||||
|
return sports
|
||||||
|
.filter((sport) => sport.id === workout.sport_id)
|
||||||
|
.map((sport) => sport.img)[0]
|
||||||
|
}
|
||||||
|
return { getSportImg }
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss"></style>
|
@ -0,0 +1,87 @@
|
|||||||
|
<template>
|
||||||
|
<div id="user-calendar">
|
||||||
|
<Card class="calendar-card">
|
||||||
|
<template #content>
|
||||||
|
<CalendarHeader :day="day" locale-options="enGB" />
|
||||||
|
<CalendarDays :start-date="calendarDates.start" locale-options="enGB" />
|
||||||
|
<CalendaCells
|
||||||
|
:currentDay="day"
|
||||||
|
:end-date="calendarDates.end"
|
||||||
|
:sports="sports"
|
||||||
|
:start-date="calendarDates.start"
|
||||||
|
:timezone="user.timezone"
|
||||||
|
:workouts="calendarWorkouts"
|
||||||
|
:weekStartingMonday="user.weekm"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { format } from 'date-fns'
|
||||||
|
import {
|
||||||
|
PropType,
|
||||||
|
defineComponent,
|
||||||
|
onBeforeMount,
|
||||||
|
ComputedRef,
|
||||||
|
computed,
|
||||||
|
} from 'vue'
|
||||||
|
|
||||||
|
import Card from '@/components/Common/Card.vue'
|
||||||
|
import CalendaCells from '@/components/Dashboard/UserCalendar/CalendarCells.vue'
|
||||||
|
import CalendarDays from '@/components/Dashboard/UserCalendar/CalendarDays.vue'
|
||||||
|
import CalendarHeader from '@/components/Dashboard/UserCalendar/CalendarHeader.vue'
|
||||||
|
import { SPORTS_STORE, WORKOUTS_STORE } from '@/store/constants'
|
||||||
|
import { IAuthUserProfile } from '@/types/user'
|
||||||
|
import { IWorkout, IWorkoutsPayload } from '@/types/workouts'
|
||||||
|
import { useStore } from '@/use/useStore'
|
||||||
|
import { getCalendarStartAndEnd } from '@/utils/dates'
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'UserCalendar',
|
||||||
|
components: {
|
||||||
|
CalendaCells,
|
||||||
|
CalendarDays,
|
||||||
|
CalendarHeader,
|
||||||
|
Card,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
user: {
|
||||||
|
type: Object as PropType<IAuthUserProfile>,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
setup(props) {
|
||||||
|
const store = useStore()
|
||||||
|
const dateFormat = 'yyyy-MM-dd'
|
||||||
|
const day = new Date()
|
||||||
|
const calendarDates = getCalendarStartAndEnd(day, props.user.weekm)
|
||||||
|
const apiParams: IWorkoutsPayload = {
|
||||||
|
from: format(calendarDates.start, dateFormat),
|
||||||
|
to: format(calendarDates.end, dateFormat),
|
||||||
|
order: 'desc',
|
||||||
|
per_page: 100,
|
||||||
|
}
|
||||||
|
const calendarWorkouts: ComputedRef<IWorkout[]> = computed(
|
||||||
|
() => store.getters[WORKOUTS_STORE.GETTERS.CALENDAR_WORKOUTS]
|
||||||
|
)
|
||||||
|
const sports = computed(() => store.getters[SPORTS_STORE.GETTERS.SPORTS])
|
||||||
|
|
||||||
|
onBeforeMount(() =>
|
||||||
|
store.dispatch(WORKOUTS_STORE.ACTIONS.GET_CALENDAR_WORKOUTS, apiParams)
|
||||||
|
)
|
||||||
|
|
||||||
|
return { day, calendarDates, calendarWorkouts, sports }
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
@import '~@/scss/base';
|
||||||
|
#user-calendar {
|
||||||
|
.calendar-card {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -52,7 +52,7 @@
|
|||||||
computed,
|
computed,
|
||||||
defineComponent,
|
defineComponent,
|
||||||
ref,
|
ref,
|
||||||
watch,
|
onBeforeMount,
|
||||||
} from 'vue'
|
} from 'vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
@ -106,18 +106,16 @@
|
|||||||
displayedData.value = event.target.name
|
displayedData.value = event.target.name
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(
|
function getStatistics() {
|
||||||
() => props.user.username,
|
store.dispatch(STATS_STORE.ACTIONS.GET_USER_STATS, {
|
||||||
async (newUsername) => {
|
username: props.user.username,
|
||||||
if (newUsername) {
|
filterType: 'by_time',
|
||||||
store.dispatch(STATS_STORE.ACTIONS.GET_USER_STATS, {
|
params: apiParams,
|
||||||
username: newUsername,
|
})
|
||||||
filterType: 'by_time',
|
}
|
||||||
params: apiParams,
|
|
||||||
})
|
onBeforeMount(() => getStatistics())
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
return {
|
return {
|
||||||
chartParams,
|
chartParams,
|
||||||
displayedData,
|
displayedData,
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
:root {
|
:root {
|
||||||
--app-background-color: #FFFFFF;
|
--app-background-color: #FFFFFF;
|
||||||
--app-color: #2c3e50;
|
--app-color: #2c3e50;
|
||||||
|
--app-color-light: #808b96;
|
||||||
--app-a-color: #40578a;
|
--app-a-color: #40578a;
|
||||||
--app-shadow-color: lightgrey;
|
--app-shadow-color: lightgrey;
|
||||||
--app-loading-color: #f3f3f3;
|
--app-loading-color: #f3f3f3;
|
||||||
@ -9,6 +10,10 @@
|
|||||||
--card-border-color: #c4c7cf;
|
--card-border-color: #c4c7cf;
|
||||||
--input-border-color: #9da3af;
|
--input-border-color: #9da3af;
|
||||||
|
|
||||||
|
--calendar-border-color: #c4c7cf;
|
||||||
|
--calendar-week-end-color: #f5f5f5;
|
||||||
|
--calendar-today-color: #eff1f3;
|
||||||
|
|
||||||
--nav-bar-background-color: #FFFFFF;
|
--nav-bar-background-color: #FFFFFF;
|
||||||
--nav-bar-link-active: #485b6e;
|
--nav-bar-link-active: #485b6e;
|
||||||
--nav-border-color: #c5ccdb;
|
--nav-border-color: #c5ccdb;
|
||||||
|
@ -8,11 +8,13 @@ import { IRootState } from '@/store/modules/root/types'
|
|||||||
import sportsModule from '@/store/modules/sports'
|
import sportsModule from '@/store/modules/sports'
|
||||||
import statsModule from '@/store/modules/statistics'
|
import statsModule from '@/store/modules/statistics'
|
||||||
import userModule from '@/store/modules/user'
|
import userModule from '@/store/modules/user'
|
||||||
|
import workoutsModule from '@/store/modules/workouts'
|
||||||
|
|
||||||
const modules: ModuleTree<IRootState> = {
|
const modules: ModuleTree<IRootState> = {
|
||||||
sportsModule,
|
sportsModule,
|
||||||
statsModule,
|
statsModule,
|
||||||
userModule,
|
userModule,
|
||||||
|
workoutsModule,
|
||||||
}
|
}
|
||||||
|
|
||||||
const root: Module<IRootState, IRootState> = {
|
const root: Module<IRootState, IRootState> = {
|
||||||
|
@ -25,7 +25,7 @@ export const actions: ActionTree<IWorkoutsState, IRootState> &
|
|||||||
if (res.data.status === 'success') {
|
if (res.data.status === 'success') {
|
||||||
context.commit(
|
context.commit(
|
||||||
WORKOUTS_STORE.MUTATIONS.SET_CALENDAR_WORKOUTS,
|
WORKOUTS_STORE.MUTATIONS.SET_CALENDAR_WORKOUTS,
|
||||||
res.data.data.statistics
|
res.data.data.workouts
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
handleError(context, null)
|
handleError(context, null)
|
||||||
|
@ -13,3 +13,5 @@ const workouts: Module<IWorkoutsState, IRootState> = {
|
|||||||
getters,
|
getters,
|
||||||
mutations,
|
mutations,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default workouts
|
||||||
|
@ -2,10 +2,13 @@ import {
|
|||||||
addDays,
|
addDays,
|
||||||
addMonths,
|
addMonths,
|
||||||
addYears,
|
addYears,
|
||||||
|
endOfMonth,
|
||||||
|
endOfWeek,
|
||||||
startOfMonth,
|
startOfMonth,
|
||||||
startOfWeek,
|
startOfWeek,
|
||||||
startOfYear,
|
startOfYear,
|
||||||
} from 'date-fns'
|
} from 'date-fns'
|
||||||
|
import { utcToZonedTime } from 'date-fns-tz'
|
||||||
|
|
||||||
export const startDate = (
|
export const startDate = (
|
||||||
duration: string,
|
duration: string,
|
||||||
@ -40,3 +43,20 @@ export const incrementDate = (duration: string, day: Date): Date => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const getDateWithTZ = (dateInUTC: string, tz: string): Date => {
|
||||||
|
return utcToZonedTime(new Date(dateInUTC), tz)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getCalendarStartAndEnd = (
|
||||||
|
date: Date,
|
||||||
|
weekStartingMonday: boolean
|
||||||
|
): Record<string, Date> => {
|
||||||
|
const monthStart = startOfMonth(date)
|
||||||
|
const monthEnd = endOfMonth(date)
|
||||||
|
const weekStartsOn = weekStartingMonday ? 1 : 0
|
||||||
|
return {
|
||||||
|
start: startOfWeek(monthStart, { weekStartsOn }),
|
||||||
|
end: endOfWeek(monthEnd),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div id="dashboard">
|
<div id="dashboard" v-if="authUser.username">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<UserStats :user="authUser" v-if="authUser.username" />
|
<UserStats :user="authUser" />
|
||||||
</div>
|
</div>
|
||||||
<div class="container dashboard-container">
|
<div class="container dashboard-container">
|
||||||
<div class="left-container dashboard-sub-container">
|
<div class="left-container dashboard-sub-container">
|
||||||
@ -9,7 +9,7 @@
|
|||||||
<UserRecords />
|
<UserRecords />
|
||||||
</div>
|
</div>
|
||||||
<div class="right-container dashboard-sub-container">
|
<div class="right-container dashboard-sub-container">
|
||||||
<UserCalendar />
|
<UserCalendar :user="authUser" />
|
||||||
<Timeline />
|
<Timeline />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -20,7 +20,7 @@
|
|||||||
import { computed, ComputedRef, defineComponent } from 'vue'
|
import { computed, ComputedRef, defineComponent } from 'vue'
|
||||||
|
|
||||||
import Timeline from '@/components/Dashboard/Timeline.vue'
|
import Timeline from '@/components/Dashboard/Timeline.vue'
|
||||||
import UserCalendar from '@/components/Dashboard/UserCalendar.vue'
|
import UserCalendar from '@/components/Dashboard/UserCalendar/index.vue'
|
||||||
import UserMonthStats from '@/components/Dashboard/UserMonthStats.vue'
|
import UserMonthStats from '@/components/Dashboard/UserMonthStats.vue'
|
||||||
import UserRecords from '@/components/Dashboard/UserRecords.vue'
|
import UserRecords from '@/components/Dashboard/UserRecords.vue'
|
||||||
import UserStats from '@/components/Dashboard/UserStats.vue'
|
import UserStats from '@/components/Dashboard/UserStats.vue'
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { assert, expect } from 'chai'
|
import { assert, expect } from 'chai'
|
||||||
|
|
||||||
import { incrementDate, startDate } from '@/utils/dates'
|
import { getCalendarStartAndEnd, incrementDate, startDate } from '@/utils/dates'
|
||||||
|
|
||||||
describe('startDate (week starting Sunday)', () => {
|
describe('startDate (week starting Sunday)', () => {
|
||||||
const testsParams = [
|
const testsParams = [
|
||||||
@ -135,3 +135,23 @@ describe('dateIncrement', () => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('getCalendarStartAndEnd', () => {
|
||||||
|
const testsParams = [
|
||||||
|
{
|
||||||
|
description: 'returns empty string if no date provided',
|
||||||
|
inputDate: 'September 5, 2021 20:00:00',
|
||||||
|
expectedStartDate: 'August 29, 2021 00:00:00',
|
||||||
|
expectedEndDate: 'October 2, 2021 23:59:59.999',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
testsParams.map((testParams) =>
|
||||||
|
it(testParams.description, () => {
|
||||||
|
const date: Date = new Date(testParams.inputDate)
|
||||||
|
const results = getCalendarStartAndEnd(date, false)
|
||||||
|
assert.deepEqual(results.start, new Date(testParams.expectedStartDate))
|
||||||
|
assert.deepEqual(results.end, new Date(testParams.expectedEndDate))
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
@ -3581,6 +3581,11 @@ data-urls@^1.1.0:
|
|||||||
whatwg-mimetype "^2.2.0"
|
whatwg-mimetype "^2.2.0"
|
||||||
whatwg-url "^7.0.0"
|
whatwg-url "^7.0.0"
|
||||||
|
|
||||||
|
date-fns-tz@^1.1.6:
|
||||||
|
version "1.1.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/date-fns-tz/-/date-fns-tz-1.1.6.tgz#93cbf354e2aeb2cd312ffa32e462c1943cf20a8e"
|
||||||
|
integrity sha512-nyy+URfFI3KUY7udEJozcoftju+KduaqkVfwyTIE0traBiVye09QnyWKLZK7drRr5h9B7sPJITmQnS3U6YOdQg==
|
||||||
|
|
||||||
date-fns@^2.23.0:
|
date-fns@^2.23.0:
|
||||||
version "2.23.0"
|
version "2.23.0"
|
||||||
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.23.0.tgz#4e886c941659af0cf7b30fafdd1eaa37e88788a9"
|
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.23.0.tgz#4e886c941659af0cf7b30fafdd1eaa37e88788a9"
|
||||||
|
Loading…
Reference in New Issue
Block a user