Overview

  • The React Scheduler component can be set a timesheet mode, where days are displayed on the vertical axis and hours on the horizontal axis.

  • Learn how to hide non-business hours, add daily totals to the row headers and scroll to the specified time of day.

  • Tasks listed in the timesheet can be allocated to a particular project and differentiated using color-coding.

  • You can generate your own React project with a preconfigured timesheet using the UI Builder online application.

License

Licensed for testing and evaluation purposes. Please see the license agreement included in the sample project. You can use the source code of the tutorial if you are a licensed user of DayPilot Pro for JavaScript.

How to Set Up the React Timesheet?

react timesheet setup

The React timesheet is implemented using the React Scheduler component from DayPilot Pro for JavaScript. In order to display the timesheet, you need to switch the Scheduler to the timesheet mode using the viewType property. In the timesheet mode, the Scheduler displays days on the vertical axis and hours of day on the horizontal axis. Each day is displayed as one row.

The row headers show the date in the predefined format (datePattern of the current locale). You can add additional information to the row. In this example, we add one more column which will show the day of week. In the next step, we will see how to add a column with a daily summary.

The timesheet displays 24 hours on the horizontal axis. Our component displays cells with a duration of 15 minutes. The time headers at the top of the timesheet show hours and minutes of each time column.

The cell width is set to be calculated automatically (cellWidthSpec: "Auto"). The Scheduler will fill the available horizontal space with the grid and no horizontal scrollbar will be visible. However, to maintain readability, we also add the cellWidthMin property and specify the minimum cell width. If the screen is not wide enough to display all cell in full width, the minimum cell width will be used and the Scheduler will display a horizontal scrollbar.

import React, {useState} from 'react';
import {DayPilot, DayPilotScheduler} from "daypilot-pro-react";

const Timesheet = () => {

  const [config, setConfig] = useState({
    rowHeaderColumns: [
      {name: "Date"},
      {name: "Day", width: 40}
    ],
    onBeforeRowHeaderRender: (args) => {
      args.row.columns[0].horizontalAlignment = "center";
      args.row.columns[1].text = args.row.start.toString("ddd");
    },
    cellWidthSpec: "Auto",
    cellWidthMin: 25,
    timeHeaders: [{"groupBy":"Hour"},{"groupBy":"Cell","format":"mm"}],
    scale: "CellDuration",
    cellDuration: 15,
    days: DayPilot.Date.today().daysInMonth(),
    viewType: "Days",
    startDate: DayPilot.Date.today().firstDayOfMonth(),
    showNonBusiness: true,
    // ...
  });

  return (
    <div>
      <DayPilotScheduler
        {...config}
      />
    </div>
  );
}

export default Timesheet;

How to Hide Non-Business Hours in the React Timesheet?

react timesheet hide non business hours

By default, the timesheet displays full 24 hours. The business hours defined as Monday to Friday, 9 am to 5 pm and the non-business hours use a different background color for the timesheet cells. If you don’t need to display the full time range in your React application, you can hide the non-business hours.

JSX:

<label><input type={"checkbox"} onChange={changeBusiness} checked={showBusinessOnly} /> Show only business hours</label>

Event handler:

const changeBusiness = (e) => {
  setShowBusinessOnly(e.target.checked);
  setConfig(prevConfig => ({
    ...prevConfig,
    showNonBusiness: !e.target.checked
  }));
}

By default, this mode hides weekends from the timesheet as well. If you want to display the weekends (Saturday, Sunday), you can use businessWeekends property.

How to Show Daily Totals in the React Timesheet?

react timesheet daily totals

You can show the daily totals in the rows headers by adding a custom column with a calculated value:

1. Extend the rowHeaderColumns array with a new column (“Total”).

setConfig(prevConfig => ({
  ...prevConfig,
  rowHeaderColumns: [
    {name: "Date"},
    {name: "Day", width: 40},
    {name: "Total", width: 60}
  ]
}));

2. Use onBeforeRowHeaderRender event handler to define the column content. You can calculate the total hours using events.totalDuration() method of the DayPilot.Row object:

onBeforeRowHeaderRender: (args) => {
  // ...
  args.row.columns[2].text = args.row.events.totalDuration().toString("h:mm");
},

How to Scroll to the Specified Time of Day in the React Timesheet?

To set the initial scrollbar position (or scroll to a specified time of day anytime later), you can use the scrollTo() method:

useEffect(() => {
  getScheduler().scrollTo(DayPilot.Date.today().firstDayOfMonth().addHours(9));
}, []);

The date part of the parameter will be used to scroll vertically to the given date. If you want to only set the horizontal position, use the date derived from the startDate value (the first visible day).

How to Create a New Timesheet Record?

react timesheet new task modal dialog

New timesheet records can be added using drag and drop. The timesheet component fires the onTimeRangeSelected event handler when a user selects a time range:

1. It opens a modal dialog for entering the timesheet task details using DayPilot.Modal.form() method. This method lets you programmatically define a modal dialog with the specified fields. The modal form has the following fields:

2. The initial data object defines the preset values (start, end, project id, and text).

3. The "en-us" locale defined using the options parameter defines how the date and time values will be formatted.

4. Note that the modal dialog implements a basic date range validation using onValidate property of the end date/time field. This makes sure that the provided end is not before the start.

When the user confirms the input data, a new record is added to the React timesheet using the events.add() method. The data provided by the user are available in the modal.result property.

const [config, setConfig] = useState({
  // ...
  onTimeRangeSelected: async (args) => {
    const dp = args.control;
    const form = [
      {name: "Text", id: "text"},
      {name: "Start", id: "start", type: "datetime"},
      {name: "End", id: "end", type: "datetime", onValidate: (args) => {
          if (args.values.end.getTime() < args.values.start.getTime()) {
            args.valid = false;
            args.message = "End must be after start";
          }
        }
      },
      {name: "Project", id: "project", options: projects}
    ];
    const data = {
      start: args.start,
      end: args.end,
      project: projects[0].id,
      text: "New task"
    };
    const options = {
      locale: "en-us",
    };
    const modal = await DayPilot.Modal.form(form, data, options);
    dp.clearSelection();
    if (modal.canceled) { return; }
    dp.events.add({
      start: args.start,
      end: args.end,
      id: DayPilot.guid(),
      resource: args.resource,
      text: modal.result
    });
  }
  // ...
});

How to Show Project Details in the Timesheet Tasks?

react timesheet task project details

To display additional information in the task box, you can use the onBeforeEventRender event handler. This handler lets you customize the event content (color, text, CSS class) and add active elements (icons, buttons, drag handles, etc.).

We will use the active areas feature to add multiple text fields at specific positions within the task box. Active areas are also supported during image and PDF export.

Note that we clear the default content (args.data.html = "";) and show three active areas:

  • the task description (args.data.text) at the top

  • the task project (args.data.project) at the bottom

  • and the task duration on the right

const [config, setConfig] = useState({
  // ...
  onBeforeEventRender: (args) => {
    const duration = new DayPilot.Duration(args.data.start, args.data.end);
    const project = projects.find(p => p.id === args.data.project);
    args.data.barColor = project.color;

    args.data.html = "";
    args.data.areas = [
      {
        top: 5,
        left: 5,
        text: args.data.text,
      },
      {
        top: 20,
        left: 5,
        text: project.name,
        fontColor: "#999999"
      },
      {
        top: 13,
        right: 5,
        text: duration.toString("h:mm"),
        fontColor: "#999999"
      }
    ];

  },
  // ...
});

To provide an at-a-glance view of task distribution among projects, we will implement color-coding. Each task bar will be set to a project-specific color using args.data.backColor":

const project = projects.find(p => p.id === args.data.project);
args.data.barColor = project.color;

The timesheet projects are defined statically using a simple array. In a typical production React timesheet application, you would load them from a server-side API instead.

const projects = [
  {id: 1, name: "Project A", color: "#38761d"},
  {id: 2, name: "Project B", color: "#0d8ecf"},
  {id: 3, name: "Project C", color: "#f1c232"},
];