Refactor zone page layout part 2

This commit is contained in:
nemunaire 2023-11-23 10:34:29 +01:00
parent f17fd5224c
commit bfa1ecc470
7 changed files with 205 additions and 226 deletions

View File

@ -151,17 +151,12 @@
<ModalFooter>
{#if zoneDiff}
{#if zoneDiff.length > 0}
<div class="w-100 row">
<div class="col-auto d-flex flex-column justify-content-center">
<label for="commitmsg">{$t('domains.commit-msg')}</label>
</div>
<div class="col">
<Input
id="commitmsg"
bind:value={diffCommitMsg}
/>
</div>
</div>
<Input
id="commitmsg"
placeholder={$t('domains.commit-msg')}
size="sm"
bind:value={diffCommitMsg}
/>
{/if}
{#if zoneDiffCreated}
<span class="text-success">

View File

@ -61,12 +61,15 @@
"domains": {
"kind": "domain",
"actions": {
"audit": "View changes logs",
"do-migration": "Migrate now",
"reimport": "Re-import",
"view": "View my zone",
"history": "View changes history",
"propagate": "Publish my changes",
"reimport": "Fetch current deployed zone",
"rollback": "Roll back to this version",
"upload": "Import a zone file"
"share": "Share the zone…",
"upload": "Import a zone file",
"view": "View my zone"
},
"alert": {
"remove": "This action only removes {{domain}} from your happyDomain managed domains. All history and abstracted zones will be discarded. The domain {{domain}} remains fully intact at the provider. Are you sure you want to continue?",
@ -94,7 +97,7 @@
"others": "{{count:eq; 0:no other change; 1:{{count}} other change; default:{{count}} others changes}}"
},
"attached-new": "New domain attached to happyDomain!",
"commit-msg": "What's changed:",
"commit-msg": "What's changed?",
"create-new-key": "Create new {{id}} key",
"discard": "Discard",
"drop-alias": "Drop alias",

View File

@ -61,12 +61,15 @@
"domains": {
"kind": "un domaine",
"actions": {
"reimport": "Importer à nouveau",
"view": "Voir ma zone",
"propagate": "Diffuser mes changements",
"rollback": "Revenir à cette version",
"audit": "Voir le journal d'actions du domaine",
"do-migration": "Migrer maintenant",
"upload": "Importer un fichier de zone"
"history": "Voir l'historique des changements",
"propagate": "Diffuser mes changements",
"reimport": "Récupérer la zone actuelle",
"rollback": "Revenir à cette version",
"share": "Partager la zone…",
"upload": "Importer un fichier de zone",
"view": "Voir ma zone"
},
"alert": {
"remove": "Cette action retirera définitivement le domaine {{domain}} de votre liste. L'historique et les zones abstraites seront supprimés. Cela ne supprimera ni n'affectera votre domaine auprès de votre fournisseur, ni ne modifiera ce qui est actuellement diffusé. Cela ne concerne seulement que ce que vous voyez dans happyDomain. Êtes-vous certain de vouloir continuer?",
@ -94,7 +97,7 @@
"others": "{{count:eq; 0:pas d'autres changements; 1:{{count}} autre changement; default:{{count}} autres changements}}"
},
"attached-new": "Nouveau domaine lié à happyDomain!",
"commit-msg": "Description du changement:",
"commit-msg": "Qu'est-ce qui a changé?",
"create-new-key": "Créer une nouvelle clé {{id}}",
"discard": "Supprimer",
"drop-alias": "Supprimer l'alias",

View File

@ -1,8 +1,13 @@
import { get_store_value } from 'svelte/internal';
import type { Load } from '@sveltejs/kit';
import { domains, refreshDomains } from '$lib/stores/domains';
export const load: Load = async({ parent, params }) => {
const data = await parent();
if (!get_store_value(domains)) await refreshDomains();
return {
domain: params.dn,
...data,

View File

@ -7,9 +7,13 @@
import {
Alert,
Button,
ButtonDropdown,
ButtonGroup,
Col,
Container,
DropdownItem,
DropdownMenu,
DropdownToggle,
Icon,
Input,
Row,
@ -28,6 +32,7 @@
import ModalDomainDelete, { controls as ctrlDomainDelete } from '$lib/components/ModalDomainDelete.svelte';
import ModalUploadZone, { controls as ctrlUploadZone } from '$lib/components/ModalUploadZone.svelte';
import ModalViewZone, { controls as ctrlViewZone } from '$lib/components/ModalViewZone.svelte';
import { fqdn } from '$lib/dns';
import type { Domain, DomainInList } from '$lib/model/domain';
import type { ZoneMeta } from '$lib/model/zone';
import { domains, domains_idx, refreshDomains } from '$lib/stores/domains';
@ -64,13 +69,14 @@
let main_error: string | null = null;
let selectedHistory: string | undefined = data.history;
let selectedHistory: string | undefined;
$: selectedHistory = data.history;
$: if (!data.history && $domains_idx[selectedDomain] && $domains_idx[selectedDomain].zone_history && $domains_idx[selectedDomain].zone_history.length > 0) {
selectedHistory = $domains_idx[selectedDomain].zone_history[0] as string;
}
$: if (selectedHistory && data.history != selectedHistory) {
main_error = null;
goto('/domains/' + encodeURIComponent(selectedDomain) + '/' + encodeURIComponent(selectedHistory));
//goto('/domains/' + encodeURIComponent(selectedDomain) + '/' + encodeURIComponent(selectedHistory));
}
let retrievalInProgress = false;
@ -192,127 +198,120 @@
</Input>
</div>
{#if domain && domain.zone_history && $domains_idx[selectedDomain] && domain.id === $domains_idx[selectedDomain].id}
<form class="mt-3">
<div class="d-flex justify-content-between">
<label class="fw-bolder" for="zhistory">{$t('domains.history')}:</label>
<div class="d-flex gap-1">
<Button
outline
color="secondary"
size="sm"
title={$t('domains.actions.upload')}
on:click={ctrlUploadZone.Open}
{#if data && data.streamed && data.streamed.sortedDomains}
<div class="d-flex gap-2 pb-2 sticky-top bg-light" style="padding-top: 10px">
<Button
type="button"
color="secondary"
outline
size="sm"
class="flex-fill"
>
<Icon name="server" />
{$t('domains.add-a-subdomain')}
</Button>
<ButtonDropdown>
<DropdownToggle
color="secondary"
outline
size="sm"
>
<Icon name="wrench-adjustable-circle" aria-hidden="true" />
</DropdownToggle>
<DropdownMenu>
<DropdownItem header class="font-monospace">
{data.selectedDomain.domain}
</DropdownItem>
<DropdownItem
href={`/domains/${data.selectedDomain.domain}/history`}
>
<Icon name="cloud-upload" />
</Button>
<Button
outline
color="secondary"
size="sm"
title={$t('domains.actions.reimport')}
disabled={retrievalInProgress}
{$t('domains.actions.history')}
</DropdownItem>
<DropdownItem
href={`/domains/${data.selectedDomain.domain}/logs`}
>
{$t('domains.actions.audit')}
</DropdownItem>
<DropdownItem divider />
<DropdownItem
on:click={viewZone}
>
{$t('domains.actions.view')}
</DropdownItem>
<DropdownItem
on:click={retrieveZone}
>
{#if retrievalInProgress}
<Spinner size="sm" />
{:else}
<Icon name="cloud-download" />
{/if}
</Button>
</div>
</div>
{#key domain.zone_history}
<select
class="form-select"
id="zhistory"
bind:value={selectedHistory}
{$t('domains.actions.reimport')}
</DropdownItem>
<DropdownItem
on:click={ctrlUploadZone.Open}
>
{$t('domains.actions.upload')}
</DropdownItem>
<DropdownItem divider />
<DropdownItem disabled title="Coming soon...">
{$t('domains.actions.share')}
</DropdownItem>
<DropdownItem
on:click={() => ctrlDomainDelete.Open()}
>
{$t('domains.stop')}
</DropdownItem>
<DropdownItem divider />
<DropdownItem
href={"/providers/" + encodeURIComponent($domains_idx[selectedDomain].id_provider)}
>
{$t('provider.update')}
</DropdownItem>
</DropdownMenu>
</ButtonDropdown>
</div>
{#await data.streamed.sortedDomains then sortedDomains}
<div style="min-height:0; overflow-y: auto;">
{#each sortedDomains as dn}
<a
href={'#' + (dn?dn:'@')}
title={fqdn(dn, data.selectedDomain.domain)}
class="d-block text-truncate font-monospace text-muted text-decoration-none"
style={'max-width: none; padding-left: ' + (dn === '' ? 0 : (dn.split('.').length * 10)) + 'px'}
>
{#each domain.zone_history as history}
<option value={history.id}>{history.last_modified}</option>
{/each}
</select>
{/key}
</form>
{fqdn(dn, data.selectedDomain.domain)}
</a>
{/each}
</div>
{/await}
{/if}
<ButtonGroup class="mt-3 w-100">
<Button
size="sm"
outline
color="info"
title={$t('domains.actions.view')}
on:click={viewZone}
>
<Icon name="list-ul" aria-hidden="true" /><br>
{$t('domains.actions.view')}
</Button>
<div class="flex-fill" />
{#if domain && domain.zone_history && $domains_idx[selectedDomain] && domain.id === $domains_idx[selectedDomain].id}
<ButtonGroup class="mt-2 w-100">
{#if $domains_idx[selectedDomain].zone_history && selectedHistory === $domains_idx[selectedDomain].zone_history[0]}
<Button
size="sm"
size="lg"
color="success"
title={$t('domains.actions.propagate')}
on:click={showDiff}
>
<Icon name="cloud-upload" aria-hidden="true" /><br>
<Icon name="cloud-upload" aria-hidden="true" />
{$t('domains.actions.propagate')}
</Button>
{:else}
<Button
size="sm"
size="lg"
color="warning"
title={$t('domains.actions.rollback')}
on:click={showDiff}
>
<Icon name="cloud-upload" aria-hidden="true" /><br>
<Icon name="cloud-upload" aria-hidden="true" />
{$t('domains.actions.rollback')}
</Button>
{/if}
</ButtonGroup>
{/if}
<div class="flex-fill my-3" />
<Button
class="w-100"
type="button"
outline
color="danger"
disabled={deleteInProgress}
on:click={() => ctrlDomainDelete.Open()}
>
{#if deleteInProgress}
<Spinner size="sm" />
{:else}
<Icon name="trash-fill" />
{/if}
{$t('domains.stop')}
</Button>
{#if $providers_idx && $providers_idx[$domains_idx[selectedDomain].id_provider]}
<form class="mt-2">
<!-- svelte-ignore a11y-label-has-associated-control -->
<label class="font-weight-bolder">
{$t('domains.view.provider')}:
</label>
<div class="pr-2 pl-2">
<Button
href={"/providers/" + encodeURIComponent($domains_idx[selectedDomain].id_provider)}
class="p-3 w-100 text-left"
type="button"
color="info"
outline
>
<div
class="d-inline-block text-center"
style="width: 50px;"
>
<ImgProvider id_provider={$domains_idx[selectedDomain].id_provider} />
</div>
{$providers_idx[$domains_idx[selectedDomain].id_provider]._comment}
</Button>
</div>
</form>
{/if}
<p class="mt-2 mb-1 text-center">
X changes
</p>
{:else}
<div class="mt-4 text-center">
<Spinner color="primary" />
@ -322,10 +321,10 @@
<Col
sm={8}
md={9}
class="d-flex pe-0"
class="d-flex"
>
{#if main_error}
<div class="d-flex flex-column mt-3">
<div class="d-flex flex-column mt-4">
<Alert
color="danger"
fade={false}

View File

@ -1,10 +1,60 @@
import { get_store_value } from 'svelte/internal';
import { error, redirect } from '@sveltejs/kit';
import type { Load } from '@sveltejs/kit';
import { getZone } from '$lib/api/zone';
import { domainCompare } from '$lib/dns';
import { domains_idx } from '$lib/stores/domains';
export const load: Load = async({ parent, params }) => {
const data = await parent();
const domain: DomainInList | null = get_store_value(domains_idx)[data.domain];
if (domain === null) {
throw error(404, {
message: 'Domain not found'
});
}
if (!domain.zone_history || domain.zone_history.length === 0) {
throw error(500, {
message: 'Domain not initialized'
});
}
if (!params.historyid) {
params.historyid = domain.zone_history[0];
//throw redirect(307, `/domains/${data.domain}/${domain.zone_history[0]}`);
}
const zhidx = domain.zone_history.indexOf(params.historyid);
if (zhidx < 0) {
throw error(404, {
message: 'Zone not found in history'
});
}
const zoneId: string = domain.zone_history[zhidx];
const zone = getZone(domain, zoneId);
const sortedDomains = zone.then((z) => {
if (!z.services) {
return [];
}
const domains = Object.keys(z.services);
domains.sort(domainCompare);
return domains;
})
return {
history: params.historyid,
selectedDomain: domain,
zoneId,
streamed: {
zone,
sortedDomains,
},
...data,
}
}

View File

@ -9,7 +9,6 @@
import { getZone } from '$lib/api/zone';
import SubdomainList from '$lib/components/domains/SubdomainList.svelte';
import { domainCompare, fqdn } from '$lib/dns';
import type { DomainInList } from '$lib/model/domain';
import type { Zone } from '$lib/model/zone';
import { domains_idx } from '$lib/stores/domains';
@ -18,120 +17,45 @@
if (!$servicesSpecs) refreshServicesSpecs();
export let data: {domain: string; history: string;};
export let data: {domain: string; selectedDomain: DomainInList; history: string; zoneId: string; streamed: Object};
let domain: DomainInList | null = null;
$: if ($domains_idx[data.domain]) {
domain = $domains_idx[data.domain];
zoneId = null;
}
let zoneId: string | null = null;
$: if (domain && domain.zone_history && domain.zone_history.length > 0) {
let zhidx = 0;
if (data.history) {
zhidx = domain.zone_history.indexOf(data.history);
}
if (zhidx >= 0) {
zoneId = domain.zone_history[zhidx];
} else {
// TODO: Not found
}
}
let zone: Zone | null = null;
async function refreshZone(domain: DomainInList, zoneId: string) {
zone = await getZone(domain, zoneId);
}
$: if (domain && zoneId) refreshZone(domain, zoneId);
let sortedDomains: Array<string> = [];
$: if (zone && zone.services) {
const domains = Object.keys(zone.services);
domains.sort(domainCompare);
sortedDomains = domains;
} else {
sortedDomains = [];
}
let showSubdomainsList = false;
let newSubdomainModalOpened = false;
function addSubdomain() {
newSubdomainModalOpened = true;
}
export let newSubdomainModalOpened = false;
</script>
{#if !domain}
{#if !data.selectedDomain}
<div class="mt-5 text-center flex-fill">
<Spinner label="Spinning" />
<p>{$t('wait.loading')}</p>
</div>
{:else if !zone || !domain.zone_history || domain.zone_history.length == 0}
{:else if !data.selectedDomain.zone_history || data.selectedDomain.zone_history.length == 0}
<div class="mt-4 text-center flex-fill">
<Spinner label={$t('common.spinning')} />
<p>{$t('wait.importing')}</p>
</div>
{:else}
<Row class="pt-3 flex-fill" style="max-width: 100%">
<Col class="mb-5">
{#if !showSubdomainsList}
<Button
class="float-end"
color="secondary"
outline
on:click={() => showSubdomainsList = !showSubdomainsList}
style="position: relative; z-index: 2"
>
<Icon name="list" aria-hidden="true" />
</Button>
{/if}
<SubdomainList
origin={domain}
{showSubdomainsList}
{sortedDomains}
{zone}
bind:newSubdomainModalOpened={newSubdomainModalOpened}
on:update-zone-services={(event) => zone = event.detail}
/>
</Col>
{#if showSubdomainsList}
<Col
sm={3}
class="sticky-top bg-light"
style="margin-top: -10px; overflow-y: auto; max-height: 100vh"
>
<div class="d-flex gap-2 pb-2 sticky-top bg-light" style="padding-top: 10px">
<Button
type="button"
color="secondary"
outline
size="sm"
class="ml-2 w-100"
on:click={addSubdomain}
>
<Icon name="server" />
{$t('domains.add-a-subdomain')}
</Button>
<Button
color="secondary"
on:click={() => showSubdomainsList = !showSubdomainsList}
>
<Icon name="list" aria-hidden="true" /><br>
</Button>
</div>
{#each sortedDomains as dn}
<a
href={'#' + (dn?dn:'@')}
title={fqdn(dn, domain.domain)}
class="d-block text-truncate font-monospace text-muted text-decoration-none"
style={'max-width: none; padding-left: ' + (dn === '' ? 0 : (dn.split('.').length * 10)) + 'px'}
>
{fqdn(dn, domain.domain)}
</a>
{/each}
</Col>
{#await data.streamed.zone}
<div class="mt-4 text-center flex-fill">
<Spinner label={$t('common.spinning')} />
<p>{$t('wait.loading')}</p>
</div>
{:then zone}
{#if zone}
{#await data.streamed.sortedDomains}
<div class="mt-4 text-center flex-fill">
<Spinner label={$t('common.spinning')} />
<p>{$t('wait.loading')}</p>
</div>
{:then sortedDomains}
<div style="max-width: 100%;" class="pt-1">
<SubdomainList
origin={data.selectedDomain}
{sortedDomains}
{zone}
bind:newSubdomainModalOpened={newSubdomainModalOpened}
on:update-zone-services={(event) => zone = event.detail}
/>
</div>
{/await}
{/if}
</Row>
{/await}
{/if}