Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"private": true,
"dependencies": {
"@cheapreats/react-ui": "^2.2.10",
"moment": "^2.24.0",
"react": "^16.9.0",
"react-dom": "^16.9.0",
"react-router-dom": "^5.0.1",
Expand Down
1 change: 1 addition & 0 deletions public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<div id="modal"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
Expand Down
13 changes: 0 additions & 13 deletions src/pages/Dashboard/Schedule.jsx

This file was deleted.

144 changes: 144 additions & 0 deletions src/pages/Dashboard/Schedule/Column.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import React, { useMemo } from 'react';
import moment from 'moment';
import styled from 'styled-components';
import { SmallText } from '@cheapreats/react-ui';
import { Event } from './Event';

export const Column = ({
date,
events,
showModal,
headerHeight,
rowHeight,
}) => {
const boxes = useMemo(
() => [...Array(24)].map((v, i) => <Box key={i} height={rowHeight} />),
[rowHeight],
);

const eventComponents = useMemo(
() =>
calcEventSize(events).map((event, i) => (
<Event
{...event}
key={i}
rowHeight={rowHeight}
onClick={() => showModal(event)}
/>
)),
[events, showModal, rowHeight],
);

return (
<Layout>
<Header height={headerHeight}>
<HeaderText bold>
{moment.utc(date, 'DD/MM/YY').format('LL')}
</HeaderText>
</Header>
<EventLayout>
{eventComponents}
{boxes}
</EventLayout>
</Layout>
);
};

/**
* This function adds a startTime, endTime, column, and overlaps property to each event.
* Overlaps is the largest number of events that it overlaps with at a point in time
* Column is the column the event will be in when it is displayed on the schedule.
*/
function calcEventSize(events) {
events = events
.map(event => {
return {
...event,
startTime: moment.utc(event.time[0], 'H:mm'),
endTime: moment.utc(
event.time[1] === '24:00' ? '23:59' : event.time[1],
'H:mm',
),
// moment.js treats '24:00' as '0:00' so instead we temporarily change it to '23:59'
};
})
.sort((a, b) => (a.startTime < b.startTime ? -1 : 1));

/**
* We create groups on all events that overlap.
* endTimes holds a column's latest event end time
* overlapStartIndex is the index of the first event in the group
* Once we iterate on an event that does not overlap with the group, we can set
* the overlaps property on all events in the group so that we know the number
* of columns in the group. This helps with figuring out the width of the event block
*/
let endTimes = [];
let overlapStartIndex = 0;
for (let i = 0; i < events.length; i++) {
events[i].column = 0;
events[i].overlaps = 1;
const s =
events[i].startTime.hour() + events[i].startTime.minute() / 60;
const e = events[i].endTime.hour() + events[i].endTime.minute() / 60;

// if there is an overlap, then place the overlap in a group
if (s < Math.max(...endTimes)) {
let grouped = false;
for (let j = 0; j < endTimes.length; j++) {
if (!grouped && endTimes[j] <= s) {
grouped = true;
endTimes[j] = e;
events[i].column = j;
break;
}
}
if (!grouped) {
endTimes.push(e);
events[i].column = endTimes.length - 1;
}
} else {
for (let j = overlapStartIndex; j < i; j++) {
events[j].overlaps = endTimes.length;
}
// reset groupings
endTimes = [e];
overlapStartIndex = i;
}
}
for (let j = overlapStartIndex; j < events.length; j++) {
events[j].overlaps = endTimes.length;
}
return events;
}

const Layout = styled.div`
flex-grow: 2;
min-width: 200px;
border-left: 2px solid lightgrey;
&:first-child {
border-left: none;
}
`;

const EventLayout = styled.div`
position: relative;
`;

const Header = styled.div`
border-bottom: 2px solid lightgrey;
display: flex;
align-items: center;
${({ height }) => `height: ${height}px;`}
`;

const HeaderText = styled(SmallText)`
padding-left: 10px;
`;

const Box = styled.div`
border-bottom: 2px solid lightgrey;
&:last-child {
border-bottom: none;
}
${({ height }) => `height: ${height - 2}px;`}
`;
68 changes: 68 additions & 0 deletions src/pages/Dashboard/Schedule/Event.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import React from 'react';
import styled from 'styled-components';
import { Card, Paragraph } from '@cheapreats/react-ui';

export const Event = ({
title,
startTime,
endTime,
background,
primary,
overlaps,
column,
rowHeight,
onClick,
}) => {
const top = (startTime.hour() + startTime.minute() / 60) * rowHeight;
const height =
(endTime.hour() -
startTime.hour() +
(endTime.minute() - startTime.minute()) / 60) *
rowHeight;
const s = startTime.format('h:mma');
const e = endTime.format('h:mma');
return (
<Container
onClick={onClick}
key={title}
overlaps={overlaps}
column={column}
top={top}
height={height}
background={background}
>
<TextContainer>
<StyledParagraph align="left" color={primary}>
{title}
</StyledParagraph>
<StyledParagraph align="right" color={primary}>
{s} - {e === '11:59pm' ? '12:00pm' : e}
</StyledParagraph>
</TextContainer>
</Container>
);
};

const Container = styled(Card)`
cursor: pointer;
position: absolute;
padding: 5px;
margin-left: auto;
margin-right: auto;
${({ top, height, background, overlaps, column }) => `
top: ${top}px;
height: ${height - 12}px;
background-color: ${background};
width: calc(${100 / overlaps}% - 12px);
left: calc(${(100 / overlaps) * column}% + 1px);
`}
`;

const TextContainer = styled.div`
padding: 0 5px;
`;

const StyledParagraph = styled(Paragraph)`
font-size: 0.75rem;
${({ color }) => `color: ${color};`}
`;
62 changes: 62 additions & 0 deletions src/pages/Dashboard/Schedule/EventModal.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import React from 'react';
import styled from 'styled-components';
import { Heading, Modal, Paragraph, SmallText } from '@cheapreats/react-ui';
import { LabelLayout } from '@cheapreats/react-ui/dist/__Layouts';

export const EventModal = ({ state, event }) => {
return (
event && (
<StyledModal
state={state}
background={event.background}
primary={event.primary}
secondary={event.secondary}
>
<HeadingContainer>
<Heading bold type="h5" color={event.primary}>
{event.title}
</Heading>
<SmallText color={event.primary}>
{event.startTime.format('h:mma')} -{' '}
{event.endTime.format('h:mma')}
</SmallText>
</HeadingContainer>
<LabelLayout label="Location">
<StyledText color={event.primary}>
{event.location}
</StyledText>
</LabelLayout>
<LabelLayout label="Organizer">
<StyledText color={event.primary}>
{event.organizer}
</StyledText>
</LabelLayout>
<LabelLayout label="Description">
<StyledText color={event.primary}>
{event.description}
</StyledText>
</LabelLayout>
</StyledModal>
)
);
};

const StyledModal = styled(Modal)`
padding: 10px;
${({ background, primary, secondary }) => `
background-color: ${background};
color: ${primary};
border: 2px solid ${primary}
`}
`;

const HeadingContainer = styled.div`
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
`;

const StyledText = styled(Paragraph)`
opacity: 0.9;
`;
34 changes: 34 additions & 0 deletions src/pages/Dashboard/Schedule/TimeColumn.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import React, { useMemo } from 'react';
import moment from 'moment';
import styled from 'styled-components';
import { SmallText } from '@cheapreats/react-ui';

export const TimeColumn = ({ headerHeight, rowHeight }) => {
const boxes = useMemo(
() =>
[...Array(24)].map((v, i) => (
<Box key={i} height={rowHeight}>
<SmallText bold>
{moment.utc(i, 'H').format('h:mma')}
</SmallText>
</Box>
)),
[rowHeight],
);

return (
<Layout>
<Box height={headerHeight - 10} />
{boxes}
</Layout>
);
};

const Layout = styled.div`
border-right: 2px solid lightgrey;
padding: 0 5px;
`;

const Box = styled.div`
${({ height }) => `height: ${height}px;`}
`;
20 changes: 20 additions & 0 deletions src/pages/Dashboard/Schedule/Timeline.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import React from 'react';
import moment from 'moment';
import styled from 'styled-components';

export const Timeline = ({ headerHeight, rowHeight }) => {
const now = moment();
const top = headerHeight + (now.hour() + now.minute() / 60) * rowHeight;
return <StyledHr top={top} />;
};

const StyledHr = styled.hr`
border-width: 1px 0px;
border-style: solid;
border-color: #00e2ef;
background-color: #00e2ef;
height: 2px;
width: 100%;
position: absolute;
${({ top }) => `top: ${top - 6}px;`}
`;
17 changes: 17 additions & 0 deletions src/pages/Dashboard/Schedule/events/colours.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
export const HTV_OFFICIAL = {
background: '#7bbe48',
primary: 'white',
secondary: 'white',
};

export const WORKSHOPS = {
background: '#c29356',
primary: 'white',
secondary: 'white',
};

export const SIDE = {
background: '#236fc2',
primary: 'white',
secondary: 'white',
};
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I copied the files in Schedule/events from last year HTV to use as test data, https://github.com/fpunny/htv-live/tree/master/src/schedule.
When adding the actual data for the schedule, we will need to update the colours and friday/saturday/sunday files

Loading