Merge branch '1.6.x' of https://github.com/appwrite/console into feat-custom-execution-headers

This commit is contained in:
Torsten Dittmann
2024-08-12 09:52:38 +02:00
48 changed files with 521 additions and 326 deletions
+1
View File
@@ -61,6 +61,7 @@ Follow the [install instructions](https://appwrite.io/docs/advanced/self-hosting
### 4. Setup environment variables
Add a `.env` file by copying the `.env.example` file as a template in the project's root directory.
> **Note**
> If you are updating from Appwrite `1.5.x`, be aware that the variables for the console in the `.env` / `.env.example` file have changed in `1.6.x`.
+1 -1
View File
@@ -120,7 +120,7 @@
bind:this={tooltip}
class:u-width-full-line={fullWidth}
style:--arrow-size={`${arrowSize}px`}
style:z-index="10">
style:z-index="21">
<div
class="drop-arrow"
class:is-popover={isPopover}
+2 -3
View File
@@ -31,9 +31,8 @@
<div
class="drop is-no-arrow"
class:u-max-width-100-percent={fullWidth}
style={`${width ? `--drop-width-size-desktop:${width}rem; ` : ''} ${
position === 'static' ? 'position:static' : 'position:relative'
}`}>
style:--drop-width-size-desktop={width ? `${width}rem` : ''}
style:position>
{#if $$slots.list}
<section
class:u-overflow-y-auto={scrollable}
+1 -1
View File
@@ -28,7 +28,7 @@
:global(.theme-dark) .floating-action-bar {
border: 1px solid hsl(var(--color-neutral-85));
background: hsl(var(--color-neutral-90));
box-shadow: 0px 6px 16px 8px #14141f;
box-shadow: 0px 6px 16px 8px hsl(var(--color-neutral-105));
}
:global(.theme-light) .floating-action-bar {
+1 -1
View File
@@ -17,7 +17,7 @@
$organization?.billingPlan === BillingPlan.PRO ||
$organization?.billingPlan === BillingPlan.SCALE;
$: supportTimings = `${utcHourToLocaleHour('09:00')} - ${utcHourToLocaleHour('17:00')} ${localeTimezoneName()}`;
$: supportTimings = `${utcHourToLocaleHour('16:00')} - ${utcHourToLocaleHour('00:00')} ${localeTimezoneName()}`;
</script>
{#if isCloud}
+77 -38
View File
@@ -65,200 +65,239 @@ export const scopes: {
scope: string;
description: string;
category: string;
icon: string;
}[] = [
{
scope: 'sessions.write',
description: "Access to create, update and delete your project's sessions",
category: 'Auth'
category: 'Auth',
icon: 'user-group'
},
{
scope: 'users.read',
description: "Access to read your project's users",
category: 'Auth'
category: 'Auth',
icon: 'user-group'
},
{
scope: 'users.write',
description: "Access to create, update, and delete your project's users",
category: 'Auth'
category: 'Auth',
icon: 'user-group'
},
{
scope: 'teams.read',
description: "Access to read your project's teams",
category: 'Auth'
category: 'Auth',
icon: 'user-group'
},
{
scope: 'teams.write',
description: "Access to create, update, and delete your project's teams",
category: 'Auth'
category: 'Auth',
icon: 'user-group'
},
{
scope: 'databases.read',
description: "Access to read your project's databases",
category: 'Database'
category: 'Database',
icon: 'database'
},
{
scope: 'databases.write',
description: "Access to create, update, and delete your project's databases",
category: 'Database'
category: 'Database',
icon: 'database'
},
{
scope: 'collections.read',
description: "Access to read your project's database collections",
category: 'Database'
category: 'Database',
icon: 'database'
},
{
scope: 'collections.write',
description: "Access to create, update, and delete your project's database collections",
category: 'Database'
category: 'Database',
icon: 'database'
},
{
scope: 'attributes.read',
description: "Access to read your project's database collection's attributes",
category: 'Database'
category: 'Database',
icon: 'database'
},
{
scope: 'attributes.write',
description:
"Access to create, update, and delete your project's database collection's attributes",
category: 'Database'
category: 'Database',
icon: 'database'
},
{
scope: 'indexes.read',
description: "Access to read your project's database collection's indexes",
category: 'Database'
category: 'Database',
icon: 'database'
},
{
scope: 'indexes.write',
description:
"Access to create, update, and delete your project's database collection's indexes",
category: 'Database'
category: 'Database',
icon: 'database'
},
{
scope: 'documents.read',
description: "Access to read your project's database documents",
category: 'Database'
category: 'Database',
icon: 'database'
},
{
scope: 'documents.write',
description: "Access to create, update, and delete your project's database documents",
category: 'Database'
category: 'Database',
icon: 'database'
},
{
scope: 'files.read',
description: "Access to read your project's storage files and preview images",
category: 'Storage'
category: 'Storage',
icon: 'folder'
},
{
scope: 'files.write',
description: "Access to create, update, and delete your project's storage files",
category: 'Storage'
category: 'Storage',
icon: 'folder'
},
{
scope: 'buckets.read',
description: "Access to read your project's storage buckets",
category: 'Storage'
category: 'Storage',
icon: 'folder'
},
{
scope: 'buckets.write',
description: "Access to create, update, and delete your project's storage buckets",
category: 'Storage'
category: 'Storage',
icon: 'folder'
},
{
scope: 'functions.read',
description: "Access to read your project's functions and code deployments",
category: 'Functions'
category: 'Functions',
icon: 'lightning-bolt'
},
{
scope: 'functions.write',
description:
"Access to create, update, and delete your project's functions and code deployments",
category: 'Functions'
category: 'Functions',
icon: 'lightning-bolt'
},
{
scope: 'execution.read',
description: "Access to read your project's execution logs",
category: 'Functions'
category: 'Functions',
icon: 'lightning-bolt'
},
{
scope: 'execution.write',
description: "Access to execute your project's functions",
category: 'Functions'
category: 'Functions',
icon: 'lightning-bolt'
},
{
scope: 'targets.read',
description: "Access to read your project's messaging targets",
category: 'Messaging'
category: 'Messaging',
icon: 'send'
},
{
scope: 'targets.write',
description: "Access to create, update, and delete your project's messaging targets",
category: 'Messaging'
category: 'Messaging',
icon: 'send'
},
{
scope: 'providers.read',
description: "Access to read your project's messaging providers",
category: 'Messaging'
category: 'Messaging',
icon: 'send'
},
{
scope: 'providers.write',
description: "Access to create, update, and delete your project's messaging providers",
category: 'Messaging'
category: 'Messaging',
icon: 'send'
},
{
scope: 'messages.read',
description: "Access to read your project's messages",
category: 'Messaging'
category: 'Messaging',
icon: 'send'
},
{
scope: 'messages.write',
description: "Access to create, update, and delete your project's messages",
category: 'Messaging'
category: 'Messaging',
icon: 'send'
},
{
scope: 'topics.read',
description: "Access to read your project's messaging topics",
category: 'Messaging'
category: 'Messaging',
icon: 'send'
},
{
scope: 'topics.write',
description: "Access to create, update, and delete your project's messaging topics",
category: 'Messaging'
category: 'Messaging',
icon: 'send'
},
{
scope: 'subscribers.read',
description: "Access to read your project's messaging topic subscribers",
category: 'Messaging'
category: 'Messaging',
icon: 'send'
},
{
scope: 'subscribers.write',
description:
"Access to create, update, and delete your project's messaging topic subscribers",
category: 'Messaging'
category: 'Messaging',
icon: 'send'
},
{
scope: 'locale.read',
description: "Access to access your project's Locale service",
category: 'Other'
category: 'Other',
icon: 'globe'
},
{
scope: 'avatars.read',
description: "Access to access your project's Avatars service",
category: 'Other'
category: 'Other',
icon: 'globe'
},
{
scope: 'health.read',
description: "Access to read your project's health status",
category: 'Other'
category: 'Other',
icon: 'globe'
},
{
scope: 'migrations.read',
description: "Access to read your project's migration status",
category: 'Other'
category: 'Other',
icon: 'globe'
},
{
scope: 'migrations.write',
description: 'Access to create migrations',
category: 'Other'
category: 'Other',
icon: 'globe'
}
];
+6 -5
View File
@@ -32,7 +32,7 @@
}
function isFileExtensionAllowed(fileExtension: string) {
if (allowedFileExtensions.length && !allowedFileExtensions.includes(fileExtension)) {
if (allowedFileExtensions?.length && !allowedFileExtensions.includes(fileExtension)) {
return false;
}
return true;
@@ -42,7 +42,7 @@
hovering = false;
if (!ev.dataTransfer.items) return;
for (let i = 0; i < ev.dataTransfer.items.length; i++) {
const fileExtension = ev.dataTransfer.items[i].getAsFile().name.split('.')[1];
const fileExtension = ev.dataTransfer.items[i].getAsFile().name.split('.').pop();
if (!isFileExtensionAllowed(fileExtension)) {
error = 'Invalid file extension';
return;
@@ -183,15 +183,16 @@
{#if files?.length}
<ul class="upload-file-box-list u-min-width-0">
{#each fileArray as file}
{@const fileName = file.name.split('.')}
{@const fileName = file.name.split('.').slice(0, -1).join('.')}
{@const extension = file.name.split('.').pop()}
{@const fileSize = humanFileSize(file.size)}
<li class="u-flex u-cross-center u-min-width-0">
<span class="icon-document" aria-hidden="true" />
<span class="upload-file-box-name u-trim u-min-width-0">
<Trim>{fileName[0]}</Trim>
<Trim>{fileName}</Trim>
</span>
<span class="upload-file-box-name u-min-width-0 u-flex-shrink-0">
.{fileName[1]}
.{extension}
</span>
<span
class="upload-file-box-size u-margin-inline-start-4 u-margin-inline-end-16">
+43 -12
View File
@@ -1,7 +1,7 @@
<script lang="ts">
import { hoursToDays, toLocaleDateTime } from '$lib/helpers/date';
import { hoursToDays, timeFromNow, toLocaleDateTime } from '$lib/helpers/date';
import { log } from '$lib/stores/logs';
import { Alert, Card, Code, Heading, Id, SvgIcon, Tab, Tabs } from '../components';
import { Alert, Card, Code, Copy, Heading, Id, SvgIcon, Tab, Tabs } from '../components';
import { calculateTime } from '$lib/helpers/timeConversion';
import {
TableBody,
@@ -18,6 +18,7 @@
import { organization } from '$lib/stores/organization';
import { Button } from '$lib/elements/forms';
import { BillingPlan } from '$lib/constants';
import { tooltip } from '$lib/actions/tooltip';
let selectedRequest = 'parameters';
let selectedResponse = 'logs';
@@ -124,13 +125,26 @@
{/if}
</ul>
<div class="status u-margin-inline-start-auto">
<Pill
warning={execution.status === 'waiting' ||
execution.status === 'building'}
danger={execution.status === 'failed'}
info={execution.status === 'completed' || execution.status === 'ready'}>
{execution.status}
</Pill>
<div
use:tooltip={{
content: `Scheduled to execute on ${toLocaleDateTime(execution.scheduledAt)}`,
disabled:
!execution?.scheduledAt || execution.status !== 'scheduled'
}}>
<Pill
warning={execution.status === 'waiting' ||
execution.status === 'building'}
danger={execution.status === 'failed'}
info={execution.status === 'completed' ||
execution.status === 'ready'}>
{#if execution.status === 'scheduled'}
<span class="icon-clock" aria-hidden="true" />
{timeFromNow(execution.scheduledAt)}
{:else}
{execution.status}
{/if}
</Pill>
</div>
</div>
</div>
</div>
@@ -145,7 +159,23 @@
</div>
<div class="u-flex u-gap-16">
<h4 class="text u-bold">Path:</h4>
<span class="u-text-color-gray">{execution.requestPath}</span>
<Copy value={execution.requestPath}>
<div
class="interactive-text-output is-textarea"
style:min-inline-size="0">
<span class="text u-line-height-1-5 u-break-all">
{execution.requestPath}
</span>
<div class="u-flex u-cross-child-start u-gap-8">
<button
class="interactive-text-output-button"
aria-label="copy text">
<span class="icon-duplicate" aria-hidden="true" />
</button>
</div>
</div>
</Copy>
</div>
</div>
@@ -156,8 +186,9 @@
</div>
<div class="u-flex u-gap-16">
<h4 class="text u-bold">Status Code:</h4>
<span class="u-text-color-gray"
>{execution.responseStatusCode}</span>
<span class="u-text-color-gray">
{execution.responseStatusCode}
</span>
</div>
</div>
</header>
+13 -2
View File
@@ -139,7 +139,18 @@
}
$: sortedSteps = [...steps].sort(([a], [b]) => (a > b ? 1 : -1));
$: isLastStep = $wizard.step === steps.size;
$: isLastStep = (() => {
if ($wizard.step === steps.size) {
return true;
}
let lastStep = true;
steps.forEach((step, i) => {
if (i > $wizard.step && !step.disabled) {
lastStep = false;
}
});
return lastStep;
})();
$: currentStep = steps.get($wizard.step);
</script>
@@ -187,7 +198,7 @@
<svelte:component this={component} />
{/if}
{/each}
<div class="u-z-index-20 form-footer">
<div class="u-z-index-5 form-footer">
<div class="u-flex u-main-end u-gap-12">
{#if !isLastStep && currentStep?.optional}
<Button text on:click={() => dispatch('finish')}>
+1
View File
@@ -111,6 +111,7 @@ export type Credit = {
};
export type CreditList = {
available: number;
credits: Credit[];
total: number;
};
+36
View File
@@ -84,6 +84,42 @@ campaigns
]
}
})
.set('FreeCodeCamp', {
template: 'card',
title: 'Claim your $50 FreeCodeCamp credits.',
description:
'Get $50 in Cloud credits when you upgrade or create an organization with a Pro plan'
})
.set('AniaKubow', {
template: 'card',
title: 'Claim your $50 Ania Kubów credits.',
description:
'Get $50 in Cloud credits when you upgrade or create an organization with a Pro plan'
})
.set('Fireship', {
template: 'card',
title: 'Claim your $50 Fireship credits.',
description:
'Get $50 in Cloud credits when you upgrade or create an organization with a Pro plan'
})
.set('Hyperplexed', {
template: 'card',
title: 'Claim your $50 Hyperplexed credits.',
description:
'Get $50 in Cloud credits when you upgrade or create an organization with a Pro plan'
})
.set('TraversyMedia', {
template: 'card',
title: 'Claim your $50 TraversyMedia credits.',
description:
'Get $50 in Cloud credits when you upgrade or create an organization with a Pro plan'
})
.set('VueJS', {
template: 'card',
title: 'Claim your $50 VueJS credits.',
description:
'Get $50 in Cloud credits when you upgrade or create an organization with a Pro plan'
})
.set('FusionVC', {
template: 'review',
title: 'Welcome to Appwrite!',
+42 -40
View File
@@ -65,7 +65,7 @@ export const marketplace: MarketplaceTemplate[] = [
vcsProvider: 'github',
providerRepositoryId: 'templates',
providerOwner: 'appwrite',
providerBranch: 'main',
providerBranch: '0.1.*',
variables: [
{
name: 'APPWRITE_API_KEY',
@@ -75,7 +75,8 @@ export const marketplace: MarketplaceTemplate[] = [
required: false,
type: 'password'
}
]
],
scopes: ['functions.read', 'functions.write', 'documents.write']
},
{
icon: 'icon-upstash',
@@ -99,7 +100,7 @@ export const marketplace: MarketplaceTemplate[] = [
vcsProvider: 'github',
providerRepositoryId: 'templates',
providerOwner: 'appwrite',
providerBranch: 'main',
providerBranch: '0.1.*',
variables: [
{
name: 'UPSTASH_URL',
@@ -142,7 +143,7 @@ export const marketplace: MarketplaceTemplate[] = [
vcsProvider: 'github',
providerRepositoryId: 'templates',
providerOwner: 'appwrite',
providerBranch: 'main',
providerBranch: '0.1.*',
variables: [
{
name: 'REDIS_HOST',
@@ -184,7 +185,7 @@ export const marketplace: MarketplaceTemplate[] = [
vcsProvider: 'github',
providerRepositoryId: 'templates',
providerOwner: 'appwrite',
providerBranch: 'main',
providerBranch: '0.1.*',
variables: [
{
name: 'NEO4J_URI',
@@ -235,7 +236,7 @@ export const marketplace: MarketplaceTemplate[] = [
vcsProvider: 'github',
providerRepositoryId: 'templates',
providerOwner: 'appwrite',
providerBranch: 'main',
providerBranch: '0.1.*',
variables: [
{
name: 'MONGO_URI',
@@ -271,7 +272,7 @@ export const marketplace: MarketplaceTemplate[] = [
vcsProvider: 'github',
providerRepositoryId: 'templates',
providerOwner: 'appwrite',
providerBranch: 'main',
providerBranch: '0.1.*',
variables: [
{
name: 'PGHOST',
@@ -355,7 +356,7 @@ export const marketplace: MarketplaceTemplate[] = [
vcsProvider: 'github',
providerRepositoryId: 'templates',
providerOwner: 'appwrite',
providerBranch: 'main',
providerBranch: '0.1.*',
variables: [
{
name: 'OPENAI_API_KEY',
@@ -403,7 +404,7 @@ export const marketplace: MarketplaceTemplate[] = [
vcsProvider: 'github',
providerRepositoryId: 'templates',
providerOwner: 'appwrite',
providerBranch: 'main',
providerBranch: '0.1.*',
variables: [
{
name: 'DISCORD_PUBLIC_KEY',
@@ -453,7 +454,7 @@ export const marketplace: MarketplaceTemplate[] = [
vcsProvider: 'github',
providerRepositoryId: 'templates',
providerOwner: 'appwrite',
providerBranch: 'main',
providerBranch: '0.1.*',
variables: [
{
name: 'PERSPECTIVE_API_KEY',
@@ -500,7 +501,7 @@ export const marketplace: MarketplaceTemplate[] = [
vcsProvider: 'github',
providerRepositoryId: 'templates',
providerOwner: 'appwrite',
providerBranch: 'main',
providerBranch: '0.1.*',
variables: [
{
name: 'PANGEA_REDACT_TOKEN',
@@ -529,7 +530,7 @@ export const marketplace: MarketplaceTemplate[] = [
vcsProvider: 'github',
providerRepositoryId: 'templates',
providerOwner: 'appwrite',
providerBranch: 'main',
providerBranch: '0.1.*',
variables: []
},
{
@@ -555,7 +556,7 @@ export const marketplace: MarketplaceTemplate[] = [
vcsProvider: 'github',
providerRepositoryId: 'templates',
providerOwner: 'appwrite',
providerBranch: 'main',
providerBranch: '0.1.*',
variables: [
{
name: 'GITHUB_TOKEN',
@@ -597,7 +598,7 @@ export const marketplace: MarketplaceTemplate[] = [
vcsProvider: 'github',
providerRepositoryId: 'templates',
providerOwner: 'appwrite',
providerBranch: 'main',
providerBranch: '0.1.*',
variables: [
{
name: 'APPWRITE_API_KEY',
@@ -675,7 +676,7 @@ export const marketplace: MarketplaceTemplate[] = [
vcsProvider: 'github',
providerRepositoryId: 'templates',
providerOwner: 'appwrite',
providerBranch: 'main',
providerBranch: '0.1.*',
variables: [
{
name: 'APPWRITE_API_KEY',
@@ -783,7 +784,7 @@ export const marketplace: MarketplaceTemplate[] = [
vcsProvider: 'github',
providerRepositoryId: 'templates',
providerOwner: 'appwrite',
providerBranch: 'main',
providerBranch: '0.1.*',
variables: [
{
name: 'APPWRITE_API_KEY',
@@ -897,7 +898,7 @@ export const marketplace: MarketplaceTemplate[] = [
vcsProvider: 'github',
providerRepositoryId: 'templates',
providerOwner: 'appwrite',
providerBranch: 'main',
providerBranch: '0.1.*',
variables: [
{
name: 'VONAGE_API_KEY',
@@ -952,7 +953,7 @@ export const marketplace: MarketplaceTemplate[] = [
vcsProvider: 'github',
providerRepositoryId: 'templates',
providerOwner: 'appwrite',
providerBranch: 'main',
providerBranch: '0.1.*',
variables: [
{
name: 'FCM_PROJECT_ID',
@@ -1019,7 +1020,7 @@ export const marketplace: MarketplaceTemplate[] = [
vcsProvider: 'github',
providerRepositoryId: 'templates',
providerOwner: 'appwrite',
providerBranch: 'main',
providerBranch: '0.1.*',
variables: [
{
name: 'SMTP_HOST',
@@ -1089,7 +1090,7 @@ export const marketplace: MarketplaceTemplate[] = [
vcsProvider: 'github',
providerRepositoryId: 'templates',
providerOwner: 'appwrite',
providerBranch: 'main',
providerBranch: '0.1.*',
variables: [
{
name: 'APPWRITE_API_KEY',
@@ -1145,7 +1146,7 @@ export const marketplace: MarketplaceTemplate[] = [
vcsProvider: 'github',
providerRepositoryId: 'templates',
providerOwner: 'appwrite',
providerBranch: 'main',
providerBranch: '0.1.*',
variables: [
{
name: 'APPWRITE_API_KEY',
@@ -1217,7 +1218,7 @@ export const marketplace: MarketplaceTemplate[] = [
vcsProvider: 'github',
providerRepositoryId: 'templates',
providerOwner: 'appwrite',
providerBranch: 'main',
providerBranch: '0.1.*',
variables: [
{
name: 'HUGGINGFACE_ACCESS_TOKEN',
@@ -1250,7 +1251,7 @@ export const marketplace: MarketplaceTemplate[] = [
vcsProvider: 'github',
providerRepositoryId: 'templates',
providerOwner: 'appwrite',
providerBranch: 'main',
providerBranch: '0.1.*',
variables: [
{
name: 'HUGGINGFACE_ACCESS_TOKEN',
@@ -1283,7 +1284,7 @@ export const marketplace: MarketplaceTemplate[] = [
vcsProvider: 'github',
providerRepositoryId: 'templates',
providerOwner: 'appwrite',
providerBranch: 'main',
providerBranch: '0.1.*',
variables: [
{
name: 'APPWRITE_API_KEY',
@@ -1356,7 +1357,7 @@ export const marketplace: MarketplaceTemplate[] = [
vcsProvider: 'github',
providerRepositoryId: 'templates',
providerOwner: 'appwrite',
providerBranch: 'main',
providerBranch: '0.1.*',
variables: [
{
name: 'APPWRITE_API_KEY',
@@ -1429,7 +1430,7 @@ export const marketplace: MarketplaceTemplate[] = [
vcsProvider: 'github',
providerRepositoryId: 'templates',
providerOwner: 'appwrite',
providerBranch: 'main',
providerBranch: '0.1.*',
variables: [
{
name: 'APPWRITE_API_KEY',
@@ -1502,7 +1503,7 @@ export const marketplace: MarketplaceTemplate[] = [
vcsProvider: 'github',
providerRepositoryId: 'templates',
providerOwner: 'appwrite',
providerBranch: 'main',
providerBranch: '0.1.*',
variables: [
{
name: 'APPWRITE_API_KEY',
@@ -1575,7 +1576,7 @@ export const marketplace: MarketplaceTemplate[] = [
vcsProvider: 'github',
providerRepositoryId: 'templates',
providerOwner: 'appwrite',
providerBranch: 'main',
providerBranch: '0.1.*',
variables: [
{
name: 'REPLICATE_API_KEY',
@@ -1609,7 +1610,7 @@ export const marketplace: MarketplaceTemplate[] = [
vcsProvider: 'github',
providerRepositoryId: 'templates',
providerOwner: 'appwrite',
providerBranch: 'main',
providerBranch: '0.1.*',
variables: [
{
name: 'TOGETHER_API_KEY',
@@ -1666,7 +1667,7 @@ export const marketplace: MarketplaceTemplate[] = [
vcsProvider: 'github',
providerRepositoryId: 'templates',
providerOwner: 'appwrite',
providerBranch: 'main',
providerBranch: '0.1.*',
variables: [
{
name: 'PERPLEXITY_API_KEY',
@@ -1706,7 +1707,7 @@ export const marketplace: MarketplaceTemplate[] = [
vcsProvider: 'github',
providerRepositoryId: 'templates',
providerOwner: 'appwrite',
providerBranch: 'main',
providerBranch: '0.1.*',
variables: [
{
name: 'REPLICATE_API_KEY',
@@ -1740,7 +1741,7 @@ export const marketplace: MarketplaceTemplate[] = [
vcsProvider: 'github',
providerRepositoryId: 'templates',
providerOwner: 'appwrite',
providerBranch: 'main',
providerBranch: '0.1.*',
variables: [
{
name: 'OPENAI_API_KEY',
@@ -1818,7 +1819,7 @@ export const marketplace: MarketplaceTemplate[] = [
vcsProvider: 'github',
providerRepositoryId: 'templates',
providerOwner: 'appwrite',
providerBranch: 'main',
providerBranch: '0.1.*',
variables: [
{
name: 'OPENAI_API_KEY',
@@ -1896,7 +1897,7 @@ export const marketplace: MarketplaceTemplate[] = [
vcsProvider: 'github',
providerRepositoryId: 'templates',
providerOwner: 'appwrite',
providerBranch: 'main',
providerBranch: '0.1.*',
variables: [
{
name: 'ELEVENLABS_API_KEY',
@@ -1966,7 +1967,7 @@ export const marketplace: MarketplaceTemplate[] = [
vcsProvider: 'github',
providerRepositoryId: 'templates',
providerOwner: 'appwrite',
providerBranch: 'main',
providerBranch: '0.1.*',
variables: [
{
name: 'LMNT_API_KEY',
@@ -2022,7 +2023,7 @@ export const marketplace: MarketplaceTemplate[] = [
vcsProvider: 'github',
providerRepositoryId: 'templates',
providerOwner: 'appwrite',
providerBranch: 'main',
providerBranch: '0.1.*',
variables: [
{
name: 'ANYSCALE_API_KEY',
@@ -2062,7 +2063,7 @@ export const marketplace: MarketplaceTemplate[] = [
vcsProvider: 'github',
providerRepositoryId: 'templates',
providerOwner: 'appwrite',
providerBranch: 'main',
providerBranch: '0.1.*',
variables: [
{
name: 'APPWRITE_API_KEY',
@@ -2119,7 +2120,7 @@ export const marketplace: MarketplaceTemplate[] = [
vcsProvider: 'github',
providerRepositoryId: 'templates',
providerOwner: 'appwrite',
providerBranch: 'main',
providerBranch: '0.1.*',
variables: [
{
name: 'FAL_API_KEY',
@@ -2153,7 +2154,7 @@ export const marketplace: MarketplaceTemplate[] = [
vcsProvider: 'github',
providerRepositoryId: 'templates',
providerOwner: 'appwrite',
providerBranch: 'main',
providerBranch: '0.1.*',
variables: [
{
name: 'APPWRITE_API_KEY',
@@ -2223,7 +2224,7 @@ export const marketplace: MarketplaceTemplate[] = [
vcsProvider: 'github',
providerRepositoryId: 'templates',
providerOwner: 'appwrite',
providerBranch: 'main',
providerBranch: '0.1.*',
variables: [
{
name: 'APPWRITE_API_KEY',
@@ -2322,4 +2323,5 @@ export type MarketplaceTemplate = {
providerOwner: string;
providerBranch: string;
variables: Variable[];
scopes?: string[];
};
@@ -1,39 +0,0 @@
<script lang="ts">
import { Box } from '$lib/components';
import { FormList, Helper, InputChoice, InputPassword } from '$lib/elements/forms';
import type { Variable } from '$lib/stores/marketplace';
import { templateConfig } from '../store';
export let appwriteVariable: Variable;
</script>
<Box radius="small" padding={16}>
<FormList>
<div>
<InputPassword
id={appwriteVariable.name}
label={appwriteVariable.name}
placeholder={appwriteVariable.placeholder ?? 'Enter value'}
showPasswordButton
required={appwriteVariable.required && !$templateConfig.generateKey}
bind:value={$templateConfig.appwriteApiKey}
disabled={!!$templateConfig.generateKey} />
<Helper type="neutral">
This API key will allow you to interact with the Appwrite server APIs. <a
href="https://appwrite.io/docs/advanced/platform/api-keys"
target="_blank"
rel="noopener noreferrer"
class="link">Learn more</a
>.
</Helper>
</div>
<InputChoice
bind:value={$templateConfig.generateKey}
id="generate"
label="Generate API key on completion"
disabled={!!$templateConfig.appwriteApiKey}>
The <code class="inline-code">APPWRITE_API_KEY</code> will be automatically generated for
your project and applied to this function once it's created.
</InputChoice>
</FormList>
</Box>
@@ -16,8 +16,8 @@
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
import { base } from '$app/paths';
import { page } from '$app/stores';
import Details from './steps/details.svelte';
import Configuration from './steps/configuration.svelte';
import Details from './steps/manualDetails.svelte';
import Configuration from './steps/manualConfiguration.svelte';
import ExecuteAccess from './steps/executeAccess.svelte';
import { isValueOfStringEnum } from '$lib/helpers/types';
+60 -40
View File
@@ -1,22 +1,28 @@
<script lang="ts">
import { ID, Runtime } from '@appwrite.io/console';
import { Wizard } from '$lib/layout';
import type { WizardStepsType } from '$lib/layout/wizard.svelte';
import { sdk } from '$lib/stores/sdk';
import { wizard } from '$lib/stores/wizard';
import { goto } from '$app/navigation';
import { choices, installation, repository, template, templateConfig } from './store';
import {
choices,
installation,
repository,
template,
templateConfig,
templateStepsComponents
} from './store';
import { addNotification } from '$lib/stores/notifications';
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
import { base } from '$app/paths';
import { page } from '$app/stores';
import GitConfiguration from './steps/gitConfiguration.svelte';
import TemplateConfiguration from './steps/templateConfiguration.svelte';
import RepositoryBehaviour from './steps/repositoryBehaviour.svelte';
import CreateRepository from './steps/createRepository.svelte';
import TemplateVariables from './steps/templateVariables.svelte';
import { scopes } from '$lib/constants';
import { isValueOfStringEnum } from '$lib/helpers/types';
import TemplateConfiguration from './steps/templateConfiguration.svelte';
import TemplatePermissions from './steps/templatePermissions.svelte';
import TemplateVariables from './steps/templateVariables.svelte';
import TemplateDeployment from './steps/templateDeployment.svelte';
import CreateRepository from './steps/createRepository.svelte';
import GitConfiguration from './steps/gitConfiguration.svelte';
async function create() {
try {
@@ -26,22 +32,12 @@
const runtimeDetail = $template.runtimes.find(
(r) => r.name === $templateConfig.runtime
);
if ($templateConfig.appwriteApiKey) {
$templateConfig.variables['APPWRITE_API_KEY'] = $templateConfig.appwriteApiKey;
} else if ($templateConfig?.generateKey) {
const key = await sdk.forConsole.projects.createKey(
$page.params.project,
'Generated for Template',
scopes.map((scope) => scope.scope)
);
$templateConfig.variables['APPWRITE_API_KEY'] = key.secret;
}
const response = await sdk.forProject.functions.create(
$templateConfig.$id || ID.unique(),
$templateConfig.name,
$templateConfig.runtime,
$template.permissions || undefined,
$templateConfig?.execute ? $template.permissions || undefined : undefined,
$template.events || undefined,
$template.cron || undefined,
$template.timeout || undefined,
@@ -49,16 +45,28 @@
undefined,
runtimeDetail.entrypoint,
runtimeDetail.commands || undefined,
undefined,
$installation.$id,
$repository.id,
$choices.branch,
$choices.silentMode || undefined,
$choices.rootDir || undefined,
$template.providerRepositoryId,
$template.providerOwner,
runtimeDetail.providerRootDirectory,
$template.providerBranch
$templateConfig?.scopes?.length ? $templateConfig.scopes : undefined,
$templateConfig.repositoryBehaviour === 'manual' ? undefined : $installation.$id,
$templateConfig.repositoryBehaviour === 'manual' ? undefined : $repository.id,
$templateConfig.repositoryBehaviour === 'manual' ? undefined : $choices.branch,
$templateConfig.repositoryBehaviour === 'manual'
? undefined
: $choices.silentMode || undefined,
$templateConfig.repositoryBehaviour === 'manual'
? undefined
: $choices.rootDir || undefined,
$templateConfig.repositoryBehaviour === 'manual'
? undefined
: $template.providerRepositoryId,
$templateConfig.repositoryBehaviour === 'manual'
? undefined
: $template.providerOwner,
$templateConfig.repositoryBehaviour === 'manual'
? undefined
: runtimeDetail.providerRootDirectory,
$templateConfig.repositoryBehaviour === 'manual'
? undefined
: $template.providerBranch
);
if ($templateConfig.variables) {
@@ -75,7 +83,10 @@
type: 'success'
});
trackEvent(Submit.FunctionCreate, {
customId: !!response.$id
customId: !!response.$id,
runtime: response.runtime,
deployment_type: $templateConfig.repositoryBehaviour,
scopes: $templateConfig.scopes
});
resetState();
} catch (error) {
@@ -93,27 +104,36 @@
installation.set(null);
}
const stepsComponents: WizardStepsType = new Map();
stepsComponents.set(1, {
$templateStepsComponents.set(1, {
label: 'Configuration',
component: TemplateConfiguration
});
stepsComponents.set(2, {
$templateStepsComponents.set(2, {
label: 'Permissions',
component: TemplatePermissions,
disabled: !$template?.scopes?.length
});
$templateStepsComponents.set(3, {
label: 'Variables',
component: TemplateVariables
component: TemplateVariables,
disabled: !$template?.variables?.length
});
stepsComponents.set(3, {
label: 'Connect',
component: RepositoryBehaviour
$templateStepsComponents.set(4, {
label: 'Deployment',
component: TemplateDeployment
});
stepsComponents.set(4, {
$templateStepsComponents.set(5, {
label: 'Repository',
component: CreateRepository
});
stepsComponents.set(5, {
$templateStepsComponents.set(6, {
label: 'Branch',
component: GitConfiguration
});
</script>
<Wizard title="Create Function" steps={stepsComponents} on:finish={create} on:exit={resetState} />
<Wizard
title="Create Function"
steps={$templateStepsComponents}
on:finish={create}
on:exit={resetState} />
@@ -1,41 +0,0 @@
<script lang="ts">
import { LabelCard } from '$lib/components';
import { WizardStep } from '$lib/layout';
import { app } from '$lib/stores/app';
import { templateConfig } from '../store';
async function beforeSubmit() {
if (!$templateConfig.repositoryBehaviour) {
throw new Error('Please select repository behaviour.');
}
}
</script>
<WizardStep {beforeSubmit}>
<svelte:fragment slot="title">Connect</svelte:fragment>
<svelte:fragment slot="subtitle">
Connect function to a new repository or to an existing one within a selected Git
organization.
</svelte:fragment>
<ul class="u-flex u-flex-vertical u-gap-16">
<LabelCard
name="behaviour"
value="new"
backgroundColor={$app.themeInUse === 'light' ? 'var(--color-neutral-5)' : null}
backgroundColorHover={$app.themeInUse === 'light' ? 'var(--color-neutral-10)' : null}
bind:group={$templateConfig.repositoryBehaviour}>
<svelte:fragment slot="title">Create a new repository</svelte:fragment>
Clone the template and create a new repository in your selected organization.
</LabelCard>
<LabelCard
name="behaviour"
value="existing"
backgroundColor={$app.themeInUse === 'light' ? 'var(--color-neutral-5)' : null}
backgroundColorHover={$app.themeInUse === 'light' ? 'var(--color-neutral-10)' : null}
bind:group={$templateConfig.repositoryBehaviour}>
<svelte:fragment slot="title">Add to existing repository</svelte:fragment>
Clone the template to an existing repository in your selected organization.
</LabelCard>
</ul>
</WizardStep>
@@ -0,0 +1,53 @@
<script lang="ts">
import { LabelCard } from '$lib/components';
import { FormList } from '$lib/elements/forms';
import { WizardStep } from '$lib/layout';
import { templateConfig, templateStepsComponents } from '../store';
async function beforeSubmit() {
if (!$templateConfig.repositoryBehaviour) {
throw new Error('Please select repository behaviour.');
}
}
$: if ($templateConfig.repositoryBehaviour === 'manual') {
$templateStepsComponents.get(5).disabled = true;
$templateStepsComponents.get(6).disabled = true;
$templateStepsComponents = $templateStepsComponents;
} else {
$templateStepsComponents.get(5).disabled = false;
$templateStepsComponents.get(6).disabled = false;
$templateStepsComponents = $templateStepsComponents;
}
</script>
<WizardStep {beforeSubmit}>
<svelte:fragment slot="title">Deployment</svelte:fragment>
<svelte:fragment slot="subtitle">
Connect your function to a Git repository for automatic deployments or deploy your function
using the CLI.
</svelte:fragment>
<h3>Automatic with Git <span class="inline-code">Recommended</span></h3>
<FormList gap={16} class="u-margin-block-start-8">
<LabelCard name="behaviour" value="new" bind:group={$templateConfig.repositoryBehaviour}>
<svelte:fragment slot="title">Create a new repository</svelte:fragment>
Clone the template to a newly created repository in your organization.
</LabelCard>
<LabelCard
name="behaviour"
value="existing"
bind:group={$templateConfig.repositoryBehaviour}>
<svelte:fragment slot="title">Add to existing repository</svelte:fragment>
Clone the template to an existing repository in your organization.
</LabelCard>
</FormList>
<h3 class="u-margin-block-start-24">Manual with CLI</h3>
<ul class="u-margin-block-start-8">
<LabelCard name="behaviour" value="manual" bind:group={$templateConfig.repositoryBehaviour}>
<svelte:fragment slot="title">Create manually</svelte:fragment>
Deploy a function from a template and change the code using the CLI.
</LabelCard>
</ul>
</WizardStep>
@@ -0,0 +1,86 @@
<script lang="ts">
import { scopes } from '$lib/constants';
import { FormList, InputChoice } from '$lib/elements/forms';
import { WizardStep } from '$lib/layout';
import { onMount } from 'svelte';
import { template, templateConfig } from '../store';
import { tooltip } from '$lib/actions/tooltip';
let templateScopes = [];
onMount(() => {
$templateConfig.execute = $template.permissions.includes('any');
templateScopes = scopes
.filter((scope) => $template.scopes.includes(scope.scope))
.map((scope) => ({
...scope,
active: true
}));
});
$: $templateConfig.scopes = templateScopes
?.filter((scope) => scope.active)
?.map((scope) => scope.scope);
</script>
<WizardStep>
<svelte:fragment slot="title">Permissions</svelte:fragment>
<svelte:fragment slot="subtitle">
Enable recommended scopes and execute access for when your function is deployed.
</svelte:fragment>
<h3 class="label">Execute permissions</h3>
<FormList class="u-margin-block-start-16">
<div class="user-profile">
<span class="avatar" style:--p-text-size="1rem">
<span class="icon-lock-open" />
</span>
<span class="user-profile-info u-flex u-main-space-between u-gap-16">
<div>
<p class="name u-bold">
Public (anyone can execute) <span
class="icon-info"
use:tooltip={{
content:
'You can further customize execute permissions in your function settings.'
}} />
</p>
<p class="text u-margin-block-start-4">
This could include unauthorized users and search engines.
</p>
</div>
<InputChoice
type="switchbox"
id="execute"
label="Execute"
showLabel={false}
bind:value={$templateConfig.execute}></InputChoice>
</span>
</div>
</FormList>
<h3 class="label u-margin-block-start-48">Function scopes</h3>
<FormList class="u-margin-block-start-16">
{#each templateScopes as scope, i}
<div class="user-profile">
<span class="avatar" style:--p-text-size="1rem">
<span class={`icon-${scope.icon}`} />
</span>
<span class="user-profile-info u-flex u-main-space-between u-gap-16">
<div>
<p class="name u-bold">{scope.scope}</p>
<p class="text u-margin-block-start-4">{scope.description}</p>
</div>
<InputChoice
type="switchbox"
id={scope.scope}
label={scope.scope}
showLabel={false}
bind:value={scope.active}>
</InputChoice>
</span>
</div>
{#if i < templateScopes.length - 1}
<div class="with-separators"></div>
{/if}
{/each}
</FormList>
</WizardStep>
@@ -12,7 +12,6 @@
InputNumber
} from '$lib/elements/forms';
import { Card, Collapsible, CollapsibleItem } from '$lib/components';
import AppwriteVariable from '../components/appwriteVariable.svelte';
import type { SvelteComponent } from 'svelte';
async function beforeSubmit() {
@@ -20,10 +19,6 @@
if (!variable.required) {
continue;
}
if (variable.name === 'APPWRITE_API_KEY') {
if ($templateConfig.appwriteApiKey || $templateConfig.generateKey) continue;
else throw new Error(`Please set ${variable.name} variable or generate it.`);
}
if (!$templateConfig.variables[variable.name]) {
throw new Error(`Please set ${variable.name} variable.`);
}
@@ -72,25 +67,21 @@
<FormList>
{#each requiredVariables as variable}
{#if variable.name === 'APPWRITE_API_KEY'}
<AppwriteVariable appwriteVariable={variable} />
{:else}
<div>
<svelte:component
this={selectComponent(variable.type)}
id={variable.name}
label={variable.name}
placeholder={variable.placeholder ?? 'Enter value'}
required={variable.required}
autocomplete={false}
minlength={variable.type === 'password' ? 0 : null}
showPasswordButton={variable.type === 'password'}
bind:value={$templateConfig.variables[variable.name]} />
<Helper type="neutral">
{@html variable.description}
</Helper>
</div>
{/if}
<div>
<svelte:component
this={selectComponent(variable.type)}
id={variable.name}
label={variable.name}
placeholder={variable.placeholder ?? 'Enter value'}
required={variable.required}
autocomplete={false}
minlength={variable.type === 'password' ? 0 : null}
showPasswordButton={variable.type === 'password'}
bind:value={$templateConfig.variables[variable.name]} />
<Helper type="neutral">
{@html variable.description}
</Helper>
</div>
{/each}
</FormList>
</CollapsibleItem>
@@ -107,25 +98,21 @@
<FormList>
{#each optionalVariables as variable}
{#if variable.name === 'APPWRITE_API_KEY'}
<AppwriteVariable appwriteVariable={variable} />
{:else}
<div>
<svelte:component
this={selectComponent(variable.type)}
id={variable.name}
label={variable.name}
placeholder={variable.placeholder ?? 'Enter value'}
required={variable.required}
autocomplete={false}
showPasswordButton={variable.type === 'password'}
bind:value={$templateConfig.variables[variable.name]} />
<div>
<svelte:component
this={selectComponent(variable.type)}
id={variable.name}
label={variable.name}
placeholder={variable.placeholder ?? 'Enter value'}
required={variable.required}
autocomplete={false}
showPasswordButton={variable.type === 'password'}
bind:value={$templateConfig.variables[variable.name]} />
<Helper type="neutral">
{@html variable.description}
</Helper>
</div>
{/if}
<Helper type="neutral">
{@html variable.description}
</Helper>
</div>
{/each}
</FormList>
</CollapsibleItem>
+9 -6
View File
@@ -1,7 +1,8 @@
import { page } from '$app/stores';
import type { WizardStepsType } from '$lib/layout/wizard.svelte';
import type { MarketplaceTemplate } from '$lib/stores/marketplace';
import type { Models } from '@appwrite.io/console';
import { derived, writable } from 'svelte/store';
import { derived, writable, type Writable } from 'svelte/store';
export const template = writable<MarketplaceTemplate>();
export const templateConfig = writable<{
@@ -9,12 +10,12 @@ export const templateConfig = writable<{
name: string;
runtime: string;
variables: { [key: string]: unknown };
repositoryBehaviour: 'new' | 'existing';
repositoryName: string;
repositoryPrivate: boolean;
repositoryBehaviour: 'new' | 'existing' | 'manual';
repositoryName?: string;
repositoryPrivate?: boolean;
repositoryId: string;
appwriteApiKey?: string;
generateKey?: boolean;
execute?: boolean;
scopes?: string[];
}>();
export const repository = writable<Models.ProviderRepository>();
export const installation = writable<Models.Installation>();
@@ -59,3 +60,5 @@ function createFunctionStore() {
export const createFunction = createFunctionStore();
export const createFunctionDeployment = writable<FileList>();
export const templateStepsComponents: Writable<WizardStepsType> = writable(new Map());
+7 -1
View File
@@ -1,7 +1,13 @@
import type { PageLoad } from './$types';
import { sdk } from '$lib/stores/sdk';
import { redirect } from '@sveltejs/kit';
import { base } from '$app/paths';
export const load: PageLoad = async () => {
export const load: PageLoad = async ({ parent }) => {
const { mfaRequired } = await parent();
if (!mfaRequired) {
redirect(303, base);
}
return {
factors: await sdk.forConsole.account.listMfaFactors()
};
@@ -119,8 +119,8 @@
$provider.subdomain,
$provider.region,
$provider.adminSecret,
$provider.database,
$provider.username,
$provider.database || $provider.subdomain,
$provider.username || 'postgres',
$provider.password
);
}
@@ -186,7 +186,7 @@
</div>
</Box>
{#if report && !isVersionAtLeast(version, '1.4.0')}
{#if report && !isVersionAtLeast(version, '1.4.0') && $provider.provider === 'appwrite'}
<div class="u-margin-block-start-24">
<Alert
type="warning"
@@ -1,6 +1,6 @@
<script lang="ts">
import { page } from '$app/stores';
import { InputSelect } from '$lib/elements/forms';
import { FormList, InputSelect } from '$lib/elements/forms';
import InputText from '$lib/elements/forms/inputText.svelte';
import { WizardStep } from '$lib/layout';
import { Query, type Models, ID } from '@appwrite.io/console';
@@ -44,7 +44,7 @@
<WizardStep {beforeSubmit}>
<svelte:fragment slot="title">Project</svelte:fragment>
<div class="u-flex u-flex-vertical u-gap-24">
<FormList>
{#if organizations.length > 1}
<InputSelect
id="organization"
@@ -107,7 +107,7 @@
{/if}
</div>
{/if}
</div>
</FormList>
</WizardStep>
<style lang="scss">
@@ -4,20 +4,17 @@
import { Modal } from '$lib/components';
import { Dependencies } from '$lib/constants';
import { Button } from '$lib/elements/forms';
import FormList from '$lib/elements/forms/formList.svelte';
import InputDigits from '$lib/elements/forms/inputDigits.svelte';
import { addNotification } from '$lib/stores/notifications';
import { sdk } from '$lib/stores/sdk';
import { AuthenticatorType } from '@appwrite.io/console';
export let showDelete = false;
let code: string;
let error: string;
async function deleteAuthenticator() {
try {
await sdk.forConsole.account.deleteMfaAuthenticator(AuthenticatorType.Totp, code);
await sdk.forConsole.account.deleteMfaAuthenticator(AuthenticatorType.Totp);
await invalidate(Dependencies.ACCOUNT);
await invalidate(Dependencies.FACTORS);
showDelete = false;
@@ -33,7 +30,6 @@
}
$: if (showDelete) {
code = '';
error = '';
}
</script>
@@ -46,10 +42,10 @@
state="warning"
bind:error
headerDivider={false}>
<p>Enter the 6-digit verification code generated by your authenticator app to continue.</p>
<FormList>
<InputDigits autofocus required bind:value={code} autoSubmit={false} />
</FormList>
<p>
Are you sure you want to delete this authentication method? You will no longer be able to
use this method to authenticate your account.
</p>
<svelte:fragment slot="footer">
<Button text on:click={() => (showDelete = false)}>Cancel</Button>
@@ -1,15 +1,15 @@
<script lang="ts">
import { Alert, CardGrid, Empty, Heading, PaginationInline } from '$lib/components';
import {
Table,
TableBody,
TableCellHead,
TableCellText,
TableHeader,
TableRow
TableRow,
TableScroll
} from '$lib/elements/table';
import { toLocaleDate } from '$lib/helpers/date';
import type { Credit, CreditList } from '$lib/sdk/billing';
import type { CreditList } from '$lib/sdk/billing';
import { organization } from '$lib/stores/organization';
import { sdk } from '$lib/stores/sdk';
import { wizard } from '$lib/stores/wizard';
@@ -25,6 +25,7 @@
let offset = 0;
let creditList: CreditList = {
available: 0,
credits: [],
total: 0
};
@@ -56,9 +57,6 @@
request();
}
$: balance =
creditList?.credits?.reduce((acc: number, curr: Credit) => acc + curr.credits, 0) ?? 0;
$: {
if (reloadOnWizardClose && !$wizard.show) {
request();
@@ -86,8 +84,8 @@
{:else}
<div class="u-flex u-cross-center u-main-space-between">
<div class="u-flex u-gap-8 u-cross-center">
<h4 class="body-text-1 u-bold">Credit balance</h4>
<span class="inline-tag">{formatCurrency(balance)}</span>
<h4 class="body-text-1 u-bold">Balance</h4>
<span class="inline-tag">{formatCurrency(creditList.available)}</span>
</div>
{#if creditList?.total}
<Button secondary on:click={handleCredits}>
@@ -97,30 +95,34 @@
{/if}
</div>
{#if creditList?.total}
<Table noStyles noMargin>
<TableScroll noStyles noMargin class="u-margin-block-start-16">
<TableHeader>
<TableCellHead>Date Added</TableCellHead>
<TableCellHead>Expiry Date</TableCellHead>
<TableCellHead>Amount</TableCellHead>
<TableCellHead>Code</TableCellHead>
<TableCellHead>Total</TableCellHead>
<TableCellHead>Remaining</TableCellHead>
<TableCellHead>Expires at</TableCellHead>
</TableHeader>
<TableBody>
{#each creditList.credits as credit}
<TableRow>
<TableCellText title="date added">
{toLocaleDate(credit.$createdAt)}
<TableCellText title="code">
{credit?.couponId ?? '-'}
</TableCellText>
<TableCellText title="total">
{formatCurrency(credit.total)}
</TableCellText>
<TableCellText title="remaining">
{formatCurrency(credit.credits)}
</TableCellText>
<TableCellText title="expiry date">
{toLocaleDate(credit.expiration)}
</TableCellText>
<TableCellText title="amount">
{formatCurrency(credit.total)}
</TableCellText>
</TableRow>
{/each}
</TableBody>
</Table>
</TableScroll>
<div class="u-flex u-main-space-between">
<p class="text">Total results: {creditList?.total}</p>
<p class="text">Total credits: {creditList?.total}</p>
<PaginationInline {limit} bind:offset sum={creditList?.total} hidePages />
</div>
{:else}
@@ -53,8 +53,8 @@
</p>
<InputSwitch id="state" bind:value={enabled} label={enabled ? 'Enabled' : 'Disabled'} />
<InputText
id="bundleID"
label="Bundle ID"
id="servicesID"
label="Services ID"
autofocus={true}
placeholder="com.company.appname"
bind:value={appId} />
@@ -56,7 +56,7 @@
<Form onSubmit={updatePrefs}>
<CardGrid>
<Heading tag="h6" size="7">Team preferences</Heading>
<Heading tag="h6" size="7">Preferences</Heading>
<p>
You can update your team's preferences by storing shared information on the teams's
objects so they can easily be shared across members.
@@ -11,11 +11,7 @@
$: if (prefs) {
if (JSON.stringify(prefs) !== JSON.stringify(Object.entries($user.prefs))) {
if (!!prefs[prefs.length - 1][0] && !!prefs[prefs.length - 1][1]) {
arePrefsDisabled = false;
} else {
arePrefsDisabled = true;
}
arePrefsDisabled = false;
} else {
arePrefsDisabled = true;
}
@@ -86,7 +82,7 @@
text
disabled={(!key || !value) && index === 0}
on:click={() => {
if (index === 0) {
if (index === 0 && prefs?.length === 1) {
prefs = [['', '']];
} else {
prefs.splice(index, 1);
@@ -80,7 +80,7 @@
<FormList gap={16}>
<InputFile
label="Gzipped code (tar.gz)"
allowedFileExtensions={['gz']}
allowedFileExtensions={['gz', 'tar']}
bind:files
required={true} />
@@ -66,8 +66,8 @@
{@const status = execution.status}
<div
use:tooltip={{
content: `Scheduled to execute on ${toLocaleDateTime(execution?.schedule)}`,
disabled: !execution?.schedule
content: `Scheduled to execute on ${toLocaleDateTime(execution.scheduledAt)}`,
disabled: !execution?.scheduledAt || status !== 'scheduled'
}}>
<Pill
warning={status === 'waiting' || status === 'building'}
@@ -75,8 +75,10 @@
info={status === 'completed' || status === 'ready'}>
{#if status === 'scheduled'}
<span class="icon-clock" aria-hidden="true" />
{timeFromNow(execution.scheduledAt)}
{:else}
{status}
{/if}
{status}
</Pill>
</div>
</TableCell>
+2 -2
View File
@@ -84,8 +84,8 @@
$wizard.finalAction = handleSubmit;
const workTimings = {
start: '09:00',
end: '17:00',
start: '16:00',
end: '00:00',
startDay: 'Monday' as WeekDay,
endDay: 'Friday' as WeekDay
};
+1 -1
View File
@@ -22,7 +22,7 @@ export function isSupportOnline() {
return false;
}
if (hour < 9 || hour > 17) {
if (hour < 14) {
return false;
}
+5 -2
View File
@@ -33,12 +33,15 @@ export const load: LayoutLoad = async ({ depends, url, route }) => {
}
const isPublicRoute = route.id?.startsWith('/(public)');
if (url.pathname && !isPublicRoute) {
if (!isPublicRoute) {
url.searchParams.set('redirect', url.pathname);
}
if (error.type === 'user_more_factors_required') {
if (url.pathname === `${base}/mfa`) return; // Ensure any previous account/organizations are cleared
if (url.pathname === `${base}/mfa`)
return {
mfaRequired: true
};
redirect(303, withParams(`${base}/mfa`, url.searchParams));
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 416 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 384 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 416 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 384 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 416 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 384 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 416 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 384 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 416 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 384 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 416 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 384 KiB