Skip to content
Snippets Groups Projects
Unverified Commit f338b9be authored by Ahmad Farhat's avatar Ahmad Farhat Committed by GitHub
Browse files

Recording view improvements (#5497)

* Added pagination to recording tables

* Make dropdown go up for last item in list

* rspec
parent 3a810f93
No related branches found
No related tags found
No related merge requests found
Showing
with 49 additions and 28 deletions
...@@ -197,8 +197,6 @@ input.search-bar { ...@@ -197,8 +197,6 @@ input.search-bar {
} }
#footer { #footer {
margin-top: $footer-buffer-height;
#footer-container { #footer-container {
border-top: 1px solid #d0d5dd; border-top: 1px solid #d0d5dd;
} }
...@@ -464,7 +462,7 @@ input.search-bar { ...@@ -464,7 +462,7 @@ input.search-bar {
box-shadow: 0 0 0 0.25rem var(--brand-color-light) !important; box-shadow: 0 0 0 0.25rem var(--brand-color-light) !important;
} }
&::after { &::after {
display: none; display: none !important;
} }
} }
......
...@@ -34,14 +34,14 @@ ...@@ -34,14 +34,14 @@
} }
.no-header-height { .no-header-height {
min-height: calc(100vh - $footer-height - $footer-buffer-height); min-height: calc(100vh - $footer-height);
} }
.regular-height { .regular-height {
min-height: calc(100vh - $header-height - $footer-height - $footer-buffer-height); min-height: calc(100vh - $header-height - $footer-height);
.vertical-center { .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);
} }
} }
......
...@@ -13,10 +13,15 @@ ...@@ -13,10 +13,15 @@
// //
// You should have received a copy of the GNU Lesser General Public License along // You should have received a copy of the GNU Lesser General Public License along
// with Greenlight; if not, see <http://www.gnu.org/licenses/>. // with Greenlight; if not, see <http://www.gnu.org/licenses/>.
#user-recordings {
min-height: 699px;
}
#user-recordings, #room-recordings { #room-recordings {
min-height: 400px; min-height: 491px;
}
#user-recordings, #room-recordings {
table { table {
border-top-right-radius: $border-radius-lg; border-top-right-radius: $border-radius-lg;
border-top-left-radius: $border-radius-lg; border-top-left-radius: $border-radius-lg;
......
...@@ -37,7 +37,7 @@ module Api ...@@ -37,7 +37,7 @@ module Api
def index def index
sort_config = config_sorting(allowed_columns: %w[name length visibility]) 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 render_data data: recordings, meta: pagy_metadata(pagy), status: :ok
end end
......
...@@ -133,7 +133,7 @@ module Api ...@@ -133,7 +133,7 @@ module Api
def recordings def recordings
sort_config = config_sorting(allowed_columns: %w[name length visibility]) 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 render_data data: room_recordings, meta: pagy_metadata(pagy), status: :ok
end end
......
...@@ -39,7 +39,7 @@ export default function EditUser() { ...@@ -39,7 +39,7 @@ export default function EditUser() {
} }
return ( return (
<div id="admin-panel" className="pb-3"> <div id="admin-panel" className="pb-4">
<h3 className="py-5"> { t('admin.admin_panel') } </h3> <h3 className="py-5"> { t('admin.admin_panel') } </h3>
<Card className="border-0 card-shadow"> <Card className="border-0 card-shadow">
<Tab.Container activekey="users"> <Tab.Container activekey="users">
......
...@@ -47,7 +47,7 @@ export default function ManageUsers() { ...@@ -47,7 +47,7 @@ export default function ManageUsers() {
} }
return ( return (
<div id="admin-panel" className="pb-3"> <div id="admin-panel" className="pb-4">
<h3 className="py-5">{t('admin.admin_panel')}</h3> <h3 className="py-5">{t('admin.admin_panel')}</h3>
<Card className="border-0 card-shadow"> <Card className="border-0 card-shadow">
<Tab.Container activeKey="users"> <Tab.Container activeKey="users">
......
...@@ -44,7 +44,7 @@ export default function EditRole() { ...@@ -44,7 +44,7 @@ export default function EditRole() {
if (isLoading) return null; if (isLoading) return null;
return ( return (
<div id="admin-panel" className="pb-3"> <div id="admin-panel" className="pb-4">
<h3 className="py-5">{ t('admin.admin_panel') }</h3> <h3 className="py-5">{ t('admin.admin_panel') }</h3>
<Card className="border-0 card-shadow"> <Card className="border-0 card-shadow">
<Tab.Container activeKey="roles"> <Tab.Container activeKey="roles">
......
...@@ -39,7 +39,7 @@ export default function Roles() { ...@@ -39,7 +39,7 @@ export default function Roles() {
} }
return ( return (
<div id="admin-panel" className="pb-3"> <div id="admin-panel" className="pb-4">
<h3 className="py-5"> { t('admin.admin_panel') } </h3> <h3 className="py-5"> { t('admin.admin_panel') } </h3>
<Card className="border-0 card-shadow"> <Card className="border-0 card-shadow">
<Tab.Container activeKey="roles"> <Tab.Container activeKey="roles">
......
...@@ -37,7 +37,7 @@ export default function RoomConfig() { ...@@ -37,7 +37,7 @@ export default function RoomConfig() {
} }
return ( return (
<div id="admin-panel" className="pb-3"> <div id="admin-panel" className="pb-4">
<h3 className="py-5"> { t('admin.admin_panel') } </h3> <h3 className="py-5"> { t('admin.admin_panel') } </h3>
<Card className="border-0 card-shadow"> <Card className="border-0 card-shadow">
<Tab.Container activeKey="room_configuration"> <Tab.Container activeKey="room_configuration">
......
...@@ -38,7 +38,7 @@ export default function ServerRecordings() { ...@@ -38,7 +38,7 @@ export default function ServerRecordings() {
} }
return ( return (
<div id="admin-panel" className="pb-3"> <div id="admin-panel" className="pb-4">
<h3 className="py-5"> {t('admin.admin_panel')} </h3> <h3 className="py-5"> {t('admin.admin_panel')} </h3>
<Card className="border-0 card-shadow"> <Card className="border-0 card-shadow">
<Tab.Container activeKey="server_recordings"> <Tab.Container activeKey="server_recordings">
......
...@@ -44,7 +44,7 @@ export default function ServerRooms() { ...@@ -44,7 +44,7 @@ export default function ServerRooms() {
} }
return ( return (
<div id="admin-panel" className="pb-3"> <div id="admin-panel" className="pb-4">
<h3 className="py-5"> { t('admin.admin_panel') } </h3> <h3 className="py-5"> { t('admin.admin_panel') } </h3>
<Card className="border-0 card-shadow"> <Card className="border-0 card-shadow">
<Tab.Container activeKey="server_rooms"> <Tab.Container activeKey="server_rooms">
......
...@@ -37,7 +37,7 @@ export default function SiteSettings() { ...@@ -37,7 +37,7 @@ export default function SiteSettings() {
} }
return ( return (
<div id="admin-panel" className="pb-3"> <div id="admin-panel" className="pb-4">
<h3 className="py-5">{ t('admin.admin_panel') }</h3> <h3 className="py-5">{ t('admin.admin_panel') }</h3>
<Card className="border-0 card-shadow"> <Card className="border-0 card-shadow">
<Tab.Container activeKey="site_settings"> <Tab.Container activeKey="site_settings">
......
...@@ -45,7 +45,7 @@ export default function Tenants() { ...@@ -45,7 +45,7 @@ export default function Tenants() {
const { data: tenants, isLoading } = useTenants({ search: searchInput, page }); const { data: tenants, isLoading } = useTenants({ search: searchInput, page });
return ( return (
<div id="admin-panel" className="pb-3"> <div id="admin-panel" className="pb-4">
<h3 className="py-5">{ t('admin.admin_panel') }</h3> <h3 className="py-5">{ t('admin.admin_panel') }</h3>
<Card className="border-0 card-shadow"> <Card className="border-0 card-shadow">
<Tab.Container activeKey="tenants"> <Tab.Container activeKey="tenants">
......
...@@ -35,7 +35,7 @@ import SimpleSelect from '../shared_components/utilities/SimpleSelect'; ...@@ -35,7 +35,7 @@ import SimpleSelect from '../shared_components/utilities/SimpleSelect';
// TODO: Amir - Refactor this. // TODO: Amir - Refactor this.
export default function RecordingRow({ export default function RecordingRow({
recording, visibilityMutation: useVisibilityAPI, deleteMutation: useDeleteAPI, adminTable, recording, visibilityMutation: useVisibilityAPI, deleteMutation: useDeleteAPI, adminTable, dropUp,
}) { }) {
const { t } = useTranslation(); const { t } = useTranslation();
...@@ -104,6 +104,7 @@ export default function RecordingRow({ ...@@ -104,6 +104,7 @@ export default function RecordingRow({
<td className="border-0"> <td className="border-0">
<SimpleSelect <SimpleSelect
defaultValue={recording.visibility} defaultValue={recording.visibility}
dropUp={dropUp}
> >
<Dropdown.Item <Dropdown.Item
key="Public/Protected" key="Public/Protected"
...@@ -205,6 +206,7 @@ export default function RecordingRow({ ...@@ -205,6 +206,7 @@ export default function RecordingRow({
RecordingRow.defaultProps = { RecordingRow.defaultProps = {
adminTable: false, adminTable: false,
dropUp: false,
}; };
RecordingRow.propTypes = { RecordingRow.propTypes = {
...@@ -227,4 +229,5 @@ RecordingRow.propTypes = { ...@@ -227,4 +229,5 @@ RecordingRow.propTypes = {
visibilityMutation: PropTypes.func.isRequired, visibilityMutation: PropTypes.func.isRequired,
deleteMutation: PropTypes.func.isRequired, deleteMutation: PropTypes.func.isRequired,
adminTable: PropTypes.bool, adminTable: PropTypes.bool,
dropUp: PropTypes.bool,
}; };
...@@ -28,7 +28,7 @@ import SearchBar from '../shared_components/search/SearchBar'; ...@@ -28,7 +28,7 @@ import SearchBar from '../shared_components/search/SearchBar';
import ProcessingRecordingRow from './ProcessingRecordingRow'; import ProcessingRecordingRow from './ProcessingRecordingRow';
export default function RecordingsList({ export default function RecordingsList({
recordings, isLoading, setPage, searchInput, setSearchInput, recordingsProcessing, adminTable, recordings, isLoading, setPage, searchInput, setSearchInput, recordingsProcessing, adminTable, numPlaceholders,
}) { }) {
const { t } = useTranslation(); const { t } = useTranslation();
...@@ -65,14 +65,19 @@ export default function RecordingsList({ ...@@ -65,14 +65,19 @@ export default function RecordingsList({
<tbody className="border-top-0"> <tbody className="border-top-0">
{[...Array(recordingsProcessing)].map(() => <ProcessingRecordingRow />)} {[...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 // eslint-disable-next-line react/no-array-index-key
<RecordingsListRowPlaceHolder key={idx} /> <RecordingsListRowPlaceHolder key={idx} />
))) )))
} }
{ {
(recordings?.data?.length > 0 && recordings?.data?.map((recording) => ( (recordings?.data?.length > 0 && recordings?.data?.map((recording, idx) => (
<RoomsRecordingRow key={recording.id} recording={recording} adminTable={adminTable} /> <RoomsRecordingRow
key={recording.id}
recording={recording}
adminTable={adminTable}
dropUp={(recordings?.meta?.page || 0) * (recordings?.meta?.items || 0) - 1 === idx}
/>
))) )))
} }
</tbody> </tbody>
...@@ -99,10 +104,11 @@ export default function RecordingsList({ ...@@ -99,10 +104,11 @@ export default function RecordingsList({
} }
RecordingsList.defaultProps = { RecordingsList.defaultProps = {
recordings: { data: [], meta: { page: 1, pages: 1 } }, recordings: { data: [], meta: { page: 1, pages: 1, items: 3 } },
recordingsProcessing: 0, recordingsProcessing: 0,
searchInput: '', searchInput: '',
adminTable: false, adminTable: false,
numPlaceholders: 7,
}; };
RecordingsList.propTypes = { RecordingsList.propTypes = {
...@@ -124,6 +130,7 @@ RecordingsList.propTypes = { ...@@ -124,6 +130,7 @@ RecordingsList.propTypes = {
meta: PropTypes.shape({ meta: PropTypes.shape({
page: PropTypes.number, page: PropTypes.number,
pages: PropTypes.number, pages: PropTypes.number,
items: PropTypes.number,
}), }),
}), }),
isLoading: PropTypes.bool.isRequired, isLoading: PropTypes.bool.isRequired,
...@@ -132,4 +139,5 @@ RecordingsList.propTypes = { ...@@ -132,4 +139,5 @@ RecordingsList.propTypes = {
setSearchInput: PropTypes.func.isRequired, setSearchInput: PropTypes.func.isRequired,
recordingsProcessing: PropTypes.number, recordingsProcessing: PropTypes.number,
adminTable: PropTypes.bool, adminTable: PropTypes.bool,
numPlaceholders: PropTypes.number,
}; };
...@@ -31,6 +31,7 @@ export default function UserRecordings() { ...@@ -31,6 +31,7 @@ export default function UserRecordings() {
setPage={setPage} setPage={setPage}
setSearchInput={setSearchInput} setSearchInput={setSearchInput}
searchInput={searchInput} searchInput={searchInput}
numPlaceholders={5}
/> />
</div> </div>
); );
......
...@@ -36,6 +36,7 @@ export default function RoomRecordings() { ...@@ -36,6 +36,7 @@ export default function RoomRecordings() {
setSearchInput={setSearchInput} setSearchInput={setSearchInput}
searchInput={searchInput} searchInput={searchInput}
recordingsProcessing={roomRecordingsProcessing.data} recordingsProcessing={roomRecordingsProcessing.data}
numPlaceholders={3}
/> />
</div> </div>
); );
......
...@@ -20,19 +20,21 @@ import useUpdateRecordingVisibility from '../../../hooks/mutations/recordings/us ...@@ -20,19 +20,21 @@ import useUpdateRecordingVisibility from '../../../hooks/mutations/recordings/us
import useDeleteRecording from '../../../hooks/mutations/recordings/useDeleteRecording'; import useDeleteRecording from '../../../hooks/mutations/recordings/useDeleteRecording';
import RecordingRow from '../RecordingRow'; import RecordingRow from '../RecordingRow';
export default function RoomsRecordingRow({ recording, adminTable }) { export default function RoomsRecordingRow({ recording, adminTable, dropUp }) {
return ( return (
<RecordingRow <RecordingRow
adminTable={adminTable} adminTable={adminTable}
recording={recording} recording={recording}
visibilityMutation={useUpdateRecordingVisibility} visibilityMutation={useUpdateRecordingVisibility}
deleteMutation={useDeleteRecording} deleteMutation={useDeleteRecording}
dropUp={dropUp}
/> />
); );
} }
RoomsRecordingRow.defaultProps = { RoomsRecordingRow.defaultProps = {
adminTable: false, adminTable: false,
dropUp: false,
}; };
RoomsRecordingRow.propTypes = { RoomsRecordingRow.propTypes = {
...@@ -51,4 +53,5 @@ RoomsRecordingRow.propTypes = { ...@@ -51,4 +53,5 @@ RoomsRecordingRow.propTypes = {
map: PropTypes.func, map: PropTypes.func,
}).isRequired, }).isRequired,
adminTable: PropTypes.bool, adminTable: PropTypes.bool,
dropUp: PropTypes.bool,
}; };
...@@ -19,12 +19,12 @@ import React from 'react'; ...@@ -19,12 +19,12 @@ import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { ChevronDownIcon } from '@heroicons/react/20/solid'; 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 // Get the currently selected option and set the dropdown toggle to that value
const defaultString = children?.filter((item) => item.props.value === defaultValue)[0]; const defaultString = children?.filter((item) => item.props.value === defaultValue)[0];
return ( return (
<Dropdown className="simple-select"> <Dropdown className="simple-select" drop={dropUp ? 'up' : undefined}>
<Dropdown.Toggle> <Dropdown.Toggle>
{ defaultString?.props?.children } { defaultString?.props?.children }
<ChevronDownIcon className="hi-s float-end" /> <ChevronDownIcon className="hi-s float-end" />
...@@ -38,10 +38,12 @@ export default function SimpleSelect({ defaultValue, children }) { ...@@ -38,10 +38,12 @@ export default function SimpleSelect({ defaultValue, children }) {
SimpleSelect.defaultProps = { SimpleSelect.defaultProps = {
defaultValue: '', defaultValue: '',
dropUp: false,
children: undefined, children: undefined,
}; };
SimpleSelect.propTypes = { SimpleSelect.propTypes = {
defaultValue: PropTypes.string, defaultValue: PropTypes.string,
dropUp: PropTypes.bool,
children: PropTypes.arrayOf(PropTypes.element), children: PropTypes.arrayOf(PropTypes.element),
}; };
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment