mirror of
https://github.com/kremalicious/umami.git
synced 2025-02-01 12:29:35 +01:00
Update insights report parameters. Added contains logic.
This commit is contained in:
parent
d59477deb5
commit
5daad2726e
@ -1,7 +1,7 @@
|
||||
.popup {
|
||||
display: flex;
|
||||
max-width: 300px;
|
||||
max-height: 400px;
|
||||
max-height: 210px;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
@ -19,6 +19,6 @@
|
||||
min-width: 180px;
|
||||
}
|
||||
|
||||
.menu {
|
||||
min-width: 200px;
|
||||
.text {
|
||||
min-width: 180px;
|
||||
}
|
||||
|
@ -1,6 +1,19 @@
|
||||
import { useState, useMemo } from 'react';
|
||||
import { Form, FormRow, Item, Flexbox, Dropdown, Button } from 'react-basics';
|
||||
import {
|
||||
Form,
|
||||
FormRow,
|
||||
Item,
|
||||
Flexbox,
|
||||
Dropdown,
|
||||
Button,
|
||||
TextField,
|
||||
Menu,
|
||||
Popup,
|
||||
PopupTrigger,
|
||||
} from 'react-basics';
|
||||
import { useMessages, useFilters, useFormat, useLocale } from 'components/hooks';
|
||||
import { safeDecodeURIComponent } from 'next-basics';
|
||||
import { OPERATORS } from 'lib/constants';
|
||||
import styles from './FieldFilterForm.module.css';
|
||||
|
||||
export interface FieldFilterFormProps {
|
||||
@ -22,12 +35,11 @@ export default function FieldFilterForm({
|
||||
}: FieldFilterFormProps) {
|
||||
const { formatMessage, labels } = useMessages();
|
||||
const [filter, setFilter] = useState('eq');
|
||||
const [value, setValue] = useState();
|
||||
const [value, setValue] = useState('');
|
||||
const { getFilters } = useFilters();
|
||||
const { formatValue } = useFormat();
|
||||
const { locale } = useLocale();
|
||||
const filters = getFilters(type);
|
||||
const [search, setSearch] = useState('');
|
||||
|
||||
const formattedValues = useMemo(() => {
|
||||
const formatted = {};
|
||||
@ -45,21 +57,25 @@ export default function FieldFilterForm({
|
||||
}, [formatValue, locale, name, values]);
|
||||
|
||||
const filteredValues = useMemo(() => {
|
||||
return search ? values.filter(n => n.includes(search)) : values;
|
||||
}, [search, formattedValues]);
|
||||
return value ? values.filter(n => n.includes(value)) : values;
|
||||
}, [value, formattedValues]);
|
||||
|
||||
const renderFilterValue = value => {
|
||||
return filters.find(f => f.value === value)?.label;
|
||||
};
|
||||
|
||||
const renderValue = value => {
|
||||
return formattedValues[value];
|
||||
};
|
||||
|
||||
const handleAdd = () => {
|
||||
onSelect({ name, type, filter, value });
|
||||
};
|
||||
|
||||
const handleMenuSelect = value => {
|
||||
setValue(value);
|
||||
};
|
||||
|
||||
const showMenu =
|
||||
[OPERATORS.equals, OPERATORS.notEquals].includes(filter as any) &&
|
||||
!(filteredValues.length === 1 && filteredValues[0] === value);
|
||||
|
||||
return (
|
||||
<Form>
|
||||
<FormRow label={label} className={styles.filter}>
|
||||
@ -77,21 +93,24 @@ export default function FieldFilterForm({
|
||||
}}
|
||||
</Dropdown>
|
||||
)}
|
||||
<Dropdown
|
||||
className={styles.dropdown}
|
||||
popupProps={{ className: styles.popup }}
|
||||
menuProps={{ className: styles.menu }}
|
||||
items={filteredValues}
|
||||
value={value}
|
||||
renderValue={renderValue}
|
||||
onChange={(key: any) => setValue(key)}
|
||||
allowSearch={true}
|
||||
onSearch={setSearch}
|
||||
>
|
||||
{(value: string) => {
|
||||
return <Item key={value}>{formattedValues[value]}</Item>;
|
||||
}}
|
||||
</Dropdown>
|
||||
<PopupTrigger>
|
||||
<TextField
|
||||
className={styles.text}
|
||||
value={decodeURIComponent(value)}
|
||||
onChange={e => setValue(e.target.value)}
|
||||
/>
|
||||
{showMenu && (
|
||||
<Popup className={styles.popup} alignment="end">
|
||||
{filteredValues.length > 0 && (
|
||||
<Menu variant="popup" onSelect={handleMenuSelect}>
|
||||
{filteredValues.map(value => {
|
||||
return <Item key={value}>{safeDecodeURIComponent(value)}</Item>;
|
||||
})}
|
||||
</Menu>
|
||||
)}
|
||||
</Popup>
|
||||
)}
|
||||
</PopupTrigger>
|
||||
</Flexbox>
|
||||
<Button variant="primary" onClick={handleAdd} disabled={!filter || !value}>
|
||||
{formatMessage(labels.add)}
|
||||
|
@ -24,14 +24,14 @@ function useValues(websiteId: string, type: string) {
|
||||
|
||||
export interface FilterSelectFormProps {
|
||||
websiteId: string;
|
||||
items: any[];
|
||||
fields: any[];
|
||||
onSelect?: (key: any) => void;
|
||||
allowFilterSelect?: boolean;
|
||||
}
|
||||
|
||||
export default function FilterSelectForm({
|
||||
websiteId,
|
||||
items,
|
||||
fields,
|
||||
onSelect,
|
||||
allowFilterSelect,
|
||||
}: FilterSelectFormProps) {
|
||||
@ -39,7 +39,7 @@ export default function FilterSelectForm({
|
||||
const { data, isLoading } = useValues(websiteId, field?.name);
|
||||
|
||||
if (!field) {
|
||||
return <FieldSelectForm fields={items} onSelect={setField} showType={false} />;
|
||||
return <FieldSelectForm fields={fields} onSelect={setField} showType={false} />;
|
||||
}
|
||||
|
||||
if (isLoading) {
|
||||
|
@ -13,9 +13,4 @@
|
||||
border: 1px solid var(--base400);
|
||||
border-radius: var(--border-radius);
|
||||
box-shadow: 1px 1px 1px var(--base400);
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.icon {
|
||||
align-self: center;
|
||||
}
|
||||
|
@ -1,40 +1,36 @@
|
||||
import { ReactNode } from 'react';
|
||||
import { Icon, TooltipPopup } from 'react-basics';
|
||||
import { Icon } from 'react-basics';
|
||||
import Icons from 'components/icons';
|
||||
import Empty from 'components/common/Empty';
|
||||
import { useMessages } from 'components/hooks';
|
||||
import styles from './ParameterList.module.css';
|
||||
|
||||
export interface ParameterListProps {
|
||||
items: any[];
|
||||
children?: ReactNode | ((item: any) => ReactNode);
|
||||
onRemove: (index: number, e: any) => void;
|
||||
children?: ReactNode;
|
||||
}
|
||||
|
||||
export function ParameterList({ items = [], children, onRemove }: ParameterListProps) {
|
||||
export function ParameterList({ children }: ParameterListProps) {
|
||||
const { formatMessage, labels } = useMessages();
|
||||
|
||||
return (
|
||||
<div className={styles.list}>
|
||||
{!items.length && <Empty message={formatMessage(labels.none)} />}
|
||||
{items.map((item, index) => {
|
||||
return (
|
||||
<div key={index} className={styles.item}>
|
||||
{typeof children === 'function' ? children(item) : item}
|
||||
<TooltipPopup
|
||||
className={styles.icon}
|
||||
label={formatMessage(labels.remove)}
|
||||
position="right"
|
||||
>
|
||||
<Icon onClick={onRemove.bind(null, index)}>
|
||||
<Icons.Close />
|
||||
</Icon>
|
||||
</TooltipPopup>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
{!children && <Empty message={formatMessage(labels.none)} />}
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const Item = ({ children, onRemove }: { children?: ReactNode; onRemove?: () => void }) => {
|
||||
return (
|
||||
<div className={styles.item}>
|
||||
{children}
|
||||
<Icon onClick={onRemove}>
|
||||
<Icons.Close />
|
||||
</Icon>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
ParameterList.Item = Item;
|
||||
|
||||
export default ParameterList;
|
||||
|
@ -2,4 +2,5 @@
|
||||
display: grid;
|
||||
grid-template-rows: max-content 1fr;
|
||||
grid-template-columns: max-content 1fr;
|
||||
margin-bottom: 60px;
|
||||
}
|
||||
|
75
src/app/(main)/reports/insights/InsightsFieldParameters.tsx
Normal file
75
src/app/(main)/reports/insights/InsightsFieldParameters.tsx
Normal file
@ -0,0 +1,75 @@
|
||||
import { useMessages } from 'components/hooks';
|
||||
import Icons from 'components/icons';
|
||||
import { useContext } from 'react';
|
||||
import { Button, FormRow, Icon, Popup, PopupTrigger } from 'react-basics';
|
||||
import FieldSelectForm from '../[reportId]/FieldSelectForm';
|
||||
import ParameterList from '../[reportId]/ParameterList';
|
||||
import PopupForm from '../[reportId]/PopupForm';
|
||||
import { ReportContext } from '../[reportId]/Report';
|
||||
|
||||
export function InsightsFieldParameters() {
|
||||
const { report, updateReport } = useContext(ReportContext);
|
||||
const { formatMessage, labels } = useMessages();
|
||||
const { parameters } = report || {};
|
||||
const { fields } = parameters || {};
|
||||
|
||||
const fieldOptions = [
|
||||
{ name: 'url', type: 'string', label: formatMessage(labels.url) },
|
||||
{ name: 'title', type: 'string', label: formatMessage(labels.pageTitle) },
|
||||
{ name: 'referrer', type: 'string', label: formatMessage(labels.referrer) },
|
||||
{ name: 'query', type: 'string', label: formatMessage(labels.query) },
|
||||
{ name: 'browser', type: 'string', label: formatMessage(labels.browser) },
|
||||
{ name: 'os', type: 'string', label: formatMessage(labels.os) },
|
||||
{ name: 'device', type: 'string', label: formatMessage(labels.device) },
|
||||
{ name: 'country', type: 'string', label: formatMessage(labels.country) },
|
||||
{ name: 'region', type: 'string', label: formatMessage(labels.region) },
|
||||
{ name: 'city', type: 'string', label: formatMessage(labels.city) },
|
||||
];
|
||||
|
||||
const handleAdd = (value: { name: any }) => {
|
||||
if (!fields.find(({ name }) => name === value.name)) {
|
||||
updateReport({ parameters: { fields: fields.concat(value) } });
|
||||
}
|
||||
};
|
||||
|
||||
const handleRemove = (name: string) => {
|
||||
updateReport({ parameters: { fields: fields.filter(f => f.name !== name) } });
|
||||
};
|
||||
|
||||
const AddButton = () => {
|
||||
return (
|
||||
<PopupTrigger>
|
||||
<Button size="sm">
|
||||
<Icon>
|
||||
<Icons.Plus />
|
||||
</Icon>
|
||||
</Button>
|
||||
<Popup position="bottom" alignment="start">
|
||||
<PopupForm>
|
||||
<FieldSelectForm
|
||||
fields={fieldOptions.filter(({ name }) => !fields.find(f => f.name === name))}
|
||||
onSelect={handleAdd}
|
||||
showType={false}
|
||||
/>
|
||||
</PopupForm>
|
||||
</Popup>
|
||||
</PopupTrigger>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<FormRow label={formatMessage(labels.fields)} action={<AddButton />}>
|
||||
<ParameterList>
|
||||
{fields.map(({ name }) => {
|
||||
return (
|
||||
<ParameterList.Item key={name} onRemove={() => handleRemove(name)}>
|
||||
{fieldOptions.find(f => f.name === name)?.label}
|
||||
</ParameterList.Item>
|
||||
);
|
||||
})}
|
||||
</ParameterList>
|
||||
</FormRow>
|
||||
);
|
||||
}
|
||||
|
||||
export default InsightsFieldParameters;
|
@ -0,0 +1,36 @@
|
||||
.item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.label {
|
||||
color: var(--base800);
|
||||
border: 1px solid var(--base300);
|
||||
font-weight: 900;
|
||||
padding: 2px 8px;
|
||||
border-radius: 5px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.filter {
|
||||
color: var(--blue900);
|
||||
background-color: var(--blue100);
|
||||
font-size: 12px;
|
||||
font-weight: 900;
|
||||
padding: 2px 8px;
|
||||
border-radius: 5px;
|
||||
text-transform: uppercase;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.value {
|
||||
color: var(--base900);
|
||||
background-color: var(--base100);
|
||||
font-weight: 900;
|
||||
padding: 2px 8px;
|
||||
border-radius: 5px;
|
||||
white-space: nowrap;
|
||||
}
|
88
src/app/(main)/reports/insights/InsightsFilterParameters.tsx
Normal file
88
src/app/(main)/reports/insights/InsightsFilterParameters.tsx
Normal file
@ -0,0 +1,88 @@
|
||||
import { useMessages, useFormat, useFilters } from 'components/hooks';
|
||||
import Icons from 'components/icons';
|
||||
import { useContext } from 'react';
|
||||
import { Button, FormRow, Icon, Popup, PopupTrigger } from 'react-basics';
|
||||
import FilterSelectForm from '../[reportId]/FilterSelectForm';
|
||||
import ParameterList from '../[reportId]/ParameterList';
|
||||
import PopupForm from '../[reportId]/PopupForm';
|
||||
import { ReportContext } from '../[reportId]/Report';
|
||||
import styles from './InsightsFilterParameters.module.css';
|
||||
import { safeDecodeURIComponent } from 'next-basics';
|
||||
import { OPERATORS } from 'lib/constants';
|
||||
|
||||
export function InsightsFilterParameters() {
|
||||
const { report, updateReport } = useContext(ReportContext);
|
||||
const { formatMessage, labels } = useMessages();
|
||||
const { formatValue } = useFormat();
|
||||
const { filterLabels } = useFilters();
|
||||
const { parameters } = report || {};
|
||||
const { websiteId, filters } = parameters || {};
|
||||
|
||||
const fieldOptions = [
|
||||
{ name: 'url', type: 'string', label: formatMessage(labels.url) },
|
||||
{ name: 'title', type: 'string', label: formatMessage(labels.pageTitle) },
|
||||
{ name: 'referrer', type: 'string', label: formatMessage(labels.referrer) },
|
||||
{ name: 'query', type: 'string', label: formatMessage(labels.query) },
|
||||
{ name: 'browser', type: 'string', label: formatMessage(labels.browser) },
|
||||
{ name: 'os', type: 'string', label: formatMessage(labels.os) },
|
||||
{ name: 'device', type: 'string', label: formatMessage(labels.device) },
|
||||
{ name: 'country', type: 'string', label: formatMessage(labels.country) },
|
||||
{ name: 'region', type: 'string', label: formatMessage(labels.region) },
|
||||
{ name: 'city', type: 'string', label: formatMessage(labels.city) },
|
||||
];
|
||||
|
||||
const handleAdd = (value: { name: any }) => {
|
||||
if (!filters.find(({ name }) => name === value.name)) {
|
||||
updateReport({ parameters: { filters: filters.concat(value) } });
|
||||
}
|
||||
};
|
||||
|
||||
const handleRemove = (name: string) => {
|
||||
updateReport({ parameters: { filters: filters.filter(f => f.name !== name) } });
|
||||
};
|
||||
|
||||
const AddButton = () => {
|
||||
return (
|
||||
<PopupTrigger>
|
||||
<Button size="sm">
|
||||
<Icon>
|
||||
<Icons.Plus />
|
||||
</Icon>
|
||||
</Button>
|
||||
<Popup position="bottom" alignment="start">
|
||||
<PopupForm>
|
||||
<FilterSelectForm
|
||||
websiteId={websiteId}
|
||||
fields={fieldOptions.filter(({ name }) => !filters.find(f => f.name === name))}
|
||||
onSelect={handleAdd}
|
||||
/>
|
||||
</PopupForm>
|
||||
</Popup>
|
||||
</PopupTrigger>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<FormRow label={formatMessage(labels.filters)} action={<AddButton />}>
|
||||
<ParameterList>
|
||||
{filters.map(({ name, filter, value }) => {
|
||||
const label = fieldOptions.find(f => f.name === name)?.label;
|
||||
const isEquals = [OPERATORS.equals, OPERATORS.notEquals].includes(filter);
|
||||
return (
|
||||
<ParameterList.Item key={name} onRemove={() => handleRemove(name)}>
|
||||
<div className={styles.item}>
|
||||
<div className={styles.label}>{label}</div>
|
||||
<div className={styles.filter}>{filterLabels[filter]}</div>
|
||||
<div className={styles.value}>
|
||||
{safeDecodeURIComponent(isEquals ? formatValue(value, name) : value)}
|
||||
</div>
|
||||
</div>
|
||||
</ParameterList.Item>
|
||||
);
|
||||
})}
|
||||
</ParameterList>
|
||||
</FormRow>
|
||||
);
|
||||
}
|
||||
|
||||
export default InsightsFilterParameters;
|
@ -1,17 +0,0 @@
|
||||
.parameter {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.op {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.popup {
|
||||
margin-top: -10px;
|
||||
margin-inline-start: 30px;
|
||||
}
|
@ -1,136 +1,29 @@
|
||||
import { useFilters, useFormat, useMessages } from 'components/hooks';
|
||||
import Icons from 'components/icons';
|
||||
import { useMessages } from 'components/hooks';
|
||||
import { useContext } from 'react';
|
||||
import {
|
||||
Form,
|
||||
FormButtons,
|
||||
FormRow,
|
||||
Icon,
|
||||
Popup,
|
||||
PopupTrigger,
|
||||
SubmitButton,
|
||||
TooltipPopup,
|
||||
} from 'react-basics';
|
||||
import { Form, FormButtons, SubmitButton } from 'react-basics';
|
||||
import BaseParameters from '../[reportId]/BaseParameters';
|
||||
import FieldSelectForm from '../[reportId]/FieldSelectForm';
|
||||
import FilterSelectForm from '../[reportId]/FilterSelectForm';
|
||||
import ParameterList from '../[reportId]/ParameterList';
|
||||
import PopupForm from '../[reportId]/PopupForm';
|
||||
import { ReportContext } from '../[reportId]/Report';
|
||||
import styles from './InsightsParameters.module.css';
|
||||
import InsightsFieldParameters from './InsightsFieldParameters';
|
||||
import InsightsFilterParameters from './InsightsFilterParameters';
|
||||
|
||||
export function InsightsParameters() {
|
||||
const { report, runReport, updateReport, isRunning } = useContext(ReportContext);
|
||||
const { report, runReport, isRunning } = useContext(ReportContext);
|
||||
const { formatMessage, labels } = useMessages();
|
||||
const { formatValue } = useFormat();
|
||||
const { filterLabels } = useFilters();
|
||||
const { id, parameters } = report || {};
|
||||
const { websiteId, dateRange, fields, filters } = parameters || {};
|
||||
const { startDate, endDate } = dateRange || {};
|
||||
const parametersSelected = websiteId && startDate && endDate;
|
||||
const queryEnabled = websiteId && dateRange && (fields?.length || filters?.length);
|
||||
|
||||
const fieldOptions = [
|
||||
{ name: 'url', type: 'string', label: formatMessage(labels.url) },
|
||||
{ name: 'title', type: 'string', label: formatMessage(labels.pageTitle) },
|
||||
{ name: 'referrer', type: 'string', label: formatMessage(labels.referrer) },
|
||||
{ name: 'query', type: 'string', label: formatMessage(labels.query) },
|
||||
{ name: 'browser', type: 'string', label: formatMessage(labels.browser) },
|
||||
{ name: 'os', type: 'string', label: formatMessage(labels.os) },
|
||||
{ name: 'device', type: 'string', label: formatMessage(labels.device) },
|
||||
{ name: 'country', type: 'string', label: formatMessage(labels.country) },
|
||||
{ name: 'region', type: 'string', label: formatMessage(labels.region) },
|
||||
{ name: 'city', type: 'string', label: formatMessage(labels.city) },
|
||||
];
|
||||
|
||||
const parameterGroups = [
|
||||
{ id: 'fields', label: formatMessage(labels.fields) },
|
||||
{ id: 'filters', label: formatMessage(labels.filters) },
|
||||
];
|
||||
|
||||
const parameterData = {
|
||||
fields,
|
||||
filters,
|
||||
};
|
||||
|
||||
const handleSubmit = (values: any) => {
|
||||
runReport(values);
|
||||
};
|
||||
|
||||
const handleAdd = (id: string | number, value: { name: any }) => {
|
||||
const data = parameterData[id];
|
||||
|
||||
if (!data.find(({ name }) => name === value.name)) {
|
||||
updateReport({ parameters: { [id]: data.concat(value) } });
|
||||
}
|
||||
};
|
||||
|
||||
const handleRemove = (id: string, index: number) => {
|
||||
const data = [...parameterData[id]];
|
||||
data.splice(index, 1);
|
||||
updateReport({ parameters: { [id]: data } });
|
||||
};
|
||||
|
||||
const AddButton = ({ id, onAdd }) => {
|
||||
return (
|
||||
<PopupTrigger>
|
||||
<TooltipPopup label={formatMessage(labels.add)} position="top">
|
||||
<Icon>
|
||||
<Icons.Plus />
|
||||
</Icon>
|
||||
</TooltipPopup>
|
||||
<Popup position="bottom" alignment="start" className={styles.popup}>
|
||||
<PopupForm>
|
||||
{id === 'fields' && (
|
||||
<FieldSelectForm
|
||||
fields={fieldOptions}
|
||||
onSelect={onAdd.bind(null, id)}
|
||||
showType={false}
|
||||
/>
|
||||
)}
|
||||
{id === 'filters' && (
|
||||
<FilterSelectForm
|
||||
websiteId={websiteId}
|
||||
items={fieldOptions}
|
||||
onSelect={onAdd.bind(null, id)}
|
||||
/>
|
||||
)}
|
||||
</PopupForm>
|
||||
</Popup>
|
||||
</PopupTrigger>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Form values={parameters} onSubmit={handleSubmit}>
|
||||
<BaseParameters allowWebsiteSelect={!id} />
|
||||
{parametersSelected &&
|
||||
parameterGroups.map(({ id, label }) => {
|
||||
return (
|
||||
<FormRow key={label} label={label} action={<AddButton id={id} onAdd={handleAdd} />}>
|
||||
<ParameterList items={parameterData[id]} onRemove={index => handleRemove(id, index)}>
|
||||
{({ name, filter, value }) => {
|
||||
return (
|
||||
<div className={styles.parameter}>
|
||||
{id === 'fields' && (
|
||||
<>
|
||||
<div>{fieldOptions.find(f => f.name === name)?.label}</div>
|
||||
</>
|
||||
)}
|
||||
{id === 'filters' && (
|
||||
<>
|
||||
<div>{fieldOptions.find(f => f.name === name)?.label}</div>
|
||||
<div className={styles.op}>{filterLabels[filter]}</div>
|
||||
<div>{formatValue(value, name)}</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
</ParameterList>
|
||||
</FormRow>
|
||||
);
|
||||
})}
|
||||
{parametersSelected && <InsightsFieldParameters />}
|
||||
{parametersSelected && <InsightsFilterParameters />}
|
||||
<FormButtons>
|
||||
<SubmitButton variant="primary" disabled={!queryEnabled} isLoading={isRunning}>
|
||||
{formatMessage(labels.runQuery)}
|
||||
|
@ -1,5 +1,5 @@
|
||||
.bar {
|
||||
font-size: 14px;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
color: var(--base600);
|
||||
|
@ -22,7 +22,7 @@ export function useFilters() {
|
||||
};
|
||||
|
||||
const typeFilters = {
|
||||
string: [OPERATORS.equals, OPERATORS.notEquals],
|
||||
string: [OPERATORS.equals, OPERATORS.notEquals, OPERATORS.contains, OPERATORS.doesNotContain],
|
||||
array: [OPERATORS.contains, OPERATORS.doesNotContain],
|
||||
boolean: [OPERATORS.true, OPERATORS.false],
|
||||
number: [
|
||||
|
@ -69,6 +69,8 @@ function mapFilter(column: string, filter: string, name: string, type: string =
|
||||
return `${column} != {${name}:${type}}`;
|
||||
case OPERATORS.contains:
|
||||
return `positionCaseInsensitive(${column}, {${name}:${type}}) > 0`;
|
||||
case OPERATORS.doesNotContain:
|
||||
return `positionCaseInsensitive(${column}, {${name}:${type}}) = 0`;
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
|
@ -100,6 +100,8 @@ function mapFilter(column: string, filter: string, name: string, type = 'varchar
|
||||
return `${column} != {{${name}::${type}}}`;
|
||||
case OPERATORS.contains:
|
||||
return `${column} like {{${name}::${type}}}`;
|
||||
case OPERATORS.doesNotContain:
|
||||
return `${column} not like {{${name}::${type}}}`;
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user