Skip to content

Commit

Permalink
Merge branch 'main' into patch-1
Browse files Browse the repository at this point in the history
  • Loading branch information
delbaoliveira authored Nov 16, 2023
2 parents 61d6418 + fc5605c commit 8f2522d
Show file tree
Hide file tree
Showing 19 changed files with 184 additions and 204 deletions.
2 changes: 1 addition & 1 deletion dashboard/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
## Next.js App Router Course - Build a Dashboard
# Next.js App Router Course - Build a Dashboard

This repository contains the starter templates for the [Next.js App Router Course](https://nextjs.org/learn), separated by chapters.

Expand Down
2 changes: 1 addition & 1 deletion dashboard/final-example/app/lib/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ export async function fetchFilteredCustomers(query: string) {

export async function getUser(email: string) {
try {
const user = await sql`SELECT * from USERS where email=${email}`;
const user = await sql`SELECT * FROM users WHERE email=${email}`;
return user.rows[0] as User;
} catch (error) {
console.error('Failed to fetch user:', error);
Expand Down
4 changes: 2 additions & 2 deletions dashboard/final-example/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import AcmeLogo from '@/app/ui/acme-logo';
import { ArrowRightIcon } from '@heroicons/react/24/outline';
import Link from 'next/link';
import { lusitana } from '@/app/ui/fonts';
import Image from 'next/image';
import Link from 'next/link';

export default function Page() {
return (
Expand Down Expand Up @@ -34,7 +34,7 @@ export default function Page() {
src="/hero-desktop.png"
width={1000}
height={760}
alt="Screenshots of the dashboard project showing desktop and mobile versions"
alt="Screenshots of the dashboard project showing desktop version"
className="hidden md:block"
/>
<Image
Expand Down
2 changes: 1 addition & 1 deletion dashboard/final-example/app/ui/dashboard/sidenav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export default function SideNav() {
await signOut();
}}
>
<button className="flex h-[48px] grow items-center justify-center gap-2 rounded-md bg-gray-50 p-3 text-sm font-medium hover:bg-sky-100 hover:text-blue-600 md:flex-none md:justify-start md:p-2 md:px-3">
<button className="flex h-[48px] w-full grow items-center justify-center gap-2 rounded-md bg-gray-50 p-3 text-sm font-medium hover:bg-sky-100 hover:text-blue-600 md:flex-none md:justify-start md:p-2 md:px-3">
<PowerIcon className="w-6" />
<div className="hidden md:block">Sign Out</div>
</button>
Expand Down
71 changes: 31 additions & 40 deletions dashboard/final-example/app/ui/invoices/create-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export default function Form({ customers }: { customers: CustomerField[] }) {
<select
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"
className="peer block w-full cursor-pointer rounded-md border border-gray-200 py-2 pl-10 text-sm outline-2 placeholder:text-gray-500"
defaultValue=""
aria-describedby="customer-error"
>
Expand All @@ -44,17 +44,14 @@ export default function Form({ customers }: { customers: CustomerField[] }) {
<UserCircleIcon className="pointer-events-none absolute left-3 top-1/2 h-[18px] w-[18px] -translate-y-1/2 text-gray-500" />
</div>

{state.errors?.customerId ? (
<div
id="customer-error"
aria-live="polite"
className="mt-2 text-sm text-red-500"
>
{state.errors.customerId.map((error: string) => (
<p key={error}>{error}</p>
<div id="customer-error" aria-live="polite" aria-atomic="true">
{state.errors?.customerId &&
state.errors.customerId.map((error: string) => (
<p className="mt-2 text-sm text-red-500" key={error}>
{error}
</p>
))}
</div>
) : null}
</div>
</div>

{/* Invoice Amount */}
Expand All @@ -77,17 +74,14 @@ export default function Form({ customers }: { customers: CustomerField[] }) {
</div>
</div>

{state.errors?.amount ? (
<div
id="amount-error"
aria-live="polite"
className="mt-2 text-sm text-red-500"
>
{state.errors.amount.map((error: string) => (
<p key={error}>{error}</p>
<div id="amount-error" aria-live="polite" aria-atomic="true">
{state.errors?.amount &&
state.errors.amount.map((error: string) => (
<p className="mt-2 text-sm text-red-500" key={error}>
{error}
</p>
))}
</div>
) : null}
</div>
</div>

{/* Invoice Status */}
Expand All @@ -103,11 +97,11 @@ export default function Form({ customers }: { customers: CustomerField[] }) {
name="status"
type="radio"
value="pending"
className="h-4 w-4 border-gray-300 bg-gray-100 text-gray-600 focus:ring-2 focus:ring-gray-500 dark:border-gray-600 dark:bg-gray-700 dark:ring-offset-gray-800 dark:focus:ring-gray-600"
className="h-4 w-4 cursor-pointer border-gray-300 bg-gray-100 text-gray-600 focus:ring-2 focus:ring-gray-500 dark:border-gray-600 dark:bg-gray-700 dark:ring-offset-gray-800 dark:focus:ring-gray-600"
/>
<label
htmlFor="pending"
className="ml-2 flex items-center gap-1.5 rounded-full bg-gray-100 px-3 py-1.5 text-xs font-medium text-gray-600 dark:text-gray-300"
className="ml-2 flex cursor-pointer items-center gap-1.5 rounded-full bg-gray-100 px-3 py-1.5 text-xs font-medium text-gray-600 dark:text-gray-300"
>
Pending <ClockIcon className="h-4 w-4" />
</label>
Expand All @@ -118,35 +112,32 @@ export default function Form({ customers }: { customers: CustomerField[] }) {
name="status"
type="radio"
value="paid"
className="h-4 w-4 border-gray-300 bg-gray-100 text-gray-600 focus:ring-2 focus:ring-gray-500 dark:border-gray-600 dark:bg-gray-700 dark:ring-offset-gray-800 dark:focus:ring-gray-600"
className="h-4 w-4 cursor-pointer border-gray-300 bg-gray-100 text-gray-600 focus:ring-2 focus:ring-gray-500 dark:border-gray-600 dark:bg-gray-700 dark:ring-offset-gray-800 dark:focus:ring-gray-600"
/>
<label
htmlFor="paid"
className="ml-2 flex items-center gap-1.5 rounded-full bg-green-500 px-3 py-1.5 text-xs font-medium text-white dark:text-gray-300"
className="ml-2 flex cursor-pointer items-center gap-1.5 rounded-full bg-green-500 px-3 py-1.5 text-xs font-medium text-white dark:text-gray-300"
>
Paid <CheckIcon className="h-4 w-4" />
</label>
</div>
</div>
</div>
{state.errors?.status ? (
<div
aria-describedby="status-error"
aria-live="polite"
className="mt-2 text-sm text-red-500"
>
{state.errors.status.map((error: string) => (
<p key={error}>{error}</p>
<div id="status-error" aria-live="polite" aria-atomic="true">
{state.errors?.status &&
state.errors.status.map((error: string) => (
<p className="mt-2 text-sm text-red-500" key={error}>
{error}
</p>
))}
</div>
) : null}
</div>
</fieldset>

{state.message ? (
<div aria-live="polite" className="my-2 text-sm text-red-500">
<p>{state.message}</p>
</div>
) : null}
<div aria-live="polite" aria-atomic="true">
{state.message ? (
<p className="mt-2 text-sm text-red-500">{state.message}</p>
) : null}
</div>
</div>
<div className="mt-6 flex justify-end gap-4">
<Link
Expand Down
71 changes: 31 additions & 40 deletions dashboard/final-example/app/ui/invoices/edit-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export default function EditInvoiceForm({
<select
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"
className="peer block w-full cursor-pointer rounded-md border border-gray-200 py-2 pl-10 text-sm outline-2 placeholder:text-gray-500"
defaultValue={invoice.customer_id}
aria-describedby="customer-error"
>
Expand All @@ -51,17 +51,14 @@ export default function EditInvoiceForm({
<UserCircleIcon className="pointer-events-none absolute left-3 top-1/2 h-[18px] w-[18px] -translate-y-1/2 text-gray-500" />
</div>

{state.errors?.customerId ? (
<div
id="customer-error"
aria-live="polite"
className="mt-2 text-sm text-red-500"
>
{state.errors.customerId.map((error: string) => (
<p key={error}>{error}</p>
<div id="customer-error" aria-live="polite" aria-atomic="true">
{state.errors?.customerId &&
state.errors.customerId.map((error: string) => (
<p className="mt-2 text-sm text-red-500" key={error}>
{error}
</p>
))}
</div>
) : null}
</div>
</div>

{/* Invoice Amount */}
Expand All @@ -84,17 +81,14 @@ export default function EditInvoiceForm({
</div>
</div>

{state.errors?.amount ? (
<div
id="amount-error"
aria-live="polite"
className="mt-2 text-sm text-red-500"
>
{state.errors.amount.map((error: string) => (
<p key={error}>{error}</p>
<div id="amount-error" aria-live="polite" aria-atomic="true">
{state.errors?.amount &&
state.errors.amount.map((error: string) => (
<p className="mt-2 text-sm text-red-500" key={error}>
{error}
</p>
))}
</div>
) : null}
</div>
</div>

{/* Invoice Status */}
Expand All @@ -111,11 +105,11 @@ export default function EditInvoiceForm({
type="radio"
value="pending"
defaultChecked={invoice.status === 'pending'}
className="h-4 w-4 border-gray-300 bg-gray-100 text-gray-600 focus:ring-2 focus:ring-gray-500 dark:border-gray-600 dark:bg-gray-700 dark:ring-offset-gray-800 dark:focus:ring-gray-600"
className="h-4 w-4 cursor-pointer border-gray-300 bg-gray-100 text-gray-600 focus:ring-2 focus:ring-gray-500 dark:border-gray-600 dark:bg-gray-700 dark:ring-offset-gray-800 dark:focus:ring-gray-600"
/>
<label
htmlFor="pending"
className="ml-2 flex items-center gap-1.5 rounded-full bg-gray-100 px-3 py-1.5 text-xs font-medium text-gray-600 dark:text-gray-300"
className="ml-2 flex cursor-pointer items-center gap-1.5 rounded-full bg-gray-100 px-3 py-1.5 text-xs font-medium text-gray-600 dark:text-gray-300"
>
Pending <ClockIcon className="h-4 w-4" />
</label>
Expand All @@ -127,35 +121,32 @@ export default function EditInvoiceForm({
type="radio"
value="paid"
defaultChecked={invoice.status === 'paid'}
className="h-4 w-4 border-gray-300 bg-gray-100 text-gray-600 focus:ring-2 focus:ring-gray-500 dark:border-gray-600 dark:bg-gray-700 dark:ring-offset-gray-800 dark:focus:ring-gray-600"
className="h-4 w-4 cursor-pointer border-gray-300 bg-gray-100 text-gray-600 focus:ring-2 focus:ring-gray-500 dark:border-gray-600 dark:bg-gray-700 dark:ring-offset-gray-800 dark:focus:ring-gray-600"
/>
<label
htmlFor="paid"
className="ml-2 flex items-center gap-1.5 rounded-full bg-green-500 px-3 py-1.5 text-xs font-medium text-white dark:text-gray-300"
className="ml-2 flex cursor-pointer items-center gap-1.5 rounded-full bg-green-500 px-3 py-1.5 text-xs font-medium text-white dark:text-gray-300"
>
Paid <CheckIcon className="h-4 w-4" />
</label>
</div>
</div>
</div>
{state.errors?.status ? (
<div
aria-describedby="status-error"
aria-live="polite"
className="mt-2 text-sm text-red-500"
>
{state.errors.status.map((error: string) => (
<p key={error}>{error}</p>
<div id="status-error" aria-live="polite" aria-atomic="true">
{state.errors?.status &&
state.errors.status.map((error: string) => (
<p className="mt-2 text-sm text-red-500" key={error}>
{error}
</p>
))}
</div>
) : null}
</div>
</fieldset>

{state.message ? (
<div aria-live="polite" className="my-2 text-sm text-red-500">
<p>{state.message}</p>
</div>
) : null}
<div aria-live="polite" aria-atomic="true">
{state.message ? (
<p className="my-2 text-sm text-red-500">{state.message}</p>
) : null}
</div>
</div>
<div className="mt-6 flex justify-end gap-4">
<Link
Expand Down
17 changes: 9 additions & 8 deletions dashboard/final-example/app/ui/login-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,10 @@ import { Button } from './button';
import { useFormState, useFormStatus } from 'react-dom';

export default function LoginForm() {
const [code, action] = useFormState(authenticate, undefined);
const { pending } = useFormStatus();
const [state, dispatch] = useFormState(authenticate, undefined);

return (
<form action={action} className="space-y-3">
<form action={dispatch} className="space-y-3">
<div className="flex-1 rounded-lg bg-gray-50 px-6 pb-4 pt-8">
<h1 className={`${lusitana.className} mb-3 text-2xl`}>
Please log in to continue.
Expand Down Expand Up @@ -63,13 +62,15 @@ export default function LoginForm() {
</div>
</div>
<LoginButton />
<div className="flex h-8 items-end space-x-1">
{code === 'CredentialsSignin' && (
<div
className="flex h-8 items-end space-x-1"
aria-live="polite"
aria-atomic="true"
>
{state === 'CredentialsSignin' && (
<>
<ExclamationCircleIcon className="h-5 w-5 text-red-500" />
<p aria-live="polite" className="text-sm text-red-500">
Invalid credentials
</p>
<p className="text-sm text-red-500">Invalid credentials</p>
</>
)}
</div>
Expand Down
2 changes: 1 addition & 1 deletion dashboard/final-example/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { authConfig } from './auth.config';

async function getUser(email: string): Promise<User | undefined> {
try {
const user = await sql<User>`SELECT * from USERS where email=${email}`;
const user = await sql<User>`SELECT * FROM users WHERE email=${email}`;
return user.rows[0];
} catch (error) {
console.error('Failed to fetch user:', error);
Expand Down
Loading

0 comments on commit 8f2522d

Please sign in to comment.