Checkpoint: Boîte à idées : table BDD, API tRPC (créer, lister, répondre, changer statut), page avec liste filtrée par statut et recherche, bouton Nouvelle demande, réponse admin avec statut colorisé, menu dans la sidebar
This commit is contained in:
44
server/db.ts
44
server/db.ts
@@ -9,6 +9,8 @@ import {
|
||||
appSettings,
|
||||
importLogs,
|
||||
InsertLocalUser,
|
||||
ideas,
|
||||
InsertIdea,
|
||||
} from "../drizzle/schema";
|
||||
import { ENV } from "./_core/env";
|
||||
|
||||
@@ -315,3 +317,45 @@ export async function getImportStats() {
|
||||
totalNewRows,
|
||||
};
|
||||
}
|
||||
|
||||
// ─── Boîte à idées ────────────────────────────────────────────────────────────
|
||||
|
||||
export async function createIdea(data: InsertIdea) {
|
||||
const db = await getDb();
|
||||
if (!db) throw new Error("Database not available");
|
||||
await db.insert(ideas).values(data);
|
||||
}
|
||||
|
||||
export async function getAllIdeas() {
|
||||
const db = await getDb();
|
||||
if (!db) return [];
|
||||
return db.select().from(ideas).orderBy(desc(ideas.createdAt));
|
||||
}
|
||||
|
||||
export async function getIdeasByUser(userId: number) {
|
||||
const db = await getDb();
|
||||
if (!db) return [];
|
||||
return db.select().from(ideas).where(eq(ideas.userId, userId)).orderBy(desc(ideas.createdAt));
|
||||
}
|
||||
|
||||
export async function repondreIdea(
|
||||
id: number,
|
||||
reponseAdmin: string,
|
||||
reponduPar: string,
|
||||
statut: "ouvert" | "en_cours" | "resolu" | "ferme"
|
||||
) {
|
||||
const db = await getDb();
|
||||
if (!db) throw new Error("Database not available");
|
||||
await db.update(ideas).set({
|
||||
reponseAdmin,
|
||||
reponduPar,
|
||||
reponduAt: new Date(),
|
||||
statut,
|
||||
}).where(eq(ideas.id, id));
|
||||
}
|
||||
|
||||
export async function updateIdeaStatut(id: number, statut: "ouvert" | "en_cours" | "resolu" | "ferme") {
|
||||
const db = await getDb();
|
||||
if (!db) throw new Error("Database not available");
|
||||
await db.update(ideas).set({ statut }).where(eq(ideas.id, id));
|
||||
}
|
||||
|
||||
@@ -17,6 +17,11 @@ import {
|
||||
createLocalUser,
|
||||
updateLocalUser,
|
||||
deleteLocalUser,
|
||||
createIdea,
|
||||
getAllIdeas,
|
||||
getIdeasByUser,
|
||||
repondreIdea,
|
||||
updateIdeaStatut,
|
||||
} from "./db";
|
||||
import { importVeille, importAAP, runFullImport, getImportConfig } from "./importer";
|
||||
import { loginLocalUser, hashPassword, ensureAdminExists } from "./localAuth";
|
||||
@@ -238,6 +243,67 @@ export const appRouter = router({
|
||||
return { success: true };
|
||||
}),
|
||||
}),
|
||||
|
||||
// ─── Boîte à idées ───────────────────────────────────────────────────────────
|
||||
ideas: router({
|
||||
// Créer une nouvelle idée / question
|
||||
create: protectedProcedure
|
||||
.input(
|
||||
z.object({
|
||||
titre: z.string().min(3).max(512),
|
||||
message: z.string().min(10),
|
||||
})
|
||||
)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await createIdea({
|
||||
userId: ctx.user.id,
|
||||
userName: ctx.user.name ?? ctx.user.email ?? "Utilisateur",
|
||||
titre: input.titre,
|
||||
message: input.message,
|
||||
});
|
||||
return { success: true };
|
||||
}),
|
||||
|
||||
// Lister toutes les idées (admin) ou les siennes (user)
|
||||
list: protectedProcedure.query(async ({ ctx }) => {
|
||||
if (ctx.user.role === "admin") {
|
||||
return getAllIdeas();
|
||||
}
|
||||
return getIdeasByUser(ctx.user.id);
|
||||
}),
|
||||
|
||||
// Répondre à une idée (admin uniquement)
|
||||
repondre: adminProcedure
|
||||
.input(
|
||||
z.object({
|
||||
id: z.number().int().positive(),
|
||||
reponseAdmin: z.string().min(1),
|
||||
statut: z.enum(["ouvert", "en_cours", "resolu", "ferme"]),
|
||||
})
|
||||
)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await repondreIdea(
|
||||
input.id,
|
||||
input.reponseAdmin,
|
||||
ctx.user.name ?? ctx.user.email ?? "Admin",
|
||||
input.statut
|
||||
);
|
||||
return { success: true };
|
||||
}),
|
||||
|
||||
// Changer le statut (admin uniquement)
|
||||
updateStatut: adminProcedure
|
||||
.input(
|
||||
z.object({
|
||||
id: z.number().int().positive(),
|
||||
statut: z.enum(["ouvert", "en_cours", "resolu", "ferme"]),
|
||||
})
|
||||
)
|
||||
.mutation(async ({ input }) => {
|
||||
await updateIdeaStatut(input.id, input.statut);
|
||||
return { success: true };
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
export type AppRouter = typeof appRouter;
|
||||
|
||||
Reference in New Issue
Block a user