From 68138ad368dd0962da2179b260691dc28a13d169 Mon Sep 17 00:00:00 2001 From: kalambriento Date: Tue, 25 Nov 2025 16:41:10 +0100 Subject: [PATCH 1/3] implemantacion estructura --- .learn/config.json | 9 +++++ package-lock.json | 13 +++++++ package.json | 5 +-- src/components/ScrollToTop.jsx | 26 +++++++------- src/main.jsx | 64 ++++++++++++++++++++++++---------- src/pages/ContactCreate.jsx | 32 +++++++++++++++++ src/pages/ContactEdit.jsx | 7 ++++ src/pages/ContactList.jsx | 20 +++++++++++ src/routes.jsx | 33 +++++++++++------- 9 files changed, 163 insertions(+), 46 deletions(-) create mode 100644 .learn/config.json create mode 100644 src/pages/ContactCreate.jsx create mode 100644 src/pages/ContactEdit.jsx create mode 100644 src/pages/ContactList.jsx diff --git a/.learn/config.json b/.learn/config.json new file mode 100644 index 000000000..5a3c721fd --- /dev/null +++ b/.learn/config.json @@ -0,0 +1,9 @@ +{ + "config": { + "editor": { + "agent": "vscode" + }, + "autoPlay": true + }, + "currentExercise": null +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 40ae560d5..beed9fd14 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "prop-types": "^15.8.1", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-icons": "^5.5.0", "react-router-dom": "^6.18.0" }, "devDependencies": { @@ -22,6 +23,9 @@ "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-refresh": "^0.4.3", "vite": "^4.4.8" + }, + "engines": { + "node": ">=20.0.0" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -3281,6 +3285,15 @@ "react": "^18.2.0" } }, + "node_modules/react-icons": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.5.0.tgz", + "integrity": "sha512-MEFcXdkP3dLo8uumGI5xN3lDFNsRtrjbOEKDLD7yv76v4wpnEq2Lt2qeHaQOr34I/wPN3s3+N08WkQ+CW37Xiw==", + "license": "MIT", + "peerDependencies": { + "react": "*" + } + }, "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", diff --git a/package.json b/package.json index 7d59d13c8..2567d9f0f 100755 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "prop-types": "^15.8.1", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-icons": "^5.5.0", "react-router-dom": "^6.18.0" }, "devDependencies": { @@ -27,6 +28,6 @@ "vite": "^4.4.8" }, "engines": { - "node": ">=20.0.0" - } + "node": ">=20.0.0" + } } diff --git a/src/components/ScrollToTop.jsx b/src/components/ScrollToTop.jsx index fe79dc5be..fd27b1ab6 100644 --- a/src/components/ScrollToTop.jsx +++ b/src/components/ScrollToTop.jsx @@ -2,25 +2,25 @@ import { useEffect, useRef } from "react"; import PropTypes from "prop-types"; // This component allows the scroll to go to the beginning when changing the view, -// otherwise it would remain in the position of the previous view. -// Investigate more about this React behavior :D +// otherwise it would remain in the position of the previous view. +// Investigate more about this React behavior :D const ScrollToTop = ({ location, children }) => { - const prevLocation = useRef(location); + const prevLocation = useRef(location); - useEffect(() => { - if (location !== prevLocation.current) { - window.scrollTo(0, 0); - } - prevLocation.current = location; - }, [location]); + useEffect(() => { + if (location !== prevLocation.current) { + window.scrollTo(0, 0); + } + prevLocation.current = location; + }, [location]); - return children; + return children; }; export default ScrollToTop; ScrollToTop.propTypes = { - location: PropTypes.object, - children: PropTypes.any -}; \ No newline at end of file + location: PropTypes.object, + children: PropTypes.any, +}; diff --git a/src/main.jsx b/src/main.jsx index 3a122d76a..b83e66afa 100644 --- a/src/main.jsx +++ b/src/main.jsx @@ -1,22 +1,50 @@ -import React from 'react' -import ReactDOM from 'react-dom/client' -import './index.css' // Global styles for your application -import { RouterProvider } from "react-router-dom"; // Import RouterProvider to use the router -import { router } from "./routes"; // Import the router configuration -import { StoreProvider } from './hooks/useGlobalReducer'; // Import the StoreProvider for global state management +import React, { createContext, useReducer } from "react"; +import ReactDOM from "react-dom/client"; +import "./index.css"; // Global styles for your application +import { RouterProvider } from "react-router-dom"; // Import RouterProvider to use the router +import { router } from "./routes"; // Import the router configuration + +export const Context = createContext(); + +const reducter = (state, action) => { + if (action.type === "CREATE_CONTACT") { + const updatedContactList = [...state.contactList, action.value]; + return { ...store, constacList: updatedContactList }; + } + if (action.type === "EDIT_CONTACT") { + store.constacList.map((contact, index) => { + if (index === action.position) { + return action.value; + } + return contact; + }); + return { ...store, contacList: updatedContactList }; + } + + if (action.type === "REMOVE_CONTACT") { + const updatedContactList = store.contacList.filter((_, index) => { + return action.position !== index; + }); + return { ...store, constacList: updatedContactList }; + } + return store; +}; const Main = () => { - return ( - - {/* Provide global state to all components */} - - {/* Set up routing for the application */} - - - - - ); -} + const [store, dispatch] = useReducer(reducter, { + constacList: [{ mail: "raulreyes2@gmail.com" }], + }); + + return ( + + {/* Provide global state to all components */} + + {/* Set up routing for the application */} + + + + ); +}; // Render the Main component into the root DOM element. -ReactDOM.createRoot(document.getElementById('root')).render(
) +ReactDOM.createRoot(document.getElementById("root")).render(
); diff --git a/src/pages/ContactCreate.jsx b/src/pages/ContactCreate.jsx new file mode 100644 index 000000000..834711a7d --- /dev/null +++ b/src/pages/ContactCreate.jsx @@ -0,0 +1,32 @@ +import { useContext, useState } from "react"; +import { Link } from "react-router-dom"; +import { useNavigate } from "react-router-dom"; +import { Context } from "../main.jsx"; + +export const ContactCreate = () => { + const [mail, setMail] = useState(""); + const { store, dispatch } = useContext(Context); + const navigate = useNavigate(); + + const saveContact = () => { + const newContact = { mail }; + dispatch({ type: "CREATE_CONTACT", value: newContact }); + navigate("/"); + }; + + return ( + <> +

Crear un contacto

+ + + + setMail(e.target.value)} + /> + + + ); +}; diff --git a/src/pages/ContactEdit.jsx b/src/pages/ContactEdit.jsx new file mode 100644 index 000000000..78f04b15f --- /dev/null +++ b/src/pages/ContactEdit.jsx @@ -0,0 +1,7 @@ +export const ContactEdit = () => { + return ( + <> +

Contact Edit

+ + ); +}; diff --git a/src/pages/ContactList.jsx b/src/pages/ContactList.jsx new file mode 100644 index 000000000..9a11055a6 --- /dev/null +++ b/src/pages/ContactList.jsx @@ -0,0 +1,20 @@ +import { useContext } from "react"; +import { Context } from "../main.jsx"; +import { Link } from "react-router-dom"; + +export const ContactList = () => { + const { store, dispatch } = useContext(Context); + return ( + <> +

Contact List

+ + + +
    + {store.constacList.map((contact) => ( +
  • {contact.mail}
  • + ))} +
+ + ); +}; diff --git a/src/routes.jsx b/src/routes.jsx index 0557df614..56f5f3bb4 100644 --- a/src/routes.jsx +++ b/src/routes.jsx @@ -1,30 +1,37 @@ // Import necessary components and functions from react-router-dom. import { - createBrowserRouter, - createRoutesFromElements, - Route, + createBrowserRouter, + createRoutesFromElements, + Route, } from "react-router-dom"; import { Layout } from "./pages/Layout"; import { Home } from "./pages/Home"; import { Single } from "./pages/Single"; import { Demo } from "./pages/Demo"; +import { ContactList } from "./pages/ContactList"; +import { ContactCreate } from "./pages/ContactCreate"; +import { ContactEdit } from "./pages/ContactEdit"; export const router = createBrowserRouter( - createRoutesFromElements( + createRoutesFromElements( // CreateRoutesFromElements function allows you to build route elements declaratively. // Create your routes here, if you want to keep the Navbar and Footer in all views, add your new routes inside the containing Route. // Root, on the contrary, create a sister Route, if you have doubts, try it! // Note: keep in mind that errorElement will be the default page when you don't get a route, customize that page to make your project more attractive. // Note: The child paths of the Layout element replace the Outlet component with the elements contained in the "element" attribute of these child paths. - // Root Route: All navigation will start from here. - } errorElement={

Not found!

} > + // Root Route: All navigation will start from here. + //} errorElement={

Not found!

} > - {/* Nested Routes: Defines sub-routes within the BaseHome component. */} - } /> - } /> {/* Dynamic route for single items */} - } /> - - ) -); \ No newline at end of file + // } /> + // } /> + // } /> + // + <> + } path="/" /> + } path="/create" /> + } path="/edit" /> + + ) +); From 002809e9ed64e20f27233ae4e8182294e82024de Mon Sep 17 00:00:00 2001 From: kalambriento Date: Wed, 26 Nov 2025 10:05:03 +0100 Subject: [PATCH 2/3] por fin --- src/ContactForm.css | 164 ++++++++++++++++++++++++++++++++++++ src/main.jsx | 23 +++-- src/pages/ContactCreate.jsx | 92 ++++++++++++++++---- src/pages/ContactEdit.jsx | 107 ++++++++++++++++++++++- src/pages/ContactList.jsx | 61 +++++++++++--- src/routes.jsx | 2 +- 6 files changed, 414 insertions(+), 35 deletions(-) create mode 100644 src/ContactForm.css diff --git a/src/ContactForm.css b/src/ContactForm.css new file mode 100644 index 000000000..303ca84df --- /dev/null +++ b/src/ContactForm.css @@ -0,0 +1,164 @@ + +body { + background-color: #f8f9fa; +} + +.contact-list-container { + max-width: 900px; + margin: 20px auto; + padding: 20px; + background-color: white; + border-radius: 8px; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); +} + +.contact-list-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 20px; +} + +.form-container { + max-width: 600px; + margin: 40px auto; + padding: 20px; + background-color: #fff; +} + +.form-title { + text-align: center; + font-size: 24px; + font-weight: 500; + margin-bottom: 30px; + color: #333; +} + +.form-group { + margin-bottom: 20px; +} + +.label-text { + display: none; +} + +.form-input { + width: 100%; + padding: 12px 15px; + border: 1px solid #ccc; + border-radius: 4px; + box-sizing: border-box; + font-size: 16px; + color: #6c757d; +} + +.form-input:focus { + border-color: #007bff; + outline: none; + box-shadow: 0 0 5px rgba(0, 123, 255, 0.2); +} + +.save-button { + width: 100%; + padding: 15px; + margin-top: 10px; + background-color: #0f8878; + color: white; + border: none; + border-radius: 4px; + font-size: 18px; + text-transform: uppercase; + cursor: pointer; + font-weight: 600; +} + +.save-button:hover { + background-color: #0056b3; +} + +.back-link-container { + text-align: left; + margin-top: 15px; +} + +.back-link { + color: #007bff; + font-size: 14px; + text-decoration: underline; + cursor: pointer; + background: none; + border: none; + padding: 0; + margin: 0; +} + +/* Contenedor General */ +.contact-list-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 20px; +} + +.create-contact-btn { + padding: 10px 15px; + background-color: #007bff; /* Color primario */ + color: white; + border: none; + border-radius: 5px; + cursor: pointer; +} + +.contact-ul { + list-style: none; /* Quita los puntos de la lista */ + padding: 0; +} + +/* Tarjeta de Contacto (Filas) */ +.contact-card { + display: flex; + justify-content: space-between; /* Alinea los detalles a la izquierda y las acciones a la derecha */ + align-items: center; + padding: 15px 20px; + margin-bottom: 15px; + border: 1px solid #ddd; + border-radius: 8px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); + background-color: white; +} + +.contact-details { + display: flex; + gap: 20px; + align-items: center; +} + +.contact-info p { + margin: 5px 0; /* Espaciado entre las l铆neas de informaci贸n */ + font-size: 0.95em; +} + +/* Acciones (Botones) */ +.contact-actions { + display: flex; + gap: 10px; +} + +.edit-btn, +.delete-btn { + padding: 8px 12px; + border: 1px solid transparent; + border-radius: 5px; + cursor: pointer; + font-weight: bold; +} + +.edit-btn { + background-color: #ffc107; /* Amarillo */ + color: #333; +} + +.delete-btn { + background-color: #dc3545; /* Rojo */ + color: white; +} \ No newline at end of file diff --git a/src/main.jsx b/src/main.jsx index b83e66afa..2f311e47b 100644 --- a/src/main.jsx +++ b/src/main.jsx @@ -6,33 +6,40 @@ import { router } from "./routes"; // Import the router configuration export const Context = createContext(); -const reducter = (state, action) => { +const reducter = (store, action) => { if (action.type === "CREATE_CONTACT") { - const updatedContactList = [...state.contactList, action.value]; - return { ...store, constacList: updatedContactList }; + const updatedContactList = [...store.contactList, action.value]; + return { ...store, contactList: updatedContactList }; } if (action.type === "EDIT_CONTACT") { - store.constacList.map((contact, index) => { + const updatedContactList = store.contactList.map((contact, index) => { if (index === action.position) { return action.value; } return contact; }); - return { ...store, contacList: updatedContactList }; + return { ...store, contactList: updatedContactList }; } if (action.type === "REMOVE_CONTACT") { - const updatedContactList = store.contacList.filter((_, index) => { + const updatedContactList = store.contactList.filter((_, index) => { return action.position !== index; }); - return { ...store, constacList: updatedContactList }; + return { ...store, contactList: updatedContactList }; } return store; }; const Main = () => { const [store, dispatch] = useReducer(reducter, { - constacList: [{ mail: "raulreyes2@gmail.com" }], + contactList: [ + { + name: "", + email: "ejemplo@mail.com", + Phone: "", + Adress: "", + }, + ], }); return ( diff --git a/src/pages/ContactCreate.jsx b/src/pages/ContactCreate.jsx index 834711a7d..781f8f1c9 100644 --- a/src/pages/ContactCreate.jsx +++ b/src/pages/ContactCreate.jsx @@ -3,30 +3,92 @@ import { Link } from "react-router-dom"; import { useNavigate } from "react-router-dom"; import { Context } from "../main.jsx"; +import "../ContactForm.css"; + export const ContactCreate = () => { - const [mail, setMail] = useState(""); + const [email, setEmail] = useState(""); + const [name, setName] = useState(""); + const [phone, setPhone] = useState(""); + const [address, setAddress] = useState(""); const { store, dispatch } = useContext(Context); const navigate = useNavigate(); const saveContact = () => { - const newContact = { mail }; + const newContact = { name, email, phone, address }; dispatch({ type: "CREATE_CONTACT", value: newContact }); navigate("/"); }; return ( - <> -

Crear un contacto

- - - - setMail(e.target.value)} - /> - - +
+

Agregar nuevo contacto

+
e.preventDefault()}> +
+ + setName(e.target.value)} + /> +
+ +
+ + setEmail(e.target.value)} + /> +
+ +
+ + setPhone(e.target.value)} + /> +
+ +
+ + setAddress(e.target.value)} + /> +
+ + + +
+ + Volver al Inicio. + +
+
+
); }; diff --git a/src/pages/ContactEdit.jsx b/src/pages/ContactEdit.jsx index 78f04b15f..a8aaf9571 100644 --- a/src/pages/ContactEdit.jsx +++ b/src/pages/ContactEdit.jsx @@ -1,7 +1,112 @@ +import { useContext, useEffect, useState } from "react"; +import { Link, useNavigate, useParams } from "react-router-dom"; +import { Context } from "../main.jsx"; + export const ContactEdit = () => { + const [email, setEmail] = useState(""); + const [name, setName] = useState(""); + const [phone, setPhone] = useState(""); + const [address, setAddress] = useState(""); + + const { store, dispatch } = useContext(Context); + const navigate = useNavigate(); + const { position } = useParams(); + + useEffect(() => { + if (store.contactList && store.contactList[position]) { + const contact = store.contactList[position]; + setEmail(contact.email); + setName(contact.name); + setPhone(contact.phone); + setAddress(contact.address); + } else { + navigate("/"); + } + }, [position, store.contactList, navigate]); + + const handleContacEdit = () => { + const updatedContact = { name, email, phone, address }; + dispatch({ + type: "EDIT_CONTACT", + value: updatedContact, + position: parseInt(position), + }); + navigate("/"); + }; + return ( <> -

Contact Edit

+
+

Editar Contacto

+
e.preventDefault()}> +
+ setName(e.target.value)} + /> +
+ +
+ + setEmail(e.target.value)} + /> +
+ +
+ + setPhone(e.target.value)} + /> +
+ +
+ + setAddress(e.target.value)} + /> +
+ + + +
+ + Volver al Inicio. + +
+
+
); }; diff --git a/src/pages/ContactList.jsx b/src/pages/ContactList.jsx index 9a11055a6..29fbfd342 100644 --- a/src/pages/ContactList.jsx +++ b/src/pages/ContactList.jsx @@ -1,20 +1,61 @@ import { useContext } from "react"; import { Context } from "../main.jsx"; -import { Link } from "react-router-dom"; +import { Link, useNavigate } from "react-router-dom"; export const ContactList = () => { const { store, dispatch } = useContext(Context); + const navigate = useNavigate(); + + const removeContact = (position) => { + dispatch({ type: "REMOVE_CONTACT", position }); + }; + + const contactEdit = (position) => { + navigate(`/edit/${position}`); + }; + return ( <> -

Contact List

- - - -
    - {store.constacList.map((contact) => ( -
  • {contact.mail}
  • - ))} -
+
+
+

Lista de Contactos

+ + + +
+ +
+
    + {store.contactList.map((contact, index) => ( +
  • +
    +
    +

    + Nombre: {contact.name} +

    +

    馃摟 Email: {contact.email}

    +

    馃摓 Tel茅fono: {contact.phone}

    +

    + 馃彔 Direcci贸n: {contact.address} +

    +
    +
    + +
    + + +
    +
  • + ))} +
+
); }; diff --git a/src/routes.jsx b/src/routes.jsx index 56f5f3bb4..1019241ed 100644 --- a/src/routes.jsx +++ b/src/routes.jsx @@ -31,7 +31,7 @@ export const router = createBrowserRouter( <> } path="/" /> } path="/create" /> - } path="/edit" /> + } path="/edit/:position" /> ) ); From 109cf3b4a870c30dca34948afd8f78102b03823a Mon Sep 17 00:00:00 2001 From: kalambriento Date: Wed, 26 Nov 2025 10:10:00 +0100 Subject: [PATCH 3/3] ahora si por fin --- src/pages/ContactCreate.jsx | 6 +++--- src/pages/ContactEdit.jsx | 7 +++++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/pages/ContactCreate.jsx b/src/pages/ContactCreate.jsx index 781f8f1c9..59684028a 100644 --- a/src/pages/ContactCreate.jsx +++ b/src/pages/ContactCreate.jsx @@ -25,7 +25,7 @@ export const ContactCreate = () => {
e.preventDefault()}>
{
{
{

Editar Contacto

e.preventDefault()}>
+ {
{