feat: automatically detect language on server and translate UI (#530)
* feat: detect language on the server and translate page * translate auth pages * PR reviews
This commit is contained in:
Vendored
+1
-1
@@ -19,6 +19,6 @@
|
|||||||
"deno.importMap": "./supabase/functions/import_map.json",
|
"deno.importMap": "./supabase/functions/import_map.json",
|
||||||
"editor.formatOnSave": true,
|
"editor.formatOnSave": true,
|
||||||
"editor.codeActionsOnSave": {
|
"editor.codeActionsOnSave": {
|
||||||
"source.organizeImports": true
|
"source.organizeImports": "explicit"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { page } from '$app/stores';
|
import { page } from '$app/stores';
|
||||||
import PrimaryButton from '$lib/components/PrimaryButton/index.svelte';
|
|
||||||
import { VARIANTS } from '$lib/components/PrimaryButton/constants';
|
|
||||||
import GoogleIconColored from '../Icons/GoogleIconColored.svelte';
|
|
||||||
import Avatar from '$lib/components/Avatar/index.svelte';
|
import Avatar from '$lib/components/Avatar/index.svelte';
|
||||||
|
import { VARIANTS } from '$lib/components/PrimaryButton/constants';
|
||||||
|
import PrimaryButton from '$lib/components/PrimaryButton/index.svelte';
|
||||||
|
import { t } from '$lib/utils/functions/translations';
|
||||||
import { currentOrg } from '$lib/utils/store/org';
|
import { currentOrg } from '$lib/utils/store/org';
|
||||||
import type { SupabaseClient } from '@supabase/supabase-js';
|
import type { SupabaseClient } from '@supabase/supabase-js';
|
||||||
|
import GoogleIconColored from '../Icons/GoogleIconColored.svelte';
|
||||||
|
|
||||||
export let supabase: SupabaseClient;
|
export let supabase: SupabaseClient;
|
||||||
export let handleSubmit = () => {};
|
export let handleSubmit = () => {};
|
||||||
@@ -47,11 +48,11 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="app-background w-full min-h-screen flex items-center justify-center">
|
<div class="app-background flex min-h-screen w-full items-center justify-center">
|
||||||
<div class="container border border-gray bg-white dark:bg-black">
|
<div class="border-gray container border bg-white dark:bg-black">
|
||||||
<div class="flex items-center flex-col p-2 lg:px-8 lg:py-3">
|
<div class="flex flex-col items-center p-2 lg:px-8 lg:py-3">
|
||||||
{#if !showOnlyContent || showLogo}
|
{#if !showOnlyContent || showLogo}
|
||||||
<div class="flex flex-col items-center justify-center w-full pt-2">
|
<div class="flex w-full flex-col items-center justify-center pt-2">
|
||||||
<Avatar
|
<Avatar
|
||||||
src={$currentOrg.avatar_url ? $currentOrg.avatar_url : '/logo-192.png'}
|
src={$currentOrg.avatar_url ? $currentOrg.avatar_url : '/logo-192.png'}
|
||||||
name={$currentOrg.name ? $currentOrg.name : 'ClassroomIO'}
|
name={$currentOrg.name ? $currentOrg.name : 'ClassroomIO'}
|
||||||
@@ -61,7 +62,7 @@
|
|||||||
className="mr-2"
|
className="mr-2"
|
||||||
/>
|
/>
|
||||||
<a href="/">
|
<a href="/">
|
||||||
<h4 class="dark:text-white text-xl mt-0">
|
<h4 class="mt-0 text-xl dark:text-white">
|
||||||
{$currentOrg.name ? $currentOrg.name : 'ClassroomIO'}
|
{$currentOrg.name ? $currentOrg.name : 'ClassroomIO'}
|
||||||
</h4>
|
</h4>
|
||||||
</a>
|
</a>
|
||||||
@@ -70,13 +71,13 @@
|
|||||||
<form
|
<form
|
||||||
bind:this={formRef}
|
bind:this={formRef}
|
||||||
on:submit|preventDefault={handleSubmit}
|
on:submit|preventDefault={handleSubmit}
|
||||||
class="flex items-center flex-col w-10/12"
|
class="flex w-10/12 flex-col items-center"
|
||||||
>
|
>
|
||||||
<slot />
|
<slot />
|
||||||
</form>
|
</form>
|
||||||
{#if !showOnlyContent && !hideGoogleAuth}
|
{#if !showOnlyContent && !hideGoogleAuth}
|
||||||
<div class="w-10/12 mb-3">
|
<div class="mb-3 w-10/12">
|
||||||
<p class="dark:text-white text-sm mb-5">or sign up with:</p>
|
<p class="mb-5 text-sm dark:text-white">{$t('login.signup_with')}:</p>
|
||||||
<PrimaryButton
|
<PrimaryButton
|
||||||
variant={VARIANTS.OUTLINED}
|
variant={VARIANTS.OUTLINED}
|
||||||
onClick={signInWithGoogle}
|
onClick={signInWithGoogle}
|
||||||
@@ -84,22 +85,24 @@
|
|||||||
className="py-3 sm:w-full w-full"
|
className="py-3 sm:w-full w-full"
|
||||||
>
|
>
|
||||||
<GoogleIconColored />
|
<GoogleIconColored />
|
||||||
<span class="ml-2">{isLogin ? 'Sign in' : 'Sign up'} with Google</span>
|
<span class="ml-2">
|
||||||
|
{isLogin ? $t('login.login_with_google') : $t('login.signup_with_google')}
|
||||||
|
</span>
|
||||||
</PrimaryButton>
|
</PrimaryButton>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{#if !showOnlyContent}
|
{#if !showOnlyContent}
|
||||||
<div class="w-full p-6 border-t border-grey text-center">
|
<div class="border-grey w-full border-t p-6 text-center">
|
||||||
{#if isLogin}
|
{#if isLogin}
|
||||||
Not registered yet? <a
|
{$t('login.not_registered_yet')}
|
||||||
class="hover:underline text-primary-700"
|
<a class="text-primary-700 hover:underline" href="/signup{$page.url.search}"
|
||||||
href="/signup{$page.url.search}">Sign up</a
|
>{$t('login.signup')}</a
|
||||||
>
|
>
|
||||||
{:else}
|
{:else}
|
||||||
Already have an account? <a
|
{$t('login.already_have_account')}
|
||||||
class="hover:underline text-primary-700"
|
<a class="text-primary-700 hover:underline" href="/login{$page.url.search}"
|
||||||
href="/login{$page.url.search}">Log In</a
|
>{$t('login.login')}</a
|
||||||
>
|
>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,34 +1,34 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { fade } from 'svelte/transition';
|
import { goto } from '$app/navigation';
|
||||||
import get from 'lodash/get';
|
|
||||||
import LogoFacebook from 'carbon-icons-svelte/lib/LogoFacebook.svelte';
|
|
||||||
import LogoTwitter from 'carbon-icons-svelte/lib/LogoTwitter.svelte';
|
|
||||||
import LogoLinkedin from 'carbon-icons-svelte/lib/LogoLinkedin.svelte';
|
|
||||||
import { Accordion, AccordionItem, Grid, Row, Column } from 'carbon-components-svelte';
|
|
||||||
import LocationFilled from 'carbon-icons-svelte/lib/LocationFilled.svelte';
|
|
||||||
import Phone from 'carbon-icons-svelte/lib/Phone.svelte';
|
|
||||||
import Email from 'carbon-icons-svelte/lib/Email.svelte';
|
|
||||||
import Rocket from 'carbon-icons-svelte/lib/Rocket.svelte';
|
|
||||||
import PageLoader from './PageLoader.svelte';
|
|
||||||
import { orgLandingpageValidation } from '$lib/utils/functions/validator';
|
|
||||||
import TextField from '$lib/components/Form/TextField.svelte';
|
|
||||||
import PrimaryButton from '$lib/components/PrimaryButton/index.svelte';
|
|
||||||
import { VARIANTS } from '$lib/components/PrimaryButton/constants';
|
|
||||||
import Navigation from '$lib/components/Navigation/index.svelte';
|
|
||||||
import Box from '$lib/components/Box/index.svelte';
|
import Box from '$lib/components/Box/index.svelte';
|
||||||
import Card from '$lib/components/Courses/components/Card/index.svelte';
|
import Card from '$lib/components/Courses/components/Card/index.svelte';
|
||||||
import CardLoader from '$lib/components/Courses/components/Card/Loader.svelte';
|
import CardLoader from '$lib/components/Courses/components/Card/Loader.svelte';
|
||||||
import CoursesEmptyIcon from '$lib/components/Icons/CoursesEmptyIcon.svelte';
|
import { courseMetaDeta, courses } from '$lib/components/Courses/store';
|
||||||
import { getSupabase } from '$lib/utils/functions/supabase';
|
|
||||||
import { courses, courseMetaDeta } from '$lib/components/Courses/store';
|
|
||||||
import { getCourseBySiteName } from '$lib/utils/services/org';
|
|
||||||
import { validateEmail } from '$lib/utils/functions/validateEmail';
|
|
||||||
import TextArea from '$lib/components/Form/TextArea.svelte';
|
import TextArea from '$lib/components/Form/TextArea.svelte';
|
||||||
import { goto } from '$app/navigation';
|
import TextField from '$lib/components/Form/TextField.svelte';
|
||||||
|
import CoursesEmptyIcon from '$lib/components/Icons/CoursesEmptyIcon.svelte';
|
||||||
|
import Navigation from '$lib/components/Navigation/index.svelte';
|
||||||
import { landingPageSettings } from '$lib/components/Org/Settings/store';
|
import { landingPageSettings } from '$lib/components/Org/Settings/store';
|
||||||
import { t } from '$lib/utils/functions/translations';
|
import { VARIANTS } from '$lib/components/PrimaryButton/constants';
|
||||||
|
import PrimaryButton from '$lib/components/PrimaryButton/index.svelte';
|
||||||
import PoweredBy from '$lib/components/Upgrade/PoweredBy.svelte';
|
import PoweredBy from '$lib/components/Upgrade/PoweredBy.svelte';
|
||||||
|
import { getSupabase } from '$lib/utils/functions/supabase';
|
||||||
|
import { t } from '$lib/utils/functions/translations';
|
||||||
|
import { validateEmail } from '$lib/utils/functions/validateEmail';
|
||||||
|
import { orgLandingpageValidation } from '$lib/utils/functions/validator';
|
||||||
|
import { getCourseBySiteName } from '$lib/utils/services/org';
|
||||||
import type { CurrentOrg } from '$lib/utils/types/org';
|
import type { CurrentOrg } from '$lib/utils/types/org';
|
||||||
|
import { Accordion, AccordionItem, Column, Grid, Row } from 'carbon-components-svelte';
|
||||||
|
import Email from 'carbon-icons-svelte/lib/Email.svelte';
|
||||||
|
import LocationFilled from 'carbon-icons-svelte/lib/LocationFilled.svelte';
|
||||||
|
import LogoFacebook from 'carbon-icons-svelte/lib/LogoFacebook.svelte';
|
||||||
|
import LogoLinkedin from 'carbon-icons-svelte/lib/LogoLinkedin.svelte';
|
||||||
|
import LogoTwitter from 'carbon-icons-svelte/lib/LogoTwitter.svelte';
|
||||||
|
import Phone from 'carbon-icons-svelte/lib/Phone.svelte';
|
||||||
|
import Rocket from 'carbon-icons-svelte/lib/Rocket.svelte';
|
||||||
|
import get from 'lodash/get';
|
||||||
|
import { fade } from 'svelte/transition';
|
||||||
|
import PageLoader from './PageLoader.svelte';
|
||||||
|
|
||||||
export let orgSiteName = '';
|
export let orgSiteName = '';
|
||||||
export let org: CurrentOrg | null;
|
export let org: CurrentOrg | null;
|
||||||
@@ -115,7 +115,6 @@
|
|||||||
if (!siteName) return;
|
if (!siteName) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
console.log('sitename', siteName);
|
|
||||||
$courseMetaDeta.isLoading = true;
|
$courseMetaDeta.isLoading = true;
|
||||||
const coursesResult = await getCourseBySiteName(siteName);
|
const coursesResult = await getCourseBySiteName(siteName);
|
||||||
courses.set(coursesResult);
|
courses.set(coursesResult);
|
||||||
@@ -182,7 +181,7 @@
|
|||||||
{#if $landingPageSettings.header.show}
|
{#if $landingPageSettings.header.show}
|
||||||
<header
|
<header
|
||||||
id="header"
|
id="header"
|
||||||
class={`w-full h-[100vh] md:h-[90vh] mb-10 relative ${
|
class={`relative mb-10 h-[100vh] w-full md:h-[90vh] ${
|
||||||
$landingPageSettings.header.background?.show
|
$landingPageSettings.header.background?.show
|
||||||
? 'bg-cover bg-center'
|
? 'bg-cover bg-center'
|
||||||
: 'border-b border-gray-300'
|
: 'border-b border-gray-300'
|
||||||
@@ -196,25 +195,25 @@
|
|||||||
isOrgSite={true}
|
isOrgSite={true}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div class="absolute h-[100vh] md:h-[90vh] top-0 w-full opacity-80 z-10 bg-white" />
|
<div class="absolute top-0 z-10 h-[100vh] w-full bg-white opacity-80 md:h-[90vh]" />
|
||||||
{#if $landingPageSettings.header.banner.show}
|
{#if $landingPageSettings.header.banner.show}
|
||||||
<div class="flex items-center justify-center md:h-full py-2">
|
<div class="flex items-center justify-center py-2 md:h-full">
|
||||||
<div
|
<div
|
||||||
class="md:w-11/12 lg:w-5/6 w-full flex items-center justify-center md:justify-between flex-col-reverse md:flex-row z-20 relative"
|
class="relative z-20 flex w-full flex-col-reverse items-center justify-center md:w-11/12 md:flex-row md:justify-between lg:w-5/6"
|
||||||
>
|
>
|
||||||
<!-- Course Description -->
|
<!-- Course Description -->
|
||||||
<div class="md:w-2/5 w-11/12 py-10 flex flex-col items-center md:items-start mr-3">
|
<div class="mr-3 flex w-11/12 flex-col items-center py-10 md:w-2/5 md:items-start">
|
||||||
<p class=" text-primary-600 text-2xl font-semibold capitalize">
|
<p class=" text-primary-600 text-2xl font-semibold capitalize">
|
||||||
{org.name}
|
{org.name}
|
||||||
</p>
|
</p>
|
||||||
<h1
|
<h1
|
||||||
class="text-4xl md:text-5xl lg:text-6xl text-center md:text-start my-4 font-bold"
|
class="my-4 text-center text-4xl font-bold md:text-start md:text-5xl lg:text-6xl"
|
||||||
>
|
>
|
||||||
{$landingPageSettings.header.title} <br /><span class="text-primary-600"
|
{$landingPageSettings.header.title} <br /><span class="text-primary-600"
|
||||||
>{$landingPageSettings.header.titleHighlight}</span
|
>{$landingPageSettings.header.titleHighlight}</span
|
||||||
>
|
>
|
||||||
</h1>
|
</h1>
|
||||||
<p class="text-md mb-3 md:mb-5 text-xl text-center md:text-start">
|
<p class="text-md mb-3 text-center text-xl md:mb-5 md:text-start">
|
||||||
{$landingPageSettings.header.subtitle}
|
{$landingPageSettings.header.subtitle}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
@@ -228,7 +227,7 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="w-5/6 md:w-1/2 md:max-w-[650px] flex">
|
<div class="flex w-5/6 md:w-1/2 md:max-w-[650px]">
|
||||||
{#if isYouTubeLink($landingPageSettings.header?.banner?.video) && $landingPageSettings.header.banner.type === 'video'}
|
{#if isYouTubeLink($landingPageSettings.header?.banner?.video) && $landingPageSettings.header.banner.type === 'video'}
|
||||||
<div bind:this={player} id="player" style="width:100%; border-radius:12px">
|
<div bind:this={player} id="player" style="width:100%; border-radius:12px">
|
||||||
<iframe
|
<iframe
|
||||||
@@ -256,10 +255,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<div class="w-full h-full flex items-center justify-center md:flex-row z-20 relative">
|
<div class="relative z-20 flex h-full w-full items-center justify-center md:flex-row">
|
||||||
<div class="max-w-[600px] mx-auto w-11/12 py-10 flex flex-col items-center">
|
<div class="mx-auto flex w-11/12 max-w-[600px] flex-col items-center py-10">
|
||||||
<p class=" text-primary-600 text-2xl font-semibold capitalize">{org.name}</p>
|
<p class=" text-primary-600 text-2xl font-semibold capitalize">{org.name}</p>
|
||||||
<h1 class="text-4xl md:text-5xl lg:text-6xl text-center my-4 font-bold">
|
<h1 class="my-4 text-center text-4xl font-bold md:text-5xl lg:text-6xl">
|
||||||
{$landingPageSettings.header.title} <br /><span class="text-primary-600"
|
{$landingPageSettings.header.title} <br /><span class="text-primary-600"
|
||||||
>{$landingPageSettings.header.titleHighlight}</span
|
>{$landingPageSettings.header.titleHighlight}</span
|
||||||
>
|
>
|
||||||
@@ -284,9 +283,9 @@
|
|||||||
|
|
||||||
<!-- Our Story section -->
|
<!-- Our Story section -->
|
||||||
{#if $landingPageSettings.aboutUs.show}
|
{#if $landingPageSettings.aboutUs.show}
|
||||||
<section id="aboutus" class="m-h-[400px] my-10 mx-auto max-w-6xl w-full">
|
<section id="aboutus" class="m-h-[400px] mx-auto my-10 w-full max-w-6xl">
|
||||||
<div class="mx-4 flex items-center justify-evenly flex-col lg:flex-row">
|
<div class="mx-4 flex flex-col items-center justify-evenly lg:flex-row">
|
||||||
<div class="max-w-[600px] lg:w-2/5 mr-5 mb-5 lg:mb-0">
|
<div class="mb-5 mr-5 max-w-[600px] lg:mb-0 lg:w-2/5">
|
||||||
<h2 class="text-4xl md:text-5xl lg:text-6xl">{$landingPageSettings.aboutUs.title}</h2>
|
<h2 class="text-4xl md:text-5xl lg:text-6xl">{$landingPageSettings.aboutUs.title}</h2>
|
||||||
<p class="mb-2">
|
<p class="mb-2">
|
||||||
{$landingPageSettings.aboutUs.content}
|
{$landingPageSettings.aboutUs.content}
|
||||||
@@ -297,7 +296,7 @@
|
|||||||
<img
|
<img
|
||||||
src={$landingPageSettings.aboutUs.imageUrl}
|
src={$landingPageSettings.aboutUs.imageUrl}
|
||||||
alt="Our Story"
|
alt="Our Story"
|
||||||
class=" rounded-2xl max-h-[450px]"
|
class=" max-h-[450px] rounded-2xl"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -306,10 +305,10 @@
|
|||||||
|
|
||||||
<!-- Courses Section -->
|
<!-- Courses Section -->
|
||||||
{#if $landingPageSettings.courses.show}
|
{#if $landingPageSettings.courses.show}
|
||||||
<section id="courses" transition:fade class="my-10 mx-auto max-w-6xl w-full">
|
<section id="courses" transition:fade class="mx-auto my-10 w-full max-w-6xl">
|
||||||
<div class="w-full">
|
<div class="w-full">
|
||||||
<div class="max-w-[500px] mx-auto w-11/12 py-10">
|
<div class="mx-auto w-11/12 max-w-[500px] py-10">
|
||||||
<h1 class="text-4xl md:text-5xl lg:text-6xl text-center my-4 font-bold">
|
<h1 class="my-4 text-center text-4xl font-bold md:text-5xl lg:text-6xl">
|
||||||
{$landingPageSettings.courses.title}
|
{$landingPageSettings.courses.title}
|
||||||
<span class="text-primary-600">{$landingPageSettings.courses.titleHighlight}</span>
|
<span class="text-primary-600">{$landingPageSettings.courses.titleHighlight}</span>
|
||||||
</h1>
|
</h1>
|
||||||
@@ -319,13 +318,13 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{#if $courseMetaDeta.isLoading}
|
{#if $courseMetaDeta.isLoading}
|
||||||
<div class="cards-container my-4 mx-2">
|
<div class="cards-container mx-2 my-4">
|
||||||
<CardLoader />
|
<CardLoader />
|
||||||
<CardLoader />
|
<CardLoader />
|
||||||
<CardLoader />
|
<CardLoader />
|
||||||
</div>
|
</div>
|
||||||
{:else if $courses.length > 0}
|
{:else if $courses.length > 0}
|
||||||
<div class="cards-container my-4 mx-2">
|
<div class="cards-container mx-2 my-4">
|
||||||
{#each $courses.slice(0, viewAll ? $courses.length : 3) as courseData}
|
{#each $courses.slice(0, viewAll ? $courses.length : 3) as courseData}
|
||||||
<Card
|
<Card
|
||||||
id={courseData.id}
|
id={courseData.id}
|
||||||
@@ -350,17 +349,17 @@
|
|||||||
{:else}
|
{:else}
|
||||||
<Box>
|
<Box>
|
||||||
<CoursesEmptyIcon />
|
<CoursesEmptyIcon />
|
||||||
<h3 class="dark:text-white text-2xl my-5">
|
<h3 class="my-5 text-2xl dark:text-white">
|
||||||
{$t('course.navItem.landing_page.no_course_published')}
|
{$t('course.navItem.landing_page.no_course_published')}
|
||||||
</h3>
|
</h3>
|
||||||
<p class="dark:text-white w-1/3 text-center">
|
<p class="w-1/3 text-center dark:text-white">
|
||||||
{$t('course.navItem.landing_page.coming_your_way')}
|
{$t('course.navItem.landing_page.coming_your_way')}
|
||||||
</p>
|
</p>
|
||||||
</Box>
|
</Box>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if $courses.length > 3}
|
{#if $courses.length > 3}
|
||||||
<div class="w-full mt-3 flex justify-center">
|
<div class="mt-3 flex w-full justify-center">
|
||||||
<PrimaryButton
|
<PrimaryButton
|
||||||
variant={VARIANTS.OUTLINED}
|
variant={VARIANTS.OUTLINED}
|
||||||
onClick={() => (viewAll = !viewAll)}
|
onClick={() => (viewAll = !viewAll)}
|
||||||
@@ -376,9 +375,9 @@
|
|||||||
|
|
||||||
<!-- FAQ Section -->
|
<!-- FAQ Section -->
|
||||||
{#if $landingPageSettings.faq.show}
|
{#if $landingPageSettings.faq.show}
|
||||||
<section id="faq" transition:fade class="my-10 mx-auto max-w-[700px] w-full">
|
<section id="faq" transition:fade class="mx-auto my-10 w-full max-w-[700px]">
|
||||||
<div class="py-10">
|
<div class="py-10">
|
||||||
<h1 class="text-4xl md:text-5xl lg:text-6xl text-center my-4 font-bold">
|
<h1 class="my-4 text-center text-4xl font-bold md:text-5xl lg:text-6xl">
|
||||||
{$landingPageSettings.faq.title}
|
{$landingPageSettings.faq.title}
|
||||||
</h1>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
@@ -398,10 +397,10 @@
|
|||||||
|
|
||||||
<!-- Contact Section-->
|
<!-- Contact Section-->
|
||||||
{#if $landingPageSettings.contact.show}
|
{#if $landingPageSettings.contact.show}
|
||||||
<section id="contact" transition:fade class="my-10 w-full bg-primary-50">
|
<section id="contact" transition:fade class="bg-primary-50 my-10 w-full">
|
||||||
<div class="mx-auto max-w-6xl w-full">
|
<div class="mx-auto w-full max-w-6xl">
|
||||||
<div class="max-w-[500px] mx-auto w-11/12 py-10">
|
<div class="mx-auto w-11/12 max-w-[500px] py-10">
|
||||||
<h1 class="text-4xl md:text-5xl lg:text-6xl text-center my-4 font-bold">
|
<h1 class="my-4 text-center text-4xl font-bold md:text-5xl lg:text-6xl">
|
||||||
{$landingPageSettings.contact.title}
|
{$landingPageSettings.contact.title}
|
||||||
<span class="text-primary-600">{$landingPageSettings.contact.titleHighlight}</span>
|
<span class="text-primary-600">{$landingPageSettings.contact.titleHighlight}</span>
|
||||||
</h1>
|
</h1>
|
||||||
@@ -411,37 +410,37 @@
|
|||||||
<!-- Contact Details -->
|
<!-- Contact Details -->
|
||||||
<Row>
|
<Row>
|
||||||
<Column
|
<Column
|
||||||
class="flex items-center flex-col justify-center break-all text-center cursor-pointer hover:shadow-xl rounded-lg transition-all duration-500 py-2 mx-2"
|
class="mx-2 flex cursor-pointer flex-col items-center justify-center break-all rounded-lg py-2 text-center transition-all duration-500 hover:shadow-xl"
|
||||||
>
|
>
|
||||||
<LocationFilled size={32} />
|
<LocationFilled size={32} />
|
||||||
<p class="text-xs md:text-sm mt-3 max-w-[200px]">
|
<p class="mt-3 max-w-[200px] text-xs md:text-sm">
|
||||||
{$landingPageSettings.contact.address}
|
{$landingPageSettings.contact.address}
|
||||||
</p>
|
</p>
|
||||||
</Column>
|
</Column>
|
||||||
<Column
|
<Column
|
||||||
class="flex items-center flex-col justify-center break-all text-center cursor-pointer hover:shadow-xl rounded-lg transition-all duration-500 py-2 mx-2"
|
class="mx-2 flex cursor-pointer flex-col items-center justify-center break-all rounded-lg py-2 text-center transition-all duration-500 hover:shadow-xl"
|
||||||
>
|
>
|
||||||
<Phone size={32} />
|
<Phone size={32} />
|
||||||
<p class="text-xs md:text-sm mt-3">{$landingPageSettings.contact.phone}</p>
|
<p class="mt-3 text-xs md:text-sm">{$landingPageSettings.contact.phone}</p>
|
||||||
</Column>
|
</Column>
|
||||||
<Column
|
<Column
|
||||||
class="flex items-center flex-col justify-center break-all text-center cursor-pointer hover:shadow-xl rounded-lg transition-all duration-500 py-2 mx-2"
|
class="mx-2 flex cursor-pointer flex-col items-center justify-center break-all rounded-lg py-2 text-center transition-all duration-500 hover:shadow-xl"
|
||||||
>
|
>
|
||||||
<Email size={32} />
|
<Email size={32} />
|
||||||
<p class="text-xs md:text-sm mt-3">{$landingPageSettings.contact.email}</p>
|
<p class="mt-3 text-xs md:text-sm">{$landingPageSettings.contact.email}</p>
|
||||||
</Column>
|
</Column>
|
||||||
</Row>
|
</Row>
|
||||||
|
|
||||||
<!-- Contact Form -->
|
<!-- Contact Form -->
|
||||||
<div class="mt-8 bg-white p-7 rounded-lg">
|
<div class="mt-8 rounded-lg bg-white p-7">
|
||||||
{#if successContactSaved}
|
{#if successContactSaved}
|
||||||
<div class="w-full flex items-center justify-center">
|
<div class="flex w-full items-center justify-center">
|
||||||
{$t('course.navItem.landing_page.thank_you')}
|
{$t('course.navItem.landing_page.thank_you')}
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<form on:submit|preventDefault={handleContactSubmit}>
|
<form on:submit|preventDefault={handleContactSubmit}>
|
||||||
<div class="w-full flex justify-between flex-col md:flex-row">
|
<div class="flex w-full flex-col justify-between md:flex-row">
|
||||||
<div class="w-full md:w-2/4 mr-5">
|
<div class="mr-5 w-full md:w-2/4">
|
||||||
<TextField
|
<TextField
|
||||||
label={$t('course.navItem.landing_page.name')}
|
label={$t('course.navItem.landing_page.name')}
|
||||||
bind:value={contact.name}
|
bind:value={contact.name}
|
||||||
@@ -481,7 +480,7 @@
|
|||||||
type="submit"
|
type="submit"
|
||||||
isLoading={isContactSubmiting}
|
isLoading={isContactSubmiting}
|
||||||
>
|
>
|
||||||
<span class="mr-2 text-md">{$t('course.navItem.landing_page.submit')}</span>
|
<span class="text-md mr-2">{$t('course.navItem.landing_page.submit')}</span>
|
||||||
<Rocket size={24} />
|
<Rocket size={24} />
|
||||||
</PrimaryButton>
|
</PrimaryButton>
|
||||||
</form>
|
</form>
|
||||||
@@ -494,12 +493,12 @@
|
|||||||
|
|
||||||
<!-- Waitlist Section -->
|
<!-- Waitlist Section -->
|
||||||
{#if $landingPageSettings.mailinglist.show}
|
{#if $landingPageSettings.mailinglist.show}
|
||||||
<section id="waitlist" transition:fade class="my-10 mx-auto max-w-6xl w-[95%]">
|
<section id="waitlist" transition:fade class="mx-auto my-10 w-[95%] max-w-6xl">
|
||||||
<div
|
<div
|
||||||
class="bg-primary-700 rounded-lg flex flex-col lg:flex-row lg:items-center px-4 md:px-10 py-14"
|
class="bg-primary-700 flex flex-col rounded-lg px-4 py-14 md:px-10 lg:flex-row lg:items-center"
|
||||||
>
|
>
|
||||||
<div class="w-full md:w-[65%] md:mr-4">
|
<div class="w-full md:mr-4 md:w-[65%]">
|
||||||
<h1 class="text-4xl font-bold mb-5 mt-0 text-white">
|
<h1 class="mb-5 mt-0 text-4xl font-bold text-white">
|
||||||
{$landingPageSettings.mailinglist.title}
|
{$landingPageSettings.mailinglist.title}
|
||||||
</h1>
|
</h1>
|
||||||
<p class="text-lg text-white">
|
<p class="text-lg text-white">
|
||||||
@@ -507,7 +506,7 @@
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<form on:submit|preventDefault={handleSubmit} class="my-4 w-full md:w-fit">
|
<form on:submit|preventDefault={handleSubmit} class="my-4 w-full md:w-fit">
|
||||||
<div class="flex items-center flex-col sm:flex-row">
|
<div class="flex flex-col items-center sm:flex-row">
|
||||||
{#if success}
|
{#if success}
|
||||||
<p class="text-white">{$t('course.navItem.landing_page.successful_sub')}</p>
|
<p class="text-white">{$t('course.navItem.landing_page.successful_sub')}</p>
|
||||||
{:else}
|
{:else}
|
||||||
@@ -538,9 +537,9 @@
|
|||||||
{#if $landingPageSettings.footer.show}
|
{#if $landingPageSettings.footer.show}
|
||||||
<footer
|
<footer
|
||||||
id="footer"
|
id="footer"
|
||||||
class="flex justify-center items-center flex-col my-10 w-full px-5 py-10 md:py-3 border-b-0 border-r-0 border-t border-l-0 border-gray-300"
|
class="my-10 flex w-full flex-col items-center justify-center border-b-0 border-l-0 border-r-0 border-t border-gray-300 px-5 py-10 md:py-3"
|
||||||
>
|
>
|
||||||
<ul class="flex w-11/12 items-center flex-col sm:flex-row">
|
<ul class="flex w-11/12 flex-col items-center sm:flex-row">
|
||||||
<div class="logo">
|
<div class="logo">
|
||||||
<a
|
<a
|
||||||
href="/"
|
href="/"
|
||||||
@@ -552,16 +551,16 @@
|
|||||||
<img
|
<img
|
||||||
src={org.avatar_url || '/logo-192.png'}
|
src={org.avatar_url || '/logo-192.png'}
|
||||||
alt={`${org.name} logo`}
|
alt={`${org.name} logo`}
|
||||||
class="rounded max-h-10 w-10 inline-block mx-auto"
|
class="mx-auto inline-block max-h-10 w-10 rounded"
|
||||||
data-atf="1"
|
data-atf="1"
|
||||||
/>
|
/>
|
||||||
<h3 class="text-black ml-3 text-xl">{org.name}</h3>
|
<h3 class="ml-3 text-xl text-black">{org.name}</h3>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<span class="flex-grow" />
|
<span class="flex-grow" />
|
||||||
|
|
||||||
<div class="flex mt-5 sm:mt-0 gap-2">
|
<div class="mt-5 flex gap-2 sm:mt-0">
|
||||||
{#if $landingPageSettings.footer.facebook}
|
{#if $landingPageSettings.footer.facebook}
|
||||||
<li>
|
<li>
|
||||||
<a
|
<a
|
||||||
|
|||||||
@@ -2,13 +2,13 @@
|
|||||||
import Modal from '$lib/components/Modal/index.svelte';
|
import Modal from '$lib/components/Modal/index.svelte';
|
||||||
import PrimaryButton from '$lib/components/PrimaryButton/index.svelte';
|
import PrimaryButton from '$lib/components/PrimaryButton/index.svelte';
|
||||||
import { snackbar } from '$lib/components/Snackbar/store';
|
import { snackbar } from '$lib/components/Snackbar/store';
|
||||||
import { profile } from '$lib/utils/store/user';
|
import { t } from '$lib/utils/functions/translations';
|
||||||
import {
|
import {
|
||||||
triggerSendEmail,
|
NOTIFICATION_NAME,
|
||||||
NOTIFICATION_NAME
|
triggerSendEmail
|
||||||
} from '$lib/utils/services/notification/notification';
|
} from '$lib/utils/services/notification/notification';
|
||||||
import { currentOrg } from '$lib/utils/store/org';
|
import { currentOrg } from '$lib/utils/store/org';
|
||||||
import { t } from '$lib/utils/functions/translations';
|
import { profile } from '$lib/utils/store/user';
|
||||||
|
|
||||||
const WAIT_SEC = 120;
|
const WAIT_SEC = 120;
|
||||||
const WAIT_TIME = WAIT_SEC * 1000;
|
const WAIT_TIME = WAIT_SEC * 1000;
|
||||||
@@ -51,21 +51,21 @@
|
|||||||
}, WAIT_TIME);
|
}, WAIT_TIME);
|
||||||
};
|
};
|
||||||
|
|
||||||
$: open = Boolean(!$profile.is_email_verified && !!$currentOrg.id);
|
$: open = Boolean(!$profile.is_email_verified && !!$profile.id && !!$currentOrg.id);
|
||||||
$: open && sendVerificationCode();
|
$: open && sendVerificationCode();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Modal {open} isCloseable={false} width="w-4/5" maxWidth="w-[500px]" containerClass="p-4">
|
<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">
|
<div class="flex flex-col items-center space-y-6 text-center">
|
||||||
<img src="/verify-email.svg" alt="email verification" />
|
<img src="/verify-email.svg" alt="email verification" />
|
||||||
<p class="font-bold text-xl">{$t('verify_email_modal.heading')}</p>
|
<p class="text-xl font-bold">{$t('verify_email_modal.heading')}</p>
|
||||||
<p class="text-sm w-[70%] text-gray-700 dark:text-gray-200">
|
<p class="w-[70%] text-sm text-gray-700 dark:text-gray-200">
|
||||||
{$t('verify_email_modal.sent_verification')}
|
{$t('verify_email_modal.sent_verification')}
|
||||||
{$profile.email}
|
{$profile.email}
|
||||||
{$t('verify_email_modal.to_confirm')}
|
{$t('verify_email_modal.to_confirm')}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div class="flex items-center flex-col">
|
<div class="flex flex-col items-center">
|
||||||
<PrimaryButton
|
<PrimaryButton
|
||||||
isDisabled={loading || isSent}
|
isDisabled={loading || isSent}
|
||||||
className="font-normal"
|
className="font-normal"
|
||||||
|
|||||||
@@ -76,7 +76,10 @@ export async function getProfile({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Skip refetching profile, if already in store
|
// Skip refetching profile, if already in store
|
||||||
if (profileStore.id) return;
|
if (profileStore.id) {
|
||||||
|
handleLocaleChange(profileStore.locale);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Check if user has profile
|
// Check if user has profile
|
||||||
let {
|
let {
|
||||||
@@ -124,7 +127,7 @@ export async function getProfile({
|
|||||||
setAnalyticsUser();
|
setAnalyticsUser();
|
||||||
|
|
||||||
// Fetch language
|
// Fetch language
|
||||||
handleLocaleChange(profileStore.locale);
|
handleLocaleChange(newProfileData[0].locale);
|
||||||
|
|
||||||
if (isOrgSite) {
|
if (isOrgSite) {
|
||||||
const { data, error } = await supabase
|
const { data, error } = await supabase
|
||||||
@@ -179,8 +182,7 @@ export async function getProfile({
|
|||||||
// Set user in sentry
|
// Set user in sentry
|
||||||
setAnalyticsUser();
|
setAnalyticsUser();
|
||||||
|
|
||||||
// Fetch language
|
handleLocaleChange(profileData.locale);
|
||||||
handleLocaleChange(profileStore.locale);
|
|
||||||
|
|
||||||
const orgRes = await getOrganizations(profileData.id, isOrgSite, orgSiteName);
|
const orgRes = await getOrganizations(profileData.id, isOrgSite, orgSiteName);
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
|
import { LOCALE } from '$lib/utils/types';
|
||||||
import { writable } from 'svelte/store';
|
import { writable } from 'svelte/store';
|
||||||
import i18n, { type Config } from 'sveltekit-i18n';
|
import i18n, { type Config } from 'sveltekit-i18n';
|
||||||
import { LOCALE } from '$lib/utils/types';
|
|
||||||
|
|
||||||
interface Params {
|
interface Params {
|
||||||
dateValue: number;
|
dateValue: number;
|
||||||
@@ -10,8 +10,7 @@ interface Params {
|
|||||||
val: Date;
|
val: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
const config: Config<Partial<Params>> = {
|
export const config: Config<Partial<Params>> = {
|
||||||
initLocale: 'en',
|
|
||||||
// parser: parser(),
|
// parser: parser(),
|
||||||
loaders: [
|
loaders: [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
|
import { t } from '$lib/utils/functions/translations';
|
||||||
import isNumber from 'lodash/isNumber';
|
import isNumber from 'lodash/isNumber';
|
||||||
import z from 'zod';
|
import z from 'zod';
|
||||||
import { validateEmail } from './validateEmail';
|
import { validateEmail } from './validateEmail';
|
||||||
import { t } from '$lib/utils/functions/translations';
|
|
||||||
|
|
||||||
const getOrgNameValidation = () =>
|
const getOrgNameValidation = () =>
|
||||||
z
|
z
|
||||||
@@ -85,12 +85,12 @@ const forgotValidationSchema = z.object({
|
|||||||
|
|
||||||
const authValidationSchema = z.object({
|
const authValidationSchema = z.object({
|
||||||
email: z.string().email({
|
email: z.string().email({
|
||||||
message: `${t.get('validations.auth.email.invalid_email')}`,
|
message: 'validations.auth.email.invalid_email',
|
||||||
invalid_type_error: `${t.get('validations.invalid_type_error')}`
|
invalid_type_error: 'validations.invalid_type_error'
|
||||||
}),
|
}),
|
||||||
password: z.string().min(6, {
|
password: z.string().min(6, {
|
||||||
message: `${t.get('validations.auth.password.min_char')}`,
|
message: 'validations.auth.password.min_char',
|
||||||
invalid_type_error: `${t.get('validations.invalid_type_error')}`
|
invalid_type_error: 'validations.invalid_type_error'
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,21 +1,21 @@
|
|||||||
import { get } from 'svelte/store';
|
|
||||||
import { supabase } from '$lib/utils/functions/supabase';
|
|
||||||
import { isUUID } from '$lib/utils/functions/isUUID';
|
|
||||||
import { QUESTION_TYPE } from '$lib/components/Question/constants';
|
import { QUESTION_TYPE } from '$lib/components/Question/constants';
|
||||||
|
import { STATUS } from '$lib/utils/constants/course';
|
||||||
|
import { isUUID } from '$lib/utils/functions/isUUID';
|
||||||
|
import { supabase } from '$lib/utils/functions/supabase';
|
||||||
|
import { isOrgAdmin } from '$lib/utils/store/org';
|
||||||
import type {
|
import type {
|
||||||
Lesson,
|
|
||||||
Course,
|
Course,
|
||||||
|
Exercise,
|
||||||
|
ExerciseTemplate,
|
||||||
Group,
|
Group,
|
||||||
Groupmember,
|
Groupmember,
|
||||||
Exercise,
|
Lesson,
|
||||||
LessonCompletion,
|
LessonCompletion,
|
||||||
ExerciseTemplate,
|
LessonSection,
|
||||||
LessonSection
|
ProfileCourseProgress
|
||||||
} from '$lib/utils/types';
|
} from '$lib/utils/types';
|
||||||
import { STATUS } from '$lib/utils/constants/course';
|
import type { PostgrestError, PostgrestSingleResponse } from '@supabase/supabase-js';
|
||||||
import type { PostgrestSingleResponse, PostgrestError } from '@supabase/supabase-js';
|
import { get } from 'svelte/store';
|
||||||
import type { ProfileCourseProgress } from '$lib/utils/types';
|
|
||||||
import { isOrgAdmin } from '$lib/utils/store/org';
|
|
||||||
|
|
||||||
export async function fetchCourses(profileId, orgId) {
|
export async function fetchCourses(profileId, orgId) {
|
||||||
if (!orgId || !profileId) return;
|
if (!orgId || !profileId) return;
|
||||||
@@ -144,10 +144,7 @@ export async function fetchCourse(courseId?: Course['id'], slug?: Course['slug']
|
|||||||
|
|
||||||
const { data, error } = response;
|
const { data, error } = response;
|
||||||
|
|
||||||
console.log(`error`, error);
|
|
||||||
console.log(`data`, data);
|
|
||||||
if (!data || error) {
|
if (!data || error) {
|
||||||
console.log(`data`, data);
|
|
||||||
console.log(`fetchCourse => error`, error);
|
console.log(`fetchCourse => error`, error);
|
||||||
// return this.redirect(307, '/courses');
|
// return this.redirect(307, '/courses');
|
||||||
return { data, error };
|
return { data, error };
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { get } from 'svelte/store';
|
|
||||||
import type { PostgrestError } from '@supabase/supabase-js';
|
|
||||||
import { goto } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
import { supabase } from '$lib/utils/functions/supabase';
|
|
||||||
import { orgs, currentOrg, orgAudience, orgTeam } from '$lib/utils/store/org';
|
|
||||||
import { ROLE, ROLE_LABEL } from '$lib/utils/constants/roles';
|
import { ROLE, ROLE_LABEL } from '$lib/utils/constants/roles';
|
||||||
import type { CurrentOrg, OrgTeamMember } from '$lib/utils/types/org';
|
import { supabase } from '$lib/utils/functions/supabase';
|
||||||
|
import { currentOrg, orgAudience, orgs, orgTeam } from '$lib/utils/store/org';
|
||||||
import type { OrganizationPlan } from '$lib/utils/types';
|
import type { OrganizationPlan } from '$lib/utils/types';
|
||||||
|
import type { CurrentOrg, OrgTeamMember } from '$lib/utils/types/org';
|
||||||
|
import type { PostgrestError } from '@supabase/supabase-js';
|
||||||
|
import { get } from 'svelte/store';
|
||||||
|
|
||||||
export async function getOrgTeam(orgId: string) {
|
export async function getOrgTeam(orgId: string) {
|
||||||
const { data, error } = await supabase
|
const { data, error } = await supabase
|
||||||
@@ -164,8 +164,6 @@ export async function getOrgAudience(orgId: string) {
|
|||||||
.eq('groupmember.group.organization_id', orgId)
|
.eq('groupmember.group.organization_id', orgId)
|
||||||
.eq('groupmember.role_id', ROLE.STUDENT);
|
.eq('groupmember.role_id', ROLE.STUDENT);
|
||||||
|
|
||||||
console.log('data', data);
|
|
||||||
|
|
||||||
const audience = (data || []).map((profile) => ({
|
const audience = (data || []).map((profile) => ({
|
||||||
id: profile.id,
|
id: profile.id,
|
||||||
name: profile.fullname,
|
name: profile.fullname,
|
||||||
|
|||||||
@@ -1293,7 +1293,29 @@
|
|||||||
"email": "Deine E-Mail",
|
"email": "Deine E-Mail",
|
||||||
"password": "Ihr Passwort",
|
"password": "Ihr Passwort",
|
||||||
"forgot": "Passwort vergessen?",
|
"forgot": "Passwort vergessen?",
|
||||||
"login": "Anmeldung"
|
"login": "Anmeldung",
|
||||||
|
"login_with": "oder loggen Sie sich mit ein",
|
||||||
|
"signup_with": "oder melden Sie sich an",
|
||||||
|
"already_have_account": "Sie haben bereits ein Konto?",
|
||||||
|
"not_registered_yet": "Noch nicht registriert?",
|
||||||
|
"signup": "Melden Sie sich an",
|
||||||
|
"signup_with_google": "Melden Sie sich bei Google an",
|
||||||
|
"login_with_google": "Melden Sie sich mit Google an",
|
||||||
|
"create_to_join": "Erstellen Sie ein kostenloses Konto, um Mitglied zu werden",
|
||||||
|
"login_to_join": "Melden Sie sich an, um beizutreten",
|
||||||
|
"validation": {
|
||||||
|
"unable_to_create_profile": "Profil kann nicht erstellt werden",
|
||||||
|
"user_cannot_be_created": "Benutzer kann nicht erstellt werden"
|
||||||
|
},
|
||||||
|
"fields": {
|
||||||
|
"email": "Ihre E-Mail",
|
||||||
|
"full_name": "Vollständiger Name",
|
||||||
|
"password": "Ihr Passwort",
|
||||||
|
"confirm_password": "Passwort bestätigen",
|
||||||
|
"password_helper_message": "Das Passwort muss mehr als 6 Zeichen lang sein",
|
||||||
|
"create_account": "Benutzerkonto erstellen"
|
||||||
|
},
|
||||||
|
"create_account": "Benutzerkonto erstellen"
|
||||||
},
|
},
|
||||||
"welcome_modal": {
|
"welcome_modal": {
|
||||||
"we_at": "Wir bei",
|
"we_at": "Wir bei",
|
||||||
|
|||||||
@@ -49,7 +49,29 @@
|
|||||||
"email": "Your email",
|
"email": "Your email",
|
||||||
"password": "Your password",
|
"password": "Your password",
|
||||||
"forgot": "Forgot password?",
|
"forgot": "Forgot password?",
|
||||||
"login": "Log In"
|
"login": "Log In",
|
||||||
|
"login_with": "or login with",
|
||||||
|
"signup_with": "or sign up with",
|
||||||
|
"already_have_account": "Already have an account?",
|
||||||
|
"not_registered_yet": "Not registered yet?",
|
||||||
|
"signup": "Sign up",
|
||||||
|
"signup_with_google": "Sign up with Google",
|
||||||
|
"login_with_google": "Login with Google",
|
||||||
|
"create_to_join": "Create a free account to join",
|
||||||
|
"create_account": "Create Account",
|
||||||
|
"login_to_join": "Log in to join",
|
||||||
|
"fields": {
|
||||||
|
"email": "Your Email",
|
||||||
|
"full_name": "Full Name",
|
||||||
|
"password": "Your Password",
|
||||||
|
"confirm_password": "Confirm Password",
|
||||||
|
"password_helper_message": "Password must be more than 6 characters",
|
||||||
|
"create_account": "Create Account"
|
||||||
|
},
|
||||||
|
"validation": {
|
||||||
|
"unable_to_create_profile": "Unable to create profile",
|
||||||
|
"user_cannot_be_created": "User cannot be created"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"profileMenu": {
|
"profileMenu": {
|
||||||
"profile": "PROFILE",
|
"profile": "PROFILE",
|
||||||
|
|||||||
@@ -1293,7 +1293,29 @@
|
|||||||
"email": "Tu correo electrónico",
|
"email": "Tu correo electrónico",
|
||||||
"password": "Tu contraseña",
|
"password": "Tu contraseña",
|
||||||
"forgot": "¿Has olvidado tu contraseña?",
|
"forgot": "¿Has olvidado tu contraseña?",
|
||||||
"login": "Acceso"
|
"login": "Acceso",
|
||||||
|
"login_with": "o inicia sesión con",
|
||||||
|
"signup_with": "o regístrate con",
|
||||||
|
"already_have_account": "¿Ya tienes una cuenta?",
|
||||||
|
"not_registered_yet": "¿Aún no estás registrado?",
|
||||||
|
"signup": "Inscribirse",
|
||||||
|
"signup_with_google": "Regístrate con Google",
|
||||||
|
"login_with_google": "Iniciar sesión con Google",
|
||||||
|
"create_to_join": "Crea una cuenta gratuita para unirte",
|
||||||
|
"login_to_join": "Inicia sesión para unirte",
|
||||||
|
"validation": {
|
||||||
|
"unable_to_create_profile": "No se puede crear el perfil",
|
||||||
|
"user_cannot_be_created": "No se puede crear usuario"
|
||||||
|
},
|
||||||
|
"fields": {
|
||||||
|
"email": "Tu correo electrónico",
|
||||||
|
"full_name": "Nombre completo",
|
||||||
|
"password": "Tu contraseña",
|
||||||
|
"confirm_password": "confirmar Contraseña",
|
||||||
|
"password_helper_message": "La contraseña debe tener más de 6 caracteres.",
|
||||||
|
"create_account": "Crear una cuenta"
|
||||||
|
},
|
||||||
|
"create_account": "Crear una cuenta"
|
||||||
},
|
},
|
||||||
"welcome_modal": {
|
"welcome_modal": {
|
||||||
"we_at": "nosotros en",
|
"we_at": "nosotros en",
|
||||||
|
|||||||
@@ -1293,7 +1293,29 @@
|
|||||||
"email": "Votre email",
|
"email": "Votre email",
|
||||||
"password": "Votre mot de passe",
|
"password": "Votre mot de passe",
|
||||||
"forgot": "Mot de passe oublié?",
|
"forgot": "Mot de passe oublié?",
|
||||||
"login": "Se connecter"
|
"login": "Se connecter",
|
||||||
|
"login_with": "ou connectez-vous avec",
|
||||||
|
"signup_with": "ou inscrivez-vous avec",
|
||||||
|
"already_have_account": "Vous avez déjà un compte ?",
|
||||||
|
"not_registered_yet": "Pas encore inscrit ?",
|
||||||
|
"signup": "S'inscrire",
|
||||||
|
"signup_with_google": "Inscrivez-vous avec Google",
|
||||||
|
"login_with_google": "Connectez-vous avec Google",
|
||||||
|
"create_to_join": "Créez un compte gratuit pour vous inscrire",
|
||||||
|
"login_to_join": "Connectez-vous pour rejoindre",
|
||||||
|
"validation": {
|
||||||
|
"unable_to_create_profile": "Impossible de créer un profil",
|
||||||
|
"user_cannot_be_created": "L'utilisateur ne peut pas être créé"
|
||||||
|
},
|
||||||
|
"fields": {
|
||||||
|
"email": "Votre e-mail",
|
||||||
|
"full_name": "Nom et prénom",
|
||||||
|
"password": "Votre mot de passe",
|
||||||
|
"confirm_password": "Confirmez le mot de passe",
|
||||||
|
"password_helper_message": "Le mot de passe doit comporter plus de 6 caractères",
|
||||||
|
"create_account": "Créer un compte"
|
||||||
|
},
|
||||||
|
"create_account": "Créer un compte"
|
||||||
},
|
},
|
||||||
"welcome_modal": {
|
"welcome_modal": {
|
||||||
"we_at": "Nous à",
|
"we_at": "Nous à",
|
||||||
|
|||||||
@@ -1293,7 +1293,29 @@
|
|||||||
"email": "आपका ईमेल",
|
"email": "आपका ईमेल",
|
||||||
"password": "आपका पासवर्ड",
|
"password": "आपका पासवर्ड",
|
||||||
"forgot": "पासवर्ड भूल गए?",
|
"forgot": "पासवर्ड भूल गए?",
|
||||||
"login": "लॉग इन करें"
|
"login": "लॉग इन करें",
|
||||||
|
"login_with": "या के साथ लॉगिन करें",
|
||||||
|
"signup_with": "या इसके साथ साइन अप करें",
|
||||||
|
"already_have_account": "क्या आपके पास पहले से एक खाता मौजूद है?",
|
||||||
|
"not_registered_yet": "अभी तक पंजीकृत नहीं हैं?",
|
||||||
|
"signup": "साइन अप करें",
|
||||||
|
"signup_with_google": "Google के साथ साइन अप करें",
|
||||||
|
"login_with_google": "गूगल से लॉगिन करें",
|
||||||
|
"create_to_join": "शामिल होने के लिए एक निःशुल्क खाता बनाएँ",
|
||||||
|
"login_to_join": "शामिल होने के लिए लॉग इन करें",
|
||||||
|
"validation": {
|
||||||
|
"unable_to_create_profile": "प्रोफ़ाइल बनाने में असमर्थ",
|
||||||
|
"user_cannot_be_created": "उपयोगकर्ता नहीं बनाया जा सकता"
|
||||||
|
},
|
||||||
|
"fields": {
|
||||||
|
"email": "आपका ईमेल",
|
||||||
|
"full_name": "पूरा नाम",
|
||||||
|
"password": "आपका पासवर्ड",
|
||||||
|
"confirm_password": "पासवर्ड की पुष्टि कीजिये",
|
||||||
|
"password_helper_message": "पासवर्ड 6 अक्षर से अधिक होना चाहिए",
|
||||||
|
"create_account": "खाता बनाएं"
|
||||||
|
},
|
||||||
|
"create_account": "खाता बनाएं"
|
||||||
},
|
},
|
||||||
"welcome_modal": {
|
"welcome_modal": {
|
||||||
"we_at": "हम पर",
|
"we_at": "हम पर",
|
||||||
|
|||||||
@@ -1293,7 +1293,29 @@
|
|||||||
"email": "Seu email",
|
"email": "Seu email",
|
||||||
"password": "Sua senha",
|
"password": "Sua senha",
|
||||||
"forgot": "Esqueceu sua senha?",
|
"forgot": "Esqueceu sua senha?",
|
||||||
"login": "Conecte-se"
|
"login": "Conecte-se",
|
||||||
|
"login_with": "ou faça login com",
|
||||||
|
"signup_with": "ou cadastre-se com",
|
||||||
|
"already_have_account": "Já tem uma conta?",
|
||||||
|
"not_registered_yet": "Ainda não está cadastrado?",
|
||||||
|
"signup": "Inscrever-se",
|
||||||
|
"signup_with_google": "Inscreva-se no Google",
|
||||||
|
"login_with_google": "Faça login com o Google",
|
||||||
|
"create_to_join": "Crie uma conta gratuita para participar",
|
||||||
|
"login_to_join": "Faça login para participar",
|
||||||
|
"validation": {
|
||||||
|
"unable_to_create_profile": "Não foi possível criar o perfil",
|
||||||
|
"user_cannot_be_created": "O usuário não pode ser criado"
|
||||||
|
},
|
||||||
|
"fields": {
|
||||||
|
"email": "Seu e-mail",
|
||||||
|
"full_name": "Nome completo",
|
||||||
|
"password": "Sua senha",
|
||||||
|
"confirm_password": "Confirme sua senha",
|
||||||
|
"password_helper_message": "A senha deve ter mais de 6 caracteres",
|
||||||
|
"create_account": "Criar uma conta"
|
||||||
|
},
|
||||||
|
"create_account": "Criar uma conta"
|
||||||
},
|
},
|
||||||
"welcome_modal": {
|
"welcome_modal": {
|
||||||
"we_at": "Nós em",
|
"we_at": "Nós em",
|
||||||
|
|||||||
@@ -1293,7 +1293,29 @@
|
|||||||
"email": "Ваш адрес электронной почты",
|
"email": "Ваш адрес электронной почты",
|
||||||
"password": "Ваш пароль",
|
"password": "Ваш пароль",
|
||||||
"forgot": "Забыли пароль?",
|
"forgot": "Забыли пароль?",
|
||||||
"login": "Авторизоваться"
|
"login": "Авторизоваться",
|
||||||
|
"login_with": "или войдите с помощью",
|
||||||
|
"signup_with": "или зарегистрируйтесь через",
|
||||||
|
"already_have_account": "У вас уже есть аккаунт?",
|
||||||
|
"not_registered_yet": "Еще не зарегистрированы?",
|
||||||
|
"signup": "Зарегистрироваться",
|
||||||
|
"signup_with_google": "Зарегистрируйтесь через Google",
|
||||||
|
"login_with_google": "Войти через Google",
|
||||||
|
"create_to_join": "Создайте бесплатную учетную запись, чтобы присоединиться",
|
||||||
|
"login_to_join": "Войдите, чтобы присоединиться",
|
||||||
|
"validation": {
|
||||||
|
"unable_to_create_profile": "Невозможно создать профиль",
|
||||||
|
"user_cannot_be_created": "Пользователь не может быть создан"
|
||||||
|
},
|
||||||
|
"fields": {
|
||||||
|
"email": "Ваш адрес электронной почты",
|
||||||
|
"full_name": "Полное имя",
|
||||||
|
"password": "Ваш пароль",
|
||||||
|
"confirm_password": "Подтвердите пароль",
|
||||||
|
"password_helper_message": "Пароль должен быть более 6 символов",
|
||||||
|
"create_account": "Зарегистрироваться"
|
||||||
|
},
|
||||||
|
"create_account": "Зарегистрироваться"
|
||||||
},
|
},
|
||||||
"welcome_modal": {
|
"welcome_modal": {
|
||||||
"we_at": "Мы в",
|
"we_at": "Мы в",
|
||||||
|
|||||||
@@ -1293,7 +1293,29 @@
|
|||||||
"email": "Email của bạn",
|
"email": "Email của bạn",
|
||||||
"password": "Mật khẩu của bạn",
|
"password": "Mật khẩu của bạn",
|
||||||
"forgot": "Quên mật khẩu?",
|
"forgot": "Quên mật khẩu?",
|
||||||
"login": "Đăng nhập"
|
"login": "Đăng nhập",
|
||||||
|
"login_with": "hoặc đăng nhập với",
|
||||||
|
"signup_with": "hoặc đăng ký với",
|
||||||
|
"already_have_account": "Đã có tài khoản?",
|
||||||
|
"not_registered_yet": "Chưa đăng ký?",
|
||||||
|
"signup": "Đăng ký",
|
||||||
|
"signup_with_google": "Đăng ký với Google",
|
||||||
|
"login_with_google": "Đăng nhập bằng Google",
|
||||||
|
"create_to_join": "Tạo một tài khoản miễn phí để tham gia",
|
||||||
|
"login_to_join": "Đăng nhập để tham gia",
|
||||||
|
"validation": {
|
||||||
|
"unable_to_create_profile": "Không thể tạo hồ sơ",
|
||||||
|
"user_cannot_be_created": "Không thể tạo người dùng"
|
||||||
|
},
|
||||||
|
"fields": {
|
||||||
|
"email": "Email của bạn",
|
||||||
|
"full_name": "Tên đầy đủ",
|
||||||
|
"password": "Mật khẩu của bạn",
|
||||||
|
"confirm_password": "Xác nhận mật khẩu",
|
||||||
|
"password_helper_message": "Mật khẩu phải nhiều hơn 6 ký tự",
|
||||||
|
"create_account": "Tạo tài khoản"
|
||||||
|
},
|
||||||
|
"create_account": "Tạo tài khoản"
|
||||||
},
|
},
|
||||||
"welcome_modal": {
|
"welcome_modal": {
|
||||||
"we_at": "Chúng tôi ở",
|
"we_at": "Chúng tôi ở",
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
import type { MetaTagsProps } from 'svelte-meta-tags';
|
import { dev } from '$app/environment';
|
||||||
import { dev, browser } from '$app/environment';
|
|
||||||
import { redirect } from '@sveltejs/kit';
|
|
||||||
import { blockedSubdomain } from '$lib/utils/constants/app';
|
|
||||||
import { getCurrentOrg } from '$lib/utils/services/org';
|
|
||||||
import { getSupabase, supabase } from '$lib/utils/functions/supabase';
|
|
||||||
import { loadTranslations } from '$lib/utils/functions/translations';
|
|
||||||
import type { CurrentOrg } from '$lib/utils/types/org';
|
|
||||||
import { IS_SELFHOSTED } from '$env/static/private';
|
|
||||||
import { env } from '$env/dynamic/private';
|
import { env } from '$env/dynamic/private';
|
||||||
|
import { IS_SELFHOSTED } from '$env/static/private';
|
||||||
|
import { blockedSubdomain } from '$lib/utils/constants/app';
|
||||||
|
import { getSupabase, supabase } from '$lib/utils/functions/supabase';
|
||||||
|
import { getCurrentOrg } from '$lib/utils/services/org';
|
||||||
|
import type { CurrentOrg } from '$lib/utils/types/org';
|
||||||
|
import { redirect } from '@sveltejs/kit';
|
||||||
|
import type { MetaTagsProps } from 'svelte-meta-tags';
|
||||||
|
|
||||||
if (!supabase) {
|
if (!supabase) {
|
||||||
getSupabase();
|
getSupabase();
|
||||||
@@ -19,19 +18,19 @@ interface LoadOutput {
|
|||||||
skipAuth: boolean;
|
skipAuth: boolean;
|
||||||
org: CurrentOrg | null;
|
org: CurrentOrg | null;
|
||||||
baseMetaTags: MetaTagsProps;
|
baseMetaTags: MetaTagsProps;
|
||||||
|
serverLang: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const load = async ({ url, cookies }): Promise<LoadOutput> => {
|
export const load = async ({ url, cookies, request }): Promise<LoadOutput> => {
|
||||||
const response: LoadOutput = {
|
const response: LoadOutput = {
|
||||||
orgSiteName: '',
|
orgSiteName: '',
|
||||||
isOrgSite: false,
|
isOrgSite: false,
|
||||||
skipAuth: false,
|
skipAuth: false,
|
||||||
org: null,
|
org: null,
|
||||||
baseMetaTags: getBaseMetaTags(url)
|
baseMetaTags: getBaseMetaTags(url),
|
||||||
|
serverLang: request.headers?.get('accept-language') || ''
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log('IS_SELFHOSTED', IS_SELFHOSTED);
|
|
||||||
|
|
||||||
// Selfhosted usecase would be here
|
// Selfhosted usecase would be here
|
||||||
if (IS_SELFHOSTED === 'true') {
|
if (IS_SELFHOSTED === 'true') {
|
||||||
const subdomain = getSubdomain(url);
|
const subdomain = getSubdomain(url);
|
||||||
@@ -105,11 +104,6 @@ export const load = async ({ url, cookies }): Promise<LoadOutput> => {
|
|||||||
throw redirect(307, 'https://app.classroomio.com');
|
throw redirect(307, 'https://app.classroomio.com');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load translations
|
|
||||||
const { pathname } = url;
|
|
||||||
const initLocale = getInitialLocale();
|
|
||||||
await loadTranslations(initLocale, pathname);
|
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -151,18 +145,7 @@ function getBaseMetaTags(url: URL) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Define getInitialLocale function
|
|
||||||
function getInitialLocale(): string {
|
|
||||||
if (browser) {
|
|
||||||
try {
|
|
||||||
return window.navigator.language.split('-')[0];
|
|
||||||
} catch (e) {
|
|
||||||
return 'en';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 'en';
|
|
||||||
}
|
|
||||||
|
|
||||||
function getSubdomain(url: URL) {
|
function getSubdomain(url: URL) {
|
||||||
const host = url.host.replace('www.', '');
|
const host = url.host.replace('www.', '');
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { browser } from '$app/environment';
|
import { browser } from '$app/environment';
|
||||||
import { page } from '$app/stores';
|
import { page } from '$app/stores';
|
||||||
|
import debounce from 'lodash/debounce';
|
||||||
|
|
||||||
import Apps from '$lib/components/Apps/index.svelte';
|
import Apps from '$lib/components/Apps/index.svelte';
|
||||||
import { course } from '$lib/components/Course/store';
|
import { course } from '$lib/components/Course/store';
|
||||||
import OrgNavigation from '$lib/components/Navigation/app.svelte';
|
import OrgNavigation from '$lib/components/Navigation/app.svelte';
|
||||||
@@ -22,7 +24,6 @@
|
|||||||
import { globalStore } from '$lib/utils/store/app';
|
import { globalStore } from '$lib/utils/store/app';
|
||||||
import { currentOrg } from '$lib/utils/store/org';
|
import { currentOrg } from '$lib/utils/store/org';
|
||||||
import { isMobile } from '$lib/utils/store/useMobile';
|
import { isMobile } from '$lib/utils/store/useMobile';
|
||||||
import { user } from '$lib/utils/store/user';
|
|
||||||
import { Theme } from 'carbon-components-svelte';
|
import { Theme } from 'carbon-components-svelte';
|
||||||
import type { CarbonTheme } from 'carbon-components-svelte/types/Theme/Theme.svelte';
|
import type { CarbonTheme } from 'carbon-components-svelte/types/Theme/Theme.svelte';
|
||||||
import merge from 'lodash/merge';
|
import merge from 'lodash/merge';
|
||||||
@@ -42,6 +43,8 @@
|
|||||||
isMobile.update(() => window.innerWidth <= 760);
|
isMobile.update(() => window.innerWidth <= 760);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getProfileDebounced = debounce(getProfile, 1000);
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
console.log(
|
console.log(
|
||||||
'Welcome to ClassroomIO, we are grateful you chose us.',
|
'Welcome to ClassroomIO, we are grateful you chose us.',
|
||||||
@@ -77,18 +80,15 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Skip Authentication
|
// Skip Authentication
|
||||||
if (data.skipAuth || $user.fetchingUser) return;
|
if (data.skipAuth) return;
|
||||||
|
|
||||||
// Authentication Steps
|
// Authentication Steps
|
||||||
if (event === 'SIGNED_IN' || event === 'INITIAL_SESSION') {
|
if (event === 'SIGNED_IN' || event === 'INITIAL_SESSION') {
|
||||||
$user.fetchingUser = true;
|
getProfileDebounced({
|
||||||
getProfile({
|
|
||||||
path,
|
path,
|
||||||
queryParam,
|
queryParam,
|
||||||
isOrgSite: data.isOrgSite,
|
isOrgSite: data.isOrgSite,
|
||||||
orgSiteName: data.orgSiteName
|
orgSiteName: data.orgSiteName
|
||||||
}).then(() => {
|
|
||||||
$user.fetchingUser = false;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// else if (!['TOKEN_REFRESHED'].includes(event)) {
|
// else if (!['TOKEN_REFRESHED'].includes(event)) {
|
||||||
@@ -125,6 +125,7 @@
|
|||||||
<Theme bind:theme={carbonTheme} />
|
<Theme bind:theme={carbonTheme} />
|
||||||
|
|
||||||
<UpgradeModal />
|
<UpgradeModal />
|
||||||
|
|
||||||
<Snackbar />
|
<Snackbar />
|
||||||
|
|
||||||
{#if data.org?.is_restricted || $currentOrg.is_restricted}
|
{#if data.org?.is_restricted || $currentOrg.is_restricted}
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import { config, loadTranslations } from '$lib/utils/functions/translations';
|
||||||
|
import { profile } from '$lib/utils/store/user';
|
||||||
|
import { get } from 'svelte/store';
|
||||||
|
|
||||||
|
const SUPPORTED_LANGUAGES = config?.loaders?.map((loader) => loader.locale) || [];
|
||||||
|
|
||||||
|
export const load = async ({ url, data }) => {
|
||||||
|
const { pathname } = url;
|
||||||
|
|
||||||
|
const profileStore = get(profile);
|
||||||
|
|
||||||
|
const serverLang = data.serverLang.split('-')[0];
|
||||||
|
|
||||||
|
const userLocale = profileStore.id ? profileStore.locale : getInitialLocale(serverLang);
|
||||||
|
|
||||||
|
const initLocale = getInitialLocale(userLocale);
|
||||||
|
await loadTranslations(initLocale, pathname); // keep this just before the `return`
|
||||||
|
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
function getInitialLocale(lang: string): string {
|
||||||
|
const locale = lang.split('-')[0];
|
||||||
|
|
||||||
|
if (SUPPORTED_LANGUAGES.includes(locale)) return locale;
|
||||||
|
|
||||||
|
return 'en';
|
||||||
|
}
|
||||||
@@ -1,21 +1,22 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { goto } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
|
import AuthUI from '$lib/components/AuthUI/index.svelte';
|
||||||
|
import type { Profile } from '$lib/components/Course/components/People/types';
|
||||||
import TextField from '$lib/components/Form/TextField.svelte';
|
import TextField from '$lib/components/Form/TextField.svelte';
|
||||||
import PrimaryButton from '$lib/components/PrimaryButton/index.svelte';
|
import PrimaryButton from '$lib/components/PrimaryButton/index.svelte';
|
||||||
|
import { SIGNUP_FIELDS } from '$lib/utils/constants/authentication';
|
||||||
|
import { logout } from '$lib/utils/functions/logout';
|
||||||
import { getSupabase } from '$lib/utils/functions/supabase';
|
import { getSupabase } from '$lib/utils/functions/supabase';
|
||||||
|
import { setTheme } from '$lib/utils/functions/theme';
|
||||||
|
import { t } from '$lib/utils/functions/translations';
|
||||||
import {
|
import {
|
||||||
authValidation,
|
authValidation,
|
||||||
getConfirmPasswordError,
|
getConfirmPasswordError,
|
||||||
getDisableSubmit
|
getDisableSubmit
|
||||||
} from '$lib/utils/functions/validator';
|
} from '$lib/utils/functions/validator';
|
||||||
import { SIGNUP_FIELDS } from '$lib/utils/constants/authentication';
|
|
||||||
import AuthUI from '$lib/components/AuthUI/index.svelte';
|
|
||||||
import { currentOrg, currentOrgPath } from '$lib/utils/store/org';
|
import { currentOrg, currentOrgPath } from '$lib/utils/store/org';
|
||||||
import { setTheme } from '$lib/utils/functions/theme';
|
|
||||||
import type { CurrentOrg } from '$lib/utils/types/org';
|
import type { CurrentOrg } from '$lib/utils/types/org';
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import type { Profile } from '$lib/components/Course/components/People/types';
|
|
||||||
import { logout } from '$lib/utils/functions/logout';
|
|
||||||
|
|
||||||
export let data;
|
export let data;
|
||||||
|
|
||||||
@@ -61,7 +62,7 @@
|
|||||||
if (error) throw error;
|
if (error) throw error;
|
||||||
|
|
||||||
if (!signupData.user) {
|
if (!signupData.user) {
|
||||||
throw 'User cannot be created ';
|
throw $t('login.validation.user_cannot_be_created');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert profile
|
// Insert profile
|
||||||
@@ -86,7 +87,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!profile?.id) {
|
if (!profile?.id) {
|
||||||
throw 'Unable to create profile';
|
throw $t('login.validation.unable_to_create_profile');
|
||||||
}
|
}
|
||||||
|
|
||||||
const res = await supabase.auth.signInWithPassword({
|
const res = await supabase.auth.signInWithPassword({
|
||||||
@@ -142,9 +143,6 @@
|
|||||||
$: disableSubmit = getDisableSubmit(fields);
|
$: disableSubmit = getDisableSubmit(fields);
|
||||||
|
|
||||||
$: console.log('data', data.invite);
|
$: console.log('data', data.invite);
|
||||||
|
|
||||||
$: console.log('org', $currentOrg);
|
|
||||||
$: console.log('path', $currentOrgPath);
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
@@ -153,15 +151,15 @@
|
|||||||
|
|
||||||
<AuthUI {supabase} isLogin={false} {handleSubmit} isLoading={loading} showLogo={true} bind:formRef>
|
<AuthUI {supabase} isLogin={false} {handleSubmit} isLoading={loading} showLogo={true} bind:formRef>
|
||||||
<div class="mt-4 w-full">
|
<div class="mt-4 w-full">
|
||||||
<p class="dark:text-white text-lg font-semibold mb-6">
|
<p class="mb-6 text-lg font-semibold dark:text-white">
|
||||||
{#if data.invite.profile}
|
{#if data.invite.profile}
|
||||||
Log in to join
|
{$t('login.login_to_join')}
|
||||||
{:else}
|
{:else}
|
||||||
Create a free account to join
|
{$t('login.create_to_join')}
|
||||||
{/if}
|
{/if}
|
||||||
</p>
|
</p>
|
||||||
<TextField
|
<TextField
|
||||||
label="Your Email"
|
label={$t('login.fields.email')}
|
||||||
value={data.invite.email}
|
value={data.invite.email}
|
||||||
type="email"
|
type="email"
|
||||||
placeholder="you@domain.com"
|
placeholder="you@domain.com"
|
||||||
@@ -171,7 +169,7 @@
|
|||||||
/>
|
/>
|
||||||
{#if !data.invite.profile}
|
{#if !data.invite.profile}
|
||||||
<TextField
|
<TextField
|
||||||
label="Full Name"
|
label={$t('login.fields.full_name')}
|
||||||
bind:value={fields.name}
|
bind:value={fields.name}
|
||||||
type="text"
|
type="text"
|
||||||
autoFocus={true}
|
autoFocus={true}
|
||||||
@@ -184,7 +182,7 @@
|
|||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
<TextField
|
<TextField
|
||||||
label="Your Password"
|
label={$t('login.fields.password')}
|
||||||
bind:value={fields.password}
|
bind:value={fields.password}
|
||||||
type="password"
|
type="password"
|
||||||
placeholder="************"
|
placeholder="************"
|
||||||
@@ -192,12 +190,12 @@
|
|||||||
inputClassName="w-full"
|
inputClassName="w-full"
|
||||||
isDisabled={loading}
|
isDisabled={loading}
|
||||||
errorMessage={errors.password}
|
errorMessage={errors.password}
|
||||||
helperMessage="Password must be more than 6 characters"
|
helperMessage={$t('login.fields.password_helper_message')}
|
||||||
isRequired
|
isRequired
|
||||||
/>
|
/>
|
||||||
{#if !data.invite.profile}
|
{#if !data.invite.profile}
|
||||||
<TextField
|
<TextField
|
||||||
label="Confirm Password"
|
label={$t('login.fields.confirm_password')}
|
||||||
bind:value={fields.confirmPassword}
|
bind:value={fields.confirmPassword}
|
||||||
type="password"
|
type="password"
|
||||||
placeholder="************"
|
placeholder="************"
|
||||||
@@ -213,7 +211,7 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="my-4 w-full flex justify-end items-center">
|
<div class="my-4 flex w-full items-center justify-end">
|
||||||
<PrimaryButton
|
<PrimaryButton
|
||||||
label="Accept Invite"
|
label="Accept Invite"
|
||||||
type="submit"
|
type="submit"
|
||||||
|
|||||||
@@ -1,15 +1,14 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { page } from '$app/stores';
|
import AuthUI from '$lib/components/AuthUI/index.svelte';
|
||||||
import TextField from '$lib/components/Form/TextField.svelte';
|
import TextField from '$lib/components/Form/TextField.svelte';
|
||||||
import PrimaryButton from '$lib/components/PrimaryButton/index.svelte';
|
import PrimaryButton from '$lib/components/PrimaryButton/index.svelte';
|
||||||
import { getSupabase } from '$lib/utils/functions/supabase';
|
|
||||||
import { authValidation } from '$lib/utils/functions/validator';
|
|
||||||
import { LOGIN_FIELDS } from '$lib/utils/constants/authentication';
|
import { LOGIN_FIELDS } from '$lib/utils/constants/authentication';
|
||||||
import AuthUI from '$lib/components/AuthUI/index.svelte';
|
import { getSupabase } from '$lib/utils/functions/supabase';
|
||||||
import { currentOrg } from '$lib/utils/store/org';
|
|
||||||
import { capturePosthogEvent } from '$lib/utils/services/posthog';
|
|
||||||
import { t } from '$lib/utils/functions/translations';
|
import { t } from '$lib/utils/functions/translations';
|
||||||
|
import { authValidation } from '$lib/utils/functions/validator';
|
||||||
|
import { capturePosthogEvent } from '$lib/utils/services/posthog';
|
||||||
import { globalStore } from '$lib/utils/store/app';
|
import { globalStore } from '$lib/utils/store/app';
|
||||||
|
import { currentOrg } from '$lib/utils/store/org';
|
||||||
|
|
||||||
let formRef: HTMLFormElement;
|
let formRef: HTMLFormElement;
|
||||||
let supabase = getSupabase();
|
let supabase = getSupabase();
|
||||||
@@ -18,9 +17,6 @@
|
|||||||
let loading = false;
|
let loading = false;
|
||||||
let errors = Object.assign({}, LOGIN_FIELDS);
|
let errors = Object.assign({}, LOGIN_FIELDS);
|
||||||
|
|
||||||
let query = new URLSearchParams($page.url.search);
|
|
||||||
let redirect = query.get('redirect');
|
|
||||||
|
|
||||||
async function handleSubmit() {
|
async function handleSubmit() {
|
||||||
const validationRes = authValidation(fields);
|
const validationRes = authValidation(fields);
|
||||||
console.log('validationRes', validationRes);
|
console.log('validationRes', validationRes);
|
||||||
@@ -48,7 +44,7 @@
|
|||||||
email: fields.email
|
email: fields.email
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error: any) {
|
||||||
submitError = error.error_description || error.message;
|
submitError = error.error_description || error.message;
|
||||||
loading = false;
|
loading = false;
|
||||||
}
|
}
|
||||||
@@ -61,7 +57,7 @@
|
|||||||
|
|
||||||
<AuthUI {supabase} isLogin={true} {handleSubmit} isLoading={loading} bind:formRef>
|
<AuthUI {supabase} isLogin={true} {handleSubmit} isLoading={loading} bind:formRef>
|
||||||
<div class="mt-4 w-full">
|
<div class="mt-4 w-full">
|
||||||
<p class="dark:text-white text-lg font-semibold mb-6">{$t('login.welcome')}</p>
|
<p class="mb-6 text-lg font-semibold dark:text-white">{$t('login.welcome')}</p>
|
||||||
<TextField
|
<TextField
|
||||||
label={$t('login.email')}
|
label={$t('login.email')}
|
||||||
bind:value={fields.email}
|
bind:value={fields.email}
|
||||||
@@ -71,7 +67,7 @@
|
|||||||
className="mb-6"
|
className="mb-6"
|
||||||
inputClassName="w-full"
|
inputClassName="w-full"
|
||||||
isDisabled={loading}
|
isDisabled={loading}
|
||||||
errorMessage={errors.email}
|
errorMessage={$t(errors.email)}
|
||||||
/>
|
/>
|
||||||
<TextField
|
<TextField
|
||||||
label={$t('login.password')}
|
label={$t('login.password')}
|
||||||
@@ -81,7 +77,7 @@
|
|||||||
className="mb-6"
|
className="mb-6"
|
||||||
inputClassName="w-full"
|
inputClassName="w-full"
|
||||||
isDisabled={loading}
|
isDisabled={loading}
|
||||||
errorMessage={errors.password}
|
errorMessage={$t(errors.password)}
|
||||||
/>
|
/>
|
||||||
{#if submitError}
|
{#if submitError}
|
||||||
<p class="text-sm text-red-500">{submitError}</p>
|
<p class="text-sm text-red-500">{submitError}</p>
|
||||||
@@ -91,7 +87,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="my-4 w-full flex justify-end items-center">
|
<div class="my-4 flex w-full items-center justify-end">
|
||||||
<!-- <a href="/login" class="text-primary-700 text-sm">Create an account</a> -->
|
<!-- <a href="/login" class="text-primary-700 text-sm">Create an account</a> -->
|
||||||
<PrimaryButton
|
<PrimaryButton
|
||||||
label={$t('login.login')}
|
label={$t('login.login')}
|
||||||
|
|||||||
@@ -9,6 +9,23 @@ async function getGenericData(orgSlug: string): Promise<{
|
|||||||
orgHasAvatarUrl: boolean;
|
orgHasAvatarUrl: boolean;
|
||||||
courseData: any;
|
courseData: any;
|
||||||
}> {
|
}> {
|
||||||
|
const { data: publishedCourse } = await supabase
|
||||||
|
.from('course')
|
||||||
|
.select(
|
||||||
|
`
|
||||||
|
is_published,
|
||||||
|
group:group_id!inner(
|
||||||
|
organization!inner(
|
||||||
|
siteName
|
||||||
|
)
|
||||||
|
)
|
||||||
|
`
|
||||||
|
)
|
||||||
|
.eq('is_published', true)
|
||||||
|
.eq('group.organization.siteName', orgSlug)
|
||||||
|
.limit(1)
|
||||||
|
.returns<{ is_published: boolean }>();
|
||||||
|
|
||||||
const { data } = await supabase
|
const { data } = await supabase
|
||||||
.from('course')
|
.from('course')
|
||||||
.select(
|
.select(
|
||||||
@@ -23,14 +40,13 @@ async function getGenericData(orgSlug: string): Promise<{
|
|||||||
)
|
)
|
||||||
`
|
`
|
||||||
)
|
)
|
||||||
.eq('group.organization.siteName', orgSlug);
|
.eq('group.organization.siteName', orgSlug)
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
const result = data || [];
|
const result = data || [];
|
||||||
|
|
||||||
console.log('courseData', result);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isCoursePublished: result.some((c) => c.is_published === true),
|
isCoursePublished: publishedCourse?.is_published === true,
|
||||||
isCourseCreated: result.length > 0,
|
isCourseCreated: result.length > 0,
|
||||||
orgHasAvatarUrl: !!get(result[0], 'group.organization.avatar_url', ''),
|
orgHasAvatarUrl: !!get(result[0], 'group.organization.avatar_url', ''),
|
||||||
courseData: result
|
courseData: result
|
||||||
@@ -54,7 +70,8 @@ async function getIsLessonCreated(
|
|||||||
)
|
)
|
||||||
`
|
`
|
||||||
)
|
)
|
||||||
.eq('course.group.organization.siteName', orgSlug);
|
.eq('course.group.organization.siteName', orgSlug)
|
||||||
|
.limit(1);
|
||||||
console.log('lessonsData', data);
|
console.log('lessonsData', data);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -80,7 +97,8 @@ async function getIsExerciseCreated(orgSlug: string): Promise<boolean> {
|
|||||||
)
|
)
|
||||||
`
|
`
|
||||||
)
|
)
|
||||||
.eq('lesson.course.group.organization.siteName', orgSlug);
|
.eq('lesson.course.group.organization.siteName', orgSlug)
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
return Array.isArray(data) ? data?.length > 0 : false;
|
return Array.isArray(data) ? data?.length > 0 : false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +1,22 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { goto } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
import { page } from '$app/stores';
|
import { page } from '$app/stores';
|
||||||
|
import AuthUI from '$lib/components/AuthUI/index.svelte';
|
||||||
import TextField from '$lib/components/Form/TextField.svelte';
|
import TextField from '$lib/components/Form/TextField.svelte';
|
||||||
import PrimaryButton from '$lib/components/PrimaryButton/index.svelte';
|
import PrimaryButton from '$lib/components/PrimaryButton/index.svelte';
|
||||||
|
import SenjaEmbed from '$lib/components/Senja/Embed.svelte';
|
||||||
|
import { SIGNUP_FIELDS } from '$lib/utils/constants/authentication';
|
||||||
import { getSupabase } from '$lib/utils/functions/supabase';
|
import { getSupabase } from '$lib/utils/functions/supabase';
|
||||||
|
import { t } from '$lib/utils/functions/translations';
|
||||||
import {
|
import {
|
||||||
authValidation,
|
authValidation,
|
||||||
getConfirmPasswordError,
|
getConfirmPasswordError,
|
||||||
getDisableSubmit
|
getDisableSubmit
|
||||||
} from '$lib/utils/functions/validator';
|
} from '$lib/utils/functions/validator';
|
||||||
import { SIGNUP_FIELDS } from '$lib/utils/constants/authentication';
|
|
||||||
import AuthUI from '$lib/components/AuthUI/index.svelte';
|
|
||||||
import { profile } from '$lib/utils/store/user';
|
|
||||||
import { currentOrg } from '$lib/utils/store/org';
|
|
||||||
import { capturePosthogEvent } from '$lib/utils/services/posthog';
|
import { capturePosthogEvent } from '$lib/utils/services/posthog';
|
||||||
import { globalStore } from '$lib/utils/store/app';
|
import { globalStore } from '$lib/utils/store/app';
|
||||||
import SenjaEmbed from '$lib/components/Senja/Embed.svelte';
|
import { currentOrg } from '$lib/utils/store/org';
|
||||||
|
import { profile } from '$lib/utils/store/user';
|
||||||
|
|
||||||
let supabase = getSupabase();
|
let supabase = getSupabase();
|
||||||
let fields = Object.assign({}, SIGNUP_FIELDS);
|
let fields = Object.assign({}, SIGNUP_FIELDS);
|
||||||
@@ -112,7 +113,7 @@
|
|||||||
formRef?.reset();
|
formRef?.reset();
|
||||||
success = true;
|
success = true;
|
||||||
fields = Object.assign({}, SIGNUP_FIELDS);
|
fields = Object.assign({}, SIGNUP_FIELDS);
|
||||||
} catch (error) {
|
} catch (error: any) {
|
||||||
submitError = error?.error_description || error?.message;
|
submitError = error?.error_description || error?.message;
|
||||||
loading = false;
|
loading = false;
|
||||||
}
|
}
|
||||||
@@ -130,7 +131,7 @@
|
|||||||
|
|
||||||
<AuthUI {supabase} isLogin={false} {handleSubmit} isLoading={loading} bind:formRef>
|
<AuthUI {supabase} isLogin={false} {handleSubmit} isLoading={loading} bind:formRef>
|
||||||
<div class="mt-4 w-full">
|
<div class="mt-4 w-full">
|
||||||
<p class="dark:text-white text-lg font-semibold mb-6">Create a free account</p>
|
<p class="mb-6 text-lg font-semibold dark:text-white">Create a free account</p>
|
||||||
<!-- <TextField
|
<!-- <TextField
|
||||||
label="Full Name"
|
label="Full Name"
|
||||||
bind:value={fields.name}
|
bind:value={fields.name}
|
||||||
@@ -144,30 +145,30 @@
|
|||||||
isRequired
|
isRequired
|
||||||
/> -->
|
/> -->
|
||||||
<TextField
|
<TextField
|
||||||
label="Your Email"
|
label={$t('login.fields.email')}
|
||||||
bind:value={fields.email}
|
bind:value={fields.email}
|
||||||
type="email"
|
type="email"
|
||||||
placeholder="you@domain.com"
|
placeholder="you@domain.com"
|
||||||
className="mb-6"
|
className="mb-6"
|
||||||
inputClassName="w-full"
|
inputClassName="w-full"
|
||||||
isDisabled={loading}
|
isDisabled={loading}
|
||||||
errorMessage={errors.email}
|
errorMessage={$t(errors.email ?? '')}
|
||||||
isRequired
|
isRequired
|
||||||
/>
|
/>
|
||||||
<TextField
|
<TextField
|
||||||
label="Your Password"
|
label={$t('login.fields.password')}
|
||||||
bind:value={fields.password}
|
bind:value={fields.password}
|
||||||
type="password"
|
type="password"
|
||||||
placeholder="************"
|
placeholder="************"
|
||||||
className="mb-6"
|
className="mb-6"
|
||||||
inputClassName="w-full"
|
inputClassName="w-full"
|
||||||
isDisabled={loading}
|
isDisabled={loading}
|
||||||
errorMessage={errors.password}
|
errorMessage={$t(errors.password ?? '')}
|
||||||
helperMessage="Password must be more than 6 characters"
|
helperMessage={$t('login.fields.password_helper_message')}
|
||||||
isRequired
|
isRequired
|
||||||
/>
|
/>
|
||||||
<TextField
|
<TextField
|
||||||
label="Confirm Password"
|
label={$t('login.fields.confirm_password')}
|
||||||
bind:value={fields.confirmPassword}
|
bind:value={fields.confirmPassword}
|
||||||
type="password"
|
type="password"
|
||||||
placeholder="************"
|
placeholder="************"
|
||||||
@@ -182,9 +183,9 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="my-4 w-full flex justify-end items-center">
|
<div class="my-4 flex w-full items-center justify-end">
|
||||||
<PrimaryButton
|
<PrimaryButton
|
||||||
label="Create Account"
|
label={$t('login.create_account')}
|
||||||
type="submit"
|
type="submit"
|
||||||
className="sm:w-full w-full"
|
className="sm:w-full w-full"
|
||||||
isDisabled={disableSubmit || loading}
|
isDisabled={disableSubmit || loading}
|
||||||
|
|||||||
Reference in New Issue
Block a user