Skip to content

Commit a2d6e2b

Browse files
committed
languages bar tooltip, Warning Sign at /saved page
1 parent 7cc024c commit a2d6e2b

File tree

5 files changed

+364
-100
lines changed

5 files changed

+364
-100
lines changed

app/saved/page.tsx

Lines changed: 55 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ import { Card, CardContent } from '@/components/ui/card';
55
import { Button } from '@/components/ui/button';
66
import { Star, GitFork, Tag } from 'lucide-react';
77
import { useEffect, useState } from 'react';
8+
import {
9+
Tooltip,
10+
TooltipContent,
11+
TooltipProvider,
12+
TooltipTrigger,
13+
} from "@/components/ui/tooltip";
814

915
interface Language {
1016
[key: string]: number;
@@ -53,21 +59,39 @@ const LanguageBar = ({ languages }: { languages: Language }) => {
5359
<div className="space-y-4">
5460
<h2 className="text-lg font-semibold text-white">Languages</h2>
5561

56-
<div className="h-2 w-full flex rounded-full overflow-hidden">
57-
{percentages.map(({ name, percentage }) => (
58-
<div
59-
key={name}
60-
style={{
61-
width: `${percentage}%`,
62-
backgroundColor: languageColors[name] || '#ededed'
63-
}}
64-
/>
65-
))}
66-
</div>
62+
<TooltipProvider>
63+
<div className="h-2 w-full flex rounded-full overflow-hidden">
64+
{percentages.map(({ name, percentage }) => (
65+
<Tooltip key={name} delayDuration={0}>
66+
<TooltipTrigger asChild>
67+
<div
68+
style={{
69+
width: `${percentage}%`,
70+
backgroundColor: languageColors[name] || '#ededed'
71+
}}
72+
className="transition-opacity hover:opacity-80"
73+
/>
74+
</TooltipTrigger>
75+
<TooltipContent
76+
className="bg-slate-800 border-slate-700 text-white"
77+
side="top"
78+
>
79+
<div className="flex items-center gap-2">
80+
<div
81+
className="w-3 h-3 rounded-full"
82+
style={{ backgroundColor: languageColors[name] || '#ededed' }}
83+
/>
84+
<span>{name}: {percentage.toFixed(1)}%</span>
85+
</div>
86+
</TooltipContent>
87+
</Tooltip>
88+
))}
89+
</div>
90+
</TooltipProvider>
6791

6892
<div className="flex flex-wrap gap-4">
6993
{percentages
70-
.filter(({ percentage }) => percentage >= 2)
94+
.filter(({ percentage }) => percentage >= 2)
7195
.map(({ name, percentage }) => (
7296
<div key={name} className="flex items-center gap-2">
7397
<span
@@ -98,6 +122,7 @@ function getRandomGradient() {
98122
export default function SavedPage() {
99123
const { removeProject } = useSaved();
100124
const [savedProjectDetails, setSavedProjectDetails] = useState<Project[]>([]);
125+
const [showWarning, setShowWarning] = useState<boolean>(true);
101126

102127
useEffect(() => {
103128
const fetchSavedProjects = async () => {
@@ -132,10 +157,24 @@ export default function SavedPage() {
132157
<div className="min-h-screen bg-gradient-to-br from-slate-900 via-slate-800 to-slate-900">
133158
<div className="pt-24 p-8">
134159
<div className="max-w-6xl mx-auto">
135-
<div className="text-center mb-8">
136-
<h1 className="text-4xl font-bold mb-6 bg-gradient-to-r from-blue-400 to-purple-400 text-transparent bg-clip-text">
137-
Saved Projects
138-
</h1>
160+
<div className="text-center mb-8">
161+
<h1 className="text-4xl font-bold mb-6 bg-gradient-to-r from-blue-400 to-purple-400 text-transparent bg-clip-text">
162+
Saved Projects
163+
</h1>
164+
{showWarning && (
165+
<div className="flex items-center justify-center gap-1">
166+
<p className="text-yellow-400/80 text-sm flex items-center gap-2">
167+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="lucide-alert-triangle"><path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z"/><path d="M12 9v4"/><path d="M12 17h.01"/></svg>
168+
Projects are saved in your browsers local storage and will be deleted if you clear your browser data
169+
</p>
170+
<button
171+
onClick={() => setShowWarning(false)}
172+
className="ml-1 text-gray-400 hover:text-gray-300 p-1 rounded-full hover:bg-gray-700/50 transition-colors"
173+
>
174+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>
175+
</button>
176+
</div>
177+
)}
139178
</div>
140179
{savedProjectDetails.length === 0 ? (
141180
<p className="text-gray-400 text-center">No projects saved yet.</p>

components/GithubProjectsPage.tsx

Lines changed: 108 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,12 @@ import {
1818
AlertDialogHeader,
1919
AlertDialogTitle,
2020
} from "@/components/ui/alert-dialog";
21+
import {
22+
Tooltip,
23+
TooltipContent,
24+
TooltipProvider,
25+
TooltipTrigger,
26+
} from "@/components/ui/tooltip";
2127

2228
const DiscordIcon = ({ className = "" }) => (
2329
<svg
@@ -85,32 +91,50 @@ const LanguageBar = ({ languages }: { languages: Language }) => {
8591
<div className="space-y-4">
8692
<h2 className="text-lg font-semibold text-white">Languages</h2>
8793

88-
<div className="h-2 w-full flex rounded-full overflow-hidden">
89-
{percentages.map(({ name, percentage }) => (
90-
<div
91-
key={name}
92-
style={{
93-
width: `${percentage}%`,
94-
backgroundColor: languageColors[name] || '#ededed'
95-
}}
96-
/>
97-
))}
98-
</div>
94+
<TooltipProvider>
95+
<div className="h-2 w-full flex rounded-full overflow-hidden">
96+
{percentages.map(({ name, percentage }) => (
97+
<Tooltip key={name} delayDuration={0}>
98+
<TooltipTrigger asChild>
99+
<div
100+
style={{
101+
width: `${percentage}%`,
102+
backgroundColor: languageColors[name] || '#ededed'
103+
}}
104+
className="transition-opacity hover:opacity-80"
105+
/>
106+
</TooltipTrigger>
107+
<TooltipContent
108+
className="bg-slate-800 border-slate-700 text-white"
109+
side="top"
110+
>
111+
<div className="flex items-center gap-2">
112+
<div
113+
className="w-3 h-3 rounded-full"
114+
style={{ backgroundColor: languageColors[name] || '#ededed' }}
115+
/>
116+
<span>{name}: {percentage.toFixed(1)}%</span>
117+
</div>
118+
</TooltipContent>
119+
</Tooltip>
120+
))}
121+
</div>
122+
</TooltipProvider>
99123

100124
<div className="flex flex-wrap gap-4">
101-
{percentages
102-
.filter(({ percentage }) => percentage >= 2)
103-
.map(({ name, percentage }) => (
104-
<div key={name} className="flex items-center gap-2">
105-
<span
106-
className="w-3 h-3 rounded-full"
107-
style={{ backgroundColor: languageColors[name] || '#ededed' }}
108-
/>
109-
<span className="font-medium text-white">{name}</span>
110-
<span className="text-gray-400">{percentage.toFixed(1)}%</span>
125+
{percentages
126+
.filter(({ percentage }) => percentage >= 2)
127+
.map(({ name, percentage }) => (
128+
<div key={name} className="flex items-center gap-2">
129+
<span
130+
className="w-3 h-3 rounded-full"
131+
style={{ backgroundColor: languageColors[name] || '#ededed' }}
132+
/>
133+
<span className="font-medium text-white">{name}</span>
134+
<span className="text-gray-400">{percentage.toFixed(1)}%</span>
135+
</div>
136+
))}
111137
</div>
112-
))}
113-
</div>
114138
</div>
115139
);
116140
};
@@ -272,70 +296,70 @@ export default function GithubProjectsPage() {
272296
</div>
273297

274298
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8 mb-8">
275-
{currentProjects.map((project) => {
276-
const isSaved = savedProjects.includes(project.name);
277-
return (
299+
{currentProjects.map((project) => {
300+
const isSaved = savedProjects.includes(project.name);
301+
return (
302+
<div
303+
key={project.name}
304+
className="group relative cursor-pointer"
305+
onClick={() => handleCardClick(project.url)}
306+
>
307+
<div className={`absolute inset-0 ${project.color} rounded-xl blur-md opacity-20 group-hover:opacity-30 transition-all duration-500`}></div>
308+
<Card className="relative h-full bg-slate-800/50 border-slate-700 hover:border-slate-600 transition-all duration-500 backdrop-blur-sm transform-gpu hover:-translate-y-2 hover:scale-105">
309+
<CardContent className="p-6">
310+
<div className="flex justify-between items-start mb-4">
311+
<h2 className="text-2xl font-bold text-white group-hover:text-transparent group-hover:bg-gradient-to-r group-hover:from-blue-400 group-hover:via-purple-400 group-hover:to-pink-400 group-hover:bg-clip-text transition-all duration-300">
312+
{project.name}
313+
</h2>
314+
<Button
315+
variant="ghost"
316+
size="sm"
317+
className={`text-gray-400 ${isSaved ? 'text-green-500' : 'hover:text-white'}`}
318+
onClick={(e) => {
319+
e.stopPropagation();
320+
if (isSaved) {
321+
removeProject(project.name);
322+
} else {
323+
addProject(project.name);
324+
}
325+
}}
326+
>
327+
{isSaved ? 'Unsave' : 'Save'}
328+
</Button>
329+
</div>
330+
<p className="text-gray-300 mb-6">{project.description}</p>
331+
<div className="flex flex-wrap gap-2 mb-6">
332+
{project.tags.map((tag, tagIndex) => (
278333
<div
279-
key={project.name}
280-
className="group relative cursor-pointer"
281-
onClick={() => handleCardClick(project.url)}
334+
key={tagIndex}
335+
onClick={(e) => {
336+
e.stopPropagation();
337+
setSearchTerm(tag);
338+
}}
339+
className="bg-slate-700/50 text-gray-300 text-sm px-3 py-1 rounded-full flex items-center transform transition-all duration-300 hover:scale-105 hover:bg-slate-600/50"
282340
>
283-
<div className={`absolute inset-0 ${project.color} rounded-xl blur-md opacity-20 group-hover:opacity-30 transition-all duration-500`}></div>
284-
<Card className="relative h-full bg-slate-800/50 border-slate-700 hover:border-slate-600 transition-all duration-500 backdrop-blur-sm transform-gpu hover:-translate-y-2 hover:scale-105">
285-
<CardContent className="p-6">
286-
<div className="flex justify-between items-start mb-4">
287-
<h2 className="text-2xl font-bold text-white group-hover:text-transparent group-hover:bg-gradient-to-r group-hover:from-blue-400 group-hover:via-purple-400 group-hover:to-pink-400 group-hover:bg-clip-text transition-all duration-300">
288-
{project.name}
289-
</h2>
290-
<Button
291-
variant="ghost"
292-
size="sm"
293-
className={`text-gray-400 ${isSaved ? 'text-green-500' : 'hover:text-white'}`}
294-
onClick={(e) => {
295-
e.stopPropagation();
296-
if (isSaved) {
297-
removeProject(project.name);
298-
} else {
299-
addProject(project.name);
300-
}
301-
}}
302-
>
303-
{isSaved ? 'Unsave' : 'Save'}
304-
</Button>
305-
</div>
306-
307-
<p className="text-gray-300 mb-6">{project.description}</p>
308-
309-
<div className="flex flex-wrap gap-2 mb-6">
310-
{project.tags.map((tag, tagIndex) => (
311-
<div
312-
key={tagIndex}
313-
className="bg-slate-700/50 text-gray-300 text-sm px-3 py-1 rounded-full flex items-center transform transition-all duration-300 hover:scale-105 hover:bg-slate-600/50"
314-
>
315-
<Tag className="h-3 w-3 mr-1.5" />
316-
{tag}
317-
</div>
318-
))}
319-
</div>
320-
321-
<LanguageBar languages={project.languages} />
322-
323-
<div className="flex justify-start gap-6 text-sm text-gray-400 mt-6">
324-
<span className="flex items-center">
325-
<Star className="h-4 w-4 mr-1.5 text-yellow-500" />
326-
{project.stars}
327-
</span>
328-
<span className="flex items-center">
329-
<GitFork className="h-4 w-4 mr-1.5" />
330-
{project.forks}
331-
</span>
332-
</div>
333-
</CardContent>
334-
</Card>
341+
<Tag className="h-3 w-3 mr-1.5" />
342+
{tag}
335343
</div>
336-
);
337-
})}
338-
</div>
344+
))}
345+
</div>
346+
<LanguageBar languages={project.languages} />
347+
<div className="flex justify-start gap-6 text-sm text-gray-400 mt-6">
348+
<span className="flex items-center">
349+
<Star className="h-4 w-4 mr-1.5 text-yellow-500" />
350+
{project.stars}
351+
</span>
352+
<span className="flex items-center">
353+
<GitFork className="h-4 w-4 mr-1.5" />
354+
{project.forks}
355+
</span>
356+
</div>
357+
</CardContent>
358+
</Card>
359+
</div>
360+
);
361+
})}
362+
</div>
339363

340364
<div className="flex justify-center gap-4 mb-16">
341365
<Button

components/ui/tooltip.tsx

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
"use client"
2+
3+
import * as React from "react"
4+
import * as TooltipPrimitive from "@radix-ui/react-tooltip"
5+
6+
import { cn } from "@/lib/utils"
7+
8+
const TooltipProvider = TooltipPrimitive.Provider
9+
10+
const Tooltip = TooltipPrimitive.Root
11+
12+
const TooltipTrigger = TooltipPrimitive.Trigger
13+
14+
const TooltipContent = React.forwardRef<
15+
React.ElementRef<typeof TooltipPrimitive.Content>,
16+
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
17+
>(({ className, sideOffset = 4, ...props }, ref) => (
18+
<TooltipPrimitive.Content
19+
ref={ref}
20+
sideOffset={sideOffset}
21+
className={cn(
22+
"z-50 overflow-hidden rounded-md border bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
23+
className
24+
)}
25+
{...props}
26+
/>
27+
))
28+
TooltipContent.displayName = TooltipPrimitive.Content.displayName
29+
30+
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }

0 commit comments

Comments
 (0)