Skip to content

Commit

Permalink
Merge pull request #176 from santiment/feat/notifications-implementat…
Browse files Browse the repository at this point in the history
…ion-using-svelte-5-san-webkit-next

feat(notifications): added notifications
  • Loading branch information
DmitriVanGuard authored Sep 25, 2024
2 parents c8dd70d + 5dd9343 commit 1bf62c7
Show file tree
Hide file tree
Showing 8 changed files with 185 additions and 27 deletions.
4 changes: 2 additions & 2 deletions src/lib/flow/logout/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { goto } from '$app/navigation'
import { Query } from '$lib/api/executor.js'
import { ApiMutation } from '$lib/api/index.js'
import { useCustomerCtx } from '$lib/ctx/customer/index.svelte.js'
import { notifcation } from '$ui/core/Notifications/index.js'
import { notification } from '$ui/core/Notifications/index.js'

const mutateLogout = ApiMutation(
() => `mutation {
Expand All @@ -21,7 +21,7 @@ export function useLogoutFlow() {
.then(() => {
customer.reload()

notifcation.info("You've been logged out")
notification.info("You've been logged out")
})
}

Expand Down
2 changes: 1 addition & 1 deletion src/lib/ui/app/InsightCard/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export { default } from './InsightCard.svelte';
export { default } from './InsightCard.svelte'
6 changes: 3 additions & 3 deletions src/lib/ui/app/PaymentForm/flow.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Query } from '$lib/api/executor.js'
import { useStripeCtx } from '$lib/ctx/stripe/index.js'
import { notifcation as notification } from '$ui/core/Notifications/index.js'
import { notification } from '$ui/core/Notifications/index.js'
import type { ConfirmCardSetupData, SetupIntent, Stripe, Token } from '@stripe/stripe-js'
import { mutateSubscribe } from './api.js'
import { usePaymentFormCtx } from './state.js'
Expand Down Expand Up @@ -160,7 +160,7 @@ export function usePaymentFlow() {

if (error) {
notification.error(`Error during the payment`, {
description: error.message || 'Please try again or contact our support',
content: error.message || 'Please try again or contact our support',
duration: 10000,
})
trackEvent('payment_fail', { ...analytics, error_code: error.code || '3ds_error' })
Expand Down Expand Up @@ -212,7 +212,7 @@ export function usePaymentFlow() {
})
.catch((error) => {
notification.error(`Error during the payment`, {
description: 'Please try again or contact our support',
content: 'Please try again or contact our support',
})
trackEvent('payment_fail', { ...analytics, error_code: 'api_error' })

Expand Down
2 changes: 1 addition & 1 deletion src/lib/ui/app/PlanChangeDialog/PlanChangeDialog.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import { trackEvent } from '$lib/analytics/index.js'
import Dialog, { dialogs$, type TDialogProps } from '$ui/core/Dialog/index.js'
import Button from '$ui/core/Button/index.js'
import { notifcation as notification } from '$ui/core/Notifications/index.js'
import { notification } from '$ui/core/Notifications/index.js'
import { useCustomerCtx } from '$lib/ctx/customer/index.svelte.js'
import { Query } from '$lib/api/executor.js'
import { getFormattedMonthDayYear } from '$lib/utils/dates.js'
Expand Down
66 changes: 66 additions & 0 deletions src/lib/ui/core/Notifications/Notification.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<script lang="ts">
import { createEventDispatcher, type Snippet } from 'svelte'
import Button from '$ui/core/Button/index.js'
import Svg from '$ui/core/Svg/index.js'
import { cn } from '$ui/utils/index.js'
type Props = {
icon: 'info' | 'checkmark-circle' | 'warning' | 'error'
message: string
content?: string | Snippet
action?: { label: string; onClick: (event: MouseEvent) => void }
class?: string
}
const { icon, message, content, action, class: className }: Props = $props()
const dispatch = createEventDispatcher()
const ICONS = {
info: { class: 'fill-waterloo' },
'checkmark-circle': { class: 'fill-green' },
warning: { class: 'fill-orange', h: 14 },
error: { class: 'fill-red' },
}
</script>

<section
role="alert"
class={cn(
'flex w-[460px] max-w-full gap-4 rounded-lg border bg-white pl-6 pr-2.5 pt-5 shadow',
content && !action ? 'pb-6' : 'pb-5',
className,
)}
>
<figure class="flex h-6 w-4 center">
<Svg id={icon} {...ICONS[icon]} />
</figure>

<div class="flex-1 items-start gap-2 column">
<h4 class="text-base font-medium text-rhino">{message}</h4>

{#if content}
<p class="text-base text-fiord">
{#if typeof content === 'function'}
{@render content()}
{:else}
{content}
{/if}
</p>
{/if}

{#if action}
<Button variant="fill" class="mt-1" onclick={action.onClick}>
{action.label}
</Button>
{/if}
</div>

<Button
aria-label="Close notification"
icon="close"
iconSize={10}
class="-ml-2 -mt-2.5 flex size-5 rounded !fill-waterloo center hover:bg-porcelain xs:size-8"
onclick={() => dispatch('closeToast')}
/>
</section>
15 changes: 5 additions & 10 deletions src/lib/ui/core/Notifications/Notifications.svelte
Original file line number Diff line number Diff line change
@@ -1,19 +1,14 @@
<script lang="ts">
import { BROWSER } from 'esm-env'
import { Toaster } from 'svelte-sonner'
import { useDeviceCtx } from '$lib/ctx/device/index.svelte.js'
const { device } = useDeviceCtx()
</script>

{#if BROWSER}
<Toaster
position="bottom-left"
toastOptions={{
unstyled: true,
classes: {
toast: 'border rounded shadow bg-white row p-4 gap-2',
title: 'text-black font-medium',
description: 'text-waterloo',
success: 'fill-green',
},
}}
position={device.$.isMobile ? 'top-center' : 'bottom-left'}
toastOptions={{ unstyled: true }}
></Toaster>
{/if}
32 changes: 31 additions & 1 deletion src/lib/ui/core/Notifications/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,32 @@
import type { ComponentProps, ComponentType } from 'svelte'
import { toast } from 'svelte-sonner'
import Component from './Notification.svelte'

export { default } from './Notifications.svelte'
export { toast as notifcation } from 'svelte-sonner'

type TOptions = Omit<ComponentProps<Component>, 'icon' | 'message'> & { duration?: number }
type TIcon = ComponentProps<Component>['icon']

function createToast(icon: TIcon, message: string, { duration = 5000, ...options }: TOptions = {}) {
return toast.custom(Component as unknown as ComponentType, {
duration,
componentProps: { ...options, icon, message },
})
}

const createNotificationType =
(icon: TIcon) =>
(...rest: [message: string, options?: TOptions]) =>
createToast(icon, ...rest)

const notification: Record<
'info' | 'error' | 'warning' | 'success',
(message: string, options?: TOptions) => string | number
> = {
info: createNotificationType('info'),
error: createNotificationType('error'),
warning: createNotificationType('warning'),
success: createNotificationType('checkmark-circle'),
}

export { notification }
85 changes: 76 additions & 9 deletions src/stories/Design System - Core UI/Notifications/index.svelte
Original file line number Diff line number Diff line change
@@ -1,21 +1,88 @@
<script>
import Button from '$ui/core/Button/index'
import { notifcation } from '$ui/core/Notifications/index'
import Button from '$ui/core/Button'
import { notification } from '$ui/core/Notifications'
import Notification from '$ui/core/Notifications/Notification.svelte'
</script>

<div class="flex flex-col justify-center divide-y p-6">
<div class="flex gap-4">
<Button variant="border" onclick={() => notifcation('Event has been created')}>Info</Button>
{#snippet content()}
We will check you insight and <a href="about:blank" target="_blank" class="text-green">
publish it very soon
</a>
{/snippet}

<div class="gap-4 p-3 column">
<Notification icon="info" message="Info notification"></Notification>
<Notification icon="checkmark-circle" message="Success notification"></Notification>
<Notification icon="warning" message="Warning notification"></Notification>
<Notification icon="error" message="Error notification"></Notification>

<Notification
icon="checkmark-circle"
message="Long message: Lorem ipsum dolor, sit amet consectetur adipisicing elit. Tenetur, recusandae doloribus! Iure praesentium"
></Notification>
</div>

<div class="flex-coljustify-center flex divide-y p-6">
<div class="flex flex-wrap gap-4">
<Button
variant="border"
onclick={() =>
notifcation.success('Event has been created', {
description: 'This is a description',
})}>Success</Button
notification.info('Thanks for your thoughts', {
content,
})}
>
Info
</Button>

<Button
variant="border"
onclick={() =>
notification.info('Informational', {
content: 'We will check your insight and publish it very soon',
action: {
label: 'Button',
onClick: () => console.log('test'),
},
})}
>
Info with button
</Button>

<Button
variant="border"
onclick={() =>
notification.info('Notification channel settings is changed', {
content,
action: {
label: 'Undo',
onClick: () => console.log('test'),
},
})}
>
Info with button
</Button>

<Button
variant="border"
onclick={() =>
notification.success('Event has been created', {
content: 'We will check your insight and publish it very soon.',
})}
>Success
</Button>

<Button
variant="border"
onclick={() =>
notification.warning('Warning', {
content:
'To activate your SanR NFT subscription, you will need to cancel your existing Sanbase Pro subscription first.',
className: 'bg-porcelain',
})}
>Warning
</Button>

<Button variant="border" onclick={() => notifcation.error('Event has been created')}
<Button variant="border" onclick={() => notification.error('Event has been created')}
>Error</Button
>
</div>
Expand Down

0 comments on commit 1bf62c7

Please sign in to comment.