-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
13 changed files
with
1,089 additions
and
742 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,38 +16,38 @@ | |
"@formkit/auto-animate": "^0.8.2", | ||
"@heroicons/react": "^2.1.3", | ||
"@js-temporal/polyfill": "^0.4.4", | ||
"@mui/base": "^5.0.0-beta.42", | ||
"@next/third-parties": "^14.2.2", | ||
"@mui/base": "^5.0.0-beta.43", | ||
"@next/third-parties": "^14.2.3", | ||
"@tailwindcss/container-queries": "^0.1.1", | ||
"@types/node": "^20.12.7", | ||
"@types/react": "^18.2.79", | ||
"@types/react-dom": "^18.2.25", | ||
"@types/node": "^20.12.8", | ||
"@types/react": "^18.3.1", | ||
"@types/react-dom": "^18.3.0", | ||
"algoliasearch": "^4.23.3", | ||
"autoprefixer": "^10.4.19", | ||
"axios": "^1.6.8", | ||
"clsx": "^2.1.0", | ||
"decanter": "^7.2.0", | ||
"clsx": "^2.1.1", | ||
"decanter": "^7.3.0", | ||
"drupal-jsonapi-params": "^2.3.1", | ||
"eslint": "^8.57.0", | ||
"eslint-config-next": "^14.2.2", | ||
"eslint-config-next": "^14.2.3", | ||
"graphql": "^16.8.1", | ||
"graphql-request": "^6.1.0", | ||
"graphql-tag": "^2.12.6", | ||
"html-entities": "^2.5.2", | ||
"html-react-parser": "^5.1.10", | ||
"next": "^14.2.2", | ||
"next": "^14.2.3", | ||
"next-drupal": "^1.6.0", | ||
"postcss": "^8.4.38", | ||
"qs": "^6.12.1", | ||
"react": "^18.2.0", | ||
"react-dom": "^18.2.0", | ||
"react-focus-lock": "^2.11.3", | ||
"react-instantsearch": "^7.7.2", | ||
"react-instantsearch-nextjs": "^0.2.1", | ||
"react": "^18.3.1", | ||
"react-dom": "^18.3.1", | ||
"react-focus-lock": "^2.12.1", | ||
"react-instantsearch": "^7.7.3", | ||
"react-instantsearch-nextjs": "^0.2.2", | ||
"react-slick": "^0.30.2", | ||
"react-tiny-oembed": "^1.1.0", | ||
"sharp": "^0.33.3", | ||
"tailwind-merge": "^2.2.2", | ||
"tailwind-merge": "^2.3.0", | ||
"tailwindcss": "^3.4.3", | ||
"typescript": "^5.4.5", | ||
"usehooks-ts": "^3.1.0" | ||
|
@@ -58,23 +58,23 @@ | |
"@graphql-codegen/import-types-preset": "^3.0.0", | ||
"@graphql-codegen/typescript-graphql-request": "^6.2.0", | ||
"@graphql-codegen/typescript-operations": "^4.2.0", | ||
"@next/bundle-analyzer": "^14.2.2", | ||
"@storybook/addon-essentials": "^8.0.8", | ||
"@storybook/addon-interactions": "^8.0.8", | ||
"@storybook/addon-links": "^8.0.8", | ||
"@next/bundle-analyzer": "^14.2.3", | ||
"@storybook/addon-essentials": "^8.0.9", | ||
"@storybook/addon-interactions": "^8.0.9", | ||
"@storybook/addon-links": "^8.0.9", | ||
"@storybook/addon-styling": "^1.3.7", | ||
"@storybook/blocks": "^8.0.8", | ||
"@storybook/nextjs": "^8.0.8", | ||
"@storybook/react": "^8.0.8", | ||
"@storybook/blocks": "^8.0.9", | ||
"@storybook/nextjs": "^8.0.9", | ||
"@storybook/react": "^8.0.9", | ||
"@storybook/testing-library": "^0.2.2", | ||
"@types/react-slick": "^0.23.13", | ||
"concurrently": "^8.2.2", | ||
"encoding": "^0.1.13", | ||
"eslint-plugin-deprecation": "^2.0.0", | ||
"eslint-plugin-storybook": "^0.8.0", | ||
"eslint-plugin-unused-imports": "^3.1.0", | ||
"eslint-plugin-unused-imports": "^3.2.0", | ||
"react-docgen": "^7.0.3", | ||
"storybook": "^8.0.8", | ||
"storybook": "^8.0.9", | ||
"tsconfig-paths-webpack-plugin": "^4.1.0" | ||
}, | ||
"packageManager": "[email protected]" | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
38 changes: 38 additions & 0 deletions
38
src/components/paragraphs/sum-calculator/sum-calculator-paragraph.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import {HtmlHTMLAttributes} from "react"; | ||
import {ParagraphSumCalculator} from "@lib/gql/__generated__/drupal.d"; | ||
import SumCalculator from "@components/paragraphs/sum-calculator/sum-calculator"; | ||
|
||
type Props = HtmlHTMLAttributes<HTMLDivElement> & { | ||
paragraph: ParagraphSumCalculator | ||
} | ||
|
||
const SumCalculatorParagraph = ({paragraph, ...props}: Props) => { | ||
|
||
const costPerStudentType = new Map<string, Map<number, number>>(); | ||
costPerStudentType.set("undergraduate", new Map(paragraph.sumCalcUgUnitCost.map(cost => [cost.first as number, cost.second as number]))) | ||
costPerStudentType.set("highschool", new Map(paragraph.sumCalcHighUnitCost.map(cost => [cost.first as number, cost.second as number]))) | ||
costPerStudentType.set("graduate", new Map(paragraph.sumCalcGradUnitCost.map(cost => [cost.first as number, cost.second as number]))) | ||
|
||
const appCosts =new Map(paragraph.sumCalcAppFee.map(item => [item.first as string, item.second as number])); | ||
const progCosts =new Map(paragraph.sumCalcProgFee.map(item => [item.first as string, item.second as number])); | ||
const housingCosts =new Map(paragraph.sumCalcHouseFees.map(item => [item.first as string, item.second as number])); | ||
|
||
return ( | ||
<SumCalculator | ||
costPerStudentTypes={costPerStudentType} | ||
appCosts={appCosts} | ||
progCosts={progCosts} | ||
i20Cost={paragraph.sumCalcI20Fee} | ||
housingCosts={housingCosts} | ||
mealPlanCost={paragraph.sumCalcMeals} | ||
techCost={paragraph.sumCalcTechFee} | ||
mailCost={paragraph.sumCalcMailFee} | ||
insuranceCost={paragraph.sumCalcInsurance} | ||
booksSuppliesCost={paragraph.sumCalcBooks} | ||
healthServiceCost={paragraph.sumCalcHealthFee} | ||
documentsCost={paragraph.sumCalcDocuments} | ||
{...props} | ||
/> | ||
) | ||
} | ||
export default SumCalculatorParagraph |
226 changes: 226 additions & 0 deletions
226
src/components/paragraphs/sum-calculator/sum-calculator.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,226 @@ | ||
"use client"; | ||
|
||
import {HtmlHTMLAttributes, useId, useState} from "react"; | ||
import SelectList from "@components/elements/select-list"; | ||
import {H2, H3} from "@components/elements/headers"; | ||
import {useBoolean, useCounter} from "usehooks-ts"; | ||
import {formatCurrency} from "@lib/utils/format-currency"; | ||
import useAccordion from "@lib/hooks/useAccordion"; | ||
import {clsx} from "clsx"; | ||
import {ChevronDownIcon} from "@heroicons/react/20/solid"; | ||
|
||
type Props = HtmlHTMLAttributes<HTMLDivElement> & { | ||
costPerStudentTypes: Map<string, Map<number, number>> | ||
appCosts: Map<string, number> | ||
progCosts: Map<string, number> | ||
i20Cost: number | ||
housingCosts: Map<string, number> | ||
mealPlanCost: number | ||
techCost: number | ||
mailCost: number | ||
insuranceCost: number | ||
booksSuppliesCost: number | ||
healthServiceCost: number | ||
documentsCost: number | ||
} | ||
|
||
const SumCalculatorParagraph = ({ | ||
costPerStudentTypes, | ||
appCosts, | ||
progCosts, | ||
i20Cost, | ||
housingCosts, | ||
mealPlanCost, | ||
techCost, | ||
mailCost, | ||
insuranceCost, | ||
booksSuppliesCost, | ||
healthServiceCost, | ||
documentsCost, | ||
...props | ||
}: Props) => { | ||
const id = useId(); | ||
|
||
const [studentType, setStudentType] = useState("") | ||
const {value: needsI20, setValue: setNeedsI20} = useBoolean(false) | ||
const {value: onCampus, setValue: setOnCampus} = useBoolean(false) | ||
const {count: units, setCount: setUnits} = useCounter(0) | ||
const {value: waivingInsurance, setValue: setWaivingInsurance} = useBoolean(true) | ||
|
||
const {buttonProps, panelProps, expanded} = useAccordion(); | ||
|
||
const appFee = appCosts.get(studentType) || 0 | ||
const progFee = progCosts.get(studentType) || 0 | ||
const i20Fee = needsI20 ? i20Cost : 0 | ||
const housingFee = onCampus ? housingCosts.get(studentType) || 0 : 0 | ||
const mealPlan = onCampus ? mealPlanCost : 0 | ||
const techFee = onCampus ? techCost : 0 | ||
const mailFee = onCampus ? mailCost : 0 | ||
const insurance = waivingInsurance ? 0 : insuranceCost | ||
|
||
const unitsCost = costPerStudentTypes.get(studentType)?.get(units) || 0 | ||
|
||
const totalCost = appFee + progFee + i20Fee + housingFee + mealPlan + techFee + mailFee + insurance + unitsCost + booksSuppliesCost + healthServiceCost + documentsCost; | ||
const unitOptions: { value: string, label: string }[] = []; | ||
|
||
for (let i = 3; i <= 20; i++) { | ||
unitOptions.push({value: `${i}`, label: `${i}`}) | ||
} | ||
|
||
return ( | ||
<div {...props}> | ||
<div className="max-w-7xl mx-auto pb-72"> | ||
<div> | ||
<div id={`${id}-type`}>I am a/an _________ student</div> | ||
|
||
<SelectList | ||
ariaLabelledby={`${id}-type`} | ||
options={[ | ||
{value: "undergraduate", label: "Undergraduate"}, | ||
{value: "highschool", label: "High School"}, | ||
{value: "graduate", label: "Graduate"} | ||
]} | ||
onChange={(_e, value) => setStudentType(value as string)} | ||
required | ||
/> | ||
</div> | ||
|
||
<div> | ||
<div id={`${id}-i20`}>Are you an international student that requires a Stanford issued I-20?</div> | ||
|
||
<SelectList | ||
ariaLabelledby={`${id}-i20`} | ||
options={[ | ||
{value: "yes", label: "Yes, I am an international student requiring a Stanford Sponsored I-20"}, | ||
{value: "no1", label: "No, I am a US citizen or permanent US resident"}, | ||
{value: "no2", label: "No, I am an international student with an I-20 sponsored by another institution"} | ||
]} | ||
onChange={(_e, value) => setNeedsI20(value === "yes")} | ||
required | ||
disabled={!studentType} | ||
/> | ||
</div> | ||
|
||
<div> | ||
<div id={`${id}-housing`}>Will you be living on campus?</div> | ||
<SelectList | ||
ariaLabelledby={`${id}-housing`} | ||
options={[ | ||
{value: "yes", label: "On-Campus"}, | ||
{value: "no", label: "Living off campus and commuting"}, | ||
]} | ||
onChange={(_e, value) => setOnCampus(value === "yes")} | ||
required | ||
disabled={!studentType} | ||
/> | ||
</div> | ||
|
||
<div> | ||
<div id={`${id}-units`}>How many units will you be taking?</div> | ||
|
||
<SelectList | ||
ariaLabelledby={`${id}-units`} | ||
options={unitOptions} | ||
onChange={(_e, value) => setUnits(parseInt(value as string))} | ||
required | ||
disabled={!studentType} | ||
/> | ||
</div> | ||
|
||
<div> | ||
<div id={`${id}-insurance`}>Will you be waiving Cardinal Care Health Insurance?</div> | ||
|
||
<SelectList | ||
ariaLabelledby={`${id}-insurance`} | ||
options={[ | ||
{value: "yes", label: "Yes, I will be waiving Cardinal Care."}, | ||
{value: "no", label: "No, I would like to stay enrolled in Cardinal Care Health Insurance"}, | ||
]} | ||
onChange={(_e, value) => setWaivingInsurance(value === "yes")} | ||
required | ||
disabled={!studentType} | ||
/> | ||
|
||
</div> | ||
</div> | ||
|
||
<div className="absolute bottom-0 bg-black-10 w-screen left-1/2 -translate-x-1/2"> | ||
<div className="max-w-7xl mx-auto"> | ||
<H2 className="flex justify-between" aria-live="polite" aria-atomic> | ||
<span>Estimated total cost</span> | ||
<span>{formatCurrency(totalCost)}</span> | ||
</H2> | ||
<p className="text-left">* Disclaimer: this is only an estimate. Actual fees are subject to change.</p> | ||
|
||
<div {...panelProps} className={clsx({"hidden": !expanded})}> | ||
|
||
|
||
{!!units && | ||
<div> | ||
<div className="flex items-baseline gap-5"><H3>Tuition</H3>(Based on the total number of units)</div> | ||
<SummaryCost label={`${units} Units`} cost={unitsCost}/> | ||
</div> | ||
} | ||
|
||
{studentType && | ||
<div> | ||
<H3>General Fees</H3> | ||
<SummaryCost label="Application Fee" cost={appFee}/> | ||
<SummaryCost label="Program Fee" cost={progFee}/> | ||
<SummaryCost label="Campus Health Services Fee" cost={healthServiceCost}/> | ||
{needsI20 && | ||
<SummaryCost label="I-20 Processing Fee" cost={i20Fee}/> | ||
} | ||
</div> | ||
} | ||
|
||
{onCampus && | ||
<div> | ||
<H3>On-campus Fees</H3> | ||
<SummaryCost label="Housing Fee" cost={housingFee}/> | ||
<SummaryCost label="Meal Plan" cost={mealPlan}/> | ||
<SummaryCost label="Mail Service Fee" cost={mailFee}/> | ||
<SummaryCost label="Technology Fee " cost={techFee}/> | ||
</div> | ||
} | ||
|
||
|
||
<div> | ||
<H3>Extra Fees</H3> | ||
<SummaryCost label="Books and Supplies (optional)" cost={booksSuppliesCost}/> | ||
<SummaryCost label="Document Fee" cost={documentsCost}/> | ||
{!waivingInsurance && | ||
<SummaryCost label="Cardinal Care Health Insurance optional" cost={insurance}/> | ||
} | ||
</div> | ||
|
||
<div> | ||
<p className="text-m4 flex justify-between"> | ||
<span>Estimated total cost</span> <span>{formatCurrency(totalCost)}</span> | ||
</p> | ||
|
||
<p className="text-left">* Disclaimer: this is only an estimate. Actual fees are subject to change.</p> | ||
</div> | ||
</div> | ||
|
||
<button {...buttonProps} className="block ml-auto"> | ||
<span className="flex items-center"> | ||
{expanded ? "Close" : "See"} details | ||
<ChevronDownIcon width={20} className={clsx("transition duration-150", {"rotate-180": expanded})}/> | ||
</span> | ||
</button> | ||
</div> | ||
</div> | ||
</div> | ||
) | ||
} | ||
|
||
const SummaryCost = ({label, cost}: { label: string, cost: number }) => { | ||
return ( | ||
<div className="flex items-center justify-between"> | ||
<span>{label}</span><span>{formatCurrency(cost)}</span> | ||
</div> | ||
) | ||
} | ||
|
||
export default SumCalculatorParagraph |
Oops, something went wrong.