change(ui) - fix date range in url, other misc changes
This commit is contained in:
parent
227bcda4e5
commit
b6f2cc59f7
6 changed files with 217 additions and 115 deletions
|
|
@ -68,13 +68,11 @@ class Login extends React.Component {
|
|||
|
||||
return (
|
||||
<div className="flex flex-col md:flex-row" style={{ height: '100vh' }}>
|
||||
<div className={cn('md:w-6/12 relative', stl.left)}>
|
||||
<div className={cn('md:w-6/12 relative overflow-hidden', stl.left)}>
|
||||
<div className="px-6 pt-10">
|
||||
<img src="/assets/logo-white.svg" />
|
||||
</div>
|
||||
<div className="color-white text-lg flex items-center">
|
||||
<img style={{ width: '800px', position: 'absolute', bottom: 0, left: 0 }} src={LoginBg} />
|
||||
</div>
|
||||
<img style={{ width: '800px', position: 'absolute', bottom: -100, left: 0 }} src={LoginBg} />
|
||||
</div>
|
||||
<div className="md:w-6/12 flex items-center justify-center py-10">
|
||||
<div className="">
|
||||
|
|
|
|||
|
|
@ -5,47 +5,54 @@ import { Icon } from 'UI';
|
|||
import stl from './signup.module.css';
|
||||
import cn from 'classnames';
|
||||
import SignupForm from './SignupForm';
|
||||
|
||||
import RegisterBg from '../../svg/register.svg';
|
||||
|
||||
const BulletItem = ({ text }) => (
|
||||
<div className="flex items-center mb-4">
|
||||
<div className="mr-3 h-8 w-8 rounded-full bg-white shadow flex items-center justify-center">
|
||||
<Icon name="check" size="26"/>
|
||||
<Icon name="check" size="26" />
|
||||
</div>
|
||||
<div>{text}</div>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
@withPageTitle('Signup - OpenReplay')
|
||||
export default class Signup extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<div className="flex" style={{ height: '100vh'}}>
|
||||
<div className={cn("w-6/12", stl.left)}>
|
||||
<div className="flex" style={{ height: '100vh' }}>
|
||||
<div className={cn('w-6/12 relative overflow-hidden', stl.left)}>
|
||||
<div className="px-6 pt-10">
|
||||
<img src="/assets/logo-white.svg" />
|
||||
<img src="/assets/logo-white.svg" />
|
||||
</div>
|
||||
<div className="color-white text-lg flex items-center px-20 pt-32">
|
||||
<div>
|
||||
<div className="flex items-center text-3xl font-bold mb-6">
|
||||
OpenReplay Cloud <div className="ml-2"><Icon name="signup" size="28" color="white" /></div>
|
||||
</div>
|
||||
<div>OpenReplay Cloud is the hosted version of our open-source project.</div>
|
||||
<div>We’ll manage hosting, scaling and upgrades.</div>
|
||||
|
||||
<div className="mt-8">
|
||||
<BulletItem text="First 1K sessions free every month." />
|
||||
<BulletItem text="Pay per use, cancel anytime" />
|
||||
<BulletItem text="Community, Slack & email support" />
|
||||
<img
|
||||
style={{ width: '800px', position: 'absolute', bottom: -100, left: 0 }}
|
||||
src={RegisterBg}
|
||||
/>
|
||||
<div className="color-white text-lg flex items-center px-20 pt-32">
|
||||
<div>
|
||||
<div className="flex items-center text-3xl font-bold mb-6">
|
||||
OpenReplay Cloud{' '}
|
||||
<div className="ml-2">
|
||||
<Icon name="signup" size="28" color="white" />
|
||||
</div>
|
||||
</div>
|
||||
<div>OpenReplay Cloud is the hosted version of our open-source project.</div>
|
||||
<div>We’ll manage hosting, scaling and upgrades.</div>
|
||||
|
||||
<div className="mt-8">
|
||||
<BulletItem text="First 1K sessions free every month." />
|
||||
<BulletItem text="Pay per use, cancel anytime" />
|
||||
<BulletItem text="Community, Slack & email support" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-6/12 flex items-center justify-center">
|
||||
<div className="">
|
||||
<SignupForm />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-6/12 flex items-center justify-center">
|
||||
<div className="">
|
||||
<SignupForm />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,10 +2,8 @@ import React, { useEffect } from 'react';
|
|||
import { useHistory } from 'react-router';
|
||||
import { connect } from 'react-redux';
|
||||
import { addFilterByKeyAndValue, addFilter } from 'Duck/search';
|
||||
import { getFilterKeyTypeByKey, setQueryParamKeyFromFilterkey } from 'Types/filter/filterType';
|
||||
import { filtersMap } from 'App/types/filter/newFilter';
|
||||
import Filter from 'Types/filter/filter';
|
||||
import { applyFilter } from 'Duck/search';
|
||||
import { createUrlQuery, getFiltersFromQuery } from 'App/utils/search';
|
||||
|
||||
interface Props {
|
||||
appliedFilter: any;
|
||||
|
|
@ -13,93 +11,25 @@ interface Props {
|
|||
addFilterByKeyAndValue: typeof addFilterByKeyAndValue;
|
||||
addFilter: typeof addFilter;
|
||||
}
|
||||
const SessionSearchQueryParamHandler = React.memo((props: Props) => {
|
||||
const SessionSearchQueryParamHandler = (props: Props) => {
|
||||
const { appliedFilter } = props;
|
||||
const history = useHistory();
|
||||
|
||||
const createUrlQuery = (filters: any) => {
|
||||
const query: any = [];
|
||||
filters.forEach((filter: any) => {
|
||||
const item: any = {};
|
||||
if (filter.value.length > 0) {
|
||||
const _key = setQueryParamKeyFromFilterkey(filter.key);
|
||||
if (_key) {
|
||||
let str = `${filter.operator}|${filter.value.join('|')}`;
|
||||
if (filter.hasSource) {
|
||||
str = `${str}^${filter.sourceOperator}|${filter.source.join('|')}`;
|
||||
}
|
||||
item.key = _key + '[]';
|
||||
item.value = str;
|
||||
} else {
|
||||
let str = `${filter.operator}|${filter.value.join('|')}`;
|
||||
item.key = [filter.key] + '[]';
|
||||
item.value = str;
|
||||
}
|
||||
|
||||
query.push(item);
|
||||
}
|
||||
|
||||
});
|
||||
return query;
|
||||
};
|
||||
|
||||
const applyFilterFromQuery = () => {
|
||||
if (appliedFilter.filters.size > 0 || history.location.search === "") {
|
||||
return;
|
||||
}
|
||||
const entires = getQueryObject(history.location.search);
|
||||
const _filters: any = { ...filtersMap };
|
||||
if (entires.length > 0) {
|
||||
const filters: any = [];
|
||||
entires.forEach((item: any) => {
|
||||
if (!item.key || !item.value) { return }
|
||||
let filter: any = {}
|
||||
const filterKey = getFilterKeyTypeByKey(item.key);
|
||||
const tmp = item.value.split('^');
|
||||
const valueArr = tmp[0].split('|');
|
||||
const operator = valueArr.shift();
|
||||
const sourceArr = tmp[1] ? tmp[1].split('|') : [];
|
||||
const sourceOperator = sourceArr.shift();
|
||||
|
||||
if (filterKey) {
|
||||
filter.type = filterKey;
|
||||
filter.key = filterKey;
|
||||
} else {
|
||||
filter = _filters[item.key];
|
||||
if (!!filter) {
|
||||
filter.type = filter.key;
|
||||
filter.key = filter.key;
|
||||
}
|
||||
}
|
||||
filter.value = valueArr;
|
||||
filter.operator = operator;
|
||||
filter.source = sourceArr;
|
||||
filter.sourceOperator = !!sourceOperator ? decodeURI(sourceOperator) : null;
|
||||
filters.push(filter);
|
||||
});
|
||||
const f = Filter({ filters })
|
||||
props.applyFilter(f);
|
||||
}
|
||||
const filter = getFiltersFromQuery(history.location.search, appliedFilter);
|
||||
props.applyFilter(filter, true);
|
||||
};
|
||||
|
||||
const generateUrlQuery = () => {
|
||||
const query: any = createUrlQuery(appliedFilter.filters);
|
||||
|
||||
let queryString = query.reduce((acc: any, curr: any, index: any) => {
|
||||
acc += `${curr.key}=${curr.value}`;
|
||||
if (index < query.length - 1) {
|
||||
acc += '&';
|
||||
}
|
||||
return acc;
|
||||
}, '');
|
||||
|
||||
history.replace({ search: queryString });
|
||||
const search: any = createUrlQuery(appliedFilter);
|
||||
history.replace({ search });
|
||||
};
|
||||
|
||||
useEffect(applyFilterFromQuery, []);
|
||||
useEffect(generateUrlQuery, [appliedFilter]);
|
||||
|
||||
return <></>;
|
||||
});
|
||||
};
|
||||
|
||||
export default connect(
|
||||
(state: any) => ({
|
||||
|
|
@ -107,11 +37,3 @@ export default connect(
|
|||
}),
|
||||
{ addFilterByKeyAndValue, addFilter, applyFilter }
|
||||
)(SessionSearchQueryParamHandler);
|
||||
|
||||
function getQueryObject(search: any) {
|
||||
let jsonArray = search.slice(1).split('&').map((item: any) => {
|
||||
let [key, value] = item.split('=');
|
||||
return {key: key.slice(0, -2), value};
|
||||
});
|
||||
return jsonArray;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -71,6 +71,7 @@ function reducer(state = initialState, action = {}) {
|
|||
case EDIT:
|
||||
return state.mergeIn(['instance'], action.instance).set('currentPage', 1);
|
||||
case APPLY:
|
||||
|
||||
return action.fromUrl ? state.set('instance', Filter(action.filter)) : state.mergeIn(['instance'], action.filter).set('currentPage', 1);
|
||||
case success(FETCH):
|
||||
return state.set('instance', action.data);
|
||||
|
|
|
|||
57
frontend/app/svg/register.svg
Normal file
57
frontend/app/svg/register.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 48 KiB |
117
frontend/app/utils/search.ts
Normal file
117
frontend/app/utils/search.ts
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
import { getFilterKeyTypeByKey, setQueryParamKeyFromFilterkey } from 'Types/filter/filterType';
|
||||
import Period, { LAST_24_HOURS, LAST_7_DAYS, LAST_30_DAYS, CUSTOM_RANGE } from 'Types/app/period';
|
||||
import Filter from 'Types/filter/filter';
|
||||
import { filtersMap } from 'App/types/filter/newFilter';
|
||||
|
||||
export const createUrlQuery = (filter: any) => {
|
||||
const query = [];
|
||||
|
||||
for (const f of filter.filters) {
|
||||
if (!f.value.length) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let str = `${f.operator}|${f.value.join('|')}`;
|
||||
if (f.hasSource) {
|
||||
str = `${str}^${f.sourceOperator}|${f.source.join('|')}`;
|
||||
}
|
||||
|
||||
let key: any = setQueryParamKeyFromFilterkey(f.key);
|
||||
if (!key) {
|
||||
key = [f.key];
|
||||
}
|
||||
|
||||
query.push({ key: key + '[]', value: str });
|
||||
}
|
||||
|
||||
if (query.length > 0) {
|
||||
query.push({ key: 'range[]', value: filter.rangeValue });
|
||||
if (filter.rangeValue === CUSTOM_RANGE) {
|
||||
query.push({ key: 'rStart[]', value: filter.startDate });
|
||||
query.push({ key: 'rEnd[]', value: filter.endDate });
|
||||
}
|
||||
}
|
||||
|
||||
return query.map(({ key, value }) => `${key}=${value}`).join('&');
|
||||
};
|
||||
|
||||
export const getFiltersFromQuery = (search: string, filter: any) => {
|
||||
if (!search || filter.filters.size > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const entires = getQueryObject(search);
|
||||
const period: any = getPeriodFromEntries(entires);
|
||||
const filters = getFiltersFromEntries(entires);
|
||||
|
||||
return Filter({ filters, rangeValue: period.rangeName });
|
||||
};
|
||||
|
||||
const getFiltersFromEntries = (entires: any) => {
|
||||
const _filters: any = { ...filtersMap };
|
||||
const filters: any = [];
|
||||
if (entires.length > 0) {
|
||||
entires.forEach((item: any) => {
|
||||
if (!item.key || !item.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
let filter: any = {};
|
||||
const filterKey = getFilterKeyTypeByKey(item.key);
|
||||
if (!filterKey) {
|
||||
return;
|
||||
}
|
||||
const tmp = item.value.split('^');
|
||||
const valueArr = tmp[0].split('|');
|
||||
const operator = valueArr.shift();
|
||||
const sourceArr = tmp[1] ? tmp[1].split('|') : [];
|
||||
const sourceOperator = sourceArr.shift();
|
||||
|
||||
if (filterKey) {
|
||||
filter.type = filterKey;
|
||||
filter.key = filterKey;
|
||||
} else {
|
||||
filter = _filters[item.key];
|
||||
if (!!filter) {
|
||||
filter.type = filter.key;
|
||||
filter.key = filter.key;
|
||||
}
|
||||
}
|
||||
|
||||
filter.value = valueArr;
|
||||
filter.operator = operator;
|
||||
filter.source = sourceArr;
|
||||
filter.sourceOperator = !!sourceOperator ? decodeURI(sourceOperator) : null;
|
||||
if (!filter.filters || filter.filters.size === 0) {
|
||||
filters.push(filter);
|
||||
}
|
||||
});
|
||||
}
|
||||
return filters;
|
||||
};
|
||||
|
||||
const getPeriodFromEntries = (entires: any) => {
|
||||
const rangeFilter = entires.find(({ key }: any) => key === 'range');
|
||||
if (!rangeFilter) {
|
||||
return Period();
|
||||
}
|
||||
|
||||
if (rangeFilter.value === CUSTOM_RANGE) {
|
||||
const start = entires.find(({ key }: any) => key === 'rStart').value;
|
||||
const end = entires.find(({ key }: any) => key === 'rEnd').value;
|
||||
return Period({ rangeName: rangeFilter.value, start, end });
|
||||
}
|
||||
|
||||
return Period({ rangeName: rangeFilter.value });
|
||||
};
|
||||
|
||||
function getQueryObject(search: any) {
|
||||
let jsonArray = search
|
||||
.slice(1)
|
||||
.split('&')
|
||||
.map((item: any) => {
|
||||
let [key, value] = item.split('=');
|
||||
return { key: key.slice(0, -2), value };
|
||||
});
|
||||
return jsonArray;
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue