Skip to content
Open
Show file tree
Hide file tree
Changes from 3 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
25 changes: 20 additions & 5 deletions apps/frontend/app/dashboard/business/campaigns/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,30 @@ import { EscrowPanel } from "@/components/dashboard/business/escrow-panel";
import { QuickOperationsPanel } from "@/components/dashboard/business/quick-operations-panel";
import { ActivityTimelinePanel } from "@/components/dashboard/business/activity-timeline-panel";
import { MarketReachPanel } from "@/components/dashboard/business/market-reach-panel";
import { campaignDetail } from "@/components/dashboard/business/campaign-detail-data";
import { getBusinessCampaignDetailById, buildCampaignDetailFromListItem } from "@/components/dashboard/business/campaign-detail-data";
import { findBusinessCampaignById } from "@/components/dashboard/business/campaigns-list-data";
import { notFound } from "next/navigation";

export default function CampaignDetailPage({
interface PageParams {
id: string;
}

export default async function CampaignDetailPage({
params,
}: {
params: { id: string };
params: Promise<PageParams>;
}) {
const campaign = campaignDetail; // always mock for now — params.id ignored
const { id } = await params;

let campaign = getBusinessCampaignDetailById(id);

if (!campaign) {
const listItem = findBusinessCampaignById(id);
if (!listItem) {
notFound();
}
campaign = buildCampaignDetailFromListItem(id, listItem.title, listItem.status);
}

return (
<CampaignTabsProvider>
Expand Down Expand Up @@ -42,4 +58,3 @@ export default function CampaignDetailPage({
</CampaignTabsProvider>
);
}

43 changes: 35 additions & 8 deletions apps/frontend/app/marketplace/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import type { Metadata } from "next";
import { notFound } from "next/navigation";
import { Navbar } from "@/components/landing/Navbar";
import { Footer } from "@/components/landing/Footer";
import { campaignDetailMock } from "@/components/marketplace/campaign-detail-data";
import { getCampaignDetailById, campaignDetailMock } from "@/components/marketplace/campaign-detail-data";
import { findCampaignById } from "@/components/marketplace/marketplace-data";
import { CampaignDetailHeader } from "@/components/marketplace/campaign-detail-header";
import { CampaignHeroImage } from "@/components/marketplace/campaign-hero-image";
import { CampaignBriefSection } from "@/components/marketplace/campaign-brief-section";
Expand All @@ -11,24 +13,49 @@ import { ApplicationForm } from "@/components/marketplace/application-form";
import { CampaignHelpCard } from "@/components/marketplace/campaign-help-card";
import { Breadcrumb } from "@/components/marketplace/breadcrumb";

interface PageParams {
id: string;
}

export async function generateMetadata({
params,
}: {
params: { id: string };
params: Promise<PageParams>;
}): Promise<Metadata> {
const campaign = campaignDetailMock;
const { id } = await params;
const marketplaceCampaign = findCampaignById(id);

if (!marketplaceCampaign) {
return {
title: "Campaign Not Found — AdsBazaar Marketplace",
description: "The campaign you're looking for doesn't exist.",
};
}

return {
title: `${campaign.title} — AdsBazaar Marketplace`,
description: campaign.brief,
title: `${marketplaceCampaign.title} — AdsBazaar Marketplace`,
description: marketplaceCampaign.description,
};
}

export default function CampaignDetailPage({
export default async function CampaignDetailPage({
params,
}: {
params: { id: string };
params: Promise<PageParams>;
}) {
const campaign = campaignDetailMock;
const { id } = await params;
const marketplaceCampaign = findCampaignById(id);

if (!marketplaceCampaign) {
notFound();
}

const campaign = getCampaignDetailById(id) || {
...campaignDetailMock,
id: marketplaceCampaign.id,
title: marketplaceCampaign.title,
category: "MARKETPLACE",
};

return (
<>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,4 +95,35 @@ export const campaignDetail: CampaignDetail = {
engagementRate: "4.8%",
engagementProgress: 48,
},
};
};

export function getBusinessCampaignDetailById(id: string): CampaignDetail | null {
if (id === campaignDetail.id) {
return campaignDetail;
}

return null;
}

export function buildCampaignDetailFromListItem(id: string, name: string, status: string): CampaignDetail {
return {
id,
name,
status: status as CampaignDetailStatus,
dateRange: "",
location: "Global (Stellar Ecosystem)",
description: "Campaign details coming soon.",
escrow: { total: "0 XLM", available: "0 XLM", reserved: "0 XLM" },
applicantCount: 0,
proofQueueCount: 0,
applicants: [],
proofQueue: [],
activity: [],
reach: {
totalImpressions: "—",
impressionsProgress: 0,
engagementRate: "—",
engagementProgress: 0,
},
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,7 @@ export const campaignsList: CampaignListItem[] = [
];

export const filterTabs = ["All", "Active", "Scheduled", "Drafts", "Completed"] as const;

export function findBusinessCampaignById(id: string): CampaignListItem | null {
return campaignsList.find(campaign => campaign.id === id) || null;
}
18 changes: 13 additions & 5 deletions apps/frontend/components/marketplace/application-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,29 @@

import { useEffect, useState } from "react";
import { ChevronDown, CircleCheck } from "lucide-react";
import { campaignDetailMock } from "./campaign-detail-data";
import Link from "next/link";

const PITCH_MIN_LENGTH = 20;

const DEFAULT_CONTENT_FORMATS = [
"Video Review",
"Photography Posts",
"Instagram Stories",
"TikTok Short",
];

type FormErrors = {
portfolioLink?: string;
contentFormat?: string;
pitchMessage?: string;
};

export function ApplicationForm({
campaignId = campaignDetailMock.id,
campaignId,
contentFormats = DEFAULT_CONTENT_FORMATS,
}: {
campaignId?: string;
campaignId: string;
contentFormats?: string[];
}) {
const storageKey = `campaign-application:${campaignId}`;

Expand Down Expand Up @@ -147,7 +155,7 @@ export function ApplicationForm({
<option value="" disabled>
Select a format
</option>
{campaignDetailMock.contentFormats.map((format) => (
{contentFormats.map((format) => (
<option key={format} value={format}>
{format}
</option>
Expand All @@ -166,7 +174,7 @@ export function ApplicationForm({
PITCH MESSAGE
</label>
<textarea
placeholder="Why are you a good fit for this wearable launch?"
placeholder="Why are you a good fit for this campaign?"
value={pitchMessage}
onChange={(e) => {
setPitchMessage(e.target.value);
Expand Down
8 changes: 8 additions & 0 deletions apps/frontend/components/marketplace/campaign-detail-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,11 @@ export const campaignDetailMock: CampaignDetail = {
"TikTok Short",
],
};

export function getCampaignDetailById(id: string): CampaignDetail | null {
if (id === campaignDetailMock.id) {
return campaignDetailMock;
}

return null;
}
4 changes: 4 additions & 0 deletions apps/frontend/components/marketplace/marketplace-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,3 +187,7 @@ export const marketplaceCampaigns: MarketplaceCampaign[] = [
];

export const quickTags = ["WEB3 NATIVE", "E-COMMERCE", "TIKTOK TREND", "UGC"];

export function findCampaignById(id: string): MarketplaceCampaign | null {
return marketplaceCampaigns.find(campaign => campaign.id === id) || null;
}