mobile friendly

This commit is contained in:
Sebastian 2023-07-12 15:42:27 -03:00
parent 8c2fed4e1c
commit b7823407c0
No known key found for this signature in database
GPG Key ID: 173909D1A5F66069
11 changed files with 283 additions and 332 deletions

View File

@ -63,20 +63,29 @@ export function LangSelectorLikePy(): VNode {
}, []);
return (
<Fragment>
<button
<a
href="#"
class="pure-button"
name="language"
onClick={(ev) => {
ev.preventDefault();
setHidden((h) => !h);
ev.stopPropagation();
}}
>
{getLangName(lang)}
</button>
<div id="lang" class={hidden ? "hide" : ""}>
</a>
<div
id="lang"
class={hidden ? "hide" : ""}
style={{
display: "inline-block",
}}
>
<div style="position: relative; overflow: visible;">
<div
class="nav"
style="position: absolute; max-height: 60vh; overflow-y: scroll"
style="position: absolute; max-height: 60vh; overflow-y: auto; margin-left: -120px; margin-top: 20px"
>
{Object.keys(messages)
.filter((l) => l !== lang)

View File

@ -683,8 +683,14 @@ export function ShowAccountDetails({
onChange={(a) => setSubmitAccount(a)}
/>
<p>
<div style={{ display: "flex", justifyContent: "space-between" }}>
<p class="buttons-account">
<div
style={{
display: "flex",
justifyContent: "space-between",
flexFlow: "wrap-reverse",
}}
>
<div>
{onClear ? (
<input

View File

@ -49,16 +49,14 @@ function MaybeBusinessButton({
const result = useBusinessAccountDetails(account);
if (!result.ok) return <Fragment />;
return (
<div class="some-space">
<a
href="#"
class="pure-button pure-button-primary"
onClick={(e) => {
e.preventDefault();
onClick();
}}
>{i18n.str`Business Profile`}</a>
</div>
<a
href="#"
class="pure-button pure-button-primary"
onClick={(e) => {
e.preventDefault();
onClick();
}}
>{i18n.str`Business Profile`}</a>
);
}
@ -88,7 +86,7 @@ export function BankFrame({
style="display: flex; flex-direction: row; justify-content: space-between;"
>
<a href="#main" class="skip">{i18n.str`Skip to main content`}</a>
<div style="max-width: 50em; margin-left: 2em;">
<div style="max-width: 50em; margin-left: 2em; margin-right: 2em;">
<h1>
<span class="it">
<a href="/">{bankUiSettings.bankName}</a>
@ -112,35 +110,21 @@ export function BankFrame({
</p>,
)}
</div>
<a href="https://taler.net/">
<img
src={talerLogo}
alt={i18n.str`Taler logo`}
height="100"
width="224"
style="margin: 2em 2em"
/>
</a>
</header>
<div style="display:flex; flex-direction: column;" class="navcontainer">
<nav class="demolist">
{maybeDemoContent(<Fragment>{demo_sites}</Fragment>)}
<div class="right">
<LangSelector />
</div>
</nav>
</div>
<section id="main" class="content">
<StatusBanner />
{backend.state.status === "loggedIn" ? (
<div class="top-right">
{goToBusinessAccount && !backend.state.isUserAdministrator ? (
<MaybeBusinessButton
account={backend.state.username}
onClick={goToBusinessAccount}
/>
) : undefined}
<div class="some-space">
{backend.state.status === "loggedIn" ? (
<Fragment>
{goToBusinessAccount && !backend.state.isUserAdministrator ? (
<MaybeBusinessButton
account={backend.state.username}
onClick={goToBusinessAccount}
/>
) : undefined}
<LangSelector />
<a
href="#"
class="pure-button logout-button"
@ -149,26 +133,27 @@ export function BankFrame({
updateSettings("currentWithdrawalOperationId", undefined);
}}
>{i18n.str`Logout`}</a>
</div>
</div>
) : null}
</Fragment>
) : undefined}
</nav>
</div>
<section id="main" class="content">
<StatusBanner />
{children}
</section>
<section id="footer" class="footer">
<div class="footer">
<hr />
<div>
<p>
You can learn more about GNU Taler on our{" "}
<a href="https://taler.net">main website</a>.
</p>
</div>
<div style="flex-grow:1" />
<hr />
<div>
<p>
Copyright &copy; 2014&mdash;2022 Taler Systems SA. {versionText}{" "}
<TestingTag />
You can learn more about GNU Taler on our{" "}
<a href="https://taler.net">main website</a>.
</p>
</div>
<div style="flex-grow:1" />
<p>
Copyright &copy; 2014&mdash;2022 Taler Systems SA. {versionText}{" "}
<TestingTag />
</p>
</section>
</Fragment>
);
@ -192,7 +177,7 @@ export function ErrorBannerFloat({
<div
style={{
position: "fixed",
top: 0,
top: 10,
zIndex: 200,
width: "90%",
}}
@ -262,7 +247,7 @@ function StatusBanner(): VNode | null {
<div
style={{
position: "fixed",
top: 0,
top: 10,
zIndex: 200,
width: "90%",
}}

View File

@ -47,6 +47,7 @@ import { LoginForm } from "./LoginForm.js";
import { ShowInputErrorLabel } from "./ShowInputErrorLabel.js";
import { handleNotOkResult } from "./HomePage.js";
import { ErrorMessage, notifyInfo } from "../hooks/notification.js";
import { Amount } from "./WalletWithdrawForm.js";
interface Props {
onClose: () => void;
@ -345,34 +346,23 @@ function CreateCashout({
? i18n.str`Amount to send`
: i18n.str`Amount to receive`}
</label>
<div style={{ width: "max-content" }}>
<input
type="text"
readonly
class="currency-indicator"
size={amount?.currency.length ?? 0}
maxLength={amount?.currency.length ?? 0}
tabIndex={-1}
value={amount?.currency ?? ""}
/>
&nbsp;
<input
type="number"
// ref={ref}
id="withdraw-amount"
name="withdraw-amount"
value={form.amount ?? ""}
onChange={(e): void => {
form.amount = e.currentTarget.value;
<div style={{ display: "flex" }}>
<Amount
currency={amount.currency}
value={form.amount}
onChange={(v) => {
form.amount = v;
updateForm(structuredClone(form));
}}
error={errors?.amount}
/>
&nbsp;
<label class="toggle">
<label class="toggle" style={{ marginLeft: 4, marginTop: 0 }}>
<input
class="toggle-checkbox"
type="checkbox"
name="asd"
onChange={(e): void => {
console.log("asdasd", form.isDebit);
form.isDebit = !form.isDebit;
updateForm(structuredClone(form));
}}
@ -380,10 +370,6 @@ function CreateCashout({
<div class="toggle-switch"></div>
</label>
</div>
<ShowInputErrorLabel
message={errors?.amount}
isDirty={form.amount !== undefined}
/>
</fieldset>
<fieldset>
<label>{i18n.str`Conversion rate`}</label>
@ -391,118 +377,43 @@ function CreateCashout({
</fieldset>
<fieldset>
<label>{i18n.str`Balance now`}</label>
<div style={{ width: "max-content" }}>
<input
type="text"
readonly
class="currency-indicator"
size={balance.currency.length}
maxLength={balance.currency.length}
tabIndex={-1}
value={balance.currency}
/>
&nbsp;
<input
type="number"
id="withdraw-amount"
disabled
name="withdraw-amount"
value={Amounts.stringifyValue(balance)}
/>
</div>
<Amount
currency={balance.currency}
value={Amounts.stringifyValue(balance)}
/>
</fieldset>
<fieldset>
<label
style={{ fontWeight: "bold", color: "red" }}
>{i18n.str`Total cost`}</label>
<div style={{ width: "max-content" }}>
<input
type="text"
readonly
class="currency-indicator"
size={balance.currency.length}
maxLength={balance.currency.length}
tabIndex={-1}
value={balance.currency}
/>
&nbsp;
<input
type="number"
// ref={ref}
id="withdraw-amount"
disabled
name="withdraw-amount"
value={Amounts.stringifyValue(calc.debit)}
/>
</div>
<Amount
currency={balance.currency}
value={Amounts.stringifyValue(calc.debit)}
/>
</fieldset>
<fieldset>
<label>{i18n.str`Balance after`}</label>
<div style={{ width: "max-content" }}>
<input
type="text"
readonly
class="currency-indicator"
size={balance.currency.length}
maxLength={balance.currency.length}
tabIndex={-1}
value={balance.currency}
/>
&nbsp;
<input
type="number"
// ref={ref}
id="withdraw-amount"
disabled
name="withdraw-amount"
value={balanceAfter ? Amounts.stringifyValue(balanceAfter) : ""}
/>
</div>
<Amount
currency={balance.currency}
value={balanceAfter ? Amounts.stringifyValue(balanceAfter) : ""}
/>
</fieldset>{" "}
{Amounts.isZero(sellFee) ? undefined : (
<Fragment>
<fieldset>
<label>{i18n.str`Amount after conversion`}</label>
<div style={{ width: "max-content" }}>
<input
type="text"
readonly
class="currency-indicator"
size={fiatCurrency.length}
maxLength={fiatCurrency.length}
tabIndex={-1}
value={fiatCurrency}
/>
&nbsp;
<input
// type="number"
style={{ color: "black" }}
disabled
value={Amounts.stringifyValue(calc.beforeFee)}
/>
</div>
<Amount
currency={fiatCurrency}
value={Amounts.stringifyValue(calc.beforeFee)}
/>
</fieldset>
<fieldset>
<label>{i18n.str`Cashout fee`}</label>
<div style={{ width: "max-content" }}>
<input
type="text"
readonly
class="currency-indicator"
size={fiatCurrency.length}
maxLength={fiatCurrency.length}
tabIndex={-1}
value={fiatCurrency}
/>
&nbsp;
<input
// type="number"
style={{ color: "black" }}
disabled
value={Amounts.stringifyValue(sellFee)}
/>
</div>
<Amount
currency={fiatCurrency}
value={Amounts.stringifyValue(sellFee)}
/>
</fieldset>
</Fragment>
)}
@ -510,26 +421,10 @@ function CreateCashout({
<label
style={{ fontWeight: "bold", color: "green" }}
>{i18n.str`Total cashout transfer`}</label>
<div style={{ width: "max-content" }}>
<input
type="text"
readonly
class="currency-indicator"
size={fiatCurrency.length}
maxLength={fiatCurrency.length}
tabIndex={-1}
value={fiatCurrency}
/>
&nbsp;
<input
type="number"
// ref={ref}
id="withdraw-amount"
disabled
name="withdraw-amount"
value={Amounts.stringifyValue(calc.credit)}
/>
</div>
<Amount
currency={fiatCurrency}
value={Amounts.stringifyValue(calc.credit)}
/>
</fieldset>
<fieldset>
<label>{i18n.str`Confirmation channel`}</label>

View File

@ -45,7 +45,7 @@ export function PaymentOptions({ limit }: { limit: AmountJson }): VNode {
setTab("charge-wallet");
}}
>
{i18n.str`Obtain digital cash`}
{i18n.str`Withdraw `}
</button>
<button
class={tab === "wire-transfer" ? "tablinks active" : "tablinks"}
@ -53,7 +53,7 @@ export function PaymentOptions({ limit }: { limit: AmountJson }): VNode {
setTab("wire-transfer");
}}
>
{i18n.str`Transfer to bank account`}
{i18n.str`Wire transfer`}
</button>
</div>
{tab === "charge-wallet" && (

View File

@ -100,75 +100,78 @@ export function PaytoWireTransferForm({
autoCapitalize="none"
autoCorrect="off"
>
<p>
<label for="iban">{i18n.str`Receiver IBAN:`}</label>&nbsp;
<input
ref={ref}
type="text"
id="iban"
name="iban"
value={iban ?? ""}
placeholder="CC0123456789"
required
pattern={ibanRegex}
onInput={(e): void => {
setIban(e.currentTarget.value);
}}
/>
<br />
<ShowInputErrorLabel
message={errorsWire?.iban}
isDirty={iban !== undefined}
/>
<br />
<label for="subject">{i18n.str`Transfer subject:`}</label>&nbsp;
<label for="iban">{i18n.str`Receiver IBAN:`}</label>&nbsp;
<input
ref={ref}
type="text"
id="iban"
name="iban"
value={iban ?? ""}
placeholder="CC0123456789"
required
pattern={ibanRegex}
onInput={(e): void => {
setIban(e.currentTarget.value);
}}
/>
<ShowInputErrorLabel
message={errorsWire?.iban}
isDirty={iban !== undefined}
/>
<label for="subject">{i18n.str`Transfer subject:`}</label>&nbsp;
<input
type="text"
name="subject"
id="subject"
placeholder="subject"
value={subject ?? ""}
required
onInput={(e): void => {
setSubject(e.currentTarget.value);
}}
/>
<ShowInputErrorLabel
message={errorsWire?.subject}
isDirty={subject !== undefined}
/>
<label for="amount">{i18n.str`Amount:`}</label>&nbsp;
<div style={{ width: "max-content", display: "flex" }}>
<input
type="text"
name="subject"
id="subject"
placeholder="subject"
value={subject ?? ""}
readonly
class="currency-indicator"
size={limit.currency.length}
maxLength={limit.currency.length}
tabIndex={-1}
style={{
borderTopRightRadius: 0,
borderBottomRightRadius: 0,
borderRight: 0,
}}
value={limit.currency}
/>
<input
type="number"
name="amount"
id="amount"
placeholder="amount"
required
style={{
borderTopLeftRadius: 0,
borderBottomLeftRadius: 0,
borderLeft: 0,
width: 150,
}}
value={amount ?? ""}
onInput={(e): void => {
setSubject(e.currentTarget.value);
setAmount(e.currentTarget.value);
}}
/>
<br />
<ShowInputErrorLabel
message={errorsWire?.subject}
isDirty={subject !== undefined}
/>
<br />
<label for="amount">{i18n.str`Amount:`}</label>&nbsp;
<div style={{ width: "max-content" }}>
<input
type="text"
readonly
class="currency-indicator"
size={limit.currency.length}
maxLength={limit.currency.length}
tabIndex={-1}
value={limit.currency}
/>
&nbsp;
<input
type="number"
name="amount"
id="amount"
placeholder="amount"
required
value={amount ?? ""}
onInput={(e): void => {
setAmount(e.currentTarget.value);
}}
/>
</div>
<ShowInputErrorLabel
message={errorsWire?.amount}
isDirty={amount !== undefined}
/>
</p>
</div>
<ShowInputErrorLabel
message={errorsWire?.amount}
isDirty={amount !== undefined}
/>
<p style={{ display: "flex", justifyContent: "space-between" }}>
<input
type="submit"

View File

@ -51,19 +51,14 @@ export function QrCodeSection({
const { abortWithdrawal } = useAccessAnonAPI();
return (
<section id="main" class="content">
<h1 class="nav">{i18n.str`Transfer to Taler Wallet`}</h1>
<h1 class="nav">{i18n.str`Charge your GNU Taler wallet`}</h1>
<article>
<div class="qr-div">
<p>{i18n.str`Use this QR code to withdraw to your mobile wallet:`}</p>
<div class="qr-div ">
<a href={talerWithdrawUri} class="pure-button pure-button-primary">
<i18n.Translate>Continue with GNU Taler</i18n.Translate>
</a>
<p>{i18n.str`Or scan this QR code with your mobile to receive the coin in another device:`}</p>
<QR text={talerWithdrawUri} />
<p>
<i18n.Translate>
Click{" "}
<a href={talerWithdrawUri}>{i18n.str`this taler:// link`}</a> to
open your Taler wallet
</i18n.Translate>{" "}
</p>
<br />
<a
class="pure-button btn-cancel"
onClick={async (e) => {
@ -92,7 +87,7 @@ export function QrCodeSection({
}
}
}}
>{i18n.str`Abort`}</a>
>{i18n.str`Cancel`}</a>
</div>
</article>
</section>

View File

@ -25,14 +25,16 @@ import {
RequestError,
useTranslationContext,
} from "@gnu-taler/web-util/browser";
import { VNode, h } from "preact";
import { Ref, VNode, h } from "preact";
import { useEffect, useRef, useState } from "preact/hooks";
import { useAccessAPI } from "../hooks/access.js";
import { notifyError } from "../hooks/notification.js";
import { buildRequestErrorMessage, undefinedIfEmpty } from "../utils.js";
import { ShowInputErrorLabel } from "./ShowInputErrorLabel.js";
import { forwardRef } from "preact/compat";
const logger = new Logger("WalletWithdrawForm");
const RefAmount = forwardRef(Amount);
export function WalletWithdrawForm({
focus,
@ -68,6 +70,7 @@ export function WalletWithdrawForm({
? i18n.str`balance is not enough`
: undefined,
});
return (
<form
id="reserve-form"
@ -82,32 +85,15 @@ export function WalletWithdrawForm({
<p>
<label for="withdraw-amount">{i18n.str`Amount to withdraw:`}</label>
&nbsp;
<div style={{ width: "max-content" }}>
<input
type="text"
readonly
class="currency-indicator"
size={limit.currency.length}
maxLength={limit.currency.length}
tabIndex={-1}
value={limit.currency}
/>
&nbsp;
<input
type="number"
ref={ref}
id="withdraw-amount"
name="withdraw-amount"
value={amountStr ?? ""}
onChange={(e): void => {
setAmountStr(e.currentTarget.value);
}}
/>
<ShowInputErrorLabel
message={errors?.amount}
isDirty={amountStr !== undefined}
/>
</div>
<RefAmount
currency={limit.currency}
value={amountStr}
onChange={(v) => {
setAmountStr(v);
}}
error={errors?.amount}
ref={ref}
/>
</p>
<p>
<div>
@ -160,3 +146,61 @@ export function WalletWithdrawForm({
</form>
);
}
export function Amount(
{
currency,
value,
error,
onChange,
}: {
error?: string;
currency: string;
value: string | undefined;
onChange?: (s: string) => void;
},
ref: Ref<HTMLInputElement>,
): VNode {
return (
<div style={{ width: "max-content" }}>
<div>
<input
type="text"
readonly
class="currency-indicator"
size={currency.length}
maxLength={currency.length}
tabIndex={-1}
style={{
borderTopRightRadius: 0,
borderBottomRightRadius: 0,
borderRight: 0,
}}
value={currency}
/>
<input
type="number"
ref={ref}
name="amount"
id="amount"
placeholder="0"
style={{
borderTopLeftRadius: 0,
borderBottomLeftRadius: 0,
borderLeft: 0,
width: 150,
color: "black",
}}
value={value ?? ""}
disabled={!onChange}
onInput={(e): void => {
if (onChange) {
onChange(e.currentTarget.value);
}
}}
/>
</div>
<ShowInputErrorLabel message={error} isDirty={value !== undefined} />
</div>
);
}

View File

@ -310,3 +310,8 @@ h1.nav {
background: rgb(66, 184, 221);
/* this is a light blue */
}
[name=wire-transfer-form] > input {
margin-bottom: 1em;
}

View File

@ -39,8 +39,8 @@ Colors:
}
.content {
margin-left: 2em;
margin-right: 2em;
margin-left: 1em;
margin-right: 1em;
overflow-x: auto;
}
@ -60,8 +60,8 @@ body {
margin-bottom: 50px;
width: 100%;
color: white;
position: -webkit-sticky;
position: sticky;
// position: -webkit-sticky;
// position: sticky;
top: 0px;
width: 100vw;
backdrop-filter: blur(10px);
@ -70,7 +70,7 @@ body {
}
nav {
left: 1vw;
// left: 1vw;
position: relative;
background: #0042b2;
z-index: 100;
@ -83,7 +83,7 @@ nav span,
border: none;
color: white;
text-align: center;
text-decoration: none;
// text-decoration: none;
display: inline-block;
font-size: 16px;
background: #0042b2;
@ -94,9 +94,10 @@ nav a,
nav button,
nav span,
.navbtn {
padding: 15px 32px;
padding: 8px;
}
nav a:hover,
nav span:hover,
.navbtn:hover {
@ -138,7 +139,7 @@ nav .hide div.nav {
// }
.langbtn {
width: 100%;
width: 100px;
text-align: left;
}
@ -156,3 +157,11 @@ nav .hide div.nav {
width: auto;
height: auto;
}
.demolist > a {
margin: 8px;
}
.buttons-account input.pure-button {
margin: 8px;
}

View File

@ -1091,29 +1091,29 @@ since IE8 won't execute CSS that contains a CSS3 selector.
}
@media only screen and (max-width: 480px) {
.pure-form button[type="submit"] {
margin: 0.7em 0 0;
}
// .pure-form button[type="submit"] {
// margin: 0.7em 0 0;
// }
.pure-form input:not([type]),
.pure-form input[type="text"],
.pure-form input[type="password"],
.pure-form input[type="email"],
.pure-form input[type="url"],
.pure-form input[type="date"],
.pure-form input[type="month"],
.pure-form input[type="time"],
.pure-form input[type="datetime"],
.pure-form input[type="datetime-local"],
.pure-form input[type="week"],
.pure-form input[type="number"],
.pure-form input[type="search"],
.pure-form input[type="tel"],
.pure-form input[type="color"],
.pure-form label {
margin-bottom: 0.3em;
display: block;
}
// .pure-form input:not([type]),
// .pure-form input[type="text"],
// .pure-form input[type="password"],
// .pure-form input[type="email"],
// .pure-form input[type="url"],
// .pure-form input[type="date"],
// .pure-form input[type="month"],
// .pure-form input[type="time"],
// .pure-form input[type="datetime"],
// .pure-form input[type="datetime-local"],
// .pure-form input[type="week"],
// .pure-form input[type="number"],
// .pure-form input[type="search"],
// .pure-form input[type="tel"],
// .pure-form input[type="color"],
// .pure-form label {
// margin-bottom: 0.3em;
// display: block;
// }
.pure-group input:not([type]),
.pure-group input[type="text"],