mirror of
https://github.com/kamranahmedse/developer-roadmap.git
synced 2025-09-02 13:52:46 +02:00
Friends listing page
This commit is contained in:
@@ -1,6 +1,5 @@
|
|||||||
import { useEffect, useState } from 'preact/hooks';
|
import { useEffect, useState } from 'preact/hooks';
|
||||||
import { httpDelete, httpGet, httpPatch, httpPost } from '../lib/http';
|
import { httpDelete, httpGet, httpPatch, httpPost } from '../lib/http';
|
||||||
import ErrorIcon from '../icons/error.svg';
|
|
||||||
import { pageProgressMessage } from '../stores/page';
|
import { pageProgressMessage } from '../stores/page';
|
||||||
import { isLoggedIn } from '../lib/jwt';
|
import { isLoggedIn } from '../lib/jwt';
|
||||||
import { showLoginPopup } from '../lib/popup';
|
import { showLoginPopup } from '../lib/popup';
|
||||||
@@ -10,7 +9,8 @@ import { DeleteUserIcon } from './ReactIcons/DeleteUserIcon';
|
|||||||
import { useToast } from '../hooks/use-toast';
|
import { useToast } from '../hooks/use-toast';
|
||||||
import { useAuth } from '../hooks/use-auth';
|
import { useAuth } from '../hooks/use-auth';
|
||||||
import { AddedUserIcon } from './ReactIcons/AddedUserIcon';
|
import { AddedUserIcon } from './ReactIcons/AddedUserIcon';
|
||||||
import {StopIcon} from "./ReactIcons/StopIcon";
|
import { StopIcon } from './ReactIcons/StopIcon';
|
||||||
|
import { ErrorIcon } from './ReactIcons/ErrorIcon';
|
||||||
|
|
||||||
export type FriendshipStatus =
|
export type FriendshipStatus =
|
||||||
| 'none'
|
| 'none'
|
||||||
@@ -106,11 +106,7 @@ export function Befriend() {
|
|||||||
if (!user) {
|
if (!user) {
|
||||||
return (
|
return (
|
||||||
<div className="container text-center">
|
<div className="container text-center">
|
||||||
<img
|
<ErrorIcon additionalClasses="mx-auto mb-4 mt-24 w-20 opacity-20" />
|
||||||
alt={'error'}
|
|
||||||
src={ErrorIcon}
|
|
||||||
className="mx-auto mb-4 mt-24 w-20 opacity-20"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<h2 className={'mb-1 text-2xl font-bold'}>Error</h2>
|
<h2 className={'mb-1 text-2xl font-bold'}>Error</h2>
|
||||||
<p class="mb-4 text-base leading-6 text-gray-600">
|
<p class="mb-4 text-base leading-6 text-gray-600">
|
||||||
|
@@ -1,12 +1,53 @@
|
|||||||
import { useEffect } from 'preact/hooks';
|
import { useEffect, useState } from 'preact/hooks';
|
||||||
import UserPlus from '../../icons/user-plus.svg';
|
|
||||||
import { pageProgressMessage } from '../../stores/page';
|
import { pageProgressMessage } from '../../stores/page';
|
||||||
import { useAuth } from '../../hooks/use-auth';
|
import { useAuth } from '../../hooks/use-auth';
|
||||||
|
import { AddUserIcon } from '../ReactIcons/AddUserIcon';
|
||||||
|
import { httpGet } from '../../lib/http';
|
||||||
|
import type { FriendshipStatus } from '../Befriend';
|
||||||
|
import { useToast } from '../../hooks/use-toast';
|
||||||
import { EmptyFriends } from './EmptyFriends';
|
import { EmptyFriends } from './EmptyFriends';
|
||||||
|
|
||||||
|
type FriendResourceProgress = {
|
||||||
|
updatedAt: string;
|
||||||
|
title: string;
|
||||||
|
resourceId: string;
|
||||||
|
resourceType: string;
|
||||||
|
learning: number;
|
||||||
|
skipped: number;
|
||||||
|
done: number;
|
||||||
|
total: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
type ListFriendsResponse = {
|
||||||
|
userId: string;
|
||||||
|
name: string;
|
||||||
|
avatar: string;
|
||||||
|
status: FriendshipStatus;
|
||||||
|
roadmaps: FriendResourceProgress[];
|
||||||
|
bestPractices: FriendResourceProgress[];
|
||||||
|
}[];
|
||||||
|
|
||||||
export function FriendsPage() {
|
export function FriendsPage() {
|
||||||
|
const toast = useToast();
|
||||||
|
const [friends, setFriends] = useState<ListFriendsResponse>([]);
|
||||||
|
|
||||||
|
async function loadFriends() {
|
||||||
|
const { response, error } = await httpGet<ListFriendsResponse>(
|
||||||
|
`${import.meta.env.PUBLIC_API_URL}/v1-list-friends`
|
||||||
|
);
|
||||||
|
|
||||||
|
if (error || !response) {
|
||||||
|
toast.error(error?.message || 'Something went wrong');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setFriends(response);
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
loadFriends().finally(() => {
|
||||||
pageProgressMessage.set('');
|
pageProgressMessage.set('');
|
||||||
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const user = useAuth();
|
const user = useAuth();
|
||||||
@@ -15,16 +56,18 @@ export function FriendsPage() {
|
|||||||
: 'https://roadmap.sh';
|
: 'https://roadmap.sh';
|
||||||
const befriendUrl = `${baseUrl}/befriend?u=${user?.id}`;
|
const befriendUrl = `${baseUrl}/befriend?u=${user?.id}`;
|
||||||
|
|
||||||
|
if (friends.length === 0) {
|
||||||
return <EmptyFriends befriendUrl={befriendUrl} />;
|
return <EmptyFriends befriendUrl={befriendUrl} />;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className="mb-4 flex items-center justify-between">
|
<div className="mb-4 flex items-center justify-between">
|
||||||
<span className={'text-sm text-gray-400'}>
|
<span className={'text-sm text-gray-400'}>
|
||||||
You have 4 friends on Roadmap.sh
|
You have 4 active friends
|
||||||
</span>
|
</span>
|
||||||
<button class="flex items-center justify-center gap-2 rounded-md border border-gray-400 bg-gray-50 p-1 px-2 text-sm hover:border-gray-500 hover:bg-gray-100">
|
<button class="flex items-center justify-center gap-1.5 rounded-md border border-gray-400 bg-gray-50 p-1 px-2 text-sm hover:border-gray-500 hover:bg-gray-100">
|
||||||
<img src={UserPlus} className="h-4 w-4" alt="Invite Friends" />
|
<AddUserIcon additionalClasses="w-4 h-4" />
|
||||||
Invite Friends
|
Invite Friends
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
27
src/components/ReactIcons/AddUserIcon.tsx
Normal file
27
src/components/ReactIcons/AddUserIcon.tsx
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
type CheckIconProps = {
|
||||||
|
additionalClasses?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function AddUserIcon(props: CheckIconProps) {
|
||||||
|
const { additionalClasses = 'mr-2 w-[20px] h-[20px]' } = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
className={`relative ${additionalClasses}`}
|
||||||
|
>
|
||||||
|
<path d="M14 19a6 6 0 0 0-12 0" />
|
||||||
|
<circle cx="8" cy="9" r="4" />
|
||||||
|
<line x1="19" x2="19" y1="8" y2="14" />
|
||||||
|
<line x1="22" x2="16" y1="11" y2="11" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
Reference in New Issue
Block a user