create-dfactory 0.1.0
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/LICENSE +21 -0
- package/README.md +37 -0
- package/package.json +50 -0
- package/templates/base/dfactory.config.ts +34 -0
- package/templates/react/src/templates/invoice/template.tsx +154 -0
- package/templates/react/src/templates/invoice-reference/components/InvoiceReferenceDocument.tsx +309 -0
- package/templates/react/src/templates/invoice-reference/components/InvoiceReferenceFooter.tsx +26 -0
- package/templates/react/src/templates/invoice-reference/components/InvoiceReferenceHeader.tsx +23 -0
- package/templates/react/src/templates/invoice-reference/components/InvoiceReferencePagination.tsx +24 -0
- package/templates/react/src/templates/invoice-reference/components/InvoiceReferenceToc.tsx +30 -0
- package/templates/react/src/templates/invoice-reference/components/InvoiceReferenceWatermark.tsx +16 -0
- package/templates/react/src/templates/invoice-reference/components/types.ts +65 -0
- package/templates/react/src/templates/invoice-reference/template.tsx +307 -0
- package/templates/vue/src/templates/invoice/InvoiceTemplate.vue +96 -0
- package/templates/vue/src/templates/invoice/template.ts +85 -0
- package/templates/vue/src/templates/invoice-reference/components/InvoiceReferenceDocument.vue +241 -0
- package/templates/vue/src/templates/invoice-reference/components/InvoiceReferenceFooter.vue +26 -0
- package/templates/vue/src/templates/invoice-reference/components/InvoiceReferenceHeader.vue +25 -0
- package/templates/vue/src/templates/invoice-reference/components/InvoiceReferencePagination.vue +24 -0
- package/templates/vue/src/templates/invoice-reference/components/InvoiceReferenceToc.vue +34 -0
- package/templates/vue/src/templates/invoice-reference/components/InvoiceReferenceWatermark.vue +20 -0
- package/templates/vue/src/templates/invoice-reference/components/types.ts +65 -0
- package/templates/vue/src/templates/invoice-reference/template.ts +300 -0
- package/templates/vue/src/vue-shims.d.ts +6 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { TemplateTocHeading } from "@dfactory/core";
|
|
2
|
+
|
|
3
|
+
export function InvoiceReferenceToc(props: {
|
|
4
|
+
title: string;
|
|
5
|
+
headings: TemplateTocHeading[];
|
|
6
|
+
}) {
|
|
7
|
+
return (
|
|
8
|
+
<nav
|
|
9
|
+
aria-label={props.title}
|
|
10
|
+
style={{
|
|
11
|
+
marginBottom: "24px",
|
|
12
|
+
padding: "14px 16px",
|
|
13
|
+
borderRadius: "10px",
|
|
14
|
+
border: "1px solid rgba(79, 70, 229, 0.18)",
|
|
15
|
+
background: "rgba(79, 70, 229, 0.04)"
|
|
16
|
+
}}
|
|
17
|
+
>
|
|
18
|
+
<h2 style={{ margin: 0, fontSize: "16px", color: "#312e81" }}>{props.title}</h2>
|
|
19
|
+
<ol style={{ margin: "10px 0 0", paddingLeft: "18px" }}>
|
|
20
|
+
{props.headings.map((heading) => (
|
|
21
|
+
<li key={heading.id} style={{ marginTop: "4px", marginLeft: `${(heading.level - 1) * 10}px` }}>
|
|
22
|
+
<a href={`#${heading.id}`} style={{ color: "#1e293b", textDecoration: "none" }}>
|
|
23
|
+
{heading.text}
|
|
24
|
+
</a>
|
|
25
|
+
</li>
|
|
26
|
+
))}
|
|
27
|
+
</ol>
|
|
28
|
+
</nav>
|
|
29
|
+
);
|
|
30
|
+
}
|
package/templates/react/src/templates/invoice-reference/components/InvoiceReferenceWatermark.tsx
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export function InvoiceReferenceWatermark(props: { text: string }) {
|
|
2
|
+
return (
|
|
3
|
+
<div
|
|
4
|
+
style={{
|
|
5
|
+
transform: "rotate(-24deg)",
|
|
6
|
+
fontSize: "52px",
|
|
7
|
+
fontWeight: 700,
|
|
8
|
+
letterSpacing: "0.08em",
|
|
9
|
+
color: "rgba(79, 70, 229, 0.14)",
|
|
10
|
+
textTransform: "uppercase"
|
|
11
|
+
}}
|
|
12
|
+
>
|
|
13
|
+
{props.text}
|
|
14
|
+
</div>
|
|
15
|
+
);
|
|
16
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
export interface InvoiceReferenceCompany {
|
|
2
|
+
name: string;
|
|
3
|
+
address: string;
|
|
4
|
+
email: string;
|
|
5
|
+
phone: string;
|
|
6
|
+
website: string;
|
|
7
|
+
taxId: string;
|
|
8
|
+
logoUrl?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface InvoiceReferenceCustomer {
|
|
12
|
+
name: string;
|
|
13
|
+
contact: string;
|
|
14
|
+
email: string;
|
|
15
|
+
address: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface InvoiceReferenceLineItem {
|
|
19
|
+
id: string;
|
|
20
|
+
name: string;
|
|
21
|
+
description?: string;
|
|
22
|
+
qty: number;
|
|
23
|
+
unitPrice: number;
|
|
24
|
+
discount?: number;
|
|
25
|
+
taxRate?: number;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface InvoiceReferenceSection {
|
|
29
|
+
title: string;
|
|
30
|
+
description: string;
|
|
31
|
+
notes?: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface InvoiceReferencePayment {
|
|
35
|
+
iban: string;
|
|
36
|
+
swift: string;
|
|
37
|
+
bankName: string;
|
|
38
|
+
instructions?: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface InvoiceReferenceWatermark {
|
|
42
|
+
enabled: boolean;
|
|
43
|
+
text: string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface InvoiceReferenceBrand {
|
|
47
|
+
accentColor?: string;
|
|
48
|
+
supportEmail?: string;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface InvoiceReferencePayload {
|
|
52
|
+
invoiceNumber: string;
|
|
53
|
+
purchaseOrder?: string;
|
|
54
|
+
issuedAt: string;
|
|
55
|
+
dueAt: string;
|
|
56
|
+
currency: string;
|
|
57
|
+
company: InvoiceReferenceCompany;
|
|
58
|
+
customer: InvoiceReferenceCustomer;
|
|
59
|
+
items: InvoiceReferenceLineItem[];
|
|
60
|
+
sections: InvoiceReferenceSection[];
|
|
61
|
+
payment: InvoiceReferencePayment;
|
|
62
|
+
notes?: string;
|
|
63
|
+
watermark?: InvoiceReferenceWatermark;
|
|
64
|
+
brand?: InvoiceReferenceBrand;
|
|
65
|
+
}
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
import { defineTemplate } from "@dfactory/template-kit";
|
|
4
|
+
|
|
5
|
+
import { InvoiceReferenceDocument } from "./components/InvoiceReferenceDocument";
|
|
6
|
+
import { InvoiceReferenceFooter } from "./components/InvoiceReferenceFooter";
|
|
7
|
+
import { InvoiceReferenceHeader } from "./components/InvoiceReferenceHeader";
|
|
8
|
+
import { InvoiceReferencePagination } from "./components/InvoiceReferencePagination";
|
|
9
|
+
import { InvoiceReferenceToc } from "./components/InvoiceReferenceToc";
|
|
10
|
+
import { InvoiceReferenceWatermark } from "./components/InvoiceReferenceWatermark";
|
|
11
|
+
|
|
12
|
+
const lineItemSchema = z.object({
|
|
13
|
+
id: z.string(),
|
|
14
|
+
name: z.string(),
|
|
15
|
+
description: z.string().optional(),
|
|
16
|
+
qty: z.number().positive(),
|
|
17
|
+
unitPrice: z.number().nonnegative(),
|
|
18
|
+
discount: z.number().nonnegative().max(100).optional(),
|
|
19
|
+
taxRate: z.number().nonnegative().max(100).optional()
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
const sectionSchema = z.object({
|
|
23
|
+
title: z.string(),
|
|
24
|
+
description: z.string(),
|
|
25
|
+
notes: z.string().optional()
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
const invoiceReferenceSchema = z.object({
|
|
29
|
+
invoiceNumber: z.string(),
|
|
30
|
+
purchaseOrder: z.string().optional(),
|
|
31
|
+
issuedAt: z.string(),
|
|
32
|
+
dueAt: z.string(),
|
|
33
|
+
currency: z.string(),
|
|
34
|
+
company: z.object({
|
|
35
|
+
name: z.string(),
|
|
36
|
+
address: z.string(),
|
|
37
|
+
email: z.string(),
|
|
38
|
+
phone: z.string(),
|
|
39
|
+
website: z.string(),
|
|
40
|
+
taxId: z.string(),
|
|
41
|
+
logoUrl: z.string().url().optional()
|
|
42
|
+
}),
|
|
43
|
+
customer: z.object({
|
|
44
|
+
name: z.string(),
|
|
45
|
+
contact: z.string(),
|
|
46
|
+
email: z.string(),
|
|
47
|
+
address: z.string()
|
|
48
|
+
}),
|
|
49
|
+
items: z.array(lineItemSchema).min(1),
|
|
50
|
+
sections: z.array(sectionSchema).min(1),
|
|
51
|
+
payment: z.object({
|
|
52
|
+
iban: z.string(),
|
|
53
|
+
swift: z.string(),
|
|
54
|
+
bankName: z.string(),
|
|
55
|
+
instructions: z.string().optional()
|
|
56
|
+
}),
|
|
57
|
+
notes: z.string().optional(),
|
|
58
|
+
watermark: z
|
|
59
|
+
.object({
|
|
60
|
+
enabled: z.boolean(),
|
|
61
|
+
text: z.string()
|
|
62
|
+
})
|
|
63
|
+
.optional(),
|
|
64
|
+
brand: z
|
|
65
|
+
.object({
|
|
66
|
+
accentColor: z.string().optional(),
|
|
67
|
+
supportEmail: z.string().optional()
|
|
68
|
+
})
|
|
69
|
+
.optional()
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
type InvoiceReferencePayload = z.infer<typeof invoiceReferenceSchema>;
|
|
73
|
+
|
|
74
|
+
function createItems(count: number): InvoiceReferencePayload["items"] {
|
|
75
|
+
return Array.from({ length: count }, (_, index) => {
|
|
76
|
+
const row = index + 1;
|
|
77
|
+
return {
|
|
78
|
+
id: `line-${row}`,
|
|
79
|
+
name: `Professional Service Block ${row}`,
|
|
80
|
+
description:
|
|
81
|
+
row % 2 === 0
|
|
82
|
+
? "Includes discovery, implementation, and QA handoff."
|
|
83
|
+
: "Includes architecture review and stakeholder alignment.",
|
|
84
|
+
qty: (row % 3) + 1,
|
|
85
|
+
unitPrice: 180 + row * 12,
|
|
86
|
+
discount: row % 5 === 0 ? 8 : 0,
|
|
87
|
+
taxRate: 21
|
|
88
|
+
};
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function createSections(count: number): InvoiceReferencePayload["sections"] {
|
|
93
|
+
return Array.from({ length: count }, (_, index) => {
|
|
94
|
+
const row = index + 1;
|
|
95
|
+
return {
|
|
96
|
+
title: `Delivery Milestone ${row}`,
|
|
97
|
+
description:
|
|
98
|
+
"This section demonstrates long-form print content with structured headings, making TOC and page breaks easy to validate during authoring.",
|
|
99
|
+
notes:
|
|
100
|
+
row % 2 === 0
|
|
101
|
+
? "Attach acceptance notes and sign-off evidence for enterprise audits."
|
|
102
|
+
: "Use pagination markers for sections that should stay together."
|
|
103
|
+
};
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function createPayload(options: {
|
|
108
|
+
invoiceNumber: string;
|
|
109
|
+
lineCount: number;
|
|
110
|
+
watermarkText: string;
|
|
111
|
+
accentColor: string;
|
|
112
|
+
}): InvoiceReferencePayload {
|
|
113
|
+
return {
|
|
114
|
+
invoiceNumber: options.invoiceNumber,
|
|
115
|
+
purchaseOrder: "PO-40218",
|
|
116
|
+
issuedAt: "2026-03-27",
|
|
117
|
+
dueAt: "2026-04-10",
|
|
118
|
+
currency: "EUR",
|
|
119
|
+
company: {
|
|
120
|
+
name: "DFactory Labs Ltd.",
|
|
121
|
+
address: "A. Briana Street 9A\nRiga, LV-1001\nLatvia",
|
|
122
|
+
email: "finance@dfactory.dev",
|
|
123
|
+
phone: "+371 6700 4500",
|
|
124
|
+
website: "https://dfactory.dev",
|
|
125
|
+
taxId: "LV40203577891"
|
|
126
|
+
},
|
|
127
|
+
customer: {
|
|
128
|
+
name: "Northwind Trading Group",
|
|
129
|
+
contact: "Marta Jensen",
|
|
130
|
+
email: "marta.jensen@northwind.example",
|
|
131
|
+
address: "28 Innovation Plaza\nTallinn, 10111\nEstonia"
|
|
132
|
+
},
|
|
133
|
+
items: createItems(options.lineCount),
|
|
134
|
+
sections: createSections(Math.max(4, Math.ceil(options.lineCount / 4))),
|
|
135
|
+
payment: {
|
|
136
|
+
iban: "LV80HABA0551045933178",
|
|
137
|
+
swift: "HABALV22",
|
|
138
|
+
bankName: "Swedbank AS",
|
|
139
|
+
instructions: "Please reference invoice number in payment details."
|
|
140
|
+
},
|
|
141
|
+
notes:
|
|
142
|
+
"All deliverables were accepted during sprint review. This reference template showcases advanced TOC, pagination markers, watermark layers, and custom header/footer element rendering.",
|
|
143
|
+
watermark: {
|
|
144
|
+
enabled: true,
|
|
145
|
+
text: options.watermarkText
|
|
146
|
+
},
|
|
147
|
+
brand: {
|
|
148
|
+
accentColor: options.accentColor,
|
|
149
|
+
supportEmail: "support@dfactory.dev"
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const template = defineTemplate({
|
|
155
|
+
meta: {
|
|
156
|
+
id: "invoice-reference",
|
|
157
|
+
title: "Invoice Reference (Advanced)",
|
|
158
|
+
description:
|
|
159
|
+
"Rich multi-file reference template with first-class TOC/header/footer/watermark/pagination elements.",
|
|
160
|
+
framework: "react",
|
|
161
|
+
version: "1.0.0",
|
|
162
|
+
tags: ["billing", "reference", "advanced"]
|
|
163
|
+
},
|
|
164
|
+
schema: invoiceReferenceSchema,
|
|
165
|
+
pdf: {
|
|
166
|
+
page: {
|
|
167
|
+
size: "A4",
|
|
168
|
+
marginsMm: { top: 16, right: 12, bottom: 16, left: 12 }
|
|
169
|
+
},
|
|
170
|
+
toc: {
|
|
171
|
+
enabled: true,
|
|
172
|
+
maxDepth: 3,
|
|
173
|
+
title: "Invoice Reference Contents"
|
|
174
|
+
},
|
|
175
|
+
pagination: {
|
|
176
|
+
mode: "css"
|
|
177
|
+
},
|
|
178
|
+
headerFooter: {
|
|
179
|
+
enabled: true
|
|
180
|
+
},
|
|
181
|
+
assets: {
|
|
182
|
+
maxAssetCount: 24,
|
|
183
|
+
maxAssetBytes: 1024 * 1024,
|
|
184
|
+
timeoutMs: 4000
|
|
185
|
+
},
|
|
186
|
+
metadata: {
|
|
187
|
+
title: "DFactory Invoice Reference Document",
|
|
188
|
+
author: "DFactory",
|
|
189
|
+
keywords: ["invoice", "reference", "pdf", "dfactory"]
|
|
190
|
+
},
|
|
191
|
+
watermark: {
|
|
192
|
+
text: "DRAFT",
|
|
193
|
+
opacity: 0.12,
|
|
194
|
+
fontSize: 44
|
|
195
|
+
}
|
|
196
|
+
},
|
|
197
|
+
pdfElements: {
|
|
198
|
+
toc: {
|
|
199
|
+
render(context) {
|
|
200
|
+
return (
|
|
201
|
+
<InvoiceReferenceToc
|
|
202
|
+
title={`${context.template.title} TOC`}
|
|
203
|
+
headings={context.headings}
|
|
204
|
+
/>
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
},
|
|
208
|
+
header: {
|
|
209
|
+
render(context) {
|
|
210
|
+
return (
|
|
211
|
+
<InvoiceReferenceHeader
|
|
212
|
+
title={context.template.title}
|
|
213
|
+
invoiceNumber={
|
|
214
|
+
(context.payload as InvoiceReferencePayload).invoiceNumber
|
|
215
|
+
}
|
|
216
|
+
generatedAtToken={context.tokens.date}
|
|
217
|
+
/>
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
},
|
|
221
|
+
footer: {
|
|
222
|
+
render(context) {
|
|
223
|
+
return (
|
|
224
|
+
<InvoiceReferenceFooter
|
|
225
|
+
templateId={context.templateId}
|
|
226
|
+
pageNumberToken={context.tokens.pageNumber}
|
|
227
|
+
totalPagesToken={context.tokens.totalPages}
|
|
228
|
+
supportEmail={
|
|
229
|
+
(context.payload as InvoiceReferencePayload).brand?.supportEmail
|
|
230
|
+
}
|
|
231
|
+
/>
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
},
|
|
235
|
+
watermark: {
|
|
236
|
+
render(context) {
|
|
237
|
+
const watermark = (context.payload as InvoiceReferencePayload).watermark;
|
|
238
|
+
if (!watermark?.enabled) {
|
|
239
|
+
return "";
|
|
240
|
+
}
|
|
241
|
+
return <InvoiceReferenceWatermark text={watermark.text} />;
|
|
242
|
+
}
|
|
243
|
+
},
|
|
244
|
+
pagination: {
|
|
245
|
+
render(context) {
|
|
246
|
+
return (
|
|
247
|
+
<InvoiceReferencePagination
|
|
248
|
+
pageNumberToken={context.tokens.pageNumber}
|
|
249
|
+
totalPagesToken={context.tokens.totalPages}
|
|
250
|
+
/>
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
},
|
|
255
|
+
examples: [
|
|
256
|
+
{
|
|
257
|
+
name: "short",
|
|
258
|
+
description: "Single-page styled invoice with minimal line items.",
|
|
259
|
+
payload: createPayload({
|
|
260
|
+
invoiceNumber: "INV-REF-1001",
|
|
261
|
+
lineCount: 4,
|
|
262
|
+
watermarkText: "INTERNAL",
|
|
263
|
+
accentColor: "#4f46e5"
|
|
264
|
+
})
|
|
265
|
+
},
|
|
266
|
+
{
|
|
267
|
+
name: "multi-page",
|
|
268
|
+
description: "Long-form invoice designed to exercise TOC and pagination.",
|
|
269
|
+
payload: createPayload({
|
|
270
|
+
invoiceNumber: "INV-REF-1002",
|
|
271
|
+
lineCount: 24,
|
|
272
|
+
watermarkText: "REVIEW",
|
|
273
|
+
accentColor: "#3730a3"
|
|
274
|
+
})
|
|
275
|
+
},
|
|
276
|
+
{
|
|
277
|
+
name: "branded",
|
|
278
|
+
description: "Branded variation with strong accent and support metadata.",
|
|
279
|
+
payload: createPayload({
|
|
280
|
+
invoiceNumber: "INV-REF-1003",
|
|
281
|
+
lineCount: 12,
|
|
282
|
+
watermarkText: "BRANDED",
|
|
283
|
+
accentColor: "#1d4ed8"
|
|
284
|
+
})
|
|
285
|
+
},
|
|
286
|
+
{
|
|
287
|
+
name: "watermark-heavy",
|
|
288
|
+
description: "Watermark-forward profile to validate overlay readability.",
|
|
289
|
+
payload: createPayload({
|
|
290
|
+
invoiceNumber: "INV-REF-1004",
|
|
291
|
+
lineCount: 18,
|
|
292
|
+
watermarkText: "CONFIDENTIAL",
|
|
293
|
+
accentColor: "#4338ca"
|
|
294
|
+
})
|
|
295
|
+
}
|
|
296
|
+
],
|
|
297
|
+
render(payload) {
|
|
298
|
+
return <InvoiceReferenceDocument payload={payload} />;
|
|
299
|
+
}
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
export const meta = template.meta;
|
|
303
|
+
export const schema = template.schema;
|
|
304
|
+
export const pdf = template.pdf;
|
|
305
|
+
export const pdfElements = template.pdfElements;
|
|
306
|
+
export const examples = template.examples;
|
|
307
|
+
export const render = template.render;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed } from "vue";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
AvoidBreakInside,
|
|
6
|
+
Document,
|
|
7
|
+
Heading,
|
|
8
|
+
KeepWithNext,
|
|
9
|
+
NumericCell,
|
|
10
|
+
Page,
|
|
11
|
+
PageBreakBefore,
|
|
12
|
+
Paragraph,
|
|
13
|
+
Section,
|
|
14
|
+
Table,
|
|
15
|
+
TableBody,
|
|
16
|
+
TableCell,
|
|
17
|
+
TableHead,
|
|
18
|
+
TableHeaderCell,
|
|
19
|
+
TableRow
|
|
20
|
+
} from "@dfactory/pdf-primitives-vue";
|
|
21
|
+
|
|
22
|
+
interface InvoiceItem {
|
|
23
|
+
name: string;
|
|
24
|
+
qty: number;
|
|
25
|
+
price: number;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
interface InvoicePayload {
|
|
29
|
+
invoiceNumber: string;
|
|
30
|
+
customerName: string;
|
|
31
|
+
issuedAt: string;
|
|
32
|
+
items: InvoiceItem[];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const props = defineProps<{
|
|
36
|
+
payload: InvoicePayload;
|
|
37
|
+
}>();
|
|
38
|
+
|
|
39
|
+
const total = computed(() => {
|
|
40
|
+
return props.payload.items.reduce((sum, item) => sum + item.qty * item.price, 0);
|
|
41
|
+
});
|
|
42
|
+
</script>
|
|
43
|
+
|
|
44
|
+
<template>
|
|
45
|
+
<Document>
|
|
46
|
+
<Page>
|
|
47
|
+
<Section :style="{ padding: '24px' }">
|
|
48
|
+
<KeepWithNext>
|
|
49
|
+
<Heading as="h1" :style="{ marginBottom: '8px', color: 'var(--df-pdf-color-accent)' }">
|
|
50
|
+
Invoice {{ props.payload.invoiceNumber }}
|
|
51
|
+
</Heading>
|
|
52
|
+
<Paragraph :style="{ color: 'var(--df-pdf-color-muted)', marginTop: 0 }">
|
|
53
|
+
Customer: {{ props.payload.customerName }}
|
|
54
|
+
</Paragraph>
|
|
55
|
+
<Paragraph :style="{ color: 'var(--df-pdf-color-muted)', marginTop: '4px' }">
|
|
56
|
+
Issued: {{ props.payload.issuedAt }}
|
|
57
|
+
</Paragraph>
|
|
58
|
+
</KeepWithNext>
|
|
59
|
+
|
|
60
|
+
<AvoidBreakInside>
|
|
61
|
+
<Heading as="h2" :style="{ marginTop: '20px', marginBottom: '8px', fontSize: '16px' }">
|
|
62
|
+
Line Items
|
|
63
|
+
</Heading>
|
|
64
|
+
<Table :style="{ marginTop: '10px' }">
|
|
65
|
+
<TableHead>
|
|
66
|
+
<TableRow>
|
|
67
|
+
<TableHeaderCell :style="{ textAlign: 'left' }">Item</TableHeaderCell>
|
|
68
|
+
<TableHeaderCell :style="{ textAlign: 'right' }">Qty</TableHeaderCell>
|
|
69
|
+
<TableHeaderCell :style="{ textAlign: 'right' }">Price</TableHeaderCell>
|
|
70
|
+
</TableRow>
|
|
71
|
+
</TableHead>
|
|
72
|
+
<TableBody>
|
|
73
|
+
<TableRow v-for="item in props.payload.items" :key="item.name">
|
|
74
|
+
<TableCell>{{ item.name }}</TableCell>
|
|
75
|
+
<NumericCell>{{ item.qty }}</NumericCell>
|
|
76
|
+
<NumericCell>${{ (item.price * item.qty).toFixed(2) }}</NumericCell>
|
|
77
|
+
</TableRow>
|
|
78
|
+
</TableBody>
|
|
79
|
+
</Table>
|
|
80
|
+
|
|
81
|
+
<Paragraph :style="{ textAlign: 'right', marginTop: '16px', fontWeight: 700 }">
|
|
82
|
+
Total: ${{ total.toFixed(2) }}
|
|
83
|
+
</Paragraph>
|
|
84
|
+
</AvoidBreakInside>
|
|
85
|
+
|
|
86
|
+
<PageBreakBefore>
|
|
87
|
+
<Heading as="h2" :style="{ marginTop: '24px', fontSize: '16px' }">Payment Terms</Heading>
|
|
88
|
+
<Paragraph :style="{ color: 'var(--df-pdf-color-text)', lineHeight: 1.6 }">
|
|
89
|
+
Payment due within 14 calendar days from invoice date. Late payments may incur additional
|
|
90
|
+
charges.
|
|
91
|
+
</Paragraph>
|
|
92
|
+
</PageBreakBefore>
|
|
93
|
+
</Section>
|
|
94
|
+
</Page>
|
|
95
|
+
</Document>
|
|
96
|
+
</template>
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { h } from "vue";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
|
|
4
|
+
import { defineTemplate } from "@dfactory/template-kit";
|
|
5
|
+
|
|
6
|
+
import InvoiceTemplate from "./InvoiceTemplate.vue";
|
|
7
|
+
|
|
8
|
+
const template = defineTemplate({
|
|
9
|
+
meta: {
|
|
10
|
+
id: "invoice",
|
|
11
|
+
title: "Invoice",
|
|
12
|
+
description: "Default starter invoice template",
|
|
13
|
+
framework: "vue",
|
|
14
|
+
version: "1.0.0",
|
|
15
|
+
tags: ["billing", "starter"]
|
|
16
|
+
},
|
|
17
|
+
schema: z.object({
|
|
18
|
+
invoiceNumber: z.string(),
|
|
19
|
+
customerName: z.string(),
|
|
20
|
+
issuedAt: z.string(),
|
|
21
|
+
items: z.array(
|
|
22
|
+
z.object({
|
|
23
|
+
name: z.string(),
|
|
24
|
+
qty: z.number(),
|
|
25
|
+
price: z.number()
|
|
26
|
+
})
|
|
27
|
+
)
|
|
28
|
+
}),
|
|
29
|
+
pdf: {
|
|
30
|
+
page: {
|
|
31
|
+
size: "A4",
|
|
32
|
+
marginsMm: { top: 12, right: 12, bottom: 14, left: 12 }
|
|
33
|
+
},
|
|
34
|
+
toc: {
|
|
35
|
+
enabled: true,
|
|
36
|
+
maxDepth: 2,
|
|
37
|
+
title: "Invoice Overview"
|
|
38
|
+
},
|
|
39
|
+
pagination: {
|
|
40
|
+
mode: "css"
|
|
41
|
+
},
|
|
42
|
+
headerFooter: {
|
|
43
|
+
enabled: true,
|
|
44
|
+
footerTemplate:
|
|
45
|
+
"<div style=\"width:100%;font-size:9px;padding:0 12px;color:#64748b;display:flex;justify-content:space-between;\"><span>{{title}}</span><span>{{pageXofY}}</span></div>"
|
|
46
|
+
},
|
|
47
|
+
metadata: {
|
|
48
|
+
title: "Invoice Document",
|
|
49
|
+
keywords: ["invoice", "billing", "starter"]
|
|
50
|
+
},
|
|
51
|
+
theme: {
|
|
52
|
+
font: {
|
|
53
|
+
family: "Inter, 'Segoe UI', sans-serif"
|
|
54
|
+
},
|
|
55
|
+
color: {
|
|
56
|
+
accent: "#1d4ed8"
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
examples: [
|
|
61
|
+
{
|
|
62
|
+
name: "default",
|
|
63
|
+
payload: {
|
|
64
|
+
invoiceNumber: "INV-1001",
|
|
65
|
+
customerName: "Northwind Trading",
|
|
66
|
+
issuedAt: "2026-03-27",
|
|
67
|
+
items: [
|
|
68
|
+
{ name: "Consulting", qty: 2, price: 420 },
|
|
69
|
+
{ name: "Support", qty: 1, price: 180 }
|
|
70
|
+
]
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
],
|
|
74
|
+
render(payload) {
|
|
75
|
+
return h(InvoiceTemplate, {
|
|
76
|
+
payload
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
export const meta = template.meta;
|
|
82
|
+
export const schema = template.schema;
|
|
83
|
+
export const pdf = template.pdf;
|
|
84
|
+
export const examples = template.examples;
|
|
85
|
+
export const render = template.render;
|