diff --git a/README.md b/README.md index a6b7c1e..bbb44a0 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,15 @@ -# Full-Stack Starter +# Full Stack Starter This repository contains a "starter" project for web application development in JavaScript. This includes the following components, from front-end to back-end: -- React 18.2.0 -- React Router 6.20.0 -- Bootstrap 5.3.2 -- Node.js 20.11.0 -- Express 4.18.2 -- Sequelize 6.35.1 -- Postgres 15.5 +- React 18.3.1 +- React Router 7.1.1 +- Bootstrap 5.3.3 +- Vite 6.0.7 +- Fastify 5.2.0 +- Prisma 6.1.0 +- Node.js 22.12.0 +- Postgres 17.2 ## One-time Setup @@ -42,10 +43,10 @@ This repository contains a "starter" project for web application development in ``` full-stack-starter-server-1 | 5:31:23 PM client.1 | VITE v4.3.9 ready in 327 ms - full-stack-starter-server-1 | 5:31:23 PM client.1 | ➜ Local: http://localhost:3000/ + full-stack-starter-server-1 | 5:31:23 PM client.1 | ➜ Local: http://localhost:5000/ ``` -5. Now you should be able to open the web app in your browser at: http://localhost:3000/ +5. Now you should be able to open the web app in your browser at: http://localhost:5000/ 6. Open a new tab or window of your shell, change into your repo directory as needed, and execute this command: @@ -90,6 +91,49 @@ This repository contains a "starter" project for web application development in 8. That's it! After all this setup is complete, the only command you need to run to get started again is the `docker compose up` command. +## Development Tools + +This project includes components with helpful developer tools, such as the following: + +1. Mailcatcher + + The Docker Compose configuration includes the Mailcatcher development mail server. Email sent from the + server will be captured by this mail server and can be viewed on the web at: + + http://localhost:1080 + + NO live emails will be sent over the Internet. + +2. Prisma Studio + + The Prisma library includes a web interface for browsing the contents of the development database at: + + http://localhost:5555 + +3. Scalar API Documentation Renderer + + The Scalar library automatically generates web-based API documentation for the server based on the + Swagger/OpenAPI schema definitions included with each route, viewable at: + + http://localhost:5000/api/reference + +4. Minio + + The Docker Compose configuration includes the Minio object storage server as a local development + simulation of AWS S3. You can browse the contents of the storage server at: + + http://localhost:9001 + + Username and password are: minioadmin/minioadmin + +## Testing + +This repo includes a Github Actions workflow for running server tests. To test locally, log in +to a running server container as describe above (`docker compose exec server bash -l`) and then run +`npm test`. The server tests use the Testcontainers library to automatically launch test databases and +storage servers for testing- if tests terminate unexpectedly, you may have dangling/orphan containers +running. Use `docker ps` to list and check running containers. + ## Heroku Deployment Setup 1. Sign up for a Heroku account at: https://signup.heroku.com/ @@ -290,8 +334,8 @@ This repository contains a "starter" project for web application development in ## License -Full-Stack Starter -Copyright (C) 2023 +Full Stack Starter +Copyright (C) 2025 Dev/Mission This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as diff --git a/client/.eslintrc.cjs b/client/.eslintrc.cjs deleted file mode 100644 index 6910e43..0000000 --- a/client/.eslintrc.cjs +++ /dev/null @@ -1,10 +0,0 @@ -module.exports = { - env: { browser: true, es2020: true }, - extends: ['eslint:recommended', 'plugin:react/recommended', 'plugin:react/jsx-runtime', 'plugin:react-hooks/recommended'], - parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, - settings: { react: { version: '18.2' } }, - plugins: ['react-refresh'], - rules: { - 'react-refresh/only-export-components': 'warn', - }, -}; diff --git a/client/eslint.config.js b/client/eslint.config.js index 238d2e4..aa78f70 100644 --- a/client/eslint.config.js +++ b/client/eslint.config.js @@ -1,38 +1,8 @@ -import js from '@eslint/js' -import globals from 'globals' -import react from 'eslint-plugin-react' -import reactHooks from 'eslint-plugin-react-hooks' -import reactRefresh from 'eslint-plugin-react-refresh' +import neostandard from 'neostandard'; -export default [ - { ignores: ['dist'] }, - { - files: ['**/*.{js,jsx}'], - languageOptions: { - ecmaVersion: 2020, - globals: globals.browser, - parserOptions: { - ecmaVersion: 'latest', - ecmaFeatures: { jsx: true }, - sourceType: 'module', - }, - }, - settings: { react: { version: '18.3' } }, - plugins: { - react, - 'react-hooks': reactHooks, - 'react-refresh': reactRefresh, - }, - rules: { - ...js.configs.recommended.rules, - ...react.configs.recommended.rules, - ...react.configs['jsx-runtime'].rules, - ...reactHooks.configs.recommended.rules, - 'react/jsx-no-target-blank': 'off', - 'react-refresh/only-export-components': [ - 'warn', - { allowConstantExport: true }, - ], - }, - }, -] +export default neostandard({ + ignores: [ + 'dist/*', + ], + semi: true +}); diff --git a/client/package.json b/client/package.json index ae6a637..6b39e04 100644 --- a/client/package.json +++ b/client/package.json @@ -8,7 +8,7 @@ "build": "npm run build:client && npm run build:server", "build:client": "vite build --outDir dist/client", "build:server": "vite build --ssr src/entry-server.jsx --outDir dist/server", - "lint": "eslint .", + "lint": "eslint --fix", "preview": "vite preview", "test": "" }, @@ -27,15 +27,12 @@ "react-router": "^7.1.1" }, "devDependencies": { - "@eslint/js": "^9.17.0", "@types/react": "^18.3.18", "@types/react-dom": "^18.3.5", "@vitejs/plugin-react-swc": "^3.7.2", "eslint": "^9.17.0", - "eslint-plugin-react": "^7.37.2", - "eslint-plugin-react-hooks": "^5.0.0", - "eslint-plugin-react-refresh": "^0.4.16", "globals": "^15.14.0", + "neostandard": "^0.12.0", "sass": "^1.83.0", "vite": "^6.0.7" }, diff --git a/client/src/Admin/AdminRoutes.jsx b/client/src/Admin/AdminRoutes.jsx index d899a60..b90acfc 100644 --- a/client/src/Admin/AdminRoutes.jsx +++ b/client/src/Admin/AdminRoutes.jsx @@ -2,11 +2,11 @@ import { Navigate, Routes, Route } from 'react-router'; import AdminUsersRoutes from './Users/AdminUsersRoutes'; -function AdminRoutes() { +function AdminRoutes () { return ( - } /> - } /> + } /> + } /> ); } diff --git a/client/src/Admin/Users/AdminUserInvite.jsx b/client/src/Admin/Users/AdminUserInvite.jsx index 232dfa8..d472848 100644 --- a/client/src/Admin/Users/AdminUserInvite.jsx +++ b/client/src/Admin/Users/AdminUserInvite.jsx @@ -9,7 +9,7 @@ import UnexpectedError from '../../UnexpectedError'; import ValidationError from '../../ValidationError'; import { useStaticContext } from '../../StaticContext'; -function AdminUserInvite() { +function AdminUserInvite () { const staticContext = useStaticContext(); const navigate = useNavigate(); const [invite, setInvite] = useState({ @@ -21,13 +21,13 @@ function AdminUserInvite() { const [isLoading, setLoading] = useState(false); const [error, setError] = useState(null); - function onChange(event) { + function onChange (event) { const newInvite = { ...invite }; newInvite[event.target.name] = event.target.value; setInvite(newInvite); } - async function onSubmit(event) { + async function onSubmit (event) { event.preventDefault(); setLoading(true); setError(null); @@ -50,72 +50,72 @@ function AdminUserInvite() { Invite a new User - {staticContext?.env?.VITE_SITE_TITLE ?? ''} -
-
-
-
-
-

Invite a new User

+
+
+
+
+
+

Invite a new User

- {error && error.message &&
{error.message}
} + {error && error.message &&
{error.message}
}
-
-