ode-ngjs-front 1.4.16-develop-integration.202510212224 → 1.4.16-develop-integration.202510221210

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"widgets.module.d.ts","sourceRoot":"","sources":["../../../src/ts/modules/widgets.module.ts"],"names":[],"mappings":"AAAA,OAAgB,EAAQ,OAAO,EAAE,MAAM,SAAS,CAAC;AA4CjD,oBAAY,WAAW;IACnB,SAAS,qBAA2B;IACpC,MAAM,kBAA2B;IACjC,MAAM,kBAA2B;IACjC,QAAQ,cAAqB;IAC7B,MAAM,kBAA2B;IACjC,KAAK,UAAoB;IACzB,QAAQ,oBAA2B;IACnC,GAAG,eAA2B;IAC9B,MAAM,YAAqB;IAC3B,YAAY,mBAAsB;IAClC,YAAY,wBAA2B;IACvC,QAAQ,oBAA2B;IACnC,WAAW,uBAA2B;IACtC,OAAO,mBAA2B;IAClC,SAAS,sBAA4B;IACrC,QAAQ,oBAA2B;IACnC,WAAW,uBAA2B;IACtC,OAAO,mBAA2B;IAClC,gBAAgB,6BAA6B;IAC7C,UAAU,uBAA4B;CAEzC;AACD,oBAAY,YAAY,GAAG,CAAC,UAAU,EAAC,MAAM,KAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AA6Z9D;;GAEG;AACH,wBAAgB,eAAe,IAAG,OAAO,CAExC"}
1
+ {"version":3,"file":"widgets.module.d.ts","sourceRoot":"","sources":["../../../src/ts/modules/widgets.module.ts"],"names":[],"mappings":"AAAA,OAAgB,EAAQ,OAAO,EAAE,MAAM,SAAS,CAAC;AA4CjD,oBAAY,WAAW;IACnB,SAAS,qBAA2B;IACpC,MAAM,kBAA2B;IACjC,MAAM,kBAA2B;IACjC,QAAQ,cAAqB;IAC7B,MAAM,kBAA2B;IACjC,KAAK,UAAoB;IACzB,QAAQ,oBAA2B;IACnC,GAAG,eAA2B;IAC9B,MAAM,YAAqB;IAC3B,YAAY,mBAAsB;IAClC,YAAY,wBAA2B;IACvC,QAAQ,oBAA2B;IACnC,WAAW,uBAA2B;IACtC,OAAO,mBAA2B;IAClC,SAAS,sBAA4B;IACrC,QAAQ,oBAA2B;IACnC,WAAW,uBAA2B;IACtC,OAAO,mBAA2B;IAClC,gBAAgB,6BAA6B;IAC7C,UAAU,uBAA4B;CAEzC;AACD,oBAAY,YAAY,GAAG,CAAC,UAAU,EAAC,MAAM,KAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AA4Z9D;;GAEG;AACH,wBAAgB,eAAe,IAAG,OAAO,CAExC"}
@@ -1,23 +1,45 @@
1
1
  import { Moment } from "moment";
2
2
  import 'moment/locale/fr';
3
3
  export declare class Controller {
4
+ platform: {
5
+ readonly deploymentTag: string;
6
+ readonly cdnDomain: string;
7
+ readonly apps: {
8
+ initialize(app: import("ode-ts-client").App, alternativeApp?: boolean | undefined): Promise<void>;
9
+ getPublicConf(app: import("ode-ts-client").App): Promise<any>;
10
+ getWebAppConf(app: import("ode-ts-client").App): Promise<import("ode-ts-client").IWebApp | undefined>;
11
+ loadI18n(app: import("ode-ts-client").App): Promise<void>;
12
+ };
13
+ readonly theme: import("ode-ts-client").ITheme;
14
+ readonly analytics: {
15
+ parameters<T extends import("ode-ts-client").ITrackingParams>(type: import("ode-ts-client").TrackingType): Promise<T | undefined>;
16
+ xiti(): Promise<import("ode-ts-client").IXitiTrackingParams | undefined>;
17
+ readonly status: import("ode-ts-client").AnalyticStatus;
18
+ };
19
+ readonly idiom: import("ode-ts-client").IIdiom;
20
+ listLanguages(): Promise<string[]>;
21
+ };
22
+ lang: import("ode-ts-client").IIdiom;
4
23
  selectedUser: string;
5
24
  viewMode: "weekly" | "daily";
6
25
  userData: {
7
26
  [key: string]: {
8
- weekly: any;
9
- daily: any;
27
+ weekly?: any;
28
+ daily?: any;
29
+ yesterday?: any;
30
+ today?: any;
31
+ yesterdayTotal?: number;
10
32
  };
11
33
  };
12
34
  selectedDailyDate: string;
35
+ tempDailyDate: string;
13
36
  weekStart: Moment;
14
37
  weekEnd: Moment;
15
38
  updateChart: () => void;
16
39
  todayOnCampus: number;
17
40
  todayOffCampus: number;
18
41
  todayTotal: number;
19
- todaySchoolUsePercentage: number;
20
- todayOutOfSchoolPercentage: number;
42
+ yesterdayTotal: number;
21
43
  weeklyAvgOnCampus: number;
22
44
  weeklyAvgOffCampus: number;
23
45
  weeklyTotalAverage: number;
@@ -26,8 +48,6 @@ export declare class Controller {
26
48
  fixedTodayOnCampus: number;
27
49
  fixedTodayOffCampus: number;
28
50
  fixedTodayTotal: number;
29
- fixedTodaySchoolUsePercentage: number;
30
- fixedTodayOutOfSchoolPercentage: number;
31
51
  fixedWeeklyAvgOnCampus: number;
32
52
  fixedWeeklyAvgOffCampus: number;
33
53
  fixedWeeklyTotalAverage: number;
@@ -48,12 +68,20 @@ export declare class Controller {
48
68
  selectedChildHistogram: string;
49
69
  toggleDatePicker(show: boolean): void;
50
70
  setDateFromPicker(date: string): void;
71
+ commitTempDate(): void;
72
+ tempDateHandleKeyPress(event: any): void;
51
73
  toggleLightbox(show: boolean): void;
74
+ private getCurrentSchoolYear;
75
+ private setSelectedDate;
76
+ private setSelectedWeek;
77
+ canGoToPreviousWeek(): boolean;
78
+ canGoToNextWeek(): boolean;
79
+ canGoToPreviousDay(): boolean;
80
+ canGoToNextDay(): boolean;
52
81
  changeDay(offset: number): void;
53
82
  changeWeek(offset: number): void;
83
+ get selectedDailyDateLabel(): string;
54
84
  get weekLabel(): string;
55
- get dailyLabel(): string;
56
- get yesterdayTotal(): number;
57
85
  }
58
86
  export declare const odeModuleName = "odeCantineWidgetModule";
59
87
  //# sourceMappingURL=screen-time-widget.widget.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"screen-time-widget.widget.d.ts","sourceRoot":"","sources":["../../../../src/ts/widgets/screen-time-widget/screen-time-widget.widget.ts"],"names":[],"mappings":"AAGA,OAAe,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,kBAAkB,CAAC;AAO1B,qBAAa,UAAU;IACZ,YAAY,EAAE,MAAM,CAAM;IAC1B,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAY;IAExC,QAAQ,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG;YAAE,MAAM,EAAE,GAAG,CAAC;YAAC,KAAK,EAAE,GAAG,CAAA;SAAE,CAAA;KAAE,CAAM;IAC9D,iBAAiB,EAAE,MAAM,CAAiC;IAC1D,SAAS,EAAE,MAAM,CAA+B;IAChD,OAAO,EAAE,MAAM,CAA6B;IAE5C,WAAW,EAAE,MAAM,IAAI,CAAa;IAEpC,aAAa,SAAK;IAClB,cAAc,SAAK;IACnB,UAAU,SAAK;IACf,wBAAwB,EAAE,MAAM,CAAK;IACrC,0BAA0B,EAAE,MAAM,CAAK;IAEvC,iBAAiB,SAAK;IACtB,kBAAkB,SAAK;IACvB,kBAAkB,SAAK;IACvB,4BAA4B,EAAE,MAAM,CAAK;IACzC,8BAA8B,EAAE,MAAM,CAAK;IAG3C,kBAAkB,SAAK;IACvB,mBAAmB,SAAK;IACxB,eAAe,SAAK;IACpB,6BAA6B,EAAE,MAAM,CAAK;IAC1C,+BAA+B,EAAE,MAAM,CAAK;IAE5C,sBAAsB,SAAK;IAC3B,uBAAuB,SAAK;IAC5B,uBAAuB,SAAK;IAC5B,iCAAiC,EAAE,MAAM,CAAK;IAC9C,mCAAmC,EAAE,MAAM,CAAK;IAMhD,QAAQ,EAAE,OAAO,CAAS;IAE1B,QAAQ,EAAE,OAAO,CAAS;IAC1B,YAAY,EAAE,MAAM,CAAM;IAE1B,uBAAuB,EAAE,MAAM,IAAI,CAAa;IAChD,iBAAiB,EAAE,MAAM,IAAI,CAAa;IAE1C,YAAY,EAAE,OAAO,CAAS;IAC9B,cAAc,EAAE,OAAO,CAAS;IAEhC,QAAQ,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,CAAM;IAC9D,sBAAsB,SAAM;IAE5B,gBAAgB,CAAC,IAAI,EAAE,OAAO;IAI9B,iBAAiB,CAAC,IAAI,EAAE,MAAM;IAM9B,cAAc,CAAC,IAAI,EAAE,OAAO;IAwB5B,SAAS,CAAC,MAAM,EAAE,MAAM;IAKxB,UAAU,CAAC,MAAM,EAAE,MAAM;IAOhC,IAAW,SAAS,IAAI,MAAM,CAI7B;IAED,IAAW,UAAU,IAAI,MAAM,CAE9B;IAED,IAAW,cAAc,IAAI,MAAM,CAiBlC;CACJ;AA4UD,eAAO,MAAM,aAAa,2BAA2B,CAAC"}
1
+ {"version":3,"file":"screen-time-widget.widget.d.ts","sourceRoot":"","sources":["../../../../src/ts/widgets/screen-time-widget/screen-time-widget.widget.ts"],"names":[],"mappings":"AAGA,OAAe,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,kBAAkB,CAAC;AAM1B,qBAAa,UAAU;IACZ,QAAQ;;;;;;;;;;;;;;;;;MAAmB;IAC3B,IAAI,iCAAuB;IAC3B,YAAY,EAAE,MAAM,CAAM;IAC1B,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAY;IAExC,QAAQ,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG;YAAE,MAAM,CAAC,EAAE,GAAG,CAAC;YAAC,KAAK,CAAC,EAAE,GAAG,CAAC;YAAC,SAAS,CAAC,EAAE,GAAG,CAAC;YAAC,KAAK,CAAC,EAAE,GAAG,CAAC;YAAC,cAAc,CAAC,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,CAAM;IACvH,iBAAiB,EAAE,MAAM,CAAiC;IAC1D,aAAa,EAAE,MAAM,CAAiC;IACtD,SAAS,EAAE,MAAM,CAA+B;IAChD,OAAO,EAAE,MAAM,CAA6B;IAE5C,WAAW,EAAE,MAAM,IAAI,CAAa;IAEpC,aAAa,SAAK;IAClB,cAAc,SAAK;IACnB,UAAU,SAAK;IACf,cAAc,SAAK;IAEnB,iBAAiB,SAAK;IACtB,kBAAkB,SAAK;IACvB,kBAAkB,SAAK;IACvB,4BAA4B,EAAE,MAAM,CAAK;IACzC,8BAA8B,EAAE,MAAM,CAAK;IAG3C,kBAAkB,SAAK;IACvB,mBAAmB,SAAK;IACxB,eAAe,SAAK;IAEpB,sBAAsB,SAAK;IAC3B,uBAAuB,SAAK;IAC5B,uBAAuB,SAAK;IAC5B,iCAAiC,EAAE,MAAM,CAAK;IAC9C,mCAAmC,EAAE,MAAM,CAAK;IAKhD,QAAQ,EAAE,OAAO,CAAS;IAE1B,QAAQ,EAAE,OAAO,CAAS;IAC1B,YAAY,EAAE,MAAM,CAAM;IAE1B,uBAAuB,EAAE,MAAM,IAAI,CAAa;IAChD,iBAAiB,EAAE,MAAM,IAAI,CAAa;IAE1C,YAAY,EAAE,OAAO,CAAS;IAC9B,cAAc,EAAE,OAAO,CAAS;IAEhC,QAAQ,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,CAAM;IAC9D,sBAAsB,SAAM;IAE5B,gBAAgB,CAAC,IAAI,EAAE,OAAO;IAiB9B,iBAAiB,CAAC,IAAI,EAAE,MAAM;IAO9B,cAAc;IAcd,sBAAsB,CAAC,KAAK,EAAE,GAAG;IAMjC,cAAc,CAAC,IAAI,EAAE,OAAO;IA0BnC,OAAO,CAAC,oBAAoB;IAqB5B,OAAO,CAAC,eAAe;IASvB,OAAO,CAAC,eAAe;IAOjB,mBAAmB,IAAI,OAAO;IAO9B,eAAe,IAAI,OAAO;IAO1B,kBAAkB,IAAI,OAAO;IAO5B,cAAc,IAAI,OAAO;IAOzB,SAAS,CAAC,MAAM,EAAE,MAAM;IAmBxB,UAAU,CAAC,MAAM,EAAE,MAAM;IAMhC,IAAW,sBAAsB,IAAI,MAAM,CAE1C;IAED,IAAW,SAAS,IAAI,MAAM,CAQ7B;CACJ;AAgjBD,eAAO,MAAM,aAAa,2BAA2B,CAAC"}
package/dist/version.txt CHANGED
@@ -1 +1 @@
1
- ode-ngjs-front 21/10/2025 22:24:38
1
+ ode-ngjs-front 22/10/2025 12:11:26
@@ -1,2 +1,2 @@
1
- "use strict";(self.webpackChunkode_ngjs_front=self.webpackChunkode_ngjs_front||[]).push([[202],{5688:(e,t,r)=>{r.d(t,{Z:()=>o});const o='<style>.screen-time-summary{padding:16px;font-family:Roboto,sans-serif;display:flex;flex-direction:column;gap:12px}.title{padding-bottom:6px;font-size:16px;color:#4a4a4a;font-weight:700;border-bottom:1px solid #e0e0e0}.summary-row{display:flex;gap:12px}.summary-row:nth-of-type(2){margin-top:10px}.summary-block{flex:1;background:#e5f5ff;border-radius:12px;padding:12px;box-shadow:0 1px 3px rgba(0,0,0,.1)}.summary-block .label{font-size:16px;color:#4a4a4a;font-weight:700;margin-bottom:4px}.summary-block .value{font-size:32px;color:#333}.custom-select{appearance:none;border:none;padding:8px 32px 8px 12px;border-radius:6px;font-size:16px;color:#333;font-family:Roboto,sans-serif;background:#fff url("data:image/svg+xml,%3Csvg fill=\'none\' stroke=\'%23666\' stroke-width=\'2\' viewBox=\'0 0 24 24\' xmlns=\'http://www.w3.org/2000/svg\'%3E%3Cpath stroke-linecap=\'round\' stroke-linejoin=\'round\' d=\'M19 9l-7 7-7-7\'%3E%3C/path%3E%3C/svg%3E") no-repeat right 10px center;background-size:16px;cursor:pointer;min-width:150px}.custom-select:focus{outline:0;box-shadow:0 0 0 2px rgba(42,156,200,.3)}.see-more{display:flex;justify-content:flex-end;margin-top:8px;padding-left:4px}.see-more button{background-color:transparent;border:none;font-family:Roboto,sans-serif;font-weight:600;font-size:16px;line-height:24px;padding:6px 12px;cursor:pointer;color:#2a9cc8;border-radius:4px;transition:background-color .2s ease}.see-more button:hover{background-color:#f0f8fc}.centered-controls{margin:0 auto 20px auto;width:fit-content;text-align:center;display:flex;flex-direction:column;justify-content:center;align-items:flex-start;gap:15px}.control-group{display:flex;flex-direction:row;align-items:center;gap:10px}.control-group label{min-width:80px;text-align:left}.close-button-container{display:flex;justify-content:flex-end;margin-top:20px}.close-button{width:84px;height:40px;padding:8px 16px;background:#ff8d2e;color:#fff;font-size:16px;font-weight:700;border:none;border-radius:8px;cursor:pointer;transition:background .3s ease}.close-button:hover{background:#e67c24}.date-navigation{display:flex;align-items:center;justify-content:center;gap:1rem}.date-navigation button{background:0 0;border:none;font-size:1.5rem;cursor:pointer}.selected-date{cursor:pointer;font-weight:700}.error-message{background-color:#ffe5e5;color:#c00;border:1px solid #c00;padding:12px;border-radius:6px;font-size:14px;font-weight:500}</style> <div class="screen-time-widget-container"> <div class="screen-time-summary"> <div class="title"> <div class="label"><i18n>screenTime.title.uppercase</i18n></div> </div> <div ng-if="ctrl.isParent"> <select id="userSelect" class="custom-select" ng-model="ctrl.selectedUser" ng-options="child.userId as child.name for child in ctrl.children" ng-change="ctrl.fetchDataForCurrentUser()"> </select> </div> <div class="error-message" ng-if="ctrl.errorMessage"> {{ ctrl.errorMessage }} </div> <div ng-if="!ctrl.errorMessage"> <div class="summary-row"> <div class="summary-block"> <div class="label"><i18n>screenTime.today</i18n></div> <div class="value">{{ ctrl.todayTotal | duration }}</div> </div> </div> <div class="summary-row"> <div class="summary-block"> <div class="label"><i18n>screenTime.yesterday</i18n></div> <div class="value">{{ ctrl.yesterdayTotal | duration }}</div> </div> </div> <div class="legend"> <div class="see-more"> <button ng-click="ctrl.toggleLightbox(true)"><i18n>screenTime.displayDetails</i18n></button> </div> </div> </div> </div> <ode-modal visible="ctrl.showLightbox" on-close="ctrl.toggleLightbox(false)" size="lg" dnd-nodrag> <ode-modal-title> <h2><i18n>screenTime.title.normal</i18n></h2> </ode-modal-title> <ode-modal-body> <div class="centered-controls"> <div class="control-group"> <div ng-if="ctrl.isParent"> <label style="font-size:14px" for="userSelect"><i18n>screenTime.user</i18n> :</label> <select id="userSelect" class="custom-select" ng-model="ctrl.selectedChildHistogram" ng-options="child.userId as child.name for child in ctrl.children"> </select> </div> </div> <div class="control-group"> <label for="viewMode"><i18n>screenTime.viewMode</i18n> :</label> <select id="viewMode" class="custom-select" ng-model="ctrl.viewMode" ng-change="ctrl.updateChart()"> <option value="weekly">{{ \'screenTime.viewMode.weekly\' | i18n }}</option> <option value="daily">{{ \'screenTime.viewMode.daily\' | i18n }}</option> </select> </div> </div> <canvas id="myChart" style="width:100%;height:200px"></canvas> <div class="date-selectors centered-controls" ng-if="ctrl.showLightbox"> <div class="date-navigation" ng-if="ctrl.viewMode === \'daily\'"> <button ng-click="ctrl.changeDay(-1)">←</button> <div ng-if="!ctrl.showDatePicker" class="selected-date" ng-click="ctrl.toggleDatePicker(true)"> {{ ctrl.dailyLabel }} </div> <input type="date" ng-if="ctrl.showDatePicker" ng-model="ctrl.selectedDailyDate" ng-change="ctrl.setDateFromPicker(ctrl.selectedDailyDate)" ng-blur="ctrl.toggleDatePicker(false)" max="{{ ctrl.selectedDailyDate }}"/> <button ng-click="ctrl.changeDay(1)">→</button> </div> <div class="date-navigation" ng-if="ctrl.viewMode === \'weekly\'"> <button ng-click="ctrl.changeWeek(-1)">←</button> <div class="selected-date">{{ ctrl.weekLabel }}</div> <button ng-click="ctrl.changeWeek(1)">→</button> </div> </div> <div class="close-button-container"> <button class="close-button" ng-click="ctrl.toggleLightbox(false)"><i18n>screenTime.modal.close</i18n></button> </div> </ode-modal-body> </ode-modal> </div>'},58:function(e,t,r){var o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.odeModuleName=t.Controller=void 0;var i=o(r(4202)),a=r(9419),s=r(5668),n=o(r(381));r(4470);var l=r(7211);s.Chart.register.apply(s.Chart,s.registerables),n.default.locale("fr");var c=function(){function e(){this.selectedUser="",this.viewMode="weekly",this.userData={},this.selectedDailyDate=(0,n.default)().format("YYYY-MM-DD"),this.weekStart=(0,n.default)().startOf("isoWeek"),this.weekEnd=(0,n.default)().endOf("isoWeek"),this.updateChart=function(){},this.todayOnCampus=0,this.todayOffCampus=0,this.todayTotal=0,this.todaySchoolUsePercentage=0,this.todayOutOfSchoolPercentage=0,this.weeklyAvgOnCampus=0,this.weeklyAvgOffCampus=0,this.weeklyTotalAverage=0,this.weeklyAvgSchoolUsePercentage=0,this.weeklyAvgOutOfSchoolPercentage=0,this.fixedTodayOnCampus=0,this.fixedTodayOffCampus=0,this.fixedTodayTotal=0,this.fixedTodaySchoolUsePercentage=0,this.fixedTodayOutOfSchoolPercentage=0,this.fixedWeeklyAvgOnCampus=0,this.fixedWeeklyAvgOffCampus=0,this.fixedWeeklyTotalAverage=0,this.fixedWeeklyAvgSchoolUsePercentage=0,this.fixedWeeklyAvgOutOfSchoolPercentage=0,this.isParent=!1,this.hasError=!1,this.errorMessage="",this.fetchDataForCurrentUser=function(){},this.fetchLightboxData=function(){},this.showLightbox=!1,this.showDatePicker=!1,this.children=[],this.selectedChildHistogram=""}return e.prototype.toggleDatePicker=function(e){this.showDatePicker=e},e.prototype.setDateFromPicker=function(e){this.selectedDailyDate=(0,n.default)(e).format("YYYY-MM-DD"),this.showDatePicker=!1,this.fetchLightboxData()},e.prototype.toggleLightbox=function(e){this.showLightbox=e,e&&(this.fixedTodayOnCampus=this.todayOnCampus,this.fixedTodayOffCampus=this.todayOffCampus,this.fixedTodayTotal=this.todayTotal,this.fixedTodaySchoolUsePercentage=this.todaySchoolUsePercentage,this.fixedTodayOutOfSchoolPercentage=this.todayOutOfSchoolPercentage,this.fixedWeeklyAvgOnCampus=this.weeklyAvgOnCampus,this.fixedWeeklyAvgOffCampus=this.weeklyAvgOffCampus,this.fixedWeeklyTotalAverage=this.weeklyTotalAverage,this.fixedWeeklyAvgSchoolUsePercentage=this.weeklyAvgSchoolUsePercentage,this.fixedWeeklyAvgOutOfSchoolPercentage=this.weeklyAvgOutOfSchoolPercentage,this.selectedDailyDate=(0,n.default)().format("YYYY-MM-DD"),this.weekStart=(0,n.default)().startOf("isoWeek"),this.weekEnd=(0,n.default)().endOf("isoWeek"),this.selectedChildHistogram=this.selectedUser)},e.prototype.changeDay=function(e){this.selectedDailyDate=(0,n.default)(this.selectedDailyDate).add(e,"days").format("YYYY-MM-DD"),this.fetchLightboxData()},e.prototype.changeWeek=function(e){this.weekStart=this.weekStart.clone().add(e,"weeks"),this.weekEnd=this.weekStart.clone().endOf("isoWeek"),this.fetchLightboxData()},Object.defineProperty(e.prototype,"weekLabel",{get:function(){var e=this.weekStart.format("dddd D MMMM"),t=this.weekEnd.format("dddd D MMMM");return"du ".concat(e," au ").concat(t)},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"dailyLabel",{get:function(){return(0,n.default)(this.selectedDailyDate).format("dddd D MMMM")},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"yesterdayTotal",{get:function(){var e=(0,n.default)().subtract(1,"day").format("YYYY-MM-DD"),t="".concat(this.selectedUser,"_daily_").concat((0,n.default)().format("YYYY-MM-DD"),"_weekly_").concat((0,n.default)().startOf("isoWeek").format("YYYY-MM-DD"),"_").concat((0,n.default)().endOf("isoWeek").format("YYYY-MM-DD")),r=this.userData[t];if(!r||!r.weekly)return 0;var o=r.weekly[e];return o&&o.duration||0},enumerable:!1,configurable:!0}),e}();function d(e,t,r,o,i,a){t.children=u(t);var s="/appregistry/screen-time/".concat(r,"/weekly?startDate=").concat(i,"&endDate=").concat(a),n="/appregistry/screen-time/".concat(r,"/daily?date=").concat(o);return Promise.all([e.get(s),e.get(n)]).then(function(e){var r=e[0],o=e[1];return console.log("weekly: ",r.data),console.log("daily: ",o.data),t.hasError=!1,t.errorMessage="",{weekly:r.data.dailySummaries.reduce(function(e,t){return e[t.date]={duration:t.durationMinutes,schoolUsePercentage:t.schoolUsePercentage/100},e},{}),daily:o.data.durations.map(function(e){return{hour:e.hour,duration:e.durationMinutes,schoolUsePercentage:e.schoolUsePercentage/100}})}}).catch(function(e){return t.hasError=!0,404===(null==e?void 0:e.status)?t.errorMessage="Erreur lors de l’identification de l’utilisateur. Contactez l’administrateur de votre établissement.":t.errorMessage="Un problème technique est survenu. Si le problème persiste contactez l’administrateur de votre établissement.",console.error("Error fetching data for current user and dates:",e),Promise.reject(e)})}function u(e){var t=(0,a.session)().user.children;e.isParent=!1;if(t&&Object.keys(t).length>0){e.isParent=!0;var r=100;return Object.entries(t).map(function(e){var t=e[0],o=e[1];(r++).toString();return{id:t,name:"".concat(o.firstName," ").concat(o.lastName),userId:t}})}return[]}t.Controller=c;var f=function(){function e(){this.restrict="E",this.template=r(5688).Z,this.scope={},this.bindToController=!0,this.controller=[c],this.controllerAs="ctrl",this.require=["odeScreenTimeWidget"]}return e.prototype.link=function(e,t,r,o){var l=o?o[0]:null;if(l){var c=i.default.injector(["ng"]).get("$http"),f=(0,a.session)().user.userId;l.fetchDataForCurrentUser();var h=null,m=function(e,t,r,o){return"".concat(e,"_daily_").concat(t,"_weekly_").concat(r,"_").concat(o)};l.fetchDataForCurrentUser=function(){l.errorMessage="",l.hasError=!1;var t=m(l.selectedUser,(0,n.default)().format("YYYY-MM-DD"),(0,n.default)().startOf("isoWeek").format("YYYY-MM-DD"),(0,n.default)().endOf("isoWeek").format("YYYY-MM-DD"));d(c,l,l.selectedUser,(0,n.default)().format("YYYY-MM-DD"),(0,n.default)().startOf("isoWeek").format("YYYY-MM-DD"),(0,n.default)().endOf("isoWeek").format("YYYY-MM-DD")).then(function(r){var o,i,a,s;l.userData[t]={weekly:r.weekly,daily:r.daily},o=r.weekly,i=(0,n.default)().format("YYYY-MM-DD"),a=0,s=0,Object.keys(o).forEach(function(e){var t=o[e],r=t.duration||0,c=t.schoolUsePercentage||0,d=r*c,u=r*(1-c);(0,n.default)(e).format("YYYY-MM-DD")===i&&(a=d,s=u,l.todaySchoolUsePercentage=100*t.schoolUsePercentage,l.todayOutOfSchoolPercentage=100-100*t.schoolUsePercentage)}),l.todayOnCampus=a,l.todayOffCampus=s,l.todayTotal=a+s,e.$applyAsync()}).catch(function(t){e.$applyAsync(),console.error("Error fetching initial data for current user:",t)})},l.fetchLightboxData=function(){var t=l.selectedChildHistogram||l.selectedUser;if(t){var r=m(t,l.selectedDailyDate,l.weekStart.format("YYYY-MM-DD"),l.weekEnd.format("YYYY-MM-DD"));d(c,l,t,l.selectedDailyDate,l.weekStart.format("YYYY-MM-DD"),l.weekEnd.format("YYYY-MM-DD")).then(function(t){l.userData[r]={weekly:t.weekly,daily:t.daily},l.showLightbox&&setTimeout(function(){return l.updateChart()},50),e.$applyAsync()}).catch(function(e){console.error("Error fetching data for lightbox:",e)})}},l.updateChart=function(){var e=t[0].querySelector("#myChart");if(e){var r=e.getContext("2d");if(r){h&&h.destroy();var o=[],i=[],a=l.selectedChildHistogram||l.selectedUser,c=m(a,l.selectedDailyDate,l.weekStart.format("YYYY-MM-DD"),l.weekEnd.format("YYYY-MM-DD")),d=l.userData[c];if(d){var u="weekly"===l.viewMode?d.weekly:d.daily;if(u){if("weekly"===l.viewMode)Object.keys(u).sort().forEach(function(e){var t=u[e];o.push((0,n.default)(e).format("ddd D")),i.push(t.duration)});else u.forEach(function(e){o.push("".concat(e.hour,"h")),i.push(e.duration)});h=new s.Chart(r,{type:"bar",data:{labels:o,datasets:[{label:"",data:i,backgroundColor:"#2A9CC8"}]},options:{plugins:{legend:{display:!1},tooltip:{callbacks:{label:function(e){var t=e.raw;return"".concat(t," minutes")}}}},responsive:!0,scales:{x:{},y:{beginAtZero:!0,title:{display:!0,text:"Minutes"}}}}})}}}}},l.children=u(l),l.isParent&&l.children.length>0?(l.selectedUser||(l.selectedUser=l.children[0].userId),l.fetchDataForCurrentUser()):!l.isParent&&f?(l.selectedUser=f,l.fetchDataForCurrentUser()):(l.hasError=!0,l.errorMessage="Aucun utilisateur disponible pour afficher les données de temps d'écran.",e.$applyAsync()),e.$watch(function(){return l.viewMode},function(e,t){e!==t&&l.showLightbox&&setTimeout(function(){return l.updateChart()},50)}),e.$watch(function(){return l.selectedChildHistogram},function(e,t){e!==t&&l.showLightbox&&l.fetchLightboxData()}),e.$watch(function(){return l.showLightbox},function(e){e?(l.selectedChildHistogram=l.selectedUser,setTimeout(function(){l.updateChart()},100)):h&&(h.destroy(),h=null)})}},e}();t.odeModuleName="odeCantineWidgetModule",i.default.module(t.odeModuleName,[(0,l.odeI18nModule)().name]).filter("duration",function(){return function(e){if(isNaN(e)||null===e||e<0)return"0m";var t=Math.floor(e),r=Math.floor(t/60),o=t%60,i="";return r>0&&(i+="".concat(r,"h")),(o>0||0===r&&0===t)&&(i+="".concat(o,"m")),i.trim()||"0m"}}).directive("odeScreenTimeWidget",function(){return new f}),(0,a.notif)().onLangReady().promise.then(function(e){if("en"===e)(0,a.conf)().Platform.idiom.addKeys(r(5840));else(0,a.conf)().Platform.idiom.addKeys(r(1245))})},5840:e=>{e.exports=JSON.parse('{"screenTime.title.normal":"Screen Time","screenTime.title.uppercase":"SCREEN TIME","screenTime.today":"Today","screenTime.yesterday":"Yesterday","screenTime.weekAverage":"Week Average","screenTime.displayDetails":"View Details","screenTime.user":"User","screenTime.viewMode":"Display","screenTime.viewMode.weekly":"Weekly","screenTime.viewMode.daily":"Daily","screenTime.modal.close":"Close","screenTime.information.first":"\'Screen Time\' is a feature implemented by the Bouches-du-Rhône Department.","screenTime.information.second":"It allows measuring the student\'s activity time on the laptop provided by the Department during school hours and outside school hours.","screenTime.information.third":"\'Screen Time\' only observes the opening and closing of the student\'s session on their computer.","screenTime.information.fourth":"The feature does not record any personal usage or internet browsing data.","screenTime.error.generic":"A technical problem has occurred. If the problem persists, contact your school administrator.","screenTime.error.404":"Error identifying the user. Contact your school administrator.","screenTime.error.noUser":"No user available to display screen time data.","screenTime.totalUsage":"Total usage","screenTime.hours":"Hours","screenTime.minutes":"Minutes","screenTime.from":"from","screenTime.to":"to"}')},1245:e=>{e.exports=JSON.parse('{"screenTime.title.normal":"Temps d\'écran","screenTime.title.uppercase":"TEMPS D\'ÉCRAN","screenTime.today":"Aujourd\'hui","screenTime.yesterday":"Hier","screenTime.weekAverage":"Moyenne hebdomadaire","screenTime.displayDetails":"Voir le détail","screenTime.user":"Utilisateur","screenTime.viewMode":"Affichage","screenTime.viewMode.weekly":"Hebdomadaire","screenTime.viewMode.daily":"Quotidien","screenTime.modal.close":"Fermer","screenTime.information.first":"\'Temps d\'écran\' est une fonctionnalité mise en place par le Département des Bouches-du-Rhône.","screenTime.information.second":"Elle permet de mesurer le temps d\'activité de l\'élève sur l\'ordinateur portable fourni par le Département pendant les heures de cours et en dehors des heures de cours.","screenTime.information.third":"\'Temps d\'écran\' observe uniquement l\'ouverture et la fermeture de session de l\'élève sur son ordinateur.","screenTime.information.fourth":"La fonctionnalité n\'enregistre aucune donnée d\'usage personnel ou de navigation sur internet.","screenTime.error.generic":"Un problème technique est survenu. Si le problème persiste, contactez l\'administrateur de votre établissement.","screenTime.error.404":"Erreur lors de l\'identification de l\'utilisateur. Contactez l\'administrateur de votre établissement.","screenTime.error.noUser":"Aucun utilisateur disponible pour afficher les données de temps d\'écran.","screenTime.totalUsage":"Usage total","screenTime.hours":"Heures","screenTime.minutes":"Minutes","screenTime.from":"du","screenTime.to":"au"}')}}]);
1
+ "use strict";(self.webpackChunkode_ngjs_front=self.webpackChunkode_ngjs_front||[]).push([[202],{5688:(e,t,r)=>{r.d(t,{Z:()=>a});const a='<style>.screen-time-summary{padding:16px;font-family:Roboto,sans-serif;display:flex;flex-direction:column;gap:12px}.title{padding-bottom:6px;font-size:16px;color:#4a4a4a;font-weight:700;border-bottom:1px solid #e0e0e0}.modal-title{display:block;width:100%}.modal-body{margin:1rem 0!important}.summary-row{display:flex;gap:12px}.summary-row:nth-of-type(2){margin-top:10px}.summary-block{flex:1;background:#e5f5ff;border-radius:12px;padding:12px;box-shadow:0 1px 3px rgba(0,0,0,.1)}.summary-block .label{font-size:16px;color:#4a4a4a;font-weight:700;margin-bottom:4px}.summary-block .value{font-size:32px;color:#333}.custom-select{appearance:none;border:none;padding:8px 32px 8px 12px;border-radius:6px;font-size:16px;color:#333;font-family:Roboto,sans-serif;background:#fff url("data:image/svg+xml,%3Csvg fill=\'none\' stroke=\'%23666\' stroke-width=\'2\' viewBox=\'0 0 24 24\' xmlns=\'http://www.w3.org/2000/svg\'%3E%3Cpath stroke-linecap=\'round\' stroke-linejoin=\'round\' d=\'M19 9l-7 7-7-7\'%3E%3C/path%3E%3C/svg%3E") no-repeat right 10px center;background-size:16px;cursor:pointer;min-width:150px}.custom-select:focus{outline:0;box-shadow:0 0 0 2px rgba(42,156,200,.3)}.see-more{display:flex;justify-content:flex-end;margin-top:8px;padding-left:4px}.see-more button{background-color:transparent;border:none;font-family:Roboto,sans-serif;font-weight:600;font-size:16px;line-height:24px;padding:6px 12px;cursor:pointer;color:#2a9cc8;border-radius:4px;transition:background-color .2s ease}.see-more button:hover{background-color:#f0f8fc}.centered-controls{margin:0 auto 20px auto;width:fit-content;text-align:center}.view-controls{display:flex;width:100%;flex-direction:row;justify-content:space-between;margin-bottom:16px}.control-group{display:flex;align-items:center}.close-button-container{display:flex;justify-content:flex-end;margin-top:20px}.close-button{width:84px;height:40px;padding:8px 16px;background:#ff8d2e;color:#fff;font-size:16px;font-weight:700;border:none;border-radius:8px;cursor:pointer;transition:background .3s ease}.close-button:hover{background:#e67c24}.date-navigation{display:flex;align-items:center;justify-content:center;gap:1rem}.date-navigation button{background:0 0;border:none;font-size:1.5rem;cursor:pointer}.date-navigation button:disabled{color:#d3d3d3;cursor:not-allowed}.selected-date{cursor:pointer;font-weight:700}.error-message{background-color:#ffe5e5;color:#c00;border:1px solid #c00;padding:12px;border-radius:6px;font-size:14px;font-weight:500}.info-popover{position:absolute;z-index:10;display:block;width:90%;background-color:#fff;border:1px solid #e0e0e0;box-shadow:0 4px 6px 0 rgba(0,0,0,.08);font-size:1.5rem;line-height:1.8;padding:1rem;border-radius:6px}</style> <div class="screen-time-widget-container"> <div class="screen-time-summary"> <div class="title"> <div class="label"> <i18n>screenTime.title.uppercase</i18n> </div> </div> <div ng-if="ctrl.isParent"> <select id="userSelect" class="custom-select" ng-model="ctrl.selectedUser" ng-options="child.userId as child.name for child in ctrl.children" ng-change="ctrl.fetchDataForCurrentUser()"> </select> </div> <div class="error-message" ng-if="ctrl.errorMessage"> {{ ctrl.errorMessage }} </div> <div ng-if="!ctrl.errorMessage"> <div class="summary-row"> <div class="summary-block"> <div class="label"> <i18n>screenTime.today</i18n> </div> <div class="value">{{ ctrl.todayTotal | duration }}</div> </div> </div> <div class="summary-row"> <div class="summary-block"> <div class="label"> <i18n>screenTime.yesterday</i18n> </div> <div class="value">{{ ctrl.yesterdayTotal | duration }}</div> </div> </div> <div class="legend"> <div class="see-more"> <button ng-click="ctrl.toggleLightbox(true)"> <i18n>screenTime.displayDetails</i18n> </button> </div> </div> </div> </div> <ode-modal visible="ctrl.showLightbox" on-close="ctrl.toggleLightbox(false)" size="lg" dnd-nodrag> <ode-modal-title class="modal-title"> <h2> <i18n>screenTime.title.normal</i18n> <popover> <popover-opener><i class="fa-solid fa-circle-info"></i></popover-opener> <popover-content class="info-popover"> <ul> <li> <i18n>screenTime.information.first</i18n> </li> <li> <i18n>screenTime.information.second</i18n> </li> <li> <i18n>screenTime.information.third</i18n> </li> <li> <i18n>screenTime.information.fourth</i18n> </li> </ul> </popover-content> </popover> </h2> <div class="view-controls"> <div class="control-group"> <label style="font-size:14px" for="userSelect"> <i18n>screenTime.user</i18n> :</label> <div ng-if="ctrl.isParent"> <select id="userSelect" class="custom-select" ng-model="ctrl.selectedChildHistogram" ng-options="child.userId as child.name for child in ctrl.children"> </select> </div> </div> <div class="control-group"> <label for="viewMode"> <i18n>screenTime.viewMode</i18n> :</label> <select id="viewMode" class="custom-select" ng-model="ctrl.viewMode" ng-change="ctrl.updateChart()"> <option value="weekly"> [[ctrl.lang.translate("screenTime.viewMode.weekly")]] </option> <option value="daily"> [[ctrl.lang.translate("screenTime.viewMode.daily")]] </option> </select> </div> </div> </ode-modal-title> <ode-modal-body class="modal-body"> <div class="date-selectors centered-controls" ng-if="ctrl.showLightbox"> <div class="date-navigation" ng-if="ctrl.viewMode === \'daily\'"> <button ng-click="ctrl.changeDay(-1)" ng-disabled="!ctrl.canGoToPreviousDay()">←</button> <div ng-if="!ctrl.showDatePicker" class="selected-date" ng-click="ctrl.toggleDatePicker(true)"> {{ ctrl.selectedDailyDateLabel }} </div> <input type="date" ng-if="ctrl.showDatePicker" ng-model="ctrl.tempDailyDate" ng-blur="ctrl.commitTempDate()" ng-keypress="ctrl.tempDateHandleKeyPress($event)"/> <button ng-click="ctrl.changeDay(1)" ng-disabled="!ctrl.canGoToNextDay()">→</button> </div> <div class="date-navigation" ng-if="ctrl.viewMode === \'weekly\'"> <button ng-click="ctrl.changeWeek(-1)" ng-disabled="!ctrl.canGoToPreviousWeek()">←</button> <div class="selected-date">{{ ctrl.weekLabel }}</div> <button ng-click="ctrl.changeWeek(1)" ng-disabled="!ctrl.canGoToNextWeek()">→</button> </div> </div> <canvas id="myChart" style="width:100%;height:500px;max-height:500px"></canvas> </ode-modal-body> </ode-modal> </div>'},58:function(e,t,r){var a=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.odeModuleName=t.Controller=void 0;var o=a(r(4202)),i=r(9419),n=r(5668),s=a(r(381));r(4470),n.Chart.register.apply(n.Chart,n.registerables),s.default.locale("fr");var l=function(){function e(){this.platform=(0,i.conf)().Platform,this.lang=this.platform.idiom,this.selectedUser="",this.viewMode="weekly",this.userData={},this.selectedDailyDate=(0,s.default)().format("YYYY-MM-DD"),this.tempDailyDate=(0,s.default)().format("YYYY-MM-DD"),this.weekStart=(0,s.default)().startOf("isoWeek"),this.weekEnd=(0,s.default)().endOf("isoWeek"),this.updateChart=function(){},this.todayOnCampus=0,this.todayOffCampus=0,this.todayTotal=0,this.yesterdayTotal=0,this.weeklyAvgOnCampus=0,this.weeklyAvgOffCampus=0,this.weeklyTotalAverage=0,this.weeklyAvgSchoolUsePercentage=0,this.weeklyAvgOutOfSchoolPercentage=0,this.fixedTodayOnCampus=0,this.fixedTodayOffCampus=0,this.fixedTodayTotal=0,this.fixedWeeklyAvgOnCampus=0,this.fixedWeeklyAvgOffCampus=0,this.fixedWeeklyTotalAverage=0,this.fixedWeeklyAvgSchoolUsePercentage=0,this.fixedWeeklyAvgOutOfSchoolPercentage=0,this.isParent=!1,this.hasError=!1,this.errorMessage="",this.fetchDataForCurrentUser=function(){},this.fetchLightboxData=function(){},this.showLightbox=!1,this.showDatePicker=!1,this.children=[],this.selectedChildHistogram=""}return e.prototype.toggleDatePicker=function(e){var t=this;this.showDatePicker=e,e&&(this.tempDailyDate=this.selectedDailyDate,setTimeout(function(){if(t.tempDailyDate){var e=document.querySelector('input[type="date"]');e&&(e.value=t.tempDailyDate)}},0))},e.prototype.setDateFromPicker=function(e){var t=(0,s.default)(e).format("YYYY-MM-DD");this.showDatePicker=!1,this.setSelectedDate(t),this.fetchLightboxData()},e.prototype.commitTempDate=function(){var e=(0,s.default)(this.tempDailyDate).format("YYYY-MM-DD");(0,s.default)(e).isValid()?(this.setSelectedDate(e),this.showDatePicker=!1,this.fetchLightboxData()):(this.tempDailyDate=this.selectedDailyDate,this.showDatePicker=!1)},e.prototype.tempDateHandleKeyPress=function(e){"Enter"===e.key&&this.commitTempDate()},e.prototype.toggleLightbox=function(e){this.showLightbox=e,e&&(this.fixedTodayOnCampus=this.todayOnCampus,this.fixedTodayOffCampus=this.todayOffCampus,this.fixedTodayTotal=this.todayTotal,this.fixedWeeklyAvgOnCampus=this.weeklyAvgOnCampus,this.fixedWeeklyAvgOffCampus=this.weeklyAvgOffCampus,this.fixedWeeklyTotalAverage=this.weeklyTotalAverage,this.fixedWeeklyAvgSchoolUsePercentage=this.weeklyAvgSchoolUsePercentage,this.fixedWeeklyAvgOutOfSchoolPercentage=this.weeklyAvgOutOfSchoolPercentage,this.selectedDailyDate||(this.selectedDailyDate=(0,s.default)().format("YYYY-MM-DD")),this.weekStart&&this.weekEnd||(this.weekStart=(0,s.default)().startOf("isoWeek"),this.weekEnd=(0,s.default)().endOf("isoWeek")),this.selectedChildHistogram=this.selectedUser)},e.prototype.getCurrentSchoolYear=function(){var e=(0,s.default)(),t=e.year(),r=e.month()<8?(0,s.default)({year:t-1,month:8,day:1}):(0,s.default)({year:t,month:8,day:1}),a=r.clone().add(1,"year").subtract(1,"day");return{schoolYearStart:r.startOf("day"),schoolYearEnd:a.endOf("day")}},e.prototype.setSelectedDate=function(e){var t=(0,s.default)(e);this.selectedDailyDate=t.format("YYYY-MM-DD"),this.weekStart=t.clone().startOf("isoWeek"),this.weekEnd=t.clone().endOf("isoWeek")},e.prototype.setSelectedWeek=function(e){this.weekStart=e.clone().startOf("isoWeek"),this.weekEnd=this.weekStart.clone().endOf("isoWeek"),this.selectedDailyDate=this.weekStart.format("YYYY-MM-DD")},e.prototype.canGoToPreviousWeek=function(){var e=this.weekStart.clone().subtract(1,"week"),t=this.getCurrentSchoolYear(),r=t.schoolYearStart,a=t.schoolYearEnd;return e.isBetween(r,a)},e.prototype.canGoToNextWeek=function(){var e=this.weekStart.clone().add(1,"week"),t=this.getCurrentSchoolYear(),r=t.schoolYearStart,a=t.schoolYearEnd;return e.isBetween(r,a)},e.prototype.canGoToPreviousDay=function(){var e=(0,s.default)(this.selectedDailyDate).subtract(1,"day"),t=this.getCurrentSchoolYear(),r=t.schoolYearStart,a=t.schoolYearEnd;return e.isBetween(r,a)},e.prototype.canGoToNextDay=function(){var e=(0,s.default)(this.selectedDailyDate).add(1,"day"),t=this.getCurrentSchoolYear(),r=t.schoolYearStart,a=t.schoolYearEnd;return e.isBetween(r,a)},e.prototype.changeDay=function(e){var t=this,r=(0,s.default)(this.selectedDailyDate).add(e,"days");this.setSelectedDate(r),this.tempDailyDate=this.selectedDailyDate,this.fetchLightboxData(),this.showDatePicker&&setTimeout(function(){var e=document.querySelector('input[type="date"]');e&&(e.value=t.tempDailyDate)},0)},e.prototype.changeWeek=function(e){var t=this.weekStart.clone().add(e,"weeks");this.setSelectedWeek(t),this.fetchLightboxData()},Object.defineProperty(e.prototype,"selectedDailyDateLabel",{get:function(){return(0,s.default)(this.selectedDailyDate).format("dddd D MMMM YYYY")},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"weekLabel",{get:function(){var e=this.weekStart.format("dddd D MMMM"),t=this.weekEnd.format("dddd D MMMM"),r=this.lang.translate("screenTime.from"),a=this.lang.translate("screenTime.to");return"".concat(r," ").concat(e," ").concat(a," ").concat(t)},enumerable:!1,configurable:!0}),e}();function c(e){var t=(0,i.session)().user.children;return e.isParent=!1,t&&Object.keys(t).length>0?(e.isParent=!0,Object.entries(t).map(function(e){var t=e[0],r=e[1];return{id:t,name:"".concat(r.firstName," ").concat(r.lastName),userId:t}})):[]}t.Controller=l;var d=function(){function e(){this.restrict="E",this.template=r(5688).Z,this.scope={},this.bindToController=!0,this.controller=[l],this.controllerAs="ctrl",this.require=["odeScreenTimeWidget"]}return e.prototype.link=function(e,t,r,a){var l=a?a[0]:null;if(l){var d=o.default.injector(["ng"]).get("$http"),u=(0,i.session)().user.userId;l.fetchDataForCurrentUser();var h=null,f=function(e,t,r,a){return"".concat(e,"_daily_").concat(t,"_weekly_").concat(r,"_").concat(a)};l.fetchDataForCurrentUser=function(){l.errorMessage="",l.hasError=!1;var t=(0,s.default)().format("YYYY-MM-DD");(function(e,t,r,a){t.children=c(t);var o=(0,s.default)(a).subtract(1,"day").format("YYYY-MM-DD"),i="/appregistry/screen-time/".concat(r,"/daily?date=").concat(a),n="/appregistry/screen-time/".concat(r,"/daily?date=").concat(o);return Promise.all([e.get(n),e.get(i)]).then(function(e){var r=e[0],a=e[1];t.hasError=!1,t.errorMessage="";var o=0;r&&r.data&&r.data.durations&&(o=r.data.durations.reduce(function(e,t){return e+(t.durationMinutes||0)},0));var i=[];return a&&a.data&&a.data.durations&&(i=a.data.durations.map(function(e){return{hour:e.hour,duration:e.durationMinutes,schoolUsePercentage:e.schoolUsePercentage/100}})),{yesterdayTotal:o,today:i}}).catch(function(e){return t.hasError=!0,404===(null==e?void 0:e.status)?t.errorMessage=t.lang.translate("screenTime.error.404"):t.errorMessage=t.lang.translate("screenTime.error.generic"),console.error("Error fetching initial daily data:",e),Promise.reject(e)})})(d,l,l.selectedUser,t).then(function(r){var a,o,i,n,s="".concat(l.selectedUser,"_initial_").concat(t);l.userData[s]={yesterdayTotal:r.yesterdayTotal,today:r.today},a=r.today,o=r.yesterdayTotal,i=0,n=0,a.forEach(function(e){var t=e.duration||0,r=e.schoolUsePercentage||0;i+=t*r,n+=t*(1-r)}),l.todayOnCampus=i,l.todayOffCampus=n,l.todayTotal=i+n,l.yesterdayTotal=o,e.$applyAsync()}).catch(function(t){e.$applyAsync(),console.error("Error fetching initial daily data for current user:",t)})},l.fetchLightboxData=function(){var t=l.selectedChildHistogram||l.selectedUser;if(t){var r=f(t,l.selectedDailyDate,l.weekStart.format("YYYY-MM-DD"),l.weekEnd.format("YYYY-MM-DD")),a=l.userData[r];if("weekly"===l.viewMode&&a&&a.weekly)return setTimeout(function(){return l.updateChart()},50),void e.$applyAsync();if("daily"===l.viewMode&&a&&a.daily)return setTimeout(function(){return l.updateChart()},50),void e.$applyAsync();if("weekly"===l.viewMode){var o="/appregistry/screen-time/".concat(t,"/weekly?startDate=").concat(l.weekStart.format("YYYY-MM-DD"),"&endDate=").concat(l.weekEnd.format("YYYY-MM-DD"));d.get(o).then(function(t){l.hasError=!1,l.errorMessage="";var a=t.data.dailySummaries.reduce(function(e,t){return e[t.date]={duration:t.durationMinutes,schoolUsePercentage:t.schoolUsePercentage/100},e},{});l.userData[r]||(l.userData[r]={}),l.userData[r].weekly=a,l.showLightbox&&setTimeout(function(){return l.updateChart()},50),e.$applyAsync()}).catch(function(t){l.hasError=!0,404===(null==t?void 0:t.status)?l.errorMessage=l.lang.translate("screenTime.error.404"):l.errorMessage=l.lang.translate("screenTime.error.generic"),console.error("Error fetching weekly data for lightbox:",t),e.$applyAsync()})}else{var i="/appregistry/screen-time/".concat(t,"/daily?date=").concat(l.selectedDailyDate);d.get(i).then(function(t){l.hasError=!1,l.errorMessage="";var a=t.data.durations.map(function(e){return{hour:e.hour,duration:e.durationMinutes,schoolUsePercentage:e.schoolUsePercentage/100}});l.userData[r]||(l.userData[r]={}),l.userData[r].daily=a,l.showLightbox&&setTimeout(function(){return l.updateChart()},50),e.$applyAsync()}).catch(function(t){l.hasError=!0,404===(null==t?void 0:t.status)?l.errorMessage=l.lang.translate("screenTime.error.404"):l.errorMessage=l.lang.translate("screenTime.error.generic"),console.error("Error fetching daily data for lightbox:",t),e.$applyAsync()})}}},l.updateChart=function(){var e=t[0].querySelector("#myChart");if(e){var r=e.getContext("2d");if(r){var a=[],o=[],i=l.selectedChildHistogram||l.selectedUser,c=f(i,l.selectedDailyDate,l.weekStart.format("YYYY-MM-DD"),l.weekEnd.format("YYYY-MM-DD")),d=l.userData[c];if(d){var u="weekly"===l.viewMode?d.weekly:d.daily;if(u){if("weekly"===l.viewMode)Object.keys(u).sort().forEach(function(e){var t=u[e];a.push((0,s.default)(e).format("ddd D")),o.push(t.duration/60)});else u.forEach(function(e){a.push("".concat(e.hour,"h")),o.push(e.duration)});h?(h.data.labels=a,h.data.datasets[0].data=o,h.options={plugins:{tooltip:{callbacks:{label:function(e){var t=e.raw;if("weekly"===l.viewMode){var r=Math.floor(t),a=Math.round(60*(t-r));return 0===a?"".concat(e.dataset.label,": ").concat(r,"h"):"".concat(e.dataset.label,": ").concat(r,"h ").concat(a,"m")}return"".concat(e.dataset.label,": ").concat(t," ").concat(l.lang.translate("screenTime.minutes"))}}}},scales:{x:{stacked:!0},y:{stacked:!0,beginAtZero:!0,title:{display:!0,text:"weekly"===l.viewMode?l.lang.translate("screenTime.hours"):l.lang.translate("screenTime.minutes")}}}},h.update()):h=new n.Chart(r,{type:"bar",data:{labels:a,datasets:[{label:l.lang.translate("screenTime.totalUsage"),data:o,backgroundColor:"#2A9CC8"}]},options:{plugins:{tooltip:{callbacks:{label:function(e){var t=e.raw;if("weekly"===l.viewMode){var r=Math.floor(t),a=Math.round(60*(t-r));return 0===a?"".concat(e.dataset.label,": ").concat(r,"h"):"".concat(e.dataset.label,": ").concat(r,"h ").concat(a,"m")}return"".concat(e.dataset.label,": ").concat(t," ").concat(l.lang.translate("screenTime.minutes"))}}},legend:{display:!1,position:"bottom"}},responsive:!0,scales:{x:{stacked:!0},y:{stacked:!0,beginAtZero:!0,title:{display:!0,text:"weekly"===l.viewMode?l.lang.translate("screenTime.hours"):l.lang.translate("screenTime.minutes")}}}}})}else l.fetchLightboxData()}else l.fetchLightboxData()}}},l.children=c(l),l.isParent&&l.children.length>0?(l.selectedUser||(l.selectedUser=l.children[0].userId),l.fetchDataForCurrentUser()):!l.isParent&&u?(l.selectedUser=u,l.fetchDataForCurrentUser()):(l.hasError=!0,l.errorMessage=l.lang.translate("screenTime.error.noUser"),e.$applyAsync()),e.$watch(function(){return l.viewMode},function(e,t){e!==t&&l.showLightbox&&setTimeout(function(){return l.updateChart()},50)}),e.$watch(function(){return l.selectedChildHistogram},function(e,t){e!==t&&l.showLightbox&&l.fetchLightboxData()}),e.$watch(function(){return l.showLightbox},function(e){e?(l.selectedChildHistogram=l.selectedUser,setTimeout(function(){l.updateChart()},100)):h&&(h.destroy(),h=null)})}},e}();t.odeModuleName="odeCantineWidgetModule",o.default.module(t.odeModuleName,[]).filter("duration",function(){return function(e){if(isNaN(e)||null===e||e<0)return"0m";var t=Math.floor(e),r=Math.floor(t/60),a=t%60,o="";return r>0&&(o+="".concat(r,"h")),(a>0||0===r&&0===t)&&(o+="".concat(a,"m")),o.trim()||"0m"}}).directive("odeScreenTimeWidget",function(){return new d}),(0,i.notif)().onLangReady().promise.then(function(e){if(function(e){try{"en"===e?(r(2243),s.default.locale("en-gb")):(r(4470),s.default.locale("fr"))}catch(e){console.warn("Failed to load moment locale:",e),s.default.locale("fr")}}(e),"en"===e)(0,i.conf)().Platform.idiom.addKeys(r(5840));else(0,i.conf)().Platform.idiom.addKeys(r(1245))})},5840:e=>{e.exports=JSON.parse('{"screenTime.title.normal":"Screen Time","screenTime.title.uppercase":"SCREEN TIME","screenTime.today":"Today","screenTime.yesterday":"Yesterday","screenTime.weekAverage":"Week Average","screenTime.displayDetails":"View Details","screenTime.user":"User","screenTime.viewMode":"Display","screenTime.viewMode.weekly":"Weekly","screenTime.viewMode.daily":"Daily","screenTime.modal.close":"Close","screenTime.information.first":"\'Screen Time\' is a feature implemented by the Bouches-du-Rhône Department.","screenTime.information.second":"It allows measuring the student\'s activity time on the laptop provided by the Department during school hours and outside school hours.","screenTime.information.third":"\'Screen Time\' only observes the opening and closing of the student\'s session on their computer.","screenTime.information.fourth":"The feature does not record any personal usage or internet browsing data.","screenTime.error.generic":"A technical problem has occurred. If the problem persists, contact your school administrator.","screenTime.error.404":"Error identifying the user. Contact your school administrator.","screenTime.error.noUser":"No user available to display screen time data.","screenTime.totalUsage":"Total usage","screenTime.hours":"Hours","screenTime.minutes":"Minutes","screenTime.from":"from","screenTime.to":"to"}')},1245:e=>{e.exports=JSON.parse('{"screenTime.title.normal":"Temps d\'écran","screenTime.title.uppercase":"TEMPS D\'ÉCRAN","screenTime.today":"Aujourd\'hui","screenTime.yesterday":"Hier","screenTime.weekAverage":"Moyenne de la semaine","screenTime.displayDetails":"Voir le détail","screenTime.user":"Utilisateur","screenTime.viewMode":"Affichage","screenTime.viewMode.weekly":"Hebdomadaire","screenTime.viewMode.daily":"Quotidien","screenTime.modal.close":"Fermer","screenTime.information.first":"\'Temps d\'écran\' est une fonctionnalité mise en place par le Département des Bouches-du-Rhône.","screenTime.information.second":"Elle permet de mesurer le temps d\'activité de l\'élève sur l\'ordinateur portable mis à disposition par le Département pendant le temps scolaire et en dehors du temps scolaire.","screenTime.information.third":"\'Temps d\'écran\' observe uniquement l\'ouverture et la fermeture de la session par l\'élève sur son ordinateur.","screenTime.information.fourth":"La fonctionnalité n\'enregistre aucune donnée personnelle d\'utilisation ou de navigation internet.","screenTime.error.generic":"Un problème technique est survenu. Si le problème persiste contactez l’administrateur de votre établissement.","screenTime.error.404":"Erreur lors de l’identification de l’utilisateur. Contactez l’administrateur de votre établissement.","screenTime.error.noUser":"Aucun utilisateur disponible pour afficher les données de temps d\'écran.","screenTime.totalUsage":"Usage total","screenTime.hours":"Heures","screenTime.minutes":"Minutes","screenTime.from":"du","screenTime.to":"au"}')}}]);
2
2
  //# sourceMappingURL=screen-time-widget.widget.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"widgets/screen-time-widget/screen-time-widget.widget.js","mappings":"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","sources":["webpack://ode-ngjs-front/./src/ts/widgets/screen-time-widget/screen-time-widget.widget.html","webpack://ode-ngjs-front/./src/ts/widgets/screen-time-widget/screen-time-widget.widget.ts"],"sourcesContent":["// Module\nvar code = \"<style>.screen-time-summary{padding:16px;font-family:Roboto,sans-serif;display:flex;flex-direction:column;gap:12px}.title{padding-bottom:6px;font-size:16px;color:#4a4a4a;font-weight:700;border-bottom:1px solid #e0e0e0}.summary-row{display:flex;gap:12px}.summary-row:nth-of-type(2){margin-top:10px}.summary-block{flex:1;background:#e5f5ff;border-radius:12px;padding:12px;box-shadow:0 1px 3px rgba(0,0,0,.1)}.summary-block .label{font-size:16px;color:#4a4a4a;font-weight:700;margin-bottom:4px}.summary-block .value{font-size:32px;color:#333}.custom-select{appearance:none;border:none;padding:8px 32px 8px 12px;border-radius:6px;font-size:16px;color:#333;font-family:Roboto,sans-serif;background:#fff url(\\\"data:image/svg+xml,%3Csvg fill='none' stroke='%23666' stroke-width='2' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='M19 9l-7 7-7-7'%3E%3C/path%3E%3C/svg%3E\\\") no-repeat right 10px center;background-size:16px;cursor:pointer;min-width:150px}.custom-select:focus{outline:0;box-shadow:0 0 0 2px rgba(42,156,200,.3)}.see-more{display:flex;justify-content:flex-end;margin-top:8px;padding-left:4px}.see-more button{background-color:transparent;border:none;font-family:Roboto,sans-serif;font-weight:600;font-size:16px;line-height:24px;padding:6px 12px;cursor:pointer;color:#2a9cc8;border-radius:4px;transition:background-color .2s ease}.see-more button:hover{background-color:#f0f8fc}.centered-controls{margin:0 auto 20px auto;width:fit-content;text-align:center;display:flex;flex-direction:column;justify-content:center;align-items:flex-start;gap:15px}.control-group{display:flex;flex-direction:row;align-items:center;gap:10px}.control-group label{min-width:80px;text-align:left}.close-button-container{display:flex;justify-content:flex-end;margin-top:20px}.close-button{width:84px;height:40px;padding:8px 16px;background:#ff8d2e;color:#fff;font-size:16px;font-weight:700;border:none;border-radius:8px;cursor:pointer;transition:background .3s ease}.close-button:hover{background:#e67c24}.date-navigation{display:flex;align-items:center;justify-content:center;gap:1rem}.date-navigation button{background:0 0;border:none;font-size:1.5rem;cursor:pointer}.selected-date{cursor:pointer;font-weight:700}.error-message{background-color:#ffe5e5;color:#c00;border:1px solid #c00;padding:12px;border-radius:6px;font-size:14px;font-weight:500}</style> <div class=\\\"screen-time-widget-container\\\"> <div class=\\\"screen-time-summary\\\"> <div class=\\\"title\\\"> <div class=\\\"label\\\"><i18n>screenTime.title.uppercase</i18n></div> </div> <div ng-if=\\\"ctrl.isParent\\\"> <select id=\\\"userSelect\\\" class=\\\"custom-select\\\" ng-model=\\\"ctrl.selectedUser\\\" ng-options=\\\"child.userId as child.name for child in ctrl.children\\\" ng-change=\\\"ctrl.fetchDataForCurrentUser()\\\"> </select> </div> <div class=\\\"error-message\\\" ng-if=\\\"ctrl.errorMessage\\\"> {{ ctrl.errorMessage }} </div> <div ng-if=\\\"!ctrl.errorMessage\\\"> <div class=\\\"summary-row\\\"> <div class=\\\"summary-block\\\"> <div class=\\\"label\\\"><i18n>screenTime.today</i18n></div> <div class=\\\"value\\\">{{ ctrl.todayTotal | duration }}</div> </div> </div> <div class=\\\"summary-row\\\"> <div class=\\\"summary-block\\\"> <div class=\\\"label\\\"><i18n>screenTime.yesterday</i18n></div> <div class=\\\"value\\\">{{ ctrl.yesterdayTotal | duration }}</div> </div> </div> <div class=\\\"legend\\\"> <div class=\\\"see-more\\\"> <button ng-click=\\\"ctrl.toggleLightbox(true)\\\"><i18n>screenTime.displayDetails</i18n></button> </div> </div> </div> </div> <ode-modal visible=\\\"ctrl.showLightbox\\\" on-close=\\\"ctrl.toggleLightbox(false)\\\" size=\\\"lg\\\" dnd-nodrag> <ode-modal-title> <h2><i18n>screenTime.title.normal</i18n></h2> </ode-modal-title> <ode-modal-body> <div class=\\\"centered-controls\\\"> <div class=\\\"control-group\\\"> <div ng-if=\\\"ctrl.isParent\\\"> <label style=\\\"font-size:14px\\\" for=\\\"userSelect\\\"><i18n>screenTime.user</i18n> :</label> <select id=\\\"userSelect\\\" class=\\\"custom-select\\\" ng-model=\\\"ctrl.selectedChildHistogram\\\" ng-options=\\\"child.userId as child.name for child in ctrl.children\\\"> </select> </div> </div> <div class=\\\"control-group\\\"> <label for=\\\"viewMode\\\"><i18n>screenTime.viewMode</i18n> :</label> <select id=\\\"viewMode\\\" class=\\\"custom-select\\\" ng-model=\\\"ctrl.viewMode\\\" ng-change=\\\"ctrl.updateChart()\\\"> <option value=\\\"weekly\\\">{{ 'screenTime.viewMode.weekly' | i18n }}</option> <option value=\\\"daily\\\">{{ 'screenTime.viewMode.daily' | i18n }}</option> </select> </div> </div> <canvas id=\\\"myChart\\\" style=\\\"width:100%;height:200px\\\"></canvas> <div class=\\\"date-selectors centered-controls\\\" ng-if=\\\"ctrl.showLightbox\\\"> <div class=\\\"date-navigation\\\" ng-if=\\\"ctrl.viewMode === 'daily'\\\"> <button ng-click=\\\"ctrl.changeDay(-1)\\\">←</button> <div ng-if=\\\"!ctrl.showDatePicker\\\" class=\\\"selected-date\\\" ng-click=\\\"ctrl.toggleDatePicker(true)\\\"> {{ ctrl.dailyLabel }} </div> <input type=\\\"date\\\" ng-if=\\\"ctrl.showDatePicker\\\" ng-model=\\\"ctrl.selectedDailyDate\\\" ng-change=\\\"ctrl.setDateFromPicker(ctrl.selectedDailyDate)\\\" ng-blur=\\\"ctrl.toggleDatePicker(false)\\\" max=\\\"{{ ctrl.selectedDailyDate }}\\\"/> <button ng-click=\\\"ctrl.changeDay(1)\\\">→</button> </div> <div class=\\\"date-navigation\\\" ng-if=\\\"ctrl.viewMode === 'weekly'\\\"> <button ng-click=\\\"ctrl.changeWeek(-1)\\\">←</button> <div class=\\\"selected-date\\\">{{ ctrl.weekLabel }}</div> <button ng-click=\\\"ctrl.changeWeek(1)\\\">→</button> </div> </div> <div class=\\\"close-button-container\\\"> <button class=\\\"close-button\\\" ng-click=\\\"ctrl.toggleLightbox(false)\\\"><i18n>screenTime.modal.close</i18n></button> </div> </ode-modal-body> </ode-modal> </div>\";\n// Exports\nexport default code;","import angular, { IAttributes, IController, IDirective, IScope, IHttpService } from \"angular\";\nimport { conf, notif, session } from \"../../utils\";\nimport { Chart, registerables } from \"chart.js\";\nimport moment, { Moment } from \"moment\";\nimport 'moment/locale/fr';\nimport { odeI18nModule } from \"../../modules/i18n.module\";\n\nChart.register(...registerables);\nmoment.locale('fr');\n\n// Controller\nexport class Controller {\n public selectedUser: string = \"\";\n public viewMode: \"weekly\" | \"daily\" = \"weekly\";\n\n public userData: { [key: string]: { weekly: any, daily: any } } = {};\n public selectedDailyDate: string = moment().format('YYYY-MM-DD');\n public weekStart: Moment = moment().startOf('isoWeek'); // Monday\n public weekEnd: Moment = moment().endOf('isoWeek'); // Sunday\n\n public updateChart: () => void = () => { };\n\n public todayOnCampus = 0;\n public todayOffCampus = 0;\n public todayTotal = 0;\n public todaySchoolUsePercentage: number = 0;\n public todayOutOfSchoolPercentage: number = 0;\n\n public weeklyAvgOnCampus = 0;\n public weeklyAvgOffCampus = 0;\n public weeklyTotalAverage = 0;\n public weeklyAvgSchoolUsePercentage: number = 0;\n public weeklyAvgOutOfSchoolPercentage: number = 0;\n\n // new props\n public fixedTodayOnCampus = 0;\n public fixedTodayOffCampus = 0;\n public fixedTodayTotal = 0;\n public fixedTodaySchoolUsePercentage: number = 0;\n public fixedTodayOutOfSchoolPercentage: number = 0;\n\n public fixedWeeklyAvgOnCampus = 0;\n public fixedWeeklyAvgOffCampus = 0;\n public fixedWeeklyTotalAverage = 0;\n public fixedWeeklyAvgSchoolUsePercentage: number = 0;\n public fixedWeeklyAvgOutOfSchoolPercentage: number = 0;\n\n\n // end of new \n\n\n public isParent: boolean = false;\n\n public hasError: boolean = false;\n public errorMessage: string = \"\";\n\n public fetchDataForCurrentUser: () => void = () => { };\n public fetchLightboxData: () => void = () => { };\n\n public showLightbox: boolean = false;\n public showDatePicker: boolean = false;\n\n public children: { id: string; name: string; userId: string }[] = [];\n public selectedChildHistogram = \"\";\n\n public toggleDatePicker(show: boolean) {\n this.showDatePicker = show;\n }\n\n public setDateFromPicker(date: string) {\n this.selectedDailyDate = moment(date).format('YYYY-MM-DD');\n this.showDatePicker = false;\n this.fetchLightboxData();\n }\n\n public toggleLightbox(show: boolean) {\n this.showLightbox = show;\n if (show) {\n // Store the current summary values when lightbox opens\n this.fixedTodayOnCampus = this.todayOnCampus;\n this.fixedTodayOffCampus = this.todayOffCampus;\n this.fixedTodayTotal = this.todayTotal;\n this.fixedTodaySchoolUsePercentage = this.todaySchoolUsePercentage;\n this.fixedTodayOutOfSchoolPercentage = this.todayOutOfSchoolPercentage;\n this.fixedWeeklyAvgOnCampus = this.weeklyAvgOnCampus;\n this.fixedWeeklyAvgOffCampus = this.weeklyAvgOffCampus;\n this.fixedWeeklyTotalAverage = this.weeklyTotalAverage;\n this.fixedWeeklyAvgSchoolUsePercentage = this.weeklyAvgSchoolUsePercentage;\n this.fixedWeeklyAvgOutOfSchoolPercentage = this.weeklyAvgOutOfSchoolPercentage;\n\n // Initialize lightbox dates to current date/week\n this.selectedDailyDate = moment().format('YYYY-MM-DD');\n this.weekStart = moment().startOf('isoWeek');\n this.weekEnd = moment().endOf('isoWeek');\n this.selectedChildHistogram = this.selectedUser;\n\n }\n }\n\n public changeDay(offset: number) {\n this.selectedDailyDate = moment(this.selectedDailyDate).add(offset, 'days').format('YYYY-MM-DD');\n this.fetchLightboxData();\n }\n\n public changeWeek(offset: number) {\n this.weekStart = this.weekStart.clone().add(offset, 'weeks');\n this.weekEnd = this.weekStart.clone().endOf('isoWeek');\n this.fetchLightboxData();\n }\n\n\n public get weekLabel(): string {\n const start = this.weekStart.format('dddd D MMMM');\n const end = this.weekEnd.format('dddd D MMMM');\n return `du ${start} au ${end}`;\n }\n\n public get dailyLabel(): string {\n return moment(this.selectedDailyDate).format('dddd D MMMM');\n }\n\n public get yesterdayTotal(): number {\n const yesterday = moment().subtract(1, 'day').format(\"YYYY-MM-DD\");\n \n // Find the current user's data key\n const currentKey = `${this.selectedUser}_daily_${moment().format('YYYY-MM-DD')}_weekly_${moment().startOf('isoWeek').format('YYYY-MM-DD')}_${moment().endOf('isoWeek').format('YYYY-MM-DD')}`;\n const userData = this.userData[currentKey];\n \n if (!userData || !userData.weekly) {\n return 0;\n }\n \n const yesterdayData = userData.weekly[yesterday];\n if (!yesterdayData) {\n return 0;\n }\n \n return yesterdayData.duration || 0;\n }\n}\n\nfunction fetchAllScreenTimeDataForUserAndDates($http: IHttpService, ctrl: Controller, userId: string, dailyDate: string, startDate: string, endDate: string): Promise<{ weekly: any, daily: any }> {\n ctrl.children = fetchChildren(ctrl);\n\n const weeklyEndpoint = `/appregistry/screen-time/${userId}/weekly?startDate=${startDate}&endDate=${endDate}`;\n const dailyEndpoint = `/appregistry/screen-time/${userId}/daily?date=${dailyDate}`;\n\n return Promise.all([\n $http.get(weeklyEndpoint),\n $http.get(dailyEndpoint)\n ]).then(([weeklyResponse, dailyResponse]: [any, any]) => {\n console.log(\"weekly: \", weeklyResponse.data);\n console.log(\"daily: \", dailyResponse.data);\n ctrl.hasError = false;\n ctrl.errorMessage = \"\";\n return {\n weekly: weeklyResponse.data.dailySummaries.reduce((acc: any, day: any) => {\n acc[day.date] = {\n duration: day.durationMinutes,\n schoolUsePercentage: day.schoolUsePercentage / 100 // convert to ratio\n };\n return acc;\n }, {}),\n daily: dailyResponse.data.durations.map((hour: any) => ({\n hour: hour.hour,\n duration: hour.durationMinutes,\n schoolUsePercentage: hour.schoolUsePercentage / 100 // convert to ratio\n }))\n };\n }).catch(error => {\n ctrl.hasError = true;\n\n if (error?.status === 404) {\n ctrl.errorMessage = \"Erreur lors de l’identification de l’utilisateur. Contactez l’administrateur de votre établissement.\";\n } else {\n ctrl.errorMessage = \"Un problème technique est survenu. Si le problème persiste contactez l’administrateur de votre établissement.\";\n }\n\n console.error(\"Error fetching data for current user and dates:\", error);\n return Promise.reject(error);\n });\n}\n\nfunction fetchChildren(ctrl: Controller) {\n const childrenObj = session().user.children;\n ctrl.isParent = false;\n\n const USE_MOCK_IDS = false;\n\n if (childrenObj && Object.keys(childrenObj).length > 0) {\n ctrl.isParent = true;\n\n // Replace userIds with mocked values: 100, 101, 102, ...\n let mockIdCounter = 100;\n\n return Object.entries(childrenObj).map(([userId, childData]) => {\n const child = childData as { firstName: string; lastName: string };\n const mockedId = (mockIdCounter++).toString(); // To remove and use real Id\n return {\n id: userId,\n name: `${child.firstName} ${child.lastName}`,\n userId: USE_MOCK_IDS ? mockedId : userId\n };\n });\n } else {\n return [];\n }\n}\n\nclass Directive implements IDirective<IScope, JQLite, IAttributes, IController[]> {\n restrict = \"E\";\n template = require(\"./screen-time-widget.widget.html\").default;\n scope = {};\n bindToController = true;\n controller = [Controller];\n controllerAs = \"ctrl\";\n require = [\"odeScreenTimeWidget\"];\n\n link(scope: IScope, elem: angular.IAugmentedJQuery, attrs: IAttributes, controllers?: IController[]) {\n const ctrl: Controller | null = controllers ? (controllers[0] as Controller) : null;\n if (!ctrl) return;\n\n const $http = angular.injector([\"ng\"]).get<IHttpService>(\"$http\");\n const currentUserId = session().user.userId;\n\n ctrl.fetchDataForCurrentUser();\n\n let chartInstance: Chart | null = null;\n\n // Function to update the summary values from the pre-fetched weekly data\n const updateSummary = (weeklyData: any) => {\n const today = moment().format(\"YYYY-MM-DD\");\n let todayOnCampusTemp = 0;\n let todayOffCampusTemp = 0;\n\n Object.keys(weeklyData).forEach(dateStr => {\n const entry = weeklyData[dateStr];\n const duration = entry.duration || 0;\n const schoolUsePct = entry.schoolUsePercentage || 0;\n\n const onCampus = duration * schoolUsePct;\n const offCampus = duration * (1 - schoolUsePct);\n\n if (moment(dateStr).format(\"YYYY-MM-DD\") === today) {\n todayOnCampusTemp = onCampus;\n todayOffCampusTemp = offCampus;\n ctrl.todaySchoolUsePercentage = entry.schoolUsePercentage * 100;\n ctrl.todayOutOfSchoolPercentage = 100 - (entry.schoolUsePercentage * 100);\n }\n });\n\n ctrl.todayOnCampus = todayOnCampusTemp;\n ctrl.todayOffCampus = todayOffCampusTemp;\n ctrl.todayTotal = todayOnCampusTemp + todayOffCampusTemp;\n };\n\n // Helper to generate a unique key for caching data based on user and date range\n const generateDataKey = (userId: string, dailyDate: string, startDate: string, endDate: string) => {\n return `${userId}_daily_${dailyDate}_weekly_${startDate}_${endDate}`;\n };\n\n // New function assigned to ctrl.fetchDataForCurrentUser to be called from HTML\n ctrl.fetchDataForCurrentUser = () => {\n ctrl.errorMessage = \"\";\n ctrl.hasError = false;\n const currentKey = generateDataKey(\n ctrl.selectedUser,\n moment().format('YYYY-MM-DD'), // Always use today's date for initial load\n moment().startOf('isoWeek').format('YYYY-MM-DD'),\n moment().endOf('isoWeek').format('YYYY-MM-DD')\n );\n\n // Fetch initial summary data (today and current week)\n fetchAllScreenTimeDataForUserAndDates(\n $http,\n ctrl,\n ctrl.selectedUser,\n moment().format('YYYY-MM-DD'),\n moment().startOf('isoWeek').format('YYYY-MM-DD'),\n moment().endOf('isoWeek').format('YYYY-MM-DD')\n ).then((data) => {\n ctrl.userData[currentKey] = { weekly: data.weekly, daily: data.daily };\n updateSummary(data.weekly); // Update the main summary values\n scope.$applyAsync();\n }).catch(error => {\n scope.$applyAsync();\n console.error(\"Error fetching initial data for current user:\", error);\n });\n };\n\n ctrl.fetchLightboxData = () => {\n\n const userIdForLightbox = ctrl.selectedChildHistogram || ctrl.selectedUser;\n\n if (!userIdForLightbox) return;\n\n const currentKey = generateDataKey(\n userIdForLightbox,\n ctrl.selectedDailyDate,\n ctrl.weekStart.format('YYYY-MM-DD'),\n ctrl.weekEnd.format('YYYY-MM-DD')\n );\n\n fetchAllScreenTimeDataForUserAndDates(\n $http,\n ctrl,\n userIdForLightbox,\n ctrl.selectedDailyDate,\n ctrl.weekStart.format('YYYY-MM-DD'),\n ctrl.weekEnd.format('YYYY-MM-DD')\n ).then((data) => {\n ctrl.userData[currentKey] = { weekly: data.weekly, daily: data.daily };\n if (ctrl.showLightbox) {\n setTimeout(() => ctrl.updateChart(), 50);\n }\n scope.$applyAsync();\n }).catch(error => {\n console.error(\"Error fetching data for lightbox:\", error);\n });\n };\n\n ctrl.updateChart = () => {\n const canvas = elem[0].querySelector<HTMLCanvasElement>(\"#myChart\");\n if (!canvas) return;\n\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) return;\n\n if (chartInstance) chartInstance.destroy();\n\n let labels: string[] = [];\n let appTimes: number[] = [];\n\n const userIdForChart = ctrl.selectedChildHistogram || ctrl.selectedUser;\n\n // Use the dates from the lightbox controls for the chart data key\n const currentDataKey = generateDataKey(\n userIdForChart,\n ctrl.selectedDailyDate,\n ctrl.weekStart.format('YYYY-MM-DD'),\n ctrl.weekEnd.format('YYYY-MM-DD')\n );\n const currentUserAndDateData = ctrl.userData[currentDataKey];\n\n if (!currentUserAndDateData) return;\n\n const dataToUse: any = ctrl.viewMode === \"weekly\" ? currentUserAndDateData.weekly : currentUserAndDateData.daily;\n if (!dataToUse) return;\n\n if (ctrl.viewMode === \"weekly\") {\n const sortedDates = Object.keys(dataToUse).sort();\n\n sortedDates.forEach(dateStr => {\n const entry = dataToUse[dateStr];\n labels.push(moment(dateStr).format(\"ddd D\"));\n appTimes.push(entry.duration); // Total duration instead of separated\n });\n } else {\n dataToUse.forEach((hourData: any) => {\n labels.push(`${hourData.hour}h`);\n appTimes.push(hourData.duration); // Total duration instead of separated\n });\n }\n\n chartInstance = new Chart(ctx, {\n type: \"bar\",\n data: {\n labels,\n datasets: [\n {\n label: \"\", // Empty label to hide from legend\n data: appTimes,\n backgroundColor: \"#2A9CC8\"\n }\n ]\n },\n options: {\n plugins: {\n legend: {\n display: false // Hide legend completely\n },\n tooltip: {\n callbacks: {\n label: function (context) {\n const value = context.raw;\n return `${value} minutes`;\n }\n }\n }\n },\n responsive: true,\n scales: {\n x: {\n // Removed stacked: true since we only have one dataset\n },\n y: {\n // Removed stacked: true since we only have one dataset\n beginAtZero: true,\n title: {\n display: true,\n text: \"Minutes\"\n }\n }\n }\n }\n });\n };\n\n ctrl.children = fetchChildren(ctrl);\n\n // Determine the user whose data should be fetched on load\n if (ctrl.isParent && ctrl.children.length > 0) {\n // If a parent, and has children, default to the first child if no child is selected yet\n if (!ctrl.selectedUser) {\n ctrl.selectedUser = ctrl.children[0].userId;\n }\n ctrl.fetchDataForCurrentUser();\n } else if (!ctrl.isParent && currentUserId) {\n // If not a parent, and current user ID is available, fetch data for themselves\n ctrl.selectedUser = currentUserId; // Set selectedUser to the current logged-in user's ID\n ctrl.fetchDataForCurrentUser();\n } else {\n // No children AND no current user ID (or other error condition)\n ctrl.hasError = true;\n ctrl.errorMessage = \"Aucun utilisateur disponible pour afficher les données de temps d'écran.\";\n scope.$applyAsync(); // Ensure the error message is displayed\n }\n\n // Watch for changes in view mode (within lightbox): only updates the chart\n scope.$watch(() => ctrl.viewMode, (newVal, oldVal) => {\n if (newVal !== oldVal && ctrl.showLightbox) {\n setTimeout(() => ctrl.updateChart(), 50);\n }\n });\n // Watch for selectedUser changes within the lightbox to update chart data\n scope.$watch(() => ctrl.selectedChildHistogram, (newVal, oldVal) => { // Changed from ctrl.selectedUser\n if (newVal !== oldVal && ctrl.showLightbox) {\n ctrl.fetchLightboxData();\n }\n });\n\n // Watch for lightbox visibility changes\n scope.$watch(() => ctrl.showLightbox, (isVisible: boolean) => {\n if (isVisible) {\n // When lightbox opens:\n // 1. Set selectedChildHistogram. This will trigger the selectedChildHistogram watch,\n // which will then call fetchLightboxData() (the single desired API call).\n ctrl.selectedChildHistogram = ctrl.selectedUser;\n\n // 2. Schedule chart update after a short delay, allowing fetchLightboxData() to begin.\n setTimeout(() => {\n ctrl.updateChart();\n }, 100);\n } else {\n // When lightbox closes\n if (chartInstance) {\n chartInstance.destroy();\n chartInstance = null;\n }\n }\n });\n\n }\n}\n\n// Factory function for the directive\nfunction DirectiveFactory() {\n return new Directive();\n}\n\n// Define the module name\nexport const odeModuleName = \"odeCantineWidgetModule\";\n\n// Angular module definition\nangular\n .module(odeModuleName, [odeI18nModule().name])\n // Define the custom 'duration' filter\n .filter('duration', function () {\n return function (input: number) {\n if (isNaN(input) || input === null || input < 0) {\n return '0m';\n }\n\n const totalMinutes = Math.floor(input);\n const hours = Math.floor(totalMinutes / 60);\n const minutes = totalMinutes % 60;\n\n let result = '';\n\n if (hours > 0) {\n result += `${hours}h`;\n }\n if (minutes > 0 || (hours === 0 && totalMinutes === 0)) {\n result += `${minutes}m`;\n }\n\n return result.trim() || '0m';\n };\n })\n\n // Register the directive\n .directive(\"odeScreenTimeWidget\", DirectiveFactory);\n\n// Internationalization setup\nnotif()\n .onLangReady()\n .promise.then((lang: any): void => {\n switch (lang) {\n case \"en\":\n conf().Platform.idiom.addKeys(require(\"./i18n/en.json\"));\n break;\n default:\n conf().Platform.idiom.addKeys(require(\"./i18n/fr.json\"));\n break;\n }\n });\n"],"names":["Chart","register","registerables","locale","selectedUser","viewMode","userData","selectedDailyDate","format","weekStart","startOf","weekEnd","endOf","updateChart","todayOnCampus","todayOffCampus","todayTotal","todaySchoolUsePercentage","todayOutOfSchoolPercentage","weeklyAvgOnCampus","weeklyAvgOffCampus","weeklyTotalAverage","weeklyAvgSchoolUsePercentage","weeklyAvgOutOfSchoolPercentage","fixedTodayOnCampus","fixedTodayOffCampus","fixedTodayTotal","fixedTodaySchoolUsePercentage","fixedTodayOutOfSchoolPercentage","fixedWeeklyAvgOnCampus","fixedWeeklyAvgOffCampus","fixedWeeklyTotalAverage","fixedWeeklyAvgSchoolUsePercentage","fixedWeeklyAvgOutOfSchoolPercentage","isParent","hasError","errorMessage","fetchDataForCurrentUser","fetchLightboxData","showLightbox","showDatePicker","children","selectedChildHistogram","toggleDatePicker","show","this","setDateFromPicker","date","toggleLightbox","changeDay","offset","add","changeWeek","clone","start","end","yesterday","subtract","currentKey","weekly","yesterdayData","duration","fetchAllScreenTimeDataForUserAndDates","$http","ctrl","userId","dailyDate","startDate","endDate","fetchChildren","weeklyEndpoint","dailyEndpoint","Promise","all","get","then","weeklyResponse","dailyResponse","console","log","data","dailySummaries","reduce","acc","day","durationMinutes","schoolUsePercentage","daily","durations","map","hour","catch","error","status","reject","childrenObj","session","user","Object","keys","length","entries","child","toString","id","name","firstName","lastName","Controller","restrict","template","scope","bindToController","controller","controllerAs","require","link","elem","attrs","controllers","injector","currentUserId","chartInstance","generateDataKey","weeklyData","today","todayOnCampusTemp","todayOffCampusTemp","forEach","dateStr","entry","schoolUsePct","onCampus","offCampus","$applyAsync","userIdForLightbox","setTimeout","canvas","querySelector","ctx","getContext","destroy","labels","appTimes","userIdForChart","currentDataKey","currentUserAndDateData","dataToUse","sort","push","hourData","type","datasets","label","backgroundColor","options","plugins","legend","display","tooltip","callbacks","context","value","raw","responsive","scales","x","y","beginAtZero","title","text","$watch","newVal","oldVal","isVisible","odeModuleName","module","odeI18nModule","filter","input","isNaN","totalMinutes","Math","floor","hours","minutes","result","trim","directive","Directive","notif","onLangReady","promise","lang","conf","Platform","idiom","addKeys"],"sourceRoot":""}
1
+ {"version":3,"file":"widgets/screen-time-widget/screen-time-widget.widget.js","mappings":"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","sources":["webpack://ode-ngjs-front/./src/ts/widgets/screen-time-widget/screen-time-widget.widget.html","webpack://ode-ngjs-front/./src/ts/widgets/screen-time-widget/screen-time-widget.widget.ts"],"sourcesContent":["// Module\nvar code = \"<style>.screen-time-summary{padding:16px;font-family:Roboto,sans-serif;display:flex;flex-direction:column;gap:12px}.title{padding-bottom:6px;font-size:16px;color:#4a4a4a;font-weight:700;border-bottom:1px solid #e0e0e0}.modal-title{display:block;width:100%}.modal-body{margin:1rem 0!important}.summary-row{display:flex;gap:12px}.summary-row:nth-of-type(2){margin-top:10px}.summary-block{flex:1;background:#e5f5ff;border-radius:12px;padding:12px;box-shadow:0 1px 3px rgba(0,0,0,.1)}.summary-block .label{font-size:16px;color:#4a4a4a;font-weight:700;margin-bottom:4px}.summary-block .value{font-size:32px;color:#333}.custom-select{appearance:none;border:none;padding:8px 32px 8px 12px;border-radius:6px;font-size:16px;color:#333;font-family:Roboto,sans-serif;background:#fff url(\\\"data:image/svg+xml,%3Csvg fill='none' stroke='%23666' stroke-width='2' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='M19 9l-7 7-7-7'%3E%3C/path%3E%3C/svg%3E\\\") no-repeat right 10px center;background-size:16px;cursor:pointer;min-width:150px}.custom-select:focus{outline:0;box-shadow:0 0 0 2px rgba(42,156,200,.3)}.see-more{display:flex;justify-content:flex-end;margin-top:8px;padding-left:4px}.see-more button{background-color:transparent;border:none;font-family:Roboto,sans-serif;font-weight:600;font-size:16px;line-height:24px;padding:6px 12px;cursor:pointer;color:#2a9cc8;border-radius:4px;transition:background-color .2s ease}.see-more button:hover{background-color:#f0f8fc}.centered-controls{margin:0 auto 20px auto;width:fit-content;text-align:center}.view-controls{display:flex;width:100%;flex-direction:row;justify-content:space-between;margin-bottom:16px}.control-group{display:flex;align-items:center}.close-button-container{display:flex;justify-content:flex-end;margin-top:20px}.close-button{width:84px;height:40px;padding:8px 16px;background:#ff8d2e;color:#fff;font-size:16px;font-weight:700;border:none;border-radius:8px;cursor:pointer;transition:background .3s ease}.close-button:hover{background:#e67c24}.date-navigation{display:flex;align-items:center;justify-content:center;gap:1rem}.date-navigation button{background:0 0;border:none;font-size:1.5rem;cursor:pointer}.date-navigation button:disabled{color:#d3d3d3;cursor:not-allowed}.selected-date{cursor:pointer;font-weight:700}.error-message{background-color:#ffe5e5;color:#c00;border:1px solid #c00;padding:12px;border-radius:6px;font-size:14px;font-weight:500}.info-popover{position:absolute;z-index:10;display:block;width:90%;background-color:#fff;border:1px solid #e0e0e0;box-shadow:0 4px 6px 0 rgba(0,0,0,.08);font-size:1.5rem;line-height:1.8;padding:1rem;border-radius:6px}</style> <div class=\\\"screen-time-widget-container\\\"> <div class=\\\"screen-time-summary\\\"> <div class=\\\"title\\\"> <div class=\\\"label\\\"> <i18n>screenTime.title.uppercase</i18n> </div> </div> <div ng-if=\\\"ctrl.isParent\\\"> <select id=\\\"userSelect\\\" class=\\\"custom-select\\\" ng-model=\\\"ctrl.selectedUser\\\" ng-options=\\\"child.userId as child.name for child in ctrl.children\\\" ng-change=\\\"ctrl.fetchDataForCurrentUser()\\\"> </select> </div> <div class=\\\"error-message\\\" ng-if=\\\"ctrl.errorMessage\\\"> {{ ctrl.errorMessage }} </div> <div ng-if=\\\"!ctrl.errorMessage\\\"> <div class=\\\"summary-row\\\"> <div class=\\\"summary-block\\\"> <div class=\\\"label\\\"> <i18n>screenTime.today</i18n> </div> <div class=\\\"value\\\">{{ ctrl.todayTotal | duration }}</div> </div> </div> <div class=\\\"summary-row\\\"> <div class=\\\"summary-block\\\"> <div class=\\\"label\\\"> <i18n>screenTime.yesterday</i18n> </div> <div class=\\\"value\\\">{{ ctrl.yesterdayTotal | duration }}</div> </div> </div> <div class=\\\"legend\\\"> <div class=\\\"see-more\\\"> <button ng-click=\\\"ctrl.toggleLightbox(true)\\\"> <i18n>screenTime.displayDetails</i18n> </button> </div> </div> </div> </div> <ode-modal visible=\\\"ctrl.showLightbox\\\" on-close=\\\"ctrl.toggleLightbox(false)\\\" size=\\\"lg\\\" dnd-nodrag> <ode-modal-title class=\\\"modal-title\\\"> <h2> <i18n>screenTime.title.normal</i18n> <popover> <popover-opener><i class=\\\"fa-solid fa-circle-info\\\"></i></popover-opener> <popover-content class=\\\"info-popover\\\"> <ul> <li> <i18n>screenTime.information.first</i18n> </li> <li> <i18n>screenTime.information.second</i18n> </li> <li> <i18n>screenTime.information.third</i18n> </li> <li> <i18n>screenTime.information.fourth</i18n> </li> </ul> </popover-content> </popover> </h2> <div class=\\\"view-controls\\\"> <div class=\\\"control-group\\\"> <label style=\\\"font-size:14px\\\" for=\\\"userSelect\\\"> <i18n>screenTime.user</i18n> :</label> <div ng-if=\\\"ctrl.isParent\\\"> <select id=\\\"userSelect\\\" class=\\\"custom-select\\\" ng-model=\\\"ctrl.selectedChildHistogram\\\" ng-options=\\\"child.userId as child.name for child in ctrl.children\\\"> </select> </div> </div> <div class=\\\"control-group\\\"> <label for=\\\"viewMode\\\"> <i18n>screenTime.viewMode</i18n> :</label> <select id=\\\"viewMode\\\" class=\\\"custom-select\\\" ng-model=\\\"ctrl.viewMode\\\" ng-change=\\\"ctrl.updateChart()\\\"> <option value=\\\"weekly\\\"> [[ctrl.lang.translate(\\\"screenTime.viewMode.weekly\\\")]] </option> <option value=\\\"daily\\\"> [[ctrl.lang.translate(\\\"screenTime.viewMode.daily\\\")]] </option> </select> </div> </div> </ode-modal-title> <ode-modal-body class=\\\"modal-body\\\"> <div class=\\\"date-selectors centered-controls\\\" ng-if=\\\"ctrl.showLightbox\\\"> <div class=\\\"date-navigation\\\" ng-if=\\\"ctrl.viewMode === 'daily'\\\"> <button ng-click=\\\"ctrl.changeDay(-1)\\\" ng-disabled=\\\"!ctrl.canGoToPreviousDay()\\\">←</button> <div ng-if=\\\"!ctrl.showDatePicker\\\" class=\\\"selected-date\\\" ng-click=\\\"ctrl.toggleDatePicker(true)\\\"> {{ ctrl.selectedDailyDateLabel }} </div> <input type=\\\"date\\\" ng-if=\\\"ctrl.showDatePicker\\\" ng-model=\\\"ctrl.tempDailyDate\\\" ng-blur=\\\"ctrl.commitTempDate()\\\" ng-keypress=\\\"ctrl.tempDateHandleKeyPress($event)\\\"/> <button ng-click=\\\"ctrl.changeDay(1)\\\" ng-disabled=\\\"!ctrl.canGoToNextDay()\\\">→</button> </div> <div class=\\\"date-navigation\\\" ng-if=\\\"ctrl.viewMode === 'weekly'\\\"> <button ng-click=\\\"ctrl.changeWeek(-1)\\\" ng-disabled=\\\"!ctrl.canGoToPreviousWeek()\\\">←</button> <div class=\\\"selected-date\\\">{{ ctrl.weekLabel }}</div> <button ng-click=\\\"ctrl.changeWeek(1)\\\" ng-disabled=\\\"!ctrl.canGoToNextWeek()\\\">→</button> </div> </div> <canvas id=\\\"myChart\\\" style=\\\"width:100%;height:500px;max-height:500px\\\"></canvas> </ode-modal-body> </ode-modal> </div>\";\n// Exports\nexport default code;","import angular, { IAttributes, IController, IDirective, IScope, IHttpService } from \"angular\";\nimport { conf, notif, session } from \"../../utils\";\nimport { Chart, registerables } from \"chart.js\";\nimport moment, { Moment } from \"moment\";\nimport 'moment/locale/fr';\n\nChart.register(...registerables);\nmoment.locale('fr');\n\n// Controller\nexport class Controller {\n public platform = conf().Platform;\n public lang = this.platform.idiom;\n public selectedUser: string = \"\";\n public viewMode: \"weekly\" | \"daily\" = \"weekly\";\n\n public userData: { [key: string]: { weekly?: any, daily?: any, yesterday?: any, today?: any, yesterdayTotal?: number } } = {};\n public selectedDailyDate: string = moment().format('YYYY-MM-DD');\n public tempDailyDate: string = moment().format('YYYY-MM-DD');\n public weekStart: Moment = moment().startOf('isoWeek'); // Monday\n public weekEnd: Moment = moment().endOf('isoWeek'); // Sunday\n\n public updateChart: () => void = () => { };\n\n public todayOnCampus = 0;\n public todayOffCampus = 0;\n public todayTotal = 0;\n public yesterdayTotal = 0;\n\n public weeklyAvgOnCampus = 0;\n public weeklyAvgOffCampus = 0;\n public weeklyTotalAverage = 0;\n public weeklyAvgSchoolUsePercentage: number = 0;\n public weeklyAvgOutOfSchoolPercentage: number = 0;\n\n // new props\n public fixedTodayOnCampus = 0;\n public fixedTodayOffCampus = 0;\n public fixedTodayTotal = 0;\n\n public fixedWeeklyAvgOnCampus = 0;\n public fixedWeeklyAvgOffCampus = 0;\n public fixedWeeklyTotalAverage = 0;\n public fixedWeeklyAvgSchoolUsePercentage: number = 0;\n public fixedWeeklyAvgOutOfSchoolPercentage: number = 0;\n\n\n // end of new \n\n public isParent: boolean = false;\n\n public hasError: boolean = false;\n public errorMessage: string = \"\";\n\n public fetchDataForCurrentUser: () => void = () => { };\n public fetchLightboxData: () => void = () => { };\n\n public showLightbox: boolean = false;\n public showDatePicker: boolean = false;\n\n public children: { id: string; name: string; userId: string }[] = [];\n public selectedChildHistogram = \"\";\n\n public toggleDatePicker(show: boolean) {\n this.showDatePicker = show;\n if (show) {\n this.tempDailyDate = this.selectedDailyDate;\n // Use $timeout to ensure the input shows the correct value after DOM update\n setTimeout(() => {\n if (this.tempDailyDate) {\n // Force Angular to update the input value\n const dateInput = document.querySelector('input[type=\"date\"]') as HTMLInputElement;\n if (dateInput) {\n dateInput.value = this.tempDailyDate;\n }\n }\n }, 0);\n }\n }\n\n public setDateFromPicker(date: string) {\n const newDate = moment(date).format('YYYY-MM-DD');\n this.showDatePicker = false;\n this.setSelectedDate(newDate);\n this.fetchLightboxData();\n }\n\n public commitTempDate() {\n const newDate = moment(this.tempDailyDate).format('YYYY-MM-DD');\n // Validate that the date is valid before committing\n if (moment(newDate).isValid()) {\n this.setSelectedDate(newDate);\n this.showDatePicker = false;\n this.fetchLightboxData();\n } else {\n // If invalid date, reset to current selected date and close picker\n this.tempDailyDate = this.selectedDailyDate;\n this.showDatePicker = false;\n }\n }\n \n public tempDateHandleKeyPress(event: any) {\n if (event.key === \"Enter\") {\n this.commitTempDate();\n }\n }\n\n public toggleLightbox(show: boolean) {\n this.showLightbox = show;\n if (show) {\n // Store the current summary values when lightbox opens\n this.fixedTodayOnCampus = this.todayOnCampus;\n this.fixedTodayOffCampus = this.todayOffCampus;\n this.fixedTodayTotal = this.todayTotal;\n this.fixedWeeklyAvgOnCampus = this.weeklyAvgOnCampus;\n this.fixedWeeklyAvgOffCampus = this.weeklyAvgOffCampus;\n this.fixedWeeklyTotalAverage = this.weeklyTotalAverage;\n this.fixedWeeklyAvgSchoolUsePercentage = this.weeklyAvgSchoolUsePercentage;\n this.fixedWeeklyAvgOutOfSchoolPercentage = this.weeklyAvgOutOfSchoolPercentage;\n\n // Initialize lightbox dates to current date/week only if not already set\n if (!this.selectedDailyDate) {\n this.selectedDailyDate = moment().format('YYYY-MM-DD');\n }\n if (!this.weekStart || !this.weekEnd) {\n this.weekStart = moment().startOf('isoWeek');\n this.weekEnd = moment().endOf('isoWeek');\n }\n this.selectedChildHistogram = this.selectedUser;\n\n }\n }\n\n private getCurrentSchoolYear(): { schoolYearStart: moment.Moment, schoolYearEnd: moment.Moment } {\n const today = moment();\n const currentYear = today.year();\n const currentMonth = today.month();\n\n // If current month is less than september, school year is the previous one\n const schoolYearStart = currentMonth < 8\n ? moment({ year: currentYear - 1, month: 8, day: 1 }) // 1st september of previous school year\n : moment({ year: currentYear, month: 8, day: 1 }); // 1st september of current school year\n\n const schoolYearEnd = schoolYearStart\n .clone()\n .add(1, 'year')\n .subtract(1, 'day'); // 31 august of next school year\n\n return {\n schoolYearStart: schoolYearStart.startOf('day'),\n schoolYearEnd: schoolYearEnd.endOf('day'),\n };\n }\n\n private setSelectedDate(date: string | moment.Moment) {\n const momentDate = moment(date);\n\n this.selectedDailyDate = momentDate.format('YYYY-MM-DD');\n\n this.weekStart = momentDate.clone().startOf('isoWeek');\n this.weekEnd = momentDate.clone().endOf('isoWeek');\n }\n\n private setSelectedWeek(weekStart: moment.Moment) {\n this.weekStart = weekStart.clone().startOf('isoWeek');\n this.weekEnd = this.weekStart.clone().endOf('isoWeek');\n\n this.selectedDailyDate = this.weekStart.format('YYYY-MM-DD');\n }\n\n public canGoToPreviousWeek(): boolean {\n const offsetWeekStart = this.weekStart.clone().subtract(1, 'week');\n const { schoolYearStart, schoolYearEnd } = this.getCurrentSchoolYear();\n\n return offsetWeekStart.isBetween(schoolYearStart, schoolYearEnd);\n }\n\n public canGoToNextWeek(): boolean {\n const offsetWeekStart = this.weekStart.clone().add(1, 'week');\n const { schoolYearStart, schoolYearEnd } = this.getCurrentSchoolYear();\n\n return offsetWeekStart.isBetween(schoolYearStart, schoolYearEnd);\n }\n\n public canGoToPreviousDay(): boolean {\n const offsetDayStart = moment(this.selectedDailyDate).subtract(1, 'day');\n const { schoolYearStart, schoolYearEnd } = this.getCurrentSchoolYear();\n\n return offsetDayStart.isBetween(schoolYearStart, schoolYearEnd);\n }\n\n public canGoToNextDay(): boolean {\n const offsetDayStart = moment(this.selectedDailyDate).add(1, 'day');\n const { schoolYearStart, schoolYearEnd } = this.getCurrentSchoolYear();\n\n return offsetDayStart.isBetween(schoolYearStart, schoolYearEnd);\n }\n\n public changeDay(offset: number) {\n const newDate = moment(this.selectedDailyDate).add(offset, 'days');\n this.setSelectedDate(newDate);\n // Always keep tempDailyDate in sync with selectedDailyDate\n this.tempDailyDate = this.selectedDailyDate;\n this.fetchLightboxData();\n // Force Angular to update the binding if date picker is open\n if (this.showDatePicker) {\n // Use setTimeout to ensure Angular processes the change\n setTimeout(() => {\n // Force the date input to update its value\n const dateInput = document.querySelector('input[type=\"date\"]') as HTMLInputElement;\n if (dateInput) {\n dateInput.value = this.tempDailyDate;\n }\n }, 0);\n }\n }\n\n public changeWeek(offset: number) {\n const newWeekStart = this.weekStart.clone().add(offset, 'weeks');\n this.setSelectedWeek(newWeekStart);\n this.fetchLightboxData();\n }\n\n public get selectedDailyDateLabel(): string {\n return moment(this.selectedDailyDate).format('dddd D MMMM YYYY');\n }\n\n public get weekLabel(): string {\n const start = this.weekStart.format('dddd D MMMM');\n const end = this.weekEnd.format('dddd D MMMM');\n\n const fromText = this.lang.translate(\"screenTime.from\");\n const toText = this.lang.translate(\"screenTime.to\");\n\n return `${fromText} ${start} ${toText} ${end}`;\n }\n}\n\n// Function for initial load - only fetches daily data for today and yesterday\nfunction fetchInitialDailyData($http: IHttpService, ctrl: Controller, userId: string, todayDate: string): Promise<{ yesterdayTotal: number, today: any }> {\n ctrl.children = fetchChildren(ctrl);\n\n // Calculate yesterday's date\n const yesterdayDate = moment(todayDate).subtract(1, 'day').format('YYYY-MM-DD');\n \n // Create endpoints for today and yesterday\n const todayEndpoint = `/appregistry/screen-time/${userId}/daily?date=${todayDate}`;\n const yesterdayEndpoint = `/appregistry/screen-time/${userId}/daily?date=${yesterdayDate}`;\n\n return Promise.all([\n $http.get(yesterdayEndpoint), // Get yesterday's data\n $http.get(todayEndpoint) // Get today's data\n ]).then(([yesterdayResponse, todayResponse]: [any, any]) => {\n ctrl.hasError = false;\n ctrl.errorMessage = \"\";\n \n // Handle yesterday's data - calculate total from hourly durations\n let yesterdayTotalDuration = 0;\n if (yesterdayResponse && yesterdayResponse.data && yesterdayResponse.data.durations) {\n yesterdayTotalDuration = yesterdayResponse.data.durations.reduce((total: number, hour: any) => {\n return total + (hour.durationMinutes || 0);\n }, 0);\n } else {\n // Yesterday data not available - this is normal if there's no usage\n }\n \n // Handle today's data - check if data exists and has the expected structure\n let todayData = [];\n if (todayResponse && todayResponse.data && todayResponse.data.durations) {\n todayData = todayResponse.data.durations.map((hour: any) => ({\n hour: hour.hour,\n duration: hour.durationMinutes,\n schoolUsePercentage: hour.schoolUsePercentage / 100 // convert to ratio\n }));\n } else {\n // Today data not available - this is normal if there's no usage\n }\n \n return {\n yesterdayTotal: yesterdayTotalDuration,\n today: todayData\n };\n }).catch(error => {\n ctrl.hasError = true;\n\n if (error?.status === 404) {\n ctrl.errorMessage = ctrl.lang.translate(\"screenTime.error.404\");\n } else {\n ctrl.errorMessage = ctrl.lang.translate(\"screenTime.error.generic\");\n }\n\n console.error(\"Error fetching initial daily data:\", error);\n return Promise.reject(error);\n });\n}\n\n// Function for lightbox - fetches both weekly and daily data\nfunction fetchAllScreenTimeDataForUserAndDates($http: IHttpService, ctrl: Controller, userId: string, dailyDate: string, startDate: string, endDate: string): Promise<{ weekly: any, daily: any }> {\n ctrl.children = fetchChildren(ctrl);\n\n const weeklyEndpoint = `/appregistry/screen-time/${userId}/weekly?startDate=${startDate}&endDate=${endDate}`;\n const dailyEndpoint = `/appregistry/screen-time/${userId}/daily?date=${dailyDate}`;\n\n return Promise.all([\n $http.get(weeklyEndpoint),\n $http.get(dailyEndpoint)\n ]).then(([weeklyResponse, dailyResponse]: [any, any]) => {\n ctrl.hasError = false;\n ctrl.errorMessage = \"\";\n return {\n weekly: weeklyResponse.data.dailySummaries.reduce((acc: any, day: any) => {\n acc[day.date] = {\n duration: day.durationMinutes,\n schoolUsePercentage: day.schoolUsePercentage / 100 // convert to ratio\n };\n return acc;\n }, {}),\n daily: dailyResponse.data.durations.map((hour: any) => ({\n hour: hour.hour,\n duration: hour.durationMinutes,\n schoolUsePercentage: hour.schoolUsePercentage / 100 // convert to ratio\n }))\n };\n }).catch(error => {\n ctrl.hasError = true;\n\n if (error?.status === 404) {\n ctrl.errorMessage = ctrl.lang.translate(\"screenTime.error.404\");\n } else {\n ctrl.errorMessage = ctrl.lang.translate(\"screenTime.error.generic\");\n }\n\n console.error(\"Error fetching data for current user and dates:\", error);\n return Promise.reject(error);\n });\n}\n\nfunction fetchChildren(ctrl: Controller) {\n const childrenObj = session().user.children;\n ctrl.isParent = false;\n\n if (childrenObj && Object.keys(childrenObj).length > 0) {\n ctrl.isParent = true;\n\n return Object.entries(childrenObj).map(([userId, childData]) => {\n const child = childData as { firstName: string; lastName: string };\n return {\n id: userId,\n name: `${child.firstName} ${child.lastName}`,\n userId: userId\n };\n });\n } else {\n return [];\n }\n}\n\nclass Directive implements IDirective<IScope, JQLite, IAttributes, IController[]> {\n restrict = \"E\";\n template = require(\"./screen-time-widget.widget.html\").default;\n scope = {};\n bindToController = true;\n controller = [Controller];\n controllerAs = \"ctrl\";\n require = [\"odeScreenTimeWidget\"];\n\n link(scope: IScope, elem: angular.IAugmentedJQuery, attrs: IAttributes, controllers?: IController[]) {\n const ctrl: Controller | null = controllers ? (controllers[0] as Controller) : null;\n if (!ctrl) return;\n\n const $http = angular.injector([\"ng\"]).get<IHttpService>(\"$http\");\n const currentUserId = session().user.userId;\n\n ctrl.fetchDataForCurrentUser();\n\n let chartInstance: Chart | null = null;\n\n // Function to update the summary values from initial daily data (today and yesterday)\n const updateInitialSummary = (todayData: any, yesterdayTotal: number) => {\n // Calculate today's totals from hourly data\n let todayOnCampusTemp = 0;\n let todayOffCampusTemp = 0;\n\n todayData.forEach((hourData: any) => {\n const duration = hourData.duration || 0;\n const schoolUsePct = hourData.schoolUsePercentage || 0;\n\n const onCampus = duration * schoolUsePct;\n const offCampus = duration * (1 - schoolUsePct);\n\n todayOnCampusTemp += onCampus;\n todayOffCampusTemp += offCampus;\n });\n\n ctrl.todayOnCampus = todayOnCampusTemp;\n ctrl.todayOffCampus = todayOffCampusTemp;\n ctrl.todayTotal = todayOnCampusTemp + todayOffCampusTemp;\n\n // Set yesterday's total directly\n ctrl.yesterdayTotal = yesterdayTotal;\n };\n\n // Function to update the summary values from the pre-fetched weekly data (for lightbox)\n const updateSummary = (weeklyData: any) => {\n let totalOnCampus = 0;\n let totalOffCampus = 0;\n let daysCount = 0;\n const today = moment().format(\"YYYY-MM-DD\");\n let todayOnCampusTemp = 0;\n let todayOffCampusTemp = 0;\n\n let weeklyTotalSchoolUseDuration = 0;\n let weeklyTotalOutOfSchoolDuration = 0;\n let weeklyTotalOverallDuration = 0;\n\n Object.keys(weeklyData).forEach(dateStr => {\n const entry = weeklyData[dateStr];\n const duration = entry.duration || 0;\n const schoolUsePct = entry.schoolUsePercentage || 0;\n\n const onCampus = duration * schoolUsePct;\n const offCampus = duration * (1 - schoolUsePct);\n\n totalOnCampus += onCampus;\n totalOffCampus += offCampus;\n daysCount++;\n\n weeklyTotalSchoolUseDuration += onCampus;\n weeklyTotalOutOfSchoolDuration += offCampus;\n weeklyTotalOverallDuration += duration;\n\n if (moment(dateStr).format(\"YYYY-MM-DD\") === today) {\n todayOnCampusTemp = onCampus;\n todayOffCampusTemp = offCampus;\n }\n });\n\n ctrl.todayOnCampus = todayOnCampusTemp;\n ctrl.todayOffCampus = todayOffCampusTemp;\n ctrl.todayTotal = todayOnCampusTemp + todayOffCampusTemp;\n ctrl.weeklyAvgOnCampus = daysCount > 0 ? totalOnCampus / daysCount : 0;\n ctrl.weeklyAvgOffCampus = daysCount > 0 ? totalOffCampus / daysCount : 0;\n ctrl.weeklyTotalAverage = ctrl.weeklyAvgOnCampus + ctrl.weeklyAvgOffCampus;\n\n if (weeklyTotalOverallDuration > 0) {\n ctrl.weeklyAvgSchoolUsePercentage = (weeklyTotalSchoolUseDuration / weeklyTotalOverallDuration) * 100;\n ctrl.weeklyAvgOutOfSchoolPercentage = (weeklyTotalOutOfSchoolDuration / weeklyTotalOverallDuration) * 100;\n } else {\n ctrl.weeklyAvgSchoolUsePercentage = 0;\n ctrl.weeklyAvgOutOfSchoolPercentage = 0;\n }\n };\n\n // Helper to generate a unique key for caching data based on user and date range\n const generateDataKey = (userId: string, dailyDate: string, startDate: string, endDate: string) => {\n return `${userId}_daily_${dailyDate}_weekly_${startDate}_${endDate}`;\n };\n\n // New function assigned to ctrl.fetchDataForCurrentUser to be called from HTML\n ctrl.fetchDataForCurrentUser = () => {\n ctrl.errorMessage = \"\";\n ctrl.hasError = false;\n const today = moment().format('YYYY-MM-DD');\n\n // Fetch initial daily data (today and yesterday only)\n fetchInitialDailyData(\n $http,\n ctrl,\n ctrl.selectedUser,\n today\n ).then((data) => {\n // Store the initial data with a special key\n const initialKey = `${ctrl.selectedUser}_initial_${today}`;\n ctrl.userData[initialKey] = { yesterdayTotal: data.yesterdayTotal, today: data.today };\n updateInitialSummary(data.today, data.yesterdayTotal); // Update the main summary values\n scope.$applyAsync();\n }).catch(error => {\n scope.$applyAsync();\n console.error(\"Error fetching initial daily data for current user:\", error);\n });\n };\n\n ctrl.fetchLightboxData = () => {\n const userIdForLightbox = ctrl.selectedChildHistogram || ctrl.selectedUser;\n\n if (!userIdForLightbox) return;\n\n const currentKey = generateDataKey(\n userIdForLightbox,\n ctrl.selectedDailyDate,\n ctrl.weekStart.format('YYYY-MM-DD'),\n ctrl.weekEnd.format('YYYY-MM-DD')\n );\n\n // Check if we already have the data we need for the current view mode\n const existingData = ctrl.userData[currentKey];\n if (ctrl.viewMode === \"weekly\" && existingData && existingData.weekly) {\n // We have weekly data, just update the chart\n setTimeout(() => ctrl.updateChart(), 50);\n scope.$applyAsync();\n return;\n }\n if (ctrl.viewMode === \"daily\" && existingData && existingData.daily) {\n // We have daily data, just update the chart\n setTimeout(() => ctrl.updateChart(), 50);\n scope.$applyAsync();\n return;\n }\n\n // Fetch only the data we need based on the current view mode\n if (ctrl.viewMode === \"weekly\") {\n // Fetch only weekly data\n const weeklyEndpoint = `/appregistry/screen-time/${userIdForLightbox}/weekly?startDate=${ctrl.weekStart.format('YYYY-MM-DD')}&endDate=${ctrl.weekEnd.format('YYYY-MM-DD')}`;\n \n $http.get(weeklyEndpoint).then((weeklyResponse: any) => {\n ctrl.hasError = false;\n ctrl.errorMessage = \"\";\n \n const weeklyData = weeklyResponse.data.dailySummaries.reduce((acc: any, day: any) => {\n acc[day.date] = {\n duration: day.durationMinutes,\n schoolUsePercentage: day.schoolUsePercentage / 100\n };\n return acc;\n }, {});\n\n // Update or create the data entry\n if (!ctrl.userData[currentKey]) {\n ctrl.userData[currentKey] = {};\n }\n ctrl.userData[currentKey].weekly = weeklyData;\n\n if (ctrl.showLightbox) {\n setTimeout(() => ctrl.updateChart(), 50);\n }\n scope.$applyAsync();\n }).catch(error => {\n ctrl.hasError = true;\n if (error?.status === 404) {\n ctrl.errorMessage = ctrl.lang.translate(\"screenTime.error.404\");\n } else {\n ctrl.errorMessage = ctrl.lang.translate(\"screenTime.error.generic\");\n }\n console.error(\"Error fetching weekly data for lightbox:\", error);\n scope.$applyAsync();\n });\n } else {\n // Fetch only daily data\n const dailyEndpoint = `/appregistry/screen-time/${userIdForLightbox}/daily?date=${ctrl.selectedDailyDate}`;\n \n $http.get(dailyEndpoint).then((dailyResponse: any) => {\n ctrl.hasError = false;\n ctrl.errorMessage = \"\";\n \n const dailyData = dailyResponse.data.durations.map((hour: any) => ({\n hour: hour.hour,\n duration: hour.durationMinutes,\n schoolUsePercentage: hour.schoolUsePercentage / 100\n }));\n\n // Update or create the data entry\n if (!ctrl.userData[currentKey]) {\n ctrl.userData[currentKey] = {};\n }\n ctrl.userData[currentKey].daily = dailyData;\n\n if (ctrl.showLightbox) {\n setTimeout(() => ctrl.updateChart(), 50);\n }\n scope.$applyAsync();\n }).catch(error => {\n ctrl.hasError = true;\n if (error?.status === 404) {\n ctrl.errorMessage = ctrl.lang.translate(\"screenTime.error.404\");\n } else {\n ctrl.errorMessage = ctrl.lang.translate(\"screenTime.error.generic\");\n }\n console.error(\"Error fetching daily data for lightbox:\", error);\n scope.$applyAsync();\n });\n }\n };\n\n ctrl.updateChart = () => {\n const canvas = elem[0].querySelector<HTMLCanvasElement>(\"#myChart\");\n if (!canvas) return;\n\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) return;\n\n let labels: string[] = [];\n let totalTimes: number[] = [];\n\n const userIdForChart = ctrl.selectedChildHistogram || ctrl.selectedUser;\n\n // Use the dates from the lightbox controls for the chart data key\n const currentDataKey = generateDataKey(\n userIdForChart,\n ctrl.selectedDailyDate,\n ctrl.weekStart.format('YYYY-MM-DD'),\n ctrl.weekEnd.format('YYYY-MM-DD')\n );\n const currentUserAndDateData = ctrl.userData[currentDataKey];\n\n if (!currentUserAndDateData) {\n // No data available, fetch it\n ctrl.fetchLightboxData();\n return;\n }\n\n const dataToUse: any = ctrl.viewMode === \"weekly\" ? currentUserAndDateData.weekly : currentUserAndDateData.daily;\n if (!dataToUse) {\n // Data not available for current view mode, fetch it\n ctrl.fetchLightboxData();\n return;\n }\n\n if (ctrl.viewMode === \"weekly\") {\n const sortedDates = Object.keys(dataToUse).sort();\n\n sortedDates.forEach(dataStr => {\n const entry = dataToUse[dataStr];\n labels.push(moment(dataStr).format('ddd D'));\n totalTimes.push(entry.duration / 60);\n })\n } else {\n dataToUse.forEach((hourData: any) => {\n labels.push(`${hourData.hour}h`);\n totalTimes.push(hourData.duration);\n });\n }\n\n // If the chart already exists, we simply update it\n if (chartInstance) {\n chartInstance.data.labels = labels;\n chartInstance.data.datasets[0].data = totalTimes;\n\n chartInstance.options = {\n plugins: {\n tooltip: {\n callbacks: {\n label: function (context) {\n const value = context.raw as number;\n if (ctrl.viewMode === \"weekly\") {\n // Convert decimal hours to hours and minutes\n const hours = Math.floor(value);\n const minutes = Math.round((value - hours) * 60);\n if (minutes === 0) {\n return `${context.dataset.label}: ${hours}h`;\n } else {\n return `${context.dataset.label}: ${hours}h ${minutes}m`;\n }\n } else {\n return `${context.dataset.label}: ${value} ${ctrl.lang.translate(\"screenTime.minutes\")}`;\n }\n }\n }\n }\n },\n scales: {\n x: {\n stacked: true\n },\n y: {\n stacked: true,\n beginAtZero: true,\n title: {\n display: true,\n text: ctrl.viewMode === \"weekly\" ? ctrl.lang.translate(\"screenTime.hours\") : ctrl.lang.translate(\"screenTime.minutes\"),\n }\n }\n }\n }\n\n chartInstance.update();\n } else {\n chartInstance = new Chart(ctx, {\n type: \"bar\",\n data: {\n labels,\n datasets: [\n {\n label: ctrl.lang.translate(\"screenTime.totalUsage\"),\n data: totalTimes,\n backgroundColor: \"#2A9CC8\"\n }\n ]\n },\n options: {\n plugins: {\n tooltip: {\n callbacks: {\n label: function (context) {\n const value = context.raw as number;\n if (ctrl.viewMode === \"weekly\") {\n // Convert decimal hours to hours and minutes\n const hours = Math.floor(value);\n const minutes = Math.round((value - hours) * 60);\n if (minutes === 0) {\n return `${context.dataset.label}: ${hours}h`;\n } else {\n return `${context.dataset.label}: ${hours}h ${minutes}m`;\n }\n } else {\n return `${context.dataset.label}: ${value} ${ctrl.lang.translate(\"screenTime.minutes\")}`;\n }\n }\n }\n },\n legend: {\n display: false,\n position: \"bottom\"\n }\n },\n responsive: true,\n scales: {\n x: {\n stacked: true\n },\n y: {\n stacked: true,\n beginAtZero: true,\n title: {\n display: true,\n text: ctrl.viewMode === \"weekly\" ? ctrl.lang.translate(\"screenTime.hours\") : ctrl.lang.translate(\"screenTime.minutes\"),\n }\n }\n }\n }\n });\n }\n };\n\n ctrl.children = fetchChildren(ctrl);\n\n // Determine the user whose data should be fetched on load\n if (ctrl.isParent && ctrl.children.length > 0) {\n // If a parent, and has children, default to the first child if no child is selected yet\n if (!ctrl.selectedUser) {\n ctrl.selectedUser = ctrl.children[0].userId;\n }\n ctrl.fetchDataForCurrentUser();\n } else if (!ctrl.isParent && currentUserId) {\n // If not a parent, and current user ID is available, fetch data for themselves\n ctrl.selectedUser = currentUserId; // Set selectedUser to the current logged-in user's ID\n ctrl.fetchDataForCurrentUser();\n } else {\n // No children AND no current user ID (or other error condition)\n ctrl.hasError = true;\n ctrl.errorMessage = ctrl.lang.translate(\"screenTime.error.noUser\");\n scope.$applyAsync(); // Ensure the error message is displayed\n }\n\n // Watch for changes in view mode (within lightbox): only updates the chart\n scope.$watch(() => ctrl.viewMode, (newVal, oldVal) => {\n if (newVal !== oldVal && ctrl.showLightbox) {\n setTimeout(() => ctrl.updateChart(), 50);\n }\n });\n // Watch for selectedUser changes within the lightbox to update chart data\n scope.$watch(() => ctrl.selectedChildHistogram, (newVal, oldVal) => { // Changed from ctrl.selectedUser\n if (newVal !== oldVal && ctrl.showLightbox) {\n ctrl.fetchLightboxData();\n }\n });\n\n // Watch for lightbox visibility changes\n scope.$watch(() => ctrl.showLightbox, (isVisible: boolean) => {\n if (isVisible) {\n // When lightbox opens:\n // 1. Set selectedChildHistogram. This will trigger the selectedChildHistogram watch,\n // which will then call fetchLightboxData() (the single desired API call).\n ctrl.selectedChildHistogram = ctrl.selectedUser;\n\n // 2. Schedule chart update after a short delay, allowing fetchLightboxData() to begin.\n setTimeout(() => {\n ctrl.updateChart();\n }, 100);\n } else {\n // When lightbox closes\n if (chartInstance) {\n chartInstance.destroy();\n chartInstance = null;\n }\n }\n });\n\n }\n}\n\n// Factory function for the directive\nfunction DirectiveFactory() {\n return new Directive();\n}\n\n// Define the module name\nexport const odeModuleName = \"odeCantineWidgetModule\";\n\n// Angular module definition\nangular\n .module(odeModuleName, [])\n // Define the custom 'duration' filter\n .filter('duration', function () {\n return function (input: number) {\n if (isNaN(input) || input === null || input < 0) {\n return '0m';\n }\n\n const totalMinutes = Math.floor(input);\n const hours = Math.floor(totalMinutes / 60);\n const minutes = totalMinutes % 60;\n\n let result = '';\n\n if (hours > 0) {\n result += `${hours}h`;\n }\n if (minutes > 0 || (hours === 0 && totalMinutes === 0)) {\n result += `${minutes}m`;\n }\n\n return result.trim() || '0m';\n };\n })\n\n // Register the directive\n .directive(\"odeScreenTimeWidget\", DirectiveFactory);\n\nfunction setupMomentLocale(lang: string) {\n try {\n if (lang === \"en\") {\n require('moment/locale/en-gb');\n moment.locale('en-gb');\n } else {\n require('moment/locale/fr');\n moment.locale('fr');\n }\n } catch (error) {\n console.warn('Failed to load moment locale:', error);\n moment.locale('fr');\n }\n}\n\n// Internationalization setup\nnotif()\n .onLangReady()\n .promise.then((lang) => {\n setupMomentLocale(lang);\n\n switch (lang) {\n case \"en\":\n conf().Platform.idiom.addKeys(require(\"./i18n/en.json\"));\n break;\n default:\n conf().Platform.idiom.addKeys(require(\"./i18n/fr.json\"));\n break;\n }\n });\n"],"names":["Chart","register","registerables","locale","platform","conf","Platform","lang","this","idiom","selectedUser","viewMode","userData","selectedDailyDate","format","tempDailyDate","weekStart","startOf","weekEnd","endOf","updateChart","todayOnCampus","todayOffCampus","todayTotal","yesterdayTotal","weeklyAvgOnCampus","weeklyAvgOffCampus","weeklyTotalAverage","weeklyAvgSchoolUsePercentage","weeklyAvgOutOfSchoolPercentage","fixedTodayOnCampus","fixedTodayOffCampus","fixedTodayTotal","fixedWeeklyAvgOnCampus","fixedWeeklyAvgOffCampus","fixedWeeklyTotalAverage","fixedWeeklyAvgSchoolUsePercentage","fixedWeeklyAvgOutOfSchoolPercentage","isParent","hasError","errorMessage","fetchDataForCurrentUser","fetchLightboxData","showLightbox","showDatePicker","children","selectedChildHistogram","toggleDatePicker","show","setTimeout","dateInput","document","querySelector","value","setDateFromPicker","date","newDate","setSelectedDate","commitTempDate","isValid","tempDateHandleKeyPress","event","key","toggleLightbox","getCurrentSchoolYear","today","currentYear","year","schoolYearStart","month","day","schoolYearEnd","clone","add","subtract","momentDate","setSelectedWeek","canGoToPreviousWeek","offsetWeekStart","isBetween","canGoToNextWeek","canGoToPreviousDay","offsetDayStart","canGoToNextDay","changeDay","offset","changeWeek","newWeekStart","start","end","fromText","translate","toText","fetchChildren","ctrl","childrenObj","session","user","Object","keys","length","entries","map","userId","child","id","name","firstName","lastName","Controller","restrict","template","scope","bindToController","controller","controllerAs","require","link","elem","attrs","controllers","$http","injector","get","currentUserId","chartInstance","generateDataKey","dailyDate","startDate","endDate","todayDate","yesterdayDate","todayEndpoint","yesterdayEndpoint","Promise","all","then","yesterdayResponse","todayResponse","yesterdayTotalDuration","data","durations","reduce","total","hour","durationMinutes","todayData","duration","schoolUsePercentage","catch","error","status","console","reject","fetchInitialDailyData","todayOnCampusTemp","todayOffCampusTemp","initialKey","forEach","hourData","schoolUsePct","$applyAsync","userIdForLightbox","currentKey","existingData","weekly","daily","weeklyEndpoint","weeklyResponse","weeklyData","dailySummaries","acc","dailyEndpoint","dailyResponse","dailyData","canvas","ctx","getContext","labels","totalTimes","userIdForChart","currentDataKey","currentUserAndDateData","dataToUse","sort","dataStr","entry","push","datasets","options","plugins","tooltip","callbacks","label","context","raw","hours","Math","floor","minutes","round","dataset","scales","x","stacked","y","beginAtZero","title","display","text","update","type","backgroundColor","legend","position","responsive","$watch","newVal","oldVal","isVisible","destroy","odeModuleName","module","filter","input","isNaN","totalMinutes","result","trim","directive","Directive","notif","onLangReady","promise","warn","setupMomentLocale","addKeys"],"sourceRoot":""}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ode-ngjs-front",
3
- "version": "1.4.16-develop-integration.202510212224",
3
+ "version": "1.4.16-develop-integration.202510221210",
4
4
  "description": "Open Digital Education Frontend Framework",
5
5
  "homepage": "https://github.com/opendigitaleducation/ode-ngjs-front#readme",
6
6
  "bugs": {