Source

firebase/client.js

import firebase from "firebase"
import "firebase/auth"
import "firebase/firestore"

const firebaseConfig = {
  apiKey: "AIzaSyB4BwDPJQiCJEPQkGLNBGVF5hmvsdqEXus",
  authDomain: "aquilastore-5e5b4.firebaseapp.com",
  projectId: "aquilastore-5e5b4",
  storageBucket: "aquilastore-5e5b4.appspot.com",
  messagingSenderId: "541611517575",
  appId: "1:541611517575:web:a4607cc5e3b2969536ba22",
  measurementId: "G-6M9DZJMWYY",
}

try {
  !firebase.apps.length && firebase.initializeApp(firebaseConfig)
} catch (error) {
  console.error("firebase-error", error)
}

const db = firebase.firestore()

/**
 * Firebase Client
 * @category  Firebase
 * @see https://firebase.google.com/docs/web/setup?hl=es
 * @author Braian D. Vaylet
 * @description Funciones para mapear, obtener, crear, editar y eliminar registros de la base de datos.
 */
export class FirebaseClient {
  // * MAP FUNC
  /**
   * mapUserFromFirebaseAuthToUser
   * @function
   * @param {object} user
   * @returns {object} {avatar: String, username: String, email: String, uid: String}
   * @description [Firebase] Mapea al user que obtengo desde la BD a un nuevo objeto de user
   */
  mapUserFromFirebaseAuthToUser(user) {
    const { displayName, email, photoURL, uid } = user
    return {
      avatar: photoURL,
      username: displayName,
      email,
      uid,
    }
  }

  /**
   * mapProductFromFirebaseToProduct
   * @function
   * @param {object} doc
   * @returns {object} {...data, id: String, createdAt: Date}
   * @description [Firebase] Mapea el elemento Product que obtengo desde la BD a un nuevo objeto Product con el id y la fecha de creaci贸n
   */
  mapProductFromFirebaseToProduct(doc) {
    const data = doc.data()
    const id = doc.id
    const createdAt =
      data.createdAt || firebase.firestore.Timestamp.fromDate(new Date())
    return { id, ...data, createdAt: +createdAt.toDate() }
  }

  /**
   * mapPurchaseFromFirebaseToPurchase
   * @function
   * @param {object} doc
   * @returns {object} {...data, id: String, createdAt: Date}
   * @description [Firebase] Mapea el elemento Purchase que obtengo desde la BD a un nuevo objeto Purchase con el id y la fecha de creaci贸n
   */
  mapPurchaseFromFirebaseToPurchase(doc) {
    const data = doc.data()
    const id = doc.id
    const createdAt =
      data.createdAt || firebase.firestore.Timestamp.fromDate(new Date())
    return { id, ...data, createdAt: +createdAt.toDate() }
  }

  /**
   * mapStorageFromFirebaseToStorage
   * @function
   * @param {object} doc
   * @returns {object} {...data, id: String, createdAt: Date}
   * @description [Firebase] Mapea el elemento Storage que obtengo desde la BD a un nuevo objeto Storage con el id y la fecha de creaci贸n
   */
  mapStorageFromFirebaseToStorage(doc) {
    const data = doc.data()
    const id = doc.id
    const createdAt =
      data.createdAt || firebase.firestore.Timestamp.fromDate(new Date())
    return { id, ...data, createdAt: +createdAt.toDate() }
  }

  /**
   * mapMessagesFromFirebaseToPurchase
   * @function
   * @param {object} doc
   * @returns {object} {...data, id: String, createdAt: Date}
   * @description [Firebase] Mapea el elemento Message que obtengo desde la BD a un nuevo objeto Message con el id y la fecha de creaci贸n
   */
  mapMessagesFromFirebaseToPurchase(doc) {
    const data = doc.data()
    const id = doc.id
    const createdAt =
      data.createdAt || firebase.firestore.Timestamp.fromDate(new Date())
    return { id, ...data, createdAt: +createdAt.toDate() }
  }

  // * AUTHENTICATION FUNC
  /**
   * onAuthStateChanged
   * @param {object} doc
   * @returns {object} mapUserFromFirebaseAuthToUser(user)
   * @description [Firebase] Reviso si el usuario esta autentificado
   */
  onAuthStateChanged(onChange) {
    return firebase.auth().onAuthStateChanged((user) => {
      const normalizedUser = user
        ? this.mapUserFromFirebaseAuthToUser(user)
        : null
      onChange(normalizedUser)
    })
  }

  /**
   * loginWithGoogle
   * @returns {Promise}
   * @description [Firebase] Autentificaci贸n con Google
   */
  loginWithGoogle() {
    const googleProvider = new firebase.auth.GoogleAuthProvider()
    return firebase.auth().signInWithPopup(googleProvider)
  }

  /**
   * loginWithFacebook
   * @returns {Promise}
   * @description [Firebase] Autentificaci贸n con Facebook
   */
  loginWithFacebook() {
    const facebookProvider = new firebase.auth.FacebookAuthProvider()
    return firebase.auth().signInWithPopup(facebookProvider)
  }

  /**
   * onAuthSignOut
   * @returns {Promise}
   * @description [Firebase] Desautentificar al user
   */
  onAuthSignOut() {
    firebase.auth().signOut()
  }

  // * ADD FUNC
  /**
   * addProduct
   * @param {object} Product { title: String, description: String, pictureName: String, pictureUrl: String, price: Number, stock: Number, brand: String, model: String, category: [Jackets, Shirts, Shoes, Accesories], calification: Number, gender: [Male, Female, All], sizes: Array(String), colors: Array(String),}
   * @returns {Promise<object>}
   * @description [Firebase] Agrego un nuevo producto a la base de datos
   */
  addProduct({
    title,
    description,
    pictureName,
    pictureUrl,
    price,
    stock,
    brand,
    model,
    category,
    calification,
    gender,
    sizes,
    colors,
  }) {
    return db.collection("products").add({
      title,
      description,
      pictureName,
      pictureUrl,
      price,
      stock,
      brand,
      model,
      category,
      calification,
      gender,
      sizes,
      colors,
      isActive: true,
      createdAt: firebase.firestore.Timestamp.fromDate(new Date()),
    })
  }

  /**
   * addPurchase
   * @param {object} Purchase { email:String, fullname:String, dni:String, phone:String, address:String, addressNum :String, addressInfo:String, products: Array(Product), total: Number, itsPaid: Boolean, createdAt = firebase(data), status = "init" }
   * @returns {Promise}
   * @description [Firebase] Agrego un nueva compra a la base de datos
   */
  addPurchase({
    email,
    fullname,
    dni,
    phone,
    address,
    addressNum,
    addressInfo,
    products,
    total,
    itsPaid = false,
    createdAt = firebase.firestore.Timestamp.fromDate(new Date()),
    status = "init",
  }) {
    products.forEach(async (product) => {
      const productDb = await this.fetchProductsByID(product.id)
      if (productDb.stock > 0 && productDb.stock >= product.count) {
        // actualizo stock
        await db
          .collection("products")
          .doc(product.id)
          .update({ stock: productDb.stock - product.count })
      } else {
        return new Error("No hay Stock")
      }
    })
    // creo una compra
    return db.collection("purchases").add({
      email,
      fullname,
      dni,
      phone,
      address,
      addressNum,
      addressInfo,
      products,
      total,
      itsPaid,
      createdAt,
      status,
    })
  }

  /**
   * addStorage
   * @param {object} Storage { email:String, favourites: Array(Product), notification: Array(object(Notification)), cart> Array(Product) }
   * @returns {Promise}
   * @description [Firebase] Guardo una copia del LocalStorage del usuario, al momento de hacer un onAuthSignOut se guarda el contenido en la bd y se levanta al momento de volver a autentificarse
   */
  async addStorage({ email, favourites, notifications, cart }) {
    try {
      const docs = await this.fetchStorageByUser(email)
      if (docs.length === 0) {
        return db
          .collection("storage")
          .add({ email, favourites, notifications, cart })
      } else if (docs.length === 1) {
        return db
          .collection("storage")
          .doc(docs[0].id)
          .update({ favourites, notifications, cart })
      } else {
        return new Error(
          "hay mas de un registro en storage para este usuario, revisar!"
        )
      }
    } catch (error) {
      console.error("error", error)
    }
  }

  /**
   * addMessageWorkWithUs
   * @param {object} Message { email:String, name:String, phone:String, linkedin:String }
   * @returns {Promise}
   * @description [Firebase] Guardo un nuevo mensaje en la BD del tipo 'workWithUs'
   */
  addMessageWorkWithUs({ email, name, phone, linkedin }) {
    return db.collection("messages").add({
      email,
      name,
      phone,
      linkedin,
      viewed: false,
      type: "workWithUs",
      createdAt: firebase.firestore.Timestamp.fromDate(new Date()),
    })
  }

  /**
   * addMessageHelp
   * @param {object} Message { email:String, name:String, phone:String, comment:String }
   * @returns {Promise}
   * @description [Firebase] Guardo un nuevo mensaje en la BD del tipo 'help'
   */
  addMessageHelp({ email, name, phone, comment }) {
    return db.collection("messages").add({
      email,
      name,
      phone,
      comment,
      viewed: false,
      type: "help",
      createdAt: firebase.firestore.Timestamp.fromDate(new Date()),
    })
  }

  // * EDIT FUNC
  /**
   * editProduct
   * @param {string} id
   * @param {object} Product { title: String, description: String, pictureName: String, pictureUrl: String, price: Number, stock: Number, brand: String, model: String, category: [Jackets, Shirts, Shoes, Accesories], calification: Number, gender: [Male, Female, All], sizes: Array(String), colors: Array(String),}
   * @returns {Promise}
   * @description [Firebase] Edito un producto por id
   */
  editProduct(
    id,
    {
      title,
      description,
      pictureName,
      pictureUrl,
      price,
      stock,
      brand,
      model,
      category,
      calification,
      gender,
      sizes,
      colors,
    }
  ) {
    return db
      .collection("products")
      .doc(id)
      .update({
        title,
        description,
        pictureName,
        pictureUrl,
        price,
        stock,
        brand,
        model,
        category,
        calification,
        gender,
        sizes,
        colors,
        isActive: true,
        createdAt: firebase.firestore.Timestamp.fromDate(new Date()),
      })
  }

  /**
   * changeIsActiveProductByID
   * @param {string} id
   * @param {boolean} active
   * @returns {Promise}
   * @description [Firebase] Edito el estado isActive de un producto
   */
  async changeIsActiveProductByID(id, active) {
    try {
      return await db
        .collection("products")
        .doc(id)
        .update({ isActive: active })
    } catch (error) {
      console.error("error", error)
    }
  }

  // * GET FUNC
  /**
   * fetchAllProducts
   * @returns {array} Products
   * @description [Firebase] Obtengo todos los productos de la base de datos
   */
  async fetchAllProducts() {
    try {
      const doc = await db.collection("products").get()
      return doc.docs.map(this.mapProductFromFirebaseToProduct)
    } catch (error) {
      console.error("error", error)
    }
  }

  /**
   * fetchProducts
   * @returns {array} Products
   * @description [Firebase] Obtengo todos los productos de la base de datos si isActive === true
   */
  async fetchProducts() {
    try {
      const doc = await db
        .collection("products")
        .where("isActive", "==", true)
        .get()
      return doc.docs.map(this.mapProductFromFirebaseToProduct)
    } catch (error) {
      console.error("error", error)
    }
  }

  /**
   * fetchProductsByCategory
   * @param {string} category
   * @returns {array} Products
   * @description [Firebase] Obtengo todos los productos de la base de datos filtrados por categoria donde isActive === true
   */
  async fetchProductsByCategory(category) {
    try {
      const doc = await db
        .collection("products")
        .where("category", "==", category)
        .where("isActive", "==", true)
        .get()
      return doc.docs.map(this.mapProductFromFirebaseToProduct)
    } catch (error) {
      console.error("error", error)
    }
  }

  /**
   * fetchProductsByID
   * @param {string} id
   * @returns {object} Product
   * @description [Firebase] Obtengo un producto de la base de datos por id
   */
  async fetchProductsByID(id) {
    try {
      const doc = await db.collection("products").doc(id).get()
      if (doc.exists) {
        return this.mapProductFromFirebaseToProduct(doc)
      } else {
        console.error("error", "Error, el producto no existe")
        return {}
      }
    } catch (error) {
      console.error("error", error)
    }
  }

  /**
   * fetchAllPurchases
   * @returns {array} Purchases
   * @description [Firebase] Obtengo todas las compras de la base de datos
   */
  async fetchAllPurchases() {
    try {
      const doc = await db
        .collection("purchases")
        .orderBy("createdAt", "asc")
        .get()
      return doc.docs.map(this.mapPurchaseFromFirebaseToPurchase)
    } catch (error) {
      console.error("error", error)
    }
  }

  /**
   * fetchAllPurchasesByUser
   * @param {string} email
   * @returns {array} Purchases
   * @description [Firebase] Obtengo todas las compras de la base de datos filtradas por el email de un user
   */
  async fetchAllPurchasesByUser(email) {
    try {
      const doc = await db
        .collection("purchases")
        .where("email", "==", email)
        .get()
      return doc.docs.map(this.mapPurchaseFromFirebaseToPurchase)
    } catch (error) {
      console.error("error", error)
    }
  }

  /**
   * fetchStorageByUser
   * @param {string} email
   * @returns {object} Storage {favourites, notifications, cart}
   * @description [Firebase] Obtengo la copia del storage almacenada en la bd por email del user
   */
  async fetchStorageByUser(email) {
    try {
      const doc = await db
        .collection("storage")
        .where("email", "==", email)
        .get()
      return doc.docs.map(this.mapStorageFromFirebaseToStorage)
    } catch (error) {
      console.error("error", error)
    }
  }

  /**
   * fetchAllMessages
   * @returns {array} Messages
   * @description [Firebase] Obtengo todos los mensajes de la base de datos
   */
  async fetchAllMessages() {
    try {
      const doc = await db
        .collection("messages")
        .orderBy("createdAt", "asc")
        .get()
      return doc.docs.map(this.mapMessagesFromFirebaseToPurchase)
    } catch (error) {
      console.error("error", error)
    }
  }

  // * DELETE FUNC
  /**
   * deleteProductsByID
   * @param {string} id
   * @returns {Promise}
   * @description [Firebase] Elimino un producto de la bd por id
   */
  async deleteProductsByID(id) {
    try {
      return await db.collection("products").doc(id).delete()
    } catch (error) {
      console.error("error", error)
    }
  }

  /**
   * deleteMessagesByID
   * @param {string} id
   * @returns {Promise}
   * @description [Firebase] Elimino un mensaje de la bd por id
   */
  async deleteMessagesByID(id) {
    try {
      return await db.collection("messages").doc(id).delete()
    } catch (error) {
      console.error("error", error)
    }
  }
}