noalyss-commit
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Noalyss-commit] [noalyss] 01/09: widget : Creation of widget for the DA


From: dwm
Subject: [Noalyss-commit] [noalyss] 01/09: widget : Creation of widget for the DASHBOARD Contains widget Agenda, Todo List, Report , ...
Date: Mon, 19 Aug 2024 15:03:09 -0400 (EDT)

sparkyx pushed a commit to branch unstable
in repository noalyss.

commit 16347125b10bdb92f0ae5e172769b64921b293a0
Author: sparkyx <danydb@noalyss.eu>
AuthorDate: Sun Aug 11 10:37:43 2024 +0200

    widget : Creation of widget for the DASHBOARD Contains widget Agenda, Todo 
List, Report ,...
---
 html/ajax_misc.php                                 |  10 +
 html/css/style-classic7.css                        | 130 +++++++-
 html/js/noalyss_script.js                          | 271 ++++++++++++++++-
 html/js/todo_list.js                               |   8 +-
 include/ajax/ajax_preference.php                   |  19 --
 include/class/acc_ledger.class.php                 |   7 +-
 include/class/noalyss_user.class.php               |  31 --
 include/constant.php                               |   3 +-
 include/dashboard.inc.php                          |  15 +-
 include/template/calendar.php                      |   7 +-
 include/template/dashboard.php                     | 210 ++-----------
 .../status_operation_event-main_display.php        |   2 +-
 include/widget/agenda/agenda.php                   |  55 ++++
 include/widget/ajax.php                            | 140 +++++++++
 include/widget/bookmark/bookmark.php               |  72 +++++
 include/widget/bookmark/install.php                |  33 ++
 include/widget/coming_event/coming_event.php       |  44 +++
 include/widget/event/event-display.php             |  54 ++++
 include/widget/event/event.php                     |  71 +++++
 include/widget/event/install.php                   |  33 ++
 include/widget/invoice/install.php                 |  33 ++
 include/widget/invoice/invoice-display.php         |  64 ++++
 include/widget/invoice/invoice.php                 |  82 +++++
 include/widget/last_event/last_event-display.php   |  46 +++
 include/widget/last_event/last_event.php           |  51 ++++
 .../last_operation/last_operation-display.php      |  54 ++++
 include/widget/last_operation/last_operation.php   |  49 +++
 include/widget/mini_report/mini_report.php         | 120 ++++++++
 .../todo_list/todo_list-display_new_note.php       |  45 +++
 include/widget/todo_list/todo_list.php             |  77 +++++
 include/widget/widget-development.md               | 108 +++++++
 include/widget/widget.php                          | 335 +++++++++++++++++++++
 sql/upgrade.sql                                    |  59 ++++
 33 files changed, 2065 insertions(+), 273 deletions(-)

diff --git a/html/ajax_misc.php b/html/ajax_misc.php
index bb676e1e3..7f4a49dc3 100644
--- a/html/ajax_misc.php
+++ b/html/ajax_misc.php
@@ -143,6 +143,16 @@ if ( LOGINPUT)
         fwrite($file_loginput,"include '".basename(__FILE__)."';\n");
         fclose($file_loginput);
     }
+
+/**
+ * for widget we call immediately a file outside the ajax folder
+ */
+if ($op == 'widget') {
+    session_write_close();
+    require_once NOALYSS_INCLUDE.'/widget/ajax.php';
+    return;
+}
+
 $path = array(
     // search accounting , detail ...
     "account"=>"ajax_poste",
diff --git a/html/css/style-classic7.css b/html/css/style-classic7.css
index 186268a2b..940c1606c 100644
--- a/html/css/style-classic7.css
+++ b/html/css/style-classic7.css
@@ -405,9 +405,7 @@ tr.odd > td,tr.even > td {
 tr.highlight {
     font-weight: bold;
 }
-div.even,tr.even {
-    /*! background-color: #F7F8FC; */
-}
+
 td.odd{
     background-color:#DDE6FF;
 }
@@ -813,7 +811,7 @@ div.content div.pc_calendar tr{
 }
 @media only screen and (min-width: 640px) {
     div.pc_calendar tr{
-    height:110px;
+    height:10%;
     }
 }
 @media only screen and (max-width: 640px) {
@@ -1449,6 +1447,12 @@ h2.title {
     font-family: OpenSansRegular;
     margin-bottom: 1px;
 }
+
+ .box h2.title {
+     font-size: 1rem;
+     font-variant-caps: normal;
+ }
+
 h3.title {
     color: #0000FF;
     margin-left: 30px;
@@ -1802,9 +1806,6 @@ div.box  {
     font-size:0.90rem;
     border-radius:5px;
     width:97%;
-    margin:1% 1.5% 1% 1.5%;
-    margin-right:2px
-   
 }
 /* SM */
 @media (min-width: 576px) {
@@ -1819,16 +1820,17 @@ div.box  {
 @media (min-width: 992px) {
     div.box  {
         width: 46%;
-        margin: 1.5%;
 
+        height:40%;
     }
 
 }
 /* XL */
 @media (min-width: 1200px) {
     div.box  {
-        width: 32.8%;
-        margin: 0.2%;
+        width: 35rem;
+        max-height: 30%;
+        height: 45vh;
 
     }
 }
@@ -1836,14 +1838,18 @@ div.box  {
 div.box table{
     border-spacing:0px;
     font-size: 96%;
+    width: 80%;
 }
-div.box tr.odd {
-    background-color: lightblue;
-    
+div.odd, div.box tr.odd {
+    background-color: #dce7f5;
+    border: 1px solid whitesmoke;
+    padding:2px;
+
 }
-div.box tr.even {
+div.even ,div.box tr.even {
     background-color: white;
-    
+    border: 1px solid whitesmoke;
+    padding:2px;
 }
 #jrn_name_div h2{
     font-size:38.4px;
@@ -3462,4 +3468,98 @@ li.li-active {
 
  .hoverclass-drag {
     background-color: white;
+ }
+
+
+ 
/**********************************************************************************************************************
+  * User's widget list
+  *
+  
*********************************************************************************************************************/
+ #widget_box_id {
+     #widget_box_id {
+         width: 100%;
+         margin-left: 0px;
+         margin-right: 0px;
+     }
+ }
+
+ #contain_widget  li {
+     padding : 0.75rem;
+     margin:2px;
+     border:navy solid 1px;
+ }
+
+ #widget_add  li {
+     padding : 0.75rem;
+     margin:2px;
+     border:navy solid 1px;
+ }
+
+
+ @media  (min-width: 576px) {
+
+
+ }
+ /* MeDium */
+ @media (min-width: 768px) {
+     #widget_box_id {
+         width: 100%;
+         margin-left: 0px;
+         margin-right: 0px;
+     }
+ }
+
+ /* LarGe */
+ @media (min-width: 992px) {
+     #widget_box_id {
+         width: 70%;
+         margin-left: 15%;
+
+     }
+ }
+ /* eXtraLarge */
+ @media (min-width: 1200px) {
+
+ }
+span.widget-name {
+    font-size:105%;
+    font-family: bold;
+    font-stretch: extra-expanded;
+    margin-right: 2rem;
+    margin-top: -5px;
+    width: 5rem;
+    display: inline-block;
+
+}
+
+ #dashboard_div_id {
+     margin-top:2vh;
+     display: flex;
+     flex-wrap: wrap;
+     align-items: center;
+     justify-content: center;
+     row-gap: 20px;
+     column-gap: 20px;
+ }
+
+ /*
+  * WIDGET
+  */
+ #widget_box_select_id {
+     max-width: 70rem;
+ }
+ #widget_box_id {
+     max-width: 70rem;
+
+ }
+ .widget_param {
+     font-size: 105%;
+     font-weight: bold;
+     font-style: inherit;
+     border-width: 1px;
+     display: inline-block;
+     border-color: navy;
+     padding: 5px;
+     border-style: groove;
+     margin: 2px;
  }
\ No newline at end of file
diff --git a/html/js/noalyss_script.js b/html/js/noalyss_script.js
index e21663ea0..2b2ec4dcb 100644
--- a/html/js/noalyss_script.js
+++ b/html/js/noalyss_script.js
@@ -2886,11 +2886,11 @@ function calendar_zoom(obj) {
                         obj.outdiv = 'calendar_zoom_div';
                     }
                     if ($(obj.outdiv) == undefined) {
-                        var str_style = 'top:10%;margin-left:2%;';
+                        var str_style = 'top:10%;min-height:60rem';
 //                            var str_style = fixed_position(0, 120);
                         add_div({
                             id: obj.outdiv,
-                            style: 'margin-left:3%;width:94%;' + str_style,
+                            style: 'width:94%;' + str_style,
                             cssclass: "inner_box",
                             drag: 0
                         });
@@ -4323,3 +4323,270 @@ function activate_plugin(elt)
                        alert_box(e.message);
                }
 }
+/**********************************************************************************************************************/
+/**
+ * @class Widget
+ */
+/**********************************************************************************************************************/
+
+Widget = function(dossier_id) {
+    this.dossier_id=dossier_id;
+}
+/**
+ * Display the widget in the elt box
+ * @param box DOMID of the target
+ * @param dossier_id
+ * @param user_widget_id
+ * @param widget_code
+ */
+Widget.prototype.display = function (box,user_widget_id,widget_code) {
+    try {
+
+        var queryString = {
+            gDossier: this.dossier_id,
+            'op': 'widget',
+            'user_widget_id': user_widget_id,
+            'widget_code': widget_code,
+            'action': 'widget.display'
+        }
+        var action = new Ajax.Request(
+            "ajax_misc.php",
+            {
+                method: 'GET',
+                parameters: queryString,
+                onFailure: ajax_misc_failure,
+                onSuccess: function (req) {
+                    if (req.responseText == 'NOCONX') {
+                        reconnect();
+                        return;
+                    }
+                    $(box).replace(req.responseText);
+
+                }
+            }
+        );
+    } catch (e) {
+        alert_box(e.message);
+    }
+
+}
+
+/**
+ * Manage the widget
+ * @param dossier_id
+ * @returns {boolean}
+ */
+Widget.prototype.manage = function () {
+    try {
+        var box = 'widget_box_id';
+        var queryString = {
+            gDossier: this.dossier_id,
+            'op': 'widget',
+            'action': 'widget.manage'
+        }
+        var action = new Ajax.Request(
+            "ajax_misc.php",
+            {
+                method: 'GET',
+                parameters: queryString,
+                onFailure: ajax_misc_failure,
+                onSuccess: function (req) {
+                    if (req.responseText == 'NOCONX') {
+                        reconnect();
+                        return;
+                    }
+                    var style = 'position:absolute;';
+                    var y = calcy(200);
+                    style = style + ' ;top : ' + y + 'px';
+
+                    add_div({id: box, cssclass: 'inner_box', html: loading(), 
style: style})
+
+                    $(box).update(req.responseText);
+
+                }
+            }
+        );
+    } catch (e) {
+        alert_box(e.message);
+        console.error("widget_manage" + e.message);
+    }
+    return false;
+}
+/**
+ * create a list  of sortable elements
+ */
+Widget.prototype.create_sortable=function() {
+
+    Sortable.create('contain_widget',{tag:'li',onUpdate:function(){ 
$('order_widget_hidden').value=Sortable.serialize('contain_widget')}})
+    $('order_widget_hidden').value=Sortable.serialize('contain_widget');
+}
+/**
+ * Save the order of widget
+ **/
+Widget.prototype.save = function () {
+       try
+               {
+                var here = this;
+               var dgbox="widget_box_id";
+               waiting_box();
+
+               // For form , most of the parameters are in the FORM
+               // method is then POST
+                //var queryString=$(p_form_id).serialize(true);
+
+              var queryString = {
+                       op : 'widget',
+                       action : 'widget.save',
+                       gDossier: this.dossier_id,
+                        param : Sortable.serialize('contain_widget')
+                   };
+               var action = new Ajax.Request(
+                                         "ajax_misc.php" ,
+                                         {
+                                             method:'GET',
+                                             parameters:queryString,
+                                             onFailure:ajax_misc_failure,
+                                             onSuccess:function(req){
+                                                       remove_waiting_box();
+                               if (req.responseText == 'NOCONX') {
+                                   reconnect();
+                                   return;
+                               }
+                                                       removeDiv(dgbox)
+                                here.refresh();
+
+                                             }
+                                         }
+                     );
+               }catch( e)
+               {
+                       alert_box(e.message);
+               }
+}
+/**
+ * refresh the DASHBOARD (dashboard_div_id)
+ */
+Widget.prototype.refresh = function () {
+    try {
+        var dgbox='dashboard_div_id'
+        var queryString = {
+            op : 'widget',
+            action : 'widget.refresh',
+            gDossier: this.dossier_id
+        };
+        var action = new Ajax.Request(
+                  "ajax_misc.php" ,
+                  {
+                      method:'GET',
+                      parameters:queryString,
+                      onFailure:ajax_misc_failure,
+                      onSuccess:function(req){
+                        if (req.responseText == 'NOCONX') {
+                            reconnect();
+                            return;
+                        }
+
+                        $(dgbox).replace(req.responseText);
+
+                      }
+                  }
+              );
+        }catch( e) {
+        console.error("widget.refresh "+e.message)
+        }
+}
+/**
+ * delete a widget : remove from the list
+ * @param user_widget_id {integer}
+ */
+Widget.prototype.delete=function (user_widget_id) {
+    $('elt_'+user_widget_id).remove()
+    $('order_widget_hidden').value=Sortable.serialize('contain_widget');
+}
+/**
+ * display list widget we can add
+ */
+Widget.prototype.input = function () {
+    try {
+        var box="widget_box_select_id";
+
+        var queryString = {
+            op: 'widget',
+            action: 'widget.input',
+            gDossier: this.dossier_id
+        };
+        var action = new Ajax.Request(
+            "ajax_misc.php",
+            {
+                method: 'GET',
+                parameters: queryString,
+                onFailure: ajax_misc_failure,
+                onSuccess: function (req) {
+                    remove_waiting_box();
+                    if (req.responseText == 'NOCONX') {
+                        reconnect();
+                        return;
+                    }
+                    var style = 'position:absolute;';
+                    var y = calcy(200);
+                    style = style + ' ;top : ' + y + 'px';
+
+                    add_div({id: box, cssclass: 'inner_box', html: loading(), 
style: style})
+
+                    $(box).update(req.responseText);
+
+
+                }
+            }
+        );
+    } catch (e) {
+        alert_box(e.message);
+    }
+}
+/**
+ * add a widget for  the user , refresh the dashboard afterward
+ * @param widget_code {string}
+ */
+Widget.prototype.add=function (widget_code) {
+       try
+               {
+                here=this;
+                var param = {};
+                if ($(widget_code+"_param")) {
+                    console.debug(`found a FORM`)
+                    param=$(widget_code+"_param").serialize()
+                }
+                query = {
+                    op : 'widget',
+                    action : 'widget.insert',
+                    gDossier: this.dossier_id,
+                    param : param,
+                    widget_code:widget_code
+                }
+               var action = new Ajax.Request(
+                                         "ajax_misc.php" ,
+                                         {
+                                             method:'GET',
+                                             parameters:query,
+                                             onFailure:ajax_misc_failure,
+                                             onSuccess:function(req){
+                               if (req.responseText == 'NOCONX') {
+                                   reconnect();
+                                   return;
+                               }
+                                var new_element=new Element("li");
+                                $('contain_widget').appendChild(new_element);
+                                new_element.replace(req.responseText)
+                                removeDiv('widget_box_select_id')
+                                  here.create_sortable()
+                                here.refresh()
+
+                                             }
+                                         }
+                     );
+               }catch( e)
+               {
+                       alert_box(e.message);
+               }
+
+}
\ No newline at end of file
diff --git a/html/js/todo_list.js b/html/js/todo_list.js
index c22eb30ea..426da8736 100644
--- a/html/js/todo_list.js
+++ b/html/js/todo_list.js
@@ -185,11 +185,11 @@ function zoom_todo ()
     waiting_box();
     if ( ! todo_maximize)
     {
-        var clonetodo=$('todo_listg_div').clone();
+        var clonetodo=$('todo_list').clone();
         clonetodo.setAttribute('id','clone_todo_list')
         
clonetodo.setStyle({'z-index':1,'position':'absolute','width':'95%','height':'95%','top':'2%','right':'2%','left':'2%'})
-        clonetodo.innerHTML=$('todo_listg_div').innerHTML;
-        $('todo_listg_div').innerHTML="";
+        clonetodo.innerHTML=$('todo_list').innerHTML;
+        $('todo_list').innerHTML="";
         clonetodo.addClassName('inner_box');
         clonetodo.removeClassName('box');
         document.body.appendChild(clonetodo);
@@ -197,7 +197,7 @@ function zoom_todo ()
     } else
     {
         todo_maximize=false;
-         $('todo_listg_div').innerHTML=$('clone_todo_list').innerHTML;
+         $('todo_list').innerHTML=$('clone_todo_list').innerHTML;
         $('clone_todo_list').remove();
     }
     
diff --git a/include/ajax/ajax_preference.php b/include/ajax/ajax_preference.php
index f66c6cb99..719bf21f4 100644
--- a/include/ajax/ajax_preference.php
+++ b/include/ajax/ajax_preference.php
@@ -257,23 +257,6 @@ if ( $action == 'display_form' )
                 
         </fieldset>
        <?php
-       if ($inside_dossier)
-       {
-           /* Pref for welcome page */
-           echo '<fieldset style="margin: 1%">';
-           echo '<legend>' . _('Options pour la page d\'accueil') . 
'</legend>';
-           echo _('Mini-Rapport : ');
-           $rapport = new Acc_Report($cn);
-           $aRapport = $rapport->make_array();
-           $aRapport[] = array("value" => 0, "label" => _('Aucun mini 
rapport'));
-           $wRapport = new ISelect();
-           $wRapport->name = "minirap";
-           $wRapport->selected = $g_user->get_mini_report();
-           $wRapport->value = $aRapport;
-           echo $wRapport->input();
-           echo '<span class="notice">' . _('Le mini rapport est un rapport 
qui s\'affiche  sur votre page d\'accueil') . '</span>';
-           echo '</fieldset>';
-       }
 
        echo '<fieldset  style="margin: 1%">';
        echo '<legend>' . _('Langue') . '</legend>';
@@ -351,10 +334,8 @@ if ($action == 'save')
     }
     if ( $inside_dossier)
     {
-        $minirap=$http->post("minirap","number","0");
         $period=$http->post("period","number");
         $g_user->set_periode($period);
-        $g_user->set_mini_report($minirap);
     }
     $g_user->save_global_preference('THEME', $style_user);
     $g_user->save_global_preference('LANG', $lang);
diff --git a/include/class/acc_ledger.class.php 
b/include/class/acc_ledger.class.php
index 2f08d0063..488c81182 100644
--- a/include/class/acc_ledger.class.php
+++ b/include/class/acc_ledger.class.php
@@ -3158,9 +3158,11 @@ class Acc_Ledger  extends jrn_def_sql
             default:
                 throw new Exception('Ledger_type invalid : '.$p_ledger_type);
         }
+        if ( ! in_array($sql_op ,array('>','<','=','>=','<='))) {
+            throw new \Exception ("AC3162 : invalid \$sql_op = [$sql_op]");
+        }
 
-
-        $sql="select jr_id, jr_internal, jr_date, 
jr_comment,jr_pj_number,jr_montant
+        $sql="select jr_id, jr_internal, jr_date, 
jr_comment,jr_pj_number,jr_montant,jr_ech
                                from jrn
                                join jrn_def on (jrn_def_id=jr_def_id)
                                where
@@ -3168,6 +3170,7 @@ class Acc_Ledger  extends jrn_def_sql
                                and jr_ech $sql_op to_date($1,'DD.MM.YYYY')
                                and coalesce (jr_rapt,'xx') <> 'paid'
                                and $filter
+                order by jr_date
                                ";
         $array=$this->db->get_array($sql, array($p_date));
         return $array;
diff --git a/include/class/noalyss_user.class.php 
b/include/class/noalyss_user.class.php
index 1d45d33a6..250ec4733 100644
--- a/include/class/noalyss_user.class.php
+++ b/include/class/noalyss_user.class.php
@@ -752,37 +752,6 @@ class Noalyss_User
         return $array['PERIODE'];
     }
 
-    /**
-     * 
-     * \brief return the mini rapport to display on the welcome page
-     * \return 0 if nothing if found or the report to display 
(form_definition.fr_id)
-     */
-    function get_mini_report()
-    {
-        $array=$this->get_preference();
-        $fr_id=(isset($array['MINIREPORT']))?$array['MINIREPORT']:0;
-        return $fr_id;
-    }
-
-    /**
-     * \brief set the mini rapport to display on the welcome page
-     */
-    function set_mini_report($p_id)
-    {
-        $count=$this->db->get_value("select count(*) from user_local_pref 
where user_id=$1 and parameter_type=$2",
-                array($this->id, 'MINIREPORT'));
-        if ($count==1)
-        {
-            $sql="update user_local_pref set parameter_value=$1 where 
user_id=$2 and parameter_type='MINIREPORT'";
-            $Res=$this->db->exec_sql($sql, array($p_id, $this->id));
-        }
-        else
-        {
-            $sql="insert into user_local_pref 
(user_id,parameter_type,parameter_value)".
-                    "values($1,'MINIREPORT',$2)";
-            $Res=$this->db->exec_sql($sql, array($this->id, $p_id));
-        }
-    }
 
     /**
      * @brief Save the preference , the scope is global, the settings are saved
diff --git a/include/constant.php b/include/constant.php
index 3c08813d5..6ceb328f1 100644
--- a/include/constant.php
+++ b/include/constant.php
@@ -406,7 +406,8 @@ function noalyss_class_autoloader($class)
         "htmlinput" => "lib/html_input.class.php",
         'noalyss\dbg'=>"lib/dbg.php",
         'noalyss\file_cache'=>"lib/file_cache.class.php",
-        "pdfland"=>"class/pdf_land.class.php"
+        "pdfland"=>"class/pdf_land.class.php",
+        "noalyss\widget\widget"=>"widget/widget.php"
     );
     if (isset ($aClass[$class])) {
         require_once NOALYSS_INCLUDE . "/" . $aClass[$class];
diff --git a/include/dashboard.inc.php b/include/dashboard.inc.php
index b0f0e1e43..2a00531bc 100644
--- a/include/dashboard.inc.php
+++ b/include/dashboard.inc.php
@@ -4,21 +4,10 @@ if ( ! defined ('ALLOWED') ) die('Appel direct ne sont pas 
permis');
  *@file
  *@brief Dashboard
  */
-require_once  NOALYSS_INCLUDE.'/constant.php';
-require_once  NOALYSS_INCLUDE.'/lib/ac_common.php';
-require_once  NOALYSS_INCLUDE.'/lib/user_menu.php';
-
-echo '<div class="content">';
-global $g_user;
-/* others report */
-$cal=new Calendar();
-$cal->get_preference();
-
-$obj=sprintf("{gDossier:%d,invalue:'%s',outdiv:'%s','distype':'%s'}",
-        dossier::id(),'per','calendar_zoom_div','cal');
+require_once NOALYSS_TEMPLATE.'/dashboard.php';
 
 
-require_once NOALYSS_TEMPLATE.'/dashboard.php';
+echo \HtmlInput::button_action(_('Elements du tableau de bord'), 
"widget.manage()");
 
 
 ?>
diff --git a/include/template/calendar.php b/include/template/calendar.php
index fe2fa9bb2..76ca5ad9a 100644
--- a/include/template/calendar.php
+++ b/include/template/calendar.php
@@ -12,9 +12,9 @@
  ?>
     
 <?php if ($zoom == 1 ): ?>    
-<table style="width:100%;height:80%">
+<table style="width:100%;height:70%">
     <?php else: ?>
-<table style="width:100%;">
+<table style="width:100%;height:70%">
     <?php endif; ?>
 <tr>
 <?php
@@ -36,10 +36,11 @@ $week=$nFirstDay;
 $nCol=0;
 $today_month=date('m');
 $today_day=date('j');
+$height=($zoom == 1)?"15vh":"2rem";
 while ($ind <= $this->day) {
      if ($nCol==0)
     {
-        echo "<tr>";
+        echo "<tr style='height:{$height} '>";
     }
     $class="workday";
     if ($week==0||$week==6)
diff --git a/include/template/dashboard.php b/include/template/dashboard.php
index 558ccc9b3..9330d0645 100644
--- a/include/template/dashboard.php
+++ b/include/template/dashboard.php
@@ -1,197 +1,43 @@
-<?php
-//This file is part of NOALYSS and is under GPL 
-//see licence.txt
-?><!-- left div -->
-<div class="row" style="margin-right: 0px;margin-left:0px">
-<div id="calendar_box_div" class="box">
-<?php echo 
HtmlInput::title_box(_('Calendrier'),'cal_div','zoom',"calendar_zoom($obj)",'n');?>
-<?php echo $cal->display('short',0); ?>
-</div>
-
-<div id="todo_listg_div" class="box"> <?php echo 
HtmlInput::title_box(_('Pense-Bête'),"todo_listg_div",'zoom',"zoom_todo()",'n')?>
 
 <?php
 /*
- * Todo list
+ *   This file is part of NOALYSS.
+ *
+ *   PhpCompta is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   PhpCompta is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with PhpCompta; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
-echo dossier::hidden();
-$todo=new Todo_List($cn);
-$array=$todo->load_all();
-$a_todo=Todo_List::to_object($cn,$array);
-
-echo HtmlInput::button('add',_('Ajout'),'onClick="add_todo()"','smallbutton');
-  echo '<table id="table_todo" class="sortable" width="100%">';
-  echo '<tr><th class=" sorttable_sorted_reverse" 
id="todo_list_date">Date</th><th>Titre</th><th></th>';
-if ( ! empty ($array) )  {
-  $nb=0;
-  $today=date('d.m.Y');
-
-  foreach ($a_todo as $row) {
-    if ( $nb % 2 == 0 ) $odd='odd '; else $odd='even ';
-    $nb++;
-    echo $row->display_row($odd);
-  }
-}
-  echo '</table>';
-?>
-</div>
-
-<div id="situation_div" class="box"> 
-  <?=Status_Operation_Event::main_display($cn)?>
-</div>
-
-<!-- Mini rapport -->
-<?php
-/*
- * Mini Report
+// Copyright (2024) Author Dany De Bontridder <dany@alchimerys.be>
+/**
+ * @file
+ * @brief : display all  available widgets for the connected user
  */
-$report=$g_user->get_mini_report();
-
-$rapport=new Acc_Report($cn,$report);
+global $g_user,$cn;
+use \Noalyss\Widget\Widget;
 
-if ( $rapport->exist() == false ) {
-  $g_user->set_mini_report(0);
-  $report=0;
-}
+$aWidget=\Noalyss\Widget\Widget::get_enabled_widget();
 
-if ( $report != 0 ) : ?>
-<div id="report_div" class="box"><?php echo 
HtmlInput::title_box($rapport->get_name(),'report_div','none','','n');?>
-<?php    
-  $exercice=$g_user->get_exercice();
-  if ( $exercice == 0 ) {
-    alert(_('Aucune periode par defaut'));
-  } else {
-    $periode=new Periode($cn);
-    $limit=$periode->limit_year($exercice);
 
-    $result=$rapport->get_row($limit['start'],$limit['end'],'periode');
-    $ix=0;
-    if ( !empty ($result ) && count ($result) >  0)
-    {
-        echo '<table class="result">';
-        foreach ($result as $row) {
-          $ix++;
-              $class=($ix%2==0)?' class="even" ':' class="odd" ';
-          echo '<tr '.$class.'>';
-
-          echo '<td> '.$row['desc'].'</td>';
-            $style='style="text-align:right;"';
-            if ($row['montant']<0) { 
$style='style="color:red;text-align:right;"';}
-            echo  "<td $style>".nbm($row['montant'])."</td>";
-          echo '</tr>';
-        }
-        echo '</table>';
-    } else {
-        echo _('Aucun résultat');
-    }
-  }
-  ?>
-  </div>
-<?php
-  else :
-?>
-  <div id="report_div" class="box"> <?php echo HtmlInput::title_box(_('Aucun 
rapport défini'),'report_div','none','','n')?>
-<p>
-  <a href="javascript:void(0)" class="cell" onclick="set_preference('<?php 
echo dossier::id()?>')"><?php echo _('Cliquez ici pour mettre à jour vos 
préférences')?></a>
-<p>
-</div>
-<?php
-endif;
+$nb=count($aWidget);
 ?>
+<div id="dashboard_div_id">
 
-
-
-
-
-<div id="last_operation_box_div" class="box">
-<?php echo HtmlInput::title_box(_('Dernières 
opérations'),"last_operation_box_div",'zoom','popup_recherche('.dossier::id().')','n')?>
-
-<table class="result" >
 <?php
-$Ledger=new Acc_Ledger($cn,0);
-$last_ledger=array();
-$last_ledger=$Ledger->get_last(20);
-
-for($i=0;$i<count($last_ledger);$i++):
-       $class=($i%2==0)?' class="even" ':' class="odd" ';
-?>
-<tr <?php echo $class ?>>
-       <td class="box">
-            <?php echo   smaller_date($last_ledger[$i]['jr_date_fmt'])?>
-       </td>
-       <td class="box">
-               <?php echo $last_ledger[$i]['jr_pj_number']?>
-            
-        </td>
-<td class="box">
-   <?php echo h(mb_substr($last_ledger[$i]['jr_comment']??"",0,40,'UTF-8'))?>
-</td>
-<td class="box">
-<?php echo HtmlInput::detail_op($last_ledger[$i]['jr_id'], 
$last_ledger[$i]['jr_internal'])?>
-</td>
-<td class="num box">
-<?php echo nbm($last_ledger[$i]['jr_montant'])?>
-</td>
+for ($i=0;$i < $nb ;$i++):
+    
$widget=Widget::build_user_widget($aWidget[$i]['uw_id'],$aWidget[$i]['wd_code']);
+    Widget::ajax_display($widget);
 
-</tr>
-<?php endfor;?>
-</table>
-    
-</div>
-<div id="last_operation_management_div" class="box">
-    <?php 
-     echo 
HtmlInput::title_box(_('Suivi'),"last_operation_management_div",'zoom','action_show('.dossier::id().')','n');
+endfor;
     ?>
-    <?php
-    $gestion=new Follow_Up($cn);
-    $array=$gestion->get_last(MAX_ACTION_SHOW);
-    $len_array=count($array);
-    ?>
-    <table class="result" >
-    <?php
-    for ($i=0;$i < $len_array;$i++) :
-    ?>
-        <tr class=" <?php echo ($i%2==0)?'even':'odd'?>">
-            <td class="box">
-                <?php echo smaller_date($array[$i]['ag_timestamp_fmt']) ;?>
-            </td>
-            <td class="box">
-                <?php echo HtmlInput::detail_action($array[$i]['ag_id'], 
$array[$i]['ag_ref'], 1)  ?>
-            </td>
-            <td class="box">
-                <?php echo mb_substr(h($array[$i]['quick_code']),0,15)?>
-            </td>
-            <td class="box cut">
-                <?php echo h($array[$i]['ag_title'])?>
-            </td>
-        </tr>
-    <?php
-    endfor;
-    ?>
-    </table>
-</div>
-</div><!-- class="row" -->
-<div id="add_todo_list" class="box" style="display:none">
-       
-<form method="post">
-<?php
-$wDate=new IDate('p_date_todo');
-$wDate->id='p_date_todo';
-$wTitle=new IText('p_title');
-$wDesc=new ITextArea('p_desc');
-$wDesc->heigh=5;
-$wDesc->width=40;
-echo HtmlInput::title_box(_("Note"),"add_todo_list","hide",'',"n");
-echo _("Date")." ".$wDate->input().'<br>';
-echo _("Titre")." ".$wTitle->input().'<br>';
-echo _("Description")."<br>".$wDesc->input().'<br>';
-echo dossier::hidden();
-echo HtmlInput::hidden('tl_id',0);
-echo 
HtmlInput::submit('save_todo_list',_('Sauve'),'onClick="Effect.Fold(\'add_todo_list\');return
 true;"');
-echo 
HtmlInput::button('hide',_('Annuler'),'onClick="Effect.Fold(\'add_todo_list\');return
 true;"');
-?>
-</form>
 </div>
-</div>
-
 
diff --git a/include/template/status_operation_event-main_display.php 
b/include/template/status_operation_event-main_display.php
index f2e504383..c1018eb82 100644
--- a/include/template/status_operation_event-main_display.php
+++ b/include/template/status_operation_event-main_display.php
@@ -38,7 +38,7 @@ $customer_late=$Ledger->get_customer_late();
 
 $gDossier_id=Dossier::id();
 ?>
-  <?php echo 
HtmlInput::title_box(_("Situation"),"situation_div",'none','','n')?>
+  <?php echo HtmlInput::title_box(_("Résumé"),"situation_div",'none','','n')?>
     <table class='result'>
                <tr>
                        <th>
diff --git a/include/widget/agenda/agenda.php b/include/widget/agenda/agenda.php
new file mode 100644
index 000000000..530811993
--- /dev/null
+++ b/include/widget/agenda/agenda.php
@@ -0,0 +1,55 @@
+<?php
+/*
+ *   This file is part of NOALYSS.
+ *
+ *   PhpCompta is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   PhpCompta is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with PhpCompta; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+// Copyright (2024) Author Dany De Bontridder <dany@alchimerys.be>
+/**
+ * @file
+ * @brief : widget agenda,
+ * @note this widget is  included in Noalyss Core and a part of the code 
(javascript + css + ajax)
+ *   are still included in NOALYSS Code
+ */
+namespace Noalyss\Widget;
+
+/*!
+ * \class Agenda
+ * \brief : widget agenda,
+ * \note this widget is  included in Noalyss Core and a part of the code 
(javascript + css + ajax)
+ *  are still included in NOALYSS Code, this code should move here and will be 
part of a "cleansing code" process
+*/
+class Agenda extends Widget
+{
+
+    function display()
+    {
+        global $g_user;
+        /* others report */
+        $cal=new \Calendar();
+        $cal->get_preference();
+
+        $obj=sprintf("{gDossier:%d,invalue:'%s',outdiv:'%s','distype':'%s'}",
+            \Dossier::id(),'per','calendar_zoom_div','cal');
+
+       $this->open_div();
+        echo 
\HtmlInput::title_box(_('Calendrier'),'cal_div','zoom',"calendar_zoom($obj)",'n');
+        echo $cal->display('short',0);
+        $this->close_div();
+    }
+
+
+
+}
\ No newline at end of file
diff --git a/include/widget/ajax.php b/include/widget/ajax.php
new file mode 100644
index 000000000..b3dd83897
--- /dev/null
+++ b/include/widget/ajax.php
@@ -0,0 +1,140 @@
+<?php
+/*
+ *   This file is part of NOALYSS.
+ *
+ *   NOALYSS is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   NOALYSS is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with NOALYSS; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+// Copyright Author Dany De Bontridder danydb@aevalys.eu 12/08/24
+/*! 
+ * \file
+ * \brief manage Ajax call for widget , called from html/ajax_misc.php.
+ * \par Variables are :
+ *   *  action = action to perform
+ *   *  w is the widget code or if empty , the Widget class will be used
+ *
+ * \par for Widget Class, possible actions are
+ *
+ *    * widget.display : display the widget param : user_widget_id, 
user_widget_code
+ *    * widget.refresh
+ *    * widget.manage
+ *    * widget.save
+ *    * widget.insert
+ *
+ */
+global $cn;
+$http=new HttpInput();
+
+require_once 'widget.php';
+
+try {
+    $action=$http->request("action");
+    $w=$http->request("w","string","widget");
+} catch (\Exception $e) {
+    echo $e->getMessage();
+}
+
+// action = display,
+if ($action == 'widget.display') {
+    try {
+        
$widget=\Noalyss\Widget\Widget::build_user_widget($http->request('user_widget_id'),$http->request("widget_code"));
+        $widget?->display();
+
+    } catch (\Exception $e) {
+        echo $e->getMessage();
+    }
+
+    return;
+}
+// call from a widget
+if ( $w != "widget") {
+    // security
+    // widget exists ? Protect against attack when w is a relative path to 
something else
+    $count=$cn->get_value("select count(*) from widget_dashboard where 
wd_code=$1",[$w]);
+    if ($count == 1 && file_exists(NOALYSS_INCLUDE."/widget/$w/ajax.php")) {
+        require NOALYSS_INCLUDE."/widget/$w/ajax.php";
+        return;
+    }
+}
+// action == manage , display a dialog box to add , remove or change parameter 
of widget
+// if must possible to add several time the same widget , example mini-report
+if ( $action == 'widget.manage') {
+    echo \HtmlInput::title_box(_("Elements"), 'widget_box_id');
+    echo '<div style="padding:0.3rem">';
+    echo span(_('Organiser les éléments en utilisant la souris (Drag & Drop) 
puis sauver'),'class="text-muted text-center"');
+    \Noalyss\Widget\Widget::display_available();
+
+
+    echo '<ul class="aligned-block">';
+    echo '<li>'.\HtmlInput::button_close('widget_box_id','button').'</li>';
+    echo '<li>'.\HtmlInput::button_action(_('Sauver'),'widget.save()').'</li>';
+    echo 
'<li>'.\HtmlInput::button_action(_('Ajouter'),'widget.input()').'</li>';
+
+    echo '<ul>';
+
+    echo create_script("widget.create_sortable()");
+    echo '</div>';
+    return;
+}
+/**************************************************************************
+ *
+// save the order of the widget
+ *************************************************************************/
+if ( $action == 'widget.save') {
+    $query=$http->request("param","string","");
+    try {
+        parse_str($query, $aWigdet);
+        \Noalyss\Widget\Widget::save($aWigdet['contain_widget']);
+
+    } catch (\Exception $e) {
+        echo "NOK";
+        echo $e->getMessage();
+    }
+    return;
+}
+/**************************************************************************
+ * refresh dashboard
+ *
+ *************************************************************************/
+if ($action == 'widget.refresh') {
+    require NOALYSS_TEMPLATE."/dashboard.php";
+    return;
+}
+if ($action == "widget.input") {
+    echo \HtmlInput::title_box(_("Elements à ajouter"), 
'widget_box_select_id');
+    echo '<div style="padding:0.3rem">';
+    \Noalyss\Widget\Widget::select_available();
+    echo '</div>';
+    echo '<ul class="aligned-block">';
+    echo 
'<li>'.\HtmlInput::button_close('widget_box_select_id','button').'</li>';
+    echo '<ul>';
+    echo '</div>';
+    return;
+}
+/*************************************************************************
+ * insert a new widget with param if any
+ ************************************************************************/
+if ($action == 'widget.insert') {
+    // insert new widget in user_widget + list "contain_widget"
+    $param = $http->request("param","string",null);
+    $widget_code = $http->request("widget_code");
+    $widget_id=$cn->get_value("select wd_id from widget_dashboard where 
wd_code=$1 ",[$widget_code]);
+    if (empty($widget_id)) return;
+
+    $user_widget_id = $cn->get_value("insert into 
user_widget(use_login,dashboard_widget_id,uw_parameter,uw_order)
+values ($1,$2,$3,1000) returning 
uw_id",[$g_user->getLogin(),$widget_id,$param]);
+        $widget=\Noalyss\Widget\Widget::build_user_widget($user_widget_id, 
$widget_code);
+        $widget->input();
+    return;
+}
\ No newline at end of file
diff --git a/include/widget/bookmark/bookmark.php 
b/include/widget/bookmark/bookmark.php
new file mode 100644
index 000000000..da9175f47
--- /dev/null
+++ b/include/widget/bookmark/bookmark.php
@@ -0,0 +1,72 @@
+<?php
+/*
+ *   This file is part of NOALYSS.
+ *
+ *   NOALYSS is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   NOALYSS is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with NOALYSS; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+// Copyright Author Dany De Bontridder danydb@aevalys.eu 18/08/24
+/*! 
+ * \file
+ * \brief bookmark widget
+ */
+namespace Noalyss\Widget;
+
+class Bookmark extends Widget
+{
+    function display()
+    {
+        global $g_user;
+        $this->open_div();
+        echo '<h2 class="title">',_("Favoris"),'&#x2728;','</h2>';
+        $bookmark_sql="select distinct 
b_id,b_action,b_order,me_code,me_description, javascript"
+            . " from bookmark "
+            . "join v_menu_description_favori on (code=b_action or 
b_action=me_code)"
+            . "where "
+            . "login=$1 order by me_code";
+        $a_bookmark=$this->db->get_array($bookmark_sql,array($g_user->login));
+
+        $dossier_id=\Dossier::id();
+        if (count($a_bookmark) >0 ) {
+            $p=0;
+            foreach ($a_bookmark as $item) {
+                $a_code=  explode('/',$item['b_action']);
+                $idx=count($a_code);
+                $code=$a_code[$idx-1];
+                
$url=http_build_query(array("ac"=>$item['b_action'],"gDossier"=>$dossier_id));
+                $p++;
+                $class=($p&1)?' odd ':'even';
+                $description=h($item['me_description']);
+                echo <<<EOF
+<div class="row {$class}">
+    <div class="col-3">
+        
+        
+        <a class="mtitle" href="do.php?{$url}">{$code}  </a> 
+    </div>
+    <div class="col-7">
+        
+        
+        <a class="mtitle" href="do.php?{$url}">{$description}</a> 
+    </div>
+</div>
+EOF;
+
+            }
+        }
+        $this->close_div();
+
+    }
+
+}
\ No newline at end of file
diff --git a/include/widget/bookmark/install.php 
b/include/widget/bookmark/install.php
new file mode 100644
index 000000000..463eb4cc5
--- /dev/null
+++ b/include/widget/bookmark/install.php
@@ -0,0 +1,33 @@
+<?php
+/*
+ *   This file is part of NOALYSS.
+ *
+ *   NOALYSS is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   NOALYSS is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with NOALYSS; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+// Copyright Author Dany De Bontridder danydb@aevalys.eu 18/08/24
+/*! 
+ * \file
+ * \brief install the widget event 
+ */
+global $cn;
+
+$cn->exec_sql("insert into widget_dashboard 
(wd_code,wd_description,wd_parameter,wd_name) values ($1,$2,$3,$4)",
+    array (
+        'bookmark'
+        ,'Raccourcis vers vos menus préférés'
+        ,0
+        ,'Raccourcis'
+        )
+);
diff --git a/include/widget/coming_event/coming_event.php 
b/include/widget/coming_event/coming_event.php
new file mode 100644
index 000000000..312f4937d
--- /dev/null
+++ b/include/widget/coming_event/coming_event.php
@@ -0,0 +1,44 @@
+<?php
+/*
+ *   This file is part of NOALYSS.
+ *
+ *   NOALYSS is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   NOALYSS is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with NOALYSS; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+// Copyright Author Dany De Bontridder danydb@aevalys.eu 10/08/24
+/*! 
+ * \file
+ * \brief show the event for today , or late
+ * \note this widget is  included in Noalyss Core and a part of the code 
(javascript + css + ajax)
+ *  are still included in NOALYSS Code, this code should move here and will be 
part of a "cleansing code" process
+ */
+namespace Noalyss\Widget;
+
+/*!
+ *\class Coming_Event
+ * \brief show the event for today , or late
+ * \note this widget is  included in Noalyss Core and a part of the code 
(javascript + css + ajax)
+ *  are still included in NOALYSS Code, this code should move here and will be 
part of a "cleansing code" process
+ */
+class Coming_Event extends  Widget
+{
+    function display()
+    {
+        global $cn;
+        $this->open_div();
+        \Status_Operation_Event::main_display($cn);
+        $this->close_div();
+    }
+
+}
diff --git a/include/widget/event/event-display.php 
b/include/widget/event/event-display.php
new file mode 100644
index 000000000..27c7130da
--- /dev/null
+++ b/include/widget/event/event-display.php
@@ -0,0 +1,54 @@
+<?php
+/*
+ *   This file is part of NOALYSS.
+ *
+ *   NOALYSS is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   NOALYSS is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with NOALYSS; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+// Copyright Author Dany De Bontridder danydb@aevalys.eu 18/08/24
+/*! 
+ * \file
+ * \brief display events for 10 days
+ */
+echo h2('Actions','class="title"');
+if ( empty ($array)) {
+    echo _("Aucun événement en retard ou prévu");
+    return;
+}
+$idx=0;
+foreach ($array as $item):
+    $idx++;
+$class=($idx&1)?'odd':'even';
+$style="";
+$date=date('ymd');
+if ($item['remind_date'] < $date) {
+    $style="background-color:brown;color:white;";
+} elseif ($item['remind_date'] == $date ) {
+    $style="background-color:orange;color:white;";
+}
+?>
+<div class="row <?=$class?> border" style="<?=$style?>">
+    <div class="col-2">
+        <?=$item['ag_timestamp_fmt']?>
+    </div>
+    <div class="col" 
style="text-overflow:hidden;text-wrap:nowrap;text-overflow:ellipsis ">
+        <?=HtmlInput::detail_action($item['ag_id'],
+        $item['ag_ref']." ".$item['ag_title'])?>
+    </div>
+</div>
+<?php
+endforeach;
+?>
+
+
diff --git a/include/widget/event/event.php b/include/widget/event/event.php
new file mode 100644
index 000000000..63c43d410
--- /dev/null
+++ b/include/widget/event/event.php
@@ -0,0 +1,71 @@
+<?php
+/*
+ *   This file is part of NOALYSS.
+ *
+ *   NOALYSS is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   NOALYSS is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with NOALYSS; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+// Copyright Author Dany De Bontridder danydb@aevalys.eu 18/08/24
+/*! 
+ * \file
+ * \brief show 10 next events , or 10 late
+ */
+
+namespace Noalyss\Widget;
+
+/*!
+ * \class
+ * \brief show 10 next events , or 10 late
+*/
+
+class Event extends Widget
+{
+    /**
+     * @brief get the action where the remind day is within 10 days
+     * @return array
+     */
+    function get_next10()
+    {
+        $sql="select ag_ref
+        ,ag_hour
+        ,coalesce(vw_name,'Interne') as vw_name
+        ,ag_id
+        ,ag_title
+        ,ag_ref
+        , dt_value
+        ,to_char(ag_remind_date,'DD.MM.YY') as ag_timestamp_fmt
+        ,ag_timestamp 
+        ,to_char(ag_remind_date,'YYMMDD') as remind_date 
+        from action_gestion join document_type 
+             on (ag_type=dt_id) 
+                  left join vw_fiche_attr on (f_id=f_id_dest) 
+                  where 
+                  ag_state not in (1,4)
+                  and 
to_char(ag_remind_date,'DDMMYYYY')<=to_char(now()+interval '7 days','DDMMYYYY')
+                  and ". \Follow_Up::sql_security_filter($this->db,'R')
+        ." order by ag_remind_date asc";
+        $array=$this->db->get_array($sql);
+        return $array;
+    }
+
+    function display()
+    {
+
+       $this->open_div();
+       $array=$this->get_next10();
+       require "event-display.php";
+
+       $this->close_div();
+    }
+}
\ No newline at end of file
diff --git a/include/widget/event/install.php b/include/widget/event/install.php
new file mode 100644
index 000000000..9120cf054
--- /dev/null
+++ b/include/widget/event/install.php
@@ -0,0 +1,33 @@
+<?php
+/*
+ *   This file is part of NOALYSS.
+ *
+ *   NOALYSS is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   NOALYSS is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with NOALYSS; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+// Copyright Author Dany De Bontridder danydb@aevalys.eu 18/08/24
+/*! 
+ * \file
+ * \brief install the widget event 
+ */
+global $cn;
+
+$cn->exec_sql("insert into widget_dashboard 
(wd_code,wd_description,wd_parameter,wd_name) values ($1,$2,$3,$4)",
+    array (
+        'event'
+        ,'Affiche les actions à venir et celles en retards'
+        ,0
+        ,'A faire'
+        )
+);
diff --git a/include/widget/invoice/install.php 
b/include/widget/invoice/install.php
new file mode 100644
index 000000000..308e0a3b6
--- /dev/null
+++ b/include/widget/invoice/install.php
@@ -0,0 +1,33 @@
+<?php
+/*
+ *   This file is part of NOALYSS.
+ *
+ *   NOALYSS is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   NOALYSS is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with NOALYSS; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+// Copyright Author Dany De Bontridder danydb@aevalys.eu 18/08/24
+/*! 
+ * \file
+ * \brief install the widget event 
+ */
+global $cn;
+
+$cn->exec_sql("insert into widget_dashboard 
(wd_code,wd_description,wd_parameter,wd_name) values ($1,$2,$3,$4)",
+    array (
+        'invoice'
+        ,'Affiche les prochaines factures fournisseurs ou clients ou en retard'
+        ,1
+        ,'Factures'
+        )
+);
diff --git a/include/widget/invoice/invoice-display.php 
b/include/widget/invoice/invoice-display.php
new file mode 100644
index 000000000..6870ef6a2
--- /dev/null
+++ b/include/widget/invoice/invoice-display.php
@@ -0,0 +1,64 @@
+<?php
+/*
+ *   This file is part of NOALYSS.
+ *
+ *   NOALYSS is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   NOALYSS is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with NOALYSS; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+// Copyright Author Dany De Bontridder danydb@aevalys.eu 18/08/24
+/*! 
+ * \file
+ * \brief display invoice for supplier or customer to pay, late or for today
+ */
+if ( empty($array)) {
+    echo _("Aucune facture");
+    return;
+}
+$p=0;
+foreach ($array as $item):
+    /**
+     * item : jr_id, jr_internal,jr_date , jr_comment, 
jr_pj_number,jr_montant,jr_ech
+     */
+    $ech=\DateTime::createFromFormat("Y-m-d", $item['jr_ech']);
+
+    $today=new \DateTime();
+    if ( $aParam['time_limit'] == 'P' && 
$today->format('ymd')>$ech->format('ymd') )
+        continue;
+    $p++;
+    $class=($p&1)?' odd ':'even';
+?>
+<div class="row <?=$class?>">
+    <div class="col-2">
+        <?=$ech->format('d.m.y')?>
+    </div>
+    <div class="col-2">
+        <?=\HtmlInput::detail_op($item['jr_id'],$item['jr_pj_number'])?>
+    </div>
+    <div class="col">
+        <?=h($item['jr_comment'])?>
+        <?=h($item['jr_date'])?>
+    </div>
+    <div class="col-2">
+        <?=nbm($item['jr_montant'],2)?>
+    </div>
+</div>
+
+<?php
+endforeach;
+if ($p == 0) {
+    echo _("Aucune facture");
+    return;
+}
+?>
+
diff --git a/include/widget/invoice/invoice.php 
b/include/widget/invoice/invoice.php
new file mode 100644
index 000000000..0289821f3
--- /dev/null
+++ b/include/widget/invoice/invoice.php
@@ -0,0 +1,82 @@
+<?php
+/*
+ *   This file is part of NOALYSS.
+ *
+ *   NOALYSS is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   NOALYSS is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with NOALYSS; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+// Copyright Author Dany De Bontridder danydb@aevalys.eu 18/08/24
+/*! 
+ * \file
+ * \brief display the next invoice to  to be paid or late for customer or 
supplier
+ */
+namespace Noalyss\Widget;
+
+class Invoice extends Widget
+{
+    function input_parameter()
+    {
+        $tiers = new \ISelect('tiers');
+        $tiers->value[] = array('value' => 'S', 'label' => _("Fournisseurs"));
+        $tiers->value[] = array('value' => 'C', 'label' => _("Clients"));
+        $time_limit = new \ISelect('time_limit');
+        $time_limit->value[] = array('value' => 'P', 'label' => _("Prochaines 
factures"));
+        $time_limit->value[] = array('value' => 'R', 'label' => _("Factures en 
retard"));
+        $time_limit->value[] = array('value' => 'T', 'label' => _("Factures 
pour aujourd'hui"));
+
+        $input = _("Factures ") . $tiers->input() . " " . _("échéance") . " " 
. $time_limit->input();
+        $this->make_form($input);
+
+    }
+
+    function display_parameter()
+    {
+        $aParam = $this->get_parameter();
+        $aTiers = ['S' => _("Fournisseurs"), "C" => _("Clients")];
+        $aLimit = ['P' => _("Prochaines"), "R" => 
"Retard",'T'=>_("Aujourd'hui")];
+        echo '<span class="widget_param">'.$aTiers[$aParam['tiers']] . " " . 
$aLimit[$aParam["time_limit"]].'</span>';
+    }
+
+    function display()
+    {
+        $this->open_div();
+        $aParam = $this->get_parameter();
+        $aTiers = ['S' => _("Fournisseurs"), "C" => _("Clients")];
+        $aLimit = ['P' => _("Prochaines factures"), "R" => "facture en 
retard",'T'=>_("Aujourd'hui")];
+        $title = $aTiers[$aParam['tiers']] . " " . 
$aLimit[$aParam["time_limit"]];
+        echo h2($title, 'class="title"');
+        $acc_ledger = new \Acc_Ledger($this->db, 0);
+
+        $ledger_type = 'ACH';
+        if ($aParam['tiers'] == 'C') {
+            $ledger_type = 'VEN';
+        }
+
+        switch ($aParam['time_limit']) {
+            case 'P':
+                $array = $acc_ledger->get_operation_date(date('d.m.Y'), 
$ledger_type, '>');
+                break;
+            case 'R':
+                $array = $acc_ledger->get_operation_date(date('d.m.Y'), 
$ledger_type, '<');
+                break;
+            case 'T':
+                $array = $acc_ledger->get_operation_date(date('d.m.Y'), 
$ledger_type, '=');
+                break;
+        }
+        include "invoice-display.php";
+        $this->close_div();
+
+
+    }
+}
\ No newline at end of file
diff --git a/include/widget/last_event/last_event-display.php 
b/include/widget/last_event/last_event-display.php
new file mode 100644
index 000000000..f2b3279db
--- /dev/null
+++ b/include/widget/last_event/last_event-display.php
@@ -0,0 +1,46 @@
+<?php
+/*
+ *   This file is part of NOALYSS.
+ *
+ *   NOALYSS is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   NOALYSS is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with NOALYSS; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+// Copyright Author Dany De Bontridder danydb@aevalys.eu 11/08/24
+/*! 
+ * \file
+ * \brief display last follow-up
+ */
+ ?>
+ <table class="result" >
+    <?php
+    for ($i=0;$i < $len_array;$i++) :
+    ?>
+        <tr class=" <?php echo ($i%2==0)?'even':'odd'?>">
+            <td class="box">
+                <?php echo smaller_date($array[$i]['ag_timestamp_fmt']) ;?>
+            </td>
+            <td class="box">
+                <?php echo HtmlInput::detail_action($array[$i]['ag_id'], 
$array[$i]['ag_ref'], 1)  ?>
+            </td>
+            <td class="box">
+                <?php echo mb_substr(h($array[$i]['quick_code']),0,15)?>
+            </td>
+            <td class="box cut">
+                <?php echo h($array[$i]['ag_title'])?>
+            </td>
+        </tr>
+    <?php
+    endfor;
+    ?>
+    </table>
\ No newline at end of file
diff --git a/include/widget/last_event/last_event.php 
b/include/widget/last_event/last_event.php
new file mode 100644
index 000000000..02af98855
--- /dev/null
+++ b/include/widget/last_event/last_event.php
@@ -0,0 +1,51 @@
+<?php
+/*
+ *   This file is part of NOALYSS.
+ *
+ *   NOALYSS is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   NOALYSS is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with NOALYSS; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+// Copyright Author Dany De Bontridder danydb@aevalys.eu 10/08/24
+/*! 
+ * \file
+ * \brief display last follow-up
+ */
+namespace Noalyss\Widget;
+
+use HtmlInput;
+use Dossier;
+/**
+ * @class Last_Event
+ * @brief display last_followup
+ */
+class Last_Event extends Widget
+{
+    /**
+     * @brief display 10 last action
+     * @return void
+     * @throws \Exception
+     */
+    function display()
+    {
+        global $cn;
+        $this->open_div();
+        echo 
HtmlInput::title_box(_('Suivi'),"last_operation_management_div",'zoom','action_show('.dossier::id().')','n');
+        $gestion=new \Follow_Up($cn);
+        $array=$gestion->get_last(MAX_ACTION_SHOW);
+        $len_array=count($array);
+        include "last_event-display.php";
+        $this->close_div();
+    }
+
+}
\ No newline at end of file
diff --git a/include/widget/last_operation/last_operation-display.php 
b/include/widget/last_operation/last_operation-display.php
new file mode 100644
index 000000000..e0805929f
--- /dev/null
+++ b/include/widget/last_operation/last_operation-display.php
@@ -0,0 +1,54 @@
+<?php
+/*
+ *   This file is part of NOALYSS.
+ *
+ *   NOALYSS is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   NOALYSS is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with NOALYSS; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+// Copyright Author Dany De Bontridder danydb@aevalys.eu 11/08/24
+/*! 
+ * \file
+ * \brief 
+ */
+?>
+<table class="result">
+    <?php
+    $Ledger = new \Acc_Ledger($cn, 0);
+    $last_ledger = array();
+    $last_ledger = $Ledger->get_last(20);
+
+    for ($i = 0; $i < count($last_ledger); $i++):
+        $class = ($i % 2 == 0) ? ' class="even" ' : ' class="odd" ';
+        ?>
+        <tr <?php echo $class ?>>
+            <td class="box">
+                <?php echo \smaller_date($last_ledger[$i]['jr_date_fmt']) ?>
+            </td>
+            <td class="box">
+                <?php echo $last_ledger[$i]['jr_pj_number'] ?>
+
+            </td>
+            <td class="box">
+                <?php echo h(mb_substr($last_ledger[$i]['jr_comment'] ?? "", 
0, 40, 'UTF-8')) ?>
+            </td>
+            <td class="box">
+                <?php echo \HtmlInput::detail_op($last_ledger[$i]['jr_id'], 
$last_ledger[$i]['jr_internal']) ?>
+            </td>
+            <td class="num box">
+                <?php echo nbm($last_ledger[$i]['jr_montant']) ?>
+            </td>
+
+        </tr>
+    <?php endfor; ?>
+</table>
\ No newline at end of file
diff --git a/include/widget/last_operation/last_operation.php 
b/include/widget/last_operation/last_operation.php
new file mode 100644
index 000000000..7fdb4f994
--- /dev/null
+++ b/include/widget/last_operation/last_operation.php
@@ -0,0 +1,49 @@
+<?php
+/*
+ *   This file is part of NOALYSS.
+ *
+ *   NOALYSS is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   NOALYSS is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with NOALYSS; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+// Copyright Author Dany De Bontridder danydb@aevalys.eu 10/08/24
+/*! 
+ * \file
+ * \brief display last operation
+ */
+namespace Noalyss\Widget;
+/**
+ * @class Last_Operation
+ * @brief display last accountancy operation
+ * @note this widget is  included in Noalyss Core and a part of the code 
(javascript + css + ajax)
+ * are still included in NOALYSS Code, this code should move here and will be 
part of a "cleansing code" process
+ *
+ */
+class Last_Operation extends Widget
+{
+    /**
+     * @brief display last accounting
+     * @return void
+     * @throws \Exception
+     */
+    public function display()
+    {
+        global $cn;
+        $this->open_div();
+
+        echo \HtmlInput::title_box(_('Dernières 
opérations'),"last_operation_box_div",'zoom','popup_recherche('.\Dossier::id().')','n');
+        require_once "last_operation-display.php";
+        $this->close_div();
+    }
+
+}
\ No newline at end of file
diff --git a/include/widget/mini_report/mini_report.php 
b/include/widget/mini_report/mini_report.php
new file mode 100644
index 000000000..d703ccfe9
--- /dev/null
+++ b/include/widget/mini_report/mini_report.php
@@ -0,0 +1,120 @@
+<?php
+
+/*
+ *   This file is part of NOALYSS.
+ *
+ *   NOALYSS is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   NOALYSS is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with NOALYSS; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+// Copyright Author Dany De Bontridder danydb@aevalys.eu 10/08/24
+/*!
+ * \file
+ * \brief manage mini report
+ * \note this widget is  included in Noalyss Core and a part of the code 
(javascript + css + ajax)
+ *   are still included in NOALYSS Code, this code should move here and will 
be part of a "cleansing code" process
+ */
+namespace Noalyss\Widget;
+
+use HtmlInput;
+use Periode;
+/*!
+ * \class
+ * \brief display simple report in a widget on the DASHBOARD
+ * \note this widget is  included in Noalyss Core and a part of the code 
(javascript + css + ajax)
+ *  are still included in NOALYSS Code, this code should move here and will be 
part of a "cleansing code" process
+ */
+class Mini_Report extends Widget
+{
+    /**
+     * @brief  show the simple report from USER_WIDGET.WD_PARAMETER
+     * @return void
+     * @throws \Exception
+     */
+    function display()
+    {
+        global $g_user, $cn;
+
+        $param = $this->db->get_value("select uw_parameter from user_widget 
where uw_id=$1",[$this->user_widget_id]);
+        parse_str($param, $aReport);
+        $report=$aReport['simple_report'];
+        $rapport = new \Acc_Report($cn, $report);
+
+        if ($rapport->exist() == false) {
+            $report = 0;
+        }
+        $this->open_div();
+        if ($report != 0) {
+            ?>
+           <?php echo HtmlInput::title_box($rapport->get_name(), 'report_div', 
'none', '', 'n'); ?>
+            <?php
+            $exercice = $g_user->get_exercice();
+            if ($exercice == 0) {
+                alert(_('Aucune periode par defaut'));
+            } else {
+                $periode = new Periode($cn);
+                $limit = $periode->limit_year($exercice);
+
+                $result = $rapport->get_row($limit['start'], $limit['end'], 
'periode');
+                $ix = 0;
+                if (!empty ($result) && count($result) > 0) {
+                    echo '<table class="result">';
+                    foreach ($result as $row) {
+                        $ix++;
+                        $class = ($ix % 2 == 0) ? ' class="even" ' : ' 
class="odd" ';
+                        echo '<tr ' . $class . '>';
+
+                        echo '<td> ' . $row['desc'] . '</td>';
+                        $style = 'style="text-align:right;"';
+                        if ($row['montant'] < 0) {
+                            $style = 'style="color:red;text-align:right;"';
+                        }
+                        echo "<td $style>" . nbm($row['montant']) . "</td>";
+                        echo '</tr>';
+                    }
+                    echo '</table>';
+                } else {
+                    echo _('Aucun résultat');
+                }
+            }
+        }
+        $this->close_div();
+    }
+
+    /**
+     * @brief select the simple report (FORM_DEFINITION) to display
+     * @see Widget::make_form()
+     * @return void
+     */
+    function input_parameter() {
+
+        $select=new \ISelect('simple_report');
+        $select->value=$this->db->make_array("select fr_id, fr_label from 
form_definition order by 2");
+
+        $this->make_form($select->input());
+    }
+
+    /**
+     * @brief Display the parameter of the form
+     * @return void
+     */
+    function display_parameter() {
+        $aParam= $this->get_parameter();
+        $name = $this->db->get_value("select fr_label from form_definition 
where fr_id=$1",[$aParam['simple_report']]);
+        echo " ";
+        echo span(_("Rapport") ." ".h($name),'class="widget_param"');
+
+
+    }
+
+}
\ No newline at end of file
diff --git a/include/widget/todo_list/todo_list-display_new_note.php 
b/include/widget/todo_list/todo_list-display_new_note.php
new file mode 100644
index 000000000..53cf5d337
--- /dev/null
+++ b/include/widget/todo_list/todo_list-display_new_note.php
@@ -0,0 +1,45 @@
+<?php
+/*
+ *   This file is part of NOALYSS.
+ *
+ *   NOALYSS is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   NOALYSS is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with NOALYSS; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+// Copyright Author Dany De Bontridder danydb@aevalys.eu 11/08/24
+/*! 
+ * \file
+ * \brief 
+ */
+?>
+<div id="add_todo_list" class="box" style="display:none">
+
+    <form method="post">
+        <?php
+        $wDate=new IDate('p_date_todo');
+        $wDate->id='p_date_todo';
+        $wTitle=new IText('p_title');
+        $wDesc=new ITextArea('p_desc');
+        $wDesc->heigh=5;
+        $wDesc->width=40;
+        echo HtmlInput::title_box(_("Note"),"add_todo_list","hide",'',"n");
+        echo _("Date")." ".$wDate->input().'<br>';
+        echo _("Titre")." ".$wTitle->input().'<br>';
+        echo _("Description")."<br>".$wDesc->input().'<br>';
+        echo dossier::hidden();
+        echo HtmlInput::hidden('tl_id',0);
+        echo 
HtmlInput::submit('save_todo_list',_('Sauve'),'onClick="Effect.Fold(\'add_todo_list\');return
 true;"');
+        echo 
HtmlInput::button('hide',_('Annuler'),'onClick="Effect.Fold(\'add_todo_list\');return
 true;"');
+        ?>
+    </form>
+</div>
diff --git a/include/widget/todo_list/todo_list.php 
b/include/widget/todo_list/todo_list.php
new file mode 100644
index 000000000..e572c0f22
--- /dev/null
+++ b/include/widget/todo_list/todo_list.php
@@ -0,0 +1,77 @@
+<?php
+/*
+ *   This file is part of NOALYSS.
+ *
+ *   NOALYSS is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   NOALYSS is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with NOALYSS; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+// Copyright Author Dany De Bontridder danydb@aevalys.eu 10/08/24
+/*! 
+ * \file
+ * \brief widget Todo List
+ * \note this widget is  included in Noalyss Core and a part of the code 
(javascript + css + ajax)
+ *  are still included in NOALYSS Code, this code should move here and will be 
part of a "cleansing code" process
+ */
+
+namespace Noalyss\Widget;
+
+/**
+ * \class Todo_List
+ * \brief widget Todo List
+ * @note this widget is  included in Noalyss Core and a part of the code 
(javascript + css + ajax)
+ * are still included in NOALYSS Code, this code should move here and will be 
part of a "cleansing code" process
+ */
+class Todo_List extends Widget
+{
+
+
+    function display()
+    {
+        global $cn;
+        echo '<div class="box" id="todo_list">';
+        echo \HtmlInput::title_box(_('Pense-Bête'), "todo_listg_div", 'zoom', 
"zoom_todo()", 'n');
+        echo \Dossier::hidden();
+        $todo = new \Todo_List($cn);
+        $array = $todo->load_all();
+        $a_todo = \Todo_List::to_object($cn, $array);
+
+        echo \HtmlInput::button('add', _('Ajout'), 'onClick="add_todo()"', 
'smallbutton');
+        echo '<table id="table_todo" class="sortable" style="width:100%">';
+        echo '<tr><th class=" sorttable_sorted_reverse" 
id="todo_list_date">Date</th><th>Titre</th><th></th>';
+        if (!empty ($array)) {
+            $nb = 0;
+            $today = date('d.m.Y');
+
+            foreach ($a_todo as $row) {
+                if ($nb % 2 == 0) $odd = 'odd '; else $odd = 'even ';
+                $nb++;
+                echo $row->display_row($odd);
+            }
+        }
+        echo '</table>';
+        echo $this->display_new_note();
+        echo '</div>';
+    }
+
+    /**************************************************************************
+     * Ajout d'une nouvelle note
+     *************************************************************************/
+    private function display_new_note()
+    {
+        require_once 'todo_list-display_new_note.php';
+    }
+
+}
+
+
diff --git a/include/widget/widget-development.md 
b/include/widget/widget-development.md
new file mode 100644
index 000000000..f6c4756e8
--- /dev/null
+++ b/include/widget/widget-development.md
@@ -0,0 +1,108 @@
+# Développement de "widget"
+
+le code du widget , est toujours le même que celui du sous-répertoire dans 
NOALYSS_INCLUDE/widget, que le nom du fichier
+principal et aussi, le nom de la CLASS. Donc ce nom ne peut pas contenir 
d'autres caractères que des lettres 
+et des soulignés. Toujours en minuscule.
+
+Il faut aussi l'ajouter dans la table WIDGET_DASHBOARD
+    * wd_id clef primaire
+    * wd_code code du widget 
+    * wd_description description du widget , ce qu'il fait
+    * wd_parameter int , égal à 0, s'il n'y a pas de paramètre, à 1 s'il 
existe un paramètre (voir Paramètre)
+
+## Installation
+
+Création dans le répertoire d'un fichier appelé "install.php" dans le 
sous-répertoide qui sera exécuté si 
+le widget n'est pas encore dans la base de données
+
+Exemple
+
+    global $cn;
+    
+    $cn->exec_sql("insert into widget_dashboard 
(wd_code,wd_description,wd_parameter,wd_name) values ($1,$2,$3,$4)",
+    array (
+    'event'
+    ,'Affiche les 10 actions en retards, celles à venir , les 10 prochaines 
factures client ou fournisseurs, ou celles en retard '
+    ,1
+    ,'10 actions ou factures'
+    )
+    );
+
+
+## Ajax 
+
+Tous les appels ajax appellent "html/ajax_misc.php" , la requête doit contenir 
: 
+
+    * la variable "op" : "widget"
+    * la variable "w" : chaine qui est le nom du widget (nom du 
sous-répertoire de include/widget) 
+
+le fichier ajax.php du sous-répertoire NOALYSS_INCLUDE/widget/(w)/ajax.php 
sera appelé
+
+## Application
+
+La classe s'appele toujours "nom-du-widget.php" , elle est dérivée de widget 
et doit avoir les fonctions 
+
+* display  : affichage du widget
+* input : affichage de la description et permet son activation (visible dans 
la box )
+* input-parameter : si des paramètres doivent être sauvées, les paramètres 
sont par utilisateurs et par widget activés,
+* display_parameter
+
+### Paramètres
+
+Certains widgets peuvent être paramétrés, et il est possible d'ajouter 
plusieurs fois le même widget
+
+Exemple mini-report : choix du mini-report à afficher, 
+
+Pour cela, il faut ajouter certaines fonctions :
+
+1. function input_parameter() : création d'un FORM dont le DOMID sera le code 
widget (wd_code) suivi de "_param"
+
+exemple pour mini_report
+
+    function input_parameter() {
+   
+        $select=new \ISelect('simple_report');
+        $select->value=$this->db->make_array("select fr_id, fr_label from 
form_definition order by 2");
+        $this->make_form($select->input());
+    }
+
+2. display_parameter : affichage du paramètre 
+
+Les paramètres sont toujours sauvés "brut" , comme une chaîne URL, il faut 
donc pouvoir l'afficher, en la travaillant
+avec parse_str, pour cela il faut appeler Widget->get_parameter(), le résultat 
doit être dans un SPAN avec la classe
+"widget_param".
+
+Exemple pour mini_report
+
+    function display_parameter() {
+        $aParam=$this->get_parameter();
+        $name = $this->db->get_value("select fr_label from form_definition 
where fr_id=$1",[$aParam['simple_report']]);
+        echo " ";
+        echo span(_("Rapport") ." ".h($name),' class="widget_param"');
+
+
+    }
+
+
+### function  display
+
+Toujours commencer par un DIV (id=code_widget+uw_id), Widget::open_div et 
Widget::close_div
+
+Exemple :
+
+      $this->open_div();
+      echo h2('Planifications','class="title"');
+        // Code pour le widget
+      $this->open_div();
+
+Voir aussi "Paramètres"
+
+
+# Problèmes à résoudre
+
+## Taille
+
+Certains widget ont besoin de plus de place 
+Utilisation de vw et vh , attention agenda utilise des tailles fixes !
+
+## Position
diff --git a/include/widget/widget.php b/include/widget/widget.php
new file mode 100644
index 000000000..d96d31e46
--- /dev/null
+++ b/include/widget/widget.php
@@ -0,0 +1,335 @@
+<?php
+/*
+ *   This file is part of NOALYSS.
+ *
+ *   NOALYSS is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   NOALYSS is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with NOALYSS; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+// Copyright Author Dany De Bontridder danydb@aevalys.eu 10/08/24
+/*! 
+ * \file
+ * \brief Main class for widget
+ */
+
+namespace Noalyss\Widget;
+
+/*!
+ *\class
+ * \brief Main class for widget
+ */
+abstract class Widget
+{
+
+    public function __construct(protected int $user_widget_id=0,protected 
string $widget_code="",protected  $db=null)
+    {
+        if ($db == null) {
+            $this->db=\Dossier::connect();
+        }
+    }
+
+    public function get_user_widget_id(): int
+    {
+        return $this->user_widget_id;
+    }
+
+    public function set_user_widget_id(int $user_widget_id): Widget
+    {
+        $this->user_widget_id = $user_widget_id;
+        return $this;
+    }
+
+    public function get_widget_code(): string
+    {
+        return $this->widget_code;
+    }
+
+    public function set_widget_code(string $widget_code):  Widget
+    {
+        $this->widget_code = $widget_code;
+        return $this;
+    }
+
+    /**
+     * @brief display the content for the current connected user of the widget 
with the parameter
+     * @return mixed
+     */
+    abstract function display();
+
+    /**
+     * @brief display a description of the widget and allow to save it for the 
current user, call input_param function
+     * of the widget if it exists
+     * @return mixed
+     */
+    function input()
+    {
+
+        //read description from database
+        $row=$this->db->get_row("
+                    select 
+                        wd_code
+                        ,wd_name
+                        ,wd_parameter,
+                        wd_description 
+                    from 
+                        widget_dashboard 
+                    where 
+                        wd_code=$1",
+        [$this->widget_code]);
+
+        echo "<li id=\"elt_{$this->user_widget_id}\"> <span 
class='widget-name'>{$row['wd_name']}</span>{$row['wd_description']}";
+
+        if ( $this->user_widget_id > 0) {
+            if ( $row['wd_parameter'] == 1) {
+                $this->display_parameter();
+            }
+            echo '<span 
style="float:right;color:red">'.\Icon_Action::trash(uniqid(),sprintf("widget.delete('%s')",$this->user_widget_id));
+        }
+        else {
+            if ( $row['wd_parameter'] == 1) {
+                $this->input_parameter();
+            }
+            echo '<span 
style="float:right;">'.\Icon_Action::icon_add(uniqid(),sprintf("widget.add('%s')",$this->widget_code));
+        }
+
+        echo '</span>';
+        echo '</li>';
+    }
+
+    /**
+     * @brief returns an array  of widget for the connected user, ordered
+     * @return array [ uw_id,dashboard_widget_id,wd_code,wd_description
+     */
+    static function get_enabled_widget():array
+    {
+        global $g_user,$cn;
+        return $cn->get_array("
+        select 
+       uw.uw_id,
+       uw.dashboard_widget_id ,
+       wd.wd_code,
+       wd.wd_description
+       from user_widget uw 
+join widget_dashboard wd on (uw.dashboard_widget_id=wd.wd_id)
+where use_login=$1 order by uw.uw_order
+",[$g_user->login]);
+
+    }
+
+    /**
+     * @brief Build a widget thank the user_widget_id (SQL :PK :  
USER_WIDGET.UW_ID) and $widget_code
+     * @param $user_widget_id integer (SQL :PK :  USER_WIDGET.UW_ID)
+     * @param $widget_code string (SQL WIDGET_DASHBOARD.WD_CODE)
+     * @return Widget
+     */
+    static function build_user_widget($user_widget_id,$widget_code):?Widget
+    {
+        // load the class if file (code/code.php) exists.
+        if 
(file_exists(NOALYSS_INCLUDE."/widget/$widget_code/$widget_code.php")) {
+            require_once 
NOALYSS_INCLUDE."/widget/$widget_code/$widget_code.php";
+            $class=sprintf("\\Noalyss\\Widget\\%s",$widget_code);
+            $obj= new $class;
+            $obj->set_widget_code($widget_code);
+            $obj->set_user_widget_id($user_widget_id);
+            return $obj;
+        }
+
+        // return the object
+        return null;
+    }
+
+    /**
+     * @brief output the DIV HTML with class and id for the widget
+     * @return void
+     */
+    function open_div() {
+        printf( '<div id="%s_%s" 
class="box">',$this->widget_code,$this->user_widget_id);
+    }
+    function close_div() {
+        echo '</div>';
+    }
+
+    /**
+     * @brief display a box and fills it with the content of an ajax calls , 
the ajax calls Widget::display
+     * @param Widget $widget
+     * @return void
+     */
+    static function ajax_display(Widget $widget ){
+        $box= sprintf( 
'%s_%s',$widget->get_widget_code(),$widget->get_user_widget_id());
+        $widget->open_div();
+
+        print '<div class="loading_msg"></div>';
+        print '<div class="loading_msg"></div>';
+        print '<div class="loading_msg"></div>';
+        print '<div class="loading_msg"></div>';
+        print '<div class="loading_msg"></div>';
+        print '<div class="loading_msg"></div>';
+        print '<div class="loading_msg"></div>';
+        print '<div class="loading_msg"></div>';
+        echo p(_("Un instant, on charge :-)"));
+        $widget->close_div();
+
+        $dossier_id=\Dossier::id();
+        echo <<<EOF
+<script>
+var widget= new Widget('{$dossier_id}') 
+widget.display('{$box}',{$widget->get_user_widget_id()},'{$widget->get_widget_code()}')
+</script>
+
+
+EOF;
+
+
+
+    }
+
+    /**
+     * @brier display activated widgets
+     * @return void
+     */
+    static function display_available()
+    {
+
+        $aWidget=Widget::get_enabled_widget();
+        echo '<ul class="list-unstyled" id="contain_widget">';
+        foreach ($aWidget as $item) {
+            $widget=Widget::build_user_widget($item['uw_id'],$item['wd_code']);
+            $widget->input();
+        }
+        echo '</ul>';
+        echo \HtmlInput::hidden("order_widget_hidden", "");
+        create_script("widget.create_sortable()");
+    }
+
+    /**
+     * @brief save widget order from an array
+     * @param $array array of USER_WIDGET.UW_ID
+     * @return void
+     * @exception DatabaseCore fails , cannot update
+     */
+    static function save($array) {
+        if (empty($array)) return;
+        global $cn,$g_user;
+        try {
+            $cn->start();
+            $order=10;
+            $cn->exec_sql("create temporary table tmp_widget(user_widget_id 
integer,tw_order integer )");
+            foreach ($array as $item) {
+                $cn->exec_sql('insert into tmp_widget(user_widget_id,tw_order 
) values ($1,$2)',
+                [$item,$order]);
+                $order+=20;
+            }
+            $cn->exec_sql("delete from user_widget where use_login = $1 and 
uw_id not in (select user_widget_id from tmp_widget)",
+                array($g_user->getLogin()));
+
+            $cn->exec_sql("update user_widget set uw_order =tw_order from  
tmp_widget where user_widget_id=uw_id");
+
+            $cn->commit();
+
+        } catch (\Exception $e) {
+            throw ($e);
+        }
+
+
+    }
+
+    /**
+     * @brief show all the widget that can be added
+     * @return void
+     */
+    public static function select_available()
+    {
+        global $cn;
+        Widget::scanfolder();
+        $aWidget=$cn->get_array("select wd_code,wd_name, 
wd_description,wd_parameter from widget_dashboard order by wd_name");
+        echo '<ul id="widget_add" class="list-unstyled">';
+        foreach ($aWidget as $item) {
+            $widget=Widget::build_user_widget(-1,$item['wd_code']);
+            $widget?->input();
+
+        }
+        echo '</ul>';
+    }
+
+    /**
+     * @brief open a form with the DOMID "widget_code"_param, it appears once 
only for each widget in the dialog box
+     * for adding widget to the dashboard
+     * @param $html_input string HTML string with all the HTML  INPUT that 
will be enclosed by the FORM
+     * @return void
+     */
+    function make_form($html_input)
+    {
+        printf ('<form id="%s_param" 
style="display:inline">',$this->widget_code);
+        echo $html_input;
+        printf ('</form>');
+    }
+
+    /**
+     * @brief MUST BE overrided if the widget needs extra parameters, create a 
FORM to add extra-parameter
+     * @return void
+     * @throws \Exception
+     */
+    function input_parameter ()
+    {
+        throw new \Exception(__FUNCTION__." not implemented");
+    }
+
+    /**
+     * @brief MUST BE overrided if the widget needs extra parameters, display 
the content of extra-parameter
+     * @param $user_widget_id
+     * @return void
+     * @throws \Exception
+     */
+
+    function display_parameter() {
+        throw new \Exception(__FUNCTION__." not implemented");
+    }
+
+    /**
+     * @brief scan folder to find install.php file , include them if the code 
is not in DB
+     * @return void
+     */
+    static   function scanfolder()
+    {
+        global $cn;
+        $handle=opendir(NOALYSS_INCLUDE."/widget");
+        while (($dir = readdir($handle)) != false ) {
+            $directory=NOALYSS_INCLUDE."/widget".DIRECTORY_SEPARATOR.$dir;
+            if (is_dir($directory) && $dir != "." && $dir != "..") {
+                // code exists in DB ?
+                $cnt=$cn->get_value("select count(*) from widget_dashboard 
where wd_code = $1",[$dir]);
+                if ( $cnt > 0) {
+                    continue;
+                } else {
+                    // include the install.php file if any and install the 
widget
+                    if (file_exists($directory . DIRECTORY_SEPARATOR . 
"install.php")){
+                        include $directory . DIRECTORY_SEPARATOR . 
"install.php";
+                    }
+                }
+            }
+        }
+
+    }
+
+    /**
+     * @brief get the parameter of the widget and returns an array
+     * @return array  key=>value
+     */
+    function get_parameter()
+    {
+        $param = $this->db->get_value("select uw_parameter from user_widget 
where uw_id=$1",[$this->user_widget_id]);
+        if (empty ($param)) return [];
+        parse_str($param,$aParam);
+        return $aParam;
+    }
+}
\ No newline at end of file
diff --git a/sql/upgrade.sql b/sql/upgrade.sql
index 8f116d3cf..605cce64b 100644
--- a/sql/upgrade.sql
+++ b/sql/upgrade.sql
@@ -21,3 +21,62 @@ select replace_menu_code('CFGTAG','C0TAG');
 
 update menu_ref set me_description ='Configuration des extensions' where 
me_code='C0PLG';
 update menu_ref set me_description ='Clef de répartition pour la comptabilité 
analytique' where me_code='ANCKEY';
+
+--- widget pour DASHBOARD
+drop table dashboard_widget;
+drop table if exists user_widget;
+drop table if exists  user_widger;
+drop table if exists widget_dashboard;
+
+create table widget_dashboard(
+                                 wd_id int primary key generated by default as 
identity,
+                                 wd_code text not null,
+                                 wd_description text not null,
+                                 wp_parameter int not null default 0
+);
+
+
+
+create table  user_widget(
+                             uw_id int primary key generated by default as 
identity,
+                             use_login text not null,
+                             dashboard_widget_id int not null references 
widget_dashboard(wd_id) on update cascade on delete cascade,
+                             uw_parameter text ,
+                             uw_order int
+);
+
+ALTER TABLE public.widget_dashboard RENAME COLUMN wp_parameter TO wd_parameter;
+ALTER TABLE public.widget_dashboard ADD CONSTRAINT widget_dashboard_unique 
UNIQUE (wd_code);
+
+INSERT INTO public.widget_dashboard 
(wd_id,wd_code,wd_description,wd_parameter,wd_name) VALUES
+                                                                               
       (1,'agenda','Présentation d''un calendrier avec les dates de rappel des 
événements',0,'Agenda'),
+                                                                               
       (2,'todo_list','Liste de choses à faire , de petites 
notes',0,'Pense-Bête'),
+                                                                               
       (3,'coming_event','Rappel pour des actions pour  aujourd''hui ou en 
retard',0,'Actions'),
+                                                                               
       (4,'last_operation','Affichage des dernières opérations comptables 
saisies',0,'Comptabilité'),
+                                                                               
       (5,'last_event','Affiche les derniers actions de suivi',0,'Suivi'),
+                                                                               
       (6,'mini_report','Affiche des rapports de comptabilité',1,'Rapport');
+
+
+INSERT INTO public.user_widget 
(use_login,dashboard_widget_id,uw_parameter,uw_order)
+    VALUES
+         ('admin',1,NULL,10),
+         ('admin',3,NULL,30),
+         ('admin',4,NULL,40),
+         ('admin',5,NULL,50),
+         ('admin',2,NULL,45);
+
+alter table  public.widget_dashboard add wd_name text;
+ALTER TABLE public.widget_dashboard ALTER COLUMN wd_name SET NOT NULL;
+
+ALTER TABLE public.widget_dashboard ALTER COLUMN wd_name SET NOT NULL;
+COMMENT ON COLUMN public.widget_dashboard.wd_name IS 'Name';
+COMMENT ON COLUMN public.widget_dashboard.wd_id IS 'PK';
+COMMENT ON COLUMN public.widget_dashboard.wd_code IS 'Code';
+COMMENT ON COLUMN public.widget_dashboard.wd_description IS 'Description';
+COMMENT ON COLUMN public.widget_dashboard.wd_parameter IS 'presence of  there 
is a parameter';
+
+update menu_ref set me_menu = me_menu ||' &#128100;' where 
me_code='PREFERENCE';
+update menu_ref set me_menu = 'Impression &#x1F4CA;' where me_code='PRINT';
+update menu_ref set me_menu = me_menu || '&#x1F4CA;' where me_code='RAPAV';
+update menu_ref set me_menu = me_menu ||' &#x1F4C7;' where me_code='CARD';
+update menu_ref set me_menu  = 'Favori &#x2728;' where me_code='BOOKMARK';



reply via email to

[Prev in Thread] Current Thread [Next in Thread]