Add frontend button

Signed-off-by: Bryan Frimin <bryan@getprobo.com>
This commit is contained in:
Bryan Frimin
2025-07-03 11:58:37 +02:00
parent 8189fa2a14
commit ff7feedbfa
4 changed files with 163 additions and 4 deletions

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<29bf3465293095c44e826394c602cde1>>
* @generated SignedSource<<25880ab79f8b6ae18f3ead4431a90329>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -10,7 +10,7 @@
import { ConcreteRequest } from 'relay-runtime';
import { FragmentRefs } from "relay-runtime";
export type DocumentOrderField = "CREATED_AT" | "TITLE";
export type DocumentOrderField = "CREATED_AT" | "DOCUMENT_TYPE" | "TITLE";
export type OrderDirection = "ASC" | "DESC";
export type DocumentOrder = {
direction: OrderDirection;

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<b61f74ae791f86da55afd868c68c85e4>>
* @generated SignedSource<<9ea096db73885458f35ae718a00ded47>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -10,7 +10,7 @@
import { ConcreteRequest } from 'relay-runtime';
import { FragmentRefs } from "relay-runtime";
export type DocumentOrderField = "CREATED_AT" | "TITLE";
export type DocumentOrderField = "CREATED_AT" | "DOCUMENT_TYPE" | "TITLE";
export type OrderDirection = "ASC" | "DESC";
export type DocumentOrder = {
direction: OrderDirection;

View File

@@ -1,9 +1,11 @@
import {
Avatar,
Button,
Dialog,
DialogContent,
DialogFooter,
DialogTitle,
IconArrowInbox,
Markdown,
} from "@probo/ui";
import { graphql } from "relay-runtime";
@@ -11,9 +13,11 @@ import { useTranslate } from "@probo/i18n";
import { useState, type ReactNode } from "react";
import { useFragment } from "react-relay";
import type { DocumentVersionHistoryDialogFragment$key } from "./__generated__/DocumentVersionHistoryDialogFragment.graphql";
import type { DocumentVersionHistoryDialogExportPDFMutation } from "./__generated__/DocumentVersionHistoryDialogExportPDFMutation.graphql";
import clsx from "clsx";
import type { NodeOf } from "/types";
import type { DocumentDetailPageDocumentFragment$data } from "../__generated__/DocumentDetailPageDocumentFragment.graphql";
import { useMutationWithToasts } from "/hooks/useMutationWithToasts";
const historyFragment = graphql`
fragment DocumentVersionHistoryDialogFragment on DocumentVersion {
@@ -30,6 +34,16 @@ const historyFragment = graphql`
}
`;
const exportPDFMutation = graphql`
mutation DocumentVersionHistoryDialogExportPDFMutation(
$documentVersionId: ID!
) {
exportDocumentVersionPDF(input: { documentVersionId: $documentVersionId }) {
data
}
}
`;
type Props = {
document: DocumentDetailPageDocumentFragment$data;
children?: ReactNode;
@@ -41,6 +55,46 @@ export function DocumentVersionHistoryDialog(props: Props) {
const { __ } = useTranslate();
const versions = props.document.versions.edges.map((edge) => edge.node);
const [selectedVersion, setSelectedVersion] = useState<Version>(versions[0]);
const [exportPDF, isExporting] =
useMutationWithToasts<DocumentVersionHistoryDialogExportPDFMutation>(
exportPDFMutation,
{
errorMessage: __("Failed to export PDF"),
successMessage: __("PDF exported successfully"),
}
);
const handleDownloadPDF = () => {
exportPDF({
variables: { documentVersionId: selectedVersion.id },
onCompleted: (data) => {
if (data.exportDocumentVersionPDF.data) {
// Handle data URI format (data:application/pdf;base64,...)
const dataUri = data.exportDocumentVersionPDF.data;
// Extract base64 part from data URI
const base64Data = dataUri.split(",")[1];
// Convert base64 to blob and download
const binaryString = atob(base64Data);
const bytes = new Uint8Array(binaryString.length);
for (let i = 0; i < binaryString.length; i++) {
bytes[i] = binaryString.charCodeAt(i);
}
const blob = new Blob([bytes], { type: "application/pdf" });
const url = URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = url;
link.download = `${props.document.title}-v${selectedVersion.version}.pdf`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(url);
}
},
});
};
return (
<Dialog trigger={props.children}>
<DialogContent className="flex" scrollableChildren>
@@ -59,6 +113,16 @@ export function DocumentVersionHistoryDialog(props: Props) {
))}
</aside>
<main className="flex-1 px-12 py-8">
<div className="mb-4">
<Button
onClick={handleDownloadPDF}
icon={IconArrowInbox}
variant="secondary"
disabled={isExporting}
>
{isExporting ? __("Exporting...") : __("Download PDF")}
</Button>
</div>
<Markdown content={selectedVersion.content} />
</main>
</DialogContent>

View File

@@ -0,0 +1,95 @@
/**
* @generated SignedSource<<06126b60bc37d8fbbb9a475af6d5d29d>>
* @lightSyntaxTransform
* @nogrep
*/
/* tslint:disable */
/* eslint-disable */
// @ts-nocheck
import { ConcreteRequest } from 'relay-runtime';
export type DocumentVersionHistoryDialogExportPDFMutation$variables = {
documentVersionId: string;
};
export type DocumentVersionHistoryDialogExportPDFMutation$data = {
readonly exportDocumentVersionPDF: {
readonly data: string;
};
};
export type DocumentVersionHistoryDialogExportPDFMutation = {
response: DocumentVersionHistoryDialogExportPDFMutation$data;
variables: DocumentVersionHistoryDialogExportPDFMutation$variables;
};
const node: ConcreteRequest = (function(){
var v0 = [
{
"defaultValue": null,
"kind": "LocalArgument",
"name": "documentVersionId"
}
],
v1 = [
{
"alias": null,
"args": [
{
"fields": [
{
"kind": "Variable",
"name": "documentVersionId",
"variableName": "documentVersionId"
}
],
"kind": "ObjectValue",
"name": "input"
}
],
"concreteType": "ExportDocumentVersionPDFPayload",
"kind": "LinkedField",
"name": "exportDocumentVersionPDF",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "data",
"storageKey": null
}
],
"storageKey": null
}
];
return {
"fragment": {
"argumentDefinitions": (v0/*: any*/),
"kind": "Fragment",
"metadata": null,
"name": "DocumentVersionHistoryDialogExportPDFMutation",
"selections": (v1/*: any*/),
"type": "Mutation",
"abstractKey": null
},
"kind": "Request",
"operation": {
"argumentDefinitions": (v0/*: any*/),
"kind": "Operation",
"name": "DocumentVersionHistoryDialogExportPDFMutation",
"selections": (v1/*: any*/)
},
"params": {
"cacheID": "45204e42c1018e00dcceaae84eb9a5f7",
"id": null,
"metadata": {},
"name": "DocumentVersionHistoryDialogExportPDFMutation",
"operationKind": "mutation",
"text": "mutation DocumentVersionHistoryDialogExportPDFMutation(\n $documentVersionId: ID!\n) {\n exportDocumentVersionPDF(input: {documentVersionId: $documentVersionId}) {\n data\n }\n}\n"
}
};
})();
(node as any).hash = "587bcc8bb6cf72e64a5eca18769ac432";
export default node;