Skip to content

Commit

Permalink
Merge branch 'IMP_navigation_buttons'
Browse files Browse the repository at this point in the history
  • Loading branch information
vokimon committed Dec 29, 2023
2 parents b839bb3 + 5430ee9 commit 9cd6277
Show file tree
Hide file tree
Showing 10 changed files with 250 additions and 81 deletions.
4 changes: 3 additions & 1 deletion backend/datasources/dummy.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,9 @@ def dummy_installation_details(username: str, contract_number: str) -> Installat
code=contract_number,
error=f"{contract_number} (Dummy error)",
))
return InstallationDetailsResult(**ns.load('frontend/src/data/dummyinstallationdetail.yaml'))
details = InstallationDetailsResult(**ns.load('frontend/src/data/dummyinstallationdetail.yaml'))
details.installation_details.contract_number = contract_number
return details

invoice_pdf_exceptions = {
e.__name__: e
Expand Down
21 changes: 16 additions & 5 deletions frontend/src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import InstallationsPage from './pages/InstallationsPage'
import DetailInstallationPage from './pages/DetailInstallationPage'
import InvoicesPage from './pages/InvoicesPage'
import ProductionPage from './pages/ProductionPage'
import { InstallationContextProvider } from './components/InstallationProvider'

const routes = [
{
Expand Down Expand Up @@ -73,11 +74,21 @@ const routes = [
},
{
path: '/installation',
element: <InstallationsPage />,
},
{
path: '/installation/:contract_number',
element: <DetailInstallationPage />,
element: (
<InstallationContextProvider>
<Outlet />
</InstallationContextProvider>
),
children: [
{
path: '',
element: <InstallationsPage />,
},
{
path: ':contract_number',
element: <DetailInstallationPage />,
},
],
},
{
path: '/invoices',
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/BreakPointIndicator.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export default function BreakPointIndicator() {
minWidth: '4rem',
padding: '0.5rem 0.9rem',
borderRadius: '5px',
alignText: 'center',
textAlign: 'center',
color: 'black',
backgroundColor: {
xs: '#d99a',
Expand Down
48 changes: 48 additions & 0 deletions frontend/src/components/InstallationProvider.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import React, { useEffect, useState, useMemo } from 'react'
import PropTypes from 'prop-types'
import { useAuth } from './AuthProvider'
import ovapi from '../services/ovapi'

const InstallationContext = React.createContext()

const InstallationContextProvider = ({ children }) => {
const { currentUser } = useAuth()
const [installations, setInstallations] = useState(null)
const [loading, setLoading] = useState(true)
const [error, setError] = useState(null)
const memoizedCurrentUser = useMemo(() => currentUser, [currentUser])

useEffect(() => {
const getInstallations = async () => {
try {
const installationsData = await ovapi.installations(memoizedCurrentUser)
setInstallations(installationsData)
} catch (error) {
setError(error)
} finally {
setLoading(false)
}
}

getInstallations()
}, [memoizedCurrentUser])
getInstallations()
}, [memoizedCurrentUser])

const contextValue = useMemo(
() => ({ installations, loading, error }),
[installations, loading, error],
)

return installations !== null ? (
<InstallationContext.Provider value={contextValue}>
{children}
</InstallationContext.Provider>
) : null
}

InstallationContextProvider.propTypes = {
children: PropTypes.node.isRequired,
}

export { InstallationContext, InstallationContextProvider }
20 changes: 13 additions & 7 deletions frontend/src/components/NavigationButtons.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,23 +19,29 @@ const StyledButton = styled(Button)(({ theme }) => ({

const NavigationButtons = (props) => {
const { toBefore, toNext, toReturn, returnIcon } = props

return (
<Box
sx={{
widht: '100%',
width: '100%',
ml: '.5rem',
display: 'flex',
justifyContent: 'flex-end',
marginRight: '1rem',
marginTop: '1rem',
}}
>
<ButtonGroup size="small">
<StyledButton component={Link} to={toBefore}>
<NavigateBeforeIcon />
</StyledButton>
<StyledButton component={Link} to={toNext}>
<NavigateNextIcon />
</StyledButton>
{toBefore && (
<StyledButton component={Link} to={toBefore}>
<NavigateBeforeIcon />
</StyledButton>
)}
{toNext && (
<StyledButton component={Link} to={toNext}>
<NavigateNextIcon />
</StyledButton>
)}
<StyledButton component={Link} to={toReturn}>
{returnIcon}
</StyledButton>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export const contractFields = [
'discharge_date',
'status',
]

export function transformInstallationDetails(data) {
const t = i18n.t
const productionTecnologyOptions = {
Expand All @@ -39,7 +40,7 @@ export function transformInstallationDetails(data) {
technology: format.enumeration(data.technology, productionTecnologyOptions),
}
}
export default function transformContractDetails(contract) {
function transformContractDetails(contract) {
const t = i18n.t
const billingModeOptions = {
index: t('CONTRACT_DETAIL.BILLING_MODE_INDEX'),
Expand Down Expand Up @@ -88,3 +89,26 @@ export default function transformContractDetails(contract) {

return contract
}

function computeNavigationInfo(installations, currentInstallationContractNumber) {
if (installations.length < 2) {
return {}
}

// Find the index of the current installation
const currentIndex = installations.findIndex(
(installation) => installation.contract_number === currentInstallationContractNumber,
)

// Determine the index of the previous and next installations
const previousIndex = currentIndex > 0 ? currentIndex - 1 : installations.length - 1
const nextIndex = currentIndex < installations.length - 1 ? currentIndex + 1 : 0

// Extract the contract numbers for the previous and next installations
const before = installations[previousIndex].contract_number
const next = installations[nextIndex].contract_number

return { before, next }
}

export { transformContractDetails, computeNavigationInfo }
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { describe, expect, it } from 'vitest'
import i18n from '../../i18n/i18n'

import transformContractDetails, {
import {
transformContractDetails,
transformInstallationDetails,
computeNavigationInfo,
} from './detailInstallationData'

describe('transformContractDetails', () => {
Expand Down Expand Up @@ -138,3 +140,77 @@ describe('transformInstallationDetails', () => {
expect(result['rated_power']).toEqual('11 kW')
})
})

describe('computeNavigationInfo', () => {
it('Returns empty when installations length is smaller than 2', () => {
const installations = [
{
contract_number: 'a_contract_number',
installation_name: 'an_installation_name',
},
]
const currentInstallationContractNumber = 'a_contract_number'

const result = computeNavigationInfo(installations, currentInstallationContractNumber)

expect(result).toEqual({})
})

describe('Having the installation 2 elements', () => {
it('Returns the not current element as before and next navigation values', () => {
const installations = [
{
contract_number: 'a_contract_number',
installation_name: 'an_installation_name',
},
{
contract_number: 'another_contract_number',
installation_name: 'another_installation_name',
},
]
const currentInstallationContractNumber = 'a_contract_number'

const result = computeNavigationInfo(
installations,
currentInstallationContractNumber,
)

const expected_result = {
before: 'another_contract_number',
next: 'another_contract_number',
}
expect(result).toEqual(expected_result)
})
})

describe('Having the installation more than 2 elements', () => {
it('Returns the not current element as before and next navigation values', () => {
const installations = [
{
contract_number: 'a_contract_number',
installation_name: 'an_installation_name',
},
{
contract_number: 'another_contract_number',
installation_name: 'another_installation_name',
},
{
contract_number: 'yet_another_contract_number',
installation_name: 'yet_another_installation_name',
},
]
const currentInstallationContractNumber = 'another_contract_number'

const result = computeNavigationInfo(
installations,
currentInstallationContractNumber,
)

const expected_result = {
before: 'a_contract_number',
next: 'yet_another_contract_number',
}
expect(result).toEqual(expected_result)
})
})
})
58 changes: 36 additions & 22 deletions frontend/src/pages/DetailInstallationPage/index.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useEffect, useState } from 'react'
import React, { useEffect, useState, useContext, useMemo } from 'react'
import { useParams } from 'react-router-dom'
import { useTranslation } from 'react-i18next'
import Container from '@mui/material/Container'
Expand All @@ -12,36 +12,52 @@ import ErrorSplash from '../../components/ErrorSplash'
import NavigationButtons from '../../components/NavigationButtons'
import { contractFields } from './detailInstallationData'
import { installationFields } from './detailInstallationData'
import transformContractDetails, {
import {
transformContractDetails,
transformInstallationDetails,
computeNavigationInfo,
} from './detailInstallationData'
import { InstallationContext } from '../../components/InstallationProvider'

export default function DetailInstallationPage(params) {
export default function DetailInstallationPage() {
const { contract_number } = useParams()
const { t } = useTranslation()
const [installationDetail, setInstallationDetail] = useState(undefined)
const [contractDetail, setContractDetail] = useState(undefined)
const [error, setError] = useState(false)
const { installations } = useContext(InstallationContext)
const memoizedInstallations = useMemo(() => installations, [installations])
const navigationInfo = computeNavigationInfo(
memoizedInstallations,
installationDetail?.contract_number,
)
const navigationBeforeUrl = navigationInfo.before
? `/installation/${navigationInfo.before}`
: undefined
const navigationNextUrl = navigationInfo.next
? `/installation/${navigationInfo.next}`
: undefined

useEffect(() => {
getDetailInstallation()
}, [contract_number])
}, [contract_number, memoizedInstallations])

async function getDetailInstallation() {
setError(false)
setInstallationDetail(undefined)
setContractDetail(undefined)
var result
try {
result = await ovapi.installationDetails(contract_number)
} catch (e) {
setError(e)
return
const result = await ovapi.installationDetails(contract_number)
if (!result) {
setError(true)
return
}
setInstallationDetail(result?.installation_details)
const contractData = transformContractDetails(result?.contract_details)
setContractDetail(contractData)
} catch (error) {
setError(true)
}
const installationData = transformInstallationDetails(result?.installation_details)
setInstallationDetail(installationData)
const contractData = transformContractDetails(result?.contract_details)
setContractDetail(contractData)
}

return !error && (!installationDetail || !contractDetail) ? (
Expand All @@ -50,23 +66,21 @@ export default function DetailInstallationPage(params) {
<Container>
<PageTitle Icon={SolarPowerIcon}>
{t('INSTALLATION_DETAIL.DETAILS_TITLE')}
<NavigationButtons
toBefore={navigationBeforeUrl}
toNext={navigationNextUrl}
toReturn="/installation"
returnIcon={<FormatListBulletedIcon />}
/>
</PageTitle>
{error ? (
<ErrorSplash
title={t('INSTALLATION_DETAIL.ERROR_LOADING_DATA')}
message={error.error}
message={t('INSTALLATION_DETAIL.ERROR_LOADING_DATA')}
backlink="/installation"
backtext={t('INSTALLATION_DETAIL.BACK_TO_INSTALLATIONS')}
/>
) : (
<>
{/* TODO: get the before and after user installations */}
<NavigationButtons
toBefore="/installation/00001"
toNext="/installation/00001"
toReturn="/installation"
returnIcon={<FormatListBulletedIcon />}
/>
<SimpleTable
fields={installationDetail}
fieldsOrder={installationFields}
Expand Down
Loading

0 comments on commit 9cd6277

Please sign in to comment.