Maison  >  Article  >  interface Web  >  Méga-marché

Méga-marché

王林
王林original
2024-07-18 18:38:58583parcourir

Mega Mart

Ceci est une soumission pour le Wix Studio Challenge.

Arrière-plan

Pourquoi suivre des méthodes dépassées alors que l'IA transforme le paysage commercial ? J'ai créé ce e-commerce car aujourd'hui l'IA est la tendance des entreprises. Mega Mart est une plate-forme qui offre non seulement une grande variété de produits, mais améliore également l'UI/UX grâce à des fonctionnalités avancées réalisées dans Wix Studio.


Ce que j'ai construit

J'ai développé des options de recherche avancées par l'IA pour résoudre le problème de la découverte de produits inefficace, dans le but de créer une expérience d'achat plus intuitive et agréable pour les utilisateurs.

Avez-vous déjà vu quelqu'un porter un t-shirt que vous aimez mais vous ne vouliez pas lui demander la marque ? Avec Mega Market, prenez simplement une photo et téléchargez-la sur notre plateforme ; nous le rechercherons dans notre base de données de produits.

Pourquoi aller au centre commercial quand vous pouvez rechercher le produit que vous souhaitez comme vous le demanderiez à un employé de magasin ? écrivez simplement votre requête en langage naturel et nous vous montrerons des exemples des produits que vous recherchez. Par exemple, essayez de taper J'ai besoin d'une veste d'hiver pour femme ou Je voudrais un smartphone noir avec appareil photo.

La meilleure partie est que le site ne nécessite pas d'abonnement Wix pour terminer le processus de paiement car il est entièrement construit à l'aide d'éléments Wix et d'API Velo.


Comment acheter au Mega Market

Créer un compte (ou pas...)

Chez Mega Mart, vous avez la possibilité d'acheter des produits avec ou sans compte.
La création d'un compte offre plusieurs avantages :

  • Lors du paiement, vos informations personnelles et adresses précédentes sont automatiquement renseignées, ce qui vous fait gagner du temps.
  • De plus, vous recevez un cashback instantané qui peut être utilisé pour de futurs achats.
  • Vous aimez un produit mais vous ne souhaitez pas encore l'acheter ? Votre compte comprend une fonctionnalité liste de souhaits, vous permettant d'enregistrer des produits pour plus tard.

Rechercher des articles de produits

Comme mentionné précédemment, vous pouvez rechercher des produits en téléchargeant des images depuis votre appareil, en utilisant l'IA ou en parcourant tous les articles pour sélectionner celui que vous aimez le plus.

Vérifiez votre panier

Avant de finaliser votre commande, assurez-vous d'avoir ajouté tous les produits souhaités à votre panier. Ajustez la quantité des articles si nécessaire ou supprimez les erreurs. De plus, vérifiez votre total et la date de livraison estimée.

Vérifier

Si vous êtes connecté, vos informations personnelles seront automatiquement renseignées. Vous pouvez choisir l'une de vos adresses précédemment enregistrées ou en saisir une nouvelle, qui sera ajoutée à votre compte pour les prochaines commandes.
Entrez les informations de votre carte de crédit ou de débit, puis cliquez sur le dernier bouton pour finaliser le paiement.

Votre commande est terminée !

Vous serez redirigé vers la page de commande, où vous pourrez consulter les détails de votre achat et son statut : s'il est en cours de traitement, en transit pour livraison ou déjà livré. Vous recevrez également un email de confirmation de votre commande.


Avantages du Méga-Mart

Sans mot de passe

Qui veut mémoriser ses mots de passe de nos jours ? Chez Mega Mart, vous n'avez pas à vous en soucier ; chaque fois que vous souhaitez vous connecter à votre compte, vous recevrez un code à un chiffre par e-mail, SMS ou appel.

Remise en argent

Qui n'aime pas être récompensé pour ses achats ? Chaque fois que vous achetez chez Mega Mart, vous recevrez 1 % de remise en argent, que vous pourrez utiliser pour payer de futures commandes.

Liste de souhaits

Avez-vous déjà trouvé un produit que vous aimez mais que vous n’êtes pas encore prêt à acheter ? Chez Mega Mart, vous pouvez l'enregistrer dans votre liste de souhaits pour un prochain achat.

Statut de la commande

Vous n'avez pas besoin de basculer entre les pages pour voir l'état de votre envoi. Depuis la page de commande, vous pouvez facilement vérifier si elle est en cours, en transit ou déjà livrée.

Entièrement réactif

Vous êtes-vous déjà demandé si un site Web pouvait être tout aussi fluide sur votre ordinateur, votre tablette ou votre téléphone ? Chez Mega Mart, notre site est entièrement réactif sur tous les appareils.

Enregistrez vos adresses

Curieuse de savoir à quel point faire du shopping peut être pratique ? Chez Mega Mart, vos adresses sont enregistrées pour de futurs achats et vous pouvez les gérer facilement depuis la page de votre compte.

Aucun compte requis

Alors que de nombreux sites de commerce électronique nécessitent la création d'un compte pour faire des achats, chez Mega Mart, vous pouvez acheter en tant qu'invité. Cependant, n'oubliez pas tous les avantages dont vous pouvez bénéficier en tant que client fréquent de Mega Mart.


Parcours de développement

L'utilisation de Wix Studio m'a aidé à accélérer le développement du frontend, en réduisant considérablement le temps tout en garantissant une conception entièrement réactive sur tous les appareils. JavaScript joue un rôle important dans l'amélioration de l'interaction de l'utilisateur avec les éléments de Wix Studio, permettant ainsi des fonctionnalités dynamiques de manière transparente. De plus, notre site utilise des mesures de sécurité robustes, notamment la sécurité des bases de données, des méthodes Web et du routeur, pour protéger les données des utilisateurs et garantir une expérience de navigation sécurisée.

Avant de découvrir comment j'ai créé Mega Mart, il est important de dire que je n'ai installé aucune application du Wix Market. Alors que j'utilisais Wix Stores pour enregistrer des produits dans le panier, puis les récupérer dans certaines pages, j'ai choisi de créer entièrement les pages de produits, le panier et les pages de paiement à l'aide de Wix Elements.

Avis de non-responsabilité
Puisqu'il s'agit d'une démo pour le concours, je n'ai enregistré que 29 produits dans la base de données. Pour tirer le meilleur parti des méthodes de reconnaissance de produits d'images IA et de recherche IA, veuillez d'abord vous familiariser avec les produits disponibles dans le magasin. Par exemple, la recherche d'une imprimante ou d'un marteau peut ne pas donner de résultats, car ces éléments ne figurent pas dans la base de données de démonstration. Cependant, si ce projet était étendu à de grandes entreprises comme Home Depot ou Walmart, qui ont des milliers de produits dans leurs bases de données, les fonctionnalités de l'IA effectueraient des recherches complètes et fourniraient un plus large éventail de résultats.


Essayez-le !

Curieux de voir ce que Mega Mart peut apporter à votre expérience de magasinage ? Cliquez pour explorer et découvrir les possibilités.
Allons-y !


Comment je l'ai construit

Développer ce site de commerce électronique n'a pas été facile, surtout compte tenu du calendrier serré, mais je suis fier de l'avoir construit avec toutes ses fonctionnalités en seulement 2 semaines.

Maison

La première page que j'ai développée était la page d'accueil, où j'avais pour objectif d'incorporer des fonctionnalités couramment trouvées sur d'autres sites de commerce électronique, telles que la présentation des best-sellers, des nouveautés et la mise en évidence des avantages des achats en magasin.
Vous trouverez ci-dessous un exemple de la façon dont j'ai récupéré les données sur les meilleures ventes et les produits les plus récents dans le code.

import wixData from 'wix-data';
import wixWindowFrontend from 'wix-window-frontend';
import wixLocationFrontend from 'wix-location-frontend';

const limit = wixWindowFrontend.formFactor === "Mobile" ? 6 : 5

const bestSellers = (await wixData.query("Products").descending("rating").limit(limit).find()).items
$w('#repeater2').data = bestSellers

$w('#repeater2').onItemReady(($item, itemData, index)=>{
    $item('#imageX9').src = itemData.imageUrl[0].src
    $item('#imageX9').alt = itemData.name

    $item('#text12').text = itemData.name
    $item('#text13').text = "$" + itemData.price

    $item('#box15').onClick(() => {
        wixLocationFrontend.to(`/product/${itemData._id}`)
    })
})

const newProucts = (await wixData.query("Products").descending("_createdDate").limit(limit).find()).items
$w('#repeater3').data = newProucts

$w('#repeater3').onItemReady(($item, itemData, index)=>{
    $item('#imageX10').src = itemData.imageUrl[0].src
    $item('#imageX10').alt = itemData.name

    $item('#text15').text = itemData.name
    $item('#text14').text = "$" + itemData.price

    $item('#box19').onClick(() => {
        wixLocationFrontend.to(`/product/${itemData._id}`)
    })
})

Comme vous pouvez le voir, le code inclut également une logique pour détecter si l'utilisateur navigue sur un appareil mobile ou non. En vue mobile, les répéteurs sont affichés sur deux colonnes, tandis qu'en modes tablette et ordinateur, ils sont disposés sur une seule ligne avec cinq colonnes.

Connexion et inscription

Je pense que les pages d'inscription et de connexion doivent avoir une interface UI/UX intuitive. Si l'un ou l'autre des processus est difficile ou frustrant pour les utilisateurs, ils peuvent quitter le site, entraînant une perte de vente.
Pour éviter cela, Mega Mart utilise un système sans mot de passe. Chaque fois que vous souhaitez vous connecter, un code à usage unique sera envoyé par SMS, appel ou e-mail, éliminant ainsi le besoin de mémoriser plusieurs mots de passe.

Registre

import { verify, sendVerification } from "backend/twilio.web"
import { register } from "backend/members.web"

$w('#button1').onClick(async () => {
    $w('#imageX9').show()
    $w('#button1').disable()
    if ($w('#box37').isVisible) {
        if ($w('#input5').valid) {

            $w('#text35').collapse()

            const verifyEmailResponse = await verify($w('#input3').value, $w('#input5').value)
            const verifyPhoneResponse = await verify($w('#input4').value, $w('#input5').value)

            if (verifyEmailResponse || verifyPhoneResponse) {
                try {
                    const sessionToken = await register($w('#input1').value, $w('#input2').value, $w('#input3').value, $w('#input4').value)
                    await authentication.applySessionToken(sessionToken)
                } catch (error) {
                    $w('#text35').text = "Error registering: " + new Error(error).message
                    $w('#text35').expand()
                }

            } else {
                $w('#text35').text = "Wrong verification code."
                $w('#text35').expand()
            }
        }
    } else {
        const sendEmailResponse = await sendVerification("email", $w('#input3').value)
        const sendSMSResponse = await sendVerification("sms", $w('#input4').value)
        if (sendEmailResponse && sendSMSResponse) {
            $w('#input1,#input2,#box36').collapse()
            $w('#box37').expand()
            $w('#button1').label = "Register"
            $w('#text37').hide()
        }
    }
    $w('#imageX9').hide()
    $w('#button1').enable()
})

Ce code gère le processus d'inscription lorsque vous cliquez sur le bouton. Si la zone de saisie du code de vérification est visible et que la saisie du code de vérification est valide, il vérifie l'e-mail ou le numéro de téléphone de l'utilisateur avec le code de vérification fourni. Si l'une ou l'autre des vérifications réussit, il tente d'enregistrer l'utilisateur, puis applique un jeton de session pour l'authentification. Si la vérification échoue, un message d'erreur s'affiche.
Si la boîte n'est pas visible, elle envoie les codes de vérification à l'e-mail et au téléphone de l'utilisateur, puis met à jour l'interface utilisateur pour permettre à l'utilisateur de terminer l'inscription.

Se connecter

Envoi du code de vérification à l'utilisateur

import { sendVerification } from "backend/twilio.web"
import { verifyEmailOrPhoneNumber} from "backend/members.web"

$w('#button2,#button3').onClick(async (rest) => {
    if ($w('#input7').valid || $w('#input8').valid) {
        $w('#imageX10').show()
        $w('#text34').collapse()
        $w('#button2,#button3').disable()

        let toSearch = $w('#input7').isVisible ? $w('#input7').value : $w('#input8').value
        if (!(await verifyEmailOrPhoneNumber(toSearch))) {
            $w('#text34').text = "There isn't an account with that email or phone number."
            $w('#text34').expand()
        } else {
            switch (rest.target) {
            case $w('#button2'):
                let response = false
                if ($w('#input7').isVisible) {
                    response = await sendVerification("email", $w('#input7').value)
                    verificationTypeAtLogin = "email"
                    $w('#text31').text = "Please check you email. It may be at junk mails."
                } else {
                    response = await sendVerification("sms", $w('#input8').value)
                    verificationTypeAtLogin = "sms"
                    $w('#text31').text = "Please check you SMS."
                }

                if (response) {
                    $w('#text33,#box41,#box47').collapse()
                    $w('#box46,#box48').expand()
                    $w('#text36').hide()
                } else {
                    $w('#text34').text = "We couldn't send the verification code. Try using another method."
                    $w('#text34').expand()
                }
                break;
            case $w('#button3'):
                verificationTypeAtLogin = "call"
                $w('#text31').text = "You will receive a phone call in a few moments."

                if (await sendVerification("call", $w('#input8').value)) {
                    $w('#text33,#box41,#box47').collapse()
                    $w('#box46,#box48').expand()
                    $w('#text36').hide()
                } else {
                    $w('#text34').text = "We couldn't send the verification code. Try using another method."
                    $w('#text34').expand()
                }
                break;
            }
        }
        $w('#imageX10').hide()
        $w('#button2,#button3').enable()
    }
})

Il vérifie d'abord l'existence de l'email ou du numéro de téléphone saisi.
Selon le bouton cliqué, il envoie un code de vérification par e-mail, SMS ou appel. Ensuite, il étend la saisie du code de vérification pour que l'utilisateur puisse écrire le code reçu.
Si la vérification échoue, un message d'erreur approprié s'affiche. Enfin, l'image de chargement est masquée et les boutons sont réactivés.

Vérification du code de vérification

import { verify } from "backend/twilio.web"
import { login } from "backend/members.web"

if ($w('#input9').valid) {
    $w('#imageX11').show()
    $w('#text34').collapse()
    $w('#button4').disable()

    let verifyResponse = false
    if (verificationTypeAtLogin === "email") verifyResponse = await verify($w('#input7').value, $w('#input9').value)
    else verifyResponse = await verify($w('#input8').value, $w('#input9').value)

    if (verifyResponse) {
        try {
            const sessionToken = await login(verificationTypeAtLogin === "email" ? $w('#input7').value : "", verificationTypeAtLogin !== "email" ? $w('#input8').value : "")
            await authentication.applySessionToken(sessionToken)
        } catch (error) {
            $w('#text34').text = "Error registering: " + new Error(error).message
            $w('#text34').expand()
            $w('#imageX11').hide()
            $w('#button4').enable()
        }

    } else {
        $w('#text34').text = "Wrong verification code."
        $w('#text34').expand()
        $w('#imageX11').hide()
        $w('#button4').enable()
    }

}

Il vérifie le code fourni en fonction du type de vérification (e-mail ou téléphone). Si la vérification réussit, elle tente de connecter l'utilisateur et d'appliquer le jeton de session pour l'authentification.

Redirection de l'utilisateur

Que l'utilisateur se connecte ou s'enregistre, après un processus réussi, il sera redirigé vers la dernière page visitée.

import { authentication } from "wix-members"
import wixLocationFrontend from 'wix-location-frontend'

authentication.onLogin(() => {
    wixLocationFrontend.to(wixLocationFrontend.query.redirect)
})

Fenêtre de la barre de recherche

Afin de permettre aux utilisateurs de rechercher des produits à partir de n'importe quelle page, j'ai ajouté la barre de recherche dans une lightbox.

Search type

First check what search type the user selected.

import wixWindowFrontend from 'wix-window-frontend';

$w('#imageX7').hide()
$w('#button2,#input1,#searchByImage,#searchByAI').enable()
const type = wixWindowFrontend.lightbox.getContext()

let instructions = ""
switch (type) {
case "text":
    $w('#searchByTextMobile').show("fade", { duration: 500 })
    instructions = "Enter keywords or product names into the search bar."
    break;
case "image":
    $w('#searchByImage').show("fade", { duration: 500 })
    instructions = "Upload an image of the product you are looking for."
    break;
case "AI":
    $w('#searchByAI').show("fade", { duration: 500 })
    instructions = "Type your query in natural language (e.g., 'I need a winter jacket for women')."
    break;
}

$w('#text21').text = `Search by ${type}`
$w('#text22').text = instructions

Handle search type

By image

import { getImageLabels } from "backend/googleVision.web"

$w('#searchByImage').onChange(async () => {
    $w('#searchByImage').disable()
    $w('#imageX7').show()
    const file = await $w('#searchByImage').uploadFiles()
    const labels = await getImageLabels(file[0].fileUrl)

    sendTo("tags", labels)
})

First, the image is uploaded to the Media Manager to obtain its URL. Then, the URL is sent to Google Vision to identify tags related to the image.

By AI

$w('#searchByAI').onKeyPress(async (rest) => {
    if ($w('#searchByAI').valid && rest.key === "Enter") {
        $w('#searchByAI').disable()
        $w('#imageX7').show()

        const labels = await getTextLabels($w('#searchByAI').value)
        sendTo("tags", labels)
    }
})

If the user presses Enter, the input text is tokenized to extract nouns.

Search redirection

Once one of the methods is completed, the user is redirected to the search router page with the queries included in the URL.

import wixLocationFrontend from 'wix-location-frontend';

function sendTo(type = "", value = []) {
    wixLocationFrontend.to(`/search?type=${type}&value=${JSON.stringify(value)}`)
}

Search page

This is a Wix Router page that helped me show the different types of search, and also the wishlist of the products the user saved.

Repeater display

import wixWindowFrontend from 'wix-window-frontend';
import { cart } from "wix-stores-frontend";

let originalItem = wixWindowFrontend.getRouterData()
let items = wixWindowFrontend.getRouterData()
initRepeater(items)

$w('#repeater1').onItemReady(async ($item, itemData, index) => {
    $item('#imageX9').src = itemData.imageUrl[0].src
    $item('#text12').text = itemData.name
    $item('#text13').text = "$" + itemData.price
    $item('#ratingsDisplay1').rating = itemData.rating

    $item('#button1').onClick(async () => {
        const product = [{
            productId: itemData._id,
            quantity: 1,
        }]

        await cart.addProducts(product)
        await showAlert("add")
    })

    $item('#box38').onClick(() => {
        wixLocationFrontend.to(`/product/${itemData._id}`)
    })

    if (authentication.loggedIn()) {
        const userID = await currentMember.getMember({ fieldsets: ["FULL"] })
        const item = await wixData.query("Wishlist").eq("product", itemData._id).eq("_owner", userID._id).find()
        if (item.totalCount === 0) $item('#button2').expand()
        else {
            itemData.wishList = item.items[0]._id
            $item('#button3').expand()
        }
    } else $item('#button2').expand()

    $item('#button2').onClick(async () => {
        if (authentication.loggedIn()) {
            const itemWishlist = await wixData.insert("Wishlist", { product: itemData._id })
            itemData.wishList = itemWishlist._id
            await showAlert("save")
            $item('#button2').collapse().then(() => {
                $item('#button3').expand()
            })
        } else wixWindowFrontend.openLightbox("loginToWishlist")
    })

    $item('#button3').onClick(async () => {
        await wixData.remove("Wishlist", itemData.wishList)
        await showAlert("remove")
        $item('#button3').collapse().then(() => {
            $item('#button2').expand()
        })
    })
})

function initRepeater(items = [], changeFilter = true) {
    $w('#repeater1').data = items

    if ($w('#repeater1').data.length === 0) {
        if (wixLocationFrontend.query.type === "wishlist") {
            $w('#text27').text = "You haven't add any item to your wishlist. Add your first product!"
        } else {
            $w('#text27').text = "Try searching with another method or include more details if you are using AI."
        }
        $w('#section5').collapse()
        $w('#section6').expand()
    } else {
        const htmlLinks = items[0].tags.slice(0, 5).map(word => `<span style="font-size: 10px;"><a href="/search?type=tags&value=[%22${word}%22]">${word}</a></span>`).join(' - ');
        $w('#text22').html = htmlLinks
        $w('#section6').collapse()
        $w('#section5').expand()

        $w('#switch1').checked = false

        if (changeFilter) {
            const maxPrice = sortByPrice(items, "desc")[0]

            $w('#slider1').max = maxPrice.price
            $w('#slider2').max = maxPrice.price

            $w('#slider2').value = maxPrice.price
        }

    }

}

As you can see, this includes the import { cart } from "wix-stores-frontend" statement, which allows the product to be added to the cart when the user clicks the button inside the repeater.

Sort products

$w('#switch1').onChange(() => {
    $w('#repeater1').data = []

    items = sortByPrice(items, $w('#switch1').checked ? "desc" : "asc")
    $w('#repeater1').data = items
})

function sortByPrice(products = [{}], order = 'asc') {
    return products.sort((a, b) => {
        if (order === 'asc') {
            return a.price - b.price;
        } else if (order === 'desc') {
            return b.price - a.price;
        } else {
            throw new Error("Order must be either 'asc' or 'desc'");
        }
    });
}

Filter products

$w('#slider1,#slider2').onChange(() => {
    items = filterByPriceRange(originalItem, $w('#slider1').value, $w('#slider2').value)
    initRepeater(items, false)
})

function filterByPriceRange(products, minPrice, maxPrice) {
    return products.filter(product => product.price >= minPrice && product.price <= maxPrice);
}

Wixlocation.onChange()

Since searching for new products redirects the user to the same page /search and only changes the URL queries, I needed to handle this with wixLocationFrontend.onChange() to update the repeater items.

Product page

This is also a router page /product/${productID} that fetches product information from the backend and displays it using wixWindowFrontend.getRouterData().

import wixWindowFrontend from 'wix-window-frontend';

const product = wixWindowFrontend.getRouterData()

$w('#gallery1').items = product.imageUrl
$w('#features').text = product.features
$w('#title').text = product.name
$w('#brand').text = product.brand
$w('#color').text = product.color
$w('#price').text = String(product.price)
$w('#description').text = product.description
$w('#ratings').rating = product.rating
$w('#stock').text = `${product.stock} available`

Since you can add the product to your cart from here, with the option to set a quantity, I used the following code to achieve this.

import { cart } from "wix-stores-frontend"

$w('#addCart').onClick(async () => {
    const productToSave = [{
        productId: product._id,
        quantity: Number($w('#inputQuantity').value),
    }]

    await cart.addProducts(productToSave)
    await showAlert("add")

})

$w('#quantityLess,#quantityMore').onClick((rest) => {
    let value = Number($w('#inputQuantity').value)

    switch (rest.target) {
    case $w('#quantityLess'):
        if (value > 1) value--
        break;
    case $w('#quantityMore'):
        value++
        break;
    }

    $w('#inputQuantity').value = String(value)
})

Also having the option to add the product to the wishlist or delete it.

$w('#addWishList').onClick(async () => {
    if (authentication.loggedIn()) {
        await wixData.insert("Wishlist", { product: product._id })
        await showAlert("save")
        $w('#addWishList').collapse().then(() => {
            $w('#deleteWishlist').expand()
        })
    } else wixWindowFrontend.openLightbox("loginToWishlist")
})

$w('#deleteWishlist').onClick(async () => {
    await wixData.remove("Wishlist", product.wishList)
    await showAlert("remove")

    $w('#deleteWishlist').collapse().then(() => {
        $w('#addWishList').expand()
    })
})

Cart page

Need to review your cart? This page allows you to check if you have added the desired quantities of the products you want to buy and also lets you delete any items if needed.

import { cart } from "wix-stores-frontend"

$w('#repeater1').onItemReady(($item, itemData, index) => {
$item('#quantityLess,#quantityMore').onClick(async (rest) => {
        $item('#quantityLess,#quantityMore,#button3').disable()
        let value = Number($item('#inputQuantity').value)

        switch (rest.target) {
        case $item('#quantityLess'):
            if (value > 1) value--
            break;
        case $item('#quantityMore'):
            value++
            break;
        }

        $item('#inputQuantity').value = String(value)
        $item('#text22').text = `$${(itemData.price * value).toFixed(2)}`

        const updadedCart = await cart.updateLineItemQuantity(Number(itemData._id), value)
        $item('#quantityLess,#quantityMore,#button3').enable()

        $w('#text25').text = `Total: $${updadedCart.totals.total.toFixed(2)}`
    })

    $item('#button3').onClick(async () => {
        $item('#quantityLess,#quantityMore,#button3').disable()
        await cart.removeProduct(Number(itemData._id))
        await initCart()
    })
})

Clicking the buttons #quantityLess and #quantityMore triggers an event to edit the product quantity. Clicking the #button3 removes the product from the cart.

Checkout

This is the final step for purchasing at Mega Market.
If the user is a Site Member, it retrieves their saved addresses and accumulated cashback from their account.

import { authentication, currentMember } from "wix-members-frontend"

if (authentication.loggedIn()) {
    $w('#text25').collapse()

    const user = await currentMember.getMember({ fieldsets: ["FULL"] })

    $w('#input1').value = user.contactDetails.firstName
    $w('#input2').value = user.contactDetails.lastName
    $w('#input3').value = user.loginEmail
    $w('#input4').value = user.contactDetails.phones[0]

    const addresses = await getUserAdderess(user._id)

    if (addresses.length === 0) {
        $w('#dropdown1').options = [{ value: "No addresses founded", label: "No addresses founded" }]
        $w('#dropdown1').value = "No addresses founded"
        $w('#dropdown1').disable()
    } else {
        const addressesOptions = addresses.map(address => ({
            label: `${address.addressLine} ${address.state}, ${address.country}`,
            value: JSON.stringify(address)
        }));

        $w('#dropdown1').options = addressesOptions
    }

    const totalCashback = await getTotalCashback(user._id)

    if (totalCashback === 0) {
        $w('#switch1').disable()
        $w('#switch1').checked = false
        $w('#text28').text = `You will earn $${Number(currentCart.totals.total * 0.01).toFixed(2)} in cashback with this order.`
    } else {
        cashback = totalCashback > currentCart.totals.total ? currentCart.totals.total : totalCashback;
        $w('#switch1').enable()
        $w('#text28').text = `Use my cashback: $${Number(cashback).toFixed(2)}`
    }

}

It also validates that the card number of the user is a Master Card, Visa or AMEX.

$w('#input10').onCustomValidation((value, reject) => {
        const cardsInfo = {
            "Unknown": {
                "image": "https://static.wixstatic.com/media/4c0a11_600a3b93f607463296a7c9149268800e~mv2.gif",
                "regex": /^\d+$/,
                "length": 16
            },
            "Master Credit": {
                "image": "https://www.mercadopago.com/org-img/MP3/API/logos/master.gif",
                "regex": ,
                "length": 16
            },
            "Visa Credit": {
                "image": "https://www.mercadopago.com/org-img/MP3/API/logos/visa.gif",
                "regex": ,
                "length": 16
            },
            "AMEX": {
                "image": "https://www.mercadopago.com/org-img/MP3/API/logos/amex.gif",
                "regex": ,
                "length": 15
            },
            "Visa Debit": {
                "image": "https://www.mercadopago.com/org-img/MP3/API/logos/debvisa.gif",
                "regex": ,
                "length": 16
            },
            "Master Debit": {
                "image": "https://www.mercadopago.com/org-img/MP3/API/logos/debmaster.gif",
                "regex": ,
                "length": 16
            }
        }

        const cardCategory = identifyCardCategory(value)
        $w('#imageX8').src = cardsInfo[cardCategory].image
        $w('#input10').maxLength = cardsInfo[cardCategory].length
        if (!cardsInfo[cardCategory].regex.test(value)) {
            reject("Invalid card number")
        }
    })

function identifyCardCategory(cardNumber) {
    const patterns = {
        masterCredit: ,
        visaCredit: ,
        amex: ,
        visaDebit: ,
        masterDebit: 
    };

    if (patterns.masterCredit.test(cardNumber)) return "Master Credit";
    if (patterns.visaCredit.test(cardNumber)) return "Visa Credit";
    if (patterns.amex.test(cardNumber)) return "AMEX";
    if (patterns.visaDebit.test(cardNumber)) return "Visa Debit";
    if (patterns.masterDebit.test(cardNumber)) return "Master Debit";

    return "Unknown";
}

After the user inputs all the required information, it's time to process the order.

$w('#button2').onClick(async () => {
    if ($w('#input1').valid && $w('#input2').valid && $w('#input3').valid && $w('#input4').valid && $w('#input5').valid && $w('#input6').valid && $w('#input7').valid && $w('#input8').valid && $w('#input9').valid && $w('#input10').valid && $w('#dropdown2').valid && $w('#dropdown3').valid && $w('#dropdown4').valid) {
        $w('TextInput,Dropdown').disable()
        $w('#box53').show()

        const productArray = currentCart.lineItems.map(item => ({
            quantity: item.quantity,
            name: item.name,
            priceData: {
                price: item.price,
            },
            mediaItem: {
                src: item.mediaItem.src
            }
        }));

        const insertedItem = await wixData.insert("Orders", {
            lineItems: productArray,
            totals: {
                subtotal: subTotal,
                total: total,
            },
            shippingInfo: {
                shipmentDetails: {
                    address: {
                        city: $w('#dropdown4').value,
                        country: $w('#dropdown2').value,
                        addressLine: $w('#input5').value,
                        postalCode: $w('#input6').value,
                        subdivision: $w('#dropdown3').value,
                    }
                }
            },
            billingInfo: {
                address: {
                    city: $w('#dropdown4').value,
                    country: $w('#dropdown2').value,
                    addressLine: $w('#input5').value,
                    postalCode: $w('#input6').value,
                    subdivision: $w('#dropdown3').value,
                },
                lastName: $w('#input2').value,
                firstName: $w('#input1').value,
                email: $w('#input3').value,
                phone: $w('#input4').value
            },
            cashback: $w('#switch1').checked
        })

        for (let lineItem of currentCart.lineItems) {
            await cart.removeProduct(lineItem.id)
        }

        wixLocationFrontend.to(`/order/${insertedItem._id}`)
    }
})

This wixData.insert() triggers a data hook that creates the order using wixStoresBackend.createOrder() which I'll talk of it later.

Order page

This page displays the order information passed though the wixWindowFrontend.getRouterData().

import wixWindowFrontend from 'wix-window-frontend';

const orderInfo = wixWindowFrontend.getRouterData()
const contact = orderInfo.billingInfo
const shipping = contact.address

$w('#text21').text = `Order #${orderInfo.orderNumber}`
$w('#text48').text = `Amount paid: $${orderInfo.totals.total}`
$w('#text47').text = `Subtotal: $${Number(orderInfo.totals.subtotal).toFixed(2)} | Discounts: $${Number(orderInfo.totals.subtotal - orderInfo.totals.total).toFixed(2)}`

$w('#text26').text = contact.firstName
$w('#text27').text = contact.lastName
$w('#text28').text = contact.phone
$w('#text29').text = contact.email

$w('#text30').text = shipping.addressLine
$w('#text31').text = shipping.city
$w('#text32').text = shipping.postalCode
$w('#text33').text = `${shipping.subdivision}, ${shipping.country}`

APIs

Velo APIs

API Method Location in Project
Wix Data 1. query() 2. insert() 3. remove() 4. get() 1. Home page 2. Search page 3. Checkout
Wix Window Frontend 1. formFactor 2. lightbox.getContext() 3. getRouterData() 4. openLightbox() 1. Home page 2. Login 3. Search bar 4. Search page 5. Product page
Wix Location Frontend 1. to() 2. onChange() 3. query 1. Home page 2. Login 3. Search bar 4. Search page 5. Cart page 6. Checkout 7. Order page
{authentication} from Wix Members 1. applySessionToken() 2. onLogin() 1. Login 2. Checkout
{ cart } from Wix Stores 1. addProducts() 1. Search page 2. Product page 3. Cart page 4. Checkout
{currentMember} from Wix Members 1. getMember() 1. Product page 2. Checkout
Wix Secrets getSecret() Backend

External services

country-state-city

Helped to get the countries, states of countries and cities of states.

import { Permissions, webMethod } from "wix-web-module";

export const getAllCountries = webMethod(
    Permissions.Anyone,
    async () => {
        const countries = await require('country-state-city').Country.getAllCountries();

        const toReturn = countries.map(state => ({
            label: state.name,
            value: state.isoCode
        }));

        return toReturn;
    }
);

export const getStatesOfCountry = webMethod(
    Permissions.Anyone,
    async (country) => {
        const states = await require('country-state-city').State.getStatesOfCountry(country);

        const toReturn = states.map(state => ({
            label: state.name,
            value: state.isoCode
        }));

        return toReturn;
    }
);

export const getCitiesOfStates = webMethod(
    Permissions.Anyone,
    async (country, state) => {
        const states = await require('country-state-city').City.getCitiesOfState(country, state);

        const toReturn = states.map(state => ({
            label: state.name,
            value: state.name
        }));

        return toReturn;
    }
);

google-cloud/vision

Reads the image and provides labels of what it identifies.

const vision = require('@google-cloud/vision');

export const getImageLabels = webMethod(
    Permissions.Anyone,
    async (imageUrl) => {
        const credentials = JSON.parse(await wixSecretsBackend.getSecret("googleVisionCredentials"))

        const client = new vision.ImageAnnotatorClient({ credentials });

        try {
            const [result] = await client.labelDetection(getPublicURL(imageUrl));
            const labels = result.labelAnnotations.map(label => label.description);

            const lowerCaseLabels = labels.map(element => element.toLowerCase());

            return lowerCaseLabels;
        } catch (error) {
            console.error('Error detecting labels:', error);
            return []
        }
    }
);

Compromise

Extracts the nouns from a text.

const nlp = require('compromise');

export const getTextLabels = webMethod(
    Permissions.Anyone,
    async (text) => {
        let doc = nlp(text);

        let categories = doc.match('#Noun').out('array');
        const lowerCaseCategories = categories.map(element => element.toLowerCase());

        return lowerCaseCategories
    }
);

Twilio

Used for sending the verification code via SMS, call or email and to verify the one-time digit code.

export const sendVerification = webMethod(
    Permissions.Anyone,
    async (type =  "email" || "sms" || "call", to = "diego_deap@outlook.com") => {
        try {
            const { accountSid, authToken, service } = await auth()

            const client = twilio(accountSid, authToken);

            await client.verify.v2
                .services(service)
                .verifications.create({
                    channel: type,
                    to: to,
                });

            return true
        } catch (error) {
            return false
        }
    }
);

export const verify = webMethod(
    Permissions.Anyone,
    async (to = "", code = "") => {
        const { accountSid, authToken, service } = await auth()

        const client = twilio(accountSid, authToken);

        const verificationCheck = await client.verify.v2
            .services(service)
            .verificationChecks.create({
                code: code,
                to: to,
            });
        const status = verificationCheck.status === "approved" ? true : false;

        return status
    }
);

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn