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:
@@ -1,34 +1,34 @@
|
||||
<script lang="ts">
|
||||
import { fade } from 'svelte/transition';
|
||||
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 { goto } from '$app/navigation';
|
||||
import Box from '$lib/components/Box/index.svelte';
|
||||
import Card from '$lib/components/Courses/components/Card/index.svelte';
|
||||
import CardLoader from '$lib/components/Courses/components/Card/Loader.svelte';
|
||||
import CoursesEmptyIcon from '$lib/components/Icons/CoursesEmptyIcon.svelte';
|
||||
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 { courseMetaDeta, courses } from '$lib/components/Courses/store';
|
||||
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 { 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 { 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 { 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 org: CurrentOrg | null;
|
||||
@@ -115,7 +115,6 @@
|
||||
if (!siteName) return;
|
||||
|
||||
try {
|
||||
console.log('sitename', siteName);
|
||||
$courseMetaDeta.isLoading = true;
|
||||
const coursesResult = await getCourseBySiteName(siteName);
|
||||
courses.set(coursesResult);
|
||||
@@ -182,7 +181,7 @@
|
||||
{#if $landingPageSettings.header.show}
|
||||
<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
|
||||
? 'bg-cover bg-center'
|
||||
: 'border-b border-gray-300'
|
||||
@@ -196,25 +195,25 @@
|
||||
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}
|
||||
<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
|
||||
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 -->
|
||||
<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">
|
||||
{org.name}
|
||||
</p>
|
||||
<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.titleHighlight}</span
|
||||
>
|
||||
</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}
|
||||
</p>
|
||||
|
||||
@@ -228,7 +227,7 @@
|
||||
/>
|
||||
</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'}
|
||||
<div bind:this={player} id="player" style="width:100%; border-radius:12px">
|
||||
<iframe
|
||||
@@ -256,10 +255,10 @@
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="w-full h-full flex items-center justify-center md:flex-row z-20 relative">
|
||||
<div class="max-w-[600px] mx-auto w-11/12 py-10 flex flex-col items-center">
|
||||
<div class="relative z-20 flex h-full w-full items-center justify-center md:flex-row">
|
||||
<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>
|
||||
<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.titleHighlight}</span
|
||||
>
|
||||
@@ -284,9 +283,9 @@
|
||||
|
||||
<!-- Our Story section -->
|
||||
{#if $landingPageSettings.aboutUs.show}
|
||||
<section id="aboutus" class="m-h-[400px] my-10 mx-auto max-w-6xl w-full">
|
||||
<div class="mx-4 flex items-center justify-evenly flex-col lg:flex-row">
|
||||
<div class="max-w-[600px] lg:w-2/5 mr-5 mb-5 lg:mb-0">
|
||||
<section id="aboutus" class="m-h-[400px] mx-auto my-10 w-full max-w-6xl">
|
||||
<div class="mx-4 flex flex-col items-center justify-evenly lg:flex-row">
|
||||
<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>
|
||||
<p class="mb-2">
|
||||
{$landingPageSettings.aboutUs.content}
|
||||
@@ -297,7 +296,7 @@
|
||||
<img
|
||||
src={$landingPageSettings.aboutUs.imageUrl}
|
||||
alt="Our Story"
|
||||
class=" rounded-2xl max-h-[450px]"
|
||||
class=" max-h-[450px] rounded-2xl"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -306,10 +305,10 @@
|
||||
|
||||
<!-- Courses Section -->
|
||||
{#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="max-w-[500px] mx-auto w-11/12 py-10">
|
||||
<h1 class="text-4xl md:text-5xl lg:text-6xl text-center my-4 font-bold">
|
||||
<div class="mx-auto w-11/12 max-w-[500px] py-10">
|
||||
<h1 class="my-4 text-center text-4xl font-bold md:text-5xl lg:text-6xl">
|
||||
{$landingPageSettings.courses.title}
|
||||
<span class="text-primary-600">{$landingPageSettings.courses.titleHighlight}</span>
|
||||
</h1>
|
||||
@@ -319,13 +318,13 @@
|
||||
</div>
|
||||
</div>
|
||||
{#if $courseMetaDeta.isLoading}
|
||||
<div class="cards-container my-4 mx-2">
|
||||
<div class="cards-container mx-2 my-4">
|
||||
<CardLoader />
|
||||
<CardLoader />
|
||||
<CardLoader />
|
||||
</div>
|
||||
{: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}
|
||||
<Card
|
||||
id={courseData.id}
|
||||
@@ -350,17 +349,17 @@
|
||||
{:else}
|
||||
<Box>
|
||||
<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')}
|
||||
</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')}
|
||||
</p>
|
||||
</Box>
|
||||
{/if}
|
||||
|
||||
{#if $courses.length > 3}
|
||||
<div class="w-full mt-3 flex justify-center">
|
||||
<div class="mt-3 flex w-full justify-center">
|
||||
<PrimaryButton
|
||||
variant={VARIANTS.OUTLINED}
|
||||
onClick={() => (viewAll = !viewAll)}
|
||||
@@ -376,9 +375,9 @@
|
||||
|
||||
<!-- FAQ Section -->
|
||||
{#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">
|
||||
<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}
|
||||
</h1>
|
||||
</div>
|
||||
@@ -398,10 +397,10 @@
|
||||
|
||||
<!-- Contact Section-->
|
||||
{#if $landingPageSettings.contact.show}
|
||||
<section id="contact" transition:fade class="my-10 w-full bg-primary-50">
|
||||
<div class="mx-auto max-w-6xl w-full">
|
||||
<div class="max-w-[500px] mx-auto w-11/12 py-10">
|
||||
<h1 class="text-4xl md:text-5xl lg:text-6xl text-center my-4 font-bold">
|
||||
<section id="contact" transition:fade class="bg-primary-50 my-10 w-full">
|
||||
<div class="mx-auto w-full max-w-6xl">
|
||||
<div class="mx-auto w-11/12 max-w-[500px] py-10">
|
||||
<h1 class="my-4 text-center text-4xl font-bold md:text-5xl lg:text-6xl">
|
||||
{$landingPageSettings.contact.title}
|
||||
<span class="text-primary-600">{$landingPageSettings.contact.titleHighlight}</span>
|
||||
</h1>
|
||||
@@ -411,37 +410,37 @@
|
||||
<!-- Contact Details -->
|
||||
<Row>
|
||||
<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} />
|
||||
<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}
|
||||
</p>
|
||||
</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} />
|
||||
<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
|
||||
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} />
|
||||
<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>
|
||||
</Row>
|
||||
|
||||
<!-- Contact Form -->
|
||||
<div class="mt-8 bg-white p-7 rounded-lg">
|
||||
<div class="mt-8 rounded-lg bg-white p-7">
|
||||
{#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')}
|
||||
</div>
|
||||
{:else}
|
||||
<form on:submit|preventDefault={handleContactSubmit}>
|
||||
<div class="w-full flex justify-between flex-col md:flex-row">
|
||||
<div class="w-full md:w-2/4 mr-5">
|
||||
<div class="flex w-full flex-col justify-between md:flex-row">
|
||||
<div class="mr-5 w-full md:w-2/4">
|
||||
<TextField
|
||||
label={$t('course.navItem.landing_page.name')}
|
||||
bind:value={contact.name}
|
||||
@@ -481,7 +480,7 @@
|
||||
type="submit"
|
||||
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} />
|
||||
</PrimaryButton>
|
||||
</form>
|
||||
@@ -494,12 +493,12 @@
|
||||
|
||||
<!-- Waitlist Section -->
|
||||
{#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
|
||||
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">
|
||||
<h1 class="text-4xl font-bold mb-5 mt-0 text-white">
|
||||
<div class="w-full md:mr-4 md:w-[65%]">
|
||||
<h1 class="mb-5 mt-0 text-4xl font-bold text-white">
|
||||
{$landingPageSettings.mailinglist.title}
|
||||
</h1>
|
||||
<p class="text-lg text-white">
|
||||
@@ -507,7 +506,7 @@
|
||||
</p>
|
||||
</div>
|
||||
<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}
|
||||
<p class="text-white">{$t('course.navItem.landing_page.successful_sub')}</p>
|
||||
{:else}
|
||||
@@ -538,9 +537,9 @@
|
||||
{#if $landingPageSettings.footer.show}
|
||||
<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">
|
||||
<a
|
||||
href="/"
|
||||
@@ -552,16 +551,16 @@
|
||||
<img
|
||||
src={org.avatar_url || '/logo-192.png'}
|
||||
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"
|
||||
/>
|
||||
<h3 class="text-black ml-3 text-xl">{org.name}</h3>
|
||||
<h3 class="ml-3 text-xl text-black">{org.name}</h3>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<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}
|
||||
<li>
|
||||
<a
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
import Modal from '$lib/components/Modal/index.svelte';
|
||||
import PrimaryButton from '$lib/components/PrimaryButton/index.svelte';
|
||||
import { snackbar } from '$lib/components/Snackbar/store';
|
||||
import { profile } from '$lib/utils/store/user';
|
||||
import { t } from '$lib/utils/functions/translations';
|
||||
import {
|
||||
triggerSendEmail,
|
||||
NOTIFICATION_NAME
|
||||
NOTIFICATION_NAME,
|
||||
triggerSendEmail
|
||||
} from '$lib/utils/services/notification/notification';
|
||||
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_TIME = WAIT_SEC * 1000;
|
||||
@@ -51,21 +51,21 @@
|
||||
}, WAIT_TIME);
|
||||
};
|
||||
|
||||
$: open = Boolean(!$profile.is_email_verified && !!$currentOrg.id);
|
||||
$: open = Boolean(!$profile.is_email_verified && !!$profile.id && !!$currentOrg.id);
|
||||
$: open && 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="font-bold text-xl">{$t('verify_email_modal.heading')}</p>
|
||||
<p class="text-sm w-[70%] text-gray-700 dark:text-gray-200">
|
||||
<p class="text-xl font-bold">{$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 items-center flex-col">
|
||||
<div class="flex flex-col items-center">
|
||||
<PrimaryButton
|
||||
isDisabled={loading || isSent}
|
||||
className="font-normal"
|
||||
|
||||
Reference in New Issue
Block a user