feat: ajouter le litige par un clic et isoler les sauvegardes du build
Some checks failed
Validation applicative / TypeScript, tests et build (push) Failing after 1m51s
Some checks failed
Validation applicative / TypeScript, tests et build (push) Failing after 1m51s
This commit is contained in:
@@ -9,3 +9,17 @@ client/dist
|
||||
README.md
|
||||
uploads
|
||||
storage
|
||||
# Les sauvegardes et secrets ne doivent jamais entrer dans une image de build.
|
||||
**/backups
|
||||
**/*backup*
|
||||
**/.ssh
|
||||
**/.manus-logs
|
||||
**/__pycache__
|
||||
**/*.pyc
|
||||
**/*.sql.gz
|
||||
**/*.bundle
|
||||
**/*.zip
|
||||
**/*.tar
|
||||
**/*.tar.gz
|
||||
**/*.pem
|
||||
**/*.key
|
||||
|
||||
@@ -66,6 +66,21 @@ export default function InvoiceDetail() {
|
||||
|
||||
const upsertLearningMutation = trpc.learnings.upsert.useMutation();
|
||||
|
||||
// Le premier clic ajoute le litige, puis ouvre la saisie du commentaire.
|
||||
const markDisputeMutation = trpc.invoices.setDispute.useMutation({
|
||||
onSuccess: async () => {
|
||||
utils.invoices.getById.setData({ id: invoiceId }, current => current ? { ...current, isDisputed: 1 } : current);
|
||||
setDisputeDialogOpen(true);
|
||||
toast.success("Facture ajoutée aux litiges");
|
||||
await Promise.all([
|
||||
utils.invoices.getById.invalidate({ id: invoiceId }),
|
||||
utils.invoices.list.invalidate(),
|
||||
utils.invoices.listDisputes.invalidate(),
|
||||
]);
|
||||
},
|
||||
onError: error => toast.error(error.message || "Impossible d’ajouter cette facture aux litiges"),
|
||||
});
|
||||
|
||||
const updateMutation = trpc.invoices.update.useMutation({
|
||||
onSuccess: () => {
|
||||
toast.success("Facture mise à jour avec succès");
|
||||
@@ -198,7 +213,10 @@ export default function InvoiceDetail() {
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<Button variant="outline" onClick={() => setDisputeDialogOpen(true)} className="border-amber-200 bg-amber-50 text-amber-800 hover:bg-amber-100">
|
||||
<Button variant="outline" disabled={markDisputeMutation.isPending} onClick={() => {
|
||||
if (canEdit && invoice.isDisputed !== 1) markDisputeMutation.mutate({ id: invoiceId, isDisputed: true });
|
||||
else setDisputeDialogOpen(true);
|
||||
}} className="border-amber-200 bg-amber-50 text-amber-800 hover:bg-amber-100">
|
||||
<Flag className="w-4 h-4 mr-2" aria-hidden="true" />
|
||||
Litige
|
||||
{invoice.isDisputed === 1 ? <Badge className="ml-2 bg-amber-100 text-amber-800 hover:bg-amber-100">En cours</Badge> : null}
|
||||
|
||||
18
server/disputeDeployment.test.ts
Normal file
18
server/disputeDeployment.test.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { readFileSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
import { describe, expect, it } from "vitest";
|
||||
describe("Déploiement et action directe Litige", () => {
|
||||
it("ajoute le litige dès le clic sur le bouton avant de proposer le commentaire", () => {
|
||||
const detail = readFileSync(join(process.cwd(), "client/src/pages/InvoiceDetail.tsx"), "utf8");
|
||||
expect(detail).toContain('markDisputeMutation.mutate({ id: invoiceId, isDisputed: true })');
|
||||
expect(detail).toContain('canEdit && invoice.isDisputed !== 1');
|
||||
});
|
||||
it("exclut les sauvegardes et les fichiers d’exploitation sans exclure les migrations SQL", () => {
|
||||
const patterns = readFileSync(join(process.cwd(), ".dockerignore"), "utf8").split("\n");
|
||||
expect(patterns).toContain('**/backups');
|
||||
expect(patterns).toContain('**/*.sql.gz');
|
||||
expect(patterns).toContain('**/*.bundle');
|
||||
expect(patterns).not.toContain('**/*.sql');
|
||||
expect(patterns).not.toContain('drizzle');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user