Reporting Dashboard - Copy this React, Tailwind Component to your project
return ( <Container> <h3>Reportes (Tablas)</h3> <Row> {/* Horas Trabajadas */} <Col md={12} className="mb 4"> <Card> <Card.Body> <h5>Reporte de Horas Trabajadas</h5> <Row> <Col md={3}> <label>Desde</label> <DatePicker selected={workedHoursFilters.fromDate} onChange={(date) => setWorkedHoursFilters((prev) => ({ ...prev, fromDate: date, })) } dateFormat="yyyy MM dd" /> </Col> <Col md={3}> <label>Hasta</label> <DatePicker selected={workedHoursFilters.toDate} onChange={(date) => setWorkedHoursFilters((prev) => ({ ...prev, toDate: date, })) } dateFormat="yyyy MM dd" /> </Col> <Col md={6} className="d flex align items end"> <Button onClick={() => handleFilter("worked hours", workedHoursFilters, setWorkedHoursData) } > Filtrar </Button> <Button variant="secondary" className="ms 2" onClick={() => { setWorkedHoursFilters({ fromDate: null, toDate: null }); fetchData(); }} > Sin Filtro </Button> <Button variant="success" className="ms 2" onClick={() => downloadCSV(workedHoursData, "horas_trabajadas.csv")} > Descargar CSV </Button> </Col> </Row> <div className="mt 3 table responsive"> <table className="table table striped"> <thead> <tr> <th>RUT</th> <th>Nombre Completo</th> <th>Fecha</th> <th>Horas Totales (hrs decimales)</th> </tr> </thead> <tbody> {workedHoursData.map((entry, index) => { const totalHours = entry.worked_hours ? (entry.worked_hours.hours || 0) + ((entry.worked_hours.minutes || 0) / 60) + ((entry.worked_hours.seconds || 0) / 3600) : 0; const fullName = getFullName(entry); return ( <tr key={index}> <td>{entry.rut_employee}</td> <td>{fullName}</td> <td>{entry.work_date}</td> <td>{totalHours.toFixed(2)}</td> </tr> ); })} </tbody> </table> </div> </Card.Body> </Card> </Col> {/* Puntualidad */} <Col md={12} className="mb 4"> <Card> <Card.Body> <h5>Reporte de Puntualidad</h5> <Row> <Col md={2}> <label>Desde</label> <DatePicker selected={punctualityFilters.fromDate} onChange={(date) => setPunctualityFilters((prev) => ({ ...prev, fromDate: date, })) } dateFormat="yyyy MM dd" /> </Col> <Col md={2}> <label>Hasta</label> <DatePicker selected={punctualityFilters.toDate} onChange={(date) => setPunctualityFilters((prev) => ({ ...prev, toDate: date, })) } dateFormat="yyyy MM dd" /> </Col> <Col md={3}> <InputGroup> <FormControl placeholder="RUT" value={punctualityFilters.rut} onChange={(e) => setPunctualityFilters((prev) => ({ ...prev, rut: e.target.value, })) } /> </InputGroup> </Col> <Col md={3}> <Dropdown onSelect={(state) => setPunctualityFilters((prev) => ({ ...prev, state, })) } > <Dropdown.Toggle> {punctualityFilters.state || "Estado"} </Dropdown.Toggle> <Dropdown.Menu> <Dropdown.Item eventKey="">Sin Filtro</Dropdown.Item> <Dropdown.Item eventKey="Atrasado">Atrasado</Dropdown.Item> <Dropdown.Item eventKey="Puntual">Puntual</Dropdown.Item> </Dropdown.Menu> </Dropdown> </Col> <Col md={2} className="d flex align items end"> <Button onClick={() => handleFilter("punctuality", punctualityFilters, setPunctualityData) } > Filtrar </Button> <Button variant="secondary" className="ms 2" onClick={() => { setPunctualityFilters({ state: "", rut: "", fromDate: null, toDate: null }); fetchData(); }} > Sin Filtro </Button> <Button variant="success" className="ms 2" onClick={() => downloadCSV(punctualityData, "puntualidad.csv")} > CSV </Button> </Col> </Row> <div className="mt 3 table responsive"> <table className="table table striped"> <thead> <tr> <th>RUT</th> <th>Nombre Completo</th> <th>Estado</th> <th>Fecha</th> </tr> </thead> <tbody> {punctualityData.map((entry, index) => { const fullName = getFullName(entry); return ( <tr key={index}> <td>{entry.rut_employee}</td> <td>{fullName}</td> <td>{entry.status}</td> <td>{entry.work_date}</td> </tr> ); })} </tbody> </table> </div> </Card.Body> </Card> </Col> {/* Ausencias */} <Col md={12} className="mb 4"> <Card> <Card.Body> <h5>Reporte de Ausencias</h5> <Row> <Col md={2}> <label>Desde</label> <DatePicker selected={absenceFilters.fromDate} onChange={(date) => setAbsenceFilters((prev) => ({ ...prev, fromDate: date, })) } dateFormat="yyyy MM dd" /> </Col> <Col md={2}> <label>Hasta</label> <DatePicker selected={absenceFilters.toDate} onChange={(date) => setAbsenceFilters((prev) => ({ ...prev, toDate: date, })) } dateFormat="yyyy MM dd" /> </Col> <Col md={2}> <FormControl placeholder="RUT" value={absenceFilters.rut} onChange={(e) => setAbsenceFilters((prev) => ({ ...prev, rut: e.target.value, })) } /> </Col> <Col md={2}> <FormControl placeholder="Tipo de Ausencia" value={absenceFilters.type} onChange={(e) => setAbsenceFilters((prev) => ({ ...prev, type: e.target.value, })) } /> </Col> <Col md={2}> <Dropdown onSelect={(justification) => setAbsenceFilters((prev) => ({ ...prev, justification, })) } > <Dropdown.Toggle> {absenceFilters.justification || "Justificación"} </Dropdown.Toggle> <Dropdown.Menu> <Dropdown.Item eventKey="">Sin Filtro</Dropdown.Item> <Dropdown.Item eventKey="Justificado"> Justificado </Dropdown.Item> <Dropdown.Item eventKey="No justificado"> No justificado </Dropdown.Item> </Dropdown.Menu> </Dropdown> </Col> <Col md={2} className="d flex align items end"> <Button onClick={() => handleFilter("absence", absenceFilters, setAbsenceData) } > Filtrar </Button> <Button variant="secondary" className="ms 2" onClick={() => { setAbsenceFilters({ type: "", justification: "", rut: "", fromDate: null, toDate: null, }); fetchData(); }} > Sin Filtro </Button> <Button variant="success" className="ms 2" onClick={() => downloadCSV(absenceData, "ausencias.csv")} > CSV </Button> </Col> </Row> <div className="mt 3 table responsive"> <table className="table table striped"> <thead> <tr> <th>RUT</th> <th>Nombre Completo</th> <th>Tipo</th> <th>Justificación</th> <th>Fecha (Inicio Ausencia)</th> </tr> </thead> <tbody> {absenceData.map((entry, index) => { const fullName = getFullName(entry); return ( <tr key={index}> <td>{entry.rut_employee}</td> <td>{fullName}</td> <td>{entry.absence_type || ""}</td> <td>{entry.justification_status}</td> <td>{entry.absence_start_date}</td> </tr> ); })} </tbody> </table> </div> </Card.Body> </Card> </Col> {/* Asistencia Diaria */} <Col md={12} className="mb 4"> <Card> <Card.Body> <h5>Reporte de Asistencia Diaria</h5> <Row> <Col md={2}> <label>Desde</label> <DatePicker selected={attendanceFilters.fromDate} onChange={(date) => setAttendanceFilters((prev) => ({ ...prev, fromDate: date, })) } dateFormat="yyyy MM dd" /> </Col> <Col md={2}> <label>Hasta</label> <DatePicker selected={attendanceFilters.toDate} onChange={(date) => setAttendanceFilters((prev) => ({ ...prev, toDate: date, })) } dateFormat="yyyy MM dd" /> </Col> <Col md={4}> <InputGroup> <FormControl placeholder="RUT" value={attendanceFilters.rut} onChange={(e) => setAttendanceFilters((prev) => ({ ...prev, rut: e.target.value, })) } /> </InputGroup> </Col> <Col md={2}> <Dropdown onSelect={(state) => setAttendanceFilters((prev) => ({ ...prev, state, })) } > <Dropdown.Toggle> {attendanceFilters.state || "Estado"} </Dropdown.Toggle> <Dropdown.Menu> <Dropdown.Item eventKey="">Sin Filtro</Dropdown.Item> <Dropdown.Item eventKey="Presente">Presente</Dropdown.Item> <Dropdown.Item eventKey="Ausente">Ausente</Dropdown.Item> </Dropdown.Menu> </Dropdown> </Col> <Col md={2} className="d flex align items end"> <Button onClick={() => handleFilter("attendance", attendanceFilters, setAttendanceData) } > Filtrar </Button> <Button variant="secondary" className="ms 2" onClick={() => { setAttendanceFilters({ state: "", rut: "", fromDate: null, toDate: null }); fetchData(); }} > Sin Filtro </Button> <Button variant="success" className="ms 2" onClick={() => downloadCSV(attendanceData, "asistencia_diaria.csv")} > CSV </Button> </Col> </Row> <div className="mt 3 table responsive"> <table className="table table striped"> <thead> <tr> <th>RUT</th> <th>Nombre Completo</th> <th>Estado</th> <th>Fecha</th> </tr> </thead> <tbody> {attendanceData.map((entry, index) => { const fullName = getFullName(entry); return ( <tr key={index}> <td>{entry.rut_employee}</td> <td>{fullName}</td> <td>{entry.attendance_status}</td> <td>{entry.work_date}</td> </tr> ); })} </tbody> </table> </div> </Card.Body> </Card> </Col> {/* Horas Extras */} <Col md={12} className="mb 4"> <Card> <Card.Body> <h5>Reporte de Horas Extras</h5> <Row> <Col md={2}> <label>Desde</label> <DatePicker selected={extraHoursFilters.fromDate} onChange={(date) => setExtraHoursFilters((prev) => ({ ...prev, fromDate: date, })) } dateFormat="yyyy MM dd" /> </Col> <Col md={2}> <label>Hasta</label> <DatePicker selected={extraHoursFilters.toDate} onChange={(date) => setExtraHoursFilters((prev) => ({ ...prev, toDate: date, })) } dateFormat="yyyy MM dd" /> </Col> <Col md={4}> <Dropdown onSelect={(extra) => setExtraHoursFilters((prev) => ({ ...prev, extra, })) } > <Dropdown.Toggle> {extraHoursFilters.extra || "Filtrar por horas extra"} </Dropdown.Toggle> <Dropdown.Menu> <Dropdown.Item eventKey="">Sin Filtro</Dropdown.Item> <Dropdown.Item eventKey="yes">Con Horas Extra</Dropdown.Item> <Dropdown.Item eventKey="no">Sin Horas Extra</Dropdown.Item> </Dropdown.Menu> </Dropdown> </Col> <Col md={4} className="d flex align items end"> <Button onClick={() => handleFilter("extra hours", extraHoursFilters, setExtraHoursData) } > Filtrar </Button> <Button variant="secondary" className="ms 2" onClick={() => { setExtraHoursFilters({ extra: "", fromDate: null, toDate: null }); fetchData(); }} > Sin Filtro </Button> <Button variant="success" className="ms 2" onClick={() => downloadCSV(extraHoursData, "horas_extras.csv")} > CSV </Button> </Col> </Row> <div className="mt 3 table responsive"> <table className="table table striped"> <thead> <tr> <th>RUT</th> <th>Nombre Completo</th> <th>Fecha</th> <th>Horas Extras</th> </tr> </thead> <tbody> {extraHoursData.map((entry, index) => { const extraHoursDecimal = entry.extra_hours_worked ? ((entry.extra_hours_worked.hours || 0) + (entry.extra_hours_worked.minutes || 0) / 60 + (entry.extra_hours_worked.seconds || 0) / 3600) : 0; const fullName = getFullName(entry); return ( <tr key={index}> <td>{entry.employee_id}</td> <td>{fullName}</td> <td>{entry.work_date}</td> <td>{extraHoursDecimal.toFixed(2)}</td> </tr> ); })} </tbody> </table> </div> </Card.Body> </Card> </Col> </Row> </Container> ); como mejorarías visualmente este componente, para que además ya no utilice react bootstrap, si no que unicamente tailwind, react chart.js
