Skip to content

Commit

Permalink
WIP: Copied over components from v0.dev
Browse files Browse the repository at this point in the history
  • Loading branch information
Rajat Saxena committed Nov 24, 2024
1 parent e2d1711 commit 3a8f727
Show file tree
Hide file tree
Showing 23 changed files with 2,022 additions and 2 deletions.
Binary file not shown.
Binary file not shown.
25 changes: 25 additions & 0 deletions apps/web/app/api/gifs/search/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { type NextRequest } from "next/server";

const GIPHY_API_KEY = process.env.GIPHY_API_KEY;

export async function GET(req: NextRequest) {
const searchParams = req.nextUrl.searchParams;
const q = searchParams.get("q");

if (!q) {
return Response.json(
{ error: "Query parameter is required" },
{ status: 400 },
);
}

try {
const response = await fetch(
`https://api.giphy.com/v1/gifs/search?api_key=${GIPHY_API_KEY}&q=${encodeURIComponent(q as string)}&limit=20`,
);
const data = await response.json();
return Response.json(data);
} catch (error) {
return Response.json({ error: "Error fetching GIFs" }, { status: 500 });
}
}
13 changes: 13 additions & 0 deletions apps/web/app/api/gifs/trending/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const GIPHY_API_KEY = process.env.GIPHY_API_KEY;

export async function GET() {
try {
const response = await fetch(
`https://api.giphy.com/v1/gifs/trending?api_key=${GIPHY_API_KEY}&limit=20`,
);
const data = await response.json();
return Response.json(data);
} catch (error) {
return Response.json({ error: "Error fetching GIFs" }, { status: 500 });
}
}
16 changes: 16 additions & 0 deletions apps/web/app/dashboard4/(sidebar)/community/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { COMMUNITY_HEADER } from "@ui-config/strings";
import type { Metadata, ResolvingMetadata } from "next";
import { ReactNode } from "react";

export async function generateMetadata(
_: any,
parent: ResolvingMetadata,
): Promise<Metadata> {
return {
title: `${COMMUNITY_HEADER} | ${(await parent)?.title?.absolute}`,
};
}

export default function Layout({ children }: { children: ReactNode }) {
return children;
}
19 changes: 19 additions & 0 deletions apps/web/app/dashboard4/(sidebar)/community/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
"use client";

import DashboardContent from "@components/admin/dashboard-content";
import { CommunityForum } from "@components/community";
import { COMMUNITY_HEADER } from "@ui-config/strings";
import { useSearchParams } from "next/navigation";

const breadcrumbs = [{ label: COMMUNITY_HEADER, href: "#" }];

export default function Page() {
const searchParams = useSearchParams();
const category = searchParams?.get("category") || "All";

return (
<DashboardContent breadcrumbs={breadcrumbs}>
<CommunityForum activeCategory={category} />
</DashboardContent>
);
}
103 changes: 103 additions & 0 deletions apps/web/components/community/comment.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import { useState } from "react";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { Button } from "@/components/ui/button";
import { Textarea } from "@/components/ui/textarea";
import { ThumbsUp, MessageSquare } from "lucide-react";

interface CommentProps {
comment: Comment;
onLike: (commentId: number) => void;
onReply: (commentId: number, content: string) => void;
depth?: number;
}

export function Comment({ comment, onLike, onReply, depth = 0 }: CommentProps) {
const [isReplying, setIsReplying] = useState(false);
const [replyContent, setReplyContent] = useState("");

const handleReply = () => {
if (replyContent.trim()) {
onReply(comment.id, replyContent);
setReplyContent("");
setIsReplying(false);
}
};

return (
<div className={`space-y-2 ${depth > 0 ? "ml-6" : ""}`}>
<div className="flex items-start gap-2">
<Avatar className="h-8 w-8">
<AvatarImage
src={comment.avatar}
alt={`${comment.author}'s avatar`}
/>
<AvatarFallback>
{comment.author
.split(" ")
.map((n) => n[0])
.join("")}
</AvatarFallback>
</Avatar>
<div className="flex-1">
<div className="flex items-center gap-2">
<span className="font-semibold">{comment.author}</span>
<span className="text-sm text-muted-foreground">
{comment.time}
</span>
</div>
<p className="text-sm mt-1">{comment.content}</p>
<div className="flex items-center gap-4 mt-2">
<Button
variant="ghost"
size="sm"
className={`text-muted-foreground ${comment.hasLiked ? "bg-accent" : ""}`}
onClick={() => onLike(comment.id)}
>
<ThumbsUp className="h-4 w-4 mr-2" />
{comment.likes}
</Button>
<Button
variant="ghost"
size="sm"
className="text-muted-foreground"
onClick={() => setIsReplying(!isReplying)}
>
<MessageSquare className="h-4 w-4 mr-2" />
Reply
</Button>
</div>
</div>
</div>
{isReplying && (
<div className="mt-2 space-y-2">
<Textarea
placeholder="Write a reply..."
value={replyContent}
onChange={(e) => setReplyContent(e.target.value)}
/>
<div className="flex justify-end gap-2">
<Button
variant="outline"
size="sm"
onClick={() => setIsReplying(false)}
>
Cancel
</Button>
<Button size="sm" onClick={handleReply}>
Reply
</Button>
</div>
</div>
)}
{comment.replies.map((reply) => (
<Comment
key={reply.id}
comment={reply}
onLike={onLike}
onReply={onReply}
depth={depth + 1}
/>
))}
</div>
);
}
Loading

0 comments on commit 3a8f727

Please sign in to comment.