That loop is the whole 15 seconds. One query for the list plus two per row is 241 round trips for 120 applicants, and each one pays the network latency to the database.
Fetch it in one query and let Postgres do the joining:
const { data } = await supabase.from('applications')
.select('id, status, created_at, plots(number), profiles(full_name)')
.eq('season', 2026)That is a single request and it will come back in tens of milliseconds. Then add create index on applications (season, status) so the season filter does not scan three years of rows. Three seasons is small enough that the index is a nicety today and the join is the real fix.