Refactor avatar components across the application to use the new UserAvatar component, replacing instances of the Avatar component for improved consistency and maintainability.

This commit is contained in:
Rotimi Best
2026-03-28 23:22:55 +01:00
parent 935b1c493d
commit 2e38f7541b
18 changed files with 78 additions and 123 deletions
@@ -11,7 +11,7 @@
import { t } from '$lib/utils/functions/translations';
import EmptyState from './empty-state.svelte';
import * as Avatar from '@cio/ui/base/avatar';
import { UserAvatar } from '@cio/ui/custom/user-avatar';
import type { CourseAnalytics } from '$features/course/utils/types';
interface Props {
@@ -80,17 +80,11 @@
class="sticky left-0 z-10 min-w-[200px] bg-white px-4 py-3 transition-colors group-hover:bg-gray-50 dark:bg-neutral-800 dark:group-hover:bg-gray-700"
>
<div class="flex items-center gap-3">
<Avatar.Root class="size-8">
{#if student.profile.avatar_url}
<Avatar.Image
src={student.profile.avatar_url}
alt={student.profile.fullname ? student.profile.fullname : $t('analytics.student')}
/>
{/if}
<Avatar.Fallback>
<UserIcon class="ui:size-4 ui:text-muted-foreground" />
</Avatar.Fallback>
</Avatar.Root>
<UserAvatar
src={student.profile.avatar_url}
alt={student.profile.fullname ?? $t('analytics.student')}
class="size-8"
/>
<div class="min-w-0 flex-1">
<p class="truncate font-medium text-gray-900 dark:text-white">
{student.profile.fullname}
@@ -14,6 +14,7 @@
import * as Dialog from '@cio/ui/base/dialog';
import { TextareaField } from '@cio/ui/custom/textarea-field';
import { Button } from '@cio/ui/base/button';
import { UserAvatar } from '@cio/ui/custom/user-avatar';
// import { useCompletion } from 'ai/svelte';
// import { QUESTION_TYPE } from '$features/ui/question/constants';
@@ -330,10 +331,10 @@
</p>
{#if data.student}
<div class="flex flex-row items-center justify-center rounded-md bg-gray-100 p-[6px] dark:bg-neutral-700">
<img
alt={$t('course.navItem.submissions.grading_modal.student_avatar')}
class="flex h-5 w-5 rounded-full"
<UserAvatar
src={data.student.avatarUrl}
alt={$t('course.navItem.submissions.grading_modal.student_avatar')}
class="h-5 w-5"
/>
<p class="ml-2 line-clamp-1 text-sm font-semibold dark:text-white">
{data.student.fullname}
@@ -1,6 +1,7 @@
<script lang="ts">
import { Spinner } from '@cio/ui/base/spinner';
import { ExerciseQuestion } from '@cio/ui';
import { UserAvatar } from '@cio/ui/custom/user-avatar';
import { submissions } from './store';
import { questionnaire } from '../store';
@@ -17,9 +18,6 @@
let { isLoading = $bindable(false) }: Props = $props();
const defaultImg =
'https://images.unsplash.com/photo-1599566150163-29194dcaad36?auto=format&fit=crop&q=60&w=800&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8NHx8YXZhdGFyc3xlbnwwfHwwfHx8Mg%3D%3D';
let studentSelected = $state(0);
const questionLabels = $derived(getExerciseQuestionLabels());
@@ -57,10 +55,10 @@
studentSelected == i ? 'border-primary-700 border-[3px]' : ''
}`}
>
<img
src={student.groupmember?.profile.avatarUrl ? student.groupmember.profile.avatarUrl : defaultImg}
<UserAvatar
src={student.groupmember?.profile.avatarUrl}
alt={$t('course.navItem.lessons.exercises.all_exercises.analytics.individual.student_avatar')}
class="m-1 max-h-10 w-10 rounded-full bg-white"
class="m-1 size-10"
/>
</div>
<p class="line-clamp-2 w-20 leading-4 whitespace-pre-wrap">
@@ -2,9 +2,7 @@
import { untrack } from 'svelte';
import * as DropdownMenu from '@cio/ui/base/dropdown-menu';
import EllipsisVerticalIcon from '@lucide/svelte/icons/ellipsis-vertical';
import UserIcon from '@lucide/svelte/icons/user';
import * as Avatar from '@cio/ui/base/avatar';
import { UserAvatar } from '@cio/ui/custom/user-avatar';
import { TextareaField } from '@cio/ui/custom/textarea-field';
import { DeleteModal } from '$features/ui';
import { Button } from '@cio/ui/base/button';
@@ -98,14 +96,7 @@
</div>
<div>
<div class="flex h-full items-start gap-3">
<Avatar.Root class="mt-2 size-6!">
{#if $profile.avatarUrl}
<Avatar.Image src={$profile.avatarUrl} alt={$profile.fullname ? $profile.fullname : 'User'} />
{/if}
<Avatar.Fallback>
<UserIcon class="ui:size-3 ui:text-muted-foreground" />
</Avatar.Fallback>
</Avatar.Root>
<UserAvatar src={$profile.avatarUrl} alt={$profile.fullname ?? 'User'} class="mt-2 size-6!" />
<div class="h-full w-full">
<TextareaField
label={$t('course.navItem.lessons.comments.text_area_title')}
@@ -125,14 +116,7 @@
<div class="my-5">
{#each comments as commentItem (commentItem.id)}
<div class="mt-2 flex items-start gap-3 pb-2">
<Avatar.Root class="mt-2 size-6!">
{#if commentItem.avatar}
<Avatar.Image src={commentItem.avatar} alt={commentItem.name ? commentItem.name : 'User'} />
{/if}
<Avatar.Fallback>
<UserIcon class="ui:size-3 ui:text-muted-foreground" />
</Avatar.Fallback>
</Avatar.Root>
<UserAvatar src={commentItem.avatar} alt={commentItem.name ?? 'User'} class="mt-2 size-6!" />
<div class="w-full rounded-md border px-3 py-2 dark:border-neutral-700">
<div class="flex items-center justify-between gap-2">
@@ -12,6 +12,7 @@
import { newsfeedApi } from '$features/course/api';
import { Button } from '@cio/ui/base/button';
import { UserAvatar } from '@cio/ui/custom/user-avatar';
import type { Feed } from '$features/course/utils/types';
import type { NewsfeedCommentsByFeedId } from '$features/course/api';
@@ -193,13 +194,7 @@
<!-- Input for new comment -->
<div class="flex items-center justify-between gap-2">
<div class="h-7 w-7">
<img
src={author.avatarUrl}
alt={$t('course.navItem.news_feed.user_avatar_alt')}
class="h-full w-full rounded-full object-cover"
/>
</div>
<UserAvatar src={author.avatarUrl} alt={$t('course.navItem.news_feed.user_avatar_alt')} class="size-7" />
<div class="flex-1">
<input
type="text"