diff --git a/src/components/todoStuff/todoBoard.js b/src/components/todoStuff/todoBoard.js
new file mode 100644
index 0000000..36d0abd
--- /dev/null
+++ b/src/components/todoStuff/todoBoard.js
@@ -0,0 +1,49 @@
+import React from 'react'
+
+import TodoCard from './todoCard.js'
+
+import styles from './todoBoard.module.css'
+
+export default function TodoBoard(props) {
+ const { columns, cards, userId, categories, onCardClick } = props
+
+ const clickableTypes = Object.keys(columns).reduce((acc, key) => {
+ const col = columns[key]
+ if (col.clickable) {
+ acc.push(key)
+ }
+ return acc
+ }, [])
+
+ return (
+
+
+ {Object.keys(columns).map((key) => {
+ const category = columns[key]
+ return (
+
+
{category.name}
+ {cards.map((card, i) => {
+ if (card.state === key) {
+ // TODO: determine if clickable here
+ return (
+
+ )
+ } else {
+ return null
+ }
+ })}
+
+ )
+ })}
+
+
+ )
+}
diff --git a/src/components/todoStuff/todoBoard.module.css b/src/components/todoStuff/todoBoard.module.css
new file mode 100644
index 0000000..7e603fa
--- /dev/null
+++ b/src/components/todoStuff/todoBoard.module.css
@@ -0,0 +1,23 @@
+.tableContainer {
+ overflow-y: hidden;
+ overflow-x: auto;
+ margin: 5px 0px;
+}
+
+.table {
+ display: flex;
+ min-width: 800px;
+}
+
+.categoryName {
+ text-align: center;
+ margin: 5px 0px;
+ font-size: 16px;
+ font-weight: bold;
+ color: white;
+ white-space: nowrap;
+}
+
+.tableCol {
+ flex: 1;
+}
diff --git a/src/components/todoStuff/todoRow.js b/src/components/todoStuff/todoRow.js
new file mode 100644
index 0000000..6714428
--- /dev/null
+++ b/src/components/todoStuff/todoRow.js
@@ -0,0 +1,34 @@
+import React from 'react'
+
+import styles from './todoRow.module.css'
+
+export default function TodoRow(props) {
+ const { categories, userId } = props
+ const { name, description, category, votes, id } = props.rowData
+ const voted = votes.includes(userId)
+
+ return (
+
+
{`#${id}`}
+
{name}
+
+
+ {categories[category].name}
+
+
+
{`Votes: ${votes.length}`}
+
+ )
+}
diff --git a/src/components/todoStuff/todoRow.module.css b/src/components/todoStuff/todoRow.module.css
new file mode 100644
index 0000000..18af760
--- /dev/null
+++ b/src/components/todoStuff/todoRow.module.css
@@ -0,0 +1,40 @@
+.row {
+ display: flex;
+ border: 2px solid #99f;
+ border-radius: 2px;
+ padding: 5px;
+ margin: 6px 3px;
+}
+
+.row > div {
+ margin: 0px 5px;
+}
+
+.id {
+ flex: 0 20px;
+ margin: 0px 3px;
+ color: #999;
+}
+
+.description {
+ flex: 1;
+ word-break: normal;
+ font-size: 14px;
+ color: white;
+}
+
+.votes {
+}
+
+.voted {
+ border: 2px solid #cf9;
+}
+
+.catName {
+ display: flex;
+}
+
+.category {
+ word-break: break-all;
+ font-size: 10px;
+}
diff --git a/src/components/todoStuff/todos.js b/src/components/todoStuff/todos.js
new file mode 100644
index 0000000..27956bf
--- /dev/null
+++ b/src/components/todoStuff/todos.js
@@ -0,0 +1,107 @@
+import React, { useState, useEffect } from 'react'
+
+import LoadingIndicator from '../LoadingIndicator.js'
+import TodoBoard from './todoBoard.js'
+import TodoTable from './todoTable.js'
+
+import constants from '../../constants.json'
+
+const byVotes = (a, b) => {
+ return b.votes.length - a.votes.length
+}
+
+export default function Todos() {
+ const [loaded, setLoaded] = useState(false)
+ const [columns, setColumns] = useState(null)
+ const [boardCards, setBoardCards] = useState(null)
+ const [tables, setTables] = useState(null)
+ const [tableCards, setTableCards] = useState(null)
+ const [categories, setCategories] = useState(null)
+ const [userId, setUserId] = useState(null)
+
+ useEffect(() => {
+ console.info('Fetching todos')
+ fetch(`${constants.apiUrl}todos`, {
+ credentials: 'include',
+ })
+ .then((resp) => {
+ return resp.json()
+ })
+ .then((data) => {
+ setTodos(data)
+ })
+ }, [])
+
+ const setTodos = (data) => {
+ setCategories(data.todos.categories)
+ setUserId(data.userId)
+
+ let bCards = []
+ let tCards = []
+ data.todos.cards.forEach((card) => {
+ if (data.todos.columns[card.state].type === 'table') {
+ tCards.push(card)
+ } else {
+ bCards.push(card)
+ }
+ })
+
+ setTableCards(tCards.sort(byVotes))
+ setBoardCards(bCards.sort(byVotes))
+
+ let cols = {}
+ let tables = {}
+ Object.keys(data.todos.columns).forEach((key) => {
+ const col = data.todos.columns[key]
+ if (col.type !== 'table') {
+ cols = {
+ ...cols,
+ [key]: col,
+ }
+ } else {
+ tables = {
+ ...tables,
+ [key]: col,
+ }
+ }
+ })
+ setColumns(cols)
+ setTables(tables)
+
+ setLoaded(true)
+ }
+
+ const onCardClick = (id) => {
+ fetch(`${constants.apiUrl}todos?id=${id}`, {
+ credentials: 'include',
+ })
+ .then((resp) => {
+ return resp.json()
+ })
+ .then((data) => {
+ setTodos(data)
+ })
+ }
+
+ if (!loaded) {
+ return
+ }
+
+ return (
+
+
+
+
+ )
+}