Skip to content

Commit

Permalink
final:ultimos ajustes
Browse files Browse the repository at this point in the history
  • Loading branch information
PhelipeG committed Dec 5, 2023
1 parent df6f964 commit 9ef8d83
Show file tree
Hide file tree
Showing 33 changed files with 730 additions and 83 deletions.
5 changes: 5 additions & 0 deletions app/dashboard/(overview)/loading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import DashboardSkeleton from "../../ui/skeletons";

export default function Loading() {
return <DashboardSkeleton/>
}
32 changes: 32 additions & 0 deletions app/dashboard/(overview)/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import CardWrapper from '@/app/ui/dashboard/cards';
import RevenueChart from '@/app/ui/dashboard/revenue-chart';
import LatestInvoices from '@/app/ui/dashboard/latest-invoices';
import { lusitana } from '@/app/ui/fonts';
import { fetchCardData } from '../../lib/data';
import { Suspense } from 'react';
import { CardsSkeleton, LatestInvoicesSkeleton, RevenueChartSkeleton } from '@/app/ui/skeletons';

export default async function Page() {

const { totalPaidInvoices, numberOfCustomers, totalPendingInvoices, numberOfInvoices } = await fetchCardData()
return (
<main>
<h1 className={`${lusitana.className} mb-4 text-xl md:text-2xl`}>
Dashboard
</h1>
<div className="grid gap-6 sm:grid-cols-2 lg:grid-cols-4">
<Suspense fallback={<CardsSkeleton />}>
<CardWrapper />
</Suspense>
</div>
<div className="mt-6 grid grid-cols-1 gap-6 md:grid-cols-4 lg:grid-cols-8">
<Suspense fallback={<RevenueChartSkeleton />}>
<RevenueChart />
</Suspense>
<Suspense fallback={<LatestInvoicesSkeleton />}>
<LatestInvoices />
</Suspense>
</div>
</main>
);
}
18 changes: 18 additions & 0 deletions app/dashboard/invoices/[id]/edit/not-found.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import Link from 'next/link';
import { FaceFrownIcon } from '@heroicons/react/24/outline';

export default function NotFound() {
return (
<main className="flex h-full flex-col items-center justify-center gap-2">
<FaceFrownIcon className="w-10 text-gray-400" />
<h2 className="text-xl font-semibold">404 Not Found</h2>
<p>Recurso nao encontrado ou nao existe !</p>
<Link
href="/dashboard/invoices"
className="mt-4 rounded-md bg-blue-500 px-4 py-2 text-sm text-white transition-colors hover:bg-blue-400"
>
Voltar
</Link>
</main>
);
}
33 changes: 33 additions & 0 deletions app/dashboard/invoices/[id]/edit/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import Form from '@/app/ui/invoices/edit-form';
import Breadcrumbs from '@/app/ui/invoices/breadcrumbs';
import { fetchCustomers, fetchInvoiceById } from '@/app/lib/data';
import { notFound } from 'next/navigation';

export default async function Page({ params }: { params: { id: string } }) {
const id = params.id;

const [invoice, customers] = await Promise.all([
fetchInvoiceById(id),
fetchCustomers(),
]);

if (!invoice) { //se nao existir fatura com o id existente na base de dados
return notFound();
}

return (
<main>
<Breadcrumbs
breadcrumbs={[
{ label: 'Invoices', href: '/dashboard/invoices' },
{
label: 'Edit Invoice',
href: `/dashboard/invoices/${id}/edit`,
active: true,
},
]}
/>
<Form invoice={invoice} customers={customers} />
</main>
);
}
23 changes: 23 additions & 0 deletions app/dashboard/invoices/create/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import Form from '@/app/ui/invoices/create-form';
import Breadcrumbs from '@/app/ui/invoices/breadcrumbs';
import { fetchCustomers } from '@/app/lib/data';

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

return (
<main>
<Breadcrumbs
breadcrumbs={[
{ label: 'Invoices', href: '/dashboard/invoices' },
{
label: 'Create Invoice',
href: '/dashboard/invoices/create',
active: true,
},
]}
/>
<Form customers={customers} />
</main>
);
}
30 changes: 30 additions & 0 deletions app/dashboard/invoices/error.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
'use client'; //error.tsxprecisa ser um componente cliente.

import { useEffect } from 'react';

export default function Error({
error,
reset,
}: {
error: Error & { digest?: string };
reset: () => void;
}) {
useEffect(() => {
// Optionally log the error to an error reporting service
console.error(error);
}, [error]);

return (
<main className="flex h-full flex-col items-center justify-center">
<h2 className="text-center">Something went wrong!</h2>
<button
className="mt-4 rounded-md bg-blue-500 px-4 py-2 text-sm text-white transition-colors hover:bg-blue-400"
onClick={
() => reset() //reset: Esta é uma função para redefinir o limite de erro. Quando executada, a função tentará renderizar novamente o segmento da rota.
}
>
Try again
</button>
</main>
);
}
39 changes: 37 additions & 2 deletions app/dashboard/invoices/page.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,38 @@
export default function Page() {
return <p>Invoices Page</p>;
import Pagination from '@/app/ui/invoices/pagination';
import Search from '@/app/ui/search';
import Table from '@/app/ui/invoices/table';
import { CreateInvoice } from '@/app/ui/invoices/buttons';
import { lusitana } from '@/app/ui/fonts';
import { InvoicesTableSkeleton } from '@/app/ui/skeletons';
import { Suspense } from 'react';
import { fetchInvoicesPages } from '@/app/lib/data';

export default async function Page({ searchParams, }: {
searchParams?: {
query?: string,
page?: string
}
}) {
const query = searchParams?.query || '';
const currentPage = Number(searchParams?.page) || 1;

const totalPages = await fetchInvoicesPages(query);

return (
<div className="w-full">
<div className="flex w-full items-center justify-between">
<h1 className={`${lusitana.className} text-2xl`}>Invoices</h1>
</div>
<div className="mt-4 flex items-center justify-between gap-2 md:mt-8">
<Search placeholder="Pesquisar Faturas" />
<CreateInvoice />
</div>
<Suspense key={query + currentPage} fallback={<InvoicesTableSkeleton />}>
<Table query={query} currentPage={currentPage} />
</Suspense>
<div className="mt-5 flex w-full justify-center">
<Pagination totalPages={totalPages} />
</div>
</div>
);
}
32 changes: 0 additions & 32 deletions app/dashboard/page.tsx

This file was deleted.

14 changes: 13 additions & 1 deletion app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,25 @@
import '@/app/ui/global.css';
import { inter } from '@/app/ui/fonts';
import { Metadata} from 'next'

export const meta: Metadata = {
title: 'FaturaNow',
applicationName: 'FaturaNow',
description: 'Aplicacao de controle de faturas',
viewport: 'width=device-width, initial-scale=1',
referrer: 'origin-when-cross-origin',
creator:'Luis Felipe G Silva',
publisher:'Luis Felipe G Silva',
keywords: ['FaturaNow, Controle de Faturas, Controle de Gastos, Controle de Contas'],
};

export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<html lang="pt-Br">
<body className={`${inter.className} antialiased`}>{children}</body>
</html>
);
Expand Down
123 changes: 123 additions & 0 deletions app/lib/action.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
'use server';

import { sql } from '@vercel/postgres';
import { z } from 'zod';
import { revalidatePath } from 'next/cache';
import { redirect } from 'next/navigation';
import { signIn } from '@/auth';
import { AuthError } from 'next-auth';

export async function authenticate(
prevState: string | undefined,
formData: FormData,
) {
try {
await signIn('credentials', formData);
} catch (error) {
if (error instanceof AuthError) {
switch (error.type) {
case 'CredentialsSignin':
return 'Invalid credentials.';
default:
return 'Something went wrong.';
}
}
throw error;
}
}

//criando schema object
const FormSchema = z.object({
id: z.string(),
customerId: z.string({
invalid_type_error : 'Porfavor selecione um cliente.'
}),
amount: z.coerce.number().gt(0, { message : 'Dgite um valor maior que 0'}),
status: z.enum(['pending', 'paid'],{
invalid_type_error: 'Porfavor selecione um status.'

}),
date: z.string(),
});
//Tipagem do State
export type State = {
errors?: {
customerId?: string[];
amount?: string[];
status?: string[];
};
message?: string | null;
};
const CreateInvoice = FormSchema.omit({ id: true, date: true });

export async function createInvoice(prevState: State, formData: FormData) {
const validatedFields = CreateInvoice.safeParse({ //safeParse()retornará um objeto contendo um campo successou error.
customerId: formData.get('customerId'),
amount: formData.get('amount'),
status: formData.get('status'),
});

//verifcando se foram validados corretamente
if (!validatedFields.success) {
return {
errors: validatedFields.error.flatten().fieldErrors,
message: 'Missing Fields. Failed to Create Invoice.',
};
}
//preparando para mandar os dados para o banco de dados
const { customerId, amount, status } = validatedFields.data;
const amountInCents = amount * 100;
const date = new Date().toISOString().split('T')[0];

try {
//inserindo os novos dados no banco de dados
await sql`
INSERT INTO invoices (customer_id, amount, status, date)
VALUES (${customerId}, ${amountInCents}, ${status}, ${date})`;
} catch(error) {
console.error('Database Error:', error);
throw new Error('Failed to create invoice.');

}
/*
Assim que o banco de dados for atualizado, o /dashboard/invoices
caminho será revalidado e novos dados serão obtidos do servidor.
*/
revalidatePath('/dashboard/invoices');
redirect('/dashboard/invoices');
}


// Use Zod to update the expected types
const UpdateInvoice = FormSchema.omit({ id: true, date: true });

export async function updateInvoice(id: string, formData: FormData) {
const { customerId, amount, status } = UpdateInvoice.parse({
customerId: formData.get('customerId'),
amount: formData.get('amount'),
status: formData.get('status'),
});

const amountInCents = amount * 100;
try {
await sql`
UPDATE invoices
SET customer_id = ${customerId}, amount = ${amountInCents}, status = ${status}
WHERE id = ${id}
`;
} catch (error) {
console.error('Database Error:', error);
throw new Error('Failed to update invoice.');
}
revalidatePath('/dashboard/invoices');
redirect('/dashboard/invoices');
}
export async function deleteInvoice(id: string) {
try {
await sql`DELETE FROM invoices WHERE id = ${id}`;
revalidatePath('/dashboard/invoices');
return { message: 'Deleted Invoice.' };
} catch (error) {
return { message: 'Database Error: Failed to Delete Invoice.' };
}
}
Loading

0 comments on commit 9ef8d83

Please sign in to comment.