diff --git a/public/arrow-up-right.svg b/public/arrow-up-right.svg new file mode 100644 index 0000000..e2034c2 --- /dev/null +++ b/public/arrow-up-right.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/app/(frontend)/components/FAQItem.tsx b/src/app/(frontend)/components/FAQItem.tsx index 61e0a44..bb872b4 100644 --- a/src/app/(frontend)/components/FAQItem.tsx +++ b/src/app/(frontend)/components/FAQItem.tsx @@ -9,6 +9,7 @@ const FAQItem = ({ question, answer }: FAQItemProps) => {

{question}

+ {
-

{answer}

+

{answer}

) } diff --git a/src/app/(frontend)/components/FAQSection.tsx b/src/app/(frontend)/components/FAQSection.tsx index b82e9e9..a26c981 100644 --- a/src/app/(frontend)/components/FAQSection.tsx +++ b/src/app/(frontend)/components/FAQSection.tsx @@ -1,6 +1,5 @@ import FAQItem from './FAQItem' -// Sample FAQ data to be replaced with actual FAQs const faqData = [ { id: 1, @@ -52,11 +51,13 @@ const FAQSection = () => {

FAQs

+
{faqData.map((item) => ( ))}
+
) diff --git a/src/app/(frontend)/components/OpportunitySection.tsx b/src/app/(frontend)/components/OpportunitySection.tsx new file mode 100644 index 0000000..73d06d9 --- /dev/null +++ b/src/app/(frontend)/components/OpportunitySection.tsx @@ -0,0 +1,244 @@ +'use client' + +import { useEffect, useMemo, useState } from 'react' +import OpportunityTable from './OpportunityTable' + +// TODO: replace with real data from API + +const opportunities = [ + { + id: 1, + type: 'Scholarship', + title: 'Lodge of the Liberal Arts: Howard Wyatt Memorial Scholarship', + deadlineLabel: '20th of May, 11:59pm NZST', + deadlineDate: '2026-05-20T23:59:00+12:00', + description: + 'The Freemasons of Lodge No.500 have established a trust for charitable purposes, to assist young musicians in their education. Scholarships totalling $3,000 are granted each year to members of AYO who have shown outstanding...', + applyUrl: '#', + }, + { + id: 2, + type: 'Scholarship', + title: 'Chip and Muriel Stevens Award', + deadlineLabel: '20th of May, 11:59pm NZST', + deadlineDate: '2026-05-20T23:59:00+12:00', + description: + 'This $1,500 award is dedicated to the memory of a former Chairman of AYO, N.W. (Chip) Stevens, who spent his lifetime encouraging young people to love music and young musicians to reach their full potential.', + applyUrl: '#', + }, + { + id: 3, + type: 'Competition', + title: 'AYO Soloist Competition', + deadlineLabel: '15th of August, 11:59pm NZST', + deadlineDate: '2026-08-15T23:59:00+12:00', + description: + 'The AYO Soloist Competition offers existing orchestra members the chance to compete for monetary prizes and a concerto appearance with the orchestra. The orchestra showcases young soloists and composers; it...', + applyUrl: '#', + }, + { + id: 4, + type: 'Scholarship', + title: 'AYO International Performance Grant', + deadlineLabel: '1st of June, 11:59pm NZST', + deadlineDate: '2026-06-01T23:59:00+12:00', + description: + 'Supports orchestra members travelling internationally for advanced musical training and performance opportunities.', + applyUrl: '#', + }, + { + id: 5, + type: 'Competition', + title: 'Emerging Composer Competition', + deadlineLabel: '10th of July, 11:59pm NZST', + deadlineDate: '2026-07-10T23:59:00+12:00', + description: + 'Young composers are invited to submit original orchestral works for adjudication and potential live performance.', + applyUrl: '#', + }, + { + id: 6, + type: 'Workshop', + title: 'Conducting Masterclass Programme', + deadlineLabel: '5th of June, 11:59pm NZST', + deadlineDate: '2026-06-05T23:59:00+12:00', + description: + 'A practical workshop series led by professional conductors focusing on rehearsal technique, score preparation, and ensemble leadership.', + applyUrl: '#', + }, + { + id: 7, + type: 'Scholarship', + title: 'Regional Music Development Scholarship', + deadlineLabel: '25th of May, 11:59pm NZST', + deadlineDate: '2026-05-25T23:59:00+12:00', + description: + 'Financial assistance for students from regional communities pursuing advanced orchestral studies.', + applyUrl: '#', + }, + { + id: 8, + type: 'Competition', + title: 'Chamber Ensemble Showcase', + deadlineLabel: '30th of September, 11:59pm NZST', + deadlineDate: '2026-09-30T23:59:00+12:00', + description: + 'Small ensembles compete for performance opportunities during the annual AYO concert season.', + applyUrl: '#', + }, + { + id: 9, + type: 'Residency', + title: 'Composer-in-Residence Programme', + deadlineLabel: '18th of August, 11:59pm NZST', + deadlineDate: '2026-08-18T23:59:00+12:00', + description: + 'Selected applicants will collaborate directly with the orchestra over a six-month residency period developing new compositions.', + applyUrl: '#', + }, + { + id: 10, + type: 'Workshop', + title: 'Advanced Audition Preparation Intensive', + deadlineLabel: '12th of June, 11:59pm NZST', + deadlineDate: '2026-06-12T23:59:00+12:00', + description: + 'An intensive coaching programme helping musicians prepare orchestral excerpts, solo repertoire, and audition strategies.', + applyUrl: '#', + }, +] + +export default function OpportunitySection() { + const [sortOrder, setSortOrder] = useState<'asc' | 'desc'>('asc') + const [selectedType, setSelectedType] = useState('All') + const [showCount, setShowCount] = useState(5) + const [currentPage, setCurrentPage] = useState(1) + + // Dynamically generate available types + const opportunityTypes = ['All', ...new Set(opportunities.map((opp) => opp.type))] + + // Filter opportunities + const filteredOpportunities = + selectedType === 'All' + ? opportunities + : opportunities.filter((opp) => opp.type === selectedType) + + // Sort opportunities + const sortedOpportunities = useMemo(() => { + return [...filteredOpportunities].sort((a, b) => { + return sortOrder === 'asc' + ? new Date(a.deadlineDate).getTime() - new Date(b.deadlineDate).getTime() + : new Date(b.deadlineDate).getTime() - new Date(a.deadlineDate).getTime() + }) + }, [filteredOpportunities, sortOrder]) + + // Reset page when controls change + useEffect(() => { + setCurrentPage(1) + }, [selectedType, sortOrder, showCount]) + + // Pagination + const totalPages = Math.ceil(sortedOpportunities.length / showCount) + + const paginatedOpportunities = sortedOpportunities.slice( + (currentPage - 1) * showCount, + currentPage * showCount, + ) + + return ( +
+
+

Opportunities

+ +

+ There are a range of opportunities we offer, exclusively to AYO players. +

+ + {/* Controls */} +
+ {/* Type */} +
+ + + +
+ + {/* Sort */} +
+ + + +
+ + {/* Show */} +
+ + + +
+ + {/* Count */} + + Showing {paginatedOpportunities.length}{' '} + {paginatedOpportunities.length === 1 ? 'opportunity' : 'opportunities'} + +
+ +
+ + {/* Table */} + + + {/* Pagination */} +
+
+ + + +
+ + + {currentPage} of {totalPages} + +
+
+
+ ) +} diff --git a/src/app/(frontend)/components/OpportunityTable.tsx b/src/app/(frontend)/components/OpportunityTable.tsx new file mode 100644 index 0000000..6821b7f --- /dev/null +++ b/src/app/(frontend)/components/OpportunityTable.tsx @@ -0,0 +1,85 @@ +'use client' + +import { useState } from 'react' +import OpportunityModal from './OpportunityModal' +import ArrowUpRight from '/arrow-up-right.svg' + +type Opportunity = { + id: number + type: string + title: string + deadlineLabel: string + deadlineDate: string + description: string + applyUrl: string +} + +type OpportunityTableProps = { + opportunities: Opportunity[] +} + +type OpportunityRowProps = Opportunity & { + onReadMore: () => void +} + +const OpportunityRow = ({ + title, + deadlineLabel, + description, + applyUrl, + onReadMore, +}: OpportunityRowProps) => { + return ( +
+
+

{title}

+ +

Apply by {deadlineLabel}

+
+ +

{description}

+ +
+ + + + Apply + + +
+
+ ) +} + +const OpportunityTable = ({ opportunities }: OpportunityTableProps) => { + const [selectedOpp, setSelectedOpp] = useState(null) + + return ( +
+ {opportunities.map((opp, index) => ( +
+ {index > 0 &&
} + + setSelectedOpp(opp)} /> +
+ ))} + + {/* Modal */} + {selectedOpp && ( + setSelectedOpp(null)} + /> + )} +
+ ) +} + +export default OpportunityTable diff --git a/src/app/(frontend)/join-ayo/page.tsx b/src/app/(frontend)/join-ayo/page.tsx index 70e9e10..2a79da4 100644 --- a/src/app/(frontend)/join-ayo/page.tsx +++ b/src/app/(frontend)/join-ayo/page.tsx @@ -1,9 +1,11 @@ import FAQSection from '../components/FAQSection' +import OpportunitySection from '../components/OpportunitySection' +import OpportunityModal from '../components/OpportunityModal' export default function JoinAyoPage() { return (
- {/*

This is the join AYO page

*/} +
)