diff --git a/src/lib/components/avatar.svelte b/src/lib/components/avatar.svelte
new file mode 100644
index 0000000000..f688636bdd
--- /dev/null
+++ b/src/lib/components/avatar.svelte
@@ -0,0 +1,14 @@
+
+
+
diff --git a/src/lib/components/index.ts b/src/lib/components/index.ts
index fd1b70c23b..a21de557ae 100644
--- a/src/lib/components/index.ts
+++ b/src/lib/components/index.ts
@@ -13,3 +13,7 @@ export { default as DropList } from './dropList.svelte';
export { default as DropListItem } from './dropListItem.svelte';
export { default as DropListLink } from './dropListLink.svelte';
export { default as Collapsible } from './collapsible.svelte';
+export { default as Avatar } from './avatar.svelte';
+export { default as SwitchBox } from './switchBox.svelte';
+export { default as SwitchBoxes } from './switchBoxes.svelte';
+export { default as InfoSection } from './infoSection.svelte';
diff --git a/src/lib/components/infoSection.svelte b/src/lib/components/infoSection.svelte
new file mode 100644
index 0000000000..8ea26d98ef
--- /dev/null
+++ b/src/lib/components/infoSection.svelte
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/src/lib/components/switchBox.svelte b/src/lib/components/switchBox.svelte
new file mode 100644
index 0000000000..53ef2a1a76
--- /dev/null
+++ b/src/lib/components/switchBox.svelte
@@ -0,0 +1,48 @@
+
+
+
+
+
diff --git a/src/lib/components/switchBoxes.svelte b/src/lib/components/switchBoxes.svelte
new file mode 100644
index 0000000000..9bbec18f9a
--- /dev/null
+++ b/src/lib/components/switchBoxes.svelte
@@ -0,0 +1,13 @@
+
+
+
+ {#each boxes as box}
+ dispatch('updated', e.detail)} />
+ {/each}
+
diff --git a/src/lib/elements/forms/inputPassword.svelte b/src/lib/elements/forms/inputPassword.svelte
index 64f56a2e0a..3fa8bcc9e5 100644
--- a/src/lib/elements/forms/inputPassword.svelte
+++ b/src/lib/elements/forms/inputPassword.svelte
@@ -9,6 +9,7 @@
export let required = false;
export let disabled = false;
export let autofocus = false;
+ export let meter = true;
let element: HTMLInputElement;
@@ -33,14 +34,16 @@
class="input-text"
bind:value
bind:this={element} />
- 100 ? 100 : strength}
- min="0"
- max="100"
- class="password-meter"
- class:is-weak={strength !== 0 && strength <= 33}
- class:is-medium={strength > 33 && strength <= 66}
- class:is-strong={strength > 66 && strength <= 100}
- aria-label="Password strength week" />
+ {#if meter}
+ 100 ? 100 : strength}
+ min="0"
+ max="100"
+ class="password-meter"
+ class:is-weak={strength !== 0 && strength <= 33}
+ class:is-medium={strength > 33 && strength <= 66}
+ class:is-strong={strength > 66 && strength <= 100}
+ aria-label="Password strength weak" />
+ {/if}
diff --git a/src/lib/elements/forms/inputSearch.svelte b/src/lib/elements/forms/inputSearch.svelte
index 5a89508e09..7903ae8cd9 100644
--- a/src/lib/elements/forms/inputSearch.svelte
+++ b/src/lib/elements/forms/inputSearch.svelte
@@ -17,7 +17,7 @@
}
});
- const onKeyUp = (event: KeyboardEvent) => {
+ const valueChange = (event: Event) => {
clearTimeout(timer);
timer = setTimeout(() => {
const target = event.target as HTMLInputElement;
@@ -32,5 +32,5 @@
{required}
type="search"
class="input-text"
- on:keyup={onKeyUp}
+ on:input={valueChange}
bind:this={element} />
diff --git a/src/lib/elements/forms/inputText.svelte b/src/lib/elements/forms/inputText.svelte
index 83ce788900..120f796545 100644
--- a/src/lib/elements/forms/inputText.svelte
+++ b/src/lib/elements/forms/inputText.svelte
@@ -9,6 +9,7 @@
export let required = false;
export let disabled = false;
export let autofocus = false;
+ export let autocomplete = true;
export let maxlength: number = null;
let element: HTMLInputElement;
@@ -29,6 +30,7 @@
{disabled}
{required}
{maxlength}
+ autocomplete={autocomplete ? 'on' : 'off'}
type="text"
class="input-text"
bind:value
diff --git a/src/lib/layout/footer.svelte b/src/lib/layout/footer.svelte
index 41d36bbd64..24579831c4 100644
--- a/src/lib/layout/footer.svelte
+++ b/src/lib/layout/footer.svelte
@@ -4,7 +4,7 @@
- GitHun
+ GitHub
diff --git a/src/lib/layout/header.svelte b/src/lib/layout/header.svelte
index b5209bb120..58f52e668f 100644
--- a/src/lib/layout/header.svelte
+++ b/src/lib/layout/header.svelte
@@ -2,7 +2,7 @@
import { goto } from '$app/navigation';
import { base } from '$app/paths';
import { page } from '$app/stores';
- import { DropList, DropListItem, DropListLink } from '$lib/components';
+ import { DropList, DropListItem, DropListLink, Avatar } from '$lib/components';
import { app } from '$lib/stores/app';
import { sdkForConsole } from '$lib/stores/sdk';
import { user } from '$lib/stores/user';
@@ -46,12 +46,10 @@
Your Account
diff --git a/src/routes/console/[project]/database/__layout.svelte b/src/routes/console/[project]/database/__layout.svelte
index 440d15da66..5a41d46614 100644
--- a/src/routes/console/[project]/database/__layout.svelte
+++ b/src/routes/console/[project]/database/__layout.svelte
@@ -1,6 +1,5 @@
+
+
diff --git a/src/routes/console/[project]/users/_toggleOAuth.svelte b/src/routes/console/[project]/users/_toggleOAuth.svelte
new file mode 100644
index 0000000000..d4a1d0d79d
--- /dev/null
+++ b/src/routes/console/[project]/users/_toggleOAuth.svelte
@@ -0,0 +1,56 @@
+
+
+
diff --git a/src/routes/console/[project]/users/index.svelte b/src/routes/console/[project]/users/index.svelte
index bf5a8447fb..e7048aea68 100644
--- a/src/routes/console/[project]/users/index.svelte
+++ b/src/routes/console/[project]/users/index.svelte
@@ -44,6 +44,7 @@
{:then response}
{#if response.total}
+ {response.total} users found
diff --git a/src/routes/console/[project]/users/settings.svelte b/src/routes/console/[project]/users/settings.svelte
index c270642c71..676a38dd9d 100644
--- a/src/routes/console/[project]/users/settings.svelte
+++ b/src/routes/console/[project]/users/settings.svelte
@@ -1,10 +1,125 @@
-
- Settings
-
-
+
+ {authLimit ? `${authLimit} Users allowed` : 'Unlimited Users '}
+
+
+ Settings
+ Choose auth methods you wish to use.
+
+ OAuth2 Providers
+
+
+{#if provider && showModal}
+
+{/if}
+
+
diff --git a/src/routes/console/[project]/users/teams.svelte b/src/routes/console/[project]/users/teams.svelte
index cd1f34dff3..52dd3f6d9d 100644
--- a/src/routes/console/[project]/users/teams.svelte
+++ b/src/routes/console/[project]/users/teams.svelte
@@ -44,6 +44,7 @@
{:then response}
{#if response.total}
+ {response.total} teams found
diff --git a/tests/unit/components/switchBox.test.ts b/tests/unit/components/switchBox.test.ts
new file mode 100644
index 0000000000..a2f4c538d5
--- /dev/null
+++ b/tests/unit/components/switchBox.test.ts
@@ -0,0 +1,74 @@
+import '@testing-library/jest-dom';
+import { render, fireEvent } from '@testing-library/svelte';
+import { SwitchBox } from '../../../src/lib/components';
+
+const box = {
+ id: 'input',
+ src: 'https://via.placeholder.com/50',
+ label: 'Bool',
+ alt: 'image',
+ required: false,
+ disabled: false,
+ value: false
+};
+
+test('shows boolean input', () => {
+ const { getByText, getByRole, getByAltText } = render(SwitchBox, {
+ box
+ });
+ const checkbox = getByRole('switch');
+ const img = getByAltText('image');
+
+ expect(getByText('Bool')).toBeInTheDocument();
+ expect(img).toBeInTheDocument();
+ expect(checkbox).toBeInTheDocument();
+ expect(checkbox).toHaveAttribute('type', 'checkbox');
+});
+
+test('shows boolean input - required', () => {
+ box.required = true;
+ const { getByRole } = render(SwitchBox, { box });
+
+ expect(getByRole('switch')).toBeRequired();
+});
+
+test('shows boolean input - disabled', () => {
+ box.disabled = true;
+ const { getByRole } = render(SwitchBox, { box });
+
+ expect(getByRole('switch')).toBeDisabled();
+});
+
+test('state', async () => {
+ const { getByRole, component } = render(SwitchBox, { box });
+ const checkbox = getByRole('switch');
+
+ setTimeout(() => {
+ expect(checkbox).not.toBeChecked();
+ expect(component.box.value).toStrictEqual(false);
+ }, 1);
+
+ await fireEvent.click(checkbox);
+ setTimeout(() => {
+ expect(checkbox).toBeChecked();
+ expect(component.box.value).toStrictEqual(true);
+ }, 1);
+
+ await fireEvent.click(checkbox);
+ setTimeout(() => {
+ expect(checkbox).not.toBeChecked();
+ expect(component.box.value).toStrictEqual(false);
+ }, 1);
+
+ component.box.value = true;
+ setTimeout(() => {
+ expect(checkbox).toBeChecked();
+ expect(component.box.value).toStrictEqual(true);
+ }, 1);
+
+ component.box.value = false;
+ setTimeout(() => {
+ expect(checkbox).not.toBeChecked();
+ expect(component.box.value).toStrictEqual(false);
+ }, 1);
+});
diff --git a/tests/unit/elements/inputSearch.test.ts b/tests/unit/elements/inputSearch.test.ts
new file mode 100644
index 0000000000..3b3e061651
--- /dev/null
+++ b/tests/unit/elements/inputSearch.test.ts
@@ -0,0 +1,57 @@
+import '@testing-library/jest-dom';
+import { render } from '@testing-library/svelte';
+import userEvent from '@testing-library/user-event';
+import { InputSearch } from '../../../src/lib/elements/forms';
+
+test('shows search input', () => {
+ const { getByPlaceholderText } = render(InputSearch, { placeholder: 'search' });
+
+ expect(getByPlaceholderText('search')).toBeInTheDocument();
+ expect(getByPlaceholderText('search')).toHaveAttribute('type', 'search');
+});
+
+test('shows input search - required', () => {
+ const { getByPlaceholderText } = render(InputSearch, { placeholder: 'search', required: true });
+
+ expect(getByPlaceholderText('search')).toBeRequired();
+});
+
+test('shows input search - disabled', () => {
+ const { getByPlaceholderText } = render(InputSearch, { placeholder: 'search', disabled: true });
+
+ expect(getByPlaceholderText('search')).toBeDisabled();
+});
+
+test('shows input search - autofocus', () => {
+ const { getByPlaceholderText } = render(InputSearch, {
+ placeholder: 'search',
+ autofocus: true
+ });
+
+ expect(getByPlaceholderText('search')).toHaveFocus();
+});
+
+test('shows input search - placeholder', () => {
+ const { getByPlaceholderText } = render(InputSearch, {
+ placeholder: 'find me'
+ });
+
+ expect(getByPlaceholderText('find me')).toBeInTheDocument();
+});
+
+test('state change after debounce', async () => {
+ const debounce = 250;
+ const { component, getByPlaceholderText } = render(InputSearch, {
+ placeholder: 'search',
+ value: '',
+ debounce
+ });
+ const input = getByPlaceholderText('search');
+
+ expect(component.value).toEqual('');
+ await userEvent.type(input, 'lorem');
+ expect(component.value).not.toEqual('lorem');
+ setTimeout(() => {
+ expect(component.value).toEqual('lorem');
+ }, debounce);
+});