Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions app/api/agenda/[matricula_doctor]/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { NextRequest, NextResponse } from "next/server";
import { agendaSeed } from "@/lib/seeders/agenda";

export async function GET(
request: NextRequest,
{ params }: { params: Promise<{ matricula_doctor: string }> }
) {
const { matricula_doctor } = await params;
const fecha = request.nextUrl.searchParams.get("fecha");

let citas = agendaSeed.filter(
(c) => c.matricula_doctor === matricula_doctor
);

if (fecha) {
citas = citas.filter((c) => c.fecha === fecha);
}

if (citas.length === 0) {
return NextResponse.json(
{ error: `No se encontraron citas para el doctor ${matricula_doctor}` },
{ status: 404 }
);
}

return NextResponse.json(
{
matricula_doctor,
fecha: fecha ?? "todas",
total: citas.length,
agenda: citas,
},
{ status: 200 }
);
}
29 changes: 29 additions & 0 deletions app/api/historial/[matricula]/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { NextRequest, NextResponse } from "next/server";
import { historialSeed } from "@/lib/seeders/historial";

export async function GET(
request: NextRequest,
{ params }: { params: Promise<{ matricula: string }> }
) {
const { matricula } = await params;

const consultas = historialSeed.filter(
(c) => c.matricula_paciente === matricula
);

if (consultas.length === 0) {
return NextResponse.json(
{ error: `No se encontró historial para la matrícula ${matricula}` },
{ status: 404 }
);
}

return NextResponse.json(
{
matricula,
total: consultas.length,
historial: consultas,
},
{ status: 200 }
);
}
13 changes: 13 additions & 0 deletions app/components/Buttons/SubmitBtn.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
interface Props {
children: React.ReactNode;
}
export default function SubmitBtn({ children }: Props) {
return (
<button
type="submit"
className="inline-block px-4 min-w-full py-3 mx-2 text-white duration-150 font-medium bg-[#B3DCCE] rounded-md hover:bg-[#5C6B73] active:bg-[#5C6B73] md:text-sm capitalize"
>
{children}
</button>
);
}
13 changes: 13 additions & 0 deletions app/components/Forms/Form.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export default function Form({ title, children }: { title: string; children: React.ReactNode }) {
return (
<form
className="items-center justify-center max-w-full min-w-full max-h-full mx-auto p-4 grid grid-cols-2 md:grid-cols-1 gap-2"
method="post"
>
<h2 className="text-4xl dark:text-white font-bold text-black text-center col-span-2 md:col-span-1 mb-10">
{title}
</h2>
{children}
</form>
);
}
56 changes: 56 additions & 0 deletions app/components/Forms/FormInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { InputType } from "@/Types/InputTypes";
interface Props {
name: string;
title?: string;
type: InputType;
isRequired?: boolean;
placeholder?: string;
value?: string | number;
isDisabled?: boolean;
multiple?: boolean;
children?: React.ReactNode;
onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void;
accept?: string;
}
export default function FormInput({
name,
title,
type,
isRequired,
placeholder,
value,
isDisabled,
multiple,
children,
onChange,
accept
}: Props) {
return (
<div className="flex-1 mb-4">
<label className="block text-sm dark:text-white mb-1 capitalize font-bold">
{title ? title : children}
{isRequired && <span className="text-red-500"> *</span>}
</label>
<input
type={type}
id={name}
name={name}
placeholder={placeholder}
value={value}
required={isRequired}
disabled={isDisabled}
aria-describedby="helper-text-explanation"
step="any"
multiple={multiple}
accept={accept}
onChange={onChange}
className={`w-full p-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2
focus:ring-[#5C6B73] focus:border-[#5C6B73] dark:border-2 dark:border-[#9DB4C0]
dark:text-[#E0FBFC] flex-1 min-w-50
[appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none
${isDisabled ? "dark:bg-gray-700/70" : "dark:bg-gray-700/20"}
`}
/>
</div>
);
}
15 changes: 15 additions & 0 deletions app/components/UI/Counter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
interface Props {
count: number
children: string
}

export default function Counter({ count, children }: Props) {
return (
<div className="border dark:border-gray-600 rounded-lg p-2 w-fit text-sm">
<p className="font-bold text-center text-purple-500">
{count}
</p>
{children}
</div>
)
}
7 changes: 7 additions & 0 deletions app/components/UI/Icons/LoaderIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { LoaderCircle } from "lucide-react";

export default function LoaderIcon(){
return (
<LoaderCircle className="animate-spin"/>
)
}
31 changes: 31 additions & 0 deletions app/components/UI/StatusPill.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { CheckCircle, LoaderCircle, Pickaxe, TriangleAlert, XCircle } from "lucide-react"
import LoaderIcon from "./Icons/LoaderIcon"

const colorClasses = {
red: "bg-red-500/30 dark:bg-red-600/30 text-red-500 border-red-500",
blue: "bg-blue-500/30 dark:bg-blue-600/30 text-blue-500 border-blue-500",
green: "bg-emerald-500/30 dark:bg-emerald-600/30 text-green-500 border-green-500",
yellow: "bg-amber-500/30 dark:bg-amber-600/30 text-amber-500 border-amber-500",
}
const colorIcons = {
red: XCircle,
blue: LoaderIcon,
green: CheckCircle,
yellow: TriangleAlert,
}

export default function StatusPill({
status,
color,
}: {
status: string
color: "red" | "blue" | "green" | "yellow"
}) {
const Icon = colorIcons[color]
return (
<span className={`m-2 border px-2 py-1 rounded-md inline-flex items-center justify-center ${colorClasses[color]}`}>
<Icon className="w-3 h-3 mr-1" />
<p className="whitespace-nowrap text-[12px] capitalize">{status}</p>
</span>
)
}
9 changes: 9 additions & 0 deletions app/components/UI/UserIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { User } from "lucide-react";

export default function UserIcon() {
return (
<div className="flex size-16 p-4 items-center justify-center rounded-full border dark:text-black dark:dark:border-0 dark:bg-white">
<User className="size-14" />
</div>
);
}
11 changes: 9 additions & 2 deletions app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,17 @@ export default function RootLayout({
}>) {
return (
<html lang="en">
<head>
<link rel="preconnect" href="https://fonts.googleapis.com"/>
<link rel="preconnect" href="https://fonts.gstatic.com"/>
<link href="https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100..900;1,100..900&display=swap" rel="stylesheet"/>
</head>
<body
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
className={`${geistSans.variable} ${geistMono.variable} antialiased dark:bg-[#253237] bg-[#E0FBFC] bg-[url('https://flowbite.s3.amazonaws.com/docs/jumbotron/hero-pattern.svg')] dark:bg-[url('https://flowbite.s3.amazonaws.com/docs/jumbotron/hero-pattern-dark.svg')]`}
>
{children}
<main className="flex-1 flex flex-col items-center justify-center min-w-screen min-h-screen">
{children}
</main>
</body>
</html>
);
Expand Down
14 changes: 14 additions & 0 deletions app/turso.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { createClient } from "@libsql/client";

if (!process.env.TURSO_DATABASE_URL) {
throw new Error("TURSO_DATABASE_URL no está definida");
}

if (!process.env.TURSO_AUTH_TOKEN) {
throw new Error("TURSO_AUTH_TOKEN no está definida");
}

export const turso = createClient({
url: process.env.TURSO_DATABASE_URL,
authToken: process.env.TURSO_AUTH_TOKEN,
});
9 changes: 9 additions & 0 deletions app/types/AcceptedDocumentsType.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export type AcceptedDocuments =
| "application/pdf"
| "application/msword"
| "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
| "application/vnd.ms-excel"
| "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
| "application/vnd.ms-powerpoint"
| "application/vnd.openxmlformats-officedocument.presentationml.presentation"
| "text/plain";
16 changes: 16 additions & 0 deletions app/types/AppointmentType.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { AcceptedDocuments } from "./AcceptedDocumentsType";

export type AppointmentStatus = "pendiente" | "completada" | "cancelada";

export type Appointment = {
id: string;
matricula_paciente: string;
matricula_doctor: string;
nombre_paciente: string;
fecha: string;
estatus: AppointmentStatus;
diagnostico?: string;
tratamiento?: string;
medico_responsable?: string;
documents?: AcceptedDocuments[];
};
17 changes: 17 additions & 0 deletions app/types/InputTypes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
export type InputType = "checkbox" | "color" | "date" | "datetime-local" | "email" | "file" | "hidden" | "month" | "number" | "password" | "search" | "tel" | "text" | "time" | "url" | "week";

export type InputField = {
title: string;
type: InputType;
isRequired: boolean;
placeholder?: string;
}

export type InputFieldArr = {
name: string;
label: string;
type: InputType;
isRequired: boolean;
placeholder?: string;
value?: string | number;
};
Loading
Loading