Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix form bugs #209

Merged
merged 6 commits into from
Oct 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions dashboard/15-final/app/dashboard/invoices/[id]/edit/page.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { fetchInvoiceById, fetchCustomerNames } from '@/app/lib/data';
import { notFound } from 'next/navigation';
import Form from '@/app/ui/invoices/edit-form';
import Breadcrumbs from '@/app/ui/invoices/breadcrumbs';
import { fetchInvoiceById, fetchCustomers } from '@/app/lib/data';
import { notFound } from 'next/navigation';

export default async function Page({ params }: { params: { id: string } }) {
const id = params.id;
const invoice = await fetchInvoiceById(id);
const customerNames = await fetchCustomerNames();
const customers = await fetchCustomers();

if (!invoice) {
notFound();
Expand All @@ -24,7 +24,7 @@ export default async function Page({ params }: { params: { id: string } }) {
},
]}
/>
<Form invoice={invoice} customerNames={customerNames} id={id} />
<Form invoice={invoice} customers={customers} />
</main>
);
}
6 changes: 3 additions & 3 deletions dashboard/15-final/app/dashboard/invoices/create/page.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { fetchCustomerNames } from '@/app/lib/data';
import { fetchCustomers } from '@/app/lib/data';
import Form from '@/app/ui/invoices/create-form';
import Breadcrumbs from '@/app/ui/invoices/breadcrumbs';

export default async function Page() {
const customerNames = await fetchCustomerNames();
const customers = await fetchCustomers();

return (
<main>
Expand All @@ -17,7 +17,7 @@ export default async function Page() {
},
]}
/>
<Form customerNames={customerNames} />
<Form customers={customers} />
</main>
);
}
13 changes: 7 additions & 6 deletions dashboard/15-final/app/lib/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,15 +58,16 @@ export async function createInvoice(prevState: State, formData: FormData) {
INSERT INTO invoices (customer_id, amount, status, date)
VALUES (${customerId}, ${amountInCents}, ${status}, ${date})
`;

revalidatePath('/dashboard/invoices');
redirect('/dashboard/invoices');
} catch (error) {
// If a database error occurs, return a more specific error.
return {
message: 'Database error: Failed to create invoice.',
};
}

// Revalidate cache and redirect user to invoices page
revalidatePath('/dashboard/invoices');
redirect('/dashboard/invoices');
}

export async function updateInvoice(prevState: State, formData: FormData) {
Expand All @@ -93,12 +94,12 @@ export async function updateInvoice(prevState: State, formData: FormData) {
SET customer_id = ${customerId}, amount = ${amountInCents}, status = ${status}
WHERE id = ${id}
`;

revalidatePath('/dashboard/invoices');
redirect('/dashboard/invoices');
} catch (error) {
return { message: 'Database error: Failed to update invoice.' };
}

revalidatePath('/dashboard/invoices');
redirect('/dashboard/invoices');
}

export async function deleteInvoice(formData: FormData) {
Expand Down
17 changes: 8 additions & 9 deletions dashboard/15-final/app/lib/data.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { sql } from '@vercel/postgres';
import { formatCurrency } from './utils';
import {
Revenue,
InvoicesTable,
CustomerField,
CustomersTable,
InvoiceForm,
CustomerName,
InvoicesTable,
LatestInvoiceRaw,
Revenue,
} from './definitions';
import { formatCurrency } from './utils';

export async function fetchRevenue() {
try {
Expand Down Expand Up @@ -141,11 +141,10 @@ export async function fetchInvoiceById(id: string) {
const data = await sql<InvoiceForm>`
SELECT
invoices.id,
invoices.customer_id,
invoices.amount,
invoices.status,
customers.name
invoices.status
FROM invoices
JOIN customers ON invoices.customer_id = customers.id
WHERE invoices.id = ${id};
`;

Expand All @@ -162,9 +161,9 @@ export async function fetchInvoiceById(id: string) {
}
}

export async function fetchCustomerNames() {
export async function fetchCustomers() {
try {
const data = await sql<CustomerName>`
const data = await sql<CustomerField>`
SELECT
id,
name
Expand Down
4 changes: 2 additions & 2 deletions dashboard/15-final/app/lib/definitions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,14 +75,14 @@ export type FormattedCustomersTable = {
total_paid: string;
};

export type CustomerName = {
export type CustomerField = {
id: string;
name: string;
};

export type InvoiceForm = {
id: string;
name: string;
customer_id: string;
amount: number;
status: 'pending' | 'paid';
};
19 changes: 7 additions & 12 deletions dashboard/15-final/app/ui/invoices/create-form.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use client';

import { CustomerName } from '@/app/lib/definitions';
import { CustomerField } from '@/app/lib/definitions';
import Link from 'next/link';
import {
CheckIcon,
Expand All @@ -13,11 +13,7 @@ import { createInvoice } from '@/app/lib/actions';
// @ts-ignore React types do not yet include useFormState
import { experimental_useFormState as useFormState } from 'react-dom';

export default function Form({
customerNames,
}: {
customerNames: CustomerName[];
}) {
export default function Form({ customers }: { customers: CustomerField[] }) {
const initialState = { message: null, errors: [] };
const [state, dispatch] = useFormState(createInvoice, initialState);

Expand All @@ -40,9 +36,9 @@ export default function Form({
<option value="" disabled>
Select a customer
</option>
{customerNames.map((name) => (
<option key={name.id} value={name.id}>
{name.name}
{customers.map((customer) => (
<option key={customer.id} value={customer.id}>
{customer.name}
</option>
))}
</select>
Expand Down Expand Up @@ -73,12 +69,11 @@ export default function Form({
id="amount"
name="amount"
type="number"
step="0.01"
placeholder="Enter USD amount"
className="peer block w-full rounded-md border border-gray-200 py-2 pl-10 text-sm outline-2 placeholder:text-gray-500"
aria-describedby="amount-error"
style={
{ '-moz-appearance': 'textfield' } as React.CSSProperties
}
style={{ MozAppearance: 'textfield' } as React.CSSProperties}
/>
<CurrencyDollarIcon className="pointer-events-none absolute left-3 top-1/2 h-[18px] w-[18px] -translate-y-1/2 text-gray-500 peer-focus:text-gray-900" />
</div>
Expand Down
24 changes: 11 additions & 13 deletions dashboard/15-final/app/ui/invoices/edit-form.tsx
Original file line number Diff line number Diff line change
@@ -1,33 +1,33 @@
'use client';

import { CustomerName, InvoiceForm } from '@/app/lib/definitions';
import Link from 'next/link';
import { CustomerField, InvoiceForm } from '@/app/lib/definitions';
import {
CheckIcon,
ClockIcon,
CurrencyDollarIcon,
UserCircleIcon,
} from '@heroicons/react/24/outline';
import Link from 'next/link';
import { Button } from '../button';
import { updateInvoice } from '@/app/lib/actions';
// @ts-ignore React types do not yet include useFormState
import { experimental_useFormState as useFormState } from 'react-dom';

export default function EditInvoiceForm({
id,
invoice,
customerNames,
customers,
}: {
id: string;
invoice: InvoiceForm;
customerNames: CustomerName[];
customers: CustomerField[];
}) {
const initialState = { message: null, errors: [] };
const [state, dispatch] = useFormState(updateInvoice, initialState);

return (
<form action={dispatch}>
<div className="rounded-md bg-gray-50 p-4 md:p-6">
{/* Invoice ID */}
<input type="hidden" name="id" value={invoice.id} />
{/* Customer Name */}
<div className="mb-4">
<label htmlFor="customer" className="mb-2 block text-sm font-medium">
Expand All @@ -38,15 +38,15 @@ export default function EditInvoiceForm({
id="customer"
name="customerId"
className="peer block w-full rounded-md border border-gray-200 py-2 pl-10 text-sm outline-2 placeholder:text-gray-500"
defaultValue={invoice.name}
defaultValue={invoice.customer_id}
aria-describedby="customer-error"
>
<option value="" disabled>
Select a customer
</option>
{customerNames.map((name) => (
<option key={name.id} value={name.id}>
{name.name}
{customers.map((customer) => (
<option key={customer.id} value={customer.id}>
{customer.name}
</option>
))}
</select>
Expand Down Expand Up @@ -81,9 +81,7 @@ export default function EditInvoiceForm({
placeholder="Enter USD amount"
className="peer block w-full rounded-md border border-gray-200 py-2 pl-10 text-sm outline-2 placeholder:text-gray-500"
aria-describedby="amount-error"
style={
{ '-moz-appearance': 'textfield' } as React.CSSProperties
}
style={{ MozAppearance: 'textfield' } as React.CSSProperties}
/>
<CurrencyDollarIcon className="pointer-events-none absolute left-3 top-1/2 h-[18px] w-[18px] -translate-y-1/2 text-gray-500 peer-focus:text-gray-900" />
</div>
Expand Down
Loading