chore: some refactor and semplification

This commit is contained in:
2025-01-19 11:25:31 +01:00
parent d473941773
commit 3bd243778c
8 changed files with 50 additions and 41 deletions

View File

@@ -0,0 +1,148 @@
import { CommandDefinition } from '@utils/types';
export class CommandParser {
private commands: Map<string, CommandDefinition>;
constructor() {
this.commands = new Map();
}
registerCommand(definition: CommandDefinition) {
this.commands.set(definition.name.toLowerCase(), definition);
}
parse(input: string): {
command: string;
id?: string;
flags: Record<string, string | number | boolean | Date>;
} {
const parts = input.match(/(?:[^\s"]+|"[^"]*")+/g);
if (!parts || parts.length === 0) {
throw new Error('Invalid command format');
}
const command = parts[0].toLowerCase();
const definition = this.commands.get(command);
if (!definition) {
throw new Error(`Unknown command: ${command}`);
}
let currentIndex = 1;
const flags: Record<string, string | number | boolean | Date> = {};
if (definition.hasId) {
if (parts.length < 2) {
throw new Error(`Command ${command} requires an ID`);
}
const id = parts[1];
currentIndex = 2;
flags.id = id;
}
while (currentIndex < parts.length) {
const flag = parts[currentIndex];
if (!flag.startsWith('-')) {
throw new Error(`Invalid flag format at: ${flag}`);
}
const flagName = flag.slice(1);
const flagDef = definition.flags.find(
f => f.name === flagName || f.alias === flagName
);
if (!flagDef) {
throw new Error(`Unknown flag: ${flagName}`);
}
currentIndex++;
if (currentIndex >= parts.length) {
throw new Error(`Missing value for flag: ${flagName}`);
}
const value = parts[currentIndex].replace(/^"(.*)"$/, '$1');
switch (flagDef.type) {
case 'number': {
const num = Number(value);
if (isNaN(num)) {
throw new Error(`Invalid number for flag ${flagName}: ${value}`);
}
flags[flagDef.name] = num;
break;
}
case 'date': {
const date = new Date(value);
if (isNaN(date.getTime())) {
throw new Error(`Invalid date for flag ${flagName}: ${value}`);
}
flags[flagDef.name] = date;
break;
}
case 'boolean': {
flags[flagDef.name] = value.toLowerCase() === 'true';
break;
}
default: {
flags[flagDef.name] = value;
}
}
currentIndex++;
}
for (const flagDef of definition.flags) {
if (flagDef.required && !(flagDef.name in flags)) {
throw new Error(`Missing required flag: ${flagDef.name}`);
}
}
return {
command,
...(flags.id ? { id: flags.id as string } : {}),
flags: Object.fromEntries(
Object.entries(flags).filter(([key]) => key !== 'id')
)
};
}
}
export const diaryCommands: CommandDefinition[] = [
{
name: 'add',
flags: [
{ name: 'desc', type: 'string', required: true },
{ name: 'cost', type: 'number', required: true },
{ name: 'cat', type: 'string', required: false }
]
},
{
name: 'update',
hasId: true,
flags: [
{ name: 'desc', type: 'string', required: false },
{ name: 'cost', type: 'number', required: false },
{ name: 'cat', type: 'string', required: false }
]
},
{
name: 'delete',
hasId: true,
flags: []
},
{
name: 'report',
flags: [
{ name: 'from', type: 'date', required: true },
{ name: 'to', type: 'date', required: true },
{ name: 'export', type: 'boolean', required: false }
]
},
{
name: 'daylog',
flags: [
{ name: 'text', type: 'string', required: true },
{ name: 'date', type: 'date', required: false }
]
}
];

View File

@@ -0,0 +1,67 @@
import { Prisma } from '@prisma/client';
import prisma from '@prisma/prisma';
import { ShortcutsResponse } from '@utils/types';
export async function processDayLog(
text: string,
date?: Date
): Promise<ShortcutsResponse> {
try {
const normalizedDate = new Date(date || new Date());
normalizedDate.setUTCHours(0, 0, 0, 0);
const newComment: Prisma.JsonObject = {
text,
timestamp: new Date().toISOString()
};
const existingLog = await prisma.dayLog.findUnique({
where: {
date: normalizedDate
}
});
if (existingLog) {
let existingComments: Prisma.JsonArray = [];
if (Array.isArray(existingLog.comments)) {
existingComments = existingLog.comments as Prisma.JsonArray;
}
const updatedLog = await prisma.dayLog.update({
where: {
id: existingLog.id
},
data: {
comments: [...existingComments, newComment]
}
});
return {
success: true,
message: `Added comment to existing log for ${normalizedDate.toLocaleDateString()}`,
data: updatedLog
};
} else {
const newLog = await prisma.dayLog.create({
data: {
date: normalizedDate,
comments: [newComment]
}
});
return {
success: true,
message: `Created new log for ${normalizedDate.toLocaleDateString()}`,
data: newLog
};
}
} catch (error) {
console.error('Error processing daylog:', error);
return {
success: false,
message:
error instanceof Error ? error.message : 'Failed to process daylog'
};
}
}

View File

@@ -0,0 +1,67 @@
import prisma from '@prisma/prisma';
import { ExpenseType } from '@utils/types';
const createOrGetCategory = async (name: string) => {
const category = await prisma.category.upsert({
where: { name },
update: {},
create: { name }
});
return category;
};
export const createExpense = async (data: ExpenseType) => {
const category = await createOrGetCategory(data.categoryName);
const newExpense = await prisma.expense.create({
data: {
description: data.description,
cost: data.cost,
categoryId: category.id,
deleted: false
},
include: {
category: true
}
});
return newExpense;
};
export const updateExpense = async (id: string, data: Partial<ExpenseType>) => {
let categoryId = undefined;
if (data.categoryName) {
const category = await createOrGetCategory(data.categoryName);
categoryId = category.id;
}
const updatedExpense = await prisma.expense.update({
where: {
id,
deleted: false
},
data: {
...(data.description && { description: data.description }),
...(data.cost && { cost: data.cost }),
...(categoryId && { categoryId })
},
include: {
category: true
}
});
return updatedExpense;
};
export const deleteExpense = async (id: string) => {
await prisma.expense.update({
where: {
id,
deleted: false
},
data: {
deleted: true
}
});
};