react-bootstrap#ButtonGroup TypeScript Examples

The following examples show how to use react-bootstrap#ButtonGroup. 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: Settings.tsx    From apps with MIT License 6 votes vote down vote up
Settings = ({ visibleOnly, updateVisibleOnly, localTime, updateLocalTime }: IProps) => (
    <div>
        <ButtonGroup>
            <Button disabled variant="outline-dark">
                Showing{" "}
            </Button>
            <Button variant={visibleOnly ? "warning" : "success"} onClick={() => updateVisibleOnly(!visibleOnly)}>
                {visibleOnly ? "only entries with visible changes" : "all entries"}
            </Button>
            <Button variant={localTime ? "warning" : "success"} onClick={() => updateLocalTime(!localTime)}>
                {localTime ? "with local timestamps" : "with UTC timestamps"}
            </Button>
        </ButtonGroup>
        <br />
        &nbsp;
    </div>
)
Example #2
Source File: BattleActorAttackDisplay.tsx    From apps with MIT License 6 votes vote down vote up
render() {
        return (
            <div>
                <ButtonGroup className="battle-actor-action-display">
                    <Button className="action" variant="danger" onClick={(e) => this.queueAction(Card.BUSTER)}>
                        B
                    </Button>
                    <Button className="action" variant="success" onClick={(e) => this.queueAction(Card.QUICK)}>
                        Q
                    </Button>
                    <Button className="action" variant="primary" onClick={(e) => this.queueAction(Card.ARTS)}>
                        A
                    </Button>
                    <Button className="action" variant="secondary">
                        NP
                    </Button>
                </ButtonGroup>
            </div>
        );
    }
Example #3
Source File: BgmDescriptor.tsx    From apps with MIT License 5 votes vote down vote up
export default function BgmDescriptor(props: {
    region: Region;
    bgm: Bgm.Bgm;
    showName?: string;
    showLink?: boolean;
    style?: React.CSSProperties;
}) {
    const bgm = props.bgm;
    if (bgm.id === 0) {
        return null;
    } else if (bgm.audioAsset !== undefined) {
        const showName = getBgmName(bgm);
        const toLink = props.showLink ? (
            <>
                <Button variant="primary" as={Link} to={`/${props.region}/bgm/${bgm.id}`}>
                    <FontAwesomeIcon icon={faShare} title={`Go to ${props.region} BGM ${showName}`} />
                </Button>
            </>
        ) : null;
        const downloadButton = bgm.notReleased ? (
            <Button disabled variant="secondary" target="_blank" title={showName}>
                {showName}
            </Button>
        ) : (
            <Button variant={"info"} href={bgm.audioAsset} target="_blank" title={`Download ${showName}`}>
                {props.showName ?? showName}&nbsp;
                <FontAwesomeIcon icon={faFileAudio} />
            </Button>
        );
        return (
            <>
                <ButtonGroup size="sm" style={props.style}>
                    <VoiceLinePlayer audioAssetUrls={[bgm.audioAsset]} delay={[0]} title={bgm.name} />
                    {downloadButton}
                    {toLink}
                </ButtonGroup>
            </>
        );
    } else {
        return (
            <Button variant={"info"} disabled style={props.style}>
                {bgm.name}
            </Button>
        );
    }
}
Example #4
Source File: SettingForm.tsx    From apps with MIT License 5 votes vote down vote up
render() {
        return (
            <div>
                <Form>
                    <Form.Group>
                        <Form.Label>Language</Form.Label>
                        <Form.Control
                            as={"select"}
                            value={this.props.language}
                            onChange={(ev: Event) => this.updateLanguage(ev.target.value)}
                        >
                            {Object.values(Language).map((v) => (
                                <option key={v} value={v}>
                                    {v}
                                </option>
                            ))}
                        </Form.Control>
                    </Form.Group>
                    <Form.Group>
                        <Form.Label>Theme</Form.Label>
                        <Form.Control
                            as={"select"}
                            value={this.props.theme}
                            onChange={(ev: Event) => this.updateTheme(ev.target.value)}
                        >
                            {Object.values(Theme).map((v) => (
                                <option key={v} value={v}>
                                    {v}
                                </option>
                            ))}
                        </Form.Control>
                    </Form.Group>
                </Form>
                <ButtonGroup style={{ width: "100%", marginBottom: "1em" }}>
                    <Button
                        variant={Manager.shopPlannerEnabled() ? "success" : "secondary"}
                        onClick={() => this.updateShopPlannerEnabled(!Manager.shopPlannerEnabled())}
                    >
                        Shop planner: {Manager.shopPlannerEnabled() ? "Enabled" : "Disabled"}
                    </Button>
                </ButtonGroup>
                <ButtonGroup style={{ width: "100%", marginBottom: "1em" }}>
                    <Button
                        variant={Manager.scriptSceneEnabled() ? "success" : "secondary"}
                        onClick={() => this.updateScriptSceneEnabled(!Manager.scriptSceneEnabled())}
                    >
                        Script Scene: {Manager.scriptSceneEnabled() ? "Enabled" : "Disabled"}
                    </Button>
                </ButtonGroup>
                <ButtonGroup style={{ width: "100%", marginBottom: "1em" }}>
                    <Button
                        variant={Manager.showScriptLine() ? "success" : "secondary"}
                        onClick={() => Manager.setShowScriptLine(!Manager.showScriptLine())}
                    >
                        Original line number in scripts: {Manager.showScriptLine() ? "Shown" : "Hidden"}
                    </Button>
                </ButtonGroup>
                <ButtonGroup style={{ width: "100%" }}>
                    <Button
                        variant={Manager.hideEnemyFunctions() ? "success" : "secondary"}
                        onClick={() => this.updateHideEnemyFunction(!Manager.hideEnemyFunctions())}
                    >
                        Enemy functions {Manager.hideEnemyFunctions() ? "hidden" : "shown"}
                    </Button>
                </ButtonGroup>
            </div>
        );
    }
Example #5
Source File: Header.tsx    From peterportal-client with MIT License 5 votes vote down vote up
Header: FC<HeaderProps> = ({ courseCount, unitCount, saveRoadmap }) => {
  const dispatch = useAppDispatch();
  const [target, setTarget] = useState<any>(null!);
  const [showMenu, setShowMenu] = useState(false);

  const buttons = <>
    <Button variant={isMobile ? "primary" : 'light'} className={isMobile ? 'my-1' : "header-btn"} onClick={() => dispatch(setShowTransfer(true))}>
      Transfer Credits
      <ArrowLeftRight className="header-icon" />
    </Button>
    <Button variant={isMobile ? "primary" : 'light'} className={isMobile ? 'my-1' : "header-btn"} onClick={saveRoadmap}>
      Save
      <Save className="header-icon" />
    </Button>
    <Button variant={isMobile ? "primary" : 'light'} className={isMobile ? 'my-1' : "header-btn"} onClick={() => dispatch(clearPlanner())}>
      Clear
      <Trash className="header-icon" />
    </Button>
  </>

  const onMenuClick = (event: React.MouseEvent) => {
    setShowMenu(!showMenu);
    setTarget(event.target);
  };

  return (
    <div className="header">
      <Transfer />
      <div>
        <div id="title">
          Peter's Roadmap
        </div>
        <span id="planner-stats">
          Total: <span id="course-count">{courseCount}</span>{" "}
          {courseCount === 1 ? "course" : "courses"},{" "}
          <span id="unit-count">{unitCount}</span>{" "}
          {unitCount === 1 ? "unit" : "units"}
        </span>
      </div>
      <div>
        {
          isMobile && <>
            <Button variant="light" className="header-btn add-course" onClick={() => { dispatch(setShowSearch(true)) }}>
              <Plus className="header-icon mr-1" />
              Add Course
            </Button>
            <List className='mx-3' onClick={onMenuClick} />
            <Overlay show={showMenu} target={target} placement="left">
              <Popover id='roadmap-header-buttons'>
                <Popover.Content>
                  <div className='d-flex flex-column'>
                    {buttons}
                  </div>
                </Popover.Content>
              </Popover>
            </Overlay>
          </>
        }
        {
          isBrowser && <ButtonGroup>
            {buttons}
          </ButtonGroup>
        }
      </div>
    </div >
  )
}
Example #6
Source File: IpfsConsoleView.tsx    From 3Speak-app with GNU General Public License v3.0 4 votes vote down vote up
//JSON editor specific
export function IpfsConsoleView() {
  const [ipfsConfig, setIpfsConfig] = useState({} as any)
  const [ipfsInfo, setIpfsInfo] = useState({} as any)
  const [configError, setConfigError] = useState(false)
  const editor = useRef<any>()
  const loopPid = useRef<any>()

  const getIpfsConfig = async () => {
    const info = await IpfsHandler.getIpfs()
    setIpfsInfo(info)

    let jsonContent
    const { ipfs } = info

    if (editor.current) {
      editor.current?.createEditor({
        value: await ipfs.config.getAll(),
        ace: ace,
        mode: 'code',
        theme: 'ace/theme/solarized_dark',
        ref: editor,
        htmlElementProps: {
          style: {
            height: '500px',
          },
        },
        onChange: (json) => {
          jsonContent = json
        },
      })
    } else {
      throw new Error(`editor ref is not defined!  Cannot create editor.`)
    }
  }

  const update = async () => {
    console.log(`UPDATING`)
    const annotations = editor.current.jsonEditor.aceEditor.getSession().getAnnotations()
    setConfigError(annotations.length === 0 ? false : true)
  }

  useEffect(() => {
    void getIpfsConfig()
    loopPid.current = setInterval(update, 150)

    return () => {
      clearInterval(loopPid.current)
    }
  }, [])

  return (
    <div style={{ padding: '5px', overflow: 'hidden' }}>
      <h3>
        This is the IPFS Debug Console. This is for advanced users only, if you don't know what you
        are doing stay out of this area.
      </h3>
      <div style={{ overflow: 'show' }}>
        <Row>
          <Col style={{ background: '#f8f9fa', margin: '5px' }}>
            <Editor
              value={ipfsConfig}
              ace={ace}
              mode="code"
              theme="ace/theme/solarized_dark"
              ref={editor}
              htmlElementProps={{
                style: {
                  height: '560px',
                },
              }}
              onChange={(json) => {
                console.log(json)
              }}
            />
            <ButtonGroup>
              <Button
                variant="success"
                onClick={async () => {
                  try {
                    const jsonContent = editor.current.jsonEditor.get()
                    NotificationManager.success('IPFS config saved')
                    await ipfsInfo.ipfs.config.replace(jsonContent)
                  } catch (ex) {
                    console.error(ex)
                  }
                }}
                disabled={configError}
              >
                Save
              </Button>
            </ButtonGroup>
          </Col>
          <Col style={{ background: '#f8f9fa', margin: '5px' }}>
            <IpfsStatsView />
          </Col>
        </Row>
        <Row>
          <Col style={{ background: '#f8f9fa', margin: '5px' }}></Col>
          <Col style={{ background: '#f8f9fa', margin: '5px' }}></Col>
        </Row>
      </div>
    </div>
  )
}
Example #7
Source File: BgmsPage.tsx    From apps with MIT License 4 votes vote down vote up
render() {
        if (this.state.error) return <ErrorStatus error={this.state.error} />;

        if (this.state.loading) return <Loading />;

        const bgms = this.bgms(),
            results = bgms.slice(this.state.perPage * this.state.page, this.state.perPage * (this.state.page + 1));

        const pageNavigator = this.paginator(bgms.length);

        return (
            <div id="bgms" className="listing-page">
                <Row>
                    <Col md={12} lg={6} id="item-type">
                        <ButtonGroup>
                            <Button
                                variant={this.state.releaseOnlyFilter === true ? "success" : "outline-dark"}
                                onClick={(_) => this.toggleReleaseOnlyFilter(true)}
                            >
                                Buyable in my room
                            </Button>
                            <Button
                                variant={this.state.releaseOnlyFilter === false ? "success" : "outline-dark"}
                                onClick={(_) => this.toggleReleaseOnlyFilter(false)}
                            >
                                Not buyable in my room
                            </Button>
                        </ButtonGroup>
                    </Col>
                    <Col md={12} lg={3} id="item-search">
                        <Form inline>
                            <Form.Control
                                placeholder={"Search"}
                                value={this.state.search ?? ""}
                                onChange={(ev: ChangeEvent) => {
                                    this.setState({
                                        search: ev.target.value,
                                        page: 0,
                                    });
                                }}
                            />
                        </Form>
                    </Col>
                </Row>
                <Row>
                    <Col>{pageNavigator}</Col>
                </Row>
                <hr />

                <Table striped bordered hover responsive>
                    <thead>
                        <tr>
                            <th className="col-center">#</th>
                            <th className="col-center">Logo</th>
                            <th>Name</th>
                            <th>Unlock Cost</th>
                            <th>Player</th>
                        </tr>
                    </thead>
                    <tbody>
                        {results.map((bgm) => {
                            const route = `/${this.props.region}/bgm/${bgm.id}`;
                            const showName = getBgmName(bgm);

                            const shopDetail = bgm.shop ? (
                                <>
                                    <ItemDescriptor region={this.props.region} item={bgm.shop.cost.item} /> ×
                                    {bgm.shop.cost.amount}
                                </>
                            ) : null;

                            return (
                                <tr key={bgm.id}>
                                    <td className="col-center">
                                        <Link to={route}>{bgm.id}</Link>
                                    </td>
                                    <td className="col-center">
                                        <Link to={route}>
                                            <img src={bgm.logo} style={{ height: "1.5em" }} alt="BGM Logo" />
                                        </Link>
                                    </td>
                                    <td
                                        style={{
                                            whiteSpace: Manager.showingJapaneseText() ? "pre-wrap" : "normal",
                                        }}
                                    >
                                        <Link to={route}>{showName}</Link>
                                    </td>
                                    <td>{shopDetail}</td>
                                    <td>
                                        <BgmDescriptor region={this.props.region} bgm={bgm} showName="Download" />
                                    </td>
                                </tr>
                            );
                        })}
                    </tbody>
                </Table>

                {pageNavigator}
            </div>
        );
    }
Example #8
Source File: CommandCodesPage.tsx    From apps with MIT License 4 votes vote down vote up
render() {
        if (this.state.error) return <ErrorStatus error={this.state.error} />;

        if (this.state.loading) return <Loading />;

        return (
            <div id="command-codes" className="listing-page">
                <Row>
                    <Col sm={6} md={5} id="item-rarity">
                        <ButtonGroup>
                            {[...new Set(this.state.commandCodes.map((s) => s.rarity))]
                                // deduplicate star counts
                                .sort((a, b) => a - b)
                                // sort
                                .map((rarity) => (
                                    <Button
                                        variant={
                                            this.state.activeRarityFilters.includes(rarity) ? "success" : "outline-dark"
                                        }
                                        key={rarity}
                                        onClick={(_) => this.toggleRarityFilter(rarity)}
                                    >
                                        {rarity} ★
                                    </Button>
                                ))}
                        </ButtonGroup>
                    </Col>
                    <Col sm={6} md={3} id="item-search">
                        <Form inline>
                            <Form.Control
                                placeholder={"Search"}
                                value={this.state.search ?? ""}
                                onChange={(ev: ChangeEvent) => {
                                    this.setState({ search: ev.target.value });
                                }}
                            />
                        </Form>
                    </Col>
                </Row>

                <hr />

                <Table striped bordered hover responsive>
                    <thead>
                        <tr>
                            <th className="col-center">#</th>
                            <th className="col-center">Thumbnail</th>
                            <th>Name</th>
                            <th className="rarity-col">Rarity</th>
                        </tr>
                    </thead>
                    <tbody>
                        {this.commandCodes().map((commandCode) => {
                            const route = `/${this.props.region}/command-code/${commandCode.collectionNo}`;

                            return (
                                <tr key={commandCode.id}>
                                    <td className="col-center">
                                        <Link to={route}>{commandCode.collectionNo}</Link>
                                    </td>
                                    <td className="col-center">
                                        <Link to={route}>
                                            <FaceIcon
                                                rarity={commandCode.rarity}
                                                location={commandCode.face}
                                                height={50}
                                            />
                                        </Link>
                                    </td>
                                    <td>
                                        <Link to={route}>{commandCode.name}</Link>
                                    </td>
                                    <td className="rarity-col">
                                        <RarityDescriptor rarity={commandCode.rarity} />
                                    </td>
                                </tr>
                            );
                        })}
                    </tbody>
                </Table>
            </div>
        );
    }
Example #9
Source File: CraftEssencesPage.tsx    From apps with MIT License 4 votes vote down vote up
render() {
        if (this.state.error) return <ErrorStatus error={this.state.error} />;

        if (this.state.loading) return <Loading />;

        const craftEssences = this.craftEssences(),
            results = craftEssences.slice(
                this.state.perPage * this.state.page,
                this.state.perPage * (this.state.page + 1)
            );

        const pageNavigator = this.paginator(craftEssences.length);

        return (
            <div id="craft-essences" className="listing-page">
                <Row>
                    <Col md={12} lg={6} xl={5} id="item-type">
                        <ButtonGroup>
                            {[
                                [CEType.OTHER, "Regular CE"],
                                [CEType.VALENTINE, "Valentine CE"],
                                [CEType.BOND, "Bond CE"],
                                [CEType.COMMEMORATIVE, "EXP CE"],
                            ].map(([ceType, buttonText]) => {
                                return (
                                    <Button
                                        variant={
                                            this.state.activeCETypeFilters.includes(ceType as CEType)
                                                ? "success"
                                                : "outline-dark"
                                        }
                                        key={ceType}
                                        onClick={(_) => this.toggleCETypeFilter(ceType as CEType)}
                                    >
                                        {buttonText}
                                    </Button>
                                );
                            })}
                        </ButtonGroup>
                    </Col>
                    <Col md={12} lg={3} id="item-search">
                        <Form inline>
                            <Form.Control
                                placeholder={"Search"}
                                value={this.state.search ?? ""}
                                onChange={(ev: ChangeEvent) => {
                                    this.setState({ search: ev.target.value, page: 0 });
                                }}
                            />
                        </Form>
                    </Col>
                </Row>
                <Row>
                    <Col sm={12} md={5} id="item-rarity">
                        <ButtonGroup>
                            {[...new Set(this.state.craftEssences.map((s) => s.rarity))]
                                // deduplicate star counts
                                .sort((a, b) => a - b)
                                // sort
                                .map((rarity) => (
                                    <Button
                                        variant={
                                            this.state.activeRarityFilters.includes(rarity) ? "success" : "outline-dark"
                                        }
                                        key={rarity}
                                        onClick={(_) => this.toggleRarityFilter(rarity)}
                                    >
                                        {rarity} ★
                                    </Button>
                                ))}
                        </ButtonGroup>
                    </Col>
                    <Col sm={12} md={7}>
                        {pageNavigator}
                    </Col>
                </Row>
                <hr />

                <Table striped bordered hover responsive>
                    <thead>
                        <tr>
                            <th className="col-center">#</th>
                            <th className="col-center">Thumbnail</th>
                            <th>Name</th>
                            <th className="rarity-col">Rarity</th>
                        </tr>
                    </thead>
                    <tbody>
                        {results.map((craftEssence) => {
                            const route = `/${this.props.region}/craft-essence/${craftEssence.collectionNo}`;

                            return (
                                <tr key={craftEssence.id}>
                                    <td className="col-center">
                                        <Link to={route}>
                                            {craftEssence.collectionNo} (
                                            <span className="listing-svtId" lang="en-US">
                                                {craftEssence.id}
                                            </span>
                                            )
                                        </Link>
                                    </td>
                                    <td className="col-center">
                                        <Link to={route}>
                                            <FaceIcon
                                                type={Entity.EntityType.SERVANT_EQUIP}
                                                rarity={craftEssence.rarity}
                                                location={craftEssence.face}
                                                height={50}
                                            />
                                        </Link>
                                    </td>
                                    <td style={{ whiteSpace: Manager.showingJapaneseText() ? "pre-wrap" : "normal" }}>
                                        <Link to={route}>{craftEssence.name}</Link>
                                    </td>
                                    <td className="rarity-col">
                                        <RarityDescriptor rarity={craftEssence.rarity} />
                                    </td>
                                </tr>
                            );
                        })}
                    </tbody>
                </Table>

                {pageNavigator}
            </div>
        );
    }
Example #10
Source File: Shop.tsx    From apps with MIT License 4 votes vote down vote up
ShopTab = ({ region, shops, filters, onChange, itemCache, questCache }: IProps) => {
    let [forceEnablePlanner, setForceEnablePlanner] = useState<boolean | undefined>(undefined);
    let [itemFilters, setItemFilters] = useState(new Set<number>());

    const allItems = new Map(shops.map((shop) => [shop.cost.item.id, shop.cost.item]));

    let shopEnabled = forceEnablePlanner === undefined ? Manager.shopPlannerEnabled() : forceEnablePlanner;
    let counted = shops
        .filter((shop) => (shopEnabled ? filters.has(shop.id) : true))
        .map(
            (shop) =>
                [shop.cost.item, (shopEnabled ? filters.get(shop.id)! : shop.limitNum) * shop.cost.amount] as const
        );

    let items = new Map(counted.map((tuple) => [tuple[0].id, tuple[0]]));

    let amounts = new Map<number, number>();
    for (let [item, amount] of counted)
        if (amounts.has(item.id)) amounts.set(item.id, (amounts.get(item.id) ?? 0) + amount);
        else amounts.set(item.id, amount);

    // reset filter if nothing is chosen
    if (!amounts.size && itemFilters.size) setItemFilters(new Set());

    const excludeItemIds = (itemIds: number[]) => {
        return new Map(
            shops
                .filter((shop) => shop.payType !== Shop.PayType.FREE)
                .filter((shop) => shop.limitNum !== 0)
                .filter((shop) => !itemIds.includes(shop.targetIds[0]) || shop.purchaseType !== Shop.PurchaseType.ITEM)
                .map((shop) => [shop.id, shop.limitNum])
        );
    };

    return (
        <>
            <Alert variant="success" style={{ margin: "1em 0", display: "flex" }}>
                <div style={{ flexGrow: 1 }}>
                    {shopEnabled
                        ? amounts.size > 0
                            ? "Total amount for chosen items: "
                            : "No item was chosen. Choose at least one to get calculations."
                        : "Total currency amount needed to clear the shop: "}
                    {[...amounts]
                        .filter(([_, amount]) => amount > 0)
                        .map(([itemId, amount]) => (
                            <span style={{ whiteSpace: "nowrap", paddingRight: "1ch" }} key={itemId}>
                                <IconLink region={region} item={items.get(itemId)!} />
                                <b>×{amount.toLocaleString()}</b>
                            </span>
                        ))}
                </div>
                <div style={{ flexBasis: "auto", paddingLeft: "10px" }}>
                    <Button
                        variant={shopEnabled ? "dark" : "success"}
                        onClick={() => setForceEnablePlanner(!forceEnablePlanner)}
                    >
                        <FontAwesomeIcon icon={faEdit} title={shopEnabled ? "Disable planner" : "Enable planner"} />
                    </Button>
                </div>
            </Alert>
            {shopEnabled && (
                <>
                    <ButtonGroup>
                        <Button disabled variant="outline-dark">
                            Quick toggle
                        </Button>
                        <Button variant="outline-success" onClick={() => onChange?.(excludeItemIds([]))}>
                            All
                        </Button>
                        <Button variant="outline-success" onClick={() => onChange?.(new Map())}>
                            None
                        </Button>
                        <Dropdown as={ButtonGroup}>
                            <Dropdown.Toggle variant="outline-success">Exclude</Dropdown.Toggle>
                            <Dropdown.Menu>
                                <Dropdown.Item
                                    as={Button}
                                    onClick={() =>
                                        onChange?.(excludeItemIds([...gemIds, ...magicGemIds, ...secretGemIds]))
                                    }
                                >
                                    Gems
                                </Dropdown.Item>
                                <Dropdown.Item
                                    as={Button}
                                    onClick={() => onChange?.(excludeItemIds([...monumentIds, ...pieceIds]))}
                                >
                                    Monuments & Pieces
                                </Dropdown.Item>
                                <Dropdown.Item as={Button} onClick={() => onChange?.(excludeItemIds(monumentIds))}>
                                    Monuments
                                </Dropdown.Item>
                                <Dropdown.Item as={Button} onClick={() => onChange?.(excludeItemIds(pieceIds))}>
                                    Pieces
                                </Dropdown.Item>
                            </Dropdown.Menu>
                        </Dropdown>
                    </ButtonGroup>
                    <div>&nbsp;</div>
                </>
            )}
            <Table hover responsive className="shopTable">
                <thead>
                    <tr>
                        <th style={{ textAlign: "left" }}>Detail</th>
                        <th style={{ whiteSpace: "nowrap" }}>
                            Currency&nbsp;
                            <Dropdown as={ButtonGroup}>
                                <Dropdown.Toggle size="sm">
                                    <FontAwesomeIcon style={{ display: "inline" }} icon={faFilter} />
                                </Dropdown.Toggle>
                                <Dropdown.Menu>
                                    {/* Actually a checkbox is the best here */}
                                    <Dropdown.Item
                                        as={Button}
                                        onClick={() => {
                                            setItemFilters(new Set());
                                        }}
                                    >
                                        Reset
                                    </Dropdown.Item>
                                    {[...allItems].map(([itemId, item]) => (
                                        <Dropdown.Item
                                            key={item.id}
                                            as={Button}
                                            onClick={() => {
                                                setItemFilters(new Set([itemId]));
                                            }}
                                        >
                                            <ItemIcon region={region} item={item} height={40} />
                                            {item.name}
                                        </Dropdown.Item>
                                    ))}
                                </Dropdown.Menu>
                            </Dropdown>
                        </th>
                        <th>Cost</th>
                        <th>Item</th>
                        <th>Set</th>
                        <th>Limit</th>
                        {shopEnabled && <th>Target</th>}
                    </tr>
                </thead>
                <tbody>
                    {shops
                        .filter((shop) =>
                            itemFilters.size && amounts.size ? itemFilters.has(shop.cost.item.id) : true
                        )
                        .sort((a, b) => a.priority - b.priority)
                        .map((shop) => {
                            let limitNumIndicator = shopEnabled ? (
                                <Button
                                    variant="light"
                                    onClick={() => {
                                        filters.set(shop.id, shop.limitNum);
                                        onChange?.(filters);
                                    }}
                                >
                                    {shop.limitNum.toLocaleString()}
                                </Button>
                            ) : (
                                <>{shop.limitNum.toLocaleString()}</>
                            );

                            return (
                                <tr key={shop.id}>
                                    <td style={{ minWidth: "10em" }}>
                                        <b>{shop.name}</b>
                                        <div style={{ fontSize: "0.75rem" }} className="newline">
                                            {colorString(shop.detail)}
                                            <ScriptLink region={region} shop={shop} />
                                            <br />
                                            <div>
                                                {shop.releaseConditions.length ? (
                                                    <ul className="condition-list">
                                                        {shop.releaseConditions.map((cond, index) => (
                                                            <li key={index} style={{ fontSize: "0.75rem" }}>
                                                                {cond.closedMessage && `${cond.closedMessage} — `}
                                                                <CondTargetNumDescriptor
                                                                    region={region}
                                                                    cond={cond.condType}
                                                                    targets={cond.condValues}
                                                                    num={cond.condNum}
                                                                    quests={questCache}
                                                                    items={itemCache}
                                                                />
                                                            </li>
                                                        ))}
                                                    </ul>
                                                ) : (
                                                    ""
                                                )}
                                            </div>
                                        </div>
                                    </td>
                                    <td style={{ textAlign: "center" }}>
                                        {shop.payType !== Shop.PayType.FREE ? (
                                            <IconLink region={region} item={shop.cost.item} />
                                        ) : null}
                                    </td>
                                    <td style={{ textAlign: "center" }}>
                                        {shop.payType !== Shop.PayType.FREE ? shop.cost.amount.toLocaleString() : null}
                                    </td>
                                    <td>
                                        <ShopPurchaseDescriptor region={region} shop={shop} itemMap={itemCache} />
                                    </td>
                                    <td style={{ textAlign: "center" }}>{shop.setNum.toLocaleString()}</td>
                                    <td style={{ textAlign: "center" }}>
                                        {shop.limitNum === 0 ? <>Unlimited</> : limitNumIndicator}
                                    </td>
                                    {shopEnabled && (
                                        <>
                                            <td style={{ textAlign: "center", maxWidth: "5em" }}>
                                                <InputGroup size="sm">
                                                    <Form.Control
                                                        type="number"
                                                        value={filters.get(shop.id) ?? 0}
                                                        min={0}
                                                        max={shop.limitNum || undefined}
                                                        onChange={(event) => {
                                                            let value = +event.target.value;
                                                            if (value) filters.set(shop.id, value);
                                                            else filters.delete(shop.id);
                                                            onChange?.(filters);
                                                        }}
                                                    />
                                                </InputGroup>
                                            </td>
                                        </>
                                    )}
                                </tr>
                            );
                        })}
                </tbody>
            </Table>
        </>
    );
}
Example #11
Source File: EventsPage.tsx    From apps with MIT License 4 votes vote down vote up
render() {
        if (this.state.error) return <ErrorStatus error={this.state.error} />;

        if (this.state.loading) return <Loading />;

        const events = this.events(),
            results = events.slice(this.state.perPage * this.state.page, this.state.perPage * (this.state.page + 1));

        const pageNavigator = this.paginator(events.length);

        const currentTimestamp = getCurrentTimestamp();

        return (
            <div id="events" className="listing-page">
                <Row>
                    <Col md={12} lg={6} id="item-type">
                        <ButtonGroup>
                            {[
                                [Event.EventType.EVENT_QUEST, "Event"],
                                [Event.EventType.COMBINE_CAMPAIGN, "Servant Lvl Up"],
                                [Event.EventType.SVTEQUIP_COMBINE_CAMPAIGN, "CE Lvl Up"],
                                [Event.EventType.QUEST_CAMPAIGN, "AP Cost"],
                                [Event.EventType.WAR_BOARD, "Grail Front"],
                            ].map(([eventType, buttonText]) => {
                                return (
                                    <Button
                                        variant={
                                            this.state.activeEventTypeFilters.includes(eventType as Event.EventType)
                                                ? "success"
                                                : "outline-dark"
                                        }
                                        key={eventType}
                                        onClick={(_) => this.toggleEventTypeFilter(eventType as Event.EventType)}
                                    >
                                        {buttonText}
                                    </Button>
                                );
                            })}
                        </ButtonGroup>
                    </Col>
                    <Col md={12} lg={3} id="item-search">
                        <Form inline>
                            <Form.Control
                                placeholder={"Search"}
                                value={this.state.search ?? ""}
                                onChange={(ev: ChangeEvent) => {
                                    this.setState({
                                        search: ev.target.value,
                                        page: 0,
                                    });
                                }}
                            />
                        </Form>
                    </Col>
                </Row>
                <Row>
                    <Col sm={12}>{pageNavigator}</Col>
                </Row>
                <hr />

                <Table striped bordered hover responsive>
                    <thead>
                        <tr>
                            <th className="col-center">#</th>
                            <th className="col-center">Ongoing</th>
                            <th>Name</th>
                        </tr>
                    </thead>
                    <tbody>
                        {results.map((event) => {
                            const route = `/${this.props.region}/event/${event.id}`;
                            const isOngoing = currentTimestamp >= event.startedAt && currentTimestamp <= event.endedAt;

                            return (
                                <tr key={event.id}>
                                    <td className="col-center">
                                        <Link to={route}>{event.id}</Link>
                                    </td>
                                    <td className="col-center">
                                        {isOngoing ? (
                                            <FontAwesomeIcon
                                                icon={faCheckCircle}
                                                title="Master mission is ongoing right now"
                                            />
                                        ) : null}
                                    </td>
                                    <td>
                                        <Link to={route}>{event.name}</Link>
                                    </td>
                                </tr>
                            );
                        })}
                    </tbody>
                </Table>

                {pageNavigator}
            </div>
        );
    }
Example #12
Source File: ScriptPage.tsx    From apps with MIT License 4 votes vote down vote up
ScriptPage = (props: { region: Region; scriptId: string }) => {
    const { region, scriptId } = props;
    const showScriptLine = Manager.showScriptLine();
    const [loading, setLoading] = useState<boolean>(true);
    const [error, setError] = useState<AxiosError | undefined>(undefined);
    const [script, setScript] = useState<string>("");
    const [scriptData, setScriptData] = useState<Script.Script | undefined>(undefined);
    const [enableScene, setEnableScene] = useState<boolean>(Manager.scriptSceneEnabled());

    useEffect(() => {
        Manager.setRegion(region);
        setError(undefined);
        setLoading(true);
        Promise.all([axios.get<string>(getScriptAssetURL(region, scriptId), { timeout: 10000 }), Api.script(scriptId)])
            .then(([rawScript, scriptData]) => {
                setScript(rawScript.data);
                setScriptData(scriptData);
                setLoading(false);
            })
            .catch((e) => setError(e));
    }, [region, scriptId]);

    if (error !== undefined) return <ErrorStatus error={error} />;

    if (loading) return <Loading />;

    if (script === "" || scriptData === undefined) return null;

    document.title = `[${region}] Script ${scriptId} - Atlas Academy DB`;

    const parsedScript = parseScript(region, script);

    const audioUrls = [] as string[];
    let hasDialogueLines = false;
    const addAudioUrls = (component: ScriptComponent) => {
        switch (component.type) {
            case ScriptComponentType.DIALOGUE:
                if (component.voice !== undefined) audioUrls.push(component.voice.audioAsset);
                hasDialogueLines = true;
                break;
            case ScriptComponentType.SOUND_EFFECT:
                audioUrls.push(component.soundEffect.audioAsset);
                break;
        }
    };

    for (const { content: component } of parsedScript.components) {
        switch (component.type) {
            case ScriptComponentType.CHOICES:
                for (const choice of component.choices) {
                    for (const choiceComponent of choice.results) {
                        addAudioUrls(choiceComponent);
                    }
                }
                break;
            default:
                addAudioUrls(component);
        }
    }

    const scrollRefs = new Map(audioUrls.map((url) => [url, createRef<HTMLTableRowElement>()]));
    for (const { content: component } of parsedScript.components) {
        if (component.type === ScriptComponentType.LABEL) {
            scrollRefs.set(component.name, createRef<HTMLTableRowElement>());
        }
    }

    const scrollToRow = (assetUrl: string) => {
        let rowRef = scrollRefs.get(assetUrl);
        if (rowRef !== undefined && rowRef.current !== null) {
            rowRef.current.scrollIntoView({ behavior: "smooth" });
        }
    };

    const showRawData = new Map<string, ScriptComponentWrapper[]>();
    for (const component of parsedScript.components) {
        const typeName = ScriptComponentType[component.content.type],
            mapEntry = showRawData.get(typeName);
        if (mapEntry !== undefined) {
            mapEntry.push(component);
        } else {
            showRawData.set(typeName, [component]);
        }
    }
    showRawData.set("ALL_COMPONENTS", parsedScript.components);

    return (
        <>
            <h1>Script {scriptId}</h1>
            <br />
            <ScriptMainData
                region={region}
                scriptData={scriptData}
                wordCount={countWord(
                    region,
                    parsedScript.components.map((c) => c.content)
                )}
            >
                <ButtonGroup style={{ margin: "1em 0" }}>
                    {hasDialogueLines ? (
                        <VoiceLinePlayer
                            audioAssetUrls={audioUrls}
                            delay={new Array(audioUrls.length).fill(0).fill(1, 1)}
                            title="voice lines"
                            showTitle
                            handleNavigateAssetUrl={scrollToRow}
                        />
                    ) : null}
                    <Button
                        variant={enableScene ? "success" : "secondary"}
                        onClick={() => setEnableScene(!enableScene)}
                    >
                        Scene {enableScene ? "Enabled" : "Disabled"}
                    </Button>
                    <Button
                        variant={showScriptLine ? "success" : "secondary"}
                        onClick={() => Manager.setShowScriptLine(!showScriptLine)}
                    >
                        Line number {showScriptLine ? "shown" : "hidden"}
                    </Button>
                    <RawDataViewer text="Parsed Script" data={fromEntries(showRawData)} block={false} />
                </ButtonGroup>
                <ShowScriptLineContext.Provider value={showScriptLine}>
                    <ScriptTable region={region} script={parsedScript} showScene={enableScene} refs={scrollRefs} />
                </ShowScriptLineContext.Provider>
            </ScriptMainData>
        </>
    );
}
Example #13
Source File: ServantVoiceLines.tsx    From apps with MIT License 4 votes vote down vote up
VoiceLinesTable = ({
    region,
    voice,
    mergedDownloadNamePrefix,
    servants,
    costumes,
}: {
    region: Region;
    voice: Profile.VoiceGroup;
    mergedDownloadNamePrefix: string;
    servants: Map<number, Servant.ServantBasic>;
    costumes?: {
        [key: string]: Profile.CostumeDetail;
    };
}) => {
    const voiceLines = voice.voiceLines.sort((a, b) => (b.priority || 0) - (a.priority || 0));
    const voiceLineNames: string[] = [];
    const voiceNameCount: Record<string, number> = {};
    for (const line of voiceLines) {
        line.conds = line.conds.filter(
            (cond) => !(cond.condType === Profile.VoiceCondType.EVENT_END && cond.value === 0)
        );
        let lineName = line.overwriteName || line.name || "";
        if (lineName in voiceNameCount) {
            voiceNameCount[lineName]++;
        } else {
            voiceNameCount[lineName] = 1;
        }
        voiceLineNames.push(lineName.replace("{0}", voiceNameCount[lineName].toString()));
    }

    return (
        <Table bordered className="mb-0">
            <tbody>
                {voiceLines.map((line, index) => (
                    <tr key={`line_${index}`}>
                        <td style={{ verticalAlign: "middle" }}>
                            <b className="newline">{voiceLineNames[index]}</b>
                            <br />
                            <div className="newline">
                                {voiceTextField(region, voice.type) ? (
                                    line.text.map((line, i) => (
                                        <VoiceSubtitleFormat key={i} region={region} inputString={line} />
                                    ))
                                ) : (
                                    <VoiceSubtitleFormat region={region} inputString={line.subtitle} />
                                )}
                            </div>
                            {line.conds.length || line.playConds.length || line.summonScript ? (
                                <>
                                    <Alert variant="info" style={{ marginBottom: 0, marginTop: "1em" }}>
                                        {line.summonScript === undefined ? null : (
                                            <>
                                                Summoning Script:{" "}
                                                <ScriptDescriptor
                                                    region={region}
                                                    scriptId={line.summonScript.scriptId}
                                                    scriptType=""
                                                />
                                            </>
                                        )}
                                        {line.conds.length > 1 && (
                                            <>
                                                <b>Unlock Requirements (all of the following):</b>
                                                <br />
                                                <ul style={{ marginBottom: 0 }}>
                                                    {line.conds.map((cond, index) => (
                                                        <li key={index}>
                                                            <VoiceCondTypeDescriptor
                                                                region={region}
                                                                servants={servants}
                                                                costumes={costumes}
                                                                cond={cond}
                                                            />
                                                        </li>
                                                    ))}
                                                </ul>
                                            </>
                                        )}
                                        {line.conds.length === 1 && (
                                            <>
                                                <b>Unlock Requirement:</b>
                                                <br />
                                                <VoiceCondTypeDescriptor
                                                    region={region}
                                                    servants={servants}
                                                    costumes={costumes}
                                                    cond={line.conds[0]}
                                                />
                                                <br />
                                            </>
                                        )}
                                        <VoicePlayCondDescriptor
                                            region={region}
                                            playConds={line.playConds}
                                            servants={servants}
                                        />
                                    </Alert>
                                </>
                            ) : (
                                ""
                            )}
                        </td>
                        <td style={{ verticalAlign: "middle", width: "1px" }}>
                            <ButtonGroup>
                                <VoiceLinePlayer
                                    audioAssetUrls={line.audioAssets}
                                    delay={line.delay}
                                    title={voiceLineNames[index]}
                                />
                                <Dropdown as={ButtonGroup}>
                                    <Dropdown.Toggle variant={"info"} title={`Download ${voiceLineNames[index]}`}>
                                        <FontAwesomeIcon icon={faFileAudio} />
                                        &nbsp;
                                    </Dropdown.Toggle>

                                    <Dropdown.Menu title={`Download ${voiceLineNames[index]}`}>
                                        <Dropdown.Item
                                            title={`Download ${voiceLineNames[index]} merged file`}
                                            onClick={() => {
                                                const fileName = `${mergedDownloadNamePrefix} - ${voiceLineNames[index]}`;
                                                mergeVoiceLine(line.audioAssets, line.delay, fileName);
                                            }}
                                        >
                                            Merged
                                        </Dropdown.Item>
                                        {line.audioAssets.map((asset, i) => (
                                            <Dropdown.Item
                                                key={i}
                                                href={asset}
                                                target="_blank"
                                                title={`Download ${voiceLineNames[index]} part ${i + 1}`}
                                            >
                                                Part {i + 1}
                                            </Dropdown.Item>
                                        ))}
                                    </Dropdown.Menu>
                                </Dropdown>
                            </ButtonGroup>
                        </td>
                    </tr>
                ))}
            </tbody>
        </Table>
    );
}
Example #14
Source File: ServantsPage.tsx    From apps with MIT License 4 votes vote down vote up
render() {
        if (this.state.error) return <ErrorStatus error={this.state.error} />;

        if (this.state.loading) return <Loading />;

        const servants = this.servants(),
            hasPaginator = servants.length > this.state.perPage,
            results = servants.slice(this.state.perPage * this.state.page, this.state.perPage * (this.state.page + 1));

        return (
            <div id="servants" className="listing-page">
                <Row>
                    <Col md={12} lg="auto" id="class-name">
                        {normalClasses.map((className) => {
                            const active = this.isClassFilterActive(className);
                            return (
                                <span
                                    key={className}
                                    className={"filter"}
                                    style={{ opacity: active ? 1 : 0.5 }}
                                    onClick={(ev: MouseEvent) => {
                                        this.toggleClassFilter(className);
                                    }}
                                >
                                    <ClassIcon height={37} rarity={active ? 5 : 3} className={className} />
                                </span>
                            );
                        })}
                        <div className={"d-block d-lg-none"} style={{ flexBasis: "100%", height: 0 }}></div>
                        {extraClasses.map((className) => {
                            const active = this.isClassFilterActive(className);
                            return (
                                <span
                                    key={className}
                                    className={"filter"}
                                    style={{ opacity: active ? 1 : 0.5 }}
                                    onClick={(ev: MouseEvent) => {
                                        this.toggleClassFilter(className);
                                    }}
                                >
                                    <ClassIcon height={37} rarity={active ? 5 : 3} className={className} />
                                </span>
                            );
                        })}
                    </Col>
                    <Col sm={12} lg={3} id="servant-search">
                        <Form>
                            <Form.Control
                                placeholder={"Search"}
                                value={this.state.search ?? ""}
                                onChange={(ev: ChangeEvent) => {
                                    this.setState({ search: ev.target.value });
                                }}
                            />
                        </Form>
                    </Col>
                </Row>
                <Row>
                    <Col sm={12} md={6} lg={5} id="servant-rarity">
                        <ButtonGroup>
                            {[...new Set(this.state.servants.map((s) => s.rarity))]
                                // deduplicate star counts
                                .sort((a, b) => a - b)
                                // sort
                                .map((rarity) => (
                                    <Button
                                        variant={
                                            this.state.activeRarityFilters.includes(rarity) ? "success" : "outline-dark"
                                        }
                                        key={rarity}
                                        onClick={(ev: MouseEvent) => this.toggleRarityFilter(rarity)}
                                    >
                                        {rarity} ★
                                    </Button>
                                ))}
                        </ButtonGroup>
                    </Col>
                    <Col sm={12} md={6} lg={7}>
                        {this.paginator(servants.length)}
                    </Col>
                </Row>
                <hr />

                <Table striped bordered hover responsive>
                    <thead>
                        <tr>
                            <th className="col-center text-nowrap">
                                <Button
                                    variant=""
                                    className="py-0 px-2 border-0 align-bottom"
                                    onClick={() => {
                                        this.setState({
                                            sortKey: this.state.sortKey === "collectionNo" ? "id" : "collectionNo",
                                        });
                                    }}
                                >
                                    {this.state.sortKey === "collectionNo" ? (
                                        <FontAwesomeIcon icon={faHashtag} title="Collection No" />
                                    ) : (
                                        <FontAwesomeIcon icon={faKey} title="Servant ID" />
                                    )}
                                </Button>
                                <Button
                                    variant=""
                                    className="py-0 px-2 border-0 align-bottom"
                                    onClick={() => {
                                        this.setState({
                                            sortDirection:
                                                this.state.sortDirection === "ascending" ? "descending" : "ascending",
                                        });
                                    }}
                                >
                                    {this.state.sortDirection === "ascending" ? (
                                        <FontAwesomeIcon icon={faArrowDown19} title="Ascending" />
                                    ) : (
                                        <FontAwesomeIcon icon={faArrowDown91} title="Descending" />
                                    )}
                                </Button>
                            </th>
                            <th className="col-center">Class</th>
                            <th className="col-center">Thumbnail</th>
                            <th>Name</th>
                            <th className="rarity-col">Rarity</th>
                        </tr>
                    </thead>
                    <tbody>
                        {results.map((servant) => {
                            const route = `/${this.props.region}/servant/${servant.collectionNo}`;

                            return (
                                <tr key={servant.id}>
                                    <td className="col-center">
                                        <Link to={route}>
                                            {servant.collectionNo} (
                                            <span className="listing-svtId" lang="en-US">
                                                {servant.id}
                                            </span>
                                            )
                                        </Link>
                                    </td>
                                    <td className="col-center">
                                        <ClassIcon className={servant.className} rarity={servant.rarity} height={50} />
                                    </td>
                                    <td className="col-center">
                                        <Link to={route}>
                                            <img
                                                src={servant.face}
                                                alt={`${servant.name} face icon`}
                                                width={50}
                                                height={50}
                                            />
                                        </Link>
                                    </td>
                                    <td style={{ whiteSpace: Manager.showingJapaneseText() ? "nowrap" : "normal" }}>
                                        <Link to={route}>{servant.name}</Link>
                                    </td>
                                    <td className="rarity-col">
                                        <RarityDescriptor rarity={servant.rarity} />
                                    </td>
                                </tr>
                            );
                        })}
                    </tbody>
                </Table>

                {hasPaginator ? this.paginator(servants.length) : undefined}
            </div>
        );
    }
Example #15
Source File: WarsPage.tsx    From apps with MIT License 4 votes vote down vote up
render() {
        if (this.state.error) return <ErrorStatus error={this.state.error} />;

        if (this.state.loading) return <Loading />;

        const currentSortingOrder = this.state.sort;
        const wars = this.wars().sort((w1, w2) => (currentSortingOrder ? -1 : 1) * (w1.id - w2.id)),
            results = wars.slice(this.state.perPage * this.state.page, this.state.perPage * (this.state.page + 1));

        const pageNavigator = this.paginator(wars.length);

        return (
            <div id="wars" className="listing-page">
                <Row>
                    <Col md={12} lg={4} id="item-type">
                        <ButtonGroup>
                            {[WarType.MAIN, WarType.CHALDEA_GATE, WarType.OTHER].map((warType) => {
                                return (
                                    <Button
                                        variant={
                                            this.state.activeWarTypeFilters.includes(warType)
                                                ? "success"
                                                : "outline-dark"
                                        }
                                        key={warType}
                                        onClick={(_) => {
                                            this.toggleWarTypeFilter(warType);
                                            this.setState({ page: 0 });
                                        }}
                                    >
                                        {warType.toString()}
                                    </Button>
                                );
                            })}
                        </ButtonGroup>
                    </Col>
                    <Col md={12} lg={3} id="item-search">
                        <Form inline>
                            <Form.Control
                                placeholder={"Search"}
                                value={this.state.search ?? ""}
                                onChange={(ev: ChangeEvent) => {
                                    this.setState({
                                        search: ev.target.value,
                                        page: 0,
                                    });
                                }}
                            />
                        </Form>
                    </Col>
                </Row>
                <Row>
                    <Col sm={12}>{pageNavigator}</Col>
                </Row>
                <hr />

                <Table striped bordered hover responsive>
                    <thead>
                        <tr>
                            <th className="col-center">
                                <Button
                                    variant=""
                                    style={{ outline: "none" }}
                                    onClick={() =>
                                        this.setState({
                                            sort: currentSortingOrder ? SortingOrder.ASC : SortingOrder.DESC,
                                        })
                                    }
                                >
                                    {currentSortingOrder ? (
                                        <FontAwesomeIcon icon={faSortNumericDownAlt} />
                                    ) : (
                                        <FontAwesomeIcon icon={faSortNumericDown} />
                                    )}
                                </Button>
                            </th>
                            <th>War Name</th>
                            <th>Event</th>
                        </tr>
                    </thead>
                    <tbody>
                        {results.map((war) => {
                            const route = `/${this.props.region}/war/${war.id}`;

                            return (
                                <tr key={war.id}>
                                    <td className="col-center">
                                        <Link to={route}>{war.id}</Link>
                                    </td>
                                    <td>
                                        <Link to={route}>{war.longName}</Link>
                                    </td>
                                    <td>
                                        {war.eventId !== 0 ? (
                                            <Link to={`/${this.props.region}/event/${war.eventId}`}>
                                                {war.eventName !== "" ? war.eventName : `Event ${war.eventId}`}
                                            </Link>
                                        ) : (
                                            ""
                                        )}
                                    </td>
                                </tr>
                            );
                        })}
                    </tbody>
                </Table>

                {pageNavigator}
            </div>
        );
    }