reactstrap#Spinner TypeScript Examples

The following examples show how to use reactstrap#Spinner. 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: 3_selectTutor.tsx    From TutorBase with MIT License 5 votes vote down vote up
/* Step three of the appointment scheduler is for
   the user to pick their tutor */
export function Step3() {
    let clientFlowData = useSelector(selectClientFlowData);
    let tutors = useSelector(selectAvailableTutors);
    let dispatch = useDispatch();

    useEffect(() => {
        // Get a tutor by ID from the API
        const getTutor = async (id: String) => {
            return (await api.GetTutorById(id)).data[0];
        }

        // Get all tutors given their IDs
        const getAllTutors = (ids: Array<string>) => {
            let tutor_array: Array<Tutor> = []
            tutors = []; // Reset aval tutors array

            ids.forEach(async id => {
                let tutor = await getTutor(id);
                tutor_array = Object.assign([], tutor_array);
                tutor_array.push(tutor);
                tutor_array.push(...tutors)
                dispatch(actions.setAvailableTutors(tutor_array));
            })
        }

        getAllTutors(clientFlowData.availableTutorIds);
    }, [clientFlowData.availableTutorIds])

    const decideRender = () => {
        if (tutors.length !== 0) {
            return tutors.map((tutor, i) => (
                <TutorCard tutor={tutor} key={i} checked={clientFlowData.selectedTutor._id === tutor._id} />
            ));
        } else {
            return <p>No Tutors Found!</p>;
        }
    }

    // Loading return
    if (clientFlowData.isLoading)
        return <Spinner color="primary" />

    // Content return
    return (
        <Container>
            <h3 className="hr mt-1">Select a Tutor</h3>
            <Cards>
                {decideRender()}
            </Cards>
        </Container>
    );
}
Example #2
Source File: Payment.tsx    From reference-merchant with Apache License 2.0 5 votes vote down vote up
export default function Payment({ product, isOpen, demoMode, onClose }: PaymentProps) {
  const [paymentProcessingDetails, setPaymentProcessingDetails] = useState<
    PaymentProcessingDetails | undefined
  >();
  type PaymentState = "inactive" | "fetchingProcessingDetails" | "paying" | "paymentCleared";
  const [paymentState, setPaymentState] = useState<PaymentState>("inactive");
  const [showOrderDetails, setShowOrderDetails] = useState<boolean>(false);

  if (paymentState === "inactive" && isOpen && !!product) {
    setPaymentState("fetchingProcessingDetails");
  }

  // Initiates payment
  useEffect(() => {
    let isOutdated = false;

    const fetchPaymentUrl = async () => {
      try {
        if (paymentState !== "fetchingProcessingDetails") return;

        const payment = await new BackendClient().checkoutOne(product!.gtin);

        if (!isOutdated) {
          setPaymentProcessingDetails(payment);
          setPaymentState("paying");
        }
      } catch (e) {
        console.error("Unexpected error", e);
      }
    };

    // noinspection JSIgnoredPromiseFromCall
    fetchPaymentUrl();

    return () => {
      isOutdated = true;
    };
  }, [paymentState, product]);

  const timeoutRef = useRef<NodeJS.Timeout>();

  const onModalClosed = () => {
    setPaymentState("inactive");
    onClose();
  };

  return (
    <Modal isOpen={isOpen} centered={true} size="md" toggle={onModalClosed} fade={true}>
      <ModalHeader toggle={onModalClosed}>{product?.name}</ModalHeader>
      <ModalBody className="p-0">
        {paymentState === "fetchingProcessingDetails" && (
          <div className="d-flex justify-content-center my-5">
            <Spinner color="primary" />
          </div>
        )}

        {paymentState === "paying" && (
          <iframe
            title="Checkout form"
            height="560"
            src={
              demoMode
                ? `${paymentProcessingDetails?.paymentFormUrl}&demoMode=True`
                : paymentProcessingDetails?.paymentFormUrl
            }
            frameBorder="0"
            allowFullScreen
          />
        )}

        {paymentState === "paymentCleared" && (
          <h4 className="my-5 text-success text-center">
            <i className="fa fa-check" /> Paid successfully!
          </h4>
        )}
      </ModalBody>
    </Modal>
  );
}
Example #3
Source File: MeetingCard.tsx    From TutorBase with MIT License 4 votes vote down vote up
export function MeetingCard(props: IProps) {
    let { appt } = props;
    let cardType = appt.confirmed ? "upcoming-card" : "pending-card";
    let cardStatus = appt.confirmed ? "Upcoming" : "Pending";
    let [modalOpen, setModalOpen] = useState(false);
    let [cardExpanded, toggleCardExpansion] = useState<boolean>(false);
    let [meetingLink, setMeetingLink] = useState(appt.link !== null ? appt.link! : "");
    let [loading, setLoading] = useState(false);
    let [check, setCheck] = useState(false);
    let [err, setErr] = useState(false);
    let [clientData, setClientData] = useState<User>({
        _id: "",
        profile_img: "",
        phone: "",
        email: "",
        first_name: "",
        last_name: "",
    });
    function setMeetingLinkChange(link: React.FormEvent<HTMLInputElement>) {
        setMeetingLink(link.currentTarget.value);
    }
    async function updateMeetingLink() {
        setLoading(true);
        try {
            let res = await api.SetMeetingLink(appt.appt_id, meetingLink);
            setLoading(false);
            if (res.status === 200) {
                setCheck(true);
            }
        }
        catch {
            setLoading(false);
            setErr(true);
        }
        //setModalOpen(!modalOpen);
    }
    let [tutorFirstName, setTutorFirstName] = useState("");
    let [tutorLastName, setTutorLastName] = useState("");

    const confirmAppt = async () => {
        await api.ConfirmAppointment(appt.appt_id);
    }

    useEffect(() => {
        const getTutor = async () => {
            let tutor = (await api.GetTutorById(appt.tutor_id));
            setTutorFirstName(tutor.data[0]?.first_name ?? "Unknown");
            setTutorLastName(tutor.data[0]?.last_name ?? "Unknown");
        }

        getTutor();
    }, []);

    // Time checks for different card types
    if (!IsFutureDate(appt.start_time) && appt.confirmed){
        cardType = "completed-card";
        cardStatus = "Completed";
    }

    if (!IsFutureDate(appt.start_time) && !appt.confirmed){
        cardType = "denied-card";
        cardStatus = "Denied";
    }

    if (IsFutureDate(appt.start_time) && props.includePrevious) {
        return <></>
    }

    if (!IsFutureDate(appt.start_time) && !props.includePrevious) {
        return <></>
    }

    // Card creation
    let name = CapitalizeFirstLetter(tutorFirstName + " " + tutorLastName);
    let location = CapitalizeFirstLetter(appt.location);
    let date_time = BreakDownTime(appt.start_time);

    // Card tag setup
    let cardTag = <div className={"card-status"}>{cardStatus}</div>;
    if (cardStatus === "Pending" && props.isTutor) {
        cardTag = (
            <>
                <div className={"card-icon"}>
                    <Button color="success" onClick={() => confirmAppt()}>
                        <FontAwesomeIcon icon={faCheck} />
                    </Button>
                </div>

                <div className={"card-status"}>
                    {cardStatus}
                </div>
            </>
        );
    }

    // Card details
    let upperCardContent = (
        <>
            <div className={"card-name"}>{name}</div>
            <div className={"card-location"}>{location}</div>
            <div className={"card-time"}>{date_time[0] + " at " + date_time[1]}</div>
        </>
    );

    // Card structure
    let card = (
        <CompressedCard
            onClick={() => { toggleCardExpansion(!cardExpanded) }}
            className={cardType}
        >
            <div className={"card-container-start"}>
                {upperCardContent}
            </div>

            <div className={"card-container-end"}>
                {cardStatus === "Completed"
                    ? <FeedbackForm apptTutorId={appt.tutor_id} />
                    : <></>}
                {cardTag}
                <Button
                    color="none"
                    onClick={(e) => {
                        toggleCardExpansion(!cardExpanded)
                    }} >
                    <FontAwesomeIcon
                        icon={faArrowDown}
                    />
                </Button>
            </div>
        </CompressedCard>
    );

    if(cardExpanded) {
        card = (
            <ExpandedCard
                onClick={() => { toggleCardExpansion(!cardExpanded) }}
                className={cardType}
            >
                <div className={"card-container-start-expanded"}>{upperCardContent}</div>
                <div className={"card-container-end-expanded"}>
                    {cardStatus === "Completed" ? <FeedbackForm apptTutorId={appt.tutor_id} /> : <></>}
                    {cardTag}
                    <Button
                    color="none"
                    onClick={(e) => {
                    toggleCardExpansion(!cardExpanded)
                    }} >
                    <FontAwesomeIcon
                    icon={faArrowUp}
                    />
                    </Button>
                </div>

                <hr style={{width: '100%', backgroundColor: 'black', margin: '0 0.5em'}}/>

                <div className={"card-container-item"}>
                    {!appt.notes || appt.notes === "" ? ""
                    : "Client Notes: "
                    }
                </div>
                <div className={"break"}></div>
                <div className={"client-notes"}>{appt.notes}</div>
                <div className={"break"}></div>


                { props.isTutor ?
                <div>
                <div className={"client-notes"}>
                <Button
                    color="danger"
                    onClick={(e) => {
                        setModalOpen(!modalOpen);
                        e.stopPropagation();
                    }}
                    >
                    Add Zoom/Webex meeting link
                    </Button>
                                <Modal isOpen={modalOpen}>
                    <ModalHeader toggle={function noRefCheck(){}}>
                    Add Tutoring Meeting Link
                    </ModalHeader>
                    <ModalBody>
                    Link:
                    <Input onChange={setMeetingLinkChange} value={meetingLink}>
                    </Input>
                    </ModalBody>
                    <ModalFooter>
                    <Button
                        color={check ? "success": err ? "danger" : "primary"}

                        onClick={updateMeetingLink}
                    >
                    {loading ? (<Spinner />)
                        : check ? <FontAwesomeIcon icon={faCheck}/>
                        : err ? <div>Error<FontAwesomeIcon icon={faTimes}/></div>
                        : "Save"}
                    </Button>
                    {' '}
                    <Button onClick={() => setModalOpen(!modalOpen)}>
                        Cancel
                    </Button>
                    </ModalFooter>
                </Modal>
                </div>
                </div>
                : <div>{meetingLink === "" ? "" :
                (<div>
                    
                    <div className={"card-container-item "}>
                        Meeting Link:
                    </div>
                <div className={"client-notes"}><a href={meetingLink} target="new">{meetingLink}</a></div>
                </div>)
                }
                </div>
                }

            </ExpandedCard>
        );
    }

    return <>{card}</>;
}
Example #4
Source File: TutorPanelSignup.tsx    From TutorBase with MIT License 4 votes vote down vote up
Panel = (props: IProps) => {
    let dispatch = useDispatch();
    let id = useSelector(selectClientData).clientId;
    const [modalOpen, setModalOpen] = useState(false);
    let params : string = useLocation().pathname;
    const [selectedSubjects, setSelectedSubjects] = useState(new Set<string>());
    const [RIN, setRIN] = useState("");
    const [validRIN, setValidRIN] = useState(false);
    const [cohort, setCohort] = useState("");
    const [comments, setComments] = useState("");
    const [footerMessage, setFooterMessage] = useState("");
    const [rate, setRate] = useState(0);
    let subjects = [];
    let selectedSubjectsOutput = [];
    const [subjectsList, setSubjectsList] = useState(new Array<Subject>());
    function checkRIN(value: string) {
        if (value.length !== 9) {
            setValidRIN(false);
        }
        else {
            setValidRIN(true); 
        }
        setRIN(value);

    }
    function submit() {
        if (!validRIN
            || cohort === ""
            || cohort === "Select"
            || selectedSubjects.size === 0) {
                setFooterMessage("Please complete required fields.");
                return;
        }
        let subs: Array<String> = Array.from(selectedSubjects.keys());
        api.TutorSignup(id, RIN, subs, comments, rate).then(res =>{
            res ?
            setFooterMessage("Application submitted.")
            : setFooterMessage("Error submitting. Please try again.");
            }).catch(err => {
                setFooterMessage("Error submitting. Please try again.")
            });
    }
    
    useEffect(() => {
        // Get all avaliable subjects from API
        const getSubjects = async () => {
            return (await api.GetSubjects()).data;
        }

        getSubjects().then(value => {
                setSubjectsList(value);
            }
        )
    }, []);
    for (let i = 0; i < subjectsList.length; i++) {
        let name: string = subjectsList[i].id;
        let color = SubjectToColor(name);
        subjects.push(
            (<Button
                style={{background: color}}
                onClick={() => setSelectedSubjects(SelectedSubjectsHandler(selectedSubjects, name))}
            >
                {name}
            </Button>
            )
            );
    }
    let selectedSubs:Array<string> = Array.from(selectedSubjects.keys());
    for (let i = 0; i < selectedSubs.length; i++) {
        let name: string = selectedSubs[i];
        let color = SubjectToColor(name);
        selectedSubjectsOutput.push(
            (
               
                    <Badge
                    style={{
                        backgroundColor: color,
                        cursor:'default',
                        color: "black",
                        minWidth: '6em',
                        display: "flex",
                        flexDirection:'row',
                        alignItems: 'center',
                        marginRight: '0.5em'
                    }}
                    pill
                >
                    <div style={{
                        display: "flex",
                        flex: '50%',
                    }}>
                        {name + ' '}
                    </div> 
                    <Button 
                    close 
                    style={{
                        display: "flex",
                        flex: '50%',
                        alignItems: 'center'
                    }}
                    onClick={() => setSelectedSubjects(SelectedSubjectsHandler(selectedSubjects, name))} /> 
                    
                </Badge>
            )
        );
    }
    return (
        <div id="panel-wrapper">
            <Navbar className={classNames("navbar-expand-lg", "navbar-light", "bg-light", "border-bottom", "shadow")}>
                <Button className="btn-red" id="menu-toggle" onClick={() => {
                    dispatch(actions.toggleSidebar());
                }}>☰</Button>
            </Navbar>

            <Container fluid className="background" style={{marginBottom:'10em'}}>
                <hr></hr>
                <Row xs="2" className="parent">

                </Row>
                <div style={{display:'flex', flexDirection:'column', flexWrap:'wrap', alignContent:'center'}}>
                    {props.isLoading ? (
                        <div style={{display:'flex', flexDirection:'row', flex:'1 1 0px', flexWrap:'wrap', justifyContent:'center', marginTop:'10em'}}>
                            <Spinner style={{color:'#E66064'}}></Spinner>
                        </div>) 
                    : (
                    <div>
                        <div style={{display:'flex', flexDirection:'row', flex:'1 1 0px', flexWrap:'wrap', justifyContent:'center', marginTop:'10em'}}>
                            <h5>You are not currently signed up as a tutor. This dashboard is for tutors only. You can apply to be a TutorBase tutor below!
                            </h5></div>
                            
                            <div style={{display:'flex', flexDirection:'row', flex:'1 1 0px', flexWrap:'wrap', justifyContent:'center', marginTop:'1em'}}>
                            <Button 
                                className="btn-red" 
                                style={{height:'4em', width:'10em', borderRadius:'20em'}}
                                onClick={() => setModalOpen(true)}
                            >
                                Sign up as tutor
                            </Button>
                            <Modal
                                centered={true}
                                scrollable={true}
                                isOpen={modalOpen}
                            >
                                <ModalHeader toggle={() => setModalOpen(!modalOpen)}>
                                    Tutor Application Form
                                </ModalHeader>
                                <ModalBody>
                                <h5>RIN</h5>
                                <Input 
                                    defaultValue={RIN}
                                    onChange={(e) => checkRIN(e.target.value)}
                                    valid={validRIN}
                                    invalid={!validRIN}
                                    />
                                <p />
                                <h5>Cohort</h5>
                                <Input 
                                type="select"
                                    onChange={(e) => setCohort(e.target.value)}
                                    initialValue="Select"
                                    invalid={cohort === "" || cohort === "Select"}>
                                        <option>
                                        Select
                                    </option>
                                    <option>
                                        Freshman
                                    </option>
                                    <option>
                                        Sophomore
                                    </option>
                                    <option>
                                        Junior
                                    </option>
                                    <option>
                                        Senior
                                    </option>
                                    <option>
                                        Graduate
                                    </option>
                                    </Input>
                                <p />
                                <h5>Select Subjects to tutor</h5>
                                <ButtonGroup>
                                    {subjects}
                                    
                                </ButtonGroup>
                                <p>
                                    Selected:
                                    <Card
                                    outline={selectedSubjects.size === 0}
                                    color= {selectedSubjects.size === 0 ? "danger" : ""}>
                                <CardBody 
                                    style={{
                                        display: "flex",
                                        background: "lightgray",
                                        minHeight: "4em",
                                        flexWrap: 'wrap'
                                    }}>
                                {selectedSubjectsOutput}


                            </CardBody></Card>
                            </p>
                            <p>
                                <h5>Hourly Rate ($) (optional)</h5>
                                <Input
                                type="number"
                                    onChange={(e) => setRate(+(e.target.value))} />
                                </p>
                                <h5>Comments (optional)</h5>
                                <Input 
                                    type="textarea"
                                    onChange={(e) => setComments(e.target.value)} />

                                </ModalBody>
                                <ModalFooter>
                                <p style={{color: footerMessage === "Application submitted." ? 'green' : 'red'}}>
                                    {footerMessage}
                                </p>

                                <Button
                                    color="primary"
                                    onClick={() => submit()}
                                >
                                    Submit
                                </Button>
                                {' '}
                                <Button onClick={() => setModalOpen(false)}>
                                    Cancel
                                </Button>
                                </ModalFooter>
                            </Modal>
                        </div>
                    </div>
                    )}
                </div>
            </Container>
        </div>
    );
}
Example #5
Source File: ConfirmationDetails.tsx    From reference-merchant with Apache License 2.0 4 votes vote down vote up
function ConfirmationDetails({ orderId }: OrderDetailsProps) {
  const { t } = useTranslation(["order", "layout"]);
  const [order, setOrder] = useState<Order | undefined | null>();

  useEffect(() => {
    let isOutdated = false;

    const fetchOrder = async () => {
      try {
        const fetched = await new BackendClient().getOrderDetails(orderId);

        if (!isOutdated) {
          setOrder(fetched);
        }
      } catch (e) {
        console.error("Unexpected error", e);
      }
    };

    // noinspection JSIgnoredPromiseFromCall
    fetchOrder();

    return () => {
      isOutdated = true;
    };
  }, [orderId]);

  const cashOut = async () => {
    const client = new BackendClient();
    await client.payout(order!.vaspPaymentRef);
    const fetched = await new BackendClient().getOrderDetails(orderId);
    if (fetched) {
      setOrder(fetched);
    }
  };

  // Show spinner if order is undefined - it is being loaded
  let orderInfo = (
    <div className="d-flex justify-content-center">
      <Spinner color="primary" />
    </div>
  );

  if (order !== undefined) {
    if (order === null) {
      // There is no order with this ID
      orderInfo = (
        <Alert color="danger">
          <i className="fa fa-close" /> {t("unknown")}
        </Alert>
      );
    } else {
      orderInfo = (
        <>
          <div style={{ display: "flex", alignItems: "center" }}>
            <i className="fa fa-check-circle fa-4x" style={{ color: "#59a559" }} />
            <div style={{ marginLeft: 20, fontSize: 20, fontWeight: 500, color: "black" }}>
              {t("order_on_the_way")}
            </div>
          </div>
          <div className="h5 mt-4 mb-4 font-weight-normal text-body">
            {t("got_your_order")} <br />
            {t("order_summary")}
          </div>
          <Row style={{ alignItems: "center" }}>
            <Col xs={3}>
              <img
                src={order.products[0].product.image_url}
                width="75"
                height="75"
                alt={"product image"}
              />
            </Col>
            <Col>{order.products[0].product.name}</Col>
            <Col style={{ textAlign: "right" }}>
              {t("qty")}. {order.products[0].quantity}
            </Col>
          </Row>
          <Row className="mt-4">
            <Col xs={8}>{t("items_Total")}</Col>
            <Col xs={4} style={{ textAlign: "right" }}>
              {order.totalPrice / 1000000} XUS
            </Col>
          </Row>
          <Row className="mt-1">
            <Col xs={9}>{t("shipping")}</Col>
            <Col xs={3} className="pl-2">
              {t("free")}
            </Col>
          </Row>
          <Row className="mt-1">
            <Col xs={9}>{t("duties_taxes")}</Col>
            <Col xs={3} className="pl-2">
              {t("free")}
            </Col>
          </Row>
          <Row className="mt-1">
            <Col xs={8} className="font-weight-bold">
              {t("total_order")}
            </Col>
            <Col xs={4} style={{ textAlign: "right" }} className="font-weight-bold">
              {order.totalPrice / 1000000} XUS
            </Col>
          </Row>
        </>
      );
    }
  }

  return (
    <>
      <TestnetWarning />
      <Container className="container-very-narrow pt-5">
        <div className="text-center">
          <div className="h2">{t("layout:name")}</div>
        </div>
        <div className="d-flex flex-column justify-content-center m-3">{orderInfo}</div>
      </Container>
    </>
  );
}
Example #6
Source File: OrderDetails.tsx    From reference-merchant with Apache License 2.0 4 votes vote down vote up
function OrderDetails({ orderId }: OrderDetailsProps) {
  const { t } = useTranslation("order");
  const [order, setOrder] = useState<Order | undefined | null>();

  const tx =
    order && order.paymentStatus.blockchainTxs.length > 0
      ? order.paymentStatus.blockchainTxs[0]
      : null;
  const refundTx =
    order &&
    order.paymentStatus.blockchainTxs.length > 1 &&
    order.paymentStatus.blockchainTxs[1].isRefund
      ? order.paymentStatus.blockchainTxs[1]
      : null;

  useEffect(() => {
    let isOutdated = false;

    const fetchOrder = async () => {
      try {
        const fetched = await new BackendClient().getOrderDetails(orderId);

        if (!isOutdated) {
          setOrder(fetched);
        }
      } catch (e) {
        console.error("Unexpected error", e);
      }
    };

    // noinspection JSIgnoredPromiseFromCall
    fetchOrder();

    return () => {
      isOutdated = true;
    };
  }, [orderId]);

  const cashOut = async () => {
    const client = new BackendClient();
    await client.payout(order!.vaspPaymentRef);
    const fetched = await new BackendClient().getOrderDetails(orderId);
    if (fetched) {
      setOrder(fetched);
    }
  };

  const refund = async () => {
    const client = new BackendClient();
    await client.refund(order!.vaspPaymentRef);
    const fetched = await new BackendClient().getOrderDetails(orderId);
    if (fetched) {
      setOrder(fetched);
    }
  };

  // Show spinner if order is undefined - it is being loaded
  let orderInfo = (
    <div className="d-flex justify-content-center">
      <Spinner color="primary" />
    </div>
  );

  if (order !== undefined) {
    if (order === null) {
      // There is no order with this ID
      orderInfo = (
        <Alert color="danger">
          <i className="fa fa-close" /> {t("unknown")}
        </Alert>
      );
    } else {
      orderInfo = (
        <>
          <InfoField caption="Order ID" value={orderId.toUpperCase()} />
          <InfoField caption="Creation time" value={order.createdAt.toLocaleString()} />
          <InfoField caption="Current status" value={t(`status.${order?.paymentStatus.status}`)} />

          <div className="mt-5">
            <OrderProducts
              productOrders={order.products}
              total={order.totalPrice}
              currency={order.currency}
            />
          </div>

          <div className="mt-5">
            <h2 className="h5 font-weight-normal text-body">Payment details</h2>

            <InfoField caption="Payment ID" value={order.vaspPaymentRef.toUpperCase()} />
            <InfoField caption="Payment type" value="Direct" />
            <InfoField
              caption="Merchant Diem address"
              value={order.paymentStatus.merchantAddress}
            />
            <InfoField caption="Payer Diem address" value={tx ? tx.senderAddress : "N/A"} />
            <InfoField caption="Payer wallet type" value="Non-hosted" />
            <InfoField
              caption="Diem amount"
              value={tx ? `${tx.amount / 1000000} ${tx.currency}` : "N/A"}
            />

            <LinkField
              caption="Diem transaction ID"
              text={tx ? `${tx.transactionId}` : undefined}
              url={`https://diemexplorer.com/testnet/version/${tx?.transactionId}`}
              external
            />

            {refundTx && (
              <LinkField
                caption="Diem refund transaction ID"
                text={`${refundTx.transactionId}`}
                url={`https://diemexplorer.com/testnet/version/${refundTx.transactionId}`}
                external
              />
            )}

            <div className="mt-4">Payment events</div>
            <div className="mt-3">
              <PaymentEvents events={order.paymentStatus.events} />
            </div>

            <div className="d-flex justify-content-center m-2">
              <Button
                disabled={!order.paymentStatus.canCashOut}
                onClick={cashOut}
                className="m-2"
                color="primary"
              >
                Cash out
              </Button>
              <Button
                disabled={!order.paymentStatus.canRefund}
                onClick={refund}
                className="m-2"
                color="primary"
              >
                Refund
              </Button>
            </div>

            <div className="d-flex justify-content-center mt-5">
              <a href={`admin/order/${orderId}`} target="_blank" rel="noopener noreferrer">
                #permalink
              </a>
            </div>
          </div>
        </>
      );
    }
  }

  return (
    <Container className="container-narrow pt-5">
      <div className="text-center">
        <div className="h2">{t("title")}</div>
      </div>

      <div className="d-flex flex-column justify-content-center m-5">{orderInfo}</div>
    </Container>
  );
}