@bigbinary/neeto-pdf-frontend 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +413 -0
- package/app/javascript/src/translations/ar.json +5 -0
- package/app/javascript/src/translations/bg.json +5 -0
- package/app/javascript/src/translations/ca.json +5 -0
- package/app/javascript/src/translations/cs.json +5 -0
- package/app/javascript/src/translations/da.json +5 -0
- package/app/javascript/src/translations/de.json +5 -0
- package/app/javascript/src/translations/en.json +109 -0
- package/app/javascript/src/translations/es-MX.json +5 -0
- package/app/javascript/src/translations/es.json +5 -0
- package/app/javascript/src/translations/et.json +5 -0
- package/app/javascript/src/translations/fi.json +5 -0
- package/app/javascript/src/translations/fil.json +5 -0
- package/app/javascript/src/translations/fr.json +5 -0
- package/app/javascript/src/translations/he.json +5 -0
- package/app/javascript/src/translations/hi.json +5 -0
- package/app/javascript/src/translations/hr.json +5 -0
- package/app/javascript/src/translations/hu.json +5 -0
- package/app/javascript/src/translations/id.json +5 -0
- package/app/javascript/src/translations/index.js +3 -0
- package/app/javascript/src/translations/it.json +5 -0
- package/app/javascript/src/translations/ja.json +5 -0
- package/app/javascript/src/translations/ko.json +5 -0
- package/app/javascript/src/translations/nl.json +5 -0
- package/app/javascript/src/translations/pl.json +5 -0
- package/app/javascript/src/translations/pt-BR.json +5 -0
- package/app/javascript/src/translations/pt.json +5 -0
- package/app/javascript/src/translations/ro.json +5 -0
- package/app/javascript/src/translations/ru.json +5 -0
- package/app/javascript/src/translations/sk.json +5 -0
- package/app/javascript/src/translations/sl.json +5 -0
- package/app/javascript/src/translations/sv.json +5 -0
- package/app/javascript/src/translations/th.json +5 -0
- package/app/javascript/src/translations/tr.json +5 -0
- package/app/javascript/src/translations/uk.json +5 -0
- package/app/javascript/src/translations/vi.json +5 -0
- package/app/javascript/src/translations/zh-CN.json +5 -0
- package/app/javascript/src/translations/zh-TW.json +5 -0
- package/dist/.ready +1 -0
- package/dist/AttachmentCard-75GY2Thb.js +64 -0
- package/dist/AttachmentCard-75GY2Thb.js.map +1 -0
- package/dist/AttachmentCard-D_Gp0f5r.js +67 -0
- package/dist/AttachmentCard-D_Gp0f5r.js.map +1 -0
- package/dist/FilenameUtilities-Cw9Tvd6k.js +329 -0
- package/dist/FilenameUtilities-Cw9Tvd6k.js.map +1 -0
- package/dist/FilenameUtilities-De2k_NXh.js +318 -0
- package/dist/FilenameUtilities-De2k_NXh.js.map +1 -0
- package/dist/IconBlock-BTPLDDiO.js +15 -0
- package/dist/IconBlock-BTPLDDiO.js.map +1 -0
- package/dist/IconBlock-hZ_gOnb1.js +17 -0
- package/dist/IconBlock-hZ_gOnb1.js.map +1 -0
- package/dist/PdfAttachmentSelector.js +94 -0
- package/dist/PdfAttachmentSelector.js.map +1 -0
- package/dist/PdfAttachmentsPreview.js +43 -0
- package/dist/PdfAttachmentsPreview.js.map +1 -0
- package/dist/PdfDocumentsPage.js +555 -0
- package/dist/PdfDocumentsPage.js.map +1 -0
- package/dist/PdfEditorPage.js +2051 -0
- package/dist/PdfEditorPage.js.map +1 -0
- package/dist/cjs/PdfAttachmentSelector.js +96 -0
- package/dist/cjs/PdfAttachmentSelector.js.map +1 -0
- package/dist/cjs/PdfAttachmentsPreview.js +45 -0
- package/dist/cjs/PdfAttachmentsPreview.js.map +1 -0
- package/dist/cjs/PdfDocumentsPage.js +557 -0
- package/dist/cjs/PdfDocumentsPage.js.map +1 -0
- package/dist/cjs/PdfEditorPage.js +2072 -0
- package/dist/cjs/PdfEditorPage.js.map +1 -0
- package/dist/cjs/index.js +75 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/constants-CTDOz942.js +5 -0
- package/dist/constants-CTDOz942.js.map +1 -0
- package/dist/constants-DvvJYjxW.js +8 -0
- package/dist/constants-DvvJYjxW.js.map +1 -0
- package/dist/index.js +66 -0
- package/dist/index.js.map +1 -0
- package/package.json +240 -0
- package/types/PdfAttachmentSelector.d.ts +2 -0
- package/types/PdfAttachmentsPreview.d.ts +2 -0
- package/types/PdfDocumentsPage.d.ts +6 -0
- package/types/PdfEditorPage.d.ts +7 -0
- package/types/index.d.ts +88 -0
- package/types.d.ts +1 -0
package/README.md
ADDED
|
@@ -0,0 +1,413 @@
|
|
|
1
|
+
# neeto-pdf-nano
|
|
2
|
+
|
|
3
|
+
`neeto-pdf-nano` provides shared PDF document support for neeto products. It
|
|
4
|
+
publishes two packages:
|
|
5
|
+
|
|
6
|
+
- `neeto-pdf-engine`, a Rails engine for storing PDF templates and mappings,
|
|
7
|
+
exposing document APIs, and rendering completed PDFs.
|
|
8
|
+
- `@bigbinary/neeto-pdf-frontend`, a React package for managing templates,
|
|
9
|
+
mapping fields, previewing PDFs, and selecting email attachments.
|
|
10
|
+
|
|
11
|
+
The host product still owns its routes and permissions, the model that owns the
|
|
12
|
+
documents, the fields shown in the editor, preview records, email settings, and
|
|
13
|
+
the values used when rendering a completed PDF.
|
|
14
|
+
|
|
15
|
+
## Requirements
|
|
16
|
+
|
|
17
|
+
The backend expects a Rails application with Active Record, Active Storage,
|
|
18
|
+
PostgreSQL, and `NeetoCommonsBackend::Api::BaseController`. The provided
|
|
19
|
+
migration uses UUIDs for document and owner IDs and uses `jsonb` columns.
|
|
20
|
+
|
|
21
|
+
The frontend expects the standard neeto application setup for React Query,
|
|
22
|
+
i18next, Axios, neetoUI, neetoMolecules, and Tailwind CSS. The email attachment
|
|
23
|
+
components must be rendered inside a Formik.
|
|
24
|
+
|
|
25
|
+
## Backend setup
|
|
26
|
+
|
|
27
|
+
### 1. Add the engine
|
|
28
|
+
|
|
29
|
+
Add the gem to the host application's `Gemfile`:
|
|
30
|
+
|
|
31
|
+
```ruby
|
|
32
|
+
source "NEETO_GEM_SERVER_URL" do
|
|
33
|
+
# Existing gems
|
|
34
|
+
gem "neeto-pdf-engine"
|
|
35
|
+
end
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Then install it:
|
|
39
|
+
|
|
40
|
+
```sh
|
|
41
|
+
bundle install
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### 2. Install the database migration
|
|
45
|
+
|
|
46
|
+
Copy only `db/migrate/20260706120000_create_neeto_pdf_engine_documents.rb` from
|
|
47
|
+
this engine into the host application's `db/migrate` directory. Give the copied
|
|
48
|
+
file a timestamp that does not conflict with a host migration, then run:
|
|
49
|
+
|
|
50
|
+
```sh
|
|
51
|
+
bundle exec rails db:migrate
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Do not run the general engine migration installer yet. This repository still
|
|
55
|
+
contains migrations inherited from the nano template that do not belong to the
|
|
56
|
+
PDF feature.
|
|
57
|
+
|
|
58
|
+
The migration stores `owner_id` as a UUID. Adjust and test the migration before
|
|
59
|
+
running it if the host's owner model does not use UUID primary keys.
|
|
60
|
+
|
|
61
|
+
### 3. Configure the document owner
|
|
62
|
+
|
|
63
|
+
Create `config/initializers/neeto_pdf_engine.rb` in the host application and set
|
|
64
|
+
the class name of the model that owns PDF documents:
|
|
65
|
+
|
|
66
|
+
```ruby
|
|
67
|
+
NeetoPdfEngine.owner_class = "Form"
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
The value must be a class-name string. The owner must either be the current
|
|
71
|
+
organization or support `where(organization: @organization)`, because the engine
|
|
72
|
+
scopes every request through `@organization`.
|
|
73
|
+
|
|
74
|
+
Add an association to the owner model so its PDF documents are removed with the
|
|
75
|
+
owner:
|
|
76
|
+
|
|
77
|
+
```ruby
|
|
78
|
+
class Form < ApplicationRecord
|
|
79
|
+
belongs_to :organization
|
|
80
|
+
|
|
81
|
+
has_many :neeto_pdf_documents,
|
|
82
|
+
as: :owner,
|
|
83
|
+
class_name: "NeetoPdfEngine::Document",
|
|
84
|
+
dependent: :destroy
|
|
85
|
+
end
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### 4. Mount the engine
|
|
89
|
+
|
|
90
|
+
Mount the engine at a stable path in `config/routes.rb`:
|
|
91
|
+
|
|
92
|
+
```ruby
|
|
93
|
+
mount NeetoPdfEngine::Engine, at: "/neeto_pdf"
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
The frontend API base URL for this mount point is:
|
|
97
|
+
|
|
98
|
+
```js
|
|
99
|
+
const pdfApiBaseUrl = "/neeto_pdf/api/v1";
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### 5. Enforce product permissions
|
|
103
|
+
|
|
104
|
+
The engine authenticates requests and scopes documents to the current
|
|
105
|
+
organization and owner. It does not know the host product's permission model.
|
|
106
|
+
The host must enforce permissions for every applicable read and write endpoint
|
|
107
|
+
on the server. Add the check through the host's shared API authorization layer
|
|
108
|
+
or prepend a host-owned controller override with a permission `before_action` to
|
|
109
|
+
`NeetoPdfEngine::Api::V1::DocumentsController`.
|
|
110
|
+
|
|
111
|
+
The frontend `canManage` prop only controls parts of the interface. It is not a
|
|
112
|
+
server-side authorization check and must not be used as one.
|
|
113
|
+
|
|
114
|
+
## Frontend setup
|
|
115
|
+
|
|
116
|
+
### 1. Add the package
|
|
117
|
+
|
|
118
|
+
Install the frontend package in the host application:
|
|
119
|
+
|
|
120
|
+
```sh
|
|
121
|
+
yarn add @bigbinary/neeto-pdf-frontend pdf-lib@1.17.1 pdfjs-dist@6.1.200
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
The package uses the host's configured Axios instance and expects the normal
|
|
125
|
+
neeto request and response case transformations. It also expects a React Query
|
|
126
|
+
provider and an initialized i18next instance.
|
|
127
|
+
|
|
128
|
+
Template uploads in the provided UI use the neetoMolecules `FileUpload` direct
|
|
129
|
+
upload flow. Confirm that Active Storage direct uploads are configured and that
|
|
130
|
+
uploaded files receive a signed ID before integrating the documents page.
|
|
131
|
+
|
|
132
|
+
### 2. Include package styles
|
|
133
|
+
|
|
134
|
+
The components use Tailwind utility classes from the installed package. Make
|
|
135
|
+
sure the host's Tailwind stylesheet scans `@bigbinary` packages. For Tailwind
|
|
136
|
+
CSS 4, add an appropriate `@source` path relative to the host stylesheet:
|
|
137
|
+
|
|
138
|
+
```css
|
|
139
|
+
@import "tailwindcss" important;
|
|
140
|
+
@source "../../../../node_modules/@bigbinary";
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Keep the host application's usual neetoUI and neetoMolecules styles enabled.
|
|
144
|
+
There is no separate PDF package stylesheet to import.
|
|
145
|
+
|
|
146
|
+
### 3. Load translations
|
|
147
|
+
|
|
148
|
+
Applications using the standard neeto-commons build configuration load
|
|
149
|
+
translations from installed `@bigbinary` packages automatically. Other hosts
|
|
150
|
+
must merge the package's `app/javascript/src/translations/en.json` file into
|
|
151
|
+
their i18next resources before rendering these components.
|
|
152
|
+
|
|
153
|
+
### 4. Install the PDF.js worker
|
|
154
|
+
|
|
155
|
+
The editor loads the PDF.js worker from `/pdf.worker.min.js`. Add a script to
|
|
156
|
+
the host application's `package.json` that copies the worker from the installed
|
|
157
|
+
`pdfjs-dist` version:
|
|
158
|
+
|
|
159
|
+
```json
|
|
160
|
+
{
|
|
161
|
+
"scripts": {
|
|
162
|
+
"sync:pdf-worker": "mkdir -p public && cp -f node_modules/pdfjs-dist/build/pdf.worker.min.mjs public/pdf.worker.min.js"
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Prepend `yarn sync:pdf-worker &&` to the host's existing `build` and `dev`
|
|
168
|
+
commands so the sync runs in development and production. The host must serve the
|
|
169
|
+
copied file at the root URL `/pdf.worker.min.js`.
|
|
170
|
+
|
|
171
|
+
## Documents page
|
|
172
|
+
|
|
173
|
+
`PdfDocumentsPage` lists the owner's templates and supports uploading, renaming,
|
|
174
|
+
replacing, cloning, and deleting documents. The host owns the route that renders
|
|
175
|
+
this page and the route used to open the editor.
|
|
176
|
+
|
|
177
|
+
```jsx
|
|
178
|
+
import { PdfDocumentsPage } from "@bigbinary/neeto-pdf-frontend";
|
|
179
|
+
|
|
180
|
+
const Documents = ({ formId, selectedPdfDocumentIds }) => (
|
|
181
|
+
<PdfDocumentsPage
|
|
182
|
+
apiBaseUrl="/neeto_pdf/api/v1"
|
|
183
|
+
canManage={canManagePdfDocuments}
|
|
184
|
+
emailRoutes={{
|
|
185
|
+
submitter: submitterEmailSettingsUrl,
|
|
186
|
+
receiver: receiverEmailSettingsUrl,
|
|
187
|
+
}}
|
|
188
|
+
ownerId={formId}
|
|
189
|
+
selectedPdfDocumentIds={selectedPdfDocumentIds}
|
|
190
|
+
onOpenEditor={document => navigateToPdfEditor(document.id)}
|
|
191
|
+
/>
|
|
192
|
+
);
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
`onOpenEditor` receives the full document object, not only its ID.
|
|
196
|
+
`selectedPdfDocumentIds` is display-only on this page; the host's email settings
|
|
197
|
+
remain the source of truth for selected attachments.
|
|
198
|
+
|
|
199
|
+
Use the optional `renderHeader` and `renderContent` props when the page needs to
|
|
200
|
+
fit an existing host layout:
|
|
201
|
+
|
|
202
|
+
```jsx
|
|
203
|
+
<PdfDocumentsPage
|
|
204
|
+
renderHeader={({ actionBlock, title }) => (
|
|
205
|
+
<HostPageHeader {...{ actionBlock, title }} />
|
|
206
|
+
)}
|
|
207
|
+
renderContent={content => <HostPageContent>{content}</HostPageContent>}
|
|
208
|
+
{...otherProps}
|
|
209
|
+
/>
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
## Editor page
|
|
213
|
+
|
|
214
|
+
The host must load and normalize its product-specific fields and preview records
|
|
215
|
+
before passing them to `PdfEditorPage`. The nano does not import form or
|
|
216
|
+
submission APIs from a host product.
|
|
217
|
+
|
|
218
|
+
```jsx
|
|
219
|
+
import { PdfEditorPage } from "@bigbinary/neeto-pdf-frontend";
|
|
220
|
+
|
|
221
|
+
<PdfEditorPage
|
|
222
|
+
apiBaseUrl="/neeto_pdf/api/v1"
|
|
223
|
+
canManage={canManagePdfDocuments}
|
|
224
|
+
documentId={documentId}
|
|
225
|
+
fields={pdfFields}
|
|
226
|
+
homeUrl={pdfDocumentsUrl}
|
|
227
|
+
isPreviewLoading={isPreviewLoading}
|
|
228
|
+
mode={mode}
|
|
229
|
+
ownerId={formId}
|
|
230
|
+
ownerRecord={form}
|
|
231
|
+
previewPage={previewPage}
|
|
232
|
+
previewRecord={submission}
|
|
233
|
+
previewTotal={submissionsTotal}
|
|
234
|
+
onBack={navigateToPdfDocuments}
|
|
235
|
+
onModeChange={setMode}
|
|
236
|
+
onPreviewPageChange={setPreviewPage}
|
|
237
|
+
/>;
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
Pass a real `homeUrl` so the visible back action has a valid link. Use `onBack`
|
|
241
|
+
as well when the host needs custom history behavior. The `mode` and
|
|
242
|
+
`onModeChange` props are optional; pass them when editor mode should be stored
|
|
243
|
+
in the host URL or state.
|
|
244
|
+
|
|
245
|
+
### Field shape
|
|
246
|
+
|
|
247
|
+
Each host field may include the following values:
|
|
248
|
+
|
|
249
|
+
```js
|
|
250
|
+
{
|
|
251
|
+
id: "record-id",
|
|
252
|
+
key: "email",
|
|
253
|
+
recordId: "record-id",
|
|
254
|
+
slug: "email",
|
|
255
|
+
label: "Email",
|
|
256
|
+
kind: "email",
|
|
257
|
+
icon: EmailIcon,
|
|
258
|
+
typeLabel: "Email"
|
|
259
|
+
}
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
Use stable IDs and slugs. Convert HTML labels to plain text, omit records that
|
|
263
|
+
cannot contain answers, and attach the host's icon and type label before passing
|
|
264
|
+
fields to the editor. The nano adds its own system fields for form name,
|
|
265
|
+
submission ID, and submission date.
|
|
266
|
+
|
|
267
|
+
### Preview record shape
|
|
268
|
+
|
|
269
|
+
Preview records should have an ID, a creation time, and responses whose slugs
|
|
270
|
+
match the editor fields:
|
|
271
|
+
|
|
272
|
+
```js
|
|
273
|
+
{
|
|
274
|
+
id: "submission-id",
|
|
275
|
+
createdAt: "2026-07-14T10:30:00Z",
|
|
276
|
+
responses: [
|
|
277
|
+
{ slug: "email", value: "oliver@example.com" }
|
|
278
|
+
]
|
|
279
|
+
}
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
The preview will be blank for a field when its slug has no matching response.
|
|
283
|
+
|
|
284
|
+
## Email attachment controls
|
|
285
|
+
|
|
286
|
+
`PdfAttachmentSelector` and `PdfAttachmentsPreview` read the top-level
|
|
287
|
+
`pdfDocumentIds` field from Formik. Pass all available PDF documents to both
|
|
288
|
+
components; the preview component filters the selected documents itself.
|
|
289
|
+
|
|
290
|
+
```jsx
|
|
291
|
+
import {
|
|
292
|
+
PdfAttachmentSelector,
|
|
293
|
+
PdfAttachmentsPreview,
|
|
294
|
+
} from "@bigbinary/neeto-pdf-frontend";
|
|
295
|
+
import { Formik, Form } from "formik";
|
|
296
|
+
|
|
297
|
+
<Formik
|
|
298
|
+
initialValues={{ pdfDocumentIds: persistedPdfDocumentIds }}
|
|
299
|
+
onSubmit={saveEmailSettings}
|
|
300
|
+
>
|
|
301
|
+
<Form>
|
|
302
|
+
<PdfAttachmentSelector documents={pdfDocuments} />
|
|
303
|
+
<PdfAttachmentsPreview documents={pdfDocuments} />
|
|
304
|
+
</Form>
|
|
305
|
+
</Formik>;
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
In a typical host integration, this value appears in three forms:
|
|
309
|
+
|
|
310
|
+
- Formik uses `pdfDocumentIds`.
|
|
311
|
+
- Frontend API objects use `customFields.pdfDocumentIds`.
|
|
312
|
+
- Rails persistence uses `custom_fields["pdf_document_ids"]`.
|
|
313
|
+
|
|
314
|
+
The host email API must permit an array at `custom_fields[:pdf_document_ids]`.
|
|
315
|
+
Remove IDs for deleted or inaccessible documents before saving email settings.
|
|
316
|
+
|
|
317
|
+
## Rendering PDFs on the server
|
|
318
|
+
|
|
319
|
+
The renderer knows how to place text on the template, but it does not know how
|
|
320
|
+
the host stores or formats product data. Pass either a callable or an object
|
|
321
|
+
that implements `value_for(mapping)`:
|
|
322
|
+
|
|
323
|
+
```ruby
|
|
324
|
+
document = form.neeto_pdf_documents.find(document_id)
|
|
325
|
+
|
|
326
|
+
attachment = NeetoPdfEngine::Documents::RendererService.new(
|
|
327
|
+
document,
|
|
328
|
+
value_resolver: ->(mapping) { value_for_submission(mapping, submission) },
|
|
329
|
+
filename_context: { id: submission.id }
|
|
330
|
+
).process
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
The result contains the generated filename, MIME type, and raw PDF bytes:
|
|
334
|
+
|
|
335
|
+
```ruby
|
|
336
|
+
{
|
|
337
|
+
filename: "agreement-123.pdf",
|
|
338
|
+
mime_type: "application/pdf",
|
|
339
|
+
content: "<binary PDF>"
|
|
340
|
+
}
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
When rendering selected email attachments, always scope document IDs to their
|
|
344
|
+
owner instead of querying IDs globally:
|
|
345
|
+
|
|
346
|
+
```ruby
|
|
347
|
+
documents = NeetoPdfEngine::Document.where(
|
|
348
|
+
owner_type: "Form",
|
|
349
|
+
owner_id: form.id,
|
|
350
|
+
id: selected_pdf_document_ids
|
|
351
|
+
)
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
The host mailer decides whether the raw `content` needs Base64 encoding or any
|
|
355
|
+
other transformation.
|
|
356
|
+
|
|
357
|
+
The renderer currently turns whitespace into a single line and supports ASCII
|
|
358
|
+
and Latin-1 text. Values outside that character range are removed, so hosts
|
|
359
|
+
should account for this when deciding which fields can be rendered. Browser
|
|
360
|
+
downloads and server-generated attachments can also differ for some styles;
|
|
361
|
+
underline mappings are currently rendered only by the browser.
|
|
362
|
+
|
|
363
|
+
## API reference
|
|
364
|
+
|
|
365
|
+
With the engine mounted at `/neeto_pdf`, these endpoints are available:
|
|
366
|
+
|
|
367
|
+
```text
|
|
368
|
+
GET /neeto_pdf/api/v1/documents
|
|
369
|
+
POST /neeto_pdf/api/v1/documents
|
|
370
|
+
GET /neeto_pdf/api/v1/documents/:id
|
|
371
|
+
PATCH /neeto_pdf/api/v1/documents/:id
|
|
372
|
+
PUT /neeto_pdf/api/v1/documents/:id
|
|
373
|
+
PATCH /neeto_pdf/api/v1/documents/:id/template_file
|
|
374
|
+
POST /neeto_pdf/api/v1/documents/:id/clone
|
|
375
|
+
DELETE /neeto_pdf/api/v1/documents/:id
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
Every request must include `owner_id`. The engine derives the polymorphic owner
|
|
379
|
+
type from `NeetoPdfEngine.owner_class`; clients cannot choose `owner_type`.
|
|
380
|
+
Member actions are also scoped to that owner.
|
|
381
|
+
|
|
382
|
+
The package UI uploads templates through the host's Active Storage direct upload
|
|
383
|
+
setup and sends the signed blob ID. A custom client may instead send the PDF as
|
|
384
|
+
multipart `FormData` under `template_file`. Do not set multipart boundaries
|
|
385
|
+
manually.
|
|
386
|
+
|
|
387
|
+
When a document is renamed, update both `name` and `output_filename` if the host
|
|
388
|
+
does not expose a separate output filename field. The default filename follows
|
|
389
|
+
this pattern:
|
|
390
|
+
|
|
391
|
+
```text
|
|
392
|
+
<parameterized-document-name>-{{id}}.pdf
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
The renderer replaces `{{id}}` with `filename_context[:id]`.
|
|
396
|
+
|
|
397
|
+
## Local development
|
|
398
|
+
|
|
399
|
+
To use a local checkout of the Rails engine from a neighboring host, point the
|
|
400
|
+
host Gemfile to this repository:
|
|
401
|
+
|
|
402
|
+
```ruby
|
|
403
|
+
gem "neeto-pdf-engine", path: "../neeto-pdf-nano"
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
For frontend package development, follow the
|
|
407
|
+
[frontend package development guide](https://neetozone.neetokb.com/p/a-d34cb4b0).
|
|
408
|
+
|
|
409
|
+
## Publishing
|
|
410
|
+
|
|
411
|
+
Follow the
|
|
412
|
+
[building and releasing packages](https://neetozone.neetokb.com/articles/building-and-releasing-packages)
|
|
413
|
+
guide to publish the gem or frontend package.
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
{
|
|
2
|
+
"neetoPdf": {
|
|
3
|
+
"common": {
|
|
4
|
+
"cancel": "Cancel",
|
|
5
|
+
"height": "Height",
|
|
6
|
+
"save": "Save",
|
|
7
|
+
"width": "Width"
|
|
8
|
+
},
|
|
9
|
+
"pdfDocuments": {
|
|
10
|
+
"title": "PDF documents",
|
|
11
|
+
"description": "Upload your own PDF template and drop form answers onto it - one document is generated for every submission.",
|
|
12
|
+
"newDocument": "New document",
|
|
13
|
+
"attachedToEmail": "Attached to email",
|
|
14
|
+
"empty": "Upload a PDF template to get started.",
|
|
15
|
+
"uploadSuccess": "PDF document has been uploaded successfully.",
|
|
16
|
+
"uploadError": "Unable to upload PDF document.",
|
|
17
|
+
"errors": {
|
|
18
|
+
"invalidType": "must be a PDF file",
|
|
19
|
+
"fileTooLarge": "must be 10 MB or smaller"
|
|
20
|
+
},
|
|
21
|
+
"menu": {
|
|
22
|
+
"edit": "Edit",
|
|
23
|
+
"rename": "Rename",
|
|
24
|
+
"updatePdf": "Update PDF",
|
|
25
|
+
"clone": "Clone",
|
|
26
|
+
"delete": "Delete"
|
|
27
|
+
},
|
|
28
|
+
"rename": {
|
|
29
|
+
"title": "Rename document",
|
|
30
|
+
"label": "Name",
|
|
31
|
+
"placeholder": "Enter document name",
|
|
32
|
+
"success": "PDF document has been renamed successfully.",
|
|
33
|
+
"error": "Unable to rename PDF document."
|
|
34
|
+
},
|
|
35
|
+
"updatePdf": {
|
|
36
|
+
"success": "PDF document has been updated successfully.",
|
|
37
|
+
"error": "Unable to update PDF document."
|
|
38
|
+
},
|
|
39
|
+
"clone": {
|
|
40
|
+
"success": "PDF document has been cloned successfully.",
|
|
41
|
+
"error": "Unable to clone PDF document."
|
|
42
|
+
},
|
|
43
|
+
"delete": {
|
|
44
|
+
"title": "Delete PDF document?",
|
|
45
|
+
"message": "You are deleting \"{{name}}\". This can't be undone.",
|
|
46
|
+
"success": "PDF document has been deleted successfully.",
|
|
47
|
+
"error": "Unable to delete PDF document."
|
|
48
|
+
},
|
|
49
|
+
"sendPdfInEmail": {
|
|
50
|
+
"title": "Send PDF in email",
|
|
51
|
+
"description": "Attach the generated PDFs to the email sent on each submission.",
|
|
52
|
+
"submitter": "Send the PDF to the form submitter",
|
|
53
|
+
"notification": "Send the PDF in the notification email"
|
|
54
|
+
},
|
|
55
|
+
"attachments": {
|
|
56
|
+
"title": "Attachments",
|
|
57
|
+
"description": "Attach generated PDF documents to this email.",
|
|
58
|
+
"empty": "Upload a PDF document to attach it to this email.",
|
|
59
|
+
"fileType": "PDF",
|
|
60
|
+
"selected": "{{count}} PDF attached to email",
|
|
61
|
+
"selected_other": "{{count}} PDFs attached to email",
|
|
62
|
+
"previewTitle": "{{count}} attachment",
|
|
63
|
+
"previewTitle_other": "{{count}} attachments"
|
|
64
|
+
},
|
|
65
|
+
"editor": {
|
|
66
|
+
"back": "Back to PDF documents",
|
|
67
|
+
"addNewElement": "Add new element",
|
|
68
|
+
"addNewElementDescription": "To add elements, click or drag them onto the preview.",
|
|
69
|
+
"mapFields": "Map fields",
|
|
70
|
+
"previewWithData": "Preview with data",
|
|
71
|
+
"downloadPdf": "Download PDF",
|
|
72
|
+
"downloadTooltip": "Download this submission as a PDF",
|
|
73
|
+
"downloadHint": "Switch to Preview with data to download",
|
|
74
|
+
"downloadError": "Unable to generate the PDF.",
|
|
75
|
+
"formFields": "Form fields",
|
|
76
|
+
"formFieldsDescription": "Drag a field onto the document, or click + to place it.",
|
|
77
|
+
"searchFields": "Search fields",
|
|
78
|
+
"searchElements": "Search elements",
|
|
79
|
+
"answers": "Answers",
|
|
80
|
+
"system": "System",
|
|
81
|
+
"noFields": "No fields match your search.",
|
|
82
|
+
"addField": "Add to document",
|
|
83
|
+
"renderError": "Unable to render this PDF.",
|
|
84
|
+
"saveSuccess": "Field placements have been saved.",
|
|
85
|
+
"saveError": "Unable to save field placements.",
|
|
86
|
+
"systemFields": {
|
|
87
|
+
"formName": "Form name",
|
|
88
|
+
"submissionId": "Submission ID",
|
|
89
|
+
"submissionDate": "Submission date"
|
|
90
|
+
},
|
|
91
|
+
"field": {
|
|
92
|
+
"title": "Field",
|
|
93
|
+
"emptyTitle": "No field selected",
|
|
94
|
+
"emptyDescription": "Click a placed field on the document to edit its font, size, color and format, or drag its edges to resize.",
|
|
95
|
+
"font": "Font",
|
|
96
|
+
"size": "Size",
|
|
97
|
+
"format": "Format",
|
|
98
|
+
"fontColor": "Font color",
|
|
99
|
+
"alignment": "Alignment",
|
|
100
|
+
"remove": "Remove from document"
|
|
101
|
+
},
|
|
102
|
+
"preview": {
|
|
103
|
+
"counter": "Submission {{page}} of {{total}}",
|
|
104
|
+
"noSubmissions": "No submissions yet"
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|