Una tienda en línea completa construida con Next.js y MongoDB. Este proyecto demuestra cómo crear una aplicación e-commerce escalable con características modernas como carrito de compras persistente, pagos en línea y panel de administración.
// models/product.js
import mongoose from 'mongoose';
const productSchema = new mongoose.Schema({
name: {
type: String,
required: [true, 'El nombre del producto es requerido'],
trim: true,
maxLength: [100, 'El nombre no puede exceder 100 caracteres']
},
description: {
type: String,
required: [true, 'La descripción del producto es requerida'],
maxLength: [2000, 'La descripción no puede exceder 2000 caracteres']
},
price: {
type: Number,
required: [true, 'El precio es requerido'],
maxLength: [8, 'El precio no puede exceder 8 caracteres'],
default: 0.0
},
ratings: {
type: Number,
default: 0
},
images: [
{
public_id: {
type: String,
required: true
},
url: {
type: String,
required: true
}
}
],
category: {
type: String,
required: [true, 'Seleccione la categoría del producto'],
enum: {
values: [
'Electrónicos',
'Ropa',
'Alimentos',
'Libros',
'Belleza',
'Deportes',
'Hogar'
],
message: 'Seleccione una categoría válida'
}
},
seller: {
type: String,
required: [true, 'Ingrese el vendedor del producto']
},
stock: {
type: Number,
required: [true, 'Ingrese el stock del producto'],
maxLength: [5, 'El stock no puede exceder 5 caracteres'],
default: 0
},
numOfReviews: {
type: Number,
default: 0
},
reviews: [
{
user: {
type: mongoose.Schema.ObjectId,
ref: 'User',
required: true
},
name: {
type: String,
required: true
},
rating: {
type: Number,
required: true
},
comment: {
type: String,
required: true
}
}
],
createdAt: {
type: Date,
default: Date.now
}
});
export default mongoose.models.Product || mongoose.model('Product', productSchema);
// store/cartSlice.js
import { createSlice } from '@reduxjs/toolkit';
const cartSlice = createSlice({
name: 'cart',
initialState: {
items: [],
totalQuantity: 0,
totalAmount: 0,
},
reducers: {
addToCart(state, action) {
const newItem = action.payload;
const existingItem = state.items.find(item => item.id === newItem.id);
state.totalQuantity++;
if (!existingItem) {
state.items.push({
id: newItem.id,
price: newItem.price,
quantity: 1,
totalPrice: newItem.price,
name: newItem.name,
image: newItem.image
});
} else {
existingItem.quantity++;
existingItem.totalPrice = existingItem.totalPrice + newItem.price;
}
state.totalAmount = state.items.reduce(
(total, item) => total + item.price * item.quantity,
0
);
},
removeFromCart(state, action) {
const id = action.payload;
const existingItem = state.items.find(item => item.id === id);
state.totalQuantity--;
if (existingItem.quantity === 1) {
state.items = state.items.filter(item => item.id !== id);
} else {
existingItem.quantity--;
existingItem.totalPrice = existingItem.totalPrice - existingItem.price;
}
state.totalAmount = state.items.reduce(
(total, item) => total + item.price * item.quantity,
0
);
},
clearCart(state) {
state.items = [];
state.totalQuantity = 0;
state.totalAmount = 0;
}
}
});
export const cartActions = cartSlice.actions;
export default cartSlice.reducer;
// Uso en un componente
function ProductItem() {
const dispatch = useDispatch();
const addToCartHandler = () => {
const productData = {
id: '123',
name: 'Producto Ejemplo',
price: 29.99,
image: '/images/product.jpg'
};
dispatch(cartActions.addToCart(productData));
};
return (
<div>
<h2>Producto Ejemplo</h2>
<p>$29.99</p>
<button onClick={addToCartHandler}>
Agregar al carrito
</button>
</div>
);
}
Para comenzar con este proyecto, puedes: