chore: some refactor and semplification
This commit is contained in:
148
utils/commands/helpers/commandParser.ts
Normal file
148
utils/commands/helpers/commandParser.ts
Normal 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 }
|
||||
]
|
||||
}
|
||||
];
|
||||
67
utils/commands/helpers/dayLog.ts
Normal file
67
utils/commands/helpers/dayLog.ts
Normal 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'
|
||||
};
|
||||
}
|
||||
}
|
||||
67
utils/commands/helpers/expense.ts
Normal file
67
utils/commands/helpers/expense.ts
Normal 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
|
||||
}
|
||||
});
|
||||
};
|
||||
Reference in New Issue
Block a user