diff --git a/src/lib/actions/tooltip.ts b/src/lib/actions/tooltip.ts index 1636a14ecf..b4dc14a5f1 100644 --- a/src/lib/actions/tooltip.ts +++ b/src/lib/actions/tooltip.ts @@ -1,17 +1,24 @@ import type { Action } from 'svelte/action'; -import type { Props } from 'tippy.js'; +import type { Props as TippyProps } from 'tippy.js'; import tippy from 'tippy.js'; +type Props = TippyProps & { + disabled?: boolean; +}; + export const tooltip: Action> = (node, config) => { const instance = tippy(node, config); + if (config.disabled) instance.disable(); return { - update({ content }) { + update({ content, disabled }) { if (content !== instance.props.content) { instance.setProps({ content }); } + + disabled ? instance.disable() : instance.enable(); }, destroy() { instance.destroy(); diff --git a/src/lib/elements/forms/inputCheckbox.svelte b/src/lib/elements/forms/inputCheckbox.svelte index cecc2a4419..7e2f89ac9e 100644 --- a/src/lib/elements/forms/inputCheckbox.svelte +++ b/src/lib/elements/forms/inputCheckbox.svelte @@ -2,6 +2,7 @@ import { FormItem, Helper } from '.'; export let label: string; + export let optionalText: string | undefined = undefined; export let showLabel = true; export let id: string; export let value = false; @@ -26,7 +27,13 @@ - + +
- + +
- + +
- + +
(obj: T) { + return Object.entries(obj) as Array<[keyof T, T[keyof T]]>; +} diff --git a/src/lib/helpers/string.ts b/src/lib/helpers/string.ts index 564b286091..d479aca053 100644 --- a/src/lib/helpers/string.ts +++ b/src/lib/helpers/string.ts @@ -1,3 +1,14 @@ +/** + * Capitalizes the first letter of a string + * + * @export + * @param {string} str + * @returns {string} + */ +export function capitalize(str: string): string { + return str.charAt(0).toUpperCase() + str.slice(1); +} + /** * Given a string, returns the singular version of it, * by removing the trailing 's'. diff --git a/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/[[page]]/+page.svelte b/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/[[page]]/+page.svelte index f11d120580..9b479eb830 100644 --- a/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/[[page]]/+page.svelte +++ b/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/[[page]]/+page.svelte @@ -19,6 +19,7 @@ import { page } from '$app/stores'; import { PAGE_LIMIT } from '$lib/constants'; import CreateAttribute from '../createAttribute.svelte'; + import { tooltip } from '$lib/actions/tooltip'; export let data: PageData; let showCreateAttribute = false; @@ -33,6 +34,44 @@ title: attribute.key })) ]; + + function formatArray(array: unknown[]) { + if (array.length === 0) return '[ ]'; + + let formattedFields: string[] = []; + for (const item of array) { + if (typeof item === 'string') { + formattedFields.push(`"${item}"`); + } else { + formattedFields.push(`${item}`); + } + } + + return `[${formattedFields.join(', ')}]`; + } + + function formatColumn(column: unknown) { + let formattedColumn: string; + + if (typeof column === 'string') { + formattedColumn = column; + } else if (Array.isArray(column)) { + formattedColumn = formatArray(column); + } else if (!column) { + formattedColumn = 'n/a'; + } else { + formattedColumn = `${column}`; + } + + return { + value: + formattedColumn.length > 20 + ? `${formattedColumn.slice(0, 20)}...` + : formattedColumn, + truncated: formattedColumn.length > 20, + whole: formattedColumn + }; + } @@ -70,8 +109,15 @@ {#each columns as column} + {@const formatted = formatColumn(document[column.key])} - {document[column.key] ?? 'n/a'} +
+ {formatted.value} +
{/each} diff --git a/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/createDocument.svelte b/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/createDocument.svelte index d63d1839ce..92b4d28ae0 100644 --- a/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/createDocument.svelte +++ b/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/createDocument.svelte @@ -23,7 +23,7 @@ $createDocument.attributes = $attributes.filter((a) => a.status === 'available'); $attributes.forEach((attr) => { if (attr.array) { - $createDocument.document[attr.key] = [null]; + $createDocument.document[attr.key] = []; } else { $createDocument.document[attr.key] = null; } @@ -39,12 +39,15 @@ $createDocument.document, $createDocument.permissions ); + addNotification({ message: 'Document has been created', type: 'success' }); trackEvent('submit_document_create'); invalidate(Dependencies.DOCUMENTS); + + createDocument.reset(); wizard.hide(); } catch (error) { addNotification({ diff --git a/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/document-[document]/attribute.svelte b/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/document-[document]/attribute.svelte index 2488a8265f..7da4155315 100644 --- a/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/document-[document]/attribute.svelte +++ b/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/document-[document]/attribute.svelte @@ -9,6 +9,7 @@ export let id: string; export let label: string; + export let optionalText: string | undefined = undefined; export let value: string | number | boolean | null; export let attribute: | Models.AttributeBoolean @@ -44,6 +45,7 @@ {id} {label} {attribute} + {optionalText} bind:value /> {:else} {/if} {/if} diff --git a/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/document-[document]/attributeForm.svelte b/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/document-[document]/attributeForm.svelte new file mode 100644 index 0000000000..f0cee79658 --- /dev/null +++ b/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/document-[document]/attributeForm.svelte @@ -0,0 +1,113 @@ + + +{#if attributes.length} +
    + {#each attributes as attribute} + {@const label = attribute.required ? `${attribute.key}*` : attribute.key} + {#if attribute.array} + {#if formValues[attribute.key].length === 0} +
    + + {label} + + {getAttributeType(attribute)} + + + + +
    + {/if} + + {#if formValues[attribute.key].length !== 0} +
      + {#each [...formValues[attribute.key].keys()] as index} +
    • +
      + +
      +
      + +
      +
    • + {/each} +
    + + {/if} + {:else} + + + + {/if} + {/each} + + {#if customId !== undefined} + {#if !showCustomId} +
    + (showCustomId = !showCustomId)}> + +
    + {:else} + + {/if} + {/if} +
+{/if} diff --git a/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/document-[document]/attributes/boolean.svelte b/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/document-[document]/attributes/boolean.svelte index 2beea6cbc4..dd705b0753 100644 --- a/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/document-[document]/attributes/boolean.svelte +++ b/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/document-[document]/attributes/boolean.svelte @@ -6,12 +6,14 @@ export let label: string; export let value: boolean; export let attribute: Models.AttributeBoolean; + export let optionalText: string | undefined = undefined; {#if attribute?.array} {:else} - + {/if} diff --git a/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/document-[document]/attributes/datetime.svelte b/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/document-[document]/attributes/datetime.svelte index f6ee0dfdbc..f624c4406c 100644 --- a/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/document-[document]/attributes/datetime.svelte +++ b/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/document-[document]/attributes/datetime.svelte @@ -6,6 +6,13 @@ export let label: string; export let value: string; export let attribute: Models.AttributeDatetime; + export let optionalText: string | undefined = undefined; - + diff --git a/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/document-[document]/attributes/enum.svelte b/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/document-[document]/attributes/enum.svelte index 3790fc0d88..0499984d02 100644 --- a/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/document-[document]/attributes/enum.svelte +++ b/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/document-[document]/attributes/enum.svelte @@ -6,6 +6,7 @@ export let label: string; export let value: string; export let attribute: Models.AttributeEnum; + export let optionalText: string | undefined = undefined; $: options = attribute.elements.map((element) => { return { @@ -20,5 +21,6 @@ {options} {id} {label} + {optionalText} placeholder="Select a value" showLabel={!!label?.length} /> diff --git a/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/document-[document]/attributes/integer.svelte b/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/document-[document]/attributes/integer.svelte index b5067c22bb..37c33acf85 100644 --- a/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/document-[document]/attributes/integer.svelte +++ b/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/document-[document]/attributes/integer.svelte @@ -6,11 +6,13 @@ export let label: string; export let value: number; export let attribute: Models.AttributeInteger; + export let optionalText: string | undefined = undefined; - ![ - '$id', - '$collection', - '$collectionId', - '$databaseId', - '$createdAt', - '$updatedAt' - ].includes(key) - ) + .filter((key) => { + return ![ + '$id', + '$collection', + '$collectionId', + '$databaseId', + '$createdAt', + '$updatedAt' + ].includes(key); + }) .reduce((obj, key) => { obj[key] = $doc[key]; return obj; @@ -42,7 +42,7 @@ currentDoc = JSON.stringify($work); }); - $: if (currentDoc && $work) { + $: { if (currentDoc !== JSON.stringify($work)) { disableUpdate = false; } else { @@ -77,90 +77,13 @@ }); } } - - function addArrayItem(key: string) { - work.update((n) => { - if (!Array.isArray(n[key])) { - n[key] = []; - } - n[key].push(null); - - return n; - }); - } - - function removeArrayItem(key: string, index: number) { - work.update((n) => { - n[key].splice(index, 1); - - return n; - }); - } Update Data

Update document data based on the attributes created earlier.

-
- {#each $collection.attributes.filter((a) => a.status === 'available') as attribute} - {#if attribute.array} -
    - {#each [...$work[attribute.key].keys()] as index} -
  • -
    - -
    -
    - -
    -
  • - {:else} -
  • -
    - -
    -
    - -
    -
  • - {/each} -
- - - {:else} -
    - -
- {/if} - {/each} -
+
diff --git a/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/store.ts b/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/store.ts index 6d30597ee4..65c00bd631 100644 --- a/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/store.ts +++ b/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/store.ts @@ -1,6 +1,6 @@ -import { derived } from 'svelte/store'; import { page } from '$app/stores'; import type { Models } from '@aw-labs/appwrite-console'; +import { derived } from 'svelte/store'; export type Attributes = | Models.AttributeBoolean diff --git a/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/wizard/step1.svelte b/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/wizard/step1.svelte index e9da370254..1df88ae0c0 100644 --- a/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/wizard/step1.svelte +++ b/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/wizard/step1.svelte @@ -1,28 +1,7 @@ @@ -31,61 +10,8 @@ Provide document data based on attributes you created earlier. - {#if $createDocument.attributes.length} - - {#each $createDocument.attributes as attribute} - {@const label = attribute.required ? `${attribute.key}*` : attribute.key} - {#if attribute.array} - {#each [...$createDocument.document[attribute.key].keys()] as index} -
  • -
    - -
    -
    - -
    -
  • - {/each} - - {:else} - - - - {/if} - {/each} - {#if !showCustomId} -
    - (showCustomId = !showCustomId)}> - -
    - {:else} - - {/if} -
    - {/if} + diff --git a/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/wizard/store.ts b/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/wizard/store.ts index eea1073072..04cd8ed9f1 100644 --- a/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/wizard/store.ts +++ b/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/wizard/store.ts @@ -1,14 +1,31 @@ import { writable } from 'svelte/store'; import type { Attributes } from '../store'; -export const createDocument = writable<{ +type CreateDocument = { id?: string; document: object; permissions: string[]; attributes: Attributes[]; -}>({ +}; + +const initialCreateDocument: CreateDocument = { id: null, document: {}, permissions: [], attributes: [] -}); +}; + +function createDocumentWritable() { + const store = writable({ ...initialCreateDocument }); + + const reset = () => { + store.set({ ...initialCreateDocument }); + }; + + return { + ...store, + reset + }; +} + +export const createDocument = createDocumentWritable(); diff --git a/tests/unit/elements/inputText.test.ts b/tests/unit/elements/inputText.test.ts index a6d927fb36..c1f67eb3e6 100644 --- a/tests/unit/elements/inputText.test.ts +++ b/tests/unit/elements/inputText.test.ts @@ -72,13 +72,13 @@ test('shows text input - maxlength', () => { }); test('state', async () => { - const { component, getByLabelText } = render(InputText, { + const { component, container } = render(InputText, { id: 'input', label: 'input', value: '' }); - const input = getByLabelText('input'); + const input = container.querySelector('input'); expect(component.value).toEqual(''); await userEvent.type(input, 'lorem'); expect(component.value).toEqual('lorem'); diff --git a/tests/unit/helpers/object.test.ts b/tests/unit/helpers/object.test.ts new file mode 100644 index 0000000000..fe0991de89 --- /dev/null +++ b/tests/unit/helpers/object.test.ts @@ -0,0 +1,11 @@ +import { objectEntries } from '$lib/helpers/object'; + +test('objectEntries', () => { + const object = { + a: 1, + b: 2, + c: 3 + }; + + expect(objectEntries(object)).toEqual(Object.entries(object)); +});