React Hooks
Hooks
- Estado (useState)
- Efeitos Colaterais (useEffect)
- Contexto (useContext)
- Reducer (useReduce)
- Referência (useRef)
- Callback (useCallback)
- Cache (useMemo)
useState
Home
const [isShowValues, setIsShowValues] = useState(true);
const toggleShowValues = () => {
setIsShowValues(!isShowValues);
};
const [isShowValues, setIsShowValues] = useState(true);
const toggleShowValues = () => {
setIsShowValues(!isShowValues);
};
Conditional Rendering
{isShowValues ? <IconEye size={24} /> : <IconEyeOff size={24} />}
{isShowValues ? <IconEye size={24} /> : <IconEyeOff size={24} />}
/codes/react/hooks/invest-app/src/app/page.jsx
'use client';
import { useState } from 'react';
import { IconEye, IconEyeOff } from '@tabler/icons-react';
import InvestmentCard from '@/components/InvestmentCard';
import { investments } from '@/data/seed';
export default function Home() {
const [isShowValues, setIsShowValues] = useState(true);
const toggleShowValues = () => {
setIsShowValues(!isShowValues);
};
return (
<>
<header className="my-12">
<div className="float-right" onClick={toggleShowValues}>
{isShowValues ? <IconEye size={24} /> : <IconEyeOff size={24} />}
</div>
<h1 className="text-center text-2xl font-bold">Investimentos</h1>
</header>
<div className="investments grid grid-cols-3 gap-3">
{investments.map((investment) => (
<InvestmentCard
{...investment}
isShowValue={isShowValues}
key={investment.id}
/>
))}
</div>
</>
);
}
/codes/react/hooks/invest-app/src/app/page.jsx
'use client';
import { useState } from 'react';
import { IconEye, IconEyeOff } from '@tabler/icons-react';
import InvestmentCard from '@/components/InvestmentCard';
import { investments } from '@/data/seed';
export default function Home() {
const [isShowValues, setIsShowValues] = useState(true);
const toggleShowValues = () => {
setIsShowValues(!isShowValues);
};
return (
<>
<header className="my-12">
<div className="float-right" onClick={toggleShowValues}>
{isShowValues ? <IconEye size={24} /> : <IconEyeOff size={24} />}
</div>
<h1 className="text-center text-2xl font-bold">Investimentos</h1>
</header>
<div className="investments grid grid-cols-3 gap-3">
{investments.map((investment) => (
<InvestmentCard
{...investment}
isShowValue={isShowValues}
key={investment.id}
/>
))}
</div>
</>
);
}
InvestmentCard
{isShowValue ? formatCurrency(value / 100) : 'R$ ****'}
{isShowValue ? formatCurrency(value / 100) : 'R$ ****'}
/codes/react/hooks/invest-app/src/components/InvestmentCard.jsx
'use client';
import Link from 'next/link';
import { IconTrash } from '@tabler/icons-react';
import { formatCurrency, formatDate } from '@/lib/format';
export default function InvestmentCard({
id,
name,
value,
origin,
category,
interest,
created_at,
isShowValue,
}) {
const handleDeleteInvestment = (name) => {
confirm(`Deseja remover ${name}?`);
};
return (
<div className="bg-white shadow-md rounded-lg p-4 relative">
<div className="flex justify-between items-center">
<Link href={`/investments/${id}`}>
<h3 className="investment-name text-lg font-semibold text-gray-700">
{name}
</h3>
</Link>
<p className="investment-value text-lg font-semibold text-gray-700">
{isShowValue ? formatCurrency(value / 100) : 'R$ ****'}
</p>
</div>
<div className="mt-4">
...
</div>
<div className="absolute bottom-4 right-4 inline-flex">
<IconTrash
size={20}
className="text-gray-400 hover:text-gray-500 cursor-pointer"
onClick={() => handleDeleteInvestment(name)}
/>
</div>
</div>
);
}
/codes/react/hooks/invest-app/src/components/InvestmentCard.jsx
'use client';
import Link from 'next/link';
import { IconTrash } from '@tabler/icons-react';
import { formatCurrency, formatDate } from '@/lib/format';
export default function InvestmentCard({
id,
name,
value,
origin,
category,
interest,
created_at,
isShowValue,
}) {
const handleDeleteInvestment = (name) => {
confirm(`Deseja remover ${name}?`);
};
return (
<div className="bg-white shadow-md rounded-lg p-4 relative">
<div className="flex justify-between items-center">
<Link href={`/investments/${id}`}>
<h3 className="investment-name text-lg font-semibold text-gray-700">
{name}
</h3>
</Link>
<p className="investment-value text-lg font-semibold text-gray-700">
{isShowValue ? formatCurrency(value / 100) : 'R$ ****'}
</p>
</div>
<div className="mt-4">
...
</div>
<div className="absolute bottom-4 right-4 inline-flex">
<IconTrash
size={20}
className="text-gray-400 hover:text-gray-500 cursor-pointer"
onClick={() => handleDeleteInvestment(name)}
/>
</div>
</div>
);
}
useEffect
Home
const loadInvestment = async () => {
const investments = await Storage.read('investments');
setInvestments(investments);
};
useEffect(() => {
loadInvestment();
}, []);
const loadInvestment = async () => {
const investments = await Storage.read('investments');
setInvestments(investments);
};
useEffect(() => {
loadInvestment();
}, []);
/codes/react/hooks/invest-app/src/app/page.jsx
'use client';
import Storage from '@/services/supabase';
import { useEffect, useState } from 'react';
import { IconEye, IconEyeOff } from '@tabler/icons-react';
import InvestmentCard from '@/components/InvestmentCard';
export default function Home() {
const [isShowValues, setIsShowValues] = useState(true);
const [investments, setInvestments] = useState([]);
const toggleShowValues = () => {
setIsShowValues(!isShowValues);
};
const loadInvestment = async () => {
const investments = await Storage.read('investments');
setInvestments(investments);
};
useEffect(() => {
loadInvestment();
}, []);
return (
<>
<header className="my-12">
<div className="float-right" onClick={toggleShowValues}>
{isShowValues ? <IconEye size={24} /> : <IconEyeOff size={24} />}
</div>
<h1 className="text-center text-2xl font-bold">Investimentos</h1>
</header>
<div className="investments grid grid-cols-3 gap-3">
{investments.map((investment) => (
<InvestmentCard
{...investment}
isShowValue={isShowValues}
investments={investments}
setInvestments={setInvestments}
key={investment.id}
/>
))}
</div>
</>
);
}
/codes/react/hooks/invest-app/src/app/page.jsx
'use client';
import Storage from '@/services/supabase';
import { useEffect, useState } from 'react';
import { IconEye, IconEyeOff } from '@tabler/icons-react';
import InvestmentCard from '@/components/InvestmentCard';
export default function Home() {
const [isShowValues, setIsShowValues] = useState(true);
const [investments, setInvestments] = useState([]);
const toggleShowValues = () => {
setIsShowValues(!isShowValues);
};
const loadInvestment = async () => {
const investments = await Storage.read('investments');
setInvestments(investments);
};
useEffect(() => {
loadInvestment();
}, []);
return (
<>
<header className="my-12">
<div className="float-right" onClick={toggleShowValues}>
{isShowValues ? <IconEye size={24} /> : <IconEyeOff size={24} />}
</div>
<h1 className="text-center text-2xl font-bold">Investimentos</h1>
</header>
<div className="investments grid grid-cols-3 gap-3">
{investments.map((investment) => (
<InvestmentCard
{...investment}
isShowValue={isShowValues}
investments={investments}
setInvestments={setInvestments}
key={investment.id}
/>
))}
</div>
</>
);
}
InvestmentCard
const handleDeleteInvestment = (id) => {
if (confirm(`Deseja remover ${name}?`)) {
const newInvestments = investments.filter(
(investment) => investment.id !== id
);
setInvestments(newInvestments);
Storage.remove('investments', id);
}
};
const handleDeleteInvestment = (id) => {
if (confirm(`Deseja remover ${name}?`)) {
const newInvestments = investments.filter(
(investment) => investment.id !== id
);
setInvestments(newInvestments);
Storage.remove('investments', id);
}
};
/codes/react/hooks/invest-app/src/components/InvestmentCard.jsx
import Link from 'next/link';
import { IconTrash } from '@tabler/icons-react';
import { formatCurrency, formatDate } from '@/lib/format';
export default function InvestmentCard({
id,
name,
value,
origin,
category,
interest,
created_at,
isShowValue,
investments,
setInvestments,
}) {
const handleDeleteInvestment = (id) => {
if (confirm(`Deseja remover ${name}?`)) {
const newInvestments = investments.filter(
(investment) => investment.id !== id
);
setInvestments(newInvestments);
Storage.remove('investments', id);
}
};
return (
<div className="bg-white shadow-md rounded-lg p-4 relative">
<div className="flex justify-between items-center">
<Link href={`/investments/${id}`}>
<h3 className="investment-name text-lg font-semibold text-gray-700">
{name}
</h3>
</Link>
<p className="investment-value text-lg font-semibold text-gray-700">
{isShowValue ? formatCurrency(value / 100) : 'R$ ****'}
</p>
</div>
<div className="mt-4">
...
</div>
<div className="absolute bottom-4 right-4 inline-flex">
<IconTrash
size={20}
className="text-gray-400 hover:text-gray-500 cursor-pointer"
onClick={() => handleDeleteInvestment(id)}
/>
</div>
</div>
);
}
/codes/react/hooks/invest-app/src/components/InvestmentCard.jsx
import Link from 'next/link';
import { IconTrash } from '@tabler/icons-react';
import { formatCurrency, formatDate } from '@/lib/format';
export default function InvestmentCard({
id,
name,
value,
origin,
category,
interest,
created_at,
isShowValue,
investments,
setInvestments,
}) {
const handleDeleteInvestment = (id) => {
if (confirm(`Deseja remover ${name}?`)) {
const newInvestments = investments.filter(
(investment) => investment.id !== id
);
setInvestments(newInvestments);
Storage.remove('investments', id);
}
};
return (
<div className="bg-white shadow-md rounded-lg p-4 relative">
<div className="flex justify-between items-center">
<Link href={`/investments/${id}`}>
<h3 className="investment-name text-lg font-semibold text-gray-700">
{name}
</h3>
</Link>
<p className="investment-value text-lg font-semibold text-gray-700">
{isShowValue ? formatCurrency(value / 100) : 'R$ ****'}
</p>
</div>
<div className="mt-4">
...
</div>
<div className="absolute bottom-4 right-4 inline-flex">
<IconTrash
size={20}
className="text-gray-400 hover:text-gray-500 cursor-pointer"
onClick={() => handleDeleteInvestment(id)}
/>
</div>
</div>
);
}
useContext
InvestmentContext
/codes/react/hooks/invest-app/src/context/InvestmentContext.jsx
'use client';
import { createContext, useState, useContext } from 'react';
export const InvestmentContext = createContext({});
export function InvestmentProvider({ children }) {
const [investments, setInvestments] = useState([]);
...
return (
<InvestmentContext.Provider
value={{
investments,
setInvestments,
...
}}
>
{children}
</InvestmentContext.Provider>
);
}
export function useInvestment() {
return useContext(InvestmentContext);
}
/codes/react/hooks/invest-app/src/context/InvestmentContext.jsx
'use client';
import { createContext, useState, useContext } from 'react';
export const InvestmentContext = createContext({});
export function InvestmentProvider({ children }) {
const [investments, setInvestments] = useState([]);
...
return (
<InvestmentContext.Provider
value={{
investments,
setInvestments,
...
}}
>
{children}
</InvestmentContext.Provider>
);
}
export function useInvestment() {
return useContext(InvestmentContext);
}
/codes/react/hooks/invest-app/src/context/InvestmentContext.jsx
'use client';
import { createContext, useState, useContext } from 'react';
import Storage from '@/services/supabase';
export const InvestmentContext = createContext({});
export function InvestmentProvider({ children }) {
const [investments, setInvestments] = useState([]);
const [isShowValues, setIsShowValues] = useState(true);
const toggleShowValues = () => {
setIsShowValues(!isShowValues);
};
const loadInvestments = async () => {
const investments = await Storage.read('investments');
setInvestments(investments);
};
const removeInvestment = (id) => {
const newInvestments = investments.filter(
(investment) => investment.id !== id
);
setInvestments(newInvestments);
Storage.remove('investments', id);
};
return (
<InvestmentContext.Provider
value={{
investments,
setInvestments,
isShowValues,
setIsShowValues,
toggleShowValues,
loadInvestments,
removeInvestment,
}}
>
{children}
</InvestmentContext.Provider>
);
}
export function useInvestment() {
return useContext(InvestmentContext);
}
/codes/react/hooks/invest-app/src/context/InvestmentContext.jsx
'use client';
import { createContext, useState, useContext } from 'react';
import Storage from '@/services/supabase';
export const InvestmentContext = createContext({});
export function InvestmentProvider({ children }) {
const [investments, setInvestments] = useState([]);
const [isShowValues, setIsShowValues] = useState(true);
const toggleShowValues = () => {
setIsShowValues(!isShowValues);
};
const loadInvestments = async () => {
const investments = await Storage.read('investments');
setInvestments(investments);
};
const removeInvestment = (id) => {
const newInvestments = investments.filter(
(investment) => investment.id !== id
);
setInvestments(newInvestments);
Storage.remove('investments', id);
};
return (
<InvestmentContext.Provider
value={{
investments,
setInvestments,
isShowValues,
setIsShowValues,
toggleShowValues,
loadInvestments,
removeInvestment,
}}
>
{children}
</InvestmentContext.Provider>
);
}
export function useInvestment() {
return useContext(InvestmentContext);
}
RootLayout
/codes/react/hooks/invest-app/src/app/layout.jsx
import { Inter } from 'next/font/google';
import { InvestmentProvider } from '@/contexts/InvestmentContext';
import './globals.css';
const inter = Inter({ subsets: ['latin'] });
export const metadata = {
title: 'invest-app',
description: 'Aplicação de investimentos',
};
export default function RootLayout({ children }) {
return (
<html lang="en">
<body className={`${inter.className} bg-gray-100`}>
<div className="container min-h-screen mx-auto lg:max-w-screen-lg">
<InvestmentProvider>{children}</InvestmentProvider>
</div>
</body>
</html>
);
}
/codes/react/hooks/invest-app/src/app/layout.jsx
import { Inter } from 'next/font/google';
import { InvestmentProvider } from '@/contexts/InvestmentContext';
import './globals.css';
const inter = Inter({ subsets: ['latin'] });
export const metadata = {
title: 'invest-app',
description: 'Aplicação de investimentos',
};
export default function RootLayout({ children }) {
return (
<html lang="en">
<body className={`${inter.className} bg-gray-100`}>
<div className="container min-h-screen mx-auto lg:max-w-screen-lg">
<InvestmentProvider>{children}</InvestmentProvider>
</div>
</body>
</html>
);
}
Home
const { investments, loadInvestments, isShowValues, toggleShowValues } =
useInvestment();
const { investments, loadInvestments, isShowValues, toggleShowValues } =
useInvestment();
/codes/react/hooks/invest-app/src/app/page.jsx
'use client';
import { useEffect } from 'react';
import { IconEye, IconEyeOff } from '@tabler/icons-react';
import InvestmentCard from '@/components/InvestmentCard';
import { useInvestment } from '@/contexts/InvestmentContext';
export default function Home() {
const { investments, loadInvestments, isShowValues, toggleShowValues } =
useInvestment();
useEffect(() => {
loadInvestments();
}, []);
return (
<>
<header className="my-12">
<div className="float-right" onClick={toggleShowValues}>
{isShowValues ? <IconEye size={24} /> : <IconEyeOff size={24} />}
</div>
<h1 className="text-center text-2xl font-bold">Investimentos</h1>
</header>
<div className="investments grid grid-cols-3 gap-3">
{investments.map((investment) => (
<InvestmentCard {...investment} key={investment.id} />
))}
</div>
</>
);
}
/codes/react/hooks/invest-app/src/app/page.jsx
'use client';
import { useEffect } from 'react';
import { IconEye, IconEyeOff } from '@tabler/icons-react';
import InvestmentCard from '@/components/InvestmentCard';
import { useInvestment } from '@/contexts/InvestmentContext';
export default function Home() {
const { investments, loadInvestments, isShowValues, toggleShowValues } =
useInvestment();
useEffect(() => {
loadInvestments();
}, []);
return (
<>
<header className="my-12">
<div className="float-right" onClick={toggleShowValues}>
{isShowValues ? <IconEye size={24} /> : <IconEyeOff size={24} />}
</div>
<h1 className="text-center text-2xl font-bold">Investimentos</h1>
</header>
<div className="investments grid grid-cols-3 gap-3">
{investments.map((investment) => (
<InvestmentCard {...investment} key={investment.id} />
))}
</div>
</>
);
}
InvestmentCard
const { removeInvestment, isShowValues } = useInvestment();
const { removeInvestment, isShowValues } = useInvestment();
/codes/react/hooks/invest-app/src/components/InvestmentCard.jsx
'use client';
import Link from 'next/link';
import { IconTrash } from '@tabler/icons-react';
import { formatCurrency, formatDate } from '@/lib/format';
import { useInvestment } from '@/contexts/InvestmentContext';
export default function InvestmentCard({
id,
name,
value,
origin,
category,
interest,
created_at,
}) {
const { removeInvestment, isShowValues } = useInvestment();
const handleDeleteInvestment = (id) => {
if (confirm(`Deseja remover ${name}?`)) {
removeInvestment(id);
}
};
return (
<div className="bg-white shadow-md rounded-lg p-4 relative">
<div className="flex justify-between items-center">
<Link href={`/investments/${id}`}>
<h3 className="investment-name text-lg font-semibold text-gray-700">
{name}
</h3>
</Link>
<p className="investment-value text-lg font-semibold text-gray-700">
{isShowValues ? formatCurrency(value / 100) : 'R$ ****'}
</p>
</div>
<div className="mt-4">
...
</div>
<div className="absolute bottom-4 right-4 inline-flex">
<IconTrash
size={20}
className="text-gray-400 hover:text-gray-500 cursor-pointer"
onClick={() => handleDeleteInvestment(id)}
/>
</div>
</div>
);
}
/codes/react/hooks/invest-app/src/components/InvestmentCard.jsx
'use client';
import Link from 'next/link';
import { IconTrash } from '@tabler/icons-react';
import { formatCurrency, formatDate } from '@/lib/format';
import { useInvestment } from '@/contexts/InvestmentContext';
export default function InvestmentCard({
id,
name,
value,
origin,
category,
interest,
created_at,
}) {
const { removeInvestment, isShowValues } = useInvestment();
const handleDeleteInvestment = (id) => {
if (confirm(`Deseja remover ${name}?`)) {
removeInvestment(id);
}
};
return (
<div className="bg-white shadow-md rounded-lg p-4 relative">
<div className="flex justify-between items-center">
<Link href={`/investments/${id}`}>
<h3 className="investment-name text-lg font-semibold text-gray-700">
{name}
</h3>
</Link>
<p className="investment-value text-lg font-semibold text-gray-700">
{isShowValues ? formatCurrency(value / 100) : 'R$ ****'}
</p>
</div>
<div className="mt-4">
...
</div>
<div className="absolute bottom-4 right-4 inline-flex">
<IconTrash
size={20}
className="text-gray-400 hover:text-gray-500 cursor-pointer"
onClick={() => handleDeleteInvestment(id)}
/>
</div>
</div>
);
}
Invest-app (CRUD)
InvestmenContext
/codes/react/hooks/invest-app/src/context/InvestmenContext.jsx
'use client';
import { createContext, useState, useContext } from 'react';
import Storage from '@/services/supabase';
import { formatDate } from '@/lib/format';
export const InvestmentContext = createContext({});
export function InvestmentProvider({ children }) {
const initialInvestmentFormData = {
name: '',
value: '',
origin: '',
category: '',
interest: '',
created_at: '',
};
const [investments, setInvestments] = useState([]);
const [isShowValues, setIsShowValues] = useState(true);
const [isShowModal, setIsShowModal] = useState(false);
const [isShowInvestmentForm, setIsShowInvestmentForm] = useState(false);
const [investmentModalData, setInvestmentModalData] = useState({});
const [investmentFormData, setInvestmentFormData] = useState(
initialInvestmentFormData
);
const [investmentFormAction, setInvestmentFormAction] = useState('');
const toggleShowValues = () => {
setIsShowValues(!isShowValues);
};
const toggleShowModal = () => {
setIsShowModal(!isShowModal);
};
const toggleShowInvestmentForm = () => {
setIsShowInvestmentForm(!isShowInvestmentForm);
};
const loadInvestments = async () => {
const investments = await Storage.read('investments');
setInvestments(investments);
};
const createInvestment = async (investment) => {
const newInvestment = await Storage.create('investments', investment);
setInvestments([...investments, newInvestment]);
};
const updateInvestment = async (investment) => {
const newInvestments = investments.filter(
(item) => item.id !== investment.id
);
setInvestments([...newInvestments, investment]);
Storage.update('investments', investment);
};
const removeInvestment = (id) => {
const newInvestments = investments.filter(
(investment) => investment.id !== id
);
setInvestments(newInvestments);
Storage.remove('investments', id);
};
const handleLoadModalData = (id) => {
const investment = investments.find((investment) => investment.id === id);
setInvestmentModalData(investment);
toggleShowModal();
};
const handleCreateInvestment = async () => {
setInvestmentFormData(initialInvestmentFormData);
setInvestmentFormAction('create');
toggleShowInvestmentForm();
};
const handleDeleteInvestment = () => {
removeInvestment(investmentModalData.id);
toggleShowModal();
};
const handleUpdateInvestment = async (id) => {
loadInvestmentFormData(id);
setInvestmentFormAction('update');
toggleShowInvestmentForm();
};
const handleFormSubmit = async (event) => {
event.preventDefault();
investmentFormData.value = Number(investmentFormData.value) * 100;
investmentFormData.created_at = new Date(
investmentFormData.created_at + 'T00:00:00-03:00'
).toISOString();
investmentFormAction === 'create'
? createInvestment(investmentFormData)
: updateInvestment(investmentFormData);
toggleShowInvestmentForm();
};
const loadInvestmentFormData = (id) => {
const investment = investments.find((investment) => investment.id === id);
investment.value = Number(investment.value) / 100;
investment.created_at = formatDate(investment.created_at, 'ymd');
setInvestmentFormData(investment);
};
return (
<InvestmentContext.Provider
value={{
createInvestment,
handleCreateInvestment,
handleDeleteInvestment,
handleFormSubmit,
handleLoadModalData,
handleUpdateInvestment,
initialInvestmentFormData,
investmentFormData,
investmentModalData,
investments,
isShowInvestmentForm,
isShowModal,
isShowValues,
loadInvestmentFormData,
loadInvestments,
removeInvestment,
setInvestmentFormData,
setInvestments,
setIsShowModal,
setIsShowValues,
toggleShowInvestmentForm,
toggleShowModal,
toggleShowValues,
updateInvestment,
}}
>
{children}
</InvestmentContext.Provider>
);
}
export function useInvestment() {
return useContext(InvestmentContext);
}
/codes/react/hooks/invest-app/src/context/InvestmenContext.jsx
'use client';
import { createContext, useState, useContext } from 'react';
import Storage from '@/services/supabase';
import { formatDate } from '@/lib/format';
export const InvestmentContext = createContext({});
export function InvestmentProvider({ children }) {
const initialInvestmentFormData = {
name: '',
value: '',
origin: '',
category: '',
interest: '',
created_at: '',
};
const [investments, setInvestments] = useState([]);
const [isShowValues, setIsShowValues] = useState(true);
const [isShowModal, setIsShowModal] = useState(false);
const [isShowInvestmentForm, setIsShowInvestmentForm] = useState(false);
const [investmentModalData, setInvestmentModalData] = useState({});
const [investmentFormData, setInvestmentFormData] = useState(
initialInvestmentFormData
);
const [investmentFormAction, setInvestmentFormAction] = useState('');
const toggleShowValues = () => {
setIsShowValues(!isShowValues);
};
const toggleShowModal = () => {
setIsShowModal(!isShowModal);
};
const toggleShowInvestmentForm = () => {
setIsShowInvestmentForm(!isShowInvestmentForm);
};
const loadInvestments = async () => {
const investments = await Storage.read('investments');
setInvestments(investments);
};
const createInvestment = async (investment) => {
const newInvestment = await Storage.create('investments', investment);
setInvestments([...investments, newInvestment]);
};
const updateInvestment = async (investment) => {
const newInvestments = investments.filter(
(item) => item.id !== investment.id
);
setInvestments([...newInvestments, investment]);
Storage.update('investments', investment);
};
const removeInvestment = (id) => {
const newInvestments = investments.filter(
(investment) => investment.id !== id
);
setInvestments(newInvestments);
Storage.remove('investments', id);
};
const handleLoadModalData = (id) => {
const investment = investments.find((investment) => investment.id === id);
setInvestmentModalData(investment);
toggleShowModal();
};
const handleCreateInvestment = async () => {
setInvestmentFormData(initialInvestmentFormData);
setInvestmentFormAction('create');
toggleShowInvestmentForm();
};
const handleDeleteInvestment = () => {
removeInvestment(investmentModalData.id);
toggleShowModal();
};
const handleUpdateInvestment = async (id) => {
loadInvestmentFormData(id);
setInvestmentFormAction('update');
toggleShowInvestmentForm();
};
const handleFormSubmit = async (event) => {
event.preventDefault();
investmentFormData.value = Number(investmentFormData.value) * 100;
investmentFormData.created_at = new Date(
investmentFormData.created_at + 'T00:00:00-03:00'
).toISOString();
investmentFormAction === 'create'
? createInvestment(investmentFormData)
: updateInvestment(investmentFormData);
toggleShowInvestmentForm();
};
const loadInvestmentFormData = (id) => {
const investment = investments.find((investment) => investment.id === id);
investment.value = Number(investment.value) / 100;
investment.created_at = formatDate(investment.created_at, 'ymd');
setInvestmentFormData(investment);
};
return (
<InvestmentContext.Provider
value={{
createInvestment,
handleCreateInvestment,
handleDeleteInvestment,
handleFormSubmit,
handleLoadModalData,
handleUpdateInvestment,
initialInvestmentFormData,
investmentFormData,
investmentModalData,
investments,
isShowInvestmentForm,
isShowModal,
isShowValues,
loadInvestmentFormData,
loadInvestments,
removeInvestment,
setInvestmentFormData,
setInvestments,
setIsShowModal,
setIsShowValues,
toggleShowInvestmentForm,
toggleShowModal,
toggleShowValues,
updateInvestment,
}}
>
{children}
</InvestmentContext.Provider>
);
}
export function useInvestment() {
return useContext(InvestmentContext);
}
Home
/codes/react/hooks/invest-app/src/app/page.jsx
'use client';
import { useEffect } from 'react';
import { IconEye, IconEyeOff } from '@tabler/icons-react';
import InvestmentCard from '@/components/InvestmentCard';
import { useInvestment } from '@/contexts/InvestmentContext';
import InvestmentForm from '@/components/InvestmentForm';
import Modal from '@/components/Modal';
export default function Home() {
const {
investments,
loadInvestments,
isShowValues,
toggleShowValues,
handleCreateInvestment,
} = useInvestment();
useEffect(() => {
loadInvestments();
}, []);
return (
<>
<header className="py-12">
<div className="float-right" onClick={toggleShowValues}>
{isShowValues ? <IconEye size={24} /> : <IconEyeOff size={24} />}
</div>
<h1 className="text-center text-2xl font-bold">Investimentos</h1>
</header>
<div className="investments grid grid-cols-3 gap-3">
{investments.map((investment) => (
<InvestmentCard {...investment} key={investment.id} />
))}
</div>
<div className="fixed bottom-8 right-8">
<button
type="button"
className="new-investment-btn py-3 px-4 inline-flex justify-center items-center gap-2 rounded-md border border-transparent font-semibold bg-gray-500 text-white hover:bg-gray-600 focus:outline-none focus:ring-2 focus:ring-gray-500 focus:ring-offset-2 transition-all text-sm dark:bg-gray-700 dark:hover:bg-gray-600 dark:focus:ring-offset-gray-800"
onClick={() => handleCreateInvestment()}
>
+
</button>
</div>
<InvestmentForm />
<Modal />
</>
);
}
/codes/react/hooks/invest-app/src/app/page.jsx
'use client';
import { useEffect } from 'react';
import { IconEye, IconEyeOff } from '@tabler/icons-react';
import InvestmentCard from '@/components/InvestmentCard';
import { useInvestment } from '@/contexts/InvestmentContext';
import InvestmentForm from '@/components/InvestmentForm';
import Modal from '@/components/Modal';
export default function Home() {
const {
investments,
loadInvestments,
isShowValues,
toggleShowValues,
handleCreateInvestment,
} = useInvestment();
useEffect(() => {
loadInvestments();
}, []);
return (
<>
<header className="py-12">
<div className="float-right" onClick={toggleShowValues}>
{isShowValues ? <IconEye size={24} /> : <IconEyeOff size={24} />}
</div>
<h1 className="text-center text-2xl font-bold">Investimentos</h1>
</header>
<div className="investments grid grid-cols-3 gap-3">
{investments.map((investment) => (
<InvestmentCard {...investment} key={investment.id} />
))}
</div>
<div className="fixed bottom-8 right-8">
<button
type="button"
className="new-investment-btn py-3 px-4 inline-flex justify-center items-center gap-2 rounded-md border border-transparent font-semibold bg-gray-500 text-white hover:bg-gray-600 focus:outline-none focus:ring-2 focus:ring-gray-500 focus:ring-offset-2 transition-all text-sm dark:bg-gray-700 dark:hover:bg-gray-600 dark:focus:ring-offset-gray-800"
onClick={() => handleCreateInvestment()}
>
+
</button>
</div>
<InvestmentForm />
<Modal />
</>
);
}
InvestmentCard
/codes/react/hooks/invest-app/src/components/InvestmentCard.jsx
'use client';
import Link from 'next/link';
import { IconTrash, IconPencil } from '@tabler/icons-react';
import { formatCurrency, formatDate } from '@/lib/format';
import { useInvestment } from '@/contexts/InvestmentContext';
export default function InvestmentCard({
id,
name,
value,
origin,
category,
interest,
created_at,
}) {
const { isShowValues, handleLoadModalData, handleUpdateInvestment } =
useInvestment();
return (
<div className="bg-white shadow-md rounded-lg p-4 relative">
<div className="flex justify-between items-center">
<Link href={`/investments/${id}`}>
<h3 className="investment-name text-lg font-semibold text-gray-700">
{name}
</h3>
</Link>
<p className="investment-value text-lg font-semibold text-gray-700">
{isShowValues ? formatCurrency(value / 100) : 'R$ ****'}
</p>
</div>
<div className="mt-4">
<p className="text-sm text-gray-500">
<span className="font-bold mr-1">Origem:</span>
<span className="investment-origin">{origin}</span>
</p>
<p className="text-sm text-gray-500">
<span className="font-bold mr-1">Categoria:</span>
<span className="investment-category">{category}</span>
</p>
<p className="text-sm text-gray-500">
<span className="font-bold mr-1">Taxa:</span>
<span className="investment-interest">{interest}</span>
</p>
<p className="text-sm text-gray-500">
<span className="font-bold mr-1">Data:</span>
<span className="investment-created_at">
{formatDate(created_at)}
</span>
</p>
</div>
<div className="absolute bottom-4 right-4 inline-flex">
<IconTrash
size={20}
className="text-gray-400 hover:text-gray-500 cursor-pointer"
onClick={() => handleLoadModalData(id)}
/>
<IconPencil
size={20}
className="text-gray-400 hover:text-gray-500 cursor-pointer"
onClick={() => handleUpdateInvestment(id)}
/>
</div>
</div>
);
}
/codes/react/hooks/invest-app/src/components/InvestmentCard.jsx
'use client';
import Link from 'next/link';
import { IconTrash, IconPencil } from '@tabler/icons-react';
import { formatCurrency, formatDate } from '@/lib/format';
import { useInvestment } from '@/contexts/InvestmentContext';
export default function InvestmentCard({
id,
name,
value,
origin,
category,
interest,
created_at,
}) {
const { isShowValues, handleLoadModalData, handleUpdateInvestment } =
useInvestment();
return (
<div className="bg-white shadow-md rounded-lg p-4 relative">
<div className="flex justify-between items-center">
<Link href={`/investments/${id}`}>
<h3 className="investment-name text-lg font-semibold text-gray-700">
{name}
</h3>
</Link>
<p className="investment-value text-lg font-semibold text-gray-700">
{isShowValues ? formatCurrency(value / 100) : 'R$ ****'}
</p>
</div>
<div className="mt-4">
<p className="text-sm text-gray-500">
<span className="font-bold mr-1">Origem:</span>
<span className="investment-origin">{origin}</span>
</p>
<p className="text-sm text-gray-500">
<span className="font-bold mr-1">Categoria:</span>
<span className="investment-category">{category}</span>
</p>
<p className="text-sm text-gray-500">
<span className="font-bold mr-1">Taxa:</span>
<span className="investment-interest">{interest}</span>
</p>
<p className="text-sm text-gray-500">
<span className="font-bold mr-1">Data:</span>
<span className="investment-created_at">
{formatDate(created_at)}
</span>
</p>
</div>
<div className="absolute bottom-4 right-4 inline-flex">
<IconTrash
size={20}
className="text-gray-400 hover:text-gray-500 cursor-pointer"
onClick={() => handleLoadModalData(id)}
/>
<IconPencil
size={20}
className="text-gray-400 hover:text-gray-500 cursor-pointer"
onClick={() => handleUpdateInvestment(id)}
/>
</div>
</div>
);
}
InvestmentForm
/codes/react/hooks/invest-app/src/components/InvestmentForm.jsx
import { useInvestment } from '@/contexts/InvestmentContext';
export default function InvestmentForm() {
const {
isShowInvestmentForm,
toggleShowInvestmentForm,
investmentFormData,
setInvestmentFormData,
handleFormSubmit,
} = useInvestment();
const handleChange = (event) => {
let { name, value } = event.target;
setInvestmentFormData({ ...investmentFormData, [name]: value });
};
return (
<>
{isShowInvestmentForm && (
<div>
<div
className="fixed h-full w-full right-0 top-0 bg-black/50 z-10"
onClick={() => toggleShowInvestmentForm()}
></div>
<div
className="fixed bottom-0 right-0 top-0 z-[1045] flex w-96 max-w-full flex-col border-none bg-white bg-clip-padding text-neutral-700 shadow-sm outline-none transition duration-300 ease-in-out dark:bg-neutral-800 dark:text-neutral-200 [&[data-te-offcanvas-show]]:transform-none"
tabIndex={-1}
>
<div className="flex justify-between items-center py-3 px-4 border-b dark:border-gray-700">
<h3 className="font-bold text-gray-800 dark:text-white">
Investimento
</h3>
<button
type="button"
className="investment-drawer-close inline-flex flex-shrink-0 justify-center items-center h-8 w-8 rounded-md text-gray-500 hover:text-gray-700 focus:outline-none focus:ring-2 focus:ring-gray-400 focus:ring-offset-2 focus:ring-offset-white text-sm dark:text-gray-500 dark:hover:text-gray-400 dark:focus:ring-gray-700 dark:focus:ring-offset-gray-800"
onClick={() => toggleShowInvestmentForm()}
>
<span className="sr-only">Cadastro de Investimento</span>
<svg
className="w-3.5 h-3.5"
width={8}
height={8}
viewBox="0 0 8 8"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M0.258206 1.00652C0.351976 0.912791 0.479126 0.860131 0.611706 0.860131C0.744296 0.860131 0.871447 0.912791 0.965207 1.00652L3.61171 3.65302L6.25822 1.00652C6.30432 0.958771 6.35952 0.920671 6.42052 0.894471C6.48152 0.868271 6.54712 0.854471 6.61352 0.853901C6.67992 0.853321 6.74572 0.865971 6.80722 0.891111C6.86862 0.916251 6.92442 0.953381 6.97142 1.00032C7.01832 1.04727 7.05552 1.1031 7.08062 1.16454C7.10572 1.22599 7.11842 1.29183 7.11782 1.35822C7.11722 1.42461 7.10342 1.49022 7.07722 1.55122C7.05102 1.61222 7.01292 1.6674 6.96522 1.71352L4.31871 4.36002L6.96522 7.00648C7.05632 7.10078 7.10672 7.22708 7.10552 7.35818C7.10442 7.48928 7.05182 7.61468 6.95912 7.70738C6.86642 7.80018 6.74102 7.85268 6.60992 7.85388C6.47882 7.85498 6.35252 7.80458 6.25822 7.71348L3.61171 5.06702L0.965207 7.71348C0.870907 7.80458 0.744606 7.85498 0.613506 7.85388C0.482406 7.85268 0.357007 7.80018 0.264297 7.70738C0.171597 7.61468 0.119017 7.48928 0.117877 7.35818C0.116737 7.22708 0.167126 7.10078 0.258206 7.00648L2.90471 4.36002L0.258206 1.71352C0.164476 1.61976 0.111816 1.4926 0.111816 1.36002C0.111816 1.22744 0.164476 1.10028 0.258206 1.00652Z"
fill="currentColor"
/>
</svg>
</button>
</div>
<div className="p-4 overflow-y-auto">
<form onSubmit={(event) => handleFormSubmit(event)}>
<input type="hidden" id="id" name="id" />
<div className="mb-3">
<label
htmlFor="name"
className="block text-sm font-medium mb-2 dark:text-white"
>
Nome
</label>
<input
type="text"
id="name"
name="name"
onChange={handleChange}
value={investmentFormData.name}
className="py-3 px-4 block w-full border border-gray-200 rounded-md text-sm focus:border-blue-500 focus:ring-blue-500 dark:bg-slate-900 dark:border-gray-700 dark:text-gray-400"
required=""
/>
</div>
<div className="mb-3">
<label
htmlFor="value"
className="block text-sm font-medium mb-2 dark:text-white"
>
Valor
</label>
<input
type="number"
id="value"
name="value"
onChange={handleChange}
value={investmentFormData.value}
className="py-3 px-4 block w-full border border-gray-200 rounded-md text-sm focus:border-blue-500 focus:ring-blue-500 dark:bg-slate-900 dark:border-gray-700 dark:text-gray-400"
required=""
/>
</div>
<div className="mb-3">
<label
htmlFor="origin"
className="block text-sm font-medium mb-2 dark:text-white"
>
Origem
</label>
<input
type="text"
id="origin"
name="origin"
onChange={handleChange}
value={investmentFormData.origin}
className="py-3 px-4 block w-full border border-gray-200 rounded-md text-sm focus:border-blue-500 focus:ring-blue-500 dark:bg-slate-900 dark:border-gray-700 dark:text-gray-400"
required=""
/>
</div>
<div className="mb-3">
<label
htmlFor="category"
className="block text-sm font-medium mb-2 dark:text-white"
>
Categoria
</label>
<input
type="text"
id="category"
name="category"
onChange={handleChange}
value={investmentFormData.category}
className="py-3 px-4 block w-full border border-gray-200 rounded-md text-sm focus:border-blue-500 focus:ring-blue-500 dark:bg-slate-900 dark:border-gray-700 dark:text-gray-400"
required=""
/>
</div>
<div className="mb-3">
<label
htmlFor="interest"
className="block text-sm font-medium mb-2 dark:text-white"
>
Taxa
</label>
<input
type="text"
id="interest"
name="interest"
onChange={handleChange}
value={investmentFormData.interest}
className="py-3 px-4 block w-full border border-gray-200 rounded-md text-sm focus:border-blue-500 focus:ring-blue-500 dark:bg-slate-900 dark:border-gray-700 dark:text-gray-400"
required=""
/>
</div>
<div className="mb-3">
<label
htmlFor="created_at"
className="block text-sm font-medium mb-2 dark:text-white"
>
Data
</label>
<input
type="date"
id="created_at"
name="created_at"
onChange={handleChange}
value={investmentFormData.created_at}
className="py-3 px-4 block w-full border border-gray-200 rounded-md text-sm focus:border-blue-500 focus:ring-blue-500 dark:bg-slate-900 dark:border-gray-700 dark:text-gray-400"
required=""
/>
</div>
<div>
<button
type="submit"
className="py-3 px-4 inline-flex w-full justify-center items-center gap-2 rounded-md border border-transparent font-semibold bg-gray-500 text-white hover:bg-gray-600 focus:outline-none focus:ring-2 focus:ring-gray-500 focus:ring-offset-2 transition-all text-sm dark:bg-gray-700 dark:hover:bg-gray-600 dark:focus:ring-offset-gray-800"
>
Enviar
</button>
</div>
</form>
</div>
</div>
</div>
)}
</>
);
}
/codes/react/hooks/invest-app/src/components/InvestmentForm.jsx
import { useInvestment } from '@/contexts/InvestmentContext';
export default function InvestmentForm() {
const {
isShowInvestmentForm,
toggleShowInvestmentForm,
investmentFormData,
setInvestmentFormData,
handleFormSubmit,
} = useInvestment();
const handleChange = (event) => {
let { name, value } = event.target;
setInvestmentFormData({ ...investmentFormData, [name]: value });
};
return (
<>
{isShowInvestmentForm && (
<div>
<div
className="fixed h-full w-full right-0 top-0 bg-black/50 z-10"
onClick={() => toggleShowInvestmentForm()}
></div>
<div
className="fixed bottom-0 right-0 top-0 z-[1045] flex w-96 max-w-full flex-col border-none bg-white bg-clip-padding text-neutral-700 shadow-sm outline-none transition duration-300 ease-in-out dark:bg-neutral-800 dark:text-neutral-200 [&[data-te-offcanvas-show]]:transform-none"
tabIndex={-1}
>
<div className="flex justify-between items-center py-3 px-4 border-b dark:border-gray-700">
<h3 className="font-bold text-gray-800 dark:text-white">
Investimento
</h3>
<button
type="button"
className="investment-drawer-close inline-flex flex-shrink-0 justify-center items-center h-8 w-8 rounded-md text-gray-500 hover:text-gray-700 focus:outline-none focus:ring-2 focus:ring-gray-400 focus:ring-offset-2 focus:ring-offset-white text-sm dark:text-gray-500 dark:hover:text-gray-400 dark:focus:ring-gray-700 dark:focus:ring-offset-gray-800"
onClick={() => toggleShowInvestmentForm()}
>
<span className="sr-only">Cadastro de Investimento</span>
<svg
className="w-3.5 h-3.5"
width={8}
height={8}
viewBox="0 0 8 8"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M0.258206 1.00652C0.351976 0.912791 0.479126 0.860131 0.611706 0.860131C0.744296 0.860131 0.871447 0.912791 0.965207 1.00652L3.61171 3.65302L6.25822 1.00652C6.30432 0.958771 6.35952 0.920671 6.42052 0.894471C6.48152 0.868271 6.54712 0.854471 6.61352 0.853901C6.67992 0.853321 6.74572 0.865971 6.80722 0.891111C6.86862 0.916251 6.92442 0.953381 6.97142 1.00032C7.01832 1.04727 7.05552 1.1031 7.08062 1.16454C7.10572 1.22599 7.11842 1.29183 7.11782 1.35822C7.11722 1.42461 7.10342 1.49022 7.07722 1.55122C7.05102 1.61222 7.01292 1.6674 6.96522 1.71352L4.31871 4.36002L6.96522 7.00648C7.05632 7.10078 7.10672 7.22708 7.10552 7.35818C7.10442 7.48928 7.05182 7.61468 6.95912 7.70738C6.86642 7.80018 6.74102 7.85268 6.60992 7.85388C6.47882 7.85498 6.35252 7.80458 6.25822 7.71348L3.61171 5.06702L0.965207 7.71348C0.870907 7.80458 0.744606 7.85498 0.613506 7.85388C0.482406 7.85268 0.357007 7.80018 0.264297 7.70738C0.171597 7.61468 0.119017 7.48928 0.117877 7.35818C0.116737 7.22708 0.167126 7.10078 0.258206 7.00648L2.90471 4.36002L0.258206 1.71352C0.164476 1.61976 0.111816 1.4926 0.111816 1.36002C0.111816 1.22744 0.164476 1.10028 0.258206 1.00652Z"
fill="currentColor"
/>
</svg>
</button>
</div>
<div className="p-4 overflow-y-auto">
<form onSubmit={(event) => handleFormSubmit(event)}>
<input type="hidden" id="id" name="id" />
<div className="mb-3">
<label
htmlFor="name"
className="block text-sm font-medium mb-2 dark:text-white"
>
Nome
</label>
<input
type="text"
id="name"
name="name"
onChange={handleChange}
value={investmentFormData.name}
className="py-3 px-4 block w-full border border-gray-200 rounded-md text-sm focus:border-blue-500 focus:ring-blue-500 dark:bg-slate-900 dark:border-gray-700 dark:text-gray-400"
required=""
/>
</div>
<div className="mb-3">
<label
htmlFor="value"
className="block text-sm font-medium mb-2 dark:text-white"
>
Valor
</label>
<input
type="number"
id="value"
name="value"
onChange={handleChange}
value={investmentFormData.value}
className="py-3 px-4 block w-full border border-gray-200 rounded-md text-sm focus:border-blue-500 focus:ring-blue-500 dark:bg-slate-900 dark:border-gray-700 dark:text-gray-400"
required=""
/>
</div>
<div className="mb-3">
<label
htmlFor="origin"
className="block text-sm font-medium mb-2 dark:text-white"
>
Origem
</label>
<input
type="text"
id="origin"
name="origin"
onChange={handleChange}
value={investmentFormData.origin}
className="py-3 px-4 block w-full border border-gray-200 rounded-md text-sm focus:border-blue-500 focus:ring-blue-500 dark:bg-slate-900 dark:border-gray-700 dark:text-gray-400"
required=""
/>
</div>
<div className="mb-3">
<label
htmlFor="category"
className="block text-sm font-medium mb-2 dark:text-white"
>
Categoria
</label>
<input
type="text"
id="category"
name="category"
onChange={handleChange}
value={investmentFormData.category}
className="py-3 px-4 block w-full border border-gray-200 rounded-md text-sm focus:border-blue-500 focus:ring-blue-500 dark:bg-slate-900 dark:border-gray-700 dark:text-gray-400"
required=""
/>
</div>
<div className="mb-3">
<label
htmlFor="interest"
className="block text-sm font-medium mb-2 dark:text-white"
>
Taxa
</label>
<input
type="text"
id="interest"
name="interest"
onChange={handleChange}
value={investmentFormData.interest}
className="py-3 px-4 block w-full border border-gray-200 rounded-md text-sm focus:border-blue-500 focus:ring-blue-500 dark:bg-slate-900 dark:border-gray-700 dark:text-gray-400"
required=""
/>
</div>
<div className="mb-3">
<label
htmlFor="created_at"
className="block text-sm font-medium mb-2 dark:text-white"
>
Data
</label>
<input
type="date"
id="created_at"
name="created_at"
onChange={handleChange}
value={investmentFormData.created_at}
className="py-3 px-4 block w-full border border-gray-200 rounded-md text-sm focus:border-blue-500 focus:ring-blue-500 dark:bg-slate-900 dark:border-gray-700 dark:text-gray-400"
required=""
/>
</div>
<div>
<button
type="submit"
className="py-3 px-4 inline-flex w-full justify-center items-center gap-2 rounded-md border border-transparent font-semibold bg-gray-500 text-white hover:bg-gray-600 focus:outline-none focus:ring-2 focus:ring-gray-500 focus:ring-offset-2 transition-all text-sm dark:bg-gray-700 dark:hover:bg-gray-600 dark:focus:ring-offset-gray-800"
>
Enviar
</button>
</div>
</form>
</div>
</div>
</div>
)}
</>
);
}
Modal
/codes/react/hooks/invest-app/src/components/Modal.jsx
import { useInvestment } from '@/contexts/InvestmentContext';
export default function Modal() {
const {
isShowModal,
toggleShowModal,
handleDeleteInvestment,
investmentModalData,
} = useInvestment();
return (
<>
{isShowModal && (
<div>
<div
className="fixed h-full w-full right-0 top-0 bg-black/50 z-10"
onClick={() => toggleShowModal()}
></div>
<div className="fixed top-1/3 left-1/2 -translate-x-1/2 z-[60] overflow-x-hidden overflow-y-auto sm:max-w-lg sm:w-full sm:mx-auto flex flex-col bg-white border shadow-sm rounded-xl dark:bg-gray-800 dark:border-gray-700 dark:shadow-slate-700/[.7]">
<div className="flex justify-between items-center py-3 px-4 border-b dark:border-gray-700">
<h3 className="font-bold text-gray-800 dark:text-white">
Remover Investimento
</h3>
<button
type="button"
className="hs-dropdown-toggle inline-flex flex-shrink-0 justify-center items-center h-8 w-8 rounded-md text-gray-500 hover:text-gray-400 focus:outline-none focus:ring-2 focus:ring-gray-400 focus:ring-offset-2 focus:ring-offset-white transition-all text-sm dark:focus:ring-gray-700 dark:focus:ring-offset-gray-800"
onClick={toggleShowModal}
>
<span className="sr-only">Close</span>
<svg
className="w-3.5 h-3.5"
width={8}
height={8}
viewBox="0 0 8 8"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M0.258206 1.00652C0.351976 0.912791 0.479126 0.860131 0.611706 0.860131C0.744296 0.860131 0.871447 0.912791 0.965207 1.00652L3.61171 3.65302L6.25822 1.00652C6.30432 0.958771 6.35952 0.920671 6.42052 0.894471C6.48152 0.868271 6.54712 0.854471 6.61352 0.853901C6.67992 0.853321 6.74572 0.865971 6.80722 0.891111C6.86862 0.916251 6.92442 0.953381 6.97142 1.00032C7.01832 1.04727 7.05552 1.1031 7.08062 1.16454C7.10572 1.22599 7.11842 1.29183 7.11782 1.35822C7.11722 1.42461 7.10342 1.49022 7.07722 1.55122C7.05102 1.61222 7.01292 1.6674 6.96522 1.71352L4.31871 4.36002L6.96522 7.00648C7.05632 7.10078 7.10672 7.22708 7.10552 7.35818C7.10442 7.48928 7.05182 7.61468 6.95912 7.70738C6.86642 7.80018 6.74102 7.85268 6.60992 7.85388C6.47882 7.85498 6.35252 7.80458 6.25822 7.71348L3.61171 5.06702L0.965207 7.71348C0.870907 7.80458 0.744606 7.85498 0.613506 7.85388C0.482406 7.85268 0.357007 7.80018 0.264297 7.70738C0.171597 7.61468 0.119017 7.48928 0.117877 7.35818C0.116737 7.22708 0.167126 7.10078 0.258206 7.00648L2.90471 4.36002L0.258206 1.71352C0.164476 1.61976 0.111816 1.4926 0.111816 1.36002C0.111816 1.22744 0.164476 1.10028 0.258206 1.00652Z"
fill="currentColor"
/>
</svg>
</button>
</div>
<div className="p-4 overflow-y-auto">
<p className="mt-1 text-gray-800 dark:text-gray-400">
Deseja remover o investimento{' '}
<span className="investment-name font-semibold">
{' '}
{investmentModalData.name}
</span>
?
</p>
</div>
<div className="flex justify-end items-center gap-x-2 py-3 px-4 border-t dark:border-gray-700">
<button
type="button"
className="hs-dropdown-toggle py-3 px-4 inline-flex justify-center items-center gap-2 rounded-md border font-medium bg-white text-gray-700 shadow-sm align-middle hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-white focus:ring-blue-600 transition-all text-sm dark:bg-slate-900 dark:hover:bg-slate-800 dark:border-gray-700 dark:text-gray-400 dark:hover:text-white dark:focus:ring-offset-gray-800"
onClick={toggleShowModal}
>
Cancelar
</button>
<button
type="button"
className="remove-investment-btn py-3 px-4 inline-flex justify-center items-center gap-2 rounded-md border border-transparent font-semibold bg-blue-500 text-white hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 transition-all text-sm dark:focus:ring-offset-gray-800"
onClick={handleDeleteInvestment}
>
Confirmar
</button>
</div>
</div>
</div>
)}
</>
);
}
/codes/react/hooks/invest-app/src/components/Modal.jsx
import { useInvestment } from '@/contexts/InvestmentContext';
export default function Modal() {
const {
isShowModal,
toggleShowModal,
handleDeleteInvestment,
investmentModalData,
} = useInvestment();
return (
<>
{isShowModal && (
<div>
<div
className="fixed h-full w-full right-0 top-0 bg-black/50 z-10"
onClick={() => toggleShowModal()}
></div>
<div className="fixed top-1/3 left-1/2 -translate-x-1/2 z-[60] overflow-x-hidden overflow-y-auto sm:max-w-lg sm:w-full sm:mx-auto flex flex-col bg-white border shadow-sm rounded-xl dark:bg-gray-800 dark:border-gray-700 dark:shadow-slate-700/[.7]">
<div className="flex justify-between items-center py-3 px-4 border-b dark:border-gray-700">
<h3 className="font-bold text-gray-800 dark:text-white">
Remover Investimento
</h3>
<button
type="button"
className="hs-dropdown-toggle inline-flex flex-shrink-0 justify-center items-center h-8 w-8 rounded-md text-gray-500 hover:text-gray-400 focus:outline-none focus:ring-2 focus:ring-gray-400 focus:ring-offset-2 focus:ring-offset-white transition-all text-sm dark:focus:ring-gray-700 dark:focus:ring-offset-gray-800"
onClick={toggleShowModal}
>
<span className="sr-only">Close</span>
<svg
className="w-3.5 h-3.5"
width={8}
height={8}
viewBox="0 0 8 8"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M0.258206 1.00652C0.351976 0.912791 0.479126 0.860131 0.611706 0.860131C0.744296 0.860131 0.871447 0.912791 0.965207 1.00652L3.61171 3.65302L6.25822 1.00652C6.30432 0.958771 6.35952 0.920671 6.42052 0.894471C6.48152 0.868271 6.54712 0.854471 6.61352 0.853901C6.67992 0.853321 6.74572 0.865971 6.80722 0.891111C6.86862 0.916251 6.92442 0.953381 6.97142 1.00032C7.01832 1.04727 7.05552 1.1031 7.08062 1.16454C7.10572 1.22599 7.11842 1.29183 7.11782 1.35822C7.11722 1.42461 7.10342 1.49022 7.07722 1.55122C7.05102 1.61222 7.01292 1.6674 6.96522 1.71352L4.31871 4.36002L6.96522 7.00648C7.05632 7.10078 7.10672 7.22708 7.10552 7.35818C7.10442 7.48928 7.05182 7.61468 6.95912 7.70738C6.86642 7.80018 6.74102 7.85268 6.60992 7.85388C6.47882 7.85498 6.35252 7.80458 6.25822 7.71348L3.61171 5.06702L0.965207 7.71348C0.870907 7.80458 0.744606 7.85498 0.613506 7.85388C0.482406 7.85268 0.357007 7.80018 0.264297 7.70738C0.171597 7.61468 0.119017 7.48928 0.117877 7.35818C0.116737 7.22708 0.167126 7.10078 0.258206 7.00648L2.90471 4.36002L0.258206 1.71352C0.164476 1.61976 0.111816 1.4926 0.111816 1.36002C0.111816 1.22744 0.164476 1.10028 0.258206 1.00652Z"
fill="currentColor"
/>
</svg>
</button>
</div>
<div className="p-4 overflow-y-auto">
<p className="mt-1 text-gray-800 dark:text-gray-400">
Deseja remover o investimento{' '}
<span className="investment-name font-semibold">
{' '}
{investmentModalData.name}
</span>
?
</p>
</div>
<div className="flex justify-end items-center gap-x-2 py-3 px-4 border-t dark:border-gray-700">
<button
type="button"
className="hs-dropdown-toggle py-3 px-4 inline-flex justify-center items-center gap-2 rounded-md border font-medium bg-white text-gray-700 shadow-sm align-middle hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-white focus:ring-blue-600 transition-all text-sm dark:bg-slate-900 dark:hover:bg-slate-800 dark:border-gray-700 dark:text-gray-400 dark:hover:text-white dark:focus:ring-offset-gray-800"
onClick={toggleShowModal}
>
Cancelar
</button>
<button
type="button"
className="remove-investment-btn py-3 px-4 inline-flex justify-center items-center gap-2 rounded-md border border-transparent font-semibold bg-blue-500 text-white hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 transition-all text-sm dark:focus:ring-offset-gray-800"
onClick={handleDeleteInvestment}
>
Confirmar
</button>
</div>
</div>
</div>
)}
</>
);
}