feat: redo settings and all org pages. (#601)

* feat: new components, deploy storybook

* refactor: simplify component field definitions and enhance button loading state

- Removed unnecessary type imports and simplified the FIELDS array definitions across multiple components in Storybook.
- Updated button component to use a loading state instead of isLoading, improving clarity and consistency in props.
- Adjusted button stories to reflect the new loading prop and enhanced the button's loading visual feedback.
- Minor CSS adjustments for better layout and styling consistency.

* chore: add field, card, password components

* feat: improve page component & update AuthUI path

* chore: update bits-ui to version 2.14.4 and all org related UI's

- Updated bits-ui dependency in pnpm-lock.yaml and package.json to version 2.14.4.
- Refactored imports for RoleBasedSecurity and UpgradeBanner components to use the new ui feature directory.
- Removed unused Course components and related files to clean up the codebase.
- Enhanced various components to improve consistency and maintainability.

* chore: move all setting buttons to the top and update auth ui styles

* chore: remove svelte-loading-spinners and replace with @cio/ui/base/spinner

- Removed all instances of svelte-loading-spinners from the project.
- Updated components to use the new Spinner from @cio/ui/base/spinner.
- Cleaned up pnpm-lock.yaml and package.json to reflect the removal of svelte-loading-spinners.
- Added new CSS animations for rocket launch effects in app.css.
- Enhanced email change flow with updated verification messages in translations.

* chore: remove supabase file upload from dashboard and move to r2

* chore: update organization API and UI components

- Added new API methods for organization plan operations and team management.
- Introduced middleware for API key authentication and organization admin checks.
- Refactored organization-related components to utilize the new API structure.
- Updated various UI components to improve consistency and maintainability.
- Enhanced error handling and validation for organization-related actions.
- Removed deprecated services and streamlined the codebase.
This commit is contained in:
Rotimi Best
2025-12-02 09:54:38 +01:00
committed by GitHub
parent 531516a8f7
commit c7854efae3
350 changed files with 13928 additions and 3895 deletions
@@ -0,0 +1,176 @@
import { BaseApiWithErrors, classroomio } from '$lib/utils/services/api';
export interface SetupItem {
id: string;
title: string;
desc: string;
is_completed: boolean;
button_label: string;
}
export interface SetupProgressData {
setup: SetupItem[];
courses: Array<{ id: string; [key: string]: unknown }>;
lessons: Array<{ id: string; [key: string]: unknown }>;
orgSiteName: string;
}
type TCreateSetupData = {
orgHasAvatarUrl?: boolean;
isCourseCreated?: boolean;
isLessonCreated?: boolean;
isExerciseCreated?: boolean;
isCoursePublished?: boolean;
};
// Base setup data structure - created once
const createSetupData = ({
orgHasAvatarUrl = false,
isCourseCreated = false,
isLessonCreated = false,
isExerciseCreated = false,
isCoursePublished = false
}: TCreateSetupData = {}): SetupItem[] => [
{
id: 'profile',
title: 'setup.personal_profile.title',
desc: 'setup.personal_profile.desc',
is_completed: false,
button_label: 'setup.personal_profile.button_label'
},
{
id: 'organization',
title: 'setup.organization_profile.title',
desc: 'setup.organization_profile.desc',
is_completed: orgHasAvatarUrl,
button_label: 'setup.organization_profile.button_label'
},
{
id: 'course',
title: 'setup.course.title',
desc: 'setup.course.desc',
is_completed: isCourseCreated,
button_label: 'setup.course.button_label'
},
{
id: 'lesson',
title: 'setup.lesson.title',
desc: 'setup.lesson.desc',
is_completed: isLessonCreated,
button_label: 'setup.lesson.button_label'
},
{
id: 'exercise',
title: 'setup.exercise.title',
desc: 'setup.exercise.desc',
is_completed: isExerciseCreated,
button_label: 'setup.exercise.button_label'
},
{
id: 'publish',
title: 'setup.publish_course.title',
desc: 'setup.publish_course.desc',
is_completed: isCoursePublished,
button_label: 'setup.publish_course.button_label'
}
];
/**
* API class for setup progress operations
*/
class SetupProgressApi extends BaseApiWithErrors {
progress = $state<SetupProgressData>({
orgSiteName: '',
setup: [],
courses: [],
lessons: []
});
constructor() {
super();
this.isLoading = true;
}
/**
* Computed setup list with profile completion check
* This computation is shared across components to avoid duplication
* Note: Profile completion check should be done in components using $profile
* This returns the base setup list, and components can derive the final list
*/
get setupList(): SetupItem[] {
return this.progress.setup || [];
}
/**
* Fetches setup progress data for an organization
* @param siteName Organization site name
*/
async fetchSetupProgress(siteName: string) {
await this.execute<typeof classroomio.organization.setup.$get>({
requestFn: () =>
classroomio.organization.setup.$get({
query: { siteName }
}),
logContext: 'fetching setup progress',
onSuccess: (response) => {
if (!response.data) {
// Set default empty state if no data
const setup = createSetupData();
this.progress = {
orgSiteName: siteName,
setup,
courses: [],
lessons: []
};
return;
}
const {
isCourseCreated,
isCoursePublished,
orgHasAvatarUrl,
courseData,
lessonData,
isLessonCreated,
isExerciseCreated
} = response.data;
const setup = createSetupData({
orgHasAvatarUrl,
isCourseCreated,
isLessonCreated,
isExerciseCreated,
isCoursePublished
});
this.progress = {
orgSiteName: siteName,
setup,
courses: courseData || [],
lessons: lessonData || []
};
},
onError: (error) => {
console.error('Error fetching setup progress:', error);
// Set default empty state on error
const setup = createSetupData();
this.progress = {
orgSiteName: siteName,
setup,
courses: [],
lessons: []
};
}
});
}
}
export const setupProgressApi = new SetupProgressApi();
/**
* Calculates progress percentage from setup items
*/
export function calculateProgress(setupItems: SetupItem[]): number {
if (!setupItems || setupItems.length === 0) return 0;
const completed = setupItems.filter((item) => item.is_completed).length;
return Math.round((completed / setupItems.length) * 100);
}
@@ -0,0 +1,53 @@
<script lang="ts">
import { calculateProgress, setupProgressApi } from '../api/setup-progress.svelte';
import type { SetupItem } from '../api/setup-progress.svelte';
import { Skeleton } from '@cio/ui/base/skeleton';
let { setupItems, size = 'default' } = $props<{
setupItems?: SetupItem[];
size?: 'small' | 'default';
}>();
const effectiveSetupItems = $derived(setupItems ?? setupProgressApi.progress.setup ?? []);
const progressPercentage = $derived(calculateProgress(effectiveSetupItems));
const isLoading = $derived(setupProgressApi.isLoading);
const sizeClass = $derived(size === 'small' ? 'size-10' : 'size-14');
</script>
<div class="relative shrink-0">
{#if isLoading}
<!-- Loading skeleton - round circle -->
<Skeleton class="{sizeClass} rounded-full" />
{:else}
<svg class="{sizeClass} -rotate-90" viewBox="0 0 100 100">
<!-- Background circle -->
<circle
cx="50"
cy="50"
r="45"
fill="none"
stroke="currentColor"
stroke-width="8"
class="text-gray-200 dark:text-gray-700"
/>
<!-- Progress circle -->
<circle
cx="50"
cy="50"
r="45"
fill="none"
stroke="currentColor"
stroke-width="8"
stroke-linecap="round"
class="text-green-600"
stroke-dasharray={2 * Math.PI * 45}
stroke-dashoffset={2 * Math.PI * 45 * (1 - progressPercentage / 100)}
style="transition: stroke-dashoffset 0.5s ease-in-out;"
/>
</svg>
<!-- Center text -->
<div class="absolute inset-0 flex flex-col items-center justify-center">
<p class="{size === 'small' ? 'text-[10px]' : 'text-sm'} font-bold">{progressPercentage}%</p>
</div>
{/if}
</div>