Now able to create categories!
This commit is contained in:
parent
47e8371fb3
commit
78144d0083
106
apps/cli/src/commands/categories.ts
Normal file
106
apps/cli/src/commands/categories.ts
Normal file
@ -0,0 +1,106 @@
|
||||
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";
|
||||
|
||||
export const categoriesCmd = new Command()
|
||||
.name("categories")
|
||||
.description("Manipulate categories");
|
||||
|
||||
categoriesCmd
|
||||
.command("list")
|
||||
.description("lists all defined categories")
|
||||
.action(async () => {
|
||||
const api = getAPIClient();
|
||||
|
||||
try {
|
||||
const categories = (await api.categories.list.query()).categories;
|
||||
// colors.sort((a, b) => b.numCategories - a.numCategories);
|
||||
if (getGlobalOptions().json) {
|
||||
printObject(categories);
|
||||
} else {
|
||||
const data: string[][] = [["Code", "Name", "Description", "Color"]];
|
||||
|
||||
categories.forEach((c) => {
|
||||
data.push([c.code.toString(), c.name, c.description ?? "none", c.color.name]);
|
||||
});
|
||||
console.log(
|
||||
data.length <= 1 ?
|
||||
"No categories found. Add one with `lifetracker categories create <code> <name> <description> <color>`" :
|
||||
table(data, {
|
||||
// border: getBorderCharacters("ramac"),
|
||||
// singleLine: true,
|
||||
drawVerticalLine: (lineIndex, columnCount) => {
|
||||
return lineIndex === 0 || lineIndex === columnCount;
|
||||
},
|
||||
drawHorizontalLine: (lineIndex, rowCount) => {
|
||||
return lineIndex < 2 || lineIndex === rowCount;
|
||||
},
|
||||
}),
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
printErrorMessageWithReason("Failed to list all categories", error as object);
|
||||
}
|
||||
});
|
||||
|
||||
categoriesCmd
|
||||
.command("create")
|
||||
.description("create a category")
|
||||
.argument("<code>", "the code of the category")
|
||||
.argument("<name>", "the name of the category")
|
||||
.argument("<description>", "the description of the category")
|
||||
.argument("[color]", "the color of the category")
|
||||
.option('-p, --parent <CODE>', "specify a parent category by code")
|
||||
.action(async (codeStr: string, name: string, description: string, colorName?: string, flags?) => {
|
||||
const api = getAPIClient();
|
||||
|
||||
const color = flags?.parent === undefined ?
|
||||
(await api.colors.get.query({ colorName: colorName! }))
|
||||
:
|
||||
(await api.categories.get.query({ categoryCode: parseInt(flags.parent) })).color
|
||||
|
||||
|
||||
try {
|
||||
await api.categories.create
|
||||
.mutate({
|
||||
code: parseFloat(codeStr),
|
||||
name: name,
|
||||
description: description,
|
||||
color: color,
|
||||
})
|
||||
.then(printSuccess(`Successfully created the category "${name}"`))
|
||||
.catch(printError(`Failed to create the category "${name}"`));
|
||||
api.categories.list
|
||||
}
|
||||
catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
categoriesCmd
|
||||
.command("delete")
|
||||
.description("delete a category")
|
||||
.argument("<code>", "the code of the category")
|
||||
.action(async (codeStr: string) => {
|
||||
const api = getAPIClient();
|
||||
|
||||
try {
|
||||
await api.categories.delete
|
||||
.mutate({
|
||||
categoryCode: parseInt(codeStr),
|
||||
})
|
||||
.then(printSuccess(`Successfully deleted category "${codeStr}"`))
|
||||
.catch(printError(`Failed to delete category "${codeStr}"`));
|
||||
}
|
||||
catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
});
|
||||
@ -25,17 +25,23 @@ colorsCmd
|
||||
if (getGlobalOptions().json) {
|
||||
printObject(colors);
|
||||
} else {
|
||||
const data: string[][] = [["Id", "Name", "Hexcode", "user"]];
|
||||
const data: string[][] = [["Name", "Hexcode", "Inverse"]];
|
||||
|
||||
colors.forEach((color) => {
|
||||
data.push([color.id, color.name, color.hexcode, color.userId]);
|
||||
data.push([color.name, color.hexcode, color.inverse]);
|
||||
});
|
||||
console.log(
|
||||
data.length <= 1 ?
|
||||
"No colors found. Add one with `lifetracker colors create <name> <hexcode>`" :
|
||||
table(data, {
|
||||
border: getBorderCharacters("ramac"),
|
||||
singleLine: true,
|
||||
// border: getBorderCharacters("ramac"),
|
||||
// singleLine: true,
|
||||
drawVerticalLine: (lineIndex, columnCount) => {
|
||||
return lineIndex === 0 || lineIndex === columnCount;
|
||||
},
|
||||
drawHorizontalLine: (lineIndex, rowCount) => {
|
||||
return lineIndex < 2 || lineIndex === rowCount;
|
||||
},
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@ import { Command, Option } from "@commander-js/extra-typings";
|
||||
|
||||
import { whoamiCmd } from "@/commands/whoami";
|
||||
import { colorsCmd } from "@/commands/colors";
|
||||
import { categoriesCmd } from "@/commands/categories";
|
||||
|
||||
import { config } from "dotenv";
|
||||
|
||||
@ -34,11 +35,9 @@ const program = new Command()
|
||||
: "0.0.0",
|
||||
);
|
||||
|
||||
program.addCommand(whoamiCmd, {
|
||||
isDefault: true
|
||||
});
|
||||
program.addCommand(whoamiCmd);
|
||||
program.addCommand(colorsCmd);
|
||||
|
||||
program.addCommand(categoriesCmd);
|
||||
|
||||
|
||||
setGlobalOptions(program.opts());
|
||||
|
||||
@ -52,7 +52,7 @@ export default function CategoriesView() {
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{categories.categories.map((c) => (
|
||||
<TableRow key={c.id} style={{ backgroundColor: c.color }}>
|
||||
<TableRow key={c.id} style={{ backgroundColor: c.color.hexcode }}>
|
||||
<TableCell className="py-1">{c.code}</TableCell>
|
||||
<TableCell className="py-1">{c.name}</TableCell>
|
||||
<TableCell className="py-1">{c.description}</TableCell>
|
||||
|
||||
@ -33,9 +33,9 @@ import { TRPCClientError } from "@trpc/client";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { z } from "zod";
|
||||
|
||||
import { zLabelSchema } from "@lifetracker/shared/types/categories";
|
||||
import { zCategorySchema } from "@lifetracker/shared/types/categories";
|
||||
|
||||
type CreateLabelSchema = z.infer<typeof zCategorySchema>;
|
||||
type CreateCategorySchema = z.infer<typeof zCategorySchema>;
|
||||
|
||||
export default function EditCategoryDialog({
|
||||
children,
|
||||
|
||||
@ -19,26 +19,26 @@ import AddColor from "./AddColor";
|
||||
// import EditCategoryDialog from "./EditCategoryDialog";
|
||||
import Link from "next/link";
|
||||
|
||||
export default function ColorssView() {
|
||||
export default function ColorsView() {
|
||||
const { data: session } = useSession();
|
||||
const { data: colors } = api.colors.list.useQuery();
|
||||
|
||||
// const invalidateColorList = api.useUtils().colors.list.invalidate;
|
||||
// const { mutate: deleteColor, isPending: isDeletionPending } =
|
||||
// api.colors.delete.useMutation({
|
||||
// onSuccess: () => {
|
||||
// toast({
|
||||
// description: "Color deleted",
|
||||
// });
|
||||
// invalidateCategoryList();
|
||||
// },
|
||||
// onError: (e) => {
|
||||
// toast({
|
||||
// variant: "destructive",
|
||||
// description: `Something went wrong: ${e.message}`,
|
||||
// });
|
||||
// },
|
||||
// });
|
||||
const invalidateColorList = api.useUtils().colors.list.invalidate;
|
||||
const { mutate: deleteColor, isPending: isDeletionPending } =
|
||||
api.colors.delete.useMutation({
|
||||
onSuccess: () => {
|
||||
toast({
|
||||
description: "Color deleted",
|
||||
});
|
||||
invalidateColorList();
|
||||
},
|
||||
onError: (e) => {
|
||||
toast({
|
||||
variant: "destructive",
|
||||
description: `Something went wrong: ${e.message}`,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const ColorsTable = ({ colors }) => (
|
||||
<Table>
|
||||
@ -52,13 +52,13 @@ export default function ColorssView() {
|
||||
<TableRow key={c.id} style={{ backgroundColor: c.hexcode, color: c.inverse }}>
|
||||
<TableCell className="py-1">{c.name}</TableCell>
|
||||
<TableCell className="py-1">
|
||||
numEntries
|
||||
{c.numCategories}
|
||||
</TableCell>
|
||||
<TableCell className="flex gap-1 py-1">
|
||||
<ActionButtonWithTooltip
|
||||
tooltip="Delete category"
|
||||
variant="outline"
|
||||
onClick={() => deleteColor({ colorId: c.id })}
|
||||
onClick={() => deleteColor({ colorName: c.name })}
|
||||
loading={false}
|
||||
>
|
||||
<Trash size={16} color="red" />
|
||||
|
||||
@ -28,7 +28,7 @@ CREATE TABLE `category` (
|
||||
`id` text PRIMARY KEY NOT NULL,
|
||||
`createdAt` integer NOT NULL,
|
||||
`name` text NOT NULL,
|
||||
`code` integer NOT NULL,
|
||||
`code` real NOT NULL,
|
||||
`description` text,
|
||||
`colorId` text NOT NULL,
|
||||
`parentId` text,
|
||||
@ -86,7 +86,7 @@ CREATE TABLE `verificationToken` (
|
||||
--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX `apiKey_keyId_unique` ON `apiKey` (`keyId`);--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX `apiKey_name_userId_unique` ON `apiKey` (`name`,`userId`);--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX `category_userId_name_unique` ON `category` (`userId`,`name`);--> 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 `day_date_unique` ON `day` (`date`);--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX `user_email_unique` ON `user` (`email`);
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"version": "6",
|
||||
"dialect": "sqlite",
|
||||
"id": "d35127c1-6892-410e-b933-d7ec7aabe6f5",
|
||||
"id": "7f1d6157-2405-4b96-be6a-a4bfb35f9aaf",
|
||||
"prevId": "00000000-0000-0000-0000-000000000000",
|
||||
"tables": {
|
||||
"account": {
|
||||
@ -219,7 +219,7 @@
|
||||
},
|
||||
"code": {
|
||||
"name": "code",
|
||||
"type": "integer",
|
||||
"type": "real",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
@ -254,11 +254,11 @@
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"category_userId_name_unique": {
|
||||
"name": "category_userId_name_unique",
|
||||
"category_userId_code_unique": {
|
||||
"name": "category_userId_code_unique",
|
||||
"columns": [
|
||||
"userId",
|
||||
"name"
|
||||
"code"
|
||||
],
|
||||
"isUnique": true
|
||||
}
|
||||
|
||||
@ -5,8 +5,8 @@
|
||||
{
|
||||
"idx": 0,
|
||||
"version": "6",
|
||||
"when": 1732260188078,
|
||||
"tag": "0000_gigantic_doctor_strange",
|
||||
"when": 1732441653703,
|
||||
"tag": "0000_awesome_stepford_cuckoos",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
|
||||
@ -1,27 +0,0 @@
|
||||
import { db } from "./drizzle";
|
||||
import { apiKeys, users } from "./schema";
|
||||
import { generateApiKey } from "../trpc/auth";
|
||||
import { and, count, eq } from "drizzle-orm";
|
||||
|
||||
db.transaction(async (trx) => {
|
||||
const ryan = await trx
|
||||
.insert(users)
|
||||
.values({
|
||||
name: "Ryan Pandya",
|
||||
email: "ryan@ryanpandya.com",
|
||||
password: "$2a$10$ngv9752uxDT11hSPfdZmAe2D8VXLB9mcXkN7TRPI5GZQCuriIu1gC",
|
||||
role: "admin",
|
||||
})
|
||||
.returning({
|
||||
id: users.id,
|
||||
name: users.name,
|
||||
email: users.email,
|
||||
role: users.role,
|
||||
});
|
||||
|
||||
db.query.users.findFirst({
|
||||
where: eq(users.email, "ryan@ryanpandya.com"),
|
||||
}).then((user) => {
|
||||
generateApiKey("CLI App", ryan.id);
|
||||
});
|
||||
});
|
||||
@ -5,6 +5,7 @@ import {
|
||||
AnySQLiteColumn,
|
||||
index,
|
||||
integer,
|
||||
real,
|
||||
primaryKey,
|
||||
sqliteTable,
|
||||
text,
|
||||
@ -28,6 +29,10 @@ export function calcInverseColor(hexcode: string): string {
|
||||
return luminance > 186 ? "#000000" : "#ffffff";
|
||||
}
|
||||
|
||||
// export function calcCategoryParent(parentId: string | null){
|
||||
// return parentId ??
|
||||
// }
|
||||
|
||||
|
||||
|
||||
export const config = sqliteTable("config", {
|
||||
@ -155,7 +160,7 @@ export const categories = sqliteTable(
|
||||
.$defaultFn(() => createId()),
|
||||
createdAt: createdAtField(),
|
||||
name: text("name").notNull(),
|
||||
code: integer("code").notNull(),
|
||||
code: real("code").notNull(),
|
||||
description: text("description"),
|
||||
colorId: text("colorId")
|
||||
.notNull()
|
||||
@ -165,8 +170,8 @@ export const categories = sqliteTable(
|
||||
.notNull()
|
||||
.references(() => users.id, { onDelete: "cascade" }),
|
||||
},
|
||||
(lb) => ({
|
||||
uniq: unique().on(lb.userId, lb.name)
|
||||
(c) => ({
|
||||
uniq: unique().on(c.userId, c.code)
|
||||
}),
|
||||
);
|
||||
|
||||
|
||||
@ -1,14 +1,26 @@
|
||||
import { z } from "zod";
|
||||
import { zColorSchema } from "./colors";
|
||||
|
||||
export const zCategorySchema = z.object({
|
||||
id: z.string(),
|
||||
code: z.coerce.number(),
|
||||
name: z.string(),
|
||||
colorId: z.string(),
|
||||
description: z.string().optional(),
|
||||
color: zColorSchema,
|
||||
parentId: z.string().optional(),
|
||||
});
|
||||
export type ZCategories = z.infer<typeof zCategorySchema>;
|
||||
|
||||
export const zCreateCategorySchema = z.object({
|
||||
code: z.coerce.number(),
|
||||
name: z.string(),
|
||||
description: z.string().optional(),
|
||||
color: z.string(),
|
||||
parentId: z.string().optional(),
|
||||
});
|
||||
export type ZCreateCategories = z.infer<typeof zCreateCategorySchema>;
|
||||
|
||||
|
||||
|
||||
export const zGetCategoryResponseSchema = z.object({
|
||||
id: z.string(),
|
||||
code: z.number(),
|
||||
|
||||
@ -3,5 +3,7 @@ import { z } from "zod";
|
||||
export const zColorSchema = z.object({
|
||||
name: z.string(),
|
||||
hexcode: z.string(),
|
||||
inverse: z.string().optional(),
|
||||
id: z.string().optional(),
|
||||
});
|
||||
export type ZColor = z.infer<typeof zColorSchema>;
|
||||
|
||||
@ -3,18 +3,22 @@ import { and, desc, eq, inArray, notExists } from "drizzle-orm";
|
||||
import { z } from "zod";
|
||||
|
||||
import { SqliteError } from "@lifetracker/db";
|
||||
import { categories } from "@lifetracker/db/schema";
|
||||
import { categories, colors } from "@lifetracker/db/schema";
|
||||
import {
|
||||
ZCategories,
|
||||
zCategorySchema,
|
||||
ZCreateCategories,
|
||||
zCreateCategorySchema,
|
||||
zGetCategoryResponseSchema,
|
||||
zUpdateCategoryRequestSchema
|
||||
} from "@lifetracker/shared/types/categories";
|
||||
import type { Context } from "../index";
|
||||
import { authedProcedure, router } from "../index";
|
||||
import { zColorSchema } from "@lifetracker/shared/types/colors";
|
||||
|
||||
|
||||
function conditionFromInput(input: { categoryId: string }, userId: string) {
|
||||
return and(eq(categories.id, input.categoryId), eq(categories.userId, userId));
|
||||
function conditionFromInput(input: { categoryCode: string }, userId: string) {
|
||||
return and(eq(categories.code, input.categoryCode), eq(categories.userId, userId));
|
||||
}
|
||||
|
||||
async function createCategory(
|
||||
@ -22,10 +26,8 @@ async function createCategory(
|
||||
ctx: Context,
|
||||
) {
|
||||
|
||||
console.log("Creating a category");
|
||||
|
||||
return ctx.db.transaction(async (trx) => {
|
||||
|
||||
console.log("Creating a category", input);
|
||||
try {
|
||||
const result = await trx
|
||||
.insert(categories)
|
||||
@ -33,23 +35,31 @@ async function createCategory(
|
||||
name: input.name,
|
||||
code: input.code,
|
||||
description: input.description,
|
||||
color: input.color,
|
||||
colorId: input.color.id,
|
||||
userId: ctx.user!.id,
|
||||
parentId: input.parentId ?? undefined
|
||||
})
|
||||
.returning({
|
||||
id: categories.id,
|
||||
name: categories.name,
|
||||
code: categories.code,
|
||||
description: categories.description,
|
||||
color: categories.color,
|
||||
colorId: categories.colorId
|
||||
});
|
||||
return result[0];
|
||||
const { colorId, ...newCategory } = result[0];
|
||||
|
||||
return {
|
||||
color: input.color,
|
||||
...newCategory
|
||||
};
|
||||
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
if (e instanceof SqliteError) {
|
||||
if (e.code == "SQLITE_CONSTRAINT_UNIQUE") {
|
||||
throw new TRPCError({
|
||||
code: "BAD_REQUEST",
|
||||
message: "Line 48 trpc routers categories.ts",
|
||||
message: "There's already a category with this code",
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -93,7 +103,10 @@ export const categoriesAppRouter = router({
|
||||
categories: z.array(
|
||||
z.object({
|
||||
id: z.string(),
|
||||
code: z.number(),
|
||||
name: z.string(),
|
||||
description: z.string().optional(),
|
||||
color: zColorSchema,
|
||||
}),
|
||||
),
|
||||
}),
|
||||
@ -102,34 +115,43 @@ export const categoriesAppRouter = router({
|
||||
const dbCategories = await ctx.db
|
||||
.select({
|
||||
id: categories.id,
|
||||
code: categories.code,
|
||||
name: categories.name,
|
||||
description: categories.description,
|
||||
color: {
|
||||
name: colors.name,
|
||||
hexcode: colors.hexcode,
|
||||
inverse: colors.inverse
|
||||
}
|
||||
})
|
||||
.from(categories);
|
||||
|
||||
// console.log("Listing cats");
|
||||
// console.log(dbCategories);
|
||||
// console.log(dbCategories.map(({ ...category }) => ({
|
||||
// ...category
|
||||
// })));
|
||||
.from(categories)
|
||||
.leftJoin(colors, eq(categories.colorId, colors.id))
|
||||
;
|
||||
|
||||
return {
|
||||
categories: dbCategories.map(({ ...category }) => ({
|
||||
...category
|
||||
categories: dbCategories.map(({ color, ...category }) => ({
|
||||
...category,
|
||||
color,
|
||||
})),
|
||||
};
|
||||
}),
|
||||
get: authedProcedure
|
||||
.input(
|
||||
z.object({
|
||||
categoryId: z.string(),
|
||||
categoryCode: z.number(),
|
||||
}),
|
||||
)
|
||||
.output(zGetCategoryResponseSchema)
|
||||
.output(zCategorySchema)
|
||||
.query(async ({ input, ctx }) => {
|
||||
const res = await ctx.db
|
||||
.select({
|
||||
id: categories.id,
|
||||
name: categories.name
|
||||
code: categories.code,
|
||||
name: categories.name,
|
||||
description: categories.description,
|
||||
colorId: categories.colorId,
|
||||
parentId: categories.parentId,
|
||||
userId: categories.userId,
|
||||
})
|
||||
.from(categories)
|
||||
.where(
|
||||
@ -155,11 +177,28 @@ export const categoriesAppRouter = router({
|
||||
{ ai: 0, human: 0 },
|
||||
);
|
||||
|
||||
return {
|
||||
id: res[0].id,
|
||||
name: res[0].name,
|
||||
numEntries: 420
|
||||
const [color] = await ctx.db.select().from(colors).where(
|
||||
and(
|
||||
and(eq(colors.id, res[0].colorId), eq(colors.userId, res[0].userId)),
|
||||
eq(res[0].userId, ctx.user.id),
|
||||
)
|
||||
);
|
||||
|
||||
const categoryId: string = res[0].id;
|
||||
|
||||
const { id, ...categoryProps } = res[0];
|
||||
const category: ZCategories = {
|
||||
color: color,
|
||||
...categoryProps
|
||||
};
|
||||
|
||||
if (category.parentId == null) {
|
||||
category.parentId = categoryId;
|
||||
}
|
||||
|
||||
console.log(category);
|
||||
|
||||
return category;
|
||||
}),
|
||||
create: authedProcedure
|
||||
.input(zCategorySchema)
|
||||
@ -168,12 +207,11 @@ export const categoriesAppRouter = router({
|
||||
id: z.string(),
|
||||
code: z.number(),
|
||||
name: z.string(),
|
||||
color: z.string().default("#000000"),
|
||||
color: zColorSchema,
|
||||
description: z.string().optional(),
|
||||
}),
|
||||
)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
console.log("Just started creating a category");
|
||||
return createCategory(input, ctx);
|
||||
}),
|
||||
update: authedProcedure
|
||||
@ -240,7 +278,7 @@ export const categoriesAppRouter = router({
|
||||
delete: authedProcedure
|
||||
.input(
|
||||
z.object({
|
||||
categoryId: z.string(),
|
||||
categoryCode: z.number(),
|
||||
}),
|
||||
)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
|
||||
@ -12,7 +12,7 @@ import { authedProcedure, router } from "../index";
|
||||
import { titleCase } from "title-case";
|
||||
|
||||
function conditionFromInput(input: { colorName: string }, userId: string) {
|
||||
return and(eq(titleCase(colors.name), titleCase(input.colorName)), eq(colors.userId, userId));
|
||||
return and(eq(colors.name, titleCase(input.colorName)), eq(colors.userId, userId));
|
||||
}
|
||||
|
||||
async function createColor(
|
||||
@ -62,7 +62,8 @@ export const colorsAppRouter = router({
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
hexcode: z.string(),
|
||||
userId: z.string(),
|
||||
inverse: z.string(),
|
||||
numCategories: z.number(),
|
||||
}),
|
||||
),
|
||||
}))
|
||||
@ -72,13 +73,14 @@ export const colorsAppRouter = router({
|
||||
id: colors.id,
|
||||
name: colors.name,
|
||||
hexcode: colors.hexcode,
|
||||
userId: colors.userId,
|
||||
inverse: colors.inverse,
|
||||
})
|
||||
.from(colors);
|
||||
|
||||
return {
|
||||
colors: dbColors.map(({ ...color }) => ({
|
||||
...color,
|
||||
numCategories: 0
|
||||
})),
|
||||
};
|
||||
}),
|
||||
@ -108,4 +110,32 @@ export const colorsAppRouter = router({
|
||||
throw new TRPCError({ code: "NOT_FOUND" });
|
||||
}
|
||||
}),
|
||||
get: authedProcedure
|
||||
.input(
|
||||
z.object({
|
||||
colorName: z.string(),
|
||||
}),
|
||||
)
|
||||
.output(
|
||||
z.object({
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
hexcode: z.string(),
|
||||
}))
|
||||
.query(async ({ input, ctx }) => {
|
||||
const res = await ctx.db
|
||||
.select({
|
||||
id: colors.id,
|
||||
name: colors.name,
|
||||
hexcode: colors.hexcode,
|
||||
})
|
||||
.from(colors)
|
||||
.where(conditionFromInput(input, ctx.user.id));
|
||||
|
||||
if (res.length == 0) {
|
||||
throw new TRPCError({ code: "NOT_FOUND" });
|
||||
}
|
||||
|
||||
return res[0];
|
||||
}),
|
||||
});
|
||||
|
||||
79
scripts/create_categories.sh
Normal file
79
scripts/create_categories.sh
Normal file
@ -0,0 +1,79 @@
|
||||
pnpm --filter=@lifetracker/cli run run categories create 0 "Sleep" "Time spent sleeping." black
|
||||
pnpm --filter=@lifetracker/cli run run categories create --parent 0 0.4 "Travel sleep" "Typically shitty half-sleep on a plane or in a car."
|
||||
pnpm --filter=@lifetracker/cli run run categories create --parent 0 0.5 "Nap" "Sleep other than at night."
|
||||
pnpm --filter=@lifetracker/cli run run categories create --parent 0 0.9 "Sick" "Resting while sick or injured."
|
||||
pnpm --filter=@lifetracker/cli run run categories create 1 "Family" "Time spent with family." crimson
|
||||
pnpm --filter=@lifetracker/cli run run categories create --parent 1 13 "Wedding, etc." "Wedding and related stuff."
|
||||
pnpm --filter=@lifetracker/cli run run categories create --parent 1 15 "Neighborhood / Community" "Local family, if you will."
|
||||
pnpm --filter=@lifetracker/cli run run categories create --parent 1 16 "Watching something" "TV, movies, or the like, watched with family."
|
||||
pnpm --filter=@lifetracker/cli run run categories create 2 "Friends" "Time spent with friends." teal
|
||||
pnpm --filter=@lifetracker/cli run run categories create --parent 2 22 "Party" "Party with friends."
|
||||
pnpm --filter=@lifetracker/cli run run categories create --parent 2 26 "Active / Sports" "Playing active games or sports with friends."
|
||||
pnpm --filter=@lifetracker/cli run run categories create --parent 2 28 "Meal" "Preparing or eating a meal with friends."
|
||||
pnpm --filter=@lifetracker/cli run run categories create --parent 2 29 "Drugs" "Drugs with friends."
|
||||
pnpm --filter=@lifetracker/cli run run categories create 3 "Jen" "Time spent on dates or primarily with/for Jen." pink
|
||||
pnpm --filter=@lifetracker/cli run run categories create --parent 3 31 "Jen's family" "Time spent with Jen's family."
|
||||
pnpm --filter=@lifetracker/cli run run categories create --parent 3 32 "Jen's friends" "Time with primarily Jen's friends."
|
||||
pnpm --filter=@lifetracker/cli run run categories create --parent 3 33 "Sex" "Sex or sex-related activities."
|
||||
pnpm --filter=@lifetracker/cli run run categories create --parent 3 34 "Exploring" "Exploring and adventuring, with a more urban (non-nature) focus."
|
||||
pnpm --filter=@lifetracker/cli run run categories create --parent 3 35 "Errands & Logistics" "When shit needs to get done, for me and Jen."
|
||||
pnpm --filter=@lifetracker/cli run run categories create --parent 3 36 "Watching something" "Watching a movie, TV show, or Internet stuff with Jen."
|
||||
pnpm --filter=@lifetracker/cli run run categories create --parent 3 37 "Hiking / Adventuring" "Hiking, camping, and backcountry adventures with an active focus."
|
||||
pnpm --filter=@lifetracker/cli run run categories create --parent 3 38 "Meal with Jen" "Cooking, eating out, or just having a meal with my love."
|
||||
pnpm --filter=@lifetracker/cli run run categories create --parent 3 39 "Relaxing / Napping" "Rest and cuddles, other than primarily for sleep or sex."
|
||||
pnpm --filter=@lifetracker/cli run run categories create 4 "Flying" "Anything related to private flying." blue
|
||||
pnpm --filter=@lifetracker/cli run run categories create --parent 4 41 "Airports, etc." "Dealing with FBOs, getting in or out of the plane, etc."
|
||||
pnpm --filter=@lifetracker/cli run run categories create --parent 4 42 "Flying with friends" "Tour or flight for fun with friends."
|
||||
pnpm --filter=@lifetracker/cli run run categories create --parent 4 43 "Flying with Jen" "A flying trip primarily for me and Jen."
|
||||
pnpm --filter=@lifetracker/cli run run categories create --parent 4 44 "Flight planning" "Checking weather, planning, filing flight plan, etc."
|
||||
pnpm --filter=@lifetracker/cli run run categories create --parent 4 45 "Flying for work" "A primarily business-oriented flying trip."
|
||||
pnpm --filter=@lifetracker/cli run run categories create --parent 4 46 "Maintenance" "A primarily maintenance-related flying trip."
|
||||
pnpm --filter=@lifetracker/cli run run categories create --parent 4 47 "Training" "A primarily training-related flying trip."
|
||||
pnpm --filter=@lifetracker/cli run run categories create --parent 4 49 "Other Flying Related" "e.g., Phone calls, research, buying/selling"
|
||||
pnpm --filter=@lifetracker/cli run run categories create 5 "Work" "Time spent working for money." green
|
||||
pnpm --filter=@lifetracker/cli run run categories create --parent 5 51 "Investors" "Pitching, diligence, calls, or somehow convincing investors to pony up."
|
||||
pnpm --filter=@lifetracker/cli run run categories create --parent 5 52 "Cofounders" "Meeting with one or more of Perumal, Bonney, TM."
|
||||
pnpm --filter=@lifetracker/cli run run categories create --parent 5 53 "[REMAP ME] Wedding prep" "This was set up because I was working so hard on the wedding in 2023 I thought I was going to die, but it really doesn't have much use being in the 5s."
|
||||
pnpm --filter=@lifetracker/cli run run categories create --parent 5 55 "Board" "Dealing with Board members or Board matters."
|
||||
pnpm --filter=@lifetracker/cli run run categories create --parent 5 56 "Emails" "Getting the inbox under control."
|
||||
pnpm --filter=@lifetracker/cli run run categories create --parent 5 57 "Work meditation" "Thinking, ruminating, or journaling about work."
|
||||
pnpm --filter=@lifetracker/cli run run categories create --parent 5 58 "Socializing" "Out building work-related relationships."
|
||||
pnpm --filter=@lifetracker/cli run run categories create 6 "Productive" "Time spent active (working out), fulfilling obligations, running errands, doing work without pay, or otherwise in a useful and productive way." orange
|
||||
pnpm --filter=@lifetracker/cli run run categories create --parent 6 61 "Cleaning" "Tidying, organizing, or cleaning a space."
|
||||
pnpm --filter=@lifetracker/cli run run categories create --parent 6 62 "Relationship work" "Having important conversations or working on the big stuff."
|
||||
pnpm --filter=@lifetracker/cli run run categories create --parent 6 63 "Home maintenance" "Productivity related to the home or household."
|
||||
pnpm --filter=@lifetracker/cli run run categories create --parent 6 65 "Personal finances / Life admin" "Chores related to life administration and personal finances."
|
||||
pnpm --filter=@lifetracker/cli run run categories create --parent 6 66 "Outdoor exercise" "Walking, running, biking, or hiking outdoors."
|
||||
pnpm --filter=@lifetracker/cli run run categories create --parent 6 67 "Gym" "Working on my fitness in a gym."
|
||||
pnpm --filter=@lifetracker/cli run run categories create --parent 6 68 "Meditation / Therapy" "Working on myself."
|
||||
pnpm --filter=@lifetracker/cli run run categories create --parent 6 69 "Bureaucracy / Bullshit" "Working on a needed but Kafkaesque nightmare chore."
|
||||
pnpm --filter=@lifetracker/cli run run categories create 7 "Hobbies & Skills" "Time used for personal development, improving skills, or what I consider to be productive hobbies." yellow
|
||||
pnpm --filter=@lifetracker/cli run run categories create --parent 7 71 "Language learning" "Learning, studying, reading, or practicing a foreign language."
|
||||
pnpm --filter=@lifetracker/cli run run categories create --parent 7 72 "Cooking" "Preparing food."
|
||||
pnpm --filter=@lifetracker/cli run run categories create --parent 7 73 "Home improvement" "Fun or useful stuff for the house, including gardening, automation, or other projecs that are basically hobbies."
|
||||
pnpm --filter=@lifetracker/cli run run categories create --parent 7 75 "Reading" "Reading an article, book, or something else edifying."
|
||||
pnpm --filter=@lifetracker/cli run run categories create --parent 7 76 "Organizing / Workflow" "Working on a hobby or skill, but doing the boring / annoying bits."
|
||||
pnpm --filter=@lifetracker/cli run run categories create --parent 7 78 "Programming / Computers" "Nerding out and building something with computers."
|
||||
pnpm --filter=@lifetracker/cli run run categories create --parent 7 79 "Shopping" "Shopping that isn't a waste of time."
|
||||
pnpm --filter=@lifetracker/cli run run categories create 8 "Relaxation & Leisure" "Time spent gaming, relaxing, consuming entertainment, or participating in passive activities." purple
|
||||
pnpm --filter=@lifetracker/cli run run categories create --parent 8 81 "Watching something" "Watching something produced for the screen (TV / Movies / Tiktok / YouTube)"
|
||||
pnpm --filter=@lifetracker/cli run run categories create --parent 8 82 "Hot tub / Sauna / Pool" "Sweating or swimming in a relaxing way, other than to get clean."
|
||||
pnpm --filter=@lifetracker/cli run run categories create --parent 8 83 "Masturbation" "Self sex."
|
||||
pnpm --filter=@lifetracker/cli run run categories create --parent 8 87 "Drugs" "Exploring the inner cosmos."
|
||||
pnpm --filter=@lifetracker/cli run run categories create --parent 8 88 "Video games" "High-octane relaxation."
|
||||
pnpm --filter=@lifetracker/cli run run categories create --parent 8 89 "Social media" "Scrolling."
|
||||
pnpm --filter=@lifetracker/cli run run categories create 9 "Waste" "Time better spent doing something else." red
|
||||
pnpm --filter=@lifetracker/cli run run categories create --parent 9 91 "Waiting / Killing time" "Literally staring at a clock with nothing else to do."
|
||||
pnpm --filter=@lifetracker/cli run run categories create --parent 9 93 "Fight" "Not understanding or communicating well."
|
||||
pnpm --filter=@lifetracker/cli run run categories create --parent 9 96 "Disaster" "Something has gone horribly wrong, and everything else is canceled."
|
||||
pnpm --filter=@lifetracker/cli run run categories create --parent 9 97 "Shopping" "Ah, mindless consumerism!"
|
||||
pnpm --filter=@lifetracker/cli run run categories create --parent 9 98 "Can't sleep" "Lying in bed wishing I was asleep."
|
||||
pnpm --filter=@lifetracker/cli run run categories create --parent 9 99 "Stress eating / drinking / smoking" "Shoveling empty calories or toxins, knowing it's a mistake."
|
||||
pnpm --filter=@lifetracker/cli run run categories create 10 "Health & Travel" "Time spent for personal hygiene, getting around, or similar." lime
|
||||
pnpm --filter=@lifetracker/cli run run categories create --parent 10 101 "Food" "A regular ol' meal."
|
||||
pnpm --filter=@lifetracker/cli run run categories create --parent 10 103 "Bath" "Taking a bath."
|
||||
pnpm --filter=@lifetracker/cli run run categories create --parent 10 104 "Travel" "Getting from a place to another place."
|
||||
pnpm --filter=@lifetracker/cli run run categories create --parent 10 105 "Bathroom" "Getting clean to go somewhere or do something -- or, flossing, brushing, and getting ready for bed.."
|
||||
pnpm --filter=@lifetracker/cli run run categories create --parent 10 106 "Packing & Cleaning" "Packing for a trip and cleaning before or after a trip."
|
||||
pnpm --filter=@lifetracker/cli run run categories create --parent 10 107 "Medical / Health" "A doctor, dentist, hospital visit, or appointment of some kind."
|
||||
pnpm --filter=@lifetracker/cli run run categories create --parent 10 108 "Grooming / Massage" "Taking care of myself in a more aesthetic or relaxing way."
|
||||
11
scripts/create_colors.sh
Normal file
11
scripts/create_colors.sh
Normal file
@ -0,0 +1,11 @@
|
||||
pnpm --filter=@lifetracker/cli run run colors create Black "#273036"
|
||||
pnpm --filter=@lifetracker/cli run run colors create Blue "#00A9B3"
|
||||
pnpm --filter=@lifetracker/cli run run colors create Lime "#BFFF55"
|
||||
pnpm --filter=@lifetracker/cli run run colors create Green "#189749"
|
||||
pnpm --filter=@lifetracker/cli run run colors create Pink "#FF65AE"
|
||||
pnpm --filter=@lifetracker/cli run run colors create Purple "#5B3AB1"
|
||||
pnpm --filter=@lifetracker/cli run run colors create Orange "#FF6D01"
|
||||
pnpm --filter=@lifetracker/cli run run colors create Yellow "#FFF336"
|
||||
pnpm --filter=@lifetracker/cli run run colors create Teal "#005744"
|
||||
pnpm --filter=@lifetracker/cli run run colors create Crimson "#C71634"
|
||||
pnpm --filter=@lifetracker/cli run run colors create Red "#FF2816"
|
||||
Loading…
Reference in New Issue
Block a user