antd#Affix TypeScript Examples

The following examples show how to use antd#Affix. 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: index.tsx    From leek with Apache License 2.0 6 votes vote down vote up
export function AppLayout({ children }: any) {
  return (
    <Layout>
      <Header />
      <Content
        style={{
          padding: "0 50px",
          marginTop: 64,
        }}
      >
        {children}
        <Affix style={{ position: "fixed", bottom: 13, right: 50 }}>
          <Row>
            <Space>
              <Text code>Leek v{env.LEEK_VERSION}</Text>
              <span>-</span>
              <a
                href="https://tryleek.com"
                target="_blank"
                rel="noopener noreferrer"
              >
                Docs
              </a>
            </Space>
          </Row>
        </Affix>
      </Content>
    </Layout>
  );
}
Example #2
Source File: ActionBar.tsx    From condo with MIT License 6 votes vote down vote up
ActionBar: React.FC<IActionBarProps> = (props) => {
    const { children, hidden } = props
    const barWidthStyle = { width: props.isFormActionBar ? '100%' : 'calc(100% + 48px)' }

    if (hidden) {
        return null
    }

    return (
        <Affix offsetBottom={48} style={barWidthStyle}>
            <Space wrap={true} size={[24, 24]} css={actionBar} style={barWidthStyle}>
                { children }
            </Space>
        </Affix>
    )
}
Example #3
Source File: index.tsx    From tinyhouse with MIT License 5 votes vote down vote up
function App() {
    const [viewer, setViewer] = useState<Viewer>(initialViewer);
    const [logIn, { error }] = useMutation<LogInData, LogInVariables>(LOG_IN, {
        onCompleted: (data) => {
            if (data && data.logIn) {
                setViewer(data.logIn);
                if (data.logIn.token) {
                    sessionStorage.setItem("token", data.logIn.token);
                }
            } else {
                sessionStorage.removeItem("token");
            }
        },
    });
    const logInRef = useRef(logIn);
    useEffect(() => {
        logInRef.current();
    }, []);

    if (!viewer.didRequest && !error) {
        return (
            <Layout className="app-skeleton">
                <AppHeaderSkeleton />
                <div className="app-skeleton__spin-section">
                    <Spin size="large" tip="Launching TinyHouse" />
                </div>
            </Layout>
        );
    }

    const logInErrorBannerElement = error ? (
        <ErrorBanner description="We weren't able to verify if you were logged in. Please try again later!" />
    ) : null;
    return (
        <BrowserRouter>
            <Layout id="app">
                {logInErrorBannerElement}
                <Affix offsetTop={0} className="app__affix-header">
                    <AppHeader viewer={viewer} setViewer={setViewer} />
                </Affix>
                <Routes>
                    <Route path="/" element={<Home />} />
                    <Route path="/host" element={<Host />} />
                    <Route path="/listing/:listingId" element={<Listing />} />
                    <Route
                        path="/listings"
                        element={<Listings title="TinyHouse Listings" />}
                    >
                        <Route
                            path="/listings/:location"
                            element={<Listings title="TinyHouse Listings" />}
                        />
                    </Route>
                    <Route
                        path="/login"
                        element={<Login setViewer={setViewer} />}
                    />
                    <Route
                        path="/user/:userId"
                        element={<User viewer={viewer} />}
                    />
                    <Route path="*" element={<NotFound />} />
                </Routes>
            </Layout>
        </BrowserRouter>
    );
}
Example #4
Source File: TopNotifications.tsx    From condo with MIT License 5 votes vote down vote up
useTopNotificationsHook = (): ITopNotificationHookResult => {
    const [topNotifications, setTopNotifications] = useState<ITopNotification[]>([])
    const addNotification = (notification: ITopNotification) => {
        if (!topNotifications.find(existedNotification => existedNotification.id === notification.id)) {
            setTopNotifications([...topNotifications, notification])
        }
    }
    const removeNotification = (notificationId) => {
        setTopNotifications([...topNotifications.filter(notification => notification.id !== notificationId)])
    }

    const TopNotificationComponent: React.FC = () => {
        const { isSmall } = useLayoutContext()
        return (
            <>
                <Affix>{
                    topNotifications.map(notification => {
                        return (
                            <Alert
                                showIcon
                                icon={(<InfoCircleFilled />)}
                                message={notification.message}
                                type={notification.type}
                                key={notification.id}
                                css={notificationAlert({ isSmall })}
                                action={<Space size={20}>
                                    {
                                        notification.actions.map((action, idx) => {
                                            return (
                                                <Button
                                                    onClick={async () => {
                                                        await action.action()
                                                        removeNotification(notification.id)
                                                    }}
                                                    size={isSmall ? 'middle' : 'large'}
                                                    type={'sberPrimary'}
                                                    secondary={action.secondary}
                                                    key={idx}
                                                >
                                                    {action.title}
                                                </Button>
                                            )
                                        })}
                                </Space>}
                            />
                        )
                    })
                }
                </Affix>
            </>
        )
    }

    return {
        addNotification,
        TopNotificationComponent,
    }
}
Example #5
Source File: index.tsx    From shippo with MIT License 4 votes vote down vote up
CreationLayout: React.FC = () => {
  const [current, setCurrent] = useState('app1')

  const handleClick: MenuClickEventHandler = (event) => {
    console.log('click ', event)
    setCurrent(event.key)
  }
  const onSearch = (value: string) => console.log(value)
  const callback = (key: string) => {
    console.log(key)
  }

  const data = [
    {
      icon: <FormOutlined />,
      title: '投稿',
    },
    {
      icon: <QuestionCircleOutlined />,
      title: '帮助',
    },
  ]

  return (
    <Layout>
      <Header>
        <div style={{ display: 'flex', backgroundColor: '#fff' }}>
          <div style={{ width: '250px', fontSize: '25px', color: '#1890ff', textAlign: 'center' }}>
            Shippo 创作中心
          </div>
          <div style={{ flex: '1 1 0%' }}>
            <span style={{ fontSize: '16px', margin: '0 30px', color: '#757575' }}>
              <CrownOutlined style={{ marginRight: '5px' }} />
              主页
            </span>
          </div>
          <div style={{ padding: '0 50px' }}>
            <Dropdown
              placement="bottomCenter"
              overlay={
                <Menu>
                  <Menu.Item>个人中心</Menu.Item>
                  <Menu.Item>投稿管理</Menu.Item>
                  <Menu.Divider />
                  <Menu.Item>退出登录</Menu.Item>
                </Menu>
              }
            >
              <Avatar size={40} icon={<UserOutlined />} />
            </Dropdown>
          </div>
        </div>
      </Header>
      <Layout>
        <Sider width="250px" theme="light" style={{ paddingTop: '20px' }}>
          <Affix offsetTop={20} onChange={(affixed) => console.log(affixed)}>
            <div style={{ overflow: 'auto', maxHeight: '100vh' }}>
              <div style={{ padding: '10px 25px', textAlign: 'center' }}>
                <Button type="primary" size="large" style={{ width: '120px' }}>
                  投稿
                </Button>
              </div>
              <div style={{ padding: '0 25px' }}>
                <StyledMenu
                  style={{ width: '200px', border: 0, backgroundColor: '#fff' }}
                  defaultSelectedKeys={['1']}
                  mode="inline"
                >
                  <Menu.Item key="1" icon={<HomeOutlined />}>
                    首页
                  </Menu.Item>
                  <SubMenu key="sub1" icon={<FileTextOutlined />} title="内容管理">
                    <Menu.Item key="2">稿件管理</Menu.Item>
                  </SubMenu>
                  <Menu.Item key="5" icon={<TeamOutlined />}>
                    粉丝管理
                  </Menu.Item>
                  <SubMenu key="sub2" icon={<MessageOutlined />} title="互动管理">
                    <Menu.Item key="6">评论管理</Menu.Item>
                  </SubMenu>
                  <Menu.Item key="7" icon={<SettingOutlined />}>
                    创作设置
                  </Menu.Item>
                </StyledMenu>
              </div>
            </div>
          </Affix>
        </Sider>
        <Content>
          <div style={{ padding: '30px 50px' }}>
            <StyledTabs defaultActiveKey="1" style={{ backgroundColor: '#fff' }}>
              <TabPane tab="文章管理" key="1">
                <Tinymce></Tinymce>
              </TabPane>
              <TabPane tab="文章管理" key="2">
                Content of Tab Pane 2
              </TabPane>
              <TabPane tab="文章管理" key="3">
                Content of Tab Pane 3
              </TabPane>
            </StyledTabs>
          </div>
        </Content>
      </Layout>
    </Layout>
  )
}
Example #6
Source File: index.tsx    From shippo with MIT License 4 votes vote down vote up
ReadLayout: React.FC = () => {
  const [current, setCurrent] = useState('app1')

  const handleClick: MenuClickEventHandler = (event) => {
    console.log('click ', event)
    setCurrent(event.key)
  }
  const onSearch = (value: string) => console.log(value)
  const callback = (key: string) => {
    console.log(key)
  }

  const data = [
    {
      icon: <FormOutlined />,
      title: '投稿',
    },
    {
      icon: <QuestionCircleOutlined />,
      title: '帮助',
    },
  ]

  return (
    <Layout>
      <Header>
        <div style={{ display: 'flex' }}>
          <div>
            <Menu
              onClick={handleClick}
              selectedKeys={[current]}
              mode="horizontal"
              style={{ borderBottom: '1px solid #fff' }}
            >
              <Menu.Item key="index" icon={<img width="40px" src={avatar} alt="" />}>
                Shippo
              </Menu.Item>
              <Menu.Item key="app1">导航1</Menu.Item>
              <Menu.Item key="app2">导航2</Menu.Item>
              <Menu.Item key="app3">导航3</Menu.Item>
              <Menu.Item key="app4">导航4</Menu.Item>
            </Menu>
          </div>
          <div style={{ flex: '1 1 0%', backgroundColor: '#fff' }}>
            <Search
              placeholder=""
              allowClear
              onSearch={onSearch}
              style={{ width: '100%', maxWidth: '500px', padding: '12px 10px 0 50px' }}
              size="large"
            />
          </div>
          <div style={{ backgroundColor: '#fff', padding: '0 20px' }}>
            <Space size={30}>
              <Dropdown
                placement="bottomCenter"
                overlay={
                  <Menu>
                    <Menu.Item>个人中心</Menu.Item>
                    <Menu.Item>投稿管理</Menu.Item>
                    <Menu.Divider />
                    <Menu.Item>退出登录</Menu.Item>
                  </Menu>
                }
              >
                <Avatar size={40} icon={<UserOutlined />} />
              </Dropdown>

              <Button type="primary">投稿</Button>
            </Space>
          </div>
        </div>
      </Header>
      <Layout>
        <Sider width="250px" theme="light" style={{ paddingTop: '20px' }}>
          <Affix offsetTop={20} onChange={(affixed) => console.log(affixed)}>
            <Menu
              // onClick={handleClick}
              style={{ width: '250px' }}
              defaultSelectedKeys={['home']}
              mode="inline"
            >
              <Menu.Item key="home" icon={<MailOutlined />}>
                推荐
              </Menu.Item>
              <Menu.Item key="a" icon={<MailOutlined />}>
                动画
              </Menu.Item>
              <Menu.Item key="c" icon={<MailOutlined />}>
                漫画
              </Menu.Item>
              <Menu.Item key="g" icon={<MailOutlined />}>
                游戏
              </Menu.Item>
              <Menu.Item key="n" icon={<MailOutlined />}>
                轻小说
              </Menu.Item>
            </Menu>
          </Affix>
        </Sider>
        <Content>
          <p style={{ height: '200px', padding: '30px' }}>内容</p>
          <p style={{ height: '200px', padding: '30px' }}>内容</p>
          <p style={{ height: '200px', padding: '30px' }}>内容</p>
          <p style={{ height: '200px', padding: '30px' }}>内容</p>
          <p style={{ height: '200px', padding: '30px' }}>内容</p>
          <p style={{ height: '200px', padding: '30px' }}>内容</p>
          <p style={{ height: '200px', padding: '30px' }}>内容</p>
          <p style={{ height: '200px', padding: '30px' }}>内容</p>
          <p style={{ height: '200px', padding: '30px' }}>内容</p>
          <p style={{ height: '200px', padding: '30px' }}>内容</p>
          <p style={{ height: '200px', padding: '30px' }}>内容</p>
          <p style={{ height: '200px', padding: '30px' }}>内容</p>
          <p style={{ height: '200px', padding: '30px' }}>内容</p>
          <p style={{ height: '200px', padding: '30px' }}>内容</p>
          <p style={{ height: '200px', padding: '30px' }}>内容</p>
          <p style={{ height: '200px', padding: '30px' }}>内容</p>
          <p style={{ height: '200px', padding: '30px' }}>内容</p>
          <p style={{ height: '200px', padding: '30px' }}>内容</p>
          <p style={{ height: '200px', padding: '30px' }}>内容</p>
          <p style={{ height: '200px', padding: '30px' }}>内容</p>
          <p style={{ height: '200px', padding: '30px' }}>内容</p>
          <p style={{ height: '200px', padding: '30px' }}>内容</p>
          <p style={{ height: '200px', padding: '30px' }}>内容</p>
          <p style={{ height: '200px', padding: '30px' }}>内容</p>
          <p style={{ height: '200px', padding: '30px' }}>内容</p>
          <p style={{ height: '200px', padding: '30px' }}>内容</p>
          <p style={{ height: '200px', padding: '30px' }}>内容</p>
          <p style={{ height: '200px', padding: '30px' }}>内容</p>
          <p style={{ height: '200px', padding: '30px' }}>内容</p>
        </Content>
        <Sider theme="light" width="300px" style={{ paddingTop: '20px' }}>
          <Affix offsetTop={20} onChange={(affixed) => console.log(affixed)}>
            <div style={{ overflow: 'auto', maxHeight: '100vh' }}>
              <Search
                placeholder="input search text"
                allowClear
                onSearch={onSearch}
                style={{ width: '300px' }}
              />
              <Card title="排行榜" bordered={false} style={{ width: '300px' }}>
                <Tabs defaultActiveKey="1" onChange={callback}>
                  <TabPane tab="日榜" key="1">
                    日榜
                  </TabPane>
                  <TabPane tab="周榜" key="2">
                    周榜
                  </TabPane>
                  <TabPane tab="月榜" key="3">
                    月榜
                  </TabPane>
                </Tabs>
              </Card>
              <Card title="更多" bordered={false} style={{ width: '300px' }}>
                <List
                  itemLayout="horizontal"
                  dataSource={data}
                  split={false}
                  renderItem={(item) => (
                    <List.Item>
                      <List.Item.Meta
                        avatar={<Avatar shape="square" icon={item.icon} />}
                        title={<a href="https://ant.design">{item.title}</a>}
                      />
                    </List.Item>
                  )}
                />
              </Card>
            </div>
          </Affix>
        </Sider>
      </Layout>
    </Layout>
  )
}
Example #7
Source File: index.tsx    From jetlinks-ui-antd with MIT License 4 votes vote down vote up
FieldAccess: React.FC<Props> = props => {
  const initState: State = {
    currentItem: props.data,
    tempFieldAccess: [],
  };
  const [currentItem, setCurrentItem] = useState(initState.currentItem);
  const [tempFieldAccess, setTempFieldAccess] = useState(initState.tempFieldAccess);
  useEffect(() => {
    setCurrentItem(props.data);
    const tempKey: string[] = [];
    ((props.data.current || {}).dataAccesses || []).forEach(e =>
      e.config.fields.forEach((i: string) => tempKey.push(e.action + '-' + i)),
    );
    const temp = (props.data.optionalFields || []).map(field => {
      const actions = (props.data.actions || []).map(a => {
        let key = a.action + '-' + field.name;
        return { checked: tempKey.find(i => i === key) ? false : true, ...a };
      });
      return { actions, ...field } as TempField;
    });
    setTempFieldAccess(temp);
  }, [props.data]);

  const buildAccess = () => {
    let tempAccess = new Map();
    tempFieldAccess.forEach(item => {
      item.actions.forEach(ac => {
        if (ac.checked) return;
        //获取不到就创建
        let fieldAccess =
          tempAccess.get(ac.action) ||
          tempAccess
            .set(ac.action, {
              action: ac.action,
              type: 'DENY_FILEDS',
              config: {
                fields: [],
              },
            })
            .get(ac.action);
        fieldAccess.config.fields.push(item.name);
      });
    });
    currentItem.dataAccesses = [...tempAccess.values()];
    props.save(currentItem);
    message.success('保存成功');
  };

  const checkItem = (field: string, action: string) => {
    const temp = tempFieldAccess.map(item => {
      if (item.name === field) {
        item.actions.map(ac => {
          if (ac.action === action) {
            ac.checked = !ac.checked;
          }
          return ac;
        });
      }
      return item;
    });
    setTempFieldAccess(temp);
  };

  return (
    <Modal
      title={`设置${currentItem.name}字段权限`}
      visible
      width={800}
      onCancel={() => {
        props.close();
        setCurrentItem({});
      }}
      onOk={() => {
        buildAccess();
      }}
    >
      <Alert
        message='描述: 如果角色对当前对象设置了 "新建" 或 "导入" 权限,带*号的必填字段默认设置为“读写”,不可设置为“只读”或“不可见”,否则会造成新建/导入不成功'
        type="info"
      />

      <Divider type="horizontal" />

      <Card bordered={false} id="field-access-card" style={{ height: 300, overflow: 'auto' }}>
        <Col span={20}>
          {tempFieldAccess.map(data => {
            return (
              <Row style={{ width: '100%', marginTop: 20 }} key={data.name} id={data.name}>
                <Col span={4} style={{ fontWeight: 700 }}>
                  <span style={{ fontSize: '14px', color: '#f5222d' }}>* </span>
                  {data.describe}
                </Col>
                <Col span={20}>
                  <Checkbox.Group
                    style={{ width: '100%' }}
                    value={data.actions.filter(e => e.checked === true).map(e => e.action)}
                  >
                    {data.actions.map(item => (
                      <Col key={item.action} span={5}>
                        <Checkbox
                          value={item.action}
                          onClick={() => {
                            checkItem(data.name, item.action);
                          }}
                        >
                          {item.name}
                        </Checkbox>
                      </Col>
                    ))}
                  </Checkbox.Group>
                </Col>
              </Row>
            );
          })}
        </Col>

        <Col span={3} push={1} style={{ height: 600, overflow: 'auto' }}>
          <Affix>
            <Anchor getContainer={() => document.getElementById('field-access-card') || window}>
              {(props.data.optionalFields || []).map(data => (
                <Anchor.Link href={'#' + data.name} title={data.describe} key={data.name} />
              ))}
            </Anchor>
          </Affix>
        </Col>
      </Card>
    </Modal>
  );
}
Example #8
Source File: index.tsx    From jetlinks-ui-antd with MIT License 4 votes vote down vote up
AuthorizationSetting: React.FC<Props> = (props) => {
    const initState: State = {
        permissionList: [],
        // currentList: [],
        mergeData: [],
        loading: true,
        fieldAccessVisible: false,
        checkedPermission: {},
    }

    const [permissionList, setPermissionList] = useState(initState.permissionList);
    // const [currentList, setCurrentList] = useState(initState.currentList);
    const [mergeData, setMergeData] = useState(initState.mergeData);
    const [loading, setLoading] = useState(initState.loading);
    const [fieldAccessVisible, setFieldAccessVisible] = useState(initState.fieldAccessVisible);
    const [checkedPermission, setCheckedPermission] = useState(initState.checkedPermission);

    useEffect(() => {
        setLoading(true);
        getPermissionList();
    }, [props.dimension]);

    const getPermissionList = () => {
        apis.permission.listNoPaging().then(p => {
            if (p.status === 200) {
                if (props.dimension && props.dimension.id) {
                    const params = {
                        terms: { dimensionTarget: props.dimension.id, }
                    };
                    apis.dimensions.queryAutz(encodeQueryParam(params)).then(e => {
                        if (e.status === 200) {

                            const temp = (p.result).map((item: PermissionItem) => {
                                const autz = (e.result).find((i: DimensionsSetting) => i.permission === item.id);
                                //增加all选项
                                if (autz) {
                                    (autz.actions || []).length >= (item.actions || []).length ? autz.actions.push('all') : autz;
                                }
                                return autz ? { 'current': autz, ...item } : item;
                            });
                            setPermissionList(temp);
                        }
                    }).catch(() => {

                    });
                } else {
                    setPermissionList(p.result);
                }
                setLoading(false);
            }
        }).catch(() => {

        });
    }

    const saveFieldsAccess = (permision: PermissionItem) => {
        const index = permissionList.findIndex(p => p.id === permision.id);
        permissionList[index] = permision;
    }

    /*
    * * checkItem:  选择权限
    * * permissionID :权限ID
    * * item :权限
    * */
    const checkItem = (permissionId: string, item?: string) => {
        const permission = permissionList.find(e => e.id === permissionId);
        if (!permission) return;
        const index = permissionList.findIndex(e => e.id === permissionId);
        if (permission.current) {
            let currentActions = permission.current.actions || [];
            //存在current才会有删除的可能
            if (item) {
                const y = currentActions.find((e: string) => e === item);
                if (y) {
                    //如果存在则删除
                    permission.current.actions = currentActions.filter((e: string) => e !== item && e !== 'all');
                } else {
                    currentActions.push(item);
                }
                if (currentActions.length >= (permission.actions || []).length) {
                    currentActions.push('all');
                }
            } else {
                if (currentActions.find((e: string) => e === 'all')) {
                    const { current, ...temp } = permission;
                    permissionList[index] = temp;
                } else {
                    let current = {
                        actions: ['all', ...((permission.actions || []).map((e: any) => e.action))],
                    }
                    permission.current = current
                    permissionList[index] = permission;
                }
            }
        } else {
            if (item) {
                let current = {
                    actions: [item],
                }
                permissionList[index] = { current, ...permission };
            } else {
                let current = {
                    actions: ['all', ...((permission.actions || []).map((e: any) => e.action))],
                }
                permissionList[index] = { current, ...permission };
            }
        }
        setPermissionList(permissionList);
        setMergeData([]);
    }

    const saveAutzSetting = () => {
        //组装成接口所需格式
        setLoading(true);
        const data: any[] = [];
        permissionList.forEach(p => {
            let action = ((p.current || {}).actions || []).filter(e => e !== 'all');
            data.push({
                'id': (p.current || {}).id,
                'permission': p.id,
                'dimensionType': props.dimension.typeId,
                'dimensionTypeName': props.dimension.typeName,
                'dimensionTarget': props.dimension.id,
                'dimensionTargetName': props.dimension.name,
                'state': 1,
                'actions': action,
                'priority': 10,
                'merge': true,
                'dataAccesses': p.dataAccesses,
            });
        });
        apis.dimensions.saveAutzSetting(data).then(e => {
            if (e.status === 200) {
                message.success('保存成功');
            } else {
                message.error('保存失败');
            }
        }).finally(() => {
            setLoading(false);
            setMergeData([]);
            getPermissionList();
        })
    }

    return (
        <Spin spinning={loading}>
            <Card
            >
                <Descriptions title={(props.dimension || {}).name}>
                    <Descriptions.Item>{(props.dimension || {}).describe}</Descriptions.Item>
                </Descriptions>

                <Col span={20}>
                    {

                        permissionList.map(permission => {
                            return (
                                <Card
                                    id={permission.id}
                                    key={permission.id}
                                    title={permission.name}
                                    bordered={false}
                                    extra={
                                        <span>
                                            {
                                                permission.optionalFields &&
                                                <span>
                                                    <Button type="link" onClick={() => { setFieldAccessVisible(true); setCheckedPermission(permission) }}>字段权限</Button>
                                                    <Divider type="vertical" />
                                                </span>
                                            }
                                            {/* <Button type="link" onClick={() => message.warn('开发中')}>数据权限</Button> */}
                                        </span>
                                    }
                                >
                                    <Checkbox.Group style={{ width: '100%' }} value={(permission.current || {}).actions}>
                                        <Col span={6} style={{ marginBottom: 10 }}>
                                            <Checkbox
                                                value="all"
                                                onClick={() => checkItem(permission.id)}
                                                indeterminate={
                                                    ((permission.current || {}).actions || []).length > 0 &&
                                                    ((permission.current || {}).actions || []).length < (permission.actions || []).length
                                                }
                                            >全选</Checkbox>
                                        </Col>

                                        {
                                            (permission.actions || []).map((action: any) => {
                                                return (
                                                    <Col span={6} style={{ marginBottom: 10 }} key={action.action}>
                                                        <Checkbox value={action.action} onClick={() => { checkItem(permission.id, action.action) }}>{action.name}</Checkbox>
                                                    </Col>
                                                )
                                            })
                                        }
                                    </Checkbox.Group>
                                </Card>

                            )
                        }
                        )
                    }

                    <Affix offsetBottom={20} style={{ float: "right" }}>
                        <Button
                            type="primary"
                            onClick={() => { saveAutzSetting() }}
                        >
                            保存
                        </Button>
                    </Affix>
                </Col>
                {/* <Col span={3} push={1} style={{ height: 600, overflow: 'auto' }}>
                    <Affix>
                        <Anchor>
                            {
                                permissionList.map(permission =>
                                    <Anchor.Link href={'#' + permission.id} title={permission.name} key={permission.id} />
                                )
                            }
                        </Anchor>
                    </Affix>
                </Col> */}

            </Card>
            {
                fieldAccessVisible &&
                <FieldAccess
                    close={() => setFieldAccessVisible(false)}
                    data={checkedPermission}
                    save={(item: PermissionItem) => saveFieldsAccess(item)}
                />
            }
        </Spin>

    );
}
Example #9
Source File: index.tsx    From leek with Apache License 2.0 4 votes vote down vote up
IndexPage = () => {
  const { currentApp, currentEnv } = useApplication();
  const [stats, setStats] = useState<any>({});
  const stats_widgets = StatsWidgets(stats);

  // Metadata
  const metricsService = new MetricsService();
  const [seenWorkers, setSeenWorkers] = useState<
    MetricsContextData["seenWorkers"]
  >([]);
  const [seenTasks, setSeenTasks] = useState<MetricsContextData["seenTasks"]>(
    []
  );
  const [processedEvents, setProcessedEvents] =
    useState<MetricsContextData["processedEvents"]>(0);
  const [processedTasks, setProcessedTasks] =
    useState<MetricsContextData["processedTasks"]>(0);
  const [seenStates, setSeenStates] = useState<
    MetricsContextData["seenStates"]
  >([]);
  const [seenTaskStates, setSeenTaskStates] = useState<
    MetricsContextData["seenStates"]
  >([]);
  const [seenRoutingKeys, setSeenRoutingKeys] = useState<
    MetricsContextData["seenRoutingKeys"]
  >([]);
  const [seenQueues, setSeenQueues] = useState<
    MetricsContextData["seenQueues"]
  >([]);
  const [searchDriftLoading, setSearchDriftLoading] = useState<boolean>(true);
  const [searchDrift, setSearchDrift] = useState<any>(null);

  const [timeFilters, setTimeFilters] = useState<any>({
    timestamp_type: "timestamp",
    interval_type: "past",
    offset: 900000,
  });

  function getSearchDrift() {
    if (!currentApp || !currentEnv) return;
    setSearchDriftLoading(true);
    metricsService
      .getSearchDrift(currentApp, currentEnv)
      .then(handleAPIResponse)
      .then((result: any) => {
        setSearchDrift(result);
      }, handleAPIError)
      .catch(handleAPIError)
      .finally(() => setSearchDriftLoading(false));
  }

  function getMetrics() {
    if (!currentApp) return;
    metricsService
      .getBasicMetrics(currentApp, currentEnv, timeFilters)
      .then(handleAPIResponse)
      .then((result: any) => {
        setProcessedEvents(result.aggregations.processed_events.value);
        const processed = result.aggregations.seen_states.buckets.reduce(
          (result, item) => {
            if (!workerStates.includes(item.key)) {
              return result + item.doc_count;
            }
            return result;
          },
          0
        );
        setProcessedTasks(processed);
        setSeenWorkers(result.aggregations.seen_workers.buckets);
        setSeenTasks(result.aggregations.seen_tasks.buckets);
        setSeenStates(result.aggregations.seen_states.buckets);
        setSeenTaskStates(
          tasksStatesDefaults
            .map(
              (obj) =>
                result.aggregations.seen_states.buckets.find(
                  (o) => o.key === obj.key
                ) || obj
            )
            .filter((item) => !workerStates.includes(item.key))
        );
        setSeenRoutingKeys(result.aggregations.seen_routing_keys.buckets);
        setSeenQueues(result.aggregations.seen_queues.buckets);
      }, handleAPIError)
      .catch(handleAPIError);
  }

  useEffect(() => {
    let adapted = {
      SEEN_TASKS: seenTasks.length,
      SEEN_WORKERS: seenWorkers.length,
      PROCESSED_EVENTS: processedEvents,
      PROCESSED_TASKS: processedTasks,
      TASKS: 0,
      EVENTS: 0,
      // Tasks
      QUEUED: 0,
      RECEIVED: 0,
      STARTED: 0,
      SUCCEEDED: 0,
      FAILED: 0,
      REJECTED: 0,
      REVOKED: 0,
      IGNORED: 0,
      RETRY: 0,
      RECOVERED: 0,
      CRITICAL: 0,
      // Worker
      ONLINE: 0,
      HEARTBEAT: 0,
      OFFLINE: 0,
    };
    seenStates.map((task, _) => (adapted[task.key] = task.doc_count));
    setStats(adapted);
  }, [seenTasks, seenWorkers, seenStates]);

  useEffect(() => {
    getMetrics();
    getSearchDrift();
    return () => {
      clearInterval(metricsInterval);
    };
  }, []);

  useEffect(() => {
    // Stop refreshing metadata
    if (metricsInterval) clearInterval(metricsInterval);
    // If no application specified, return
    if (!currentApp) return;

    // Else, get metrics every 10 seconds
    getMetrics();
    getSearchDrift();
    metricsInterval = setInterval(() => {
      getMetrics();
      getSearchDrift();
    }, 10000);
  }, [currentApp, currentEnv, timeFilters]);

  return (
    <>
      <Helmet>
        <html lang="en" />
        <title>Metrics</title>
        <meta name="description" content="Events metrics" />
        <meta name="keywords" content="celery, tasks" />
      </Helmet>

      <Row justify="space-between" align="middle" style={{ marginBottom: 16 }}>
        <Statistic
          loading={searchDriftLoading}
          title={
            <Tooltip title="The time of the latest event processed by leek.">
              <span>Latest Event </span>
              <InfoCircleOutlined />
            </Tooltip>
          }
          value={
            searchDrift && searchDrift.latest_event_timestamp
              ? moment(searchDrift.latest_event_timestamp).format(
                  "MMM D HH:mm:ss Z"
                )
              : ""
          }
          valueStyle={{ fontSize: 17.5 }}
          prefix={<FieldTimeOutlined />}
        />

        <Affix
          style={{
            position: "fixed",
            left: "50%",
            transform: "translate(-50%, 0)",
          }}
        >
          <Row>
            <TimeFilter
              timeFilter={timeFilters}
              onTimeFilterChange={setTimeFilters}
            />
          </Row>
        </Affix>

        <Statistic
          loading={searchDriftLoading}
          title={
            <Tooltip title="How many events in the queue waiting to be indexed.">
              <span>Current Drift </span>
              <InfoCircleOutlined />
            </Tooltip>
          }
          value={
            searchDrift && searchDrift.messages_count
              ? searchDrift.messages_count
              : "0"
          }
          valueStyle={{ fontSize: 17.5 }}
          prefix={<EyeInvisibleOutlined />}
        />
      </Row>

      <Row gutter={16} justify="center" align="middle">
        {stats_widgets.map((widget, idx) => (
          <Col
            lg={12}
            md={12}
            sm={12}
            xs={24}
            key={idx}
            style={{ marginBottom: "16px" }}
          >
            <StickerWidget
              number={widget.number}
              text={widget.text}
              icon={widget.icon}
              tooltip={widget.tooltip}
            />
          </Col>
        ))}
      </Row>
    </>
  );
}
Example #10
Source File: index.tsx    From condo with MIT License 4 votes vote down vote up
TicketPageContent = ({ organization, employee, TicketContent }) => {
    const intl = useIntl()
    const ServerErrorMessage = intl.formatMessage({ id: 'ServerError' })
    const UpdateMessage = intl.formatMessage({ id: 'Edit' })
    const PrintMessage = intl.formatMessage({ id: 'Print' })
    const SourceMessage = intl.formatMessage({ id: 'pages.condo.ticket.field.Source' })
    const TicketAuthorMessage = intl.formatMessage({ id: 'Author' })
    const EmergencyMessage = intl.formatMessage({ id: 'Emergency' })
    const PaidMessage = intl.formatMessage({ id: 'Paid' })
    const WarrantyMessage = intl.formatMessage({ id: 'Warranty' })
    const ReturnedMessage = intl.formatMessage({ id: 'Returned' })
    const ChangedMessage = intl.formatMessage({ id: 'Changed' })
    const TimeHasPassedMessage = intl.formatMessage({ id: 'TimeHasPassed' })
    const DaysShortMessage = intl.formatMessage({ id: 'DaysShort' })
    const HoursShortMessage = intl.formatMessage({ id: 'HoursShort' })
    const MinutesShortMessage = intl.formatMessage({ id: 'MinutesShort' })
    const LessThanMinuteMessage = intl.formatMessage({ id: 'LessThanMinute' })
    const ResidentCannotReadTicketMessage = intl.formatMessage({ id: 'pages.condo.ticket.title.ResidentCannotReadTicket' })

    const router = useRouter()
    const auth = useAuth() as { user: { id: string } }
    const user = get(auth, 'user')
    const { isSmall } = useLayoutContext()

    // NOTE: cast `string | string[]` to `string`
    const { query: { id } } = router as { query: { [key: string]: string } }

    const { refetch: refetchTicket, loading, obj: ticket, error } = Ticket.useObject({
        where: { id: id },
    }, {
        fetchPolicy: 'network-only',
    })
    // TODO(antonal): get rid of separate GraphQL query for TicketChanges
    const ticketChangesResult = TicketChange.useObjects({
        where: { ticket: { id } },
        // TODO(antonal): fix "Module not found: Can't resolve '@condo/schema'"
        // sortBy: [SortTicketChangesBy.CreatedAtDesc],
        // @ts-ignore
        sortBy: ['createdAt_DESC'],
    }, {
        fetchPolicy: 'network-only',
    })

    const { objs: comments, refetch: refetchComments } = TicketComment.useObjects({
        where: { ticket: { id } },
        // @ts-ignore
        sortBy: ['createdAt_DESC'],
    })

    const commentsIds = useMemo(() => map(comments, 'id'), [comments])

    const { objs: ticketCommentFiles, refetch: refetchCommentFiles } = TicketCommentFile.useObjects({
        where: { ticketComment: { id_in: commentsIds } },
        // @ts-ignore
        sortBy: ['createdAt_DESC'],
    })

    const commentsWithFiles = useMemo(() => comments.map(comment => {
        comment.files = ticketCommentFiles.filter(file => file.ticketComment.id === comment.id)

        return comment
    }), [comments, ticketCommentFiles])

    const updateComment = TicketComment.useUpdate({}, () => {
        refetchComments()
        refetchCommentFiles()
    })
    const deleteComment = TicketComment.useSoftDelete({}, () => {
        refetchComments()
    })

    const createCommentAction = TicketComment.useCreate({
        ticket: id,
        user: auth.user && auth.user.id,
    }, () => Promise.resolve())

    const { obj: ticketCommentsTime, refetch: refetchTicketCommentsTime } = TicketCommentsTime.useObject({
        where: {
            ticket: { id: id },
        },
    })
    const {
        obj: userTicketCommentReadTime, refetch: refetchUserTicketCommentReadTime, loading: loadingUserTicketCommentReadTime,
    } = UserTicketCommentReadTime.useObject({
        where: {
            user: { id: user.id },
            ticket: { id: id },
        },
    })
    const createUserTicketCommentReadTime = UserTicketCommentReadTime.useCreate({
        user: user.id,
        ticket: id,
    }, () => refetchUserTicketCommentReadTime())
    const updateUserTicketCommentReadTime = UserTicketCommentReadTime.useUpdate({
        user: user.id,
        ticket: id,
    }, () => refetchUserTicketCommentReadTime())

    const canShareTickets = get(employee, 'role.canShareTickets')
    const TicketTitleMessage = useMemo(() => getTicketTitleMessage(intl, ticket), [ticket])
    const TicketCreationDate = useMemo(() => getTicketCreateMessage(intl, ticket), [ticket])

    const refetchCommentsWithFiles = useCallback(async () => {
        await refetchComments()
        await refetchCommentFiles()
        await refetchTicketCommentsTime()
        await refetchUserTicketCommentReadTime()
    }, [refetchCommentFiles, refetchComments, refetchTicketCommentsTime, refetchUserTicketCommentReadTime])

    const actionsFor = useCallback(comment => {
        const isAuthor = comment.user.id === auth.user.id
        const isAdmin = get(auth, ['user', 'isAdmin'])
        return {
            updateAction: isAdmin || isAuthor ? updateComment : null,
            deleteAction: isAdmin || isAuthor ? deleteComment : null,
        }
    }, [auth, deleteComment, updateComment])

    useEffect(() => {
        const handler = setInterval(refetchCommentsWithFiles, COMMENT_RE_FETCH_INTERVAL)
        return () => {
            clearInterval(handler)
        }
    })

    const isEmergency = get(ticket, 'isEmergency')
    const isPaid = get(ticket, 'isPaid')
    const isWarranty = get(ticket, 'isWarranty')
    const statusReopenedCounter = get(ticket, 'statusReopenedCounter')

    const handleTicketStatusChanged = () => {
        refetchTicket()
        ticketChangesResult.refetch()
    }

    const ticketStatusType = get(ticket, ['status', 'type'])
    const disabledEditButton = useMemo(() => ticketStatusType === CLOSED_STATUS_TYPE, [ticketStatusType])
    const statusUpdatedAt = get(ticket, 'statusUpdatedAt')
    const isResidentTicket = useMemo(() => get(ticket, ['createdBy', 'type']) === RESIDENT, [ticket])
    const canReadByResident = useMemo(() => get(ticket,  'canReadByResident'), [ticket])
    const canCreateComments = useMemo(() => get(auth, ['user', 'isAdmin']) || get(employee, ['role', 'canManageTicketComments']),
        [auth, employee])

    const getTimeSinceCreation = useCallback(() => {
        const diffInMinutes = dayjs().diff(dayjs(statusUpdatedAt), 'minutes')
        const daysHavePassed = dayjs.duration(diffInMinutes, 'minutes').format('D')
        const hoursHavePassed = dayjs.duration(diffInMinutes, 'minutes').format('H')
        const minutesHavePassed = dayjs.duration(diffInMinutes, 'minutes').format('m')

        const timeSinceCreation = compact([
            Number(daysHavePassed) > 0 && DaysShortMessage.replace('${days}', daysHavePassed),
            Number(hoursHavePassed) > 0 && HoursShortMessage.replace('${hours}', hoursHavePassed),
            Number(minutesHavePassed) > 0 && MinutesShortMessage.replace('${minutes}', minutesHavePassed),
        ])

        if (isEmpty(timeSinceCreation)) {
            return LessThanMinuteMessage
        }

        return timeSinceCreation.join(' ')
    }, [DaysShortMessage, HoursShortMessage, LessThanMinuteMessage, MinutesShortMessage, statusUpdatedAt])

    if (!ticket) {
        return (
            <LoadingOrErrorPage title={TicketTitleMessage} loading={loading} error={error ? ServerErrorMessage : null}/>
        )
    }

    return (
        <>
            <Head>
                <title>{TicketTitleMessage}</title>
            </Head>
            <PageWrapper>
                <PageContent>
                    <Row gutter={[0, 40]}>
                        <Col lg={16} xs={24}>
                            <Row gutter={[0, 40]}>
                                <Col span={24}>
                                    <Row gutter={[0, 40]}>
                                        <Col lg={18} xs={24}>
                                            <Row gutter={[0, 20]}>
                                                <Col span={24}>
                                                    <Typography.Title style={{ margin: 0 }} level={1}>{TicketTitleMessage}</Typography.Title>
                                                </Col>
                                                <Col span={24}>
                                                    <Row>
                                                        <Col span={24}>
                                                            <Typography.Text style={TICKET_CREATE_INFO_TEXT_STYLE}>
                                                                <Typography.Text style={TICKET_CREATE_INFO_TEXT_STYLE} type='secondary'>{TicketCreationDate}, {TicketAuthorMessage} </Typography.Text>
                                                                <UserNameField user={get(ticket, ['createdBy'])}>
                                                                    {({ name, postfix }) => (
                                                                        <Typography.Text style={TICKET_CREATE_INFO_TEXT_STYLE}>
                                                                            {name}
                                                                            {postfix && <Typography.Text type='secondary' ellipsis>&nbsp;{postfix}</Typography.Text>}
                                                                        </Typography.Text>
                                                                    )}
                                                                </UserNameField>
                                                            </Typography.Text>
                                                        </Col>
                                                        <Col span={24}>
                                                            <Typography.Text type='secondary' style={TICKET_CREATE_INFO_TEXT_STYLE}>
                                                                {SourceMessage} — {get(ticket, ['source', 'name'], '').toLowerCase()}
                                                            </Typography.Text>
                                                        </Col>
                                                        <Col span={24}>
                                                            {
                                                                !isResidentTicket && !canReadByResident && (
                                                                    <Typography.Text type='secondary' style={TICKET_CREATE_INFO_TEXT_STYLE}>
                                                                        <FormattedMessage
                                                                            id={'pages.condo.ticket.title.CanReadByResident'}
                                                                            values={{
                                                                                canReadByResident: (
                                                                                    <Typography.Text type={'danger'}>
                                                                                        {ResidentCannotReadTicketMessage}
                                                                                    </Typography.Text>
                                                                                ),
                                                                            }}
                                                                        />
                                                                    </Typography.Text>
                                                                )
                                                            }
                                                        </Col>
                                                    </Row>
                                                </Col>
                                            </Row>
                                        </Col>
                                        <Col lg={6} xs={24}>
                                            <Row justify={isSmall ? 'center' : 'end'} gutter={[0, 20]}>
                                                <Col span={24}>
                                                    <TicketStatusSelect
                                                        organization={organization}
                                                        employee={employee}
                                                        ticket={ticket}
                                                        onUpdate={handleTicketStatusChanged}
                                                        loading={loading}
                                                        data-cy={'ticket__status-select'}
                                                    />
                                                </Col>
                                                {
                                                    statusUpdatedAt && (
                                                        <Col>
                                                            <Typography.Paragraph style={TICKET_UPDATE_INFO_TEXT_STYLE}>
                                                                {ChangedMessage}: {dayjs(statusUpdatedAt).format('DD.MM.YY, HH:mm')}
                                                            </Typography.Paragraph>
                                                            <Typography.Paragraph style={TICKET_UPDATE_INFO_TEXT_STYLE} type={'secondary'}>
                                                                {TimeHasPassedMessage.replace('${time}', getTimeSinceCreation())}
                                                            </Typography.Paragraph>
                                                        </Col>
                                                    )
                                                }
                                            </Row>
                                        </Col>
                                    </Row>
                                    <Space direction={'horizontal'} style={{ marginTop: '1.6em ' }}>
                                        {isEmergency && <TicketTag color={TICKET_TYPE_TAG_COLORS.emergency}>{EmergencyMessage.toLowerCase()}</TicketTag>}
                                        {isPaid && <TicketTag color={TICKET_TYPE_TAG_COLORS.paid}>{PaidMessage.toLowerCase()}</TicketTag>}
                                        {isWarranty && <TicketTag color={TICKET_TYPE_TAG_COLORS.warranty}>{WarrantyMessage.toLowerCase()}</TicketTag>}
                                        {
                                            statusReopenedCounter > 0 && (
                                                <TicketTag color={TICKET_TYPE_TAG_COLORS.returned}>
                                                    {ReturnedMessage.toLowerCase()} {statusReopenedCounter > 1 && `(${statusReopenedCounter})`}
                                                </TicketTag>
                                            )
                                        }
                                    </Space>
                                </Col>
                                <TicketContent ticket={ticket}/>
                                <ActionBar>
                                    <Link href={`/ticket/${ticket.id}/update`}>
                                        <Button
                                            disabled={disabledEditButton}
                                            color={'green'}
                                            type={'sberDefaultGradient'}
                                            secondary
                                            icon={<EditFilled />}
                                            data-cy={'ticket__update-link'}
                                        >
                                            {UpdateMessage}
                                        </Button>
                                    </Link>
                                    {
                                        !isSmall && (
                                            <Button
                                                type={'sberDefaultGradient'}
                                                icon={<FilePdfFilled />}
                                                href={`/ticket/${ticket.id}/pdf`}
                                                target={'_blank'}
                                                secondary
                                            >
                                                {PrintMessage}
                                            </Button>
                                        )
                                    }
                                    {
                                        canShareTickets
                                            ? <ShareTicketModal
                                                organization={organization}
                                                date={get(ticket, 'createdAt')}
                                                number={get(ticket, 'number')}
                                                details={get(ticket, 'details')}
                                                id={id}
                                                locale={get(organization, 'country')}
                                            />
                                            : null
                                    }
                                </ActionBar>
                                <TicketChanges
                                    loading={get(ticketChangesResult, 'loading')}
                                    items={get(ticketChangesResult, 'objs')}
                                    total={get(ticketChangesResult, 'count')}
                                />
                            </Row>
                        </Col>
                        <Col lg={7} xs={24} offset={isSmall ? 0 : 1}>
                            <Affix offsetTop={40}>
                                <Comments
                                    ticketCommentsTime={ticketCommentsTime}
                                    userTicketCommentReadTime={userTicketCommentReadTime}
                                    createUserTicketCommentReadTime={createUserTicketCommentReadTime}
                                    updateUserTicketCommentReadTime={updateUserTicketCommentReadTime}
                                    loadingUserTicketCommentReadTime={loadingUserTicketCommentReadTime}
                                    FileModel={TicketCommentFile}
                                    fileModelRelationField={'ticketComment'}
                                    ticket={ticket}
                                    // @ts-ignore
                                    createAction={createCommentAction}
                                    updateAction={updateComment}
                                    refetchComments={refetchCommentsWithFiles}
                                    comments={commentsWithFiles}
                                    canCreateComments={canCreateComments}
                                    actionsFor={actionsFor}
                                />
                            </Affix>
                        </Col>
                    </Row>
                </PageContent>
            </PageWrapper>
        </>
    )
}
Example #11
Source File: index.tsx    From datart with Apache License 2.0 4 votes vote down vote up
EditorPage: FC = () => {
  const [form] = Form.useForm();
  const [container, setContainer] = useState<HTMLDivElement | null>(null);
  const dispatch = useDispatch();
  const [jobType, setJobType] = useState(JobTypes.Email);
  const [fileType, setFileType] = useState<FileTypes[]>(
    DEFAULT_VALUES.type || [],
  );
  const [periodUnit, setPeriodUnit] = useState<TimeModes>(TimeModes.Minute);
  const [periodInput, setPeriodInput] = useState(false);
  const { params } = useRouteMatch<{ scheduleId: string; orgId: string }>();
  const editingSchedule = useSelector(selectEditingSchedule);
  const loading = useSelector(selectScheduleDetailLoading);
  const saveLoding = useSelector(selectSaveLoading);
  const unarchiveLoading = useSelector(selectUnarchiveLoading);
  const deleteLoding = useSelector(selectDeleteLoading);
  const { toDetails } = useToScheduleDetails();
  const isArchived = editingSchedule?.status === 0;
  const t = useI18NPrefix('main.pages.schedulePage.sidebar.editorPage.index');

  const { actions } = useScheduleSlice();
  const { scheduleId, orgId } = params;
  const isAdd = useMemo(() => {
    return scheduleId === 'add';
  }, [scheduleId]);
  const active = useMemo(() => {
    return editingSchedule?.active;
  }, [editingSchedule]);
  const refreshScheduleList = useCallback(() => {
    dispatch(getSchedules(orgId));
  }, [dispatch, orgId]);
  const onFinish = useCallback(() => {
    form.validateFields().then((values: FormValues) => {
      if (!(values?.folderContent && values?.folderContent?.length > 0)) {
        message.error(t('tickToSendContent'));
        return;
      }
      const params = toScheduleSubmitParams(values, orgId);
      if (isAdd) {
        dispatch(
          addSchedule({
            params,
            resolve: (id: string) => {
              message.success(t('addSuccess'));
              toDetails(orgId, id);
              refreshScheduleList();
            },
          }),
        );
      } else {
        dispatch(
          editSchedule({
            scheduleId: editingSchedule?.id as string,
            params: { ...params, id: editingSchedule?.id as string },
            resolve: () => {
              message.success(t('saveSuccess'));
              refreshScheduleList();
            },
          }),
        );
      }
    });
  }, [
    form,
    isAdd,
    orgId,
    dispatch,
    editingSchedule,
    refreshScheduleList,
    toDetails,
    t,
  ]);

  const onResetForm = useCallback(() => {
    form.resetFields();
    setJobType(DEFAULT_VALUES.jobType as JobTypes);
    setPeriodUnit(TimeModes.Minute);
    setPeriodInput(false);
    setFileType([FileTypes.Image]);
    dispatch(actions.clearEditingSchedule);
  }, [form, dispatch, actions?.clearEditingSchedule]);

  useEffect(() => {
    dispatch(getFolders(orgId));
    if (scheduleId === 'add') {
      onResetForm();
    } else if (scheduleId) {
      dispatch(getScheduleDetails(scheduleId));
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [scheduleId, dispatch]);
  const onJobTypeChange = useCallback(
    (v: JobTypes) => {
      setJobType(v);
      setFileType([FileTypes.Image]);
      form.setFieldsValue({
        type: [FileTypes.Image],
        imageWidth: DEFAULT_VALUES.imageWidth,
      });
    },
    [form],
  );
  const onFileTypeChange = useCallback(
    (v: FileTypes[]) => {
      setFileType(v);
      form.setFieldsValue({
        imageWidth:
          v && v.includes(FileTypes.Image)
            ? DEFAULT_VALUES.imageWidth
            : undefined,
      });
    },
    [form],
  );
  const onPeriodUnitChange = useCallback(
    (v: TimeModes) => {
      setPeriodUnit(v);
      switch (v) {
        case TimeModes.Minute:
          form.setFieldsValue({ minute: 10 });
          break;
      }
    },
    [form],
  );
  const onPeriodInputChange = useCallback(
    (v: boolean) => {
      if (v) {
        form.setFieldsValue({
          cronExpression: getCronExpressionByPartition(form.getFieldsValue()),
        });
      } else {
        const timeValues = getTimeValues(form.getFieldValue('cronExpression'));
        form.setFieldsValue(timeValues);
        setPeriodUnit(timeValues?.periodUnit);
      }
      setPeriodInput(v);
    },
    [form],
  );

  const unarchive = useCallback(() => {
    dispatch(
      unarchiveSchedule({
        id: editingSchedule!.id,
        resolve: () => {
          message.success(t('restoredSuccess'));
          toDetails(orgId);
        },
      }),
    );
  }, [dispatch, toDetails, orgId, editingSchedule, t]);

  const del = useCallback(
    archive => () => {
      dispatch(
        deleteSchedule({
          id: editingSchedule!.id,
          archive,
          resolve: () => {
            message.success(
              `${t('success')}${archive ? t('moveToTrash') : t('delete')}`,
            );
            toDetails(orgId);
          },
        }),
      );
    },
    [dispatch, toDetails, orgId, editingSchedule, t],
  );

  useEffect(() => {
    if (editingSchedule) {
      const _type = editingSchedule?.type as JobTypes,
        echoValues = toEchoFormValues(editingSchedule);
      form.setFieldsValue(echoValues);
      setFileType(echoValues?.type as FileTypes[]);
      setJobType(_type);
      setPeriodUnit(echoValues?.periodUnit as TimeModes);
      setPeriodInput(!!echoValues?.setCronExpressionManually);
    }
    return () => {
      setJobType(DEFAULT_VALUES.jobType as JobTypes);
      dispatch(actions.clearEditingSchedule);
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [editingSchedule]);

  return (
    <Container ref={setContainer}>
      <Affix offsetTop={0} target={() => container}>
        <DetailPageHeader
          title={isAdd ? t('newTimedTask') : editingSchedule?.name}
          actions={
            isArchived ? (
              <>
                <Popconfirm title={t('sureToRestore')} onConfirm={unarchive}>
                  <Button loading={unarchiveLoading}>{t('restore')}</Button>
                </Popconfirm>
                <Popconfirm title={t('sureToDelete')} onConfirm={del(false)}>
                  <Button loading={deleteLoding} danger>
                    {t('delete')}
                  </Button>
                </Popconfirm>
              </>
            ) : (
              <>
                <Tooltip
                  placement="bottom"
                  title={active ? t('allowModificationAfterStopping') : ''}
                >
                  <Button
                    loading={saveLoding}
                    type="primary"
                    onClick={form.submit}
                    disabled={active}
                  >
                    {t('save')}
                  </Button>
                </Tooltip>
                {!isAdd && (
                  <Tooltip
                    placement="bottom"
                    title={active ? t('allowMoveAfterStopping') : ''}
                  >
                    <Popconfirm
                      title={t('sureMoveRecycleBin')}
                      onConfirm={del(true)}
                    >
                      <Button loading={deleteLoding} disabled={active} danger>
                        {t('moveToTrash')}
                      </Button>
                    </Popconfirm>
                  </Tooltip>
                )}
              </>
            )
          }
        />
      </Affix>
      <EditorWrapper>
        <Spin spinning={loading}>
          <Form
            wrapperCol={{ span: 19 }}
            labelCol={{ span: 5 }}
            initialValues={DEFAULT_VALUES}
            form={form}
            onFinish={onFinish}
            scrollToFirstError
          >
            <FormAreaWrapper>
              {!isAdd && editingSchedule?.id ? (
                <ScheduleErrorLog scheduleId={editingSchedule?.id} />
              ) : null}
              <FormCard title={t('basicSettings')}>
                <FormWrapper>
                  <BasicBaseForm
                    isAdd={isAdd}
                    orgId={orgId}
                    onJobTypeChange={onJobTypeChange}
                    initialName={editingSchedule?.name}
                    periodUnit={periodUnit}
                    onPeriodUnitChange={onPeriodUnitChange}
                    periodInput={periodInput}
                    onPeriodInputChange={onPeriodInputChange}
                  />
                </FormWrapper>
              </FormCard>

              {jobType === JobTypes.Email ? (
                <FormCard title={t('emailSetting')}>
                  <FormWrapper>
                    <EmailSettingForm
                      fileType={fileType}
                      onFileTypeChange={onFileTypeChange}
                    />
                  </FormWrapper>
                </FormCard>
              ) : (
                <FormCard title={t('enterpriseWeChatSettings')}>
                  <FormWrapper>
                    <WeChartSetttingForm />
                  </FormWrapper>
                </FormCard>
              )}
              <FormCard title={t('sendContentSettings')}>
                <FormWrapper>
                  <SendContentForm />
                </FormWrapper>
              </FormCard>
            </FormAreaWrapper>
          </Form>
        </Spin>
      </EditorWrapper>
    </Container>
  );
}