diff --git a/client/.env.example b/client/.env.example new file mode 100644 index 0000000..1b76fe4 --- /dev/null +++ b/client/.env.example @@ -0,0 +1 @@ +API_URL=https://clinica-medica-production.up.railway.app/api/v1/ \ No newline at end of file diff --git a/client/.gitignore b/client/.gitignore index 8460eea..b1095a4 100644 --- a/client/.gitignore +++ b/client/.gitignore @@ -19,6 +19,7 @@ # misc .DS_Store *.pem +.vscode # debug npm-debug.log* @@ -26,6 +27,7 @@ yarn-debug.log* yarn-error.log* # local env files +.env .env*.local # vercel diff --git a/client/public/images/home.jpg b/client/public/images/home.jpg deleted file mode 100644 index 1bd9b0a..0000000 Binary files a/client/public/images/home.jpg and /dev/null differ diff --git a/client/public/images/home.png b/client/public/images/home.png new file mode 100644 index 0000000..e918756 Binary files /dev/null and b/client/public/images/home.png differ diff --git a/client/src/actions/doctors/doctorActions.ts b/client/src/actions/doctors/doctorActions.ts new file mode 100644 index 0000000..ac1f049 --- /dev/null +++ b/client/src/actions/doctors/doctorActions.ts @@ -0,0 +1,26 @@ +'use server' + +const BASE_URL = process.env.API_URL + +export const getAllDoctors = async () => { + const url = BASE_URL + '/doctor/allDoctors' + const data = await fetch(url).then((res) => res.json()) + + if (data) { + return data + } + + return [] +} + +export const getDoctorById = async (id: number) => { + const url = BASE_URL + '/doctor/getById/' + id + + const data = await fetch(url).then((res) => res.json()) + + if (data) { + return data + } + + return null +} diff --git a/client/src/app/(patients)/appointment/[id]/page.tsx b/client/src/app/(patients)/appointment/[id]/page.tsx index 001c645..c26706c 100644 --- a/client/src/app/(patients)/appointment/[id]/page.tsx +++ b/client/src/app/(patients)/appointment/[id]/page.tsx @@ -1,31 +1,10 @@ -import { Data } from '@/interfaces/user' -import data from '../../../../utils/data.json' import { DoctorDetails } from '@/ui/doctors/Details' +import { getDoctorById } from '@/actions/doctors/doctorActions' export default async function AppointmentById({ params }: { params: { id: string } }) { const id = Number(params.id) - /* const url = '../../../../utils/data.json' - const data = await fetch(url) */ - const getDoctorData = (data: Data) => { - const doctor = data.doctors.find((doctor) => { - const user = data.users.find((user) => user.id === doctor.id_user && user.id === id) - return user - }) - - const user = data.users.find((user) => user.id === doctor?.id_user) - return { - speciality: doctor?.speciality || 'error', - name: user?.name || 'error', - dni: user?.dni || '12312312', - phone: user?.phone || 'error', - email: user?.email || 'error', - address: user?.address || 'error' - } - - } - - const doctor = getDoctorData(data) + const doctor = await getDoctorById(id) return (
diff --git a/client/src/app/(patients)/appointment/page.tsx b/client/src/app/(patients)/appointment/page.tsx index 524731b..7b91263 100644 --- a/client/src/app/(patients)/appointment/page.tsx +++ b/client/src/app/(patients)/appointment/page.tsx @@ -1,9 +1,10 @@ -import data from '../../../utils/data.json' import { DoctorList } from "@/ui/doctors/List" import { Search, SearchResults } from "@/components"; import { Suspense } from "react"; +import { getAllDoctors } from '@/actions/doctors/doctorActions'; +import { DoctorFromResponse } from "@/interfaces/user"; -export default function AppointmentsPage({ +export default async function AppointmentsPage({ searchParams, }: { searchParams?: { @@ -15,18 +16,22 @@ export default function AppointmentsPage({ }) { const query = searchParams?.q || ''; - const doctorListWithPlacesFiltered = data.doctors.filter((doctor, index, self) => + const data: DoctorFromResponse[] = await getAllDoctors() + + const doctorListWithPlacesFiltered = data.filter((doctor, index, self) => index === self.findIndex((d) => d.place === doctor.place) ) - const doctorListWithSpecialityFiltered = data.doctors.filter((doctor, index, self) => - index === self.findIndex((d) => d.speciality === doctor.speciality) + const doctorListWithSpecialityFiltered = data.filter((doctor, index, self) => + index === self.findIndex((d) => d.specialization === doctor.specialization) ) + + return (
- +
diff --git a/client/src/app/layout.tsx b/client/src/app/layout.tsx index d232901..a193cb1 100644 --- a/client/src/app/layout.tsx +++ b/client/src/app/layout.tsx @@ -1,10 +1,12 @@ import type { Metadata } from "next"; -import { Inter } from 'next/font/google' +import { Poppins } from 'next/font/google' import "./globals.css"; +import { Header } from "@/ui/patient/Header"; -const inter = Inter({ - subsets: ['latin'], - display: 'swap', +const poppins = Poppins({ + style: 'normal', + weight: ['400', '500', '600'], + subsets: ['latin'] }) export const metadata: Metadata = { @@ -20,9 +22,10 @@ export default function RootLayout({ return (
+
{children}
diff --git a/client/src/app/page.tsx b/client/src/app/page.tsx index dba6dfa..32b8903 100644 --- a/client/src/app/page.tsx +++ b/client/src/app/page.tsx @@ -1,56 +1,36 @@ import Image from "next/image"; import Link from "next/link"; -import HomeImage from '/public/images/home.jpg' +import HomeImage from '/public/images/home.png' +import { ButtonComponent } from "@/ui"; export default function Home() { return (
- {/* Navbar */} - - - {/* Slider */} -
-
-
- {/* Imagen*/} -
- Policonsultorio 1 -
+
+
+
+ Policonsultorio 1
-
+
- {/* Contenido principal */} -
-

Bienvenido

-

Policonsultorio

- Agendar Cita -
+
+

Comenzá gestionar tus citas + en un solo lugar.

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas dolor nulla, maximus sit amet nulla egestas, egestas maximus sapien. Aliquam eget libero id justo molestie ultricies. Fusce ac nisi dignissim, finibus libero amet, congue orci. Fusce non gravida nunc. Praesent fermentum egestas eros, non imperdiet dolor.

+ + + +
-
+
); } diff --git a/client/src/components/search/SearchResults.tsx b/client/src/components/search/SearchResults.tsx index 25a25a2..f58b854 100644 --- a/client/src/components/search/SearchResults.tsx +++ b/client/src/components/search/SearchResults.tsx @@ -1,38 +1,37 @@ -import data from '../../utils/data.json' -import { Data } from "@/interfaces/user" -import { DoctorCard, PlaceCard, SpecialityCard } from "@/ui" +import { DoctorFromResponse } from '@/interfaces/user' +import { DoctorCard, PlaceCard, Placeholder, SpecialityCard } from "@/ui" type Props = { query: string + data: DoctorFromResponse[] } -export const SearchResults: React.FC = async ({ query }) => { - const getDoctorData = (data: Data) => { - const doctorList = data.doctors +export const SearchResults: React.FC = async ({ query, data }) => { + const getDoctorData = (data: DoctorFromResponse[]) => { + const doctorList = data .filter((doctor) => { - const user = data.users.find((user) => user.id === doctor.id_user) + const user = data.find((user) => user.id === doctor.id) if (user) { const matchesQuery = user.name.toLowerCase().includes(query.toLowerCase()) || - doctor.place.toLowerCase().includes(query.toLowerCase()) || - doctor.speciality.toLowerCase().includes(query.toLowerCase()) + doctor.place?.toLowerCase().includes(query.toLowerCase()) || + doctor.specialization.toLowerCase().includes(query.toLowerCase()) return matchesQuery } return false }) .map((doctor) => { - const user = data.users.find((user) => user.id === doctor.id_user) + const user = data.find((user) => user.id === doctor.id) if (user) { return { id: user.id, name: user.name, - speciality: doctor.speciality, - place: doctor.place, - address: doctor.address || "Dirección no disponible" - + speciality: doctor.specialization, + place: 'Consultorio Principal', + address: doctor.email || 'Dirección no disponible', } } }) @@ -40,7 +39,7 @@ export const SearchResults: React.FC = async ({ query }) => { const uniqueDoctorList = Array.from( new Map( - doctorList.map(item => [`${item?.speciality}-${item?.place}`, item]) + doctorList.map((item) => [`${item?.speciality}-${item?.place}`, item]) ).values() ) @@ -48,19 +47,20 @@ export const SearchResults: React.FC = async ({ query }) => { } const results = getDoctorData(data) + console.log(results); const isNameSearch = results.some(result => result?.name.toLowerCase().includes(query.toLowerCase())) const isSpecialitySearch = results.some(result => result?.speciality.toLowerCase().includes(query.toLowerCase())) const isPlaceSearch = results.some(result => result?.place.toLowerCase().includes(query.toLowerCase())) - let filteredResults = results; + let filteredResults = results if (isSpecialitySearch) { filteredResults = Array.from( new Map( filteredResults.map(item => [item?.speciality, item]) ).values() - ); + ) } if (isPlaceSearch) { @@ -68,7 +68,7 @@ export const SearchResults: React.FC = async ({ query }) => { new Map( filteredResults.map(item => [item?.place, item]) ).values() - ); + ) } return ( @@ -82,7 +82,7 @@ export const SearchResults: React.FC = async ({ query }) => { <>
{filteredResults.map((doctor) => ( -
+
{isNameSearch && ( )} @@ -95,9 +95,7 @@ export const SearchResults: React.FC = async ({ query }) => {
))}
-
-

Chat bot o Publicidad

-
+ )} {results.length === 0 && query && ( diff --git a/client/src/interfaces/user.d.ts b/client/src/interfaces/user.d.ts index 24fc55f..c595289 100644 --- a/client/src/interfaces/user.d.ts +++ b/client/src/interfaces/user.d.ts @@ -38,3 +38,16 @@ export interface Data { users: User[] doctors: Doctor[] } + +export interface DoctorFromResponse { + id: number + name: string + password: string + email: string + phone: string + img: string + active: boolean + specialization: string + licenseNumber: string + place?: string +} diff --git a/client/src/ui/buttons/ButtonComponent.tsx b/client/src/ui/buttons/ButtonComponent.tsx new file mode 100644 index 0000000..e30adf4 --- /dev/null +++ b/client/src/ui/buttons/ButtonComponent.tsx @@ -0,0 +1,17 @@ +interface Props { + className?: string + size: 'normal' | 'big' + variant: 'light' | 'dark' + text: string +} + +export function ButtonComponent({ className, size, variant, text }: Props) { + const style = variant === 'light' ? 'bg-white text-secondaryBlue-500' : 'bg-secondaryBlue-500 text-white ' + const sizing = size === 'normal' ? 'text-lg w-[220px] h-16' : 'text-[32px] w-[419px] h-24' + + return ( + + ) +} \ No newline at end of file diff --git a/client/src/ui/doctors/Details.tsx b/client/src/ui/doctors/Details.tsx index 8a24669..8eeced4 100644 --- a/client/src/ui/doctors/Details.tsx +++ b/client/src/ui/doctors/Details.tsx @@ -1,10 +1,10 @@ 'use client' -import { DoctorComplete } from "@/interfaces/user" +import { DoctorFromResponse } from "@/interfaces/user" import { Alert } from "../alert" interface Props { - doctor: DoctorComplete + doctor: DoctorFromResponse } export function DoctorDetails({ doctor }: Props) { @@ -12,11 +12,9 @@ export function DoctorDetails({ doctor }: Props) {

{doctor.name}

-

Especialidad: {doctor.speciality}

-

DNI: {doctor.dni}

+

Especialidad: {doctor.specialization}

Teléfono: {doctor.phone}

Email: {doctor.email}

-

Dirección:{doctor.address}

diff --git a/client/src/ui/doctors/List.tsx b/client/src/ui/doctors/List.tsx index b8f3afb..83e7612 100644 --- a/client/src/ui/doctors/List.tsx +++ b/client/src/ui/doctors/List.tsx @@ -1,8 +1,8 @@ -import { Doctor } from "@/interfaces/user" +import { DoctorFromResponse } from "@/interfaces/user" import { SpecialityCard, PlaceCard } from "../index" interface Props { - list: Doctor[] + list: DoctorFromResponse[] title: string } @@ -13,9 +13,9 @@ export function DoctorList({ list, title }: Props) { {list.map((doctor) => ( <> {title === 'speciality' ? ( - + ) : ( - + )} ))} diff --git a/client/src/ui/index.ts b/client/src/ui/index.ts index 5b2532f..6c93446 100644 --- a/client/src/ui/index.ts +++ b/client/src/ui/index.ts @@ -2,4 +2,9 @@ export { RegisterForm } from "./auth/RegisterForm" export { LoginForm } from "./auth/LoginForm" export { TopMenu } from "./dashboard/TopMenu" export { Select } from "./clinic/Select" -export { DoctorCard } from "./cards/DoctorCard" \ No newline at end of file +export { Alert } from './alert' +export { PlaceCard } from './cards/PlaceCard' +export { SpecialityCard } from './cards/SpecialityCard' +export { DoctorCard } from './cards/DoctorCard' +export { Placeholder } from './placeholder' +export { ButtonComponent } from './buttons/ButtonComponent' diff --git a/client/src/ui/patient/Header.tsx b/client/src/ui/patient/Header.tsx new file mode 100644 index 0000000..554a789 --- /dev/null +++ b/client/src/ui/patient/Header.tsx @@ -0,0 +1,31 @@ +import Link from "next/link"; +import { ButtonComponent } from "@/ui"; + +export function Header() { + + return ( + +
+ + Logo + + + +
+ ) +} diff --git a/client/src/ui/placeholder.tsx b/client/src/ui/placeholder.tsx new file mode 100644 index 0000000..c9ab7ab --- /dev/null +++ b/client/src/ui/placeholder.tsx @@ -0,0 +1,7 @@ +export function Placeholder() { + return ( +
+

Chat bot o Publicidad

+
+ ) +} \ No newline at end of file diff --git a/client/tailwind.config.ts b/client/tailwind.config.ts index 420c0b9..9fbb65c 100644 --- a/client/tailwind.config.ts +++ b/client/tailwind.config.ts @@ -1,67 +1,107 @@ import type { Config } from 'tailwindcss' const config: Config = { - darkMode: ['class'], - content: [ - './src/pages/**/*.{js,ts,jsx,tsx,mdx}', - './src/components/**/*.{js,ts,jsx,tsx,mdx}', - './src/ui/**/*.{js,ts,jsx,tsx,mdx}', - './src/app/**/*.{js,ts,jsx,tsx,mdx}', - ], - theme: { - extend: { - colors: { - background: 'hsl(var(--background))', - foreground: 'hsl(var(--foreground))', - card: { - DEFAULT: 'hsl(var(--card))', - foreground: 'hsl(var(--card-foreground))' - }, - popover: { - DEFAULT: 'hsl(var(--popover))', - foreground: 'hsl(var(--popover-foreground))' - }, - primary: { - DEFAULT: 'hsl(var(--primary))', - foreground: 'hsl(var(--primary-foreground))' - }, - secondary: { - DEFAULT: 'hsl(var(--secondary))', - foreground: 'hsl(var(--secondary-foreground))' - }, - muted: { - DEFAULT: 'hsl(var(--muted))', - foreground: 'hsl(var(--muted-foreground))' - }, - accent: { - DEFAULT: 'hsl(var(--accent))', - foreground: 'hsl(var(--accent-foreground))' - }, - destructive: { - DEFAULT: 'hsl(var(--destructive))', - foreground: 'hsl(var(--destructive-foreground))' - }, - border: 'hsl(var(--border))', - input: 'hsl(var(--input))', - ring: 'hsl(var(--ring))', - chart: { - '1': 'hsl(var(--chart-1))', - '2': 'hsl(var(--chart-2))', - '3': 'hsl(var(--chart-3))', - '4': 'hsl(var(--chart-4))', - '5': 'hsl(var(--chart-5))' - } - }, - borderRadius: { - lg: 'var(--radius)', - md: 'calc(var(--radius) - 2px)', - sm: 'calc(var(--radius) - 4px)' - }, - boxShadow: { - "list-select": "0px 2px 3px 0px rgba(0, 0, 0, 0.3)" - } - } - }, - plugins: [require("tailwindcss-animate")], + darkMode: ['class'], + content: [ + './src/pages/**/*.{js,ts,jsx,tsx,mdx}', + './src/components/**/*.{js,ts,jsx,tsx,mdx}', + './src/ui/**/*.{js,ts,jsx,tsx,mdx}', + './src/app/**/*.{js,ts,jsx,tsx,mdx}', + ], + theme: { + extend: { + colors: { + blue: { + 50: '#E6EFF7', + 100: '#B1CDE5', + 200: '#8BB4D8', + 300: '#5592C7', + 400: '#5F8FD3', + 500: '#025DAB', + 600: '#02559C', + 700: '#014279', + 800: '#01335E', + 900: '#012748', + }, + secondaryBlue: { + 50: '#E6EDF3', + 100: '#B0C6D9', + 200: '#8AAAC6', + 300: '#5484AD', + 400: '#336C9D', + 500: '#004784', + 600: '#004178', + 700: '#00325E', + 800: '#002749', + 900: '#001E37', + }, + green: { + 50: '#FFFFFF', + 100: '#FCFDFD', + 150: '#F5F6F6', + 200: '#F0F1F1', + 250: '#D9DCDD', + 300: '#BFC5C6', + 400: '#8C9699', + 450: '#59676B', + 500: '#455459', + 600: '#26383E', + 700: '#1F3137', + 800: '#14282E', + 900: '#00151C', + }, + yellow: { + 200: '#FFEFCE', + }, + + background: 'hsl(var(--background))', + foreground: 'hsl(var(--foreground))', + card: { + DEFAULT: 'hsl(var(--card))', + foreground: 'hsl(var(--card-foreground))', + }, + popover: { + DEFAULT: 'hsl(var(--popover))', + foreground: 'hsl(var(--popover-foreground))', + }, + primary: { + DEFAULT: 'hsl(var(--primary))', + foreground: 'hsl(var(--primary-foreground))', + }, + secondary: { + DEFAULT: 'hsl(var(--secondary))', + foreground: 'hsl(var(--secondary-foreground))', + }, + muted: { + DEFAULT: 'hsl(var(--muted))', + foreground: 'hsl(var(--muted-foreground))', + }, + accent: { + DEFAULT: 'hsl(var(--accent))', + foreground: 'hsl(var(--accent-foreground))', + }, + destructive: { + DEFAULT: 'hsl(var(--destructive))', + foreground: 'hsl(var(--destructive-foreground))', + }, + border: 'hsl(var(--border))', + input: 'hsl(var(--input))', + ring: 'hsl(var(--ring))', + chart: { + '1': 'hsl(var(--chart-1))', + '2': 'hsl(var(--chart-2))', + '3': 'hsl(var(--chart-3))', + '4': 'hsl(var(--chart-4))', + '5': 'hsl(var(--chart-5))', + }, + }, + borderRadius: { + lg: 'var(--radius)', + md: 'calc(var(--radius) - 2px)', + sm: 'calc(var(--radius) - 4px)', + }, + }, + }, + plugins: [require('tailwindcss-animate')], } export default config