feat: migrate course UI from supabase (#615)
* chore: fix storybook build * chore: implement theming * community list not showing properly * refactor: migrate Chip component to @cio/ui/custom/chip - Updated imports for the Chip component across multiple files to use the new location in the @cio/ui package. - Replaced the previous Chip implementation with a new version that utilizes a Badge for better styling and functionality. - Adjusted related components to ensure consistent usage of the updated Chip component. * fix: align course sidebar design to match organization design (#611) * fix: align course sidebar design to match organization design * feat: add hideSearch prop to AppHeader and update sidebar layout --------- Co-authored-by: Rotimi Best <best@classroomio.com> * refactor: replace PrimaryButton with Button component across multiple files - Updated various components to use the new Button component from @cio/ui/base/button instead of the deprecated PrimaryButton. - Ensured consistent button usage throughout the application for improved styling and functionality. - Removed the PrimaryButton component and its associated constants from the codebase. * refactor: standardize button and input usage across components - Replaced instances of deprecated components such as TextField, TextArea, and IconButton with their updated counterparts (InputField, TextareaField, and updated IconButton). - Ensured consistent event handling by changing `onClick` to `onclick` for better alignment with Svelte's event binding conventions. - Removed unused components and streamlined imports to enhance code maintainability and readability. * chore: themeing, course card, use $features * refactor: centralize UI components and update modal implementations - Refactored various components to centralize UI elements, improving discoverability and maintainability. - Replaced deprecated modal components with the new Dialog component for consistent modal behavior across the application. - Removed unused components and streamlined imports to enhance code clarity and reduce complexity. * feat: add new documentation and refactor org and lms UI components - Introduced new documentation files for Auth Flow, Breadcrumbs Refactoring, Page and Route Creation, and Route Page Setup to enhance developer onboarding and reference. - Refactored various UI components, including the Course Sidebar and Lesson Modal, to improve layout and functionality. - Removed deprecated components and streamlined imports for better maintainability. - Updated translations across multiple languages to ensure consistency in user experience. * refactor: update UI components and improve styling consistency - Refactored various components to enhance styling consistency, including updates to button and text color classes. - Removed deprecated components and streamlined imports for better maintainability. - Replaced instances of PageBody with a more flexible Page component structure across multiple routes. - Updated translations and UI elements to align with the new design system. - Deleted unused components to reduce code complexity and improve clarity. * update output.css --------- Co-authored-by: Abdulmuizz Hamzat <103611443+canhamzacode@users.noreply.github.com>
This commit is contained in:
co-authored by
Abdulmuizz Hamzat
parent
e6a3cf9332
commit
9fb6c7cbaa
@@ -0,0 +1,197 @@
|
||||
<script lang="ts">
|
||||
import { preventDefault } from '$lib/utils/functions/svelte';
|
||||
|
||||
import { onMount } from 'svelte';
|
||||
import * as UnderlineTabs from '@cio/ui/custom/underline-tabs';
|
||||
import { snackbar } from '$features/ui/snackbar/store';
|
||||
import * as Dialog from '@cio/ui/base/dialog';
|
||||
import { handleOpenWidget } from '$features/ui/course-landing-page/store';
|
||||
import { queryUnsplash } from './utils';
|
||||
|
||||
import { t } from '$lib/utils/functions/translations';
|
||||
import { uploadImage } from '$lib/utils/services/upload';
|
||||
import { Button } from '@cio/ui/base/button';
|
||||
|
||||
interface Props {
|
||||
imageURL?: string;
|
||||
onchange?: (_v: string) => void;
|
||||
}
|
||||
|
||||
let { imageURL = $bindable(''), onchange }: Props = $props();
|
||||
|
||||
const tabs = [
|
||||
{ label: 'Unsplash', value: 'unsplash' },
|
||||
{ label: 'Upload', value: 'upload' }
|
||||
];
|
||||
|
||||
let isUploading = $state(false);
|
||||
let currentTab = $state(tabs[0].value);
|
||||
let searchQuery = $state('');
|
||||
let unsplashImages: {
|
||||
id: string | number;
|
||||
user: {
|
||||
name: string;
|
||||
username: string;
|
||||
};
|
||||
urls: {
|
||||
regular: string;
|
||||
};
|
||||
alt_description: string;
|
||||
}[] = $state([]);
|
||||
let fileInput: HTMLInputElement | undefined = $state();
|
||||
|
||||
let label = $state($t('snackbar.landing_page_settings.error.label'));
|
||||
|
||||
async function handleImageClick(img: string) {
|
||||
$handleOpenWidget.open = false;
|
||||
|
||||
onchange?.(img);
|
||||
imageURL = img;
|
||||
}
|
||||
|
||||
const onFileSelected = () => {
|
||||
const file = fileInput?.files?.[0];
|
||||
const sizeInkb = file?.size! / 1024;
|
||||
if (sizeInkb > 500) {
|
||||
snackbar.error('snackbar.landing_page_settings.error.file_size');
|
||||
label = $t('snackbar.landing_page_settings.error.try_again');
|
||||
return;
|
||||
}
|
||||
if (file) {
|
||||
let reader = new FileReader();
|
||||
reader.readAsDataURL(file);
|
||||
reader.onload = () => {
|
||||
handleUploadImage(file);
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const handleUploadImage = async (image: File) => {
|
||||
isUploading = true;
|
||||
if (!image) {
|
||||
return;
|
||||
}
|
||||
|
||||
imageURL = await uploadImage(image);
|
||||
|
||||
onchange?.(imageURL);
|
||||
isUploading = false;
|
||||
|
||||
snackbar.success(`snackbar.landing_page_settings.success.complete`);
|
||||
$handleOpenWidget.open = false;
|
||||
};
|
||||
|
||||
function handleUpload() {
|
||||
fileInput?.click();
|
||||
}
|
||||
|
||||
async function handleSubmit() {
|
||||
try {
|
||||
unsplashImages = await queryUnsplash(searchQuery || 'rocks');
|
||||
} catch (error) {
|
||||
snackbar.error('snackbar.landing_page_settings.error.fetch_error');
|
||||
console.error('Error fetching images from Unsplash:', error);
|
||||
}
|
||||
}
|
||||
|
||||
onMount(handleSubmit);
|
||||
</script>
|
||||
|
||||
<Dialog.Root
|
||||
bind:open={$handleOpenWidget.open}
|
||||
onOpenChange={(isOpen) => {
|
||||
if (!isOpen) $handleOpenWidget.open = false;
|
||||
}}
|
||||
>
|
||||
<Dialog.Content class="w-3/5">
|
||||
<Dialog.Header>
|
||||
<Dialog.Title>{$t('course.navItem.landing_page.upload_widget.title')}</Dialog.Title>
|
||||
</Dialog.Header>
|
||||
<div class="w-full bg-white p-5 dark:bg-inherit">
|
||||
<UnderlineTabs.Root bind:value={currentTab}>
|
||||
<UnderlineTabs.List>
|
||||
{#each tabs as tab}
|
||||
<UnderlineTabs.Trigger value={tab.value}>
|
||||
{tab.label}
|
||||
</UnderlineTabs.Trigger>
|
||||
{/each}
|
||||
</UnderlineTabs.List>
|
||||
<UnderlineTabs.Content value={tabs[1].value}>
|
||||
<!-- Your Upload content here -->
|
||||
<div class="w-full">
|
||||
<input
|
||||
type="file"
|
||||
style="display: none;"
|
||||
bind:this={fileInput}
|
||||
onchange={onFileSelected}
|
||||
disabled={isUploading}
|
||||
/>
|
||||
<Button onclick={handleUpload} loading={isUploading}>
|
||||
{label}
|
||||
</Button>
|
||||
<p class="my-2 text-center text-sm text-gray-500">
|
||||
{$t('course.navItem.landing_page.upload_widget.width')}
|
||||
</p>
|
||||
<p class="text-center text-sm text-gray-500">
|
||||
{$t('course.navItem.landing_page.upload_widget.size')}
|
||||
</p>
|
||||
</div>
|
||||
</UnderlineTabs.Content>
|
||||
<UnderlineTabs.Content value={tabs[0].value}>
|
||||
<!-- Your Images content here -->
|
||||
<div class="h-full overflow-y-scroll">
|
||||
<form onsubmit={preventDefault(handleSubmit)} class="mt-1 flex gap-2 pb-3">
|
||||
<input type="text" bind:value={searchQuery} name="" id="" class="ml-2 w-[85%] rounded-lg dark:text-black" />
|
||||
<button type="submit" class="rounded-lg border border-gray-500 bg-white px-3 py-1 text-black"
|
||||
>{$t('course.navItem.landing_page.upload_widget.submit')}</button
|
||||
>
|
||||
</form>
|
||||
{#if unsplashImages && unsplashImages.length > 0}
|
||||
<div class="hide-scrollbar flex max-h-[300px] flex-row flex-wrap items-center gap-2 px-[10px]">
|
||||
{#each unsplashImages as unsplashImages (unsplashImages.id)}
|
||||
<div>
|
||||
<div class="relative h-[130px] w-[195px] overflow-hidden">
|
||||
<button onclick={() => handleImageClick(unsplashImages.urls.regular)}>
|
||||
<img
|
||||
src={unsplashImages.urls.regular}
|
||||
alt={unsplashImages.alt_description}
|
||||
class="h-full w-full cursor-pointer rounded-md object-cover hover:opacity-80"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
{#if unsplashImages.user.name}
|
||||
<p class="mt-1 text-center text-xs font-light text-gray-500">
|
||||
By <a
|
||||
href={`https://unsplash.com/@${unsplashImages.user.username}`}
|
||||
target="_blank"
|
||||
class="hover:text-red-700">{unsplashImages.user.name}</a
|
||||
>
|
||||
</p>
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{:else}
|
||||
<p class="pt-7 text-center">
|
||||
{$t('course.navItem.landing_page.upload_widget.no_images')}
|
||||
</p>
|
||||
{/if}
|
||||
</div>
|
||||
</UnderlineTabs.Content>
|
||||
</UnderlineTabs.Root>
|
||||
</div>
|
||||
</Dialog.Content>
|
||||
</Dialog.Root>
|
||||
|
||||
<style>
|
||||
.hide-scrollbar {
|
||||
width: 100%;
|
||||
overflow: auto;
|
||||
scrollbar-width: none;
|
||||
-ms-overflow-style: none;
|
||||
}
|
||||
.hide-scrollbar::-webkit-scrollbar {
|
||||
width: 0;
|
||||
background: transparent;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user