Prettier format pending files
This commit is contained in:
parent
6cb7f3081d
commit
1a680a424f
17 changed files with 117 additions and 76 deletions
|
|
@ -36,7 +36,8 @@ interface GhostApiResponse {
|
|||
|
||||
async function fetchFromGhost(slug: string): Promise<any | null> {
|
||||
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) {
|
||||
return null;
|
||||
|
|
|
|||
|
|
@ -36,7 +36,8 @@ interface GhostApiResponse {
|
|||
|
||||
async function fetchFromGhost(limit: number = 50): Promise<any[]> {
|
||||
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) {
|
||||
return [];
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
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 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
|
||||
const isAuthorized = await isUserAdminOrStaff(userId);
|
||||
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 {
|
||||
|
|
|
|||
|
|
@ -55,7 +55,11 @@ export default async function handler(req: VercelRequest, res: VercelResponse) {
|
|||
|
||||
const overdue = (invoices || [])
|
||||
.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(
|
||||
(c: any) => c.status === "active",
|
||||
|
|
@ -120,12 +124,14 @@ export default async function handler(req: VercelRequest, res: VercelResponse) {
|
|||
pending_invoices: (invoices || []).filter(
|
||||
(i: any) => i.status === "pending" || i.status === "sent",
|
||||
).length,
|
||||
overdue_invoices: (invoices || []).filter((i: any) => i.status === "overdue")
|
||||
.length,
|
||||
overdue_invoices: (invoices || []).filter(
|
||||
(i: any) => i.status === "overdue",
|
||||
).length,
|
||||
},
|
||||
health: {
|
||||
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:
|
||||
outstanding > totalInvoiced * 0.5 ? "at_risk" : "healthy",
|
||||
},
|
||||
|
|
|
|||
|
|
@ -148,7 +148,9 @@ export default async function handler(req: VercelRequest, res: VercelResponse) {
|
|||
.single();
|
||||
|
||||
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 = {};
|
||||
|
|
|
|||
|
|
@ -23,14 +23,8 @@ export default async function handler(req: VercelRequest, res: VercelResponse) {
|
|||
try {
|
||||
if (req.method === "POST") {
|
||||
// Create new invoice
|
||||
const {
|
||||
description,
|
||||
issue_date,
|
||||
due_date,
|
||||
items,
|
||||
notes,
|
||||
currency,
|
||||
} = req.body;
|
||||
const { description, issue_date, due_date, items, notes, currency } =
|
||||
req.body;
|
||||
|
||||
if (!due_date || !items || items.length === 0) {
|
||||
return res.status(400).json({
|
||||
|
|
@ -39,7 +33,10 @@ export default async function handler(req: VercelRequest, res: VercelResponse) {
|
|||
}
|
||||
|
||||
// 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
|
||||
const { count } = await admin
|
||||
|
|
@ -118,7 +115,9 @@ export default async function handler(req: VercelRequest, res: VercelResponse) {
|
|||
.single();
|
||||
|
||||
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 = {};
|
||||
|
|
|
|||
|
|
@ -107,7 +107,9 @@ export default async function handler(req: VercelRequest, res: VercelResponse) {
|
|||
.single();
|
||||
|
||||
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 = {};
|
||||
|
|
@ -155,7 +157,9 @@ export default async function handler(req: VercelRequest, res: VercelResponse) {
|
|||
.single();
|
||||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -56,7 +56,9 @@ export default async function handler(req: VercelRequest, res: VercelResponse) {
|
|||
.single();
|
||||
|
||||
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
|
||||
|
|
@ -99,7 +101,8 @@ export default async function handler(req: VercelRequest, res: VercelResponse) {
|
|||
.eq("user_id", contract.creator_id)
|
||||
.single();
|
||||
|
||||
const newEarnings = (creatorProfile?.total_earnings || 0) + contract.creator_payout_amount;
|
||||
const newEarnings =
|
||||
(creatorProfile?.total_earnings || 0) + contract.creator_payout_amount;
|
||||
|
||||
await admin
|
||||
.from("nexus_creator_profiles")
|
||||
|
|
|
|||
|
|
@ -49,7 +49,9 @@ export default async function handler(req: VercelRequest, res: VercelResponse) {
|
|||
if (oppError || !opportunity || opportunity.posted_by !== user.id) {
|
||||
return res
|
||||
.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
|
||||
|
|
|
|||
|
|
@ -103,7 +103,8 @@ export default async function handler(req: VercelRequest, res: VercelResponse) {
|
|||
|
||||
if (!account.charges_enabled || !account.payouts_enabled) {
|
||||
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,
|
||||
payoutsEnabled: account.payouts_enabled,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -19,14 +19,19 @@ export default async function handler(req: VercelRequest, res: VercelResponse) {
|
|||
|
||||
try {
|
||||
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);
|
||||
} catch (error: any) {
|
||||
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 {
|
||||
|
|
@ -60,18 +65,16 @@ export default async function handler(req: VercelRequest, res: VercelResponse) {
|
|||
.single();
|
||||
|
||||
if (!existingPayment) {
|
||||
await admin
|
||||
.from("nexus_payments")
|
||||
.insert({
|
||||
contract_id: contract.id,
|
||||
amount: contract.total_amount,
|
||||
creator_payout: contract.creator_payout_amount,
|
||||
aethex_commission: contract.aethex_commission_amount,
|
||||
payment_method: "stripe",
|
||||
payment_status: "completed",
|
||||
payment_date: new Date().toISOString(),
|
||||
stripe_payment_intent_id: paymentIntent.id,
|
||||
});
|
||||
await admin.from("nexus_payments").insert({
|
||||
contract_id: contract.id,
|
||||
amount: contract.total_amount,
|
||||
creator_payout: contract.creator_payout_amount,
|
||||
aethex_commission: contract.aethex_commission_amount,
|
||||
payment_method: "stripe",
|
||||
payment_status: "completed",
|
||||
payment_date: new Date().toISOString(),
|
||||
stripe_payment_intent_id: paymentIntent.id,
|
||||
});
|
||||
}
|
||||
|
||||
// Update creator earnings
|
||||
|
|
@ -82,7 +85,8 @@ export default async function handler(req: VercelRequest, res: VercelResponse) {
|
|||
.single();
|
||||
|
||||
const newEarnings =
|
||||
(creatorProfile?.total_earnings || 0) + contract.creator_payout_amount;
|
||||
(creatorProfile?.total_earnings || 0) +
|
||||
contract.creator_payout_amount;
|
||||
|
||||
await admin
|
||||
.from("nexus_creator_profiles")
|
||||
|
|
@ -123,17 +127,15 @@ export default async function handler(req: VercelRequest, res: VercelResponse) {
|
|||
.eq("id", contract.id);
|
||||
|
||||
// Create failed payment record
|
||||
await admin
|
||||
.from("nexus_payments")
|
||||
.insert({
|
||||
contract_id: contract.id,
|
||||
amount: contract.total_amount,
|
||||
creator_payout: 0,
|
||||
aethex_commission: 0,
|
||||
payment_method: "stripe",
|
||||
payment_status: "failed",
|
||||
stripe_payment_intent_id: paymentIntent.id,
|
||||
});
|
||||
await admin.from("nexus_payments").insert({
|
||||
contract_id: contract.id,
|
||||
amount: contract.total_amount,
|
||||
creator_payout: 0,
|
||||
aethex_commission: 0,
|
||||
payment_method: "stripe",
|
||||
payment_status: "failed",
|
||||
stripe_payment_intent_id: paymentIntent.id,
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,13 @@ import { useState } from "react";
|
|||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
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 { useAethexToast } from "@/hooks/use-aethex-toast";
|
||||
import { Loader2, X } from "lucide-react";
|
||||
|
|
@ -28,7 +34,9 @@ const BlogEditor = ({ onPublish, initialData }: BlogEditorProps) => {
|
|||
const [excerpt, setExcerpt] = useState(initialData?.excerpt || "");
|
||||
const [html, setHtml] = useState(initialData?.html || "");
|
||||
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 [tagInput, setTagInput] = useState("");
|
||||
const [metaTitle, setMetaTitle] = useState(initialData?.meta_title || "");
|
||||
|
|
@ -139,7 +147,8 @@ const BlogEditor = ({ onPublish, initialData }: BlogEditorProps) => {
|
|||
/>
|
||||
{!slug && title && (
|
||||
<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>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -173,7 +182,9 @@ const BlogEditor = ({ onPublish, initialData }: BlogEditorProps) => {
|
|||
<Input
|
||||
value={tagInput}
|
||||
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"
|
||||
className="border-border/50"
|
||||
/>
|
||||
|
|
@ -238,7 +249,7 @@ const BlogEditor = ({ onPublish, initialData }: BlogEditorProps) => {
|
|||
<Textarea
|
||||
value={html}
|
||||
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"
|
||||
/>
|
||||
<div className="text-xs text-muted-foreground">
|
||||
|
|
@ -255,7 +266,9 @@ const BlogEditor = ({ onPublish, initialData }: BlogEditorProps) => {
|
|||
</CardHeader>
|
||||
<CardContent className="prose prose-invert max-w-none">
|
||||
{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 }} />
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
|
|
|||
|
|
@ -300,7 +300,10 @@ export default function AdminBlogManager() {
|
|||
/>
|
||||
</div>
|
||||
<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
|
||||
</Label>
|
||||
<select
|
||||
|
|
@ -441,7 +444,9 @@ export default function AdminBlogManager() {
|
|||
{!slug && title && (
|
||||
<p className="text-xs text-muted-foreground">
|
||||
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>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,11 +1,7 @@
|
|||
import { Button } from "@/components/ui/button";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { Link } from "react-router-dom";
|
||||
import {
|
||||
Zap,
|
||||
Building2,
|
||||
ArrowRight,
|
||||
} from "lucide-react";
|
||||
import { Zap, Building2, ArrowRight } from "lucide-react";
|
||||
|
||||
interface BlogCTAProps {
|
||||
variant?: "nexus" | "corp" | "both";
|
||||
|
|
|
|||
|
|
@ -41,7 +41,8 @@ export async function fetchGhostPosts(
|
|||
limit: String(limit),
|
||||
page: String(page),
|
||||
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()}`;
|
||||
|
|
@ -72,7 +73,8 @@ export async function fetchGhostPostBySlug(
|
|||
const params = new URLSearchParams({
|
||||
key: GHOST_CONTENT_API_KEY,
|
||||
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()}`;
|
||||
|
|
@ -110,10 +112,7 @@ export function transformGhostPost(post: GhostPost) {
|
|||
})
|
||||
: "",
|
||||
readTime: post.reading_time || null,
|
||||
category:
|
||||
post.tags && post.tags.length > 0
|
||||
? post.tags[0].name
|
||||
: "General",
|
||||
category: post.tags && post.tags.length > 0 ? post.tags[0].name : "General",
|
||||
image: post.feature_image || null,
|
||||
trending: false,
|
||||
likes: null,
|
||||
|
|
|
|||
|
|
@ -58,8 +58,12 @@ function generateGhostJWT(): string {
|
|||
aud: "/admin/",
|
||||
};
|
||||
|
||||
const headerEncoded = Buffer.from(JSON.stringify(header)).toString("base64url");
|
||||
const payloadEncoded = Buffer.from(JSON.stringify(payload)).toString("base64url");
|
||||
const headerEncoded = Buffer.from(JSON.stringify(header)).toString(
|
||||
"base64url",
|
||||
);
|
||||
const payloadEncoded = Buffer.from(JSON.stringify(payload)).toString(
|
||||
"base64url",
|
||||
);
|
||||
|
||||
const signatureInput = `${headerEncoded}.${payloadEncoded}`;
|
||||
const signature = crypto
|
||||
|
|
|
|||
|
|
@ -34,9 +34,7 @@
|
|||
"permanent": true
|
||||
}
|
||||
],
|
||||
"rewrites": [
|
||||
{ "source": "/(.*)", "destination": "/index.html" }
|
||||
],
|
||||
"rewrites": [{ "source": "/(.*)", "destination": "/index.html" }],
|
||||
"headers": [
|
||||
{
|
||||
"source": "/assets/(.*)",
|
||||
|
|
|
|||
Loading…
Reference in a new issue