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.
Files changed (24) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +37 -0
  3. package/package.json +50 -0
  4. package/templates/base/dfactory.config.ts +34 -0
  5. package/templates/react/src/templates/invoice/template.tsx +154 -0
  6. package/templates/react/src/templates/invoice-reference/components/InvoiceReferenceDocument.tsx +309 -0
  7. package/templates/react/src/templates/invoice-reference/components/InvoiceReferenceFooter.tsx +26 -0
  8. package/templates/react/src/templates/invoice-reference/components/InvoiceReferenceHeader.tsx +23 -0
  9. package/templates/react/src/templates/invoice-reference/components/InvoiceReferencePagination.tsx +24 -0
  10. package/templates/react/src/templates/invoice-reference/components/InvoiceReferenceToc.tsx +30 -0
  11. package/templates/react/src/templates/invoice-reference/components/InvoiceReferenceWatermark.tsx +16 -0
  12. package/templates/react/src/templates/invoice-reference/components/types.ts +65 -0
  13. package/templates/react/src/templates/invoice-reference/template.tsx +307 -0
  14. package/templates/vue/src/templates/invoice/InvoiceTemplate.vue +96 -0
  15. package/templates/vue/src/templates/invoice/template.ts +85 -0
  16. package/templates/vue/src/templates/invoice-reference/components/InvoiceReferenceDocument.vue +241 -0
  17. package/templates/vue/src/templates/invoice-reference/components/InvoiceReferenceFooter.vue +26 -0
  18. package/templates/vue/src/templates/invoice-reference/components/InvoiceReferenceHeader.vue +25 -0
  19. package/templates/vue/src/templates/invoice-reference/components/InvoiceReferencePagination.vue +24 -0
  20. package/templates/vue/src/templates/invoice-reference/components/InvoiceReferenceToc.vue +34 -0
  21. package/templates/vue/src/templates/invoice-reference/components/InvoiceReferenceWatermark.vue +20 -0
  22. package/templates/vue/src/templates/invoice-reference/components/types.ts +65 -0
  23. package/templates/vue/src/templates/invoice-reference/template.ts +300 -0
  24. package/templates/vue/src/vue-shims.d.ts +6 -0
@@ -0,0 +1,241 @@
1
+ <script setup lang="ts">
2
+ import { computed } from "vue";
3
+
4
+ import {
5
+ AvoidBreakInside,
6
+ Divider,
7
+ Document,
8
+ KeepWithNext,
9
+ Page,
10
+ PageBreakBefore
11
+ } from "@dfactory/pdf-primitives-vue";
12
+
13
+ import type { InvoiceReferencePayload } from "./types";
14
+
15
+ const props = defineProps<{
16
+ payload: InvoiceReferencePayload;
17
+ }>();
18
+
19
+ function formatMoney(value: number, currency: string): string {
20
+ return new Intl.NumberFormat("en-US", {
21
+ style: "currency",
22
+ currency
23
+ }).format(value);
24
+ }
25
+
26
+ function lineTotal(options: {
27
+ qty: number;
28
+ unitPrice: number;
29
+ discount?: number;
30
+ taxRate?: number;
31
+ }): number {
32
+ const gross = options.qty * options.unitPrice;
33
+ const discount = options.discount ?? 0;
34
+ const discounted = gross - gross * (discount / 100);
35
+ const taxRate = options.taxRate ?? 0;
36
+ return discounted + discounted * (taxRate / 100);
37
+ }
38
+
39
+ const accent = computed(() => props.payload.brand?.accentColor ?? "#4f46e5");
40
+ const subtotal = computed(() => {
41
+ return props.payload.items.reduce((sum, item) => sum + item.qty * item.unitPrice, 0);
42
+ });
43
+ const total = computed(() => {
44
+ return props.payload.items.reduce((sum, item) => {
45
+ return sum + lineTotal(item);
46
+ }, 0);
47
+ });
48
+ </script>
49
+
50
+ <template>
51
+ <Document>
52
+ <Page>
53
+ <KeepWithNext
54
+ as="header"
55
+ style="border-bottom: 2px solid #e2e8f0; padding-bottom: 16px; margin-bottom: 18px"
56
+ >
57
+ <h1 :style="{ margin: 0, color: accent }">Invoice {{ props.payload.invoiceNumber }}</h1>
58
+ <p style="margin: 6px 0 0; color: #475569">
59
+ Issued {{ props.payload.issuedAt }} • Due {{ props.payload.dueAt }}
60
+ </p>
61
+ <p v-if="props.payload.purchaseOrder" style="margin: 4px 0 0; color: #64748b">
62
+ Purchase Order: {{ props.payload.purchaseOrder }}
63
+ </p>
64
+ </KeepWithNext>
65
+
66
+ <section style="display: grid; grid-template-columns: 1fr 1fr; gap: 18px; margin-bottom: 16px">
67
+ <AvoidBreakInside as="article">
68
+ <h2 style="font-size: 16px; margin-bottom: 8px">Bill From</h2>
69
+ <p style="margin: 0; font-weight: 600">{{ props.payload.company.name }}</p>
70
+ <p style="margin: 4px 0 0; color: #475569; white-space: pre-line">
71
+ {{ props.payload.company.address }}
72
+ </p>
73
+ <p style="margin: 8px 0 0; color: #334155">
74
+ {{ props.payload.company.email }} • {{ props.payload.company.phone }}
75
+ </p>
76
+ <p style="margin: 4px 0 0; color: #64748b">
77
+ {{ props.payload.company.website }} • Tax ID {{ props.payload.company.taxId }}
78
+ </p>
79
+ </AvoidBreakInside>
80
+
81
+ <AvoidBreakInside as="article">
82
+ <h2 style="font-size: 16px; margin-bottom: 8px">Bill To</h2>
83
+ <p style="margin: 0; font-weight: 600">{{ props.payload.customer.name }}</p>
84
+ <p style="margin: 4px 0 0; color: #475569">
85
+ Contact: {{ props.payload.customer.contact }}
86
+ </p>
87
+ <p style="margin: 4px 0 0; color: #334155">{{ props.payload.customer.email }}</p>
88
+ <p style="margin: 4px 0 0; color: #64748b; white-space: pre-line">
89
+ {{ props.payload.customer.address }}
90
+ </p>
91
+ </AvoidBreakInside>
92
+ </section>
93
+
94
+ <AvoidBreakInside as="section">
95
+ <h2 style="font-size: 16px; margin: 20px 0 8px">Services & Deliverables</h2>
96
+ <table
97
+ style="
98
+ width: 100%;
99
+ border-collapse: collapse;
100
+ border: 1px solid #e2e8f0;
101
+ border-radius: 8px;
102
+ overflow: hidden;
103
+ "
104
+ >
105
+ <thead style="background-color: #eef2ff">
106
+ <tr>
107
+ <th style="text-align: left; padding: 10px 12px">Description</th>
108
+ <th style="text-align: right; padding: 10px 12px">Qty</th>
109
+ <th style="text-align: right; padding: 10px 12px">Unit</th>
110
+ <th style="text-align: right; padding: 10px 12px">Discount</th>
111
+ <th style="text-align: right; padding: 10px 12px">Tax</th>
112
+ <th style="text-align: right; padding: 10px 12px">Line Total</th>
113
+ </tr>
114
+ </thead>
115
+ <tbody>
116
+ <tr v-for="item in props.payload.items" :key="item.id">
117
+ <td style="padding: 10px 12px; border-top: 1px solid #e2e8f0">
118
+ <p style="margin: 0; font-weight: 600">{{ item.name }}</p>
119
+ <p v-if="item.description" style="margin: 4px 0 0; color: #64748b">
120
+ {{ item.description }}
121
+ </p>
122
+ </td>
123
+ <td style="text-align: right; padding: 10px 12px; border-top: 1px solid #e2e8f0">
124
+ {{ item.qty }}
125
+ </td>
126
+ <td style="text-align: right; padding: 10px 12px; border-top: 1px solid #e2e8f0">
127
+ {{ formatMoney(item.unitPrice, props.payload.currency) }}
128
+ </td>
129
+ <td style="text-align: right; padding: 10px 12px; border-top: 1px solid #e2e8f0">
130
+ {{ (item.discount ?? 0).toFixed(1) }}%
131
+ </td>
132
+ <td style="text-align: right; padding: 10px 12px; border-top: 1px solid #e2e8f0">
133
+ {{ (item.taxRate ?? 0).toFixed(1) }}%
134
+ </td>
135
+ <td
136
+ style="
137
+ text-align: right;
138
+ padding: 10px 12px;
139
+ border-top: 1px solid #e2e8f0;
140
+ font-weight: 600;
141
+ "
142
+ >
143
+ {{ formatMoney(lineTotal(item), props.payload.currency) }}
144
+ </td>
145
+ </tr>
146
+ </tbody>
147
+ </table>
148
+ </AvoidBreakInside>
149
+
150
+ <section style="margin-top: 16px; display: grid; grid-template-columns: 1fr 280px; gap: 24px">
151
+ <article>
152
+ <h2 style="font-size: 16px; margin-bottom: 8px">Summary Notes</h2>
153
+ <p style="color: #475569; margin-top: 0">
154
+ The sections below intentionally include long-form text to provide a strong multi-page
155
+ reference for TOC, pagination, and marker helpers.
156
+ </p>
157
+ <template v-for="(section, index) in props.payload.sections" :key="`${section.title}-${index}`">
158
+ <PageBreakBefore
159
+ v-if="index === 2"
160
+ as="section"
161
+ style="
162
+ margin-top: 14px;
163
+ padding: 12px;
164
+ border: 1px solid #e2e8f0;
165
+ border-radius: 8px;
166
+ background: #f8fafc;
167
+ "
168
+ >
169
+ <h3 style="margin-top: 0; margin-bottom: 8px; color: #1e293b">{{ section.title }}</h3>
170
+ <p style="margin: 0; color: #334155">{{ section.description }}</p>
171
+ <p v-if="section.notes" style="margin: 8px 0 0; color: #64748b">{{ section.notes }}</p>
172
+ </PageBreakBefore>
173
+ <section
174
+ v-else
175
+ style="
176
+ margin-top: 14px;
177
+ padding: 12px;
178
+ border: 1px solid #e2e8f0;
179
+ border-radius: 8px;
180
+ background: #f8fafc;
181
+ "
182
+ >
183
+ <h3 style="margin-top: 0; margin-bottom: 8px; color: #1e293b">{{ section.title }}</h3>
184
+ <p style="margin: 0; color: #334155">{{ section.description }}</p>
185
+ <p v-if="section.notes" style="margin: 8px 0 0; color: #64748b">{{ section.notes }}</p>
186
+ </section>
187
+ </template>
188
+ </article>
189
+
190
+ <KeepWithNext
191
+ as="aside"
192
+ style="
193
+ border: 1px solid #dbeafe;
194
+ background: #eff6ff;
195
+ border-radius: 10px;
196
+ padding: 14px;
197
+ "
198
+ >
199
+ <h2 style="margin: 0; font-size: 15px">Totals</h2>
200
+ <p style="margin: 10px 0 0; color: #475569">
201
+ Subtotal: <strong>{{ formatMoney(subtotal, props.payload.currency) }}</strong>
202
+ </p>
203
+ <p style="margin: 6px 0 0; color: #475569">
204
+ Grand Total: <strong>{{ formatMoney(total, props.payload.currency) }}</strong>
205
+ </p>
206
+ <Divider
207
+ :style="{
208
+ borderTopColor: 'rgba(71, 85, 105, 0.2)',
209
+ marginTop: '10px',
210
+ marginBottom: '8px'
211
+ }"
212
+ />
213
+ <p style="margin: 10px 0 0; color: #1e293b; font-weight: 600">
214
+ Payment via {{ props.payload.payment.bankName }}
215
+ </p>
216
+ <p style="margin: 4px 0 0; color: #475569">IBAN: {{ props.payload.payment.iban }}</p>
217
+ <p style="margin: 4px 0 0; color: #475569">SWIFT: {{ props.payload.payment.swift }}</p>
218
+ <p v-if="props.payload.payment.instructions" style="margin: 8px 0 0; color: #64748b">
219
+ {{ props.payload.payment.instructions }}
220
+ </p>
221
+ </KeepWithNext>
222
+ </section>
223
+
224
+ <PageBreakBefore
225
+ v-if="props.payload.notes"
226
+ as="section"
227
+ style="
228
+ margin-top: 20px;
229
+ padding: 16px;
230
+ border: 1px dashed #cbd5e1;
231
+ border-radius: 10px;
232
+ "
233
+ >
234
+ <h2 style="margin-top: 0; font-size: 16px">Additional Terms</h2>
235
+ <p style="margin-bottom: 0; color: #334155; white-space: pre-line">
236
+ {{ props.payload.notes }}
237
+ </p>
238
+ </PageBreakBefore>
239
+ </Page>
240
+ </Document>
241
+ </template>
@@ -0,0 +1,26 @@
1
+ <script setup lang="ts">
2
+ defineProps<{
3
+ templateId: string;
4
+ pageNumberToken: string;
5
+ totalPagesToken: string;
6
+ supportEmail?: string;
7
+ }>();
8
+ </script>
9
+
10
+ <template>
11
+ <div
12
+ style="
13
+ width: 100%;
14
+ padding: 0 14px;
15
+ font-size: 9px;
16
+ color: #475569;
17
+ display: flex;
18
+ justify-content: space-between;
19
+ align-items: center;
20
+ "
21
+ >
22
+ <span>{{ supportEmail ?? "billing@dfactory.dev" }}</span>
23
+ <span>{{ templateId }}</span>
24
+ <span>{{ pageNumberToken }} / {{ totalPagesToken }}</span>
25
+ </div>
26
+ </template>
@@ -0,0 +1,25 @@
1
+ <script setup lang="ts">
2
+ defineProps<{
3
+ title: string;
4
+ invoiceNumber: string;
5
+ generatedAtToken: string;
6
+ }>();
7
+ </script>
8
+
9
+ <template>
10
+ <div
11
+ style="
12
+ width: 100%;
13
+ padding: 0 14px;
14
+ font-size: 9px;
15
+ color: #475569;
16
+ display: flex;
17
+ justify-content: space-between;
18
+ align-items: center;
19
+ "
20
+ >
21
+ <span>{{ title }}</span>
22
+ <span>{{ invoiceNumber }}</span>
23
+ <span>{{ generatedAtToken }}</span>
24
+ </div>
25
+ </template>
@@ -0,0 +1,24 @@
1
+ <script setup lang="ts">
2
+ import { AvoidBreakInside } from "@dfactory/pdf-primitives-vue";
3
+
4
+ defineProps<{
5
+ pageNumberToken: string;
6
+ totalPagesToken: string;
7
+ }>();
8
+ </script>
9
+
10
+ <template>
11
+ <AvoidBreakInside
12
+ style="
13
+ width: 100%;
14
+ padding: 4px 16px 8px;
15
+ font-size: 9px;
16
+ color: #64748b;
17
+ display: flex;
18
+ justify-content: space-between;
19
+ "
20
+ >
21
+ <span>DFactory Reference Pagination Layer</span>
22
+ <span>Page {{ pageNumberToken }} of {{ totalPagesToken }}</span>
23
+ </AvoidBreakInside>
24
+ </template>
@@ -0,0 +1,34 @@
1
+ <script setup lang="ts">
2
+ import type { TemplateTocHeading } from "@dfactory/core";
3
+
4
+ defineProps<{
5
+ title: string;
6
+ headings: TemplateTocHeading[];
7
+ }>();
8
+ </script>
9
+
10
+ <template>
11
+ <nav
12
+ :aria-label="title"
13
+ style="
14
+ margin-bottom: 24px;
15
+ padding: 14px 16px;
16
+ border-radius: 10px;
17
+ border: 1px solid rgba(79, 70, 229, 0.18);
18
+ background: rgba(79, 70, 229, 0.04);
19
+ "
20
+ >
21
+ <h2 style="margin: 0; font-size: 16px; color: #312e81">{{ title }}</h2>
22
+ <ol style="margin: 10px 0 0; padding-left: 18px">
23
+ <li
24
+ v-for="heading in headings"
25
+ :key="heading.id"
26
+ :style="{ marginTop: '4px', marginLeft: `${(heading.level - 1) * 10}px` }"
27
+ >
28
+ <a :href="`#${heading.id}`" style="color: #1e293b; text-decoration: none">
29
+ {{ heading.text }}
30
+ </a>
31
+ </li>
32
+ </ol>
33
+ </nav>
34
+ </template>
@@ -0,0 +1,20 @@
1
+ <script setup lang="ts">
2
+ defineProps<{
3
+ text: string;
4
+ }>();
5
+ </script>
6
+
7
+ <template>
8
+ <div
9
+ style="
10
+ transform: rotate(-24deg);
11
+ font-size: 52px;
12
+ font-weight: 700;
13
+ letter-spacing: 0.08em;
14
+ color: rgba(79, 70, 229, 0.14);
15
+ text-transform: uppercase;
16
+ "
17
+ >
18
+ {{ text }}
19
+ </div>
20
+ </template>
@@ -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
+ }