Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
pontusab committed Oct 27, 2024
1 parent 9fede55 commit 647ba09
Show file tree
Hide file tree
Showing 26 changed files with 295 additions and 99 deletions.
5 changes: 4 additions & 1 deletion apps/dashboard/.env-example
Original file line number Diff line number Diff line change
Expand Up @@ -81,4 +81,7 @@ AZURE_DOCUMENT_INTELLIGENCE_KEY=
NEXT_PUBLIC_GOOGLE_API_KEY=

# VatCheckAPI
VATCHECKAPI_API_KEY=
VATCHECKAPI_API_KEY=

# Invoice
INVOICE_JWT_SECRET=secret
4 changes: 4 additions & 0 deletions apps/dashboard/src/actions/create-customer-action.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"use server";

import { LogEvents } from "@midday/events/events";
import { generateToken } from "@midday/invoice/token";
import { revalidateTag } from "next/cache";
import { authActionClient } from "./safe-action";
import { createCustomerSchema } from "./schema";
Expand All @@ -15,10 +16,13 @@ export const createCustomerAction = authActionClient
},
})
.action(async ({ parsedInput: input, ctx: { user, supabase } }) => {
const token = await generateToken(user.id);

const { data } = await supabase
.from("customers")
.insert({
...input,
token,
team_id: user.team_id,
})
.select("id, name")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export const deleteInvoiceAction = authActionClient

revalidateTag(`invoices_${teamId}`);
revalidateTag(`invoice_summary_${teamId}`);
revalidateTag(`invoice_number_count_${teamId}`);
revalidateTag(`invoice_number_${teamId}`);

return data;
});
8 changes: 7 additions & 1 deletion apps/dashboard/src/actions/invoice/draft-invoice-action.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"use server";

import { authActionClient } from "@/actions/safe-action";
import { generateToken } from "@midday/invoice/token";
import { revalidateTag } from "next/cache";
import { draftInvoiceSchema } from "./schema";

Expand All @@ -16,6 +17,10 @@ export const draftInvoiceAction = authActionClient
}) => {
const teamId = user.team_id;

// Generate token if customer_id is not provided because it's a new invoice
// We use upsert so we don't have to check if the invoice already exists
const token = !input.customer_id && (await generateToken(id));

const { payment_details, from_details, ...restTemplate } = template;

const { data } = await supabase
Expand All @@ -28,6 +33,7 @@ export const draftInvoiceAction = authActionClient
payment_details,
from_details,
template: restTemplate,
token,
...input,
},
{
Expand All @@ -40,7 +46,7 @@ export const draftInvoiceAction = authActionClient

revalidateTag(`invoice_summary_${teamId}`);
revalidateTag(`invoices_${teamId}`);
revalidateTag(`invoice_number_count_${teamId}`);
revalidateTag(`invoice_number_${teamId}`);

return data;
},
Expand Down
2 changes: 2 additions & 0 deletions apps/dashboard/src/actions/invoice/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export const draftInvoiceSchema = z.object({
from_details: z.any().optional(),
customer_details: z.any().optional(),
customer_id: z.string().uuid().optional(),
customer_name: z.string().optional(),
payment_details: z.any().optional(),
note_details: z.any().optional(),
due_date: z.coerce.date(),
Expand Down Expand Up @@ -99,6 +100,7 @@ export const invoiceFormSchema = z.object({
from_details: z.any(),
customer_details: z.any(),
customer_id: z.string().uuid(),
customer_name: z.string().optional(),
payment_details: z.any(),
note_details: z.any().optional(),
due_date: z.coerce.date(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export const updateInvoiceAction = authActionClient

revalidateTag(`invoice_summary_${teamId}`);
revalidateTag(`invoices_${teamId}`);
revalidateTag(`invoice_number_count_${teamId}`);
revalidateTag(`invoice_number_${teamId}`);

return data;
},
Expand Down

This file was deleted.

61 changes: 61 additions & 0 deletions apps/dashboard/src/app/[locale]/(public)/i/[token]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { verify } from "@midday/invoice/token";
import { getInvoiceQuery } from "@midday/supabase/queries";
import { createClient } from "@midday/supabase/server";
import type { Metadata } from "next";
import { notFound } from "next/navigation";

export async function generateMetadata({
params,
}: { params: { token: string } }): Promise<Metadata> {
const supabase = createClient({ admin: true });

try {
const { id } = await verify(params.token);
const { data: invoice } = await getInvoiceQuery(supabase, id);

if (!invoice) {
return {
title: "Invoice Not Found",
robots: {
index: false,
follow: false,
},
};
}

return {
title: `Invoice ${invoice.invoice_number} | ${invoice.team?.name}`,
description: `Invoice for ${invoice.customer?.name || "Customer"}`,
robots: {
index: false,
follow: false,
},
};
} catch (error) {
return {
title: "Invalid Invoice",
robots: {
index: false,
follow: false,
},
};
}
}

export default async function Page({ params }: { params: { token: string } }) {
const supabase = createClient({ admin: true });

try {
const { id } = await verify(params.token);
console.log("katt", id);
const { data: invoice } = await getInvoiceQuery(supabase, id);

if (!invoice) {
notFound();
}

return <div>Invoice: {invoice.invoice_number}</div>;
} catch (error) {
notFound();
}
}
23 changes: 23 additions & 0 deletions apps/dashboard/src/app/[locale]/not-found.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import Image from "next/image";
import Link from "next/link";
import appIcon from "public/appicon.png";

export default function NotFound() {
return (
<div className="h-screen flex flex-col items-center justify-center text-center text-sm text-[#606060]">
<Image
src={appIcon}
width={80}
height={80}
alt="Midday"
quality={100}
className="mb-10"
/>
<h2 className="text-xl font-semibold mb-2">Not Found</h2>
<p className="mb-4">Could not find requested resource</p>
<Link href="/" className="underline">
Return Home
</Link>
</div>
);
}
4 changes: 2 additions & 2 deletions apps/dashboard/src/components/copy-input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@ export function CopyInput({ value, className }: Props) {
onClick={handleClipboard}
className={cn(
"flex items-center relative w-full border py-2 px-4 cursor-pointer",
className
className,
)}
>
<div className="pr-7 text-[#878787] text-sm">{value}</div>
<div className="pr-7 text-[#878787] text-sm truncate">{value}</div>
<motion.div
className="absolute right-4 top-2.5"
initial={{ opacity: 1, scale: 1 }}
Expand Down
42 changes: 22 additions & 20 deletions apps/dashboard/src/components/invoice-details.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export function InvoiceDetails({
invoice_date,
invoice_number,
template,
short_id,
token,
}: Props) {
return (
<div>
Expand Down Expand Up @@ -122,28 +122,30 @@ export function InvoiceDetails({
</div>
</div>

<div className="mt-6 flex flex-col space-y-2 border-t border-border pt-6">
<span className="text-sm text-[#606060]">Invoice link</span>
<div className="flex space-x-2">
<CopyInput value={`https://app.midday.ai/c/jnsf234/i/${short_id}`} />
{customer && (
<div className="mt-6 flex flex-col space-y-2 border-t border-border pt-6">
<span className="text-sm text-[#606060]">Invoice link</span>
<div className="flex space-x-2">
<CopyInput value={`https://app.midday.ai/i/${token}`} />

{status !== "draft" && (
<a
href={`/api/download/invoice?id=${id}&size=${template?.size}`}
download
>
<Button
variant="secondary"
className="size-[40px] hover:bg-secondary"
{status !== "draft" && (
<a
href={`/api/download/invoice?id=${id}&size=${template?.size}`}
download
>
<div>
<Icons.Download className="size-4" />
</div>
</Button>
</a>
)}
<Button
variant="secondary"
className="size-[40px] hover:bg-secondary"
>
<div>
<Icons.Download className="size-4" />
</div>
</Button>
</a>
)}
</div>
</div>
</div>
)}

<Accordion type="single" collapsible className="mt-6">
<AccordionItem value="note">
Expand Down
1 change: 1 addition & 0 deletions apps/dashboard/src/components/invoice/customer-details.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export interface Customer {
phone?: string;
address_line_1?: string;
address_line_2?: string;
token: string;
city?: string;
state?: string;
zip?: string;
Expand Down
33 changes: 22 additions & 11 deletions apps/dashboard/src/components/invoice/form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { useAction } from "next-safe-action/hooks";
import Link from "next/link";
import { useEffect, useState } from "react";
import { useFormContext, useWatch } from "react-hook-form";
import type { Invoice } from "../tables/invoices/columns";
import { CreateButton } from "./create-button";
import { type Customer, CustomerDetails } from "./customer-details";
import { FromDetails } from "./from-details";
Expand All @@ -27,7 +28,8 @@ type Props = {
};

export function Form({ teamId, customers }: Props) {
const { selectedCustomerId, invoiceId } = useInvoiceParams();
const { selectedCustomerId } = useInvoiceParams();
const [data, setData] = useState<Invoice | undefined>();
const [lastUpdated, setLastUpdated] = useState<Date | undefined>();
const [lastEditedText, setLastEditedText] = useState("");

Expand All @@ -37,7 +39,10 @@ export function Form({ teamId, customers }: Props) {

const draftInvoice = useAction(draftInvoiceAction, {
onSuccess: ({ data }) => {
setLastUpdated(new Date());
if (data) {
setData(data);
setLastUpdated(new Date());
}
},
});

Expand All @@ -47,6 +52,7 @@ export function Form({ teamId, customers }: Props) {
name: [
"template",
"customer_id",
"customer_name",
"line_items",
"amount",
"vat",
Expand All @@ -71,8 +77,11 @@ export function Form({ teamId, customers }: Props) {
}, [debouncedValues, isDirty]);

useEffect(() => {
const customer = customers.find((c) => c.id === selectedCustomerId);

if (selectedCustomerId) {
form.setValue("customer_id", selectedCustomerId);
form.setValue("customer_id", customer?.id);
form.setValue("customer_name", customer?.name);
}
}, [selectedCustomerId]);

Expand Down Expand Up @@ -140,14 +149,16 @@ export function Form({ teamId, customers }: Props) {
<div className="absolute bottom-14 w-full h-9">
<div className="flex justify-between items-center mt-auto">
<div className="flex space-x-2 items-center">
<Link
href={`/preview/invoice/${invoiceId}`}
className="text-xs text-[#808080] flex items-center gap-1"
target="_blank"
>
<Icons.ExternalLink className="size-3" />
<span>Preview invoice</span>
</Link>
{data && (
<Link
href={`/i/${data.token}`}
className="text-xs text-[#808080] flex items-center gap-1"
target="_blank"
>
<Icons.ExternalLink className="size-3" />
<span>Preview invoice</span>
</Link>
)}

{lastEditedText && (
<motion.div
Expand Down
17 changes: 10 additions & 7 deletions apps/dashboard/src/components/invoice/settings-menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -95,15 +95,18 @@ export function SettingsMenu() {
key={optionIndex.toString()}
className="text-xs"
checked={watch(watchKey) === option.value}
onCheckedChange={() => {
setValue(watchKey, option.value, {
shouldValidate: true,
});
onCheckedChange={(checked) => {
if (checked) {
setValue(watchKey, option.value, {
shouldValidate: true,
});

updateInvoiceTemplate.execute({
[item.key]: option.value,
});
updateInvoiceTemplate.execute({
[item.key]: option.value,
});
}
}}
onSelect={(event) => event.preventDefault()}
>
{watchKey === "template.date_format"
? format(new Date(), option.value as string)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export function CustomerCreateSheet() {

return (
<Sheet open={isOpen} onOpenChange={() => setParams(null)}>
<SheetContent style={{ maxWidth: 610 }} stack>
<SheetContent stack>
<SheetHeader className="mb-6 flex justify-between items-center flex-row">
<h2 className="text-xl">Create Customer</h2>
<Button
Expand Down
Loading

0 comments on commit 647ba09

Please sign in to comment.