Skip to content

Commit 25c025e

Browse files
committed
fix cli
1 parent 4a52ffb commit 25c025e

File tree

21 files changed

+1004
-632
lines changed

21 files changed

+1004
-632
lines changed
File renamed without changes.

apps/asdf/package.json

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"name": "htmldocs-starter",
3+
"version": "0.0.0",
4+
"private": true,
5+
"scripts": {
6+
"dev": "htmldocs dev",
7+
"publish": "htmldocs publish"
8+
},
9+
"dependencies": {
10+
"@htmldocs/react": "0.1.0",
11+
"@htmldocs/render": "0.1.0",
12+
"clsx": "^2.1.1",
13+
"htmldocs": "0.1.1",
14+
"react": "^18.2.0",
15+
"react-dom": "^18.2.0"
16+
},
17+
"devDependencies": {
18+
"@types/react": "^18.2.61",
19+
"@types/react-dom": "^18.2.19",
20+
"typescript": "^5.4.5"
21+
}
22+
}

packages/htmldocs/bun.lockb

215 KB
Binary file not shown.

packages/htmldocs/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,8 @@
7474
"@types/babel__core": "^7.20.5",
7575
"@types/mime-types": "^2.1.4",
7676
"@types/node": "^20",
77-
"@types/react": "^18",
78-
"@types/react-dom": "^18",
77+
"@types/react": "^18.2.61",
78+
"@types/react-dom": "^18.2.19",
7979
"tsup": "^8.0.2",
8080
"typescript": "^5"
8181
}

packages/htmldocs/public/next.svg

Lines changed: 0 additions & 1 deletion
This file was deleted.
Lines changed: 335 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,335 @@
1+
import { Document, Head, Page, Spacer } from "@htmldocs/react";
2+
import clsx from "clsx";
3+
import "~/index.css";
4+
5+
const tableHeaderStyle =
6+
"text-sm font-medium text-gray-900 py-2 whitespace-nowrap";
7+
8+
interface BilledTo {
9+
name: string;
10+
address: string;
11+
city: string;
12+
state: string;
13+
zip: string;
14+
phone: string;
15+
}
16+
17+
interface YourCompany {
18+
name: string;
19+
address: string;
20+
city: string;
21+
state: string;
22+
zip: string;
23+
taxId: string;
24+
phone: string;
25+
email: string;
26+
}
27+
28+
interface Service {
29+
name: string;
30+
description?: string;
31+
quantity: number;
32+
rate: number;
33+
}
34+
35+
export interface InvoiceProps {
36+
billedTo: BilledTo;
37+
yourCompany: YourCompany;
38+
services: Service[];
39+
}
40+
41+
function Invoice({ billedTo, yourCompany, services }: InvoiceProps) {
42+
const issueDate = new Date().toLocaleDateString("en-US", {
43+
day: "2-digit",
44+
month: "short",
45+
year: "numeric",
46+
});
47+
// 7 days from now
48+
const dueDate = new Date(
49+
Date.now() + 7 * 24 * 60 * 60 * 1000
50+
).toLocaleDateString("en-US", {
51+
day: "2-digit",
52+
month: "short",
53+
year: "numeric",
54+
});
55+
56+
const subtotal = services.reduce(
57+
(acc, service) => acc + service.quantity * service.rate,
58+
0
59+
);
60+
const taxRate = 0.12;
61+
const tax = subtotal * taxRate;
62+
const total = subtotal + tax;
63+
64+
return (
65+
<Document size="A4" orientation="portrait" margin="0.5in">
66+
<Head>
67+
<title>Invoice</title>
68+
<link rel="preconnect" href="https://rsms.me/" />
69+
<link rel="stylesheet" href="https://rsms.me/inter/inter.css" />
70+
<style>
71+
{`
72+
:root {
73+
font-family: Inter, sans-serif;
74+
font-feature-settings: 'liga' 1, 'calt' 1; /* fix for Chrome */
75+
}
76+
@supports (font-variation-settings: normal) {
77+
:root { font-family: InterVariable, sans-serif; }
78+
}
79+
`}
80+
</style>
81+
</Head>
82+
<Page className="flex flex-col justify-between">
83+
<div id="invoice_body">
84+
<div
85+
id="document_header"
86+
className="flex flex-row items-center justify-between"
87+
>
88+
<div id="header_left" className="flex flex-col">
89+
<div className="uppercase text-4xl font-medium mb-1">Invoice</div>
90+
<p className="text-gray-500">#AB2324-01</p>
91+
</div>
92+
<div id="header_right">
93+
<img src="/static/logo.svg" alt="Company Logo" />
94+
</div>
95+
</div>
96+
<Spacer height={48} />
97+
<div className="flex flex-row justify-between border-y border-gray-300 divide-x divide-gray-300">
98+
<div className="flex-1 flex flex-col justify-between p-4 pl-0">
99+
<div>
100+
<h2 className="text-sm font-medium">Issued</h2>
101+
<p className="text-sm text-gray-500 font-medium">{issueDate}</p>
102+
</div>
103+
<div>
104+
<h2 className="text-sm font-medium mt-2">Due</h2>
105+
<p className="text-sm text-gray-500 font-medium">{dueDate}</p>
106+
</div>
107+
</div>
108+
<div className="flex-1 flex flex-col p-4">
109+
<h2 className="text-sm font-medium">Billed To</h2>
110+
<p className="text-sm text-gray-500 font-medium">
111+
{billedTo.name}
112+
</p>
113+
<p className="text-sm text-gray-500">{billedTo.address}</p>
114+
<p className="text-sm text-gray-500">
115+
{billedTo.city}, {billedTo.state} {billedTo.zip}
116+
</p>
117+
<p className="text-sm text-gray-500">{billedTo.phone}</p>
118+
</div>
119+
<div className="flex-1 flex flex-col p-4 pr-0">
120+
<h2 className="text-sm font-medium">From</h2>
121+
<p className="text-sm text-gray-500 font-semibold">
122+
{yourCompany.name}
123+
</p>
124+
<p className="text-sm text-gray-500">{yourCompany.address}</p>
125+
<p className="text-sm text-gray-500">
126+
{yourCompany.city}, {yourCompany.state} {yourCompany.zip}
127+
</p>
128+
<p className="text-sm text-gray-500">
129+
TAX ID {yourCompany.taxId}
130+
</p>
131+
</div>
132+
</div>
133+
<Spacer height={32} />
134+
<div className="flex flex-col mt-4">
135+
<div className="overflow-x-auto">
136+
<table className="min-w-full">
137+
<thead className="border-b">
138+
<tr>
139+
<th
140+
scope="col"
141+
className={clsx(tableHeaderStyle, "text-left")}
142+
>
143+
Service
144+
</th>
145+
<th
146+
scope="col"
147+
className={clsx(tableHeaderStyle, "pr-16 text-right")}
148+
>
149+
Qty
150+
</th>
151+
<th
152+
scope="col"
153+
className={clsx(tableHeaderStyle, "text-right")}
154+
>
155+
Rate
156+
</th>
157+
<th
158+
scope="col"
159+
className={clsx(tableHeaderStyle, "pl-16 text-right")}
160+
>
161+
Line total
162+
</th>
163+
</tr>
164+
</thead>
165+
<tbody>
166+
{services.map((service) => (
167+
<TableRow service={service} />
168+
))}
169+
<tr className="border-b"></tr>
170+
<tr className="h-12">
171+
<td className="w-full"></td>
172+
<td className="text-left font-medium text-sm whitespace-nowrap border-b">
173+
Subtotal
174+
</td>
175+
<td className="border-b"></td>
176+
<td className="text-right text-sm text-gray-900 whitespace-nowrap border-b">
177+
{subtotal.toLocaleString("en-US", {
178+
style: "currency",
179+
currency: "USD",
180+
})}
181+
</td>
182+
</tr>
183+
<tr className="h-12">
184+
<td className="w-full"></td>
185+
<td className="text-left font-medium text-sm whitespace-nowrap border-b">
186+
Tax ({taxRate * 100}%)
187+
</td>
188+
<td className="border-b"></td>
189+
<td className="text-right text-sm text-gray-900 whitespace-nowrap border-b">
190+
{tax.toLocaleString("en-US", {
191+
style: "currency",
192+
currency: "USD",
193+
})}
194+
</td>
195+
</tr>
196+
<tr className="h-12">
197+
<td className="w-full"></td>
198+
<td className="text-left font-medium text-sm whitespace-nowrap border-b">
199+
Total
200+
</td>
201+
<td className="border-b"></td>
202+
<td className="text-right text-sm text-gray-900 whitespace-nowrap border-b">
203+
{total.toLocaleString("en-US", {
204+
style: "currency",
205+
currency: "USD",
206+
})}
207+
</td>
208+
</tr>
209+
<tr className="h-12 text-purple-700">
210+
<td className="w-full"></td>
211+
<td className="text-left font-medium text-sm whitespace-nowrap border-y-2 border-purple-700">
212+
Amount due
213+
</td>
214+
<td className="border-y-2 border-purple-700"></td>
215+
<td className="text-right font-medium text-sm whitespace-nowrap border-y-2 border-purple-700">
216+
{total.toLocaleString("en-US", {
217+
style: "currency",
218+
currency: "USD",
219+
})}
220+
</td>
221+
</tr>
222+
</tbody>
223+
</table>
224+
</div>
225+
</div>
226+
</div>
227+
<div id="footer">
228+
<div className="flex flex-col pb-4 border-b">
229+
<p className="text-sm font-medium">Thank you for your business!</p>
230+
<p className="flex items-center gap-2">
231+
<svg
232+
width="16"
233+
height="16"
234+
viewBox="0 0 10 10"
235+
fill="none"
236+
xmlns="http://www.w3.org/2000/svg"
237+
>
238+
<path
239+
fillRule="evenodd"
240+
clipRule="evenodd"
241+
d="M2 0C0.895431 0 0 0.89543 0 2V8C0 9.10457 0.89543 10 2 10H8C9.10457 10 10 9.10457 10 8V2C10 0.895431 9.10457 0 8 0H2ZM4.72221 2.95508C4.72221 2.7825 4.58145 2.64014 4.41071 2.66555C3.33092 2.82592 2.5 3.80797 2.5 4.99549V7.01758C2.5 7.19016 2.63992 7.33008 2.8125 7.33008H4.40971C4.58229 7.33008 4.72221 7.19016 4.72221 7.01758V5.6021C4.72221 5.42952 4.58229 5.2896 4.40971 5.2896H3.61115V4.95345C3.61115 4.41687 3.95035 3.96422 4.41422 3.82285C4.57924 3.77249 4.72221 3.63715 4.72221 3.4645V2.95508ZM7.5 2.95508C7.5 2.7825 7.35924 2.64014 7.18849 2.66555C6.1087 2.82592 5.27779 3.80797 5.27779 4.99549V7.01758C5.27779 7.19016 5.41771 7.33008 5.59029 7.33008H7.1875C7.36008 7.33008 7.5 7.19016 7.5 7.01758V5.6021C7.5 5.42952 7.36008 5.2896 7.1875 5.2896H6.38885V4.95345C6.38885 4.41695 6.72813 3.96422 7.19193 3.82285C7.35703 3.77249 7.5 3.63715 7.5 3.4645V2.95508Z"
242+
fill="#8B919E"
243+
/>
244+
</svg>
245+
<span className="text-sm text-gray-500">
246+
Please pay within 15 days of receiving this invoice.
247+
</span>
248+
</p>
249+
</div>
250+
<Spacer height={36} />
251+
<div className="flex justify-between text-sm text-gray-500">
252+
{yourCompany.name} &copy; {new Date().getFullYear()}
253+
<div className="flex items-center gap-8">
254+
<p className="text-sm">{yourCompany.phone}</p>
255+
<p className="text-sm">{yourCompany.email}</p>
256+
</div>
257+
</div>
258+
</div>
259+
</Page>
260+
</Document>
261+
);
262+
}
263+
264+
interface TableRowProps {
265+
service: Service;
266+
}
267+
268+
const TableRow = ({ service }: TableRowProps) => {
269+
const total = service.quantity * service.rate;
270+
const cellStyle = "text-sm text-gray-900 font-light py-4 whitespace-nowrap";
271+
const detailStyle =
272+
"whitespace-nowrap align-top overflow-hidden overflow-ellipsis";
273+
274+
return (
275+
<tr>
276+
<td className={clsx(cellStyle, "w-full")}>
277+
<div className="flex flex-col gap-1">
278+
<span className="font-medium">{service.name}</span>
279+
<span className="text-gray-500">{service.description}</span>
280+
</div>
281+
</td>
282+
<td className={clsx(cellStyle, detailStyle, "text-left")}>
283+
{service.quantity}
284+
</td>
285+
<td className={clsx(cellStyle, detailStyle, "text-right")}>
286+
{service.rate.toLocaleString("en-US", {
287+
style: "currency",
288+
currency: "USD",
289+
})}
290+
</td>
291+
<td className={clsx(cellStyle, detailStyle, "text-right")}>
292+
{total.toLocaleString("en-US", { style: "currency", currency: "USD" })}
293+
</td>
294+
</tr>
295+
);
296+
};
297+
298+
Invoice.PreviewProps = {
299+
billedTo: {
300+
name: "John Doe",
301+
address: "123 Elm Street",
302+
city: "Anytown",
303+
state: "CA",
304+
zip: "12345",
305+
phone: "123-456-7890",
306+
},
307+
yourCompany: {
308+
name: "Your Company",
309+
address: "456 Banana Rd.",
310+
city: "San Francisco",
311+
state: "CA",
312+
zip: "94107",
313+
taxId: "00XXXXX1234X0XX",
314+
phone: "123-456-7890",
315+
316+
},
317+
services: [
318+
{
319+
name: "Design",
320+
description: "Description",
321+
quantity: 1,
322+
rate: 1000,
323+
},
324+
{
325+
name: "Development",
326+
description: "Description",
327+
quantity: 2,
328+
rate: 1200,
329+
},
330+
],
331+
};
332+
333+
Invoice.documentId = "invoice"
334+
335+
export default Invoice;

0 commit comments

Comments
 (0)