Skip to content

Commit

Permalink
Merge pull request #46 from uktrade/ORPD-100-consume-drf-endpoint-in-…
Browse files Browse the repository at this point in the history
…react

feature/ORPD-100 - Consume Django Rest Framework endpoint in React
  • Loading branch information
gdbarnes authored Nov 19, 2024
2 parents b3ee544 + 81550bf commit 591b097
Show file tree
Hide file tree
Showing 10 changed files with 191 additions and 107 deletions.
5 changes: 5 additions & 0 deletions front_end/stylesheets/helpers.scss
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,8 @@
text-decoration: underline;
}
}

.orp-marked-text {
font-weight: bold;
background-color: inherit;
}
2 changes: 1 addition & 1 deletion orp/orp_search/public_gateway.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ def __init__(self):
"""
self._base_url = (
"https://data.api.trade.gov.uk/v1/datasets/orp-regulations"
"/versions/v1.0.0/data"
"/versions/v1.0.1/data"
)

def build_cache(self, config):
Expand Down
24 changes: 12 additions & 12 deletions orp/orp_search/utils/paginate.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,18 +34,18 @@ def paginate(
start_time = time.time()

for paginated_document in paginated_documents:
if hasattr(paginated_document, "description"):
description = paginated_document.description
if description:
paginated_document.description = (
(
description[:100] + "..."
if len(description) > 100
else description
)
.lstrip(".")
.capitalize()
)
# if hasattr(paginated_document, "description"):
# description = paginated_document.description
# if description:
# paginated_document.description = (
# (
# description[:100] + "..."
# if len(description) > 100
# else description
# )
# .lstrip(".")
# .capitalize()
# )
if hasattr(paginated_document, "regulatory_topics"):
regulatory_topics = paginated_document.regulatory_topics
if regulatory_topics:
Expand Down
24 changes: 14 additions & 10 deletions react_front_end/src/App.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useState, useEffect, useMemo, useCallback } from "react"
import { useQueryParams } from "./hooks/useQueryParams"
import { fetchData } from "./utils/fetch-ords"
import { fetchData } from "./utils/fetch-drf"
import { DOCUMENT_TYPES, PUBLISHERS } from "./utils/constants"

import { Search } from "./components/Search"
Expand All @@ -24,9 +24,6 @@ function App() {
const [data, setData] = useState([])
const [isLoading, setIsLoading] = useState(true)

// Get results count and memoize it
const resultsCount = useMemo(() => data.length, [data])

// Memoize the initial checked state for document types and publishers
const initialDocumentTypeCheckedState = useMemo(
() => generateCheckedState(DOCUMENT_TYPES, docTypeQuery),
Expand Down Expand Up @@ -82,16 +79,18 @@ function App() {

const handleSearchSubmit = useCallback(() => {
setSearchQuery([searchInput])
// setPageQuery([1])

const filterParams = {
...(searchInput.length > 0 && { search: searchInput }),
...(docTypeQuery.length > 0 && { document_type: docTypeQuery }),
...(publisherQuery.length > 0 && { publisher: publisherQuery }),
sort: sortQuery.join(","),
page: pageQuery.join(","),
page: 1,
}

fetchDataWithLoading(filterParams)
}, [searchInput, docTypeQuery, publisherQuery, sortQuery, pageQuery])
}, [searchInput, docTypeQuery, publisherQuery, sortQuery])

useEffect(() => {
const handler = setTimeout(() => {
Expand Down Expand Up @@ -178,7 +177,12 @@ function App() {
</div>
<div className="govuk-grid-column-two-thirds">
<div className="orp-flex orp-flex--space-between">
<ResultsCount totalResults={resultsCount} isLoading={isLoading} />
<ResultsCount
isLoading={isLoading}
start={data.start_index}
end={data.end_index}
totalResults={data.results_total_count}
/>
<p className="govuk-body govuk-!-margin-bottom-0">
<a
href=""
Expand All @@ -201,13 +205,13 @@ function App() {
)}
<SortSelect sortQuery={sortQuery} setSortQuery={setSortQuery} />
<hr className="govuk-section-break govuk-section-break--m govuk-section-break--visible" />
{data.length === 0 && !isLoading ? (
{data.results_total_count === 0 && !isLoading ? (
<NoResultsContent />
) : (
<Results results={data} isLoading={isLoading} searchQuery={searchQuery} />
<Results results={data.results} isLoading={isLoading} searchQuery={searchQuery} />
)}

<Pagination pageQuery={pageQuery} setPageQuery={setPageQuery} />
<Pagination pageData={data} pageQuery={pageQuery} setPageQuery={setPageQuery} />
</div>
</div>
)
Expand Down
104 changes: 63 additions & 41 deletions react_front_end/src/components/Pagination.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
function Pagination({ pageQuery, setPageQuery }) {
function Pagination({ pageData, pageQuery, setPageQuery }) {
const handlePreviousPage = (e) => {
e.preventDefault()
setPageQuery([parseInt(pageQuery[0]) - 1])
Expand All @@ -9,34 +9,23 @@ function Pagination({ pageQuery, setPageQuery }) {
setPageQuery([parseInt(pageQuery[0]) + 1])
}

// Data I will need fot the pagination
// results.has_previous
// results.previous_page_number
// results.paginator.page_range
// results.number
// results.paginator.num_pages
// results.has_next
// results.next_page_number
const results = {
has_previous: true,
previous_page_number: 1,
paginator: {
page_range: [1, 2, 3, 4, 5],
num_pages: 5,
},
number: 1,
has_next: true,
next_page_number: 2,
}
const { current_page, is_paginated, results_page_total } = pageData

// Generate page range array from results_page_total
const page_range = Array.from({ length: results_page_total }, (_, i) => i + 1)

return (
const isPreviousPage = current_page - 1 >= 1
const isNextPage = current_page + 1 <= results_page_total

return is_paginated ? (
<nav className="govuk-pagination" role="navigation" aria-label="Pagination">
{results.has_previous && (
{isPreviousPage ? (
<div className="govuk-pagination__prev">
<a
className="govuk-link govuk-pagination__link govuk-link--no-visited-state"
href="#"
onClick={handlePreviousPage}
role="button"
aria-label="Previous page"
>
<svg
Expand All @@ -55,25 +44,58 @@ function Pagination({ pageQuery, setPageQuery }) {
</span>
</a>
</div>
)}
) : null}
<ul className="govuk-pagination__list">
{results.paginator.page_range.map((page_number) => (
<li key={page_number} className="govuk-pagination__item">
<a
className={`govuk-link govuk-pagination__link ${results.number === page_number ? "govuk-pagination__link--current" : ""}`}
href="#"
onClick={(e) => {
e.preventDefault()
setPageQuery([page_number])
}}
aria-current={results.number === page_number ? "page" : undefined}
>
{page_number}
</a>
</li>
))}
{page_range.map((page_number) => {
if (page_number === current_page) {
return (
<li key={page_number} className="govuk-pagination__item govuk-pagination__item--current">
<a
className="govuk-link govuk-pagination__link govuk-link--no-visited-state"
href="#"
onClick={(e) => {
e.preventDefault()
setPageQuery([page_number])
}}
role="button"
aria-current={current_page === page_number ? "page" : undefined}
>
{page_number}
</a>
</li>
)
} else if (
page_number === 1 ||
page_number === results_page_total ||
page_number === current_page - 1 ||
page_number === current_page + 1
) {
return (
<li key={page_number} className="govuk-pagination__item">
<a
className="govuk-link govuk-pagination__link"
href="#"
onClick={(e) => {
e.preventDefault()
setPageQuery([page_number])
}}
role="button"
aria-label="Next page"
>
{page_number}
</a>
</li>
)
} else if (page_number === current_page - 2 || page_number === current_page + 2) {
return (
<li key={page_number} className="govuk-pagination__item govuk-pagination__item--ellipses">
&hellip;
</li>
)
}
})}
</ul>
{results.has_next && (
{isNextPage ? (
<div className="govuk-pagination__next">
<a
className="govuk-link govuk-pagination__link govuk-link--no-visited-state"
Expand All @@ -97,9 +119,9 @@ function Pagination({ pageQuery, setPageQuery }) {
</svg>
</a>
</div>
)}
) : null}
</nav>
)
) : null
}

export { Pagination }
84 changes: 50 additions & 34 deletions react_front_end/src/components/Results.js
Original file line number Diff line number Diff line change
@@ -1,79 +1,95 @@
import { SkeletonResults } from "./SkeletonResults"

function Results({ results, isLoading, searchQuery }) {
// console.log("Results", results)

if (isLoading) {
return <SkeletonResults />
}

const highlight = (text) => {
const searchWords = searchQuery.join("|")
const regex = new RegExp(searchWords, "gi")
const highlightedText = text.replace(regex, (match) => `<mark>${match}</mark>`)
const highlightedText = text.replace(regex, (match) => `<mark class="orp-marked-text">${match}</mark>`)
return { __html: highlightedText }
}

const truncateDescription = (description, matchIndex) => {
if (matchIndex === -1 || matchIndex <= 200) {
return description.length > 200 ? description.substring(0, 200) + "..." : description
} else {
const start = 0 // Always start from the beginning
const end = Math.min(description.length, matchIndex + 150) // Include some context after the match
return description.substring(start, end) + "..."
}
}

// This needs to be in a utils file
const formatDate = (dateString) => {
const date = new Date(dateString)
return date.toLocaleDateString("en-GB", {
day: "numeric",
month: "long",
year: "numeric",
})
}

const renderRegulatoryTopics = (topics, searchQuery) => {
return topics.map((topic, index) => {
const highlightedTopic = topic.toLowerCase().includes(searchQuery[0].toLowerCase()) ? (
<span dangerouslySetInnerHTML={highlight(topic)} />
) : (
topic
)

return (
<li key={index} className="govuk-body-s orp-secondary-text-colour">
{highlightedTopic}
</li>
)
})
}

return results ? (
<div className="govuk-summary-list orp-search-results">
{results.slice(0, 10).map((result) => {
const { id, type, title, description, publisher, date_modified, regulatory_topics } = result.c
{results.map((result) => {
const { id, type, title, description, publisher, date_modified, regulatory_topics } = result

// Check if the search term appears within the first 200 characters
const searchWords = searchQuery.join("|")
const regex = new RegExp(searchWords, "gi")
const matchIndex = description.search(regex)

let concatDescription
if (matchIndex === -1 || matchIndex <= 200) {
concatDescription = description.length > 200 ? description.substring(0, 200) + "..." : description
} else {
// Adjust the substring to include the highlighted text without cutting off the start
const start = 0 // Always start from the beginning
const end = Math.min(description.length, matchIndex + 150) // Include some context after the match
concatDescription = description.substring(start, end) + "..."
}

const highlightedTitle = title ? <span dangerouslySetInnerHTML={highlight(title)} /> : ""
const truncatedDescription = truncateDescription(description, matchIndex)
// const highlightedTitle = title ? <span dangerouslySetInnerHTML={highlight(title)} /> : ""
const highlightedDescription = description ? (
<span dangerouslySetInnerHTML={highlight(concatDescription)} />
<span dangerouslySetInnerHTML={highlight(truncatedDescription)} />
) : (
""
)

const date = new Date(date_modified)
const govukDate = date.toLocaleDateString("en-GB", {
day: "numeric",
month: "long",
year: "numeric",
})

const regulatory_topics_array = regulatory_topics.split("\n")
const govukDate = formatDate(date_modified)

return (
<div className="govuk-summary-list__row--no-border" key={id}>
<span className="govuk-caption-m">{type}</span>
<h2 className="govuk-heading-m">
<a href={`/document/${id}`} className="govuk-link">
{highlightedTitle}
{title}
</a>
</h2>
<p className="govuk-body">{highlightedDescription}</p>
<p className="govuk-body-s orp-secondary-text-colour govuk-!-margin-bottom-2">Published by: {publisher}</p>
<p className="govuk-body-s orp-secondary-text-colour">Last updated: {govukDate}</p>
<ul className="govuk-list orp-topics-list">
{regulatory_topics_array.map((regulatory_topic, index) => (
<li key={index} className="govuk-body-s orp-secondary-text-colour">
{regulatory_topic}
</li>
))}
</ul>
{regulatory_topics && regulatory_topics.length > 0 ? (
<ul className="govuk-list orp-topics-list">{renderRegulatoryTopics(regulatory_topics, searchQuery)}</ul>
) : null}
<hr className="govuk-section-break govuk-section-break--l govuk-section-break--visible" />
</div>
)
})}
</div>
) : (
<p>No results found</p>
)
) : null
}

export { Results }
4 changes: 2 additions & 2 deletions react_front_end/src/components/ResultsCount.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
function ResultsCount({ totalResults, isLoading }) {
function ResultsCount({ isLoading, start, end, totalResults }) {
return (
<p className="govuk-body govuk-!-font-weight-bold govuk-!-margin-bottom-0">
{isLoading ? "Loading..." : totalResults ? `${totalResults} results` : "No results found"}
{isLoading ? "Loading..." : totalResults ? `${start} to ${end} of ${totalResults} results` : "No results found"}
</p>
)
}
Expand Down
Loading

0 comments on commit 591b097

Please sign in to comment.