Skip to content

Commit

Permalink
Update:
Browse files Browse the repository at this point in the history
a) Add historyReducer and allow simple passing paramters to MainPage;
b) Reset Db tables and update Db read procedures;
c) Initialize Privacy Policy page.
  • Loading branch information
ccxzhang committed Aug 7, 2024
1 parent e01979e commit 5b38673
Show file tree
Hide file tree
Showing 12 changed files with 247 additions and 110 deletions.
7 changes: 4 additions & 3 deletions src/renderer/src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { useState, useEffect } from 'react'
import { HashRouter as Router, Routes, Route, Link, Navigate } from 'react-router-dom'
import { useSelector, useDispatch } from 'react-redux'
import { initializeAuth, disconnect } from './reducers/authReducer'
import { initializeAuth } from './reducers/authReducer'
import MainPage from './pages/MainPage'
import Login from './pages/Login'
import Footer from './pages/Footer'
Expand All @@ -10,13 +10,13 @@ import NavBar from './pages/NavBar'
import HistoryPage from './pages/DownloadHistory'
import DataDictionary from './pages/DataDictionary'
import ModalManager from './pages/Modals/ModalManager'
import PrivacyPolicy from './pages/Privacy'
import { servicesDb, dictionaryDb, queryDb } from './service/db'
import { openModal } from './reducers/modalReducer'

const App = () => {
const dispatch = useDispatch()
const { dhis2Url, username, password, accessToken } = useSelector((state) => state.auth)
const { isLoading, notification } = useSelector((state) => state.status)

useEffect(() => {
dispatch(initializeAuth())
Expand Down Expand Up @@ -50,7 +50,7 @@ const App = () => {
path="/history"
element={
<PrivateRoute>
<HistoryPage queryDb={queryDb} />
<HistoryPage dictionaryDb={dictionaryDb} queryDb={queryDb} />
</PrivateRoute>
}
/>
Expand All @@ -66,6 +66,7 @@ const App = () => {
</PrivateRoute>
}
/>
<Route path="/privacy" element={<PrivacyPolicy />} />
</Routes>
<ModalManager />
</div>
Expand Down
41 changes: 22 additions & 19 deletions src/renderer/src/output.css
Original file line number Diff line number Diff line change
Expand Up @@ -649,6 +649,10 @@ html {
margin-bottom: 0.5rem;
}

.mb-3 {
margin-bottom: 0.75rem;
}

.mb-4 {
margin-bottom: 1rem;
}
Expand Down Expand Up @@ -921,13 +925,12 @@ html {
border-bottom-width: 1px;
}

.border-r {
border-right-width: 1px;
.border-b-2 {
border-bottom-width: 2px;
}

.border-gray-200 {
--tw-border-opacity: 1;
border-color: rgb(229 231 235 / var(--tw-border-opacity));
.border-r {
border-right-width: 1px;
}

.border-gray-300 {
Expand Down Expand Up @@ -985,11 +988,6 @@ html {
background-color: rgb(31 41 55 / var(--tw-bg-opacity));
}

.bg-indigo-500 {
--tw-bg-opacity: 1;
background-color: rgb(99 102 241 / var(--tw-bg-opacity));
}

.bg-red-500 {
--tw-bg-opacity: 1;
background-color: rgb(239 68 68 / var(--tw-bg-opacity));
Expand Down Expand Up @@ -1199,11 +1197,6 @@ html {
color: rgb(17 24 39 / var(--tw-text-opacity));
}

.text-indigo-600 {
--tw-text-opacity: 1;
color: rgb(79 70 229 / var(--tw-text-opacity));
}

.text-red-500 {
--tw-text-opacity: 1;
color: rgb(239 68 68 / var(--tw-text-opacity));
Expand Down Expand Up @@ -1310,19 +1303,29 @@ html {
background-color: rgb(156 163 175 / var(--tw-bg-opacity));
}

.hover\:bg-indigo-600:hover {
.hover\:bg-gray-50:hover {
--tw-bg-opacity: 1;
background-color: rgb(249 250 251 / var(--tw-bg-opacity));
}

.hover\:bg-gray-600:hover {
--tw-bg-opacity: 1;
background-color: rgb(79 70 229 / var(--tw-bg-opacity));
background-color: rgb(75 85 99 / var(--tw-bg-opacity));
}

.hover\:bg-red-600:hover {
--tw-bg-opacity: 1;
background-color: rgb(220 38 38 / var(--tw-bg-opacity));
}

.hover\:text-blue-700:hover {
.hover\:text-blue-600:hover {
--tw-text-opacity: 1;
color: rgb(37 99 235 / var(--tw-text-opacity));
}

.hover\:text-blue-800:hover {
--tw-text-opacity: 1;
color: rgb(29 78 216 / var(--tw-text-opacity));
color: rgb(30 64 175 / var(--tw-text-opacity));
}

.hover\:text-gray-300:hover {
Expand Down
15 changes: 4 additions & 11 deletions src/renderer/src/pages/DataDictionary.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { useRef, useState, useEffect, useCallback, useMemo } from 'react'
import { objectToCsv } from '../utils/downloadUtils'
import { updateFormulaNames } from '../utils/helpers'
import ExportLink from '../components/ExportLink'
import { triggerLoading } from '../reducers/statusReducer'

// eslint-disable-next-line react/prop-types
const DataDictionary = ({ dictionaryDb }) => {
Expand All @@ -13,15 +14,9 @@ const DataDictionary = ({ dictionaryDb }) => {
const [searchQuery, setSearchQuery] = useState('')
const [searchInput, setSearchInput] = useState('')
const dictionaryDbRef = useRef(dictionaryDb)
const elements = useLiveQuery(() => dictionaryDbRef.current.dataElements.toArray(), []) || []
const indicators = useLiveQuery(() => dictionaryDbRef.current.indicators.toArray(), []) || []
const catOptionCombos =
useLiveQuery(() => dictionaryDbRef.current.catOptionCombos.toArray(), []) || []

const data = useMemo(
() => [...elements, ...indicators, ...catOptionCombos],
[elements, indicators, catOptionCombos]
)
const allElements = useLiveQuery(() => dictionaryDbRef.current.elements.toArray(), []) || []

const data = useMemo(() => allElements, [allElements])

const filteredData = useMemo(
() =>
Expand Down Expand Up @@ -85,8 +80,6 @@ const DataDictionary = ({ dictionaryDb }) => {
downloadLink.click()
} catch (error) {
console.log(error)
} finally {
setIsLoading('')
}
}

Expand Down
137 changes: 100 additions & 37 deletions src/renderer/src/pages/DownloadHistory/index.jsx
Original file line number Diff line number Diff line change
@@ -1,35 +1,47 @@
import React, { useEffect, useState } from 'react'
import React, { useState, useEffect } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import JSZip from 'jszip'
import { useLiveQuery } from 'dexie-react-hooks'
import { objectToCsv, jsonToCsv } from '../../utils/downloadUtils'
import { objectToCsv } from '../../utils/downloadUtils'
import { setNotification, setLoading, triggerNotification } from '../../reducers/statusReducer'
import { queryHeaders } from '../../service/db'
import {
selectAllRows,
setSelectedRows,
setEditedRow,
clearEditedRow
} from '../../reducers/historyReducer'
import { addSelectedElements } from '../../reducers/dataElementsReducer'

// eslint-disable-next-line react/prop-types
const HistoryPage = ({ queryDb }) => {
const HistoryPage = ({ dictionaryDb, queryDb }) => {
const downloadQueries = useLiveQuery(() => queryDb.query.toArray(), []) || []
const [selectedRows, setSelectedRows] = useState([])
const [note, setNote] = useState('')
const [editableRowId, setEditableRowId] = useState(null)
const [temporaryNote, setTemporaryNote] = useState('')
const dispatch = useDispatch()
const { dhis2Url, username, password } = useSelector((state) => state.auth)
const { selectedRows, editedRow } = useSelector((state) => state.history)
const worker = new Worker(new URL('../../service/worker.js', import.meta.url))
const allElements = useLiveQuery(() => dictionaryDb.elements.toArray(), []) || []

useEffect(() => {
if (editedRow.rowId !== null) {
setTemporaryNote(editedRow.note || '')
}
}, [editedRow])

const handleCheckboxChange = (id) => {
setSelectedRows((prev) =>
prev.includes(id) ? prev.filter((rowId) => rowId !== id) : [...prev, id]
)
dispatch(setSelectedRows(id))
}

const handleDeleteRow = () => {
// eslint-disable-next-line react/prop-types
queryDb.query.bulkDelete(selectedRows)
}

const handleSelectAllChange = () => {
const allSelected = selectedRows.length === downloadQueries.length
const newSelection = allSelected ? [] : downloadQueries.map((q) => q.id)
setSelectedRows(newSelection)
dispatch(selectAllRows(newSelection))
}

const handleQuickRedownload = async () => {
Expand Down Expand Up @@ -99,38 +111,84 @@ const HistoryPage = ({ queryDb }) => {
}

const handleEditClick = (id, currentNote) => {
setEditableRowId(id)
setNote(currentNote || '')
dispatch(setEditedRow({ id, note: currentNote }))
}

const handleNoteChange = (e) => {
setNote(e.target.value)
setTemporaryNote(e.target.value)
}

const handleSaveNotes = async (event) => {
event.preventDefault()
if (editableRowId !== null) {
await queryDb.query.update(editableRowId, { notes: note })
dispatch(
setNotification({ message: `Note ${editableRowId} updated successfully.`, type: 'success' })
)
setEditableRowId(null)
setNote('')
if (editedRow.rowId !== null) {
try {
await queryDb.query.update(editedRow.rowId, { notes: temporaryNote })
dispatch(
triggerNotification({
message: `Note ${editedRow.rowId} updated successfully.`,
type: 'success'
})
)
dispatch(clearEditedRow())
} catch (error) {
console.error('Error updating note:', error)
dispatch(
triggerNotification({
message: `Failed to update note: ${error.message}`,
type: 'error'
})
)
}
}
}

const handlePassParams = (id) => {
console.log(downloadQueries)
const params = downloadQueries.filter((el) => el.id === id)
const dimensions = params.flatMap((param) =>
param.dimension.includes(';') ? param.dimension.split(';') : param.dimension
)
console.log(dimensions)
const elementsInfo = allElements.filter((el) => dimensions.includes(el.id))
dispatch(
addSelectedElements(elementsInfo.map((el) => ({ id: el.id, displayName: el.displayName })))
)
dispatch(
triggerNotification({
message: `Parameters passed for row ${id}.`,
type: 'info'
})
)
dispatch(clearEditedRow())
}

if (downloadQueries.length === 0) {
return <p className="text-center text-gray-500">No download history available.</p>
}

return (
<div className="mb-8 w-full flex flex-col space-y-4 p-4">
<div className="overflow-x-auto w-full">
<div className="mb-8 w-full flex flex-col space-y-4">
{/* Toolbar Component */}
<div className="bg-white px-4 py-2 shadow-md">
<div className="flex justify-between">
<div className="flex space-x-2">
<button
className="text-blue-600 hover:text-blue-800 font-semibold"
onClick={handleExportDownloadHistory}
disabled={downloadQueries.length === 0}
>
Export
</button>
</div>
</div>
</div>
{/* Table Component */}
<div className="overflow-x-auto px-4">
<form onSubmit={handleSaveNotes}>
<table className="w-full max-w-4xl mx-auto bg-white rounded-lg">
<table className="w-full max-w-4xl mx-auto bg-white rounded-lg shadow-lg">
<thead>
<tr className="bg-gray-100 text-gray-800 uppercase text-sm leading-normal">
<th className="py-3 px-4 text-left">
<th className="py-3 px-3 border-b-2 border-gray-300">
<input
type="checkbox"
className="form-checkbox h-4 w-4 text-gray-600 transition duration-150 ease-in-out"
Expand All @@ -140,7 +198,7 @@ const HistoryPage = ({ queryDb }) => {
</th>

{queryHeaders.map((name, index) => (
<th key={index} className="py-3 px-4 text-left">
<th key={index} className="py-3 px-3 border-b-2 border-gray-300">
{name}
</th>
))}
Expand All @@ -149,7 +207,7 @@ const HistoryPage = ({ queryDb }) => {
<tbody className="text-gray-800 text-xs font-light">
{downloadQueries.map((el) => (
<tr key={el.id} className="hover:bg-gray-50">
<td className="py-3 px-4 text-left">
<td className="py-2 px-3 border-b border-gray-300">
<input
type="checkbox"
className="form-checkbox h-4 w-4 text-gray-600 transition duration-150 ease-in-out"
Expand All @@ -160,14 +218,26 @@ const HistoryPage = ({ queryDb }) => {
<button type="button" onClick={() => handleEditClick(el.id, el.notes)}>
Edit
</button>
{editableRowId === el.id && <button type="submit">Save</button>}
{editedRow.rowId === el.id && (
<>
<button type="submit">Save</button>
<button type="button" onClick={() => handlePassParams(el.id)}>
Pass
</button>
</>
)}
</td>
</td>

{queryHeaders.map((header) => (
<td key={header}>
{editableRowId === el.id && header === 'notes' ? (
<input type="text" name={header} value={note} onChange={handleNoteChange} />
<td key={header} className="py-2 px-3 border-b border-gray-300">
{editedRow.rowId === el.id && header === 'notes' ? (
<input
type="text"
name={header}
value={temporaryNote}
onChange={handleNoteChange}
/>
) : header === 'url' ? (
<a
href={el[header]}
Expand Down Expand Up @@ -196,13 +266,6 @@ const HistoryPage = ({ queryDb }) => {
>
Delete
</button>
<button
onClick={handleExportDownloadHistory}
className="bg-gray-500 text-white px-4 py-2 rounded-md hover:bg-gray-600 transition duration-150 ease-in-out"
disabled={downloadQueries.length === 0}
>
Export Downloading History
</button>
<button
onClick={handleQuickRedownload}
className="bg-gray-500 text-white px-4 py-2 rounded-md hover:bg-gray-600 transition duration-150 ease-in-out"
Expand Down
Loading

0 comments on commit 5b38673

Please sign in to comment.