Skip to content

Commit

Permalink
feat: add category selection
Browse files Browse the repository at this point in the history
  • Loading branch information
louisewang1 committed Nov 30, 2021
1 parent 0972539 commit ccd7f24
Show file tree
Hide file tree
Showing 8 changed files with 643 additions and 382 deletions.
11 changes: 7 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
"license": "AGPL-3.0-only",
"dependencies": {
"@graasp/chatbox": "git://github.com/graasp/graasp-chatbox.git#main",
"@graasp/query-client": "git://github.com/graasp/graasp-query-client.git#82/updateDeleteItemsRoute",
"@graasp/ui": "git://github.com/graasp/graasp-ui.git#master",
"@graasp/query-client": "git://github.com/graasp/graasp-query-client.git#89/category",
"@graasp/ui": "git://github.com/graasp/graasp-ui.git#50/cjs",
"@material-ui/core": "4.11.2",
"@material-ui/icons": "5.0.0-beta.4",
"@material-ui/lab": "4.0.0-alpha.57",
Expand Down Expand Up @@ -35,7 +35,7 @@
"react-beautiful-dnd": "13.1.0",
"react-dom": "^17.0.1",
"react-i18next": "11.11.4",
"react-query": "3.16.0",
"react-query": "3.31.0",
"react-quill": "1.3.5",
"react-redux": "7.2.2",
"react-redux-toastr": "7.6.5",
Expand Down Expand Up @@ -119,5 +119,8 @@
"src/serviceWorker.js"
]
},
"packageManager": "[email protected]"
"packageManager": "[email protected]",
"resolutions": {
"@graasp/query-client": "portal:/home/louise/Desktop/graasp/graasp-query-client"
}
}
2 changes: 1 addition & 1 deletion src/components/RecycleBinScreen.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const RowActions = ({ data: item }) => (
</>
);
RowActions.propTypes = {
data: PropTypes.shape({}).isRequired,
data: PropTypes.shape({ id: PropTypes.string }).isRequired,
};

const ToolbarActions = ({ selectedIds }) => (
Expand Down
211 changes: 211 additions & 0 deletions src/components/item/sharing/CategorySelection.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
/* eslint-disable react/jsx-props-no-spreading */
import React, { useContext, useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import { Loader } from '@graasp/ui';
import { Map } from 'immutable';
import { makeStyles } from '@material-ui/core/styles';
import { useTranslation } from 'react-i18next';
import Typography from '@material-ui/core/Typography';
import TextField from '@material-ui/core/TextField';
import Autocomplete from '@material-ui/lab/Autocomplete';
import { useParams } from 'react-router';
import { MUTATION_KEYS } from '@graasp/query-client';
import { hooks, useMutation } from '../../../config/queryClient';
import {
SHARE_ITEM_CATEGORY_AGE,
SHARE_ITEM_CATEGORY_DISCIPLINE,
} from '../../../config/selectors';
import { CurrentUserContext } from '../../context/CurrentUserContext';

const { useCategoryTypes, useCategories, useItemCategories } = hooks;
const { POST_ITEM_CATEGORY, DELETE_ITEM_CATEGORY } = MUTATION_KEYS;

const useStyles = makeStyles({
Selection: {
marginTop: 20,
},
DropMenu: {
marginBottom: 10,
},
});

function CategorySelection({ item, edit }) {
const { t } = useTranslation();
const classes = useStyles();
const { mutate: createItemCategory } = useMutation(POST_ITEM_CATEGORY);
const { mutate: deleteItemCategory } = useMutation(DELETE_ITEM_CATEGORY);

// user
const { isLoading: isMemberLoading } = useContext(CurrentUserContext);

// current item
const { itemId } = useParams();

// extra value
const {
data: itemCategories,
isLoading: isItemCategoriesLoading,
} = useItemCategories(itemId);
const {
data: categoryTypes,
isLoading: isCategoryTypesLoading,
} = useCategoryTypes();
const { data: allCategories, isLoading: isCategoriesLoading } = useCategories(
[],
);
const ageList = allCategories?.filter(
(entry) => entry.type === categoryTypes?.get(0).id,
);
const disciplineList = allCategories?.filter(
(entry) => entry.type === categoryTypes?.get(1).id,
);

const mapItemCategory = new Map(
itemCategories?.map((obj) => [obj.category_id, obj.id]),
);
const ageCategory = ageList?.map((entry) => entry.id);
const disciplineCategory = disciplineList?.map((entry) => entry.id);

// seperate age and discipline category
const initialAgeValue = itemCategories?.filter((itemCategory) =>
ageCategory?.includes(itemCategory.category_id),
);
const initialDisciplineValue = itemCategories?.filter((itemCategory) =>
disciplineCategory?.includes(itemCategory.category_id),
);
const selectedDisciplineIds = initialDisciplineValue
?.map((entry) => entry.category_id)
.toArray();
const selectedAgeIds = initialAgeValue
?.map((entry) => entry.category_id)
.toArray();
// set initial value
const [ageOptionsValue, setAgeOptionsValue] = useState([]);
const [disciplineOptionsValue, setDisciplineOptionsValue] = useState([]);

// update state variables depending on fetch values
useEffect(() => {
if (selectedAgeIds && ageList) {
setAgeOptionsValue(
ageList.filter((entry) => selectedAgeIds.includes(entry.id)).toArray(),
);
}
}, [item, itemCategories, ageList, selectedAgeIds]);

useEffect(() => {
if (selectedDisciplineIds && disciplineList) {
setDisciplineOptionsValue(
disciplineList
.filter((entry) => selectedDisciplineIds.includes(entry.id))
.toArray(),
);
}
}, [item, itemCategories, disciplineList, selectedDisciplineIds]);

if (
isMemberLoading ||
isItemCategoriesLoading ||
isCategoryTypesLoading ||
isCategoriesLoading
) {
return <Loader />;
}

const handleAgeChange = (event, value, reason) => {
if (reason === 'select-option') {
// post new category (discipline)
const newCategoryId = value.at(-1).id;
createItemCategory({
itemId,
categoryId: newCategoryId,
});
} else if (reason === 'remove-option') {
// remove an option
const result = ageOptionsValue.filter(
({ id: id1 }) => !value.some(({ id: id2 }) => id2 === id1),
);
const entryId = mapItemCategory.get(result[0].id);
deleteItemCategory({
itemId,
entryId,
});
}
};

const handleDisciplineChange = (event, value, reason) => {
if (reason === 'select-option') {
// post new category (discipline)
const newCategoryId = value.at(-1).id;
createItemCategory({
itemId,
categoryId: newCategoryId,
});
} else if (reason === 'remove-option') {
// remove an option
const result = disciplineOptionsValue.filter(
({ id: id1 }) => !value.some(({ id: id2 }) => id2 === id1),
);
const entryId = mapItemCategory.get(result[0].id);
deleteItemCategory({
itemId,
entryId,
});
}
};
const disciplines = disciplineList.toArray();
const ageGroups = ageList.toArray();
return (
<>
<Typography variant="h6" className={classes.Selection}>
{t('Category')}
</Typography>
<Typography variant="body1"> Age Range: </Typography>
{edit && (
<Autocomplete
multiple
disableClearable
id={SHARE_ITEM_CATEGORY_AGE}
value={ageOptionsValue}
getOptionSelected={(option, value) => option.id === value.id}
options={ageGroups}
getOptionLabel={(option) => option.name}
onChange={handleAgeChange}
renderInput={(params) => (
<TextField
{...params}
variant="outlined"
placeholder="Please Choose From List"
/>
)}
/>
)}
<Typography variant="body1"> Discipline: </Typography>
{edit && (
<Autocomplete
multiple
disableClearable
id={SHARE_ITEM_CATEGORY_DISCIPLINE}
value={disciplineOptionsValue}
getOptionSelected={(option, value) => option.id === value.id}
options={disciplines}
getOptionLabel={(option) => option.name}
onChange={handleDisciplineChange}
renderInput={(params) => (
<TextField
{...params}
variant="outlined"
placeholder="Please Choose From List"
/>
)}
/>
)}
</>
);
}

CategorySelection.propTypes = {
item: PropTypes.instanceOf(Map).isRequired,
edit: PropTypes.bool.isRequired,
};

export default CategorySelection;
2 changes: 2 additions & 0 deletions src/components/item/sharing/ItemSharingTab.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { PSEUDONIMIZED_USER_MAIL } from '../../../config/constants';
import { getItemLoginSchema } from '../../../utils/itemExtra';
import { LayoutContext } from '../../context/LayoutContext';
import { CurrentUserContext } from '../../context/CurrentUserContext';
import CategorySelection from './CategorySelection';

const useStyles = makeStyles((theme) => ({
title: {
Expand Down Expand Up @@ -128,6 +129,7 @@ const ItemSharingTab = ({ item, memberships }) => {
<SharingLink itemId={item.get('id')} />

<VisibilitySelect item={item} edit={canEdit} />
<CategorySelection item={item} edit={canEdit} />

{renderMembershipSettings()}
</Container>
Expand Down
4 changes: 3 additions & 1 deletion src/components/table/DragCellRenderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ const DragCellRenderer = ({ data: item, registerRowDragger }) => {
};

DragCellRenderer.propTypes = {
data: PropTypes.shape({}).isRequired,
data: PropTypes.shape({
id: PropTypes.string,
}).isRequired,
registerRowDragger: PropTypes.func.isRequired,
};

Expand Down
7 changes: 6 additions & 1 deletion src/components/table/NameCellRenderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,12 @@ const NameCellRenderer = ({ data: item }) => {
};

NameCellRenderer.propTypes = {
data: PropTypes.shape({}).isRequired,
data: PropTypes.shape({
type: PropTypes.string,
id: PropTypes.string,
name: PropTypes.string,
extra: PropTypes.shape({}),
}).isRequired,
};

export default NameCellRenderer;
2 changes: 2 additions & 0 deletions src/config/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,8 @@ export const CHATBOX_ID = 'chatbox';
export const CHATBOX_INPUT_BOX_ID = 'chatboxInputBox';
export const CONFIRM_RECYCLE_BUTTON_ID = 'confirmRecycleButton';
export const SHARE_ITEM_VISIBILITY_SELECT_ID = 'shareItemVisiblitySelect';
export const SHARE_ITEM_CATEGORY_AGE = 'shareItemCategoryAge';
export const SHARE_ITEM_CATEGORY_DISCIPLINE = 'shareItemCategoryDiscipline';
export const SHARE_ITEM_PSEUDONYMIZED_SCHEMA_ID =
'shareItemPseudonymizedSchema';
export const ITEM_RECYCLE_BUTTON_CLASS = 'itemRecycleButton';
Expand Down
Loading

0 comments on commit ccd7f24

Please sign in to comment.