Display database table on v-calendar - laravel

Hi Guys hope someone can help, I am working on the test project using Laravel for backend and vue, vuetify for the front end.
I have this store table
and I want to display on v-calendar based on the date from the store table
any idea how to make this work?, maybe you have some reference or link you can share.
[edit]
I got this code from https://vuetifyjs.com/en/components/calendars/#usage but am still lost, hope someone can explain and point out what I should do.
thanks
<script>
export default {
data: () => ({
type: 'month',
types: ['month', 'week', 'day', '4day'],
mode: 'stack',
modes: ['stack', 'column'],
weekday: [0, 1, 2, 3, 4, 5, 6],
weekdays: [
{ text: 'Sun - Sat', value: [0, 1, 2, 3, 4, 5, 6] },
{ text: 'Mon - Sun', value: [1, 2, 3, 4, 5, 6, 0] },
{ text: 'Mon - Fri', value: [1, 2, 3, 4, 5] },
{ text: 'Mon, Wed, Fri', value: [1, 3, 5] },
],
value: '',
events: [],
colors: ['blue', 'indigo', 'deep-purple', 'cyan', 'green', 'orange', 'grey darken-1'],
names: ['Meeting', 'Holiday', 'PTO', 'Travel', 'Event', 'Birthday', 'Conference', 'Party'],
}),
methods: {
getEvents ({ start, end }) {
const events = []
const min = new Date(`${start.date}T00:00:00`)
const max = new Date(`${end.date}T23:59:59`)
const days = (max.getTime() - min.getTime()) / 86400000
const eventCount = this.rnd(days, days + 20)
for (let i = 0; i < eventCount; i++) {
const allDay = this.rnd(0, 3) === 0
const firstTimestamp = this.rnd(min.getTime(), max.getTime())
const first = new Date(firstTimestamp - (firstTimestamp % 900000))
const secondTimestamp = this.rnd(2, allDay ? 288 : 8) * 900000
const second = new Date(first.getTime() + secondTimestamp)
events.push({
name: this.names[this.rnd(0, this.names.length - 1)],
start: first,
end: second,
color: this.colors[this.rnd(0, this.colors.length - 1)],
timed: !allDay,
})
}
this.events = events
},
getEventColor (event) {
return event.color
},
rnd (a, b) {
return Math.floor((b - a + 1) * Math.random()) + a
},
},
}
</script>
<template>
<div>
<v-sheet
tile
height="54"
class="d-flex"
>
<v-btn
icon
class="ma-2"
#click="$refs.calendar.prev()"
>
<v-icon>mdi-chevron-left</v-icon>
</v-btn>
<v-select
v-model="type"
:items="types"
dense
outlined
hide-details
class="ma-2"
label="type"
></v-select>
<v-select
v-model="mode"
:items="modes"
dense
outlined
hide-details
label="event-overlap-mode"
class="ma-2"
></v-select>
<v-select
v-model="weekday"
:items="weekdays"
dense
outlined
hide-details
label="weekdays"
class="ma-2"
></v-select>
<v-spacer></v-spacer>
<v-btn
icon
class="ma-2"
#click="$refs.calendar.next()"
>
<v-icon>mdi-chevron-right</v-icon>
</v-btn>
</v-sheet>
<v-sheet height="600">
<v-calendar
ref="calendar"
v-model="value"
:weekdays="weekday"
:type="type"
:events="events"
:event-overlap-mode="mode"
:event-overlap-threshold="30"
:event-color="getEventColor"
#change="getEvents"
></v-calendar>
</v-sheet>
</div>
</template>

Actually you need to pass data through events prop of v-calendar
You can see how to pass data in vuetify docs code.
They are sharing examples.
One of examples here

Related

Using useState for a slider - not working

Slider component
SliderItem component
Browser
Can someone help me with this issue?
Im expecting to increase the "currentTab" with + 1 when the button "Next slide" is pressed
Instead of using filter which should be used to filter the items array before map which is responsible for rendering, better check if slide index is current and then render it
for example :
import {useState} from 'react';
export default function App() {
return (
<div className="App">
<Slider/>
</div>
);
}
const items = [
{
id: 1,
title: 'Slide 1',
description: 'Description 1',
image: 'https://picsum.photos/800/400?image=0',
},
{
id: 2,
title: 'Slide 2',
description: 'Description 2',
image: 'https://picsum.photos/800/400?image=1',
},
{
id: 3,
title: 'Slide 3',
description: 'Description 3',
image: 'https://picsum.photos/800/400?image=2',
}];
const Slider = () => {
const [current, setCurrent] = useState(0)
const length = items.length
const nextSlide = () => {
setCurrent(current === length - 1 ? 0 : current + 1)
}
return (
<section className="slider">
{items
.map((item, index) => {
return (
<div
className={index === current ? 'slide active' : 'slide'}
key={item.id}
>
{index === current && (
<>
<img src={item.image} alt="travel image" className="image" />
<button onClick={nextSlide}>next</button>
</>
)}
</div>
)
})}
</section>
)
}
https://codesandbox.io/s/laughing-wilson-xpdiyg?file=/src/App.js

How to write React code to display data of the array object using useEffect

What I am trying:
Take a random character from the characters array and display its abilities and role
Take other four unique random chars name from the same array and display these as option.
Please note that the options must have the correct answer too
If the guessed character is correct the score should increase by 1 else decrease by 1
Code:
import React, { Fragment, useEffect, useState } from "react";
import "../styles/App.css";
const characters = [
{
id: 1,
name: "Jett",
role: "Duelist",
abilities: ["TailWind", "Cloud Burst", "UpDraft", "Blade Storm"],
},
{
id: 2,
name: "Phoenix",
role: "Duelist",
abilities: ["HotHands", "Blaze", "Curve Ball", "Run It Back"],
},
{
id: 3,
name: "Yoru",
role: "Duelist",
abilities: ["GateCrash", "Fakeout", "Blind Side", "Dimensional Drift"],
},
{
id: 4,
name: "Reyna",
role: "Duelist",
abilities: ["Dismiss", "Leer", "Devour", "Empress"],
},
{
id: 5,
name: "Raze",
role: "Duelist",
abilities: ["Paint Shells", "Boom Bot", "BlastPack", "ShowStopper"],
}
];
const App = () => {
const [currChar, setCurrChar] = useState({
name: "",
role: "",
abilities: [],
options: [],
});
const [score, setScore] = useState(0);
const changeChar = () => {
}
const scoreHandler = (e) => {
};
useEffect(() => {
});
return (
<div id="main">
<div className="container">
<h1 className="header">Guess the Character</h1>
<div className="ques-area">
<div className="score" id='score'>Score: {score}</div>
<h3>The character has the following abilities:</h3>
<h4>Role: {currChar.role}</h4>
{currChar.abilities.join()}
<div className="options">
{currChar.options.map((option) => (
<button onClick={scoreHandler}>
{option.name}
</button>
))}
</div>
</div>
</div>
</div>
);
};
export default App;
useEffect(() => {
getRandomObject(characters);
}, []);
const [score, setScore] = useState(0);
const getRandomObject = (array) => {
const optionArr = [];
for (let i = 0; i < array.length; i++) {
optionArr.push(array[Math.floor(Math.random() * array.length)]);
}
const randomObject = array[Math.floor(Math.random() * array.length)];
const obj = {
name: randomObject.name,
role: randomObject.role,
abilities: randomObject.abilities,
options: optionArr.slice(0, 4)
};
setCurrChar(obj);
};

Vuetify v-data-table override itemsPerPageOptions when using the options.sync prop

I am using the :options.sync prop on my v-data-table in to fetch paginated data server side. However, the default itemsPerPage options are 5,10,15 & ALL.
I tried changing the options using :items-per-page-options using a property inside my data object itemsPerPageOptions:[50,100,250,-1] but it is still showing the old values. The way I am trying worked before I was using the "new" :options prop, but with it in place its not doing it. From the documentation itself, the options prop has no key value pair for items-per-page-options.
<v-data-table
:server-items-length="totalTrades"
:options.sync="options"
:footer-props="{ 'items-per-page options': itemsPerPageOptions }"
:loading="loading"
:items="items"
:headers="headers"
class="transparent"
dense
fixed-header
height="800"
item-key="id"
></v-data-table>
data: () => ({
options: {},
itemsPerPageOptions: [50, 100, 250, -1],
totalTrades: -1,
search: "",
searchForWallet: "",
})
You are providing wrong value for footer-props - you should use camelCase rather than kebab-case:
<template>
<v-data-table :items="items" :headers="headers" :options.sync="pagination" :footer-props="footerOptions" />
</template>
<script>
export default
{
name: 'MyComponent',
data()
{
return {
pagination:
{
page: 1,
itemsPerPage: 50,
sortBy: ['product_name'],
sortDesc: [false],
},
footerOptions:
{
itemsPerPageOptions: [25, 50, 100], // this is the proper name - not "items-per-page options" like what you're using
}
}
}
}
</script>

Increasing performance of v-data-table with custom cells and async data loading

I'm creating a page with v-data-table. Some content of this table is loading at mounted stage, but the data for one column should be loaded line-by-line in the background by async API calls after rendering the whole table. Table rows should also be colored based on data returned from API call.
I've already developed this page, but stuck into one issue - when the table contains composite cells that was redefined by item slot (by example, a cell with icons, tooltips or spans), table row update time significantly increases.
According to business logic, the page may contain a large amount of rows, but I can't use v-data-table pagination to reduce entries count at one page.
The question is - how can I update row (in fact, just its color and one cell value) with a little performance loss as possible?
There is a Codepen with this problem. The way of loading data into the page is completely preserved in this Codepen, but API calls was replaced by promises with fixed timeout.
The problem still exists in Codepen. By default all the requests for 100 items have passed in 12-13 seconds (there's a counter at the bottom of the page). When I comment out last td, they're passed just in 7-8 seconds. When I comment out another one td (second from the end), they're passed in 6 seconds. When I increase items count to 1000, row update time is also increases.
new Vue({
el: '#app',
vuetify: new Vuetify(),
data () {
return {
headers: [
{
text: 'Dessert (100g serving)',
value: 'name',
},
{ text: 'Second name', value: 'secondName' },
{ text: 'Fat (g)', value: 'fat' },
{ text: 'Carbs (g)', value: 'carbs' },
{ text: 'Protein (g)', value: 'protein' },
{ text: 'Max value', value: 'maxValue' },
{ text: 'Actions', value: 'id' },
],
desserts: [],
timerStart: null,
loadingTime: null,
}
},
created() {
this.generateDesserts();
},
mounted() {
this.countMaxValues(this.desserts).then(() => {
this.loadingTime = (Date.now() - this.timerStart) / 1000;
});
},
methods: {
generateDesserts() {
let dessertNames = [
'Frozen Yogurt ',
'Ice cream sandwich ',
'Eclair',
'Cupcake',
'Gingerbread',
'Jelly bean',
'Lollipop',
'Honeycomb',
'Donut',
'KitKat',
null
];
for (let i = 0; i < 100; i++) {
let dessert = {
id: i,
name: dessertNames[Math.floor(Math.random() * dessertNames.length)],
secondName: dessertNames[8 + Math.floor(Math.random() * (dessertNames.length - 8))],
fat: Math.random() * 100,
carbs: Math.floor(Math.random() * 100),
protein: Math.random() * 10
};
this.desserts.push(dessert);
}
},
async countMaxValues(array) {
this.timerStart = Date.now();
for (const item of array) {
await this.countMaxValue(item).catch(() => {
//Even when one request throws error we should not stop others
})
}
},
async countMaxValue(item) {
await new Promise(resolve => setTimeout(resolve, 50)).then(() => {
let maxVal = Math.random() * 100;
item.maxValue = maxVal < 20 ? null : maxVal;
this.desserts.splice(item.id, 1, item);
});
}
}
})
<div id="app">
<v-app id="inspire">
<v-data-table
:headers="headers"
:items="desserts"
:footer-props='{
itemsPerPageOptions: [-1],
prevIcon: null,
nextIcon: null,
}'
>
<template v-slot:item="props">
<tr :style="{
background: (props.item.maxValue !== null && (props.item.carbs < props.item.maxValue))
? '#ffcdd2'
: (
(props.item.maxValue !== null && (props.item.carbs > props.item.maxValue)
? '#ffee58'
: (
props.item.maxValue === null ? '#ef5350' : 'transparent'
)
)
)}">
<td>{{ props.item.name || '—' }}</td>
<td>{{ props.item.secondName || '—' }}</td>
<td>{{ props.item.fat }}</td>
<td>{{ props.item.carbs }}</td>
<td>{{ props.item.protein }}</td>
<td>
<span>
{{ props.item.maxValue || '—' }}
</span>
<v-btn v-if="props.item.name && props.item.maxValue" icon>
<v-icon small>mdi-refresh</v-icon>
</v-btn>
</td>
<td class="justify-center text-center" style="min-width: 100px">
<v-tooltip bottom v-if="props.item.name && props.item.secondName">
<template v-slot:activator="{ on }">
<v-icon v-on="on"
class="mr-2"
small
>
format_list_numbered_rtl
</v-icon>
</template>
<span>Some action tooltip</span>
</v-tooltip>
<v-tooltip bottom v-if="props.item.name && props.item.secondName">
<template v-slot:activator="{ on }">
<v-icon v-on="on"
class="mr-2"
small
>
edit
</v-icon>
</template>
<span>Edit action tooltip</span>
</v-tooltip>
<v-tooltip bottom v-if="props.item.name === 'KitKat'">
<template v-slot:activator="{ on }">
<v-icon v-on="on"
small
>
delete
</v-icon>
</template>
<span>Delete action tooltip</span>
</v-tooltip>
</td>
</tr>
</template>
</v-data-table>
<p>{{ "Page loading time (sec): " + (loadingTime || '...') }}</p>
</v-app>
</div>
It seems Vue can update DOM more efficient if it is wrap in component (Sorry, I don't know in detail why).
This is your original code in JSFiddle. It will use around 12-13 seconds.
Then I create a component which wrap your entire tr:
const Tr = {
props: {
item: Object
},
template: `
<tr>
... // change props.item to item
</tr>
`
}
new Vue({
el: '#app',
vuetify: new Vuetify(),
components: {
'tr-component': Tr // register Tr component
},
...
async countMaxValue(item) {
await new Promise(resolve => setTimeout(resolve, 50)).then(() => {
let maxVal = Math.random() * 100;
// update entire object instead of one property since we send it as object to Tr
let newItem = {
...item,
maxValue: maxVal < 20 ? null : maxVal
}
this.desserts.splice(newItem.id, 1, newItem);
});
}
})
And your html will looks like:
<v-data-table
:headers="headers"
:items="desserts"
:footer-props='{
itemsPerPageOptions: [-1],
prevIcon: null,
nextIcon: null,
}'>
<template v-slot:item="props">
<tr-component :item='props.item'/>
</template>
</v-data-table>
The result will use around 6-7 seconds which is only 1-2 seconds to update DOM.
Or if you find out that your function trigger very fast (In your example use 50ms which is too fast in my opinion) you could try throttle it to less update DOM.
...
methods: {
async countMaxValue(item) {
await new Promise(resolve => setTimeout(resolve, 50)).then(() => {
let maxVal = Math.random() * 100;
let newItem = {
...item,
maxValue: maxVal < 20 ? null : maxVal
}
this.changes.push(newItem) // keep newItem to change later
this.applyChanges() // try to apply changes if it already schedule it will do nothing
});
},
applyChanges () {
if (this.timeoutId) return
this.timeoutId = setTimeout(() => {
while (this.changes.length) {
let item = this.changes.pop()
this.desserts.splice(item.id, 1, item)
}
this.timeoutId = null
}, 1500)
}
}
The result will use around 5-6 seconds but as you can see it's not immediately update.
Or you may try to call your API in parallel such as 10 requests, you could reduce from to wait 100 * 50 ms to around 10 * 50 ms (mathematically).
I hope this help.

Sort table Vue.js

I am trying to sort a table by columns. That when pressing the ID button all the column is ordered from highest to lowest or vice versa, and the same by pressing the other two. How can I do it?
<table id="mi-tabla">
<thead>
<tr class="encabezado-derecha" >
<th>ID</th>
<th>Nombre de sección</th>
<th>Potencial (€)</th>
</tr>
</thead>
<tbody>
<tr class="item" v-for="user in userInfo" #click="openDiv(), showInfo1(user.id_section)">
<td>{{user.id_section}}</td>
<td>{{user.desc_section}}</td>
<div class="acceder">
<td>{{user.sale_potential | currency}}</td>
<img src="../../iconos/icon/chevron/right#3x.svg" alt />
</div>
</tr>
</tbody>
</table>
{
"id_store": 4,
"id_section": 1,
"desc_section": "MATERIALES DE CONSTRUCCION yeh",
"id_rule": 1,
"sale_potential": "69413.5525190617"
},
{
"id_store": 4,
"id_section": 2,
"desc_section": "CARPINTERIA Y MADERA",
"id_rule": 1,
"sale_potential": "74704.3439572555"
},
{
"id_store": 4,
"id_section": 3,
"desc_section": "ELECTR-FONTAN-CALOR",
"id_rule": 1,
"sale_potential": "101255.89182774"
}
]
Here's what it might look like if you want to implement yourself, note that this is very basic functionality and as you start to add additional features, you might see more benefit from using a component that already does it all.
Anyhow, the way you can do it is by using a computed (sortedList) to store a sorted version of the array. Then use another data variable to store which column you want to store by (sortBy), and optionally, you can store a sort direction (sortOrder)
then add a sort method that passes the sort key and updates the sortBy value and/or the sortOrder. When either of these values (or even the source array) changes, the computed will re-sort the array using the sort function.
new Vue({
el: "#app",
data: {
sortBy: "id_section",
sortOrder: 1,
userInfo: [
{
"id_store": 4,
"id_section": 1,
"desc_section": "MATERIALES DE CONSTRUCCION yeh",
"id_rule": 1,
"sale_potential": "69413.5525190617"
},
{
"id_store": 4,
"id_section": 2,
"desc_section": "CARPINTERIA Y MADERA",
"id_rule": 1,
"sale_potential": "74704.3439572555"
},
{
"id_store": 4,
"id_section": 3,
"desc_section": "ELECTR-FONTAN-CALOR",
"id_rule": 1,
"sale_potential": "101255.89182774"
}
]
},
computed: {
sortedList() {
return [...this.userInfo]
.map(i => ({...i, sale_potential:parseFloat(i.sale_potential)}))
.sort((a,b) => {
if (a[this.sortBy] >= b[this.sortBy]) {
return this.sortOrder
}
return -this.sortOrder
})
}
},
methods: {
sort: function(sortBy){
if(this.sortBy === sortBy) {
this.sortOrder = -this.sortOrder;
} else {
this.sortBy = sortBy
}
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>
<div id="app">
[{{sortBy}}] [{{sortOrder}}]
<table id="mi-tabla">
<thead>
<tr class="encabezado-derecha">
<th #click='sort("id_section")'>{{ sortBy === 'id_section' ? '*' : '' }}ID</th>
<th #click='sort("desc_section")'>{{ sortBy === 'desc_section' ? '*' : '' }}Nombre de sección</th>
<th #click='sort("sale_potential")'>{{ sortBy === 'sale_potential' ? '*' : '' }}Potencial (€)</th>
</tr>
</thead>
<tbody>
<tr class="item" v-for="user in sortedList">
<td>{{user.id_section}}</td>
<td>{{user.desc_section}}</td>
<div class="acceder">
<td>{{user.sale_potential | currency}}</td>
<img src="../../iconos/icon/chevron/right#3x.svg" alt />
</div>
</tr>
</tbody>
</table>
</div>
I would recommend you to use bootstrap Vue tables which come with filtering and sorting. All you have to do is pass your data to the table.
Here is a link you can check it out.
https://bootstrap-vue.js.org/docs/components/table#complete-example
< script >
export default {
data() {
return {
items: [{
"id_store": 4,
"id_section": 1,
"desc_section": "MATERIALES DE CONSTRUCCION yeh",
"id_rule": 1,
"sale_potential": "69413.5525190617"
},
{
"id_store": 4,
"id_section": 2,
"desc_section": "CARPINTERIA Y MADERA",
"id_rule": 1,
"sale_potential": "74704.3439572555"
},
{
"id_store": 4,
"id_section": 3,
"desc_section": "ELECTR-FONTAN-CALOR",
"id_rule": 1,
"sale_potential": "101255.89182774"
}
],
fields: [{
key: 'id_store',
label: 'id',
sortable: true
}, {
key: 'desc_section',
label: 'Nombre de sección'
}, {
key: 'sale_potential'
},{key:'actions'}]
}
},
} <
/script>
<b-table striped hover :items="items" :fields="fields">
<template v-slot:cell(sale_potential)="row">
<p>{{row.item.sale_potential |currency}}</p>
<img src="../../iconos/icon/chevron/right#3x.svg" alt />
</template>
<template v-slot:cell(actions)="row">
<button #click="openDiv(); showInfo1(row.item.id_section);"
class="btn" variant="primary">Action</button>
</template>
</b-table>
If you want to add this functionality yourself you can achieve it using a computed value to sort your data.
data () => ({
...
sortBy : null,
}),
computed : {
userInfoSorted () {
const sortBy = this.sortBy
if (!sortBy) return this.userInfo
return this.userInfo.sort((a,b)=> a[sortBy] > b[sortBy] ? 1 : -1)
}
}
Then update your sortBy value within the <th> tags in your template:
<th #click="sortBy='id_section'">ID</th>
and link your rows to the computed value:
<tr class="item" v-for="user in userInfoSorted">
EDIT: CHANGE SORT ORDER
To add an option to toggle the order, start by adding the headers to your data object:
data () => ({
...
headers : {
id_section : {
text : 'ID',
reverse : true
}
}
})
Then update your template to also change the reverse value on click:
<th v-for="(val,key) in headers" #click="sortBy=key; val.reverse=!val.reverse">
{{ val.text }}
</th>
Finally include the reverse value in your sort function:
userInfoSorted () {
const sortBy = this.sortBy
const r = this.headers[sortBy].reverse ? -1 : 1;
if (!sortBy) return this.userInfo
return this.userInfo.sort((a,b)=> a[sortBy] > b[sortBy] ? 1*r : -1*r)
}

Resources