feat: [lw] free tools (#297)

* checkpoint

* checkpoint

* feat: Create free tools page

* responsiveness fix

* rename free-tools to tools

* add slug

* feat: Random name picker

* quick fix

* PR Fixes

* PR Fixes

* - adjustments

* update env example

* remove base url

* remove base url in .env.example

* fix type errors

* add og images

* add signup cta to /tools layout

* update cta

* add socials avatar

* add meta tags

* feat: tic tac toe (#307)

* Feat: tic tac toe

* PR Adjustments

* feat: Implement Timed Activity Tool (#316)

* chore: update verification email content

* chore: landing page improvements

* feat: grade exercise with AI (#296)

* working on the UI of the grading

* working on the grading function of the radio and checkboxes

* ai grading fully implemented with dummy functions for generating scores

* working on the openai response

* working on the openai response

* finished with the ai_grading

* finished with the ai_grading

* creating new migration file

* working on reviews

* updated the translations

* improvements

---------

Co-authored-by: Rotimi Best <best@classroomio.com>

* feat: selfhost tinymce

* fix: logo resolution error in manifest.json

* feat: move from gpt3.5 to gpt-4o

* feat: customize the LMS from dashboard (#304)

* Done with the customization of the LMS task

* generated custom_lms migration file

* updated the migration file

* copy improvements

* update wording

* made updates based on reviews,i hope i didnt miss anything

* updated the translations and migration files

* refactoring

* disabled improvements for apps

* more improvements

* more custom improvements

* pr refactoring

---------

Co-authored-by: Rotimi Best <best@classroomio.com>

* fix: CustomizeLMS not found

* fix: CustomizeLms wrong path

* setup

* docs: update short hero tagline

* debug: courses not showing on vercel

* more debugging

* i think this fixes it

* fix: phew, clean up debug cause it works

* fix: move RBS into coursecontainer on marks page

* chore: cleaner way to implement redirect if role permission denied

* docs: update tagline

* fix: delete /org/# route

* checkpoint

* add reusable header component

* fix: empty lesson title bug (#314)

* finished the lesson title bug

* made chnages based on review

* Update +page.svelte

---------

Co-authored-by: Rotimi Best <best@classroomio.com>

* feat: add sound cdn

* remove console.log

* feat: course types - self paced and live class (#309)

* working on the course type

* trying to mark it persist

* almost done here

* fixed sendig emtpy options bug

* created migration file

* created migration file

* made updates based on PR review

* merge

* update course type card

* add redirect logic with classroomio.com/new

* make new course modal only open/close with url

* feat: page active state (#315)

* finished the active link state

* updated the active link state based on review

* simplify code

---------

Co-authored-by: Rotimi Best <best@classroomio.com>

* add type to course card

---------

Co-authored-by: Rotimi Best <best@classroomio.com>

* general improvements to free tools

* fix timer and styling issues

---------

Co-authored-by: Rotimi Best <best@classroomio.com>
Co-authored-by: Em_Dev <Chifez1@gmail.com>

* update tagline for tools page

* feat: Add Free Section to Landing Page (#332)

* feat: Add Free Section to Landing Page

* PR Fixes

* refactor

* PR Fixes

* restructure

* improve cursor UX

---------

Co-authored-by: Rotimi Best <best@classroomio.com>

* feat: Progress Report Tool (#329)

* checkpoint

* fix: corrections

* finalization

* improve ux of progress tool

* more styling improvements

* PR Fixes

* PR Fixes

* fix: mood bg on report

* fix: generate exercise with AI bug (#331)

* fix: enable lesson cascade delete (#335)

* styling improvements

* chore: hide course grading toggle in settings (#338)

* feat: reset select in community page (#339)

* Feat: Add Reset for comminity dropdown

* - add translation

* refactor

---------

Co-authored-by: Rotimi Best <best@classroomio.com>

* fix: prevent hypen at start or end of organzation or domain name (#340)

* fix: adding hypen to start or end of org or domain name

* fix: add org name validation to settings page

* chore: remove unnecessary variable use

* chore: remove error reset function

* Update OrgSettings.svelte

---------

Co-authored-by: Rotimi Best <best@classroomio.com>

* fix: full screen

* fix: pnpm version error & add top level .nvmrc (#343)

* update name of tool

* chore: add translations for validation messages (#345)

* chore: add validation texts to locale file

* feat: switch all validations from plain text to use i18n locale

* feat: switch review form validations from plain text to use locale file

* run translation script

* chore: add translations for validation messages (#345) (#347)

* chore: add translations for validation messages (#345)

* chore: add validation texts to locale file

* feat: switch all validations from plain text to use i18n locale

* feat: switch review form validations from plain text to use locale file

* run translation script

---------

Co-authored-by: Iniubong Obonguko <iniubongobonguko2018@gmail.com>

* PR Fixes

* Bug fixes on mobile

* Final bug fix

* code improvements

* PR Fixes

* Merge Fix

* PR Fixes

* fix: width

* code cleanup

* feat: Promodo Tool (tracks tim spent)

* feat: Pomodoro timer (without tracking feature)

* fix:
- change pause functionality to next
- correct spelling of pomodoro

* fix: pomodoro logo rename

* styling improvements

---------

Co-authored-by: Rotimi Best <best@classroomio.com>
Co-authored-by: Em_Dev <Chifez1@gmail.com>
Co-authored-by: Iniubong Obonguko <iniubongobonguko2018+github@gmail.com>
Co-authored-by: Iniubong Obonguko <iniubongobonguko2018@gmail.com>
Co-authored-by: Aditya Deshlahre <132184385+adityadeshlahre@users.noreply.github.com>

* feat: Implement Bottom menu (#341)

* popup logic done

* popup UI first phase done

* finished the menupop up for both lms and admin

* finished the menupop up for both lms and admin

* done

* cleanup

* fixed popover position

* still working on the routing

* i think that is it for the day

* updated the menupopup currentorgdomain

* rename popup to bottomMenu

* refactoring

---------

Co-authored-by: Rotimi Best <best@classroomio.com>

* remove logout

* change tagline

* feat: Store todolist to localStorage (#375)

* feat: Save todo to localStorage

* remove fly animation to todos

* feat:  Progress Report avatar update (#376)

* feat: Reorder Avatar list

* quick fix

* change avatar url

* more improvements

* quick background fix

* fix: white background

* body background fix

* update lock

---------

Co-authored-by: Rotimi Best <best@classroomio.com>
Co-authored-by: Em_Dev <Chifez1@gmail.com>
Co-authored-by: Iniubong Obonguko <iniubongobonguko2018+github@gmail.com>
Co-authored-by: Iniubong Obonguko <iniubongobonguko2018@gmail.com>
Co-authored-by: Aditya Deshlahre <132184385+adityadeshlahre@users.noreply.github.com>
This commit is contained in:
Adeyemi Adetunji
2024-09-30 01:36:12 +01:00
committed by GitHub
co-authored by Rotimi Best Em_Dev Iniubong Obonguko Iniubong Obonguko Aditya Deshlahre
parent 91bd9d4414
commit 9c066c79be
106 changed files with 3778 additions and 478 deletions
+1 -1
View File
@@ -12,7 +12,7 @@
## ✨ About ClassroomIO
<img alt="ClassroomIO Courses page" src="https://classroomio.com/classroomio-courses.png" />
<img alt="ClassroomIO Courses page" src="https://brand.cdn.clsrio.com/classroomio-courses.png" />
Streamline training for everyone with ClassroomIO.com. Our all-in-one platform empowers bootcamps, educators, and businesses to manage training programs easily. With our platform, anyone can run multiple classes and cohorts all from one UI. The application is mobile-first, which means that students can access your lesson materials from any device.
@@ -1,30 +0,0 @@
<script lang="ts">
import PrimaryButton from '$lib/components/PrimaryButton/index.svelte';
import { VARIANTS } from '$lib/components/PrimaryButton/constants';
import { supabase } from '$lib/utils/functions/supabase';
import { capturePosthogEvent } from '$lib/utils/services/posthog';
import { currentOrg, orgs, defaultCurrentOrgState } from '$lib/utils/store/org';
import { user, profile, defaultProfileState, defaultUserState } from '$lib/utils/store/user';
import { t } from '$lib/utils/functions/translations';
import posthog from 'posthog-js';
import { goto } from '$app/navigation';
async function logout() {
const { error } = await supabase.auth.signOut();
if (error) {
console.error('Error logging out: ', error);
}
currentOrg.set(defaultCurrentOrgState);
orgs.set([]);
user.set(defaultUserState);
profile.set(defaultProfileState);
capturePosthogEvent('user_logged_out');
posthog.reset();
goto('/login');
}
</script>
<PrimaryButton variant={VARIANTS.OUTLINED} label={$t('settings.profile.logout')} onClick={logout} />
@@ -3,16 +3,17 @@
import HelpIcon from 'carbon-icons-svelte/lib/Help.svelte';
import LicenseDraft from 'carbon-icons-svelte/lib/LicenseDraft.svelte';
import Explore from 'carbon-icons-svelte/lib/Explore.svelte';
import Settings from 'carbon-icons-svelte/lib/Settings.svelte';
import HomeIcon from '$lib/components/Icons/HomeIcon.svelte';
import CourseIcon from '$lib/components/Icons/CourseIcon.svelte';
import CommunityIcon from '$lib/components/Icons/CommunityIcon.svelte';
import Avatar from '$lib/components/Avatar/index.svelte';
import { profile } from '$lib/utils/store/user';
import { NavClasses } from '$lib/utils/constants/reusableClass';
import { sideBar } from '$lib/components/Org/store';
import { profileMenu, sideBar } from '$lib/components/Org/store';
import { t } from '$lib/utils/functions/translations';
import { currentOrg } from '$lib/utils/store/org';
import { ChevronRight } from 'carbon-icons-svelte';
import ProfileMenu from '$lib/components/Org/ProfileMenu/index.svelte';
interface SideLinks {
name: string;
@@ -73,70 +74,87 @@
};
</script>
<aside
class={`${
$sideBar.hidden
? '-translate-x-[100%] absolute md:translate-x-0 md:relative z-40'
: 'translate-x-0 absolute md:relative z-40'
} overflow-y-auto transition w-[250px] min-w-[250px] bg-gray-100 dark:bg-neutral-900 h-[calc(100vh-48px)]`}
>
<div class="h-full flex flex-col">
<div class="border-b border-gray-200 dark:border-neutral-600 pt-5 px-4">
<div class="w-full flex flex-col items-center">
<Avatar
src={$profile.avatar_url}
name={$profile.fullname}
shape="rounded-full"
width="w-20"
height="h-20"
/>
<div bind:this={$profileMenu.ref} class="static md:relative">
<aside
class={`${
$sideBar.hidden
? '-translate-x-[100%] absolute md:translate-x-0 md:relative z-40 top-[48px] md:top-0'
: 'translate-x-0 absolute md:relative z-40 top-[48px] md:top-0'
} overflow-y-auto transition w-[250px] min-w-[250px] bg-gray-100 dark:bg-neutral-900 h-[calc(100vh-48px)]`}
>
<div class="h-full flex flex-col">
<div class="border-b border-gray-200 dark:border-neutral-600 pt-5 px-4">
<div class="w-full flex flex-col items-center">
<Avatar
src={$profile.avatar_url}
name={$profile.fullname}
shape="rounded-full"
width="w-20"
height="h-20"
/>
<div class="mt-5 flex justify-center w-full">
<p
class="dark:text-white text-lg font-bold whitespace-nowrap truncate max-w-[80%] text-center"
>
{$profile.fullname}
</p>
</div>
</div>
<ul class="my-5">
{#each sideLinks as item (item.link)}
<a href={item.link} class="text-black" on:click={toggleSidebar}>
<li
class="flex items-center py-3 px-4 mb-2 {NavClasses.item} {isActive(
$page.url.pathname,
`${item.link}`
)
? NavClasses.active
: 'dark:text-white'}"
<div class="mt-5 flex justify-center w-full">
<p
class="dark:text-white text-lg font-bold whitespace-nowrap truncate max-w-[80%] text-center"
>
<svelte:component this={item.icon} size={24} class="carbon-icon dark:fill-[#fff]" />
<p class="dark:text-white ml-2">{item.name}</p>
</li>
</a>
{/each}
{$profile.fullname}
</p>
</div>
</div>
<ul class="my-5">
{#each sideLinks as item (item.name)}
<a href={item.link} class="text-black" on:click={toggleSidebar}>
<li
class="flex items-center py-3 px-4 mb-2 {NavClasses.item} {isActive(
$page.url.pathname,
`${item.link}`
)
? NavClasses.active
: 'dark:text-white'}"
>
<svelte:component this={item.icon} size={24} class="carbon-icon dark:fill-[#fff]" />
<p class="dark:text-white ml-2">{item.name}</p>
</li>
</a>
{/each}
</ul>
</div>
<span class="flex-grow" />
<ul class="my-5 pb-5 px-4">
<a href="/lms" class="text-black" on:click={toggleSidebar}>
<li class="flex items-center py-3 px-4 mb-2 rounded">
<HelpIcon size={20} class="carbon-icon dark:text-white" />
<p class="dark:text-white ml-2">{$t('lms_navigation.help')}</p>
</li>
</a>
<button
class="w-full"
on:click={() => {
$profileMenu.open = !$profileMenu.open;
$sideBar.hidden = true;
}}
>
<div
class="text-black no-underline cursor-pointer flex items-center justify-between mb-2 px-2.5 py-1.5 w-full {NavClasses.item}"
>
<div class="flex text-start items-center justify-start space-x-1">
<Avatar
src={$profile.avatar_url}
name={$profile.username}
width="w-[1.2rem]"
height="h-[1.2rem]"
/>
<p class="text-sm font-medium truncate max-w-full">{$profile.fullname}</p>
</div>
<div>
<ChevronRight />
</div>
</div>
</button>
</ul>
</div>
<span class="flex-grow" />
<ul class="my-5 pb-5 px-4">
<a href="/lms" class="text-black" on:click={toggleSidebar}>
<li class="flex items-center py-3 px-4 mb-2 rounded">
<HelpIcon size={20} class="carbon-icon dark:text-white" />
<p class="dark:text-white ml-2">{$t('lms_navigation.help')}</p>
</li>
</a>
<a href="/lms/settings" class="text-black" on:click={toggleSidebar}>
<li
class="flex items-center py-3 px-4 mb-2 {NavClasses.item} {isActive(
$page.url.pathname,
`/lms/settings`
) && NavClasses.active}"
>
<Settings size={20} class="carbon-icon dark:text-white" />
<p class="dark:text-white ml-2">{$t('lms_navigation.settings')}</p>
</li>
</a>
</ul>
</div>
</aside>
</aside>
<ProfileMenu />
</div>
@@ -7,7 +7,6 @@
import { supabase } from '$lib/utils/functions/supabase';
import { profile } from '$lib/utils/store/user';
import { snackbar } from '$lib/components/Snackbar/store';
import LogoutButton from '$lib/components/Buttons/Logout/index.svelte';
import generateUUID from '$lib/utils/functions/generateUUID';
import { t } from '$lib/utils/functions/translations';
import LanguagePicker from '$lib/components/Org/Settings/LanguagePicker.svelte';
@@ -120,6 +119,5 @@
isDisabled={loading}
onClick={handleUpdate}
/>
<LogoutButton />
</Row>
</Grid>
@@ -0,0 +1,165 @@
<script>
import { globalStore } from '$lib/utils/store/app';
import { user, profile, defaultProfileState, defaultUserState } from '$lib/utils/store/user';
import {
currentOrg,
orgs,
defaultCurrentOrgState,
currentOrgDomain,
currentOrgPath
} from '$lib/utils/store/org';
import { ChevronDown, Settings } from 'carbon-icons-svelte';
import Logout from 'carbon-icons-svelte/lib/Logout.svelte';
import Rocket from 'carbon-icons-svelte/lib/Rocket.svelte';
import NewTab from 'carbon-icons-svelte/lib/NewTab.svelte';
import { goto } from '$app/navigation';
import { profileMenu } from '../store';
import { supabase } from '$lib/utils/functions/supabase';
import { capturePosthogEvent } from '$lib/utils/services/posthog';
import { t } from '$lib/utils/functions/translations';
import posthog from 'posthog-js';
async function logout() {
const { error } = await supabase.auth.signOut();
if (error) {
console.error('Error logging out: ', error);
}
currentOrg.set(defaultCurrentOrgState);
orgs.set([]);
user.set(defaultUserState);
profile.set(defaultProfileState);
capturePosthogEvent('user_logged_out');
posthog.reset();
closeMenu();
goto('/login');
}
function closeMenu() {
$profileMenu.open = false;
}
</script>
<div class="md:px-4 md:py-4 py-2 px-2 rounded-md cursor-pointer">
<div class="border-b py-3 space-y-4">
<p class="text-xs font-semibold text-gray-500">{$t('profileMenu.profile')}</p>
<a
href={`${!$globalStore.isOrgSite ? $currentOrgPath : '/lms'}/settings`}
class="flex items-center justify-between hover:no-underline"
on:click={closeMenu}
>
<span class="flex items-center gap-2 max-w-[70%]">
<img src={$profile.avatar_url} alt="profile" class="h-8 w-8 rounded-full" />
<div>
<p class="text-sm font-semibold w-[80%] truncate">{$profile.fullname}</p>
<p class="text-xs w-[80%] truncate">{$profile.email}</p>
</div>
</span>
<div>
<Settings size={20} />
</div>
</a>
</div>
{#if !$globalStore.isOrgSite}
<div class="border-b py-3 space-y-4">
<p class="text-xs font-semibold text-gray-500">{$t('profileMenu.current_org')}</p>
<a
href={`${$currentOrgPath}/settings?tab=org`}
class="flex items-center justify-between hover:no-underline"
on:click={closeMenu}
>
<span class="flex items-center gap-2 max-w-[70%]">
<div
class="flex items-center justify-center h-8 w-8 p-4 bg-blue-900 text-white font-semibold rounded-lg"
>
{$currentOrg.shortName}
</div>
<div>
<p class="text-sm font-semibold w-[80%] truncate">{$currentOrg.name}</p>
<p class="text-xs w-[80%] truncate">
{$currentOrgDomain}
</p>
</div>
</span>
<div>
<Settings size={20} />
</div>
</a>
</div>
{/if}
<div class="border-b py-3 space-y-4">
<p class="text-xs font-semibold text-gray-500">{$t('profileMenu.free_tools')}</p>
<a
href="https://classroomio.com/tools/progress"
target="_blank"
class="flex items-center gap-2"
on:click={closeMenu}
>
<img src="/progress.svg" alt="progress" class="h-6 w-6 rounded-full" />
<p class="text-sm font-semibold">{$t('profileMenu.progress')}</p>
</a>
<a
href="https://classroomio.com/tools/activity-stopwatch"
target="_blank"
class=" flex items-center gap-2"
on:click={closeMenu}
>
<img src="/timer.svg" alt="timer" class="h-6 w-6 rounded-full" />
<p class="text-sm font-semibold">{$t('profileMenu.timer')}</p>
</a>
<a
href="https://classroomio.com/tools/tic-tac-toe"
target="_blank"
class=" flex items-center gap-2"
on:click={closeMenu}
>
<img src="/tictac.svg" alt="tic_tac_toe" class="h-6 w-6 rounded-full" />
<p class="text-sm font-semibold">{$t('profileMenu.tic_tac')}</p>
</a>
<a
href="https://classroomio.com/tools"
on:click={closeMenu}
target="_blank"
class="flex ml-auto w-fit items-center justify-end"
>
<div class="text-blue-900 font-semibold text-xs flex items-center gap-1">
{$t('profileMenu.see_more')}
<ChevronDown class="text-blue-900" />
</div>
</a>
</div>
{#if !$globalStore.isOrgSite}
<div class="border-b py-3 space-y-4">
<a
href="https://classroomio.com/roadmap"
target="_blank"
on:click={closeMenu}
class="hover:no-underline flex items-center gap-2"
>
<NewTab />
<p class="text-sm font-semibold">{$t('profileMenu.whats_new')}</p>
</a>
<a
href="https://classroomio.com/blog/launch-week"
target="_blank"
on:click={closeMenu}
class="hover:no-underline flex items-center gap-2"
>
<Rocket />
<p class="text-sm font-semibold">{$t('profileMenu.launch_week')}</p>
</a>
</div>
{/if}
<button on:click={logout} class="w-full pt-3 space-y-4">
<span class="flex items-center gap-2">
<Logout />
<p class="text-sm font-semibold">{$t('settings.profile.logout')}</p>
</span>
</button>
</div>
@@ -0,0 +1,31 @@
<script>
import { Popover } from 'carbon-components-svelte';
import Menu from './Menu.svelte';
import Modal from '$lib/components/Modal/index.svelte';
import { isMobile } from '$lib/utils/store/useMobile';
import { profileMenu } from '../store';
</script>
{#if $isMobile}
<Modal
bind:open={$profileMenu.open}
onClose={() => ($profileMenu.open = false)}
width="w-4/5"
containerClass="h-full !max-h-[70vh] pt-0 pb-2"
headerClass="py-1"
>
<Menu />
</Modal>
{:else}
<Popover
bind:open={$profileMenu.open}
align="right-bottom"
caret={true}
on:click:outside={({ detail }) => {
console.log('on:click:outside');
$profileMenu.open = !!$profileMenu.ref?.contains(detail.target);
}}
>
<Menu />
</Popover>
{/if}
@@ -8,7 +8,6 @@
import { supabase } from '$lib/utils/functions/supabase';
import { profile } from '$lib/utils/store/user';
import { snackbar } from '$lib/components/Snackbar/store';
import LogoutButton from '$lib/components/Buttons/Logout/index.svelte';
import generateUUID from '$lib/utils/functions/generateUUID';
import { t } from '$lib/utils/functions/translations';
import LanguagePicker from './LanguagePicker.svelte';
@@ -129,6 +128,5 @@
isDisabled={loading}
onClick={handleUpdate}
/>
<LogoutButton />
</Row>
</Grid>
@@ -21,24 +21,21 @@
function getSelectedByTab(tabKey = '') {
const tab = tabs.find((t) => t.tabKey === tabKey);
return tab ? tab.key : 0;
}
function changeRouteOnTabChange(key = 0) {
function onTabChange(e) {
const key = e.detail;
const tab = tabs.find((t) => t.key === key);
if (tab) {
return goto(tab.href);
goto(tab.href);
}
}
onMount(() => {
$: if (browser) {
query = new URLSearchParams($page.url.search);
tabKey = query.get('tab') || '';
selected = getSelectedByTab(tabKey);
});
$: {
if (browser) {
changeRouteOnTabChange(selected);
}
}
$: {
@@ -82,7 +79,7 @@
}
</script>
<Tabs autoWidth bind:selected>
<Tabs autoWidth bind:selected on:change={onTabChange}>
{#each tabs as tab}
<Tab label={tab.label} href={tab.href} disabled={tab.disabled} />
{/each}
@@ -2,7 +2,7 @@
import { page } from '$app/stores';
import HelpIcon from 'carbon-icons-svelte/lib/Help.svelte';
import ForumIcon from 'carbon-icons-svelte/lib/Forum.svelte';
import { SettingsAdjust } from 'carbon-icons-svelte';
import { ChevronRight, SettingsAdjust } from 'carbon-icons-svelte';
import PrimaryButton from '$lib/components/PrimaryButton/index.svelte';
import OrgSelector from '$lib/components/OrgSelector/OrgSelector.svelte';
import HomeIcon from '$lib/components/Icons/HomeIcon.svelte';
@@ -11,13 +11,15 @@
import SiteSettingsIcon from '$lib/components/Icons/SiteSettingsIcon.svelte';
import AudienceIcon from '$lib/components/Icons/AudienceIcon.svelte';
import Avatar from '$lib/components/Avatar/index.svelte';
import { currentOrgPath, currentOrgPlan, isFreePlan } from '$lib/utils/store/org';
import { currentOrgPath, isFreePlan } from '$lib/utils/store/org';
import { isOrgAdmin } from '$lib/utils/store/org';
import { profile } from '$lib/utils/store/user';
import { NavClasses } from '$lib/utils/constants/reusableClass';
import { sideBar } from './store';
import { sideBar, profileMenu } from './store';
import { t } from '$lib/utils/functions/translations';
import { goto } from '$app/navigation';
import ProfileMenu from '$lib/components/Org/ProfileMenu/index.svelte';
interface menuItems {
label: string;
@@ -45,43 +47,6 @@
goto(window.location.pathname + '?upgrade=true');
};
// $: {
// menuItems = [
// {
// path: '',
// label: 'Dashboard',
// show: true
// },
// // {
// // path: '/quiz',
// // label: 'Quizzes'
// // },
// {
// path: '/courses',
// label: 'Courses',
// show: true
// },
// {
// path: '/community',
// label: 'Community',
// show: true
// },
// // {
// // path: '/site',
// // label: 'Site settings',
// // },
// {
// path: '/audience',
// label: 'Audience',
// show: true
// },
// {
// path: '/setup',
// label: 'Setup',
// show: $isOrgAdmin
// }
// ];
// }
$: menuItems = [
{
path: '',
@@ -111,99 +76,110 @@
];
</script>
<aside
class={`${
$sideBar.hidden
? 'absolute z-40 -translate-x-[100%] md:relative md:translate-x-0'
: 'absolute z-40 translate-x-0 md:relative'
} border-r-1 h-[calc(100vh-48px)] w-[250px] min-w-[250px] overflow-y-auto border border-b-0 border-l-0 border-t-0 border-gray-100 dark:border-neutral-600 bg-gray-100 transition dark:bg-neutral-900`}
>
<div class="flex h-full flex-col">
<div class="">
<OrgSelector />
<div bind:this={$profileMenu.ref} class="static md:relative">
<aside
class={`${
$sideBar.hidden
? 'absolute z-40 -translate-x-[100%] md:relative md:translate-x-0 top-[48px] md:top-0'
: 'absolute z-40 translate-x-0 md:relative top-[48px] md:top-0'
} border-r-1 h-[calc(100vh-48px)] w-[250px] min-w-[250px] overflow-y-auto border border-b-0 border-l-0 border-t-0 border-gray-100 dark:border-neutral-600 bg-gray-100 transition dark:bg-neutral-900`}
>
<div class="flex h-full flex-col">
<div class="">
<OrgSelector />
<ul class="mt-4 my-2 px-4">
{#each menuItems as menuItem}
{#if menuItem.show}
<a
href="{$currentOrgPath}{menuItem.path}"
class="text-black no-underline"
on:click={toggleSidebar}
>
<li
class="mb-1 flex items-center gap-2.5 px-2.5 py-2 {NavClasses.item} {isActive(
$page.url.pathname,
`${$currentOrgPath}${menuItem.path}`
)
? NavClasses.active
: 'dark:text-white'}"
<ul class="mt-4 my-2 px-4">
{#each menuItems as menuItem}
{#if menuItem.show}
<a
href="{$currentOrgPath}{menuItem.path}"
class="text-black no-underline"
on:click={toggleSidebar}
>
{#if menuItem.path === ''}
<HomeIcon />
{:else if menuItem.path === '/courses'}
<CourseIcon />
{:else if menuItem.path === '/site'}
<SiteSettingsIcon />
{:else if menuItem.path === '/community'}
<ForumIcon size={20} class="carbon-icon fill-[#000] dark:fill-[#fff]" />
{:else if menuItem.path === '/quiz'}
<QuizIcon />
{:else if menuItem.path === '/audience'}
<AudienceIcon />
{:else if menuItem.path === '/setup'}
<SettingsAdjust />
{/if}
<p class="text-sm font-medium">{menuItem.label}</p>
</li>
</a>
{/if}
{/each}
<li
class="mb-1 flex items-center gap-2.5 px-2.5 py-2 {NavClasses.item} {isActive(
$page.url.pathname,
`${$currentOrgPath}${menuItem.path}`
)
? NavClasses.active
: 'dark:text-white'}"
>
{#if menuItem.path === ''}
<HomeIcon />
{:else if menuItem.path === '/courses'}
<CourseIcon />
{:else if menuItem.path === '/site'}
<SiteSettingsIcon />
{:else if menuItem.path === '/community'}
<ForumIcon size={20} class="carbon-icon fill-[#000] dark:fill-[#fff]" />
{:else if menuItem.path === '/quiz'}
<QuizIcon />
{:else if menuItem.path === '/audience'}
<AudienceIcon />
{:else if menuItem.path === '/setup'}
<SettingsAdjust />
{/if}
<p class="text-sm font-medium">{menuItem.label}</p>
</li>
</a>
{/if}
{/each}
</ul>
</div>
<span class="flex-grow" />
{#if $isFreePlan}
<div
class="border-primary-700 mx-4 flex flex-col items-center justify-center gap-4 rounded-md border px-2 py-6 text-center hover:scale-95 transition-all ease-in-out"
>
<img src="/upgrade.png" alt="upgrade" class="h-16 w-16" />
<span class="flex flex-col gap-1">
<p class="text-base font-semibold">{$t('org_navigation.early_adopter')}</p>
<p class="text-xs">{$t('org_navigation.unlock')}</p>
</span>
<PrimaryButton
label={$t('org_navigation.upgrade')}
onClick={openModal}
className="font-normal"
/>
</div>
{/if}
<ul class="my-5 px-4 pb-5">
<a href={$currentOrgPath} class="text-black no-underline" on:click={toggleSidebar}>
<li class="mb-2 flex items-center rounded px-2.5 py-1.5">
<HelpIcon size={20} class="carbon-icon dark:text-white" />
<p class="ml-2.5 dark:text-white text-sm font-medium">{$t('org_navigation.help')}</p>
</li>
</a>
<button
class="w-full"
on:click={() => {
$profileMenu.open = !$profileMenu.open;
$sideBar.hidden = true;
}}
>
<div
class="text-black no-underline cursor-pointer flex items-center justify-between gap-2.5 px-2.5 py-2 mb-2 {NavClasses.item}"
>
<div class="flex text-start items-center justify-start space-x-1 w-full">
<Avatar
src={$profile.avatar_url}
name={$profile.username}
width="w-[1.2rem]"
height="h-[1.2rem]"
/>
<p class="text-sm font-medium truncate max-w-full">{$profile.fullname}</p>
</div>
<div>
<ChevronRight />
</div>
</div>
</button>
</ul>
</div>
<span class="flex-grow" />
</aside>
{#if $isFreePlan}
<div
class="border-primary-700 mx-4 flex flex-col items-center justify-center gap-4 rounded-md border px-2 py-6 text-center hover:scale-95 transition-all ease-in-out"
>
<img src="/upgrade.png" alt="upgrade" class="h-16 w-16" />
<span class="flex flex-col gap-1">
<p class="text-base font-semibold">{$t('org_navigation.early_adopter')}</p>
<p class="text-xs">{$t('org_navigation.unlock')}</p>
</span>
<PrimaryButton
label={$t('org_navigation.upgrade')}
onClick={openModal}
className="font-normal"
/>
</div>
{/if}
<ul class="my-5 px-4 pb-5">
<a href={$currentOrgPath} class="text-black no-underline" on:click={toggleSidebar}>
<li class="mb-2 flex items-center rounded px-2.5 py-1.5">
<HelpIcon size={20} class="carbon-icon dark:text-white" />
<p class="ml-2.5 dark:text-white text-sm font-medium">{$t('org_navigation.help')}</p>
</li>
</a>
<a href="{$currentOrgPath}/settings" class="text-black no-underline" on:click={toggleSidebar}>
<li
class="mb-2 flex items-center px-2.5 py-1.5 {NavClasses.item} {isActive(
$page.url.pathname,
`${$currentOrgPath}/settings`
)
? NavClasses.active
: 'dark:text-white'}"
>
<Avatar
src={$profile.avatar_url}
name={$profile.username}
width="w-[1.2rem]"
height="h-[1.2rem]"
/>
<p class="ml-2.5 text-sm font-medium">{$t('org_navigation.settings')}</p>
</li>
</a>
</ul>
</div>
</aside>
<ProfileMenu />
</div>
@@ -7,3 +7,11 @@ export const newOrgModal = writable({
export const sideBar = writable({
hidden: true
});
export const profileMenu = writable<{
ref?: HTMLDivElement;
open: boolean;
}>({
ref: undefined,
open: false
});
@@ -1326,6 +1326,17 @@
"specialization": {
"course_tag": "Spezialisierung"
},
"profileMenu": {
"profile": "PROFIL",
"current_org": "AKTUELLE ORGANISATION",
"free_tools": "KOSTENLOSE WERKZEUGE",
"progress": "Fortschrittsbericht",
"timer": "Aktivitätstimer",
"tic_tac": "Tictac-Zehe",
"see_more": "Mehr sehen",
"whats_new": "Was ist neu",
"launch_week": "Startwoche"
},
"validations": {
"invalid_type_error": "Darf nicht leer sein",
"email": {
@@ -49,7 +49,18 @@
"forgot": "Forgot password?",
"login": "Log In"
},
"profileMenu": {
"profile": "PROFILE",
"current_org": "CURRENT ORGANIZATION",
"free_tools": "FREE TOOLS",
"progress": "Progress report",
"timer": "Activity timer",
"tic_tac": "Tictac toe",
"see_more": "See more",
"whats_new": "What's new",
"launch_week": "Launch week"
},
"settings": {
"heading": "Settings",
"subheadings": {
@@ -1326,6 +1326,17 @@
"specialization": {
"course_tag": "Especialización"
},
"profileMenu": {
"profile": "PERFIL",
"current_org": "ORGANIZACIÓN ACTUAL",
"free_tools": "HERRAMIENTAS GRATUITAS",
"progress": "Informe de progreso",
"timer": "Temporizador de actividad",
"tic_tac": "dedo del pie tres en raya",
"see_more": "Ver más",
"whats_new": "Qué hay de nuevo",
"launch_week": "Semana de lanzamiento"
},
"validations": {
"invalid_type_error": "No debe estar vacío",
"email": {
@@ -1326,6 +1326,17 @@
"specialization": {
"course_tag": "Spécialisation"
},
"profileMenu": {
"profile": "PROFIL",
"current_org": "ORGANISATION ACTUELLE",
"free_tools": "OUTILS GRATUITS",
"progress": "Rapport d'étape",
"timer": "Minuterie d'activité",
"tic_tac": "Tic-tac",
"see_more": "Voir plus",
"whats_new": "Quoi de neuf",
"launch_week": "Semaine de lancement"
},
"validations": {
"invalid_type_error": "Ne doit pas être vide",
"email": {
@@ -1326,6 +1326,17 @@
"specialization": {
"course_tag": "विशेषज्ञता"
},
"profileMenu": {
"profile": "प्रोफ़ाइल",
"current_org": "वर्तमान संघ",
"free_tools": "मुफ़्त उपकरण",
"progress": "प्रगति रिपोर्ट",
"timer": "गतिविधि टाइमर",
"tic_tac": "टिक टीएसी को पैर की अंगुली",
"see_more": "और देखें",
"whats_new": "नया क्या है",
"launch_week": "लॉन्च सप्ताह"
},
"validations": {
"invalid_type_error": "खाली नहीं होना चाहिए",
"email": {
@@ -1326,6 +1326,17 @@
"specialization": {
"course_tag": "Especialização"
},
"profileMenu": {
"profile": "PERFIL",
"current_org": "ORGANIZAÇÃO ATUAL",
"free_tools": "FERRAMENTAS GRATUITAS",
"progress": "Relatório de progresso",
"timer": "Temporizador de atividade",
"tic_tac": "Jogo da velha",
"see_more": "Ver mais",
"whats_new": "O que há de novo",
"launch_week": "Semana de lançamento"
},
"validations": {
"invalid_type_error": "Não deve estar vazio",
"email": {
@@ -1326,6 +1326,17 @@
"specialization": {
"course_tag": "Специализация"
},
"profileMenu": {
"profile": "ПРОФИЛЬ",
"current_org": "ТЕКУЩАЯ ОРГАНИЗАЦИЯ",
"free_tools": "БЕСПЛАТНЫЕ ИНСТРУМЕНТЫ",
"progress": "Отчет о ходе работы",
"timer": "Таймер активности",
"tic_tac": "Крестики-нолики",
"see_more": "Узнать больше",
"whats_new": "Что нового",
"launch_week": "Неделя запуска"
},
"validations": {
"invalid_type_error": "Не должно быть пустым",
"email": {
@@ -1326,6 +1326,17 @@
"specialization": {
"course_tag": "Chuyên môn"
},
"profileMenu": {
"profile": "HỒ SƠ",
"current_org": "TỔ CHỨC HIỆN TẠI",
"free_tools": "CÔNG CỤ MIỄN PHÍ",
"progress": "Báo cáo tiến trình",
"timer": "Hẹn giờ hoạt động",
"tic_tac": "TIC Tac Toe",
"see_more": "Xem thêm",
"whats_new": "Có gì mới",
"launch_week": "Tuần ra mắt"
},
"validations": {
"invalid_type_error": "Không được để trống",
"email": {
@@ -10,6 +10,8 @@
export let data;
let ref = null;
$: if ($currentOrg.id && data.orgName === '*') {
const newUrl = $page.url.pathname.replace('*', $currentOrg.siteName);
goto(newUrl + $page.url.search);
@@ -2,7 +2,6 @@
import UserAvatarIcon from 'carbon-icons-svelte/lib/UserAvatar.svelte';
import BookIcon from 'carbon-icons-svelte/lib/Book.svelte';
import TextField from '$lib/components/Form/TextField.svelte';
import LogoutButton from '$lib/components/Buttons/Logout/index.svelte';
import PrimaryButton from '$lib/components/PrimaryButton/index.svelte';
import UploadImage from '$lib/components/UploadImage/index.svelte';
import { supabase } from '$lib/utils/functions/supabase';
@@ -193,7 +192,6 @@
</div>
</div>
</div>
<LogoutButton />
</div>
<!-- {:else if loading}
<Box>
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 148 KiB

+10
View File
@@ -0,0 +1,10 @@
<svg width="26" height="26" viewBox="0 0 26 26" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="1" y="0.630737" width="24" height="24.7385" rx="12" fill="white"/>
<rect x="1" y="0.630737" width="24" height="24.7385" rx="12" stroke="#EAEAEA" stroke-width="0.52635"/>
<path d="M19.4979 16.3003H19.7057L19.6813 16.094C19.6209 15.5847 19.3838 15.1232 18.9739 14.7906C18.5647 14.4585 17.9961 14.2647 17.2876 14.2647C15.7814 14.2647 14.6792 15.4281 14.6792 16.9558C14.6792 18.4914 15.7962 19.6606 17.3151 19.6606C18.0049 19.6606 18.5714 19.4935 18.9882 19.1731C19.4074 18.8508 19.6577 18.3865 19.7359 17.8283L19.7653 17.6181H19.553H18.7612H18.6205L18.5832 17.7537C18.525 17.9649 18.4221 18.1574 18.2368 18.299C18.0511 18.441 17.7619 18.5477 17.3014 18.5477C16.4549 18.5477 15.8196 17.9094 15.8196 16.9627C15.8196 16.008 16.4559 15.3707 17.2945 15.3707C17.7394 15.3707 18.0175 15.4783 18.1964 15.62C18.3759 15.7623 18.4765 15.9554 18.5352 16.1654L18.5729 16.3003H18.713H19.4979Z" fill="#9747FF" stroke="#9747FF" stroke-width="0.369231"/>
<path d="M9.94507 19.3017L9.85279 19.5505H10.1182H10.9514H11.0816L11.1253 19.4279L11.6875 17.8497H13.0905L13.7058 19.4328L13.7515 19.5505H13.8778H14.7041H14.9738L14.8763 19.2991L13.0033 14.4722L12.9576 14.3543H12.8312H11.9085H11.7801L11.7354 14.4747L9.94507 19.3017ZM11.9321 16.8745L12.3706 15.8367L12.8189 16.8745H11.9321Z" fill="#9747FF" stroke="#9747FF" stroke-width="0.369231"/>
<path d="M8.64565 19.5571H8.83026V19.3725V15.4531H9.9815H10.1661V15.2685V14.5524V14.3678H9.9815H6.53857H6.35396V14.5524V15.2685V15.4531H6.53857H7.68982V19.3725V19.5571H7.87443H8.64565Z" fill="#9747FF" stroke="#9747FF" stroke-width="0.369231"/>
<path d="M19.7394 9.60292H19.9472L19.9228 9.39658C19.8467 8.75465 19.5481 8.1743 19.0326 7.75591C18.5178 7.33808 17.7993 7.09175 16.8983 7.09175C14.9931 7.09175 13.5981 8.5617 13.5981 10.4981C13.5981 12.4445 15.0119 13.9222 16.9337 13.9222C17.8121 13.9222 18.528 13.7094 19.0521 13.3064C19.5786 12.9016 19.8943 12.3179 19.9931 11.6124L20.0225 11.4022H19.8102H18.7924H18.6517L18.6144 11.5378C18.5376 11.8168 18.4003 12.0755 18.1503 12.2666C17.9 12.4579 17.516 12.5971 16.916 12.5971C15.7964 12.5971 14.9586 11.7505 14.9586 10.507C14.9586 9.25362 15.7973 8.40804 16.9071 8.40804C17.4877 8.40804 17.8576 8.54854 18.0991 8.73988C18.3413 8.93172 18.4753 9.19114 18.5526 9.46799L18.5903 9.60292H18.7304H19.7394Z" fill="#9747FF" stroke="#9747FF" stroke-width="0.369231"/>
<path d="M11.8378 13.6048V13.7894H12.0225H13.0226H13.2072V13.6048V7.40906V7.22444H13.0226H12.0225H11.8378V7.40906V13.6048Z" fill="#9747FF" stroke="#9747FF" stroke-width="0.369231"/>
<path d="M9.24698 13.7894H9.4316V13.6048V8.51418H10.9641H11.1487V8.32956V7.40906V7.22444H10.9641H6.53857H6.35396V7.40906V8.32956V8.51418H6.53857H8.07105V13.6048V13.7894H8.25567H9.24698Z" fill="#9747FF" stroke="#9747FF" stroke-width="0.369231"/>
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 97 KiB