diff --git a/ui/src/App.tsx b/ui/src/App.tsx index ef31dcb..ddc58a0 100644 --- a/ui/src/App.tsx +++ b/ui/src/App.tsx @@ -65,7 +65,7 @@ function App() { event.dataTransfer.setDragImage(dragImage, 0, 0) - event.target.addEventListener('dragend', function() { + event.target.addEventListener('dragend', function () { const eventIframe = (event.target as Element).querySelector( 'iframe' ) as HTMLIFrameElement @@ -122,7 +122,9 @@ function App() { } useEffect(() => { - console.log(`path: ${activeWindowID ? windowMap.get(activeWindowID) : 'null'}`) + console.log( + `path: ${activeWindowID ? windowMap.get(activeWindowID) : 'null'}` + ) console.log(`activeWindowID: ${activeWindowID}`) console.log(`maxWindow: ${maxWindow}`) const handleKeyDown = (event: KeyboardEvent) => { diff --git a/ui/src/components/Window.tsx b/ui/src/components/Window.tsx index 6ca8600..018040e 100644 --- a/ui/src/components/Window.tsx +++ b/ui/src/components/Window.tsx @@ -10,11 +10,11 @@ import TextHTML from './renderers/TextHTML' import ApplicationPDF from './renderers/ApplicationPDF' import ReactDOMServer from 'react-dom/server' import TextPlain from './renderers/TextPlain' +import ApplicationJSON from './renderers/ApplicationJSON' export interface WindowProps { id: number path: string | null - setMaxWindow: React.Dispatch> | null handleDrop: (event: React.DragEvent, id: number) => void handleDragStart: (event: React.DragEvent, id: number) => void dragWindow: number @@ -152,8 +152,8 @@ export default function Window({ } case 'application/json': { console.log('Processing JSON data...') - const txt = await res.text() - return + const JSON = await res.json() + return } case 'application/xml': { console.log('Processing XML file...') diff --git a/ui/src/components/renderers/ApplicationJSON.tsx b/ui/src/components/renderers/ApplicationJSON.tsx new file mode 100644 index 0000000..abfafb3 --- /dev/null +++ b/ui/src/components/renderers/ApplicationJSON.tsx @@ -0,0 +1,67 @@ +import { useState } from 'react' + +interface ApplicationJSONProps { + json: JSON +} + +export default function ApplicationJSON({ + json, +}: ApplicationJSONProps): JSX.Element { + console.log('rendering ', json) + + const jsonData = Array.isArray(json) ? json : [json] + const [sortedData, setSortedData] = useState(jsonData) + const [sortDirection, setSortDirection] = useState('asc') // 'asc' for ascending, 'desc' for descending + const [sortColumn, setSortColumn] = useState('') + + const handleSort = (column: string) => { + const direction = + sortColumn === column && sortDirection === 'asc' ? 'desc' : 'asc' + setSortColumn(column) + setSortDirection(direction) + + const sorted = [...jsonData].sort((a, b) => { + const aValue = a[column] + const bValue = b[column] + + if (typeof aValue === 'string' && typeof bValue === 'string') { + return direction === 'asc' + ? aValue.localeCompare(bValue) + : bValue.localeCompare(aValue) + } + + return direction === 'asc' ? aValue - bValue : bValue - aValue + }) + + setSortedData(sorted) + } + + return ( +
+ + + + {Object.keys(sortedData[0]).map(key => ( + + ))} + + + + {sortedData.map((row, index) => ( + + {Object.keys(row).map(key => + row ? ( + + ) : null + )} + + ))} + +
handleSort(key)}> + {key.charAt(0).toUpperCase() + key.slice(1)} +
+ {row[key]} +
+
+ ) +} diff --git a/ui/src/components/renderers/FileJSON.tsx b/ui/src/components/renderers/FileJSON.tsx new file mode 100644 index 0000000..5f7a950 --- /dev/null +++ b/ui/src/components/renderers/FileJSON.tsx @@ -0,0 +1,161 @@ +import { useState, useCallback } from 'react' +import useWindowStore from '../../state/useWindowStore' +import { debounce } from 'lodash' +import { put } from '../../api/sky' + +interface FileJSONProps { + json: JSON +} + +export default function FileJSON({ json }: FileJSONProps): JSX.Element { + const jsonData = Array.isArray(json) ? json : [json] + const [sortedData, setSortedData] = useState(jsonData) + const [sortDirection, setSortDirection] = useState('asc') // 'asc' for ascending, 'desc' for descending + const [sortColumn, setSortColumn] = useState('') + const [editCell, setEditCell] = useState({ row: 0, key: '' }) + const { activeWindowPath } = useWindowStore() + const [path, setPath] = useState('') + + const handleSort = (column: string) => { + const direction = + sortColumn === column && sortDirection === 'asc' ? 'desc' : 'asc' + setSortColumn(column) + setSortDirection(direction) + + const sorted = [...sortedData].sort((a, b) => { + const aValue = a[column] + const bValue = b[column] + + if (typeof aValue === 'string' && typeof bValue === 'string') { + return direction === 'asc' + ? aValue.localeCompare(bValue) + : bValue.localeCompare(aValue) + } + + return direction === 'asc' ? aValue - bValue : bValue - aValue + }) + + return sorted + } + + const handleClickSort = (column: string) => { + const sorted = handleSort(column) + setSortedData(sorted) + } + + const handleClickCell = (index: number, key: string) => { + setEditCell({ row: index, key: key }) + } + + const handleCellChange = (value: string) => { + const updatedData = [...sortedData] + updatedData[editCell.row][editCell.key] = value + setSortedData(updatedData) + setPath(activeWindowPath) + console.log('data updated', updatedData) + } + + const handleSubmitCell = useCallback( + debounce(async () => { + if (activeWindowPath) { + const formData = new FormData() + // Sorting data by the first key before uploading + setSortDirection('desc') + const data = handleSort(Object.keys(sortedData[0])[0]) + + const file = new File([JSON.stringify(data)], 'file.json', { + type: 'application/json', + }) + console.log('updating data to ', file) + formData.append('file', file) + console.log('form data', formData) + + try { + await put(activeWindowPath, formData) + console.log('Upload successful') + } catch (error) { + console.error('Upload failed:', error) + } + } + }, 500), + [activeWindowPath] + ) + + const handleBlur = () => { + // Prevents JSON file from being saved to unintended path, + // if different window gained focus after blur event occured. + console.log('paths', path, activeWindowPath) + if (path === activeWindowPath) { + console.log('handle Blur') + setEditCell({ row: 0, key: '' }) + handleSubmitCell() + } + } + + const handleKeyDown = (e: React.KeyboardEvent) => { + if (e.key === 'Enter') { + console.log('handle keydown Enter') + setEditCell({ row: 0, key: '' }) + handleSubmitCell() + } + } + + return ( +
+ + + + {Object.keys(sortedData[0]).map(key => ( + + ))} + + + + {sortedData.map((row, index) => ( + + {Object.keys(row).map(key => + row ? ( + + ) : null + )} + + ))} + +
handleClickSort(key)} + > + {key.charAt(0).toUpperCase() + key.slice(1)} +
{ + handleClickCell(index, key) + }} + > + {editCell.row === index && editCell.key === key ? ( + { + handleCellChange(e.target.value) + }} + onBlur={() => { + handleBlur() + }} + onKeyDown={( + e: React.KeyboardEvent + ) => { + handleKeyDown(e) + }} + /> + ) : ( + row[key] + )} +
+
+ ) +} diff --git a/ui/src/components/renderers/FileSystem.tsx b/ui/src/components/renderers/FileSystem.tsx index 1787802..805990d 100644 --- a/ui/src/components/renderers/FileSystem.tsx +++ b/ui/src/components/renderers/FileSystem.tsx @@ -6,6 +6,7 @@ import FileMarkdown from './FileMarkdown' import FileHTML from './FileHTML' import FilePDF from './FilePDF' import FilePlain from './FilePlain' +import FileJSON from './FileJSON' interface FileSystemProps { id: number @@ -40,7 +41,7 @@ async function renderFile(res: Response): Promise { } case 'application/json': { console.log('Rendering application/json') - return + return } case 'text/markdown': { console.log('Rendering text/markdown')