remove unused

This commit is contained in:
Sebastian 2022-12-09 12:07:01 -03:00
parent f2b319921c
commit 6b6f80466e
No known key found for this signature in database
GPG Key ID: BE4FF68352439FC1
19 changed files with 3 additions and 1590 deletions

View File

@ -1,3 +0,0 @@
{
"presets": ["preact-cli/babel"]
}

View File

@ -1,60 +0,0 @@
/*
This file is part of GNU Taler
(C) 2022 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
*
* @author Sebastian Javier Marchano (sebasjm)
*/
import { ComponentChildren, h, VNode } from "preact";
import { useLayoutEffect, useRef } from "preact/hooks";
import { useAsync } from "../hooks/async.js";
type Props = {
children: ComponentChildren;
disabled?: boolean;
onClick?: () => Promise<void>;
grabFocus?: boolean;
[rest: string]: any;
};
export function AsyncButton({
onClick,
grabFocus,
disabled,
children,
...rest
}: Props): VNode {
const { isLoading, request } = useAsync(onClick);
const buttonRef = useRef<HTMLButtonElement>(null);
useLayoutEffect(() => {
if (grabFocus) buttonRef.current?.focus();
}, [grabFocus]);
// if (isSlow) {
// return <LoadingModal onCancel={cancel} />;
// }
if (isLoading) return <button class="button">Loading...</button>;
return (
<span data-tooltip={rest["data-tooltip"]} style={{ marginLeft: 5 }}>
<button {...rest} ref={buttonRef} onClick={request} disabled={disabled}>
{children}
</button>
</span>
);
}

View File

@ -1,71 +0,0 @@
/*
This file is part of GNU Taler
(C) 2022 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
import { h, VNode } from "preact";
import { useRef, useState } from "preact/hooks";
const MAX_IMAGE_UPLOAD_SIZE = 1024 * 1024;
export interface FileTypeContent {
content: string;
type: string;
name: string;
}
interface Props {
label: string;
onChange: (v: FileTypeContent | undefined) => void;
}
export function FileButton(props: Props): VNode {
const fileInputRef = useRef<HTMLInputElement>(null);
const [sizeError, setSizeError] = useState(false);
return (
<div>
<button class="button" onClick={(e) => fileInputRef.current?.click()}>
<span>{props.label}</span>
</button>
<input
ref={fileInputRef}
style={{ display: "none" }}
type="file"
onChange={(e) => {
const f: FileList | null = e.currentTarget.files;
if (!f || f.length != 1) return props.onChange(undefined);
if (f[0].size > MAX_IMAGE_UPLOAD_SIZE) {
setSizeError(true);
return props.onChange(undefined);
}
setSizeError(false);
return f[0].arrayBuffer().then((b) => {
const content = new Uint8Array(b).reduce(
(data, byte) => data + String.fromCharCode(byte),
"",
);
return props.onChange({
content,
name: f[0].name,
type: f[0].type,
});
});
}}
/>
{sizeError && (
<p class="help is-danger">File should be smaller than 1 MB</p>
)}
</div>
);
}

View File

@ -1,74 +0,0 @@
/*
This file is part of GNU Taler
(C) 2022 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
*
* @author Sebastian Javier Marchano (sebasjm)
*/
import { h, VNode } from "preact";
export interface Notification {
message: string;
description?: string | VNode;
type: MessageType;
}
export type MessageType = "INFO" | "WARN" | "ERROR" | "SUCCESS";
interface Props {
notifications: Notification[];
removeNotification?: (n: Notification) => void;
}
function messageStyle(type: MessageType): string {
switch (type) {
case "INFO":
return "message is-info";
case "WARN":
return "message is-warning";
case "ERROR":
return "message is-danger";
case "SUCCESS":
return "message is-success";
default:
return "message";
}
}
export function Notifications({
notifications,
removeNotification,
}: Props): VNode {
return (
<div class="block">
{notifications.map((n, i) => (
<article key={i} class={messageStyle(n.type)}>
<div class="message-header">
<p>{n.message}</p>
{removeNotification && (
<button
class="delete"
onClick={() => removeNotification && removeNotification(n)}
/>
)}
</div>
{n.description && <div class="message-body">{n.description}</div>}
</article>
))}
</div>
);
}

View File

@ -1,102 +0,0 @@
/*
This file is part of GNU Taler
(C) 2022 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
import { format, subYears } from "date-fns";
import { h, VNode } from "preact";
import { useLayoutEffect, useRef, useState } from "preact/hooks";
import { DatePicker } from "../picker/DatePicker.js";
export interface DateInputProps {
label: string;
grabFocus?: boolean;
tooltip?: string;
error?: string;
years?: Array<number>;
onConfirm?: () => void;
bind: [string, (x: string) => void];
}
export function DateInput(props: DateInputProps): VNode {
const inputRef = useRef<HTMLInputElement>(null);
useLayoutEffect(() => {
if (props.grabFocus) inputRef.current?.focus();
}, [props.grabFocus]);
const [opened, setOpened] = useState(false);
const value = props.bind[0] || "";
const [dirty, setDirty] = useState(false);
const showError = dirty && props.error;
const calendar = subYears(new Date(), 30);
return (
<div class="field">
<label class="label">
{props.label}
{props.tooltip && (
<span class="icon has-tooltip-right" data-tooltip={props.tooltip}>
<i class="mdi mdi-information" />
</span>
)}
</label>
<div class="control">
<div class="field has-addons">
<p class="control">
<input
type="text"
class={showError ? "input is-danger" : "input"}
value={value}
onKeyPress={(e) => {
if (e.key === "Enter" && props.onConfirm) props.onConfirm();
}}
onInput={(e) => {
const text = e.currentTarget.value;
setDirty(true);
props.bind[1](text);
}}
ref={inputRef}
/>
</p>
<p class="control">
<a
class="button"
onClick={() => {
setOpened(true);
}}
>
<span class="icon">
<i class="mdi mdi-calendar" />
</span>
</a>
</p>
</div>
</div>
<p class="help">Using the format yyyy-mm-dd</p>
{showError && <p class="help is-danger">{props.error}</p>}
<DatePicker
opened={opened}
initialDate={calendar}
years={props.years}
closeFunction={() => setOpened(false)}
dateReceiver={(d) => {
setDirty(true);
const v = format(d, "yyyy-MM-dd");
props.bind[1](v);
}}
/>
</div>
);
}

View File

@ -1,69 +0,0 @@
/*
This file is part of GNU Taler
(C) 2022 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
import { h, VNode } from "preact";
import { useLayoutEffect, useRef, useState } from "preact/hooks";
export interface TextInputProps {
label: string;
grabFocus?: boolean;
error?: string;
placeholder?: string;
tooltip?: string;
onConfirm?: () => void;
bind: [string, (x: string) => void];
}
export function EmailInput(props: TextInputProps): VNode {
const inputRef = useRef<HTMLInputElement>(null);
useLayoutEffect(() => {
if (props.grabFocus) inputRef.current?.focus();
}, [props.grabFocus]);
const value = props.bind[0];
const [dirty, setDirty] = useState(false);
const showError = dirty && props.error;
return (
<div class="field">
<label class="label">
{props.label}
{props.tooltip && (
<span class="icon has-tooltip-right" data-tooltip={props.tooltip}>
<i class="mdi mdi-information" />
</span>
)}
</label>
<div class="control has-icons-right">
<input
value={value}
required
placeholder={props.placeholder}
type="email"
class={showError ? "input is-danger" : "input"}
onKeyPress={(e) => {
if (e.key === "Enter" && props.onConfirm) props.onConfirm();
}}
onInput={(e) => {
setDirty(true);
props.bind[1]((e.target as HTMLInputElement).value);
}}
ref={inputRef}
style={{ display: "block" }}
/>
</div>
{showError && <p class="help is-danger">{props.error}</p>}
</div>
);
}

View File

@ -1,102 +0,0 @@
/*
This file is part of GNU Taler
(C) 2022 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
*
* @author Sebastian Javier Marchano (sebasjm)
*/
import { h, VNode } from "preact";
import { useLayoutEffect, useRef, useState } from "preact/hooks";
const MAX_IMAGE_UPLOAD_SIZE = 1024 * 1024;
export interface FileTypeContent {
content: string;
type: string;
name: string;
}
export interface FileInputProps {
label: string;
grabFocus?: boolean;
disabled?: boolean;
error?: string;
placeholder?: string;
tooltip?: string;
onChange: (v: FileTypeContent | undefined) => void;
}
export function FileInput(props: FileInputProps): VNode {
const inputRef = useRef<HTMLInputElement>(null);
useLayoutEffect(() => {
if (props.grabFocus) inputRef.current?.focus();
}, [props.grabFocus]);
const fileInputRef = useRef<HTMLInputElement>(null);
const [sizeError, setSizeError] = useState(false);
return (
<div class="field">
<label class="label">
<a class="button" onClick={(e) => fileInputRef.current?.click()}>
<div class="icon is-small ">
<i class="mdi mdi-folder" />
</div>
<span>{props.label}</span>
</a>
{props.tooltip && (
<span class="icon has-tooltip-right" data-tooltip={props.tooltip}>
<i class="mdi mdi-information" />
</span>
)}
</label>
<div class="control">
<input
ref={fileInputRef}
style={{ display: "none" }}
type="file"
// name={String(name)}
onChange={(e) => {
const f: FileList | null = e.currentTarget.files;
if (!f || f.length != 1) return props.onChange(undefined);
if (f[0].size > MAX_IMAGE_UPLOAD_SIZE) {
setSizeError(true);
return props.onChange(undefined);
}
setSizeError(false);
return f[0].arrayBuffer().then((b) => {
const b64 = btoa(
new Uint8Array(b).reduce(
(data, byte) => data + String.fromCharCode(byte),
"",
),
);
return props.onChange({
content: `data:${f[0].type};base64,${b64}`,
name: f[0].name,
type: f[0].type,
});
});
}}
/>
{props.error && <p class="help is-danger">{props.error}</p>}
{sizeError && (
<p class="help is-danger">File should be smaller than 1 MB</p>
)}
</div>
</div>
);
}

View File

@ -1,86 +0,0 @@
/*
This file is part of GNU Taler
(C) 2022 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
import { h, VNode } from "preact";
import { useLayoutEffect, useRef, useState } from "preact/hooks";
import emptyImage from "../../assets/empty.png";
import { TextInputProps } from "./TextInput.js";
const MAX_IMAGE_UPLOAD_SIZE = 1024 * 1024;
export function ImageInput(props: TextInputProps): VNode {
const inputRef = useRef<HTMLInputElement>(null);
useLayoutEffect(() => {
if (props.grabFocus) inputRef.current?.focus();
}, [props.grabFocus]);
const value = props.bind[0];
// const [dirty, setDirty] = useState(false)
const image = useRef<HTMLInputElement>(null);
const [sizeError, setSizeError] = useState(false);
function onChange(v: string): void {
// setDirty(true);
props.bind[1](v);
}
return (
<div class="field">
<label class="label">
{props.label}
{props.tooltip && (
<span class="icon has-tooltip-right" data-tooltip={props.tooltip}>
<i class="mdi mdi-information" />
</span>
)}
</label>
<div class="control">
<img
src={!value ? emptyImage : value}
style={{ width: 200, height: 200 }}
onClick={() => image.current?.click()}
/>
<input
ref={image}
style={{ display: "none" }}
type="file"
name={String(name)}
onChange={(e) => {
const f: FileList | null = e.currentTarget.files;
if (!f || f.length != 1) return onChange(emptyImage);
if (f[0].size > MAX_IMAGE_UPLOAD_SIZE) {
setSizeError(true);
return onChange(emptyImage);
}
setSizeError(false);
return f[0].arrayBuffer().then((b) => {
const b64 = btoa(
new Uint8Array(b).reduce(
(data, byte) => data + String.fromCharCode(byte),
"",
),
);
return onChange(`data:${f[0].type};base64,${b64}` as any);
});
}}
/>
{props.error && <p class="help is-danger">{props.error}</p>}
{sizeError && (
<p class="help is-danger">Image should be smaller than 1 MB</p>
)}
</div>
</div>
);
}

View File

@ -1,68 +0,0 @@
/*
This file is part of GNU Taler
(C) 2022 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
import { h, VNode } from "preact";
import { useLayoutEffect, useRef, useState } from "preact/hooks";
export interface TextInputProps {
label: string;
grabFocus?: boolean;
error?: string;
placeholder?: string;
tooltip?: string;
onConfirm?: () => void;
bind: [string, (x: string) => void];
}
export function PhoneNumberInput(props: TextInputProps): VNode {
const inputRef = useRef<HTMLInputElement>(null);
useLayoutEffect(() => {
if (props.grabFocus) inputRef.current?.focus();
}, [props.grabFocus]);
const value = props.bind[0];
const [dirty, setDirty] = useState(false);
const showError = dirty && props.error;
return (
<div class="field">
<label class="label">
{props.label}
{props.tooltip && (
<span class="icon has-tooltip-right" data-tooltip={props.tooltip}>
<i class="mdi mdi-information" />
</span>
)}
</label>
<div class="control has-icons-right">
<input
value={value}
type="tel"
placeholder={props.placeholder}
class={showError ? "input is-danger" : "input"}
onKeyPress={(e) => {
if (e.key === "Enter" && props.onConfirm) props.onConfirm();
}}
onInput={(e) => {
setDirty(true);
props.bind[1]((e.target as HTMLInputElement).value);
}}
ref={inputRef}
style={{ display: "block" }}
/>
</div>
{showError && <p class="help is-danger">{props.error}</p>}
</div>
);
}

View File

@ -1,80 +0,0 @@
/*
This file is part of GNU Taler
(C) 2022 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
import { h, VNode } from "preact";
import { useLayoutEffect, useRef, useState } from "preact/hooks";
export interface TextInputProps {
inputType?: "text" | "number" | "multiline" | "password";
label: string;
grabFocus?: boolean;
disabled?: boolean;
error?: string;
placeholder?: string;
tooltip?: string;
onConfirm?: () => void;
bind: [string, (x: string) => void];
}
const TextInputType = function ({ inputType, grabFocus, ...rest }: any): VNode {
const inputRef = useRef<HTMLInputElement>(null);
useLayoutEffect(() => {
if (grabFocus) inputRef.current?.focus();
}, [grabFocus]);
return inputType === "multiline" ? (
<textarea {...rest} rows={5} ref={inputRef} style={{ height: "unset" }} />
) : (
<input {...rest} type={inputType} ref={inputRef} />
);
};
export function TextInput(props: TextInputProps): VNode {
const value = props.bind[0];
const [dirty, setDirty] = useState(false);
const showError = dirty && props.error;
return (
<div class="field">
<label class="label">
{props.label}
{props.tooltip && (
<span class="icon has-tooltip-right" data-tooltip={props.tooltip}>
<i class="mdi mdi-information" />
</span>
)}
</label>
<div class="control has-icons-right">
<TextInputType
inputType={props.inputType}
value={value}
grabFocus={props.grabFocus}
disabled={props.disabled}
placeholder={props.placeholder}
class={showError ? "input is-danger" : "input"}
onKeyPress={(e: any) => {
if (e.key === "Enter" && props.onConfirm) props.onConfirm();
}}
onInput={(e: any) => {
setDirty(true);
props.bind[1]((e.target as HTMLInputElement).value);
}}
style={{ display: "block" }}
/>
</div>
{showError && <p class="help is-danger">{props.error}</p>}
</div>
);
}

View File

@ -1,51 +0,0 @@
/*
This file is part of GNU Taler
(C) 2022 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
*
* @author Sebastian Javier Marchano (sebasjm)
*/
import { h, VNode } from "preact";
interface Props {
onMobileMenu: () => void;
title: string;
}
export function NavigationBar({ onMobileMenu, title }: Props): VNode {
return (
<nav
class="navbar is-fixed-top"
role="navigation"
aria-label="main navigation"
>
<div class="navbar-brand">
<span class="navbar-item" style={{ fontSize: 24, fontWeight: 900 }}>
{title}
</span>
</div>
<div class="navbar-menu ">
<div class="navbar-end">
<div class="navbar-item" style={{ paddingTop: 4, paddingBottom: 4 }}>
{/* <LangSelector /> */}
</div>
</div>
</div>
</nav>
);
}

View File

@ -1,74 +0,0 @@
/*
This file is part of GNU Taler
(C) 2022 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
*
* @author Sebastian Javier Marchano (sebasjm)
*/
import { h, VNode } from "preact";
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
interface Props {
mobile?: boolean;
}
export function Sidebar({ mobile }: Props): VNode {
// const config = useConfigContext();
const config = { version: "none" };
// FIXME: add replacement for __VERSION__ with the current version
const process = { env: { __VERSION__: "0.0.0" } };
const { i18n } = useTranslationContext();
return (
<aside class="aside is-placed-left is-expanded">
<div class="aside-tools">
<div class="aside-tools-label">
<div>
<b>euFin bank</b>
</div>
<div
class="is-size-7 has-text-right"
style={{ lineHeight: 0, marginTop: -10 }}
>
Version {process.env.__VERSION__} ({config.version})
</div>
</div>
</div>
<div class="menu is-menu-main">
<p class="menu-label">
<i18n.Translate>Bank menu</i18n.Translate>
</p>
<ul class="menu-list">
<li>
<div class="ml-4">
<span class="menu-item-label">
<i18n.Translate>Select option1</i18n.Translate>
</span>
</div>
</li>
<li>
<div class="ml-4">
<span class="menu-item-label">
<i18n.Translate>Select option2</i18n.Translate>
</span>
</div>
</li>
</ul>
</div>
</aside>
);
}

View File

@ -1,135 +0,0 @@
/*
This file is part of GNU Taler
(C) 2022 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
import { ComponentChildren, Fragment, h, VNode } from "preact";
import Match from "preact-router/match";
import { useEffect, useState } from "preact/hooks";
import { NavigationBar } from "./NavigationBar.js";
import { Sidebar } from "./SideBar.js";
interface MenuProps {
title: string;
}
function WithTitle({
title,
children,
}: {
title: string;
children: ComponentChildren;
}): VNode {
useEffect(() => {
document.title = `${title}`;
}, [title]);
return <Fragment>{children}</Fragment>;
}
export function Menu({ title }: MenuProps): VNode {
const [mobileOpen, setMobileOpen] = useState(false);
return (
<Match>
{({ path }: { path: string }) => {
const titleWithSubtitle = title; // title ? title : (!admin ? getInstanceTitle(path, instance) : getAdminTitle(path, instance))
return (
<WithTitle title={titleWithSubtitle}>
<div
class={mobileOpen ? "has-aside-mobile-expanded" : ""}
onClick={() => setMobileOpen(false)}
>
<NavigationBar
onMobileMenu={() => setMobileOpen(!mobileOpen)}
title={titleWithSubtitle}
/>
<Sidebar mobile={mobileOpen} />
</div>
</WithTitle>
);
}}
</Match>
);
}
interface NotYetReadyAppMenuProps {
title: string;
onLogout?: () => void;
}
interface NotifProps {
notification?: Notification;
}
export function NotificationCard({
notification: n,
}: NotifProps): VNode | null {
if (!n) return null;
return (
<div class="notification">
<div class="columns is-vcentered">
<div class="column is-12">
<article
class={
n.type === "ERROR"
? "message is-danger"
: n.type === "WARN"
? "message is-warning"
: "message is-info"
}
>
<div class="message-header">
<p>{n.message}</p>
</div>
{n.description && <div class="message-body">{n.description}</div>}
</article>
</div>
</div>
</div>
);
}
export function NotYetReadyAppMenu({
onLogout,
title,
}: NotYetReadyAppMenuProps): VNode {
const [mobileOpen, setMobileOpen] = useState(false);
useEffect(() => {
document.title = `Taler Backoffice: ${title}`;
}, [title]);
return (
<div
class="has-aside-mobile-expanded"
// class={mobileOpen ? "has-aside-mobile-expanded" : ""}
onClick={() => setMobileOpen(false)}
>
<NavigationBar
onMobileMenu={() => setMobileOpen(!mobileOpen)}
title={title}
/>
{onLogout && <Sidebar mobile={mobileOpen} />}
</div>
);
}
export interface Notification {
message: string;
description?: string | VNode;
type: MessageType;
}
export type ValueOrFunction<T> = T | ((p: T) => T);
export type MessageType = "INFO" | "WARN" | "ERROR" | "SUCCESS";

View File

@ -1,347 +0,0 @@
/*
This file is part of GNU Taler
(C) 2022 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
*
* @author Sebastian Javier Marchano (sebasjm)
*/
import { h, Component } from "preact";
interface Props {
closeFunction?: () => void;
dateReceiver?: (d: Date) => void;
initialDate?: Date;
years?: Array<number>;
opened?: boolean;
}
interface State {
displayedMonth: number;
displayedYear: number;
selectYearMode: boolean;
currentDate: Date;
}
const now = new Date();
const monthArrShortFull = [
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December",
];
const monthArrShort = [
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
"Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec",
];
const dayArr = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
const yearArr: number[] = [];
// inspired by https://codepen.io/m4r1vs/pen/MOOxyE
export class DatePicker extends Component<Props, State> {
closeDatePicker() {
this.props.closeFunction && this.props.closeFunction(); // Function gets passed by parent
}
/**
* Gets fired when a day gets clicked.
* @param {object} e The event thrown by the <span /> element clicked
*/
dayClicked(e: any) {
const element = e.target; // the actual element clicked
if (element.innerHTML === "") return false; // don't continue if <span /> empty
// get date from clicked element (gets attached when rendered)
const date = new Date(element.getAttribute("data-value"));
// update the state
this.setState({ currentDate: date });
this.passDateToParent(date);
}
/**
* returns days in month as array
* @param {number} month the month to display
* @param {number} year the year to display
*/
getDaysByMonth(month: number, year: number) {
const calendar = [];
const date = new Date(year, month, 1); // month to display
const firstDay = new Date(year, month, 1).getDay(); // first weekday of month
const lastDate = new Date(year, month + 1, 0).getDate(); // last date of month
let day: number | null = 0;
// the calendar is 7*6 fields big, so 42 loops
for (let i = 0; i < 42; i++) {
if (i >= firstDay && day !== null) day = day + 1;
if (day !== null && day > lastDate) day = null;
// append the calendar Array
calendar.push({
day: day === 0 || day === null ? null : day, // null or number
date: day === 0 || day === null ? null : new Date(year, month, day), // null or Date()
today:
day === now.getDate() &&
month === now.getMonth() &&
year === now.getFullYear(), // boolean
});
}
return calendar;
}
/**
* Display previous month by updating state
*/
displayPrevMonth() {
if (this.state.displayedMonth <= 0)
this.setState({
displayedMonth: 11,
displayedYear: this.state.displayedYear - 1,
});
else
this.setState({
displayedMonth: this.state.displayedMonth - 1,
});
}
/**
* Display next month by updating state
*/
displayNextMonth() {
if (this.state.displayedMonth >= 11)
this.setState({
displayedMonth: 0,
displayedYear: this.state.displayedYear + 1,
});
else
this.setState({
displayedMonth: this.state.displayedMonth + 1,
});
}
/**
* Display the selected month (gets fired when clicking on the date string)
*/
displaySelectedMonth() {
if (this.state.selectYearMode) this.toggleYearSelector();
else {
if (!this.state.currentDate) return false;
this.setState({
displayedMonth: this.state.currentDate.getMonth(),
displayedYear: this.state.currentDate.getFullYear(),
});
}
}
toggleYearSelector() {
this.setState({ selectYearMode: !this.state.selectYearMode });
}
changeDisplayedYear(e: any) {
const element = e.target;
this.toggleYearSelector();
this.setState({
displayedYear: parseInt(element.innerHTML, 10),
displayedMonth: 0,
});
}
/**
* Pass the selected date to parent when 'OK' is clicked
*/
passSavedDateDateToParent() {
this.passDateToParent(this.state.currentDate);
}
passDateToParent(date: Date) {
if (typeof this.props.dateReceiver === "function")
this.props.dateReceiver(date);
this.closeDatePicker();
}
componentDidUpdate() {
// if (this.state.selectYearMode) {
// document.getElementsByClassName('selected')[0].scrollIntoView(); // works in every browser incl. IE, replace with scrollIntoViewIfNeeded when browsers support it
// }
}
constructor(props: any) {
super(props);
this.closeDatePicker = this.closeDatePicker.bind(this);
this.dayClicked = this.dayClicked.bind(this);
this.displayNextMonth = this.displayNextMonth.bind(this);
this.displayPrevMonth = this.displayPrevMonth.bind(this);
this.getDaysByMonth = this.getDaysByMonth.bind(this);
this.changeDisplayedYear = this.changeDisplayedYear.bind(this);
this.passDateToParent = this.passDateToParent.bind(this);
this.toggleYearSelector = this.toggleYearSelector.bind(this);
this.displaySelectedMonth = this.displaySelectedMonth.bind(this);
const initial = props.initialDate || now;
this.state = {
currentDate: initial,
displayedMonth: initial.getMonth(),
displayedYear: initial.getFullYear(),
selectYearMode: false,
};
}
render() {
const { currentDate, displayedMonth, displayedYear, selectYearMode } =
this.state;
return (
<div>
<div class={`datePicker ${this.props.opened && "datePicker--opened"}`}>
<div class="datePicker--titles">
<h3
style={{
color: selectYearMode
? "rgba(255,255,255,.87)"
: "rgba(255,255,255,.57)",
}}
onClick={this.toggleYearSelector}
>
{currentDate.getFullYear()}
</h3>
<h2
style={{
color: !selectYearMode
? "rgba(255,255,255,.87)"
: "rgba(255,255,255,.57)",
}}
onClick={this.displaySelectedMonth}
>
{dayArr[currentDate.getDay()]},{" "}
{monthArrShort[currentDate.getMonth()]} {currentDate.getDate()}
</h2>
</div>
{!selectYearMode && (
<nav>
<span onClick={this.displayPrevMonth} class="icon">
<i
style={{ transform: "rotate(180deg)" }}
class="mdi mdi-forward"
/>
</span>
<h4>
{monthArrShortFull[displayedMonth]} {displayedYear}
</h4>
<span onClick={this.displayNextMonth} class="icon">
<i class="mdi mdi-forward" />
</span>
</nav>
)}
<div class="datePicker--scroll">
{!selectYearMode && (
<div class="datePicker--calendar">
<div class="datePicker--dayNames">
{["S", "M", "T", "W", "T", "F", "S"].map((day, i) => (
<span key={i}>{day}</span>
))}
</div>
<div onClick={this.dayClicked} class="datePicker--days">
{/*
Loop through the calendar object returned by getDaysByMonth().
*/}
{this.getDaysByMonth(
this.state.displayedMonth,
this.state.displayedYear,
).map((day) => {
let selected = false;
if (currentDate && day.date)
selected =
currentDate.toLocaleDateString() ===
day.date.toLocaleDateString();
return (
<span
key={day.day}
class={
(day.today ? "datePicker--today " : "") +
(selected ? "datePicker--selected" : "")
}
disabled={!day.date}
data-value={day.date}
>
{day.day}
</span>
);
})}
</div>
</div>
)}
{selectYearMode && (
<div class="datePicker--selectYear">
{(this.props.years || yearArr).map((year) => (
<span
key={year}
class={year === displayedYear ? "selected" : ""}
onClick={this.changeDisplayedYear}
>
{year}
</span>
))}
</div>
)}
</div>
</div>
<div
class="datePicker--background"
onClick={this.closeDatePicker}
style={{
display: this.props.opened ? "block" : "none",
}}
/>
</div>
);
}
}
for (let i = 2010; i <= now.getFullYear() + 10; i++) yearArr.push(i);

View File

@ -1,55 +0,0 @@
/*
This file is part of GNU Taler
(C) 2022 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
*
* @author Sebastian Javier Marchano (sebasjm)
*/
import { h, FunctionalComponent } from "preact";
import { useState } from "preact/hooks";
import { DurationPicker as TestedComponent } from "./DurationPicker.js";
export default {
title: "Components/Picker/Duration",
component: TestedComponent,
argTypes: {
onCreate: { action: "onCreate" },
goBack: { action: "goBack" },
},
};
function createExample<Props>(
Component: FunctionalComponent<Props>,
props: Partial<Props>,
) {
const r = (args: any) => <Component {...args} />;
r.args = props;
return r;
}
export const Example = createExample(TestedComponent, {
days: true,
minutes: true,
hours: true,
seconds: true,
value: 10000000,
});
export const WithState = () => {
const [v, s] = useState<number>(1000000);
return <TestedComponent value={v} onChange={s} days minutes hours seconds />;
};

View File

@ -1,210 +0,0 @@
/*
This file is part of GNU Taler
(C) 2022 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
*
* @author Sebastian Javier Marchano (sebasjm)
*/
import { h, VNode } from "preact";
import { useState } from "preact/hooks";
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
import "../../scss/DurationPicker.scss";
export interface Props {
hours?: boolean;
minutes?: boolean;
seconds?: boolean;
days?: boolean;
onChange: (value: number) => void;
value: number;
}
// inspiration taken from https://github.com/flurmbo/react-duration-picker
export function DurationPicker({
days,
hours,
minutes,
seconds,
onChange,
value,
}: Props): VNode {
const ss = 1000;
const ms = ss * 60;
const hs = ms * 60;
const ds = hs * 24;
const { i18n } = useTranslationContext();
return (
<div class="rdp-picker">
{days && (
<DurationColumn
unit={i18n.str`days`}
max={99}
value={Math.floor(value / ds)}
onDecrease={value >= ds ? () => onChange(value - ds) : undefined}
onIncrease={value < 99 * ds ? () => onChange(value + ds) : undefined}
onChange={(diff) => onChange(value + diff * ds)}
/>
)}
{hours && (
<DurationColumn
unit={i18n.str`hours`}
max={23}
min={1}
value={Math.floor(value / hs) % 24}
onDecrease={value >= hs ? () => onChange(value - hs) : undefined}
onIncrease={value < 99 * ds ? () => onChange(value + hs) : undefined}
onChange={(diff) => onChange(value + diff * hs)}
/>
)}
{minutes && (
<DurationColumn
unit={i18n.str`minutes`}
max={59}
min={1}
value={Math.floor(value / ms) % 60}
onDecrease={value >= ms ? () => onChange(value - ms) : undefined}
onIncrease={value < 99 * ds ? () => onChange(value + ms) : undefined}
onChange={(diff) => onChange(value + diff * ms)}
/>
)}
{seconds && (
<DurationColumn
unit={i18n.str`seconds`}
max={59}
value={Math.floor(value / ss) % 60}
onDecrease={value >= ss ? () => onChange(value - ss) : undefined}
onIncrease={value < 99 * ds ? () => onChange(value + ss) : undefined}
onChange={(diff) => onChange(value + diff * ss)}
/>
)}
</div>
);
}
interface ColProps {
unit: string;
min?: number;
max: number;
value: number;
onIncrease?: () => void;
onDecrease?: () => void;
onChange?: (diff: number) => void;
}
function InputNumber({
initial,
onChange,
}: {
initial: number;
onChange: (n: number) => void;
}) {
const [value, handler] = useState<{ v: string }>({
v: toTwoDigitString(initial),
});
return (
<input
value={value.v}
onBlur={(e) => onChange(parseInt(value.v, 10))}
onInput={(e) => {
e.preventDefault();
const n = Number.parseInt(e.currentTarget.value, 10);
if (isNaN(n)) return handler({ v: toTwoDigitString(initial) });
return handler({ v: toTwoDigitString(n) });
}}
style={{
width: 50,
border: "none",
fontSize: "inherit",
background: "inherit",
}}
/>
);
}
function DurationColumn({
unit,
min = 0,
max,
value,
onIncrease,
onDecrease,
onChange,
}: ColProps): VNode {
const cellHeight = 35;
return (
<div class="rdp-column-container">
<div class="rdp-masked-div">
<hr class="rdp-reticule" style={{ top: cellHeight * 2 - 1 }} />
<hr class="rdp-reticule" style={{ top: cellHeight * 3 - 1 }} />
<div class="rdp-column" style={{ top: 0 }}>
<div class="rdp-cell" key={value - 2}>
{onDecrease && (
<button
style={{ width: "100%", textAlign: "center", margin: 5 }}
onClick={onDecrease}
>
<span class="icon">
<i class="mdi mdi-chevron-up" />
</span>
</button>
)}
</div>
<div class="rdp-cell" key={value - 1}>
{value > min ? toTwoDigitString(value - 1) : ""}
</div>
<div class="rdp-cell rdp-center" key={value}>
{onChange ? (
<InputNumber
initial={value}
onChange={(n) => onChange(n - value)}
/>
) : (
toTwoDigitString(value)
)}
<div>{unit}</div>
</div>
<div class="rdp-cell" key={value + 1}>
{value < max ? toTwoDigitString(value + 1) : ""}
</div>
<div class="rdp-cell" key={value + 2}>
{onIncrease && (
<button
style={{ width: "100%", textAlign: "center", margin: 5 }}
onClick={onIncrease}
>
<span class="icon">
<i class="mdi mdi-chevron-down" />
</span>
</button>
)}
</div>
</div>
</div>
</div>
);
}
function toTwoDigitString(n: number) {
if (n < 10) return `0${n}`;
return `${n}`;
}

View File

@ -15,8 +15,8 @@
*/
import App from "./components/app.js";
export default App;
import { render, h } from "preact";
import { h, render } from "preact";
import "./scss/main.scss";
const app = document.getElementById("app");

View File

@ -42,7 +42,7 @@ export function QrCodeSection({
<article>
<div class="qr-div">
<p>{i18n.str`Use this QR code to withdraw to your mobile wallet:`}</p>
{QR({ text: talerWithdrawUri })}
<QR text={talerWithdrawUri} />
<p>
<i18n.Translate>
Click{" "}