Signed-off-by: Bryan Frimin <bryan@getprobo.com>
This commit is contained in:
Bryan Frimin
2026-03-18 23:59:32 +01:00
parent 239f201a93
commit 32cc4a4f9c
4 changed files with 35 additions and 69 deletions

View File

@@ -170,6 +170,7 @@ export const useCreateAudit = (connectionId: string) => {
validUntil?: string; validUntil?: string;
reportKey?: string; reportKey?: string;
state?: string; state?: string;
file?: File | null;
}) => { }) => {
if (!input.organizationId) { if (!input.organizationId) {
return alert(__("Failed to create audit: organization is required")); return alert(__("Failed to create audit: organization is required"));
@@ -188,9 +189,11 @@ export const useCreateAudit = (connectionId: string) => {
validUntil: input.validUntil, validUntil: input.validUntil,
reportKey: input.reportKey, reportKey: input.reportKey,
state: input.state || "NOT_STARTED", state: input.state || "NOT_STARTED",
file: input.file ? null : undefined,
}, },
connections: [connectionId], connections: [connectionId],
}, },
...(input.file ? { uploadables: { "input.file": input.file } } : {}),
}); });
}; };
}; };

View File

@@ -23,35 +23,15 @@ import {
} from "@probo/ui"; } from "@probo/ui";
import { Suspense } from "react"; import { Suspense } from "react";
import { type Control, Controller } from "react-hook-form"; import { type Control, Controller } from "react-hook-form";
import { useLazyLoadQuery, useMutation } from "react-relay"; import { useLazyLoadQuery } from "react-relay";
import { graphql } from "relay-runtime"; import { graphql } from "relay-runtime";
import { z } from "zod"; import { z } from "zod";
import type { CreateAuditDialogFrameworksQuery } from "#/__generated__/core/CreateAuditDialogFrameworksQuery.graphql"; import type { CreateAuditDialogFrameworksQuery } from "#/__generated__/core/CreateAuditDialogFrameworksQuery.graphql";
import type { CreateAuditDialogUploadReportMutation } from "#/__generated__/core/CreateAuditDialogUploadReportMutation.graphql";
import { ControlledField } from "#/components/form/ControlledField"; import { ControlledField } from "#/components/form/ControlledField";
import { useCreateAudit } from "#/hooks/graph/AuditGraph"; import { useCreateAudit } from "#/hooks/graph/AuditGraph";
import { useFormWithSchema } from "#/hooks/useFormWithSchema"; import { useFormWithSchema } from "#/hooks/useFormWithSchema";
const uploadAuditReportMutation = graphql`
mutation CreateAuditDialogUploadReportMutation(
$input: UploadAuditReportInput!
) {
uploadAuditReport(input: $input) {
audit {
id
report {
id
filename
downloadUrl
createdAt
}
updatedAt
}
}
}
`;
const frameworksQuery = graphql` const frameworksQuery = graphql`
query CreateAuditDialogFrameworksQuery($organizationId: ID!) { query CreateAuditDialogFrameworksQuery($organizationId: ID!) {
organization: node(id: $organizationId) { organization: node(id: $organizationId) {
@@ -115,68 +95,29 @@ export function CreateAuditDialog({
const internalRef = useDialogRef(); const internalRef = useDialogRef();
const ref = externalRef ?? internalRef; const ref = externalRef ?? internalRef;
const createAudit = useCreateAudit(connection); const createAudit = useCreateAudit(connection);
const [uploadMutate] = useMutation<CreateAuditDialogUploadReportMutation>(uploadAuditReportMutation);
const onSubmit = async (data: z.infer<typeof schema>) => { const onSubmit = async (data: z.infer<typeof schema>) => {
try { try {
const response = await createAudit({ await createAudit({
organizationId, organizationId,
frameworkId: data.frameworkId, frameworkId: data.frameworkId,
name: data.name || null, name: data.name || null,
validFrom: formatDatetime(data.validFrom), validFrom: formatDatetime(data.validFrom),
validUntil: formatDatetime(data.validUntil), validUntil: formatDatetime(data.validUntil),
state: data.state, state: data.state,
file: file ?? null,
}); });
const auditId = (response as { createAudit: { auditEdge: { node: { id: string } } } })
.createAudit.auditEdge.node.id;
if (file && auditId) {
try {
await new Promise<void>((resolve, reject) => {
uploadMutate({
variables: {
input: {
auditId,
file: null,
},
},
uploadables: {
"input.file": file,
},
onCompleted: (_response, errors) => {
if (errors) {
reject(errors);
} else {
resolve();
}
},
onError: error => reject(error),
});
});
toast({
title: __("Success"),
description: __("Audit created and report uploaded successfully"),
variant: "success",
});
} catch {
toast({
title: __("Warning"),
description: __("Audit created but report upload failed. You can upload the report from the audit detail page."),
variant: "warning",
});
}
} else {
toast({
title: __("Success"),
description: __("Audit created successfully"),
variant: "success",
});
}
ref.current?.close(); ref.current?.close();
reset(); reset();
onClose?.(); onClose?.();
toast({
title: __("Success"),
description: file
? __("Audit created and report uploaded successfully")
: __("Audit created successfully"),
variant: "success",
});
} catch (error) { } catch (error) {
toast({ toast({
title: __("Error"), title: __("Error"),

View File

@@ -4459,6 +4459,7 @@ input CreateAuditInput {
validUntil: Datetime validUntil: Datetime
state: AuditState state: AuditState
trustCenterVisibility: TrustCenterVisibility trustCenterVisibility: TrustCenterVisibility
file: Upload
} }
input UpdateAuditInput { input UpdateAuditInput {

View File

@@ -5338,6 +5338,27 @@ func (r *mutationResolver) CreateAudit(ctx context.Context, input types.CreateAu
return nil, gqlutils.Internal(ctx) return nil, gqlutils.Internal(ctx)
} }
if input.File != nil {
uploadReq := probo.UploadAuditReportRequest{
AuditID: audit.ID,
File: probo.File{
Content: input.File.File,
Filename: input.File.Filename,
Size: input.File.Size,
ContentType: input.File.ContentType,
},
}
audit, err = prb.Audits.UploadReport(ctx, uploadReq)
if err != nil {
if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok {
return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors)
}
r.logger.ErrorCtx(ctx, "cannot upload audit report", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
}
return &types.CreateAuditPayload{ return &types.CreateAuditPayload{
AuditEdge: types.NewAuditEdge(audit, coredata.AuditOrderFieldCreatedAt), AuditEdge: types.NewAuditEdge(audit, coredata.AuditOrderFieldCreatedAt),
}, nil }, nil