Move from @vercel/postgres to postgres (provider-agnostic) (#989)

* Postgres

* fix

* fix

* prettier-fix

---------

Co-authored-by: Delba de Oliveira <32464864+delbaoliveira@users.noreply.github.com>
This commit is contained in:
Lee Robinson
2025-01-29 11:39:32 -06:00
committed by GitHub
parent fc3a4e3faa
commit 46d15fa2e5
14 changed files with 3185 additions and 5265 deletions

View File

@@ -1,12 +1,14 @@
'use server';
import { z } from 'zod';
import { sql } from '@vercel/postgres';
import postgres from 'postgres';
import { revalidatePath } from 'next/cache';
import { redirect } from 'next/navigation';
import { signIn } from '@/auth';
import { AuthError } from 'next-auth';
const sql = postgres(process.env.POSTGRES_URL!, { ssl: 'require' });
const FormSchema = z.object({
id: z.string(),
customerId: z.string({

View File

@@ -1,4 +1,4 @@
import { sql } from '@vercel/postgres';
import postgres from 'postgres';
import {
CustomerField,
CustomersTableType,
@@ -9,6 +9,8 @@ import {
} from './definitions';
import { formatCurrency } from './utils';
const sql = postgres(process.env.POSTGRES_URL!, { ssl: 'require' });
export async function fetchRevenue() {
try {
// Artificially delay a response for demo purposes.
@@ -17,11 +19,11 @@ export async function fetchRevenue() {
// console.log('Fetching revenue data...');
// await new Promise((resolve) => setTimeout(resolve, 3000));
const data = await sql<Revenue>`SELECT * FROM revenue`;
const data = await sql<Revenue[]>`SELECT * FROM revenue`;
// console.log('Data fetch completed after 3 seconds.');
return data.rows;
return data;
} catch (error) {
console.error('Database Error:', error);
throw new Error('Failed to fetch revenue data.');
@@ -30,14 +32,14 @@ export async function fetchRevenue() {
export async function fetchLatestInvoices() {
try {
const data = await sql<LatestInvoiceRaw>`
const data = await sql<LatestInvoiceRaw[]>`
SELECT invoices.amount, customers.name, customers.image_url, customers.email, invoices.id
FROM invoices
JOIN customers ON invoices.customer_id = customers.id
ORDER BY invoices.date DESC
LIMIT 5`;
const latestInvoices = data.rows.map((invoice) => ({
const latestInvoices = data.map((invoice) => ({
...invoice,
amount: formatCurrency(invoice.amount),
}));
@@ -66,10 +68,10 @@ export async function fetchCardData() {
invoiceStatusPromise,
]);
const numberOfInvoices = Number(data[0].rows[0].count ?? '0');
const numberOfCustomers = Number(data[1].rows[0].count ?? '0');
const totalPaidInvoices = formatCurrency(data[2].rows[0].paid ?? '0');
const totalPendingInvoices = formatCurrency(data[2].rows[0].pending ?? '0');
const numberOfInvoices = Number(data[0].count ?? '0');
const numberOfCustomers = Number(data[1].count ?? '0');
const totalPaidInvoices = formatCurrency(data[2][0].paid ?? '0');
const totalPendingInvoices = formatCurrency(data[2][0].pending ?? '0');
return {
numberOfCustomers,
@@ -91,7 +93,7 @@ export async function fetchFilteredInvoices(
const offset = (currentPage - 1) * ITEMS_PER_PAGE;
try {
const invoices = await sql<InvoicesTable>`
const invoices = await sql<InvoicesTable[]>`
SELECT
invoices.id,
invoices.amount,
@@ -112,7 +114,7 @@ export async function fetchFilteredInvoices(
LIMIT ${ITEMS_PER_PAGE} OFFSET ${offset}
`;
return invoices.rows;
return invoices;
} catch (error) {
console.error('Database Error:', error);
throw new Error('Failed to fetch invoices.');
@@ -121,7 +123,7 @@ export async function fetchFilteredInvoices(
export async function fetchInvoicesPages(query: string) {
try {
const count = await sql`SELECT COUNT(*)
const data = await sql`SELECT COUNT(*)
FROM invoices
JOIN customers ON invoices.customer_id = customers.id
WHERE
@@ -132,7 +134,7 @@ export async function fetchInvoicesPages(query: string) {
invoices.status ILIKE ${`%${query}%`}
`;
const totalPages = Math.ceil(Number(count.rows[0].count) / ITEMS_PER_PAGE);
const totalPages = Math.ceil(Number(data[0].count) / ITEMS_PER_PAGE);
return totalPages;
} catch (error) {
console.error('Database Error:', error);
@@ -142,7 +144,7 @@ export async function fetchInvoicesPages(query: string) {
export async function fetchInvoiceById(id: string) {
try {
const data = await sql<InvoiceForm>`
const data = await sql<InvoiceForm[]>`
SELECT
invoices.id,
invoices.customer_id,
@@ -152,7 +154,7 @@ export async function fetchInvoiceById(id: string) {
WHERE invoices.id = ${id};
`;
const invoice = data.rows.map((invoice) => ({
const invoice = data.map((invoice) => ({
...invoice,
// Convert amount from cents to dollars
amount: invoice.amount / 100,
@@ -167,7 +169,7 @@ export async function fetchInvoiceById(id: string) {
export async function fetchCustomers() {
try {
const data = await sql<CustomerField>`
const customers = await sql<CustomerField[]>`
SELECT
id,
name
@@ -175,7 +177,6 @@ export async function fetchCustomers() {
ORDER BY name ASC
`;
const customers = data.rows;
return customers;
} catch (err) {
console.error('Database Error:', err);
@@ -185,7 +186,7 @@ export async function fetchCustomers() {
export async function fetchFilteredCustomers(query: string) {
try {
const data = await sql<CustomersTableType>`
const data = await sql<CustomersTableType[]>`
SELECT
customers.id,
customers.name,
@@ -203,7 +204,7 @@ export async function fetchFilteredCustomers(query: string) {
ORDER BY customers.name ASC
`;
const customers = data.rows.map((customer) => ({
const customers = data.map((customer) => ({
...customer,
total_pending: formatCurrency(customer.total_pending),
total_paid: formatCurrency(customer.total_paid),

View File

@@ -1,122 +1,117 @@
// import bcrypt from 'bcrypt';
// import { db } from '@vercel/postgres';
// import { invoices, customers, revenue, users } from '../lib/placeholder-data';
import bcrypt from 'bcrypt';
import postgres from 'postgres';
import { invoices, customers, revenue, users } from '../lib/placeholder-data';
// const client = await db.connect();
const sql = postgres(process.env.POSTGRES_URL!, { ssl: 'require' });
// async function seedUsers() {
// await client.sql`CREATE EXTENSION IF NOT EXISTS "uuid-ossp"`;
// await client.sql`
// CREATE TABLE IF NOT EXISTS users (
// id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
// name VARCHAR(255) NOT NULL,
// email TEXT NOT NULL UNIQUE,
// password TEXT NOT NULL
// );
// `;
async function seedUsers() {
await sql`CREATE EXTENSION IF NOT EXISTS "uuid-ossp"`;
await sql`
CREATE TABLE IF NOT EXISTS users (
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
name VARCHAR(255) NOT NULL,
email TEXT NOT NULL UNIQUE,
password TEXT NOT NULL
);
`;
// const insertedUsers = await Promise.all(
// users.map(async (user) => {
// const hashedPassword = await bcrypt.hash(user.password, 10);
// return client.sql`
// INSERT INTO users (id, name, email, password)
// VALUES (${user.id}, ${user.name}, ${user.email}, ${hashedPassword})
// ON CONFLICT (id) DO NOTHING;
// `;
// }),
// );
const insertedUsers = await Promise.all(
users.map(async (user) => {
const hashedPassword = await bcrypt.hash(user.password, 10);
return sql`
INSERT INTO users (id, name, email, password)
VALUES (${user.id}, ${user.name}, ${user.email}, ${hashedPassword})
ON CONFLICT (id) DO NOTHING;
`;
}),
);
// return insertedUsers;
// }
return insertedUsers;
}
// async function seedInvoices() {
// await client.sql`CREATE EXTENSION IF NOT EXISTS "uuid-ossp"`;
async function seedInvoices() {
await sql`CREATE EXTENSION IF NOT EXISTS "uuid-ossp"`;
// await client.sql`
// CREATE TABLE IF NOT EXISTS invoices (
// id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
// customer_id UUID NOT NULL,
// amount INT NOT NULL,
// status VARCHAR(255) NOT NULL,
// date DATE NOT NULL
// );
// `;
await sql`
CREATE TABLE IF NOT EXISTS invoices (
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
customer_id UUID NOT NULL,
amount INT NOT NULL,
status VARCHAR(255) NOT NULL,
date DATE NOT NULL
);
`;
// const insertedInvoices = await Promise.all(
// invoices.map(
// (invoice) => client.sql`
// INSERT INTO invoices (customer_id, amount, status, date)
// VALUES (${invoice.customer_id}, ${invoice.amount}, ${invoice.status}, ${invoice.date})
// ON CONFLICT (id) DO NOTHING;
// `,
// ),
// );
const insertedInvoices = await Promise.all(
invoices.map(
(invoice) => sql`
INSERT INTO invoices (customer_id, amount, status, date)
VALUES (${invoice.customer_id}, ${invoice.amount}, ${invoice.status}, ${invoice.date})
ON CONFLICT (id) DO NOTHING;
`,
),
);
// return insertedInvoices;
// }
return insertedInvoices;
}
// async function seedCustomers() {
// await client.sql`CREATE EXTENSION IF NOT EXISTS "uuid-ossp"`;
async function seedCustomers() {
await sql`CREATE EXTENSION IF NOT EXISTS "uuid-ossp"`;
// await client.sql`
// CREATE TABLE IF NOT EXISTS customers (
// id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
// name VARCHAR(255) NOT NULL,
// email VARCHAR(255) NOT NULL,
// image_url VARCHAR(255) NOT NULL
// );
// `;
await sql`
CREATE TABLE IF NOT EXISTS customers (
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
name VARCHAR(255) NOT NULL,
email VARCHAR(255) NOT NULL,
image_url VARCHAR(255) NOT NULL
);
`;
// const insertedCustomers = await Promise.all(
// customers.map(
// (customer) => client.sql`
// INSERT INTO customers (id, name, email, image_url)
// VALUES (${customer.id}, ${customer.name}, ${customer.email}, ${customer.image_url})
// ON CONFLICT (id) DO NOTHING;
// `,
// ),
// );
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;
`,
),
);
// return insertedCustomers;
// }
return insertedCustomers;
}
// async function seedRevenue() {
// await client.sql`
// CREATE TABLE IF NOT EXISTS revenue (
// month VARCHAR(4) NOT NULL UNIQUE,
// revenue INT NOT NULL
// );
// `;
async function seedRevenue() {
await sql`
CREATE TABLE IF NOT EXISTS revenue (
month VARCHAR(4) NOT NULL UNIQUE,
revenue INT NOT NULL
);
`;
// const insertedRevenue = await Promise.all(
// revenue.map(
// (rev) => client.sql`
// INSERT INTO revenue (month, revenue)
// VALUES (${rev.month}, ${rev.revenue})
// ON CONFLICT (month) DO NOTHING;
// `,
// ),
// );
const insertedRevenue = await Promise.all(
revenue.map(
(rev) => sql`
INSERT INTO revenue (month, revenue)
VALUES (${rev.month}, ${rev.revenue})
ON CONFLICT (month) DO NOTHING;
`,
),
);
// return insertedRevenue;
// }
return insertedRevenue;
}
export async function GET() {
return Response.json({
message:
'Uncomment this file and remove this line. You can delete this file when you are finished.',
});
// try {
// await client.sql`BEGIN`;
// await seedUsers();
// await seedCustomers();
// await seedInvoices();
// await seedRevenue();
// await client.sql`COMMIT`;
try {
const result = await sql.begin((sql) => [
seedUsers(),
seedCustomers(),
seedInvoices(),
seedRevenue(),
]);
// return Response.json({ message: 'Database seeded successfully' });
// } catch (error) {
// await client.sql`ROLLBACK`;
// return Response.json({ error }, { status: 500 });
// }
return Response.json({ message: 'Database seeded successfully' });
} catch (error) {
return Response.json({ error }, { status: 500 });
}
}

View File

@@ -1,15 +1,17 @@
import NextAuth from 'next-auth';
import Credentials from 'next-auth/providers/credentials';
import bcrypt from 'bcrypt';
import { sql } from '@vercel/postgres';
import postgres from 'postgres';
import { z } from 'zod';
import type { User } from '@/app/lib/definitions';
import { authConfig } from './auth.config';
const sql = postgres(process.env.POSTGRES_URL!, { ssl: 'require' });
async function getUser(email: string): Promise<User | undefined> {
try {
const user = await sql<User>`SELECT * FROM users WHERE email=${email}`;
return user.rows[0];
const user = await sql<User[]>`SELECT * FROM users WHERE email=${email}`;
return user[0];
} catch (error) {
console.error('Failed to fetch user:', error);
throw new Error('Failed to fetch user.');

View File

@@ -8,13 +8,13 @@
"dependencies": {
"@heroicons/react": "^2.2.0",
"@tailwindcss/forms": "^0.5.10",
"@vercel/postgres": "^0.10.0",
"autoprefixer": "10.4.20",
"bcrypt": "^5.1.1",
"clsx": "^2.1.1",
"next": "latest",
"next-auth": "5.0.0-beta.25",
"postcss": "8.5.1",
"postgres": "^3.4.5",
"react": "latest",
"react-dom": "latest",
"tailwindcss": "3.4.17",

View File

@@ -14,9 +14,6 @@ importers:
'@tailwindcss/forms':
specifier: ^0.5.10
version: 0.5.10(tailwindcss@3.4.17)
'@vercel/postgres':
specifier: ^0.10.0
version: 0.10.0(utf-8-validate@6.0.5)
autoprefixer:
specifier: 10.4.20
version: 10.4.20(postcss@8.5.1)
@@ -35,6 +32,9 @@ importers:
postcss:
specifier: 8.5.1
version: 8.5.1
postgres:
specifier: ^3.4.5
version: 3.4.5
react:
specifier: latest
version: 19.0.0
@@ -226,9 +226,6 @@ packages:
resolution: {integrity: sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==}
hasBin: true
'@neondatabase/serverless@0.9.5':
resolution: {integrity: sha512-siFas6gItqv6wD/pZnvdu34wEqgG3nSE6zWZdq5j2DEsa+VvX8i/5HXJOo06qrw5axPXn+lGCxeR+NLaSPIXug==}
'@next/env@15.1.6':
resolution: {integrity: sha512-d9AFQVPEYNr+aqokIiPLNK/MTyt3DWa/dpKveiAaVccUadFbhFEvY6FXYX2LJO2Hv7PHnLBu2oWwB4uBuHjr/w==}
@@ -319,9 +316,6 @@ packages:
'@types/node@22.10.7':
resolution: {integrity: sha512-V09KvXxFiutGp6B7XkpaDXlNadZxrzajcY50EuoLIpQ6WWYCSvf19lVIazzfIzQvhUN2HjX12spLojTnhuKlGg==}
'@types/pg@8.11.6':
resolution: {integrity: sha512-/2WmmBXHLsfRqzfHW7BNZ8SbYzE8OSk7i3WjFYvfgRHj7S1xj+16Je5fUKv3lVdVzk/zn9TXOqf+avFCFIE0yQ==}
'@types/react-dom@19.0.3':
resolution: {integrity: sha512-0Knk+HJiMP/qOZgMyNFamlIjw9OFCsyC2ZbigmEEyXXixgre6IQpm/4V+r3qH4GC1JPvRJKInw+on2rV6YZLeA==}
peerDependencies:
@@ -330,10 +324,6 @@ packages:
'@types/react@19.0.7':
resolution: {integrity: sha512-MoFsEJKkAtZCrC1r6CM8U22GzhG7u2Wir8ons/aCKH6MBdD1ibV24zOSSkdZVUKqN5i396zG5VKLYZ3yaUZdLA==}
'@vercel/postgres@0.10.0':
resolution: {integrity: sha512-fSD23DxGND40IzSkXjcFcxr53t3Tiym59Is0jSYIFpG4/0f0KO9SGtcp1sXiebvPaGe7N/tU05cH4yt2S6/IPg==}
engines: {node: '>=18.14'}
abbrev@1.1.1:
resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==}
@@ -408,10 +398,6 @@ packages:
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
hasBin: true
bufferutil@4.0.9:
resolution: {integrity: sha512-WDtdLmJvAuNNPzByAYpRo2rF1Mmradw6gvWsQKf63476DDXmomT9zUiGypLcG4ibIM67vhAj8jJRdbmEws2Aqw==}
engines: {node: '>=6.14.2'}
busboy@1.6.0:
resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==}
engines: {node: '>=10.16.0'}
@@ -746,10 +732,6 @@ packages:
encoding:
optional: true
node-gyp-build@4.8.4:
resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==}
hasBin: true
node-releases@2.0.19:
resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==}
@@ -781,9 +763,6 @@ packages:
resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==}
engines: {node: '>= 6'}
obuf@1.1.2:
resolution: {integrity: sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==}
once@1.4.0:
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
@@ -805,21 +784,6 @@ packages:
resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==}
engines: {node: '>=16 || 14 >=14.18'}
pg-int8@1.0.1:
resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==}
engines: {node: '>=4.0.0'}
pg-numeric@1.0.2:
resolution: {integrity: sha512-BM/Thnrw5jm2kKLE5uJkXqqExRUY/toLHda65XgFTBTFYZyopbKjBe29Ii3RbkvlsMoFwD+tHeGaCjjv0gHlyw==}
engines: {node: '>=4'}
pg-protocol@1.7.0:
resolution: {integrity: sha512-hTK/mE36i8fDDhgDFjy6xNOG+LCorxLG3WO17tku+ij6sVHXh1jQUJ8hYAnRhNla4QVD2H8er/FOjc/+EgC6yQ==}
pg-types@4.0.2:
resolution: {integrity: sha512-cRL3JpS3lKMGsKaWndugWQoLOCoP+Cic8oseVcbr0qhPzYD5DWXK+RZ9LY9wxRf7RQia4SCwQlXk0q6FCPrVng==}
engines: {node: '>=10'}
picocolors@1.1.1:
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
@@ -880,25 +844,10 @@ packages:
resolution: {integrity: sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==}
engines: {node: ^10 || ^12 || >=14}
postgres-array@3.0.2:
resolution: {integrity: sha512-6faShkdFugNQCLwucjPcY5ARoW1SlbnrZjmGl0IrrqewpvxvhSLHimCVzqeuULCbG0fQv7Dtk1yDbG3xv7Veog==}
postgres@3.4.5:
resolution: {integrity: sha512-cDWgoah1Gez9rN3H4165peY9qfpEo+SA61oQv65O3cRUE1pOEoJWwddwcqKE8XZYjbblOJlYDlLV4h67HrEVDg==}
engines: {node: '>=12'}
postgres-bytea@3.0.0:
resolution: {integrity: sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw==}
engines: {node: '>= 6'}
postgres-date@2.1.0:
resolution: {integrity: sha512-K7Juri8gtgXVcDfZttFKVmhglp7epKb1K4pgrkLxehjqkrgPhfG6OO8LHLkfaqkbpjNRnra018XwAr1yQFWGcA==}
engines: {node: '>=12'}
postgres-interval@3.0.0:
resolution: {integrity: sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw==}
engines: {node: '>=12'}
postgres-range@1.1.4:
resolution: {integrity: sha512-i/hbxIE9803Alj/6ytL7UHQxRvZkI9O4Sy+J3HGc4F4oo/2eQAjTSNJ0bfxyse3bH0nuVesCk+3IRLaMtG3H6w==}
preact-render-to-string@5.2.3:
resolution: {integrity: sha512-aPDxUn5o3GhWdtJtW0svRC2SS/l8D9MAgo2+AWml+BhDImb27ALf04Q2d+AHqUUOc6RdSXFIBVa2gxzgMKgtZA==}
peerDependencies:
@@ -1088,10 +1037,6 @@ packages:
peerDependencies:
react: '*'
utf-8-validate@6.0.5:
resolution: {integrity: sha512-EYZR+OpIXp9Y1eG1iueg8KRsY8TuT8VNgnanZ0uA3STqhHQTLwbl+WX76/9X5OY12yQubymBpaBSmMPkSTQcKA==}
engines: {node: '>=6.14.2'}
util-deprecate@1.0.2:
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
@@ -1120,18 +1065,6 @@ packages:
wrappy@1.0.2:
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
ws@8.18.0:
resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==}
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
yallist@4.0.0:
resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
@@ -1282,10 +1215,6 @@ snapshots:
- encoding
- supports-color
'@neondatabase/serverless@0.9.5':
dependencies:
'@types/pg': 8.11.6
'@next/env@15.1.6': {}
'@next/swc-darwin-arm64@15.1.6':
@@ -1350,12 +1279,6 @@ snapshots:
dependencies:
undici-types: 6.20.0
'@types/pg@8.11.6':
dependencies:
'@types/node': 22.10.7
pg-protocol: 1.7.0
pg-types: 4.0.2
'@types/react-dom@19.0.3(@types/react@19.0.7)':
dependencies:
'@types/react': 19.0.7
@@ -1364,14 +1287,6 @@ snapshots:
dependencies:
csstype: 3.1.3
'@vercel/postgres@0.10.0(utf-8-validate@6.0.5)':
dependencies:
'@neondatabase/serverless': 0.9.5
bufferutil: 4.0.9
ws: 8.18.0(bufferutil@4.0.9)(utf-8-validate@6.0.5)
transitivePeerDependencies:
- utf-8-validate
abbrev@1.1.1: {}
agent-base@6.0.2:
@@ -1448,10 +1363,6 @@ snapshots:
node-releases: 2.0.19
update-browserslist-db: 1.1.2(browserslist@4.24.4)
bufferutil@4.0.9:
dependencies:
node-gyp-build: 4.8.4
busboy@1.6.0:
dependencies:
streamsearch: 1.1.0
@@ -1752,8 +1663,6 @@ snapshots:
dependencies:
whatwg-url: 5.0.0
node-gyp-build@4.8.4: {}
node-releases@2.0.19: {}
nopt@5.0.0:
@@ -1777,8 +1686,6 @@ snapshots:
object-hash@3.0.0: {}
obuf@1.1.2: {}
once@1.4.0:
dependencies:
wrappy: 1.0.2
@@ -1796,22 +1703,6 @@ snapshots:
lru-cache: 10.4.3
minipass: 7.1.2
pg-int8@1.0.1: {}
pg-numeric@1.0.2: {}
pg-protocol@1.7.0: {}
pg-types@4.0.2:
dependencies:
pg-int8: 1.0.1
pg-numeric: 1.0.2
postgres-array: 3.0.2
postgres-bytea: 3.0.0
postgres-date: 2.1.0
postgres-interval: 3.0.0
postgres-range: 1.1.4
picocolors@1.1.1: {}
picomatch@2.3.1: {}
@@ -1863,17 +1754,7 @@ snapshots:
picocolors: 1.1.1
source-map-js: 1.2.1
postgres-array@3.0.2: {}
postgres-bytea@3.0.0:
dependencies:
obuf: 1.1.2
postgres-date@2.1.0: {}
postgres-interval@3.0.0: {}
postgres-range@1.1.4: {}
postgres@3.4.5: {}
preact-render-to-string@5.2.3(preact@10.11.3):
dependencies:
@@ -2088,11 +1969,6 @@ snapshots:
dependencies:
react: 19.0.0
utf-8-validate@6.0.5:
dependencies:
node-gyp-build: 4.8.4
optional: true
util-deprecate@1.0.2: {}
webidl-conversions@3.0.1: {}
@@ -2124,11 +2000,6 @@ snapshots:
wrappy@1.0.2: {}
ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@6.0.5):
optionalDependencies:
bufferutil: 4.0.9
utf-8-validate: 6.0.5
yallist@4.0.0: {}
yaml@2.7.0: {}

View File

@@ -1,4 +1,4 @@
import { sql } from '@vercel/postgres';
import postgres from 'postgres';
import {
CustomerField,
CustomersTableType,
@@ -9,6 +9,8 @@ import {
} from './definitions';
import { formatCurrency } from './utils';
const sql = postgres(process.env.POSTGRES_URL!, { ssl: 'require' });
export async function fetchRevenue() {
try {
// Artificially delay a response for demo purposes.
@@ -17,11 +19,11 @@ export async function fetchRevenue() {
// console.log('Fetching revenue data...');
// await new Promise((resolve) => setTimeout(resolve, 3000));
const data = await sql<Revenue>`SELECT * FROM revenue`;
const data = await sql<Revenue[]>`SELECT * FROM revenue`;
// console.log('Data fetch completed after 3 seconds.');
return data.rows;
return data;
} catch (error) {
console.error('Database Error:', error);
throw new Error('Failed to fetch revenue data.');
@@ -30,14 +32,14 @@ export async function fetchRevenue() {
export async function fetchLatestInvoices() {
try {
const data = await sql<LatestInvoiceRaw>`
const data = await sql<LatestInvoiceRaw[]>`
SELECT invoices.amount, customers.name, customers.image_url, customers.email, invoices.id
FROM invoices
JOIN customers ON invoices.customer_id = customers.id
ORDER BY invoices.date DESC
LIMIT 5`;
const latestInvoices = data.rows.map((invoice) => ({
const latestInvoices = data.map((invoice) => ({
...invoice,
amount: formatCurrency(invoice.amount),
}));
@@ -66,10 +68,10 @@ export async function fetchCardData() {
invoiceStatusPromise,
]);
const numberOfInvoices = Number(data[0].rows[0].count ?? '0');
const numberOfCustomers = Number(data[1].rows[0].count ?? '0');
const totalPaidInvoices = formatCurrency(data[2].rows[0].paid ?? '0');
const totalPendingInvoices = formatCurrency(data[2].rows[0].pending ?? '0');
const numberOfInvoices = Number(data[0].count ?? '0');
const numberOfCustomers = Number(data[1].count ?? '0');
const totalPaidInvoices = formatCurrency(data[2][0].paid ?? '0');
const totalPendingInvoices = formatCurrency(data[2][0].pending ?? '0');
return {
numberOfCustomers,
@@ -91,7 +93,7 @@ export async function fetchFilteredInvoices(
const offset = (currentPage - 1) * ITEMS_PER_PAGE;
try {
const invoices = await sql<InvoicesTable>`
const invoices = await sql<InvoicesTable[]>`
SELECT
invoices.id,
invoices.amount,
@@ -112,7 +114,7 @@ export async function fetchFilteredInvoices(
LIMIT ${ITEMS_PER_PAGE} OFFSET ${offset}
`;
return invoices.rows;
return invoices;
} catch (error) {
console.error('Database Error:', error);
throw new Error('Failed to fetch invoices.');
@@ -121,7 +123,7 @@ export async function fetchFilteredInvoices(
export async function fetchInvoicesPages(query: string) {
try {
const count = await sql`SELECT COUNT(*)
const data = await sql`SELECT COUNT(*)
FROM invoices
JOIN customers ON invoices.customer_id = customers.id
WHERE
@@ -132,7 +134,7 @@ export async function fetchInvoicesPages(query: string) {
invoices.status ILIKE ${`%${query}%`}
`;
const totalPages = Math.ceil(Number(count.rows[0].count) / ITEMS_PER_PAGE);
const totalPages = Math.ceil(Number(data[0].count) / ITEMS_PER_PAGE);
return totalPages;
} catch (error) {
console.error('Database Error:', error);
@@ -142,7 +144,7 @@ export async function fetchInvoicesPages(query: string) {
export async function fetchInvoiceById(id: string) {
try {
const data = await sql<InvoiceForm>`
const data = await sql<InvoiceForm[]>`
SELECT
invoices.id,
invoices.customer_id,
@@ -152,7 +154,7 @@ export async function fetchInvoiceById(id: string) {
WHERE invoices.id = ${id};
`;
const invoice = data.rows.map((invoice) => ({
const invoice = data.map((invoice) => ({
...invoice,
// Convert amount from cents to dollars
amount: invoice.amount / 100,
@@ -167,7 +169,7 @@ export async function fetchInvoiceById(id: string) {
export async function fetchCustomers() {
try {
const data = await sql<CustomerField>`
const customers = await sql<CustomerField[]>`
SELECT
id,
name
@@ -175,7 +177,6 @@ export async function fetchCustomers() {
ORDER BY name ASC
`;
const customers = data.rows;
return customers;
} catch (err) {
console.error('Database Error:', err);
@@ -185,7 +186,7 @@ export async function fetchCustomers() {
export async function fetchFilteredCustomers(query: string) {
try {
const data = await sql<CustomersTableType>`
const data = await sql<CustomersTableType[]>`
SELECT
customers.id,
customers.name,
@@ -203,7 +204,7 @@ export async function fetchFilteredCustomers(query: string) {
ORDER BY customers.name ASC
`;
const customers = data.rows.map((customer) => ({
const customers = data.map((customer) => ({
...customer,
total_pending: formatCurrency(customer.total_pending),
total_paid: formatCurrency(customer.total_paid),

View File

@@ -1,26 +0,0 @@
// import { db } from "@vercel/postgres";
// const client = await db.connect();
// async function listInvoices() {
// const data = await client.sql`
// SELECT invoices.amount, customers.name
// FROM invoices
// JOIN customers ON invoices.customer_id = customers.id
// WHERE invoices.amount = 666;
// `;
// return data.rows;
// }
export async function GET() {
return Response.json({
message:
'Uncomment this file and remove this line. You can delete this file when you are finished.',
});
// try {
// return Response.json(await listInvoices());
// } catch (error) {
// return Response.json({ error }, { status: 500 });
// }
}

View File

@@ -1,122 +1,117 @@
// import bcrypt from 'bcrypt';
// import { db } from '@vercel/postgres';
// import { invoices, customers, revenue, users } from '../lib/placeholder-data';
import bcrypt from 'bcrypt';
import postgres from 'postgres';
import { invoices, customers, revenue, users } from '../lib/placeholder-data';
// const client = await db.connect();
const sql = postgres(process.env.POSTGRES_URL!, { ssl: 'require' });
// async function seedUsers() {
// await client.sql`CREATE EXTENSION IF NOT EXISTS "uuid-ossp"`;
// await client.sql`
// CREATE TABLE IF NOT EXISTS users (
// id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
// name VARCHAR(255) NOT NULL,
// email TEXT NOT NULL UNIQUE,
// password TEXT NOT NULL
// );
// `;
async function seedUsers() {
await sql`CREATE EXTENSION IF NOT EXISTS "uuid-ossp"`;
await sql`
CREATE TABLE IF NOT EXISTS users (
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
name VARCHAR(255) NOT NULL,
email TEXT NOT NULL UNIQUE,
password TEXT NOT NULL
);
`;
// const insertedUsers = await Promise.all(
// users.map(async (user) => {
// const hashedPassword = await bcrypt.hash(user.password, 10);
// return client.sql`
// INSERT INTO users (id, name, email, password)
// VALUES (${user.id}, ${user.name}, ${user.email}, ${hashedPassword})
// ON CONFLICT (id) DO NOTHING;
// `;
// }),
// );
const insertedUsers = await Promise.all(
users.map(async (user) => {
const hashedPassword = await bcrypt.hash(user.password, 10);
return sql`
INSERT INTO users (id, name, email, password)
VALUES (${user.id}, ${user.name}, ${user.email}, ${hashedPassword})
ON CONFLICT (id) DO NOTHING;
`;
}),
);
// return insertedUsers;
// }
return insertedUsers;
}
// async function seedInvoices() {
// await client.sql`CREATE EXTENSION IF NOT EXISTS "uuid-ossp"`;
async function seedInvoices() {
await sql`CREATE EXTENSION IF NOT EXISTS "uuid-ossp"`;
// await client.sql`
// CREATE TABLE IF NOT EXISTS invoices (
// id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
// customer_id UUID NOT NULL,
// amount INT NOT NULL,
// status VARCHAR(255) NOT NULL,
// date DATE NOT NULL
// );
// `;
await sql`
CREATE TABLE IF NOT EXISTS invoices (
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
customer_id UUID NOT NULL,
amount INT NOT NULL,
status VARCHAR(255) NOT NULL,
date DATE NOT NULL
);
`;
// const insertedInvoices = await Promise.all(
// invoices.map(
// (invoice) => client.sql`
// INSERT INTO invoices (customer_id, amount, status, date)
// VALUES (${invoice.customer_id}, ${invoice.amount}, ${invoice.status}, ${invoice.date})
// ON CONFLICT (id) DO NOTHING;
// `,
// ),
// );
const insertedInvoices = await Promise.all(
invoices.map(
(invoice) => sql`
INSERT INTO invoices (customer_id, amount, status, date)
VALUES (${invoice.customer_id}, ${invoice.amount}, ${invoice.status}, ${invoice.date})
ON CONFLICT (id) DO NOTHING;
`,
),
);
// return insertedInvoices;
// }
return insertedInvoices;
}
// async function seedCustomers() {
// await client.sql`CREATE EXTENSION IF NOT EXISTS "uuid-ossp"`;
async function seedCustomers() {
await sql`CREATE EXTENSION IF NOT EXISTS "uuid-ossp"`;
// await client.sql`
// CREATE TABLE IF NOT EXISTS customers (
// id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
// name VARCHAR(255) NOT NULL,
// email VARCHAR(255) NOT NULL,
// image_url VARCHAR(255) NOT NULL
// );
// `;
await sql`
CREATE TABLE IF NOT EXISTS customers (
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
name VARCHAR(255) NOT NULL,
email VARCHAR(255) NOT NULL,
image_url VARCHAR(255) NOT NULL
);
`;
// const insertedCustomers = await Promise.all(
// customers.map(
// (customer) => client.sql`
// INSERT INTO customers (id, name, email, image_url)
// VALUES (${customer.id}, ${customer.name}, ${customer.email}, ${customer.image_url})
// ON CONFLICT (id) DO NOTHING;
// `,
// ),
// );
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;
`,
),
);
// return insertedCustomers;
// }
return insertedCustomers;
}
// async function seedRevenue() {
// await client.sql`
// CREATE TABLE IF NOT EXISTS revenue (
// month VARCHAR(4) NOT NULL UNIQUE,
// revenue INT NOT NULL
// );
// `;
async function seedRevenue() {
await sql`
CREATE TABLE IF NOT EXISTS revenue (
month VARCHAR(4) NOT NULL UNIQUE,
revenue INT NOT NULL
);
`;
// const insertedRevenue = await Promise.all(
// revenue.map(
// (rev) => client.sql`
// INSERT INTO revenue (month, revenue)
// VALUES (${rev.month}, ${rev.revenue})
// ON CONFLICT (month) DO NOTHING;
// `,
// ),
// );
const insertedRevenue = await Promise.all(
revenue.map(
(rev) => sql`
INSERT INTO revenue (month, revenue)
VALUES (${rev.month}, ${rev.revenue})
ON CONFLICT (month) DO NOTHING;
`,
),
);
// return insertedRevenue;
// }
return insertedRevenue;
}
export async function GET() {
return Response.json({
message:
'Uncomment this file and remove this line. You can delete this file when you are finished.',
});
// try {
// await client.sql`BEGIN`;
// await seedUsers();
// await seedCustomers();
// await seedInvoices();
// await seedRevenue();
// await client.sql`COMMIT`;
try {
const result = await sql.begin((sql) => [
seedUsers(),
seedCustomers(),
seedInvoices(),
seedRevenue(),
]);
// return Response.json({ message: 'Database seeded successfully' });
// } catch (error) {
// await client.sql`ROLLBACK`;
// return Response.json({ error }, { status: 500 });
// }
return Response.json({ message: 'Database seeded successfully' });
} catch (error) {
return Response.json({ error }, { status: 500 });
}
}

View File

@@ -8,13 +8,13 @@
"dependencies": {
"@heroicons/react": "^2.2.0",
"@tailwindcss/forms": "^0.5.10",
"@vercel/postgres": "^0.10.0",
"autoprefixer": "10.4.20",
"bcrypt": "^5.1.1",
"clsx": "^2.1.1",
"next": "latest",
"next-auth": "5.0.0-beta.25",
"postcss": "8.5.1",
"postgres": "^3.4.5",
"react": "latest",
"react-dom": "latest",
"tailwindcss": "3.4.17",

View File

@@ -14,9 +14,6 @@ importers:
'@tailwindcss/forms':
specifier: ^0.5.10
version: 0.5.10(tailwindcss@3.4.17)
'@vercel/postgres':
specifier: ^0.10.0
version: 0.10.0(utf-8-validate@6.0.5)
autoprefixer:
specifier: 10.4.20
version: 10.4.20(postcss@8.5.1)
@@ -35,6 +32,9 @@ importers:
postcss:
specifier: 8.5.1
version: 8.5.1
postgres:
specifier: ^3.4.5
version: 3.4.5
react:
specifier: latest
version: 19.0.0
@@ -226,9 +226,6 @@ packages:
resolution: {integrity: sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==}
hasBin: true
'@neondatabase/serverless@0.9.5':
resolution: {integrity: sha512-siFas6gItqv6wD/pZnvdu34wEqgG3nSE6zWZdq5j2DEsa+VvX8i/5HXJOo06qrw5axPXn+lGCxeR+NLaSPIXug==}
'@next/env@15.1.6':
resolution: {integrity: sha512-d9AFQVPEYNr+aqokIiPLNK/MTyt3DWa/dpKveiAaVccUadFbhFEvY6FXYX2LJO2Hv7PHnLBu2oWwB4uBuHjr/w==}
@@ -319,9 +316,6 @@ packages:
'@types/node@22.10.7':
resolution: {integrity: sha512-V09KvXxFiutGp6B7XkpaDXlNadZxrzajcY50EuoLIpQ6WWYCSvf19lVIazzfIzQvhUN2HjX12spLojTnhuKlGg==}
'@types/pg@8.11.6':
resolution: {integrity: sha512-/2WmmBXHLsfRqzfHW7BNZ8SbYzE8OSk7i3WjFYvfgRHj7S1xj+16Je5fUKv3lVdVzk/zn9TXOqf+avFCFIE0yQ==}
'@types/react-dom@19.0.3':
resolution: {integrity: sha512-0Knk+HJiMP/qOZgMyNFamlIjw9OFCsyC2ZbigmEEyXXixgre6IQpm/4V+r3qH4GC1JPvRJKInw+on2rV6YZLeA==}
peerDependencies:
@@ -330,10 +324,6 @@ packages:
'@types/react@19.0.7':
resolution: {integrity: sha512-MoFsEJKkAtZCrC1r6CM8U22GzhG7u2Wir8ons/aCKH6MBdD1ibV24zOSSkdZVUKqN5i396zG5VKLYZ3yaUZdLA==}
'@vercel/postgres@0.10.0':
resolution: {integrity: sha512-fSD23DxGND40IzSkXjcFcxr53t3Tiym59Is0jSYIFpG4/0f0KO9SGtcp1sXiebvPaGe7N/tU05cH4yt2S6/IPg==}
engines: {node: '>=18.14'}
abbrev@1.1.1:
resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==}
@@ -408,10 +398,6 @@ packages:
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
hasBin: true
bufferutil@4.0.9:
resolution: {integrity: sha512-WDtdLmJvAuNNPzByAYpRo2rF1Mmradw6gvWsQKf63476DDXmomT9zUiGypLcG4ibIM67vhAj8jJRdbmEws2Aqw==}
engines: {node: '>=6.14.2'}
busboy@1.6.0:
resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==}
engines: {node: '>=10.16.0'}
@@ -746,10 +732,6 @@ packages:
encoding:
optional: true
node-gyp-build@4.8.4:
resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==}
hasBin: true
node-releases@2.0.19:
resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==}
@@ -781,9 +763,6 @@ packages:
resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==}
engines: {node: '>= 6'}
obuf@1.1.2:
resolution: {integrity: sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==}
once@1.4.0:
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
@@ -805,21 +784,6 @@ packages:
resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==}
engines: {node: '>=16 || 14 >=14.18'}
pg-int8@1.0.1:
resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==}
engines: {node: '>=4.0.0'}
pg-numeric@1.0.2:
resolution: {integrity: sha512-BM/Thnrw5jm2kKLE5uJkXqqExRUY/toLHda65XgFTBTFYZyopbKjBe29Ii3RbkvlsMoFwD+tHeGaCjjv0gHlyw==}
engines: {node: '>=4'}
pg-protocol@1.7.0:
resolution: {integrity: sha512-hTK/mE36i8fDDhgDFjy6xNOG+LCorxLG3WO17tku+ij6sVHXh1jQUJ8hYAnRhNla4QVD2H8er/FOjc/+EgC6yQ==}
pg-types@4.0.2:
resolution: {integrity: sha512-cRL3JpS3lKMGsKaWndugWQoLOCoP+Cic8oseVcbr0qhPzYD5DWXK+RZ9LY9wxRf7RQia4SCwQlXk0q6FCPrVng==}
engines: {node: '>=10'}
picocolors@1.1.1:
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
@@ -880,25 +844,10 @@ packages:
resolution: {integrity: sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==}
engines: {node: ^10 || ^12 || >=14}
postgres-array@3.0.2:
resolution: {integrity: sha512-6faShkdFugNQCLwucjPcY5ARoW1SlbnrZjmGl0IrrqewpvxvhSLHimCVzqeuULCbG0fQv7Dtk1yDbG3xv7Veog==}
postgres@3.4.5:
resolution: {integrity: sha512-cDWgoah1Gez9rN3H4165peY9qfpEo+SA61oQv65O3cRUE1pOEoJWwddwcqKE8XZYjbblOJlYDlLV4h67HrEVDg==}
engines: {node: '>=12'}
postgres-bytea@3.0.0:
resolution: {integrity: sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw==}
engines: {node: '>= 6'}
postgres-date@2.1.0:
resolution: {integrity: sha512-K7Juri8gtgXVcDfZttFKVmhglp7epKb1K4pgrkLxehjqkrgPhfG6OO8LHLkfaqkbpjNRnra018XwAr1yQFWGcA==}
engines: {node: '>=12'}
postgres-interval@3.0.0:
resolution: {integrity: sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw==}
engines: {node: '>=12'}
postgres-range@1.1.4:
resolution: {integrity: sha512-i/hbxIE9803Alj/6ytL7UHQxRvZkI9O4Sy+J3HGc4F4oo/2eQAjTSNJ0bfxyse3bH0nuVesCk+3IRLaMtG3H6w==}
preact-render-to-string@5.2.3:
resolution: {integrity: sha512-aPDxUn5o3GhWdtJtW0svRC2SS/l8D9MAgo2+AWml+BhDImb27ALf04Q2d+AHqUUOc6RdSXFIBVa2gxzgMKgtZA==}
peerDependencies:
@@ -1088,10 +1037,6 @@ packages:
peerDependencies:
react: '*'
utf-8-validate@6.0.5:
resolution: {integrity: sha512-EYZR+OpIXp9Y1eG1iueg8KRsY8TuT8VNgnanZ0uA3STqhHQTLwbl+WX76/9X5OY12yQubymBpaBSmMPkSTQcKA==}
engines: {node: '>=6.14.2'}
util-deprecate@1.0.2:
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
@@ -1120,18 +1065,6 @@ packages:
wrappy@1.0.2:
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
ws@8.18.0:
resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==}
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
yallist@4.0.0:
resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
@@ -1282,10 +1215,6 @@ snapshots:
- encoding
- supports-color
'@neondatabase/serverless@0.9.5':
dependencies:
'@types/pg': 8.11.6
'@next/env@15.1.6': {}
'@next/swc-darwin-arm64@15.1.6':
@@ -1350,12 +1279,6 @@ snapshots:
dependencies:
undici-types: 6.20.0
'@types/pg@8.11.6':
dependencies:
'@types/node': 22.10.7
pg-protocol: 1.7.0
pg-types: 4.0.2
'@types/react-dom@19.0.3(@types/react@19.0.7)':
dependencies:
'@types/react': 19.0.7
@@ -1364,14 +1287,6 @@ snapshots:
dependencies:
csstype: 3.1.3
'@vercel/postgres@0.10.0(utf-8-validate@6.0.5)':
dependencies:
'@neondatabase/serverless': 0.9.5
bufferutil: 4.0.9
ws: 8.18.0(bufferutil@4.0.9)(utf-8-validate@6.0.5)
transitivePeerDependencies:
- utf-8-validate
abbrev@1.1.1: {}
agent-base@6.0.2:
@@ -1448,10 +1363,6 @@ snapshots:
node-releases: 2.0.19
update-browserslist-db: 1.1.2(browserslist@4.24.4)
bufferutil@4.0.9:
dependencies:
node-gyp-build: 4.8.4
busboy@1.6.0:
dependencies:
streamsearch: 1.1.0
@@ -1752,8 +1663,6 @@ snapshots:
dependencies:
whatwg-url: 5.0.0
node-gyp-build@4.8.4: {}
node-releases@2.0.19: {}
nopt@5.0.0:
@@ -1777,8 +1686,6 @@ snapshots:
object-hash@3.0.0: {}
obuf@1.1.2: {}
once@1.4.0:
dependencies:
wrappy: 1.0.2
@@ -1796,22 +1703,6 @@ snapshots:
lru-cache: 10.4.3
minipass: 7.1.2
pg-int8@1.0.1: {}
pg-numeric@1.0.2: {}
pg-protocol@1.7.0: {}
pg-types@4.0.2:
dependencies:
pg-int8: 1.0.1
pg-numeric: 1.0.2
postgres-array: 3.0.2
postgres-bytea: 3.0.0
postgres-date: 2.1.0
postgres-interval: 3.0.0
postgres-range: 1.1.4
picocolors@1.1.1: {}
picomatch@2.3.1: {}
@@ -1863,17 +1754,7 @@ snapshots:
picocolors: 1.1.1
source-map-js: 1.2.1
postgres-array@3.0.2: {}
postgres-bytea@3.0.0:
dependencies:
obuf: 1.1.2
postgres-date@2.1.0: {}
postgres-interval@3.0.0: {}
postgres-range@1.1.4: {}
postgres@3.4.5: {}
preact-render-to-string@5.2.3(preact@10.11.3):
dependencies:
@@ -2088,11 +1969,6 @@ snapshots:
dependencies:
react: 19.0.0
utf-8-validate@6.0.5:
dependencies:
node-gyp-build: 4.8.4
optional: true
util-deprecate@1.0.2: {}
webidl-conversions@3.0.1: {}
@@ -2124,11 +2000,6 @@ snapshots:
wrappy@1.0.2: {}
ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@6.0.5):
optionalDependencies:
bufferutil: 4.0.9
utf-8-validate: 6.0.5
yallist@4.0.0: {}
yaml@2.7.0: {}

7522
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,32 +1,32 @@
export const countries = [
{ name: "China", cca2: "CN", population: 1439323776 },
{ name: "India", cca2: "IN", population: 1380004385 },
{ name: "United States", cca2: "US", population: 331002651 },
{ name: "Indonesia", cca2: "ID", population: 273523615 },
{ name: "Pakistan", cca2: "PK", population: 220892340 },
{ name: "Brazil", cca2: "BR", population: 212559417 },
{ name: "Nigeria", cca2: "NG", population: 206139589 },
{ name: "Bangladesh", cca2: "BD", population: 164689383 },
{ name: "Russia", cca2: "RU", population: 145934462 },
{ name: "Mexico", cca2: "MX", population: 128932753 },
{ name: "Japan", cca2: "JP", population: 126476461 },
{ name: "Philippines", cca2: "PH", population: 109581078 },
{ name: "Egypt", cca2: "EG", population: 102334404 },
{ name: "Ethiopia", cca2: "ET", population: 114963588 },
{ name: "Vietnam", cca2: "VN", population: 97338579 },
{ name: "Germany", cca2: "DE", population: 83783942 },
{ name: "Turkey", cca2: "TR", population: 84339067 },
{ name: "Iran", cca2: "IR", population: 83992949 },
{ name: "Thailand", cca2: "TH", population: 69799978 },
{ name: "United Kingdom", cca2: "GB", population: 67886011 },
{ name: "France", cca2: "FR", population: 65273511 },
{ name: "Italy", cca2: "IT", population: 60461826 },
{ name: "South Africa", cca2: "ZA", population: 59308690 },
{ name: "Tanzania", cca2: "TZ", population: 59734218 },
{ name: "Myanmar", cca2: "MM", population: 54409800 },
{ name: "South Korea", cca2: "KR", population: 51269185 },
{ name: "Colombia", cca2: "CO", population: 50882891 },
{ name: "Kenya", cca2: "KE", population: 53771296 },
{ name: "Spain", cca2: "ES", population: 46754778 },
{ name: "Argentina", cca2: "AR", population: 45195774 }
];
{ name: 'China', cca2: 'CN', population: 1439323776 },
{ name: 'India', cca2: 'IN', population: 1380004385 },
{ name: 'United States', cca2: 'US', population: 331002651 },
{ name: 'Indonesia', cca2: 'ID', population: 273523615 },
{ name: 'Pakistan', cca2: 'PK', population: 220892340 },
{ name: 'Brazil', cca2: 'BR', population: 212559417 },
{ name: 'Nigeria', cca2: 'NG', population: 206139589 },
{ name: 'Bangladesh', cca2: 'BD', population: 164689383 },
{ name: 'Russia', cca2: 'RU', population: 145934462 },
{ name: 'Mexico', cca2: 'MX', population: 128932753 },
{ name: 'Japan', cca2: 'JP', population: 126476461 },
{ name: 'Philippines', cca2: 'PH', population: 109581078 },
{ name: 'Egypt', cca2: 'EG', population: 102334404 },
{ name: 'Ethiopia', cca2: 'ET', population: 114963588 },
{ name: 'Vietnam', cca2: 'VN', population: 97338579 },
{ name: 'Germany', cca2: 'DE', population: 83783942 },
{ name: 'Turkey', cca2: 'TR', population: 84339067 },
{ name: 'Iran', cca2: 'IR', population: 83992949 },
{ name: 'Thailand', cca2: 'TH', population: 69799978 },
{ name: 'United Kingdom', cca2: 'GB', population: 67886011 },
{ name: 'France', cca2: 'FR', population: 65273511 },
{ name: 'Italy', cca2: 'IT', population: 60461826 },
{ name: 'South Africa', cca2: 'ZA', population: 59308690 },
{ name: 'Tanzania', cca2: 'TZ', population: 59734218 },
{ name: 'Myanmar', cca2: 'MM', population: 54409800 },
{ name: 'South Korea', cca2: 'KR', population: 51269185 },
{ name: 'Colombia', cca2: 'CO', population: 50882891 },
{ name: 'Kenya', cca2: 'KE', population: 53771296 },
{ name: 'Spain', cca2: 'ES', population: 46754778 },
{ name: 'Argentina', cca2: 'AR', population: 45195774 },
];

View File

@@ -1,32 +1,32 @@
export const countries = [
{ name: "China", cca2: "CN", population: 1439323776 },
{ name: "India", cca2: "IN", population: 1380004385 },
{ name: "United States", cca2: "US", population: 331002651 },
{ name: "Indonesia", cca2: "ID", population: 273523615 },
{ name: "Pakistan", cca2: "PK", population: 220892340 },
{ name: "Brazil", cca2: "BR", population: 212559417 },
{ name: "Nigeria", cca2: "NG", population: 206139589 },
{ name: "Bangladesh", cca2: "BD", population: 164689383 },
{ name: "Russia", cca2: "RU", population: 145934462 },
{ name: "Mexico", cca2: "MX", population: 128932753 },
{ name: "Japan", cca2: "JP", population: 126476461 },
{ name: "Philippines", cca2: "PH", population: 109581078 },
{ name: "Egypt", cca2: "EG", population: 102334404 },
{ name: "Ethiopia", cca2: "ET", population: 114963588 },
{ name: "Vietnam", cca2: "VN", population: 97338579 },
{ name: "Germany", cca2: "DE", population: 83783942 },
{ name: "Turkey", cca2: "TR", population: 84339067 },
{ name: "Iran", cca2: "IR", population: 83992949 },
{ name: "Thailand", cca2: "TH", population: 69799978 },
{ name: "United Kingdom", cca2: "GB", population: 67886011 },
{ name: "France", cca2: "FR", population: 65273511 },
{ name: "Italy", cca2: "IT", population: 60461826 },
{ name: "South Africa", cca2: "ZA", population: 59308690 },
{ name: "Tanzania", cca2: "TZ", population: 59734218 },
{ name: "Myanmar", cca2: "MM", population: 54409800 },
{ name: "South Korea", cca2: "KR", population: 51269185 },
{ name: "Colombia", cca2: "CO", population: 50882891 },
{ name: "Kenya", cca2: "KE", population: 53771296 },
{ name: "Spain", cca2: "ES", population: 46754778 },
{ name: "Argentina", cca2: "AR", population: 45195774 }
];
{ name: 'China', cca2: 'CN', population: 1439323776 },
{ name: 'India', cca2: 'IN', population: 1380004385 },
{ name: 'United States', cca2: 'US', population: 331002651 },
{ name: 'Indonesia', cca2: 'ID', population: 273523615 },
{ name: 'Pakistan', cca2: 'PK', population: 220892340 },
{ name: 'Brazil', cca2: 'BR', population: 212559417 },
{ name: 'Nigeria', cca2: 'NG', population: 206139589 },
{ name: 'Bangladesh', cca2: 'BD', population: 164689383 },
{ name: 'Russia', cca2: 'RU', population: 145934462 },
{ name: 'Mexico', cca2: 'MX', population: 128932753 },
{ name: 'Japan', cca2: 'JP', population: 126476461 },
{ name: 'Philippines', cca2: 'PH', population: 109581078 },
{ name: 'Egypt', cca2: 'EG', population: 102334404 },
{ name: 'Ethiopia', cca2: 'ET', population: 114963588 },
{ name: 'Vietnam', cca2: 'VN', population: 97338579 },
{ name: 'Germany', cca2: 'DE', population: 83783942 },
{ name: 'Turkey', cca2: 'TR', population: 84339067 },
{ name: 'Iran', cca2: 'IR', population: 83992949 },
{ name: 'Thailand', cca2: 'TH', population: 69799978 },
{ name: 'United Kingdom', cca2: 'GB', population: 67886011 },
{ name: 'France', cca2: 'FR', population: 65273511 },
{ name: 'Italy', cca2: 'IT', population: 60461826 },
{ name: 'South Africa', cca2: 'ZA', population: 59308690 },
{ name: 'Tanzania', cca2: 'TZ', population: 59734218 },
{ name: 'Myanmar', cca2: 'MM', population: 54409800 },
{ name: 'South Korea', cca2: 'KR', population: 51269185 },
{ name: 'Colombia', cca2: 'CO', population: 50882891 },
{ name: 'Kenya', cca2: 'KE', population: 53771296 },
{ name: 'Spain', cca2: 'ES', population: 46754778 },
{ name: 'Argentina', cca2: 'AR', population: 45195774 },
];