Skip to content

Update/loading #280

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Oct 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 17 additions & 30 deletions src/src/lib/components/workspaces/workspace/Sidebar.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,13 @@
type NavLink = { name: string; href: string };

interface LecturerLinks {
dashboard: NavLink[];
announcements: NavLink[];
management: NavLink[];
resources: NavLink[];
}

interface StudentLinks {
announcements: NavLink[];
courseWork: NavLink[];
additional: NavLink[];
}
Expand All @@ -36,25 +37,24 @@

const navLinks: NavLinks = {
lecturer: {
dashboard: [{ name: 'Dashboard', href: workspaceURL + '/dashboard' }],
announcements: [{ name: 'Announcements', href: workspaceURL + '/announcements' }],
management: [
{ name: 'Grade Center', href: workspaceURL + '/gradecenter' },
{ name: 'Announcements', href: workspaceURL + '/announcements' },
{ name: 'Materials', href: workspaceURL + '/materials' },
{ name: 'Lessons', href: workspaceURL + '/lessons' },
{ name: 'Live Sessions', href: workspaceURL + '/lessons' },
{ name: 'Quizzes', href: workspaceURL + '/quizzes' },
{ name: 'Interactive Lessons', href: workspaceURL + '/interactive' }
{ name: 'Practices', href: workspaceURL + '/interactive' }
],
resources: [{ name: 'Environments', href: workspaceURL + '/environments' }]
},
student: {
announcements: [{ name: 'Announcements', href: workspaceURL + '/announcements' }],
courseWork: [
{ name: 'Announcements', href: workspaceURL + '/announcements' },
{ name: 'Activities', href: workspaceURL + '/activities' },
{ name: 'Materials', href: workspaceURL + '/materials' },
{ name: 'Lessons', href: workspaceURL + '/lessons' },
{ name: 'Live Sessions', href: workspaceURL + '/lessons' },
{ name: 'Quizzes', href: workspaceURL + '/quizzes' },
{ name: 'Interactive Lessons', href: workspaceURL + '/interactive' }
{ name: 'Practices', href: workspaceURL + '/interactive' }
],
additional: [
{ name: 'Environments', href: workspaceURL + '/environments' },
Expand All @@ -66,7 +66,7 @@
$: currentLinks = navLinks[role];

function isLecturerLinks(links: LecturerLinks | StudentLinks): links is LecturerLinks {
return 'dashboard' in links;
return 'management' in links;
}

function isStudentLinks(links: LecturerLinks | StudentLinks): links is StudentLinks {
Expand Down Expand Up @@ -94,28 +94,15 @@
</NavBrand>
<NavHamburger />
<NavUl>
{#if isLecturerLinks(currentLinks)}
{#if currentLinks.dashboard.length > 1}
<NavLi class="nav-link cursor-pointer">
Dashboard<ChevronDownOutline
class="ms-2 inline h-6 w-6 text-primary-800 dark:text-white"
/>
</NavLi>
<Dropdown class="z-20 w-44">
{#each currentLinks.dashboard as { name, href }}
<DropdownItem {href}>{name}</DropdownItem>
{/each}
</Dropdown>
{:else}
<NavLi
class="nav-link"
href={currentLinks.dashboard[0].href}
active={activeUrl === currentLinks.dashboard[0].href}
>
{currentLinks.dashboard[0].name}
</NavLi>
{/if}
<NavLi
class="nav-link"
href={currentLinks.announcements[0].href}
active={activeUrl === currentLinks.announcements[0].href}
>
{currentLinks.announcements[0].name}
</NavLi>

{#if isLecturerLinks(currentLinks)}
{#if currentLinks.management.length > 1}
<NavLi class="nav-link cursor-pointer">
Management<ChevronDownOutline
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,24 @@
<div class="sm:flex sm:items-center sm:justify-between">
<div>
<div class="flex items-center gap-x-3">
<h2 class="text-xl font-bold text-gray-800 dark:text-white">Interactive Lessons</h2>
<h2 class="text-xl font-bold text-gray-800 dark:text-white">Practice Material</h2>
<span
class="rounded-full bg-green-100 px-3 py-1 text-xs text-green-600 dark:bg-gray-800 dark:text-green-400"
>
{interactive.length} Interactive lessons
{interactive.length} Practice material
</span>
</div>
</div>
{#if role === 'lecturer'}
<Button size="sm" on:click={() => (isCreateModalOpen = true)}>Create Assesment</Button>
<Button size="sm" on:click={() => (isCreateModalOpen = true)}>Create Practice Material</Button
>
{/if}
</div>
<br />

{#if interactive.length === 0}
<p class="text-l text-gray-800 dark:text-white">
There are no Interactive lessons scheduled at the moment.
There are no practice material available at the moment.
</p>
{:else}
<InteractiveLessons lessons={interactive} {role} />
Expand Down
199 changes: 148 additions & 51 deletions src/src/routes/(auth)/reset-password/[token]/+page.svelte
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
<script lang="ts">
import { page } from '$app/stores';
import { enhance } from '$app/forms';
import { Input, Label, Button, Spinner } from 'flowbite-svelte';
import { Button, Spinner } from 'flowbite-svelte';
import { EyeOutline, EyeSlashOutline } from 'flowbite-svelte-icons';
import { onMount } from 'svelte';

export let form;

Expand All @@ -11,6 +12,7 @@
let loading = false;
let successful = false;
let showPassword = false;
let darkMode = false;

function activateLoading() {
loading = true;
Expand All @@ -23,52 +25,112 @@
if (result.type === 'success') successful = true;
};
}

// Function to update theme
function updateTheme(isDark: boolean) {
darkMode = isDark;
if (darkMode) {
document.documentElement.classList.add('dark');
} else {
document.documentElement.classList.remove('dark');
}
}

// Initialize theme on mount and set up listener for changes
onMount(() => {
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
updateTheme(mediaQuery.matches);

// Listen for changes in color scheme preference
mediaQuery.addEventListener('change', (e) => updateTheme(e.matches));

// Cleanup listener on component destroy
return () => mediaQuery.removeEventListener('change', updateTheme);
});

// Function to generate random floating elements
const floatingElements = Array(20)
.fill()
.map(() => ({
size: Math.random() * 100 + 50,
left: Math.random() * 100,
top: Math.random() * 100,
duration: Math.random() * 10 + 5,
delay: Math.random() * 5
}));
</script>

<main
class="flex h-screen flex-col items-center justify-center bg-gradient-to-r from-green-300 to-green-600"
class="relative flex min-h-screen items-center justify-center overflow-hidden bg-gradient-to-br from-green-400 via-green-500 to-emerald-600 p-4 transition-colors duration-300 dark:from-green-800 dark:via-green-900 dark:to-emerald-950 sm:p-6 md:p-8"
>
<div class="align-center flex flex-col items-center justify-center rounded-xl bg-white">
<div class="m-4 flex items-center justify-center">
<img class="w-1/6" src="/images/class-connect-logo.png" alt="ClassConnect logo" />
<h1 class="font-roboto m-4 text-4xl font-bold">ClassConnect</h1>
</div>
<!-- Animated background elements -->
<div class="absolute inset-0 overflow-hidden">
{#each floatingElements as element}
<div
class="animate-float absolute rounded-full bg-white bg-opacity-10 dark:bg-gray-300 dark:bg-opacity-10"
style="width: {element.size}px; height: {element.size}px; left: {element.left}%; top: {element.top}%; animation-duration: {element.duration}s; animation-delay: {element.delay}s;"
/>
{/each}
</div>

{#if successful}
<div class="w-1/2 rounded-xl bg-white bg-opacity-80 p-4">
<div class="p-2 text-center">
<h1 class="text-center text-3xl font-bold">Password Reset</h1>
<!-- Main content -->
<div
class="w-full max-w-md rounded-xl border border-white border-opacity-20 bg-white bg-opacity-10 p-6 shadow-2xl backdrop-blur-lg transition-colors duration-300 dark:border-gray-700 dark:border-opacity-50 dark:bg-gray-800 dark:bg-opacity-30 sm:max-w-lg sm:p-8 md:max-w-xl md:p-10 lg:max-w-2xl lg:p-12"
>
<div class="mb-6 flex justify-center sm:mb-8">
<img
src="/images/class-connect-logo.png"
alt="ClassConnect Logo"
class="h-24 w-24 sm:h-32 sm:w-32 md:h-36 md:w-36 lg:h-40 lg:w-40"
/>
</div>

<p>Your password has been reset successfully.</p>
<h1
class="mb-4 text-center text-3xl font-bold text-white dark:text-gray-100 sm:mb-6 sm:text-4xl"
>
Reset Password
</h1>

<Button href="/signin" class="mt-4">Go back to Sign In</Button>
</div>
{#if successful}
<div class="text-center">
<p class="mb-8 text-lg text-gray-100 dark:text-gray-300 sm:mb-10 sm:text-xl">
Your password has been reset successfully.
</p>
<Button
href="/signin"
class="w-full transform rounded-lg bg-green-600 px-6 py-3 font-semibold text-white transition duration-300 ease-in-out hover:-translate-y-1 hover:bg-green-700 hover:shadow-xl dark:bg-green-700 dark:hover:bg-green-800 sm:w-2/3 md:w-1/2"
>
Go back to Sign In
</Button>
</div>
{:else}
<div class="w-1/2 rounded-xl bg-white bg-opacity-80 p-4">
<div class="p-2 text-center">
<h1 class="text-center text-3xl font-bold">Reset Password</h1>
<p>Enter your new password below.</p>
</div>

{#if form?.error}
<p class="mt-2 text-center text-red-500">{form.error}</p>
{/if}

<form method="POST" use:enhance={activateLoading}>
<input type="hidden" name="token" value={token} />

<Label for="password" class="mb-2 mt-2">New Password</Label>
<p class="mb-8 text-center text-lg text-gray-100 dark:text-gray-300 sm:mb-10 sm:text-xl">
Enter your new password below.
</p>

{#if form?.error}
<p class="mb-4 text-center text-red-500">{form.error}</p>
{/if}

<form method="POST" use:enhance={activateLoading} class="space-y-4">
<input type="hidden" name="token" value={token} />

<div>
<label
for="password"
class="mb-2 block text-sm font-medium text-gray-100 dark:text-gray-300"
>New Password</label
>
<div class="relative">
<Input
<input
type={showPassword ? 'text' : 'password'}
id="password"
name="password"
class="block w-full rounded-lg border border-gray-300 bg-gray-50 p-2.5 text-sm text-gray-900 focus:border-green-500 focus:ring-green-500 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400 dark:focus:border-green-500 dark:focus:ring-green-500"
placeholder="•••••••••"
disabled={loading}
required
/>

<button
type="button"
aria-label="Toggle password visibility"
Expand All @@ -82,29 +144,64 @@
{/if}
</button>
</div>
</div>

<Label for="confirmPassword" class="mb-2 mt-2">Confirm New Password</Label>
<div class="relative">
<Input
type="password"
id="confirmPassword"
name="confirmPassword"
placeholder="•••••••••"
disabled={loading}
required
/>
</div>
<div>
<label
for="confirmPassword"
class="mb-2 block text-sm font-medium text-gray-100 dark:text-gray-300"
>Confirm New Password</label
>
<input
type="password"
id="confirmPassword"
name="confirmPassword"
class="block w-full rounded-lg border border-gray-300 bg-gray-50 p-2.5 text-sm text-gray-900 focus:border-green-500 focus:ring-green-500 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400 dark:focus:border-green-500 dark:focus:ring-green-500"
placeholder="•••••••••"
disabled={loading}
required
/>
</div>

{#if loading}
<Button disabled class="my-4 w-full">
<Spinner class="me-3" size="4" color="white" data-testid="spinner" />
Resetting Password
</Button>
{:else}
<Button type="submit" class="my-4 w-full">Reset Password</Button>
{/if}
</form>
</div>
{#if loading}
<Button disabled class="w-full">
<Spinner class="me-3" size="4" color="white" data-testid="spinner" />
Resetting Password
</Button>
{:else}
<Button
type="submit"
class="w-full transform rounded-lg bg-green-600 px-6 py-3 font-semibold text-white transition duration-300 ease-in-out hover:-translate-y-1 hover:bg-green-700 hover:shadow-xl dark:bg-green-700 dark:hover:bg-green-800"
>Reset Password</Button
>
{/if}
</form>
{/if}
</div>
</main>

<style>
@keyframes float {
0% {
transform: translateY(0px);
}
50% {
transform: translateY(-20px);
}
100% {
transform: translateY(0px);
}
}

.animate-float {
animation: float 3s ease-in-out infinite;
}

:global(body) {
overflow: auto;
}

:global(.dark) {
color-scheme: dark;
}
</style>
Loading
Loading