components#T JavaScript Examples

The following examples show how to use components#T. 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: app.jsx    From apps with GNU Affero General Public License v3.0 6 votes vote down vote up
render() {
        const { settings, route, router, user } = this.props;
        const { outdated } = this.state;
        const RouteComponent = route.handler.component;

        let notice;

        if (outdated) {
            notice = (
                <Message className="kip-outdated-notice" type="warning">
                    <p>
                        <T t={t} k="outdated" />
                    </p>
                    <Button type="info" onClick={() => location.reload()}>
                        <T t={t} k="reload" />
                    </Button>
                </Message>
            );
        }

        let content;
        if (route.handler.isSimple)
            content = this.renderSimple(RouteComponent, route.handler.props);
        else content = this.renderFull(RouteComponent, route.handler.props);

        return (
            <Fragment>
                {notice}
                {content}
            </Fragment>
        );
    }
Example #2
Source File: wizard.jsx    From apps with GNU Affero General Public License v3.0 6 votes vote down vote up
Hi = withSettings(({ settings }) => (
    <React.Fragment>
        <CardContent>
            <p>
                <T
                    t={t}
                    k="wizard.hi"
                    link={
                        <A
                            key="letUsKnow"
                            external
                            href={settings.get('supportEmail')}
                        >
                            <T t={t} k="wizard.letUsKnow" key="letUsKnow" />
                        </A>
                    }
                />
            </p>
        </CardContent>
        <CardFooter>
            <Button type="success" href={`/user/setup/enter-contact-data`}>
                <T t={t} k="wizard.continue" />
            </Button>
        </CardFooter>
    </React.Fragment>
))
Example #3
Source File: appointments.jsx    From apps with GNU Affero General Public License v3.0 6 votes vote down vote up
InvitationDeleted = ({}) => {
    return (
        <F>
            <Message type="danger">
                <T t={t} k="invitation-accepted.deleted" />
            </Message>
            <CardFooter>
                <Button
                    type="warning"
                    onClick={() =>
                        confirmDeletionAction().then(acceptedInvitationAction)
                    }
                >
                    <T t={t} k="invitation-accepted.confirm-deletion" />
                </Button>
            </CardFooter>
        </F>
    );
}
Example #4
Source File: summary-box.jsx    From apps with GNU Affero General Public License v3.0 6 votes vote down vote up
SummaryBox = ({ open, booked, active }) => (
    <Card className="kip-is-fullheight kip-is-fullwidth kip-summary-box" flex>
        <CardHeader>
            <h2>
                <T t={t} k="summary" />
            </h2>
        </CardHeader>
        <CardContent>
            <p className="kip-summary-box-number">
                <span>{formatNumbers(open)}</span>
                <T t={t} k="open" />
            </p>
            <p className="kip-summary-box-number">
                <span>{formatNumbers(booked)}</span>
                <T t={t} k="booked" />
            </p>
            <p className="kip-summary-box-number">
                <span>{formatNumbers(active)}</span>
                <T t={t} k="active" />
            </p>
        </CardContent>
    </Card>
)
Example #5
Source File: appointments.jsx    From apps with GNU Affero General Public License v3.0 6 votes vote down vote up
ProviderDetails = ({ data }) => {
    return (
        <div className="kip-provider-details">
            <ul>
                <li>{data.json.name}</li>
                <li>{data.json.street}</li>
                <li>{data.json.zipCode}</li>
                <li>{data.json.city}</li>
                {data.json.accessible && (
                    <li>
                        <T t={t} k="provider-details.accessible" />
                    </li>
                )}
            </ul>
            {data.json.description && (
                <Message type="info">{data.json.description}</Message>
            )}
        </div>
    );
}
Example #6
Source File: wizard.jsx    From apps with GNU Affero General Public License v3.0 6 votes vote down vote up
Hi = withSettings(({ settings }) => (
    <React.Fragment>
        <CardContent>
            <p>
                <T
                    t={t}
                    k="wizard.hi"
                    link={
                        <A
                            key="letUsKnow"
                            external
                            href={settings.get('supportEmail')}
                        >
                            <T t={t} k="wizard.letUsKnow" key="letUsKnow" />
                        </A>
                    }
                />
            </p>
        </CardContent>
        <CardFooter>
            <Button type="success" href={`/provider/setup/enter-provider-data`}>
                <T t={t} k="wizard.continue" />
            </Button>
        </CardFooter>
    </React.Fragment>
))
Example #7
Source File: week-calendar.jsx    From apps with GNU Affero General Public License v3.0 6 votes vote down vote up
DayLabelRow = ({ day, date }) => {
    return (
        <div className="kip-hour-row kip-is-day-label">
            <span className="kip-day">
                <T t={t} k={`day-${day + 1}`} />
            </span>
            <span className="kip-date">{date.toLocaleDateString()}</span>
        </div>
    );
}
Example #8
Source File: menu.jsx    From apps with GNU Affero General Public License v3.0 6 votes vote down vote up
menu = new Map([
    [
        'hd',
        new Map([
            [
                'dashboard',
                {
                    title: (
                        <Title
                            title={<T t={t} k="drawingBoard" />}
                            icon="chalkboard"
                        />
                    ),
                    route: '',
                },
            ],
        ]),
    ],
    ['nav', new Map([])],
])
Example #9
Source File: index.jsx    From apps with GNU Affero General Public License v3.0 5 votes vote down vote up
Dashboard = ({
    route: {
        handler: {
            props: { tab, action, secondaryAction, id },
        },
    },
}) => {
    const settings = useSettings();
    const mediator = useMediator();

    let content, modal;

    if (mediator.keyPairs === null) {
        modal = <UploadKeyPairsModal />;
    }

    if (mediator.keyPairs !== null) {
        switch (tab) {
            case 'settings':
                content = (
                    <Settings
                        action={action}
                        secondaryAction={secondaryAction}
                        id={id}
                    />
                );
                break;
            case 'providers':
                content = <Providers action={action} id={secondaryAction} />;
                break;
            case 'stats':
                content = (
                    <Stats
                        action={action}
                        secondaryAction={secondaryAction}
                        id={id}
                    />
                );
                break;
        }
    }

    let invalidKeyMessage;

    // to do: implement validation flow
    /*
    if (mediator.validKeyPairs !== undefined && validKeyPairs.valid === false) {
        invalidKeyMessage = (
            <Message type="danger">
                <T t={t} k="invalidKey" />
            </Message>
        );
    }
    */

    return (
        <CenteredCard size="fullwidth" tight>
            <CardHeader>
                <Tabs>
                    <Tab
                        active={tab === 'providers'}
                        href="/mediator/providers"
                    >
                        <T t={t} k="providers.title" />
                    </Tab>
                    <Tab active={tab === 'stats'} href="/mediator/stats">
                        <T t={t} k="stats.title" />
                    </Tab>
                    <Tab active={tab === 'settings'} href="/mediator/settings">
                        <T t={t} k="settings.title" />
                    </Tab>
                </Tabs>
            </CardHeader>
            {modal}
            {content}
        </CenteredCard>
    );
}
Example #10
Source File: verify.jsx    From apps with GNU Affero General Public License v3.0 5 votes vote down vote up
Verify = withSettings(
    withActions(({ settings, contactData, contactDataAction }) => {
        const [initialized, setInitialized] = useState(false);

        useEffect(() => {
            if (initialized) return;
            contactDataAction();
            setInitialized(true);
        });

        const render = () => (
            <React.Fragment>
                <CardContent>
                    <p className="kip-verify-notice">
                        <T
                            t={t}
                            k="verify.text"
                            link={
                                <A
                                    key="letUsKnow"
                                    external
                                    href={settings.get('supportEmail')}
                                >
                                    <T
                                        t={t}
                                        k="wizard.letUsKnow"
                                        key="letUsKnow"
                                    />
                                </A>
                            }
                        />
                    </p>
                    <div className="kip-contact-data-box">
                        <ul>
                            <li>
                                <span>
                                    <T t={t} k="contact-data.email.label" />
                                </span>{' '}
                                {contactData.data.email || (
                                    <T t={t} k="contact-data.not-given" />
                                )}
                            </li>
                        </ul>
                    </div>
                    <div className="kip-contact-data-links">
                        <A
                            className="bulma-button bulma-is-small"
                            href="/user/setup/enter-contact-data"
                        >
                            <T t={t} k="contact-data.change" />
                        </A>
                    </div>
                </CardContent>
                <CardFooter>
                    <Button type="success" href={`/user/setup/finalize`}>
                        <T t={t} k="wizard.continue" />
                    </Button>
                </CardFooter>
            </React.Fragment>
        );
        return <WithLoader resources={[contactData]} renderLoaded={render} />;
    }, [])
)
Example #11
Source File: appointments.jsx    From apps with GNU Affero General Public License v3.0 5 votes vote down vote up
NoInvitations = ({ tokenData }) => {
    let createdAt;

    if (tokenData.createdAt !== undefined)
        createdAt = new Date(tokenData.createdAt);

    let content;

    // in the first 10 minutes since the creation of the token we show a 'please wait'
    // message, as it can take some time for appointments to show up...
    if (
        createdAt !== undefined &&
        new Date(createdAt.getTime() + 1000 * 60 * 10) > new Date()
    ) {
        content = (
            <F>
                <Message type="success">
                    <T t={t} k="no-invitations.please-wait" />
                </Message>
            </F>
        );
    } else {
        content = (
            <F>
                <Message type="warning">
                    <T t={t} k="no-invitations.notice" />
                </Message>
            </F>
        );
    }
    return (
        <F>
            <CardContent>
                <div className="kip-no-invitations">{content}</div>
            </CardContent>
            <Message type="info">
                <ButtonIcon icon="circle-notch fa-spin" /> &nbsp;
                <T t={t} k="no-invitations.update-notice" />
            </Message>
        </F>
    );
}
Example #12
Source File: appointments.jsx    From apps with GNU Affero General Public License v3.0 5 votes vote down vote up
OfferDetails = ({ offer }) => {
    const settings = useSettings();
    const lang = settings.get('lang');
    const notices = [];
    const properties = settings.get('appointmentProperties');
    for (const [category, values] of Object.entries(properties)) {
        for (const [k, v] of Object.entries(values.values)) {
            if (
                (offer[k] === true ||
                    (offer.properties !== undefined &&
                        offer.properties[category] === k)) &&
                v.notice !== undefined &&
                v.infosUrl !== undefined &&
                v.anamnesisUrl !== undefined
            )
                notices.push(
                    <F key="k">
                        <p>{v.notice[lang]}</p>
                        <p>
                            <T
                                t={t}
                                k="offer-notice-text"
                                vaccine={
                                    <strong key="vaccine">{v[lang]}</strong>
                                }
                                info={
                                    <a
                                        key="infos"
                                        target="_blank"
                                        href={v.infosUrl[lang]}
                                    >
                                        <T t={t} k="info" />
                                    </a>
                                }
                                anamnesis={
                                    <a
                                        key="anamnesis"
                                        target="_blank"
                                        href={v.anamnesisUrl[lang]}
                                    >
                                        <T t={t} k="anamnesis" />
                                    </a>
                                }
                            />
                        </p>
                    </F>
                );
        }
    }
    return <div className="kip-offer-details">{notices}</div>;
}
Example #13
Source File: store-secrets.jsx    From apps with GNU Affero General Public License v3.0 5 votes vote down vote up
BackupDataLink = withSettings(
    withActions(
        ({
            onSuccess,
            settings,
            keyPairs,
            downloadText,
            providerData,
            keyPairsAction,
            providerSecret,
            providerSecretAction,
            backupData,
            backupDataAction,
        }) => {
            const [initialized, setInitialized] = useState(false);

            let providerName;

            try {
                providerName = providerData.data.data.name
                    .replaceAll(' ', '-')
                    .replaceAll('.', '-')
                    .toLowerCase();
            } catch (e) {}

            useEffect(() => {
                if (initialized) return;
                setInitialized(true);
                keyPairsAction().then((kp) =>
                    providerSecretAction().then((ps) =>
                        backupDataAction(kp.data, ps.data)
                    )
                );
            });

            let blob;

            if (backupData !== undefined && backupData.status === 'succeeded') {
                blob = new Blob([str2ab(JSON.stringify(backupData.data))], {
                    type: 'application/octet-stream',
                });
            }

            const title = settings.get('title').toLowerCase();

            const dateString = formatDate(new Date());

            const filename = `${title}-backup-${dateString}-${providerName}.enc`;

            if (blob !== undefined)
                return (
                    <a
                        onClick={onSuccess}
                        className="bulma-button bulma-is-success"
                        download={filename}
                        href={URL.createObjectURL(blob)}
                        type="success"
                    >
                        {downloadText || (
                            <T t={t} k="wizard.download-backup-data" />
                        )}
                    </a>
                );

            return (
                <Message waiting type="warning">
                    <T t={t} k="wizard.generating-backup-data" />
                </Message>
            );
        },
        []
    )
)
Example #14
Source File: store-secrets.jsx    From apps with GNU Affero General Public License v3.0 5 votes vote down vote up
DataSecret = withSettings(
    ({ settings, secret, embedded, hideNotice }) => {
        const [succeeded, setSucceeded] = useState(false);
        const [failed, setFailed] = useState(false);

        const chunks = secret.match(/.{1,4}/g);

        const fragments = [];
        for (let i = 0; i < chunks.length; i++) {
            fragments.push(<F key={`${i}-main`}>{chunks[i]}</F>);
            if (i < chunks.length - 1)
                fragments.push(
                    <strong key={`${i}-dot`} style={{ userSelect: 'none' }}>
                        ·
                    </strong>
                );
        }

        const copy = () => {
            if (!copyToClipboard(secret)) setFailed(true);
            else setSucceeded(true);
        };

        return (
            <React.Fragment>
                {!embedded && (
                    <p className="kip-secrets-notice">
                        <T t={t} k="store-secrets.online.text" safe />
                    </p>
                )}
                <div
                    className={
                        'kip-secrets-box' + (embedded ? ' kip-is-embedded' : '')
                    }
                >
                    {
                        <React.Fragment>
                            <div className="kip-uid">
                                {!hideNotice && (
                                    <span>
                                        <T t={t} k="store-secrets.secret" />
                                    </span>
                                )}
                                <code>{fragments}</code>
                            </div>
                        </React.Fragment>
                    }
                </div>
                {!embedded && (
                    <div className="kip-secrets-links">
                        <Button
                            type={
                                failed ? 'danger' : succeeded ? '' : 'primary'
                            }
                            disabled={failed}
                            className="bulma-button bulma-is-small"
                            onClick={copy}
                        >
                            <T
                                t={t}
                                k={
                                    failed
                                        ? 'store-secrets.copy-failed'
                                        : succeeded
                                        ? 'store-secrets.copy-succeeded'
                                        : 'store-secrets.copy'
                                }
                            />
                        </Button>
                    </div>
                )}
            </React.Fragment>
        );
    }
)
Example #15
Source File: index.jsx    From apps with GNU Affero General Public License v3.0 5 votes vote down vote up
UploadKeyPairsModal = () => {
    const mediator = useMediator();

    const [invalidFile, setInvalidFile] = useState(false);

    const readFile = (e) => {
        const file = e.target.files[0];
        const reader = new FileReader();

        reader.onload = function (e) {
            const json = JSON.parse(e.target.result);
            if (
                json.signing === undefined ||
                json.encryption === undefined ||
                json.provider === undefined
            )
                setInvalidFile(true);
            else mediator.keyPairs = json;
        };

        reader.readAsBinaryString(file);
    };

    let notice;

    if (invalidFile)
        notice = (
            <Message type="danger">
                <T t={t} k="upload-key-pairs.invalid-file" />
            </Message>
        );
    else notice = <T t={t} k="upload-key-pairs.notice" />;

    const footer = (
        <Form>
            <FieldSet>
                <label htmlFor="file-upload" className="custom-file-upload">
                    <T t={t} k="upload-key-pairs.input" />
                    <input
                        id="file-upload"
                        className="bulma-input"
                        type="file"
                        onChange={(e) => readFile(e)}
                    />
                </label>
            </FieldSet>
        </Form>
    );
    return (
        <Modal
            footer={footer}
            className="kip-upload-key-pairs"
            title={<T t={t} k="upload-key-pairs.title" />}
        >
            {notice}
        </Modal>
    );
}
Example #16
Source File: settings.jsx    From apps with GNU Affero General Public License v3.0 5 votes vote down vote up
TestQueuesModal = withRouter(
    withActions(({ keyPairs, router, testQueues, testQueuesAction }) => {
        const [initialized, setInitialized] = useState(false);

        useEffect(() => {
            if (initialized) return;
            setInitialized(true);
            testQueuesAction(keyPairs);
        });

        const readFile = (e) => {
            const file = e.target.files[0];
            const reader = new FileReader();

            reader.onload = function (e) {
                const json = JSON.parse(e.target.result);
                testQueuesAction(keyPairs, json);
            };

            reader.readAsBinaryString(file);
        };

        let notice;

        const { status } = testQueues;

        if (status === 'invalid')
            notice = (
                <Message type="danger">
                    <T t={t} k="upload-queues.invalid-file" />
                </Message>
            );
        else if (status === 'valid')
            notice = (
                <Message type="success">
                    <T t={t} k="upload-queues.valid-file" />
                </Message>
            );
        else notice = <T t={t} k="upload-queues.notice" />;

        const footer = (
            <Form>
                <FieldSet>
                    <label htmlFor="file-upload" className="custom-file-upload">
                        <T t={t} k="upload-queues.input" />
                        <input
                            id="file-upload"
                            disabled={
                                keyPairs === undefined || keyPairs.data === null
                            }
                            className="bulma-input"
                            type="file"
                            onChange={(e) => readFile(e)}
                        />
                    </label>
                </FieldSet>
            </Form>
        );
        return (
            <Modal
                footer={footer}
                onClose={() => router.navigateToUrl('/mediator/settings')}
                className="kip-upload-file"
                title={<T t={t} k="upload-queues.title" />}
            >
                {notice}
            </Modal>
        );
    }, [])
)
Example #17
Source File: index.jsx    From apps with GNU Affero General Public License v3.0 5 votes vote down vote up
Dashboard = ({
    route: {
        handler: {
            props: { tab, action, secondaryAction, id },
        },
    },
    route,
}) => {
    const provider = useProvider();

    useInterval(async () => {
        const response = await provider.checkData().get();
    }, 10000);

    let content;

    switch (tab) {
        case 'settings':
            content = <Settings key="settings" action={action} />;
            break;
        case 'schedule':
            content = (
                <Schedule
                    action={action}
                    route={route}
                    secondaryAction={secondaryAction}
                    id={id}
                    key="schedule"
                />
            );
            break;
    }

    let invalidKeyMessage;

    if (provider.verifiedData === null) {
        invalidKeyMessage = (
            <Message waiting type="warning">
                <T t={t} k="invalid-key" />
            </Message>
        );
    }

    return (
        <CenteredCard size="fullwidth" tight>
            <CardHeader>
                <Tabs>
                    <Tab active={tab === 'schedule'} href="/provider/schedule">
                        <T t={t} k="schedule.title" />
                    </Tab>
                    <Tab active={tab === 'settings'} href="/provider/settings">
                        <T t={t} k="settings.title" />
                    </Tab>
                    <Tab
                        last
                        icon={<Icon icon="sign-out-alt" />}
                        active={tab === 'log-out'}
                        href="/provider/settings/logout"
                    >
                        <T t={t} k="log-out" />
                    </Tab>
                </Tabs>
            </CardHeader>
            {invalidKeyMessage}
            {content}
        </CenteredCard>
    );
}
Example #18
Source File: settings.jsx    From apps with GNU Affero General Public License v3.0 4 votes vote down vote up
BaseSettings = ({
    type,
    settings,
    keyPairs,
    keyPairsAction,
    action,
    secondaryAction,
    id,
    router,
}) => {
    let modal;

    const [initialized, setInitialized] = useState(false);
    const [loggingOut, setLoggingOut] = useState(false);

    useEffect(() => {
        if (initialized) return;
        setInitialized(true);
        keyPairsAction();
    });

    const cancel = () => {
        router.navigateToUrl('/mediator/settings');
    };

    const logOut = () => {
        setLoggingOut(true);

        const backend = settings.get('backend');
        backend.local.deleteAll('mediator::');
        router.navigateToUrl('/mediator/logged-out');
    };

    if (action === 'test-queues') {
        modal = <TestQueuesModal keyPairs={keyPairs} />;
    } else if (action === 'logout') {
        modal = (
            <Modal
                onClose={cancel}
                save={<T t={t} k="log-out" />}
                disabled={loggingOut}
                waiting={loggingOut}
                title={<T t={t} k="log-out-modal.title" />}
                onCancel={cancel}
                onSave={logOut}
                saveType="warning"
            >
                <p>
                    <T
                        t={t}
                        k={
                            loggingOut
                                ? 'log-out-modal.logging-out'
                                : 'log-out-modal.text'
                        }
                    />
                </p>
            </Modal>
        );
    }

    return (
        <F>
            {modal}
            <CardContent>
                <div className="kip-mediator-settings">
                    <h2>
                        <T t={t} k="test-queues.title" />
                    </h2>
                    <p>
                        <T t={t} k="test-queues.text" />
                    </p>
                    <div className="kip-buttons">
                        <Button
                            type="success"
                            href="/mediator/settings/test-queues"
                        >
                            <T t={t} k="test-queues.button" />
                        </Button>
                    </div>
                </div>
            </CardContent>
            <CardFooter>
                <div className="kip-buttons">
                    <Button type="warning" href="/mediator/settings/logout">
                        <T t={t} k="log-out" />
                    </Button>
                </div>
            </CardFooter>
        </F>
    );
}
Example #19
Source File: wizard.jsx    From apps with GNU Affero General Public License v3.0 4 votes vote down vote up
Wizard = ({ route, router, page, privacyManager }) => {
    const pageRef = useRef(null);

    const checkPage = () => {
        return true;
    };

    const index = pages.indexOf(page);

    const canShow = (_page) => {
        return pages.indexOf(_page) <= index;
    };

    if (!page) page = pages[0];

    // we always
    useEffect(() => {
        if (pageRef.current !== undefined)
            pageRef.current.scrollIntoView({
                behavior: 'smooth',
                block: 'start',
            });
    });

    const renderLoaded = () => {
        const { app } = route.handler;
        const components = new Map([]);
        let i = 1;

        if (!checkPage()) return <div />;

        for (const p of pages)
            components.set(
                p,
                <a key={`${p}Link`} ref={p === page ? pageRef : undefined}>
                    <CardNav
                        key={p}
                        disabled={!canShow(p)}
                        onClick={() => {
                            if (canShow(p))
                                router.navigateToUrl(`/user/setup/${p}`);
                        }}
                        active={page === p}
                    >
                        {i++}. <T t={t} k={`wizard.steps.${p}`} />
                    </CardNav>
                </a>
            );

        const populate = (p, component) => {
            const existingComponent = components.get(p);
            const newComponent = (
                <React.Fragment key={p}>
                    {existingComponent}
                    {component}
                </React.Fragment>
            );
            components.set(p, newComponent);
        };

        switch (page) {
            case 'hi':
                populate('hi', <Hi key="hiNotice" />);
                break;
            case 'enter-contact-data':
                populate(
                    'enter-contact-data',
                    <ContactData key="enterContactData" />
                );
                break;
            case 'store-secrets':
                populate('store-secrets', <StoreSecrets key="storeSecrets" />);
                break;
            case 'verify':
                populate('verify', <Verify key="verify" />);
                break;
            case 'finalize':
                populate('finalize', <Finalize key="finalize" />);
                break;
        }

        return (
            <React.Fragment>{Array.from(components.values())}</React.Fragment>
        );
    };

    return (
        <CenteredCard className="kip-cm-wizard">
            <WithLoader resources={[]} renderLoaded={renderLoaded} />
        </CenteredCard>
    );
}
Example #20
Source File: providers.jsx    From apps with GNU Affero General Public License v3.0 4 votes vote down vote up
Providers = ({ action, id }) => {
    const router = useRouter();
    const mediator = useMediator();
    const [view, setView] = useState('pending');

    useInterval(async () => {
        await mediator.pendingProviders().get();
        await mediator.verifiedProviders().get();
    }, 10000);

    const render = () => {
        const providers =
            view === 'pending'
                ? mediator.pendingProviders().result()
                : mediator.verifiedProviders().result();

        const showProvider = (i) => {
            const id = buf2hex(b642buf(i));
            router.navigateToUrl(`/mediator/providers/show/${id}`);
        };

        let modal;

        const closeModal = () => router.navigateToUrl('/mediator/providers');

        if (action === 'show' && id !== undefined) {
            const base64Id = buf2b64(hex2buf(id));
            const provider = providers.data.find(
                (provider) => provider.data.publicKeys.signing === base64Id
            );

            const doConfirmProvider = async () => {
                const result = await mediator.confirmProvider(provider);
                console.log(result);
            };

            if (provider !== undefined)
                modal = (
                    <Modal
                        title={<T t={t} k="providers.edit" />}
                        save={<T t={t} k="providers.confirm" />}
                        onSave={doConfirmProvider}
                        saveType="success"
                        onClose={closeModal}
                        onCancel={closeModal}
                    >
                        <div className="kip-provider-data">
                            <T t={t} k="providers.confirmText" />
                            <table className="bulma-table bulma-is-fullwidth bulma-is-striped">
                                <thead>
                                    <tr>
                                        <th>
                                            <T t={t} k="provider-data.field" />
                                        </th>
                                        <th>
                                            <T t={t} k="provider-data.value" />
                                        </th>
                                    </tr>
                                </thead>
                                <tbody>
                                    <tr>
                                        <td>
                                            <T t={t} k="provider-data.name" />
                                        </td>
                                        <td>{provider.data.name}</td>
                                    </tr>
                                    <tr>
                                        <td>
                                            <T t={t} k="provider-data.street" />
                                        </td>
                                        <td>{provider.data.street}</td>
                                    </tr>
                                    <tr>
                                        <td>
                                            <T t={t} k="provider-data.city" />
                                        </td>
                                        <td>{provider.data.city}</td>
                                    </tr>
                                    <tr>
                                        <td>
                                            <T
                                                t={t}
                                                k="provider-data.zipCode"
                                            />
                                        </td>
                                        <td>{provider.data.zipCode}</td>
                                    </tr>
                                    <tr>
                                        <td>
                                            <T t={t} k="provider-data.email" />
                                        </td>
                                        <td>{provider.data.email}</td>
                                    </tr>
                                    <tr>
                                        <td>
                                            <T t={t} k="provider-data.phone" />
                                        </td>
                                        <td>{provider.data.phone}</td>
                                    </tr>
                                    <tr>
                                        <td>
                                            <T
                                                t={t}
                                                k="provider-data.description"
                                            />
                                        </td>
                                        <td>{provider.data.description}</td>
                                    </tr>
                                </tbody>
                            </table>
                        </div>
                    </Modal>
                );
        }

        const providerItems = providers.data
            .sort(sortProviderByDate)
            .map((provider) => (
                <ListItem
                    onClick={() =>
                        showProvider(provider.data.publicKeys.signing)
                    }
                    key={provider.data.publicKeys.signing}
                    isCard
                >
                    <ListColumn size="md">{provider.data.name}</ListColumn>
                    <ListColumn size="md">
                        {provider.data.street} · {provider.data.city}
                    </ListColumn>
                </ListItem>
            ));

        return (
            <CardContent>
                <div className="kip-providers">
                    {modal}
                    <DropdownMenu
                        title={
                            <F>
                                <Icon icon="check-circle" />
                                <T t={t} k={`providers.${view}`} />
                            </F>
                        }
                    >
                        <DropdownMenuItem
                            icon="check-circle"
                            onClick={() => setView('verified')}
                        >
                            <T t={t} k="providers.verified" />
                        </DropdownMenuItem>
                        <DropdownMenuItem
                            icon="exclamation-circle"
                            onClick={() => setView('pending')}
                        >
                            <T t={t} k="providers.pending" />
                        </DropdownMenuItem>
                    </DropdownMenu>
                    <List>
                        <ListHeader>
                            <ListColumn size="md">
                                <T t={t} k="providers.name" />
                            </ListColumn>
                            <ListColumn size="md">
                                <T t={t} k="providers.address" />
                            </ListColumn>
                        </ListHeader>
                        {providerItems}
                    </List>
                </div>
            </CardContent>
        );
    };
    return (
        <WithLoader
            resources={[
                mediator.pendingProviders().result(),
                mediator.verifiedProviders().result(),
            ]}
            renderLoaded={render}
        />
    );
}
Example #21
Source File: store-secrets.jsx    From apps with GNU Affero General Public License v3.0 4 votes vote down vote up
StoreOnline = ({ secret, embedded, hideNotice }) => {
    const [bookmarkModal, setBookmarkModal] = useState(false);
    const [copyModal, setCopyModal] = useState(false);
    const settings = useSettings();
    const user = useUser();

    let modal;

    const showBookmarkModal = () => {
        history.pushState(
            {},
            settings.t(t, 'store-secrets.restore.title'),
            `/user/restore#${user.secret}`
        );
        setBookmarkModal(true);
    };

    const hideBookmarkModal = () => {
        history.back();
        setBookmarkModal(false);
    };

    if (bookmarkModal)
        modal = (
            <Modal
                title={<T t={t} k="store-secrets.bookmark-modal.title" />}
                onClose={hideBookmarkModal}
            >
                <T t={t} k="store-secrets.bookmark-modal.text" />
            </Modal>
        );

    const chunks = user.secret.match(/.{1,4}/g);

    const fragments = [];
    for (let i = 0; i < chunks.length; i++) {
        fragments.push(<F key={`${i}-main`}>{chunks[i]}</F>);
        if (i < chunks.length - 1)
            fragments.push(
                <strong key={`${i}-dot`} style={{ userSelect: 'none' }}>
                    ·
                </strong>
            );
    }

    return (
        <F>
            {modal}
            {!embedded && (
                <p className="kip-secrets-notice">
                    <T t={t} k="store-secrets.online.text" safe />
                </p>
            )}
            <div
                className={
                    'kip-secrets-box' + (embedded ? ' kip-is-embedded' : '')
                }
            >
                {
                    <F>
                        <div className="kip-uid">
                            {!hideNotice && (
                                <span>
                                    <T t={t} k="store-secrets.secret" />
                                </span>
                            )}
                            <code>{fragments}</code>
                        </div>
                    </F>
                }
            </div>
            {!embedded && (
                <div className="kip-secrets-links">
                    <A
                        className="bulma-button bulma-is-small"
                        onClick={showBookmarkModal}
                    >
                        <T t={t} k="store-secrets.bookmark" />
                    </A>
                </div>
            )}
        </F>
    );
}
Example #22
Source File: finalize.jsx    From apps with GNU Affero General Public License v3.0 4 votes vote down vote up
Finalize = withForm(
    withSettings(
        withRouter(
            ({
                settings,
                router,
                form: { set, data, error, valid, reset },
            }) => {
                const [initialized, setInitialized] = useState(false);
                const [modified, setModified] = useState(false);
                const [submitting, setSubmitting] = useState(false);
                const [tv, setTV] = useState(0);
                const user = useUser();
                useEffect(async () => {
                    if (initialized) return;
                    setInitialized(true);

                    await user.initialize();

                    const initialData = {
                        distance: 5,
                    };
                    for (const [k, v] of Object.entries(
                        t['contact-data'].properties
                    )) {
                        for (const [kv, vv] of Object.entries(v.values)) {
                            initialData[kv] = vv._default;
                        }
                    }
                    reset(user.queueData || initialData);
                });

                const submit = async () => {
                    setSubmitting(true);

                    // we store the queue data
                    user.queueData = data;

                    const result = await user.getToken({});

                    setSubmitting(false);

                    if (result.status === Status.Failed) return;

                    await user.backupData();

                    router.navigateToUrl('/user/setup/store-secrets');
                };

                const setAndMarkModified = (key, value) => {
                    setModified(true);
                    set(key, value);
                };

                const properties = Object.entries(
                    t['contact-data'].properties
                ).map(([k, v]) => {
                    const items = Object.entries(v.values).map(([kv, vv]) => (
                        <li key={kv}>
                            <Switch
                                id={kv}
                                checked={data[kv] || false}
                                onChange={(value) =>
                                    setAndMarkModified(kv, value)
                                }
                            >
                                &nbsp;
                            </Switch>

                            <label htmlFor={kv}>
                                <T
                                    t={t}
                                    k={`contact-data.properties.${k}.values.${kv}`}
                                />
                            </label>
                        </li>
                    ));

                    return (
                        <F key={k}>
                            <h2>
                                <T
                                    t={t}
                                    k={`contact-data.properties.${k}.title`}
                                />
                            </h2>
                            <ul className="kip-properties">{items}</ul>
                        </F>
                    );
                });

                const render = () => {
                    let failedMessage;
                    let failed;

                    if (
                        getToken !== undefined &&
                        getToken.status === 'failed'
                    ) {
                        failed = true;
                        if (getToken.error.error.code === 401) {
                            failedMessage = (
                                <Message type="danger">
                                    <T t={t} k="wizard.failed.invalid-code" />
                                </Message>
                            );
                        }
                    }

                    if (failed && !failedMessage)
                        failedMessage = (
                            <Message type="danger">
                                <T t={t} k="wizard.failed.notice" />
                            </Message>
                        );

                    return (
                        <React.Fragment>
                            <CardContent>
                                {failedMessage}
                                <div className="kip-finalize-fields">
                                    <ErrorFor error={error} field="zipCode" />
                                    <RetractingLabelInput
                                        value={data.zipCode || ''}
                                        onChange={(value) =>
                                            setAndMarkModified('zipCode', value)
                                        }
                                        label={
                                            <T
                                                t={t}
                                                k="contact-data.zip-code"
                                            />
                                        }
                                    />
                                    <label
                                        className="kip-control-label"
                                        htmlFor="distance"
                                    >
                                        <T
                                            t={t}
                                            k="contact-data.distance.label"
                                        />
                                        <span className="kip-control-notice">
                                            <T
                                                t={t}
                                                k="contact-data.distance.notice"
                                            />
                                        </span>
                                    </label>
                                    <ErrorFor error={error} field="distance" />
                                    <RichSelect
                                        id="distance"
                                        value={data.distance || 5}
                                        onChange={(value) =>
                                            setAndMarkModified(
                                                'distance',
                                                value.value
                                            )
                                        }
                                        options={[
                                            {
                                                value: 5,
                                                description: (
                                                    <T
                                                        t={t}
                                                        k="contact-data.distance.option"
                                                        distance={5}
                                                    />
                                                ),
                                            },
                                            {
                                                value: 10,
                                                description: (
                                                    <T
                                                        t={t}
                                                        k="contact-data.distance.option"
                                                        distance={10}
                                                    />
                                                ),
                                            },
                                            {
                                                value: 20,
                                                description: (
                                                    <T
                                                        t={t}
                                                        k="contact-data.distance.option"
                                                        distance={20}
                                                    />
                                                ),
                                            },
                                            {
                                                value: 30,
                                                description: (
                                                    <T
                                                        t={t}
                                                        k="contact-data.distance.option"
                                                        distance={30}
                                                    />
                                                ),
                                            },
                                            {
                                                value: 40,
                                                description: (
                                                    <T
                                                        t={t}
                                                        k="contact-data.distance.option"
                                                        distance={40}
                                                    />
                                                ),
                                            },
                                            {
                                                value: 50,
                                                description: (
                                                    <T
                                                        t={t}
                                                        k="contact-data.distance.option"
                                                        distance={50}
                                                    />
                                                ),
                                            },
                                        ]}
                                    />
                                    {properties}
                                </div>
                            </CardContent>
                            <CardFooter>
                                <Button
                                    waiting={submitting}
                                    type={failed ? 'danger' : 'success'}
                                    onClick={submit}
                                    disabled={submitting || !valid}
                                >
                                    <T
                                        t={t}
                                        k={
                                            failed
                                                ? 'wizard.failed.title'
                                                : submitting
                                                ? 'wizard.please-wait'
                                                : 'wizard.continue'
                                        }
                                    />
                                </Button>
                            </CardFooter>
                        </React.Fragment>
                    );
                };
                return <WithLoader resources={[]} renderLoaded={render} />;
            }
        )
    ),
    FinalizeForm,
    'form'
)
Example #23
Source File: contact-data.jsx    From apps with GNU Affero General Public License v3.0 4 votes vote down vote up
BaseContactData = ({
    form: { set, data, error, valid, reset },
    router,
}) => {
    const [modified, setModified] = useState(false);
    const [initialized, setInitialized] = useState(false);
    const user = useUser();

    const onSubmit = () => {
        if (!valid) return;
        user.contactData = data;
        // we redirect to the 'verify' step
        router.navigateToUrl(`/user/setup/finalize`);
    };

    useEffect(() => {
        if (initialized) return;
        setInitialized(true);
        setModified(false);
        reset(user.contactData || {});
    });

    const submitting = false;

    const setAndMarkModified = (key, value) => {
        setModified(true);
        set(key, value);
    };

    const controls = (
        <React.Fragment>
            <ErrorFor error={error} field="code" />
            <RetractingLabelInput
                value={data.code || ''}
                onChange={(value) => setAndMarkModified('code', value)}
                description={
                    <T t={t} k="contact-data.access-code.description" />
                }
                label={<T t={t} k="contact-data.access-code.label" />}
            />
        </React.Fragment>
    );

    const redirecting = false;

    return (
        <React.Fragment>
            <div className="kip-cm-contact-data">
                <FormComponent onSubmit={onSubmit}>
                    <FieldSet disabled={submitting}>
                        {
                            <React.Fragment>
                                <CardContent>{controls}</CardContent>
                                <CardFooter>
                                    <SubmitField
                                        disabled={!valid}
                                        type={'success'}
                                        onClick={onSubmit}
                                        waiting={submitting || redirecting}
                                        title={
                                            redirecting ? (
                                                <T
                                                    t={t}
                                                    k="contact-data.success"
                                                />
                                            ) : submitting ? (
                                                <T
                                                    t={t}
                                                    k="contact-data.saving"
                                                />
                                            ) : (
                                                <T
                                                    t={t}
                                                    k={
                                                        'contact-data.save-and-continue'
                                                    }
                                                />
                                            )
                                        }
                                    />
                                </CardFooter>
                            </React.Fragment>
                        }
                    </FieldSet>
                </FormComponent>
            </div>
        </React.Fragment>
    );
}
Example #24
Source File: settings.jsx    From apps with GNU Affero General Public License v3.0 4 votes vote down vote up
Settings = withActions(
    withSettings(
        withRouter(
            ({ settings, action, router, userSecret, backupDataAction }) => {
                const [loggingOut, setLoggingOut] = useState(false);

                let logOutModal;

                const cancel = () => {
                    router.navigateToUrl('/user/settings');
                };

                const logOut = () => {
                    setLoggingOut(true);
                    const backend = settings.get('backend');
                    // we perform a backup before logging the user out...
                    backupDataAction(userSecret.data, 'logout').then(() => {
                        backend.local.deleteAll('user::');
                        setLoggingOut(false);
                        router.navigateToUrl('/user/logged-out');
                    });
                };

                if (action === 'logout') {
                    logOutModal = (
                        <Modal
                            onClose={cancel}
                            save={<T t={t} k="log-out" />}
                            disabled={loggingOut}
                            waiting={loggingOut}
                            title={<T t={t} k="log-out-modal.title" />}
                            onCancel={cancel}
                            onSave={logOut}
                            saveType="warning"
                        >
                            <p>
                                <T
                                    t={t}
                                    k={
                                        loggingOut
                                            ? 'log-out-modal.logging-out'
                                            : 'log-out-modal.text'
                                    }
                                />
                            </p>
                            <hr />
                            {userSecret !== undefined && (
                                <StoreOnline
                                    secret={userSecret.data}
                                    embedded={true}
                                    hideNotice={true}
                                />
                            )}
                        </Modal>
                    );
                }

                return (
                    <F>
                        <CardContent>
                            <div className="kip-user-settings">
                                {logOutModal}
                                <h2>
                                    <T t={t} k="user-data.title" />
                                </h2>
                                <p>
                                    <T t={t} k="user-data.notice" />
                                </p>
                            </div>
                        </CardContent>
                        <CardFooter>
                            <div className="kip-buttons">
                                <Button
                                    type="warning"
                                    href="/user/settings/logout"
                                >
                                    <T t={t} k="log-out" />
                                </Button>
                            </div>
                        </CardFooter>
                    </F>
                );
            }
        )
    ),
    []
)
Example #25
Source File: appointments.jsx    From apps with GNU Affero General Public License v3.0 4 votes vote down vote up
InvitationDetails = ({}) => {
    const settings = useSettings();

    const [confirming, setConfirming] = useState(false);
    const [initialized, setInitialized] = useState(false);

    useEffect(() => {
        if (initialized) return;
        setInitialized(true);
        toggleOffersAction(null);
    });

    const toggle = (offer) => {
        toggleOffersAction(offer, data.offers);
    };

    const doConfirmOffers = () => {
        const selectedOffers = [];
        // we add the selected offers in the order the user chose
        for (const offerID of toggleOffers.data) {
            const offer = data.offers.find((offer) => offer.id === offerID);
            selectedOffers.push(offer);
        }
        setConfirming(true);
        const p = confirmOffersAction(selectedOffers, data, tokenData.data);
        p.then(() => {
            acceptedInvitationAction();
        });
        p.finally(() => {
            setConfirming(false);
            toggleOffersAction(null);
        });
    };

    let content;

    const properties = settings.get('appointmentProperties');
    // to do: use something better than the index i for the key?
    const offers = data.offers
        .filter((offer) => offer.slotData.some((sl) => sl.open))
        .filter((a) => new Date(a.timestamp) > new Date())
        .sort((a, b) => new Date(a.timestamp) - new Date(b.timestamp))
        .map((offer, i) => {
            const d = new Date(offer.timestamp);
            const selected = toggleOffers.data.includes(offer.id);
            let pref;
            if (selected) pref = toggleOffers.data.indexOf(offer.id) + 1;
            return (
                <tr
                    key={offer.id}
                    className={classNames(`kip-pref-${pref}`, {
                        'kip-selected': selected,
                        'kip-failed': false,
                    })}
                    onClick={() => toggle(offer)}
                >
                    <td>{selected ? pref : '-'}</td>
                    <td>
                        {d.toLocaleString('de-DE', {
                            month: '2-digit',
                            day: '2-digit',
                            year: '2-digit',
                            hour: '2-digit',
                            minute: '2-digit',
                        })}
                    </td>
                    <td>{formatDuration(offer.duration, settings, t)}</td>
                    <td>
                        <PropertyTags appointment={offer} />
                    </td>
                </tr>
            );
        });

    let offerDetails;

    if (offers.length === 0)
        offerDetails = (
            <Message type="warning">
                <T t={t} k="no-offers-anymore" />
            </Message>
        );
    else
        offerDetails = (
            <table className="bulma-table bulma-is-striped bulma-is-fullwidth">
                <thead>
                    <tr>
                        <th>
                            <T t={t} k="appointment-preference" />
                        </th>
                        <th>
                            <T t={t} k="appointment-date" />
                        </th>
                        <th>
                            <T t={t} k="appointment-duration" />
                        </th>
                        <th>
                            <T t={t} k="appointment-vaccine" />
                        </th>
                    </tr>
                </thead>
                <tbody>{offers}</tbody>
            </table>
        );

    return (
        <F>
            <CardContent>
                <div className="kip-invitation-details">
                    <ProviderDetails data={data.provider} />
                    <p>
                        <T t={t} k="appointments-notice" />
                    </p>
                    {offerDetails}
                </div>
            </CardContent>
            <CardFooter>
                <Button
                    waiting={confirming}
                    onClick={doConfirmOffers}
                    disabled={
                        confirming ||
                        Object.keys(
                            toggleOffers.data.filter((id) =>
                                data.offers.find((of) => of.id === id)
                            )
                        ).length === 0
                    }
                    type="success"
                >
                    <T t={t} k="confirm-appointment" />
                </Button>
            </CardFooter>
        </F>
    );
}
Example #26
Source File: provider-data.jsx    From apps with GNU Affero General Public License v3.0 4 votes vote down vote up
BaseProviderData = ({
    embedded,
    form: { set, data, error, valid, reset },
}) => {
    const [modified, setModified] = useState(false);

    const router = useRouter();
    const provider = useProvider();

    const onSubmit = () => {
        if (!valid) return;
        provider.data = data;
        // we redirect to the 'verify' step
        router.navigateToUrl(`/provider/setup/verify`);
    };

    useEffectOnce(() => {
        reset(provider.data || {});
        setModified(false);
    });

    const setAndMarkModified = (key, value) => {
        setModified(true);
        set(key, value);
    };

    const controls = (
        <React.Fragment>
            <ErrorFor error={error} field="name" />
            <RetractingLabelInput
                value={data.name || ''}
                onChange={(value) => setAndMarkModified('name', value)}
                label={<T t={t} k="provider-data.name" />}
            />
            <ErrorFor error={error} field="street" />
            <RetractingLabelInput
                value={data.street || ''}
                onChange={(value) => setAndMarkModified('street', value)}
                label={<T t={t} k="provider-data.street" />}
            />
            <ErrorFor error={error} field="zipCode" />
            <RetractingLabelInput
                value={data.zipCode || ''}
                onChange={(value) => setAndMarkModified('zipCode', value)}
                label={<T t={t} k="provider-data.zip-code" />}
            />
            <ErrorFor error={error} field="city" />
            <RetractingLabelInput
                value={data.city || ''}
                onChange={(value) => setAndMarkModified('city', value)}
                label={<T t={t} k="provider-data.city" />}
            />
            <ErrorFor error={error} field="website" />
            <RetractingLabelInput
                value={data.website || ''}
                onChange={(value) => setAndMarkModified('website', value)}
                label={<T t={t} k="provider-data.website" />}
            />
            <ErrorFor error={error} field="description" />
            <label htmlFor="description">
                <T t={t} k="provider-data.description" />
            </label>
            <textarea
                id="description"
                className="bulma-textarea"
                value={data.description || ''}
                onChange={(e) =>
                    setAndMarkModified('description', e.target.value)
                }
            />
            <h3>
                <T t={t} k="provider-data.for-mediator" />
            </h3>
            <ErrorFor error={error} field="phone" />
            <RetractingLabelInput
                value={data.phone || ''}
                onChange={(value) => setAndMarkModified('phone', value)}
                label={<T t={t} k="provider-data.phone" />}
            />
            <ErrorFor error={error} field="email" />
            <RetractingLabelInput
                value={data.email || ''}
                onChange={(value) => setAndMarkModified('email', value)}
                label={<T t={t} k="provider-data.email" />}
            />
            <hr />
            <ErrorFor error={error} field="code" />
            <RetractingLabelInput
                value={data.code || ''}
                onChange={(value) => setAndMarkModified('code', value)}
                description={
                    <T t={t} k="provider-data.access-code.description" />
                }
                label={<T t={t} k="provider-data.access-code.label" />}
            />
            <hr />
            <ul className="kip-properties">
                <li className="kip-property">
                    <Switch
                        id="accessible"
                        checked={
                            data.accessible !== undefined
                                ? data.accessible
                                : false
                        }
                        onChange={(value) =>
                            setAndMarkModified('accessible', value)
                        }
                    >
                        &nbsp;
                    </Switch>

                    <label htmlFor="accessible">
                        <T t={t} k="provider-data.accessible" />
                    </label>
                </li>
            </ul>
        </React.Fragment>
    );

    return (
        <React.Fragment>
            <div className="kip-provider-data">
                <FormComponent onSubmit={onSubmit}>
                    <FieldSet>
                        <CardContent>{controls}</CardContent>
                        <CardFooter>
                            <SubmitField
                                disabled={!valid || embedded & !modified}
                                type={'success'}
                                onClick={onSubmit}
                                title={
                                    <T
                                        t={t}
                                        k="provider-data.save-and-continue"
                                    />
                                }
                            />
                        </CardFooter>
                    </FieldSet>
                </FormComponent>
            </div>
        </React.Fragment>
    );
}
Example #27
Source File: stats.jsx    From apps with GNU Affero General Public License v3.0 4 votes vote down vote up
Stats = () => {
    const mediator = useMediator();
    useEffectOnce(() => {
        const params = {
            filter: { zipCode: null },
            id: 'queues',
            type: 'hour',
            from: todayPlusN(-1).toISOString(),
            to: todayPlusN(1).toISOString(),
        };
        mediator.stats().get(params);
    });

    const renderLoaded = () => {
        const stats = mediator.stats().result();
        const summary = prepareOverallStats(stats);
        let content;
        if (summary.show === 0) {
            content = (
                <div className="bulma-column bulma-is-fullwidth-desktop">
                    <Card size="fullwidth">
                        <Message type="warning">
                            <T t={t} k="noData" />
                        </Message>
                    </Card>
                </div>
            );
        } else {
            content = (
                <React.Fragment>
                    <div className="bulma-column bulma-is-full-desktop">
                        <Card size="fullwidth" flex>
                            <CardContent>
                                <T
                                    key="span"
                                    t={t}
                                    k="dateSpan"
                                    from={
                                        <strong key="s1">
                                            {new Date(
                                                summary.from
                                            ).toLocaleString('en-US', opts)}
                                        </strong>
                                    }
                                    to={
                                        <strong key="s2">
                                            {new Date(
                                                summary.to
                                            ).toLocaleString('en-US', opts)}
                                        </strong>
                                    }
                                />
                            </CardContent>
                        </Card>
                    </div>
                    <div className="bulma-column bulma-is-one-quarter-desktop">
                        <SummaryBox
                            open={summary.open}
                            booked={summary.booked}
                            active={summary.active}
                        />
                    </div>
                    <div className="bulma-column bulma-is-three-quarters-desktop bulma-is-flex">
                        <Card size="fullwidth" flex>
                            <CardHeader>
                                <h2>
                                    <T t={t} k="bookingRate" />
                                </h2>
                            </CardHeader>
                            <CardContent className="kip-cm-overview">
                                <BarChart
                                    hash={stats.hash}
                                    data={prepareHourlyStats(stats)}
                                />
                            </CardContent>
                        </Card>
                    </div>
                </React.Fragment>
            );
        }
        return (
            <CardContent>
                <div className="bulma-columns bulma-is-multiline bulma-is-desktop">
                    {content}
                </div>
            </CardContent>
        );
    };

    return (
        <WithLoader
            resources={[mediator.stats().result()]}
            renderLoaded={renderLoaded}
        />
    );
}
Example #28
Source File: new-appointment.jsx    From apps with GNU Affero General Public License v3.0 4 votes vote down vote up
NewAppointment = withForm(
    ({ route, action, id, form: { valid, error, data, set, reset } }) => {
        let actionUrl = '';
        const settings = useSettings();
        const router = useRouter();
        if (action !== undefined) actionUrl = `/${action}`;
        if (id !== undefined) actionUrl += `/view/${id}`;
        const [saving, setSaving] = useState(false);
        const cancel = () =>
            router.navigateToUrl(`/provider/schedule${actionUrl}`);

        let appointment;

        if (id !== undefined)
            appointment = appointments.find((app) => hexId(app.id) === id);

        const save = () => {
            let action;
            setSaving(true);
            // we remove unnecessary fields like 'time' and 'date'
            delete data.time;
            delete data.date;
            if (appointment !== undefined) action = updateAppointmentAction;
            else action = createAppointmentAction;
            const promise = action(data, appointment);
            promise.finally(() => setSaving(false));
            promise.then(() => {
                // we reload the appointments
                openAppointmentsAction();
                // and we go back to the schedule view
                router.navigateToUrl(`/provider/schedule${actionUrl}`);
            });
        };

        useEffectOnce(() => {
            if (appointment !== undefined) {
                const appointmentData = {
                    time: formatTime(appointment.timestamp),
                    date: formatDate(appointment.timestamp),
                    slots: appointment.slots,
                    duration: appointment.duration,
                };
                for (const [_, v] of Object.entries(properties)) {
                    for (const [kk, _] of Object.entries(v.values)) {
                        if (appointment[kk] !== undefined)
                            appointmentData[kk] = true;
                        else delete appointmentData[kk];
                    }
                }
                reset(appointmentData);
            } else {
                const newData = {
                    duration: data.duration || 30,
                    slots: data.slots || 1,
                };

                let firstProperty;
                let found = false;
                addProps: for (const [_, v] of Object.entries(properties)) {
                    for (const [kk, _] of Object.entries(v.values)) {
                        if (firstProperty === undefined) firstProperty = kk;
                        if (data[kk] !== undefined) {
                            found = true;
                            newData[kk] = true;
                            break addProps;
                        }
                    }
                }
                if (!found) newData[firstProperty] = true;

                if (route.hashParams !== undefined) {
                    if (route.hashParams.timestamp !== undefined) {
                        const date = new Date(route.hashParams.timestamp);
                        newData.time = formatTime(date);
                        newData.date = formatDate(date);
                    }
                }
                reset(newData);
            }
        });

        const properties = settings.get('appointmentProperties');

        const apptProperties = Object.entries(properties).map(([k, v]) => {
            const options = Object.entries(v.values).map(([kv, vv]) => ({
                value: kv,
                key: vv,
                title: <T t={properties} k={`${k}.values.${kv}`} />,
            }));

            let currentOption;

            for (const [k, v] of Object.entries(data)) {
                for (const option of options) {
                    if (k === option.value && v === true) currentOption = k;
                }
            }

            const changeTo = (option) => {
                const newData = { ...data };
                for (const option of options) newData[option.value] = undefined;
                newData[option.value] = true;
                reset(newData);
            };

            return (
                <React.Fragment key={k}>
                    <h2>
                        <T t={properties} k={`${k}.title`} />
                    </h2>
                    <RichSelect
                        options={options}
                        value={currentOption}
                        onChange={(option) => changeTo(option)}
                    />
                </React.Fragment>
            );
        });

        const durations = [
            5, 10, 15, 20, 30, 45, 60, 90, 120, 150, 180, 210, 240,
        ].map((v) => ({
            value: v,
            title: (
                <T
                    t={t}
                    k={`schedule.appointment.duration.title`}
                    duration={v}
                />
            ),
        }));

        return (
            <Modal
                saveDisabled={!valid || saving}
                cancelDisabled={saving}
                closeDisabled={saving}
                className="kip-new-appointment"
                onSave={save}
                waiting={saving}
                onCancel={cancel}
                onClose={cancel}
                title={
                    <T
                        t={t}
                        k={
                            appointment !== undefined
                                ? 'edit-appointment.title'
                                : 'new-appointment.title'
                        }
                    />
                }
            >
                <FormComponent>
                    <FieldSet>
                        <div className="kip-field">
                            <Label htmlFor="date">
                                <T t={t} k="new-appointment.date" />
                            </Label>
                            <ErrorFor error={error} field="date" />
                            <input
                                value={data.date || ''}
                                type="date"
                                className="bulma-input"
                                onChange={(e) => set('date', e.target.value)}
                            />
                        </div>
                        <div className="kip-field">
                            <Label htmlFor="time">
                                <T t={t} k="new-appointment.time" />
                            </Label>
                            <ErrorFor error={error} field="time" />
                            <input
                                type="time"
                                className="bulma-input"
                                value={data.time || ''}
                                onChange={(e) => set('time', e.target.value)}
                                step={60}
                            />
                        </div>
                        <div className="kip-field kip-is-fullwidth kip-slider">
                            <Label htmlFor="slots">
                                <T t={t} k="new-appointment.slots" />
                            </Label>
                            <ErrorFor error={error} field="slots" />
                            <input
                                type="number"
                                className="bulma-input"
                                value={data.slots || 1}
                                onChange={(e) =>
                                    set('slots', parseInt(e.target.value))
                                }
                                step={1}
                                min={1}
                                max={50}
                            />
                        </div>
                        <div className="kip-field kip-is-fullwidth">
                            <RichSelect
                                id="duration"
                                value={data.duration || 30}
                                onChange={(value) =>
                                    set('duration', value.value)
                                }
                                options={durations}
                            />
                        </div>

                        <div className="kip-field kip-is-fullwidth">
                            {apptProperties}
                        </div>
                    </FieldSet>
                </FormComponent>
            </Modal>
        );
    },
    AppointmentForm,
    'form'
)
Example #29
Source File: wizard.jsx    From apps with GNU Affero General Public License v3.0 4 votes vote down vote up
Wizard = ({ route, router, page, status }) => {
    const pageRef = useRef(null);

    const checkPage = () => {
        return true;
    };

    const index = pages.indexOf(page);

    const canShow = (_page) => {
        return pages.indexOf(_page) <= index;
    };

    if (!page) page = pages[0];

    // we always
    useEffect(() => {
        if (pageRef.current !== undefined)
            pageRef.current.scrollIntoView({
                behavior: 'smooth',
                block: 'start',
            });
    });

    const renderLoaded = () => {
        const { app } = route.handler;
        const components = new Map([]);
        let i = 1;

        if (!checkPage()) return <div />;

        for (const p of pages)
            components.set(
                p,
                <a key={`${p}Link`} ref={p === page ? pageRef : undefined}>
                    <CardNav
                        key={p}
                        disabled={!canShow(p)}
                        onClick={() => {
                            if (canShow(p))
                                router.navigateToUrl(`/provider/setup/${p}`);
                        }}
                        active={page === p}
                    >
                        {i++}. <T t={t} k={`wizard.steps.${p}`} />
                    </CardNav>
                </a>
            );

        const populate = (p, component) => {
            const existingComponent = components.get(p);
            const newComponent = (
                <React.Fragment key={p}>
                    {existingComponent}
                    {component}
                </React.Fragment>
            );
            components.set(p, newComponent);
        };

        switch (page) {
            case 'hi':
                populate('hi', <Hi key="hiNotice" />);
                break;
            case 'enter-provider-data':
                populate(
                    'enter-provider-data',
                    <ProviderData key="enterProviderData" />
                );
                break;
            case 'store-secrets':
                populate(
                    'store-secrets',
                    <StoreSecrets key="storeSecrets" status={status} />
                );
                break;
            case 'verify':
                populate('verify', <Verify key="verify" />);
                break;
        }

        return (
            <React.Fragment>{Array.from(components.values())}</React.Fragment>
        );
    };

    return (
        <CenteredCard className="kip-cm-wizard">
            <WithLoader resources={[]} renderLoaded={renderLoaded} />
        </CenteredCard>
    );
}