Prettier format pending files

This commit is contained in:
Builder.io 2025-11-15 21:13:01 +00:00
parent 6cb7f3081d
commit 1a680a424f
17 changed files with 117 additions and 76 deletions

View file

@ -36,7 +36,8 @@ interface GhostApiResponse {
async function fetchFromGhost(slug: string): Promise<any | null> { async function fetchFromGhost(slug: string): Promise<any | null> {
const ghostUrl = process.env.GHOST_API_URL || process.env.VITE_GHOST_API_URL; const ghostUrl = process.env.GHOST_API_URL || process.env.VITE_GHOST_API_URL;
const ghostKey = process.env.GHOST_CONTENT_API_KEY || process.env.VITE_GHOST_CONTENT_API_KEY; const ghostKey =
process.env.GHOST_CONTENT_API_KEY || process.env.VITE_GHOST_CONTENT_API_KEY;
if (!ghostUrl || !ghostKey) { if (!ghostUrl || !ghostKey) {
return null; return null;

View file

@ -36,7 +36,8 @@ interface GhostApiResponse {
async function fetchFromGhost(limit: number = 50): Promise<any[]> { async function fetchFromGhost(limit: number = 50): Promise<any[]> {
const ghostUrl = process.env.GHOST_API_URL || process.env.VITE_GHOST_API_URL; const ghostUrl = process.env.GHOST_API_URL || process.env.VITE_GHOST_API_URL;
const ghostKey = process.env.GHOST_CONTENT_API_KEY || process.env.VITE_GHOST_CONTENT_API_KEY; const ghostKey =
process.env.GHOST_CONTENT_API_KEY || process.env.VITE_GHOST_CONTENT_API_KEY;
if (!ghostUrl || !ghostKey) { if (!ghostUrl || !ghostKey) {
return []; return [];

View file

@ -1,5 +1,8 @@
import { createClient } from "@supabase/supabase-js"; import { createClient } from "@supabase/supabase-js";
import { publishPostToGhost, updatePostInGhost } from "../../server/ghost-admin-api"; import {
publishPostToGhost,
updatePostInGhost,
} from "../../server/ghost-admin-api";
const supabaseUrl = process.env.SUPABASE_URL || ""; const supabaseUrl = process.env.SUPABASE_URL || "";
const supabaseServiceRole = process.env.SUPABASE_SERVICE_ROLE || ""; const supabaseServiceRole = process.env.SUPABASE_SERVICE_ROLE || "";
@ -42,7 +45,9 @@ export default async function handler(req: any, res: any) {
// Check if user is admin or staff // Check if user is admin or staff
const isAuthorized = await isUserAdminOrStaff(userId); const isAuthorized = await isUserAdminOrStaff(userId);
if (!isAuthorized) { if (!isAuthorized) {
return res.status(403).json({ error: "Forbidden: Admin/Staff access required" }); return res
.status(403)
.json({ error: "Forbidden: Admin/Staff access required" });
} }
const { const {

View file

@ -55,7 +55,11 @@ export default async function handler(req: VercelRequest, res: VercelResponse) {
const overdue = (invoices || []) const overdue = (invoices || [])
.filter((i: any) => i.status === "overdue") .filter((i: any) => i.status === "overdue")
.reduce((sum: number, i: any) => sum + ((i.amount_due || 0) - (i.amount_paid || 0)), 0); .reduce(
(sum: number, i: any) =>
sum + ((i.amount_due || 0) - (i.amount_paid || 0)),
0,
);
const activeContracts = (contracts || []).filter( const activeContracts = (contracts || []).filter(
(c: any) => c.status === "active", (c: any) => c.status === "active",
@ -120,12 +124,14 @@ export default async function handler(req: VercelRequest, res: VercelResponse) {
pending_invoices: (invoices || []).filter( pending_invoices: (invoices || []).filter(
(i: any) => i.status === "pending" || i.status === "sent", (i: any) => i.status === "pending" || i.status === "sent",
).length, ).length,
overdue_invoices: (invoices || []).filter((i: any) => i.status === "overdue") overdue_invoices: (invoices || []).filter(
.length, (i: any) => i.status === "overdue",
).length,
}, },
health: { health: {
payment_rate: Math.round((totalPaid / totalInvoiced) * 100) || 0, payment_rate: Math.round((totalPaid / totalInvoiced) * 100) || 0,
contract_completion_rate: completedContracts / (activeContracts + completedContracts) || 0, contract_completion_rate:
completedContracts / (activeContracts + completedContracts) || 0,
cash_flow_status: cash_flow_status:
outstanding > totalInvoiced * 0.5 ? "at_risk" : "healthy", outstanding > totalInvoiced * 0.5 ? "at_risk" : "healthy",
}, },

View file

@ -148,7 +148,9 @@ export default async function handler(req: VercelRequest, res: VercelResponse) {
.single(); .single();
if (!contract) { if (!contract) {
return res.status(403).json({ error: "Contract not found or unauthorized" }); return res
.status(403)
.json({ error: "Contract not found or unauthorized" });
} }
const updateData: any = {}; const updateData: any = {};

View file

@ -23,14 +23,8 @@ export default async function handler(req: VercelRequest, res: VercelResponse) {
try { try {
if (req.method === "POST") { if (req.method === "POST") {
// Create new invoice // Create new invoice
const { const { description, issue_date, due_date, items, notes, currency } =
description, req.body;
issue_date,
due_date,
items,
notes,
currency,
} = req.body;
if (!due_date || !items || items.length === 0) { if (!due_date || !items || items.length === 0) {
return res.status(400).json({ return res.status(400).json({
@ -39,7 +33,10 @@ export default async function handler(req: VercelRequest, res: VercelResponse) {
} }
// Calculate total from items // Calculate total from items
const amountDue = items.reduce((sum: number, item: any) => sum + (item.amount || 0), 0); const amountDue = items.reduce(
(sum: number, item: any) => sum + (item.amount || 0),
0,
);
// Generate invoice number // Generate invoice number
const { count } = await admin const { count } = await admin
@ -118,7 +115,9 @@ export default async function handler(req: VercelRequest, res: VercelResponse) {
.single(); .single();
if (verifyError || !invoice) { if (verifyError || !invoice) {
return res.status(403).json({ error: "Invoice not found or unauthorized" }); return res
.status(403)
.json({ error: "Invoice not found or unauthorized" });
} }
const updateData: any = {}; const updateData: any = {};

View file

@ -107,7 +107,9 @@ export default async function handler(req: VercelRequest, res: VercelResponse) {
.single(); .single();
if (verifyError || !member) { if (verifyError || !member) {
return res.status(403).json({ error: "Member not found or unauthorized" }); return res
.status(403)
.json({ error: "Member not found or unauthorized" });
} }
const updateData: any = {}; const updateData: any = {};
@ -155,7 +157,9 @@ export default async function handler(req: VercelRequest, res: VercelResponse) {
.single(); .single();
if (!member) { if (!member) {
return res.status(403).json({ error: "Member not found or unauthorized" }); return res
.status(403)
.json({ error: "Member not found or unauthorized" });
} }
const { error: deleteError } = await admin const { error: deleteError } = await admin

View file

@ -56,7 +56,9 @@ export default async function handler(req: VercelRequest, res: VercelResponse) {
.single(); .single();
if (contractError || !contract) { if (contractError || !contract) {
return res.status(403).json({ error: "Contract not found or unauthorized" }); return res
.status(403)
.json({ error: "Contract not found or unauthorized" });
} }
// Update contract status to active // Update contract status to active
@ -99,7 +101,8 @@ export default async function handler(req: VercelRequest, res: VercelResponse) {
.eq("user_id", contract.creator_id) .eq("user_id", contract.creator_id)
.single(); .single();
const newEarnings = (creatorProfile?.total_earnings || 0) + contract.creator_payout_amount; const newEarnings =
(creatorProfile?.total_earnings || 0) + contract.creator_payout_amount;
await admin await admin
.from("nexus_creator_profiles") .from("nexus_creator_profiles")

View file

@ -49,7 +49,9 @@ export default async function handler(req: VercelRequest, res: VercelResponse) {
if (oppError || !opportunity || opportunity.posted_by !== user.id) { if (oppError || !opportunity || opportunity.posted_by !== user.id) {
return res return res
.status(403) .status(403)
.json({ error: "Not authorized to post contracts for this opportunity" }); .json({
error: "Not authorized to post contracts for this opportunity",
});
} }
// Verify the creator has applied // Verify the creator has applied

View file

@ -103,7 +103,8 @@ export default async function handler(req: VercelRequest, res: VercelResponse) {
if (!account.charges_enabled || !account.payouts_enabled) { if (!account.charges_enabled || !account.payouts_enabled) {
return res.status(400).json({ return res.status(400).json({
error: "Account setup not complete. Please complete all required steps.", error:
"Account setup not complete. Please complete all required steps.",
chargesEnabled: account.charges_enabled, chargesEnabled: account.charges_enabled,
payoutsEnabled: account.payouts_enabled, payoutsEnabled: account.payouts_enabled,
}); });

View file

@ -19,14 +19,19 @@ export default async function handler(req: VercelRequest, res: VercelResponse) {
try { try {
if (!sig || !webhookSecret) { if (!sig || !webhookSecret) {
return res.status(400).json({ error: "Missing webhook signature or secret" }); return res
.status(400)
.json({ error: "Missing webhook signature or secret" });
} }
const body = typeof req.body === "string" ? req.body : JSON.stringify(req.body); const body =
typeof req.body === "string" ? req.body : JSON.stringify(req.body);
event = stripe.webhooks.constructEvent(body, sig, webhookSecret); event = stripe.webhooks.constructEvent(body, sig, webhookSecret);
} catch (error: any) { } catch (error: any) {
console.error("Webhook signature verification failed:", error.message); console.error("Webhook signature verification failed:", error.message);
return res.status(400).json({ error: "Webhook signature verification failed" }); return res
.status(400)
.json({ error: "Webhook signature verification failed" });
} }
try { try {
@ -60,18 +65,16 @@ export default async function handler(req: VercelRequest, res: VercelResponse) {
.single(); .single();
if (!existingPayment) { if (!existingPayment) {
await admin await admin.from("nexus_payments").insert({
.from("nexus_payments") contract_id: contract.id,
.insert({ amount: contract.total_amount,
contract_id: contract.id, creator_payout: contract.creator_payout_amount,
amount: contract.total_amount, aethex_commission: contract.aethex_commission_amount,
creator_payout: contract.creator_payout_amount, payment_method: "stripe",
aethex_commission: contract.aethex_commission_amount, payment_status: "completed",
payment_method: "stripe", payment_date: new Date().toISOString(),
payment_status: "completed", stripe_payment_intent_id: paymentIntent.id,
payment_date: new Date().toISOString(), });
stripe_payment_intent_id: paymentIntent.id,
});
} }
// Update creator earnings // Update creator earnings
@ -82,7 +85,8 @@ export default async function handler(req: VercelRequest, res: VercelResponse) {
.single(); .single();
const newEarnings = const newEarnings =
(creatorProfile?.total_earnings || 0) + contract.creator_payout_amount; (creatorProfile?.total_earnings || 0) +
contract.creator_payout_amount;
await admin await admin
.from("nexus_creator_profiles") .from("nexus_creator_profiles")
@ -123,17 +127,15 @@ export default async function handler(req: VercelRequest, res: VercelResponse) {
.eq("id", contract.id); .eq("id", contract.id);
// Create failed payment record // Create failed payment record
await admin await admin.from("nexus_payments").insert({
.from("nexus_payments") contract_id: contract.id,
.insert({ amount: contract.total_amount,
contract_id: contract.id, creator_payout: 0,
amount: contract.total_amount, aethex_commission: 0,
creator_payout: 0, payment_method: "stripe",
aethex_commission: 0, payment_status: "failed",
payment_method: "stripe", stripe_payment_intent_id: paymentIntent.id,
payment_status: "failed", });
stripe_payment_intent_id: paymentIntent.id,
});
} }
break; break;
} }

View file

@ -2,7 +2,13 @@ import { useState } from "react";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Textarea } from "@/components/ui/textarea"; import { Textarea } from "@/components/ui/textarea";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Badge } from "@/components/ui/badge"; import { Badge } from "@/components/ui/badge";
import { useAethexToast } from "@/hooks/use-aethex-toast"; import { useAethexToast } from "@/hooks/use-aethex-toast";
import { Loader2, X } from "lucide-react"; import { Loader2, X } from "lucide-react";
@ -28,7 +34,9 @@ const BlogEditor = ({ onPublish, initialData }: BlogEditorProps) => {
const [excerpt, setExcerpt] = useState(initialData?.excerpt || ""); const [excerpt, setExcerpt] = useState(initialData?.excerpt || "");
const [html, setHtml] = useState(initialData?.html || ""); const [html, setHtml] = useState(initialData?.html || "");
const [slug, setSlug] = useState(initialData?.slug || ""); const [slug, setSlug] = useState(initialData?.slug || "");
const [featureImage, setFeatureImage] = useState(initialData?.feature_image || ""); const [featureImage, setFeatureImage] = useState(
initialData?.feature_image || "",
);
const [tags, setTags] = useState<string[]>(initialData?.tags || []); const [tags, setTags] = useState<string[]>(initialData?.tags || []);
const [tagInput, setTagInput] = useState(""); const [tagInput, setTagInput] = useState("");
const [metaTitle, setMetaTitle] = useState(initialData?.meta_title || ""); const [metaTitle, setMetaTitle] = useState(initialData?.meta_title || "");
@ -139,7 +147,8 @@ const BlogEditor = ({ onPublish, initialData }: BlogEditorProps) => {
/> />
{!slug && title && ( {!slug && title && (
<p className="text-xs text-muted-foreground"> <p className="text-xs text-muted-foreground">
Auto-slug: <code className="bg-background/80 px-2 py-1">{autoSlug}</code> Auto-slug:{" "}
<code className="bg-background/80 px-2 py-1">{autoSlug}</code>
</p> </p>
)} )}
</div> </div>
@ -173,7 +182,9 @@ const BlogEditor = ({ onPublish, initialData }: BlogEditorProps) => {
<Input <Input
value={tagInput} value={tagInput}
onChange={(e) => setTagInput(e.target.value)} onChange={(e) => setTagInput(e.target.value)}
onKeyPress={(e) => e.key === "Enter" && (e.preventDefault(), addTag())} onKeyPress={(e) =>
e.key === "Enter" && (e.preventDefault(), addTag())
}
placeholder="Add tag and press Enter" placeholder="Add tag and press Enter"
className="border-border/50" className="border-border/50"
/> />
@ -238,7 +249,7 @@ const BlogEditor = ({ onPublish, initialData }: BlogEditorProps) => {
<Textarea <Textarea
value={html} value={html}
onChange={(e) => setHtml(e.target.value)} onChange={(e) => setHtml(e.target.value)}
placeholder='<p>Write your post content here...</p>' placeholder="<p>Write your post content here...</p>"
className="border-border/50 font-mono h-96" className="border-border/50 font-mono h-96"
/> />
<div className="text-xs text-muted-foreground"> <div className="text-xs text-muted-foreground">
@ -255,7 +266,9 @@ const BlogEditor = ({ onPublish, initialData }: BlogEditorProps) => {
</CardHeader> </CardHeader>
<CardContent className="prose prose-invert max-w-none"> <CardContent className="prose prose-invert max-w-none">
{title && <h1>{title}</h1>} {title && <h1>{title}</h1>}
{excerpt && <p className="text-muted-foreground italic">{excerpt}</p>} {excerpt && (
<p className="text-muted-foreground italic">{excerpt}</p>
)}
<div dangerouslySetInnerHTML={{ __html: html }} /> <div dangerouslySetInnerHTML={{ __html: html }} />
</CardContent> </CardContent>
</Card> </Card>

View file

@ -300,7 +300,10 @@ export default function AdminBlogManager() {
/> />
</div> </div>
<div className="flex-1"> <div className="flex-1">
<Label htmlFor="filter-category" className="text-xs mb-1 block"> <Label
htmlFor="filter-category"
className="text-xs mb-1 block"
>
Category Category
</Label> </Label>
<select <select
@ -441,7 +444,9 @@ export default function AdminBlogManager() {
{!slug && title && ( {!slug && title && (
<p className="text-xs text-muted-foreground"> <p className="text-xs text-muted-foreground">
Auto-slug:{" "} Auto-slug:{" "}
<code className="bg-background/80 px-2 py-1">{autoSlug}</code> <code className="bg-background/80 px-2 py-1">
{autoSlug}
</code>
</p> </p>
)} )}
</div> </div>

View file

@ -1,11 +1,7 @@
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Card, CardContent } from "@/components/ui/card"; import { Card, CardContent } from "@/components/ui/card";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { import { Zap, Building2, ArrowRight } from "lucide-react";
Zap,
Building2,
ArrowRight,
} from "lucide-react";
interface BlogCTAProps { interface BlogCTAProps {
variant?: "nexus" | "corp" | "both"; variant?: "nexus" | "corp" | "both";

View file

@ -41,7 +41,8 @@ export async function fetchGhostPosts(
limit: String(limit), limit: String(limit),
page: String(page), page: String(page),
include: "authors,tags", include: "authors,tags",
fields: "id,title,slug,excerpt,html,feature_image,published_at,reading_time,authors,tags", fields:
"id,title,slug,excerpt,html,feature_image,published_at,reading_time,authors,tags",
}); });
const url = `${GHOST_API_URL}/ghost/api/content/posts/?${params.toString()}`; const url = `${GHOST_API_URL}/ghost/api/content/posts/?${params.toString()}`;
@ -72,7 +73,8 @@ export async function fetchGhostPostBySlug(
const params = new URLSearchParams({ const params = new URLSearchParams({
key: GHOST_CONTENT_API_KEY, key: GHOST_CONTENT_API_KEY,
include: "authors,tags", include: "authors,tags",
fields: "id,title,slug,excerpt,html,feature_image,published_at,reading_time,authors,tags", fields:
"id,title,slug,excerpt,html,feature_image,published_at,reading_time,authors,tags",
}); });
const url = `${GHOST_API_URL}/ghost/api/content/posts/slug/${encodeURIComponent(slug)}/?${params.toString()}`; const url = `${GHOST_API_URL}/ghost/api/content/posts/slug/${encodeURIComponent(slug)}/?${params.toString()}`;
@ -110,10 +112,7 @@ export function transformGhostPost(post: GhostPost) {
}) })
: "", : "",
readTime: post.reading_time || null, readTime: post.reading_time || null,
category: category: post.tags && post.tags.length > 0 ? post.tags[0].name : "General",
post.tags && post.tags.length > 0
? post.tags[0].name
: "General",
image: post.feature_image || null, image: post.feature_image || null,
trending: false, trending: false,
likes: null, likes: null,

View file

@ -58,8 +58,12 @@ function generateGhostJWT(): string {
aud: "/admin/", aud: "/admin/",
}; };
const headerEncoded = Buffer.from(JSON.stringify(header)).toString("base64url"); const headerEncoded = Buffer.from(JSON.stringify(header)).toString(
const payloadEncoded = Buffer.from(JSON.stringify(payload)).toString("base64url"); "base64url",
);
const payloadEncoded = Buffer.from(JSON.stringify(payload)).toString(
"base64url",
);
const signatureInput = `${headerEncoded}.${payloadEncoded}`; const signatureInput = `${headerEncoded}.${payloadEncoded}`;
const signature = crypto const signature = crypto

View file

@ -34,9 +34,7 @@
"permanent": true "permanent": true
} }
], ],
"rewrites": [ "rewrites": [{ "source": "/(.*)", "destination": "/index.html" }],
{ "source": "/(.*)", "destination": "/index.html" }
],
"headers": [ "headers": [
{ {
"source": "/assets/(.*)", "source": "/assets/(.*)",