Feat: frontend working almost

This commit is contained in:
2026-02-14 22:57:24 -07:00
parent ad375d78fd
commit f0eafcd865
16 changed files with 1664 additions and 670 deletions

View File

@@ -1,38 +1,42 @@
import { Play, Volume2, Heart, MessageCircle, Share2, Bookmark, MoreVertical } from 'lucide-react'
import { Play, Volume2, MoreVertical, Clock } from 'lucide-react'
export default function AudioPostCard({ post, onPlay }) {
const formatDate = (dateString) => {
const date = new Date(dateString)
const now = new Date()
const diffMs = now - date
const diffMins = Math.floor(diffMs / 60000)
if (diffMins < 60) return `${diffMins}m ago`
if (diffMins < 1440) return `${Math.floor(diffMins / 60)}h ago`
return `${Math.floor(diffMins / 1440)}d ago`
}
export default function AudioPostCard({ post, onLike, onComment, onShare, onBookmark }) {
return (
<article className="bg-white rounded-lg border border-gray-200 overflow-hidden shadow-sm hover:shadow-md transition-shadow">
{/* Post Header */}
<div className="p-6 pb-4">
<div className="flex items-start justify-between mb-4">
<div className="flex items-center gap-3">
<div
className="w-12 h-12 rounded-full flex items-center justify-center text-white font-semibold text-lg"
style={{ background: post.user.avatarColor }}
>
{post.user.initials}
<div className="flex-1">
<div className="flex items-center gap-2 mb-1">
<h3 className="text-lg font-semibold text-gray-900">{post.title}</h3>
{post.visibility === 'private' && (
<span className="text-xs px-2 py-0.5 bg-gray-100 text-gray-600 rounded">
Private
</span>
)}
</div>
<div>
<div className="flex items-center gap-2">
<span className="font-semibold text-gray-900">{post.user.name}</span>
<span className="text-gray-500 text-sm"> {post.timeAgo}</span>
</div>
<div className="flex items-center gap-2 mt-1">
{post.categories.map((category, index) => (
<span
key={index}
className={`text-xs px-2 py-1 rounded border ${
category.color === 'yellow' ? 'bg-[#f4b840]/10 text-[#f4b840] border-[#f4b840]/20' :
category.color === 'blue' ? 'bg-blue-500/10 text-blue-600 border-blue-500/20' :
category.color === 'purple' ? 'bg-purple-500/10 text-purple-600 border-purple-500/20' :
'bg-green-500/10 text-green-600 border-green-500/20'
}`}
>
{category.name}
</span>
))}
</div>
<div className="flex items-center gap-2 text-sm text-gray-500">
<Clock size={14} />
<span>{formatDate(post.created_at)}</span>
<span></span>
<span className={`px-2 py-0.5 rounded text-xs ${
post.status === 'ready' ? 'bg-green-100 text-green-700' :
post.status === 'processing' ? 'bg-yellow-100 text-yellow-700' :
'bg-red-100 text-red-700'
}`}>
{post.status}
</span>
</div>
</div>
<button className="text-gray-500 hover:text-gray-700">
@@ -40,74 +44,62 @@ export default function AudioPostCard({ post, onLike, onComment, onShare, onBook
</button>
</div>
<h3 className="text-lg font-semibold text-gray-900 mb-3">{post.title}</h3>
{/* Description */}
{post.description && (
<p className="text-sm text-gray-700 mb-4 line-clamp-2">
{post.description}
</p>
)}
{/* Audio Player */}
<div className="bg-gray-50 rounded-lg p-4 border border-gray-200">
<div className="flex items-center gap-4 mb-3">
<button className="w-10 h-10 bg-[#f4b840] hover:bg-[#e5a930] rounded-full flex items-center justify-center text-[#1a1a1a]">
<Play size={16} fill="currentColor" />
</button>
<div className="flex-1">
<div className="h-1.5 bg-gray-300 rounded-full overflow-hidden mb-2">
<div
className="h-full bg-[#f4b840] rounded-full"
style={{ width: `${post.audio.progress}%` }}
></div>
</div>
<div className="flex justify-between text-xs text-gray-600">
<span>{post.audio.currentTime}</span>
<span>{post.audio.duration}</span>
{/* Audio Player - Only show if ready */}
{post.status === 'ready' && (
<div className="bg-gray-50 rounded-lg p-4 border border-gray-200">
<div className="flex items-center gap-4">
<button
onClick={() => onPlay?.(post)}
className="w-10 h-10 bg-[#f4b840] hover:bg-[#e5a930] rounded-full flex items-center justify-center text-[#1a1a1a] transition-colors"
>
<Play size={16} fill="currentColor" />
</button>
<div className="flex-1">
<div className="h-1.5 bg-gray-300 rounded-full overflow-hidden mb-2">
<div className="h-full w-0 bg-[#f4b840] rounded-full"></div>
</div>
<div className="flex justify-between text-xs text-gray-600">
<span>0:00</span>
<span>--:--</span>
</div>
</div>
<button className="text-gray-600 hover:text-gray-900">
<Volume2 size={18} />
</button>
</div>
<button className="text-gray-600 hover:text-gray-900">
<Volume2 size={18} />
</button>
</div>
</div>
{/* Transcription Preview */}
{post.transcript && (
<div className="mt-4 p-4 bg-gray-50 rounded-lg border border-gray-200">
<p className="text-sm text-gray-700 leading-relaxed line-clamp-3">
{post.transcript}
</p>
<button className="text-xs text-[#f4b840] hover:text-[#e5a930] mt-2 font-medium">
Read full transcript
</button>
</div>
)}
</div>
{/* Post Actions */}
<div className="px-6 py-4 border-t border-gray-200 flex items-center gap-6">
<button
onClick={() => onLike?.(post.id)}
className="flex items-center gap-2 text-gray-600 hover:text-gray-900 text-sm"
>
<Heart size={18} />
<span>{post.likes}</span>
</button>
<button
onClick={() => onComment?.(post.id)}
className="flex items-center gap-2 text-gray-600 hover:text-gray-900 text-sm"
>
<MessageCircle size={18} />
<span>{post.comments}</span>
</button>
<button
onClick={() => onShare?.(post.id)}
className="flex items-center gap-2 text-gray-600 hover:text-gray-900 text-sm"
>
<Share2 size={18} />
<span>Share</span>
</button>
<button
onClick={() => onBookmark?.(post.id)}
className="ml-auto text-gray-600 hover:text-gray-900"
>
<Bookmark size={18} />
</button>
{/* Processing Status */}
{post.status === 'processing' && (
<div className="bg-yellow-50 rounded-lg p-4 border border-yellow-200 text-center">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-[#f4b840] mx-auto mb-2"></div>
<p className="text-sm text-yellow-800">Processing audio and generating transcript...</p>
</div>
)}
{/* Failed Status */}
{post.status === 'failed' && (
<div className="bg-red-50 rounded-lg p-4 border border-red-200">
<p className="text-sm text-red-800">Failed to process this recording. Please try uploading again.</p>
</div>
)}
{/* Language Tag */}
{post.language && (
<div className="mt-3">
<span className="text-xs px-2 py-1 bg-blue-100 text-blue-700 rounded">
{post.language.toUpperCase()}
</span>
</div>
)}
</div>
</article>
)