I work on an auction-like VueJs app with the Laravel backend. When I open the chrome browser it works as I want it to work. However, in safari, it doesn't seem to do anything.
I have two components
DealerAuction.vue
Timer.vue (in the DealerAuction)
DealerAuction:
<template>
<div class="flex flex-col items-center b p-10 rounded shadow-lg bg-white">
<div
class="
absolute
top-0
bg-green-200
w-full
px-3
py-2
uppercase
font-bold
leading-relaxed
tracking-widest
text-sm
rounded-t
border-b-2 border-green-300
"
>
Aukcia prebieha - končí o 24 minút
</div>
<div v-if="beforeAuction">
<p>Auckia ešte nezačala.</p>
</div>
<div v-if="afterAuction">
<p>Auckia sa skončila.</p>
</div>
<div v-if="auctionRunning" >
<p class="text-xl text-blueGray-800 leading-relaxed mt-6 text-center">
Aktuálna cena:
</p>
<div class="flex flex-col items-center justify-evenly w-full mt-3 mb-6">
<div>
<h1 class="text-xl md:text-4xl mb-4 font-bold font-heading">
{{ price }} €
</h1>
</div>
<p
v-if="highestBidder"
class="mb-5 bg-green-800 text-xs text-white p-1 rounded font-bold"
>
Vaša ponuka je najvyššia!
</p>
<p
v-if="!highestBidder"
class="mb-5 bg-red-800 text-xs text-white p-1 rounded font-bold"
>
Niekto má vyššiu ponuku!
</p>
<div>
<a
href="#"
class="
inline-block
py-4
px-8
text-lg text-white text-center
font-bold
leading-none
bg-blue-600
hover:bg-blue-700
rounded
"
#click="raisePrice"
>Prihodiť 100 €</a
>
<p>{{ koniec }}</p>
</div>
</div>
</div>
<hr class="w-full mb-6" />
<table class="w-full">
<tbody>
<tr>
<td>Rezervovaná cena:</td>
<td>{{ auction.minimal_price }} €</td>
</tr>
<Timer
v-bind:starttime="starting"
v-bind:endtime="ending"
v-bind:beforeParent="before"
v-bind:runningParent="running"
v-bind:endedParent="ended"
trans='{
"day":"Day",
"hours":"Hours",
"minutes":"Minuts",
"seconds":"Seconds",
"expired":"Event has been expired.",
"running":"Till the end of event.",
"upcoming":"Till start of event.",
"status": {
"expired":"Expired",
"running":"Running",
"upcoming":"Future"
}}'
></Timer>
</tbody>
</table>
</div>
</template>
<script>
import Timer from "./Timer.vue";
export default {
name: "DealerAuction",
components: {
Timer,
},
props: ["id", "bidder_id", "starting", "ending"],
data() {
return {
auction: "",
price: "",
newPrice: null,
channel: null,
actualBidder: "",
highestBidder: false,
koniec: "",
beforeAuction: false,
auctionRunning: false,
afterAuction: false,
};
},
created() {
console.log(this);
window.axios.defaults.headers.common = {
"X-Requested-With": "XMLHttpRequest",
"X-CSRF-TOKEN": document
.querySelector('meta[name="csrf-token"]')
.getAttribute("content"),
};
this.fetchPrice();
this.changeBidderView();
//PUSHER RECIEVER-------------------------------------------------------------
Pusher.logToConsole = true;
var pusher = new Pusher("1234567890", {
cluster: "eu",
});
this.channel = pusher.subscribe(this.id.toString());
var that = this;
this.channel.bind("price-raise", function (data) {
that.price = data.price;
that.actualBidder = data.bidder;
that.changeBidderView();
});
//-----------------------------------------------------------------------------
},
methods: {
fetchPrice() {
axios.get(`/auction/${this.id}`).then((response) => {
this.auction = response.data;
this.price = response.data.actual_price;
this.actualBidder = response.data.bidder_id;
if (this.actualBidder == this.bidder_id) {
this.highestBidder = true;
}
});
},
raisePrice() {
this.newPrice = this.price + 100;
this.price = this.price + 100;
const req_data = {
actual_price: this.newPrice,
id: this.id,
bidder_id: parseInt(this.bidder_id),
};
axios
.post("/auction/raise/" + this.id, req_data)
.then((response) => {
console.log(response.data);
})
.catch(function (error) {
console.log(error);
});
},
changeBidderView() {
if (this.actualBidder == this.bidder_id) {
this.highestBidder = true;
} else {
this.highestBidder = false;
}
},
before() {
this.beforeAuction = true;
this.auctionRunning = false;
this.afterAuction = false;
},
running() {
this.beforeAuction = false;
this.auctionRunning = true;
this.afterAuction = false;
},
ended() {
this.beforeAuction = false;
this.auctionRunning = false;
this.afterAuction = true;
},
},
};
</script>
And here is the Timer.vue:
<template>
<div>
<div v-show="statusType !== 'expired'">
<div class="day">
<span class="number">{{ days }}</span>
<div class="format">{{ wordString.day }}</div>
</div>
<div class="hour">
<span class="number">{{ hours }}</span>
<div class="format">{{ wordString.hours }}</div>
</div>
<div class="min">
<span class="number">{{ minutes }}</span>
<div class="format">{{ wordString.minutes }}</div>
</div>
<div class="sec">
<span class="number">{{ seconds }}</span>
<div class="format">{{ wordString.seconds }}</div>
</div>
</div>
<div class="message">{{ message }}</div>
<div class="status-tag" :class="statusType">{{ statusText }}</div>
</div>
</template>
<script>
export default {
props: ["auction", "trans", "endedParent", "beforeParent", "runningParent", "starttime", "endtime"],
data: function () {
return {
timer: "",
wordString: {},
start: "",
end: "",
interval: "",
days: "",
minutes: "",
hours: "",
seconds: "",
message: "",
statusType: "",
statusText: "",
};
},
created: function () {
this.wordString = JSON.parse(this.trans);
},
mounted() {
console.log(this);
this.start = new Date(this.starttime).getTime();
this.end = new Date(this.endtime).getTime();
// Update the count down every 1 second
this.timerCount(this.start, this.end);
this.interval = setInterval(() => {
this.timerCount(this.start, this.end);
}, 1000);
},
methods: {
timerCount: function (start, end) {
// Get todays date and time
var now = new Date().getTime();
// Find the distance between now an the count down date
var distance = start - now;
var passTime = end - now;
if (distance < 0 && passTime < 0) {
this.message = this.wordString.expired;
this.statusType = "expired";
this.statusText = this.wordString.status.expired;
this.endedParent();
clearInterval(this.interval);
return;
} else if (distance < 0 && passTime > 0) {
this.calcTime(passTime);
this.message = this.wordString.running;
this.statusType = "running";
this.runningParent();
this.statusText = this.wordString.status.running;
} else if (distance > 0 && passTime > 0) {
this.calcTime(distance);
this.message = this.wordString.upcoming;
this.statusType = "upcoming";
this.beforeParent();
this.statusText = this.wordString.status.upcoming;
}
},
calcTime: function (dist) {
// Time calculations for days, hours, minutes and seconds
this.days = Math.floor(dist / (1000 * 60 * 60 * 24));
this.hours = Math.floor(
(dist % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)
);
this.minutes = Math.floor((dist % (1000 * 60 * 60)) / (1000 * 60));
this.seconds = Math.floor((dist % (1000 * 60)) / 1000);
},
},
};
</script>
<style>
</style>
Plus I can't find any error in the console regarding these components.
I would appreciate any help. I can't figure out what's going on.
Thank you all in advance!
Developer build does not work in Safari because poly-fillers are not present while serving your application on localhost, the error I was seeing using serve was about Promise's Safari needs poly-fillers for Promises.
However the production build is likely to work since any poly-fills would have been compiled into the build.
This was my experience with crappy apple.
Little side note on apple devices, it is their strict policy that all 3rd Party browsers must utilize Apples web rendering engine so Chrome, Firefox etc are technically just skins and additional features, so on apple those browsers aren't exactly the Chromium and Mozilla engines.
Installing momentJS have helped with the problem. Apperently, Date() has a different format on safari and thus can't calculate the difference between starttime, now and endtime.
Related
I am learning JS, and making an appointments project for learning.
Below after fetching and getting data from firebase, I'm storing the data in an array object so that I can sort the data based on dates, however when I'm displaying the data on the page it is showing multiple entries instead of 10 which is actually the total records in the database.
// Showing appointments of the current and previous month.
let startDateArr = []
let startMonthArr = []
let allRecords = []
let update = false
const showingApts = (doc) => {
let newTime = new Date(doc.data().dateInMills.at(-1) * 1000)
allRecords.push({
id: doc.id,
name: doc.data().aptName,
slot: doc.data().aptTimeSlot,
email: doc.data().aptEmail,
day: doc.data().aptDay.at(-1),
type: doc.data().aptType,
date: doc.data().aptStartDate.at(-1),
month: doc.data().aptStartMonth.at(-1),
year: doc.data().aptStartYear.at(-1),
status: doc.data().appointmentStatus,
softDelete: doc.data().softDelete,
time: newTime
})
allRecords.sort(function(a, b) {
return a.time - b.time
})
// Fetching names from profiles db.
db.collection('profiles').onSnapshot((querySnapshotProfiles) => {
querySnapshotProfiles.forEach((profileDoc) => {
if (profileDoc.data().aptEmail.at(-1) === doc.data().aptEmail) {
startDateArr.push(doc.data().aptStartDate.at(-1))
startMonthArr.push(doc.data().aptStartMonth.at(-1))
}
})
})
setTimeout(() => {
let aptStartDate = startDateArr.at(-1)
let aptStartMonth = startMonthArr.at(-1)
if (doc.data().aptStartDate.at(-1) === aptStartDate && doc.data().aptStartMonth.at(-1) === aptStartMonth) {
return update = true
}
for (let record of allRecords) {
let currentMonthAppointments = `
<div class="flex flex-col justify-center align-middle tableRow12">
<div class="grid grid-cols-6 text-center py-4 place-items-center text-xs border-b border-gray-200 hover:bg-blue-100 ease-in-out duration-300 text-blue-600 font-semibold px-2" data-id="${ record.id }">
<span>
<span class='scheduleName'>${ record.name } </span>
<span class='scheduleEmail block text-[10px] text-gray-400 font-medium'>${ record.email } </span>
<div class='text-center w-full mt-2 font-medium'><button class='addMoreApt ${ update === true ? 'inline' : 'hidden' } text-center rounded-md bg-rose-600 p-2 text-white cursor-pointer text-[10px] '>Add 5 more appointments</button></div>
</span>
<span>${ record.slot }</span>
<span>
<span> ${ record.day }, </span>
<span>${ record.date }-${ record.month }-${ record.year } </span>
</span>
<span>${ record.type }</span>
</div>
</div>
`
scheduleTableRows.innerHTML += currentMonthAppointments
}
}, 1000)
}
// Clearing old rows in the table
const rowsToDelete = () => {
let tableRowsToDelete = document.querySelectorAll('.tableRow12')
for (let rowsToDelete of tableRowsToDelete) {
rowsToDelete.remove()
}
}
When I console.log(allRecords) I get ten arrays instead of one, and all the data is populated on the page when showed.
Database structure
Profile section
I have a table in my database and I am trying to calculate the total in my react application.
The problem is that when I use a filter I get back not a number (NaN)
I need to show the total price when applying the different filters.
What I tried was to define a function like this:
const [totalRevenue, setTotalRevenue] = useState(0);
useEffect(() => {
console.log("songs", songs);
const total = songs.reduce((acc, song) => {
const n = Number(song.Revenue);
return isNaN(n) ? acc : acc + n;
}, 0);
setTotalRevenue(total);
}, [songs]);
The filter looks something like this:
const displaySongs = songs
.slice(pagesVisited, pagesVisited + songsPerPage)
.filter((song) => {
let keepSong = true;
keepSong &&=
filterDistributor === "" || totalRevenue === "" ||
song.Distributor?.toLowerCase().includes(
filterDistributor.toLowerCase() || song.Distributor?.Revenue?.toLowerCase().includes(totalRevenue.toLowerCase())
);
return keepSong;
})
.map((song) => {
return (
<tr className="font-medium bg-white border-b dark:bg-gray-800 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-600">
<td className="py-2 px-4 text-left">{song.Distributor}</td>
<td className="py-2 px-4 text-left">{song.Revenue}</td>
</tr>
);
});
return (
<div>
<div className="flex w-full">
<select
id="Labels"
className="bg-slate-900 text-white border border-gray-500 text-sm focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
value={filterDistributor || totalRevenue}
onChange={(e) => setFilterDistributor(e.target.value) || setTotalRevenue(e.target.value)}
>
<option value="">Choose a Distributor</option>
{[...new Set(songs.map((song) => song.Distributor))].map(
(Distributor) => (
<option key={Distributor} value={Distributor}>
{Distributor}
</option>
)
)}
</select>
</div>
<div className="w-96 p-6 my-10 gap-4 shadow-md rounded-md bg-slate-900 text-center">
<p className="font-normal text-white dark:text-gray-400">
Total Revenue:
</p>
<p className="text-2xl font-bold tracking-tight text-white dark:text-white">
{Math.round(totalRevenue).toFixed(2)} €
</p>
</div>
</div>
Unfortunately, as mentioned above, this function returns an invalid value.
Here is an example of my data which is the same as you get when console logging:
[
{
"id": 1,
"Period From": "2021-01-31T23:00:00.000Z",
"Period To": "2021-02-27T23:00:00.000Z",
"Transaction Date": null,
"Distributor": "DistriburName",
"UPC": "UpcNumber",
"Cat. No.": "CaNumber",
"ISRC": "CaNumber",
"Label": "LabelName",
"Release Title": "ReleaseTitle",
"Track Title": "TrackTitle",
"Mix Name": "Original Mix",
"Artist": "ArtistName",
"Content Type": "Asset",
"Delivery Method": "Non-Interactive Stream",
"Territory": "US",
"Quantity": 2,
"Revenue": 0.001091
}
]
Its because you are using the reduce function wrong.
here is something you can try:
useEffect(() => {
console.log("songs", songs);
const total = songs.reduce(function(prev, cur) {
if(isNaN(cur.Revenue)){
console.log("is not a number")
return prev
}
return prev + cur.Revenue
}, 0);
setTotalRevenue(total);
}, [songs]);
reference: https://ibb.co/4R73VSj
check the updated answer, just so u know, NaN is a number and when u convert any string to Number you cannot check if its a number or not after because it will always be a number, cause NaN is a number try doing this
console.log(isNaN(NaN))
it will return true
I am beginner web developer. I make my first crud in Laravel 8 and Vue.
I use this component t in my project: https://www.npmjs.com/package/vuejs-datatable
I have this code:
DataTable.vue:
<template>
<div>
<div class="row mb-3">
<div class="col-3">
<div class="input-group">
<input
v-model="search"
class="form-control"
placeholder="Szukaj..."
type="text"
>
<div class="input-group-append">
<button class="btn btn-primary" type="button" #click.prevent="handleSearch">
<font-awesome-icon icon="fas fa-search" />
</button>
</div>
</div>
</div>
<div class="col-2">
<div class="input-group">
<label for="pageOption" class="mt-2 mr-2">Na stronie</label>
<select class="form-control" v-model="perPage" #change="handlePerPage" id="pageOption">
<option v-for="page in pageOptions" :key="page" :value="page">{{ page }}</option>
</select>
</div>
</div>
</div>
<table class="table table-hover">
<thead>
<tr>
<th class="table-head">#</th>
<th v-for="column in columns" :key="column" #click="sortByColumn(column)"
class="table-head">
{{ column | columnHead }}
<span v-if="column === sortedColumn">
<font-awesome-icon v-if="order === 'asc' " icon="fas fa-angle-up" />
<font-awesome-icon v-else icon="fas fa-angle-down" />
</span>
</th>
</tr>
</thead>
<tbody>
<tr class="" v-if="tableData.length === 0">
<td class="lead text-center" :colspan="columns.length + 1">Brak danych do wyświetlenia.</td>
</tr>
<tr v-for="(data, key1) in tableData" :key="data.id" class="m-datatable__row" v-else>
<td>{{ serialNumber(key1) }}</td>
<td v-for="(value, key) in data">{{ value }}</td>
</tr>
</tbody>
</table>
</div>
</template>
<script type="text/ecmascript-6">
import axios from 'axios';
import Vue from 'vue';
import 'vuejs-datatable/dist/themes/bootstrap-4.esm';
import {
VuejsDatatableFactory,
IDataFnParams,
IDisplayHandlerParam,
ITableContentParam,
TColumnsDefinition,
VueDatatable
} from 'vuejs-datatable';
Vue.use(VuejsDatatableFactory, VueDatatable);
export default {
props: {
fetchUrl: {type: String, required: true},
columns: {type: Array, required: true},
},
data() {
return {
tableData: [],
url: '',
pagination: {
meta: {to: 1, from: 1}
},
offset: 4,
currentPage: 1,
perPage: 1,
sortedColumn: this.columns[0],
order: 'asc',
search: null,
pageOptions: [1, 10, 20, 50],
}
},
watch: {
fetchUrl: {
handler: function (fetchUrl) {
this.url = fetchUrl
},
immediate: true
}
},
created() {
return this.fetchData()
},
computed: {
/**
* Get the pages number array for displaying in the pagination.
* */
pagesNumber() {
if (!this.pagination.meta.to) {
return []
}
let from = this.pagination.meta.current_page - this.offset
if (from < 1) {
from = 1
}
let to = from + (this.offset * 2)
if (to >= this.pagination.meta.last_page) {
to = this.pagination.meta.last_page
}
let pagesArray = []
for (let page = from; page <= to; page++) {
pagesArray.push(page)
}
return pagesArray
},
/**
* Get the total data displayed in the current page.
* */
totalData() {
return (this.pagination.meta.to - this.pagination.meta.from) + 1
}
},
methods: {
fetchData() {
let dataFetchUrl = `${this.url}&page=${this.currentPage}&column=${this.sortedColumn}&order=${this.order}&per_page=${this.perPage}`
axios.get(dataFetchUrl)
.then(({data}) => {
this.pagination = data
this.tableData = data.data
}).catch(error => this.tableData = [])
},
/**
* Get the serial number.
* #param key
* */
serialNumber(key) {
return (this.currentPage - 1) * this.perPage + 1 + key
},
/**
* Change the page.
* #param pageNumber
*/
changePage(pageNumber) {
this.currentPage = pageNumber
this.fetchData()
},
/**
* Sort the data by column.
* */
sortByColumn(column) {
if (column === this.sortedColumn) {
this.order = (this.order === 'asc') ? 'desc' : 'asc'
} else {
this.sortedColumn = column
this.order = 'asc'
}
this.fetchData()
},
handleSearch() {
this.fetchData()
},
handlePerPage($event) {
this.perPage = $event.target.value;
this.fetchData()
}
},
filters: {
columnHead(value) {
return value.split('_').join(' ').toUpperCase()
}
},
translate: {
nextButton: 'Dalej',
previousButton: 'Wstecz',
placeholderSearch: 'Szukaj...',
},
name: 'DataTable'
}
</script>
<style scoped>
</style>
Notes.vue:
<template>
<CRow>
<CCol col="12">
<transition name="slide">
<CCard>
<CCardBody>
<h4>
Menus
</h4>
<CButton color="success" #click="createNote()" class="mb-3 my-5">Add Menu <font-awesome-icon icon="fas fa-plus" /> </CButton>
<div class="flex-center position-ref full-height">
<data-table
:fetch-url="datatTableUrl"
:columns="['name', 'email', 'id' , 'created_at']"
:headers="['nazwa', 'adres email', 'ID' , 'utworzono']"
></data-table>
</div>
</CCardBody>
</CCard>
</transition>
</CCol>
</CRow>
</template>
<script>
import Vue from 'vue';
export default {
data() {
return {
datatTableUrl: '',
}
},
created: function () {
this.datatTableUrl = Vue.prototype.$apiAdress + '/api/users/dataTable' + '?token=' + localStorage.getItem("api_token");
},
methods: {
noteLink(id) {
return `notes/${id.toString()}`
},
editLink(id) {
return `notes/${id.toString()}/edit`
},
showNote(id) {
const noteLink = this.noteLink(id);
this.$router.push({path: noteLink});
},
editNote(id) {
const editLink = this.editLink(id);
this.$router.push({path: editLink});
},
deleteNote(id) {
let self = this;
let noteId = id;
axios.post(this.$apiAdress + '/api/notes/' + id + '?token=' + localStorage.getItem("api_token"), {
_method: 'DELETE'
})
.then(function (response) {
self.message = 'Successfully deleted note.';
self.showAlert();
self.getNotes();
}).catch(function (error) {
console.log(error);
self.$router.push({path: '/login'});
});
},
createNote() {
this.$router.push({path: 'notes/create'});
},
}
}
</script>
This code work fine.
I have 2 problems:
I would like the column headers in the table to be taken from: headers - currently these are the column names from the database (ie: columns).
I would like to add a link to edit and delete a record. I have created methods: editLink (), deleteNote (). How can I add them to my table? I would like them to be visible next to the "name" column
How can I make it?
Please help me :)
For the problem #1. I would do it this way.
Merge the columns and the headers as one Object, ex: where the key will be the column name (Important: don't forget to register the headers prop).
<data-table
:fetch-url="datatTableUrl"
:headers="{'name': 'nazwa','email': 'adres email','id': 'ID' , 'created_at': 'utworzono'}"
></data-table>
In the DataTable:
<th v-for="(column, label) in headers" :key="column" #click="sortByColumn(column)" class="table-head">
{{ label | columnHead }}
<span v-if="column === sortedColumn">
<font-awesome-icon v-if="order === 'asc' " icon="fas fa-angle-up" />
<font-awesome-icon v-else icon="fas fa-angle-down" />
</span>
</th>
For the Second problem #2 (Not very clear), Better to move these functions to the DataTable and add the actions as a new column, short example:
<td><button #click="editLink(key1)">Edit link or some fa fa icon</button></td>
Add as prop in the DataTable:
props: {
fetchUrl: {type: String, required: true},
columns: {type: Array, required: true},
headers: {type: Object, required: true} //<-- This
},
I'm trying to have react forms save and then update some user settings.
import React, { useState, useEffect } from "react";
import Dropdown from "components/Settings/Preferences/Dropdown";
import { useForm } from "react-hook-form";
function Pill({ value, handleChange,optionName,substanceKey,key }) {
return (
<div className="bg-gray-600 text-white text-xs px-2 py-0.5 w-max-content rounded-lg align-middle mr-1 mb-1">
{value}
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
className="inline-block align-middle cursor-pointer"
onClick={() => handleChange(value, "remove",optionName,substanceKey)}
>
<line x1="18" y1="6" x2="6" y2="18" />
<line x1="6" y1="6" x2="18" y2="18" />
</svg>
</div>
);
}
function InventoryPage(props) {
const [substanceDetails, setSettings] = useState(
[
{
substance: "Modafinil",
scheduledtimes: [8, 13],
startdate: "1996-12-02",
enddate: "2020-01-01",
duration: 2,
planneddose: "10mg"
},
{
substance: "Coveve",
scheduledtimes: [8, 12],
startdate: "1996-12-02",
enddate: "2020-01-01",
duration: 2,
planneddose: "10mg"
}
);
const { register, handleSubmit,watch, errors,formState } = useForm();
const handleChange = (value, mode,optionName,substanceKey) => {
var removedSubstances ;
if(mode==="remove") {
if (optionName === "substance" ) {
removedSubstances = substanceDetails.find(v=> v.substance === value) ? substanceDetails.filter(v => v.substance !== value) : [...substanceDetails, value]
}
else {
removedSubstances = substanceDetails.reduce((acc, key) => {
// return acc; // remove keys
if (optionName === "scheduledtimes") { //works
// remove 14 in times for keys
return [...acc, { ...key,
scheduledtimes: key.scheduledtimes.filter(time => time !== value)
}]
}
if (optionName === "planneddose") {
// modify the power by concatenating an new object with partial info
if (key.substance == substanceKey){
return [...acc, {...key,
planneddose: null
}];
} else {
return [...acc, {...key,
planneddose: key.planneddose
}];
}
}
if (optionName === "startdate") {
// modify the power by concatenating an new object with partial info
if (key.substance == substanceKey){
return [...acc, {...key,
startdate: null
}];
} else {
return [...acc, {...key,
startdate: key.startdate
}];
}
}
if (optionName === "enddate") {
// modify the power by concatenating an new object with partial info
if (key.substance == substanceKey){
return [...acc, {...key,
enddate: null
}];
} else {
return [...acc, {...key,
enddate: key.enddate
}];
}
}
if (optionName === "duration") {
// modify the power by concatenating an new object with partial info
if (key.substance == substanceKey){
return [...acc, {...key,
duration: null
}];
} else {
return [...acc, {...key,
duration: key.duration
}];
}
}
}
, []);
}
setSettings(removedSubstances)
}
};
const onSubmit = data => console.log(data);
const [pending, setPending] = useState(false);
console.log(watch("example")); // watch input value by passing the name of it
if (substanceDetails === false) {
return (
<div className="md:grid md:grid-cols-3 md:gap-6">
<div className="md:col-span-1">
<h3 className="text-lg font-medium leading-6 text-gray-900">
Substances
</h3>
</div>
<div className="mt-5 md:mt-0 md:col-span-2 font-mono font-medium text-blue-500">
loading...
</div>
</div>
);
}
return (
<div className="md:grid md:grid-cols-3 md:gap-6">
<div className="mt-5 md:mt-0 md:col-span-2">
<form onSubmit={handleSubmit(onSubmit)} id="formName">
<div className="flex flex-wrap mt-2">
{substanceDetails &&
substanceDetails.map((subst) => (
<Pill
registerInput={register}
optionLabel="substance"
value={subst.substance}
key={subst.substance}
substanceKey = {subst.substance}
optionName={"substance"}
// allOptions={["Dexamphetamine", "Ritalin"]}
handleChange={handleChange}
error={formState.errors?.content ? true : false}
/>
))}
</div>
<Dropdown
registerInput={register}
optionLabel="Substance"
selectedOption={substanceDetails.substance}
optionName={"substance"}
allOptions={["Dexamphetamine", "Ritalin"]}
error={formState.errors?.content ? true : false}
/>
<button
className="inline-flex items-center justify-center px-5 py-2 border border-transparent text-base leading-6 font-medium rounded-md text-indigo-700 bg-indigo-100 hover:text-indigo-600 hover:bg-indigo-50 focus:outline-none focus:shadow-outline focus:border-indigo-300 transition duration-150 ease-in-out"
variant={props.buttonColor}
size={props.inputSize}
type="submit"
disabled={pending}
form="formName"
>
{pending ? (
<>
<span>Saving</span>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
className="animate-spin h-4 w-4 ml-3 fill-current"
>
<path d="M0 11c.511-6.158 5.685-11 12-11s11.489 4.842 12 11h-2.009c-.506-5.046-4.793-9-9.991-9s-9.485 3.954-9.991 9h-2.009zm21.991 2c-.506 5.046-4.793 9-9.991 9s-9.485-3.954-9.991-9h-2.009c.511 6.158 5.685 11 12 11s11.489-4.842 12-11h-2.009z" />
</svg>
</>
) : (
<span>Save</span>
)}
</button>
</form>
</div>
</div>
);
}
export default InventoryPage;
Currently when hitting submit, the form itself is just refreshing the page, and it's not using the onSubmit constant. I'm not sure what's going on, love some help :)
Sandbox link, code is compiling but probably easier to vet the code itself
https://codesandbox.io/s/using-tailwind-with-nextjs-forked-nnr0l?file=/pages/index.js
You are calling onSubmit inside handleSubmitRemove.
Removing onSubmit from handleSubmitRemove might work.
Try using the id attribute on the form tag, i.e
<form onSubmit={handleSubmit(onSubmit)} id="formName">
And on the button tag, you use the form attribute which will be the same as the form id, i.e
<button
variant={props.buttonColor}
size={props.inputSize}
type="submit"
disabled={pending}
form="formName"
>
instead of button try input element with type="submit" and style it
if nothing works, from above suggestions.
use preventDefault for submit
and handle submit with a different function , get all the data you need using document.getElement or querySelector and handle them in the function.
Please take a look at this example
https://codesandbox.io/s/using-tailwind-with-next-js-forked-tizjq?file=/pages/index.js
After required fields are filled submit button is clicked, form submits
Your CodeSandbox link wouldn't compile for me, but take a look at this discussion on the RHF github about how to prevent form submission in the submit handler. It's involving async stuff but should be similar for your needs. I think you just need to pass the event and preventDefault as others have said, and maybe their example will be worth following.
I am a beginner in web development and am developing at a rapid pace. This may be a question that can be found in the research.
What I want to do
I am trying to use Next.js to retrieve information from an external API and display it on the screen.
The API we want to call is this one.
https://en.wiki.zks.org/interact-with-zkswap/restful-api#get-account-balance
My goal is to display the balance of tokens in this Response on the screen.
What I did
Specifically, when I press the button in the header, I want to get the address from Metamask and at the same time get the ETH balance in zkSync.
To implement this, I wrote the following code
import { useState } from "react";
import { Contract, utils, ethers } from 'ethers'
import ConnectMetamaskButton from '../components/ConnectMetamaskButton';
import zkswapABI from '../../zkswap.ABI.json'
import * as zksync from "zksync"
export default function ConnectWallet(props) {
const [Accounts, setAccounts] = useState("Connect Metamask");
const ethersProvider = ethers.getDefaultProvider("ropsten");
var MNEMONIC = process.env.MNEMONIC;
const ethWallet = ethers.Wallet.fromMnemonic(MNEMONIC).connect(ethersProvider);
console.log("Accounts = ", Accounts);
async function ConnectMetamask() {
try {
const newAccounts = await ethereum.request({
method: 'eth_requestAccounts',
})
let accounts = newAccounts;
setAccounts(accounts[0]);
console.log("Accounts = ", Accounts);
} catch (error) {
console.error(error);
}
getBalases();
};
async function getBalases() {
const address = Accounts;
console.log("address = ", address);
const zkSyncUrl = "https://api.zks.app/v2/3/account/0x2D1Ac1CA744da293c5AcceAe25BE8DCd71168241/balances";
const response = await fetch(zkSyncUrl);
const data = await response.json();
console.log("data = ", data);
};
async function withdrawETH() {
const syncProvider = await zksync.getDefaultProvider("ropsten");
const syncWallet = await zksync.Wallet.fromEthSigner(ethWallet, syncProvider);
const withdraw =
await
syncWallet.withdrawFromSyncToEthereum({
ethAddress: ethWallet.address,
token: "ETH",
amount: ethers.utils.parseEther("0.001"),
});
}
async function depositETH() {
let ABI = zkswapABI;
let ZKSwapContract = '0x010254cd670aCbb632A1c23a26Abe570Ab2Bc467'
const contract = new Contract(ZKSwapContract, ABI, ethWallet)
const tx = await contract.depositETH(ethWallet.address, {
value: utils.parseEther('0.1')
})
return tx
}
async function zkSyncToZKSwap() {
event.preventDefault();
console.log("amount = ", event.target.amount.value);
withdrawETH();
depositETH();
}
return (
<div>
<header className="text-gray-100 bg-gray-900 body-font shadow w-full">
<div className="container mx-auto flex flex-wrap p-5 flex-col md:flex-row items-center">
<nav className="flex lg:w-2/5 flex-wrap items-center text-base md:ml-auto">
<a className="mr-5 hover:text-gray-900 cursor-pointer border-b border-transparent hover:border-indigo-600"></a>
</nav>
<a
className="flex order-first lg:order-none lg:w-1/5 title-font font-medium items-center lg:items-center lg:justify-center mb-4 md:mb-0">
<span className="ml-3 text-xl">L2 DEX HUB</span>
</a>
<div className="lg:w-2/5 inline-flex lg:justify-end ml-5 lg:ml-0">
<ConnectMetamaskButton Accounts={Accounts} onClick={() => { ConnectMetamask(); }}></ConnectMetamaskButton>
</div>
</div>
</header>
<div>
<section className="h-screen w-4/5 max-w-5xl mx-auto flex items-center justify-center flex-col">
<form onSubmit={zkSyncToZKSwap}>
<div className="flex flex-col rounded-lg overflow-hidden sm:flex-row">
<label htmlFor="amount"></label>
<input className="py-3 px-4 bg-gray-200 text-gray-800 border-gray-300 border-2 outline-none placeholder-gray-500 focus:bg-gray-100" id="amount" type="text" amount="amount" placeholder="Amount" />
<button className="py-3 px-4 bg-gray-700 text-gray-100 font-semibold uppercase hover:bg-gray-600" type="submit" >Exchange</button>
</div>
</form>
</section>
</div>
</div>
);
};
withdrawETH() and depositETH() are example of actually using mnemonics to create a wallet object and perform a transaction.
In this implementation, we have already confirmed that the money transfer actually takes place when the Exchange button is pressed!
Problem
We have succeeded in getting the data in console.log("data = ", data);
After this, how can I specify the token and get only the balance?
{
"success": true,
"data": {
"balances": {
"tokens": [
{
"id": 0,
"amount": "2.84",
"value": "8226.013602874849956332"
}
],
"pairs": []
},
"asset": {
"tokens": "8226.013602874849956332",
"pairs": "0",
"total": "8226.013602874849956332"
}
}
}
Priblem(optional)
Here, I am writing the address directly in the API URL, but I want to enter the accounts in useState and have it change dynamically, how do I rewrite it?