//////////////////////////////////////////////////////////////////////
// Copyright (C) SUGIMURA Lab. 2021.11.05
// HAL 表示関係の処理
//////////////////////////////////////////////////////////////////////
'use strict'
////////////////////////////////////////////////////////////////////////////////
// HTMLロードしたら準備
/**
* @namespace subHAL
*/
window.addEventListener('DOMContentLoaded', function () {
console.dir('## DOMContentLoaded subHAL.js');
// 内部変数
let profile = { name: 'No Profile', UID: 'No Data', sex: 'No Data', age: 'No Data' };
let majorResults = {};
let minorResults = {};
let minorkeyMeans = {};
// HTML内部とリンク
let divHALArea = document.getElementById('divHALArea');
let divId = document.getElementById("divId");
let divDatetime = document.getElementById("divDatetime");
let divTotalPoint = document.getElementById("divTotalPoint");
let divTotalRank = document.getElementById("divTotalRank");
let divSLI = document.getElementById("divSLI");
let divClothing = document.getElementById("divClothing");
let divFood = document.getElementById("divFood");
let divHousing = document.getElementById("divHousing");
let divPhysicalHealth = document.getElementById("divPhysicalHealth");
let divMentalHealth = document.getElementById("divMentalHealth");
let divEcology = document.getElementById("divEcology");
let divClothingRatio = document.getElementById('divClothingRatio');
let divFoodRatio = document.getElementById('divFoodRatio');
let divHousingRatio = document.getElementById('divHousingRatio');
let divPhysicalRatio = document.getElementById('divPhysicalRatio');
let divMentalRatio = document.getElementById('divMentalRatio');
let divEcologyRatio = document.getElementById('divEcologyRatio');
let divHALPoint = document.getElementById('divHALPoint');
let divComment = document.getElementById("divComment");
let imgKadecot = document.getElementById("imgKadecot");
let imgSmartilab = document.getElementById("imgSmartilab");
let imgSugilab = document.getElementById("imgSugilab");
let canMajorRaderChart = document.getElementById("canMajorRaderChart");
let majorRaderChart = null;
// 詳細
let canClothingBarChart = document.getElementById("canClothingBarChart");
let clothingBarCart = null;
let canFoodBarChart = document.getElementById("canFoodBarChart");
let foodBarChart = null;
let canHousingBarChart = document.getElementById("canHousingBarChart");
let housingBarChart = null;
let canPhysicalBarChart = document.getElementById("canPhysicalBarChart");
let physicalBarChart = null;
let canMentalBarChart = document.getElementById("canMentalBarChart");
let mentalBarChart = null;
let canEcologyBarChart = document.getElementById("canEcologyBarChart");
let ecologyBarChart = null;
// config
let inHALApiKey = document.getElementById('inHALApiKey'); // HALApiKey
let spanHALinfo = document.getElementById('spanHALinfo'); // tokenが登録されているとき
let spanHALsuggenst = document.getElementById('spanHALsuggenst'); // token未登録のとき
let pSetHalApiTokenErr = document.getElementById('pSetHalApiTokenErr'); // API登録エラー表示
let inUserNickname = document.getElementById('inUserNickname'); // ニックネーム
let inUserAge = document.getElementById('inUserAge'); // 年齢
let inUserHeight = document.getElementById('inUserHeight'); // 身長
let inUserWeight = document.getElementById('inUserWeight'); // 体重
let btnSetHalApiToken = document.getElementById('btnSetHalApiToken'); // API Key登録ボタン
let btnHALSync = document.getElementById('btnHALSync'); // HAL同期ボタン
/**
* @func renewMajorResults
* @desc 内部関数
* @memberof subHAL
* @param {void}
* @return {void}
*/
let renewMajorResults = function () {
if (!majorResults) return;
let clothingRatio = ranking(majorResults.clothingPoint);
let foodRatio = ranking(majorResults.foodPoint);
let housingRatio = ranking(majorResults.housingPoint);
let physicalRatio = ranking(majorResults.physicalHealthPoint);
let mentalRatio = ranking(majorResults.mentalHealthPoint);
let ecologyRatio = ranking(majorResults.ecologyPoint);
if (majorRaderChart) { majorRaderChart.destroy(); } // chartがすでにctxを使っていると、リエントラントで"Canvas is already in use."のエラーが出る
majorRaderChart = new Chart(canMajorRaderChart, {
type: 'radar',
data: {
labels: ["衣服・身だしなみ", "食事", "住居", "体の健康", "心の健康", "エコ度"],
datasets: [{
label: '今日のあなた',
data: [majorResults.clothingPoint, majorResults.foodPoint, majorResults.housingPoint,
majorResults.physicalHealthPoint, majorResults.mentalHealthPoint, majorResults.ecologyPoint],
backgroundColor: 'RGBA(225,95,150, 0.5)',
borderColor: 'RGBA(225,95,150, 1)',
borderWidth: 1,
pointBackgroundColor: 'RGB(46,106,177)'
}]
},
options: {
responsive: true,
title: {
display: true,
text: 'あなたの生活評価バランス',
fontSize: '30'
},
scale: {
r: {
suggestedMax: 100,
suggestedMin: 0,
beginAtZero: true
}
}
}
});
divId.innerHTML = profile.name;
divDatetime.innerHTML = new Date(majorResults.updatedAt).toLocaleString();
divTotalPoint.innerHTML = Math.round(majorResults.totalPoint * 10) / 10;
divHALPoint.innerHTML = Math.round(majorResults.totalPoint * 10) / 10;
divTotalRank.innerHTML = majorResults.totalRank;
divSLI.innerHTML = majorResults.smartLifeIndex;
divClothing.innerHTML = Math.round(majorResults.clothingPoint * 10) / 10;
divFood.innerHTML = Math.round(majorResults.foodPoint * 10) / 10;
divHousing.innerHTML = Math.round(majorResults.housingPoint * 10) / 10;
divPhysicalHealth.innerHTML = Math.round(majorResults.physicalHealthPoint * 10) / 10;
divMentalHealth.innerHTML = Math.round(majorResults.mentalHealthPoint * 10) / 10;
divEcology.innerHTML = Math.round(majorResults.ecologyPoint * 10) / 10;
divClothingRatio.innerHTML = clothingRatio;
divFoodRatio.innerHTML = foodRatio;
divHousingRatio.innerHTML = housingRatio;
divPhysicalRatio.innerHTML = physicalRatio;
divMentalRatio.innerHTML = mentalRatio;
divEcologyRatio.innerHTML = ecologyRatio;
divComment.innerHTML = majorResults.comments ? majorResults.comments : "この調子で頑張ろう!";
};
/**
* @func renewMinorResults
* @memberof subHAL
* @desc 内部関数
* @param {void}
* @return {void}
*/
let renewMinorResults = function () {
if (!minorResults) return;
// 両方揃わないでグラフを作るとChart.jsがバグる
if (Object.keys(minorResults).length == 0) return;
if (Object.keys(minorkeyMeans).length == 0) return;
let clothingLabels = [];
let clothingData = [];
let foodLabels = [];
let foodData = [];
let housingLabels = [];
let housingData = [];
let physicalLabels = [];
let physicalData = [];
let mentalLabels = [];
let mentalData = [];
let ecologyLabels = [];
let ecologyData = [];
Object.keys(minorkeyMeans).forEach(function (key) {
let d = minorkeyMeans[key];
switch (d.majorKey) {
case 1:
clothingLabels[d.minorKey - 1] = d.means;
break;
case 2:
foodLabels[d.minorKey - 1] = d.means;
break;
case 3:
housingLabels[d.minorKey - 1] = d.means;
break;
case 4:
physicalLabels[d.minorKey - 1] = d.means;
break;
case 5:
mentalLabels[d.minorKey - 1] = d.means;
break;
case 6:
ecologyLabels[d.minorKey - 1] = d.means;
break;
}
});
Object.keys(minorkeyMeans).forEach(function (key) {
let d = minorkeyMeans[key];
switch (d.majorKey) {
case 1:
clothingData[d.minorKey - 1] = minorResults['r_' + d.majorKey + '_' + d.minorKey];
break;
case 2:
foodData[d.minorKey - 1] = minorResults['r_' + d.majorKey + '_' + d.minorKey];
break;
case 3:
housingData[d.minorKey - 1] = minorResults['r_' + d.majorKey + '_' + d.minorKey];
break;
case 4:
physicalData[d.minorKey - 1] = minorResults['r_' + d.majorKey + '_' + d.minorKey];
break;
case 5:
mentalData[d.minorKey - 1] = minorResults['r_' + d.majorKey + '_' + d.minorKey];
break;
case 6:
ecologyData[d.minorKey - 1] = minorResults['r_' + d.majorKey + '_' + d.minorKey];
break;
}
});
// 衣類
if (clothingBarCart) { clothingBarCart.destroy(); }
clothingBarCart = new Chart(canClothingBarChart, {
type: 'bar',
data: {
labels: clothingLabels,
datasets: [
{
label: 'あなたの衣服・身だしなみの点数',
data: clothingData,
backgroundColor: "#dda0dd"
}
]
},
options: {
title: {
display: true,
text: '衣服・身だしなみ'
},
scales: {
y: {
suggestedMax: 100,
suggestedMin: 0
}
}
}
});
// 食
if (foodBarChart) { foodBarChart.destroy(); }
foodBarChart = new Chart(canFoodBarChart, {
type: 'bar',
data: {
labels: foodLabels,
datasets: [
{
label: 'あなたの食事の点数',
data: foodData,
backgroundColor: "#ffa500"
}
]
},
options: {
title: {
display: true,
text: '食事'
},
scales: {
y: {
suggestedMax: 100,
suggestedMin: 0
}
}
}
});
// 住居
if (housingBarChart) { housingBarChart.destroy(); }
housingBarChart = new Chart(canHousingBarChart, {
type: 'bar',
data: {
labels: housingLabels,
datasets: [
{
label: 'あなたの住居の点数',
data: housingData,
backgroundColor: "#6495ed"
}
]
},
options: {
title: {
display: true,
text: '住居'
},
scales: {
y: {
suggestedMax: 100,
suggestedMin: 0
}
}
}
});
// 体
if (physicalBarChart) { physicalBarChart.destroy(); }
physicalBarChart = new Chart(canPhysicalBarChart, {
type: 'bar',
data: {
labels: physicalLabels,
datasets: [
{
label: 'あなたの体の健康の点数',
data: physicalData,
backgroundColor: "#9acd32"
}
]
},
options: {
title: {
display: true,
text: '体の健康'
},
scales: {
y: {
suggestedMax: 100,
suggestedMin: 0
}
}
}
});
// 心
if (mentalBarChart) { mentalBarChart.destroy(); }
mentalBarChart = new Chart(canMentalBarChart, {
type: 'bar',
data: {
labels: mentalLabels,
datasets: [
{
label: 'あなたの心の健康の点数',
data: mentalData,
backgroundColor: "#ffc0cb"
}
]
},
options: {
title: {
display: true,
text: '心の健康'
},
scales: {
y: {
suggestedMax: 100,
suggestedMin: 0
}
}
}
});
// エコ
if (ecologyBarChart) { ecologyBarChart.destroy(); }
ecologyBarChart = new Chart(canEcologyBarChart, {
type: 'bar',
data: {
labels: ecologyLabels,
datasets: [
{
label: 'あなたのエコ度の点数',
data: ecologyData,
backgroundColor: "#c0c0c0"
}
]
},
options: {
title: {
display: true,
text: 'エコ度'
},
scales: {
y: {
suggestedMax: 100,
suggestedMin: 0
}
}
}
});
};
/**
* @func ranking
* @memberof subHAL
* @desc 内部関数,ランク付け
* @param {void}
* @return {void}
*/
let ranking = function (point) {
return point >= 90 ? 'SSS'
: point >= 80 ? 'SS'
: point >= 70 ? 'S'
: point >= 60 ? 'A'
: point >= 50 ? 'B'
: point >= 40 ? 'C'
: point >= 30 ? 'D'
: point >= 20 ? 'E'
: 'F';
};
/**
* @func window.renewHALProfile
* @desc Profileもらって画面更新
* @param {void}
* @return {void}
*/
window.renewHALProfile = function (_profile) {
profile = _profile;
divId.innerHTML = profile.name;
// HALのデータを優先し、read onlyにする
inUserNickname.value = profile.name;
inUserNickname.readOnly = true;
inUserAge.value = profile.age;
inUserAge.readOnly = true;
// inUserHeight.value = profile.height;
// inUserWeight.value = profile.weight;
};
////////////////////////////////////////////////////////////////////////////////
// GUIのボタン
/**
* @func window.btnDeleteHalApiToken_Click
* @desc HAL API トークン設定削除ボタンが押されたときの処理
* @param {void}
* @return {void}
*/
window.btnDeleteHalApiToken_Click = function () {
window.ipc.HALdeleteApiToken();
};
// HAL API Key登録ボタンクリック
let timer; // HALからの応答待ち、タイムアウトタイマー
/**
* @func window.btnSetHalApiTokenBtn_Click
* @desc window.btnSetHalApiTokenBtn_Click
* @param {void}
* @return {void}
*/
window.btnSetHalApiTokenBtn_Click = function () {
pSetHalApiTokenErr.textContent = '';
btnSetHalApiToken.disabled = true;
let HALtoken = inHALApiKey.value;
let err = '';
if (!HALtoken) {
err = 'API トークンを入力してください。';
} else if (!/^[\x21-\x7e]+$/.test(HALtoken)) {
err = 'API トークンに不適切な文字が含まれています。';
}
if (err) {
pSetHalApiTokenErr.textContent = err;
btnSetHalApiToken.disabled = false;
return;
}
let HAL_REQUEST_TIMEOUT = 5000;
timer = setTimeout(() => {
pSetHalApiTokenErr.textContent = 'TIMEOUT: HAL の応答がありませんでした。';
btnSetHalApiToken.disabled = false;
}, HAL_REQUEST_TIMEOUT);
window.ipc.HALsetApiTokenRequest(HALtoken);
};
/**
* @func window.btnHALsync_Click
* @desc HAL同期ボタンが押されたときの処理
* @param {void}
* @return {void}
*/
window.btnHALsync_Click = function () {
btnHALSync.disabled = true;
btnHALSync.textContent = 'HAL Cloud 同期中…';
window.ipc.HALSyncRequeset();
};
////////////////////////////////////////////////////////////////////////////////
// mainプロセスから呼ばれるやつ
//----------------------------------------------------------------
/**
* @func window.renewHALConfigView
* @desc configデータをもらって画面更新
* @param {void}
* @return {void}
*/
window.renewHALConfigView = function (config) {
// 取得したトークンが有効かどうかを確認するために HAL ユーザープロファイルを取得
if (config.halApiToken) {
inHALApiKey.value = config.halApiToken;
window.ipc.HALgetUserProfileRequest();
} else {
inHALApiKey.value = ""; // undefined がテキストボックスに表示されないように
}
};
/**
* @func window.renewHALToken
* @desc HAL Tokenに変更があったら呼ばれる
* @param {void}
* @return {void}
*/
window.renewHALToken = async function (HALtoken) {
console.log('renewHALToken(): HALtoken:', HALtoken);
// 取得したトークンが有効かどうかを確認するために HAL ユーザープロファイルを取得
if (HALtoken) {
inHALApiKey.value = HALtoken;
window.ipc.HALgetUserProfileRequest();
} else {
spanHALinfo.style.display = 'none'; // 同期済み情報表示
spanHALsuggenst.style.display = 'block'; // 同期済み情報表示
inHALApiKey.value = ""; // undefined がテキストボックスに表示されないように
}
};
//----------------------------------------------------------------
/**
* @func window.HALRedraw
* @desc データをもらって画面更新
* @param {void}
* @return {void}
*/
window.HALRedraw = function (MajorResults, MinorResults, MinorkeyMeans) {
majorResults = MajorResults;
renewMajorResults();
minorResults = MinorResults;
renewMinorResults();
minorkeyMeans = MinorkeyMeans;
renewMinorResults();
};
//----------------------------------------------------------------
/**
* @func window.HALSyncResponse
* @desc HAL cloud: 同期の応答、同期処理終了
* @param arg {arg.error} errorがあるときだけ入ってる予定、成功は空オブジェクト
* @return {void}
*/
window.HALSyncResponse = function (arg) {
if (arg?.error) {
alert(arg.error);
} else {
btnHALSync.disabled = false;
btnHALSync.textContent = 'HAL Cloud 同期開始';
window.ipc.HALrenew(); // 同期成功したなら最新のHALもらう
}
}
//----------------------------------------------------------------
/**
* @func window.HALsetApiTokenResponse
* @desc HAL API登録完了したら呼ばれる
* @param {void}
* @return {void}
*/
window.HALsetApiTokenResponse = function (res) {
console.log('window.HALsetApiTokenResponse() res:', res);
if (timer) {
clearTimeout(timer);
}
if (res.error) {
pSetHalApiTokenErr.textContent = res.error;
} else {
btnHALSync.style.display = 'block'; // 同期ボタン表示
spanHALinfo.style.display = 'block'; // 同期済み情報表示
spanHALsuggenst.style.display = 'none'; // 同期済み情報表示
window.addToast('Info', 'HAL 連携が成功しました。');
window.renewHALToken(inHALApiKey.value);
// configSave();
// 同期もする
btnHALSync.disabled = true;
btnHALSync.textContent = 'HAL Cloud 同期中…';
window.ipc.HALSyncRequeset();
}
};
//----------------------------------------------------------------
/**
* @func window.HALdeleteApiTokenResponse
* @desc HAL API トークン設定削除の応答、HALとの同期をやめた場合、mainから応答があって実行
* @param {void}
* @return {void}
*/
window.HALdeleteApiTokenResponse = function () {
console.log('window.HALdeleteApiTokenResponse()');
divHALArea.style.display = 'none'; // 同期ボタン非表示
window.addToast('Info', 'HAL 連携設定を削除しました。');
window.renewHALToken(null);
};
//----------------------------------------------------------------
/**
* @func window.HALgetUserProfileResponse
* @desc HAL ユーザープロファイル取得の応答
* @param {void}
* @return {void}
*/
window.HALgetUserProfileResponse = function (res) {
// console.log('window.HALgetUserProfileResponse() res:', res);
// 取得したトークンが有効かどうかを確認するために HAL ユーザープロファイルを取得
if (inHALApiKey.value) {
try {
window.renewHALProfile(res.profile);
} catch (error) {
console.error(error);
pSetHalApiTokenErr.textContent = error.message;
}
} else {
inHALApiKey.value = ""; // undefined がテキストボックスに表示されないように
}
if (inHALApiKey.value && inHALApiKey.value != 'null' && res.profile) { // API OK
divHALArea.style.display = 'block'; // 同期ボタン表示
spanHALinfo.style.display = 'block';
spanHALsuggenst.style.display = 'none';
} else {
divHALArea.style.display = 'none'; // 同期ボタン表示
spanHALinfo.style.display = 'none';
spanHALsuggenst.style.display = 'block';
}
};
//================================================================
/**
* @func btnQuestionnaireSubmit_click
* @memberof subHAL
* @desc local HAL, アンケート回答の投稿ボタンを押したときの処理
* @param {void}
* @return {void}
*/
btnQuestionnaireSubmit.addEventListener('click', function () {
let submitData = window.getQuestionnaire();
if (submitData != null) {
// HAL にアンケート回答が POST される。
window.ipc.HALsubmitQuestionnaire(submitData);
}
});
});