Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/app/dashboard/maintainer/PipelineBoard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,10 @@ function PipelineColumn({ label, bounties }: { label: string; bounties: Bounty[]
<p className="truncate text-xs text-slate-400 dark:text-slate-500">
{b.org}/{b.repo} #{b.issueNumber}
</p>
<p className="mt-1 line-clamp-2 text-sm font-medium text-slate-900 dark:text-white">
<p
className="mt-1 line-clamp-2 text-sm font-medium text-slate-900 dark:text-white"
title={b.title}
>
{b.title}
</p>
<p className="mt-2 text-sm font-semibold text-emerald-600 dark:text-emerald-400">
Expand Down
9 changes: 6 additions & 3 deletions src/app/dashboard/maintainer/page.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import Link from "next/link";
import { Layers, Clock, Coins, FolderGit2, CheckCircle2 } from "lucide-react";
import { fetchBounties } from "@/lib/api";
import { mockBounties, recentActivity, bountiesCompletedSparkline } from "@/lib/mock-data";
Expand Down Expand Up @@ -42,9 +43,11 @@ export default async function MaintainerDashboardPage() {
title="Bounty pipeline"
subtitle="Create bounties from your GitHub issues and approve completed work. Payout release happens automatically once a linked pull request is merged."
action={
<span className="inline-flex items-center gap-2 rounded-full bg-slate-900 px-4 py-2 text-sm font-medium text-white dark:bg-white dark:text-slate-900">
Create bounty
</span>
<Link href="/issues">
<span className="inline-flex items-center gap-2 rounded-full bg-slate-900 px-4 py-2 text-sm font-medium text-white hover:bg-slate-800 dark:bg-white dark:text-slate-900 dark:hover:bg-slate-200">
Create bounty
</span>
</Link>
}
>
{/* Pass statStatus so that a fetchBounties failure renders error states
Expand Down
4 changes: 2 additions & 2 deletions src/app/issues/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,9 @@ export default async function IssueDetailPage({
<div className="mt-8">
<h2 className="font-medium text-slate-900 dark:text-white">Team payout split</h2>
<div className="mt-3 space-y-2">
{bounty.teamSplits.map((split) => (
{bounty.teamSplits.map((split, index) => (
<div
key={split.role}
key={`${split.role}-${split.contributor ?? "unassigned"}-${index}`}
className="flex items-center justify-between rounded-xl border border-slate-200 bg-white px-4 py-3 text-sm shadow-sm dark:border-slate-800 dark:bg-slate-900"
>
<span className="text-slate-600 dark:text-slate-300">
Expand Down
5 changes: 5 additions & 0 deletions src/app/milestones/MilestoneActions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,16 @@ export function PoolDepositButton({ poolId }: { poolId: string }) {

return (
<div className="mt-4 flex items-center gap-2">
<label htmlFor={`deposit-amount-${poolId}`} className="sr-only">
Deposit amount
</label>
<input
id={`deposit-amount-${poolId}`}
type="number"
min="1"
value={amount}
onChange={(e) => setAmount(e.target.value)}
aria-label="Deposit amount"
className="w-24 rounded-lg border border-slate-200 bg-white px-3 py-1.5 text-sm text-slate-900 focus:border-indigo-400 focus:outline-none dark:border-slate-800 dark:bg-slate-900 dark:text-white"
/>
<Button size="sm" variant="outline" onClick={handleDeposit} disabled={pending || connecting}>
Expand Down
5 changes: 3 additions & 2 deletions src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
recentActivity,
faqs,
} from "@/lib/mock-data";
import { STELLAR_NETWORK } from "@/lib/config";
import { formatCurrency } from "@/lib/utils";

const integrations = [
Expand Down Expand Up @@ -90,7 +91,7 @@ export default function HomePage() {
<div className="mx-auto max-w-6xl px-6 pb-20 pt-20 text-center">
<span className="inline-flex items-center gap-2 rounded-full border border-slate-200 bg-white px-3 py-1 text-xs font-medium text-slate-600 shadow-sm dark:border-slate-800 dark:bg-slate-900 dark:text-slate-300">
<span className="h-1.5 w-1.5 rounded-full bg-emerald-500" />
Built for Stellar Mainnet
{STELLAR_NETWORK === "PUBLIC" ? "Built for Stellar Mainnet" : `Built for Stellar (${STELLAR_NETWORK})`}
</span>
<h1 className="mx-auto mt-6 max-w-3xl text-4xl font-semibold tracking-tight text-slate-900 sm:text-6xl dark:text-white">
Merge code.
Expand Down Expand Up @@ -118,7 +119,7 @@ export default function HomePage() {
<div className="mt-8 flex items-center justify-center gap-3 text-sm text-slate-500 dark:text-slate-400">
<AvatarStack seeds={topContributors.map((c) => c.handle)} />
<span>
Joined by <strong className="text-slate-900 dark:text-white">341 contributors</strong>{" "}
Joined by <strong className="text-slate-900 dark:text-white">{platformStats.activeContributors.toLocaleString()} contributors</strong>{" "}
already earning
</span>
</div>
Expand Down
8 changes: 7 additions & 1 deletion src/app/reputation/[handle]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,15 @@ export default async function ReputationPage({
params: Promise<{ handle: string }>;
}) {
const { handle } = await params;
const normalizedHandle = handle.toLowerCase();
const matchedMockKey = Object.keys(mockReputationProfiles).find(
(k) => k.toLowerCase() === normalizedHandle,
);
const fallbackProfile = matchedMockKey ? mockReputationProfiles[matchedMockKey] : null;

const profile = await fetchReputationByUsername(
handle,
mockReputationProfiles[handle] ?? null,
fallbackProfile,
);

if (!profile) notFound();
Expand Down
15 changes: 14 additions & 1 deletion src/app/sitemap.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,24 @@
import type { MetadataRoute } from "next";
import { mockBounties, mockReputationProfiles } from "@/lib/mock-data";

const BASE_URL = process.env.NEXT_PUBLIC_SITE_URL ?? "https://mergefi.app";

export default function sitemap(): MetadataRoute.Sitemap {
const routes = ["", "/issues", "/milestones", "/connect"];
return routes.map((route) => ({
const staticEntries = routes.map((route) => ({
url: `${BASE_URL}${route}`,
lastModified: new Date(),
}));

const issueEntries = mockBounties.map((b) => ({
url: `${BASE_URL}/issues/${b.id}`,
lastModified: new Date(),
}));

const reputationEntries = Object.keys(mockReputationProfiles).map((handle) => ({
url: `${BASE_URL}/reputation/${handle}`,
lastModified: new Date(),
}));

return [...staticEntries, ...issueEntries, ...reputationEntries];
}
2 changes: 1 addition & 1 deletion src/components/ui/EmptyState.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export function EmptyState({
return (
<div className="flex flex-col items-center justify-center rounded-2xl border border-dashed border-slate-200 bg-slate-50/50 px-6 py-12 text-center dark:border-slate-800 dark:bg-slate-900/40">
<span className="flex h-11 w-11 items-center justify-center rounded-full bg-slate-100 dark:bg-slate-800">
<Icon className="h-5 w-5 text-slate-400 dark:text-slate-500" />
<Icon className="h-5 w-5 text-slate-400 dark:text-slate-500" aria-hidden="true" />
</span>
<p className="mt-3 font-medium text-slate-700 dark:text-slate-200">{title}</p>
<p className="mt-1 max-w-sm text-sm text-slate-500 dark:text-slate-400">{description}</p>
Expand Down
3 changes: 2 additions & 1 deletion src/lib/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,8 @@ export async function fetchReputationByUsername(
): Promise<ReputationProfile | null> {
try {
const users = await request<(RawUserProfile & { id: string })[]>("/users");
const user = users.find((u) => u.username === username);
const normalizedTarget = username.toLowerCase();
const user = users.find((u) => u.username.toLowerCase() === normalizedTarget);
if (!user) return fallback;
const snapshot = await request<RawReputationSnapshot | null>(
`/reputation/${user.id}`,
Expand Down