Skip to content

Commit 480d981

Browse files
feat: implement user profile page with info - [CU-869awe6gv] (#30)
Co-authored-by: Habiba Ayman <137416623+habibayman@users.noreply.github.com> Co-authored-by: habibayman <habibaayman2004@outlook.com>
1 parent 227aef4 commit 480d981

44 files changed

Lines changed: 1088 additions & 251 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

app/components/SideBar/Left/Tab.vue

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
<script lang="ts" setup>
2-
import type { LeftSidebarTab } from '~~/shared/types/leftsidebar';
3-
42
const props = defineProps<{
53
tab: LeftSidebarTab;
64
}>();
Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,32 @@
11
<script lang="ts" setup>
2-
import { SIDEBAR_TABS } from '~/constants/leftsidebar';
2+
const userStore = useUserStore();
33
</script>
44
<template>
55
<div class="flex h-screen flex-col items-center xl:items-start">
6-
<div class="hover:bg-accent my-2 w-min p-2 hover:rounded-full">
6+
<div class="hover:bg-foreground/10 my-2 w-min p-2 hover:rounded-full">
77
<NuxtLink to="/">
88
<LogoRaven class="h-8 w-8" />
99
</NuxtLink>
1010
</div>
1111
<div class="mt-2 flex flex-col items-center space-y-3 xl:items-start">
12-
<SideBarLeftTab v-for="tab in SIDEBAR_TABS" :key="tab.label" :tab />
12+
<SideBarLeftTab :tab="{ label: 'home', icon: 'home', route: '/home' }"></SideBarLeftTab>
13+
<SideBarLeftTab
14+
:tab="{ label: 'explore', icon: 'search', route: '/explore' }"
15+
></SideBarLeftTab>
16+
<SideBarLeftTab
17+
:tab="{ label: 'notifications', icon: 'notifications', route: '/notifications' }"
18+
></SideBarLeftTab>
19+
<SideBarLeftTab
20+
:tab="{ label: 'messages', icon: 'chat', route: '/messages' }"
21+
></SideBarLeftTab>
22+
<SideBarLeftTab
23+
:tab="{
24+
label: 'profile',
25+
icon: 'person',
26+
route: `/profile/${userStore.user?.username || ''}`,
27+
}"
28+
></SideBarLeftTab>
29+
<SideBarLeftTab :tab="{ label: 'more', icon: 'more-horiz', route: '#' }"></SideBarLeftTab>
1330
</div>
1431
</div>
1532
</template>

app/components/SideBar/Right/PreviewCard/index.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ const props = defineProps({
1313
</h1>
1414
<slot />
1515

16-
<div class="text-primary hover:bg-background cursor-pointer p-3 text-sm">
16+
<div class="text-primary hover:bg-foreground/10 cursor-pointer p-3 text-sm">
1717
{{ $t('rightsidebar.show-more') }}
1818
</div>
1919
</div>
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<script lang="ts" setup></script>
22

33
<template>
4-
<div class="border-border hover:bg-background cursor-pointer border-b p-3">
4+
<div class="border-border hover:bg-foreground/10 cursor-pointer border-b p-3">
55
<slot />
66
</div>
77
</template>

app/components/SideBar/Right/index.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ const whoToFollowItems = [
4444
</script>
4545

4646
<template>
47-
<div>
47+
<div class="ms-4">
4848
<!-- preview Card -->
4949
<SideBarRightPreviewCard :title="$t('rightsidebar.whats-happening.title')">
5050
<div>
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<script lang="ts" setup>
2+
defineProps<{
3+
profileImg: string;
4+
}>();
5+
</script>
6+
<template>
7+
<div class="mx-4 flex flex-wrap items-center justify-between gap-4">
8+
<div>
9+
<NuxtImg
10+
:src="profileImg"
11+
alt="Profile picture"
12+
class="-mt-16 size-34 rounded-full border-4 object-contain"
13+
loading="eager"
14+
/>
15+
</div>
16+
<UiButton variant="outline">{{ $t('profile-info.edit-profile') }}</UiButton>
17+
</div>
18+
</template>
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<script lang="ts" setup>
2+
defineProps<{
3+
coverImg: string;
4+
}>();
5+
</script>
6+
<template>
7+
<NuxtImg :src="coverImg" alt="Profile Cover" class="h-48 w-full object-cover" loading="eager" />
8+
</template>
Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
1-
<script setup lang="ts"></script>
1+
<script lang="ts" setup>
2+
import type { UserProfile } from '~~/shared/types/user';
23
4+
defineProps<{
5+
userProfile: UserProfile;
6+
}>();
7+
</script>
38
<template>
4-
<div class="bg-accent/10 h-[460px]"></div>
9+
<div>
10+
<ProfileCover :cover-img="userProfile.bannerUrl" />
11+
<ProfileAvatarSection :profile-img="userProfile.avatarUrl" />
12+
<ProfileInfo :user-profile="userProfile" />
13+
</div>
514
</template>
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<script lang="ts" setup>
2+
import type { UserProfile } from '~~/shared/types/user';
3+
import { formatMonthYear } from '~/utils/date';
4+
const props = defineProps<{
5+
userProfile: UserProfile;
6+
}>();
7+
const displayUrl = computed(() => {
8+
if (props.userProfile.websiteUrl) {
9+
const cleanedUrl = cleanUrl(props.userProfile.websiteUrl);
10+
return cleanedUrl.length > 30 ? cleanedUrl.slice(0, 29) + '...' : cleanedUrl;
11+
}
12+
return '';
13+
});
14+
</script>
15+
<template>
16+
<div class="mt-2 flex flex-col">
17+
<div class="px-4">
18+
<h2 class="text-foreground text-2xl font-bold">{{ userProfile.displayName }}</h2>
19+
<p class="text-muted-foreground">{{ userProfile.username }}</p>
20+
<p class="mt-2">{{ userProfile.bio }}</p>
21+
<div class="mt-2 flex flex-wrap">
22+
<a
23+
:href="userProfile.websiteUrl"
24+
target="_blank"
25+
rel="noopener noreferrer"
26+
class="text-brand-blue me-2 flex items-center gap-1 hover:underline"
27+
>
28+
<Icon class="text-muted-foreground" name="ic:sharp-link" size="18" />
29+
{{ displayUrl }}
30+
</a>
31+
32+
<p class="text-muted-foreground flex items-center gap-1">
33+
<Icon name="ic:sharp-calendar-month" />
34+
{{ $t('profile-info.joined') }}
35+
{{ formatMonthYear(userProfile.joinedAt) }}
36+
</p>
37+
</div>
38+
<div class="mt-4 flex space-x-4">
39+
<span
40+
><strong>{{ userProfile.followingCount }}</strong>
41+
<span class="text-muted-foreground ms-1"> {{ $t('profile-info.following') }} </span>
42+
</span>
43+
<span
44+
><strong>{{ userProfile.followersCount }}</strong>
45+
<span class="text-muted-foreground ms-1"> {{ $t('profile-info.followers') }} </span>
46+
</span>
47+
</div>
48+
</div>
49+
</div>
50+
</template>

app/composables/useUserProfile.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import type { UserProfile } from '~~/shared/types/user';
2+
3+
export const useUserProfile = (username: string) => {
4+
const {
5+
data: userProfile,
6+
error,
7+
pending,
8+
} = useFetch<UserProfile>(`/api/users/${username}/profile`, {
9+
key: `user-profile-${username}`,
10+
});
11+
return {
12+
userProfile,
13+
error,
14+
loading: pending,
15+
};
16+
};

0 commit comments

Comments
 (0)