change(ui) - assist recordings pagination api

This commit is contained in:
Shekar Siri 2023-04-28 17:29:48 +02:00
parent 9fb1735d2c
commit b78b05791e
4 changed files with 86 additions and 55 deletions

View file

@ -5,31 +5,45 @@ import RecordingsSearch from './RecordingsSearch';
import RecordingsList from './RecordingsList'; import RecordingsList from './RecordingsList';
import { useStore } from 'App/mstore'; import { useStore } from 'App/mstore';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import SelectDateRange from 'Shared/SelectDateRange/SelectDateRange';
import Period from 'Types/app/period';
import { observer } from 'mobx-react-lite';
function Recordings({ userId }: { userId: string }) { interface Props {
userId: string;
filter: any;
}
function Recordings(props: Props) {
const { userId } = props;
const { recordingsStore } = useStore(); const { recordingsStore } = useStore();
const recordingsOwner = [ const recordingsOwner = [
{ value: '0', label: 'All Recordings' }, { value: '0', label: 'All Recordings' },
{ value: userId, label: 'My Recordings' }, { value: userId, label: 'My Recordings' }
]; ];
const onDateChange = (e: any) => {
recordingsStore.updateTimestamps(e);
};
return ( return (
<div style={{ maxWidth: '1300px', margin: 'auto' }} className="bg-white rounded py-4 border"> <div style={{ maxWidth: '1300px', margin: 'auto' }} className='bg-white rounded py-4 border'>
<div className="flex items-center mb-4 justify-between px-6"> <div className='flex items-center mb-4 justify-between px-6'>
<div className="flex items-baseline mr-3"> <div className='flex items-baseline mr-3'>
<PageTitle title="Recordings" /> <PageTitle title='Recordings' />
</div> </div>
<div className="ml-auto flex items-center"> <div className='ml-auto flex items-center'>
<SelectDateRange period={recordingsStore.period} onChange={onDateChange} right={true} />
<Select <Select
name="recsOwner" name='recsOwner'
plain plain
right right
options={recordingsOwner} options={recordingsOwner}
onChange={({ value }) => recordingsStore.setUserId(value.value)} onChange={({ value }) => recordingsStore.setUserId(value.value)}
defaultValue={recordingsOwner[0].value} defaultValue={recordingsOwner[0].value}
/> />
<div className="ml-4 w-1/4" style={{ minWidth: 300 }}> <div className='ml-4 w-1/4' style={{ minWidth: 300 }}>
<RecordingsSearch /> <RecordingsSearch />
</div> </div>
</div> </div>
@ -39,6 +53,6 @@ function Recordings({ userId }: { userId: string }) {
); );
} }
export default connect((state: any) => ({ userId: state.getIn(['user', 'account', 'id']) }))( export default connect((state: any) => ({
Recordings userId: state.getIn(['user', 'account', 'id'])
); }))(observer(Recordings));

View file

@ -1,42 +1,38 @@
import { observer } from 'mobx-react-lite'; import { observer } from 'mobx-react-lite';
import React from 'react'; import React from 'react';
import { NoContent, Pagination, Icon, Button } from 'UI'; import { NoContent, Pagination, Loader } from 'UI';
import { useStore } from 'App/mstore'; import { useStore } from 'App/mstore';
import { filterList } from 'App/utils';
import { sliceListPerPage } from 'App/utils';
import RecordsListItem from './RecordsListItem'; import RecordsListItem from './RecordsListItem';
import AnimatedSVG, { ICONS } from 'Shared/AnimatedSVG/AnimatedSVG'; import AnimatedSVG, { ICONS } from 'Shared/AnimatedSVG/AnimatedSVG';
function RecordingsList() { function RecordingsList() {
const { recordingsStore } = useStore(); const { recordingsStore } = useStore();
const [shownRecordings, setRecordings] = React.useState<any[]>([]); // const [shownRecordings, setRecordings] = React.useState<any[]>([]);
const recordings = recordingsStore.recordings; const recordings = recordingsStore.recordings;
const recordsSearch = recordingsStore.search; const recordsSearch = recordingsStore.search;
const page = recordingsStore.page;
const pageSize = recordingsStore.pageSize;
const total = recordingsStore.total;
React.useEffect(() => { React.useEffect(() => {
recordingsStore.fetchRecordings(); recordingsStore.fetchRecordings();
}, []); }, [page, recordingsStore.period, recordsSearch, recordingsStore.userId]);
React.useEffect(() => { const length = recordings.length;
setRecordings(filterList(recordings, recordsSearch, ['createdBy', 'name']));
}, [recordsSearch]);
const list = recordsSearch !== '' ? shownRecordings : recordings;
const length = list.length;
return ( return (
<NoContent <NoContent
show={length === 0} show={length === 0}
title={ title={
<div className="flex flex-col items-center justify-center"> <div className='flex flex-col items-center justify-center'>
<AnimatedSVG name={ICONS.NO_RECORDINGS} size={180} /> <AnimatedSVG name={ICONS.NO_RECORDINGS} size={180} />
<div className="text-center text-gray-600 mt-4"> <div className='text-center text-gray-600 mt-4'>
{recordsSearch !== '' ? 'No matching results' : 'No recordings available yet.'} {recordsSearch !== '' ? 'No matching results' : 'No recordings available yet.'}
</div> </div>
</div> </div>
} }
subtext={ subtext={
<div className="text-center flex justify-center items-center flex-col"> <div className='text-center flex justify-center items-center flex-col'>
<span> <span>
Record your co-browsing sessions and share them with your team for product feedback or Record your co-browsing sessions and share them with your team for product feedback or
training purposes. training purposes.
@ -44,32 +40,34 @@ function RecordingsList() {
</div> </div>
} }
> >
<div className="mt-3 border-b"> <div className='mt-3 border-b'>
<div className="grid grid-cols-12 py-2 font-medium px-6"> <Loader loading={recordingsStore.loading}>
<div className="col-span-8">Name</div> <div className='grid grid-cols-12 py-2 font-medium px-6'>
<div className="col-span-4">Recorded by</div> <div className='col-span-8'>Name</div>
</div> <div className='col-span-4'>Recorded by</div>
</div>
{sliceListPerPage(list, recordingsStore.page - 1, recordingsStore.pageSize).map( {recordings.map(
(record: any) => ( (record: any) => (
<React.Fragment key={record.recordId}> <React.Fragment key={record.recordId}>
<RecordsListItem record={record} /> <RecordsListItem record={record} />
</React.Fragment> </React.Fragment>
) )
)} )}
</Loader>
</div> </div>
<div className="w-full flex items-center justify-between pt-4 px-6"> <div className='w-full flex items-center justify-between pt-4 px-6'>
<div className="text-disabled-text"> <div className='text-disabled-text'>
Showing{' '} Showing{' '}
<span className="font-semibold">{Math.min(list.length, recordingsStore.pageSize)}</span>{' '} <span className='font-semibold'>{Math.min(length, pageSize)}</span>{' '}
out of <span className="font-semibold">{list.length}</span> Recording out of <span className='font-semibold'>{total}</span> Recording
</div> </div>
<Pagination <Pagination
page={recordingsStore.page} page={page}
totalPages={Math.ceil(length / recordingsStore.pageSize)} totalPages={Math.ceil(total / pageSize)}
onPageChange={(page) => recordingsStore.updatePage(page)} onPageChange={(page) => recordingsStore.updatePage(page)}
limit={recordingsStore.pageSize} limit={pageSize}
debounceRequest={100} debounceRequest={100}
/> />
</div> </div>

View file

@ -1,34 +1,48 @@
import { makeAutoObservable } from 'mobx'; import { makeAutoObservable } from 'mobx';
import { recordingsService } from 'App/services'; import { recordingsService } from 'App/services';
import { IRecord } from 'App/services/RecordingsService'; import { IRecord } from 'App/services/RecordingsService';
import Period, { LAST_7_DAYS } from 'Types/app/period';
export default class RecordingsStore { export default class RecordingsStore {
recordings: IRecord[] = []; recordings: IRecord[] = [];
loading: boolean; loading: boolean;
page = 1; page = 1;
pageSize = 15; total: number = 0;
pageSize = 5;
order: 'desc' | 'asc' = 'desc'; order: 'desc' | 'asc' = 'desc';
search = ''; search = '';
// later we will add search by user id // later we will add search by user id
userId = '0'; userId = '0';
startTimestamp = 0;
endTimestamp = 0;
rangeName: string = 'LAST_24_HOURS';
period: any = Period({ rangeName: LAST_7_DAYS });
constructor() { constructor() {
makeAutoObservable(this); makeAutoObservable(this);
} }
setRecordings(records: IRecord[]) { setRecordings(records: IRecord[], total?: number) {
this.total = total || 0;
this.recordings = records; this.recordings = records;
} }
setUserId(userId: string) { setUserId(userId: string) {
this.userId = userId; this.userId = userId;
this.fetchRecordings();
} }
updateSearch(val: string) { updateSearch(val: string) {
this.search = val; this.search = val;
this.page = 1;
} }
updateTimestamps(period: any): void {
const { start, end, rangeName } = period;
this.period = Period({ start, end, rangeName });
this.page = 1;
}
updatePage(page: number) { updatePage(page: number) {
this.page = page; this.page = page;
} }
@ -38,15 +52,17 @@ export default class RecordingsStore {
page: this.page, page: this.page,
limit: this.pageSize, limit: this.pageSize,
order: this.order, order: this.order,
search: this.search, query: this.search,
userId: this.userId === '0' ? undefined : this.userId, userId: this.userId === '0' ? undefined : this.userId,
startTimestamp: this.period.start,
endTimestamp: this.period.end
}; };
this.loading = true; this.loading = true;
try { try {
const recordings = await recordingsService.fetchRecordings(filter); const response: { records: [], total: number } = await recordingsService.fetchRecordings(filter);
this.setRecordings(recordings); this.setRecordings(response.records, response.total);
return recordings; return response.records;
} catch (e) { } catch (e) {
console.error(e); console.error(e);
} finally { } finally {
@ -54,7 +70,7 @@ export default class RecordingsStore {
} }
} }
async fetchRecordingUrl(id: number): Promise<string> { async fetchRecordingUrl(id: number): Promise<string | undefined> {
this.loading = true; this.loading = true;
try { try {
const recording = await recordingsService.fetchRecording(id); const recording = await recordingsService.fetchRecording(id);

View file

@ -10,7 +10,10 @@ interface FetchFilter {
page: number; page: number;
limit: number; limit: number;
order: 'asc' | 'desc'; order: 'asc' | 'desc';
search: string; query: string;
startTimestamp: number;
endTimestamp: number;
userId?: string;
} }
export interface IRecord { export interface IRecord {
@ -57,7 +60,7 @@ export default class RecordingsService {
}); });
} }
fetchRecordings(filters: FetchFilter): Promise<IRecord[]> { fetchRecordings(filters: FetchFilter): Promise<any> {
return this.client.post(`/assist/records`, filters).then((r) => { return this.client.post(`/assist/records`, filters).then((r) => {
return r.json().then((j) => j.data); return r.json().then((j) => j.data);
}); });