From 78544b7f5c2bbb70c2f3ed255215f8fea321766b Mon Sep 17 00:00:00 2001 From: r-southworth Date: Fri, 30 Aug 2024 09:53:28 -0400 Subject: [PATCH 1/3] trying to move fetching to server --- .../services/ResourcesMain copy.tsx | 564 ++++++++++++++++++ src/components/services/ResourcesMain.tsx | 330 +++------- src/components/services/ViewCard.astro | 14 + src/components/services/ViewCard.tsx | 4 - src/lib/types.ts | 12 + src/pages/api/filterPosts.ts | 148 +++++ src/pages/resources copy.astro | 22 + .../20240829200614_refactorFilterPosts.sql | 29 + 8 files changed, 870 insertions(+), 253 deletions(-) create mode 100644 src/components/services/ResourcesMain copy.tsx create mode 100644 src/components/services/ViewCard.astro create mode 100644 src/pages/api/filterPosts.ts create mode 100644 src/pages/resources copy.astro create mode 100644 supabase/migrations/20240829200614_refactorFilterPosts.sql diff --git a/src/components/services/ResourcesMain copy.tsx b/src/components/services/ResourcesMain copy.tsx new file mode 100644 index 00000000..ab0b3636 --- /dev/null +++ b/src/components/services/ResourcesMain copy.tsx @@ -0,0 +1,564 @@ +import type { Component } from "solid-js"; +import type { Post } from "@lib/types"; +import { createSignal, createEffect, onMount, Show } from "solid-js"; +import supabase from "../../lib/supabaseClient"; +import { CategoryCarousel } from "./CategoryCarousel"; +import { ViewCard } from "./ViewCard"; +import { MobileViewCard } from "./MobileViewCard"; +import { GradeFilter } from "./GradeFilter"; +import { SubjectFilter } from "./SubjectFilter"; +import { SecularFilter } from "./SecularFilter"; +import { FiltersMobile } from "./FiltersMobile"; +import { SearchBar } from "./SearchBar"; +import { ui } from "../../i18n/ui"; +import type { uiObject } from "../../i18n/uiType"; +import { getLangFromUrl, useTranslations } from "../../i18n/utils"; +import * as allFilters from "../posts/fetchPosts"; +import stripe from "../../lib/stripe"; +import { useStore } from "@nanostores/solid"; +import { windowSize } from "@components/common/WindowSizeStore"; +import useLocalStorage from "@lib/LocalStorageHook"; +import { IconX } from "@tabler/icons-solidjs"; +import { sortResourceTypes } from "@lib/utils/resourceSort"; +import type { FilterPostsParams } from "@lib/types"; + +const lang = getLangFromUrl(new URL(window.location.href)); +const t = useTranslations(lang); + +//get the categories from the language files so they translate with changes in the language picker +const values = ui[lang] as uiObject; +const productCategories = values.subjectCategoryInfo.subjects; + +async function fetchPosts({ + subjectFilters, + gradeFilters, + searchString, + resourceFilters, + secularFilter, +}: FilterPostsParams) { + const response = await fetch("/api/filterPosts", { + method: "POST", + body: JSON.stringify({ + subjectFilters: subjectFilters, + gradeFilters: gradeFilters, + searchString: searchString, + resourceFilters: resourceFilters, + secularFilter: secularFilter, + lang: lang, + }), + }); + const data = await response.json(); + + return data; +} + +export const ResourcesView: Component = () => { + const [posts, setPosts] = createSignal>([]); + const [searchPost, setSearchPost] = createSignal>([]); + const [currentPosts, setCurrentPosts] = createSignal>([]); + const [subjectFilters, setSubjectFilters] = createSignal>([]); + const [gradeFilters, setGradeFilters] = createSignal>([]); + const [resourceTypesFilters, setResourceTypeFilters] = createSignal< + Array + >([]); + const [resourceFilters, setResourceFilters] = createSignal>( + [] + ); + const [searchString, setSearchString] = createSignal(""); + const [noPostsVisible, setNoPostsVisible] = createSignal(false); + const [secularFilters, setSecularFilters] = createSignal(false); + + const screenSize = useStore(windowSize); + + onMount(async () => { + if ( + localStorage.getItem("selectedSubjects") !== null && + localStorage.getItem("selectedSubjects") + ) { + setSubjectFilters([ + ...subjectFilters(), + ...JSON.parse(localStorage.getItem("selectedSubjects")!), + ]); + } + if ( + localStorage.getItem("selectedGrades") !== null && + localStorage.getItem("selectedGrades") + ) { + setGradeFilters([ + ...gradeFilters(), + ...JSON.parse(localStorage.getItem("selectedGrades")!), + ]); + } + if ( + localStorage.getItem("searchString") !== null && + localStorage.getItem("searchString") !== undefined + ) { + const searchStringValue = + localStorage.getItem("searchString") || ""; + setSearchString(searchStringValue); + } + if ( + localStorage.getItem("selectedResourceTypes") !== null && + localStorage.getItem("selectedResourceTypes") + ) { + setResourceFilters([ + ...resourceFilters(), + ...JSON.parse(localStorage.getItem("selectedResourceTypes")!), + ]); + } + await filterPosts(); + }); + + window.addEventListener("beforeunload", () => { + localStorage.removeItem("selectedGrades"); + localStorage.removeItem("selectedSubjects"); + localStorage.removeItem("searchString"); + localStorage.removeItem("selectedResourceTypes"); + }); + + const searchPosts = async () => { + if (localStorage.getItem("searchString") !== null) { + setSearchString(localStorage.getItem("searchString") as string); + } + + filterPosts(); + }; + + const setCategoryFilter = (currentCategory: string) => { + if (subjectFilters().includes(currentCategory)) { + let currentFilters = subjectFilters().filter( + (el) => el !== currentCategory + ); + setSubjectFilters(currentFilters); + } else { + setSubjectFilters([...subjectFilters(), currentCategory]); + } + + filterPosts(); + }; + + let timeouts: (string | number | NodeJS.Timeout | undefined)[] = []; + + const filterPosts = async () => { + const noPostsMessage = document.getElementById("no-posts-message"); + + const res = await fetchPosts({ + subjectFilters: subjectFilters(), + gradeFilters: gradeFilters(), + searchString: searchString(), + resourceFilters: resourceTypesFilters(), + secularFilter: secularFilters(), + lang: lang, + }); + + if (res === null || res === undefined) { + noPostsMessage?.classList.remove("hidden"); + setTimeout(() => { + noPostsMessage?.classList.add("hidden"); + }, 3000); + + setPosts([]); + setCurrentPosts([]); + console.error(); + } else if (Object.keys(res).length === 0) { + noPostsMessage?.classList.remove("hidden"); + + setTimeout(() => { + noPostsMessage?.classList.add("hidden"); + }, 3000); + + timeouts.push( + setTimeout(() => { + //Clear all filters after the timeout otherwise the message immediately disappears (probably not a perfect solution) + clearAllFilters(); + }, 3000) + ); + + let allPosts = await fetchPosts({ + subjectFilters: [], + gradeFilters: [], + searchString: "", + resourceFilters: [], + secularFilter: false, + lang: lang, + }); + + //Add the categories to the posts in the current language + // const allUpdatedPosts = await Promise.all( + // allPosts + // ? allPosts.map(async (item) => { + // item.subject = []; + // productCategories.forEach((productCategories) => { + // item.product_subject.map( + // (productSubject: string) => { + // if ( + // productSubject === + // productCategories.id + // ) { + // item.subject.push( + // productCategories.name + // ); + // } + // } + // ); + // }); + // delete item.product_subject; + + // const { data: gradeData, error: gradeError } = + // await supabase.from("grade_level").select("*"); + + // if (gradeError) { + // console.log( + // "supabase error: " + gradeError.message + // ); + // } else { + // item.grade = []; + // gradeData.forEach((databaseGrade) => { + // item.post_grade.map((itemGrade: string) => { + // if ( + // itemGrade === + // databaseGrade.id.toString() + // ) { + // item.grade.push(databaseGrade.grade); + // } + // }); + // }); + // } + + // const { + // data: resourceTypesData, + // error: resourceTypesError, + // } = await supabase.from("resource_types").select("*"); + + // if (resourceTypesError) { + // console.log( + // "supabase error: " + + // resourceTypesError.message + // ); + // } else { + // sortResourceTypes(resourceTypesData); + // item.resourceTypes = []; + // resourceTypesData.forEach( + // (databaseResourceTypes) => { + // item.resource_types.map( + // (itemResourceTypes: string) => { + // if ( + // itemResourceTypes === + // databaseResourceTypes.id.toString() + // ) { + // item.resource_types.push( + // databaseResourceTypes.resource_types + // ); + // } + // } + // ); + // } + // ); + // } + + // return item; + // }) + // : [] + // ); + + // setPosts(allUpdatedPosts!); + // setCurrentPosts(allUpdatedPosts!); + // setPosts(allPosts); + // setCurrentPosts(allPosts); + console.log(allPosts); + } else { + for (let i = 0; i < timeouts.length; i++) { + clearTimeout(timeouts[i]); + } + + timeouts = []; + + let resPosts = await Promise.all( + res.map(async (item) => { + item.subject = []; + productCategories.forEach((productCategories) => { + item.product_subject.map((productSubject: string) => { + if (productSubject === productCategories.id) { + item.subject.push(productCategories.name); + } + }); + }); + delete item.product_subject; + + const { data: gradeData, error: gradeError } = + await supabase.from("grade_level").select("*"); + + if (gradeError) { + console.log("supabase error: " + gradeError.message); + } else { + item.grade = []; + gradeData.forEach((databaseGrade) => { + item.post_grade.map((itemGrade: string) => { + if (itemGrade === databaseGrade.id.toString()) { + item.grade.push(databaseGrade.grade); + } + }); + }); + } + const { + data: resourceTypesData, + error: resourceTypesError, + } = await supabase.from("resource_types").select("*"); + + if (resourceTypesError) { + console.log( + "supabase error: " + resourceTypesError.message + ); + } else { + sortResourceTypes(resourceTypesData); + item.resource_types = []; + resourceTypesData.forEach((databaseResourceTypes) => { + item.resource_types.map( + (itemResourceTypes: string) => { + if ( + itemResourceTypes === + databaseResourceTypes.id.toString() + ) { + item.resource_types.push( + databaseResourceTypes.resource_types + ); + } + } + ); + }); + } + return item; + }) + ); + setPosts(resPosts); + setCurrentPosts(resPosts); + } + }; + + const filterPostsByGrade = (grade: string) => { + if (gradeFilters().includes(grade)) { + let currentGradeFilters = gradeFilters().filter( + (el) => el !== grade + ); + setGradeFilters(currentGradeFilters); + } else { + setGradeFilters([...gradeFilters(), grade]); + } + + filterPosts(); + }; + + const filterPostsByResourceTypes = (type: string) => { + if (resourceTypesFilters().includes(type)) { + let currentResourceTypesFilter = resourceTypesFilters().filter( + (el) => el !== type + ); + setResourceTypeFilters(currentResourceTypesFilter); + } else { + setResourceTypeFilters([...resourceTypesFilters(), type]); + } + + filterPosts(); + }; + + const filterPostsBySecular = (secular: boolean) => { + setSecularFilters(secular); + filterPosts(); + }; + + const clearAllFilters = () => { + let searchInput = document.getElementById("search") as HTMLInputElement; + const subjectCheckboxes = document.querySelectorAll( + "input[type='checkbox'].subject" + ) as NodeListOf; + const gradeCheckboxes = document.querySelectorAll( + "input[type='checkbox'].grade" + ) as NodeListOf; + const resourceTypesCheckoxes = document.querySelectorAll( + "input[type='checkbox'].resourceType" + ) as NodeListOf; + + console.log(subjectCheckboxes); + console.log(gradeCheckboxes); + console.log(resourceTypesCheckoxes); + + if (searchInput !== null && searchInput.value !== null) { + searchInput.value = ""; + } + + gradeCheckboxes.forEach((checkbox) => { + if (checkbox && checkbox.checked) { + checkbox.checked = false; + } + }); + + subjectCheckboxes.forEach((checkbox) => { + if (checkbox && checkbox.checked) { + checkbox.checked = false; + } + }); + + resourceTypesCheckoxes.forEach((checkbox) => { + if (checkbox && checkbox.checked) { + checkbox.checked = false; + } + }); + + localStorage.removeItem("selectedGrades"); + localStorage.removeItem("selectedSubjects"); + localStorage.removeItem("searchString"); + localStorage.removeItem("selectedResourceTypes"); + + setSearchPost([]); + setSearchString(""); + // localStorage.setItem("searchString", ""); + setSubjectFilters([]); + setGradeFilters([]); + setResourceTypeFilters([]); + setSecularFilters(false); + + filterPosts(); + }; + + const clearSubjects = () => { + const subjectCheckboxes = document.querySelectorAll( + "input[type='checkbox'].subject" + ) as NodeListOf; + + subjectCheckboxes.forEach((checkbox) => { + if (checkbox && checkbox.checked) { + checkbox.checked = false; + } + }); + + localStorage.removeItem("selectedSubjects"); + setSubjectFilters([]); + filterPosts(); + }; + + const clearGrade = () => { + const gradeCheckboxes = document.querySelectorAll( + "input[type='checkbox'].grade" + ) as NodeListOf; + + console.log(gradeCheckboxes); + + gradeCheckboxes.forEach((checkbox) => { + if (checkbox && checkbox.checked) { + checkbox.checked = false; + } + }); + + localStorage.removeItem("selectedGrades"); + setGradeFilters([]); + filterPosts(); + }; + + const clearResourceTypes = () => { + const resourceTypesCheckoxes = document.querySelectorAll( + "input[type='checkbox'].resourceType" + ) as NodeListOf; + + console.log(resourceTypesCheckoxes); + + resourceTypesCheckoxes.forEach((checkbox) => { + if (checkbox && checkbox.checked) { + checkbox.checked = false; + } + }); + + // localStorage.removeItem("selectedGrades"); + setResourceTypeFilters([]); + filterPosts(); + }; + + const clearSecular = () => { + const secularCheckbox = document.getElementById( + "secularCheck" + ) as HTMLInputElement; + + console.log(secularCheckbox); + + if (secularCheckbox && secularCheckbox.checked) { + secularCheckbox.checked = false; + } + + setSecularFilters(false); + filterPosts(); + }; + + return ( +
+
+ + {/* */} +
+ + + + + + +
+

+ {t("pageTitles.services")} +

+
+
+ +
+ + + + +
+ + +
+

+ {t("pageTitles.services")} +

+
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ ); +}; diff --git a/src/components/services/ResourcesMain.tsx b/src/components/services/ResourcesMain.tsx index 570c8a8c..3a3e58c7 100644 --- a/src/components/services/ResourcesMain.tsx +++ b/src/components/services/ResourcesMain.tsx @@ -20,6 +20,7 @@ import { windowSize } from "@components/common/WindowSizeStore"; import useLocalStorage from "@lib/LocalStorageHook"; import { IconX } from "@tabler/icons-solidjs"; import { sortResourceTypes } from "@lib/utils/resourceSort"; +import type { FilterPostsParams } from "@lib/types"; const lang = getLangFromUrl(new URL(window.location.href)); const t = useTranslations(lang); @@ -28,12 +29,28 @@ const t = useTranslations(lang); const values = ui[lang] as uiObject; const productCategories = values.subjectCategoryInfo.subjects; -// interface Props { -// subject: string | null; -// grade: string | null; -// searchString: string | null; -// resourceTypes: string | null; -// } +async function fetchPosts({ + subjectFilters, + gradeFilters, + searchString, + resourceFilters, + secularFilter, +}: FilterPostsParams) { + const response = await fetch("/api/filterPosts", { + method: "POST", + body: JSON.stringify({ + subjectFilters: subjectFilters, + gradeFilters: gradeFilters, + searchString: searchString, + resourceFilters: resourceFilters, + secularFilter: secularFilter, + lang: lang, + }), + }); + const data = await response.json(); + + return data; +} export const ResourcesView: Component = () => { const [posts, setPosts] = createSignal>([]); @@ -123,188 +140,67 @@ export const ResourcesView: Component = () => { let timeouts: (string | number | NodeJS.Timeout | undefined)[] = []; const filterPosts = async () => { + console.log("Filtering posts..."); const noPostsMessage = document.getElementById("no-posts-message"); - const res = await allFilters.fetchFilteredPosts( - subjectFilters(), - gradeFilters(), - searchString(), - resourceTypesFilters(), - secularFilters() - ); - - if (res === null || res === undefined) { - noPostsMessage?.classList.remove("hidden"); - setTimeout(() => { - noPostsMessage?.classList.add("hidden"); - }, 3000); - - setPosts([]); - setCurrentPosts([]); - console.error(); - } else if (Object.keys(res).length === 0) { - noPostsMessage?.classList.remove("hidden"); - - setTimeout(() => { - noPostsMessage?.classList.add("hidden"); - }, 3000); - - timeouts.push( - setTimeout(() => { - //Clear all filters after the timeout otherwise the message immediately disappears (probably not a perfect solution) - clearAllFilters(); - }, 3000) - ); - - let allPosts = await allFilters.fetchAllPosts(); - - //Add the categories to the posts in the current language - const allUpdatedPosts = await Promise.all( - allPosts - ? allPosts.map(async (item) => { - item.subject = []; - productCategories.forEach((productCategories) => { - item.product_subject.map( - (productSubject: string) => { - if ( - productSubject === - productCategories.id - ) { - item.subject.push( - productCategories.name - ); - } - } - ); - }); - delete item.product_subject; - - const { data: gradeData, error: gradeError } = - await supabase.from("grade_level").select("*"); - - if (gradeError) { - console.log( - "supabase error: " + gradeError.message - ); - } else { - item.grade = []; - gradeData.forEach((databaseGrade) => { - item.post_grade.map((itemGrade: string) => { - if ( - itemGrade === - databaseGrade.id.toString() - ) { - item.grade.push(databaseGrade.grade); - } - }); - }); - } - - const { - data: resourceTypesData, - error: resourceTypesError, - } = await supabase.from("resource_types").select("*"); - - if (resourceTypesError) { - console.log( - "supabase error: " + - resourceTypesError.message - ); - } else { - sortResourceTypes(resourceTypesData); - item.resourceTypes = []; - resourceTypesData.forEach( - (databaseResourceTypes) => { - item.resource_types.map( - (itemResourceTypes: string) => { - if ( - itemResourceTypes === - databaseResourceTypes.id.toString() - ) { - item.resource_types.push( - databaseResourceTypes.resource_types - ); - } - } - ); - } - ); - } - - return item; - }) - : [] - ); - - setPosts(allUpdatedPosts!); - setCurrentPosts(allUpdatedPosts!); - } else { - for (let i = 0; i < timeouts.length; i++) { - clearTimeout(timeouts[i]); - } + const res = await fetchPosts({ + subjectFilters: subjectFilters(), + gradeFilters: gradeFilters(), + searchString: searchString(), + resourceFilters: resourceTypesFilters(), + secularFilter: secularFilters(), + lang: lang, + }); - timeouts = []; - - let resPosts = await Promise.all( - res.map(async (item) => { - item.subject = []; - productCategories.forEach((productCategories) => { - item.product_subject.map((productSubject: string) => { - if (productSubject === productCategories.id) { - item.subject.push(productCategories.name); - } - }); - }); - delete item.product_subject; - - const { data: gradeData, error: gradeError } = - await supabase.from("grade_level").select("*"); - - if (gradeError) { - console.log("supabase error: " + gradeError.message); - } else { - item.grade = []; - gradeData.forEach((databaseGrade) => { - item.post_grade.map((itemGrade: string) => { - if (itemGrade === databaseGrade.id.toString()) { - item.grade.push(databaseGrade.grade); - } - }); - }); - } - const { - data: resourceTypesData, - error: resourceTypesError, - } = await supabase.from("resource_types").select("*"); - - if (resourceTypesError) { - console.log( - "supabase error: " + resourceTypesError.message - ); - } else { - sortResourceTypes(resourceTypesData); - item.resource_types = []; - resourceTypesData.forEach((databaseResourceTypes) => { - item.resource_types.map( - (itemResourceTypes: string) => { - if ( - itemResourceTypes === - databaseResourceTypes.id.toString() - ) { - item.resource_types.push( - databaseResourceTypes.resource_types - ); - } - } - ); - }); - } - return item; - }) - ); - setPosts(resPosts); - setCurrentPosts(resPosts); - } + console.log(res); + + // if (res === null || res === undefined) { + // noPostsMessage?.classList.remove("hidden"); + // setTimeout(() => { + // noPostsMessage?.classList.add("hidden"); + // }, 3000); + + // setPosts([]); + // setCurrentPosts([]); + // console.error(); + // } else if (Object.keys(res).length === 0) { + // noPostsMessage?.classList.remove("hidden"); + + // setTimeout(() => { + // noPostsMessage?.classList.add("hidden"); + // }, 3000); + + // timeouts.push( + // setTimeout(() => { + // //Clear all filters after the timeout otherwise the message immediately disappears (probably not a perfect solution) + // clearAllFilters(); + // }, 3000) + // ); + + // let allPosts = await fetchPosts({ + // subjectFilters: [], + // gradeFilters: [], + // searchString: "", + // resourceFilters: [], + // secularFilter: false, + // lang: lang, + // }); + + // // setPosts(allUpdatedPosts!); + // // setCurrentPosts(allUpdatedPosts!); + // // setPosts(allPosts); + // // setCurrentPosts(allPosts); + // console.log(allPosts); + // } else { + // for (let i = 0; i < timeouts.length; i++) { + // clearTimeout(timeouts[i]); + // } + + // timeouts = []; + + // setPosts(resPosts); + // setCurrentPosts(resPosts); + // } }; const filterPostsByGrade = (grade: string) => { @@ -501,70 +397,6 @@ export const ResourcesView: Component = () => { clearResourceTypes={clearResourceTypes} filterPostsByResourceTypes={filterPostsByResourceTypes} /> - {/*
-
-
- - - -
- - -
- -
- -
- -
- -
-
*/}
diff --git a/src/components/services/ViewCard.astro b/src/components/services/ViewCard.astro new file mode 100644 index 00000000..31d4fbac --- /dev/null +++ b/src/components/services/ViewCard.astro @@ -0,0 +1,14 @@ +--- +import PostComponent from "./PostComponent.astro"; +import { fetchAllPosts } from "@components/posts/fetchPosts"; + +const posts = await fetchAllPosts(); + +if (posts === null || posts === undefined) { + console.error(); +} +--- + +
+ {posts?.map((post) => )} +
diff --git a/src/components/services/ViewCard.tsx b/src/components/services/ViewCard.tsx index 76b42c29..d28ca8b4 100644 --- a/src/components/services/ViewCard.tsx +++ b/src/components/services/ViewCard.tsx @@ -4,12 +4,8 @@ import { createSignal, createEffect, Show } from "solid-js"; import { DeletePostButton } from "../posts/DeletePostButton"; import supabase from "../../lib/supabaseClient"; import { getLangFromUrl, useTranslations } from "../../i18n/utils"; -import { SocialMediaShares } from "../posts/SocialMediaShares"; -import SocialModal from "../posts/SocialModal"; import { AddToCart } from "../common/cart/AddToCartButton"; -import { Quantity } from "@components/common/cart/Quantity"; import type { AuthSession } from "@supabase/supabase-js"; -import { DownloadBtn } from "@components/members/user/DownloadBtn"; import { FavoriteButton } from "@components/posts/AddFavorite"; import { sortResourceTypes } from "@lib/utils/resourceSort"; diff --git a/src/lib/types.ts b/src/lib/types.ts index 0474c511..05c79bdc 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -1,3 +1,5 @@ +import type { ui } from "@i18n/ui.ts"; + export interface Post { //Posts should be pulled from the sellerposts view id: number; //Post ID used for directing to the post details page @@ -17,6 +19,7 @@ export interface Post { secular: boolean; draft_status: boolean; resource_urls: string; + unit_amount: number; //These fields are not stored in the database and must be fetched from stripe (price) or set by the code subject: Array | null; //Array of subject names @@ -55,3 +58,12 @@ export interface Creator { seller_about: string; contribution: number; } + +export interface FilterPostsParams { + subjectFilters: string[]; + gradeFilters: string[]; + searchString: string; + resourceFilters: string[]; + secularFilter: boolean; + lang: "en" | "es" | "fr"; +} \ No newline at end of file diff --git a/src/pages/api/filterPosts.ts b/src/pages/api/filterPosts.ts new file mode 100644 index 00000000..9f1e7461 --- /dev/null +++ b/src/pages/api/filterPosts.ts @@ -0,0 +1,148 @@ +import supabase from "@lib/supabaseClientServiceRole"; +import type { APIRoute } from "astro"; +import { useTranslations } from "@i18n/utils"; +import type { FilterPostsParams, Post } from "@lib/types"; +import { ui } from "../../i18n/ui"; +import type { uiObject } from "../../i18n/uiType"; +import { sortResourceTypes } from "@lib/utils/resourceSort"; + +export const POST: APIRoute = async ({ request, redirect }) => { + const { + subjectFilters, + gradeFilters, + searchString, + resourceFilters, + secularFilter, + lang, + }: FilterPostsParams = await request.json(); + + const values = ui[lang] as uiObject; + const postSubjects = values.subjectCategoryInfo.subjects; + + try { + let query = supabase + .from("sellerposts") + .select("*") + .order("id", { ascending: false }) + .eq("listing_status", true) + .eq("draft_status", false); + + if (Array.isArray(subjectFilters) && subjectFilters.length !== 0) { + query = query.overlaps("product_subject", subjectFilters); + } + if (Array.isArray(gradeFilters) && gradeFilters.length !== 0) { + query = query.overlaps("post_grade", gradeFilters); + } + if (searchString && searchString.length !== 0) { + query = query.textSearch("title_content", searchString); + } + if (secularFilter === true) { + query = query.is("secular", true); + } + if (Array.isArray(resourceFilters) && resourceFilters.length !== 0) { + query = query.overlaps("resource_types", resourceFilters); + } + + const { data: posts, error } = await query; + if (error) { + return new Response( + JSON.stringify({ + message: error.message, + code: error.code, + }), + { status: 500 } + ); + } + + const { data: gradeData, error: gradeError } = await supabase + .from("grade_level") + .select("*"); + + if (gradeError) { + return new Response( + JSON.stringify({ + message: gradeError.message, + }), + { status: 500 } + ); + } + + const { data: resourceTypesData, error: resourceTypesError } = + await supabase.from("resource_types").select("*"); + + if (resourceTypesError) { + return new Response( + JSON.stringify({ + message: resourceTypesError.message, + }), + { status: 500 } + ); + } + + let formattedPosts: Post[] = []; + + if (posts && gradeData && resourceTypesData) { + formattedPosts = await Promise.all( + posts.map(async (post: Post) => { + post.subject = []; + postSubjects.forEach((subject) => { + post.product_subject.map((productSubject: string) => { + if (productSubject === subject.id) { + post.subject?.push(subject.name); + } + }); + }); + + post.grade = []; + gradeData.forEach((databaseGrade) => { + post.post_grade.map((postGrade: string) => { + if (postGrade === databaseGrade.id.toString()) { + post.grade?.push(databaseGrade.grade); + } + }); + }); + + sortResourceTypes(resourceTypesData); + post.resourceTypes = []; + resourceTypesData.forEach((databaseResourceTypes) => { + post.resource_types.map((postResourceTypes: string) => { + if ( + postResourceTypes === + databaseResourceTypes.id.toString() + ) { + post.resource_types.push( + databaseResourceTypes.resource_types + ); + } + }); + }); + + return post; + }) + ); + } + + return new Response( + JSON.stringify({ + body: formattedPosts, + }), + { status: 200 } + ); + } catch (e) { + console.error(e); + if (e instanceof Error) { + return new Response( + JSON.stringify({ + message: e.message, + }), + { status: 500 } + )} else { + return new Response( + JSON.stringify({ + message: "Something went wrong", + }), + { status: 500 } + ) + }; + } +}; diff --git a/src/pages/resources copy.astro b/src/pages/resources copy.astro new file mode 100644 index 00000000..df230256 --- /dev/null +++ b/src/pages/resources copy.astro @@ -0,0 +1,22 @@ +--- +import PageLayout from "@layouts/PageLayout.astro"; +import { ResourcesView } from "@components/services/ResourcesMain"; +import { getLangFromUrl, useTranslations } from "@i18n/utils"; +import { SearchBar } from "@components/services/SearchBar"; + +const lang = getLangFromUrl(Astro.url); +const t = useTranslations(lang); +--- + + +
+
+
+ {/* */} +
+
+
+
] diff --git a/supabase/migrations/20240829200614_refactorFilterPosts.sql b/supabase/migrations/20240829200614_refactorFilterPosts.sql new file mode 100644 index 00000000..e211dc70 --- /dev/null +++ b/supabase/migrations/20240829200614_refactorFilterPosts.sql @@ -0,0 +1,29 @@ +create or replace view "public"."sellerposts" as SELECT seller_post.id, + seller_post.title, + seller_post.content, + seller_post.user_id, + seller_post.image_urls, + seller_post.product_subject, + seller_post.post_grade, + sellers.seller_name, + sellers.seller_id, + profiles.email, + seller_post.stripe_price_id AS price_id, + seller_post.stripe_product_id AS product_id, + seller_post.resource_types, + seller_post.listing_status, + seller_post.secular, + seller_post.draft_status, + seller_post.resource_urls, + seller_post.resource_links, + prices.unit_amount + FROM ((seller_post + LEFT JOIN profiles ON ((seller_post.user_id = profiles.user_id)) + LEFT JOIN sellers ON ((seller_post.user_id = sellers.user_id))) + LEFT JOIN stripe.prices ON ((seller_post.stripe_price_id = prices.id))); + +CREATE OR REPLACE FUNCTION "public"."title_content"("public"."sellerposts") RETURNS "text" + LANGUAGE "sql" IMMUTABLE + AS $_$ + select $1.title || ' ' || $1.content; +$_$; \ No newline at end of file From edc9c879ca3a2357f7b8c915ec92307593dd9713 Mon Sep 17 00:00:00 2001 From: r-southworth Date: Fri, 30 Aug 2024 11:12:08 -0400 Subject: [PATCH 2/3] move fetch to server --- src/components/home/Home.tsx | 141 +++-- .../services/ResourcesMain copy.tsx | 564 ------------------ src/components/services/ResourcesMain.tsx | 110 ++-- src/lib/types.ts | 4 + .../{filterPosts.ts => fetchFilterPosts.ts} | 23 +- .../20240829200614_refactorFilterPosts.sql | 29 - 6 files changed, 134 insertions(+), 737 deletions(-) delete mode 100644 src/components/services/ResourcesMain copy.tsx rename src/pages/api/{filterPosts.ts => fetchFilterPosts.ts} (86%) delete mode 100644 supabase/migrations/20240829200614_refactorFilterPosts.sql diff --git a/src/components/home/Home.tsx b/src/components/home/Home.tsx index 991d9c50..72c1d6cb 100644 --- a/src/components/home/Home.tsx +++ b/src/components/home/Home.tsx @@ -1,5 +1,5 @@ import type { Component } from "solid-js"; -import type { Post } from "@lib/types"; +import type { FilterPostsParams, Post } from "@lib/types"; import { createEffect, createSignal, Show, onMount } from "solid-js"; import { useStore } from "@nanostores/solid"; import { windowSize } from "@components/common/WindowSizeStore"; @@ -28,6 +28,37 @@ function redirectToResourcesPage() { window.location.href = `/${lang}/resources`; } +async function fetchPosts({ + subjectFilters, + gradeFilters, + searchString, + resourceFilters, + secularFilter, + lang, + draft_status, + listing_status, + orderAscending, +}: FilterPostsParams) { + const response = await fetch("/api/fetchFilterPosts", { + method: "POST", + body: JSON.stringify({ + subjectFilters: subjectFilters, + gradeFilters: gradeFilters, + searchString: searchString, + resourceFilters: resourceFilters, + secularFilter: secularFilter, + lang: lang, + limit: 8, + draft_status: draft_status, + listing_status: listing_status, + orderAscending: orderAscending, + }), + }); + const data = await response.json(); + + return data; +} + export const Home: Component = () => { const [posts, setPosts] = createSignal>([]); const [currentPosts, setCurrentPosts] = createSignal>([]); @@ -46,83 +77,45 @@ export const Home: Component = () => { let test: any; onMount(async () => { - const { data, error } = await supabase - .from("sellerposts") - .select("*") - .order("id", { ascending: true }) - .eq("listing_status", true) - .eq("draft_status", false) - .limit(8); - if (!data) { + const res = await fetchPosts({ + subjectFilters: [], + gradeFilters: [], + searchString: "", + resourceFilters: [], + secularFilter: false, + lang: lang, + }); + + if ( + res.body === null || + res.body === undefined || + res.body.length < 1 + ) { alert("No posts available."); } - if (error) { - console.log("supabase error: " + error.message); - } else { - const newItems = await Promise.all( - data?.map(async (item) => { - item.subject = []; - productSubjects.forEach((productCategories) => { - item.product_subject.map((productSubject: string) => { - if (productSubject === productCategories.id) { - item.subject.push(productCategories.name); - } - }); - }); - delete item.product_subject; - - if (item.price_id !== null) { - const priceData = await stripe.prices.retrieve( - item.price_id - ); - item.price = priceData.unit_amount! / 100; - } - return item; - }) - ); - setPopularPosts(newItems); - console.log(popularPosts()); - } - }); - createEffect(async () => { - const { data, error } = await supabase - .from("sellerposts") - .select("*") - .eq("listing_status", true) - .eq("draft_status", false) - .order("id", { ascending: false }) - .limit(8); - - if (!data) { - alert("No posts available"); - } - - if (error) { - console.error("supabase error: " + error.message); + setPopularPosts(res.body); + console.log("Pop posts", popularPosts()); + + const newRes = await fetchPosts({ + subjectFilters: [], + gradeFilters: [], + searchString: "", + resourceFilters: [], + secularFilter: false, + lang: lang, + orderAscending: true, + }); + + if ( + newRes.body === null || + newRes.body === undefined || + newRes.body.length < 1 + ) { + alert("No posts available."); } else { - const popItems = await Promise.all( - data?.map(async (item) => { - item.subject = []; - productSubjects.forEach((productCategories) => { - item.product_subject.map((productSubject: string) => { - if (productSubject === productCategories.id) { - item.subject.push(productCategories.name); - } - }); - }); - delete item.product_subject; - - if (item.price_id !== null) { - const priceData = await stripe.prices.retrieve( - item.price_id - ); - item.price = priceData.unit_amount! / 100; - } - return item; - }) - ); - setNewPosts(popItems); + setNewPosts(newRes.body); + console.log("New Posts", newPosts()); } }); diff --git a/src/components/services/ResourcesMain copy.tsx b/src/components/services/ResourcesMain copy.tsx deleted file mode 100644 index ab0b3636..00000000 --- a/src/components/services/ResourcesMain copy.tsx +++ /dev/null @@ -1,564 +0,0 @@ -import type { Component } from "solid-js"; -import type { Post } from "@lib/types"; -import { createSignal, createEffect, onMount, Show } from "solid-js"; -import supabase from "../../lib/supabaseClient"; -import { CategoryCarousel } from "./CategoryCarousel"; -import { ViewCard } from "./ViewCard"; -import { MobileViewCard } from "./MobileViewCard"; -import { GradeFilter } from "./GradeFilter"; -import { SubjectFilter } from "./SubjectFilter"; -import { SecularFilter } from "./SecularFilter"; -import { FiltersMobile } from "./FiltersMobile"; -import { SearchBar } from "./SearchBar"; -import { ui } from "../../i18n/ui"; -import type { uiObject } from "../../i18n/uiType"; -import { getLangFromUrl, useTranslations } from "../../i18n/utils"; -import * as allFilters from "../posts/fetchPosts"; -import stripe from "../../lib/stripe"; -import { useStore } from "@nanostores/solid"; -import { windowSize } from "@components/common/WindowSizeStore"; -import useLocalStorage from "@lib/LocalStorageHook"; -import { IconX } from "@tabler/icons-solidjs"; -import { sortResourceTypes } from "@lib/utils/resourceSort"; -import type { FilterPostsParams } from "@lib/types"; - -const lang = getLangFromUrl(new URL(window.location.href)); -const t = useTranslations(lang); - -//get the categories from the language files so they translate with changes in the language picker -const values = ui[lang] as uiObject; -const productCategories = values.subjectCategoryInfo.subjects; - -async function fetchPosts({ - subjectFilters, - gradeFilters, - searchString, - resourceFilters, - secularFilter, -}: FilterPostsParams) { - const response = await fetch("/api/filterPosts", { - method: "POST", - body: JSON.stringify({ - subjectFilters: subjectFilters, - gradeFilters: gradeFilters, - searchString: searchString, - resourceFilters: resourceFilters, - secularFilter: secularFilter, - lang: lang, - }), - }); - const data = await response.json(); - - return data; -} - -export const ResourcesView: Component = () => { - const [posts, setPosts] = createSignal>([]); - const [searchPost, setSearchPost] = createSignal>([]); - const [currentPosts, setCurrentPosts] = createSignal>([]); - const [subjectFilters, setSubjectFilters] = createSignal>([]); - const [gradeFilters, setGradeFilters] = createSignal>([]); - const [resourceTypesFilters, setResourceTypeFilters] = createSignal< - Array - >([]); - const [resourceFilters, setResourceFilters] = createSignal>( - [] - ); - const [searchString, setSearchString] = createSignal(""); - const [noPostsVisible, setNoPostsVisible] = createSignal(false); - const [secularFilters, setSecularFilters] = createSignal(false); - - const screenSize = useStore(windowSize); - - onMount(async () => { - if ( - localStorage.getItem("selectedSubjects") !== null && - localStorage.getItem("selectedSubjects") - ) { - setSubjectFilters([ - ...subjectFilters(), - ...JSON.parse(localStorage.getItem("selectedSubjects")!), - ]); - } - if ( - localStorage.getItem("selectedGrades") !== null && - localStorage.getItem("selectedGrades") - ) { - setGradeFilters([ - ...gradeFilters(), - ...JSON.parse(localStorage.getItem("selectedGrades")!), - ]); - } - if ( - localStorage.getItem("searchString") !== null && - localStorage.getItem("searchString") !== undefined - ) { - const searchStringValue = - localStorage.getItem("searchString") || ""; - setSearchString(searchStringValue); - } - if ( - localStorage.getItem("selectedResourceTypes") !== null && - localStorage.getItem("selectedResourceTypes") - ) { - setResourceFilters([ - ...resourceFilters(), - ...JSON.parse(localStorage.getItem("selectedResourceTypes")!), - ]); - } - await filterPosts(); - }); - - window.addEventListener("beforeunload", () => { - localStorage.removeItem("selectedGrades"); - localStorage.removeItem("selectedSubjects"); - localStorage.removeItem("searchString"); - localStorage.removeItem("selectedResourceTypes"); - }); - - const searchPosts = async () => { - if (localStorage.getItem("searchString") !== null) { - setSearchString(localStorage.getItem("searchString") as string); - } - - filterPosts(); - }; - - const setCategoryFilter = (currentCategory: string) => { - if (subjectFilters().includes(currentCategory)) { - let currentFilters = subjectFilters().filter( - (el) => el !== currentCategory - ); - setSubjectFilters(currentFilters); - } else { - setSubjectFilters([...subjectFilters(), currentCategory]); - } - - filterPosts(); - }; - - let timeouts: (string | number | NodeJS.Timeout | undefined)[] = []; - - const filterPosts = async () => { - const noPostsMessage = document.getElementById("no-posts-message"); - - const res = await fetchPosts({ - subjectFilters: subjectFilters(), - gradeFilters: gradeFilters(), - searchString: searchString(), - resourceFilters: resourceTypesFilters(), - secularFilter: secularFilters(), - lang: lang, - }); - - if (res === null || res === undefined) { - noPostsMessage?.classList.remove("hidden"); - setTimeout(() => { - noPostsMessage?.classList.add("hidden"); - }, 3000); - - setPosts([]); - setCurrentPosts([]); - console.error(); - } else if (Object.keys(res).length === 0) { - noPostsMessage?.classList.remove("hidden"); - - setTimeout(() => { - noPostsMessage?.classList.add("hidden"); - }, 3000); - - timeouts.push( - setTimeout(() => { - //Clear all filters after the timeout otherwise the message immediately disappears (probably not a perfect solution) - clearAllFilters(); - }, 3000) - ); - - let allPosts = await fetchPosts({ - subjectFilters: [], - gradeFilters: [], - searchString: "", - resourceFilters: [], - secularFilter: false, - lang: lang, - }); - - //Add the categories to the posts in the current language - // const allUpdatedPosts = await Promise.all( - // allPosts - // ? allPosts.map(async (item) => { - // item.subject = []; - // productCategories.forEach((productCategories) => { - // item.product_subject.map( - // (productSubject: string) => { - // if ( - // productSubject === - // productCategories.id - // ) { - // item.subject.push( - // productCategories.name - // ); - // } - // } - // ); - // }); - // delete item.product_subject; - - // const { data: gradeData, error: gradeError } = - // await supabase.from("grade_level").select("*"); - - // if (gradeError) { - // console.log( - // "supabase error: " + gradeError.message - // ); - // } else { - // item.grade = []; - // gradeData.forEach((databaseGrade) => { - // item.post_grade.map((itemGrade: string) => { - // if ( - // itemGrade === - // databaseGrade.id.toString() - // ) { - // item.grade.push(databaseGrade.grade); - // } - // }); - // }); - // } - - // const { - // data: resourceTypesData, - // error: resourceTypesError, - // } = await supabase.from("resource_types").select("*"); - - // if (resourceTypesError) { - // console.log( - // "supabase error: " + - // resourceTypesError.message - // ); - // } else { - // sortResourceTypes(resourceTypesData); - // item.resourceTypes = []; - // resourceTypesData.forEach( - // (databaseResourceTypes) => { - // item.resource_types.map( - // (itemResourceTypes: string) => { - // if ( - // itemResourceTypes === - // databaseResourceTypes.id.toString() - // ) { - // item.resource_types.push( - // databaseResourceTypes.resource_types - // ); - // } - // } - // ); - // } - // ); - // } - - // return item; - // }) - // : [] - // ); - - // setPosts(allUpdatedPosts!); - // setCurrentPosts(allUpdatedPosts!); - // setPosts(allPosts); - // setCurrentPosts(allPosts); - console.log(allPosts); - } else { - for (let i = 0; i < timeouts.length; i++) { - clearTimeout(timeouts[i]); - } - - timeouts = []; - - let resPosts = await Promise.all( - res.map(async (item) => { - item.subject = []; - productCategories.forEach((productCategories) => { - item.product_subject.map((productSubject: string) => { - if (productSubject === productCategories.id) { - item.subject.push(productCategories.name); - } - }); - }); - delete item.product_subject; - - const { data: gradeData, error: gradeError } = - await supabase.from("grade_level").select("*"); - - if (gradeError) { - console.log("supabase error: " + gradeError.message); - } else { - item.grade = []; - gradeData.forEach((databaseGrade) => { - item.post_grade.map((itemGrade: string) => { - if (itemGrade === databaseGrade.id.toString()) { - item.grade.push(databaseGrade.grade); - } - }); - }); - } - const { - data: resourceTypesData, - error: resourceTypesError, - } = await supabase.from("resource_types").select("*"); - - if (resourceTypesError) { - console.log( - "supabase error: " + resourceTypesError.message - ); - } else { - sortResourceTypes(resourceTypesData); - item.resource_types = []; - resourceTypesData.forEach((databaseResourceTypes) => { - item.resource_types.map( - (itemResourceTypes: string) => { - if ( - itemResourceTypes === - databaseResourceTypes.id.toString() - ) { - item.resource_types.push( - databaseResourceTypes.resource_types - ); - } - } - ); - }); - } - return item; - }) - ); - setPosts(resPosts); - setCurrentPosts(resPosts); - } - }; - - const filterPostsByGrade = (grade: string) => { - if (gradeFilters().includes(grade)) { - let currentGradeFilters = gradeFilters().filter( - (el) => el !== grade - ); - setGradeFilters(currentGradeFilters); - } else { - setGradeFilters([...gradeFilters(), grade]); - } - - filterPosts(); - }; - - const filterPostsByResourceTypes = (type: string) => { - if (resourceTypesFilters().includes(type)) { - let currentResourceTypesFilter = resourceTypesFilters().filter( - (el) => el !== type - ); - setResourceTypeFilters(currentResourceTypesFilter); - } else { - setResourceTypeFilters([...resourceTypesFilters(), type]); - } - - filterPosts(); - }; - - const filterPostsBySecular = (secular: boolean) => { - setSecularFilters(secular); - filterPosts(); - }; - - const clearAllFilters = () => { - let searchInput = document.getElementById("search") as HTMLInputElement; - const subjectCheckboxes = document.querySelectorAll( - "input[type='checkbox'].subject" - ) as NodeListOf; - const gradeCheckboxes = document.querySelectorAll( - "input[type='checkbox'].grade" - ) as NodeListOf; - const resourceTypesCheckoxes = document.querySelectorAll( - "input[type='checkbox'].resourceType" - ) as NodeListOf; - - console.log(subjectCheckboxes); - console.log(gradeCheckboxes); - console.log(resourceTypesCheckoxes); - - if (searchInput !== null && searchInput.value !== null) { - searchInput.value = ""; - } - - gradeCheckboxes.forEach((checkbox) => { - if (checkbox && checkbox.checked) { - checkbox.checked = false; - } - }); - - subjectCheckboxes.forEach((checkbox) => { - if (checkbox && checkbox.checked) { - checkbox.checked = false; - } - }); - - resourceTypesCheckoxes.forEach((checkbox) => { - if (checkbox && checkbox.checked) { - checkbox.checked = false; - } - }); - - localStorage.removeItem("selectedGrades"); - localStorage.removeItem("selectedSubjects"); - localStorage.removeItem("searchString"); - localStorage.removeItem("selectedResourceTypes"); - - setSearchPost([]); - setSearchString(""); - // localStorage.setItem("searchString", ""); - setSubjectFilters([]); - setGradeFilters([]); - setResourceTypeFilters([]); - setSecularFilters(false); - - filterPosts(); - }; - - const clearSubjects = () => { - const subjectCheckboxes = document.querySelectorAll( - "input[type='checkbox'].subject" - ) as NodeListOf; - - subjectCheckboxes.forEach((checkbox) => { - if (checkbox && checkbox.checked) { - checkbox.checked = false; - } - }); - - localStorage.removeItem("selectedSubjects"); - setSubjectFilters([]); - filterPosts(); - }; - - const clearGrade = () => { - const gradeCheckboxes = document.querySelectorAll( - "input[type='checkbox'].grade" - ) as NodeListOf; - - console.log(gradeCheckboxes); - - gradeCheckboxes.forEach((checkbox) => { - if (checkbox && checkbox.checked) { - checkbox.checked = false; - } - }); - - localStorage.removeItem("selectedGrades"); - setGradeFilters([]); - filterPosts(); - }; - - const clearResourceTypes = () => { - const resourceTypesCheckoxes = document.querySelectorAll( - "input[type='checkbox'].resourceType" - ) as NodeListOf; - - console.log(resourceTypesCheckoxes); - - resourceTypesCheckoxes.forEach((checkbox) => { - if (checkbox && checkbox.checked) { - checkbox.checked = false; - } - }); - - // localStorage.removeItem("selectedGrades"); - setResourceTypeFilters([]); - filterPosts(); - }; - - const clearSecular = () => { - const secularCheckbox = document.getElementById( - "secularCheck" - ) as HTMLInputElement; - - console.log(secularCheckbox); - - if (secularCheckbox && secularCheckbox.checked) { - secularCheckbox.checked = false; - } - - setSecularFilters(false); - filterPosts(); - }; - - return ( -
-
- - {/* */} -
- - - - - - -
-

- {t("pageTitles.services")} -

-
-
- -
- - - - -
- - -
-

- {t("pageTitles.services")} -

-
-
- -
- -
-
- -
- -
-
-
-
-
- ); -}; diff --git a/src/components/services/ResourcesMain.tsx b/src/components/services/ResourcesMain.tsx index 3a3e58c7..67f82e2e 100644 --- a/src/components/services/ResourcesMain.tsx +++ b/src/components/services/ResourcesMain.tsx @@ -1,34 +1,20 @@ import type { Component } from "solid-js"; import type { Post } from "@lib/types"; import { createSignal, createEffect, onMount, Show } from "solid-js"; -import supabase from "../../lib/supabaseClient"; -import { CategoryCarousel } from "./CategoryCarousel"; import { ViewCard } from "./ViewCard"; import { MobileViewCard } from "./MobileViewCard"; -import { GradeFilter } from "./GradeFilter"; -import { SubjectFilter } from "./SubjectFilter"; -import { SecularFilter } from "./SecularFilter"; import { FiltersMobile } from "./FiltersMobile"; import { SearchBar } from "./SearchBar"; import { ui } from "../../i18n/ui"; import type { uiObject } from "../../i18n/uiType"; import { getLangFromUrl, useTranslations } from "../../i18n/utils"; -import * as allFilters from "../posts/fetchPosts"; -import stripe from "../../lib/stripe"; import { useStore } from "@nanostores/solid"; import { windowSize } from "@components/common/WindowSizeStore"; -import useLocalStorage from "@lib/LocalStorageHook"; -import { IconX } from "@tabler/icons-solidjs"; -import { sortResourceTypes } from "@lib/utils/resourceSort"; import type { FilterPostsParams } from "@lib/types"; const lang = getLangFromUrl(new URL(window.location.href)); const t = useTranslations(lang); -//get the categories from the language files so they translate with changes in the language picker -const values = ui[lang] as uiObject; -const productCategories = values.subjectCategoryInfo.subjects; - async function fetchPosts({ subjectFilters, gradeFilters, @@ -36,7 +22,7 @@ async function fetchPosts({ resourceFilters, secularFilter, }: FilterPostsParams) { - const response = await fetch("/api/filterPosts", { + const response = await fetch("/api/fetchFilterPosts", { method: "POST", body: JSON.stringify({ subjectFilters: subjectFilters, @@ -154,53 +140,49 @@ export const ResourcesView: Component = () => { console.log(res); - // if (res === null || res === undefined) { - // noPostsMessage?.classList.remove("hidden"); - // setTimeout(() => { - // noPostsMessage?.classList.add("hidden"); - // }, 3000); - - // setPosts([]); - // setCurrentPosts([]); - // console.error(); - // } else if (Object.keys(res).length === 0) { - // noPostsMessage?.classList.remove("hidden"); - - // setTimeout(() => { - // noPostsMessage?.classList.add("hidden"); - // }, 3000); - - // timeouts.push( - // setTimeout(() => { - // //Clear all filters after the timeout otherwise the message immediately disappears (probably not a perfect solution) - // clearAllFilters(); - // }, 3000) - // ); - - // let allPosts = await fetchPosts({ - // subjectFilters: [], - // gradeFilters: [], - // searchString: "", - // resourceFilters: [], - // secularFilter: false, - // lang: lang, - // }); - - // // setPosts(allUpdatedPosts!); - // // setCurrentPosts(allUpdatedPosts!); - // // setPosts(allPosts); - // // setCurrentPosts(allPosts); - // console.log(allPosts); - // } else { - // for (let i = 0; i < timeouts.length; i++) { - // clearTimeout(timeouts[i]); - // } - - // timeouts = []; - - // setPosts(resPosts); - // setCurrentPosts(resPosts); - // } + if ( + res.body === null || + res.body === undefined || + res.body.length < 1 + ) { + noPostsMessage?.classList.remove("hidden"); + setTimeout(() => { + noPostsMessage?.classList.add("hidden"); + }, 3000); + + setPosts([]); + setCurrentPosts([]); + console.error(); + + timeouts.push( + setTimeout(() => { + //Clear all filters after the timeout otherwise the message immediately disappears (probably not a perfect solution) + clearAllFilters(); + }, 3000) + ); + + let allPosts = await fetchPosts({ + subjectFilters: [], + gradeFilters: [], + searchString: "", + resourceFilters: [], + secularFilter: false, + lang: lang, + }); + + setPosts(allPosts); + setCurrentPosts(allPosts); + console.log(allPosts); + } else { + for (let i = 0; i < timeouts.length; i++) { + clearTimeout(timeouts[i]); + } + + timeouts = []; + + setPosts(res.body); + setCurrentPosts(res.body); + } }; const filterPostsByGrade = (grade: string) => { @@ -246,10 +228,6 @@ export const ResourcesView: Component = () => { "input[type='checkbox'].resourceType" ) as NodeListOf; - console.log(subjectCheckboxes); - console.log(gradeCheckboxes); - console.log(resourceTypesCheckoxes); - if (searchInput !== null && searchInput.value !== null) { searchInput.value = ""; } diff --git a/src/lib/types.ts b/src/lib/types.ts index 05c79bdc..1fc16f01 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -66,4 +66,8 @@ export interface FilterPostsParams { resourceFilters: string[]; secularFilter: boolean; lang: "en" | "es" | "fr"; + limit?: number; + draft_status?: boolean; + listing_status?: boolean; + orderAscending?: boolean; } \ No newline at end of file diff --git a/src/pages/api/filterPosts.ts b/src/pages/api/fetchFilterPosts.ts similarity index 86% rename from src/pages/api/filterPosts.ts rename to src/pages/api/fetchFilterPosts.ts index 9f1e7461..a433374d 100644 --- a/src/pages/api/filterPosts.ts +++ b/src/pages/api/fetchFilterPosts.ts @@ -1,10 +1,11 @@ -import supabase from "@lib/supabaseClientServiceRole"; +import supabase from "@lib/supabaseClientServer"; import type { APIRoute } from "astro"; import { useTranslations } from "@i18n/utils"; import type { FilterPostsParams, Post } from "@lib/types"; import { ui } from "../../i18n/ui"; import type { uiObject } from "../../i18n/uiType"; import { sortResourceTypes } from "@lib/utils/resourceSort"; +import stripe from "@lib/stripe"; export const POST: APIRoute = async ({ request, redirect }) => { const { @@ -14,6 +15,10 @@ export const POST: APIRoute = async ({ request, redirect }) => { resourceFilters, secularFilter, lang, + limit, + draft_status, + listing_status, + orderAscending }: FilterPostsParams = await request.json(); const values = ui[lang] as uiObject; @@ -23,9 +28,9 @@ export const POST: APIRoute = async ({ request, redirect }) => { let query = supabase .from("sellerposts") .select("*") - .order("id", { ascending: false }) - .eq("listing_status", true) - .eq("draft_status", false); + .order("id", { ascending: orderAscending ? orderAscending : false }) + .eq("listing_status", listing_status? listing_status : true) + .eq("draft_status", draft_status? draft_status : false); if (Array.isArray(subjectFilters) && subjectFilters.length !== 0) { query = query.overlaps("product_subject", subjectFilters); @@ -42,6 +47,9 @@ export const POST: APIRoute = async ({ request, redirect }) => { if (Array.isArray(resourceFilters) && resourceFilters.length !== 0) { query = query.overlaps("resource_types", resourceFilters); } + if (limit){ + query = query.limit(limit) + } const { data: posts, error } = await query; if (error) { @@ -117,6 +125,13 @@ export const POST: APIRoute = async ({ request, redirect }) => { }); }); + if (post.price_id !== null) { + const priceData = await stripe.prices.retrieve( + post.price_id + ); + post.price = priceData.unit_amount! / 100; + } + return post; }) ); diff --git a/supabase/migrations/20240829200614_refactorFilterPosts.sql b/supabase/migrations/20240829200614_refactorFilterPosts.sql deleted file mode 100644 index e211dc70..00000000 --- a/supabase/migrations/20240829200614_refactorFilterPosts.sql +++ /dev/null @@ -1,29 +0,0 @@ -create or replace view "public"."sellerposts" as SELECT seller_post.id, - seller_post.title, - seller_post.content, - seller_post.user_id, - seller_post.image_urls, - seller_post.product_subject, - seller_post.post_grade, - sellers.seller_name, - sellers.seller_id, - profiles.email, - seller_post.stripe_price_id AS price_id, - seller_post.stripe_product_id AS product_id, - seller_post.resource_types, - seller_post.listing_status, - seller_post.secular, - seller_post.draft_status, - seller_post.resource_urls, - seller_post.resource_links, - prices.unit_amount - FROM ((seller_post - LEFT JOIN profiles ON ((seller_post.user_id = profiles.user_id)) - LEFT JOIN sellers ON ((seller_post.user_id = sellers.user_id))) - LEFT JOIN stripe.prices ON ((seller_post.stripe_price_id = prices.id))); - -CREATE OR REPLACE FUNCTION "public"."title_content"("public"."sellerposts") RETURNS "text" - LANGUAGE "sql" IMMUTABLE - AS $_$ - select $1.title || ' ' || $1.content; -$_$; \ No newline at end of file From 2506e02e5846c541cd6cc360bbf57abab5d524a7 Mon Sep 17 00:00:00 2001 From: r-southworth Date: Fri, 30 Aug 2024 12:10:22 -0400 Subject: [PATCH 3/3] cleanup files not being used --- src/components/services/ViewCard.astro | 14 -------------- src/pages/resources copy.astro | 22 ---------------------- 2 files changed, 36 deletions(-) delete mode 100644 src/components/services/ViewCard.astro delete mode 100644 src/pages/resources copy.astro diff --git a/src/components/services/ViewCard.astro b/src/components/services/ViewCard.astro deleted file mode 100644 index 31d4fbac..00000000 --- a/src/components/services/ViewCard.astro +++ /dev/null @@ -1,14 +0,0 @@ ---- -import PostComponent from "./PostComponent.astro"; -import { fetchAllPosts } from "@components/posts/fetchPosts"; - -const posts = await fetchAllPosts(); - -if (posts === null || posts === undefined) { - console.error(); -} ---- - -
- {posts?.map((post) => )} -
diff --git a/src/pages/resources copy.astro b/src/pages/resources copy.astro deleted file mode 100644 index df230256..00000000 --- a/src/pages/resources copy.astro +++ /dev/null @@ -1,22 +0,0 @@ ---- -import PageLayout from "@layouts/PageLayout.astro"; -import { ResourcesView } from "@components/services/ResourcesMain"; -import { getLangFromUrl, useTranslations } from "@i18n/utils"; -import { SearchBar } from "@components/services/SearchBar"; - -const lang = getLangFromUrl(Astro.url); -const t = useTranslations(lang); ---- - - -
-
-
- {/* */} -
-
-
-
]