/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ import React, { useState, useEffect, useRef, useCallback } from 'react'; import { Row, Col, FormControl, FormGroup, FormControlProps, } from 'react-bootstrap'; import Modal from 'src/common/components/Modal'; import Button from 'src/components/Button'; import Dialog from 'react-bootstrap-dialog'; import { OptionsType } from 'react-select/src/types'; import { AsyncSelect } from 'src/components/Select'; import rison from 'rison'; import { t, SupersetClient } from '@superset-ui/core'; import Chart, { Slice } from 'src/types/Chart'; import FormLabel from 'src/components/FormLabel'; import getClientErrorObject from '../../utils/getClientErrorObject'; type PropertiesModalProps = { slice: Slice; onHide: () => void; onSave: (chart: Chart) => void; show: boolean; }; type OwnerOption = { label: string; value: number; }; export default function PropertiesModal({ slice, onHide, onSave, show, }: PropertiesModalProps) { const [submitting, setSubmitting] = useState(false); const errorDialog = useRef(null); // values of form inputs const [name, setName] = useState(slice.slice_name || ''); const [description, setDescription] = useState(slice.description || ''); const [cacheTimeout, setCacheTimeout] = useState( slice.cache_timeout != null ? slice.cache_timeout : '', ); const [owners, setOwners] = useState | null>(null); function showError({ error, statusText, message }: any) { let errorText = error || statusText || t('An error has occurred'); if (message === 'Forbidden') { errorText = t('You do not have permission to edit this chart'); } errorDialog.current.show({ title: 'Error', bsSize: 'medium', bsStyle: 'danger', actions: [Dialog.DefaultAction('Ok', () => {}, 'btn-danger')], body: errorText, }); } const fetchChartData = useCallback( async function fetchChartData() { try { const response = await SupersetClient.get({ endpoint: `/api/v1/chart/${slice.slice_id}`, }); const chart = response.json.result; setOwners( chart.owners.map((owner: any) => ({ value: owner.id, label: `${owner.first_name} ${owner.last_name}`, })), ); } catch (response) { const clientError = await getClientErrorObject(response); showError(clientError); } }, [slice.slice_id], ); // get the owners of this slice useEffect(() => { fetchChartData(); }, [fetchChartData]); const loadOptions = (input = '') => { const query = rison.encode({ filter: input, }); return SupersetClient.get({ endpoint: `/api/v1/chart/related/owners?q=${query}`, }).then( response => { const { result } = response.json; return result.map((item: any) => ({ value: item.value, label: item.text, })); }, badResponse => { getClientErrorObject(badResponse).then(showError); return []; }, ); }; const onSubmit = async (event: React.FormEvent) => { event.stopPropagation(); event.preventDefault(); setSubmitting(true); const payload: { [key: string]: any } = { slice_name: name || null, description: description || null, cache_timeout: cacheTimeout || null, }; if (owners) { payload.owners = owners.map(o => o.value); } try { const res = await SupersetClient.put({ endpoint: `/api/v1/chart/${slice.slice_id}`, headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload), }); // update the redux state const updatedChart = { ...res.json.result, id: slice.slice_id, }; onSave(updatedChart); onHide(); } catch (res) { const clientError = await getClientErrorObject(res); showError(clientError); } setSubmitting(false); }; return ( } responsive wrapProps={{ 'data-test': 'properties-edit-modal' }} >

{t('Basic Information')}

{t('Name')} , ) => setName((event.currentTarget?.value as string) ?? '')} /> {t('Description')} , ) => setDescription((event.currentTarget?.value as string) ?? '') } style={{ maxWidth: '100%' }} />

{t( 'The description can be displayed as widget headers in the dashboard view. Supports markdown.', )}

{t('Configuration')}

{t('Cache Timeout')} , ) => { const targetValue = (event.currentTarget?.value as string) ?? ''; setCacheTimeout(targetValue.replace(/[^0-9]/, '')); }} />

{t( "Duration (in seconds) of the caching timeout for this chart. Note this defaults to the dataset's timeout if undefined.", )}

{t('Access')}

{t('Owners')}

{t( 'A list of users who can alter the chart. Searchable by name or username.', )}

); }