diff --git a/app/api/prendas/route.js b/app/api/prendas/route.js index 96aa423..72fe69b 100644 --- a/app/api/prendas/route.js +++ b/app/api/prendas/route.js @@ -1,3 +1,95 @@ +<<<<<<< HEAD +// import { NextResponse } from 'next/server'; +// import { Prenda, syncDatabase } from '@/lib/models/index.js'; +// import { verifyToken } from '@/lib/auth.js'; + +// let dbInitialized = false; +// const initDB = async () => { +// if (!dbInitialized) { +// await syncDatabase(); +// dbInitialized = true; +// } +// }; + +// // GET: Obtener todas las prendas del usuario autenticado +// export async function GET(request) { +// try { +// await initDB(); + +// // Obtener token del header +// const authHeader = request.headers.get('authorization'); +// if (!authHeader || !authHeader.startsWith('Bearer ')) { +// return NextResponse.json({ error: 'No autorizado' }, { status: 401 }); +// } + +// const token = authHeader.split(' ')[1]; +// const decoded = verifyToken(token); + +// if (!decoded) { +// return NextResponse.json({ error: 'Token inválido' }, { status: 401 }); +// } + +// const prendas = await Prenda.findAll({ +// where: { usuario_id: decoded.id }, +// order: [['createdAt', 'DESC']], +// }); + +// return NextResponse.json({ prendas }); + +// } catch (error) { +// console.error('Error GET prendas:', error); +// return NextResponse.json({ error: 'Error interno' }, { status: 500 }); +// } +// } + +// // POST: Crear una nueva prenda +// export async function POST(request) { +// try { +// await initDB(); + +// const authHeader = request.headers.get('authorization'); +// if (!authHeader || !authHeader.startsWith('Bearer ')) { +// return NextResponse.json({ error: 'No autorizado' }, { status: 401 }); +// } + +// const token = authHeader.split(' ')[1]; +// const decoded = verifyToken(token); + +// if (!decoded) { +// return NextResponse.json({ error: 'Token inválido' }, { status: 401 }); +// } + +// const { nombre, descripcion, imagen_url, categoria, talle, color, etiquetas } = await request.json(); + +// if (!nombre || !imagen_url) { +// return NextResponse.json({ error: 'Nombre e imagen son obligatorios' }, { status: 400 }); +// } + +// const prenda = await Prenda.create({ +// nombre, +// descripcion, +// imagen_url, +// categoria: categoria || 'otro', +// talle, +// color, +// etiquetas: etiquetas || [], +// usuario_id: decoded.id, +// }); + +// return NextResponse.json({ prenda }, { status: 201 }); + +// } catch (error) { +// console.error('Error POST prenda:', error); +// return NextResponse.json({ error: error.message }, { status: 500 }); +// } +// } + + +import { NextResponse } from 'next/server'; +import prisma from '@/lib/prisma'; +import { verifyToken } from '@/lib/auth.js'; + +======= import { NextResponse } from 'next/server'; import prisma from '@/lib/prisma'; import { verifyToken } from '@/lib/auth.js'; @@ -25,7 +117,11 @@ export async function GET(request) { return NextResponse.json({ error: 'Token inválido' }, { status: 401 }); } +<<<<<<< HEAD + const prendas = await prisma.prendas.findMany({ +======= const prendas = await prisma.prenda.findMany({ +>>>>>>> c288ec095e3c1a150ec685593bd5da517f6f53ac where: { id_usuario: decoded.id }, orderBy: { createdAt: 'desc' }, }); @@ -87,6 +183,7 @@ export async function POST(request) { } const prenda = await prisma.prenda.create({ +>>>>>>> c288ec095e3c1a150ec685593bd5da517f6f53ac data: { nombre, tipo, diff --git a/app/closet/nueva/page.tsx b/app/closet/nueva/page.tsx new file mode 100644 index 0000000..6b96b3d --- /dev/null +++ b/app/closet/nueva/page.tsx @@ -0,0 +1,389 @@ +"use client"; + +import { useState, useRef } from "react"; + +const CATEGORIAS_PREDEFINIDAS = [ + "Remera", "Camisa", "Vestido", "Pantalón", "Jean", "Pollera", + "Abrigo", "Campera", "Sweater", "Shorts", "Calzado", "Accesorio" +]; + +const ETIQUETAS_PREDEFINIDAS = [ + "Favorita", "Verano", "Invierno", "Primavera", "Otoño", + "Casual", "Formal", "Deportivo", "Fiesta", "Trabajo" +]; + +const TALLES_ROPA = ["XS", "S", "M", "L", "XL", "XXL", "34", "36", "38", "40", "42", "44"]; + +interface Props { + onClose: () => void; +} + +export default function NuevaPrenda({ onClose }: Props) { + const [imagen, setImagen] = useState(null); + const [preview, setPreview] = useState(null); + const [nombre, setNombre] = useState(""); + const [categoria, setCategoria] = useState(""); + const [categoriaCustom, setCategoriaCustom] = useState(""); + const [talle, setTalle] = useState(""); + const [color, setColor] = useState(""); + const [descripcion, setDescripcion] = useState(""); + const [etiquetas, setEtiquetas] = useState([]); + const [etiquetaCustom, setEtiquetaCustom] = useState(""); + const [cargando, setCargando] = useState(false); + const [exito, setExito] = useState(false); + const [error, setError] = useState(""); + const fileRef = useRef(null); + + const handleImagen = (e: React.ChangeEvent) => { + const file = e.target.files?.[0]; + if (!file) return; + const permitidos = ["image/jpeg", "image/png", "image/webp"]; + if (!permitidos.includes(file.type)) { + setError("Formato no válido. Usá JPG, PNG o WEBP."); + return; + } + if (file.size > 5 * 1024 * 1024) { + setError("La imagen no puede superar 5MB."); + return; + } + setError(""); + setImagen(file); + setPreview(URL.createObjectURL(file)); + }; + + const toggleEtiqueta = (e: string) => { + setEtiquetas(prev => prev.includes(e) ? prev.filter(x => x !== e) : [...prev, e]); + }; + + const agregarEtiquetaCustom = () => { + const tag = etiquetaCustom.trim(); + if (tag && !etiquetas.includes(tag)) { + setEtiquetas(prev => [...prev, tag]); + setEtiquetaCustom(""); + } + }; + + const handleSubmit = async () => { + if (!nombre || !categoria || !talle || !color) { + setError("Completá nombre, categoría, talle y color."); + return; + } + setCargando(true); + setError(""); + try { + const formData = new FormData(); + if (imagen) formData.append("imagen", imagen); + formData.append("nombre", nombre); + formData.append("tipo", categoriaCustom || categoria); + formData.append("talle", talle); + formData.append("color", color); + formData.append("descripcion", descripcion); + formData.append("etiquetas", JSON.stringify(etiquetas)); + + const res = await fetch("/api/prendas", { method: "POST", body: formData }); + if (!res.ok) throw new Error("Error al guardar"); + setExito(true); + setTimeout(() => onClose(), 1800); + } catch { + setError("Algo salió mal. Intentá de nuevo."); + } finally { + setCargando(false); + } + }; + + const inputStyle = { + width: "100%", + boxSizing: "border-box" as const, + background: "#fafaf7", + border: "1px solid #e8e4de", + borderRadius: "10px", + padding: "10px 14px", + fontSize: "13px", + color: "#1a1a1a", + outline: "none", + fontFamily: "inherit", + }; + + const labelStyle = { + display: "block", + fontSize: "10px", + fontWeight: 500, + letterSpacing: "0.15em", + textTransform: "uppercase" as const, + color: "#6b6b60", + marginBottom: "6px", + }; + + if (exito) { + return ( +
+
+ 🌿 +
+

¡Listo!

+

Prenda agregada

+

Ya forma parte de tu closet sostenible ✦

+
+ ); + } + + return ( +
+ {/* Header del modal */} +
+
+

Tu closet

+

Agregar prenda

+
+ +
+ + {/* Body */} +
+ + {/* Upload imagen */} +
+ +
fileRef.current?.click()} + style={{ + border: "1.5px dashed #e8e4de", + borderRadius: "14px", + padding: "1.5rem", + textAlign: "center", + cursor: "pointer", + background: preview ? "#fafaf7" : "transparent", + transition: "border-color 0.2s", + }} + onMouseEnter={e => (e.currentTarget.style.borderColor = "#A8C5A0")} + onMouseLeave={e => (e.currentTarget.style.borderColor = "#e8e4de")} + > + {preview ? ( + preview + ) : ( + <> +

📷

+

Tocá para subir una foto

+

JPG, PNG o WEBP · Máx 5MB

+ + )} +
+ +
+ + {/* Nombre */} +
+ + setNombre(e.target.value)} + onFocus={e => (e.target.style.borderColor = "#A8C5A0")} + onBlur={e => (e.target.style.borderColor = "#e8e4de")} + /> +
+ + {/* Categoría */} +
+ +
+ {CATEGORIAS_PREDEFINIDAS.map(cat => ( + + ))} +
+ { setCategoriaCustom(e.target.value); setCategoria(""); }} + onFocus={e => (e.target.style.borderColor = "#A8C5A0")} + onBlur={e => (e.target.style.borderColor = "#e8e4de")} + /> +
+ + {/* Talle y color en fila */} +
+
+ + +
+
+ + setColor(e.target.value)} + onFocus={e => (e.target.style.borderColor = "#A8C5A0")} + onBlur={e => (e.target.style.borderColor = "#e8e4de")} + /> +
+
+ + {/* Descripción */} +
+ +