Skip to content

Commit

Permalink
feat: Authorization documents (#199)
Browse files Browse the repository at this point in the history
  • Loading branch information
ccallendar committed Jul 29, 2024
1 parent 38d34ef commit 6f4845d
Show file tree
Hide file tree
Showing 42 changed files with 367 additions and 87 deletions.
2 changes: 0 additions & 2 deletions frontend/e2e/pages/auth.details.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,6 @@ export const authorization_details_page = async (page: Page) => {
page.getByText('Schedule 2 - Composting Operations'),
).toBeVisible()

await expect(page.getByText('Documents', { exact: true })).toBeVisible()

const backBtn = page.getByRole('button', { name: 'Back to Text Search' })
await expect(backBtn).toBeVisible()
await backBtn.click()
Expand Down
68 changes: 67 additions & 1 deletion frontend/src/App.test.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,28 @@
import { useSelector } from 'react-redux'
import { screen } from '@testing-library/react'

import App from '@/App'
import { render } from '@/test-utils'
import { initialState } from '@/features/omrr/omrr-slice'
import {
initialState,
selectAllResults,
selectStatus as selectOmrrStatus,
} from '@/features/omrr/omrr-slice'
import {
selectAllDocuments,
useDocumentsStatus,
} from '@/features/omrr/documents-slice'
import {
selectAllApplications,
selectStatus as selectApplicationStatus,
} from '@/features/omrr/applications-slice'
import OmrrData from '@/interfaces/omrr'
import { OmrrApplicationStatus } from '@/interfaces/omrr-application-status'
import { OmrrAuthzDocsResponse } from '@/interfaces/omrr-documents'
import { LoadingStatusType } from '@/interfaces/loading-status'
import { mockOmrrData } from '@/mocks/mock-omrr-data'
import { mockOmrrApplicationStatusResponse } from '@/mocks/mock-omrr-application-status'
import { mockOmrrDocuments } from '@/mocks/mock-omrr-documents'

describe('App suite', () => {
test('renders the App with idle state', () => {
Expand Down Expand Up @@ -65,4 +85,50 @@ describe('App suite', () => {
screen.getByText('Organics Info')
screen.getByText('Loading failed, please try again later')
})

test('renders the App and loads API data using MSW', async () => {
interface State {
allResults?: OmrrData[]
omrrStatus?: LoadingStatusType
allApplications?: OmrrApplicationStatus[]
applicationStatus?: LoadingStatusType
allDocuments?: OmrrAuthzDocsResponse[]
documentStatus?: LoadingStatusType
}
const state: State = {}

const TestComponent = () => {
Object.assign(state, {
allResults: useSelector(selectAllResults),
omrrStatus: useSelector(selectOmrrStatus),
allApplications: useSelector(selectAllApplications),
applicationStatus: useSelector(selectApplicationStatus),
allDocuments: useSelector(selectAllDocuments),
documentStatus: useDocumentsStatus(),
})
return <App />
}
render(<TestComponent />, {
withStateProvider: true,
withApiData: true,
})

await screen.findByText(
'Find an authorized compost and biosolid facility in British Columbia',
)
// Ensure state is set properly
expect(state.allResults).toBeDefined()
expect(state.allResults).toHaveLength(mockOmrrData.length)
expect(state.omrrStatus).toBe('succeeded')

expect(state.allApplications).toBeDefined()
expect(state.allApplications).toHaveLength(
mockOmrrApplicationStatusResponse.length,
)
expect(state.applicationStatus).toBe('succeeded')

expect(state.allDocuments).toBeDefined()
expect(state.allDocuments).toHaveLength(mockOmrrDocuments.length)
expect(state.documentStatus).toBe('succeeded')
})
})
15 changes: 11 additions & 4 deletions frontend/src/app/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,18 @@ import { combineReducers, configureStore } from '@reduxjs/toolkit'
import { fetchOMRRData, omrrSlice } from '@/features/omrr/omrr-slice'
import { mapSlice } from '@/features/map/map-slice'
import {
applicationStatusSlice,
applicationsSlice,
fetchOmrrApplicationStatus,
} from '@/features/omrr/application-status-slice'
} from '@/features/omrr/applications-slice'
import {
documentsSlice,
fetchOmrrDocuments,
} from '@/features/omrr/documents-slice'

const rootReducer = combineReducers({
omrr: omrrSlice.reducer,
applicationStatus: applicationStatusSlice.reducer,
applications: applicationsSlice.reducer,
documents: documentsSlice.reducer,
map: mapSlice.reducer,
})

Expand All @@ -20,11 +25,13 @@ export function setupStore(preloadedState?: Partial<RootState>) {
}

export const store = setupStore()
type StoreType = typeof store

// Load initial data from the API
export function loadApiData() {
export function loadApiData(store: StoreType) {
store.dispatch(fetchOMRRData())
store.dispatch(fetchOmrrApplicationStatus())
store.dispatch(fetchOmrrDocuments())
}

// Infer the `RootState` and `AppDispatch` types from the store itself
Expand Down
5 changes: 4 additions & 1 deletion frontend/src/components/AppError.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { useSelector } from 'react-redux'
import { Snackbar } from '@mui/material'
import { useTheme } from '@mui/material/styles'

import { isTest } from '@/constants/env'
import { selectError } from '@/features/omrr/omrr-slice'

export default function AppError() {
Expand All @@ -11,7 +12,9 @@ export default function AppError() {
backgroundColor: theme.palette.error.main,
color: theme.palette.secondary.main,
}
console.info('App error:', error)
if (!isTest) {
console.info('App error:', error)
}

return (
<Snackbar
Expand Down
25 changes: 25 additions & 0 deletions frontend/src/components/AuthorizationStatusChip.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
div.authorization-status-chip {
color: #ffffff;
background-color: var(--icons-color-disabled);
display: inline-flex;
justify-content: center;
align-items: center;
height: 40px;
padding: 0 var(--layout-padding-large);
font-size: 16px;
border-radius: 24px;
}

.authorization-status-chip .MuiChip-label {
padding: 0;
}

div.authorization-status-chip--small {
height: 24px;
font-size: 14px;
padding: 0 var(--layout-padding-medium);
}

div.authorization-status-chip--active {
background-color: var(--support-border-color-success);
}
20 changes: 9 additions & 11 deletions frontend/src/components/AuthorizationStatusChip.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { Chip } from '@mui/material'
import clsx from 'clsx'

import './AuthorizationStatusChip.css'

interface Props {
status: string
Expand All @@ -12,18 +15,13 @@ export function AuthorizationStatusChip({
const isActive = status === 'Active'
return (
<Chip
sx={{
color: '#ffffff',
backgroundColor: isActive ? '#42814a' : '#9f9d9c',
display: 'inline-flex',
height: size === 'small' ? '24px' : '40px',
fontSize: size === 'small' ? '14px' : '16px',
padding: '0 12px',
borderRadius: '24px',
justifyContent: 'center',
alignItems: 'center',
}}
className={clsx(
'authorization-status-chip',
`authorization-status-chip--${size}`,
isActive && 'authorization-status-chip--active',
)}
label={status}
size={size}
/>
)
}
1 change: 0 additions & 1 deletion frontend/src/components/DataLayersCheckboxGroup.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import React from 'react'
import { useDispatch } from 'react-redux'
import { Button, Stack, Typography } from '@mui/material'
import clsx from 'clsx'
Expand Down
1 change: 0 additions & 1 deletion frontend/src/components/DropdownButton.test.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import React from 'react'
import { screen } from '@testing-library/react'

import { render } from '@/test-utils'
Expand Down
1 change: 0 additions & 1 deletion frontend/src/components/FilterByCheckboxGroup.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import React from 'react'
import { useDispatch, useSelector } from 'react-redux'
import {
Button,
Expand Down
1 change: 0 additions & 1 deletion frontend/src/components/HorizontalScroller.test.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import React from 'react'
import { screen } from '@testing-library/react'

import { render } from '@/test-utils'
Expand Down
1 change: 0 additions & 1 deletion frontend/src/components/LocationIconButton.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import React from 'react'
import { useDispatch } from 'react-redux'
import { IconButton } from '@mui/material'

Expand Down
1 change: 0 additions & 1 deletion frontend/src/components/NoResults.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import React from 'react'
import { useDispatch } from 'react-redux'
import { Button, Stack, Typography } from '@mui/material'

Expand Down
2 changes: 1 addition & 1 deletion frontend/src/constants/data-layers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ export const DATA_LAYER_GROUPS: DataLayerGroup[] = [
name: 'PMBC Parcel Cadastre',
url: `${BASE_URL}/WHSE_CADASTRE.PMBC_PARCEL_FABRIC_POLY_SVW/ows`,
layers: 'pub:WHSE_CADASTRE.PMBC_PARCEL_FABRIC_POLY_SVW',
styles: '5898',
styles: '5899', // Provincial Crown Lands
},
],
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,6 @@ import { OmrrApplicationStatus } from '@/interfaces/omrr-application-status'
import apiService from '@/service/api-service'
import { truncateDate } from '@/utils/utils'

export interface ApplicationStatusSliceState {
status: LoadingStatusType
error?: string
allApplications: OmrrApplicationStatus[]
}

export const fetchOmrrApplicationStatus = createAsyncThunk(
'data/fetchOmrrApplicationStatus',
async () => {
Expand All @@ -28,19 +22,23 @@ export const fetchOmrrApplicationStatus = createAsyncThunk(
},
)

export const initialState: ApplicationStatusSliceState = {
export interface ApplicationsSliceState {
status: LoadingStatusType
error?: string
allApplications: OmrrApplicationStatus[]
}

export const initialState: ApplicationsSliceState = {
status: 'idle',
error: undefined,
allApplications: [],
}

export const applicationStatusSlice = createSlice({
name: 'applicationStatus',
export const applicationsSlice = createSlice({
name: 'applications',
initialState,
reducers: {},
extraReducers: (
builder: ActionReducerMapBuilder<ApplicationStatusSliceState>,
) => {
extraReducers: (builder: ActionReducerMapBuilder<ApplicationsSliceState>) => {
builder.addCase(fetchOmrrApplicationStatus.pending, (state) => {
state.status = 'loading'
})
Expand Down Expand Up @@ -71,15 +69,11 @@ export const applicationStatusSlice = createSlice({
},
})

// export const {} = applicationStatusSlice.actions

// export default applicationStatusSlice.reducer

// Selectors
//export const selectStatus = (state: RootState) => state.applicationStatus.status
export const selectStatus = (state: RootState) => state.applications.status
//export const selectError = (state: RootState) => state.applicationStatus.error
export const selectAllApplications = (state: RootState) =>
state.applicationStatus.allApplications
state.applications.allApplications

export const useFindApplicationStatus = (
authorizationNumber: number,
Expand Down
93 changes: 93 additions & 0 deletions frontend/src/features/omrr/documents-slice.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { useSelector } from 'react-redux'
import {
ActionReducerMapBuilder,
createAsyncThunk,
createSlice,
PayloadAction,
} from '@reduxjs/toolkit'

import { RootState } from '@/app/store'
import { LoadingStatusType } from '@/interfaces/loading-status'
import apiService from '@/service/api-service'
import {
OmrrAuthzDocs,
OmrrAuthzDocsResponse,
} from '@/interfaces/omrr-documents'

export const fetchOmrrDocuments = createAsyncThunk(
'data/fetchOmrrDocuments',
async () => {
const result = await apiService
.getAxiosInstance()
.get('/omrr/authorization-docs')
return result?.data
},
)

export interface DocumentsSliceState {
status: LoadingStatusType
error?: string
allDocuments: OmrrAuthzDocsResponse[]
}

export const initialState: DocumentsSliceState = {
status: 'idle',
error: undefined,
allDocuments: [],
}

export const documentsSlice = createSlice({
name: 'documents',
initialState,
reducers: {},
extraReducers: (builder: ActionReducerMapBuilder<DocumentsSliceState>) => {
builder.addCase(fetchOmrrDocuments.pending, (state) => {
state.status = 'loading'
})
builder.addCase(
fetchOmrrDocuments.fulfilled,
(state, action: PayloadAction<OmrrAuthzDocsResponse[]>) => {
const data: OmrrAuthzDocsResponse[] = action.payload
if (Array.isArray(data) && data.length > 0) {
state.status = 'succeeded'
state.allDocuments = data
} else {
state.status = 'failed'
state.error = 'No data found'
console.log('No authorization documents loaded.')
}
},
)
builder.addCase(fetchOmrrDocuments.rejected, (state, action) => {
state.status = 'failed'
state.error = action.error.message
console.error(
'Failed to load authorization documents',
action.error.message,
)
})
},
})

// Selectors
export const selectStatus = (state: RootState) => state.documents.status
export const useDocumentsStatus = () => useSelector(selectStatus)

export const selectAllDocuments = (state: RootState) =>
state.documents.allDocuments

export const useFindAuthorizationDocuments = (
authorizationNumber: number,
): OmrrAuthzDocs[] | undefined => {
const allDocuments = useSelector(selectAllDocuments)
if (authorizationNumber > 0) {
const response = allDocuments.find(
(response: OmrrAuthzDocsResponse) =>
response['Authorization Number'] === authorizationNumber,
)
if (response) {
return response.doc_links
}
}
return undefined
}
Loading

0 comments on commit 6f4845d

Please sign in to comment.