Merge branch 'feat-pink-v2' of https://github.com/appwrite/console into feat-new-onboarding-flow

This commit is contained in:
Torsten Dittmann
2025-01-29 19:15:39 +01:00
26 changed files with 5103 additions and 5918 deletions
+1 -1
View File
@@ -23,7 +23,7 @@
"@appwrite.io/pink-icons": "0.25.0",
"@appwrite.io/pink-icons-svelte": "https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@7484609",
"@appwrite.io/pink-legacy": "^1.0.0",
"@appwrite.io/pink-svelte": "https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@a7f0462",
"@appwrite.io/pink-svelte": "https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@464cb37",
"@popperjs/core": "^2.11.8",
"@sentry/sveltekit": "^8.38.0",
"@stripe/stripe-js": "^3.5.0",
+4619 -5558
View File
File diff suppressed because it is too large Load Diff
+35 -12
View File
@@ -1,6 +1,5 @@
<script lang="ts">
import { base } from '$app/paths';
import { page } from '$app/stores';
import { EmptySearch } from '$lib/components';
import { Button, InputSearch, InputSelect } from '$lib/elements/forms';
import { timeFromNow } from '$lib/helpers/date';
@@ -9,10 +8,11 @@
import { repositories } from '$routes/(console)/project-[project]/functions/function-[function]/store';
import { installation, installations, repository } from '$lib/stores/vcs';
import { createEventDispatcher } from 'svelte';
import { Layout, Table, Typography, Icon, Avatar } from '@appwrite.io/pink-svelte';
import { Layout, Table, Typography, Icon, Avatar, Skeleton } from '@appwrite.io/pink-svelte';
import { IconLockClosed } from '@appwrite.io/pink-icons-svelte';
import ConnectGit from './connectGit.svelte';
import Link from '$lib/elements/link.svelte';
import SvgIcon from '../svgIcon.svelte';
import { getFrameworkIcon } from '$routes/(console)/project-[project]/sites/store';
const dispatch = createEventDispatcher();
@@ -71,6 +71,12 @@
return $repositories.repositories.slice(0, 4);
}
async function detectFramework(repo) {
console.log(repo);
// TODO add code once backend is implemented
return '';
}
</script>
{#if hasInstallations}
@@ -110,17 +116,25 @@
<InputSearch placeholder="Search repositories" bind:value={search} />
</Layout.Stack>
{/await}
<p>
Manage organization configuration in your <Link
href={`${base}/project-${$page.params.project}/settings`}>project settings</Link
>.
</p>
</Layout.Stack>
{#if selectedInstallation}
{#await loadRepositories(selectedInstallation, search)}
<Layout.Stack justifyContent="center" alignContent="center" alignItems="center">
<div class="loader u-margin-32" />
</Layout.Stack>
<Table.Root>
{#each Array(4) as _}
<Table.Row>
<Table.Cell>
<Layout.Stack direction="row" alignItems="center">
<Skeleton variant="circle" width={24} />
<Layout.Stack gap="s" direction="row" alignItems="center">
<Skeleton variant="line" width={200} height={20} />
</Layout.Stack>
<Skeleton variant="line" width={76} height={32} />
</Layout.Stack>
</Table.Cell>
</Table.Row>
{/each}
</Table.Root>
{:then response}
{#if response?.length}
<Table.Root>
@@ -138,7 +152,16 @@
value={repo.id} />
{/if}
{#if product === 'sites'}
<Avatar size="xs" alt={repo.name} />
{#await detectFramework(repo)}
<Avatar size="xs" alt={repo.name} empty />
{:then framework}
<Avatar
size="xs"
alt={repo.name}
empty={!framework}>
<SvgIcon name={getFrameworkIcon(framework)} />
</Avatar>
{/await}
{:else}
<Avatar
size="xs"
+19 -20
View File
@@ -1,13 +1,12 @@
<script lang="ts">
import { Button, InputChoice } from '$lib/elements/forms';
import { DropList } from '.';
import { Button, InputCheckbox, InputChoice } from '$lib/elements/forms';
import { page } from '$app/stores';
import type { Writable } from 'svelte/store';
import { preferences } from '$lib/stores/preferences';
import { onMount } from 'svelte';
import { View } from '$lib/helpers/load';
import type { Column } from '$lib/helpers/types';
import { Tooltip } from '@appwrite.io/pink-svelte';
import { ActionMenu, Layout, Popover, Tooltip } from '@appwrite.io/pink-svelte';
export let columns: Writable<Column[]>;
export let view: View;
@@ -75,12 +74,8 @@
{#if !hideColumns && view === View.Table}
{#if $columns?.length}
<DropList bind:show={showSelectColumns} scrollable wrapperFullWidth={fullWidthMobile}>
<Button
size="s"
secondary
on:click={() => (showSelectColumns = !showSelectColumns)}
{fullWidthMobile}>
<Popover let:toggle>
<Button size="s" secondary on:click={toggle} {fullWidthMobile}>
<span
class="icon-view-boards u-opacity-50"
aria-hidden="true"
@@ -90,18 +85,22 @@
{/if}
<span class="inline-tag">{selectedColumnsNumber}</span>
</Button>
<svelte:fragment slot="list">
{#each $columns as column}
<InputChoice
id={column.id}
label={column.title}
bind:value={column.show}
disabled={allowNoColumns
? false
: selectedColumnsNumber <= 1 && column.show} />
{/each}
<svelte:fragment slot="tooltip">
<ActionMenu.Root>
<Layout.Stack>
{#each $columns as column}
<InputCheckbox
id={column.id}
label={column.title}
bind:checked={column.show}
disabled={allowNoColumns
? false
: selectedColumnsNumber <= 1 && column.show} />
{/each}
</Layout.Stack>
</ActionMenu.Root>
</svelte:fragment>
</DropList>
</Popover>
{/if}
{/if}
@@ -15,6 +15,7 @@
leadingIcon?: ComponentType;
leadingHtml?: string;
}[];
export let isSearchable = false;
let element: HTMLSelectElement;
let error: string;
@@ -50,6 +51,7 @@
{options}
{placeholder}
{disabled}
{isSearchable}
helper={error}
state={error ? 'error' : 'default'}
on:invalid={handleInvalid}
@@ -24,7 +24,7 @@
<Typography.Caption variant="400">Framework</Typography.Caption>
<Layout.Stack gap="xs" alignItems="center" direction="row">
{#if frameworkIcon}
<SvgIcon name={frameworkIcon} />
<SvgIcon iconSize="small" size={20} name={frameworkIcon} />
{/if}
<Typography.Text variant="m-500" color="--color-fgcolor-neutral-primary">
{framework.name}
@@ -1,5 +1,5 @@
<script lang="ts">
import { Button, InputChoice, InputSelectSearch, InputText } from '$lib/elements/forms';
import { Button, InputChoice, InputSelect, InputText } from '$lib/elements/forms';
import { Fieldset, Layout } from '@appwrite.io/pink-svelte';
import SelectRootModal from '../(components)/selectRootModal.svelte';
@@ -13,19 +13,16 @@
<Fieldset legend="Branch">
<Layout.Stack gap="l">
<InputSelectSearch
required={true}
<InputSelect
required
id="branch"
label="Production branch"
placeholder="Select branch"
hideRequired
isSearchable
bind:value={branch}
bind:search={branch}
on:select={(event) => {
branch = event.detail.value;
}}
interactiveOutput
name="branch"
{options} />
<Layout.Stack direction="row" gap="s" alignItems="flex-end">
<InputText
@@ -9,7 +9,7 @@
import { Wizard } from '$lib/layout';
import { installation, repository } from '$lib/stores/vcs.js';
import type { Models } from '@appwrite.io/console';
import { Layout, Typography } from '@appwrite.io/pink-svelte';
import { Fieldset, Layout, Typography } from '@appwrite.io/pink-svelte';
export let data;
let hasInstallations = !!data?.installations?.total;
@@ -26,12 +26,15 @@
</script>
<Wizard title="Create site" href={`${base}/project-${$page.params.project}/sites/`}>
<Repositories
bind:hasInstallations
bind:selectedRepository
product="sites"
action="button"
on:connect={onConnect} />
<Fieldset legend="Git repository">
<Repositories
bind:hasInstallations
bind:selectedRepository
product="sites"
action="button"
on:connect={onConnect} />
</Fieldset>
<svelte:fragment slot="aside">
<Card>
<Layout.Stack gap="m">
@@ -137,9 +137,9 @@
<Details bind:name bind:id />
{#await loadBranches()}
<div class="u-flex u-gap-8 u-cross-center u-main-center">
<Layout.Stack justifyContent="center" alignItems="center">
<div class="loader u-margin-32" />
</div>
</Layout.Stack>
{:then branches}
{@const options =
branches
@@ -34,7 +34,7 @@
alt={site.name}
avatar>
<svelte:fragment slot="avatar">
<SvgIcon name={getFrameworkIcon(site.framework)} />
<SvgIcon name={getFrameworkIcon(site.framework)} iconSize="small" />
</svelte:fragment>
<Popover placement="bottom-end" let:toggle>
<Button
@@ -19,13 +19,16 @@
<Container>
<Layout.Stack gap="xxl">
<SiteCard deployment={data.deployment} proxyRuleList={data.proxyRuleList}>
<svelte:fragment slot="footer">
<Button external href={`${$protocol}${data.deployment.domain}`}>Visit</Button>
<!-- TODO: disable when disabled={data.hasProdReadyDeployments} -->
<Button secondary on:click={() => (showRollback = true)}>Instant rollback</Button>
</svelte:fragment>
</SiteCard>
{#if data.deployment}
<SiteCard deployment={data.deployment} proxyRuleList={data.proxyRuleList}>
<svelte:fragment slot="footer">
<Button external href={`${$protocol}${data.deployment.domain}`}>Visit</Button>
<!-- TODO: disable when disabled={data.hasProdReadyDeployments} -->
<Button secondary on:click={() => (showRollback = true)}
>Instant rollback</Button>
</svelte:fragment>
</SiteCard>
{/if}
<Divider />
@@ -7,8 +7,7 @@ export const load = async ({ params, depends, parent }) => {
depends(Dependencies.SITES_DOMAINS);
const { site, proxyRuleList } = await parent();
const [deployment, deploymentList, prodReadyDeployments] = await Promise.all([
sdk.forProject.sites.getDeployment(params.site, site.deploymentId),
const [deploymentList, prodReadyDeployments] = await Promise.all([
sdk.forProject.sites.listDeployments(params.site, [Query.limit(5), Query.orderDesc('')]),
sdk.forProject.sites.listDeployments(params.site, [
Query.limit(1)
@@ -18,8 +17,10 @@ export const load = async ({ params, depends, parent }) => {
]);
return {
site,
deployment,
deploymentList,
deployment: deploymentList?.total
? await sdk.forProject.sites.getDeployment(params.site, site.deploymentId)
: null,
proxyRuleList,
hasProdReadyDeployments: prodReadyDeployments?.deployments?.length > 0
};
@@ -1,25 +1,22 @@
<script lang="ts">
import { PaginationWithLimit, ViewSelector, EmptyFilter } from '$lib/components';
import { Button, InputSelect } from '$lib/elements/forms';
import { PaginationWithLimit, ViewSelector, EmptyFilter, Empty } from '$lib/components';
import { Button } from '$lib/elements/forms';
import { Container } from '$lib/layout';
import { SiteUsageRange, type Models } from '@appwrite.io/console';
import { type Models } from '@appwrite.io/console';
import { Filters } from '$lib/components/filters';
import { queries, tags } from '$lib/components/filters/store';
import { View } from '$lib/helpers/load';
import { Pill } from '$lib/elements';
import { onMount } from 'svelte';
import { ActionMenu, Layout, Popover, Typography } from '@appwrite.io/pink-svelte';
import { sdk } from '$lib/stores/sdk';
import { page } from '$app/stores';
import { toLocaleDate } from '$lib/helpers/date';
import { ActionMenu, Icon, Layout, Popover } from '@appwrite.io/pink-svelte';
import Table from './table.svelte';
import QuickFilters from './quickFilters.svelte';
import RedeployModal from '../../redeployModal.svelte';
import CreateGitDeploymentModal from './createGitDeploymentModal.svelte';
import UsageCard from './usageCard.svelte';
import ConnectRepoModal from '../../(components)/connectRepoModal.svelte';
import { columns } from './store';
import CreateManualDeploymentModal from './createManualDeploymentModal.svelte';
import DeploymentMetrics from './deploymentMetrics.svelte';
import { IconPlus } from '@appwrite.io/pink-icons-svelte';
export let data;
@@ -32,196 +29,129 @@
let showConnectManual = false;
let showMobileFilters = false;
let range = SiteUsageRange.ThirtyDays;
const rangeOptions = [
{ value: SiteUsageRange.TwentyFourHours, label: 'Last 24 hours' },
{ value: SiteUsageRange.ThirtyDays, label: 'Last 30 days' },
{ value: SiteUsageRange.NinetyDays, label: 'Last 90 days' }
];
const now = new Date();
let metrics = [
{
id: 'buildsTotal',
value: null,
description: 'Total build count'
},
{
id: 'buildsStorageTotal',
value: null,
description: 'Total build size'
},
{
id: 'buildsTimeTotal',
value: null,
description: 'Total build time'
},
{
id: 'avgTime',
value: null,
description: 'Average build time'
},
{
id: 'success',
value: null,
description: 'Successful deployment'
},
{
id: 'failed',
value: null,
description: 'Failed deployment'
}
];
onMount(async () => {
data?.query ? (showMobileFilters = true) : (showMobileFilters = false);
await fetchUsage();
});
async function fetchUsage() {
metrics.forEach((metric) => {
metric.value = null;
});
metrics = metrics;
try {
const usage = await sdk.forProject.sites.getSiteUsage($page.params.site, range);
metrics = metrics.map((metric) => {
metric.value = usage[metric.id];
return metric;
});
metrics = metrics;
console.log(usage);
} catch (error) {
console.log(error);
}
}
function clearAll() {
queries.clearAll();
queries.apply();
}
$: console.log(metrics);
</script>
<Container>
<Layout.Stack gap="xxl">
<Layout.Stack gap="xl">
<Layout.Stack direction="row" justifyContent="space-between" alignItems="center">
<Typography.Text variant="m-400" color="--color-fgcolor-neutral-tertiary">
Metrics for {range !== SiteUsageRange.TwentyFourHours
? `${toLocaleDate(
new Date(
now.getTime() -
parseInt(range.split('d')[0]) * 24 * 60 * 60 * 1000
).toString()
)} to`
: ''}
{toLocaleDate(now.toString())}
</Typography.Text>
<div>
<InputSelect
id="range"
bind:value={range}
options={rangeOptions}
on:change={fetchUsage} />
</div>
</Layout.Stack>
<Layout.Stack direction="row">
{#each metrics as metric}
<UsageCard description={metric.description} bind:value={metric.value} />
{/each}
</Layout.Stack>
</Layout.Stack>
{#if data.deploymentList.total}
<DeploymentMetrics deploymentList={data.deploymentList} />
{/if}
<Layout.Stack gap="l">
<Layout.Stack justifyContent="space-between" direction="row">
<Layout.Stack alignItems="center" direction="row" gap="s">
<QuickFilters {columns} />
<Filters query={data.query} {columns} let:disabled let:toggle singleCondition>
<Layout.Stack alignItems="center" direction="row" gap="xs">
<Button
text
on:click={toggle}
{disabled}
size="s"
ariaLabel="open filter">
<span class="icon-filter-line" />
<span class="text">More filters</span>
</Button>
{#if $tags?.length}
<!-- TODO: add vertical divider to pink 2 -->
<div
style="flex-basis:1px; background-color:hsl(var(--color-border)); width: 1px">
</div>
<Button text on:click={clearAll} size="s">Clear all</Button>
{/if}
</Layout.Stack>
</Filters>
{#if data.deploymentList.total}
<QuickFilters {columns} />
<Filters
query={data.query}
{columns}
let:disabled
let:toggle
singleCondition>
<Layout.Stack alignItems="center" direction="row" gap="xs">
<Button
text
on:click={toggle}
{disabled}
size="s"
ariaLabel="open filter">
<span class="icon-filter-line" />
<span class="text">More filters</span>
</Button>
{#if $tags?.length}
<!-- TODO: add vertical divider to pink 2 -->
<div
style="flex-basis:1px; background-color:hsl(var(--color-border)); width: 1px">
</div>
<Button text on:click={clearAll} size="s">Clear all</Button>
{/if}
</Layout.Stack>
</Filters>
{/if}
</Layout.Stack>
<ViewSelector view={View.Table} {columns} hideView allowNoColumns hideText />
<Popover let:toggle>
<Button size="s" on:click={toggle}>Create deployment</Button>
<svelte:fragment slot="tooltip">
<ActionMenu.Root>
<ActionMenu.Item.Button
badge="Recommended"
on:click={() => {
if (!hasInstallation) {
showConnectRepo = true;
} else {
showCreateDeployment = true;
}
}}>
Git
</ActionMenu.Item.Button>
<ActionMenu.Item.Button
on:click={() => {
showConnectCLI = true;
}}>
CLI
</ActionMenu.Item.Button>
<ActionMenu.Item.Button
on:click={() => {
showConnectManual = true;
}}>
Manual
</ActionMenu.Item.Button>
</ActionMenu.Root>
</svelte:fragment>
</Popover>
</Layout.Stack>
<div class="is-only-mobile">
<Layout.Stack justifyContent="space-between" direction="row">
<Button
text
size="s"
on:click={() => (showMobileFilters = !showMobileFilters)}
ariaLabel="toggle filters">
<span class="icon-filter-line" />
<span class="text">Filters</span>
</Button>
<ViewSelector view={View.Table} {columns} hideView allowNoColumns />
</Layout.Stack>
<div
class:u-hide={!showMobileFilters}
class:u-flex={showMobileFilters}
class=" u-gap-8 u-flex-wrap u-margin-block-start-16">
<QuickFilters {columns} />
<Filters query={data.query} {columns} clearOnClick>
<svelte:fragment slot="mobile" let:disabled let:toggle>
<Pill
button
on:click={toggle}
{disabled}
class="u-flex u-gap-4 u-cross-center"
style="--p-tag-content-height: auto">
<span class="text">More filters</span>
<span class="icon-cheveron-down" />
</Pill>
<Layout.Stack direction="row" inline>
{#if data.deploymentList.total}
<ViewSelector
view={View.Table}
{columns}
hideView
allowNoColumns
hideText />
{/if}
<Popover let:toggle>
<Button size="s" on:click={toggle}>
<Icon size="s" icon={IconPlus} />
Create deployment
</Button>
<svelte:fragment slot="tooltip">
<ActionMenu.Root>
<ActionMenu.Item.Button
badge="Recommended"
on:click={() => {
if (!hasInstallation) {
showConnectRepo = true;
} else {
showCreateDeployment = true;
}
}}>
Git
</ActionMenu.Item.Button>
<ActionMenu.Item.Button
on:click={() => {
showConnectCLI = true;
}}>
CLI
</ActionMenu.Item.Button>
<ActionMenu.Item.Button
on:click={() => {
showConnectManual = true;
}}>
Manual
</ActionMenu.Item.Button>
</ActionMenu.Root>
</svelte:fragment>
</Filters>
</Popover>
</Layout.Stack>
</Layout.Stack>
{#if data.deploymentList.total}
<div class="is-only-mobile">
<Layout.Stack justifyContent="space-between" direction="row">
<Button
text
size="s"
on:click={() => (showMobileFilters = !showMobileFilters)}
ariaLabel="toggle filters">
<span class="icon-filter-line" />
<span class="text">Filters</span>
</Button>
<ViewSelector view={View.Table} {columns} hideView allowNoColumns />
</Layout.Stack>
<div
class:u-hide={!showMobileFilters}
class:u-flex={showMobileFilters}
class=" u-gap-8 u-flex-wrap u-margin-block-start-16">
<QuickFilters {columns} />
<Filters query={data.query} {columns} clearOnClick>
<svelte:fragment slot="mobile" let:disabled let:toggle>
<Pill
button
on:click={toggle}
{disabled}
class="u-flex u-gap-4 u-cross-center"
style="--p-tag-content-height: auto">
<span class="text">More filters</span>
<span class="icon-cheveron-down" />
</Pill>
</svelte:fragment>
</Filters>
</div>
</div>
</div>
{/if}
{#if data.deploymentList.total}
<Table {data} />
@@ -232,6 +162,17 @@
total={data.deploymentList?.total} />
{:else if data?.query}
<EmptyFilter resource="deployments" />
{:else}
<Empty
single
target="deployment"
on:click={() => {
if (!hasInstallation) {
showConnectRepo = true;
} else {
showCreateDeployment = true;
}
}} />
{/if}
</Layout.Stack>
</Layout.Stack>
@@ -5,7 +5,7 @@ import { Dependencies, PAGE_LIMIT } from '$lib/constants';
import { queries, queryParamToMap } from '$lib/components/filters';
export const load = async ({ params, depends, url, route, parent }) => {
const data = await parent();
const { site } = await parent();
depends(Dependencies.DEPLOYMENTS);
const page = getPage(url);
const limit = getLimit(url, route, PAGE_LIMIT);
@@ -29,10 +29,11 @@ export const load = async ({ params, depends, url, route, parent }) => {
offset,
limit,
query,
activeDeployment: data.site.deploymentId
? await sdk.forProject.sites.getDeployment(params.site, data.site.deploymentId)
: null,
deploymentList,
activeDeployment:
site.deploymentId && deploymentList?.total
? await sdk.forProject.sites.getDeployment(params.site, site.deploymentId)
: null,
installations
};
};
@@ -143,6 +143,7 @@
<Repositories
bind:selectedRepository
installationList={installations}
product="sites"
action="button"
callbackState={{
createDeployment: 'true'
@@ -0,0 +1,103 @@
<script lang="ts">
import { InputSelect } from '$lib/elements/forms';
import { toLocaleDate } from '$lib/helpers/date';
import { Layout, Typography } from '@appwrite.io/pink-svelte';
import UsageCard from './usageCard.svelte';
import { sdk } from '$lib/stores/sdk';
import { SiteUsageRange, type Models } from '@appwrite.io/console';
import { onMount } from 'svelte';
import { page } from '$app/stores';
export let deploymentList: Models.DeploymentList;
let range = SiteUsageRange.ThirtyDays;
const rangeOptions = [
{ value: SiteUsageRange.TwentyFourHours, label: 'Last 24 hours' },
{ value: SiteUsageRange.ThirtyDays, label: 'Last 30 days' },
{ value: SiteUsageRange.NinetyDays, label: 'Last 90 days' }
];
const now = new Date();
let metrics = [
{
id: 'buildsTotal',
value: null,
description: 'Total build count'
},
{
id: 'buildsStorageTotal',
value: null,
description: 'Total build size'
},
{
id: 'buildsTimeTotal',
value: null,
description: 'Total build time'
},
{
id: 'avgTime',
value: null,
description: 'Average build time'
},
{
id: 'success',
value: null,
description: 'Successful deployment'
},
{
id: 'failed',
value: null,
description: 'Failed deployment'
}
];
onMount(async () => {
if (deploymentList.total) {
await fetchUsage();
}
});
async function fetchUsage() {
metrics.forEach((metric) => {
metric.value = null;
});
metrics = metrics;
try {
const usage = await sdk.forProject.sites.getSiteUsage($page.params.site, range);
metrics = metrics.map((metric) => {
metric.value = usage[metric.id];
return metric;
});
metrics = metrics;
console.log(usage);
} catch (error) {
console.log(error);
}
}
$: console.log(metrics);
</script>
<Layout.Stack gap="xl">
<Layout.Stack direction="row" justifyContent="space-between" alignItems="center">
<Typography.Text variant="m-400" color="--color-fgcolor-neutral-tertiary">
Metrics for {range !== SiteUsageRange.TwentyFourHours
? `${toLocaleDate(
new Date(
now.getTime() - parseInt(range.split('d')[0]) * 24 * 60 * 60 * 1000
).toString()
)} to`
: ''}
{toLocaleDate(now.toString())}
</Typography.Text>
<div>
<InputSelect
id="range"
bind:value={range}
options={rangeOptions}
on:change={fetchUsage} />
</div>
</Layout.Stack>
<Layout.Stack direction="row">
{#each metrics as metric}
<UsageCard description={metric.description} bind:value={metric.value} />
{/each}
</Layout.Stack>
</Layout.Stack>
@@ -19,6 +19,7 @@
import { IconDotsHorizontal, IconGlobeAlt, IconRefresh } from '@appwrite.io/pink-icons-svelte';
import RedeployModal from '../redeployModal.svelte';
import PromoteModal from '../promoteModal.svelte';
import { Card } from '$lib/components';
export let site: Models.Site;
export let activeDeployment: Models.Deployment;
@@ -27,100 +28,108 @@
let showPromote = false;
let showRedeploy = false;
let selectedDeployment: Models.Deployment = null;
$: console.log(deploymentList);
</script>
<Layout.Stack>
<Layout.Stack direction="row" justifyContent="space-between" alignItems="center">
<Layout.Stack gap="xs" direction="row" alignItems="center" inline>
<Typography.Text variant="l-500">Deployments</Typography.Text><Badge
variant="secondary"
content={deploymentList?.total.toString()}
size="s" />
{#if deploymentList.total}
<Layout.Stack>
<Layout.Stack direction="row" justifyContent="space-between" alignItems="center">
<Layout.Stack gap="xs" direction="row" alignItems="center" inline>
<Typography.Text variant="l-500">Deployments</Typography.Text><Badge
variant="secondary"
content={deploymentList?.total.toString()}
size="s" />
</Layout.Stack>
<Button
secondary
href={`${base}/project-${$page.params.project}/sites/site-${$page.params.site}/deployments`}>
View all
</Button>
</Layout.Stack>
<Button
secondary
href={`${base}/project-${$page.params.project}/sites/site-${$page.params.site}/deployments`}>
View all
</Button>
</Layout.Stack>
{#if deploymentList?.total}
<Table.Root>
<svelte:fragment slot="header">
<Table.Header.Cell>Deployment ID</Table.Header.Cell>
<Table.Header.Cell>Status</Table.Header.Cell>
<Table.Header.Cell>Source</Table.Header.Cell>
<Table.Header.Cell>Updated</Table.Header.Cell>
<Table.Header.Cell />
</svelte:fragment>
{#each deploymentList?.deployments as deployment}
<Table.Link
href={`${base}/project-${$page.params.project}/sites/site-${$page.params.site}/deployments/deployment-${deployment.$id}`}>
<Table.Cell>
<Id value={deployment.$id}>{deployment.$id}</Id>
</Table.Cell>
<Table.Cell>
{@const status = deployment.status}
{#if activeDeployment?.$id === deployment?.$id}
<Status status="complete" label="active" />
{:else}
<Status
status={status === 'failed'
? status
: status === 'building'
? 'pending'
: 'ready'}
label={status} />
{/if}
</Table.Cell>
{#if deploymentList?.total}
<Table.Root>
<svelte:fragment slot="header">
<Table.Header.Cell>Deployment ID</Table.Header.Cell>
<Table.Header.Cell>Status</Table.Header.Cell>
<Table.Header.Cell>Source</Table.Header.Cell>
<Table.Header.Cell>Updated</Table.Header.Cell>
<Table.Header.Cell />
</svelte:fragment>
{#each deploymentList?.deployments as deployment}
<Table.Link
href={`${base}/project-${$page.params.project}/sites/site-${$page.params.site}/deployments/deployment-${deployment.$id}`}>
<Table.Cell>
<Id value={deployment.$id}>{deployment.$id}</Id>
</Table.Cell>
<Table.Cell>
{@const status = deployment.status}
{#if activeDeployment?.$id === deployment?.$id}
<Status status="complete" label="active" />
{:else}
<Status
status={status === 'failed'
? status
: status === 'building'
? 'pending'
: 'ready'}
label={status} />
{/if}
</Table.Cell>
<Table.Cell>
<DeploymentSource {deployment} />
</Table.Cell>
<Table.Cell>
<DeploymentCreatedBy {deployment} />
</Table.Cell>
<Table.Cell>
<Popover placement="bottom-end" let:toggle>
<Button
text
icon
size="s"
on:click={(e) => {
e.preventDefault();
toggle(e);
}}>
<Icon size="s" icon={IconDotsHorizontal} /></Button>
<svelte:fragment slot="tooltip" let:toggle>
<ActionMenu.Root>
<ActionMenu.Item.Button
leadingIcon={IconRefresh}
on:click={(e) => {
e.preventDefault();
selectedDeployment = deployment;
showRedeploy = true;
toggle(e);
}}>
Redeploy
</ActionMenu.Item.Button>
<ActionMenu.Item.Button
leadingIcon={IconGlobeAlt}
on:click={(e) => {
e.preventDefault();
selectedDeployment = deployment;
showPromote = true;
toggle(e);
}}>
Promote
</ActionMenu.Item.Button>
</ActionMenu.Root>
</svelte:fragment>
</Popover>
</Table.Cell>
</Table.Link>
{/each}
</Table.Root>
{/if}
</Layout.Stack>
<Table.Cell>
<DeploymentSource {deployment} />
</Table.Cell>
<Table.Cell>
<DeploymentCreatedBy {deployment} />
</Table.Cell>
<Table.Cell>
<Popover placement="bottom-end" let:toggle>
<Button
text
icon
size="s"
on:click={(e) => {
e.preventDefault();
toggle(e);
}}>
<Icon size="s" icon={IconDotsHorizontal} /></Button>
<svelte:fragment slot="tooltip" let:toggle>
<ActionMenu.Root>
<ActionMenu.Item.Button
leadingIcon={IconRefresh}
on:click={(e) => {
e.preventDefault();
selectedDeployment = deployment;
showRedeploy = true;
toggle(e);
}}>
Redeploy
</ActionMenu.Item.Button>
<ActionMenu.Item.Button
leadingIcon={IconGlobeAlt}
on:click={(e) => {
e.preventDefault();
selectedDeployment = deployment;
showPromote = true;
toggle(e);
}}>
Promote
</ActionMenu.Item.Button>
</ActionMenu.Root>
</svelte:fragment>
</Popover>
</Table.Cell>
</Table.Link>
{/each}
</Table.Root>
{/if}
</Layout.Stack>
{:else}
<Card>
<Typography.Text>No deployments exist</Typography.Text>
</Card>
{/if}
{#if selectedDeployment && showRedeploy}
<RedeployModal {site} selectedDeploymentId={selectedDeployment.$id} bind:show={showRedeploy} />
@@ -37,6 +37,12 @@
let selectedDomain: Models.ProxyRule = null;
$: console.log(data.domains);
// TODO: replace status once backend is fixed
// created - Verification Failed
// verified - Verification OK , Certificate OK
// verifying - Verifiacitok OK
// unverified - Verificaiton OK , Certificate Failed
</script>
<Container>
@@ -69,7 +75,12 @@
{domain.domain}
<Icon icon={IconExternalLink} size="s" />
</Link>
{#if domain.status !== 'verified'}
{#if domain.status === 'created'}
<Badge
variant="secondary"
content="Verification failed"
type="error" />
{:else if domain.status === 'veryfing'}
<Badge
variant="secondary"
content="Pending verification"
@@ -16,7 +16,8 @@
Divider,
Fieldset,
Layout,
Typography
Typography,
Spinner
} from '@appwrite.io/pink-svelte';
import RecordsCard from '../recordsCard.svelte';
import { invalidate } from '$app/navigation';
@@ -97,11 +98,24 @@
<Button secondary on:click={verifyStatus}>Verify</Button>
</Layout.Stack>
</RecordsCard>
{:else if domainData.status === 'verifying'}
<Card radius="s">
<Layout.Stack gap="s" direction="row" alignItems="center">
<Typography.Text variant="l-500">{domainData.domain}</Typography.Text>
<Badge variant="secondary" type="success" content="Verified" />
<Badge variant="secondary" content="Generating certificate...">
<svelte:fragment slot="start">
<Spinner size="s" />
</svelte:fragment>
</Badge>
</Layout.Stack>
</Card>
{:else if domainData.status === 'verified'}
<Card radius="s">
<Layout.Stack gap="s" direction="row" alignItems="center">
<Typography.Text variant="l-500">{domainData.domain}</Typography.Text>
<Badge variant="secondary" type="success" content="Verified" />
<Badge variant="secondary" type="success" content="Generated certificate" />
</Layout.Stack>
</Card>
{/if}
@@ -121,9 +135,8 @@
</Layout.Stack>
</Form>
<Alert.Inline title="Domain providers and DNS settings" hideActions>
A list of all domain providers and their DNS setting is available <Link
size="s"
href="#">here</Link
A list of all domain providers and their DNS setting is available <Link href="#"
>here</Link
>.
</Alert.Inline>
</Layout.Stack>
@@ -133,9 +146,8 @@
<svelte:fragment slot="aside">
<Card>
<Layout.Stack direction="column">
<!-- TODO: Update the Accordion component to conditionally render a divider, as it currently includes one by default for every instance -->
{#each siteDomainTips as tips}
<Accordion title={tips.title}>
{#each siteDomainTips as tips, i}
<Accordion title={tips.title} hideDivider={i === siteDomainTips.length - 1}>
{tips.message}
</Accordion>
{/each}
@@ -12,6 +12,7 @@
export let show = false;
export let selectedDomain: Models.ProxyRule;
let error = null;
async function retryDomain() {
try {
await sdk.forProject.proxy.updateRuleVerification(selectedDomain.$id);
@@ -22,17 +23,18 @@
message: `${selectedDomain.domain} has been deleted`
});
trackEvent(Submit.DomainDelete);
} catch (error) {
addNotification({
type: 'error',
message: error.message
});
trackError(error, Submit.DomainDelete);
} catch (e) {
error = e;
trackError(e, Submit.DomainDelete);
}
}
$: if (!show) {
error = null;
}
</script>
<Modal title="Retry verification" bind:show onSubmit={retryDomain}>
<Modal title="Retry verification" bind:show onSubmit={retryDomain} bind:error>
{#if selectedDomain}
<RecordsCard domain={selectedDomain} />
{/if}
@@ -5,7 +5,7 @@
import OpenOnMobileModal from '../(components)/openOnMobileModal.svelte';
import { timeFromNow } from '$lib/helpers/date';
import { IconExternalLink, IconQrcode } from '@appwrite.io/pink-icons-svelte';
import { protocol } from '$routes/(console)/store';
import { consoleVariables, protocol } from '$routes/(console)/store';
import { type Models } from '@appwrite.io/console';
import { Link } from '$lib/elements';
import { Button } from '$lib/elements/forms';
@@ -15,6 +15,12 @@
let showDomainQR = false;
let selectedDomainURL: string;
$: hasCustomDomain = proxyRuleList?.total
? proxyRuleList.rules.some((rule) =>
rule.domain.includes($consoleVariables._APP_DOMAIN_TARGET)
)
: false;
</script>
<Layout.Stack>
@@ -32,8 +32,10 @@ export function getFrameworkIcon(framework: string) {
return 'nextjs';
case framework.toLocaleLowerCase().includes('astro'):
return 'astro';
case framework.toLocaleLowerCase().includes('remix'):
return 'remix';
default:
return undefined;
return framework.toLocaleLowerCase();
}
}
+2 -2
View File
@@ -1,4 +1,4 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7.57212 16.0036C6.72926 15.2433 6.48322 13.6458 6.83438 12.4884C7.44325 13.2181 8.2869 13.4493 9.16075 13.5797C10.5098 13.7811 11.8347 13.7058 13.0879 13.0973C13.2312 13.0277 13.3637 12.935 13.5204 12.8412C13.638 13.1779 13.6686 13.5177 13.6275 13.8636C13.5276 14.7061 13.1028 15.3569 12.427 15.8502C12.1568 16.0475 11.8709 16.2239 11.5919 16.4099C10.7345 16.9817 10.5025 17.6522 10.8247 18.6276C10.8324 18.6513 10.8392 18.6751 10.8565 18.7331C10.4188 18.5398 10.099 18.2583 9.85541 17.8882C9.59809 17.4976 9.47566 17.0655 9.46922 16.598C9.466 16.3704 9.466 16.1409 9.43499 15.9166C9.35929 15.3697 9.09914 15.1249 8.60907 15.1108C8.1061 15.0963 7.70823 15.4032 7.60272 15.8864C7.59467 15.9235 7.58299 15.9601 7.57132 16.0032L7.57212 16.0036Z" fill="#17191E"/>
<path d="M3.33331 12.8428C3.33331 12.8428 5.63796 11.7229 7.94905 11.7229L9.69153 6.34361C9.75675 6.08346 9.94725 5.90668 10.1623 5.90668C10.3773 5.90668 10.5678 6.08346 10.633 6.34361L12.3755 11.7229C15.1127 11.7229 16.9912 12.8428 16.9912 12.8428C16.9912 12.8428 13.0766 2.20506 13.0689 2.18372C12.9566 1.86921 12.7669 1.66666 12.5112 1.66666H7.81374C7.55803 1.66666 7.37601 1.86921 7.25599 2.18372C7.24753 2.20466 3.33331 12.8428 3.33331 12.8428Z" fill="#17191E"/>
<path d="M7.57218 16.0036C6.72932 15.2433 6.48328 13.6458 6.83444 12.4884C7.44331 13.2181 8.28696 13.4493 9.16081 13.5798C10.5099 13.7811 11.8347 13.7058 13.0879 13.0973C13.2313 13.0277 13.3638 12.935 13.5204 12.8412C13.638 13.1779 13.6686 13.5177 13.6276 13.8637C13.5277 14.7061 13.1028 15.3569 12.4271 15.8502C12.1569 16.0475 11.871 16.2239 11.5919 16.4099C10.7346 16.9818 10.5026 17.6523 10.8248 18.6276C10.8324 18.6513 10.8393 18.6751 10.8566 18.7331C10.4189 18.5398 10.0991 18.2583 9.85547 17.8882C9.59815 17.4976 9.47572 17.0655 9.46928 16.598C9.46606 16.3705 9.46606 16.1409 9.43505 15.9166C9.35936 15.3698 9.0992 15.1249 8.60913 15.1108C8.10616 15.0963 7.70829 15.4032 7.60278 15.8864C7.59473 15.9235 7.58306 15.9601 7.57138 16.0032L7.57218 16.0036Z" fill="white"/>
<path d="M3.33337 12.8428C3.33337 12.8428 5.63802 11.7229 7.94911 11.7229L9.69159 6.34362C9.75681 6.08347 9.94731 5.90669 10.1623 5.90669C10.3774 5.90669 10.5679 6.08347 10.6331 6.34362L12.3756 11.7229C15.1127 11.7229 16.9913 12.8428 16.9913 12.8428C16.9913 12.8428 13.0767 2.20508 13.069 2.18374C12.9567 1.86923 12.767 1.66667 12.5113 1.66667H7.8138C7.55809 1.66667 7.37607 1.86923 7.25605 2.18374C7.24759 2.20468 3.33337 12.8428 3.33337 12.8428Z" fill="white"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

+1 -1
View File
@@ -1,3 +1,3 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9.45424 1.67114C9.4184 1.6744 9.30435 1.6858 9.20171 1.69395C6.83449 1.90734 4.61716 3.18444 3.21279 5.14733C2.43078 6.23873 1.93062 7.47674 1.74163 8.78805C1.67483 9.24579 1.66669 9.38099 1.66669 10.0016C1.66669 10.6223 1.67483 10.7575 1.74163 11.2152C2.19455 14.3444 4.42166 16.9736 7.44218 17.9477C7.98308 18.122 8.55329 18.2409 9.20171 18.3126C9.45424 18.3402 10.5458 18.3402 10.7983 18.3126C11.9176 18.1888 12.8658 17.9118 13.8009 17.4345C13.9443 17.3612 13.972 17.3417 13.9524 17.3254C13.9394 17.3156 13.3285 16.4963 12.5953 15.5059L11.2626 13.7059L9.59272 11.2347C8.67386 9.87619 7.91791 8.76524 7.91139 8.76524C7.90488 8.76361 7.89836 9.86153 7.8951 11.2022C7.89021 13.5495 7.88858 13.644 7.85926 13.6994C7.8169 13.7792 7.78431 13.8117 7.71589 13.8476C7.66375 13.8736 7.61814 13.8785 7.37213 13.8785H7.09028L7.01533 13.8313C6.96646 13.8003 6.93062 13.7596 6.90618 13.7124L6.87197 13.6391L6.87522 10.373L6.88011 7.10534L6.93062 7.04181C6.95668 7.0076 7.01208 6.96362 7.05118 6.94244C7.11797 6.90986 7.14404 6.90661 7.42589 6.90661C7.75825 6.90661 7.81364 6.91964 7.89999 7.01412C7.92443 7.04018 8.82863 8.40199 9.91041 10.0423C10.9922 11.6827 12.4715 13.9225 13.1981 15.0221L14.5178 17.0208L14.5846 16.9768C15.176 16.5924 15.8016 16.045 16.2969 15.4749C17.3509 14.2646 18.0303 12.7888 18.2584 11.2152C18.3252 10.7575 18.3334 10.6223 18.3334 10.0016C18.3334 9.38099 18.3252 9.24579 18.2584 8.78805C17.8055 5.65883 15.5784 3.02969 12.5579 2.05558C12.0251 1.88291 11.4581 1.76399 10.8228 1.69232C10.6664 1.67603 9.58946 1.65811 9.45424 1.67114ZM12.8658 6.71113C12.944 6.75023 13.0075 6.82516 13.0303 6.90335C13.0434 6.9457 13.0466 7.8514 13.0434 9.89248L13.0385 12.8213L12.522 12.0297L12.0039 11.238V9.10895C12.0039 7.73249 12.0104 6.95873 12.0202 6.92127C12.0463 6.83004 12.1033 6.75837 12.1815 6.71602C12.2483 6.68181 12.2727 6.67855 12.5285 6.67855C12.7697 6.67855 12.812 6.68181 12.8658 6.71113Z" fill="black"/>
<path d="M9.45418 1.67116C9.41834 1.67441 9.30429 1.68582 9.20165 1.69396C6.83443 1.90736 4.6171 3.18446 3.21273 5.14735C2.43072 6.23875 1.93056 7.47676 1.74157 8.78806C1.67477 9.2458 1.66663 9.381 1.66663 10.0016C1.66663 10.6223 1.67477 10.7575 1.74157 11.2152C2.19449 14.3444 4.42159 16.9736 7.44212 17.9477C7.98302 18.122 8.55323 18.2409 9.20165 18.3126C9.45418 18.3403 10.5457 18.3403 10.7983 18.3126C11.9175 18.1888 12.8657 17.9118 13.8009 17.4346C13.9442 17.3613 13.9719 17.3417 13.9524 17.3254C13.9394 17.3156 13.3284 16.4963 12.5953 15.5059L11.2626 13.7059L9.59266 11.2348C8.67379 9.87621 7.91785 8.76526 7.91133 8.76526C7.90481 8.76363 7.8983 9.86155 7.89504 11.2022C7.89015 13.5495 7.88852 13.644 7.8592 13.6994C7.81684 13.7792 7.78425 13.8118 7.71583 13.8476C7.66369 13.8737 7.61808 13.8786 7.37207 13.8786H7.09022L7.01527 13.8313C6.9664 13.8004 6.93056 13.7596 6.90612 13.7124L6.8719 13.6391L6.87516 10.373L6.88005 7.10535L6.93056 7.04182C6.95662 7.00762 7.01201 6.96363 7.05112 6.94246C7.11791 6.90988 7.14398 6.90662 7.42583 6.90662C7.75819 6.90662 7.81358 6.91965 7.89993 7.01413C7.92436 7.04019 8.82857 8.402 9.91035 10.0424C10.9921 11.6827 12.4714 13.9225 13.1981 15.0221L14.5177 17.0208L14.5845 16.9768C15.1759 16.5924 15.8015 16.0451 16.2968 15.4749C17.3509 14.2646 18.0303 12.7888 18.2583 11.2152C18.3251 10.7575 18.3333 10.6223 18.3333 10.0016C18.3333 9.381 18.3251 9.2458 18.2583 8.78806C17.8054 5.65884 15.5783 3.02971 12.5578 2.05559C12.025 1.88292 11.4581 1.76401 10.8227 1.69233C10.6663 1.67604 9.5894 1.65813 9.45418 1.67116ZM12.8657 6.71115C12.9439 6.75024 13.0075 6.82517 13.0303 6.90336C13.0433 6.94572 13.0466 7.85141 13.0433 9.8925L13.0384 12.8214L12.522 12.0297L12.0039 11.238V9.10897C12.0039 7.7325 12.0104 6.95875 12.0202 6.92128C12.0462 6.83006 12.1032 6.75839 12.1815 6.71603C12.2482 6.68182 12.2727 6.67857 12.5285 6.67857C12.7696 6.67857 12.8119 6.68182 12.8657 6.71115Z" fill="white"/>
</svg>

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

+3
View File
@@ -0,0 +1,3 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10.5876 3C13.8692 3 15.5 4.71409 15.5 7.45218C15.5 9.50017 14.3524 10.8358 12.8022 11.0584C14.1108 11.3478 14.8758 12.1715 15.0168 13.7965L15.0372 14.0953L15.055 14.3747L15.0704 14.636L15.0811 14.8331L15.0926 15.0656L15.0985 15.1983L15.107 15.4091L15.1138 15.6082L15.1172 15.7226L15.1218 15.906L15.1258 16.1156L15.1272 16.2169L15.1298 16.5105L15.1306 16.8168L15.1307 17H11.7465L11.7467 16.9236L11.7479 16.7752L11.7499 16.6307L11.7558 16.2806L11.7572 16.1633L11.7584 15.9792L11.7582 15.8615L11.7573 15.7381L11.7559 15.6413L11.7531 15.5057L11.7491 15.3618L11.7437 15.2089L11.7368 15.046L11.7327 14.9605L11.7258 14.827L11.7178 14.6866L11.7055 14.4883L11.6949 14.3307C11.6918 14.2821 11.6883 14.2347 11.6843 14.1883L11.6756 14.0972C11.5639 13.0207 11.1748 12.5807 10.5084 12.434L10.4523 12.4224C10.4144 12.4152 10.3757 12.4088 10.3361 12.4032L10.2761 12.3955C10.2659 12.3943 10.2558 12.3931 10.2456 12.392L10.1836 12.3861L10.1204 12.3812L10.0559 12.3774L9.99079 12.3746L9.92444 12.3728L9.85685 12.3719L4.5 12.3718V9.45568H9.98364C10.0733 9.45568 10.1602 9.45381 10.2443 9.45L10.3275 9.44555L10.4088 9.43978L10.4883 9.43269C10.5014 9.43139 10.5145 9.43004 10.5274 9.42864L10.6041 9.41952C10.6546 9.41299 10.7039 9.40555 10.752 9.39717L10.8232 9.3839C11.713 9.20685 12.1579 8.68846 12.1579 7.67686C12.1579 6.54159 11.4332 5.85355 9.98364 5.85355H4.5V3H10.5876ZM8.07828 14.8261C8.52228 14.8261 8.70805 15.0966 8.77423 15.3556L8.78296 15.3928L8.79013 15.4297L8.79479 15.4588L8.79679 15.4732L8.80013 15.5017L8.80264 15.5295L8.80361 15.5432L8.80497 15.57L8.80539 15.5831L8.80572 15.6086V17H4.5V14.8261H8.07828Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

+5
View File
@@ -0,0 +1,5 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Mode=color, Icon=remix">
<path id="Vector" d="M10.5876 3C13.8692 3 15.5 4.71409 15.5 7.45218C15.5 9.50017 14.3524 10.8358 12.8022 11.0584C14.1108 11.3478 14.8758 12.1715 15.0168 13.7965L15.0372 14.0953L15.055 14.3747L15.0704 14.636L15.0811 14.8331L15.0926 15.0656L15.0985 15.1983L15.107 15.4091L15.1138 15.6082L15.1172 15.7226L15.1218 15.906L15.1258 16.1156L15.1272 16.2169L15.1298 16.5105L15.1306 16.8168L15.1307 17H11.7465L11.7467 16.9236L11.7479 16.7752L11.7499 16.6307L11.7558 16.2806L11.7572 16.1633L11.7584 15.9792L11.7582 15.8615L11.7573 15.7381L11.7559 15.6413L11.7531 15.5057L11.7491 15.3618L11.7437 15.2089L11.7368 15.046L11.7327 14.9605L11.7258 14.827L11.7178 14.6866L11.7055 14.4883L11.6949 14.3307C11.6918 14.2821 11.6883 14.2347 11.6843 14.1883L11.6756 14.0972C11.5639 13.0207 11.1748 12.5807 10.5084 12.434L10.4523 12.4224C10.4144 12.4152 10.3757 12.4088 10.3361 12.4032L10.2761 12.3955C10.2659 12.3943 10.2558 12.3931 10.2456 12.392L10.1836 12.3861L10.1204 12.3812L10.0559 12.3774L9.99079 12.3746L9.92444 12.3728L9.85685 12.3719L4.5 12.3718V9.45568H9.98364C10.0733 9.45568 10.1602 9.45381 10.2443 9.45L10.3275 9.44555L10.4088 9.43978L10.4883 9.43269C10.5014 9.43139 10.5145 9.43004 10.5274 9.42864L10.6041 9.41952C10.6546 9.41299 10.7039 9.40555 10.752 9.39717L10.8232 9.3839C11.713 9.20685 12.1579 8.68846 12.1579 7.67686C12.1579 6.54159 11.4332 5.85355 9.98364 5.85355H4.5V3H10.5876ZM8.07828 14.8261C8.52228 14.8261 8.70805 15.0966 8.77423 15.3556L8.78296 15.3928L8.79013 15.4297L8.79479 15.4588L8.79679 15.4732L8.80013 15.5017L8.80264 15.5295L8.80361 15.5432L8.80497 15.57L8.80539 15.5831L8.80572 15.6086V17H4.5V14.8261H8.07828Z" fill="#19191C"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB