Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/lib/elements/forms/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export { default as Form } from './form.svelte';
export { default as FormItem } from './formItem.svelte';
export { default as FormList } from './formList.svelte';
export { default as Button } from './button.svelte';
export { default as InputDomain } from './inputDomain.svelte';
export { default as InputEmail } from './inputEmail.svelte';
export { default as InputPassword } from './inputPassword.svelte';
export { default as InputText } from './inputText.svelte';
Expand Down
69 changes: 69 additions & 0 deletions src/lib/elements/forms/inputDomain.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<script lang="ts">
import { onMount } from 'svelte';
import { FormItem, Helper } from '.';

export let label: string;
export let showLabel = true;
export let id: string;
export let value = '';
export let placeholder = '';
export let required = false;
export let disabled = false;
export let readonly = false;
export let autofocus = false;
export let autocomplete = false;
export let maxlength: number = null;

// https://www.geeksforgeeks.org/how-to-validate-a-domain-name-using-regular-expression/
const pattern = String.raw`^(?!-)[A-Za-z0-9-]+([\-\.]{1}[a-z0-9]+)*\.[A-Za-z]{2,6}$`;

let element: HTMLInputElement;
let error: string;

onMount(() => {
if (element && autofocus) {
element.focus();
}
});

const handleInvalid = (event: Event) => {
event.preventDefault();

if (element.validity.patternMismatch) {
error = 'Must be a valid domain';
return;
}
if (element.validity.valueMissing) {
error = 'This field is required';
return;
}

error = element.validationMessage;
};

$: if (value) {
error = null;
}
</script>

<FormItem>
<label class:u-hide={!showLabel} class="label" for={id}>{label}</label>
<div class="input-text-wrapper">
<input
{id}
{placeholder}
{disabled}
{required}
{maxlength}
{pattern}
{readonly}
type="text"
autocomplete={autocomplete ? 'on' : 'off'}
bind:value
bind:this={element}
on:invalid={handleInvalid} />
</div>
{#if error}
<Helper type="warning">{error}</Helper>
{/if}
</FormItem>
38 changes: 0 additions & 38 deletions src/routes/console/project-[project]/settings/_createDomain.svelte

This file was deleted.

177 changes: 137 additions & 40 deletions src/routes/console/project-[project]/settings/domains/+page.svelte
Original file line number Diff line number Diff line change
@@ -1,64 +1,161 @@
<script lang="ts">
import { page } from '$app/stores';
import { tooltip } from '$lib/actions/tooltip';
import { Empty } from '$lib/components';
import { Pill } from '$lib/elements';
import { Button } from '$lib/elements/forms';
import {
Table,
TableBody,
TableCell,
TableCellHead,
TableCellText,
TableHeader,
TableRow
} from '$lib/elements/table';
import { Container } from '$lib/layout';
import { addNotification } from '$lib/stores/notifications';
import { sdkForConsole } from '$lib/stores/sdk';
import CreateDomain from '../_createDomain.svelte';
import { wizard } from '$lib/stores/wizard';
import Create from './_create.svelte';
import Delete from './_delete.svelte';
import { domainList } from './store';
import type { Models } from '@aw-labs/appwrite-console';

const projectId = $page.params.project;
const listDomains = () => sdkForConsole.projects.listDomains(projectId);
let showDelete = false;
let selectedDomain: Models.Domain;
let isVerifying = {};

let request = listDomains();
let addDomain = false;
const openWizard = () => {
wizard.start(Create);
};

const refreshDomain = async (domain: Models.Domain, i: number) => {
const domainId = domain.$id;
try {
isVerifying[domainId] = true;

if (domain.verification) {
await domainList.load(projectId);
return;
}
const result = await sdkForConsole.projects.updateDomainVerification(
projectId,
domainId
);
$domainList.domains[i] = result;
} catch (error) {
addNotification({
message: error.message,
type: 'error'
});
} finally {
isVerifying[domainId] = false;
}
};
</script>

<Container>
<h1>Custom Domains</h1>
{#await request}
<div class="u-flex u-gap-12 common-section u-main-space-between">
<h2 class="heading-level-5">Custom Domains</h2>

<Button on:click={openWizard}>
<span class="icon-plus" aria-hidden="true" /> <span class="text">Create domain</span>
</Button>
</div>
{#await domainList.load(projectId)}
<div aria-busy="true" />
{:then response}
<Table>
<TableHeader>
<TableCellHead width={96} />
<TableCellHead>Domain</TableCellHead>
<TableCellHead>TLS</TableCellHead>
</TableHeader>
<TableBody>
{#each response.domains as domain}
<TableRow>
<TableCellText title="Scopes">
<Pill danger={!domain.verification} success={domain.verification}>
{domain.verification ? 'verified' : 'unverified'}
</Pill>
</TableCellText>
<TableCellText title="Domain">
{domain.domain}
</TableCellText>
<TableCellText title="TLS">
{#if domain.certificateId}
Verified
{:else if domain.verification}
In Progress
{:else}
Pending Verification
{/if}
</TableCellText>
</TableRow>
{/each}
</TableBody>
</Table>
{:then}
{#if $domainList.total}
<Table>
<TableHeader>
<TableCellHead />
<TableCellHead>Domain Name</TableCellHead>
<TableCellHead>TLS</TableCellHead>
<TableCellHead />
</TableHeader>
<TableBody>
{#each $domainList.domains as domain, i}
<TableRow>
<TableCellText title="Status">
<Pill warning={!domain.verification} success={domain.verification}>
{domain.verification ? 'verified' : 'unverified'}
</Pill>
</TableCellText>
<TableCellText title="Domain">
{domain.domain}
</TableCellText>
<TableCellText title="TLS">
{#if domain.certificateId}
<Pill success>enabled</Pill>
{:else}
<span
use:tooltip={{
content:
"The process might take a while, click the retry button to see if it's finished"
}}>
<Pill warning>
{!domain.verification
? 'pending verification'
: 'in progress'}
</Pill>
</span>
{/if}
</TableCellText>
<TableCell title="Actions">
<div class="u-flex u-gap-8 u-cross-center u-main-end">
{#if isVerifying[domain.$id]}
<!-- TODO: remove inline styles -->
<div
class="loader"
style="color: hsl(var(--color-neutral-50)); inline-size: 1.25rem; block-size: 1.25rem" />
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably let the UI library handle all these inline styles. Let's add a TODO: remove inline styles to the file remember :)

{:else if !domain.certificateId}
<!-- TODO: remove inline styles -->
<button
class="button is-text is-only-icon u-padding-inline-0"
style="--p-button-size: var(--button-size, 2.0rem);"
aria-label="Verify item"
on:click={() => {
refreshDomain(domain, i);
}}>
<span class="icon-refresh" aria-hidden="true" />
</button>
{/if}
<!-- TODO: remove inline styles -->
<button
class="button tooltip is-text is-only-icon u-padding-inline-0"
style="--p-button-size: var(--button-size, 2.0rem);"
aria-label="Delete item"
on:click={async () => {
showDelete = true;
selectedDomain = domain;
}}>
<span class="icon-trash" aria-hidden="true" />
<span class="tooltip-popup is-bottom" role="tooltip">
Delete
</span>
</button>
</div>
</TableCell>
</TableRow>
{/each}
</TableBody>
</Table>
{:else}
<Empty isButton single on:click={openWizard}>
<div class="common-section">
<div class="u-text-center common-section">
<p>Create your first Domain to get started</p>
</div>
<div class="u-flex u-gap-16 common-section u-main-center">
<Button external href="#/" text>Documentation</Button>
<Button secondary>Create domain</Button>
</div>
</div>
</Empty>
{/if}
{/await}

<Button on:click={() => (addDomain = true)}>Add Domain</Button>
</Container>

<CreateDomain bind:show={addDomain} on:created={() => (request = listDomains())} />
<Delete bind:showDelete bind:selectedDomain />
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<script lang="ts">
import { Wizard } from '$lib/layout';
import { beforeNavigate } from '$app/navigation';
import { wizard } from '$lib/stores/wizard';
import type { WizardStepsType } from '$lib/layout/wizard.svelte';
import { domain } from './wizard/store';
import Step1 from './wizard/step1.svelte';
import Step2 from './wizard/step2.svelte';
import Step3 from './wizard/step3.svelte';
import Step4 from './wizard/step4.svelte';
import { onDestroy } from 'svelte';
onDestroy(() => {
domain.set({ id: '', name: '' });
});
beforeNavigate(() => {
wizard.hide();
});
const stepsComponents: WizardStepsType = new Map();
stepsComponents.set(1, {
label: 'Add your domain',
component: Step1
});
stepsComponents.set(2, {
label: 'Add a CNAME Record',
component: Step2
});
stepsComponents.set(3, {
label: 'Verify domain',
component: Step3
});
stepsComponents.set(4, {
label: 'SSL Certificate',
component: Step4
});
</script>

<Wizard
title="Create domain"
steps={stepsComponents}
finalAction="Go to console"
on:finish={wizard.hide} />
Loading