A sample CRUD app in React
This project is not set up with create-react-app.
See official docs
npm create vite@latest react-crud-exercise -- --template react-ts
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p // did not work for me, so I added configs manually, see below
npm install @tailwindcss/postcss
npm install -D tailwindcss@latest postcss@latest autoprefixer@latest
npm install -D tailwindcss@3
npm install -D postcss-import
// tailwind.config.cjs
module.exports = {
content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'],
theme: {
extend: {},
},
plugins: [],
};
// postcss.config.js
export default {
plugins: {
'@tailwindcss/postcss': {},
autoprefixer: {},
},
};
Code to add to your CSS file (e.g., index.css
):
@import 'tailwindcss/preflight';
@tailwind utilities;
@import 'tailwindcss';
Copilot, Perplexity, and Gemini produced utter BS here. Look at these docs to get Tailwind working with Vite.
Also added Tailwind CSS Forms for better form styling.
npm install -D @tailwindcss/forms
// tailwind.config.cjs
module.exports = {
content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'],
theme: {
extend: {},
},
plugins: [require('@tailwindcss/forms')],
};
npm install --save-dev @types/react
npm install --save-dev @types/react-dom
Follow these instructions to add React Router to your app.
npm i react-router
npm i react-router-dom
npm i @types/react-router-dom
This project uses Firebase for authentication and Firestore for the database. Follow Firebase instructions.
npm install firebase
Firebase documentation:
This project was inspired by the ReactJS and Tailwind CSS Fundamentals taught by Stackt on Coursera. I took the course (including certificate), but for this code implementation I deliberately chose different libraries ans set-up, used TypeScript and the most recent version of React. Also the features differ significantly from the tutorial. You will not be able to follow along with the course with this code.
- Add Storybook
- Add content database/s (aim for basic CMS or blog or "shopping window", Builder.io CMS?)
- Set up unit tests
- Add unit tests
- Add e2e tests (Playwright?)
- Add i18n
- Add headless UI, e.g. TansStack Table
- Add/update documentation / set up instructions / tutorial
- Refactor / restructure code
- Refactor to use same component for create and edit product?
- Implement a proper, consistent UI design
- Add Figma design?
- Maybe change displayName behavior in Firebase auth to match Firestore displayName, see Hints and Ideas below.
- Vendor profiles should have a custom vendor image (email login user do not have a profile image, Google OAuth users do) and a vendor link (instead of their email address being exposed)
- Add configuration and filter for currency display
- Make location optional
- Handle case of user trying to upload more than 5 images (max images per product)
- Handle case of user trying to upload an image larger than 5MB (max image size)
- Add more distinctive user roles (admin, vendor, user) and permissions
- Wishlist/bookmarks for products
- Checkout process
- Add payment processing (Stripe?)
- Add contact form for site feedback (and for individual vendors?)
- Allow user to change password
Additional features:
- Add a "protected content" wrapper component for page content exclusive to logged-in users
Actual refactoring suggestions:
- Profile could be refactored to only rely on the ProtectedRoute component for authentication. The inline authentication check could be removed.
The displayName is read from the Firestore users collection. When it was changed it will not be the same as the Firebase auth displayName. The Firebase auth displayName is not updated when the Firestore user is updated. I don't want to change this behavior, while testing the app. I want to keep the Firebase auth displayName as it is. Maybe I will change this behavior when the app is ready for production.
This project's code is licensed under : Currently not licensed. Intended to become MIT or GPL license when ready.
Images by Unsplash are licensed under the Unsplash License:
- "Road through trees", Kellen Riggen
- "Welcome aboard, life saver", Adam Cai
- "Welcome Back", Nick Fewings
Created with prompt:
Please look at the entire @workspace. Can you deduce what a novice user, after checking out this project, would have to do to get the shop running (including Firebase set-up). Please add instructions after "Installation of the complete shop" in the Readme.
Follow these steps to set up and run the React CRUD Exercise project:
# Clone the repository
git clone https://github.com/yourusername/react-crud-exercise.git
cd react-crud-exercise
# Install dependencies
npm install
- Create a Firebase account at firebase.google.com if you don't have one
- Create a new Firebase project from the Firebase Console
- Enable the following services:
- Authentication (Email/Password and Google Sign-In)
- Firestore Database
- Storage
- In the Firebase Console, go to Authentication → Sign-in method
- Enable Email/Password and Google authentication providers
- In the Firebase Console, go to Firestore Database → Create database
- Start in test mode, then add security rules
- Add the following security rules in Firestore Database → Rules:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// Listings
match /listings/{listing} {
allow read;
allow create: if request.auth != null && request.resource.data.imgUrls.size() < 7;
allow delete: if resource.data.userRef == request.auth.uid;
allow update: if resource.data.userRef == request.auth.uid;
}
// Users
match /users/{user} {
allow read;
allow create;
allow update: if request.auth.uid == user;
}
}
}
- In the Firebase Console, go to Storage → Get started
- Start in test mode, then add security rules
- Add the following security rules in Storage → Rules:
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read;
allow write: if request.auth != null && request.resource.size < 5 * 1024 * 1024 && // 5MB
request.resource.contentType.matches('image/.*');
// Allow delete only for files that include the user's ID in the path
allow delete: if request.auth != null &&
resource.name.matches('products/' + request.auth.uid + '.*');
}
}
}
Create a composite index as described in Firebase.md:
- Go to Firestore Database → Indexes → Add Index
- Set:
- Collection ID:
listings
- Fields:
isNew
(Ascending)createdAt
(Descending)
- Collection ID:
- Click Create index and wait for it to build
- In the Firebase Console, go to Project Overview → Project settings
- Scroll down to Your apps and click the Web icon (</>) to add a web app
- Register your app and copy the Firebase configuration
- Edit firebase.ts and replace the
firebaseConfig
object with your own:
const firebaseConfig: FirebaseConfig = {
apiKey: 'YOUR_API_KEY',
authDomain: 'YOUR_PROJECT_ID.firebaseapp.com',
projectId: 'YOUR_PROJECT_ID',
storageBucket: 'YOUR_PROJECT_ID.appspot.com',
messagingSenderId: 'YOUR_MESSAGING_SENDER_ID',
appId: 'YOUR_APP_ID',
};
npm run dev
Your application should now be running at http://localhost:5173