205 lines
5.5 KiB
JavaScript

// eslint-disable-next-line max-len
// source: https://blog.flowandform.agency/create-a-custom-calendar-in-react-3df1bfd0b728
import {
addDays,
addMonths,
endOfMonth,
endOfWeek,
format,
isSameDay,
isSameMonth,
startOfMonth,
startOfWeek,
subMonths,
} from 'date-fns'
import React, { Fragment } from 'react'
import { connect } from 'react-redux'
import { Link } from 'react-router-dom'
import { getMonthActivities } from '../../actions/activities'
import { getDateWithTZ } from '../../utils'
import { recordsLabels } from '../../utils/activities'
const getStartAndEndMonth = (date, weekStartOnMonday) => {
const monthStart = startOfMonth(date)
const monthEnd = endOfMonth(date)
const weekStartsOn = weekStartOnMonday ? 1 : 0
return {
start: startOfWeek(monthStart, { weekStartsOn }),
end: endOfWeek(monthEnd),
}
}
class Calendar extends React.Component {
constructor(props, context) {
super(props, context)
const calendarDate = new Date()
this.state = {
currentMonth: calendarDate,
startDate: getStartAndEndMonth(calendarDate, props.weekm).start,
endDate: getStartAndEndMonth(calendarDate, props.weekm).end,
weekStartOnMonday: props.weekm,
}
}
componentDidMount() {
this.props.loadMonthActivities(this.state.startDate, this.state.endDate)
}
renderHeader() {
const dateFormat = 'MMM yyyy'
return (
<div className="header row flex-middle">
<div className="col col-start" onClick={() => this.handlePrevMonth()}>
<i className="fa fa-chevron-left" aria-hidden="true" />
</div>
<div className="col col-center">
<span>{format(this.state.currentMonth, dateFormat)}</span>
</div>
<div className="col col-end" onClick={() => this.handleNextMonth()}>
<i className="fa fa-chevron-right" aria-hidden="true" />
</div>
</div>
)
}
renderDays() {
const dateFormat = 'EEE'
const days = []
const { startDate } = this.state
for (let i = 0; i < 7; i++) {
days.push(
<div className="col col-center" key={i}>
{format(addDays(startDate, i), dateFormat)}
</div>
)
}
return <div className="days row">{days}</div>
}
filterActivities(day) {
const { activities, user } = this.props
if (activities) {
return activities.filter(act =>
isSameDay(getDateWithTZ(act.activity_date, user.timezone), day)
)
}
return []
}
renderCells() {
const { currentMonth, startDate, endDate } = this.state
const { sports } = this.props
const dateFormat = 'd'
const rows = []
let days = []
let day = startDate
let formattedDate = ''
while (day <= endDate) {
for (let i = 0; i < 7; i++) {
formattedDate = format(day, dateFormat)
const dayActivities = this.filterActivities(day)
const isDisabled = isSameMonth(day, currentMonth) ? '' : 'disabled'
days.push(
<div className={`col cell img-${isDisabled}`} key={day}>
<span className="number">{formattedDate}</span>
{dayActivities.map(act => (
<Link key={act.id} to={`/activities/${act.id}`}>
<Fragment>
<img
alt="activity sport logo"
className={`activity-sport ${isDisabled}`}
src={sports
.filter(s => s.id === act.sport_id)
.map(s => s.img)}
title={act.title}
/>
{act.records.length > 0 && (
<sup>
<i
className="fa fa-trophy custom-fa-small"
aria-hidden="true"
title={act.records.map(
rec =>
` ${
recordsLabels.filter(
r => r.record_type === rec.record_type
)[0].label
}`
)}
/>
</sup>
)}
</Fragment>
</Link>
))}
</div>
)
day = addDays(day, 1)
}
rows.push(
<div className="row" key={day}>
{days}
</div>
)
days = []
}
return <div className="body">{rows}</div>
}
updateStateDate(calendarDate) {
const { start, end } = getStartAndEndMonth(
calendarDate,
this.state.weekStartOnMonday
)
this.setState({
currentMonth: calendarDate,
startDate: start,
endDate: end,
})
this.props.loadMonthActivities(start, end)
}
handleNextMonth() {
const calendarDate = addMonths(this.state.currentMonth, 1)
this.updateStateDate(calendarDate)
}
handlePrevMonth() {
const calendarDate = subMonths(this.state.currentMonth, 1)
this.updateStateDate(calendarDate)
}
render() {
return (
<div className="card activity-card">
<div className="calendar">
{this.renderHeader()}
{this.renderDays()}
{this.renderCells()}
</div>
</div>
)
}
}
export default connect(
state => ({
activities: state.calendarActivities.data,
sports: state.sports.data,
user: state.user,
}),
dispatch => ({
loadMonthActivities: (start, end) => {
const dateFormat = 'yyyy-MM-dd'
dispatch(
getMonthActivities(format(start, dateFormat), format(end, dateFormat))
)
},
})
)(Calendar)