diff --git a/.github/ISSUE_TEMPLATE/bug.md b/.github/ISSUE_TEMPLATE/bug.md new file mode 100644 index 0000000..7038a98 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug.md @@ -0,0 +1,18 @@ +--- +name: Bug +about: Reportar un error +labels: bug +--- + +**Qué pasa** +Descripción breve. + +**Pasos para reproducir** +1. +2. + +**Qué esperabas** + +**Entorno** +- Navegador: +- Sistema operativo: diff --git a/.github/ISSUE_TEMPLATE/feature.md b/.github/ISSUE_TEMPLATE/feature.md new file mode 100644 index 0000000..3c6af3f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature.md @@ -0,0 +1,14 @@ +--- +name: Propuesta de mejora +about: Sugerir una funcionalidad o mejora +labels: enhancement +--- + +**Problema o necesidad** +¿Qué te falta? ¿Quién la usaría? + +**Propuesta** +Cómo lo resolverías. Mantenlo simple: si la propuesta requiere muchas piezas +nuevas, conviene discutirla antes en Discussions. + +**Alternativas que has considerado** diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..9c44854 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,13 @@ +## Qué cambia + + + +## Por qué + + + +## Notas para el revisor + +- [ ] La app arranca con `npm run dev`. +- [ ] No he añadido dependencias innecesarias. +- [ ] He seguido la estructura existente. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..dc9ad98 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +node_modules +dist +.DS_Store +*.log +.vscode +.idea +.env +.env.local diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..244be86 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) [AÑO] [ORGANIZACIÓN] + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 4012910..5208a91 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,38 @@ -# LocalESP -Registro colaborativo de negocios españoles +# Business Map + +Registro colaborativo de negocios, en un mapa abierto. + +Prototipo open source mantenido por **[ORGANIZACIÓN]**. Cualquiera puede ver +los negocios registrados y proponer nuevos; las propuestas son revisadas +antes de aparecer públicamente. + +## Demo local + +```bash +npm install +npm run dev +``` + +Abre la URL que indique Vite (normalmente `http://localhost:5173`). + +## Stack + +- React 18 + Vite +- Leaflet + OpenStreetMap (mapa) +- CSS plano + +Sin backend en el prototipo: los datos vienen de un mock en memoria. Ver +`docs/02-implementation-alternatives.md` para la decisión de backend. + +## Documentación + +- [`docs/01-use-cases.md`](docs/01-use-cases.md) – qué hace y qué no. +- [`docs/02-implementation-alternatives.md`](docs/02-implementation-alternatives.md) – decisiones técnicas. +- [`docs/03-decisions.md`](docs/03-decisions.md) – ADRs. +- [`docs/04-governance.md`](docs/04-governance.md) – cómo se gobierna el proyecto. +- [`docs/05-contributing.md`](docs/05-contributing.md) – cómo contribuir. +- [`docs/06-github-setup.md`](docs/06-github-setup.md) – configuración inicial del repo. + +## Licencia + +MIT. Ver [LICENSE](LICENSE). diff --git a/docs/01-use-cases.md b/docs/01-use-cases.md new file mode 100644 index 0000000..b1233cb --- /dev/null +++ b/docs/01-use-cases.md @@ -0,0 +1,51 @@ +# Casos de uso + +Este documento describe los casos de uso del prototipo. El objetivo es mantener +el alcance acotado para minimizar el coste de mantenimiento. + +## Actores + +- **Visitante**: cualquier persona que accede a la aplicación. No requiere + autenticación. Puede consultar el mapa y contribuir con nuevas entradas. +- **Revisor**: persona designada por la organización que aprueba o rechaza las + contribuciones. Su flujo está fuera del frontend del prototipo (ver + alternativas de implementación). + +## Casos de uso del prototipo + +### CU-01: Explorar negocios en el mapa +- El visitante abre la aplicación. +- Ve un mapa centrado en una ubicación por defecto con marcadores de los + negocios aprobados. +- Puede hacer zoom y desplazarse libremente. + +### CU-02: Ver detalles de un negocio +- El visitante selecciona un marcador en el mapa. +- La barra lateral muestra los detalles del negocio: nombre, categoría, + dirección, descripción corta y enlace/contacto si existe. +- Al seleccionar otro marcador, la barra lateral se actualiza. + +### CU-03: Contribuir con un nuevo negocio +- El visitante abre el formulario de contribución desde la interfaz principal. +- Rellena los campos mínimos: nombre, categoría, dirección, descripción y + contacto opcional. La ubicación se obtiene geocodificando la dirección o + haciendo clic en el mapa. +- Envía el formulario. La entrada queda en estado "pendiente de revisión" y no + aparece en el mapa hasta ser aprobada. +- El visitante recibe confirmación visual del envío. + +## Fuera de alcance del prototipo + +Decisiones tomadas para mantener el proyecto simple (ver +`02-implementation-alternatives.md` para el razonamiento): + +- Autenticación de usuarios. +- Panel de moderación dentro de la aplicación. La revisión se hace en el + backend (por ejemplo, en el dashboard del proveedor de datos elegido). +- Edición o eliminación de negocios por el visitante. +- Búsqueda avanzada, filtros complejos o categorías jerárquicas. +- Comentarios, valoraciones, fotos múltiples o multimedia. +- Internacionalización. El prototipo usa un único idioma. + +Estos casos pueden añadirse después si la comunidad lo pide, pero no forman +parte del prototipo. diff --git a/docs/02-implementation-alternatives.md b/docs/02-implementation-alternatives.md new file mode 100644 index 0000000..e4ff051 --- /dev/null +++ b/docs/02-implementation-alternatives.md @@ -0,0 +1,85 @@ +# Alternativas de implementación + +Este documento registra las decisiones técnicas tomadas y por qué. El criterio +guía es **mínimo coste de mantenimiento** para una organización sin ánimo de +lucro, siguiendo KISS y evitando YAGNI. + +## 1. Framework frontend + +| Opción | Pros | Contras | +|---|---|---| +| **React + Vite** (elegido) | Ecosistema enorme, fácil encontrar contribuidores, build rápido. | Requiere build step. | +| Next.js | SSR, rutas, imágenes. | Demasiado para una SPA de una página; aumenta complejidad y coste de hosting. | +| Vanilla JS | Sin dependencias. | Más código manual, menos contribuidores familiarizados. | + +**Decisión**: React + Vite. Es el stack más común y los contribuidores +potenciales lo conocen. Vite mantiene la configuración mínima. + +## 2. Mapa + +| Opción | Pros | Contras | +|---|---|---| +| **Leaflet + OpenStreetMap** (elegido) | Open source, sin claves API, sin coste, tiles gratuitos. | Menos pulido que Mapbox. | +| Mapbox GL | Más bonito, mejor rendimiento. | Requiere API key y tiene cuotas de pago. | +| Google Maps | Familiar. | Requiere facturación activada, cuotas estrictas, no encaja con espíritu open source. | + +**Decisión**: Leaflet con tiles de OpenStreetMap. Cero coste, sin claves, y la +librería `react-leaflet` lo hace trivial de integrar. + +## 3. Backend y almacenamiento de datos + +| Opción | Pros | Contras | +|---|---|---| +| **Backend-as-a-Service** (Supabase o similar) elegido | Sin servidor que mantener, base de datos gestionada, panel de moderación incluido en su dashboard. Plan gratuito generoso. | Dependencia de un proveedor. | +| Backend propio (Node + Postgres) | Control total. | Hay que mantenerlo, pagarlo, securizarlo. Alto coste para una ONG. | +| Archivo JSON en el repositorio | Sin backend, máxima simplicidad. | Las contribuciones tendrían que llegar como Pull Requests, fricción alta para no-técnicos. | + +**Decisión**: BaaS tipo Supabase. Para el prototipo, la capa de datos se +abstrae en `src/services/businessRepository.js`, así que cambiar de proveedor +después es localizado. El prototipo entregado usa un **mock en memoria** para +no acoplar a un proveedor concreto antes de que la organización lo decida. + +## 4. Moderación + +| Opción | Pros | Contras | +|---|---|---| +| **Aprobación desde el dashboard del BaaS** (elegido) | Cero código extra. El revisor cambia un campo `status` de `pending` a `approved`. | Requiere acceso al dashboard. | +| Panel de moderación dentro de la app | Más cómodo. | Requiere autenticación, roles, vistas extra: explosión de complejidad. | + +**Decisión**: La moderación vive fuera del frontend público. El modelo de +datos incluye un campo `status` (`pending` / `approved` / `rejected`). El +frontend solo lee los `approved`. + +## 5. Estilos + +| Opción | Pros | Contras | +|---|---|---| +| **CSS plano con variables** (elegido) | Cero dependencias, fácil de leer, los contribuidores con HTML/CSS básico pueden colaborar. | Sin utilidades. | +| Tailwind | Productivo. | Cadena de build extra, curva de aprendizaje. | +| CSS-in-JS | Componentes encapsulados. | Runtime extra, peor accesibilidad para nuevos contribuidores. | + +**Decisión**: CSS plano. La app es de una página; no se justifica más. + +## 6. Gestión de estado + +**Decisión**: `useState` y `useEffect` de React. No hay Redux, Zustand ni +Context global. La app es pequeña; añadir una librería de estado sería YAGNI. + +## 7. Geocodificación de direcciones del formulario + +**Decisión para el prototipo**: el usuario hace clic en el mapa para fijar la +ubicación, o introduce lat/lng. Geocodificación automática vía Nominatim +(OpenStreetMap) queda como mejora futura con bajo coste de añadir. + +## 8. Tests + +**Decisión**: el prototipo no incluye suite de tests para no inflarlo, pero la +estructura (servicios separados de componentes, funciones puras donde posible) +facilita añadirlos. Cuando se incorporen, **Vitest** es la elección natural por +integrarse con Vite sin configuración. + +## 9. Despliegue + +**Decisión recomendada**: **GitHub Pages** o **Cloudflare Pages** (gratuitos +para open source). Una SPA estática construida con `vite build` se despliega +sin coste y sin servidor. Ver `docs/06-github-setup.md`. diff --git a/docs/03-decisions.md b/docs/03-decisions.md new file mode 100644 index 0000000..c6292b5 --- /dev/null +++ b/docs/03-decisions.md @@ -0,0 +1,49 @@ +# Registro de decisiones + +Lista corta y cronológica de decisiones que merecen ser revisadas más +adelante. Formato ADR ligero. + +## D-001: SPA de una sola página +**Contexto**: el uso principal es ver un mapa y unos detalles. +**Decisión**: una sola página, sin router. El formulario es un panel/modal en +la misma vista. +**Consecuencia**: si en el futuro hay vistas adicionales (estadísticas, perfil +del negocio en URL propia), habrá que introducir React Router. Reevaluar +cuando aparezca el segundo caso de uso navegable. + +## D-002: Capa de servicios +**Decisión**: todo acceso a datos pasa por `src/services/businessRepository.js`. +**Razón**: cumplir Dependency Inversion (SOLID). Los componentes no saben si +los datos vienen de un mock, de Supabase o de un JSON. +**Consecuencia**: cambiar de proveedor es modificar un único archivo. + +## D-003: Modelo de datos mínimo +Campos del negocio: +- `id` (string) +- `name` (string) +- `category` (string) +- `address` (string) +- `description` (string) +- `contact` (string, opcional) +- `lat` (number) +- `lng` (number) +- `status` (`pending` | `approved` | `rejected`) +- `createdAt` (ISO string) + +Sin slugs, sin fotos, sin tags. Reevaluar tras feedback de la comunidad. + +## D-004: Sin autenticación en el prototipo +**Razón**: el visitante no necesita cuenta. El moderador opera en el panel del +backend. +**Reevaluar si**: aparecen casos de uso que requieran identidad (favoritos, +edición por el dueño del negocio). + +## D-005: Idioma +El prototipo está en español, sin i18n. Si la organización quiere expandirse, +añadir `react-i18next` cuando exista una segunda lengua real, no antes. + +## D-006: Mock de datos en el prototipo +El repositorio entregado usa un mock en memoria con varios negocios de +ejemplo. La organización debe decidir el backend antes de pasar a producción. +La interfaz del repositorio (`getApproved`, `submit`) está pensada para +mapearse 1:1 a Supabase u otro BaaS. diff --git a/docs/04-governance.md b/docs/04-governance.md new file mode 100644 index 0000000..8456c00 --- /dev/null +++ b/docs/04-governance.md @@ -0,0 +1,40 @@ +# Gobernanza + +Este proyecto es mantenido por **[NOMBRE DE LA ORGANIZACIÓN]**, una entidad +sin ánimo de lucro. Estas pautas son deliberadamente breves; queremos invertir +el esfuerzo en código y comunidad, no en burocracia. + +## Roles + +- **Mantenedores**: personas con permisos de merge en `main`. Son nombradas + por la organización. Responsables de revisar PRs, etiquetar issues y publicar + releases. +- **Contribuidores**: cualquiera que envíe un Pull Request, abra un issue o + participe en las discusiones. +- **Moderadores de contenido**: personas con acceso al panel del backend para + aprobar o rechazar los negocios enviados por los visitantes. No + necesariamente coinciden con los mantenedores del código. + +## Toma de decisiones + +- Decisiones pequeñas (bugs, mejoras menores): las toma quien revisa el PR. +- Decisiones de diseño o que afecten al modelo de datos: discusión pública en + un issue durante al menos 7 días. Cierra el debate un mantenedor buscando + consenso. Si no lo hay, decide la organización. +- Las decisiones relevantes se registran en `docs/03-decisions.md`. + +## Código de conducta + +Adoptamos el [Contributor Covenant](https://www.contributor-covenant.org/). En +resumen: trata a las personas con respeto. Las violaciones se reportan al +correo `[CORREO_CONTACTO]` y las gestiona la organización. + +## Licencia + +El proyecto se publica bajo **MIT**. Las contribuciones se entienden +licenciadas bajo los mismos términos. + +## Releases + +Versionado semántico (semver). Los mantenedores publican versiones cuando +acumulan cambios suficientes. No hay calendario fijo. diff --git a/docs/05-contributing.md b/docs/05-contributing.md new file mode 100644 index 0000000..ebf3a8c --- /dev/null +++ b/docs/05-contributing.md @@ -0,0 +1,46 @@ +# Cómo contribuir + +¡Gracias por querer ayudar! El proyecto se mantiene gracias a la comunidad. + +## Antes de empezar + +1. Mira los [issues abiertos](../../issues), sobre todo los marcados + `good first issue`. +2. Si tu cambio es grande, abre un issue antes de programar para discutirlo. + +## Flujo + +1. Haz fork del repositorio. +2. Crea una rama desde `main`: `git checkout -b mi-cambio`. +3. Escribe el código siguiendo las pautas de abajo. +4. Asegúrate de que la app arranca: `npm install && npm run dev`. +5. Abre un Pull Request hacia `main` describiendo el cambio. + +## Pautas de código + +- **Mantén la app simple.** Si dudas entre dos soluciones, elige la más corta + y obvia. Este proyecto evita la complejidad a propósito. +- **Sigue la estructura existente.** Componentes en `src/components`, acceso a + datos en `src/services`. +- **No añadas dependencias sin necesidad.** Cada nueva librería es coste de + mantenimiento. +- **SOLID y Clean Code aplicados con sentido común.** Nombres claros, + funciones cortas, una responsabilidad por archivo. +- **Estilo**: 2 espacios de indentación, comillas dobles en JSX y simples en JS. + +## Tipos de contribución + +- **Código**: bugs, mejoras, nuevas funciones acordadas en issues. +- **Documentación**: cualquier archivo de `docs/` o el README. +- **Diseño**: propuestas en issues con mockups. +- **Reportar negocios**: usa la propia aplicación; no es necesario un PR. + +## Revisión + +Un mantenedor revisará tu PR. Puede pedir cambios. Una vez aprobado, hará +merge. Sé paciente: somos voluntarios. + +## Reconocimiento + +Todas las personas que han contribuido aparecen automáticamente en la pestaña +de Contributors del repositorio. diff --git a/docs/06-github-setup.md b/docs/06-github-setup.md new file mode 100644 index 0000000..b0c3bf7 --- /dev/null +++ b/docs/06-github-setup.md @@ -0,0 +1,76 @@ +# Configuración del repositorio en GitHub + +Pasos para dejar listo el repositorio. Pensado para que lo hagas una sola vez. + +## 1. Crear el repositorio + +1. En GitHub, crea un repositorio en la cuenta de la organización. + Recomendado: nombre corto, kebab-case, por ejemplo `business-map`. +2. Marca **Public**. +3. **No** inicialices con README, .gitignore ni licencia (ya vienen en este + prototipo). +4. Sube el código: + ```bash + git init + git add . + git commit -m "Prototipo inicial" + git branch -M main + git remote add origin git@github.com:ORG/business-map.git + git push -u origin main + ``` + +## 2. Ajustes básicos (Settings → General) + +- **Features**: deja activos Issues y Discussions. Desactiva Wikis y Projects + hasta que se usen. +- **Pull Requests**: marca "Allow squash merging" como única opción y "Always + suggest updating pull request branches". +- **Archive**: deja desactivado. + +## 3. Protección de la rama `main` (Settings → Branches) + +Añade una regla para `main`: +- Require a pull request before merging. +- Require approvals: 1. +- Dismiss stale approvals when new commits are pushed. +- Do not allow bypassing the above settings. + +## 4. Plantillas + +Las plantillas ya están en `.github/` del prototipo: +- `ISSUE_TEMPLATE/bug.md` +- `ISSUE_TEMPLATE/feature.md` +- `PULL_REQUEST_TEMPLATE.md` + +## 5. Documentos visibles en GitHub + +Para que GitHub los muestre como insignias del repositorio: +- `README.md` (raíz). +- `LICENSE` (raíz). +- `docs/05-contributing.md`: súbelo además como `CONTRIBUTING.md` en la raíz o + enlázalo desde el README. +- `CODE_OF_CONDUCT.md` recomendado en la raíz (Contributor Covenant oficial). + +## 6. Despliegue gratuito + +Opción recomendada: **Cloudflare Pages** o **GitHub Pages**. + +Para GitHub Pages con Vite: +1. Settings → Pages → Build and deployment → Source: **GitHub Actions**. +2. Añade un workflow en `.github/workflows/deploy.yml` (no incluido en el + prototipo para mantenerlo mínimo; añadirlo cuando se decida la URL final). +3. Ajusta `base` en `vite.config.js` al path del repo. + +## 7. Etiquetas iniciales sugeridas + +Crea en Issues → Labels: +- `good first issue` (verde) +- `bug` (rojo) +- `enhancement` (azul) +- `documentation` (gris) +- `discussion` (morado) + +## 8. Discussions + +Activa Discussions con las categorías: Announcements, Ideas, Q&A. Sirven como +foro de baja fricción para la comunidad. diff --git a/index.html b/index.html new file mode 100644 index 0000000..f12229e --- /dev/null +++ b/index.html @@ -0,0 +1,17 @@ + + + + + + Business Map + + + +
+ + + diff --git a/package.json b/package.json new file mode 100644 index 0000000..16c11ae --- /dev/null +++ b/package.json @@ -0,0 +1,21 @@ +{ + "name": "business-map", + "private": true, + "version": "0.1.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "dependencies": { + "leaflet": "^1.9.4", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-leaflet": "^4.2.1" + }, + "devDependencies": { + "@vitejs/plugin-react": "^4.3.1", + "vite": "^5.4.0" + } +} diff --git a/src/App.jsx b/src/App.jsx new file mode 100644 index 0000000..b1e1214 --- /dev/null +++ b/src/App.jsx @@ -0,0 +1,53 @@ +import { useEffect, useState } from "react"; +import MapView from "./components/MapView.jsx"; +import Sidebar from "./components/Sidebar.jsx"; +import SubmitForm from "./components/SubmitForm.jsx"; +import { getApproved, submit } from "./services/businessRepository.js"; + +export default function App() { + const [businesses, setBusinesses] = useState([]); + const [selectedId, setSelectedId] = useState(null); + const [formOpen, setFormOpen] = useState(false); + const [pickedLocation, setPickedLocation] = useState(null); + + useEffect(() => { + setBusinesses(getApproved()); + }, []); + + const selected = businesses.find((b) => b.id === selectedId) || null; + + function handleSubmit(data) { + submit(data); + setFormOpen(false); + setPickedLocation(null); + alert("¡Gracias! Tu propuesta queda pendiente de revisión."); + } + + return ( +
+ setFormOpen(true)} + onClose={() => setSelectedId(null)} + /> + + {formOpen && ( + { + setFormOpen(false); + setPickedLocation(null); + }} + /> + )} +
+ ); +} diff --git a/src/components/MapView.jsx b/src/components/MapView.jsx new file mode 100644 index 0000000..3135ddc --- /dev/null +++ b/src/components/MapView.jsx @@ -0,0 +1,68 @@ +import { MapContainer, TileLayer, Marker, useMapEvents } from "react-leaflet"; +import L from "leaflet"; + +// Configuración del icono por defecto. Leaflet no encuentra sus iconos cuando +// se empaqueta con Vite; los referenciamos desde el CDN para no copiar +// archivos a public/. +const icon = new L.Icon({ + iconUrl: "https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon.png", + iconRetinaUrl: "https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon-2x.png", + shadowUrl: "https://unpkg.com/leaflet@1.9.4/dist/images/marker-shadow.png", + iconSize: [25, 41], + iconAnchor: [12, 41], +}); + +const DEFAULT_CENTER = [40.4168, -3.7038]; +const DEFAULT_ZOOM = 13; + +function LocationPicker({ onPick }) { + useMapEvents({ + click(e) { + onPick({ lat: e.latlng.lat, lng: e.latlng.lng }); + }, + }); + return null; +} + +export default function MapView({ + businesses, + selectedId, + onSelect, + pickingLocation, + onPickLocation, + pickedLocation, +}) { + return ( +
+ + + {businesses.map((b) => ( + onSelect(b.id) }} + opacity={selectedId && selectedId !== b.id ? 0.6 : 1} + /> + ))} + {pickingLocation && } + {pickedLocation && ( + + )} + + {pickingLocation && ( +
Haz clic en el mapa para fijar la ubicación
+ )} +
+ ); +} diff --git a/src/components/Sidebar.jsx b/src/components/Sidebar.jsx new file mode 100644 index 0000000..211b216 --- /dev/null +++ b/src/components/Sidebar.jsx @@ -0,0 +1,51 @@ +export default function Sidebar({ business, onContribute, onClose }) { + return ( + + ); +} + +function Empty() { + return ( +
+

Selecciona un punto del mapa para ver sus detalles.

+
+ ); +} + +function BusinessDetails({ business, onClose }) { + return ( +
+ + {business.category} +

{business.name}

+

{business.address}

+

{business.description}

+ {business.contact && ( +

+ Contacto: {business.contact} +

+ )} +
+ ); +} diff --git a/src/components/SubmitForm.jsx b/src/components/SubmitForm.jsx new file mode 100644 index 0000000..e046e08 --- /dev/null +++ b/src/components/SubmitForm.jsx @@ -0,0 +1,103 @@ +import { useState } from "react"; + +const EMPTY = { + name: "", + category: "", + address: "", + description: "", + contact: "", +}; + +export default function SubmitForm({ pickedLocation, onSubmit, onCancel }) { + const [values, setValues] = useState(EMPTY); + + function update(field) { + return (e) => setValues({ ...values, [field]: e.target.value }); + } + + function handleSubmit(e) { + e.preventDefault(); + if (!pickedLocation) { + alert("Selecciona una ubicación haciendo clic en el mapa."); + return; + } + onSubmit({ ...values, lat: pickedLocation.lat, lng: pickedLocation.lng }); + } + + return ( +
+
e.stopPropagation()} + onSubmit={handleSubmit} + > +

Proponer un negocio

+

+ Los datos se revisarán antes de aparecer en el mapa. +

+ + + + + + + +