feat: 🚀 😃 convert repo into a monorepo 🚀 😃 (#51)

* Merge main into turbo-monrepo

* feat: rename apps

* feat: remove supbase videos and serve all from project

* feat: move supabase to root

* feat: update README

* docs: update link
This commit is contained in:
Rotimi Best
2023-11-25 21:56:02 +00:00
committed by GitHub
parent 03ca379094
commit 41e29014ef
806 changed files with 85853 additions and 5746 deletions
+112
View File
@@ -0,0 +1,112 @@
<script lang="ts">
import Learning from '$lib/components/LMS/components/Learning.svelte';
import PrimaryButton from '$lib/components/PrimaryButton/index.svelte';
import { VARIANTS } from '$lib/components/PrimaryButton/constants';
import { goto } from '$app/navigation';
import { profile } from '$lib/utils/store/user';
import { currentOrg } from '$lib/utils/store/org';
import { fetchCourses } from '$lib/components/Courses/api';
import { courses, courseMetaDeta } from '$lib/components/Courses/store';
import type { Course } from '$lib/utils/types';
import { getGreeting } from '$lib/utils/functions/date';
let hasFetched = false;
let progressPercentage = 0;
let totalLessons = 0;
let totalComPleted = 0;
async function getCourses(userId: string | undefined, orgId: string) {
if (hasFetched || !userId || !orgId) {
return;
}
$courseMetaDeta.isLoading = true;
const coursesResult = await fetchCourses(userId, orgId);
console.log(`coursesResult`, coursesResult);
$courseMetaDeta.isLoading = false;
if (!coursesResult) return;
courses.set(coursesResult.allCourses);
hasFetched = true;
}
function calcTotalProgress(courses: Course[]) {
totalComPleted = courses.reduce((acc, cur) => acc + (cur.progress_rate || 0), 0);
totalLessons = courses.reduce((acc, cur) => acc + (cur.total_lessons || 0), 0);
progressPercentage = Math.round((totalComPleted / totalLessons) * 100);
}
$: getCourses($profile.id, $currentOrg.id);
$: calcTotalProgress($courses);
</script>
<svelte:head>
<title>Student Dashboard - ClassroomIO</title>
</svelte:head>
<section class="max-w-6xl gap-5 mx-auto">
<div class="m-5">
<h1 class="dark:text-white text-2xl md:text-3xl font-bold mb-3">
{getGreeting()}
{$profile.fullname}!
</h1>
<div
class="w-full h-fit lg:h-[265px] flex md:items-center justify-between flex-col-reverse md:flex-row p-5 lg:p-10 rounded-md bg-primary-900 my-2"
>
<span>
<p class="w-full md:w-[75%] lg:w-[80%] text-white text-xs lg:text-xl font-normal mb-5">
Today is another day to bring you closer to your learning goals. Don’t give up, the more
you learn the better you get.
</p>
<PrimaryButton
label="Don't give up"
variant={VARIANTS.CONTAINED_WHITE}
onClick={() => goto('/lms/mylearning')}
/>
</span>
<img
src="/images/student-learning.svg"
alt="student Learning Pictogram"
class="w-28 md:block md:w-1/3 lg:w-[275px] lg:h-[205px] mb-3 md:mb-0"
/>
</div>
<section class="flex w-full flex-col lg:flex-row md:gap-5">
<div class="w-full lg:w-[50%] mt-10 xl:mt-2">
<Learning />
</div>
<div class="mt-10 xl:mt-2 w-full lg:w-[50%]">
<p class="text-base font-semibold text-[#040F2D] pb-3 dark:text-white">Your Progress</p>
<div
class="flex items-center justify-center border border-[#EAEAEA] dark:bg-neutral-800 gap-2 rounded h-fit lg:h-[40vh] lg:overflow-y-auto p-3"
>
<div
class="w-full h-full flex flex-col sm:flex-row xl:flex-col items-center justify-between lg:items-center xl:items-start gap-5 lg:justify-around"
>
<div>
<img src="/images/target.svg" alt="student Learning score" />
</div>
<span class="text-center xl:text-start">
<p class="text-base font-semibold py-2 text-[#040F2D] dark:text-white">
Your Progress
</p>
{#if totalLessons > 0}
<p class="text-xs font-normal text-[#656565] dark:text-white">
{totalComPleted}/{totalLessons} lessons completed
</p>
{:else}
<p class="text-xs font-normal text-[#656565] dark:text-white">No courses started</p>
{/if}
</span>
<h1
class="text-5xl lg:text-6xl font-bold text-[#262626] dark:text-white my-0 whitespace-nowrap"
>
{progressPercentage} %
</h1>
</div>
</div>
</div>
</section>
</div>
</section>
@@ -0,0 +1,25 @@
<script>
import { goto } from '$app/navigation';
import Community from '$lib/components/Org/Community/index.svelte';
import PrimaryButton from '$lib/components/PrimaryButton/index.svelte';
import { currentOrgPath } from '$lib/utils/store/org';
function askCommunity() {
goto(`${$currentOrgPath}/community/ask`);
}
</script>
<svelte:head>
<title>Community - ClassroomIO</title>
</svelte:head>
<section class="w-full max-w-4xl mx-auto">
<div class="py-10 px-5">
<div class="flex items-center justify-between mb-10">
<h1 class="dark:text-white text-3xl font-bold">Community</h1>
<PrimaryButton label="Ask Community" onClick={askCommunity} />
</div>
<Community isLMS={true} />
</div>
</section>
@@ -0,0 +1,5 @@
export const load = ({ params = { slug: '' } }) => {
return {
slug: params.slug
};
};
@@ -0,0 +1,402 @@
<script>
import { goto } from '$app/navigation';
import pluralize from 'pluralize';
import TrashCanIcon from 'carbon-icons-svelte/lib/TrashCan.svelte';
import { SkeletonPlaceholder, SkeletonText } from 'carbon-components-svelte';
import ArrowLeftIcon from 'carbon-icons-svelte/lib/ArrowLeft.svelte';
import Vote from '$lib/components/Vote/index.svelte';
import PrimaryButton from '$lib/components/PrimaryButton/index.svelte';
import { VARIANTS } from '$lib/components/PrimaryButton/constants';
import Avatar from '$lib/components/Avatar/index.svelte';
import IconButton from '$lib/components/IconButton/index.svelte';
import CheckmarkOutlineIcon from 'carbon-icons-svelte/lib/CheckmarkOutline.svelte';
import { currentOrg } from '$lib/utils/store/org';
import { profile } from '$lib/utils/store/user';
import { supabase } from '$lib/utils/functions/supabase';
import {
askCommunityValidation,
commentInCommunityValidation
} from '$lib/utils/functions/validator';
import { snackbar } from '$lib/components/Snackbar/store';
import TextField from '$lib/components/Form/TextField.svelte';
import DeleteCommentModal from '$lib/components/Org/Community/DeleteCommentModal.svelte';
import TextEditor from '$lib/components/TextEditor/index.svelte';
import { calDateDiff } from '$lib/utils/functions/date';
export let data;
const { slug } = data;
let question;
let comment = '';
let errors = {};
let isValidAnswer = false; // V2 allow admin mark an answer as accepted
let resetInput = 1;
let voted = { question: false, comment: {} };
let isEditMode = false;
let deleteComment = {
shouldDelete: false,
commentId: null
};
let editContent = {
title: '',
body: ''
};
let editorInstance = false;
function mapResToQuestion(data) {
return {
id: data.id,
title: data.title,
votes: data.votes,
author: {
id: data?.author?.profile?.id || '',
name: data?.author?.profile?.fullname || '',
avatar: data?.author?.profile?.avatar_url || ''
},
body: data.body,
createdAt: calDateDiff(data.created_at),
comments: data.comments.map((c) => ({
id: c.id,
authorId: c.author?.profile?.id || '',
name: c.author?.profile?.fullname || '',
avatar: c.author?.profile?.avatar_url || '',
votes: c.votes,
comment: c.body,
createdAt: calDateDiff(c.created_at)
})),
totalComments: 0
};
}
async function fetchCommunityQuestion(slug) {
if (!slug) return;
const { data, error } = await supabase
.from('community_question')
.select(
`
id,
title,
body,
votes,
created_at,
comments:community_answer(
id,
body,
votes,
created_at,
author:organizationmember!community_answer_author_id_fkey!inner(
profile!inner(id, fullname, avatar_url)
)
),
author:organizationmember!community_question_author_id_fkey!inner(
profile!inner(id, fullname, avatar_url)
)
`
)
.eq('slug', slug)
.single();
if (error) {
console.error('Error loading community', error);
return goto('/lms/community');
}
question = mapResToQuestion(data);
question.totalComments = question.comments.length;
}
async function submitComment() {
errors = commentInCommunityValidation({ comment });
console.log('submitComment errors', errors);
if (Object.keys(errors).length) {
return;
}
const { data, error } = await supabase
.from('community_answer')
.insert({
id: undefined,
body: comment,
question_id: question.id,
author_id: $currentOrg.memberId,
votes: 0
})
.select();
if (error) {
console.error('Error: commenting', error);
snackbar.error('Error - Please try again later');
} else {
console.log('Success: commenting', data);
snackbar.success();
// Add to comment
const _c = data?.[0];
question.comments = [
...question.comments,
{
id: _c.id,
name: $profile?.fullname || '',
avatar: $profile?.avatar_url || '',
votes: 0,
comment: _c.body,
createdAt: calDateDiff(_c.created_at)
}
];
// Reset input
comment = '';
resetInput = new Date().getTime();
}
}
async function upvoteQuestion(type, commentId) {
const isQuestion = type === 'question';
if (isQuestion && voted.question) return;
if (!isQuestion && voted.comment[commentId]) return;
const table = isQuestion ? 'community_question' : 'community_answer';
const matchId = isQuestion ? question.id : commentId;
let votes = 0;
if (isQuestion) {
question.votes = question.votes + 1;
votes = question.votes;
} else {
question.comments = question.comments.map((c) => {
if (c.id === commentId) {
c.votes = c.votes + 1;
votes = c.votes;
}
return c;
});
}
const { error } = await supabase.from(table).update({ votes }).match({ id: matchId });
if (error) {
console.error('Error: upvoteQuestion', error);
snackbar.error();
} else {
if (isQuestion) {
voted.question = true;
} else {
voted.comment[commentId] = true;
}
}
}
async function handleQuestionEdit() {
if (isEditMode) {
const fields = { title: editContent.title, body: editContent.body };
errors = askCommunityValidation(fields);
console.log('handleQuestionEdit errors', errors);
if (Object.keys(errors).length) {
return;
}
}
isEditMode = !isEditMode;
editorInstance = !editorInstance;
if (!isEditMode) {
const fields = { title: editContent.title, body: editContent.body };
errors = askCommunityValidation(fields);
console.log('handleQuestionEdit errors', errors);
if (Object.keys(errors).length) {
return;
}
const { error } = await supabase
.from('community_question')
.update(fields)
.match({ id: question.id });
if (error) {
console.error('Error: handleQuestionEdit', error);
snackbar.error();
} else {
question.title = fields.title;
question.body = fields.body;
editContent.title = '';
editContent.body = '';
}
} else {
editContent.title = question.title;
editContent.body = question.body;
}
}
async function handleCommentDelete() {
const { error } = await supabase
.from('community_answer')
.delete()
.match({ id: deleteComment.commentId });
if (!error) {
question.comments = question.comments.filter((c) => c.id !== deleteComment.commentId);
deleteComment.shouldDelete = false;
deleteComment.commentId = null;
}
}
$: fetchCommunityQuestion(slug);
</script>
<svelte:head>
<title>{question?.title || 'Question'}</title>
</svelte:head>
<DeleteCommentModal
bind:open={deleteComment.shouldDelete}
onCancel={() => {
deleteComment.shouldDelete = false;
deleteComment.commentId = null;
}}
onDelete={handleCommentDelete}
/>
<section class="max-w-3xl mx-auto md:mx-10 lg:mb-20">
{#if !question}
<div class="py-10 px-5 mb-3">
<SkeletonText style="width: 25%;" />
<SkeletonText style="width: 100%; margin-bottom: 2rem" />
<SkeletonPlaceholder style="width: 100%; height: 20rem;" />
</div>
{:else}
<div class="py-10 px-5">
<a class="text-gray-500 dark:text-white text-md flex items-center" href={`/lms/community`}>
<ArrowLeftIcon size={24} class="carbon-icon dark:text-white" /> Go Back
</a>
<div class="my-5 flex justify-between items-center">
{#if isEditMode}
<TextField
bind:value={editContent.title}
className="w-full mr-2"
errorMessage={errors.title}
/>
{:else}
<div class="flex items-center">
<Vote
value={question.votes}
upVote={() => upvoteQuestion('question')}
disabled={voted.question}
/>
<h2 class="text-3xl">{question.title}</h2>
</div>
{/if}
{#if question.author.id === $profile.id}
<PrimaryButton
label={isEditMode ? 'Save' : 'Edit'}
variant={VARIANTS.OUTLINED}
onClick={handleQuestionEdit}
className="h-fit"
/>
{#if isEditMode}
<PrimaryButton
label="Cancel"
variant={VARIANTS.TEXT}
onClick={() => (isEditMode = !isEditMode)}
className="py-3 px-6 rounded-sm h-fit"
disablePadding={true}
/>
{/if}
{/if}
</div>
<div class="my-1 px-1 rounded-lg border border-1 border-gray">
<header class="flex items-center justify-between leading-none p-2">
<div class="flex items-center no-underline hover:underline text-black">
<Avatar
src={question.author.avatar}
name={question.author.name}
width="w-7"
height="h-7"
/>
<p class="dark:text-white ml-2 text-sm">{question.author.name}</p>
<p class="dark:text-white ml-2 text-sm text-gray-500">
{question.createdAt}
</p>
</div>
</header>
{#if isEditMode && editorInstance}
<div class="my-2">
<TextEditor
bind:value={editContent.body}
placeholder="Give an answer"
onChange={(html) => (editContent.body = html)}
/>
</div>
{:else}
<section class="prose prose-sm sm:prose p-2">
{@html question.body}
</section>
{/if}
</div>
<div class="my-8 font-bold">
{pluralize('answers', question.totalComments, true)}
</div>
{#each question.comments as comment}
<div class="my-5 px-1 flex items-start">
<Vote
value={comment.votes}
upVote={() => upvoteQuestion('comment', comment.id)}
disabled={voted.comment[comment.id]}
/>
<div class="w-full rounded-lg border border-1 border-gray">
<header class="flex items-center justify-between leading-none p-2">
<div class="flex items-center text-black">
<Avatar src={comment.avatar} name={comment.name} width="w-7" height="h-7" />
<p class="dark:text-white ml-2 text-sm">{comment.name}</p>
<p class="dark:text-white ml-2 text-sm text-gray-500">
{comment.createdAt}
</p>
</div>
{#if isValidAnswer}
<CheckmarkOutlineIcon size={20} />
{/if}
{#if comment.authorId === $profile.id}
<IconButton
value="delete-comment"
onClick={() => {
deleteComment.shouldDelete = true;
deleteComment.commentId = comment.id;
}}
>
<TrashCanIcon size={16} class="carbon-icon dark:text-white" />
</IconButton>
{/if}
</header>
<article class="prose prose-sm sm:prose p-2">
{@html comment.comment}
</article>
</div>
</div>
{/each}
<hr />
<div>
{#if !editorInstance}
<TextEditor
bind:value={comment}
placeholder="Give an answer"
onChange={(html) => (comment = html)}
/>
{/if}
<div class="flex justify-end mr-2">
<PrimaryButton label="Comment" onClick={submitComment} />
</div>
</div>
</div>
{/if}
</section>
@@ -0,0 +1,89 @@
<script>
import { goto } from '$app/navigation';
import ArrowLeftIcon from 'carbon-icons-svelte/lib/ArrowLeft.svelte';
import { currentOrg } from '$lib/utils/store/org';
import PrimaryButton from '$lib/components/PrimaryButton/index.svelte';
import { askCommunityValidation } from '$lib/utils/functions/validator';
import { supabase } from '$lib/utils/functions/supabase';
import { snackbar } from '$lib/components/Snackbar/store';
import generateSlug from '$lib/utils/functions/generateSlug';
import TextEditor from '$lib/components/TextEditor/index.svelte';
let errors = {};
let fields = {
title: '',
body: ''
};
async function handleSave() {
errors = askCommunityValidation(fields);
console.log('handleSave errors', errors);
if (Object.keys(errors).length) {
return;
}
const { data: question, error } = await supabase
.from('community_question')
.insert({
...fields,
organization_id: $currentOrg.id,
author_id: $currentOrg.memberId,
votes: 0,
slug: generateSlug(fields.title)
})
.select();
if (error) {
console.error('Error: asking question', error);
snackbar.error();
} else {
console.log('Success: asking question', question);
snackbar.success();
goto(`/lms/community/${question[0].slug}`);
}
}
</script>
<svelte:head>
<title>Ask the Community - ClassroomIO</title>
</svelte:head>
<section class="w-full max-w-3xl mx-auto">
<div class="p-5">
<a class="text-gray-500 dark:text-white text-md flex items-center" href={`/lms/community`}>
<ArrowLeftIcon size={24} class="carbon-icon dark:text-white" /> Go Back
</a>
<div class="flex items-center justify-between">
<h1 class="dark:text-white text-3xl font-bold">Ask the community</h1>
<PrimaryButton label="Publish" onClick={handleSave} />
</div>
</div>
<div class="mb-3 p-2">
<input
class="title rounded-lg border border-1 border-gray p-2 w-full focus:outline-none"
error="false"
autocomplete="off"
placeholder="Title"
aria-label="Title"
aria-describedby="title-input-validation"
type="text"
name="discussion_title"
id="discussion_title"
bind:value={fields.title}
/>
{#if errors.title}
<p class="dark:text-white text-red-500 text-sm">
{errors.title}
</p>
{/if}
</div>
<TextEditor
bind:value={fields.body}
placeholder="Ask the community any question you might have"
onChange={(html) => (fields.body = html)}
/>
</section>
@@ -0,0 +1,173 @@
<script lang="ts">
import { cloneDeep } from 'lodash';
import Chip from '$lib/components/Chip/index.svelte';
import { profile } from '$lib/utils/store/user';
import { browser } from '$app/environment';
import { currentOrg } from '$lib/utils/store/org';
import { snackbar } from '$lib/components/Snackbar/store';
import { fetchLMSExercises } from '$lib/utils/services/lms/exercises';
import type { LMSExercise } from '$lib/utils/services/lms/exercises';
import { calDateDiff } from '$lib/utils/functions/date';
const defaultSections = [
{
id: 0,
title: 'Not Submitted',
items: [],
className: 'text-[#E35353] bg-[#FDDFE4]'
},
{
id: 1,
title: 'Submitted',
items: [],
className: 'text-orange-700 bg-orange-200'
},
{
id: 2,
title: 'Grading In Progress',
items: [],
className: 'text-yellow-700 bg-yellow-200'
},
{
id: 3,
title: 'Graded',
value: 0,
items: [],
className: 'text-green-700 bg-green-200'
}
];
let sections: Section[] = cloneDeep(defaultSections);
interface Section {
id: number;
title: string;
className: string;
items: ExerciseItem[];
}
interface ExerciseItem {
courseTitle: string;
courseURL: string;
exerciseId: string;
exerciseTitle: string;
exerciseURL: string;
grade: string;
lessonNo: string;
lessonTitle: string;
lessonURL: string;
submissionStatus: number;
submissionUpdatedAt: string;
}
function generateSections(exercises: LMSExercise[]): Section[] {
const _sections: Section[] = cloneDeep(defaultSections);
for (const exercise of exercises) {
const { id, title, updated_at, submission, lesson, questions } = exercise;
const submissionItem = submission[0] || {
status_id: 0,
updated_at,
total: 0
};
const courseURL = `/courses/${lesson.course.id}`;
const lessonURL = `${courseURL}/lessons/${lesson.id}`;
const exerciseURL = `${lessonURL}/exercises/${id}`;
const grade = `${submissionItem.total}/${questions.reduce(
(acc, cur) => (acc += cur.points),
0
)}`;
const item: ExerciseItem = {
exerciseId: id,
courseTitle: lesson.course.title,
courseURL,
exerciseTitle: title,
exerciseURL,
lessonTitle: lesson.title,
lessonNo: lesson.order < 9 ? '0' + (lesson.order + 1) : `${lesson.order}`,
lessonURL,
submissionStatus: submissionItem.status_id,
submissionUpdatedAt: calDateDiff(submissionItem.updated_at),
grade
};
_sections[submissionItem.status_id].items.push(item);
}
return _sections;
}
async function fetchData(profileId: string, orgId: string) {
const { exercises, error } = await fetchLMSExercises(profileId, orgId);
console.log('exercises', exercises);
console.log('error', error);
if (error) {
snackbar.error('Fetching exercises failed, please reload the page');
return;
}
if (!exercises) return;
sections = generateSections(exercises);
console.log('sections', sections);
}
$: if (browser && $profile.id && $currentOrg.id) {
fetchData($profile.id, $currentOrg.id);
}
</script>
<section class="w-full max-w-6xl mx-auto">
<div class="p-5">
<div class="flex items-center justify-between mb-10">
<h1 class="dark:text-white text-3xl font-bold">Exercises</h1>
</div>
<div>
<div class="flex items-center w-full">
{#each sections as { title, items, className, id }}
<div
class="min-w-[355px] max-w-[355px] h-[70vh] rounded-md bg-gray-100 dark:bg-black border border-gray-50 dark:border-neutral-700 p-3 mr-3 overflow-hidden"
>
<div class="flex items-center mb-2 gap-2">
<p class="dark:text-white ml-2 font-bold">{title}</p>
<Chip value={items.length} {className} />
</div>
<div class="pr-2 h-full overflow-y-auto pb-3">
{#each items as item}
<div class=" w-full my-2 mx-0 rounded-md bg-white dark:bg-neutral-800 py-3 px-3">
<a
class="flex w-full items-center cursor-pointer text-primary-600 mb-2"
href={item.courseURL}
>
<p class="text-xs">{item.courseTitle}</p>
</a>
<a class="text-black dark:text-white text-md font-bold" href={item.exerciseURL}>
{#if id === 3}
({item.grade}) -
{/if}
{item.exerciseTitle}
</a>
<a
class="flex items-center no-underline hover:underline text-black my-2 w-fit"
href={item.lessonURL}
>
<p class="dark:text-white text-grey text-sm">
Lesson: <span class="italic">{item.lessonTitle}</span>
</p>
</a>
<p class="dark:text-white text-gray-500 text-xs">
{item.submissionUpdatedAt}
</p>
</div>
{/each}
</div>
</div>
{/each}
</div>
</div>
</div>
</section>
@@ -0,0 +1,77 @@
<script lang="ts">
import { Search } from 'carbon-components-svelte';
import Tabs from '$lib/components/Tabs/index.svelte';
import TabContent from '$lib/components/TabContent/index.svelte';
import Courses from '$lib/components/Courses/index.svelte';
import { fetchCourses } from '$lib/components/Courses/api';
import { profile } from '$lib/utils/store/user';
import { currentOrg } from '$lib/utils/store/org';
import { courses, courseMetaDeta, coursesComplete } from '$lib/components/Courses/store';
import { browser } from '$app/environment';
const tabs = [
{
label: 'In progress',
value: 1
},
{
label: 'Complete',
value: 2
}
];
let currentTab = tabs[0].value;
let hasFetched = false;
function onChange(tab: 0) {
return () => (currentTab = tab);
}
async function getCourses(userId: string | null, orgId: string) {
console.log('hasFetched', hasFetched);
if (hasFetched || !userId || !orgId) {
return;
}
hasFetched = true;
$courseMetaDeta.isLoading = true;
const coursesResult = await fetchCourses(userId, orgId);
console.log(`get courses result`, coursesResult);
$courseMetaDeta.isLoading = false;
if (!coursesResult) return;
courses.set(coursesResult.allCourses);
hasFetched = true;
}
$: if (browser && $profile.id && $currentOrg.id) {
getCourses($profile.id, $currentOrg.id);
}
</script>
<section class="max-w-6xl mx-auto">
<div class="m-2 md:m-5">
<div role="searchbox" class=" bg-gray-100 w-full md:w-[60%] lg:w-[30%]">
<Search placeholder="Search courses" class="dark:text-black" />
</div>
<h1 class="text-3xl font-semibold my-4">My Learning</h1>
<Tabs {tabs} {currentTab} {onChange}>
<slot:fragment slot="content">
<TabContent value={tabs[0].value} index={currentTab}>
<Courses
courses={$courses}
emptyTitle="No Course In progress"
emptyDescription="Any course that you start will be displayed here"
/>
</TabContent>
<TabContent value={tabs[1].value} index={currentTab}>
<Courses
courses={$coursesComplete}
emptyTitle="No Course Completed"
emptyDescription="Any course that you complete will be displayed here"
/>
</TabContent>
</slot:fragment>
</Tabs>
</div>
</section>
@@ -0,0 +1,47 @@
<script>
import { page } from '$app/stores';
import { Tabs, Tab, TabContent } from 'carbon-components-svelte';
import Profile from '$lib/components/LMS/components/Profile.svelte';
import Integrations from '$lib/components/Org/Settings/Integrations.svelte';
let selected = 0;
const tabs = [
{
key: 0,
label: 'Profile',
tabKey: '',
href: $page.url.pathname
},
{
key: 1,
label: 'Integrations',
tabKey: 'integrations',
href: `${$page.url.pathname}?tab=integrations`,
disabled: false
}
];
</script>
<section class="w-full max-w-6xl mx-auto">
<div class="py-10 px-5">
<div class="flex items-center justify-between mb-10">
<h1 class="dark:text-white text-3xl font-bold">Settings</h1>
</div>
<div class="">
<Tabs autoWidth bind:selected>
{#each tabs as tab}
<Tab label={tab.label} href={tab.href} />
{/each}
<svelte:fragment slot="content">
<TabContent>
<Profile />
</TabContent>
<TabContent>
<Integrations />
</TabContent>
</svelte:fragment>
</Tabs>
</div>
</div>
</section>