diff --git a/src/lib/components/copyInput.svelte b/src/lib/components/copyInput.svelte index 507d69fcc..782e89c80 100644 --- a/src/lib/components/copyInput.svelte +++ b/src/lib/components/copyInput.svelte @@ -4,6 +4,8 @@ import { addNotification } from '$lib/stores/notifications'; export let value: string; + export let label: string = null; + export let showLabel = false; let content = 'Click to copy'; @@ -21,17 +23,20 @@
- - + +
+ + +
diff --git a/src/lib/components/modal.svelte b/src/lib/components/modal.svelte index 5283ba4f9..97d5e979a 100644 --- a/src/lib/components/modal.svelte +++ b/src/lib/components/modal.svelte @@ -10,6 +10,7 @@ export let error: string = null; export let closable = true; + let alert: HTMLElement; const dispatch = createEventDispatcher(); const transitionFly: FlyParams = { duration: 150, @@ -48,6 +49,10 @@ document.body.classList.remove('u-overflow-hidden'); } } + + $: if (error) { + alert?.scrollIntoView({ behavior: 'smooth', block: 'start', inline: 'nearest' }); + } @@ -81,14 +86,16 @@ diff --git a/src/lib/elements/forms/index.ts b/src/lib/elements/forms/index.ts index 559b59d69..af1ac21e7 100644 --- a/src/lib/elements/forms/index.ts +++ b/src/lib/elements/forms/index.ts @@ -15,6 +15,7 @@ export { default as InputSearch } from './inputSearch.svelte'; export { default as InputRadio } from './inputRadio.svelte'; export { default as InputSelect } from './inputSelect.svelte'; export { default as InputCheckbox } from './inputCheckbox.svelte'; +export { default as InputChoice } from './inputChoice.svelte'; export { default as InputPhone } from './inputPhone.svelte'; export { default as InputChoice } from './inputChoice.svelte'; export { default as Helper } from './helper.svelte'; diff --git a/src/lib/elements/forms/inputChoice.svelte b/src/lib/elements/forms/inputChoice.svelte index 4f1a1319a..be04a386a 100644 --- a/src/lib/elements/forms/inputChoice.svelte +++ b/src/lib/elements/forms/inputChoice.svelte @@ -8,7 +8,6 @@ export let value = false; export let required = false; export let disabled = false; - export let description = ''; diff --git a/src/lib/elements/table/cellText.svelte b/src/lib/elements/table/cellText.svelte index a8a00db42..ca995e627 100644 --- a/src/lib/elements/table/cellText.svelte +++ b/src/lib/elements/table/cellText.svelte @@ -1,8 +1,9 @@
- +
diff --git a/src/lib/layout/header.svelte b/src/lib/layout/header.svelte index bd0dfc233..b4d0dabff 100644 --- a/src/lib/layout/header.svelte +++ b/src/lib/layout/header.svelte @@ -86,7 +86,9 @@ showDropdown = false; newOrgModal.set(true); }}>New organization - Your Account + (showDropdown = false)}>Your Account
diff --git a/src/lib/layout/shell.svelte b/src/lib/layout/shell.svelte index b0a782cd3..e839fa80d 100644 --- a/src/lib/layout/shell.svelte +++ b/src/lib/layout/shell.svelte @@ -13,6 +13,8 @@ } from '$lib/stores/organization'; import { sdkForConsole } from '$lib/stores/sdk'; import { base } from '$app/paths'; + import { user } from '$lib/stores/user'; + import { goto } from '$app/navigation'; import { browser } from '$app/env'; export let isOpen = false; @@ -57,6 +59,11 @@ } }); + const logout = async () => { + await user.logout(); + await goto(`${base}/login`); + }; + const onScroll = () => { if (!tabsList) { return; @@ -174,6 +181,12 @@ {$title} {/if} + {#if $page.url.pathname.includes('/console/account')} +
+ +
+ {/if} + {#if $copyData?.value} diff --git a/src/lib/layout/unauthenticated.svelte b/src/lib/layout/unauthenticated.svelte index 564fe2567..18a634b3f 100644 --- a/src/lib/layout/unauthenticated.svelte +++ b/src/lib/layout/unauthenticated.svelte @@ -3,6 +3,7 @@ import LoginLight from '$lib/images/login/login-light-mode.svg'; import LoginDark from '$lib/images/login/login-dark-mode.svg'; import { app } from '$lib/stores/app'; + import { base } from '$app/paths'; const technologies = [ 'js', @@ -31,8 +32,9 @@
+
{#if $app.themeInUse === 'dark'} @@ -43,16 +45,18 @@
+

Integrate with your favourite technologies

    + class="u-flex u-main-center u-flex-wrap u-gap-16 u-margin-block-start-32 u-line-height-1 u-opacity-0-5"> {#each technologies as tech}
  • -
  • @@ -62,7 +66,7 @@
-
+
diff --git a/src/lib/stores/project-services.ts b/src/lib/stores/project-services.ts new file mode 100644 index 000000000..6f4a8cb0d --- /dev/null +++ b/src/lib/stores/project-services.ts @@ -0,0 +1,117 @@ +import { writable } from 'svelte/store'; +import type { Models } from '@aw-labs/appwrite-console'; + +export type Service = { + label: string; + method: string; + value: boolean | null; +}; + +function createServices() { + const { subscribe, set } = writable({ + list: [ + { + label: 'Account', + method: 'account', + value: null + }, + { + label: 'Avatars', + method: 'avatars', + value: null + }, + { + label: 'Databases', + method: 'databases', + value: null + }, + { + label: 'Functions', + method: 'functions', + value: null + }, + { + label: 'Health', + method: 'health', + value: null + }, + { + label: 'Locale', + method: 'locale', + value: null + }, + { + label: 'Storage', + method: 'storage', + value: null + }, + { + label: 'Teams', + method: 'teams', + value: null + }, + { + label: 'Users', + method: 'users', + value: null + } + ] + }); + + return { + subscribe, + set, + load: (project: Models.Project) => { + const list = [ + { + label: 'Account', + method: 'account', + value: project.serviceStatusForAccount + }, + { + label: 'Avatars', + method: 'avatars', + value: project.serviceStatusForAvatars + }, + { + label: 'Databases', + method: 'databases', + value: project.serviceStatusForDatabases + }, + { + label: 'Functions', + method: 'functions', + value: project.serviceStatusForFunctions + }, + { + label: 'Health', + method: 'health', + value: project.serviceStatusForHealth + }, + { + label: 'Locale', + method: 'locale', + value: project.serviceStatusForLocale + }, + { + label: 'Storage', + method: 'storage', + value: project.serviceStatusForStorage + }, + { + label: 'Teams', + method: 'teams', + value: project.serviceStatusForTeams + }, + { + label: 'Users', + method: 'users', + value: project.serviceStatusForUsers + } + ]; + set({ list }); + } + }; +} + +export const services = createServices(); diff --git a/src/lib/stores/user.ts b/src/lib/stores/user.ts index fe9993066..12a84121b 100644 --- a/src/lib/stores/user.ts +++ b/src/lib/stores/user.ts @@ -10,6 +10,9 @@ function createUserStore() { set, fetchUser: async () => { set(await sdkForConsole.account.get()); + }, + logout: async () => { + await sdkForConsole.account.deleteSession('current'); } }; } diff --git a/src/routes/__layout.svelte b/src/routes/__layout.svelte index 71975c517..2b2f30128 100644 --- a/src/routes/__layout.svelte +++ b/src/routes/__layout.svelte @@ -23,6 +23,8 @@ window.GOOGLE_ANALYTICS = import.meta.env.VITE_GOOGLE_ANALYTICS?.toString() ?? false; } + const acceptedRoutes = ['/login', '/register', '/recover', '/invite']; + onMount(async () => { try { if (!$user) { @@ -33,7 +35,9 @@ await redirectTo(); } } catch (error) { - await goto(`${base}/login`); + if (acceptedRoutes.includes($page.url.pathname)) { + await goto(`${base}${$page.url.pathname}${$page.url.search}`); + } else await goto(`${base}/login`); } finally { loaded = true; } diff --git a/src/routes/console/$me.svelte b/src/routes/console/$me.svelte deleted file mode 100644 index 732f4edb7..000000000 --- a/src/routes/console/$me.svelte +++ /dev/null @@ -1,20 +0,0 @@ - - -

hey

- - diff --git a/src/routes/console/__layout.svelte b/src/routes/console/__layout.svelte index e772abdce..d31f50a6b 100644 --- a/src/routes/console/__layout.svelte +++ b/src/routes/console/__layout.svelte @@ -36,7 +36,10 @@ Appwrite - Console - +
diff --git a/src/routes/console/account/__layout.svelte b/src/routes/console/account/__layout.svelte new file mode 100644 index 000000000..d6b6d2ab4 --- /dev/null +++ b/src/routes/console/account/__layout.svelte @@ -0,0 +1,51 @@ + + + + Appwrite - User + + + diff --git a/src/routes/console/account/_delete.svelte b/src/routes/console/account/_delete.svelte new file mode 100644 index 000000000..f64b36636 --- /dev/null +++ b/src/routes/console/account/_delete.svelte @@ -0,0 +1,36 @@ + + +
+ + Delete Account +

Are you sure you want to delete your account?

+ + + + +
+
diff --git a/src/routes/console/account/activity.svelte b/src/routes/console/account/activity.svelte new file mode 100644 index 000000000..5f00d11e2 --- /dev/null +++ b/src/routes/console/account/activity.svelte @@ -0,0 +1,106 @@ + + + + {#await request} +
+ {:then response} + {#if response.total} + + + Date + Event + Client + Location + IP + + + {#each response.logs as log} + + {toLocaleDateTime(log.time)} + {log.event} + + + {#if log.clientName} +
+
+ {log.clientName} +
+

+ {log.clientName} + {log.clientVersion} + on {log.osName} + {log.osVersion} +

+
+ {:else} +
+ +

Unknown

+
+ {/if} +
+ + + {#if log.countryCode !== '--'} + {log.countryName} + {:else} + Unknown + {/if} + + {log.ip} +
+ {/each} +
+
+ {:else} + +
+
+

No logs available

+
+
+ +
+
+
+ {/if} +
+

Total results: {response.total}

+ +
+ {/await} + diff --git a/src/routes/console/account/index.svelte b/src/routes/console/account/index.svelte new file mode 100644 index 000000000..df6fadaa2 --- /dev/null +++ b/src/routes/console/account/index.svelte @@ -0,0 +1,187 @@ + + + +
+ +
Update Name
+ + +
    + +
+
+ + + + +
+
+
+ +
Update Email
+ + + + + {#if email !== $user.email && email} + + {/if} + + + + + + +
+
+
+ +
Update Password
+

+ Forgot your password? Recover your password +

+ + + + + + + + + + + +
+
+ +
+
Delete Account
+
+

+ Your account will be permanently deleted and access will be lost to any of your teams + and data. This action is irreversible. +

+ + + + + + +
{$user.name}
+
+
+
+ + + + +
+
+ + diff --git a/src/routes/console/account/organizations.svelte b/src/routes/console/account/organizations.svelte new file mode 100644 index 000000000..3005a8d47 --- /dev/null +++ b/src/routes/console/account/organizations.svelte @@ -0,0 +1,110 @@ + + + +
+

Organizations

+ + +
+ + {#if $organizationList?.teams?.length} +
    3 ? '22rem' : '25rem' + };`}> + {#each $organizationList.teams as organization, index} + {@const avatarList = getMemberships(organization.$id)} + {#if index >= offset && index < limit + offset} + + {organization?.total ? organization?.total : 'No'} projects + + {organization.name} + + {#await avatarList} + + {:then avatars} + + {/await} + + {/if} + {/each} + {#if $organizationList?.total < limit + offset && ($organizationList?.total % 2 !== 0 || $organizationList?.total % 4 === 0)} + (addOrganization = true)}> +
    + +
    +
    +

    Create a new organization

    +
    +
    + {/if} +
+ +
+

Total results: {$organizationList?.total}

+ +
+ {:else} + +
+
+
+ +
+
+

Create a new organization

+
+
+
+
+
+

Total results: {$organizationList?.total}

+ +
+ {/if} +
+ + diff --git a/src/routes/console/account/sessions.svelte b/src/routes/console/account/sessions.svelte new file mode 100644 index 000000000..6bc5c6eca --- /dev/null +++ b/src/routes/console/account/sessions.svelte @@ -0,0 +1,124 @@ + + + +
+

Sessions

+ + +
+ {#await request} +
+ {:then response} + {#if response.total} + + + Client + Location + IP + + + + {#each response.sessions as session} + + +
+
+ {#if session.clientName} +
+ {session.clientName} +
+

+ {session.clientName} + {session.clientVersion} + on {session.osName} + {session.osVersion} +

+ {:else} + +

Unknown

+ {/if} +
+ + {session.provider} + {#if session.current} + current session + {/if} +
+
+ + {#if session.countryCode !== '--'} + {session.countryName} + {:else} + Unknown + {/if} + + {session.ip} + + + +
+ {/each} +
+
+ {:else} + +
+
+

No sessions available

+
+
+ +
+
+
+ {/if} +
+

Total results: {response.total}

+ +
+ {/await} + diff --git a/src/routes/console/organization-[organization]/_createMember.svelte b/src/routes/console/organization-[organization]/_createMember.svelte index 237c167ca..b3b2f67c5 100644 --- a/src/routes/console/organization-[organization]/_createMember.svelte +++ b/src/routes/console/organization-[organization]/_createMember.svelte @@ -14,7 +14,7 @@ const dispatch = createEventDispatcher(); let email: string, name: string, error: string; - const url = `${$page.url.origin}/console/`; + const url = `${$page.url.origin}/invite`; const create = async () => { try { diff --git a/src/routes/console/organization-[organization]/_deleteMember.svelte b/src/routes/console/organization-[organization]/_deleteMember.svelte index 215cee0a8..8a7ba31f5 100644 --- a/src/routes/console/organization-[organization]/_deleteMember.svelte +++ b/src/routes/console/organization-[organization]/_deleteMember.svelte @@ -1,4 +1,6 @@ + +
+ + Delete Project +

+ This project will be deleted, along with all of its metadata, stats, and other + resources. This action is irreversible. +

+ + + + + + + + + +
+
diff --git a/src/routes/console/project-[project]/settings/index.svelte b/src/routes/console/project-[project]/settings/index.svelte index d2fb3e02f..663857030 100644 --- a/src/routes/console/project-[project]/settings/index.svelte +++ b/src/routes/console/project-[project]/settings/index.svelte @@ -1,15 +1,34 @@ -

Overview

{#if $project} - -
- - - - - -
+
+ +
Update Name
+ + + + + + + + + + +
+
+ + +
API Credentials
+

+ Access Appwrite services using your API Endpoint and Project ID. You can connect + Appwrite to your applications and server-side code by integrating a new platform + or + creating an API key. +

+ + + + + + +
+ + +
Services
+

Choose services you wish to enable or disable.

+ + +
+
    + {#each $services.list as service} + { + serviceUpdate(service); + }} /> + {/each} +
+
+
+
+
+ + +
+
Delete Project
+
+

+ The project will be permanently deleted, including all the metadata, resources and + stats within it. This action is irreversible. +

+ + + +
{$project.name}
+
+

Last update: {toLocaleDateTime($project.$updatedAt)}

+
+
+ + + + +
{/if}
+ + diff --git a/src/routes/console/project-[project]/settings/members.svelte b/src/routes/console/project-[project]/settings/members.svelte deleted file mode 100644 index da6770b55..000000000 --- a/src/routes/console/project-[project]/settings/members.svelte +++ /dev/null @@ -1,50 +0,0 @@ - - - -

Members

- {#await request} -
- {:then response} - {#if response} - - {#each response.memberships as membership} - - -

- - {membership.userName} - -

- {#each membership.roles as role} - {role} - {/each} - {#if !membership.confirm} - Pending Approval - {/if} -
- - {membership.userEmail} - - - - -
- {:else} -

No sessions available.

- {/each} -
- {/if} - {/await} - diff --git a/src/routes/invite.svelte b/src/routes/invite.svelte new file mode 100644 index 000000000..374ebc914 --- /dev/null +++ b/src/routes/invite.svelte @@ -0,0 +1,76 @@ + + + + Appwrite - Accept invite + + + + Invite + + {#if !userId || !secret || !membershipId || !teamId} +

Invalid invite link.

+ {:else} +

You have been invited to join a team project on Appwrite

+
+ + + By accepting the invitation, you agree to the Terms and Conditions + and + + Privacy Policy. + +
+ + +
+
+
+ {/if} +
+