chore: organize ts types, fix eslint errors

This commit is contained in:
Vasily Zubarev
2025-04-09 12:45:56 +02:00
parent 707a030a0a
commit 416c45d08c
29 changed files with 277 additions and 84 deletions

View File

@@ -38,7 +38,7 @@ export async function GET(request: Request) {
// CSV rows
for (const transaction of transactions) {
const row: Record<string, any> = {}
const row: Record<string, unknown> = {}
for (const field of existingFields) {
let value
if (field.isExtra) {

View File

@@ -1,5 +1,6 @@
"use server"
import { ActionState } from "@/lib/actions"
import { getCurrentUser } from "@/lib/auth"
import { getUserUploadsDirectory, unsortedFilePath } from "@/lib/files"
import { createFile } from "@/models/files"
@@ -8,7 +9,7 @@ import { mkdir, writeFile } from "fs/promises"
import { revalidatePath } from "next/cache"
import path from "path"
export async function uploadFilesAction(prevState: any, formData: FormData) {
export async function uploadFilesAction(formData: FormData): Promise<ActionState<null>> {
const user = await getCurrentUser()
const files = formData.getAll("files")

View File

@@ -1,12 +1,17 @@
"use server"
import { ActionState } from "@/lib/actions"
import { getCurrentUser } from "@/lib/auth"
import { EXPORT_AND_IMPORT_FIELD_MAP } from "@/models/export_and_import"
import { createTransaction } from "@/models/transactions"
import { parse } from "@fast-csv/parse"
import { Transaction } from "@prisma/client"
import { revalidatePath } from "next/cache"
export async function parseCSVAction(prevState: any, formData: FormData) {
export async function parseCSVAction(
_prevState: ActionState<string[][]> | null,
formData: FormData
): Promise<ActionState<string[][]>> {
const file = formData.get("file") as File
if (!file) {
return { success: false, error: "No file uploaded" }
@@ -38,7 +43,10 @@ export async function parseCSVAction(prevState: any, formData: FormData) {
}
}
export async function saveTransactionsAction(prevState: any, formData: FormData) {
export async function saveTransactionsAction(
_prevState: ActionState<Transaction> | null,
formData: FormData
): Promise<ActionState<Transaction>> {
const user = await getCurrentUser()
try {
const rows = JSON.parse(formData.get("rows") as string) as Record<string, unknown>[]

View File

@@ -8,19 +8,23 @@ import {
settingsFormSchema,
} from "@/forms/settings"
import { userFormSchema } from "@/forms/users"
import { ActionState } from "@/lib/actions"
import { getCurrentUser } from "@/lib/auth"
import { codeFromName, randomHexColor } from "@/lib/utils"
import { createCategory, deleteCategory, updateCategory } from "@/models/categories"
import { createCurrency, deleteCurrency, updateCurrency } from "@/models/currencies"
import { createField, deleteField, updateField } from "@/models/fields"
import { createProject, deleteProject, updateProject } from "@/models/projects"
import { updateSettings } from "@/models/settings"
import { SettingsMap, updateSettings } from "@/models/settings"
import { updateUser } from "@/models/users"
import { Prisma } from "@prisma/client"
import { Prisma, User } from "@prisma/client"
import { revalidatePath } from "next/cache"
import { redirect } from "next/navigation"
export async function saveSettingsAction(prevState: any, formData: FormData) {
export async function saveSettingsAction(
_prevState: ActionState<SettingsMap> | null,
formData: FormData
): Promise<ActionState<SettingsMap>> {
const user = await getCurrentUser()
const validatedForm = settingsFormSchema.safeParse(Object.fromEntries(formData))
@@ -29,7 +33,10 @@ export async function saveSettingsAction(prevState: any, formData: FormData) {
}
for (const key in validatedForm.data) {
await updateSettings(user.id, key, validatedForm.data[key as keyof typeof validatedForm.data])
const value = validatedForm.data[key as keyof typeof validatedForm.data]
if (value !== undefined) {
await updateSettings(user.id, key, value)
}
}
revalidatePath("/settings")
@@ -37,7 +44,10 @@ export async function saveSettingsAction(prevState: any, formData: FormData) {
// return { success: true }
}
export async function saveProfileAction(prevState: any, formData: FormData) {
export async function saveProfileAction(
_prevState: ActionState<User> | null,
formData: FormData
): Promise<ActionState<User>> {
const user = await getCurrentUser()
const validatedForm = userFormSchema.safeParse(Object.fromEntries(formData))

View File

@@ -1,5 +1,6 @@
"use server"
import { ActionState } from "@/lib/actions"
import { getCurrentUser } from "@/lib/auth"
import { prisma } from "@/lib/db"
import { getUserUploadsDirectory } from "@/lib/files"
@@ -12,7 +13,14 @@ const SUPPORTED_BACKUP_VERSIONS = ["1.0"]
const REMOVE_EXISTING_DATA = true
const MAX_BACKUP_SIZE = 256 * 1024 * 1024 // 256MB
export async function restoreBackupAction(prevState: any, formData: FormData) {
type BackupRestoreResult = {
counters: Record<string, number>
}
export async function restoreBackupAction(
_prevState: ActionState<BackupRestoreResult> | null,
formData: FormData
): Promise<ActionState<BackupRestoreResult>> {
const user = await getCurrentUser()
const userUploadsDirectory = await getUserUploadsDirectory(user)
const file = formData.get("file") as File
@@ -32,7 +40,7 @@ export async function restoreBackupAction(prevState: any, formData: FormData) {
const fileData = Buffer.from(fileBuffer)
zip = await JSZip.loadAsync(fileData)
} catch (error) {
return { success: false, error: "Bad zip archive" }
return { success: false, error: "Bad zip archive: " + (error as Error).message }
}
// Check metadata and start restoring
@@ -133,7 +141,7 @@ export async function restoreBackupAction(prevState: any, formData: FormData) {
}
}
return { success: true, message: "Restore completed successfully", counters }
return { success: true, data: { counters } }
} catch (error) {
console.error("Error restoring from backup:", error)
return {

View File

@@ -9,7 +9,7 @@ import path from "path"
const MAX_FILE_SIZE = 64 * 1024 * 1024 // 64MB
const BACKUP_VERSION = "1.0"
export async function GET(request: Request) {
export async function GET() {
const user = await getCurrentUser()
const userUploadsDirectory = await getUserUploadsDirectory(user)
@@ -87,7 +87,7 @@ export async function GET(request: Request) {
}
async function getAllFilePaths(dirPath: string): Promise<string[]> {
let filePaths: string[] = []
const filePaths: string[] = []
async function readDirectoryRecursively(currentPath: string) {
const isDirExists = await fileExists(currentPath)

View File

@@ -62,7 +62,7 @@ export default function BackupSettingsPage() {
<h2 className="text-xl font-semibold">Backup restored successfully</h2>
<p className="text-sm text-muted-foreground">You can now continue using the app. Import stats:</p>
<ul className="list-disc list-inside">
{Object.entries(restoreState.counters || {}).map(([key, value]) => (
{Object.entries(restoreState.data?.counters || {}).map(([key, value]) => (
<li key={key}>
<span className="font-bold">{key}</span>: {value} items
</li>

View File

@@ -17,8 +17,9 @@ export default async function FieldsSettingsPage() {
<div className="container">
<h1 className="text-2xl font-bold mb-2">Custom Fields</h1>
<p className="text-sm text-gray-500 mb-6 max-w-prose">
You can add new fields to your transactions. Standard fields can't be removed but you can tweak their prompts or
hide them. If you don't want a field to be analyzed by AI but filled in by hand, leave the "LLM prompt" empty.
You can add new fields to your transactions. Standard fields can&apos;t be removed but you can tweak their
prompts or hide them. If you don&apos;t want a field to be analyzed by AI but filled in by hand, leave the
&quot;LLM prompt&quot; empty.
</p>
<CrudTable
items={fieldsWithActions}

View File

@@ -1,6 +1,7 @@
"use server"
import { transactionFormSchema } from "@/forms/transactions"
import { ActionState } from "@/lib/actions"
import { getCurrentUser } from "@/lib/auth"
import { getTransactionFileUploadPath, getUserUploadsDirectory } from "@/lib/files"
import { updateField } from "@/models/fields"
@@ -13,12 +14,16 @@ import {
updateTransaction,
updateTransactionFiles,
} from "@/models/transactions"
import { Transaction } from "@prisma/client"
import { randomUUID } from "crypto"
import { mkdir, writeFile } from "fs/promises"
import { revalidatePath } from "next/cache"
import path from "path"
export async function createTransactionAction(prevState: any, formData: FormData) {
export async function createTransactionAction(
_prevState: ActionState<Transaction> | null,
formData: FormData
): Promise<ActionState<Transaction>> {
try {
const user = await getCurrentUser()
const validatedForm = transactionFormSchema.safeParse(Object.fromEntries(formData.entries()))
@@ -30,14 +35,17 @@ export async function createTransactionAction(prevState: any, formData: FormData
const transaction = await createTransaction(user.id, validatedForm.data)
revalidatePath("/transactions")
return { success: true, transactionId: transaction.id }
return { success: true, data: transaction }
} catch (error) {
console.error("Failed to create transaction:", error)
return { success: false, error: "Failed to create transaction" }
}
}
export async function saveTransactionAction(prevState: any, formData: FormData) {
export async function saveTransactionAction(
_prevState: ActionState<Transaction> | null,
formData: FormData
): Promise<ActionState<Transaction>> {
try {
const user = await getCurrentUser()
const transactionId = formData.get("transactionId") as string
@@ -50,14 +58,17 @@ export async function saveTransactionAction(prevState: any, formData: FormData)
const transaction = await updateTransaction(transactionId, user.id, validatedForm.data)
revalidatePath("/transactions")
return { success: true, transactionId: transaction.id }
return { success: true, data: transaction }
} catch (error) {
console.error("Failed to update transaction:", error)
return { success: false, error: "Failed to save transaction" }
}
}
export async function deleteTransactionAction(prevState: any, transactionId: string) {
export async function deleteTransactionAction(
_prevState: ActionState<Transaction> | null,
transactionId: string
): Promise<ActionState<Transaction>> {
try {
const user = await getCurrentUser()
const transaction = await getTransactionById(transactionId, user.id)
@@ -67,7 +78,7 @@ export async function deleteTransactionAction(prevState: any, transactionId: str
revalidatePath("/transactions")
return { success: true, transactionId: transaction.id }
return { success: true, data: transaction }
} catch (error) {
console.error("Failed to delete transaction:", error)
return { success: false, error: "Failed to delete transaction" }
@@ -77,7 +88,7 @@ export async function deleteTransactionAction(prevState: any, transactionId: str
export async function deleteTransactionFileAction(
transactionId: string,
fileId: string
): Promise<{ success: boolean; error?: string }> {
): Promise<ActionState<Transaction>> {
if (!fileId || !transactionId) {
return { success: false, error: "File ID and transaction ID are required" }
}
@@ -96,10 +107,10 @@ export async function deleteTransactionFileAction(
await deleteFile(fileId, user.id)
revalidatePath(`/transactions/${transactionId}`)
return { success: true }
return { success: true, data: transaction }
}
export async function uploadTransactionFilesAction(formData: FormData): Promise<{ success: boolean; error?: string }> {
export async function uploadTransactionFilesAction(formData: FormData): Promise<ActionState<Transaction>> {
try {
const transactionId = formData.get("transactionId") as string
const files = formData.getAll("files") as File[]

View File

@@ -5,13 +5,14 @@ import { AnalyzeAttachment, loadAttachmentsForAI } from "@/ai/attachments"
import { buildLLMPrompt } from "@/ai/prompt"
import { fieldsToJsonSchema } from "@/ai/schema"
import { transactionFormSchema } from "@/forms/transactions"
import { ActionState } from "@/lib/actions"
import { getCurrentUser } from "@/lib/auth"
import config from "@/lib/config"
import { getTransactionFileUploadPath, getUserUploadsDirectory } from "@/lib/files"
import { DEFAULT_PROMPT_ANALYSE_NEW_FILE } from "@/models/defaults"
import { deleteFile, getFileById, updateFile } from "@/models/files"
import { createTransaction, updateTransactionFiles } from "@/models/transactions"
import { Category, Field, File, Project } from "@prisma/client"
import { Category, Field, File, Project, Transaction } from "@prisma/client"
import { mkdir, rename } from "fs/promises"
import { revalidatePath } from "next/cache"
import path from "path"
@@ -22,7 +23,7 @@ export async function analyzeFileAction(
fields: Field[],
categories: Category[],
projects: Project[]
): Promise<{ success: boolean; data?: Record<string, any>; error?: string }> {
): Promise<ActionState<Record<string, string>>> {
const user = await getCurrentUser()
if (!file || file.userId !== user.id) {
@@ -58,7 +59,10 @@ export async function analyzeFileAction(
return results
}
export async function saveFileAsTransactionAction(prevState: any, formData: FormData) {
export async function saveFileAsTransactionAction(
_prevState: ActionState<Transaction> | null,
formData: FormData
): Promise<ActionState<Transaction>> {
try {
const user = await getCurrentUser()
const validatedForm = transactionFormSchema.safeParse(Object.fromEntries(formData.entries()))
@@ -97,14 +101,17 @@ export async function saveFileAsTransactionAction(prevState: any, formData: Form
revalidatePath("/unsorted")
revalidatePath("/transactions")
return { success: true, transactionId: transaction.id }
return { success: true, data: transaction }
} catch (error) {
console.error("Failed to save transaction:", error)
return { success: false, error: `Failed to save transaction: ${error}` }
}
}
export async function deleteUnsortedFileAction(prevState: any, fileId: string) {
export async function deleteUnsortedFileAction(
_prevState: ActionState<Transaction> | null,
fileId: string
): Promise<ActionState<Transaction>> {
try {
const user = await getCurrentUser()
await deleteFile(fileId, user.id)