diff --git a/package-lock.json b/package-lock.json
index 8e3adab6..aa9e9a77 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -2312,6 +2312,12 @@
"assert-plus": "^1.0.0"
}
},
+ "dayjs": {
+ "version": "1.10.7",
+ "resolved": "https://flipplib.jfrog.io/artifactory/api/npm/flipp-npm/dayjs/-/dayjs-1.10.7.tgz",
+ "integrity": "sha1-LPX5Gt0oEWdIRAhmoKHSbzps5Gg=",
+ "dev": true
+ },
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
@@ -3254,6 +3260,12 @@
"verror": "1.10.0"
}
},
+ "just-throttle": {
+ "version": "2.3.1",
+ "resolved": "https://flipplib.jfrog.io/artifactory/api/npm/flipp-npm/just-throttle/-/just-throttle-2.3.1.tgz",
+ "integrity": "sha1-70oHEAc7tLD699rRvEzKuu7E0OY=",
+ "dev": true
+ },
"lie": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz",
@@ -3834,15 +3846,6 @@
"pinkie": "^2.0.0"
}
},
- "polka": {
- "version": "1.0.0-next.22",
- "resolved": "https://registry.npmjs.org/polka/-/polka-1.0.0-next.22.tgz",
- "integrity": "sha512-a7tsZy5gFbJr0aUltZS97xCkbPglXuD67AMvTyZX7BTDBH384FWf0ZQF6rPvdutSxnO1vUlXM2zSLf5tCKk5RA==",
- "requires": {
- "@polka/url": "^1.0.0-next.21",
- "trouter": "^3.1.0"
- }
- },
"postcss": {
"version": "8.3.0",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.3.0.tgz",
@@ -4163,11 +4166,6 @@
"@babel/runtime": "^7.8.4"
}
},
- "regexparam": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/regexparam/-/regexparam-1.3.0.tgz",
- "integrity": "sha512-6IQpFBv6e5vz1QAqI+V4k8P2e/3gRrqfCJ9FI+O1FLQTO+Uz6RXZEZOPmTJ6hlGj7gkERzY5BRCv09whKP96/g=="
- },
"regexpu-core": {
"version": "4.7.1",
"resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.1.tgz",
@@ -4712,6 +4710,16 @@
"svelte": "^3.15.0"
}
},
+ "svelte-calendar": {
+ "version": "3.1.6",
+ "resolved": "https://flipplib.jfrog.io/artifactory/api/npm/flipp-npm/svelte-calendar/-/svelte-calendar-3.1.6.tgz",
+ "integrity": "sha1-vb+Unxnq4ZnQSGDaeFertQAOhEQ=",
+ "dev": true,
+ "requires": {
+ "dayjs": "^1.10.6",
+ "just-throttle": "^2.3.1"
+ }
+ },
"svelte-dnd-action": {
"version": "0.9.9",
"resolved": "https://registry.npmjs.org/svelte-dnd-action/-/svelte-dnd-action-0.9.9.tgz",
@@ -4921,14 +4929,6 @@
"resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz",
"integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM="
},
- "trouter": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/trouter/-/trouter-3.2.0.tgz",
- "integrity": "sha512-rLLXbhTObLy2MBVjLC+jTnoIKw99n0GuJs9ov10J870vDw5qhTurPzsDrudNtBf5w/CZ9ctZy2p2IMmhGcel2w==",
- "requires": {
- "regexparam": "^1.3.0"
- }
- },
"true-case-path": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-1.0.3.tgz",
diff --git a/package.json b/package.json
index fa52a625..9c2e2c28 100644
--- a/package.json
+++ b/package.json
@@ -57,6 +57,7 @@
"sass": "^1.32.13",
"svelte": "^3.17.3",
"svelte-awesome": "^2.3.1",
+ "svelte-calendar": "^3.1.6",
"svelte-dnd-action": "^0.9.9",
"svelte-loading-spinners": "^0.1.7",
"svelte-preprocess": "^4.7.3",
diff --git a/rollup.config.js b/rollup.config.js
index 15e4d9e7..882b9b3b 100644
--- a/rollup.config.js
+++ b/rollup.config.js
@@ -14,7 +14,8 @@ import pkg from './package.json';
const mode = process.env.NODE_ENV;
const dev = mode === 'development';
-const sourcemap = dev ? false : 'inline';
+// const sourcemap = dev ? false : 'inline';
+const sourcemap = 'inline';
const legacy = !!process.env.SAPPER_LEGACY_BUILD;
const onwarn = (warning, onwarn) => {
diff --git a/src/client.js b/src/client.js
index c39b2e5f..09e27ae6 100644
--- a/src/client.js
+++ b/src/client.js
@@ -6,20 +6,22 @@ import config from './config';
console.log(`isProd`, config.isProd);
-Sentry.init({
- dsn: 'https://c966f7e8cb1d4306be20b26bb4f0cc96@o476906.ingest.sentry.io/5999999',
- integrations: [
- new Integrations.BrowserTracing(),
- new CaptureConsole({
- levels: ['error'],
- }),
- ],
- environment: config.isProd ? 'production' : 'development',
- // Set tracesSampleRate to 1.0 to capture 100%
- // of transactions for performance monitoring.
- // We recommend adjusting this value in production
- tracesSampleRate: 1.0,
-});
+if (config.isProd) {
+ Sentry.init({
+ dsn: 'https://c966f7e8cb1d4306be20b26bb4f0cc96@o476906.ingest.sentry.io/5999999',
+ integrations: [
+ new Integrations.BrowserTracing(),
+ new CaptureConsole({
+ levels: ['error'],
+ }),
+ ],
+ environment: config.isProd ? 'production' : 'development',
+ // Set tracesSampleRate to 1.0 to capture 100%
+ // of transactions for performance monitoring.
+ // We recommend adjusting this value in production
+ tracesSampleRate: 1.0,
+ });
+}
sapper.start({
target: document.querySelector('#sapper'),
diff --git a/src/components/Navigation/index.svelte b/src/components/Navigation/index.svelte
index 8e934f52..10329014 100644
--- a/src/components/Navigation/index.svelte
+++ b/src/components/Navigation/index.svelte
@@ -28,7 +28,7 @@
{#if $user.isLoggedIn}
-
+
+ export async function preload(page, { user, config }) {
+ return { user, config };
+ }
+ -->
+
+
+
+ Admin Dashboard - ClassroomIO
+
+
+
+
+ {#each boxes as box}
+
+ {#if box.icon === 'students'}
+
+ {:else if box.icon === 'tutor'}
+
+ {:else if box.icon === 'courses'}
+
+ {/if}
+
{box.label}
+
{box.value}
+
+ {/each}
+
+
+
+
+
+
Outstanding students
+
+
+ {#each topStudents as topStudent, index}
+
+
{index + 1}
+
{topStudent.name}
+
+
{topStudent.score}
+
+ {/each}
+
+
+
+
+
Top rated courses
+
+
+ {#each topCourses as topCourse, index}
+
+
{index + 1}
+
{topCourse.title}
+
+
{topCourse.rating}
+
+ {/each}
+
+
+
+
+
diff --git a/src/routes/courses.svelte b/src/routes/courses.svelte
index cee4cf0d..907af12a 100644
--- a/src/routes/courses.svelte
+++ b/src/routes/courses.svelte
@@ -130,7 +130,7 @@
- Courses
+ Courses - ClassroomIO
diff --git a/src/routes/dashboard.svelte b/src/routes/dashboard.svelte
index 27d44560..899acb3c 100644
--- a/src/routes/dashboard.svelte
+++ b/src/routes/dashboard.svelte
@@ -1,151 +1,259 @@
-
-
Dashboard - ClassroomIO
-
-
- {#each boxes as box}
-
- {#if box.icon === 'students'}
-
- {:else if box.icon === 'tutor'}
-
- {:else if box.icon === 'courses'}
-
- {/if}
-
{box.label}
-
{box.value}
+
+
+ {#if !$isMobile}
+
+
- {/each}
-
-
-
-
-
-
Outstanding students
-
-
- {#each topStudents as topStudent, index}
-
-
{index + 1}
-
{topStudent.name}
-
-
{topStudent.score}
+ {/if}
+
+ {#if $isMobile}
+
- {/each}
-
-
-
-
-
Top rated courses
-
-
- {#each topCourses as topCourse, index}
+ {:else}
-
{index + 1}
-
{topCourse.title}
-
-
{topCourse.rating}
+ {formatDate($store?.selected)}
+ {/if}
+
+ {#each selectedDateLessonData as lessonData}
+
+ {:else}
+
+ No lesson on this day
+
{/each}
-
+
diff --git a/src/utils/functions/routes/dashboard.ts b/src/utils/functions/routes/dashboard.ts
new file mode 100644
index 00000000..904fc9ae
--- /dev/null
+++ b/src/utils/functions/routes/dashboard.ts
@@ -0,0 +1,56 @@
+import dayjs from 'dayjs';
+import isYesterday from 'dayjs/plugin/isYesterday';
+import isToday from 'dayjs/plugin/isToday';
+import isTomorrow from 'dayjs/plugin/isTomorrow';
+
+import type { UserLessonDataType, LessonsByMonthIndexType } from '../../types';
+
+dayjs.extend(isYesterday);
+dayjs.extend(isToday);
+dayjs.extend(isTomorrow);
+
+export const formatUserUpcomingData = (
+ data: UserLessonDataType[]
+): LessonsByMonthIndexType => {
+ const result: LessonsByMonthIndexType = {};
+
+ for (const item of data) {
+ const { lesson_at } = item;
+ const lessonAtDate = new Date(lesson_at);
+ const monthIndex = lessonAtDate.getMonth();
+ const dateIndex = lessonAtDate.getDate();
+
+ if (result[monthIndex]) {
+ const prevDateData = result[monthIndex][dateIndex];
+
+ if (Array.isArray(prevDateData)) {
+ result[monthIndex] = {
+ ...result[monthIndex],
+ [dateIndex]: [...prevDateData, item],
+ };
+ } else {
+ result[monthIndex] = { ...result[monthIndex], [dateIndex]: [item] };
+ }
+ } else {
+ result[monthIndex] = { [dateIndex]: [item] };
+ }
+ }
+
+ return result;
+};
+
+export const formatDate = (_selectedDate: string | undefined): string => {
+ if (!_selectedDate) return '';
+
+ const selectedDate = dayjs(_selectedDate);
+
+ if (selectedDate.isToday()) {
+ return 'Today';
+ } else if (selectedDate.isYesterday()) {
+ return 'Yesterday';
+ } else if (selectedDate.isTomorrow()) {
+ return 'Tomorrow';
+ }
+
+ return selectedDate.format('ddd, MMM D');
+};
diff --git a/src/utils/functions/supabase.js b/src/utils/functions/supabase.js
deleted file mode 100644
index 00d23c0f..00000000
--- a/src/utils/functions/supabase.js
+++ /dev/null
@@ -1,24 +0,0 @@
-import { createClient } from '@supabase/supabase-js';
-
-export let supabase;
-
-export const getSupabase = (config) => {
- if (supabase) return supabase;
-
- if (!config && process && process.env) {
- config = {
- isProd: process.env.NODE_ENV === 'development',
- supabaseConfig: {
- url: process.env.SVELTE_APP_SUPABASE_URL,
- anonKey: process.env.SVELTE_APP_SUPABASE_ANON_KEY,
- },
- };
- }
-
- supabase = createClient(
- config.supabaseConfig.url,
- config.supabaseConfig.anonKey
- );
-
- return supabase;
-};
diff --git a/src/utils/functions/supabase.ts b/src/utils/functions/supabase.ts
new file mode 100644
index 00000000..c3278142
--- /dev/null
+++ b/src/utils/functions/supabase.ts
@@ -0,0 +1,25 @@
+import { createClient, SupabaseClient } from '@supabase/supabase-js';
+import type { ConfigType } from '../types';
+
+export let supabase: SupabaseClient;
+
+export const getSupabase = (config: ConfigType) => {
+ if (supabase) return supabase;
+
+ if (!config && process && process.env) {
+ config = {
+ isProd: process.env.NODE_ENV === 'development',
+ supabaseConfig: {
+ url: process.env.SVELTE_APP_SUPABASE_URL || '',
+ anonKey: process.env.SVELTE_APP_SUPABASE_ANON_KEY || '',
+ },
+ };
+ }
+
+ supabase = createClient(
+ config.supabaseConfig.url,
+ config.supabaseConfig.anonKey
+ );
+
+ return supabase;
+};
diff --git a/src/utils/services/dashboard/index.ts b/src/utils/services/dashboard/index.ts
new file mode 100644
index 00000000..65b95f21
--- /dev/null
+++ b/src/utils/services/dashboard/index.ts
@@ -0,0 +1,25 @@
+import type { UserLessonDataType } from '../../types';
+import { supabase } from '../../functions/supabase';
+
+export async function fetchUserUpcomingData(
+ profileId: string | null
+): Promise
{
+ if (!profileId) {
+ return [];
+ }
+
+ const { data: userUpcomingData } = await supabase.rpc(
+ 'get_user_upcoming_lessons',
+ {
+ profile_id_arg: profileId,
+ }
+ );
+
+ console.log(`userUpcomingData`, userUpcomingData);
+
+ if (!Array.isArray(userUpcomingData)) {
+ return [];
+ }
+
+ return userUpcomingData;
+}
diff --git a/src/utils/types/config.ts b/src/utils/types/config.ts
new file mode 100644
index 00000000..afec539d
--- /dev/null
+++ b/src/utils/types/config.ts
@@ -0,0 +1,4 @@
+export type ConfigType = {
+ isProd: boolean;
+ supabaseConfig: { url: string; anonKey: string };
+};
diff --git a/src/utils/types/dashboard.ts b/src/utils/types/dashboard.ts
new file mode 100644
index 00000000..25e7bab6
--- /dev/null
+++ b/src/utils/types/dashboard.ts
@@ -0,0 +1,16 @@
+export type UserLessonDataType = {
+ call_url: string;
+ course_id: string;
+ course_title: string;
+ lesson_at: string;
+ lesson_id: string;
+ lesson_title: string;
+};
+
+type LessonByDayType = {
+ [dayIndex: number]: UserLessonDataType[];
+};
+
+export type LessonsByMonthIndexType = {
+ [monthIndex: number]: LessonByDayType;
+};
diff --git a/src/utils/types/index.ts b/src/utils/types/index.ts
new file mode 100644
index 00000000..e5959572
--- /dev/null
+++ b/src/utils/types/index.ts
@@ -0,0 +1,3 @@
+export * from './config';
+export * from './dashboard';
+export * from './user';
diff --git a/src/utils/types/user.ts b/src/utils/types/user.ts
new file mode 100644
index 00000000..a8cf6750
--- /dev/null
+++ b/src/utils/types/user.ts
@@ -0,0 +1,17 @@
+export interface CurrentSessionType {
+ access_token: string;
+ expires_at: number;
+ expires_in: number;
+ refresh_token: string;
+ token_type: string;
+}
+
+export interface UserType {
+ openAuthModal: boolean;
+ fetchingUser: boolean;
+ isLoggedIn: boolean;
+ currentSession: CurrentSessionType | null;
+ expiresAt: number;
+}
+
+export interface ProfileType {}