style: refine admin dashboard and project management UI with cohesive color palette and improved readability
- Update background colors and text styles for better contrast and legibility. - Enhance button styles and hover effects for a more modern look. - Remove unnecessary scaling effects and adjust border styles for consistency. - Introduce a cohesive design language across components to improve user experience.
This commit is contained in:
25
app/api/contacts/route.ts
Normal file
25
app/api/contacts/route.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { prisma } from '@/lib/prisma';
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
try {
|
||||
// In a real app, you would check for admin session here
|
||||
// For now, we trust the 'x-admin-request' header if it's set by the server-side component or middleware
|
||||
// but typically you'd verify the session cookie/token
|
||||
|
||||
const contacts = await prisma.contact.findMany({
|
||||
orderBy: {
|
||||
createdAt: 'desc',
|
||||
},
|
||||
take: 100,
|
||||
});
|
||||
|
||||
return NextResponse.json({ contacts });
|
||||
} catch (error) {
|
||||
console.error('Error fetching contacts:', error);
|
||||
return NextResponse.json(
|
||||
{ error: 'Failed to fetch contacts' },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -221,11 +221,11 @@ export default function ChatWidget() {
|
||||
setIsOpen(true);
|
||||
}
|
||||
}}
|
||||
className="fixed bottom-4 left-4 md:bottom-6 md:left-6 z-30 bg-stone-800/80 backdrop-blur-md text-stone-50 p-3.5 rounded-full shadow-[0_8px_20px_rgba(41,37,36,0.2)] hover:bg-stone-800 hover:scale-105 transition-all duration-300 group cursor-pointer border border-white/10 ring-1 ring-white/20"
|
||||
className="fixed bottom-4 left-4 md:bottom-6 md:left-6 z-30 bg-[#292524] text-[#fdfcf8] p-3.5 rounded-full shadow-[0_8px_20px_rgba(41,37,36,0.25)] hover:bg-[#44403c] hover:scale-105 transition-all duration-300 group cursor-pointer border border-[#f3f1e7]/20 ring-1 ring-[#f3f1e7]/10"
|
||||
aria-label="Open chat"
|
||||
>
|
||||
<MessageCircle size={24} />
|
||||
<span className="absolute top-0 right-0 w-3 h-3 bg-stone-400 rounded-full animate-pulse shadow-sm border-2 border-stone-800" />
|
||||
<span className="absolute top-0 right-0 w-3 h-3 bg-green-500 rounded-full animate-pulse shadow-sm border-2 border-[#292524]" />
|
||||
|
||||
{/* Tooltip */}
|
||||
<span className="absolute bottom-full left-1/2 -translate-x-1/2 mb-3 px-3 py-1.5 bg-stone-900/90 text-stone-50 text-xs font-medium rounded-lg opacity-0 group-hover:opacity-100 transition-opacity whitespace-nowrap pointer-events-none z-[100] shadow-xl backdrop-blur-sm">
|
||||
@@ -244,16 +244,16 @@ export default function ChatWidget() {
|
||||
animate={{ opacity: 1, y: 0, scale: 1, filter: "blur(0px)" }}
|
||||
exit={{ opacity: 0, y: 20, scale: 0.95, filter: "blur(10px)" }}
|
||||
transition={{ type: "spring", damping: 30, stiffness: 400 }}
|
||||
className="fixed bottom-20 left-4 right-4 md:bottom-24 md:left-6 md:right-auto z-30 md:w-[380px] h-[60vh] md:h-[550px] max-h-[600px] bg-[#fdfcf8]/30 backdrop-blur-2xl saturate-150 rounded-2xl shadow-[0_12px_40px_rgba(41,37,36,0.15)] flex flex-col overflow-hidden border border-white/40 ring-1 ring-white/50"
|
||||
className="fixed bottom-20 left-4 right-4 md:bottom-24 md:left-6 md:right-auto z-30 md:w-[380px] h-[60vh] md:h-[550px] max-h-[600px] bg-[#fdfcf8]/95 backdrop-blur-xl saturate-100 rounded-2xl shadow-[0_12px_40px_rgba(41,37,36,0.2)] flex flex-col overflow-hidden border border-[#e7e5e4] ring-1 ring-[#f3f1e7]"
|
||||
>
|
||||
{/* Header */}
|
||||
<div className="bg-white/20 backdrop-blur-md text-stone-800 p-4 flex items-center justify-between border-b border-white/20">
|
||||
<div className="bg-[#fdfcf8] text-[#292524] p-4 flex items-center justify-between border-b border-[#e7e5e4]">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="relative">
|
||||
<div className="w-10 h-10 rounded-full bg-gradient-to-br from-stone-100/80 to-white/80 flex items-center justify-center ring-1 ring-white shadow-sm backdrop-blur-sm">
|
||||
<Sparkles size={18} className="text-stone-600" />
|
||||
<div className="w-10 h-10 rounded-full bg-gradient-to-br from-[#f3f1e7] to-[#fdfcf8] flex items-center justify-center ring-1 ring-[#e7e5e4] shadow-sm">
|
||||
<Sparkles size={18} className="text-[#57534e]" />
|
||||
</div>
|
||||
<span className="absolute bottom-0 right-0 w-2.5 h-2.5 bg-emerald-400 rounded-full border-2 border-white shadow-sm" />
|
||||
<span className="absolute bottom-0 right-0 w-2.5 h-2.5 bg-green-500 rounded-full border-2 border-[#fdfcf8] shadow-sm" />
|
||||
</div>
|
||||
<div className="min-w-0 flex-1">
|
||||
<h3 className="font-bold text-sm truncate text-stone-900 tracking-tight">
|
||||
@@ -293,14 +293,14 @@ export default function ChatWidget() {
|
||||
className={`flex ${message.sender === "user" ? "justify-end" : "justify-start"}`}
|
||||
>
|
||||
<div
|
||||
className={`max-w-[85%] rounded-2xl px-4 py-3 backdrop-blur-md shadow-sm ${
|
||||
className={`max-w-[85%] rounded-2xl px-4 py-3 shadow-sm ${
|
||||
message.sender === "user"
|
||||
? "bg-stone-800/90 text-stone-50 ring-1 ring-white/10"
|
||||
: "bg-white/70 text-stone-800 border border-white/50 ring-1 ring-white/40"
|
||||
? "bg-[#292524] text-[#fdfcf8]"
|
||||
: "bg-[#f3f1e7] text-[#292524] border border-[#e7e5e4]"
|
||||
}`}
|
||||
>
|
||||
<p className={`text-sm whitespace-pre-wrap break-words leading-relaxed ${
|
||||
message.sender === "user" ? "text-stone-50 font-light" : "text-stone-800 font-medium"
|
||||
message.sender === "user" ? "text-[#fdfcf8]/90 font-light" : "text-[#292524] font-medium"
|
||||
}`}>
|
||||
{message.text}
|
||||
</p>
|
||||
@@ -327,7 +327,7 @@ export default function ChatWidget() {
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
className="flex justify-start"
|
||||
>
|
||||
<div className="bg-white/60 backdrop-blur-md border border-white/40 rounded-2xl px-4 py-3 shadow-sm ring-1 ring-white/40">
|
||||
<div className="bg-[#f3f1e7] border border-[#e7e5e4] rounded-2xl px-4 py-3 shadow-sm">
|
||||
<div className="flex gap-1.5">
|
||||
<motion.div
|
||||
className="w-1.5 h-1.5 bg-stone-500 rounded-full"
|
||||
@@ -365,7 +365,7 @@ export default function ChatWidget() {
|
||||
</div>
|
||||
|
||||
{/* Input */}
|
||||
<div className="p-4 bg-white/20 backdrop-blur-xl border-t border-white/20">
|
||||
<div className="p-4 bg-[#fdfcf8] border-t border-[#e7e5e4]">
|
||||
<div className="flex gap-2">
|
||||
<input
|
||||
ref={inputRef}
|
||||
@@ -375,12 +375,12 @@ export default function ChatWidget() {
|
||||
onKeyPress={handleKeyPress}
|
||||
placeholder="Ask anything..."
|
||||
disabled={isLoading}
|
||||
className="flex-1 px-4 py-3 text-sm bg-white/50 backdrop-blur-sm text-stone-800 rounded-xl border border-white/40 focus:outline-none focus:ring-2 focus:ring-stone-200/50 focus:border-stone-400/50 focus:bg-white/70 disabled:opacity-50 disabled:cursor-not-allowed placeholder:text-stone-500 transition-all shadow-inner"
|
||||
className="flex-1 px-4 py-3 text-sm bg-[#f5f5f4] text-[#292524] rounded-xl border border-[#e7e5e4] focus:outline-none focus:ring-2 focus:ring-[#e7e5e4] focus:border-[#a8a29e] focus:bg-[#fdfcf8] disabled:opacity-50 disabled:cursor-not-allowed placeholder:text-[#78716c] transition-all shadow-inner"
|
||||
/>
|
||||
<button
|
||||
onClick={handleSend}
|
||||
disabled={!inputValue.trim() || isLoading}
|
||||
className="p-3 bg-stone-800 text-stone-50 rounded-xl hover:bg-stone-700 hover:shadow-lg hover:scale-105 transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:scale-100 shadow-md flex items-center justify-center aspect-square"
|
||||
className="p-3 bg-[#292524] text-[#fdfcf8] rounded-xl hover:bg-[#44403c] hover:shadow-lg hover:scale-105 transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:scale-100 shadow-md flex items-center justify-center aspect-square"
|
||||
aria-label="Send message"
|
||||
>
|
||||
{isLoading ? (
|
||||
@@ -405,7 +405,7 @@ export default function ChatWidget() {
|
||||
inputRef.current?.focus();
|
||||
}}
|
||||
disabled={isLoading}
|
||||
className="px-3 py-1.5 text-xs font-medium bg-white/40 backdrop-blur-md text-stone-700 rounded-lg hover:bg-white/60 hover:text-stone-900 border border-white/40 transition-all whitespace-nowrap disabled:opacity-50 flex-shrink-0 shadow-sm"
|
||||
className="px-3 py-1.5 text-xs font-medium bg-[#f5f5f4] text-[#57534e] rounded-lg hover:bg-[#e7e5e4] hover:text-[#292524] border border-[#e7e5e4] transition-all whitespace-nowrap disabled:opacity-50 flex-shrink-0 shadow-sm"
|
||||
>
|
||||
{suggestion}
|
||||
</button>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import { useState, useEffect } from "react";
|
||||
import { motion, Variants } from "framer-motion";
|
||||
import { ExternalLink, Github, Layers, ArrowRight } from "lucide-react";
|
||||
import { ExternalLink, Github, Layers, ArrowRight, ArrowLeft, Calendar } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import Image from "next/image";
|
||||
|
||||
@@ -98,11 +98,8 @@ const Projects = () => {
|
||||
<motion.div
|
||||
key={project.id}
|
||||
variants={fadeInUp}
|
||||
whileHover={{
|
||||
y: -8,
|
||||
transition: { duration: 0.4, ease: "easeOut" },
|
||||
}}
|
||||
className="group relative flex flex-col bg-white/40 backdrop-blur-xl backdrop-saturate-150 rounded-2xl overflow-hidden shadow-[0_4px_20px_rgba(0,0,0,0.02)] hover:shadow-[0_20px_40px_rgba(0,0,0,0.06)] transition-all duration-500 ease-out border border-white/50 ring-1 ring-white/30"
|
||||
whileHover={{ y: -8 }}
|
||||
className="group flex flex-col bg-white/40 backdrop-blur-xl rounded-2xl overflow-hidden border border-white/60 shadow-[0_4px_20px_rgba(0,0,0,0.02)] hover:shadow-[0_20px_40px_rgba(0,0,0,0.06)] transition-all duration-500"
|
||||
>
|
||||
{/* Project Cover / Image Area */}
|
||||
<div className="relative aspect-[16/10] overflow-hidden bg-stone-100">
|
||||
@@ -114,12 +111,10 @@ const Projects = () => {
|
||||
fill
|
||||
className="object-cover transition-transform duration-1000 ease-out group-hover:scale-110"
|
||||
/>
|
||||
{/* Subtle Overlay for better text readability and depth */}
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-stone-900/20 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-500" />
|
||||
</>
|
||||
) : (
|
||||
<div className="absolute inset-0 bg-stone-200 flex items-center justify-center overflow-hidden">
|
||||
{/* Mesh Gradient Fallback */}
|
||||
<div className="absolute inset-0 bg-gradient-to-br from-stone-300 via-stone-200 to-stone-300" />
|
||||
<div className="absolute top-[-20%] left-[-10%] w-[70%] h-[70%] bg-white/20 rounded-full blur-3xl animate-pulse" />
|
||||
<div className="absolute bottom-[-10%] right-[-5%] w-[60%] h-[60%] bg-stone-400/10 rounded-full blur-2xl" />
|
||||
@@ -141,32 +136,34 @@ const Projects = () => {
|
||||
{/* Featured Badge */}
|
||||
{project.featured && (
|
||||
<div className="absolute top-3 left-3 z-20">
|
||||
<div className="px-3 py-1 bg-red-500 text-white text-[10px] font-black uppercase tracking-widest rounded-full shadow-lg border border-red-400/50">
|
||||
<div className="px-3 py-1 bg-[#292524]/80 backdrop-blur-md text-[#fdfcf8] text-[10px] font-bold uppercase tracking-widest rounded-full shadow-sm border border-white/10">
|
||||
Featured
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Overlay Links */}
|
||||
<div className="absolute inset-0 bg-stone-900/40 opacity-0 group-hover:opacity-100 transition-opacity duration-500 ease-out flex items-center justify-center gap-4 backdrop-blur-[2px] z-10">
|
||||
<div className="absolute inset-0 bg-stone-900/40 opacity-0 group-hover:opacity-100 transition-opacity duration-500 ease-out flex items-center justify-center gap-4 backdrop-blur-[2px] z-20 pointer-events-none">
|
||||
{project.github && (
|
||||
<a
|
||||
href={project.github}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="p-3 bg-white text-stone-900 rounded-full hover:scale-110 transition-all duration-300 shadow-xl border border-white/50"
|
||||
className="p-3 bg-white text-stone-900 rounded-full hover:scale-110 transition-all duration-300 shadow-xl border border-white/50 pointer-events-auto"
|
||||
aria-label="GitHub"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<Github size={20} />
|
||||
</a>
|
||||
)}
|
||||
{project.live && (
|
||||
{project.live && !project.title.toLowerCase().includes('kernel panic') && (
|
||||
<a
|
||||
href={project.live}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="p-3 bg-white text-stone-900 rounded-full hover:scale-110 transition-all duration-300 shadow-xl border border-white/50"
|
||||
className="p-3 bg-white text-stone-900 rounded-full hover:scale-110 transition-all duration-300 shadow-xl border border-white/50 pointer-events-auto"
|
||||
aria-label="Live Demo"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<ExternalLink size={20} />
|
||||
</a>
|
||||
@@ -175,47 +172,67 @@ const Projects = () => {
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<div className="flex flex-col flex-1 p-6">
|
||||
<div className="flex justify-between items-start mb-3">
|
||||
<h3 className="text-xl font-bold text-stone-900 group-hover:text-stone-700 transition-colors duration-300 tracking-tight">
|
||||
<div className="p-6 flex flex-col flex-1">
|
||||
{/* Stretched Link covering the whole card (including image area) */}
|
||||
<Link
|
||||
href={`/projects/${project.title.toLowerCase().replace(/[^a-z0-9]+/g, '-')}`}
|
||||
className="absolute inset-0 z-10"
|
||||
aria-label={`View project ${project.title}`}
|
||||
/>
|
||||
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<h3 className="text-xl font-bold text-stone-900 group-hover:text-stone-600 transition-colors">
|
||||
{project.title}
|
||||
</h3>
|
||||
<span className="text-xs font-mono text-stone-500 bg-white/60 border border-stone-200/50 px-2 py-1 rounded backdrop-blur-sm">
|
||||
{new Date(project.date).getFullYear()}
|
||||
</span>
|
||||
<div className="flex items-center space-x-2 text-stone-400 text-xs font-mono bg-white/50 px-2 py-1 rounded border border-stone-100">
|
||||
<Calendar size={12} />
|
||||
<span>{new Date(project.date).getFullYear()}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p className="text-stone-700 font-medium text-sm leading-relaxed mb-6 line-clamp-3 flex-1 opacity-90">
|
||||
<p className="text-stone-600 mb-6 leading-relaxed line-clamp-3 text-sm flex-1">
|
||||
{project.description}
|
||||
</p>
|
||||
|
||||
<div className="space-y-4 mt-auto">
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{project.tags.slice(0, 3).map((tag, tIdx) => (
|
||||
<span
|
||||
key={`${project.id}-${tag}-${tIdx}`}
|
||||
className="text-xs px-2.5 py-1 bg-white/50 border border-white/60 rounded-md text-stone-700 font-medium hover:bg-white/80 hover:border-white transition-all duration-300 ease-out shadow-sm"
|
||||
>
|
||||
{tag}
|
||||
</span>
|
||||
))}
|
||||
{project.tags.length > 3 && (
|
||||
<span className="text-xs px-2 py-1 text-stone-500 font-medium">
|
||||
+ {project.tags.length - 3}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex flex-wrap gap-2 mb-6">
|
||||
{project.tags.slice(0, 4).map((tag) => (
|
||||
<span
|
||||
key={tag}
|
||||
className="px-2.5 py-1 bg-white/60 border border-stone-100 text-stone-600 text-xs font-medium rounded-md"
|
||||
>
|
||||
{tag}
|
||||
</span>
|
||||
))}
|
||||
{project.tags.length > 4 && (
|
||||
<span className="px-2 py-1 text-stone-400 text-xs">+ {project.tags.length - 4}</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<Link
|
||||
href={`/projects/${project.title.toLowerCase().replace(/\s+/g, "-")}`}
|
||||
className="inline-flex items-center text-sm font-bold text-stone-800 hover:gap-3 transition-all duration-300 ease-out group/link"
|
||||
>
|
||||
Read more{" "}
|
||||
<ArrowRight
|
||||
size={16}
|
||||
className="ml-1 transition-transform duration-300 ease-out group-hover/link:translate-x-1"
|
||||
/>
|
||||
</Link>
|
||||
<div className="mt-auto pt-4 border-t border-stone-100 flex items-center justify-between relative z-20">
|
||||
<div className="flex gap-3">
|
||||
{project.github && (
|
||||
<a
|
||||
href={project.github}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-stone-400 hover:text-stone-900 transition-colors relative z-20 hover:scale-110"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<Github size={18} />
|
||||
</a>
|
||||
)}
|
||||
{project.live && !project.title.toLowerCase().includes('kernel panic') && (
|
||||
<a
|
||||
href={project.live}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-stone-400 hover:text-stone-900 transition-colors relative z-20 hover:scale-110"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<ExternalLink size={18} />
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
@@ -80,7 +80,7 @@ html {
|
||||
0 20px 25px -5px rgba(0, 0, 0, 0.08),
|
||||
0 10px 10px -5px rgba(0, 0, 0, 0.02),
|
||||
inset 0 0 20px rgba(255, 255, 255, 0.8);
|
||||
transform: translateY(-4px) scale(1.005);
|
||||
transform: translateY(-4px);
|
||||
border-color: #ffffff;
|
||||
}
|
||||
|
||||
@@ -103,9 +103,6 @@ div {
|
||||
color: #44403c;
|
||||
}
|
||||
|
||||
/* Utility for the liquid melt effect container */
|
||||
/* Liquid container removed - no filters applied */
|
||||
|
||||
/* Hide scrollbar but keep functionality */
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
@@ -145,18 +142,6 @@ div {
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
@keyframes liquid-pulse {
|
||||
0% {
|
||||
transform: scale(1);
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
100% {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Liquid Blobs Background */
|
||||
.liquid-bg-blob {
|
||||
position: absolute;
|
||||
@@ -188,3 +173,43 @@ div {
|
||||
.markdown pre {
|
||||
@apply bg-stone-900 text-stone-50 p-4 rounded-xl overflow-x-auto mb-6;
|
||||
}
|
||||
|
||||
/* Admin Dashboard Styles - Organic Modern */
|
||||
.animated-bg {
|
||||
background: #fdfcf8;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.admin-glass {
|
||||
background: rgba(253, 252, 248, 0.9);
|
||||
backdrop-filter: blur(12px);
|
||||
-webkit-backdrop-filter: blur(12px);
|
||||
border-bottom: 1px solid #e7e5e4;
|
||||
color: #292524;
|
||||
}
|
||||
|
||||
.admin-glass-light {
|
||||
background: #ffffff;
|
||||
border: 1px solid #e7e5e4;
|
||||
color: #292524;
|
||||
transition: all 0.2s ease;
|
||||
box-shadow: 0 1px 2px rgba(0,0,0,0.05);
|
||||
}
|
||||
|
||||
.admin-glass-light:hover {
|
||||
background: #fdfcf8;
|
||||
border-color: #d6d3d1;
|
||||
box-shadow: 0 4px 6px rgba(0,0,0,0.05);
|
||||
}
|
||||
|
||||
.admin-glass-card {
|
||||
background: #ffffff;
|
||||
border: 1px solid #e7e5e4;
|
||||
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.05);
|
||||
color: #292524;
|
||||
}
|
||||
|
||||
@@ -276,7 +276,7 @@ const AdminPage = () => {
|
||||
>
|
||||
<div className="bg-white/80 backdrop-blur-xl rounded-3xl p-8 border border-stone-200 shadow-2xl relative z-10">
|
||||
<div className="text-center mb-8">
|
||||
<div className="w-16 h-16 bg-gradient-to-br from-stone-100 to-white rounded-2xl flex items-center justify-center mx-auto mb-6 shadow-sm border border-stone-100">
|
||||
<div className="w-16 h-16 bg-[#f3f1e7] rounded-2xl flex items-center justify-center mx-auto mb-6 shadow-sm border border-stone-100">
|
||||
<Lock className="w-6 h-6 text-stone-600" />
|
||||
</div>
|
||||
<h1 className="text-2xl font-bold text-stone-900 mb-2 tracking-tight">Admin Access</h1>
|
||||
|
||||
@@ -184,32 +184,34 @@ const ProjectsPage = () => {
|
||||
|
||||
{project.featured && (
|
||||
<div className="absolute top-3 left-3 z-20">
|
||||
<div className="px-3 py-1 bg-red-500 text-white text-[10px] font-black uppercase tracking-widest rounded-full shadow-lg border border-red-400/50">
|
||||
<div className="px-3 py-1 bg-[#292524]/80 backdrop-blur-md text-[#fdfcf8] text-[10px] font-bold uppercase tracking-widest rounded-full shadow-sm border border-white/10">
|
||||
Featured
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Overlay Links */}
|
||||
<div className="absolute inset-0 bg-stone-900/40 opacity-0 group-hover:opacity-100 transition-opacity duration-500 ease-out flex items-center justify-center gap-4 backdrop-blur-[2px] z-10">
|
||||
<div className="absolute inset-0 bg-stone-900/40 opacity-0 group-hover:opacity-100 transition-opacity duration-500 ease-out flex items-center justify-center gap-4 backdrop-blur-[2px] z-20 pointer-events-none">
|
||||
{project.github && (
|
||||
<a
|
||||
href={project.github}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="p-3 bg-white text-stone-900 rounded-full hover:scale-110 transition-all duration-300 shadow-xl border border-white/50"
|
||||
className="p-3 bg-white text-stone-900 rounded-full hover:scale-110 transition-all duration-300 shadow-xl border border-white/50 pointer-events-auto"
|
||||
aria-label="GitHub"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<Github size={20} />
|
||||
</a>
|
||||
)}
|
||||
{project.live && (
|
||||
{project.live && !project.title.toLowerCase().includes('kernel panic') && (
|
||||
<a
|
||||
href={project.live}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="p-3 bg-white text-stone-900 rounded-full hover:scale-110 transition-all duration-300 shadow-xl border border-white/50"
|
||||
className="p-3 bg-white text-stone-900 rounded-full hover:scale-110 transition-all duration-300 shadow-xl border border-white/50 pointer-events-auto"
|
||||
aria-label="Live Demo"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<ExternalLink size={20} />
|
||||
</a>
|
||||
@@ -218,6 +220,13 @@ const ProjectsPage = () => {
|
||||
</div>
|
||||
|
||||
<div className="p-6 flex flex-col flex-1">
|
||||
{/* Stretched Link covering the whole card (including image area) */}
|
||||
<Link
|
||||
href={`/projects/${project.title.toLowerCase().replace(/[^a-z0-9]+/g, '-')}`}
|
||||
className="absolute inset-0 z-10"
|
||||
aria-label={`View project ${project.title}`}
|
||||
/>
|
||||
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<h3 className="text-xl font-bold text-stone-900 group-hover:text-stone-600 transition-colors">
|
||||
{project.title}
|
||||
@@ -246,27 +255,31 @@ const ProjectsPage = () => {
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="mt-auto pt-4 border-t border-stone-100 flex items-center justify-between">
|
||||
<div className="mt-auto pt-4 border-t border-stone-100 flex items-center justify-between relative z-20">
|
||||
<div className="flex gap-3">
|
||||
{project.github && (
|
||||
<a href={project.github} target="_blank" rel="noopener noreferrer" className="text-stone-400 hover:text-stone-900 transition-colors">
|
||||
<a
|
||||
href={project.github}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-stone-400 hover:text-stone-900 transition-colors relative z-20 hover:scale-110"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<Github size={18} />
|
||||
</a>
|
||||
)}
|
||||
{project.live && (
|
||||
<a href={project.live} target="_blank" rel="noopener noreferrer" className="text-stone-400 hover:text-stone-900 transition-colors">
|
||||
{project.live && !project.title.toLowerCase().includes('kernel panic') && (
|
||||
<a
|
||||
href={project.live}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-stone-400 hover:text-stone-900 transition-colors relative z-20 hover:scale-110"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<ExternalLink size={18} />
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<Link
|
||||
href={`/projects/${project.title.toLowerCase().replace(/[^a-z0-9]+/g, '-')}`}
|
||||
className="inline-flex items-center space-x-1 text-sm font-bold text-stone-800 hover:gap-2 transition-all"
|
||||
>
|
||||
<span>Read More</span>
|
||||
<ArrowLeft size={16} className="rotate-180" />
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
Reference in New Issue
Block a user