diff --git a/packages/server/src/models/Category.ts b/packages/server/src/models/Category.ts index c182d2d4..fa5190e5 100644 --- a/packages/server/src/models/Category.ts +++ b/packages/server/src/models/Category.ts @@ -32,7 +32,7 @@ export async function categoryDocumentToCategory( // ); if (d.hasOwnProperty("_bsontype")) { // console.warn("This appears to be an ObjectId"); - // console.trace(); + // cons ole.trace(); return null; } } @@ -110,10 +110,19 @@ CategorySchema.statics.getByStub = async function( return result; }; +/* Finds and returns all categories from an array of category stubs.*/ +CategorySchema.statics.getByStubs = async function( + stubs: string[] +): Promise { + const result = await this.find({ stub: { $in: stubs } }); + return result; +}; + /** * Creates or finds an existing subcategory by its name and adds * it as a child of this category */ + (CategorySchema.methods as any).addSubcategory = async function( subcategoryName: string, subcategoryStub: string @@ -138,4 +147,5 @@ export default Category as typeof Category & { ) => Promise; getCategoryList: () => Promise; getByStub: (stub: string) => Promise; + getByStubs: (stubs: string[]) => Promise; }; diff --git a/packages/server/src/models/Resource.ts b/packages/server/src/models/Resource.ts index 20e7e1d7..9658fd55 100644 --- a/packages/server/src/models/Resource.ts +++ b/packages/server/src/models/Resource.ts @@ -365,6 +365,43 @@ ResourceSchema.statics.getByResourceIds = async function( .populate("lastModifiedBy"); }; +/** + * Retrieve all resources from an array of category stubs. `null` if there is no matching Resources. + * Can also optionally specify specific resources to filter using an array of resourceIds. + * The `includeDeleted` flag must be set to `true` to return trashed resources. + */ +ResourceSchema.statics.getBySubcategoryIds = async function( + subcategoryIds: ObjectId[], + resourceIds: ObjectId[] = null, + includeDeleted: boolean = false +): Promise { + const resourcesFromSubcategories = await this.find({ + subcategories: { $in: subcategoryIds }, + deleted: { $in: [false, includeDeleted] }, + }); + + const resources = resourceIds + ? resourcesFromSubcategories.filter( + (subcategoryResource: TResourceDocument) => { + const id = subcategoryResource.resourceId; + // console.log(id, resourceIds[0], id in { resourceIds }); + + return resourceIds.reduce( + (isIncluded: boolean, resourceId: ObjectId) => { + if (resourceId == id) { + isIncluded = true; + } + return isIncluded; + }, + false + ); + } + ) + : resourcesFromSubcategories; + + return resources; +}; + /** * Retrieve all resources. */ @@ -431,6 +468,10 @@ export default Resource as typeof Resource & { getByResourceIds: (resourceIds: ObjectId[]) => Promise; getByResourceId: (resourceId: ObjectId) => Promise; getUncategorized: () => Promise; + getBySubcategoryIds: ( + subCategoryIds: ObjectId[], + resourceIds?: ObjectId[] + ) => Promise; }; const DraftResource = mongoose.model( diff --git a/packages/server/src/routes/api/resources/index.ts b/packages/server/src/routes/api/resources/index.ts index a72b593b..f987a56c 100644 --- a/packages/server/src/routes/api/resources/index.ts +++ b/packages/server/src/routes/api/resources/index.ts @@ -1,13 +1,32 @@ import Resource, { resourceDocumentToResource } from "../../../models/Resource"; +import Category from "../../../models/Category"; import { ObjectId } from "bson"; +import { TCategoryDocument } from "../../../models/Category"; + export async function get(_req, res, _next) { try { const idParam = _req.query.id; - const resourceIds = idParam?.split(",")?.map(ObjectId.createFromHexString); - const resourceDocuments = resourceIds + const resourceIds = idParam?.split(","); + + const categoryStubs = _req.query?.categories?.split(","); + const categoryDocuments = await Category.getByStubs(categoryStubs); + + const subcategoryIds = categoryDocuments + .map((categoryDocument: TCategoryDocument) => { + return categoryDocument.subcategories; + }) + .flat(1); + + const resourceDocuments = categoryStubs + ? await Resource.getBySubcategoryIds( + subcategoryIds as ObjectId[], + resourceIds + ) + : resourceIds ? await Resource.getByResourceIds(resourceIds) : await Resource.getAll(); + const resources = await Promise.all( resourceDocuments.map(resourceDocumentToResource) ); diff --git a/packages/web/src/components/useResourcesByCategory.tsx b/packages/web/src/components/useResourcesByCategory.tsx index 7562844f..f4da6e6f 100644 --- a/packages/web/src/components/useResourcesByCategory.tsx +++ b/packages/web/src/components/useResourcesByCategory.tsx @@ -1,65 +1,32 @@ import { TResource } from "@upswyng/types"; -import { TResourcesByCategoryPayload } from "../webTypes"; +import { TResourcePayload } from "../webTypes"; import apiClient from "../utils/apiClient"; import { useQuery } from "react-query"; const getResourcesByCategory = async ( _queryKey: string, params: { category: string } -): Promise => { - const { data } = await apiClient.get( - `/category/${params.category}` +): Promise => { + const { data } = await apiClient.get( + `/resources/?categories=${params.category}` ); - if (!data.category) { + if (!data.resources) { throw new Error("no category found in resources by category response"); } - const { - category: { subcategories }, - } = data; - if (!(subcategories || []).length) { - throw new Error( - "no sub-categories found in resources by category response" - ); - } + // const { + // category: { subcategories }, + // } = data; + // if (!(subcategories || []).length) { + // throw new Error( + // "no sub-categories found in resources by category response" + // ); + // } return data; }; -const getUniqueFlattenedResources = ( - payload?: TResourcesByCategoryPayload -): TResource[] | undefined => { - const subcategories = payload?.category?.subcategories; - if (!subcategories || !subcategories.length) { - return; - } - - const uniqueResources = subcategories.reduce( - (categoryResources, subcategory) => { - const { resources: subcategoryResources } = subcategory; - if (!subcategoryResources || !subcategoryResources.length) { - return categoryResources; - } - - const uniqueSubcategoryResources = categoryResources.length - ? subcategoryResources.filter( - resource => - !categoryResources.find( - categoryResource => - categoryResource.resourceId === resource.resourceId - ) - ) - : subcategoryResources; - - return categoryResources.concat(uniqueSubcategoryResources); - }, - [] - ); - - return uniqueResources; -}; - const useResourcesByCategory = ( categoryStub?: string ): { @@ -73,7 +40,7 @@ const useResourcesByCategory = ( staleTime: 900000, // 15 min } ); - const resources = getUniqueFlattenedResources(data); + const resources = data?.resources; return { data: resources, status }; };