anastasis-webui: ui tweaks
This commit is contained in:
parent
16662b194d
commit
6a0c5263bb
@ -109,13 +109,23 @@ export * from "./challenge-feedback-types.js";
|
||||
|
||||
const logger = new Logger("anastasis-core:index.ts");
|
||||
|
||||
function getContinents(): ContinentInfo[] {
|
||||
function getContinents(
|
||||
opts: { requireProvider?: boolean } = {},
|
||||
): ContinentInfo[] {
|
||||
const currenciesWithProvider = new Set<string>();
|
||||
anastasisData.providersList.anastasis_provider.forEach((x) => {
|
||||
currenciesWithProvider.add(x.currency);
|
||||
});
|
||||
const continentSet = new Set<string>();
|
||||
const continents: ContinentInfo[] = [];
|
||||
for (const country of anastasisData.countriesList.countries) {
|
||||
if (continentSet.has(country.continent)) {
|
||||
continue;
|
||||
}
|
||||
if (opts.requireProvider && !currenciesWithProvider.has(country.currency)) {
|
||||
// Country's currency doesn't have any providers => skip
|
||||
continue;
|
||||
}
|
||||
continentSet.add(country.continent);
|
||||
continents.push({
|
||||
...{ name_i18n: country.continent_i18n },
|
||||
@ -148,9 +158,18 @@ export class ReducerError extends Error {
|
||||
* Get countries for a continent, abort with ReducerError
|
||||
* exception when continent doesn't exist.
|
||||
*/
|
||||
function getCountries(continent: string): CountryInfo[] {
|
||||
function getCountries(
|
||||
continent: string,
|
||||
opts: { requireProvider?: boolean } = {},
|
||||
): CountryInfo[] {
|
||||
const currenciesWithProvider = new Set<string>();
|
||||
anastasisData.providersList.anastasis_provider.forEach((x) => {
|
||||
currenciesWithProvider.add(x.currency);
|
||||
});
|
||||
const countries = anastasisData.countriesList.countries.filter(
|
||||
(x) => x.continent === continent,
|
||||
(x) =>
|
||||
x.continent === continent &&
|
||||
(!opts.requireProvider || currenciesWithProvider.has(x.currency)),
|
||||
);
|
||||
if (countries.length <= 0) {
|
||||
throw new ReducerError({
|
||||
@ -164,14 +183,18 @@ function getCountries(continent: string): CountryInfo[] {
|
||||
export async function getBackupStartState(): Promise<ReducerStateBackup> {
|
||||
return {
|
||||
backup_state: BackupStates.ContinentSelecting,
|
||||
continents: getContinents(),
|
||||
continents: getContinents({
|
||||
requireProvider: true,
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
export async function getRecoveryStartState(): Promise<ReducerStateRecovery> {
|
||||
return {
|
||||
recovery_state: RecoveryStates.ContinentSelecting,
|
||||
continents: getContinents(),
|
||||
continents: getContinents({
|
||||
requireProvider: true,
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
@ -1073,7 +1096,9 @@ async function backupSelectContinent(
|
||||
state: ReducerStateBackup,
|
||||
args: ActionArgsSelectContinent,
|
||||
): Promise<ReducerStateBackup | ReducerStateError> {
|
||||
const countries = getCountries(args.continent);
|
||||
const countries = getCountries(args.continent, {
|
||||
requireProvider: true,
|
||||
});
|
||||
if (countries.length <= 0) {
|
||||
return {
|
||||
code: TalerErrorCode.ANASTASIS_REDUCER_INPUT_INVALID,
|
||||
@ -1092,7 +1117,9 @@ async function recoverySelectContinent(
|
||||
state: ReducerStateRecovery,
|
||||
args: ActionArgsSelectContinent,
|
||||
): Promise<ReducerStateRecovery | ReducerStateError> {
|
||||
const countries = getCountries(args.continent);
|
||||
const countries = getCountries(args.continent, {
|
||||
requireProvider: true,
|
||||
});
|
||||
return {
|
||||
...state,
|
||||
recovery_state: RecoveryStates.CountrySelecting,
|
||||
|
@ -15,13 +15,13 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Javier Marchano (sebasjm)
|
||||
*/
|
||||
*
|
||||
* @author Sebastian Javier Marchano (sebasjm)
|
||||
*/
|
||||
|
||||
import { h, VNode } from 'preact';
|
||||
import logo from '../../assets/logo.jpeg';
|
||||
import { LangSelector } from './LangSelector';
|
||||
import { h, VNode } from "preact";
|
||||
import logo from "../../assets/logo.jpeg";
|
||||
import { LangSelector } from "./LangSelector";
|
||||
|
||||
interface Props {
|
||||
onMobileMenu: () => void;
|
||||
@ -29,27 +29,51 @@ interface Props {
|
||||
}
|
||||
|
||||
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>
|
||||
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>
|
||||
<a
|
||||
href="mailto:contact@anastasis.lu"
|
||||
style={{ alignSelf: "center", padding: "0.5em" }}
|
||||
>
|
||||
Contact us
|
||||
</a>
|
||||
<a
|
||||
href="https://bugs.anastasis.li/"
|
||||
style={{ alignSelf: "center", padding: "0.5em" }}
|
||||
>
|
||||
Report a bug
|
||||
</a>
|
||||
<a
|
||||
role="button"
|
||||
class="navbar-burger"
|
||||
aria-label="menu"
|
||||
aria-expanded="false"
|
||||
onClick={(e) => {
|
||||
onMobileMenu();
|
||||
e.stopPropagation();
|
||||
}}
|
||||
>
|
||||
<span aria-hidden="true" />
|
||||
<span aria-hidden="true" />
|
||||
<span aria-hidden="true" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<a role="button" class="navbar-burger" aria-label="menu" aria-expanded="false" onClick={(e) => {
|
||||
onMobileMenu()
|
||||
e.stopPropagation()
|
||||
}}>
|
||||
<span aria-hidden="true" />
|
||||
<span aria-hidden="true" />
|
||||
<span aria-hidden="true" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="navbar-menu ">
|
||||
<div class="navbar-end">
|
||||
<div class="navbar-item" style={{ paddingTop: 4, paddingBottom: 4 }}>
|
||||
{/* <LangSelector /> */}
|
||||
<div class="navbar-menu ">
|
||||
<div class="navbar-end">
|
||||
<div class="navbar-item" style={{ paddingTop: 4, paddingBottom: 4 }}>
|
||||
{/* <LangSelector /> */}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -44,9 +44,9 @@ export function Sidebar({ mobile }: Props): VNode {
|
||||
</div>} */}
|
||||
<div class="aside-tools">
|
||||
<div class="aside-tools-label">
|
||||
<div><b>Anastasis</b> Reducer</div>
|
||||
<div><b>Anastasis</b></div>
|
||||
<div class="is-size-7 has-text-right" style={{ lineHeight: 0, marginTop: -10 }}>
|
||||
{process.env.__VERSION__} ({config.version})
|
||||
Version {process.env.__VERSION__} ({config.version})
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -15,41 +15,53 @@
|
||||
*/
|
||||
|
||||
import { ComponentChildren, Fragment, h, VNode } from "preact";
|
||||
import Match from 'preact-router/match';
|
||||
import Match from "preact-router/match";
|
||||
import { useEffect, useState } from "preact/hooks";
|
||||
import { NavigationBar } from "./NavigationBar";
|
||||
import { Sidebar } from "./SideBar";
|
||||
|
||||
|
||||
|
||||
|
||||
interface MenuProps {
|
||||
title: string;
|
||||
}
|
||||
|
||||
function WithTitle({ title, children }: { title: string; children: ComponentChildren }): VNode {
|
||||
function WithTitle({
|
||||
title,
|
||||
children,
|
||||
}: {
|
||||
title: string;
|
||||
children: ComponentChildren;
|
||||
}): VNode {
|
||||
useEffect(() => {
|
||||
document.title = `Taler Backoffice: ${title}`
|
||||
}, [title])
|
||||
return <Fragment>{children}</Fragment>
|
||||
document.title = `${title}`;
|
||||
}, [title]);
|
||||
return <Fragment>{children}</Fragment>;
|
||||
}
|
||||
|
||||
export function Menu({ title }: MenuProps): VNode {
|
||||
const [mobileOpen, setMobileOpen] = useState(false)
|
||||
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>
|
||||
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 {
|
||||
@ -60,37 +72,56 @@ interface NotYetReadyAppMenuProps {
|
||||
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>
|
||||
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>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function NotYetReadyAppMenu({ onLogout, title }: NotYetReadyAppMenuProps): VNode {
|
||||
const [mobileOpen, setMobileOpen] = useState(false)
|
||||
export function NotYetReadyAppMenu({
|
||||
onLogout,
|
||||
title,
|
||||
}: NotYetReadyAppMenuProps): VNode {
|
||||
const [mobileOpen, setMobileOpen] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
document.title = `Taler Backoffice: ${title}`
|
||||
}, [title])
|
||||
|
||||
return <div class={mobileOpen ? "has-aside-mobile-expanded" : ""} onClick={() => setMobileOpen(false)}>
|
||||
<NavigationBar onMobileMenu={() => setMobileOpen(!mobileOpen)} title={title} />
|
||||
{onLogout && <Sidebar mobile={mobileOpen} />}
|
||||
</div>
|
||||
document.title = `Taler Backoffice: ${title}`;
|
||||
}, [title]);
|
||||
|
||||
return (
|
||||
<div
|
||||
class={mobileOpen ? "has-aside-mobile-expanded" : ""}
|
||||
onClick={() => setMobileOpen(false)}
|
||||
>
|
||||
<NavigationBar
|
||||
onMobileMenu={() => setMobileOpen(!mobileOpen)}
|
||||
title={title}
|
||||
/>
|
||||
{onLogout && <Sidebar mobile={mobileOpen} />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export interface Notification {
|
||||
@ -99,6 +130,5 @@ export interface Notification {
|
||||
type: MessageType;
|
||||
}
|
||||
|
||||
export type ValueOrFunction<T> = T | ((p: T) => T)
|
||||
export type MessageType = 'INFO' | 'WARN' | 'ERROR' | 'SUCCESS'
|
||||
|
||||
export type ValueOrFunction<T> = T | ((p: T) => T);
|
||||
export type MessageType = "INFO" | "WARN" | "ERROR" | "SUCCESS";
|
||||
|
@ -123,13 +123,35 @@ export function ContinentSelectionScreen(): VNode {
|
||||
</div>
|
||||
<div class="column is-two-third">
|
||||
<p>
|
||||
Your location will help us to determine which personal information
|
||||
to ask you for the next step.
|
||||
Your choice will help us with asking the right information to unique
|
||||
identify you when you want to recover your backed up secrets.
|
||||
</p>
|
||||
<p>
|
||||
You should choose the country that issued most of your long-term
|
||||
legal documents or personal identifiers.
|
||||
Choose the country that issued most of your long-term legal
|
||||
documents or personal identifiers.
|
||||
</p>
|
||||
<div
|
||||
style={{
|
||||
border: "1px solid gray",
|
||||
borderRadius: "0.5em",
|
||||
backgroundColor: "#fbfcbd",
|
||||
padding: "0.5em",
|
||||
}}
|
||||
>
|
||||
<p>
|
||||
If you just want to try out Anastasis, we recomment that you
|
||||
choose <b>Testcontinent</b> with <b>Demoland</b>. For this special
|
||||
country, you will be asked for a simple number and not real,
|
||||
personal identifiable information.
|
||||
</p>
|
||||
{/*
|
||||
<p>
|
||||
Because of the diversity of personally identifying information in
|
||||
different countries and cultures, we do not support all countries
|
||||
yet. If you want to improve the supported countries,{" "}
|
||||
<a href="mailto:contact@anastasis.lu">contact us</a>.
|
||||
</p> */}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</AnastasisClientFrame>
|
||||
|
Loading…
Reference in New Issue
Block a user