Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/api/src/query/classification.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { Prisma } from "@prisma/client";
import { prisma } from "../model";
import {
CategoryGroup,
ClassificationCategoryTree,
ContentClassification,
PartialContentClassification,
Expand All @@ -20,6 +19,7 @@ import {
getIsEditor,
} from "../utils/permissions";
import { InvalidRequestError } from "../utils/error";
import { CategoryGroup } from "@doenet-tools/shared";

export async function getClassificationCategories() {
const results = await prisma.classificationSystems.findMany({
Expand Down
13 changes: 0 additions & 13 deletions apps/api/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,19 +103,6 @@ export type UserInfoWithEmail = UserInfo & {
isEditor?: boolean;
};

export type CategoryGroup = {
name: string;
isRequired: boolean;
isExclusive: boolean;
categories: Category[];
};

export type Category = {
code: string;
description: string;
term: string;
};

export type ContentClassification = {
id: number;
code: string;
Expand Down
7 changes: 2 additions & 5 deletions apps/app/src/drawers/ExploreFilterDrawer.cy.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
import { ExploreFilterDrawer } from "./ExploreFilterDrawer";
import {
CategoryGroup,
PartialContentClassification,
UserInfo,
} from "../types";
import { PartialContentClassification, UserInfo } from "../types";
import { CategoryGroup } from "@doenet-tools/shared";

describe("ExploreFilterDrawer", { tags: ["@group1"] }, () => {
const mockAuthors: UserInfo[] = [
Expand Down
7 changes: 2 additions & 5 deletions apps/app/src/drawers/ExploreFilterDrawer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,8 @@ import {
DrawerContent,
DrawerOverlay,
} from "@chakra-ui/react";
import {
CategoryGroup,
PartialContentClassification,
UserInfo,
} from "../types";
import { PartialContentClassification, UserInfo } from "../types";
import { CategoryGroup } from "@doenet-tools/shared";
import { NavigateFunction } from "react-router";
import { FilterPanel } from "../widgets/FilterPanel";

Expand Down
8 changes: 2 additions & 6 deletions apps/app/src/paths/Explore.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,8 @@ import Searchbar from "../widgets/SearchBar";
import { Form, useFetcher } from "react-router";
import { CardContent } from "../widgets/Card";
import { createNameNoTag, createNameCheckCurateTag } from "../utils/names";
import {
Content,
UserInfo,
PartialContentClassification,
CategoryGroup,
} from "../types";
import { Content, UserInfo, PartialContentClassification } from "../types";
import { CategoryGroup } from "@doenet-tools/shared";
import CardList from "../widgets/CardList";
import { intWithCommas } from "../utils/formatting";
import { MdFilterAlt, MdFilterAltOff } from "react-icons/md";
Expand Down
16 changes: 7 additions & 9 deletions apps/app/src/paths/editor/EditorHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,7 @@ import { IoGitBranch } from "react-icons/io5";
import { LuCircleHelp, LuLibraryBig } from "react-icons/lu";

import axios from "axios";
import {
AssignmentStatus,
ContentType,
ContentDescription,
CategoryGroup,
Category,
} from "../../types";
import { AssignmentStatus, ContentType, ContentDescription } from "../../types";
import { contentTypeToName, getIconInfo } from "../../utils/activity";
import { SiteContext } from "../SiteHeader";
import { getDiscourseUrl } from "../../utils/discourse";
Expand All @@ -63,12 +57,16 @@ import { LibraryEditorControls } from "../../widgets/editor/LibraryEditorControl
import { editorUrl } from "../../utils/url";
import { NameBar } from "../../widgets/NameBar";
import { loader as settingsLoader } from "./EditorSettingsMode";
import { isActivityFullyCategorized } from "../../utils/classification";
import { useIframeMenuDismissOverlay } from "../../utils/useIframeMenuDismissOverlay";
import { MenuDismissOverlay } from "../../components/MenuDismissOverlay";
import { IFRAME_MENU_IDS } from "../../utils/iframeMenuIds";
import { useControlledMenu } from "../../utils/useControlledMenu";
import { useMenuTooltipSuppression } from "../../utils/useMenuTooltipSuppression";
import {
isBrowsable,
type Category,
type CategoryGroup,
} from "@doenet-tools/shared";

export async function loader({
params,
Expand Down Expand Up @@ -165,7 +163,7 @@ export function EditorHeader() {
const notBrowsable =
isPublic &&
categoryCheckFetcher.data &&
!isActivityFullyCategorized({
!isBrowsable({
allCategories: categoryCheckFetcher.data.allCategories as CategoryGroup[],
categories: categoryCheckFetcher.data.categories as Category[],
});
Expand Down
3 changes: 1 addition & 2 deletions apps/app/src/paths/editor/EditorSettingsMode.cy.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import { EditorSettingsModeComponent } from "./EditorSettingsMode";
import {
AssignmentMode,
Category,
CategoryGroup,
ContentClassification,
DoenetmlVersion,
LicenseCode,
} from "../../types";
import { Category, CategoryGroup } from "@doenet-tools/shared";
import { FetcherWithComponents } from "react-router";

describe("EditorSettingsModeComponent", { tags: ["@group1"] }, () => {
Expand Down
3 changes: 1 addition & 2 deletions apps/app/src/paths/editor/EditorSettingsMode.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,10 @@ import {
import {
AssignmentMode,
ContentClassification,
Category,
DoenetmlVersion,
LicenseCode,
CategoryGroup,
} from "../../types";
import { Category, CategoryGroup } from "@doenet-tools/shared";
import axios from "axios";
import { Box, Heading, VStack } from "@chakra-ui/react";
import { BlueBanner } from "../../widgets/BlueBanner";
Expand Down
6 changes: 3 additions & 3 deletions apps/app/src/popups/ShareMyContentModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import { FiCode } from "react-icons/fi";

import { loader as settingsLoader } from "../paths/editor/EditorSettingsMode";
import { editorUrl } from "../utils/url";
import { isActivityFullyCategorized } from "../utils/classification";
import { isBrowsable } from "@doenet-tools/shared";

export async function loadShareStatus({ params }: { params: any }) {
const { data } = await axios.get(
Expand Down Expand Up @@ -238,12 +238,12 @@ function SharePublicly({
const [copiedShareLink, setCopiedShareLink] = useState(false);
const [copiedEmbedCode, setCopiedEmbedCode] = useState(false);

const unspecifiedCategories = !isActivityFullyCategorized({
const notBrowsable = !isBrowsable({
allCategories: settings.allCategories,
categories: settings.categories,
});

const browseWarning = unspecifiedCategories && (
const browseWarning = notBrowsable && (
<Alert status="warning">
<AlertIcon />
<AlertTitle>Not browsable</AlertTitle>
Expand Down
13 changes: 0 additions & 13 deletions apps/app/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,19 +87,6 @@ export type UserInfoWithEmail = UserInfo & {
isEditor?: boolean;
};

export type CategoryGroup = {
name: string;
isRequired: boolean;
isExclusive: boolean;
categories: Category[];
};

export type Category = {
code: string;
description: string;
term: string;
};

export type ContentClassification = {
id: number;
code: string;
Expand Down
29 changes: 1 addition & 28 deletions apps/app/src/utils/classification.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ReactElement } from "react";
import { Category, CategoryGroup, ContentClassification } from "../types";
import { ContentClassification } from "../types";
import {
Text,
Accordion,
Expand Down Expand Up @@ -73,30 +73,3 @@ export function returnClassificationsAccordionPanel(
</AccordionPanel>
);
}

/**
* Detect whether or not this activity has the required categories filled out.
* For each group that is required, make sure this activity has at least 1 category in that group.
* Returns true if all required groups have at least one category filled out, false otherwise.
*/
export function isActivityFullyCategorized({
allCategories,
categories,
}: {
allCategories: CategoryGroup[];
categories: Category[];
}) {
const existingCodes = categories.map((c) => c.code);

for (const group of allCategories.filter((g) => g.isRequired)) {
const groupCategoryCodes = group.categories.map((c) => c.code);
const intersection = existingCodes.filter((code) =>
groupCategoryCodes.includes(code),
);
if (intersection.length === 0) {
return false;
}
}

return true;
}
3 changes: 2 additions & 1 deletion apps/app/src/widgets/FilterPanel.cy.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { FilterPanel } from "./FilterPanel";
import { CategoryGroup, UserInfo } from "../types";
import { UserInfo } from "../types";
import { CategoryGroup } from "@doenet-tools/shared";

type FilterPanelProps = React.ComponentProps<typeof FilterPanel>;

Expand Down
7 changes: 2 additions & 5 deletions apps/app/src/widgets/FilterPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,8 @@ import { ReactElement } from "react";
import { createNameNoTag } from "../utils/names";
import { CloseIcon } from "@chakra-ui/icons";
import { activityCategoryIcons } from "../utils/activity";
import {
CategoryGroup,
PartialContentClassification,
UserInfo,
} from "../types";
import { PartialContentClassification, UserInfo } from "../types";
import { CategoryGroup } from "@doenet-tools/shared";
import { intWithCommas } from "../utils/formatting";
import { Link as ReactRouterLink, NavigateFunction } from "react-router";
import { clearQueryParameter } from "../utils/explore";
Expand Down
2 changes: 1 addition & 1 deletion apps/app/src/widgets/editor/EditCategories.cy.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { EditCategories } from "./EditCategories";
import { CategoryGroup } from "../../types";
import { CategoryGroup } from "@doenet-tools/shared";

describe("EditCategories Component", { tags: ["@group3"] }, () => {
const mockAllCategories: CategoryGroup[] = [
Expand Down
2 changes: 1 addition & 1 deletion apps/app/src/widgets/editor/EditCategories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ import {
} from "@chakra-ui/react";
import { useFetcher } from "react-router";
import { optimistic } from "../../utils/optimistic_ui";
import { Category, CategoryGroup } from "../../types";
import { activityCategoryIcons } from "../../utils/activity";
import { Category, CategoryGroup } from "@doenet-tools/shared";

export function EditCategories({
contentId,
Expand Down
2 changes: 2 additions & 0 deletions packages/shared/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
export * from "./types/activityViewer.js";
export * from "./types/categories.js";
export * from "./utils/ipfs.js";
export * from "./utils/test.js";
export * from "./apiTypes.js";
export * from "./logic/browsable.js";
38 changes: 38 additions & 0 deletions packages/shared/src/logic/browsable.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import type { Category, CategoryGroup } from "../types/categories.js";

type BrowsableData = {
allCategories: CategoryGroup[];
categories: Category[];
};

/**
* Determine whether this activity should be discoverable.
* Currently this checks category completeness; additional browsable criteria may be added here later.
* TODO: check for no errors
* TODO: check for no accessibility violations
*/
export function isBrowsable(data: BrowsableData) {
return isActivityFullyCategorized(data);
}

/**
* Detect whether or not this activity has the required categories filled out.
* For each group that is required, make sure this activity has at least 1 category in that group.
*/
function isActivityFullyCategorized({
allCategories,
categories,
}: BrowsableData) {
const existingCodes = new Set(categories.map((c) => c.code));

for (const group of allCategories.filter((g) => g.isRequired)) {
const hasMatch = group.categories.some((category) =>
existingCodes.has(category.code),
);
if (!hasMatch) {
return false;
}
}

return true;
}
12 changes: 12 additions & 0 deletions packages/shared/src/types/categories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export type CategoryGroup = {
name: string;
isRequired: boolean;
isExclusive: boolean;
categories: Category[];
};

export type Category = {
code: string;
description: string;
term: string;
};
Loading