Add database (#153)

* add database to project. Seed data. Update customerId to customer_id

* seed customers table data

* use database everywhere

* refactor

* fix ts lint errors

* add type to invoice edit page

* remove fetch-data file and fetch data directly in components

* update lates invoices to use sql

* in invoice table,  search the database here with SQL

* rename tsx files to ts and add node script to seed data

* address rest of PR comments

* move all data fetches to own file

* add types to filter invoices function

* remove unused param

* prettier

* update function names
This commit is contained in:
Stephanie Dietz
2023-09-13 12:16:09 -05:00
committed by GitHub
parent 5f38f0fa81
commit 7e4b69351e
17 changed files with 689 additions and 84 deletions

7
dashboard/15-final/.env Normal file
View File

@@ -0,0 +1,7 @@
POSTGRES_URL="postgres://default:IQA2FonHJi9d@ep-square-union-29037633-pooler.us-east-1.postgres.vercel-storage.com:5432/verceldb"
POSTGRES_PRISMA_URL="postgres://default:IQA2FonHJi9d@ep-square-union-29037633-pooler.us-east-1.postgres.vercel-storage.com:5432/verceldb?pgbouncer=true&connect_timeout=15"
POSTGRES_URL_NON_POOLING="postgres://default:IQA2FonHJi9d@ep-square-union-29037633.us-east-1.postgres.vercel-storage.com:5432/verceldb"
POSTGRES_USER="default"
POSTGRES_HOST="ep-square-union-29037633-pooler.us-east-1.postgres.vercel-storage.com"
POSTGRES_PASSWORD="IQA2FonHJi9d"
POSTGRES_DATABASE="verceldb"

View File

@@ -1,10 +1,12 @@
import InvoiceForm from '@/app/ui/invoices/form';
import { invoices } from '@/app/lib/dummy-data';
import { notFound } from 'next/navigation';
import { Invoice } from '@/app/lib/definitions';
import { fetchInvoiceById } from '@/app/lib/data-fetches';
export default function Page({ params }: { params: { id: string } }) {
export default async function Page({ params }: { params: { id: string } }) {
const id = params.id ? parseInt(params.id) : null;
const invoice = invoices.find((invoice) => invoice.id === id);
const invoiceData = await fetchInvoiceById(id);
const invoice = invoiceData.rows[0] as Invoice;
if (!invoice) {
notFound();

View File

@@ -1,4 +1,5 @@
import { Invoice, Revenue } from './definitions';
import { fetchLatestInvoices } from './data-fetches';
export const calculateAllInvoices = (
invoices: Invoice[],
@@ -19,7 +20,7 @@ export const calculateCustomerInvoices = (
customerId: number,
) => {
return invoices
.filter((invoice) => invoice.customerId === customerId)
.filter((invoice) => invoice.customer_id === customerId)
.filter((invoice) => !status || invoice.status === status)
.reduce((total, invoice) => total + invoice.amount / 100, 0)
.toLocaleString('en-US', {
@@ -38,13 +39,12 @@ export const countCustomerInvoices = (
invoices: Invoice[],
customerId: number,
) => {
return invoices.filter((invoice) => invoice.customerId === customerId).length;
return invoices.filter((invoice) => invoice.customer_id === customerId)
.length;
};
export const findLatestInvoices = (invoices: Invoice[]) => {
return [...invoices]
.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime())
.slice(0, 5);
export const findLatestInvoices = async () => {
return await fetchLatestInvoices();
};
export const generateYAxis = (revenue: Revenue[]) => {

View File

@@ -0,0 +1,65 @@
import { sql } from '@vercel/postgres';
export async function fetchAllInvoices() {
const invoicesData = await sql`SELECT * FROM invoices`;
return invoicesData.rows;
}
export async function fetchAllCustomers() {
const customersData = await sql`SELECT * FROM customers`;
return customersData.rows;
}
export async function fetchAllRevenue() {
const revenueData = await sql`SELECT * FROM revenue`;
return revenueData.rows;
}
export async function fetchFilteredInvoices(
searchTerm: string,
currentPage: number,
ITEMS_PER_PAGE: number,
) {
const invoicesData = await sql`
SELECT
invoices.*,
customers.name AS customer_name,
customers.email AS customer_email,
customers.image_url AS customer_image
FROM
invoices
JOIN
customers ON invoices.customer_id = customers.id
WHERE
invoices.id::text ILIKE ${`%${searchTerm}%`} OR
customers.name ILIKE ${`%${searchTerm}%`} OR
customers.email ILIKE ${`%${searchTerm}%`} OR
invoices.amount::text ILIKE ${`%${searchTerm}%`} OR
invoices.date::text ILIKE ${`%${searchTerm}%`} OR
invoices.status ILIKE ${`%${searchTerm}%`}
LIMIT ${ITEMS_PER_PAGE}
OFFSET ${(currentPage - 1) * ITEMS_PER_PAGE}
`;
return invoicesData.rows;
}
export async function fetchInvoiceCountBySearchTerm(searchTerm: string) {
const { rows: countRows } = await sql`
SELECT COUNT(*)
FROM invoices
LEFT JOIN customers ON invoices.customer_id = customers.id
WHERE (invoices.id::text ILIKE ${`%${searchTerm}%`} OR customers.name ILIKE ${`%${searchTerm}%`} OR customers.email ILIKE ${`%${searchTerm}%`})
`;
return countRows[0].count;
}
export async function fetchInvoiceById(id: number | null) {
return await sql`SELECT * from INVOICES where id=${id}`;
}
export async function fetchLatestInvoices() {
const latestInvoices = await sql`SELECT * FROM invoices
ORDER BY date DESC
LIMIT 5;`;
return latestInvoices.rows;
}

View File

@@ -12,12 +12,12 @@ export type Customer = {
id: number;
name: string;
email: string;
imageUrl: string;
image_url: string;
};
export type Invoice = {
id: number;
customerId: number;
customer_id: number;
amount: number;
// In TypeScript, this is called a string union type.
// It means that the "status" property can only be one of the two strings: 'pending' or 'paid'.

View File

@@ -1,7 +1,5 @@
import { User, Customer, Invoice, Revenue } from './definitions';
// This file contains dummy data that you'll be replacing with real data in Chapter 7.
export const users: User[] = [
const users = [
{
id: 1,
name: 'User',
@@ -10,142 +8,142 @@ export const users: User[] = [
},
];
export const customers: Customer[] = [
const customers = [
{
id: 1,
name: 'Ada Lovelace',
email: 'ada@lovelace.com',
imageUrl: '/customers/ada-lovelace.png',
image_url: '/customers/ada-lovelace.png',
},
{
id: 2,
name: 'Grace Hopper',
email: 'grace@hopper.com',
imageUrl: '/customers/grace-hopper.png',
image_url: '/customers/grace-hopper.png',
},
{
id: 3,
name: 'Hedy Lammar',
email: 'hedy@lammar.com',
imageUrl: '/customers/hedy-lammar.png',
image_url: '/customers/hedy-lammar.png',
},
{
id: 4,
name: 'Margaret Hamilton',
email: 'margaret@hamilton.com',
imageUrl: '/customers/margaret-hamilton.png',
image_url: '/customers/margaret-hamilton.png',
},
];
export const invoices: Invoice[] = [
const invoices = [
{
id: 1,
customerId: 1,
customer_id: 1,
amount: 15795,
status: 'pending',
date: '2023-12-06',
},
{
id: 2,
customerId: 2,
customer_id: 2,
amount: 20348,
status: 'pending',
date: '2023-11-14',
},
{
id: 3,
customerId: 3,
customer_id: 3,
amount: 3040,
status: 'paid',
date: '2023-10-29',
},
{
id: 4,
customerId: 4,
customer_id: 4,
amount: 44800,
status: 'paid',
date: '2023-09-10',
},
{
id: 5,
customerId: 1,
customer_id: 1,
amount: 34577,
status: 'pending',
date: '2023-08-05',
},
{
id: 6,
customerId: 2,
customer_id: 2,
amount: 54246,
status: 'pending',
date: '2023-07-16',
},
{
id: 7,
customerId: 3,
customer_id: 3,
amount: 8945,
status: 'pending',
date: '2023-06-27',
},
{
id: 8,
customerId: 4,
customer_id: 4,
amount: 32545,
status: 'paid',
date: '2023-06-09',
},
{
id: 9,
customerId: 3,
customer_id: 3,
amount: 1250,
status: 'paid',
date: '2023-06-17',
},
{
id: 10,
customerId: 1,
customer_id: 1,
amount: 8945,
status: 'paid',
date: '2023-06-07',
},
{
id: 11,
customerId: 2,
customer_id: 2,
amount: 500,
status: 'paid',
date: '2023-08-19',
},
{
id: 12,
customerId: 3,
customer_id: 3,
amount: 8945,
status: 'paid',
date: '2023-06-03',
},
{
id: 13,
customerId: 3,
customer_id: 3,
amount: 8945,
status: 'paid',
date: '2023-06-18',
},
{
id: 14,
customerId: 4,
customer_id: 4,
amount: 8945,
status: 'paid',
date: '2023-10-04',
},
{
id: 15,
customerId: 3,
customer_id: 3,
amount: 1000,
status: 'paid',
date: '2022-06-05',
},
];
export const revenue: Revenue[] = [
const revenue = [
{ month: 'Jan', revenue: 2000 },
{ month: 'Feb', revenue: 1800 },
{ month: 'Mar', revenue: 2200 },
@@ -159,3 +157,10 @@ export const revenue: Revenue[] = [
{ month: 'Nov', revenue: 3000 },
{ month: 'Dec', revenue: 4800 },
];
module.exports = {
users,
customers,
invoices,
revenue,
};

View File

@@ -1,11 +1,15 @@
import { customers, invoices } from '@/app/lib/dummy-data';
import {
countCustomerInvoices,
calculateCustomerInvoices,
} from '@/app/lib/calculations';
import { Customer, Invoice } from '@/app/lib/definitions';
import { fetchAllCustomers, fetchAllInvoices } from '@/app/lib/data-fetches';
import Image from 'next/image';
export default function CustomersTable() {
export default async function CustomersTable() {
const invoices = (await fetchAllInvoices()) as Invoice[];
const customers = (await fetchAllCustomers()) as Customer[];
return (
<div className="w-full">
<div className="flex w-full items-center justify-between">
@@ -42,7 +46,7 @@ export default function CustomersTable() {
<td className="whitespace-nowrap py-4 pl-4 pr-3 text-sm font-medium text-black sm:pl-6">
<div className="flex items-center gap-3">
<Image
src={customer.imageUrl}
src={customer.image_url}
className="rounded-full"
alt={customer.name}
width={28}

View File

@@ -3,14 +3,14 @@ import { Customer, Invoice } from '@/app/lib/definitions';
import { findLatestInvoices } from '@/app/lib/calculations';
import Image from 'next/image';
export default function LatestInvoices({
export default async function LatestInvoices({
invoices,
customers,
}: {
invoices: Invoice[];
customers: Customer[];
}) {
const lastFiveInvoices = findLatestInvoices(invoices);
const lastFiveInvoices = await findLatestInvoices();
return (
<div className="w-full rounded-xl border p-6 shadow-sm md:col-span-4 lg:col-span-3">
@@ -18,7 +18,7 @@ export default function LatestInvoices({
{lastFiveInvoices.map((invoice) => {
const customer = customers.find(
(customer) => customer.id === invoice.customerId,
(customer) => customer.id === invoice.customer_id,
);
return (
<div
@@ -27,7 +27,7 @@ export default function LatestInvoices({
>
<div className="flex items-center">
<Image
src={customer?.imageUrl || ''}
src={customer?.image_url || ''}
alt={customer?.name || ''}
className="mr-4 rounded-full"
width={32}

View File

@@ -1,10 +1,19 @@
import Card from '@/app/ui/dashboard/card';
import { invoices, customers, revenue } from '@/app/lib/dummy-data';
import { calculateAllInvoices } from '@/app/lib/calculations';
import RevenueChart from '@/app/ui/dashboard/revenue-chart';
import LatestInvoices from '@/app/ui/dashboard/latest-invoices';
import { Customer, Invoice, Revenue } from '@/app/lib/definitions';
import {
fetchAllCustomers,
fetchAllInvoices,
fetchAllRevenue,
} from '@/app/lib/data-fetches';
export default async function DashboardOverview() {
const invoices = (await fetchAllInvoices()) as Invoice[];
const customers = (await fetchAllCustomers()) as Customer[];
const revenue = (await fetchAllRevenue()) as Revenue[];
export default function DashboardOverview() {
const totalPaidInvoices = calculateAllInvoices(invoices, 'paid');
const totalPendingInvoices = calculateAllInvoices(invoices, 'pending');
const numberOfInvoices = invoices.length;

View File

@@ -16,7 +16,7 @@ export default function InvoiceForm({
}) {
// TO DO: Replace state and handleSubmit with a Server Action
const customer = customers.find(
(customer) => customer.id === invoice?.customerId,
(customer) => customer.id === invoice?.customer_id,
);
const initialCustomer = customer ? customer.id : 0;
const initialAmount = invoice?.amount ? invoice.amount / 100 : 0;
@@ -31,7 +31,7 @@ export default function InvoiceForm({
if (selectedCustomer && amount) {
const newInvoice: Invoice = {
customerId: selectedCustomer,
customer_id: selectedCustomer,
amount: amount * 100, // Convert to cents
// These would be generated on the server

View File

@@ -1,5 +1,3 @@
import { invoices, customers } from '@/app/lib/dummy-data';
import { Customer } from '@/app/lib/definitions';
import Link from 'next/link';
import Image from 'next/image';
import {
@@ -10,6 +8,10 @@ import {
import DeleteInvoice from '@/app/ui/invoices/delete-button';
import TableSearch from './table-search';
import PaginationButtons from './pagination';
import {
fetchFilteredInvoices,
fetchInvoiceCountBySearchTerm,
} from '@/app/lib/data-fetches';
const ITEMS_PER_PAGE = 10;
@@ -42,7 +44,7 @@ function formatDateToLocal(dateStr: string, locale: string = 'en-US') {
return formatter.format(date);
}
export default function InvoicesTable({
export default async function InvoicesTable({
searchParams,
}: {
searchParams: {
@@ -53,35 +55,14 @@ export default function InvoicesTable({
const searchTerm = searchParams.query ?? '';
const currentPage = parseInt(searchParams.page ?? '1');
const filteredInvoices = invoices.filter((invoice) => {
const customer = getCustomerById(invoice.customerId);
const invoiceMatches = Object.values(invoice).some(
(value) =>
value?.toString().toLowerCase().includes(searchTerm.toLowerCase()),
);
const customerMatches =
customer &&
Object.values(customer).some(
(value) =>
value?.toString().toLowerCase().includes(searchTerm.toLowerCase()),
);
return invoiceMatches || customerMatches;
});
const paginatedInvoices = filteredInvoices.slice(
(currentPage - 1) * ITEMS_PER_PAGE,
currentPage * ITEMS_PER_PAGE,
const invoices = await fetchFilteredInvoices(
searchTerm,
currentPage,
ITEMS_PER_PAGE,
);
function getCustomerById(customerId: number): Customer | null {
const customer = customers.find((customer) => customer.id === customerId);
return customer ? customer : null;
}
const totalPages = Math.ceil(filteredInvoices.length / ITEMS_PER_PAGE);
const totalCount = await fetchInvoiceCountBySearchTerm(searchTerm);
const totalPages = Math.ceil(totalCount / ITEMS_PER_PAGE);
return (
<div className="w-full">
@@ -132,7 +113,7 @@ export default function InvoicesTable({
</tr>
</thead>
<tbody className="divide-y divide-gray-200 text-gray-500">
{paginatedInvoices.map((invoice) => (
{invoices?.map((invoice) => (
<tr key={invoice.id}>
<td className="whitespace-nowrap py-4 pl-4 pr-3 text-sm font-medium text-black sm:pl-6">
{invoice.id}
@@ -140,18 +121,17 @@ export default function InvoicesTable({
<td className="whitespace-nowrap px-3 py-4 text-sm">
<div className="flex items-center gap-3">
<Image
src={`${getCustomerById(invoice.customerId)
?.imageUrl}`}
src={`${invoice.customer_image}`}
className="rounded-full"
alt="Customer Image"
width={28}
height={28}
/>
<p>{getCustomerById(invoice.customerId)?.name}</p>
<p>{invoice.customer_name}</p>
</div>
</td>
<td className="whitespace-nowrap px-3 py-4 text-sm">
{getCustomerById(invoice.customerId)?.email}
{invoice.customer_email}
</td>
<td className="whitespace-nowrap px-3 py-4 text-sm">
{(invoice.amount / 100).toLocaleString('en-US', {

View File

@@ -13,6 +13,7 @@
"@types/node": "20.5.7",
"@types/react": "18.2.21",
"@types/react-dom": "18.2.7",
"@vercel/postgres": "^0.4.1",
"autoprefixer": "10.4.15",
"clsx": "^2.0.0",
"next": "13.4.19",
@@ -22,6 +23,9 @@
"tailwindcss": "3.3.3",
"typescript": "5.2.2"
},
"devDependencies": {
"dotenv": "^16.3.1"
},
"engines": {
"node": ">=18"
}
@@ -88,6 +92,14 @@
"@jridgewell/sourcemap-codec": "^1.4.14"
}
},
"node_modules/@neondatabase/serverless": {
"version": "0.5.6",
"resolved": "https://registry.npmjs.org/@neondatabase/serverless/-/serverless-0.5.6.tgz",
"integrity": "sha512-Ru0lG6W/nQtHRkDFVQFF+1PJYx8wd3jereln0Ep0YkiHey50hjTLVUycQoE4X977605pXMuFWORweuktzph+Xg==",
"dependencies": {
"@types/pg": "8.6.6"
}
},
"node_modules/@next/env": {
"version": "13.4.19",
"resolved": "https://registry.npmjs.org/@next/env/-/env-13.4.19.tgz",
@@ -284,6 +296,16 @@
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.7.tgz",
"integrity": "sha512-dP7f3LdZIysZnmvP3ANJYTSwg+wLLl8p7RqniVlV7j+oXSXAbt9h0WIBFmJy5inWZoX9wZN6eXx+YXd9Rh3RBA=="
},
"node_modules/@types/pg": {
"version": "8.6.6",
"resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.6.6.tgz",
"integrity": "sha512-O2xNmXebtwVekJDD+02udOncjVcMZQuTEQEMpKJ0ZRf5E7/9JJX3izhKUcUifBkyKpljyUM6BTgy2trmviKlpw==",
"dependencies": {
"@types/node": "*",
"pg-protocol": "*",
"pg-types": "^2.2.0"
}
},
"node_modules/@types/prop-types": {
"version": "15.7.5",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz",
@@ -312,6 +334,20 @@
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz",
"integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ=="
},
"node_modules/@vercel/postgres": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/@vercel/postgres/-/postgres-0.4.1.tgz",
"integrity": "sha512-rYlNnaXrr2/NWK/OodhAUyed0bomaizKKC8XXjNYv8I1K3m75oocP4IGTcBpZe76VCrHuaKW5d6jLQnuRRoNKg==",
"dependencies": {
"@neondatabase/serverless": "0.5.6",
"bufferutil": "4.0.7",
"utf-8-validate": "6.0.3",
"ws": "8.13.0"
},
"engines": {
"node": ">=14.6"
}
},
"node_modules/any-promise": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
@@ -434,6 +470,18 @@
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
}
},
"node_modules/bufferutil": {
"version": "4.0.7",
"resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.7.tgz",
"integrity": "sha512-kukuqc39WOHtdxtw4UScxF/WVnMFVSQVKhtx3AjZJzhd0RGZZldcrfSEbVsWWe6KNH253574cq5F+wpv0G9pJw==",
"hasInstallScript": true,
"dependencies": {
"node-gyp-build": "^4.3.0"
},
"engines": {
"node": ">=6.14.2"
}
},
"node_modules/busboy": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
@@ -561,6 +609,18 @@
"resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
"integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA=="
},
"node_modules/dotenv": {
"version": "16.3.1",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz",
"integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==",
"dev": true,
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/motdotla/dotenv?sponsor=1"
}
},
"node_modules/electron-to-chromium": {
"version": "1.4.503",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.503.tgz",
@@ -921,6 +981,16 @@
"node": "^10 || ^12 || >=14"
}
},
"node_modules/node-gyp-build": {
"version": "4.6.1",
"resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.6.1.tgz",
"integrity": "sha512-24vnklJmyRS8ViBNI8KbtK/r/DmXQMRiOMXTNz2nrTnAYUwjmEEbnnpB/+kt+yWRv73bPsSPRFddrcIbAxSiMQ==",
"bin": {
"node-gyp-build": "bin.js",
"node-gyp-build-optional": "optional.js",
"node-gyp-build-test": "build-test.js"
}
},
"node_modules/node-releases": {
"version": "2.0.13",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz",
@@ -979,6 +1049,34 @@
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
},
"node_modules/pg-int8": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
"integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==",
"engines": {
"node": ">=4.0.0"
}
},
"node_modules/pg-protocol": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.0.tgz",
"integrity": "sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q=="
},
"node_modules/pg-types": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz",
"integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==",
"dependencies": {
"pg-int8": "1.0.1",
"postgres-array": "~2.0.0",
"postgres-bytea": "~1.0.0",
"postgres-date": "~1.0.4",
"postgres-interval": "^1.1.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/picocolors": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
@@ -1135,6 +1233,41 @@
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="
},
"node_modules/postgres-array": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",
"integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==",
"engines": {
"node": ">=4"
}
},
"node_modules/postgres-bytea": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz",
"integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/postgres-date": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz",
"integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/postgres-interval": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz",
"integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==",
"dependencies": {
"xtend": "^4.0.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/queue-microtask": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
@@ -1457,6 +1590,18 @@
"browserslist": ">= 4.21.0"
}
},
"node_modules/utf-8-validate": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-6.0.3.tgz",
"integrity": "sha512-uIuGf9TWQ/y+0Lp+KGZCMuJWc3N9BHA+l/UmHd/oUHwJJDeysyTRxNQVkbzsIWfGFbRe3OcgML/i0mvVRPOyDA==",
"hasInstallScript": true,
"dependencies": {
"node-gyp-build": "^4.3.0"
},
"engines": {
"node": ">=6.14.2"
}
},
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
@@ -1479,6 +1624,34 @@
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
},
"node_modules/ws": {
"version": "8.13.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz",
"integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==",
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": ">=5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
},
"node_modules/xtend": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
"engines": {
"node": ">=0.4"
}
},
"node_modules/yaml": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.2.tgz",
@@ -1542,6 +1715,14 @@
"@jridgewell/sourcemap-codec": "^1.4.14"
}
},
"@neondatabase/serverless": {
"version": "0.5.6",
"resolved": "https://registry.npmjs.org/@neondatabase/serverless/-/serverless-0.5.6.tgz",
"integrity": "sha512-Ru0lG6W/nQtHRkDFVQFF+1PJYx8wd3jereln0Ep0YkiHey50hjTLVUycQoE4X977605pXMuFWORweuktzph+Xg==",
"requires": {
"@types/pg": "8.6.6"
}
},
"@next/env": {
"version": "13.4.19",
"resolved": "https://registry.npmjs.org/@next/env/-/env-13.4.19.tgz",
@@ -1645,6 +1826,16 @@
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.7.tgz",
"integrity": "sha512-dP7f3LdZIysZnmvP3ANJYTSwg+wLLl8p7RqniVlV7j+oXSXAbt9h0WIBFmJy5inWZoX9wZN6eXx+YXd9Rh3RBA=="
},
"@types/pg": {
"version": "8.6.6",
"resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.6.6.tgz",
"integrity": "sha512-O2xNmXebtwVekJDD+02udOncjVcMZQuTEQEMpKJ0ZRf5E7/9JJX3izhKUcUifBkyKpljyUM6BTgy2trmviKlpw==",
"requires": {
"@types/node": "*",
"pg-protocol": "*",
"pg-types": "^2.2.0"
}
},
"@types/prop-types": {
"version": "15.7.5",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz",
@@ -1673,6 +1864,17 @@
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz",
"integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ=="
},
"@vercel/postgres": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/@vercel/postgres/-/postgres-0.4.1.tgz",
"integrity": "sha512-rYlNnaXrr2/NWK/OodhAUyed0bomaizKKC8XXjNYv8I1K3m75oocP4IGTcBpZe76VCrHuaKW5d6jLQnuRRoNKg==",
"requires": {
"@neondatabase/serverless": "0.5.6",
"bufferutil": "4.0.7",
"utf-8-validate": "6.0.3",
"ws": "8.13.0"
}
},
"any-promise": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
@@ -1743,6 +1945,14 @@
"update-browserslist-db": "^1.0.11"
}
},
"bufferutil": {
"version": "4.0.7",
"resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.7.tgz",
"integrity": "sha512-kukuqc39WOHtdxtw4UScxF/WVnMFVSQVKhtx3AjZJzhd0RGZZldcrfSEbVsWWe6KNH253574cq5F+wpv0G9pJw==",
"requires": {
"node-gyp-build": "^4.3.0"
}
},
"busboy": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
@@ -1826,6 +2036,12 @@
"resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
"integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA=="
},
"dotenv": {
"version": "16.3.1",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz",
"integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==",
"dev": true
},
"electron-to-chromium": {
"version": "1.4.503",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.503.tgz",
@@ -2075,6 +2291,11 @@
}
}
},
"node-gyp-build": {
"version": "4.6.1",
"resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.6.1.tgz",
"integrity": "sha512-24vnklJmyRS8ViBNI8KbtK/r/DmXQMRiOMXTNz2nrTnAYUwjmEEbnnpB/+kt+yWRv73bPsSPRFddrcIbAxSiMQ=="
},
"node-releases": {
"version": "2.0.13",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz",
@@ -2118,6 +2339,28 @@
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
},
"pg-int8": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
"integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw=="
},
"pg-protocol": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.0.tgz",
"integrity": "sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q=="
},
"pg-types": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz",
"integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==",
"requires": {
"pg-int8": "1.0.1",
"postgres-array": "~2.0.0",
"postgres-bytea": "~1.0.0",
"postgres-date": "~1.0.4",
"postgres-interval": "^1.1.0"
}
},
"picocolors": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
@@ -2197,6 +2440,29 @@
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="
},
"postgres-array": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",
"integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA=="
},
"postgres-bytea": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz",
"integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w=="
},
"postgres-date": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz",
"integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q=="
},
"postgres-interval": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz",
"integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==",
"requires": {
"xtend": "^4.0.0"
}
},
"queue-microtask": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
@@ -2395,6 +2661,14 @@
"picocolors": "^1.0.0"
}
},
"utf-8-validate": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-6.0.3.tgz",
"integrity": "sha512-uIuGf9TWQ/y+0Lp+KGZCMuJWc3N9BHA+l/UmHd/oUHwJJDeysyTRxNQVkbzsIWfGFbRe3OcgML/i0mvVRPOyDA==",
"requires": {
"node-gyp-build": "^4.3.0"
}
},
"util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
@@ -2414,6 +2688,17 @@
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
},
"ws": {
"version": "8.13.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz",
"integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==",
"requires": {}
},
"xtend": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="
},
"yaml": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.2.tgz",

View File

@@ -5,6 +5,7 @@
"scripts": {
"build": "next build",
"dev": "next dev",
"seed": "node -r dotenv/config ./scripts/seed.js",
"start": "next start"
},
"dependencies": {
@@ -13,6 +14,7 @@
"@types/node": "20.5.7",
"@types/react": "18.2.21",
"@types/react-dom": "18.2.7",
"@vercel/postgres": "^0.4.1",
"autoprefixer": "10.4.15",
"clsx": "^2.0.0",
"next": "13.4.19",
@@ -22,6 +24,9 @@
"tailwindcss": "3.3.3",
"typescript": "5.2.2"
},
"devDependencies": {
"dotenv": "^16.3.1"
},
"engines": {
"node": ">=18"
}

View File

@@ -0,0 +1,118 @@
const { sql } = require('@vercel/postgres');
const { invoices, customers, revenue } = require('../app/lib/dummy-data.js');
async function seedInvoices() {
try {
// Create the "invoices" table if it doesn't exist
const createTable = await sql`
CREATE TABLE IF NOT EXISTS invoices (
id SERIAL PRIMARY KEY,
customer_id INT NOT NULL,
amount INT NOT NULL,
status VARCHAR(255) NOT NULL,
date DATE NOT NULL
);
`;
console.log(`Created "invoices" table`);
// Insert data into the "invoices" table
const insertedInvoices = await Promise.all(
invoices.map(
(invoice) => sql`
INSERT INTO invoices (id, customer_id, amount, status, date)
VALUES (${invoice.id}, ${invoice.customer_id}, ${invoice.amount}, ${invoice.status}, ${invoice.date})
ON CONFLICT (id) DO NOTHING;
`,
),
);
console.log(`Seeded ${insertedInvoices.length} invoices`);
return {
createTable,
invoices: insertedInvoices,
};
} catch (error) {
console.error('Error seeding invoices:', error);
throw error;
}
}
async function seedCustomers() {
try {
// Create the "invoices" table if it doesn't exist
const createTable = await sql`
CREATE TABLE IF NOT EXISTS customers (
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
email VARCHAR(255) NOT NULL,
image_url VARCHAR(255) NOT NULL
);
`;
console.log(`Created "customers" table`);
// Insert data into the "customers" table
const insertedCustomers = await Promise.all(
customers.map(
(customer) => sql`
INSERT INTO customers (id, name, email, image_url)
VALUES (${customer.id}, ${customer.name}, ${customer.email}, ${customer.image_url})
ON CONFLICT (id) DO NOTHING;
`,
),
);
console.log(`Seeded ${insertedCustomers.length} customers`);
return {
createTable,
customers: insertedCustomers,
};
} catch (error) {
console.error('Error seeding customers:', error);
throw error;
}
}
async function seedRevenue() {
try {
// Create the "revenue" table if it doesn't exist
const createTable = await sql`
CREATE TABLE IF NOT EXISTS revenue (
month VARCHAR(4) NOT NULL UNIQUE,
revenue INT NOT NULL
);
`;
console.log(`Created "revenue" table`);
// Insert data into the "revenue" table
const insertedRevenue = await Promise.all(
revenue.map(
(rev) => sql`
INSERT INTO revenue (month, revenue)
VALUES (${rev.month}, ${rev.revenue})
ON CONFLICT (month) DO NOTHING;
`,
),
);
console.log(`Seeded ${insertedRevenue.length} revenue`);
return {
createTable,
revenue: insertedRevenue,
};
} catch (error) {
console.error('Error seeding revenue:', error);
throw error;
}
}
(async () => {
await seedCustomers();
await seedInvoices();
await seedRevenue();
})();

View File

@@ -22,6 +22,13 @@
"@/*": ["./*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts",
"app/lib/dummy-data.js",
"scripts/seed.js"
],
"exclude": ["node_modules"]
}

120
pnpm-lock.yaml generated
View File

@@ -1,5 +1,9 @@
lockfileVersion: '6.0'
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
importers:
.:
@@ -224,6 +228,9 @@ importers:
'@types/react-dom':
specifier: 18.2.7
version: 18.2.7
'@vercel/postgres':
specifier: ^0.4.1
version: 0.4.1
autoprefixer:
specifier: 10.4.15
version: 10.4.15(postcss@8.4.28)
@@ -248,6 +255,10 @@ importers:
typescript:
specifier: 5.2.2
version: 5.2.2
devDependencies:
dotenv:
specifier: ^16.3.1
version: 16.3.1
seo/demo:
dependencies:
@@ -585,6 +596,12 @@ packages:
resolution: {integrity: sha512-9b8mPpKrfeGRuhFH5iO1iwCLeIIsV6+H1sRfxbkoGXIyQE2BTsPd9zqSqQJ+pv5sJ/hT5M1zvOFL02MnEezFug==}
dev: true
/@neondatabase/serverless@0.5.6:
resolution: {integrity: sha512-Ru0lG6W/nQtHRkDFVQFF+1PJYx8wd3jereln0Ep0YkiHey50hjTLVUycQoE4X977605pXMuFWORweuktzph+Xg==}
dependencies:
'@types/pg': 8.6.6
dev: false
/@next/env@13.4.19:
resolution: {integrity: sha512-FsAT5x0jF2kkhNkKkukhsyYOrRqtSxrEhfliniIq0bwWbuXLgyt3Gv0Ml+b91XwjwArmuP7NxCiGd++GGKdNMQ==}
dev: false
@@ -763,7 +780,6 @@ packages:
/@types/node@18.11.9:
resolution: {integrity: sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==}
dev: true
/@types/node@20.5.7:
resolution: {integrity: sha512-dP7f3LdZIysZnmvP3ANJYTSwg+wLLl8p7RqniVlV7j+oXSXAbt9h0WIBFmJy5inWZoX9wZN6eXx+YXd9Rh3RBA==}
@@ -777,6 +793,14 @@ packages:
resolution: {integrity: sha512-SuT16Q1K51EAVPz1K29DJ/sXjhSQ0zjvsypYJ6tlwVsRV9jwW5Adq2ch8Dq8kDBCkYnELS7N7VNCSB5nC56t/g==}
dev: false
/@types/pg@8.6.6:
resolution: {integrity: sha512-O2xNmXebtwVekJDD+02udOncjVcMZQuTEQEMpKJ0ZRf5E7/9JJX3izhKUcUifBkyKpljyUM6BTgy2trmviKlpw==}
dependencies:
'@types/node': 18.11.9
pg-protocol: 1.6.0
pg-types: 2.2.0
dev: false
/@types/prop-types@15.7.5:
resolution: {integrity: sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==}
@@ -997,6 +1021,16 @@ packages:
eslint-visitor-keys: 3.4.3
dev: true
/@vercel/postgres@0.4.1:
resolution: {integrity: sha512-rYlNnaXrr2/NWK/OodhAUyed0bomaizKKC8XXjNYv8I1K3m75oocP4IGTcBpZe76VCrHuaKW5d6jLQnuRRoNKg==}
engines: {node: '>=14.6'}
dependencies:
'@neondatabase/serverless': 0.5.6
bufferutil: 4.0.7
utf-8-validate: 6.0.3
ws: 8.13.0(bufferutil@4.0.7)(utf-8-validate@6.0.3)
dev: false
/@vercel/style-guide@5.0.1(eslint@8.48.0)(prettier@3.0.3)(typescript@4.8.4):
resolution: {integrity: sha512-3J/5xpwJ2Wk+cKB3EGY2KCdVQycaThLKhjBmgXPfIKb+E74lPpXVIDfaQE0D2JoAyIzGsqdH7Lbmr+DojwofxQ==}
engines: {node: '>=16'}
@@ -1281,6 +1315,14 @@ packages:
node-releases: 2.0.13
update-browserslist-db: 1.0.11(browserslist@4.21.10)
/bufferutil@4.0.7:
resolution: {integrity: sha512-kukuqc39WOHtdxtw4UScxF/WVnMFVSQVKhtx3AjZJzhd0RGZZldcrfSEbVsWWe6KNH253574cq5F+wpv0G9pJw==}
engines: {node: '>=6.14.2'}
requiresBuild: true
dependencies:
node-gyp-build: 4.6.1
dev: false
/builtin-modules@3.3.0:
resolution: {integrity: sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==}
engines: {node: '>=6'}
@@ -1576,6 +1618,11 @@ packages:
esutils: 2.0.3
dev: true
/dotenv@16.3.1:
resolution: {integrity: sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==}
engines: {node: '>=12'}
dev: true
/electron-to-chromium@1.4.508:
resolution: {integrity: sha512-FFa8QKjQK/A5QuFr2167myhMesGrhlOBD+3cYNxO9/S4XzHEXesyTD/1/xF644gC8buFPz3ca6G1LOQD0tZrrg==}
@@ -3408,6 +3455,11 @@ packages:
- babel-plugin-macros
dev: false
/node-gyp-build@4.6.1:
resolution: {integrity: sha512-24vnklJmyRS8ViBNI8KbtK/r/DmXQMRiOMXTNz2nrTnAYUwjmEEbnnpB/+kt+yWRv73bPsSPRFddrcIbAxSiMQ==}
hasBin: true
dev: false
/node-releases@2.0.13:
resolution: {integrity: sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==}
@@ -3648,6 +3700,26 @@ packages:
engines: {node: '>=8'}
dev: true
/pg-int8@1.0.1:
resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==}
engines: {node: '>=4.0.0'}
dev: false
/pg-protocol@1.6.0:
resolution: {integrity: sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q==}
dev: false
/pg-types@2.2.0:
resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==}
engines: {node: '>=4'}
dependencies:
pg-int8: 1.0.1
postgres-array: 2.0.0
postgres-bytea: 1.0.0
postgres-date: 1.0.7
postgres-interval: 1.2.0
dev: false
/picocolors@1.0.0:
resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}
@@ -3749,6 +3821,28 @@ packages:
source-map-js: 1.0.2
dev: false
/postgres-array@2.0.0:
resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==}
engines: {node: '>=4'}
dev: false
/postgres-bytea@1.0.0:
resolution: {integrity: sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==}
engines: {node: '>=0.10.0'}
dev: false
/postgres-date@1.0.7:
resolution: {integrity: sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==}
engines: {node: '>=0.10.0'}
dev: false
/postgres-interval@1.2.0:
resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==}
engines: {node: '>=0.10.0'}
dependencies:
xtend: 4.0.2
dev: false
/prelude-ls@1.2.1:
resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
engines: {node: '>= 0.8.0'}
@@ -4644,6 +4738,14 @@ packages:
punycode: 2.3.0
dev: true
/utf-8-validate@6.0.3:
resolution: {integrity: sha512-uIuGf9TWQ/y+0Lp+KGZCMuJWc3N9BHA+l/UmHd/oUHwJJDeysyTRxNQVkbzsIWfGFbRe3OcgML/i0mvVRPOyDA==}
engines: {node: '>=6.14.2'}
requiresBuild: true
dependencies:
node-gyp-build: 4.6.1
dev: false
/util-deprecate@1.0.2:
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
dev: false
@@ -4766,6 +4868,22 @@ packages:
/wrappy@1.0.2:
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
/ws@8.13.0(bufferutil@4.0.7)(utf-8-validate@6.0.3):
resolution: {integrity: sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==}
engines: {node: '>=10.0.0'}
peerDependencies:
bufferutil: ^4.0.1
utf-8-validate: '>=5.0.2'
peerDependenciesMeta:
bufferutil:
optional: true
utf-8-validate:
optional: true
dependencies:
bufferutil: 4.0.7
utf-8-validate: 6.0.3
dev: false
/xtend@4.0.2:
resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==}
engines: {node: '>=0.4'}