Passing Function between components Vue - javascript

I'm currently trying to pass the getUser function from a file called Login.vue which gets the login details to an Authenticated.vue page to display the user and their email. How can I go about doing this. I've tried following the official vue documents but can't get it to work. Not sure whats going wrong.
Login.Vue
<template>
<form #submit.prevent="submitLogin">
<div>
<!-- Email -->
<div>
<label for="email" class="block font-medium text-sm text-gray-700">
Email
</label>
<input v-model="loginForm.email" id="email" type="email" class="block mt-1 w-full rounded-md shadow-sm border-gray-300 focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50" required autofocus autocomplete="username">
<!-- Validation Errors -->
<div class="text-red-600 mt-1">
<div v-for="message in validationErrors?.email">
{{ message }}
</div>
</div>
</div>
<!-- Password -->
<div class="mt-4">
<label for="password" class="block font-medium text-sm text-gray-700">
Password
</label>
<input v-model="loginForm.password" id="password" type="password" class="block mt-1 w-full rounded-md shadow-sm border-gray-300 focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50" required autocomplete="current-password">
<!-- Validation Errors -->
<div class="text-red-600 mt-1">
<div v-for="message in validationErrors?.password">
{{ message }}
</div>
</div>
</div>
<!-- Remember me -->
<div class="block mt-4">
<label class="flex items-center">
<input type="checkbox" name="remember" v-model="loginForm.remember" class="rounded border-gray-300 text-indigo-600 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50" />
<span class="ml-2 text-sm text-gray-600">Remember me</span>
</label>
</div>
<!-- Buttons -->
<div class="flex items-center justify-end mt-4">
<button class="inline-flex items-center px-4 py-2 bg-gray-800 border border-transparent rounded-md font-semibold text-xs text-white uppercase tracking-widest hover:bg-gray-700 active:bg-gray-900 focus:outline-none focus:border-gray-900 focus:shadow-outline-gray transition ease-in-out duration-150 ml-4" :class="{ 'opacity-25': processing }" :disabled="processing">
Log in
</button>
</div>
</div>
</form>
</template>
<script>
import { useRouter} from 'vue-router';
import { reactive } from '#vue/runtime-core'
export default {
data() {
return {
categories: {},
router: useRouter(),
loginForm: reactive({
email: '',
password: '',
remember: false
}),
user: reactive({
name: '',
email: '',
}),
processing: false,
validationErrors: {},
}
},
mounted() {
},
methods: {
submitLogin() {
if (this.processing) return
this.processing = true;
axios.post('/login', this.loginForm)
.then(response => this.loginUser(response))
.catch(error => {
if (error.response?.data) {
this.validationErrors = error.response.data.errors
}
})
.finally(this.processing = false)
},
loginUser(response){
this.user.name = response.data.name
this.user.email = response.data.email
localStorage.setItem('loggedIn', JSON.stringify(true))
this.router.push({name: 'posts.index'})
},
getUser(){
axios.get('/api/user')
.then(response => {
loginUser(response)
})
this.user.name = response.data.name
this.user.email = response.data.email
console.log(this.user.name);
console.log(this.user.email);
},
},
}
</script>
Authenticated.vue
<template>
<div class="min-h-screen bg-gray-100">
<nav class="bg-white border-b border-gray-100">
<!-- Primary Navigation Menu -->
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex justify-between h-16">
<div class="flex">
<!-- Logo -->
<div class="shrink-0 flex items-center">
<a href="/">
<svg viewBox="0 0 316 316" xmlns="http://www.w3.org/2000/svg" class="block h-10 w-auto fill-current text-gray-600">
<path d="M305.8 81.125C305.77 80.995 305.69 80.885 305.65 80.755C305.56 80.525 305.49 80.285 305.37 80.075C305.29 79.935 305.17 79.815 305.07 79.685C304.94 79.515 304.83 79.325 304.68 79.175C304.55 79.045 304.39 78.955 304.25 78.845C304.09 78.715 303.95 78.575 303.77 78.475L251.32 48.275C249.97 47.495 248.31 47.495 246.96 48.275L194.51 78.475C194.33 78.575 194.19 78.725 194.03 78.845C193.89 78.955 193.73 79.045 193.6 79.175C193.45 79.325 193.34 79.515 193.21 79.685C193.11 79.815 192.99 79.935 192.91 80.075C192.79 80.285 192.71 80.525 192.63 80.755C192.58 80.875 192.51 80.995 192.48 81.125C192.38 81.495 192.33 81.875 192.33 82.265V139.625L148.62 164.795V52.575C148.62 52.185 148.57 51.805 148.47 51.435C148.44 51.305 148.36 51.195 148.32 51.065C148.23 50.835 148.16 50.595 148.04 50.385C147.96 50.245 147.84 50.125 147.74 49.995C147.61 49.825 147.5 49.635 147.35 49.485C147.22 49.355 147.06 49.265 146.92 49.155C146.76 49.025 146.62 48.885 146.44 48.785L93.99 18.585C92.64 17.805 90.98 17.805 89.63 18.585L37.18 48.785C37 48.885 36.86 49.035 36.7 49.155C36.56 49.265 36.4 49.355 36.27 49.485C36.12 49.635 36.01 49.825 35.88 49.995C35.78 50.125 35.66 50.245 35.58 50.385C35.46 50.595 35.38 50.835 35.3 51.065C35.25 51.185 35.18 51.305 35.15 51.435C35.05 51.805 35 52.185 35 52.575V232.235C35 233.795 35.84 235.245 37.19 236.025L142.1 296.425C142.33 296.555 142.58 296.635 142.82 296.725C142.93 296.765 143.04 296.835 143.16 296.865C143.53 296.965 143.9 297.015 144.28 297.015C144.66 297.015 145.03 296.965 145.4 296.865C145.5 296.835 145.59 296.775 145.69 296.745C145.95 296.655 146.21 296.565 146.45 296.435L251.36 236.035C252.72 235.255 253.55 233.815 253.55 232.245V174.885L303.81 145.945C305.17 145.165 306 143.725 306 142.155V82.265C305.95 81.875 305.89 81.495 305.8 81.125ZM144.2 227.205L100.57 202.515L146.39 176.135L196.66 147.195L240.33 172.335L208.29 190.625L144.2 227.205ZM244.75 114.995V164.795L226.39 154.225L201.03 139.625V89.825L219.39 100.395L244.75 114.995ZM249.12 57.105L292.81 82.265L249.12 107.425L205.43 82.265L249.12 57.105ZM114.49 184.425L96.13 194.995V85.305L121.49 70.705L139.85 60.135V169.815L114.49 184.425ZM91.76 27.425L135.45 52.585L91.76 77.745L48.07 52.585L91.76 27.425ZM43.67 60.135L62.03 70.705L87.39 85.305V202.545V202.555V202.565C87.39 202.735 87.44 202.895 87.46 203.055C87.49 203.265 87.49 203.485 87.55 203.695V203.705C87.6 203.875 87.69 204.035 87.76 204.195C87.84 204.375 87.89 204.575 87.99 204.745C87.99 204.745 87.99 204.755 88 204.755C88.09 204.905 88.22 205.035 88.33 205.175C88.45 205.335 88.55 205.495 88.69 205.635L88.7 205.645C88.82 205.765 88.98 205.855 89.12 205.965C89.28 206.085 89.42 206.225 89.59 206.325C89.6 206.325 89.6 206.325 89.61 206.335C89.62 206.335 89.62 206.345 89.63 206.345L139.87 234.775V285.065L43.67 229.705V60.135ZM244.75 229.705L148.58 285.075V234.775L219.8 194.115L244.75 179.875V229.705ZM297.2 139.625L253.49 164.795V114.995L278.85 100.395L297.21 89.825V139.625H297.2Z"/>
</svg>
</a>
</div>
<!-- Navigation Links -->
<div class="hidden space-x-8 sm:-my-px sm:ml-10 sm:flex">
<router-link :to="{name:'posts.index'}" active-class="border-b-2 border-indigo-400" class="inline-flex items-center px-1 pt-1 text-sm font-medium leading-5 text-gray-900 focus:outline-none focus:border-indigo-700 transition duration-150 ease-in-out">
Posts
</router-link>
<router-link :to="{name:'posts.create'}" active-class="border-b-2 border-indigo-400" class="inline-flex items-center px-1 pt-1 text-sm font-medium leading-5 text-gray-900 focus:outline-none focus:border-indigo-700 transition duration-150 ease-in-out">
Create Post
</router-link>
</div>
</div>
<div class="flex items-center">
<div>
<div>Hi, {{user.name}}</div>
<div class="text-sm text-gray-500"> {{user.email}} </div>
</div>
</div>
<button #click="logout" type="button" class="inline-flex items-center px-4 py-2 bg-gray-800 border border-transparent rounded-md font-semibold text-xs text-white uppercase tracking-widest hover:bg-gray-700 active:bg-gray-900 focus:outline-none focus:border-gray-900 focus:shadow-outline-gray transition ease-in-out duration-150 ml-4" :class="{ 'opacity-25': processing }" :disabled="processing">
Log out
</button>
</div>
</div>
</nav>
<!-- Page Heading -->
<header class="bg-white shadow">
<div class="max-w-7xl mx-auto py-6 px-4 sm:px-6 lg:px-8">
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
{{currentPageTitle}}
</h2>
</div>
</header>
<!-- Page Content -->
<main>
<div class="py-12">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
<div class="p-6 bg-white border-b border-gray-200">
<router-view></router-view>
</div>
</div>
</div>
</div>
</main>
</div>
</template>
<script>
import { reactive } from '#vue/runtime-core'
export default {
props: {
getUser: {type: Function}
},
computed: {
currentPageTitle(){
return this.$route.meta.title;
}
},
mounted() {
this.getUser()
},
methods: {
}
}
</script>
App.js
require('./bootstrap');
import { createApp, onMounted } from 'vue'
//import App from './layouts/App'
import LaravelVuePagination from 'laravel-vue-pagination';
import VueSweetalert2 from 'vue-sweetalert2';
import 'sweetalert2/dist/sweetalert2.min.css';
import router from './routes/index'
const app = createApp({
})
app.use(router)
app.use(VueSweetalert2)
app.component('Pagination', LaravelVuePagination)
app.mount('#app')

you should vuex for this perpose.
getUser(){
axios.get('/api/user')
.then(response => {
this.$store.commit('storeUser',response)
this.router.push({name: 'posts.index'})
})
},
in store/index.js (create folder of store in root )
import { createApp } from 'vue'
import { createStore } from 'vuex'
// Create a new store instance.
const store = createStore({
state () {
return {
userData:{}
}
},
getters: {
userData(state){
return state.userData
}
},
mutations: {
storeUser (state,payload) {
state.userData= payload
}
}
})
export default store
in main.js
import store from 'store/index.js'
const app = createApp({ /* your root component */ })
// Install the store instance as a plugin
app.use(store)
in Authenticated.vue (or whereevet you want to use any value from store)
computed:{
userData(){
return this.$store.getters.userData // here you can get your
response object from api
which you call during login
}
}

Related

Hydrate Nextjs React Redux Thunk

Problem: Error: Hydration failed because the initial UI does not match what was rendered on the server.
How can I iterate the cart without having to create another state :c
const initialState = {
cart: Cookies.get("cart") ? JSON.parse(Cookies.get("cart")) : [],
loading: null,
error: null,
};
import Layout from "../../Layout";
import { connect } from "react-redux";
import { removeFromCart, clearCart } from "../../redux/actions/cart";
function PasarelaPago({ loading, removeFromCart, clearCart, cart }) {
return (
<Layout>
<div className="px-2 py-10">
<h1 className="text-2xl text-gray-600 font-bold tracking-wider text-center">
Pasarela de pago
</h1>
<div className="w-full lg:w-1/2 mx-auto">
<h3 className="text-lg font-light tracking-widest text-gray-500 px-4 my-4">
Tu bolsa de compras
</h3>
<div className="flex justify-between px-3 text-xs text-gray-400 border-b pb-2">
<div>PRODUCTOS</div>
<div>CANTIDAD</div>
<div>TOTAL</div>
</div>
<div className="flex flex-col gap-3">
ERROR!!
{cart && cart.map((item) => <div>{item.name}</div>)}
</div>
</div>
</div>
</Layout>
);
}
const mapStateToProps = (state) => ({
loading: state.Cart.loading,
cart: state.Cart.cart,
});
export default connect(mapStateToProps, {
removeFromCart,
clearCart,
})(PasarelaPago);

How to set up Vue 3 parent component to emit event to child component

I am attempting to set up a button in a Vue 3 Tailwind parent component to trigger a HeadlessUI transition event in a child component. My goal is to enable the button in the parent to emit an event, while the child component "watches" for the event before triggering the transition event as part of the callback function in the watch. So far, I have the parent component set up to trigger the emit, while the child component is set up to watch for the "transition" event. However, the event is not being executed. I'm afraid I don't have the watch in the child component set up correctly, so as to watch for the button click in the parent component. How can I go about enabling the child component to watch for the click of the button in the parent component?
Here is my code so far:
Parent:
<!-- This example requires Tailwind CSS v2.0+ -->
<template>
<div class="min-h-full">
<Disclosure as="nav" class="bg-gray-800">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex items-center justify-between h-16">
<div class="flex items-center">
<div class="hidden md:block">
<div class="ml-10 flex items-baseline space-x-4">
<button type="button" #click="transition" class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800">Click to transition</button>
</div>
</div>
</div>
</div>
</div>
</Disclosure>
<main>
<div class="max-w-7xl mx-auto py-6 sm:px-6 lg:px-8">
<div class="px-4 py-6 sm:px-0">
<HelloWorld :event="transition" />
</div>
</div>
</main>
</div>
</template>
<script setup>
import { Disclosure, DisclosureButton, DisclosurePanel, Menu, MenuButton, MenuItem, MenuItems } from '#headlessui/vue'
import { BellIcon, MenuIcon, XIcon } from '#heroicons/vue/outline'
import HelloWorld from './components/HelloWorld.vue'
</script>
Child:
<template>
<div class="flex flex-col items-center py-16">
<div class="w-96 h-96">
<TransitionRoot
appear
:show="isShowing"
as="template"
enter="transform transition duration-[400ms]"
enter-from="opacity-0 rotate-[-120deg] scale-50"
enter-to="opacity-100 rotate-0 scale-100"
leave="transform duration-200 transition ease-in-out"
leave-from="opacity-100 rotate-0 scale-100 "
leave-to="opacity-0 scale-95 "
>
<div class="w-full h-full bg-gray-400 rounded-md shadow-lg" />
</TransitionRoot>
</div>
</div>
</template>
<script setup>
import { ref, toRefs, watch } from 'vue'
import { TransitionRoot } from '#headlessui/vue'
const props = defineProps({
transition: Function
})
const { transition } = toRefs(props)
const isShowing = ref(true)
watch(transition, () => {
isShowing.value = false
setTimeout(() => {
isShowing.value = true
}, 500)
})
</script>
events should go up and state should go down.
make your child component to watch a property and the button in parent should change the state of that property
update:
const { transition } = toRefs(props)
you might be losing reactivity here.
more info: https://stackoverflow.com/a/64926664/420096
update2:
the way you made it should work, but point directly to the prop is fine too:
https://codesandbox.io/s/relaxed-sea-y95x6c?file=/src/App.vue
Based on Sombriks' feedback, here is the answer:
Parent:
<!-- This example requires Tailwind CSS v2.0+ -->
<template>
<div class="min-h-full">
<Disclosure as="nav" class="bg-gray-800">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex items-center justify-between h-16">
<div class="flex items-center">
<div class="hidden md:block">
<div class="ml-10 flex items-baseline space-x-4">
<button type="button" #click="transition" class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800">Click to transition</button>
</div>
</div>
</div>
</div>
</div>
</Disclosure>
<main>
<div class="max-w-7xl mx-auto py-6 sm:px-6 lg:px-8">
<div class="px-4 py-6 sm:px-0">
<HelloWorld :show="show" />
</div>
</div>
</main>
</div>
</template>
<script setup>
import { Disclosure, DisclosureButton, DisclosurePanel, Menu, MenuButton, MenuItem, MenuItems } from '#headlessui/vue'
import { BellIcon, MenuIcon, XIcon } from '#heroicons/vue/outline'
import { ref } from 'vue'
import HelloWorld from './components/HelloWorld.vue'
const show = ref(true)
const transition = () => {
show.value = !show.value
}
</script>
Child:
<template>
<div class="flex flex-col items-center py-16">
<div class="w-96 h-96">
<TransitionRoot
appear
:show="isShowing"
as="template"
enter="transform transition duration-[400ms]"
enter-from="opacity-0 rotate-[-120deg] scale-50"
enter-to="opacity-100 rotate-0 scale-100"
leave="transform duration-200 transition ease-in-out"
leave-from="opacity-100 rotate-0 scale-100 "
leave-to="opacity-0 scale-95 "
>
<div class="w-full h-full bg-gray-400 rounded-md shadow-lg" />
</TransitionRoot>
</div>
</div>
</template>
<script setup>
import { ref, toRefs, watch } from 'vue'
import { TransitionRoot } from '#headlessui/vue'
const props = defineProps({
show: Boolean
})
const { show } = toRefs(props)
const isShowing = ref(true)
watch(show, () => {
isShowing.value = false
setTimeout(() => {
isShowing.value = true
}, 500)
})
</script>

Getting null when I use context.query with getServerSideProps() in Nextjs?

Intro
Basically, I am building an e-commerce website. But I got stuck in this problem while saving the order details after I click payment button (although I had not used any payment gateway).
Problem
After selecting the items in cart , I fill the details in Checkout page and go to pay option to confirm order. In this checkout page I call addorder api which first do some checks(like whether item got out of stock or not) on the product in cart and then create new order.
After creating a new order in order DB (MOngo DB I am using), I goto order page like this with my orderId
res.redirect('/order/' + orderToAdd.orderId, 200);
After clicking the pay button shown in the image above, it redirects to http://localhost:3000/order/814407650978 giving 404 (Not Found).
Thus when I fetch the order details from the MyOrder.js using context.query.orderId, from getServerSideProps() , I am getting null.
export async function getServerSideProps(context) {
if (!mongoose.connections[0].readyState) {
await mongoose.connect(process.env.MONGO_URI);
}
let order = await Order.findById({orderId:context.query.orderId})
console.log("got = ", order);
return {
props: { order: JSON.parse(JSON.stringify(order)) }, // will be passed to the page component as props
};
}
You can see full code below
Code
This is a complete order DB entry, where I am only sending the products object to MyOrder.js
complete order = {
email: 'mohit6#test.com',
orderId: '869972292107',
paymentInfo: 'No payment info',
products: {
'wear-the-chess-king-M-Red': {
qty: 2,
price: 599,
name: 'King Chess Stylish Men’s t-Shirt',
size: 'M',
variant: 'Red'
}
},
address: 'A-22\nVinayak campus',
subtotal: 1198,
status: 'Paid',
_id: new ObjectId("626bd1bd01597b226fc76531"),
createdAt: 2022-04-29T11:53:33.046Z,
updatedAt: 2022-04-29T11:53:33.046Z,
__v: 0
}
Checkout.js
import React, { useState } from "react";
import { AiFillPlusCircle, AiFillMinusCircle } from "react-icons/ai";
import { MdOutlinePayment } from "react-icons/md";
import { useRouter } from "next/router";
import "react-toastify/dist/ReactToastify.css";
import { toast, ToastContainer } from "react-toastify";
// Added payment AiOutlineGateway, but due to no merchant key, it is creating invalid checksum => hence push to different branch in both local & remote
const Checkout = ({ cart, clearCart, subtotal, addToCart, removeFromCart }) => {
const [name, setName] = useState("");
const [email, setEmail] = useState("");
const [phone, setPhone] = useState("");
const [address, setAddress] = useState("");
const [pincode, setPincode] = useState("");
const [city, setCity] = useState("");
const [statemap, setStatemap] = useState("");
const [disable, setDisable] = useState(true);
const router = useRouter();
const handleChange = async (e) => {
if (e.target.name === "name") {
setName(e.target.value);
} else if (e.target.name === "email") {
setEmail(e.target.value);
} else if (e.target.name === "phone") {
setPhone(e.target.value);
} else if (e.target.name === "address") {
setAddress(e.target.value);
} else if (e.target.name === "pincode") {
setPincode(e.target.value);
if (e.target.value.length == 6) {
let pins = await fetch(`${process.env.NEXT_PUBLIC_HOST}/api/pincode`);
let pinjson = await pins.json();
//console.log(pinjson)
if (Object.keys(pinjson).includes(e.target.value)) {
setCity(pinjson[e.target.value][0]);
setStatemap(pinjson[e.target.value][1]);
} else {
setCity("");
setStatemap("");
}
} else {
setCity("");
setStatemap("");
}
}
if (name && email && phone && address && pincode) setDisable(false);
else setDisable(true);
};
const initiateOrder = async () => {
let oid = Math.floor(Math.random() * Date.now());
const data = {
cart: cart,
subtotal: subtotal,
oid: oid,
email: email,
name: name,
address: address,
pincode: pincode,
phone: phone,
};
//console.log(JSON.stringify(data));
let res = await fetch(`${process.env.NEXT_PUBLIC_HOST}/api/addorder`, {
method: "POST", // or 'PUT'
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(data),
});
let response = await res.json();
//console.log("response from order- ", response);
if (response.success === true) {
localStorage.setItem("token", response.authToken);
toast.success("Order Added Successfully!", {
position: "bottom-center",
autoClose: 2000,
hideProgressBar: false,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
progress: undefined,
});
setTimeout(() => {
router.push("/order");
}, 2500);
} else {
if(response.error === "err1"){
toast.error("Total price of your cart have changed accidently", {
position: "bottom-center",
autoClose: 2000,
hideProgressBar: false,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
progress: undefined,
});
}
else if(response.error === "err2"){
toast.error("Some items in your cart went out of stock !", {
position: "bottom-center",
autoClose: 2000,
hideProgressBar: false,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
progress: undefined,
});
}
else if(response.error === "err5"){
toast.error("Prices of some of the items in your cart have changed !", {
position: "bottom-center",
autoClose: 2000,
hideProgressBar: false,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
progress: undefined,
});
}
else {
toast.error("Error in Adding Order !", {
position: "bottom-center",
autoClose: 2000,
hideProgressBar: false,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
progress: undefined,
});
}
}
};
return (
<>
<div className="container px-2 sm:m-auto">
<ToastContainer
position="bottom-center"
autoClose={2000}
hideProgressBar={false}
newestOnTop={false}
closeOnClick
rtl={false}
pauseOnFocusLoss
draggable
pauseOnHover
/>
<h1 className="text-xl md:text-3xl text-center my-8 font-semibold">
Checkout
</h1>
{/* part 1 */}
<h2 className="text-xl my-4 font-semibold">1. Delivery Details</h2>
<div className="mx-auto flex">
<div className="px-2 w-1/2">
<div className="mb-4">
<label htmlFor="name" className="leading-7 text-sm text-gray-600">
Name
</label>
<input
type="name"
id="name"
name="name"
value={name}
onChange={handleChange}
className="w-full bg-white rounded border border-gray-300 focus:border-blue-500 focus:ring-2 focus:ring-blue-200 text-base outline-none text-gray-700 py-1 px-3 leading-8 transition-colors duration-200 ease-in-out"
/>
</div>
</div>
<div className="px-2 w-1/2">
<div className=" mb-4">
<label
htmlFor="email"
className="leading-7 text-sm text-gray-600"
>
Email
</label>
<input
type="email"
id="email"
name="email"
value={email}
onChange={handleChange}
className="w-full bg-white rounded border border-gray-300 focus:border-blue-500 focus:ring-2 focus:ring-blue-200 text-base outline-none text-gray-700 py-1 px-3 leading-8 transition-colors duration-200 ease-in-out"
/>
</div>
</div>
</div>
<div className="px-2 w-full">
<div className=" mb-4">
<label
htmlFor="address"
className="leading-7 text-sm text-gray-600"
>
Address
</label>
<textarea
id="address"
name="address"
value={address}
onChange={handleChange}
className="w-full bg-white rounded border border-gray-300 focus:border-blue-500 focus:ring-2 focus:ring-blue-200 text-base outline-none text-gray-700 py-1 px-3 leading-8 transition-colors duration-200 ease-in-out"
/>
</div>
</div>
<div className="mx-auto flex">
<div className="px-2 w-1/2">
<div className="mb-4">
<label
htmlFor="phone"
className="leading-7 text-sm text-gray-600"
>
Phone
</label>
<input
type="text"
id="phone"
name="phone"
value={phone}
onChange={handleChange}
className="w-full bg-white rounded border border-gray-300 focus:border-blue-500 focus:ring-2 focus:ring-blue-200 text-base outline-none text-gray-700 py-1 px-3 leading-8 transition-colors duration-200 ease-in-out"
/>
</div>
</div>
{/* city */}
<div className="px-2 w-1/2">
<div className=" mb-4">
<label
htmlFor="pincode"
className="leading-7 text-sm text-gray-600"
>
Pincode
</label>
<input
type="text"
id="pincode"
name="pincode"
value={pincode}
onChange={handleChange}
className="w-full bg-white rounded border border-gray-300 focus:border-blue-500 focus:ring-2 focus:ring-blue-200 text-base outline-none text-gray-700 py-1 px-3 leading-8 transition-colors duration-200 ease-in-out"
/>
</div>
</div>
</div>
<div className="mx-auto flex">
<div className="px-2 w-1/2">
<div className="mb-4">
<label
htmlFor="state"
className="leading-7 text-sm text-gray-600"
>
State
</label>
<input
type="text"
id="state"
name="state"
value={statemap}
className="w-full bg-white rounded border border-gray-300 focus:border-blue-500 focus:ring-2 focus:ring-blue-200 text-base outline-none text-gray-700 py-1 px-3 leading-8 transition-colors duration-200 ease-in-out"
onChange={handleChange}
/>
</div>
</div>
<div className="px-2 w-1/2">
<div className=" mb-4">
<label htmlFor="city" className="leading-7 text-sm text-gray-600">
City
</label>
<input
type="text"
id="city"
name="city"
value={city}
className="w-full bg-white rounded border border-gray-300 focus:border-blue-500 focus:ring-2 focus:ring-blue-200 text-base outline-none text-gray-700 py-1 px-3 leading-8 transition-colors duration-200 ease-in-out"
onChange={handleChange}
/>
</div>
</div>
</div>
{/* part 2 */}
<h2 className="text-xl my-4 font-semibold">2. Review Cart Items</h2>
<div className="z-10 sideCart p-6 mx-2 my-4 bg-blue-100 border-[1px] border-blue-800 rounded">
<ol className="list-decimal font-semibold">
{Object.keys(cart).length === 0 && (
<div className="mt-5 text-center text-xl font-extralight">
Your Cart is Empty :(
</div>
)}
{
//k is cart {k: {name, price,qty,size,variant} }
Object.keys(cart).map((k) => {
return (
<li key={k}>
<div className="flex item my-5 items-center justify-between">
<div className=" font-semibold">
{cart[k].name} [{cart[k].variant} - {cart[k].size}]
</div>
<div className="w-1/3 font-semibold flex items-center justify-center">
<AiFillPlusCircle
onClick={() =>
addToCart(
k,
1,
cart[k].price,
cart[k].name,
cart[k].size,
cart[k].variant
)
}
className="text-blue-700 text-xl cursor-pointer"
/>
<span className="mx-2 text-sm">{cart[k].qty}</span>
<AiFillMinusCircle
onClick={() =>
removeFromCart(
k,
1,
cart[k].price,
cart[k].name,
cart[k].size,
cart[k].variant
)
}
className="text-blue-700 text-xl cursor-pointer"
/>
</div>
</div>
</li>
);
})
}
</ol>
<span className="subtotal text-xl font-extrabold">
Subtotal : ₹ {subtotal} /-
</span>
</div>
<button
disabled={disable}
onClick={initiateOrder}
className="disabled:bg-blue-300 flex text-white bg-blue-500 border-0 py-2 px-3 focus:outline-none hover:bg-blue-600 rounded text-base mx-2 my-4"
>
<MdOutlinePayment className="m-1" />
Pay ₹ {subtotal}
</button>
</div>
</>
);
};
export default Checkout;
/api/addorder.js
import connectDB from "../../middleware/mongoose";
import Order from "../../models/Order";
import Product from "../../models/Product";
const handler = async (req, res) => {
if (req.method === "POST") {
let product,
sumtotal = 0;
for (let item in req.body.cart) {
//got key(slug) as item
console.log(item);
sumtotal += req.body.cart[item].qty * req.body.cart[item].price;
product = await Product.findOne({ slug: item });
//TODO1: check if cart is tampered or not
if (product.price !== req.body.cart[item].price) {
res.status(500).json({ success: false, error: "err1" });
//console.log("ResT - ",res.json)
return;
}
//TODO2: check if cart items are out of stock
if (product.availableQty < req.body.cart[item].qty) {
res.status(500).json({ success: false, error: "err2" });
return;
}
}
//console.log("sumtotal = ",sumtotal," and subtotal = ",req.body.subtotal)
if (sumtotal != req.body.subtotal) {
res.status(500).json({ success: false, error: "err5" });
//console.log("ResP - ",res)
return;
}
//TODO3: check if details are valid or not
let orderToAdd = new Order({
email: req.body.email,
orderId: req.body.oid,
address: req.body.address,
subtotal: req.body.subtotal,
products: req.body.cart,
status: "Paid",
});
await orderToAdd.save();
let ordered_products = orderToAdd.products;
console.log("Orederd pro = ", ordered_products);
console.log("complete order = ", orderToAdd)
for (let slug in ordered_products) {
await Product.findOneAndUpdate(
{ slug: slug },
{ $inc: { availableQty: -ordered_products[slug].qty } }
);
}
res.redirect('/order/' + orderToAdd.orderId, 200)
} else {
// Handle any other HTTP method
res.status(400).json({ success: false });
}
};
export default connectDB(handler);
MyOrder.js
import React from "react";
import mongoose from "mongoose";
import Order from "../models/Order";
//TODO: To change the orderdetail fecthing resource from cart,subtotal to orders DB (user specific orders)
const MyOrder = ({ order }) => {
console.log("order = ",order)
const products = order.products;
console.log(order,products);
return (
<div>
<section className="text-gray-600 body-font overflow-hidden">
<div className="container px-5 py-24 mx-auto">
<div className="lg:w-4/5 mx-auto flex flex-wrap">
<div className="lg:w-1/2 w-full lg:pr-10 lg:py-6 mb-6 lg:mb-0">
<h2 className="text-sm title-font text-gray-500 tracking-widest">
CHESSWEAR.COM
</h2>
<h1 className="text-gray-900 text-3xl title-font font-medium mb-4">
Order Id: 2242434535
</h1>
<p className="leading-relaxed mb-4 text-green-700 rounded-3xl bg-green-100 hover:bg-green-100/70 p-3 border-[1px] border-green-700 inline-flex items-center justify-center ">
Your Order has been successfully placed !
</p>
<div className="flex mb-4 justify-evenly">
<a className="flex-grow w-12 py-2 text-lg px-1">Description</a>
<a className="flex-grow py-2 text-lg px-1">Quantity</a>
<a className="flex-grow py-2 text-lg px-1">Price</a>
</div>
{Object.keys(cart).map((key) => {
return (
<div key={key} className="flex border-t border-gray-200 py-2">
<span className="text-gray-500 w-28 ">
{cart[key].name} ({cart[key].size} - {cart[key].variant})
</span>
<span className="m-auto text-gray-900">
{cart[key].qty}
</span>
<span className="mr-auto mt-auto mb-auto text-gray-900">
₹ {cart[key].price}
</span>
</div>
);
})}
<div className="flex py-2 md:py-4">
<span className="title-font font-medium text-2xl text-gray-900">
Subtotal : <span className="ml-4 text-red-900 font-bold text-3xl leading-tight">₹ {subtotal} /-</span>
</span>
<button className="flex ml-auto text-white bg-blue-500 border-0 py-2 px-6 focus:outline-none hover:bg-blue-600 rounded">
Track Order
</button>
</div>
</div>
<img
alt="ecommerce"
className="lg:w-1/2 w-full lg:h-auto h-64 object-cover object-center rounded"
src="https://dummyimage.com/400x400"
/>
</div>
</div>
</section>
</div>
);
};
export async function getServerSideProps(context) {
if (!mongoose.connections[0].readyState) {
await mongoose.connect(process.env.MONGO_URI);
}
let order = await Order.findById({orderId:context.query.orderId})
console.log("got = ", order);
return {
props: { order: JSON.parse(JSON.stringify(order)) }, // will be passed to the page component as props
};
}
export default MyOrder;
File Structure
Please help me???

Toggle #headlessui/vue Popover component

I'm not entirely sure how to toggle the popover based on a boolean, in my case: if there are search results. I thought I could just use the open attribute. Any help is appreciated!
To sum up this component:
Type in a search term in the input
An API call is being made and results are returned from te API
The PopoverPanel should then be opened
<template>
<form :action="action" method="GET" class="flex-1">
<Popover :open="searchResults.length > 0" class="relative w-full">
<PopoverOverlay
:class="`bg-black ${open ? 'fixed inset-0 opacity-10' : 'opacity-0'}`"
#click="() => emptySearchResults()"
/>
<div class="relative">
<label for="search" class="sr-only">Search...</label>
<input
id="search"
v-model="searchTerm"
type="search"
placeholder="Search..."
class="h-10 w-full px-4 py-2 text-sm rounded-lg placeholder-gray-400 focus:outline-none"
>
</div>
<PopoverPanel class="absolute top-full right-0 w-full mt-3 py-2 px-2 bg-gray-700 rounded-lg shadow-lg">
<a
v-for="result in searchResults"
:key="result.id"
:href="result.url"
class="flex items-center font-semibold p-2 text-sm text-white leading-none rounded-lg hover:bg-gray-600"
>
<span class="mr-3 py-1 px-2 text-xs text-gray-900 leading-none bg-gray-300 rounded-full">
{{ result.module }}
</span>
{{ result.title }}
</a>
</PopoverPanel>
</Popover>
</form>
</template>
<script setup>
import { ref, watch } from 'vue';
import { Popover, PopoverPanel } from '#headlessui/vue';
import debounce from '#/Helpers/debounce';
import xhr from '#/Helpers/xhr';
const searchTerm = ref('');
const searchResults = ref([]);
const getSearchResults = debounce(async (value) => {
if (value === '') return;
const { data } = await xhr.post('search', { q: value });
const { results } = await data;
searchResults.value = results;
}, 250);
const emptySearchResults = () => {
searchTerm.value = '';
searchResults.value = [];
};
watch(searchTerm, (value) => getSearchResults(value));
</script>

Why my redux state is go back to intial value on Page Refresh?

After SignIn, my authState becomes true, which is expected. and then It redirected to the home page. But after that If I refresh the page, my state goes back to initial value. what could be the reason of this?
Login Component:
import React, { useState, useEffect } from 'react';
import { toast } from 'react-toastify';
import axios from 'axios';
import { Redirect } from 'react-router-dom';
import { useDispatch, useSelector } from 'react-redux';
import { setCurrentUser } from 'src/store/reducer/authReducer';
import jwtDecode from 'jwt-decode';
import { authenticate, isAuth } from '../helpers/auth';
import Protection from '../assets/Protection.svg';
import useInputState from '../hooks/useInputState';
const Login = (props) => {
const auth = useSelector((state) => state.auth);
const [submitted, setSubmitted] = useState(false);
const [btnText, setBtnText] = useState('Sign In');
const dispatch = useDispatch();
const { location } = props;
const initialValue = { name: '', email: '', password: '' };
const [state, onChangeHandler, reset] = useInputState(initialValue);
const { email, password } = state;
// submit data to backend
const handleSubmit = async (e) => {
e.preventDefault();
try {
const res = await axios.post(`${process.env.API_URL}/api/signin`, {
email,
password,
});
reset();
setSubmitted(true);
setBtnText('Submitted');
const { token: jwtToken } = res.data;
localStorage.setItem('jwtToken', jwtToken);
const decoded = jwtDecode(jwtToken);
dispatch(setCurrentUser(decoded));
} catch (err) {
const { errors } = err.response.data;
for (const error of errors) {
const msg = Object.values(error).toString();
toast.error(msg);
}
}
};
useEffect(() => {
if (location.message) {
toast.success(location.message);
}
}, [location.message]);
if (submitted && auth.isAuthenticated) {
return <Redirect to={{ pathname: '/', message: 'You are signed in' }} />;
}
return (
<div className="min-h-screen bg-gray-100 text-gray-900 flex justify-center">
<div className="max-w-screen-xl m-0 sm:m-20 bg-white shadow sm:rounded-lg flex justify-center flex-1">
<div className="lg:w-1/2 xl:w-5/12 p-6 sm:p-12">
<div className="mt-12 flex flex-col items-center">
<h1 className="text-2xl xl:text-3xl font-extrabold">
Sign In for MernAuth
</h1>
<form
className="w-full flex-1 mt-8 text-indigo-500"
onSubmit={handleSubmit}
>
<div className="mx-auto max-w-xs relative">
<input
className="w-full px-8 py-4 rounded-lg font-medium bg-gray-100 border-gray-200 placeholder-gray-500 text-sm focus:outline-none focus:border-gray-400 focus:bg-white mt-5"
type="email"
name="email"
placeholder="Email"
onChange={onChangeHandler}
value={email}
/>
<input
className="w-full px-8 py-4 rounded-lg font-medium bg-gray-100 border-gray-200 placeholder-gray-500 text-sm focus:outline-none focus:border-gray-400 focus:bg-white mt-5"
type="password"
name="password"
placeholder="Password"
onChange={onChangeHandler}
value={password}
/>
<button
className="mt-5 tracking-wide font-semibold bg-indigo-500 text-gray-100 w-full py-4 rounded-lg hover:bg-indigo-700 transition-all duration-300 ease-in-out flex items-center justify-center focus:shadow-outline focus:outline-none border-none outline-none"
type="submit"
>
<i className="fas fa-user-plus fa 1x w-6 -ml-2" />
<span className="ml-3">{btnText}</span>
</button>
</div>
<div className="my-12 border-b text-center">
<div className="leading-node px-2 inline-block text-sm text-gray-600 tracking-wide font-medium bg-white transform tranlate-y-1/2">
Or sign in with email or social login
</div>
</div>
<div className="flex flex-col items-center">
<a
className="w-full max-w-xs font-bold shadow-sm rounded-lg py-3
bg-indigo-100 text-gray-800 flex items-center justify-center transition-all duration-300 ease-in-out focus:outline-none hover:shadow focus:shadow-sm focus:shadow-outline mt-5"
href="/login"
target="_self"
>
<i className="fas fa-sign-in-alt fa 1x w-6 -ml-2 text-indigo-500" />
<span className="ml-4">Sign Up</span>
</a>
</div>
</form>
</div>
</div>
<div className="flex-1 bg-indigo-100 text-center hidden lg:flex">
<div
className="m-12 xl:m-16 w-full bg-contain bg-center bg-no-repeat"
style={{ backgroundImage: `url(${Protection})` }}
/>
</div>
</div>
</div>
);
};
export default Login;
my home component
import React, { useEffect } from 'react';
import { toast } from 'react-toastify';
const App = (props) => {
const { location } = props;
useEffect(() => {
if (location.message) {
toast.success(location.message);
}
}, [location.message]);
return <div>App</div>;
};
export default App;
I don't want the state to go back to its initial value on page refresh.
Your data is not persisted. Save the user token in the localStorage and when the app is initializing verify the localStorage and if there have value simply populate the store with this data.
Since you want to store user token, I would suggest that you use a cookie to store it, here is a link where you can find how to create and use a cookie.

Categories

Resources