Checkpoint: Ajout de l'onglet "Signatures" dans la page Paramètres : table DB signatures (firstName, lastName, imageKey, imageUrl), migration appliquée, routes tRPC (list, upload, create, delete), composant SignaturesSection avec formulaire d'ajout (prénom+nom+upload image), grille d'affichage des signatures avec aperçu, bouton de suppression au survol
This commit is contained in:
@@ -56,6 +56,10 @@ import {
|
||||
createAutomationRule,
|
||||
updateAutomationRule,
|
||||
deleteAutomationRule,
|
||||
getSignaturesByUser,
|
||||
getSignatureById,
|
||||
createSignature,
|
||||
deleteSignature,
|
||||
} from "./db";
|
||||
import { loginLocal, hashPassword, getAzureAuthUrl, isAzureAdConfigured, generateToken } from "./auth";
|
||||
import { extractInvoicesWithMistral, generateMetadataJSON } from "./invoiceExtractor";
|
||||
@@ -1170,6 +1174,65 @@ export const appRouter = router({
|
||||
});
|
||||
}),
|
||||
}),
|
||||
|
||||
// ============= SIGNATURES ROUTES =============
|
||||
signatures: router({
|
||||
list: protectedProcedure.query(async ({ ctx }) => {
|
||||
return await getSignaturesByUser(ctx.user.id);
|
||||
}),
|
||||
|
||||
upload: protectedProcedure
|
||||
.input(z.object({
|
||||
firstName: z.string().min(1).max(100),
|
||||
lastName: z.string().min(1).max(100),
|
||||
fileName: z.string().min(1),
|
||||
fileData: z.string(), // Base64 encoded image
|
||||
mimeType: z.string().default("image/png"),
|
||||
}))
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
const userId = ctx.user.id;
|
||||
const fileBuffer = Buffer.from(input.fileData, "base64");
|
||||
const safeFileName = `${input.firstName}-${input.lastName}-${Date.now()}-${input.fileName}`
|
||||
.replace(/[^a-zA-Z0-9._-]/g, "_");
|
||||
const imageKey = generateStorageKey(userId, safeFileName);
|
||||
const result = await localStoragePut(imageKey, fileBuffer, input.mimeType);
|
||||
return await createSignature({
|
||||
userId,
|
||||
firstName: input.firstName,
|
||||
lastName: input.lastName,
|
||||
imageKey,
|
||||
imageUrl: result.url,
|
||||
});
|
||||
}),
|
||||
|
||||
create: protectedProcedure
|
||||
.input(z.object({
|
||||
firstName: z.string().min(1).max(100),
|
||||
lastName: z.string().min(1).max(100),
|
||||
imageKey: z.string().min(1),
|
||||
imageUrl: z.string().min(1),
|
||||
}))
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
return await createSignature({
|
||||
userId: ctx.user.id,
|
||||
firstName: input.firstName,
|
||||
lastName: input.lastName,
|
||||
imageKey: input.imageKey,
|
||||
imageUrl: input.imageUrl,
|
||||
});
|
||||
}),
|
||||
|
||||
delete: protectedProcedure
|
||||
.input(z.object({ id: z.number() }))
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
const sig = await getSignatureById(input.id);
|
||||
if (!sig || sig.userId !== ctx.user.id) {
|
||||
throw new TRPCError({ code: "NOT_FOUND" });
|
||||
}
|
||||
await deleteSignature(input.id);
|
||||
return { success: true };
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
export type AppRouter = typeof appRouter;
|
||||
|
||||
Reference in New Issue
Block a user