What should I add to my project so that the owner who is authenticated can add products or modify them?
What should I do so that the registered user can add products to the catalog from the Administrator Panel?
I have an application that I create with npx create-react-app my-app
The Web App is an Administration panel for the products of a restaurant, store, or street vendor.
In it, the business owner adds products to the Firebase database, which can later be viewed from a Mobile App through this Administrator Panel.
To increase my knowledge, I have followed projects and tutorials to add the required registration of the user to the App (Administrator Panel)
Finally, I managed to create a small user registration application with firebase, you can see it in this GitHub repository: https://github.com/miguelitolaparra/react-firebase-email-login
With the same components as in the react-firebase-email-login App I have created a registration system in the Administrator Panel and everything works, you can see in the following link that I uploaded the App to a server:
http://restaurantcliente.webapplicationdeveloper.es/
This should be from where the business owner manages their products, adding or removing from the catalog.
To prevent anyone from registering and modifying the owner's catalog, remove the SignUp part and register the user from the Firebase database.
Change the Firebase Rules to be safe as follows:
service cloud.firestore {
match / databases / {database} / documents {
match / {document = **} {
allow read: if true
allow write: if request.auth.uid == request.data.author_uid
}
}
}
The problem has occurred when I try to add new products from the Administrator Panel, since the products are not added to the database. When I change the rules, so that anyone can read and write, it works and the administrator can add products to the database from the Administrator Panel
service cloud.firestore {
match / databases / {database} / documents {
match / {document = **} {
allow read, write: if true;
}
}
}
I've seen several examples and tutorials using Private Routes and other components, but I always run into problems adding those systems to my project, due to the new changes to react-router-dom and react-router, which have been removed elements like <Switch>, createHistory, etc.
I don't know how to make my project secure and when the user is registered, I can add items to the catalog from the Administrator panel, without adding them manually from the database.
I don't know what else to do, after trying with examples that I found on GitHub and not achieving the goal. I cannot add all the examples, as I have tried at least 30 different projects. What should I add to my project so the owner can add products or modify them?
What should I do so that the registered user can add products to the catalog from the Administrator Panel?
EDIT to add more code to the question
I hope this can help me get it to work. You can try the example in the following link:
http://restaurantcliente.webapplicationdeveloper.es
Administrator:
restaurantcliente@webapplicationdeveloper.es
Password: Restaurant1920
The biggest problem is that the Web application, I need to wrap it entirely in the authentication mode, so that all its elements and functionalities are available when I edit the rules "as Safe" But this I don't know how to do it
The database consists of two collections: Orders and Products The Orders collection is created from the Mobile App, when a customer places an order from the mobile application. The Products collection is created by the restaurant owner from the Administrator Panel.
This is the file: NuevoPlato.js
import React, { useContext, useState } from 'react'
import { useFormik } from 'formik'
import * as Yup from 'yup'
import { FirebaseContext } from '../../firebase'
import { useNavigate } from 'react-router-dom'
import FileUploader from 'react-firebase-file-uploader'
const NuevoPlato = () => {
// state para las imagenes
const [subiendo, guardarSubiendo] = useState(false)
const [progreso, guardarProgreso] = useState(0)
const [urlimagen, guardarUrlimagen] = useState('')
// Context con las operaciones de firebase
const { firebase } = useContext(FirebaseContext)
//console.log(firebase)
// Hook para redireccionar
const navigate = useNavigate()
// validación y leer los datos del formulario
const formik = useFormik({
initialValues: {
nombre: '',
precio: '',
categoria: '',
imagen: '',
descripcion: '',
},
validationSchema: Yup.object({
nombre: Yup.string()
.min(3, 'Los Platillos deben tener al menos 3 caracteres')
.required('El Nombre del platillo es obligatorio'),
precio: Yup.number()
.min(1, 'Debes agregar un número')
.required('El Precio es obligatorio'),
categoria: Yup.string()
.required('La categoría es obligatoria'),
descripcion: Yup.string()
.min(10, 'La descripción debe ser más larga')
.required('La descripción es obligatoria')
}),
onSubmit: plato => {
try {
plato.existencia = true
plato.imagen = urlimagen
firebase.db.collection('productos').add(plato)
// Redireccionar
navigate('/menu')
} catch (error) {
console.log(error)
}
}
})
// Todo sobre las imagenes
const handleUploadStart = () => {
guardarProgreso(0)
guardarSubiendo(true)
}
const handleUploadError = error => {
guardarSubiendo(false)
console.log(error)
}
const handleUploadSuccess = async nombre => {
guardarProgreso(100)
guardarSubiendo(false)
// Almacenar la URL de destino
const url = await firebase
.storage
.ref("productos")
.child(nombre)
.getDownloadURL()
console.log(url)
guardarUrlimagen(url)
}
const handleProgress = progreso => {
guardarProgreso(progreso)
console.log(progreso)
}
return (
<>
<h1 className="text-3xl font-light mb-4">Add new plate</h1>
<div className="flex justify-center mt-10">
<div className="w-full max-w-3xl">
<form
onSubmit={formik.handleSubmit}
>
// THIS IS THE FORM WITH THE DATA <input
type="submit"
className="bg-gray-800 hover:bg-gray-900 w-full mt-5 p-2 text-white uppercase font-bold"
value="Agregar Plato"
/>
</form>
</div>
</div>
</>
)
}
export default NuevoPlato
From the Administrator panel, the "delivery time" is also managed, the time it will take for the order to be finished and which is defined by the owner of the restaurant from the panel.
But I cannot modify this data either so that it is displayed in the Mobile App, if I edit the "As Safe" rules.
File setting delivery time Orden.js
import React, { useState, useContext} from 'react'
import { FirebaseContext } from '../../firebase'
const Orden = ({ orden }) => {
const [ tiempoentrega, guardarTiempoEntrega ]= useState(0)
// Context de Firebase
const { firebase } = useContext(FirebaseContext)
// define el tiempo de entrega del pedido en tiempo real
const definirTiempo = id => {
try {
firebase.db.collection('ordenes')
.doc(id)
.update({
tiempoentrega
})
} catch (error) {
console.log(error)
}
}
// Marcar que el pedido esta completado
const completarOrden = id => {
try {
firebase.db.collection('ordenes')
.doc(id)
.update({
completado: true
})
} catch (error) {
console.lor(error)
}
}
return (
<div className="sm:w-1/2 lg:w-1/3 px-2 mb-4">
<div className="p-3 shadow-md bg-white">
<h1 className="text-yellow-600 text-lg font-bold"> {orden.id} </h1>
{orden.orden.map(platos => (
<p className="text-gray-600"> {platos.cantidad} {platos.nombre} </p>
))}
<p className="text-gray-700 font-bold">Total a Pay: {orden.total}$</p>
{orden.tiempoentrega === 0 && (
<div className="mb-4">
<label className="block text-gray-700 text-sm font-bold mb-2">
Delivery Time
</label>
<input
type="number"
className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
min="1"
max="30"
placeholder="Pon El Tiempo"
value={tiempoentrega}
onChange={ e => guardarTiempoEntrega( parseInt(e.target.value))}
/>
<button
onClick={ () => definirTiempo(orden.id) }
type="submit"
className="bg-gray-500 hover:bg-gray-700 w-full mt-5 p-2 text-white uppercase font-bold"
>
Define Time
</button>
</div>
)}
{orden.tiempoentrega > 0 && (
<p className="text-gray-700">Delivery Time:
<span className="font-bold"> {orden.tiempoentrega} Minutes </span>
</p>
)}
{ !orden.completado && orden.tiempoentrega > 0 &&(
<button
type="button"
className="bg-blue-700 hover:bg-blue-400 w-full mt-5 p-2 text-white uppercase font-bold"
onClick={ () => completarOrden(orden.id)}
>
Declare Done
</button>
)}
</div>
</div>
)
}
export default Orden
I show some of the files
File App.js
import React, { useState, useEffect } from "react"
import { Routes, Route } from "react-router"
import firebase, { FirebaseContext } from "./firebase"
//import { auth } from 'firebase'
import firebaseObj from "./firebase"
import Ordenes from "./components/paginas/Ordenes"
import Menu from "./components/paginas/Menu"
import NuevoPlato from "./components/paginas/NuevoPlato"
import Sidebar from "./components/ui/Sidebar"
import Signin from "./components/Signin"
const auth = firebaseObj.auth
function App() {
const [user, setUser] = useState(null)
useEffect(() => {
const unsubscribe = auth.onAuthStateChanged((userAuth) => {
const user = {
uid: userAuth?.uid,
email: userAuth?.email,
}
if (userAuth) {
console.log(userAuth)
setUser(user)
} else {
setUser(null)
}
})
return unsubscribe;
}, [])
if (!user) {
return (
<div className="md:flex min-h-screen">
<div className="md:w-2/5 xl:w-4/5 p-6">
<Signin />
</div>
</div>
)
} else {
return (
<FirebaseContext.Provider
value={{
firebase,
}}
>
<div className="md:flex min-h-screen">
<Sidebar />
<div className="md:w-2/5 xl:w-4/5 p-6">
<Routes>
<Route path="/" element={<Ordenes />} />
<Route path="/menu" element={<Menu />} />
<Route path="/nuevo-plato" element={<NuevoPlato />} />
</Routes>
</div>
</div>
</FirebaseContext.Provider>
);
}
}
export default App
File Signin.js
import React, { useRef } from 'react'
import firebaseObj from '../firebase/firebase'
const auth = firebaseObj.auth
const Signin = () => {
const emailRef = useRef(null);
const passwordRef = useRef(null);
const signUp = e => {
e.preventDefault();
auth.createUserWithEmailAndPassword(
emailRef.current.value,
passwordRef.current.value
).then(user => {
console.log(user)
}).catch(err => {
console.log(err)
})
}
const signIn = e => {
e.preventDefault();
auth.signInWithEmailAndPassword(
emailRef.current.value,
passwordRef.current.value
).then(user => {
console.log(user)
}).catch(err => {
console.log(err)
})
}
return (
<div className="flex justify-center mt-10">
<div className="w-full max-w-3xl">
<h1 className="my-8 text-yellow-600 text-lg font-bold text-center text-7xl"> My humble restaurant </h1>
<div className="mb-4">
<form action="">
<h1 className="text-center m-3">Start the Work Session</h1>
<div className="mb-4">
<label className="block text-gray-700 text-sm font-bold mb-2" htmlFor="nombre">Email</label>
<input className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
ref={emailRef} type="email" placeholder="email" />
</div>
<div className="mb-4">
<label className="block text-gray-700 text-sm font-bold mb-2" htmlFor="nombre">Password</label>
<input className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
ref={passwordRef} type="password" placeholder="password" />
</div>
<button
type="button"
className="bg-blue-700 hover:bg-blue-400 w-full mt-5 p-2 text-white uppercase font-bold"
onClick={signIn}
>
Log In
</button>
<div className="mb-4 mt-8 ">
<h2 className="block text-gray-700 text-sm font-bold mb-2 text-3xl">Enter the administration panel of your restaurant and start preparing dishes and orders</h2>
</div>
</form>
</div>
</div>
</div>
)
}
export default Signin
File package.json
{
"name": "restaurantcliente",
"version": "0.1.0",
"private": true,
"homepage": "/build",
"dependencies": {
"@testing-library/jest-dom": "^5.11.4",
"@testing-library/react": "^11.1.0",
"@testing-library/user-event": "^12.1.10",
"firebase": "^7.19.0",
"formik": "^2.2.9",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-firebase-file-uploader": "^2.4.4",
"react-router-dom": "^6.0.0-beta.0",
"react-scripts": "^4.0.3",
"web-vitals": "^1.0.1",
"yup": "^0.32.9"
},
"scripts": {
"start": "npm run watch:css && react-scripts start",
"build": "npm run build:css && react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject",
"build:css": "postcss src/css/tailwind.css -o src/css/main.css",
"watch:css": "postcss src/css/tailwind.css -o src/css/main.css"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"autoprefixer": "^10.3.1",
"history": "^5.0.0",
"postcss-cli": "^8.3.1",
"react-router": "^6.0.0-beta.0",
"tailwindcss": "^2.2.7"
}
}

