Skip to content

Commit 183722d

Browse files
committed
✨ Enhance user profile and language components with loading states and additional data fields
1 parent c37a69c commit 183722d

File tree

5 files changed

+207
-170
lines changed

5 files changed

+207
-170
lines changed
Lines changed: 58 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,71 @@
1-
import { Card, CardHeader, CardBody, CardFooter, Link } from '@nextui-org/react'
1+
import { Card, CardHeader, CardBody, CardFooter, Link, Skeleton } from '@nextui-org/react'
22
import { FaEnvelope, FaMapMarkerAlt, FaGithub } from 'react-icons/fa'
33
import { BsBuildings, BsTwitterX } from 'react-icons/bs'
44
import { GitHubUserContextProps } from '@/types'
55

66
export default function ContactCard({ userData, isLoading, error }: GitHubUserContextProps) {
7-
if (isLoading) return <p>Loading...</p>
87
if (error) return <p>Error: {error}</p>
98

109
return (
1110
<Card isHoverable className="flex items-center content-center justify-center w-full mb-4">
12-
<CardHeader>
13-
<h4 className="font-open-sans text-small text-default-600">Contact to me.</h4>
14-
</CardHeader>
15-
<CardBody className="px-3 py-0 text-small text-default-400">
16-
<div className="flex flex-col">
17-
<div className="flex items-center justify-between">
18-
<Link className="flex items-center space-x-2 text-default-600">
19-
<FaMapMarkerAlt /> <span>Location</span>
20-
</Link>
21-
<span className="text-default-400">{userData?.location}</span>
11+
<Skeleton isLoaded={!isLoading} className="w-full h-full">
12+
<CardHeader>
13+
<h4 className="font-open-sans text-small text-default-600">Contact to me.</h4>
14+
</CardHeader>
15+
<CardBody className="px-3 py-0 text-small text-default-400">
16+
<div className="flex flex-col">
17+
<div className="flex items-center justify-between">
18+
<Link className="flex items-center space-x-2 text-default-600">
19+
<FaMapMarkerAlt /> <span>Location</span>
20+
</Link>
21+
<span className="text-default-400">{userData?.location}</span>
22+
</div>
23+
<div className="flex items-center justify-between">
24+
<Link
25+
showAnchorIcon
26+
href="https://maddonsmanager.github.io/"
27+
isExternal
28+
className="flex items-center space-x-2 text-default-600 hover:text-blue-600"
29+
>
30+
<BsBuildings /> <span>Company</span>
31+
</Link>
32+
<span className="text-default-400">{userData?.company}</span>
33+
</div>
34+
<div className="flex items-center justify-between">
35+
<Link
36+
showAnchorIcon
37+
href="https://x.com/__J3ff_"
38+
isExternal
39+
className="flex items-center space-x-2 text-default-600 hover:text-blue-600"
40+
>
41+
<BsTwitterX /> <span>Xtwitter</span>
42+
</Link>
43+
<span className="text-default-400">{userData?.twitter_username}</span>
44+
</div>
45+
<div className="flex items-center justify-between">
46+
<Link
47+
href={userData?.html_url}
48+
isExternal
49+
showAnchorIcon
50+
className="flex items-center space-x-2 text-default-600 hover:text-blue-600"
51+
>
52+
<FaGithub /> <span>GitHub</span>
53+
</Link>
54+
<span className="text-default-400">{userData?.html_url}</span>
55+
</div>
56+
<div className="flex items-center justify-between">
57+
<Link
58+
href="mailto:pentsec.2@protonmail.com"
59+
className="flex items-center space-x-2 text-default-600 hover:text-blue-600"
60+
>
61+
<FaEnvelope /> <span>Email</span>
62+
</Link>
63+
<span className="text-default-400">pentsec.2@protonmail.com</span>
64+
</div>
2265
</div>
23-
<div className="flex items-center justify-between">
24-
<Link
25-
showAnchorIcon
26-
href="https://maddonsmanager.github.io/"
27-
isExternal
28-
className="flex items-center space-x-2 text-default-600 hover:text-blue-600"
29-
>
30-
<BsBuildings /> <span>Company</span>
31-
</Link>
32-
<span className="text-default-400">{userData?.company}</span>
33-
</div>
34-
<div className="flex items-center justify-between">
35-
<Link
36-
showAnchorIcon
37-
href="https://x.com/__J3ff_"
38-
isExternal
39-
className="flex items-center space-x-2 text-default-600 hover:text-blue-600"
40-
>
41-
<BsTwitterX /> <span>Xtwitter</span>
42-
</Link>
43-
<span className="text-default-400">{userData?.twitter_username}</span>
44-
</div>
45-
<div className="flex items-center justify-between">
46-
<Link
47-
href={userData?.html_url}
48-
isExternal
49-
showAnchorIcon
50-
className="flex items-center space-x-2 text-default-600 hover:text-blue-600"
51-
>
52-
<FaGithub /> <span>GitHub</span>
53-
</Link>
54-
<span className="text-default-400">{userData?.html_url}</span>
55-
</div>
56-
<div className="flex items-center justify-between">
57-
<Link
58-
href="mailto:pentsec.2@protonmail.com"
59-
className="flex items-center space-x-2 text-default-600 hover:text-blue-600"
60-
>
61-
<FaEnvelope /> <span>Email</span>
62-
</Link>
63-
<span className="text-default-400">pentsec.2@protonmail.com</span>
64-
</div>
65-
</div>
66-
</CardBody>
67-
<CardFooter></CardFooter>
66+
</CardBody>
67+
<CardFooter></CardFooter>
68+
</Skeleton>
6869
</Card>
6970
)
7071
}

src/components/Languages/Languages.tsx

Lines changed: 30 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Card, CardBody, CardHeader, Chip } from '@nextui-org/react'
1+
import { Card, CardBody, CardHeader, Chip, Skeleton } from '@nextui-org/react'
22
import languageColors from '@/data/colors.json'
33
import { GitHubUserContextProps } from '@/types/index'
44

@@ -7,39 +7,40 @@ const Languages = ({ languageStats, isLoading, error }: GitHubUserContextProps)
77
return languageColors[language as keyof typeof languageColors]?.color || ''
88
}
99

10-
if (isLoading) return <p>Loading...</p>
1110
if (error) return <p>Error: {error}</p>
1211
if (!languageStats) return <p>No language stats found</p>
1312

1413
return (
1514
<Card isHoverable className="mb-4">
16-
<CardHeader>
17-
<h4 className="leading-none font-open-sans text-small text-default-600">
18-
Languages I've learned: {languageStats.length}
19-
</h4>
20-
</CardHeader>
21-
<CardBody>
22-
<div className="flex flex-wrap gap-2 mt-2">
23-
{languageStats.map(({ language }) => {
24-
const languageColor = getLanguageColor(language)
25-
return (
26-
<div key={language} className="flex-shrink-0">
27-
<Chip
28-
variant="faded"
29-
startContent={
30-
<p
31-
className="w-3 h-3 mr-1 rounded-full opacity-60"
32-
style={{ backgroundColor: languageColor }}
33-
/>
34-
}
35-
>
36-
{language}
37-
</Chip>
38-
</div>
39-
)
40-
})}
41-
</div>
42-
</CardBody>
15+
<Skeleton isLoaded={!isLoading} className="w-full h-full">
16+
<CardHeader>
17+
<h4 className="leading-none font-open-sans text-small text-default-600">
18+
Languages I've learned: {languageStats.length}
19+
</h4>
20+
</CardHeader>
21+
<CardBody>
22+
<div className="flex flex-wrap gap-2 mt-2">
23+
{languageStats.map(({ language }) => {
24+
const languageColor = getLanguageColor(language)
25+
return (
26+
<div key={language} className="flex-shrink-0">
27+
<Chip
28+
variant="faded"
29+
startContent={
30+
<p
31+
className="w-3 h-3 mr-1 rounded-full opacity-60"
32+
style={{ backgroundColor: languageColor }}
33+
/>
34+
}
35+
>
36+
{language}
37+
</Chip>
38+
</div>
39+
)
40+
})}
41+
</div>
42+
</CardBody>
43+
</Skeleton>
4344
</Card>
4445
)
4546
}

src/components/ProfileCard/ProfileCard.tsx

Lines changed: 60 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,23 @@
1-
import { Card, CardHeader, CardBody, CardFooter, Avatar, Tooltip } from '@nextui-org/react'
1+
import {
2+
Card,
3+
CardHeader,
4+
CardBody,
5+
CardFooter,
6+
Avatar,
7+
Tooltip,
8+
Skeleton
9+
} from '@nextui-org/react'
210
import { useState } from 'react'
311
import { ModalSkills } from '@/components'
4-
import { GitHubUserContextProps } from '@/types'
12+
import { UserRepo } from '@/types'
513

6-
export default function ProfileCard({ userData, isLoading, error }: GitHubUserContextProps) {
14+
interface ProfileCardProps {
15+
userData: UserRepo
16+
isLoading: boolean
17+
error: string
18+
}
19+
20+
export default function ProfileCard({ userData, isLoading, error }: ProfileCardProps) {
721
const [isModalOpen, setModalIsOpen] = useState<boolean | undefined>(false)
822

923
const openModal = () => {
@@ -12,7 +26,6 @@ export default function ProfileCard({ userData, isLoading, error }: GitHubUserCo
1226
}
1327
const closeModal = () => setModalIsOpen(false)
1428

15-
if (isLoading) return <p>Loading...</p>
1629
if (error) return <p>Error: {error}</p>
1730

1831
return (
@@ -37,47 +50,50 @@ export default function ProfileCard({ userData, isLoading, error }: GitHubUserCo
3750
}}
3851
>
3952
<Card isPressable isHoverable className="w-full mb-4" onPress={openModal}>
40-
<CardHeader className="items-center justify-center justify-col">
41-
<div className="flex flex-col items-center justify-center gap-1">
42-
<Avatar
43-
isBordered
44-
color="success"
45-
radius="full"
46-
className="w-32 h-32 mb-4 text-large"
47-
src={userData?.avatar_url}
48-
/>
49-
50-
<h4 className="text-2xl font-semibold leading-none text-default-600">
51-
{userData?.name ?? 'Jeff'}
52-
</h4>
53-
<h5 className="text-lg tracking-tight text-default-400">
54-
@{userData?.login}
55-
</h5>
56-
</div>
57-
</CardHeader>
58-
<CardBody className="h-auto px-3 py-0 overflow-hidden font-rubik text-small text-default-400">
59-
<p>{userData?.bio}</p>
60-
<span className="pt-2">
61-
#Freed0m4All
62-
<span className="py-2" aria-label="computer" role="img">
63-
💻
53+
<div className="flex items-center justify-center justify-col mt-2">
54+
<Avatar
55+
isBordered
56+
color="success"
57+
radius="full"
58+
className="w-32 h-32 mb-4 text-large "
59+
src={userData?.avatar_url}
60+
/>
61+
</div>
62+
<Skeleton isLoaded={!isLoading} className="w-full h-full">
63+
<CardHeader className="items-center justify-center justify-col">
64+
<div className="flex flex-col items-center justify-center gap-1">
65+
<h4 className="text-2xl font-semibold leading-none text-default-600">
66+
{userData?.name ?? 'Jeff'}
67+
</h4>
68+
<h5 className="text-lg tracking-tight text-default-400">
69+
@{userData?.login}
70+
</h5>
71+
</div>
72+
</CardHeader>
73+
<CardBody className="h-auto px-3 py-0 overflow-hidden font-rubik text-small text-default-400">
74+
<p>{userData?.bio}</p>
75+
<span className="pt-2">
76+
#Freed0m4All
77+
<span className="py-2" aria-label="computer" role="img">
78+
💻
79+
</span>
6480
</span>
65-
</span>
66-
</CardBody>
67-
<CardFooter className="gap-3">
68-
<div className="flex gap-1">
69-
<p className="font-semibold text-default-400 text-small">
70-
{userData?.following}
71-
</p>
72-
<p className=" text-default-400 text-small">Following</p>
73-
</div>
74-
<div className="flex gap-1">
75-
<p className="font-semibold text-default-400 text-small">
76-
{userData?.followers}
77-
</p>
78-
<p className="text-default-400 text-small">Followers</p>
79-
</div>
80-
</CardFooter>
81+
</CardBody>
82+
<CardFooter className="gap-3">
83+
<div className="flex gap-1">
84+
<p className="font-semibold text-default-400 text-small">
85+
{userData?.following}
86+
</p>
87+
<p className=" text-default-400 text-small">Following</p>
88+
</div>
89+
<div className="flex gap-1">
90+
<p className="font-semibold text-default-400 text-small">
91+
{userData?.followers}
92+
</p>
93+
<p className="text-default-400 text-small">Followers</p>
94+
</div>
95+
</CardFooter>
96+
</Skeleton>
8197
</Card>
8298
</Tooltip>
8399
{isModalOpen && <ModalSkills onClose={closeModal} />}

0 commit comments

Comments
 (0)