Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

issue 488 #495

Closed
wants to merge 8 commits into from
Closed
12 changes: 11 additions & 1 deletion packages/server/src/models/Category.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
Expand Down Expand Up @@ -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<TCategoryDocument[] | null> {
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
Expand All @@ -138,4 +147,5 @@ export default Category as typeof Category & {
) => Promise<TCategoryDocument>;
getCategoryList: () => Promise<TCategoryDocument[]>;
getByStub: (stub: string) => Promise<TCategoryDocument | null>;
getByStubs: (stubs: string[]) => Promise<TCategoryDocument[] | null>;
};
41 changes: 41 additions & 0 deletions packages/server/src/models/Resource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<TResourceDocument | null> {
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.
*/
Expand Down Expand Up @@ -431,6 +468,10 @@ export default Resource as typeof Resource & {
getByResourceIds: (resourceIds: ObjectId[]) => Promise<TResourceDocument[]>;
getByResourceId: (resourceId: ObjectId) => Promise<TResourceDocument | null>;
getUncategorized: () => Promise<TResourceDocument[]>;
getBySubcategoryIds: (
subCategoryIds: ObjectId[],
resourceIds?: ObjectId[]
) => Promise<TResourceDocument[]>;
};

const DraftResource = mongoose.model<TResourceDocument>(
Expand Down
23 changes: 21 additions & 2 deletions packages/server/src/routes/api/resources/index.ts
Original file line number Diff line number Diff line change
@@ -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)
);
Expand Down
61 changes: 14 additions & 47 deletions packages/web/src/components/useResourcesByCategory.tsx
Original file line number Diff line number Diff line change
@@ -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<TResourcesByCategoryPayload> => {
const { data } = await apiClient.get<TResourcesByCategoryPayload>(
`/category/${params.category}`
): Promise<TResourcePayload> => {
const { data } = await apiClient.get<TResourcePayload>(
`/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<TResource[]>(
(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
): {
Expand All @@ -73,7 +40,7 @@ const useResourcesByCategory = (
staleTime: 900000, // 15 min
}
);
const resources = getUniqueFlattenedResources(data);
const resources = data?.resources;

return { data: resources, status };
};
Expand Down