pdfn 0.0.1 → 0.2.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 +124 -0
- package/dist/cli.js +2217 -0
- package/dist/cli.js.map +1 -0
- package/dist/server/index.d.ts +186 -0
- package/dist/server/index.js +634 -0
- package/dist/server/index.js.map +1 -0
- package/package.json +61 -13
- package/templates/inline/contract.tsx +217 -0
- package/templates/inline/invoice.tsx +188 -0
- package/templates/inline/letter.tsx +129 -0
- package/templates/inline/poster.tsx +128 -0
- package/templates/inline/ticket.tsx +138 -0
- package/templates/tailwind/contract.tsx +220 -0
- package/templates/tailwind/invoice.tsx +191 -0
- package/templates/tailwind/letter.tsx +132 -0
- package/templates/tailwind/poster.tsx +124 -0
- package/templates/tailwind/ticket.tsx +140 -0
- package/cli.js +0 -14
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { Document, Page } from "@pdfn/react";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Business Letter template - US Letter size (inline styles)
|
|
5
|
+
*
|
|
6
|
+
* Demonstrates:
|
|
7
|
+
* - Professional letterhead
|
|
8
|
+
* - Proper business letter format
|
|
9
|
+
* - Single page layout
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
interface LetterProps {
|
|
13
|
+
sender?: {
|
|
14
|
+
name: string;
|
|
15
|
+
title?: string;
|
|
16
|
+
company: string;
|
|
17
|
+
address: string;
|
|
18
|
+
city: string;
|
|
19
|
+
email: string;
|
|
20
|
+
phone: string;
|
|
21
|
+
};
|
|
22
|
+
recipient?: {
|
|
23
|
+
name: string;
|
|
24
|
+
title?: string;
|
|
25
|
+
company: string;
|
|
26
|
+
address: string;
|
|
27
|
+
city: string;
|
|
28
|
+
};
|
|
29
|
+
date?: string;
|
|
30
|
+
subject?: string;
|
|
31
|
+
body?: string[];
|
|
32
|
+
closing?: string;
|
|
33
|
+
signature?: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export default function Letter({
|
|
37
|
+
sender = {
|
|
38
|
+
name: "Alex Chen",
|
|
39
|
+
title: "Head of Partnerships",
|
|
40
|
+
company: "Your Company",
|
|
41
|
+
address: "123 Business St, Suite 100",
|
|
42
|
+
city: "San Francisco, CA 94102",
|
|
43
|
+
email: "alex@yourcompany.com",
|
|
44
|
+
phone: "+1 (555) 123-4567",
|
|
45
|
+
},
|
|
46
|
+
recipient = {
|
|
47
|
+
name: "Sarah Johnson",
|
|
48
|
+
title: "Chief Technology Officer",
|
|
49
|
+
company: "Acme Corporation",
|
|
50
|
+
address: "456 Enterprise Blvd, Suite 100",
|
|
51
|
+
city: "Austin, TX 78701",
|
|
52
|
+
},
|
|
53
|
+
date = "January 15, 2025",
|
|
54
|
+
subject = "Partnership Proposal",
|
|
55
|
+
body = [
|
|
56
|
+
"I hope this letter finds you well. Following our conversation last month, I wanted to formally present our partnership proposal for your consideration.",
|
|
57
|
+
"Our solution has helped over 500 companies streamline their workflows, reducing development time by an average of 60%. We believe our platform would be an excellent fit for your needs.",
|
|
58
|
+
"I would welcome the opportunity to schedule a technical demo with your team. Please let me know if you would be available for a call next week.",
|
|
59
|
+
],
|
|
60
|
+
closing = "Best regards",
|
|
61
|
+
signature = "Alex Chen",
|
|
62
|
+
}: LetterProps) {
|
|
63
|
+
return (
|
|
64
|
+
<Document title={`Letter - ${subject}`}>
|
|
65
|
+
<Page size="Letter" margin="1in">
|
|
66
|
+
{/* Letterhead */}
|
|
67
|
+
<div style={{ marginBottom: "24px", paddingBottom: "12px", borderBottom: "2px solid #1f2937" }}>
|
|
68
|
+
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-start" }}>
|
|
69
|
+
<div>
|
|
70
|
+
<div style={{ fontSize: "20px", fontWeight: "bold", color: "#111827" }}>{sender.company}</div>
|
|
71
|
+
<div style={{ fontSize: "10px", color: "#6b7280", marginTop: "4px" }}>
|
|
72
|
+
{sender.address} • {sender.city}
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
<div style={{ textAlign: "right", fontSize: "10px", color: "#6b7280" }}>
|
|
76
|
+
<div>{sender.email}</div>
|
|
77
|
+
<div>{sender.phone}</div>
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
</div>
|
|
81
|
+
|
|
82
|
+
{/* Date */}
|
|
83
|
+
<div style={{ fontSize: "14px", color: "#374151", marginBottom: "24px" }}>{date}</div>
|
|
84
|
+
|
|
85
|
+
{/* Recipient Info */}
|
|
86
|
+
<div style={{ marginBottom: "24px" }}>
|
|
87
|
+
<div style={{ fontSize: "14px", fontWeight: "600", color: "#111827" }}>{recipient.name}</div>
|
|
88
|
+
{recipient.title && (
|
|
89
|
+
<div style={{ fontSize: "14px", color: "#4b5563" }}>{recipient.title}</div>
|
|
90
|
+
)}
|
|
91
|
+
<div style={{ fontSize: "14px", color: "#4b5563" }}>{recipient.company}</div>
|
|
92
|
+
<div style={{ fontSize: "14px", color: "#6b7280" }}>
|
|
93
|
+
{recipient.address}, {recipient.city}
|
|
94
|
+
</div>
|
|
95
|
+
</div>
|
|
96
|
+
|
|
97
|
+
{/* Subject Line */}
|
|
98
|
+
<div style={{ marginBottom: "16px", padding: "6px 0 6px 12px", borderLeft: "4px solid #1f2937", backgroundColor: "#f9fafb" }}>
|
|
99
|
+
<span style={{ fontSize: "14px", fontWeight: "bold", color: "#111827", textTransform: "uppercase", letterSpacing: "0.05em" }}>Re: </span>
|
|
100
|
+
<span style={{ fontSize: "14px", fontWeight: "500", color: "#1f2937" }}>{subject}</span>
|
|
101
|
+
</div>
|
|
102
|
+
|
|
103
|
+
{/* Salutation */}
|
|
104
|
+
<div style={{ fontSize: "14px", color: "#111827", marginBottom: "12px" }}>Dear {recipient.name},</div>
|
|
105
|
+
|
|
106
|
+
{/* Body */}
|
|
107
|
+
<div style={{ marginBottom: "24px" }}>
|
|
108
|
+
{body.map((paragraph, i) => (
|
|
109
|
+
<p key={i} style={{ fontSize: "14px", color: "#374151", lineHeight: "1.625", marginBottom: "12px" }}>
|
|
110
|
+
{paragraph}
|
|
111
|
+
</p>
|
|
112
|
+
))}
|
|
113
|
+
</div>
|
|
114
|
+
|
|
115
|
+
{/* Closing & Signature */}
|
|
116
|
+
<div style={{ marginTop: "24px" }}>
|
|
117
|
+
<div style={{ fontSize: "14px", color: "#111827", marginBottom: "24px" }}>{closing},</div>
|
|
118
|
+
<div style={{ borderBottom: "1px solid #d1d5db", width: "160px", marginBottom: "4px" }}></div>
|
|
119
|
+
<div style={{ fontSize: "14px", fontWeight: "bold", color: "#111827" }}>{signature}</div>
|
|
120
|
+
{sender.title && (
|
|
121
|
+
<div style={{ fontSize: "10px", color: "#4b5563" }}>
|
|
122
|
+
{sender.title}, {sender.company}
|
|
123
|
+
</div>
|
|
124
|
+
)}
|
|
125
|
+
</div>
|
|
126
|
+
</Page>
|
|
127
|
+
</Document>
|
|
128
|
+
);
|
|
129
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { Document, Page } from "@pdfn/react";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Event Poster template - Tabloid size, Landscape (inline styles)
|
|
5
|
+
*
|
|
6
|
+
* Demonstrates:
|
|
7
|
+
* - Large format (Tabloid: 17" x 11")
|
|
8
|
+
* - Landscape orientation
|
|
9
|
+
* - Full-bleed design (margin: 0)
|
|
10
|
+
* - Bold typography and visual hierarchy
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
interface PosterProps {
|
|
14
|
+
headline?: string;
|
|
15
|
+
year?: string;
|
|
16
|
+
subheadline?: string;
|
|
17
|
+
date?: string;
|
|
18
|
+
venue?: string;
|
|
19
|
+
highlights?: string[];
|
|
20
|
+
cta?: string;
|
|
21
|
+
website?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export default function Poster({
|
|
25
|
+
headline = "Tech Conference",
|
|
26
|
+
year = "2025",
|
|
27
|
+
subheadline = "Innovation Meets Inspiration",
|
|
28
|
+
date = "March 15-17, 2025",
|
|
29
|
+
venue = "Convention Center, San Francisco",
|
|
30
|
+
highlights = ["50+ Speakers", "Workshops", "Networking"],
|
|
31
|
+
cta = "Get Tickets",
|
|
32
|
+
website = "techconf2025.com",
|
|
33
|
+
}: PosterProps) {
|
|
34
|
+
// Tabloid landscape dimensions
|
|
35
|
+
const pageHeight = "792pt"; // 11 inches
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<Document title={`Poster - ${headline}`}>
|
|
39
|
+
<Page size="Tabloid" orientation="landscape" margin="0">
|
|
40
|
+
{/* Full bleed dark background */}
|
|
41
|
+
<div
|
|
42
|
+
style={{
|
|
43
|
+
backgroundColor: "#111827",
|
|
44
|
+
color: "white",
|
|
45
|
+
padding: "48px",
|
|
46
|
+
display: "flex",
|
|
47
|
+
flexDirection: "column",
|
|
48
|
+
minHeight: pageHeight,
|
|
49
|
+
height: pageHeight,
|
|
50
|
+
}}
|
|
51
|
+
>
|
|
52
|
+
{/* Top Section: Logo and Accent */}
|
|
53
|
+
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-start", marginBottom: "16px" }}>
|
|
54
|
+
<div style={{ fontSize: "24px", fontWeight: "900", color: "white" }}>PDFN</div>
|
|
55
|
+
<div style={{ display: "flex", gap: "8px" }}>
|
|
56
|
+
<div style={{ height: "6px", width: "128px", backgroundColor: "#06b6d4", borderRadius: "9999px" }}></div>
|
|
57
|
+
<div style={{ height: "6px", width: "64px", backgroundColor: "rgba(6, 182, 212, 0.5)", borderRadius: "9999px" }}></div>
|
|
58
|
+
<div style={{ height: "6px", width: "32px", backgroundColor: "rgba(6, 182, 212, 0.25)", borderRadius: "9999px" }}></div>
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
|
|
62
|
+
{/* Main Content - Vertically Centered */}
|
|
63
|
+
<div style={{ flex: 1, display: "flex", flexDirection: "column", justifyContent: "center" }}>
|
|
64
|
+
{/* Headline */}
|
|
65
|
+
<h1 style={{ fontSize: "96px", fontWeight: "900", letterSpacing: "-0.025em", lineHeight: 1, marginBottom: "24px", margin: 0 }}>
|
|
66
|
+
{headline}
|
|
67
|
+
{year && <span style={{ color: "#22d3ee" }}> {year}</span>}
|
|
68
|
+
</h1>
|
|
69
|
+
|
|
70
|
+
{subheadline && (
|
|
71
|
+
<p style={{ fontSize: "30px", color: "#9ca3af", fontWeight: "300", maxWidth: "768px", marginBottom: "48px", marginTop: "24px" }}>
|
|
72
|
+
{subheadline}
|
|
73
|
+
</p>
|
|
74
|
+
)}
|
|
75
|
+
|
|
76
|
+
{/* Event Details */}
|
|
77
|
+
<div style={{ display: "flex", gap: "80px" }}>
|
|
78
|
+
<div>
|
|
79
|
+
<div style={{ fontSize: "14px", color: "#06b6d4", textTransform: "uppercase", letterSpacing: "0.2em", fontWeight: "bold", marginBottom: "8px" }}>
|
|
80
|
+
Date
|
|
81
|
+
</div>
|
|
82
|
+
<div style={{ fontSize: "36px", fontWeight: "bold" }}>{date}</div>
|
|
83
|
+
</div>
|
|
84
|
+
<div>
|
|
85
|
+
<div style={{ fontSize: "14px", color: "#06b6d4", textTransform: "uppercase", letterSpacing: "0.2em", fontWeight: "bold", marginBottom: "8px" }}>
|
|
86
|
+
Venue
|
|
87
|
+
</div>
|
|
88
|
+
<div style={{ fontSize: "36px", fontWeight: "bold" }}>{venue}</div>
|
|
89
|
+
</div>
|
|
90
|
+
</div>
|
|
91
|
+
</div>
|
|
92
|
+
|
|
93
|
+
{/* Bottom Section */}
|
|
94
|
+
<div style={{ display: "flex", alignItems: "flex-end", justifyContent: "space-between" }}>
|
|
95
|
+
{/* Highlights/Tags */}
|
|
96
|
+
<div style={{ display: "flex", gap: "16px" }}>
|
|
97
|
+
{highlights.map((highlight, i) => (
|
|
98
|
+
<div
|
|
99
|
+
key={i}
|
|
100
|
+
style={{ border: "2px solid #4b5563", color: "white", padding: "12px 24px", borderRadius: "9999px", fontSize: "16px", fontWeight: "600" }}
|
|
101
|
+
>
|
|
102
|
+
{highlight}
|
|
103
|
+
</div>
|
|
104
|
+
))}
|
|
105
|
+
</div>
|
|
106
|
+
|
|
107
|
+
{/* CTA */}
|
|
108
|
+
<div style={{ textAlign: "right" }}>
|
|
109
|
+
<div style={{ display: "inline-block", backgroundColor: "#06b6d4", color: "#111827", fontSize: "24px", fontWeight: "900", padding: "20px 40px", borderRadius: "12px" }}>
|
|
110
|
+
{cta}
|
|
111
|
+
</div>
|
|
112
|
+
<div style={{ fontSize: "16px", color: "#6b7280", marginTop: "16px", fontFamily: "monospace", letterSpacing: "0.05em" }}>
|
|
113
|
+
{website}
|
|
114
|
+
</div>
|
|
115
|
+
</div>
|
|
116
|
+
</div>
|
|
117
|
+
|
|
118
|
+
{/* Bottom Accent */}
|
|
119
|
+
<div style={{ display: "flex", justifyContent: "flex-end", gap: "8px", marginTop: "32px" }}>
|
|
120
|
+
<div style={{ height: "6px", width: "32px", backgroundColor: "rgba(6, 182, 212, 0.25)", borderRadius: "9999px" }}></div>
|
|
121
|
+
<div style={{ height: "6px", width: "64px", backgroundColor: "rgba(6, 182, 212, 0.5)", borderRadius: "9999px" }}></div>
|
|
122
|
+
<div style={{ height: "6px", width: "128px", backgroundColor: "#06b6d4", borderRadius: "9999px" }}></div>
|
|
123
|
+
</div>
|
|
124
|
+
</div>
|
|
125
|
+
</Page>
|
|
126
|
+
</Document>
|
|
127
|
+
);
|
|
128
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { Document, Page } from "@pdfn/react";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Event Ticket template - A5 size (inline styles)
|
|
5
|
+
*
|
|
6
|
+
* Demonstrates:
|
|
7
|
+
* - Compact page size (A5: 148mm x 210mm)
|
|
8
|
+
* - Creative visual design with banner
|
|
9
|
+
* - QR code placeholder
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
interface TicketProps {
|
|
13
|
+
event?: string;
|
|
14
|
+
year?: string;
|
|
15
|
+
tagline?: string;
|
|
16
|
+
date?: string;
|
|
17
|
+
time?: string;
|
|
18
|
+
venue?: string;
|
|
19
|
+
venueAddress?: string;
|
|
20
|
+
attendee?: string;
|
|
21
|
+
ticketType?: string;
|
|
22
|
+
ticketNumber?: string;
|
|
23
|
+
price?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export default function Ticket({
|
|
27
|
+
event = "Tech Conference",
|
|
28
|
+
year = "2025",
|
|
29
|
+
tagline = "Innovation Meets Inspiration",
|
|
30
|
+
date = "March 15, 2025",
|
|
31
|
+
time = "9:00 AM - 6:00 PM",
|
|
32
|
+
venue = "Convention Center",
|
|
33
|
+
venueAddress = "123 Main St, San Francisco, CA",
|
|
34
|
+
attendee = "John Smith",
|
|
35
|
+
ticketType = "VIP Access",
|
|
36
|
+
ticketNumber = "TC25-VIP-001234",
|
|
37
|
+
price = "$599.00",
|
|
38
|
+
}: TicketProps) {
|
|
39
|
+
return (
|
|
40
|
+
<Document title={`Ticket - ${event}`}>
|
|
41
|
+
<Page size="A5" margin="0">
|
|
42
|
+
{/* Header Banner */}
|
|
43
|
+
<div style={{ backgroundColor: "#111827", padding: "28px 24px", textAlign: "center" }}>
|
|
44
|
+
<div style={{ fontSize: "30px", fontWeight: "900", color: "white", letterSpacing: "0.05em" }}>
|
|
45
|
+
{event}
|
|
46
|
+
{year && <span style={{ color: "#22d3ee" }}> {year}</span>}
|
|
47
|
+
</div>
|
|
48
|
+
{tagline && (
|
|
49
|
+
<div style={{ fontSize: "14px", color: "#22d3ee", marginTop: "8px", fontWeight: "500", textTransform: "uppercase", letterSpacing: "0.2em" }}>
|
|
50
|
+
{tagline}
|
|
51
|
+
</div>
|
|
52
|
+
)}
|
|
53
|
+
</div>
|
|
54
|
+
|
|
55
|
+
{/* Ticket Content */}
|
|
56
|
+
<div style={{ padding: "20px 24px" }}>
|
|
57
|
+
{/* Ticket Type Badge */}
|
|
58
|
+
<div style={{ display: "flex", justifyContent: "center", marginTop: "-40px", marginBottom: "20px" }}>
|
|
59
|
+
<div style={{ backgroundColor: "#06b6d4", color: "#111827", fontSize: "10px", fontWeight: "bold", textTransform: "uppercase", padding: "8px 20px", borderRadius: "9999px", letterSpacing: "0.2em" }}>
|
|
60
|
+
{ticketType}
|
|
61
|
+
</div>
|
|
62
|
+
</div>
|
|
63
|
+
|
|
64
|
+
{/* Event Details Grid */}
|
|
65
|
+
<div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: "16px", marginBottom: "20px" }}>
|
|
66
|
+
<div style={{ backgroundColor: "#f9fafb", borderRadius: "8px", padding: "14px", textAlign: "center" }}>
|
|
67
|
+
<div style={{ fontSize: "10px", color: "#6b7280", textTransform: "uppercase", letterSpacing: "0.1em", fontWeight: "500" }}>
|
|
68
|
+
Date
|
|
69
|
+
</div>
|
|
70
|
+
<div style={{ fontSize: "16px", fontWeight: "bold", color: "#111827", marginTop: "4px" }}>{date}</div>
|
|
71
|
+
</div>
|
|
72
|
+
<div style={{ backgroundColor: "#f9fafb", borderRadius: "8px", padding: "14px", textAlign: "center" }}>
|
|
73
|
+
<div style={{ fontSize: "10px", color: "#6b7280", textTransform: "uppercase", letterSpacing: "0.1em", fontWeight: "500" }}>
|
|
74
|
+
Time
|
|
75
|
+
</div>
|
|
76
|
+
<div style={{ fontSize: "16px", fontWeight: "bold", color: "#111827", marginTop: "4px" }}>{time}</div>
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
|
|
80
|
+
{/* Venue */}
|
|
81
|
+
<div style={{ backgroundColor: "#f9fafb", borderRadius: "8px", padding: "14px", textAlign: "center", marginBottom: "20px" }}>
|
|
82
|
+
<div style={{ fontSize: "10px", color: "#6b7280", textTransform: "uppercase", letterSpacing: "0.1em", fontWeight: "500" }}>
|
|
83
|
+
Venue
|
|
84
|
+
</div>
|
|
85
|
+
<div style={{ fontSize: "16px", fontWeight: "bold", color: "#111827", marginTop: "4px" }}>{venue}</div>
|
|
86
|
+
<div style={{ fontSize: "10px", color: "#6b7280", marginTop: "4px" }}>{venueAddress}</div>
|
|
87
|
+
</div>
|
|
88
|
+
|
|
89
|
+
{/* Tear Line */}
|
|
90
|
+
<div style={{ display: "flex", alignItems: "center", gap: "8px", margin: "20px 0" }}>
|
|
91
|
+
<div style={{ flex: 1, borderTop: "2px dashed #d1d5db" }}></div>
|
|
92
|
+
<div style={{ fontSize: "14px", color: "#9ca3af" }}>✂</div>
|
|
93
|
+
<div style={{ flex: 1, borderTop: "2px dashed #d1d5db" }}></div>
|
|
94
|
+
</div>
|
|
95
|
+
|
|
96
|
+
{/* Attendee Section */}
|
|
97
|
+
<div style={{ textAlign: "center", marginBottom: "20px" }}>
|
|
98
|
+
<div style={{ fontSize: "10px", color: "#6b7280", textTransform: "uppercase", letterSpacing: "0.1em", fontWeight: "500", marginBottom: "4px" }}>
|
|
99
|
+
Admit One
|
|
100
|
+
</div>
|
|
101
|
+
<div style={{ fontSize: "36px", fontWeight: "900", color: "#111827" }}>{attendee}</div>
|
|
102
|
+
</div>
|
|
103
|
+
|
|
104
|
+
{/* QR Code Area */}
|
|
105
|
+
<div style={{ display: "flex", justifyContent: "center", marginBottom: "20px" }}>
|
|
106
|
+
<div style={{ width: "144px", height: "144px", backgroundColor: "white", border: "2px solid #111827", borderRadius: "12px", padding: "8px", display: "flex", alignItems: "center", justifyContent: "center" }}>
|
|
107
|
+
{/* Simulated QR pattern */}
|
|
108
|
+
<div style={{ display: "grid", gridTemplateColumns: "repeat(5, 1fr)", gap: "4px", width: "100%", height: "100%" }}>
|
|
109
|
+
{[...Array(25)].map((_, i) => (
|
|
110
|
+
<div
|
|
111
|
+
key={i}
|
|
112
|
+
style={{
|
|
113
|
+
borderRadius: "2px",
|
|
114
|
+
backgroundColor: [0, 1, 2, 4, 5, 6, 10, 12, 14, 18, 19, 20, 22, 23, 24].includes(i)
|
|
115
|
+
? "#111827"
|
|
116
|
+
: "#f3f4f6"
|
|
117
|
+
}}
|
|
118
|
+
/>
|
|
119
|
+
))}
|
|
120
|
+
</div>
|
|
121
|
+
</div>
|
|
122
|
+
</div>
|
|
123
|
+
|
|
124
|
+
{/* Footer */}
|
|
125
|
+
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", fontSize: "14px", borderTop: "2px solid #111827", paddingTop: "12px" }}>
|
|
126
|
+
<div style={{ fontFamily: "monospace", fontSize: "10px", color: "#4b5563" }}>{ticketNumber}</div>
|
|
127
|
+
<div style={{ fontWeight: "bold", fontSize: "18px", color: "#111827" }}>{price}</div>
|
|
128
|
+
</div>
|
|
129
|
+
|
|
130
|
+
{/* Terms */}
|
|
131
|
+
<p style={{ textAlign: "center", fontSize: "10px", color: "#9ca3af", marginTop: "12px" }}>
|
|
132
|
+
Non-transferable • Non-refundable • Present at entry
|
|
133
|
+
</p>
|
|
134
|
+
</div>
|
|
135
|
+
</Page>
|
|
136
|
+
</Document>
|
|
137
|
+
);
|
|
138
|
+
}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { Document, Page, PageNumber, TotalPages, AvoidBreak } from "@pdfn/react";
|
|
2
|
+
import { Tailwind } from "@pdfn/tailwind";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Service Agreement Contract template - Legal size (taller page)
|
|
6
|
+
*
|
|
7
|
+
* Demonstrates:
|
|
8
|
+
* - Watermark (e.g., "CONFIDENTIAL", "DRAFT")
|
|
9
|
+
* - Repeating header and footer
|
|
10
|
+
* - Multi-page content with numbered terms
|
|
11
|
+
* - AvoidBreak for signature block
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
interface ContractProps {
|
|
15
|
+
title?: string;
|
|
16
|
+
effectiveDate?: string;
|
|
17
|
+
watermark?: string;
|
|
18
|
+
parties?: {
|
|
19
|
+
provider: {
|
|
20
|
+
name: string;
|
|
21
|
+
address: string;
|
|
22
|
+
representative: string;
|
|
23
|
+
};
|
|
24
|
+
client: {
|
|
25
|
+
name: string;
|
|
26
|
+
address: string;
|
|
27
|
+
representative: string;
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
terms?: Array<{
|
|
31
|
+
title: string;
|
|
32
|
+
content: string;
|
|
33
|
+
}>;
|
|
34
|
+
signatures?: {
|
|
35
|
+
provider: { name: string; title: string };
|
|
36
|
+
client: { name: string; title: string };
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export default function Contract({
|
|
41
|
+
title = "SERVICE AGREEMENT",
|
|
42
|
+
effectiveDate = "January 1, 2025",
|
|
43
|
+
watermark = "DRAFT",
|
|
44
|
+
parties = {
|
|
45
|
+
provider: {
|
|
46
|
+
name: "Your Company, Inc.",
|
|
47
|
+
address: "123 Business St, San Francisco, CA 94102",
|
|
48
|
+
representative: "Alex Chen, CEO",
|
|
49
|
+
},
|
|
50
|
+
client: {
|
|
51
|
+
name: "Acme Corporation",
|
|
52
|
+
address: "456 Enterprise Blvd, Austin, TX 78701",
|
|
53
|
+
representative: "Sarah Johnson, CTO",
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
terms = [
|
|
57
|
+
{
|
|
58
|
+
title: "Services",
|
|
59
|
+
content:
|
|
60
|
+
"Provider agrees to deliver the Platform including all features and any updates released during the term of this Agreement.",
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
title: "Compensation",
|
|
64
|
+
content:
|
|
65
|
+
"Client shall pay Provider an annual license fee as specified in the Order Form. All fees are non-refundable and exclusive of applicable taxes.",
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
title: "Term and Termination",
|
|
69
|
+
content:
|
|
70
|
+
"This Agreement shall commence on the Effective Date and continue for twelve (12) months, automatically renewing unless either party provides written notice at least thirty (30) days prior.",
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
title: "Confidentiality",
|
|
74
|
+
content:
|
|
75
|
+
"Both parties agree to maintain the confidentiality of any proprietary information disclosed during this engagement. This obligation shall survive termination for three (3) years.",
|
|
76
|
+
},
|
|
77
|
+
],
|
|
78
|
+
signatures = {
|
|
79
|
+
provider: { name: "Alex Chen", title: "CEO" },
|
|
80
|
+
client: { name: "Sarah Johnson", title: "CTO" },
|
|
81
|
+
},
|
|
82
|
+
}: ContractProps) {
|
|
83
|
+
return (
|
|
84
|
+
<Document title={title}>
|
|
85
|
+
<Tailwind>
|
|
86
|
+
<Page
|
|
87
|
+
size="Legal"
|
|
88
|
+
margin="1in"
|
|
89
|
+
watermark={
|
|
90
|
+
watermark
|
|
91
|
+
? {
|
|
92
|
+
text: watermark,
|
|
93
|
+
opacity: 0.08,
|
|
94
|
+
rotation: -35,
|
|
95
|
+
}
|
|
96
|
+
: undefined
|
|
97
|
+
}
|
|
98
|
+
header={
|
|
99
|
+
<div className="flex justify-between items-center pb-3 border-b border-gray-300 mb-6">
|
|
100
|
+
<div className="text-xs font-semibold text-gray-600 uppercase tracking-wider">
|
|
101
|
+
{title}
|
|
102
|
+
</div>
|
|
103
|
+
<div className="text-xs text-gray-500">Effective: {effectiveDate}</div>
|
|
104
|
+
</div>
|
|
105
|
+
}
|
|
106
|
+
footer={
|
|
107
|
+
<div className="pt-3 border-t border-gray-300">
|
|
108
|
+
<div className="flex justify-between items-center text-xs text-gray-500">
|
|
109
|
+
<div>{parties.provider.name} — Confidential</div>
|
|
110
|
+
<div>
|
|
111
|
+
Page <PageNumber /> of <TotalPages />
|
|
112
|
+
</div>
|
|
113
|
+
</div>
|
|
114
|
+
</div>
|
|
115
|
+
}
|
|
116
|
+
>
|
|
117
|
+
{/* Parties Introduction */}
|
|
118
|
+
<p className="text-sm text-gray-700 leading-relaxed mb-6">
|
|
119
|
+
This {title} (“Agreement”) is entered into as of{" "}
|
|
120
|
+
<span className="font-semibold">{effectiveDate}</span> by and between the following
|
|
121
|
+
parties:
|
|
122
|
+
</p>
|
|
123
|
+
|
|
124
|
+
{/* Parties */}
|
|
125
|
+
<div className="grid grid-cols-2 gap-6 mb-8">
|
|
126
|
+
<div className="border-2 border-gray-800 rounded-lg p-4">
|
|
127
|
+
<div className="text-xs font-bold text-gray-800 uppercase tracking-wider mb-2">
|
|
128
|
+
Service Provider
|
|
129
|
+
</div>
|
|
130
|
+
<div className="text-sm font-bold text-gray-900">{parties.provider.name}</div>
|
|
131
|
+
<div className="text-xs text-gray-600 mt-1">{parties.provider.address}</div>
|
|
132
|
+
<div className="text-xs text-gray-500 mt-2">
|
|
133
|
+
<span className="font-medium">Representative: </span>
|
|
134
|
+
{parties.provider.representative}
|
|
135
|
+
</div>
|
|
136
|
+
</div>
|
|
137
|
+
<div className="border border-gray-300 rounded-lg p-4">
|
|
138
|
+
<div className="text-xs font-bold text-gray-500 uppercase tracking-wider mb-2">
|
|
139
|
+
Client
|
|
140
|
+
</div>
|
|
141
|
+
<div className="text-sm font-bold text-gray-900">{parties.client.name}</div>
|
|
142
|
+
<div className="text-xs text-gray-600 mt-1">{parties.client.address}</div>
|
|
143
|
+
<div className="text-xs text-gray-500 mt-2">
|
|
144
|
+
<span className="font-medium">Representative: </span>
|
|
145
|
+
{parties.client.representative}
|
|
146
|
+
</div>
|
|
147
|
+
</div>
|
|
148
|
+
</div>
|
|
149
|
+
|
|
150
|
+
{/* Terms Section Header */}
|
|
151
|
+
<div className="flex items-center gap-4 mb-6">
|
|
152
|
+
<div className="flex-1 border-t-2 border-gray-800"></div>
|
|
153
|
+
<h2 className="text-sm font-black text-gray-900 uppercase tracking-wider">
|
|
154
|
+
Terms and Conditions
|
|
155
|
+
</h2>
|
|
156
|
+
<div className="flex-1 border-t-2 border-gray-800"></div>
|
|
157
|
+
</div>
|
|
158
|
+
|
|
159
|
+
{/* Terms */}
|
|
160
|
+
<div className="space-y-5 mb-8">
|
|
161
|
+
{terms.map((term, i) => (
|
|
162
|
+
<div key={i} className="flex gap-4">
|
|
163
|
+
<div className="w-7 h-7 rounded-full bg-gray-800 text-white text-xs font-bold flex items-center justify-center flex-shrink-0">
|
|
164
|
+
{i + 1}
|
|
165
|
+
</div>
|
|
166
|
+
<div className="flex-1">
|
|
167
|
+
<h3 className="text-sm font-bold text-gray-900 mb-1">{term.title}</h3>
|
|
168
|
+
<p className="text-xs text-gray-600 leading-relaxed">{term.content}</p>
|
|
169
|
+
</div>
|
|
170
|
+
</div>
|
|
171
|
+
))}
|
|
172
|
+
</div>
|
|
173
|
+
|
|
174
|
+
{/* Agreement Statement */}
|
|
175
|
+
<div className="mb-8 bg-gray-100 p-5 rounded-lg">
|
|
176
|
+
<p className="text-xs text-gray-700 leading-relaxed text-center">
|
|
177
|
+
<strong className="text-gray-900">IN WITNESS WHEREOF</strong>, the parties have
|
|
178
|
+
executed this Agreement as of the Effective Date. Both parties acknowledge that they
|
|
179
|
+
have read, understood, and agree to be bound by all terms and conditions set forth
|
|
180
|
+
herein.
|
|
181
|
+
</p>
|
|
182
|
+
</div>
|
|
183
|
+
|
|
184
|
+
{/* Signatures */}
|
|
185
|
+
<AvoidBreak>
|
|
186
|
+
<div className="grid grid-cols-2 gap-10">
|
|
187
|
+
{/* Provider Signature */}
|
|
188
|
+
<div>
|
|
189
|
+
<div className="text-xs font-bold text-gray-800 uppercase tracking-wider mb-4">
|
|
190
|
+
Service Provider
|
|
191
|
+
</div>
|
|
192
|
+
<div className="border-b-2 border-gray-800 mb-2 h-10"></div>
|
|
193
|
+
<div className="text-sm font-bold text-gray-900">
|
|
194
|
+
{signatures.provider.name}
|
|
195
|
+
</div>
|
|
196
|
+
<div className="text-xs text-gray-600">{signatures.provider.title}</div>
|
|
197
|
+
<div className="mt-3 flex gap-4 text-xs text-gray-500">
|
|
198
|
+
<div>Date: _______________</div>
|
|
199
|
+
</div>
|
|
200
|
+
</div>
|
|
201
|
+
|
|
202
|
+
{/* Client Signature */}
|
|
203
|
+
<div>
|
|
204
|
+
<div className="text-xs font-bold text-gray-500 uppercase tracking-wider mb-4">
|
|
205
|
+
Client
|
|
206
|
+
</div>
|
|
207
|
+
<div className="border-b-2 border-gray-800 mb-2 h-10"></div>
|
|
208
|
+
<div className="text-sm font-bold text-gray-900">{signatures.client.name}</div>
|
|
209
|
+
<div className="text-xs text-gray-600">{signatures.client.title}</div>
|
|
210
|
+
<div className="mt-3 flex gap-4 text-xs text-gray-500">
|
|
211
|
+
<div>Date: _______________</div>
|
|
212
|
+
</div>
|
|
213
|
+
</div>
|
|
214
|
+
</div>
|
|
215
|
+
</AvoidBreak>
|
|
216
|
+
</Page>
|
|
217
|
+
</Tailwind>
|
|
218
|
+
</Document>
|
|
219
|
+
);
|
|
220
|
+
}
|