Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
pontusab committed Oct 15, 2024
1 parent 09de6e1 commit 4c6583c
Show file tree
Hide file tree
Showing 16 changed files with 270 additions and 64 deletions.
16 changes: 16 additions & 0 deletions apps/dashboard/src/components/invoice/create-button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
"use client";

import { Button } from "@midday/ui/button";
import { useFormContext } from "react-hook-form";
import type { InvoiceFormValues } from "./schema";

export function CreateButton() {
const { handleSubmit } = useFormContext<InvoiceFormValues>();

const onSubmit = handleSubmit((data) => {
// Handle form submission
console.log(data);
});

return <Button onClick={onSubmit}>Create</Button>;
}
8 changes: 2 additions & 6 deletions apps/dashboard/src/components/invoice/customer-content.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import { Editor } from "@/components/editor";
import type { JSONContent } from "novel";
import { useState } from "react";
import { LabelInput } from "./label-input";

const defaultContent: JSONContent = {
type: "paragraph",
Expand Down Expand Up @@ -50,13 +50,9 @@ const defaultContent: JSONContent = {
};

export function CustomerContent() {
const [content, setContent] = useState(null);

return (
<div>
<span className="font-mono text-[#878787] mb-2 text-[11px] block">
To
</span>
<LabelInput name="settings.customerContent" />
<Editor initialContent={defaultContent} className="h-[115px]" />
</div>
);
Expand Down
39 changes: 37 additions & 2 deletions apps/dashboard/src/components/invoice/due-date.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,42 @@
import { Calendar } from "@midday/ui/calendar";
import { Popover, PopoverContent, PopoverTrigger } from "@midday/ui/popover";
import { format } from "date-fns";
import { useState } from "react";
import { useFormContext } from "react-hook-form";
import { LabelInput } from "./label-input";
import type { InvoiceFormValues } from "./schema";

export function DueDate() {
const { setValue, watch } = useFormContext<InvoiceFormValues>();
const dueDate = watch("dueDate");
const [isOpen, setIsOpen] = useState(false);

const handleSelect = (date: Date | undefined) => {
if (date) {
setValue("dueDate", date, { shouldValidate: true });
setIsOpen(false);
}
};

return (
<div className="text-[11px] text-[#878787] font-mono">
Due date: <span className="text-primary">08/12/2024</span>
<div className="flex space-x-1 items-center">
<div className="flex items-center">
<LabelInput name="settings.dueDate" />
<span className="text-[11px] text-[#878787] font-mono">:</span>
</div>
<Popover open={isOpen} onOpenChange={setIsOpen}>
<PopoverTrigger className="text-primary text-[11px] font-mono whitespace-nowrap flex">
{format(dueDate || new Date(), "MM/dd/yyyy")}
</PopoverTrigger>
<PopoverContent className="w-auto p-0">
<Calendar
mode="single"
selected={dueDate}
onSelect={handleSelect}
initialFocus
/>
</PopoverContent>
</Popover>
</div>
);
}
28 changes: 19 additions & 9 deletions apps/dashboard/src/components/invoice/form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { zodResolver } from "@hookform/resolvers/zod";
import { Button } from "@midday/ui/button";
import { ScrollArea } from "@midday/ui/scroll-area";
import { FormProvider, useForm } from "react-hook-form";
import { CreateButton } from "./create-button";
import { CustomerContent } from "./customer-content";
import { FromContent } from "./from-content";
import { LineItems } from "./line-items";
Expand All @@ -16,21 +17,30 @@ export function Form() {
const form = useForm<InvoiceFormValues>({
resolver: zodResolver(invoiceSchema),
defaultValues: {
settings: {
invoiceNo: "Invoice NO",
issueDate: "Issue Date",
dueDate: "Due Date",
customerContent: "To",
fromContent: "From",
description: "Description",
price: "Price",
quantity: "Quantity",
total: "Total",
vat: "VAT",
tax: "Tax",
paymentDetails: "Payment Details",
note: "Note",
logoUrl: undefined,
},
currency: "USD",
lineItems: [{ name: "", quantity: 0, price: 0 }],
},
});

const onSubmit = (data: InvoiceFormValues) => {
console.log(data);
};

return (
<FormProvider {...form}>
<form
onSubmit={form.handleSubmit(onSubmit)}
className="relative h-full antialiased"
>
<form className="relative h-full antialiased">
<ScrollArea
className="w-[544px] h-full max-h-[770px] bg-background"
hideScrollbar
Expand Down Expand Up @@ -74,7 +84,7 @@ export function Form() {

<div className="absolute bottom-14 w-full h-9">
<div className="flex justify-end mt-auto">
<Button>Create & Send</Button>
<CreateButton />
</div>
</div>
</form>
Expand Down
8 changes: 2 additions & 6 deletions apps/dashboard/src/components/invoice/from-content.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import { Editor } from "@/components/editor";
import type { JSONContent } from "novel";
import { useState } from "react";
import { LabelInput } from "./label-input";

const defaultContent: JSONContent = {
type: "paragraph",
Expand Down Expand Up @@ -50,13 +50,9 @@ const defaultContent: JSONContent = {
};

export function FromContent() {
const [content, setContent] = useState(null);

return (
<div>
<span className="font-mono text-[#878787] mb-2 text-[11px] block">
From
</span>
<LabelInput name="settings.fromContent" />
<Editor initialContent={defaultContent} className="h-[115px]" />
</div>
);
Expand Down
12 changes: 10 additions & 2 deletions apps/dashboard/src/components/invoice/invoice-no.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
import { LabelInput } from "./label-input";

export function InvoiceNo() {
return (
<div className="text-[11px] text-[#878787] font-mono">
Invoice NO: <span className="text-primary">INV-01</span>
<div className="flex space-x-1 items-center">
<div className="flex items-center">
<LabelInput name="settings.invoiceNo" />
<span className="text-[11px] text-[#878787] font-mono">:</span>
</div>
<span className="text-primary text-[11px] font-mono whitespace-nowrap">
INV-01
</span>
</div>
);
}
39 changes: 37 additions & 2 deletions apps/dashboard/src/components/invoice/issue-date.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,42 @@
import { Calendar } from "@midday/ui/calendar";
import { Popover, PopoverContent, PopoverTrigger } from "@midday/ui/popover";
import { format } from "date-fns";
import { useState } from "react";
import { useFormContext } from "react-hook-form";
import { LabelInput } from "./label-input";
import type { InvoiceFormValues } from "./schema";

export function IssueDate() {
const { setValue, watch } = useFormContext<InvoiceFormValues>();
const issueDate = watch("issueDate");
const [isOpen, setIsOpen] = useState(false);

const handleSelect = (date: Date | undefined) => {
if (date) {
setValue("issueDate", date, { shouldValidate: true });
setIsOpen(false);
}
};

return (
<div className="text-[11px] text-[#878787] font-mono">
Issue date: <span className="text-primary">08/12/2024</span>
<div className="flex space-x-1 items-center">
<div className="flex items-center">
<LabelInput name="settings.issueDate" />
<span className="text-[11px] text-[#878787] font-mono">:</span>
</div>
<Popover open={isOpen} onOpenChange={setIsOpen}>
<PopoverTrigger className="text-primary text-[11px] font-mono whitespace-nowrap flex">
{format(issueDate || new Date(), "MM/dd/yyyy")}
</PopoverTrigger>
<PopoverContent className="w-auto p-0">
<Calendar
mode="single"
selected={issueDate}
onSelect={handleSelect}
initialFocus
/>
</PopoverContent>
</Popover>
</div>
);
}
30 changes: 30 additions & 0 deletions apps/dashboard/src/components/invoice/label-input.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
"use client";

import { cn } from "@midday/ui/cn";
import { useFormContext } from "react-hook-form";

type Props = {
name: string;
required?: boolean;
className?: string;
};

export function LabelInput({ name, className }: Props) {
const { setValue, watch } = useFormContext();
const value = watch(name);

return (
<span
className={cn("text-[11px] text-[#878787] font-mono", className)}
id={name}
contentEditable
suppressContentEditableWarning
onBlur={(e) => {
const newValue = e.currentTarget.textContent || "";
setValue(name, newValue, { shouldValidate: true });
}}
>
{value}
</span>
);
}
17 changes: 5 additions & 12 deletions apps/dashboard/src/components/invoice/line-items.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { Reorder, useDragControls, useMotionValue } from "framer-motion";
import { useFieldArray, useFormContext } from "react-hook-form";
import { AmountInput } from "./amount-input";
import { Input } from "./input";
import { LabelInput } from "./label-input";
import type { InvoiceFormValues } from "./schema";
import { VATInput } from "./vat-input";

Expand Down Expand Up @@ -41,18 +42,10 @@ export function LineItems() {
return (
<div className="space-y-4">
<div className="flex items-end mb-2">
<Label className="text-[11px] text-[#878787] font-mono w-1/2 mr-4">
Name
</Label>
<Label className="text-[11px] text-[#878787] font-mono w-40 mr-4">
Price
</Label>
<Label className="text-[11px] text-[#878787] font-mono w-24 mr-4">
Quantity
</Label>
<Label className="text-[11px] text-[#878787] font-mono w-24 text-right">
VAT
</Label>
<LabelInput name="settings.description" className="w-1/2 mr-4" />
<LabelInput name="settings.price" className="w-40 mr-4" />
<LabelInput name="settings.quantity" className="w-24 mr-4" />
<LabelInput name="settings.vat" className="w-24 text-right" />
</div>

<Reorder.Group axis="y" values={fields} onReorder={reorderList}>
Expand Down
58 changes: 57 additions & 1 deletion apps/dashboard/src/components/invoice/logo.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,61 @@
"use client";

import { useUpload } from "@/hooks/use-upload";
import { Skeleton } from "@midday/ui/skeleton";
import { useToast } from "@midday/ui/use-toast";
import { useFormContext } from "react-hook-form";
import type { InvoiceFormValues } from "./schema";

export function Logo() {
const { watch, setValue } = useFormContext<InvoiceFormValues>();
const logoUrl = watch("logoUrl");
const { uploadFile, isLoading } = useUpload();
const { toast } = useToast();

const handleUpload = async (event: React.ChangeEvent<HTMLInputElement>) => {
const file = event.target.files?.[0];
if (file) {
try {
const { url } = await uploadFile({
file,
path: ["dd6a039e-d071-423a-9a4d-9ba71325d890", "invoice", file.name],
bucket: "avatars",
});

setValue("logoUrl", url, { shouldValidate: true });
} catch (error) {
toast({
title: "Something went wrong, please try again.",
variant: "error",
});
}
}
};

return (
<div className="size-[78px] bg-[repeating-linear-gradient(-60deg,#DBDBDB,#DBDBDB_1px,background_1px,background_5px)] dark:bg-[repeating-linear-gradient(-60deg,#2C2C2C,#2C2C2C_1px,background_1px,background_5px)]" />
<div className="relative size-[78px]">
<label htmlFor="logo-upload" className="absolute inset-0">
{isLoading ? (
<Skeleton className="w-full h-full" />
) : logoUrl ? (
<img
src={logoUrl}
alt="Invoice logo"
className="w-full h-full object-cover"
/>
) : (
<div className="size-[78px] bg-[repeating-linear-gradient(-60deg,#DBDBDB,#DBDBDB_1px,background_1px,background_5px)] dark:bg-[repeating-linear-gradient(-60deg,#2C2C2C,#2C2C2C_1px,background_1px,background_5px)]" />
)}
</label>

<input
id="logo-upload"
type="file"
accept="image/*"
className="hidden"
onChange={handleUpload}
disabled={isLoading}
/>
</div>
);
}
6 changes: 2 additions & 4 deletions apps/dashboard/src/components/invoice/note-content.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
"use client";

import { Editor } from "@/components/editor";
import { LabelInput } from "./label-input";

export function NoteContent() {
return (
<div>
<span className="font-mono text-[#878787] mb-2 text-[11px] block">
Note
</span>

<LabelInput name="settings.note" className="mb-2 block" />
<Editor className="h-[78px]" />
</div>
);
Expand Down
6 changes: 2 additions & 4 deletions apps/dashboard/src/components/invoice/payment-details.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
"use client";

import { Editor } from "@/components/editor";
import { LabelInput } from "./label-input";

export function PaymentDetails() {
return (
<div>
<span className="font-mono text-[#878787] mb-2 text-[11px] block">
Payment details
</span>

<LabelInput name="settings.paymentDetails" className="mb-2 block" />
<Editor className="h-[78px]" />
</div>
);
Expand Down
Loading

0 comments on commit 4c6583c

Please sign in to comment.