parent
828be268a2
commit
758ff47a80
@ -0,0 +1 @@
|
||||
API_URL=http://localhost:8082
|
@ -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).*)',],
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -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
|
@ -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: {},
|
||||
},
|
||||
}
|
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…
Reference in new issue