diff --git a/src/component/ApplyPage/Position/index.jsx b/src/component/ApplyPage/Position/index.jsx
deleted file mode 100644
index d2d94c1f..00000000
--- a/src/component/ApplyPage/Position/index.jsx
+++ /dev/null
@@ -1,213 +0,0 @@
-import React, { useState } from 'react'
-import { useParams } from 'react-router-dom'
-import Container from 'react-bootstrap/Container'
-import Col from 'react-bootstrap/Col'
-import Row from 'react-bootstrap/Row'
-import ShadowedButton from '../../ShadowedButton'
-import './style.css'
-import applicationsByTeams from '../../../data/ApplyData/allApps'
-import useWebsite from '../../../shared/useWebsite'
-import NavBar from '../../NavBar'
-
-export default function Position() {
-
- const isWebsite = useWebsite()
- const isBigScreen = !window.matchMedia('(max-device-width: 650px)').matches
- const mobile = !isBigScreen || !isWebsite
-
- const { categoryType, index } = useParams()
- const [position, ] = useState(
- applicationsByTeams[categoryType][index]
- )
- const [showText, setShowText] = useState('Share')
-
- const parseList = (stringList) => {
- if (stringList) {
- return stringList.split('.')
- } else {
- return []
- }
- }
-
- const copyShareLink = () => {
- console.log(window.location.href)
- navigator.clipboard.writeText(window.location.href)
- setShowText('Copied!')
- }
-
- const handleApply = () => {
- window.location.assign(position.applicationLink)
- }
-
- if (position) {
- return !mobile ? (
-
-
- {/* Left */}
-
-
-
- {position.positionTitle}
-
- {position.categoryType}
-
-
-
-
-
- {(position.active && (
-
- )) ||
- (!position.active && (
- console.log('not implemented')}
- />
- ))}
-
-
-
-
-
-
- {/* Right */}
-
- {/* Link back to list of positions */}
-
- < -- positions
-
-
- {/* Position summary */}
- {position.description}
-
- {/* Position responsibilities */}
- {position.responsibilities && (
- What you’ll do
- )}
-
- {parseList(position.responsibilities).map((info, index) => (
-
- {info}
-
- ))}
-
-
- {/* Position qualifications */}
- {position.requirements && (
- Requirements
- )}
-
- {parseList(position.requirements).map((info, index) => (
- {info}
- ))}
-
-
- {/* Position timeline */}
-
-
- Duration
-
- {position.startDate} to {position.endDate}
-
-
-
- Weekly commitment
- {`Up to ${position.workCommitment} hours`}
-
-
-
-
-
- {position.remarks}
-
-
-
- {/* Link to information about position type */}
-
- learn more -- >
-
-
-
-
- ) : (
-
-
-
-
-
-
{position.positionTitle}
-
{position.categoryType}
-
-
- {'apply now'}
-
-
- {'share'}
-
-
-
-
-
- < -- positions
-
-
{position.description}
-
What you'll do
-
- {parseList(position.responsibilities).map((info, index) => (
-
- {info}
-
- ))}
-
-
Requirements
-
- {parseList(position.requirements).map((info, index) => (
-
- {info}
-
- ))}
-
-
-
-
Duration
-
- {position.startDate} to {position.endDate}
-
-
-
-
Weekly Commitment
-
- {`Up to ${position.workCommitment} hours`}
-
-
-
-
{position.remarks}
-
- learn more -- >
-
-
-
- )
- } else {
- return null
- }
-}
diff --git a/src/component/ApplyPage/Position/index.tsx b/src/component/ApplyPage/Position/index.tsx
new file mode 100644
index 00000000..527f312d
--- /dev/null
+++ b/src/component/ApplyPage/Position/index.tsx
@@ -0,0 +1,211 @@
+import React, { useState } from 'react';
+import Col from 'react-bootstrap/Col';
+import Container from 'react-bootstrap/Container';
+import Row from 'react-bootstrap/Row';
+import { useParams } from 'react-router-dom';
+
+import applicationsByTeams from '../../../data/ApplyData/allApps';
+import './style.css';
+import useWebsite from '../../../shared/useWebsite';
+import NavBar from '../../NavBar';
+import ShadowedButton from '../../ShadowedButton';
+
+type ApplicationGroup = keyof typeof applicationsByTeams;
+
+const isApplicationGroup = (value: string | undefined): value is ApplicationGroup =>
+ value !== undefined && Object.hasOwn(applicationsByTeams, value);
+
+export default function Position() {
+ const isWebsite = useWebsite();
+ const isBigScreen = !window.matchMedia('(max-device-width: 650px)').matches;
+ const mobile = !isBigScreen || !isWebsite;
+
+ const { categoryType, index } = useParams();
+
+ const [position] = useState(
+ isApplicationGroup(categoryType) ? applicationsByTeams[categoryType][Number(index)] : undefined
+ );
+
+ const [showText, setShowText] = useState('Share');
+
+ const parseList = (stringList: string) => {
+ if (stringList) {
+ return stringList.split('.');
+ } else {
+ return [];
+ }
+ };
+
+ const copyShareLink = () => {
+ console.log(window.location.href);
+ navigator.clipboard.writeText(window.location.href);
+ setShowText('Copied!');
+ };
+
+ const handleApply = () => {
+ if (position) {
+ window.location.assign(position.applicationLink);
+ }
+ };
+
+ if (position) {
+ return !mobile ? (
+
+
+ {/* Left */}
+
+
+
+ {position.positionTitle}
+ {position.categoryType}
+
+
+
+
+ {(position.active && (
+
+ )) ||
+ (!position.active && (
+ console.log('not implemented')}
+ />
+ ))}
+
+
+
+
+
+
+ {/* Right */}
+
+ {/* Link back to list of positions */}
+
+ < -- positions
+
+
+ {/* Position summary */}
+ {position.description}
+
+ {/* Position responsibilities */}
+ {position.responsibilities && What you’ll do }
+
+ {parseList(position.responsibilities).map((info, index) => (
+
+ {info}
+
+ ))}
+
+
+ {/* Position qualifications */}
+ {position.requirements && Requirements }
+
+ {parseList(position.requirements).map((info, index) => (
+ {info}
+ ))}
+
+
+ {/* Position timeline */}
+
+
+ Duration
+
+ {position.startDate} to {position.endDate}
+
+
+
+ Weekly commitment
+ {`Up to ${position.workCommitment} hours`}
+
+
+
+
+
+ {position.remarks}
+
+
+
+ {/* Link to information about position type */}
+
+ learn more -- >
+
+
+
+
+ ) : (
+
+
+
+
+
+
{position.positionTitle}
+
{position.categoryType}
+
+
+ {'apply now'}
+
+
+ {'share'}
+
+
+
+
+
+ < -- positions
+
+
{position.description}
+
What you'll do
+
+ {parseList(position.responsibilities).map((info, index) => (
+
+ {info}
+
+ ))}
+
+
Requirements
+
+ {parseList(position.requirements).map((info, index) => (
+
+ {info}
+
+ ))}
+
+
+
+
Duration
+
+ {position.startDate} to {position.endDate}
+
+
+
+
Weekly Commitment
+
{`Up to ${position.workCommitment} hours`}
+
+
+
{position.remarks}
+
+ learn more -- >
+
+
+
+ );
+ } else {
+ return null;
+ }
+}
diff --git a/src/pages/ApplyPageV2/RoleCategory/index.jsx b/src/pages/ApplyPageV2/RoleCategory/index.jsx
deleted file mode 100644
index 1994ba7f..00000000
--- a/src/pages/ApplyPageV2/RoleCategory/index.jsx
+++ /dev/null
@@ -1,48 +0,0 @@
-import './style.css'
-
-export default function RoleCategory({
- roleCategory: { name = '', description = '', roles = [], color = '' } = {}
-}) {
- const halfLength = Math.ceil(roles.length / 2)
- const firstColumn = roles.slice(0, halfLength)
- const secondColumn = roles.slice(halfLength)
-
- return (
-
-
{name}
-
- {description}
-
-
-
- )
-}
diff --git a/src/pages/ApplyPageV2/RoleCategory/index.tsx b/src/pages/ApplyPageV2/RoleCategory/index.tsx
new file mode 100644
index 00000000..c654d745
--- /dev/null
+++ b/src/pages/ApplyPageV2/RoleCategory/index.tsx
@@ -0,0 +1,60 @@
+import type { ReactNode } from 'react';
+
+import './style.css';
+import type { ApplicationRole } from '../types';
+
+type RoleCategoryProps = {
+ roleCategory?: {
+ name?: string;
+ description?: ReactNode;
+ roles?: ApplicationRole[];
+ color?: string;
+ };
+};
+
+export default function RoleCategory({
+ roleCategory: { name = '', description = '', roles = [], color = '' } = {},
+}: RoleCategoryProps) {
+ const halfLength = Math.ceil(roles.length / 2);
+ const firstColumn = roles.slice(0, halfLength);
+ const secondColumn = roles.slice(halfLength);
+
+ return (
+
+
{name}
+
+ {description}
+
+
+
+ );
+}
diff --git a/src/pages/ApplyPageV2/Tag/index.jsx b/src/pages/ApplyPageV2/Tag/index.jsx
deleted file mode 100644
index 3df48327..00000000
--- a/src/pages/ApplyPageV2/Tag/index.jsx
+++ /dev/null
@@ -1,9 +0,0 @@
-import './style.css'
-
-export default function Tag({ title }) {
- return (
-
- )
-}
diff --git a/src/pages/ApplyPageV2/Tag/index.tsx b/src/pages/ApplyPageV2/Tag/index.tsx
new file mode 100644
index 00000000..5690d832
--- /dev/null
+++ b/src/pages/ApplyPageV2/Tag/index.tsx
@@ -0,0 +1,13 @@
+import './style.css';
+
+type TagProps = {
+ title: string;
+};
+
+export default function Tag({ title }: TagProps) {
+ return (
+
+ );
+}
diff --git a/src/pages/ApplyPageV2/TeamApplicationCard/index.jsx b/src/pages/ApplyPageV2/TeamApplicationCard/index.tsx
similarity index 95%
rename from src/pages/ApplyPageV2/TeamApplicationCard/index.jsx
rename to src/pages/ApplyPageV2/TeamApplicationCard/index.tsx
index 6c66f507..4e8ec2b7 100644
--- a/src/pages/ApplyPageV2/TeamApplicationCard/index.jsx
+++ b/src/pages/ApplyPageV2/TeamApplicationCard/index.tsx
@@ -3,11 +3,16 @@ import React, { useState } from 'react';
import RightArrow from '../../../assets/images/applypage-v2/RightArrow';
import WhiteDownArrow from '../../../assets/images/applypage-v2/WhiteDownArrow.svg?react';
import WhiteUpArrow from '../../../assets/images/applypage-v2/WhiteUpArrow.svg?react';
-import RoleCategory from '../RoleCategory/index.jsx';
-import Tag from '../Tag/index.jsx';
+import RoleCategory from '../RoleCategory';
+import Tag from '../Tag';
import './style.css';
+import type { ApplyTeam } from '../types';
-export default function TeamApplicationCard({ team }) {
+type TeamApplicationCardProps = {
+ team: ApplyTeam;
+};
+
+export default function TeamApplicationCard({ team }: TeamApplicationCardProps) {
const [expanded, setExpanded] = useState(false);
const expand = team.externalLink === undefined;
const clientCard = team.team === 'Clients';
diff --git a/src/pages/ApplyPageV2/index.jsx b/src/pages/ApplyPageV2/index.jsx
deleted file mode 100644
index a8a55ffe..00000000
--- a/src/pages/ApplyPageV2/index.jsx
+++ /dev/null
@@ -1,99 +0,0 @@
-import React from 'react'
-import NavBar from '../../component/NavBar'
-import TeamApplicationCard from './TeamApplicationCard'
-import Footer from '../LandingPageV3/Footer'
-import { useSanity } from '../../services/useSanity'
-import useWebsite from '../../shared/useWebsite'
-import './style.css'
-
-const header = 'join generate'
-const quote =
- "We're always looking for passionate individuals who are ready to learn and grow. We have opportunities that run across interests and experience levels. If the position you're hoping for isn't available right now, we re-open applications prior to each semester so check back soon!"
-
-const desktopContent = (firstColumn, secondColumn) => {
- return (
- <>
-
-
-
-
-
-
{header}
-
{quote}
-
-
- {firstColumn.map((team) => (
-
- ))}
-
-
- {secondColumn.map((team) => (
-
- ))}
-
-
-
-
- >
- )
-}
-
-const mobileContent = (teams) => {
- return (
- <>
-
-
-
-
-
-
{header}
-
{quote}
-
- {teams.map((team) => (
-
- ))}
-
-
-
- >
- )
-}
-
-export default function ApplyPage() {
- const query = `*[_type == "team"] {
- ...,
- "contributorRoles": contributorRoles[]-> {
- role,
- applicationLink,
- activeApplication
- },
- "leadRoles": leadRoles[]-> {
- role,
- applicationLink,
- activeApplication
- },
- "chiefRoles": chiefRoles[]-> {
- role,
- applicationLink,
- activeApplication
- }
- }| order(zIndex desc)`
- const teams = useSanity(query, {}, (data) =>
- data
- ? data.map((team) => ({
- ...team,
- color: team.color.hex
- }))
- : []
- )
-
- const halfLength = Math.ceil(teams.length / 2)
- const leftCol = teams.slice(0, halfLength)
- const rightCol = teams.slice(halfLength)
-
- const isWebsite = useWebsite()
- const isBigScreen = !window.matchMedia('(max-device-width: 650px)').matches
- const mobile = !isBigScreen || !isWebsite
-
- return !mobile ? desktopContent(leftCol, rightCol) : mobileContent(teams)
-}
diff --git a/src/pages/ApplyPageV2/index.tsx b/src/pages/ApplyPageV2/index.tsx
new file mode 100644
index 00000000..24776bd4
--- /dev/null
+++ b/src/pages/ApplyPageV2/index.tsx
@@ -0,0 +1,103 @@
+import React from 'react';
+
+import NavBar from '../../component/NavBar';
+import { useSanity } from '../../services/useSanity';
+import useWebsite from '../../shared/useWebsite';
+import Footer from '../LandingPageV3/Footer';
+import TeamApplicationCard from './TeamApplicationCard';
+import './style.css';
+import type { ApplyTeam, SanityApplyTeam } from './types';
+
+const header = 'join generate';
+
+const quote =
+ "We're always looking for passionate individuals who are ready to learn and grow. We have opportunities that run across interests and experience levels. If the position you're hoping for isn't available right now, we re-open applications prior to each semester so check back soon!";
+
+const desktopContent = (firstColumn: ApplyTeam[], secondColumn: ApplyTeam[]) => {
+ return (
+ <>
+
+
+
+
+
+
{header}
+
{quote}
+
+
+ {firstColumn.map(team => (
+
+ ))}
+
+
+ {secondColumn.map(team => (
+
+ ))}
+
+
+
+
+ >
+ );
+};
+
+const mobileContent = (teams: ApplyTeam[]) => {
+ return (
+ <>
+
+
+
+
+
+
{header}
+
{quote}
+
+ {teams.map(team => (
+
+ ))}
+
+
+
+ >
+ );
+};
+
+export default function ApplyPage() {
+ const query = `*[_type == "team"] {
+ ...,
+ "contributorRoles": contributorRoles[]-> {
+ role,
+ applicationLink,
+ activeApplication
+ },
+ "leadRoles": leadRoles[]-> {
+ role,
+ applicationLink,
+ activeApplication
+ },
+ "chiefRoles": chiefRoles[]-> {
+ role,
+ applicationLink,
+ activeApplication
+ }
+ }| order(zIndex desc)`;
+
+ const teams = useSanity(query, {}, data =>
+ data
+ ? data.map(team => ({
+ ...team,
+ color: team.color.hex,
+ }))
+ : []
+ );
+
+ const halfLength = Math.ceil(teams.length / 2);
+ const leftCol = teams.slice(0, halfLength);
+ const rightCol = teams.slice(halfLength);
+
+ const isWebsite = useWebsite();
+ const isBigScreen = !window.matchMedia('(max-device-width: 650px)').matches;
+ const mobile = !isBigScreen || !isWebsite;
+
+ return !mobile ? desktopContent(leftCol, rightCol) : mobileContent(teams);
+}
diff --git a/src/pages/ApplyPageV2/types.ts b/src/pages/ApplyPageV2/types.ts
new file mode 100644
index 00000000..e33c7cca
--- /dev/null
+++ b/src/pages/ApplyPageV2/types.ts
@@ -0,0 +1,23 @@
+export type ApplicationRole = {
+ role: string;
+ applicationLink?: string;
+ activeApplication?: boolean;
+};
+
+export type SanityApplyTeam = {
+ team: string;
+ color: { hex: string };
+ tags?: string[];
+ teamDescription?: string;
+ contributorDescription?: string;
+ leadDescription?: string;
+ chiefDescription?: string;
+ contributorRoles?: ApplicationRole[] | null;
+ leadRoles?: ApplicationRole[] | null;
+ chiefRoles?: ApplicationRole[] | null;
+ externalLink?: string;
+};
+
+export type ApplyTeam = Omit & {
+ color: string;
+};