react-bootstrap#Modal TypeScript Examples
The following examples show how to use
react-bootstrap#Modal.
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: queue.tsx From remote-office-hours-queue with Apache License 2.0 | 6 votes |
ChangeMeetingTypeDialog = (props: ChangeMeetingTypeDialogProps) => {
const handleSubmit = () => {
props.onClose();
props.onSubmit(props.queue.my_meeting?.backend_type as string);
}
return (
<Modal show={props.show} onHide={props.onClose}>
<Modal.Header closeButton>
<Modal.Title>Change Meeting Type</Modal.Title>
</Modal.Header>
<Modal.Body>
<div className="row col-lg">
<p>Select Meeting Type</p>
<p className="required">*</p>
</div>
<BackendSelector backends={props.backends}
allowedBackends={new Set(props.queue.allowed_backends)}
onChange={props.onChangeBackend}
selectedBackend={props.selectedBackend}/>
</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={props.onClose}>Cancel</Button>
<Button variant="primary" onClick={handleSubmit}>OK</Button>
</Modal.Footer>
</Modal>
);
}
Example #2
Source File: common.tsx From remote-office-hours-queue with Apache License 2.0 | 6 votes |
LoginDialog = (props: LoginDialogProps) =>
<Modal show={props.visible}>
<Modal.Header>
<Modal.Title>Session Expired</Modal.Title>
</Modal.Header>
<Modal.Body>
<p className="alert alert-warning">Your session has timed out. Some work may be lost. Please login again via the "Login" link below.</p>
</Modal.Body>
<Modal.Footer>
<a href={props.loginUrl + '?next=' + location.pathname} className="btn btn-primary">Login</a>
</Modal.Footer>
</Modal>
Example #3
Source File: RawDataViewer.tsx From apps with MIT License | 6 votes |
render() {
const block = this.props.block ?? true;
return (
<>
<Button
variant="outline-info"
block={block}
onClick={() => {
this.show();
}}
>
{this.props.text || "View"}
<FontAwesomeIcon icon={faSearchPlus} />
</Button>
<Modal size={"lg"} show={this.state.showing} onHide={() => this.hide()}>
<Modal.Header closeButton>
<Modal.Title>Raw Data Viewer</Modal.Title>
</Modal.Header>
<Modal.Body lang="en-US">
{this.state.data ? (
<ReactJson
style={{ wordBreak: "break-all" }}
src={this.state.data}
collapsed={1}
theme={viewerTheme.get(Manager.theme()) ?? "rjv-default"}
enableClipboard={(clipboard) => {
if (typeof clipboard.src === "string") {
copy(clipboard.src);
}
}}
/>
) : null}
</Modal.Body>
</Modal>
</>
);
}
Example #4
Source File: EnemyActorConfigModal.tsx From apps with MIT License | 6 votes |
render() {
return (
<Modal show={this.props.open} size="xl" onHide={this.props.close}>
<Modal.Header closeButton>
<Modal.Title>Enemy Configuration</Modal.Title>
</Modal.Header>
<Modal.Body>
<Form>
<Form.Group>
<Form.Label>Name</Form.Label>
<Form.Control
type="text"
disabled={this.props.loading}
value={this.props.servantOptions?.name}
/>
</Form.Group>
</Form>
</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={this.props.close}>
Close
</Button>
<Button variant="primary" onClick={this.props.add}>
Add
</Button>
</Modal.Footer>
</Modal>
);
}
Example #5
Source File: index.tsx From nouns-monorepo with GNU General Public License v3.0 | 6 votes |
NetworkAlert = () => {
return (
<>
<Modal show={true} backdrop="static" keyboard={false}>
<Modal.Header>
<Modal.Title>Wrong Network Detected</Modal.Title>
</Modal.Header>
<Modal.Body>
<p>
Nouns DAO auctions require you to switch over {networkName()} to be able to participate.
</p>
<p>
<b>To get started, please switch your network by following the instructions below:</b>
</p>
<ol>
<li>Open Metamask</li>
<li>Click the network select dropdown</li>
<li>Click on "{metamaskNetworkName()}"</li>
</ol>
</Modal.Body>
</Modal>
</>
);
}
Example #6
Source File: CustomModal.tsx From cftracker with MIT License | 6 votes |
CustomModal = (props: PropsType) => {
const [show, setShow] = useState(false);
const handleClose = () => setShow(false);
const handleShow = () => setShow(true);
return (
<>
<button
type="button"
className={"btn " + props.theme.btn}
onClick={handleShow}>
{<FontAwesomeIcon icon={faFilter} />}
</button>
<Modal className="modal" show={show} onHide={handleClose}>
<Modal.Header className={"modal-header " + props.theme.bgText}>
<Modal.Title>{props.title}</Modal.Title>
<button
type="button"
className="btn-close"
data-bs-dismiss="modal"
aria-label="Close"
onClick={() => handleClose()}></button>
</Modal.Header>
<Modal.Body className={props.theme.bgText}>{props.children}</Modal.Body>
</Modal>
</>
);
}
Example #7
Source File: message-modal.tsx From polkabtc-ui with Apache License 2.0 | 6 votes |
export default function MessageModal(props: MessageModalProps): ReactElement {
const { t } = useTranslation();
return (
<Modal
show={props.show}
onHide={props.onClose}
size='lg'>
<Modal.Header closeButton>
<Modal.Title>{t('message')}</Modal.Title>
</Modal.Header>
<Modal.Body>
{props.statusUpdate && (
<React.Fragment>
<div className='row'>
<div className='col-xl-9 col-lg-8 col-md-6'>
{props.statusUpdate.message === '' ? '-' : props.statusUpdate.message}
</div>
</div>
</React.Fragment>
)}
</Modal.Body>
<Modal.Footer>
<Button
variant='secondary'
onClick={props.onClose}>
{t('cancel')}
</Button>
</Modal.Footer>
</Modal>
);
}
Example #8
Source File: notification-page.component.tsx From cwa-quick-test-frontend with Apache License 2.0 | 6 votes |
NotificationPage = (props: any) => {
const { t } = useTranslation();
const [show, setShow] = React.useState(true);
React.useEffect(() => {
if (props)
setShow(props.show);
}, [props, props.show])
return (
<>
<Modal
dialogClassName='align-items-end'
contentClassName='border-0 bg-transparent'
show={show}
backdrop={false}
keyboard={false}
centered
onEnter={() => setTimeout(props.setNotificationShow, 3000, false)}
>
<Alert className='qt-notification' variant='success' onClose={() => props.setNotificationShow(false)}>
<Image src={successIcon} className='mr-3 my-3' />
<p className='qt-notification-text my-auto mx-1'>
{t('translation:successfull-transferred')}
</p>
</Alert>
</Modal>
</>
)
}
Example #9
Source File: LabelModal.tsx From devex with GNU General Public License v3.0 | 5 votes |
LabelModal: React.FC<IProps> = ({ show, handleCloseModal, addLabel }) => {
const themeContext = useContext(ThemeContext);
const { theme } = themeContext!;
const [labelInput, setLabelInput] = useState("");
const handleSubmit = (e: React.SyntheticEvent) => {
e.preventDefault();
setLabelInput("");
addLabel(labelInput);
};
return (
<Modal
className={
theme === "dark"
? "custom-modal dark-theme"
: "custom-modal light-theme"
}
show={show}
onHide={handleCloseModal}
>
<div className="modal-header">
<h6>Add Label</h6>
</div>
<Modal.Body>
<Form onSubmit={handleSubmit}>
<div className="mb-3">
<Form.Control
autoFocus={true}
required
type="text"
value={labelInput}
maxLength={20}
onChange={(e) => {
setLabelInput(sanitizeHtml(e.target.value));
}}
placeholder="Label Name"
/>
</div>
<div>
<Button block type="submit">
Save
</Button>
</div>
</Form>
<div className="modal-footer">
<span>Labels can be accessed from the Labels Page</span>
<br />
<span>Label data is saved in the local storage of your browser</span>
</div>
</Modal.Body>
</Modal>
);
}
Example #10
Source File: queueManager.tsx From remote-office-hours-queue with Apache License 2.0 | 5 votes |
MeetingInfoDialog = (props: MeetingInfoDialogProps) => {
const attendeeDetails = props.meeting?.attendees.map((a, key) => <p key={key}><UserDisplay user={a}/></p>);
const generalInfo = props.meeting
&& (
<>
Attendees:
<div>{attendeeDetails}</div>
<p>
Time Joined: <DateTimeDisplay dateTime={props.meeting.created_at}/>
</p>
<p>
Agenda: {props.meeting.agenda}
</p>
</>
);
const meetingType = props.meeting?.backend_type;
let metadataInfo;
if (props.meeting && meetingType && props.meeting.backend_metadata) {
const meetingBackend = getBackendByName(meetingType, props.backends);
metadataInfo = VideoBackendNames.includes(meetingType)
? (
<>
<p>This meeting will be via <strong>{meetingBackend.friendly_name}</strong>.</p>
<DialInContent metadata={props.meeting.backend_metadata} backend={meetingBackend} isHost />
</>
)
: <div><p>This meeting will be <strong>In Person</strong>.</p></div>;
}
return (
<Modal show={!!props.meeting} onHide={props.onClose}>
<Modal.Header closeButton>
<Modal.Title data-id={props.meeting?.id}>Join Info</Modal.Title>
</Modal.Header>
<Modal.Body>
{generalInfo}
{metadataInfo}
</Modal.Body>
<Modal.Footer>
<Button variant="primary" onClick={props.onClose}>Close</Button>
</Modal.Footer>
</Modal>
);
}
Example #11
Source File: UploadTypeChoiceModal.tsx From bada-frame with GNU General Public License v3.0 | 5 votes |
export default function UploadTypeChoiceModal({
onHide,
show,
uploadFiles,
uploadFolders,
uploadGoogleTakeoutZips,
}) {
return (
<Modal
show={show}
aria-labelledby="contained-modal-title-vcenter"
centered
dialogClassName="file-type-choice-modal">
<Modal.Header
onHide={onHide}
style={{
borderBottom: 'none',
height: '4em',
}}>
<Modal.Title
id="contained-modal-title-vcenter"
style={{
fontSize: '1.8em',
marginLeft: '5%',
color: 'white',
}}>
<b>{constants.CHOOSE_UPLOAD_TYPE}</b>
</Modal.Title>
<IoMdClose
size={30}
onClick={onHide}
style={{ cursor: 'pointer' }}
/>
</Modal.Header>
<Modal.Body>
<Container>
<UploadTypeRow
uploadFunc={uploadFiles}
Icon={FileUploadIcon}
uploadName={constants.UPLOAD_FILES}
/>
<UploadTypeRow
uploadFunc={uploadFolders}
Icon={FolderUploadIcon}
uploadName={constants.UPLOAD_DIRS}
/>
<UploadTypeRow
uploadFunc={uploadGoogleTakeoutZips}
Icon={GoogleIcon}
uploadName={constants.UPLOAD_GOOGLE_TAKEOUT}
/>
</Container>
</Modal.Body>
</Modal>
);
}
Example #12
Source File: CollectionSelector.tsx From bada-frame with GNU General Public License v3.0 | 5 votes |
function CollectionSelector({
attributes,
collectionsAndTheirLatestFile,
...props
}: Props) {
const [collectionToShow, setCollectionToShow] = useState<
CollectionAndItsLatestFile[]
>([]);
useEffect(() => {
if (!attributes || !props.show) {
return;
}
const user: User = getData(LS_KEYS.USER);
const personalCollectionsOtherThanFrom =
collectionsAndTheirLatestFile?.filter(
(item) =>
item.collection.id !== attributes.fromCollection &&
item.collection.owner.id === user?.id &&
item.collection.type !== CollectionType.favorites
);
if (personalCollectionsOtherThanFrom.length === 0) {
props.onHide();
attributes.showNextModal();
} else {
setCollectionToShow(personalCollectionsOtherThanFrom);
}
}, [props.show]);
if (!attributes) {
return <Modal />;
}
const CollectionIcons: JSX.Element[] = collectionToShow?.map((item) => (
<CollectionIcon
key={item.collection.id}
onClick={() => {
attributes.callback(item.collection);
props.onHide();
}}>
<CollectionCard>
<PreviewCard file={item.file} forcedEnable />
<Card.Text className="text-center">
{item.collection.name}
</Card.Text>
</CollectionCard>
</CollectionIcon>
));
return (
<Modal
{...props}
size="xl"
centered
contentClassName="plan-selector-modal-content">
<Modal.Header closeButton onHide={() => props.onHide(true)}>
<Modal.Title>{attributes.title}</Modal.Title>
</Modal.Header>
<Modal.Body
style={{
display: 'flex',
flexWrap: 'wrap',
}}>
<AddCollectionButton showNextModal={attributes.showNextModal} />
{CollectionIcons}
</Modal.Body>
</Modal>
);
}
Example #13
Source File: disclamer-btn.component.tsx From cwa-quick-test-frontend with Apache License 2.0 | 5 votes |
DisclamerButton = (props: any) => {
const { t } = useTranslation();
const [show, setShow] = React.useState(false);
React.useEffect(() => {
setShow(props.firstTimeShow);
props.onInit();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
return (
<>
<Image
src={SpeechBubbleImage}
className='speech-bubble'
onClick={() => { setShow(true) }}
/>
<Modal
contentClassName='data-modal'
show={show}
backdrop={true}
onHide={() => { setShow(false) }}
keyboard={false}
centered
>
<Modal.Header id='data-header' className='pb-0' >
<Row>
<Col >
<Card.Title className='m-0 jcc-xs-jcfs-md' as={'h2'} >
{t('translation:disclaimer-title')}</Card.Title>
</Col>
</Row>
</Modal.Header>
<Modal.Body className='bg-light py-0'>
<hr />
<h5 className='disclaimer-text'>
<Trans>{props.disclaimerText}</Trans>
</h5>
<hr />
<FormGroupConsentCkb controlId='formDoNotShowCheckbox' title={t('translation:disclaimer-do-not-show')}
onChange={(evt: any) => props.onCheckChange(evt.currentTarget.checked)}
type='checkbox'
checked={props.checked}
/>
</Modal.Body>
<Modal.Footer id='data-footer'>
<Container className='p-0'>
<Row className='justify-content-end'>
<Col xs='6' className='p-0'>
<Button
className='py-0'
block
onClick={() => { setShow(false) }}
>
{t('translation:ok')}
</Button>
</Col>
</Row>
</Container>
</Modal.Footer>
</Modal>
</>
)
}
Example #14
Source File: StartUp.tsx From 3Speak-app with GNU General Public License v3.0 | 5 votes |
export function StartUp(props: any) {
const [show, setShow] = useState(false)
const [message, setMessage] = useState('')
useEffect(() => {
const load = async () => {
const backendStatus = (await PromiseIpc.send('core.status', undefined as any)) as any
if (backendStatus.ready === false) {
setShow(true)
const pid = setInterval(async () => {
const status = (await PromiseIpc.send('core.status', undefined as any)) as any
setMessage(status.start_progress.message)
}, 25)
PromiseIpc.send('core.ready', undefined as any).then((eda) => {
setShow(false)
clearInterval(pid)
})
}
}
void load()
}, [])
return (
<div>
<Modal show={show} backdrop={'static'} backdropClassName={'start-backdrop'}>
<Modal.Header>
<Modal.Title>App Starting Up</Modal.Title>
</Modal.Header>
<Modal.Body>
<div style={{ textAlign: 'center' }}>
<h1 style={{ paddingTop: '50px' }}>Loading</h1>
<hr />
<p style={{ fontSize: '15px' }}>{message}</p>
</div>
</Modal.Body>
</Modal>
</div>
)
}
Example #15
Source File: NetworkModal.tsx From devex with GNU General Public License v3.0 | 5 votes |
NetworkModal: React.FC<IProps> = ({ show, handleCloseModal, cb }) => {
const themeContext = useContext(ThemeContext);
const { theme } = themeContext!;
const [networkUrlInput, setNetworkUrlInput] = useState("");
const [networkNameInput, setNetworkNameInput] = useState("");
const handleSubmit = (e: React.SyntheticEvent) => {
e.preventDefault();
cb(sanitizeHtml(networkUrlInput), sanitizeHtml(networkNameInput));
setNetworkUrlInput("");
setNetworkNameInput("");
};
return (
<Modal
className={
theme === "dark"
? "custom-modal dark-theme"
: "custom-modal light-theme"
}
show={show}
onHide={handleCloseModal}
>
<div className="modal-header">
<h6>Add Network</h6>
</div>
<Modal.Body>
<Form onSubmit={handleSubmit}>
<Form.Group>
<div className="d-flex align-items-center mb-2">
<FontAwesomeIcon className="mr-3" icon={faTag} />
<Form.Control
required
type="text"
value={networkNameInput}
maxLength={20}
onChange={(e) => {
setNetworkNameInput(e.target.value.toString());
}}
placeholder="Enter Name"
/>
</div>
</Form.Group>
<Form.Group>
<div className="d-flex align-items-center mb-4">
<FontAwesomeIcon className="mr-3" icon={faLink} />
<Form.Control
required
type="text"
value={networkUrlInput}
onChange={(e) => {
setNetworkUrlInput(e.target.value.toString());
}}
placeholder="Enter Url"
/>
</div>
</Form.Group>
<div>
<Button block type="submit">
Save
</Button>
</div>
</Form>
</Modal.Body>
</Modal>
);
}
Example #16
Source File: PlayerActorConfigModal.tsx From apps with MIT License | 5 votes |
render() {
return (
<Modal animation={false} show={this.props.open} size="xl" onHide={this.props.close}>
<Modal.Header closeButton>
<Modal.Title>Servant Configuration</Modal.Title>
</Modal.Header>
<Modal.Body>
<Form>
<Form.Group>
<Form.Label>Name</Form.Label>
<Form.Control
type="text"
disabled={this.props.loading}
onBlur={this.props.validate}
onChange={(event) => this.setName(event.target.value)}
value={this.props.servantOptions?.name}
/>
</Form.Group>
<Form.Group>
<Form.Label>Level</Form.Label>
<Form.Control
type="number"
disabled={this.props.loading}
onBlur={this.props.validate}
onChange={(event) => this.setLevel(event.target.value)}
value={this.props.servantOptions.level}
/>
</Form.Group>
</Form>
</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={this.props.close}>
Close
</Button>
<Button variant="primary" onClick={this.props.add}>
Add
</Button>
</Modal.Footer>
</Modal>
);
}
Example #17
Source File: confirm-modal.component.tsx From cwa-quick-test-frontend with Apache License 2.0 | 5 votes |
ConfirmModal = (props: any) => {
const { t } = useTranslation();
const [btnOkDisabled, setBtnOkDisabled] = React.useState(true);
const handleEnter = () => {
setBtnOkDisabled(false);
}
const handleOk = () => {
if (props.handleOk) {
setBtnOkDisabled(true);
props.handleOk();
}
}
return (
<Modal
contentClassName='data-modal'
show={props.show}
backdrop="static"
keyboard={false}
onEnter={handleEnter}
centered
>
<Modal.Header id='data-header' className='pb-0' >
<Card.Title className='m-0 jcc-xs-jcfs-md' as={'h3'} >{props.title}</Card.Title>
</Modal.Header>
<Modal.Body className='bg-light'>
{props.message}
</Modal.Body>
<Modal.Footer id='data-footer'>
<Container className='p-0'>
<Row>
<Col sm='6' lg='4' className='mb-2 mb-sm-0 p-0 pr-sm-2'>
<Button
className='p-0'
block
variant='outline-primary'
onClick={props.onCancel}
>
{t('translation:cancel')}
</Button>
</Col>
<Col sm='6' lg='4' className='p-0 pl-sm-2'>
<Button
className='p-0'
block
onClick={handleOk}
disabled={btnOkDisabled}
>
{t('translation:ok')}
<Spinner
as="span"
className='btn-spinner'
animation="border"
hidden={!btnOkDisabled}
size="sm"
role="status"
aria-hidden="true"
/>
</Button>
</Col>
</Row>
</Container>
</Modal.Footer>
</Modal>
)
}
Example #18
Source File: error-page.component.tsx From cwa-quick-test-frontend with Apache License 2.0 | 5 votes |
ErrorPage = (props: any) => {
const { t } = useTranslation();
const [show, setShow] = React.useState(true);
React.useEffect(() => {
if (props)
setShow(props.show);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [props.show])
return (
<>
<Modal
contentClassName='data-modal'
show={show}
backdrop="static"
keyboard={false}
onExited={props.onExit}
centered
>
<Modal.Header id='data-header' className='pb-0' >
<Row>
<Col >
<Card.Title className='m-0 jcc-xs-jcfs-md' as={'h2'} >{t('translation:error-message')}</Card.Title>
</Col>
</Row>
</Modal.Header>
{/*
content area with process number input and radios
*/}
<Modal.Body className='py-0 bg-light'>
<hr />
<p className='text-center'>
<span className='font-weight-bold'>{t('translation:serverError')}</span>
<span>{props?.error ? props?.error?.message : props.message}</span>
</p>
<hr />
</Modal.Body>
{/*
footer with cancel and submit button
*/}
<Modal.Footer id='data-footer'>
<Button
className='py-0'
onClick={() => { props.onCancel(); props.onHide(); }}
>
{t('translation:cancel')}
</Button>
</Modal.Footer>
</Modal>
</>
)
}
Example #19
Source File: dataprivacy.component.tsx From cwa-quick-test-frontend with Apache License 2.0 | 4 votes |
DataprivacyPage = (props: any) => {
const { t } = useTranslation();
const [show, setShow] = React.useState(false);
React.useEffect(() => {
if (props)
setShow(props.show);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [props.show])
const handleClose = () => {
props.setShow(false)
}
return (
<>
<Modal
size='lg'
contentClassName='bg-light'
scrollable
show={show}
aria-labelledby="example-custom-modal-styling-title"
centered
onHide={handleClose}
>
<Modal.Header id='data-header' closeButton className='pb-0' >
<Row>
<Col >
<Card.Title className='m-0 jcc-xs-jcfs-md' as={'h2'} >{t('translation:dp-title')}</Card.Title>
</Col>
</Row>
</Modal.Header>
<hr className='mx-3 mb-0' />
<Modal.Body className='px-3 bg-light'>
<Container className='px-1 px-sm-2 px-md-3'>
<h5 className='text-justify'>
Der Schutz Ihrer persönlichen Daten hat für die T-Systems International GmbH einen hohen Stellenwert. Es ist uns wichtig, Sie darüber zu informieren, welche persönlichen Daten erfasst werden, wie diese verwendet werden und welche Gestaltungsmöglichkeiten Sie dabei haben.
</h5>
<ol>
<li className='text-justify py-3'>
<strong>Welche Daten werden erfasst, wie werden sie verwendet und wie lange werden sie gespeichert?</strong>
<ol type='a' className='pr-2 pr-md-4'>
<li>
<strong>Technische Merkmale:</strong> <br />
Wenn Sie sich an unserem Schnelltestportal anmelden, verzeichnet der Server Ihren Benutzernamen, die Teststellen-ID, den verwendeten Mandanten (und die von Ihnen ausgeführten Datenbankoperationen (z.B. Eingabe von Patientendaten, Eingabe von Testergebnissen).
Die protokollierten Daten werden ausschließlich für Zwecke der Datensicherheit, insbesondere zur Abwehr von Angriffsversuchen auf unseren Server verwendet (Art. 6 Abs. 1f DSGVO). Sie werden weder für die Erstellung von individuellen Anwenderprofilen verwendet noch an Dritte weitergegeben und werden nach Ablauf eines Zeitraums von 7 Tagen bis 30 Tagen gelöscht. Die statistische Auswertung anonymisierter Datensätze behalten wir uns vor.<br />
</li>
<li className='py-3'>
<strong>Authentifizierungsdaten:</strong> <br />
Wenn Sie sich an unserem Schnelltest-Portal anmelden, werden erfolgreiche und fehlgeschlagene Anmeldeversuche dokumentiert. Diese Dokumentation umfasst den Benutzernamen, Ihren Vor- und Nachnamen, den Zeitpunkt der Anmeldung, die IP-Adresse, von der aus die Anmeldung durchgeführt wurde und die Session-Dauer. Rechtsgrundlage dieser Verarbeitung ist § 26 Abs. 1 BDSG, soweit Sie als Beschäftigter eines Unternehmens, welches unsere Leistungen in Anspruch nimmt, tätig sind. Sind Sie auf selbständiger Basis für ein Unternehmen tätig, welches unsere Leistungen in Anspruch nimmt, erfolgt die Verarbeitung auf Grund der durch Ihren Auftraggeber eingeholten Einwilligung zur Speicherung.<br />
</li>
<li>
<strong>Archivierung von Testergebnissen:</strong> <br />
Ihr Benutzername wird zusammen mit den Patientendaten des durchgeführten Tests (Name, Vorname, Geburtsdatum, Geschlecht, Adresse, Testhersteller, eingesetzter Test, Tag des Testergebnisses) und dem Testergebnis gemäß gesetzlicher Grundlage archiviert und 10 Jahre aufbewahrt und dann gelöscht.<br />
</li>
</ol>
</li>
<li className='text-justify py-3' >
<strong>Wird mein Nutzungsverhalten ausgewertet, z. B. für Werbung oder Tracking?<br /></strong>
Es werden nur für die Nutzung des Schnelltest-Portals erforderliche Cookies verwendet. Diese Cookies sind notwendig, damit Sie durch die Seiten navigieren und wesentliche Funktionen nutzen können. Sie ermöglichen die Benutzung des Schnelltestportals. Rechtsgrundlage für diese Cookies ist Art. 6 Abs. 1b DSGVO bzw. bei Drittstaaten Art. 49 Abs. 1b DSGVO.
</li>
<Table className='my-3'>
<thead>
<tr>
<th>Firma</th>
<th>Zweck</th>
<th>Speicherdauer</th>
<th>Land der Verarbeitung</th>
</tr>
</thead>
<tbody>
<tr>
<td>T-Systems</td>
<td>Login</td>
<td>Session Cookie</td>
<td>Deutschland</td>
</tr>
</tbody>
</Table>
<li className='text-justify py-3' >
<strong>Wo finde ich die Informationen, die für mich wichtig sind?</strong><br />
Dieser <strong>Datenschutzhinweis</strong> gibt einen Überblick über die Punkte, die für die Verarbeitung Ihrer Daten in diesem Webportal durch T-Systems gelten.<br />
Weitere Informationen, auch zum Datenschutz im allgemeinen und in speziellen Produkten, erhalten Sie auf <a href='https://www.telekom.com/de/verantwortung/datenschutz-und-datensicherheit/datenschutz'>https://www.telekom.com/de/verantwortung/datenschutz-und-datensicherheit/datenschutz</a> und unter <a href='http://www.telekom.de/datenschutzhinweise'>http://www.telekom.de/datenschutzhinweise</a>.
</li>
<li className='text-justify py-3' >
<strong>Wer ist verantwortlich für die Datenverarbeitung? Wer ist mein Ansprechpartner, wenn ich Fragen zum Datenschutz bei der Telekom habe?</strong><br />
Datenverantwortliche ist die T-Systems International GmbH. Bei Fragen können Sie sich an unseren <a href='http://www.telekom.de/kontakt'>Kundenservice</a> wenden oder an unseren Datenschutzbeauftragten, Herrn Dr. Claus D. Ulmer, Friedrich-Ebert-Allee 140, 53113 Bonn, <a href='mailto:[email protected]'>[email protected]</a>.
</li>
<li className='text-justify py-3' >
<strong>Welche Rechte habe ich? </strong><br />
Sie haben das Recht,
<ol type='a' className='pr-2 pr-md-4'>
<li>
<strong>Auskunft</strong> zu verlangen zu Kategorien der verarbeiteten Daten, Verarbeitungszwecken, etwaigen Empfängern der Daten, der geplanten Speicherdauer (Art. 15 DSGVO);
</li>
<li>
die <strong>Berichtigung</strong> bzw. Ergänzung unrichtiger bzw. unvollständiger Daten zu verlangen (Art. 16 DSGVO);
</li>
<li>
eine erteilte Einwilligung jederzeit mit Wirkung für die Zukunft zu <strong>widerrufen</strong> (Art. 7 Abs. 3 DSGVO);
</li>
<li>
einer Datenverarbeitung, die aufgrund eines berechtigten Interesses erfolgen soll, aus Gründen zu <strong>widersprechen</strong>, die sich aus Ihrer besonderen Situation ergeben (Art 21 Abs. 1 DSGVO);
</li>
<li>
in bestimmten Fällen im Rahmen des Art. 17 DSGVO die <strong>Löschung</strong> von Daten zu verlangen - insbesondere soweit die Daten für den vorgesehenen Zweck nicht mehr erforderlich sind bzw. unrechtmäßig verarbeitet werden, oder Sie Ihre Einwilligung gemäß oben (c) widerrufen oder einen Widerspruch gemäß oben (d) erklärt haben;
</li>
<li>
unter bestimmten Voraussetzungen die <strong>Einschränkung</strong> von Daten zu verlangen, soweit eine Löschung nicht möglich bzw. die Löschpflicht streitig ist (Art. 18 DSGVO);
</li>
<li>
auf <strong>Datenübertragbarkeit</strong>, d.h. Sie können Ihre Daten, die Sie uns bereitgestellt haben, in einem gängigen maschinenlesbaren Format, wie z.B. CSV, erhalten und ggf. an andere übermitteln (Art. 20 DSGVO);
</li>
<li>
sich bei der zuständigen <strong>Aufsichtsbehörde</strong> über die Datenverarbeitung zu <strong>beschweren</strong> (für Telekommunikationsverträge: Bundesbeauftragter für den Datenschutz und die Informationsfreiheit; im Übrigen: Landesbeauftragte für den Datenschutz und die Informationsfreiheit Nordrhein-Westfalen).
</li>
</ol>
</li>
<li className='text-justify py-3' >
<strong>An wen gibt die Telekom meine Daten weiter?</strong><br />
<strong>An Auftragsverarbeiter</strong>, das sind Unternehmen, die wir im gesetzlich vorgesehenen Rahmen mit der Verarbeitung von Daten beauftragen, Art. 28 DSGVO (Dienstleister, Erfüllungsgehilfen). Die Telekom bleibt auch in dem Fall weiterhin für den Schutz Ihrer Daten verantwortlich. Wir beauftragen Unternehmen insbesondere in folgenden Bereichen: IT, Vertrieb, Marketing, Finanzen, Beratung, Kundenservice, Personalwesen, Logistik, Druck.<br />
<strong>Aufgrund gesetzlicher Verpflichtung</strong>: In bestimmten Fällen sind wir gesetzlich verpflichtet, bestimmte Daten an die anfragende staatliche Stelle zu übermitteln.
</li>
<li className='text-justify py-3' >
<strong>Wo werden meine Daten verarbeitet?</strong><br />
Ihre Daten werden in Deutschland und im europäischen Ausland verarbeitet. Findet eine Verarbeitung Ihrer Daten in Ausnahmefällen auch in Ländern außerhalb der Europäischen Union (in sog. Drittstaaten) statt, geschieht dies,
<ol type='a' className='pr-2 pr-md-4'>
<li>
soweit Sie hierin ausdrücklich eingewilligt haben (Art. 49 Abs. 1a DSGVO). (In den meisten Ländern außerhalb der EU entspricht das Datenschutzniveau nicht den EU Standards. Dies betrifft insbesondere umfassende Überwachungs- und Kontrollrechte staatlicher Behörden, z.B. in den USA, die in den Datenschutz der europäischen Bürgerinnen und Bürger unverhältnismäßig eingreifen,
</li>
<li>
oder soweit es für unsere Leistungserbringung Ihnen gegenüber erforderlich ist (Art. 49 Abs. 1b DSGVO),
</li>
<li>
oder soweit es gesetzlich vorgesehen ist (Art. 6 Abs. 1c DSGVO).
</li>
</ol>
Darüber hinaus erfolgt eine Verarbeitung Ihrer Daten in Drittstaaten nur, soweit durch bestimmte Maßnahmen sichergestellt ist, dass hierfür ein angemessenes Datenschutzniveau besteht (z.B. Angemessenheitsbeschluss der EU-Kommission oder sog. geeignete Garantien, Art. 44ff. DSGVO).<br/><br/>
Stand der Datenschutzhinweise 29.04.2021
</li>
</ol>
</Container>
</Modal.Body>
<hr className='mx-3 mt-0' />
{/*
footer with ok button
*/}
<Modal.Footer id='data-footer'>
<Button
className='py-0'
onClick={handleClose}
>
{t('translation:cancel')}
</Button>
</Modal.Footer>
</Modal>
</>
)
}
Example #20
Source File: Navigation.tsx From apps with MIT License | 4 votes |
render() {
return (
<>
<Navbar id={"navigation"} bg={"dark"} variant={"dark"} expand={"lg"}>
<Container fluid>
<Navbar.Brand as={Link} to="/" title="Atlas Academy Database">
Atlas Academy DB
</Navbar.Brand>
<Navbar.Toggle />
<Navbar.Collapse>
<Nav activeKey={this.props.location.pathname}>
<NavPage path="servants" description="Servants" />
<NavPage path="craft-essences" description="Craft Essences" />
<NavPage path="wars" description="Wars" />
<NavDropdown title="Other" id="dropdown-other">
<NavDropdownPage path="command-codes" description="Command Codes" />
<NavDropdownPage path="mystic-codes" description="Mystic Codes" />
<NavDropdownPage path="items" description="Materials" />
<NavDropdownPage path="events" description="Events" />
<NavDropdownPage path="bgms" description="BGMs" />
<NavDropdownPage path="master-missions" description="Master Missions" />
</NavDropdown>
<NavDropdown title="Search" id="dropdown-search">
<NavDropdownPage path="entities" description="Entities" />
<NavDropdownPage path="skills" description="Skills" />
<NavDropdownPage path="noble-phantasms" description="Noble Phantasms" />
<NavDropdownPage path="funcs" description="Functions" />
<NavDropdownPage path="buffs" description="Buffs" />
<NavDropdownPage path="quests" description="Quests" />
<NavDropdownPage path="scripts" description="Scripts" />
</NavDropdown>
<NavDropdown title="Changelog" id="dropdown-search">
<NavDropdownPage path="changes" description="Master Data" />
<NavDropdownPage path="enemy-changes" description="Enemy Data" />
</NavDropdown>
</Nav>
<Nav className={"ml-auto icons"} activeKey="">
<Row>
<Col>
<Link
to={this.regionLink(Region.JP)}
className={`nav-link ${this.regionClass(Region.JP)}`}
>
<JPFlag title="View data from the JP version" />
</Link>
</Col>
<Col>
<Link
to={this.regionLink(Region.NA)}
className={`nav-link ${this.regionClass(Region.NA)}`}
>
<USFlag title="View data from the NA version" />
</Link>
</Col>
<Col>
<Link
to={this.regionLink(Region.CN)}
className={`nav-link ${this.regionClass(Region.CN)}`}
>
<CNFlag title="View data from the CN version" />
</Link>
</Col>
<Col>
<Link
to={this.regionLink(Region.KR)}
className={`nav-link ${this.regionClass(Region.KR)}`}
>
<KRFlag title="View data from the KR version" />
</Link>
</Col>
<Col>
<Link
to={this.regionLink(Region.TW)}
className={`nav-link ${this.regionClass(Region.TW)}`}
>
<TWFlag title="View data from the TW version" />
</Link>
</Col>
</Row>
<Row>
<Col>
<Nav.Link
href="https://atlasacademy.io/discord"
target="_blank"
rel="noreferrer"
>
<FontAwesomeIcon icon={faDiscord} title="Atlas Academy Discord" />
</Nav.Link>
</Col>
<Col>
<Nav.Link
href="https://twitter.com/aacademy_fgo"
target="_blank"
rel="noreferrer"
>
<FontAwesomeIcon icon={faTwitter} title="Atlas Academy Twitter" />
</Nav.Link>
</Col>
<Col>
<Nav.Link
href="https://github.com/atlasacademy/apps"
target="_blank"
rel="noreferrer"
>
<FontAwesomeIcon icon={faGithub} title="Atlas Academy DB Github" />
</Nav.Link>
</Col>
</Row>
<Button variant={"primary"} onClick={() => this.showSettings()}>
<FontAwesomeIcon icon={faCog} title="Settings" />
</Button>
</Nav>
</Navbar.Collapse>
</Container>
</Navbar>
<Modal show={this.state.showSettings} onHide={() => this.hideSettings()}>
<Modal.Header>
<Modal.Title>Settings</Modal.Title>
<button className="modal-close" onClick={() => this.hideSettings()}>
<FontAwesomeIcon icon={faXmark} title="Close Settings" />
</button>
</Modal.Header>
<Modal.Body>
<SettingForm language={this.props.language} theme={this.props.theme} />
</Modal.Body>
</Modal>
</>
);
}
Example #21
Source File: index.tsx From nouns-monorepo with GNU General Public License v3.0 | 4 votes |
ProposalTransactionFormModal = ({
show,
onHide,
onProposalTransactionAdded,
}: ProposalTransactionFormModalProps) => {
const [address, setAddress] = useState('');
const [abi, setABI] = useState<Interface>();
const [value, setValue] = useState('');
const [func, setFunction] = useState('');
const [args, setArguments] = useState<string[]>([]);
const [isABIUploadValid, setABIUploadValid] = useState<boolean>();
const [abiFileName, setABIFileName] = useState<string | undefined>('');
const addressValidator = (s: string) => {
if (!utils.isAddress(s)) {
return false;
}
// To avoid blocking stepper progress, do not `await`
populateABIIfExists(s);
return true;
};
const valueValidator = (v: string) => !v || !new BigNumber(v).isNaN();
const argumentsValidator = (a: string[]) => {
if (!func) {
return true;
}
try {
return !!abi?._encodeParams(abi?.functions[func]?.inputs, args);
} catch {
return false;
}
};
const setArgument = (index: number, value: string) => {
const values = [...args];
values[index] = value;
setArguments(values);
};
let abiErrorTimeout: NodeJS.Timeout;
const setABIInvalid = () => {
setABIUploadValid(false);
setABIFileName(undefined);
abiErrorTimeout = setTimeout(() => {
setABIUploadValid(undefined);
}, 5_000);
};
const validateAndSetABI = (file: File | undefined) => {
if (abiErrorTimeout) {
clearTimeout(abiErrorTimeout);
}
if (!file) {
return;
}
const reader = new FileReader();
reader.onload = async e => {
try {
const abi = e?.target?.result?.toString() ?? '';
setABI(new Interface(JSON.parse(abi)));
setABIUploadValid(true);
setABIFileName(file.name);
} catch {
setABIInvalid();
}
};
reader.readAsText(file);
};
const getContractInformation = async (address: string) => {
const response = await fetch(buildEtherscanApiQuery(address));
const json = await response.json();
return json?.result?.[0];
};
const getABI = async (address: string) => {
let info = await getContractInformation(address);
if (info?.Proxy === '1' && utils.isAddress(info?.Implementation)) {
info = await getContractInformation(info.Implementation);
}
return info.ABI;
};
const populateABIIfExists = async (address: string) => {
if (abiErrorTimeout) {
clearTimeout(abiErrorTimeout);
}
try {
const result = await getABI(address);
setABI(new Interface(JSON.parse(result)));
setABIUploadValid(true);
setABIFileName('etherscan-abi-download.json');
} catch {
setABIUploadValid(undefined);
setABIFileName(undefined);
}
};
const stepForwardOrCallback = () => {
if (currentStep !== steps.length - 1) {
return stepForward();
}
onProposalTransactionAdded({
address,
value: value ? utils.parseEther(value).toString() : '0',
signature: func,
calldata: (func && abi?._encodeParams(abi?.functions[func]?.inputs, args)) || '0x',
});
clearState();
};
const steps = [
{
label: 'Address',
name: 'address',
validator: () => addressValidator(address),
},
{
label: 'Value',
name: 'value',
validator: () => valueValidator(value),
},
{
label: 'Function',
name: 'function',
},
{
label: 'Arguments',
name: 'arguments',
validator: () => argumentsValidator(args),
},
{
label: 'Summary',
name: 'summary',
},
];
const { stepForward, stepBackwards, currentStep } = useStepProgress({
steps,
startingStep: 0,
});
const clearState = () => {
setAddress('');
setABI(undefined);
setValue('');
setFunction('');
setArguments([]);
setABIUploadValid(undefined);
setABIFileName(undefined);
for (let i = currentStep; i > 0; i--) {
stepBackwards();
}
};
return (
<Modal
show={show}
onHide={() => {
onHide();
clearState();
}}
dialogClassName={classes.transactionFormModal}
centered
>
<Modal.Header closeButton>
<Modal.Title>
<Trans>Add a Proposal Transaction</Trans>
</Modal.Title>
</Modal.Header>
<Modal.Body>
<StepProgressBar className={classes.stepProgressBar} steps={steps} />
<Step step={0}>
<label htmlFor="callee-address">
<Trans>Address (Callee or Recipient)</Trans>
</label>
<FormControl
value={address}
type="text"
id="callee-address"
onChange={e => setAddress(e.target.value)}
/>
</Step>
<Step step={1}>
<label htmlFor="eth-value">
<Trans>Value in ETH (Optional)</Trans>
</label>
<FormControl value={value} id="eth-value" onChange={e => setValue(e.target.value)} />
</Step>
<Step step={2}>
<label htmlFor="function">
<Trans>Function (Optional)</Trans>
</label>
<FormControl
value={func}
as="select"
id="function"
onChange={e => setFunction(e.target.value)}
>
<option className="text-muted">Select Contract Function</option>
{abi && Object.keys(abi.functions).map(func => <option value={func}>{func}</option>)}
</FormControl>
<label style={{ marginTop: '1rem' }} htmlFor="import-abi">
{abiFileName === 'etherscan-abi-download.json' ? abiFileName : 'ABI'}
</label>
<Form.Control
type="file"
id="import-abi"
accept="application/JSON"
isValid={isABIUploadValid}
isInvalid={isABIUploadValid === false}
onChange={(e: ChangeEvent<HTMLInputElement>) => validateAndSetABI(e.target.files?.[0])}
/>
</Step>
<Step step={3}>
{abi?.functions[func]?.inputs?.length ? (
<FormGroup as={Row}>
{abi?.functions[func]?.inputs.map((input, i) => (
<>
<FormLabel column sm="3">
{input.name}
</FormLabel>
<Col sm="9">
<InputGroup className="mb-2">
<InputGroup.Text className={classes.inputGroupText}>
{input.type}
</InputGroup.Text>
<FormControl
value={args[i] ?? ''}
onChange={e => setArgument(i, e.target.value)}
/>
</InputGroup>
</Col>
</>
))}
</FormGroup>
) : (
<Trans>No arguments required </Trans>
)}
</Step>
<Step step={4}>
<Row>
<Col sm="3">
<b>
<Trans>Address</Trans>
</b>
</Col>
<Col sm="9" className="text-break">
<a href={buildEtherscanAddressLink(address)} target="_blank" rel="noreferrer">
{address}
</a>
</Col>
</Row>
<Row>
<Col sm="3">
<b>
<Trans>Value</Trans>
</b>
</Col>
<Col sm="9">{value ? `${value} ETH` : <Trans>None</Trans>}</Col>
</Row>
<Row>
<Col sm="3">
<b>
<Trans>Function</Trans>
</b>
</Col>
<Col sm="9" className="text-break">
{func || <Trans>None</Trans>}
</Col>
</Row>
<Row>
<Col sm="3">
<b>
<Trans>Arguments</Trans>
</b>
</Col>
<Col sm="9">
<hr />
</Col>
<Col sm="9">{abi?.functions[func]?.inputs?.length ? '' : <Trans>None</Trans>}</Col>
</Row>
{abi?.functions[func]?.inputs.map((input, i) => (
<Row key={i}>
<Col sm="3" className={classes.functionName}>
{i + 1}. {input.name}
</Col>
<Col sm="9" className="text-break">
{args[i]}
</Col>
</Row>
))}
</Step>
<div className="d-flex justify-content-between align-items-center pt-3">
<Button
onClick={stepBackwards}
variant="outline-secondary"
size="lg"
disabled={currentStep === 0}
>
<Trans>Back</Trans>
</Button>
<Button onClick={stepForwardOrCallback} variant="primary" size="lg">
{currentStep !== steps.length - 1 ? (
<Trans>Next</Trans>
) : (
<Trans>Add Transaction</Trans>
)}
</Button>
</div>
</Modal.Body>
</Modal>
);
}
Example #22
Source File: index.tsx From polkabtc-ui with Apache License 2.0 | 4 votes |
AccountModal = ({
open,
onClose
}: Props): JSX.Element => {
const {
polkaBtcLoaded,
address,
extensions
} = useSelector((state: StoreType) => state.general);
const { t } = useTranslation();
const dispatch = useDispatch();
const [accounts, setAccounts] = React.useState<InjectedAccountWithMeta[]>();
React.useEffect(() => {
if (!extensions.length) return;
(async () => {
try {
const theAccounts = await web3Accounts();
setAccounts(theAccounts);
} catch (error) {
// TODO: should add error handling properly
console.log('[AccountModal] error.message => ', error.message);
}
})();
}, [extensions.length]);
const handleAccountSelect = (newAddress: string) => async () => {
if (!polkaBtcLoaded) {
return;
}
// TODO: should check when the app being initialized (not check everywhere)
await web3Enable(APP_NAME);
const { signer } = await web3FromAddress(newAddress);
window.polkaBTC.setAccount(newAddress, signer);
dispatch(changeAddressAction(newAddress));
onClose();
};
return (
<Modal
show={open}
onHide={onClose}
size='lg'>
<Modal.Header closeButton>
<Modal.Title>
{extensions.length ? 'Select account' : 'Pick a wallet'}
</Modal.Title>
</Modal.Header>
<Modal.Body className='account-modal'>
{extensions.length ? (
<>
{/* Create a new account when no accounts are available */}
{!accounts?.length && (
<p className='mb-4'>
{t('no_account')}
<InterlayLink
href={POLKADOT_EXTENSION}
target='_blank'
rel='noopener noreferrer'>
{t('here')}
</InterlayLink>
.
</p>
)}
{/* List all available accounts */}
<ul className='space-y-4'>
{accounts?.map(account => (
<li
key={account.address}
className={clsx(
'rounded',
'border',
'border-solid',
'shadow-sm',
'hover:bg-gray-100'
)}>
<button
className={clsx(
'p-4',
'flex',
'items-center',
'space-x-1.5',
'w-full'
)}
onClick={handleAccountSelect(account.address)}>
<span
className={clsx(
'rounded-full',
'h-3',
'w-3',
'inline-block',
address === account.address ? 'bg-green-500' : 'bg-transparent'
)} />
<span className='font-medium'>
{account.meta.name}
</span>
<span>
{`(${shortAddress(account.address)})`}
</span>
</button>
</li>
))}
</ul>
</>
) : (
<>
<p className='mb-4'>
{t('install_supported_wallets')}
</p>
<InterlayLink
className={clsx(
'flex',
'items-center',
'px-4',
'py-2.5',
'rounded',
'shadow-sm',
'border',
'border-solid',
'border-interlayRose',
'w-1/2'
)}
href={POLKADOT_EXTENSION}
target='_blank'
rel='noopener noreferrer'>
<PolkadotExtensionLogoIcon
width={30}
height={30} />
<span style={{ marginLeft: 16 }}>Polkadot.js</span>
</InterlayLink>
</>
)}
</Modal.Body>
<Modal.Footer>
<Button
variant='secondary'
onClick={onClose}>
Close
</Button>
</Modal.Footer>
</Modal>
);
}
Example #23
Source File: update-collateral.tsx From polkabtc-ui with Apache License 2.0 | 4 votes |
export default function UpdateCollateralModal(props: UpdateCollateralProps): JSX.Element {
const { polkaBtcLoaded, vaultClientLoaded, address } = useSelector((state: StoreType) => state.general);
const { register, handleSubmit, errors } = useForm<UpdateCollateralForm>();
// denoted in DOT
const currentDOTCollateral = useSelector((state: StoreType) => state.vault.collateral);
// denoted in DOT
const [newCollateral, setNewCollateral] = useState('');
// denoted in DOT
const [currentCollateral, setCurrentCollateral] = useState('');
const [newCollateralization, setNewCollateralization] = useState('∞');
const [currentButtonText, setCurrentButtonText] = useState('');
const [isUpdatePending, setUpdatePending] = useState(false);
const [isCollateralUpdateAllowed, setCollateralUpdateAllowed] = useState(false);
const dispatch = useDispatch();
const { t } = useTranslation();
const onSubmit = handleSubmit(async () => {
if (!polkaBtcLoaded) return;
if (!vaultClientLoaded) return;
setUpdatePending(true);
try {
const newCollateralBig = new Big(newCollateral);
const currentCollateralBig = new Big(currentCollateral);
if (currentCollateralBig.gt(newCollateralBig)) {
const withdrawAmount = currentCollateralBig.sub(newCollateralBig);
await window.polkaBTC.vaults.withdrawCollateral(withdrawAmount);
} else if (currentCollateralBig.lt(newCollateralBig)) {
const depositAmount = newCollateralBig.sub(currentCollateralBig);
await window.polkaBTC.vaults.lockAdditionalCollateral(depositAmount);
} else {
closeModal();
return;
}
const vaultId = window.polkaBTC.api.createType(ACCOUNT_ID_TYPE_NAME, address);
const balanceLockedDOT = await window.polkaBTC.collateral.balanceLocked(vaultId);
dispatch(updateCollateralAction(balanceLockedDOT.toString()));
let collateralization;
try {
collateralization = new Big(parseFloat(newCollateralization) / 100);
} catch {
collateralization = undefined;
}
dispatch(updateCollateralizationAction(collateralization?.mul(100).toString()));
toast.success(t('vault.successfully_updated_collateral'));
closeModal();
} catch (error) {
toast.error(error.toString());
}
setUpdatePending(false);
});
const closeModal = () => {
props.onClose();
setNewCollateralization('');
};
const onChange = async (obj: SyntheticEvent) => {
try {
const value = (obj.target as HTMLInputElement).value;
if (value === '' || !polkaBtcLoaded || Number(value) <= 0 || isNaN(Number(value)) || !vaultClientLoaded) {
setCollateralUpdateAllowed(false);
return;
}
if (!dotToPlanck(value)) {
throw new Error('Please enter an amount greater than 1 Planck');
}
// decide if we withdraw or add collateral
if (!currentDOTCollateral) {
throw new Error('Couldn\'t fetch current vault collateral');
}
setCurrentCollateral(currentDOTCollateral);
let newCollateral = new Big(currentDOTCollateral);
if (props.status === CollateralUpdateStatus.Increase) {
newCollateral = newCollateral.add(new Big(value));
} else if (props.status === CollateralUpdateStatus.Decrease) {
newCollateral = newCollateral.sub(new Big(value));
}
setNewCollateral(newCollateral.toString());
const vaultId = window.polkaBTC.api.createType(ACCOUNT_ID_TYPE_NAME, address);
const requiredCollateral = (await window.polkaBTC.vaults.getRequiredCollateralForVault(vaultId)).toString();
// collateral update only allowed if above required collateral
const allowed = newCollateral.gte(new Big(requiredCollateral));
setCollateralUpdateAllowed(allowed);
// get the updated collateralization
const newCollateralization = await window.polkaBTC.vaults.getVaultCollateralization(vaultId, newCollateral);
if (newCollateralization === undefined) {
setNewCollateralization('∞');
} else {
// The vault API returns collateralization as a regular number rather than a percentage
setNewCollateralization(newCollateralization.mul(100).toString());
}
} catch (err) {
console.log(err);
}
};
const getButtonVariant = (status: CollateralUpdateStatus): string => {
switch (status) {
case CollateralUpdateStatus.Increase:
return 'outline-success';
case CollateralUpdateStatus.Decrease:
return 'outline-danger';
default:
return '';
}
};
function getStatusText(status: CollateralUpdateStatus): string {
switch (status) {
case CollateralUpdateStatus.Increase:
if (currentButtonText !== t('vault.increase_collateral')) {
setCurrentButtonText(t('vault.increase_collateral'));
}
return t('vault.increase_collateral');
case CollateralUpdateStatus.Decrease:
if (currentButtonText !== t('vault.withdraw_collateral')) {
setCurrentButtonText(t('vault.withdraw_collateral'));
}
return t('vault.withdraw_collateral');
default:
return currentButtonText || '';
}
}
return (
<Modal
show={props.status !== CollateralUpdateStatus.Hidden}
onHide={closeModal}>
<form onSubmit={onSubmit}>
<Modal.Header closeButton>
<Modal.Title>{getStatusText(props.status)}</Modal.Title>
</Modal.Header>
<Modal.Body>
<div className='row'>
<div className='col-12 current-collateral'>
{t('vault.current_total_collateral', { currentDOTCollateral })}
</div>
<div className='col-12'>
{t('vault.update_collateral')}
</div>
<div className='col-12 basic-addon'>
<div className='input-group'>
<input
name='collateral'
type='float'
className={'form-control custom-input' + (errors.collateral ? ' error-borders' : '')}
aria-describedby='basic-addon2'
ref={register({
required: true,
min: 0
})}
onChange={onChange}>
</input>
<div className='input-group-append'>
<span
className='input-group-text'
id='basic-addon2'>
DOT
</span>
</div>
</div>
{errors.collateral && (
<div className='input-error'>
{errors.collateral.type === 'required' ?
t('vault.collateral_is_required') :
errors.collateral.message}
{errors.collateral.type === 'min' ? t('vault.collateral_higher_than_0') : errors.collateral.message}
</div>
)}
</div>
<div className='col-12'>
{t('vault.new_collateralization')}
{
// eslint-disable-next-line no-negated-condition
newCollateralization !== '∞' ?
Number(newCollateralization) > 1000 ?
' more than 1000%' :
' ' + roundTwoDecimals(newCollateralization || '0') + '%' :
' ' + newCollateralization
}
</div>
</div>
</Modal.Body>
<Modal.Footer className='row justify-center'>
<ButtonMaybePending
variant={getButtonVariant(props.status)}
isPending={isUpdatePending}
type='submit'
disabled={!isCollateralUpdateAllowed}>
{getStatusText(props.status)}
</ButtonMaybePending>
</Modal.Footer>
</form>
</Modal>
);
}
Example #24
Source File: request-replacement.tsx From polkabtc-ui with Apache License 2.0 | 4 votes |
export default function RequestReplacementModal(props: RequestReplacementProps): JSX.Element {
const { register, handleSubmit, errors } = useForm<RequestReplacementForm>();
const dispatch = useDispatch();
const { address } = useSelector((state: StoreType) => state.general);
const lockedDot = useSelector((state: StoreType) => state.vault.collateral);
const lockedBtc = useSelector((state: StoreType) => state.vault.lockedBTC);
const [isRequestPending, setRequestPending] = useState(false);
const { t } = useTranslation();
const onSubmit = handleSubmit(async ({ amount }) => {
setRequestPending(true);
try {
if (btcToSat(amount.toString()) === undefined) {
throw new Error('Amount to convert is less than 1 satoshi.');
}
const dustValue = await window.polkaBTC.redeem.getDustValue();
const amountPolkaBtc = new Big(amount);
if (amountPolkaBtc <= dustValue) {
throw new Error(`Please enter an amount greater than Bitcoin dust (${dustValue.toString()} BTC)`);
}
await window.polkaBTC.replace.request(amountPolkaBtc);
const vaultId = window.polkaBTC.api.createType(ACCOUNT_ID_TYPE_NAME, address);
const requests = await window.polkaBTC.vaults.mapReplaceRequests(vaultId);
if (!requests) return;
dispatch(addReplaceRequestsAction(parachainToUIReplaceRequests(requests)));
toast.success('Replacment request is submitted');
props.onClose();
} catch (error) {
toast.error(error.toString());
}
setRequestPending(false);
});
return (
<Modal
show={props.show}
onHide={props.onClose}>
<form onSubmit={onSubmit}>
<Modal.Header closeButton>
<Modal.Title>{t('vault.request_replacement')}</Modal.Title>
</Modal.Header>
<Modal.Body>
<div className='row'>
<div className='col-12 request-header'>{t('vault.withdraw_your_collateral')}</div>
<div className='col-12'>{t('vault.your_have')}</div>
<div className='col-12'> {lockedDot} DOT</div>
<div className='col-12 vault-empty-space'>
{t('locked')} {lockedBtc} BTC
</div>
<div className='col-12 vault-empty-space'>{t('vault.replace_amount')}</div>
<div className='col-12'>
<div className='input-group'>
<input
name='amount'
type='float'
className={'form-control custom-input' + (errors.amount ? ' error-borders' : '')}
aria-describedby='basic-addon2'
ref={register({
required: true
})}>
</input>
<div className='input-group-append'>
<span
className='input-group-text'
id='basic-addon2'>
PolkaBTC
</span>
</div>
{errors.amount && (
<div className='input-error'>
{errors.amount.type === 'required' ?
'Amount is required' :
errors.amount.message}
</div>
)}
</div>
</div>
</div>
</Modal.Body>
<Modal.Footer>
<Button
variant='secondary'
onClick={props.onClose}>
{t('cancel')}
</Button>
<ButtonMaybePending
variant='outline-danger'
type='submit'
isPending={isRequestPending}>
{t('request')}
</ButtonMaybePending>
</Modal.Footer>
</form>
</Modal>
);
}
Example #25
Source File: process-input.component.tsx From cwa-quick-test-frontend with Apache License 2.0 | 4 votes |
ProcessIdInput = (props: any) => {
const { t } = useTranslation();
const [processNo, setProcessNo] = React.useState('');
const [btnOkDisabled, setBtnOkDisabled] = React.useState(false);
const processIds = useGetPendingProcessIds(undefined, props.handleError);
const handleCancel = () => {
props.onCancel();
// props.onHide();
}
const handleOk = () => {
setBtnOkDisabled(true);
props.onChange(processNo);
// props.onHide();
}
const handleEnter = () => {
setBtnOkDisabled(false);
}
return (
<>
<Modal
contentClassName='data-modal'
show={props.show}
backdrop="static"
keyboard={false}
centered
onEnter={handleEnter}
>
<Modal.Header id='data-header' className='pb-0' >
<Row>
<Col >
<Card.Title className='m-0 jcc-xs-jcfs-md' as={'h2'} >{t('translation:testId-input-header')}</Card.Title>
</Col>
</Row>
</Modal.Header>
{/*
content area with process number input and radios
*/}
<Modal.Body className='py-0 bg-light'>
<hr />
< FormGroupInput controlId='formProcessModalInput' title={t('translation:process-number')}
value={processNo}
onChange={(evt: any) => setProcessNo(evt.currentTarget.value)}
required
min={utils.shortHashLen}
maxLength={utils.shortHashLen}
pattern={utils.pattern.processNo}
datalistId='processId-list'
datalist={processIds ? processIds.map((i: IShortHashedGuid) => <option key={i.shortHashedGuid} value={i.shortHashedGuid} />) : undefined}
/>
<hr />
</Modal.Body>
{/*
footer with cancel and submit button
*/}
<Modal.Footer id='data-footer'>
<Container className='p-0'>
<Row>
<Col xs='6' md='4' className='pl-0'>
<Button
className='py-0'
block
variant='outline-primary'
onClick={handleCancel}
>
{t('translation:cancel')}
</Button>
</Col>
<Col xs='6' md='4' className='pr-0'>
<Button
className='py-0'
block
onClick={handleOk}
disabled={processNo.length !== utils.shortHashLen || btnOkDisabled}
>
{t('translation:ok')}
<Spinner
as="span"
className='btn-spinner'
animation="border"
hidden={!btnOkDisabled}
size="sm"
role="status"
aria-hidden="true"
/>
</Button>
</Col>
</Row>
</Container>
</Modal.Footer>
</Modal>
</>
)
}
Example #26
Source File: redeem-modal.tsx From polkabtc-ui with Apache License 2.0 | 4 votes |
function RedeemModal(props: RedeemModalProps): ReactElement {
const { address, prices } = useSelector((state: StoreType) => state.general);
const selectedIdRequest = useSelector((state: StoreType) => state.redeem.id);
const userRedeemRequests = useSelector((state: StoreType) => state.redeem.redeemRequests).get(address) || [];
const request = userRedeemRequests.filter(request => request.id === selectedIdRequest)[0];
const { t } = useTranslation();
return (
<Modal
className='redeem-modal'
show={props.show}
onHide={props.onClose}
size='xl'>
{request && (
<>
<div className='redeem-modal-title'>{t('issue_page.request', { id: request.id })}</div>
<i
className='fas fa-times close-icon'
onClick={props.onClose} />
<div className='redeem-modal-horizontal-line' />
<Modal.Body>
<div className='row'>
<div className='col-xl-6 col-lg-12 justify-center'>
<div className='redeem-amount'>
<span className='wizard-number'>{request.amountPolkaBTC}</span> PolkaBTC
</div>
<div className='row usd-price-modal'>
<div className='col'>
{'~ $' + getUsdAmount(request.amountPolkaBTC || '0', prices.bitcoin.usd)}
</div>
</div>
<div className='step-item row'>
<div className='col-6 temp-text-left'>{t('bridge_fee')}</div>
<div className='col-6'>
<BitcoinLogoIcon
className='inline-block'
width={23}
height={23} />
{' '}
{request.fee} BTC
<div className='send-price'>
{'~ $' + getUsdAmount(request.fee, prices.bitcoin.usd)}
</div>
</div>
</div>
<div className='step-item row'>
<div className='col-6 temp-text-left'>{t('bitcoin_network_fee')}</div>
<div className='col-6'>
<BitcoinLogoIcon
className='inline-block'
width={23}
height={23} />
{' '}
{request.btcTransferFee} BTC
<div className='send-price'>
{'~ $' + getUsdAmount(request.btcTransferFee, prices.bitcoin.usd)}
</div>
</div>
</div>
<hr className='total-divider' />
<div className='step-item row'>
<div className='col-6 total-amount temp-text-left total-added-value'>{t('you_will_receive')}</div>
<div className='col-6 total-amount'>
<BitcoinLogoIcon
className='inline-block'
width={23}
height={23} />
{request.amountBTC} BTC
<div className='send-price'>
{'~ $' + getUsdAmount(request.amountBTC, prices.bitcoin.usd)}
</div>
</div>
</div>
<div className='step-item row'>
<div className='col-6 temp-text-left'>{t('issue_page.destination_address')}</div>
<div className='col-6 right-text'>{shortAddress(request.userBTCAddress || '')}</div>
</div>
<div className='step-item row'>
<div className='col-6 temp-text-left'>{t('issue_page.parachain_block')}</div>
<div className='col-6 right-text'>{request.creation}</div>
</div>
<div className='step-item row'>
<div className='col-6 temp-text-left'>{t('issue_page.vault_dot_address')}</div>
<div className='col-6 right-text'>{shortAddress(request.vaultDOTAddress || '')}</div>
</div>
</div>
<div className='col-xl-6 col-lg-12'>
{request.status === RedeemRequestStatus.Expired ? (
<ReimburseView
request={request}
onClose={props.onClose} />
) : (
<StatusView request={request} />
)}
</div>
</div>
</Modal.Body>
</>
)}
</Modal>
);
}
Example #27
Source File: issue-modal.tsx From polkabtc-ui with Apache License 2.0 | 4 votes |
IssueModal = (props: IssueModalProps): JSX.Element => {
const { address, prices } = useSelector((state: StoreType) => state.general);
const selectedIdRequest = useSelector((state: StoreType) => state.issue.id);
const userIssueRequests = useSelector((state: StoreType) => state.issue.issueRequests).get(address) || [];
const request = userIssueRequests.filter(request => request.id === selectedIdRequest)[0];
const { t } = useTranslation();
const renderModalStatusPanel = (request: IssueRequest) => {
switch (request.status) {
case IssueRequestStatus.PendingWithBtcTxNotFound: {
return <PaymentView request={request} />;
}
case IssueRequestStatus.RequestedRefund: {
return <WhoopsView request={request} />;
}
default: {
return <StatusView request={request} />;
}
}
};
return (
<Modal
className='issue-modal'
show={props.open}
onHide={props.onClose}
size='xl'>
{request && (
<>
<h4
className={clsx(
'break-words',
'font-medium',
'text-base',
'text-interlayRose',
'text-center',
'uppercase'
)}>
{t('issue_page.request', { id: request.id })}
</h4>
{/* ray test touch < */}
{/* TODO: could be a component */}
<i
className='fas fa-times close-icon'
onClick={props.onClose} />
{/* ray test touch > */}
<div className='issue-modal-horizontal-line' />
<Modal.Body>
<div className='row'>
<div className='col-xl-6 col-lg-12 justify-center'>
<div className='issue-amount'>
<span className='wizard-number'>
{request.issuedAmountBtc || request.requestedAmountPolkaBTC}
</span>
PolkaBTC
</div>
<div className='row usd-price-modal'>
<div className='col'>
{'~ $' + getUsdAmount(
request.issuedAmountBtc || request.requestedAmountPolkaBTC || '0',
prices.bitcoin.usd
)}
</div>
</div>
<div className='step-item row'>
<div className='col-6 temp-text-left'>{t('bridge_fee')}</div>
<div className='col-6 right-text'>
<BitcoinLogoIcon
className='inline-block'
width={23}
height={23} />
{' '}
{displayBtcAmount(request.fee)} BTC
<div className='send-price'>
{'~ $' + getUsdAmount(request.fee, prices.bitcoin.usd)}
</div>
</div>
</div>
<hr className='total-divider' />
<div className='step-item row'>
<div className='col-6 total-added-value temp-text-left'>{t('total_deposit')}</div>
<div className='col-6 total-amount right-text'>
<BitcoinLogoIcon
className='inline-block'
width={23}
height={23} />
{' '}
{displayBtcAmount(request.amountBTC)}
{' '}
BTC
<div className='send-price'>
{'~ $' + getUsdAmount(
request.issuedAmountBtc || request.requestedAmountPolkaBTC,
prices.bitcoin.usd
)}
</div>
</div>
</div>
{/* TODO: should be reusable */}
<div className='step-item row mt-2'>
<div className='col-6 temp-text-left'>{t('issue_page.destination_address')}</div>
<div className='col-6 right-text'>{shortAddress(address)}</div>
</div>
<div className='step-item row'>
<div className='col-6 temp-text-left'>{t('issue_page.parachain_block')}</div>
<div className='col-6 right-text'>{request.creation}</div>
</div>
<div className='step-item row'>
<div className='col-6 temp-text-left'>{t('issue_page.vault_dot_address')}</div>
<div className='col-6 right-text'>{shortAddress(request.vaultDOTAddress)}</div>
</div>
<div className='step-item row'>
<div className='col-6 temp-text-left'>{t('issue_page.vault_btc_address')}</div>
<div className='col-6 right-text'>{shortAddress(request.vaultBTCAddress)}</div>
</div>
<div className='row justify-center mt-3'>
<div className='col-9 note-title'>{t('note')}:</div>
</div>
<div className='row justify-center'>
<div className='col-9 note-text'>{t('issue_page.fully_decentralized')}</div>
</div>
</div>
<div className='col-xl-6 col-lg-12'>{renderModalStatusPanel(request)}</div>
</div>
</Modal.Body>
</>
)}
</Modal>
);
}
Example #28
Source File: UploadProgress.tsx From bada-frame with GNU General Public License v3.0 | 4 votes |
export default function UploadProgress(props: Props) {
const appContext = useContext(AppContext);
const fileProgressStatuses = [] as FileProgresses[];
const fileUploadResultMap = new Map<FileUploadResults, number[]>();
let filesNotUploaded = false;
let sectionInfo = null;
if (props.fileProgress) {
for (const [localID, progress] of props.fileProgress) {
fileProgressStatuses.push({
fileID: localID,
progress,
});
}
}
if (props.uploadResult) {
for (const [localID, progress] of props.uploadResult) {
if (!fileUploadResultMap.has(progress)) {
fileUploadResultMap.set(progress, []);
}
if (
progress !== FileUploadResults.UPLOADED &&
progress !== FileUploadResults.UPLOADED_WITH_STATIC_THUMBNAIL
) {
filesNotUploaded = true;
}
const fileList = fileUploadResultMap.get(progress);
fileUploadResultMap.set(progress, [...fileList, localID]);
}
}
if (props.hasLivePhotos) {
sectionInfo = constants.LIVE_PHOTOS_DETECTED();
}
function handleHideModal(): () => void {
return props.uploadStage !== UPLOAD_STAGES.FINISH
? () => {
appContext.setDialogMessage({
title: constants.STOP_UPLOADS_HEADER,
content: constants.STOP_ALL_UPLOADS_MESSAGE,
proceed: {
text: constants.YES_STOP_UPLOADS,
variant: 'danger',
action: props.cancelUploads,
},
close: {
text: constants.NO,
variant: 'secondary',
action: () => {},
},
});
}
: props.closeModal;
}
return (
<>
<Modal
show={props.show}
aria-labelledby="contained-modal-title-vcenter"
centered
backdrop={fileProgressStatuses?.length !== 0 ? 'static' : true}>
<Modal.Header
style={{
display: 'flex',
justifyContent: 'center',
textAlign: 'center',
borderBottom: 'none',
paddingTop: '30px',
paddingBottom: '0px',
}}>
<h4 style={{ width: '100%' }}>
{props.uploadStage === UPLOAD_STAGES.UPLOADING
? constants.UPLOAD[props.uploadStage](
props.fileCounter
)
: constants.UPLOAD[props.uploadStage]}
</h4>
<IoMdClose
size={30}
onClick={handleHideModal()}
style={{ cursor: 'pointer' }}
/>
</Modal.Header>
<Modal.Body>
{(props.uploadStage ===
UPLOAD_STAGES.READING_GOOGLE_METADATA_FILES ||
props.uploadStage ===
UPLOAD_STAGES.EXTRACTING_METADATA ||
props.uploadStage === UPLOAD_STAGES.UPLOADING) && (
<ProgressBar
now={props.now}
animated
variant="upload-progress-bar"
/>
)}
{props.uploadStage === UPLOAD_STAGES.UPLOADING && (
<InProgressSection
filenames={props.filenames}
fileProgressStatuses={fileProgressStatuses}
sectionTitle={constants.INPROGRESS_UPLOADS}
sectionInfo={sectionInfo}
/>
)}
<ResultSection
filenames={props.filenames}
fileUploadResultMap={fileUploadResultMap}
fileUploadResult={FileUploadResults.UPLOADED}
sectionTitle={constants.SUCCESSFUL_UPLOADS}
/>
<ResultSection
filenames={props.filenames}
fileUploadResultMap={fileUploadResultMap}
fileUploadResult={
FileUploadResults.UPLOADED_WITH_STATIC_THUMBNAIL
}
sectionTitle={
constants.THUMBNAIL_GENERATION_FAILED_UPLOADS
}
sectionInfo={constants.THUMBNAIL_GENERATION_FAILED_INFO}
/>
{props.uploadStage === UPLOAD_STAGES.FINISH &&
filesNotUploaded && (
<NotUploadSectionHeader>
{constants.FILE_NOT_UPLOADED_LIST}
</NotUploadSectionHeader>
)}
<ResultSection
filenames={props.filenames}
fileUploadResultMap={fileUploadResultMap}
fileUploadResult={FileUploadResults.BLOCKED}
sectionTitle={constants.BLOCKED_UPLOADS}
sectionInfo={constants.ETAGS_BLOCKED(
getOSSpecificDesktopAppDownloadLink()
)}
/>
<ResultSection
filenames={props.filenames}
fileUploadResultMap={fileUploadResultMap}
fileUploadResult={FileUploadResults.FAILED}
sectionTitle={constants.FAILED_UPLOADS}
/>
<ResultSection
filenames={props.filenames}
fileUploadResultMap={fileUploadResultMap}
fileUploadResult={FileUploadResults.ALREADY_UPLOADED}
sectionTitle={constants.SKIPPED_FILES}
sectionInfo={constants.SKIPPED_INFO}
/>
<ResultSection
filenames={props.filenames}
fileUploadResultMap={fileUploadResultMap}
fileUploadResult={
FileUploadResults.LARGER_THAN_AVAILABLE_STORAGE
}
sectionTitle={
constants.LARGER_THAN_AVAILABLE_STORAGE_UPLOADS
}
sectionInfo={
constants.LARGER_THAN_AVAILABLE_STORAGE_INFO
}
/>
<ResultSection
filenames={props.filenames}
fileUploadResultMap={fileUploadResultMap}
fileUploadResult={FileUploadResults.UNSUPPORTED}
sectionTitle={constants.UNSUPPORTED_FILES}
sectionInfo={constants.UNSUPPORTED_INFO}
/>
<ResultSection
filenames={props.filenames}
fileUploadResultMap={fileUploadResultMap}
fileUploadResult={FileUploadResults.TOO_LARGE}
sectionTitle={constants.TOO_LARGE_UPLOADS}
sectionInfo={constants.TOO_LARGE_INFO}
/>
</Modal.Body>
{props.uploadStage === UPLOAD_STAGES.FINISH && (
<Modal.Footer style={{ border: 'none' }}>
{props.uploadStage === UPLOAD_STAGES.FINISH &&
(fileUploadResultMap?.get(FileUploadResults.FAILED)
?.length > 0 ||
fileUploadResultMap?.get(FileUploadResults.BLOCKED)
?.length > 0 ? (
<Button
variant="outline-success"
style={{ width: '100%' }}
onClick={props.retryFailed}>
{constants.RETRY_FAILED}
</Button>
) : (
<Button
variant="outline-secondary"
style={{ width: '100%' }}
onClick={props.closeModal}>
{constants.CLOSE}
</Button>
))}
</Modal.Footer>
)}
</Modal>
</>
);
}
Example #29
Source File: PlanSelector.tsx From bada-frame with GNU General Public License v3.0 | 4 votes |
function PlanSelector(props: Props) {
const subscription: Subscription = getUserSubscription();
const [plans, setPlans] = useState<Plan[]>(null);
const [planPeriod, setPlanPeriod] = useState<PLAN_PERIOD>(PLAN_PERIOD.YEAR);
const galleryContext = useContext(GalleryContext);
const appContext = useContext(AppContext);
const togglePeriod = () => {
setPlanPeriod((prevPeriod) =>
prevPeriod === PLAN_PERIOD.MONTH
? PLAN_PERIOD.YEAR
: PLAN_PERIOD.MONTH
);
};
function onReopenClick() {
appContext.closeMessageDialog();
galleryContext.showPlanSelectorModal();
}
useEffect(() => {
if (!props.modalView) {
return;
}
const main = async () => {
try {
props.setLoading(true);
let plans = await billingService.getPlans();
const planNotListed =
plans.filter((plan) =>
isUserSubscribedPlan(plan, subscription)
).length === 0;
if (
subscription &&
!isOnFreePlan(subscription) &&
planNotListed
) {
plans = [planForSubscription(subscription), ...plans];
}
setPlans(plans);
} catch (e) {
logError(e, 'plan selector modal open failed');
props.closeModal();
props.setDialogMessage({
title: constants.OPEN_PLAN_SELECTOR_MODAL_FAILED,
content: constants.UNKNOWN_ERROR,
close: { text: 'close', variant: 'danger' },
proceed: {
text: constants.REOPEN_PLAN_SELECTOR_MODAL,
variant: 'success',
action: onReopenClick,
},
});
} finally {
props.setLoading(false);
}
};
main();
}, [props.modalView]);
async function onPlanSelect(plan: Plan) {
if (
hasMobileSubscription(subscription) &&
!isSubscriptionCancelled(subscription)
) {
props.setDialogMessage({
title: constants.ERROR,
content: constants.CANCEL_SUBSCRIPTION_ON_MOBILE,
close: { variant: 'danger' },
});
} else if (
hasPaypalSubscription(subscription) &&
!isSubscriptionCancelled(subscription)
) {
props.setDialogMessage({
title: constants.MANAGE_PLAN,
content: constants.PAYPAL_MANAGE_NOT_SUPPORTED_MESSAGE(),
close: { variant: 'danger' },
});
} else if (hasStripeSubscription(subscription)) {
props.setDialogMessage({
title: `${constants.CONFIRM} ${reverseString(
constants.UPDATE_SUBSCRIPTION
)}`,
content: constants.UPDATE_SUBSCRIPTION_MESSAGE,
staticBackdrop: true,
proceed: {
text: constants.UPDATE_SUBSCRIPTION,
action: updateSubscription.bind(
null,
plan,
props.setDialogMessage,
props.setLoading,
props.closeModal
),
variant: 'success',
},
close: { text: constants.CANCEL },
});
} else {
try {
props.setLoading(true);
await billingService.buySubscription(plan.stripeID);
} catch (e) {
props.setLoading(false);
props.setDialogMessage({
title: constants.ERROR,
content: constants.SUBSCRIPTION_PURCHASE_FAILED,
close: { variant: 'danger' },
});
}
}
}
const PlanIcons: JSX.Element[] = plans
?.filter((plan) => plan.period === planPeriod)
?.map((plan) => (
<PlanIcon
key={plan.stripeID}
className="subscription-plan-selector"
currentlySubscribed={isUserSubscribedPlan(plan, subscription)}
onClick={
isUserSubscribedPlan(plan, subscription)
? () => {}
: async () => await onPlanSelect(plan)
}>
<div>
<span
style={{
color: '#ECECEC',
fontWeight: 900,
fontSize: '40px',
lineHeight: '40px',
}}>
{convertBytesToGBs(plan.storage, 0)}
</span>
<span
style={{
color: '#858585',
fontSize: '24px',
fontWeight: 900,
}}>
{' '}
GB
</span>
</div>
<div
className="bold-text"
style={{
color: '#aaa',
lineHeight: '36px',
fontSize: '20px',
}}>
{`${plan.price} / ${plan.period}`}
</div>
<Button
variant="outline-success"
block
style={{
marginTop: '20px',
fontSize: '14px',
display: 'flex',
justifyContent: 'center',
}}
disabled={isUserSubscribedPlan(plan, subscription)}>
{constants.CHOOSE_PLAN_BTN}
<ArrowEast style={{ marginLeft: '5px' }} />
</Button>
</PlanIcon>
));
return (
<Modal
show={props.modalView}
onHide={props.closeModal}
size="xl"
centered
backdrop={hasPaidSubscription(subscription) ? true : 'static'}
contentClassName="plan-selector-modal-content">
<Modal.Header closeButton>
<Modal.Title
style={{
marginLeft: '12px',
width: '100%',
textAlign: 'center',
}}>
<span>
{hasPaidSubscription(subscription)
? constants.MANAGE_PLAN
: constants.CHOOSE_PLAN}
</span>
</Modal.Title>
</Modal.Header>
<Modal.Body style={{ marginTop: '20px' }}>
<DeadCenter>
<div style={{ display: 'flex' }}>
<span
className="bold-text"
style={{ fontSize: '16px' }}>
{constants.MONTHLY}
</span>
<Form.Switch
checked={planPeriod === PLAN_PERIOD.YEAR}
id="plan-period-toggler"
style={{
margin: '-4px 0 20px 15px',
fontSize: '10px',
}}
className="custom-switch-md"
onChange={togglePeriod}
/>
<span
className="bold-text"
style={{ fontSize: '16px' }}>
{constants.YEARLY}
</span>
</div>
</DeadCenter>
<div
style={{
display: 'flex',
justifyContent: 'space-around',
flexWrap: 'wrap',
minHeight: '212px',
margin: '5px 0',
}}>
{plans && PlanIcons}
</div>
<DeadCenter style={{ marginBottom: '30px' }}>
{hasPaidSubscription(subscription) ? (
<>
{hasStripeSubscription(subscription) && (
<>
{isSubscriptionCancelled(subscription) ? (
<LinkButton
variant="success"
onClick={() =>
props.setDialogMessage({
title: constants.CONFIRM_ACTIVATE_SUBSCRIPTION,
content:
constants.ACTIVATE_SUBSCRIPTION_MESSAGE(
subscription.expiryTime
),
staticBackdrop: true,
proceed: {
text: constants.ACTIVATE_SUBSCRIPTION,
action: activateSubscription.bind(
null,
props.setDialogMessage,
props.closeModal,
props.setLoading
),
variant: 'success',
},
close: {
text: constants.CANCEL,
},
})
}>
{constants.ACTIVATE_SUBSCRIPTION}
</LinkButton>
) : (
<LinkButton
variant="danger"
onClick={() =>
props.setDialogMessage({
title: constants.CONFIRM_CANCEL_SUBSCRIPTION,
content:
constants.CANCEL_SUBSCRIPTION_MESSAGE(),
staticBackdrop: true,
proceed: {
text: constants.CANCEL_SUBSCRIPTION,
action: cancelSubscription.bind(
null,
props.setDialogMessage,
props.closeModal,
props.setLoading
),
variant: 'danger',
},
close: {
text: constants.CANCEL,
},
})
}>
{constants.CANCEL_SUBSCRIPTION}
</LinkButton>
)}
<LinkButton
variant="primary"
onClick={updatePaymentMethod.bind(
null,
props.setDialogMessage,
props.setLoading
)}
style={{ marginTop: '20px' }}>
{constants.MANAGEMENT_PORTAL}
</LinkButton>
</>
)}
<LinkButton
variant="primary"
onClick={manageFamilyMethod.bind(
null,
props.setDialogMessage,
props.setLoading
)}
style={{ marginTop: '20px' }}>
{constants.MANAGE_FAMILY_PORTAL}
</LinkButton>
</>
) : (
<LinkButton
variant="primary"
onClick={props.closeModal}
style={{
color: 'rgb(121, 121, 121)',
marginTop: '20px',
}}>
{isOnFreePlan(subscription)
? constants.SKIP
: constants.CLOSE}
</LinkButton>
)}
</DeadCenter>
</Modal.Body>
</Modal>
);
}