From 0c40b9dbbc6746d5157b54bd5563be0eb5975f8c Mon Sep 17 00:00:00 2001 From: victor barbier <victor.barbier@recherche.gouv.fr> Date: Mon, 9 Sep 2024 12:12:57 +0200 Subject: [PATCH] feat(client): rework --- .../src/components/results/debug/index.tsx | 37 +++++++++ .../components/results/highlights/index.tsx | 81 +++++++------------ .../client/src/components/results/index.tsx | 21 +++-- .../src/components/results/result/index.tsx | 11 +-- .../components/results/utils/highlights.tsx | 38 +-------- .../src/components/results/utils/url.tsx | 2 +- project/client/src/styles/index.scss | 9 +++ project/client/src/types/data.ts | 30 ------- project/client/src/types/functions.ts | 16 ---- project/client/src/types/index.ts | 43 ++++++++++ 10 files changed, 138 insertions(+), 150 deletions(-) create mode 100644 project/client/src/components/results/debug/index.tsx delete mode 100644 project/client/src/types/data.ts delete mode 100644 project/client/src/types/functions.ts create mode 100644 project/client/src/types/index.ts diff --git a/project/client/src/components/results/debug/index.tsx b/project/client/src/components/results/debug/index.tsx new file mode 100644 index 0000000..fd14de2 --- /dev/null +++ b/project/client/src/components/results/debug/index.tsx @@ -0,0 +1,37 @@ +import { Container, Badge, BadgeGroup, Accordion } from "@dataesr/dsfr-plus" +import { MatchDebug } from "../../../types" + +type ResultsDebugArgs = { + resultsDebug: MatchDebug +} +export default function ResultsDebug({ resultsDebug }: ResultsDebugArgs) { + console.log("resultsDebug", resultsDebug) + + if (!resultsDebug) return null + + const criterionMatches = (criterion: string) => resultsDebug.criterion?.[criterion] ?? 0 + + return ( + <Accordion className="fr-container fr-mt-3w" title="See more"> + {resultsDebug.strategies.map((strategy, index) => ( + <Container key={index} className="debug-item"> + {strategy.equivalent_strategies.map((equivalent) => ( + <BadgeGroup> + {equivalent.map((criterion) => { + const matches: number = criterionMatches(criterion) + return ( + <Badge color={matches ? "yellow-moutarde" : null}>{`${criterion}: ${matches} match${ + matches == 1 ? "" : "es" + }`}</Badge> + ) + })} + </BadgeGroup> + ))} + <Badge color={strategy.matches ? "success" : "error"}>{`${strategy.matches} ${ + strategy.matches == 1 ? "possibility" : "possibilities" + }`}</Badge> + </Container> + ))} + </Accordion> + ) +} diff --git a/project/client/src/components/results/highlights/index.tsx b/project/client/src/components/results/highlights/index.tsx index 8a471f2..7b66133 100644 --- a/project/client/src/components/results/highlights/index.tsx +++ b/project/client/src/components/results/highlights/index.tsx @@ -1,65 +1,40 @@ import { IntlMessageFormat } from "intl-messageformat" -import { Container, Badge, Text, Row } from "@dataesr/dsfr-plus" -import { CriterionHighlightsArgs, ResultHighlightsArgs, StrategyHighlightsArgs } from "../../../types/functions" -import { getHighlightedText, getHighlightedQuery, strategyCriteria } from "../utils/highlights" +import { Container, Badge, BadgeGroup } from "@dataesr/dsfr-plus" +import { getHighlightedQuery } from "../utils/highlights" import useUrl from "../../../hooks/useUrl" +import { MatchHighlight } from "../../../types" -function CriterionHighlights({ criterion, criterionHighlights, setTitle }: CriterionHighlightsArgs) { - const { currentQuery } = useUrl() +export type ResultHighlightsArgs = { + resultHighlights: MatchHighlight + setTitle: Function +} - const highlightedData = criterionHighlights.map((criterionHighlight) => { - const highlightedText = getHighlightedText(criterionHighlight[0]) - return { - highlightedCriterion: highlightedText.join(" "), - highlightedQuery: getHighlightedQuery(highlightedText, currentQuery), - } - }) +export default function ResultHighlights({ resultHighlights, setTitle }: ResultHighlightsArgs) { + const { currentQuery } = useUrl() const onEnter = (highlightedQuery: string) => - setTitle(new IntlMessageFormat(highlightedQuery).format({ b: (chunks) => <strong>{chunks}</strong> })) + setTitle( + new IntlMessageFormat(highlightedQuery).format({ + b: (chunks) => <strong key={JSON.stringify(chunks)}>{chunks}</strong>, + }) + ) const onLeave = () => setTitle(currentQuery) return ( - <Row> - <Text>{`${criterion} (${highlightedData.length} match${highlightedData.length > 1 ? "es" : ""}):`}</Text> - {highlightedData.map((data, index) => ( - <Text - key={index} - className="fr-ml-1w fr-mr-1w" - onMouseEnter={() => onEnter(data.highlightedQuery)} - onMouseLeave={() => onLeave()} - > - <strong>{data.highlightedCriterion}</strong> - </Text> - ))} - </Row> - ) -} - -function StrategyHighlights({ strategy, strategyHighlights, setTitle }: StrategyHighlightsArgs) { - const criteria = strategyCriteria(strategy) - const color = criteria.length === Object.keys(strategyHighlights).length ? "success" : "error" - - return ( - <Container> - <Badge color={color}>{`Strategy: ${criteria.join(", ")}`}</Badge> - {Object.entries(strategyHighlights).map(([criterion, criterionHighlights], index) => ( - <CriterionHighlights - key={index} - criterion={criterion} - criterionHighlights={criterionHighlights} - setTitle={setTitle} - /> - ))} - </Container> - ) -} - -export default function ResultHighlights({ resultHighlights, setTitle }: ResultHighlightsArgs) { - return ( - <Container fluid className="fr-mt-4w"> - {Object.entries(resultHighlights).map(([strategy, criteriaHighlights], index) => ( - <StrategyHighlights key={index} strategy={strategy} strategyHighlights={criteriaHighlights} setTitle={setTitle} /> + <Container fluid className="fr-mt-2w"> + {Object.entries(resultHighlights.criterion).map(([criterion, highlights], groupIndex) => ( + <BadgeGroup key={groupIndex}> + <Badge color="success">{criterion}</Badge> + {highlights.map((highlight, badgeIndex) => ( + <Badge + key={badgeIndex} + onMouseEnter={() => onEnter(getHighlightedQuery(highlight, currentQuery))} + onMouseLeave={() => onLeave()} + > + {highlight?.join(" ")} + </Badge> + ))} + </BadgeGroup> ))} </Container> ) diff --git a/project/client/src/components/results/index.tsx b/project/client/src/components/results/index.tsx index fdef0ba..3a8caa8 100644 --- a/project/client/src/components/results/index.tsx +++ b/project/client/src/components/results/index.tsx @@ -5,7 +5,8 @@ import useUrl from "../../hooks/useUrl" import Error from "../error" import Result from "./result" import Fetching from "../fetching" -import { MatchIds } from "../../types/data" +import { MatchIds } from "../../types" +import ResultsDebug from "./debug" export default function Results() { const { currentQuery, currentMatcher, currentYear } = useUrl() @@ -27,10 +28,14 @@ export default function Results() { const matchIds = data.results as MatchIds if (!matchIds.length) return ( - <Container> - <Text size="lead">{currentTitle}</Text> - <Badge color="error">{`${currentMatcher} : no results`}</Badge> - </Container> + <> + <Container className="sticky card"> + <Text size="lead">{currentTitle}</Text> + </Container> + <Container className="fr-mt-3w"> + <Badge color="error">{`${currentMatcher} : 0 match`}</Badge> + </Container> + </> ) return ( @@ -38,11 +43,15 @@ export default function Results() { <Container className="sticky card"> <Text size="lead">{currentTitle}</Text> </Container> - <Container fluid className="fr-mt-5w"> + <Container className="fr-mt-3w"> + <Text size="md">{`${matchIds.length} match${matchIds.length > 1 ? "es" : ""}`}</Text> + </Container> + <Container fluid className="fr-mt-3w"> {matchIds.map((id, index) => { return <Result key={index} resultData={data} resultId={id} setTitle={setTitle} /> })} </Container> + <ResultsDebug resultsDebug={data?.debug} /> </Container> ) } diff --git a/project/client/src/components/results/result/index.tsx b/project/client/src/components/results/result/index.tsx index 94325f8..356f783 100644 --- a/project/client/src/components/results/result/index.tsx +++ b/project/client/src/components/results/result/index.tsx @@ -1,7 +1,6 @@ -import { Container, Badge, Row, BadgeGroup, Accordion } from "@dataesr/dsfr-plus" -import { MatchId, MatchResults } from "../../../types/data" +import { Container, Badge, Row, BadgeGroup } from "@dataesr/dsfr-plus" +import { MatchId, MatchResults } from "../../../types" import useUrl from "../../../hooks/useUrl" -import { getResultHighlights } from "../utils/highlights" import { getResultUrl } from "../utils/url" import ResultHighlights from "../highlights" @@ -19,7 +18,7 @@ export default function Result({ const { results: resultsIds, enriched_results: resultsEnriched, highlights: resultsHighlights } = resultData const resultIndex = resultsIds.findIndex((element) => element === resultId) const resultEnriched = resultsEnriched[resultIndex] - const resultHighlights = getResultHighlights(resultsHighlights, resultId) + const resultHighlights = resultsHighlights?.[resultId] const resultUrl = getResultUrl(resultId, currentMatcher) return ( @@ -42,9 +41,7 @@ export default function Result({ {resultEnriched?.country?.length && <Badge color="pink-macaron">{resultEnriched.country[0]}</Badge>} </BadgeGroup> </Row> - <Accordion title="Highlights"> - <ResultHighlights resultHighlights={resultHighlights} setTitle={setTitle} /> - </Accordion> + <ResultHighlights resultHighlights={resultHighlights} setTitle={setTitle} /> </Container> ) } diff --git a/project/client/src/components/results/utils/highlights.tsx b/project/client/src/components/results/utils/highlights.tsx index 46c6358..8e61694 100644 --- a/project/client/src/components/results/utils/highlights.tsx +++ b/project/client/src/components/results/utils/highlights.tsx @@ -1,40 +1,4 @@ -import { MatchHighlights, MatchId, ResultHighlights, TextHighlight } from "../../../types/data" - -export const getResultHighlights = (resultsHighlights: MatchHighlights, id: MatchId) => - Object.entries(resultsHighlights).reduce((acc: ResultHighlights, [strategy, strategyHighlights]) => { - Object.entries(strategyHighlights[id]).forEach(([criterion, criterionHiglights]) => { - if (strategy in acc) acc[strategy][criterion] = criterionHiglights - else acc[strategy] = { [criterion]: criterionHiglights } - }) - return acc - }, {}) - -export const strategyCriteria = (strategy: string) => strategy.split(";") - -const stringFindText = (str: string, text: string): Array<number> => - [...str.matchAll(new RegExp(text, "gi"))].map((match) => match.index) - -export const getHighlightedText = (highlight: string): TextHighlight => { - const startTag = "<em>" - const endTag = "</em>" - const startTags = stringFindText(highlight, startTag) - const endTags = stringFindText(highlight, endTag) - - let highlightedArray = [] - let cursor = 0 - - for (let i = 0; i < startTags.length; i++) { - const startIndex = startTags[i] - const endIndex = endTags[i] - - const highlighted = highlight.slice(startIndex + startTag.length, endIndex) - highlightedArray.push(highlighted) - - cursor = endIndex + endTag.length - } - - return highlightedArray -} +import { TextHighlight } from "../../../types" export function getHighlightedQuery(highlightedArray: TextHighlight, query: string) { let highlightedQuery = query diff --git a/project/client/src/components/results/utils/url.tsx b/project/client/src/components/results/utils/url.tsx index b73fcee..38f70fc 100644 --- a/project/client/src/components/results/utils/url.tsx +++ b/project/client/src/components/results/utils/url.tsx @@ -1,4 +1,4 @@ -import { MatchId } from "../../../types/data" +import { MatchId } from "../../../types" export function getResultUrl(resultId: MatchId, currentMatcher: string) { if (currentMatcher === "ror") return `https://ror.org/${resultId}` diff --git a/project/client/src/styles/index.scss b/project/client/src/styles/index.scss index 3a18b74..280e747 100644 --- a/project/client/src/styles/index.scss +++ b/project/client/src/styles/index.scss @@ -14,3 +14,12 @@ padding-top: 15px; padding-bottom: 1px; } + +.debug-item { + padding-bottom: 1rem !important +} + +.debug-item:not(:first-child) { + border-top: 1px solid var(--border-default-grey); + padding-top: 1rem !important; +} \ No newline at end of file diff --git a/project/client/src/types/data.ts b/project/client/src/types/data.ts deleted file mode 100644 index 5ece45b..0000000 --- a/project/client/src/types/data.ts +++ /dev/null @@ -1,30 +0,0 @@ -export type MatchId = string -export type MatchIds = Array<MatchId> - -export type MatchEnrichedResult = { - id: MatchId - acronym: Array<string> - name: Array<string> - city: Array<string> - country: Array<string> -} -export type MatchEnrichedResults = Array<MatchEnrichedResult> - -export type CriterionHighlight = Array<string> -export type CriterionHighlights = Array<CriterionHighlight> -export type CriteriaHighlights = Record<string, CriterionHighlights> -export type StrategyHighlights = Record<MatchId, CriteriaHighlights> - -export type ResultHighlights = Record<string, CriteriaHighlights> -export type MatchHighlights = Record<string, StrategyHighlights> - -export type MatchResults = { - version: string - index_date: string - results: MatchIds - other_ids: MatchIds - enriched_results: MatchEnrichedResults - highlights: MatchHighlights -} - -export type TextHighlight = Array<any> diff --git a/project/client/src/types/functions.ts b/project/client/src/types/functions.ts deleted file mode 100644 index e9cd79e..0000000 --- a/project/client/src/types/functions.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { CriterionHighlights, CriteriaHighlights, ResultHighlights } from "./data" - -export type CriterionHighlightsArgs = { - criterion: string - criterionHighlights: CriterionHighlights - setTitle: Function -} -export type StrategyHighlightsArgs = { - strategy: string - strategyHighlights: CriteriaHighlights - setTitle: Function -} -export type ResultHighlightsArgs = { - resultHighlights: ResultHighlights - setTitle: Function -} diff --git a/project/client/src/types/index.ts b/project/client/src/types/index.ts new file mode 100644 index 0000000..d101375 --- /dev/null +++ b/project/client/src/types/index.ts @@ -0,0 +1,43 @@ +export type MatchId = string +export type MatchIds = Array<MatchId> + +export type MatchEnrichedResult = { + id: MatchId + name: Array<string> + acronym?: Array<string> + city?: Array<string> + country?: Array<string> +} +export type MatchEnrichedResults = Array<MatchEnrichedResult> + +export type CriteriaHighlight = Array<Array<string>> +export type CriterionHighlight = Record<string, CriteriaHighlight> +export type StrategyHighlight = Array<string> +export type StrategiesHighlight = Array<StrategyHighlight> +export type MatchHighlight = { + criterion: CriterionHighlight + strategies: StrategiesHighlight +} +export type MatchHighlights = Record<MatchId, MatchHighlight> + +export type CriterionDebug = Record<string, number> +export type StrategiesDebug = { + equivalent_strategies: Array<Array<string>> + matches: number +} +export type MatchDebug = { + criterion: CriterionDebug + strategies: Array<StrategiesDebug> +} + +export type MatchResults = { + version: string + index_date: string + results: MatchIds + other_ids: MatchIds + enriched_results: MatchEnrichedResults + highlights?: MatchHighlights + debug?: MatchDebug +} + +export type TextHighlight = Array<any>