From 2fbcb591d518c0d1ac522a748e5721e9d4ea3378 Mon Sep 17 00:00:00 2001 From: esopp Date: Tue, 21 Apr 2020 22:06:49 -0400 Subject: [PATCH 01/14] Add 'isArchived' to Rooms model. Enable filtering by 'isArchived' on the community page. Add option to filter by 'isTrashed,' but this option is not functional yet. --- client/src/Containers/Community.js | 9 +++++-- client/src/Layout/BoxList/BoxList.js | 4 +-- client/src/Layout/Community/Community.js | 33 ++++++++++++++++++++++++ client/src/utils/apiRequests.js | 3 ++- server/controllers/RoomController.js | 8 +++++- server/models/Room.js | 1 + server/routes/api.js | 3 ++- 7 files changed, 54 insertions(+), 7 deletions(-) diff --git a/client/src/Containers/Community.js b/client/src/Containers/Community.js index 2b70f00bf..54168dfe2 100644 --- a/client/src/Containers/Community.js +++ b/client/src/Containers/Community.js @@ -127,6 +127,10 @@ class Community extends Component { filters.roomType = 'all'; } else if (filter === 'all-privacySetting') { filters.privacySetting = 'all'; + } else if (filter === 'isArchived' || filter === 'isTrashed') { + filters.roomStatus = filter; + } else if (filter === 'default-roomStatus') { + filters.roomStatus = 'default'; } if (resource === 'courses') { filters.roomType = null; @@ -143,17 +147,18 @@ class Community extends Component { privacySetting: params.get('privacy'), roomType: params.get('roomType'), search: params.get('search'), + roomStatus: params.get('roomStatus'), }; return filters; }; setQueryParams = (filters) => { const { history, match } = this.props; - const { privacySetting, roomType, search } = filters; + const { privacySetting, roomType, roomStatus, search } = filters; history.push({ pathname: match.url, search: `?privacy=${privacySetting || 'all'}&roomType=${roomType || - 'all'}&search=${search || ''}`, + 'all'}&roomStatus=${roomStatus || 'default'}&search=${search || ''}`, }); }; diff --git a/client/src/Layout/BoxList/BoxList.js b/client/src/Layout/BoxList/BoxList.js index 18d0c13df..57f795b24 100644 --- a/client/src/Layout/BoxList/BoxList.js +++ b/client/src/Layout/BoxList/BoxList.js @@ -54,7 +54,7 @@ const boxList = (props) => {
{!draggable ? ( { ) : ( ) : null} + {resource === 'rooms' ? ( + } + > +
+ toggleFilter('default-roomStatus')} + checked={filters.roomStatus === 'default'} + name="Default-roomStatus" + > + Active + + toggleFilter('isArchived')} + checked={filters.roomStatus === 'isArchived'} + name="isArchived" + > + Archived + + toggleFilter('isTrashed')} + checked={filters.roomStatus === 'isTrashed'} + name="isTrashed" + > + Trashed + +
+
+ ) : null}
{ - const { privacySetting, roomType } = filters; + const { privacySetting, roomType, roomStatus } = filters; const params = criteria ? { criteria, skip } : { skip }; if (privacySetting !== null) params.privacySetting = privacySetting; if (roomType !== null) params.roomType = roomType; + if (roomStatus !== null) params.roomStatus = roomStatus; return api.get(`/api/searchPaginated/${resource}`, { params }); }, diff --git a/server/controllers/RoomController.js b/server/controllers/RoomController.js index 2956a5f2a..ac454313a 100644 --- a/server/controllers/RoomController.js +++ b/server/controllers/RoomController.js @@ -96,7 +96,9 @@ module.exports = { }, searchPaginated: async (criteria, skip, filters) => { - const initialFilter = { tempRoom: false, isTrashed: false }; + let isTrashed = filters.roomStatus ? (filters.roomStatus == 'isTrashed') : false; + let isArchived = filters.roomStatus ? (filters.roomStatus == 'isArchived') : false; + const initialFilter = { tempRoom: false, isTrashed: isTrashed, isArchived: isArchived }; const allowedPrivacySettings = ['private', 'public']; @@ -116,6 +118,7 @@ module.exports = { tabs: 1, privacySetting: 1, updatedAt: 1, + isArchived: 1, members: { $filter: { input: '$members', @@ -159,6 +162,7 @@ module.exports = { instructions: { $first: '$instructions' }, description: { $first: '$description' }, privacySetting: { $first: '$privacySetting' }, + isArchived: { $first: '$isArchived' }, image: { $first: '$image' }, tabs: { $first: '$tabs' }, updatedAt: { $first: '$updatedAt' }, @@ -182,6 +186,7 @@ module.exports = { name: { $first: '$name' }, instructions: { $first: '$instructions' }, description: { $first: '$description' }, + isArchived: { $first: '$isArchived' }, privacySetting: { $first: '$privacySetting' }, image: { $first: '$image' }, updatedAt: { $first: '$updatedAt' }, @@ -199,6 +204,7 @@ module.exports = { 'tabs.tabType': 1, privacySetting: 1, updatedAt: 1, + isArchived: 1, 'members.role': 1, 'members.user.username': 1, 'members.user._id': 1, diff --git a/server/models/Room.js b/server/models/Room.js index 747cb82ba..cb92c71e0 100644 --- a/server/models/Room.js +++ b/server/models/Room.js @@ -48,6 +48,7 @@ const Room = new mongoose.Schema( graphImage: { type: ObjectId, ref: 'Image' }, controlledBy: { type: ObjectId, ref: 'User', default: null }, // wasNew: {type: Boolean}, + isArchived: { type: Boolean, default: false }, isTrashed: { type: Boolean, default: false }, }, { timestamps: true } diff --git a/server/routes/api.js b/server/routes/api.js index 1233477af..4f29d770a 100644 --- a/server/routes/api.js +++ b/server/routes/api.js @@ -60,7 +60,7 @@ router.get('/search/:resource', (req, res) => { router.get('/searchPaginated/:resource', (req, res) => { const { resource } = req.params; const controller = controllers[resource]; - const { criteria, skip, privacySetting, roomType } = req.query; + const { criteria, skip, privacySetting, roomType, roomStatus } = req.query; let regex; if (criteria) { regex = new RegExp(criteria, 'i'); @@ -68,6 +68,7 @@ router.get('/searchPaginated/:resource', (req, res) => { const filters = {}; if (privacySetting) filters.privacySetting = privacySetting; if (roomType) filters.roomType = roomType; + if (roomStatus) filters.roomStatus = roomStatus; controller .searchPaginated(regex, skip, filters) .then((results) => { From 5299ed97bf284193ffd76bcdbd220af5a81e7e85 Mon Sep 17 00:00:00 2001 From: esopp Date: Wed, 22 Apr 2020 14:09:29 -0400 Subject: [PATCH 02/14] One time migration to add 'isArchived' field to existing Room records. Run with NODE_ENV=dev (or NODE_ENV=production/NODE_ENV=staging as suits the environment) --- server/db_migration/addIsArchivedToRooms.js | 74 +++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 server/db_migration/addIsArchivedToRooms.js diff --git a/server/db_migration/addIsArchivedToRooms.js b/server/db_migration/addIsArchivedToRooms.js new file mode 100644 index 000000000..0ba541513 --- /dev/null +++ b/server/db_migration/addIsArchivedToRooms.js @@ -0,0 +1,74 @@ +//To run in dev mode: +//NODE_ENV=dev node addIsArchivedToRooms.js +// +//staging mode: +//NODE_ENV=staging node addIsArchivedToRooms.js +// +//prod mode: +//NODE_ENV=production node addIsArchivedToRooms.js +const mongoose = require('mongoose'); +const models = require('../models'); +const { Room } = models; +require('dotenv').config({ path: '../.env' }); + +/** + * Connect with the version of the database + * to be updated (e.g., production, dev, etc). +**/ +console.log("NODE_ENV=", process.env.NODE_ENV) +console.log("MONGO_DEV_URI=", process.env.MONGO_DEV_URI) +let mongoURI; +if (process.env.NODE_ENV === 'dev') { + mongoURI = process.env.MONGO_DEV_URI; +} else if (process.env.TRAVIS) { + mongoURI = process.env.MONGO_TEST_URI; +} else if (process.env.NODE_ENV === 'production') { + mongoURI = process.env.MONGO_PROD_URI; +} else if (process.env.NODE_ENV === 'staging') { + mongoURI = process.env.MONGO_STAGING_URI; +} else if (process.env.NODE_ENV === 'test') { + mongoURI = process.env.MONGO_TEST_URI; +} +console.log("mongoURI:", mongoURI); +mongoose.connect(mongoURI, { useNewUrlParser: true }, (err) => { + if (err) { + console.log(`DB CONNECTION FAILED: ${err}`); + } else { + console.log(`DB CONNECTION SUCCESS${mongoURI}`); + } +}); + +/** + * [addIsArchivedToRooms] + * Set the "isArchived" field in all Room records + * for which that field is not yet defined. + * @return a Promise + */ +function addIsArchivedToRooms() { + return new Promise((resolve, reject) => { + Room.updateMany( + { isArchived: { $exists: false } }, + { $set: { isArchived: false } } + ) + .exec() + .then((results) => { + if (results) { + const { n, nModified } = results; + console.log( + `Found ${n} Rooms without isArchived field and updated ${nModified} of them.` + ); + } + resolve(results); + }); + }); +} + +addIsArchivedToRooms() + .then(() => { + mongoose.connection.close(); + console.log("Closed db connection.") + }) + .catch((err) => { + console.error('something went wrong: ', err); + mongoose.connection.close(); + }); \ No newline at end of file From e8c9785f234b4d9a6ac29c6be39e2c4fc14e5c41 Mon Sep 17 00:00:00 2001 From: esopp Date: Thu, 23 Apr 2020 00:51:51 -0400 Subject: [PATCH 03/14] Only show the 'trashed rooms' filter option to admin users on the community page --- client/src/Containers/Community.js | 3 +- client/src/Layout/Community/Community.js | 89 +++++++++++++++--------- 2 files changed, 58 insertions(+), 34 deletions(-) diff --git a/client/src/Containers/Community.js b/client/src/Containers/Community.js index 54168dfe2..a089a6b38 100644 --- a/client/src/Containers/Community.js +++ b/client/src/Containers/Community.js @@ -171,7 +171,7 @@ class Community extends Component { }; render() { - const { match } = this.props; + const { match, user } = this.props; const { visibleResources, moreAvailable, searchText } = this.state; const filters = this.getQueryParams(); let linkPath; @@ -199,6 +199,7 @@ class Community extends Component { moreAvailable={moreAvailable} filters={filters} toggleFilter={this.toggleFilter} + user={user} /> ); } diff --git a/client/src/Layout/Community/Community.js b/client/src/Layout/Community/Community.js index c0ba82f7f..8b4063e96 100644 --- a/client/src/Layout/Community/Community.js +++ b/client/src/Layout/Community/Community.js @@ -36,7 +36,61 @@ class Community extends Component { setCriteria, toggleFilter, searchValue, + user, } = this.props; + + const roomStatusInfoBox = user.isAdmin ? ( + }> +
+ toggleFilter('default-roomStatus')} + checked={filters.roomStatus === 'default'} + name="Default-roomStatus" + > + Active + + toggleFilter('isArchived')} + checked={filters.roomStatus === 'isArchived'} + name="isArchived" + > + Archived + + toggleFilter('isTrashed')} + checked={filters.roomStatus === 'isTrashed'} + name="isTrashed" + > + Trashed + +
+
+ ) : ( + }> +
+ toggleFilter('default-roomStatus')} + checked={filters.roomStatus === 'default'} + name="Default-roomStatus" + > + Active + + toggleFilter('isArchived')} + checked={filters.roomStatus === 'isArchived'} + name="isArchived" + > + Archived + +
+
+ ); + return (
@@ -132,39 +186,7 @@ class Community extends Component {
) : null} - {resource === 'rooms' ? ( - } - > -
- toggleFilter('default-roomStatus')} - checked={filters.roomStatus === 'default'} - name="Default-roomStatus" - > - Active - - toggleFilter('isArchived')} - checked={filters.roomStatus === 'isArchived'} - name="isArchived" - > - Archived - - toggleFilter('isTrashed')} - checked={filters.roomStatus === 'isTrashed'} - name="isTrashed" - > - Trashed - -
-
- ) : null} + {resource === 'rooms' ? roomStatusInfoBox : null}
Date: Thu, 23 Apr 2020 16:48:41 -0400 Subject: [PATCH 04/14] add filtering by isArchived on MyVMT page. Hide the 'Enter' room button for archived rooms. --- client/src/Components/Search/Search.js | 6 +- client/src/Containers/MyVMT.js | 4 +- client/src/Containers/Room.js | 62 ++++++++----- .../Dashboard/MainContent/ResourceList.js | 88 ++++++++++++++----- .../Dashboard/MainContent/resourceList.css | 42 +++++++++ 5 files changed, 152 insertions(+), 50 deletions(-) diff --git a/client/src/Components/Search/Search.js b/client/src/Components/Search/Search.js index 017311ad3..984a3b6ab 100644 --- a/client/src/Components/Search/Search.js +++ b/client/src/Components/Search/Search.js @@ -18,6 +18,7 @@ class Search extends Component { value, isControlled, 'data-testid': dataTestId, + roomStatus, } = this.props; return (
@@ -29,7 +30,7 @@ class Search extends Component { className={[classes.Input, classes[theme]].join(' ')} type="text" placeholder={placeholder} - onChange={(event) => _search(event.target.value)} + onChange={(event) => _search(event.target.value, roomStatus)} /> ) : ( _search(event.target.value)} + onChange={(event) => _search(event.target.value, roomStatus)} /> )} @@ -54,6 +55,7 @@ Search.propTypes = { _search: PropTypes.func.isRequired, isControlled: PropTypes.bool, 'data-testid': PropTypes.string.isRequired, + roomStatus: PropTypes.string.isRequired, }; Search.defaultProps = { diff --git a/client/src/Containers/MyVMT.js b/client/src/Containers/MyVMT.js index 7f2504eb9..67753f0ba 100644 --- a/client/src/Containers/MyVMT.js +++ b/client/src/Containers/MyVMT.js @@ -82,7 +82,7 @@ class MyVMT extends Component { }; render() { - const { user, match } = this.props; + const { user, match, location } = this.props; const { bothRoles, view, tabs } = this.state; const { resource } = match.params; @@ -128,6 +128,7 @@ class MyVMT extends Component { } user={user} resource={resource} + location={location} /> } tabs={} @@ -139,6 +140,7 @@ class MyVMT extends Component { MyVMT.propTypes = { match: PropTypes.shape({}).isRequired, user: PropTypes.shape({}).isRequired, + location: PropTypes.shape({}).isRequired, }; // @NB THE LACK OF CAMEL CASE HERE IS INTENTIONAL AND ALLOWS US TO AVOID LOTS diff --git a/client/src/Containers/Room.js b/client/src/Containers/Room.js index aa94c2a5c..b191418b1 100644 --- a/client/src/Containers/Room.js +++ b/client/src/Containers/Room.js @@ -333,6 +333,43 @@ class Room extends Component { const dueDateText = 'Due Date'; // the fact that we have to do this make this not worth it let ggb = false; let desmos = false; + const sideBarNavButtons = room.isArchived ? ( + + + + + + ) : ( + + + + + + + + + ); room.tabs.forEach((tab) => { if (tab.tabType === 'geogebra') ggb = true; else if (tab.tabType === 'desmos') desmos = true; @@ -489,30 +526,7 @@ class Room extends Component { } owner={room.myRole === 'facilitator'} additionalDetails={additionalDetails} - buttons={ - - - - - - - - - } + buttons={sideBarNavButtons} editButton={ room.myRole === 'facilitator' || isAdmin ? ( diff --git a/client/src/Layout/Dashboard/MainContent/ResourceList.js b/client/src/Layout/Dashboard/MainContent/ResourceList.js index 0ee8c6ec9..b6355a0ff 100644 --- a/client/src/Layout/Dashboard/MainContent/ResourceList.js +++ b/client/src/Layout/Dashboard/MainContent/ResourceList.js @@ -5,47 +5,49 @@ import BoxList from '../../BoxList/BoxList'; import NewResource from '../../../Containers/Create/NewResource/NewResource'; import classes from './resourceList.css'; import Search from '../../../Components/Search/Search'; +import { RadioBtn, InfoBox } from '../../../Components'; // CONSIDER RENAMING TO DASHBOARDCONTENT class ResourceList extends Component { state = { participantList: [], facilitatorList: [], + roomStatus: 'default', }; componentDidMount() { - const { userResources } = this.props; - const { facilitatorList, participantList } = this.sortUserResources( - userResources - ); - this.setState({ - facilitatorList, - participantList, - }); + this.filterByRoomStatus('default'); } componentDidUpdate(prevProps) { const { userResources } = this.props; if (prevProps.userResources !== userResources) { - const { facilitatorList, participantList } = this.sortUserResources( - userResources - ); - this.setState({ - facilitatorList, - participantList, - }); + const { roomStatus } = this.state; + this.filterByRoomStatus(roomStatus); } } - search = (criteria) => { - const { userResources } = this.props; + search = (criteria, roomStatus) => { + const { userResources, resource } = this.props; let { facilitatorList, participantList } = this.sortUserResources( userResources ); - facilitatorList = facilitatorList.filter((resource) => { - return resource.name.indexOf(criteria) > -1; + console.log(JSON.stringify(facilitatorList)); + const isArchived = roomStatus === 'isArchived'; + const isTrashed = roomStatus === 'isTrashed'; + const noStateFilter = resource !== 'rooms'; + facilitatorList = facilitatorList.filter((rec) => { + return ( + rec.name.indexOf(criteria) > -1 && + (noStateFilter || + (rec.isArchived === isArchived && rec.isTrashed === isTrashed)) + ); }); - participantList = participantList.filter((resource) => { - return resource.name.indexOf(criteria) > -1; + participantList = participantList.filter((rec) => { + return ( + rec.name.indexOf(criteria) > -1 && + (noStateFilter || + (rec.isArchived === isArchived && rec.isTrashed === isTrashed)) + ); }); this.setState({ facilitatorList, @@ -53,6 +55,18 @@ class ResourceList extends Component { }); }; + filterByRoomStatus = (status) => { + const allowedRoomStatus = ['default', 'isArchived', 'isTrashed']; + let { roomStatus } = this.state; + if (allowedRoomStatus.indexOf(status) > -1) { + roomStatus = status; + } + this.setState({ + roomStatus, + }); + this.search('', roomStatus); + }; + sortUserResources = (resources) => { const facilitatorList = []; const participantList = []; @@ -77,7 +91,7 @@ class ResourceList extends Component { user, notifications, } = this.props; - const { facilitatorList, participantList } = this.state; + const { facilitatorList, participantList, roomStatus } = this.state; let linkPath = `/myVMT/${resource}/`; let linkSuffix; if (resource === 'courses') { @@ -110,10 +124,38 @@ class ResourceList extends Component { {/* @TODO don't show create optinos for participants */}
- +
{create}
+
+ {resource === 'rooms' ? ( + }> +
+ this.filterByRoomStatus('default')} + checked={roomStatus === 'default'} + name="Default-roomStatus" + > + Active + + this.filterByRoomStatus('isArchived')} + checked={roomStatus === 'isArchived'} + name="isArchived" + > + Archived + +
+
+ ) : null} +
{facilitatorList.length > 0 && participantList.length > 0 ? (
diff --git a/client/src/Layout/Dashboard/MainContent/resourceList.css b/client/src/Layout/Dashboard/MainContent/resourceList.css index 4a101eeee..99cc1b853 100644 --- a/client/src/Layout/Dashboard/MainContent/resourceList.css +++ b/client/src/Layout/Dashboard/MainContent/resourceList.css @@ -33,6 +33,48 @@ transition: 0.2s; } +.Filters { + display: flex; + flex-flow: row wrap; + align-items: center; +} + +.Filters > * { + margin-right: 12px; +} + +.FilterIcon { + font-size: 50px; + color: darkGray; + flex: 0; +} + +.FilterGroup { + display: flex; + flex-direction: column; + margin-left: 26px; + border: 1px solid lightGray; + /* flex: 2; */ +} + +.FilterTitle { + color: #5e5e5e; + padding: 4px; + border-bottom: 1px solid #5e5e5e; + margin-bottom: 4px; +} + +.FilterOpts { + padding-left: 12px; + display: flex; + /* justify-content: space-between; */ + /* flex: 1; */ +} + +.FilterOpts > * { + margin-right: 8px; +} + @media screen and (max-width: 800px) { .Row { flex-flow: column; From a69b8fd29500ad9dab49e7e9acfabea85a8beb82 Mon Sep 17 00:00:00 2001 From: esopp Date: Fri, 24 Apr 2020 16:19:46 -0400 Subject: [PATCH 05/14] functioning 'archive' button for Rooms in 'edit room' mode --- .../src/Components/UI/Modal/ArchiveModal.js | 62 +++++++++++++++++++ client/src/Components/index.js | 1 + client/src/Containers/Room.js | 45 +++++++++++--- client/src/store/actions/actionTypes.js | 1 + client/src/store/actions/rooms.js | 9 +++ client/src/store/reducers/roomsReducer.js | 8 +++ 6 files changed, 119 insertions(+), 7 deletions(-) create mode 100644 client/src/Components/UI/Modal/ArchiveModal.js diff --git a/client/src/Components/UI/Modal/ArchiveModal.js b/client/src/Components/UI/Modal/ArchiveModal.js new file mode 100644 index 000000000..a256f1e18 --- /dev/null +++ b/client/src/Components/UI/Modal/ArchiveModal.js @@ -0,0 +1,62 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import Modal from './Modal'; +import classes from './modal.css'; +import Button from '../Button/Button'; + +// NOT READY FOR USE WITH COURSES OR ACTIVITIES +const hash = { + course: 'courses', + room: 'rooms', + activity: 'activities', +}; +class ArchiveModal extends Component { + archiveResource = () => { + const { history, update, closeModal, resource, resourceId } = this.props; + history.push(`/myVMT/${hash[resource]}`); + update(resourceId, { isArchived: true }); + closeModal(); + }; + + // archiveResourceAndChildren = () => { + // const { history, update, closeModal, resource, resourceId } = this.props; + // update(resourceId, { + // isArchived: true, + // archiveChildren: true, + // }); + // closeModal(); + // history.push(`/myVMT/${hash[resource]}`); + // }; + + render() { + const { show, closeModal, resource } = this.props; + return ( + +
{`Are you sure you want to archive this ${resource}`}?
+
+ + +
+
+ ); + } +} + +ArchiveModal.propTypes = { + history: PropTypes.shape({}).isRequired, + update: PropTypes.func.isRequired, + show: PropTypes.bool.isRequired, + closeModal: PropTypes.func.isRequired, + resource: PropTypes.string.isRequired, + resourceId: PropTypes.string.isRequired, +}; +export default ArchiveModal; diff --git a/client/src/Components/index.js b/client/src/Components/index.js index 107b5a322..917f00a96 100644 --- a/client/src/Components/index.js +++ b/client/src/Components/index.js @@ -34,6 +34,7 @@ export { default as EditableText } from './EditableText/EditableText'; export { default as EditText } from './EditableText/EditText'; export { default as SelectionList } from './Form/SelectionList/SelectionList'; export { default as TrashModal } from './UI/Modal/TrashModal'; +export { default as ArchiveModal } from './UI/Modal/ArchiveModal'; export { default as Error } from './HOC/Error'; export { default as ErrorToast } from './Error/ErrorToast'; export { default as DemoBrowser } from './DemoBrowser/DemoBrowser'; diff --git a/client/src/Containers/Room.js b/client/src/Containers/Room.js index b191418b1..286c3c24d 100644 --- a/client/src/Containers/Room.js +++ b/client/src/Containers/Room.js @@ -34,6 +34,7 @@ import { TabList, EditText, TrashModal, + ArchiveModal, Error, } from '../Components'; import Access from './Access'; @@ -62,6 +63,7 @@ class Room extends Component { instructions: room ? room.instructions : null, privacySetting: room ? room.privacySetting : null, trashing: false, + archiving: false, isAdmin: false, }; } @@ -269,6 +271,10 @@ class Room extends Component { this.setState({ trashing: true }); }; + archiveRoom = () => { + this.setState({ archiving: true }); + }; + clearFirstViewModal = () => { this.setState({ firstView: false, invited: false }); }; @@ -327,6 +333,7 @@ class Room extends Component { firstView, name, trashing, + archiving, } = this.state; if (room && room.tabs && !guestMode) { // ESLINT thinks this is unnecessary but we use the keys directly in the dom and we want them to have spaces @@ -557,13 +564,24 @@ class Room extends Component { > Save - + {!room.isTrashed ? ( + + ) : null} + {!room.isArchived ? ( + + ) : null} @@ -631,6 +649,19 @@ class Room extends Component { history={history} /> ) : null} + {archiving ? ( + { + this.setState({ archiving: false }); + }} + history={history} + /> + ) : null} + {connectUpdateRoom.toString()} ); } diff --git a/client/src/store/actions/actionTypes.js b/client/src/store/actions/actionTypes.js index c71e41ef1..b49ed9b56 100644 --- a/client/src/store/actions/actionTypes.js +++ b/client/src/store/actions/actionTypes.js @@ -70,6 +70,7 @@ export const DESTROY_ROOM = 'DESTROY_ROOM'; export const UPDATED_ROOM_TAB = 'UPDATED_ROOM_TAB'; export const ADD_TO_LOG = 'ADD_TO_LOG'; export const REMOVE_ROOMS = 'REMOVE_ROOMS'; +export const ARCHIVE_ROOMS = 'ARCHIVE_ROOMS'; // COURSE TEMPLATES export const GOT_COURSE_TEMPLATES = 'GOT_COURSE_TEMPLATES'; export const GET_COURSE_TEMPLATES = 'GET_COURSE_TEMPLATES'; diff --git a/client/src/store/actions/rooms.js b/client/src/store/actions/rooms.js index fc7361b99..403378d9b 100644 --- a/client/src/store/actions/rooms.js +++ b/client/src/store/actions/rooms.js @@ -48,6 +48,13 @@ export const roomsRemoved = (roomIds) => { }; }; +export const roomsArchived = (roomIds) => { + return { + type: actionTypes.ARCHIVE_ROOMS, + roomIds, + }; +}; + export const removedRoom = (id) => { return { type: actionTypes.REMOVE_ROOM, @@ -208,6 +215,8 @@ export const updateRoom = (id, body) => { if (body.isTrashed) { dispatch(removeUserRooms([id])); dispatch(roomsRemoved([id])); + } else if (body.isArchived) { + dispatch(updatedRoom(id, body)); } else { dispatch(updatedRoom(id, body)); // Optimistically update the UI } diff --git a/client/src/store/reducers/roomsReducer.js b/client/src/store/reducers/roomsReducer.js index 520504e8a..ceaefe2d6 100644 --- a/client/src/store/reducers/roomsReducer.js +++ b/client/src/store/reducers/roomsReducer.js @@ -140,6 +140,14 @@ const reducer = (state = initialState, action) => { allIds: updatedIds, }; } + case actionTypes.ARCHIVE_ROOMS: { + const updatedRoom = { ...state.byId[action.roomId] }; + updatedRoom.isArchived = true; + return { + ...state, + byId: updatedRoom, + }; + } case actionTypes.CREATE_ROOM_CONFIRMED: return { ...state, From ae4044179e060ac259400950d5e219360c94f849 Mon Sep 17 00:00:00 2001 From: esopp Date: Tue, 28 Apr 2020 00:19:23 -0400 Subject: [PATCH 06/14] working 'restore room' button for archived rooms --- .../src/Components/UI/Modal/ArchiveModal.js | 26 +++- client/src/Containers/Room.js | 141 +++++++++++------- .../Dashboard/MainContent/ResourceList.js | 1 - client/src/store/actions/actionTypes.js | 1 + client/src/store/reducers/roomsReducer.js | 9 ++ 5 files changed, 121 insertions(+), 57 deletions(-) diff --git a/client/src/Components/UI/Modal/ArchiveModal.js b/client/src/Components/UI/Modal/ArchiveModal.js index a256f1e18..be0231a7b 100644 --- a/client/src/Components/UI/Modal/ArchiveModal.js +++ b/client/src/Components/UI/Modal/ArchiveModal.js @@ -12,9 +12,19 @@ const hash = { }; class ArchiveModal extends Component { archiveResource = () => { - const { history, update, closeModal, resource, resourceId } = this.props; + const { + history, + update, + closeModal, + resource, + resourceId, + restoring, + } = this.props; + const updateBody = restoring + ? { isArchived: false, isTrashed: false } + : { isArchived: true }; history.push(`/myVMT/${hash[resource]}`); - update(resourceId, { isArchived: true }); + update(resourceId, updateBody); closeModal(); }; @@ -29,10 +39,14 @@ class ArchiveModal extends Component { // }; render() { - const { show, closeModal, resource } = this.props; + const { show, closeModal, resource, restoring } = this.props; + const actionName = restoring ? 'restore' : 'archive'; + const iconClass = restoring ? 'fas fa-undo' : 'fas fa-archive'; return ( -
{`Are you sure you want to archive this ${resource}`}?
+
+ Are you sure you want to {actionName} this {resource}? +
- - - ) : ( - - - - - - - - - ); + const editOrRestoreButton = + room.isArchived || room.isTrashed ? ( +
+ Restore Room +
+ ) : ( +
+ Edit Room +
+ ); + const sideBarNavButtons = + room.isArchived || room.isTrashed ? ( + + {!room.isTrashed ? ( + + + + ) : null} + + ) : ( + + + + + + + + + ); room.tabs.forEach((tab) => { if (tab.tabType === 'geogebra') ggb = true; else if (tab.tabType === 'desmos') desmos = true; @@ -537,18 +574,7 @@ class Room extends Component { editButton={ room.myRole === 'facilitator' || isAdmin ? ( -
- Edit Room -
+ {editOrRestoreButton} {editing ? ( // @TODO this should be a resuable component
) : null} - {!room.isArchived ? ( + {!room.isArchived && !room.isTrashed ? ( ) : null}
diff --git a/deploy-mac.sh b/deploy-mac.sh index 840e2f3a2..f3eaf8f04 100644 --- a/deploy-mac.sh +++ b/deploy-mac.sh @@ -1,5 +1,5 @@ #!/bin/bash -echo -e "\e[1;33m Making a $2 build" +echo -e "\e[1;33m Making a $1 build" echo -e "\e[1;30m" if [ -d "./client/build" ]; then rm -rf ./client/build @@ -8,24 +8,24 @@ if [ -d "./client/encompassBuild" ]; then rm -rf ./client/encompassBuild fi cd ./server -npm run build-$2 -npm run build-enc-$2 +npm run build-$1 +npm run build-enc-$1 cd .. -if [ -d "./$2" ]; then - rm -rf ./$2 +if [ -d "./$1" ]; then + rm -rf ./$1 fi -mkdir ./$2 -cd ./$2 +mkdir ./$1 +cd ./$1 mkdir ./server cd .. -echo "made $2 directory" +echo "made $1 directory" cd ./server -cp -r ./bin ./config ./constants ./controllers ./db_migration ./middleware ./models ./routes ./services ../$2/server -echo "\e[1;33m server copied to $2" -cp app.js package.json socketInit.js sockets.js ../$2/server -cp .env ../$2 +cp -r ./bin ./config ./constants ./controllers ./db_migration ./middleware ./models ./routes ./services ../$1/server +echo "\e[1;33m server copied to $1" +cp app.js package.json socketInit.js sockets.js ../$1/server +cp .env ../$1 echo "\e[1;33mfiles copied" -cd ../$2 +cd ../$1 mkdir client mkdir client/build mkdir client/encompassBuild @@ -34,15 +34,5 @@ cp -r ../client/build/ client/build cp -r ../client/encompassBuild/ client/encompassBuild echo -e "\e[1;33m Zipping...\e[0m" -zip -r VMT-$2.zip . -echo -e "\e[1;33m Copying zipped directory...\e[0m" -scp ./VMT-$2.zip "$1"@mathematicalthinking.org:/tmp/ -echo -e "\e[1;31m You're not done yet!\e[0m" -echo -e "\e[1;35m run the following commands to complete the demployment proces" -echo -e "\e[1;34m $ \e[0m ssh $1@mathematicalthinking" -echo -e "\e[1;34m $ \e[0m cd /web/mathematicalthinking/vmt" -echo -e "\e[1;34m $ \e[0m rm -rf staging && mkdir staging && cd staging" -echo -e "\e[1;34m $ \e[0m mv /tmp/VMT-$2.zip ." -echo -e "\e[1;34m $ \e[0m unzip VMT-$2.zip" -echo -e "\e[1;34m $ \e[0m cd ./server && npm install --only=production" -echo -e "\e[1;34m $ \e[0m systemctl restart vmt-$2" +zip -r VMT-$1.zip . + From b826f1650491d75365f8fcbebe950d738b81cb09 Mon Sep 17 00:00:00 2001 From: esopp Date: Mon, 22 Jun 2020 12:11:15 -0400 Subject: [PATCH 10/14] automatically set filter for room state on community page. don't show 'restore'button for trashed rooms (this may be possible to add, but some analysis may be needed for how to treat data that is purged when the room is trashed). --- .../Components/Navigation/HomeNav/HomeNav.js | 2 +- client/src/Components/Navigation/Navbar.js | 2 +- client/src/Containers/Community.js | 3 ++ client/src/Containers/Room.js | 29 +++++++++++-------- client/src/Layout/Community/Community.js | 2 ++ 5 files changed, 24 insertions(+), 14 deletions(-) diff --git a/client/src/Components/Navigation/HomeNav/HomeNav.js b/client/src/Components/Navigation/HomeNav/HomeNav.js index a845ab412..92fa46be6 100644 --- a/client/src/Components/Navigation/HomeNav/HomeNav.js +++ b/client/src/Components/Navigation/HomeNav/HomeNav.js @@ -55,7 +55,7 @@ const Navbar = ({ page, user, loggedIn, isDark, toggleAdmin }) => {
)} {user.isAdmin ? ( diff --git a/client/src/Components/Navigation/Navbar.js b/client/src/Components/Navigation/Navbar.js index 8244b0480..e3d71e3b1 100644 --- a/client/src/Components/Navigation/Navbar.js +++ b/client/src/Components/Navigation/Navbar.js @@ -49,7 +49,7 @@ const Navbar = ({ user, location, toggleAdmin }) => {
    {user.isAdmin ? ( diff --git a/client/src/Containers/Community.js b/client/src/Containers/Community.js index a089a6b38..6575d4934 100644 --- a/client/src/Containers/Community.js +++ b/client/src/Containers/Community.js @@ -85,6 +85,9 @@ class Community extends Component { if (updatedFilters.privacySetting === 'all') { delete updatedFilters.privacySetting; } + if (updatedFilters.roomStatus === 'default') { + delete updatedFilters.roomStatus; + } API.searchPaginated( resource, updatedFilters.search, diff --git a/client/src/Containers/Room.js b/client/src/Containers/Room.js index f28968e76..c83b5bcff 100644 --- a/client/src/Containers/Room.js +++ b/client/src/Containers/Room.js @@ -346,20 +346,25 @@ class Room extends Component { const dueDateText = 'Due Date'; // the fact that we have to do this make this not worth it let ggb = false; let desmos = false; + const restoreButton = room.isArchived ? ( +
    + Restore Room +
    + ) : ( +
    + ); const editOrRestoreButton = room.isArchived || room.isTrashed ? ( -
    - Restore Room -
    + restoreButton ) : (
    setCriteria(value)} + roomStatus="" placeholder="Search by name, description, or facilitators..." data-testid="community-search" /> @@ -224,6 +225,7 @@ Community.propTypes = { filters: PropTypes.shape({ privacySetting: PropTypes.oneOf(['public', 'private', 'all']), roomType: PropTypes.oneOf(['geogebra', 'desmos', 'all']), + roomStatus: PropTypes.oneOf(['default', 'isArchived', 'isTrashed']), }).isRequired, toggleFilter: PropTypes.func.isRequired, setSkip: PropTypes.func.isRequired, From 9ded8460efdd13a9c4514f396f2b31f5677b6ab6 Mon Sep 17 00:00:00 2001 From: esopp Date: Mon, 22 Jun 2020 14:03:35 -0400 Subject: [PATCH 11/14] passes existing tests from master branch --- client/src/Containers/Room.js | 4 +++- client/src/Layout/Community/Community.js | 2 +- server/cypress/integration/community.js | 4 ++-- server/seeders/rooms.js | 14 ++++++++++++++ 4 files changed, 20 insertions(+), 4 deletions(-) diff --git a/client/src/Containers/Room.js b/client/src/Containers/Room.js index c83b5bcff..36ff929be 100644 --- a/client/src/Containers/Room.js +++ b/client/src/Containers/Room.js @@ -716,7 +716,9 @@ class Room extends Component { return ( - history.push('/community/rooms?privacy=all&roomType=all') + history.push( + '/community/rooms?privacy=all&roomType=all&roomStatus=default' + ) } resource="rooms" resourceId={match.params.room_id} diff --git a/client/src/Layout/Community/Community.js b/client/src/Layout/Community/Community.js index 92a68fb8f..07af056f4 100644 --- a/client/src/Layout/Community/Community.js +++ b/client/src/Layout/Community/Community.js @@ -99,7 +99,7 @@ class Community extends Component {
    - + Rooms
    diff --git a/server/cypress/integration/community.js b/server/cypress/integration/community.js index 06a4071fb..3f5dca6b6 100644 --- a/server/cypress/integration/community.js +++ b/server/cypress/integration/community.js @@ -307,7 +307,7 @@ describe('test community search and filter', function() { doSearch('reference'); cy.url().should( 'include', - 'community/rooms?privacy=all&roomType=all&search=reference' + 'community/rooms?privacy=all&roomType=all&roomStatus=default&search=reference' ); checkItemsLength(1); cy.contains('reference room').should('exist'); @@ -333,7 +333,7 @@ describe('test community search and filter', function() { .type('reference tool'); cy.url().should( 'include', - 'community/rooms?privacy=all&roomType=all&search=reference%20tool' + 'community/rooms?privacy=all&roomType=all&roomStatus=default&search=reference%20tool' ); cy.getTestElement('box-list') .children() diff --git a/server/seeders/rooms.js b/server/seeders/rooms.js index 3b0dd8305..af15673d1 100644 --- a/server/seeders/rooms.js +++ b/server/seeders/rooms.js @@ -17,6 +17,7 @@ module.exports = [ tempRoom: false, controlledBy: null, isTrashed: false, + isArchived: false, name: 'room 1', description: 'hello', members: [ @@ -52,6 +53,7 @@ module.exports = [ tempRoom: false, controlledBy: null, isTrashed: false, + isArchived: false, name: 'request access', description: 'hello', members: [ @@ -85,6 +87,7 @@ module.exports = [ tempRoom: false, controlledBy: null, isTrashed: false, + isArchived: false, name: 'wrong entry code room', description: 'hello', entryCode: 'hello', @@ -118,6 +121,7 @@ module.exports = [ tempRoom: false, controlledBy: null, isTrashed: false, + isArchived: false, name: "Deanna's stand alone room", description: '', creator: ObjectId('5be1eba75854270cd0920faa'), @@ -152,6 +156,7 @@ module.exports = [ tempRoom: false, controlledBy: null, isTrashed: false, + isArchived: false, name: 'room 2', description: 'hello', entryCode: 'hello', @@ -191,6 +196,7 @@ module.exports = [ tempRoom: false, controlledBy: null, isTrashed: false, + isArchived: false, name: "Deanna's course 1 room", description: '', creator: ObjectId('5be1eba75854270cd0920faa'), @@ -226,6 +232,7 @@ module.exports = [ tempRoom: false, controlledBy: null, isTrashed: false, + isArchived: false, name: "Deanna's course 2 room", description: '', creator: ObjectId('5be1eba75854270cd0920faa'), @@ -260,6 +267,7 @@ module.exports = [ tempRoom: false, controlledBy: null, isTrashed: false, + isArchived: false, name: "Q's Admin Room", description: '', creator: ObjectId('5ba289ba7223b9429888b9ee'), @@ -302,6 +310,7 @@ module.exports = [ tempRoom: false, controlledBy: null, isTrashed: false, + isArchived: false, name: 'reference room', description: 'room for testing the reference tool', creator: ObjectId('5d0d2eae535e3a522445f7a4'), @@ -337,6 +346,7 @@ module.exports = [ tempRoom: false, controlledBy: null, isTrashed: false, + isArchived: false, name: 'ssmith desmos fun', description: 'desmos test room', creator: ObjectId('5d1a59d79c78ad48c0480caa'), @@ -378,6 +388,7 @@ module.exports = [ tempRoom: false, controlledBy: null, isTrashed: false, + isArchived: false, name: 'ssmith public desmos', description: 'public desmos room', creator: ObjectId('5d1a59d79c78ad48c0480caa'), @@ -413,6 +424,7 @@ module.exports = [ tempRoom: false, controlledBy: null, isTrashed: false, + isArchived: false, name: 'ssmith c1: triangles', description: 'first room in c1', creator: ObjectId('5d1a59d79c78ad48c0480caa'), @@ -449,6 +461,7 @@ module.exports = [ tempRoom: false, controlledBy: null, isTrashed: false, + isArchived: false, name: 'ssmith c1: squares', description: '', creator: ObjectId('5d1a59d79c78ad48c0480caa'), @@ -485,6 +498,7 @@ module.exports = [ tempRoom: false, controlledBy: null, isTrashed: false, + isArchived: false, name: 'math is fun roomA', description: '', creator: ObjectId('5d1a59d79c78ad48c0480caa'), From 579832ce62b6fe3bffd97ab1897a74aad930e814 Mon Sep 17 00:00:00 2001 From: esopp Date: Mon, 22 Jun 2020 22:49:25 -0400 Subject: [PATCH 12/14] test filtering for active and archived rooms on the community page. test that user's created rooms can be archived and unarchived and filtered by their archived status on the MyVMT rooms page. --- server/cypress/fixtures/community.js | 33 ++++++-- server/cypress/integration/community.js | 108 ++++++++++++++++++++---- server/cypress/integration/create.js | 40 +++++++++ server/seeders/rooms.js | 4 +- 4 files changed, 157 insertions(+), 28 deletions(-) diff --git a/server/cypress/fixtures/community.js b/server/cypress/fixtures/community.js index d103088e1..a54863ee0 100644 --- a/server/cypress/fixtures/community.js +++ b/server/cypress/fixtures/community.js @@ -61,26 +61,41 @@ resourceTypes.forEach((type) => { const isCourses = type === 'courses'; fixtures[type] = { - all: allResources, - publicAll: allResources.filter((r) => r.privacySetting === pub), - privateAll: allResources.filter((r) => r.privacySetting === prv), + all: allResources.filter((r) => !r.isArchived), + publicAll: allResources.filter((r) => r.privacySetting === pub && !r.isArchived), + privateAll: allResources.filter((r) => r.privacySetting === prv && !r.isArchived), }; if (!isCourses) { Object.assign(fixtures[type], { - allGgb: allResources.filter((r) => r.roomType === ggb), - allDesmos: allResources.filter((r) => r.roomType === des), + allGgb: allResources.filter( + (r) => r.roomType === ggb && !r.isArchived + ), + allDesmos: allResources.filter( + (r) => r.roomType === des && !r.isArchived + ), publicGgb: allResources.filter( - (r) => r.roomType === ggb && r.privacySetting === pub + (r) => r.roomType === ggb && r.privacySetting === pub && !r.isArchived ), publicDesmos: allResources.filter( - (r) => r.roomType === des && r.privacySetting === pub + (r) => r.roomType === des && r.privacySetting === pub && !r.isArchived ), privateGgb: allResources.filter( - (r) => r.roomType === ggb && r.privacySetting === prv + (r) => r.roomType === ggb && r.privacySetting === prv && !r.isArchived ), privateDesmos: allResources.filter( - (r) => r.roomType === des && r.privacySetting === prv + (r) => r.roomType === des && r.privacySetting === prv && !r.isArchived + ), + }); + } + if (type === 'rooms') { + Object.assign(fixtures[type], { + allArchived: allResources.filter((r) => r.isArchived), + allPublicArchived: allResources.filter( + (r) => r.isArchived && r.privacySetting === pub + ), + allPrivateArchived: allResources.filter( + (r) => r.isArchived && r.privacySetting === prv ), }); } diff --git a/server/cypress/integration/community.js b/server/cypress/integration/community.js index 3f5dca6b6..fc971d4bc 100644 --- a/server/cypress/integration/community.js +++ b/server/cypress/integration/community.js @@ -4,13 +4,17 @@ const fixtures = require('../fixtures/community'); const { rooms, activities, courses } = fixtures; describe('test community search and filter', function() { - function checkUrl(privacy, roomType, query, resourceType) { + function checkUrl(privacy, roomType, query, resourceType, roomStatus) { let expectedUrl = `community/${resourceType}?privacy=${privacy}`; if (resourceType !== 'courses') { expectedUrl += `&roomType=${roomType}`; } + if (resourceType === 'rooms') { + expectedUrl += `&roomStatus=${roomStatus}`; + } + if (query) { expectedUrl += `&search=${query}`; } @@ -67,7 +71,7 @@ describe('test community search and filter', function() { all: { description: 'Initial Settings', setup: { - filtersToSelect: ['all-roomType-filter', 'all-privacy-filter'], + filtersToSelect: ['all-roomType-filter', 'all-privacy-filter', 'all-roomStatus-filter'], searchBar: { query: '', doClear: true, @@ -76,6 +80,7 @@ describe('test community search and filter', function() { urlParams: { privacy: 'all', roomType: 'all', + roomStatus: 'default', search: '', }, expectedItems: { @@ -87,7 +92,7 @@ describe('test community search and filter', function() { allGgb: { description: 'All Geogebra', setup: { - filtersToSelect: ['all-privacy-filter', 'geogebra-filter'], + filtersToSelect: ['all-privacy-filter', 'geogebra-filter', 'all-roomStatus-filter'], searchBar: { query: '', doClear: true, @@ -96,6 +101,7 @@ describe('test community search and filter', function() { urlParams: { privacy: 'all', roomType: 'geogebra', + roomStatus: 'default', search: '', }, expectedItems: { @@ -106,7 +112,7 @@ describe('test community search and filter', function() { allDesmos: { description: 'All Desmos', setup: { - filtersToSelect: ['all-privacy-filter', 'desmos-filter'], + filtersToSelect: ['all-privacy-filter', 'desmos-filter', 'all-roomStatus-filter'], searchBar: { query: '', doClear: true, @@ -115,6 +121,7 @@ describe('test community search and filter', function() { urlParams: { privacy: 'all', roomType: 'desmos', + roomStatus: 'default', search: '', }, expectedItems: { @@ -125,7 +132,7 @@ describe('test community search and filter', function() { publicAll: { description: 'All Public Rooms', setup: { - filtersToSelect: ['public-filter', 'all-roomType-filter'], + filtersToSelect: ['public-filter', 'all-roomType-filter', 'all-roomStatus-filter'], searchBar: { query: '', doClear: true, @@ -134,6 +141,7 @@ describe('test community search and filter', function() { urlParams: { privacy: 'public', roomType: 'all', + roomStatus: 'default', search: '', }, expectedItems: { @@ -145,7 +153,7 @@ describe('test community search and filter', function() { privateAll: { description: 'All Private Rooms', setup: { - filtersToSelect: ['private-filter', 'all-roomType-filter'], + filtersToSelect: ['private-filter', 'all-roomType-filter', 'all-roomStatus-filter'], searchBar: { query: '', doClear: true, @@ -154,6 +162,7 @@ describe('test community search and filter', function() { urlParams: { privacy: 'private', roomType: 'all', + roomStatus: 'default', search: '', }, expectedItems: { @@ -165,7 +174,7 @@ describe('test community search and filter', function() { publicGgb: { description: 'Public Geogebra Rooms', setup: { - filtersToSelect: ['public-filter', 'geogebra-filter'], + filtersToSelect: ['public-filter', 'geogebra-filter', 'all-roomStatus-filter'], searchBar: { query: '', doClear: true, @@ -174,6 +183,7 @@ describe('test community search and filter', function() { urlParams: { privacy: 'public', roomType: 'geogebra', + roomStatus: 'default', search: '', }, expectedItems: { @@ -184,7 +194,7 @@ describe('test community search and filter', function() { publicDesmos: { description: 'Public Desmos Rooms', setup: { - filtersToSelect: ['public-filter', 'desmos-filter'], + filtersToSelect: ['public-filter', 'desmos-filter', 'all-roomStatus-filter'], searchBar: { query: '', doClear: true, @@ -193,6 +203,7 @@ describe('test community search and filter', function() { urlParams: { privacy: 'public', roomType: 'desmos', + roomStatus: 'default', search: '', }, expectedItems: { @@ -203,7 +214,7 @@ describe('test community search and filter', function() { privateDesmos: { description: 'Private Desmos Rooms', setup: { - filtersToSelect: ['private-filter', 'desmos-filter'], + filtersToSelect: ['private-filter', 'desmos-filter', 'all-roomStatus-filter'], searchBar: { query: '', doClear: true, @@ -212,6 +223,7 @@ describe('test community search and filter', function() { urlParams: { privacy: 'private', roomType: 'desmos', + roomStatus: 'default', search: '', }, expectedItems: { @@ -219,12 +231,69 @@ describe('test community search and filter', function() { activities: activities.privateDesmos, }, }, + allArchived: { + description: 'All Archived Rooms', + setup: { + filtersToSelect: ['all-roomType-filter', 'all-privacy-filter', 'archived-roomStatus-filter'], + searchBar: { + query: '', + doClear: true, + }, + }, + urlParams: { + privacy: 'all', + roomType: 'all', + roomStatus: 'isArchived', + search: '', + }, + expectedItems: { + rooms: rooms.allArchived, + }, + }, + allPublicArchived: { + description: 'All Public Archived Rooms', + setup: { + filtersToSelect: ['all-roomType-filter', 'public-filter', 'archived-roomStatus-filter'], + searchBar: { + query: '', + doClear: true, + }, + }, + urlParams: { + privacy: 'public', + roomType: 'all', + roomStatus: 'isArchived', + search: '', + }, + expectedItems: { + rooms: rooms.allPublicArchived, + }, + }, + allPrivateArchived: { + description: 'All Private Archived Rooms', + setup: { + filtersToSelect: ['all-roomType-filter', 'private-filter', 'archived-roomStatus-filter'], + searchBar: { + query: '', + doClear: true, + }, + }, + urlParams: { + privacy: 'private', + roomType: 'all', + roomStatus: 'isArchived', + search: '', + }, + expectedItems: { + rooms: rooms.allPrivateArchived, + }, + } }; describe('Filtering and Sorting Rooms', function() { return Object.values(configs).map((config) => { const { description, setup, urlParams, expectedItems } = config; - const { privacy, roomType, search: searchParam } = urlParams; + const { privacy, roomType, roomStatus, search: searchParam } = urlParams; const { filtersToSelect, searchBar } = setup; const { query, doClear } = searchBar; @@ -237,12 +306,17 @@ describe('test community search and filter', function() { clickResourceTab(resourceType); filtersToSelect.forEach((testId) => { const doSkip = - resourceType === 'courses' && - [ - 'geogebra-filter', - 'desmos-filter', - 'all-roomType-filter', - ].includes(testId); + (resourceType === 'courses' && + [ + 'geogebra-filter', + 'desmos-filter', + 'all-roomType-filter', + 'all-roomStatus-filter', + ].includes(testId) + ) || ( + resourceType === 'activities' && + testId === 'all-roomStatus-filter' + ); if (!doSkip) { clickFilter(testId); } @@ -250,7 +324,7 @@ describe('test community search and filter', function() { if (query) { doSearch(query, { doClear }); } - checkUrl(privacy, roomType, searchParam, resourceType); + checkUrl(privacy, roomType, searchParam, resourceType, roomStatus); }); it(`should display ${resources.length} items`, function() { diff --git a/server/cypress/integration/create.js b/server/cypress/integration/create.js index 46ea005e8..8ea29f498 100644 --- a/server/cypress/integration/create.js +++ b/server/cypress/integration/create.js @@ -212,6 +212,45 @@ describe('create each type of resource', function() { }); }); + it('creates and archives a new ggb room', function() { + const { name, description } = room; + createResource({ + resourceType: 'room', + name: `${name} to archive`, + description, + }); + cy.getTestElement(`content-box-${name} to archive`).click(); + cy.getTestElement('edit-room') + .contains('Edit Room') + .click(); + cy.getTestElement('archive-room') + .contains('ARCHIVE') + .click(); + cy.getTestElement('confirm-archive') + .contains('archive this room') + .click(); + //Should return to the MyVMT rooms tab for this user + cy.url("include", "/myVMT/rooms"); + //archived rooms should not appear on the myVMT page by default + cy.getTestElement(`content-box-${name} to archive`).should('not.exist'); + //filter for archived rooms + cy.getTestElement('archived-roomStatus-filter').click(); + //go to the archived room's detail page and restore it + cy.getTestElement(`content-box-[ARCHIVED] ${name} to archive`) + .contains(`[ARCHIVED] ${name} to archive`) + .click(); + cy.getTestElement('restore-room') + .contains('Restore Room') + .click(); + cy.getTestElement('confirm-archive') + .contains('restore this room') + .click(); + //Should return to the MyVMT rooms tab for this user + cy.url("include", "/myVMT/rooms"); + //restored room should appear on the myVMT page by default + cy.getTestElement(`content-box-${name} to archive`).should('exist'); + }); + it('creates a public ggb activity', function() { const { name, description } = activity; createResource({ resourceType: 'activity', name, description }); @@ -347,6 +386,7 @@ describe('create each type of resource', function() { }); }); + // it("creates a course room from a course activity", function() { // cy.getTestElement("content-box-course 1").click(); // cy.getTestElement("tab") diff --git a/server/seeders/rooms.js b/server/seeders/rooms.js index af15673d1..113d6bf86 100644 --- a/server/seeders/rooms.js +++ b/server/seeders/rooms.js @@ -461,7 +461,7 @@ module.exports = [ tempRoom: false, controlledBy: null, isTrashed: false, - isArchived: false, + isArchived: true, name: 'ssmith c1: squares', description: '', creator: ObjectId('5d1a59d79c78ad48c0480caa'), @@ -498,7 +498,7 @@ module.exports = [ tempRoom: false, controlledBy: null, isTrashed: false, - isArchived: false, + isArchived: true, name: 'math is fun roomA', description: '', creator: ObjectId('5d1a59d79c78ad48c0480caa'), From 527af3a5682bfd32f2a7eaa9e7509dd0dba080df Mon Sep 17 00:00:00 2001 From: esopp Date: Mon, 22 Jun 2020 22:54:31 -0400 Subject: [PATCH 13/14] update version date --- client/src/Layout/Homepage/Homepage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/Layout/Homepage/Homepage.js b/client/src/Layout/Homepage/Homepage.js index 666264683..73a131a13 100644 --- a/client/src/Layout/Homepage/Homepage.js +++ b/client/src/Layout/Homepage/Homepage.js @@ -87,7 +87,7 @@ class Homepage extends PureComponent { VMT is currently in Alpha. If you encounter bugs or want to suggest new features please email vmt@21pstem.org

    -

    last updated: 06.19.2020

    +

    last updated: 06.22.2020

    From 52f37b169251929e2fd5ab04f76272d278b34d4d Mon Sep 17 00:00:00 2001 From: esopp Date: Mon, 22 Jun 2020 23:23:54 -0400 Subject: [PATCH 14/14] see if restoring the deploy-mac.sh file fixes travis tests on remote github branch --- deploy-mac.sh | 40 +++++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/deploy-mac.sh b/deploy-mac.sh index f3eaf8f04..840e2f3a2 100644 --- a/deploy-mac.sh +++ b/deploy-mac.sh @@ -1,5 +1,5 @@ #!/bin/bash -echo -e "\e[1;33m Making a $1 build" +echo -e "\e[1;33m Making a $2 build" echo -e "\e[1;30m" if [ -d "./client/build" ]; then rm -rf ./client/build @@ -8,24 +8,24 @@ if [ -d "./client/encompassBuild" ]; then rm -rf ./client/encompassBuild fi cd ./server -npm run build-$1 -npm run build-enc-$1 +npm run build-$2 +npm run build-enc-$2 cd .. -if [ -d "./$1" ]; then - rm -rf ./$1 +if [ -d "./$2" ]; then + rm -rf ./$2 fi -mkdir ./$1 -cd ./$1 +mkdir ./$2 +cd ./$2 mkdir ./server cd .. -echo "made $1 directory" +echo "made $2 directory" cd ./server -cp -r ./bin ./config ./constants ./controllers ./db_migration ./middleware ./models ./routes ./services ../$1/server -echo "\e[1;33m server copied to $1" -cp app.js package.json socketInit.js sockets.js ../$1/server -cp .env ../$1 +cp -r ./bin ./config ./constants ./controllers ./db_migration ./middleware ./models ./routes ./services ../$2/server +echo "\e[1;33m server copied to $2" +cp app.js package.json socketInit.js sockets.js ../$2/server +cp .env ../$2 echo "\e[1;33mfiles copied" -cd ../$1 +cd ../$2 mkdir client mkdir client/build mkdir client/encompassBuild @@ -34,5 +34,15 @@ cp -r ../client/build/ client/build cp -r ../client/encompassBuild/ client/encompassBuild echo -e "\e[1;33m Zipping...\e[0m" -zip -r VMT-$1.zip . - +zip -r VMT-$2.zip . +echo -e "\e[1;33m Copying zipped directory...\e[0m" +scp ./VMT-$2.zip "$1"@mathematicalthinking.org:/tmp/ +echo -e "\e[1;31m You're not done yet!\e[0m" +echo -e "\e[1;35m run the following commands to complete the demployment proces" +echo -e "\e[1;34m $ \e[0m ssh $1@mathematicalthinking" +echo -e "\e[1;34m $ \e[0m cd /web/mathematicalthinking/vmt" +echo -e "\e[1;34m $ \e[0m rm -rf staging && mkdir staging && cd staging" +echo -e "\e[1;34m $ \e[0m mv /tmp/VMT-$2.zip ." +echo -e "\e[1;34m $ \e[0m unzip VMT-$2.zip" +echo -e "\e[1;34m $ \e[0m cd ./server && npm install --only=production" +echo -e "\e[1;34m $ \e[0m systemctl restart vmt-$2"