Files
mstfyldz/components/JournalCard.tsx
T
2026-08-16 17:18:23 +03:00

451 lines
16 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
import React, { useState } from "react";
import { JournalPost } from "./types";
import {
Heart,
MessageSquare,
Tag,
Play,
Pause,
Maximize2,
Terminal,
Volume2,
CornerDownRight,
Check,
Sparkles
} from "lucide-react";
interface JournalCardProps {
post: JournalPost;
onOpenDetail: (post: JournalPost) => void;
onTagClick: (tag: string) => void;
}
export const JournalCard: React.FC<JournalCardProps> = ({
post,
onOpenDetail,
onTagClick,
}) => {
const [likes, setLikes] = useState(post.likes);
const [hasLiked, setHasLiked] = useState(false);
const [isPlayingAudio, setIsPlayingAudio] = useState(false);
const [stampFeedback, setStampFeedback] = useState(false);
const handleLike = (e: React.MouseEvent) => {
e.stopPropagation();
if (!hasLiked) {
setLikes((prev) => prev + 1);
setHasLiked(true);
setStampFeedback(true);
setTimeout(() => setStampFeedback(false), 1200);
} else {
setLikes((prev) => prev - 1);
setHasLiked(false);
}
};
const toggleAudio = (e: React.MouseEvent) => {
e.stopPropagation();
setIsPlayingAudio(!isPlayingAudio);
};
// Render format based on type
if (post.type === "sticky") {
return (
<div
onClick={() => onOpenDetail(post)}
className="group relative bg-[#FEF3C7] border border-[#D97706]/20 p-5 sm:p-6 rounded-lg shadow-sm hover:shadow-md transition-all duration-300 transform hover:-translate-y-1 rotate-[-1deg] cursor-pointer flex flex-col justify-between"
>
{/* Washi Tape across top */}
<div className="washi-tape washi-tape-amber top-[-10px] left-1/2 -translate-x-1/2 w-28" />
<div>
{/* Header */}
<div className="flex items-center justify-between text-xs font-mono text-[#D97706] mb-3 pt-2">
<span className="font-bold">{post.date} {post.timestamp}</span>
{post.stampedText && (
<span className="rubber-stamp text-[9px] text-[#B45309] border-[#B45309] rotate-[3deg]">
{post.stampedText}
</span>
)}
</div>
{/* Content */}
<p className="font-serif text-[#18181B] text-base sm:text-lg leading-snug font-medium mb-4">
&ldquo;{post.content}&rdquo;
</p>
{/* Margin Notes */}
{post.marginNotes && post.marginNotes.length > 0 && (
<div className="my-2 p-2 rounded bg-[#FDE68A]/40 border border-[#D97706]/20">
{post.marginNotes.map((note, idx) => (
<div key={idx} className="font-handwritten text-[#92400E] text-base leading-tight">
{note}
</div>
))}
</div>
)}
</div>
{/* Footer */}
<div className="flex items-center justify-between pt-3 border-t border-[#D97706]/15 mt-3">
<div className="flex items-center gap-1.5 flex-wrap">
{post.tags.map((tag) => (
<button
key={tag}
onClick={(e) => {
e.stopPropagation();
onTagClick(tag);
}}
className="text-[11px] font-mono text-[#92400E] hover:underline"
>
#{tag}
</button>
))}
</div>
<button
onClick={handleLike}
className={`flex items-center gap-1 text-xs font-mono px-2 py-1 rounded transition-colors ${
hasLiked ? "text-[#E11D48] font-bold" : "text-[#B45309] hover:text-[#18181B]"
}`}
>
<Heart className={`w-3.5 h-3.5 ${hasLiked ? "fill-current" : ""}`} />
<span>{likes}</span>
</button>
</div>
</div>
);
}
if (post.type === "polaroid") {
return (
<div
onClick={() => onOpenDetail(post)}
className="group relative bg-[#FAF7F2] border border-[#18181B]/12 p-3 sm:p-4 rounded-sm shadow-sm hover:shadow-lg transition-all duration-300 transform hover:rotate-[0.5deg] rotate-[1deg] cursor-pointer"
>
{/* Washi Tape */}
<div className="washi-tape top-[-10px] left-1/2 -translate-x-1/2 w-32" />
{/* Image Container */}
<div className="w-full aspect-[4/3] rounded-xs overflow-hidden bg-[#18181B]/5 relative mb-3">
{post.imageUrl && (
<img
src={post.imageUrl}
alt={post.imageCaption || "Polaroid memory"}
className="w-full h-full object-cover group-hover:scale-102 transition-transform duration-500"
/>
)}
<div className="absolute bottom-2 right-2 px-2 py-0.5 rounded bg-black/60 backdrop-blur-xs text-white text-[10px] font-mono">
{post.date}
</div>
</div>
{/* Polaroid Handwritten Caption */}
<div className="px-1 pb-1">
<div className="font-handwritten text-[#18181B] text-xl leading-snug font-bold">
{post.imageCaption || post.content}
</div>
<p className="text-xs font-serif text-[#52525B] mt-1.5 line-clamp-2">
{post.content}
</p>
{/* Margin Note */}
{post.marginNotes && post.marginNotes.length > 0 && (
<div className="font-handwritten text-[#0284C7] text-base mt-2 flex items-center gap-1">
<CornerDownRight className="w-3.5 h-3.5 shrink-0" />
<span>{post.marginNotes[0]}</span>
</div>
)}
{/* Footer */}
<div className="flex items-center justify-between pt-2.5 border-t border-[#18181B]/8 mt-3">
<div className="flex items-center gap-1.5">
{post.tags.map((tag) => (
<button
key={tag}
onClick={(e) => {
e.stopPropagation();
onTagClick(tag);
}}
className="text-[10px] font-mono text-[#787D89] hover:text-[#18181B]"
>
#{tag}
</button>
))}
</div>
<button
onClick={handleLike}
className={`flex items-center gap-1 text-xs font-mono transition-colors ${
hasLiked ? "text-[#E11D48] font-bold" : "text-[#787D89] hover:text-[#18181B]"
}`}
>
<Heart className={`w-3.5 h-3.5 ${hasLiked ? "fill-current" : ""}`} />
<span>{likes}</span>
</button>
</div>
</div>
</div>
);
}
if (post.type === "code") {
return (
<div
onClick={() => onOpenDetail(post)}
className="group relative bg-[#1E232D] text-[#FAF7F2] p-5 sm:p-6 rounded-xl shadow-md hover:shadow-xl transition-all duration-300 border border-white/10 cursor-pointer"
>
{/* Terminal Header */}
<div className="flex items-center justify-between pb-3 border-b border-white/10 mb-3">
<div className="flex items-center gap-2">
<div className="flex items-center gap-1.5">
<div className="w-2.5 h-2.5 rounded-full bg-[#EF4444]" />
<div className="w-2.5 h-2.5 rounded-full bg-[#F59E0B]" />
<div className="w-2.5 h-2.5 rounded-full bg-[#10B981]" />
</div>
<span className="text-xs font-mono text-white/60 ml-2">
{post.title || "terminal_scrap.rs"}
</span>
</div>
<span className="text-[10px] font-mono uppercase px-2 py-0.5 rounded bg-white/10 text-white/80">
{post.codeLanguage || "code"}
</span>
</div>
{/* Snippet */}
{post.codeSnippet && (
<pre className="p-3.5 rounded-lg bg-black/40 text-emerald-400 font-mono text-xs overflow-x-auto leading-relaxed border border-white/5 my-2">
<code>{post.codeSnippet}</code>
</pre>
)}
{/* Narrative */}
<p className="text-xs sm:text-sm font-serif text-white/80 mt-3 leading-relaxed">
{post.content}
</p>
{/* Handwritten Margin Note on Dark Card */}
{post.marginNotes && post.marginNotes.length > 0 && (
<div className="font-handwritten text-[#FDE047] text-base mt-2 flex items-center gap-1.5">
<span>&gt;&gt;</span>
<span>{post.marginNotes.join(" • ")}</span>
</div>
)}
{/* Footer */}
<div className="flex items-center justify-between pt-3 border-t border-white/10 mt-4">
<div className="flex items-center gap-2">
{post.tags.map((tag) => (
<button
key={tag}
onClick={(e) => {
e.stopPropagation();
onTagClick(tag);
}}
className="text-[10px] font-mono text-white/60 hover:text-white"
>
#{tag}
</button>
))}
</div>
<button
onClick={handleLike}
className={`flex items-center gap-1 text-xs font-mono ${
hasLiked ? "text-[#E11D48] font-bold" : "text-white/60 hover:text-white"
}`}
>
<Heart className={`w-3.5 h-3.5 ${hasLiked ? "fill-current" : ""}`} />
<span>{likes}</span>
</button>
</div>
</div>
);
}
if (post.type === "audio") {
return (
<div
onClick={() => onOpenDetail(post)}
className="group relative bg-[#F3ECE2] border border-[#18181B]/12 p-5 sm:p-6 rounded-xl shadow-sm hover:shadow-md transition-all duration-300 cursor-pointer"
>
{/* Brass clip top right */}
<div className="binder-clip absolute top-[-6px] right-6 w-8 h-4 rounded-xs" />
{/* Header */}
<div className="flex items-center justify-between text-xs font-mono text-[#52525B] mb-2">
<span className="font-bold flex items-center gap-1.5">
<Volume2 className="w-3.5 h-3.5 text-[#0284C7]" />
SES KAYDI {post.date}
</span>
<span className="text-[11px] font-mono">{post.audioDuration || "01:00"}</span>
</div>
{/* Cassette Tape Mockup */}
<div className="my-3 p-3.5 rounded-lg bg-[#18181B] text-[#FAF7F2] flex items-center gap-3">
<button
onClick={toggleAudio}
title={isPlayingAudio ? "Durdur" : "Dinle"}
aria-label={isPlayingAudio ? "Durdur" : "Dinle"}
className="w-10 h-10 rounded-full bg-[#0284C7] hover:bg-[#0369A1] text-white flex items-center justify-center transition-transform active:scale-95 shrink-0"
>
{isPlayingAudio ? <Pause className="w-4 h-4" /> : <Play className="w-4 h-4 ml-0.5" />}
</button>
<div className="flex-1 overflow-hidden">
<div className="text-xs font-mono font-bold truncate text-white">
{post.audioTitle || "voice_note.m4a"}
</div>
{/* Animated Audio Bars */}
<div className="flex items-end gap-1 h-5 mt-1.5">
{[40, 75, 30, 90, 60, 85, 45, 95, 35, 70, 50, 80, 65, 40].map((h, i) => (
<div
key={i}
className={`w-1 rounded-full transition-all duration-150 ${
isPlayingAudio ? "bg-[#38BDF8] animate-pulse" : "bg-white/30"
}`}
style={{
height: isPlayingAudio ? `${Math.max(20, (h * (i % 2 === 0 ? 1.2 : 0.8)) % 100)}%` : `${h * 0.4}%`,
}}
/>
))}
</div>
</div>
</div>
<p className="text-xs sm:text-sm font-serif text-[#18181B] mt-2 leading-relaxed">
{post.content}
</p>
{post.marginNotes && post.marginNotes.length > 0 && (
<div className="font-handwritten text-[#0284C7] text-base mt-2">
* {post.marginNotes[0]}
</div>
)}
{/* Footer */}
<div className="flex items-center justify-between pt-3 border-t border-[#18181B]/8 mt-3">
<div className="flex items-center gap-1.5">
{post.tags.map((tag) => (
<button
key={tag}
onClick={(e) => {
e.stopPropagation();
onTagClick(tag);
}}
className="text-[10px] font-mono text-[#787D89] hover:text-[#18181B]"
>
#{tag}
</button>
))}
</div>
<button
onClick={handleLike}
className={`flex items-center gap-1 text-xs font-mono ${
hasLiked ? "text-[#E11D48] font-bold" : "text-[#787D89] hover:text-[#18181B]"
}`}
>
<Heart className={`w-3.5 h-3.5 ${hasLiked ? "fill-current" : ""}`} />
<span>{likes}</span>
</button>
</div>
</div>
);
}
// Default: Field Notebook Page (Longform / Deep Note)
return (
<div
onClick={() => onOpenDetail(post)}
className="group relative notebook-sheet rounded-xl p-5 sm:p-7 shadow-sm hover:shadow-lg transition-all duration-300 border border-[#18181B]/10 cursor-pointer"
>
{/* Top Binder Clip */}
<div className="binder-clip absolute top-[-7px] left-8 w-10 h-4.5 rounded-xs" />
{/* Header with stamped date and mood badge */}
<div className="flex items-start justify-between gap-2 pb-3 border-b border-[#18181B]/8 mb-4">
<div>
<div className="flex items-center gap-2">
<span className="font-mono text-xs font-bold text-[#18181B]">
{post.date} {post.timestamp}
</span>
<span className="text-[10px] font-mono px-2 py-0.5 rounded-full bg-[#18181B]/5 text-[#52525B]">
{post.moodLabel}
</span>
</div>
{post.title && (
<h3 className="font-heading font-extrabold text-lg sm:text-xl text-[#18181B] mt-1.5 group-hover:text-[#E11D48] transition-colors leading-snug">
{post.title}
</h3>
)}
</div>
{post.stampedText && (
<span className="rubber-stamp text-[10px] text-[#059669] border-[#059669] rotate-[-2deg] shrink-0">
{post.stampedText}
</span>
)}
</div>
{/* Body with Highlighter markers */}
<div className="font-serif text-[#18181B] text-sm sm:text-base leading-relaxed space-y-2">
<p className="line-clamp-4 whitespace-pre-line">
{post.content}
</p>
</div>
{/* Marginalia & Handwritten Comments */}
{post.marginNotes && post.marginNotes.length > 0 && (
<div className="my-3.5 p-3 rounded-lg bg-[#FAF7F2] border border-[#18181B]/8 space-y-1">
<div className="text-[10px] font-mono uppercase text-[#787D89] font-bold">Kenar Notları:</div>
{post.marginNotes.map((note, idx) => (
<div key={idx} className="font-handwritten text-[#E11D48] text-base leading-tight flex items-center gap-1.5">
<span></span>
<span>{note}</span>
</div>
))}
</div>
)}
{/* Footer */}
<div className="flex items-center justify-between pt-3 border-t border-[#18181B]/8 mt-4">
<div className="flex items-center gap-1.5 flex-wrap">
{post.tags.map((tag) => (
<button
key={tag}
onClick={(e) => {
e.stopPropagation();
onTagClick(tag);
}}
className="text-[11px] font-mono px-2 py-0.5 rounded bg-[#F3ECE2] text-[#52525B] hover:bg-[#EFE7D6] hover:text-[#18181B] transition-colors"
>
#{tag}
</button>
))}
</div>
<div className="flex items-center gap-3">
<button
onClick={handleLike}
className={`flex items-center gap-1 text-xs font-mono transition-colors ${
hasLiked ? "text-[#E11D48] font-bold" : "text-[#787D89] hover:text-[#18181B]"
}`}
>
<Heart className={`w-3.5 h-3.5 ${hasLiked ? "fill-current" : ""}`} />
<span>{likes}</span>
</button>
<span className="text-xs font-heading font-semibold text-[#18181B] flex items-center gap-1 group-hover:translate-x-0.5 transition-transform">
<span>Oku</span>
<Maximize2 className="w-3 h-3 text-[#787D89]" />
</span>
</div>
</div>
</div>
);
};