In an application in ReactJs, I am trying to use a modal to confirm the deletion of a user. Normally I have a delete function created in the users table that deletes a user from my database. Now I would like to pass this functionality to the modal. I am wondering how I can easily pass the delete function to the modal. That when I confirm the delete action in modal a user is deleted. I would be grateful for any guidance.
MyModal.js
import React from "react";
export default function MyModal() {
const [showModal, setShowModal] = React.useState(false);
return (
<>
<button
className="bg-red-600 text-white active:bg-pink-600 font-bold uppercase text-sm px-6 py-3 rounded shadow hover:shadow-lg outline-none focus:outline-none mr-1 mb-1 ease-linear transition-all duration-150"
type="button"
onClick={() => setShowModal(true)}
>
Delete
</button>
{showModal ? (
<>
<div className="justify-center items-center flex overflow-x-hidden overflow-y-auto fixed inset-0 z-50 outline-none focus:outline-none">
<div className="relative w-auto my-6 mx-auto max-w-3xl">
{/*content*/}
<div className="border-0 rounded-lg shadow-lg relative flex flex-col w-full bg-white outline-none focus:outline-none">
{/*header*/}
<div className="flex items-start justify-between p-5 border-b border-solid border-slate-200 rounded-t">
<h3 className="text-3xl font-semibold">Modal Title</h3>
<button
className="p-1 ml-auto bg-transparent border-0 text-black opacity-5 float-right text-3xl leading-none font-semibold outline-none focus:outline-none"
onClick={() => setShowModal(false)}
>
<span className="bg-transparent text-black opacity-5 h-6 w-6 text-2xl block outline-none focus:outline-none">
×
</span>
</button>
</div>
{/*body*/}
<div className="relative p-6 flex-auto">
<p className="my-4 text-slate-500 text-lg leading-relaxed">
Are you sure you want to delete this user?
</p>
</div>
{/*footer*/}
<div className="flex items-center justify-end p-6 border-t border-solid border-slate-200 rounded-b">
<button
className="text-blue-600 background-transparent font-bold uppercase px-6 py-2 text-sm outline-none focus:outline-none mr-1 mb-1 ease-linear transition-all duration-150"
type="button"
onClick={() => setShowModal(false)}
>
Close
</button>
<button
className="bg-red-600 text-white active:bg-emerald-600 font-bold uppercase text-sm px-6 py-3 rounded shadow hover:shadow-lg outline-none focus:outline-none mr-1 mb-1 ease-linear transition-all duration-150"
type="button"
onClick={() => setShowModal(false)}
>
Delete
</button>
</div>
</div>
</div>
</div>
<div className="opacity-25 fixed inset-0 z-40 bg-black"></div>
</>
) : null}
</>
);
}
ManageUser.js
import { useEffect, useState } from "react";
import { useAuthContext } from "../hooks/useAuthContext";
import MyModal from "../components/MyModal"
import LoadingSpinner from "../components/LoadingSpinner";
function ManageUser() {
const [users, setUser] = useState([]);
const [loading, setLoading] = useState(false)
useEffect(() => {
getData();
}, []);
async function deleteOperation(_id) {
let result = await fetch(`/api/user/${_id}`, {
method: "DELETE",
});
result = await result.json();
console.warn(result);
getData();
}
async function getData() {
setLoading(true)
let result = await fetch("/api/user/users");
result = await result.json();
setUser(result);
setLoading(false)
}
return (
<div>
<h1 className="flex justify-center py-4 text-xl font-bold">Zarządzaj użytkownikami:</h1>
<div className="flex justify-center py-2">
<div className="flex justify-between items-center h-30 max-w-[1240px] mx-auto px-4">
{ loading ? (<div className="flex justify-center items-center "><LoadingSpinner/></div>) :
<div className=" overflow-x-auto relative shadow-md sm:rounded-lg">
<table className="w-full text-sm text-center text-white">
<thead className="text-xs text-white uppercase bg-rgba(6, 18, 36, 0.945)">
<tr>
<th scope="col" className="py-3 px-6">
Nazwa
</th>
<th scope="col" className="py-3 px-6 hidden sm:table-cell">
Email
</th>
<th scope="col" className="py-3 px-6">
Admin
</th>
<th scope="col" className="py-3 px-6">
</th>
</tr>
</thead>
<tbody>
{users.map((user) => (
<tr key={user._id} user={user} className="bg-rgba(6, 18, 36, 0.945) border-b border-[#00df9a] ">
<th scope="row" className="py-4 px-6 font-medium text-white whitespace-nowrap">
{user.name}
</th>
<td className="py-4 px-6 hidden sm:table-cell">
{user.email}
</td>
<td className="py-4 px-6">
{user.isAdmin ? "Tak" : "Nie"}
</td>
<td className="py-4 px-6 text-right">
<MyModal/><button className="bg-red-500 hover:bg-[#00df9a] text-white font-semibold py-2 px-4 border border-zinc-900 rounded shadow" onClick={() => deleteOperation(user._id)}>Delete</button>
</td>
</tr>
))}
</tbody>
</table>
</div>
}
</div>
</div>
</div>
);
}
export default ManageUser;
Is there any quick way to pass the function that I use for deleting a user to this modal?
You could just pass a property to your model component, lets call it onSubmit
export default function MyModal( { onSubmit } ) {
Within your modal you can handle the submitting with a submitHandler like
const submitHandler = () => {
setShowModal(false);
onSubmit();
}
Your delete button in the MyModal can call this submitHandler like
<button onClick={submitHandler}>
Delete
</button>
and within your ManageUser component you could pass the deleteOperation as the onSubmit function like
<MyModal onSubmit={() => deleteOperation(user.id)}/>
Related
I am trying to make a clinic locator that when you put where you are, you get as a result the nearby clinics, I can not enter data in the imput, not even let me write and I can not find the problem.
import React, { useState, useEffect } from 'react'
import { getClinic } from '../../api/drupalAPI'
import {Clinic} from '#icofcv/common';
import Spinner from '../spinner/Spinner';
interface Props {
showModalLocator: boolean,
closeModalLocator: () => void
}
export const ClinicLocator: React.FC<Props> = ({ children, showModalLocator, closeModalLocator }) => {
const [clinicList, setClinicList] = useState<Clinic[]>([]);
const [clinicListFiltered, setClinicListFiltered] = useState<Clinic[]>([]);
const [searchClinic, setSearchClinic] = useState("");
const handleChange= (e) => async () => {
getClinic().then((response)=>{
console.log(response)
setClinicList(response);
setClinicListFiltered(response)
}).catch ( (error) => {
console.error(error);
throw error;
});
setSearchClinic(e.target.value);
filter(e.target.value);
}
const filter=(termSearch)=>{
const resultSearch= clinicList.filter((element)=>{
if(element.address?.toString().toLowerCase().includes(termSearch.toLowerCase())
|| element.province?.toString().toLowerCase().includes(termSearch.toLowerCase())
|| element.town?.toString().toLowerCase().includes(termSearch.toLowerCase())
){
return element;
}
});
setClinicListFiltered(resultSearch);
}
return (
<>
<div>
{showModalLocator ? (
<>
<div className="justify-center items-center flex overflow-x-hidden overflow-y-auto fixed inset-0 z-50 outline-none focus:outline-none">
<div className="relative p-2 w-full max-w-3xl h-full md:h-auto">
{/*content*/}
<div className="relative bg-white rounded-lg shadow">
{/*header*/}
<div className="flex justify-between items-start px-4 py-3 rounded-t border-b">
<h3 className="text-lg font-medium">Localizador de clinicas</h3>
<button className="text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm p-1.5 ml-auto inline-flex items-center" onClick={closeModalLocator}>
<svg aria-hidden="true" className="w-5 h-5" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"></path>
</svg>
</button>
</div>
{/*body*/}
<div className="relative px-3 py-3 flex-auto overflow-auto modal-body">
<h2 className="text-sm font-medium mb-2">¿Dónde te encuentras?</h2>
<input
value={searchClinic}
onChange={handleChange}
type="search"
className="w-100 bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block p-2"
placeholder="Introduce una ubicación"
/>
<div className="py-3 border-b flex flex-col md:flex-row items-start md:items-center md:justify-between justify-start gap-2">
<div className="flex items-center">
<label className="inline-flex relative items-center mr-5 cursor-pointer">
<input type="checkbox" className="sr-only peer" checked/>
<div className="w-11 h-6 bg-gray-200 rounded-full peer peer-focus:ring-teeal-600
peer-checked:after:translate-x-full peer-checked:after:border-white after:content-['']
after:absolute after:top-0.5 after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-teal-600"></div>
<span className="ml-2 text-xs font-medium text-gray-900">Centro privado</span>
</label>
</div>
<div className="flex items-center">
<label className="inline-flex relative items-center mr-5 cursor-pointer">
<input type="checkbox" className="sr-only peer" readOnly/>
<div className="w-11 h-6 bg-gray-200 rounded-full peer peer-focus:ring-teeal-600
peer-checked:after:translate-x-full peer-checked:after:border-white after:content-['']
after:absolute after:top-0.5 after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-teal-600"></div>
<span className="ml-2 text-xs font-medium text-gray-900">con Mútuas</span>
</label>
</div>
<div className="flex items-center">
<label className="inline-flex relative items-center mr-5 cursor-pointer">
<input type="checkbox" className="sr-only peer" readOnly/>
<div className="w-11 h-6 bg-gray-200 rounded-full peer peer-focus:ring-teeal-600
peer-checked:after:translate-x-full peer-checked:after:border-white after:content-['']
after:absolute after:top-0.5 after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-teal-600"></div>
<span className="ml-2 text-xs font-medium text-gray-900">con Compañías de Salud</span>
</label>
</div>
</div>
<div>
<h2 className="text-sm font-medium my-3">Resultados</h2>
<div className="w-100">
<iframe className="w-100" src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d2664.3238269926374!2d-0.3805919350162851!3d39.46959682083709!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0xd604f4bee0957f3%3A0x6686ff7d230b3965!2zQy4gZGUgU2FudC
BWaWNlbnQgTcOgcnRpciwgNjEsIHBpc28gMsK6LCBwdGEgMsKqLCA0NjAwMiBWYWzDqG5jaWEsIEVzcGHDsWE!5e0!3m2!1ses!2sus!4v1662388390673!5m2!1ses!2sus" loading="lazy"></iframe>
</div>
<div className="md:mt-4 overflow-auto relative py-2">
<div className="grid grid-cols-1 md:grid-cols-2 gap-2">
<div className="card bg-white px-2 py-3 h-36 md:h-32">
{!clinicListFiltered ? <Spinner /> :
clinicListFiltered.map((clinic) => (
<button key={clinic.id} type="button" className="text-left">
<div className="flex items-center gap-2 md:gap-4 md:gap-4">
<img className="h-24 w-2/5 min-w-40 object-cover object-center rounded-lg" src="../carousel-1.svg" alt="#"/>
<div className="w-3/5">
<div className="text-md font-medium leading-5 clinic-title uppercase">{clinic.title}</div>
<div className="flex items-center gap-2">
<div className="text-neutral-500 text-sm">{clinic.propsPhone}</div>
<div className="text-neutral-500 text-sm">{clinic.mobile}</div>
</div>
<div className="text-teal-600 text-sm underline clinic-mail">{clinic.email}</div>
<div className="text-neutral-500 text-sm">{clinic.registry}</div>
</div>
</div>
</button>
))
}
</div>
{/* <div className="card bg-white px-2 py-3 h-36 md:h-32">
<button type="button" className="text-left">
<div className="flex items-center gap-2 md:gap-4 md:gap-4">
<img className="h-24 w-2/5 min-w-40 object-cover object-center rounded-lg" src="../carousel-1.svg" alt="#"/>
<div className="w-3/5">
<div className="text-md font-medium leading-5 clinic-title uppercase">AlamedaVLC</div>
<div className="flex items-center gap-2">
<div className="text-neutral-500 text-sm">963255652</div>
<div className="text-neutral-500 text-sm">963255652</div>
</div>
<div className="text-teal-600 text-sm underline clinic-mail">fisioalamedavlc#gmail.com</div>
<div className="text-neutral-500 text-sm">9947</div>
</div>
</div>
</button>
</div> */}
</div>
</div>
</div>
</div>
{/*footer*/}
<div className="flex items-center justify-end px-4 py-2 border-t border-solid border-slate-200 rounded-b gap-2">
<button className="btn text-black text-sm background-transparent px-8 outline-none focus:outline-none focus:ring-teal-600 focus:border-teal-600" type="button" onClick={closeModalLocator}>Cancelar</button>
<button className="btn bg-teal-600 hover:bg-teal-700 text-white text-sm active:bg-teal-700 px-8 outline-none focus:outline-none" type="button" onClick={closeModalLocator}>Buscar</button>
</div>
</div>
</div>
</div>
<div className="opacity-25 fixed inset-0 z-40 bg-black"></div>
</>
) : null}
</div>
</>
)
}
Try to change the line
const handleChange= (e) => async () => {
to
const handleChange = (e) => {
You're currently returning a function, which means that it doesn't get called. There is also no need for async as you don't have any await in the function.
handleChange returns a function which is never executed; remove the currying as it is not needed:
const handleChange= (e) => {
getClinic().then((response)=>{
console.log(response)
setClinicList(response);
setClinicListFiltered(response)
}).catch ( (error) => {
console.error(error);
throw error;
});
setSearchClinic(e.target.value);
filter(e.target.value);
}
The reason you cannot input anything is that React treats the input field as controlled component, which means that it itself won't take care of updating the fields value since you passed an initial value (of ""). With controlled components, you as the developer are required to handle the state outside of the input field, i.e. update the value attribute of the input component. Since that failed due to the mistake outlined above, the value of the input field remains fixed at "", thus not allowing you to type in the field.
Further reading:
Controlled components
Uncontrolled components
I have mapped a list of responses from my backend mongodb to my frontend. For each response, I have also added a button. I want to add an voteResponse function for my vote button. How can I continue to have access to my {response.respondee} and {response.response} in my voteResponse function?
{responses.map((response) => (
<div
key={response._id}
className='py-8 px-12 mb-12 bg-gray-50 border-b border-gray-100 transform transition duration-300 ease-in-out hover:-translate-y-2'
>
<NextLink href={`/responses/${response.response}`} passHref>
<div>
<div className='inline-block text-gray-900 mb-4'></div>
<h3 className='text-lg leading-normal mb-2 font-semibold text-black'>
{response.response}
</h3>
<p className='text-gray-500'>
{response.respondee.slice(0, 5)}...
{response.respondee.slice(38)}
</p>
</div>
</NextLink>
<div>
<button
type='submit'
className='inline-flex justify-center mt-3 py-1 px-3 border border-transparent shadow text-base font-medium rounded-md text-white bg-cyan-600 hover:bg-cyan-700 focus:outline-none'
onClick={voteResponse}
>
Vote
</button>
</div>
</div>
))}
You can do this by 2 ways. First is by making a separate component which is being rendered in map function and pass response in it through props then each component will have access to it's response.response and response.respondee.
funtion Response({response}){
const voteResponse = () =>{
console.log(response.response, response.respondee)
}
return(
<div key={response._id} className='py-8 px-12 mb-12 bg-gray-50 border-b border-gray-100 transform transition duration-300 ease-in-out hover:-translate-y-2'>
<NextLink href={`/responses/${response.response}`} passHref>
<div>
<div className='inline-block text-gray-900 mb-4'></div>
<h3 className='text-lg leading-normal mb-2 font-semibold text-black'>
{response.response}
</h3>
<p className='text-gray-500'>
{response.respondee.slice(0, 5)}... {response.respondee.slice(38)}
</p>
</div>
</NextLink>
<div>
<button type='submit' className='inline-flex justify-center mt-3 py-1 px-3 border border-transparent shadow text-base font-medium rounded-md text-white bg-cyan-600 hover:bg-cyan-700 focus:outline-none' onClick={voteResponse}>
Vote
</button>
</div>
</div>
)
}
//use it like this
{responses.map((response) => (
<Response response={response} key={response._id} />
))}
Second way to pass the index in voteResponse function and then through that index you can access the those variables like responses[index].response and responses[index].respondee.
{responses.map((response, index) => (
<div key={response._id} className='py-8 px-12 mb-12 bg-gray-50 border-b border-gray-100 transform transition duration-300 ease-in-out hover:-translate-y-2'>
<NextLink href={`/responses/${response.response}`} passHref>
<div>
<div className='inline-block text-gray-900 mb-4'></div>
<h3 className='text-lg leading-normal mb-2 font-semibold text-black'>
{response.response}
</h3>
<p className='text-gray-500'>
{response.respondee.slice(0, 5)}... {response.respondee.slice(38)}
</p>
</div>
</NextLink>
<div>
<button type='submit' className='inline-flex justify-center mt-3 py-1 px-3 border border-transparent shadow text-base font-medium rounded-md text-white bg-cyan-600 hover:bg-cyan-700 focus:outline-none' onClick={voteResponse}>
Vote
</button>
</div>
</div>
))}
Pass value to onClick prop: onClick={() => voteResponse(response)} and use it as parameter of voteResponse function:
const voteResponse = (res) => {
console.log("response.respondee is: ", res.respondee) //<=== response.respondee value is res.respondee value
console.log("response.response is: ", res.response)
}
{responses.map((response) => (
<div
key={response._id}
className='py-8 px-12 mb-12 bg-gray-50 border-b border-gray-100 transform transition duration-300 ease-in-out hover:-translate-y-2'
>
<NextLink href={`/responses/${response.response}`} passHref>
<div>
<div className='inline-block text-gray-900 mb-4'></div>
<h3 className='text-lg leading-normal mb-2 font-semibold text-black'>
{response.response}
</h3>
<p className='text-gray-500'>
{response.respondee.slice(0, 5)}...
{response.respondee.slice(38)}
</p>
</div>
</NextLink>
<div>
<button
type='submit'
className='inline-flex justify-center mt-3 py-1 px-3 border border-transparent shadow text-base font-medium rounded-md text-white bg-cyan-600 hover:bg-cyan-700 focus:outline-none'
onClick={() => voteResponse(response)
>
Vote
</button>
</div>
</div>
))}
I have an API in ExpressJS with an endpoint that pages users. A parameter is passed to the query in the api with the page number, and it takes care of the rest. Everything works normal on the API side.
I have been using actions in NextJS to make API requests. It has worked fine so far. This actions makes the request to the aforementioned endpoint:
export const getInactiveUsers = async (page) => {
try {
console.log(page)
const res = await _fetch(`${API}/user/inactives?page=${page}`, {
method: "GET",
headers: {
"Accept": "application/json",
"Content-Type": "application/json",
Authorization: `Bearer ${getCookie('token')}`,
}
})
const data = await res.json();
console.log('Data on action', data)
if (data.error) {
return data.error
}
return data
} catch (error) {
return console.error(error);
}
}
Make the request correctly. The problem is when it receives the page parameter for the first time. It always goes undefined.
An example of use is this table, which has access to the router, and receives the page number through the queries. Accessing from http://host.com/dashboard/waiting/number-of-page-passed-by-query
export default function WaitingHiringTable () {
const router = useRouter()
const { page } = router.query
const [people, setPeople] = useState([])
useEffect(() => {
loadUser()
}, [])
const loadUser = async () => {
const users = await getInactiveUsers(page)
if (users.error) {
console.log(users.error)
} else {
setPeople(users.data)
}
}
return (
<div className="flex flex-col mt-10">
<div className="-my-2 overflow-x-auto sm:-mx-6 lg:-mx-8">
<div className="py-2 align-middle inline-block min-w-full sm:px-6 lg:px-8">
<div className="shadow overflow-hidden border-b border-gray-200 sm:rounded-lg">
<table className="min-w-full divide-y divide-gray-200">
<thead className="bg-gray-50">
<tr>
<th
scope="col"
className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
>
Name
</th>
<th
scope="col"
className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
>
Email
</th>
<th
scope="col"
className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
>
Phone
</th>
<th
scope="col"
className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
>
Role
</th>
<th scope="col" className="relative px-6 py-3">
<span className="sr-only">Edit</span>
</th>
</tr>
</thead>
<tbody>
{
people
.map((person, personIdx) => (
<tr key={person.email} className={personIdx % 2 === 0 ? 'bg-white' : 'bg-gray-50'}>
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">{person.firstName} {person.lastName}</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500"><a href={`mailto:${person.email}`}>{person.email}</a></td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500"><a href={`tel:${person.phone}`}>{person.phone}</a></td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{person.role}</td>
<td className="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
<button type="button" className="inline-flex items-center px-2.5 py-1.5 border border-gray-300 shadow-sm text-xs font-medium rounded text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">Approve</button>
</td>
</tr>
))
}
</tbody>
</table>
{
people.length && (
<nav className="bg-white px-4 py-3 flex items-center justify-between border-t border-gray-200 sm:px-6" aria-label="Pagination">
<div className="hidden sm:block">
<p className="text-sm text-gray-700">
Showing <span className="font-medium">1</span> to <span className="font-medium">{people.length}</span> of{' '}
<span className="font-medium">{people.length}</span> results
</p>
</div>
<div className="flex-1 flex justify-between sm:justify-end">
<button onClick={() => setNewPage(page - 1)}
className="relative inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50">
Previous
</button>
<button onClick={() => setNewPage(page + 1)}
className="relative inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50">
Next
</button>
</div>
</nav>
)
}
</div>
</div>
</div>
</div>
)
}
I have created a modal dialog component with a function toggleModal() which toggles the dialog.
I need to import this component and trigger toggleModal() from the parent component.
Bellow the component code to be dynamically imported:
import { useState, useEffect, useContext } from 'react'
const Modal = (props) => {
const [IsModalOpen, setIsModalOpen] = useState(true)
const toggleModal = () => {
setIsModalOpen(!IsModalOpen)
}
return(
<div className="fixed z-10 inset-0 overflow-y-auto">
<div className="flex">
<span className="hidden"></span>
<div ref={ref} className="inline-block" role="dialog" aria-modal="true" aria-labelledby="modal-headline">
<div className="mt-3">
<h3 className="text-lg leading-6 font-medium text-gray-900" id="modal-headline">
Deactivate account
</h3>
<div className="mt-2">
<p className="text-sm leading-5 text-gray-500">
Are you sure you want to deactivate your account? All of your data will be permanently removed. This action cannot be undone.
</p>
</div>
</div>
<div className="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
<span className="flex w-full rounded-md shadow-sm sm:ml-3 sm:w-auto">
<button type="button" className="inline-flex justify-center w-full rounded-md border border-transparent px-4 py-2 bg-red-600 text-base leading-6 font-medium text-white shadow-sm hover:bg-red-500 focus:outline-none focus:border-red-700 focus:shadow-outline-red transition ease-in-out duration-150 sm:text-sm sm:leading-5">
Deactivate
</button>
</span>
<span className="mt-3 flex w-full rounded-md shadow-sm sm:mt-0 sm:w-auto">
<button onClick={() => toggleModal()} type="button" className="inline-flex justify-center w-full rounded-md border border-gray-300 px-4 py-2 bg-white text-base leading-6 font-medium text-gray-700 shadow-sm hover:text-gray-500 focus:outline-none focus:border-blue-300 focus:shadow-outline-blue transition ease-in-out duration-150 sm:text-sm sm:leading-5">
Cancel
</button>
</span>
</div>
</div>
</div>
</div>
)
}
export default Modal
Here I import the component:
import { useState, useEffect } from 'react'
import dynamic from 'next/dynamic'
const Event = (props) => {
const {eventKey} = props
const [eventData, setEventData] = useState(null)
const [showModal, setshowModal] = useState(false)
const Modal = dynamic(() => import('./Modal'))
const openModal = () => {
setshowModal(true)
}
return (
eventData &&
<div className="max-w-sm rounded overflow-hidden shadow-lg m-3 sm:m-3 md:m-4 lg:m-6 ">
<img className="w-full" src={eventData.img} alt="Sunset in the mountains" />
<div className="px-6 py-4">
<div className="font-bold text-xl mb-2">{eventData.title}</div>
<div className="font-medium text-xs mb-2 text-gray-500">De {eventData.start} a {eventData.end}</div>
<p className="text-gray-700 text-base">
{eventData.desc}
</p>
</div>
<div className="px-6 pt-4 pb-2">
<span className="inline-block bg-gray-200 rounded-full px-3 py-1 text-xs font-semibold text-gray-500 mr-2 mb-2">#Yoga</span>
<span className="inline-block bg-gray-200 rounded-full px-3 py-1 text-sm font-semibold text-gray-500 mr-2 mb-2">#winter</span>
<button onClick={() => openModal()} className="bg-indigo-600 hover:bg-indigo-500 text-xs font-medium text-white py-1 px-2 rounded-full">Recordarme</button>
</div>
{showModal && <Modal />}
</div>
)
}
export default Event
How can I import the component and trigger toggleModal() to display the modal dialog?
I have a download button from which the user can download the assets. but I want only authenticated users can download. If the user is not authenticated and click on the download button I want to show them a modal with a login button on it. I have a modal component. I am using React.
Please help me.
Getting currently authenticated user from the firebase.
const {currentUser} = useAuth();
This is my code for the download button.
<a className="bg-secondary text-white font-bold py-3
px-5 rounded text-2xl focus:outline-none mt-3 block w-48" href="YOUR_URL">
<i className="animate-bounce fas fa-arrow-down mr-2"></i>
Download
</a>
Modal Code:
import React from "react";
import { useState } from "react";
export default function Modal() {
const [showModal, setShowModal] = useState(true);
console.log('modal');
return (
<>
{showModal ? (
<>
<div
className="justify-center items-center flex overflow-x-hidden overflow-y-auto fixed inset-0 z-50 outline-none focus:outline-none"
onClick={() => setShowModal(false)}
>
<div className="relative w-auto my-6 mx-auto max-w-3xl">
{/*content*/}
<div className="border-0 rounded-lg shadow-lg relative flex flex-col w-full bg-white outline-none focus:outline-none">
{/*header*/}
<div className="flex items-start justify-between p-5 border-b border-solid border-gray-300 rounded-t">
<h3 className="text-3xl font-semibold">
Modal Title
</h3>
<button
className="p-1 ml-auto bg-transparent border-0 text-black opacity-5 float-right text-3xl leading-none font-semibold outline-none focus:outline-none"
onClick={() => setShowModal(false)}
>
<span className="bg-transparent text-black opacity-5 h-6 w-6 text-2xl block outline-none focus:outline-none">
×
</span>
</button>
</div>
{/*body*/}
<div className="relative p-6 flex-auto">
<p className="my-4 text-gray-600 text-lg leading-relaxed">
I always felt like I could do anything. That’s the main
thing people are controlled by! Thoughts- their perception
of themselves! They're slowed down by their perception of
themselves. If you're taught you can’t do anything, you
won’t do anything. I was taught I could do everything.
</p>
</div>
{/*footer*/}
<div className="flex items-center justify-end p-6 border-t border-solid border-gray-300 rounded-b">
<button
className="text-red-500 background-transparent font-bold uppercase px-6 py-2 text-sm outline-none focus:outline-none mr-1 mb-1"
type="button"
style={{ transition: "all .15s ease" }}
onClick={() => setShowModal(false)}
>
Close
</button>
<button
className="bg-green-500 text-white active:bg-green-600 font-bold uppercase text-sm px-6 py-3 rounded shadow hover:shadow-lg outline-none focus:outline-none mr-1 mb-1"
type="button"
style={{ transition: "all .15s ease" }}
onClick={() => setShowModal(false)}
>
Save Changes
</button>
</div>
</div>
</div>
</div>
<div className="opacity-25 fixed inset-0 z-40 bg-black"></div>
</>
) : null}
</>
);
}
You could use button instead of a and use onClick to check if currentUser exists when you click button.
This is final code:
function x {
const {currentUser} = useAuth();
const downloadAssets = () => {
if(currentUser){
window.open("url assets here", '_blank', 'noopener,noreferrer')
}else{
// Open modal here
alert("User not authenticated");
}
}
return {
<>
...
<button onClick={downloadAssets}><i className="animate-bounce fas fa-arrow-down mr-2"></I>Download</button>
...
</>
}