Created
March 13, 2021 22:53
-
-
Save anderskitson/cc50049de5fa0b3a1d3a9b9f5c9fa4b0 to your computer and use it in GitHub Desktop.
state.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { gql, useMutation, useQuery } from "@apollo/client"; | |
import useSWR from "swr"; | |
import Layout from "../../components/Layout"; | |
import React, { useState } from "react"; | |
import { useTable, usePagination } from "react-table"; | |
import styled from "styled-components"; | |
import ReactDatePicker from "react-datepicker"; | |
import { useEffect } from "react"; | |
import "react-datepicker/dist/react-datepicker.css"; | |
import { useCallback } from "react"; | |
import debounce from "lodash.debounce"; | |
const DECOR_VALUES = gql` | |
query GetDecorValues($id: ID!) { | |
findUserByID(id: $id) { | |
decor { | |
data { | |
purchaseDate | |
description | |
alterations | |
cost | |
pieces | |
category | |
purchaser | |
image | |
itemNum | |
_id | |
} | |
} | |
} | |
} | |
`; | |
const UPDATE_DECOR_DOC = gql` | |
mutation UpdateDecorDoc( | |
# $ownerID: ID! | |
$description: String! | |
$pieces: Int! | |
$purchaser: String! | |
$alterations: Boolean! | |
$cost: Int! | |
$purchaseDate: Date! | |
$category: String! | |
$image: String! | |
$itemNum: Int! | |
) { | |
updateDecor( | |
data: { | |
description: $description | |
pieces: $pieces | |
purchaser: $purchaser | |
alterations: $alterations | |
cost: $cost | |
purchaseDate: $purchaseDate | |
category: $category | |
image: $image | |
itemNum: $itemNum | |
# owner: { connect: $ownerID } | |
} | |
) { | |
description | |
} | |
} | |
`; | |
const Styles = styled.div` | |
padding: 1rem; | |
table { | |
border-spacing: 0; | |
border: 1px solid black; | |
tr { | |
:last-child { | |
td { | |
border-bottom: 0; | |
} | |
} | |
} | |
th, | |
td { | |
margin: 0; | |
padding: 0.5rem; | |
border-bottom: 1px solid black; | |
border-right: 1px solid black; | |
:last-child { | |
border-right: 0; | |
} | |
input { | |
font-size: 1rem; | |
padding: 0; | |
margin: 0; | |
border: 0; | |
} | |
&:hover { | |
background: lightpink; | |
} | |
} | |
} | |
.pagination { | |
padding: 0.5rem; | |
} | |
`; | |
// Create an editable cell renderer | |
const EditableCell = ({ | |
value: initialValue, | |
row: { index }, | |
column: { id }, | |
updateMyData, // This is a custom function that we supplied to our table instance | |
}) => { | |
// We need to keep and update the state of the cell normally | |
const [value, setValue] = React.useState(initialValue); | |
const onChange = (e) => { | |
setValue(e.target.value); | |
}; | |
const onChangeDate = (e) => { | |
setValue(e); | |
}; | |
const onChangeCheck = (e) => { | |
setValue(e.target.checked); | |
}; | |
// We'll only update the external data when the input is blurred | |
const onBlur = () => { | |
updateMyData(index, id, value); | |
}; | |
// If the initialValue is changed external, sync it up with our state | |
React.useEffect(() => { | |
setValue(initialValue); | |
}, [initialValue]); | |
if (id === "col3") { | |
return ( | |
<input | |
type="checkbox" | |
defaultChecked={value} | |
onChange={onChangeCheck} | |
onBlur={onBlur} | |
/> | |
); | |
} | |
if (id === "col1") { | |
return ( | |
<ReactDatePicker | |
onCalendarClose={onBlur} | |
selected={new Date(value)} | |
onChange={onChangeDate} | |
/> | |
); | |
} | |
return <input value={value} onChange={onChange} onBlur={onBlur} />; | |
}; | |
// Set our editable cell renderer as the default Cell renderer | |
const defaultColumn = { | |
Cell: EditableCell, | |
}; | |
const fetcher = (url) => fetch(url).then((r) => r.json()); | |
export default function DecorData() { | |
const { data: user, error: userError } = useSWR("/api/user", fetcher); | |
const { data: cookieData, error: cookieError } = useSWR( | |
"/api/cookie", | |
fetcher | |
); | |
var cookieBearer = `Bearer ${cookieData}`; | |
if (!user || !cookieData) return <p>Loading</p>; | |
if (userError) return <p>{userError.message}</p>; | |
if (cookieError) return <p>{cookieError.message}</p>; | |
return ( | |
<Layout> | |
<h1>View your Decor Catalog Table Here</h1> | |
{user && cookieBearer && ( | |
<Table user={user} cookieBearer={cookieBearer} /> | |
)} | |
</Layout> | |
); | |
} | |
const Table = ({ user, cookieBearer }) => { | |
const { loading, error, data: decorData } = useQuery(DECOR_VALUES, { | |
variables: { id: user.id }, | |
context: { | |
headers: { | |
authorization: cookieBearer, | |
}, | |
}, | |
}); | |
const massaged = decorData?.findUserByID?.decor?.data?.map((item) => { | |
var col = Object.values(item); | |
return col.map((colItem, i) => { | |
return { [`col${i}`]: colItem }; | |
}); | |
}); | |
const massaged1 = decorData?.findUserByID?.decor?.data?.map((item, int) => { | |
var col = Object.keys(item); | |
return col?.map((colItem, i) => { | |
// console.log(colItem); | |
if (colItem === "image") { | |
return { | |
Header: colItem, | |
accessor: `col${i}`, | |
Cell: ({ cell: { value } }) => <ImageComp value={value} />, | |
}; | |
} | |
return { | |
Header: colItem, | |
accessor: `col${i}`, | |
}; | |
}); | |
}); | |
const result = massaged?.map((a) => Object.assign({}, ...a)); | |
if (loading) return <p>Loading</p>; | |
if (error) return <p>{error.message}</p>; | |
if (!decorData) return <p>No Decord Data</p>; | |
return ( | |
<> | |
{result && massaged1 && ( | |
<TryThis | |
cookieBearer={cookieBearer} | |
result={result} | |
massaged1={massaged1} | |
/> | |
)} | |
</> | |
); | |
}; | |
const ImageComp = ({ value }) => { | |
return ( | |
<> | |
<img src={value} width="400" /> | |
<button>Replace Image</button> | |
</> | |
); | |
}; | |
function TryThis({ result, massaged1, cookieBearer }) { | |
const [data, setData] = React.useState(result); //bad naming here | |
// const [originalData] = React.useState(data) | |
const [skipPageReset, setSkipPageReset] = React.useState(false); | |
const updateMyData = (rowIndex, columnId, value) => { | |
// We also turn on the flag to not reset the page | |
setSkipPageReset(true); | |
setData((old) => { | |
// console.log(old); | |
return old.map((row, index) => { | |
if (index === rowIndex) { | |
return { | |
...old[rowIndex], | |
[columnId]: value, | |
}; | |
} | |
return row; | |
}); | |
}); | |
}; | |
// After data chagnes, we turn the flag back off | |
// so that if data actually changes when we're not | |
// editing it, the page is reset | |
React.useEffect(() => { | |
setSkipPageReset(false); | |
}, [data]); | |
return ( | |
<> | |
{/* {result && massaged1 && ( | |
<TableRendered result={result} massaged1={massaged1} /> | |
)} */} | |
{data && result && massaged1 && ( | |
<Styles> | |
{/* <button onClick={resetData}>Reset Data</button> */} | |
<Table1 | |
columns={massaged1[0]} | |
data={data} | |
updateMyData={updateMyData} | |
skipPageReset={skipPageReset} | |
cookieBearer={cookieBearer} | |
oldState={result} | |
/> | |
</Styles> | |
)} | |
</> | |
); | |
} | |
function Table1({ | |
columns, | |
data, | |
updateMyData, | |
skipPageReset, | |
cookieBearer, | |
oldState, | |
}) { | |
// For this example, we're using pagination to illustrate how to stop | |
// the current page from resetting when our data changes | |
// Otherwise, nothing is different here. | |
const { | |
getTableProps, | |
getTableBodyProps, | |
headerGroups, | |
prepareRow, | |
page, | |
canPreviousPage, | |
canNextPage, | |
pageOptions, | |
pageCount, | |
gotoPage, | |
nextPage, | |
previousPage, | |
setPageSize, | |
state: { pageIndex, pageSize }, | |
} = useTable( | |
{ | |
columns, | |
data, | |
defaultColumn, | |
// use the skipPageReset option to disable page resetting temporarily | |
autoResetPage: !skipPageReset, | |
// updateMyData isn't part of the API, but | |
// anything we put into these options will | |
// automatically be available on the instance. | |
// That way we can call this function from our | |
// cell renderer! | |
updateMyData, | |
}, | |
usePagination | |
); | |
// Render the UI for your table | |
return ( | |
<> | |
<table {...getTableProps()}> | |
<thead> | |
{headerGroups.map((headerGroup) => ( | |
<tr {...headerGroup.getHeaderGroupProps()}> | |
{headerGroup.headers.map((column) => ( | |
<th {...column.getHeaderProps()}>{column.render("Header")}</th> | |
))} | |
</tr> | |
))} | |
</thead> | |
<tbody {...getTableBodyProps()}> | |
{page.map((row, i) => { | |
prepareRow(row); | |
return ( | |
<tr {...row.getRowProps()}> | |
{row.cells.map((cell) => { | |
return ( | |
<td {...cell.getCellProps()}>{cell.render("Cell")}</td> | |
); | |
})} | |
</tr> | |
); | |
})} | |
</tbody> | |
</table> | |
<div className="pagination"> | |
<button onClick={() => gotoPage(0)} disabled={!canPreviousPage}> | |
{"<<"} | |
</button>{" "} | |
<button onClick={() => previousPage()} disabled={!canPreviousPage}> | |
{"<"} | |
</button>{" "} | |
<button onClick={() => nextPage()} disabled={!canNextPage}> | |
{">"} | |
</button>{" "} | |
<button onClick={() => gotoPage(pageCount - 1)} disabled={!canNextPage}> | |
{">>"} | |
</button>{" "} | |
<span> | |
Page{" "} | |
<strong> | |
{pageIndex + 1} of {pageOptions.length} | |
</strong>{" "} | |
</span> | |
<span> | |
| Go to page:{" "} | |
<input | |
type="number" | |
defaultValue={pageIndex + 1} | |
onChange={(e) => { | |
const page = e.target.value ? Number(e.target.value) - 1 : 0; | |
gotoPage(page); | |
}} | |
style={{ width: "100px" }} | |
/> | |
</span>{" "} | |
<select | |
value={pageSize} | |
onChange={(e) => { | |
setPageSize(Number(e.target.value)); | |
}} | |
> | |
{[10, 20, 30, 40, 50].map((pageSize) => ( | |
<option key={pageSize} value={pageSize}> | |
Show {pageSize} | |
</option> | |
))} | |
</select> | |
</div> | |
<pre> | |
<code> | |
{JSON.stringify( | |
{ | |
data, | |
}, | |
null, | |
2 | |
)} | |
</code> | |
</pre> | |
{cookieBearer && oldState && data && ( | |
<AutoSave | |
oldState={oldState} | |
cookieBearer={cookieBearer} | |
saveData={data} | |
/> | |
)} | |
</> | |
); | |
} | |
function AutoSave({ saveData, cookieBearer, oldState }) { | |
const [saving, setSaving] = useState(false); | |
const [newState, setNewState] = useState([]); | |
const [ | |
updateDecorDoc, | |
{ data: docData, loading: savingMutate }, | |
] = useMutation(UPDATE_DECOR_DOC, { | |
context: { | |
headers: { | |
authorization: cookieBearer, | |
}, | |
}, | |
}); | |
const debounceSave = useCallback( | |
debounce(async (saveData) => { | |
setSaving(true); | |
function compareArray(oldItem, newItem) { | |
const compared = {}; | |
for (const key in oldItem) { | |
if ( | |
(key == "col10" || oldItem[key] != newItem[key]) && | |
Object.hasOwnProperty.call(newItem, key) && | |
Object.hasOwnProperty.call(oldItem, key) | |
) { | |
compared[key] = newItem[key]; | |
} | |
} | |
return compared; | |
} | |
oldState | |
.map((old, i) => [old, saveData[i]]) | |
.forEach((item) => console.log(compareArray(...item))); | |
// console.log(test); | |
// let yourDate = saveData["col1"]; | |
// const offset = yourDate.getTimezoneOffset(); | |
// yourDate = new Date(yourDate.getTime() - offset * 60 * 1000); | |
// const date = yourDate.toISOString().split("T")[0]; | |
// /////SAVE DATA TO DB HERE | |
// const res = await createDecorEntry({ | |
// variables: { | |
// purchaseDate: date, | |
// description: saveData["col2"], | |
// alterations: saveData["col3"], | |
// cost: saveData["col4"], | |
// pieces: saveData["col5"], | |
// category: saveData["col6"], | |
// purchaser: saveData["col7"], | |
// }, | |
// }).catch(console.error); | |
}) | |
); | |
useEffect(() => { | |
if (saveData) { | |
debounceSave(saveData); | |
} | |
}, [saveData, debounceSave]); | |
if (saving) return <p>saving</p>; | |
if (!saving) return <p>Auto Save on</p>; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment