mirror of
https://github.com/appwrite/console.git
synced 2026-06-06 19:27:48 +00:00
Merge branch 'feat-pink-v2' into fix-onboarding-feedback
# Conflicts: # package.json # pnpm-lock.yaml
This commit is contained in:
@@ -340,5 +340,5 @@ export enum Submit {
|
||||
SiteConnectRepo = 'submit_site_connect_repo',
|
||||
SiteRedeploy = 'submit_site_redeploy',
|
||||
SiteDisconnectRepo = 'submit_site_disconnect_repo',
|
||||
SitePromoteDeployment = 'submit_site_promote_deployment'
|
||||
SiteActivateDeployment = 'submit_site_activate_deployment'
|
||||
}
|
||||
|
||||
@@ -22,7 +22,13 @@ export const timer: Action<HTMLElement, Props> = (node, props) => {
|
||||
|
||||
function step() {
|
||||
const diffInSeconds = Math.floor((new Date().getTime() - startDate.getTime()) / 1000);
|
||||
node.textContent = calculateTime(diffInSeconds);
|
||||
const minutes = Math.floor(diffInSeconds / 60);
|
||||
if (minutes > 0) {
|
||||
const seconds = diffInSeconds % 60;
|
||||
node.textContent = `${minutes}m ${seconds}s`;
|
||||
} else {
|
||||
node.textContent = calculateTime(diffInSeconds);
|
||||
}
|
||||
frame = window.requestAnimationFrame(step);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<script lang="ts">
|
||||
import { trackEvent } from '$lib/actions/analytics';
|
||||
import { Empty, Card } from '@appwrite.io/pink-svelte';
|
||||
import { Empty, Card, Layout, Avatar, Icon, Typography } from '@appwrite.io/pink-svelte';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import { IconPlus } from '@appwrite.io/pink-icons-svelte';
|
||||
|
||||
export let single = false;
|
||||
export let isCard = true;
|
||||
export let target: string = null;
|
||||
export let href: string = null;
|
||||
export let marginTop = false;
|
||||
@@ -48,23 +48,12 @@
|
||||
</Empty>
|
||||
</Card.Base>
|
||||
{:else}
|
||||
<button
|
||||
on:click|preventDefault
|
||||
on:click={track}
|
||||
aria-label="create"
|
||||
type="button"
|
||||
class:card={isCard}
|
||||
class="u-grid u-cross-center u-width-full-line dashed"
|
||||
class:common-section={marginTop}>
|
||||
<div class="u-flex u-cross-center u-flex-vertical u-main-center u-flex">
|
||||
<div class="common-section">
|
||||
<div class="button is-secondary is-only-icon">
|
||||
<i class="icon-plus" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="common-section">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
<Card.Button on:click on:click={track} aria-label="create">
|
||||
<Layout.Stack justifyContent="center" alignItems="center" gap="m">
|
||||
<Avatar>
|
||||
<Icon icon={IconPlus} />
|
||||
</Avatar>
|
||||
<Typography.Text variation="m-400"><slot /></Typography.Text>
|
||||
</Layout.Stack>
|
||||
</Card.Button>
|
||||
{/if}
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
export { classes as class };
|
||||
export let submissionLoader = false;
|
||||
export let forceShowLoader = false;
|
||||
export let compact = false;
|
||||
|
||||
const isSubmitting = hasContext('form')
|
||||
? getContext<FormContext>('form').isSubmitting
|
||||
@@ -45,6 +46,20 @@
|
||||
});
|
||||
}
|
||||
|
||||
function getVariant(): 'primary' | 'secondary' | 'text' | 'compact' {
|
||||
switch (true) {
|
||||
case secondary:
|
||||
return 'secondary';
|
||||
case text:
|
||||
return 'text';
|
||||
case compact:
|
||||
return 'compact';
|
||||
default:
|
||||
return 'primary';
|
||||
}
|
||||
}
|
||||
|
||||
$: variant = getVariant();
|
||||
$: resolvedClasses = [
|
||||
fullWidth && 'is-full-width',
|
||||
fullWidthMobile && 'is-full-width-mobile',
|
||||
@@ -64,7 +79,7 @@
|
||||
{size}
|
||||
{icon}
|
||||
disabled={internalDisabled}
|
||||
variant={secondary ? 'secondary' : text ? 'text' : 'primary'}
|
||||
{variant}
|
||||
target={external ? '_blank' : ''}
|
||||
rel={external ? 'noopener noreferrer' : ''}
|
||||
class={resolvedClasses}
|
||||
@@ -81,7 +96,7 @@
|
||||
{icon}
|
||||
{badge}
|
||||
disabled={internalDisabled}
|
||||
variant={secondary ? 'secondary' : text ? 'text' : 'primary'}
|
||||
{variant}
|
||||
class={resolvedClasses}
|
||||
aria-label={ariaLabel}
|
||||
type={submit ? 'submit' : 'button'}
|
||||
|
||||
@@ -94,3 +94,23 @@ export function secsToUnit(time: number, unit: Unit) {
|
||||
return time;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats time in seconds to a detailed string showing days, hours, minutes, and seconds
|
||||
* @param time Time in seconds
|
||||
* @returns Formatted string like "3d 4h 30m 23s"
|
||||
*/
|
||||
export function formatTimeDetailed(time: number): string {
|
||||
const seconds = Math.floor(time % 60);
|
||||
const minutes = Math.floor((time / 60) % 60);
|
||||
const hours = Math.floor((time / (60 * 60)) % 24);
|
||||
const days = Math.floor(time / (60 * 60 * 24));
|
||||
|
||||
const parts: string[] = [];
|
||||
if (days > 0) parts.push(`${days}d`);
|
||||
if (hours > 0) parts.push(`${hours}h`);
|
||||
if (minutes > 0) parts.push(`${minutes}m`);
|
||||
if (seconds > 0 || parts.length === 0) parts.push(`${seconds}s`);
|
||||
|
||||
return parts.join(' ');
|
||||
}
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
<script lang="ts">
|
||||
import { Modal } from '$lib/components';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import { Dialog, Layout } from '@appwrite.io/pink-svelte';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
|
||||
export let show = false;
|
||||
export let href: string = null;
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
function handleSubmit() {
|
||||
function handleExit() {
|
||||
dispatch('exit');
|
||||
show = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<Modal title="Exit Process" bind:show onSubmit={handleSubmit}>
|
||||
<p class="text">
|
||||
<slot />
|
||||
</p>
|
||||
<Dialog title="Exit process" bind:open={show}>
|
||||
<slot />
|
||||
<svelte:fragment slot="footer">
|
||||
<Button text on:click={() => (show = false)}>Cancel</Button>
|
||||
{#if href}
|
||||
<Button secondary size="s" {href}>Exit</Button>
|
||||
{:else}
|
||||
<Button secondary size="s" submit>Exit</Button>
|
||||
{/if}
|
||||
<Layout.Stack direction="row" gap="s" justifyContent="flex-end">
|
||||
<Button text on:click={() => (show = false)}>Cancel</Button>
|
||||
{#if href}
|
||||
<Button secondary size="s" {href}>Exit</Button>
|
||||
{:else}
|
||||
<Button secondary size="s" on:click={handleExit}>Exit</Button>
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
</svelte:fragment>
|
||||
</Modal>
|
||||
</Dialog>
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
Typography,
|
||||
Fieldset,
|
||||
InlineCode,
|
||||
Spinner
|
||||
} from '@appwrite.io/pink-svelte';
|
||||
import { Button, Form, InputText } from '$lib/elements/forms';
|
||||
import { IconAndroid, IconAppwrite } from '@appwrite.io/pink-icons-svelte';
|
||||
@@ -103,7 +102,7 @@ const val APPWRITE_PUBLIC_ENDPOINT = "${sdk.forProject.client.config.endpoint}"
|
||||
<InputText
|
||||
id="name"
|
||||
label="Name"
|
||||
placeholder="My Android App"
|
||||
placeholder="My Android app"
|
||||
required
|
||||
bind:value={$createPlatform.name} />
|
||||
|
||||
@@ -202,35 +201,43 @@ const val APPWRITE_PUBLIC_ENDPOINT = "${sdk.forProject.client.config.endpoint}"
|
||||
icon={IconAppwrite} />
|
||||
</Layout.Stack>
|
||||
|
||||
<Layout.Stack direction="row" justifyContent="center" alignItems="center" gap="l">
|
||||
{#if !connectionSuccessful}
|
||||
<Spinner />
|
||||
<Typography.Text variant="m-400">Waiting for connection...</Typography.Text>
|
||||
{:else}
|
||||
<!-- cannot apply fade on components -->
|
||||
<div
|
||||
in:fade={{ duration: 2500 }}
|
||||
class="u-flex u-flex-vertical u-cross-center u-gap-8">
|
||||
<Typography.Title size="m">Congratulations!</Typography.Title>
|
||||
|
||||
{#if isPlatformCreated}
|
||||
<Layout.Stack
|
||||
direction="row"
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
gap="l">
|
||||
{#if !connectionSuccessful}
|
||||
<Typography.Text variant="m-400"
|
||||
>You connected your app successfully.</Typography.Text>
|
||||
</div>
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
>Waiting for connection...</Typography.Text>
|
||||
{:else}
|
||||
<!-- cannot apply fade on components -->
|
||||
<div
|
||||
in:fade={{ duration: 2500 }}
|
||||
class="u-flex u-flex-vertical u-cross-center u-gap-8">
|
||||
<Typography.Title size="m">Congratulations!</Typography.Title>
|
||||
|
||||
<Typography.Text variant="m-400"
|
||||
>You connected your app successfully.</Typography.Text>
|
||||
</div>
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
</Card>
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="footer">
|
||||
<Button
|
||||
size="s"
|
||||
fullWidthMobile
|
||||
secondary
|
||||
disabled={isCreatingPlatform}
|
||||
href={`${base}/project-${projectId}/overview`}>
|
||||
Go to dashboard
|
||||
</Button>
|
||||
{#if isPlatformCreated}
|
||||
<Button
|
||||
size="s"
|
||||
fullWidthMobile
|
||||
secondary
|
||||
disabled={isCreatingPlatform}
|
||||
href={`${base}/project-${projectId}/overview`}>
|
||||
Go to dashboard
|
||||
</Button>
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
</Wizard>
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
Typography,
|
||||
Fieldset,
|
||||
InlineCode,
|
||||
Spinner
|
||||
} from '@appwrite.io/pink-svelte';
|
||||
import { Button, Form, InputText } from '$lib/elements/forms';
|
||||
import { IconApple, IconAppwrite } from '@appwrite.io/pink-icons-svelte';
|
||||
@@ -116,7 +115,8 @@ APPWRITE_PUBLIC_ENDPOINT: "${sdk.forProject.client.config.endpoint}"
|
||||
bind:group={platform}
|
||||
variant="primary"
|
||||
{value}
|
||||
title={key} />
|
||||
title={key}
|
||||
disabled={isPlatformCreated} />
|
||||
</div>
|
||||
{/each}
|
||||
</Layout.Stack>
|
||||
@@ -129,7 +129,7 @@ APPWRITE_PUBLIC_ENDPOINT: "${sdk.forProject.client.config.endpoint}"
|
||||
<InputText
|
||||
id="name"
|
||||
label="Name"
|
||||
placeholder="My Apple App"
|
||||
placeholder="My Apple app"
|
||||
required
|
||||
bind:value={$createPlatform.name} />
|
||||
|
||||
@@ -226,35 +226,43 @@ APPWRITE_PUBLIC_ENDPOINT: "${sdk.forProject.client.config.endpoint}"
|
||||
icon={IconAppwrite} />
|
||||
</Layout.Stack>
|
||||
|
||||
<Layout.Stack direction="row" justifyContent="center" alignItems="center" gap="l">
|
||||
{#if !connectionSuccessful}
|
||||
<Spinner />
|
||||
<Typography.Text variant="m-400">Waiting for connection...</Typography.Text>
|
||||
{:else}
|
||||
<!-- cannot apply fade on components -->
|
||||
<div
|
||||
in:fade={{ duration: 2500 }}
|
||||
class="u-flex u-flex-vertical u-cross-center u-gap-8">
|
||||
<Typography.Title size="m">Congratulations!</Typography.Title>
|
||||
|
||||
{#if isPlatformCreated}
|
||||
<Layout.Stack
|
||||
direction="row"
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
gap="l">
|
||||
{#if !connectionSuccessful}
|
||||
<Typography.Text variant="m-400"
|
||||
>You connected your app successfully.</Typography.Text>
|
||||
</div>
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
>Waiting for connection...</Typography.Text>
|
||||
{:else}
|
||||
<!-- cannot apply fade on components -->
|
||||
<div
|
||||
in:fade={{ duration: 2500 }}
|
||||
class="u-flex u-flex-vertical u-cross-center u-gap-8">
|
||||
<Typography.Title size="m">Congratulations!</Typography.Title>
|
||||
|
||||
<Typography.Text variant="m-400"
|
||||
>You connected your app successfully.</Typography.Text>
|
||||
</div>
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
</Card>
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="footer">
|
||||
<Button
|
||||
size="s"
|
||||
fullWidthMobile
|
||||
secondary
|
||||
disabled={isCreatingPlatform}
|
||||
href={`${base}/project-${projectId}/overview`}>
|
||||
Go to dashboard
|
||||
</Button>
|
||||
{#if isPlatformCreated}
|
||||
<Button
|
||||
size="s"
|
||||
fullWidthMobile
|
||||
secondary
|
||||
disabled={isCreatingPlatform}
|
||||
href={`${base}/project-${projectId}/overview`}>
|
||||
Go to dashboard
|
||||
</Button>
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
</Wizard>
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
Typography,
|
||||
Fieldset,
|
||||
InlineCode,
|
||||
Spinner
|
||||
} from '@appwrite.io/pink-svelte';
|
||||
import { Button, Form, InputText } from '$lib/elements/forms';
|
||||
import { IconFlutter, IconAppwrite } from '@appwrite.io/pink-icons-svelte';
|
||||
@@ -65,35 +64,35 @@ static const String APPWRITE_PUBLIC_ENDPOINT = "${sdk.forProject.client.config.e
|
||||
>
|
||||
> = {
|
||||
[PlatformType.Flutterandroid]: {
|
||||
name: 'My Android App',
|
||||
name: 'My Android app',
|
||||
hostname: 'com.company.appname',
|
||||
tooltip:
|
||||
'Your package name is generally the applicationId in your app-level build.gradle file.'
|
||||
},
|
||||
[PlatformType.Flutterios]: {
|
||||
name: 'My iOS App',
|
||||
name: 'My iOS app',
|
||||
hostname: 'com.company.appname',
|
||||
tooltip:
|
||||
"You can find your Bundle Identifier in the General tab for your app's primary target in Xcode."
|
||||
},
|
||||
[PlatformType.Flutterlinux]: {
|
||||
name: 'My Linux App',
|
||||
name: 'My Linux app',
|
||||
hostname: 'appname',
|
||||
tooltip: 'Your application name'
|
||||
},
|
||||
[PlatformType.Fluttermacos]: {
|
||||
name: 'My mac OS App',
|
||||
name: 'My mac OS app',
|
||||
hostname: 'com.company.appname',
|
||||
tooltip:
|
||||
"You can find your Bundle Identifier in the General tab for your app's primary target in Xcode."
|
||||
},
|
||||
[PlatformType.Flutterwindows]: {
|
||||
name: 'My Windows App',
|
||||
name: 'My Windows app',
|
||||
hostname: 'appname',
|
||||
tooltip: 'Your application name'
|
||||
},
|
||||
[PlatformType.Flutterweb]: {
|
||||
name: 'My Web App',
|
||||
name: 'My Web app',
|
||||
hostname: 'localhost',
|
||||
tooltip:
|
||||
'The hostname that your website will use to interact with the Appwrite APIs in production or development environments. No protocol or port number required.'
|
||||
@@ -101,12 +100,12 @@ static const String APPWRITE_PUBLIC_ENDPOINT = "${sdk.forProject.client.config.e
|
||||
};
|
||||
|
||||
const hostname: Partial<Record<PlatformType, string>> = {
|
||||
[PlatformType.Flutterandroid]: 'Package Name',
|
||||
[PlatformType.Flutterandroid]: 'Package name',
|
||||
[PlatformType.Flutterios]: 'Bundle ID',
|
||||
[PlatformType.Flutterlinux]: 'Package Name',
|
||||
[PlatformType.Flutterlinux]: 'Package name',
|
||||
[PlatformType.Fluttermacos]: 'Bundle ID',
|
||||
[PlatformType.Flutterweb]: 'Hostname',
|
||||
[PlatformType.Flutterwindows]: 'Package Name'
|
||||
[PlatformType.Flutterwindows]: 'Package name'
|
||||
};
|
||||
|
||||
async function createFlutterPlatform() {
|
||||
@@ -284,35 +283,43 @@ static const String APPWRITE_PUBLIC_ENDPOINT = "${sdk.forProject.client.config.e
|
||||
icon={IconAppwrite} />
|
||||
</Layout.Stack>
|
||||
|
||||
<Layout.Stack direction="row" justifyContent="center" alignItems="center" gap="l">
|
||||
{#if !connectionSuccessful}
|
||||
<Spinner />
|
||||
<Typography.Text variant="m-400">Waiting for connection...</Typography.Text>
|
||||
{:else}
|
||||
<!-- cannot apply fade on components -->
|
||||
<div
|
||||
in:fade={{ duration: 2500 }}
|
||||
class="u-flex u-flex-vertical u-cross-center u-gap-8">
|
||||
<Typography.Title size="m">Congratulations!</Typography.Title>
|
||||
|
||||
{#if isPlatformCreated}
|
||||
<Layout.Stack
|
||||
direction="row"
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
gap="l">
|
||||
{#if !connectionSuccessful}
|
||||
<Typography.Text variant="m-400"
|
||||
>You connected your app successfully.</Typography.Text>
|
||||
</div>
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
>Waiting for connection...</Typography.Text>
|
||||
{:else}
|
||||
<!-- cannot apply fade on components -->
|
||||
<div
|
||||
in:fade={{ duration: 2500 }}
|
||||
class="u-flex u-flex-vertical u-cross-center u-gap-8">
|
||||
<Typography.Title size="m">Congratulations!</Typography.Title>
|
||||
|
||||
<Typography.Text variant="m-400"
|
||||
>You connected your app successfully.</Typography.Text>
|
||||
</div>
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
</Card>
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="footer">
|
||||
<Button
|
||||
size="s"
|
||||
fullWidthMobile
|
||||
secondary
|
||||
disabled={isCreatingPlatform}
|
||||
href={`${base}/project-${projectId}/overview`}>
|
||||
Go to dashboard
|
||||
</Button>
|
||||
{#if isPlatformCreated}
|
||||
<Button
|
||||
size="s"
|
||||
fullWidthMobile
|
||||
secondary
|
||||
disabled={isCreatingPlatform}
|
||||
href={`${base}/project-${projectId}/overview`}>
|
||||
Go to dashboard
|
||||
</Button>
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
</Wizard>
|
||||
|
||||
|
||||
+34
-26
@@ -10,7 +10,6 @@
|
||||
Typography,
|
||||
Fieldset,
|
||||
InlineCode,
|
||||
Spinner
|
||||
} from '@appwrite.io/pink-svelte';
|
||||
import { Button, Form, InputText } from '$lib/elements/forms';
|
||||
import { IconReact, IconAppwrite } from '@appwrite.io/pink-icons-svelte';
|
||||
@@ -75,7 +74,7 @@ const APPWRITE_PUBLIC_ENDPOINT = "${sdk.forProject.client.config.endpoint}";
|
||||
};
|
||||
|
||||
const hostname: Partial<Record<PlatformType, string>> = {
|
||||
[PlatformType.Reactnativeandroid]: 'Package Name',
|
||||
[PlatformType.Reactnativeandroid]: 'Package name',
|
||||
[PlatformType.Reactnativeios]: 'Bundle ID'
|
||||
};
|
||||
|
||||
@@ -143,7 +142,8 @@ const APPWRITE_PUBLIC_ENDPOINT = "${sdk.forProject.client.config.endpoint}";
|
||||
bind:group={platform}
|
||||
variant="primary"
|
||||
{value}
|
||||
title={key} />
|
||||
title={key}
|
||||
disabled={isPlatformCreated} />
|
||||
</div>
|
||||
{/each}
|
||||
</Layout.Stack>
|
||||
@@ -251,35 +251,43 @@ const APPWRITE_PUBLIC_ENDPOINT = "${sdk.forProject.client.config.endpoint}";
|
||||
icon={IconAppwrite} />
|
||||
</Layout.Stack>
|
||||
|
||||
<Layout.Stack direction="row" justifyContent="center" alignItems="center" gap="l">
|
||||
{#if !connectionSuccessful}
|
||||
<Spinner />
|
||||
<Typography.Text variant="m-400">Waiting for connection...</Typography.Text>
|
||||
{:else}
|
||||
<!-- cannot apply fade on components -->
|
||||
<div
|
||||
in:fade={{ duration: 2500 }}
|
||||
class="u-flex u-flex-vertical u-cross-center u-gap-8">
|
||||
<Typography.Title size="m">Congratulations!</Typography.Title>
|
||||
|
||||
{#if isPlatformCreated}
|
||||
<Layout.Stack
|
||||
direction="row"
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
gap="l">
|
||||
{#if !connectionSuccessful}
|
||||
<Typography.Text variant="m-400"
|
||||
>You connected your app successfully.</Typography.Text>
|
||||
</div>
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
>Waiting for connection...</Typography.Text>
|
||||
{:else}
|
||||
<!-- cannot apply fade on components -->
|
||||
<div
|
||||
in:fade={{ duration: 2500 }}
|
||||
class="u-flex u-flex-vertical u-cross-center u-gap-8">
|
||||
<Typography.Title size="m">Congratulations!</Typography.Title>
|
||||
|
||||
<Typography.Text variant="m-400"
|
||||
>You connected your app successfully.</Typography.Text>
|
||||
</div>
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
</Card>
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="footer">
|
||||
<Button
|
||||
size="s"
|
||||
fullWidthMobile
|
||||
secondary
|
||||
disabled={isCreatingPlatform}
|
||||
href={`${base}/project-${projectId}/overview`}>
|
||||
Go to dashboard
|
||||
</Button>
|
||||
{#if isPlatformCreated}
|
||||
<Button
|
||||
size="s"
|
||||
fullWidthMobile
|
||||
secondary
|
||||
disabled={isCreatingPlatform}
|
||||
href={`${base}/project-${projectId}/overview`}>
|
||||
Go to dashboard
|
||||
</Button>
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
</Wizard>
|
||||
|
||||
|
||||
@@ -16,13 +16,17 @@
|
||||
|
||||
{#if deployment.type === 'vcs'}
|
||||
<Popover let:toggle>
|
||||
<Link
|
||||
on:click={(e) => {
|
||||
e.preventDefault();
|
||||
toggle(e);
|
||||
}}>
|
||||
<Icon icon={IconGithub} /> GitHub
|
||||
</Link>
|
||||
<Layout.Stack>
|
||||
<Link
|
||||
on:click={(e) => {
|
||||
e.preventDefault();
|
||||
toggle(e);
|
||||
}}>
|
||||
<Layout.Stack direction="row" gap="xs">
|
||||
<Icon icon={IconGithub} size="s" /> GitHub
|
||||
</Layout.Stack>
|
||||
</Link>
|
||||
</Layout.Stack>
|
||||
<svelte:fragment slot="tooltip">
|
||||
<ActionMenu.Root>
|
||||
<ActionMenu.Item.Anchor
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
import { timer } from '$lib/actions/timer';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import { calculateTime } from '$lib/helpers/timeConversion';
|
||||
import { capitalize } from '$lib/helpers/string';
|
||||
import { calculateTime, formatTimeDetailed } from '$lib/helpers/timeConversion';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
@@ -55,35 +56,56 @@
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function badgeType(status: string) {
|
||||
switch (status) {
|
||||
case 'failed':
|
||||
return 'error';
|
||||
case 'ready':
|
||||
return 'success';
|
||||
case 'building':
|
||||
return undefined;
|
||||
case 'processing':
|
||||
return 'warning';
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<Layout.Stack>
|
||||
<Layout.Stack gap="xl">
|
||||
<Layout.Stack direction="row" justifyContent="space-between">
|
||||
<Layout.Stack direction="row" alignItems="center">
|
||||
<Typography.Text variant="m-500">Deployment logs</Typography.Text>
|
||||
<Layout.Stack direction="row" alignItems="center" gap="s" inline>
|
||||
<Typography.Text variant="m-500" color="--color-fgcolor-neutral-primary">
|
||||
Deployment logs
|
||||
</Typography.Text>
|
||||
<Badge
|
||||
content={status}
|
||||
content={capitalize(status)}
|
||||
size="xs"
|
||||
variant="secondary"
|
||||
type={status === 'failed' ? 'error' : undefined} />
|
||||
type={badgeType(status)} />
|
||||
</Layout.Stack>
|
||||
<Layout.Stack direction="row" alignItems="center" inline>
|
||||
{#if ['processing', 'building'].includes(status)}
|
||||
<Typography.Code color="--color-fgcolor-neutral-secondary">
|
||||
<Layout.Stack direction="row" alignItems="center" inline>
|
||||
<p use:timer={{ start: deployment.$createdAt }} />
|
||||
<Spinner />
|
||||
</Layout.Stack>
|
||||
</Typography.Code>
|
||||
{:else}
|
||||
<Typography.Code color="--color-fgcolor-neutral-secondary">
|
||||
{formatTimeDetailed(deployment.buildTime)}
|
||||
</Typography.Code>
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
<div>
|
||||
<Layout.Stack direction="row" alignItems="center">
|
||||
{#if ['processing', 'building'].includes(status)}
|
||||
<span use:timer={{ start: deployment.$createdAt }} />
|
||||
<Spinner />
|
||||
{:else}
|
||||
{calculateTime(deployment.buildTime)}
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
</div>
|
||||
</Layout.Stack>
|
||||
|
||||
<Logs logs={buildLogs} />
|
||||
|
||||
<Layout.Stack alignItems="flex-end">
|
||||
{#if ['processing', 'building'].includes(status)}
|
||||
<Button size="xs" text on:click={cancelDeployment}>Cancel deployment</Button>
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
{#key buildLogs}
|
||||
<Logs logs={buildLogs} />
|
||||
{/key}
|
||||
{#if ['processing', 'building'].includes(status)}
|
||||
<Layout.Stack alignItems="flex-end">
|
||||
<Button size="s" text on:click={cancelDeployment}>Cancel deployment</Button>
|
||||
</Layout.Stack>
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
|
||||
+8
-8
@@ -12,32 +12,32 @@
|
||||
export let siteId: string;
|
||||
let error: string;
|
||||
|
||||
async function promote() {
|
||||
async function activate() {
|
||||
try {
|
||||
await sdk.forProject.sites.updateDeploymentBuild(siteId, selectedDeploymentId);
|
||||
await sdk.forProject.sites.updateDeployment(siteId, selectedDeploymentId);
|
||||
addNotification({
|
||||
type: 'success',
|
||||
message: `Deployment has been promoted`
|
||||
message: `Deployment has been activated`
|
||||
});
|
||||
trackEvent(Submit.SitePromoteDeployment);
|
||||
trackEvent(Submit.SiteActivateDeployment);
|
||||
|
||||
invalidate(Dependencies.SITE);
|
||||
invalidate(Dependencies.DEPLOYMENTS);
|
||||
show = false;
|
||||
} catch (e) {
|
||||
error = e.message;
|
||||
trackError(e, Submit.SitePromoteDeployment);
|
||||
trackError(e, Submit.SiteActivateDeployment);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<Modal title="Promote deployment" bind:show bind:error onSubmit={promote}>
|
||||
<Modal title="Activate deployment" bind:show bind:error onSubmit={activate}>
|
||||
<p class="text">
|
||||
Are you sure you want to promote this deployment? This might affect your production code.
|
||||
Are you sure you want to activate this deployment? This might affect your production code.
|
||||
</p>
|
||||
|
||||
<svelte:fragment slot="footer">
|
||||
<Button secondary on:click={() => (show = false)}>Cancel</Button>
|
||||
<Button submit>Promote</Button>
|
||||
<Button submit>Activate</Button>
|
||||
</svelte:fragment>
|
||||
</Modal>
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { Card, SvgIcon } from '$lib/components';
|
||||
import { Card, SvgIcon, Trim } from '$lib/components';
|
||||
import { Icon, Layout, Typography } from '@appwrite.io/pink-svelte';
|
||||
import { IconGithub, IconGitBranch } from '@appwrite.io/pink-icons-svelte';
|
||||
import { consoleVariables } from '$routes/(console)/store';
|
||||
@@ -14,29 +14,15 @@
|
||||
export let showAfter = true;
|
||||
</script>
|
||||
|
||||
<Card padding="xs">
|
||||
<Card padding="s" radius="s">
|
||||
<Layout.Stack gap="l">
|
||||
<slot />
|
||||
<Layout.Stack gap="l">
|
||||
{#if framework?.name}
|
||||
{@const frameworkIcon = getFrameworkIcon(framework.key)}
|
||||
<Layout.Stack gap="xxxs">
|
||||
<Typography.Caption variant="400">Framework</Typography.Caption>
|
||||
<Layout.Stack gap="xs" alignItems="center" direction="row">
|
||||
{#if frameworkIcon}
|
||||
<SvgIcon iconSize="small" size={20} name={frameworkIcon} />
|
||||
{/if}
|
||||
<Typography.Text variant="m-500" color="--color-fgcolor-neutral-primary">
|
||||
{framework.name}
|
||||
</Typography.Text>
|
||||
</Layout.Stack>
|
||||
</Layout.Stack>
|
||||
{/if}
|
||||
{#if repositoryName && showAfter}
|
||||
<Layout.Stack gap="xxxs">
|
||||
<Typography.Caption variant="400">Git repository</Typography.Caption>
|
||||
<Layout.Stack gap="xs" alignItems="center" direction="row">
|
||||
<Icon size="s" icon={IconGithub}></Icon>
|
||||
<Layout.Stack gap="xxs" alignItems="center" direction="row">
|
||||
<Icon size="s" icon={IconGithub} color="--color-fgcolor-neutral-primary" />
|
||||
<Typography.Text variant="m-500" color="--color-fgcolor-neutral-primary">
|
||||
{repositoryName}
|
||||
</Typography.Text>
|
||||
@@ -46,14 +32,18 @@
|
||||
{#if branch && showAfter}
|
||||
<Layout.Stack gap="xxxs">
|
||||
<Typography.Caption variant="400">Branch</Typography.Caption>
|
||||
<Layout.Stack gap="xs" alignItems="center" direction="row">
|
||||
<Icon size="s" icon={IconGitBranch}></Icon>
|
||||
<Layout.Stack gap="xxs" alignItems="center" direction="row">
|
||||
<Icon
|
||||
size="s"
|
||||
icon={IconGitBranch}
|
||||
color="--color-fgcolor-neutral-primary" />
|
||||
<Typography.Text variant="m-500" color="--color-fgcolor-neutral-primary">
|
||||
{branch}
|
||||
</Typography.Text>
|
||||
</Layout.Stack>
|
||||
</Layout.Stack>
|
||||
{/if}
|
||||
|
||||
{#if rootDir && showAfter}
|
||||
<Layout.Stack gap="xxxs">
|
||||
<Typography.Caption variant="400">Root directory</Typography.Caption>
|
||||
@@ -62,16 +52,33 @@
|
||||
</Typography.Text>
|
||||
</Layout.Stack>
|
||||
{/if}
|
||||
{#if framework?.name}
|
||||
{@const frameworkIcon = getFrameworkIcon(framework.key)}
|
||||
<Layout.Stack gap="xxxs">
|
||||
<Typography.Caption variant="400">Framework</Typography.Caption>
|
||||
<Layout.Stack gap="xxs" alignItems="center" direction="row">
|
||||
{#if frameworkIcon}
|
||||
<SvgIcon iconSize="small" size={16} name={frameworkIcon} />
|
||||
{/if}
|
||||
<Typography.Text variant="m-500" color="--color-fgcolor-neutral-primary">
|
||||
{framework.name}
|
||||
</Typography.Text>
|
||||
</Layout.Stack>
|
||||
</Layout.Stack>
|
||||
{/if}
|
||||
|
||||
{#if domain && showAfter}
|
||||
<Layout.Stack gap="xxxs">
|
||||
<Typography.Caption variant="400">Domain</Typography.Caption>
|
||||
<Typography.Text
|
||||
variant="m-500"
|
||||
color="--color-fgcolor-neutral-primary"
|
||||
truncate>
|
||||
{$consoleVariables._APP_OPTIONS_FORCE_HTTPS
|
||||
? 'https://'
|
||||
: 'http://'}{domain}.{$consoleVariables._APP_DOMAIN_TARGET}
|
||||
|
||||
<Typography.Text variant="m-500" color="--color-fgcolor-neutral-primary">
|
||||
{#key domain}
|
||||
<Trim alternativeTrim>
|
||||
{$consoleVariables._APP_OPTIONS_FORCE_HTTPS
|
||||
? 'https://'
|
||||
: 'http://'}{domain}.{$consoleVariables._APP_DOMAIN_TARGET}
|
||||
</Trim>
|
||||
{/key}
|
||||
</Typography.Text>
|
||||
</Layout.Stack>
|
||||
{/if}
|
||||
|
||||
@@ -10,7 +10,9 @@
|
||||
Badge,
|
||||
HiddenText,
|
||||
ActionMenu,
|
||||
Accordion
|
||||
Accordion,
|
||||
Tooltip,
|
||||
Button as PinkButton
|
||||
} from '@appwrite.io/pink-svelte';
|
||||
import {
|
||||
IconDotsHorizontal,
|
||||
@@ -28,6 +30,7 @@
|
||||
import SecretVariableModal from './secretVariableModal.svelte';
|
||||
import ImportSiteVariablesModal from './importSiteVariablesModal.svelte';
|
||||
import CreateVariable from './createVariable.svelte';
|
||||
import DeleteVariableModal from './deleteVariableModal.svelte';
|
||||
|
||||
export let frameworks: Models.Framework[];
|
||||
export let selectedFramework: Models.Framework;
|
||||
@@ -40,22 +43,16 @@
|
||||
let showEditorModal = false;
|
||||
let showImportModal = false;
|
||||
let showSecretModal = false;
|
||||
let showDelete = false;
|
||||
let showCreate = false;
|
||||
|
||||
let currentVariable: Partial<Models.Variable>;
|
||||
let frameworkId = selectedFramework.key;
|
||||
|
||||
function markAsSecret() {
|
||||
let variable = variables.find((v) => v.key === currentVariable.key);
|
||||
if (variable) {
|
||||
variable.secret = true;
|
||||
}
|
||||
}
|
||||
|
||||
$: frameworkData = frameworks.find((framework) => framework.key === selectedFramework.key);
|
||||
</script>
|
||||
|
||||
<Fieldset legend="Configuration">
|
||||
<Fieldset legend="Settings">
|
||||
<Layout.Stack gap="l">
|
||||
<InputSelect
|
||||
id="framework"
|
||||
@@ -73,37 +70,51 @@
|
||||
|
||||
<Layout.Stack>
|
||||
<Accordion title="Build settings" badge="Optional">
|
||||
<Layout.Stack>
|
||||
<Layout.Stack gap="xl">
|
||||
Set up how your project is built and where the output files are stored.
|
||||
<Layout.Stack gap="s" direction="row" alignItems="flex-end">
|
||||
<InputText
|
||||
id="installCommand"
|
||||
label="Install command"
|
||||
bind:value={installCommand}
|
||||
placeholder={frameworkData?.defaultInstallCommand} />
|
||||
<Button secondary size="s" on:click={() => (installCommand = '')}>
|
||||
Reset
|
||||
</Button>
|
||||
</Layout.Stack>
|
||||
<Layout.Stack gap="s" direction="row" alignItems="flex-end">
|
||||
<InputText
|
||||
id="buildCommand"
|
||||
label="Build command"
|
||||
bind:value={buildCommand}
|
||||
placeholder={frameworkData?.defaultBuildCommand} />
|
||||
<Button secondary size="s" on:click={() => (buildCommand = '')}>
|
||||
Reset
|
||||
</Button>
|
||||
</Layout.Stack>
|
||||
<Layout.Stack gap="s" direction="row" alignItems="flex-end">
|
||||
<InputText
|
||||
id="outputDirectory"
|
||||
label="Output directory"
|
||||
bind:value={outputDirectory}
|
||||
placeholder={frameworkData?.defaultOutputDirectory} />
|
||||
<Button secondary size="s" on:click={() => (outputDirectory = '')}>
|
||||
Reset
|
||||
</Button>
|
||||
<Layout.Stack>
|
||||
<Layout.Stack gap="s" direction="row" alignItems="flex-end">
|
||||
<InputText
|
||||
id="installCommand"
|
||||
label="Install command"
|
||||
bind:value={installCommand}
|
||||
placeholder={frameworkData?.defaultInstallCommand} />
|
||||
<Button
|
||||
secondary
|
||||
size="s"
|
||||
disabled={frameworkData?.defaultInstallCommand === installCommand}
|
||||
on:click={() => (installCommand = '')}>
|
||||
Reset
|
||||
</Button>
|
||||
</Layout.Stack>
|
||||
<Layout.Stack gap="s" direction="row" alignItems="flex-end">
|
||||
<InputText
|
||||
id="buildCommand"
|
||||
label="Build command"
|
||||
bind:value={buildCommand}
|
||||
placeholder={frameworkData?.defaultBuildCommand} />
|
||||
<Button
|
||||
secondary
|
||||
size="s"
|
||||
disabled={frameworkData?.defaultBuildCommand === buildCommand}
|
||||
on:click={() => (buildCommand = '')}>
|
||||
Reset
|
||||
</Button>
|
||||
</Layout.Stack>
|
||||
<Layout.Stack gap="s" direction="row" alignItems="flex-end">
|
||||
<InputText
|
||||
id="outputDirectory"
|
||||
label="Output directory"
|
||||
bind:value={outputDirectory}
|
||||
placeholder={frameworkData?.defaultOutputDirectory} />
|
||||
<Button
|
||||
secondary
|
||||
size="s"
|
||||
disabled={frameworkData?.defaultOutputDirectory === outputDirectory}
|
||||
on:click={() => (outputDirectory = '')}>
|
||||
Reset
|
||||
</Button>
|
||||
</Layout.Stack>
|
||||
</Layout.Stack>
|
||||
</Layout.Stack>
|
||||
</Accordion>
|
||||
@@ -112,10 +123,31 @@
|
||||
<svelte:fragment slot="title">
|
||||
Environment variables <Badge content="Optional" variant="secondary" />
|
||||
</svelte:fragment>
|
||||
<Layout.Stack gap="l">
|
||||
<Layout.Stack gap="xl">
|
||||
Set up environment variables to securely manage keys and settings for your
|
||||
project.
|
||||
<Layout.Stack gap="xl">
|
||||
Set up environment variables to securely manage keys and settings for your
|
||||
project.
|
||||
<Layout.Stack gap="l">
|
||||
<Layout.Stack direction="row">
|
||||
<Layout.Stack direction="row" gap="s">
|
||||
<Button
|
||||
secondary
|
||||
size="s"
|
||||
on:mousedown={() => (showEditorModal = true)}>
|
||||
<Icon slot="start" icon={IconCode} /> Editor
|
||||
</Button>
|
||||
<Button
|
||||
secondary
|
||||
size="s"
|
||||
on:mousedown={() => (showImportModal = true)}>
|
||||
<Icon slot="start" icon={IconUpload} /> Import .env
|
||||
</Button>
|
||||
</Layout.Stack>
|
||||
{#if variables?.length}
|
||||
<Button secondary size="s" on:mousedown={() => (showCreate = true)}>
|
||||
<Icon slot="start" icon={IconPlus} /> Create variable
|
||||
</Button>
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
|
||||
{#if variables?.length}
|
||||
<Table.Root>
|
||||
@@ -131,7 +163,16 @@
|
||||
<!-- TODO: fix max width -->
|
||||
<div style="max-width: 20rem">
|
||||
{#if variable.secret}
|
||||
<Badge content="Secret" variant="secondary" />
|
||||
<Tooltip maxWidth="26rem">
|
||||
<Badge
|
||||
content="Secret"
|
||||
variant="secondary"
|
||||
size="s" />
|
||||
<svelte:fragment slot="tooltip">
|
||||
This value is secret, you cannot see its
|
||||
value.
|
||||
</svelte:fragment>
|
||||
</Tooltip>
|
||||
{:else}
|
||||
<HiddenText
|
||||
isVisible={false}
|
||||
@@ -142,27 +183,33 @@
|
||||
<Table.Cell>
|
||||
<div style="margin-inline-start: auto">
|
||||
<Popover placement="bottom-end" let:toggle>
|
||||
<Button
|
||||
text
|
||||
<PinkButton.Button
|
||||
icon
|
||||
variant="text"
|
||||
size="s"
|
||||
aria-label="More options"
|
||||
on:click={(e) => {
|
||||
e.preventDefault();
|
||||
toggle(e);
|
||||
}}>
|
||||
<Icon size="s" icon={IconDotsHorizontal} />
|
||||
</Button>
|
||||
<Icon icon={IconDotsHorizontal} size="s" />
|
||||
</PinkButton.Button>
|
||||
|
||||
<svelte:fragment slot="tooltip">
|
||||
<ActionMenu.Root>
|
||||
<ActionMenu.Item.Button
|
||||
trailingIcon={IconPencil}
|
||||
on:click={() => {
|
||||
showEditorModal = true;
|
||||
}}>
|
||||
Edit
|
||||
</ActionMenu.Item.Button>
|
||||
<ActionMenu.Root noPadding>
|
||||
{#if !variable?.secret}
|
||||
<ActionMenu.Item.Button
|
||||
trailingIcon={IconEyeOff}
|
||||
leadingIcon={IconPencil}
|
||||
on:click={() => {
|
||||
currentVariable = variable;
|
||||
showCreate = true;
|
||||
}}>
|
||||
Edit
|
||||
</ActionMenu.Item.Button>
|
||||
{/if}
|
||||
{#if !variable?.secret}
|
||||
<ActionMenu.Item.Button
|
||||
leadingIcon={IconEyeOff}
|
||||
on:click={() => {
|
||||
currentVariable = variable;
|
||||
showSecretModal = true;
|
||||
@@ -171,9 +218,11 @@
|
||||
</ActionMenu.Item.Button>
|
||||
{/if}
|
||||
<ActionMenu.Item.Button
|
||||
trailingIcon={IconTrash}
|
||||
status="danger"
|
||||
leadingIcon={IconTrash}
|
||||
on:click={() => {
|
||||
showImportModal = true;
|
||||
currentVariable = variable;
|
||||
showDelete = true;
|
||||
}}>
|
||||
Delete
|
||||
</ActionMenu.Item.Button>
|
||||
@@ -190,25 +239,6 @@
|
||||
>Create variables to get started</Empty>
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
<Layout.Stack direction="row">
|
||||
<Layout.Stack direction="row">
|
||||
<Button
|
||||
secondary
|
||||
size="s"
|
||||
on:mousedown={() => (showEditorModal = true)}>
|
||||
<Icon icon={IconCode} /> Editor
|
||||
</Button>
|
||||
<Button
|
||||
secondary
|
||||
size="s"
|
||||
on:mousedown={() => (showImportModal = true)}>
|
||||
<Icon icon={IconUpload} /> Import .env
|
||||
</Button>
|
||||
</Layout.Stack>
|
||||
<Button secondary size="s" on:mousedown={() => (showCreate = true)}>
|
||||
<Icon icon={IconPlus} /> Create variable
|
||||
</Button>
|
||||
</Layout.Stack>
|
||||
</Layout.Stack>
|
||||
</Accordion>
|
||||
</Layout.Stack>
|
||||
@@ -220,7 +250,7 @@
|
||||
{/if}
|
||||
|
||||
{#if showSecretModal}
|
||||
<SecretVariableModal bind:show={showSecretModal} on:click={markAsSecret} />
|
||||
<SecretVariableModal bind:show={showSecretModal} bind:currentVariable bind:variables />
|
||||
{/if}
|
||||
|
||||
{#if showImportModal}
|
||||
@@ -228,5 +258,9 @@
|
||||
{/if}
|
||||
|
||||
{#if showCreate}
|
||||
<CreateVariable bind:show={showCreate} bind:variables />
|
||||
<CreateVariable bind:show={showCreate} bind:variables bind:selectedVar={currentVariable} />
|
||||
{/if}
|
||||
|
||||
{#if showDelete}
|
||||
<DeleteVariableModal bind:show={showDelete} bind:variables bind:currentVariable />
|
||||
{/if}
|
||||
|
||||
@@ -46,22 +46,25 @@
|
||||
href={`${base}/project-${$page.params.project}/settings`}>project settings</Link
|
||||
>.
|
||||
</span>
|
||||
<Layout.Stack>
|
||||
<InputText
|
||||
id="key"
|
||||
label="Key"
|
||||
placeholder="ENTER_KEY"
|
||||
bind:value={pair.key}
|
||||
required
|
||||
autofocus
|
||||
autocomplete={false} />
|
||||
<InputPassword
|
||||
id="value"
|
||||
label="Value"
|
||||
placeholder="Enter value"
|
||||
bind:value={pair.value}
|
||||
minlength={0} />
|
||||
<Layout.Stack gap="xxl">
|
||||
<Layout.Stack>
|
||||
<InputText
|
||||
id="key"
|
||||
label="Key"
|
||||
placeholder="ENTER_KEY"
|
||||
bind:value={pair.key}
|
||||
required
|
||||
autofocus
|
||||
autocomplete={false} />
|
||||
<InputPassword
|
||||
id="value"
|
||||
label="Value"
|
||||
placeholder="Enter value"
|
||||
bind:value={pair.value}
|
||||
minlength={0} />
|
||||
</Layout.Stack>
|
||||
<Selector.Checkbox
|
||||
size="s"
|
||||
id="secret"
|
||||
label="Secret"
|
||||
bind:checked={pair.secret}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
<script lang="ts">
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
import { Dialog, Layout } from '@appwrite.io/pink-svelte';
|
||||
|
||||
export let show = false;
|
||||
|
||||
export let currentVariable: Partial<Models.Variable> = null;
|
||||
export let variables: Partial<Models.Variable>[] = [];
|
||||
|
||||
function deleteVariable() {
|
||||
console.log(variables, currentVariable);
|
||||
variables = variables.filter((v) => v.key !== currentVariable.key);
|
||||
variables = [...variables];
|
||||
currentVariable = null;
|
||||
show = false;
|
||||
}
|
||||
|
||||
$: if (!show) {
|
||||
currentVariable = null;
|
||||
}
|
||||
</script>
|
||||
|
||||
<Dialog title="Delete variable" bind:open={show}>
|
||||
<p>Are you sure you want to delete this variable? This action is irreversible.</p>
|
||||
<svelte:fragment slot="footer">
|
||||
<Layout.Stack direction="row" gap="s" justifyContent="flex-end">
|
||||
<Button text on:click={() => (show = false)}>Cancel</Button>
|
||||
<Button on:click={deleteVariable}>Delete</Button>
|
||||
</Layout.Stack>
|
||||
</svelte:fragment>
|
||||
</Dialog>
|
||||
@@ -6,19 +6,29 @@
|
||||
import Button from '$lib/elements/forms/button.svelte';
|
||||
import Aside from '../aside.svelte';
|
||||
import Logs from '../../(components)/logs.svelte';
|
||||
import { getFrameworkIcon } from '../../store';
|
||||
import { SvgIcon } from '$lib/components';
|
||||
|
||||
export let data;
|
||||
|
||||
$: console.log(data);
|
||||
</script>
|
||||
|
||||
<Wizard
|
||||
title="Create site"
|
||||
href={`${base}/project-${$page.params.project}/sites/site-${data.site.$id}`}>
|
||||
<Layout.Stack gap="xl">
|
||||
<Card.Base padding="s">
|
||||
<Card.Base padding="s" radius="s">
|
||||
<Layout.Stack direction="row">
|
||||
<Layout.Stack direction="row" alignItems="center">
|
||||
<Typography.Text variant="m-500">{data.site.name}</Typography.Text>
|
||||
<Tag size="s">{data.site.$id}</Tag>
|
||||
<Layout.Stack direction="row" alignItems="center" gap="s">
|
||||
<SvgIcon
|
||||
iconSize="small"
|
||||
size={16}
|
||||
name={getFrameworkIcon(data.site.framework)} />
|
||||
<Typography.Text variant="m-500" color="--color-fgcolor-neutral-primary">
|
||||
{data.site.name}
|
||||
</Typography.Text>
|
||||
<Tag variant="code" size="xs">{data.site.$id}</Tag>
|
||||
</Layout.Stack>
|
||||
</Layout.Stack>
|
||||
</Card.Base>
|
||||
@@ -30,16 +40,18 @@
|
||||
<!-- TODO: fix use repository name instead of id-->
|
||||
<Aside
|
||||
framework={data.frameworks.frameworks.find((f) => f.name === data.site.framework)}
|
||||
repositoryName={data.site.providerRepositoryId}
|
||||
repositoryName={data.repository.name}
|
||||
branch={data.site.providerBranch}
|
||||
rootDir={data.site.providerRootDirectory}
|
||||
domain={data.deployment.domain} />
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="footer">
|
||||
<Layout.Stack direction="row" alignItems="center" justifyContent="flex-end">
|
||||
<Typography.Text variant="m-400" color="--color-fgColor-neutral-tertiary">
|
||||
Deployment will continue in the background
|
||||
</Typography.Text>
|
||||
{#if ['processing', 'building', 'failed'].includes(data.deployment.status)}
|
||||
<Typography.Text variant="m-400" color="--color-fgColor-neutral-tertiary">
|
||||
Deployment will continue in the background
|
||||
</Typography.Text>
|
||||
{/if}
|
||||
<Button
|
||||
size="s"
|
||||
fullWidthMobile
|
||||
|
||||
@@ -14,6 +14,10 @@ export const load: PageLoad = async ({ url }) => {
|
||||
|
||||
return {
|
||||
site,
|
||||
deployment
|
||||
deployment,
|
||||
repository: await sdk.forProject.vcs.getRepository(
|
||||
site.installationId,
|
||||
site.providerRepositoryId
|
||||
)
|
||||
};
|
||||
};
|
||||
|
||||
+41
-11
@@ -1,8 +1,10 @@
|
||||
<script lang="ts">
|
||||
import { Modal } from '$lib/components';
|
||||
import Alert from '$lib/components/alert.svelte';
|
||||
import { Button, InputChoice, InputFile } from '$lib/elements/forms';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
import { IconInfo } from '@appwrite.io/pink-icons-svelte';
|
||||
import { Icon, Layout, Selector, Tooltip, Typography, Upload } from '@appwrite.io/pink-svelte';
|
||||
import { parse } from 'envfile';
|
||||
|
||||
export let show = false;
|
||||
@@ -46,16 +48,44 @@
|
||||
|
||||
<Modal title="Import variables" bind:show onSubmit={handleSubmit} bind:error>
|
||||
<span slot="description"> Import new environment variables from .env file. </span>
|
||||
<InputFile bind:files />
|
||||
{#if variables?.length > 0}
|
||||
<Alert type="info" dismissible>
|
||||
This action can create and update variables but can not delete them.
|
||||
</Alert>
|
||||
{/if}
|
||||
|
||||
<InputChoice id="secret" label="Secret" bind:value={secret}>
|
||||
If selected, you and your team won't be able to read the values after creation.</InputChoice>
|
||||
|
||||
<Layout.Stack gap="xxl">
|
||||
<Layout.Stack>
|
||||
<Upload.Dropzone bind:files>
|
||||
<Layout.Stack alignItems="center" gap="s">
|
||||
<Layout.Stack
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
direction="row"
|
||||
gap="s">
|
||||
<Typography.Text variant="l-500">
|
||||
Drag and drop files here or click to upload
|
||||
</Typography.Text>
|
||||
<Tooltip>
|
||||
<Layout.Stack alignItems="center" justifyContent="center">
|
||||
<Icon icon={IconInfo} size="s" />
|
||||
</Layout.Stack>
|
||||
<svelte:fragment slot="tooltip">
|
||||
Only .env files allowed
|
||||
</svelte:fragment>
|
||||
</Tooltip>
|
||||
</Layout.Stack>
|
||||
<Typography.Caption variant="400"
|
||||
>Up to 100 variables allowed</Typography.Caption>
|
||||
</Layout.Stack>
|
||||
</Upload.Dropzone>
|
||||
{#if variables?.length > 0}
|
||||
<Alert type="info" dismissible>
|
||||
This action can create and update variables but can not delete them.
|
||||
</Alert>
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
<Selector.Checkbox
|
||||
size="s"
|
||||
id="secret"
|
||||
label="Secret"
|
||||
bind:checked={secret}
|
||||
description="If selected, you and your team won't be able to read the values after creation." />
|
||||
</Layout.Stack>
|
||||
<svelte:fragment slot="footer">
|
||||
<Button text on:click={() => (show = false)}>Cancel</Button>
|
||||
<Button submit disabled={!files?.length}>Import</Button>
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
</Layout.Stack>
|
||||
|
||||
<Selector.Checkbox
|
||||
size="s"
|
||||
id="silentMode"
|
||||
label="Silent mode"
|
||||
description="If selected, comments will not be created when pushing changes to this repository."
|
||||
|
||||
+1
-1
@@ -191,7 +191,7 @@
|
||||
</Layout.Stack>
|
||||
</Form>
|
||||
<svelte:fragment slot="aside">
|
||||
<Aside {framework} repositoryName={data.repository.name} {branch} {rootDir} />
|
||||
<Aside {framework} repositoryName={data.repository.name} {branch} {rootDir} {domain} />
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="footer">
|
||||
|
||||
+25
-12
@@ -1,12 +1,29 @@
|
||||
<script lang="ts">
|
||||
import { Modal } from '$lib/components';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import { Layout } from '@appwrite.io/pink-svelte';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
import { Dialog, Layout } from '@appwrite.io/pink-svelte';
|
||||
|
||||
export let show = false;
|
||||
|
||||
export let currentVariable: Partial<Models.Variable>;
|
||||
export let variables: Partial<Models.Variable>[];
|
||||
|
||||
function markAsSecret() {
|
||||
let variable = variables.find((v) => v.key === currentVariable.key);
|
||||
if (variable) {
|
||||
variable.secret = true;
|
||||
}
|
||||
variables = [...variables];
|
||||
currentVariable = null;
|
||||
show = false;
|
||||
}
|
||||
|
||||
$: if (!show) {
|
||||
currentVariable = null;
|
||||
}
|
||||
</script>
|
||||
|
||||
<Modal title="Secret variable" bind:show>
|
||||
<Dialog title="Secret variable" bind:open={show}>
|
||||
<Layout.Stack gap="l">
|
||||
<p>
|
||||
Secret variables are hidden from both the UI and API. Once a variable is marked as
|
||||
@@ -15,13 +32,9 @@
|
||||
<p>Are you sure you want to make this variable secret?</p>
|
||||
</Layout.Stack>
|
||||
<svelte:fragment slot="footer">
|
||||
<Button text on:click={() => (show = false)}>Cancel</Button>
|
||||
<Button
|
||||
on:click
|
||||
on:click={() => {
|
||||
show = false;
|
||||
}}>
|
||||
Mark as secret
|
||||
</Button>
|
||||
<Layout.Stack direction="row" gap="s" justifyContent="flex-end">
|
||||
<Button text on:click={() => (show = false)}>Cancel</Button>
|
||||
<Button on:click={markAsSecret}>Mark as secret</Button>
|
||||
</Layout.Stack>
|
||||
</svelte:fragment>
|
||||
</Modal>
|
||||
</Dialog>
|
||||
|
||||
+2
-1
@@ -190,7 +190,8 @@
|
||||
selectedRepository = null;
|
||||
}
|
||||
|
||||
$: console.log(domain);
|
||||
$: console.log(data.template);
|
||||
$: console.log(variables);
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
|
||||
+26
-37
@@ -11,12 +11,13 @@
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
import { Fieldset, Layout, Popover, Icon, Accordion } from '@appwrite.io/pink-svelte';
|
||||
import { IconInfo } from '@appwrite.io/pink-icons-svelte';
|
||||
import type { SvelteComponent } from 'svelte';
|
||||
import { type SvelteComponent } from 'svelte';
|
||||
|
||||
export let variables: Partial<Models.TemplateVariable>[] = [];
|
||||
export let templateVariables: Models.TemplateVariable[] = [];
|
||||
|
||||
let { requiredVariables, optionalVariables } = templateVariables.reduce(
|
||||
variables = [...templateVariables];
|
||||
let { requiredVariables, optionalVariables } = variables.reduce(
|
||||
(acc, variable) => {
|
||||
if (variable.required) {
|
||||
acc.requiredVariables.push(variable);
|
||||
@@ -46,9 +47,11 @@
|
||||
return InputPassword;
|
||||
}
|
||||
}
|
||||
|
||||
$: console.log(variables);
|
||||
</script>
|
||||
|
||||
<Fieldset legend="Configuration">
|
||||
<Fieldset legend="Settings">
|
||||
<Layout.Stack gap="l">
|
||||
<Layout.Stack>
|
||||
{#if requiredVariables?.length}
|
||||
@@ -60,23 +63,16 @@
|
||||
{#each requiredVariables as variable}
|
||||
<Layout.Stack gap="s" direction="row">
|
||||
<Layout.Stack gap="s" direction="row">
|
||||
<div style="flex-basis:1; width: 100%">
|
||||
<InputText
|
||||
id={variable.name}
|
||||
value={variable.name}
|
||||
readonly />
|
||||
</div>
|
||||
<div style="flex-basis:1;width: 100%">
|
||||
<svelte:component
|
||||
this={selectComponent(variable.type)}
|
||||
id={variable.name}
|
||||
placeholder={variable.placeholder ?? 'Enter value'}
|
||||
required={variable.required}
|
||||
autocomplete={false}
|
||||
minlength={variable.type === 'password' ? 0 : null}
|
||||
showPasswordButton={variable.type === 'password'}
|
||||
bind:value={variables[variable.name].value} />
|
||||
</div>
|
||||
<InputText id={variable.name} value={variable.name} readonly />
|
||||
<svelte:component
|
||||
this={selectComponent(variable.type)}
|
||||
id={variable.name}
|
||||
placeholder={variable.placeholder ?? 'Enter value'}
|
||||
required={variable.required}
|
||||
autocomplete={false}
|
||||
minlength={variable.type === 'password' ? 0 : null}
|
||||
showPasswordButton={variable.type === 'password'}
|
||||
bind:value={variable.value} />
|
||||
</Layout.Stack>
|
||||
<Popover placement="bottom-end" let:toggle>
|
||||
<Button
|
||||
@@ -103,23 +99,16 @@
|
||||
{#each optionalVariables as variable}
|
||||
<Layout.Stack gap="s" direction="row">
|
||||
<Layout.Stack gap="s" direction="row">
|
||||
<div style="flex-basis:1; width: 100%">
|
||||
<InputText
|
||||
id={variable.name}
|
||||
value={variable.name}
|
||||
readonly />
|
||||
</div>
|
||||
<div style="flex-basis:1;width: 100%">
|
||||
<svelte:component
|
||||
this={selectComponent(variable.type)}
|
||||
id={variable.name}
|
||||
placeholder={variable.placeholder ?? 'Enter value'}
|
||||
required={variable.required}
|
||||
autocomplete={false}
|
||||
minlength={variable.type === 'password' ? 0 : null}
|
||||
showPasswordButton={variable.type === 'password'}
|
||||
bind:value={variables[variable.name].value} />
|
||||
</div>
|
||||
<InputText id={variable.name} value={variable.name} readonly />
|
||||
<svelte:component
|
||||
this={selectComponent(variable.type)}
|
||||
id={variable.name}
|
||||
placeholder={variable.placeholder ?? 'Enter value'}
|
||||
required={variable.required}
|
||||
autocomplete={false}
|
||||
minlength={variable.type === 'password' ? 0 : null}
|
||||
showPasswordButton={variable.type === 'password'}
|
||||
bind:value={variables[variable.name].value} />
|
||||
</Layout.Stack>
|
||||
<Popover placement="bottom-end" let:toggle>
|
||||
<Button
|
||||
|
||||
+26
-24
@@ -130,31 +130,33 @@
|
||||
</Tabs.Item.Button>
|
||||
</Tabs.Root>
|
||||
|
||||
{#if tab === 'env'}
|
||||
<InputTextarea
|
||||
id="variables"
|
||||
bind:value={envCode}
|
||||
rows={10}
|
||||
placeholder={`SECRET_KEY=dQw4w9WgXcQ...`} />
|
||||
{:else if tab === 'json'}
|
||||
<InputTextarea
|
||||
id="variables"
|
||||
bind:value={jsonCode}
|
||||
rows={10}
|
||||
placeholder={`{\n "SECRET_KEY": "dQw4w9WgXcQ..."\n}`} />
|
||||
{/if}
|
||||
<Layout.Stack direction="row" gap="xs">
|
||||
<Button on:click={() => downloadVariables()} text>
|
||||
<Icon icon={IconDownload} />
|
||||
Download
|
||||
</Button>
|
||||
|
||||
<Copy value={tab == 'json' ? jsonCode : envCode}>
|
||||
<Button text>
|
||||
<Icon icon={IconDuplicate} />
|
||||
Copy
|
||||
<Layout.Stack gap="xxs">
|
||||
{#if tab === 'env'}
|
||||
<InputTextarea
|
||||
id="variables"
|
||||
bind:value={envCode}
|
||||
rows={10}
|
||||
placeholder={`SECRET_KEY=dQw4w9WgXcQ...`} />
|
||||
{:else if tab === 'json'}
|
||||
<InputTextarea
|
||||
id="variables"
|
||||
bind:value={jsonCode}
|
||||
rows={10}
|
||||
placeholder={`{\n "SECRET_KEY": "dQw4w9WgXcQ..."\n}`} />
|
||||
{/if}
|
||||
<Layout.Stack direction="row">
|
||||
<Button size="xs" on:click={() => downloadVariables()} compact>
|
||||
<Icon slot="start" icon={IconDownload} />
|
||||
Download
|
||||
</Button>
|
||||
</Copy>
|
||||
|
||||
<Copy value={tab == 'json' ? jsonCode : envCode}>
|
||||
<Button size="xs" compact>
|
||||
<Icon slot="start" icon={IconDuplicate} />
|
||||
Copy
|
||||
</Button>
|
||||
</Copy>
|
||||
</Layout.Stack>
|
||||
</Layout.Stack>
|
||||
</Layout.Stack>
|
||||
|
||||
|
||||
@@ -21,11 +21,12 @@
|
||||
IconXCircle
|
||||
} from '@appwrite.io/pink-icons-svelte';
|
||||
import { columns } from './store';
|
||||
import ActivateDeploymentModal from '../../activateDeploymentModal.svelte';
|
||||
|
||||
export let data: PageData;
|
||||
|
||||
let showDelete = false;
|
||||
// let showActivate = false;
|
||||
let showActivate = false;
|
||||
let showRedeploy = false;
|
||||
let showCancel = false;
|
||||
|
||||
@@ -139,7 +140,7 @@
|
||||
on:click={(e) => {
|
||||
e.preventDefault();
|
||||
selectedDeployment = deployment;
|
||||
// showActivate = true;
|
||||
showActivate = true;
|
||||
toggle(e);
|
||||
}}>
|
||||
Activate
|
||||
@@ -182,10 +183,16 @@
|
||||
|
||||
{#if selectedDeployment}
|
||||
<Delete {selectedDeployment} bind:showDelete />
|
||||
<!-- <Activate {selectedDeployment} bind:showActivate on:activated={handleActivate} /> -->
|
||||
|
||||
<Cancel {selectedDeployment} bind:showCancel />
|
||||
<RedeployModal
|
||||
selectedDeploymentId={selectedDeployment.$id}
|
||||
bind:show={showRedeploy}
|
||||
site={data.site} />
|
||||
{/if}
|
||||
{#if selectedDeployment && showActivate}
|
||||
<ActivateDeploymentModal
|
||||
siteId={data.site.$id}
|
||||
selectedDeploymentId={selectedDeployment.$id}
|
||||
bind:show={showActivate} />
|
||||
{/if}
|
||||
|
||||
+42
-18
@@ -16,20 +16,25 @@
|
||||
import DeploymentCreatedBy from '../(components)/deploymentCreatedBy.svelte';
|
||||
import DeploymentSource from '../(components)/deploymentSource.svelte';
|
||||
import Id from '$lib/components/id.svelte';
|
||||
import { IconDotsHorizontal, IconGlobeAlt, IconRefresh } from '@appwrite.io/pink-icons-svelte';
|
||||
import {
|
||||
IconDotsHorizontal,
|
||||
IconLightningBolt,
|
||||
IconRefresh,
|
||||
IconXCircle
|
||||
} from '@appwrite.io/pink-icons-svelte';
|
||||
import RedeployModal from '../redeployModal.svelte';
|
||||
import PromoteModal from '../promoteModal.svelte';
|
||||
import { Card } from '$lib/components';
|
||||
import ActivateDeploymentModal from '../activateDeploymentModal.svelte';
|
||||
import CancelDeploymentModal from './deployments/cancelDeploymentModal.svelte';
|
||||
|
||||
export let site: Models.Site;
|
||||
export let activeDeployment: Models.Deployment;
|
||||
export let deploymentList: Models.DeploymentList = undefined;
|
||||
|
||||
let showPromote = false;
|
||||
let showActivate = false;
|
||||
let showRedeploy = false;
|
||||
let showCancel = false;
|
||||
let selectedDeployment: Models.Deployment = null;
|
||||
|
||||
$: console.log(deploymentList);
|
||||
</script>
|
||||
|
||||
{#if deploymentList.total}
|
||||
@@ -106,16 +111,31 @@
|
||||
}}>
|
||||
Redeploy
|
||||
</ActionMenu.Item.Button>
|
||||
<ActionMenu.Item.Button
|
||||
leadingIcon={IconGlobeAlt}
|
||||
on:click={(e) => {
|
||||
e.preventDefault();
|
||||
selectedDeployment = deployment;
|
||||
showPromote = true;
|
||||
toggle(e);
|
||||
}}>
|
||||
Promote
|
||||
</ActionMenu.Item.Button>
|
||||
{#if deployment?.status === 'ready' && deployment?.$id !== site.deploymentId}
|
||||
<ActionMenu.Item.Button
|
||||
leadingIcon={IconLightningBolt}
|
||||
on:click={(e) => {
|
||||
e.preventDefault();
|
||||
selectedDeployment = deployment;
|
||||
showActivate = true;
|
||||
toggle(e);
|
||||
}}>
|
||||
Activate
|
||||
</ActionMenu.Item.Button>
|
||||
{/if}
|
||||
{#if deployment?.status === 'processing' || deployment?.status === 'building' || deployment.status === 'waiting'}
|
||||
<ActionMenu.Item.Button
|
||||
leadingIcon={IconXCircle}
|
||||
status="danger"
|
||||
on:click={(e) => {
|
||||
e.preventDefault();
|
||||
selectedDeployment = deployment;
|
||||
showCancel = true;
|
||||
toggle(e);
|
||||
}}>
|
||||
Cancel
|
||||
</ActionMenu.Item.Button>
|
||||
{/if}
|
||||
</ActionMenu.Root>
|
||||
</svelte:fragment>
|
||||
</Popover>
|
||||
@@ -135,9 +155,13 @@
|
||||
<RedeployModal {site} selectedDeploymentId={selectedDeployment.$id} bind:show={showRedeploy} />
|
||||
{/if}
|
||||
|
||||
{#if selectedDeployment && showPromote}
|
||||
<PromoteModal
|
||||
{#if selectedDeployment && showActivate}
|
||||
<ActivateDeploymentModal
|
||||
siteId={site.$id}
|
||||
selectedDeploymentId={selectedDeployment.$id}
|
||||
bind:show={showPromote} />
|
||||
bind:show={showActivate} />
|
||||
{/if}
|
||||
|
||||
{#if selectedDeployment && showCancel}
|
||||
<CancelDeploymentModal {selectedDeployment} bind:showCancel />
|
||||
{/if}
|
||||
|
||||
@@ -42,10 +42,12 @@
|
||||
<Layout.Stack alignItems="center" justifyContent="space-between" direction="row">
|
||||
<Layout.Stack gap="xs" inline>
|
||||
<Link variant="quiet-muted" href={`${$protocol}${rule.domain}`} external>
|
||||
<Trim alternativeTrim>
|
||||
{rule.domain}
|
||||
</Trim>
|
||||
<Icon icon={IconExternalLink} />
|
||||
<Layout.Stack gap="xs" inline direction="row" alignItems="center">
|
||||
<Trim alternativeTrim>
|
||||
{rule.domain}
|
||||
</Trim>
|
||||
<Icon icon={IconExternalLink} />
|
||||
</Layout.Stack>
|
||||
</Link>
|
||||
<Typography.Text truncate>
|
||||
Added {timeFromNow(rule.$createdAt)}
|
||||
|
||||
@@ -34,8 +34,12 @@ export function getFrameworkIcon(framework: string) {
|
||||
return 'astro';
|
||||
case framework.toLocaleLowerCase().includes('remix'):
|
||||
return 'remix';
|
||||
case framework.toLocaleLowerCase().includes('flutter'):
|
||||
return 'flutter';
|
||||
case framework.toLocaleLowerCase().includes('other'):
|
||||
return 'empty';
|
||||
|
||||
default:
|
||||
return framework.toLocaleLowerCase();
|
||||
return 'empty';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M18.8287 11.7565L17.5044 11.4945C17.5998 11.0122 17.65 10.5126 17.65 10C17.65 9.48736 17.5998 8.9878 17.5044 8.50553L18.8287 8.24354C18.9411 8.81163 19 9.39893 19 10C19 10.6011 18.9411 11.1884 18.8287 11.7565ZM17.4841 4.99946L16.3623 5.75049C15.8034 4.91566 15.0843 4.19662 14.2495 3.63772L15.0005 2.51591C15.982 3.17298 16.827 4.018 17.4841 4.99946ZM11.7565 1.17131L11.4945 2.49564C11.0122 2.40023 10.5126 2.35 10 2.35C9.48736 2.35 8.9878 2.40023 8.50553 2.49564L8.24354 1.17131C8.81163 1.05892 9.39893 1 10 1C10.6011 1 11.1884 1.05892 11.7565 1.17131ZM4.99946 2.51591L5.75049 3.63772C4.91566 4.19662 4.19662 4.91567 3.63772 5.75049L2.51591 4.99946C3.17298 4.018 4.018 3.17298 4.99946 2.51591ZM1.17131 8.24354C1.05892 8.81163 1 9.39893 1 10C1 10.6011 1.05892 11.1884 1.17131 11.7565L2.49564 11.4945C2.40023 11.0122 2.35 10.5126 2.35 10C2.35 9.48736 2.40023 8.9878 2.49564 8.50554L1.17131 8.24354ZM2.51591 15.0005L3.63772 14.2495C4.19662 15.0843 4.91567 15.8034 5.75049 16.3623L4.99946 17.4841C4.018 16.827 3.17298 15.982 2.51591 15.0005ZM8.24354 18.8287L8.50554 17.5044C8.9878 17.5998 9.48736 17.65 10 17.65C10.5126 17.65 11.0122 17.5998 11.4945 17.5044L11.7565 18.8287C11.1884 18.9411 10.6011 19 10 19C9.39893 19 8.81163 18.9411 8.24354 18.8287ZM15.0005 17.4841C15.982 16.827 16.827 15.982 17.4841 15.0005L16.3623 14.2495C15.8034 15.0843 15.0843 15.8034 14.2495 16.3623L15.0005 17.4841Z" fill="#EDEDF0"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M18.8287 11.7565L17.5044 11.4945C17.5998 11.0122 17.65 10.5126 17.65 10C17.65 9.48736 17.5998 8.9878 17.5044 8.50553L18.8287 8.24354C18.9411 8.81163 19 9.39893 19 10C19 10.6011 18.9411 11.1884 18.8287 11.7565ZM17.4841 4.99946L16.3623 5.75049C15.8034 4.91566 15.0843 4.19662 14.2495 3.63772L15.0005 2.51591C15.982 3.17298 16.827 4.018 17.4841 4.99946ZM11.7565 1.17131L11.4945 2.49564C11.0122 2.40023 10.5126 2.35 10 2.35C9.48736 2.35 8.9878 2.40023 8.50553 2.49564L8.24354 1.17131C8.81163 1.05892 9.39893 1 10 1C10.6011 1 11.1884 1.05892 11.7565 1.17131ZM4.99946 2.51591L5.75049 3.63772C4.91566 4.19662 4.19662 4.91567 3.63772 5.75049L2.51591 4.99946C3.17298 4.018 4.018 3.17298 4.99946 2.51591ZM1.17131 8.24354C1.05892 8.81163 1 9.39893 1 10C1 10.6011 1.05892 11.1884 1.17131 11.7565L2.49564 11.4945C2.40023 11.0122 2.35 10.5126 2.35 10C2.35 9.48736 2.40023 8.9878 2.49564 8.50554L1.17131 8.24354ZM2.51591 15.0005L3.63772 14.2495C4.19662 15.0843 4.91567 15.8034 5.75049 16.3623L4.99946 17.4841C4.018 16.827 3.17298 15.982 2.51591 15.0005ZM8.24354 18.8287L8.50554 17.5044C8.9878 17.5998 9.48736 17.65 10 17.65C10.5126 17.65 11.0122 17.5998 11.4945 17.5044L11.7565 18.8287C11.1884 18.9411 10.6011 19 10 19C9.39893 19 8.81163 18.9411 8.24354 18.8287ZM15.0005 17.4841C15.982 16.827 16.827 15.982 17.4841 15.0005L16.3623 14.2495C15.8034 15.0843 15.0843 15.8034 14.2495 16.3623L15.0005 17.4841Z" fill="#EDEDF0"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M18.8287 11.7565L17.5044 11.4945C17.5998 11.0122 17.65 10.5126 17.65 10C17.65 9.48736 17.5998 8.9878 17.5044 8.50553L18.8287 8.24354C18.9411 8.81163 19 9.39893 19 10C19 10.6011 18.9411 11.1884 18.8287 11.7565ZM17.4841 4.99946L16.3623 5.75049C15.8034 4.91566 15.0843 4.19662 14.2495 3.63772L15.0005 2.51591C15.982 3.17298 16.827 4.018 17.4841 4.99946ZM11.7565 1.17131L11.4945 2.49564C11.0122 2.40023 10.5126 2.35 10 2.35C9.48736 2.35 8.9878 2.40023 8.50553 2.49564L8.24354 1.17131C8.81163 1.05892 9.39893 1 10 1C10.6011 1 11.1884 1.05892 11.7565 1.17131ZM4.99946 2.51591L5.75049 3.63772C4.91566 4.19662 4.19662 4.91567 3.63772 5.75049L2.51591 4.99946C3.17298 4.018 4.018 3.17298 4.99946 2.51591ZM1.17131 8.24354C1.05892 8.81163 1 9.39893 1 10C1 10.6011 1.05892 11.1884 1.17131 11.7565L2.49564 11.4945C2.40023 11.0122 2.35 10.5126 2.35 10C2.35 9.48736 2.40023 8.9878 2.49564 8.50554L1.17131 8.24354ZM2.51591 15.0005L3.63772 14.2495C4.19662 15.0843 4.91567 15.8034 5.75049 16.3623L4.99946 17.4841C4.018 16.827 3.17298 15.982 2.51591 15.0005ZM8.24354 18.8287L8.50554 17.5044C8.9878 17.5998 9.48736 17.65 10 17.65C10.5126 17.65 11.0122 17.5998 11.4945 17.5044L11.7565 18.8287C11.1884 18.9411 10.6011 19 10 19C9.39893 19 8.81163 18.9411 8.24354 18.8287ZM15.0005 17.4841C15.982 16.827 16.827 15.982 17.4841 15.0005L16.3623 14.2495C15.8034 15.0843 15.0843 15.8034 14.2495 16.3623L15.0005 17.4841Z" fill="#2D2D31"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M18.8287 11.7565L17.5044 11.4945C17.5998 11.0122 17.65 10.5126 17.65 10C17.65 9.48736 17.5998 8.9878 17.5044 8.50553L18.8287 8.24354C18.9411 8.81163 19 9.39893 19 10C19 10.6011 18.9411 11.1884 18.8287 11.7565ZM17.4841 4.99946L16.3623 5.75049C15.8034 4.91566 15.0843 4.19662 14.2495 3.63772L15.0005 2.51591C15.982 3.17298 16.827 4.018 17.4841 4.99946ZM11.7565 1.17131L11.4945 2.49564C11.0122 2.40023 10.5126 2.35 10 2.35C9.48736 2.35 8.9878 2.40023 8.50553 2.49564L8.24354 1.17131C8.81163 1.05892 9.39893 1 10 1C10.6011 1 11.1884 1.05892 11.7565 1.17131ZM4.99946 2.51591L5.75049 3.63772C4.91566 4.19662 4.19662 4.91567 3.63772 5.75049L2.51591 4.99946C3.17298 4.018 4.018 3.17298 4.99946 2.51591ZM1.17131 8.24354C1.05892 8.81163 1 9.39893 1 10C1 10.6011 1.05892 11.1884 1.17131 11.7565L2.49564 11.4945C2.40023 11.0122 2.35 10.5126 2.35 10C2.35 9.48736 2.40023 8.9878 2.49564 8.50554L1.17131 8.24354ZM2.51591 15.0005L3.63772 14.2495C4.19662 15.0843 4.91567 15.8034 5.75049 16.3623L4.99946 17.4841C4.018 16.827 3.17298 15.982 2.51591 15.0005ZM8.24354 18.8287L8.50554 17.5044C8.9878 17.5998 9.48736 17.65 10 17.65C10.5126 17.65 11.0122 17.5998 11.4945 17.5044L11.7565 18.8287C11.1884 18.9411 10.6011 19 10 19C9.39893 19 8.81163 18.9411 8.24354 18.8287ZM15.0005 17.4841C15.982 16.827 16.827 15.982 17.4841 15.0005L16.3623 14.2495C15.8034 15.0843 15.0843 15.8034 14.2495 16.3623L15.0005 17.4841Z" fill="#2D2D31"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
Reference in New Issue
Block a user