mirror of
https://github.com/appwrite/console.git
synced 2026-06-06 19:27:48 +00:00
Project labels UI
This commit is contained in:
+1
-1
@@ -22,7 +22,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@ai-sdk/svelte": "^1.1.24",
|
||||
"@appwrite.io/console": "https://pkg.vc/-/@appwrite/@appwrite.io/console@f21fc7f",
|
||||
"@appwrite.io/console": "https://pkg.vc/-/@appwrite/@appwrite.io/console@5372e56",
|
||||
"@appwrite.io/pink-icons": "0.25.0",
|
||||
"@appwrite.io/pink-icons-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@865e2fc",
|
||||
"@appwrite.io/pink-legacy": "^1.0.3",
|
||||
|
||||
Generated
+6
-6
@@ -12,8 +12,8 @@ importers:
|
||||
specifier: ^1.1.24
|
||||
version: 1.1.24(svelte@5.25.3)(zod@3.24.3)
|
||||
'@appwrite.io/console':
|
||||
specifier: https://pkg.vc/-/@appwrite/@appwrite.io/console@f21fc7f
|
||||
version: https://pkg.vc/-/@appwrite/@appwrite.io/console@f21fc7f
|
||||
specifier: https://pkg.vc/-/@appwrite/@appwrite.io/console@5372e56
|
||||
version: https://pkg.vc/-/@appwrite/@appwrite.io/console@5372e56
|
||||
'@appwrite.io/pink-icons':
|
||||
specifier: 0.25.0
|
||||
version: 0.25.0
|
||||
@@ -272,9 +272,9 @@ packages:
|
||||
'@analytics/type-utils@0.6.2':
|
||||
resolution: {integrity: sha512-TD+xbmsBLyYy/IxFimW/YL/9L2IEnM7/EoV9Aeh56U64Ify8o27HJcKjo38XY9Tcn0uOq1AX3thkKgvtWvwFQg==}
|
||||
|
||||
'@appwrite.io/console@https://pkg.vc/-/@appwrite/@appwrite.io/console@f21fc7f':
|
||||
resolution: {tarball: https://pkg.vc/-/@appwrite/@appwrite.io/console@f21fc7f}
|
||||
version: 1.10.0
|
||||
'@appwrite.io/console@https://pkg.vc/-/@appwrite/@appwrite.io/console@5372e56':
|
||||
resolution: {tarball: https://pkg.vc/-/@appwrite/@appwrite.io/console@5372e56}
|
||||
version: 2.1.0
|
||||
|
||||
'@appwrite.io/pink-icons-svelte@2.0.0-RC.1':
|
||||
resolution: {integrity: sha512-iLFlV55hj8mGuAbmxJGenxN5RaZMmVT4GJb9dv/MP1xBAtYibFq7JvBcxm18qV2KU8c31Rntf+Ub4GL7HwqTYg==}
|
||||
@@ -3823,7 +3823,7 @@ snapshots:
|
||||
|
||||
'@analytics/type-utils@0.6.2': {}
|
||||
|
||||
'@appwrite.io/console@https://pkg.vc/-/@appwrite/@appwrite.io/console@f21fc7f': {}
|
||||
'@appwrite.io/console@https://pkg.vc/-/@appwrite/@appwrite.io/console@5372e56': {}
|
||||
|
||||
'@appwrite.io/pink-icons-svelte@2.0.0-RC.1(svelte@5.25.3)':
|
||||
dependencies:
|
||||
|
||||
@@ -247,6 +247,7 @@ export enum Submit {
|
||||
ProjectDelete = 'submit_project_delete',
|
||||
ProjectUpdateName = 'submit_project_update_name',
|
||||
ProjectUpdateTeam = 'submit_project_update_team',
|
||||
ProjectUpdateLabels = 'submit_project_update_labels',
|
||||
ProjectService = 'submit_project_service',
|
||||
ProjectUpdateSMTP = 'submit_project_update_smtp',
|
||||
MemberCreate = 'submit_member_create',
|
||||
|
||||
@@ -23,6 +23,11 @@ export const load: PageLoad = async ({ params, url, route, depends, parent }) =>
|
||||
const archivedPage =
|
||||
Number.isFinite(archivedPageRaw) && archivedPageRaw > 0 ? archivedPageRaw : 1;
|
||||
const archivedOffset = pageToOffset(archivedPage, limit);
|
||||
|
||||
const searchQueries = search
|
||||
? [Query.or([Query.search('search', search), Query.contains('labels', search)])]
|
||||
: [];
|
||||
|
||||
const [activeProjects, archivedProjects, activeTotal, archivedTotal] = await Promise.all([
|
||||
sdk.forConsole.projects.list({
|
||||
queries: [
|
||||
@@ -30,9 +35,9 @@ export const load: PageLoad = async ({ params, url, route, depends, parent }) =>
|
||||
Query.equal('teamId', params.organization),
|
||||
Query.or([Query.equal('status', 'active'), Query.isNull('status')]),
|
||||
Query.limit(limit),
|
||||
Query.orderDesc('')
|
||||
],
|
||||
search: search || undefined
|
||||
Query.orderDesc(''),
|
||||
...searchQueries
|
||||
]
|
||||
}),
|
||||
sdk.forConsole.projects.list({
|
||||
queries: [
|
||||
@@ -40,23 +45,23 @@ export const load: PageLoad = async ({ params, url, route, depends, parent }) =>
|
||||
Query.equal('teamId', params.organization),
|
||||
Query.equal('status', 'archived'),
|
||||
Query.limit(limit),
|
||||
Query.orderDesc('')
|
||||
],
|
||||
search: search || undefined
|
||||
Query.orderDesc(''),
|
||||
...searchQueries
|
||||
]
|
||||
}),
|
||||
sdk.forConsole.projects.list({
|
||||
queries: [
|
||||
Query.equal('teamId', params.organization),
|
||||
Query.or([Query.equal('status', 'active'), Query.isNull('status')])
|
||||
],
|
||||
search: search || undefined
|
||||
Query.or([Query.equal('status', 'active'), Query.isNull('status')]),
|
||||
...searchQueries
|
||||
]
|
||||
}),
|
||||
sdk.forConsole.projects.list({
|
||||
queries: [
|
||||
Query.equal('teamId', params.organization),
|
||||
Query.equal('status', 'archived')
|
||||
],
|
||||
search: search || undefined
|
||||
Query.equal('status', 'archived'),
|
||||
...searchQueries
|
||||
]
|
||||
})
|
||||
]);
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
import ChangeOrganization from './changeOrganization.svelte';
|
||||
import UpdateVariables from '../updateVariables.svelte';
|
||||
import { page } from '$app/state';
|
||||
import UpdateLabels from './updateLabels.svelte';
|
||||
|
||||
export let data;
|
||||
|
||||
@@ -84,8 +85,9 @@
|
||||
|
||||
<Container>
|
||||
{#if $project}
|
||||
<UpdateName />
|
||||
{#if $canWriteProjects}
|
||||
<UpdateName />
|
||||
<UpdateLabels />
|
||||
<UpdateServices />
|
||||
<UpdateInstallations {...data.installations} limit={data.limit} offset={data.offset} />
|
||||
<UpdateVariables
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import { invalidate } from '$app/navigation';
|
||||
import { Submit, trackEvent, trackError } from '$lib/actions/analytics';
|
||||
import { CardGrid } from '$lib/components';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { Button, Form, InputTags } from '$lib/elements/forms';
|
||||
import { symmetricDifference } from '$lib/helpers/array';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { project } from '../store';
|
||||
import { Tag, Input, Layout, Icon } from '@appwrite.io/pink-svelte';
|
||||
import { IconPlus } from '@appwrite.io/pink-icons-svelte';
|
||||
|
||||
const alphaNumericRegExp = /^[a-zA-Z0-9]+$/;
|
||||
let suggestedLabels = ['live', 'stage', 'internal'];
|
||||
let labels: string[] = [];
|
||||
let error = '';
|
||||
|
||||
onMount(async () => {
|
||||
labels = [...$project.labels];
|
||||
});
|
||||
|
||||
async function updateLabels() {
|
||||
try {
|
||||
await sdk.forConsole.projects.updateLabels({ projectId: $project.$id, labels });
|
||||
await invalidate(Dependencies.PROJECT);
|
||||
await invalidate(Dependencies.ORGANIZATION);
|
||||
isDisabled = true;
|
||||
addNotification({
|
||||
type: 'success',
|
||||
message: 'Project labels have been updated'
|
||||
});
|
||||
trackEvent(Submit.ProjectUpdateLabels);
|
||||
} catch (error) {
|
||||
addNotification({
|
||||
message: error.message,
|
||||
type: 'error'
|
||||
});
|
||||
trackError(error, Submit.ProjectUpdateLabels);
|
||||
}
|
||||
}
|
||||
|
||||
$: isDisabled = !!error || !symmetricDifference(labels, $project.labels).length;
|
||||
|
||||
$: if (labels) {
|
||||
const invalidLabels = [];
|
||||
|
||||
labels.forEach((label) => {
|
||||
if (!alphaNumericRegExp.test(label)) {
|
||||
invalidLabels.push(label);
|
||||
}
|
||||
});
|
||||
|
||||
if (invalidLabels.length) {
|
||||
error = `Invalid labels: ${invalidLabels.join(', ')}`;
|
||||
} else {
|
||||
error = '';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<Form onSubmit={updateLabels}>
|
||||
<CardGrid>
|
||||
<svelte:fragment slot="title">Labels</svelte:fragment>
|
||||
Categorize and manage your projects for easy searching based on specific criteria by assigning
|
||||
them customizable labels.
|
||||
<svelte:fragment slot="aside">
|
||||
<Layout.Stack gap="s">
|
||||
{#key labels.length}
|
||||
<InputTags
|
||||
id="project-labels"
|
||||
label="Labels"
|
||||
placeholder="Select or type project labels"
|
||||
bind:tags={labels} />
|
||||
{/key}
|
||||
<Layout.Stack direction="row">
|
||||
{#each suggestedLabels as suggestedLabel}
|
||||
<Tag
|
||||
size="s"
|
||||
selected={labels.includes(suggestedLabel)}
|
||||
on:click={() => {
|
||||
if (!labels.includes(suggestedLabel)) {
|
||||
labels = [...labels, suggestedLabel];
|
||||
} else {
|
||||
labels = labels.filter((e) => e !== suggestedLabel);
|
||||
}
|
||||
}}>
|
||||
<Icon icon={IconPlus} size="s" slot="start" />
|
||||
{suggestedLabel}
|
||||
</Tag>
|
||||
{/each}
|
||||
</Layout.Stack>
|
||||
<Input.Helper state={error ? 'warning' : 'default'}
|
||||
>{error ? error : 'Only alphanumeric characters are allowed'}</Input.Helper>
|
||||
</Layout.Stack>
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="actions">
|
||||
<Button disabled={isDisabled} submit>Update</Button>
|
||||
</svelte:fragment>
|
||||
</CardGrid>
|
||||
</Form>
|
||||
Reference in New Issue
Block a user