@ant-design/icons#SaveOutlined TypeScript Examples

The following examples show how to use @ant-design/icons#SaveOutlined. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: Cohort.tsx    From posthog-foss with MIT License 6 votes vote down vote up
export function CohortFooter(props: { cohort: CohortType }): JSX.Element {
    const logic = cohortLogic(props)
    const { cohort } = useValues(logic)
    const { saveCohort } = useActions(logic)

    return (
        <Row style={{ display: 'flex' }}>
            <div style={{ flexGrow: 1, textAlign: 'right' }}>
                <Button
                    disabled={!cohort.name}
                    type="primary"
                    htmlType="submit"
                    data-attr="save-cohort"
                    onClick={() => saveCohort()}
                    style={{ float: 'right' }}
                    icon={<SaveOutlined />}
                >
                    Save cohort
                </Button>
            </div>
        </Row>
    )
}
Example #2
Source File: config-toolbar.ts    From XFlow with MIT License 6 votes vote down vote up
registerIcon = () => {
  IconStore.set('SaveOutlined', SaveOutlined)
  IconStore.set('UndoOutlined', UndoOutlined)
  IconStore.set('RedoOutlined', RedoOutlined)
  IconStore.set('VerticalAlignTopOutlined', VerticalAlignTopOutlined)
  IconStore.set('VerticalAlignBottomOutlined', VerticalAlignBottomOutlined)
  IconStore.set('GatewayOutlined', GatewayOutlined)
  IconStore.set('GroupOutlined', GroupOutlined)
  IconStore.set('UngroupOutlined', UngroupOutlined)
  IconStore.set('CopyOutlined', CopyOutlined)
  IconStore.set('SnippetsOutlined', SnippetsOutlined)
}
Example #3
Source File: Children.tsx    From ui-visualization with MIT License 5 votes vote down vote up
Children = (props: Props) => {

    const childLayout = [
        { i: 'd', x: 6, y: 0, w: 3, h: 2, minW: 2, maxW: 4 },
        { i: 'e', x: 8, y: 0, w: 1, h: 2 }
    ]
    const [edit, setEdit] = useState<boolean>(false);

    const [layout, setLayout] = useState<any[]>(childLayout);

    return (
        <Fragment>

            <GridLayout
                className="layout"
                layout={layout}
                cols={12}
                rowHeight={30}
                isDraggable={edit}
                isResizable={edit}
                width={1200}>
                {
                    layout.map(i => <div key={i.i} style={{ backgroundColor: '#69E2D4' }}>{i.i}</div>)
                }
            </GridLayout>
            <div className={styles.layoutOption}>
                {edit ?
                    <div style={{ float: 'right' }}>

                        <Tooltip title="新增" style={{ float: 'right' }}>
                            <Button
                                type="danger"
                                shape="circle"
                                size="large"
                                onClick={() => {
                                    layout.push({ i: Math.random(), x: 8, y: 7, w: 4, h: 4 })
                                    setLayout([...layout]);
                                }}
                            >
                                <PlusOutlined />
                            </Button>
                        </Tooltip>
                        <div style={{ float: 'right', marginLeft: 10 }}>
                            <Tooltip title="保存" >
                                <Button
                                    type="primary"
                                    shape="circle"
                                    size="large"
                                    onClick={() => {
                                        setEdit(false)
                                    }}
                                >
                                    <SaveOutlined />
                                </Button>
                            </Tooltip>
                        </div>
                    </div> :
                    <div style={{ float: 'right', textAlign: 'center' }}>
                        <Tooltip title="编辑" >
                            <Button
                                type="danger"
                                shape="circle"
                                size="large"
                                onClick={() => {
                                    setEdit(true)
                                    props.edit()
                                }}
                            >
                                <EditOutlined />
                            </Button>
                        </Tooltip>
                    </div>
                }
            </div>
        </Fragment >
    )
}
Example #4
Source File: index.tsx    From jetlinks-ui-antd with MIT License 5 votes vote down vote up
OptionGroup: React.FC<Props> = props => {
    const { edit } = props;

    return (
        <div className={styles.optionGroup}>
            {edit ?
                <div style={{ float: 'right' }}>
                    <Tooltip title="新增" style={{ float: 'right' }}>
                        <Button
                            type="danger"
                            shape="circle"
                            size="large"
                            onClick={() => {

                            }}
                        >
                            <PlusOutlined />
                        </Button>
                    </Tooltip>
                    <div style={{ float: 'right', marginLeft: 10 }}>
                        <Tooltip title="保存">
                            <Button
                                type="primary"
                                shape="circle"
                                size="large"
                                onClick={() => {

                                }}
                            >

                                <SaveOutlined />
                            </Button>
                        </Tooltip>
                    </div>
                </div> :
                <div style={{ textAlign: 'center' }}>
                    <Tooltip title="编辑">
                        <Button
                            type="danger"
                            shape="circle"
                            size="large"
                            onClick={() => {

                            }}
                        >
                            <EditOutlined />
                        </Button>
                    </Tooltip>
                </div>
            }
        </div>
    )
}
Example #5
Source File: EditorHeader.tsx    From datart with Apache License 2.0 5 votes vote down vote up
EditorHeader: FC = memo(({ children }) => {
  const dispatch = useDispatch();
  const history = useHistory();
  const t = useI18NPrefix(`viz.action`);
  const { updateBoard } = useContext(BoardActionContext);
  const { onEditClearActiveWidgets } = useContext(WidgetActionContext);
  const { name, status } = useContext(BoardContext);
  const { saving } = useContext(BoardInfoContext);
  const title = useStatusTitle(name, status);
  const onCloseBoardEditor = () => {
    const pathName = history.location.pathname;
    const prePath = pathName.split('/boardEditor')[0];
    history.push(`${prePath}`);
    dispatch(clearEditBoardState());
  };
  const onUpdateBoard = () => {
    onEditClearActiveWidgets();
    setImmediate(() => {
      updateBoard?.(onCloseBoardEditor);
    });
  };

  return (
    <Wrapper onClick={onEditClearActiveWidgets}>
      <h1 className={classnames({ disabled: status < 2 })}>
        <LeftOutlined onClick={onCloseBoardEditor} />
        {title}
      </h1>
      <Space>
        {children}
        <>
          <Button
            key="cancel"
            icon={<CloseOutlined />}
            onClick={onCloseBoardEditor}
          >
            {t('common.cancel')}
          </Button>

          <Button
            key="update"
            type="primary"
            loading={saving}
            icon={<SaveOutlined />}
            onClick={onUpdateBoard}
          >
            {t('common.save')}
          </Button>
        </>
      </Space>
    </Wrapper>
  );
})
Example #6
Source File: NewDashboard.tsx    From posthog-foss with MIT License 5 votes vote down vote up
export function NewDashboard(): JSX.Element {
    const [form] = Form.useForm()
    const { addDashboard } = useActions(dashboardsModel)

    return (
        <Form
            layout="vertical"
            form={form}
            onFinish={(values) => {
                addDashboard(values)
            }}
        >
            <Form.Item
                name="name"
                label="Dashboard name"
                rules={[{ required: true, message: 'Please give your dashboard a name.' }]}
            >
                <Input
                    autoFocus={true}
                    onChange={(e) => form.setFieldsValue({ key: slugify(e.target.value) })}
                    data-attr="dashboard-name-input"
                    className="ph-ignore-input"
                />
            </Form.Item>

            <Form.Item name="useTemplate" label="Start from">
                <Select data-attr="copy-from-template" style={{ width: '100%' }}>
                    <Select.Option data-attr="dashboard-select-empty" value="">
                        Empty Dashboard
                    </Select.Option>
                    <Select.Option data-attr="dashboard-select-default-app" value="DEFAULT_APP">
                        Default Dashboard - Web App
                    </Select.Option>
                </Select>
            </Form.Item>

            <br />

            <Form.Item>
                <Button icon={<SaveOutlined />} htmlType="submit" type="primary" data-attr="dashboard-submit">
                    Create
                </Button>
            </Form.Item>
        </Form>
    )
}
Example #7
Source File: save.tsx    From imove with MIT License 5 votes vote down vote up
Save: React.FC<IProps> = makeBtnWidget({
  tooltip: '保存',
  handler: shortcuts.save.handler,
  getIcon() {
    return <SaveOutlined />;
  },
})
Example #8
Source File: FormTableBlocks.tsx    From condo with MIT License 5 votes vote down vote up
function RenderActionsColumn (text, item, index) {
    const intl = useIntl()
    const AreYouSureMessage = intl.formatMessage({ id: 'AreYouSure' })
    const DeleteMessage = intl.formatMessage({ id: 'Delete' })
    const EditMessage = intl.formatMessage({ id: 'Edit' })

    const { user } = useAuth()

    const { isUnsavedNew } = item
    const { action, remove, form, setEditing, setLoading, editing, loading } = _useTableRowForm()

    function validateFields () {
        setLoading(true)
        return form.validateFields()
            .then((values) => action('CreateOrUpdate', { values, item, form }))
            .then(() => (isUnsavedNew) ? remove({ id: item.id }) : null)
            .then(() => (isUnsavedNew) ? null : setEditing(false))
            .finally(() => setLoading(false))
    }

    function deleteRow () {
        setLoading(false)
        setEditing(false)
        remove({ id: item.id })
    }

    return <Space>
        {(isUnsavedNew || editing) ?
            <Button size="small" type={'primary'} onClick={validateFields} loading={loading}>
                <SaveOutlined/>
            </Button>
            : null}
        {(isUnsavedNew) ?
            <Button size="small" type={'primary'} onClick={deleteRow}>
                <DeleteOutlined/>
            </Button>
            :
            <ExtraDropdownActionsMenu actions={[
                (item.user && item.user.id === user.id) ? null : {
                    confirm: {
                        title: AreYouSureMessage,
                        icon: <QuestionCircleOutlined style={{ color: 'red' }}/>,
                    },
                    label: DeleteMessage,
                    action: () => action('Delete', { values: { id: item.id }, item, form }),
                },
                {
                    label: EditMessage,
                    action: () => {
                        setEditing(true)
                    },
                },
            ]}/>
        }
    </Space>
}
Example #9
Source File: MaterialEdit.tsx    From mayoor with MIT License 4 votes vote down vote up
MaterialEdit: React.FC = () => {
	const { t } = useTranslation();
	const [currentlyLoading, setCurrentlyLoading] = useState<string | null>(null);

	const { data } = useQuery<GetAllMaterials>(GET_ALL_MATERIALS);

	const [updateMaterial] = useMutation<UpdateMaterial, UpdateMaterialVariables>(UPDATE_MATERIAL, {
		onCompleted: () => {
			message.success(t('Material updated'));
		},
	});
	const [deleteMaterial] = useMutation<DeleteMaterial, DeleteMaterialVariables>(DELETE_MATERIAL, {
		onCompleted: () => {
			message.success(t('Material deleted'));
		},
		update: (cache, { data }) => {
			const cached = cache.readQuery<GetAllMaterials>({ query: GET_ALL_MATERIALS });
			if (cached === null) {
				return;
			}
			const { getAllMaterials } = cached;
			cache.writeQuery<GetAllMaterials>({
				query: GET_ALL_MATERIALS,
				data: {
					getAllMaterials: getAllMaterials.filter(
						({ id }) => id !== data?.deleteMaterial.id,
					),
				},
			});
		},
	});

	return (
		<>
			<PageTitle>{t('Material')}</PageTitle>
			<StyledForm>
				<MaterialEditWrapper>
					<>
						<Row gutter={24}>
							<Col sm={14}>
								<StyledLabel>{t('Material name')}</StyledLabel>
							</Col>
							<Col sm={6}>
								<StyledLabel>{t('Price')}</StyledLabel>
							</Col>
							<Col sm={4}></Col>
						</Row>
						{data?.getAllMaterials.map((material) => (
							<Formik<GetAllMaterials_getAllMaterials>
								key={material.id}
								initialValues={material}
								onSubmit={async (values) => {
									setCurrentlyLoading(material.id);
									await updateMaterial({
										variables: {
											id: material.id,
											name: values.name,
											price: Number(values.price),
										},
									});
									setCurrentlyLoading(null);
								}}
								validate={getFormikValidate(t)}
							>
								{({ handleSubmit }) => (
									<Row gutter={18}>
										<Col sm={14}>
											<FormInput
												label={t('Material name')}
												name="name"
											></FormInput>
										</Col>
										<Col sm={5}>
											<FormInput label={t('Price')} name="price"></FormInput>
										</Col>
										<Col sm={3}>
											<Button
												onClick={() => handleSubmit()}
												loading={currentlyLoading === material.id}
												icon={<SaveOutlined />}
												style={{ width: '100%' }}
											>
												{t('Save')}
											</Button>
										</Col>
										<Col sm={1} style={{ textAlign: 'right' }}>
											<Popconfirm
												placement="topRight"
												onConfirm={async () =>
													await deleteMaterial({
														variables: { id: material.id },
													})
												}
												title={t(
													'Do you really want to remove this material?',
												)}
											>
												<Button
													shape="circle"
													icon={<DeleteOutlined />}
												></Button>
											</Popconfirm>
										</Col>
									</Row>
								)}
							</Formik>
						))}
						<MaterialCreate />
					</>
				</MaterialEditWrapper>
			</StyledForm>
		</>
	);
}
Example #10
Source File: index.tsx    From ui-visualization with MIT License 4 votes vote down vote up
Visualization = () => {
    const data = [
        { i: 'a', x: 0, y: 0, w: 10, h: 8, },
        { i: 'b', x: 1, y: 0, w: 3, h: 2, minW: 2, maxW: 4 },
        { i: 'c', x: 4, y: 0, w: 1, h: 2 },
    ];

    const [layout, setLayout] = useState(data);
    const [flag, setFlag] = useState(false);
    const [edit, setEdit] = useState<boolean>(false);

    const changeStatus = (key: string) => {
        setFlag(!flag);
    }
    return (
        <>
            <GridLayout
                className="layout"
                isDraggable={edit}
                isResizable={edit}
                layout={layout}
                cols={12} rowHeight={30} width={1200}>
                <div key="a" style={{ backgroundColor: '#1879FF' }}

                >
                    <PlusCircleOutlined style={{ margin: '5px' }} />
                    <EditOutlined onClick={() => {
                        changeStatus("a")
                    }} />
                    <Children edit={() => changeStatus("a")} />
                </div>
                <div key="b" style={{ backgroundColor: '#1879FF' }}>b</div>
                <div key="c" style={{ backgroundColor: '#1879FF' }}>c</div>
            </GridLayout>
            <div className={styles.optionGroup}>
                {edit ?
                    <div style={{ float: 'right' }}>

                        <Tooltip title="新增" style={{ float: 'right' }}>
                            <Button
                                type="danger"
                                shape="circle"
                                size="large"
                                onClick={() => {

                                }}
                            >
                                <PlusOutlined />
                            </Button>
                        </Tooltip>
                        <div style={{ float: 'right', marginLeft: 10 }}>
                            <Tooltip title="保存" >
                                <Button
                                    type="primary"
                                    shape="circle"
                                    size="large"
                                    onClick={() => {
                                        setEdit(false)
                                    }}
                                >
                                    <SaveOutlined />
                                </Button>
                            </Tooltip>
                        </div>
                    </div> :
                    <div style={{ textAlign: 'center' }}>
                        <Tooltip title="编辑" >
                            <Button
                                type="danger"
                                shape="circle"
                                size="large"
                                onClick={() => setEdit(true)}
                            >
                                <EditOutlined />
                            </Button>
                        </Tooltip>
                    </div>
                }
            </div>
        </>
    )
}
Example #11
Source File: EditInfo.tsx    From foodie with MIT License 4 votes vote down vote up
EditInfo: React.FC<IProps> = ({ isOwnProfile, profile }) => {
    const [isUpdating, setIsUpdating] = useState(false);
    const [field, setField] = useState({
        firstname: profile?.firstname || '',
        lastname: profile?.lastname || '',
        gender: profile?.info?.gender || '',
        bio: profile?.info?.bio || '',
        birthday: profile?.info?.birthday || ''
    });
    const [bioLength, setBioLength] = useState(200 - profile?.info?.bio?.length || 0);
    const history = useHistory();
    const dispatch = useDispatch();
    const didMount = useDidMount();

    useDocumentTitle(`Edit Info - ${profile.username} | Foodie`);
    useEffect(() => {
        setField({
            firstname: profile.firstname,
            lastname: profile.lastname,
            gender: profile.info.gender,
            bio: profile.info?.bio || '',
            birthday: profile.info.birthday
        });

    }, [profile]);

    const handleUpdateUser = async () => {
        try {
            setIsUpdating(true);
            const user = await updateUser(profile.username, field);

            dispatch(updateProfileInfo(user));

            if (didMount) {
                setIsUpdating(false);

                history.push(`/user/${profile.username}/info`);
                toast.dark('Profile updated successfully.')
            }
        } catch (e) {
            if (didMount) {
                setIsUpdating(false);
            }

            toast.dismiss();
            toast.error(e.error?.message || 'Unable to process your request.');
        }
    };

    const handleFirstnameChange = (e: React.ChangeEvent<HTMLInputElement>) => {
        setField({ ...field, firstname: e.target.value });
    }

    const handleLastnameChange = (e: React.ChangeEvent<HTMLInputElement>) => {
        setField({ ...field, lastname: e.target.value });
    }

    const handleGenderChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
        setField({ ...field, gender: e.target.value });
    }

    const handleBirthdayChange = (e: React.ChangeEvent<HTMLInputElement>) => {
        setField({ ...field, birthday: e.target.value });
    }

    const handleBioChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
        const val = e.target.value;

        setField({ ...field, bio: val });
        setBioLength(200 - val.length);
    }

    const handleTextOnlyInput = (e: React.KeyboardEvent<HTMLInputElement>) => {
        const key = e.nativeEvent.key;
        const regex = /[a-zA-Z\s]/ig;
        if (!regex.test(key)) {
            e.preventDefault();
        }
    }

    const handleBack = () => {
        history.push(`/user/${profile.username}/info`)
    };

    const handleSubmit = (e: FormEvent) => {
        e.preventDefault();
        handleUpdateUser();
    }

    return (!isOwnProfile && profile.username) ? <Redirect to={`/user/${profile.username}`} /> : (
        <div className="p-4 pb-8 bg-white dark:bg-indigo-1000 rounded-md min-h-10rem shadow-lg">
            <h3 className="text-gray-500 dark:text-white">Edit Info</h3>
            <form className="mt-8 space-y-4 divide-y divide-gray-100 dark:divide-gray-800" onSubmit={handleSubmit}>
                {/* ---- FIRST NAME ------- */}
                <div className="flex flex-col py-2">
                    <label htmlFor="firstname" className="ml-4 text-gray-400 mb-2">First Name</label>
                    <input
                        className="dark:bg-indigo-1100 dark:text-white dark:border-gray-800"
                        readOnly={isUpdating}
                        id="firstname"
                        type="text"
                        maxLength={50}
                        onChange={handleFirstnameChange}
                        onKeyDown={handleTextOnlyInput}
                        value={field.firstname}
                    />
                </div>
                {/* ---- LAST NAME ------- */}
                <div className="flex flex-col py-2">
                    <label htmlFor="lastname" className="ml-4 text-gray-400 mb-2">Last Name</label>
                    <input
                        className="dark:bg-indigo-1100 dark:text-white dark:border-gray-800"
                        readOnly={isUpdating}
                        id="lastname"
                        type="text"
                        maxLength={50}
                        onChange={handleLastnameChange}
                        onKeyDown={handleTextOnlyInput}
                        value={field.lastname}
                    />
                </div>
                {/* ---- GENDER && BIRTHDAY ------- */}
                <div className="grid laptop:grid-cols-2">
                    <div className="flex flex-col py-2">
                        <label htmlFor="gender" className="ml-4 text-gray-400 mb-2">Gender</label>
                        <select
                            className="dark:bg-indigo-1100 dark:text-white dark:border-gray-800"
                            id="gender"
                            onChange={handleGenderChange}
                            disabled={isUpdating}
                            value={field.gender === null ? '' : field.gender}
                        >
                            <option disabled value="">Select Gender</option>
                            <option value="male">Male</option>
                            <option value="female">Female</option>
                            <option value="unspecified">Better Not Say</option>
                        </select>
                    </div>
                    <div className="flex flex-col py-2">
                        <label htmlFor="birthday" className="ml-4 text-gray-400 mb-2">Birthday (mm/dd/yyyy)</label>
                        <input
                            className="dark:bg-indigo-1100 dark:text-white dark:border-gray-800"
                            readOnly={isUpdating}
                            type="date"
                            value={field.birthday ? new Date(field.birthday).toISOString().split('T')[0] : ''}
                            onChange={handleBirthdayChange}
                        />
                    </div>
                </div>
                {/* ---- BIO ------- */}
                <div className="flex flex-col py-2">
                    <label htmlFor="bio" className="ml-4 text-gray-400 mb-2">Bio</label>
                    <textarea
                        placeholder="Tell something about yourself"
                        id="bio"
                        cols={10}
                        rows={4}
                        className="dark:bg-indigo-1100 dark:text-white dark:border-gray-800"
                        readOnly={isUpdating}
                        onChange={handleBioChange}
                        maxLength={200}
                        value={field.bio}
                    />
                    <span className="text-xs text-gray-400 block text-right">
                        {bioLength} {bioLength <= 1 ? 'character' : 'characters'} left
                    </span>
                </div>
                {/* ---- SUBMIT BUTTON ----- */}
                <div className="flex justify-between pt-8">
                    <button
                        disabled={isUpdating}
                        type="button"
                        onClick={handleBack}
                        className="button--muted !rounded-full dark:bg-indigo-1100 dark:text-white dark:hover:bg-indigo-1100"
                    >
                        <ArrowLeftOutlined className="text-xl mr-4" />
                        Back to Info
                    </button>
                    <button
                        className="flex items-center"
                        type="submit"
                        disabled={isUpdating}
                    >
                        <SaveOutlined className="text-xl mr-4" />
                        {isUpdating ? 'Updating...' : 'Update'}
                    </button>
                </div>
            </form>
        </div>
    );
}
Example #12
Source File: DetailOrderProduction.tsx    From mayoor with MIT License 4 votes vote down vote up
DetailOrderProduction: React.FC<Props> = ({
	productionLogType,
	productionButtonText,
}) => {
	const routeParams = useParams<{ id: string }>();
	const { t } = useTranslation();
	const [noteValue, setNoteValue] = useState<string | undefined | null>();

	const { data } = useQuery<GetOrder, GetOrderVariables>(GET_ORDER, {
		variables: { number: Number(routeParams.id) },
		onCompleted: (data) => {
			setNoteValue(data.getOrderByNumber?.note);
		},
	});

	const [updateOrderNote] = useMutation<UpdateOrderNote, UpdateOrderNoteVariables>(
		UPDATE_ORDER_NOTE,
	);
	const [addProductionLog] = useMutation<AddProductionLog, AddProductionLogVariables>(
		ADD_PRODUCTION_LOG_MUTATION,
	);

	const updateNoteHandler = async () => {
		const id = data?.getOrderByNumber?.id;
		if (!id) {
			return;
		}
		try {
			await updateOrderNote({
				variables: {
					id,
					note: noteValue,
				},
			});
			message.success(t('note_updated'));
		} catch (err) {
			console.error(err);
			message.error(t('note_update_fail'));
		}
	};

	const productionButtonHandler = (pieces: number, orderItemId: string) => {
		addProductionLog({ variables: { orderItemId, pieces, action: productionLogType } });
	};

	if (!data || !data.getOrderByNumber) {
		return (
			<PageTitle>
				<Skeleton active />
			</PageTitle>
		);
	}

	return (
		<>
			<PageTitle>
				{t('Order #{{number}}', {
					number: data?.getOrderByNumber?.number,
				})}
			</PageTitle>
			<DetailDescription
				createdAt={data?.getOrderByNumber?.createdAt}
				createdByName={data?.getOrderByNumber?.createdBy.name}
				updatedAt={data?.getOrderByNumber?.updatedAt}
			></DetailDescription>
			<OrderWrapper>
				<Row gutter={12}>
					<Col sm={4}>
						<StyledLabel>{t('Material')}</StyledLabel>
					</Col>
					<Col sm={4}>
						<StyledLabel>{t('Name')}</StyledLabel>
					</Col>
					<Col sm={2}>
						<StyledLabel>{t('Width')}</StyledLabel>
					</Col>
					<Col sm={2}>
						<StyledLabel>{t('Height')}</StyledLabel>
					</Col>
					<Col sm={2}>
						<StyledLabel>{t('Pieces')}</StyledLabel>
					</Col>
					<Col sm={2}>
						<StyledLabel>{t('Printed pcs')}</StyledLabel>
					</Col>
					<Col sm={2}>
						<StyledLabel>{t('Production done pcs')}</StyledLabel>
					</Col>
					<Col sm={5}></Col>
				</Row>
				{data.getOrderByNumber.items.map((item) => (
					<ProductionRow
						key={item.id}
						item={item}
						onProductionClick={productionButtonHandler}
						productionButtonText={productionButtonText}
					/>
				))}
				<Row gutter={32}>
					<Col sm={12}>
						<StyledFormItem>
							<StyledLabel>{t('Note')}</StyledLabel>
							<Input.TextArea
								rows={4}
								name="note"
								placeholder={t('note_placeholder')}
								onChange={(e) => setNoteValue(e.target.value)}
								value={noteValue || ''}
							/>
							<Button icon={<SaveOutlined />} onClick={updateNoteHandler}>
								{t('Save note')}
							</Button>
						</StyledFormItem>
					</Col>
					<Col sm={12}>
						<Row justify="end" style={{ marginTop: 20 }}>
							<UpdateStatusButton
								orderId={data.getOrderByNumber.id}
								productionLogType={productionLogType}
								orderStatus={data.getOrderByNumber.status}
							/>
						</Row>
					</Col>
				</Row>
			</OrderWrapper>
		</>
	);
}
Example #13
Source File: YakExecutor.tsx    From yakit with GNU Affero General Public License v3.0 4 votes vote down vote up
YakExecutor: React.FC<YakExecutorProp> = (props) => {
    const [codePath, setCodePath] = useState<string>("")
    const [loading, setLoading] = useState<boolean>(false)
    const [fileList, setFileList] = useState<tabCodeProps[]>([])
    const [tabList, setTabList] = useState<tabCodeProps[]>([])
    const [activeTab, setActiveTab] = useState<string>("")
    const [unTitleCount, setUnTitleCount] = useState(1)

    const [hintShow, setHintShow] = useState<boolean>(false)
    const [hintFile, setHintFile] = useState<string>("")
    const [hintIndex, setHintIndex] = useState<number>(0)

    const [renameHint, setRenameHint] = useState<boolean>(false)
    const [renameIndex, setRenameIndex] = useState<number>(-1)
    const [renameFlag, setRenameFlag] = useState<boolean>(false)
    const [renameCache, setRenameCache] = useState<string>("")

    const [fullScreen, setFullScreen] = useState<boolean>(false)

    const [errors, setErrors] = useState<string[]>([])
    const [executing, setExecuting] = useState(false)
    const [outputEncoding, setOutputEncoding] = useState<"utf8" | "latin1">("utf8")
    const xtermAsideRef = useRef(null)
    const xtermRef = useRef(null)
    const timer = useRef<any>(null)

    const [extraParams, setExtraParams] = useState("")

    // trigger for updating
    const [triggerForUpdatingHistory, setTriggerForUpdatingHistory] = useState<any>(0)

    const addFileTab = useMemoizedFn((res: any) => {
        const {name, code} = res

        const tab: tabCodeProps = {
            tab: `${name}.yak`,
            code: code,
            suffix: "yak",
            isFile: false
        }
        setActiveTab(`${tabList.length}`)
        setTabList(tabList.concat([tab]))
        setUnTitleCount(unTitleCount + 1)
    })

    useEffect(() => {
        ipcRenderer.on("fetch-send-to-yak-running", (e, res: any) => addFileTab(res))
        return () => ipcRenderer.removeAllListeners("fetch-send-to-yak-running")
    }, [])

    // 自动保存
    const autoSave = useMemoizedFn(() => {
        for (let tabInfo of tabList) {
            if (tabInfo.isFile) {
                ipcRenderer.invoke("write-file", {
                    route: tabInfo.route,
                    data: tabInfo.code
                })
            }
        }
    })
    // 保存近期文件内的15个
    const saveFiliList = useMemoizedFn(() => {
        let files = cloneDeep(fileList).reverse()
        files.splice(14)
        files = files.reverse()
        ipcRenderer.invoke("set-value", RecentFileList, files)
    })

    // 获取和保存近期打开文件信息,同时展示打开默认内容
    useEffect(() => {
        let time: any = null
        let timer: any = null
        setLoading(true)
        ipcRenderer
            .invoke("get-value", RecentFileList)
            .then((value: any) => {
                if ((value || []).length !== 0) {
                    setFileList(value)
                } else {
                    const tab: tabCodeProps = {
                        tab: `Untitle-${unTitleCount}.yak`,
                        code: "# input your yak code\nprintln(`Hello Yak World!`)",
                        suffix: "yak",
                        isFile: false
                    }
                    setActiveTab(`${tabList.length}`)
                    setTabList([tab])
                    setUnTitleCount(unTitleCount + 1)
                }
            })
            .catch(() => {})
            .finally(() => {
                setTimeout(() => setLoading(false), 300)
                time = setInterval(() => {
                    autoSave()
                }, 2000)
                timer = setInterval(() => {
                    saveFiliList()
                }, 5000)
            })

        return () => {
            saveFiliList()
            if (time) clearInterval(time)
            if (timer) clearInterval(timer)
        }
    }, [])

    // 全局监听重命名事件是否被打断
    useEffect(() => {
        document.onmousedown = (e) => {
            // @ts-ignore
            if (e.path[0].id !== "rename-input" && renameFlag) {
                renameCode(renameIndex)
                setRenameFlag(false)
            }
        }
    }, [renameFlag])

    // 打开文件
    const addFile = useMemoizedFn((file: any) => {
        const isExists = fileList.filter((item) => item.tab === file.name && item.route === file.path).length === 1

        if (isExists) {
            for (let index in tabList) {
                const item = tabList[index]
                if (item.tab === file.name && item.route === file.path) {
                    setActiveTab(`${index}`)
                    return false
                }
            }
        }
        ipcRenderer
            .invoke("fetch-file-content", file.path)
            .then((res) => {
                const tab: tabCodeProps = {
                    tab: file.name,
                    code: res,
                    suffix: file.name.split(".").pop() === "yak" ? "yak" : "http",
                    isFile: true,
                    route: file.path,
                    extraParams: file.extraParams
                }
                setActiveTab(`${tabList.length}`)
                if (!isExists) setFileList(fileList.concat([tab]))
                setTabList(tabList.concat([tab]))
            })
            .catch(() => {
                failed("无法获取该文件内容,请检查后后重试!")
                const files = cloneDeep(fileList)
                for (let i in files) if (files[i].route === file.path) files.splice(i, 1)
                setFileList(files)
            })
        return false
    })
    // 新建文件
    const newFile = useMemoizedFn(() => {
        const tab: tabCodeProps = {
            tab: `Untitle-${unTitleCount}.yak`,
            code: "# input your yak code\nprintln(`Hello Yak World!`)",
            suffix: "yak",
            isFile: false
        }
        setActiveTab(`${tabList.length}`)
        setTabList(tabList.concat([tab]))
        setUnTitleCount(unTitleCount + 1)
    })
    //修改文件
    const modifyCode = useMemoizedFn((value: string, index: number) => {
        const tabs = cloneDeep(tabList)
        tabs[index].code = value
        setTabList(tabs)
    })
    // 保存文件
    const saveCode = useMemoizedFn((info: tabCodeProps, index: number) => {
        if (info.isFile) {
            ipcRenderer.invoke("write-file", {
                route: info.route,
                data: info.code
            })
        } else {
            ipcRenderer.invoke("show-save-dialog", `${codePath}${codePath ? '/' : ''}${info.tab}`).then((res) => {
                if (res.canceled) return

                const path = res.filePath
                const name = res.name
                ipcRenderer
                    .invoke("write-file", {
                        route: res.filePath,
                        data: info.code
                    })
                    .then(() => {
                        const suffix = name.split(".").pop()

                        var tabs = cloneDeep(tabList)
                        var active = null
                        tabs = tabs.filter((item) => item.route !== path)
                        tabs = tabs.map((item, index) => {
                            if (!item.route && item.tab === info.tab) {
                                active = index
                                item.tab = name
                                item.isFile = true
                                item.suffix = suffix === "yak" ? suffix : "http"
                                item.route = path
                                return item
                            }
                            return item
                        })
                        if (active !== null) setActiveTab(`${active}`)
                        setTabList(tabs)
                        const file: tabCodeProps = {
                            tab: name,
                            code: info.code,
                            isFile: true,
                            suffix: suffix === "yak" ? suffix : "http",
                            route: res.filePath,
                            extraParams: info.extraParams
                        }
                        for (let item of fileList) {
                            if (item.route === file.route) {
                                return
                            }
                        }
                        setFileList(fileList.concat([file]))
                    })
            })
        }
    })
    //关闭文件
    const closeCode = useMemoizedFn((index, isFileList: boolean) => {
        const tabInfo = isFileList ? fileList[+index] : tabList[+index]
        if (isFileList) {
            for (let i in tabList) {
                if (tabList[i].tab === tabInfo.tab && tabList[i].route === tabInfo.route) {
                    const tabs = cloneDeep(tabList)
                    tabs.splice(i, 1)
                    setTabList(tabs)
                    setActiveTab(tabs.length >= 1 ? `0` : "")
                }
            }
            const files = cloneDeep(fileList)
            files.splice(+index, 1)
            setFileList(files)
        } else {
            setActiveTab(index)

            if (!tabInfo.isFile) {
                setHintFile(tabInfo.tab)
                setHintIndex(index)
                setHintShow(true)
            } else {
                const tabs = cloneDeep(tabList)
                tabs.splice(+index, 1)
                setTabList(tabs)
                setActiveTab(tabs.length >= 1 ? `0` : "")
            }
        }
    })
    // 关闭虚拟文件不保存
    const ownCloseCode = useMemoizedFn(() => {
        const tabs = cloneDeep(tabList)
        tabs.splice(hintIndex, 1)
        setTabList(tabs)
        setHintShow(false)
        setActiveTab(tabs.length >= 1 ? `0` : "")
    })
    // 删除文件
    const delCode = useMemoizedFn((index) => {
        const fileInfo = fileList[index]

        ipcRenderer
            .invoke("delelte-code-file", fileInfo.route)
            .then(() => {
                for (let i in tabList) {
                    if (tabList[i].tab === fileInfo.tab && tabList[i].route === fileInfo.route) {
                        const tabs = cloneDeep(tabList)
                        tabs.splice(i, 1)
                        setTabList(tabs)
                        setActiveTab(tabs.length >= 1 ? `0` : "")
                    }
                }
                const arr = cloneDeep(fileList)
                arr.splice(index === undefined ? hintIndex : index, 1)
                setFileList(arr)
            })
            .catch(() => {
                failed("文件删除失败!")
            })
    })
    //重命名操作
    const renameCode = useMemoizedFn((index: number) => {
        const tabInfo = fileList[index]

        if (renameCache === tabInfo.tab) return
        if (!renameCache) return

        if (!tabInfo.route) return
        const flagStr = tabInfo.route?.indexOf("/") > -1 ? "/" : "\\"
        const routes = tabInfo.route?.split(flagStr)
        routes?.pop()
        ipcRenderer
            .invoke("is-exists-file", routes?.concat([renameCache]).join(flagStr))
            .then(() => {
                const newRoute = routes?.concat([renameCache]).join(flagStr)
                if (!tabInfo.route || !newRoute) return
                renameFile(index, renameCache, tabInfo.route, newRoute)
            })
            .catch(() => {
                setRenameHint(true)
            })
    })
    // 重命名文件
    const renameFile = useMemoizedFn(
        (index: number, rename: string, oldRoute: string, newRoute: string, callback?: () => void) => {
            ipcRenderer.invoke("rename-file", {old: oldRoute, new: newRoute}).then(() => {
                const suffix = rename.split(".").pop()

                var files = cloneDeep(fileList)
                var tabs = cloneDeep(tabList)
                var active = null
                files = files.filter((item) => item.route !== newRoute)
                tabs = tabs.filter((item) => item.route !== newRoute)

                files = files.map((item) => {
                    if (item.route === oldRoute) {
                        item.tab = rename
                        item.suffix = suffix === "yak" ? suffix : "http"
                        item.route = newRoute
                        return item
                    }
                    return item
                })
                tabs = tabs.map((item, index) => {
                    if (item.route === oldRoute) {
                        active = index
                        item.tab = rename
                        item.suffix = suffix === "yak" ? suffix : "http"
                        item.route = newRoute
                        return item
                    }
                    return item
                })
                if (active !== null) setActiveTab(`${active}`)
                setFileList(files)
                setTabList(tabs)

                if (callback) callback()
            })
        }
    )

    const fileFunction = (kind: string, index: string, isFileList: boolean) => {
        const tabCodeInfo = isFileList ? fileList[index] : tabList[index]

        switch (kind) {
            case "own":
                closeCode(index, isFileList)
                return
            case "other":
                const tabInfo: tabCodeProps = cloneDeep(tabList[index])
                for (let i in tabList) {
                    if (i !== index && !tabList[i].isFile) {
                        const arr: tabCodeProps[] =
                            +i > +index
                                ? [tabInfo].concat(tabList.splice(+i, tabList.length))
                                : tabList.splice(+i, tabList.length)
                        const num = +i > +index ? 1 : 0

                        setActiveTab(`${num}`)
                        setTabList(arr)
                        setHintFile(arr[num].tab)
                        setHintIndex(num)
                        setHintShow(true)
                        return
                    }
                }
                const code = cloneDeep(tabList[index])
                setTabList([code])
                setActiveTab(`0`)
                return
            case "all":
                for (let i in tabList) {
                    if (!tabList[i].isFile) {
                        const arr = tabList.splice(+i, tabList.length)
                        setActiveTab("0")
                        setTabList(arr)
                        setHintFile(arr[0].tab)
                        setHintIndex(0)
                        setHintShow(true)
                        return
                    }
                }
                setActiveTab("")
                setTabList([])
                return
            case "remove":
                closeCode(index, isFileList)
                return
            case "delete":
                delCode(index)
                return
            case "rename":
                setRenameIndex(+index)
                setRenameFlag(true)
                setRenameCache(tabCodeInfo.tab)
                return
        }
    }

    const openFileLayout = (file: any) => {
        addFile(file)
    }

    useEffect(() => {
        ipcRenderer.invoke("fetch-code-path")
            .then((path: string) => {
                ipcRenderer.invoke("is-exists-file", path)
                    .then(() => {
                        setCodePath("")
                    })
                    .catch(() => {
                        setCodePath(path)
                    })
            })
    }, [])

    useEffect(() => {
        if (tabList.length === 0) setFullScreen(false)
    }, [tabList])

    useEffect(() => {
        if (!xtermRef) {
            return
        }
        // let buffer = "";
        ipcRenderer.on("client-yak-error", async (e: any, data) => {
            failed(`FoundError: ${JSON.stringify(data)}`)
            if (typeof data === "object") {
                setErrors([...errors, `${JSON.stringify(data)}`])
            } else if (typeof data === "string") {
                setErrors([...errors, data])
            } else {
                setErrors([...errors, `${data}`])
            }
        })
        ipcRenderer.on("client-yak-end", () => {
            info("Yak 代码执行完毕")
            setTriggerForUpdatingHistory(getRandomInt(100000))
            setTimeout(() => {
                setExecuting(false)
            }, 300)
        })
        ipcRenderer.on("client-yak-data", async (e: any, data: ExecResult) => {
            if (data.IsMessage) {
                // alert(Buffer.from(data.Message).toString("utf8"))
            }
            if (data?.Raw) {
                writeExecResultXTerm(xtermRef, data, outputEncoding)
                // writeXTerm(xtermRef, Buffer.from(data.Raw).toString(outputEncoding).replaceAll("\n", "\r\n"))
                // monacoEditorWrite(currentOutputEditor, )
            }
        })
        return () => {
            ipcRenderer.removeAllListeners("client-yak-data")
            ipcRenderer.removeAllListeners("client-yak-end")
            ipcRenderer.removeAllListeners("client-yak-error")
        }
    }, [xtermRef])

    const bars = (props: any, TabBarDefault: any) => {
        return (
            <TabBarDefault
                {...props}
                children={(barNode: React.ReactElement) => {
                    return (
                        <Dropdown
                            overlay={CustomMenu(barNode.key, false, tabMenu, fileFunction)}
                            trigger={["contextMenu"]}
                        >
                            {barNode}
                        </Dropdown>
                    )
                }}
            />
        )
    }

    return (
        <AutoCard
            className={"yak-executor-body"}
            // title={"Yak Runner"}
            headStyle={{minHeight: 0}}
            bodyStyle={{padding: 0, overflow: "hidden"}}
        >
            <div
                style={{width: "100%", height: "100%", display: "flex", backgroundColor: "#E8E9E8"}}
                tabIndex={0}
                onKeyDown={(e) => {
                    if (e.keyCode === 78 && (e.ctrlKey || e.metaKey)) {
                        newFile()
                    }
                    if (e.keyCode === 83 && (e.ctrlKey || e.metaKey) && activeTab) {
                        saveCode(tabList[+activeTab], +activeTab)
                    }
                }}
            >
                <div style={{width: `${fullScreen ? 0 : 15}%`}}>
                    <AutoSpin spinning={loading}>
                        <ExecutorFileList
                            lists={fileList}
                            activeFile={tabList[+activeTab]?.route || ""}
                            renameFlag={renameFlag}
                            renameIndex={renameIndex}
                            renameCache={renameCache}
                            setRenameCache={setRenameCache}
                            addFile={addFile}
                            newFile={newFile}
                            openFile={openFileLayout}
                            fileFunction={fileFunction}
                        />
                    </AutoSpin>
                </div>
                <div style={{width: `${fullScreen ? 100 : 85}%`}} className='executor-right-body'>
                    {tabList.length > 0 && (
                        <ResizeBox
                            isVer
                            firstNode={
                                <Tabs
                                    className={"right-editor"}
                                    style={{height: "100%"}}
                                    type='editable-card'
                                    activeKey={activeTab}
                                    hideAdd={true}
                                    onChange={(activeTab) => setActiveTab(activeTab)}
                                    onEdit={(key, event: "add" | "remove") => {
                                        switch (event) {
                                            case "remove":
                                                closeCode(key, false)
                                                return
                                            case "add":
                                                return
                                        }
                                    }}
                                    renderTabBar={(props, TabBarDefault) => {
                                        return bars(props, TabBarDefault)
                                    }}
                                    tabBarExtraContent={
                                        tabList.length && (
                                            <Space style={{marginRight: 5}} size={0}>
                                                <Button
                                                    style={{height: 25}}
                                                    type={"link"}
                                                    size={"small"}
                                                    disabled={
                                                        tabList[+activeTab] && tabList[+activeTab].suffix !== "yak"
                                                    }
                                                    onClick={(e) => {
                                                        let m = showDrawer({
                                                            width: "60%",
                                                            placement: "left",
                                                            title: "选择你的 Yak 模块执行特定功能",
                                                            content: (
                                                                <>
                                                                    <YakScriptManagerPage
                                                                        type={"yak"}
                                                                        onLoadYakScript={(s) => {
                                                                            const tab: tabCodeProps = {
                                                                                tab: `Untitle-${unTitleCount}.yak`,
                                                                                code: s.Content,
                                                                                suffix: "yak",
                                                                                isFile: false
                                                                            }
                                                                            info(`加载 Yak 模块:${s.ScriptName}`)
                                                                            xtermClear(xtermRef)
                                                                            setActiveTab(`${tabList.length}`)
                                                                            setTabList(tabList.concat([tab]))
                                                                            setUnTitleCount(unTitleCount + 1)
                                                                            m.destroy()
                                                                        }}
                                                                    />
                                                                </>
                                                            )
                                                        })
                                                    }}
                                                >
                                                    Yak脚本模板
                                                </Button>

                                                <Button
                                                    icon={
                                                        fullScreen ? (
                                                            <FullscreenExitOutlined style={{fontSize: 15}} />
                                                        ) : (
                                                            <FullscreenOutlined style={{fontSize: 15}} />
                                                        )
                                                    }
                                                    type={"link"}
                                                    size={"small"}
                                                    style={{width: 30, height: 25}}
                                                    onClick={() => setFullScreen(!fullScreen)}
                                                />
                                                <Popover
                                                    trigger={["click"]}
                                                    title={"设置命令行额外参数"}
                                                    placement="bottomRight"
                                                    content={
                                                        <Space style={{width: 400}}>
                                                            <div>yak {tabList[+activeTab]?.tab || "[file]"}</div>
                                                            <Divider type={"vertical"} />
                                                            <Paragraph
                                                                style={{width: 200, marginBottom: 0}}
                                                                editable={{
                                                                    icon: <Space>
                                                                        <EditOutlined />
                                                                        <SaveOutlined onClick={(e) => {
                                                                            e.stopPropagation()
                                                                            tabList[+activeTab].extraParams = extraParams
                                                                            setTabList(tabList)
                                                                            if(tabList[+activeTab].isFile){
                                                                                const files = fileList.map(item => {
                                                                                    if(item.route === tabList[+activeTab].route){
                                                                                        item.extraParams = extraParams
                                                                                        return item
                                                                                    }
                                                                                    return item
                                                                                })
                                                                                setFileList(files)
                                                                            }
                                                                            success("保存成功")
                                                                        }} 
                                                                    /></Space>,
                                                                    tooltip: '编辑/保存为该文件默认参数',
                                                                    onChange: setExtraParams
                                                                }}
                                                            >
                                                                {extraParams}
                                                            </Paragraph>
                                                        </Space>
                                                    }
                                                >
                                                    <Button type={"link"} icon={<EllipsisOutlined />} onClick={() => {
                                                        setExtraParams(tabList[+activeTab]?.extraParams || "")
                                                    }} />
                                                </Popover>
                                                {executing ? (
                                                    <Button
                                                        icon={<PoweroffOutlined style={{fontSize: 15}} />}
                                                        type={"link"}
                                                        danger={true}
                                                        size={"small"}
                                                        style={{width: 30, height: 25}}
                                                        onClick={() => ipcRenderer.invoke("cancel-yak")}
                                                    />
                                                ) : (
                                                    <Button
                                                        icon={<CaretRightOutlined style={{fontSize: 15}} />}
                                                        type={"link"}
                                                        ghost={true}
                                                        size={"small"}
                                                        style={{width: 30, height: 25}}
                                                        disabled={
                                                            tabList[+activeTab] && tabList[+activeTab].suffix !== "yak"
                                                        }
                                                        onClick={() => {
                                                            setErrors([])
                                                            setExecuting(true)
                                                            ipcRenderer.invoke("exec-yak", {
                                                                Script: tabList[+activeTab].code,
                                                                Params: [],
                                                                RunnerParamRaw: extraParams
                                                            })
                                                        }}
                                                    />
                                                )}
                                            </Space>
                                        )
                                    }
                                >
                                    {tabList.map((item, index) => {
                                        return (
                                            <TabPane tab={item.tab} key={`${index}`}>
                                                <div style={{height: "100%"}}>
                                                    <AutoSpin spinning={executing}>
                                                        <div style={{height: "100%"}}>
                                                            <YakEditor
                                                                type={item.suffix}
                                                                value={item.code}
                                                                setValue={(value) => {
                                                                    modifyCode(value, index)
                                                                }}
                                                            />
                                                        </div>
                                                    </AutoSpin>
                                                </div>
                                            </TabPane>
                                        )
                                    })}
                                </Tabs>
                            }
                            firstRatio='70%'
                            secondNode={
                                <div
                                    ref={xtermAsideRef}
                                    style={{
                                        width: "100%",
                                        height: "100%",
                                        overflow: "hidden",
                                        borderTop: "1px solid #dfdfdf"
                                    }}
                                >
                                    <Tabs
                                        style={{height: "100%"}}
                                        className={"right-xterm"}
                                        size={"small"}
                                        tabBarExtraContent={
                                            <Space>
                                                <SelectOne
                                                    formItemStyle={{marginBottom: 0}}
                                                    value={outputEncoding}
                                                    setValue={setOutputEncoding}
                                                    size={"small"}
                                                    data={[
                                                        {text: "GBxxx编码", value: "latin1"},
                                                        {text: "UTF-8编码", value: "utf8"}
                                                    ]}
                                                />
                                                <Button
                                                    size={"small"}
                                                    icon={<DeleteOutlined />}
                                                    type={"link"}
                                                    onClick={(e) => {
                                                        xtermClear(xtermRef)
                                                    }}
                                                />
                                            </Space>
                                        }
                                    >
                                        <TabPane
                                            tab={<div style={{width: 50, textAlign: "center"}}>输出</div>}
                                            key={"output"}
                                        >
                                            <div style={{width: "100%", height: "100%"}}>
                                                <CVXterm
                                                    ref={xtermRef}
                                                    options={{
                                                        convertEol: true,
                                                        theme: {
                                                            foreground: "#536870",
                                                            background: "#E8E9E8",
                                                            cursor: "#536870",

                                                            black: "#002831",
                                                            brightBlack: "#001e27",

                                                            red: "#d11c24",
                                                            brightRed: "#bd3613",

                                                            green: "#738a05",
                                                            brightGreen: "#475b62",

                                                            yellow: "#a57706",
                                                            brightYellow: "#536870",

                                                            blue: "#2176c7",
                                                            brightBlue: "#708284",

                                                            magenta: "#c61c6f",
                                                            brightMagenta: "#5956ba",

                                                            cyan: "#259286",
                                                            brightCyan: "#819090",

                                                            white: "#eae3cb",
                                                            brightWhite: "#fcf4dc"
                                                        }
                                                    }}
                                                />
                                            </div>
                                        </TabPane>
                                        <TabPane
                                            tab={
                                                <div style={{width: 50, textAlign: "center"}} key={"terminal"}>
                                                    终端(监修中)
                                                </div>
                                            }
                                            disabled
                                        >
                                            <Terminal />
                                        </TabPane>
                                    </Tabs>
                                </div>
                            }
                            secondRatio='30%'
                        />
                    )}
                    {tabList.length === 0 && (
                        <Empty className='right-empty' description={<p>请点击左侧打开或新建文件</p>}></Empty>
                    )}
                </div>

                <Modal
                    visible={hintShow}
                    onCancel={() => setHintShow(false)}
                    footer={[
                        <Button key='link' onClick={() => setHintShow(false)}>
                            取消
                        </Button>,
                        <Button key='submit' onClick={() => ownCloseCode()}>
                            不保存
                        </Button>,
                        <Button key='back' type='primary' onClick={() => saveCode(tabList[hintIndex], hintIndex)}>
                            保存
                        </Button>
                    ]}
                >
                    <div style={{height: 40}}>
                        <ExclamationCircleOutlined style={{fontSize: 22, color: "#faad14"}} />
                        <span style={{fontSize: 18, marginLeft: 15}}>文件未保存</span>
                    </div>
                    <p style={{fontSize: 15, marginLeft: 37}}>{`是否要保存${hintFile}里面的内容吗?`}</p>
                </Modal>

                <Modal
                    visible={renameHint}
                    onCancel={() => setHintShow(false)}
                    footer={[
                        <Button key='link' onClick={() => setRenameHint(false)}>
                            取消
                        </Button>,
                        <Button
                            key='back'
                            type='primary'
                            onClick={() => {
                                const oldRoute = tabList[renameIndex].route
                                if (!oldRoute) return
                                const flagStr = oldRoute?.indexOf("/") > -1 ? "/" : "\\"
                                const routes = oldRoute?.split(flagStr)
                                routes?.pop()
                                const newRoute = routes?.concat([renameCache]).join(flagStr)
                                if (!oldRoute || !newRoute) return
                                renameFile(renameIndex, renameCache, oldRoute, newRoute, () => {
                                    setRenameHint(false)
                                })
                            }}
                        >
                            确定
                        </Button>
                    ]}
                >
                    <div style={{height: 40}}>
                        <ExclamationCircleOutlined style={{fontSize: 22, color: "#faad14"}} />
                        <span style={{fontSize: 18, marginLeft: 15}}>文件已存在</span>
                    </div>
                    <p style={{fontSize: 15, marginLeft: 37}}>{`是否要覆盖已存在的文件吗?`}</p>
                </Modal>
            </div>
        </AutoCard>
    )
}
Example #14
Source File: UserCreate.tsx    From mayoor with MIT License 4 votes vote down vote up
UserCreate: React.FC = () => {
	const { t } = useTranslation();

	const [createUser] = useMutation<CreateUser, CreateUserVariables>(CREATE_USER, {
		onCompleted: () => {
			message.success(t('User created'));
		},
		update: (cache, { data }) => {
			const cached = cache.readQuery<GetAllUsers>({ query: GET_ALL_USERS });
			if (cached === null || !data) {
				return;
			}
			const { getAllUsers } = cached;
			cache.writeQuery<GetAllUsers>({
				query: GET_ALL_USERS,
				data: {
					getAllUsers: [...getAllUsers, data.addUser],
				},
			});
		},
	});

	return (
		<>
			<StyledDivider orientation="left">{t('Add new user')}</StyledDivider>
			<Row gutter={18}>
				<Col sm={4}>
					<StyledLabel>{t('Login email')}</StyledLabel>
				</Col>
				<Col sm={4}>
					<StyledLabel>{t('Password')}</StyledLabel>
				</Col>
				<Col sm={6}>
					<StyledLabel>{t('User Name')}</StyledLabel>
				</Col>
				<Col sm={4}>
					<StyledLabel>{t('Role')}</StyledLabel>
				</Col>
				<Col sm={4}></Col>
			</Row>
			<Formik<FormValue>
				initialValues={{
					name: '',
					password: '',
					email: '',
					role: UserRole.FACTORY,
				}}
				onSubmit={(values) =>
					createUser({
						variables: {
							input: {
								email: values.email,
								name: values.name,
								password: values.password,
								role: values.role,
							},
						},
					})
				}
				validationSchema={getUserValidationSchema(t)}
			>
				{({ handleSubmit }) => (
					<form onSubmit={handleSubmit}>
						<Row gutter={18}>
							<Col sm={4}>
								<FormInput label={t('Login email')} name="email"></FormInput>
							</Col>
							<Col sm={4}>
								<FormInput
									label={t('Password')}
									name="password"
									type="password"
								></FormInput>
							</Col>
							<Col sm={6}>
								<FormInput label={t('User Name')} name="name"></FormInput>
							</Col>
							<Col sm={4}>
								<UserRoleSelect />
							</Col>
							<Col sm={3}>
								<Button
									icon={<SaveOutlined />}
									htmlType="submit"
									style={{ width: '100%' }}
									type="primary"
								>
									{t('Add')}
								</Button>
							</Col>
						</Row>
					</form>
				)}
			</Formik>
		</>
	);
}
Example #15
Source File: UserEdit.tsx    From mayoor with MIT License 4 votes vote down vote up
UserEdit: React.FC = () => {
	const { t } = useTranslation();
	const [currentlyLoading, setCurrentlyLoading] = useState<string | null>(null);

	const { data } = useQuery<GetAllUsers>(GET_ALL_USERS);

	const [updateUser] = useMutation<UpdateUser, UpdateUserVariables>(UPDATE_USER, {
		onCompleted: () => {
			message.success(t('User updated'));
		},
	});
	const [deleteUser] = useMutation<DeleteUser, DeleteUserVariables>(DELETE_USER, {
		onCompleted: () => {
			message.success(t('User deleted'));
		},
		update: (cache, { data }) => {
			const cached = cache.readQuery<GetAllUsers>({ query: GET_ALL_USERS });
			if (cached === null) {
				return;
			}
			const { getAllUsers } = cached;
			cache.writeQuery<GetAllUsers>({
				query: GET_ALL_USERS,
				data: {
					getAllUsers: getAllUsers.filter(({ id }) => id !== data?.deleteUser.id),
				},
			});
		},
	});

	return (
		<>
			<PageTitle>{t('Users')}</PageTitle>
			<StyledForm>
				<MaterialEditWrapper>
					<>
						<Row gutter={18}>
							<Col sm={4}>
								<StyledLabel>{t('Login email')}</StyledLabel>
							</Col>
							<Col sm={4}>
								<StyledLabel>{t('Change password')}</StyledLabel>
							</Col>
							<Col sm={6}>
								<StyledLabel>{t('User Name')}</StyledLabel>
							</Col>
							<Col sm={4}>
								<StyledLabel>{t('Role')}</StyledLabel>
							</Col>
							<Col sm={4}></Col>
						</Row>
						{data?.getAllUsers.map((user) => (
							<Formik<FormikValues>
								key={user.id}
								initialValues={{ ...user, password: undefined }}
								onSubmit={async (values) => {
									setCurrentlyLoading(user.id);
									await updateUser({
										variables: {
											id: user.id,
											input: {
												email: values.email,
												name: values.name,
												role: values.role,
												password: values.password || undefined,
											},
										},
									});
									setCurrentlyLoading(null);
								}}
								validationSchema={getUserValidationSchema(t)}
							>
								{({ handleSubmit }) => (
									<Row gutter={18}>
										<Col sm={4}>
											<FormInput
												label={t('Login email')}
												name="email"
											></FormInput>
										</Col>
										<Col sm={4}>
											<FormInput
												label={t('New Password')}
												name="password"
												type="password"
											></FormInput>
										</Col>
										<Col sm={6}>
											<FormInput
												label={t('User Name')}
												name="name"
											></FormInput>
										</Col>
										<Col sm={4}>
											<UserRoleSelect />
										</Col>
										<Col sm={3}>
											<Button
												onClick={() => handleSubmit()}
												loading={currentlyLoading === user.id}
												icon={<SaveOutlined />}
												style={{ width: '100%' }}
											>
												{t('Save')}
											</Button>
										</Col>
										<Col sm={1} style={{ textAlign: 'right' }}>
											<Popconfirm
												placement="topRight"
												onConfirm={() =>
													deleteUser({ variables: { id: user.id } })
												}
												title={t('Do you really want to remove this user?')}
											>
												<Button
													shape="circle"
													icon={<DeleteOutlined />}
												></Button>
											</Popconfirm>
										</Col>
									</Row>
								)}
							</Formik>
						))}
						<UserCreate />
					</>
				</MaterialEditWrapper>
			</StyledForm>
		</>
	);
}
Example #16
Source File: index.tsx    From memex with MIT License 4 votes vote down vote up
OperatorBar: React.FC<OperatorBarProps> = () => {
  const dispatch = useDispatch();

  const menus = [
    {
      id: 'inbox',
      name: 'Inbox',
      icon: <InboxOutlined />,
      onClick: () => {
        dispatch({
          type: 'CHANGE_PAGE',
          payload: {
            page: 'inbox',
          },
        });
      },
    },
    {
      id: 'notes',
      name: 'Notes & Highlights',
      icon: <ReadOutlined />,
      onClick: () => {
        dispatch({
          type: 'CHANGE_PAGE',
          payload: {
            page: 'notes',
          },
        });
      },
    },
    {
      id: 'search',
      name: 'Search',
      icon: <SearchOutlined />,
    },
    {
      id: 'divider1',
      isDivider: true,
    },
    {
      id: 'tag_management',
      name: 'Tag Management',
      icon: <ApartmentOutlined />,
      onClick: () => {
        dispatch({
          type: 'CHANGE_PAGE',
          payload: {
            page: 'tag_management',
          },
        });
      },
    },
    {
      id: 'diffuse',
      name: 'Knowledge Map',
      icon: <DeploymentUnitOutlined />,
      onClick: () => {
        dispatch({
          type: 'CHANGE_PAGE',
          payload: {
            page: 'graph',
          },
        });
      },
    },
    {
      id: 'divider2',
      isDivider: true,
    },

    {
      id: 'save',
      name: 'Save',
      icon: <SaveOutlined />,
    },
    {
      id: 'clear',
      name: 'Delete',
      icon: <DeleteOutlined />,
    },
    {
      id: 'setting',
      name: 'Settings',
      icon: <SettingOutlined />,
    },
  ];
  return (
    <div>
      <ToolBar data={menus} className="operator-bar" />
    </div>
  );
}
Example #17
Source File: palette.tsx    From jmix-frontend with Apache License 2.0 4 votes vote down vote up
palette = () => (
  <Palette>
    <Category name="Text">
      <Component name="Formatted Message">
        <Variant>
          <FormattedMessage />
        </Variant>
      </Component>
      <Component name="Heading">
        <Variant name="h1">
          <Typography.Title></Typography.Title>
        </Variant>
        <Variant name="h2">
          <Typography.Title level={2}></Typography.Title>
        </Variant>
        <Variant name="h3">
          <Typography.Title level={3}></Typography.Title>
        </Variant>
        <Variant name="h4">
          <Typography.Title level={4}></Typography.Title>
        </Variant>
        <Variant name="h5">
          <Typography.Title level={5}></Typography.Title>
        </Variant>
      </Component>
      <Component name="Text">
        <Variant>
          <Typography.Text></Typography.Text>
        </Variant>
        <Variant name="Secondary">
          <Typography.Text type="secondary"></Typography.Text>
        </Variant>
        <Variant name="Success">
          <Typography.Text type="success"></Typography.Text>
        </Variant>
        <Variant name="Warning">
          <Typography.Text type="warning"></Typography.Text>
        </Variant>
        <Variant name="Danger">
          <Typography.Text type="danger"></Typography.Text>
        </Variant>
        <Variant name="Disabled">
          <Typography.Text disabled></Typography.Text>
        </Variant>
      </Component>
    </Category>
    <Category name="Layout">
      <Component name="Divider">
        <Variant>
          <Divider />
        </Variant>
      </Component>

      <Component name="Grid">
        <Variant name="Simple Row">
          <Row></Row>
        </Variant>
        <Variant name="Two columns">
          <Row>
            <Col span={12}></Col>
            <Col span={12}></Col>
          </Row>
        </Variant>
        <Variant name="Three columns">
          <Row>
            <Col span={8}></Col>
            <Col span={8}></Col>
            <Col span={8}></Col>
          </Row>
        </Variant>
      </Component>

      <Component name="Space">
        <Variant>
          <Space />
        </Variant>
        <Variant name="Small">
          <Space size={"small"} />
        </Variant>
        <Variant name="Large">
          <Space size={"large"} />
        </Variant>
      </Component>
    </Category>
    <Category name="Controls">
      <Component name="Autocomplete">
        <Variant>
          <AutoComplete placeholder="input here" />
        </Variant>
      </Component>

      <Component name="Button">
        <Variant>
          <Button></Button>
        </Variant>
        <Variant name="Primary">
          <Button type="primary"></Button>
        </Variant>
        <Variant name="Link">
          <Button type="link"></Button>
        </Variant>
        <Variant name="Dropdown">
          <Dropdown
            trigger={["click"]}
            overlay={
              <Menu>
                <Menu.Item></Menu.Item>
                <Menu.Item></Menu.Item>
                <Menu.Item></Menu.Item>
              </Menu>
            }
          >
            <Button></Button>
          </Dropdown>
        </Variant>
      </Component>

      <Component name="Checkbox">
        <Variant>
          <Checkbox />
        </Variant>
      </Component>

      <Component name="Switch">
        <Variant>
          <Switch />
        </Variant>
      </Component>

      <Component name="Radio Group">
        <Variant>
          <Radio.Group>
            <Radio value={1}>A</Radio>
            <Radio value={2}>B</Radio>
            <Radio value={3}>C</Radio>
            <Radio value={4}>D</Radio>
          </Radio.Group>
        </Variant>
        <Variant name="Button">
          <Radio.Group>
            <Radio.Button value={1}>A</Radio.Button>
            <Radio.Button value={2}>B</Radio.Button>
            <Radio.Button value={3}>C</Radio.Button>
            <Radio.Button value={4}>D</Radio.Button>
          </Radio.Group>
        </Variant>
      </Component>

      <Component name="DatePicker">
        <Variant>
          <DatePicker />
        </Variant>
        <Variant name="Range">
          <DatePicker.RangePicker />
        </Variant>
      </Component>

      <Component name="TimePicker">
        <Variant>
          <TimePicker />
        </Variant>
        <Variant name="Range">
          <TimePicker.RangePicker />
        </Variant>
      </Component>

      <Component name="Input">
        <Variant>
          <Input />
        </Variant>
        <Variant name="Number">
          <InputNumber />
        </Variant>
      </Component>

      <Component name="Select">
        <Variant>
          <Select defaultValue="1">
            <Select.Option value="1">1</Select.Option>
            <Select.Option value="2">2</Select.Option>
          </Select>
        </Variant>
        <Variant name="Multiple">
          <Select defaultValue={["1"]} mode="multiple" allowClear>
            <Select.Option value="1">1</Select.Option>
            <Select.Option value="2">2</Select.Option>
          </Select>
        </Variant>
      </Component>

      <Component name="Link">
        <Variant>
          <Typography.Link href="" target="_blank"></Typography.Link>
        </Variant>
      </Component>

      <Component name="Slider">
        <Variant>
          <Slider defaultValue={30} />
        </Variant>
        <Variant name="Range">
          <Slider range defaultValue={[20, 50]} />
        </Variant>
      </Component>
    </Category>
    <Category name="Data Display">
      <Component name="Field">
        <Variant>
          <Field
            entityName={ENTITY_NAME}
            disabled={readOnlyMode}
            propertyName=""
            formItemProps={{
              style: { marginBottom: "12px" }
            }}
          />
        </Variant>
      </Component>
      <Component name="Card">
        <Variant>
          <Card />
        </Variant>
        <Variant name="With Title">
          <Card>
            <Card title="Card title">
              <p>Card content</p>
            </Card>
          </Card>
        </Variant>
        <Variant name="My custom card">
          <Card>
            <Card title="Card title">
              <p>Card content</p>
              <Avatar />
            </Card>
          </Card>
        </Variant>
      </Component>
      <Component name="Tabs">
        <Variant>
          <Tabs defaultActiveKey="1">
            <Tabs.TabPane tab="Tab 1" key="1">
              Content of Tab Pane 1
            </Tabs.TabPane>
            <Tabs.TabPane tab="Tab 2" key="2">
              Content of Tab Pane 2
            </Tabs.TabPane>
            <Tabs.TabPane tab="Tab 3" key="3">
              Content of Tab Pane 3
            </Tabs.TabPane>
          </Tabs>
        </Variant>
        <Variant name="Tab Pane">
          <Tabs.TabPane></Tabs.TabPane>
        </Variant>
      </Component>
      <Component name="Collapse">
        <Variant>
          <Collapse defaultActiveKey="1">
            <Collapse.Panel
              header="This is panel header 1"
              key="1"
            ></Collapse.Panel>
            <Collapse.Panel
              header="This is panel header 2"
              key="2"
            ></Collapse.Panel>
            <Collapse.Panel
              header="This is panel header 3"
              key="3"
            ></Collapse.Panel>
          </Collapse>
        </Variant>
      </Component>
      <Component name="Image">
        <Variant>
          <Image width={200} src="" />
        </Variant>
      </Component>
      <Component name="Avatar">
        <Variant>
          <Avatar icon={<UserOutlined />} />
        </Variant>
        <Variant name="Image">
          <Avatar src="https://joeschmoe.io/api/v1/random" />
        </Variant>
      </Component>
      <Component name="Badge">
        <Variant>
          <Badge count={1}></Badge>
        </Variant>
      </Component>
      <Component name="Statistic">
        <Variant>
          <Statistic title="Title" value={112893} />
        </Variant>
      </Component>
      <Component name="Alert">
        <Variant name="Success">
          <Alert message="Text" type="success" />
        </Variant>
        <Variant name="Info">
          <Alert message="Text" type="info" />
        </Variant>
        <Variant name="Warning">
          <Alert message="Text" type="warning" />
        </Variant>
        <Variant name="Error">
          <Alert message="Text" type="error" />
        </Variant>
      </Component>
      <Component name="List">
        <Variant>
          <List
            bordered
            dataSource={[]}
            renderItem={item => <List.Item></List.Item>}
          />
        </Variant>
      </Component>
    </Category>
    <Category name="Icons">
      <Component name="Arrow">
        <Variant name="Up">
          <ArrowUpOutlined />
        </Variant>
        <Variant name="Down">
          <ArrowDownOutlined />
        </Variant>
        <Variant name="Left">
          <ArrowLeftOutlined />
        </Variant>
        <Variant name="Right">
          <ArrowRightOutlined />
        </Variant>
      </Component>
      <Component name="Question">
        <Variant>
          <QuestionOutlined />
        </Variant>
        <Variant name="Circle">
          <QuestionCircleOutlined />
        </Variant>
      </Component>
      <Component name="Plus">
        <Variant>
          <PlusOutlined />
        </Variant>
        <Variant name="Circle">
          <PlusCircleOutlined />
        </Variant>
      </Component>
      <Component name="Info">
        <Variant>
          <InfoOutlined />
        </Variant>
        <Variant name="Circle">
          <InfoCircleOutlined />
        </Variant>
      </Component>
      <Component name="Exclamation">
        <Variant>
          <ExclamationOutlined />
        </Variant>
        <Variant name="Circle">
          <ExclamationCircleOutlined />
        </Variant>
      </Component>
      <Component name="Close">
        <Variant>
          <CloseOutlined />
        </Variant>
        <Variant name="Circle">
          <CloseCircleOutlined />
        </Variant>
      </Component>
      <Component name="Check">
        <Variant>
          <CheckOutlined />
        </Variant>
        <Variant name="Circle">
          <CheckCircleOutlined />
        </Variant>
      </Component>
      <Component name="Edit">
        <Variant>
          <EditOutlined />
        </Variant>
      </Component>
      <Component name="Copy">
        <Variant>
          <CopyOutlined />
        </Variant>
      </Component>
      <Component name="Delete">
        <Variant>
          <DeleteOutlined />
        </Variant>
      </Component>
      <Component name="Bars">
        <Variant>
          <BarsOutlined />
        </Variant>
      </Component>
      <Component name="Bell">
        <Variant>
          <BellOutlined />
        </Variant>
      </Component>
      <Component name="Clear">
        <Variant>
          <ClearOutlined />
        </Variant>
      </Component>
      <Component name="Download">
        <Variant>
          <DownloadOutlined />
        </Variant>
      </Component>
      <Component name="Upload">
        <Variant>
          <UploadOutlined />
        </Variant>
      </Component>
      <Component name="Sync">
        <Variant>
          <SyncOutlined />
        </Variant>
      </Component>
      <Component name="Save">
        <Variant>
          <SaveOutlined />
        </Variant>
      </Component>
      <Component name="Search">
        <Variant>
          <SearchOutlined />
        </Variant>
      </Component>
      <Component name="Settings">
        <Variant>
          <SettingOutlined />
        </Variant>
      </Component>
      <Component name="Paperclip">
        <Variant>
          <PaperClipOutlined />
        </Variant>
      </Component>
      <Component name="Phone">
        <Variant>
          <PhoneOutlined />
        </Variant>
      </Component>
      <Component name="Mail">
        <Variant>
          <MailOutlined />
        </Variant>
      </Component>
      <Component name="Home">
        <Variant>
          <HomeOutlined />
        </Variant>
      </Component>
      <Component name="Contacts">
        <Variant>
          <ContactsOutlined />
        </Variant>
      </Component>
      <Component name="User">
        <Variant>
          <UserOutlined />
        </Variant>
        <Variant name="Add">
          <UserAddOutlined />
        </Variant>
        <Variant name="Remove">
          <UserDeleteOutlined />
        </Variant>
      </Component>
      <Component name="Team">
        <Variant>
          <TeamOutlined />
        </Variant>
      </Component>
    </Category>
    <Category name="Screens">
      <Component name="ExampleCustomScreen">
        <Variant>
          <ExampleCustomScreen />
        </Variant>
      </Component>
      <Component name="CustomEntityFilterTest">
        <Variant>
          <CustomEntityFilterTest />
        </Variant>
      </Component>
      <Component name="CustomFormControls">
        <Variant>
          <CustomFormControls />
        </Variant>
      </Component>
      <Component name="CustomDataDisplayComponents">
        <Variant>
          <CustomDataDisplayComponents />
        </Variant>
      </Component>
      <Component name="CustomAppLayouts">
        <Variant>
          <CustomAppLayouts />
        </Variant>
      </Component>
      <Component name="CustomControls">
        <Variant>
          <CustomControls />
        </Variant>
      </Component>
      <Component name="ErrorBoundaryTests">
        <Variant>
          <ErrorBoundaryTests />
        </Variant>
      </Component>
      <Component name="TestBlankScreen">
        <Variant>
          <TestBlankScreen />
        </Variant>
      </Component>
      <Component name="CarEditor">
        <Variant>
          <CarEditor />
        </Variant>
      </Component>
      <Component name="CarBrowserCards">
        <Variant>
          <CarBrowserCards />
        </Variant>
      </Component>
      <Component name="CarBrowserList">
        <Variant>
          <CarBrowserList />
        </Variant>
      </Component>
      <Component name="CarBrowserTable">
        <Variant>
          <CarBrowserTable />
        </Variant>
      </Component>
      <Component name="CarCardsGrid">
        <Variant>
          <CarCardsGrid />
        </Variant>
      </Component>
      <Component name="FavoriteCars">
        <Variant>
          <FavoriteCars />
        </Variant>
      </Component>
      <Component name="CarCardsWithDetails">
        <Variant>
          <CarCardsWithDetails />
        </Variant>
      </Component>
      <Component name="CarTableWithFilters">
        <Variant>
          <CarTableWithFilters />
        </Variant>
      </Component>
      <Component name="CarMasterDetail">
        <Variant>
          <CarMasterDetail />
        </Variant>
      </Component>
      <Component name="FormWizardCompositionO2O">
        <Variant>
          <FormWizardCompositionO2O />
        </Variant>
      </Component>
      <Component name="FormWizardEditor">
        <Variant>
          <FormWizardEditor />
        </Variant>
      </Component>
      <Component name="FormWizardBrowserTable">
        <Variant>
          <FormWizardBrowserTable />
        </Variant>
      </Component>
      <Component name="CarMultiSelectionTable">
        <Variant>
          <CarMultiSelectionTable />
        </Variant>
      </Component>
      <Component name="DatatypesTestEditor">
        <Variant>
          <DatatypesTestEditor />
        </Variant>
      </Component>
      <Component name="DatatypesTestBrowserCards">
        <Variant>
          <DatatypesTestBrowserCards />
        </Variant>
      </Component>
      <Component name="DatatypesTestBrowserList">
        <Variant>
          <DatatypesTestBrowserList />
        </Variant>
      </Component>
      <Component name="DatatypesTestBrowserTable">
        <Variant>
          <DatatypesTestBrowserTable />
        </Variant>
      </Component>
      <Component name="DatatypesTestCards">
        <Variant>
          <DatatypesTestCards />
        </Variant>
      </Component>
      <Component name="AssociationO2OEditor">
        <Variant>
          <AssociationO2OEditor />
        </Variant>
      </Component>
      <Component name="AssociationO2OBrowserTable">
        <Variant>
          <AssociationO2OBrowserTable />
        </Variant>
      </Component>
      <Component name="AssociationO2MEditor">
        <Variant>
          <AssociationO2MEditor />
        </Variant>
      </Component>
      <Component name="AssociationO2MBrowserTable">
        <Variant>
          <AssociationO2MBrowserTable />
        </Variant>
      </Component>
      <Component name="AssociationM2OEditor">
        <Variant>
          <AssociationM2OEditor />
        </Variant>
      </Component>
      <Component name="AssociationM2OBrowserTable">
        <Variant>
          <AssociationM2OBrowserTable />
        </Variant>
      </Component>
      <Component name="AssociationM2MEditor">
        <Variant>
          <AssociationM2MEditor />
        </Variant>
      </Component>
      <Component name="AssociationM2MBrowserTable">
        <Variant>
          <AssociationM2MBrowserTable />
        </Variant>
      </Component>
      <Component name="CompositionO2OEditor">
        <Variant>
          <CompositionO2OEditor />
        </Variant>
      </Component>
      <Component name="CompositionO2OBrowserTable">
        <Variant>
          <CompositionO2OBrowserTable />
        </Variant>
      </Component>
      <Component name="CompositionO2MEditor">
        <Variant>
          <CompositionO2MEditor />
        </Variant>
      </Component>
      <Component name="CompositionO2MBrowserTable">
        <Variant>
          <CompositionO2MBrowserTable />
        </Variant>
      </Component>
      <Component name="DeeplyNestedTestEntityEditor">
        <Variant>
          <DeeplyNestedTestEntityEditor />
        </Variant>
      </Component>
      <Component name="DeeplyNestedO2MTestEntityTable">
        <Variant>
          <DeeplyNestedO2MTestEntityTable />
        </Variant>
      </Component>
      <Component name="DeeplyNestedO2MTestEntityEditor">
        <Variant>
          <DeeplyNestedO2MTestEntityEditor />
        </Variant>
      </Component>
      <Component name="IntIdEditor">
        <Variant>
          <IntIdEditor />
        </Variant>
      </Component>
      <Component name="IntIdBrowserTable">
        <Variant>
          <IntIdBrowserTable />
        </Variant>
      </Component>
      <Component name="IntIdBrowserCards">
        <Variant>
          <IntIdBrowserCards />
        </Variant>
      </Component>
      <Component name="IntIdBrowserList">
        <Variant>
          <IntIdBrowserList />
        </Variant>
      </Component>
      <Component name="IntIdentityIdCards">
        <Variant>
          <IntIdentityIdCards />
        </Variant>
      </Component>
      <Component name="IntIdentityIdEditor">
        <Variant>
          <IntIdentityIdEditor />
        </Variant>
      </Component>
      <Component name="IntIdentityIdBrowserTable">
        <Variant>
          <IntIdentityIdBrowserTable />
        </Variant>
      </Component>
      <Component name="IntIdentityIdBrowserCards">
        <Variant>
          <IntIdentityIdBrowserCards />
        </Variant>
      </Component>
      <Component name="IntIdentityIdBrowserList">
        <Variant>
          <IntIdentityIdBrowserList />
        </Variant>
      </Component>
      <Component name="StringIdCards">
        <Variant>
          <StringIdCards />
        </Variant>
      </Component>
      <Component name="StringIdMgtCardsEdit">
        <Variant>
          <StringIdMgtCardsEdit />
        </Variant>
      </Component>
      <Component name="StringIdBrowserCards">
        <Variant>
          <StringIdBrowserCards />
        </Variant>
      </Component>
      <Component name="StringIdBrowserList">
        <Variant>
          <StringIdBrowserList />
        </Variant>
      </Component>
      <Component name="StringIdBrowserTable">
        <Variant>
          <StringIdBrowserTable />
        </Variant>
      </Component>
      <Component name="WeirdStringIdEditor">
        <Variant>
          <WeirdStringIdEditor />
        </Variant>
      </Component>
      <Component name="WeirdStringIdBrowserCards">
        <Variant>
          <WeirdStringIdBrowserCards />
        </Variant>
      </Component>
      <Component name="WeirdStringIdBrowserList">
        <Variant>
          <WeirdStringIdBrowserList />
        </Variant>
      </Component>
      <Component name="WeirdStringIdBrowserTable">
        <Variant>
          <WeirdStringIdBrowserTable />
        </Variant>
      </Component>
      <Component name="BoringStringIdEditor">
        <Variant>
          <BoringStringIdEditor />
        </Variant>
      </Component>
      <Component name="BoringStringIdBrowserTable">
        <Variant>
          <BoringStringIdBrowserTable />
        </Variant>
      </Component>
      <Component name="TrickyIdEditor">
        <Variant>
          <TrickyIdEditor />
        </Variant>
      </Component>
      <Component name="TrickyIdBrowserTable">
        <Variant>
          <TrickyIdBrowserTable />
        </Variant>
      </Component>
    </Category>
  </Palette>
)
Example #18
Source File: index.tsx    From jetlinks-ui-antd with MIT License 4 votes vote down vote up
Visualization: React.FC<Props> = props => {

    const [addItem, setAddItem] = useState(false);
    const [edit, setEdit] = useState<boolean>(false);

    const [layout, setLayout] = useState<any>([]);
    const [current, setCurrent] = useState<any>();

    const removeCard = (item: any) => {
        const temp = layout.findIndex((it: any) => item.i === it.i);
        layout.splice(temp, 1);
        setLayout([...layout]);
    }

    const tempRef = useRef<any>();


    useEffect(() => {
        let subscription: any;
        apis.visualization.getLayout({
            target: props.target,
            type: props.type
        }).then(response => {
            if (response.status === 200) {

                const currentLayout = response.result.metadata === "" ? [] : JSON.parse(response.result.metadata);
                subscription = rxjs.from(currentLayout).pipe(map((item: any) => {
                    const temp = item;
                    const tempProps = {
                        // item: item,
                        ready: (onReady: Function) => {
                            temp.doReady = onReady;
                        },
                        ref: tempRef,
                        onLoad: () => tempRef.current.onLoad(),
                        edit: (doEdit: Function) => {
                            temp.doEdit = doEdit;
                        },
                        complate: {},
                        loading: {},
                        hide: {},
                        onEvent: {},
                        id: item.i,
                    };
                    temp.props = tempProps;
                    return item;
                }))
                    .pipe(toArray())
                    .subscribe(
                        item => {
                            // console.log(item, 'tiem')
                            setLayout(item);
                        },
                        () => { message.error('error') },
                        () => { }
                    )
            }
        }).catch(() => {
            message.error('加载数据错误');
            setLayout([]);
        })

        return () => subscription && subscription.unsubscribe();
    }, []);


    const saveLayout = () => {

        apis.visualization.saveOrUpdate({
            metadata: JSON.stringify(layout),
            type: props.type,
            target: props.target,
            name: props.name,
            id: `${props.type}:${props.target}`
        } as VisualizationItem).then(response => {
            if (response.status === 200) {
                message.success('保存成功');
            }
        }).catch(() => {
            message.error('保存失败!');
        })
    }

    const layoutChange = (currnetLayout: any[]) => {
        const newLayout = layout.map((item: any) => {
            const temp = currnetLayout.find(i => i.i === item.i);
            return { ...item, ...temp, };
        });
        setLayout(newLayout);
    }


    const saveLayoutItem = (item: any) => {
        const id = randomString(8);
        if (current) {
            const index = layout.findIndex((i: any) => i.i === current.i);
            current.config = item;
            layout[index] = current;
            setLayout(layout);
        } else {
            setLayout([{
                i: id,
                x: 0,
                y: Infinity,
                config: item,
                h: 5,
                w: 5,
            }, ...layout]);
        }
        setAddItem(false);
    }

    const renderGridLayout = () =>
        (
            <>
                <ReactGridLayout
                    onLayoutChange={(item: any) => {
                        layoutChange(item)
                    }}
                    // cols={{ md: 12 }}
                    isResizable={edit}
                    isDraggable={edit}
                    onDragStop={() => {
                        setLayout([...layout])
                    }}
                    onResizeStop={() => {
                        setLayout([...layout])
                    }}
                    className="layout"
                    layout={layout}
                    rowHeight={30}
                >
                    {layout.map((item: any) => (
                        <Card
                            style={{ overflow: "hidden" }}
                            key={item.i}
                            id={item.i}
                        >

                            <div style={{ position: 'absolute', right: 15, top: 5, }}>
                                <div style={{ float: 'right' }}>
                                    <Fragment>
                                        {edit && (
                                            <>
                                                <Tooltip title="删除">
                                                    <CloseCircleOutlined onClick={() => removeCard(item)} />
                                                </Tooltip>
                                                <Divider type="vertical" />
                                                <Tooltip title="编辑">
                                                    <EditOutlined onClick={() => {
                                                        setAddItem(true);
                                                        setCurrent(item)
                                                    }} />
                                                </Tooltip>
                                            </>)}
                                        {item.doReady &&
                                            <>
                                                <Divider type="vertical" />
                                                <Tooltip title="刷新">
                                                    <SyncOutlined onClick={() => { item.doReady() }} />
                                                </Tooltip>

                                            </>}
                                    </Fragment>
                                </div>
                            </div>
                            <GridCard
                                {...item}
                                productId={props.productId}
                                deviceId={props.target} />
                        </Card>))}

                </ReactGridLayout>
            </>
        )


    return (
        <>
            {layout.length > 0 ? renderGridLayout() : (
                <Button
                    style={{ width: '300px', height: '200px' }}
                    type="dashed"
                    onClick={() => {
                        setCurrent(undefined);
                        setEdit(true)
                        setAddItem(true);
                    }}
                >
                    <Icon type="plus" />
                   新增
                </Button>
            )}

            <div className={styles.optionGroup}>
                {edit ?
                    <div style={{ float: 'right' }}>

                        <Tooltip title="新增" style={{ float: 'right' }}>
                            <Button
                                type="danger"
                                shape="circle"
                                size="large"
                                onClick={() => {
                                    setCurrent(undefined);
                                    setAddItem(true)
                                }}
                            >
                                <PlusOutlined />
                            </Button>
                        </Tooltip>
                        <div style={{ float: 'right', marginLeft: 10 }}>
                            <Tooltip title="保存" >
                                <Button
                                    type="primary"
                                    shape="circle"
                                    size="large"
                                    onClick={() => {
                                        setEdit(false);
                                        saveLayout();
                                    }}
                                >
                                    <SaveOutlined />
                                </Button>
                            </Tooltip>
                        </div>
                    </div> :
                    <div style={{ textAlign: 'center' }}>
                        <Tooltip title="编辑" >
                            <Button
                                type="danger"
                                shape="circle"
                                size="large"
                                onClick={() => setEdit(true)}
                            >
                                <EditOutlined />
                            </Button>
                        </Tooltip>
                    </div>
                }
            </div>
            {addItem && (
                <AddItem
                    close={() => { setAddItem(false) }}
                    metaData={props.metaData}
                    current={current}
                    save={(item: any) => {
                        saveLayoutItem(item);
                    }}
                />
            )}
        </ >

    )
}
Example #19
Source File: MixedArguments.tsx    From yugong with MIT License 4 votes vote down vote up
Mixedarguments: React.FC<Props> = ({ typeArguments, onChange, className }) => {
  const [jsonData, setJsonData] = useState<AppDataLayoutItemTypes>();
  const [jsonMode, setJsonMode] = useState<'view' | 'code'>('view');

  useEffect(() => {
    const result = cloneDeep(typeArguments);
    setJsonData(result.data || {});
  }, [typeArguments]);

  const jsoneditor = useRef<JSONEditor>();
  const container = useRef<any>();

  const onsubmit = useCallback(() => {
    try {
      var json = jsoneditor.current?.get();
      if (json && onChange instanceof Function) {
        const result = cloneDeep(typeArguments);
        result.data = json;
        jsoneditor.current?.setMode('view');
        setJsonMode('view');
        onChange(result);
        message.success(`${typeArguments.name}已更新!`);
      }
    } catch (e) {
      message.error('保存失败!JSON数据格式不正确');
      return;
    }
  }, [onChange, typeArguments]);

  useEffect(() => {
    if (container.current && jsonData) {
      jsoneditor.current = new JSONEditor(container.current, {
        mode: jsonMode,
        mainMenuBar: false,
      });
      jsoneditor.current.set(jsonData);
    }

    return () => {
      if (jsoneditor.current) {
        jsoneditor.current.destroy();
      }
    };
  }, [jsonData, jsonMode]);

  const onChangeJsonMode = useCallback((e) => {
    try {
      var json = jsoneditor.current?.get();
      if (json) {
        jsoneditor.current?.setMode('code');
        setJsonMode('code');
      }
    } catch (error) {
      console.error(error);
    }
  }, []);

  return (
    <div className={classNames(className)}>
      <div className={s.toolbar} >
        <div>
          &nbsp;
          <CopyToClipboard
            text={JSON.stringify(jsonData)}
            onCopy={() => message.info('已复制到剪切板')}
          >
            <Button size="small" icon={<CopyOutlined alt="复制到剪切板" />}>
              复制
            </Button>
          </CopyToClipboard>
          &nbsp;
          {jsonMode === 'view' ? (
            <Button
              size="small"
              type="primary"
              onClick={onChangeJsonMode}
              icon={<FormOutlined alt="编辑JSON" />}
            >
              编辑
            </Button>
          ) : null}
          {jsonMode === 'code' ? (
            <Button
              size="small"
              type="primary"
              onClick={onsubmit}
              icon={<SaveOutlined alt="保存JSON" />}
            >
              保存
            </Button>
          ) : null}
          &nbsp;
        </div>
      </div>
      <div className={s.wrap} ref={container} />
    </div>
  );
}
Example #20
Source File: FunctionDebuggerToolbar.tsx    From next-basics with GNU General Public License v3.0 4 votes vote down vote up
export function FunctionDebuggerToolbar({
  type,
  status,
  saveDisabled,
  onButtonClick,
}: FunctionDebuggerToolbarProps): React.ReactElement {
  const refinedType = type ?? "input";
  const isInput = refinedType === "input" || refinedType === "test-input";

  const handleRunClick = useCallback(() => {
    onButtonClick?.({ action: "run" });
  }, [onButtonClick]);

  const handleSaveClick = useCallback(() => {
    if (!saveDisabled) {
      onButtonClick?.({ action: "save" });
    }
  }, [onButtonClick, saveDisabled]);

  const handleDeleteClick = useCallback(() => {
    onButtonClick?.({ action: "delete" });
  }, [onButtonClick]);

  return (
    <div
      className={classNames(
        styles.debuggerToolbar,
        status && styles[status],
        refinedType === "input" || refinedType === "output"
          ? styles.debug
          : styles.test,
        isInput ? styles.input : styles.output
      )}
      data-override-theme="dark"
    >
      <div className={styles.header}>
        {refinedType === "input"
          ? "Input"
          : refinedType === "test-input"
          ? "Test Input"
          : refinedType === "test-output"
          ? "Expect Output"
          : "Output"}
        {isInput && (
          <span className={styles.headerSuffix}>
            &nbsp;(argument list in JSON format)
          </span>
        )}
      </div>
      {isInput ? (
        <div className={styles.buttons}>
          <Tooltip title="Run">
            <div className={styles.debuggerButton} onClick={handleRunClick}>
              <PlayCircleOutlined />
            </div>
          </Tooltip>
          <Tooltip
            title={refinedType === "input" ? "Add as a test case" : "Update"}
          >
            <div
              className={classNames(styles.debuggerButton, {
                [styles.disabled]: saveDisabled,
              })}
              onClick={handleSaveClick}
            >
              {refinedType === "input" ? (
                <PlusCircleOutlined />
              ) : (
                <SaveOutlined />
              )}
            </div>
          </Tooltip>
          {refinedType === "test-input" && (
            <Tooltip title="Delete">
              <div
                className={styles.debuggerButton}
                onClick={handleDeleteClick}
              >
                <DeleteOutlined />
              </div>
            </Tooltip>
          )}
        </div>
      ) : (
        refinedType === "test-output" && (
          <div className={styles.secondHeader}>
            {status === "ok" ? (
              <>
                <span className={styles.secondHeaderIcon}>
                  <CheckOutlined />
                </span>
                <span>Test: passed</span>
              </>
            ) : status === "failed" ? (
              <>
                <span className={styles.secondHeaderIcon}>
                  <CloseOutlined />
                </span>
                <span>Test: failed</span>
              </>
            ) : (
              <>
                <span className={styles.secondHeaderIcon}>
                  <QuestionOutlined />
                </span>
                <span>Test: expired</span>
              </>
            )}
          </div>
        )
      )}
    </div>
  );
}
Example #21
Source File: Icon.tsx    From html2sketch with MIT License 4 votes vote down vote up
IconSymbol: FC = () => {
  return (
    <Row>
      {/*<CaretUpOutlined*/}
      {/*  className="icon"*/}
      {/*  symbolName={'1.General/2.Icons/1.CaretUpOutlined'}*/}
      {/*/>*/}
      {/*  className="icon"*/}
      {/*  symbolName={'1.General/2.Icons/2.MailOutlined'}*/}
      {/*/>*/}
      {/*<StepBackwardOutlined*/}
      {/*  className="icon"*/}
      {/*  symbolName={'1.General/2.Icons/2.StepBackwardOutlined'}*/}
      {/*/>*/}
      {/*<StepForwardOutlined*/}
      {/*  className="icon"*/}
      {/*  symbolName={'1.General/2.Icons/2.StepBackwardOutlined'}*/}
      {/*/>*/}
      <StepForwardOutlined />
      <ShrinkOutlined />
      <ArrowsAltOutlined />
      <DownOutlined />
      <UpOutlined />
      <LeftOutlined />
      <RightOutlined />
      <CaretUpOutlined />
      <CaretDownOutlined />
      <CaretLeftOutlined />
      <CaretRightOutlined />
      <VerticalAlignTopOutlined />
      <RollbackOutlined />
      <FastBackwardOutlined />
      <FastForwardOutlined />
      <DoubleRightOutlined />
      <DoubleLeftOutlined />
      <VerticalLeftOutlined />
      <VerticalRightOutlined />
      <VerticalAlignMiddleOutlined />
      <VerticalAlignBottomOutlined />
      <ForwardOutlined />
      <BackwardOutlined />
      <EnterOutlined />
      <RetweetOutlined />
      <SwapOutlined />
      <SwapLeftOutlined />
      <SwapRightOutlined />
      <ArrowUpOutlined />
      <ArrowDownOutlined />
      <ArrowLeftOutlined />
      <ArrowRightOutlined />
      <LoginOutlined />
      <LogoutOutlined />
      <MenuFoldOutlined />
      <MenuUnfoldOutlined />
      <BorderBottomOutlined />
      <BorderHorizontalOutlined />
      <BorderInnerOutlined />
      <BorderOuterOutlined />
      <BorderLeftOutlined />
      <BorderRightOutlined />
      <BorderTopOutlined />
      <BorderVerticleOutlined />
      <PicCenterOutlined />
      <PicLeftOutlined />
      <PicRightOutlined />
      <RadiusBottomleftOutlined />
      <RadiusBottomrightOutlined />
      <RadiusUpleftOutlined />
      <RadiusUprightOutlined />
      <FullscreenOutlined />
      <FullscreenExitOutlined />
      <QuestionOutlined />
      <PauseOutlined />
      <MinusOutlined />
      <PauseCircleOutlined />
      <InfoOutlined />
      <CloseOutlined />
      <ExclamationOutlined />
      <CheckOutlined />
      <WarningOutlined />
      <IssuesCloseOutlined />
      <StopOutlined />
      <EditOutlined />
      <CopyOutlined />
      <ScissorOutlined />
      <DeleteOutlined />
      <SnippetsOutlined />
      <DiffOutlined />
      <HighlightOutlined />
      <AlignCenterOutlined />
      <AlignLeftOutlined />
      <AlignRightOutlined />
      <BgColorsOutlined />
      <BoldOutlined />
      <ItalicOutlined />
      <UnderlineOutlined />
      <StrikethroughOutlined />
      <RedoOutlined />
      <UndoOutlined />
      <ZoomInOutlined />
      <ZoomOutOutlined />
      <FontColorsOutlined />
      <FontSizeOutlined />
      <LineHeightOutlined />
      <SortAscendingOutlined />
      <SortDescendingOutlined />
      <DragOutlined />
      <OrderedListOutlined />
      <UnorderedListOutlined />
      <RadiusSettingOutlined />
      <ColumnWidthOutlined />
      <ColumnHeightOutlined />
      <AreaChartOutlined />
      <PieChartOutlined />
      <BarChartOutlined />
      <DotChartOutlined />
      <LineChartOutlined />
      <RadarChartOutlined />
      <HeatMapOutlined />
      <FallOutlined />
      <RiseOutlined />
      <StockOutlined />
      <BoxPlotOutlined />
      <FundOutlined />
      <SlidersOutlined />
      <AndroidOutlined />
      <AppleOutlined />
      <WindowsOutlined />
      <IeOutlined />
      <ChromeOutlined />
      <GithubOutlined />
      <AliwangwangOutlined />
      <DingdingOutlined />
      <WeiboSquareOutlined />
      <WeiboCircleOutlined />
      <TaobaoCircleOutlined />
      <Html5Outlined />
      <WeiboOutlined />
      <TwitterOutlined />
      <WechatOutlined />
      <AlipayCircleOutlined />
      <TaobaoOutlined />
      <SkypeOutlined />
      <FacebookOutlined />
      <CodepenOutlined />
      <CodeSandboxOutlined />
      <AmazonOutlined />
      <GoogleOutlined />
      <AlipayOutlined />
      <AntDesignOutlined />
      <AntCloudOutlined />
      <ZhihuOutlined />
      <SlackOutlined />
      <SlackSquareOutlined />
      <BehanceSquareOutlined />
      <DribbbleOutlined />
      <DribbbleSquareOutlined />
      <InstagramOutlined />
      <YuqueOutlined />
      <AlibabaOutlined />
      <YahooOutlined />
      <RedditOutlined />
      <SketchOutlined />
      <AccountBookOutlined />
      <AlertOutlined />
      <ApartmentOutlined />
      <ApiOutlined />
      <QqOutlined />
      <MediumWorkmarkOutlined />
      <GitlabOutlined />
      <MediumOutlined />
      <GooglePlusOutlined />
      <AppstoreAddOutlined />
      <AppstoreOutlined />
      <AudioOutlined />
      <AudioMutedOutlined />
      <AuditOutlined />
      <BankOutlined />
      <BarcodeOutlined />
      <BarsOutlined />
      <BellOutlined />
      <BlockOutlined />
      <BookOutlined />
      <BorderOutlined />
      <BranchesOutlined />
      <BuildOutlined />
      <BulbOutlined />
      <CalculatorOutlined />
      <CalendarOutlined />
      <CameraOutlined />
      <CarOutlined />
      <CarryOutOutlined />
      <CiCircleOutlined />
      <CiOutlined />
      <CloudOutlined />
      <ClearOutlined />
      <ClusterOutlined />
      <CodeOutlined />
      <CoffeeOutlined />
      <CompassOutlined />
      <CompressOutlined />
      <ContactsOutlined />
      <ContainerOutlined />
      <ControlOutlined />
      <CopyrightCircleOutlined />
      <CopyrightOutlined />
      <CreditCardOutlined />
      <CrownOutlined />
      <CustomerServiceOutlined />
      <DashboardOutlined />
      <DatabaseOutlined />
      <DeleteColumnOutlined />
      <DeleteRowOutlined />
      <DisconnectOutlined />
      <DislikeOutlined />
      <DollarCircleOutlined />
      <DollarOutlined />
      <DownloadOutlined />
      <EllipsisOutlined />
      <EnvironmentOutlined />
      <EuroCircleOutlined />
      <EuroOutlined />
      <ExceptionOutlined />
      <ExpandAltOutlined />
      <ExpandOutlined />
      <ExperimentOutlined />
      <ExportOutlined />
      <EyeOutlined />
      <FieldBinaryOutlined />
      <FieldNumberOutlined />
      <FieldStringOutlined />
      <DesktopOutlined />
      <DingtalkOutlined />
      <FileAddOutlined />
      <FileDoneOutlined />
      <FileExcelOutlined />
      <FileExclamationOutlined />
      <FileOutlined />
      <FileImageOutlined />
      <FileJpgOutlined />
      <FileMarkdownOutlined />
      <FilePdfOutlined />
      <FilePptOutlined />
      <FileProtectOutlined />
      <FileSearchOutlined />
      <FileSyncOutlined />
      <FileTextOutlined />
      <FileUnknownOutlined />
      <FileWordOutlined />
      <FilterOutlined />
      <FireOutlined />
      <FlagOutlined />
      <FolderAddOutlined />
      <FolderOutlined />
      <FolderOpenOutlined />
      <ForkOutlined />
      <FormatPainterOutlined />
      <FrownOutlined />
      <FunctionOutlined />
      <FunnelPlotOutlined />
      <GatewayOutlined />
      <GifOutlined />
      <GiftOutlined />
      <GlobalOutlined />
      <GoldOutlined />
      <GroupOutlined />
      <HddOutlined />
      <HeartOutlined />
      <HistoryOutlined />
      <HomeOutlined />
      <HourglassOutlined />
      <IdcardOutlined />
      <ImportOutlined />
      <InboxOutlined />
      <InsertRowAboveOutlined />
      <InsertRowBelowOutlined />
      <InsertRowLeftOutlined />
      <InsertRowRightOutlined />
      <InsuranceOutlined />
      <InteractionOutlined />
      <KeyOutlined />
      <LaptopOutlined />
      <LayoutOutlined />
      <LikeOutlined />
      <LineOutlined />
      <LinkOutlined />
      <Loading3QuartersOutlined />
      <LoadingOutlined />
      <LockOutlined />
      <MailOutlined />
      <ManOutlined />
      <MedicineBoxOutlined />
      <MehOutlined />
      <MenuOutlined />
      <MergeCellsOutlined />
      <MessageOutlined />
      <MobileOutlined />
      <MoneyCollectOutlined />
      <MonitorOutlined />
      <MoreOutlined />
      <NodeCollapseOutlined />
      <NodeExpandOutlined />
      <NodeIndexOutlined />
      <NotificationOutlined />
      <NumberOutlined />
      <PaperClipOutlined />
      <PartitionOutlined />
      <PayCircleOutlined />
      <PercentageOutlined />
      <PhoneOutlined />
      <PictureOutlined />
      <PoundCircleOutlined />
      <PoundOutlined />
      <PoweroffOutlined />
      <PrinterOutlined />
      <ProfileOutlined />
      <ProjectOutlined />
      <PropertySafetyOutlined />
      <PullRequestOutlined />
      <PushpinOutlined />
      <QrcodeOutlined />
      <ReadOutlined />
      <ReconciliationOutlined />
      <RedEnvelopeOutlined />
      <ReloadOutlined />
      <RestOutlined />
      <RobotOutlined />
      <RocketOutlined />
      <SafetyCertificateOutlined />
      <SafetyOutlined />
      <ScanOutlined />
      <ScheduleOutlined />
      <SearchOutlined />
      <SecurityScanOutlined />
      <SelectOutlined />
      <SendOutlined />
      <SettingOutlined />
      <ShakeOutlined />
      <ShareAltOutlined />
      <ShopOutlined />
      <ShoppingCartOutlined />
      <ShoppingOutlined />
      <SisternodeOutlined />
      <SkinOutlined />
      <SmileOutlined />
      <SolutionOutlined />
      <SoundOutlined />
      <SplitCellsOutlined />
      <StarOutlined />
      <SubnodeOutlined />
      <SyncOutlined />
      <TableOutlined />
      <TabletOutlined />
      <TagOutlined />
      <TagsOutlined />
      <TeamOutlined />
      <ThunderboltOutlined />
      <ToTopOutlined />
      <ToolOutlined />
      <TrademarkCircleOutlined />
      <TrademarkOutlined />
      <TransactionOutlined />
      <TrophyOutlined />
      <UngroupOutlined />
      <UnlockOutlined />
      <UploadOutlined />
      <UsbOutlined />
      <UserAddOutlined />
      <UserDeleteOutlined />
      <UserOutlined />
      <UserSwitchOutlined />
      <UsergroupAddOutlined />
      <UsergroupDeleteOutlined />
      <VideoCameraOutlined />
      <WalletOutlined />
      <WifiOutlined />
      <BorderlessTableOutlined />
      <WomanOutlined />
      <BehanceOutlined />
      <DropboxOutlined />
      <DeploymentUnitOutlined />
      <UpCircleOutlined />
      <DownCircleOutlined />
      <LeftCircleOutlined />
      <RightCircleOutlined />
      <UpSquareOutlined />
      <DownSquareOutlined />
      <LeftSquareOutlined />
      <RightSquareOutlined />
      <PlayCircleOutlined />
      <QuestionCircleOutlined />
      <PlusCircleOutlined />
      <PlusSquareOutlined />
      <MinusSquareOutlined />
      <MinusCircleOutlined />
      <InfoCircleOutlined />
      <ExclamationCircleOutlined />
      <CloseCircleOutlined />
      <CloseSquareOutlined />
      <CheckCircleOutlined />
      <CheckSquareOutlined />
      <ClockCircleOutlined />
      <FormOutlined />
      <DashOutlined />
      <SmallDashOutlined />
      <YoutubeOutlined />
      <CodepenCircleOutlined />
      <AliyunOutlined />
      <PlusOutlined />
      <LinkedinOutlined />
      <AimOutlined />
      <BugOutlined />
      <CloudDownloadOutlined />
      <CloudServerOutlined />
      <CloudSyncOutlined />
      <CloudUploadOutlined />
      <CommentOutlined />
      <ConsoleSqlOutlined />
      <EyeInvisibleOutlined />
      <FileGifOutlined />
      <DeliveredProcedureOutlined />
      <FieldTimeOutlined />
      <FileZipOutlined />
      <FolderViewOutlined />
      <FundProjectionScreenOutlined />
      <FundViewOutlined />
      <MacCommandOutlined />
      <PlaySquareOutlined />
      <OneToOneOutlined />
      <RotateLeftOutlined />
      <RotateRightOutlined />
      <SaveOutlined />
      <SwitcherOutlined />
      <TranslationOutlined />
      <VerifiedOutlined />
      <VideoCameraAddOutlined />
      <WhatsAppOutlined />

      {/*</Col>*/}
    </Row>
  );
}
Example #22
Source File: EnabledPluginsSection.tsx    From posthog-foss with MIT License 4 votes vote down vote up
export function EnabledPluginSection(): JSX.Element {
    const { user } = useValues(userLogic)

    const {
        rearrange,
        setTemporaryOrder,
        cancelRearranging,
        savePluginOrders,
        makePluginOrderSaveable,
        toggleSectionOpen,
    } = useActions(pluginsLogic)

    const {
        enabledPlugins,
        filteredEnabledPlugins,
        sortableEnabledPlugins,
        unsortableEnabledPlugins,
        rearranging,
        loading,
        temporaryOrder,
        pluginOrderSaveable,
        searchTerm,
        sectionsOpen,
    } = useValues(pluginsLogic)

    const canRearrange: boolean = canConfigurePlugins(user?.organization) && sortableEnabledPlugins.length > 1

    const rearrangingButtons = rearranging ? (
        <>
            <Button
                type="primary"
                icon={<SaveOutlined />}
                loading={loading}
                onClick={(e) => {
                    e.stopPropagation()
                    savePluginOrders(temporaryOrder)
                }}
                disabled={!pluginOrderSaveable}
            >
                Save order
            </Button>
            <Button
                type="default"
                icon={<CloseOutlined />}
                onClick={(e) => {
                    cancelRearranging()
                    e.stopPropagation()
                }}
            >
                Cancel
            </Button>
        </>
    ) : (
        <Tooltip
            title={
                enabledPlugins.length <= 1 ? (
                    'At least two plugins need to be enabled for reordering.'
                ) : (
                    <>
                        {!!searchTerm ? (
                            'Editing the order of plugins is disabled when searching.'
                        ) : (
                            <>
                                Order matters because event processing with plugins works like a pipe: the event is
                                processed by every enabled plugin <b>in sequence</b>.
                            </>
                        )}
                    </>
                )
            }
            placement="top"
        >
            <Button
                icon={<OrderedListOutlined />}
                onClick={(e) => {
                    e.stopPropagation()
                    rearrange()
                }}
                disabled={!!searchTerm || sortableEnabledPlugins.length <= 1}
            >
                Edit order
            </Button>
        </Tooltip>
    )

    const onSortEnd = ({ oldIndex, newIndex }: { oldIndex: number; newIndex: number }): void => {
        if (oldIndex === newIndex) {
            return
        }

        const move = (arr: PluginTypeWithConfig[], from: number, to: number): { id: number; order: number }[] => {
            const clone = [...arr]
            Array.prototype.splice.call(clone, to, 0, Array.prototype.splice.call(clone, from, 1)[0])
            return clone.map(({ id }, order) => ({ id, order: order + 1 }))
        }

        const movedPluginId: number = enabledPlugins[oldIndex]?.id

        const newTemporaryOrder: Record<number, number> = {}
        for (const { id, order } of move(enabledPlugins, oldIndex, newIndex)) {
            newTemporaryOrder[id] = order
        }

        if (!rearranging) {
            rearrange()
        }
        setTemporaryOrder(newTemporaryOrder, movedPluginId)
    }

    const EnabledPluginsHeader = (): JSX.Element => (
        <div className="plugins-installed-tab-section-header" onClick={() => toggleSectionOpen(PluginSection.Enabled)}>
            <Subtitle
                subtitle={
                    <>
                        {sectionsOpen.includes(PluginSection.Enabled) ? <CaretDownOutlined /> : <CaretRightOutlined />}
                        {` Enabled plugins (${filteredEnabledPlugins.length})`}
                        {rearranging && sectionsOpen.includes(PluginSection.Enabled) && (
                            <Tag color="red" style={{ fontWeight: 'normal', marginLeft: 10 }}>
                                Reordering in progress
                            </Tag>
                        )}
                    </>
                }
                buttons={<Space>{sectionsOpen.includes(PluginSection.Enabled) && rearrangingButtons}</Space>}
            />
        </div>
    )

    if (enabledPlugins.length === 0) {
        return (
            <>
                <EnabledPluginsHeader />
                {sectionsOpen.includes(PluginSection.Enabled) && <p style={{ margin: 10 }}>No plugins enabled.</p>}
            </>
        )
    }

    return (
        <>
            <EnabledPluginsHeader />
            {sectionsOpen.includes(PluginSection.Enabled) && (
                <>
                    {sortableEnabledPlugins.length === 0 && unsortableEnabledPlugins.length === 0 && (
                        <p style={{ margin: 10 }}>No plugins match your search.</p>
                    )}
                    {canRearrange || rearranging ? (
                        <>
                            {sortableEnabledPlugins.length > 0 && (
                                <>
                                    <SortablePlugins
                                        useDragHandle
                                        onSortEnd={onSortEnd}
                                        onSortOver={makePluginOrderSaveable}
                                    >
                                        {sortableEnabledPlugins.map((plugin, index) => (
                                            <SortablePlugin
                                                key={plugin.id}
                                                plugin={plugin}
                                                index={index}
                                                order={index + 1}
                                                maxOrder={enabledPlugins.length}
                                                rearranging={rearranging}
                                            />
                                        ))}
                                    </SortablePlugins>
                                </>
                            )}
                        </>
                    ) : (
                        <Row gutter={16} style={{ marginTop: 16 }}>
                            {sortableEnabledPlugins.length > 0 && (
                                <>
                                    {sortableEnabledPlugins.map((plugin, index) => (
                                        <InstalledPlugin
                                            key={plugin.id}
                                            plugin={plugin}
                                            order={index + 1}
                                            maxOrder={filteredEnabledPlugins.length}
                                        />
                                    ))}
                                </>
                            )}
                        </Row>
                    )}
                    {unsortableEnabledPlugins.map((plugin) => (
                        <InstalledPlugin
                            key={plugin.id}
                            plugin={plugin}
                            maxOrder={enabledPlugins.length}
                            rearranging={rearranging}
                            unorderedPlugin
                        />
                    ))}
                </>
            )}
        </>
    )
}
Example #23
Source File: NewPropertyComponent.tsx    From posthog-foss with MIT License 4 votes vote down vote up
NewPropertyComponent = (): JSX.Element => {
    const initialState = { creating: false, propertyType: 'string' } as NewPropertyInterface
    const [state, setState] = useState(initialState)
    const { editProperty } = useActions(personsLogic)

    const saveProperty = (): void => {
        if (state.key && state.value !== undefined) {
            editProperty(state.key, state.value)
            setState(initialState)
        }
    }

    return (
        <>
            <div className="mb">
                <div className="text-right">
                    <Button
                        data-attr="add-prop-button"
                        onClick={() => setState({ ...state, creating: true })}
                        type="primary"
                        icon={<PlusOutlined />}
                    >
                        New property
                    </Button>
                </div>
            </div>
            <Modal
                visible={state.creating}
                destroyOnClose
                onCancel={() => setState(initialState)}
                title="Adding new property"
                okText={
                    <>
                        <SaveOutlined style={{ marginRight: 4 }} />
                        Save Property
                    </>
                }
                okButtonProps={{ disabled: !state.key || state.value === undefined }}
                onOk={saveProperty}
            >
                <div className="input-set">
                    <label htmlFor="propertyKey">Key</label>
                    <Input
                        id="propertyKey"
                        autoFocus
                        placeholder="try email, first_name, is_verified, membership_level, total_revenue"
                        onChange={(e) => setState({ ...state, key: e.target.value })}
                    />
                </div>
                <div className="input-set">
                    <label htmlFor="propertyType">Type of Property</label>
                    <div>
                        <Radio.Group
                            value={state.propertyType}
                            onChange={(e) =>
                                setState({
                                    ...state,
                                    propertyType: e.target.value,
                                    value: e.target.value === 'string' ? undefined : 'true',
                                })
                            }
                            id="propertyType"
                            buttonStyle="solid"
                        >
                            <Radio.Button value="string">Text or Number</Radio.Button>
                            <Radio.Button value="boolean">Boolean or Null</Radio.Button>
                        </Radio.Group>
                    </div>
                </div>

                <div className="input-set">
                    <label htmlFor="propertyValue">Value</label>
                    {state.propertyType === 'boolean' ? (
                        <div>
                            <Radio.Group
                                value={state.value}
                                onChange={(e) =>
                                    setState({
                                        ...state,
                                        value: e.target.value,
                                    })
                                }
                                id="propertyValue"
                                buttonStyle="solid"
                            >
                                <Radio.Button value="true" defaultChecked>
                                    <CheckOutlined /> True
                                </Radio.Button>
                                <Radio.Button value="false">
                                    <CloseOutlined /> False
                                </Radio.Button>
                                <Radio.Button value="null">
                                    <StopOutlined /> Null
                                </Radio.Button>
                            </Radio.Group>
                        </div>
                    ) : (
                        <Input
                            placeholder="try [email protected], gold, 1"
                            onChange={(e) => setState({ ...state, value: e.target.value })}
                            id="propertyValue"
                            onKeyDown={(e) => e.key === 'Enter' && saveProperty()}
                        />
                    )}
                </div>
            </Modal>
        </>
    )
}
Example #24
Source File: FeatureFlag.tsx    From posthog-foss with MIT License 4 votes vote down vote up
export function FeatureFlag(): JSX.Element {
    const [form] = Form.useForm()
    const {
        featureFlag,
        featureFlagId,
        multivariateEnabled,
        variants,
        nonEmptyVariants,
        areVariantRolloutsValid,
        variantRolloutSum,
        groupTypes,
        aggregationTargetName,
        taxonomicGroupTypes,
    } = useValues(featureFlagLogic)
    const {
        addConditionSet,
        updateConditionSet,
        removeConditionSet,
        duplicateConditionSet,
        saveFeatureFlag,
        deleteFeatureFlag,
        setMultivariateEnabled,
        addVariant,
        updateVariant,
        removeVariant,
        distributeVariantsEqually,
        setFeatureFlag,
        setAggregationGroupTypeIndex,
    } = useActions(featureFlagLogic)
    const { showGroupsOptions, aggregationLabel } = useValues(groupsModel)
    const { hasAvailableFeature, upgradeLink } = useValues(userLogic)

    // whether the key for an existing flag is being changed
    const [hasKeyChanged, setHasKeyChanged] = useState(false)
    // whether to warn the user that their variants will be lost
    const [showVariantDiscardWarning, setShowVariantDiscardWarning] = useState(false)

    useEffect(() => {
        form.setFieldsValue({ ...featureFlag })
    }, [featureFlag])

    // :KLUDGE: Match by select only allows Select.Option as children, so render groups option directly rather than as a child
    const matchByGroupsIntroductionOption = GroupsIntroductionOption({ value: -2 })

    return (
        <div className="feature-flag">
            {featureFlag ? (
                <Form
                    layout="vertical"
                    form={form}
                    initialValues={{ name: featureFlag.name, key: featureFlag.key, active: featureFlag.active }}
                    onValuesChange={(newValues) => {
                        if (featureFlagId !== 'new' && newValues.key) {
                            setHasKeyChanged(newValues.key !== featureFlag.key)
                        }
                        setFeatureFlag({ ...featureFlag, ...newValues })
                    }}
                    onFinish={(values) =>
                        saveFeatureFlag({
                            ...featureFlag,
                            ...values,
                            filters: featureFlag.filters,
                        })
                    }
                    requiredMark={false}
                    scrollToFirstError
                >
                    <PageHeader
                        title="Feature Flag"
                        buttons={
                            <div style={{ display: 'flex' }}>
                                <Form.Item className="enabled-switch">
                                    <Form.Item
                                        shouldUpdate={(prevValues, currentValues) =>
                                            prevValues.active !== currentValues.active
                                        }
                                        style={{ marginBottom: 0, marginRight: 6 }}
                                    >
                                        {({ getFieldValue }) => {
                                            return (
                                                <span className="ant-form-item-label" style={{ lineHeight: '1.5rem' }}>
                                                    {getFieldValue('active') ? (
                                                        <span className="text-success">Enabled</span>
                                                    ) : (
                                                        <span className="text-danger">Disabled</span>
                                                    )}
                                                </span>
                                            )
                                        }}
                                    </Form.Item>
                                    <Form.Item name="active" noStyle valuePropName="checked">
                                        <Switch />
                                    </Form.Item>
                                </Form.Item>
                                {featureFlagId !== 'new' && (
                                    <Button
                                        data-attr="delete-flag"
                                        danger
                                        icon={<DeleteOutlined />}
                                        onClick={() => {
                                            deleteFeatureFlag(featureFlag)
                                        }}
                                        style={{ marginRight: 16 }}
                                    >
                                        Delete
                                    </Button>
                                )}
                                <Button
                                    icon={<SaveOutlined />}
                                    type="primary"
                                    data-attr="feature-flag-submit"
                                    htmlType="submit"
                                >
                                    Save changes
                                </Button>
                            </div>
                        }
                    />
                    <h3 className="l3">General configuration</h3>
                    <div className="text-muted mb">
                        General settings for your feature flag and integration instructions.
                    </div>
                    <Row gutter={16} style={{ marginBottom: 32 }}>
                        <Col span={12}>
                            <Form.Item
                                name="key"
                                label="Key (must be unique)"
                                rules={[
                                    { required: true, message: 'You need to set a key.' },
                                    {
                                        pattern: /^([A-z]|[a-z]|[0-9]|-|_)+$/,
                                        message: 'Only letters, numbers, hyphens (-) & underscores (_) are allowed.',
                                    },
                                ]}
                                validateStatus={hasKeyChanged ? 'warning' : undefined}
                                help={
                                    hasKeyChanged ? (
                                        <small>
                                            <b>Warning! </b>Changing this key will
                                            <a
                                                href={`https://posthog.com/docs/features/feature-flags${UTM_TAGS}#feature-flag-persistence`}
                                                target="_blank"
                                                rel="noopener"
                                            >
                                                {' '}
                                                affect the persistence of your flag <IconOpenInNew />
                                            </a>
                                        </small>
                                    ) : undefined
                                }
                            >
                                <Input
                                    data-attr="feature-flag-key"
                                    className="ph-ignore-input"
                                    autoFocus
                                    placeholder="examples: new-landing-page, betaFeature, ab_test_1"
                                    autoComplete="off"
                                    autoCapitalize="off"
                                    autoCorrect="off"
                                    spellCheck={false}
                                />
                            </Form.Item>

                            <Form.Item name="name" label="Description">
                                <Input.TextArea
                                    className="ph-ignore-input"
                                    data-attr="feature-flag-description"
                                    placeholder="Adding a helpful description can ensure others know what this feature is for."
                                />
                            </Form.Item>
                        </Col>
                        <Col span={12} style={{ paddingTop: 31 }}>
                            <Collapse>
                                <Collapse.Panel
                                    header={
                                        <div style={{ display: 'flex', fontWeight: 'bold', alignItems: 'center' }}>
                                            <IconJavascript style={{ marginRight: 6 }} /> Javascript integration
                                            instructions
                                        </div>
                                    }
                                    key="js"
                                >
                                    <Form.Item
                                        shouldUpdate={(prevValues, currentValues) =>
                                            prevValues.key !== currentValues.key
                                        }
                                    >
                                        {({ getFieldValue }) => <JSSnippet flagKey={getFieldValue('key')} />}
                                    </Form.Item>
                                </Collapse.Panel>
                                <Collapse.Panel
                                    header={
                                        <div style={{ display: 'flex', fontWeight: 'bold', alignItems: 'center' }}>
                                            <IconPython style={{ marginRight: 6 }} /> Python integration instructions
                                        </div>
                                    }
                                    key="python"
                                >
                                    <Form.Item
                                        shouldUpdate={(prevValues, currentValues) =>
                                            prevValues.key !== currentValues.key
                                        }
                                    >
                                        {({ getFieldValue }) => <PythonSnippet flagKey={getFieldValue('key')} />}
                                    </Form.Item>
                                </Collapse.Panel>
                                <Collapse.Panel
                                    header={
                                        <div style={{ display: 'flex', fontWeight: 'bold', alignItems: 'center' }}>
                                            <ApiFilled style={{ marginRight: 6 }} /> API integration instructions
                                        </div>
                                    }
                                    key="api"
                                >
                                    <Form.Item
                                        shouldUpdate={(prevValues, currentValues) =>
                                            prevValues.key !== currentValues.key
                                        }
                                    >
                                        <APISnippet />
                                    </Form.Item>
                                </Collapse.Panel>
                            </Collapse>
                        </Col>
                    </Row>

                    <div className="mb-2">
                        <h3 className="l3">Served value</h3>
                        <div className="mb-05">
                            <Popconfirm
                                placement="top"
                                title="Change value type? The variants below will be lost."
                                visible={showVariantDiscardWarning}
                                onConfirm={() => {
                                    setMultivariateEnabled(false)
                                    setShowVariantDiscardWarning(false)
                                }}
                                onCancel={() => setShowVariantDiscardWarning(false)}
                                okText="OK"
                                cancelText="Cancel"
                            >
                                <Radio.Group
                                    options={[
                                        {
                                            label: 'Boolean value (A/B test)',
                                            value: false,
                                        },
                                        {
                                            label: (
                                                <Tooltip
                                                    title={
                                                        hasAvailableFeature(AvailableFeature.MULTIVARIATE_FLAGS)
                                                            ? ''
                                                            : 'This feature is not available on your current plan.'
                                                    }
                                                >
                                                    <div>
                                                        {!hasAvailableFeature(AvailableFeature.MULTIVARIATE_FLAGS) && (
                                                            <Link to={upgradeLink} target="_blank">
                                                                <LockOutlined
                                                                    style={{ marginRight: 4, color: 'var(--warning)' }}
                                                                />
                                                            </Link>
                                                        )}
                                                        String value (Multivariate test){' '}
                                                        <LemonTag type="warning">Beta</LemonTag>
                                                    </div>
                                                </Tooltip>
                                            ),
                                            value: true,
                                            disabled: !hasAvailableFeature(AvailableFeature.MULTIVARIATE_FLAGS),
                                        },
                                    ]}
                                    onChange={(e) => {
                                        const { value } = e.target
                                        if (value === false && nonEmptyVariants.length) {
                                            setShowVariantDiscardWarning(true)
                                        } else {
                                            setMultivariateEnabled(value)
                                            focusVariantKeyField(0)
                                        }
                                    }}
                                    value={multivariateEnabled}
                                    optionType="button"
                                />
                            </Popconfirm>
                        </div>
                        <div className="text-muted mb">
                            {capitalizeFirstLetter(aggregationTargetName)} will be served{' '}
                            {multivariateEnabled ? (
                                <>
                                    <strong>a variant key</strong> according to the below distribution
                                </>
                            ) : (
                                <strong>
                                    <code>true</code>
                                </strong>
                            )}{' '}
                            if they match one or more release condition groups.
                        </div>
                        {multivariateEnabled && (
                            <div className="variant-form-list">
                                <Row gutter={8} className="label-row">
                                    <Col span={7}>Variant key</Col>
                                    <Col span={7}>Description</Col>
                                    <Col span={9}>
                                        <span>Rollout percentage</span>
                                        <Button
                                            type="link"
                                            onClick={distributeVariantsEqually}
                                            icon={<MergeCellsOutlined />}
                                            style={{ padding: '0 0 0 0.5em' }}
                                            title="Distribute variants equally"
                                        >
                                            Distribute
                                        </Button>
                                    </Col>
                                </Row>
                                {variants.map(({ rollout_percentage }, index) => (
                                    <Form
                                        key={index}
                                        onValuesChange={(changedValues) => updateVariant(index, changedValues)}
                                        initialValues={variants[index]}
                                        validateTrigger={['onChange', 'onBlur']}
                                    >
                                        <Row gutter={8}>
                                            <Col span={7}>
                                                <Form.Item
                                                    name="key"
                                                    rules={[
                                                        { required: true, message: 'Key should not be empty.' },
                                                        {
                                                            pattern: /^([A-z]|[a-z]|[0-9]|-|_)+$/,
                                                            message:
                                                                'Only letters, numbers, hyphens (-) & underscores (_) are allowed.',
                                                        },
                                                    ]}
                                                >
                                                    <Input
                                                        data-attr="feature-flag-variant-key"
                                                        data-key-index={index.toString()}
                                                        className="ph-ignore-input"
                                                        placeholder={`example-variant-${index + 1}`}
                                                        autoComplete="off"
                                                        autoCapitalize="off"
                                                        autoCorrect="off"
                                                        spellCheck={false}
                                                    />
                                                </Form.Item>
                                            </Col>
                                            <Col span={7}>
                                                <Form.Item name="name">
                                                    <Input
                                                        data-attr="feature-flag-variant-name"
                                                        className="ph-ignore-input"
                                                        placeholder="Description"
                                                    />
                                                </Form.Item>
                                            </Col>
                                            <Col span={7}>
                                                <Slider
                                                    tooltipPlacement="top"
                                                    value={rollout_percentage}
                                                    onChange={(value: number) =>
                                                        updateVariant(index, { rollout_percentage: value })
                                                    }
                                                />
                                            </Col>
                                            <Col span={2}>
                                                <InputNumber
                                                    min={0}
                                                    max={100}
                                                    value={rollout_percentage}
                                                    onChange={(value) => {
                                                        if (value !== null && value !== undefined) {
                                                            const valueInt = parseInt(value.toString())
                                                            if (!isNaN(valueInt)) {
                                                                updateVariant(index, {
                                                                    rollout_percentage: valueInt,
                                                                })
                                                            }
                                                        }
                                                    }}
                                                    style={{
                                                        width: '100%',
                                                        borderColor: areVariantRolloutsValid
                                                            ? undefined
                                                            : 'var(--danger)',
                                                    }}
                                                />
                                            </Col>
                                            {variants.length > 1 && (
                                                <Col span={1}>
                                                    <Tooltip title="Delete this variant" placement="bottomLeft">
                                                        <Button
                                                            type="link"
                                                            icon={<DeleteOutlined />}
                                                            onClick={() => removeVariant(index)}
                                                            style={{ color: 'var(--danger)' }}
                                                        />
                                                    </Tooltip>
                                                </Col>
                                            )}
                                        </Row>
                                    </Form>
                                ))}
                                {variants.length > 0 && !areVariantRolloutsValid && (
                                    <p className="text-danger">
                                        Percentage rollouts for variants must sum to 100 (currently {variantRolloutSum}
                                        ).
                                    </p>
                                )}
                                <Button
                                    type="dashed"
                                    block
                                    icon={<PlusOutlined />}
                                    onClick={() => {
                                        const newIndex = variants.length
                                        addVariant()
                                        focusVariantKeyField(newIndex)
                                    }}
                                    style={{ marginBottom: 16 }}
                                >
                                    Add Variant
                                </Button>
                            </div>
                        )}
                    </div>

                    <div className="feature-flag-form-row">
                        <div>
                            <h3 className="l3">Release conditions</h3>
                            <div className="text-muted mb">
                                Specify the {aggregationTargetName} to which you want to release this flag. Note that
                                condition sets are rolled out independently of each other.
                            </div>
                        </div>
                        {showGroupsOptions && (
                            <div className="centered">
                                Match by
                                <Select
                                    value={
                                        featureFlag.filters.aggregation_group_type_index != null
                                            ? featureFlag.filters.aggregation_group_type_index
                                            : -1
                                    }
                                    onChange={(value) => {
                                        const groupTypeIndex = value !== -1 ? value : null
                                        setAggregationGroupTypeIndex(groupTypeIndex)
                                    }}
                                    style={{ marginLeft: 8 }}
                                    data-attr="feature-flag-aggregation-filter"
                                    dropdownMatchSelectWidth={false}
                                    dropdownAlign={{
                                        // Align this dropdown by the right-hand-side of button
                                        points: ['tr', 'br'],
                                    }}
                                >
                                    <Select.Option key={-1} value={-1}>
                                        Users
                                    </Select.Option>
                                    {groupTypes.map((groupType) => (
                                        <Select.Option
                                            key={groupType.group_type_index}
                                            value={groupType.group_type_index}
                                        >
                                            {capitalizeFirstLetter(aggregationLabel(groupType.group_type_index).plural)}
                                        </Select.Option>
                                    ))}
                                    {matchByGroupsIntroductionOption}
                                </Select>
                            </div>
                        )}
                    </div>
                    <Row gutter={16}>
                        {featureFlag.filters.groups.map((group, index) => (
                            <Col span={24} md={24} key={`${index}-${featureFlag.filters.groups.length}`}>
                                {index > 0 && (
                                    <div style={{ display: 'flex', marginLeft: 16 }}>
                                        <div className="stateful-badge or-light-grey mb">OR</div>
                                    </div>
                                )}
                                <Card style={{ marginBottom: 16 }}>
                                    <div className="feature-flag-form-row" style={{ height: 24 }}>
                                        <div>
                                            <span className="simple-tag tag-light-blue" style={{ marginRight: 8 }}>
                                                Set {index + 1}
                                            </span>
                                            {group.properties?.length ? (
                                                <>
                                                    Matching <b>{aggregationTargetName}</b> with filters
                                                </>
                                            ) : (
                                                <>
                                                    Condition set will match <b>all {aggregationTargetName}</b>
                                                </>
                                            )}
                                        </div>
                                        <div>
                                            <Tooltip title="Duplicate this condition set" placement="bottomLeft">
                                                <Button
                                                    type="link"
                                                    icon={<CopyOutlined />}
                                                    style={{ width: 24, height: 24 }}
                                                    onClick={() => duplicateConditionSet(index)}
                                                />
                                            </Tooltip>
                                            {featureFlag.filters.groups.length > 1 && (
                                                <Tooltip title="Delete this condition set" placement="bottomLeft">
                                                    <Button
                                                        type="link"
                                                        icon={<DeleteOutlined />}
                                                        style={{ width: 24, height: 24 }}
                                                        onClick={() => removeConditionSet(index)}
                                                    />
                                                </Tooltip>
                                            )}
                                        </div>
                                    </div>

                                    <LemonSpacer large />
                                    <PropertyFilters
                                        style={{ marginLeft: 15 }}
                                        pageKey={`feature-flag-${featureFlag.id}-${index}-${
                                            featureFlag.filters.groups.length
                                        }-${featureFlag.filters.aggregation_group_type_index ?? ''}`}
                                        propertyFilters={group?.properties}
                                        onChange={(properties) => updateConditionSet(index, undefined, properties)}
                                        taxonomicGroupTypes={taxonomicGroupTypes}
                                        showConditionBadge
                                        greyBadges
                                    />
                                    <LemonSpacer large />

                                    <div className="feature-flag-form-row">
                                        <div className="centered">
                                            Roll out to{' '}
                                            <InputNumber
                                                style={{ width: 100, marginLeft: 8, marginRight: 8 }}
                                                onChange={(value): void => {
                                                    updateConditionSet(index, value as number)
                                                }}
                                                value={
                                                    group.rollout_percentage != null ? group.rollout_percentage : 100
                                                }
                                                min={0}
                                                max={100}
                                                addonAfter="%"
                                            />{' '}
                                            of <b>{aggregationTargetName}</b> in this set
                                        </div>
                                    </div>
                                </Card>
                            </Col>
                        ))}
                    </Row>
                    <Card size="small" style={{ marginBottom: 16 }}>
                        <Button type="link" onClick={addConditionSet} style={{ marginLeft: 5 }}>
                            <PlusOutlined style={{ marginRight: 15 }} /> Add condition set
                        </Button>
                    </Card>
                    <Form.Item className="text-right">
                        <Button
                            icon={<SaveOutlined />}
                            htmlType="submit"
                            type="primary"
                            data-attr="feature-flag-submit-bottom"
                        >
                            Save changes
                        </Button>
                    </Form.Item>
                </Form>
            ) : (
                // TODO: This should be skeleton loaders
                <SceneLoading />
            )}
        </div>
    )
}
Example #25
Source File: DashboardItem.tsx    From posthog-foss with MIT License 4 votes vote down vote up
export function DashboardItem({
    item,
    dashboardId,
    receivedErrorFromAPI,
    updateItemColor,
    setDiveDashboard,
    loadDashboardItems,
    isDraggingRef,
    isReloading,
    reload,
    dashboardMode,
    isOnEditMode,
    setEditMode,
    index,
    layout,
    footer,
    onClick,
    moveDashboardItem,
    saveDashboardItem,
    duplicateDashboardItem,
    isHighlighted = false,
    doNotLoad = false,
}: DashboardItemProps): JSX.Element {
    const [initialLoaded, setInitialLoaded] = useState(false)
    const [showSaveModal, setShowSaveModal] = useState(false)
    const { currentTeamId } = useValues(teamLogic)
    const { nameSortedDashboards } = useValues(dashboardsModel)
    const { renameInsight } = useActions(insightsModel)
    const { featureFlags } = useValues(featureFlagLogic)

    const _type = getDisplayedType(item.filters)

    const insightTypeDisplayName =
        item.filters.insight === InsightType.RETENTION
            ? 'Retention'
            : item.filters.insight === InsightType.PATHS
            ? 'Paths'
            : item.filters.insight === InsightType.FUNNELS
            ? 'Funnel'
            : item.filters.insight === InsightType.STICKINESS
            ? 'Stickiness'
            : 'Trends'

    const className = displayMap[_type].className
    const Element = displayMap[_type].element
    const viewText = displayMap[_type].viewText
    const link = combineUrl(urls.insightView(item.short_id, item.filters), undefined, {
        fromDashboard: item.dashboard,
    }).url
    const color = item.color || 'white'
    const otherDashboards: DashboardType[] = nameSortedDashboards.filter((d: DashboardType) => d.id !== dashboardId)
    const getDashboard = (id: number): DashboardType | undefined => nameSortedDashboards.find((d) => d.id === id)

    const longPressProps = useLongPress(setEditMode, {
        ms: 500,
        touch: true,
        click: false,
        exclude: 'table, table *',
    })

    const filters = { ...item.filters, from_dashboard: item.dashboard || undefined }
    const logicProps: InsightLogicProps = {
        dashboardItemId: item.short_id,
        filters: filters,
        cachedResults: (item as any).result,
        doNotLoad,
    }
    const { insightProps, showTimeoutMessage, showErrorMessage, insight, insightLoading, isLoading } = useValues(
        insightLogic(logicProps)
    )
    const { loadResults } = useActions(insightLogic(logicProps))

    const { reportDashboardItemRefreshed } = useActions(eventUsageLogic)
    const { areFiltersValid, isValidFunnel, areExclusionFiltersValid } = useValues(funnelLogic(insightProps))
    const previousLoading = usePrevious(insightLoading)
    const diveDashboard = item.dive_dashboard ? getDashboard(item.dive_dashboard) : null

    // if a load is performed and returns that is not the initial load, we refresh dashboard item to update timestamp
    useEffect(() => {
        if (previousLoading && !insightLoading && !initialLoaded) {
            setInitialLoaded(true)
        }
    }, [insightLoading])

    // Empty states that completely replace the graph
    const BlockingEmptyState = (() => {
        // Insight specific empty states - note order is important here
        if (item.filters.insight === InsightType.FUNNELS) {
            if (!areFiltersValid) {
                return <FunnelSingleStepState />
            }
            if (!areExclusionFiltersValid) {
                return <FunnelInvalidExclusionState />
            }
            if (!isValidFunnel && !(insightLoading || isLoading)) {
                return <InsightEmptyState />
            }
        }

        // Insight agnostic empty states
        if (showErrorMessage || receivedErrorFromAPI) {
            return <InsightErrorState excludeDetail={true} />
        }
        if (showTimeoutMessage) {
            return <InsightTimeoutState isLoading={isLoading} />
        }

        // Deprecated insights
        if ((item.filters.insight as string) === 'SESSIONS') {
            return <InsightDeprecatedState deleteCallback={loadDashboardItems} itemId={item.id} itemName={item.name} />
        }

        return null
    })()

    // Empty states that can coexist with the graph (e.g. Loading)
    const CoexistingEmptyState = (() => {
        if (isLoading || insightLoading || isReloading) {
            return <Loading />
        }
        return null
    })()

    const response = (
        <div
            key={item.short_id}
            className={`dashboard-item ${item.color || 'white'} di-width-${layout?.w || 0} di-height-${
                layout?.h || 0
            } ph-no-capture`}
            {...longPressProps}
            data-attr={'dashboard-item-' + index}
            style={{ border: isHighlighted ? '1px solid var(--primary)' : undefined }}
        >
            {!BlockingEmptyState && CoexistingEmptyState}
            <div className={`dashboard-item-container ${className}`}>
                <div className="dashboard-item-header" style={{ cursor: isOnEditMode ? 'move' : 'inherit' }}>
                    <div className="dashboard-item-title" data-attr="dashboard-item-title">
                        {dashboardMode === DashboardMode.Public ? (
                            item.name
                        ) : (
                            <Link
                                draggable={false}
                                to={link}
                                title={item.name}
                                preventClick
                                onClick={() => {
                                    if (!isDraggingRef?.current) {
                                        onClick ? onClick() : router.actions.push(link)
                                    }
                                }}
                                style={{ fontSize: 16, fontWeight: 500 }}
                            >
                                {item.name || `Untitled ${insightTypeDisplayName} Query`}
                            </Link>
                        )}
                    </div>
                    {dashboardMode !== DashboardMode.Public && (
                        <div className="dashboard-item-settings">
                            {saveDashboardItem &&
                                dashboardMode !== DashboardMode.Internal &&
                                (!item.saved && item.dashboard ? (
                                    <Link to={'/dashboard/' + item.dashboard}>
                                        <small>View dashboard</small>
                                    </Link>
                                ) : (
                                    <Tooltip title="Save insight">
                                        <SaveOutlined
                                            style={{
                                                cursor: 'pointer',
                                                marginTop: -3,
                                                ...(item.saved
                                                    ? {
                                                          background: 'var(--primary)',
                                                          color: 'white',
                                                      }
                                                    : {}),
                                            }}
                                            onClick={() => {
                                                if (item.saved) {
                                                    return saveDashboardItem({ ...item, saved: false })
                                                }
                                                if (item.name) {
                                                    // If item already has a name we don't have to ask for it again
                                                    return saveDashboardItem({ ...item, saved: true })
                                                }
                                                setShowSaveModal(true)
                                            }}
                                        />
                                    </Tooltip>
                                ))}
                            {dashboardMode !== DashboardMode.Internal && (
                                <>
                                    {featureFlags[FEATURE_FLAGS.DIVE_DASHBOARDS] &&
                                        typeof item.dive_dashboard === 'number' && (
                                            <Tooltip title={`Dive to ${diveDashboard?.name || 'connected dashboard'}`}>
                                                <LinkButton
                                                    to={dashboardDiveLink(item.dive_dashboard, item.short_id)}
                                                    icon={
                                                        <span role="img" aria-label="dive" className="anticon">
                                                            <DiveIcon />
                                                        </span>
                                                    }
                                                    data-attr="dive-btn-dive"
                                                    className="dive-btn dive-btn-dive"
                                                >
                                                    Dive
                                                </LinkButton>
                                            </Tooltip>
                                        )}
                                    <Dropdown
                                        overlayStyle={{ minWidth: 240, border: '1px solid var(--primary)' }}
                                        placement="bottomRight"
                                        trigger={['click']}
                                        overlay={
                                            <Menu
                                                data-attr={'dashboard-item-' + index + '-dropdown-menu'}
                                                style={{ padding: '12px 4px' }}
                                            >
                                                <Menu.Item data-attr={'dashboard-item-' + index + '-dropdown-view'}>
                                                    <Link to={link}>{viewText}</Link>
                                                </Menu.Item>
                                                <Menu.Item
                                                    data-attr={'dashboard-item-' + index + '-dropdown-refresh'}
                                                    onClick={() => {
                                                        // On dashboards we use custom reloading logic, which updates a
                                                        // global "loading 1 out of n" label, and loads 4 items at a time
                                                        if (reload) {
                                                            reload()
                                                        } else {
                                                            loadResults(true)
                                                        }
                                                        reportDashboardItemRefreshed(item)
                                                    }}
                                                >
                                                    <Tooltip
                                                        placement="left"
                                                        title={
                                                            <i>
                                                                Last updated:{' '}
                                                                {item.last_refresh
                                                                    ? dayjs(item.last_refresh).fromNow()
                                                                    : 'recently'}
                                                            </i>
                                                        }
                                                    >
                                                        Refresh
                                                    </Tooltip>
                                                </Menu.Item>
                                                <Menu.Item
                                                    data-attr={'dashboard-item-' + index + '-dropdown-rename'}
                                                    onClick={() => renameInsight(item)}
                                                >
                                                    Rename
                                                </Menu.Item>
                                                {updateItemColor && (
                                                    <Menu.SubMenu
                                                        data-attr={'dashboard-item-' + index + '-dropdown-color'}
                                                        key="colors"
                                                        title="Set color"
                                                    >
                                                        {Object.entries(dashboardColorNames).map(
                                                            ([itemClassName, itemColor], colorIndex) => (
                                                                <Menu.Item
                                                                    key={itemClassName}
                                                                    onClick={() =>
                                                                        updateItemColor(item.id, itemClassName)
                                                                    }
                                                                    data-attr={
                                                                        'dashboard-item-' +
                                                                        index +
                                                                        '-dropdown-color-' +
                                                                        colorIndex
                                                                    }
                                                                >
                                                                    <span
                                                                        style={{
                                                                            background: dashboardColors[itemClassName],
                                                                            border: '1px solid #eee',
                                                                            display: 'inline-block',
                                                                            width: 13,
                                                                            height: 13,
                                                                            verticalAlign: 'middle',
                                                                            marginRight: 5,
                                                                            marginBottom: 1,
                                                                        }}
                                                                    />
                                                                    {itemColor}
                                                                </Menu.Item>
                                                            )
                                                        )}
                                                    </Menu.SubMenu>
                                                )}
                                                {featureFlags[FEATURE_FLAGS.DIVE_DASHBOARDS] && setDiveDashboard && (
                                                    <Menu.SubMenu
                                                        data-attr={'dashboard-item-' + index + '-dive-dashboard'}
                                                        key="dive"
                                                        title={`Set dive dashboard`}
                                                    >
                                                        {otherDashboards.map((dashboard, diveIndex) => (
                                                            <Menu.Item
                                                                data-attr={
                                                                    'dashboard-item-' +
                                                                    index +
                                                                    '-dive-dashboard-' +
                                                                    diveIndex
                                                                }
                                                                key={dashboard.id}
                                                                onClick={() => setDiveDashboard(item.id, dashboard.id)}
                                                                disabled={dashboard.id === item.dive_dashboard}
                                                            >
                                                                {dashboard.name}
                                                            </Menu.Item>
                                                        ))}
                                                        <Menu.Item
                                                            data-attr={
                                                                'dashboard-item-' + index + '-dive-dashboard-remove'
                                                            }
                                                            key="remove"
                                                            onClick={() => setDiveDashboard(item.id, null)}
                                                            className="text-danger"
                                                        >
                                                            Remove
                                                        </Menu.Item>
                                                    </Menu.SubMenu>
                                                )}
                                                {duplicateDashboardItem && otherDashboards.length > 0 && (
                                                    <Menu.SubMenu
                                                        data-attr={'dashboard-item-' + index + '-dropdown-copy'}
                                                        key="copy"
                                                        title="Copy to"
                                                    >
                                                        {otherDashboards.map((dashboard, copyIndex) => (
                                                            <Menu.Item
                                                                data-attr={
                                                                    'dashboard-item-' +
                                                                    index +
                                                                    '-dropdown-copy-' +
                                                                    copyIndex
                                                                }
                                                                key={dashboard.id}
                                                                onClick={() =>
                                                                    duplicateDashboardItem(item, dashboard.id)
                                                                }
                                                            >
                                                                <span
                                                                    style={{
                                                                        background: dashboardColors[className],
                                                                        border: '1px solid #eee',
                                                                        display: 'inline-block',
                                                                        width: 13,
                                                                        height: 13,
                                                                        verticalAlign: 'middle',
                                                                        marginRight: 5,
                                                                        marginBottom: 1,
                                                                    }}
                                                                />
                                                                {dashboard.name}
                                                            </Menu.Item>
                                                        ))}
                                                    </Menu.SubMenu>
                                                )}
                                                {moveDashboardItem &&
                                                    (otherDashboards.length > 0 ? (
                                                        <Menu.SubMenu
                                                            data-attr={'dashboard-item-' + index + '-dropdown-move'}
                                                            key="move"
                                                            title="Move to"
                                                        >
                                                            {otherDashboards.map((dashboard, moveIndex) => (
                                                                <Menu.Item
                                                                    data-attr={
                                                                        'dashboard-item-' +
                                                                        index +
                                                                        '-dropdown-move-' +
                                                                        moveIndex
                                                                    }
                                                                    key={dashboard.id}
                                                                    onClick={() =>
                                                                        moveDashboardItem(item, dashboard.id)
                                                                    }
                                                                >
                                                                    {dashboard.name}
                                                                </Menu.Item>
                                                            ))}
                                                        </Menu.SubMenu>
                                                    ) : null)}
                                                {duplicateDashboardItem && (
                                                    <Menu.Item
                                                        data-attr={'dashboard-item-' + index + '-dropdown-duplicate'}
                                                        onClick={() => duplicateDashboardItem(item)}
                                                    >
                                                        Duplicate
                                                    </Menu.Item>
                                                )}
                                                <Menu.Item
                                                    data-attr={'dashboard-item-' + index + '-dropdown-delete'}
                                                    onClick={() =>
                                                        deleteWithUndo({
                                                            object: {
                                                                id: item.id,
                                                                name: item.name,
                                                            },
                                                            endpoint: `projects/${currentTeamId}/insights`,
                                                            callback: loadDashboardItems,
                                                        })
                                                    }
                                                    className="text-danger"
                                                >
                                                    Delete
                                                </Menu.Item>
                                            </Menu>
                                        }
                                    >
                                        <span
                                            data-attr={'dashboard-item-' + index + '-dropdown'}
                                            style={{ cursor: 'pointer', marginTop: -3 }}
                                        >
                                            <EllipsisOutlined />
                                        </span>
                                    </Dropdown>
                                </>
                            )}
                        </div>
                    )}
                </div>
                {item.description && (
                    <div style={{ padding: '0 16px', marginBottom: 16, fontSize: 12 }}>{item.description}</div>
                )}

                <div className={`dashboard-item-content ${_type}`} onClickCapture={onClick}>
                    {!!BlockingEmptyState ? (
                        BlockingEmptyState
                    ) : (
                        <Alert.ErrorBoundary message="Error rendering graph!">
                            {dashboardMode === DashboardMode.Public && !insight.result && !item.result ? (
                                <Skeleton />
                            ) : (
                                <Element
                                    dashboardItemId={item.short_id}
                                    cachedResults={item.result}
                                    filters={filters}
                                    color={color}
                                    theme={color === 'white' ? 'light' : 'dark'}
                                    inSharedMode={dashboardMode === DashboardMode.Public}
                                />
                            )}
                        </Alert.ErrorBoundary>
                    )}
                </div>
                {footer}
            </div>
            {showSaveModal && saveDashboardItem && (
                <SaveModal
                    title="Save Chart"
                    prompt="Name of Chart"
                    textLabel="Name"
                    textPlaceholder="DAUs Last 14 days"
                    visible={true}
                    onCancel={() => {
                        setShowSaveModal(false)
                    }}
                    onSubmit={(text) => {
                        saveDashboardItem({ ...item, name: text, saved: true })
                        setShowSaveModal(false)
                    }}
                />
            )}
        </div>
    )

    return (
        <BindLogic logic={insightLogic} props={insightProps}>
            {response}
        </BindLogic>
    )
}
Example #26
Source File: ActionEdit.tsx    From posthog-foss with MIT License 4 votes vote down vote up
export function ActionEdit({ action: loadedAction, id, onSave, temporaryToken }: ActionEditLogicProps): JSX.Element {
    const relevantActionEditLogic = actionEditLogic({
        id: id,
        action: loadedAction,
        onSave: (action) => onSave(action),
        temporaryToken,
    })
    const { action, errorActionId, actionCount, actionCountLoading } = useValues(relevantActionEditLogic)
    const { setAction, saveAction } = useActions(relevantActionEditLogic)
    const { loadActions } = useActions(actionsModel)
    const { currentTeam } = useValues(teamLogic)

    const [edited, setEdited] = useState(false)
    const slackEnabled = currentTeam?.slack_incoming_webhook

    function addMatchGroup(): void {
        setAction({ ...action, steps: [...(action.steps || []), { isNew: uuid() }] })
    }

    const addGroup = (
        <Button onClick={addMatchGroup} size="small">
            Add another match group
        </Button>
    )

    const deleteAction = id ? (
        <Button
            data-attr="delete-action"
            danger
            icon={<DeleteOutlined />}
            onClick={() => {
                deleteWithUndo({
                    endpoint: api.actions.determineDeleteEndpoint(),
                    object: action,
                    callback: () => {
                        router.actions.push('/events/actions')
                        loadActions()
                    },
                })
            }}
        >
            Delete
        </Button>
    ) : undefined

    return (
        <div className="action-edit-container">
            <PageHeader title={id ? 'Editing action' : 'Creating action'} buttons={deleteAction} />
            <form
                onSubmit={(e) => {
                    e.preventDefault()
                    saveAction()
                }}
            >
                <div className="input-set">
                    <label htmlFor="actionName">Action name</label>
                    <Input
                        required
                        placeholder="e.g. user account created, purchase completed, movie watched"
                        value={action.name}
                        style={{ maxWidth: 500, display: 'block' }}
                        onChange={(e) => {
                            setAction({ ...action, name: e.target.value })
                            setEdited(e.target.value ? true : false)
                        }}
                        data-attr="edit-action-input"
                        id="actionName"
                    />
                    {id && (
                        <div>
                            <span className="text-muted mb-05">
                                {actionCountLoading && <LoadingOutlined />}
                                {actionCount !== null && actionCount > -1 && (
                                    <>
                                        This action matches <b>{compactNumber(actionCount)}</b> events in the last 3
                                        months
                                    </>
                                )}
                            </span>
                        </div>
                    )}
                </div>

                <div className="match-group-section" style={{ overflow: 'visible' }}>
                    <h2 className="subtitle">Match groups</h2>
                    <div>
                        Your action will be triggered whenever <b>any of your match groups</b> are received.{' '}
                        <a href="https://posthog.com/docs/features/actions" target="_blank">
                            <InfoCircleOutlined />
                        </a>
                    </div>
                    <div style={{ textAlign: 'right', marginBottom: 12 }}>{addGroup}</div>

                    <Row gutter={[24, 24]}>
                        {action.steps?.map((step, index) => (
                            <ActionStep
                                key={step.id || step.isNew}
                                identifier={String(step.id || step.isNew)}
                                index={index}
                                step={step}
                                actionId={action.id || 0}
                                isOnlyStep={!!action.steps && action.steps.length === 1}
                                onDelete={() => {
                                    const identifier = step.id ? 'id' : 'isNew'
                                    setAction({
                                        ...action,
                                        steps: action.steps?.filter((s) => s[identifier] !== step[identifier]),
                                    })
                                    setEdited(true)
                                }}
                                onChange={(newStep) => {
                                    setAction({
                                        ...action,
                                        steps: action.steps?.map((s) =>
                                            (step.id && s.id == step.id) || (step.isNew && s.isNew === step.isNew)
                                                ? {
                                                      id: step.id,
                                                      isNew: step.isNew,
                                                      ...newStep,
                                                  }
                                                : s
                                        ),
                                    })
                                    setEdited(true)
                                }}
                            />
                        ))}
                        <Col span={24} md={12}>
                            <div className="match-group-add-skeleton" onClick={addMatchGroup}>
                                <PlusOutlined style={{ fontSize: 28, color: '#666666' }} />
                            </div>
                        </Col>
                    </Row>
                </div>
                <div>
                    <div style={{ margin: '1rem 0' }}>
                        <p>
                            <input
                                id="webhook-checkbox"
                                type="checkbox"
                                onChange={(e) => {
                                    setAction({ ...action, post_to_slack: e.target.checked })
                                    setEdited(true)
                                }}
                                checked={!!action.post_to_slack}
                                disabled={!slackEnabled}
                            />
                            <label
                                className={slackEnabled ? '' : 'disabled'}
                                style={{ marginLeft: '0.5rem', marginBottom: '0.5rem' }}
                                htmlFor="webhook-checkbox"
                            >
                                Post to webhook when this action is triggered.
                            </label>{' '}
                            <Link to="/project/settings#webhook">
                                {slackEnabled ? 'Configure' : 'Enable'} this integration in Setup.
                            </Link>
                        </p>
                        {action.post_to_slack && (
                            <>
                                <Input
                                    addonBefore="Message format (optional)"
                                    placeholder="Default: [action.name] triggered by [user.name]"
                                    value={action.slack_message_format}
                                    onChange={(e) => {
                                        setAction({ ...action, slack_message_format: e.target.value })
                                        setEdited(true)
                                    }}
                                    disabled={!slackEnabled || !action.post_to_slack}
                                    data-attr="edit-slack-message-format"
                                />
                                <small>
                                    <a
                                        href="https://posthog.com/docs/integrations/message-formatting/"
                                        target="_blank"
                                        rel="noopener noreferrer"
                                    >
                                        See documentation on how to format webhook messages.
                                    </a>
                                </small>
                            </>
                        )}
                    </div>
                </div>
                {errorActionId && (
                    <p className="text-danger">
                        Action with this name already exists.{' '}
                        <a href={urls.action(errorActionId)}>Click here to edit.</a>
                    </p>
                )}
                <div className="float-right">
                    <span data-attr="delete-action-bottom">{deleteAction}</span>
                    <Button
                        disabled={!edited}
                        data-attr="save-action-button"
                        type="primary"
                        icon={<SaveOutlined />}
                        onClick={saveAction}
                        style={{ marginLeft: 16 }}
                    >
                        Save action
                    </Button>
                </div>
            </form>
        </div>
    )
}
Example #27
Source File: palette.tsx    From jmix-frontend with Apache License 2.0 4 votes vote down vote up
palette = () =>
  <Palette>
    <Category name="Text">
      <Component name="Formatted Message">
        <Variant>
          <FormattedMessage />
        </Variant>
      </Component>
      <Component name="Heading">
        <Variant name='h1'>
          <Typography.Title></Typography.Title>
        </Variant>
        <Variant name='h2'>
          <Typography.Title level = {2}></Typography.Title>
        </Variant>
        <Variant name='h3'>
          <Typography.Title level = {3}></Typography.Title>
        </Variant>
        <Variant name='h4'>
          <Typography.Title level = {4}></Typography.Title>
        </Variant>
        <Variant name='h5'>
          <Typography.Title level = {5}></Typography.Title>
        </Variant>
      </Component>
      <Component name='Text'>
        <Variant>
          <Typography.Text></Typography.Text>
        </Variant>
        <Variant name = 'Secondary'>
          <Typography.Text type="secondary"></Typography.Text>
        </Variant>
        <Variant name = 'Success'>
          <Typography.Text type="success"></Typography.Text>
        </Variant>
        <Variant name = 'Warning'>
          <Typography.Text type="warning"></Typography.Text>
        </Variant>
        <Variant name = 'Danger'>
          <Typography.Text type="danger"></Typography.Text>
        </Variant>
        <Variant name = 'Disabled'>
          <Typography.Text disabled></Typography.Text>
        </Variant>
      </Component>
    </Category>
    <Category name="Layout">
      <Component name="Divider">
        <Variant>
          <Divider />
        </Variant>
      </Component>

      <Component name="Grid">
        <Variant name="Simple Row">
          <Row></Row>
        </Variant>
        <Variant name="Two columns">
          <Row>
            <Col span={12}></Col>
            <Col span={12}></Col>
          </Row>
        </Variant>
        <Variant name="Three columns">
          <Row>
            <Col span={8}></Col>
            <Col span={8}></Col>
            <Col span={8}></Col>
          </Row>
        </Variant>
      </Component>

      <Component name="Space">
        <Variant>
          <Space />
        </Variant>
        <Variant name="Small">
          <Space size={"small"} />
        </Variant>
        <Variant name="Large">
          <Space size={"large"} />
        </Variant>
      </Component>
    </Category>
    <Category name="Controls">
      <Component name="Autocomplete">
        <Variant>
          <AutoComplete placeholder="input here" />
        </Variant>
      </Component>

      <Component name="Button">
        <Variant>
          <Button></Button>
        </Variant>
        <Variant name="Primary">
          <Button type="primary" ></Button>
        </Variant>
        <Variant name="Link">
          <Button type="link" ></Button>
        </Variant>
        <Variant name="Dropdown">
          <Dropdown
            trigger={['click']}
            overlay={<Menu>
              <Menu.Item>
              </Menu.Item>
              <Menu.Item>
              </Menu.Item>
              <Menu.Item>
              </Menu.Item>
            </Menu>}
          >
            <Button></Button>
          </Dropdown>
        </Variant>
      </Component>

      <Component name="Checkbox">
        <Variant>
          <Checkbox />
        </Variant>
      </Component>

      <Component name='Switch'>
        <Variant>
          <Switch />
        </Variant>
      </Component>

      <Component name='Radio Group'>
        <Variant>
          <Radio.Group>
            <Radio value={1}>A</Radio>
            <Radio value={2}>B</Radio>
            <Radio value={3}>C</Radio>
            <Radio value={4}>D</Radio>
          </Radio.Group>
        </Variant>
        <Variant name = 'Button'>
          <Radio.Group>
            <Radio.Button value={1}>A</Radio.Button>
            <Radio.Button value={2}>B</Radio.Button>
            <Radio.Button value={3}>C</Radio.Button>
            <Radio.Button value={4}>D</Radio.Button>
          </Radio.Group>
        </Variant>
      </Component>

      <Component name="DatePicker">
        <Variant>
          <DatePicker />
        </Variant>
        <Variant name="Range">
          <DatePicker.RangePicker />
        </Variant>
      </Component>

      <Component name="TimePicker">
        <Variant>
          <TimePicker />
        </Variant>
        <Variant name="Range">
          <TimePicker.RangePicker />
        </Variant>
      </Component>

      <Component name="Input">
        <Variant>
          <Input />
        </Variant>
        <Variant name='Number'>
          <InputNumber />
        </Variant>
      </Component>

      <Component name='Select'>
        <Variant>
          <Select defaultValue="1">
            <Select.Option value="1">1</Select.Option>
            <Select.Option value="2">2</Select.Option>
          </Select>
        </Variant>
        <Variant name='Multiple'>
          <Select
            defaultValue={["1"]}
            mode="multiple"
            allowClear
          >
            <Select.Option value="1">1</Select.Option>
            <Select.Option value="2">2</Select.Option>
          </Select>
        </Variant>
      </Component>

      <Component name="Link">
        <Variant>
          <Typography.Link href="" target="_blank">
          </Typography.Link>
        </Variant>
      </Component>

      <Component name='Slider'>
        <Variant>
          <Slider defaultValue={30} />
        </Variant>
        <Variant name = 'Range'>
          <Slider range defaultValue={[20, 50]}/>
        </Variant>
      </Component>
    </Category>
    <Category name="Data Display">
    <Component name="Field">
        <Variant>
          <Field
            entityName={ENTITY_NAME}
            disabled={readOnlyMode}
            propertyName=''
            formItemProps={{
              style: { marginBottom: "12px" }
            }}
          />
        </Variant>
      </Component>
      <Component name="Card">
        <Variant>
          <Card />
        </Variant>
        <Variant name="With Title">
          <Card>
            <Card title="Card title">
              <p>Card content</p>
            </Card>
          </Card>
        </Variant>
        <Variant name="My custom card">
          <Card>
            <Card title="Card title">
              <p>Card content</p>
              <Avatar />
            </Card>
          </Card>
        </Variant>
      </Component>
      <Component name="Tabs">
        <Variant>
          <Tabs defaultActiveKey="1">
            <Tabs.TabPane tab="Tab 1" key="1">
              Content of Tab Pane 1
            </Tabs.TabPane>
            <Tabs.TabPane tab="Tab 2" key="2">
              Content of Tab Pane 2
            </Tabs.TabPane>
            <Tabs.TabPane tab="Tab 3" key="3">
              Content of Tab Pane 3
            </Tabs.TabPane>
          </Tabs>
        </Variant>
        <Variant name = "Tab Pane">
          <Tabs.TabPane>
          </Tabs.TabPane>
        </Variant>
      </Component>
      <Component name="Collapse">
        <Variant>
          <Collapse defaultActiveKey='1'>
            <Collapse.Panel header="This is panel header 1" key="1">
            </Collapse.Panel>
            <Collapse.Panel header="This is panel header 2" key="2">
            </Collapse.Panel>
            <Collapse.Panel header="This is panel header 3" key="3">
            </Collapse.Panel>
          </Collapse>
        </Variant>
      </Component>
      <Component name="Image">
        <Variant>
          <Image
            width={200}
            src=""
          />
        </Variant>
      </Component>
      <Component name="Avatar">
        <Variant>
          <Avatar icon={<UserOutlined />} />
        </Variant>
        <Variant name="Image">
          <Avatar src="https://joeschmoe.io/api/v1/random" />
        </Variant>
      </Component>
      <Component name="Badge">
        <Variant>
          <Badge count={1}>
          </Badge>
        </Variant>
      </Component>
      <Component name="Statistic">
        <Variant>
          <Statistic title="Title" value={112893} />
        </Variant>
      </Component>
      <Component name="Alert">
        <Variant name="Success">
          <Alert message="Text" type="success" />
        </Variant>
        <Variant name="Info">
          <Alert message="Text" type="info" />
        </Variant>
        <Variant name="Warning">
          <Alert message="Text" type="warning" />
        </Variant>
        <Variant name="Error">
          <Alert message="Text" type="error" />
        </Variant>
      </Component>
      <Component name='List'>
        <Variant>
          <List
            bordered
            dataSource={[]}
            renderItem={item => (
              <List.Item>
              </List.Item>
            )}
          />
        </Variant>
      </Component>
    </Category>
    <Category name="Icons">
      <Component name="Arrow">
        <Variant name = 'Up'>
          <ArrowUpOutlined />
        </Variant>
        <Variant name = 'Down'>
          <ArrowDownOutlined />
        </Variant>
        <Variant name = 'Left'>
          <ArrowLeftOutlined />
        </Variant>
        <Variant name = 'Right'>
          <ArrowRightOutlined />
        </Variant>
      </Component>
      <Component name = 'Question'>
        <Variant>
          <QuestionOutlined />
        </Variant>
        <Variant name = 'Circle'>
          <QuestionCircleOutlined />
        </Variant>
      </Component>
      <Component name = 'Plus'>
        <Variant>
          <PlusOutlined />
        </Variant>
        <Variant name = 'Circle'>
          <PlusCircleOutlined />
        </Variant>
      </Component>
      <Component name = 'Info'>
        <Variant>
          <InfoOutlined />
        </Variant>
        <Variant name = 'Circle'>
          <InfoCircleOutlined />
        </Variant>
      </Component>
      <Component name = 'Exclamation'>
        <Variant>
          <ExclamationOutlined />
        </Variant>
        <Variant name = 'Circle'>
          <ExclamationCircleOutlined />
        </Variant>
      </Component>
      <Component name = 'Close'>
        <Variant>
          <CloseOutlined />
        </Variant>
        <Variant name = 'Circle'>
          <CloseCircleOutlined />
        </Variant>
      </Component>
      <Component name = 'Check'>
        <Variant>
          <CheckOutlined />
        </Variant>
        <Variant name = 'Circle'>
          <CheckCircleOutlined />
        </Variant>
      </Component>
      <Component name = 'Edit'>
        <Variant>
          <EditOutlined />
        </Variant>
      </Component>
      <Component name = 'Copy'>
        <Variant>
          <CopyOutlined />
        </Variant>
      </Component>
      <Component name = 'Delete'>
        <Variant>
          <DeleteOutlined />
        </Variant>
      </Component>
      <Component name = 'Bars'>
        <Variant>
          <BarsOutlined />
        </Variant>
      </Component>
      <Component name = 'Bell'>
        <Variant>
          <BellOutlined />
        </Variant>
      </Component>
      <Component name = 'Clear'>
        <Variant>
          <ClearOutlined />
        </Variant>
      </Component>
      <Component name = 'Download'>
        <Variant>
          <DownloadOutlined />
        </Variant>
      </Component>
      <Component name = 'Upload'>
        <Variant>
          <UploadOutlined />
        </Variant>
      </Component>
      <Component name = 'Sync'>
        <Variant>
          <SyncOutlined />
        </Variant>
      </Component>
      <Component name = 'Save'>
        <Variant>
          <SaveOutlined />
        </Variant>
      </Component>
      <Component name = 'Search'>
        <Variant>
          <SearchOutlined />
        </Variant>
      </Component>
      <Component name = 'Settings'>
        <Variant>
          <SettingOutlined />
        </Variant>
      </Component>
      <Component name = 'Paperclip'>
        <Variant>
          <PaperClipOutlined />
        </Variant>
      </Component>
      <Component name = 'Phone'>
        <Variant>
          <PhoneOutlined />
        </Variant>
      </Component>
      <Component name = 'Mail'>
        <Variant>
          <MailOutlined />
        </Variant>
      </Component>
      <Component name = 'Home'>
        <Variant>
          <HomeOutlined />
        </Variant>
      </Component>
      <Component name = 'Contacts'>
        <Variant>
          <ContactsOutlined />
        </Variant>
      </Component>
      <Component name = 'User'>
        <Variant>
          <UserOutlined />
        </Variant>
        <Variant name = 'Add'>
          <UserAddOutlined />
        </Variant>
        <Variant name = 'Remove'>
          <UserDeleteOutlined />
        </Variant>
      </Component>
      <Component name = 'Team'>
        <Variant>
          <TeamOutlined />
        </Variant>
      </Component>
    </Category>
  </Palette>