mirror of
https://github.com/appwrite/console.git
synced 2026-06-06 19:27:48 +00:00
feat: ask for feedback on second rel submit
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { createPopper, type Instance } from '@popperjs/core';
|
||||
import { onDestroy, onMount } from 'svelte';
|
||||
import { createEventDispatcher, onDestroy, onMount } from 'svelte';
|
||||
|
||||
export let show = false;
|
||||
export let noArrow = false;
|
||||
@@ -14,6 +14,8 @@
|
||||
export let fullWidth = false;
|
||||
export let fixed = false;
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
let element: HTMLDivElement;
|
||||
let tooltip: HTMLDivElement;
|
||||
let arrow: HTMLDivElement;
|
||||
@@ -79,6 +81,7 @@
|
||||
)
|
||||
) {
|
||||
show = false;
|
||||
dispatch('blur');
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { Drop } from '.';
|
||||
import type { Placement } from './drop.svelte';
|
||||
|
||||
@@ -12,9 +13,19 @@
|
||||
export let width: string = null;
|
||||
export let fullWidth = false;
|
||||
export let position: 'relative' | 'static' = 'relative';
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
</script>
|
||||
|
||||
<Drop bind:show {placement} {childStart} {noArrow} {noStyle} {fullWidth} {fixed}>
|
||||
<Drop
|
||||
bind:show
|
||||
{placement}
|
||||
{childStart}
|
||||
{noArrow}
|
||||
{noStyle}
|
||||
{fullWidth}
|
||||
{fixed}
|
||||
on:blur={() => dispatch('blur')}>
|
||||
<slot />
|
||||
<svelte:fragment slot="list">
|
||||
<div
|
||||
|
||||
@@ -10,8 +10,6 @@
|
||||
import { feedback } from '$lib/stores/app';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
|
||||
export let show = false;
|
||||
|
||||
let message: string;
|
||||
let name: string;
|
||||
let email: string;
|
||||
@@ -29,7 +27,7 @@
|
||||
message: error.message
|
||||
});
|
||||
} finally {
|
||||
show = false;
|
||||
feedback.toggleFeedback();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -43,7 +41,7 @@
|
||||
style="--button-size:1.5rem;"
|
||||
aria-label="Close Modal"
|
||||
title="Close Modal"
|
||||
on:click={() => (show = false)}>
|
||||
on:click={() => feedback.toggleFeedback()}>
|
||||
<span class="icon-x" aria-hidden="true" />
|
||||
</button>
|
||||
</header>
|
||||
@@ -64,7 +62,7 @@
|
||||
</FormList>
|
||||
|
||||
<div class="u-flex u-main-end u-gap-16 u-margin-block-start-24">
|
||||
<Button text on:click={() => (show = false)}>Cancel</Button>
|
||||
<Button text on:click={() => feedback.toggleFeedback()}>Cancel</Button>
|
||||
<Button secondary submit>Submit</Button>
|
||||
</div>
|
||||
</Form>
|
||||
|
||||
@@ -11,8 +11,6 @@
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import Evaluation from './evaluation.svelte';
|
||||
|
||||
export let show = false;
|
||||
|
||||
let value: number = null;
|
||||
let message: string;
|
||||
let email: string;
|
||||
@@ -21,12 +19,18 @@
|
||||
try {
|
||||
await feedback.submitFeedback('feedback-nps', message, name, email, value);
|
||||
feedback.switchType('general');
|
||||
addNotification({
|
||||
type: 'success',
|
||||
message: 'Thank you for your feedback!'
|
||||
});
|
||||
} catch (error) {
|
||||
feedback.switchType('general');
|
||||
addNotification({
|
||||
type: 'error',
|
||||
message: error.message
|
||||
});
|
||||
} finally {
|
||||
feedback.toggleFeedback();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -40,7 +44,7 @@
|
||||
style="--button-size:1.5rem;"
|
||||
aria-label="Close Modal"
|
||||
title="Close Modal"
|
||||
on:click={() => (show = false)}>
|
||||
on:click={() => feedback.toggleFeedback()}>
|
||||
<span class="icon-x" aria-hidden="true" />
|
||||
</button>
|
||||
</header>
|
||||
@@ -67,7 +71,7 @@
|
||||
</FormList>
|
||||
{/if}
|
||||
<div class="u-flex u-main-end u-gap-16 u-margin-block-start-24">
|
||||
<Button text on:click={() => (show = false)}>No thanks</Button>
|
||||
<Button text on:click={() => feedback.toggleFeedback()}>No thanks</Button>
|
||||
<Button disabled={!value} secondary submit>Submit</Button>
|
||||
</div>
|
||||
</Form>
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
import SystemMode from '$lib/images/mode/system-mode.svg';
|
||||
import { FeedbackNPS } from '$lib/components';
|
||||
|
||||
let showFeedback = false;
|
||||
import { slide } from 'svelte/transition';
|
||||
import { page } from '$app/stores';
|
||||
import { Submit, trackEvent } from '$lib/actions/analytics';
|
||||
@@ -27,7 +26,7 @@
|
||||
let droplistElement: HTMLDivElement;
|
||||
|
||||
function toggleFeedback() {
|
||||
showFeedback = !showFeedback;
|
||||
feedback.toggleFeedback();
|
||||
if ($feedback.notification) {
|
||||
feedback.toggleNotification();
|
||||
feedback.addVisualization();
|
||||
@@ -73,15 +72,15 @@
|
||||
<div class="pulse-notification" />
|
||||
</div>
|
||||
{/if}
|
||||
<DropList bind:show={showFeedback} scrollable>
|
||||
<DropList show={$feedback.show} scrollable on:blur={toggleFeedback}>
|
||||
<button class="button is-small is-text" on:click={toggleFeedback}>
|
||||
<span class="text">Feedback</span>
|
||||
</button>
|
||||
<svelte:fragment slot="other">
|
||||
{#if $feedback.type === 'nps'}
|
||||
<FeedbackNPS bind:show={showFeedback} />
|
||||
<FeedbackNPS />
|
||||
{:else}
|
||||
<FeedbackGeneral bind:show={showFeedback} />
|
||||
<FeedbackGeneral />
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
</DropList>
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
import type { Notification } from '../stores/notifications';
|
||||
|
||||
export let type: Notification['type'] = 'info';
|
||||
export let icon: Notification['icon'] = null;
|
||||
export let title: Notification['title'];
|
||||
export let buttons: Notification['buttons'];
|
||||
|
||||
@@ -26,10 +27,11 @@
|
||||
</button>
|
||||
<div class="alert-sticky-image">
|
||||
<span
|
||||
class:icon-check-circle={type === 'success'}
|
||||
class:icon-exclamation={type === 'warning'}
|
||||
class:icon-exclamation-circle={type === 'error'}
|
||||
class:icon-check-circle={type === 'success' && !icon}
|
||||
class:icon-exclamation={type === 'warning' && !icon}
|
||||
class:icon-exclamation-circle={type === 'error' && !icon}
|
||||
class:icon-info={type === 'info'}
|
||||
class={icon ? `icon-${icon}` : ''}
|
||||
aria-hidden="true" />
|
||||
</div>
|
||||
<div class="alert-sticky-content">
|
||||
@@ -41,7 +43,9 @@
|
||||
{#if buttons}
|
||||
<div class="alert-sticky-buttons u-flex">
|
||||
{#each buttons as button}
|
||||
<button class="button is-text is-small" on:click={button.method}>
|
||||
<button
|
||||
class="button is-text is-small"
|
||||
on:click|preventDefault|stopPropagation={button.method}>
|
||||
<span class="text">{button.name}</span>
|
||||
</button>
|
||||
{/each}
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
<Notification
|
||||
type={notification.type}
|
||||
title={notification.title}
|
||||
icon={notification.icon}
|
||||
on:dismiss={() => dismissNotification(notification.id)}
|
||||
buttons={notification?.buttons}>
|
||||
{notification.message}
|
||||
|
||||
+10
-2
@@ -12,6 +12,7 @@ export type Feedback = {
|
||||
visualized: number;
|
||||
notification: boolean;
|
||||
type: 'nps' | 'general';
|
||||
show: boolean;
|
||||
};
|
||||
|
||||
export const app = writable<AppStore>({
|
||||
@@ -24,17 +25,24 @@ function createFeedbackStore() {
|
||||
elapsed: browser ? parseInt(localStorage.getItem('feedbackElapsed')) : 0,
|
||||
visualized: browser ? parseInt(localStorage.getItem('feedbackVisualized')) : 0,
|
||||
notification: false,
|
||||
type: 'general'
|
||||
type: 'general',
|
||||
show: false
|
||||
});
|
||||
return {
|
||||
subscribe,
|
||||
update,
|
||||
toggleFeedback: () => {
|
||||
update((feedback) => {
|
||||
feedback.show = !feedback.show;
|
||||
return feedback;
|
||||
});
|
||||
},
|
||||
toggleNotification: () =>
|
||||
update((feedback) => {
|
||||
feedback.notification = !feedback.notification;
|
||||
return feedback;
|
||||
}),
|
||||
switchType: (feedType: 'nps' | 'general') =>
|
||||
switchType: (feedType: Feedback['type']) =>
|
||||
update((feedback) => {
|
||||
feedback.type = feedType;
|
||||
return feedback;
|
||||
|
||||
@@ -7,6 +7,7 @@ export type Notification = {
|
||||
timeout?: number;
|
||||
message: string;
|
||||
title?: string;
|
||||
icon?: string;
|
||||
buttons?: Buttons[];
|
||||
};
|
||||
|
||||
@@ -23,6 +24,10 @@ export const dismissNotification = (id: number) => {
|
||||
notifications.update((all) => all.filter((t) => t.id !== id));
|
||||
};
|
||||
|
||||
export const dismissAllNotifications = () => {
|
||||
notifications.update(() => []);
|
||||
};
|
||||
|
||||
export const addNotification = (notification: Omit<Notification, 'id'>) => {
|
||||
const defaults = {
|
||||
id: counter++,
|
||||
|
||||
@@ -48,7 +48,6 @@ function createPreferences() {
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
getCustomCollectionColumns: (collectionId: string): Preferences['columns'] => {
|
||||
let preferences: PreferencesStore;
|
||||
subscribe((n) => (preferences = n))();
|
||||
@@ -111,7 +110,6 @@ function createPreferences() {
|
||||
|
||||
return n;
|
||||
}),
|
||||
|
||||
loadTeamPrefs: async () => {
|
||||
const id = get(organization)?.$id;
|
||||
if (!id) return {};
|
||||
|
||||
+1
@@ -161,6 +161,7 @@
|
||||
on:click={() => {
|
||||
selectedAttribute = attribute;
|
||||
showDelete = true;
|
||||
showDropdown[index] = false;
|
||||
}}>
|
||||
Delete
|
||||
</DropListItem>
|
||||
|
||||
+39
-4
@@ -5,11 +5,12 @@
|
||||
import { goto, invalidate } from '$app/navigation';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { page } from '$app/stores';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { addNotification, dismissAllNotifications } from '$lib/stores/notifications';
|
||||
import { base } from '$app/paths';
|
||||
import type { Attributes } from './store';
|
||||
import { Submit, trackEvent, trackError } from '$lib/actions/analytics';
|
||||
import { preferences } from '$lib/stores/preferences';
|
||||
import { feedback } from '$lib/stores/app';
|
||||
|
||||
export let showCreate = false;
|
||||
export let selectedOption: string = null;
|
||||
@@ -27,9 +28,10 @@
|
||||
async function submit() {
|
||||
try {
|
||||
await $option.create(databaseId, collectionId, key, data);
|
||||
let selected = preferences.getCustomCollectionColumns(collectionId);
|
||||
selected.push(key ?? data?.key);
|
||||
preferences.setCustomCollectionColumns(selected);
|
||||
|
||||
let selectedColumns = preferences.getCustomCollectionColumns(collectionId);
|
||||
selectedColumns.push(key ?? data?.key);
|
||||
preferences.setCustomCollectionColumns(selectedColumns);
|
||||
await invalidate(Dependencies.COLLECTION);
|
||||
if (!$page.url.pathname.includes('attributes')) {
|
||||
await goto(
|
||||
@@ -40,6 +42,39 @@
|
||||
type: 'success',
|
||||
message: `Attribute ${key ?? data?.key} has been created`
|
||||
});
|
||||
if ($option.type === 'relationship') {
|
||||
let counter = localStorage.getItem('createRelationshipCounter');
|
||||
|
||||
if (counter) {
|
||||
const parsedCounter = parseInt(counter);
|
||||
if (parsedCounter > 2) {
|
||||
return;
|
||||
} else if (parsedCounter === 2) {
|
||||
addNotification({
|
||||
type: 'info',
|
||||
icon: 'question-mark-circle',
|
||||
message: `How is your experience with our new "Relationships" feature? We'd love to hear your feedback!`,
|
||||
timeout: 15000,
|
||||
buttons: [
|
||||
{
|
||||
name: 'Give Feedback',
|
||||
method: () => {
|
||||
feedback.toggleFeedback();
|
||||
dismissAllNotifications();
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
} else {
|
||||
localStorage.setItem(
|
||||
'createRelationshipCounter',
|
||||
(parseInt(counter) + 1).toString()
|
||||
);
|
||||
}
|
||||
} else {
|
||||
localStorage.setItem('createRelationshipCounter', '1');
|
||||
}
|
||||
}
|
||||
showCreate = false;
|
||||
trackEvent(Submit.AttributeCreate);
|
||||
} catch (e) {
|
||||
|
||||
Reference in New Issue
Block a user