mirror of
https://github.com/frappe/books.git
synced 2024-12-22 19:09:01 +00:00
incr: add MouseFollower
- unpaid invoices tooltips - fix getstarted
This commit is contained in:
parent
1e35635170
commit
e9226f38c8
@ -56,6 +56,10 @@ export class Converter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static toDocValue(value: RawValue, field: Field, fyo: Fyo): DocValue {
|
static toDocValue(value: RawValue, field: Field, fyo: Fyo): DocValue {
|
||||||
|
if (!field?.fieldtype) {
|
||||||
|
console.log(value, field);
|
||||||
|
console.trace();
|
||||||
|
}
|
||||||
switch (field.fieldtype) {
|
switch (field.fieldtype) {
|
||||||
case FieldTypeEnum.Currency:
|
case FieldTypeEnum.Currency:
|
||||||
return toDocCurrency(value, field, fyo);
|
return toDocCurrency(value, field, fyo);
|
||||||
@ -102,6 +106,9 @@ export class Converter {
|
|||||||
for (const fieldname in rawValueMap) {
|
for (const fieldname in rawValueMap) {
|
||||||
const field = fieldValueMap[fieldname];
|
const field = fieldValueMap[fieldname];
|
||||||
const rawValue = rawValueMap[fieldname];
|
const rawValue = rawValueMap[fieldname];
|
||||||
|
if (!field) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (Array.isArray(rawValue)) {
|
if (Array.isArray(rawValue)) {
|
||||||
const parentSchemaName = (field as TargetField).target;
|
const parentSchemaName = (field as TargetField).target;
|
||||||
|
@ -24,8 +24,13 @@
|
|||||||
"fieldtype": "Check"
|
"fieldtype": "Check"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "itemCreated",
|
"fieldname": "salesItemCreated",
|
||||||
"label": "Item Created",
|
"label": "Purchase Item Created",
|
||||||
|
"fieldtype": "Check"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "purchaseItemCreated",
|
||||||
|
"label": "Sales Item Created",
|
||||||
"fieldtype": "Check"
|
"fieldtype": "Check"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -122,7 +122,7 @@
|
|||||||
>
|
>
|
||||||
<div class="flex flex-col justify-center items-center">
|
<div class="flex flex-col justify-center items-center">
|
||||||
<p>
|
<p>
|
||||||
{{ xi > -1 ? xLabels[xi] : '' }}
|
{{ xi > -1 ? formatX(xLabels[xi]) : '' }}
|
||||||
</p>
|
</p>
|
||||||
<p class="font-semibold">
|
<p class="font-semibold">
|
||||||
{{ yi > -1 ? format(points[yi][xi]) : '' }}
|
{{ yi > -1 ? format(points[yi][xi]) : '' }}
|
||||||
|
35
src/components/MouseFollower.vue
Normal file
35
src/components/MouseFollower.vue
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<template>
|
||||||
|
<Tooltip ref="tooltip"><slot></slot></Tooltip>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { defineComponent } from 'vue';
|
||||||
|
import Tooltip from './Tooltip.vue';
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
props: { show: { type: Boolean, default: false } },
|
||||||
|
components: { Tooltip },
|
||||||
|
watch: {
|
||||||
|
show(val) {
|
||||||
|
if (val) {
|
||||||
|
this.$refs.tooltip.create();
|
||||||
|
this.setListeners();
|
||||||
|
} else {
|
||||||
|
this.$refs.tooltip.destroy();
|
||||||
|
this.removeListener();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
mousemoveListener(e) {
|
||||||
|
this.$refs.tooltip.update(e);
|
||||||
|
},
|
||||||
|
setListeners() {
|
||||||
|
window.addEventListener('mousemove', this.mousemoveListener);
|
||||||
|
},
|
||||||
|
removeListener() {
|
||||||
|
window.removeEventListener('mousemove', this.mousemoveListener);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
@ -46,7 +46,8 @@ import { ModelNameEnum } from 'models/types';
|
|||||||
import LineChart from 'src/components/Charts/LineChart.vue';
|
import LineChart from 'src/components/Charts/LineChart.vue';
|
||||||
import { fyo } from 'src/initFyo';
|
import { fyo } from 'src/initFyo';
|
||||||
import { formatXLabels, getYMax } from 'src/utils/chart';
|
import { formatXLabels, getYMax } from 'src/utils/chart';
|
||||||
import { getDatesAndPeriodicity } from 'src/utils/misc';
|
import { getDatesAndPeriodList } from 'src/utils/misc';
|
||||||
|
import { getMapFromList } from 'utils/';
|
||||||
import PeriodSelector from './PeriodSelector';
|
import PeriodSelector from './PeriodSelector';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@ -89,8 +90,25 @@ export default {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
async setData() {
|
async setData() {
|
||||||
const { fromDate, toDate } = await getDatesAndPeriodicity(this.period);
|
const { periodList, fromDate, toDate } = await getDatesAndPeriodList(
|
||||||
this.data = await fyo.db.getCashflow(fromDate, toDate);
|
this.period
|
||||||
|
);
|
||||||
|
|
||||||
|
const data = await fyo.db.getCashflow(fromDate, toDate);
|
||||||
|
const dataMap = getMapFromList(data, 'month-year');
|
||||||
|
this.data = periodList.map((p) => {
|
||||||
|
const key = p.toFormat('yyyy-MM');
|
||||||
|
const item = dataMap[key];
|
||||||
|
if (item) {
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
inflow: 0,
|
||||||
|
outflow: 0,
|
||||||
|
'month-year': key,
|
||||||
|
};
|
||||||
|
});
|
||||||
},
|
},
|
||||||
async setHasData() {
|
async setHasData() {
|
||||||
const accounts = await fyo.db.getAllRaw('Account', {
|
const accounts = await fyo.db.getAllRaw('Account', {
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<div class="flex justify-between gap-10">
|
<div class="flex justify-between gap-10">
|
||||||
<div
|
<div
|
||||||
class="flex-col justify-between flex-1"
|
class="flex-col justify-between flex-1"
|
||||||
v-for="invoice in invoices"
|
v-for="(invoice, i) in invoices"
|
||||||
:key="invoice.title"
|
:key="invoice.title"
|
||||||
>
|
>
|
||||||
<!-- Title and Period Selector -->
|
<!-- Title and Period Selector -->
|
||||||
@ -10,7 +10,7 @@
|
|||||||
<template #title>{{ invoice.title }}</template>
|
<template #title>{{ invoice.title }}</template>
|
||||||
<template #action>
|
<template #action>
|
||||||
<PeriodSelector
|
<PeriodSelector
|
||||||
v-if="invoice.count"
|
v-if="invoice.hasData"
|
||||||
:value="$data[invoice.periodKey]"
|
:value="$data[invoice.periodKey]"
|
||||||
@change="(value) => ($data[invoice.periodKey] = value)"
|
@change="(value) => ($data[invoice.periodKey] = value)"
|
||||||
/>
|
/>
|
||||||
@ -53,7 +53,11 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Widget Bar -->
|
<!-- Widget Bar -->
|
||||||
<div class="mt-2 relative rounded overflow-hidden">
|
<div
|
||||||
|
class="mt-2 relative rounded overflow-hidden"
|
||||||
|
@mouseenter="idx = i"
|
||||||
|
@mouseleave="idx = -1"
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
class="w-full h-4"
|
class="w-full h-4"
|
||||||
:class="
|
:class="
|
||||||
@ -74,14 +78,33 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<MouseFollower
|
||||||
|
v-if="invoices[0].hasData || invoices[1].hasData"
|
||||||
|
:show="idx >= 0"
|
||||||
|
class="text-sm shadow-md px-2 py-1 bg-white text-gray-900 border-l-2"
|
||||||
|
:style="{ borderColor: colors[idx] }"
|
||||||
|
>
|
||||||
|
<div class="flex justify-between gap-4">
|
||||||
|
<p>{{ t`Paid` }}</p>
|
||||||
|
<p class="font-semibold">{{ invoices[idx]?.paidCount ?? 0 }}</p>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="invoices[idx]?.unpaidCount > 0"
|
||||||
|
class="flex justify-between gap-4"
|
||||||
|
>
|
||||||
|
<p>{{ t`Unpaid` }}</p>
|
||||||
|
<p class="font-semibold">{{ invoices[idx]?.unpaidCount ?? 0 }}</p>
|
||||||
|
</div>
|
||||||
|
</MouseFollower>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import { t } from 'fyo';
|
import { t } from 'fyo';
|
||||||
import { ModelNameEnum } from 'models/types';
|
import { ModelNameEnum } from 'models/types';
|
||||||
import Button from 'src/components/Button.vue';
|
import Button from 'src/components/Button.vue';
|
||||||
|
import MouseFollower from 'src/components/MouseFollower.vue';
|
||||||
import { fyo } from 'src/initFyo';
|
import { fyo } from 'src/initFyo';
|
||||||
import { getDatesAndPeriodicity } from 'src/utils/misc';
|
import { getDatesAndPeriodList } from 'src/utils/misc';
|
||||||
import { routeTo } from 'src/utils/ui';
|
import { routeTo } from 'src/utils/ui';
|
||||||
import PeriodSelector from './PeriodSelector.vue';
|
import PeriodSelector from './PeriodSelector.vue';
|
||||||
import SectionHeader from './SectionHeader.vue';
|
import SectionHeader from './SectionHeader.vue';
|
||||||
@ -92,16 +115,22 @@ export default {
|
|||||||
PeriodSelector,
|
PeriodSelector,
|
||||||
SectionHeader,
|
SectionHeader,
|
||||||
Button,
|
Button,
|
||||||
|
MouseFollower,
|
||||||
},
|
},
|
||||||
data: () => ({
|
data: () => ({
|
||||||
|
idx: -1,
|
||||||
|
colors: ['#33A1FF', '#B7BFC6'],
|
||||||
invoices: [
|
invoices: [
|
||||||
{
|
{
|
||||||
title: t`Sales Invoices`,
|
title: t`Sales Invoices`,
|
||||||
schemaName: ModelNameEnum.SalesInvoice,
|
schemaName: ModelNameEnum.SalesInvoice,
|
||||||
total: 0,
|
total: 0,
|
||||||
unpaid: 0,
|
unpaid: 0,
|
||||||
|
hasData: false,
|
||||||
paid: 0,
|
paid: 0,
|
||||||
count: 0,
|
count: 0,
|
||||||
|
unpaidCount: 0,
|
||||||
|
paidCount: 0,
|
||||||
color: 'blue',
|
color: 'blue',
|
||||||
periodKey: 'salesInvoicePeriod',
|
periodKey: 'salesInvoicePeriod',
|
||||||
barWidth: 40,
|
barWidth: 40,
|
||||||
@ -111,8 +140,11 @@ export default {
|
|||||||
schemaName: ModelNameEnum.PurchaseInvoice,
|
schemaName: ModelNameEnum.PurchaseInvoice,
|
||||||
total: 0,
|
total: 0,
|
||||||
unpaid: 0,
|
unpaid: 0,
|
||||||
|
hasData: false,
|
||||||
paid: 0,
|
paid: 0,
|
||||||
count: 0,
|
count: 0,
|
||||||
|
unpaidCount: 0,
|
||||||
|
paidCount: 0,
|
||||||
color: 'gray',
|
color: 'gray',
|
||||||
periodKey: 'purchaseInvoicePeriod',
|
periodKey: 'purchaseInvoicePeriod',
|
||||||
barWidth: 60,
|
barWidth: 60,
|
||||||
@ -131,7 +163,7 @@ export default {
|
|||||||
methods: {
|
methods: {
|
||||||
async calculateInvoiceTotals() {
|
async calculateInvoiceTotals() {
|
||||||
for (const invoice of this.invoices) {
|
for (const invoice of this.invoices) {
|
||||||
const { fromDate, toDate } = await getDatesAndPeriodicity(
|
const { fromDate, toDate } = await getDatesAndPeriodList(
|
||||||
this.$data[invoice.periodKey]
|
this.$data[invoice.periodKey]
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -141,14 +173,19 @@ export default {
|
|||||||
toDate
|
toDate
|
||||||
);
|
);
|
||||||
|
|
||||||
const count = await fyo.db.count(invoice.schemaName, {
|
const { countTotal, countOutstanding } = await this.getCounts(
|
||||||
filters: { cancelled: false, submitted: true },
|
invoice.schemaName,
|
||||||
});
|
DateTime.fromISO(fromDate),
|
||||||
|
DateTime.fromISO(toDate)
|
||||||
|
);
|
||||||
|
|
||||||
invoice.total = total ?? 0;
|
invoice.total = total ?? 0;
|
||||||
invoice.unpaid = outstanding ?? 0;
|
invoice.unpaid = outstanding ?? 0;
|
||||||
invoice.paid = total - outstanding;
|
invoice.paid = total - outstanding;
|
||||||
invoice.count = count;
|
invoice.hasData = countTotal > 0;
|
||||||
|
invoice.count = countTotal;
|
||||||
|
invoice.paidCount = countTotal - countOutstanding;
|
||||||
|
invoice.unpaidCount = countOutstanding;
|
||||||
invoice.barWidth = (invoice.paid / (invoice.total || 1)) * 100;
|
invoice.barWidth = (invoice.paid / (invoice.total || 1)) * 100;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -156,6 +193,26 @@ export default {
|
|||||||
let doc = await fyo.doc.getNewDoc(invoice.schemaName);
|
let doc = await fyo.doc.getNewDoc(invoice.schemaName);
|
||||||
routeTo(`/edit/${invoice.schemaName}/${doc.name}`);
|
routeTo(`/edit/${invoice.schemaName}/${doc.name}`);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async getCounts(schemaName, fromDate, toDate) {
|
||||||
|
const outstandingAmounts = await fyo.db.getAllRaw(schemaName, {
|
||||||
|
fields: ['outstandingAmount'],
|
||||||
|
filters: {
|
||||||
|
cancelled: false,
|
||||||
|
submitted: true,
|
||||||
|
date: ['<=', toDate.toISO(), '>=', fromDate.toISO()],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const isOutstanding = outstandingAmounts.map((o) =>
|
||||||
|
parseFloat(o.outstandingAmount)
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
countTotal: isOutstanding.length,
|
||||||
|
countOutstanding: isOutstanding.filter((o) => o > 0).length,
|
||||||
|
};
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
@ -166,43 +166,40 @@ export default {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!fyo.singles.GetStarted.itemCreated) {
|
if (!fyo.singles.GetStarted.salesItemCreated) {
|
||||||
const count = await fyo.db.count('Item');
|
const count = await fyo.db.count('Item', { filters: { for: 'Sales' } });
|
||||||
if (count > 0) {
|
toUpdate.salesItemCreated = count > 0;
|
||||||
toUpdate.itemCreated = 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!fyo.singles.GetStarted.purchaseItemCreated) {
|
||||||
|
const count = await fyo.db.count('Item', {
|
||||||
|
filters: { for: 'Purchases' },
|
||||||
|
});
|
||||||
|
toUpdate.purchaseItemCreated = count > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!fyo.singles.GetStarted.invoiceCreated) {
|
if (!fyo.singles.GetStarted.invoiceCreated) {
|
||||||
const count = await fyo.db.count('SalesInvoice');
|
const count = await fyo.db.count('SalesInvoice');
|
||||||
if (count > 0) {
|
toUpdate.invoiceCreated = count > 0;
|
||||||
toUpdate.invoiceCreated = 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!fyo.singles.GetStarted.customerCreated) {
|
if (!fyo.singles.GetStarted.customerCreated) {
|
||||||
const count = fyo.db.count('Party', {
|
const count = await fyo.db.count('Party', {
|
||||||
filters: { role: 'Customer' },
|
filters: { role: 'Customer' },
|
||||||
});
|
});
|
||||||
if (count > 0) {
|
toUpdate.customerCreated = count > 0;
|
||||||
toUpdate.customerCreated = 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!fyo.singles.GetStarted.billCreated) {
|
if (!fyo.singles.GetStarted.billCreated) {
|
||||||
const count = await fyo.db.count('SalesInvoice');
|
const count = await fyo.db.count('SalesInvoice');
|
||||||
if (count > 0) {
|
toUpdate.billCreated = count > 0;
|
||||||
toUpdate.billCreated = 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!fyo.singles.GetStarted.supplierCreated) {
|
if (!fyo.singles.GetStarted.supplierCreated) {
|
||||||
const count = fyo.db.count('Party', {
|
const count = await fyo.db.count('Party', {
|
||||||
filters: { role: 'Supplier' },
|
filters: { role: 'Supplier' },
|
||||||
});
|
});
|
||||||
if (count > 0) {
|
toUpdate.supplierCreated = count > 0;
|
||||||
toUpdate.supplierCreated = 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
await this.updateChecks(toUpdate);
|
await this.updateChecks(toUpdate);
|
||||||
},
|
},
|
||||||
@ -211,10 +208,10 @@ export default {
|
|||||||
await fyo.doc.getSingle('GetStarted');
|
await fyo.doc.getSingle('GetStarted');
|
||||||
},
|
},
|
||||||
isCompleted(item) {
|
isCompleted(item) {
|
||||||
return fyo.singles.GetStarted.get(item.fieldname) || 0;
|
return fyo.singles.GetStarted.get(item.fieldname) || false;
|
||||||
},
|
},
|
||||||
getIconComponent(item) {
|
getIconComponent(item) {
|
||||||
let completed = fyo.singles.GetStarted[item.fieldname] || 0;
|
let completed = fyo.singles.GetStarted[item.fieldname] || false;
|
||||||
let name = completed ? 'green-check' : item.icon;
|
let name = completed ? 'green-check' : item.icon;
|
||||||
let size = completed ? '24' : '18';
|
let size = completed ? '24' : '18';
|
||||||
return {
|
return {
|
||||||
|
@ -84,8 +84,8 @@ export function getGetStartedConfig() {
|
|||||||
label: t`Add Items`,
|
label: t`Add Items`,
|
||||||
icon: 'item',
|
icon: 'item',
|
||||||
description: t`Add products or services that you sell to your customers`,
|
description: t`Add products or services that you sell to your customers`,
|
||||||
action: () => routeTo('/list/Item'),
|
action: () => routeTo(`/list/Item/for/Sales/${t`Sales Items`}`),
|
||||||
fieldname: 'itemCreated',
|
fieldname: 'salesItemCreated',
|
||||||
documentation: 'https://frappebooks.com/docs/setting-up#3-add-items',
|
documentation: 'https://frappebooks.com/docs/setting-up#3-add-items',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -93,7 +93,7 @@ export function getGetStartedConfig() {
|
|||||||
label: t`Add Customers`,
|
label: t`Add Customers`,
|
||||||
icon: 'customer',
|
icon: 'customer',
|
||||||
description: t`Add a few customers to create your first invoice`,
|
description: t`Add a few customers to create your first invoice`,
|
||||||
action: () => routeTo('/list/Customer'),
|
action: () => routeTo(`/list/Party/role/Customer/${t`Customers`}`),
|
||||||
fieldname: 'customerCreated',
|
fieldname: 'customerCreated',
|
||||||
documentation:
|
documentation:
|
||||||
'https://frappebooks.com/docs/setting-up#4-add-customers',
|
'https://frappebooks.com/docs/setting-up#4-add-customers',
|
||||||
@ -118,15 +118,16 @@ export function getGetStartedConfig() {
|
|||||||
label: t`Add Items`,
|
label: t`Add Items`,
|
||||||
icon: 'item',
|
icon: 'item',
|
||||||
description: t`Add products or services that you buy from your suppliers`,
|
description: t`Add products or services that you buy from your suppliers`,
|
||||||
action: () => routeTo('/list/Item'),
|
action: () =>
|
||||||
fieldname: 'itemCreated',
|
routeTo(`/list/Item/for/Purchases/${t`Purchase Items`}`),
|
||||||
|
fieldname: 'purchaseItemCreated',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'Add Suppliers',
|
key: 'Add Suppliers',
|
||||||
label: t`Add Suppliers`,
|
label: t`Add Suppliers`,
|
||||||
icon: 'supplier',
|
icon: 'supplier',
|
||||||
description: t`Add a few suppliers to create your first bill`,
|
description: t`Add a few suppliers to create your first bill`,
|
||||||
action: () => routeTo('/list/Supplier'),
|
action: () => routeTo(`/list/Party/role/Supplier/${t`Suppliers`}`),
|
||||||
fieldname: 'supplierCreated',
|
fieldname: 'supplierCreated',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -7,9 +7,9 @@ import SetupWizardSchema from 'schemas/app/SetupWizard.json';
|
|||||||
import { Schema } from 'schemas/types';
|
import { Schema } from 'schemas/types';
|
||||||
import { fyo } from 'src/initFyo';
|
import { fyo } from 'src/initFyo';
|
||||||
|
|
||||||
export function getDatesAndPeriodicity(
|
export function getDatesAndPeriodList(
|
||||||
period: 'This Year' | 'This Quarter' | 'This Month'
|
period: 'This Year' | 'This Quarter' | 'This Month'
|
||||||
): { fromDate: string; toDate: string } {
|
): { periodList: DateTime[]; fromDate: string; toDate: string } {
|
||||||
const toDate: DateTime = DateTime.now();
|
const toDate: DateTime = DateTime.now();
|
||||||
let fromDate: DateTime;
|
let fromDate: DateTime;
|
||||||
|
|
||||||
@ -23,7 +23,22 @@ export function getDatesAndPeriodicity(
|
|||||||
fromDate = toDate.minus({ days: 1 });
|
fromDate = toDate.minus({ days: 1 });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* periodList: Monthly decrements before toDate until fromDate
|
||||||
|
*/
|
||||||
|
const periodList: DateTime[] = [toDate];
|
||||||
|
while (true) {
|
||||||
|
const nextDate = periodList.at(0)!.minus({ months: 1 });
|
||||||
|
if (nextDate.toMillis() < fromDate.toMillis()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
periodList.unshift(nextDate);
|
||||||
|
}
|
||||||
|
periodList.shift();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
periodList,
|
||||||
fromDate: fromDate.toISO(),
|
fromDate: fromDate.toISO(),
|
||||||
toDate: toDate.toISO(),
|
toDate: toDate.toISO(),
|
||||||
};
|
};
|
||||||
|
@ -29,3 +29,21 @@ export function useKeys(callback?: (keys: Set<string>) => void) {
|
|||||||
|
|
||||||
return keys;
|
return keys;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function useMouseLocation() {
|
||||||
|
const loc = ref({ clientX: 0, clientY: 0 });
|
||||||
|
|
||||||
|
const mousemoveListener = (e: MouseEvent) => {
|
||||||
|
loc.value.clientX = e.clientX;
|
||||||
|
loc.value.clientY = e.clientY;
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
window.addEventListener('mousemove', mousemoveListener);
|
||||||
|
});
|
||||||
|
onUnmounted(() => {
|
||||||
|
window.removeEventListener('mousemove', mousemoveListener);
|
||||||
|
});
|
||||||
|
|
||||||
|
return loc;
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user