Skip to content

Commit

Permalink
feat(ui): select time ui
Browse files Browse the repository at this point in the history
  • Loading branch information
SergioRibera committed Aug 14, 2024
1 parent 23b1a6f commit 3147103
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 11 deletions.
25 changes: 21 additions & 4 deletions migrations/0000_init.sql
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,39 @@
CREATE TABLE forms (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
description TEXT
image TEXT,
description TEXT NOT NULL,
duration INTEGER NOT NULL DEFAULT 45
);

-- Crear tabla de disponibilidad
CREATE TABLE availability (
id INTEGER PRIMARY KEY AUTOINCREMENT,
form_id INTEGER NOT NULL,
available BOOLEAN NOT NULL DEFAULT TRUE,
day_of_week INTEGER NOT NULL, -- 0 = Domingo, 1 = Lunes, ..., 6 = Sábado
start_time DATE NOT NULL,
end_time DATE NOT NULL,
start_time TEXT NOT NULL, -- Formato HH:MM:SS
end_time TEXT NOT NULL, -- Formato HH:MM:SS
email TEXT NOT NULL,
FOREIGN KEY (form_id) REFERENCES forms(id)
);

-- Crear tabla de citas programadas
CREATE TABLE appointments (
id INTEGER PRIMARY KEY AUTOINCREMENT,
form_id INTEGER NOT NULL,
availability_id INTEGER NOT NULL,
appointment_date DATE NOT NULL,
time TEXT NOT NULL, -- Formato HH:MM:SS
-- status TEXT NOT NULL DEFAULT 'scheduled', -- 'scheduled', 'completed', 'cancelled'
FOREIGN KEY (form_id) REFERENCES forms(id),
FOREIGN KEY (availability_id) REFERENCES availability(id)
);

-- Crear índices para mejorar el rendimiento de las consultas
CREATE INDEX idx_availability_form_id ON availability(form_id);
CREATE INDEX idx_availability_day_of_week ON availability(day_of_week);
CREATE INDEX idx_availability_email ON availability(email);
CREATE INDEX idx_appointments_form_id ON appointments(form_id);
CREATE INDEX idx_appointments_availability_id ON appointments(availability_id);
CREATE INDEX idx_appointments_date ON appointments(appointment_date);
-- CREATE INDEX idx_appointments_status ON appointments(status);
43 changes: 39 additions & 4 deletions pages/form/[id].vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<section class="container">
<Card class="card">
<template #title>
<section v-if="data && data.form" class="column">
<section v-if="data.form" class="column">
<h1 class="text-center">
{{data.form.title}}
</h1>
Expand All @@ -11,7 +11,7 @@
<span class="text-center" style="margin-bottom:2em">{{data.form.description}}</span>
<div class="flex gap bold">
<i class="flex content-center pi pi-clock" style="color:#3F3F46" />
<span class="bold" style="color:#3F3F46">45 min</span>
<span class="bold" style="color:#3F3F46">{{data.form.duration}} min</span>
</div>
<div class="flex gap bold">
<i class="flex content-center pi pi-video" style="color:#3F3F46" />
Expand All @@ -20,7 +20,25 @@
</div>
<Divider layout="vertical" />
<Divider layout="horizontal" />
<DatePicker inline :minDate="tomorrow"/>
<client-only>
<DatePicker inline :minDate="tomorrow" v-model="selectedDate" @date-select="selectDate"/>
<Listbox
v-if="selectedDate"
v-model="selectedTime"
:options="groupedAvailableTime"
scrollHeight="100%"
optionLabel="value"
optionGroupLabel="label"
optionGroupChildren="items">
<template #optiongroup="slotProps">
<div class="flex items-center">
<i v-if="slotProps.option.label == 'am'" class="flex content-center pi pi-sun" />
<i v-else class="flex content-center pi pi-moon" />
<div style="margin-left:8px">{{ slotProps.option.label }}</div>
</div>
</template>
</Listbox>
</client-only>
</section>
</section>
<h1 v-else class="text-center">No se encontro ningun Calendario</h1>
Expand All @@ -37,9 +55,26 @@
<script setup>
const route = useRoute()
const { id } = route.params
const { data, status } = await useAsyncData('getFormId', async () => await $fetch(`/api/availability/${id}`))
const today = new Date()
const selectedDate = ref()
const selectedTime = ref()
const groupedAvailableTime = ref()
const tomorrow = new Date(today.getFullYear(), today.getMonth(), today.getDate() + 1)
const { data, status } = await useAsyncData(
'getFormId',
async () => await $fetch(`/api/availability/${id}`),
)
function selectDate (date) {
console.log(date.getDay())
const availables = (data.value.availabilities || [])
.filter(el => el.day_of_week === date.getDay())
.map(el => getIntervals(el.start_time, el.end_time, data.value.form.duration))
.flat(1)
console.log(data.value.availabilities, availables)
groupedAvailableTime.value = availables
}
</script>

<style>
Expand Down
13 changes: 10 additions & 3 deletions server/api/availability/[formId].ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,17 @@ export default defineEventHandler(async ({ context }) => {
const { formId } = context.params as { formId: number }

try {
const { results: availabilities } = await db.prepare('SELECT * FROM availability WHERE form_id = ?').bind(parseInt(formId)).all()
const { results: form } = await db.prepare('SELECT * FROM forms WHERE id = ?').bind(formId).run()
const results = await db.batch([
db.prepare('SELECT * FROM forms WHERE id = ?').bind(formId),
db.prepare('SELECT * FROM availability WHERE form_id = ?').bind(formId),
db.prepare('SELECT * FROM appointments WHERE form_id = ?').bind(formId),
])

return { availabilities, form: form[0] }
return {
form: results[0].results[0] || null,
availabilities: results[1].results,
appointments: results[2].results,
}
} catch (error) {
console.error('Error al consultar la base de datos:', error)
return { error: 'Error al obtener disponibilidades' }
Expand Down
56 changes: 56 additions & 0 deletions utils/getIntervals.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/**
* Genera un array de horarios disponibles entre dos tiempos.
* @param {string} startTime - Hora de inicio en formato "HH:mm".
* @param {string} endTime - Hora de fin en formato "HH:mm".
* @param {number} duration - Duración de cada bloque en minutos.
* @param {number} breakTime - Tiempo de descanso entre bloques en minutos (por defecto 10).
* @returns {string[]} Array de horarios disponibles en formato "HH:mm".
*/
export default function getIntervals(start, end, duration, breakTime = 10) {
const result = [
{ label: "am", items: [] },
{ label: "pm", items: [] }
];

const today = new Date();
const utcToday = new Date(Date.UTC(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate()));

// Convertir start y end de string a objeto Date en UTC
const [startHours, startMinutes] = start.split(':').map(Number);
const [endHours, endMinutes] = end.split(':').map(Number);

let currentTime = new Date(utcToday.setUTCHours(startHours, startMinutes, 0));
const endTime = new Date(utcToday.setUTCHours(endHours, endMinutes, 0));

// Si el tiempo final es antes que el inicial, asumimos que cruza la medianoche
if (endTime <= currentTime) {
endTime.setUTCDate(endTime.getUTCDate() + 1);
}

while (currentTime <= endTime) {
// Convertir el tiempo UTC a tiempo local para el resultado
const localTime = new Date(currentTime.toUTCString());
const timeString = localTime.toTimeString().slice(0, 8); // Formato HH:MM:SS
const isPM = localTime.getHours() >= 12;
const item = { value: timeString };

if (isPM) {
result[1].items.push(item);
} else {
result[0].items.push(item);
}

// Avanzar el tiempo en UTC
currentTime.setUTCMinutes(currentTime.getUTCMinutes() + duration + breakTime);

const nextBlockEnd = new Date(currentTime);
nextBlockEnd.setUTCMinutes(nextBlockEnd.getUTCMinutes() + duration);

if (nextBlockEnd > endTime) {
break;
}
}

// Eliminar objetos vacíos
return result.filter(obj => obj.items.length > 0);
}

0 comments on commit 3147103

Please sign in to comment.