Skip to content

Commit

Permalink
#1215 add filter by publication state to official board (#1216)
Browse files Browse the repository at this point in the history
* add filter by publication state to official board

* add isProductionDeployment check to prevent accidental early release

* add comments
  • Loading branch information
radoslavzeman authored Mar 26, 2024
1 parent 8f4d692 commit 2e83cff
Show file tree
Hide file tree
Showing 9 changed files with 117 additions and 26 deletions.
9 changes: 6 additions & 3 deletions next/backend/ginis/fetchers/officialBoardListFetcher.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
import { OfficialBoardListResponse } from '@backend/ginis/types'
import { OfficialBoardListResponse, OfficialBoardPublicationState } from '@backend/ginis/types'
import axios from 'axios'

export type OfficialBoardListFilters = {
search: string
pageSize: number
page: number
categoryId?: string
publicationState?: OfficialBoardPublicationState
}

export const officialBoardListDefaultFilters: OfficialBoardListFilters = {
export const officialBoardListDefaultFilters = {
search: '',
pageSize: 10,
page: 1,
}
publicationState: 'vyveseno',
} satisfies OfficialBoardListFilters

export const getOfficialBoardListQueryKey = (filters: OfficialBoardListFilters) => [
'Search',
Expand All @@ -26,6 +28,7 @@ export const officialBoardListFetcher = async (filters: OfficialBoardListFilters
filters.search ? `search=${filters.search}` : '',
filters.pageSize ? `pageSize=${filters.pageSize.toString()}` : '',
filters.page ? `page=${filters.page.toString()}` : '',
filters.publicationState ? `publicationState=${filters.publicationState}` : '',
filters.categoryId ? `categoryId=${filters.categoryId}` : '',
]
.filter(Boolean)
Expand Down
14 changes: 12 additions & 2 deletions next/backend/ginis/server/getOfficialBoardParsedList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,22 @@ import { ParsedOfficialBoardDocument } from '@backend/ginis/types'
import { SeznamDokumentuResponseItem } from '@ginis-sdk/api/json/ude/seznam-dokumentu'
import { isDefined } from '@utils/isDefined'

export const getOfficialBoardParsedList = async (searchQuery?: string, categoryId?: string) => {
export const getOfficialBoardParsedList = async (options: {
searchQuery?: string
publicationState?: string
categoryId?: string
}) => {
const { searchQuery, publicationState, categoryId } = options

let documents: SeznamDokumentuResponseItem[] = []

// Nazev - Max. délka: 254
const searchQueryTrimmed = searchQuery?.trim().slice(0, 254) ?? ''

// Stav?: 'vyveseno' | 'sejmuto'
const publicationStateSanitized =
publicationState === 'vyveseno' || publicationState === 'sejmuto' ? publicationState : undefined

try {
/**
* The `bodyObj` uses same keys as the requests in Ginis docs
Expand All @@ -19,7 +29,7 @@ export const getOfficialBoardParsedList = async (searchQuery?: string, categoryI
* Otherwise, it will throw 400 Bad Request error.
*/
const dataXrg = await ginis.json.ude.seznamDokumentu({
Stav: 'vyveseno',
Stav: publicationStateSanitized,
IdKategorie: categoryId,
Nazev: searchQueryTrimmed,
})
Expand Down
5 changes: 5 additions & 0 deletions next/backend/ginis/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { SeznamDokumentuRequestBody } from '@ginis-sdk/api/json/ude/seznam-dokumentu'

export type ParsedOfficialBoardDocument = {
id: string
title: string
Expand Down Expand Up @@ -35,3 +37,6 @@ export type ParsedOfficialBoardCategory = {
}

export type OfficialBoardCategoryListResponse = ParsedOfficialBoardCategory[]

// NonNullable removes `undefined` from the type
export type OfficialBoardPublicationState = NonNullable<SeznamDokumentuRequestBody['Stav']>
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,32 @@ import {
getOfficialBoardCategoriesQueryKey,
officialBoardCategoriesFetcher,
} from '@backend/ginis/fetchers/officialBoardCategoriesFetcher'
import { ParsedOfficialBoardCategory } from '@backend/ginis/types'
import { OfficialBoardPublicationState, ParsedOfficialBoardCategory } from '@backend/ginis/types'
import SelectField, {
SelectItem,
} from '@components/forms/widget-components/SelectField/SelectField'
import { useQuery } from '@tanstack/react-query'
import { isDefined } from '@utils/isDefined'
import { isProductionDeployment } from '@utils/utils'
import { useTranslations } from 'next-intl'
import React from 'react'

// TODO maybe we shouldn't use czech string for values, but parse them in handler or somewhere else?
type Props = {
categoryId: string | null
setCategoryId: (categoryId: string | null) => void
publicationState: OfficialBoardPublicationState
setPublicationState: React.Dispatch<React.SetStateAction<OfficialBoardPublicationState>>
}

const OfficialBoardAdditionalFilters = ({ categoryId, setCategoryId }: Props) => {
const OfficialBoardAdditionalFilters = ({
categoryId,
setCategoryId,
publicationState,
setPublicationState,
}: Props) => {
const t = useTranslations('OfficialBoard')

// TODO handle loading and error
const {
data: officialBoardCategories,
Expand All @@ -28,6 +40,11 @@ const OfficialBoardAdditionalFilters = ({ categoryId, setCategoryId }: Props) =>
select: (res) => res.data,
})

/**
* This approach makes 'numberOfPostedDocuments' and 'numberOfArchivedDocuments' optional,
* while keeping the same props as used in ParsedOfficialBoardCategory.
* It is needed for "all" option, which doesn't have these values.
*/
const categorySelectOptions: (Omit<
ParsedOfficialBoardCategory,
'numberOfPostedDocuments' | 'numberOfArchivedDocuments'
Expand All @@ -37,27 +54,63 @@ const OfficialBoardAdditionalFilters = ({ categoryId, setCategoryId }: Props) =>
>)[] = [
{
id: 'all',
title: 'Všetky',
title: t('allOptions'),
} as const,
...(officialBoardCategories ?? []),
]

const publicationStateSelectOptions: {
id: OfficialBoardPublicationState
title: string
}[] = [
{
id: 'vyveseno',
title: t('published'),
},
{
id: 'sejmuto',
title: t('archived'),
},
]

return (
<SelectField
label="Kategória"
items={categorySelectOptions}
selectedKey={categoryId}
onSelectionChange={(selected) => setCategoryId(selected as string | null | 'all')}
>
{(item) => (
<SelectItem
label={`${item.title} ${
isDefined(item.numberOfPostedDocuments) ? ` [${item.numberOfPostedDocuments}]` : ''
}`}
id={item.id}
/>
<div className="grid grid-cols-1 gap-4 lg:grid-cols-2">
<SelectField
label={t('category')}
selectedKey={categoryId}
onSelectionChange={(selected) => setCategoryId(selected as string | null | 'all')}
>
{/* We use .map instead of dynamic `items` to be able to show number of documents depending on publication state */}
{categorySelectOptions.map((item) => {
const numberOfDocuments =
publicationState === 'vyveseno'
? item.numberOfPostedDocuments
: item.numberOfArchivedDocuments

return (
<SelectItem
id={item.id}
textValue={item.title}
label={`${item.title}${
isDefined(numberOfDocuments) ? ` [${numberOfDocuments}]` : ''
}`}
/>
)
})}
</SelectField>

{/* TODO remove this check, but for now, we want to test in on staging without being block by accidental release */}
{isProductionDeployment() ? null : (
<SelectField
label={t('publicationState')}
items={publicationStateSelectOptions}
selectedKey={publicationState}
onSelectionChange={(selected) => setPublicationState(selected as typeof publicationState)}
>
{(item) => <SelectItem label={item.title} id={item.id} />}
</SelectField>
)}
</SelectField>
</div>
)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { officialBoardListDefaultFilters } from '@backend/ginis/fetchers/officialBoardListFetcher'
import { OfficialBoardPublicationState } from '@backend/ginis/types'
import { Typography } from '@bratislava/component-library'
import Chip from '@components/forms/simple-components/Chip'
import OfficialBoardAdditionalFilters from '@components/molecules/sections/general/OfficialBoardSection/OfficialBoardAdditionalFilters'
Expand Down Expand Up @@ -116,7 +118,11 @@ const GlobalSearchSectionContent = ({ variant, searchOption }: Props) => {
setCurrentPage(1)
}, [searchValue, selection])

/* OfficialBoard specific filters state */
const [categoryId, setCategoryId] = useState<string | null>(null)
const [publicationState, setPublicationState] = useState<OfficialBoardPublicationState>(
officialBoardListDefaultFilters.publicationState,
)

const [resultsCount, setResultsCount] = useState(
Object.fromEntries(searchOptions.map((option): [string, number] => [option.id, 0])),
Expand Down Expand Up @@ -168,6 +174,7 @@ const GlobalSearchSectionContent = ({ variant, searchOption }: Props) => {
tagIds: [],
// Official board category id
categoryId: !categoryId || categoryId === 'all' ? undefined : categoryId,
publicationState: publicationState ?? undefined,
}

const searchRef = useRef<null | HTMLInputElement>(null)
Expand Down Expand Up @@ -225,6 +232,8 @@ const GlobalSearchSectionContent = ({ variant, searchOption }: Props) => {
<OfficialBoardAdditionalFilters
categoryId={categoryId}
setCategoryId={setCategoryId}
publicationState={publicationState}
setPublicationState={setPublicationState}
/>
</div>
</div>
Expand Down
2 changes: 1 addition & 1 deletion next/ginis-sdk/api/json/ude/seznam-dokumentu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export type SeznamDokumentuRequestBody = {
* vyveseno - vyvěšeno
* sejmuto - sejmuto
*/
Stav?: string
Stav?: 'vyveseno' | 'sejmuto'
IdUredniDesky?: string
IdKategorie?: string
/**
Expand Down
6 changes: 5 additions & 1 deletion next/messages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -318,9 +318,13 @@
"publishedFrom": "Published from",
"publishedTo": "Published to",
"category": "Category",
"allOptions": "All",
"publicationState": "Publication state",
"description": "Description",
"attachments": "Attachments",
"details": "Details",
"noAttachmentsMessage": "We didn't find any attachments to this notice."
"noAttachmentsMessage": "We didn't find any attachments to this notice.",
"published": "Published",
"archived": "Archived"
}
}
6 changes: 5 additions & 1 deletion next/messages/sk.json
Original file line number Diff line number Diff line change
Expand Up @@ -319,9 +319,13 @@
"publishedFrom": "Zverejnené od",
"publishedTo": "Zverejnené do",
"category": "Kategória",
"allOptions": "Všetky",
"publicationState": "Stav zverejnenia",
"description": "Popis",
"attachments": "Prílohy",
"details": "Podrobnosti",
"noAttachmentsMessage": "K tomuto dokumentu sa nepodarilo nájsť žiadne prílohy."
"noAttachmentsMessage": "K tomuto dokumentu sa nepodarilo nájsť žiadne prílohy.",
"published": "Zverejnené",
"archived": "Archivované"
}
}
5 changes: 4 additions & 1 deletion next/pages/api/ginis/official-board-list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const handler = async (
search: searchParam,
pageSize: pageSizeParam,
page: pageParam,
publicationState: publicationStateParam,
categoryId: categoryIdParam,
} = req.query

Expand All @@ -27,6 +28,8 @@ const handler = async (
: officialBoardListDefaultFilters.pageSize
const page =
typeof pageParam === 'string' ? parseInt(pageParam, 10) : officialBoardListDefaultFilters.page
const publicationState =
typeof publicationStateParam === 'string' ? publicationStateParam : publicationStateParam?.[0]
const categoryId =
typeof categoryIdParam === 'string' ? categoryIdParam : categoryIdParam?.[0] ?? ''

Expand All @@ -35,7 +38,7 @@ const handler = async (
try {
result = shouldMockGinis()
? mockedParsedDocuments
: await getOfficialBoardParsedList(search, categoryId)
: await getOfficialBoardParsedList({ searchQuery: search, publicationState, categoryId })
} catch (error) {
// TODO handle error
console.log(error)
Expand Down

0 comments on commit 2e83cff

Please sign in to comment.