diff --git a/components/EventList/index.jsx b/components/EventList/index.jsx
new file mode 100644
index 0000000..084f7aa
--- /dev/null
+++ b/components/EventList/index.jsx
@@ -0,0 +1,232 @@
+import { imageUrlFor } from "@/store/sanity";
+import theme from "@/utils/theme";
+import dayjs from "dayjs";
+import Link from "next/link";
+import React from "react";
+import styled from "styled-components";
+
+const EventList = props => {
+ const { events, venues } = props;
+
+ const displayArena = event => {
+ switch (event.category) {
+ case "0":
+ return "Ekstern arena";
+ break;
+ case "1":
+ return "Pride Parade";
+ break;
+ case "2":
+ return "Pride Park";
+ break;
+ case "3":
+ return "Pride House";
+ break;
+ case "4":
+ return "Pride Art";
+ break;
+ }
+ };
+
+ const displayEventType = event => {
+ switch (event.eventType) {
+ case "0":
+ return "Annet";
+ break;
+ case "1":
+ return "Konsert";
+ break;
+ case "2":
+ return "Debatt";
+ break;
+ case "3":
+ return "Utstilling";
+ break;
+ case "4":
+ return "Fest";
+ break;
+ }
+ };
+
+ const getVenueName = reference => {
+ const venueData = venues.data.find(venue => venue._id === reference);
+ return venueData.name;
+ };
+
+ return (
+ <>
+ {groupEventsByDay(events).map(day => {
+ const currentDay = dayjs(day[0].startingTime);
+ return (
+
+
+
+ {currentDay.format("dddd")}{" "}
+ {currentDay.format("D. MMMM")}
+
+
+
+
+ );
+ })}
+ >
+ );
+};
+
+const groupEventsByDay = events => {
+ if (events.length === 0) {
+ return [];
+ }
+
+ const sortedEvents = [...events];
+
+ sortedEvents.sort(
+ (a, b) => dayjs(a.startingTime).unix() - dayjs(b.startingTime).unix()
+ );
+
+ const groupedEvents = [[sortedEvents[0]]];
+
+ sortedEvents.slice(1).forEach(event => {
+ const lastGroup = groupedEvents[groupedEvents.length - 1];
+ const lastEvent = lastGroup[lastGroup.length - 1];
+
+ const lastEventStart = dayjs(lastEvent.startingTime);
+ const currentEventStart = dayjs(event.startingTime);
+
+ if (lastEventStart.format("dddd") === currentEventStart.format("dddd")) {
+ lastGroup.push(event);
+ } else {
+ groupedEvents.push([event]);
+ }
+ });
+ return groupedEvents;
+};
+
+export default EventList;
+
+const Event = styled.div`
+ width: 100%;
+ max-width: 1000px;
+`;
+
+const EventDay = styled.div`
+ background-color: ${theme.purple};
+ width: 100%;
+
+ h2 {
+ font-size: 25px;
+ font-weight: 500;
+ color: white;
+ text-transform: uppercase;
+ text-align: center;
+ }
+`;
+
+const EventLink = styled.div`
+ cursor: pointer;
+ border-bottom: 2px solid lightgrey;
+ padding: 10px 0;
+
+ &:last-child {
+ border-bottom: 0;
+ }
+
+ & > a {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ text-decoration: none;
+
+ :hover {
+ text-decoration: underline;
+ }
+ }
+`;
+
+const EventImage = styled.img`
+ width: 80px;
+ height: 80px;
+ object-fit: cover;
+`;
+
+const EventInfo = styled.div`
+ display: flex;
+ flex-flow: row wrap;
+ justify-content: flex-start;
+ margin-left: 20px;
+ width: 100%;
+`;
+
+const EventTitle = styled.div`
+ width: 100%;
+ font-size: 20px;
+ font-weight: 500;
+`;
+
+const EventTime = styled.div`
+ font-size: 18px;
+ font-weight: 600;
+ color: ${theme.orange};
+ margin-right: 10px;
+`;
+
+const EventPlace = styled.div`
+ font-size: 18px;
+ font-weight: 300;
+ margin-right: 10px;
+`;
+
+const EventType = styled.div`
+ font-size: 18px;
+ font-weight: 300;
+`;
+
+const Descriptor = styled.span`
+ font-size: 18px;
+ font-weight: 500;
+`;
diff --git a/components/Filter/Selector.jsx b/components/Filter/Selector.jsx
new file mode 100644
index 0000000..eb813c1
--- /dev/null
+++ b/components/Filter/Selector.jsx
@@ -0,0 +1,62 @@
+import React, { useCallback, useState } from "react";
+import styled from "styled-components";
+
+const Selector = ({ selectors, defaultSelector, className }) => {
+ const [checked, setChecked] = useState(defaultSelector);
+ const onChange = useCallback(
+ (value, callback) => () => {
+ setChecked(value);
+ callback(value);
+ },
+ [setChecked]
+ );
+
+ const inspectedChecked = selectors.some(({ value }) => value === checked)
+ ? checked
+ : defaultSelector;
+
+ const selectorElements = selectors.map(({ name, value, callback }) => (
+
+ ));
+
+ return (
+
+ Arena
+ {selectorElements}
+
+ );
+};
+
+export default Selector;
+
+const Container = styled.div`
+ width: 100%;
+`;
+
+const SelectorTitle = styled.div`
+ font-weight: bold;
+ text-align: center;
+ margin-bottom: 5px;
+`;
+
+const SelectorList = styled.div`
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: center;
+
+ label {
+ margin: 2px 8px;
+ }
+`;
+
+const SelectorLabel = styled.span`
+ margin-left: 5px;
+`;
diff --git a/components/Filter/index.jsx b/components/Filter/index.jsx
new file mode 100644
index 0000000..d69f372
--- /dev/null
+++ b/components/Filter/index.jsx
@@ -0,0 +1,17 @@
+import theme from "@/utils/theme";
+import React from "react";
+import styled from "styled-components";
+import Selector from "./Selector";
+
+const Filter = ({ selector }) => (
+ {selector && }
+);
+
+export default Filter;
+
+const Wrapper = styled.div`
+ display: flex;
+ flex-direction: column;
+ padding: 10px;
+ border: 3px solid ${theme.purple};
+`;
diff --git a/components/Filter/useURLFilter.js b/components/Filter/useURLFilter.js
new file mode 100644
index 0000000..3f4e022
--- /dev/null
+++ b/components/Filter/useURLFilter.js
@@ -0,0 +1,63 @@
+import Router from "next/router";
+import { useMemo } from "react";
+
+const toArray = val =>
+ val === undefined ? [] : Array.isArray(val) ? val : [val];
+
+export default (objects, query) =>
+ useMemo(() => {
+ let filteredObjects = objects;
+ Object.keys(query).forEach(key => {
+ const filters = toArray(query[key]);
+ filteredObjects =
+ filters.length === 0
+ ? objects // Return all objects if no filtes are applied
+ : objects.filter(obj => filters.includes(obj[key]));
+ });
+ return filteredObjects;
+ }, [objects, query]);
+
+export const addFilter = (key, value) => {
+ const prevFilterList = toArray(Router.query[key]);
+ const newFilterList = prevFilterList.includes(value)
+ ? prevFilterList
+ : [...prevFilterList, value];
+ Router.replace({
+ pathname: Router.route,
+ query: {
+ ...Router.query,
+ [key]: newFilterList.length === 1 ? newFilterList[0] : newFilterList
+ }
+ });
+};
+
+export const setFilter = (key, value) => {
+ Router.replace({
+ pathname: Router.route,
+ query: { ...Router.query, [key]: value }
+ });
+};
+
+export const removeFilter = (key, value) => {
+ const prevFilterList = toArray(Router.query[key]);
+ const newFilterList = prevFilterList.filter(v => v !== value);
+ Router.replace({
+ pathname: Router.route,
+ query: {
+ ...Router.query,
+ [key]: newFilterList.length === 1 ? newFilterList[0] : newFilterList
+ }
+ });
+};
+
+export const resetFilter = key => {
+ Router.replace({
+ pathname: Router.route,
+ query: Object.keys(Router.query).reduce((query, k) => {
+ if (k !== key) {
+ query[k] = Router.query[k];
+ }
+ return query;
+ }, {})
+ });
+};
diff --git a/pages/events/index.jsx b/pages/events/index.jsx
index 2f9cdd1..924e98b 100644
--- a/pages/events/index.jsx
+++ b/pages/events/index.jsx
@@ -1,165 +1,92 @@
+import EventList from "@/components/EventList";
+import Filter from "@/components/Filter";
+import useURLFilter, {
+ resetFilter,
+ setFilter
+} from "@/components/Filter/useURLFilter";
import Sheet from "@/components/Sheet";
import { eventsActions, getEvents } from "@/store/events";
import { webResponseInitial } from "@/store/helpers";
-import { imageUrlFor } from "@/store/sanity";
import { getVenues, venuesActions } from "@/store/venues";
import theme from "@/utils/theme";
-import dayjs from "dayjs";
import NextSeo from "next-seo";
-import Link from "next/link";
-import React from "react";
+import React, { useState } from "react";
import { connect } from "react-redux";
import styled from "styled-components";
-const groupEventsByDay = events => {
- if (events.length === 0) {
- return [];
- }
-
- const sortedEvents = [...events];
-
- sortedEvents.sort(
- (a, b) => dayjs(a.startingTime).unix() - dayjs(b.startingTime).unix()
- );
-
- const groupedEvents = [[sortedEvents[0]]];
-
- sortedEvents.slice(1).forEach(event => {
- const lastGroup = groupedEvents[groupedEvents.length - 1];
- const lastEvent = lastGroup[lastGroup.length - 1];
-
- const lastEventStart = dayjs(lastEvent.startingTime);
- const currentEventStart = dayjs(event.startingTime);
-
- if (lastEventStart.format("dddd") === currentEventStart.format("dddd")) {
- lastGroup.push(event);
- } else {
- groupedEvents.push([event]);
- }
- });
-
- return groupedEvents;
-};
-
-const displayArena = event => {
- switch (event.category) {
+const arenaNameMapper = arena => {
+ switch (arena) {
case "0":
return "Ekstern arena";
- break;
case "1":
return "Pride Parade";
- break;
case "2":
return "Pride Park";
- break;
case "3":
return "Pride House";
- break;
case "4":
return "Pride Art";
- break;
- }
-};
-
-const displayEventType = event => {
- switch (event.eventType) {
- case "0":
- return "Annet";
- break;
- case "1":
- return "Konsert";
- break;
- case "2":
- return "Debatt";
- break;
- case "3":
- return "Utstilling";
- break;
- case "4":
- return "Fest";
- break;
+ default:
+ return "Ukjent";
}
};
const Events = props => {
- const { events, venues } = props;
+ const { events, venues, query } = props;
+ const [visible, setVisible] = useState(false);
+ const filteredEvents = useURLFilter(events.data || [], query);
+
+ const toggleFilter = () => setVisible(!visible);
+ const defaultSelector = query.category || "-1";
if (events.status !== "SUCCESS" || venues.status !== "SUCCESS") {
// TODO: Make a better UX while loading
return
Laster ...
;
}
- const getVenueName = reference => {
- const venueData = venues.data.find(venue => venue._id === reference);
- return venueData.name;
- };
-
return (
<>
Program 2019
- {!events.data.length ? Kommer snart!
: null}
-
- {groupEventsByDay(events.data).map(day => {
- const currentDay = dayjs(day[0].startingTime);
- return (
-
-
-
- {currentDay.format("dddd")}{" "}
- {currentDay.format("D. MMMM")}
-
-
-
-
- );
- })}
+ resetFilter("category")
+ },
+ {
+ name: "Pride Parade",
+ value: "1",
+ callback: value => setFilter("category", "1")
+ },
+ {
+ name: "Pride Park",
+ value: "2",
+ callback: value => setFilter("category", "2")
+ },
+ {
+ name: "Pride House",
+ value: "3",
+ callback: value => setFilter("category", "3")
+ },
+ {
+ name: "Pride Art",
+ value: "4",
+ callback: value => setFilter("category", "4")
+ }
+ ]
+ }}
+ />
+
+ {events.data.length ? (
+
+ ) : (
+ Kommer snart!
+ )}
{
);
};
-Events.getInitialProps = async ({ store, isServer }) => {
+Events.getInitialProps = async ({ store, isServer, query }) => {
if (store.getState().events.status === webResponseInitial().status) {
store.dispatch(eventsActions.request());
if (isServer) {
@@ -209,6 +136,8 @@ Events.getInitialProps = async ({ store, isServer }) => {
}
}
}
+
+ return { query };
};
const mapStateToProps = state => ({
@@ -223,85 +152,3 @@ const PageTitle = styled.h1`
text-transform: uppercase;
text-align: center;
`;
-
-const Event = styled.div`
- width: 100%;
- max-width: 1000px;
-`;
-
-const EventDay = styled.div`
- background-color: ${theme.purple};
- width: 100%;
-
- h2 {
- font-size: 25px;
- font-weight: 500;
- color: white;
- text-transform: uppercase;
- text-align: center;
- }
-`;
-
-const EventLink = styled.div`
- cursor: pointer;
- border-bottom: 2px solid lightgrey;
- padding: 10px 0;
-
- &:last-child {
- border-bottom: 0;
- }
-
- & > a {
- display: flex;
- flex-direction: row;
- align-items: center;
- text-decoration: none;
-
- :hover {
- text-decoration: underline;
- }
- }
-`;
-
-const EventImage = styled.img`
- width: 80px;
- height: 80px;
- object-fit: cover;
-`;
-
-const EventInfo = styled.div`
- display: flex;
- flex-flow: row wrap;
- justify-content: flex-start;
- margin-left: 20px;
- width: 100%;
-`;
-
-const EventTitle = styled.div`
- width: 100%;
- font-size: 20px;
- font-weight: 500;
-`;
-
-const EventTime = styled.div`
- font-size: 18px;
- font-weight: 600;
- color: ${theme.orange};
- margin-right: 10px;
-`;
-
-const EventPlace = styled.div`
- font-size: 18px;
- font-weight: 300;
- margin-right: 10px;
-`;
-
-const EventType = styled.div`
- font-size: 18px;
- font-weight: 300;
-`;
-
-const Descriptor = styled.span`
- font-size: 18px;
- font-weight: 500;
-`;
diff --git a/utils/theme.js b/utils/theme.js
index 993b1ba..5fd28f1 100644
--- a/utils/theme.js
+++ b/utils/theme.js
@@ -3,13 +3,16 @@ import { rgb } from "polished";
export default {
red: rgb(164, 29, 47),
orange: rgb(239, 82, 27),
+ lightOrange: rgb(250, 198, 173),
yellow: rgb(255, 193, 1),
green: rgb(65, 140, 98),
+ lightGreen: rgb(199, 223, 213),
blue: rgb(51, 80, 185),
lightBlue: rgb(167, 195, 245),
purple: rgb(53, 32, 118),
lightPurple: rgb(191, 180, 211),
pink: rgb(227, 80, 161),
+ lightPink: rgb(242, 199, 225),
gray: rgb(204, 204, 204),
darkgray: rgb(74, 74, 74),
background: rgb(241, 244, 249)