feat: multi language support (#254)
* feat: multitranslational support * - completion of settings page - completion of sidebar * sidebar rerendering bug fix * completion of the lesson navigation & materials page * completion of the course navigation * completion of requested languages * - Implementation of Language Picker - Rename Bug Fixes * checkpoint save * - addition of the language picker to the onboarding. - addition of the language switcher for the lesson notes. * merge completion * initialization of the new branch * add transition.svelte file * lesson note bug fix * feat: add translation migration * completion of translation * fix height issue of dropdown * refactoring * fix: improve code with LOCALE from db * finally get it fully functional * connect with AI * backward compatibility for lesson note * preserve old content, when generating AI content * undo translations in upgrade modal * fix duplicate key issue from codacy trigger --------- Co-authored-by: Rotimi Best <best@classroomio.com>
This commit is contained in:
co-authored by
Rotimi Best
parent
ff7d2cee35
commit
d75e2f258e
@@ -14,6 +14,7 @@
|
||||
import { group } from '$lib/components/Course/store';
|
||||
import { profile } from '$lib/utils/store/user';
|
||||
import { calDateDiff } from '$lib/utils/functions/date';
|
||||
import { t } from '$lib/utils/functions/translations';
|
||||
|
||||
export let handleClose = () => {};
|
||||
|
||||
@@ -49,7 +50,7 @@
|
||||
...comments,
|
||||
{
|
||||
comment: comment,
|
||||
name: 'You',
|
||||
name: $t('course.navItem.lessons.comments.you'),
|
||||
avatar: $profile.avatar_url,
|
||||
commentAt: new Date()
|
||||
}
|
||||
@@ -165,7 +166,7 @@
|
||||
commentAt: lessonComment.created_at,
|
||||
name:
|
||||
lessonComment.groupmember.id === groupmember?.id
|
||||
? 'You'
|
||||
? $t('course.navItem.lessons.comments.you')
|
||||
: lessonComment.groupmember.profile.fullname
|
||||
};
|
||||
});
|
||||
@@ -191,7 +192,7 @@
|
||||
$: !isFetched && fetchComments($group.people, $lesson.id);
|
||||
</script>
|
||||
|
||||
<PageNav title="Comments" overidableStyle="padding: 0 10px">
|
||||
<PageNav title={$t('course.navItem.lessons.comments.title')} overidableStyle="padding: 0 10px">
|
||||
<div slot="widget">
|
||||
<CloseButton onClick={handleClose} />
|
||||
</div>
|
||||
@@ -224,7 +225,7 @@
|
||||
|
||||
<div class="footer">
|
||||
<TextField
|
||||
placeholder="Say something"
|
||||
placeholder={$t('course.navItem.lessons.comments.placeholder')}
|
||||
bind:value={comment}
|
||||
onKeyDown={handleKeyDown}
|
||||
isDisabled={isSaving}
|
||||
|
||||
+13
-8
@@ -8,6 +8,7 @@
|
||||
import TextField from '$lib/components/Form/TextField.svelte';
|
||||
import { Add, TrashCan } from 'carbon-icons-svelte';
|
||||
import IconButton from '$lib/components/IconButton/index.svelte';
|
||||
import { t } from '$lib/utils/functions/translations';
|
||||
|
||||
export let title = 'Poll';
|
||||
export let isSaving = false;
|
||||
@@ -20,7 +21,7 @@
|
||||
question: '',
|
||||
expiration: new Date().toDateString(),
|
||||
isPublic: false,
|
||||
status: 'draft',
|
||||
status: $t('course.navItem.lessons.poll.draft'),
|
||||
author: {
|
||||
id: $profile.id || '',
|
||||
username: $profile.username || '',
|
||||
@@ -70,14 +71,14 @@
|
||||
</div>
|
||||
<div class="p-3">
|
||||
<TextField
|
||||
label="Question"
|
||||
label={$t('course.navItem.lessons.poll.question')}
|
||||
className="w-full mb-3"
|
||||
bind:value={poll.question}
|
||||
placeholder="Poll question"
|
||||
placeholder={$t('course.navItem.lessons.poll.poll_question')}
|
||||
isRequired={true}
|
||||
/>
|
||||
<DateTime
|
||||
label="Expiration"
|
||||
label={$t('course.navItem.lessons.poll.expiration')}
|
||||
className="w-full mb-3"
|
||||
bind:value={poll.expiration}
|
||||
isRequired={true}
|
||||
@@ -85,7 +86,7 @@
|
||||
|
||||
<div>
|
||||
<p class="dark:text-white p-0 m-0 mb-1 text-md flex items-center gap-2">
|
||||
Options
|
||||
{$t('course.navItem.lessons.poll.options')}
|
||||
|
||||
<IconButton onClick={handleAddOptions} contained={true} size="small">
|
||||
<Add size={16} />
|
||||
@@ -99,7 +100,7 @@
|
||||
label=""
|
||||
className="w-full"
|
||||
bind:value={option.label}
|
||||
placeholder="Option Label"
|
||||
placeholder={$t('course.navItem.lessons.poll.option_label')}
|
||||
isRequired={true}
|
||||
/>
|
||||
<IconButton onClick={handleRemoveOptions(index)} contained={true} size="small">
|
||||
@@ -111,11 +112,15 @@
|
||||
|
||||
<div class="w-full flex justify-center gap-2 mb-3">
|
||||
<PrimaryButton
|
||||
label="Create poll"
|
||||
label={$t('course.navItem.lessons.poll.create_poll')}
|
||||
onClick={finishPoll}
|
||||
isLoading={isSaving}
|
||||
isDisabled={isCreateDisabled}
|
||||
/>
|
||||
<PrimaryButton label="Cancel" variant={VARIANTS.OUTLINED} onClick={onCancel} />
|
||||
<PrimaryButton
|
||||
label={$t('course.navItem.lessons.poll.cancel')}
|
||||
variant={VARIANTS.OUTLINED}
|
||||
onClick={onCancel}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
<script lang="ts">
|
||||
<script script lang="ts">
|
||||
import PrimaryButton from '$lib/components/PrimaryButton/index.svelte';
|
||||
import { VARIANTS } from '$lib/components/PrimaryButton/constants';
|
||||
import Label from './Label.svelte';
|
||||
import Avatar from './Avatar.svelte';
|
||||
import type { PollType } from '../types';
|
||||
import { updatePollStatus } from '../service';
|
||||
import { t } from '$lib/utils/functions/translations';
|
||||
import { Dashboard } from 'carbon-icons-svelte';
|
||||
|
||||
export let poll: PollType;
|
||||
export let onSelect: (a: string) => void;
|
||||
@@ -62,7 +64,7 @@
|
||||
{`<`}
|
||||
</button>
|
||||
<div class="flex-grow text-center">
|
||||
<h3 class="dark:text-white font-bold">Voted</h3>
|
||||
<h3 class="dark:text-white font-bold">{$t('course.navItem.lessons.poll.voted')}</h3>
|
||||
</div>
|
||||
</div>
|
||||
{#each selectedOptionToView.selectedBy as user}
|
||||
@@ -81,13 +83,16 @@
|
||||
{poll.question}
|
||||
</h3>
|
||||
<p class="dark:text-white text-sm italic">
|
||||
Created by <span class="text-primary-500">@{poll.author.username}</span>
|
||||
{$t('course.navItem.lessons.poll.created_by')}
|
||||
<span class="text-primary-500">@{poll.author.username}</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{#if isAuthor}
|
||||
<div class="flex mb-3 justify-end items-center">
|
||||
<label class="mr-2 font-bold" for="user-select">Status:</label>
|
||||
<label class="mr-2 font-bold" for="user-select"
|
||||
>{$t('course.navItem.lessons.poll.status')}:</label
|
||||
>
|
||||
|
||||
<select
|
||||
name="users"
|
||||
@@ -95,8 +100,8 @@
|
||||
class="dark:text-white dark:bg-black"
|
||||
bind:value={poll.status}
|
||||
>
|
||||
<option value="draft">Draft</option>
|
||||
<option value="published">Publish</option>
|
||||
<option value="draft">{$t('course.navItem.lessons.poll.draft')}</option>
|
||||
<option value="published">{$t('course.navItem.lessons.poll.publish')}</option>
|
||||
</select>
|
||||
</div>
|
||||
{/if}
|
||||
@@ -105,20 +110,20 @@
|
||||
{#if viewResult}
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="mr-5">
|
||||
<Label>Expiration</Label>
|
||||
<Label>{$t('course.navItem.lessons.poll.expiration')}</Label>
|
||||
<p class="dark:text-white text-sm text-gray-600 mb-4">
|
||||
{poll.expiration || 'Today'}
|
||||
{poll.expiration || $t('dashboard.today')}
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex flex-col items-center">
|
||||
<Label>Total Votes</Label>
|
||||
<Label>{$t('course.navItem.lessons.poll.total_votes')}</Label>
|
||||
<p class="dark:text-white text-sm text-gray-600 mb-4">
|
||||
{totalVotes}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Label>Responses</Label>
|
||||
<Label>{$t('course.navItem.lessons.poll.responses')}</Label>
|
||||
{#each poll.options as option}
|
||||
<div class="border-b p-5">
|
||||
<p class="dark:text-white">
|
||||
@@ -176,11 +181,11 @@
|
||||
{/each}
|
||||
<div class="flex items-center">
|
||||
<p class="dark:text-white text-sm text-gray-600">
|
||||
Expires: {poll.expiration || 'Today'}
|
||||
{$t('course.navItem.lessons.poll.expires')}: {poll.expiration || $t('dashboard.today')}
|
||||
</p>
|
||||
<span class="mx-2">|</span>
|
||||
<p class="dark:text-white text-sm text-gray-600">
|
||||
Voted: {totalVotes}
|
||||
{$t('course.navItem.lessons.poll.voted')}: {totalVotes}
|
||||
</p>
|
||||
</div>
|
||||
{/if}
|
||||
@@ -188,10 +193,16 @@
|
||||
{#if isAuthor}
|
||||
<div class="w-full flex items-center mt-3">
|
||||
<PrimaryButton
|
||||
label={viewResult ? 'Back' : 'Result'}
|
||||
label={viewResult
|
||||
? $t('course.navItem.lessons.poll.back')
|
||||
: $t('course.navItem.lessons.poll.result')}
|
||||
onClick={() => (viewResult = !viewResult)}
|
||||
/>
|
||||
<PrimaryButton label="Delete" variant={VARIANTS.TEXT_DANGER} onClick={handlePollDelete} />
|
||||
<PrimaryButton
|
||||
label={$t('course.navItem.lessons.poll.delete')}
|
||||
variant={VARIANTS.TEXT_DANGER}
|
||||
onClick={handlePollDelete}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
RealtimePostgresChangesPayload,
|
||||
PostgrestSingleResponse
|
||||
} from '@supabase/supabase-js';
|
||||
import { t } from '$lib/utils/functions/translations';
|
||||
|
||||
export let handleClose = () => {};
|
||||
|
||||
@@ -62,7 +63,7 @@
|
||||
|
||||
if (error || !data) {
|
||||
console.log(error);
|
||||
snackbar.error('An error occurred while creating poll');
|
||||
snackbar.error('snackbar.poll.error.creating_poll');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -78,7 +79,7 @@
|
||||
|
||||
if (appsPollOptionsError || !appsPollOptionsData) {
|
||||
console.log({ appsPollOptionsError });
|
||||
snackbar.error('An error occurred while creating poll');
|
||||
snackbar.error('snackbar.poll.error.creating_poll');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -229,12 +230,12 @@
|
||||
);
|
||||
tabs = [
|
||||
{
|
||||
label: 'Active polls',
|
||||
label: $t('course.navItem.lessons.polls.active_polls'),
|
||||
value: 0,
|
||||
number: activePolls.length
|
||||
},
|
||||
{
|
||||
label: 'Expired polls',
|
||||
label: $t('course.navItem.lessons.polls.expired_polls'),
|
||||
value: 1,
|
||||
number: expiredPolls.length
|
||||
}
|
||||
@@ -242,7 +243,11 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<PageNav title="Poll" overidableStyle="padding: 0 10px" paddingClass="w-full">
|
||||
<PageNav
|
||||
title={$t('course.navItem.lessons.polls.title')}
|
||||
overidableStyle="padding: 0 10px"
|
||||
paddingClass="w-full"
|
||||
>
|
||||
<div slot="widget">
|
||||
<CloseButton onClick={handleClose} />
|
||||
</div>
|
||||
@@ -260,7 +265,7 @@
|
||||
<Tabs {tabs} bind:selectedTab onCreate={() => (openCreatePollForm = !openCreatePollForm)} />
|
||||
|
||||
{#if isLoading}
|
||||
Loading...
|
||||
{$t('course.navItem.lessons.polls.loading')}...
|
||||
{:else}
|
||||
{#each selectedTab === tabs[0].value ? activePolls : expiredPolls as poll}
|
||||
<Poll
|
||||
@@ -273,7 +278,7 @@
|
||||
<div
|
||||
class="bg-gray-100 dark:bg-neutral-800 border rounded-md h-60 flex items-center justify-center"
|
||||
>
|
||||
<h2 class="text-xl font-bold">No polls to display</h2>
|
||||
<h2 class="text-xl font-bold">{$t('course.navItem.lessons.polls.no_polls')}</h2>
|
||||
</div>
|
||||
{/each}
|
||||
{/if}
|
||||
|
||||
@@ -45,7 +45,7 @@ export const updatePollStatus = async (pollId: string, status: PollType['status'
|
||||
|
||||
if (error) {
|
||||
console.log(error);
|
||||
snackbar.error('An error occurred while updating poll status');
|
||||
snackbar.error('snackbar.poll.error.updating_poll');
|
||||
return;
|
||||
}
|
||||
};
|
||||
@@ -64,7 +64,7 @@ export const togglePollSubmission = async (
|
||||
});
|
||||
if (error) {
|
||||
console.log(error);
|
||||
snackbar.error('An error occurred while submitting poll');
|
||||
snackbar.error('snackbar.poll.error.submitting_poll');
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
@@ -76,7 +76,7 @@ export const togglePollSubmission = async (
|
||||
|
||||
if (error) {
|
||||
console.log(error);
|
||||
snackbar.error('An error occurred while submitting poll');
|
||||
snackbar.error('snackbar.poll.error.submitting_poll');
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -101,7 +101,7 @@ export function handleVote(pollId: string, groupmemberId: string, author: PollTy
|
||||
if (poll.id === pollId) {
|
||||
// Prevent user from voting on a poll that the status is not published
|
||||
if (poll.status !== 'published') {
|
||||
snackbar.info('Poll is not published yet');
|
||||
snackbar.info('snackbar.poll.info.not_published');
|
||||
return poll;
|
||||
}
|
||||
|
||||
@@ -109,7 +109,7 @@ export function handleVote(pollId: string, groupmemberId: string, author: PollTy
|
||||
if (
|
||||
poll.options.some((option) => isOptionSelectedByCurrentUser(option, groupmemberId))
|
||||
) {
|
||||
snackbar.info('You can only vote once');
|
||||
snackbar.info('snackbar.poll.info.vote_once');
|
||||
return poll;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user