Calendario de Reuniones

Uso de Calendarios para mostrar reuniones de los proyectos

Esta es otra de las funcionalidades requeridas de los proyectos. En este caso, el ejemplo es para informar de la propuesta de solución de representación de “calendarios” utilizando una de las librerías de Daypilot .

Como indico siempre, esto no es una solución de planificación de reuniones, si no un ejemplo de uso de estas librerías integradas en PHPRunner para que tú la adaptes a tus requisitos.

También, indicar que se puede potenciar mucho más el uso de este interface y sus interacciones con PHPRunner, pero para dejar un ejemplo no muy complejo se ha dejado bastante simple, no obstante se deja código comentado para evolucionar y, siempre me podéis preguntar, si algo no veis claro.

Definición de los requisitos del ejemplo

He partido de un esquema de Base de Datos que ya había trabajado en los ejemplos de Project y de Kanban, en donde teníamos Proyectos que a su vez estaban relacionados con Compañías y Departamentos de estas Compañías.

Las reuniones (Meetings) las he hecho depender de los proyectos y a ellas se les asigna Recursos (Usuarios) y Ficheros (para compartir antes de la reunión).

El aplicativo tiene que facilitar:

  • Mostrar las reuniones de un Proyecto seleccionado, o de una Compañía o de un Departamento.
  • Mostrar las reuniones de un usuario.
  • Mostrar la ocupación de los usuarios relacionados en una Reunión, para ver si la fecha propuesta (fecha de la reunión) está disponible o no para todos los usuarios.

A continuación muestro algunas de las pantallas del ejemplo y explico algunas características reseñables de las mismas.

Esta es la pantalla del menú y no tiene nada de especial (con respecto a los otros ejemplos que os he facilitado).

Tiene:

  • Una imagen de fondo.
  • Un icono de la aplicación.

Estas 5 tablas tienen el mismo aspecto que esta, es decir, se ha añadido un botón para mostrar el Calendario de reuniones filtrado por la Entidad y registro en donde está el botón que se pulsa.

El calendario solamente tiene un representación para todos los botones de todas las entidades, también, se ajusta a la disponibilidad del espacio disponible en pantalla, pero no está adaptado para el móvil.

Los proyectos y usuarios tienen un color asignado y se utiliza en la presentación.

Desde esta misma pantalla, se pueden dar nuevas altas de reuniones y haciendo clic sobre una de las reuniones, se pasa a consulta y de ahí, se puede pasar a edición de la reunión. Siempre, en las altas de reuniones y modificación de las mismas se produce un recarga de la pantalla y todos sus datos.

Las pantallas de alta, consulta y edición, son las pantallas de PHPRunner en su versión de “popup”.

Daypilot tiene unas librerías javascript muy, muy potentes y todas ellas se utilizan de la misma forma que este ejemplo, por lo que si tú necesitas mostrar y gestionar tus datos asociados a fechas, seguramente tienes solución con estas librerías.

Para poder probar el ejemplo, conectaros a https://fhumanes.com/meetings/ . Utilizad el usuario “admin”  y password “admin”. Por favor, no destruyáis los datos. Si por error se borran, os ruego me lo digáis (email) para restaurarlos.

Os muestro el código de presentación, que está separado en el fichero  “ICM/calendar.php”.

calendar.php
 
<?php
global $conn;
/* Session variable
$_SESSION['project_id']
$_SESSION['project_name']
$_SESSION['option_calendar'] valor:  project | user | company | departament | meeting
$_SESSION['language']
$_SESSION['user_id']
$_SESSION['login'] 
$_SESSION['email'] 
$_SESSION['username']
$_SESSION['company_id']
$_SESSION['dept_id']
 * ----------Currently unused ------------------
$_SESSION['nav_start'];
$_SESSION['nav_end']
 * ----------------------------------------------
 */
$site       = $_SESSION['config'][array_search('URL', array_column($_SESSION['config'], 'name'))][value].'/';
// $ajax_mes   = $_SESSION['config'][array_search('MEETINGS_AJAX', array_column($_SESSION['config'], 'name'))][value].'?';
/*  ----------Currently unused ------------------
    $start_date = date("Y-m-d",strtotime("-1 month",strtotime(date("Y-m-01",strtotime("now")))));
    $end_date   = date("Y-m-d",strtotime("+1 month",strtotime(date("Y-m-01",strtotime("now")))));
    $day =  date("Y-m-d",strtotime(date("Y-m-01",strtotime("now"))));
    $start_date.='T12:00:00';
    $end_date.='T12:00:00';
    $day.='T12:00:00';
 */
if ($_SESSION['language'] == 'Spanish') {
    $language = 'es-es'; // Spanish
} else {
    $language = 'en-us'; // English
}
switch ($_SESSION['option_calendar']) {
    case 'project':
        $sql = "SELECT idmeetings id , m.name, username resource, m.start_date, m.end_date, p.color_identifier color
                FROM meetings m
                join users on ( owner = user_id )
                join projects p on (projects_project_id = project_id)
                where projects_project_id =" . $_SESSION['project_id'];
        break;
    case 'user':
        $sql = "SELECT distinct idmeetings id , m.name, username resource, m.start_date, m.end_date, p.color_identifier color
            FROM meetings m
            join projects p on (m.projects_project_id = p.project_id)
            join users_meetings um on (um.meetings_idmeetings = m.idmeetings)
            join users u on ( um.users_user_id = u.user_id )
            where u.user_id =" . $_SESSION['user_id'];
        break;
    case 'company':
        $sql = "SELECT idmeetings id , m.name, username resource, m.start_date, m.end_date, p.color_identifier color
            FROM meetings m
            join users u on ( owner = user_id )
            join projects p on (m.projects_project_id = p.project_id)
            join companies c on (p.companies_company_id = c.company_id)
            where c.company_id =" . $_SESSION['company_id'];
        break;
    case 'departament':
        $sql = "SELECT idmeetings id , m.name, username resource, m.start_date, m.end_date, p.color_identifier color
            FROM meetings m
            join users u on ( owner = user_id )
            join projects p on (m.projects_project_id = p.project_id)
            join departments d on (p.departments_dept_id = d.dept_id)
            where d.dept_id =" . $_SESSION['departament_id'];
        break;
    case 'meeting':
        $sql = "SELECT idmeetings id , m.name, u.username resource, m.start_date, m.end_date, u.color_identifier color
            FROM meetings m
            join users_meetings um on (um.meetings_idmeetings = m.idmeetings)
            join users u on ( um.users_user_id = u.user_id )
            where idmeetings <>" . $_SESSION['meeting_id'];
        break;
}
$resql = db_query($sql,$conn);
If (mysqli_num_rows($resql) == 0) { // No records
    echo 'There is no information to show';    
} else {
$str1 = <<<EOD
    <!-- helper libraries 
    <script src="daypilot/js/jquery-1.12.2.min.js" type="text/javascript"></script>
      -->
    <!-- daypilot libraries -->
    <script src="daypilot/js/daypilot-all.min.js?v=2019.4.4160" type="text/javascript"></script>
    <!-- daypilot themes -->
    <link type="text/css" rel="stylesheet" href="daypilot/themes/areas.css?v=2019.4.4160" />
    <link type="text/css" rel="stylesheet" href="daypilot/themes/month_white.css?v=2019.4.4160" />
     <!-- 
    <link type="text/css" rel="stylesheet" href="../demo/themes/month_green.css?v=2019.4.4160" />
    <link type="text/css" rel="stylesheet" href="../demo/themes/month_transparent.css?v=2019.4.4160" />
    <link type="text/css" rel="stylesheet" href="../demo/themes/month_traditional.css?v=2019.4.4160" />
     -->
    <link type="text/css" rel="stylesheet" href="daypilot/themes/navigator_8.css?v=2019.4.4160" />
    <link type="text/css" rel="stylesheet" href="daypilot/themes/navigator_white.css?v=2019.4.4160" />
    <link type="text/css" rel="stylesheet" href="daypilot/themes/calendar_white.css?v=2019.4.4160" />
    <!-- 
    <link type="text/css" rel="stylesheet" href="../demo/themes/calendar_transparent.css?v=2019.4.4160" />
    <link type="text/css" rel="stylesheet" href="../demo/themes/calendar_green.css?v=2019.4.4160" />
    <link type="text/css" rel="stylesheet" href="../demo/themes/calendar_traditional.css?v=2019.4.4160" />
    -->
     
</head>
<body>
<div style="float:left">
    <div id="nav"></div>
</div>
<div style="margin-left: 243px;">
    <div id="dp"></div>
</div>
<div id="print"></div>
<script type="text/javascript">
// AJAX function to communicate actions to the Server
         
    var HttpClient = function() {
        this.get = function(aUrl, aCallback) {
            var anHttpRequest = new XMLHttpRequest();
            anHttpRequest.onreadystatechange = function() { 
                if (anHttpRequest.readyState == 4 && anHttpRequest.status == 200)
                    aCallback(anHttpRequest.responseText);
            }
            anHttpRequest.open( "GET", aUrl, true );            
            anHttpRequest.send( null );
        }
    }
EOD;
$str2 = "
    // -------------------------------Currently unused ------------------------------------------------------
    // var site='$site'                     // Host URL
    // var url ='$ajax_mes';                // To report the month change 
    // -----------------------------------------------------------------------------------------------------
";
$str3 = <<<EOD
    var nav = new DayPilot.Navigator("nav");
    nav.showMonths = 2;
    nav.skipMonths = 1; 
    nav.selectMode = "week";
    nav.freeHandSelectionEnabled = true;
    nav.showWeekNumbers = true;
    nav.visibleRangeChangedHandling = "Disabled"; // It only works in ASP.NET
    nav.selectionDay = DayPilot.Date.today();
     
    // nav.select(date_start);            // Set the submission date
    // nav.selectionDay = date_day; 
         
    nav.onTimeRangeSelected = function(args) {
        console.log(args);
        var start = args.start;
        var end   = args.end;
         
        dp.startDate = args.start;
        dp.update();
    };
     
    nav.onBeforeCellRender = function(args) {
        if (args.cell.isCurrentMonth) {
            args.cell.cssClass = "current-month";
        }
    };
      
    nav.onVisibleRangeChange = function(args) { // When the months change
   
      var start = args.start;
      var end = args.end;
        var day = nav.selectionDay;
      console.log('Start: '+start+ ' End: '+ end+ ' Day: '+day);
      if (start <= nav.selectionDay && nav.selectionDay < end) {
            return;
         }
         
        // Update request for date change and data reload ----Currently unused -------
        // var client = new HttpClient();
        // client.get(site+url+'start='+start+'&end='+end, function(response) {
        //     console.log('Respuesta: '+response);
        //     location.reload();
        //     }); 
         
        var day = nav.selectionDay.getDay();
        var target = start.firstDayOfMonth().addDays(day);
        nav.select(target);
    };
     
    var dp = new DayPilot.Calendar("dp");
EOD;
$str4 = "       
    nav.locale = '$language';       // Language   
    dp.locale = '$language';       // Language 
";
$str5 = <<<EOD
         
    // view
    dp.startDate = nav.selectionDay;
    dp.viewType = "Week";
    // event creating
       
    /*
    dp.onTimeRangeSelected = function (args) {
        var name = prompt("New event name:", "Event");
        if (!name) return;
        var e = new DayPilot.Event({
            start: args.start,
            end: args.end,
            id: DayPilot.guid(),
            resource: args.resource,
            text: "Event"
        });
        dp.events.add(e);
        dp.clearSelection();
        dp.message("Created");
    };
    */
     
    dp.eventDeleteHandling = "Disabled";
      /*
    dp.onEventDelete = function(args) {
        alert("deleting: " + args.e.text());
    };
      */
    // bubble, with async loading
    dp.bubble = new DayPilot.Bubble({
        onLoad: function(args) {
            var ev = args.source;
            //alert("event: " + ev);
            args.async = true;  // notify manually using .loaded()
            // simulating slow server-side load
            setTimeout(function() {
                args.html = "Id: " + ev.id() + "<BR>Resource: " + ev.resource();
                args.loaded();
            }, 500);
        }
    });
/*
    dp.contextMenu = new DayPilot.Menu({
        items: [
        {text:"Show event ID", onclick: function() {alert("Event value: " + this.source.value());} },
        {text:"Show event text", onclick: function() {alert("Event text: " + this.source.text());} },
        {text:"Show event start", onclick: function() {alert("Event start: " + this.source.start().toStringSortable());} },
        {text:"Delete", onclick: function() { dp.events.remove(this.source); } }
    ]});
*/
    // event moving
      dp.eventMoveHandling = "Diabled";
/*
    dp.onEventMoved = function (args) {
        dp.message("Moved: " + args.e.text());
    };
*/
    // event resizing
      dp.eventResizeHandling = "Disabled";
/*
    dp.onEventResized = function (args) {
        dp.message("Resized: " + args.e.text());
    };
*/
    // event creating
    /*
    dp.onTimeRangeSelected = function (args) {
        var name = prompt("New event name:", "Event");
        dp.clearSelection();
        if (!name) return;
        var e = new DayPilot.Event({
            start: args.start,
            end: args.end,
            id: DayPilot.guid(),
            resource: args.resource,
            text: name
        });
        dp.events.add(e);
        dp.message("Created");
    };
    */
    
   /*
   // Right button in range
    dp.onTimeRangeRightClick = function(args) {
        window.console && console.log(args);
    };
      */
      /*
    dp.onTimeRangeDoubleClicked = function(args) {
        alert("DoubleClick: start: " + args.start + " end: " + args.end + " resource: " + args.resource);
    };
      */
    dp.onEventClick = function(args) { // Run in VIEW popup of the meeting
            var meeting = args.e.id();
            var url = "calendar_view.php?editid1="+meeting;
            var header = '<h2 data-itemtype="view_header" data-itemid="view_header" data-pageid="10">'+'Meeting: '+ meeting+'</h2>' ;
            window.popup = Runner.displayPopup({
                    url: url,
                    width: 800,
                    height: 550,
                    header: header
            });      
    };
    dp.eventDoubleClickHandling = "Disabled";
      /*
    dp.onEventDoubleClick = function(args) {
        alert("double click");
    };
      */
//   dp.showEventStartEnd = true;
    dp.scrollLabelsVisible = true;
    
 /*
   // Right button in range
    dp.contextMenuSelection = new DayPilot.Menu({
        items: [
            {
                'text': 'Create new event (JavaScript)', 'onclick': function () {
                dp.events.add(new DayPilot.Event({
                    start: this.source.start,
                    end: this.source.end,
                    text: "New event",
                    resource: this.source.resource
                }));
            }
            },
            {'text': '-'},
            {
                'text': 'Show selection details', 'onclick': function () {
                alert('Start: ' + this.source.start + '\nEnd: ' + this.source.end + '\nResource id: ' + this.source.resource);
            }
            },
            {
                'text': 'Clean selection', 'onclick': function () {
                dp.clearSelection();
            }
            }]
    });
*/
//  dp.timeRangeSelectingStartEndEnabled = true;
EOD;
$str6 = '';
while ($row = db_fetch_array($resql)) { // Loop of records processing
$str6.= 
' 
        var e = new DayPilot.Event({
            start: new DayPilot.Date("'.substr($row[start_date],0,10).'T'.substr($row[start_date],-8).'"),
            end: new DayPilot.Date("'.substr($row[end_date],0,10).'T'.substr($row[end_date],-8).'"),
            id: "'.$row[id].'",
            text: "'.addslashes($row[name]).'",
            resource: "'.addslashes($row[resource]).'",
            barColor: "'.$row[color].'"
        });
        dp.events.add(e);
' ;            
};
 $str6 .= <<<EOD
         
    nav.init(); 
    dp.init();
</script>
EOD;
echo $str1.$str2.$str3.$str4.$str5.$str6;
}; // end of record control
?>

Para las dudas o lo que os surja, por favor, indicádmelo a través de mi email [email protected].

Os dejo los ficheros del ejemplo.

Adjuntos

Archivo Tamaño de archivo Descargas
zip Proyecto PHPRunner 10.2 6 MB 973
zip Backup de Base de Datos del ejemplo 10 KB 750

Blog personal para facilitar soporte gratuito a usuarios de PHPRunner