Merge branch 'main' of github.com:appwrite/appwrite-console-poc into fix-function-review
@@ -1,3 +1,4 @@
|
||||
VITE_APPWRITE_ENDPOINT=
|
||||
VITE_GOOGLE_ANALYTICS=
|
||||
VITE_SENTRY_DSN=
|
||||
VITE_GA_PROJECT=
|
||||
VITE_SENTRY_DSN=
|
||||
VITE_CONSOLE_MODE=self-hosted
|
||||
@@ -4,10 +4,6 @@
|
||||
[](https://twitter.com/appwrite)
|
||||
[](https://appwrite.io)
|
||||
|
||||
<p align="center">
|
||||
<a href="https://www.producthunt.com/posts/console-2-0?utm_source=badge-featured&utm_medium=badge&utm_souce=badge-console-2-0" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=367206&theme=light" alt="Console 2.0 - Backend as a Service, reimagined by Appwrite | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a>
|
||||
</p>
|
||||
|
||||

|
||||
|
||||
[Appwrite](https://appwrite.io) Console is the Graphical User Interface that developers interact with when accessing their Appwrite instance in the web browser.
|
||||
|
||||
@@ -3,12 +3,13 @@ import googleAnalytics from '@analytics/google-analytics';
|
||||
import { get } from 'svelte/store';
|
||||
import { page } from '$app/stores';
|
||||
import { user } from '$lib/stores/user';
|
||||
import { Mode } from '$lib/constants';
|
||||
|
||||
const analytics = Analytics({
|
||||
app: 'appwrite',
|
||||
plugins: [
|
||||
googleAnalytics({
|
||||
measurementIds: ['G-R4YJ9JN8L4']
|
||||
measurementIds: [import.meta.env.VITE_GA_PROJECT?.toString() || 'G-R4YJ9JN8L4']
|
||||
})
|
||||
]
|
||||
});
|
||||
@@ -48,6 +49,7 @@ function sendEventToGrowth(event: string, path: string, data: object = null): vo
|
||||
action: event,
|
||||
label: event,
|
||||
url: window.location.origin + path,
|
||||
account: import.meta.env.VITE_CONSOLE_MODE?.toString() || Mode.SELF_HOSTED,
|
||||
data: {
|
||||
email,
|
||||
name,
|
||||
|
||||
@@ -2,6 +2,11 @@ export const PAGE_LIMIT = 12; // default page limit
|
||||
export const CARD_LIMIT = 6; // default card limit
|
||||
export const INTERVAL = 5 * 60000; // default interval to check for feedback
|
||||
|
||||
export enum Mode {
|
||||
CLOUD = 'mode:cloud',
|
||||
SELF_HOSTED = 'mode:self-hosted'
|
||||
}
|
||||
|
||||
export enum Dependencies {
|
||||
ORGANIZATION = 'dependency:organization',
|
||||
PROJECT = 'dependency:project',
|
||||
|
||||
@@ -3,6 +3,7 @@ export function intersection(arr1: unknown[], arr2: unknown[]) {
|
||||
const intersection = new Set(arr1.filter((elem) => set.has(elem)));
|
||||
return Array.from(intersection);
|
||||
}
|
||||
|
||||
export function difference(arr1: unknown[], arr2: unknown[]) {
|
||||
const set = new Set(arr2);
|
||||
const intersection = new Set(arr1.filter((elem) => !set.has(elem)));
|
||||
@@ -12,3 +13,17 @@ export function difference(arr1: unknown[], arr2: unknown[]) {
|
||||
export function symmetricDifference(arr1: unknown[], arr2: unknown[]) {
|
||||
return difference(arr1, arr2).concat(difference(arr2, arr1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the element at the specified index from the array, and returns a new array.
|
||||
*
|
||||
* @export
|
||||
* @template T
|
||||
* @param {T[]} arr
|
||||
* @param {number} index
|
||||
* @returns {T[]}
|
||||
*/
|
||||
export function remove<T>(arr: T[], index: number): T[] {
|
||||
// Remove the element at the given index, return a new array
|
||||
return [...arr.slice(0, index), ...arr.slice(index + 1)];
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ import Apple from '../../routes/console/project-[project]/auth/appleOAuth.svelte
|
||||
import Microsoft from '../../routes/console/project-[project]/auth/microsoftOAuth.svelte';
|
||||
import Okta from '../../routes/console/project-[project]/auth/oktaOAuth.svelte';
|
||||
import Auth0 from '../../routes/console/project-[project]/auth/auth0OAuth.svelte';
|
||||
import Authentik from '../../routes/console/project-[project]/auth/authentikOAuth.svelte';
|
||||
import GitLab from '../../routes/console/project-[project]/auth/gitlabOAuth.svelte';
|
||||
import Main from '../../routes/console/project-[project]/auth/mainOAuth.svelte';
|
||||
|
||||
export type Provider = Models.Provider & {
|
||||
@@ -36,6 +38,13 @@ const setProviders = (project: Models.Project): Provider[] => {
|
||||
docs = 'https://auth0.com/developers';
|
||||
component = Auth0;
|
||||
break;
|
||||
case 'authentik':
|
||||
docs = 'https://goauthentik.io/integrations/sources/oauth/';
|
||||
component = Authentik;
|
||||
break;
|
||||
case 'autodesk':
|
||||
docs = 'https://forge.autodesk.com/en/docs/oauth/v2/developers_guide/overview/';
|
||||
break;
|
||||
case 'bitbucket':
|
||||
docs = 'https://developer.atlassian.com/bitbucket';
|
||||
break;
|
||||
@@ -45,12 +54,21 @@ const setProviders = (project: Models.Project): Provider[] => {
|
||||
case 'box':
|
||||
docs = 'https://developer.box.com/reference/';
|
||||
break;
|
||||
case 'dailymotion':
|
||||
docs = 'https://developers.dailymotion.com/api/';
|
||||
break;
|
||||
case 'discord':
|
||||
docs = 'https://discordapp.com/developers/docs/topics/oauth2';
|
||||
break;
|
||||
case 'disqus':
|
||||
docs = 'https://disqus.com/api/docs/auth/';
|
||||
break;
|
||||
case 'dropbox':
|
||||
docs = 'https://www.dropbox.com/developers/documentation';
|
||||
break;
|
||||
case 'etsy':
|
||||
docs = 'https://developers.etsy.com/';
|
||||
break;
|
||||
case 'facebook':
|
||||
docs = 'https://developers.facebook.com/';
|
||||
break;
|
||||
@@ -59,6 +77,7 @@ const setProviders = (project: Models.Project): Provider[] => {
|
||||
break;
|
||||
case 'gitlab':
|
||||
docs = 'https://docs.gitlab.com/ee/api/';
|
||||
component = GitLab;
|
||||
break;
|
||||
case 'google':
|
||||
docs = 'https://support.google.com/googleapi/answer/6158849';
|
||||
@@ -84,6 +103,9 @@ const setProviders = (project: Models.Project): Provider[] => {
|
||||
icon = 'paypal';
|
||||
docs = 'https://developer.paypal.com/docs/api/overview/';
|
||||
break;
|
||||
case 'podio':
|
||||
docs = 'https://developers.podio.com/doc/oauth-authorization';
|
||||
break;
|
||||
case 'salesforce':
|
||||
docs = 'https://developer.salesforce.com/docs/';
|
||||
break;
|
||||
|
||||
@@ -8,9 +8,6 @@ import type { LayoutLoad } from './$types';
|
||||
export const ssr = false;
|
||||
|
||||
export const load: LayoutLoad = async ({ depends, url }) => {
|
||||
if (url.pathname.startsWith('/auth')) {
|
||||
return;
|
||||
}
|
||||
depends(Dependencies.ACCOUNT);
|
||||
try {
|
||||
const account = await sdkForConsole.account.get();
|
||||
@@ -20,7 +17,15 @@ export const load: LayoutLoad = async ({ depends, url }) => {
|
||||
organizations: sdkForConsole.teams.list()
|
||||
};
|
||||
} catch (error) {
|
||||
const acceptedRoutes = ['/login', '/register', '/recover', '/invite'];
|
||||
const acceptedRoutes = [
|
||||
'/login',
|
||||
'/register',
|
||||
'/recover',
|
||||
'/invite',
|
||||
'/auth/magic-url',
|
||||
'/auth/oauth2/success',
|
||||
'/auth/oauth2/failure'
|
||||
];
|
||||
|
||||
if (!acceptedRoutes.includes(url.pathname)) {
|
||||
throw redirect(303, '/login');
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
<script lang="ts">
|
||||
import { invalidate } from '$app/navigation';
|
||||
import { trackEvent } from '$lib/actions/analytics';
|
||||
import { Modal } from '$lib/components';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { sdkForConsole } from '$lib/stores/sdk';
|
||||
import { user } from '$lib/stores/user';
|
||||
|
||||
export let showDelete = false;
|
||||
|
||||
const deleteAccount = async () => {
|
||||
try {
|
||||
await sdkForConsole.users.delete($user.$id);
|
||||
await sdkForConsole.account.updateStatus();
|
||||
showDelete = false;
|
||||
invalidate(Dependencies.ACCOUNT);
|
||||
addNotification({
|
||||
type: 'success',
|
||||
message: `Account was deleted `
|
||||
|
||||
@@ -55,7 +55,9 @@
|
||||
</button>
|
||||
<svelte:fragment slot="list">
|
||||
{#each $organizationList.teams as org}
|
||||
<DropListLink href={`${base}/console/organization-${org.$id}`}>
|
||||
<DropListLink
|
||||
href={`${base}/console/organization-${org.$id}`}
|
||||
on:click={() => (showDropdown = false)}>
|
||||
{org.name}
|
||||
</DropListLink>
|
||||
{/each}
|
||||
|
||||
@@ -84,7 +84,7 @@
|
||||
<CopyInput
|
||||
value={`${
|
||||
sdkForConsole.client.config.endpoint
|
||||
}/account/session/oauth2/callback/${provider.name.toLocaleLowerCase()}/${projectId}`} />
|
||||
}/account/sessions/oauth2/callback/${provider.name.toLocaleLowerCase()}/${projectId}`} />
|
||||
</div>
|
||||
</FormList>
|
||||
<svelte:fragment slot="footer">
|
||||
|
||||
@@ -93,7 +93,7 @@
|
||||
<CopyInput
|
||||
value={`${
|
||||
sdkForConsole.client.config.endpoint
|
||||
}/account/session/oauth2/callback/${provider.name.toLocaleLowerCase()}/${projectId}`} />
|
||||
}/account/sessions/oauth2/callback/${provider.name.toLocaleLowerCase()}/${projectId}`} />
|
||||
</div>
|
||||
</FormList>
|
||||
<svelte:fragment slot="footer">
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/stores';
|
||||
import { Modal, CopyInput, Alert } from '$lib/components';
|
||||
import { Button, InputPassword, InputText, InputSwitch, FormList } from '$lib/elements/forms';
|
||||
import { sdkForConsole } from '$lib/stores/sdk';
|
||||
import type { Provider } from '$lib/stores/oauth-providers';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { onMount } from 'svelte';
|
||||
import { invalidate } from '$app/navigation';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { trackEvent } from '$lib/actions/analytics';
|
||||
|
||||
export let provider: Provider;
|
||||
|
||||
let appId: string = null;
|
||||
let enabled: boolean = null;
|
||||
let clientSecret: string = null;
|
||||
let authentikDomain: string = null;
|
||||
let error: string;
|
||||
|
||||
onMount(() => {
|
||||
appId ??= provider.appId;
|
||||
enabled ??= provider.enabled;
|
||||
if (provider.secret) ({ clientSecret, authentikDomain } = JSON.parse(provider.secret));
|
||||
});
|
||||
|
||||
const projectId = $page.params.project;
|
||||
const update = async () => {
|
||||
try {
|
||||
await sdkForConsole.projects.updateOAuth2(
|
||||
projectId,
|
||||
provider.name.toLowerCase(),
|
||||
appId,
|
||||
secret,
|
||||
enabled
|
||||
);
|
||||
addNotification({
|
||||
type: 'success',
|
||||
message: `${provider.name} authentication has been ${
|
||||
provider.enabled ? 'enabled' : 'disabled'
|
||||
}`
|
||||
});
|
||||
trackEvent('submit_provider_update', {
|
||||
provider,
|
||||
enabled
|
||||
});
|
||||
provider = null;
|
||||
invalidate(Dependencies.PROJECT);
|
||||
} catch ({ message }) {
|
||||
error = message;
|
||||
}
|
||||
};
|
||||
|
||||
$: secret =
|
||||
clientSecret && authentikDomain
|
||||
? JSON.stringify({ clientSecret, authentikDomain })
|
||||
: provider.secret;
|
||||
</script>
|
||||
|
||||
<Modal {error} size="big" show on:submit={update} on:close>
|
||||
<svelte:fragment slot="header">{provider.name} OAuth2 Settings</svelte:fragment>
|
||||
<FormList>
|
||||
<p>
|
||||
To use {provider.name} authentication in your application, first fill in this form. For more
|
||||
info you can
|
||||
<a class="link" href={provider.docs} target="_blank" rel="noopener noreferrer"
|
||||
>visit the docs.</a>
|
||||
</p>
|
||||
<InputSwitch id="state" bind:value={enabled} label={enabled ? 'Enabled' : 'Disabled'} />
|
||||
<InputText
|
||||
id="clientID"
|
||||
label="Client ID"
|
||||
autofocus={true}
|
||||
placeholder="Enter ID"
|
||||
bind:value={appId} />
|
||||
<InputPassword
|
||||
id="secret"
|
||||
label="Client Secret"
|
||||
placeholder="Enter Client Secret"
|
||||
minlength={0}
|
||||
showPasswordButton
|
||||
bind:value={clientSecret} />
|
||||
<InputText
|
||||
id="domain"
|
||||
label="Authentik Base-Domain"
|
||||
placeholder="Your Authentik domain"
|
||||
bind:value={authentikDomain} />
|
||||
<Alert type="info">
|
||||
To complete set up, add this OAuth2 redirect URI to your {provider.name} app configuration.
|
||||
</Alert>
|
||||
<div>
|
||||
<p>URI</p>
|
||||
<CopyInput
|
||||
value={`${
|
||||
sdkForConsole.client.config.endpoint
|
||||
}/account/sessions/oauth2/callback/${provider.name.toLocaleLowerCase()}/${projectId}`} />
|
||||
</div>
|
||||
</FormList>
|
||||
<svelte:fragment slot="footer">
|
||||
<Button secondary on:click={() => (provider = null)}>Cancel</Button>
|
||||
<Button
|
||||
disabled={(secret === provider.secret &&
|
||||
enabled === provider.enabled &&
|
||||
appId === provider.appId) ||
|
||||
!(appId && clientSecret && authentikDomain)}
|
||||
submit>Update</Button>
|
||||
</svelte:fragment>
|
||||
</Modal>
|
||||
@@ -0,0 +1,106 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/stores';
|
||||
import { Modal, CopyInput, Alert } from '$lib/components';
|
||||
import { Button, InputPassword, InputText, InputSwitch, FormList } from '$lib/elements/forms';
|
||||
import { sdkForConsole } from '$lib/stores/sdk';
|
||||
import type { Provider } from '$lib/stores/oauth-providers';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { onMount } from 'svelte';
|
||||
import { invalidate } from '$app/navigation';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { trackEvent } from '$lib/actions/analytics';
|
||||
|
||||
export let provider: Provider;
|
||||
|
||||
let appId: string = null;
|
||||
let enabled: boolean = null;
|
||||
let clientSecret: string = null;
|
||||
let endpoint: string = null;
|
||||
let error: string;
|
||||
|
||||
onMount(() => {
|
||||
appId ??= provider.appId;
|
||||
enabled ??= provider.enabled;
|
||||
if (provider.secret) ({ clientSecret, endpoint } = JSON.parse(provider.secret));
|
||||
});
|
||||
|
||||
const projectId = $page.params.project;
|
||||
const update = async () => {
|
||||
try {
|
||||
await sdkForConsole.projects.updateOAuth2(
|
||||
projectId,
|
||||
provider.name.toLowerCase(),
|
||||
appId,
|
||||
secret,
|
||||
enabled
|
||||
);
|
||||
addNotification({
|
||||
type: 'success',
|
||||
message: `${provider.name} authentication has been ${
|
||||
provider.enabled ? 'enabled' : 'disabled'
|
||||
}`
|
||||
});
|
||||
trackEvent('submit_provider_update', {
|
||||
provider,
|
||||
enabled
|
||||
});
|
||||
provider = null;
|
||||
invalidate(Dependencies.PROJECT);
|
||||
} catch ({ message }) {
|
||||
error = message;
|
||||
}
|
||||
};
|
||||
|
||||
$: secret =
|
||||
clientSecret && endpoint ? JSON.stringify({ clientSecret, endpoint }) : provider.secret;
|
||||
</script>
|
||||
|
||||
<Modal {error} size="big" show on:submit={update} on:close>
|
||||
<svelte:fragment slot="header">{provider.name} OAuth2 Settings</svelte:fragment>
|
||||
<FormList>
|
||||
<p>
|
||||
To use {provider.name} authentication in your application, first fill in this form. For more
|
||||
info you can
|
||||
<a class="link" href={provider.docs} target="_blank" rel="noopener noreferrer"
|
||||
>visit the docs.</a>
|
||||
</p>
|
||||
<InputSwitch id="state" bind:value={enabled} label={enabled ? 'Enabled' : 'Disabled'} />
|
||||
<InputText
|
||||
id="appID"
|
||||
label="App ID"
|
||||
autofocus={true}
|
||||
placeholder="Enter ID"
|
||||
bind:value={appId} />
|
||||
<InputPassword
|
||||
id="secret"
|
||||
label="App Secret"
|
||||
placeholder="Enter App Secret"
|
||||
minlength={0}
|
||||
showPasswordButton
|
||||
bind:value={clientSecret} />
|
||||
<InputText
|
||||
id="endpoint"
|
||||
label="Endpoint (optional)"
|
||||
placeholder="Your endpoint"
|
||||
bind:value={endpoint} />
|
||||
<Alert type="info">
|
||||
To complete set up, add this OAuth2 redirect URI to your {provider.name} app configuration.
|
||||
</Alert>
|
||||
<div>
|
||||
<p>URI</p>
|
||||
<CopyInput
|
||||
value={`${
|
||||
sdkForConsole.client.config.endpoint
|
||||
}/account/sessions/oauth2/callback/${provider.name.toLocaleLowerCase()}/${projectId}`} />
|
||||
</div>
|
||||
</FormList>
|
||||
<svelte:fragment slot="footer">
|
||||
<Button secondary on:click={() => (provider = null)}>Cancel</Button>
|
||||
<Button
|
||||
disabled={(secret === provider.secret &&
|
||||
enabled === provider.enabled &&
|
||||
appId === provider.appId) ||
|
||||
!(appId && clientSecret && endpoint)}
|
||||
submit>Update</Button>
|
||||
</svelte:fragment>
|
||||
</Modal>
|
||||
@@ -84,7 +84,7 @@
|
||||
<CopyInput
|
||||
value={`${
|
||||
sdkForConsole.client.config.endpoint
|
||||
}/account/session/oauth2/callback/${provider.name.toLocaleLowerCase()}/${projectId}`} />
|
||||
}/account/sessions/oauth2/callback/${provider.name.toLocaleLowerCase()}/${projectId}`} />
|
||||
</div>
|
||||
</FormList>
|
||||
<svelte:fragment slot="footer">
|
||||
|
||||
@@ -93,7 +93,7 @@
|
||||
<CopyInput
|
||||
value={`${
|
||||
sdkForConsole.client.config.endpoint
|
||||
}/account/session/oauth2/callback/${provider.name.toLocaleLowerCase()}/${projectId}`} />
|
||||
}/account/sessions/oauth2/callback/${provider.name.toLocaleLowerCase()}/${projectId}`} />
|
||||
</div>
|
||||
</FormList>
|
||||
<svelte:fragment slot="footer">
|
||||
|
||||
@@ -35,7 +35,8 @@
|
||||
projectId,
|
||||
provider.name.toLowerCase(),
|
||||
appId,
|
||||
secret
|
||||
secret,
|
||||
enabled
|
||||
);
|
||||
addNotification({
|
||||
type: 'success',
|
||||
@@ -102,7 +103,7 @@
|
||||
<CopyInput
|
||||
value={`${
|
||||
sdkForConsole.client.config.endpoint
|
||||
}/account/session/oauth2/callback/${provider.name.toLocaleLowerCase()}/${projectId}`} />
|
||||
}/account/sessions/oauth2/callback/${provider.name.toLocaleLowerCase()}/${projectId}`} />
|
||||
</div>
|
||||
</FormList>
|
||||
<svelte:fragment slot="footer">
|
||||
|
||||
@@ -1,500 +1,20 @@
|
||||
<script lang="ts">
|
||||
import { toLocaleDateTime } from '$lib/helpers/date';
|
||||
import {
|
||||
CardGrid,
|
||||
Box,
|
||||
DropList,
|
||||
DropListItem,
|
||||
Heading,
|
||||
AvatarInitials
|
||||
} from '$lib/components';
|
||||
import { Pill } from '$lib/elements';
|
||||
import {
|
||||
Button,
|
||||
Form,
|
||||
InputText,
|
||||
InputEmail,
|
||||
InputPassword,
|
||||
InputPhone
|
||||
} from '$lib/elements/forms';
|
||||
import { Container } from '$lib/layout';
|
||||
import { sdkForProject } from '$lib/stores/sdk';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import DeleteUser from './deleteUser.svelte';
|
||||
import { user } from './store';
|
||||
import { onMount } from 'svelte';
|
||||
import { invalidate } from '$app/navigation';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { trackEvent } from '$lib/actions/analytics';
|
||||
|
||||
$: if (prefs) {
|
||||
if (JSON.stringify(prefs) !== JSON.stringify(Object.entries($user.prefs))) {
|
||||
if (!!prefs[prefs.length - 1][0] && !!prefs[prefs.length - 1][1]) {
|
||||
arePrefsDisabled = false;
|
||||
} else {
|
||||
arePrefsDisabled = true;
|
||||
}
|
||||
} else {
|
||||
arePrefsDisabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
let showDelete = false;
|
||||
let userName: string = null,
|
||||
userEmail: string = null,
|
||||
userPhone: string = null,
|
||||
newPassword: string = null;
|
||||
let prefs: [string, unknown][] = null;
|
||||
let arePrefsDisabled = true;
|
||||
let showVerifcationDropdown = false;
|
||||
|
||||
onMount(async () => {
|
||||
prefs = Object.entries($user.prefs);
|
||||
if (!prefs?.length) {
|
||||
prefs.push(['', '']);
|
||||
}
|
||||
userName ??= $user.name;
|
||||
userEmail ??= $user.email;
|
||||
userPhone ??= $user.phone;
|
||||
});
|
||||
|
||||
async function updateVerificationEmail() {
|
||||
showVerifcationDropdown = false;
|
||||
try {
|
||||
await sdkForProject.users.updateEmailVerification($user.$id, !$user.emailVerification);
|
||||
invalidate(Dependencies.USER);
|
||||
addNotification({
|
||||
message: `${$user.name || $user.email || $user.phone || 'The account'} has been ${
|
||||
$user.emailVerification ? 'verified' : 'unverified'
|
||||
}`,
|
||||
type: 'success'
|
||||
});
|
||||
trackEvent('submit_user_update_verification_email');
|
||||
} catch (error) {
|
||||
addNotification({
|
||||
message: error.message,
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
}
|
||||
async function updateVerificationPhone() {
|
||||
showVerifcationDropdown = false;
|
||||
try {
|
||||
await sdkForProject.users.updatePhoneVerification($user.$id, !$user.phoneVerification);
|
||||
invalidate(Dependencies.USER);
|
||||
addNotification({
|
||||
message: `${$user.name || $user.email || $user.phone || 'The account'} has been ${
|
||||
$user.phoneVerification ? 'verified' : 'unverified'
|
||||
}`,
|
||||
type: 'success'
|
||||
});
|
||||
trackEvent('submit_user_update_verification_phone');
|
||||
} catch (error) {
|
||||
addNotification({
|
||||
message: error.message,
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
}
|
||||
async function updateStatus() {
|
||||
try {
|
||||
await sdkForProject.users.updateStatus($user.$id, !$user.status);
|
||||
invalidate(Dependencies.USER);
|
||||
addNotification({
|
||||
message: `${$user.name || $user.email || $user.phone || 'The account'} has been ${
|
||||
$user.status ? 'unblocked' : 'blocked'
|
||||
}`,
|
||||
type: 'success'
|
||||
});
|
||||
trackEvent('submit_user_update_status');
|
||||
} catch (error) {
|
||||
addNotification({
|
||||
message: error.message,
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
}
|
||||
async function updateName() {
|
||||
try {
|
||||
await sdkForProject.users.updateName($user.$id, userName);
|
||||
invalidate(Dependencies.USER);
|
||||
addNotification({
|
||||
message: 'Name has been updated',
|
||||
type: 'success'
|
||||
});
|
||||
trackEvent('submit_user_update_name');
|
||||
} catch (error) {
|
||||
addNotification({
|
||||
message: error.message,
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
}
|
||||
async function updateEmail() {
|
||||
try {
|
||||
await sdkForProject.users.updateEmail($user.$id, userEmail);
|
||||
invalidate(Dependencies.USER);
|
||||
addNotification({
|
||||
message: 'Email has been updated',
|
||||
type: 'success'
|
||||
});
|
||||
trackEvent('submit_user_update_email');
|
||||
} catch (error) {
|
||||
addNotification({
|
||||
message: error.message,
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
}
|
||||
async function updatePhone() {
|
||||
try {
|
||||
await sdkForProject.users.updatePhone($user.$id, userPhone);
|
||||
invalidate(Dependencies.USER);
|
||||
addNotification({
|
||||
message: 'Phone has been updated',
|
||||
type: 'success'
|
||||
});
|
||||
trackEvent('submit_user_update_phone');
|
||||
} catch (error) {
|
||||
addNotification({
|
||||
message: error.message,
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
}
|
||||
async function updatePassword() {
|
||||
try {
|
||||
await sdkForProject.users.updatePassword($user.$id, newPassword);
|
||||
newPassword = null;
|
||||
addNotification({
|
||||
message: 'Password has been updated',
|
||||
type: 'success'
|
||||
});
|
||||
trackEvent('submit_user_update_password');
|
||||
} catch (error) {
|
||||
addNotification({
|
||||
message: error.message,
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function updatePrefs() {
|
||||
try {
|
||||
let updatedPrefs = Object.fromEntries(prefs);
|
||||
|
||||
await sdkForProject.users.updatePrefs($user.$id, updatedPrefs);
|
||||
invalidate(Dependencies.USER);
|
||||
arePrefsDisabled = true;
|
||||
|
||||
addNotification({
|
||||
message: 'Preferences have been updated',
|
||||
type: 'success'
|
||||
});
|
||||
trackEvent('submit_user_update_preferences');
|
||||
} catch (error) {
|
||||
addNotification({
|
||||
message: error.message,
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
}
|
||||
import DangerZone from './dangerZone.svelte';
|
||||
import UpdateEmail from './updateEmail.svelte';
|
||||
import UpdateName from './updateName.svelte';
|
||||
import UpdatePassword from './updatePassword.svelte';
|
||||
import UpdatePhone from './updatePhone.svelte';
|
||||
import UpdatePrefs from './updatePrefs.svelte';
|
||||
import UpdateStatus from './updateStatus.svelte';
|
||||
</script>
|
||||
|
||||
<Container>
|
||||
<CardGrid>
|
||||
<div class="grid-1-2-col-1 u-flex u-cross-center u-gap-16">
|
||||
{#if $user.email || $user.phone}
|
||||
{#if $user.name}
|
||||
<AvatarInitials size={48} name={$user.name} />
|
||||
<Heading tag="h6" size="7">{$user.name}</Heading>
|
||||
{:else}
|
||||
<div class="avatar">
|
||||
<span class="icon-minus-sm" aria-hidden="true" />
|
||||
</div>
|
||||
{/if}
|
||||
{:else}
|
||||
<div class="avatar">
|
||||
<span class="icon-anonymous" aria-hidden="true" />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
<svelte:fragment slot="aside">
|
||||
<div class="u-flex u-main-space-between">
|
||||
<div>
|
||||
{#if $user.email}
|
||||
<p class="title">{$user.email}</p>
|
||||
{/if}
|
||||
{#if $user.phone}
|
||||
<p class="title">{$user.phone}</p>
|
||||
{/if}
|
||||
<p>Joined: {toLocaleDateTime($user.registration)}</p>
|
||||
</div>
|
||||
{#if !$user.status}
|
||||
<Pill danger>blocked</Pill>
|
||||
{:else if $user.email && $user.phone}
|
||||
<Pill success={$user.emailVerification || $user.phoneVerification}>
|
||||
{$user.emailVerification && $user.phoneVerification
|
||||
? 'verified'
|
||||
: $user.emailVerification
|
||||
? 'verified email'
|
||||
: $user.phoneVerification
|
||||
? 'verified phone'
|
||||
: 'unverified'}
|
||||
</Pill>
|
||||
{:else}
|
||||
<Pill success={$user.emailVerification || $user.phoneVerification}>
|
||||
{$user.emailVerification
|
||||
? 'verified '
|
||||
: $user.phoneVerification
|
||||
? 'verified '
|
||||
: 'unverified'}
|
||||
</Pill>
|
||||
{/if}
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="actions">
|
||||
<Button text={$user.status} secondary={!$user.status} on:click={() => updateStatus()}>
|
||||
{$user.status ? 'Block account' : 'Unblock account'}
|
||||
</Button>
|
||||
{#if $user.status}
|
||||
{#if $user.phone && $user.email}
|
||||
<DropList bind:show={showVerifcationDropdown} placement="top-start">
|
||||
<Button
|
||||
secondary
|
||||
on:click={() => (showVerifcationDropdown = !showVerifcationDropdown)}>
|
||||
{$user.emailVerification ? 'Unverify' : 'Verify'} account
|
||||
</Button>
|
||||
<svelte:fragment slot="list">
|
||||
<DropListItem icon="mail" on:click={() => updateVerificationEmail()}>
|
||||
{$user.emailVerification ? 'Unverify' : 'Verify'} email
|
||||
</DropListItem>
|
||||
<DropListItem icon="phone" on:click={() => updateVerificationPhone()}>
|
||||
{$user.phoneVerification ? 'Unverify' : 'Verify'} phone
|
||||
</DropListItem>
|
||||
</svelte:fragment>
|
||||
</DropList>
|
||||
{:else if $user.email}
|
||||
<Button secondary on:click={() => updateVerificationEmail()}>
|
||||
{$user.emailVerification ? 'Unverify' : 'Verify'} account
|
||||
</Button>
|
||||
{:else if $user.phone}
|
||||
<Button secondary on:click={() => updateVerificationPhone()}>
|
||||
{$user.phoneVerification ? 'Unverify' : 'Verify'} account
|
||||
</Button>
|
||||
{/if}
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
</CardGrid>
|
||||
|
||||
<Form on:submit={updateName}>
|
||||
<CardGrid>
|
||||
<Heading tag="h6" size="7">Update Name</Heading>
|
||||
|
||||
<svelte:fragment slot="aside">
|
||||
<ul>
|
||||
<InputText
|
||||
id="name"
|
||||
label="Name"
|
||||
placeholder="Enter name"
|
||||
autocomplete={false}
|
||||
bind:value={userName} />
|
||||
</ul>
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="actions">
|
||||
<Button disabled={userName === $user.name} submit>Update</Button>
|
||||
</svelte:fragment>
|
||||
</CardGrid>
|
||||
</Form>
|
||||
|
||||
<Form on:submit={updateEmail}>
|
||||
<CardGrid>
|
||||
<Heading tag="h6" size="7">Update Email</Heading>
|
||||
<svelte:fragment slot="aside">
|
||||
<ul>
|
||||
<InputEmail
|
||||
id="email"
|
||||
label="Email"
|
||||
placeholder="Enter email"
|
||||
autocomplete={false}
|
||||
bind:value={userEmail} />
|
||||
</ul>
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="actions">
|
||||
<Button disabled={userEmail === $user.email} submit>Update</Button>
|
||||
</svelte:fragment>
|
||||
</CardGrid>
|
||||
</Form>
|
||||
|
||||
<Form on:submit={updatePhone}>
|
||||
<CardGrid>
|
||||
<Heading tag="h6" size="7">Update Phone</Heading>
|
||||
<svelte:fragment slot="aside">
|
||||
<ul>
|
||||
<InputPhone
|
||||
id="phone"
|
||||
label="Phone"
|
||||
placeholder="Enter phone number"
|
||||
autocomplete={false}
|
||||
bind:value={userPhone} />
|
||||
</ul>
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="actions">
|
||||
<Button disabled={userPhone === $user.phone} submit>Update</Button>
|
||||
</svelte:fragment>
|
||||
</CardGrid>
|
||||
</Form>
|
||||
|
||||
<Form on:submit={updatePassword}>
|
||||
<CardGrid>
|
||||
<div>
|
||||
<Heading tag="h6" size="7">Update Password</Heading>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
Enter a new password. A password must contain <b>at least 8 characters.</b>
|
||||
</p>
|
||||
<svelte:fragment slot="aside">
|
||||
<ul>
|
||||
<InputPassword
|
||||
id="newPassword"
|
||||
label="New Password"
|
||||
placeholder="Enter new password"
|
||||
autocomplete={false}
|
||||
meter={false}
|
||||
showPasswordButton={true}
|
||||
bind:value={newPassword} />
|
||||
</ul>
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="actions">
|
||||
<Button disabled={!newPassword} submit>Update</Button>
|
||||
</svelte:fragment>
|
||||
</CardGrid>
|
||||
</Form>
|
||||
|
||||
<Form on:submit={updatePrefs}>
|
||||
<CardGrid>
|
||||
<Heading tag="h6" size="7">User Preferences</Heading>
|
||||
<p>
|
||||
You can update your user preferences by storing information on the user's objects so
|
||||
they can easily be shared across devices and sessions.
|
||||
</p>
|
||||
<svelte:fragment slot="aside">
|
||||
<form class="form u-grid u-gap-16">
|
||||
<ul class="form-list">
|
||||
{#if prefs}
|
||||
{#each prefs as [key, value], index}
|
||||
<li class="form-item is-multiple">
|
||||
<div class="form-item-part u-stretch">
|
||||
<label class="label" for={`key-${index}`}>Key</label>
|
||||
<div class="input-text-wrapper">
|
||||
<input
|
||||
id={`key-${key}`}
|
||||
placeholder="Enter key"
|
||||
type="text"
|
||||
class="input-text"
|
||||
bind:value={key} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-item-part u-stretch">
|
||||
<label class="label" for={`value-${index}`}> Value </label>
|
||||
<div class="input-text-wrapper">
|
||||
<input
|
||||
id={`value-${value}`}
|
||||
placeholder="Enter value"
|
||||
type="text"
|
||||
class="input-text"
|
||||
bind:value />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-item-part u-cross-child-end">
|
||||
<Button
|
||||
text
|
||||
disabled={(!key || !value) && index === 0}
|
||||
on:click={() => {
|
||||
if (index === 0) {
|
||||
prefs = [['', '']];
|
||||
} else {
|
||||
prefs.splice(index, 1);
|
||||
prefs = prefs;
|
||||
}
|
||||
}}>
|
||||
<span class="icon-x" aria-hidden="true" />
|
||||
</Button>
|
||||
</div>
|
||||
</li>
|
||||
{/each}
|
||||
{/if}
|
||||
</ul>
|
||||
<Button
|
||||
noMargin
|
||||
text
|
||||
on:click={() => {
|
||||
if (prefs[prefs.length - 1][0] && prefs[prefs.length - 1][1]) {
|
||||
prefs.push(['', '']);
|
||||
prefs = prefs;
|
||||
}
|
||||
}}>
|
||||
<span class="icon-plus" aria-hidden="true" />
|
||||
<span class="text">Add Preference</span>
|
||||
</Button>
|
||||
</form>
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="actions">
|
||||
<Button disabled={arePrefsDisabled} submit>Update</Button>
|
||||
</svelte:fragment>
|
||||
</CardGrid>
|
||||
</Form>
|
||||
|
||||
<CardGrid danger>
|
||||
<div>
|
||||
<Heading tag="h6" size="7">Danger Zone</Heading>
|
||||
</div>
|
||||
<p>
|
||||
The user will be permanently deleted, including all data associated with this user. This
|
||||
action is irreversible.
|
||||
</p>
|
||||
<svelte:fragment slot="aside">
|
||||
<Box>
|
||||
<svelte:fragment slot="image">
|
||||
{#if $user.email || $user.phone}
|
||||
{#if $user.name}
|
||||
<AvatarInitials size={48} name={$user.name} />
|
||||
{:else}
|
||||
<div class="avatar">
|
||||
<span class="icon-minus-sm" aria-hidden="true" />
|
||||
</div>
|
||||
{/if}
|
||||
{:else}
|
||||
<div class="avatar">
|
||||
<span class="icon-anonymous" aria-hidden="true" />
|
||||
</div>
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="title">
|
||||
<h6 class="u-bold u-trim-1">
|
||||
{$user.name || $user.email || $user.phone || 'Anonymous'}
|
||||
</h6>
|
||||
</svelte:fragment>
|
||||
<p class="u-trim-1">
|
||||
{$user.email && $user.phone
|
||||
? [$user.email, $user.phone].join(',')
|
||||
: $user.email || $user.phone}
|
||||
</p>
|
||||
</Box>
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="actions">
|
||||
<Button secondary on:click={() => (showDelete = true)} event="delete_user"
|
||||
>Delete</Button>
|
||||
</svelte:fragment>
|
||||
</CardGrid>
|
||||
<UpdateStatus />
|
||||
<UpdateName />
|
||||
<UpdateEmail />
|
||||
<UpdatePhone />
|
||||
<UpdatePassword />
|
||||
<UpdatePrefs />
|
||||
<DangerZone />
|
||||
</Container>
|
||||
|
||||
<DeleteUser bind:showDelete />
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
<script lang="ts">
|
||||
import { CardGrid, Box, Heading, AvatarInitials } from '$lib/components';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import DeleteUser from './deleteUser.svelte';
|
||||
import { user } from './store';
|
||||
|
||||
let showDelete = false;
|
||||
</script>
|
||||
|
||||
<CardGrid danger>
|
||||
<div>
|
||||
<Heading tag="h6" size="7">Danger Zone</Heading>
|
||||
</div>
|
||||
<p>
|
||||
The user will be permanently deleted, including all data associated with this user. This
|
||||
action is irreversible.
|
||||
</p>
|
||||
<svelte:fragment slot="aside">
|
||||
<Box>
|
||||
<svelte:fragment slot="image">
|
||||
{#if $user.email || $user.phone}
|
||||
{#if $user.name}
|
||||
<AvatarInitials size={48} name={$user.name} />
|
||||
{:else}
|
||||
<div class="avatar">
|
||||
<span class="icon-minus-sm" aria-hidden="true" />
|
||||
</div>
|
||||
{/if}
|
||||
{:else}
|
||||
<div class="avatar">
|
||||
<span class="icon-anonymous" aria-hidden="true" />
|
||||
</div>
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="title">
|
||||
<h6 class="u-bold u-trim-1">
|
||||
{$user.name || $user.email || $user.phone || 'Anonymous'}
|
||||
</h6>
|
||||
</svelte:fragment>
|
||||
<p class="u-trim-1">
|
||||
{$user.email && $user.phone
|
||||
? [$user.email, $user.phone].join(',')
|
||||
: $user.email || $user.phone}
|
||||
</p>
|
||||
</Box>
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="actions">
|
||||
<Button secondary on:click={() => (showDelete = true)} event="delete_user">Delete</Button>
|
||||
</svelte:fragment>
|
||||
</CardGrid>
|
||||
|
||||
<DeleteUser bind:showDelete />
|
||||
@@ -72,7 +72,7 @@
|
||||
<Button
|
||||
external
|
||||
secondary
|
||||
href="https://appwrite.io/docs/server/auth?sdk=nodejs-default#usersGetMemberships">
|
||||
href="https://appwrite.io/docs/server/users?sdk=nodejs-default#usersListMemberships">
|
||||
Documentation
|
||||
</Button>
|
||||
</Empty>
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
<script lang="ts">
|
||||
import { invalidate } from '$app/navigation';
|
||||
import { trackEvent } from '$lib/actions/analytics';
|
||||
import { CardGrid, Heading } from '$lib/components';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { Button, Form, InputEmail } from '$lib/elements/forms';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { sdkForProject } from '$lib/stores/sdk';
|
||||
import { onMount } from 'svelte';
|
||||
import { user } from './store';
|
||||
|
||||
let userEmail: string = null;
|
||||
onMount(async () => {
|
||||
userEmail ??= $user.email;
|
||||
});
|
||||
|
||||
async function updateEmail() {
|
||||
try {
|
||||
await sdkForProject.users.updateEmail($user.$id, userEmail);
|
||||
invalidate(Dependencies.USER);
|
||||
addNotification({
|
||||
message: 'Email has been updated',
|
||||
type: 'success'
|
||||
});
|
||||
trackEvent('submit_user_update_email');
|
||||
} catch (error) {
|
||||
addNotification({
|
||||
message: error.message,
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<Form on:submit={updateEmail}>
|
||||
<CardGrid>
|
||||
<Heading tag="h6" size="7">Update Email</Heading>
|
||||
<svelte:fragment slot="aside">
|
||||
<ul>
|
||||
<InputEmail
|
||||
id="email"
|
||||
label="Email"
|
||||
placeholder="Enter email"
|
||||
autocomplete={false}
|
||||
bind:value={userEmail} />
|
||||
</ul>
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="actions">
|
||||
<Button disabled={userEmail === $user.email} submit>Update</Button>
|
||||
</svelte:fragment>
|
||||
</CardGrid>
|
||||
</Form>
|
||||
@@ -0,0 +1,54 @@
|
||||
<script lang="ts">
|
||||
import { invalidate } from '$app/navigation';
|
||||
import { trackEvent } from '$lib/actions/analytics';
|
||||
import { CardGrid, Heading } from '$lib/components';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { Button, Form, InputText } from '$lib/elements/forms';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { sdkForProject } from '$lib/stores/sdk';
|
||||
import { onMount } from 'svelte';
|
||||
import { user } from './store';
|
||||
|
||||
let userName: string = null;
|
||||
onMount(async () => {
|
||||
userName ??= $user.name;
|
||||
});
|
||||
|
||||
async function updateName() {
|
||||
try {
|
||||
await sdkForProject.users.updateName($user.$id, userName);
|
||||
invalidate(Dependencies.USER);
|
||||
addNotification({
|
||||
message: 'Name has been updated',
|
||||
type: 'success'
|
||||
});
|
||||
trackEvent('submit_user_update_name');
|
||||
} catch (error) {
|
||||
addNotification({
|
||||
message: error.message,
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<Form on:submit={updateName}>
|
||||
<CardGrid>
|
||||
<Heading tag="h6" size="7">Update Name</Heading>
|
||||
|
||||
<svelte:fragment slot="aside">
|
||||
<ul>
|
||||
<InputText
|
||||
id="name"
|
||||
label="Name"
|
||||
placeholder="Enter name"
|
||||
autocomplete={false}
|
||||
bind:value={userName} />
|
||||
</ul>
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="actions">
|
||||
<Button disabled={userName === $user.name} submit>Update</Button>
|
||||
</svelte:fragment>
|
||||
</CardGrid>
|
||||
</Form>
|
||||
@@ -0,0 +1,55 @@
|
||||
<script lang="ts">
|
||||
import { trackEvent } from '$lib/actions/analytics';
|
||||
import { CardGrid, Heading } from '$lib/components';
|
||||
import { Button, Form, InputPassword } from '$lib/elements/forms';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { sdkForProject } from '$lib/stores/sdk';
|
||||
import { user } from './store';
|
||||
|
||||
let newPassword: string = null;
|
||||
|
||||
async function updatePassword() {
|
||||
try {
|
||||
await sdkForProject.users.updatePassword($user.$id, newPassword);
|
||||
newPassword = null;
|
||||
addNotification({
|
||||
message: 'Password has been updated',
|
||||
type: 'success'
|
||||
});
|
||||
trackEvent('submit_user_update_password');
|
||||
} catch (error) {
|
||||
addNotification({
|
||||
message: error.message,
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<Form on:submit={updatePassword}>
|
||||
<CardGrid>
|
||||
<div>
|
||||
<Heading tag="h6" size="7">Update Password</Heading>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
Enter a new password. A password must contain <b>at least 8 characters.</b>
|
||||
</p>
|
||||
<svelte:fragment slot="aside">
|
||||
<ul>
|
||||
<InputPassword
|
||||
id="newPassword"
|
||||
label="New Password"
|
||||
placeholder="Enter new password"
|
||||
autocomplete={false}
|
||||
meter={false}
|
||||
showPasswordButton={true}
|
||||
bind:value={newPassword} />
|
||||
</ul>
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="actions">
|
||||
<Button disabled={!newPassword} submit>Update</Button>
|
||||
</svelte:fragment>
|
||||
</CardGrid>
|
||||
</Form>
|
||||
@@ -0,0 +1,53 @@
|
||||
<script lang="ts">
|
||||
import { invalidate } from '$app/navigation';
|
||||
import { trackEvent } from '$lib/actions/analytics';
|
||||
import { CardGrid, Heading } from '$lib/components';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { Button, Form, InputPhone } from '$lib/elements/forms';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { sdkForProject } from '$lib/stores/sdk';
|
||||
import { onMount } from 'svelte';
|
||||
import { user } from './store';
|
||||
|
||||
let userPhone: string = null;
|
||||
onMount(async () => {
|
||||
userPhone ??= $user.phone;
|
||||
});
|
||||
|
||||
async function updatePhone() {
|
||||
try {
|
||||
await sdkForProject.users.updatePhone($user.$id, userPhone);
|
||||
invalidate(Dependencies.USER);
|
||||
addNotification({
|
||||
message: 'Phone has been updated',
|
||||
type: 'success'
|
||||
});
|
||||
trackEvent('submit_user_update_phone');
|
||||
} catch (error) {
|
||||
addNotification({
|
||||
message: error.message,
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<Form on:submit={updatePhone}>
|
||||
<CardGrid>
|
||||
<Heading tag="h6" size="7">Update Phone</Heading>
|
||||
<svelte:fragment slot="aside">
|
||||
<ul>
|
||||
<InputPhone
|
||||
id="phone"
|
||||
label="Phone"
|
||||
placeholder="Enter phone number"
|
||||
autocomplete={false}
|
||||
bind:value={userPhone} />
|
||||
</ul>
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="actions">
|
||||
<Button disabled={userPhone === $user.phone} submit>Update</Button>
|
||||
</svelte:fragment>
|
||||
</CardGrid>
|
||||
</Form>
|
||||
@@ -0,0 +1,129 @@
|
||||
<script lang="ts">
|
||||
import { invalidate } from '$app/navigation';
|
||||
import { trackEvent } from '$lib/actions/analytics';
|
||||
import { CardGrid, Heading } from '$lib/components';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { Button, Form } from '$lib/elements/forms';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { sdkForProject } from '$lib/stores/sdk';
|
||||
import { onMount } from 'svelte';
|
||||
import { user } from './store';
|
||||
|
||||
$: if (prefs) {
|
||||
if (JSON.stringify(prefs) !== JSON.stringify(Object.entries($user.prefs))) {
|
||||
if (!!prefs[prefs.length - 1][0] && !!prefs[prefs.length - 1][1]) {
|
||||
arePrefsDisabled = false;
|
||||
} else {
|
||||
arePrefsDisabled = true;
|
||||
}
|
||||
} else {
|
||||
arePrefsDisabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
let prefs: [string, unknown][] = null;
|
||||
let arePrefsDisabled = true;
|
||||
|
||||
onMount(async () => {
|
||||
prefs = Object.entries($user.prefs);
|
||||
if (!prefs?.length) {
|
||||
prefs.push(['', '']);
|
||||
}
|
||||
});
|
||||
|
||||
async function updatePrefs() {
|
||||
try {
|
||||
let updatedPrefs = Object.fromEntries(prefs);
|
||||
|
||||
await sdkForProject.users.updatePrefs($user.$id, updatedPrefs);
|
||||
invalidate(Dependencies.USER);
|
||||
arePrefsDisabled = true;
|
||||
|
||||
addNotification({
|
||||
message: 'Preferences have been updated',
|
||||
type: 'success'
|
||||
});
|
||||
trackEvent('submit_user_update_preferences');
|
||||
} catch (error) {
|
||||
addNotification({
|
||||
message: error.message,
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<Form on:submit={updatePrefs}>
|
||||
<CardGrid>
|
||||
<Heading tag="h6" size="7">User Preferences</Heading>
|
||||
<p>
|
||||
You can update your user preferences by storing information on the user's objects so
|
||||
they can easily be shared across devices and sessions.
|
||||
</p>
|
||||
<svelte:fragment slot="aside">
|
||||
<form class="form u-grid u-gap-16">
|
||||
<ul class="form-list">
|
||||
{#if prefs}
|
||||
{#each prefs as [key, value], index}
|
||||
<li class="form-item is-multiple">
|
||||
<div class="form-item-part u-stretch">
|
||||
<label class="label" for={`key-${index}`}>Key</label>
|
||||
<div class="input-text-wrapper">
|
||||
<input
|
||||
id={`key-${key}`}
|
||||
placeholder="Enter key"
|
||||
type="text"
|
||||
class="input-text"
|
||||
bind:value={key} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-item-part u-stretch">
|
||||
<label class="label" for={`value-${index}`}> Value </label>
|
||||
<div class="input-text-wrapper">
|
||||
<input
|
||||
id={`value-${value}`}
|
||||
placeholder="Enter value"
|
||||
type="text"
|
||||
class="input-text"
|
||||
bind:value />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-item-part u-cross-child-end">
|
||||
<Button
|
||||
text
|
||||
disabled={(!key || !value) && index === 0}
|
||||
on:click={() => {
|
||||
if (index === 0) {
|
||||
prefs = [['', '']];
|
||||
} else {
|
||||
prefs.splice(index, 1);
|
||||
prefs = prefs;
|
||||
}
|
||||
}}>
|
||||
<span class="icon-x" aria-hidden="true" />
|
||||
</Button>
|
||||
</div>
|
||||
</li>
|
||||
{/each}
|
||||
{/if}
|
||||
</ul>
|
||||
<Button
|
||||
noMargin
|
||||
text
|
||||
on:click={() => {
|
||||
if (prefs[prefs.length - 1][0] && prefs[prefs.length - 1][1]) {
|
||||
prefs.push(['', '']);
|
||||
prefs = prefs;
|
||||
}
|
||||
}}>
|
||||
<span class="icon-plus" aria-hidden="true" />
|
||||
<span class="text">Add Preference</span>
|
||||
</Button>
|
||||
</form>
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="actions">
|
||||
<Button disabled={arePrefsDisabled} submit>Update</Button>
|
||||
</svelte:fragment>
|
||||
</CardGrid>
|
||||
</Form>
|
||||
@@ -0,0 +1,157 @@
|
||||
<script lang="ts">
|
||||
import { invalidate } from '$app/navigation';
|
||||
import { trackEvent } from '$lib/actions/analytics';
|
||||
import { AvatarInitials, CardGrid, DropList, DropListItem, Heading } from '$lib/components';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { Pill } from '$lib/elements';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import { toLocaleDateTime } from '$lib/helpers/date';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { sdkForProject } from '$lib/stores/sdk';
|
||||
import { user } from './store';
|
||||
|
||||
let showVerifcationDropdown = false;
|
||||
|
||||
async function updateVerificationEmail() {
|
||||
showVerifcationDropdown = false;
|
||||
try {
|
||||
await sdkForProject.users.updateEmailVerification($user.$id, !$user.emailVerification);
|
||||
invalidate(Dependencies.USER);
|
||||
addNotification({
|
||||
message: `${$user.name || $user.email || $user.phone || 'The account'} has been ${
|
||||
$user.emailVerification ? 'verified' : 'unverified'
|
||||
}`,
|
||||
type: 'success'
|
||||
});
|
||||
trackEvent('submit_user_update_verification_email');
|
||||
} catch (error) {
|
||||
addNotification({
|
||||
message: error.message,
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
}
|
||||
async function updateVerificationPhone() {
|
||||
showVerifcationDropdown = false;
|
||||
try {
|
||||
await sdkForProject.users.updatePhoneVerification($user.$id, !$user.phoneVerification);
|
||||
invalidate(Dependencies.USER);
|
||||
addNotification({
|
||||
message: `${$user.name || $user.email || $user.phone || 'The account'} has been ${
|
||||
$user.phoneVerification ? 'verified' : 'unverified'
|
||||
}`,
|
||||
type: 'success'
|
||||
});
|
||||
trackEvent('submit_user_update_verification_phone');
|
||||
} catch (error) {
|
||||
addNotification({
|
||||
message: error.message,
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
}
|
||||
async function updateStatus() {
|
||||
try {
|
||||
await sdkForProject.users.updateStatus($user.$id, !$user.status);
|
||||
invalidate(Dependencies.USER);
|
||||
addNotification({
|
||||
message: `${$user.name || $user.email || $user.phone || 'The account'} has been ${
|
||||
$user.status ? 'unblocked' : 'blocked'
|
||||
}`,
|
||||
type: 'success'
|
||||
});
|
||||
trackEvent('submit_user_update_status');
|
||||
} catch (error) {
|
||||
addNotification({
|
||||
message: error.message,
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<CardGrid>
|
||||
<div class="grid-1-2-col-1 u-flex u-cross-center u-gap-16">
|
||||
{#if $user.email || $user.phone}
|
||||
{#if $user.name}
|
||||
<AvatarInitials size={48} name={$user.name} />
|
||||
<Heading tag="h6" size="7">{$user.name}</Heading>
|
||||
{:else}
|
||||
<div class="avatar">
|
||||
<span class="icon-minus-sm" aria-hidden="true" />
|
||||
</div>
|
||||
{/if}
|
||||
{:else}
|
||||
<div class="avatar">
|
||||
<span class="icon-anonymous" aria-hidden="true" />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
<svelte:fragment slot="aside">
|
||||
<div class="u-flex u-main-space-between">
|
||||
<div>
|
||||
{#if $user.email}
|
||||
<p class="title">{$user.email}</p>
|
||||
{/if}
|
||||
{#if $user.phone}
|
||||
<p class="title">{$user.phone}</p>
|
||||
{/if}
|
||||
<p>Joined: {toLocaleDateTime($user.registration)}</p>
|
||||
</div>
|
||||
{#if !$user.status}
|
||||
<Pill danger>blocked</Pill>
|
||||
{:else if $user.email && $user.phone}
|
||||
<Pill success={$user.emailVerification || $user.phoneVerification}>
|
||||
{$user.emailVerification && $user.phoneVerification
|
||||
? 'verified'
|
||||
: $user.emailVerification
|
||||
? 'verified email'
|
||||
: $user.phoneVerification
|
||||
? 'verified phone'
|
||||
: 'unverified'}
|
||||
</Pill>
|
||||
{:else}
|
||||
<Pill success={$user.emailVerification || $user.phoneVerification}>
|
||||
{$user.emailVerification
|
||||
? 'verified '
|
||||
: $user.phoneVerification
|
||||
? 'verified '
|
||||
: 'unverified'}
|
||||
</Pill>
|
||||
{/if}
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="actions">
|
||||
<Button text={$user.status} secondary={!$user.status} on:click={() => updateStatus()}>
|
||||
{$user.status ? 'Block account' : 'Unblock account'}
|
||||
</Button>
|
||||
{#if $user.status}
|
||||
{#if $user.phone && $user.email}
|
||||
<DropList bind:show={showVerifcationDropdown} placement="top-start">
|
||||
<Button
|
||||
secondary
|
||||
on:click={() => (showVerifcationDropdown = !showVerifcationDropdown)}>
|
||||
{$user.emailVerification ? 'Unverify' : 'Verify'} account
|
||||
</Button>
|
||||
<svelte:fragment slot="list">
|
||||
<DropListItem icon="mail" on:click={() => updateVerificationEmail()}>
|
||||
{$user.emailVerification ? 'Unverify' : 'Verify'} email
|
||||
</DropListItem>
|
||||
<DropListItem icon="phone" on:click={() => updateVerificationPhone()}>
|
||||
{$user.phoneVerification ? 'Unverify' : 'Verify'} phone
|
||||
</DropListItem>
|
||||
</svelte:fragment>
|
||||
</DropList>
|
||||
{:else if $user.email}
|
||||
<Button secondary on:click={() => updateVerificationEmail()}>
|
||||
{$user.emailVerification ? 'Unverify' : 'Verify'} account
|
||||
</Button>
|
||||
{:else if $user.phone}
|
||||
<Button secondary on:click={() => updateVerificationPhone()}>
|
||||
{$user.phoneVerification ? 'Unverify' : 'Verify'} account
|
||||
</Button>
|
||||
{/if}
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
</CardGrid>
|
||||
@@ -12,6 +12,7 @@
|
||||
import { createDocument } from './wizard/store';
|
||||
import type { WizardStepsType } from '$lib/layout/wizard.svelte';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { trackEvent } from '$lib/actions/analytics';
|
||||
|
||||
const databaseId = $page.params.database;
|
||||
const collectionId = $page.params.collection;
|
||||
@@ -42,6 +43,7 @@
|
||||
message: 'Document has been created',
|
||||
type: 'success'
|
||||
});
|
||||
trackEvent('submit_document_create');
|
||||
invalidate(Dependencies.DOCUMENTS);
|
||||
wizard.hide();
|
||||
} catch (error) {
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { invalidate } from '$app/navigation';
|
||||
import Attribute from './attribute.svelte';
|
||||
import { trackEvent } from '$lib/actions/analytics';
|
||||
|
||||
let disableUpdate = true;
|
||||
let currentDoc: string;
|
||||
@@ -61,6 +62,9 @@
|
||||
|
||||
currentDoc = JSON.stringify($work);
|
||||
invalidate(Dependencies.DOCUMENT);
|
||||
trackEvent('submit_document_update', {
|
||||
type: 'android'
|
||||
});
|
||||
disableUpdate = true;
|
||||
addNotification({
|
||||
message: 'Document was updated!',
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import Select from './select.svelte';
|
||||
import { trackEvent } from '$lib/actions/analytics';
|
||||
import { remove } from '$lib/helpers/array';
|
||||
|
||||
export let showCreateIndex = false;
|
||||
export let externalAttribute: Attributes = null;
|
||||
@@ -24,115 +25,77 @@
|
||||
{ value: 'unique', label: 'Unique' },
|
||||
{ value: 'fulltext', label: 'FullText' }
|
||||
];
|
||||
let newAttr = false;
|
||||
let selectedType = 'key';
|
||||
$: attributeOptions = $collection.attributes.map((attribute: Attributes) => ({
|
||||
|
||||
let attributeOptions = $collection.attributes.map((attribute: Attributes) => ({
|
||||
value: attribute.key,
|
||||
label: attribute.key
|
||||
}));
|
||||
$: attributeList = [];
|
||||
let attributeList = [{ value: '', order: '' }];
|
||||
|
||||
let selectedAttribute = '';
|
||||
let selectedOrder = '';
|
||||
$: addAttributeDisabled = !attributeList.at(-1)?.value || !attributeList.at(-1)?.order;
|
||||
|
||||
onMount(() => {
|
||||
if (externalAttribute) {
|
||||
attributeList = [{ value: externalAttribute.key, order: 'ASC' }];
|
||||
}
|
||||
if (!externalAttribute) return;
|
||||
attributeList = [{ value: externalAttribute.key, order: 'ASC' }];
|
||||
});
|
||||
|
||||
$: if (showCreateIndex) {
|
||||
attributeList = [];
|
||||
selectedOrder = selectedAttribute = '';
|
||||
attributeList = [{ value: '', order: '' }];
|
||||
selectedType = 'key';
|
||||
key = null;
|
||||
}
|
||||
const created = async () => {
|
||||
if (key && selectedAttribute && selectedOrder && selectedType) {
|
||||
if (selectedAttribute && selectedOrder) {
|
||||
attributeList.push({ value: selectedAttribute, order: selectedOrder });
|
||||
selectedAttribute = selectedOrder = '';
|
||||
}
|
||||
try {
|
||||
await sdkForProject.databases.createIndex(
|
||||
databaseId,
|
||||
$collection.$id,
|
||||
key,
|
||||
selectedType,
|
||||
attributeList.map((a) => a.value),
|
||||
attributeList.map((a) => a.order)
|
||||
);
|
||||
invalidate(Dependencies.COLLECTION);
|
||||
addNotification({
|
||||
message: 'Index has been created',
|
||||
type: 'success'
|
||||
});
|
||||
trackEvent('submit_index_create');
|
||||
} catch (error) {
|
||||
addNotification({
|
||||
message: error.message,
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
|
||||
async function create() {
|
||||
if (!(key && selectedType && !addAttributeDisabled)) {
|
||||
error = 'All fields are required';
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await sdkForProject.databases.createIndex(
|
||||
databaseId,
|
||||
$collection.$id,
|
||||
key,
|
||||
selectedType,
|
||||
attributeList.map((a) => a.value),
|
||||
attributeList.map((a) => a.order)
|
||||
);
|
||||
invalidate(Dependencies.COLLECTION);
|
||||
addNotification({
|
||||
message: 'Index has been created',
|
||||
type: 'success'
|
||||
});
|
||||
trackEvent('submit_index_create');
|
||||
} catch (error) {
|
||||
addNotification({
|
||||
message: error.message,
|
||||
type: 'error'
|
||||
});
|
||||
} finally {
|
||||
showCreateIndex = false;
|
||||
} else error = 'All fields are required';
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function addAttribute() {
|
||||
if (addAttributeDisabled) return;
|
||||
|
||||
// We assign instead of pushing to trigger Svelte's reactivity
|
||||
attributeList = [...attributeList, { value: '', order: '' }];
|
||||
}
|
||||
</script>
|
||||
|
||||
<Modal bind:error size="big" on:submit={created} bind:show={showCreateIndex}>
|
||||
<Modal bind:error size="big" on:submit={create} bind:show={showCreateIndex}>
|
||||
<svelte:fragment slot="header">Create Index</svelte:fragment>
|
||||
<FormList>
|
||||
<InputText id="key" label="Index Key" placeholder="Enter Key" bind:value={key} autofocus />
|
||||
<InputSelect options={types} id="type" label="Index type" bind:value={selectedType} />
|
||||
|
||||
{#if attributeList?.length}
|
||||
{#each attributeList as index, i}
|
||||
<li class="form-item is-multiple">
|
||||
<div class="form-item-part u-stretch">
|
||||
<Select id="attribute" label="Attribute" bind:value={index.value}>
|
||||
<optgroup label="Internal">
|
||||
<option value="$id">$id</option>
|
||||
<option value="$createdAt">$createdAt</option>
|
||||
<option value="$updatedAt">$updatedAt</option>
|
||||
</optgroup>
|
||||
<optgroup label="Attributes">
|
||||
{#each attributeOptions as option}
|
||||
<option
|
||||
value={option.value}
|
||||
selected={option.value === selectedAttribute}>
|
||||
{option.label}
|
||||
</option>
|
||||
{/each}
|
||||
</optgroup>
|
||||
</Select>
|
||||
</div>
|
||||
<div class="form-item-part u-stretch">
|
||||
<Select id="order" label="Order" bind:value={index.order}>
|
||||
<option value="ASC"> ASC </option>
|
||||
<option value="DESC"> DESC </option>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div class="form-item-part u-cross-child-end">
|
||||
<Button
|
||||
text
|
||||
disabled={externalAttribute && i === 0}
|
||||
on:click={() => {
|
||||
if (i === 0) attributeList = [];
|
||||
attributeList = attributeList.splice(i, 1);
|
||||
}}>
|
||||
<span class="icon-x" aria-hidden="true" />
|
||||
</Button>
|
||||
</div>
|
||||
</li>
|
||||
{/each}
|
||||
{/if}
|
||||
{#if !attributeList?.length || newAttr}
|
||||
{#each attributeList as attribute, i}
|
||||
<li class="form-item is-multiple">
|
||||
<div class="form-item-part u-stretch" style="align-items: flex-start;">
|
||||
<Select id="attribute" label="Attribute" bind:value={selectedAttribute}>
|
||||
<option value="" disabled selected hidden>Select Attribute</option>
|
||||
<div class="form-item-part u-stretch">
|
||||
<Select id={`attribute-${i}`} label="Attribute" bind:value={attribute.value}>
|
||||
<option value="" disabled hidden>Select Attribute</option>
|
||||
|
||||
<optgroup label="Internal">
|
||||
<option value="$id">$id</option>
|
||||
@@ -141,9 +104,7 @@
|
||||
</optgroup>
|
||||
<optgroup label="Attributes">
|
||||
{#each attributeOptions as option}
|
||||
<option
|
||||
value={option.value}
|
||||
selected={option.value === selectedAttribute}>
|
||||
<option value={option.value}>
|
||||
{option.label}
|
||||
</option>
|
||||
{/each}
|
||||
@@ -151,36 +112,28 @@
|
||||
</Select>
|
||||
</div>
|
||||
<div class="form-item-part u-stretch">
|
||||
<Select id="order" label="Order" bind:value={selectedOrder}>
|
||||
<option value="" disabled selected hidden>Select Order</option>
|
||||
<Select id={`order-${i}`} label="Order" bind:value={attribute.order}>
|
||||
<option value="" disabled hidden>Select Order</option>
|
||||
|
||||
<option value="ASC"> ASC </option>
|
||||
<option value="DESC"> DESC </option>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div class="form-item-part u-cross-child-end">
|
||||
<Button
|
||||
text
|
||||
disabled={false}
|
||||
disabled={attributeList.length <= 1}
|
||||
on:click={() => {
|
||||
newAttr = false;
|
||||
selectedAttribute = selectedOrder = '';
|
||||
attributeList = remove(attributeList, i);
|
||||
}}>
|
||||
<span class="icon-x" aria-hidden="true" />
|
||||
</Button>
|
||||
</div>
|
||||
</li>
|
||||
{/if}
|
||||
<Button
|
||||
text
|
||||
noMargin
|
||||
on:click={() => {
|
||||
newAttr = true;
|
||||
if (selectedAttribute && selectedOrder) {
|
||||
attributeList.push({ value: selectedAttribute, order: selectedOrder });
|
||||
selectedAttribute = selectedOrder = '';
|
||||
}
|
||||
}}>
|
||||
{/each}
|
||||
|
||||
<Button text noMargin on:click={addAttribute} disabled={addAttributeDisabled}>
|
||||
<span class="icon-plus" aria-hidden="true" />
|
||||
<span class="text">Add attribute</span>
|
||||
</Button>
|
||||
|
||||
@@ -77,7 +77,9 @@
|
||||
databaseId,
|
||||
$collection.$id,
|
||||
collectionName,
|
||||
$collection.$permissions
|
||||
$collection.$permissions,
|
||||
$collection.documentSecurity,
|
||||
$collection.enabled
|
||||
);
|
||||
invalidate(Dependencies.COLLECTION);
|
||||
showError = false;
|
||||
@@ -96,7 +98,9 @@
|
||||
databaseId,
|
||||
$collection.$id,
|
||||
$collection.name,
|
||||
collectionPermissions
|
||||
collectionPermissions,
|
||||
$collection.documentSecurity,
|
||||
$collection.enabled
|
||||
);
|
||||
invalidate(Dependencies.COLLECTION);
|
||||
arePermsDisabled = true;
|
||||
@@ -120,7 +124,8 @@
|
||||
$collection.$id,
|
||||
$collection.name,
|
||||
$collection.$permissions,
|
||||
collectionDocumentSecurity
|
||||
collectionDocumentSecurity,
|
||||
$collection.enabled
|
||||
);
|
||||
invalidate(Dependencies.COLLECTION);
|
||||
arePermsDisabled = true;
|
||||
|
||||
@@ -16,14 +16,19 @@
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
const handleVariable = () => {
|
||||
function close() {
|
||||
showCreate = false;
|
||||
selectedVar = null;
|
||||
}
|
||||
|
||||
function handleVariable() {
|
||||
if (selectedVar) {
|
||||
dispatch('updated', pair);
|
||||
} else {
|
||||
dispatch('created', pair);
|
||||
}
|
||||
showCreate = false;
|
||||
};
|
||||
close();
|
||||
}
|
||||
</script>
|
||||
|
||||
<Modal bind:show={showCreate} on:submit={handleVariable} size="big">
|
||||
@@ -48,7 +53,7 @@
|
||||
required />
|
||||
</FormList>
|
||||
<svelte:fragment slot="footer">
|
||||
<Button secondary on:click={() => (showCreate = false)}>Cancel</Button>
|
||||
<Button secondary on:click={close}>Cancel</Button>
|
||||
<Button submit>{selectedVar ? 'Update' : 'Create'}</Button>
|
||||
</svelte:fragment>
|
||||
</Modal>
|
||||
|
||||
@@ -70,7 +70,15 @@
|
||||
|
||||
async function updateName() {
|
||||
try {
|
||||
await sdkForProject.functions.update(functionId, functionName, $func.execute);
|
||||
await sdkForProject.functions.update(
|
||||
functionId,
|
||||
functionName,
|
||||
$func.execute,
|
||||
$func.events,
|
||||
$func.schedule,
|
||||
$func.timeout,
|
||||
$func.enabled
|
||||
);
|
||||
invalidate(Dependencies.FUNCTION);
|
||||
addNotification({
|
||||
message: 'Name has been updated',
|
||||
@@ -87,7 +95,15 @@
|
||||
|
||||
async function updatePermissions() {
|
||||
try {
|
||||
await sdkForProject.functions.update(functionId, $func.name, permissions);
|
||||
await sdkForProject.functions.update(
|
||||
functionId,
|
||||
$func.name,
|
||||
permissions,
|
||||
$func.events,
|
||||
$func.schedule,
|
||||
$func.timeout,
|
||||
$func.enabled
|
||||
);
|
||||
invalidate(Dependencies.FUNCTION);
|
||||
addNotification({
|
||||
message: 'Permissions have been updated',
|
||||
@@ -108,7 +124,10 @@
|
||||
functionId,
|
||||
$func.name,
|
||||
$func.execute,
|
||||
Array.from($eventSet)
|
||||
Array.from($eventSet),
|
||||
$func.schedule,
|
||||
$func.timeout,
|
||||
$func.enabled
|
||||
);
|
||||
invalidate(Dependencies.FUNCTION);
|
||||
addNotification({
|
||||
@@ -132,7 +151,8 @@
|
||||
$func.execute,
|
||||
$func.events,
|
||||
functionSchedule,
|
||||
timeout
|
||||
$func.timeout,
|
||||
$func.enabled
|
||||
);
|
||||
invalidate(Dependencies.FUNCTION);
|
||||
|
||||
@@ -157,9 +177,11 @@
|
||||
$func.execute,
|
||||
$func.events,
|
||||
$func.schedule,
|
||||
timeout
|
||||
timeout,
|
||||
$func.enabled
|
||||
);
|
||||
|
||||
invalidate(Dependencies.FUNCTION);
|
||||
addNotification({
|
||||
type: 'success',
|
||||
message: 'Timeout has been updated'
|
||||
@@ -557,10 +579,12 @@
|
||||
</Container>
|
||||
|
||||
<Delete bind:showDelete />
|
||||
<Variable
|
||||
bind:selectedVar
|
||||
bind:showCreate={showVariablesModal}
|
||||
on:created={handleVariableCreated}
|
||||
on:updated={handleVariableUpdated} />
|
||||
{#if showVariablesModal}
|
||||
<Variable
|
||||
bind:selectedVar
|
||||
bind:showCreate={showVariablesModal}
|
||||
on:created={handleVariableCreated}
|
||||
on:updated={handleVariableUpdated} />
|
||||
{/if}
|
||||
<!-- <Upload bind:show={showVariablesUpload} on:uploaded={handleVariableCreated} /> -->
|
||||
<EventModal bind:show={showEvents} on:created={handleEvent} />
|
||||
|
||||
@@ -2,5 +2,5 @@ import { page } from '$app/stores';
|
||||
import { derived, writable, type Writable } from 'svelte/store';
|
||||
import type { Models } from '@aw-labs/appwrite-console';
|
||||
|
||||
export const func = derived(page, ($page) => $page.data.function);
|
||||
export const func = derived(page, ($page) => $page.data.function as Models.Function);
|
||||
export const execute: Writable<Models.Function> = writable();
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
import { key } from './store';
|
||||
import Scopes from '../scopes.svelte';
|
||||
import Delete from './delete.svelte';
|
||||
import { trackEvent } from '$lib/actions/analytics';
|
||||
|
||||
let showDelete = false;
|
||||
let name: string = null;
|
||||
@@ -37,6 +38,7 @@
|
||||
$key.expire
|
||||
);
|
||||
invalidate(Dependencies.KEY);
|
||||
trackEvent('submit_key_update_name');
|
||||
addNotification({
|
||||
type: 'success',
|
||||
message: 'API Key name has been updated'
|
||||
@@ -59,6 +61,7 @@
|
||||
expire
|
||||
);
|
||||
invalidate(Dependencies.KEY);
|
||||
trackEvent('submit_key_update_expire');
|
||||
addNotification({
|
||||
type: 'success',
|
||||
message: 'API Key expiration has been updated'
|
||||
@@ -73,8 +76,15 @@
|
||||
|
||||
async function updateScopes() {
|
||||
try {
|
||||
await sdkForConsole.projects.updateKey($project.$id, $key.$id, $key.name, scopes);
|
||||
await sdkForConsole.projects.updateKey(
|
||||
$project.$id,
|
||||
$key.$id,
|
||||
$key.name,
|
||||
scopes,
|
||||
$key.expire
|
||||
);
|
||||
invalidate(Dependencies.KEY);
|
||||
trackEvent('submit_key_update_scopes');
|
||||
addNotification({
|
||||
type: 'success',
|
||||
message: 'API Key scopes has been updated'
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
import { onDestroy } from 'svelte';
|
||||
import { onboarding } from '../../store';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { trackEvent } from '$lib/actions/analytics';
|
||||
|
||||
async function onFinish() {
|
||||
try {
|
||||
@@ -24,6 +25,7 @@
|
||||
if ($onboarding) {
|
||||
invalidate(Dependencies.PROJECT);
|
||||
}
|
||||
trackEvent('submit_key_create');
|
||||
goto(`/console/project-${$page.params.project}/overview/keys/${$id}`);
|
||||
} catch (error) {
|
||||
addNotification({
|
||||
|
||||
@@ -48,7 +48,14 @@
|
||||
|
||||
const updateName = async () => {
|
||||
try {
|
||||
await sdkForConsole.projects.updatePlatform($project.$id, data.platform.$id, name);
|
||||
await sdkForConsole.projects.updatePlatform(
|
||||
$project.$id,
|
||||
data.platform.$id,
|
||||
name,
|
||||
data.platform.key,
|
||||
data.platform.store,
|
||||
data.platform.hostname
|
||||
);
|
||||
invalidate(Dependencies.PLATFORM);
|
||||
addNotification({
|
||||
type: 'success',
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { invalidate } from '$app/navigation';
|
||||
import { trackEvent } from '$lib/actions/analytics';
|
||||
import { CardGrid, Heading } from '$lib/components';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { Button, Form, FormList, InputText } from '$lib/elements/forms';
|
||||
@@ -21,11 +22,14 @@
|
||||
$project.$id,
|
||||
$platform.$id,
|
||||
$platform.name,
|
||||
undefined,
|
||||
undefined,
|
||||
$platform.key,
|
||||
$platform.store,
|
||||
hostname
|
||||
);
|
||||
invalidate(Dependencies.PLATFORM);
|
||||
trackEvent('submit_platform_update', {
|
||||
type: 'android'
|
||||
});
|
||||
addNotification({
|
||||
type: 'success',
|
||||
message: 'Platform Package Name has been updated'
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { invalidate } from '$app/navigation';
|
||||
import { trackEvent } from '$lib/actions/analytics';
|
||||
import { CardGrid, Heading } from '$lib/components';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { Button, Form, FormList, InputText } from '$lib/elements/forms';
|
||||
@@ -21,9 +22,14 @@
|
||||
$project.$id,
|
||||
$platform.$id,
|
||||
$platform.name,
|
||||
key
|
||||
key,
|
||||
$platform.store,
|
||||
$platform.hostname
|
||||
);
|
||||
invalidate(Dependencies.PLATFORM);
|
||||
trackEvent('submit_platform_update', {
|
||||
type: 'apple-ios'
|
||||
});
|
||||
addNotification({
|
||||
type: 'success',
|
||||
message: 'Platform Bundle ID has been updated'
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { invalidate } from '$app/navigation';
|
||||
import { trackEvent } from '$lib/actions/analytics';
|
||||
import { CardGrid, Heading } from '$lib/components';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { Button, Form, FormList, InputText } from '$lib/elements/forms';
|
||||
@@ -21,9 +22,14 @@
|
||||
$project.$id,
|
||||
$platform.$id,
|
||||
$platform.name,
|
||||
key
|
||||
key,
|
||||
$platform.store,
|
||||
$platform.hostname
|
||||
);
|
||||
invalidate(Dependencies.PLATFORM);
|
||||
trackEvent('submit_platform_update', {
|
||||
type: 'apple-macos'
|
||||
});
|
||||
addNotification({
|
||||
type: 'success',
|
||||
message: 'Platform Bundle ID has been updated'
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { invalidate } from '$app/navigation';
|
||||
import { trackEvent } from '$lib/actions/analytics';
|
||||
import { CardGrid, Heading } from '$lib/components';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { Button, Form, FormList, InputText } from '$lib/elements/forms';
|
||||
@@ -21,9 +22,14 @@
|
||||
$project.$id,
|
||||
$platform.$id,
|
||||
$platform.name,
|
||||
key
|
||||
key,
|
||||
$platform.store,
|
||||
$platform.hostname
|
||||
);
|
||||
invalidate(Dependencies.PLATFORM);
|
||||
trackEvent('submit_platform_update', {
|
||||
type: 'apple-tvos'
|
||||
});
|
||||
addNotification({
|
||||
type: 'success',
|
||||
message: 'Platform Bundle ID has been updated'
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { invalidate } from '$app/navigation';
|
||||
import { trackEvent } from '$lib/actions/analytics';
|
||||
import { CardGrid, Heading } from '$lib/components';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { Button, Form, FormList, InputText } from '$lib/elements/forms';
|
||||
@@ -21,9 +22,14 @@
|
||||
$project.$id,
|
||||
$platform.$id,
|
||||
$platform.name,
|
||||
key
|
||||
key,
|
||||
$platform.store,
|
||||
$platform.hostname
|
||||
);
|
||||
invalidate(Dependencies.PLATFORM);
|
||||
trackEvent('submit_platform_update', {
|
||||
type: 'apple-watchos'
|
||||
});
|
||||
addNotification({
|
||||
type: 'success',
|
||||
message: 'Platform Bundle ID has been updated'
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { invalidate } from '$app/navigation';
|
||||
import { trackEvent } from '$lib/actions/analytics';
|
||||
import { CardGrid, Heading } from '$lib/components';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { Button, Form, FormList, InputText } from '$lib/elements/forms';
|
||||
@@ -21,9 +22,14 @@
|
||||
$project.$id,
|
||||
$platform.$id,
|
||||
$platform.name,
|
||||
key
|
||||
key,
|
||||
$platform.store,
|
||||
$platform.hostname
|
||||
);
|
||||
invalidate(Dependencies.PLATFORM);
|
||||
trackEvent('submit_platform_update', {
|
||||
type: 'flutter-android'
|
||||
});
|
||||
addNotification({
|
||||
type: 'success',
|
||||
message: 'Platform Package Name has been updated'
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { invalidate } from '$app/navigation';
|
||||
import { trackEvent } from '$lib/actions/analytics';
|
||||
import { CardGrid, Heading } from '$lib/components';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { Button, Form, FormList, InputText } from '$lib/elements/forms';
|
||||
@@ -21,9 +22,14 @@
|
||||
$project.$id,
|
||||
$platform.$id,
|
||||
$platform.name,
|
||||
key
|
||||
key,
|
||||
$platform.store,
|
||||
$platform.hostname
|
||||
);
|
||||
invalidate(Dependencies.PLATFORM);
|
||||
trackEvent('submit_platform_update', {
|
||||
type: 'flutter-ios'
|
||||
});
|
||||
addNotification({
|
||||
type: 'success',
|
||||
message: 'Platform Bundle ID has been updated'
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { invalidate } from '$app/navigation';
|
||||
import { trackEvent } from '$lib/actions/analytics';
|
||||
import { CardGrid, Heading } from '$lib/components';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { Button, Form, FormList, InputText } from '$lib/elements/forms';
|
||||
@@ -21,9 +22,14 @@
|
||||
$project.$id,
|
||||
$platform.$id,
|
||||
$platform.name,
|
||||
key
|
||||
key,
|
||||
$platform.store,
|
||||
$platform.hostname
|
||||
);
|
||||
invalidate(Dependencies.PLATFORM);
|
||||
trackEvent('submit_platform_update', {
|
||||
type: 'flutter-linux'
|
||||
});
|
||||
addNotification({
|
||||
type: 'success',
|
||||
message: 'Platform Package Name has been updated'
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { invalidate } from '$app/navigation';
|
||||
import { trackEvent } from '$lib/actions/analytics';
|
||||
import { CardGrid, Heading } from '$lib/components';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { Button, Form, FormList, InputText } from '$lib/elements/forms';
|
||||
@@ -21,9 +22,14 @@
|
||||
$project.$id,
|
||||
$platform.$id,
|
||||
$platform.name,
|
||||
key
|
||||
key,
|
||||
$platform.store,
|
||||
$platform.hostname
|
||||
);
|
||||
invalidate(Dependencies.PLATFORM);
|
||||
trackEvent('submit_platform_update', {
|
||||
type: 'flutter-macos'
|
||||
});
|
||||
addNotification({
|
||||
type: 'success',
|
||||
message: 'Platform Bundle ID has been updated'
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { invalidate } from '$app/navigation';
|
||||
import { trackEvent } from '$lib/actions/analytics';
|
||||
import { CardGrid, Heading } from '$lib/components';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { Button, Form, FormList, InputText } from '$lib/elements/forms';
|
||||
@@ -21,9 +22,14 @@
|
||||
$project.$id,
|
||||
$platform.$id,
|
||||
$platform.name,
|
||||
key
|
||||
key,
|
||||
$platform.store,
|
||||
$platform.hostname
|
||||
);
|
||||
invalidate(Dependencies.PLATFORM);
|
||||
trackEvent('submit_platform_update', {
|
||||
type: 'flutter-windows'
|
||||
});
|
||||
addNotification({
|
||||
type: 'success',
|
||||
message: 'Platform Package Name has been updated'
|
||||
|
||||
@@ -21,8 +21,8 @@
|
||||
$project.$id,
|
||||
$platform.$id,
|
||||
$platform.name,
|
||||
undefined,
|
||||
undefined,
|
||||
$platform.key,
|
||||
$platform.store,
|
||||
hostname
|
||||
);
|
||||
invalidate(Dependencies.PLATFORM);
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
projectId,
|
||||
$createPlatform.$id,
|
||||
$createPlatform.name,
|
||||
undefined,
|
||||
undefined,
|
||||
$createPlatform.key,
|
||||
$createPlatform.store,
|
||||
$createPlatform.hostname
|
||||
);
|
||||
|
||||
|
||||
@@ -29,8 +29,8 @@
|
||||
projectId,
|
||||
$createPlatform.$id,
|
||||
$createPlatform.name,
|
||||
undefined,
|
||||
undefined,
|
||||
$createPlatform.key,
|
||||
$createPlatform.store,
|
||||
$createPlatform.hostname
|
||||
);
|
||||
|
||||
|
||||
@@ -85,8 +85,8 @@
|
||||
projectId,
|
||||
$createPlatform.$id,
|
||||
$createPlatform.name,
|
||||
undefined,
|
||||
undefined,
|
||||
$createPlatform.key,
|
||||
$createPlatform.store,
|
||||
$createPlatform.hostname
|
||||
);
|
||||
|
||||
|
||||
@@ -8,6 +8,8 @@ function createPlatformStore() {
|
||||
$id: null,
|
||||
name: null,
|
||||
hostname: null,
|
||||
key: null,
|
||||
store: null,
|
||||
type: null
|
||||
});
|
||||
|
||||
@@ -20,6 +22,8 @@ function createPlatformStore() {
|
||||
$id: null,
|
||||
name: null,
|
||||
hostname: null,
|
||||
key: null,
|
||||
store: null,
|
||||
type: null
|
||||
});
|
||||
}
|
||||
|
||||
@@ -21,8 +21,8 @@
|
||||
projectId,
|
||||
$createPlatform.$id,
|
||||
$createPlatform.name,
|
||||
undefined,
|
||||
undefined,
|
||||
$createPlatform.key,
|
||||
$createPlatform.store,
|
||||
$createPlatform.hostname
|
||||
);
|
||||
|
||||
|
||||
@@ -12,8 +12,12 @@
|
||||
|
||||
let method: Method = Method.NPM;
|
||||
|
||||
const example1 = `import { Appwrite } from 'appwrite';`;
|
||||
const example2 = `<script src="https://cdn.jsdelivr.net/npm/appwrite@${$versions['client-web']}" />`;
|
||||
const example1 = `import { Client } from 'appwrite';`;
|
||||
const example2 =
|
||||
`<script src="https://cdn.jsdelivr.net/npm/appwrite@${$versions['client-web']}" />
|
||||
<script>
|
||||
const { Client } = Appwrite;
|
||||
</script` + `>`; // Prevent svelte from processing the closing script tag
|
||||
</script>
|
||||
|
||||
<WizardStep>
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
import { sdkForProject } from '$lib/stores/sdk';
|
||||
|
||||
const { endpoint, project } = sdkForProject.client.config;
|
||||
const code = `const appwrite = new Appwrite();
|
||||
const code = `const client = new Client();
|
||||
|
||||
appwrite
|
||||
client
|
||||
.setEndpoint('${endpoint}')
|
||||
.setProject('${project}');`;
|
||||
</script>
|
||||
|
||||
@@ -55,7 +55,9 @@
|
||||
name,
|
||||
$webhook.events,
|
||||
$webhook.url,
|
||||
$webhook.security
|
||||
$webhook.security,
|
||||
$webhook.httpUser,
|
||||
$webhook.httpPass
|
||||
);
|
||||
invalidate(Dependencies.WEBHOOK);
|
||||
addNotification({
|
||||
@@ -78,7 +80,9 @@
|
||||
$webhook.name,
|
||||
$webhook.events,
|
||||
url,
|
||||
$webhook.security
|
||||
$webhook.security,
|
||||
$webhook.httpUser,
|
||||
$webhook.httpPass
|
||||
);
|
||||
invalidate(Dependencies.WEBHOOK);
|
||||
addNotification({
|
||||
@@ -101,7 +105,9 @@
|
||||
$webhook.name,
|
||||
Array.from($eventSet),
|
||||
$webhook.url,
|
||||
$webhook.security
|
||||
$webhook.security,
|
||||
$webhook.httpUser,
|
||||
$webhook.httpPass
|
||||
);
|
||||
invalidate(Dependencies.WEBHOOK);
|
||||
areEventsDisabled = true;
|
||||
@@ -288,6 +294,7 @@
|
||||
<InputPassword
|
||||
label="Password"
|
||||
id="password"
|
||||
minlength={0}
|
||||
showPasswordButton
|
||||
placeholder="Enter password"
|
||||
bind:value={httpPass} />
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import Delete from '../deleteBucket.svelte';
|
||||
import { trackEvent } from '$lib/actions/analytics';
|
||||
import { writable } from 'svelte/store';
|
||||
import type { Models } from '@aw-labs/appwrite-console';
|
||||
|
||||
let showDelete = false;
|
||||
|
||||
@@ -35,13 +37,14 @@
|
||||
encryption: boolean = null,
|
||||
antivirus: boolean = null,
|
||||
maxSize: number;
|
||||
let byteUnit: 'Bytes' | 'KB' | 'MB' | 'GB' = 'MB',
|
||||
options = [
|
||||
{ label: 'Bytes', value: 'Bytes' },
|
||||
{ label: 'Kilobytes', value: 'KB' },
|
||||
{ label: 'Megabytes', value: 'MB' },
|
||||
{ label: 'Gigabytes', value: 'GB' }
|
||||
];
|
||||
let byteUnit = writable('KB');
|
||||
let sizeInBytes: number = null;
|
||||
const options = [
|
||||
{ label: 'Bytes', value: 'Bytes' },
|
||||
{ label: 'Kilobytes', value: 'KB' },
|
||||
{ label: 'Megabytes', value: 'MB' },
|
||||
{ label: 'Gigabytes', value: 'GB' }
|
||||
];
|
||||
let suggestedExtensions = ['jpg', 'png', 'svg', 'gif', 'html', 'pdf', 'mp4'];
|
||||
let extensions = $bucket.allowedFileExtensions;
|
||||
let isExtensionsDisabled = true;
|
||||
@@ -54,6 +57,7 @@
|
||||
bucketPermissions ??= $bucket.$permissions;
|
||||
encryption ??= $bucket.encryption;
|
||||
antivirus ??= $bucket.antivirus;
|
||||
maxSize = $bucket.maximumFileSize / 1024;
|
||||
});
|
||||
$: if (bucketPermissions) {
|
||||
if (symmetricDifference(bucketPermissions, $bucket.$permissions).length) {
|
||||
@@ -66,78 +70,44 @@
|
||||
} else isExtensionsDisabled = true;
|
||||
}
|
||||
|
||||
async function toggleBucket() {
|
||||
type TUpdateBucketMisc = {
|
||||
successMessage?: string;
|
||||
trackEventName: string;
|
||||
trackEventData?: { value: boolean };
|
||||
arePermsDisabled?: boolean;
|
||||
};
|
||||
|
||||
async function updateBucket(updates: Partial<Models.Bucket>, misc: TUpdateBucketMisc) {
|
||||
const values = { ...$bucket, ...updates };
|
||||
|
||||
try {
|
||||
await sdkForProject.storage.updateBucket(
|
||||
$bucket.$id,
|
||||
$bucket.name,
|
||||
undefined,
|
||||
undefined,
|
||||
enabled
|
||||
values.$id,
|
||||
values.name,
|
||||
values.$permissions,
|
||||
values.fileSecurity,
|
||||
values.enabled,
|
||||
values.maximumFileSize,
|
||||
values.allowedFileExtensions,
|
||||
values.compression,
|
||||
values.encryption,
|
||||
values.antivirus
|
||||
);
|
||||
|
||||
invalidate(Dependencies.BUCKET);
|
||||
|
||||
if (misc.arePermsDisabled !== undefined) {
|
||||
arePermsDisabled = misc.arePermsDisabled;
|
||||
}
|
||||
|
||||
addNotification({
|
||||
message: `${$bucket.name} has been updated`,
|
||||
message: misc.successMessage ?? `${$bucket.name} has been updated`,
|
||||
type: 'success'
|
||||
});
|
||||
trackEvent('submit_bucket_enable', {
|
||||
value: enabled
|
||||
});
|
||||
} catch (error) {
|
||||
addNotification({
|
||||
message: error.message,
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
}
|
||||
async function updateName() {
|
||||
try {
|
||||
await sdkForProject.storage.updateBucket($bucket.$id, bucketName);
|
||||
invalidate(Dependencies.BUCKET);
|
||||
addNotification({
|
||||
message: 'Name has been updated',
|
||||
type: 'success'
|
||||
});
|
||||
trackEvent('submit_bucket_update_name');
|
||||
} catch (error) {
|
||||
addNotification({
|
||||
message: error.message,
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
}
|
||||
async function updatePermissions() {
|
||||
try {
|
||||
await sdkForProject.storage.updateBucket($bucket.$id, $bucket.name, bucketPermissions);
|
||||
invalidate(Dependencies.BUCKET);
|
||||
arePermsDisabled = true;
|
||||
addNotification({
|
||||
message: 'Permissions have been updated',
|
||||
type: 'success'
|
||||
});
|
||||
trackEvent('submit_bucket_update_permissions');
|
||||
} catch (error) {
|
||||
addNotification({
|
||||
message: error.message,
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
}
|
||||
async function updateFileSecurity() {
|
||||
try {
|
||||
await sdkForProject.storage.updateBucket(
|
||||
$bucket.$id,
|
||||
$bucket.name,
|
||||
$bucket.$permissions,
|
||||
bucketFileSecurity
|
||||
);
|
||||
invalidate(Dependencies.BUCKET);
|
||||
arePermsDisabled = true;
|
||||
addNotification({
|
||||
message: 'Security has been updated',
|
||||
type: 'success'
|
||||
});
|
||||
trackEvent('submit_bucket_update_file_security');
|
||||
|
||||
if (misc.trackEventName) {
|
||||
trackEvent(misc.trackEventName, misc.trackEventData);
|
||||
}
|
||||
} catch (error) {
|
||||
addNotification({
|
||||
message: error.message,
|
||||
@@ -146,81 +116,107 @@
|
||||
}
|
||||
}
|
||||
|
||||
async function updateSecurity() {
|
||||
try {
|
||||
await sdkForProject.storage.updateBucket(
|
||||
$bucket.$id,
|
||||
$bucket.name,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
function toggleBucket() {
|
||||
updateBucket(
|
||||
{
|
||||
enabled
|
||||
},
|
||||
{
|
||||
trackEventName: 'submit_bucket_enable',
|
||||
trackEventData: {
|
||||
value: enabled
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function updateName() {
|
||||
updateBucket(
|
||||
{
|
||||
name: bucketName
|
||||
},
|
||||
{
|
||||
successMessage: 'Name has been updated',
|
||||
trackEventName: 'submit_bucket_update_name'
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function updatePermissions() {
|
||||
updateBucket(
|
||||
{
|
||||
$permissions: bucketPermissions
|
||||
},
|
||||
{
|
||||
successMessage: 'Permissions have been updated',
|
||||
trackEventName: 'submit_bucket_update_permissions',
|
||||
arePermsDisabled: true
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function updateFileSecurity() {
|
||||
updateBucket(
|
||||
{
|
||||
fileSecurity: bucketFileSecurity
|
||||
},
|
||||
{
|
||||
successMessage: 'Security has been updated',
|
||||
trackEventName: 'submit_bucket_update_file_security',
|
||||
arePermsDisabled: true
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function updateSecurity() {
|
||||
updateBucket(
|
||||
{
|
||||
encryption,
|
||||
antivirus
|
||||
);
|
||||
invalidate(Dependencies.BUCKET);
|
||||
addNotification({
|
||||
message: `${$bucket.name} has been updated`,
|
||||
type: 'success'
|
||||
});
|
||||
trackEvent('submit_bucket_update_security');
|
||||
} catch (error) {
|
||||
addNotification({
|
||||
message: error.message,
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
}
|
||||
async function updateMaxSize() {
|
||||
let size = sizeToBytes(maxSize, byteUnit);
|
||||
try {
|
||||
await sdkForProject.storage.updateBucket(
|
||||
$bucket.$id,
|
||||
$bucket.name,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
size
|
||||
);
|
||||
invalidate(Dependencies.BUCKET);
|
||||
addNotification({
|
||||
message: `${$bucket.name} has been updated`,
|
||||
type: 'success'
|
||||
});
|
||||
trackEvent('submit_bucket_update_size');
|
||||
} catch (error) {
|
||||
addNotification({
|
||||
message: error.message,
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
},
|
||||
{
|
||||
trackEventName: 'submit_bucket_update_security'
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
async function updateAllowedExtensions() {
|
||||
try {
|
||||
await sdkForProject.storage.updateBucket(
|
||||
$bucket.$id,
|
||||
$bucket.name,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
extensions
|
||||
);
|
||||
invalidate(Dependencies.BUCKET);
|
||||
addNotification({
|
||||
message: `${$bucket.name} has been updated`,
|
||||
type: 'success'
|
||||
});
|
||||
trackEvent('submit_bucket_update_extensions');
|
||||
} catch (error) {
|
||||
addNotification({
|
||||
message: error.message,
|
||||
type: 'error'
|
||||
});
|
||||
function updateMaxSize() {
|
||||
const size = sizeToBytes(maxSize, $byteUnit);
|
||||
updateBucket(
|
||||
{
|
||||
maximumFileSize: size
|
||||
},
|
||||
{
|
||||
trackEventName: 'submit_bucket_update_size'
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function updateAllowedExtensions() {
|
||||
updateBucket(
|
||||
{
|
||||
allowedFileExtensions: extensions
|
||||
},
|
||||
{
|
||||
trackEventName: 'submit_bucket_update_extensions'
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
byteUnit.subscribe((b) => {
|
||||
if (b === 'Bytes') {
|
||||
maxSize = sizeInBytes;
|
||||
} else if (b === 'KB') {
|
||||
maxSize = sizeInBytes / 1024;
|
||||
} else if (b === 'MB') {
|
||||
maxSize = sizeInBytes / 1024 / 1024;
|
||||
} else if (b === 'GB') {
|
||||
maxSize = sizeInBytes / 1024 / 1024 / 1024;
|
||||
}
|
||||
});
|
||||
|
||||
$: if (maxSize) {
|
||||
sizeInBytes = sizeToBytes(maxSize, $byteUnit);
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -384,7 +380,9 @@
|
||||
<Button
|
||||
disabled={encryption === $bucket.encryption &&
|
||||
antivirus === $bucket.antivirus}
|
||||
submit>Update</Button>
|
||||
submit
|
||||
>Update
|
||||
</Button>
|
||||
</svelte:fragment>
|
||||
</CardGrid>
|
||||
</Form>
|
||||
@@ -398,14 +396,16 @@
|
||||
<InputNumber
|
||||
id="size"
|
||||
label="Size"
|
||||
placeholder="256"
|
||||
placeholder={$bucket.maximumFileSize.toString()}
|
||||
bind:value={maxSize} />
|
||||
<InputSelect id="bytes" label="Bytes" {options} bind:value={byteUnit} />
|
||||
<InputSelect id="bytes" label="Bytes" {options} bind:value={$byteUnit} />
|
||||
</ul>
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="actions">
|
||||
<Button disabled={!maxSize} submit>Update</Button>
|
||||
<Button disabled={sizeInBytes === $bucket.maximumFileSize} submit
|
||||
>Update
|
||||
</Button>
|
||||
</svelte:fragment>
|
||||
</CardGrid>
|
||||
</Form>
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M20.5466 17C20.686 16.7584 20.7727 16.4863 20.7727 16.2419V7.75808C20.7727 7.51367 20.6862 7.24157 20.5466 7.00001L11.8864 12L20.5466 17Z" fill="#00599C"/>
|
||||
<path d="M12.6559 21.8167L20.0031 17.5748C20.2147 17.4526 20.4071 17.2416 20.5465 17L11.8863 12L3.22607 17.0001C3.36553 17.2416 3.55795 17.4527 3.76954 17.5748L11.1167 21.8167C11.54 22.0611 12.2326 22.0611 12.6559 21.8167Z" fill="#004482"/>
|
||||
<path d="M20.5466 6.99999C20.4071 6.75838 20.2147 6.54737 20.0031 6.42517L12.6559 2.1833C12.2327 1.9389 11.54 1.9389 11.1168 2.1833L3.76957 6.42517C3.34628 6.66951 3 7.26937 3 7.75812V16.2419C3 16.4863 3.0866 16.7585 3.22611 17L11.8863 12L20.5466 6.99999Z" fill="#659AD2"/>
|
||||
<path d="M11.8864 17.9242C8.61976 17.9242 5.96216 15.2666 5.96216 12C5.96216 8.73336 8.61976 6.07576 11.8864 6.07576C13.9943 6.07576 15.9601 7.21008 17.0167 9.03602L14.4529 10.5196C13.924 9.60567 12.9406 9.03787 11.8864 9.03787C10.253 9.03787 8.92427 10.3666 8.92427 12C8.92427 13.6333 10.253 14.9621 11.8864 14.9621C12.9407 14.9621 13.9241 14.3943 14.453 13.4802L17.0169 14.9637C15.9603 16.7898 13.9944 17.9242 11.8864 17.9242Z" fill="white"/>
|
||||
<path d="M17.8106 11.671H17.1523V11.0128H16.4941V11.671H15.8358V12.3292H16.4941V12.9875H17.1523V12.3292H17.8106V11.671Z" fill="white"/>
|
||||
<path d="M20.2791 11.671H19.6208V11.0128H18.9626V11.671H18.3043V12.3292H18.9626V12.9875H19.6208V12.3292H20.2791V11.671Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg width="21" height="9" viewBox="0 0 21 9" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M1.5367 6.77066C1.44246 6.76892 1.35244 6.789 1.27079 6.82299L1.27077 6.82297L1.26809 6.82415C1.18619 6.86013 1.11066 6.91165 1.04778 6.97763C0.984416 7.04106 0.933719 7.11661 0.900246 7.20081L0.900241 7.2008L0.899816 7.2019C0.867868 7.28413 0.850324 7.37148 0.850116 7.46019M1.5367 6.77066C1.62683 6.77086 1.71694 6.78819 1.80221 6.82566C1.88344 6.86135 1.95839 6.91232 2.02096 6.97752C2.08783 7.04102 2.13744 7.11653 2.17282 7.19799L2.17349 7.19953L2.17348 7.19954C2.20882 7.28349 2.22627 7.37286 2.22627 7.4633C2.22627 7.55374 2.20882 7.64311 2.17348 7.72707C2.13908 7.80877 2.08878 7.88483 2.02167 7.94834C1.95873 8.01224 1.88363 8.06296 1.80079 8.09756L1.74298 7.95915L1.80079 8.09756C1.7175 8.13234 1.62807 8.15013 1.5378 8.1499M1.5367 6.77066C1.53724 6.77067 1.53778 6.77068 1.53832 6.7707L1.53515 6.92066V6.77066C1.53567 6.77066 1.53619 6.77066 1.5367 6.77066ZM1.5378 8.1499C1.44662 8.15149 1.35607 8.13418 1.27193 8.09904C1.1879 8.06394 1.11207 8.01181 1.0493 7.94594C0.985862 7.88244 0.935939 7.80764 0.901383 7.72555C0.866254 7.6421 0.848375 7.55224 0.850116 7.46019M1.5378 8.1499C1.53715 8.1499 1.5365 8.1499 1.53585 8.14989L1.53667 7.99989L1.53973 8.14986C1.53908 8.14988 1.53844 8.14989 1.5378 8.1499ZM0.850116 7.46019C0.850126 7.45965 0.850137 7.45912 0.850148 7.45858L1.00011 7.46179H0.850114C0.850114 7.46126 0.850115 7.46072 0.850116 7.46019ZM4.46673 7.90618V2.91575C4.46994 2.80356 4.47033 2.69143 4.46804 2.57937L7.96337 7.9876L8.0077 8.05618H8.08935H9.08495H9.23495V7.90618V1V0.85H9.08495H8.27631H8.12631V1V5.85487C8.12306 6.00128 8.12426 6.14793 8.12979 6.29447L4.68624 0.919087L4.64198 0.85H4.55993H3.5081H3.3581V1V7.90618V8.05618H3.5081H4.31673H4.46673V7.90618ZM14.7176 7.14829L14.7185 6.99739H14.5676H11.86V4.90464H14.2104H14.3604V4.75464V4.02608V3.87608H14.2104H11.86V1.88158H14.3928H14.5428V1.73158V1V0.85H14.3928H10.8816H10.7316V1V7.90618V8.05618H10.8816H14.563H14.7121L14.713 7.90708L14.7176 7.14829ZM15.1862 0.85H15.0362V1V1.73158V1.88158H15.1862H17.0274V7.90618V8.05618H17.1774H17.986H18.136V7.90618V1.88158H20H20.15V1.73158V1V0.85H20H15.1862Z" fill="white" stroke="white" stroke-width="0.3"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M20.5466 6.99999C20.4071 6.75838 20.2147 6.54737 20.0031 6.42517L12.6559 2.1833C12.2327 1.9389 11.54 1.9389 11.1168 2.1833L3.76957 6.42517C3.34628 6.66951 3 7.26937 3 7.75812V16.2419C3 16.4863 3.0866 16.7585 3.22611 17L3.22607 17C3.36553 17.2416 3.55795 17.4527 3.76954 17.5748L11.1167 21.8167C11.54 22.0611 12.2326 22.0611 12.6559 21.8167L20.0031 17.5748C20.2147 17.4526 20.4071 17.2416 20.5465 17L15.76 14.2365L17.0169 14.9638C15.9603 16.7899 13.9944 17.9243 11.8864 17.9243C8.61976 17.9243 5.96216 15.2667 5.96216 12C5.96216 8.7334 8.61976 6.07581 11.8864 6.07581C13.9943 6.07581 15.9601 7.21012 17.0167 9.03606L14.4529 10.5197C13.924 9.60572 12.9406 9.03792 11.8864 9.03792C10.253 9.03792 8.92427 10.3667 8.92427 12C8.92427 13.6333 10.253 14.9622 11.8864 14.9622C12.9402 14.9622 13.9232 14.3949 14.4523 13.4815L11.8863 12L11.8864 12L14.4523 13.4815L14.453 13.4802L15.7254 14.2165L20.5466 17C20.686 16.7584 20.7727 16.4863 20.7727 16.2419V7.75807C20.7727 7.51367 20.6862 7.24156 20.5466 7L18.6993 8.06657L20.5466 6.99999ZM17.8106 11.6709H17.1523V11.0127H16.4941V11.6709H15.8358V12.3292H16.4941V12.9874H17.1523V12.3292H17.8106V11.6709ZM19.6208 11.0127V11.6709H20.2791V12.3292H19.6208V12.9874H18.9626V12.3292H18.3043V11.6709H18.9626V11.0127H19.6208Z" fill="#C4C6D7"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M2.53673 13.7707C2.44248 13.7689 2.35245 13.789 2.27079 13.823L2.27078 13.823L2.26809 13.8241C2.18619 13.8601 2.11067 13.9117 2.04778 13.9776C1.98442 14.0411 1.93372 14.1166 1.90025 14.2008L1.90024 14.2008L1.89982 14.2019C1.86787 14.2841 1.85032 14.3715 1.85012 14.4602M2.53673 13.7707C2.62685 13.7709 2.71695 13.7882 2.80221 13.8257C2.88344 13.8613 2.9584 13.9123 3.02097 13.9775C3.08783 14.041 3.13744 14.1165 3.17282 14.198L3.17349 14.1995L3.17348 14.1995C3.20882 14.2835 3.22627 14.3729 3.22627 14.4633C3.22627 14.5537 3.20882 14.6431 3.17348 14.7271C3.13908 14.8088 3.08879 14.8848 3.02168 14.9483C2.95873 15.0122 2.88363 15.063 2.80079 15.0976C2.71749 15.1323 2.62805 15.1501 2.53777 15.1499C2.4466 15.1515 2.35607 15.1342 2.27193 15.099C2.1879 15.0639 2.11208 15.0118 2.0493 14.9459C1.98587 14.8824 1.93594 14.8076 1.90138 14.7256C1.86625 14.6421 1.84837 14.5522 1.85012 14.4602M2.53673 13.7707C2.53726 13.7707 2.53779 13.7707 2.53832 13.7707L2.53515 13.9207V13.7707C2.53568 13.7707 2.5362 13.7707 2.53673 13.7707ZM1.85012 14.4602C1.85013 14.4597 1.85014 14.4591 1.85015 14.4586L2.00011 14.4618H1.85011C1.85011 14.4613 1.85011 14.4607 1.85012 14.4602ZM5.46673 14.9062V9.91575C5.46994 9.80356 5.47033 9.69143 5.46804 9.57937L8.96337 14.9876L9.0077 15.0562H9.08935H10.0849H10.2349V14.9062V8V7.85H10.0849H9.27631H9.12631V8V12.8549C9.12306 13.0013 9.12426 13.1479 9.12979 13.2945L5.68624 7.91909L5.64198 7.85H5.55993H4.5081H4.3581V8V14.9062V15.0562H4.5081H5.31673H5.46673V14.9062ZM15.7176 14.1483L15.7185 13.9974H15.5676H12.86V11.9046H15.2104H15.3604V11.7546V11.0261V10.8761H15.2104H12.86V8.88158H15.3928H15.5428V8.73158V8V7.85H15.3928H11.8816H11.7316V8V14.9062V15.0562H11.8816H15.563H15.7121L15.713 14.9071L15.7176 14.1483ZM16.1862 7.85H16.0362V8V8.73158V8.88158H16.1862H18.0274V14.9062V15.0562H18.1774H18.986H19.136V14.9062V8.88158H21H21.15V8.73158V8V7.85H21H16.1862Z" fill="#C4C6D7" stroke="#C4C6D7" stroke-width="0.3"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
@@ -0,0 +1,8 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M20.5466 17C20.686 16.7584 20.7727 16.4863 20.7727 16.2419V7.75807C20.7727 7.51367 20.6862 7.24156 20.5466 7L11.8864 12L20.5466 17Z" fill="#00599C"/>
|
||||
<path d="M12.6559 21.8167L20.0031 17.5748C20.2147 17.4526 20.4071 17.2416 20.5465 17L11.8863 12L3.22607 17C3.36553 17.2416 3.55795 17.4527 3.76954 17.5748L11.1167 21.8167C11.54 22.0611 12.2326 22.0611 12.6559 21.8167Z" fill="#004482"/>
|
||||
<path d="M20.5466 6.99999C20.4071 6.75838 20.2147 6.54737 20.0031 6.42517L12.6559 2.1833C12.2327 1.9389 11.54 1.9389 11.1168 2.1833L3.76957 6.42517C3.34628 6.66951 3 7.26937 3 7.75812V16.2419C3 16.4863 3.0866 16.7585 3.22611 17L11.8863 12L20.5466 6.99999Z" fill="#659AD2"/>
|
||||
<path d="M11.8864 17.9242C8.61976 17.9242 5.96216 15.2666 5.96216 12C5.96216 8.73334 8.61976 6.07574 11.8864 6.07574C13.9943 6.07574 15.9601 7.21006 17.0167 9.036L14.4529 10.5196C13.924 9.60566 12.9406 9.03786 11.8864 9.03786C10.253 9.03786 8.92427 10.3666 8.92427 12C8.92427 13.6333 10.253 14.9621 11.8864 14.9621C12.9407 14.9621 13.9241 14.3943 14.453 13.4802L17.0169 14.9637C15.9603 16.7898 13.9944 17.9242 11.8864 17.9242Z" fill="white"/>
|
||||
<path d="M17.8106 11.671H17.1523V11.0128H16.4941V11.671H15.8358V12.3292H16.4941V12.9875H17.1523V12.3292H17.8106V11.671Z" fill="white"/>
|
||||
<path d="M20.2791 11.671H19.6208V11.0128H18.9626V11.671H18.3043V12.3292H18.9626V12.9875H19.6208V12.3292H20.2791V11.671Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg width="21" height="9" viewBox="0 0 21 9" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M1.5367 6.77066C1.44246 6.76892 1.35244 6.789 1.27079 6.82299L1.27077 6.82297L1.26809 6.82415C1.18619 6.86013 1.11066 6.91165 1.04778 6.97763C0.984416 7.04106 0.933719 7.11661 0.900246 7.20081L0.900241 7.2008L0.899816 7.2019C0.867868 7.28413 0.850324 7.37148 0.850116 7.46019M1.5367 6.77066C1.62683 6.77086 1.71694 6.78819 1.80221 6.82566C1.88344 6.86135 1.95839 6.91232 2.02096 6.97752C2.08783 7.04102 2.13744 7.11653 2.17282 7.19799L2.17349 7.19953L2.17348 7.19954C2.20882 7.28349 2.22627 7.37286 2.22627 7.4633C2.22627 7.55374 2.20882 7.64311 2.17348 7.72707C2.13908 7.80877 2.08878 7.88483 2.02167 7.94834C1.95873 8.01224 1.88363 8.06296 1.80079 8.09756L1.74298 7.95915L1.80079 8.09756C1.7175 8.13234 1.62807 8.15013 1.5378 8.1499M1.5367 6.77066C1.53724 6.77067 1.53778 6.77068 1.53832 6.7707L1.53515 6.92066V6.77066C1.53567 6.77066 1.53619 6.77066 1.5367 6.77066ZM1.5378 8.1499C1.44662 8.15149 1.35607 8.13418 1.27193 8.09904C1.1879 8.06394 1.11207 8.01181 1.0493 7.94594C0.985862 7.88244 0.935939 7.80764 0.901383 7.72555C0.866254 7.6421 0.848375 7.55224 0.850116 7.46019M1.5378 8.1499C1.53715 8.1499 1.5365 8.1499 1.53585 8.14989L1.53667 7.99989L1.53973 8.14986C1.53908 8.14988 1.53844 8.14989 1.5378 8.1499ZM0.850116 7.46019C0.850126 7.45965 0.850137 7.45912 0.850148 7.45858L1.00011 7.46179H0.850114C0.850114 7.46126 0.850115 7.46072 0.850116 7.46019ZM4.46673 7.90618V2.91575C4.46994 2.80356 4.47033 2.69143 4.46804 2.57937L7.96337 7.9876L8.0077 8.05618H8.08935H9.08495H9.23495V7.90618V1V0.85H9.08495H8.27631H8.12631V1V5.85487C8.12306 6.00128 8.12426 6.14793 8.12979 6.29447L4.68624 0.919087L4.64198 0.85H4.55993H3.5081H3.3581V1V7.90618V8.05618H3.5081H4.31673H4.46673V7.90618ZM14.7176 7.14829L14.7185 6.99739H14.5676H11.86V4.90464H14.2104H14.3604V4.75464V4.02608V3.87608H14.2104H11.86V1.88158H14.3928H14.5428V1.73158V1V0.85H14.3928H10.8816H10.7316V1V7.90618V8.05618H10.8816H14.563H14.7121L14.713 7.90708L14.7176 7.14829ZM15.1862 0.85H15.0362V1V1.73158V1.88158H15.1862H17.0274V7.90618V8.05618H17.1774H17.986H18.136V7.90618V1.88158H20H20.15V1.73158V1V0.85H20H15.1862Z" fill="#512BD4" stroke="#512BD4" stroke-width="0.3"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M20.5466 6.99999C20.4071 6.75838 20.2147 6.54737 20.0031 6.42517L12.6559 2.1833C12.2327 1.9389 11.54 1.9389 11.1168 2.1833L3.76957 6.42517C3.34628 6.66951 3 7.26937 3 7.75812V16.2419C3 16.4863 3.0866 16.7585 3.22611 17L3.22607 17C3.36553 17.2416 3.55795 17.4527 3.76954 17.5748L11.1167 21.8167C11.54 22.0611 12.2326 22.0611 12.6559 21.8167L20.0031 17.5748C20.2147 17.4526 20.4071 17.2416 20.5465 17L15.76 14.2365L17.0169 14.9638C15.9603 16.7899 13.9944 17.9243 11.8864 17.9243C8.61976 17.9243 5.96216 15.2667 5.96216 12C5.96216 8.7334 8.61976 6.07581 11.8864 6.07581C13.9943 6.07581 15.9601 7.21012 17.0167 9.03606L14.4529 10.5197C13.924 9.60572 12.9406 9.03792 11.8864 9.03792C10.253 9.03792 8.92427 10.3667 8.92427 12C8.92427 13.6333 10.253 14.9622 11.8864 14.9622C12.9402 14.9622 13.9232 14.3949 14.4523 13.4815L11.8863 12L11.8864 12L14.4523 13.4815L14.453 13.4802L15.7254 14.2165L20.5466 17C20.686 16.7584 20.7727 16.4863 20.7727 16.2419V7.75807C20.7727 7.51367 20.6862 7.24156 20.5466 7L18.6993 8.06657L20.5466 6.99999ZM17.8106 11.6709H17.1523V11.0127H16.4941V11.6709H15.8358V12.3292H16.4941V12.9874H17.1523V12.3292H17.8106V11.6709ZM19.6208 11.0127V11.6709H20.2791V12.3292H19.6208V12.9874H18.9626V12.3292H18.3043V11.6709H18.9626V11.0127H19.6208Z" fill="#373B4D"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M2.53673 13.7707C2.44248 13.7689 2.35245 13.789 2.27079 13.823L2.27078 13.823L2.26809 13.8241C2.18619 13.8601 2.11067 13.9117 2.04778 13.9776C1.98442 14.0411 1.93372 14.1166 1.90025 14.2008L1.90024 14.2008L1.89982 14.2019C1.86787 14.2841 1.85032 14.3715 1.85012 14.4602M2.53673 13.7707C2.62685 13.7709 2.71695 13.7882 2.80221 13.8257C2.88344 13.8613 2.9584 13.9123 3.02097 13.9775C3.08783 14.041 3.13744 14.1165 3.17282 14.198L3.17349 14.1995L3.17348 14.1995C3.20882 14.2835 3.22627 14.3729 3.22627 14.4633C3.22627 14.5537 3.20882 14.6431 3.17348 14.7271C3.13908 14.8088 3.08879 14.8848 3.02168 14.9483C2.95873 15.0122 2.88363 15.063 2.80079 15.0976C2.71749 15.1323 2.62805 15.1501 2.53777 15.1499C2.4466 15.1515 2.35607 15.1342 2.27193 15.099C2.1879 15.0639 2.11208 15.0118 2.0493 14.9459C1.98587 14.8824 1.93594 14.8076 1.90138 14.7256C1.86625 14.6421 1.84837 14.5522 1.85012 14.4602M2.53673 13.7707C2.53726 13.7707 2.53779 13.7707 2.53832 13.7707L2.53515 13.9207V13.7707C2.53568 13.7707 2.5362 13.7707 2.53673 13.7707ZM1.85012 14.4602C1.85013 14.4597 1.85014 14.4591 1.85015 14.4586L2.00011 14.4618H1.85011C1.85011 14.4613 1.85011 14.4607 1.85012 14.4602ZM5.46673 14.9062V9.91575C5.46994 9.80356 5.47033 9.69143 5.46804 9.57937L8.96337 14.9876L9.0077 15.0562H9.08935H10.0849H10.2349V14.9062V8V7.85H10.0849H9.27631H9.12631V8V12.8549C9.12306 13.0013 9.12426 13.1479 9.12979 13.2945L5.68624 7.91909L5.64198 7.85H5.55993H4.5081H4.3581V8V14.9062V15.0562H4.5081H5.31673H5.46673V14.9062ZM15.7176 14.1483L15.7185 13.9974H15.5676H12.86V11.9046H15.2104H15.3604V11.7546V11.0261V10.8761H15.2104H12.86V8.88158H15.3928H15.5428V8.73158V8V7.85H15.3928H11.8816H11.7316V8V14.9062V15.0562H11.8816H15.563H15.7121L15.713 14.9071L15.7176 14.1483ZM16.1862 7.85H16.0362V8V8.73158V8.88158H16.1862H18.0274V14.9062V15.0562H18.1774H18.986H19.136V14.9062V8.88158H21H21.15V8.73158V8V7.85H21H16.1862Z" fill="#373B4D" stroke="#373B4D" stroke-width="0.3"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |