Compare commits

...

8 commits

Author SHA1 Message Date
Ategon
469c16fac6 Force white text 2025-01-18 13:49:54 -05:00
Ategon
3cc2d95007 Add post styles 2025-01-18 13:47:40 -05:00
Ategon
e5aa7c2a2a Add post sorts 2025-01-18 13:31:28 -05:00
Ategon
32705c8662 Set likes to number 2025-01-18 12:52:16 -05:00
Ategon
bc7ed52662 Fix PostType 2025-01-18 12:50:39 -05:00
Ategon
7730f17490 Fix linting 2025-01-18 12:48:38 -05:00
Ategon
a258772f7e Merge branch 'main' of https://github.com/Ategon/Jamjar 2025-01-18 12:44:42 -05:00
Ategon
54e5cb6495 Add likes 2025-01-18 12:41:30 -05:00
10 changed files with 246 additions and 49 deletions

View file

@ -1,6 +1,6 @@
"use client";
import { Button, Form, Input } from "@nextui-org/react";
import { Button, Form, Input, Link } from "@nextui-org/react";
import { redirect } from "next/navigation";
import { useState } from "react";
import { toast } from "react-toastify";
@ -105,6 +105,9 @@ export default function UserPage() {
Reset
</Button>
</div>
<p>
Don&apos;t have an account? <Link href="/signup">Sign up</Link>
</p>
</Form>
</div>
);

View file

@ -1,6 +1,6 @@
"use client";
import { Button, Form, Input } from "@nextui-org/react";
import { Button, Form, Input, Link } from "@nextui-org/react";
import { useState } from "react";
export default function UserPage() {
@ -129,6 +129,9 @@ export default function UserPage() {
Reset
</Button>
</div>
<p>
Already have an account? <Link href="/login">Log In</Link>
</p>
</Form>
</div>
);

View file

@ -2,7 +2,7 @@ import { Calendar } from "lucide-react";
export default function JamHeader() {
return (
<div className="bg-[#124a88] flex rounded-2xl overflow-hidden">
<div className="bg-[#124a88] flex rounded-2xl overflow-hidden text-white">
<div className="bg-[#1892b3] p-4 px-6 flex items-center gap-2 font-bold">
<Calendar />
<p>Dare2Jam 1</p>

View file

@ -0,0 +1,52 @@
"use client";
import { Button } from "@nextui-org/react";
import { PostType } from "@/types/PostType";
import { Heart } from "lucide-react";
import { toast } from "react-toastify";
import { getCookie } from "@/helpers/cookie";
import { redirect } from "next/navigation";
import { useState } from "react";
export default function LikeButton({ post }: { post: PostType }) {
const [likes, setLikes] = useState<number>(post.likes.length);
return (
<Button
size="sm"
variant="bordered"
onPress={async () => {
const response = await fetch(
process.env.NEXT_PUBLIC_MODE === "PROD"
? "https://d2jam.com/api/v1/like"
: "http://localhost:3005/api/v1/like",
{
method: "POST",
headers: {
"Content-Type": "application/json",
authorization: `Bearer ${getCookie("token")}`,
},
credentials: "include",
body: JSON.stringify({
username: getCookie("user"),
postId: post.id,
}),
}
);
if (!response.ok) {
if (response.status == 401) {
redirect("/login");
} else {
toast.error("An error occured");
return;
}
} else {
setLikes(parseInt(await response.text()));
}
}}
>
<Heart size={16} /> {likes}
</Button>
);
}

View file

@ -2,48 +2,146 @@ import { Avatar, Button, Card, CardBody, Spacer } from "@nextui-org/react";
import { formatDistance } from "date-fns";
import Link from "next/link";
import { PostType } from "@/types/PostType";
import { Heart, MessageCircle } from "lucide-react";
import { MessageCircle } from "lucide-react";
import LikeButton from "./LikeButton";
import { PostStyle } from "@/types/PostStyle";
export default function PostCard({ post }: { post: PostType }) {
export default function PostCard({
post,
style,
}: {
post: PostType;
style: PostStyle;
}) {
return (
<Card className="bg-opacity-60">
<CardBody className="p-5">
<p className="text-2xl">{post.title}</p>
{style == "cozy" && (
<div>
<p className="text-2xl">{post.title}</p>
<div className="flex items-center gap-3 text-xs text-default-500 pt-1">
<p>By</p>
<Link
href={`/u/${post.author.slug}`}
className="flex items-center gap-2"
>
<Avatar
size="sm"
className="w-6 h-6"
src={post.author.profilePicture}
/>
<p>{post.author.name}</p>
</Link>
<p>
{formatDistance(new Date(post.createdAt), new Date(), {
addSuffix: true,
})}
</p>
</div>
<div className="flex items-center gap-3 text-xs text-default-500 pt-1">
<p>By</p>
<Link
href={`/u/${post.author.slug}`}
className="flex items-center gap-2"
>
<Avatar
size="sm"
className="w-6 h-6"
src={post.author.profilePicture}
/>
<p>{post.author.name}</p>
</Link>
<p>
{formatDistance(new Date(post.createdAt), new Date(), {
addSuffix: true,
})}
</p>
</div>
<Spacer y={4} />
<Spacer y={4} />
<p>{post.content}</p>
<p>{post.content}</p>
<Spacer y={4} />
<Spacer y={4} />
<div className="flex gap-3">
<Button size="sm" variant="bordered">
<Heart size={16} /> {post.likers.length}
</Button>
<Button size="sm" variant="bordered">
<MessageCircle size={16} /> {0}
</Button>
</div>
<div className="flex gap-3">
<LikeButton post={post} />
<Button size="sm" variant="bordered">
<MessageCircle size={16} /> {0}
</Button>
</div>
</div>
)}
{style == "compact" && (
<div>
<p className="text-2xl">{post.title}</p>
<div className="flex items-center gap-3 text-xs text-default-500 pt-1">
<p>By</p>
<Link
href={`/u/${post.author.slug}`}
className="flex items-center gap-2"
>
<Avatar
size="sm"
className="w-6 h-6"
src={post.author.profilePicture}
/>
<p>{post.author.name}</p>
</Link>
<p>
{formatDistance(new Date(post.createdAt), new Date(), {
addSuffix: true,
})}
</p>
</div>
</div>
)}
{style == "ultra" && (
<div className="flex items-center gap-4">
<p>{post.title}</p>
<div className="flex items-center gap-3 text-xs text-default-500 pt-1">
<p>By</p>
<Link
href={`/u/${post.author.slug}`}
className="flex items-center gap-2"
>
<Avatar
size="sm"
className="w-6 h-6"
src={post.author.profilePicture}
/>
<p>{post.author.name}</p>
</Link>
<p>
{formatDistance(new Date(post.createdAt), new Date(), {
addSuffix: true,
})}
</p>
</div>
</div>
)}
{style == "adaptive" && (
<div>
<p className="text-2xl">{post.title}</p>
<div className="flex items-center gap-3 text-xs text-default-500 pt-1">
<p>By</p>
<Link
href={`/u/${post.author.slug}`}
className="flex items-center gap-2"
>
<Avatar
size="sm"
className="w-6 h-6"
src={post.author.profilePicture}
/>
<p>{post.author.name}</p>
</Link>
<p>
{formatDistance(new Date(post.createdAt), new Date(), {
addSuffix: true,
})}
</p>
</div>
<Spacer y={4} />
<p>{post.content}</p>
<Spacer y={4} />
<div className="flex gap-3">
<LikeButton post={post} />
<Button size="sm" variant="bordered">
<MessageCircle size={16} /> {0}
</Button>
</div>
</div>
)}
</CardBody>
</Card>
);

View file

@ -3,46 +3,85 @@
import { useEffect, useState } from "react";
import PostCard from "./PostCard";
import { PostType } from "@/types/PostType";
import { Button } from "@nextui-org/react";
import {
Button,
Dropdown,
DropdownItem,
DropdownMenu,
DropdownTrigger,
} from "@nextui-org/react";
import { PostSort } from "@/types/PostSort";
import { PostStyle } from "@/types/PostStyle";
export default function Posts() {
const [posts, setPosts] = useState<PostType[]>();
const [sort, setSort] = useState<PostSort>("newest");
const [style, setStyle] = useState<PostStyle>("cozy");
useEffect(() => {
const fetchPosts = async () => {
const response = await fetch(
process.env.NEXT_PUBLIC_MODE === "PROD"
? "https://d2jam.com/api/v1/posts"
: "http://localhost:3005/api/v1/posts"
? `https://d2jam.com/api/v1/posts?sort=${sort}`
: `http://localhost:3005/api/v1/posts?sort=${sort}`
);
setPosts(await response.json());
};
fetchPosts();
}, []);
}, [sort]);
return (
<div>
<div className="flex justify-between p-4 pb-0">
<div className="flex gap-2">
<Button size="sm" className="text-xs" variant="faded">
Newest
</Button>
<Dropdown>
<DropdownTrigger>
<Button size="sm" className="text-xs" variant="faded">
{sort.charAt(0).toUpperCase() + sort.slice(1)}
</Button>
</DropdownTrigger>
<DropdownMenu
onAction={(key) => {
setSort(key as PostSort);
}}
className="text-black"
>
<DropdownItem key="newest">Newest</DropdownItem>
<DropdownItem key="top">Top</DropdownItem>
<DropdownItem key="oldest">Oldest</DropdownItem>
</DropdownMenu>
</Dropdown>
<Button size="sm" className="text-xs" variant="faded">
All Tags
</Button>
</div>
<div>
<Button size="sm" className="text-xs" variant="faded">
Cozy
</Button>
<Dropdown>
<DropdownTrigger>
<Button size="sm" className="text-xs" variant="faded">
{style.charAt(0).toUpperCase() + style.slice(1)}
</Button>
</DropdownTrigger>
<DropdownMenu
onAction={(key) => {
setStyle(key as PostStyle);
}}
className="text-black"
>
<DropdownItem key="cozy">Cozy</DropdownItem>
<DropdownItem key="compact">Compact</DropdownItem>
<DropdownItem key="ultra">Ultra Compact</DropdownItem>
<DropdownItem key="adaptive">Adaptive</DropdownItem>
</DropdownMenu>
</Dropdown>
</div>
</div>
<div className="flex flex-col gap-3 p-4">
{posts &&
posts.map((post) => (
<div key={post.id}>
<PostCard post={post} />
<PostCard post={post} style={style} />
</div>
))}
</div>

View file

@ -3,7 +3,7 @@ import Timer from "./Timer";
export default function Timers() {
return (
<div>
<div className="text-white">
<Timer
name="Jam Start"
targetDate={new Date("2025-04-04T18:00:00-05:00")}

1
src/types/PostSort.ts Normal file
View file

@ -0,0 +1 @@
export type PostSort = "newest" | "oldest";

1
src/types/PostStyle.ts Normal file
View file

@ -0,0 +1 @@
export type PostStyle = "cozy" | "compact" | "ultra" | "adaptive";

View file

@ -8,5 +8,5 @@ export interface PostType {
name: string;
};
createdAt: Date;
likers: [];
likes: [];
}