Files
classroomio/apps/dashboard/src/lib/features/onboarding/components/verify-email-modal.svelte
T
Rotimi Best a3924100dd feat: migrate auth from supabase to better auth (#596)
* feat: add README for @cio/db package and remove existingAccounts.json

* load session, load profile and organization, add create profile hook

* chore: cache session request

The trick was passing all the cookies to the backend. Previously I was only passing the better-auth.session_token and not better-auth.session_data. Read about the difference here https://www.better-auth.com/docs/concepts/cookies#custom-cookies

* feat: login, logout, onboarding, verify email, reset password.

* feat: complete change email and email verification

* feat: standardise how we make rpc calls and create org endpoints

* fix ui errors

* fix: handle potential undefined customerId and standardize avatar URL property

* refactor: standardize custom domain handling and update telegram chat ID references

* chore: add tailwind prefix to @cio/ui

this fixed the media query issues because  @cio/ui classes were overriding this dashboard tailwind build

* chore: clean up org sidebar

* chore: persist mode state

* feat: implement order breadcrumbs

* chore: upgrade to better auth 1.4

* chore: address common auth flow bugs and setup app for others to setup locally.
2025-11-24 01:00:50 +01:00

103 lines
2.9 KiB
Svelte

<script>
import { Modal } from '$lib/components/Modal';
import PrimaryButton from '$lib/components/PrimaryButton/index.svelte';
import { snackbar } from '$lib/components/Snackbar/store';
import { t } from '$lib/utils/functions/translations';
import { currentOrg } from '$lib/utils/store/org';
import { profile } from '$lib/utils/store/user';
import { authClient } from '$lib/utils/services/auth/client';
import { globalStore } from '$lib/utils/store/app';
import { page } from '$app/state';
const WAIT_SEC = 120; // 2 minutes
const WAIT_TIME = WAIT_SEC * 1000;
let loading = $state(false);
let isSent = $state(false);
let autoTriggered = $state(false);
let interval;
let countDown = $state(WAIT_SEC);
const open = $derived(Boolean(!$profile.isEmailVerified && !!$profile.id && !!$currentOrg.id));
const sendVerificationCode = async () => {
if (!$profile.email) {
snackbar.error('verify_email_modal.snackbar.error');
return;
}
if (loading || isSent) return;
loading = true;
try {
const callbackURL = new URL(page.url.href);
callbackURL.searchParams.set('trigger', 'app');
// Avoid showing welcome popup in lms
if (!$globalStore.isOrgSite) {
callbackURL.searchParams.set('welcomePopup', 'true');
}
await authClient.sendVerificationEmail({
email: $profile.email,
callbackURL: callbackURL.toString()
});
isSent = true;
} catch (error) {
snackbar.error('verify_email_modal.snackbar.error');
} finally {
loading = false;
}
interval = setInterval(() => {
countDown = countDown - 1;
}, 1000);
setTimeout(() => {
isSent = false;
clearInterval(interval);
}, WAIT_TIME);
};
$effect(() => {
if (!open || isSent || loading || autoTriggered) return;
// Only send verification code on load once
autoTriggered = true;
sendVerificationCode();
});
</script>
<Modal {open} isCloseable={false} width="w-4/5" maxWidth="w-[500px]" containerClass="p-4">
<div class="flex flex-col items-center space-y-6 text-center">
<img src="/verify-email.svg" alt="email verification" />
<p class="text-xl">{$t('verify_email_modal.heading')}</p>
<p class="w-[70%] text-sm text-gray-700 dark:text-gray-200">
{$t('verify_email_modal.sent_verification')}
{$profile.email}
{$t('verify_email_modal.to_confirm')}
</p>
<div class="flex flex-col items-center">
<PrimaryButton isDisabled={loading || isSent} className="font-normal" onClick={sendVerificationCode}>
{#if loading}
{$t('verify_email_modal.loading')}
{:else}
{$t('verify_email_modal.resend')}
{/if}
</PrimaryButton>
{#if isSent}
<p class="text-xs text-gray-700">
{$t('verify_email_modal.resend_in')}
{countDown}
{$t('verify_email_modal.seconds')}
</p>
{/if}
</div>
</div>
</Modal>