diff --git a/README.md b/README.md index e6afc4f..2b0ea77 100644 --- a/README.md +++ b/README.md @@ -1,55 +1,22 @@ -

- CSSG Logo -

+# CCC -# CSSG Starter Template +A CS + SG project in partnership with The Campus & Community Coalition -This is a starter template for CSSG projects using React, TypeScript, and Tailwind CSS. It is configured to be used with VS Code Dev Containers for a consistent development environment. +## About The Campus & Community Coalition -## Prerequisites +The Campus & Community Coalition (CCC) is a collaborative force bringing together university and community partners to address the harms associated with high-risk drinking. By fostering open dialogue, sharing power, and using data-driven strategies, they work to create an environment where everyone can thrive socially, academically, and economically. -Before you begin, ensure you have the following installed: +## Project Mission -- [Git](https://git-scm.com/) -- [Docker Desktop](https://www.docker.com/products/docker-desktop) -- [Visual Studio Code](https://code.visualstudio.com/) -- [Dev Containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) for VS Code. +CCC wants to migrate from under Downtown Chapel Hill's website to their own. [Click here to see the current website.](https://www.downtownchapelhill.com/coalition) -## Getting Started +## Project Overview -1. **Fork the repository:** +This website will be used primarily to display important trends regarding alcohol use in the Chapel Hill community alongside other important resources. - Fork this repository. Then, clone your forked repository: +This webpage uses React. [Click here to get started with React.](https://react.dev/learn) - ```bash - git clone - cd - ``` +## Get Started -2. **Open in VS Code/Cursor:** - - Open the cloned repository folder in VS Code or Cursor. - -3. **Open in Dev Container:** - - Once the project is open in VS Code, you will be prompted to "Reopen in Container". Click on it. - - If you don't see the prompt, you can open the command palette and run "Dev Containers: Reopen in Container". - - **Windows/Linux:** `Ctrl+Shift+P` - - **Mac:** `Cmd+Shift+P` - - This will build the Docker container for the development environment. The first build might take a few minutes. Subsequent loads will be much faster. - -## Available Commands - -Inside the dev container, you can use the following commands: - -| Command | Description | -| :------------------ | :--------------------------------------------------------- | -| `npm run dev` | Starts the development server with Hot Module Replacement. | -| `npm run build` | Builds the application for production. | -| `npm run start` | Serves the production build. | -| `npm run lint` | Lints the codebase using ESLint. | -| `npm run lint:fix` | Lints and automatically fixes issues. | -| `npm run format` | Formats the code using Prettier. | -| `npm run typecheck` | Runs the TypeScript compiler to check for type errors. | +Look at our environment setup docs: [Environment Setup](docs/environment_setup.md)\ +Also, make sure to look over the: [Contributing Guidelines](docs/contributing_guidelines.md) diff --git a/app/app.css b/app/app.css index 242d4f3..ac8ad18 100644 --- a/app/app.css +++ b/app/app.css @@ -1,14 +1,13 @@ @import "tailwindcss"; @theme { - --font-sans: - "Inter", ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", - "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + --font-sans: "Figtree"; } html, body { - @apply bg-white dark:bg-gray-950; + @apply bg-white text-black; + color-scheme: light; @media (prefers-color-scheme: dark) { color-scheme: dark; diff --git a/app/components/test.tsx b/app/components/test.tsx new file mode 100644 index 0000000..e69de29 diff --git a/app/helpers/validation.ts b/app/helpers/validation.ts new file mode 100644 index 0000000..dd13d23 --- /dev/null +++ b/app/helpers/validation.ts @@ -0,0 +1,20 @@ +const DISALLOWED_EMAIL_CHARS = /[\s()[\];:<>\\/"'`~!#$%^&*|+=?{}]/; +const BASIC_EMAIL_SHAPE = /^[^@\s]+@[^@\s]+\.[^@\s]+$/; + +export function normalizeEmail(value: string): string { + return (value || "").trim().toLowerCase(); +} + +export function isEmailValid(value: string): boolean { + const email = normalizeEmail(value); + + if (!email) return false; + if (DISALLOWED_EMAIL_CHARS.test(email)) return false; + if (!BASIC_EMAIL_SHAPE.test(email)) return false; + + return true; +} + +export function areRequiredFilled(obj: Record): boolean { + return Object.values(obj).every((v) => (v || "").trim().length > 0); +} diff --git a/app/images/logo.png b/app/images/logo.png new file mode 100644 index 0000000..46a4b1d Binary files /dev/null and b/app/images/logo.png differ diff --git a/app/root.tsx b/app/root.tsx index e35a017..2c71977 100644 --- a/app/root.tsx +++ b/app/root.tsx @@ -19,7 +19,7 @@ export const links: Route.LinksFunction = () => [ }, { rel: "stylesheet", - href: "https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap", + href: "https://fonts.googleapis.com/css2?family=Figtree:ital,wght@0,300..900;1,300..900&display=swap", }, ]; diff --git a/app/routes.ts b/app/routes.ts index 47ab806..398be06 100644 --- a/app/routes.ts +++ b/app/routes.ts @@ -1,3 +1,10 @@ -import { type RouteConfig, index } from "@react-router/dev/routes"; +import { type RouteConfig, route } from "@react-router/dev/routes"; -export default [index("routes/starter.tsx")] satisfies RouteConfig; +export default [ + route("/newsletter", "routes/newsletter.tsx"), + route("/data", "routes/data.tsx"), + route("*", "routes/error.tsx"), + route("/people", "routes/people.tsx"), + route("/research", "routes/research.tsx"), + route("/", "routes/starter.tsx"), +] satisfies RouteConfig; diff --git a/app/routes/data.tsx b/app/routes/data.tsx new file mode 100644 index 0000000..8997341 --- /dev/null +++ b/app/routes/data.tsx @@ -0,0 +1,8 @@ +import React from "react"; +export default function Data() { + return ( + <> +

Data Page

+ + ); +} diff --git a/app/routes/error.tsx b/app/routes/error.tsx new file mode 100644 index 0000000..0fb2391 --- /dev/null +++ b/app/routes/error.tsx @@ -0,0 +1,8 @@ +import React from "react"; +export default function Error() { + return ( + <> +

Not Found

+ + ); +} diff --git a/app/routes/newsletter.tsx b/app/routes/newsletter.tsx new file mode 100644 index 0000000..187b467 --- /dev/null +++ b/app/routes/newsletter.tsx @@ -0,0 +1,189 @@ +import React from "react"; +import { + areRequiredFilled, + isEmailValid, + normalizeEmail, +} from "~/helpers/validation"; + +const INITIAL_FORM = { + firstName: "", + lastName: "", + email: "", +}; + +export default function Newsletter() { + const [formValues, setFormValues] = + React.useState(INITIAL_FORM); + const [hasInteracted, setHasInteracted] = React.useState(false); + const [successMessage, setSuccessMessage] = React.useState( + null + ); + + const fieldsComplete = React.useMemo( + () => areRequiredFilled(formValues), + [formValues] + ); + + const emailValid = React.useMemo( + () => isEmailValid(formValues.email), + [formValues.email] + ); + + const validationError = !fieldsComplete + ? "All required fields are not filled." + : !emailValid + ? "Invalid email format." + : null; + + const shouldShowError = hasInteracted && Boolean(validationError); + const isSubmitDisabled = !fieldsComplete || !emailValid; + + const handleChange = + (field: keyof typeof INITIAL_FORM) => + (event: React.ChangeEvent) => { + const { value } = event.target; + + setHasInteracted(true); + setSuccessMessage(null); + setFormValues((prev) => ({ + ...prev, + [field]: value, + })); + }; + + const handleBlur = (field: keyof typeof INITIAL_FORM) => () => { + setFormValues((prev) => ({ + ...prev, + [field]: + field === "email" + ? normalizeEmail(prev.email) + : prev[field].trim(), + })); + }; + + const handleSubmit = (event: React.FormEvent) => { + event.preventDefault(); + setHasInteracted(true); + + if (validationError) { + setSuccessMessage(null); + setFormValues((prev) => ({ + ...prev, + email: normalizeEmail(prev.email), + firstName: prev.firstName.trim(), + lastName: prev.lastName.trim(), + })); + return; + } + + const normalizedForm = { + firstName: formValues.firstName.trim(), + lastName: formValues.lastName.trim(), + email: normalizeEmail(formValues.email), + }; + + setFormValues(normalizedForm); + setSuccessMessage("Thanks for signing up!"); + }; + + return ( +
+
+

+ Sign Up for the Newsletter! +

+

+ Receive updates, events, and more! Our newsletters are sent + out quarterly +

+ +
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ {shouldShowError && validationError ? ( +

+ {validationError} +

+ ) : successMessage ? ( +

+ {successMessage} +

+ ) : null} +
+ + +
+
+
+ ); +} diff --git a/app/routes/people.tsx b/app/routes/people.tsx new file mode 100644 index 0000000..7ad28d7 --- /dev/null +++ b/app/routes/people.tsx @@ -0,0 +1,8 @@ +import React from "react"; +export default function People() { + return ( + <> +

People Page

+ + ); +} diff --git a/app/routes/research.tsx b/app/routes/research.tsx new file mode 100644 index 0000000..21e00b5 --- /dev/null +++ b/app/routes/research.tsx @@ -0,0 +1,8 @@ +import React from "react"; +export default function Research() { + return ( + <> +

Research Page

+ + ); +} diff --git a/app/routes/starter.tsx b/app/routes/starter.tsx index d82256c..cab5cc6 100644 --- a/app/routes/starter.tsx +++ b/app/routes/starter.tsx @@ -1,15 +1,5 @@ import React from "react"; -import { - SiVite, - SiReact, - SiTypescript, - SiDocker, - SiEslint, - SiPrettier, - SiTailwindcss, - SiReactrouter, -} from "react-icons/si"; -import { VscExtensions } from "react-icons/vsc"; +import { Link } from "react-router"; export function meta() { return [ @@ -23,244 +13,14 @@ export function meta() { } export default function Home() { - const features = [ - { - icon: SiVite, - name: "Vite", - description: "Lightning fast build tool and dev server", - color: "#646CFF", - }, - { - icon: SiReact, - name: "React 18", - description: "Latest React with concurrent features", - color: "#61DAFB", - }, - { - icon: SiTypescript, - name: "TypeScript", - description: "Type-safe JavaScript development", - color: "#3178C6", - }, - { - icon: SiDocker, - name: "Docker", - description: "Containerized development and deployment", - color: "#2496ED", - }, - { - icon: SiEslint, - name: "ESLint", - description: "Code linting and quality enforcement", - color: "#4B32C3", - }, - { - icon: SiPrettier, - name: "Prettier", - description: "Automatic code formatting", - color: "#F7B93E", - }, - { - icon: SiTailwindcss, - name: "Tailwind CSS", - description: "Utility-first CSS framework", - color: "#06B6D4", - }, - { - icon: SiReactrouter, - name: "React Router", - description: "Modern routing for React applications", - color: "#CA4245", - }, - { - icon: VscExtensions, - name: "VS Code Extensions", - description: "Pre-configured development extensions", - color: "#007ACC", - }, - ]; - return ( -
- {/* Header Section */} -
-
- Starter Template Logo -
-

- Vite + React Starter -

-

- A modern, production-ready template with all the tools you - need to build amazing React applications -

-
- - {/* Features Grid */} -
- {features.map((feature, index) => { - const IconComponent = feature.icon; - return ( -
{ - e.currentTarget.style.transform = - "translateY(-2px)"; - e.currentTarget.style.boxShadow = - "0 8px 25px rgba(5, 150, 105, 0.25), 0 4px 10px rgba(5, 150, 105, 0.15)"; - }} - onMouseOut={(e) => { - e.currentTarget.style.transform = - "translateY(0)"; - e.currentTarget.style.boxShadow = - "0 4px 6px rgba(5, 150, 105, 0.15), 0 1px 3px rgba(5, 150, 105, 0.1)"; - }} - > -
- -
-

- {feature.name} -

-

- {feature.description} -

-
- ); - })} -
- - {/* Getting Started */} -
-

- Ready to Start Building? -

-

- This template includes everything you need for modern React - development. Just clone, install, and start coding! -

-
- npm install && npm run dev -
-
-
+ <> +

Campus & Community Coalition

+

+ + Preview the newsletter sign-up form + +

+ ); } diff --git a/docs/contributing_guidelines.md b/docs/contributing_guidelines.md new file mode 100644 index 0000000..9da865d --- /dev/null +++ b/docs/contributing_guidelines.md @@ -0,0 +1,98 @@ +# Contributing + +## Assigning Tickets + +All issues can be found on the Issues tab of this repository. You can also view them in the Projects bar for better organization. + +We are using GitHub issues to represent tickets and their acceptance criteria (things that must be done for the ticket to be closed). Click on them to open more info about them. + +Sometimes you may notice work to be done that does not have an issue on GitHub. If an issue does not exist for the thing you want to change, then create one detailing the changes to be made. + +When you are ready to take on a ticket, open the issue and go to "Assignees" and assign yourself to the ticket. + +## Branching - please read the entire section carefully + +Each ticket needs to be associated with a development branch.\ +There are three parts to this: + +1. Naming the branch +2. Creating the branch +3. Attaching the branch to the GitHub issue via the GitHub web interface + +### Naming Conventions + +Once you have chosen your ticket, come up with the name of your branch. This must be of the format `issuenumber-short-descriptive-name`. Base the name off the name of the GitHub issue. For example, if you intend to create a branch for a page or component, name the branch after the issue (e.g. `10-settings-page`, `11-notification-component`). + +### Creating New Branch + +Next, you need to create the actual branch. +All commands must be run inside your project terminal. For all your tasks, you will need to create a branch for your team and submit a pull request once you are done. + +**Option 1: Creating a branch on GitHub** + +1. Click the branch button on the github page of our repo. It should be right underneath the repo name and say "main" +2. Type in the name of the branch you want to create. +3. Click **Create Branch: [branch name]** +4. Open up your project +5. Run `git pull` in the terminal to update your branches +6. Run `git checkout [branch name]` or `git switch [branch name]` to switch to the newly created branch +7. Double check that you are in the correct repository by running `git branch -a` +8. Make your changes and push as normal while working within your branch + +**Option 2: Creating a branch locally** + +1. `git branch [name]` to create a branch with name of [name]. +2. `git checkout [name]` to switch to branch [name]. +3. When you've finished making your changes locally, run `git push -u origin [name]` to create the remote branch and push to there. + +## Developing in your branch + +All of the code related to your issue should be contained to your branch only. Do not make changes to other people's branches. + +A single branch should not last longer than 2 weeks and should have very self-contained, easy to identify changes. + +### Making proper commits + +A proper commit should focus on one small piece of functionality and describe what has changed.\ +Only commit code that is working. Do not commit broken code.\ +Commits should start with an imperative verb and describe the overall change.\ +Example: "Fix calendar component not appearing on home page"\ +For a resource on good commit messages, see here: [Guidelines for commit messages](https://gist.github.com/luismts/495d982e8c5b1a0ced4a57cf3d93cf60)\ +When you are ready to commit, make sure you're on your designated branch by the following code: +`git status`\ +And then commit your changes: + +1. `git add *` +2. `git commit -m "commit message"` +3. `git push` + +### Keeping your branch up to date + +Occasionally, you may want to merge the contents of `stage` into your branch to keep it up to date. Follow these steps while remaining on your branch: + +1. `git fetch origin` to fetch the latest remote changes from origin for all branches +2. `git merge origin` to merge the remote stage branch into your own local branch + +Or, you can run `git pull origin` to do both at once. + +## Submitting Pull Requests + +Pull requests (PRs) represent a set of commits on a specific branch that are proposed to be merged into the `stage` branch. + +PRs have the following lifecycle: + +1. The PR is opened by the developer, who wants to merge their work from their branch into `stage`. The developer should request a review or let one of the project leads know. +2. The PR gets reviewed by a code reviewer such as a Tech Lead. +3. If changes need to be made, the developer continues to work on that branch and re-requests another review when they are ready. +4. If the changes are accepted, the branch commits will be merged into stage and the branch is now inactive. + +Developers are expected to have successful PRs every 2 weeks - this means you should submit your PR in advance, some time after one week, so you have time for fixing any issues. + +### Making a Pull Request + +1. Navigate to the [repository page](https://github.com/cssgunc/ccc). +2. Click the **main** branch button and navigate to the branch you worked on. +3. Click the **Contribute** button +4. Click **Open Pull Request** +5. Write a short description of your changes +6. Click **Create pull request** diff --git a/docs/environment_setup.md b/docs/environment_setup.md new file mode 100644 index 0000000..2f0afe4 --- /dev/null +++ b/docs/environment_setup.md @@ -0,0 +1,47 @@ +# Setting up your environment + +## Prerequisites + +Before you begin, ensure you have the following installed: + +- [Git](https://git-scm.com/) +- [Visual Studio Code](https://code.visualstudio.com/) +- [Docker Desktop](https://www.docker.com/products/docker-desktop/) +- [Dev Containers](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) extension in VS Code + +## Cloning the repository + +[Clone the repository](https://github.com/git-guides/git-clone) into a local workspace. Avoid folders that have cloud sync services, such as Microsoft OneDrive. + +## Starting your Project + +1. Open the project in VS Code. +2. Upon doing so you should see a pop-up prompting you to reopen the folder in a container. Click this if you see it. +3. If you are not automatically prompted, open the VS Code Command Pallete (Mac: `Cmd+Shift+P`, Windows/Linux: `Ctrl+Shift+P`). +4. Run the command **Dev Containers: Rebuild and Reopen in Container**. +5. This will build the Docker container for the development environment. Note that the first build might take a few minutes. Subsequent loads will be much faster. +6. Run `npm run dev` to start the development server. + +## Available Commands + +Inside the dev container, you can use the following commands: +| Command | Description | +|---|---| +| `npm run dev` | Starts the development server with Hot Module Replacement. | +| `npm run build` | Builds the application for production. | +| `npm run start` | Serves the production build. | +| `npm run lint` | Lints the codebase using ESLint. | +| `npm run lint:fix` | Lints and automatically fixes issues. | +| `npm run format` | Formats the code using Prettier. | +| `npm run typecheck` | Runs the TypeScript compiler to check for type errors. | + +## Stopping the Project + +Close the running app with `Ctrl + C`. + +## Conclusion + +By the end of this, you should have an interactive app running on your browser.\ +You are ready to start contributing. + +Next: Read the [Contributing Guidelines](contributing_guidelines.md) before you begin! diff --git a/package-lock.json b/package-lock.json index 33c37bd..25eecb6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,8 @@ "react": "^19.1.0", "react-dom": "^19.1.0", "react-icons": "^5.5.0", - "react-router": "^7.7.1" + "react-router": "7.9.4", + "react-router-dom": "7.9.4" }, "devDependencies": { "@eslint/js": "^9.36.0", @@ -1423,19 +1424,19 @@ } } }, - "node_modules/@react-router/express": { + "node_modules/@react-router/dev/node_modules/@react-router/node": { "version": "7.9.2", - "resolved": "https://registry.npmjs.org/@react-router/express/-/express-7.9.2.tgz", - "integrity": "sha512-8mAkthF+0oNg9eK6qiWM/VGhhbDZrK6l3IEH7B1lB8yRJArHu6BJsafzFKR2jBE0NCws4bB0STP6zJZMjztfFw==", + "resolved": "https://registry.npmjs.org/@react-router/node/-/node-7.9.2.tgz", + "integrity": "sha512-mGqpEXVWs1XmwpJdbESE2fzvS3a43EdMCuiL2U3Nmm1IuGdSjc60gQK/IeKWjNGdgj1pZEyyQK17fYXPqjp5Uw==", + "dev": true, "license": "MIT", "dependencies": { - "@react-router/node": "7.9.2" + "@mjackson/node-fetch-server": "^0.2.0" }, "engines": { "node": ">=20.0.0" }, "peerDependencies": { - "express": "^4.17.1 || ^5", "react-router": "7.9.2", "typescript": "^5.1.0" }, @@ -1446,9 +1447,9 @@ } }, "node_modules/@react-router/node": { - "version": "7.9.2", - "resolved": "https://registry.npmjs.org/@react-router/node/-/node-7.9.2.tgz", - "integrity": "sha512-mGqpEXVWs1XmwpJdbESE2fzvS3a43EdMCuiL2U3Nmm1IuGdSjc60gQK/IeKWjNGdgj1pZEyyQK17fYXPqjp5Uw==", + "version": "7.9.4", + "resolved": "https://registry.npmjs.org/@react-router/node/-/node-7.9.4.tgz", + "integrity": "sha512-sdeDNRaqAB71BR2hPlhcQbPbrXh8uGJUjLVc+NpRiPsQbv6B8UvIucN4IX9YGVJkw3UxVQBn2vPSwxACAck32Q==", "license": "MIT", "dependencies": { "@mjackson/node-fetch-server": "^0.2.0" @@ -1457,7 +1458,7 @@ "node": ">=20.0.0" }, "peerDependencies": { - "react-router": "7.9.2", + "react-router": "7.9.4", "typescript": "^5.1.0" }, "peerDependenciesMeta": { @@ -1491,6 +1492,49 @@ "react-router": "7.9.2" } }, + "node_modules/@react-router/serve/node_modules/@react-router/express": { + "version": "7.9.2", + "resolved": "https://registry.npmjs.org/@react-router/express/-/express-7.9.2.tgz", + "integrity": "sha512-8mAkthF+0oNg9eK6qiWM/VGhhbDZrK6l3IEH7B1lB8yRJArHu6BJsafzFKR2jBE0NCws4bB0STP6zJZMjztfFw==", + "license": "MIT", + "dependencies": { + "@react-router/node": "7.9.2" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "express": "^4.17.1 || ^5", + "react-router": "7.9.2", + "typescript": "^5.1.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@react-router/serve/node_modules/@react-router/node": { + "version": "7.9.2", + "resolved": "https://registry.npmjs.org/@react-router/node/-/node-7.9.2.tgz", + "integrity": "sha512-mGqpEXVWs1XmwpJdbESE2fzvS3a43EdMCuiL2U3Nmm1IuGdSjc60gQK/IeKWjNGdgj1pZEyyQK17fYXPqjp5Uw==", + "license": "MIT", + "dependencies": { + "@mjackson/node-fetch-server": "^0.2.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react-router": "7.9.2", + "typescript": "^5.1.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/@remix-run/node-fetch-server": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/@remix-run/node-fetch-server/-/node-fetch-server-0.9.0.tgz", @@ -6417,9 +6461,9 @@ } }, "node_modules/react-router": { - "version": "7.9.2", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.9.2.tgz", - "integrity": "sha512-i2TPp4dgaqrOqiRGLZmqh2WXmbdFknUyiCRmSKs0hf6fWXkTKg5h56b+9F22NbGRAMxjYfqQnpi63egzD2SuZA==", + "version": "7.9.4", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.9.4.tgz", + "integrity": "sha512-SD3G8HKviFHg9xj7dNODUKDFgpG4xqD5nhyd0mYoB5iISepuZAvzSr8ywxgxKJ52yRzf/HWtVHc9AWwoTbljvA==", "license": "MIT", "dependencies": { "cookie": "^1.0.1", @@ -6438,6 +6482,22 @@ } } }, + "node_modules/react-router-dom": { + "version": "7.9.4", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.9.4.tgz", + "integrity": "sha512-f30P6bIkmYvnHHa5Gcu65deIXoA2+r3Eb6PJIAddvsT9aGlchMatJ51GgpU470aSqRRbFX22T70yQNUGuW3DfA==", + "license": "MIT", + "dependencies": { + "react-router": "7.9.4" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, "node_modules/react-router/node_modules/cookie": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", diff --git a/package.json b/package.json index 7466851..8f91f9f 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,8 @@ "react": "^19.1.0", "react-dom": "^19.1.0", "react-icons": "^5.5.0", - "react-router": "^7.7.1" + "react-router": "7.9.4", + "react-router-dom": "7.9.4" }, "devDependencies": { "@eslint/js": "^9.36.0", diff --git a/tsconfig.json b/tsconfig.json index 4540ed2..079d6d2 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -14,6 +14,7 @@ "jsx": "react-jsx", "rootDirs": [".", "./.react-router/types"], "baseUrl": ".", + "ignoreDeprecations": "6.0", "paths": { "~/*": ["./app/*"] },