@ant-design/icons#MessageOutlined TypeScript Examples

The following examples show how to use @ant-design/icons#MessageOutlined. 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: sidemenu.tsx    From ui with GNU Affero General Public License v3.0 5 votes vote down vote up
sideMenu: SideMenuElement[] = [
  {
    key: 'home',
    name: 'admin:home',
    href: '/admin',
    icon: <HomeOutlined />,
  },
  {
    key: 'profile',
    name: 'admin:profile',
    href: '/admin/profile',
    icon: <UserOutlined />,
  },
  {
    key: 'public',
    name: 'admin:forms',
    group: true,
    role: 'admin',
    items: [
      {
        key: 'forms',
        name: 'admin:forms',
        href: '/admin/forms',
        icon: <MessageOutlined />,
      },
    ],
  },
  {
    key: 'administration',
    name: 'admin:administration',
    group: true,
    role: 'superuser',
    items: [
      {
        key: 'users',
        name: 'admin:users',
        href: '/admin/users',
        icon: <TeamOutlined />,
      },
    ],
  },
]
Example #2
Source File: Header.tsx    From react_admin with MIT License 4 votes vote down vote up
Headers: React.FC<Iprops> = (props) => {
  const { collapsed } = props;
  const [fullScreen, setFullScreen] = useState(false); // 当前是否是全屏状态
  // 进入全屏
  const requestFullScreen = useCallback(() => {
    const element: HTMLElement & Element = document.documentElement;
    // 判断各种浏览器,找到正确的方法
    const requestMethod =
      element.requestFullscreen || // W3C
      element.webkitRequestFullscreen || // Chrome等
      element.mozRequestFullScreen || // FireFox
      element.msRequestFullscreen; // IE11
    if (requestMethod) {
      requestMethod.call(element);
    }
    setFullScreen(true);
  }, []);

  // 退出登录
  const onMenuClick = useCallback(
    (e) => {
      // 退出按钮被点击
      if (e.key === "logout") {
        props.onLogout();
      }
    },
    [props]
  );

  // 退出全屏
  const exitFullScreen = useCallback(() => {
    // 判断各种浏览器,找到正确的方法
    const element: Document & Element = document;
    const exitMethod =
      element.exitFullscreen || // W3C
      element.mozCancelFullScreen || // firefox
      element.webkitExitFullscreen || // Chrome等
      element.msExitFullscreen; // IE11

    if (exitMethod) {
      exitMethod.call(document);
    }
    setFullScreen(false);
  }, []);
  const toggle = () => {
    props.setColl(!collapsed);
  };
  return (
    <Header className="site-layout-background header" style={{ padding: 0 }}>
      <Tooltip title={props.collapsed ? "展开菜单" : "收起菜单"}>
        {React.createElement(
          collapsed ? MenuUnfoldOutlined : MenuFoldOutlined,
          {
            className: "trigger",
            onClick: toggle,
          }
        )}
      </Tooltip>
      <div className="rightBox">
        <Tooltip placement="bottom" title={fullScreen ? "退出全屏" : "全屏"}>
          <div className="full all_center">
            {fullScreen ? (
              <FullscreenExitOutlined
                className="icon"
                onClick={exitFullScreen}
              />
            ) : (
              <FullscreenOutlined
                className="icon"
                onClick={requestFullScreen}
              />
            )}
          </div>
        </Tooltip>
        <Dropdown
          overlay={
            <Menu className="menu" selectedKeys={[]} onClick={onMenuClick}>
              <Menu.Item key="user">
                <Link to="/home/user/admin">
                  <UserOutlined />
                  个人中心
                </Link>
              </Menu.Item>
              <Menu.Item key="message">
                <Link to="/home/user/level">
                  <MessageOutlined />
                  个人设置
                </Link>
              </Menu.Item>
              <Menu.Divider />
              <Menu.Item key="logout">
                <LogoutOutlined />
                退出登录
              </Menu.Item>
            </Menu>
          }
          placement="bottomRight"
        >
          <div className="userhead all_center">
            <SmileOutlined />
            <span className="username">admin</span>
          </div>
        </Dropdown>
      </div>
    </Header>
  );
}
Example #3
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 #4
Source File: commandPaletteLogic.ts    From posthog-foss with MIT License 4 votes vote down vote up
commandPaletteLogic = kea<
    commandPaletteLogicType<
        Command,
        CommandFlow,
        CommandRegistrations,
        CommandResult,
        CommandResultDisplayable,
        RegExpCommandPairs
    >
>({
    path: ['lib', 'components', 'CommandPalette', 'commandPaletteLogic'],
    connect: {
        actions: [personalAPIKeysLogic, ['createKey']],
        values: [teamLogic, ['currentTeam'], userLogic, ['user']],
        logic: [preflightLogic], // used in afterMount, which does not auto-connect
    },
    actions: {
        hidePalette: true,
        showPalette: true,
        togglePalette: true,
        setInput: (input: string) => ({ input }),
        onArrowUp: true,
        onArrowDown: (maxIndex: number) => ({ maxIndex }),
        onMouseEnterResult: (index: number) => ({ index }),
        onMouseLeaveResult: true,
        executeResult: (result: CommandResult) => ({ result }),
        activateFlow: (flow: CommandFlow | null) => ({ flow }),
        backFlow: true,
        registerCommand: (command: Command) => ({ command }),
        deregisterCommand: (commandKey: string) => ({ commandKey }),
        setCustomCommand: (commandKey: string) => ({ commandKey }),
        deregisterScope: (scope: string) => ({ scope }),
    },
    reducers: {
        isPaletteShown: [
            false,
            {
                hidePalette: () => false,
                showPalette: () => true,
                togglePalette: (previousState) => !previousState,
            },
        ],
        keyboardResultIndex: [
            0,
            {
                setInput: () => 0,
                executeResult: () => 0,
                activateFlow: () => 0,
                backFlow: () => 0,
                onArrowUp: (previousIndex) => (previousIndex > 0 ? previousIndex - 1 : 0),
                onArrowDown: (previousIndex, { maxIndex }) => (previousIndex < maxIndex ? previousIndex + 1 : maxIndex),
            },
        ],
        hoverResultIndex: [
            null as number | null,
            {
                activateFlow: () => null,
                backFlow: () => null,
                onMouseEnterResult: (_, { index }) => index,
                onMouseLeaveResult: () => null,
                onArrowUp: () => null,
                onArrowDown: () => null,
            },
        ],
        input: [
            '',
            {
                setInput: (_, { input }) => input,
                activateFlow: () => '',
                backFlow: () => '',
                executeResult: () => '',
            },
        ],
        activeFlow: [
            null as CommandFlow | null,
            {
                activateFlow: (currentFlow, { flow }) =>
                    flow ? { ...flow, scope: flow.scope ?? currentFlow?.scope, previousFlow: currentFlow } : null,
                backFlow: (currentFlow) => currentFlow?.previousFlow ?? null,
            },
        ],
        rawCommandRegistrations: [
            {} as CommandRegistrations,
            {
                registerCommand: (commands, { command }) => {
                    return { ...commands, [command.key]: command }
                },
                deregisterCommand: (commands, { commandKey }) => {
                    const { [commandKey]: _, ...cleanedCommands } = commands // eslint-disable-line
                    return cleanedCommands
                },
            },
        ],
    },

    listeners: ({ actions, values }) => ({
        showPalette: () => {
            posthog.capture('palette shown', { isMobile: isMobile() })
        },
        togglePalette: () => {
            if (values.isPaletteShown) {
                posthog.capture('palette shown', { isMobile: isMobile() })
            }
        },
        executeResult: ({ result }: { result: CommandResult }) => {
            if (result.executor === true) {
                actions.activateFlow(null)
                actions.hidePalette()
            } else {
                const possibleFlow = result.executor?.() || null
                actions.activateFlow(possibleFlow)
                if (!possibleFlow) {
                    actions.hidePalette()
                }
            }
            // Capture command execution, without useless data
            // eslint-disable-next-line @typescript-eslint/no-unused-vars
            const { icon, index, ...cleanedResult }: Record<string, any> = result
            // eslint-disable-next-line @typescript-eslint/no-unused-vars
            const { resolver, ...cleanedCommand } = cleanedResult.source
            cleanedResult.source = cleanedCommand
            cleanedResult.isMobile = isMobile()
            posthog.capture('palette command executed', cleanedResult)
        },
        deregisterScope: ({ scope }) => {
            for (const command of Object.values(values.commandRegistrations)) {
                if (command.scope === scope) {
                    actions.deregisterCommand(command.key)
                }
            }
        },
        setInput: async ({ input }, breakpoint) => {
            await breakpoint(300)
            if (input.length > 8) {
                const response = await api.get('api/person/?key_identifier=' + encodeURIComponent(input))
                const person = response.results[0]
                if (person) {
                    actions.registerCommand({
                        key: `person-${person.distinct_ids[0]}`,
                        resolver: [
                            {
                                icon: UserOutlined,
                                display: `View person ${input}`,
                                executor: () => {
                                    const { push } = router.actions
                                    push(urls.person(person.distinct_ids[0]))
                                },
                            },
                        ],
                        scope: GLOBAL_COMMAND_SCOPE,
                    })
                }
            }
        },
    }),
    selectors: {
        isSqueak: [
            (selectors) => [selectors.input],
            (input: string) => {
                return input.trim().toLowerCase() === 'squeak'
            },
        ],
        activeResultIndex: [
            (selectors) => [selectors.keyboardResultIndex, selectors.hoverResultIndex],
            (keyboardResultIndex: number, hoverResultIndex: number | null) => {
                return hoverResultIndex ?? keyboardResultIndex
            },
        ],
        commandRegistrations: [
            (selectors) => [
                selectors.rawCommandRegistrations,
                dashboardsModel.selectors.nameSortedDashboards,
                teamLogic.selectors.currentTeam,
            ],
            (rawCommandRegistrations: CommandRegistrations, dashboards: DashboardType[]): CommandRegistrations => ({
                ...rawCommandRegistrations,
                custom_dashboards: {
                    key: 'custom_dashboards',
                    resolver: dashboards.map((dashboard: DashboardType) => ({
                        key: `dashboard_${dashboard.id}`,
                        icon: LineChartOutlined,
                        display: `Go to Dashboard: ${dashboard.name}`,
                        executor: () => {
                            const { push } = router.actions
                            push(urls.dashboard(dashboard.id))
                        },
                    })),
                    scope: GLOBAL_COMMAND_SCOPE,
                },
            }),
        ],
        regexpCommandPairs: [
            (selectors) => [selectors.commandRegistrations],
            (commandRegistrations: CommandRegistrations) => {
                const array: RegExpCommandPairs = []
                for (const command of Object.values(commandRegistrations)) {
                    if (command.prefixes) {
                        array.push([new RegExp(`^\\s*(${command.prefixes.join('|')})(?:\\s+(.*)|$)`, 'i'), command])
                    } else {
                        array.push([null, command])
                    }
                }
                return array
            },
        ],
        commandSearchResults: [
            (selectors) => [
                selectors.isPaletteShown,
                selectors.regexpCommandPairs,
                selectors.input,
                selectors.activeFlow,
                selectors.isSqueak,
            ],
            (
                isPaletteShown: boolean,
                regexpCommandPairs: RegExpCommandPairs,
                argument: string,
                activeFlow: CommandFlow | null,
                isSqueak: boolean
            ) => {
                if (!isPaletteShown || isSqueak) {
                    return []
                }
                if (activeFlow) {
                    return resolveCommand(activeFlow, argument)
                }
                let directResults: CommandResult[] = []
                let prefixedResults: CommandResult[] = []
                for (const [regexp, command] of regexpCommandPairs) {
                    if (regexp) {
                        const match = argument.match(regexp)
                        if (match && match[1]) {
                            prefixedResults = [...prefixedResults, ...resolveCommand(command, match[2], match[1])]
                        }
                    }
                    directResults = [...directResults, ...resolveCommand(command, argument)]
                }
                const allResults = directResults.concat(prefixedResults)
                let fusableResults: CommandResult[] = []
                let guaranteedResults: CommandResult[] = []
                for (const result of allResults) {
                    if (result.guarantee) {
                        guaranteedResults.push(result)
                    } else {
                        fusableResults.push(result)
                    }
                }
                fusableResults = uniqueBy(fusableResults, (result) => result.display)
                guaranteedResults = uniqueBy(guaranteedResults, (result) => result.display)
                const fusedResults = argument
                    ? new Fuse(fusableResults, {
                          keys: ['display', 'synonyms'],
                      })
                          .search(argument)
                          .slice(0, RESULTS_MAX)
                          .map((result) => result.item)
                    : sample(fusableResults, RESULTS_MAX - guaranteedResults.length)
                return guaranteedResults.concat(fusedResults)
            },
        ],
        commandSearchResultsGrouped: [
            (selectors) => [selectors.commandSearchResults, selectors.activeFlow],
            (commandSearchResults: CommandResult[], activeFlow: CommandFlow | null) => {
                const resultsGrouped: {
                    [scope: string]: CommandResult[]
                } = {}
                if (activeFlow) {
                    resultsGrouped[activeFlow.scope ?? '?'] = []
                }
                for (const result of commandSearchResults) {
                    const scope: string = result.source.scope ?? '?'
                    if (!(scope in resultsGrouped)) {
                        resultsGrouped[scope] = []
                    } // Ensure there's an array to push to
                    resultsGrouped[scope].push({ ...result })
                }
                let rollingGroupIndex = 0
                let rollingResultIndex = 0
                const resultsGroupedInOrder: [string, CommandResultDisplayable[]][] = []
                for (const [group, results] of Object.entries(resultsGrouped)) {
                    resultsGroupedInOrder.push([group, []])
                    for (const result of results) {
                        resultsGroupedInOrder[rollingGroupIndex][1].push({ ...result, index: rollingResultIndex++ })
                    }
                    rollingGroupIndex++
                }
                return resultsGroupedInOrder
            },
        ],
    },

    events: ({ actions }) => ({
        afterMount: () => {
            const { push } = router.actions

            const goTo: Command = {
                key: 'go-to',
                scope: GLOBAL_COMMAND_SCOPE,
                prefixes: ['open', 'visit'],
                resolver: [
                    {
                        icon: FundOutlined,
                        display: 'Go to Dashboards',
                        executor: () => {
                            push(urls.dashboards())
                        },
                    },
                    {
                        icon: RiseOutlined,
                        display: 'Go to Insights',
                        executor: () => {
                            push(urls.savedInsights())
                        },
                    },
                    {
                        icon: RiseOutlined,
                        display: 'Go to Trends',
                        executor: () => {
                            // TODO: Don't reset insight on change
                            push(urls.insightNew({ insight: InsightType.TRENDS }))
                        },
                    },
                    {
                        icon: FunnelPlotOutlined,
                        display: 'Go to Funnels',
                        executor: () => {
                            // TODO: Don't reset insight on change
                            push(urls.insightNew({ insight: InsightType.FUNNELS }))
                        },
                    },
                    {
                        icon: GatewayOutlined,
                        display: 'Go to Retention',
                        executor: () => {
                            // TODO: Don't reset insight on change
                            push(urls.insightNew({ insight: InsightType.RETENTION }))
                        },
                    },
                    {
                        icon: InteractionOutlined,
                        display: 'Go to Paths',
                        executor: () => {
                            // TODO: Don't reset insight on change
                            push(urls.insightNew({ insight: InsightType.PATHS }))
                        },
                    },
                    {
                        icon: ContainerOutlined,
                        display: 'Go to Events',
                        executor: () => {
                            push(urls.events())
                        },
                    },
                    {
                        icon: AimOutlined,
                        display: 'Go to Actions',
                        executor: () => {
                            push(urls.actions())
                        },
                    },
                    {
                        icon: UserOutlined,
                        display: 'Go to Persons',
                        synonyms: ['people'],
                        executor: () => {
                            push(urls.persons())
                        },
                    },
                    {
                        icon: UsergroupAddOutlined,
                        display: 'Go to Cohorts',
                        executor: () => {
                            push(urls.cohorts())
                        },
                    },
                    {
                        icon: FlagOutlined,
                        display: 'Go to Feature Flags',
                        synonyms: ['feature flags', 'a/b tests'],
                        executor: () => {
                            push(urls.featureFlags())
                        },
                    },
                    {
                        icon: MessageOutlined,
                        display: 'Go to Annotations',
                        executor: () => {
                            push(urls.annotations())
                        },
                    },
                    {
                        icon: TeamOutlined,
                        display: 'Go to Team members',
                        synonyms: ['organization', 'members', 'invites', 'teammates'],
                        executor: () => {
                            push(urls.organizationSettings())
                        },
                    },
                    {
                        icon: ProjectOutlined,
                        display: 'Go to Project settings',
                        executor: () => {
                            push(urls.projectSettings())
                        },
                    },
                    {
                        icon: SmileOutlined,
                        display: 'Go to My settings',
                        synonyms: ['account'],
                        executor: () => {
                            push(urls.mySettings())
                        },
                    },
                    {
                        icon: ApiOutlined,
                        display: 'Go to Plugins',
                        synonyms: ['integrations'],
                        executor: () => {
                            push(urls.plugins())
                        },
                    },
                    {
                        icon: DatabaseOutlined,
                        display: 'Go to System status page',
                        synonyms: ['redis', 'celery', 'django', 'postgres', 'backend', 'service', 'online'],
                        executor: () => {
                            push(urls.systemStatus())
                        },
                    },
                    {
                        icon: PlusOutlined,
                        display: 'Create action',
                        executor: () => {
                            push(urls.createAction())
                        },
                    },
                    {
                        icon: LogoutOutlined,
                        display: 'Log out',
                        executor: () => {
                            userLogic.actions.logout()
                        },
                    },
                ],
            }

            const debugClickhouseQueries: Command = {
                key: 'debug-clickhouse-queries',
                scope: GLOBAL_COMMAND_SCOPE,
                resolver:
                    userLogic.values.user?.is_staff ||
                    userLogic.values.user?.is_impersonated ||
                    preflightLogic.values.preflight?.is_debug ||
                    preflightLogic.values.preflight?.instance_preferences?.debug_queries
                        ? {
                              icon: PlusOutlined,
                              display: 'Debug queries (ClickHouse)',
                              executor: () => {
                                  debugCHQueries()
                              },
                          }
                        : [],
            }

            const calculator: Command = {
                key: 'calculator',
                scope: GLOBAL_COMMAND_SCOPE,
                resolver: (argument) => {
                    // don't try evaluating if there's no argument or if it's a plain number already
                    if (!argument || !isNaN(+argument)) {
                        return null
                    }
                    try {
                        const result = +Parser.evaluate(argument)
                        return isNaN(result)
                            ? null
                            : {
                                  icon: CalculatorOutlined,
                                  display: `= ${result}`,
                                  guarantee: true,
                                  executor: () => {
                                      copyToClipboard(result.toString(), 'calculation result')
                                  },
                              }
                    } catch {
                        return null
                    }
                },
            }

            const openUrls: Command = {
                key: 'open-urls',
                scope: GLOBAL_COMMAND_SCOPE,
                prefixes: ['open', 'visit'],
                resolver: (argument) => {
                    const results: CommandResultTemplate[] = (teamLogic.values.currentTeam?.app_urls ?? []).map(
                        (url: string) => ({
                            icon: LinkOutlined,
                            display: `Open ${url}`,
                            synonyms: [`Visit ${url}`],
                            executor: () => {
                                open(url)
                            },
                        })
                    )
                    if (argument && isURL(argument)) {
                        results.push({
                            icon: LinkOutlined,
                            display: `Open ${argument}`,
                            synonyms: [`Visit ${argument}`],
                            executor: () => {
                                open(argument)
                            },
                        })
                    }
                    results.push({
                        icon: LinkOutlined,
                        display: 'Open PostHog Docs',
                        synonyms: ['technical documentation'],
                        executor: () => {
                            open('https://posthog.com/docs')
                        },
                    })
                    return results
                },
            }

            const createPersonalApiKey: Command = {
                key: 'create-personal-api-key',
                scope: GLOBAL_COMMAND_SCOPE,
                resolver: {
                    icon: KeyOutlined,
                    display: 'Create Personal API Key',
                    executor: () => ({
                        instruction: 'Give your key a label',
                        icon: TagOutlined,
                        scope: 'Creating Personal API Key',
                        resolver: (argument) => {
                            if (argument?.length) {
                                return {
                                    icon: KeyOutlined,
                                    display: `Create Key "${argument}"`,
                                    executor: () => {
                                        personalAPIKeysLogic.actions.createKey(argument)
                                        push(urls.mySettings(), {}, 'personal-api-keys')
                                    },
                                }
                            }
                            return null
                        },
                    }),
                },
            }

            const createDashboard: Command = {
                key: 'create-dashboard',
                scope: GLOBAL_COMMAND_SCOPE,
                resolver: {
                    icon: FundOutlined,
                    display: 'Create Dashboard',
                    executor: () => ({
                        instruction: 'Name your new dashboard',
                        icon: TagOutlined,
                        scope: 'Creating Dashboard',
                        resolver: (argument) => {
                            if (argument?.length) {
                                return {
                                    icon: FundOutlined,
                                    display: `Create Dashboard "${argument}"`,
                                    executor: () => {
                                        dashboardsModel.actions.addDashboard({ name: argument, show: true })
                                    },
                                }
                            }
                            return null
                        },
                    }),
                },
            }

            const shareFeedback: Command = {
                key: 'share-feedback',
                scope: GLOBAL_COMMAND_SCOPE,
                resolver: {
                    icon: CommentOutlined,
                    display: 'Share Feedback',
                    synonyms: ['send opinion', 'ask question', 'message posthog', 'github issue'],
                    executor: () => ({
                        scope: 'Sharing Feedback',
                        resolver: [
                            {
                                display: 'Send Message Directly to PostHog',
                                icon: CommentOutlined,
                                executor: () => ({
                                    instruction: "What's on your mind?",
                                    icon: CommentOutlined,
                                    resolver: (argument) => ({
                                        icon: SendOutlined,
                                        display: 'Send',
                                        executor: !argument?.length
                                            ? undefined
                                            : () => {
                                                  posthog.capture('palette feedback', { message: argument })
                                                  return {
                                                      resolver: {
                                                          icon: CheckOutlined,
                                                          display: 'Message Sent!',
                                                          executor: true,
                                                      },
                                                  }
                                              },
                                    }),
                                }),
                            },
                            {
                                icon: VideoCameraOutlined,
                                display: 'Schedule Quick Call',
                                executor: () => {
                                    open('https://calendly.com/posthog-feedback')
                                },
                            },
                            {
                                icon: ExclamationCircleOutlined,
                                display: 'Create GitHub Issue',
                                executor: () => {
                                    open('https://github.com/PostHog/posthog/issues/new/choose')
                                },
                            },
                        ],
                    }),
                },
            }

            actions.registerCommand(goTo)
            actions.registerCommand(openUrls)
            actions.registerCommand(debugClickhouseQueries)
            actions.registerCommand(calculator)
            actions.registerCommand(createPersonalApiKey)
            actions.registerCommand(createDashboard)
            actions.registerCommand(shareFeedback)
        },
        beforeUnmount: () => {
            actions.deregisterCommand('go-to')
            actions.deregisterCommand('open-urls')
            actions.deregisterCommand('debug-clickhouse-queries')
            actions.deregisterCommand('calculator')
            actions.deregisterCommand('create-personal-api-key')
            actions.deregisterCommand('create-dashboard')
            actions.deregisterCommand('share-feedback')
        },
    }),
})
Example #5
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 #6
Source File: index.tsx    From foodie with MIT License 4 votes vote down vote up
Messages: React.FC<{ isAuth: boolean; }> = ({ isAuth }) => {
    const id = useSelector((state: IRootReducer) => state.auth.id);
    const [isMessagesOpen, setMessagesOpen] = useState(false);
    const [isLoading, setLoading] = useState(false);
    const [offset, setOffset] = useState(0);
    const [error, setError] = useState<IError | null>(null);
    const [messages, setMessages] = useState<IMessage[]>([]);
    const [count, setCount] = useState(0);
    const dispatch = useDispatch();
    const composeModal = useModal();
    const history = useHistory();
    const isMessagesOpenRef = useRef(isMessagesOpen);


    useEffect(() => {
        isMessagesOpenRef.current = isMessagesOpen;
    }, [isMessagesOpen]);

    useEffect(() => {
        if (isMessagesOpen) {
            fetchMessages();
        }

        socket.on('newMessage', (newMessage: IMessage) => {
            setMessages((prevMessages) => updateNewMessages(prevMessages, newMessage));
            setCount((prevCount) => newMessage.isOwnMessage ? 0 : prevCount + 1);
        });

        document.addEventListener('click', handleClickOutside);

        if (isAuth) {
            getUnreadMessages()
                .then((result) => {
                    setCount(result.count);
                });
        }

        return () => {
            socket.emit('userDisconnect', id);
        }
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, []);

    const updateNewMessages = (prevMessages: IMessage[], newMessage: IMessage) => {
        let messageOnList = false;
        const updated = prevMessages
            .map((msg) => {
                const arr = [msg.from.username, msg.to.username];

                if (arr.includes(newMessage.from.username) && arr.includes(newMessage.to.username)) {
                    messageOnList = true;
                    return newMessage;
                }

                return msg;
            });
        const sorted = updated.sort((a, b) => new Date(a.createdAt) > new Date(b.createdAt) ? -1 : 1);

        return messageOnList ? sorted : [newMessage, ...sorted];
    }

    const handleClickOutside = (e: Event) => {
        const toggle = (e.target as HTMLElement).closest('.messages-toggle');
        const wrapper = (e.target as HTMLElement).closest('.messages-wrapper');
        const seeMoreButton = (e.target as HTMLElement).closest('.see-more-button');

        if (!toggle && isMessagesOpenRef.current && !wrapper && !seeMoreButton) {
            setMessagesOpen(false);
        }
    }

    const fetchMessages = async () => {
        try {
            setLoading(true);
            setError(null);
            const result = await getMessages({ offset });

            setMessages([...messages, ...result]);
            setOffset(offset + 1);
            setLoading(false);
        } catch (e) {
            setError(e);
            setLoading(false);
        }
    };

    const handleReadMessage = async (sender: IUser) => {
        try {
            dispatch(initiateChat(sender));
            setMessagesOpen(false);

            await readMessage(sender.id);
            const updated = messages.map((msg) => {
                if (msg.from.id === sender.id) {
                    return {
                        ...msg,
                        seen: true
                    }
                }

                return msg;
            });

            setMessages(updated);

            if (window.screen.width < 800) {
                history.push(`/chat/${sender.username}`);
            }
        } catch (e) {
            console.log(e);
        }
    }

    const toggleMessages = () => {
        setMessagesOpen(!isMessagesOpen);
        setCount(0);

        // Since setting state is asynchronous, we should flip the value of isMessagesOpen
        if (!isMessagesOpen && messages.length === 0) {
            fetchMessages();
        }
    }

    const onClickCompose = () => {
        composeModal.openModal();
        setMessagesOpen(false);
    }

    const infiniteRef = useInfiniteScroll({
        loading: isLoading,
        hasNextPage: !error && messages.length >= 10,
        onLoadMore: fetchMessages,
        scrollContainer: 'parent',
    });

    return (
        <div className="relative">
            <div onClick={toggleMessages}>
                <Badge count={count}>
                    <MessageOutlined className="messages-toggle text-xl focus:outline-none dark:text-white" />
                </Badge>
            </div>
            {isMessagesOpen && (
                <div className="messages-wrapper h-screen border border-transparent dark:border-gray-800 laptop:h-auto fixed top-14 laptop:top-10 pb-14 laptop:pb-0 right-0 w-full laptop:w-30rem bg-white dark:bg-indigo-1000 shadow-lg laptop:rounded-md laptop:absolute">
                    {/*  ----- HEADER ----- */}
                    <div className="px-4 py-2 border-b-gray-200 flex justify-between items-center bg-indigo-700 laptop:rounded-t-md">
                        <h6 className="text-white">Messages</h6>
                        <span
                            className="text-sm flex p-2 text-white rounded-md hover:bg-indigo-500"
                            onClick={onClickCompose}
                        >
                            <FormOutlined className="mr-2" />
                            Compose
                        </span>
                    </div>
                    {/* --- LOADER FIRST FETCH */}
                    {(isLoading && !error && messages.length === 0) && (
                        <div className="flex items-center justify-center py-8">
                            <Loader />
                        </div>
                    )}
                    {/* --- ERROR MESSAGE ---- */}
                    {(messages.length === 0 && error) && (
                        <div className="flex justify-center py-6">
                            <p className="text-gray-400 italic">
                                {error.status_code === 404
                                    ? 'You have no messages.'
                                    : (error?.error?.message || 'Something went wrong :(')
                                }
                            </p>
                        </div>
                    )}
                    {/* --- MSG LIST --- */}
                    {(messages.length !== 0) && (
                        <MessagesList
                            messages={messages}
                            handleReadMessage={handleReadMessage}
                            infiniteScrollRef={infiniteRef}
                        />
                    )}
                    {/* ---- LOADER FETCHING MORE --- */}
                    {(isLoading && !error && messages.length !== 0) && (
                        <div className="flex items-center justify-center py-4">
                            <Loader />
                        </div>
                    )}
                </div>
            )}
            {composeModal.isOpen && (
                <ComposeMessageModal
                    isOpen={composeModal.isOpen}
                    openModal={composeModal.openModal}
                    closeModal={composeModal.closeModal}
                    userID={id}
                />
            )}
        </div>
    );
}
Example #7
Source File: index.tsx    From foodie with MIT License 4 votes vote down vote up
Header: React.FC<IProps> = ({ profile, auth }) => {
    const [isUploadingProfileImage, setIsUploadingProfileImage] = useState(false);
    const [isUploadingCoverPhoto, setIsUploadingCoverPhoto] = useState(false);
    const history = useHistory();
    const { isOpen, openModal, closeModal } = useModal();
    const dispatch = useDispatch();
    const coverPhotoOverlayRef = useRef<HTMLDivElement | null>(null);
    const coverPhotoRef = useRef<HTMLDivElement | null>(null);
    const coverPhoto = useFileHandler<IImage>('single', initImageState);
    const profilePicture = useFileHandler<IImage>('single', initImageState);

    useEffect(() => {
        const cp = coverPhotoRef.current;
        const cpo = coverPhotoOverlayRef.current;

        if (cp && cpo && profile.isOwnProfile && window.screen.width > 800) {
            cp.addEventListener('mouseover', overlayOnMouseOver);
            cp.addEventListener('mouseout', overlayOnMouseOut);
        }

        return () => {
            if (cp && cpo) {
                cp.removeEventListener('mouseover', overlayOnMouseOver);
                cp.removeEventListener('mouseout', overlayOnMouseOut);
            }
        }
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [coverPhoto.imageFile.file, isUploadingCoverPhoto, profile.isOwnProfile]);

    const overlayOnMouseOver = () => {
        if (!isUploadingCoverPhoto && coverPhotoOverlayRef.current) {
            coverPhotoOverlayRef.current.style.visibility = 'visible';
        }
    }

    const overlayOnMouseOut = () => {
        if (!isUploadingCoverPhoto && !coverPhoto.imageFile.file && coverPhotoOverlayRef.current) {
            coverPhotoOverlayRef.current.style.visibility = 'hidden';
        }
    }

    const handleProfilePictureFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
        profilePicture.onFileChange(e, () => {
            openModal();
        });
    };

    const onCropSuccessCallback = async (file: File) => {
        const formData = new FormData();
        formData.append('photo', file);

        try {
            setIsUploadingProfileImage(true);
            toast('Uploading...', { hideProgressBar: true, bodyStyle: { color: '#1a1a1a' } });

            const { image } = await uploadPhoto(formData, 'picture');

            dispatch(updateProfilePicture(image));
            dispatch(updateAuthPicture(image));
            setIsUploadingProfileImage(false);

            toast.dismiss();
            toast.dark('Profile picture successfully changed.', { hideProgressBar: true });
        } catch (e) {
            console.log(e);
            setIsUploadingProfileImage(false);
            toast.error(e.error.message);
        }
    };

    const handleSaveCoverPhoto = async () => {
        if (coverPhoto.imageFile.file) {
            const formData = new FormData();
            formData.append('photo', coverPhoto.imageFile.file);

            try {
                setIsUploadingCoverPhoto(true);
                toast('Uploading Cover Photo...', { hideProgressBar: true, bodyStyle: { color: '#1a1a1a' } });

                const { image } = await uploadPhoto(formData, 'cover');

                dispatch(updateCoverPhoto(image));
                setIsUploadingCoverPhoto(false);

                coverPhoto.clearFiles();
                toast.dismiss();
                toast.dark('Cover photo successfully changed.', { hideProgressBar: true });
            } catch (e) {
                console.log(e);
                setIsUploadingCoverPhoto(false);
                toast.error(e.error.message);
            }
        }
    }

    const onClickMessage = () => {
        dispatch(initiateChat({
            username: profile.username,
            id: profile.id,
            fullname: profile.fullname || '',
            profilePicture: profile.profilePicture?.url || ''
        }));

        if (window.screen.width < 1024) {
            history.push(`/chat/${profile.username}`);
        }
    }

    return (
        <div>
            <CropProfileModal
                isOpen={isOpen}
                closeModal={closeModal}
                openModal={openModal}
                file={profilePicture.imageFile}
                onCropSuccessCallback={onCropSuccessCallback}
            />
            {/*  ----- COVER PHOTO ------- */}
            <div className="w-full h-60 mb-8 laptop:mb-0 laptop:h-80 bg-gray-200 dark:bg-gray-800 relative overflow-hidden" ref={coverPhotoRef}>
                {/* ---- OVERLAY FOR CHOOSING PHOTO AND SHOWING LOADER ----- */}
                <CoverPhotoOverlay
                    coverPhotoOverlayRef={coverPhotoOverlayRef}
                    coverPhoto={coverPhoto}
                    isUploadingCoverPhoto={isUploadingCoverPhoto}
                    isOwnProfile={profile.isOwnProfile}
                    handleSaveCoverPhoto={handleSaveCoverPhoto}
                />
                {/* ---- ACTUAL COVER PHOTO ---- */}
                <img
                    alt=""
                    className="w-full h-full object-cover"
                    src={coverPhoto.imageFile.url || profile.coverPhoto?.url || `https://source.unsplash.com/oDhGIbegZNI/1400x900`}
                />
            </div>
            <div className="laptop:px-6% w-full relative flex mt-2 laptop:transform laptop:-translate-y-2/4">
                {/* --- PROFILE PICTURE */}
                <div className="absolute left-0 right-0 mx-auto w-40 h-40 transform -translate-y-44 laptop:transform-none laptop:relative laptop:w-1/3 laptop:h-60 laptop:mr-2 flex justify-center">
                    {(!coverPhoto.imageFile.file) && (
                        <>
                            <div

                                className="w-full h-full laptop:w-60 laptop:h-60 !bg-cover !bg-no-repeat rounded-full border-4 border-white dark:border-indigo-1000 overflow-hidden"
                                style={{
                                    background: `#f7f7f7 url(${profile.profilePicture?.url || avatar_placeholder})`
                                }}
                            >
                                {isUploadingProfileImage && (
                                    <div className="w-full h-full bg-black bg-opacity-50 flex items-center justify-center">
                                        <Loader mode="light" />
                                    </div>
                                )}
                            </div>
                            {/* ---- UPDLOAD PROFILE PICTURE ---- */}
                            {profile.isOwnProfile && (
                                <div>
                                    <input
                                        type="file"
                                        hidden
                                        accept="image/*"
                                        onChange={handleProfilePictureFileChange}
                                        readOnly={isUploadingProfileImage}
                                        id="picture"
                                    />
                                    <label
                                        htmlFor="picture"
                                    >
                                        <div className="flex items-center w-10 h-10 justify-center cursor-pointer p-4 bg-indigo-700 rounded-full absolute -bottom-2 laptop:bottom-0 left-14 hover:bg-indigo-800">
                                            <CameraOutlined className="text-xl flex items-center justify-center text-white" />
                                        </div>
                                    </label>
                                </div>
                            )}
                        </>
                    )}
                </div>
                <div className="flex w-full  flex-col self-end">
                    <div className="px-4 laptop:px-0 w-full flex items-center flex-col laptop:flex-row justify-between mb-2 laptop:ml-2 laptop:mr-14">
                        {/* ---- NAME AND USERNAME */}
                        <div className="text-center laptop:text-left mb-4 laptop:mb-0">
                            <h2 className="text-3xl dark:text-white">{profile.fullname || `@${profile.username}`}</h2>
                            <span className="text-indigo-700 dark:text-indigo-400">{profile.fullname && `@${profile.username}`}</span>
                        </div>
                        {/* ---- FOLLOW/UNFOLLOW/MESSAGE BUTTON */}
                        {!profile.isOwnProfile ? (
                            <div className="flex justify-center laptop:justify-start space-x-4 items-start">
                                <FollowButton isFollowing={profile.isFollowing} userID={profile.id} />
                                <button
                                    className="button--muted !border-gray-400 !rounded-full flex items-center dark:bg-indigo-1100 dark:text-white dark:hover:text-white dark:hover:bg-indigo-900 dark:!border-gray-800"
                                    onClick={onClickMessage}
                                >
                                    <MessageOutlined className="flex items-center justify-center mr-2" />
                                    Message
                                </button>
                            </div>
                        ) : (
                            <button
                                className="button--muted !rounded-full !border !border-gray-400 !focus:bg-gray-200 !py-2 flex items-center justify-center dark:bg-indigo-1100 dark:text-white dark:hover:text-white dark:hover:bg-indigo-900 dark:!border-gray-800"
                                onClick={() => history.push(`/user/${profile.username}/edit`)}
                            >
                                <EditOutlined className="text-xl mr-4" />
                                    Edit Profile
                            </button>
                        )}
                    </div>
                    {/* ---- PROFILE NAVS ----- */}
                    <Tabs
                        username={profile.username}
                        isOwnProfile={profile.id === auth.id}
                        followersCount={profile.followersCount}
                        followingCount={profile.followingCount}
                    />
                </div>
            </div>
        </div>
    );
}
Example #8
Source File: index.tsx    From dashboard with Apache License 2.0 4 votes vote down vote up
Welcome: React.FC<WelcomeProps> = (props) => {
  const {currentStaffAdmin, collapsed} = props;
  const [isSmallScreen, setIsSmallScreen] = useState(false);
  const [showSidebar, setShowSidebar] = useState(false);
  const [mainWidth, setMainWidth] = useState(window.innerWidth);
  const [summary, setSummary] = useState<SummaryResult>();
  const [trendItems, setTrendItems] = useState<TrendItem[]>([]);
  const formRef = useRef<FormInstance>();
  const chartRef = useRef<ChartRefConfig>();
  const [allStaffs, setAllStaffs] = useState<StaffOption[]>([]);
  const initialValues = {
    statistic_type: 'total',
    date_range_type: 'week',
    date_range: [moment().add(-7, 'd'), moment()],
    ext_staff_ids: [],
  };

  useEffect(() => {
    const chartWidth = mainWidth - 36 - 4 - (showSidebar ? 300 : 0);
    // @ts-ignore
    chartRef?.current?.chart?.changeSize(chartWidth, 335);
  }, [mainWidth, showSidebar, collapsed]);

  const chartConfig: AreaConfig = {
    data: trendItems,
    // @ts-ignore
    chartRef,
    height: 360,
    xField: 'date',
    yField: 'number',
    meta: {
      date: {
        alias: '日期',
      },
      number: {
        alias: '客户数',
      },
    },
    yAxis: {
      grid: {
        line: {
          style: {
            stroke: 'rgba(0,0,0,0.15)',
            lineWidth: 1,
            lineDash: [4, 5],
          },
        },
      },
    },
    line: {
      style: {
        stroke: 'rgb(91,143,249)',
        lineWidth: 2,
      },
    },
    smooth: true,
    point: {
      size: 2,
    },
    tooltip: {
      domStyles: {
        'g2-tooltip': {
          backgroundColor: 'rgba(0,0,0,0.8)',
        },
        'g2-tooltip-title': {
          color: 'rgb(255,255,255)',
        },
        'g2-tooltip-list': {
          color: 'rgb(255,255,255)',
        },
      },
    },
    areaStyle: function areaStyle() {
      return {fill: 'l(270) 0:#ffffff 0.5:#c0d4fc 1:#5e91f9'};
    },
  };

  const formDataTransform = (params: any): GetTrendParams => {
    const values = {
      ...params,
      start_time: params.date_range[0].format('YYYY-MM-DD').toString(),
      end_time: params.date_range[1].format('YYYY-MM-DD').toString(),
    };
    delete values.date_range_type;
    delete values.date_range;
    return values;
  };

  useEffect(() => {
    QuerySimpleStaffs({page_size: 5000}).then((res) => {
      if (res.code === 0) {
        setAllStaffs(
          res?.data?.items?.map((item: SimpleStaffInterface) => {
            return {
              label: item.name,
              value: item.ext_id,
              ...item,
            };
          }) || [],
        );
      } else {
        message.error(res.message);
      }
    });

    GetTrend(formDataTransform(initialValues)).then((res) => {
      if (res.code === 0) {
        setTrendItems(res?.data || [],
        );
      } else {
        message.error(res.message);
      }
    });

    GetSummary().then((res: CommonResp) => {
      if (res.code !== 0) {
        message.error('获取统计数据失败');
        return;
      }
      setSummary(res.data);
    }).catch((err) => {
      console.log('err', err);
      message.error('获取统计数据失败');
    });

  }, []);

  return (
    <>
      <RcResizeObserver
        key='resize-observer'
        onResize={(offset) => {
          setMainWidth(offset.width);
          setIsSmallScreen(offset.width <= 576);
          setShowSidebar(offset.width >= 992);
        }}
      >
        <ProCard direction={'column'} ghost={true} gutter={8} className={styles.welcomeContainer}>
          <ProCard.Group ghost={true} wrap={!showSidebar} gutter={8} direction={isSmallScreen ? 'column' : 'row'}>
            <ProCard ghost={true} wrap={!showSidebar} gutter={8} direction={isSmallScreen ? 'column' : 'row'}>
              <ProCard ghost={true}>
                <StatisticCard.Group
                  title={
                    <>
                      客户统计
                      <Divider type={'vertical'}/>
                      <a className={styles.detailButton} onClick={() => {
                        history.push('/staff-admin/customer-management/customer');
                      }}>查看客户</a>
                    </>
                  }
                  direction={isSmallScreen ? 'column' : 'row'}
                >
                  <StatisticCard
                    statistic={{
                      title: '客户总数',
                      value: summary?.total_customers_num,
                    }}
                  />
                  <ProCard.Divider type={isSmallScreen ? 'horizontal' : 'vertical'}/>
                  <StatisticCard
                    statistic={{
                      title: '今日新增',
                      value: summary?.today_customers_increase,
                    }}
                  />
                  <ProCard.Divider type={isSmallScreen ? 'horizontal' : 'vertical'}/>
                  <StatisticCard
                    statistic={{
                      title: '今日流失',
                      value: summary?.today_customers_decrease,
                    }}
                  />
                </StatisticCard.Group>
              </ProCard>
              <ProCard ghost={true}>
                <StatisticCard.Group
                  title={
                    <>
                      群统计
                      <Divider type={'vertical'}/>
                      <a className={styles.detailButton} onClick={() => {
                        history.push('/staff-admin/group-chat/list');
                      }}>查看客户群</a>
                    </>
                  }
                  direction={isSmallScreen ? 'column' : 'row'}
                >
                  <StatisticCard
                    statistic={{
                      title: '群总数',
                      value: summary?.total_groups_num,
                    }}
                  />
                  <ProCard.Divider type={isSmallScreen ? 'horizontal' : 'vertical'}/>
                  <StatisticCard
                    colSpan={'8'}
                    statistic={{
                      title: '今日入群',
                      value: summary?.today_groups_increase,
                    }}
                  />
                  <ProCard.Divider type={isSmallScreen ? 'horizontal' : 'vertical'}/>
                  <StatisticCard
                    statistic={{
                      title: '今日退群',
                      value: summary?.today_groups_decrease,
                    }}
                  />
                </StatisticCard.Group>
              </ProCard>
            </ProCard>

            <ProCard hidden={!showSidebar} colSpan={{
              lg: '300px',
            }}>
              <div className={styles.staffInfoBox}>
                <img
                  src={currentStaffAdmin?.avatar_url}
                  className={styles.avatar}
                  alt={currentStaffAdmin?.name}
                />
                <div className={styles.textGroup}>
                  <p className={styles.nickname}>
                    {currentStaffAdmin?.name}
                  </p>
                  <p className={styles.role}>{currentStaffAdmin?.role?.name}</p>
                </div>
              </div>
              <div>
                <Typography.Paragraph style={{marginBottom: 6}}>
                  <Typography.Text style={{color: '#6b7a88'}}>企业名称:</Typography.Text>
                  <Typography.Text>成都小橘科技有限公司</Typography.Text>
                </Typography.Paragraph>
                <Typography.Paragraph style={{marginBottom: 0}}>
                  <Typography.Text style={{color: '#6b7a88'}}>员工总数:</Typography.Text>
                  <Typography.Text>12</Typography.Text>
                </Typography.Paragraph>
              </div>
            </ProCard>
          </ProCard.Group>

          <ProCard.Group ghost={true} gutter={8} style={{marginTop: 12}}>
            <ProCard className={styles.trendChartContainer}>
              <div>
                <Form
                  // @ts-ignore
                  ref={formRef}
                  initialValues={initialValues}
                  onValuesChange={(changedValues, values) => {
                    const transformedValues = {...values};
                    if (changedValues?.date_range_type) {
                      let date_range = [
                        moment(),
                        moment(),
                      ];
                      if (transformedValues.date_range_type === 'week') {
                        date_range = [
                          moment().add(-7, 'd'),
                          moment(),
                        ];
                      }
                      if (transformedValues.date_range_type === 'month') {
                        date_range = [
                          moment().add(-30, 'd'),
                          moment(),
                        ];
                      }

                      formRef.current?.setFieldsValue({
                        date_range,
                      });

                      transformedValues.date_range = date_range;
                    }

                    const hide = message.loading('加载走势数据');
                    GetTrend(formDataTransform(transformedValues)).then((res) => {
                      hide();
                      if (res.code === 0) {
                        setTrendItems(res?.data || [],
                        );
                      } else {
                        message.error(res.message);
                      }
                    }).catch((err) => {
                      console.log('err', err);
                      message.error('走势数据数据加载失败');
                    });
                  }}
                >
                  <div>
                    <Form.Item
                      name='statistic_type'
                    >
                      <Radio.Group>
                        <Radio.Button value='total'>客户总数</Radio.Button>
                        <Radio.Button value='increase'>新增客户数</Radio.Button>
                        <Radio.Button value='decrease'>流失客户数</Radio.Button>
                        <Radio.Button value='net_increase'>净增客户数</Radio.Button>
                      </Radio.Group>
                    </Form.Item>
                  </div>

                  <div style={{display: 'flex'}}>
                    <div className={styles.formItemGroup}>
                      <Form.Item
                        name='date_range_type'
                        className={styles.formItem}
                      >
                        <Select
                          style={{width: 100}}
                        >
                          <Select.Option value='day'>今日</Select.Option>
                          <Select.Option value='week'>近一周</Select.Option>
                          <Select.Option value='month'>近一个月</Select.Option>
                        </Select>
                      </Form.Item>

                      <Form.Item
                        name='date_range'
                        label={'时段'}
                        className={styles.formItem}
                      >
                        <DatePicker.RangePicker/>
                      </Form.Item>

                      <Form.Item
                        name='ext_staff_ids'
                        label={'员工'}
                        style={{width: 300}}
                        className={styles.formItem}
                      >
                        <StaffTreeSelect options={allStaffs} maxTagCount={1}/>
                      </Form.Item>

                    </div>
                    <div>
                      <Button type='default' onClick={() => {
                        formRef.current?.resetFields();
                      }}>
                        重置
                      </Button>
                    </div>
                  </div>

                </Form>

                <Area {...chartConfig} className={styles.chartBox}/>
              </div>
            </ProCard>

            <ProCard.Group hidden={!showSidebar} colSpan={'300px'} style={{padding: '0 4px'}} ghost={true}
                           direction={'column'}>
              <ProCard title={<h4 style={{fontSize: 15}}>OpenSCRM开源私域流量管理系统</h4>} bordered={true}
                       bodyStyle={{paddingTop: 10}}>
                <Row gutter={2}>
                  <Col span={12}>
                    <img src={QrcodeImage} width={100} style={{marginBottom: 12}}/>
                  </Col>
                  <Col span={12}>
                    <div style={{display: 'flex', flexWrap: 'wrap', justifyContent: 'center'}}>
                      <Paragraph type={'secondary'}>
                        遇到任何问题,欢迎联系我们
                      </Paragraph>
                      <Paragraph type={'secondary'} style={{marginBottom: 0}}>
                        微信扫码立即进入交流社群
                      </Paragraph>
                    </div>
                  </Col>
                </Row>
              </ProCard>

              <ProCard title={'消息存档'} bordered={true} style={{marginTop: 8}} bodyStyle={{paddingTop: 12}}>
                <Row gutter={2}>
                  <Col span={12}>
                    <img width={100} src={ChatImage} onClick={() => {
                      history.push('/staff-admin/corp-risk-control/chat-session');
                    }}/>
                  </Col>
                  <Col span={12}>
                    <div style={{display: 'flex', flexWrap: 'wrap', justifyContent: 'end'}}>
                      <Paragraph type={'secondary'}>轻松了解员工和客户的沟通过程</Paragraph>
                      <Button type={'primary'} onClick={() => {
                        history.push('/staff-admin/corp-risk-control/chat-session');
                      }}>立即使用</Button>
                    </div>
                  </Col>
                </Row>
              </ProCard>

              <ProCard title={'入门指引'} bordered={true} style={{marginTop: 8}} bodyStyle={{paddingTop: 10}}>
                <div className={styles.guideContainer}>
                  <div className={styles.item}>
                    文档中心 <Button size={'small'} target={'_blank'} href={'https://docs.openscrm.cn/'}
                                 type={'dashed'}>查看</Button>
                  </div>
                </div>
              </ProCard>

            </ProCard.Group>
          </ProCard.Group>

          <ProCard.Group ghost={true} gutter={8} style={{marginTop: 12}}>
            <ProCard title={'常用功能'} bordered={true} bodyStyle={{paddingTop: 6}}>
              <div className={styles.quickLinkContainer}>

                <a className={styles.item} onClick={() => {
                  history.push('/staff-admin/customer-growth/contact-way')
                }}>
                  <div className={styles.icon}>
                    {getIcon(<QrcodeOutlined style={{color: 'rgba(0,0,0,0.65)'}}/>)}
                  </div>
                  <div className={styles.textGroup}>
                    <p className={styles.title}>渠道活码</p>
                    <p className={styles.desc}>精准运营不同渠道来源的客户</p>
                  </div>
                </a>

                <a className={styles.item} onClick={() => {
                  history.push('/staff-admin/customer-conversion/customer-mass-msg')
                }}>
                  <div className={styles.icon}>
                    {getIcon('icon-fasong')}
                  </div>
                  <div className={styles.textGroup}>
                    <p className={styles.title}>客户群发</p>
                    <p className={styles.desc}>强大的定向群发,最小打扰,最优效果</p>
                  </div>
                </a>

                <a className={styles.item} onClick={() => {
                  history.push('/staff-admin/corp-risk-control/chat-session')
                }}>
                  <div className={styles.icon}>
                    {getIcon(<MessageOutlined style={{color: 'rgba(0,0,0,0.65)'}} />)}
                  </div>
                  <div className={styles.textGroup}>
                    <p className={styles.title}>消息存档</p>
                    <p className={styles.desc}>轻松了解员工和客户的沟通过程</p>
                  </div>
                </a>

                <a className={styles.item} onClick={() => {
                  history.push('/staff-admin/customer-management/customer-tag')
                }}>
                  <div className={styles.icon}>
                    {getIcon(<TagsOutlined style={{color: 'rgba(0,0,0,0.65)'}} />)}
                  </div>
                  <div className={styles.textGroup}>
                    <p className={styles.title}>客户标签</p>
                    <p className={styles.desc}>集中高效管理客户标签,支撑私域精准运营</p>
                  </div>
                </a>

              </div>
            </ProCard>

          </ProCard.Group>


        </ProCard>

      </RcResizeObserver>

    </>
  );
}
Example #9
Source File: index.tsx    From amiya with MIT License 4 votes vote down vote up
export default function Demo() {
  // 列表控制
  const listRef = useRef<any>()
  // 当前激活的 tab
  const [activeTab, setActiveTab] = useState<string>('')
  // 弹窗详情
  const [detail, setDetail] = useState<AnyKeyProps>({
    visible: false,
    data: {}
  })

  /**
   * 切换 tab 事件
   * @param value 当前 tab 值
   */
  const handleTabChange = (value: string) => {
    setActiveTab(value)
    listRef.current.reset()
  }

  /**
   * 查看详情
   * @param record 当前行数据
   */
  const goDetail = (record: Record) => {
    detailApi({ id: record.id }).then(res => {
      setDetail({
        visible: true,
        data: res.data
      })
    })
  }

  return (
    <div className="cnode-list">
      {/* tab 区域 */}
      <Tabs activeKey={activeTab} onChange={handleTabChange} type="card">
        {options.map(option => (
          <Tabs.TabPane tab={option.label} key={option.value} />
        ))}
      </Tabs>
      {/* 列表区域 */}
      <AySearchList
        api={listApi}
        extraVisible={false}
        listExtend={{ itemLayout: 'horizontal' }}
        extendSearchParams={{ tab: activeTab }}
        listHeader={<div style={{ height: 16 }}>{/* 占位空间 */}</div>}
        ref={listRef}
        onParamsChange={() => window.scrollTo({ behavior: 'smooth', top: 0 })}
        renderItem={(record: Record) => {
          return (
            <List.Item
              key={record.id}
              actions={[
                <AyAction icon={<MessageOutlined />} sub tooltip="最后回复时间">
                  <span>{moment(record.last_reply_at).fromNow()}</span>
                </AyAction>,
                <AyAction icon={<MessageOutlined />} sub tooltip="回复数">
                  <span>{record.reply_count}</span>
                </AyAction>,
                <AyAction icon={<EyeOutlined />} sub tooltip="阅读数">
                  <span>{record.visit_count}</span>
                </AyAction>
              ]}
            >
              <List.Item.Meta
                avatar={<Avatar src={record.author.avatar_url} />}
                title={
                  <Space>
                    <a onClick={() => goDetail(record)}>{record.title}</a>
                    <span>
                      {record.good && <Tag color="green">精品</Tag>}
                      {record.top && <Tag color="blue">置顶</Tag>}
                    </span>
                  </Space>
                }
                description={moment(record.create_at).fromNow()}
              />
            </List.Item>
          )
        }}
      />
      {/* 弹窗详情 */}
      <AyDialog
        drawer
        width={800}
        footer={false}
        destroyOnClose
        visible={detail.visible}
        setVisible={() => setDetail({ ...detail, visible: false })}
        title="文章详情"
      >
        <div dangerouslySetInnerHTML={{ __html: detail.data.content }}></div>
        <List
          itemLayout="horizontal"
          header={`${detail?.data?.replies?.length || 0} 条回复`}
          dataSource={detail?.data?.replies || []}
          renderItem={(comment: Record) => (
            <Comment
              key={comment.id}
              author={comment.author.loginname}
              avatar={<Avatar src={comment.author.avatar_url} alt={comment.author.avatar_url} />}
              content={<div dangerouslySetInnerHTML={{ __html: comment.content }}></div>}
              datetime={
                <Tooltip title={moment(comment.create_at).format('YYYY-MM-DD HH:mm:ss')}>
                  <span>{moment(comment.create_at).fromNow()}</span>
                </Tooltip>
              }
            />
          )}
        />
      </AyDialog>
    </div>
  )
}