Admin functionality has been disabled after removing authentication.
);
diff --git a/src/app/api/admin/flagged-submissions/route.ts b/src/app/api/admin/flagged-submissions/route.ts
new file mode 100644
index 0000000..9df77bb
--- /dev/null
+++ b/src/app/api/admin/flagged-submissions/route.ts
@@ -0,0 +1,15 @@
+import { NextRequest, NextResponse } from "next/server";
+import { getFlaggedSubmissions } from "@/lib/db/operations";
+
+export async function GET(request: NextRequest) {
+ try {
+ const submissions = await getFlaggedSubmissions();
+ return NextResponse.json(submissions);
+ } catch (error) {
+ console.error("Admin flagged submissions API error:", error);
+ return NextResponse.json(
+ { error: "Failed to fetch flagged submissions" },
+ { status: 500 }
+ );
+ }
+}
\ No newline at end of file
diff --git a/src/app/api/auth/[...nextauth]/route.ts b/src/app/api/auth/[...nextauth]/route.ts
deleted file mode 100644
index 10cc3ab..0000000
--- a/src/app/api/auth/[...nextauth]/route.ts
+++ /dev/null
@@ -1,50 +0,0 @@
-import NextAuth from "next-auth";
-import GithubProvider from "next-auth/providers/github";
-import type { GitHubProfile } from "@/types/auth";
-import { serverEnv, validateServerEnv } from "@/lib/env";
-
-// Validate environment variables on server startup
-validateServerEnv();
-
-const handler = NextAuth({
- providers: [
- GithubProvider({
- clientId: serverEnv.GITHUB_ID,
- clientSecret: serverEnv.GITHUB_SECRET,
- authorization: {
- params: {
- scope: 'read:user user:email',
- },
- },
- profile(profile: GitHubProfile) {
- return {
- id: profile.id.toString(),
- name: profile.name || profile.login,
- email: profile.email,
- image: profile.avatar_url,
- username: profile.login, // GitHub username
- };
- },
- }),
- ],
- callbacks: {
- async jwt({ token, account, profile }) {
- if (account && profile) {
- token.username = (profile as GitHubProfile).login;
- }
- return token;
- },
- async session({ session, token }) {
- if (session?.user) {
- session.user.id = token.sub!;
- session.user.username = token.username as string;
- }
- return session;
- },
- },
- pages: {
- signIn: "/",
- },
-});
-
-export { handler as GET, handler as POST };
\ No newline at end of file
diff --git a/src/app/api/health/route.ts b/src/app/api/health/route.ts
index 098e7b9..1af504a 100644
--- a/src/app/api/health/route.ts
+++ b/src/app/api/health/route.ts
@@ -1,59 +1,55 @@
import { NextRequest, NextResponse } from "next/server";
-import { ConvexHttpClient } from "convex/browser";
-import { api } from "../../../../convex/_generated/api";
+import { getDb } from "@/lib/db";
-const CONVEX_URL = process.env.NEXT_PUBLIC_CONVEX_URL;
+const DATABASE_URL = process.env.DATABASE_URL;
export async function GET(request: NextRequest) {
const checks = {
api: "ok",
- convexUrl: CONVEX_URL ? "configured" : "missing",
- convexConnection: "unknown",
+ databaseUrl: DATABASE_URL ? "configured" : "missing",
+ databaseConnection: "unknown",
timestamp: new Date().toISOString(),
};
- // Check if Convex URL is configured
- if (!CONVEX_URL) {
+ // Check if Database URL is configured
+ if (!DATABASE_URL) {
return NextResponse.json(
{
...checks,
- convexUrl: "missing",
- convexConnection: "failed",
- error: "NEXT_PUBLIC_CONVEX_URL environment variable is not set",
+ databaseUrl: "missing",
+ databaseConnection: "failed",
+ error: "DATABASE_URL environment variable is not set",
},
{ status: 503 }
);
}
- // Try to connect to Convex
+ // Try to connect to PostgreSQL
try {
- const convex = new ConvexHttpClient(CONVEX_URL);
-
- // Simple query to test connection - get leaderboard with limit 1
+ // Simple query to test connection
const timeoutPromise = new Promise((_, reject) =>
- setTimeout(() => reject(new Error("Convex health check timed out")), 5000)
+ setTimeout(() => reject(new Error("Database health check timed out")), 5000)
);
- const queryPromise = convex.query(api.submissions.getLeaderboard, { limit: 1 });
+ const queryPromise = getDb().execute('SELECT 1 as test');
await Promise.race([queryPromise, timeoutPromise]);
- checks.convexConnection = "ok";
+ checks.databaseConnection = "ok";
return NextResponse.json(checks, { status: 200 });
} catch (error: any) {
console.error("Health check failed:", {
error: error?.message || error,
- convexUrl: CONVEX_URL,
timestamp: new Date().toISOString(),
});
return NextResponse.json(
{
...checks,
- convexConnection: "failed",
- error: error?.message || "Failed to connect to Convex",
- hint: "Check Convex dashboard for service status",
+ databaseConnection: "failed",
+ error: error?.message || "Failed to connect to database",
+ hint: "Check PostgreSQL connection and DATABASE_URL",
},
{ status: 503 }
);
diff --git a/src/app/api/leaderboard/route.ts b/src/app/api/leaderboard/route.ts
new file mode 100644
index 0000000..9fb21e1
--- /dev/null
+++ b/src/app/api/leaderboard/route.ts
@@ -0,0 +1,29 @@
+import { NextRequest, NextResponse } from "next/server";
+import { getLeaderboard } from "@/lib/db/operations";
+
+export async function GET(request: NextRequest) {
+ try {
+ const { searchParams } = new URL(request.url);
+ const sortBy = searchParams.get('sortBy') as 'cost' | 'tokens' || 'cost';
+ const limit = parseInt(searchParams.get('limit') || '100');
+ const dateFrom = searchParams.get('dateFrom') || undefined;
+ const dateTo = searchParams.get('dateTo') || undefined;
+ const includeFlagged = searchParams.get('includeFlagged') === 'true';
+
+ const submissions = await getLeaderboard({
+ sortBy,
+ limit,
+ dateFrom,
+ dateTo,
+ includeFlagged,
+ });
+
+ return NextResponse.json(submissions);
+ } catch (error) {
+ console.error("Leaderboard API error:", error);
+ return NextResponse.json(
+ { error: "Failed to fetch leaderboard data" },
+ { status: 500 }
+ );
+ }
+}
\ No newline at end of file
diff --git a/src/app/api/profile/[username]/route.ts b/src/app/api/profile/[username]/route.ts
new file mode 100644
index 0000000..c531ba4
--- /dev/null
+++ b/src/app/api/profile/[username]/route.ts
@@ -0,0 +1,27 @@
+import { NextRequest, NextResponse } from "next/server";
+import { getProfile } from "@/lib/db/operations";
+
+export async function GET(
+ request: NextRequest,
+ { params }: { params: Promise<{ username: string }> }
+) {
+ try {
+ const { username } = await params;
+ const profile = await getProfile(username);
+
+ if (!profile) {
+ return NextResponse.json(
+ { error: "Profile not found" },
+ { status: 404 }
+ );
+ }
+
+ return NextResponse.json(profile);
+ } catch (error) {
+ console.error("Profile API error:", error);
+ return NextResponse.json(
+ { error: "Failed to fetch profile data" },
+ { status: 500 }
+ );
+ }
+}
\ No newline at end of file
diff --git a/src/app/api/stats/route.ts b/src/app/api/stats/route.ts
new file mode 100644
index 0000000..0357e3f
--- /dev/null
+++ b/src/app/api/stats/route.ts
@@ -0,0 +1,19 @@
+import { NextRequest, NextResponse } from "next/server";
+import { getGlobalStats } from "@/lib/db/operations";
+
+export async function GET(request: NextRequest) {
+ try {
+ const searchParams = request.nextUrl.searchParams;
+ const dateFrom = searchParams.get("dateFrom") || undefined;
+ const dateTo = searchParams.get("dateTo") || undefined;
+
+ const stats = await getGlobalStats({ dateFrom, dateTo });
+ return NextResponse.json(stats);
+ } catch (error) {
+ console.error("Stats API error:", error);
+ return NextResponse.json(
+ { error: "Failed to fetch global statistics" },
+ { status: 500 }
+ );
+ }
+}
\ No newline at end of file
diff --git a/src/app/api/submit/route.ts b/src/app/api/submit/route.ts
index e0db9db..82901d6 100644
--- a/src/app/api/submit/route.ts
+++ b/src/app/api/submit/route.ts
@@ -1,25 +1,8 @@
import { NextRequest, NextResponse } from "next/server";
-import { ConvexHttpClient } from "convex/browser";
-import { api } from "../../../../convex/_generated/api";
-import { getServerSession } from "next-auth";
-
-// Initialize Convex client with error handling
-const CONVEX_URL = process.env.NEXT_PUBLIC_CONVEX_URL;
-if (!CONVEX_URL) {
- console.error("NEXT_PUBLIC_CONVEX_URL is not set!");
-}
-const convex = new ConvexHttpClient(CONVEX_URL || "");
+import { createSubmission } from "@/lib/db/operations";
export async function POST(request: NextRequest) {
try {
- // Check if Convex URL is configured
- if (!CONVEX_URL) {
- console.error("NEXT_PUBLIC_CONVEX_URL environment variable is not set");
- return NextResponse.json(
- { error: "Server configuration error. Please contact support." },
- { status: 500 }
- );
- }
// Log request details for debugging
const cliVersion = request.headers.get("X-CLI-Version");
@@ -30,7 +13,6 @@ export async function POST(request: NextRequest) {
contentLength: request.headers.get("content-length"),
url: request.url,
method: request.method,
- convexUrl: CONVEX_URL,
});
// Check request size (Vercel has a 4.5MB limit for API routes)
@@ -54,33 +36,28 @@ export async function POST(request: NextRequest) {
);
}
- // Check for authentication
- const session = await getServerSession();
+ // Get email from header (CLI) or web form
+ const email = request.headers.get("X-User-Email") || "anonymous";
+ const source: "cli" | "web" = request.headers.get("X-User-Email") ? "cli" : "web";
+ const verified = false; // Remove automatic verification since no auth
- let githubUsername: string;
- let source: "oauth" | "cli";
- let verified: boolean;
+ console.log("Submission from:", email, "via", source);
- if (session?.user?.username) {
- // Authenticated via OAuth
- githubUsername = session.user.username;
- source = "oauth";
- verified = true;
- console.log("OAuth submission from:", githubUsername);
- } else {
- // CLI submission
- githubUsername = request.headers.get("X-GitHub-User") || "anonymous";
- source = "cli";
- verified = false;
- console.log("CLI submission from:", githubUsername);
-
- // Validate CLI submission has proper username
- if (githubUsername === "anonymous" || !githubUsername) {
- return NextResponse.json(
- { error: "GitHub username is required for CLI submissions. Please provide X-GitHub-User header." },
- { status: 400 }
- );
- }
+ // Validate submission has proper email
+ if (email === "anonymous" || !email) {
+ return NextResponse.json(
+ { error: "Email address is required. Please provide X-User-Email header." },
+ { status: 400 }
+ );
+ }
+
+ // Basic email validation
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
+ if (!emailRegex.test(email)) {
+ return NextResponse.json(
+ { error: "Invalid email format. Please provide a valid email address." },
+ { status: 400 }
+ );
}
// Check if ccData is null or undefined
@@ -130,9 +107,9 @@ export async function POST(request: NextRequest) {
);
}
- // Log submission details before sending to Convex
- console.log("Submitting to Convex:", {
- username: githubUsername,
+ // Log submission details before sending to database
+ console.log("Submitting to database:", {
+ email: email,
source: source,
verified: verified,
dataSize: JSON.stringify(ccData).length,
@@ -140,76 +117,48 @@ export async function POST(request: NextRequest) {
totals: ccData.totals,
});
- // Submit to Convex with timeout handling
+ // Submit to database with timeout handling
let submissionId;
try {
- const submissionPromise = convex.mutation(api.submissions.submit, {
- username: githubUsername,
- githubUsername: githubUsername,
+ submissionId = await createSubmission({
+ username: email,
+ email: email,
source: source,
verified: verified,
ccData: ccData,
});
-
- // Add a timeout of 25 seconds (Vercel has a 30 second timeout for API routes)
- const timeoutPromise = new Promise((_, reject) =>
- setTimeout(() => reject(new Error("Database operation timed out")), 25000)
- );
-
- submissionId = await Promise.race([submissionPromise, timeoutPromise]);
- } catch (convexError: any) {
- console.error("Convex mutation error:", {
- message: convexError?.message,
- data: convexError?.data,
- code: convexError?.code,
- stack: convexError?.stack,
- errorType: typeof convexError,
- errorString: String(convexError),
- fullError: JSON.stringify(convexError, Object.getOwnPropertyNames(convexError))
+ } catch (dbError: any) {
+ console.error("Database error:", {
+ message: dbError?.message,
+ data: dbError?.data,
+ code: dbError?.code,
+ stack: dbError?.stack,
+ errorType: typeof dbError,
+ errorString: String(dbError),
+ fullError: JSON.stringify(dbError, Object.getOwnPropertyNames(dbError))
});
// Extract meaningful error message
let errorMessage = "Database operation failed";
- let requestId = null;
- if (convexError?.message) {
- errorMessage = convexError.message;
- // Extract request ID if present
- const requestIdMatch = convexError.message.match(/Request ID: ([a-f0-9]+)/i);
- if (requestIdMatch) {
- requestId = requestIdMatch[1];
- }
- } else if (typeof convexError === 'string') {
- errorMessage = convexError;
- } else if (convexError?.data?.message) {
- errorMessage = convexError.data.message;
- }
-
- // Log additional context for Server Error
- if (errorMessage.includes("Server Error")) {
- console.error("Convex Server Error - Potential causes:");
- console.error("1. Convex service outage or degraded performance");
- console.error("2. Data validation issue that passed client but failed on server");
- console.error("3. Database quota or rate limiting");
- console.error("4. Network connectivity issues between Vercel and Convex");
- console.error("Request ID for support:", requestId || "unknown");
- console.error("Convex URL:", CONVEX_URL);
- console.error("Submission data size:", JSON.stringify(ccData).length, "bytes");
+ if (dbError?.message) {
+ errorMessage = dbError.message;
+ } else if (typeof dbError === 'string') {
+ errorMessage = dbError;
+ } else if (dbError?.data?.message) {
+ errorMessage = dbError.data.message;
}
// Re-throw with more context
const enhancedError = new Error(errorMessage);
- if (requestId) {
- (enhancedError as any).requestId = requestId;
- }
throw enhancedError;
}
return NextResponse.json({
success: true,
submissionId,
- message: `Successfully submitted data for ${githubUsername}`,
- profileUrl: `https://viberank.app/profile/${githubUsername}`
+ message: `Successfully submitted data for ${email}`,
+ profileUrl: `https://viberank.app/profile/${encodeURIComponent(email)}`
});
} catch (error) {
@@ -256,42 +205,14 @@ export async function POST(request: NextRequest) {
);
}
- // Handle Convex authentication/configuration errors
- if (error.message.includes("Unauthenticated") || error.message.includes("authentication")) {
- console.error("Convex authentication error - check CONVEX_URL configuration");
- return NextResponse.json(
- { error: "Server configuration error. The service is temporarily unavailable. Please try again later." },
- { status: 503 }
- );
- }
-
- // Handle Convex server errors more specifically
- if (error.message.includes("Server Error")) {
- // Extract request ID if available
- const requestId = (error as any).requestId ||
- (error.message.match(/Request ID: ([a-f0-9]+)/i)?.[1]) ||
- "unknown";
-
- console.error("Returning 503 for Convex Server Error with request ID:", requestId);
-
+ // Handle database connection errors
+ if (error.message.includes("connection") || error.message.includes("connect")) {
return NextResponse.json(
- {
- error: "The database service is temporarily unavailable. Please try again in a few moments.",
- requestId: requestId,
- details: "This is usually a temporary issue with the database service. If it persists for more than 5 minutes, please report it.",
- retryAdvice: "Wait 30 seconds and try submitting again."
- },
+ { error: "Database connection error. The service is temporarily unavailable. Please try again later." },
{ status: 503 }
);
}
- if (error.message.includes("Convex") || error.message.includes("mutation")) {
- return NextResponse.json(
- { error: `Database error: ${error.message}` },
- { status: 500 }
- );
- }
-
// Handle database-specific errors
if (error.message.includes("Failed to query") ||
error.message.includes("Failed to update") ||
@@ -332,14 +253,30 @@ export async function POST(request: NextRequest) {
}
}
+// Allowed development origins for CORS
+const allowedDevOrigins = [
+ "http://localhost:3000",
+ "http://127.0.0.1:3000",
+ "http://localhost:3001",
+ "http://127.0.0.1:3001",
+ "http://35.175.206.198",
+ "http://35.175.206.198:3000",
+ "http://35.175.206.198:3001"
+];
+
// Support OPTIONS for CORS if needed
-export async function OPTIONS() {
+export async function OPTIONS(request: NextRequest) {
+ const origin = request.headers.get("origin");
+ const allowedOrigin = process.env.NODE_ENV === "development" && origin && allowedDevOrigins.includes(origin)
+ ? origin
+ : "*";
+
return new NextResponse(null, {
status: 200,
headers: {
- "Access-Control-Allow-Origin": "*",
+ "Access-Control-Allow-Origin": allowedOrigin,
"Access-Control-Allow-Methods": "POST, OPTIONS",
- "Access-Control-Allow-Headers": "Content-Type, X-GitHub-User",
+ "Access-Control-Allow-Headers": "Content-Type, X-User-Email",
},
});
}
\ No newline at end of file
diff --git a/src/app/blog/layout.tsx b/src/app/blog/layout.tsx
deleted file mode 100644
index 50265da..0000000
--- a/src/app/blog/layout.tsx
+++ /dev/null
@@ -1,23 +0,0 @@
-import type { Metadata } from "next";
-
-export const metadata: Metadata = {
- title: {
- template: '%s | Viberank Blog',
- default: 'Blog | Viberank',
- },
- description: 'Insights on AI-powered development, Claude Code, vibe coding, and the future of programming.',
-};
-
-export default function BlogLayout({
- children,
-}: {
- children: React.ReactNode;
-}) {
- return (
-
-
- {children}
-
-
- );
-}
\ No newline at end of file
diff --git a/src/app/blog/page.tsx b/src/app/blog/page.tsx
deleted file mode 100644
index ad9a3e2..0000000
--- a/src/app/blog/page.tsx
+++ /dev/null
@@ -1,49 +0,0 @@
-import Link from "next/link";
-import { ArrowLeft } from "lucide-react";
-
-const blogPosts = [
- {
- slug: "vibe-coding-revolution",
- title: "Vibe Coding Explained: What Karpathy's Viral Term Really Means",
- excerpt: "From Andrej Karpathy's viral tweet to Claude Code, Cursor, and Conductor—understand what vibe coding really means for developers and the future of software.",
- date: "January 19, 2025",
- readTime: "8 min read",
- },
-];
-
-export default function BlogPage() {
- return (
- <>
-
-
- Back to Leaderboard
-
-
-
Blog
-
- Insights on AI-powered development, Claude Code, and the future of programming.
-
-
-
- {blogPosts.map((post) => (
-
-
-
- {post.title}
-
-
{post.excerpt}
-
- {post.date}
- •
- {post.readTime}
-
-
-
- ))}
-
- >
- );
-}
\ No newline at end of file
diff --git a/src/app/blog/vibe-coding-revolution/page.tsx b/src/app/blog/vibe-coding-revolution/page.tsx
deleted file mode 100644
index 945bd0b..0000000
--- a/src/app/blog/vibe-coding-revolution/page.tsx
+++ /dev/null
@@ -1,428 +0,0 @@
-import Link from "next/link";
-import { ArrowLeft, Clock, Calendar, Sparkles, Zap, Terminal, Brain, Mic, GitBranch, Users } from "lucide-react";
-import type { Metadata } from "next";
-
-export const metadata: Metadata = {
- title: "Vibe Coding Explained: What Karpathy's Viral Term Really Means",
- description: "Understand vibe coding—from Andrej Karpathy's viral tweet to how developers use Claude Code, Cursor, and Conductor to build without reading diffs. Learn what it means for the future of software development.",
- openGraph: {
- title: "Vibe Coding Explained: What Karpathy's Viral Term Really Means",
- description: "From Karpathy's viral tweet to Claude Code and Cursor—understand what vibe coding really means for developers.",
- url: "https://viberank.com/blog/vibe-coding-revolution",
- type: "article",
- publishedTime: "2025-01-19T00:00:00.000Z",
- authors: ["Viberank Team"],
- images: [
- {
- url: "/api/og?title=Vibe%20Coding%20Explained&description=What%20Karpathy's%20Viral%20Term%20Really%20Means",
- width: 1200,
- height: 630,
- alt: "Vibe Coding Explained",
- },
- ],
- },
- twitter: {
- card: "summary_large_image",
- title: "Vibe Coding Explained: What Karpathy's Viral Term Really Means",
- description: "From Karpathy's viral tweet to Claude Code and Cursor—understand what vibe coding really means.",
- images: ["/api/og?title=Vibe%20Coding%20Explained&description=What%20Karpathy's%20Viral%20Term%20Really%20Means"],
- },
-};
-
-export default function VibeCodingRevolution() {
- const jsonLd = {
- "@context": "https://schema.org",
- "@type": "BlogPosting",
- "headline": "Vibe Coding Explained: What Karpathy's Viral Term Really Means",
- "description": "Understand vibe coding—from Andrej Karpathy's viral tweet to how developers use Claude Code, Cursor, and Conductor.",
- "image": "https://viberank.com/api/og?title=Vibe%20Coding%20Explained",
- "datePublished": "2025-01-19T00:00:00.000Z",
- "dateModified": "2025-01-19T00:00:00.000Z",
- "author": {
- "@type": "Organization",
- "name": "Viberank",
- "url": "https://viberank.com"
- },
- "publisher": {
- "@type": "Organization",
- "name": "Viberank",
- "url": "https://viberank.com",
- "logo": {
- "@type": "ImageObject",
- "url": "https://viberank.com/icon.svg"
- }
- }
- };
-
- return (
- <>
-
-
-
-
-
- Back to Blog
-
-
-
-
- Vibe Coding Explained: What Karpathy's Viral Term Really Means
-
-
-
-
-
- January 19, 2025
-
-
-
- 8 min read
-
-
-
-
-
- Almost a year ago, Andrej Karpathy tweeted about
- a "new kind of coding" that would change everything. He called it vibe coding—where
- you "fully give in to the vibes, embrace exponentials, and forget that the code even exists."
- What started as a tweet has become a movement that's redefining how we build software.
-
-
-
-
-
-
-
- The Birth of a Movement
-
-
-
-
- "There's a new kind of coding I call 'vibe coding', where you fully give in to the vibes,
- embrace exponentials, and forget that the code even exists. It's possible because the LLMs
- (e.g. Cursor Composer w Sonnet) are getting too good."
-
-
- — Andrej Karpathy, February 6, 2024
-
-
-
-
- Karpathy wasn't just coining a catchy term. The former Tesla AI director and OpenAI co-founder
- was describing his actual workflow: using voice input with SuperWhisper, barely touching the keyboard,
- accepting all AI suggestions without reading the diffs, and letting code grow beyond his usual comprehension.
-
-
-
- When something breaks? Just copy-paste the error message with no comment. Can't fix a bug?
- Work around it or ask for random changes. This wasn't careful, methodical programming—it was
- vibing with the machine.
-
-
-
- Within a month, "vibe coding" was trending in the Merriam-Webster Dictionary. The New York Times,
- Guardian, and tech blogs everywhere were discussing what this meant for the future of programming.
- But here's the thing: not everyone was using the term the same way.
-
-
-
-
-
-
- The Tools Making It Real
-
-
-
- Three tools have emerged as the vibe coding trinity, each taking a different approach to the
- same goal: letting developers focus on what to build, not how to type it.
-
-
-
-
-
-
-
-
-
Cursor: The Editor That Started It All
-
-
- Cursor's Composer mode (hit Cmd+I) was Karpathy's tool of choice. It doesn't ask permission—it
- just implements your ideas across multiple files. The December 2024 Agent mode update took it further:
- no need to specify context files, automatic shell command generation, and the ability to understand
- entire projects at once.
-
-
- The vibe: "Make this thing work" → watches as files appear and code flows across your screen
-
-
-
-
-
-
-
-
-
Claude Code: The Terminal Companion
-
-
- Anthropic's CLI tool lives in your terminal. Install with npm install -g @anthropic-ai/claude-code,
- and it becomes an agentic platform that understands your entire codebase. Through MCP (Model Context Protocol),
- it connects to GitHub, databases, and APIs without leaving your terminal.
-
-
- The vibe: Natural language in the terminal → complete features implemented
-
-
-
-
-
-
-
-
-
Conductor: The Orchestrator
-
-
- The newest player changes the game entirely. This Mac app lets you run multiple Claude Code agents
- in parallel, each in an isolated workspace (using git worktrees). Assign one agent to refactor the
- backend while another updates the frontend and a third writes tests—all simultaneously.
-
-
- The vibe: You're the conductor, AI agents are your orchestra
-
-
-
-
-
-
-
-
- What Vibe Coding Actually Looks Like
-
-
-
- Let's be real about what vibe coding means in practice. Simon Willison, creator of Datasette,
- puts it bluntly: it's "building software with an LLM without reviewing the code it writes."
-
-
-
-
The Vibe Coding Workflow
-
-
- 1.
-
- Voice your intent: Use tools like SuperWhisper to describe what you want.
- "I need a dashboard that shows user activity over time with filterable date ranges."
-
-
-
- 2.
-
- Accept the flow: Watch as AI generates components, APIs, database schemas.
- Don't read the diffs. Trust the process.
-
-
-
- 3.
-
- Error? Copy-paste: Something breaks? Copy the error message, paste it back.
- No explanation needed. The AI figures it out.
-
-
-
- 4.
-
- Iterate by feel: "Make it more responsive." "Add a dark mode." "This feels slow."
- Guide by vibes, not specifications.
-
-
-
-
-
-
- This approach horrifies traditional developers—and for good reason. You're deliberately choosing
- not to understand the code you're shipping. But Karpathy's point was about prototyping, about
- weekend projects, about exploring ideas at the speed of thought.
-
-
-
- The magic happens when you combine vibe coding with tools like Conductor. Now you're not just
- vibing with one AI—you're conducting an entire symphony of agents, each handling different aspects
- of your vision while you maintain the high-level direction.
-
-
-
-
-
-
- The Reality Check
-
-
-
- Here's what the evangelists won't tell you: vibe coding isn't replacing traditional development.
- It's creating a new tier of software—prototypes that actually work, MVPs that ship in hours
- instead of weeks, and experiments that would never have been worth the time investment before.
-
-
-
-
-
Perfect for:
-
-
• Hackathon projects
-
• Proof of concepts
-
• Personal tools and scripts
-
• Learning new frameworks
-
• Rapid prototyping
-
-
-
-
-
Risky for:
-
-
• Production systems
-
• Security-critical code
-
• Performance-sensitive applications
-
• Long-term maintainable codebases
-
• Team projects requiring code reviews
-
-
-
-
-
- The professionals using AI coding tools aren't really vibe coding—they're AI-assisted coding.
- They review diffs, understand the architecture, and maintain quality standards. They use Claude Code
- and Cursor as incredibly powerful assistants, not as black boxes.
-
-
-
-
-
-
- The Cultural Shift
-
-
-
- Whether you call it vibe coding, AI-assisted development, or something else entirely, something
- fundamental has changed. The barrier between idea and implementation is dissolving. A designer
- can build a working prototype. A product manager can test a feature hypothesis. A founder can
- create an MVP without hiring developers.
-
-
-
- This isn't about replacing developers—it's about expanding who can create software. Just like
- WordPress didn't eliminate web developers but created millions of websites that wouldn't have
- existed otherwise, vibe coding is expanding the universe of what gets built.
-
-
-
-
- "The hottest new programming language is English."
-
-
— Andrej Karpathy, 2023
-
-
-
- Karpathy saw this coming. His 2023 prediction that English would become a programming language
- wasn't hyperbole—it was a roadmap. Vibe coding is just the first step toward a world where
- technical implementation is no longer the bottleneck for innovation.
-
-
-
-
-
-
- What This Means for You
-
-
-
- If you're discovering vibe coding for the first time, here's what you need to know:
-
-
-
-
-
For Non-Developers
-
- You can now build things. Real, working software. Start with Cursor and a weekend project.
- Don't worry about understanding every line—focus on making something that solves a problem you have.
-
-
-
-
-
For Developers
-
- Your expertise is more valuable than ever. You're the one who knows when to vibe and when
- to be meticulous. You can guide AI to build production-quality code, architect systems that
- scale, and review what the machines produce. Tools like Conductor multiply your effectiveness—you
- can now manage multiple development streams simultaneously.
-
-
-
-
-
For Teams
-
- Start experimenting with AI-assisted workflows. Set up Claude Code with MCP servers for
- your stack. Try Conductor for parallel development tasks. But maintain code review standards—vibe
- coding for prototypes, professional standards for production.
-
-
-
-
-
- The tools are evolving rapidly. Cursor's Agent mode, Claude Code's MCP integrations, Conductor's
- parallel workspaces—these are just the beginning. The question isn't whether to adopt AI coding
- tools, but how to use them effectively for your specific needs.
-
-
-
-
-
Track Your Vibe with Viberank
-
-
- Whether you're fully vibing or carefully reviewing every diff, Viberank helps you understand
- your AI coding journey. Upload your cc.json file
- from Claude Code to see detailed analytics about your usage patterns, token consumption, and
- development velocity.
-
-
-
- Join thousands of developers tracking their transition from traditional coding to AI-assisted
- development. See how your vibe coding stats compare to the community, discover usage patterns,
- and celebrate milestones as you explore this new way of building software.
-
-
-
-
Ready to measure your vibe?
-
- $
- npx viberank
-
-
- Track your Claude Code usage and join the leaderboard
-
-
-
-
-
-
- >
- );
-}
\ No newline at end of file
diff --git a/src/app/icon.svg b/src/app/icon.svg
deleted file mode 100644
index 629600d..0000000
--- a/src/app/icon.svg
+++ /dev/null
@@ -1,11 +0,0 @@
-
\ No newline at end of file
diff --git a/src/app/page.tsx b/src/app/page.tsx
index 48de9b3..31d45c0 100644
--- a/src/app/page.tsx
+++ b/src/app/page.tsx
@@ -2,61 +2,35 @@
import { useState } from "react";
import { motion, AnimatePresence } from "framer-motion";
-import { Trophy, Upload, Github, Sparkles, TrendingUp, Merge, X } from "lucide-react";
-import { useQuery, useMutation } from "convex/react";
-import { api } from "../../convex/_generated/api";
-import FileUpload from "@/components/FileUpload";
+import { Merge, X } from "lucide-react";
+import { useQuery } from "@/hooks/useApi";
import Leaderboard from "@/components/Leaderboard";
import UpdatesModal from "@/components/UpdatesModal";
-import NavBar from "@/components/NavBar";
import { formatNumber, formatLargeNumber } from "@/lib/utils";
-import { useSession } from "next-auth/react";
export default function Home() {
- const [showUploadModal, setShowUploadModal] = useState(false);
const [showUpdatesModal, setShowUpdatesModal] = useState(false);
- const [copiedToClipboard, setCopiedToClipboard] = useState(false);
const [showMergeBanner, setShowMergeBanner] = useState(true);
const [merging, setMerging] = useState(false);
- const { data: session } = useSession();
- const stats = useQuery(api.stats.getGlobalStats);
- const claimStatus = useQuery(
- api.submissions.checkClaimableSubmissions,
- session?.user?.username ? { githubUsername: session.user.username } : "skip"
- );
- const claimAndMergeMutation = useMutation(api.submissions.claimAndMergeSubmissions);
+ const [dateFrom, setDateFrom] = useState("");
+ const [dateTo, setDateTo] = useState("");
- const copyCommand = () => {
- navigator.clipboard.writeText("npx viberank");
- setCopiedToClipboard(true);
- setTimeout(() => setCopiedToClipboard(false), 2000);
- };
+ const stats = useQuery("/api/stats", {
+ dateFrom: dateFrom || undefined,
+ dateTo: dateTo || undefined,
+ });
+ // TODO: Implement claim and merge functionality with PostgreSQL
+ const claimStatus = null;
+ const claimAndMergeMutation = null;
const handleClaimAndMerge = async () => {
- if (!session?.user?.username) return;
-
- setMerging(true);
- try {
- const result = await claimAndMergeMutation({ githubUsername: session.user.username });
- setShowMergeBanner(false);
- // Refresh the page to show updated data
- window.location.reload();
- } catch (error) {
- console.error("Failed to process:", error);
- alert("Failed to process submissions. Please try again.");
- } finally {
- setMerging(false);
- }
+ // This functionality is disabled without authentication
+ return;
};
return (