feat: build dashboard reporting #1
@@ -65,5 +65,8 @@
|
|||||||
"vite": "^8.0.7",
|
"vite": "^8.0.7",
|
||||||
"wrangler": "^4.81.0",
|
"wrangler": "^4.81.0",
|
||||||
"zod": "^4.4.3"
|
"zod": "^4.4.3"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"date-fns": "^4.4.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+9
@@ -7,6 +7,10 @@ settings:
|
|||||||
importers:
|
importers:
|
||||||
|
|
||||||
.:
|
.:
|
||||||
|
dependencies:
|
||||||
|
date-fns:
|
||||||
|
specifier: ^4.4.0
|
||||||
|
version: 4.4.0
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@better-auth/cli':
|
'@better-auth/cli':
|
||||||
specifier: ~1.4.21
|
specifier: ~1.4.21
|
||||||
@@ -2213,6 +2217,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-G7gHKj89n2owmkGb6WX6ixcnQ0Kf/0wpa9VIh9DGdbHu8wdrlaHU4ir3/bFNERl8N8nn4G7e7qbtBG8N9caihQ==}
|
resolution: {integrity: sha512-G7gHKj89n2owmkGb6WX6ixcnQ0Kf/0wpa9VIh9DGdbHu8wdrlaHU4ir3/bFNERl8N8nn4G7e7qbtBG8N9caihQ==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
|
date-fns@4.4.0:
|
||||||
|
resolution: {integrity: sha512-+1UMbeh68lH1SegH83CGWwpb6OHHbpSgr3+s5Eww5M4CAgswBpoWS0AjTOfEJ33HiYKz1hdj/KTFprzXHmq/6w==}
|
||||||
|
|
||||||
dayjs@1.11.20:
|
dayjs@1.11.20:
|
||||||
resolution: {integrity: sha512-YbwwqR/uYpeoP4pu043q+LTDLFBLApUP6VxRihdfNTqu4ubqMlGDLd6ErXhEgsyvY0K6nCs7nggYumAN+9uEuQ==}
|
resolution: {integrity: sha512-YbwwqR/uYpeoP4pu043q+LTDLFBLApUP6VxRihdfNTqu4ubqMlGDLd6ErXhEgsyvY0K6nCs7nggYumAN+9uEuQ==}
|
||||||
|
|
||||||
@@ -5521,6 +5528,8 @@ snapshots:
|
|||||||
d3-delaunay: 6.0.4
|
d3-delaunay: 6.0.4
|
||||||
d3-scale: 4.0.2
|
d3-scale: 4.0.2
|
||||||
|
|
||||||
|
date-fns@4.4.0: {}
|
||||||
|
|
||||||
dayjs@1.11.20:
|
dayjs@1.11.20:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { addDays, addMonths, endOfMonth, format, parseISO, startOfMonth } from 'date-fns';
|
||||||
import { and, asc, count, desc, eq, gte, inArray, isNull, lt, lte, ne, sql } from 'drizzle-orm';
|
import { and, asc, count, desc, eq, gte, inArray, isNull, lt, lte, ne, sql } from 'drizzle-orm';
|
||||||
import { db } from '$lib/server/db';
|
import { db } from '$lib/server/db';
|
||||||
import {
|
import {
|
||||||
@@ -12,42 +13,20 @@ import {
|
|||||||
import { loadOrganizationContext } from '$lib/server/organizations';
|
import { loadOrganizationContext } from '$lib/server/organizations';
|
||||||
import type { PageServerLoad } from './$types';
|
import type { PageServerLoad } from './$types';
|
||||||
|
|
||||||
const dayMs = 24 * 60 * 60 * 1000;
|
|
||||||
|
|
||||||
function pad(value: number) {
|
|
||||||
return String(value).padStart(2, '0');
|
|
||||||
}
|
|
||||||
|
|
||||||
function dateOnly(date: Date) {
|
function dateOnly(date: Date) {
|
||||||
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`;
|
return format(date, 'yyyy-MM-dd');
|
||||||
}
|
}
|
||||||
|
|
||||||
function dateTimeLocal(date: Date) {
|
function dateTimeLocal(date: Date) {
|
||||||
return `${dateOnly(date)}T${pad(date.getHours())}:${pad(date.getMinutes())}`;
|
return format(date, "yyyy-MM-dd'T'HH:mm");
|
||||||
}
|
|
||||||
|
|
||||||
function startOfMonth(date: Date) {
|
|
||||||
return new Date(date.getFullYear(), date.getMonth(), 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
function endOfMonth(date: Date) {
|
|
||||||
return new Date(date.getFullYear(), date.getMonth() + 1, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
function addDays(date: Date, days: number) {
|
|
||||||
return new Date(date.getTime() + days * dayMs);
|
|
||||||
}
|
|
||||||
|
|
||||||
function addMonths(date: Date, months: number) {
|
|
||||||
return new Date(date.getFullYear(), date.getMonth() + months, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function monthKey(date: Date) {
|
function monthKey(date: Date) {
|
||||||
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}`;
|
return format(date, 'yyyy-MM');
|
||||||
}
|
}
|
||||||
|
|
||||||
function monthLabel(date: Date) {
|
function monthLabel(date: Date) {
|
||||||
return new Intl.DateTimeFormat('en-GB', { month: 'short', year: 'numeric' }).format(date);
|
return format(date, 'MMM yyyy');
|
||||||
}
|
}
|
||||||
|
|
||||||
function roomTypeLabel(type: string) {
|
function roomTypeLabel(type: string) {
|
||||||
@@ -323,7 +302,7 @@ export const load: PageServerLoad = async ({ locals }) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
for (const invoice of billingTrendInvoiceRows) {
|
for (const invoice of billingTrendInvoiceRows) {
|
||||||
const issuedAt = new Date(`${invoice.issueDate}T00:00:00`);
|
const issuedAt = parseISO(invoice.issueDate);
|
||||||
const key = monthKey(issuedAt);
|
const key = monthKey(issuedAt);
|
||||||
const existing = billingTrendMap.get(key);
|
const existing = billingTrendMap.get(key);
|
||||||
if (!existing) continue;
|
if (!existing) continue;
|
||||||
|
|||||||
Reference in New Issue
Block a user