diff options
author | Sebastian <sebasjm@gmail.com> | 2023-12-30 20:19:47 -0300 |
---|---|---|
committer | Sebastian <sebasjm@gmail.com> | 2023-12-30 20:19:47 -0300 |
commit | 6dd7cfa1ecd3ab95e4ab50e144762e5dceb03328 (patch) | |
tree | ceea1abf8e4be909d15a9219748279c9a895e667 | |
parent | a37abd2472ac2d521e81838632050963157f00af (diff) |
abs time
22 files changed, 362 insertions, 72 deletions
diff --git a/packages/aml-backoffice-ui/src/forms/902_13e.ts b/packages/aml-backoffice-ui/src/forms/902_13e.ts index 7f5887de2..1eccd4b51 100644 --- a/packages/aml-backoffice-ui/src/forms/902_13e.ts +++ b/packages/aml-backoffice-ui/src/forms/902_13e.ts @@ -98,7 +98,7 @@ export const v1 = (current: BaseForm): FlexibleForm<Form902_13.Form> => ({ }, }, { - type: "date", + type: "absoluteTime", props: { name: "dateOfBirth", label: "Date of birth" as TranslatedString, @@ -114,7 +114,7 @@ export const v1 = (current: BaseForm): FlexibleForm<Form902_13.Form> => ({ }, }, { - type: "date", + type: "absoluteTime", props: { name: "dateOfDeath", label: "Date of death" as TranslatedString, @@ -177,7 +177,7 @@ export const v1 = (current: BaseForm): FlexibleForm<Form902_13.Form> => ({ }, }, { - type: "date", + type: "absoluteTime", props: { name: "dateOfBirth", label: "Date of birth" as TranslatedString, @@ -193,7 +193,7 @@ export const v1 = (current: BaseForm): FlexibleForm<Form902_13.Form> => ({ }, }, { - type: "date", + type: "absoluteTime", props: { name: "dateOfDeath", label: "Date of death" as TranslatedString, @@ -237,7 +237,7 @@ export const v1 = (current: BaseForm): FlexibleForm<Form902_13.Form> => ({ }, }, { - type: "date", + type: "absoluteTime", props: { name: "dateOfBirth", label: "Date of birth" as TranslatedString, diff --git a/packages/aml-backoffice-ui/src/forms/902_15e.ts b/packages/aml-backoffice-ui/src/forms/902_15e.ts index 10a2533d4..92ec03716 100644 --- a/packages/aml-backoffice-ui/src/forms/902_15e.ts +++ b/packages/aml-backoffice-ui/src/forms/902_15e.ts @@ -67,7 +67,7 @@ export const v1 = (current: BaseForm): FlexibleForm<Form902_15.Form> => ({ }, }, { - type: "date", + type: "absoluteTime", props: { name: "holder.dateOfBirth", label: "Date of birth" as TranslatedString, @@ -108,7 +108,7 @@ export const v1 = (current: BaseForm): FlexibleForm<Form902_15.Form> => ({ }, }, { - type: "date", + type: "absoluteTime", props: { name: "premiumPayer.dateOfBirth", label: "Date of birth" as TranslatedString, diff --git a/packages/aml-backoffice-ui/src/forms/902_1e.ts b/packages/aml-backoffice-ui/src/forms/902_1e.ts index e2faff63e..806833cd9 100644 --- a/packages/aml-backoffice-ui/src/forms/902_1e.ts +++ b/packages/aml-backoffice-ui/src/forms/902_1e.ts @@ -61,7 +61,7 @@ export const v1 = (current: BaseForm): FlexibleForm<Form902_1.Form> => ({ }, }, { - type: "date", + type: "absoluteTime", props: { name: "naturalCustomer.dateOfBirth", label: "Date of birth" as TranslatedString, @@ -217,7 +217,7 @@ export const v1 = (current: BaseForm): FlexibleForm<Form902_1.Form> => ({ }, }, { - type: "date", + type: "absoluteTime", props: { name: "dateOfBirth", label: "Date of birth" as TranslatedString, @@ -294,7 +294,7 @@ export const v1 = (current: BaseForm): FlexibleForm<Form902_1.Form> => ({ title: "Acceptance of business relationship" as TranslatedString, fields: [ { - type: "date", + type: "absoluteTime", props: { name: "acceptance.when", pattern: "dd/MM/yyyy", diff --git a/packages/aml-backoffice-ui/src/forms/902_4e.ts b/packages/aml-backoffice-ui/src/forms/902_4e.ts index 540e05bf6..14d2d1e1c 100644 --- a/packages/aml-backoffice-ui/src/forms/902_4e.ts +++ b/packages/aml-backoffice-ui/src/forms/902_4e.ts @@ -95,7 +95,7 @@ export const v1 = (current: BaseForm): FlexibleForm<Form902_4.Form> => ({ }, }, { - type: "date", + type: "absoluteTime", props: { label: "The decision of the Senior executive body on the acceptance of a business relationship with a PEP was obtained on" as TranslatedString, @@ -139,7 +139,7 @@ export const v1 = (current: BaseForm): FlexibleForm<Form902_4.Form> => ({ }, }, { - type: "date", + type: "absoluteTime", props: { label: "The decision of the Senior executive body on the acceptance of a business relationship with a PEP was obtained on" as TranslatedString, @@ -585,7 +585,7 @@ export const v1 = (current: BaseForm): FlexibleForm<Form902_4.Form> => ({ }, }, { - type: "date", + type: "absoluteTime", props: { label: "The decision of the Senior executive body on the acceptance of a business relationship with a PEP was obtained on" as TranslatedString, diff --git a/packages/aml-backoffice-ui/src/forms/902_9e.ts b/packages/aml-backoffice-ui/src/forms/902_9e.ts index d1604dab2..cdbd1a84c 100644 --- a/packages/aml-backoffice-ui/src/forms/902_9e.ts +++ b/packages/aml-backoffice-ui/src/forms/902_9e.ts @@ -46,7 +46,7 @@ export const v1 = (current: BaseForm): FlexibleForm<Form902_9.Form> => ({ }, }, { - type: "date", + type: "absoluteTime", props: { name: "dateOfBirth", label: "Date of birth" as TranslatedString, diff --git a/packages/aml-backoffice-ui/src/handlers/Calendar.tsx b/packages/aml-backoffice-ui/src/handlers/Calendar.tsx index 9da6e1757..e476bf6f6 100644 --- a/packages/aml-backoffice-ui/src/handlers/Calendar.tsx +++ b/packages/aml-backoffice-ui/src/handlers/Calendar.tsx @@ -89,28 +89,31 @@ export function Calendar({ value, onChange }: { value: AbsoluteTime | undefined, <div>S</div> <div>S</div> </div> - <div class="isolate mt-2 grid grid-cols-7 gap-px rounded-lg bg-gray-200 text-sm shadow ring-1 ring-gray-200"> - {daysInMonth.map(current => ( - <button type="button" - data-month={isSameMonth(current, showingDate)} - data-today={isSameDay(current, today)} - data-selected={isSameDay(current, selected)} - onClick={() => { - onChange(AbsoluteTime.fromStampMs(current.getTime())) - }} - class="text-gray-400 hover:bg-gray-700 focus:z-10 py-1.5 + <div class="isolate mt-2"> + <div class="grid grid-cols-7 gap-px rounded-lg bg-gray-200 text-sm shadow ring-1 ring-gray-200"> + {daysInMonth.map(current => ( + <button type="button" + data-month={isSameMonth(current, showingDate)} + data-today={isSameDay(current, today)} + data-selected={isSameDay(current, selected)} + onClick={() => { + onChange(AbsoluteTime.fromStampMs(current.getTime())) + }} + class="text-gray-400 hover:bg-gray-700 focus:z-10 py-1.5 data-[month=false]:bg-gray-100 data-[month=true]:bg-white data-[today=true]:font-semibold data-[month=true]:text-gray-900 data-[today=true]:bg-red-300 data-[today=true]:hover:bg-red-200 data-[month=true]:hover:bg-gray-200 data-[selected=true]:!bg-blue-400 data-[selected=true]:hover:!bg-blue-300 "> - <time dateTime={format(current, "yyyy-MM-dd")} - class="mx-auto flex h-7 w-7 items-center justify-center rounded-full"> - {format(current, "dd")} - </time> - </button> - ))} + <time dateTime={format(current, "yyyy-MM-dd")} + class="mx-auto flex h-7 w-7 py-4 px-5 sm:px-8 items-center justify-center rounded-full"> + {format(current, "dd")} + </time> + </button> + ))} + </div> + {daysInMonth.length < 40 ? <div class="w-7 h-7 m-1.5" /> : undefined} </div> </div> } diff --git a/packages/aml-backoffice-ui/src/handlers/Dialog.tsx b/packages/aml-backoffice-ui/src/handlers/Dialog.tsx index f9899e94e..7b41fe487 100644 --- a/packages/aml-backoffice-ui/src/handlers/Dialog.tsx +++ b/packages/aml-backoffice-ui/src/handlers/Dialog.tsx @@ -5,8 +5,8 @@ export function Dialog({ children, onClose }: { onClose?: () => void; children: <div class="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity"></div> <div class="fixed inset-0 z-10 w-screen overflow-y-auto"> - <div class="flex min-h-full items-center justify-center p-4 text-center sm:items-center sm:p-0"> - <div class="relative transform overflow-hidden rounded-lg bg-white px-4 pb-4 pt-5 text-left shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-sm sm:p-6"> + <div class="flex min-h-full items-center justify-center p-4 text-center "> + <div class="relative transform overflow-hidden rounded-lg bg-white p-1 text-left shadow-xl transition-all" onClick={(e) => e.stopPropagation()}> {children} </div> </div> diff --git a/packages/aml-backoffice-ui/src/handlers/InputAbsoluteTime.stories.tsx b/packages/aml-backoffice-ui/src/handlers/InputAbsoluteTime.stories.tsx new file mode 100644 index 000000000..54e41ffae --- /dev/null +++ b/packages/aml-backoffice-ui/src/handlers/InputAbsoluteTime.stories.tsx @@ -0,0 +1,60 @@ +/* + 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 { AbsoluteTime, TranslatedString } from "@gnu-taler/taler-util"; +import * as tests from "@gnu-taler/web-util/testing"; +import { + NiceForm as TestedComponent, +} from "./NiceForm.js"; +import { FlexibleForm } from "./forms.js"; + +export default { + title: "Input Absolute Time", +}; + +export namespace Simplest { + export interface Form { + comment: string; + } +} + +type TargetObject = { + today: AbsoluteTime; +} +const initial: TargetObject = { + today: AbsoluteTime.now() +} + +const form: FlexibleForm<TargetObject> = { + design: [{ + title: "this is a simple form" as TranslatedString, + fields: [{ + type: "absoluteTime", + props: { + label: "label of the field" as TranslatedString, + name: "today", + pattern: "dd/MM/yyyy HH:mm" + }, + }] + }] +} + +export const SimpleComment = tests.createExample(TestedComponent, { initial, form }); diff --git a/packages/aml-backoffice-ui/src/handlers/InputDate.tsx b/packages/aml-backoffice-ui/src/handlers/InputAbsoluteTime.tsx index 794bfd7a2..0e03c5595 100644 --- a/packages/aml-backoffice-ui/src/handlers/InputDate.tsx +++ b/packages/aml-backoffice-ui/src/handlers/InputAbsoluteTime.tsx @@ -7,12 +7,13 @@ import { Calendar } from "./Calendar.js"; import { useState } from "preact/hooks"; import { useField } from "./useField.js"; import { UIFormProps } from "./FormProvider.js"; +import { TimePicker } from "./TimePicker.js"; -export function InputDate<T extends object, K extends keyof T>( +export function InputAbsoluteTime<T extends object, K extends keyof T>( props: { pattern?: string } & UIFormProps<T, K>, ): VNode { const pattern = props.pattern ?? "dd/MM/yyyy"; - const [open, setOpen] = useState(false) + const [open, setOpen] = useState(true) const { value, onChange } = useField<T, K>(props.name); return ( <Fragment> @@ -52,7 +53,7 @@ export function InputDate<T extends object, K extends keyof T>( }} {...props} /> - {open && + {/* {open && <Dialog onClose={() => setOpen(false)}> <Calendar value={value as AbsoluteTime ?? AbsoluteTime.now()} onChange={(v) => { @@ -60,7 +61,17 @@ export function InputDate<T extends object, K extends keyof T>( setOpen(false) }} /> </Dialog> - } + } */} + {open && + <Dialog onClose={() => setOpen(false)} > + <TimePicker value={value as AbsoluteTime ?? AbsoluteTime.now()} + onChange={(v) => { + onChange(v as any) + }} + onConfirm={() => { + setOpen(false) + }} /> + </Dialog>} </Fragment> ); } diff --git a/packages/aml-backoffice-ui/src/handlers/InputChoiceHorizontal.stories.tsx b/packages/aml-backoffice-ui/src/handlers/InputChoiceHorizontal.stories.tsx index e59bbe396..7872afac7 100644 --- a/packages/aml-backoffice-ui/src/handlers/InputChoiceHorizontal.stories.tsx +++ b/packages/aml-backoffice-ui/src/handlers/InputChoiceHorizontal.stories.tsx @@ -40,17 +40,27 @@ type TargetObject = { comment: string; } const initial: TargetObject = { - comment: "some initial comment" + comment: "0" } const form: FlexibleForm<TargetObject> = { design: [{ title: "this is a simple form" as TranslatedString, fields: [{ - type: "text", + type: "choiceHorizontal", props: { label: "label of the field" as TranslatedString, name: "comment", + choices: [{ + label: "first choice" as TranslatedString, + value: "1" + }, { + label: "second choice" as TranslatedString, + value: "2" + }, { + label: "thrid choice" as TranslatedString, + value: "3" + },], }, }] }] diff --git a/packages/aml-backoffice-ui/src/handlers/InputChoiceHorizontal.tsx b/packages/aml-backoffice-ui/src/handlers/InputChoiceHorizontal.tsx index 312e014c5..594b1c32e 100644 --- a/packages/aml-backoffice-ui/src/handlers/InputChoiceHorizontal.tsx +++ b/packages/aml-backoffice-ui/src/handlers/InputChoiceHorizontal.tsx @@ -63,6 +63,7 @@ export function InputChoiceHorizontal<T extends object, K extends keyof T>( <button type="button" disabled={state.disabled} + label={choice.label} class={clazz} onClick={(e) => { onChange( @@ -70,9 +71,7 @@ export function InputChoiceHorizontal<T extends object, K extends keyof T>( ); }} > - {(!converter - ? (choice.value as string) - : converter?.toStringUI(choice.value)) ?? ""} + {choice.label} </button> ); })} diff --git a/packages/aml-backoffice-ui/src/handlers/InputChoiceStacked.stories.tsx b/packages/aml-backoffice-ui/src/handlers/InputChoiceStacked.stories.tsx index bd21687bb..215418430 100644 --- a/packages/aml-backoffice-ui/src/handlers/InputChoiceStacked.stories.tsx +++ b/packages/aml-backoffice-ui/src/handlers/InputChoiceStacked.stories.tsx @@ -47,10 +47,20 @@ const form: FlexibleForm<TargetObject> = { design: [{ title: "this is a simple form" as TranslatedString, fields: [{ - type: "text", + type: "choiceStacked", props: { label: "label of the field" as TranslatedString, name: "comment", + choices: [{ + label: "first choice" as TranslatedString, + value: "1" + }, { + label: "second choice" as TranslatedString, + value: "2" + }, { + label: "thrid choice" as TranslatedString, + value: "3" + },], }, }] }] diff --git a/packages/aml-backoffice-ui/src/handlers/InputFile.stories.tsx b/packages/aml-backoffice-ui/src/handlers/InputFile.stories.tsx index 856b15bc7..8a1783bda 100644 --- a/packages/aml-backoffice-ui/src/handlers/InputFile.stories.tsx +++ b/packages/aml-backoffice-ui/src/handlers/InputFile.stories.tsx @@ -47,10 +47,15 @@ const form: FlexibleForm<TargetObject> = { design: [{ title: "this is a simple form" as TranslatedString, fields: [{ - type: "text", + type: "file", props: { label: "label of the field" as TranslatedString, name: "comment", + required: true, + maxBites: 2 * 1024 * 1024, + accept: ".png", + tooltip: "this is a very long tooltip that explain what the field does without being short" as TranslatedString, + help: "Max size of 2 mega bytes" as TranslatedString, }, }] }] diff --git a/packages/aml-backoffice-ui/src/handlers/InputInteger.stories.tsx b/packages/aml-backoffice-ui/src/handlers/InputInteger.stories.tsx index a08513075..344865817 100644 --- a/packages/aml-backoffice-ui/src/handlers/InputInteger.stories.tsx +++ b/packages/aml-backoffice-ui/src/handlers/InputInteger.stories.tsx @@ -30,27 +30,23 @@ export default { title: "Input Integer", }; -export namespace Simplest { - export interface Form { - comment: string; - } -} type TargetObject = { - comment: string; + age: number; } const initial: TargetObject = { - comment: "some initial comment" + age: 5, } const form: FlexibleForm<TargetObject> = { design: [{ title: "this is a simple form" as TranslatedString, fields: [{ - type: "text", + type: "integer", props: { label: "label of the field" as TranslatedString, - name: "comment", + name: "age", + tooltip: "just numbers" as TranslatedString, }, }] }] diff --git a/packages/aml-backoffice-ui/src/handlers/InputLine.tsx b/packages/aml-backoffice-ui/src/handlers/InputLine.tsx index 890bb54cb..8c44b1ca5 100644 --- a/packages/aml-backoffice-ui/src/handlers/InputLine.tsx +++ b/packages/aml-backoffice-ui/src/handlers/InputLine.tsx @@ -46,11 +46,11 @@ export function LabelWithTooltipMaybeRequired({ {Label} <span class="relative flex items-center group pl-2"> {TooltipIcon} - <div class="absolute bottom-0 flex flex-col items-center mb-6 group-hover:flex"> - <span class="relative z-10 p-2 text-xs leading-none text-white whitespace-no-wrap bg-black shadow-lg"> + <div class="absolute bottom-0 -ml-10 hidden flex-col items-center mb-6 group-hover:flex w-28"> + <div class="relative z-10 p-2 text-xs leading-none text-white whitespace-no-wrap bg-black shadow-lg"> {tooltip} - </span> - <div class="w-3 h-3 -mt-2 rotate-45 bg-black"></div> + </div> + <div class="w-3 h-3 -mt-2 rotate-45 bg-black"></div> </div> </span> </div> diff --git a/packages/aml-backoffice-ui/src/handlers/InputSelectMultiple.stories.tsx b/packages/aml-backoffice-ui/src/handlers/InputSelectMultiple.stories.tsx index eddb7fe94..4dac61f21 100644 --- a/packages/aml-backoffice-ui/src/handlers/InputSelectMultiple.stories.tsx +++ b/packages/aml-backoffice-ui/src/handlers/InputSelectMultiple.stories.tsx @@ -37,20 +37,51 @@ export namespace Simplest { } type TargetObject = { - comment: string; + pets: string[]; + things: string[]; } const initial: TargetObject = { - comment: "some initial comment" + pets: [], + things: [], } const form: FlexibleForm<TargetObject> = { design: [{ title: "this is a simple form" as TranslatedString, fields: [{ - type: "text", + type: "selectMultiple", props: { - label: "label of the field" as TranslatedString, - name: "comment", + label: "allow diplicates" as TranslatedString, + name: "pets", + placeholder: "search..." as TranslatedString, + choices: [{ + label: "one label" as TranslatedString, + value: "one" + }, { + label: "two label" as TranslatedString, + value: "two" + }, { + label: "five label" as TranslatedString, + value: "five" + }] + }, + }, { + type: "selectMultiple", + props: { + label: "unique values" as TranslatedString, + name: "things", + unique: true, + placeholder: "search..." as TranslatedString, + choices: [{ + label: "one label" as TranslatedString, + value: "one" + }, { + label: "two label" as TranslatedString, + value: "two" + }, { + label: "five label" as TranslatedString, + value: "five" + }] }, }] }] diff --git a/packages/aml-backoffice-ui/src/handlers/InputSelectOne.stories.tsx b/packages/aml-backoffice-ui/src/handlers/InputSelectOne.stories.tsx index 7321577c3..0bb871500 100644 --- a/packages/aml-backoffice-ui/src/handlers/InputSelectOne.stories.tsx +++ b/packages/aml-backoffice-ui/src/handlers/InputSelectOne.stories.tsx @@ -37,20 +37,31 @@ export namespace Simplest { } type TargetObject = { - comment: string; + things: string; } const initial: TargetObject = { - comment: "some initial comment" + things: "one" } const form: FlexibleForm<TargetObject> = { design: [{ title: "this is a simple form" as TranslatedString, fields: [{ - type: "text", + type: "selectOne", props: { label: "label of the field" as TranslatedString, - name: "comment", + name: "things", + placeholder: "search..." as TranslatedString, + choices: [{ + label: "one label" as TranslatedString, + value: "one" + }, { + label: "two label" as TranslatedString, + value: "two" + }, { + label: "five label" as TranslatedString, + value: "five" + }] }, }] }] diff --git a/packages/aml-backoffice-ui/src/handlers/InputDate.stories.tsx b/packages/aml-backoffice-ui/src/handlers/InputToggle.stories.tsx index 187c78bfb..735e812f3 100644 --- a/packages/aml-backoffice-ui/src/handlers/InputDate.stories.tsx +++ b/packages/aml-backoffice-ui/src/handlers/InputToggle.stories.tsx @@ -27,7 +27,7 @@ import { import { FlexibleForm } from "./forms.js"; export default { - title: "Input Date", + title: "Input Toggle", }; export namespace Simplest { @@ -47,7 +47,7 @@ const form: FlexibleForm<TargetObject> = { design: [{ title: "this is a simple form" as TranslatedString, fields: [{ - type: "text", + type: "toggle", props: { label: "label of the field" as TranslatedString, name: "comment", diff --git a/packages/aml-backoffice-ui/src/handlers/InputToggle.tsx b/packages/aml-backoffice-ui/src/handlers/InputToggle.tsx new file mode 100644 index 000000000..1ea8699b2 --- /dev/null +++ b/packages/aml-backoffice-ui/src/handlers/InputToggle.tsx @@ -0,0 +1,38 @@ +import { VNode, h } from "preact"; +import { InputLine, LabelWithTooltipMaybeRequired } from "./InputLine.js"; +import { UIFormProps } from "./FormProvider.js"; +import { useField } from "./useField.js"; + +export function InputToggle<T extends object, K extends keyof T>( + props: UIFormProps<T, K>, +): VNode { + const { + name, + label, + tooltip, + help, + placeholder, + required, + before, + after, + converter, + } = props; + const { value, onChange, state, isDirty } = useField<T, K>(name); + + const isOn = !!value + return <div class="sm:col-span-6"> + <div class="flex items-center justify-between"> + <LabelWithTooltipMaybeRequired + label={label} + required={required} + tooltip={tooltip} + /> + <button type="button" data-enabled={isOn} + class="bg-indigo-600 data-[enabled=false]:bg-gray-200 relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-indigo-600 focus:ring-offset-2" + role="switch" aria-checked="false" aria-labelledby="availability-label" aria-describedby="availability-description" + onClick={() => { onChange(!isOn as any); }}> + <span aria-hidden="true" data-enabled={isOn} class="translate-x-5 data-[enabled=false]:translate-x-0 pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out"></span> + </button> + </div> + </div> +} diff --git a/packages/aml-backoffice-ui/src/handlers/TimePicker.tsx b/packages/aml-backoffice-ui/src/handlers/TimePicker.tsx new file mode 100644 index 000000000..c6dc3e794 --- /dev/null +++ b/packages/aml-backoffice-ui/src/handlers/TimePicker.tsx @@ -0,0 +1,110 @@ +import { AbsoluteTime } from "@gnu-taler/taler-util" +import { useTranslationContext } from "@gnu-taler/web-util/browser" +import { startOfDay, getHours, getMinutes, getSeconds, setHours } from "date-fns" +import { Fragment, VNode, h } from "preact" +import { useState } from "preact/hooks" + +export function TimePicker({ value, onChange, onConfirm }: { value: AbsoluteTime | undefined, onChange: (v: AbsoluteTime) => void, onConfirm: () => void }): VNode { + const date = !value ? new Date() : new Date(AbsoluteTime.toStampMs(value)) + const hours = getHours(date) % 12 + const minutes = getMinutes(date) + const seconds = getSeconds(date) + + const { i18n } = useTranslationContext() + + return <Fragment> + <div class="flex flex-col bg-white rounded-t-sm justify-around" > + {/* time selection */} + <div id="" class="bg-[#3b71ca] dark:bg-zinc-700 h-24 rounded-t-lg p-12 flex flex-row items-center justify-center"> + <div class="flex w-full justify-evenly"> + <div class=""> + <span class="relative h-full"> + <button type="button" class="py-1 px-3 text-[3.75rem] font-light leading-[1.2] text-white opacity-[.54] border-none bg-transparent p-0 cursor-pointer hover:bg-[#00000026] hover:outline-none focus:bg-[#00000026] focus:outline-none " + style="pointer-events: none;"> + {new String(hours).padStart(2, "0")} + </button> + </span> + <span type="button" class="font-light leading-[1.2] text-[3.75rem] opacity-[.54] border-none bg-transparent p-0 text-white " >:</span> + <span class="relative h-full"> + <button type="button" class="py-1 px-3 text-[3.75rem] font-light leading-[1.2] text-white opacity-[.54] border-none bg-transparent p-0 cursor-pointer hover:bg-[#00000026] hover:outline-none focus:bg-[#00000026] focus:outline-none " > + {new String(minutes).padStart(2, "0")} + </button> + </span> + <span type="button" class="font-light leading-[1.2] text-[3.75rem] opacity-[.54] border-none bg-transparent p-0 text-white " >:</span> + <span class="relative h-full"> + <button type="button" class="py-1 px-3 text-[3.75rem] font-light leading-[1.2] text-white opacity-[.54] border-none bg-transparent p-0 cursor-pointer hover:bg-[#00000026] hover:outline-none focus:bg-[#00000026] focus:outline-none " > + {new String(seconds).padStart(2, "0")} + </button> + </span> + </div> + <div class="flex flex-col justify-center text-[18px] text-[#ffffff8a] "> + <button type="button" class="py-1 px-3 bg-transparent border-none text-white cursor-pointer hover:bg-[#00000026] hover:outline-none focus:bg-[#00000026] focus:outline-none" > + AM + </button> + <button type="button" class="py-1 px-3 bg-transparent border-none text-white cursor-pointer hover:bg-[#00000026] hover:outline-none focus:bg-[#00000026] focus:outline-none" > + PM + </button> + </div> + </div> + </div> + {/* clock */} + <div id="" class="mt-2 min-w-[310px] max-w-[325px] min-h-[305px] overflow-x-hidden h-full flex justify-center mx-auto flex-col items-center dark:bg-zinc-500" > + <div class="relative rounded-[100%] w-[260px] h-[260px] cursor-default my-0 mx-auto bg-[#00000012] dark:bg-zinc-600/50 animate-[show-up-clock_350ms_linear]" > + + <span class="top-1/2 left-1/2 w-[6px] h-[6px] -translate-y-1/2 -translate-x-1/2 rounded-[50%] bg-[#3b71ca] absolute" ></span> + <div class="bg-[#3b71ca] bottom-1/2 h-2/5 left-[calc(50%-1px)] rtl:!left-auto origin-[center_bottom_0] rtl:!origin-[50%_50%_0] w-[2px] absolute" style={{ transform: "rotateZ(60deg)", height: "calc(35% + 1px)" }}> + {/* <div class="-top-[21px] -left-[15px] w-[4px] border-[14px] border-solid border-[#3b71ca] h-[4px] box-content rounded-[100%] absolute" style="background-color: rgb(25, 118, 210);"></div> */} + </div> + + <span onClick={() => onChange(AbsoluteTime.fromStampMs(setHours(date, 12).getTime()))} class="absolute rounded-[100%] w-[32px] h-[32px] text-center cursor-pointer text-[1.1rem] bg-transparent flex justify-center items-center font-light focus:outline-none selection:bg-transparent data-[selected=true]:text-white data-[selected=true]:bg-[#3b71ca] data-[selected=true]:font-normal" style="left: 114px; bottom: 224px;"> + <span>0</span> + </span> + <span onClick={() => onChange(AbsoluteTime.fromStampMs(setHours(date, 1).getTime()))} class="absolute rounded-[100%] w-[32px] h-[32px] text-center cursor-pointer text-[1.1rem] bg-transparent flex justify-center items-center font-light focus:outline-none selection:bg-transparent data-[selected=true]:text-white data-[selected=true]:bg-[#3b71ca] data-[selected=true]:font-normal" style="left: 169px; bottom: 209.263px;"> + <span >1</span> + </span> + <span onClick={() => onChange(AbsoluteTime.fromStampMs(setHours(date, 2).getTime()))} class="absolute rounded-[100%] w-[32px] h-[32px] text-center cursor-pointer text-[1.1rem] bg-transparent flex justify-center items-center font-light focus:outline-none selection:bg-transparent data-[selected=true]:text-white data-[selected=true]:bg-[#3b71ca] data-[selected=true]:font-normal" data-selected={true} style="left: 209.263px; bottom: 169px;" > + <span >2</span> + </span> + <span onClick={() => onChange(AbsoluteTime.fromStampMs(setHours(date, 3).getTime()))} class="absolute rounded-[100%] w-[32px] h-[32px] text-center cursor-pointer text-[1.1rem] bg-transparent flex justify-center items-center font-light focus:outline-none selection:bg-transparent data-[selected=true]:text-white data-[selected=true]:bg-[#3b71ca] data-[selected=true]:font-normal" style="left: 224px; bottom: 114px;"> + <span >3</span> + </span> + <span onClick={() => onChange(AbsoluteTime.fromStampMs(setHours(date, 4).getTime()))} class="absolute rounded-[100%] w-[32px] h-[32px] text-center cursor-pointer text-[1.1rem] bg-transparent flex justify-center items-center font-light focus:outline-none selection:bg-transparent data-[selected=true]:text-white data-[selected=true]:bg-[#3b71ca] data-[selected=true]:font-normal" style="left: 209.263px; bottom: 59px;"> + <span >4</span> + </span> + <span onClick={() => onChange(AbsoluteTime.fromStampMs(setHours(date, 5).getTime()))} class="absolute rounded-[100%] w-[32px] h-[32px] text-center cursor-pointer text-[1.1rem] bg-transparent flex justify-center items-center font-light focus:outline-none selection:bg-transparent data-[selected=true]:text-white data-[selected=true]:bg-[#3b71ca] data-[selected=true]:font-normal" style="left: 169px; bottom: 18.7372px;"> + <span >5</span> + </span> + <span onClick={() => onChange(AbsoluteTime.fromStampMs(setHours(date, 6).getTime()))} class="absolute rounded-[100%] w-[32px] h-[32px] text-center cursor-pointer text-[1.1rem] bg-transparent flex justify-center items-center font-light focus:outline-none selection:bg-transparent data-[selected=true]:text-white data-[selected=true]:bg-[#3b71ca] data-[selected=true]:font-normal" style="left: 114px; bottom: 4px;"> + <span >6</span> + </span> + <span onClick={() => onChange(AbsoluteTime.fromStampMs(setHours(date, 7).getTime()))} class="absolute rounded-[100%] w-[32px] h-[32px] text-center cursor-pointer text-[1.1rem] bg-transparent flex justify-center items-center font-light focus:outline-none selection:bg-transparent data-[selected=true]:text-white data-[selected=true]:bg-[#3b71ca] data-[selected=true]:font-normal" style="left: 59px; bottom: 18.7372px;"> + <span >7</span> + </span> + <span onClick={() => onChange(AbsoluteTime.fromStampMs(setHours(date, 8).getTime()))} class="absolute rounded-[100%] w-[32px] h-[32px] text-center cursor-pointer text-[1.1rem] bg-transparent flex justify-center items-center font-light focus:outline-none selection:bg-transparent data-[selected=true]:text-white data-[selected=true]:bg-[#3b71ca] data-[selected=true]:font-normal" style="left: 18.7372px; bottom: 59px;"> + <span >8</span> + </span> + <span onClick={() => onChange(AbsoluteTime.fromStampMs(setHours(date, 9).getTime()))} class="absolute rounded-[100%] w-[32px] h-[32px] text-center cursor-pointer text-[1.1rem] bg-transparent flex justify-center items-center font-light focus:outline-none selection:bg-transparent data-[selected=true]:text-white data-[selected=true]:bg-[#3b71ca] data-[selected=true]:font-normal" style="left: 4px; bottom: 114px;"> + <span >9</span> + </span> + <span onClick={() => onChange(AbsoluteTime.fromStampMs(setHours(date, 10).getTime()))} class="absolute rounded-[100%] w-[32px] h-[32px] text-center cursor-pointer text-[1.1rem] bg-transparent flex justify-center items-center font-light focus:outline-none selection:bg-transparent data-[selected=true]:text-white data-[selected=true]:bg-[#3b71ca] data-[selected=true]:font-normal" style="left: 18.7372px; bottom: 169px;"> + <span >10</span> + </span> + <span onClick={() => onChange(AbsoluteTime.fromStampMs(setHours(date, 11).getTime()))} class="absolute rounded-[100%] w-[32px] h-[32px] text-center cursor-pointer text-[1.1rem] bg-transparent flex justify-center items-center font-light focus:outline-none selection:bg-transparent data-[selected=true]:text-white data-[selected=true]:bg-[#3b71ca] data-[selected=true]:font-normal" style="left: 59px; bottom: 209.263px;"> + <span >11</span> + </span> + </div> + </div> + </div> + <div id="" class="rounded-b-lg flex justify-between items-center w-full h-[56px] px-[12px] bg-white dark:bg-zinc-500"> + <div class="w-full flex justify-end"> + <button + type="submit" + onClick={onConfirm} + class="rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600" + > + <i18n.Translate>Confirm</i18n.Translate> + </button> + </div> + </div> + </Fragment> +} diff --git a/packages/aml-backoffice-ui/src/handlers/forms.ts b/packages/aml-backoffice-ui/src/handlers/forms.ts index b1fcc2937..1c212fafa 100644 --- a/packages/aml-backoffice-ui/src/handlers/forms.ts +++ b/packages/aml-backoffice-ui/src/handlers/forms.ts @@ -1,6 +1,6 @@ import { TranslatedString } from "@gnu-taler/taler-util"; import { InputText } from "./InputText.js"; -import { InputDate } from "./InputDate.js"; +import { InputAbsoluteTime } from "./InputAbsoluteTime.js"; import { InputInteger } from "./InputInteger.js"; import { h as create, Fragment, VNode } from "preact"; import { InputChoiceStacked } from "./InputChoiceStacked.js"; @@ -15,6 +15,7 @@ import { FormProvider, FormState } from "./FormProvider.js"; import { InputLine } from "./InputLine.js"; import { InputAmount } from "./InputAmount.js"; import { InputChoiceHorizontal } from "./InputChoiceHorizontal.js"; +import { InputToggle } from "./InputToggle.js"; export type DoubleColumnForm = Array<DoubleColumnFormSection | undefined>; @@ -42,8 +43,9 @@ type FieldType<T extends object = any, K extends keyof T = any> = { textArea: Parameters<typeof InputTextArea<T, K>>[0]; choiceStacked: Parameters<typeof InputChoiceStacked<T, K>>[0]; choiceHorizontal: Parameters<typeof InputChoiceHorizontal<T, K>>[0]; - date: Parameters<typeof InputDate<T, K>>[0]; + absoluteTime: Parameters<typeof InputAbsoluteTime<T, K>>[0]; integer: Parameters<typeof InputInteger<T, K>>[0]; + toggle: Parameters<typeof InputToggle<T, K>>[0]; amount: Parameters<typeof InputAmount<T, K>>[0]; }; @@ -63,7 +65,8 @@ export type UIFormField = | { type: "choiceStacked"; props: FieldType["choiceStacked"] } | { type: "choiceHorizontal"; props: FieldType["choiceHorizontal"] } | { type: "integer"; props: FieldType["integer"] } - | { type: "date"; props: FieldType["date"] }; + | { type: "toggle"; props: FieldType["toggle"] } + | { type: "absoluteTime"; props: FieldType["absoluteTime"] }; type FieldComponentFunction<key extends keyof FieldType> = ( props: FieldType[key], @@ -86,7 +89,7 @@ const UIFormConfiguration: UIFormFieldMap = { file: InputFile, textArea: InputTextArea, //@ts-ignore - date: InputDate, + absoluteTime: InputAbsoluteTime, //@ts-ignore choiceStacked: InputChoiceStacked, //@ts-ignore @@ -97,6 +100,8 @@ const UIFormConfiguration: UIFormFieldMap = { //@ts-ignore selectMultiple: InputSelectMultiple, //@ts-ignore + toggle: InputToggle, + //@ts-ignore amount: InputAmount, }; diff --git a/packages/aml-backoffice-ui/src/handlers/index.stories.ts b/packages/aml-backoffice-ui/src/handlers/index.stories.ts index eedcf7472..55878cb02 100644 --- a/packages/aml-backoffice-ui/src/handlers/index.stories.ts +++ b/packages/aml-backoffice-ui/src/handlers/index.stories.ts @@ -2,7 +2,7 @@ export * as a1 from "./InputAmount.stories.js"; export * as a2 from "./InputArray.stories.js"; export * as a3 from "./InputChoiceHorizontal.stories.js"; export * as a4 from "./InputChoiceStacked.stories.js"; -export * as a5 from "./InputDate.stories.js"; +export * as a5 from "./InputAbsoluteTime.stories.js"; export * as a6 from "./InputFile.stories.js"; export * as a7 from "./InputInteger.stories.js"; export * as a8 from "./InputLine.stories.js"; @@ -10,3 +10,4 @@ export * as a9 from "./InputSelectMultiple.stories.js"; export * as a10 from "./InputSelectOne.stories.js"; export * as a11 from "./InputText.stories.js"; export * as a12 from "./InputTextArea.stories.js"; +export * as a13 from "./InputToggle.stories.js"; |