ejemplo next 12

main
Freddy Heredia 2 years ago
parent 828be268a2
commit 758ff47a80

@ -0,0 +1 @@
API_URL=

@ -0,0 +1 @@
API_URL=http://localhost:8082

@ -1,3 +1,12 @@
Crear el proyecto:
npx create-next-app NombreProyecto
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
## Getting Started

@ -0,0 +1,36 @@
import { FC } from 'react'
interface Props {
title:string,
onClick?(): void,
type?:"button" | "submit" | "reset" | undefined
style?: "primary" | "secundary" | "info"
}
export const Button:FC <Props> = ({title,onClick,type='button', style="primary"}) => {
let className = "rounded-3xl m-2 px-4 py-2 shadow-md hover:shadow-xl uppercase";
switch (style) {
case "primary":
className = className + " bg-sky-300 hover:bg-sky-400"
break;
case "secundary":
className = className + " hover:bg-sky-200"
break;
default:
className = className + " bg-sky-300 hover:bg-sky-400"
}
return (
<>
<button
onClick={onClick}
type={type}
className={className}>
{title}
</button>
</>
)
}

@ -0,0 +1,26 @@
import React from 'react';
import { FC } from 'react'
import { UseFormRegister,FieldValues } from 'react-hook-form';
interface Props {
id: string,
label: string,
type?:"text"|"password"|"checkbox"|"hidden"|"number"|"date"|"datetime-local",
register: UseFormRegister<any>
}
export const Input:FC <Props> = ({id,label,type="text", register}) => {
return (
<>
<div className='flex flex-col'>
<label htmlFor={id}>{label}</label>
<input
type={type}
className='p-2 m-2 border rounded-xl focus:outline-offset-4 focus:outline-sky-500 focus:border-solid border-sky-500'
id={id}
{...register(id)}/>
</div>
</>
)
}

@ -0,0 +1,55 @@
import React, { FC, useEffect, useState } from 'react'
import { FieldValues, Controller, Control } from 'react-hook-form';
import { useFetchWithAuth } from '../hooks/useFetchWithAuth';
interface Props {
entity: string,
control: Control<any, any>
}
export const Manytoone: FC<Props> = ({entity, control }) => {
const [data, setData] = useState([{id:0,nombre:'Select one'}]);
useEffect(() => {
async function fetchData() {
try {
const { data, error } = await useFetchWithAuth(entity.toLowerCase());
setData(data);
} catch (trace) {
console.error(trace);
}
}
fetchData();
}, [entity]);
return (
<>
<label htmlFor={entity}>{entity}</label>
<Controller key={entity}
render={({ field }) => (
<select key={entity+"select"} {...field} className={' p-2 border-2 border-sky-300 outline-sky-300 bg-white rounded-xl w-full'}>
<option key={entity+0} value={0}>
Seleccione un valor
</option>
{data.map((item) => (
<option key={entity+item.id} value={item.id}>
{item['nombre']}
</option>
))}
</select>
)}
name={entity}
control={control}
/>
</>
)
}

@ -0,0 +1,52 @@
import { FC } from 'react'
import Link from 'next/link'
interface Props {
pathForm: string
pathList: string
entityName: string
currentEntity: string
}
export const Toolbar: FC<Props> = ({
pathForm,
pathList,
entityName,
currentEntity,
}) => {
return (
<>
<div className="flex flex-row items-start sm:items-center">
<Link href={pathForm+"?id=0"} >
<button className="my-2 mr-3 font-bold rounded btn btn-outline btn-sm group">
<svg className="w-3 h-3 mr-1 group-hover:animate-bounce group-hover:fill-white"
xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink" version="1.1"
viewBox="0 0 512 512" xmlSpace="preserve">
<g>
<path
d="M480,224H288V32c0-17.673-14.327-32-32-32s-32,14.327-32,32v192H32c-17.673,0-32,14.327-32,32s14.327,32,32,32h192v192 c0,17.673,14.327,32,32,32s32-14.327,32-32V288h192c17.673,0,32-14.327,32-32S497.673,224,480,224z" />
</g>
</svg>
<span className="hidden md:inline">
Nuevo
</span>
</button>
</Link>
<h1 className="flex-1 my-2 text-lg text-left">
<div className="flex-1 text-sm font-bold text-gray-600 breadcrumbs breadflex-1">
<ul>
<li>
<Link href={pathList}>{entityName}</Link>
</li>
<li>
<a>{currentEntity}</a>
</li>
</ul>
</div>
</h1>
</div>
</>
)
}

@ -0,0 +1,54 @@
import React, { FC, useEffect, useState } from 'react'
import { useFetchWithAuth } from '../../hooks/useFetchWithAuth';
import { Control, Controller, FieldValues, useFieldArray, useForm, UseFormRegister } from 'react-hook-form';
interface Props {
attribute: string,
keyRegiter?: any,
control: Control<FieldValues, any>,
}
export const Manytooneform: FC<Props> = ({ attribute, keyRegiter, control }) => {
if (!keyRegiter){
keyRegiter=attribute;
}
const [data, setData] = useState([{id:0,nombre:'Select one', documentNumber: 0} ]);
useEffect(() => {
async function fetchData() {
try {
const { data, error } = await useFetchWithAuth(attribute.toLowerCase());
setData(data);
} catch (error) {
console.error(error);
}
}
fetchData();
}, [attribute]);
return (
<div>
<Controller key={keyRegiter}
render={({ field }) => (
<select key={keyRegiter+"select"} {...field} className="select select-xs select-primary">
<option key={keyRegiter+0} value={0}>
Seleccione un valor
</option>
{data.map((option) => (
<option key={keyRegiter+option.id} value={option.id}>
{option.nombre}
</option>
))}
</select>
)}
name={keyRegiter}
control={control}
/>
</div>
)
}

@ -0,0 +1,112 @@
import React from 'react'
import { useEffect } from 'react';
import { Control, FieldValues, useFieldArray, useForm, UseFormRegister, UseFormSetValue } from 'react-hook-form';
import { useFetchWithAuth } from '../../hooks/useFetchWithAuth';
import { Manytooneform } from './Manytooneform';
import TableHeadForm from './TableHeadForm';
interface Props {
entityName: string,
entityId: number,
element:string,
oneToManyHeader: {id:string, name:string}[],
control: Control<any, any>,
register: UseFormRegister<any>,
setValue: UseFormSetValue<any>
}
export const Onetomany = ({entityName, entityId, element, oneToManyHeader, control, register, setValue }:Props) => {
const { fields, append, remove } = useFieldArray({
control,
name: element
});
const getInitData = async () => {
const { data, error } = await useFetchWithAuth(entityName + "/" + entityId);
if (!error) {
setValue(element,data[element]);
} else {
console.log(error)
}
}
useEffect(() => {
getInitData();
}, [entityId])
const renderElement = (key:string, attribute: string) => {
switch (attribute){
case 'String':
return <input key={key} type='text' className='input input-bordered input-primary input-xs' {...register(key)}/>;
case "ManyToOne":
return <Manytooneform key={key} attribute={ "producto"} keyRegiter={key.concat(".id")} control={control} />
case 'Long':
return <input key={key} type='number' className='input input-bordered input-primary input-xs' {...register(key)}/>;
case 'Boolean':
return <input key={key} type='checkbox' className='toggle toggle-primary toggle-xs' {...register(key)}/>;
case 'BigDecimal':
return <input key={key} type='number' className='input input-bordered input-primary input-xs' {...register(key)}/>;
case 'LocalDateTime':
return <input key={key} type='date' className='input input-bordered input-primary input-xs' {...register(key)}/>;
default:
console.log(key)
return <span key={key}>{key}</span>;
}
}
const columnNames = oneToManyHeader.map(column => (column.id))
let defaultvalue = "";
if (columnNames.length>0){
defaultvalue = columnNames.reduce((acc, curr) => {
acc = "";
return acc;
}, );
}else{
}
return (
<div key={entityName+"div"} className="overflow-x-auto">
<table className="table w-full table-compact">
<TableHeadForm columns={oneToManyHeader}/>
<tbody>
{
fields.map((line, index) => {
return (
<tr key={line['id']}>
{
oneToManyHeader.map(column => (
<td key={line['id']+column.id}>
{renderElement(`${element}.${index}.${column.id}`,column.name)}
</td>
))
}
</tr>
)
})
}
</tbody>
</table>
<button className='btn btn-primary'
type="button"
onClick={() => append(defaultvalue)}
>
append
</button>
</div>
)
}

@ -0,0 +1,22 @@
interface Props {
columns:{id:string, name:string} []
}
export const TableHeadForm = ({ columns }:Props) => {
return (
<thead>
<tr>
{columns.map(
(col => (
<th key={col['id']}>{col['id']}</th>
))
)}
</tr>
</thead>
)
}
export default TableHeadForm

@ -0,0 +1,25 @@
import TableBody from "./TableBody"
import TableHead from "./TableHead"
interface Props {
columns:string[]
endpoint: string
}
const Table = ({ columns, endpoint }:Props) => {
return (
<div className="overflow-x-auto">
<table className="table w-full table-compact">
<TableHead columns={columns} />
<TableBody columns={columns} endpoint={endpoint} />
</table>
</div>
)
}
export default Table

@ -0,0 +1,76 @@
import Link from 'next/link';
import { useEffect, useState } from 'react';
import { useFetchWithAuth } from '../../hooks/useFetchWithAuth';
interface Props {
endpoint: string
columns:string []
}
const TableBody = ({ columns,endpoint }:Props) => {
let defaultData:[] = [];
const [entities, setEntities] = useState(defaultData);
const getInitData = async () => {
const { data, error } = await useFetchWithAuth(endpoint);
if (!error) {
setEntities(data);
} else {
console.log(error)
}
}
useEffect(() => {
getInitData();
}, [endpoint])
return (
<tbody>
{
entities.map((entity) => {
return (
<tr key={entity['id']}>
<td>
<Link
href={{
pathname: endpoint+'/form',
query: { id: entity['id'] },
}}
>
<svg className="w-4 h-4" xmlns="http://www.w3.org/2000/svg" id="Outline" viewBox="0 0 24 24">
<path
d="M18.656.93,6.464,13.122A4.966,4.966,0,0,0,5,16.657V18a1,1,0,0,0,1,1H7.343a4.966,4.966,0,0,0,3.535-1.464L23.07,5.344a3.125,3.125,0,0,0,0-4.414A3.194,3.194,0,0,0,18.656.93Zm3,3L9.464,16.122A3.02,3.02,0,0,1,7.343,17H7v-.343a3.02,3.02,0,0,1,.878-2.121L20.07,2.344a1.148,1.148,0,0,1,1.586,0A1.123,1.123,0,0,1,21.656,3.93Z" />
<path
d="M23,8.979a1,1,0,0,0-1,1V15H18a3,3,0,0,0-3,3v4H5a3,3,0,0,1-3-3V5A3,3,0,0,1,5,2h9.042a1,1,0,0,0,0-2H5A5.006,5.006,0,0,0,0,5V19a5.006,5.006,0,0,0,5,5H16.343a4.968,4.968,0,0,0,3.536-1.464l2.656-2.658A4.968,4.968,0,0,0,24,16.343V9.979A1,1,0,0,0,23,8.979ZM18.465,21.122a2.975,2.975,0,0,1-1.465.8V18a1,1,0,0,1,1-1h3.925a3.016,3.016,0,0,1-.8,1.464Z" />
</svg>
</Link>
</td>
{columns.map(
(col => (
<td key={col}>
<span>{entity[col]}</span>
</td>
))
)}
</tr>
)
})
}
</tbody>
);
};
export default TableBody;

@ -0,0 +1,21 @@
interface Props {
columns:string []
}
export const TableHead = ({ columns }:Props) => {
return (
<thead>
<tr>
<th className="w-4"></th>
{columns.map(
(col => (
<th key={col}>{col.toLocaleUpperCase()}</th>
))
)}
</tr>
</thead>
)
}
export default TableHead

@ -0,0 +1,39 @@
const fecher = async (url: string, token: string) => {
return await fetch(url, {
method: 'POST',
mode: 'cors',
headers: {
'Authorization' :'Basic '+token
},
});
}
export const postBasicAuth = async (url: string, username:string, password: string) => {
//url= process.env.API_URL+url;
const token = Buffer.from(username + ':' + password).toString('base64');
let data;
let error;
try{
const response = await fecher(url, token)
if (response.ok){
const token = response.headers.get('Authorization') ;
if (token){
data = token.replaceAll("Basic ","")
}
}else {
const trace = await response.json();
error = "Servidor: "+ trace.message;
}
}catch (trace){
error = "Cliente: "+trace;
}
return {
data,
error
}
}

@ -0,0 +1,41 @@
import Cookies from 'js-cookie';
const fecher = async (url: string, id:string, token: string, data:any) => {
return await fetch(url, {
method: id==="0" ? 'POST': 'PATCH',
mode: 'cors',
headers: {
'Authorization' :token,
'Content-Type': 'application/json',
'Accept':'application/json'
},
body: data,
});
}
export const saveWithAuth = async (url: string, id:string, body:any) => {
url= process.env.API_URL+"/api/"+url+"/";
if ( Number(id)>0){
url = url + id +"/"
}
const token = Cookies.get('token') || "";
let data;
let error;
try{
data = JSON.stringify(body);
console.log(url)
const response = await fecher(url, id, token, data)
if (response.ok){
data = await response.json();
}else {
error = "Servidor: "+ ((await response.json()).trace).substring(1,300);
}
}catch (e){
error = "Cliente: "+e;
}
return {
data,
error
}
}

@ -0,0 +1,36 @@
import Cookies from 'js-cookie';
const fecher = async (url: string, token: string) => {
return await fetch(url, {
method: "GET",
headers: {
"Content-Type": "application/json",
"Authorization":token
},
});
}
export const useFetchWithAuth = async (url: string) => {
url= process.env.API_URL+"/api/"+url+"/";
const token = Cookies.get('token') || "";
let data;
let error;
try{
const response = await fecher(url, token)
if (response.ok){
data = await response.json();
}else {
console.log(url)
error = "Servidor: "+ ((await response.json()).trace);
}
}catch (e){
error = "Cliente: "+e;
}
return {
data,
error
}
}

@ -0,0 +1,19 @@
// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export async function middleware(request: NextRequest) {
//Validar si la url solicitada está dentro del listado de enlaces permitidos
const token = request.cookies.get('token')?.value
if (!token) {
return NextResponse.rewrite(new URL('/login', request.url))
}
}
// See "Matching Paths" below to learn more
export const config = {
matcher: ['/((?!api|_next/static|_next/image|favicon.ico|login).*)',],
}

@ -2,6 +2,9 @@
const nextConfig = {
reactStrictMode: true,
swcMinify: true,
env: {
API_URL: "http://localhost:8081",
}
}
module.exports = nextConfig

1112
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -12,9 +12,18 @@
"@types/node": "18.15.11",
"@types/react": "18.0.31",
"@types/react-dom": "18.2.4",
"js-cookie": "^3.0.1",
"next": "13.4.4",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-hook-form": "^7.44.3",
"typescript": "5.0.2"
},
"devDependencies": {
"@types/js-cookie": "^3.0.3",
"autoprefixer": "^10.4.14",
"daisyui": "^3.0.20",
"postcss": "^8.4.21",
"tailwindcss": "^3.3.0"
}
}

@ -0,0 +1,13 @@
import { Html, Head, Main, NextScript } from 'next/document'
export default function Document() {
return (
<Html lang="en" data-theme="cmyk">
<Head />
<body>
<Main />
<NextScript />
</body>
</Html>
)
}

@ -0,0 +1,91 @@
import type { NextPage } from 'next';
import { MainLayout } from '../layout/MainLayout';
import { Input } from '../../components/Input';
import { useForm } from 'react-hook-form';
import { Button } from '../../components/Button';
import { useRouter } from 'next/router';
import { useEffect } from 'react';
import { Toolbar } from '../../components/Toolbar';
import { useFetchWithAuth } from '../../hooks/useFetchWithAuth';
import { Manytoone } from '../../components/Manytoone';
import { saveWithAuth } from '../../hooks/saveWithAuth';
const ClientePage: NextPage = () => {
const router = useRouter();
const entityId: string = !!router.query.id ? router.query.id as string : "0";
const baseUrl = process.env.API_URL;
const { control, register, setValue,getValues,handleSubmit, formState: { isSubmitSuccessful, errors } } = useForm(
{
defaultValues: {
id: 0,
nombre: '',
telefono: '',
compania:0
}
}
);
const getInitData = async () => {
if (!entityId || entityId=="0")
return;
const { data, error } = await useFetchWithAuth("cliente/" + entityId);
if (!error) {
if (data.id!=0){
setValue("id", data['id'])
setValue("nombre", data['nombre'])
setValue("telefono", data['telefono'])
if (data['compania']){
setValue("compania", data['compania'].id)
}
}
} else {
console.log(error)
}
}
const onSubmit = async (entity: any) => {
try {
let endpoint = "cliente";
entity['compania'] = {id: entity['compania']}
const { data, error } = await saveWithAuth(endpoint, entityId, entity);
if (error) {
console.log(error);
} else {
router.push("/"+endpoint+"/form" + "?id=" + data.id)
}
} catch (e) {
console.log("Post error:");
console.table(e);
}
}
useEffect(() => {
getInitData();
}, [router.query.id])
return (
<>
<MainLayout title={'Compañia'} >
<Toolbar pathForm='/cliente/form' pathList='/cliente/' currentEntity={""} entityName='cliente'/>
<div className='h-96 w-4/6 mx-auto p-4 border-solid border-gray-300 border-2 rounded-xl '>
<form onSubmit={handleSubmit(onSubmit)}
>
<Input id='id' label='' register={register} type='hidden'/>
<Input id='nombre' label='Nombre' register={register} />
<Input id='telefono' label='Telefono' register={register} />
<Manytoone entity={ 'compania'} control={control} />
<Button type='submit' title='Guardar' />
{errors?.root?.server && <p>Form submit failed.</p>}
</form>
</div>
</MainLayout>
</>
)
}
export default ClientePage

@ -0,0 +1,18 @@
import type { NextPage } from 'next';
import { MainLayout } from '../layout/MainLayout';
import { Toolbar } from '../../components/Toolbar';
import Table from '../../components/table/Table';
const clientePage: NextPage = () => {
return (
<>
<MainLayout title={'cliente'} >
<Toolbar pathForm='/cliente/form' pathList='/cliente/' currentEntity='' entityName=''/>
<Table columns={['nombre','telefono']} endpoint={'cliente'} />
</MainLayout>
</>
)
}
export default clientePage

@ -0,0 +1,88 @@
import type { NextPage } from 'next';
import { MainLayout } from '../layout/MainLayout';
import { Input } from '../../components/Input';
import { Form, useForm } from 'react-hook-form';
import { Button } from '../../components/Button';
import Cookies from 'js-cookie';
import { useRouter } from 'next/router';
import { useEffect } from 'react';
import { Toolbar } from '../../components/Toolbar';
import { useFetchWithAuth } from '../../hooks/useFetchWithAuth';
const CompaniaPage: NextPage = () => {
const router = useRouter();
const entityId: string = !!router.query.id ? router.query.id as string : "0";
const baseUrl = process.env.API_URL;
const { control, register,reset, setValue, formState: { isSubmitSuccessful, errors } } = useForm(
{
defaultValues: {
id: 0,
ruc: '',
nombre: '',
direccion: ''
}
}
);
const getInitData = async () => {
if (!entityId || entityId=="0")
return;
const { data, error } = await useFetchWithAuth("compania/" + entityId);
if (!error) {
if (data.id!=0){
setValue("id", data['id'])
setValue("ruc", data['ruc'])
setValue("nombre", data['nombre'])
setValue("direccion", data['direccion'])
}
} else {
console.log(error)
}
}
useEffect(() => {
getInitData();
}, [router.query.id])
useEffect(() => {
reset({
id:0,
ruc: "",
nombre: '',
direccion: ''
});
}, [isSubmitSuccessful])
return (
<>
<MainLayout title={'Compañia'} >
<Toolbar pathForm='/compania/form' pathList='/compania/' currentEntity={""} entityName='compania'/>
<div className='h-96 w-4/6 mx-auto p-4 border-solid border-gray-300 border-2 rounded-xl '>
<Form
action={baseUrl+"/api/compania/"}
control={control}
headers={{
'Authorization': Cookies.get("token") || '',
'Content-Type': 'application/json'
}}
>
<Input id='id' label='' register={register} type='hidden'/>
<Input id='ruc' label='RUC' register={register} />
<Input id="nombre" label='Nombre' register={register}/>
<Input id="direccion" label='Dirección' register={register}/>
<Button type='submit' title='Guardar' />
{errors?.root?.server && <p>Form submit failed.</p>}
</Form>
</div>
</MainLayout>
</>
)
}
export default CompaniaPage

@ -0,0 +1,18 @@
import type { NextPage } from 'next';
import { MainLayout } from '../layout/MainLayout';
import { Toolbar } from '../../components/Toolbar';
import Table from '../../components/table/Table';
const CompaniaPage: NextPage = () => {
return (
<>
<MainLayout title={'Compania'} >
<Toolbar pathForm='/compania/form' pathList='/compania/' currentEntity='' entityName=''/>
<Table columns={['ruc','nombre','direccion']} endpoint={'compania'} />
</MainLayout>
</>
)
}
export default CompaniaPage

@ -0,0 +1,21 @@
import type { NextPage } from 'next';
import { Button } from '../../components/Button';
import Cookies from 'js-cookie';
import { useRouter } from 'next/router';
const FlexboxPage: NextPage = () => {
const router = useRouter();
return (
<>
<Button
title={'Logout'}
onClick={function (): void {
Cookies.remove("token");
router.push("/login");
}}/>
</>
)
}
export default FlexboxPage

@ -0,0 +1,15 @@
import type { NextPage } from 'next';
import { MainLayout } from './layout/MainLayout';
const HomePage: NextPage = () => {
return (
<>
<MainLayout title={'Compañia'} >
Home
</MainLayout>
</>
)
}
export default HomePage

@ -2,6 +2,7 @@ import Head from 'next/head'
import Image from 'next/image'
import styles from '../styles/Home.module.css'
export default function Home() {
return (
<div className={styles.container}>
@ -11,7 +12,7 @@ export default function Home() {
<link rel="icon" href="/favicon.ico" />
</Head>
<main className={styles.main}>
<main data-theme="cmyk" className={styles.main}>
<h1 className={styles.title}>
Welcome to <a href="https://nextjs.org">Next.js!</a>
</h1>

@ -0,0 +1,36 @@
import Head from 'next/head';
import { NextPage } from 'next';
import { Navbar } from './Navbar';
import Sidebar from './Sidebar';
interface Props {
title: string;
children?: React.ReactNode;
}
export const MainLayout:NextPage<Props> = ({ children, title} ) => {
return (
<>
<Head>
<title>{ title }</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
</Head>
<nav>
<Navbar />
</nav>
<div className='flex'>
<Sidebar />
<main className='flex-1 mx-2'>
{ children }
</main>
</div>
</>
)
}

@ -0,0 +1,43 @@
import Link from 'next/link'
import { FC } from 'react'
import { Button } from '../../components/Button';
import Cookies from 'js-cookie';
import { useRouter } from 'next/router';
interface Props {
}
export const Navbar:FC <Props> = () => {
const router = useRouter();
return (
<>
<div className="navbar">
<div className="flex-none">
<label htmlFor="my-drawer-2" className="btn btn-square btn-ghost drawer-button lg:hidden">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" className="inline-block w-5 h-5 stroke-current"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M4 6h16M4 12h16M4 18h16"></path></svg>
</label>
</div>
<div className="flex-1">
<Link href={"/home"}>
<img className="w-20 mr-4" src="/logo.png" alt="Sistema Agil" />
</Link>
</div>
<div className="flex-none">
<div className="dropdown dropdown-end">
<Button
title={'Logout'}
onClick={function (): void {
console.log("Logout")
Cookies.remove("token");
router.push("/login");
}}/>
</div>
</div>
</div>
</>
)
}

@ -0,0 +1,33 @@
import Link from 'next/link';
import { FC } from 'react';
interface SidebarProps {
};
const Sidebar:FC<SidebarProps > = () => {
return (
<>
<ul className="h-full p-4 text-white rounded-lg menu-compact w-80 sm:w-60 bg-gradient-to-b from-blue-700 to-sky-500">
<li>
<Link href={"/compania"}>{"Compañia"}</Link>
</li>
<li>
<Link href={"/cliente"}>{"Cliente"}</Link>
</li>
<li>
<Link href={"/producto"}>{"Producto"}</Link>
</li>
<li>
<Link href={"/pedido"}>{"Pedido"}</Link>
</li>
</ul>
</>
);
}
export default Sidebar

@ -0,0 +1,58 @@
import type { NextPage } from 'next';
import Image from 'next/image';
import { Input } from '../../components/Input';
import { Button } from '../../components/Button';
import { useForm } from 'react-hook-form';
import { useRouter } from 'next/router';
import { postBasicAuth } from '../../hooks/postBasicAuth';
import Cookies from 'js-cookie';
const indexPage: NextPage = () => {
const router = useRouter();
const { register, handleSubmit, getValues, formState: { errors } } = useForm();
const onSubmit = async (entity: any) => {
console.log("on submit")
try {
let url = process.env.API_URL + "/login";
const { data, error } = await postBasicAuth(url,getValues("user"),getValues("password") );
if (error) {
console.log(error);
} else {
if (data && data.length>0){
Cookies.set("token",data);
router.push("/home");
}
}
} catch (e) {
console.log("Post error:");
console.table(e);
}
}
return (
<div className='flex flex-col items-center'>
<Image alt="Logo"
src={"/logo.png"}
width={100}
height={100}
/>
<form onSubmit={handleSubmit(onSubmit)}>
<div className='flex flex-col'>
<Input id="user" label='Usuario' register={register} />
{errors.User && <span>This field is required</span>}
<Input id="password" label='Contraseña' type="password" register={register}/>
</div>
<Button type='submit' onClick={()=>{console.log("login")}} title='Login'/>
</form>
</div>
)
}
export default indexPage

@ -0,0 +1,103 @@
import type { NextPage } from 'next';
import { MainLayout } from '../layout/MainLayout';
import { Input } from '../../components/Input';
import { useForm } from 'react-hook-form';
import { Button } from '../../components/Button';
import { useRouter } from 'next/router';
import { useEffect } from 'react';
import { Toolbar } from '../../components/Toolbar';
import { useFetchWithAuth } from '../../hooks/useFetchWithAuth';
import { Manytoone } from '../../components/Manytoone';
import { saveWithAuth } from '../../hooks/saveWithAuth';
import { Onetomany } from '../../components/oneToMany/Onetomany';
const pedidoPage: NextPage = () => {
const router = useRouter();
const entityId: string = !!router.query.id ? router.query.id as string : "0";
const baseUrl = process.env.API_URL;
const { control, register, setValue,getValues,handleSubmit, formState: { isSubmitSuccessful, errors } } = useForm(
{
defaultValues: {
id: 0,
numero : 0,
cliente: 0
}
}
);
const getInitData = async () => {
if (!entityId || entityId=="0")
return;
const { data, error } = await useFetchWithAuth("pedido/" + entityId);
if (!error) {
if (data.id!=0){
setValue("id", data['id'])
setValue("numero", data['numero'])
if (data['cliente'])
setValue("cliente", data['cliente'].id)
}
} else {
console.log(error)
}
}
const onSubmit = async (entity: any) => {
try {
let endpoint = "pedido";
entity['cliente'] = {id: entity['cliente']}
const { data, error } = await saveWithAuth(endpoint, entityId, entity);
if (error) {
console.log(error);
} else {
router.push("/"+endpoint+"/form" + "?id=" + data.id)
}
} catch (e) {
console.log("Post error:");
console.table(e);
}
}
useEffect(() => {
getInitData();
}, [router.query.id])
return (
<>
<MainLayout title={'Pedido'} >
<Toolbar pathForm='/pedido/form' pathList='/pedido/' currentEntity={""} entityName='cliente'/>
<div className='h-96 w-4/6 mx-auto p-4 border-solid border-gray-300 border-2 rounded-xl '>
<p className='text-center m-2'>
Formulario <span className="text-primary">{"Pedidos"}</span>
</p>
<form onSubmit={handleSubmit(onSubmit)}
>
<Input id='id' label='' register={register} type='hidden'/>
<Input id='numero' label='Numero' register={register}/>
<Manytoone key={"cliente"} entity='cliente' control={control} />
<Onetomany
control={control}
register={register}
setValue={setValue}
entityId={Number(entityId)}
entityName={"pedido"}
element={"detalle"}
oneToManyHeader={[
{id:"producto",name:"ManyToOne"},
{id:"cantidad",name:"BigDecimal"},
{id:"precio",name:"BigDecimal"}]}
/>
<Button type='submit' title='Guardar' />
{errors?.root?.server && <p>Form submit failed.</p>}
</form>
</div>
</MainLayout>
</>
)
}
export default pedidoPage

@ -0,0 +1,18 @@
import type { NextPage } from 'next';
import { MainLayout } from '../layout/MainLayout';
import { Toolbar } from '../../components/Toolbar';
import Table from '../../components/table/Table';
const pedidoPage: NextPage = () => {
return (
<>
<MainLayout title={'pedido'} >
<Toolbar pathForm='/pedido/form' pathList='/pedido/' currentEntity='' entityName=''/>
<Table columns={['numero']} endpoint={'pedido'} />
</MainLayout>
</>
)
}
export default pedidoPage

@ -0,0 +1,92 @@
import type { NextPage } from 'next';
import { MainLayout } from '../layout/MainLayout';
import { Input } from '../../components/Input';
import { useForm } from 'react-hook-form';
import { Button } from '../../components/Button';
import { useRouter } from 'next/router';
import { useEffect } from 'react';
import { Toolbar } from '../../components/Toolbar';
import { useFetchWithAuth } from '../../hooks/useFetchWithAuth';
import { Manytoone } from '../../components/Manytoone';
import { saveWithAuth } from '../../hooks/saveWithAuth';
const productoPage: NextPage = () => {
const router = useRouter();
const entityId: string = !!router.query.id ? router.query.id as string : "0";
const baseUrl = process.env.API_URL;
const { control, register, setValue,getValues,handleSubmit, formState: { isSubmitSuccessful, errors } } = useForm(
{
defaultValues: {
id: 0,
nombre: '',
codigo: '',
precio: 0.00,
creado: new Date()
}
}
);
const getInitData = async () => {
if (!entityId || entityId=="0")
return;
const { data, error } = await useFetchWithAuth("producto/" + entityId);
if (!error) {
if (data.id!=0){
setValue("id", data['id'])
setValue("nombre", data['nombre'])
setValue("codigo", data['codigo'])
setValue("precio", Number(data["precio"]))
setValue("creado",data["creado"])
}
} else {
console.log(error)
}
}
const onSubmit = async (entity: any) => {
try {
let endpoint = "producto";
//entity['compania'] = {id: entity['compania']} (Ejemplo de conversion de manytoone)
const { data, error } = await saveWithAuth(endpoint, entityId, entity);
if (error) {
console.log(error);
} else {
router.push("/"+endpoint+"/form" + "?id=" + data.id)
}
} catch (e) {
console.log("Post error:");
console.table(e);
}
}
useEffect(() => {
getInitData();
}, [router.query.id])
return (
<>
<MainLayout title={'Compañia'} >
<Toolbar pathForm='/producto/form' pathList='/producto/' currentEntity={""} entityName='cliente'/>
<div className='h-96 w-4/6 mx-auto p-4 border-solid border-gray-300 border-2 rounded-xl '>
<form onSubmit={handleSubmit(onSubmit)}
>
<Input id='id' label='' register={register} type='hidden'/>
<Input id='nombre' label='Nombre' register={register}/>
<Input id='codigo' label='Codigo' register={register}/>
<Input id='precio' type='number' label='precio' register={register}/>
<Input id='creado' type='datetime-local' label='creado' register={register}/>
<Button type='submit' title='Guardar' />
{errors?.root?.server && <p>Form submit failed.</p>}
</form>
</div>
</MainLayout>
</>
)
}
export default productoPage

@ -0,0 +1,18 @@
import type { NextPage } from 'next';
import { MainLayout } from '../layout/MainLayout';
import { Toolbar } from '../../components/Toolbar';
import Table from '../../components/table/Table';
const productoPage: NextPage = () => {
return (
<>
<MainLayout title={'producto'} >
<Toolbar pathForm='/producto/form' pathList='/producto/' currentEntity='' entityName=''/>
<Table columns={['nombre','codigo','precio']} endpoint={'producto'} />
</MainLayout>
</>
)
}
export default productoPage

@ -0,0 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

@ -1,26 +1,3 @@
html,
body {
padding: 0;
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
}
a {
color: inherit;
text-decoration: none;
}
* {
box-sizing: border-box;
}
@media (prefers-color-scheme: dark) {
html {
color-scheme: dark;
}
body {
color: white;
background: black;
}
}
@tailwind base;
@tailwind components;
@tailwind utilities;

@ -0,0 +1,19 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
"./app/**/*.{js,ts,jsx,tsx,mdx}",
"./pages/**/*.{js,ts,jsx,tsx,mdx}",
"./components/**/*.{js,ts,jsx,tsx,mdx}",
// Or if using `src` directory:
"./src/**/*.{js,ts,jsx,tsx,mdx}",
],
theme: {
extend: {},
},
daisyui: {
themes: ["cupcake", "dark", "cmyk"],
},
plugins: [require("daisyui")],
}
Loading…
Cancel
Save