From f338b9be17fbec0dea99c2a5bd360db626ea5fb8 Mon Sep 17 00:00:00 2001 From: Ahmad Farhat <ahmad.af.farhat@gmail.com> Date: Fri, 3 Nov 2023 10:43:26 -0400 Subject: [PATCH] Recording view improvements (#5497) * Added pagination to recording tables * Make dropdown go up for last item in list * rspec --- .../stylesheets/application.bootstrap.scss | 4 +--- app/assets/stylesheets/helpers.scss | 6 +++--- app/assets/stylesheets/recordings.scss | 9 +++++++-- .../api/v1/recordings_controller.rb | 2 +- app/controllers/api/v1/rooms_controller.rb | 2 +- .../components/admin/manage_users/EditUser.jsx | 2 +- .../admin/manage_users/ManageUsers.jsx | 2 +- .../components/admin/roles/EditRole.jsx | 2 +- .../components/admin/roles/Roles.jsx | 2 +- .../admin/room_configuration/RoomConfig.jsx | 2 +- .../server_recordings/ServerRecordings.jsx | 2 +- .../admin/server_rooms/ServerRooms.jsx | 2 +- .../admin/site_settings/SiteSettings.jsx | 2 +- .../components/admin/tenants/Tenants.jsx | 2 +- .../components/recordings/RecordingRow.jsx | 5 ++++- .../components/recordings/RecordingsList.jsx | 18 +++++++++++++----- .../components/recordings/UserRecordings.jsx | 1 + .../room_recordings/RoomRecordings.jsx | 1 + .../room_recordings/RoomsRecordingRow.jsx | 5 ++++- .../utilities/SimpleSelect.jsx | 6 ++++-- spec/controllers/rooms_controller_spec.rb | 6 +++--- 21 files changed, 52 insertions(+), 31 deletions(-) diff --git a/app/assets/stylesheets/application.bootstrap.scss b/app/assets/stylesheets/application.bootstrap.scss index 2446bd5b..8339867f 100644 --- a/app/assets/stylesheets/application.bootstrap.scss +++ b/app/assets/stylesheets/application.bootstrap.scss @@ -197,8 +197,6 @@ input.search-bar { } #footer { - margin-top: $footer-buffer-height; - #footer-container { border-top: 1px solid #d0d5dd; } @@ -464,7 +462,7 @@ input.search-bar { box-shadow: 0 0 0 0.25rem var(--brand-color-light) !important; } &::after { - display: none; + display: none !important; } } diff --git a/app/assets/stylesheets/helpers.scss b/app/assets/stylesheets/helpers.scss index 107604e6..9a30d9c1 100644 --- a/app/assets/stylesheets/helpers.scss +++ b/app/assets/stylesheets/helpers.scss @@ -34,14 +34,14 @@ } .no-header-height { - min-height: calc(100vh - $footer-height - $footer-buffer-height); + min-height: calc(100vh - $footer-height); } .regular-height { - min-height: calc(100vh - $header-height - $footer-height - $footer-buffer-height); + min-height: calc(100vh - $header-height - $footer-height); .vertical-center { - min-height: calc(100vh - $header-height - $header-height - $footer-height - $footer-buffer-height); + min-height: calc(100vh - $header-height - $header-height - $footer-height); } } diff --git a/app/assets/stylesheets/recordings.scss b/app/assets/stylesheets/recordings.scss index 9b770871..6d401795 100644 --- a/app/assets/stylesheets/recordings.scss +++ b/app/assets/stylesheets/recordings.scss @@ -13,10 +13,15 @@ // // You should have received a copy of the GNU Lesser General Public License along // with Greenlight; if not, see <http://www.gnu.org/licenses/>. +#user-recordings { + min-height: 699px; +} -#user-recordings, #room-recordings { - min-height: 400px; +#room-recordings { + min-height: 491px; +} +#user-recordings, #room-recordings { table { border-top-right-radius: $border-radius-lg; border-top-left-radius: $border-radius-lg; diff --git a/app/controllers/api/v1/recordings_controller.rb b/app/controllers/api/v1/recordings_controller.rb index 6138fafe..09262909 100644 --- a/app/controllers/api/v1/recordings_controller.rb +++ b/app/controllers/api/v1/recordings_controller.rb @@ -37,7 +37,7 @@ module Api def index sort_config = config_sorting(allowed_columns: %w[name length visibility]) - pagy, recordings = pagy(current_user.recordings&.order(sort_config, recorded_at: :desc)&.search(params[:search])) + pagy, recordings = pagy(current_user.recordings&.order(sort_config, recorded_at: :desc)&.search(params[:search]), items: 5) render_data data: recordings, meta: pagy_metadata(pagy), status: :ok end diff --git a/app/controllers/api/v1/rooms_controller.rb b/app/controllers/api/v1/rooms_controller.rb index ffde5ff5..e1e4a982 100644 --- a/app/controllers/api/v1/rooms_controller.rb +++ b/app/controllers/api/v1/rooms_controller.rb @@ -133,7 +133,7 @@ module Api def recordings sort_config = config_sorting(allowed_columns: %w[name length visibility]) - pagy, room_recordings = pagy(@room.recordings&.order(sort_config, recorded_at: :desc)&.search(params[:q])) + pagy, room_recordings = pagy(@room.recordings&.order(sort_config, recorded_at: :desc)&.search(params[:q]), items: 3) render_data data: room_recordings, meta: pagy_metadata(pagy), status: :ok end diff --git a/app/javascript/components/admin/manage_users/EditUser.jsx b/app/javascript/components/admin/manage_users/EditUser.jsx index 364c48c8..fa85878b 100644 --- a/app/javascript/components/admin/manage_users/EditUser.jsx +++ b/app/javascript/components/admin/manage_users/EditUser.jsx @@ -39,7 +39,7 @@ export default function EditUser() { } return ( - <div id="admin-panel" className="pb-3"> + <div id="admin-panel" className="pb-4"> <h3 className="py-5"> { t('admin.admin_panel') } </h3> <Card className="border-0 card-shadow"> <Tab.Container activekey="users"> diff --git a/app/javascript/components/admin/manage_users/ManageUsers.jsx b/app/javascript/components/admin/manage_users/ManageUsers.jsx index b8269617..f256c9c4 100644 --- a/app/javascript/components/admin/manage_users/ManageUsers.jsx +++ b/app/javascript/components/admin/manage_users/ManageUsers.jsx @@ -47,7 +47,7 @@ export default function ManageUsers() { } return ( - <div id="admin-panel" className="pb-3"> + <div id="admin-panel" className="pb-4"> <h3 className="py-5">{t('admin.admin_panel')}</h3> <Card className="border-0 card-shadow"> <Tab.Container activeKey="users"> diff --git a/app/javascript/components/admin/roles/EditRole.jsx b/app/javascript/components/admin/roles/EditRole.jsx index a546a7e9..883e765b 100644 --- a/app/javascript/components/admin/roles/EditRole.jsx +++ b/app/javascript/components/admin/roles/EditRole.jsx @@ -44,7 +44,7 @@ export default function EditRole() { if (isLoading) return null; return ( - <div id="admin-panel" className="pb-3"> + <div id="admin-panel" className="pb-4"> <h3 className="py-5">{ t('admin.admin_panel') }</h3> <Card className="border-0 card-shadow"> <Tab.Container activeKey="roles"> diff --git a/app/javascript/components/admin/roles/Roles.jsx b/app/javascript/components/admin/roles/Roles.jsx index 78175a70..d8ed915a 100644 --- a/app/javascript/components/admin/roles/Roles.jsx +++ b/app/javascript/components/admin/roles/Roles.jsx @@ -39,7 +39,7 @@ export default function Roles() { } return ( - <div id="admin-panel" className="pb-3"> + <div id="admin-panel" className="pb-4"> <h3 className="py-5"> { t('admin.admin_panel') } </h3> <Card className="border-0 card-shadow"> <Tab.Container activeKey="roles"> diff --git a/app/javascript/components/admin/room_configuration/RoomConfig.jsx b/app/javascript/components/admin/room_configuration/RoomConfig.jsx index 3741539b..696a5e1d 100644 --- a/app/javascript/components/admin/room_configuration/RoomConfig.jsx +++ b/app/javascript/components/admin/room_configuration/RoomConfig.jsx @@ -37,7 +37,7 @@ export default function RoomConfig() { } return ( - <div id="admin-panel" className="pb-3"> + <div id="admin-panel" className="pb-4"> <h3 className="py-5"> { t('admin.admin_panel') } </h3> <Card className="border-0 card-shadow"> <Tab.Container activeKey="room_configuration"> diff --git a/app/javascript/components/admin/server_recordings/ServerRecordings.jsx b/app/javascript/components/admin/server_recordings/ServerRecordings.jsx index f51593b8..2b1ad378 100644 --- a/app/javascript/components/admin/server_recordings/ServerRecordings.jsx +++ b/app/javascript/components/admin/server_recordings/ServerRecordings.jsx @@ -38,7 +38,7 @@ export default function ServerRecordings() { } return ( - <div id="admin-panel" className="pb-3"> + <div id="admin-panel" className="pb-4"> <h3 className="py-5"> {t('admin.admin_panel')} </h3> <Card className="border-0 card-shadow"> <Tab.Container activeKey="server_recordings"> diff --git a/app/javascript/components/admin/server_rooms/ServerRooms.jsx b/app/javascript/components/admin/server_rooms/ServerRooms.jsx index c0a69c15..cde5c2e1 100644 --- a/app/javascript/components/admin/server_rooms/ServerRooms.jsx +++ b/app/javascript/components/admin/server_rooms/ServerRooms.jsx @@ -44,7 +44,7 @@ export default function ServerRooms() { } return ( - <div id="admin-panel" className="pb-3"> + <div id="admin-panel" className="pb-4"> <h3 className="py-5"> { t('admin.admin_panel') } </h3> <Card className="border-0 card-shadow"> <Tab.Container activeKey="server_rooms"> diff --git a/app/javascript/components/admin/site_settings/SiteSettings.jsx b/app/javascript/components/admin/site_settings/SiteSettings.jsx index f0821945..a8081e94 100644 --- a/app/javascript/components/admin/site_settings/SiteSettings.jsx +++ b/app/javascript/components/admin/site_settings/SiteSettings.jsx @@ -37,7 +37,7 @@ export default function SiteSettings() { } return ( - <div id="admin-panel" className="pb-3"> + <div id="admin-panel" className="pb-4"> <h3 className="py-5">{ t('admin.admin_panel') }</h3> <Card className="border-0 card-shadow"> <Tab.Container activeKey="site_settings"> diff --git a/app/javascript/components/admin/tenants/Tenants.jsx b/app/javascript/components/admin/tenants/Tenants.jsx index 70a1630e..095239f1 100644 --- a/app/javascript/components/admin/tenants/Tenants.jsx +++ b/app/javascript/components/admin/tenants/Tenants.jsx @@ -45,7 +45,7 @@ export default function Tenants() { const { data: tenants, isLoading } = useTenants({ search: searchInput, page }); return ( - <div id="admin-panel" className="pb-3"> + <div id="admin-panel" className="pb-4"> <h3 className="py-5">{ t('admin.admin_panel') }</h3> <Card className="border-0 card-shadow"> <Tab.Container activeKey="tenants"> diff --git a/app/javascript/components/recordings/RecordingRow.jsx b/app/javascript/components/recordings/RecordingRow.jsx index a465a27d..e6497890 100644 --- a/app/javascript/components/recordings/RecordingRow.jsx +++ b/app/javascript/components/recordings/RecordingRow.jsx @@ -35,7 +35,7 @@ import SimpleSelect from '../shared_components/utilities/SimpleSelect'; // TODO: Amir - Refactor this. export default function RecordingRow({ - recording, visibilityMutation: useVisibilityAPI, deleteMutation: useDeleteAPI, adminTable, + recording, visibilityMutation: useVisibilityAPI, deleteMutation: useDeleteAPI, adminTable, dropUp, }) { const { t } = useTranslation(); @@ -104,6 +104,7 @@ export default function RecordingRow({ <td className="border-0"> <SimpleSelect defaultValue={recording.visibility} + dropUp={dropUp} > <Dropdown.Item key="Public/Protected" @@ -205,6 +206,7 @@ export default function RecordingRow({ RecordingRow.defaultProps = { adminTable: false, + dropUp: false, }; RecordingRow.propTypes = { @@ -227,4 +229,5 @@ RecordingRow.propTypes = { visibilityMutation: PropTypes.func.isRequired, deleteMutation: PropTypes.func.isRequired, adminTable: PropTypes.bool, + dropUp: PropTypes.bool, }; diff --git a/app/javascript/components/recordings/RecordingsList.jsx b/app/javascript/components/recordings/RecordingsList.jsx index fbec10b4..0dde058e 100644 --- a/app/javascript/components/recordings/RecordingsList.jsx +++ b/app/javascript/components/recordings/RecordingsList.jsx @@ -28,7 +28,7 @@ import SearchBar from '../shared_components/search/SearchBar'; import ProcessingRecordingRow from './ProcessingRecordingRow'; export default function RecordingsList({ - recordings, isLoading, setPage, searchInput, setSearchInput, recordingsProcessing, adminTable, + recordings, isLoading, setPage, searchInput, setSearchInput, recordingsProcessing, adminTable, numPlaceholders, }) { const { t } = useTranslation(); @@ -65,14 +65,19 @@ export default function RecordingsList({ <tbody className="border-top-0"> {[...Array(recordingsProcessing)].map(() => <ProcessingRecordingRow />)} { - (isLoading && [...Array(7)].map((val, idx) => ( + (isLoading && [...Array(numPlaceholders)].map((val, idx) => ( // eslint-disable-next-line react/no-array-index-key <RecordingsListRowPlaceHolder key={idx} /> ))) } { - (recordings?.data?.length > 0 && recordings?.data?.map((recording) => ( - <RoomsRecordingRow key={recording.id} recording={recording} adminTable={adminTable} /> + (recordings?.data?.length > 0 && recordings?.data?.map((recording, idx) => ( + <RoomsRecordingRow + key={recording.id} + recording={recording} + adminTable={adminTable} + dropUp={(recordings?.meta?.page || 0) * (recordings?.meta?.items || 0) - 1 === idx} + /> ))) } </tbody> @@ -99,10 +104,11 @@ export default function RecordingsList({ } RecordingsList.defaultProps = { - recordings: { data: [], meta: { page: 1, pages: 1 } }, + recordings: { data: [], meta: { page: 1, pages: 1, items: 3 } }, recordingsProcessing: 0, searchInput: '', adminTable: false, + numPlaceholders: 7, }; RecordingsList.propTypes = { @@ -124,6 +130,7 @@ RecordingsList.propTypes = { meta: PropTypes.shape({ page: PropTypes.number, pages: PropTypes.number, + items: PropTypes.number, }), }), isLoading: PropTypes.bool.isRequired, @@ -132,4 +139,5 @@ RecordingsList.propTypes = { setSearchInput: PropTypes.func.isRequired, recordingsProcessing: PropTypes.number, adminTable: PropTypes.bool, + numPlaceholders: PropTypes.number, }; diff --git a/app/javascript/components/recordings/UserRecordings.jsx b/app/javascript/components/recordings/UserRecordings.jsx index ff8afc65..cf43a520 100644 --- a/app/javascript/components/recordings/UserRecordings.jsx +++ b/app/javascript/components/recordings/UserRecordings.jsx @@ -31,6 +31,7 @@ export default function UserRecordings() { setPage={setPage} setSearchInput={setSearchInput} searchInput={searchInput} + numPlaceholders={5} /> </div> ); diff --git a/app/javascript/components/recordings/room_recordings/RoomRecordings.jsx b/app/javascript/components/recordings/room_recordings/RoomRecordings.jsx index 687d2390..4de37a69 100644 --- a/app/javascript/components/recordings/room_recordings/RoomRecordings.jsx +++ b/app/javascript/components/recordings/room_recordings/RoomRecordings.jsx @@ -36,6 +36,7 @@ export default function RoomRecordings() { setSearchInput={setSearchInput} searchInput={searchInput} recordingsProcessing={roomRecordingsProcessing.data} + numPlaceholders={3} /> </div> ); diff --git a/app/javascript/components/recordings/room_recordings/RoomsRecordingRow.jsx b/app/javascript/components/recordings/room_recordings/RoomsRecordingRow.jsx index 7a2b3d35..40ecc5e9 100644 --- a/app/javascript/components/recordings/room_recordings/RoomsRecordingRow.jsx +++ b/app/javascript/components/recordings/room_recordings/RoomsRecordingRow.jsx @@ -20,19 +20,21 @@ import useUpdateRecordingVisibility from '../../../hooks/mutations/recordings/us import useDeleteRecording from '../../../hooks/mutations/recordings/useDeleteRecording'; import RecordingRow from '../RecordingRow'; -export default function RoomsRecordingRow({ recording, adminTable }) { +export default function RoomsRecordingRow({ recording, adminTable, dropUp }) { return ( <RecordingRow adminTable={adminTable} recording={recording} visibilityMutation={useUpdateRecordingVisibility} deleteMutation={useDeleteRecording} + dropUp={dropUp} /> ); } RoomsRecordingRow.defaultProps = { adminTable: false, + dropUp: false, }; RoomsRecordingRow.propTypes = { @@ -51,4 +53,5 @@ RoomsRecordingRow.propTypes = { map: PropTypes.func, }).isRequired, adminTable: PropTypes.bool, + dropUp: PropTypes.bool, }; diff --git a/app/javascript/components/shared_components/utilities/SimpleSelect.jsx b/app/javascript/components/shared_components/utilities/SimpleSelect.jsx index 7ec44282..6fd162fc 100644 --- a/app/javascript/components/shared_components/utilities/SimpleSelect.jsx +++ b/app/javascript/components/shared_components/utilities/SimpleSelect.jsx @@ -19,12 +19,12 @@ import React from 'react'; import PropTypes from 'prop-types'; import { ChevronDownIcon } from '@heroicons/react/20/solid'; -export default function SimpleSelect({ defaultValue, children }) { +export default function SimpleSelect({ defaultValue, dropUp, children }) { // Get the currently selected option and set the dropdown toggle to that value const defaultString = children?.filter((item) => item.props.value === defaultValue)[0]; return ( - <Dropdown className="simple-select"> + <Dropdown className="simple-select" drop={dropUp ? 'up' : undefined}> <Dropdown.Toggle> { defaultString?.props?.children } <ChevronDownIcon className="hi-s float-end" /> @@ -38,10 +38,12 @@ export default function SimpleSelect({ defaultValue, children }) { SimpleSelect.defaultProps = { defaultValue: '', + dropUp: false, children: undefined, }; SimpleSelect.propTypes = { defaultValue: PropTypes.string, + dropUp: PropTypes.bool, children: PropTypes.arrayOf(PropTypes.element), }; diff --git a/spec/controllers/rooms_controller_spec.rb b/spec/controllers/rooms_controller_spec.rb index 469cbc07..db67b943 100644 --- a/spec/controllers/rooms_controller_spec.rb +++ b/spec/controllers/rooms_controller_spec.rb @@ -298,8 +298,8 @@ RSpec.describe Api::V1::RoomsController, type: :controller do it 'returns recordings belonging to the room' do room1 = create(:room, user:, friendly_id: 'friendly_id_1') room2 = create(:room, user:, friendly_id: 'friendly_id_2') - recordings = create_list(:recording, 5, room: room1) - create_list(:recording, 5, room: room2) + recordings = create_list(:recording, 3, room: room1) + create_list(:recording, 3, room: room2) get :recordings, params: { friendly_id: room1.friendly_id } recording_ids = response.parsed_body['data'].pluck('id') expect(response).to have_http_status(:ok) @@ -320,7 +320,7 @@ RSpec.describe Api::V1::RoomsController, type: :controller do create(:shared_access, user_id: shared_user.id, room_id: room.id) sign_in_user(shared_user) - recordings = create_list(:recording, 5, room:) + recordings = create_list(:recording, 3, room:) get :recordings, params: { friendly_id: room.friendly_id } -- GitLab