diff --git a/eslint.config.mjs b/eslint.config.mjs index 1a0a550..1e6dc86 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -33,6 +33,16 @@ export default tseslint.config( "unicorn/prevent-abbreviations": "off", "unicorn/no-null": "off", + "unicorn/filename-case": [ + "error", + { + cases: { + kebabCase: true, + pascalCase: true, + }, + }, + ], + // Next.js rules ...pluginNext.configs.recommended.rules, ...pluginNext.configs["core-web-vitals"].rules, diff --git a/src/components/CalendarViewUI/calendar-grid.tsx b/src/app/CalendarView/CalendarGrid.tsx similarity index 98% rename from src/components/CalendarViewUI/calendar-grid.tsx rename to src/app/CalendarView/CalendarGrid.tsx index 432a4fd..deb3bc7 100644 --- a/src/components/CalendarViewUI/calendar-grid.tsx +++ b/src/app/CalendarView/CalendarGrid.tsx @@ -1,7 +1,7 @@ import Box from "@mui/material/Box"; import Grid from "@mui/material/Grid"; -import DayCell from "./day-cell"; +import DayCell from "./DayCell"; export default function CalendarGrid({ daysInMonth, diff --git a/src/components/CalendarViewUI/calendar-view.tsx b/src/app/CalendarView/CalendarView.tsx similarity index 88% rename from src/components/CalendarViewUI/calendar-view.tsx rename to src/app/CalendarView/CalendarView.tsx index 0ee290c..e5abdf0 100644 --- a/src/components/CalendarViewUI/calendar-view.tsx +++ b/src/app/CalendarView/CalendarView.tsx @@ -1,4 +1,4 @@ -import MonthSection from "./month-section"; +import MonthSection from "./MonthSection"; export default function CalendarView(): React.ReactElement { return ( diff --git a/src/components/CalendarViewUI/day-cell.tsx b/src/app/CalendarView/DayCell.tsx similarity index 91% rename from src/components/CalendarViewUI/day-cell.tsx rename to src/app/CalendarView/DayCell.tsx index 2feb1a6..dafaacf 100644 --- a/src/components/CalendarViewUI/day-cell.tsx +++ b/src/app/CalendarView/DayCell.tsx @@ -1,7 +1,7 @@ import Box from "@mui/material/Box"; import Typography from "@mui/material/Typography"; -import EventItem from "./event-item"; +import EventItem from "./EventItem"; export default function DayCell({ day }: { day: number }): React.ReactElement { return ( diff --git a/src/components/CalendarViewUI/event-item.tsx b/src/app/CalendarView/EventItem.tsx similarity index 100% rename from src/components/CalendarViewUI/event-item.tsx rename to src/app/CalendarView/EventItem.tsx diff --git a/src/components/CalendarViewUI/month-section.tsx b/src/app/CalendarView/MonthSection.tsx similarity index 93% rename from src/components/CalendarViewUI/month-section.tsx rename to src/app/CalendarView/MonthSection.tsx index 10100fc..4b54996 100644 --- a/src/components/CalendarViewUI/month-section.tsx +++ b/src/app/CalendarView/MonthSection.tsx @@ -1,6 +1,6 @@ import Box from "@mui/material/Box"; -import CalendarGrid from "./calendar-grid"; +import CalendarGrid from "./CalendarGrid"; export default function MonthSection({ monthName, diff --git a/src/components/Filters/check-boxes.tsx b/src/app/Filters/CheckBoxes.tsx similarity index 92% rename from src/components/Filters/check-boxes.tsx rename to src/app/Filters/CheckBoxes.tsx index 3e428c0..b21a4b9 100644 --- a/src/components/Filters/check-boxes.tsx +++ b/src/app/Filters/CheckBoxes.tsx @@ -43,14 +43,14 @@ type CheckboxOption = { label: string; }; -type CheckboxesGroupProps> = { +type CheckboxGroupProps> = { checked: T; setChecked: React.Dispatch>; options: readonly CheckboxOption>[]; }; -export default function CheckboxesGroup>( - props: CheckboxesGroupProps, +export default function CheckboxGroup>( + props: CheckboxGroupProps, ): React.ReactElement { const { checked, setChecked, options } = props; diff --git a/src/components/Filters/date-picker.tsx b/src/app/Filters/DateRangePicker.tsx similarity index 94% rename from src/components/Filters/date-picker.tsx rename to src/app/Filters/DateRangePicker.tsx index 9feb2d2..5d0f322 100644 --- a/src/components/Filters/date-picker.tsx +++ b/src/app/Filters/DateRangePicker.tsx @@ -9,7 +9,7 @@ import { LocalizationProvider } from "@mui/x-date-pickers/LocalizationProvider"; import { Dayjs } from "dayjs"; import * as React from "react"; -export default function DatePickerValue(): React.ReactElement { +export default function DateRangePicker(): React.ReactElement { const [start, setStart] = React.useState(null); const [end, setEnd] = React.useState(null); diff --git a/src/components/Filters/dropdown-menu.tsx b/src/app/Filters/FiltersDropdown.tsx similarity index 97% rename from src/components/Filters/dropdown-menu.tsx rename to src/app/Filters/FiltersDropdown.tsx index 436db60..0b6a8a2 100644 --- a/src/components/Filters/dropdown-menu.tsx +++ b/src/app/Filters/FiltersDropdown.tsx @@ -35,7 +35,7 @@ type DropdownProps = { onChange: (event: SelectChangeEvent) => void; }; -export default function Dropdown({ +export default function FiltersDropdown({ label, value, options, diff --git a/src/components/Filters/modal.tsx b/src/app/Filters/FiltersModal.tsx similarity index 97% rename from src/components/Filters/modal.tsx rename to src/app/Filters/FiltersModal.tsx index e220374..77891d8 100644 --- a/src/components/Filters/modal.tsx +++ b/src/app/Filters/FiltersModal.tsx @@ -17,9 +17,9 @@ import { import { SelectChangeEvent } from "@mui/material/Select"; import * as React from "react"; -import CheckboxesGroup from "./check-boxes"; -import DatePickerValue from "./date-picker"; -import Dropdown from "./dropdown-menu"; +import CheckboxesGroup from "./CheckBoxes"; +import DatePickerValue from "./DateRangePicker"; +import Dropdown from "./FiltersDropdown"; type FiltersModalProps = { open: boolean; diff --git a/src/components/Filters/index.tsx b/src/app/Filters/index.tsx similarity index 95% rename from src/components/Filters/index.tsx rename to src/app/Filters/index.tsx index 97af270..a3e9d8d 100644 --- a/src/components/Filters/index.tsx +++ b/src/app/Filters/index.tsx @@ -7,8 +7,8 @@ import { Box, Button } from "@mui/material"; import { SelectChangeEvent } from "@mui/material/Select"; import * as React from "react"; -import Dropdown from "./dropdown-menu"; -import FiltersModal from "./modal"; +import Dropdown from "./FiltersDropdown"; +import FiltersModal from "./FiltersModal"; export default function Filters(): React.ReactElement { const [open, setOpen] = React.useState(false); diff --git a/src/components/ProfileDropdown/index.tsx b/src/app/Header/ProfileDropdown.tsx similarity index 85% rename from src/components/ProfileDropdown/index.tsx rename to src/app/Header/ProfileDropdown.tsx index 8c7702c..8167e66 100644 --- a/src/components/ProfileDropdown/index.tsx +++ b/src/app/Header/ProfileDropdown.tsx @@ -47,6 +47,7 @@ export default function ProfileDropdown(): React.ReactElement { width={32} height={32} alt="User avatar" + draggable={false} /> )} @@ -65,7 +66,7 @@ export default function ProfileDropdown(): React.ReactElement { > { - void router.push("/update-profile"); + void router.push("/account/update-profile"); handleClose(); }} > @@ -73,7 +74,7 @@ export default function ProfileDropdown(): React.ReactElement { { - void router.push("/change-password"); + void router.push("/account/change-password"); handleClose(); }} > @@ -81,7 +82,7 @@ export default function ProfileDropdown(): React.ReactElement { { - void router.push("/manage-affiliations"); + void router.push("/account/manage-affiliations"); handleClose(); }} > @@ -89,7 +90,7 @@ export default function ProfileDropdown(): React.ReactElement { { - void router.push("/view-hours"); + void router.push("/account/view-hours"); handleClose(); }} > @@ -97,20 +98,12 @@ export default function ProfileDropdown(): React.ReactElement { { - void router.push("/notification-settings"); + void router.push("/account/notification-settings"); handleClose(); }} > Notification Settings - { - void router.push("/language"); - handleClose(); - }} - > - Language - { void signOut({ callbackUrl: "/" }); diff --git a/src/components/Header/index.tsx b/src/app/Header/index.tsx similarity index 95% rename from src/components/Header/index.tsx rename to src/app/Header/index.tsx index 2abb1bc..6897fbf 100644 --- a/src/components/Header/index.tsx +++ b/src/app/Header/index.tsx @@ -11,9 +11,8 @@ import Link from "next/link"; import { useSession } from "next-auth/react"; import * as React from "react"; -import AuthButton from "../AuthButton"; -import DefaultButton from "../DefaultButton"; -import ProfileDropdown from "../ProfileDropdown"; +import ProfileDropdown from "@/app/Header/ProfileDropdown"; +import { AuthButton, DefaultButton } from "@/components/Button"; const Search = styled("div")(({ theme }) => ({ position: "relative", @@ -106,7 +105,6 @@ export default function Header(): React.ReactElement { variant="h6" noWrap component="a" - href="#app-bar-with-responsive-menu" sx={{ ml: 1, mr: 2, diff --git a/src/app/README.md b/src/app/README.md new file mode 100644 index 0000000..714a78d --- /dev/null +++ b/src/app/README.md @@ -0,0 +1,7 @@ +## App Directory Conventions + +- kebab-case folders represent routes (URL segments) +- PascalCase folders represent colocated components +- A folder becomes a route only if it contains page.tsx, layout.tsx, etc. +- Components colocated in app are scoped to that route +- Helpers use camelCase diff --git a/src/components/ToggleViews/calendar-view.tsx b/src/app/ToggleViews/CalendarView.tsx similarity index 87% rename from src/components/ToggleViews/calendar-view.tsx rename to src/app/ToggleViews/CalendarView.tsx index a91d420..104d44d 100644 --- a/src/components/ToggleViews/calendar-view.tsx +++ b/src/app/ToggleViews/CalendarView.tsx @@ -1,7 +1,7 @@ import { Box, Typography } from "@mui/material"; import * as React from "react"; -import type { View } from "../ToggleViews/view-types"; +import type { View } from "./view-types"; type CalendarViewProps = { activeView: View; diff --git a/src/components/ToggleViews/list-view.tsx b/src/app/ToggleViews/ListView.tsx similarity index 86% rename from src/components/ToggleViews/list-view.tsx rename to src/app/ToggleViews/ListView.tsx index 054755b..9633a82 100644 --- a/src/components/ToggleViews/list-view.tsx +++ b/src/app/ToggleViews/ListView.tsx @@ -1,7 +1,7 @@ import { Box, Typography } from "@mui/material"; import * as React from "react"; -import type { View } from "../ToggleViews/view-types"; +import type { View } from "./view-types"; type ListViewProps = { activeView: View; diff --git a/src/components/ToggleViews/map-view.tsx b/src/app/ToggleViews/MapView.tsx similarity index 100% rename from src/components/ToggleViews/map-view.tsx rename to src/app/ToggleViews/MapView.tsx diff --git a/src/components/ToggleViews/index.tsx b/src/app/ToggleViews/index.tsx similarity index 94% rename from src/components/ToggleViews/index.tsx rename to src/app/ToggleViews/index.tsx index 3e5970b..bf9e0b2 100644 --- a/src/components/ToggleViews/index.tsx +++ b/src/app/ToggleViews/index.tsx @@ -5,9 +5,9 @@ import MapIcon from "@mui/icons-material/Map"; import { Box, ButtonGroup, IconButton } from "@mui/material"; import * as React from "react"; -import CalendarView from "./calendar-view"; -import ListView from "./list-view"; -import MapView from "./map-view"; +import CalendarView from "./CalendarView"; +import ListView from "./ListView"; +import MapView from "./MapView"; import { View } from "./view-types"; export default function ToggleViews(): React.ReactElement { diff --git a/src/components/ToggleViews/view-types.ts b/src/app/ToggleViews/view-types.ts similarity index 100% rename from src/components/ToggleViews/view-types.ts rename to src/app/ToggleViews/view-types.ts diff --git a/src/components/VolunteerEventCard/index.tsx b/src/app/VolunteerEventCard.tsx similarity index 96% rename from src/components/VolunteerEventCard/index.tsx rename to src/app/VolunteerEventCard.tsx index 70a8ee3..96ca3a7 100644 --- a/src/components/VolunteerEventCard/index.tsx +++ b/src/app/VolunteerEventCard.tsx @@ -8,7 +8,7 @@ import Typography from "@mui/material/Typography"; import { useSession } from "next-auth/react"; import * as React from "react"; -import DefaultButton from "../DefaultButton"; +import { DefaultButton } from "@/components/Button"; export default function VolunteerEventCard(): React.ReactElement { const { status } = useSession(); @@ -32,7 +32,7 @@ export default function VolunteerEventCard(): React.ReactElement { {status === "authenticated" && ( - + )} diff --git a/src/components/VolunteerEventCardHeader/index.tsx b/src/app/VolunteerEventCardHeader.tsx similarity index 100% rename from src/components/VolunteerEventCardHeader/index.tsx rename to src/app/VolunteerEventCardHeader.tsx diff --git a/src/components/WelcomeCard/index.tsx b/src/app/WelcomeCard.tsx similarity index 100% rename from src/components/WelcomeCard/index.tsx rename to src/app/WelcomeCard.tsx diff --git a/src/app/change-password/page.tsx b/src/app/account/change-password/page.tsx similarity index 100% rename from src/app/change-password/page.tsx rename to src/app/account/change-password/page.tsx diff --git a/src/app/manage-affiliations/page.tsx b/src/app/account/manage-affiliations/page.tsx similarity index 100% rename from src/app/manage-affiliations/page.tsx rename to src/app/account/manage-affiliations/page.tsx diff --git a/src/app/notification-settings/page.tsx b/src/app/account/notification-settings/page.tsx similarity index 100% rename from src/app/notification-settings/page.tsx rename to src/app/account/notification-settings/page.tsx diff --git a/src/app/update-profile/page.tsx b/src/app/account/update-profile/page.tsx similarity index 100% rename from src/app/update-profile/page.tsx rename to src/app/account/update-profile/page.tsx diff --git a/src/app/view-hours/page.tsx b/src/app/account/view-hours/page.tsx similarity index 100% rename from src/app/view-hours/page.tsx rename to src/app/account/view-hours/page.tsx diff --git a/src/app/create-account-2/page.tsx b/src/app/create-account-2/page.tsx deleted file mode 100644 index 2c3b6cb..0000000 --- a/src/app/create-account-2/page.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import { Box } from "@mui/material"; - -import OutlinedCard from "@/components/account-creation"; - -export default function CreateAccountPage2(): React.ReactElement { - return ( - - - - ); -} diff --git a/src/components/account-creation/index.tsx b/src/app/create-account/basic-info/page.tsx similarity index 79% rename from src/components/account-creation/index.tsx rename to src/app/create-account/basic-info/page.tsx index 4393a5b..80447bf 100644 --- a/src/components/account-creation/index.tsx +++ b/src/app/create-account/basic-info/page.tsx @@ -1,20 +1,23 @@ "use client"; import EmergencyIcon from "@mui/icons-material/Emergency"; +import { Typography } from "@mui/material"; import Box from "@mui/material/Box"; import Button from "@mui/material/Button"; import Card from "@mui/material/Card"; import CardContent from "@mui/material/CardContent"; +import CardHeader from "@mui/material/CardHeader"; import Checkbox from "@mui/material/Checkbox"; import FormControl from "@mui/material/FormControl"; import FormControlLabel from "@mui/material/FormControlLabel"; +import Grid from "@mui/material/Grid"; import InputLabel from "@mui/material/InputLabel"; import MenuItem from "@mui/material/MenuItem"; import Select, { SelectChangeEvent } from "@mui/material/Select"; import TextField from "@mui/material/TextField"; -import Typography from "@mui/material/Typography"; import * as React from "react"; -export default function OutlinedCard(): React.JSX.Element { +export default function BasicInfoForm(): React.JSX.Element { + const [newsletter, setNewsletter] = React.useState(true); const [location, setLocation] = React.useState(""); const [size, setSize] = React.useState(""); const [SMS, setSMS] = React.useState(""); @@ -45,13 +48,121 @@ export default function OutlinedCard(): React.JSX.Element { - + + + + + + + + + + + + Complete the form below. + + * indicates required field + + + {/* Full Name */} + + + + + + + + {/* Email */} + + Email + + + + {/* Home Address */} + + Home Address + + + + + + + + + + + Mobile Phone @@ -469,7 +580,12 @@ export default function OutlinedCard(): React.JSX.Element { }} > } + control={ + setNewsletter(e.target.checked)} + /> + } label="Would you like to receive monthly newsletters?" sx={{ "& .MuiFormControlLabel-label": { diff --git a/src/app/create-account/page.tsx b/src/app/create-account/page.tsx index 3d308ac..61e7d45 100644 --- a/src/app/create-account/page.tsx +++ b/src/app/create-account/page.tsx @@ -1,9 +1,11 @@ "use client"; -import { Box, Button, Card, CardContent, Typography } from "@mui/material"; +import { Box, Card, CardContent, Typography } from "@mui/material"; import CardHeader from "@mui/material/CardHeader"; import Grid from "@mui/material/Grid"; import * as React from "react"; +import { DefaultButton } from "@/components/Button"; + import PasswordField from "./password-field"; import UsernameField from "./username-field"; @@ -102,17 +104,13 @@ export default function CreateAccountPage(): React.ReactElement { - + diff --git a/src/app/filters/page.tsx b/src/app/filters/page.tsx deleted file mode 100644 index 9620ceb..0000000 --- a/src/app/filters/page.tsx +++ /dev/null @@ -1,19 +0,0 @@ -"use client"; -import { Box } from "@mui/material"; -import * as React from "react"; - -import Filters from "@/components/Filters"; -import ToggleViews from "@/components/ToggleViews"; - -export default function FiltersPage(): React.ReactElement { - return ( - - - - - - - - - ); -} diff --git a/src/app/language/page.tsx b/src/app/language/page.tsx deleted file mode 100644 index 3ec2686..0000000 --- a/src/app/language/page.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { Box, Typography } from "@mui/material"; -import React from "react"; - -export default function Language(): React.ReactElement { - return ( - <> - - - Language - - - - - Language will go here later - - - ); -} diff --git a/src/app/layout.tsx b/src/app/layout.tsx index b762ff7..9ea02d4 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -5,7 +5,7 @@ import type { Metadata } from "next"; import { Roboto } from "next/font/google"; import { ReactNode } from "react"; -import Header from "@/components/Header"; +import Header from "@/app/Header"; import NextAuthProvider from "@/providers/next-auth-provider"; import NotistackProvider from "@/providers/notistack-provider"; import theme from "@/styles/theme"; diff --git a/src/app/page.tsx b/src/app/page.tsx index 8786d31..4f430e3 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -2,9 +2,9 @@ import Box from "@mui/material/Box"; import { ReactNode } from "react"; -import VolunteerEventCard from "@/components/VolunteerEventCard"; -import VolunteerEventCardHeader from "@/components/VolunteerEventCardHeader"; -import WelcomeCard from "@/components/WelcomeCard"; +import VolunteerEventCard from "@/app/VolunteerEventCard"; +import VolunteerEventCardHeader from "@/app/VolunteerEventCardHeader"; +import WelcomeCard from "@/app/WelcomeCard"; export default function HomePage(): ReactNode { return ( diff --git a/src/app/sign-out/page.tsx b/src/app/sign-out/page.tsx deleted file mode 100644 index 73d4363..0000000 --- a/src/app/sign-out/page.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { Box, Typography } from "@mui/material"; -import React from "react"; - -export default function SignOut(): React.ReactElement { - return ( - <> - - - Sign Out - - - - - Sign out will go here later - - - ); -} diff --git a/src/components/AuthButton/index.tsx b/src/components/Button/AuthButton.tsx similarity index 94% rename from src/components/AuthButton/index.tsx rename to src/components/Button/AuthButton.tsx index 2140061..fc5d484 100644 --- a/src/components/AuthButton/index.tsx +++ b/src/components/Button/AuthButton.tsx @@ -8,7 +8,7 @@ type Props = { bgcolor?: string; }; -export default function AuthButton({ +export function AuthButton({ label, bgcolor = "primary.main", color = "white", diff --git a/src/components/DefaultButton/index.tsx b/src/components/Button/DefaultButton.tsx similarity index 94% rename from src/components/DefaultButton/index.tsx rename to src/components/Button/DefaultButton.tsx index 6982119..54e1c4e 100644 --- a/src/components/DefaultButton/index.tsx +++ b/src/components/Button/DefaultButton.tsx @@ -9,7 +9,7 @@ type Props = { bgcolor?: string; }; -export default function DefaultButton({ +export function DefaultButton({ label, href, bgcolor = "primary.main", diff --git a/src/components/Button/index.ts b/src/components/Button/index.ts new file mode 100644 index 0000000..ec7cb09 --- /dev/null +++ b/src/components/Button/index.ts @@ -0,0 +1,2 @@ +export { AuthButton } from "./AuthButton"; +export { DefaultButton } from "./DefaultButton"; diff --git a/src/components/README.md b/src/components/README.md new file mode 100644 index 0000000..89c82ea --- /dev/null +++ b/src/components/README.md @@ -0,0 +1,5 @@ +## Components Directory + +- Contains globally reusable UI components +- All components are PascalCase +- No route-specific logic should live here diff --git a/src/components/SignUpCard/sign-up-card.tsx b/src/components/SignUpCard/sign-up-card.tsx deleted file mode 100644 index b56250f..0000000 --- a/src/components/SignUpCard/sign-up-card.tsx +++ /dev/null @@ -1,145 +0,0 @@ -"use client"; -import { Typography } from "@mui/material"; -import Box from "@mui/material/Box"; -import Card from "@mui/material/Card"; -import CardContent from "@mui/material/CardContent"; -import CardHeader from "@mui/material/CardHeader"; -import Grid from "@mui/material/Grid"; -import MenuItem from "@mui/material/MenuItem"; -import Select from "@mui/material/Select"; -import TextField from "@mui/material/TextField"; -import * as React from "react"; - -export default function ButtonAppBar(): React.ReactElement { - return ( - - {/* Card Header Section */} - - - - - - - - - - - - - - Complete the form below. - - * indicates required field - - - {/* Full Name */} - - - - - - - - {/* Email */} - - Email - - - - {/* Home Address */} - - Home Address - - - - - - - - - - - - - - - ); -}