Skip to content
19 changes: 19 additions & 0 deletions app/[orgId]/contests/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,25 @@ export default function ContestDetailsPage() {
Problems will be available when the contest starts.
</div>
)}

{shouldShowProblems() ? (
<div>
<h3 className="text-lg font-semibold mb-2 flex items-center">
<Link href={`/${orgId}/contests/${contestId}/submissions`}>
<Button
variant="link"
className="p-0 h-auto text-primary hover:text-primary/80"
>
View Submissions
</Button>
</Link>
</h3>
</div>
) : (
<div className="text-muted-foreground italic">
Submissions will be available when the contest starts.
</div>
)}
</CardContent>
</Card>
</div>
Expand Down
Empty file.
12 changes: 12 additions & 0 deletions app/[orgId]/contests/[id]/submissions/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import SubmissionsPage from "@/app/[orgId]/submissions/page";

export default function SubmissionsPageWrapper({
params,
}: {
params: {
orgId: string;
id: string;
};
}) {
return <SubmissionsPage params={params} />;
}
13 changes: 13 additions & 0 deletions app/[orgId]/groups/mockData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export interface Group {
about?: string;
avatar?: string;
users: string; // user emails seperated by newline
userEmails: string[];
usersCount?: number;
}

Expand All @@ -19,6 +20,11 @@ export const mockGroups: Group[] = [
avatar: "https://api.dicebear.com/7.x/initials/svg?seed=ET",
users:
"[email protected]\[email protected]\[email protected]",
userEmails: [
"[email protected]",
"[email protected]",
"[email protected]",
],
},
{
id: 2,
Expand All @@ -28,6 +34,7 @@ export const mockGroups: Group[] = [
about: "Product design and UX team",
avatar: "https://api.dicebear.com/7.x/initials/svg?seed=DT",
users: "[email protected]\[email protected]",
userEmails: ["[email protected]", "[email protected]"],
},
{
id: 3,
Expand All @@ -38,6 +45,11 @@ export const mockGroups: Group[] = [
avatar: "https://api.dicebear.com/7.x/initials/svg?seed=MT",
users:
"[email protected]\[email protected]\[email protected]",
userEmails: [
"[email protected]",
"[email protected]",
"[email protected]",
],
},
{
id: 4,
Expand All @@ -47,5 +59,6 @@ export const mockGroups: Group[] = [
about: "Product management and strategy",
avatar: "https://api.dicebear.com/7.x/initials/svg?seed=PT",
users: "[email protected]\[email protected]",
userEmails: ["[email protected]", "[email protected]"],
},
];
11 changes: 8 additions & 3 deletions app/[orgId]/groups/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,10 @@ const groupSchema = z.object({
});

const injectUsersCount = (groups: Group[]) => {
console.log("injecting users count", groups);
return groups.map((group) => ({
...group,
usersCount: group.users?.split(/\r?\n/).length ?? 0,
usersCount: group.userEmails.length ?? 0,
}));
};

Expand Down Expand Up @@ -80,7 +81,9 @@ export default function GroupsPage() {
throw new Error(formatValidationErrors(errorData));
}
const data = await response.json();
setGroups(injectUsersCount(data));
const updatedData = injectUsersCount(data);
console.log("updatedData", updatedData);
setGroups(updatedData);
setShowMockAlert(false);
} catch (error) {
console.error("Error fetching groups:", error);
Expand Down Expand Up @@ -137,10 +140,12 @@ export default function GroupsPage() {

// Convert users from string to array of strings (splitting at newlines)
if (typeof groupToSave.users === "string") {
groupToSave.users = groupToSave.users
groupToSave.emails = groupToSave.users
.split("\n")
.map((user) => user.trim())
.filter((user) => user.length > 0); // Remove empty lines
// groupToSave.users = null;
console.log("groupToSave.users", groupToSave.users);
}

console.log("groups", groupToSave);
Expand Down
173 changes: 173 additions & 0 deletions app/[orgId]/posts/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
"use client";

import { CalendarIcon, UserIcon, TagIcon } from "lucide-react";
import Link from "next/link";
import { useEffect, useState } from "react";
import { useParams } from "next/navigation";

import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Button } from "@/components/ui/button";

interface Post {
id: number;
title: string;
content: string;
tags: string;
slug: string;
createdAt: string;
updatedAt: string;
author: {
id: number;
name: string;
nameId: string;
};
}

// Default data as fallback
const defaultPostData: Post = {
id: 0,
title: "Loading...",
content: "Loading post content...",
tags: "",
slug: "",
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
author: {
id: 0,
name: "Loading...",
nameId: "loading",
},
};

export default function PostDetailsPage() {
const params = useParams();
const orgId = params.orgId as string;
const postId = params.id as string;

const [postData, setPostData] = useState<Post>(defaultPostData);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<string | null>(null);

useEffect(() => {
const fetchPostData = async () => {
try {
setIsLoading(true);
const url = `/api/orgs/${orgId}/posts/${postId}`;
console.log(`Fetching post data from: ${url}`);

const res = await fetch(url);

if (!res.ok) {
console.error(
`Failed to fetch post data: ${res.status} ${res.statusText}`,
);
throw new Error(`Failed to fetch post data: ${res.status}`);
}

const data = await res.json();
console.log("Successfully fetched post data:", data);
setPostData(data);
setError(null);
} catch (err) {
console.error("Error fetching post data:", err);
setError(
err instanceof Error ? err.message : "An unknown error occurred",
);
} finally {
setIsLoading(false);
}
};

if (orgId && postId) {
fetchPostData();
}
}, [orgId, postId]);

const formatDate = (dateString: string) => {
return new Date(dateString).toLocaleString("en-US", {
year: "numeric",
month: "long",
day: "numeric",
hour: "2-digit",
minute: "2-digit",
timeZoneName: "short",
});
};

if (isLoading) {
return <div className="container px-8 py-2">Loading post details...</div>;
}

if (error) {
return (
<div className="container px-8 py-2 text-red-500">Error: {error}</div>
);
}

return (
<div className="container px-8 py-2 w-3xl h-screen">
<Card className="bg-background">
<CardHeader>
<div className="flex justify-between items-start">
<div>
<CardTitle className="text-2xl font-bold">
{postData.title}
</CardTitle>
<CardDescription className="text-muted-foreground">
Posted by {postData.author.name}
</CardDescription>
</div>
</div>
</CardHeader>
<CardContent className="space-y-6">
<div className="prose prose-sm max-w-none">
{postData.content.split("\n").map((paragraph, index) => (
<p key={index} className="mb-4">
{paragraph}
</p>
))}
</div>

<div className="flex flex-col space-y-2">
<div className="flex items-center text-muted-foreground">
<UserIcon className="mr-2 h-4 w-4" />
<Link href={`/${orgId}/users/${postData.author.nameId}`}>
<Button variant="link" className="p-0 h-auto">
{postData.author.name}
</Button>
</Link>
</div>
{postData.tags && (
<div className="flex items-center text-muted-foreground">
<TagIcon className="mr-2 h-4 w-4" />
<span>
{postData.tags.split(",").map((tag, index) => (
<span key={index} className="mr-2">
#{tag.trim()}
</span>
))}
</span>
</div>
)}
<div className="flex items-center text-muted-foreground">
<CalendarIcon className="mr-2 h-4 w-4" />
<span>Posted on {formatDate(postData.createdAt)}</span>
</div>
{postData.updatedAt !== postData.createdAt && (
<div className="flex items-center text-muted-foreground">
<CalendarIcon className="mr-2 h-4 w-4" />
<span>Updated on {formatDate(postData.updatedAt)}</span>
</div>
)}
</div>
</CardContent>
</Card>
</div>
);
}
Loading
Loading