|
1 | 1 | <script lang="ts">
|
2 |
| - import { type Models } from '@appwrite.io/console'; |
3 |
| - import { Alert, Button, Table } from '@appwrite.io/pink-svelte'; |
| 2 | + import { type Models, Query } from '@appwrite.io/console'; |
| 3 | + import { Alert, Button, Skeleton, Table } from '@appwrite.io/pink-svelte'; |
4 | 4 | import { Modal } from '$lib/components';
|
5 |
| - import { onMount } from 'svelte'; |
6 | 5 | import { sdk } from '$lib/stores/sdk';
|
7 | 6 | import { addNotification } from '$lib/stores/notifications';
|
8 | 7 | import { invalidate } from '$app/navigation';
|
9 | 8 | import { Dependencies } from '$lib/constants';
|
10 | 9 | import { billingProjectsLimitDate } from '$lib/stores/billing';
|
11 |
| - import { page } from '$app/state'; |
12 | 10 | import { toLocaleDate, toLocaleDateTime } from '$lib/helpers/date';
|
13 | 11 | import { currentPlan } from '$lib/stores/organization';
|
14 | 12 |
|
15 | 13 | let {
|
16 | 14 | showSelectProject = $bindable(false),
|
17 |
| - selectedProjects = $bindable([]) |
| 15 | + selectedProjects = $bindable([]), |
| 16 | + organizationId |
18 | 17 | }: {
|
19 | 18 | showSelectProject: boolean;
|
20 | 19 | selectedProjects: string[];
|
| 20 | + organizationId: string; |
21 | 21 | } = $props();
|
22 | 22 |
|
23 |
| - let projects = $state<Array<Models.Project>>([]); |
24 |
| - let error = $state<string | null>(null); |
| 23 | + let loading = $state(false); |
| 24 | + let projectsLoadingError = $state<string | null>(null); |
25 | 25 |
|
26 |
| - onMount(() => { |
27 |
| - const currentOrgId = page.data.organization?.$id; |
28 |
| - projects = |
29 |
| - page.data.currentOrgId === currentOrgId ? page.data.allProjects?.projects || [] : []; |
30 |
| - }); |
| 26 | + let error = $state<string | null>(null); |
| 27 | + let projects = $state<Array<Models.Project>>([]); |
31 | 28 |
|
32 | 29 | let projectsToArchive = $derived(
|
33 | 30 | projects.filter((project) => !selectedProjects.includes(project.$id))
|
|
39 | 36 | projects[0].teamId,
|
40 | 37 | selectedProjects
|
41 | 38 | );
|
| 39 | +
|
42 | 40 | showSelectProject = false;
|
43 | 41 | invalidate(Dependencies.ORGANIZATION);
|
44 | 42 | addNotification({
|
|
69 | 67 |
|
70 | 68 | return result;
|
71 | 69 | }
|
| 70 | +
|
| 71 | + $effect(() => { |
| 72 | + if (!showSelectProject) return; |
| 73 | +
|
| 74 | + const areProjectsLoaded = projects.length > 0; |
| 75 | + const teamIdInLoadedProjects = areProjectsLoaded ? projects[0].teamId : null; |
| 76 | +
|
| 77 | + if (organizationId != teamIdInLoadedProjects) { |
| 78 | + loading = true; |
| 79 | +
|
| 80 | + sdk.forConsole.projects |
| 81 | + .list([ |
| 82 | + Query.equal('teamId', organizationId), |
| 83 | + Query.limit(1000) // Get all projects for organization |
| 84 | + ]) |
| 85 | + .then((loadedProjects) => (projects = loadedProjects.projects)) |
| 86 | + .catch((err) => (projectsLoadingError = err.message)) |
| 87 | + .finally(() => (loading = false)); |
| 88 | + } |
| 89 | + }); |
72 | 90 | </script>
|
73 | 91 |
|
74 |
| -<Modal bind:show={showSelectProject} title={'Manage projects'} onSubmit={updateSelected}> |
| 92 | +<Modal bind:show={showSelectProject} title="Manage projects" onSubmit={updateSelected}> |
75 | 93 | <svelte:fragment slot="description">
|
76 | 94 | Choose which {$currentPlan?.projects || 2} projects to keep. Projects over the limit will be
|
77 | 95 | blocked after this date.
|
78 | 96 | </svelte:fragment>
|
79 |
| - {#if error} |
80 |
| - <Alert.Inline status="error" title="Error">{error}</Alert.Inline> |
81 |
| - {/if} |
82 |
| - <Table.Root |
83 |
| - let:root |
84 |
| - allowSelection |
85 |
| - bind:selectedRows={selectedProjects} |
86 |
| - columns={[{ id: 'name' }, { id: 'created' }]}> |
87 |
| - <svelte:fragment slot="header" let:root> |
88 |
| - <Table.Header.Cell column="name" {root}>Project Name</Table.Header.Cell> |
89 |
| - <Table.Header.Cell column="created" {root}>Created</Table.Header.Cell> |
90 |
| - </svelte:fragment> |
91 |
| - {#each projects as project} |
92 |
| - <Table.Row.Base {root} id={project.$id}> |
93 |
| - <Table.Cell column="name" {root}>{project.name}</Table.Cell> |
94 |
| - <Table.Cell column="created" {root} |
95 |
| - >{toLocaleDateTime(project.$createdAt)}</Table.Cell> |
96 |
| - </Table.Row.Base> |
97 |
| - {/each} |
98 |
| - </Table.Root> |
99 |
| - {#if selectedProjects.length > $currentPlan?.projects} |
100 |
| - <div class="u-text-warning u-mb-4"> |
101 |
| - You can only select {$currentPlan?.projects} projects. Please deselect others to continue. |
| 97 | + |
| 98 | + {#if loading} |
| 99 | + <div class="skeleton-projects"> |
| 100 | + <Table.Root let:root allowSelection columns={[{ id: 'name' }, { id: 'created' }]}> |
| 101 | + <svelte:fragment slot="header" let:root> |
| 102 | + <Table.Header.Cell column="name" {root}>Project Name</Table.Header.Cell> |
| 103 | + <Table.Header.Cell column="created" {root}>Created</Table.Header.Cell> |
| 104 | + </svelte:fragment> |
| 105 | + |
| 106 | + {#each Array.from({ length: 5 }) as _} |
| 107 | + <Table.Row.Base {root} select="disabled"> |
| 108 | + <Table.Cell column="name" {root}> |
| 109 | + <Skeleton variant="line" height={20} width="80%" /> |
| 110 | + </Table.Cell> |
| 111 | + <Table.Cell column="created" {root}> |
| 112 | + <Skeleton variant="line" height={20} width="80%" /> |
| 113 | + </Table.Cell> |
| 114 | + </Table.Row.Base> |
| 115 | + {/each} |
| 116 | + </Table.Root> |
102 | 117 | </div>
|
103 |
| - {/if} |
104 |
| - {#if selectedProjects.length === $currentPlan?.projects} |
105 |
| - <Alert.Inline |
106 |
| - status="warning" |
107 |
| - title={`${projects.length - selectedProjects.length} projects will be archived on ${toLocaleDate(billingProjectsLimitDate)}`}> |
108 |
| - <span> |
109 |
| - {@html formatProjectsToArchive()} |
110 |
| - will be archived. |
111 |
| - </span> |
112 |
| - </Alert.Inline> |
| 118 | + {:else if projectsLoadingError} |
| 119 | + <Alert.Inline status="error" title="Error">{projectsLoadingError}</Alert.Inline> |
| 120 | + {:else} |
| 121 | + {#if error} |
| 122 | + <Alert.Inline status="error" title="Error">{error}</Alert.Inline> |
| 123 | + {/if} |
| 124 | + |
| 125 | + <Table.Root |
| 126 | + let:root |
| 127 | + allowSelection |
| 128 | + bind:selectedRows={selectedProjects} |
| 129 | + columns={[{ id: 'name' }, { id: 'created' }]}> |
| 130 | + <svelte:fragment slot="header" let:root> |
| 131 | + <Table.Header.Cell column="name" {root}>Project Name</Table.Header.Cell> |
| 132 | + <Table.Header.Cell column="created" {root}>Created</Table.Header.Cell> |
| 133 | + </svelte:fragment> |
| 134 | + {#each projects as project} |
| 135 | + <Table.Row.Base {root} id={project.$id}> |
| 136 | + <Table.Cell column="name" {root}>{project.name}</Table.Cell> |
| 137 | + <Table.Cell column="created" {root} |
| 138 | + >{toLocaleDateTime(project.$createdAt)}</Table.Cell> |
| 139 | + </Table.Row.Base> |
| 140 | + {/each} |
| 141 | + </Table.Root> |
| 142 | + |
| 143 | + {#if selectedProjects.length > $currentPlan?.projects} |
| 144 | + <div class="u-text-warning u-mb-4"> |
| 145 | + You can only select {$currentPlan?.projects} projects. Please deselect others to continue. |
| 146 | + </div> |
| 147 | + {/if} |
| 148 | + |
| 149 | + {#if selectedProjects.length === $currentPlan?.projects} |
| 150 | + <Alert.Inline |
| 151 | + status="warning" |
| 152 | + title={`${projects.length - selectedProjects.length} projects will be archived on ${toLocaleDate(billingProjectsLimitDate)}`}> |
| 153 | + <span> |
| 154 | + {@html formatProjectsToArchive()} |
| 155 | + will be archived. |
| 156 | + </span> |
| 157 | + </Alert.Inline> |
| 158 | + {/if} |
113 | 159 | {/if}
|
114 | 160 | <svelte:fragment slot="footer">
|
115 | 161 | <Button.Button size="s" variant="secondary" on:click={() => (showSelectProject = false)}
|
|
118 | 164 | >Save</Button.Button>
|
119 | 165 | </svelte:fragment>
|
120 | 166 | </Modal>
|
| 167 | + |
| 168 | +<style lang="scss"> |
| 169 | + /* disable the top select all selector button */ |
| 170 | + .skeleton-projects :global([role='rowheader'] button) { |
| 171 | + opacity: 0.4; |
| 172 | + cursor: default; |
| 173 | + pointer-events: none; |
| 174 | + background: var(--bgcolor-neutral-tertiary); |
| 175 | + } |
| 176 | +</style> |
0 commit comments