Skip to content
2 changes: 1 addition & 1 deletion gatsby-browser.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export const wrapRootElement = ({ element }) => {
}

export const shouldUpdateScroll = ({ routerProps: { location } }) => {
const regex = /timeline\/?(\d\d\d\d-\d\d-\d\d)?$/
const regex = /(timeline|calendar)\/?(\d\d\d\d-\d\d-\d\d)?$/
const results = location.pathname.match(regex)

if (results) {
Expand Down
3 changes: 3 additions & 0 deletions gatsby-node.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ exports.onCreatePage = async ({ page, actions }) => {
if (page.path === "/timeline/") {
page.matchPath = "/timeline/*"
createPage(page)
} else if (page.path === "/calendar/") {
page.matchPath = "/calendar/*"
createPage(page)
} else if (page.path === "/profile/") {
page.matchPath = "/profile/*"
createPage(page)
Expand Down
97 changes: 97 additions & 0 deletions src/features/timeline/Calendar/Day.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import { useSelector } from "react-redux"
import { Box, makeStyles, Typography } from "@material-ui/core"
import {
addDays,
getDay,
isFuture as dateIsFuture,
isToday as dateIsToday,
isPast as dateIsPast,
} from "date-fns"
import {
selectHasPredictionsForDate,
selectPredictedMenstruationForDate,
} from "../../cycle"
import React from "react"
import { entryIdFromDate } from "../../utils/days"
import Header from "./Header"
import Entry from "./Entry"

const useStyles = makeStyles((theme) => ({
list: { listStyle: "none" },
day: {
minHeight: 100,
background: theme.palette.grey[200],
border: (props) =>
props.isPeriod
? `2px solid ${theme.palette.error.light}`
: `2px solid ${theme.palette.grey[200]}`,
},
}))

const Day = ({ date, ...props }) => {
const isPredictedMenstruation = useSelector((state) =>
selectPredictedMenstruationForDate(state, { date })
)
const classes = useStyles({ isPeriod: isPredictedMenstruation })
const hasPredictions = useSelector((state) =>
selectHasPredictionsForDate(state, { date })
)

const isPast = dateIsPast(date)
const isFuture = dateIsFuture(date)
const isToday = dateIsToday(date)
const itemProps = { date, isFuture, isToday, isPast, isSelected: false }
const scrollToId = `scrollTo-${entryIdFromDate(addDays(date, 1))}`
const weekDay = props.isFirstOfMonth ? getDay(date) : null

const columnsForFirstDay = {
0: 7,
1: 0,
2: 2,
3: 3,
4: 4,
5: 5,
6: 6,
}

return (
<>
<Box
component="li"
py={1}
ml={0}
className={classes.list}
{...props}
style={{
gridColumnStart: weekDay ? columnsForFirstDay[weekDay] : null,
}}
>
<div id={scrollToId} />
<Box
display="flex"
alignItems="center"
justifyContent={
isFuture && !hasPredictions ? "center" : "space-between"
}
className={classes.day}
flexDirection="column"
height={"100%"}
borderRadius={4}
>
{isFuture && !hasPredictions ? (
<Typography variant="body2" color="textSecondary" display="block">
{date.getDate()}
</Typography>
) : (
<>
<Header {...itemProps} />
<Entry {...itemProps} />
</>
)}
</Box>
</Box>
</>
)
}

export default Day
42 changes: 42 additions & 0 deletions src/features/timeline/Calendar/Entry.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import React from "react"
import PropTypes from "prop-types"
import { Link } from "gatsby"

import { useSelector } from "react-redux"
import { ButtonBase, Tooltip } from "@material-ui/core"
import { Brightness1 as BrightnessIcon } from "@material-ui/icons"
import { useTheme } from "@material-ui/core/styles"
import { entryIdFromDate } from "../../utils/days"
import { selectEntryNote } from "../../entries"

const Entry = ({ date, isPast, isToday, className }) => {
const theme = useTheme()
const entryId = entryIdFromDate(date)
const editPath = `/calendar/${entryId}/edit`
const entryNote = useSelector((state) => selectEntryNote(state, { date }))
const isEditable = isPast || isToday

if (!isEditable) return null

return (
<ButtonBase component={Link} to={editPath} className={className}>
{entryNote
? entryNote.split(" ").map((note) => (
<Tooltip title={note} aria-label={note}>
<BrightnessIcon
style={{ fill: theme.palette.info.main, width: 8 }}
/>
</Tooltip>
))
: null}
</ButtonBase>
)
}

Entry.propTypes = {
date: PropTypes.instanceOf(Date),
isPast: PropTypes.bool.isRequired,
isToday: PropTypes.bool.isRequired,
}

export default Entry
53 changes: 53 additions & 0 deletions src/features/timeline/Calendar/Header.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import React from "react"
import PropTypes from "prop-types"
import { useSelector } from "react-redux"
import { Typography } from "@material-ui/core"

import {
selectCycleDayForDate,
selectPredictedMenstruationForDate,
} from "../../cycle"

const TimelineHeader = ({ date, isSelected, isToday, isFuture, className }) => {
const cycleDay = useSelector((state) =>
selectCycleDayForDate(state, { date })
)

const isPredictedMenstruation = useSelector((state) =>
selectPredictedMenstruationForDate(state, { date })
)

const isMenstruation = isPredictedMenstruation

const textColor = isToday || isFuture ? "textPrimary" : "textSecondary"

return (
<header className={className}>
<Typography
variant="overline"
component={isSelected ? "strong" : "span"}
color={textColor}
display="block"
>
{isToday ? "Today" : date.getDate()}
</Typography>
<Typography
display="block"
variant="overline"
component={isMenstruation || isSelected ? "strong" : "span"}
color={isMenstruation ? "primary" : textColor}
>
Day {cycleDay}
</Typography>
</header>
)
}

TimelineHeader.propTypes = {
date: PropTypes.instanceOf(Date),
isSelected: PropTypes.bool.isRequired,
isToday: PropTypes.bool.isRequired,
isFuture: PropTypes.bool.isRequired,
}

export default TimelineHeader
67 changes: 67 additions & 0 deletions src/features/timeline/Calendar/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { getYear } from "date-fns"
import React from "react"
import { Box, Typography, makeStyles } from "@material-ui/core"
import Day from "./Day"

const useStyles = makeStyles((theme) => ({
calendar: {
display: "grid",
gridTemplateColumns: `repeat(7, calc(14.2% - ${theme.spacing(2)}px))`,
gridGap: theme.spacing(2),
padding: theme.spacing(1),

[theme.breakpoints.down("sm")]: {
gridTemplateColumns: `repeat(7, calc(14.2% - ${theme.spacing(1)}px))`,
gridGap: theme.spacing(1),
},
},
weekDays: {
display: "grid",
gridTemplateColumns: `repeat(7, calc(14.2% - ${theme.spacing(2)}px))`,
listStyle: "none",
gridGap: theme.spacing(2),
padding: theme.spacing(1),
"& li": {
textAlign: "center",
},
},
}))

const Calendar = ({ dates }) => {
const classes = useStyles()
return (
<Box>
<Box
component="header"
display="flex"
flexDirection="column"
alignItems="center"
justifyContent="center"
my={4}
>
<Typography pt={2} variant="h6" component="h1">
{dates[0].toLocaleString("default", { month: "long" })}{" "}
{getYear(dates[0])}{" "}
</Typography>
</Box>
<>
<Box component="ul" className={classes.weekDays}>
<li>Mon</li>
<li>Tues</li>
<li>Wed</li>
<li>Thurs</li>
<li>Fri</li>
<li>Sat</li>
<li>Sun</li>
</Box>
</>
<Box component="ol" className={classes.calendar}>
{dates.map((date, i) => (
<Day date={date} isFirstOfMonth={i === 0} />
))}
</Box>
</Box>
)
}

export default Calendar
8 changes: 4 additions & 4 deletions src/features/timeline/TimelineEditPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ const useStyles = makeStyles((theme) => ({
},
}))

const CycleEditPage = ({ entryId }) => {
const CycleEditPage = ({ entryId, calendar }) => {
const classes = useStyles()
const date = makeDate(entryId)

const redirectTo = calendar ? `/calendar/${entryId}` : `/${entryId}`
const dispatch = useDispatch()
const entryNote = useSelector((state) => selectEntryNote(state, { entryId }))
const [note, setNote] = useState(entryNote)
Expand All @@ -45,13 +45,13 @@ const CycleEditPage = ({ entryId }) => {
const handleReset = (event) => {
event.preventDefault()
setNote(entryNote)
navigate(`/timeline/${entryId}`)
navigate(redirectTo)
}

const handleSubmit = (event) => {
event.preventDefault()
dispatch(upsertEntry({ date, note }))
navigate(`/timeline/${entryId}`)
navigate(redirectTo)
}

return (
Expand Down
Loading