diff --git a/ai/analyze.ts b/ai/analyze.ts index 9ce3cde..7b97f19 100644 --- a/ai/analyze.ts +++ b/ai/analyze.ts @@ -1,5 +1,6 @@ "use server" +import { ActionState } from "@/lib/actions" import OpenAI from "openai" import { AnalyzeAttachment } from "./attachments" @@ -8,7 +9,7 @@ export async function analyzeTransaction( schema: Record, attachments: AnalyzeAttachment[], apiKey: string -): Promise<{ success: boolean; data?: Record; error?: string }> { +): Promise>> { const openai = new OpenAI({ apiKey, }) diff --git a/app/(app)/export/transactions/route.ts b/app/(app)/export/transactions/route.ts index c1ae5f6..a05f7d6 100644 --- a/app/(app)/export/transactions/route.ts +++ b/app/(app)/export/transactions/route.ts @@ -38,7 +38,7 @@ export async function GET(request: Request) { // CSV rows for (const transaction of transactions) { - const row: Record = {} + const row: Record = {} for (const field of existingFields) { let value if (field.isExtra) { diff --git a/app/(app)/files/actions.ts b/app/(app)/files/actions.ts index 2d1d83e..355e62e 100644 --- a/app/(app)/files/actions.ts +++ b/app/(app)/files/actions.ts @@ -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> { const user = await getCurrentUser() const files = formData.getAll("files") diff --git a/app/(app)/import/csv/actions.tsx b/app/(app)/import/csv/actions.tsx index df08c66..48d1493 100644 --- a/app/(app)/import/csv/actions.tsx +++ b/app/(app)/import/csv/actions.tsx @@ -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 | null, + formData: FormData +): Promise> { 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 | null, + formData: FormData +): Promise> { const user = await getCurrentUser() try { const rows = JSON.parse(formData.get("rows") as string) as Record[] diff --git a/app/(app)/settings/actions.ts b/app/(app)/settings/actions.ts index 8afa465..f65ebdf 100644 --- a/app/(app)/settings/actions.ts +++ b/app/(app)/settings/actions.ts @@ -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 | null, + formData: FormData +): Promise> { 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 | null, + formData: FormData +): Promise> { const user = await getCurrentUser() const validatedForm = userFormSchema.safeParse(Object.fromEntries(formData)) diff --git a/app/(app)/settings/backups/actions.ts b/app/(app)/settings/backups/actions.ts index a9b2f6f..597cba6 100644 --- a/app/(app)/settings/backups/actions.ts +++ b/app/(app)/settings/backups/actions.ts @@ -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 +} + +export async function restoreBackupAction( + _prevState: ActionState | null, + formData: FormData +): Promise> { 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 { diff --git a/app/(app)/settings/backups/data/route.ts b/app/(app)/settings/backups/data/route.ts index 4e7a971..958bea4 100644 --- a/app/(app)/settings/backups/data/route.ts +++ b/app/(app)/settings/backups/data/route.ts @@ -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 { - let filePaths: string[] = [] + const filePaths: string[] = [] async function readDirectoryRecursively(currentPath: string) { const isDirExists = await fileExists(currentPath) diff --git a/app/(app)/settings/backups/page.tsx b/app/(app)/settings/backups/page.tsx index 10e668e..63b73e3 100644 --- a/app/(app)/settings/backups/page.tsx +++ b/app/(app)/settings/backups/page.tsx @@ -62,7 +62,7 @@ export default function BackupSettingsPage() {

Backup restored successfully

You can now continue using the app. Import stats:

    - {Object.entries(restoreState.counters || {}).map(([key, value]) => ( + {Object.entries(restoreState.data?.counters || {}).map(([key, value]) => (
  • {key}: {value} items
  • diff --git a/app/(app)/settings/fields/page.tsx b/app/(app)/settings/fields/page.tsx index 69ff624..02d363f 100644 --- a/app/(app)/settings/fields/page.tsx +++ b/app/(app)/settings/fields/page.tsx @@ -17,8 +17,9 @@ export default async function FieldsSettingsPage() {

    Custom Fields

    - 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'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.

    | null, + formData: FormData +): Promise> { 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 | null, + formData: FormData +): Promise> { 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 | null, + transactionId: string +): Promise> { 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> { 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> { try { const transactionId = formData.get("transactionId") as string const files = formData.getAll("files") as File[] diff --git a/app/(app)/unsorted/actions.ts b/app/(app)/unsorted/actions.ts index 0d66f4a..e306c8b 100644 --- a/app/(app)/unsorted/actions.ts +++ b/app/(app)/unsorted/actions.ts @@ -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; error?: string }> { +): Promise>> { 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 | null, + formData: FormData +): Promise> { 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 | null, + fileId: string +): Promise> { try { const user = await getCurrentUser() await deleteFile(fileId, user.id) diff --git a/app/(auth)/actions.ts b/app/(auth)/actions.ts index c824a38..8e30ddd 100644 --- a/app/(auth)/actions.ts +++ b/app/(auth)/actions.ts @@ -15,12 +15,12 @@ export async function selfHostedGetStartedAction(formData: FormData) { const openaiApiKey = formData.get("openai_api_key") if (openaiApiKey) { - await updateSettings(user.id, "openai_api_key", openaiApiKey) + await updateSettings(user.id, "openai_api_key", openaiApiKey as string) } const defaultCurrency = formData.get("default_currency") if (defaultCurrency) { - await updateSettings(user.id, "default_currency", defaultCurrency) + await updateSettings(user.id, "default_currency", defaultCurrency as string) } revalidatePath("/dashboard") diff --git a/app/(auth)/enter/page.tsx b/app/(auth)/enter/page.tsx index 821b5c9..feed77e 100644 --- a/app/(auth)/enter/page.tsx +++ b/app/(auth)/enter/page.tsx @@ -2,6 +2,7 @@ import { LoginForm } from "@/components/auth/login-form" import { Card, CardContent, CardTitle } from "@/components/ui/card" import { ColoredText } from "@/components/ui/colored-text" import config from "@/lib/config" +import Image from "next/image" import { redirect } from "next/navigation" export default async function LoginPage() { @@ -11,7 +12,7 @@ export default async function LoginPage() { return ( - Logo + Logo TaxHacker: Cloud Edition diff --git a/app/(auth)/layout.tsx b/app/(auth)/layout.tsx index ce023a1..519b517 100644 --- a/app/(auth)/layout.tsx +++ b/app/(auth)/layout.tsx @@ -1,16 +1,17 @@ import { X } from "lucide-react" +import Link from "next/link" export default function AuthLayout({ children }: { children: React.ReactNode }) { return (
    - - +
    {children}
    ) diff --git a/app/(auth)/self-hosted/page.tsx b/app/(auth)/self-hosted/page.tsx index b564aa0..2b070d9 100644 --- a/app/(auth)/self-hosted/page.tsx +++ b/app/(auth)/self-hosted/page.tsx @@ -7,6 +7,7 @@ import config from "@/lib/config" import { DEFAULT_CURRENCIES, DEFAULT_SETTINGS } from "@/models/defaults" import { getSelfHostedUser } from "@/models/users" import { ShieldAlert } from "lucide-react" +import Image from "next/image" import { redirect } from "next/navigation" import { selfHostedGetStartedAction } from "../actions" @@ -36,12 +37,12 @@ export default async function SelfHostedWelcomePage() { return ( - Logo + Logo TaxHacker: Self-Hosted Edition -

    Welcome to your own instance of TaxHacker. Let's set up a couple of settings to get started.

    +

    Welcome to your own instance of TaxHacker. Let's set up a couple of settings to get started.

    diff --git a/app/(auth)/self-hosted/redirect/route.ts b/app/(auth)/self-hosted/redirect/route.ts index 671c281..b0ac30e 100644 --- a/app/(auth)/self-hosted/redirect/route.ts +++ b/app/(auth)/self-hosted/redirect/route.ts @@ -4,7 +4,7 @@ import { getSelfHostedUser } from "@/models/users" import { revalidatePath } from "next/cache" import { redirect } from "next/navigation" -export async function GET(request: Request) { +export async function GET() { if (!config.selfHosted.isEnabled) { redirect(config.auth.loginUrl) } diff --git a/app/landing/landing.tsx b/app/landing/landing.tsx index eb2eb67..63cbb2a 100644 --- a/app/landing/landing.tsx +++ b/app/landing/landing.tsx @@ -8,10 +8,10 @@ export default function LandingPage() {
    @@ -323,7 +323,7 @@ export default function LandingPage() { Upcoming Features

    - We're a small, indie project constantly improving. Here's what we're working on next. + We're a small, indie project constantly improving. Here's what we're working on next.

    @@ -429,9 +429,9 @@ export default function LandingPage() {
    diff --git a/components/dashboard/drop-zone-widget.tsx b/components/dashboard/drop-zone-widget.tsx index f67d815..b55ce1a 100644 --- a/components/dashboard/drop-zone-widget.tsx +++ b/components/dashboard/drop-zone-widget.tsx @@ -27,7 +27,7 @@ export default function DashboardDropZoneWidget() { // Submit the files using the server action startTransition(async () => { - const result = await uploadFilesAction(null, formData) + const result = await uploadFilesAction(formData) if (result.success) { showNotification({ code: "sidebar.unsorted", message: "new" }) setTimeout(() => showNotification({ code: "sidebar.unsorted", message: "" }), 3000) diff --git a/components/files/screen-drop-area.tsx b/components/files/screen-drop-area.tsx index 6fea0ff..6f76d18 100644 --- a/components/files/screen-drop-area.tsx +++ b/components/files/screen-drop-area.tsx @@ -65,7 +65,7 @@ export default function ScreenDropArea({ children }: { children: React.ReactNode startTransition(async () => { const result = transactionId ? await uploadTransactionFilesAction(formData) - : await uploadFilesAction(null, formData) + : await uploadFilesAction(formData) if (result.success) { showNotification({ code: "sidebar.unsorted", message: "new" }) @@ -88,18 +88,18 @@ export default function ScreenDropArea({ children }: { children: React.ReactNode // Add event listeners to document body useEffect(() => { - document.body.addEventListener("dragenter", handleDragEnter as any) - document.body.addEventListener("dragover", handleDragOver as any) - document.body.addEventListener("dragleave", handleDragLeave as any) - document.body.addEventListener("drop", handleDrop as any) + document.body.addEventListener("dragenter", handleDragEnter as unknown as EventListener) + document.body.addEventListener("dragover", handleDragOver as unknown as EventListener) + document.body.addEventListener("dragleave", handleDragLeave as unknown as EventListener) + document.body.addEventListener("drop", handleDrop as unknown as EventListener) return () => { - document.body.removeEventListener("dragenter", handleDragEnter as any) - document.body.removeEventListener("dragover", handleDragOver as any) - document.body.removeEventListener("dragleave", handleDragLeave as any) - document.body.removeEventListener("drop", handleDrop as any) + document.body.removeEventListener("dragenter", handleDragEnter as unknown as EventListener) + document.body.removeEventListener("dragover", handleDragOver as unknown as EventListener) + document.body.removeEventListener("dragleave", handleDragLeave as unknown as EventListener) + document.body.removeEventListener("drop", handleDrop as unknown as EventListener) } - }, [isDragging]) + }, [isDragging, handleDrop]) return (
    diff --git a/components/files/upload-button.tsx b/components/files/upload-button.tsx index e9d7a78..4a50ce3 100644 --- a/components/files/upload-button.tsx +++ b/components/files/upload-button.tsx @@ -28,7 +28,7 @@ export function UploadButton({ children, ...props }: { children: React.ReactNode // Submit the files using the server action startTransition(async () => { - const result = await uploadFilesAction(null, formData) + const result = await uploadFilesAction(formData) if (result.success) { showNotification({ code: "sidebar.unsorted", message: "new" }) setTimeout(() => showNotification({ code: "sidebar.unsorted", message: "" }), 3000) diff --git a/components/forms/convert-currency.tsx b/components/forms/convert-currency.tsx index 33c4f1d..c6cea27 100644 --- a/components/forms/convert-currency.tsx +++ b/components/forms/convert-currency.tsx @@ -16,16 +16,8 @@ export const FormConvertCurrency = ({ date?: Date | undefined onChange?: (value: number) => void }) => { - if ( - originalTotal === 0 || - !originalCurrencyCode || - !targetCurrencyCode || - originalCurrencyCode === targetCurrencyCode - ) { - return <> - } - const normalizedDate = startOfDay(date || new Date(Date.now() - 24 * 60 * 60 * 1000)) + const normalizedDateString = format(normalizedDate, "yyyy-MM-dd") const [exchangeRate, setExchangeRate] = useState(0) const [isLoading, setIsLoading] = useState(false) @@ -46,7 +38,11 @@ export const FormConvertCurrency = ({ } fetchData() - }, [originalCurrencyCode, targetCurrencyCode, format(normalizedDate, "LLLL-mm-dd")]) + }, [originalCurrencyCode, targetCurrencyCode, normalizedDateString, originalTotal]) + + if (!originalTotal || !originalCurrencyCode || !targetCurrencyCode || originalCurrencyCode === targetCurrencyCode) { + return <> + } return (
    diff --git a/components/forms/simple.tsx b/components/forms/simple.tsx index 6d72eee..6386985 100644 --- a/components/forms/simple.tsx +++ b/components/forms/simple.tsx @@ -117,7 +117,7 @@ export const FormDate = ({ if (!isNaN(newDate.getTime())) { setDate(newDate) } - } catch (error) {} + } catch (_) {} } return ( diff --git a/components/import/csv.tsx b/components/import/csv.tsx index 81bf8f5..586e50d 100644 --- a/components/import/csv.tsx +++ b/components/import/csv.tsx @@ -23,10 +23,11 @@ export function ImportCSVTable({ fields }: { fields: Field[] }) { useEffect(() => { if (parseState?.success && parseState.data) { - setCSVData(parseState.data) - if (parseState.data.length > 0) { + const parsedData = parseState.data as string[][] + setCSVData(parsedData) + if (parsedData.length > 0) { setColumnMappings( - parseState.data[0].map((value) => { + parsedData[0].map((value) => { const field = fields.find((field) => field.code === value || field.name === value) return field?.code || "" }) diff --git a/components/transactions/create.tsx b/components/transactions/create.tsx index d4d368c..0b5a8ce 100644 --- a/components/transactions/create.tsx +++ b/components/transactions/create.tsx @@ -44,8 +44,8 @@ export default function TransactionCreateForm({ }) useEffect(() => { - if (createState?.success) { - router.push(`/transactions/${createState.transactionId}`) + if (createState?.success && createState.data) { + router.push(`/transactions/${createState.data.id}`) } }, [createState, router]) diff --git a/forms/settings.ts b/forms/settings.ts index 418cb5b..8f1e515 100644 --- a/forms/settings.ts +++ b/forms/settings.ts @@ -8,7 +8,7 @@ export const settingsFormSchema = z.object({ default_project: z.string().optional(), openai_api_key: z.string().optional(), prompt_analyse_new_file: z.string().optional(), - is_welcome_message_hidden: z.boolean().optional(), + is_welcome_message_hidden: z.string().optional(), }) export const currencyFormSchema = z.object({ diff --git a/lib/actions.ts b/lib/actions.ts new file mode 100644 index 0000000..54b9759 --- /dev/null +++ b/lib/actions.ts @@ -0,0 +1,5 @@ +export type ActionState = { + success: boolean + error?: string | null + data?: T | null +} diff --git a/models/settings.ts b/models/settings.ts index 5615e32..cd62c9f 100644 --- a/models/settings.ts +++ b/models/settings.ts @@ -13,7 +13,7 @@ export const getSettings = cache(async (userId: string): Promise => }, {} as SettingsMap) }) -export const updateSettings = cache(async (userId: string, code: string, value: any) => { +export const updateSettings = cache(async (userId: string, code: string, value: string | undefined) => { return await prisma.setting.upsert({ where: { userId_code: { code, userId } }, update: { value }, diff --git a/package-lock.json b/package-lock.json index ea85d2b..763d41d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,7 +20,7 @@ "@radix-ui/react-popover": "^1.1.6", "@radix-ui/react-select": "^2.1.6", "@radix-ui/react-separator": "^1.1.2", - "@radix-ui/react-slot": "^1.1.2", + "@radix-ui/react-slot": "^1.2.0", "@radix-ui/react-tooltip": "^1.1.8", "@sentry/nextjs": "^9.11.0", "@types/sharp": "^0.31.1", @@ -2625,6 +2625,24 @@ } } }, + "node_modules/@radix-ui/react-collection/node_modules/@radix-ui/react-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.2.tgz", + "integrity": "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-compose-refs": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz", @@ -2691,6 +2709,24 @@ } } }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.2.tgz", + "integrity": "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-direction": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.0.tgz", @@ -2883,6 +2919,24 @@ } } }, + "node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.2.tgz", + "integrity": "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-popover": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.6.tgz", @@ -2920,6 +2974,24 @@ } } }, + "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.2.tgz", + "integrity": "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-popper": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.2.tgz", @@ -3023,6 +3095,24 @@ } } }, + "node_modules/@radix-ui/react-primitive/node_modules/@radix-ui/react-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.2.tgz", + "integrity": "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-roving-focus": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.2.tgz", @@ -3097,6 +3187,24 @@ } } }, + "node_modules/@radix-ui/react-select/node_modules/@radix-ui/react-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.2.tgz", + "integrity": "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-separator": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.1.2.tgz", @@ -3121,12 +3229,12 @@ } }, "node_modules/@radix-ui/react-slot": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.2.tgz", - "integrity": "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.0.tgz", + "integrity": "sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w==", "license": "MIT", "dependencies": { - "@radix-ui/react-compose-refs": "1.1.1" + "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", @@ -3138,6 +3246,21 @@ } } }, + "node_modules/@radix-ui/react-slot/node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", + "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-tooltip": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.1.8.tgz", @@ -3172,6 +3295,24 @@ } } }, + "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.2.tgz", + "integrity": "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-use-callback-ref": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz", diff --git a/package.json b/package.json index 17aca74..51f9f87 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "@radix-ui/react-popover": "^1.1.6", "@radix-ui/react-select": "^2.1.6", "@radix-ui/react-separator": "^1.1.2", - "@radix-ui/react-slot": "^1.1.2", + "@radix-ui/react-slot": "^1.2.0", "@radix-ui/react-tooltip": "^1.1.8", "@sentry/nextjs": "^9.11.0", "@types/sharp": "^0.31.1",