93 lines
2.8 KiB
TypeScript
93 lines
2.8 KiB
TypeScript
import type { LoaderFunctionArgs } from "react-router";
|
|
import { Link, useLoaderData } from "react-router";
|
|
|
|
import { authenticate } from "../shopify.server";
|
|
import db from "../db.server";
|
|
|
|
export const loader = async ({ request }: LoaderFunctionArgs) => {
|
|
const { session } = await authenticate.admin(request);
|
|
|
|
const [settings, recent] = await Promise.all([
|
|
db.shopSettings.findUnique({ where: { shopDomain: session.shop } }),
|
|
db.invoice.findMany({
|
|
where: { shopDomain: session.shop },
|
|
orderBy: [{ issuedAt: "desc" }],
|
|
take: 10,
|
|
}),
|
|
]);
|
|
|
|
const settingsConfigured = !!(
|
|
settings &&
|
|
settings.companyName &&
|
|
settings.addressLine1 &&
|
|
settings.iban
|
|
);
|
|
|
|
return {
|
|
settingsConfigured,
|
|
recent: recent.map((i) => ({
|
|
id: i.id,
|
|
number: i.invoiceNumber,
|
|
kind: i.kind,
|
|
orderName: i.orderName,
|
|
version: i.version,
|
|
sentAt: i.sentAt?.toISOString() ?? null,
|
|
cancelledAt: i.cancelledAt?.toISOString() ?? null,
|
|
issuedAt: i.issuedAt.toISOString(),
|
|
pdfUrl: i.pdfUrl,
|
|
})),
|
|
};
|
|
};
|
|
|
|
export default function Index() {
|
|
const { settingsConfigured, recent } = useLoaderData<typeof loader>();
|
|
|
|
return (
|
|
<s-page heading="LinumIQ Invoice">
|
|
{!settingsConfigured && (
|
|
<s-banner tone="warning" heading="Configure your invoice settings">
|
|
Complete your company, bank and numbering details so generated
|
|
invoices are legally compliant.{" "}
|
|
<Link to="/app/settings">Open settings</Link>
|
|
</s-banner>
|
|
)}
|
|
|
|
<s-section heading="What this app does">
|
|
<s-paragraph>
|
|
Generates Austrian-compliant PDF invoices for your Shopify orders.
|
|
Trigger from the order page (Generate invoice action), via Shopify
|
|
Flow, or in bulk from the Invoices page. PDFs are stored on
|
|
Shopify Files and linked to each order via metafields.
|
|
</s-paragraph>
|
|
</s-section>
|
|
|
|
<s-section heading="Recent invoices">
|
|
{recent.length === 0 ? (
|
|
<s-paragraph>No invoices generated yet.</s-paragraph>
|
|
) : (
|
|
<s-unordered-list>
|
|
{recent.map((i) => (
|
|
<s-list-item key={i.id}>
|
|
{i.kind === "storno" ? "Storno " : ""}
|
|
{i.number} — order {i.orderName} (v{i.version})
|
|
{i.cancelledAt
|
|
? " — cancelled"
|
|
: i.sentAt
|
|
? " — sent"
|
|
: ""}
|
|
{i.pdfUrl ? (
|
|
<>
|
|
{" "}
|
|
[<a href={i.pdfUrl} target="_blank" rel="noreferrer">PDF</a>]
|
|
</>
|
|
) : null}
|
|
</s-list-item>
|
|
))}
|
|
</s-unordered-list>
|
|
)}
|
|
<Link to="/app/invoices">Open invoices page</Link>
|
|
</s-section>
|
|
</s-page>
|
|
);
|
|
}
|