mirror of
https://github.com/appwrite/console.git
synced 2026-06-06 19:27:48 +00:00
Implement First Screens Transfer into Settings
This commit is contained in:
Generated
+29
-76
@@ -10,7 +10,7 @@
|
||||
"dependencies": {
|
||||
"@analytics/google-analytics": "^1.0.5",
|
||||
"@appwrite.io/pink": "^0.0.4",
|
||||
"@aw-labs/appwrite-console": "^13.1.0",
|
||||
"@aw-labs/appwrite-console": "/Users/ionicisere/Documents/github/appwrite/app/sdks/console-web/",
|
||||
"@popperjs/core": "^2.11.6",
|
||||
"@sentry/svelte": "^7.36.0",
|
||||
"@sentry/tracing": "^7.36.0",
|
||||
@@ -55,6 +55,26 @@
|
||||
"node": ">=16"
|
||||
}
|
||||
},
|
||||
"../appwrite/app/sdks/console-web": {
|
||||
"name": "@aw-labs/appwrite-console",
|
||||
"version": "7.2.0",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"cross-fetch": "3.1.5",
|
||||
"isomorphic-form-data": "2.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-typescript": "8.3.2",
|
||||
"playwright": "1.15.0",
|
||||
"rollup": "2.75.4",
|
||||
"serve-handler": "6.1.0",
|
||||
"tslib": "2.4.0",
|
||||
"typescript": "4.7.2"
|
||||
}
|
||||
},
|
||||
"../appwrite/app/sdks/console-web/dist/cjs": {
|
||||
"extraneous": true
|
||||
},
|
||||
"node_modules/@adobe/css-tools": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.1.0.tgz",
|
||||
@@ -161,13 +181,8 @@
|
||||
"integrity": "sha512-GFOodekDck6xX7OAZuI1mo7F87ThEZ9Gt0xMsgTR79zkWcnBRq0+J89pFWRPAtFNEVMPQLVHuu6s8S4UleHwtQ=="
|
||||
},
|
||||
"node_modules/@aw-labs/appwrite-console": {
|
||||
"version": "13.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@aw-labs/appwrite-console/-/appwrite-console-13.1.0.tgz",
|
||||
"integrity": "sha512-1FyKke5b3vx+D3VfNYd3YnFhzUOlsj9bD+KTRLWA4WRItb55thFVZJMWQQ4XnZtDiLPuJAn2XjbMSfMr+9XYwg==",
|
||||
"dependencies": {
|
||||
"cross-fetch": "3.1.5",
|
||||
"isomorphic-form-data": "2.0.0"
|
||||
}
|
||||
"resolved": "../appwrite/app/sdks/console-web",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@babel/code-frame": {
|
||||
"version": "7.18.6",
|
||||
@@ -2809,7 +2824,8 @@
|
||||
"node_modules/asynckit": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
|
||||
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/available-typed-arrays": {
|
||||
"version": "1.0.5",
|
||||
@@ -3297,6 +3313,7 @@
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"delayed-stream": "~1.0.0"
|
||||
},
|
||||
@@ -3347,14 +3364,6 @@
|
||||
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/cross-fetch": {
|
||||
"version": "3.1.5",
|
||||
"resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz",
|
||||
"integrity": "sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==",
|
||||
"dependencies": {
|
||||
"node-fetch": "2.6.7"
|
||||
}
|
||||
},
|
||||
"node_modules/cross-spawn": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
|
||||
@@ -3518,6 +3527,7 @@
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
@@ -4275,19 +4285,6 @@
|
||||
"is-callable": "^1.1.3"
|
||||
}
|
||||
},
|
||||
"node_modules/form-data": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz",
|
||||
"integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==",
|
||||
"dependencies": {
|
||||
"asynckit": "^0.4.0",
|
||||
"combined-stream": "^1.0.6",
|
||||
"mime-types": "^2.1.12"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.12"
|
||||
}
|
||||
},
|
||||
"node_modules/fs.realpath": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||
@@ -5068,14 +5065,6 @@
|
||||
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/isomorphic-form-data": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isomorphic-form-data/-/isomorphic-form-data-2.0.0.tgz",
|
||||
"integrity": "sha512-TYgVnXWeESVmQSg4GLVbalmQ+B4NPi/H4eWxqALKj63KsUrcu301YDjBqaOw3h+cbak7Na4Xyps3BiptHtxTfg==",
|
||||
"dependencies": {
|
||||
"form-data": "^2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/istanbul-lib-coverage": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz",
|
||||
@@ -6381,6 +6370,7 @@
|
||||
"version": "1.52.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
@@ -6389,6 +6379,7 @@
|
||||
"version": "2.1.35",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
||||
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"mime-db": "1.52.0"
|
||||
},
|
||||
@@ -6508,44 +6499,6 @@
|
||||
"integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/node-fetch": {
|
||||
"version": "2.6.7",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
|
||||
"integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
|
||||
"dependencies": {
|
||||
"whatwg-url": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "4.x || >=6.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"encoding": "^0.1.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"encoding": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/node-fetch/node_modules/tr46": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
|
||||
},
|
||||
"node_modules/node-fetch/node_modules/webidl-conversions": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
|
||||
},
|
||||
"node_modules/node-fetch/node_modules/whatwg-url": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
||||
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
|
||||
"dependencies": {
|
||||
"tr46": "~0.0.3",
|
||||
"webidl-conversions": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/node-int64": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz",
|
||||
|
||||
+1
-1
@@ -21,7 +21,7 @@
|
||||
"dependencies": {
|
||||
"@analytics/google-analytics": "^1.0.5",
|
||||
"@appwrite.io/pink": "^0.0.4",
|
||||
"@aw-labs/appwrite-console": "^13.1.0",
|
||||
"@aw-labs/appwrite-console": "/Users/ionicisere/Documents/github/appwrite/app/sdks/console-web/",
|
||||
"@popperjs/core": "^2.11.6",
|
||||
"@sentry/svelte": "^7.36.0",
|
||||
"@sentry/tracing": "^7.36.0",
|
||||
|
||||
@@ -206,5 +206,13 @@ export enum Submit {
|
||||
BucketUpdateExtensions = 'submit_bucket_update_extensions',
|
||||
FileCreate = 'submit_file_create',
|
||||
FileDelete = 'submit_file_delete',
|
||||
FileUpdatePermissions = 'submit_file_update_permissions'
|
||||
FileUpdatePermissions = 'submit_file_update_permissions',
|
||||
TransferCreate = 'submit_transfer_create',
|
||||
TransferDelete = 'submit_transfer_delete',
|
||||
DestinationCreate = 'submit_destination_create',
|
||||
DestinationDelete = 'submit_destination_delete',
|
||||
DestinationValidate = 'submit_destination_validate',
|
||||
SourceCreate = 'submit_source_create',
|
||||
SourceDelete = 'submit_source_delete',
|
||||
SourceValidate = 'submit_source_validate'
|
||||
}
|
||||
|
||||
@@ -32,7 +32,13 @@ export enum Dependencies {
|
||||
KEYS = 'dependency:keys',
|
||||
DOMAINS = 'dependency:domains',
|
||||
WEBHOOK = 'dependency:webhook',
|
||||
WEBHOOKS = 'dependency:webhooks'
|
||||
WEBHOOKS = 'dependency:webhooks',
|
||||
TRANSFER = 'dependency:transfer',
|
||||
TRANSFERS = 'dependency:transfers',
|
||||
SOURCE = 'dependency:source',
|
||||
SOURCES = 'dependency:sources',
|
||||
DESTINATION = 'dependency:destination',
|
||||
DESTINATIONS = 'dependency:destinations'
|
||||
}
|
||||
|
||||
export const scopes: {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
label: string;
|
||||
component: typeof SvelteComponent;
|
||||
optional?: boolean;
|
||||
footer?: boolean;
|
||||
}
|
||||
>;
|
||||
</script>
|
||||
@@ -128,6 +129,7 @@
|
||||
<svelte:component this={component} />
|
||||
{/if}
|
||||
{/each}
|
||||
|
||||
<div class="form-footer">
|
||||
<div class="u-flex u-main-end u-gap-12">
|
||||
{#if !isLastStep && sortedSteps[currentStep - 1][1].optional}
|
||||
@@ -135,7 +137,9 @@
|
||||
Skip optional steps
|
||||
</Button>
|
||||
{/if}
|
||||
{#if currentStep === 1}
|
||||
{#if sortedSteps[currentStep - 1][1].footer === false}
|
||||
<div />
|
||||
{:else if currentStep === 1}
|
||||
<Button secondary on:click={handleExit}>Cancel</Button>
|
||||
<Button submit>Next</Button>
|
||||
{:else if isLastStep}
|
||||
|
||||
@@ -10,7 +10,8 @@ import {
|
||||
Projects,
|
||||
Storage,
|
||||
Teams,
|
||||
Users
|
||||
Users,
|
||||
Transfers
|
||||
} from '@aw-labs/appwrite-console';
|
||||
|
||||
const endpoint = VARS.APPWRITE_ENDPOINT ?? `${globalThis?.location?.origin}/v1`;
|
||||
@@ -31,7 +32,8 @@ const sdkForConsole = {
|
||||
locale: new Locale(clientConsole),
|
||||
projects: new Projects(clientConsole),
|
||||
teams: new Teams(clientConsole),
|
||||
users: new Users(clientConsole)
|
||||
users: new Users(clientConsole),
|
||||
transfers: new Transfers(clientConsole)
|
||||
};
|
||||
|
||||
const sdkForProject = {
|
||||
@@ -45,7 +47,8 @@ const sdkForProject = {
|
||||
projects: new Projects(clientProject),
|
||||
storage: new Storage(clientProject),
|
||||
teams: new Teams(clientProject),
|
||||
users: new Users(clientProject)
|
||||
users: new Users(clientProject),
|
||||
transfers: new Transfers(clientProject)
|
||||
};
|
||||
|
||||
export { sdkForConsole, sdkForProject, setProject };
|
||||
|
||||
@@ -21,6 +21,21 @@
|
||||
href: `${path}/webhooks`,
|
||||
title: 'Webhooks',
|
||||
event: 'webhooks'
|
||||
},
|
||||
{
|
||||
href: `${path}/transfers`,
|
||||
title: 'Transfers',
|
||||
event: 'transfers'
|
||||
},
|
||||
{
|
||||
href: `${path}/transfers/sources`,
|
||||
title: 'Sources',
|
||||
event: 'sources'
|
||||
},
|
||||
{
|
||||
href: `${path}/transfers/destinations`,
|
||||
title: 'Destinations',
|
||||
event: 'destinations'
|
||||
}
|
||||
];
|
||||
</script>
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/stores';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import { Empty, CardContainer, Copy, GridItem1, Heading, Pagination } from '$lib/components';
|
||||
import { Pill } from '$lib/elements';
|
||||
import { Container } from '$lib/layout';
|
||||
import { base } from '$app/paths';
|
||||
import { app } from '$lib/stores/app';
|
||||
import { wizard } from '$lib/stores/wizard';
|
||||
import { beforeNavigate } from '$app/navigation';
|
||||
import Create from '../createTransfer.svelte';
|
||||
import type { PageData } from './$types';
|
||||
import { CARD_LIMIT } from '$lib/constants';
|
||||
|
||||
export let data: PageData;
|
||||
|
||||
let offset = 0;
|
||||
|
||||
const project = $page.params.project;
|
||||
|
||||
function openWizard() {
|
||||
wizard.start(Create);
|
||||
}
|
||||
|
||||
beforeNavigate(() => {
|
||||
wizard.hide();
|
||||
});
|
||||
</script>
|
||||
|
||||
<Container>
|
||||
<div class="u-flex u-gap-12 common-section u-main-space-between">
|
||||
<Heading tag="h2" size="5">Transfers</Heading>
|
||||
<Button on:click={openWizard} event="create_source">
|
||||
<span class="icon-plus" aria-hidden="true" /> <span class="text">Create transfer</span>
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{#if data.transfers.total}
|
||||
<CardContainer
|
||||
{offset}
|
||||
event="transfers"
|
||||
total={data.transfers.total}
|
||||
on:click={openWizard}>
|
||||
{#each data.transfers.transfers as transfer}
|
||||
<GridItem1
|
||||
href={`${base}/console/project-${project}/settings/transfers/transfer-${transfer.$id}`}>
|
||||
<svelte:fragment slot="title">
|
||||
<div class="u-flex u-gap-16 u-cross-center">
|
||||
<div class="avatar is-medium">
|
||||
<img
|
||||
src={`${base}/icons/${$app.themeInUse}/color/appwrite.svg`}
|
||||
alt="technology" />
|
||||
</div>
|
||||
<span class="text">{transfer.$id}</span>
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
|
||||
<Copy value={transfer.$id} event="transfer">
|
||||
<Pill button><i class="icon-duplicate" />Transfer ID</Pill>
|
||||
</Copy>
|
||||
</GridItem1>
|
||||
{/each}
|
||||
<svelte:fragment slot="empty">
|
||||
<p>Create a new transfer</p>
|
||||
</svelte:fragment>
|
||||
</CardContainer>
|
||||
<div class="u-flex u-margin-block-start-32 u-main-space-between">
|
||||
<p class="text">Total results: {data.transfers.total}</p>
|
||||
<Pagination
|
||||
limit={CARD_LIMIT}
|
||||
path={`/console/project-${$page.params.project}/transfers`}
|
||||
offset={data.offset}
|
||||
sum={data.transfers.total} />
|
||||
</div>
|
||||
{:else}
|
||||
<Empty
|
||||
single
|
||||
href="https://appwrite.io/docs/transfers#createTransfer"
|
||||
target="transfer"
|
||||
on:click={openWizard} />
|
||||
{/if}
|
||||
</Container>
|
||||
@@ -0,0 +1,20 @@
|
||||
import { Query } from '@aw-labs/appwrite-console';
|
||||
import { sdkForProject } from '$lib/stores/sdk';
|
||||
import { pageToOffset } from '$lib/helpers/load';
|
||||
import { CARD_LIMIT } from '$lib/constants';
|
||||
import type { PageLoad } from './$types';
|
||||
|
||||
export const load: PageLoad = async ({ params, parent }) => {
|
||||
await parent();
|
||||
const page = Number(params.page);
|
||||
const offset = pageToOffset(page, CARD_LIMIT);
|
||||
|
||||
return {
|
||||
offset,
|
||||
transfers: await sdkForProject.transfers.list([
|
||||
Query.limit(CARD_LIMIT),
|
||||
Query.offset(offset),
|
||||
Query.orderDesc('$createdAt')
|
||||
])
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,90 @@
|
||||
<script lang="ts">
|
||||
import { Wizard } from '$lib/layout';
|
||||
import { sdkForProject } from '$lib/stores/sdk';
|
||||
import { onDestroy } from 'svelte';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { wizard } from '$lib/stores/wizard';
|
||||
import { createDestination } from './wizard/store';
|
||||
import type { WizardStepsType } from '$lib/layout/wizard.svelte';
|
||||
import { goto, invalidate } from '$app/navigation';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { base } from '$app/paths';
|
||||
import { page } from '$app/stores';
|
||||
import { Submit, trackEvent, trackError } from '$lib/actions/analytics';
|
||||
import { ID } from '@aw-labs/appwrite-console';
|
||||
import Step1 from './wizard/createDestination/step1.svelte';
|
||||
import Step2 from './wizard/createDestination/step2.svelte';
|
||||
import Step3 from './wizard/createDestination/step3.svelte';
|
||||
import Step4 from './wizard/createDestination/step4.svelte';
|
||||
|
||||
const projectId = $page.params.project;
|
||||
|
||||
async function onFinish() {
|
||||
await invalidate(Dependencies.DESTINATION);
|
||||
}
|
||||
async function create() {
|
||||
try {
|
||||
let response = null;
|
||||
|
||||
switch ($createDestination.type) {
|
||||
case 'appwrite':
|
||||
response = await sdkForProject.transfers.createAppwriteDestination(
|
||||
$createDestination.data['project'],
|
||||
$createDestination.data['endpoint'],
|
||||
$createDestination.data['key'],
|
||||
$createDestination.id ?? ID.unique(),
|
||||
$createDestination.name
|
||||
);
|
||||
}
|
||||
|
||||
await invalidate(Dependencies.DESTINATION);
|
||||
goto(
|
||||
`${base}/console/project-${projectId}/settings/transfers/destinations/destination-${response.$id}`
|
||||
);
|
||||
addNotification({
|
||||
message: `${response.$id} has been created`,
|
||||
type: 'success'
|
||||
});
|
||||
trackEvent(Submit.DestinationCreate, {
|
||||
customId: !!response.$id
|
||||
});
|
||||
wizard.hide();
|
||||
} catch (error) {
|
||||
addNotification({
|
||||
message: error.message,
|
||||
type: 'error'
|
||||
});
|
||||
trackError(error, Submit.DestinationCreate);
|
||||
}
|
||||
}
|
||||
|
||||
onDestroy(() => {
|
||||
$createDestination = {
|
||||
id: null,
|
||||
type: null,
|
||||
name: null,
|
||||
data: {}
|
||||
};
|
||||
});
|
||||
|
||||
const stepsComponents: WizardStepsType = new Map();
|
||||
stepsComponents.set(1, {
|
||||
label: 'Select Provider',
|
||||
component: Step1,
|
||||
footer: false
|
||||
});
|
||||
stepsComponents.set(2, {
|
||||
label: 'Provide Credentials',
|
||||
component: Step2
|
||||
});
|
||||
stepsComponents.set(3, {
|
||||
label: 'Validation',
|
||||
component: Step3
|
||||
});
|
||||
stepsComponents.set(4, {
|
||||
label: 'Summary',
|
||||
component: Step4
|
||||
});
|
||||
</script>
|
||||
|
||||
<Wizard title="Create Destination" steps={stepsComponents} on:finish={create} on:exit={onFinish} />
|
||||
@@ -0,0 +1,119 @@
|
||||
<script lang="ts">
|
||||
import { Wizard } from '$lib/layout';
|
||||
import { sdkForProject } from '$lib/stores/sdk';
|
||||
import { onDestroy } from 'svelte';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { wizard } from '$lib/stores/wizard';
|
||||
import { createSource } from './wizard/store';
|
||||
import type { WizardStepsType } from '$lib/layout/wizard.svelte';
|
||||
import { goto, invalidate } from '$app/navigation';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { base } from '$app/paths';
|
||||
import { page } from '$app/stores';
|
||||
import { Submit, trackEvent, trackError } from '$lib/actions/analytics';
|
||||
import { ID } from '@aw-labs/appwrite-console';
|
||||
import Step1 from './wizard/createSource/step1.svelte';
|
||||
import Step2 from './wizard/createSource/step2.svelte';
|
||||
import Step3 from './wizard/createSource/step3.svelte';
|
||||
import Step4 from './wizard/createSource/step4.svelte';
|
||||
|
||||
const projectId = $page.params.project;
|
||||
|
||||
async function onFinish() {
|
||||
await invalidate(Dependencies.SOURCE);
|
||||
}
|
||||
|
||||
async function create() {
|
||||
try {
|
||||
let response;
|
||||
switch ($createSource.type) {
|
||||
case 'appwrite':
|
||||
response = await sdkForProject.transfers.createAppwriteSource(
|
||||
$createSource.data['projectId'],
|
||||
$createSource.data['endpoint'],
|
||||
$createSource.data['key'],
|
||||
$createSource.id ?? ID.unique(),
|
||||
$createSource.name
|
||||
);
|
||||
break;
|
||||
case 'supabase':
|
||||
response = await sdkForProject.transfers.createSupabaseSource(
|
||||
$createSource.data['host'],
|
||||
$createSource.data['password'],
|
||||
$createSource.id ?? ID.unique(),
|
||||
$createSource.name,
|
||||
$createSource.data['database'],
|
||||
$createSource.data['username'],
|
||||
$createSource.data['port']
|
||||
);
|
||||
break;
|
||||
case 'nhost':
|
||||
response = await sdkForProject.transfers.createNhostSource(
|
||||
$createSource.data['host'],
|
||||
$createSource.data['password'],
|
||||
$createSource.id ?? ID.unique(),
|
||||
$createSource.name,
|
||||
$createSource.data['database'],
|
||||
$createSource.data['username'],
|
||||
$createSource.data['port']
|
||||
);
|
||||
break;
|
||||
case 'firebase':
|
||||
response = await sdkForProject.transfers.createFirebaseSource(
|
||||
$createSource.data['serviceAccount'],
|
||||
$createSource.id ?? ID.unique(),
|
||||
$createSource.name
|
||||
);
|
||||
break;
|
||||
}
|
||||
await invalidate(Dependencies.SOURCE);
|
||||
goto(
|
||||
`${base}/console/project-${projectId}/settings/transfers/sources/source-${response.$id}`
|
||||
);
|
||||
addNotification({
|
||||
message: `${response.$id} has been created`,
|
||||
type: 'success'
|
||||
});
|
||||
trackEvent(Submit.SourceCreate, {
|
||||
customId: !!response.$id
|
||||
});
|
||||
wizard.hide();
|
||||
} catch (error) {
|
||||
addNotification({
|
||||
message: error.message,
|
||||
type: 'error'
|
||||
});
|
||||
trackError(error, Submit.SourceCreate);
|
||||
}
|
||||
}
|
||||
|
||||
onDestroy(() => {
|
||||
$createSource = {
|
||||
id: null,
|
||||
type: null,
|
||||
name: null,
|
||||
data: {}
|
||||
};
|
||||
});
|
||||
|
||||
const stepsComponents: WizardStepsType = new Map();
|
||||
stepsComponents.set(1, {
|
||||
label: 'Select Provider',
|
||||
component: Step1,
|
||||
footer: false
|
||||
});
|
||||
stepsComponents.set(2, {
|
||||
label: 'Provide Credentials',
|
||||
component: Step2
|
||||
});
|
||||
stepsComponents.set(3, {
|
||||
label: 'Validation',
|
||||
component: Step3
|
||||
});
|
||||
stepsComponents.set(4, {
|
||||
label: 'Summary',
|
||||
component: Step4
|
||||
});
|
||||
</script>
|
||||
|
||||
<Wizard title="Create Transfer" steps={stepsComponents} on:finish={create} on:exit={onFinish} />
|
||||
@@ -0,0 +1,89 @@
|
||||
<script lang="ts">
|
||||
import { Wizard } from '$lib/layout';
|
||||
import { sdkForProject } from '$lib/stores/sdk';
|
||||
import { onDestroy } from 'svelte';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { wizard } from '$lib/stores/wizard';
|
||||
import Step1 from './wizard/createTransfer/step1.svelte';
|
||||
import { createTransfer } from './wizard/store';
|
||||
import type { WizardStepsType } from '$lib/layout/wizard.svelte';
|
||||
import { goto, invalidate } from '$app/navigation';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { base } from '$app/paths';
|
||||
import { page } from '$app/stores';
|
||||
import { Submit, trackEvent, trackError } from '$lib/actions/analytics';
|
||||
import { ID } from '@aw-labs/appwrite-console';
|
||||
import Step2 from './wizard/createTransfer/step2.svelte';
|
||||
import Step3 from './wizard/createTransfer/step3.svelte';
|
||||
import Step4 from './wizard/createTransfer/step4.svelte';
|
||||
import Step5 from './wizard/createTransfer/step5.svelte';
|
||||
|
||||
const projectId = $page.params.project;
|
||||
|
||||
async function onFinish() {
|
||||
await invalidate(Dependencies.TRANSFER);
|
||||
}
|
||||
async function create() {
|
||||
try {
|
||||
const response = await sdkForProject.transfers.create(
|
||||
$createTransfer.id ?? ID.unique(),
|
||||
$createTransfer.source,
|
||||
$createTransfer.destination,
|
||||
$createTransfer.resources
|
||||
);
|
||||
await invalidate(Dependencies.TRANSFER);
|
||||
goto(
|
||||
`${base}/console/project-${projectId}/settings/transfers/transfer-${response.$id}`
|
||||
);
|
||||
addNotification({
|
||||
message: `${response.$id} has been created`,
|
||||
type: 'success'
|
||||
});
|
||||
trackEvent(Submit.TransferCreate, {
|
||||
customId: !!response.$id
|
||||
});
|
||||
wizard.hide();
|
||||
} catch (error) {
|
||||
addNotification({
|
||||
message: error.message,
|
||||
type: 'error'
|
||||
});
|
||||
trackError(error, Submit.TransferCreate);
|
||||
}
|
||||
}
|
||||
|
||||
onDestroy(() => {
|
||||
$createTransfer = {
|
||||
id: null,
|
||||
source: null,
|
||||
destination: null,
|
||||
resources: []
|
||||
};
|
||||
});
|
||||
|
||||
const stepsComponents: WizardStepsType = new Map();
|
||||
stepsComponents.set(1, {
|
||||
label: 'Select Source',
|
||||
component: Step1,
|
||||
footer: false
|
||||
});
|
||||
stepsComponents.set(2, {
|
||||
label: 'Select Destination',
|
||||
component: Step2,
|
||||
footer: false
|
||||
});
|
||||
stepsComponents.set(3, {
|
||||
label: 'Select Resources',
|
||||
component: Step3
|
||||
});
|
||||
stepsComponents.set(4, {
|
||||
label: 'Validation',
|
||||
component: Step4
|
||||
});
|
||||
stepsComponents.set(5, {
|
||||
label: 'Begin Transfer',
|
||||
component: Step5
|
||||
});
|
||||
</script>
|
||||
|
||||
<Wizard title="Create Transfer" steps={stepsComponents} on:finish={create} on:exit={onFinish} />
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/stores';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import { Empty, CardContainer, Copy, GridItem1, Heading, Pagination } from '$lib/components';
|
||||
import { Pill } from '$lib/elements';
|
||||
import { Container } from '$lib/layout';
|
||||
import { base } from '$app/paths';
|
||||
import { app } from '$lib/stores/app';
|
||||
import { wizard } from '$lib/stores/wizard';
|
||||
import { beforeNavigate } from '$app/navigation';
|
||||
import Create from '../../createDestination.svelte';
|
||||
import type { PageData } from './$types';
|
||||
import { CARD_LIMIT } from '$lib/constants';
|
||||
|
||||
export let data: PageData;
|
||||
|
||||
let offset = 0;
|
||||
|
||||
const project = $page.params.project;
|
||||
|
||||
function openWizard() {
|
||||
wizard.start(Create);
|
||||
}
|
||||
|
||||
beforeNavigate(() => {
|
||||
wizard.hide();
|
||||
});
|
||||
</script>
|
||||
|
||||
<Container>
|
||||
<div class="u-flex u-gap-12 common-section u-main-space-between">
|
||||
<Heading tag="h2" size="5">All Destinations</Heading>
|
||||
<Button on:click={openWizard} event="create_destination">
|
||||
<span class="icon-plus" aria-hidden="true" />
|
||||
<span class="text">Create destination</span>
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{#if data.destinations.total}
|
||||
<CardContainer
|
||||
{offset}
|
||||
event="destinations"
|
||||
total={data.destinations.total}
|
||||
on:click={openWizard}>
|
||||
{#each data.destinations.destinations as destination}
|
||||
<GridItem1
|
||||
href={`${base}/console/project-${project}/settings/transfers/destinations/destination-${destination.$id}`}>
|
||||
<svelte:fragment slot="title">
|
||||
<div class="u-flex u-gap-16 u-cross-center">
|
||||
<div class="avatar is-medium">
|
||||
<img
|
||||
src={`${base}/icons/${$app.themeInUse}/color/${destination.type}.svg`}
|
||||
alt="technology" />
|
||||
</div>
|
||||
<span class="text">{destination.name}</span>
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
|
||||
<Copy value={destination.$id} event="destinations">
|
||||
<Pill button><i class="icon-duplicate" />Destination ID</Pill>
|
||||
</Copy>
|
||||
</GridItem1>
|
||||
{/each}
|
||||
<svelte:fragment slot="empty">
|
||||
<p>Create a new destination</p>
|
||||
</svelte:fragment>
|
||||
</CardContainer>
|
||||
<div class="u-flex u-margin-block-start-32 u-main-space-between">
|
||||
<p class="text">Total results: {data.destinations.total}</p>
|
||||
<Pagination
|
||||
limit={CARD_LIMIT}
|
||||
path={`/console/project-${$page.params.project}/destinations`}
|
||||
offset={data.offset}
|
||||
sum={data.destinations.total} />
|
||||
</div>
|
||||
{:else}
|
||||
<Empty
|
||||
single
|
||||
href="https://appwrite.io/docs/transfers#createDestination"
|
||||
target="destination"
|
||||
on:click={openWizard} />
|
||||
{/if}
|
||||
</Container>
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
import { Query } from '@aw-labs/appwrite-console';
|
||||
import { sdkForProject } from '$lib/stores/sdk';
|
||||
import { pageToOffset } from '$lib/helpers/load';
|
||||
import { CARD_LIMIT } from '$lib/constants';
|
||||
import type { PageLoad } from './$types';
|
||||
|
||||
export const load: PageLoad = async ({ params, parent }) => {
|
||||
await parent();
|
||||
const page = Number(params.page);
|
||||
const offset = pageToOffset(page, CARD_LIMIT);
|
||||
|
||||
return {
|
||||
offset,
|
||||
destinations: await sdkForProject.transfers.listDestinations([
|
||||
Query.limit(CARD_LIMIT),
|
||||
Query.offset(offset),
|
||||
Query.orderDesc('$createdAt')
|
||||
])
|
||||
};
|
||||
};
|
||||
+1
@@ -0,0 +1 @@
|
||||
<slot />
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
import { sdkForProject } from '$lib/stores/sdk';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import type { LayoutLoad } from './$types';
|
||||
import Breadcrumbs from './breadcrumbs.svelte';
|
||||
import Header from './header.svelte';
|
||||
import { error } from '@sveltejs/kit';
|
||||
|
||||
export const load: LayoutLoad = async ({ params, parent, depends }) => {
|
||||
await parent();
|
||||
depends(Dependencies.DESTINATION);
|
||||
|
||||
try {
|
||||
return {
|
||||
header: Header,
|
||||
breadcrumbs: Breadcrumbs,
|
||||
destination: await sdkForProject.transfers.getDestination(params.destination)
|
||||
};
|
||||
} catch (e) {
|
||||
throw error(e.code, e.message);
|
||||
}
|
||||
};
|
||||
+139
@@ -0,0 +1,139 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/stores';
|
||||
import { Submit, trackEvent } from '$lib/actions/analytics';
|
||||
import Card from '$lib/components/card.svelte';
|
||||
import Heading from '$lib/components/heading.svelte';
|
||||
import Output from '$lib/components/output.svelte';
|
||||
import Button from '$lib/elements/forms/button.svelte';
|
||||
import Helper from '$lib/elements/forms/helper.svelte';
|
||||
import Pill from '$lib/elements/pill.svelte';
|
||||
import {
|
||||
TableBody,
|
||||
TableCellHead,
|
||||
TableCellText,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
TableScroll
|
||||
} from '$lib/elements/table';
|
||||
import { Container } from '$lib/layout';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { sdkForProject } from '$lib/stores/sdk';
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
const permissions = ['Users', 'Databases', 'Documents', 'Files', 'Functions'];
|
||||
let errors = permissions.map((permission) => {
|
||||
return {
|
||||
[permission]: false
|
||||
};
|
||||
});
|
||||
|
||||
let isChecking = false;
|
||||
let errorMessage = null;
|
||||
const destinationId = $page.params.destination;
|
||||
|
||||
const runChecks = async () => {
|
||||
isChecking = true;
|
||||
errorMessage = null;
|
||||
|
||||
try {
|
||||
await sdkForProject.transfers.validateDestination(destinationId);
|
||||
|
||||
isChecking = false;
|
||||
trackEvent(Submit.DestinationValidate, {
|
||||
customId: !!destinationId
|
||||
});
|
||||
} catch (error) {
|
||||
if (error.response.errors) {
|
||||
errors = error.response.errors;
|
||||
} else {
|
||||
errors = permissions.map((permission) => {
|
||||
return {
|
||||
[permission]: true
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
addNotification({
|
||||
type: 'error',
|
||||
title: 'Error',
|
||||
message: error.message
|
||||
});
|
||||
errorMessage = error.message;
|
||||
console.error(error);
|
||||
}
|
||||
|
||||
isChecking = false;
|
||||
};
|
||||
|
||||
onMount(() => {
|
||||
runChecks();
|
||||
});
|
||||
</script>
|
||||
|
||||
<Container>
|
||||
<div class="u-flex u-gap-12 common-section u-main-space-between">
|
||||
<Heading tag="h2" size="5">Destination Checks</Heading>
|
||||
|
||||
<Button disabled={isChecking} on:click={runChecks}>
|
||||
<span class="icon-check-circle" aria-hidden="true" />
|
||||
<span class="text">Run Checks</span>
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<TableScroll>
|
||||
<TableHeader>
|
||||
<TableCellHead width={150}>Permission</TableCellHead>
|
||||
<TableCellHead width={60}>Status</TableCellHead>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{#each permissions as permission}
|
||||
<TableRow>
|
||||
<TableCellText title="Permission">
|
||||
{permission}
|
||||
|
||||
{#if errors[permission]}
|
||||
<Helper type="warning">
|
||||
{#each errors[permission] as error}
|
||||
{error} <br />
|
||||
{/each}
|
||||
</Helper>
|
||||
{/if}
|
||||
</TableCellText>
|
||||
<TableCellText title="Permission Level">
|
||||
<Pill
|
||||
danger={errors[permission]}
|
||||
success={!errors[permission]}
|
||||
warning={isChecking}>
|
||||
{#if !errors[permission] && !isChecking}
|
||||
<span class="icon-check-circle" aria-hidden="true" />
|
||||
{:else if isChecking}
|
||||
<span class="icon-question-mark-circle" aria-hidden="true" />
|
||||
{:else}
|
||||
<span class="icon-x-circle" aria-hidden="true" />
|
||||
{/if}
|
||||
</Pill>
|
||||
</TableCellText>
|
||||
</TableRow>
|
||||
{/each}
|
||||
</TableBody>
|
||||
</TableScroll>
|
||||
|
||||
{#if errorMessage}
|
||||
<Card danger>
|
||||
<div class="u-gap-16">
|
||||
<Heading tag="h6" size="7">Validation Failed</Heading>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
There are errors with the permissions of this destination. Please fix the errors and
|
||||
run the checks again.
|
||||
</p>
|
||||
|
||||
<br />
|
||||
|
||||
<div class="u-flex u-gap-16 u-cross-center card">
|
||||
<Output value={errorMessage}>{errorMessage}</Output>
|
||||
</div>
|
||||
</Card>
|
||||
{/if}
|
||||
</Container>
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
import { sdkForProject } from '$lib/stores/sdk';
|
||||
import { pageToOffset } from '$lib/helpers/load';
|
||||
import { Dependencies, PAGE_LIMIT } from '$lib/constants';
|
||||
import type { PageLoad } from './$types';
|
||||
|
||||
export const load: PageLoad = async ({ params, parent, depends }) => {
|
||||
depends(Dependencies.DESTINATION);
|
||||
await parent();
|
||||
const page = Number(params.page);
|
||||
const offset = pageToOffset(page, PAGE_LIMIT);
|
||||
|
||||
return {
|
||||
offset,
|
||||
destination: await sdkForProject.transfers.getDestination(params.destination)
|
||||
};
|
||||
};
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
<script lang="ts">
|
||||
import { Breadcrumbs } from '$lib/layout';
|
||||
import { organization } from '$lib/stores/organization';
|
||||
import { project } from '../../../../store';
|
||||
import { destination } from './store';
|
||||
|
||||
$: breadcrumbs = [
|
||||
{
|
||||
href: `/console/organization-${$organization?.$id}`,
|
||||
title: $organization?.name
|
||||
},
|
||||
{
|
||||
href: `/console/project-${$project?.$id}`,
|
||||
title: $project?.name
|
||||
},
|
||||
{
|
||||
href: `/console/project-${$project?.$id}/settings`,
|
||||
title: 'Settings'
|
||||
},
|
||||
{
|
||||
href: `/console/project-${$project?.$id}/settings/transfers`,
|
||||
title: 'Transfers'
|
||||
},
|
||||
{
|
||||
href: `/console/project-${$project?.$id}/settings/transfers/destinations`,
|
||||
title: 'Destinations'
|
||||
},
|
||||
{
|
||||
href: `/console/project-${$project?.$id}/settings/transfers/destinations/destination-${$destination?.$id}`,
|
||||
title: $destination?.name
|
||||
}
|
||||
];
|
||||
</script>
|
||||
|
||||
<Breadcrumbs {breadcrumbs} />
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/stores';
|
||||
import { Copy, Tab, Tabs } from '$lib/components';
|
||||
import { Pill } from '$lib/elements';
|
||||
import { isTabSelected } from '$lib/helpers/load';
|
||||
import { Cover, CoverTitle } from '$lib/layout';
|
||||
import { destination } from './store';
|
||||
|
||||
const projectId = $page.params.project;
|
||||
const destinationId = $page.params.destination;
|
||||
const path = `/console/project-${projectId}/settings/transfers/destinations/destination-${destinationId}`;
|
||||
const tabs = [
|
||||
{
|
||||
href: path,
|
||||
title: 'Summary',
|
||||
event: 'summary',
|
||||
hasChildren: true
|
||||
},
|
||||
{
|
||||
href: `${path}/settings`,
|
||||
event: 'settings',
|
||||
title: 'Settings'
|
||||
}
|
||||
];
|
||||
</script>
|
||||
|
||||
<Cover>
|
||||
<svelte:fragment slot="header">
|
||||
<CoverTitle href={`/console/project-${projectId}/settings/transfers/destinations`}>
|
||||
{$destination?.name}
|
||||
</CoverTitle>
|
||||
<Copy value={$destination?.$id} event="source">
|
||||
<Pill button>
|
||||
<span class="icon-duplicate" aria-hidden="true" />
|
||||
Destination ID
|
||||
</Pill>
|
||||
</Copy>
|
||||
</svelte:fragment>
|
||||
|
||||
<Tabs>
|
||||
{#each tabs as tab}
|
||||
<Tab
|
||||
href={tab.href}
|
||||
selected={isTabSelected(tab, $page.url.pathname, path, tabs)}
|
||||
event={tab.event}>
|
||||
{tab.title}
|
||||
</Tab>
|
||||
{/each}
|
||||
</Tabs>
|
||||
</Cover>
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
<script lang="ts">
|
||||
import { base } from '$app/paths';
|
||||
import { Box, CardGrid } from '$lib/components';
|
||||
import Heading from '$lib/components/heading.svelte';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import { toLocaleDateTime } from '$lib/helpers/date';
|
||||
import { Container } from '$lib/layout';
|
||||
import { app } from '$lib/stores/app';
|
||||
import { destination } from '../store';
|
||||
import Delete from './delete.svelte';
|
||||
|
||||
let showDelete = false;
|
||||
</script>
|
||||
|
||||
<Container>
|
||||
<CardGrid>
|
||||
<div class="grid-1-2-col-1 u-flex u-cross-center u-gap-16">
|
||||
<div class="avatar is-medium">
|
||||
<img
|
||||
src={`${base}/icons/${$app.themeInUse}/color/${$destination.type}.svg`}
|
||||
alt={`${$destination.type} Logo`} />
|
||||
</div>
|
||||
<div>
|
||||
<Heading tag="h6" size="7">{$destination.$id}</Heading>
|
||||
|
||||
<p class="text u-capitalize">{$destination.type}</p>
|
||||
</div>
|
||||
</div>
|
||||
<svelte:fragment slot="aside">
|
||||
<div class="u-flex u-main-space-between">
|
||||
<div>
|
||||
<p>Destination ID: {$destination.$id}</p>
|
||||
<p>Created at: {toLocaleDateTime($destination.$createdAt)}</p>
|
||||
<p>Updated at: {toLocaleDateTime($destination.$updatedAt)}</p>
|
||||
</div>
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
</CardGrid>
|
||||
|
||||
<CardGrid danger>
|
||||
<Heading tag="h6" size="7">Delete Destination</Heading>
|
||||
<p>The destination will be permanently deleted, This action is irreversible.</p>
|
||||
<svelte:fragment slot="aside">
|
||||
<Box>
|
||||
<svelte:fragment slot="title">
|
||||
<h6 class="u-bold u-trim-1">{$destination.$id}</h6>
|
||||
</svelte:fragment>
|
||||
<p>Last Updated: {toLocaleDateTime($destination.$updatedAt)}</p>
|
||||
</Box>
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="actions">
|
||||
<Button secondary on:click={() => (showDelete = true)}>Delete</Button>
|
||||
</svelte:fragment>
|
||||
</CardGrid>
|
||||
</Container>
|
||||
|
||||
<Delete bind:showDelete />
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { sdkForProject } from '$lib/stores/sdk';
|
||||
import type { PageLoad } from './$types';
|
||||
|
||||
export const load: PageLoad = async ({ params, parent, depends }) => {
|
||||
await parent();
|
||||
depends(Dependencies.DESTINATION);
|
||||
return {
|
||||
destination: await sdkForProject.transfers.getDestination(params.destination)
|
||||
};
|
||||
};
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import { base } from '$app/paths';
|
||||
import { page } from '$app/stores';
|
||||
import { Submit, trackEvent, trackError } from '$lib/actions/analytics';
|
||||
import { Modal } from '$lib/components';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { sdkForProject } from '$lib/stores/sdk';
|
||||
|
||||
export let showDelete = false;
|
||||
const destinationId = $page.params.destination;
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
await sdkForProject.transfers.deleteDestination(destinationId);
|
||||
showDelete = false;
|
||||
addNotification({
|
||||
type: 'success',
|
||||
message: `Destination has been deleted`
|
||||
});
|
||||
await goto(
|
||||
`${base}/console/project-${$page.params.project}/settings/transfers/destinations/`
|
||||
);
|
||||
trackEvent(Submit.DestinationDelete);
|
||||
} catch (error) {
|
||||
addNotification({
|
||||
type: 'error',
|
||||
message: error.message
|
||||
});
|
||||
trackError(error, Submit.DestinationDelete);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<Modal bind:show={showDelete} on:submit={handleSubmit} warning>
|
||||
<svelte:fragment slot="header">Delete Destination</svelte:fragment>
|
||||
<p>Are you sure you want to delete this Destination from your project?</p>
|
||||
<svelte:fragment slot="footer">
|
||||
<Button text on:click={() => (showDelete = false)}>Cancel</Button>
|
||||
<Button secondary submit>Delete</Button>
|
||||
</svelte:fragment>
|
||||
</Modal>
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
import { page } from '$app/stores';
|
||||
import { derived, writable, type Writable } from 'svelte/store';
|
||||
import type { Models } from '@aw-labs/appwrite-console';
|
||||
|
||||
export const destination = derived(page, ($page) => $page.data.destination as Models.Destination);
|
||||
export const execute: Writable<Models.Destination> = writable();
|
||||
@@ -0,0 +1,45 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/stores';
|
||||
import { Tab, Tabs } from '$lib/components';
|
||||
import { isTabSelected } from '$lib/helpers/load';
|
||||
import { Cover, CoverTitle } from '$lib/layout';
|
||||
|
||||
const projectId = $page.params.project;
|
||||
const path = `/console/project-${projectId}/transfers`;
|
||||
const tabs = [
|
||||
{
|
||||
href: path,
|
||||
title: 'Transfers',
|
||||
event: 'transfers',
|
||||
hasChildren: true
|
||||
},
|
||||
{
|
||||
href: `${path}/sources`,
|
||||
title: 'Sources',
|
||||
event: 'sources',
|
||||
hasChildren: true
|
||||
},
|
||||
{
|
||||
href: `${path}/destinations`,
|
||||
title: 'Destinations',
|
||||
event: 'destinations',
|
||||
hasChildren: true
|
||||
}
|
||||
];
|
||||
</script>
|
||||
|
||||
<Cover>
|
||||
<svelte:fragment slot="header">
|
||||
<CoverTitle>Transfers</CoverTitle>
|
||||
</svelte:fragment>
|
||||
<Tabs>
|
||||
{#each tabs as tab}
|
||||
<Tab
|
||||
href={tab.href}
|
||||
selected={isTabSelected(tab, $page.url.pathname, path, tabs)}
|
||||
event={tab.event}>
|
||||
{tab.title}
|
||||
</Tab>
|
||||
{/each}
|
||||
</Tabs>
|
||||
</Cover>
|
||||
@@ -0,0 +1,90 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/stores';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import { Empty, CardContainer, Copy, GridItem1, Heading, Pagination } from '$lib/components';
|
||||
import { Pill } from '$lib/elements';
|
||||
import { Container } from '$lib/layout';
|
||||
import { base } from '$app/paths';
|
||||
import { app } from '$lib/stores/app';
|
||||
import { wizard } from '$lib/stores/wizard';
|
||||
import { beforeNavigate } from '$app/navigation';
|
||||
import Create from '../../createSource.svelte';
|
||||
import type { PageData } from './$types';
|
||||
import { CARD_LIMIT } from '$lib/constants';
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
export let data: PageData;
|
||||
|
||||
let offset = 0;
|
||||
|
||||
const project = $page.params.project;
|
||||
|
||||
function openWizard() {
|
||||
wizard.start(Create);
|
||||
}
|
||||
|
||||
beforeNavigate(() => {
|
||||
wizard.hide();
|
||||
});
|
||||
|
||||
onMount(() => {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
|
||||
if (params.get('openWizard')) {
|
||||
openWizard();
|
||||
|
||||
params.delete('openWizard');
|
||||
window.history.replaceState({}, '', `${window.location.pathname}`);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<Container>
|
||||
<div class="u-flex u-gap-12 common-section u-main-space-between">
|
||||
<Heading tag="h2" size="5">All Sources</Heading>
|
||||
<Button on:click={openWizard} event="create_transfer">
|
||||
<span class="icon-plus" aria-hidden="true" /> <span class="text">Create source</span>
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{#if data.sources.total}
|
||||
<CardContainer {offset} event="transfers" total={data.sources.total} on:click={openWizard}>
|
||||
{#each data.sources.sources as source}
|
||||
<GridItem1
|
||||
href={`${base}/console/project-${project}/settings/transfers/sources/source-${source.$id}`}>
|
||||
<svelte:fragment slot="title">
|
||||
<div class="u-flex u-gap-16 u-cross-center">
|
||||
<div class="avatar is-medium">
|
||||
<img
|
||||
src={`${base}/icons/${$app.themeInUse}/color/${source.type}.svg`}
|
||||
alt="technology" />
|
||||
</div>
|
||||
<span class="text">{source.name}</span>
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
|
||||
<Copy value={source.$id} event="source">
|
||||
<Pill button><i class="icon-duplicate" />Source ID</Pill>
|
||||
</Copy>
|
||||
</GridItem1>
|
||||
{/each}
|
||||
<svelte:fragment slot="empty">
|
||||
<p>Create a new source</p>
|
||||
</svelte:fragment>
|
||||
</CardContainer>
|
||||
<div class="u-flex u-margin-block-start-32 u-main-space-between">
|
||||
<p class="text">Total results: {data.sources.total}</p>
|
||||
<Pagination
|
||||
limit={CARD_LIMIT}
|
||||
path={`/console/project-${$page.params.project}/sources`}
|
||||
offset={data.offset}
|
||||
sum={data.sources.total} />
|
||||
</div>
|
||||
{:else}
|
||||
<Empty
|
||||
single
|
||||
href="https://appwrite.io/docs/transfers#createSource"
|
||||
target="sources"
|
||||
on:click={openWizard} />
|
||||
{/if}
|
||||
</Container>
|
||||
@@ -0,0 +1,20 @@
|
||||
import { Query } from '@aw-labs/appwrite-console';
|
||||
import { sdkForProject } from '$lib/stores/sdk';
|
||||
import { pageToOffset } from '$lib/helpers/load';
|
||||
import { CARD_LIMIT } from '$lib/constants';
|
||||
import type { PageLoad } from './$types';
|
||||
|
||||
export const load: PageLoad = async ({ params, parent }) => {
|
||||
await parent();
|
||||
const page = Number(params.page);
|
||||
const offset = pageToOffset(page, CARD_LIMIT);
|
||||
|
||||
return {
|
||||
offset,
|
||||
sources: await sdkForProject.transfers.listSources([
|
||||
Query.limit(CARD_LIMIT),
|
||||
Query.offset(offset),
|
||||
Query.orderDesc('$createdAt')
|
||||
])
|
||||
};
|
||||
};
|
||||
+1
@@ -0,0 +1 @@
|
||||
<slot />
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
import { sdkForProject } from '$lib/stores/sdk';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import type { LayoutLoad } from './$types';
|
||||
import Breadcrumbs from './breadcrumbs.svelte';
|
||||
import Header from './header.svelte';
|
||||
import { error } from '@sveltejs/kit';
|
||||
|
||||
export const load: LayoutLoad = async ({ params, parent, depends }) => {
|
||||
await parent();
|
||||
depends(Dependencies.SOURCE);
|
||||
|
||||
try {
|
||||
return {
|
||||
header: Header,
|
||||
breadcrumbs: Breadcrumbs,
|
||||
source: await sdkForProject.transfers.getSource(params.source)
|
||||
};
|
||||
} catch (e) {
|
||||
throw error(e.code, e.message);
|
||||
}
|
||||
};
|
||||
+127
@@ -0,0 +1,127 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/stores';
|
||||
import Card from '$lib/components/card.svelte';
|
||||
import Heading from '$lib/components/heading.svelte';
|
||||
import Output from '$lib/components/output.svelte';
|
||||
import Button from '$lib/elements/forms/button.svelte';
|
||||
import Helper from '$lib/elements/forms/helper.svelte';
|
||||
import Pill from '$lib/elements/pill.svelte';
|
||||
import {
|
||||
TableBody,
|
||||
TableCellHead,
|
||||
TableCellText,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
TableScroll
|
||||
} from '$lib/elements/table';
|
||||
import { Container } from '$lib/layout';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { sdkForProject } from '$lib/stores/sdk';
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
const permissions = ['Users', 'Databases', 'Documents', 'Files', 'Functions'];
|
||||
|
||||
let isChecking = false;
|
||||
let errorMessage = null;
|
||||
let errors = permissions.map((permission) => {
|
||||
return {
|
||||
[permission]: false
|
||||
};
|
||||
});
|
||||
const sourceId = $page.params.source;
|
||||
|
||||
const runChecks = async () => {
|
||||
isChecking = true;
|
||||
errorMessage = null;
|
||||
|
||||
try {
|
||||
await sdkForProject.transfers.validateSource(sourceId);
|
||||
|
||||
isChecking = false;
|
||||
} catch (error) {
|
||||
addNotification({
|
||||
type: 'error',
|
||||
title: 'Error',
|
||||
message: error.message
|
||||
});
|
||||
errors = error.response.errors;
|
||||
|
||||
errorMessage = error.message;
|
||||
console.error(error);
|
||||
}
|
||||
|
||||
isChecking = false;
|
||||
};
|
||||
|
||||
onMount(() => {
|
||||
runChecks();
|
||||
});
|
||||
</script>
|
||||
|
||||
<Container>
|
||||
<div class="u-flex u-gap-12 common-section u-main-space-between">
|
||||
<Heading tag="h2" size="5">Source Checks</Heading>
|
||||
|
||||
<Button disabled={isChecking} on:click={runChecks}>
|
||||
<span class="icon-check-circle" aria-hidden="true" />
|
||||
<span class="text">Run Checks</span>
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<TableScroll>
|
||||
<TableHeader>
|
||||
<TableCellHead width={150}>Permission</TableCellHead>
|
||||
<TableCellHead width={60}>Status</TableCellHead>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{#each permissions as permission}
|
||||
<TableRow>
|
||||
<TableCellText title="Permission">
|
||||
{permission}
|
||||
|
||||
{#if errors[permission]}
|
||||
<Helper type="warning">
|
||||
{#each errors[permission] as error}
|
||||
{error} <br />
|
||||
{/each}
|
||||
</Helper>
|
||||
{/if}
|
||||
</TableCellText>
|
||||
<TableCellText title="Status">
|
||||
<Pill
|
||||
danger={errors[permission]}
|
||||
success={!errors[permission]}
|
||||
warning={isChecking}>
|
||||
{#if !errors[permission] && !isChecking}
|
||||
<span class="icon-check-circle" aria-hidden="true" />
|
||||
{:else if isChecking}
|
||||
<span class="icon-question-mark-circle" aria-hidden="true" />
|
||||
{:else}
|
||||
<span class="icon-x-circle" aria-hidden="true" />
|
||||
{/if}
|
||||
</Pill>
|
||||
</TableCellText>
|
||||
</TableRow>
|
||||
{/each}
|
||||
</TableBody>
|
||||
</TableScroll>
|
||||
|
||||
{#if errorMessage}
|
||||
<Card danger>
|
||||
<div class="u-gap-16">
|
||||
<Heading tag="h6" size="7">Validation Failed</Heading>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
There are errors with the permissions of this source. Please fix the errors and run
|
||||
the checks again.
|
||||
</p>
|
||||
|
||||
<br />
|
||||
|
||||
<div class="u-flex u-gap-16 u-cross-center card">
|
||||
<Output value={errorMessage}>{errorMessage}</Output>
|
||||
</div>
|
||||
</Card>
|
||||
{/if}
|
||||
</Container>
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
import { Query } from '@aw-labs/appwrite-console';
|
||||
import { sdkForProject } from '$lib/stores/sdk';
|
||||
import { pageToOffset } from '$lib/helpers/load';
|
||||
import { Dependencies, PAGE_LIMIT } from '$lib/constants';
|
||||
import type { PageLoad } from './$types';
|
||||
|
||||
export const load: PageLoad = async ({ params, parent, depends }) => {
|
||||
depends(Dependencies.DESTINATIONS);
|
||||
await parent();
|
||||
const page = Number(params.page);
|
||||
const offset = pageToOffset(page, PAGE_LIMIT);
|
||||
|
||||
return {
|
||||
offset,
|
||||
transfers: await sdkForProject.transfers.list([
|
||||
Query.limit(PAGE_LIMIT),
|
||||
Query.offset(offset),
|
||||
Query.orderDesc('$createdAt')
|
||||
])
|
||||
};
|
||||
};
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
<script lang="ts">
|
||||
import { Breadcrumbs } from '$lib/layout';
|
||||
import { organization } from '$lib/stores/organization';
|
||||
import { project } from '../../../../store';
|
||||
import { source } from './store';
|
||||
|
||||
$: breadcrumbs = [
|
||||
{
|
||||
href: `/console/organization-${$organization?.$id}`,
|
||||
title: $organization?.name
|
||||
},
|
||||
{
|
||||
href: `/console/project-${$project?.$id}`,
|
||||
title: $project?.name
|
||||
},
|
||||
{
|
||||
href: `/console/project-${$project?.$id}/settings`,
|
||||
title: 'Settings'
|
||||
},
|
||||
{
|
||||
href: `/console/project-${$project?.$id}/settings/transfers`,
|
||||
title: 'Transfers'
|
||||
},
|
||||
{
|
||||
href: `/console/project-${$project?.$id}/settings/transfers/sources`,
|
||||
title: 'Sources'
|
||||
},
|
||||
{
|
||||
href: `/console/project-${$project?.$id}/settings/transfers/sources/source-${$source?.$id}`,
|
||||
title: $source?.$id
|
||||
}
|
||||
];
|
||||
</script>
|
||||
|
||||
<Breadcrumbs {breadcrumbs} />
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/stores';
|
||||
import { Copy, Tab, Tabs } from '$lib/components';
|
||||
import { Pill } from '$lib/elements';
|
||||
import { isTabSelected } from '$lib/helpers/load';
|
||||
import { Cover, CoverTitle } from '$lib/layout';
|
||||
import { source } from './store';
|
||||
|
||||
const projectId = $page.params.project;
|
||||
const sourceId = $page.params.source;
|
||||
const path = `/console/project-${projectId}/settings/transfers/sources/source-${sourceId}`;
|
||||
const tabs = [
|
||||
{
|
||||
href: path,
|
||||
title: 'Summary',
|
||||
event: 'summary',
|
||||
hasChildren: true
|
||||
},
|
||||
{
|
||||
href: `${path}/settings`,
|
||||
event: 'settings',
|
||||
title: 'Settings'
|
||||
}
|
||||
];
|
||||
</script>
|
||||
|
||||
<Cover>
|
||||
<svelte:fragment slot="header">
|
||||
<CoverTitle href={`/console/project-${projectId}/settings/transfers/sources/`}>
|
||||
{$source?.$id}
|
||||
</CoverTitle>
|
||||
<Copy value={$source?.$id} event="source">
|
||||
<Pill button>
|
||||
<span class="icon-duplicate" aria-hidden="true" />
|
||||
Source ID
|
||||
</Pill>
|
||||
</Copy>
|
||||
</svelte:fragment>
|
||||
|
||||
<Tabs>
|
||||
{#each tabs as tab}
|
||||
<Tab
|
||||
href={tab.href}
|
||||
selected={isTabSelected(tab, $page.url.pathname, path, tabs)}
|
||||
event={tab.event}>
|
||||
{tab.title}
|
||||
</Tab>
|
||||
{/each}
|
||||
</Tabs>
|
||||
</Cover>
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
<script lang="ts">
|
||||
import { base } from '$app/paths';
|
||||
import { Box, CardGrid } from '$lib/components';
|
||||
import Heading from '$lib/components/heading.svelte';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import { toLocaleDateTime } from '$lib/helpers/date';
|
||||
import { Container } from '$lib/layout';
|
||||
import { app } from '$lib/stores/app';
|
||||
import { source } from '../store';
|
||||
import Delete from './delete.svelte';
|
||||
|
||||
let showDelete = false;
|
||||
</script>
|
||||
|
||||
<Container>
|
||||
<CardGrid>
|
||||
<div class="grid-1-2-col-1 u-flex u-cross-center u-gap-16">
|
||||
<div class="avatar is-medium">
|
||||
<img
|
||||
src={`${base}/icons/${$app.themeInUse}/color/${$source.type}.svg`}
|
||||
alt={`${$source.type} Logo`} />
|
||||
</div>
|
||||
<div>
|
||||
<Heading tag="h6" size="7">{$source.$id}</Heading>
|
||||
|
||||
<p class="text u-capitalize">{$source.type}</p>
|
||||
</div>
|
||||
</div>
|
||||
<svelte:fragment slot="aside">
|
||||
<div class="u-flex u-main-space-between">
|
||||
<div>
|
||||
<p>Source ID: {$source.$id}</p>
|
||||
<p>Created at: {toLocaleDateTime($source.$createdAt)}</p>
|
||||
<p>Updated at: {toLocaleDateTime($source.$updatedAt)}</p>
|
||||
</div>
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
</CardGrid>
|
||||
|
||||
<CardGrid danger>
|
||||
<Heading tag="h6" size="7">Delete Source</Heading>
|
||||
<p>The source will be permanently deleted, This action is irreversible.</p>
|
||||
<svelte:fragment slot="aside">
|
||||
<Box>
|
||||
<svelte:fragment slot="title">
|
||||
<h6 class="u-bold u-trim-1">{$source.$id}</h6>
|
||||
</svelte:fragment>
|
||||
<p>Last Updated: {toLocaleDateTime($source.$updatedAt)}</p>
|
||||
</Box>
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="actions">
|
||||
<Button secondary on:click={() => (showDelete = true)}>Delete</Button>
|
||||
</svelte:fragment>
|
||||
</CardGrid>
|
||||
</Container>
|
||||
|
||||
<Delete bind:showDelete />
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { sdkForProject } from '$lib/stores/sdk';
|
||||
import type { PageLoad } from './$types';
|
||||
|
||||
export const load: PageLoad = async ({ params, parent, depends }) => {
|
||||
await parent();
|
||||
depends(Dependencies.SOURCE);
|
||||
return {
|
||||
source: await sdkForProject.transfers.getSource(params.source)
|
||||
};
|
||||
};
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import { base } from '$app/paths';
|
||||
import { page } from '$app/stores';
|
||||
import { Submit, trackEvent, trackError } from '$lib/actions/analytics';
|
||||
import { Modal } from '$lib/components';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { sdkForProject } from '$lib/stores/sdk';
|
||||
|
||||
export let showDelete = false;
|
||||
const sourceId = $page.params.source;
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
await sdkForProject.transfers.deleteSource(sourceId);
|
||||
showDelete = false;
|
||||
addNotification({
|
||||
type: 'success',
|
||||
message: `Source has been deleted`
|
||||
});
|
||||
await goto(
|
||||
`${base}/console/project-${$page.params.project}/settings/transfers/sources`
|
||||
);
|
||||
trackEvent(Submit.SourceDelete);
|
||||
} catch (error) {
|
||||
addNotification({
|
||||
type: 'error',
|
||||
message: error.message
|
||||
});
|
||||
trackError(error, Submit.SourceDelete);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<Modal bind:show={showDelete} on:submit={handleSubmit} warning>
|
||||
<svelte:fragment slot="header">Delete Source</svelte:fragment>
|
||||
<p>Are you sure you want to delete this Source from your project?</p>
|
||||
<svelte:fragment slot="footer">
|
||||
<Button text on:click={() => (showDelete = false)}>Cancel</Button>
|
||||
<Button secondary submit>Delete</Button>
|
||||
</svelte:fragment>
|
||||
</Modal>
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
import { page } from '$app/stores';
|
||||
import { derived, writable, type Writable } from 'svelte/store';
|
||||
import type { Models } from '@aw-labs/appwrite-console';
|
||||
|
||||
export const source = derived(page, ($page) => $page.data.source as Models.Source);
|
||||
export const execute: Writable<Models.Source> = writable();
|
||||
+1
@@ -0,0 +1 @@
|
||||
<slot />
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
import { sdkForProject } from '$lib/stores/sdk';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import type { LayoutLoad } from './$types';
|
||||
import Breadcrumbs from './breadcrumbs.svelte';
|
||||
import Header from './header.svelte';
|
||||
import { error } from '@sveltejs/kit';
|
||||
|
||||
export const load: LayoutLoad = async ({ params, parent, depends }) => {
|
||||
await parent();
|
||||
depends(Dependencies.TRANSFER);
|
||||
|
||||
try {
|
||||
return {
|
||||
header: Header,
|
||||
breadcrumbs: Breadcrumbs,
|
||||
transfer: await sdkForProject.transfers.get(params.transfer)
|
||||
};
|
||||
} catch (e) {
|
||||
throw error(e.code, e.message);
|
||||
}
|
||||
};
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
<script lang="ts">
|
||||
import { Container } from '$lib/layout';
|
||||
</script>
|
||||
|
||||
<Container>
|
||||
<div class="u-flex u-gap-12 common-section u-main-space-between">
|
||||
<button class="button u-center">Begin Transfer</button>
|
||||
</div>
|
||||
</Container>
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
import { Query } from '@aw-labs/appwrite-console';
|
||||
import { sdkForProject } from '$lib/stores/sdk';
|
||||
import { pageToOffset } from '$lib/helpers/load';
|
||||
import { Dependencies, PAGE_LIMIT } from '$lib/constants';
|
||||
import type { PageLoad } from './$types';
|
||||
|
||||
export const load: PageLoad = async ({ params, parent, depends }) => {
|
||||
depends(Dependencies.TRANSFERS);
|
||||
await parent();
|
||||
const page = Number(params.page);
|
||||
const offset = pageToOffset(page, PAGE_LIMIT);
|
||||
|
||||
return {
|
||||
offset,
|
||||
transfers: await sdkForProject.transfers.list([
|
||||
Query.limit(PAGE_LIMIT),
|
||||
Query.offset(offset),
|
||||
Query.orderDesc('$createdAt')
|
||||
])
|
||||
};
|
||||
};
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
<script lang="ts">
|
||||
import { Breadcrumbs } from '$lib/layout';
|
||||
import { organization } from '$lib/stores/organization';
|
||||
import { project } from '../../../store';
|
||||
import { transfer } from './store';
|
||||
|
||||
$: breadcrumbs = [
|
||||
{
|
||||
href: `/console/organization-${$organization?.$id}`,
|
||||
title: $organization?.name
|
||||
},
|
||||
{
|
||||
href: `/console/project-${$project?.$id}`,
|
||||
title: $project?.name
|
||||
},
|
||||
{
|
||||
href: `/console/project-${$project?.$id}/settings`,
|
||||
title: 'Settings'
|
||||
},
|
||||
{
|
||||
href: `/console/project-${$project?.$id}/settings/transfers`,
|
||||
title: 'Transfers'
|
||||
},
|
||||
{
|
||||
href: `/console/project-${$project?.$id}/settings/transfers/transfer-${$transfer?.$id}`,
|
||||
title: $transfer?.$id
|
||||
}
|
||||
];
|
||||
</script>
|
||||
|
||||
<Breadcrumbs {breadcrumbs} />
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/stores';
|
||||
import { Copy, Tab, Tabs } from '$lib/components';
|
||||
import { Pill } from '$lib/elements';
|
||||
import { isTabSelected } from '$lib/helpers/load';
|
||||
import { Cover, CoverTitle } from '$lib/layout';
|
||||
import { transfer } from './store';
|
||||
|
||||
const projectId = $page.params.project;
|
||||
const transferId = $page.params.transfer;
|
||||
const path = `/console/project-${projectId}/settings/transfers/transfer-${transferId}`;
|
||||
const tabs = [
|
||||
{
|
||||
href: path,
|
||||
title: 'Summary',
|
||||
event: 'summary',
|
||||
hasChildren: true
|
||||
},
|
||||
{
|
||||
href: `${path}/logs`,
|
||||
title: 'Logs',
|
||||
event: 'logs',
|
||||
hasChildren: true
|
||||
},
|
||||
{
|
||||
href: `${path}/settings`,
|
||||
event: 'settings',
|
||||
title: 'Settings'
|
||||
}
|
||||
];
|
||||
</script>
|
||||
|
||||
<Cover>
|
||||
<svelte:fragment slot="header">
|
||||
<CoverTitle href={`/console/project-${projectId}/settings/transfers/`}>
|
||||
{$transfer?.$id}
|
||||
</CoverTitle>
|
||||
<Copy value={$transfer?.$id} event="transfer">
|
||||
<Pill button>
|
||||
<span class="icon-duplicate" aria-hidden="true" />
|
||||
Transfer ID
|
||||
</Pill>
|
||||
</Copy>
|
||||
</svelte:fragment>
|
||||
|
||||
<Tabs>
|
||||
{#each tabs as tab}
|
||||
<Tab
|
||||
href={tab.href}
|
||||
selected={isTabSelected(tab, $page.url.pathname, path, tabs)}
|
||||
event={tab.event}>
|
||||
{tab.title}
|
||||
</Tab>
|
||||
{/each}
|
||||
</Tabs>
|
||||
</Cover>
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { Container } from '$lib/layout';
|
||||
</script>
|
||||
|
||||
<Container />
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { sdkForProject } from '$lib/stores/sdk';
|
||||
import type { PageLoad } from './$types';
|
||||
|
||||
export const load: PageLoad = async ({ params, parent, depends }) => {
|
||||
await parent();
|
||||
depends(Dependencies.VARIABLES);
|
||||
|
||||
return {
|
||||
transfer: await sdkForProject.transfers.get(params.transfer)
|
||||
};
|
||||
};
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
<script lang="ts">
|
||||
import { base } from '$app/paths';
|
||||
import { Box, CardGrid } from '$lib/components';
|
||||
import Card from '$lib/components/card.svelte';
|
||||
import Heading from '$lib/components/heading.svelte';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import { toLocaleDateTime } from '$lib/helpers/date';
|
||||
import { Container } from '$lib/layout';
|
||||
import { app } from '$lib/stores/app';
|
||||
import { project } from '../../../../store';
|
||||
import { transfer } from '../store';
|
||||
import type { PageData } from './$types';
|
||||
import Delete from './delete.svelte';
|
||||
|
||||
export let data: PageData;
|
||||
|
||||
let showDelete = false;
|
||||
</script>
|
||||
|
||||
<Container>
|
||||
<CardGrid>
|
||||
<div class="grid-1-2-col-1 u-flex u-cross-center u-gap-16">
|
||||
<div class="avatar is-medium">
|
||||
<span class="icon-text"> 🛫 </span>
|
||||
</div>
|
||||
<div>
|
||||
<Heading tag="h6" size="7">{$transfer.$id}</Heading>
|
||||
</div>
|
||||
</div>
|
||||
<svelte:fragment slot="aside">
|
||||
<div class="u-flex u-main-space-between">
|
||||
<div>
|
||||
<p>Transfer ID: {$transfer.$id}</p>
|
||||
<p>Created at: {toLocaleDateTime($transfer.$createdAt)}</p>
|
||||
<p>Updated at: {toLocaleDateTime($transfer.$updatedAt)}</p>
|
||||
</div>
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
</CardGrid>
|
||||
|
||||
<Card>
|
||||
<div class="grid-box u-flex">
|
||||
{#if data.source}
|
||||
<a
|
||||
class="u-stretch u-flex u-cross-center u-gap-16 card"
|
||||
href={`/console/project-${$project.$id}/settings/transfers/sources/source-${data.source.$id}`}>
|
||||
<div class="avatar is-medium">
|
||||
<img
|
||||
src={`${base}/icons/${$app.themeInUse}/color/${data.source.type}.svg`}
|
||||
alt="technology" />
|
||||
</div>
|
||||
<div>
|
||||
<Heading tag="h6" size="7">{data.source.name}</Heading>
|
||||
|
||||
<p class="text u-capitalize">
|
||||
{data.source.type}
|
||||
</p>
|
||||
</div>
|
||||
</a>
|
||||
{:else}
|
||||
<div class="u-stretch u-flex u-cross-center u-gap-16 card">
|
||||
<div class="avatar is-medium">
|
||||
<span class="icon-question-mark-circle" />
|
||||
</div>
|
||||
<div>
|
||||
<Heading tag="h6" size="7">Deleted Source</Heading>
|
||||
|
||||
<p class="text u-capitalize">Unknown</p>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
<span class="icon-arrow-right" style="align-self: center;" />
|
||||
{#if data.destination}
|
||||
<a
|
||||
class="u-stretch u-flex u-cross-center u-gap-16 card"
|
||||
href={`/console/project-${$project.$id}/settings/transfers/destinations/destination-${data.destination.$id}`}>
|
||||
<div class="avatar is-medium">
|
||||
<img
|
||||
src={`${base}/icons/${$app.themeInUse}/color/${data.destination.type}.svg`}
|
||||
alt="technology" />
|
||||
</div>
|
||||
<div>
|
||||
<Heading tag="h6" size="7">{data.destination.name}</Heading>
|
||||
|
||||
<p class="text u-capitalize">
|
||||
{data.destination.type}
|
||||
</p>
|
||||
</div>
|
||||
</a>
|
||||
{:else}
|
||||
<div class="u-stretch u-flex u-cross-center u-gap-16 card">
|
||||
<div class="avatar is-medium">
|
||||
<span class="icon-question-mark-circle" />
|
||||
</div>
|
||||
<div>
|
||||
<Heading tag="h6" size="7">Deleted Destination</Heading>
|
||||
|
||||
<p class="text u-capitalize">Unknown</p>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<CardGrid danger>
|
||||
<Heading tag="h6" size="7">Delete Transfer</Heading>
|
||||
<p>
|
||||
The transfer will be permanently deleted, including all in progress transfers associated
|
||||
with it. This action is irreversible.
|
||||
</p>
|
||||
<svelte:fragment slot="aside">
|
||||
<Box>
|
||||
<svelte:fragment slot="title">
|
||||
<h6 class="u-bold u-trim-1">{$transfer.$id}</h6>
|
||||
</svelte:fragment>
|
||||
<p>Last Updated: {toLocaleDateTime($transfer.$updatedAt)}</p>
|
||||
</Box>
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="actions">
|
||||
<Button secondary on:click={() => (showDelete = true)}>Delete</Button>
|
||||
</svelte:fragment>
|
||||
</CardGrid>
|
||||
</Container>
|
||||
|
||||
<Delete bind:showDelete />
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { sdkForProject } from '$lib/stores/sdk';
|
||||
import type { PageLoad } from './$types';
|
||||
|
||||
export const load: PageLoad = async ({ params, parent, depends }) => {
|
||||
await parent();
|
||||
depends(Dependencies.VARIABLES);
|
||||
|
||||
const transfer = await sdkForProject.transfers.get(params.transfer);
|
||||
const source = async () => {
|
||||
try {
|
||||
return await sdkForProject.transfers.getSource(transfer.source);
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const destination = async () => {
|
||||
try {
|
||||
return await sdkForProject.transfers.getDestination(transfer.destination);
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
transfer: transfer,
|
||||
source: await source(),
|
||||
destination: await destination()
|
||||
};
|
||||
};
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import { base } from '$app/paths';
|
||||
import { page } from '$app/stores';
|
||||
import { Submit, trackEvent, trackError } from '$lib/actions/analytics';
|
||||
import { Modal } from '$lib/components';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { sdkForProject } from '$lib/stores/sdk';
|
||||
|
||||
export let showDelete = false;
|
||||
const transferId = $page.params.transfer;
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
await sdkForProject.transfers.delete(transferId);
|
||||
showDelete = false;
|
||||
addNotification({
|
||||
type: 'success',
|
||||
message: `Transfer has been deleted`
|
||||
});
|
||||
await goto(`${base}/console/project-${$page.params.project}/settings/transfers`);
|
||||
trackEvent(Submit.TransferDelete);
|
||||
} catch (error) {
|
||||
addNotification({
|
||||
type: 'error',
|
||||
message: error.message
|
||||
});
|
||||
trackError(error, Submit.TransferDelete);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<Modal bind:show={showDelete} on:submit={handleSubmit} warning>
|
||||
<svelte:fragment slot="header">Delete Transfer</svelte:fragment>
|
||||
<p>Are you sure you want to delete this transfer and all in progress data from your project?</p>
|
||||
<svelte:fragment slot="footer">
|
||||
<Button text on:click={() => (showDelete = false)}>Cancel</Button>
|
||||
<Button secondary submit>Delete</Button>
|
||||
</svelte:fragment>
|
||||
</Modal>
|
||||
@@ -0,0 +1,6 @@
|
||||
import { page } from '$app/stores';
|
||||
import { derived, writable, type Writable } from 'svelte/store';
|
||||
import type { Models } from '@aw-labs/appwrite-console';
|
||||
|
||||
export const transfer = derived(page, ($page) => $page.data.transfer as Models.Transfer);
|
||||
export const execute: Writable<Models.Transfer> = writable();
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
<script lang="ts">
|
||||
</script>
|
||||
|
||||
<div />
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
<script lang="ts">
|
||||
import { app } from '$lib/stores/app';
|
||||
import { WizardStep } from '$lib/layout';
|
||||
import Pill from '$lib/elements/pill.svelte';
|
||||
import { createDestination } from '../store';
|
||||
|
||||
let options = ['Appwrite'];
|
||||
</script>
|
||||
|
||||
<WizardStep>
|
||||
<svelte:fragment slot="title">Select Provider</svelte:fragment>
|
||||
<svelte:fragment slot="subtitle"
|
||||
>Please select the provider you want to use for this destination.</svelte:fragment>
|
||||
|
||||
<section class="common-section">
|
||||
<h2 class="heading-level-6 common-section">Destinations</h2>
|
||||
<input
|
||||
class="u-hide"
|
||||
type="checkbox"
|
||||
name="isVerified"
|
||||
required
|
||||
bind:checked={$createDestination.type.length} />
|
||||
<ul class="grid-box common-section">
|
||||
{#each options.filter((p) => p !== 'Local') as destination}
|
||||
<li class="grid-box-item">
|
||||
<button
|
||||
on:click={() => {
|
||||
createDestination.update((d) => {
|
||||
d.type = destination.toLowerCase();
|
||||
return d;
|
||||
});
|
||||
}}
|
||||
class="card u-flex u-flex-vertical u-cross-center u-width-full-line">
|
||||
<div class="image-item">
|
||||
<img
|
||||
height="20"
|
||||
width="20"
|
||||
src={`/icons/${
|
||||
$app.themeInUse
|
||||
}/color/${destination.toLowerCase()}.svg`}
|
||||
alt={destination} />
|
||||
</div>
|
||||
<p class="u-margin-block-start-8">{destination}</p>
|
||||
<div class="u-margin-block-start-24">
|
||||
{#if $createDestination.type && $createDestination.type.toLowerCase() == destination.toLowerCase()}
|
||||
<Pill success={true}>
|
||||
<span class="icon-check-circle" />
|
||||
</Pill>
|
||||
{/if}
|
||||
</div>
|
||||
</button>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</section>
|
||||
</WizardStep>
|
||||
+165
@@ -0,0 +1,165 @@
|
||||
<script lang="ts">
|
||||
import { InputText } from '$lib/elements/forms';
|
||||
import FormList from '$lib/elements/forms/formList.svelte';
|
||||
import { WizardStep } from '$lib/layout';
|
||||
import { onMount } from 'svelte';
|
||||
import { createDestination } from '../store';
|
||||
import FirebaseAuthflow from './FirebaseAuthflow.svelte';
|
||||
|
||||
let credentialSchemas = {
|
||||
nhost: {
|
||||
host: {
|
||||
type: 'string',
|
||||
label: 'Host',
|
||||
placeholder: 'example.db.eu-central-1.nhost.run',
|
||||
required: true,
|
||||
value: ''
|
||||
},
|
||||
port: {
|
||||
type: 'number',
|
||||
label: 'Port',
|
||||
placeholder: '5432',
|
||||
required: true,
|
||||
value: '5432'
|
||||
},
|
||||
database: {
|
||||
type: 'string',
|
||||
label: 'Database',
|
||||
placeholder: 'postgres',
|
||||
required: true,
|
||||
value: 'postgres'
|
||||
},
|
||||
username: {
|
||||
type: 'string',
|
||||
label: 'Username',
|
||||
placeholder: 'postgres',
|
||||
required: true,
|
||||
value: 'postgres'
|
||||
},
|
||||
password: {
|
||||
type: 'string',
|
||||
label: 'Password',
|
||||
placeholder: 'password',
|
||||
required: true,
|
||||
value: ''
|
||||
}
|
||||
},
|
||||
supabase: {
|
||||
host: {
|
||||
type: 'string',
|
||||
label: 'Host',
|
||||
placeholder: 'db.example.supabase.co',
|
||||
required: true
|
||||
},
|
||||
port: {
|
||||
type: 'number',
|
||||
label: 'Port',
|
||||
placeholder: '5432',
|
||||
required: false,
|
||||
value: '5432'
|
||||
},
|
||||
database: {
|
||||
type: 'string',
|
||||
label: 'Database',
|
||||
placeholder: 'postgres',
|
||||
required: true
|
||||
},
|
||||
username: {
|
||||
type: 'string',
|
||||
label: 'Username',
|
||||
placeholder: 'postgres',
|
||||
required: true
|
||||
},
|
||||
password: {
|
||||
type: 'string',
|
||||
label: 'Password',
|
||||
placeholder: 'password',
|
||||
required: true
|
||||
}
|
||||
},
|
||||
appwrite: {
|
||||
endpoint: {
|
||||
type: 'string',
|
||||
label: 'Endpoint',
|
||||
placeholder: 'https://cloud.appwrite.io/v1',
|
||||
required: true
|
||||
},
|
||||
project: {
|
||||
type: 'string',
|
||||
label: 'Project',
|
||||
placeholder: 'Project ID',
|
||||
required: true
|
||||
},
|
||||
key: {
|
||||
type: 'string',
|
||||
label: 'Key',
|
||||
placeholder: 'API Key',
|
||||
required: true
|
||||
}
|
||||
},
|
||||
firebase: {
|
||||
'account-credentials': {
|
||||
type: 'file',
|
||||
label: 'Account Credentials',
|
||||
placeholder: '{type: "service_account", ...}',
|
||||
required: true
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
onMount(() => {
|
||||
if ($createDestination.type) {
|
||||
Object.keys(credentialSchemas[$createDestination.type]).forEach((key) => {
|
||||
if ($createDestination.data[key]) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (credentialSchemas[$createDestination.type][key].value) {
|
||||
$createDestination.data[key] =
|
||||
credentialSchemas[$createDestination.type][key].value;
|
||||
} else {
|
||||
$createDestination.data[key] = '';
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
let firebaseFallback = false;
|
||||
</script>
|
||||
|
||||
<WizardStep>
|
||||
<svelte:fragment slot="title">Provide Credentials</svelte:fragment>
|
||||
<svelte:fragment slot="subtitle"
|
||||
>Please provide your authentication details for your provider.</svelte:fragment>
|
||||
{#if $createDestination.type === 'firebase' && !firebaseFallback}
|
||||
<FirebaseAuthflow />
|
||||
<div class="flex flex-row justify-center">
|
||||
<button
|
||||
class="tag tooltip"
|
||||
on:click|preventDefault={() => {
|
||||
firebaseFallback = true;
|
||||
}}>
|
||||
<span class="icon-lock-closed" aria-hidden="true" />
|
||||
|
||||
<span class="text">Manual Authentication</span>
|
||||
|
||||
<span class="tooltip-popup" role="tooltip">
|
||||
<p class="text u-margin-block-start-8">
|
||||
Manually enter your account credentials.
|
||||
</p>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
{:else}
|
||||
<FormList>
|
||||
{#each Object.keys(credentialSchemas[$createDestination.type]) as key}
|
||||
<InputText
|
||||
id={key}
|
||||
label={credentialSchemas[$createDestination.type][key].label}
|
||||
placeholder={credentialSchemas[$createDestination.type][key].placeholder}
|
||||
bind:value={$createDestination.data[key]}
|
||||
required={credentialSchemas[$createDestination.type][key].required} />
|
||||
{/each}
|
||||
</FormList>
|
||||
{/if}
|
||||
</WizardStep>
|
||||
+147
@@ -0,0 +1,147 @@
|
||||
<script lang="ts">
|
||||
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
|
||||
import { WizardStep } from '$lib/layout';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { sdkForProject } from '$lib/stores/sdk';
|
||||
import { onMount } from 'svelte';
|
||||
import { createDestination } from '../store';
|
||||
import {
|
||||
TableBody,
|
||||
TableCellHead,
|
||||
TableCellText,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
TableScroll
|
||||
} from '$lib/elements/table';
|
||||
import Pill from '$lib/elements/pill.svelte';
|
||||
import Card from '$lib/components/card.svelte';
|
||||
import Heading from '$lib/components/heading.svelte';
|
||||
import Output from '$lib/components/output.svelte';
|
||||
import Helper from '$lib/elements/forms/helper.svelte';
|
||||
|
||||
const permissions = ['Users', 'Databases', 'Documents', 'Files', 'Functions'];
|
||||
let errors = permissions.map((permission) => {
|
||||
return {
|
||||
[permission]: false
|
||||
};
|
||||
});
|
||||
let isChecking = false;
|
||||
let errorMessage = null;
|
||||
let canProgress = false;
|
||||
|
||||
async function validate() {
|
||||
isChecking = true;
|
||||
errorMessage = null;
|
||||
canProgress = false;
|
||||
|
||||
try {
|
||||
switch ($createDestination.type) {
|
||||
case 'appwrite':
|
||||
await sdkForProject.transfers.validateAppwriteDestination(
|
||||
$createDestination.data['project'],
|
||||
$createDestination.data['endpoint'],
|
||||
$createDestination.data['key']
|
||||
);
|
||||
}
|
||||
|
||||
isChecking = false;
|
||||
canProgress = true;
|
||||
addNotification({
|
||||
message: `Destination is valid`,
|
||||
type: 'success'
|
||||
});
|
||||
trackEvent(Submit.DestinationValidate, {
|
||||
customId: !!$createDestination.id
|
||||
});
|
||||
} catch (error) {
|
||||
if (error.response.errors) {
|
||||
errors = error.response.errors;
|
||||
} else {
|
||||
errors = permissions.map((permission) => {
|
||||
return {
|
||||
[permission]: true
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
addNotification({
|
||||
message: error.message,
|
||||
type: 'error'
|
||||
});
|
||||
trackError(error, Submit.DestinationValidate);
|
||||
errorMessage = error.message;
|
||||
console.error(error);
|
||||
isChecking = false;
|
||||
canProgress = false;
|
||||
}
|
||||
}
|
||||
|
||||
onMount(async () => {
|
||||
validate();
|
||||
});
|
||||
</script>
|
||||
|
||||
<WizardStep>
|
||||
<svelte:fragment slot="title">Validation</svelte:fragment>
|
||||
<svelte:fragment slot="subtitle"
|
||||
>Please wait while we perform valdiation checks.</svelte:fragment>
|
||||
<input class="u-hide" type="checkbox" name="isVerified" required bind:checked={canProgress} />
|
||||
|
||||
<div>
|
||||
{#if errorMessage}
|
||||
<Card danger>
|
||||
<div class="u-gap-16">
|
||||
<Heading tag="h6" size="7">Validation Failed</Heading>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
There were errors when validating this destination. Please go back and fix the
|
||||
errors then run the checks again.
|
||||
</p>
|
||||
|
||||
<br />
|
||||
|
||||
<div class="u-flex u-gap-16 u-cross-center card">
|
||||
<Output value={errorMessage}>{errorMessage}</Output>
|
||||
</div>
|
||||
</Card>
|
||||
{/if}
|
||||
<TableScroll>
|
||||
<TableHeader>
|
||||
<TableCellHead width={150}>Permission</TableCellHead>
|
||||
<TableCellHead width={60}>Status</TableCellHead>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{#each permissions as permission}
|
||||
<TableRow>
|
||||
<TableCellText title="Permission">
|
||||
{permission}
|
||||
|
||||
{#if errors[permission]}
|
||||
<Helper type="warning">
|
||||
{#each errors[permission] as error}
|
||||
{error} <br />
|
||||
{/each}
|
||||
</Helper>
|
||||
{/if}
|
||||
</TableCellText>
|
||||
<TableCellText title="Permission Level">
|
||||
<Pill
|
||||
danger={errors[permission]}
|
||||
success={!errors[permission]}
|
||||
warning={isChecking}>
|
||||
{#if !errors[permission] && !isChecking}
|
||||
<span class="icon-check-circle" aria-hidden="true" />
|
||||
{:else if isChecking}
|
||||
<span class="icon-question-mark-circle" aria-hidden="true" />
|
||||
{:else}
|
||||
<span class="icon-x-circle" aria-hidden="true" />
|
||||
{/if}
|
||||
</Pill>
|
||||
</TableCellText>
|
||||
</TableRow>
|
||||
{/each}
|
||||
</TableBody>
|
||||
</TableScroll>
|
||||
</div>
|
||||
</WizardStep>
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
<script lang="ts">
|
||||
import { WizardStep } from '$lib/layout';
|
||||
</script>
|
||||
|
||||
<WizardStep>
|
||||
<svelte:fragment slot="title">Summary</svelte:fragment>
|
||||
<svelte:fragment slot="subtitle">
|
||||
Please review your destination details and click "Create" to continue.
|
||||
</svelte:fragment>
|
||||
|
||||
<h1>TODO: Animated graphic of ticket coming out of ticket machine with details on it.</h1>
|
||||
</WizardStep>
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
<script lang="ts">
|
||||
/**
|
||||
* Redirect to OAuth2 screen -> Get token -> Create service account -> Get Projects -> Request user to select project -> Create destination
|
||||
*/
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<button class="button">Redirect to Google</button>
|
||||
</div>
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
<script lang="ts">
|
||||
import { app } from '$lib/stores/app';
|
||||
import { WizardStep } from '$lib/layout';
|
||||
import Pill from '$lib/elements/pill.svelte';
|
||||
import { createSource } from '../store';
|
||||
|
||||
let options = ['Firebase', 'Supabase', 'NHost', 'Appwrite'];
|
||||
</script>
|
||||
|
||||
<WizardStep>
|
||||
<svelte:fragment slot="title">Select Provider</svelte:fragment>
|
||||
<svelte:fragment slot="subtitle"
|
||||
>Please select the provider you want to use for this source.</svelte:fragment>
|
||||
|
||||
<section class="common-section">
|
||||
<h2 class="heading-level-6 common-section">Sources</h2>
|
||||
<input
|
||||
class="u-hide"
|
||||
type="checkbox"
|
||||
name="isVerified"
|
||||
required
|
||||
bind:checked={$createSource.type.length} />
|
||||
<ul class="grid-box common-section">
|
||||
{#each options.filter((p) => p !== 'Local') as source}
|
||||
<li class="grid-box-item">
|
||||
<button
|
||||
on:click={() => {
|
||||
createSource.update((d) => {
|
||||
d.type = source.toLowerCase();
|
||||
return d;
|
||||
});
|
||||
}}
|
||||
class="card u-flex u-flex-vertical u-cross-center u-width-full-line">
|
||||
<div class="image-item">
|
||||
<img
|
||||
height="20"
|
||||
width="20"
|
||||
src={`/icons/${$app.themeInUse}/color/${source.toLowerCase()}.svg`}
|
||||
alt={source} />
|
||||
</div>
|
||||
<p class="u-margin-block-start-8">{source}</p>
|
||||
<div class="u-margin-block-start-24">
|
||||
{#if $createSource.type && $createSource.type.toLowerCase() == source.toLowerCase()}
|
||||
<Pill success={true}>
|
||||
<span class="icon-check-circle" />
|
||||
</Pill>
|
||||
{/if}
|
||||
</div>
|
||||
</button>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</section>
|
||||
</WizardStep>
|
||||
+168
@@ -0,0 +1,168 @@
|
||||
<script lang="ts">
|
||||
import { InputText } from '$lib/elements/forms';
|
||||
import FormList from '$lib/elements/forms/formList.svelte';
|
||||
import { WizardStep } from '$lib/layout';
|
||||
import { onMount } from 'svelte';
|
||||
import { createSource } from '../store';
|
||||
import FirebaseAuthflow from './FirebaseAuthflow.svelte';
|
||||
|
||||
// Scan the API AST for all the available credentials
|
||||
|
||||
let credentialSchemas = {
|
||||
nhost: {
|
||||
host: {
|
||||
type: 'string',
|
||||
label: 'Host',
|
||||
placeholder: 'example.db.eu-central-1.nhost.run',
|
||||
required: true,
|
||||
value: ''
|
||||
},
|
||||
port: {
|
||||
type: 'number',
|
||||
label: 'Port',
|
||||
placeholder: '5432',
|
||||
required: false,
|
||||
value: '5432'
|
||||
},
|
||||
database: {
|
||||
type: 'string',
|
||||
label: 'Database',
|
||||
placeholder: 'postgres',
|
||||
required: true,
|
||||
value: 'postgres'
|
||||
},
|
||||
username: {
|
||||
type: 'string',
|
||||
label: 'Username',
|
||||
placeholder: 'postgres',
|
||||
required: false,
|
||||
value: 'postgres'
|
||||
},
|
||||
password: {
|
||||
type: 'string',
|
||||
label: 'Password',
|
||||
placeholder: 'password',
|
||||
required: true,
|
||||
value: ''
|
||||
}
|
||||
},
|
||||
supabase: {
|
||||
host: {
|
||||
type: 'string',
|
||||
label: 'Host',
|
||||
placeholder: 'example.supabase.co',
|
||||
required: true
|
||||
},
|
||||
port: {
|
||||
type: 'number',
|
||||
label: 'Port',
|
||||
placeholder: '5432',
|
||||
required: false,
|
||||
value: '5432'
|
||||
},
|
||||
database: {
|
||||
type: 'string',
|
||||
label: 'Database',
|
||||
placeholder: 'postgres',
|
||||
required: false,
|
||||
value: 'postgres'
|
||||
},
|
||||
username: {
|
||||
type: 'string',
|
||||
label: 'Username',
|
||||
placeholder: 'postgres',
|
||||
required: false,
|
||||
value: 'postgres'
|
||||
},
|
||||
password: {
|
||||
type: 'string',
|
||||
label: 'Password',
|
||||
placeholder: 'password',
|
||||
required: true
|
||||
}
|
||||
},
|
||||
appwrite: {
|
||||
endpoint: {
|
||||
type: 'string',
|
||||
label: 'Endpoint',
|
||||
placeholder: 'https://cloud.appwrite.io/v1',
|
||||
required: true
|
||||
},
|
||||
project: {
|
||||
type: 'string',
|
||||
label: 'Project',
|
||||
placeholder: 'Project ID',
|
||||
required: true
|
||||
},
|
||||
key: {
|
||||
type: 'string',
|
||||
label: 'Key',
|
||||
placeholder: 'API Key',
|
||||
required: true
|
||||
}
|
||||
},
|
||||
firebase: {
|
||||
'account-credentials': {
|
||||
type: 'file',
|
||||
label: 'Account Credentials',
|
||||
placeholder: '{type: "service_account", ...}',
|
||||
required: true
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
onMount(() => {
|
||||
if ($createSource.type) {
|
||||
Object.keys(credentialSchemas[$createSource.type]).forEach((key) => {
|
||||
if ($createSource.data[key]) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (credentialSchemas[$createSource.type][key].value) {
|
||||
$createSource.data[key] = credentialSchemas[$createSource.type][key].value;
|
||||
} else {
|
||||
$createSource.data[key] = '';
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
let firebaseFallback = false;
|
||||
</script>
|
||||
|
||||
<WizardStep>
|
||||
<svelte:fragment slot="title">Provide Credentials</svelte:fragment>
|
||||
<svelte:fragment slot="subtitle"
|
||||
>Please provide your authentication details for your provider.</svelte:fragment>
|
||||
{#if $createSource.type === 'firebase' && !firebaseFallback}
|
||||
<FirebaseAuthflow />
|
||||
<div class="flex flex-row justify-center">
|
||||
<button
|
||||
class="tag tooltip"
|
||||
on:click|preventDefault={() => {
|
||||
firebaseFallback = true;
|
||||
}}>
|
||||
<span class="icon-lock-closed" aria-hidden="true" />
|
||||
|
||||
<span class="text">Manual Authentication</span>
|
||||
|
||||
<span class="tooltip-popup" role="tooltip">
|
||||
<p class="text u-margin-block-start-8">
|
||||
Manually enter your account credentials.
|
||||
</p>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
{:else}
|
||||
<FormList>
|
||||
{#each Object.keys(credentialSchemas[$createSource.type]) as key}
|
||||
<InputText
|
||||
id={key}
|
||||
label={credentialSchemas[$createSource.type][key].label}
|
||||
placeholder={credentialSchemas[$createSource.type][key].placeholder}
|
||||
bind:value={$createSource.data[key]}
|
||||
required={credentialSchemas[$createSource.type][key].required} />
|
||||
{/each}
|
||||
</FormList>
|
||||
{/if}
|
||||
</WizardStep>
|
||||
+152
@@ -0,0 +1,152 @@
|
||||
<script lang="ts">
|
||||
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
|
||||
import { WizardStep } from '$lib/layout';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { sdkForProject } from '$lib/stores/sdk';
|
||||
import { onMount } from 'svelte';
|
||||
import { createSource } from '../store';
|
||||
import {
|
||||
TableBody,
|
||||
TableCellHead,
|
||||
TableCellText,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
TableScroll
|
||||
} from '$lib/elements/table';
|
||||
import Pill from '$lib/elements/pill.svelte';
|
||||
import Card from '$lib/components/card.svelte';
|
||||
import Heading from '$lib/components/heading.svelte';
|
||||
import Output from '$lib/components/output.svelte';
|
||||
|
||||
const permissions = ['Users', 'Databases', 'Documents', 'Files', 'Functions'];
|
||||
let returnedPermissions = permissions;
|
||||
let isChecking = false;
|
||||
let errorMessage = null;
|
||||
let canProgress = false;
|
||||
|
||||
async function validate() {
|
||||
isChecking = true;
|
||||
errorMessage = null;
|
||||
canProgress = false;
|
||||
|
||||
try {
|
||||
switch ($createSource.type) {
|
||||
case 'appwrite':
|
||||
await sdkForProject.transfers.validateAppwriteSource(
|
||||
$createSource.data['project'],
|
||||
$createSource.data['endpoint'],
|
||||
$createSource.data['key']
|
||||
);
|
||||
break;
|
||||
case 'supabase':
|
||||
await sdkForProject.transfers.validateSupabaseSource(
|
||||
$createSource.data['host'],
|
||||
$createSource.data['password'],
|
||||
$createSource.data['database'],
|
||||
$createSource.data['username'],
|
||||
parseInt($createSource.data['port'])
|
||||
);
|
||||
break;
|
||||
case 'nhost':
|
||||
await sdkForProject.transfers.validateNhostSource(
|
||||
$createSource.data['host'],
|
||||
$createSource.data['password'],
|
||||
$createSource.data['database'],
|
||||
$createSource.data['username'],
|
||||
parseInt($createSource.data['port'])
|
||||
);
|
||||
break;
|
||||
case 'firebase':
|
||||
await sdkForProject.transfers.validateFirebaseSource(
|
||||
$createSource.data['account-credentials']
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
//TODO: Update this to use the response from the API
|
||||
|
||||
returnedPermissions = permissions;
|
||||
isChecking = false;
|
||||
canProgress = true;
|
||||
addNotification({
|
||||
message: `Source is valid`,
|
||||
type: 'success'
|
||||
});
|
||||
trackEvent(Submit.SourceValidate, {
|
||||
customId: !!$createSource.id
|
||||
});
|
||||
} catch (error) {
|
||||
addNotification({
|
||||
message: error.message,
|
||||
type: 'error'
|
||||
});
|
||||
trackError(error, Submit.SourceValidate);
|
||||
errorMessage = error.message;
|
||||
console.error(error);
|
||||
returnedPermissions = [];
|
||||
isChecking = false;
|
||||
canProgress = false;
|
||||
}
|
||||
}
|
||||
|
||||
onMount(async () => {
|
||||
validate();
|
||||
});
|
||||
</script>
|
||||
|
||||
<WizardStep>
|
||||
<svelte:fragment slot="title">Validation</svelte:fragment>
|
||||
<svelte:fragment slot="subtitle"
|
||||
>Please wait while we perform valdiation checks.</svelte:fragment>
|
||||
<input class="u-hide" type="checkbox" name="isVerified" required bind:checked={canProgress} />
|
||||
<div>
|
||||
<TableScroll>
|
||||
<TableHeader>
|
||||
<TableCellHead width={150}>Permission</TableCellHead>
|
||||
<TableCellHead width={60}>Status</TableCellHead>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{#each permissions as permission}
|
||||
<TableRow>
|
||||
<TableCellText title="Permission">
|
||||
{permission}
|
||||
</TableCellText>
|
||||
<TableCellText title="Permission Level">
|
||||
<Pill
|
||||
danger={!returnedPermissions.includes(permission)}
|
||||
success={returnedPermissions.includes(permission)}
|
||||
warning={isChecking}>
|
||||
{#if returnedPermissions.includes(permission) && !isChecking}
|
||||
<span class="icon-check-circle" aria-hidden="true" />
|
||||
{:else if isChecking}
|
||||
<span class="icon-question-mark-circle" aria-hidden="true" />
|
||||
{:else}
|
||||
<span class="icon-x-circle" aria-hidden="true" />
|
||||
{/if}
|
||||
</Pill>
|
||||
</TableCellText>
|
||||
</TableRow>
|
||||
{/each}
|
||||
</TableBody>
|
||||
</TableScroll>
|
||||
|
||||
{#if errorMessage}
|
||||
<Card danger>
|
||||
<div class="u-gap-16">
|
||||
<Heading tag="h6" size="7">Validation Failed</Heading>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
There were errors when validating this source. Please go back and fix the errors
|
||||
then run the checks again.
|
||||
</p>
|
||||
|
||||
<br />
|
||||
|
||||
<div class="u-flex u-gap-16 u-cross-center card">
|
||||
<Output value={errorMessage}>{errorMessage}</Output>
|
||||
</div>
|
||||
</Card>
|
||||
{/if}
|
||||
</div>
|
||||
</WizardStep>
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
<script lang="ts">
|
||||
import { WizardStep } from '$lib/layout';
|
||||
</script>
|
||||
|
||||
<WizardStep>
|
||||
<svelte:fragment slot="title">Summary</svelte:fragment>
|
||||
<svelte:fragment slot="subtitle">
|
||||
Please review your destination details and click "Create" to continue.
|
||||
</svelte:fragment>
|
||||
|
||||
<h1>TODO: Animated graphic of ticket coming out of ticket machine with details on it.</h1>
|
||||
</WizardStep>
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
<script lang="ts">
|
||||
import { trackEvent } from '$lib/actions/analytics';
|
||||
import { app } from '$lib/stores/app';
|
||||
import { WizardStep } from '$lib/layout';
|
||||
import { sdkForProject } from '$lib/stores/sdk';
|
||||
import { onMount } from 'svelte';
|
||||
import { createTransfer } from '../store';
|
||||
import { goto } from '$app/navigation';
|
||||
import { base } from '$app/paths';
|
||||
import { page } from '$app/stores';
|
||||
|
||||
let options = [];
|
||||
|
||||
onMount(async () => {
|
||||
let sources = await sdkForProject.transfers.listSources();
|
||||
options = sources.sources.map((source) => ({
|
||||
name: `${source.name}`,
|
||||
type: `${source.type}`,
|
||||
value: `${source.$id}`
|
||||
}));
|
||||
});
|
||||
</script>
|
||||
|
||||
<WizardStep>
|
||||
<svelte:fragment slot="title">Create your Transfer</svelte:fragment>
|
||||
<svelte:fragment slot="subtitle"
|
||||
>Please select the source you want to use for this transfer.</svelte:fragment>
|
||||
<section class="common-section">
|
||||
<h2 class="heading-level-6 common-section">Sources</h2>
|
||||
<input
|
||||
class="u-hide"
|
||||
bind:checked={$createTransfer.source.length}
|
||||
type="checkbox"
|
||||
name="hasSelected"
|
||||
required />
|
||||
<ul class="grid-box common-section">
|
||||
{#each options.filter((p) => p.name !== 'Local') as source}
|
||||
<li class="grid-box-item">
|
||||
<button
|
||||
class="card u-flex u-flex-vertical u-cross-center u-width-full-line"
|
||||
on:click={() => {
|
||||
$createTransfer.source = source.value;
|
||||
trackEvent(`click_select_source`, {
|
||||
name: source.name.toLowerCase(),
|
||||
type: source.type.toLowerCase()
|
||||
});
|
||||
}}>
|
||||
<div class="image-item">
|
||||
<img
|
||||
height="20"
|
||||
width="20"
|
||||
src={`/icons/${$app.themeInUse}/color/${source.type}.svg`}
|
||||
alt={source.type} />
|
||||
</div>
|
||||
<p class="u-margin-block-start-8">{source.name}</p>
|
||||
</button>
|
||||
</li>
|
||||
{/each}
|
||||
<li class="grid-box-item">
|
||||
<button
|
||||
class="card u-flex u-flex-vertical u-cross-center u-width-half-line"
|
||||
on:click={() =>
|
||||
goto(
|
||||
`${base}/console/project-${$page.params.project}/settings/transfers/sources?openWizard=true`
|
||||
)}>
|
||||
<div class="image-item">
|
||||
<span class="icon-plus" />
|
||||
</div>
|
||||
<p class="u-margin-block-start-8">Create new source</p>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
</WizardStep>
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
<script lang="ts">
|
||||
import { trackEvent } from '$lib/actions/analytics';
|
||||
import { app } from '$lib/stores/app';
|
||||
import { WizardStep } from '$lib/layout';
|
||||
import { sdkForProject } from '$lib/stores/sdk';
|
||||
import { onMount } from 'svelte';
|
||||
import { createTransfer } from '../store';
|
||||
import { base } from '$app/paths';
|
||||
import { goto } from '$app/navigation';
|
||||
import { page } from '$app/stores';
|
||||
|
||||
let options = [];
|
||||
|
||||
onMount(async () => {
|
||||
let destinations = await sdkForProject.transfers.listDestinations();
|
||||
options = destinations.destinations.map((destination) => ({
|
||||
name: `${destination.name}`,
|
||||
type: `${destination.type}`,
|
||||
value: `${destination.$id}`
|
||||
}));
|
||||
});
|
||||
</script>
|
||||
|
||||
<WizardStep>
|
||||
<svelte:fragment slot="title">Create your Transfer</svelte:fragment>
|
||||
<svelte:fragment slot="subtitle"
|
||||
>Please select the destination you want to use for this transfer.</svelte:fragment>
|
||||
<section class="common-section">
|
||||
<input
|
||||
class="u-hide"
|
||||
bind:checked={$createTransfer.destination.length}
|
||||
type="checkbox"
|
||||
name="hasSelected"
|
||||
required />
|
||||
<h2 class="heading-level-6 common-section">Destinations</h2>
|
||||
<ul class="grid-box common-section">
|
||||
{#each options.filter((p) => p.name !== 'Local') as destination}
|
||||
<li class="grid-box-item">
|
||||
<button
|
||||
class="card u-flex u-flex-vertical u-cross-center u-width-full-line"
|
||||
on:click={() => {
|
||||
$createTransfer.destination = destination.value;
|
||||
trackEvent(`click_select_destination`, {
|
||||
name: destination.name.toLowerCase(),
|
||||
type: destination.type.toLowerCase()
|
||||
});
|
||||
}}>
|
||||
<div class="image-item">
|
||||
<img
|
||||
height="20"
|
||||
width="20"
|
||||
src={`/icons/${$app.themeInUse}/color/${destination.type}.svg`}
|
||||
alt={destination.type} />
|
||||
</div>
|
||||
<p class="u-margin-block-start-8">{destination.name}</p>
|
||||
</button>
|
||||
</li>
|
||||
{/each}
|
||||
<li class="grid-box-item">
|
||||
<button
|
||||
class="card u-flex u-flex-vertical u-cross-center u-width-half-line"
|
||||
on:click={() =>
|
||||
goto(
|
||||
`${base}/console/project-${$page.params.project}/settings/transfers/destinations`
|
||||
)}>
|
||||
<div class="image-item">
|
||||
<span class="icon-plus" />
|
||||
</div>
|
||||
<p class="u-margin-block-start-8">Create new destination</p>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
</WizardStep>
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
<script lang="ts">
|
||||
import FormList from '$lib/elements/forms/formList.svelte';
|
||||
import InputCheckbox from '$lib/elements/forms/inputCheckbox.svelte';
|
||||
import { WizardStep } from '$lib/layout';
|
||||
import { createTransfer } from '../store';
|
||||
|
||||
let options = [
|
||||
{
|
||||
name: 'Users',
|
||||
description: 'Users and their associated data',
|
||||
icon: 'user',
|
||||
checked: false
|
||||
},
|
||||
{
|
||||
name: 'Files',
|
||||
description: 'Files and their associated data',
|
||||
icon: 'file',
|
||||
checked: false
|
||||
},
|
||||
{
|
||||
name: 'Databases',
|
||||
description: 'Databases and their associated data excluding data in the database',
|
||||
icon: 'database',
|
||||
checked: false
|
||||
},
|
||||
{
|
||||
name: 'Documents',
|
||||
description:
|
||||
'Documents and their associated data, requires Databases to also be selected',
|
||||
icon: 'document',
|
||||
checked: false
|
||||
},
|
||||
{
|
||||
name: 'Functions',
|
||||
description: 'Functions and their associated data',
|
||||
icon: 'function',
|
||||
checked: false
|
||||
}
|
||||
];
|
||||
|
||||
$: $createTransfer.resources = options
|
||||
.filter((option) => option.checked)
|
||||
.map((option) => option.name);
|
||||
</script>
|
||||
|
||||
<WizardStep>
|
||||
<svelte:fragment slot="title">Create your Transfer</svelte:fragment>
|
||||
<svelte:fragment slot="subtitle"
|
||||
>Please select the resources you want to use for this transfer.</svelte:fragment>
|
||||
<FormList>
|
||||
{#each options as option}
|
||||
<InputCheckbox
|
||||
label={option.name}
|
||||
optionalText={option.description}
|
||||
id={option.name}
|
||||
bind:value={option.checked} />
|
||||
{/each}
|
||||
</FormList>
|
||||
</WizardStep>
|
||||
+169
@@ -0,0 +1,169 @@
|
||||
<script lang="ts">
|
||||
import { Submit, trackError } from '$lib/actions/analytics';
|
||||
import { WizardStep } from '$lib/layout';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { sdkForProject } from '$lib/stores/sdk';
|
||||
import { onMount } from 'svelte';
|
||||
import { createTransfer } from '../store';
|
||||
import {
|
||||
TableBody,
|
||||
TableCellHead,
|
||||
TableCellText,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
TableScroll
|
||||
} from '$lib/elements/table';
|
||||
import Pill from '$lib/elements/pill.svelte';
|
||||
import Card from '$lib/components/card.svelte';
|
||||
import Heading from '$lib/components/heading.svelte';
|
||||
import Output from '$lib/components/output.svelte';
|
||||
import Helper from '$lib/elements/forms/helper.svelte';
|
||||
|
||||
const permissions = ['Users', 'Databases', 'Documents', 'Files', 'Functions'];
|
||||
let isChecking = false;
|
||||
let errorMessage = null;
|
||||
let sourceErrors = permissions.map((permission) => {
|
||||
return {
|
||||
[permission]: false
|
||||
};
|
||||
});
|
||||
let destinationErrors = sourceErrors;
|
||||
let canProgress = false;
|
||||
|
||||
async function validate() {
|
||||
isChecking = true;
|
||||
errorMessage = null;
|
||||
canProgress = false;
|
||||
let sourceResponse = null;
|
||||
|
||||
try {
|
||||
await sdkForProject.transfers.validateSource(
|
||||
$createTransfer.source,
|
||||
$createTransfer.resources
|
||||
);
|
||||
await sdkForProject.transfers.validateDestination(
|
||||
$createTransfer.destination,
|
||||
$createTransfer.resources
|
||||
);
|
||||
|
||||
isChecking = false;
|
||||
canProgress = true;
|
||||
} catch (error) {
|
||||
if (!sourceResponse) {
|
||||
if (error.response.errors) {
|
||||
sourceErrors = error.response.errors;
|
||||
} else {
|
||||
sourceErrors = permissions.map((permission) => {
|
||||
return {
|
||||
[permission]: true
|
||||
};
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (error.response.errors) {
|
||||
destinationErrors = error.response.errors;
|
||||
} else {
|
||||
destinationErrors = permissions.map((permission) => {
|
||||
return {
|
||||
[permission]: true
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
addNotification({
|
||||
message: error.message,
|
||||
type: 'error'
|
||||
});
|
||||
trackError(error, Submit.SourceValidate);
|
||||
errorMessage = error.message;
|
||||
console.error(error);
|
||||
isChecking = false;
|
||||
canProgress = false;
|
||||
}
|
||||
}
|
||||
|
||||
onMount(async () => {
|
||||
validate();
|
||||
});
|
||||
</script>
|
||||
|
||||
<WizardStep>
|
||||
<svelte:fragment slot="title">Validation</svelte:fragment>
|
||||
<svelte:fragment slot="subtitle"
|
||||
>Please wait while we perform validiation checks.</svelte:fragment>
|
||||
<input class="u-hide" type="checkbox" name="isVerified" required bind:checked={canProgress} />
|
||||
<div>
|
||||
<TableScroll>
|
||||
<TableHeader>
|
||||
<TableCellHead width={60}>Permission</TableCellHead>
|
||||
<TableCellHead width={60}>Source Status</TableCellHead>
|
||||
<TableCellHead width={60}>Destination Status</TableCellHead>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{#each permissions as permission}
|
||||
<TableRow>
|
||||
<TableCellText title="Permission">
|
||||
{permission}
|
||||
|
||||
{#if sourceErrors[permission]}
|
||||
<Helper type="warning">
|
||||
{#each sourceErrors[permission] as error}
|
||||
{error} <br />
|
||||
{/each}
|
||||
</Helper>
|
||||
{/if}
|
||||
</TableCellText>
|
||||
<TableCellText title="Source Status">
|
||||
<Pill
|
||||
danger={sourceErrors[permission]}
|
||||
success={!sourceErrors[permission]}
|
||||
warning={isChecking}>
|
||||
{#if !sourceErrors[permission] && !isChecking}
|
||||
<span class="icon-check-circle" aria-hidden="true" />
|
||||
{:else if isChecking}
|
||||
<span class="icon-question-mark-circle" aria-hidden="true" />
|
||||
{:else}
|
||||
<span class="icon-x-circle" aria-hidden="true" />
|
||||
{/if}
|
||||
</Pill>
|
||||
</TableCellText>
|
||||
<TableCellText title="Destination Status">
|
||||
<Pill
|
||||
danger={destinationErrors[permission]}
|
||||
success={!destinationErrors[permission]}
|
||||
warning={isChecking}>
|
||||
{#if !destinationErrors[permission] && !isChecking}
|
||||
<span class="icon-check-circle" aria-hidden="true" />
|
||||
{:else if isChecking}
|
||||
<span class="icon-question-mark-circle" aria-hidden="true" />
|
||||
{:else}
|
||||
<span class="icon-x-circle" aria-hidden="true" />
|
||||
{/if}
|
||||
</Pill>
|
||||
</TableCellText>
|
||||
</TableRow>
|
||||
{/each}
|
||||
</TableBody>
|
||||
</TableScroll>
|
||||
|
||||
{#if errorMessage}
|
||||
<Card danger>
|
||||
<div class="u-gap-16">
|
||||
<Heading tag="h6" size="7">Validation Failed</Heading>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
There were errors when validating this source. Please go back and fix the errors
|
||||
then run the checks again.
|
||||
</p>
|
||||
|
||||
<br />
|
||||
|
||||
<div class="u-flex u-gap-16 u-cross-center card">
|
||||
<Output value={errorMessage}>{errorMessage}</Output>
|
||||
</div>
|
||||
</Card>
|
||||
{/if}
|
||||
</div>
|
||||
</WizardStep>
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
<script lang="ts">
|
||||
import { WizardStep } from '$lib/layout';
|
||||
</script>
|
||||
|
||||
<WizardStep>
|
||||
<svelte:fragment slot="title">Create your Transfer</svelte:fragment>
|
||||
<svelte:fragment slot="subtitle">The Transfer has successfully begun.</svelte:fragment>
|
||||
<h1>Maybe a animated gif of a plane taking off?</h1>
|
||||
</WizardStep>
|
||||
@@ -0,0 +1,37 @@
|
||||
import { writable } from 'svelte/store';
|
||||
|
||||
export const createTransfer = writable<{
|
||||
id?: string;
|
||||
source: string;
|
||||
destination: string;
|
||||
resources: string[];
|
||||
}>({
|
||||
id: null,
|
||||
source: null,
|
||||
destination: null,
|
||||
resources: []
|
||||
});
|
||||
|
||||
export const createSource = writable<{
|
||||
id?: string;
|
||||
name: string;
|
||||
type: string;
|
||||
data: object;
|
||||
}>({
|
||||
id: null,
|
||||
name: null,
|
||||
type: null,
|
||||
data: {}
|
||||
});
|
||||
|
||||
export const createDestination = writable<{
|
||||
id?: string;
|
||||
name: string;
|
||||
type: string;
|
||||
data: object;
|
||||
}>({
|
||||
id: null,
|
||||
name: null,
|
||||
type: null,
|
||||
data: {}
|
||||
});
|
||||
@@ -0,0 +1,5 @@
|
||||
<svg width="37" height="26" viewBox="0 0 37 26" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M36.6169 13.2311C36.6169 20.165 31.2833 25.7861 24.7035 25.7861C21.6474 25.7861 18.8602 24.5731 16.7512 22.5797C20.1297 20.9767 22.4816 17.3979 22.4816 13.2402C22.4816 9.07706 20.1238 5.49458 16.7383 3.89466C18.8491 1.89396 21.6412 0.67627 24.7035 0.67627C31.2833 0.67627 36.6169 6.29726 36.6169 13.2311Z" fill="#F02E65"/>
|
||||
<path d="M20.1187 3.22595C12.4104 -2.59307 1.28646 2.10523 0.0958473 11.6832C-0.457426 16.1347 1.41186 20.5422 4.9978 23.2416C12.7104 29.0555 23.831 24.349 25.0151 14.7701C25.5644 10.3243 23.6979 5.9239 20.1187 3.22583V3.22595ZM18.7226 5.07347C25.0114 9.81382 23.8045 19.5767 16.5504 22.6467C13.1841 24.0716 9.31219 23.5941 6.39371 21.394C0.1053 16.6535 1.31191 6.89065 8.56597 3.82068C11.9324 2.39605 15.8043 2.87344 18.7226 5.07347Z" fill="#F02E65"/>
|
||||
<path d="M12.961 9.09587C12.9463 9.13141 12.7565 9.85672 12.5528 10.7173C12.3408 11.5776 12.0051 12.9358 11.8157 13.7391C11.4506 15.211 11.2316 16.1781 11.2316 16.3059C11.2316 16.3413 11.4581 16.3701 11.735 16.3701H12.2389L12.4648 15.3886C12.5963 14.8555 12.8885 13.6323 13.1217 12.6725C13.3553 11.7126 13.6396 10.5464 13.7493 10.0769C13.8587 9.60759 13.9682 9.18138 13.9901 9.13141C14.012 9.06749 13.8878 9.04639 13.5083 9.04639C13.2237 9.04639 12.9755 9.06749 12.961 9.09587ZM9.04236 12.1746L8.37109 12.8858L8.56838 13.1133C8.67754 13.2412 8.97714 13.5613 9.2323 13.8246L9.69943 14.3077H11.0276L10.3998 13.6465C10.0567 13.2914 9.77212 12.9497 9.77212 12.9073C9.77212 12.8574 10.0349 12.5377 10.3563 12.1962C10.6771 11.8481 10.9399 11.549 10.9399 11.5135C10.9399 11.4849 10.6627 11.4637 10.3271 11.4637H9.72134L9.04236 12.1746ZM14.1503 11.5064C14.1503 11.5278 14.2746 11.6626 14.4276 11.812C14.9972 12.3667 15.3984 12.8362 15.3764 12.9283C15.362 12.9785 15.0845 13.3124 14.7489 13.6607L14.1434 14.3077H14.8218L15.5004 14.3008L16.1204 13.6394C16.4638 13.2699 16.741 12.9424 16.741 12.9C16.741 12.8646 16.4492 12.5303 16.0841 12.1536L15.4279 11.4637H14.7926C14.435 11.4637 14.1503 11.4849 14.1503 11.5064Z" fill="#F02E65"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 21 KiB |
@@ -0,0 +1 @@
|
||||
<svg width="32" height="34" fill="none" xmlns="http://www.w3.org/2000/svg" aria-label="Logo of Nhost" class="mx-auto cursor-pointer"><path d="M29.013 7.305 17.12.46a3.489 3.489 0 0 0-3.461 0 3.458 3.458 0 0 0-1.729 2.984v.894l-.775-.447a3.489 3.489 0 0 0-3.461 0 3.459 3.459 0 0 0-1.729 2.99v.892l-.775-.446a3.488 3.488 0 0 0-3.46 0A3.459 3.459 0 0 0 0 10.313v21.443a1.624 1.624 0 0 0 1.803 1.612c.303-.033.591-.15.83-.34l5.898-4.636 9.096 5.233a1.652 1.652 0 0 0 1.627 0c.501-.29.814-.826.814-1.404V19.319a5.952 5.952 0 0 0-2.983-5.149l-2.983-1.716V3.448a1.288 1.288 0 0 1 1.291-1.286c.227 0 .449.06.645.173L27.93 9.177a3.795 3.795 0 0 1 1.898 3.276V28.52c0 .458-.248.884-.645 1.113l-3.151 1.813V15.884a5.953 5.953 0 0 0-2.983-5.148L15.73 6.524V9.02l6.237 3.59a3.793 3.793 0 0 1 1.898 3.275V32.38c0 .575.311 1.115.813 1.405a1.653 1.653 0 0 0 1.628 0l3.965-2.282A3.457 3.457 0 0 0 32 28.517V12.448a5.968 5.968 0 0 0-2.987-5.143ZM15.995 16.04a3.792 3.792 0 0 1 1.898 3.277v11.97l-7.53-4.333 2.417-1.898a3.424 3.424 0 0 0 1.318-2.707v-7.397l1.898 1.09v-.002Zm-4.066-2.34v8.645c0 .397-.18.766-.491 1.01l-9.27 7.283V10.31a1.286 1.286 0 0 1 1.29-1.286c.226 0 .448.06.644.173l1.863 1.07v15.325l2.168-1.704V6.878a1.286 1.286 0 0 1 1.29-1.285c.227 0 .45.06.645.173l1.861 1.07v4.367L9.762 9.955v2.498l2.17 1.249h-.003Z" fill="#0052cd"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1,23 @@
|
||||
<svg width="120" height="120" viewBox="0 0 120 120" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M63.7076 110.284C60.8481 113.885 55.0502 111.912 54.9813 107.314L53.9738 40.0627L99.1935 40.0627C107.384 40.0627 111.952 49.5228 106.859 55.9374L63.7076 110.284Z"
|
||||
fill="url(#paint0_linear)" />
|
||||
<path
|
||||
d="M63.7076 110.284C60.8481 113.885 55.0502 111.912 54.9813 107.314L53.9738 40.0627L99.1935 40.0627C107.384 40.0627 111.952 49.5228 106.859 55.9374L63.7076 110.284Z"
|
||||
fill="url(#paint1_linear)" fill-opacity="0.2" />
|
||||
<path
|
||||
d="M45.317 2.07103C48.1765 -1.53037 53.9745 0.442937 54.0434 5.041L54.4849 72.2922H9.83113C1.64038 72.2922 -2.92775 62.8321 2.1655 56.4175L45.317 2.07103Z"
|
||||
fill="#3ECF8E" />
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear" x1="53.9738" y1="54.974" x2="94.1635" y2="71.8295"
|
||||
gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#249361" />
|
||||
<stop offset="1" stop-color="#3ECF8E" />
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear" x1="36.1558" y1="30.578" x2="54.4844" y2="65.0806"
|
||||
gradientUnits="userSpaceOnUse">
|
||||
<stop />
|
||||
<stop offset="1" stop-opacity="0" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
Reference in New Issue
Block a user