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
28 changes: 28 additions & 0 deletions app/api/directory/export/email/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { NextResponse } from "next/server";

export async function POST(req: Request) {
try {
const { members, origin } = await req.json();

const memberList = members
.map(
(m: any, i: number) =>
`${i + 1}. ${m.name} (${m.email})\n - Profile: ${origin}/profile/${m.id}\n }`
)
.join("\n\n");

const subject = `Recommended Developers (${members.length})`;
const body = `Hi,\n\nHere are some recommended developers:\n\n${memberList}\n\nBest regards,\n[Your Name]`;

const gmailDraftURL = `https://mail.google.com/mail/u/0/?view=cm&fs=1&to=&su=${encodeURIComponent(
subject
)}&body=${encodeURIComponent(body)}`;

return NextResponse.json({ gmailDraftURL });
} catch (err: any) {
return NextResponse.json(
{ message: err.message || "Failed to create email draft" },
{ status: 500 }
);
}
}
17 changes: 17 additions & 0 deletions app/api/directory/options/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { NextResponse } from "next/server";
import { createClient } from "@supabase/supabase-js";

const supabase = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL || "",
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY || ""
);

export async function GET() {
const { data: skillsData } = await supabase.from("skills").select("name");
const { data: domainData } = await supabase.from("members").select("domain");

return NextResponse.json({
skills: (skillsData ?? []).map((s) => s.name),
domains: Array.from(new Set((domainData ?? []).map((d) => d.domain))).sort(),
});
}
135 changes: 97 additions & 38 deletions app/api/directory/search/route.ts
Original file line number Diff line number Diff line change
@@ -1,52 +1,111 @@
import { NextRequest, NextResponse } from 'next/server';
import { MemberService } from '@/lib/db';
import { createClient } from '@supabase/supabase-js';

// Helper function to convert year string to number
const convertYearToNumber = (year: string): number => {
if (year === '1st') return 1;
if (year === '2nd') return 2;
if (year === '3rd') return 3;
if (year === '4th') return 4;
return 5; // Alumni
};
const supabase = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL || "",
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY || ""
);

export const dynamic = 'force-dynamic';
export const revalidate = 0;

export async function GET(request: NextRequest) {
try {
const searchParams = request.nextUrl.searchParams;

// Extract and sanitize query params
const search = searchParams.get('search')?.trim() || '';
const domains = searchParams.getAll('domain').filter(Boolean);
const years = searchParams
.getAll('year')
.map(year => {
// Try to parse as number first
const numYear = Number(year);
if (!isNaN(numYear)) return numYear;
// If not a number, convert from string format
return convertYearToNumber(year);
})
.filter((y) => !isNaN(y));
const skills = searchParams.getAll('skills').filter(Boolean);

const supabase = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
);
// Fetch filtered members
const results = await MemberService.searchMembers(supabase, search, domains, years, skills);
const { searchParams } = new URL(request.url);
const search = searchParams.get("search") || "";
const domains = searchParams.getAll("domain");
const years = searchParams.getAll("year");
const skills = searchParams.getAll("skills");

const allMappedYears: string[] = years;

const baseSelectStatement = `
id,
name,
email,
picture_url,
domain,
year_of_study,
member_skills!inner(
skills!inner(
name
)
)
`;

let fetchedMembersData: any[] = [];

if (search) {
let nameSearchQuery = supabase.from("members").select(baseSelectStatement);
let skillSearchQuery = supabase.from("members").select(baseSelectStatement);
nameSearchQuery = nameSearchQuery.ilike('name', `%${search}%`);
skillSearchQuery = skillSearchQuery.ilike('member_skills.skills.name', `%${search}%`);

if (domains.length > 0) {
nameSearchQuery = nameSearchQuery.in("domain", domains);
skillSearchQuery = skillSearchQuery.in("domain", domains);
}

if (allMappedYears.length > 0) {
nameSearchQuery = nameSearchQuery.in("year_of_study", allMappedYears);
skillSearchQuery = skillSearchQuery.in("year_of_study", allMappedYears);
}

if (skills.length > 0) {
nameSearchQuery = nameSearchQuery.in('member_skills.skills.name', skills);
skillSearchQuery = skillSearchQuery.in('member_skills.skills.name', skills);
}

const [nameResult, skillResult] = await Promise.all([nameSearchQuery, skillSearchQuery]);

if (nameResult.error) {
console.error("Supabase name search error:", nameResult.error);
throw new Error(nameResult.error.message);
}
if (skillResult.error) {
console.error("Supabase skill search error:", skillResult.error);
throw new Error(skillResult.error.message);
}

const uniqueResultsMap = new Map();
[...(nameResult.data || []), ...(skillResult.data || [])].forEach(member => {
uniqueResultsMap.set(member.id, member);
});
fetchedMembersData = Array.from(uniqueResultsMap.values());

} else {
let baseQuery = supabase.from("members").select(baseSelectStatement);

if (domains.length > 0) {
baseQuery = baseQuery.in("domain", domains);
}

if (allMappedYears.length > 0) {
baseQuery = baseQuery.in("year_of_study", allMappedYears);
}

if (skills.length > 0) {
baseQuery = baseQuery.in('member_skills.skills.name', skills);
}

const { data, error } = await baseQuery;
if (error) {
console.error("Supabase base query (no search) error:", error);
throw new Error(error.message);
}
fetchedMembersData = data || [];
}

const formattedResults = fetchedMembersData.map((member: any) => ({
...member,
skills: member.member_skills.map((ms: any) => ms.skills.name),
year_of_study: member.year_of_study
}));

const response = NextResponse.json({
success: true,
results,
count: results.length,
results: formattedResults,
count: formattedResults.length
});


response.headers.set('Cache-Control','no-cache,no-store,must-revalidate,max-age=0');
response.headers.set('Pragma','no-cache');
response.headers.set('Expires','0');
Expand All @@ -70,4 +129,4 @@ export async function GET(request: NextRequest) {

return errorResponse;
}
}
}
Loading