Prettier format pending files
This commit is contained in:
parent
18b64a2760
commit
56ea7df6a9
6 changed files with 133 additions and 36 deletions
|
|
@ -223,7 +223,10 @@ const PassportSummary = ({
|
|||
>
|
||||
<div className="flex items-center gap-2 text-slate-100">
|
||||
<span className="text-xl">
|
||||
{getAchievementEmoji(achievement.icon as any, achievement.name)}
|
||||
{getAchievementEmoji(
|
||||
achievement.icon as any,
|
||||
achievement.name,
|
||||
)}
|
||||
</span>
|
||||
<span className="text-sm font-medium">
|
||||
{achievement.name}
|
||||
|
|
|
|||
|
|
@ -331,7 +331,10 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({
|
|||
if (inviteProcessedRef.current) return;
|
||||
if (!user) return;
|
||||
try {
|
||||
const qs = typeof window !== "undefined" ? new URLSearchParams(window.location.search) : null;
|
||||
const qs =
|
||||
typeof window !== "undefined"
|
||||
? new URLSearchParams(window.location.search)
|
||||
: null;
|
||||
const token = qs?.get("invite");
|
||||
if (!token) return;
|
||||
inviteProcessedRef.current = true;
|
||||
|
|
@ -339,7 +342,10 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({
|
|||
try {
|
||||
await mod.aethexSocialService.acceptInvite(token, user.id);
|
||||
try {
|
||||
aethexToast.success({ title: "Invitation accepted", description: "You're now connected." });
|
||||
aethexToast.success({
|
||||
title: "Invitation accepted",
|
||||
description: "You're now connected.",
|
||||
});
|
||||
} catch {}
|
||||
} catch (e) {
|
||||
console.warn("Invite accept failed", e);
|
||||
|
|
|
|||
|
|
@ -10,13 +10,19 @@ export const aethexSocialService = {
|
|||
.limit(limit);
|
||||
|
||||
if (error) {
|
||||
console.error("Failed to load recommended profiles:", (error as any)?.message || error);
|
||||
console.error(
|
||||
"Failed to load recommended profiles:",
|
||||
(error as any)?.message || error,
|
||||
);
|
||||
return [];
|
||||
}
|
||||
|
||||
return (data || []) as any[];
|
||||
} catch (error) {
|
||||
console.error("Unexpected error loading recommended profiles:", (error as any)?.message || error);
|
||||
console.error(
|
||||
"Unexpected error loading recommended profiles:",
|
||||
(error as any)?.message || error,
|
||||
);
|
||||
return [];
|
||||
}
|
||||
},
|
||||
|
|
@ -29,13 +35,19 @@ export const aethexSocialService = {
|
|||
.eq("follower_id", userId);
|
||||
|
||||
if (error) {
|
||||
console.error("Failed to load following list:", (error as any)?.message || error);
|
||||
console.error(
|
||||
"Failed to load following list:",
|
||||
(error as any)?.message || error,
|
||||
);
|
||||
return [];
|
||||
}
|
||||
|
||||
return (data as any[]).map((r: any) => r.following_id);
|
||||
} catch (error) {
|
||||
console.error("Unexpected error loading following list:", (error as any)?.message || error);
|
||||
console.error(
|
||||
"Unexpected error loading following list:",
|
||||
(error as any)?.message || error,
|
||||
);
|
||||
return [];
|
||||
}
|
||||
},
|
||||
|
|
@ -67,17 +79,27 @@ export const aethexSocialService = {
|
|||
const resp = await fetch("/api/invites", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ inviter_id: inviterId, invitee_email: email, message }),
|
||||
body: JSON.stringify({
|
||||
inviter_id: inviterId,
|
||||
invitee_email: email,
|
||||
message,
|
||||
}),
|
||||
});
|
||||
if (!resp.ok) {
|
||||
const err = await resp.text();
|
||||
throw new Error(err || "Failed to send invite");
|
||||
}
|
||||
return (await resp.json()) as { ok: boolean; inviteUrl: string; token: string };
|
||||
return (await resp.json()) as {
|
||||
ok: boolean;
|
||||
inviteUrl: string;
|
||||
token: string;
|
||||
};
|
||||
},
|
||||
|
||||
async listInvites(inviterId: string) {
|
||||
const resp = await fetch(`/api/invites?inviter_id=${encodeURIComponent(inviterId)}`);
|
||||
const resp = await fetch(
|
||||
`/api/invites?inviter_id=${encodeURIComponent(inviterId)}`,
|
||||
);
|
||||
if (!resp.ok) return [];
|
||||
return await resp.json();
|
||||
},
|
||||
|
|
@ -129,7 +151,9 @@ export const aethexSocialService = {
|
|||
|
||||
async endorseSkill(endorserId: string, endorsedId: string, skill: string) {
|
||||
const payload = { endorser_id: endorserId, endorsed_id: endorsedId, skill };
|
||||
const { error } = await supabase.from("endorsements").insert(payload as any);
|
||||
const { error } = await supabase
|
||||
.from("endorsements")
|
||||
.insert(payload as any);
|
||||
if (error) throw new Error(error.message || "Unable to endorse");
|
||||
await this.applyReward(endorsedId, "endorsement_received", 2);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -82,7 +82,11 @@ export default function Network() {
|
|||
toast({ description: "Invitation sent" });
|
||||
setInviteEmail("");
|
||||
} catch (e: any) {
|
||||
toast({ variant: "destructive", title: "Failed to send invite", description: e?.message || "Try again later" });
|
||||
toast({
|
||||
variant: "destructive",
|
||||
title: "Failed to send invite",
|
||||
description: e?.message || "Try again later",
|
||||
});
|
||||
} finally {
|
||||
setInviteSending(false);
|
||||
}
|
||||
|
|
@ -94,7 +98,11 @@ export default function Network() {
|
|||
await aethexSocialService.endorseSkill(user.id, targetId, skill);
|
||||
toast({ description: `Endorsed for ${skill}` });
|
||||
} catch (e: any) {
|
||||
toast({ variant: "destructive", title: "Failed to endorse", description: e?.message || "Try again later" });
|
||||
toast({
|
||||
variant: "destructive",
|
||||
title: "Failed to endorse",
|
||||
description: e?.message || "Try again later",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -252,7 +260,10 @@ export default function Network() {
|
|||
const p = (c as any).user_profiles || {};
|
||||
const display = p.full_name || p.username || c.connection_id;
|
||||
return (
|
||||
<div key={c.connection_id} className="flex items-center justify-between p-3 rounded-lg border border-border/50">
|
||||
<div
|
||||
key={c.connection_id}
|
||||
className="flex items-center justify-between p-3 rounded-lg border border-border/50"
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<Avatar className="h-10 w-10">
|
||||
<AvatarImage src={p.avatar_url || undefined} />
|
||||
|
|
@ -260,15 +271,21 @@ export default function Network() {
|
|||
</Avatar>
|
||||
<div>
|
||||
<div className="font-medium">{display}</div>
|
||||
<div className="text-xs text-muted-foreground">Connected</div>
|
||||
<div className="text-xs text-muted-foreground">
|
||||
Connected
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Button size="sm" variant="outline">Message</Button>
|
||||
<Button size="sm" variant="outline">
|
||||
Message
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
{connections.length === 0 && (
|
||||
<div className="text-sm text-muted-foreground">No connections yet.</div>
|
||||
<div className="text-sm text-muted-foreground">
|
||||
No connections yet.
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
|
@ -330,14 +347,19 @@ export default function Network() {
|
|||
<Card className="bg-card/50 border-border/50">
|
||||
<CardHeader>
|
||||
<CardTitle>Endorsements</CardTitle>
|
||||
<CardDescription>Recognize skills of your peers</CardDescription>
|
||||
<CardDescription>
|
||||
Recognize skills of your peers
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-3">
|
||||
{connections.slice(0, 6).map((c) => {
|
||||
const p = (c as any).user_profiles || {};
|
||||
const display = p.full_name || p.username || c.connection_id;
|
||||
return (
|
||||
<div key={`endorse-${c.connection_id}`} className="flex items-center justify-between p-3 rounded-lg border border-border/50">
|
||||
<div
|
||||
key={`endorse-${c.connection_id}`}
|
||||
className="flex items-center justify-between p-3 rounded-lg border border-border/50"
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<Avatar className="h-8 w-8">
|
||||
<AvatarImage src={p.avatar_url || undefined} />
|
||||
|
|
@ -346,8 +368,22 @@ export default function Network() {
|
|||
<div className="text-sm">{display}</div>
|
||||
</div>
|
||||
<div className="flex gap-2 flex-wrap">
|
||||
{(["Leadership","Systems","Frontend","Backend"] as const).map((skill) => (
|
||||
<Button key={skill} size="xs" variant="outline" onClick={() => handleEndorse(c.connection_id, skill)}>
|
||||
{(
|
||||
[
|
||||
"Leadership",
|
||||
"Systems",
|
||||
"Frontend",
|
||||
"Backend",
|
||||
] as const
|
||||
).map((skill) => (
|
||||
<Button
|
||||
key={skill}
|
||||
size="xs"
|
||||
variant="outline"
|
||||
onClick={() =>
|
||||
handleEndorse(c.connection_id, skill)
|
||||
}
|
||||
>
|
||||
{skill}
|
||||
</Button>
|
||||
))}
|
||||
|
|
|
|||
|
|
@ -431,13 +431,17 @@ const ProfilePassport = () => {
|
|||
<div className="flex items-center gap-3 text-white">
|
||||
<span className="text-3xl">
|
||||
{((): string => {
|
||||
const key = String(achievement.icon || achievement.name || "").toLowerCase();
|
||||
const key = String(
|
||||
achievement.icon || achievement.name || "",
|
||||
).toLowerCase();
|
||||
if (/founding|founder/.test(key)) return "🎖️";
|
||||
if (/trophy|award|medal|badge/.test(key)) return "🏆";
|
||||
if (/trophy|award|medal|badge/.test(key))
|
||||
return "🏆";
|
||||
if (/welcome/.test(key)) return "🎉";
|
||||
if (/star/.test(key)) return "⭐";
|
||||
if (/rocket|launch/.test(key)) return "🚀";
|
||||
return typeof achievement.icon === "string" && achievement.icon.length <= 3
|
||||
return typeof achievement.icon === "string" &&
|
||||
achievement.icon.length <= 3
|
||||
? (achievement.icon as string)
|
||||
: "🏅";
|
||||
})()}
|
||||
|
|
@ -487,14 +491,26 @@ const ProfilePassport = () => {
|
|||
</Link>
|
||||
</Button>
|
||||
) : profile.email ? (
|
||||
<Button asChild variant="outline" className="border-slate-700/70 text-slate-100">
|
||||
<a href={`mailto:${profile.email}?subject=${encodeURIComponent("Collaboration invite")}&body=${encodeURIComponent("Hi, I'd like to collaborate on a project.")}`}>
|
||||
<Button
|
||||
asChild
|
||||
variant="outline"
|
||||
className="border-slate-700/70 text-slate-100"
|
||||
>
|
||||
<a
|
||||
href={`mailto:${profile.email}?subject=${encodeURIComponent("Collaboration invite")}&body=${encodeURIComponent("Hi, I'd like to collaborate on a project.")}`}
|
||||
>
|
||||
Invite to collaborate
|
||||
</a>
|
||||
</Button>
|
||||
) : (
|
||||
<Button asChild variant="outline" className="border-slate-700/70 text-slate-100">
|
||||
<Link to={`/contact?topic=collaboration&about=${encodeURIComponent(profile.username || profile.full_name || "member")}`}>
|
||||
<Button
|
||||
asChild
|
||||
variant="outline"
|
||||
className="border-slate-700/70 text-slate-100"
|
||||
>
|
||||
<Link
|
||||
to={`/contact?topic=collaboration&about=${encodeURIComponent(profile.username || profile.full_name || "member")}`}
|
||||
>
|
||||
Invite to collaborate
|
||||
</Link>
|
||||
</Button>
|
||||
|
|
|
|||
|
|
@ -621,10 +621,11 @@ export function createServer() {
|
|||
|
||||
// Invites API
|
||||
const baseUrl =
|
||||
process.env.PUBLIC_BASE_URL || process.env.SITE_URL || "https://aethex.biz";
|
||||
process.env.PUBLIC_BASE_URL ||
|
||||
process.env.SITE_URL ||
|
||||
"https://aethex.biz";
|
||||
|
||||
const safeEmail = (v?: string | null) =>
|
||||
(v || "").trim().toLowerCase();
|
||||
const safeEmail = (v?: string | null) => (v || "").trim().toLowerCase();
|
||||
|
||||
const accrue = async (
|
||||
userId: string,
|
||||
|
|
@ -731,7 +732,8 @@ export function createServer() {
|
|||
|
||||
app.get("/api/invites", async (req, res) => {
|
||||
const inviter = String(req.query.inviter_id || "");
|
||||
if (!inviter) return res.status(400).json({ error: "inviter_id required" });
|
||||
if (!inviter)
|
||||
return res.status(400).json({ error: "inviter_id required" });
|
||||
try {
|
||||
const { data, error } = await adminSupabase
|
||||
.from("invites")
|
||||
|
|
@ -751,7 +753,9 @@ export function createServer() {
|
|||
acceptor_id?: string;
|
||||
};
|
||||
if (!token || !acceptor_id) {
|
||||
return res.status(400).json({ error: "token and acceptor_id required" });
|
||||
return res
|
||||
.status(400)
|
||||
.json({ error: "token and acceptor_id required" });
|
||||
}
|
||||
try {
|
||||
const { data: invite, error } = await adminSupabase
|
||||
|
|
@ -766,7 +770,11 @@ export function createServer() {
|
|||
const now = new Date().toISOString();
|
||||
const { error: upErr } = await adminSupabase
|
||||
.from("invites")
|
||||
.update({ status: "accepted", accepted_by: acceptor_id, accepted_at: now })
|
||||
.update({
|
||||
status: "accepted",
|
||||
accepted_by: acceptor_id,
|
||||
accepted_at: now,
|
||||
})
|
||||
.eq("id", (invite as any).id);
|
||||
if (upErr) return res.status(500).json({ error: upErr.message });
|
||||
|
||||
|
|
@ -785,10 +793,14 @@ export function createServer() {
|
|||
if (inviterId) {
|
||||
await accrue(inviterId, "xp", 100, "invite_accepted", { token });
|
||||
await accrue(inviterId, "loyalty", 50, "invite_accepted", { token });
|
||||
await accrue(inviterId, "reputation", 2, "invite_accepted", { token });
|
||||
await accrue(inviterId, "reputation", 2, "invite_accepted", {
|
||||
token,
|
||||
});
|
||||
}
|
||||
await accrue(acceptor_id, "xp", 50, "invite_accepted", { token });
|
||||
await accrue(acceptor_id, "reputation", 1, "invite_accepted", { token });
|
||||
await accrue(acceptor_id, "reputation", 1, "invite_accepted", {
|
||||
token,
|
||||
});
|
||||
|
||||
return res.json({ ok: true });
|
||||
} catch (e: any) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue