feat: setup drizzle (#594)
* chore: migration to SvelteKit 2 * stage 1: migrate to svelte 5 and fix rerendering issues * stage 2: fix more $: reactivity across dashboard * fix merge conflicts * chore: migrate code in main to svelte 5 * chore: update preventDefault * chore: simplify tsconfig and remove unused mobileMenuRef in MobileMenu.svelte * chore: enforce .ts across dashboard * rename from classroomio-com to website * stage-3: Replace carbon icon svelte with lucide icon * update lock file * chore: finish icon replacement * fix: settings and setup page multiple rerender * stage 3: More icon and rerendering fixes * feat: setup drizzle and better auth * fix: onboarding validation error showing translation key #586 (#593) * fix: onboarding validation error showing translation key #586 * fix: remove debug log for profile update errors * chore: add a clean command * fix: remove message from email verification response
This commit is contained in:
@@ -1,30 +0,0 @@
|
||||
import isSubmissionEarly from './isSubmissionEarly';
|
||||
|
||||
describe('isSubmissionEarly', () => {
|
||||
test('should return true when createdAt is earlier than dueDate', () => {
|
||||
const dueDate = Date.now();
|
||||
const createdAt = new Date('2023-12-15');
|
||||
const result = isSubmissionEarly(createdAt, dueDate);
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
test('should return true when both createdAt and dueDate are null', () => {
|
||||
const dueDate = null;
|
||||
const createdAt = null;
|
||||
const result = isSubmissionEarly(createdAt, dueDate);
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
test('should return true when createdAt is the same as dueDate', () => {
|
||||
const dueDate = new Date('2023-12-15');
|
||||
const createdAt = new Date('2023-12-15');
|
||||
const result = isSubmissionEarly(createdAt, dueDate);
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
test('should return false when createdAt is later than dueDate', () => {
|
||||
const dueDate = new Date('2023-12-15');
|
||||
const createdAt = new Date('2023-12-16');
|
||||
const result = isSubmissionEarly(createdAt, dueDate);
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -83,7 +83,7 @@ export async function getProfile({
|
||||
}
|
||||
|
||||
// Check if user has profile
|
||||
let {
|
||||
const {
|
||||
data: profileData,
|
||||
error,
|
||||
status
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
import { isCourseFree } from './course';
|
||||
|
||||
describe('course.ts', () => {
|
||||
test('Should return True when cost is 0', () => {
|
||||
const result = isCourseFree(0);
|
||||
expect(result).toBeTruthy();
|
||||
});
|
||||
|
||||
test('Should return true when cost is negative', () => {
|
||||
const result = isCourseFree(-10);
|
||||
expect(result).toBeTruthy();
|
||||
});
|
||||
|
||||
test('Should return True when cost is Not A Number', () => {
|
||||
const result = isCourseFree(NaN);
|
||||
expect(result).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -1,36 +0,0 @@
|
||||
import { calDateDiff, getGreeting } from './date';
|
||||
|
||||
describe('calDateDiff', () => {
|
||||
test('should return the correct time difference when the input date is in the past', () => {
|
||||
const inputDate = new Date('2022-01-01');
|
||||
const expectedOutput = '2 years ago';
|
||||
|
||||
const result = calDateDiff(inputDate);
|
||||
|
||||
expect(result).toBe(expectedOutput);
|
||||
});
|
||||
|
||||
test('should return the correct time difference when the input date is a number', () => {
|
||||
const inputDate = new Date(1640995200000); // January 1, 2022
|
||||
const expectedOutput = '2 years ago';
|
||||
|
||||
const result = calDateDiff(inputDate);
|
||||
|
||||
expect(result).toBe(expectedOutput);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getGreeting', () => {
|
||||
test("should return 'Good Morning' when the current time is before 12pm", () => {
|
||||
jest.useFakeTimers().setSystemTime(new Date('2023-12-10T00:21:01.691Z'));
|
||||
expect(getGreeting()).toBe('Good Morning');
|
||||
});
|
||||
test("should return 'Good Afternoon' when the current time is after 12pm", () => {
|
||||
jest.useFakeTimers().setSystemTime(new Date('2023-12-10T14:21:01.691Z'));
|
||||
expect(getGreeting()).toBe('Good Afternoon');
|
||||
});
|
||||
test("should return 'Good Evening' when the current time is after 6pm", () => {
|
||||
jest.useFakeTimers().setSystemTime(new Date('2023-12-10T19:21:01.691Z'));
|
||||
expect(getGreeting()).toBe('Good Evening');
|
||||
});
|
||||
});
|
||||
@@ -1,46 +0,0 @@
|
||||
import { formatYoutubeVideo, getEmbedId } from './formatYoutubeVideo';
|
||||
|
||||
describe('format youtube video', () => {
|
||||
test('should return the same url when given a valid youtube embed url', () => {
|
||||
const url = 'https://www.youtube.com/embed/qajK1J1neAM';
|
||||
const errors = { video: '' };
|
||||
const result = formatYoutubeVideo(url, errors);
|
||||
expect(result).toBe(url);
|
||||
});
|
||||
|
||||
test('should return the correct embed url when given a youtube url with a start time parameter', () => {
|
||||
const url = 'https://www.youtube.com/watch?v=qajK1J1neAM&start=123';
|
||||
const errors = { video: '' };
|
||||
const result = formatYoutubeVideo(url, errors);
|
||||
expect(result).toBe('https://www.youtube.com/embed/qajK1J1neAM');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getEmbedId', () => {
|
||||
test('should return the correct embed id when the url contains "embed"', () => {
|
||||
const url = 'https://www.youtube.com/embed/qajK1J1neAM';
|
||||
const expectedEmbedId = 'qajK1J1neAM';
|
||||
|
||||
const result = getEmbedId(url);
|
||||
|
||||
expect(result).toBe(expectedEmbedId);
|
||||
});
|
||||
|
||||
test('should return an empty string when the url does not contain "watch" or "embed" and is not in the format "youtu.be/"', () => {
|
||||
const url = 'https://www.youtube.com/invalid-url';
|
||||
const expectedEmbedId = '';
|
||||
|
||||
const result = getEmbedId(url);
|
||||
|
||||
expect(result).toBe(expectedEmbedId);
|
||||
});
|
||||
|
||||
test('should return an empty string when the url is an empty string', () => {
|
||||
const url = '';
|
||||
const expectedEmbedId = '';
|
||||
|
||||
const result = getEmbedId(url);
|
||||
|
||||
expect(result).toBe(expectedEmbedId);
|
||||
});
|
||||
});
|
||||
@@ -1,25 +0,0 @@
|
||||
import genUniqueId from './genUniqueId';
|
||||
|
||||
describe('Generate Unique Ids', () => {
|
||||
test('should start with letter', () => {
|
||||
const result = genUniqueId();
|
||||
const firstChar = result.charAt(0);
|
||||
expect(firstChar).toMatch(/[a-zA-Z]/);
|
||||
});
|
||||
|
||||
test('should not contain special characters', () => {
|
||||
const result = genUniqueId();
|
||||
expect(result).toMatch(/^[a-zA-Z0-9]+$/);
|
||||
});
|
||||
|
||||
test('should return different ids on multiple calls', () => {
|
||||
const result1 = genUniqueId();
|
||||
const result2 = genUniqueId();
|
||||
expect(result1).not.toEqual(result2);
|
||||
});
|
||||
test('multiple calls in closer time frames should have same first 3 letters', () => {
|
||||
const result1 = genUniqueId();
|
||||
const result2 = genUniqueId();
|
||||
expect(result1.substring(0, 4)).toEqual(result2.substring(0, 4));
|
||||
});
|
||||
});
|
||||
@@ -1,17 +0,0 @@
|
||||
import generateSlug from './generateSlug';
|
||||
|
||||
describe('generateSlug', () => {
|
||||
test('should return a string with lowercase letters and hyphens when given a valid title', () => {
|
||||
const title = 'This is a Valid Title';
|
||||
const expected = 'this-is-a-valid-title' + `-${new Date().getTime()}`;
|
||||
const result = generateSlug(title);
|
||||
expect(result).toBe(expected);
|
||||
});
|
||||
|
||||
test('should include the current timestamp at the end of the string', () => {
|
||||
const title = 'This is a Valid Title';
|
||||
const result = generateSlug(title);
|
||||
const timestamp = result.split('-').pop();
|
||||
expect(Number(timestamp)).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
@@ -1,26 +0,0 @@
|
||||
import generateUUID from './generateUUID';
|
||||
import { isUUID } from './isUUID';
|
||||
|
||||
// This test suite tests both the generateUUID and isUUID functions
|
||||
describe('GenerateUUID', () => {
|
||||
test('should be true when tested with the uuid function', () => {
|
||||
const result = generateUUID();
|
||||
expect(isUUID(result)).toBe(true);
|
||||
});
|
||||
|
||||
test('should generate unique UUIDs each time', () => {
|
||||
const uuid1 = generateUUID();
|
||||
const uuid2 = generateUUID();
|
||||
expect(uuid1).not.toBe(uuid2);
|
||||
});
|
||||
|
||||
test('should return true for a valid UUID', () => {
|
||||
const uuid = '123e4567-e89b-12d3-a456-426614174000';
|
||||
expect(isUUID(uuid)).toBe(true);
|
||||
});
|
||||
|
||||
test('should return false for an invalid UUID', () => {
|
||||
const uuid = 'invalid-uuid';
|
||||
expect(isUUID(uuid)).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -1,9 +1,9 @@
|
||||
export default function generateUUID() {
|
||||
// Public Domain/MIT
|
||||
var d = new Date().getTime(); //Timestamp
|
||||
var d2 = (typeof performance !== 'undefined' && performance.now && performance.now() * 1000) || 0; //Time in microseconds since page-load or 0 if unsupported
|
||||
let d = new Date().getTime(); //Timestamp
|
||||
let d2 = (typeof performance !== 'undefined' && performance.now && performance.now() * 1000) || 0; //Time in microseconds since page-load or 0 if unsupported
|
||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
|
||||
var r = Math.random() * 16; //random number between 0 and 16
|
||||
let r = Math.random() * 16; //random number between 0 and 16
|
||||
if (d > 0) {
|
||||
//Use timestamp until depleted
|
||||
r = (d + r) % 16 | 0;
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
import getCurrencyFormatter from './getCurrencyFormatter';
|
||||
|
||||
describe('GetCurrencyFormatter', () => {
|
||||
test('should return a new instance of Intl.NumberFormat with the correct locale and currency options when currency is "NGN"', () => {
|
||||
const result = getCurrencyFormatter('NGN');
|
||||
expect(result).toBeInstanceOf(Intl.NumberFormat);
|
||||
expect(result.resolvedOptions().locale).toBe('en-NG');
|
||||
expect(result.resolvedOptions().currency).toBe('NGN');
|
||||
expect(result.resolvedOptions().minimumFractionDigits).toBe(2);
|
||||
});
|
||||
|
||||
test('should return a new instance of Intl.NumberFormat with the correct locale and currency options when currency is "USD"', () => {
|
||||
const result = getCurrencyFormatter('USD');
|
||||
expect(result).toBeInstanceOf(Intl.NumberFormat);
|
||||
expect(result.resolvedOptions().locale).toBe('en-US');
|
||||
expect(result.resolvedOptions().currency).toBe('USD');
|
||||
expect(result.resolvedOptions().minimumFractionDigits).toBe(2);
|
||||
});
|
||||
|
||||
test('should return a new instance of Intl.NumberFormat with the correct locale and currency options when currency is "EUR"', () => {
|
||||
const result = getCurrencyFormatter('EUR');
|
||||
expect(result).toBeInstanceOf(Intl.NumberFormat);
|
||||
expect(result.resolvedOptions().locale).toBe('en-US');
|
||||
expect(result.resolvedOptions().currency).toBe('EUR');
|
||||
expect(result.resolvedOptions().minimumFractionDigits).toBe(2);
|
||||
});
|
||||
});
|
||||
@@ -1,23 +0,0 @@
|
||||
import { isObject } from './isObject';
|
||||
|
||||
describe('isObject', () => {
|
||||
test('should return true for an empty object', () => {
|
||||
const obj = {};
|
||||
expect(isObject(obj)).toBe(true);
|
||||
});
|
||||
|
||||
test('should return false for a non-object', () => {
|
||||
const obj = 'not an object';
|
||||
expect(isObject(obj)).toBe(false);
|
||||
});
|
||||
|
||||
test('should return true for a nested object', () => {
|
||||
const obj = { prop1: { prop2: 'value2' } };
|
||||
expect(isObject(obj)).toBe(true);
|
||||
});
|
||||
|
||||
test('should return true for an object with properties', () => {
|
||||
const obj = { prop1: 'value1', prop2: 'value2' };
|
||||
expect(isObject(obj)).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -1,3 +0,0 @@
|
||||
export default (created_at, due_date) => {
|
||||
return !!due_date ? !!(new Date(created_at).getTime() <= new Date(due_date).getTime()) : true;
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
export default (created_at, due_date) => {
|
||||
return due_date ? !!(new Date(created_at).getTime() <= new Date(due_date).getTime()) : true;
|
||||
};
|
||||
@@ -1,5 +1,5 @@
|
||||
export function isUUID(uuid: string) {
|
||||
let s = '' + uuid;
|
||||
const s = '' + uuid;
|
||||
|
||||
const m: RegExpMatchArray | null = s.match(
|
||||
'^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$'
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
import { genQuizPin, generateSitename } from './org';
|
||||
|
||||
describe('Generate Quiz Pin', () => {
|
||||
test('should return a random 6-digit pin', () => {
|
||||
const result = genQuizPin();
|
||||
expect(result.toString().length).toBe(6);
|
||||
});
|
||||
|
||||
test('should return random pin each time', () => {
|
||||
const result1 = genQuizPin();
|
||||
const result2 = genQuizPin();
|
||||
expect(result1).not.toBe(result2);
|
||||
});
|
||||
|
||||
test('should generate a pin between 100000 and 999999', () => {
|
||||
const result = genQuizPin();
|
||||
expect(result).toBeGreaterThanOrEqual(100000);
|
||||
expect(result).toBeLessThanOrEqual(999999);
|
||||
});
|
||||
|
||||
test('should not generate a negative pin', () => {
|
||||
const result = genQuizPin();
|
||||
expect(result).toBeGreaterThanOrEqual(0);
|
||||
});
|
||||
|
||||
test('should generate an integer pin', () => {
|
||||
const result = genQuizPin();
|
||||
expect(Number.isInteger(result)).toBe(true);
|
||||
});
|
||||
|
||||
test('should not generate a zero pin', () => {
|
||||
const result = genQuizPin();
|
||||
expect(result).not.toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Generate Site name', () => {
|
||||
test('should return slug-like string', () => {
|
||||
const result = generateSitename('classroom org international');
|
||||
const expected = 'classroom-org-international';
|
||||
expect(result).toBe(expected);
|
||||
});
|
||||
test('should remove special characters', () => {
|
||||
const result = generateSitename('classroom@ org! international');
|
||||
const expected = 'classroom-org-international';
|
||||
expect(result).toBe(expected);
|
||||
});
|
||||
|
||||
test('should return empty string when orgName is an empty string', () => {
|
||||
const result = generateSitename('');
|
||||
const expected = '';
|
||||
expect(result).toBe(expected);
|
||||
});
|
||||
|
||||
test('should return slug-like string with all uppercase characters converted to lowercase', () => {
|
||||
const result = generateSitename('Classroom Org International');
|
||||
const expected = 'classroom-org-international';
|
||||
expect(result).toBe(expected);
|
||||
});
|
||||
});
|
||||
@@ -1,8 +1,8 @@
|
||||
import { goto } from '$app/navigation';
|
||||
|
||||
export function genQuizPin(): number {
|
||||
var minm = 100000;
|
||||
var maxm = 999999;
|
||||
const minm = 100000;
|
||||
const maxm = 999999;
|
||||
return Math.floor(Math.random() * (maxm - minm + 1)) + minm;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
import { removeDuplicate } from './removeDuplicate';
|
||||
|
||||
describe('removeDuplicate', () => {
|
||||
test('should remove duplicate elements of an array', () => {
|
||||
const result = removeDuplicate([1, 1, 2, 3, 3, 4, 5]);
|
||||
const expected = [1, 2, 3, 4, 5];
|
||||
expect(result).toStrictEqual(expected);
|
||||
expect(result.length).toBe(5);
|
||||
});
|
||||
|
||||
test('should return an empty array', () => {
|
||||
const result = removeDuplicate([]);
|
||||
const expected = [];
|
||||
expect(result).toStrictEqual(expected);
|
||||
expect(result.length).toBe(0);
|
||||
});
|
||||
|
||||
test('should return an array with the same element', () => {
|
||||
const result = removeDuplicate([1]);
|
||||
const expected = [1];
|
||||
expect(result).toStrictEqual(expected);
|
||||
expect(result.length).toBe(1);
|
||||
});
|
||||
|
||||
test('should return an array with only one element', () => {
|
||||
const result = removeDuplicate([1, 1, 1, 1]);
|
||||
const expected = [1];
|
||||
expect(result).toStrictEqual(expected);
|
||||
expect(result.length).toBe(1);
|
||||
});
|
||||
|
||||
test('should return an array with the unique elements in the same order as the original array', () => {
|
||||
const result = removeDuplicate([1, 2, 3, 4, 5, 1, 2, 3, 4, 5]);
|
||||
const expected = [1, 2, 3, 4, 5];
|
||||
expect(result).toStrictEqual(expected);
|
||||
expect(result.length).toBe(5);
|
||||
});
|
||||
});
|
||||
@@ -1,26 +0,0 @@
|
||||
import showAppsSideBar from './showAppsSideBar';
|
||||
describe('ShowAppSideBar', () => {
|
||||
test("should return true when path includes '/lessons/' and does not include 'landingpage'", () => {
|
||||
const path = '/lessons/example';
|
||||
const result = showAppsSideBar(path);
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
test("should return false when path does not include '/lessons/'", () => {
|
||||
const path = '/home';
|
||||
const result = showAppsSideBar(path);
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
test("should return false when path includes 'landingpage'", () => {
|
||||
const path = '/lessons/landingpage';
|
||||
const result = showAppsSideBar(path);
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
test('should return false when path is empty', () => {
|
||||
const path = '';
|
||||
const result = showAppsSideBar(path);
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -1,28 +0,0 @@
|
||||
import { capitalizeFirstLetter } from './string';
|
||||
|
||||
describe('capitalize first letter', () => {
|
||||
test('should return the same string with the first letter capitalized', () => {
|
||||
const result = capitalizeFirstLetter('classroom');
|
||||
expect(result).toEqual('Classroom');
|
||||
});
|
||||
|
||||
test('should return an empty string when given an empty string', () => {
|
||||
const result = capitalizeFirstLetter('');
|
||||
expect(result).toEqual('');
|
||||
});
|
||||
|
||||
test('should return the same string when given a string with all uppercase letters', () => {
|
||||
const result = capitalizeFirstLetter('UPPERCASE');
|
||||
expect(result).toEqual('UPPERCASE');
|
||||
});
|
||||
|
||||
test('should return the same string when given a string with a non-alphabetical first character', () => {
|
||||
const result = capitalizeFirstLetter('123abc');
|
||||
expect(result).toEqual('123abc');
|
||||
});
|
||||
|
||||
test('should return the first letter of the first word capitalized when given a sentence', () => {
|
||||
const result = capitalizeFirstLetter('classroomio is an open source project');
|
||||
expect(result).toEqual('Classroomio is an open source project');
|
||||
});
|
||||
});
|
||||
@@ -12,17 +12,13 @@ export let supabase: SupabaseClient;
|
||||
export const getServerSupabase = () => {
|
||||
if (supabase) return supabase;
|
||||
|
||||
supabase = createClient(
|
||||
config.supabaseConfig.url,
|
||||
env.PRIVATE_SUPABASE_SERVICE_ROLE || 'some-key-here',
|
||||
{
|
||||
auth: {
|
||||
persistSession: false,
|
||||
autoRefreshToken: false,
|
||||
detectSessionInUrl: false
|
||||
}
|
||||
supabase = createClient(config.supabaseConfig.url, env.PRIVATE_SUPABASE_SERVICE_ROLE || 'some-key-here', {
|
||||
auth: {
|
||||
persistSession: false,
|
||||
autoRefreshToken: false,
|
||||
detectSessionInUrl: false
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
return supabase;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
export function once(fn) {
|
||||
return function (event) {
|
||||
// @ts-ignore
|
||||
if (fn) fn.call(this, event);
|
||||
fn = null;
|
||||
};
|
||||
}
|
||||
|
||||
export function preventDefault(fn?: Function) {
|
||||
return function (event: Event) {
|
||||
event.preventDefault();
|
||||
|
||||
if (!fn) return;
|
||||
|
||||
// @ts-ignore
|
||||
fn.call(this, event);
|
||||
};
|
||||
}
|
||||
|
||||
export function stopPropagation(fn?: Function) {
|
||||
return function (event: Event) {
|
||||
event.stopPropagation();
|
||||
|
||||
if (!fn) return;
|
||||
|
||||
// @ts-ignore
|
||||
fn.call(this, event);
|
||||
};
|
||||
}
|
||||
@@ -1,14 +1,14 @@
|
||||
import { env } from '$env/dynamic/public';
|
||||
|
||||
var win, output, textarea;
|
||||
let win, output, textarea;
|
||||
|
||||
// Follow this guide to get source code: https://editor.codecogs.com/docs/3-Tiny_MCE_v6.php
|
||||
export const addMathPlugin = (tinymce: any) => {
|
||||
tinymce.PluginManager.add('eqneditor', function (editor, url) {
|
||||
// Load necessary javascript for editor from CodeCogs
|
||||
var sl = new tinymce.dom.ScriptLoader();
|
||||
const sl = new tinymce.dom.ScriptLoader();
|
||||
sl.add('https://assets.cdn.clsrio.com/eqneditor.api.min.js');
|
||||
var loaded = sl.loadQueue();
|
||||
const loaded = sl.loadQueue();
|
||||
|
||||
// Load custom CSS
|
||||
tinymce.DOM.loadCSS('https://assets.cdn.clsrio.com/eqneditor_1.css');
|
||||
@@ -44,7 +44,7 @@ export const addMathPlugin = (tinymce: any) => {
|
||||
);
|
||||
|
||||
function showDialog(inp = '') {
|
||||
var http = 'https:' == document.location.protocol ? 'https://' : 'http://';
|
||||
const http = 'https:' == document.location.protocol ? 'https://' : 'http://';
|
||||
|
||||
win = editor.windowManager.open({
|
||||
title: 'Equation Editor',
|
||||
@@ -124,8 +124,8 @@ export const addMathPlugin = (tinymce: any) => {
|
||||
.addHistoryMenu(new EqEditor.History('history'));
|
||||
EqEditor.Toolbar.link('toolbar').addTextArea(textarea);
|
||||
|
||||
var nodes = document.getElementById('history')?.childNodes || [];
|
||||
for (var i = 0; i < nodes.length; i++) {
|
||||
const nodes = document.getElementById('history')?.childNodes || [];
|
||||
for (let i = 0; i < nodes.length; i++) {
|
||||
nodes[i].style.padding = 'revert';
|
||||
}
|
||||
|
||||
@@ -146,7 +146,7 @@ export const addMathPlugin = (tinymce: any) => {
|
||||
|
||||
editor.on('dblclick', function (e) {
|
||||
if (e.target.nodeName.toLowerCase() == 'img') {
|
||||
var sName = e.target.src.match(/(gif|svg)\.image\?(.*)/);
|
||||
const sName = e.target.src.match(/(gif|svg)\.image\?(.*)/);
|
||||
if (sName != null) showDialog(sName[2]);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -59,8 +59,7 @@ export const config = {
|
||||
]
|
||||
};
|
||||
|
||||
export const { t, loading, locales, locale, initialized, translations, loadTranslations } =
|
||||
new i18n(config);
|
||||
export const { t, loading, locales, locale, initialized, translations, loadTranslations } = new i18n(config);
|
||||
|
||||
export const selectedLocale = writable<string>('en');
|
||||
|
||||
@@ -73,7 +72,7 @@ loading.subscribe(async ($loading) => {
|
||||
}
|
||||
});
|
||||
|
||||
export function handleLocaleChange(newLocale: string) {
|
||||
export function handleLocaleChange(newLocale: LOCALE) {
|
||||
if (!newLocale) {
|
||||
return;
|
||||
}
|
||||
@@ -83,11 +82,7 @@ export function handleLocaleChange(newLocale: string) {
|
||||
selectedLocale.set(newLocale);
|
||||
}
|
||||
|
||||
export function lessonFallbackNote(
|
||||
note: string,
|
||||
translation: Record<LOCALE, string>,
|
||||
locale: LOCALE
|
||||
) {
|
||||
export function lessonFallbackNote(note: string, translation: Record<LOCALE, string>, locale: LOCALE) {
|
||||
if (!translation) {
|
||||
return note;
|
||||
}
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
import { validateEmail } from './validateEmail';
|
||||
|
||||
describe('validate email', () => {
|
||||
test('should return true for a valid email format', () => {
|
||||
const result = validateEmail('[email protected]');
|
||||
expect(result).toBeTruthy();
|
||||
});
|
||||
|
||||
test('should return false for an empty email', () => {
|
||||
const result = validateEmail('');
|
||||
expect(result).toBeFalsy();
|
||||
});
|
||||
|
||||
test('should return true for a valid email with uppercase letters', () => {
|
||||
const result = validateEmail('[email protected]');
|
||||
expect(result).toBeTruthy();
|
||||
});
|
||||
|
||||
test('should return false for an invalid email format', () => {
|
||||
const result = validateEmail('test@example');
|
||||
expect(result).toBeFalsy();
|
||||
});
|
||||
|
||||
test('should return true for a valid email with special characters', () => {
|
||||
const result = validateEmail('[email protected]');
|
||||
expect(result).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -2,141 +2,40 @@ import z from 'zod';
|
||||
import { get } from 'svelte/store';
|
||||
import isNumber from 'lodash/isNumber';
|
||||
import { validateEmail } from './validateEmail';
|
||||
import { t as _t, initialized } from '$lib/utils/functions/translations';
|
||||
import { t, initialized } from '$lib/utils/functions/translations';
|
||||
|
||||
const t = {
|
||||
get: (key: string) => {
|
||||
const isInitialized = get(initialized);
|
||||
if (!isInitialized) return key;
|
||||
|
||||
return _t.get(key);
|
||||
}
|
||||
};
|
||||
|
||||
const getOrgNameValidation = () =>
|
||||
z
|
||||
function getOrgNameValidation() {
|
||||
return z
|
||||
.string()
|
||||
.min(5, { message: `${t.get('validations.organization_name.min_char')}` })
|
||||
.refine((val) => !/^[-]|[-]$/.test(val), {
|
||||
message: `${t.get('validations.organization_name.hyphen_rule')}`
|
||||
});
|
||||
}
|
||||
|
||||
const getSiteNameValidation = () =>
|
||||
z
|
||||
function getSiteNameValidation() {
|
||||
return z
|
||||
.string()
|
||||
.min(5, { message: `${t.get('validations.site_name.min_char')}` })
|
||||
.refine((val) => !/^[-]|[-]$/.test(val), {
|
||||
message: `${t.get('validations.site_name.hyphen_rule')}`
|
||||
});
|
||||
|
||||
const getNewsfeedValidation = () =>
|
||||
z.string().min(5, { message: `${t.get('validations.news_feed.min_char')}` });
|
||||
|
||||
const lessonSchema = z.object({
|
||||
title: z.string().nonempty({ message: `${t.get('validations.lesson_schema.empty_title')}` }),
|
||||
lesson_at: z.string().optional(),
|
||||
call_url: z.string().nullable().optional(),
|
||||
is_unlocked: z.boolean().optional()
|
||||
});
|
||||
|
||||
const createQuizValidationSchema = z.object({
|
||||
title: z.string().min(6, {
|
||||
message: `${t.get('validations.course_payment.full_name.min_char')}`
|
||||
})
|
||||
});
|
||||
|
||||
const askCommunityValidationSchema = z.object({
|
||||
title: z.string().min(6, {
|
||||
message: `${t.get('validations.ask_community.title.min_char')}`
|
||||
}),
|
||||
body: z.string().min(20, {
|
||||
message: `${t.get('validations.ask_community.body.min_char')}`
|
||||
}),
|
||||
courseId: z.string().min(36, {
|
||||
message: `${t.get('validations.ask_community.course_id.select_course')}`
|
||||
})
|
||||
});
|
||||
const commentInCommunityValidationSchema = z.object({
|
||||
comment: z.string().min(6, {
|
||||
message: `${t.get('validations.comment_in_community.min_char')}`
|
||||
})
|
||||
});
|
||||
|
||||
const orgLandingpageValidationSchema = z.object({
|
||||
name: z.string().min(6, {
|
||||
message: `${t.get('validations.org_landing_page.name.min_char')}`
|
||||
}),
|
||||
email: z.string().email({
|
||||
message: `${t.get('validations.org_landing_page.email.invalid_email')}`
|
||||
}),
|
||||
phone: z.string().min(6, {
|
||||
message: `${t.get('validations.org_landing_page.phone.min_char')}`
|
||||
}),
|
||||
message: z.string().min(20, {
|
||||
message: `${t.get('validations.org_landing_page.message.min_char')}`
|
||||
})
|
||||
});
|
||||
|
||||
const forgotValidationSchema = z.object({
|
||||
email: z.string().email({
|
||||
message: `${t.get('validations.forgot.invalid_email')}`
|
||||
})
|
||||
});
|
||||
|
||||
const authValidationSchema = z.object({
|
||||
email: z.string().email({
|
||||
message: 'validations.auth.email.invalid_email'
|
||||
}),
|
||||
password: z.string().min(6, {
|
||||
message: 'validations.auth.password.min_char'
|
||||
})
|
||||
});
|
||||
|
||||
const resetValidationSchema = z.object({
|
||||
password: z.string().min(6, {
|
||||
message: `${t.get('validations.reset.password.min_char')}`
|
||||
})
|
||||
});
|
||||
|
||||
const onboardingValidationSchema = {
|
||||
stepOne: z.object({
|
||||
fullname: z
|
||||
.string()
|
||||
.min(5, { message: `${t.get('validations.onboarding.step_one.full_name.min_char')}` }),
|
||||
orgName: getOrgNameValidation(),
|
||||
siteName: getSiteNameValidation()
|
||||
}),
|
||||
stepTwo: z.object({
|
||||
goal: z
|
||||
.string({
|
||||
required_error: `${t.get('validations.onboarding.step_two.goal.required')}`
|
||||
})
|
||||
.min(1),
|
||||
source: z
|
||||
.string({
|
||||
required_error: `${t.get('validations.onboarding.step_two.source.required')}`
|
||||
})
|
||||
.min(1)
|
||||
})
|
||||
};
|
||||
|
||||
const saveCertificateSchema = z.object({
|
||||
description: z.string().max(200, 'course.navItem.certificates.description_error'),
|
||||
is_certificate_downloadable: z.boolean(),
|
||||
certificate_theme: z.string()
|
||||
});
|
||||
}
|
||||
function getNewsfeedValidation() {
|
||||
return z.string().min(5, { message: `${t.get('validations.news_feed.min_char')}` });
|
||||
}
|
||||
|
||||
export const saveCertificateValidation = (fields = {}) => {
|
||||
const { error } = saveCertificateSchema.safeParse(fields);
|
||||
const schema = z.object({
|
||||
description: z.string().max(200, 'course.navItem.certificates.description_error'),
|
||||
is_certificate_downloadable: z.boolean(),
|
||||
certificate_theme: z.string()
|
||||
});
|
||||
|
||||
const { error } = schema.safeParse(fields);
|
||||
return processErrors(error);
|
||||
};
|
||||
|
||||
const updateProfileValidationSchema = z.object({
|
||||
email: z.string().email({ message: 'validations.user_profile.email' }),
|
||||
username: z.string().nonempty({ message: 'validations.user_profile.username' }),
|
||||
fullname: z.string().min(5, { message: 'validations.user_profile.fullname' })
|
||||
});
|
||||
|
||||
export const getConfirmPasswordError = ({ password, confirmPassword }) => {
|
||||
return password > 6 && confirmPassword > 6 && password !== confirmPassword
|
||||
? `${t.get('validations.confirm_password.not_match')}`
|
||||
@@ -173,12 +72,30 @@ export const processErrors = (error, mapToId?: boolean) => {
|
||||
};
|
||||
|
||||
export const authValidation = (fields = {}) => {
|
||||
const { error } = authValidationSchema.safeParse(fields);
|
||||
const schema = z.object({
|
||||
email: z.string().email({
|
||||
message: 'validations.auth.email.invalid_email'
|
||||
}),
|
||||
password: z.string().min(6, {
|
||||
message: 'validations.auth.password.min_char'
|
||||
})
|
||||
});
|
||||
|
||||
const { error } = schema.safeParse(fields);
|
||||
|
||||
return processErrors(error);
|
||||
};
|
||||
|
||||
export const lessonValidation = (lesson = {}) => {
|
||||
const { error } = lessonSchema.safeParse(lesson);
|
||||
const schema = z.object({
|
||||
title: z.string().nonempty({ message: `${t.get('validations.lesson_schema.empty_title')}` }),
|
||||
lesson_at: z.string().optional(),
|
||||
call_url: z.string().nullable().optional(),
|
||||
is_unlocked: z.boolean().optional()
|
||||
});
|
||||
|
||||
const { error } = schema.safeParse(lesson);
|
||||
|
||||
return processErrors(error);
|
||||
};
|
||||
|
||||
@@ -197,24 +114,70 @@ export const coursePaymentValidation = (fields = {}) => {
|
||||
};
|
||||
|
||||
export const resetValidation = (fields = {}) => {
|
||||
const { error } = resetValidationSchema.safeParse(fields);
|
||||
const schema = z.object({
|
||||
password: z.string().min(6, {
|
||||
message: `${t.get('validations.reset.password.min_char')}`
|
||||
})
|
||||
});
|
||||
const { error } = schema.safeParse(fields);
|
||||
|
||||
return processErrors(error);
|
||||
};
|
||||
|
||||
export const forgotValidation = (fields = {}) => {
|
||||
const { error } = forgotValidationSchema.safeParse(fields);
|
||||
const schema = z.object({
|
||||
email: z.string().email({
|
||||
message: `${t.get('validations.forgot.invalid_email')}`
|
||||
})
|
||||
});
|
||||
const { error } = schema.safeParse(fields);
|
||||
|
||||
return processErrors(error);
|
||||
};
|
||||
|
||||
export const orgLandingpageValidation = (fields = {}) => {
|
||||
const { error } = orgLandingpageValidationSchema.safeParse(fields);
|
||||
const schema = z.object({
|
||||
name: z.string().min(6, {
|
||||
message: `${t.get('validations.org_landing_page.name.min_char')}`
|
||||
}),
|
||||
email: z.string().email({
|
||||
message: `${t.get('validations.org_landing_page.email.invalid_email')}`
|
||||
}),
|
||||
phone: z.string().min(6, {
|
||||
message: `${t.get('validations.org_landing_page.phone.min_char')}`
|
||||
}),
|
||||
message: z.string().min(20, {
|
||||
message: `${t.get('validations.org_landing_page.message.min_char')}`
|
||||
})
|
||||
});
|
||||
const { error } = schema.safeParse(fields);
|
||||
|
||||
return processErrors(error);
|
||||
};
|
||||
|
||||
export const onboardingValidation = (fields = {}, step) => {
|
||||
const onboardingValidationSchema = {
|
||||
stepOne: z.object({
|
||||
fullname: z
|
||||
.string()
|
||||
.min(5, { message: `${t.get('validations.onboarding.step_one.full_name.min_char')}` }),
|
||||
orgName: getOrgNameValidation(),
|
||||
siteName: getSiteNameValidation()
|
||||
}),
|
||||
stepTwo: z.object({
|
||||
goal: z
|
||||
.string({
|
||||
required_error: `${t.get('validations.onboarding.step_two.goal.required')}`
|
||||
})
|
||||
.min(1),
|
||||
source: z
|
||||
.string({
|
||||
required_error: `${t.get('validations.onboarding.step_two.source.required')}`
|
||||
})
|
||||
.min(1)
|
||||
})
|
||||
};
|
||||
|
||||
const schema =
|
||||
step === 1 ? onboardingValidationSchema.stepOne : onboardingValidationSchema.stepTwo;
|
||||
const { error } = schema.safeParse(fields);
|
||||
@@ -223,7 +186,11 @@ export const onboardingValidation = (fields = {}, step) => {
|
||||
};
|
||||
|
||||
export const updateProfileValidation = (fields = {}) => {
|
||||
const schema = updateProfileValidationSchema;
|
||||
const schema = z.object({
|
||||
email: z.string().email({ message: 'validations.user_profile.email' }),
|
||||
username: z.string().nonempty({ message: 'validations.user_profile.username' }),
|
||||
fullname: z.string().min(5, { message: 'validations.user_profile.fullname' })
|
||||
});
|
||||
const { error } = schema.safeParse(fields);
|
||||
|
||||
return processErrors(error);
|
||||
@@ -297,19 +264,42 @@ export const updateProfileEmailValidation = (email) => {
|
||||
};
|
||||
|
||||
export const createQuizValidation = (fields = {}) => {
|
||||
const { error } = createQuizValidationSchema.safeParse(fields);
|
||||
const schema = z.object({
|
||||
title: z.string().min(6, {
|
||||
message: `${t.get('validations.course_payment.full_name.min_char')}`
|
||||
})
|
||||
});
|
||||
|
||||
const { error } = schema.safeParse(fields);
|
||||
|
||||
return processErrors(error);
|
||||
};
|
||||
|
||||
export const askCommunityValidation = (fields = {}) => {
|
||||
const { error } = askCommunityValidationSchema.safeParse(fields);
|
||||
const schema = z.object({
|
||||
title: z.string().min(6, {
|
||||
message: `${t.get('validations.ask_community.title.min_char')}`
|
||||
}),
|
||||
body: z.string().min(20, {
|
||||
message: `${t.get('validations.ask_community.body.min_char')}`
|
||||
}),
|
||||
courseId: z.string().min(36, {
|
||||
message: `${t.get('validations.ask_community.course_id.select_course')}`
|
||||
})
|
||||
});
|
||||
|
||||
const { error } = schema.safeParse(fields);
|
||||
|
||||
return processErrors(error);
|
||||
};
|
||||
|
||||
export const commentInCommunityValidation = (fields = {}) => {
|
||||
const { error } = commentInCommunityValidationSchema.safeParse(fields);
|
||||
const schema = z.object({
|
||||
comment: z.string().min(6, {
|
||||
message: `${t.get('validations.comment_in_community.min_char')}`
|
||||
})
|
||||
});
|
||||
const { error } = schema.safeParse(fields);
|
||||
|
||||
return processErrors(error);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user