From de6cc07cb48aefca68634974e0558ce90501b5c7 Mon Sep 17 00:00:00 2001 From: Marlon Date: Thu, 24 Nov 2022 12:45:30 +0100 Subject: [PATCH] Bugfixes and date formatting (#16) --- package.json | 2 ++ src/components/CasParents.tsx | 20 +++++++++-- src/components/CasRepository.tsx | 22 ++++++++++-- src/components/forms/Edit.tsx | 31 ++++++++-------- src/components/tables/ParentTableRow.tsx | 11 ++++-- src/components/tables/RepoTable.tsx | 15 +++++--- src/components/tables/RoaTableRow.tsx | 16 ++++----- src/core/api.ts | 8 +++++ src/core/handlers/handleCaData.ts | 2 +- src/core/store.ts | 46 +++++++++++++++++++++++- src/core/translations.ts | 1 + src/core/utils.ts | 18 +++------- src/css/button.css | 4 +++ src/css/info-table.css | 8 ++--- src/locales/de.ts | 2 ++ src/locales/en.ts | 1 + src/locales/es.ts | 2 ++ src/locales/fr.ts | 2 ++ src/locales/gr.ts | 2 ++ src/locales/nl.ts | 2 ++ src/locales/pt.ts | 2 ++ yarn.lock | 10 ++++++ 22 files changed, 171 insertions(+), 56 deletions(-) diff --git a/package.json b/package.json index 92fac96..7725fc3 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ }, "dependencies": { "buffer": "^6.0.3", + "luxon": "^3.1.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-router5": "^8.0.1", @@ -19,6 +20,7 @@ }, "devDependencies": { "@playwright/test": "^1.27.0", + "@types/luxon": "^3.1.0", "@types/react": "^18.0.21", "@types/react-dom": "^18.0.6", "@typescript-eslint/eslint-plugin": "^5.40.0", diff --git a/src/components/CasParents.tsx b/src/components/CasParents.tsx index a7aeac1..b3fda17 100644 --- a/src/components/CasParents.tsx +++ b/src/components/CasParents.tsx @@ -1,12 +1,14 @@ -import React from 'react'; +import React, { useState } from 'react'; import { useRoute } from 'react-router5'; import Store from '../core/store'; +import { NotificationType } from '../core/types'; import useNavigation from '../hooks/useNavigation'; import useStore from '../hooks/useStore'; import useTranslations from '../hooks/useTranslations'; import CasHeader from './CasHeader'; import ParentModal from './forms/ParentModal'; import Layout from './Layout'; +import NotificationPopup from './NotificationPopup'; import ParentTableRow from './tables/ParentTableRow'; export default function CasParents() { @@ -14,9 +16,13 @@ export default function CasParents() { const { route } = useRoute(); const store = useStore() as Store; const navigate = useNavigation(); + const [loading, setLoading] = useState(false); const syncParents = () => { - store.loadParents(true); + setLoading(true); + store.refreshParents().then(() => { + setLoading(false); + }); }; return ( @@ -24,11 +30,21 @@ export default function CasParents() { {route.name === 'cas.parents.add' && ( )} + {loading && ( + setLoading(false)} + /> + )} {store.parents && store.ca && store.parents[store.ca]?.map((parent) => ( ))} + + -
- - -
); } diff --git a/src/components/tables/ParentTableRow.tsx b/src/components/tables/ParentTableRow.tsx index b8d3519..af7a496 100644 --- a/src/components/tables/ParentTableRow.tsx +++ b/src/components/tables/ParentTableRow.tsx @@ -6,12 +6,17 @@ import useStore from '../../hooks/useStore'; export interface ParentTableRowProps { parent: Parent, + loading: boolean, } -export default function ParentTableRow({parent}: ParentTableRowProps) { +export default function ParentTableRow({parent, loading}: ParentTableRowProps) { const t = useTranslations(); const { locale } = useStore(); + const date = loading ? + t.caDetails.loading.replace('{handle}', '...') : + formatDate(parent.last_exchange.timestamp, locale); + return (

{parent.name}

@@ -24,13 +29,13 @@ export default function ParentTableRow({parent}: ParentTableRowProps) { {t.caDetails.lastExchange} -

{formatDate(parent.last_exchange.timestamp, locale)}

{parent.last_exchange.result != 'Success' ? (

+ {date}
{parent.last_exchange.result.Failure.msg}

) :( -

+

{date}

)} diff --git a/src/components/tables/RepoTable.tsx b/src/components/tables/RepoTable.tsx index 118f7de..a434fa6 100644 --- a/src/components/tables/RepoTable.tsx +++ b/src/components/tables/RepoTable.tsx @@ -6,11 +6,16 @@ import { RepoStatus } from '../../core/types'; export interface RepoTableProps { repo: RepoStatus, locale: string, + loading: boolean, } -export default function RepoTable({ repo, locale }: RepoTableProps) { +export default function RepoTable({ repo, locale , loading}: RepoTableProps) { const t = useTranslations(); + const date = loading ? + t.caDetails.loading.replace('{handle}', '...') : + formatDate(repo.last_exchange.timestamp, locale); + return (
@@ -22,15 +27,15 @@ export default function RepoTable({ repo, locale }: RepoTableProps) { diff --git a/src/components/tables/RoaTableRow.tsx b/src/components/tables/RoaTableRow.tsx index 58f3a39..29702b6 100644 --- a/src/components/tables/RoaTableRow.tsx +++ b/src/components/tables/RoaTableRow.tsx @@ -44,6 +44,11 @@ export default function RoaTableRow({ roa, allowAdd, allowDelete, hasAnnouncemen diff --git a/src/core/api.ts b/src/core/api.ts index f563bcf..bb565f0 100644 --- a/src/core/api.ts +++ b/src/core/api.ts @@ -90,11 +90,19 @@ export default class Api { .then((suggestions) => suggestions.map((suggestion) => ({id: generateId(10), ...suggestion}))); } + refreshCaParents(): Promise { + return this.post('/api/v1/bulk/cas/sync/parent'); + } + getCaParents(ca: string): Promise { return this.get>(`/api/v1/cas/${ca}/parents`) .then((data) => Object.entries(data).map(([name, parent]) => ({ name, ...parent }))); } + refreshCaRepo(): Promise { + return this.post('/api/v1/bulk/cas/sync/repo'); + } + getCaRepoStatus(ca: string): Promise { return this.get(`/api/v1/cas/${ca}/repo/status`); } diff --git a/src/core/handlers/handleCaData.ts b/src/core/handlers/handleCaData.ts index acf5480..ca93ffc 100644 --- a/src/core/handlers/handleCaData.ts +++ b/src/core/handlers/handleCaData.ts @@ -40,7 +40,7 @@ export default async function handleCaData(toState: State, store: Store) { } if (toState.name === 'cas.analyse') { - await store.loadSuggestions(); + await store.loadSuggestions(true); } if (toState.name === 'cas.change' && toState.params.ids) { diff --git a/src/core/store.ts b/src/core/store.ts index b552a49..0c32608 100644 --- a/src/core/store.ts +++ b/src/core/store.ts @@ -22,7 +22,7 @@ import { SuggestionField, UserDetails, } from './types'; -import {compareRoa, compareSuggestion} from './utils'; +import {compareRoa, compareSuggestion, prefixMaxLength} from './utils'; export default class Store implements Data { // general purpose notification message @@ -243,6 +243,28 @@ export default class Store implements Data { }); } + async refreshParents() { + if (!this.ca) { + return; + } + + const ca: string = this.ca; + await this.api.refreshCaParents(); + + // poll every 5 seconds for an update + return new Promise((resolve) => { + const interval = setInterval(() => { + this.api.getCaParents(ca).then((parents) => { + if (JSON.stringify(parents) !== JSON.stringify(this.parents[ca])) { + this.parents[ca] = parents; + clearInterval(interval); + resolve(parents); + } + }); + }, 5 * 1000); + }); + } + async loadParents(force?: boolean) { if (!this.ca || (this.ca && this.parents[this.ca] && force !== true)) { return; @@ -267,6 +289,28 @@ export default class Store implements Data { }); } + async refreshRepo() { + if (!this.ca) { + return; + } + + const ca: string = this.ca; + await this.api.refreshCaRepo(); + + // poll every 5 seconds for an update + return new Promise((resolve) => { + const interval = setInterval(() => { + this.api.getCaRepoStatus(ca).then((status) => { + if (JSON.stringify(status) !== JSON.stringify(this.repoStatus[ca])) { + this.repoStatus[ca] = status; + clearInterval(interval); + resolve(status); + } + }); + }, 5 * 1000); + }); + } + async changeRoutes(add: Suggestion[], remove: Suggestion[]): Promise { if (this.ca === null) { return false; diff --git a/src/core/translations.ts b/src/core/translations.ts index 56a71b4..cf3ed92 100644 --- a/src/core/translations.ts +++ b/src/core/translations.ts @@ -73,6 +73,7 @@ export interface Translations { }, caDetails: { loading: string, + refresh: string, current: string, download: string, noRoas: string, diff --git a/src/core/utils.ts b/src/core/utils.ts index a3fa572..9e45847 100644 --- a/src/core/utils.ts +++ b/src/core/utils.ts @@ -1,6 +1,7 @@ // @ts-ignore import scrypt from './hash.js'; import {Roa, RoaField, SortOrder, Suggestion, SuggestionField, SuggestionReason, Suggestions} from './types.js'; +import { DateTime } from 'luxon'; function dec2hex(dec: number) { return dec.toString(16).padStart(2, '0'); @@ -23,7 +24,6 @@ export function compareRoa(a: Roa, b: Roa, field: RoaField, order: SortOrder) { return order === SortOrder.asc ? direction : -direction; } -// TODO duplicated code export function compareSuggestion(a: Suggestion, b: Suggestion, field: SuggestionField, order: SortOrder) { if (a[field] === b[field]) { return 0; @@ -45,18 +45,9 @@ export function prefixMaxLength(prefix: string | undefined): string { } export function formatDate(seconds: number, locale: string) { - return new Date(seconds * 1000).toLocaleString(locale, { - weekday: 'long', - year: 'numeric', - month: 'long', - day: 'numeric', - hour: 'numeric', - minute: 'numeric', - second: 'numeric', - hour12: false, - timeZone: 'UTC', - timeZoneName: 'short', - }); + const dt = DateTime.fromSeconds(seconds, { zone: 'UTC' }).setLocale(locale); + + return `${dt.toFormat('dd-MM-yyyy TTT')} (${dt.toRelative()})`; } export function isAbsolute(url: string): boolean { @@ -85,6 +76,7 @@ export async function krillHash(username: string, password: string): Promise { const result: Array = []; if (input.too_permissive) { diff --git a/src/css/button.css b/src/css/button.css index 8968982..1380d2e 100644 --- a/src/css/button.css +++ b/src/css/button.css @@ -24,6 +24,10 @@ background: white; } +.button.right { + float: right; +} + .button:hover { opacity: 0.8; } diff --git a/src/css/info-table.css b/src/css/info-table.css index 96f3aa8..3d62e3a 100644 --- a/src/css/info-table.css +++ b/src/css/info-table.css @@ -34,10 +34,10 @@ .info-table .failure, .info-table .success { display: block; - padding-left: 2.5rem; + padding-left: 2rem; margin-top: 0.5rem; - background: no-repeat center left; - background-size: 1.5rem; + background: no-repeat top left; + background-size: 1.2rem; min-height: 2rem; } @@ -46,7 +46,5 @@ } .info-table .success { - width: 2rem; - height: 2rem; background-image: url('../img/check.svg'); } diff --git a/src/locales/de.ts b/src/locales/de.ts index cfa4376..9fadd91 100644 --- a/src/locales/de.ts +++ b/src/locales/de.ts @@ -61,6 +61,8 @@ export const translations: Translations = { }, 'caDetails': { 'loading': 'Lade {handle}', + // TODO translate + 'refresh': 'Refreshing {handle}, this might take several minutes', 'current': 'Aktuelle Zertifizierungsstelle', 'download': 'Download PEM', 'noRoas': 'Keine ROAs gefunden.', diff --git a/src/locales/en.ts b/src/locales/en.ts index e6c4160..44abf88 100644 --- a/src/locales/en.ts +++ b/src/locales/en.ts @@ -61,6 +61,7 @@ export const translations: Translations = { }, 'caDetails': { 'loading': 'Loading {handle}', + 'refresh': 'Refreshing {handle}, this might take several minutes', 'current': 'Current Certificate Authority', 'download': 'Download PEM', 'noRoas': 'No ROAs found.', diff --git a/src/locales/es.ts b/src/locales/es.ts index 82b3be8..40f5c55 100644 --- a/src/locales/es.ts +++ b/src/locales/es.ts @@ -61,6 +61,8 @@ export const translations: Translations = { }, 'caDetails': { 'loading': 'Cargando {handle}', + // TODO translate + 'refresh': 'Refreshing {handle}, this might take several minutes', 'current': 'Autoridad de Certificación actual', 'download': 'Descargar PEM', 'noRoas': 'No se encontró ningún ROA.', diff --git a/src/locales/fr.ts b/src/locales/fr.ts index fde5bc3..1b1e905 100644 --- a/src/locales/fr.ts +++ b/src/locales/fr.ts @@ -62,6 +62,8 @@ export const translations: Translations = { }, 'caDetails': { 'loading': 'En cours de chargement {handle}', + // TODO translate + 'refresh': 'Refreshing {handle}, this might take several minutes', 'current': 'Autorité de Certification actuelle', 'download': 'Télécharger le PEM', 'noRoas': 'Pas de ROAs trouvé.', diff --git a/src/locales/gr.ts b/src/locales/gr.ts index 7315583..8cfe91c 100644 --- a/src/locales/gr.ts +++ b/src/locales/gr.ts @@ -61,6 +61,8 @@ export const translations: Translations = { }, 'caDetails': { 'loading': 'Φόρτωση {handle}', + // TODO translate + 'refresh': 'Refreshing {handle}, this might take several minutes', 'current': 'Τρέχουσα Αρχή Πιστοποίησης (CA)', 'download': 'Λήψη PEM', 'noRoas': 'Δεν βρέθηκαν ROA.', diff --git a/src/locales/nl.ts b/src/locales/nl.ts index 72e5062..ddcee00 100644 --- a/src/locales/nl.ts +++ b/src/locales/nl.ts @@ -61,6 +61,8 @@ export const translations: Translations = { }, 'caDetails': { 'loading': '{handle} wordt geladen', + // TODO translate + 'refresh': 'Refreshing {handle}, this might take several minutes', 'current': 'Huidige certificaatautoriteit', 'download': 'Download PEM', 'noRoas': 'Geen ROAs gevonden.', diff --git a/src/locales/pt.ts b/src/locales/pt.ts index f35c5c6..3eb8683 100644 --- a/src/locales/pt.ts +++ b/src/locales/pt.ts @@ -61,6 +61,8 @@ export const translations: Translations = { }, 'caDetails': { 'loading': 'Carregando {handle}', + // TODO translate + 'refresh': 'Refreshing {handle}, this might take several minutes', 'current': 'Autoridade Certificadora atual', 'download': 'Download do arquivo PEM', 'noRoas': 'Nenhuma ROAs encontrada.', diff --git a/yarn.lock b/yarn.lock index 17d2f19..0abdd60 100644 --- a/yarn.lock +++ b/yarn.lock @@ -358,6 +358,11 @@ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3" integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ== +"@types/luxon@^3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@types/luxon/-/luxon-3.1.0.tgz#b9842d233a06b267fe4117f1c224f20b8a149825" + integrity sha512-gCd/HcCgjqSxfMrgtqxCgYk/22NBQfypwFUG7ZAyG/4pqs51WLTcUzVp1hqTbieDYeHS3WoVEh2Yv/2l+7B0Vg== + "@types/node@*": version "18.8.4" resolved "https://registry.yarnpkg.com/@types/node/-/node-18.8.4.tgz#54be907698f40de8a45770b48486aa3cbd3adff7" @@ -1493,6 +1498,11 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" +luxon@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/luxon/-/luxon-3.1.0.tgz#9ac33d7142b7ea18d4ec8583cdeb0b079abef60d" + integrity sha512-7w6hmKC0/aoWnEsmPCu5Br54BmbmUp5GfcqBxQngRcXJ+q5fdfjEzn7dxmJh2YdDhgW8PccYtlWKSv4tQkrTQg== + magic-string@^0.26.2: version "0.26.7" resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.26.7.tgz#caf7daf61b34e9982f8228c4527474dac8981d6f"
{t.caDetails.lastExchange} -

- {formatDate(repo.last_exchange.timestamp, locale)} -

{repo.last_exchange.result != 'Success' ? (

+ {date}
{repo.last_exchange.result.Failure.msg}

) : ( -

+

+ {date} +

)}
{roa.comment} + {allowDelete && ( + + )} @@ -63,14 +68,9 @@ export default function RoaTableRow({ roa, allowAdd, allowDelete, hasAnnouncemen )} {allowDelete && ( - <> - - - + )}