From 7a191ae98bc108b2ee3cc0441d9307298414f9b1 Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 19 Dec 2024 16:06:47 -0500 Subject: [PATCH] Initial nextjs api endpoint commit --- .../components/view--goal-search--results.tsx | 47 +++++ src/frontend/components/view--goal-search.tsx | 196 +++++++++++------- .../node-queries.ts => lib/graphqlQueries.ts} | 85 +++++--- src/frontend/pages/[...slug].tsx | 6 +- src/frontend/pages/api/goal-search.ts | 27 +++ src/frontend/pages/index.tsx | 42 +--- 6 files changed, 265 insertions(+), 138 deletions(-) create mode 100644 src/frontend/components/view--goal-search--results.tsx rename src/frontend/{pages/api/node-queries.ts => lib/graphqlQueries.ts} (57%) create mode 100644 src/frontend/pages/api/goal-search.ts diff --git a/src/frontend/components/view--goal-search--results.tsx b/src/frontend/components/view--goal-search--results.tsx new file mode 100644 index 00000000..dc7babe0 --- /dev/null +++ b/src/frontend/components/view--goal-search--results.tsx @@ -0,0 +1,47 @@ +import { FormEvent } from "react"; +import { Search } from "@trussworks/react-uswds"; + +interface ViewGoalSearchResultsProps { + +} + +export function ViewGoalSearchResults({}: ViewGoalSearchResultsProps) { + + return ( +
+ results +
+ ); +} + +// {/* {displayGoals?.length ? ( +// +// ) : ( +//
+//
+//

+// No matching goals. +//

+//
+//
+// )} + +//
+// {offset < filteredGoalsCount && +// +// } +//
*/} \ No newline at end of file diff --git a/src/frontend/components/view--goal-search.tsx b/src/frontend/components/view--goal-search.tsx index ce3da5f6..f7c51328 100644 --- a/src/frontend/components/view--goal-search.tsx +++ b/src/frontend/components/view--goal-search.tsx @@ -4,6 +4,7 @@ import { NodeGoalProps, ViewFilter } from "lib/types"; import { NodeGoalCard } from "./node--goal--card"; import { ViewGoalSearchFacet } from "./view--goal-search--facet"; import { ViewGoalSearchFulltext } from "./view--goal-search--fulltext"; +import { ViewGoalSearchResults } from "./view--goal-search--results"; interface ViewGoalSearch { goals: Array, @@ -12,9 +13,32 @@ interface ViewGoalSearch { total: number } +async function getData(fulltext: string) { + const url = `/api/goal-search?fulltext=${fulltext}`; + try { + const response = await fetch(url); + if (!response.ok) { + throw new Error(`Response status: ${response.status}`); + } + + const { data } = await response.json(); + console.log(data); + return { + goals: data?.goalsGraphql1?.results ?? [], + filters: data?.goalsGraphql1?.filters ?? [], + total: data?.goalsGraphql1?.pageInfo?.total ?? 0, + description: data?.goalsGraphql1?.description ?? "", + }; + } catch (error) { + console.error(error.message); + } + +} + export default function GoalsSearchView({ filters, goals, total, description }: ViewGoalSearch) { - const [fulltext, setFulltext] = useState(filters[0].value ? filters[0].value : "") - const [facets] = useState([...Object.keys(filters[1].options)]) + const [fulltext, setFulltext] = useState(filters[0]?.value ? filters[0].value : "") + const [displayGoals, setDisplayGoals] = useState(goals) + const [facets] = useState(filters[1] ? [...Object.keys(filters[1]?.options)] : []) const [search, setSearch] = useState(false); const [offset, setOffset] = useState(9); const [activeTopics, setActiveTopics] = useState([]) @@ -22,9 +46,29 @@ export default function GoalsSearchView({ filters, goals, total, description }: const [filteredGoals, setFilteredGoals] = useState([...goals]) const [filteredGoalsCount, setFilteredGoalsCount] = useState(total) - function handleSearch(e: FormEvent) { + async function handleSearch(e: FormEvent) { e.preventDefault() - setSearch(true) + const url = `/api/goal-search?fulltext=${fulltext}`; + try { + const response = await fetch(url); + if (!response.ok) { + throw new Error(`Response status: ${response.status}`); + } + + const { data } = await response.json(); + console.log(data) + setDisplayGoals(data?.goalsGraphql1?.results ?? []) + setFilteredGoalsCount(data?.goalsGraphql1?.pageInfo?.total ?? 0) + // console.log(data); + // return { + // goals: data?.goalsGraphql1?.results ?? [], + // filters: data?.goalsGraphql1?.filters ?? [], + // total: data?.goalsGraphql1?.pageInfo?.total ?? 0, + // description: data?.goalsGraphql1?.description ?? "", + // }; + } catch (error) { + console.error(error.message); + } } function updateTopicFilters(topic) { @@ -38,81 +82,86 @@ export default function GoalsSearchView({ filters, goals, total, description }: setActiveTopics(currentFilters) } - function filterGoals(resetOffset = false) { - let currentGoals = [...goals]; - let notDisabled = [] - if(fulltext.length > 0) { - currentGoals = currentGoals.filter((goal) => { - let hasFulltext = false; - const searchFields = [goal?.body?.value, goal.title]; - searchFields.forEach((field) => { - if(field && field.toLowerCase().includes(fulltext.toLowerCase())) { - hasFulltext = true - if (goal.topics?.length) { - goal.topics.forEach((topic) => { - if(!notDisabled.includes(topic.name)) { - notDisabled.push(topic.name) - } - }) - } - } - }) + // function filterGoals(resetOffset = false) { + // console.log("dg", displayGoals) + // let currentGoals = [...displayGoals]; + // let notDisabled = [] + // // if(fulltext.length > 0) { + // // currentGoals = currentGoals.filter((goal) => { + // // let hasFulltext = false; + // // const searchFields = [goal?.body?.value, goal.title]; + // // searchFields.forEach((field) => { + // // if(field && field.toLowerCase().includes(fulltext.toLowerCase())) { + // // hasFulltext = true + // // if (goal.topics?.length) { + // // goal.topics.forEach((topic) => { + // // if(!notDisabled.includes(topic.name)) { + // // notDisabled.push(topic.name) + // // } + // // }) + // // } + // // } + // // }) - if(hasFulltext ) { + // // if(hasFulltext ) { - return goal - } else { - return null - } - }) + // // return goal + // // } else { + // // return null + // // } + // // }) - } + // // } - if (activeTopics.length > 0) { - currentGoals = currentGoals.filter((goal) => { - let hasActiveTopic = false - if (!goal.topics) { - return null; - } - goal.topics.forEach((topic) => { - if(activeTopics.indexOf(topic.name) > -1) { - hasActiveTopic = true - } - }) - if(hasActiveTopic) { - return goal - } else { - return null - } - }) - } + // if (activeTopics.length > 0) { + // currentGoals = currentGoals.filter((goal) => { + // let hasActiveTopic = false + // if (!goal.topics) { + // return null; + // } + // goal.topics.forEach((topic) => { + // if(activeTopics.indexOf(topic.name) > -1) { + // hasActiveTopic = true + // } + // }) + // if(hasActiveTopic) { + // return goal + // } else { + // return null + // } + // }) + // } - setNotDisabledTopics(notDisabled) - setFilteredGoalsCount(currentGoals.length) + // setNotDisabledTopics(notDisabled) + // setFilteredGoalsCount(currentGoals.length) - if (resetOffset) { - currentGoals = currentGoals.slice(0, 9); - setOffset(9) - } else { - currentGoals = currentGoals.slice(0, offset); - } - setFilteredGoals(currentGoals) - setSearch(false) - } + // if (resetOffset) { + // currentGoals = currentGoals.slice(0, 9); + // setOffset(9) + // } else { + // currentGoals = currentGoals.slice(0, offset); + // } + // setFilteredGoals(currentGoals) + // setSearch(false) + // } - useEffect(() => { - if(search) { - filterGoals(true) - } - }, [search]) + // useEffect(() => { + // if(search) { + // filterGoals(true) + // } + // }, [search]) - useEffect(() => { - filterGoals(true) - }, [activeTopics]) + // useEffect(() => { + // filterGoals(true) + // }, [activeTopics]) - useEffect(() => { - filterGoals() - }, [offset]) + // useEffect(() => { + // filterGoals() + // }, [offset]) + + // useEffect(() => { + // filterGoals() + // }, [displayGoals]) return (
@@ -149,9 +198,10 @@ export default function GoalsSearchView({ filters, goals, total, description }:
)} - {filteredGoals?.length ? ( + + {/* {displayGoals?.length ? (
    - {filteredGoals.map((goal) => ( + {displayGoals && displayGoals.map((goal) => (
  • } - + */} ); } diff --git a/src/frontend/pages/api/node-queries.ts b/src/frontend/lib/graphqlQueries.ts similarity index 57% rename from src/frontend/pages/api/node-queries.ts rename to src/frontend/lib/graphqlQueries.ts index d63be8ce..a8f544b4 100644 --- a/src/frontend/pages/api/node-queries.ts +++ b/src/frontend/lib/graphqlQueries.ts @@ -1,4 +1,4 @@ -export const nodeQueries = { +export const graphqlQueries = { nodeGoal: (path: string) => ( `query NodeGoalQuery { route(path: "${path}") { @@ -51,34 +51,65 @@ export const nodeQueries = { } }` ), -} - -export const strategicPlanQueries = { planNodeByAgency: (id: number) => ( - `query StrategicPlansByAgency { - strategicPlansByAgencyGraphql1(filter: {field_agency_target_id: ${id}}) { - pageInfo { - total - } - results { - ... on NodePlan { - id - title - link { - url - } - goals { - ... on NodeGoal { - id - fieldId - title - goalType - path + `query StrategicPlansByAgency { + strategicPlansByAgencyGraphql1(filter: {field_agency_target_id: ${id}}) { + pageInfo { + total + } + results { + ... on NodePlan { + id + title + link { + url + } + goals { + ... on NodeGoal { + id + fieldId + title + goalType + path + } + } + } + } + } + }` + ), + goalsView: (fulltext: string, facets: Array) => ( + `query GoalsQuery { + goalsGraphql1(filter: { + aggregated_field: "${fulltext}", + Topics: [${facets}], + }) { + pageInfo { + total + } + filters { + options + value + } + description + results { + ... on NodeGoal { + id + title + path + body { + value + } + + topics { + ... on TermTopic { + id + name + } + } + } } } - } - } - } -}` + }` ), } diff --git a/src/frontend/pages/[...slug].tsx b/src/frontend/pages/[...slug].tsx index 5dfb4675..38cec661 100644 --- a/src/frontend/pages/[...slug].tsx +++ b/src/frontend/pages/[...slug].tsx @@ -8,7 +8,7 @@ import { NodeBasicPage } from "components/node--basic-page"; import { NodeAgency } from "../components/node--agency"; import { NodeGoal } from "components/node--goal"; import { Layout } from "components/layout"; -import { nodeQueries, strategicPlanQueries } from "./api/node-queries"; +import { graphqlQueries } from "../lib/graphqlQueries"; const RESOURCE_TYPES = ["node--page", "node--article", "node--agency", "node--goal"]; @@ -109,7 +109,7 @@ export async function getStaticProps( method: "POST", withAuth: true, // Make authenticated requests using OAuth. body: JSON.stringify({ - query: nodeQueries.nodeGoal(path?.entity?.path), + query: graphqlQueries.nodeGoal(path?.entity?.path), }), }); const { data } = await response.json(); @@ -121,7 +121,7 @@ export async function getStaticProps( method: "POST", withAuth: true, // Make authenticated requests using OAuth. body: JSON.stringify({ - query: strategicPlanQueries.planNodeByAgency(agencyId), + query: graphqlQueries.planNodeByAgency(agencyId), }), }); const { data } = await response.json(); diff --git a/src/frontend/pages/api/goal-search.ts b/src/frontend/pages/api/goal-search.ts new file mode 100644 index 00000000..8ab2e9e7 --- /dev/null +++ b/src/frontend/pages/api/goal-search.ts @@ -0,0 +1,27 @@ +import type { NextApiRequest, NextApiResponse } from 'next' +import { drupal } from "lib/drupal"; +import { graphqlQueries } from "lib/graphqlQueries"; + +type ResponseData = { + message: string; + data: any; +} + +export default async function handler( + req: NextApiRequest, + res: NextApiResponse +) { + const graphqlUrl = drupal.buildUrl("/graphql"); + console.log("q:", req.query) + const fulltext = req.query.fulltext ? req.query.fulltext : "" + const response = await drupal.fetch(graphqlUrl.toString(), { + method: "POST", + withAuth: true, // Make authenticated requests using OAuth. + body: JSON.stringify({ + query: graphqlQueries.goalsView(fulltext, []), + }), + }); + const { data } = await response.json(); + console.log(data) + res.status(200).json({ message: 'Hello from Next.js!', data: data }) +} diff --git a/src/frontend/pages/index.tsx b/src/frontend/pages/index.tsx index 86760376..23b57255 100644 --- a/src/frontend/pages/index.tsx +++ b/src/frontend/pages/index.tsx @@ -4,6 +4,7 @@ import { drupal } from "lib/drupal"; import { Layout } from "components/layout"; import GoalsSearchView from "components/view--goal-search"; import { NodeGoalProps, ViewFilter } from "lib/types"; +import { graphqlQueries } from "lib/graphqlQueries"; interface IndexPageProps { goals: Array, @@ -19,39 +20,10 @@ export const getStaticProps = async () => { method: "POST", withAuth: true, // Make authenticated requests using OAuth. body: JSON.stringify({ - query: `query GoalsQuery { - goalsGraphql1 { - pageInfo { - total - } - filters { - options - value - } - description - results { - ... on NodeGoal { - id - title - path - body { - value - } - - topics { - ... on TermTopic { - id - name - } - } - } - } - } - }`, + query: graphqlQueries.goalsView("", []), }), }); const { data } = await response.json(); - return { props: { goals: data?.goalsGraphql1?.results ?? [], @@ -63,7 +35,7 @@ export const getStaticProps = async () => { }; export default function IndexPage(props: IndexPageProps) { - + console.log(props) return ( @@ -75,10 +47,10 @@ export default function IndexPage(props: IndexPageProps) {