123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278 |
- import styled from '@emotion/styled';
- import RadioGroup from 'sentry/components/forms/controls/radioGroup';
- import SelectField from 'sentry/components/forms/fields/selectField';
- import Input from 'sentry/components/input';
- import {space} from 'sentry/styles/space';
- import {CustomMeasurementsProvider} from 'sentry/utils/customMeasurements/customMeasurementsProvider';
- import {type Column, generateFieldAsString} from 'sentry/utils/discover/fields';
- import {useLocalStorageState} from 'sentry/utils/useLocalStorageState';
- import useOrganization from 'sentry/utils/useOrganization';
- import usePageFilters from 'sentry/utils/usePageFilters';
- import {getDatasetConfig} from 'sentry/views/dashboards/datasetConfig/base';
- import {DisplayType, WidgetType} from 'sentry/views/dashboards/types';
- import {ColumnFields} from 'sentry/views/dashboards/widgetBuilder/buildSteps/columnsStep/columnFields';
- import {YAxisSelector} from 'sentry/views/dashboards/widgetBuilder/buildSteps/yAxisStep/yAxisSelector';
- import {useWidgetBuilderContext} from 'sentry/views/dashboards/widgetBuilder/contexts/widgetBuilderContext';
- import {BuilderStateAction} from 'sentry/views/dashboards/widgetBuilder/hooks/useWidgetBuilderState';
- import {getDiscoverDatasetFromWidgetType} from 'sentry/views/dashboards/widgetBuilder/utils';
- import ResultsSearchQueryBuilder from 'sentry/views/discover/resultsSearchQueryBuilder';
- function DevBuilder() {
- const {state, dispatch} = useWidgetBuilderContext();
- const [showDevBuilder] = useLocalStorageState('showDevBuilder', false);
- if (!showDevBuilder) {
- return null;
- }
- return (
- <Body>
- <Section>
- <h1>Title:</h1>
- <div style={{flex: 1}}>{state.title}</div>
- <SimpleInput
- value={state.title}
- onChange={e =>
- dispatch({type: BuilderStateAction.SET_TITLE, payload: e.target.value})
- }
- />
- </Section>
- <Section>
- <h1>Description:</h1>
- <div style={{flex: 1}}>{state.description}</div>
- <SimpleInput
- value={state.description}
- onChange={e =>
- dispatch({
- type: BuilderStateAction.SET_DESCRIPTION,
- payload: e.target.value,
- })
- }
- />
- </Section>
- <Section>
- <h1>Display Type:</h1>
- <div style={{flex: 1}}>{state.displayType}</div>
- <SelectField
- name="displayType"
- value={state.displayType}
- options={Object.values(DisplayType).map(value => ({
- label: value,
- value,
- }))}
- onChange={newValue =>
- dispatch({
- type: BuilderStateAction.SET_DISPLAY_TYPE,
- payload: newValue,
- })
- }
- style={{width: '200px'}}
- />
- </Section>
- <Section>
- <h1>Dataset:</h1>
- <div>{state.dataset}</div>
- <RadioGroup
- label="Dataset"
- value={state.dataset ?? null}
- choices={[
- [WidgetType.DISCOVER, 'Discover'],
- [WidgetType.ISSUE, 'Issue'],
- [WidgetType.RELEASE, 'Release'],
- [WidgetType.METRICS, 'Metrics'],
- [WidgetType.ERRORS, 'Errors'],
- [WidgetType.TRANSACTIONS, 'Transactions'],
- ]}
- onChange={newValue =>
- dispatch({
- type: BuilderStateAction.SET_DATASET,
- payload: newValue,
- })
- }
- />
- </Section>
- <Section>
- <h1>Fields:</h1>
- <div>{state.fields?.map(generateFieldAsString).join(', ')}</div>
- <ColumnSelector
- displayType={state.displayType ?? DisplayType.TABLE}
- dataset={state.dataset ?? WidgetType.DISCOVER}
- fields={state.fields ?? [{field: '', kind: 'field'}]}
- onChange={newFields => {
- dispatch({
- type: BuilderStateAction.SET_FIELDS,
- payload: newFields,
- });
- }}
- />
- </Section>
- <Section>
- <h1>Y-Axis:</h1>
- <div>{state.yAxis?.map(generateFieldAsString).join(', ')}</div>
- <YAxis
- displayType={state.displayType ?? DisplayType.TABLE}
- widgetType={state.dataset ?? WidgetType.DISCOVER}
- aggregates={state.yAxis ?? [{field: '', kind: 'field'}]}
- onChange={newAggregates => {
- dispatch({
- type: BuilderStateAction.SET_Y_AXIS,
- payload: newAggregates,
- });
- }}
- />
- </Section>
- <Section>
- <h1>Query:</h1>
- <ol>{state.query?.map((query, index) => <li key={index}>{query}</li>)}</ol>
- <div>
- {state.query?.map((query, index) => (
- <div key={index} style={{display: 'flex', flexDirection: 'row'}}>
- <QueryField
- query={query}
- widgetType={state.dataset ?? WidgetType.DISCOVER}
- fields={state.fields ?? []}
- key={index}
- onSearch={queryString => {
- dispatch({
- type: BuilderStateAction.SET_QUERY,
- payload:
- state.query?.map((q, i) => (i === index ? queryString : q)) ?? [],
- });
- }}
- />
- <button
- onClick={() =>
- dispatch({
- type: BuilderStateAction.SET_QUERY,
- payload: state.query?.filter((_, i) => i !== index) ?? [],
- })
- }
- >
- Remove
- </button>
- </div>
- ))}
- <button
- onClick={() =>
- dispatch({
- type: BuilderStateAction.SET_QUERY,
- payload: [...(state.query ?? []), ''],
- })
- }
- >
- Add
- </button>
- </div>
- </Section>
- </Body>
- );
- }
- function ColumnSelector({
- displayType,
- fields,
- dataset,
- onChange,
- }: {
- dataset: WidgetType;
- displayType: DisplayType;
- fields: Column[];
- onChange: (newFields: Column[]) => void;
- }) {
- const organization = useOrganization();
- const datasetConfig = getDatasetConfig(dataset);
- const fieldOptions = datasetConfig.getTableFieldOptions(organization);
- return (
- <ColumnFields
- displayType={displayType ?? DisplayType.TABLE}
- organization={organization}
- widgetType={dataset ?? WidgetType.DISCOVER}
- fields={fields ?? []}
- errors={[]}
- fieldOptions={fieldOptions}
- isOnDemandWidget={false}
- filterAggregateParameters={() => true}
- filterPrimaryOptions={() => true}
- onChange={onChange}
- />
- );
- }
- function YAxis({
- displayType,
- widgetType,
- aggregates,
- onChange,
- }: {
- aggregates: Column[];
- displayType: DisplayType;
- onChange: (newFields: Column[]) => void;
- widgetType: WidgetType;
- }) {
- const organization = useOrganization();
- return (
- <CustomMeasurementsProvider organization={organization}>
- <YAxisSelector
- widgetType={widgetType}
- displayType={displayType}
- aggregates={aggregates}
- onChange={onChange}
- tags={{}}
- errors={[]}
- selectedAggregate={undefined}
- />
- </CustomMeasurementsProvider>
- );
- }
- function QueryField({
- query,
- widgetType,
- fields,
- onSearch,
- }: {
- fields: Column[];
- onSearch: (query: string) => void;
- query: string;
- widgetType: WidgetType;
- }) {
- const pageFilters = usePageFilters();
- return (
- <ResultsSearchQueryBuilder
- projectIds={pageFilters.selection.projects}
- query={query}
- fields={fields as any}
- onSearch={onSearch}
- dataset={getDiscoverDatasetFromWidgetType(widgetType)}
- includeTransactions
- searchSource="widget_builder"
- />
- );
- }
- const Body = styled('div')`
- margin: ${space(2)};
- padding: ${space(2)};
- `;
- const Section = styled('section')`
- display: flex;
- flex-direction: row;
- justify-content: space-around;
- border: 1px solid ${p => p.theme.border};
- align-items: center;
- > * {
- flex: 1;
- }
- `;
- const SimpleInput = styled(Input)`
- width: 100%;
- `;
- export default DevBuilder;
|