Hour get and update works - and CLI for it
This commit is contained in:
parent
f2d1ecd097
commit
4d6840559d
@ -18,6 +18,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@date-fns/tz": "^1.2.0",
|
"@date-fns/tz": "^1.2.0",
|
||||||
"@lifetracker/db": "workspace:*",
|
"@lifetracker/db": "workspace:*",
|
||||||
|
"@lifetracker/shared": "workspace:^",
|
||||||
"@lifetracker/trpc": "workspace:^",
|
"@lifetracker/trpc": "workspace:^",
|
||||||
"date-fns": "^4.1.0",
|
"date-fns": "^4.1.0",
|
||||||
"dotenv": "^16.4.1",
|
"dotenv": "^16.4.1",
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import { Command } from "@commander-js/extra-typings";
|
|||||||
import { getBorderCharacters, table } from "table";
|
import { getBorderCharacters, table } from "table";
|
||||||
import { format } from "date-fns";
|
import { format } from "date-fns";
|
||||||
import { TZDate } from "@date-fns/tz";
|
import { TZDate } from "@date-fns/tz";
|
||||||
|
import { getHourFromTime, getTimeFromHour } from "@lifetracker/shared/utils/hours";
|
||||||
|
|
||||||
function moodToStars(mood: number) {
|
function moodToStars(mood: number) {
|
||||||
// const full_stars = Math.floor(mood / 2);
|
// const full_stars = Math.floor(mood / 2);
|
||||||
@ -41,17 +42,21 @@ export const daysCmd = new Command()
|
|||||||
|
|
||||||
const moodStr = moodToStars(day.mood);
|
const moodStr = moodToStars(day.mood);
|
||||||
const dateStr = format(day.date, "EEEE, MMMM do");
|
const dateStr = format(day.date, "EEEE, MMMM do");
|
||||||
const data: string[][] = [[dateStr, moodStr], [day.comment ?? "No comment", '',]];
|
const data: string[][] = [[dateStr, '', moodStr], [day.comment ?? "No comment", '', '']];
|
||||||
|
|
||||||
|
day.hours.forEach((h) => {
|
||||||
|
data.push([getHourFromTime(h.time), h.categoryCode ?? "--", h.comment ?? ""]);
|
||||||
|
})
|
||||||
|
|
||||||
console.log(table(data, {
|
console.log(table(data, {
|
||||||
// border: getBorderCharacters("ramac"),
|
// border: getBorderCharacters("ramac"),
|
||||||
// singleLine: true,
|
// singleLine: true,
|
||||||
spanningCells: [{ col: 0, row: 1, colSpan: 2 }],
|
spanningCells: [{ col: 0, row: 1, colSpan: 3 }, { col: 1, row: 2, colSpan: 2 }],
|
||||||
drawVerticalLine: (lineIndex, columnCount) => {
|
drawVerticalLine: (lineIndex, columnCount) => {
|
||||||
return lineIndex === 0 || lineIndex === columnCount || (lineIndex === 0 && columnCount === 2);
|
return lineIndex === 0 || lineIndex === columnCount || (lineIndex === 0 && columnCount === 2);
|
||||||
},
|
},
|
||||||
drawHorizontalLine: (lineIndex, rowCount) => {
|
drawHorizontalLine: (lineIndex, rowCount) => {
|
||||||
return (lineIndex < 2 || lineIndex === rowCount);
|
return (lineIndex < 2 || lineIndex === 2 || lineIndex === rowCount);
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|||||||
67
apps/cli/src/commands/hours.ts
Normal file
67
apps/cli/src/commands/hours.ts
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
import { getGlobalOptions } from "@/lib/globals";
|
||||||
|
import {
|
||||||
|
printError,
|
||||||
|
printErrorMessageWithReason,
|
||||||
|
printObject,
|
||||||
|
printSuccess,
|
||||||
|
} from "@/lib/output";
|
||||||
|
import { getAPIClient } from "@/lib/trpc";
|
||||||
|
import { Command } from "@commander-js/extra-typings";
|
||||||
|
import { getBorderCharacters, table } from "table";
|
||||||
|
import { format } from "date-fns";
|
||||||
|
import { TZDate } from "@date-fns/tz";
|
||||||
|
import { getHourFromTime, getTimeFromHour } from "@lifetracker/shared/utils/hours";
|
||||||
|
import { ZHour } from "@lifetracker/shared/types/days";
|
||||||
|
|
||||||
|
export const hoursCmd = new Command()
|
||||||
|
.name("hour")
|
||||||
|
.description("Get or set data for a specific hour")
|
||||||
|
.argument('<date>', 'A date in ISO-8601 format, or "yesterday", "today", "tomorrow", etc.')
|
||||||
|
.argument('<hour>', 'An hour between 0-23, or 1-12 [AM/PM]')
|
||||||
|
.argument('[code]', 'Optionally set the code for this hour')
|
||||||
|
.option('-c, --comment <comment>', "edit this hour's comment")
|
||||||
|
.action(async (dateQuery = "today", hour: string, code: string | undefined, flags?) => {
|
||||||
|
const api = getAPIClient();
|
||||||
|
|
||||||
|
let res: string | ZHour;
|
||||||
|
try {
|
||||||
|
const props = { dateQuery: dateQuery, time: getTimeFromHour(hour), code: code, ...flags };
|
||||||
|
if (code) {
|
||||||
|
// Update
|
||||||
|
console.log(props);
|
||||||
|
res = await api.hours.update.mutate({ ...props });
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Get
|
||||||
|
res = await api.hours.get.query({ ...props });
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
printErrorMessageWithReason("Failed to manipulate hour", error as object);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getGlobalOptions().json) {
|
||||||
|
printObject(res);
|
||||||
|
} else {
|
||||||
|
const data = [
|
||||||
|
['Doing:', res.categoryName ?? "Undefined"],
|
||||||
|
|
||||||
|
];
|
||||||
|
if (res.comment) {
|
||||||
|
data.push(['Comment:', res.comment ?? "Undefined"]);
|
||||||
|
}
|
||||||
|
console.log(table(data, {
|
||||||
|
// border: getBorderCharacters("ramac"),
|
||||||
|
// singleLine: true,
|
||||||
|
// spanningCells: [{ col: 0, row: 0, colSpan: 2 },],
|
||||||
|
header: { alignment: "center", content: `${format(res.date, "EEEE, MMMM dd")} at ${getHourFromTime(res.time, true)}` },
|
||||||
|
drawVerticalLine: (lineIndex, columnCount) => {
|
||||||
|
return lineIndex === 0 || lineIndex === columnCount || (lineIndex === 0 && columnCount === 2);
|
||||||
|
},
|
||||||
|
drawHorizontalLine: (lineIndex, rowCount) => {
|
||||||
|
return (lineIndex < 2 || lineIndex === 2 || lineIndex === rowCount);
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
});
|
||||||
@ -5,6 +5,7 @@ import { whoamiCmd } from "@/commands/whoami";
|
|||||||
import { colorsCmd } from "@/commands/colors";
|
import { colorsCmd } from "@/commands/colors";
|
||||||
import { categoriesCmd } from "@/commands/categories";
|
import { categoriesCmd } from "@/commands/categories";
|
||||||
import { daysCmd } from "@/commands/days";
|
import { daysCmd } from "@/commands/days";
|
||||||
|
import { hoursCmd } from "./commands/hours";
|
||||||
|
|
||||||
import { config } from "dotenv";
|
import { config } from "dotenv";
|
||||||
|
|
||||||
@ -40,6 +41,7 @@ program.addCommand(whoamiCmd);
|
|||||||
program.addCommand(daysCmd);
|
program.addCommand(daysCmd);
|
||||||
program.addCommand(colorsCmd);
|
program.addCommand(colorsCmd);
|
||||||
program.addCommand(categoriesCmd);
|
program.addCommand(categoriesCmd);
|
||||||
|
program.addCommand(hoursCmd);
|
||||||
|
|
||||||
|
|
||||||
setGlobalOptions(program.opts());
|
setGlobalOptions(program.opts());
|
||||||
|
|||||||
@ -42,6 +42,11 @@ export default async function DayView({
|
|||||||
<EditableDayComment day={day}
|
<EditableDayComment day={day}
|
||||||
className="text-xl"
|
className="text-xl"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<Separator />
|
||||||
|
|
||||||
|
{day.hours}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -57,19 +57,21 @@ CREATE TABLE `day` (
|
|||||||
`id` text PRIMARY KEY NOT NULL,
|
`id` text PRIMARY KEY NOT NULL,
|
||||||
`date` text NOT NULL,
|
`date` text NOT NULL,
|
||||||
`mood` integer,
|
`mood` integer,
|
||||||
`comment` text
|
`comment` text,
|
||||||
|
`userId` text NOT NULL,
|
||||||
|
FOREIGN KEY (`userId`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE cascade
|
||||||
);
|
);
|
||||||
--> statement-breakpoint
|
--> statement-breakpoint
|
||||||
CREATE TABLE `entry` (
|
CREATE TABLE `hour` (
|
||||||
`id` text PRIMARY KEY NOT NULL,
|
`id` text PRIMARY KEY NOT NULL,
|
||||||
`createdAt` integer NOT NULL,
|
`createdAt` integer NOT NULL,
|
||||||
`userId` text NOT NULL,
|
`userId` text NOT NULL,
|
||||||
`comment` text,
|
`comment` text,
|
||||||
`dayId` integer,
|
`time` integer,
|
||||||
`startedAt` integer NOT NULL,
|
`dayId` text NOT NULL,
|
||||||
`endedAt` integer NOT NULL,
|
`categoryId` text,
|
||||||
`categoryId` text NOT NULL,
|
|
||||||
FOREIGN KEY (`userId`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE cascade,
|
FOREIGN KEY (`userId`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE cascade,
|
||||||
|
FOREIGN KEY (`dayId`) REFERENCES `day`(`id`) ON UPDATE no action ON DELETE no action,
|
||||||
FOREIGN KEY (`categoryId`) REFERENCES `category`(`id`) ON UPDATE no action ON DELETE no action
|
FOREIGN KEY (`categoryId`) REFERENCES `category`(`id`) ON UPDATE no action ON DELETE no action
|
||||||
);
|
);
|
||||||
--> statement-breakpoint
|
--> statement-breakpoint
|
||||||
@ -102,5 +104,6 @@ CREATE UNIQUE INDEX `apiKey_name_userId_unique` ON `apiKey` (`name`,`userId`);--
|
|||||||
CREATE UNIQUE INDEX `category_userId_code_unique` ON `category` (`userId`,`code`);--> statement-breakpoint
|
CREATE UNIQUE INDEX `category_userId_code_unique` ON `category` (`userId`,`code`);--> statement-breakpoint
|
||||||
CREATE UNIQUE INDEX `color_userId_name_unique` ON `color` (`userId`,`name`);--> statement-breakpoint
|
CREATE UNIQUE INDEX `color_userId_name_unique` ON `color` (`userId`,`name`);--> statement-breakpoint
|
||||||
CREATE UNIQUE INDEX `day_date_unique` ON `day` (`date`);--> statement-breakpoint
|
CREATE UNIQUE INDEX `day_date_unique` ON `day` (`date`);--> statement-breakpoint
|
||||||
CREATE UNIQUE INDEX `entry_userId_startedAt_endedAt_unique` ON `entry` (`userId`,`startedAt`,`endedAt`);--> statement-breakpoint
|
CREATE UNIQUE INDEX `hour_time_unique` ON `hour` (`time`);--> statement-breakpoint
|
||||||
|
CREATE UNIQUE INDEX `hour_dayId_time_unique` ON `hour` (`dayId`,`time`);--> statement-breakpoint
|
||||||
CREATE UNIQUE INDEX `user_email_unique` ON `user` (`email`);
|
CREATE UNIQUE INDEX `user_email_unique` ON `user` (`email`);
|
||||||
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"version": "6",
|
"version": "6",
|
||||||
"dialect": "sqlite",
|
"dialect": "sqlite",
|
||||||
"id": "ceed45ad-37d0-4e59-92bd-3be02d271535",
|
"id": "ac3ad6ee-ccd2-4f91-9be7-83bd5b1d9ec8",
|
||||||
"prevId": "00000000-0000-0000-0000-000000000000",
|
"prevId": "00000000-0000-0000-0000-000000000000",
|
||||||
"tables": {
|
"tables": {
|
||||||
"account": {
|
"account": {
|
||||||
@ -434,6 +434,13 @@
|
|||||||
"primaryKey": false,
|
"primaryKey": false,
|
||||||
"notNull": false,
|
"notNull": false,
|
||||||
"autoincrement": false
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"userId": {
|
||||||
|
"name": "userId",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"indexes": {
|
"indexes": {
|
||||||
@ -445,12 +452,26 @@
|
|||||||
"isUnique": true
|
"isUnique": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"foreignKeys": {},
|
"foreignKeys": {
|
||||||
|
"day_userId_user_id_fk": {
|
||||||
|
"name": "day_userId_user_id_fk",
|
||||||
|
"tableFrom": "day",
|
||||||
|
"tableTo": "user",
|
||||||
|
"columnsFrom": [
|
||||||
|
"userId"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "cascade",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
"compositePrimaryKeys": {},
|
"compositePrimaryKeys": {},
|
||||||
"uniqueConstraints": {}
|
"uniqueConstraints": {}
|
||||||
},
|
},
|
||||||
"entry": {
|
"hour": {
|
||||||
"name": "entry",
|
"name": "hour",
|
||||||
"columns": {
|
"columns": {
|
||||||
"id": {
|
"id": {
|
||||||
"name": "id",
|
"name": "id",
|
||||||
@ -480,23 +501,16 @@
|
|||||||
"notNull": false,
|
"notNull": false,
|
||||||
"autoincrement": false
|
"autoincrement": false
|
||||||
},
|
},
|
||||||
"dayId": {
|
"time": {
|
||||||
"name": "dayId",
|
"name": "time",
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"primaryKey": false,
|
"primaryKey": false,
|
||||||
"notNull": false,
|
"notNull": false,
|
||||||
"autoincrement": false
|
"autoincrement": false
|
||||||
},
|
},
|
||||||
"startedAt": {
|
"dayId": {
|
||||||
"name": "startedAt",
|
"name": "dayId",
|
||||||
"type": "integer",
|
"type": "text",
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"autoincrement": false
|
|
||||||
},
|
|
||||||
"endedAt": {
|
|
||||||
"name": "endedAt",
|
|
||||||
"type": "integer",
|
|
||||||
"primaryKey": false,
|
"primaryKey": false,
|
||||||
"notNull": true,
|
"notNull": true,
|
||||||
"autoincrement": false
|
"autoincrement": false
|
||||||
@ -505,25 +519,31 @@
|
|||||||
"name": "categoryId",
|
"name": "categoryId",
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"primaryKey": false,
|
"primaryKey": false,
|
||||||
"notNull": true,
|
"notNull": false,
|
||||||
"autoincrement": false
|
"autoincrement": false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"indexes": {
|
"indexes": {
|
||||||
"entry_userId_startedAt_endedAt_unique": {
|
"hour_time_unique": {
|
||||||
"name": "entry_userId_startedAt_endedAt_unique",
|
"name": "hour_time_unique",
|
||||||
"columns": [
|
"columns": [
|
||||||
"userId",
|
"time"
|
||||||
"startedAt",
|
],
|
||||||
"endedAt"
|
"isUnique": true
|
||||||
|
},
|
||||||
|
"hour_dayId_time_unique": {
|
||||||
|
"name": "hour_dayId_time_unique",
|
||||||
|
"columns": [
|
||||||
|
"dayId",
|
||||||
|
"time"
|
||||||
],
|
],
|
||||||
"isUnique": true
|
"isUnique": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"foreignKeys": {
|
"foreignKeys": {
|
||||||
"entry_userId_user_id_fk": {
|
"hour_userId_user_id_fk": {
|
||||||
"name": "entry_userId_user_id_fk",
|
"name": "hour_userId_user_id_fk",
|
||||||
"tableFrom": "entry",
|
"tableFrom": "hour",
|
||||||
"tableTo": "user",
|
"tableTo": "user",
|
||||||
"columnsFrom": [
|
"columnsFrom": [
|
||||||
"userId"
|
"userId"
|
||||||
@ -534,9 +554,22 @@
|
|||||||
"onDelete": "cascade",
|
"onDelete": "cascade",
|
||||||
"onUpdate": "no action"
|
"onUpdate": "no action"
|
||||||
},
|
},
|
||||||
"entry_categoryId_category_id_fk": {
|
"hour_dayId_day_id_fk": {
|
||||||
"name": "entry_categoryId_category_id_fk",
|
"name": "hour_dayId_day_id_fk",
|
||||||
"tableFrom": "entry",
|
"tableFrom": "hour",
|
||||||
|
"tableTo": "day",
|
||||||
|
"columnsFrom": [
|
||||||
|
"dayId"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
},
|
||||||
|
"hour_categoryId_category_id_fk": {
|
||||||
|
"name": "hour_categoryId_category_id_fk",
|
||||||
|
"tableFrom": "hour",
|
||||||
"tableTo": "category",
|
"tableTo": "category",
|
||||||
"columnsFrom": [
|
"columnsFrom": [
|
||||||
"categoryId"
|
"categoryId"
|
||||||
|
|||||||
@ -5,8 +5,8 @@
|
|||||||
{
|
{
|
||||||
"idx": 0,
|
"idx": 0,
|
||||||
"version": "6",
|
"version": "6",
|
||||||
"when": 1732544381615,
|
"when": 1732648521848,
|
||||||
"tag": "0000_powerful_zodiak",
|
"tag": "0000_even_mysterio",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "drizzle-kit studio",
|
"dev": "drizzle-kit studio",
|
||||||
"generate": "drizzle-kit generate",
|
"generate": "drizzle-kit generate",
|
||||||
"reset": "tsc reset.ts",
|
"reset": "tsx reset.ts",
|
||||||
"typecheck": "tsc --noEmit",
|
"typecheck": "tsc --noEmit",
|
||||||
"migrate": "tsx migrate.ts",
|
"migrate": "tsx migrate.ts",
|
||||||
"studio": "drizzle-kit studio"
|
"studio": "drizzle-kit studio"
|
||||||
@ -26,7 +26,8 @@
|
|||||||
"@lifetracker/typescript-config": "workspace:*",
|
"@lifetracker/typescript-config": "workspace:*",
|
||||||
"@tsconfig/node21": "^21.0.1",
|
"@tsconfig/node21": "^21.0.1",
|
||||||
"@types/better-sqlite3": "^7.6.9",
|
"@types/better-sqlite3": "^7.6.9",
|
||||||
"drizzle-kit": "^0.24.2"
|
"drizzle-kit": "^0.24.2",
|
||||||
|
"sqlite3": "^5.1.7"
|
||||||
},
|
},
|
||||||
"eslintConfig": {
|
"eslintConfig": {
|
||||||
"root": true,
|
"root": true,
|
||||||
|
|||||||
93
packages/db/reset.ts
Normal file
93
packages/db/reset.ts
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
import sqlite3 from 'sqlite3';
|
||||||
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
|
|
||||||
|
// Read JSON files
|
||||||
|
const users = JSON.parse(fs.readFileSync('./seed/user.json', 'utf-8'));
|
||||||
|
const apiKeys = JSON.parse(fs.readFileSync('./seed/apiKey.json', 'utf-8'));
|
||||||
|
const colors = JSON.parse(fs.readFileSync('./seed/color.json', 'utf-8'));
|
||||||
|
const categories = JSON.parse(fs.readFileSync('./seed/category.json', 'utf-8'));
|
||||||
|
|
||||||
|
// Connect to SQLite
|
||||||
|
const db = new sqlite3.Database('/home/ryan/Notes/lifetracker/lifetracker.db');
|
||||||
|
|
||||||
|
// Helper function to insert data
|
||||||
|
const insertData = (table, columns, values) => {
|
||||||
|
const placeholders = columns.map(() => '?').join(', ');
|
||||||
|
const sql = `INSERT INTO ${table} (${columns.join(', ')}) VALUES (${placeholders})`;
|
||||||
|
db.run(sql, values, (err) => {
|
||||||
|
if (err) {
|
||||||
|
console.error(`Error inserting into ${table}:`, err.message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Insert users
|
||||||
|
users.forEach(user => {
|
||||||
|
insertData(
|
||||||
|
'user',
|
||||||
|
['id', 'name', 'email', 'emailVerified', 'image', 'password', 'role'],
|
||||||
|
[
|
||||||
|
user.id,
|
||||||
|
user.name,
|
||||||
|
user.email,
|
||||||
|
user.emailVerified || null,
|
||||||
|
user.image || null,
|
||||||
|
user.password,
|
||||||
|
user.role || 'user'
|
||||||
|
]
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Insert API keys
|
||||||
|
apiKeys.forEach(apiKey => {
|
||||||
|
insertData(
|
||||||
|
'apiKey',
|
||||||
|
['id', 'keyHash', 'keyId', 'name', 'userId', 'createdAt'],
|
||||||
|
[
|
||||||
|
apiKey.id,
|
||||||
|
apiKey.keyHash,
|
||||||
|
apiKey.keyId,
|
||||||
|
apiKey.name,
|
||||||
|
apiKey.userId,
|
||||||
|
apiKey.createdAt || new Date().toISOString()
|
||||||
|
]
|
||||||
|
);
|
||||||
|
});
|
||||||
|
categories.forEach(category => {
|
||||||
|
insertData(
|
||||||
|
'category',
|
||||||
|
['id', 'code', 'colorId', 'createdAt', 'description', 'name', 'parentId', 'userId'],
|
||||||
|
[
|
||||||
|
category.id,
|
||||||
|
category.code,
|
||||||
|
category.colorId || null,
|
||||||
|
category.createdAt || new Date().toISOString(),
|
||||||
|
category.description || null,
|
||||||
|
category.name,
|
||||||
|
category.parentId || null,
|
||||||
|
category.userId
|
||||||
|
]
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
colors.forEach(color => {
|
||||||
|
insertData(
|
||||||
|
'color',
|
||||||
|
['id', 'name', 'hexcode', 'inverse', 'userId', 'createdAt'],
|
||||||
|
[
|
||||||
|
color.id,
|
||||||
|
color.name,
|
||||||
|
color.hexcode,
|
||||||
|
color.inverse || null,
|
||||||
|
color.userId,
|
||||||
|
color.createdAt || new Date().toISOString()
|
||||||
|
]
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Close the connection
|
||||||
|
db.close(() => {
|
||||||
|
console.log('Database population completed!');
|
||||||
|
});
|
||||||
@ -130,8 +130,34 @@ export const days = sqliteTable("day", {
|
|||||||
date: text("date").notNull().unique(),
|
date: text("date").notNull().unique(),
|
||||||
mood: integer("mood"),
|
mood: integer("mood"),
|
||||||
comment: text("comment"),
|
comment: text("comment"),
|
||||||
|
userId: text("userId")
|
||||||
|
.notNull()
|
||||||
|
.references(() => users.id, { onDelete: "cascade" }),
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const hours = sqliteTable(
|
||||||
|
"hour",
|
||||||
|
{
|
||||||
|
id: text("id")
|
||||||
|
.notNull()
|
||||||
|
.primaryKey()
|
||||||
|
.$defaultFn(() => createId()),
|
||||||
|
createdAt: createdAtField(),
|
||||||
|
userId: text("userId")
|
||||||
|
.notNull()
|
||||||
|
.references(() => users.id, { onDelete: "cascade" }),
|
||||||
|
comment: text("comment"),
|
||||||
|
time: integer("time").unique(),
|
||||||
|
dayId: text("dayId").notNull().references(() => days.id),
|
||||||
|
categoryId: text("categoryId").references(() => categories.id),
|
||||||
|
},
|
||||||
|
(e) => ({
|
||||||
|
uniq: unique().on(e.dayId, e.time)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
export const colors = sqliteTable(
|
export const colors = sqliteTable(
|
||||||
"color",
|
"color",
|
||||||
{
|
{
|
||||||
@ -179,27 +205,7 @@ export const categories = sqliteTable(
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
export const entries = sqliteTable(
|
|
||||||
"entry",
|
|
||||||
{
|
|
||||||
id: text("id")
|
|
||||||
.notNull()
|
|
||||||
.primaryKey()
|
|
||||||
.$defaultFn(() => createId()),
|
|
||||||
createdAt: createdAtField(),
|
|
||||||
userId: text("userId")
|
|
||||||
.notNull()
|
|
||||||
.references(() => users.id, { onDelete: "cascade" }),
|
|
||||||
comment: text("comment"),
|
|
||||||
dayId: integer("dayId"),
|
|
||||||
startedAt: integer("startedAt", { mode: "timestamp" }).notNull(),
|
|
||||||
endedAt: integer("endedAt", { mode: "timestamp" }).notNull(),
|
|
||||||
categoryId: text("categoryId").notNull().references(() => categories.id),
|
|
||||||
},
|
|
||||||
(e) => ({
|
|
||||||
uniq: unique().on(e.userId, e.startedAt, e.endedAt)
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
// Relations
|
// Relations
|
||||||
|
|
||||||
@ -212,7 +218,8 @@ export const apiKeyRelations = relations(apiKeys, ({ one }) => ({
|
|||||||
export const userRelations = relations(users, ({ many }) => ({
|
export const userRelations = relations(users, ({ many }) => ({
|
||||||
categories: many(categories),
|
categories: many(categories),
|
||||||
colors: many(colors),
|
colors: many(colors),
|
||||||
entries: many(entries),
|
days: many(days),
|
||||||
|
hours: many(hours),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
||||||
@ -242,31 +249,35 @@ export const categoriesRelations = relations(
|
|||||||
fields: [categories.parentId],
|
fields: [categories.parentId],
|
||||||
references: [categories.id],
|
references: [categories.id],
|
||||||
}),
|
}),
|
||||||
entries: many(entries),
|
hours: many(hours),
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
export const daysRelations = relations(
|
export const daysRelations = relations(
|
||||||
days,
|
days,
|
||||||
({ many }) => ({
|
({ many, one }) => ({
|
||||||
entries: many(entries),
|
user: one(users, {
|
||||||
|
fields: [days.userId],
|
||||||
|
references: [users.id],
|
||||||
|
}),
|
||||||
|
hours: many(hours),
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
export const entriesRelations = relations(
|
export const hoursRelations = relations(
|
||||||
entries,
|
hours,
|
||||||
({ many, one }) => ({
|
({ many, one }) => ({
|
||||||
user: one(users, {
|
user: one(users, {
|
||||||
fields: [entries.userId],
|
fields: [hours.userId],
|
||||||
references: [users.id],
|
references: [users.id],
|
||||||
}),
|
}),
|
||||||
categories: one(categories, {
|
categories: one(categories, {
|
||||||
fields: [entries.categoryId],
|
fields: [hours.categoryId],
|
||||||
references: [categories.id],
|
references: [categories.id],
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
day: one(days, {
|
day: one(days, {
|
||||||
fields: [entries.dayId],
|
fields: [hours.dayId],
|
||||||
references: [days.id],
|
references: [days.id],
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
|
|||||||
@ -5,6 +5,8 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@date-fns/tz": "^1.2.0",
|
||||||
|
"date-fns": "^4.1.0",
|
||||||
"winston": "^3.17.0",
|
"winston": "^3.17.0",
|
||||||
"zod": "^3.23.8"
|
"zod": "^3.23.8"
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,9 +1,24 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
|
// Define the schema for the "hour" object
|
||||||
|
export const zHourSchema = z.object({
|
||||||
|
id: z.string().optional(),
|
||||||
|
dayId: z.string(),
|
||||||
|
date: z.string().optional(),
|
||||||
|
time: z.number(),
|
||||||
|
categoryCode: z.coerce.number().nullable(),
|
||||||
|
categoryId: z.string().nullable(),
|
||||||
|
categoryName: z.string().nullable(),
|
||||||
|
comment: z.string().nullable(),
|
||||||
|
});
|
||||||
|
export type ZHour = z.infer<typeof zHourSchema>;
|
||||||
|
|
||||||
export const zDaySchema = z.object({
|
export const zDaySchema = z.object({
|
||||||
id: z.string().optional(),
|
id: z.string().optional(),
|
||||||
date: z.string(),
|
date: z.string(),
|
||||||
mood: z.number().nullable(),
|
mood: z.number().nullable(),
|
||||||
comment: z.string().nullable(),
|
comment: z.string().nullable(),
|
||||||
|
hours: z.array(zHourSchema),
|
||||||
|
|
||||||
});
|
});
|
||||||
export type ZDay = z.infer<typeof zDaySchema>;
|
export type ZDay = z.infer<typeof zDaySchema>;
|
||||||
|
|||||||
13
packages/shared/utils/days.ts
Normal file
13
packages/shared/utils/days.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { format } from "date-fns";
|
||||||
|
import { TZDate } from "@date-fns/tz";
|
||||||
|
|
||||||
|
export function dateFromInput(input: { dateQuery: string }) {
|
||||||
|
let t: string;
|
||||||
|
if (input.dateQuery == "today") {
|
||||||
|
t = TZDate.tz("America/Los_Angeles");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
t = new TZDate(input.dateQuery, "Etc/UTC");
|
||||||
|
}
|
||||||
|
return format(t, "yyyy-MM-dd") + "T00:00:00";
|
||||||
|
}
|
||||||
28
packages/shared/utils/hours.ts
Normal file
28
packages/shared/utils/hours.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
export function getHourFromTime(time: number, includePeriod = false) {
|
||||||
|
const hour = time == 0 || time == 12 ? 12 : time % 12;
|
||||||
|
const period = time < 12 ? "AM" : "PM";
|
||||||
|
return includePeriod ? `${hour} ${period}` : `${hour}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getTimeFromHour(hour: string) {
|
||||||
|
const match = hour.match(/^(\d{1,2}) ?([aApP][mM])?$/);
|
||||||
|
if (!match) {
|
||||||
|
throw new Error("Invalid time format");
|
||||||
|
}
|
||||||
|
|
||||||
|
let time = parseInt(match[1]);
|
||||||
|
const period = match[2] ? match[2].toUpperCase() : null;
|
||||||
|
|
||||||
|
if (time > 12 || time < 1) {
|
||||||
|
throw new Error("Invalid hour");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (period === 'PM' && time !== 12) {
|
||||||
|
time += 12;
|
||||||
|
} else if (period === 'AM' && time === 12) {
|
||||||
|
time = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return time;
|
||||||
|
}
|
||||||
|
|
||||||
@ -6,12 +6,14 @@ import { adminAppRouter } from "./admin";
|
|||||||
import { categoriesAppRouter } from "./categories";
|
import { categoriesAppRouter } from "./categories";
|
||||||
import { colorsAppRouter } from "./colors";
|
import { colorsAppRouter } from "./colors";
|
||||||
import { daysAppRouter } from "./days";
|
import { daysAppRouter } from "./days";
|
||||||
|
import { hoursAppRouter } from "./hours";
|
||||||
|
|
||||||
export const appRouter = router({
|
export const appRouter = router({
|
||||||
users: usersAppRouter,
|
users: usersAppRouter,
|
||||||
apiKeys: apiKeysAppRouter,
|
apiKeys: apiKeysAppRouter,
|
||||||
admin: adminAppRouter,
|
admin: adminAppRouter,
|
||||||
days: daysAppRouter,
|
days: daysAppRouter,
|
||||||
|
hours: hoursAppRouter,
|
||||||
colors: colorsAppRouter,
|
colors: colorsAppRouter,
|
||||||
categories: categoriesAppRouter,
|
categories: categoriesAppRouter,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -3,33 +3,22 @@ import { and, desc, eq, inArray, notExists } from "drizzle-orm";
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { SqliteError } from "@lifetracker/db";
|
import { SqliteError } from "@lifetracker/db";
|
||||||
import { days, } from "@lifetracker/db/schema";
|
import { categories, days, hours, } from "@lifetracker/db/schema";
|
||||||
import {
|
import {
|
||||||
zDaySchema, ZDay
|
zDaySchema, ZDay
|
||||||
} from "@lifetracker/shared/types/days";
|
} from "@lifetracker/shared/types/days";
|
||||||
import type { Context } from "../index";
|
import type { Context } from "../index";
|
||||||
import { authedProcedure, router } from "../index";
|
import { authedProcedure, router } from "../index";
|
||||||
import { format } from "date-fns";
|
import { dateFromInput } from "@lifetracker/shared/utils/days";
|
||||||
import { TZDate } from "@date-fns/tz";
|
|
||||||
|
|
||||||
function dateFromInput(input: { dateQuery: string }) {
|
|
||||||
let t: string;
|
|
||||||
if (input.dateQuery == "today") {
|
|
||||||
t = TZDate.tz("America/Los_Angeles");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
t = new TZDate(input.dateQuery, "Etc/UTC");
|
|
||||||
}
|
|
||||||
|
|
||||||
return format(t, "yyyy-MM-dd") + "T00:00:00";
|
|
||||||
}
|
|
||||||
|
|
||||||
async function createDay(date: string, ctx: Context) {
|
async function createDay(date: string, ctx: Context) {
|
||||||
return await ctx.db.transaction(async (trx) => {
|
return await ctx.db.transaction(async (trx) => {
|
||||||
try {
|
try {
|
||||||
const result = await trx
|
// Create the Day object
|
||||||
|
const dayRes = await trx
|
||||||
.insert(days)
|
.insert(days)
|
||||||
.values({
|
.values({
|
||||||
|
userId: ctx.user!.id,
|
||||||
date: date,
|
date: date,
|
||||||
})
|
})
|
||||||
.returning({
|
.returning({
|
||||||
@ -38,7 +27,20 @@ async function createDay(date: string, ctx: Context) {
|
|||||||
mood: days.mood,
|
mood: days.mood,
|
||||||
comment: days.comment,
|
comment: days.comment,
|
||||||
});
|
});
|
||||||
return result[0];
|
|
||||||
|
const dayId = dayRes[0].id;
|
||||||
|
|
||||||
|
// Generate 24 "hour" objects
|
||||||
|
const hoursData = Array.from({ length: 24 }, (_, hour) => ({
|
||||||
|
userId: ctx.user!.id,
|
||||||
|
dayId: dayId,
|
||||||
|
time: hour
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Insert the "hour" objects
|
||||||
|
await trx.insert(hours).values(hoursData);
|
||||||
|
|
||||||
|
return dayRes;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(e);
|
console.log(e);
|
||||||
if (e instanceof SqliteError) {
|
if (e instanceof SqliteError) {
|
||||||
@ -66,7 +68,9 @@ export const daysAppRouter = router({
|
|||||||
.query(async ({ input, ctx }) => {
|
.query(async ({ input, ctx }) => {
|
||||||
const date = dateFromInput(input);
|
const date = dateFromInput(input);
|
||||||
|
|
||||||
const res = await ctx.db
|
// Fetch the day data
|
||||||
|
let dayRes;
|
||||||
|
dayRes = await ctx.db
|
||||||
.select({
|
.select({
|
||||||
id: days.id,
|
id: days.id,
|
||||||
date: days.date,
|
date: days.date,
|
||||||
@ -76,8 +80,32 @@ export const daysAppRouter = router({
|
|||||||
.from(days)
|
.from(days)
|
||||||
.where(eq(days.date, date));
|
.where(eq(days.date, date));
|
||||||
|
|
||||||
const day = res.length == 0 ? createDay(date, ctx) : res[0];
|
if (dayRes.length === 0) {
|
||||||
return day;
|
dayRes = await createDay(date, ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
const day = dayRes[0];
|
||||||
|
|
||||||
|
// Fetch the hours data for the corresponding dayId
|
||||||
|
const hoursRes = await ctx.db
|
||||||
|
.select({
|
||||||
|
id: hours.id,
|
||||||
|
dayId: hours.dayId,
|
||||||
|
time: hours.time,
|
||||||
|
categoryId: hours.categoryId,
|
||||||
|
categoryCode: categories.code,
|
||||||
|
comment: hours.comment,
|
||||||
|
})
|
||||||
|
.from(hours)
|
||||||
|
.where(eq(hours.dayId, day.id))
|
||||||
|
.leftJoin(categories, eq(categories.id, hours.categoryId))
|
||||||
|
|
||||||
|
// Combine the day and hours data
|
||||||
|
const result = {
|
||||||
|
...day,
|
||||||
|
hours: hoursRes,
|
||||||
|
};
|
||||||
|
return result;
|
||||||
}),
|
}),
|
||||||
update: authedProcedure
|
update: authedProcedure
|
||||||
.input(
|
.input(
|
||||||
|
|||||||
111
packages/trpc/routers/hours.ts
Normal file
111
packages/trpc/routers/hours.ts
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
import { experimental_trpcMiddleware, TRPCError } from "@trpc/server";
|
||||||
|
import { and, desc, eq, inArray, notExists } from "drizzle-orm";
|
||||||
|
import { date, z } from "zod";
|
||||||
|
|
||||||
|
import { SqliteError } from "@lifetracker/db";
|
||||||
|
import { categories, days, hours, } from "@lifetracker/db/schema";
|
||||||
|
import {
|
||||||
|
zDaySchema, ZDay, ZHour, zHourSchema
|
||||||
|
} from "@lifetracker/shared/types/days";
|
||||||
|
import type { Context } from "../index";
|
||||||
|
import { authedProcedure, router } from "../index";
|
||||||
|
import { format } from "date-fns";
|
||||||
|
import { TZDate } from "@date-fns/tz";
|
||||||
|
import { dateFromInput } from "@lifetracker/shared/utils/days";
|
||||||
|
|
||||||
|
export const hoursAppRouter = router({
|
||||||
|
get: authedProcedure
|
||||||
|
.input(z.object({
|
||||||
|
dateQuery: z.string(),
|
||||||
|
time: z.number()
|
||||||
|
}))
|
||||||
|
.output(zHourSchema)
|
||||||
|
.query(async ({ input, ctx }) => {
|
||||||
|
const date = dateFromInput(input);
|
||||||
|
console.log(input);
|
||||||
|
const hourRes = await ctx.db
|
||||||
|
.select({
|
||||||
|
id: hours.id,
|
||||||
|
dayId: hours.dayId,
|
||||||
|
time: hours.time,
|
||||||
|
categoryId: hours.categoryId,
|
||||||
|
categoryCode: categories.code,
|
||||||
|
categoryName: categories.name,
|
||||||
|
comment: hours.comment,
|
||||||
|
})
|
||||||
|
.from(hours)
|
||||||
|
.leftJoin(days, eq(days.id, hours.dayId)) // Ensure days table is joined first
|
||||||
|
.leftJoin(categories, eq(categories.id, hours.categoryId))
|
||||||
|
.where(and(eq(hours.time, input.time), eq(days.date, date))) // Use correct alias for days table
|
||||||
|
|
||||||
|
console.log(hourRes);
|
||||||
|
return {
|
||||||
|
date: format(date, "yyyy-MM-dd"),
|
||||||
|
...hourRes[0]
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
update: authedProcedure
|
||||||
|
.input(
|
||||||
|
z.object({
|
||||||
|
dateQuery: z.string(),
|
||||||
|
time: z.number(),
|
||||||
|
code: z.string().optional(),
|
||||||
|
comment: z.string().nullable(),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
// .output(zHourSchema)
|
||||||
|
.mutation(async ({ input, ctx }) => {
|
||||||
|
const { dateQuery, time, code, ...updatedProps } = input;
|
||||||
|
var date = dateFromInput({ dateQuery: dateQuery });
|
||||||
|
const category = await ctx.db.select(
|
||||||
|
{
|
||||||
|
id: categories.id,
|
||||||
|
name: categories.name,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.from(categories)
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
eq(categories.code, code),
|
||||||
|
eq(categories.userId, ctx.user!.id),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
const day = await ctx.db.select(
|
||||||
|
{ id: days.id }
|
||||||
|
)
|
||||||
|
.from(days)
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
eq(days.date, date),
|
||||||
|
eq(days.userId, ctx.user!.id),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
const newProps = {
|
||||||
|
categoryId: category[0].id,
|
||||||
|
...updatedProps
|
||||||
|
};
|
||||||
|
|
||||||
|
if (newProps.comment == "") { newProps.comment = null }
|
||||||
|
|
||||||
|
const hourRes = await ctx.db
|
||||||
|
.update(hours)
|
||||||
|
.set(newProps)
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
eq(hours.time, time),
|
||||||
|
eq(hours.dayId, day[0].id),
|
||||||
|
eq(hours.userId, ctx.user!.id)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.returning();
|
||||||
|
|
||||||
|
return {
|
||||||
|
date: format(date, "yyyy-MM-dd"),
|
||||||
|
categoryName: category[0].name,
|
||||||
|
...hourRes[0]
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
});
|
||||||
543
pnpm-lock.yaml
generated
543
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
93
scripts/create_user.sh
Executable file
93
scripts/create_user.sh
Executable file
@ -0,0 +1,93 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
const sqlite3 = require('sqlite3').verbose();
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
// Read JSON files
|
||||||
|
const users = JSON.parse(fs.readFileSync('user.json'));
|
||||||
|
const apikeys = JSON.parse(fs.readFileSync('apikey.json'));
|
||||||
|
const colors = JSON.parse(fs.readFileSync('color.json'));
|
||||||
|
const categories = JSON.parse(fs.readFileSync('category.json'));
|
||||||
|
|
||||||
|
// Connect to SQLite
|
||||||
|
const db = new sqlite3.Database('/home/ryan/Notes/lifetracker/lifetracker.db');
|
||||||
|
|
||||||
|
// Helper function to insert data
|
||||||
|
const insertData = (table, columns, values) => {
|
||||||
|
const placeholders = columns.map(() => '?').join(', ');
|
||||||
|
const sql = `INSERT INTO ${table} (${columns.join(', ')}) VALUES (${placeholders})`;
|
||||||
|
db.run(sql, values, (err) => {
|
||||||
|
if (err) {
|
||||||
|
console.error(`Error inserting into ${table}:`, err.message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Insert users
|
||||||
|
users.forEach(user => {
|
||||||
|
insertData(
|
||||||
|
'user',
|
||||||
|
['id', 'name', 'email', 'emailVerified', 'image', 'password', 'role'],
|
||||||
|
[
|
||||||
|
user.id,
|
||||||
|
user.name,
|
||||||
|
user.email,
|
||||||
|
user.emailVerified || null,
|
||||||
|
user.image || null,
|
||||||
|
user.password,
|
||||||
|
user.role || 'user'
|
||||||
|
]
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Insert API keys
|
||||||
|
apiKeys.forEach(apiKey => {
|
||||||
|
insertData(
|
||||||
|
'apiKey',
|
||||||
|
['id', 'keyHash', 'keyId', 'name', 'userId', 'createdAt'],
|
||||||
|
[
|
||||||
|
apiKey.id,
|
||||||
|
apiKey.keyHash,
|
||||||
|
apiKey.keyId,
|
||||||
|
apiKey.name,
|
||||||
|
apiKey.userId,
|
||||||
|
apiKey.createdAt || new Date().toISOString()
|
||||||
|
]
|
||||||
|
);
|
||||||
|
});
|
||||||
|
categories.forEach(category => {
|
||||||
|
insertData(
|
||||||
|
'category',
|
||||||
|
['id', 'code', 'colorId', 'createdAt', 'description', 'name', 'parentId', 'userId'],
|
||||||
|
[
|
||||||
|
category.id,
|
||||||
|
category.code,
|
||||||
|
category.colorId || null,
|
||||||
|
category.createdAt || new Date().toISOString(),
|
||||||
|
category.description || null,
|
||||||
|
category.name,
|
||||||
|
category.parentId || null,
|
||||||
|
category.userId
|
||||||
|
]
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
colors.forEach(color => {
|
||||||
|
insertData(
|
||||||
|
'color',
|
||||||
|
['id', 'name', 'hexcode', 'inverse', 'userId', 'createdAt'],
|
||||||
|
[
|
||||||
|
color.id,
|
||||||
|
color.name,
|
||||||
|
color.hexcode,
|
||||||
|
color.inverse || null,
|
||||||
|
color.userId,
|
||||||
|
color.createdAt || new Date().toISOString()
|
||||||
|
]
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Close the connection
|
||||||
|
db.close(() => {
|
||||||
|
console.log('Database population completed!');
|
||||||
|
});
|
||||||
Loading…
Reference in New Issue
Block a user