aboutsummaryrefslogtreecommitdiff
path: root/packages/demobank-ui/src/pages/admin/AccountForm.tsx
diff options
context:
space:
mode:
authorSebastian <sebasjm@gmail.com>2023-09-21 10:31:10 -0300
committerSebastian <sebasjm@gmail.com>2023-09-25 14:50:41 -0300
commit062939d9cc016a186a282f7a48492c3e01cd740c (patch)
treea52c93ef1179ece9d8621731d4a34fc654f18713 /packages/demobank-ui/src/pages/admin/AccountForm.tsx
parentb3c747151bb3f50d28bf6205cafa4b7dd6ae2b1c (diff)
downloadwallet-core-062939d9cc016a186a282f7a48492c3e01cd740c.tar.xz
admin refactor
Diffstat (limited to 'packages/demobank-ui/src/pages/admin/AccountForm.tsx')
-rw-r--r--packages/demobank-ui/src/pages/admin/AccountForm.tsx219
1 files changed, 219 insertions, 0 deletions
diff --git a/packages/demobank-ui/src/pages/admin/AccountForm.tsx b/packages/demobank-ui/src/pages/admin/AccountForm.tsx
new file mode 100644
index 000000000..9ca0323a1
--- /dev/null
+++ b/packages/demobank-ui/src/pages/admin/AccountForm.tsx
@@ -0,0 +1,219 @@
+import { VNode,h } from "preact";
+import { ShowInputErrorLabel } from "../../components/ShowInputErrorLabel.js";
+import { PartialButDefined, RecursivePartial, WithIntermediate, undefinedIfEmpty, validateIBAN } from "../../utils.js";
+import { useState } from "preact/hooks";
+import { useTranslationContext } from "@gnu-taler/web-util/browser";
+import { parsePaytoUri } from "@gnu-taler/taler-util";
+
+const IBAN_REGEX = /^[A-Z][A-Z0-9]*$/;
+const EMAIL_REGEX =
+ /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
+const REGEX_JUST_NUMBERS_REGEX = /^\+[0-9 ]*$/;
+
+/**
+ * Create valid account object to update or create
+ * Take template as initial values for the form
+ * Purpose indicate if all field al read only (show), part of them (update)
+ * or none (create)
+ * @param param0
+ * @returns
+ */
+export function AccountForm({
+ template,
+ purpose,
+ onChange,
+ }: {
+ template: SandboxBackend.Circuit.CircuitAccountData | undefined;
+ onChange: (a: SandboxBackend.Circuit.CircuitAccountData | undefined) => void;
+ purpose: "create" | "update" | "show";
+ }): VNode {
+ const initial = initializeFromTemplate(template);
+ const [form, setForm] = useState(initial);
+ const [errors, setErrors] = useState<
+ RecursivePartial<typeof initial> | undefined
+ >(undefined);
+ const { i18n } = useTranslationContext();
+
+ function updateForm(newForm: typeof initial): void {
+ const parsed = !newForm.cashout_address
+ ? undefined
+ : parsePaytoUri(newForm.cashout_address);
+
+ const errors = undefinedIfEmpty<RecursivePartial<typeof initial>>({
+ cashout_address: !newForm.cashout_address
+ ? i18n.str`required`
+ : !parsed
+ ? i18n.str`does not follow the pattern`
+ : !parsed.isKnown || parsed.targetType !== "iban"
+ ? i18n.str`only "IBAN" target are supported`
+ : !IBAN_REGEX.test(parsed.iban)
+ ? i18n.str`IBAN should have just uppercased letters and numbers`
+ : validateIBAN(parsed.iban, i18n),
+ contact_data: undefinedIfEmpty({
+ email: !newForm.contact_data?.email
+ ? i18n.str`required`
+ : !EMAIL_REGEX.test(newForm.contact_data.email)
+ ? i18n.str`it should be an email`
+ : undefined,
+ phone: !newForm.contact_data?.phone
+ ? i18n.str`required`
+ : !newForm.contact_data.phone.startsWith("+")
+ ? i18n.str`should start with +`
+ : !REGEX_JUST_NUMBERS_REGEX.test(newForm.contact_data.phone)
+ ? i18n.str`phone number can't have other than numbers`
+ : undefined,
+ }),
+ iban: !newForm.iban
+ ? undefined //optional field
+ : !IBAN_REGEX.test(newForm.iban)
+ ? i18n.str`IBAN should have just uppercased letters and numbers`
+ : validateIBAN(newForm.iban, i18n),
+ name: !newForm.name ? i18n.str`required` : undefined,
+ username: !newForm.username ? i18n.str`required` : undefined,
+ });
+ setErrors(errors);
+ setForm(newForm);
+ onChange(errors === undefined ? (newForm as any) : undefined);
+ }
+
+ return (
+ <form class="pure-form">
+ <fieldset>
+ <label for="username">
+ {i18n.str`Username`}
+ {purpose === "create" && <b style={{ color: "red" }}>*</b>}
+ </label>
+ <input
+ name="username"
+ type="text"
+ disabled={purpose !== "create"}
+ value={form.username}
+ onChange={(e) => {
+ form.username = e.currentTarget.value;
+ updateForm(structuredClone(form));
+ }}
+ />{" "}
+ <ShowInputErrorLabel
+ message={errors?.username}
+ isDirty={form.username !== undefined}
+ />
+ </fieldset>
+ <fieldset>
+ <label>
+ {i18n.str`Name`}
+ {purpose === "create" && <b style={{ color: "red" }}>*</b>}
+ </label>
+ <input
+ disabled={purpose !== "create"}
+ value={form.name ?? ""}
+ onChange={(e) => {
+ form.name = e.currentTarget.value;
+ updateForm(structuredClone(form));
+ }}
+ />
+ <ShowInputErrorLabel
+ message={errors?.name}
+ isDirty={form.name !== undefined}
+ />
+ </fieldset>
+ {purpose !== "create" && (
+ <fieldset>
+ <label>{i18n.str`Internal IBAN`}</label>
+ <input
+ disabled={true}
+ value={form.iban ?? ""}
+ onChange={(e) => {
+ form.iban = e.currentTarget.value;
+ updateForm(structuredClone(form));
+ }}
+ />
+ <ShowInputErrorLabel
+ message={errors?.iban}
+ isDirty={form.iban !== undefined}
+ />
+ </fieldset>
+ )}
+ <fieldset>
+ <label>
+ {i18n.str`Email`}
+ {purpose !== "show" && <b style={{ color: "red" }}>*</b>}
+ </label>
+ <input
+ disabled={purpose === "show"}
+ value={form.contact_data.email ?? ""}
+ onChange={(e) => {
+ form.contact_data.email = e.currentTarget.value;
+ updateForm(structuredClone(form));
+ }}
+ />
+ <ShowInputErrorLabel
+ message={errors?.contact_data?.email}
+ isDirty={form.contact_data.email !== undefined}
+ />
+ </fieldset>
+ <fieldset>
+ <label>
+ {i18n.str`Phone`}
+ {purpose !== "show" && <b style={{ color: "red" }}>*</b>}
+ </label>
+ <input
+ disabled={purpose === "show"}
+ value={form.contact_data.phone ?? ""}
+ onChange={(e) => {
+ form.contact_data.phone = e.currentTarget.value;
+ updateForm(structuredClone(form));
+ }}
+ />
+ <ShowInputErrorLabel
+ message={errors?.contact_data?.phone}
+ isDirty={form.contact_data?.phone !== undefined}
+ />
+ </fieldset>
+ <fieldset>
+ <label>
+ {i18n.str`Cashout address`}
+ {purpose !== "show" && <b style={{ color: "red" }}>*</b>}
+ </label>
+ <input
+ disabled={purpose === "show"}
+ value={(form.cashout_address ?? "").substring("payto://iban/".length)}
+ onChange={(e) => {
+ form.cashout_address = "payto://iban/" + e.currentTarget.value;
+ updateForm(structuredClone(form));
+ }}
+ />
+ <ShowInputErrorLabel
+ message={errors?.cashout_address}
+ isDirty={form.cashout_address !== undefined}
+ />
+ </fieldset>
+ </form>
+ );
+ }
+
+ function initializeFromTemplate(
+ account: SandboxBackend.Circuit.CircuitAccountData | undefined,
+ ): WithIntermediate<SandboxBackend.Circuit.CircuitAccountData> {
+ const emptyAccount = {
+ cashout_address: undefined,
+ iban: undefined,
+ name: undefined,
+ username: undefined,
+ contact_data: undefined,
+ };
+ const emptyContact = {
+ email: undefined,
+ phone: undefined,
+ };
+
+ const initial: PartialButDefined<SandboxBackend.Circuit.CircuitAccountData> =
+ structuredClone(account) ?? emptyAccount;
+ if (typeof initial.contact_data === "undefined") {
+ initial.contact_data = emptyContact;
+ }
+ initial.contact_data.email;
+ return initial as any;
+ }
+
+
+ \ No newline at end of file