diff options
Diffstat (limited to 'packages/demobank-ui/src/components')
-rw-r--r-- | packages/demobank-ui/src/components/Attention.tsx | 59 | ||||
-rw-r--r-- | packages/demobank-ui/src/components/ErrorLoading.tsx | 22 | ||||
-rw-r--r-- | packages/demobank-ui/src/components/Transactions/views.tsx | 51 |
3 files changed, 98 insertions, 34 deletions
diff --git a/packages/demobank-ui/src/components/Attention.tsx b/packages/demobank-ui/src/components/Attention.tsx new file mode 100644 index 000000000..3313e5796 --- /dev/null +++ b/packages/demobank-ui/src/components/Attention.tsx @@ -0,0 +1,59 @@ +import { TranslatedString } from "@gnu-taler/taler-util"; +import { ComponentChildren, Fragment, VNode, h } from "preact"; +import { assertUnreachable } from "./Routing.js"; + +interface Props { + type?: "info" | "success" | "warning" | "danger", + onClose?: () => void, + title: TranslatedString, + children?: ComponentChildren , +} +export function Attention({ type = "info", title, children, onClose }: Props): VNode { + return <div class={`group attention-${type} mt-2`}> + <div class="rounded-md group-[.attention-info]:bg-blue-50 group-[.attention-warning]:bg-yellow-50 group-[.attention-danger]:bg-red-50 group-[.attention-success]:bg-green-50 p-4 shadow"> + <div class="flex"> + <div > + <svg xmlns="http://www.w3.org/2000/svg" stroke="none" viewBox="0 0 24 24" fill="currentColor" class="w-8 h-8 group-[.attention-info]:text-blue-400 group-[.attention-warning]:text-yellow-400 group-[.attention-danger]:text-red-400 group-[.attention-success]:text-green-400"> + {(() => { + switch (type) { + case "info": + return <path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a.75.75 0 000 1.5h.253a.25.25 0 01.244.304l-.459 2.066A1.75 1.75 0 0010.747 15H11a.75.75 0 000-1.5h-.253a.25.25 0 01-.244-.304l.459-2.066A1.75 1.75 0 009.253 9H9z" /> + case "warning": + return <path fill-rule="evenodd" d="M9.401 3.003c1.155-2 4.043-2 5.197 0l7.355 12.748c1.154 2-.29 4.5-2.599 4.5H4.645c-2.309 0-3.752-2.5-2.598-4.5L9.4 3.003zM12 8.25a.75.75 0 01.75.75v3.75a.75.75 0 01-1.5 0V9a.75.75 0 01.75-.75zm0 8.25a.75.75 0 100-1.5.75.75 0 000 1.5z" /> + case "danger": + return <path fill-rule="evenodd" d="M2.25 12c0-5.385 4.365-9.75 9.75-9.75s9.75 4.365 9.75 9.75-4.365 9.75-9.75 9.75S2.25 17.385 2.25 12zM12 8.25a.75.75 0 01.75.75v3.75a.75.75 0 01-1.5 0V9a.75.75 0 01.75-.75zm0 8.25a.75.75 0 100-1.5.75.75 0 000 1.5z" /> + case "success": + return <path fill-rule="evenodd" d="M7.493 18.75c-.425 0-.82-.236-.975-.632A7.48 7.48 0 016 15.375c0-1.75.599-3.358 1.602-4.634.151-.192.373-.309.6-.397.473-.183.89-.514 1.212-.924a9.042 9.042 0 012.861-2.4c.723-.384 1.35-.956 1.653-1.715a4.498 4.498 0 00.322-1.672V3a.75.75 0 01.75-.75 2.25 2.25 0 012.25 2.25c0 1.152-.26 2.243-.723 3.218-.266.558.107 1.282.725 1.282h3.126c1.026 0 1.945.694 2.054 1.715.045.422.068.85.068 1.285a11.95 11.95 0 01-2.649 7.521c-.388.482-.987.729-1.605.729H14.23c-.483 0-.964-.078-1.423-.23l-3.114-1.04a4.501 4.501 0 00-1.423-.23h-.777zM2.331 10.977a11.969 11.969 0 00-.831 4.398 12 12 0 00.52 3.507c.26.85 1.084 1.368 1.973 1.368H4.9c.445 0 .72-.498.523-.898a8.963 8.963 0 01-.924-3.977c0-1.708.476-3.305 1.302-4.666.245-.403-.028-.959-.5-.959H4.25c-.832 0-1.612.453-1.918 1.227z" /> + default: + assertUnreachable(type) + } + })()} + </svg> + </div> + <div class="ml-3 w-full"> + <h3 class="text-sm group-hover:text-white font-bold group-[.attention-info]:text-blue-800 group-[.attention-success]:text-green-800 group-[.attention-warning]:text-yellow-800 group-[.attention-danger]:text-red-800"> + {title} + </h3> + <div class="mt-2 text-sm group-[.attention-info]:text-blue-700 group-[.attention-warning]:text-yellow-700 group-[.attention-danger]:text-red-700 group-[.attention-success]:text-green-700"> + {children} + </div> + </div> + {onClose && + <div> + <button type="button" class="font-semibold items-center rounded bg-transparent px-2 py-1 text-xs text-gray-900 hover:bg-gray-50" + onClick={(e) => { + e.preventDefault(); + onClose(); + }} + > + <svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"> + <path d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z" /> + </svg> + </button> + </div> + } + </div> + </div> + + </div> +} diff --git a/packages/demobank-ui/src/components/ErrorLoading.tsx b/packages/demobank-ui/src/components/ErrorLoading.tsx index f83b61234..ee62671ce 100644 --- a/packages/demobank-ui/src/components/ErrorLoading.tsx +++ b/packages/demobank-ui/src/components/ErrorLoading.tsx @@ -17,25 +17,13 @@ import { HttpError, useTranslationContext } from "@gnu-taler/web-util/browser"; import { h, VNode } from "preact"; +import { Attention } from "./Attention.js"; +import { TranslatedString } from "@gnu-taler/taler-util"; export function ErrorLoading({ error }: { error: HttpError<SandboxBackend.SandboxError> }): VNode { const { i18n } = useTranslationContext() - return ( - <div><div class="rounded-md bg-red-50 p-4"> - <div class="flex"> - <div class="flex-shrink-0"> - <svg class="h-5 w-5 text-red-400" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"> - <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.28 7.22a.75.75 0 00-1.06 1.06L8.94 10l-1.72 1.72a.75.75 0 101.06 1.06L10 11.06l1.72 1.72a.75.75 0 101.06-1.06L11.06 10l1.72-1.72a.75.75 0 00-1.06-1.06L10 8.94 8.28 7.22z" clip-rule="evenodd" /> - </svg> - </div> - <div class="ml-3 flex-1 md:flex md:justify-between"> - <p class="text-sm font-medium text-red-800">{error.message}</p> - </div> - </div> - <div class="ml-3 flex-1 md:flex md:justify-between"> - <p class="text-sm font-medium text-red-800">Got status "{error.info.status}" on {error.info.url}</p> - </div> - </div> - </div> + return (<Attention type="danger" title={error.message as TranslatedString}> + <p class="text-sm font-medium text-red-800">Got status "{error.info.status}" on {error.info.url}</p> + </Attention> ); } diff --git a/packages/demobank-ui/src/components/Transactions/views.tsx b/packages/demobank-ui/src/components/Transactions/views.tsx index f8b2e3113..f92c874f3 100644 --- a/packages/demobank-ui/src/components/Transactions/views.tsx +++ b/packages/demobank-ui/src/components/Transactions/views.tsx @@ -19,6 +19,7 @@ import { useTranslationContext } from "@gnu-taler/web-util/browser"; import { State } from "./index.js"; import { format, isToday } from "date-fns"; import { Amounts } from "@gnu-taler/taler-util"; +import { useEffect, useRef } from "preact/hooks"; export function LoadingUriView({ error }: State.LoadingUriError): VNode { const { i18n } = useTranslationContext(); @@ -55,9 +56,9 @@ export function ReadyView({ transactions, onNext, onPrev }: State.Ready): VNode <thead> <tr> <th scope="col" class="pl-2 py-3.5 text-left text-sm font-semibold text-gray-900 ">{i18n.str`Date`}</th> - <th scope="col" class="pl-2 py-3.5 text-left text-sm font-semibold text-gray-900 lg:table-cell">{i18n.str`Amount`}</th> - <th scope="col" class="pl-2 py-3.5 text-left text-sm font-semibold text-gray-900 lg:table-cell">{i18n.str`Counterpart`}</th> - <th scope="col" class="pl-2 py-3.5 text-left text-sm font-semibold text-gray-900 lg:table-cell">{i18n.str`Subject`}</th> + <th scope="col" class="hidden sm:table-cell pl-2 py-3.5 text-left text-sm font-semibold text-gray-900 ">{i18n.str`Amount`}</th> + <th scope="col" class="hidden sm:table-cell pl-2 py-3.5 text-left text-sm font-semibold text-gray-900 ">{i18n.str`Counterpart`}</th> + <th scope="col" class="pl-2 py-3.5 text-left text-sm font-semibold text-gray-900 ">{i18n.str`Subject`}</th> </tr> </thead> <tbody> @@ -69,22 +70,38 @@ export function ReadyView({ transactions, onNext, onPrev }: State.Ready): VNode </th> </tr> {txs.map(item => { + const time = item.when.t_ms === "never" ? "" : format(item.when.t_ms, "HH:mm:ss") + const amount = <Fragment> + {item.negative ? "-" : ""} + {item.amount ? ( + `${Amounts.stringifyValue(item.amount)} ${item.amount.currency + }` + ) : ( + <span style={{ color: "grey" }}><{i18n.str`invalid value`}></span> + )} + </Fragment> return (<tr key={idx}> <td class="relative py-2 pl-2 pr-2 text-sm "> - <div class="font-medium text-gray-900">{item.when.t_ms === "never" - ? "" - : format(item.when.t_ms, "HH:mm:ss")}</div> + <div class="font-medium text-gray-900">{time}</div> + <dl class="font-normal sm:hidden"> + <dt class="sr-only sm:hidden"><i18n.Translate>Amount</i18n.Translate></dt> + <dd class="mt-1 truncate text-gray-700"> + {item.negative ? i18n.str`sent` : i18n.str`received`} {item.amount ? ( + `${Amounts.stringifyValue(item.amount)}` + ) : ( + <span style={{ color: "grey" }}><{i18n.str`invalid value`}></span> + )}</dd> + <dt class="sr-only sm:hidden"><i18n.Translate>Counterpart</i18n.Translate></dt> + <dd class="mt-1 truncate text-gray-500 sm:hidden"> + {item.negative ? i18n.str`to` : i18n.str`from`} {item.counterpart} + </dd> + </dl> </td> <td data-negative={item.negative ? "true" : "false"} - class="px-3 py-3.5 text-sm text-gray-500 data-[negative=false]:text-green-600 data-[negative=true]:text-red-600"> - {item.negative ? "-" : ""} - {item.amount ? ( - `${Amounts.stringifyValue(item.amount)} ${item.amount.currency - }` - ) : ( - <span style={{ color: "grey" }}><{i18n.str`invalid value`}></span> - )}</td> - <td class="px-3 py-3.5 text-sm text-gray-500">{item.counterpart}</td> + class="hidden sm:table-cell px-3 py-3.5 text-sm text-gray-500 data-[negative=false]:text-green-600 data-[negative=true]:text-red-600"> + {amount} + </td> + <td class="hidden sm:table-cell px-3 py-3.5 text-sm text-gray-500">{item.counterpart}</td> <td class="px-3 py-3.5 text-sm text-gray-500 break-all min-w-md">{item.subject}</td> </tr>) })} @@ -94,8 +111,8 @@ export function ReadyView({ transactions, onNext, onPrev }: State.Ready): VNode </tbody> </table> - - <nav class="flex items-center justify-between border-t border-gray-200 bg-white px-4 py-3 sm:px-6 rounded-lg" aria-label="Pagination"> + + <nav class="flex items-center justify-between border-t border-gray-200 bg-white px-4 py-3 sm:px-6 rounded-lg" aria-label="Pagination"> <div class="flex flex-1 justify-between sm:justify-end"> <button class="relative disabled:bg-gray-100 disabled:text-gray-500 inline-flex items-center rounded-md bg-white px-3 py-2 text-sm font-semibold text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus-visible:outline-offset-0" |