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

Various improvements throughout the app (#5325)

* Various improvements

* Invalidate right queries

* Improve recording visibility dropdown

* Fix min width

* Fix dropdown overflow

* small styling
parent 4b5f0dea
No related branches found
No related tags found
No related merge requests found
...@@ -441,6 +441,43 @@ input.search-bar { ...@@ -441,6 +441,43 @@ input.search-bar {
} }
} }
.simple-select {
button {
background: white !important;
color: black !important;
border-color: gainsboro !important;
text-align: left;
width: 220px;
&:hover, &:focus, &:active {
background: white !important;
color: black !important;
border-color: gainsboro !important;
}
&:focus {
box-shadow: 0 0 0 0.25rem var(--brand-color-light) !important;
}
&::after {
display: none;
}
}
.dropdown-menu {
min-width: 220px;
}
}
@media (max-width: 767px) {
.table-responsive .dropdown-menu {
position: static !important;
}
}
@media (min-width: 768px) {
.table-responsive {
overflow: visible;
}
}
input[type='text']:focus { input[type='text']:focus {
border-color: whitesmoke !important; border-color: whitesmoke !important;
box-shadow: 0 0 0 2px var(--brand-color-light) !important; box-shadow: 0 0 0 2px var(--brand-color-light) !important;
......
...@@ -15,11 +15,13 @@ ...@@ -15,11 +15,13 @@
// with Greenlight; if not, see <http://www.gnu.org/licenses/>. // with Greenlight; if not, see <http://www.gnu.org/licenses/>.
#user-recordings, #room-recordings { #user-recordings, #room-recordings {
min-height: 400px;
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;
border-spacing: 0; border-spacing: 0;
overflow: hidden; border-top-width: 0 !important;
} }
tr { tr {
......
...@@ -17,7 +17,6 @@ ...@@ -17,7 +17,6 @@
import { import {
VideoCameraIcon, TrashIcon, PencilSquareIcon, ClipboardDocumentIcon, EllipsisVerticalIcon, VideoCameraIcon, TrashIcon, PencilSquareIcon, ClipboardDocumentIcon, EllipsisVerticalIcon,
} from '@heroicons/react/24/outline'; } from '@heroicons/react/24/outline';
import Form from 'react-bootstrap/Form';
import React, { useState } from 'react'; import React, { useState } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { import {
...@@ -32,6 +31,7 @@ import Modal from '../shared_components/modals/Modal'; ...@@ -32,6 +31,7 @@ import Modal from '../shared_components/modals/Modal';
import { localizeDateTimeString } from '../../helpers/DateTimeHelper'; import { localizeDateTimeString } from '../../helpers/DateTimeHelper';
import useRedirectRecordingUrl from '../../hooks/mutations/recordings/useRedirectRecordingUrl'; import useRedirectRecordingUrl from '../../hooks/mutations/recordings/useRedirectRecordingUrl';
import useCopyRecordingUrl from '../../hooks/mutations/recordings/useCopyRecordingUrl'; import useCopyRecordingUrl from '../../hooks/mutations/recordings/useCopyRecordingUrl';
import SimpleSelect from '../shared_components/utilities/SimpleSelect';
// TODO: Amir - Refactor this. // TODO: Amir - Refactor this.
export default function RecordingRow({ export default function RecordingRow({
...@@ -102,29 +102,48 @@ export default function RecordingRow({ ...@@ -102,29 +102,48 @@ export default function RecordingRow({
<td className="border-0"> {t('recording.length_in_minutes', { recording })} </td> <td className="border-0"> {t('recording.length_in_minutes', { recording })} </td>
<td className="border-0"> {recording.participants} </td> <td className="border-0"> {recording.participants} </td>
<td className="border-0"> <td className="border-0">
{/* TODO: Refactor this. */} <SimpleSelect
<Form.Select
className="visibility-dropdown"
onChange={(event) => {
visibilityAPI.mutate({ visibility: event.target.value, id: recording.record_id });
}}
defaultValue={recording.visibility} defaultValue={recording.visibility}
disabled={visibilityAPI.isLoading}
> >
<option value="Published">{t('recording.published')}</option> <Dropdown.Item
<option value="Unpublished">{t('recording.unpublished')}</option> key="Public/Protected"
{recording?.protectable === true value="Public/Protected"
&& ( onClick={() => visibilityAPI.mutate({ visibility: 'Public/Protected', id: recording.record_id })}
<> >
<option value="Protected">{t('recording.protected')}</option> {t('recording.public_protected')}
<option value="Public/Protected">{t('recording.public_protected')}</option> </Dropdown.Item>
</> <Dropdown.Item
)} key="Public"
<option value="Public">{t('recording.public')}</option> value="Public"
</Form.Select> onClick={() => visibilityAPI.mutate({ visibility: 'Public', id: recording.record_id })}
>
{t('recording.public')}
</Dropdown.Item>
<Dropdown.Item
key="Protected"
value="Protected"
onClick={() => visibilityAPI.mutate({ visibility: 'Protected', id: recording.record_id })}
>
{t('recording.protected')}
</Dropdown.Item>
<Dropdown.Item
key="Published"
value="Published"
onClick={() => visibilityAPI.mutate({ visibility: 'Published', id: recording.record_id })}
>
{t('recording.published')}
</Dropdown.Item>
<Dropdown.Item
key="Unpublished"
value="Unpublished"
onClick={() => visibilityAPI.mutate({ visibility: 'Unpublished', id: recording.record_id })}
>
{t('recording.unpublished')}
</Dropdown.Item>
</SimpleSelect>
</td> </td>
<td className="border-0"> <td className="border-0">
{formats.map((format) => ( {recording?.visibility !== 'Unpublished' && formats.map((format) => (
<Button <Button
onClick={() => redirectRecordingUrl.mutate({ record_id: recording.record_id, format: format.recording_type })} onClick={() => redirectRecordingUrl.mutate({ record_id: recording.record_id, format: format.recording_type })}
className={`btn-sm rounded-pill me-1 mt-1 border-0 btn-format-${format.recording_type.toLowerCase()}`} className={`btn-sm rounded-pill me-1 mt-1 border-0 btn-format-${format.recording_type.toLowerCase()}`}
...@@ -158,6 +177,7 @@ export default function RecordingRow({ ...@@ -158,6 +177,7 @@ export default function RecordingRow({
) )
: ( : (
<Stack direction="horizontal" className="float-end recordings-icons"> <Stack direction="horizontal" className="float-end recordings-icons">
{ recording?.visibility !== 'Unpublished' && (
<Button <Button
variant="icon" variant="icon"
className="mt-1 me-3" className="mt-1 me-3"
...@@ -165,6 +185,7 @@ export default function RecordingRow({ ...@@ -165,6 +185,7 @@ export default function RecordingRow({
> >
<ClipboardDocumentIcon className="hi-s text-muted" /> <ClipboardDocumentIcon className="hi-s text-muted" />
</Button> </Button>
)}
<Modal <Modal
modalButton={<Dropdown.Item className="btn btn-icon"><TrashIcon className="hi-s me-2" /></Dropdown.Item>} modalButton={<Dropdown.Item className="btn btn-icon"><TrashIcon className="hi-s me-2" /></Dropdown.Item>}
body={( body={(
......
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
//
// Copyright (c) 2022 BigBlueButton Inc. and by respective authors (see below).
//
// This program is free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License as published by the Free Software
// Foundation; either version 3.0 of the License, or (at your option) any later
// version.
//
// Greenlight is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License along
// with Greenlight; if not, see <http://www.gnu.org/licenses/>.
import { Dropdown } from 'react-bootstrap';
import React from 'react';
import PropTypes from 'prop-types';
import { ChevronDownIcon } from '@heroicons/react/20/solid';
export default function SimpleSelect({ defaultValue, 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.Toggle>
{ defaultString?.props?.children }
<ChevronDownIcon className="hi-s float-end" />
</Dropdown.Toggle>
<Dropdown.Menu>
{children}
</Dropdown.Menu>
</Dropdown>
);
}
SimpleSelect.defaultProps = {
defaultValue: '',
children: undefined,
};
SimpleSelect.propTypes = {
defaultValue: PropTypes.string,
children: PropTypes.arrayOf(PropTypes.element),
};
...@@ -28,6 +28,8 @@ export default function useUpdateRecordingVisibility() { ...@@ -28,6 +28,8 @@ export default function useUpdateRecordingVisibility() {
{ {
onSuccess: () => { onSuccess: () => {
queryClient.invalidateQueries(['getRecordings']); queryClient.invalidateQueries(['getRecordings']);
queryClient.invalidateQueries(['getRoomRecordings']);
queryClient.invalidateQueries(['getServerRecordings']);
toast.success(t('toast.success.recording.recording_visibility_updated')); toast.success(t('toast.success.recording.recording_visibility_updated'));
}, },
onError: () => { onError: () => {
......
...@@ -20,4 +20,8 @@ class PublicRecordingSerializer < ApplicationSerializer ...@@ -20,4 +20,8 @@ class PublicRecordingSerializer < ApplicationSerializer
attributes :id, :record_id, :name, :length, :recorded_at attributes :id, :record_id, :name, :length, :recorded_at
has_many :formats has_many :formats
def formats
object.formats.filter { |format| format.recording_type != 'statistics' }
end
end end
...@@ -37,7 +37,7 @@ namespace :admin do ...@@ -37,7 +37,7 @@ namespace :admin do
task :super_admin, %i[name email password] => :environment do |_task, args| task :super_admin, %i[name email password] => :environment do |_task, args|
super_admin_email = "superadmin-#{args[:email]}" super_admin_email = "superadmin-#{args[:email]}"
user = User.create( user = User.new(
name: args[:name], name: args[:name],
email: super_admin_email, email: super_admin_email,
password: args[:password], password: args[:password],
...@@ -48,11 +48,16 @@ namespace :admin do ...@@ -48,11 +48,16 @@ namespace :admin do
language: I18n.default_locale language: I18n.default_locale
) )
if user.save
success 'User account was created successfully!' success 'User account was created successfully!'
info " Name: #{user.name}" info " Name: #{user.name}"
info " Email: #{user.email}" info " Email: #{user.email}"
info " Password: #{user.password}" info " Password: #{user.password}"
info " Role: #{user.role.name}" info " Role: #{user.role.name}"
else
warning 'There was an error creating this user'
err " Error: #{user.errors.full_messages}"
end
exit 0 exit 0
end end
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment