CLASSROOMIO-T-216: add posthug
This commit is contained in:
@@ -28,7 +28,7 @@ Ultimately I want this project to become the Wordpress or Linux for Education. A
|
|||||||
|
|
||||||
## Installation of modules
|
## Installation of modules
|
||||||
|
|
||||||
```
|
```js
|
||||||
npm i --legacy-peer-deps --registry=https://registry.npmjs.org
|
npm i --legacy-peer-deps --registry=https://registry.npmjs.org
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -36,7 +36,7 @@ Note: Make sure you are using Node 18
|
|||||||
|
|
||||||
## Environment variable
|
## Environment variable
|
||||||
|
|
||||||
```
|
```env
|
||||||
PUBLIC_SUPABASE_ANON_KEY=
|
PUBLIC_SUPABASE_ANON_KEY=
|
||||||
PUBLIC_SUPABASE_URL=
|
PUBLIC_SUPABASE_URL=
|
||||||
NETLIFY_API_KEY=
|
NETLIFY_API_KEY=
|
||||||
|
|||||||
@@ -44,6 +44,7 @@
|
|||||||
"nodemailer": "^6.9.5",
|
"nodemailer": "^6.9.5",
|
||||||
"openai-edge": "^1.2.2",
|
"openai-edge": "^1.2.2",
|
||||||
"pluralize": "^8.0.0",
|
"pluralize": "^8.0.0",
|
||||||
|
"posthog-js": "^1.87.2",
|
||||||
"sirv": "^2.0.3",
|
"sirv": "^2.0.3",
|
||||||
"svelte-email": "^0.0.4",
|
"svelte-email": "^0.0.4",
|
||||||
"unsplash-js": "^7.0.18",
|
"unsplash-js": "^7.0.18",
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
<svelte:head>
|
|
||||||
<!-- Hotjar Tracking Code for https://classroomio.com -->
|
|
||||||
<!-- Hotjar Tracking Code for https://classroomio.com -->
|
|
||||||
<script>
|
|
||||||
(function (h, o, t, j, a, r) {
|
|
||||||
h.hj =
|
|
||||||
h.hj ||
|
|
||||||
function () {
|
|
||||||
(h.hj.q = h.hj.q || []).push(arguments);
|
|
||||||
};
|
|
||||||
h._hjSettings = { hjid: 3679523, hjsv: 6 };
|
|
||||||
a = o.getElementsByTagName('head')[0];
|
|
||||||
r = o.createElement('script');
|
|
||||||
r.async = 1;
|
|
||||||
r.src = t + h._hjSettings.hjid + j + h._hjSettings.hjsv;
|
|
||||||
a.appendChild(r);
|
|
||||||
})(window, document, 'https://static.hotjar.com/c/hotjar-', '.js?sv=');
|
|
||||||
</script>
|
|
||||||
</svelte:head>
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
import { writable } from 'svelte/store';
|
|
||||||
|
|
||||||
export const user = writable({
|
|
||||||
openAuthModal: false,
|
|
||||||
fetchingUser: false,
|
|
||||||
isLoggedIn: false,
|
|
||||||
currentSession: null,
|
|
||||||
expiresAt: 0
|
|
||||||
});
|
|
||||||
|
|
||||||
export const profile = writable({
|
|
||||||
id: null,
|
|
||||||
fullname: '',
|
|
||||||
avatar_url: 'https://pbs.twimg.com/profile_images/1416443682157473795/dGtFbtht_normal.jpg',
|
|
||||||
username: 'Elon',
|
|
||||||
user_id: '',
|
|
||||||
email: '',
|
|
||||||
role: '',
|
|
||||||
goal: '',
|
|
||||||
source: '',
|
|
||||||
telegram_chat_id: null
|
|
||||||
});
|
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
import { writable } from 'svelte/store';
|
||||||
|
import type { User } from '@supabase/supabase-js';
|
||||||
|
|
||||||
|
export const user = writable<{
|
||||||
|
openAuthModal: boolean;
|
||||||
|
fetchingUser: boolean;
|
||||||
|
isLoggedIn: boolean;
|
||||||
|
currentSession: User | undefined;
|
||||||
|
expiresAt: number;
|
||||||
|
}>({
|
||||||
|
openAuthModal: false,
|
||||||
|
fetchingUser: false,
|
||||||
|
isLoggedIn: false,
|
||||||
|
currentSession: undefined,
|
||||||
|
expiresAt: 0
|
||||||
|
});
|
||||||
|
|
||||||
|
export const profile = writable<{
|
||||||
|
id: string | number | undefined;
|
||||||
|
fullname: string;
|
||||||
|
avatar_url: string;
|
||||||
|
profile_url?: string;
|
||||||
|
username: string;
|
||||||
|
user_id: string;
|
||||||
|
email: string;
|
||||||
|
role: string;
|
||||||
|
goal: string;
|
||||||
|
source: string;
|
||||||
|
telegram_chat_id: number | null;
|
||||||
|
}>({
|
||||||
|
id: undefined,
|
||||||
|
fullname: '',
|
||||||
|
avatar_url: 'https://pbs.twimg.com/profile_images/1416443682157473795/dGtFbtht_normal.jpg',
|
||||||
|
username: 'Elon',
|
||||||
|
user_id: '',
|
||||||
|
email: '',
|
||||||
|
role: '',
|
||||||
|
goal: '',
|
||||||
|
source: '',
|
||||||
|
telegram_chat_id: null
|
||||||
|
});
|
||||||
@@ -2,22 +2,31 @@
|
|||||||
import { blockedSubdomain } from '$lib/utils/constants/app';
|
import { blockedSubdomain } from '$lib/utils/constants/app';
|
||||||
import { getCurrentOrg } from '$lib/utils/services/org';
|
import { getCurrentOrg } from '$lib/utils/services/org';
|
||||||
import { getSupabase, supabase } from '$lib/utils/functions/supabase';
|
import { getSupabase, supabase } from '$lib/utils/functions/supabase';
|
||||||
|
import type { CurrentOrg } from '$lib/utils/types/org';
|
||||||
|
|
||||||
if (!supabase) {
|
if (!supabase) {
|
||||||
getSupabase();
|
getSupabase();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @type {import('./$types').LayoutServerLoad} */
|
interface LoadOutput {
|
||||||
export const load = async ({ url, cookies }) => {
|
orgSiteName: string;
|
||||||
|
isOrgSite: boolean;
|
||||||
|
skipAuth: boolean;
|
||||||
|
org: CurrentOrg;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const load = async ({ url, cookies }): LoadOutput => {
|
||||||
// if (url.hostname === 'classroomio.com') {
|
// if (url.hostname === 'classroomio.com') {
|
||||||
// throw redirect(301, 'https://about.classroomio.com');
|
// throw redirect(301, 'https://about.classroomio.com');
|
||||||
// }
|
// }
|
||||||
|
|
||||||
let response = {
|
let response: LoadOutput = {
|
||||||
orgSiteName: '',
|
orgSiteName: '',
|
||||||
isOrgSite: false,
|
isOrgSite: false,
|
||||||
skipAuth: false,
|
skipAuth: false,
|
||||||
org: {}
|
org: {
|
||||||
|
theme: ''
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const _orgSiteName = cookies.get('_orgSiteName');
|
const _orgSiteName = cookies.get('_orgSiteName');
|
||||||
+46
-28
@@ -1,4 +1,4 @@
|
|||||||
<script>
|
<script lang="ts">
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import { derived } from 'svelte/store';
|
import { derived } from 'svelte/store';
|
||||||
import { goto } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
@@ -8,7 +8,9 @@
|
|||||||
import * as Sentry from '@sentry/browser';
|
import * as Sentry from '@sentry/browser';
|
||||||
import { Integrations } from '@sentry/tracing';
|
import { Integrations } from '@sentry/tracing';
|
||||||
import { CaptureConsole } from '@sentry/integrations';
|
import { CaptureConsole } from '@sentry/integrations';
|
||||||
|
import posthog from 'posthog-js';
|
||||||
import { Theme } from 'carbon-components-svelte';
|
import { Theme } from 'carbon-components-svelte';
|
||||||
|
import type { CarbonTheme } from 'carbon-components-svelte/types/Theme/Theme.svelte';
|
||||||
import { Moon } from 'svelte-loading-spinners';
|
import { Moon } from 'svelte-loading-spinners';
|
||||||
import LandingNavigation from '$lib/components/Navigation/index.svelte';
|
import LandingNavigation from '$lib/components/Navigation/index.svelte';
|
||||||
import OrgNavigation from '$lib/components/Navigation/app.svelte';
|
import OrgNavigation from '$lib/components/Navigation/app.svelte';
|
||||||
@@ -45,30 +47,45 @@
|
|||||||
|
|
||||||
let supabase = getSupabase();
|
let supabase = getSupabase();
|
||||||
let path = $page.url?.pathname?.replace('/', '');
|
let path = $page.url?.pathname?.replace('/', '');
|
||||||
let carbonTheme = 'white';
|
let carbonTheme: CarbonTheme = 'white';
|
||||||
|
|
||||||
const delayedPreloading = derived(navigating, (currentPreloading, set) => {
|
const delayedPreloading = derived(navigating, (currentPreloading, set) => {
|
||||||
setTimeout(() => set(currentPreloading), 250);
|
setTimeout(() => set(currentPreloading), 250);
|
||||||
});
|
});
|
||||||
|
|
||||||
function setupSentry() {
|
function setupSentry() {
|
||||||
if (!dev) {
|
Sentry.init({
|
||||||
Sentry.init({
|
dsn: 'https://c966f7e8cb1d4306be20b26bb4f0cc96@o476906.ingest.sentry.io/5999999',
|
||||||
dsn: 'https://c966f7e8cb1d4306be20b26bb4f0cc96@o476906.ingest.sentry.io/5999999',
|
integrations: [
|
||||||
integrations: [
|
new Integrations.BrowserTracing(),
|
||||||
new Integrations.BrowserTracing(),
|
new CaptureConsole({
|
||||||
new CaptureConsole({
|
levels: ['error']
|
||||||
levels: ['error']
|
}),
|
||||||
}),
|
new Sentry.Replay()
|
||||||
new Sentry.Replay()
|
],
|
||||||
],
|
environment: !dev ? 'production' : 'development',
|
||||||
environment: !dev ? 'production' : 'development',
|
// This sets the sample rate to be 10%. You may want this to be 100% while
|
||||||
// This sets the sample rate to be 10%. You may want this to be 100% while
|
// in development and sample at a lower rate in production
|
||||||
// in development and sample at a lower rate in production
|
replaysSessionSampleRate: 0.5,
|
||||||
replaysSessionSampleRate: 0.5,
|
// If the entire session is not sampled, use the below sample rate to sample
|
||||||
// If the entire session is not sampled, use the below sample rate to sample
|
// sessions when an error occurs.
|
||||||
// sessions when an error occurs.
|
replaysOnErrorSampleRate: 1.0
|
||||||
replaysOnErrorSampleRate: 1.0
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function setupPosthug() {
|
||||||
|
posthog.init('phc_JfdHOZ6v0cVlGELBYx1Tmoen2nxNOrAzvgvrPA6Ksov', {
|
||||||
|
api_host: 'https://eu.posthog.com'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function setSentryUser() {
|
||||||
|
if ($profile) {
|
||||||
|
Sentry.setUser({
|
||||||
|
id: $profile.id,
|
||||||
|
username: $profile.username,
|
||||||
|
email: $profile.email,
|
||||||
|
fullname: $profile.fullname
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -99,7 +116,7 @@
|
|||||||
|
|
||||||
const [regexUsernameMatch] = [...(authUser.email?.matchAll(/(.*)@/g) || [])];
|
const [regexUsernameMatch] = [...(authUser.email?.matchAll(/(.*)@/g) || [])];
|
||||||
|
|
||||||
const { data, error } = await supabase
|
const { data: newProfileData, error } = await supabase
|
||||||
.from('profile')
|
.from('profile')
|
||||||
.insert({
|
.insert({
|
||||||
id: authUser.id,
|
id: authUser.id,
|
||||||
@@ -110,15 +127,15 @@
|
|||||||
.select();
|
.select();
|
||||||
|
|
||||||
// Profile created, go to onboarding or lms
|
// Profile created, go to onboarding or lms
|
||||||
if (!error && data) {
|
if (!error && newProfileData) {
|
||||||
$user.fetchingUser = false;
|
$user.fetchingUser = false;
|
||||||
$user.isLoggedIn = true;
|
$user.isLoggedIn = true;
|
||||||
$user.currentSession = authUser;
|
$user.currentSession = authUser;
|
||||||
|
|
||||||
profile.set(data[0]);
|
profile.set(newProfileData[0]);
|
||||||
|
|
||||||
// Set user in sentry
|
// Set user in sentry
|
||||||
Sentry.setUser($profile);
|
setSentryUser();
|
||||||
|
|
||||||
if (data.isOrgSite) {
|
if (data.isOrgSite) {
|
||||||
const { data, error } = await supabase
|
const { data, error } = await supabase
|
||||||
@@ -160,7 +177,7 @@
|
|||||||
profile.set(profileData);
|
profile.set(profileData);
|
||||||
|
|
||||||
// Set user in sentry
|
// Set user in sentry
|
||||||
Sentry.setUser($profile);
|
setSentryUser();
|
||||||
|
|
||||||
const orgRes = await getOrganizations($profile.id);
|
const orgRes = await getOrganizations($profile.id);
|
||||||
|
|
||||||
@@ -212,7 +229,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setupSentry();
|
if (!dev) {
|
||||||
|
setupSentry();
|
||||||
|
setupPosthug();
|
||||||
|
}
|
||||||
|
|
||||||
handleResize();
|
handleResize();
|
||||||
|
|
||||||
@@ -262,9 +282,7 @@
|
|||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
console.log('unsubscribed');
|
console.log('unsubscribed');
|
||||||
if (typeof authListener?.unsubscribe === 'function') {
|
authListener.subscription.unsubscribe();
|
||||||
authListener?.unsubscribe();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
import { currentOrg } from '$lib/utils/store/org';
|
import { currentOrg } from '$lib/utils/store/org';
|
||||||
import { snackbar } from '$lib/components/Snackbar/store';
|
import { snackbar } from '$lib/components/Snackbar/store';
|
||||||
import type { LessonCompletion } from '$lib/utils/types';
|
import type { LessonCompletion } from '$lib/utils/types';
|
||||||
import { profile } from '$lib/utils/store/user.js';
|
import { profile } from '$lib/utils/store/user';
|
||||||
import { getIsLessonComplete } from '$lib/components/Course/components/Lesson/functions';
|
import { getIsLessonComplete } from '$lib/components/Course/components/Lesson/functions';
|
||||||
|
|
||||||
export let data;
|
export let data;
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
import { addGroupMember } from '$lib/utils/services/courses';
|
import { addGroupMember } from '$lib/utils/services/courses';
|
||||||
import type { CurrentOrg } from '$lib/utils/types/org.js';
|
import type { CurrentOrg } from '$lib/utils/types/org.js';
|
||||||
import { ROLE } from '$lib/utils/constants/roles';
|
import { ROLE } from '$lib/utils/constants/roles';
|
||||||
import { profile } from '$lib/utils/store/user.js';
|
import { profile } from '$lib/utils/store/user';
|
||||||
import {
|
import {
|
||||||
triggerSendEmail,
|
triggerSendEmail,
|
||||||
NOTIFICATION_NAME
|
NOTIFICATION_NAME
|
||||||
@@ -50,7 +50,15 @@
|
|||||||
.from('groupmember')
|
.from('groupmember')
|
||||||
.select('id, profile(email)')
|
.select('id, profile(email)')
|
||||||
.eq('group_id', data.groupId)
|
.eq('group_id', data.groupId)
|
||||||
.eq('role_id', ROLE.TUTOR);
|
.eq('role_id', ROLE.TUTOR)
|
||||||
|
.returns<
|
||||||
|
{
|
||||||
|
id: string;
|
||||||
|
profile: {
|
||||||
|
email: string;
|
||||||
|
};
|
||||||
|
}[]
|
||||||
|
>();
|
||||||
|
|
||||||
const teachers: Array<string> =
|
const teachers: Array<string> =
|
||||||
teacherMembers.data?.map((teacher) => {
|
teacherMembers.data?.map((teacher) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user