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
|
README.md
|
||||||
uploads
|
uploads
|
||||||
storage
|
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();
|
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({
|
const updateMutation = trpc.invoices.update.useMutation({
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
toast.success("Facture mise à jour avec succès");
|
toast.success("Facture mise à jour avec succès");
|
||||||
@@ -198,7 +213,10 @@ export default function InvoiceDetail() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-wrap gap-2">
|
<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" />
|
<Flag className="w-4 h-4 mr-2" aria-hidden="true" />
|
||||||
Litige
|
Litige
|
||||||
{invoice.isDisputed === 1 ? <Badge className="ml-2 bg-amber-100 text-amber-800 hover:bg-amber-100">En cours</Badge> : null}
|
{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