Translate all Chinese comments and strings to English
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -47,9 +47,9 @@ export const useAcctMgmtStore = defineStore('acctMgmtStore', {
|
||||
response: {
|
||||
deleteAccount: null,
|
||||
},
|
||||
isOneAccountJustCreate: false, //如果有一個帳號剛剛建立,則會在列表上的第一列顯示這個帳號,並且右置一個徽章
|
||||
isOneAccountJustCreate: false, // If an account was just created, display it at the top of the list with a badge
|
||||
justCreateUsername: '', // unique username
|
||||
shouldUpdateList: false, // 控制是否該刷新列表
|
||||
shouldUpdateList: false, // Controls whether the list should be refreshed
|
||||
}),
|
||||
getters: {},
|
||||
actions: {
|
||||
@@ -113,9 +113,9 @@ export const useAcctMgmtStore = defineStore('acctMgmtStore', {
|
||||
await loginStore.getUserData();
|
||||
const loginUserData:User = loginStore.userData;
|
||||
return rawResponseData.map(user => ({
|
||||
...user, // 保留後端傳來的欄位
|
||||
...user, // Preserve fields from the backend response
|
||||
isCurrentLoggedIn: loginUserData.username === user.username,
|
||||
isDeleteHovered: false, // 針對前端顯示而額外增加的欄位
|
||||
isDeleteHovered: false, // Additional fields for frontend display state
|
||||
isRowHovered: false,
|
||||
isEditHovered: false,
|
||||
}));
|
||||
@@ -267,7 +267,7 @@ export const useAcctMgmtStore = defineStore('acctMgmtStore', {
|
||||
this.currentViewingUser.is_admin = response.data.roles.some(role => role.code === 'admin');
|
||||
return response.status === 200;
|
||||
} catch (error) {
|
||||
//不需要跳出錯誤,因為如果是錯誤反而是好事,表示帳號是獨一的
|
||||
// No need to show an error, because an error here means the account is unique
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
@@ -55,17 +55,17 @@ export const useAllMapDataStore = defineStore('allMapDataStore', {
|
||||
allFilterTimeframe: {},
|
||||
allFilterTrace: [],
|
||||
allFilterAttrs: [],
|
||||
hasResultRule: null, // click Apply 後檢查是否有 Data
|
||||
temporaryData: [], // 沒被 apply all 的 Data
|
||||
postRuleData: [], // has-result API & temp-filters API 的 Data
|
||||
hasResultRule: null, // Whether any data remains after clicking Apply
|
||||
temporaryData: [], // Data not yet applied via Apply All
|
||||
postRuleData: [], // Data for the has-result API and temp-filters API
|
||||
ruleData: [], // Funnle view's data
|
||||
isRuleData: [], // toggle button data
|
||||
allFunnelData: [],
|
||||
isUpdateFilter: false, // 是否成功儲存 Filter 檔
|
||||
isUpdateFilter: false, // Whether the filter file was saved successfully
|
||||
selectTimeFrame: [], // user select time start and end
|
||||
infinite404: null, // 無限滾動式是否到底要換頁
|
||||
infiniteStart: 0, // 無限滾動 case 開始的數字
|
||||
baseInfiniteStart: 0, // 無限滾動 case 開始的數字
|
||||
infinite404: null, // Whether infinite scroll has reached the last page
|
||||
infiniteStart: 0, // Starting index for infinite scroll cases
|
||||
baseInfiniteStart: 0, // Starting index for base infinite scroll cases
|
||||
}),
|
||||
getters: {
|
||||
processMap: state => {
|
||||
@@ -273,10 +273,10 @@ export const useAllMapDataStore = defineStore('allMapDataStore', {
|
||||
|
||||
const min = this.allFilterTimeframe.x_axis.min;
|
||||
const max = this.allFilterTimeframe.x_axis.max;
|
||||
// 給 Chart.js 原始資料,格式不同的畫會錯誤輸出
|
||||
// Preserve raw data for Chart.js; incorrect format causes wrong output
|
||||
this.allFilterTimeframe.x_axis.min_base = min;
|
||||
this.allFilterTimeframe.x_axis.max_base = max;
|
||||
// 轉成無秒的時間格式
|
||||
// Convert to a time format without seconds
|
||||
this.allFilterTimeframe.x_axis.min = min !== null ? moment(min).format('YYYY/MM/DD HH:mm') : null;
|
||||
this.allFilterTimeframe.x_axis.max = max !== null ? moment(max).format('YYYY/MM/DD HH:mm') : null;
|
||||
} catch(error) {
|
||||
|
||||
@@ -29,7 +29,7 @@ export const useCompareStore = defineStore('compareStore', {
|
||||
* @param { array } queryParams
|
||||
*/
|
||||
async getCompare(queryParams) {
|
||||
// encodeURIComponent 函數用於將字串中的特殊字符進行編碼,以確保 URL 的正確性。
|
||||
// encodeURIComponent encodes special characters in the string to ensure URL correctness.
|
||||
const queryString = JSON.stringify(queryParams);
|
||||
const api = `/api/compare?datasets=${encodeURIComponent(queryString)}`;
|
||||
|
||||
|
||||
@@ -49,12 +49,12 @@ function getFileTypeApi(state) {
|
||||
/** Pinia store for conformance checking and rule management. */
|
||||
export const useConformanceStore = defineStore('conformanceStore', {
|
||||
state: () => ({
|
||||
conformanceLogId: null, // log 檔
|
||||
conformanceFilterId: null, // filter 檔
|
||||
conformanceLogTempCheckId: null, // log 檔存檔前的 check Id
|
||||
conformanceFilterTempCheckId: null, // Filter 檔存檔前的 check Id
|
||||
conformanceLogCreateCheckId: null, // log 檔存檔後的 check Id(rule)
|
||||
conformanceFilterCreateCheckId: null, // Filter 檔存檔後的 check Id(rule)
|
||||
conformanceLogId: null, // Log file
|
||||
conformanceFilterId: null, // Filter file
|
||||
conformanceLogTempCheckId: null, // Temporary check ID for log (before saving)
|
||||
conformanceFilterTempCheckId: null, // Temporary check ID for filter (before saving)
|
||||
conformanceLogCreateCheckId: null, // Created check ID for log (rule, after saving)
|
||||
conformanceFilterCreateCheckId: null, // Created check ID for filter (rule, after saving)
|
||||
allConformanceTask: [],
|
||||
allCfmSeqStart: [],
|
||||
allCfmSeqEnd: [],
|
||||
@@ -76,11 +76,11 @@ export const useConformanceStore = defineStore('conformanceStore', {
|
||||
selectedActSeqMore: 'All', // radio
|
||||
selectedActSeqFromTo: 'From', // radio
|
||||
infinite404: null,
|
||||
isStartSelected: null, // start & end 連動先選擇 start
|
||||
isEndSelected: null, // start & end 連動先選擇 end
|
||||
isStartSelected: null, // Whether start is selected in linked start & end selection
|
||||
isEndSelected: null, // Whether end is selected in linked start & end selection
|
||||
conformanceRuleData: null, // create checkId's data to save
|
||||
isUpdateConformance: false, // 成功儲存後要跳 Modal
|
||||
conformanceFileName: null, // 儲存成功的 Modal 用
|
||||
isUpdateConformance: false, // Show modal after successful save
|
||||
conformanceFileName: null, // File name displayed in the save success modal
|
||||
}),
|
||||
getters: {
|
||||
conformanceAllTasks: state => {
|
||||
|
||||
@@ -20,7 +20,7 @@ import moment from 'moment';
|
||||
export const useConformanceInputStore = defineStore('conformanceInputStore', {
|
||||
state: () => ({
|
||||
inputDataToSave: {
|
||||
inputStart: null, // 有待釐清,start 是活動的開始,還是時間的開始?
|
||||
inputStart: null, // TODO: clarify whether "start" means activity start or time start
|
||||
inputEnd: null,
|
||||
min: null,
|
||||
max: null,
|
||||
|
||||
@@ -51,7 +51,7 @@ export const useCytoscapeStore = defineStore('cytoscapeStore', {
|
||||
if (!this.nodePositions[this.currentGraphId][direction]) {
|
||||
this.nodePositions[this.currentGraphId][direction] = [];
|
||||
}
|
||||
// 若是資訊曾經存在這張圖於localStorage中
|
||||
// If this graph's data was previously stored in localStorage
|
||||
if (localStorage.getItem(SAVE_KEY_NAME)) {
|
||||
const nodeToSave = this.nodePositions[this.currentGraphId][direction]
|
||||
.find(node => node.id === nodeId);
|
||||
|
||||
@@ -147,13 +147,13 @@ export const useFilesStore = defineStore('filesStore', {
|
||||
},
|
||||
};
|
||||
|
||||
uploadloader(); // 進度條
|
||||
uploadloader(); // Show loading progress bar
|
||||
try {
|
||||
const response = await apiClient.post(api, fromData, config);
|
||||
|
||||
this.uploadId = response.data.id;
|
||||
this.$router.push({name: 'Upload'});
|
||||
Swal.close(); // 關閉進度條
|
||||
Swal.close(); // Close the loading progress bar
|
||||
} catch(error) {
|
||||
if(error.response?.status === 422) {
|
||||
// msg: 'not in UTF-8' | 'insufficient columns' | 'the csv file is empty' | 'the filename does not ends with .csv'
|
||||
@@ -162,7 +162,7 @@ export const useFilesStore = defineStore('filesStore', {
|
||||
|
||||
uploadFailedFirst(detail[0].type, detail[0].msg, detail[0].loc[2]);
|
||||
} else {
|
||||
Swal.close(); // 關閉進度條
|
||||
Swal.close(); // Close the loading progress bar
|
||||
apiError(error, 'Failed to upload the files.');
|
||||
}
|
||||
}
|
||||
@@ -189,13 +189,13 @@ export const useFilesStore = defineStore('filesStore', {
|
||||
const uploadId = this.uploadId;
|
||||
const api = `/api/logs/csv-uploads/${uploadId}`;
|
||||
|
||||
uploadloader(); // 進度條
|
||||
uploadloader(); // Show loading progress bar
|
||||
try {
|
||||
const response = await apiClient.post(api, data);
|
||||
|
||||
this.uploadLogId = response.data.id;
|
||||
Swal.close(); // 關閉進度條
|
||||
await this.rename(); // 改檔名
|
||||
Swal.close(); // Close the loading progress bar
|
||||
await this.rename(); // Rename the file
|
||||
await uploadSuccess();
|
||||
this.$router.push({name: 'Files'});
|
||||
} catch(error) {
|
||||
@@ -204,7 +204,7 @@ export const useFilesStore = defineStore('filesStore', {
|
||||
|
||||
uploadFailedSecond(detail);
|
||||
} else {
|
||||
Swal.close(); // 關閉進度條
|
||||
Swal.close(); // Close the loading progress bar
|
||||
apiError(error, 'Failed to upload the log files.');
|
||||
}
|
||||
}
|
||||
@@ -216,7 +216,7 @@ export const useFilesStore = defineStore('filesStore', {
|
||||
* @param {string} name - The file name.
|
||||
*/
|
||||
async rename(type, id, fileName) {
|
||||
// 先判斷有沒有 uploadLogId,有就設定 id 和 type;再判斷檔案型別。
|
||||
// If uploadLogId exists, set id and type accordingly; then check the file type.
|
||||
if(this.uploadId && this.uploadFileName) {
|
||||
type = 'log';
|
||||
id = this.uploadLogId;
|
||||
|
||||
@@ -39,7 +39,7 @@ export const useLoginStore = defineStore('loginStore', {
|
||||
const api = '/api/oauth/token';
|
||||
const config = {
|
||||
headers: {
|
||||
// http post 預設的 url 編碼,非 json 格式
|
||||
// Default URL-encoded format for HTTP POST, not JSON
|
||||
'Content-Type':'application/x-www-form-urlencoded',
|
||||
},
|
||||
};
|
||||
@@ -48,7 +48,7 @@ export const useLoginStore = defineStore('loginStore', {
|
||||
const response = await axios.post(api, this.auth, config);
|
||||
const accessToken = response.data.access_token;
|
||||
const refresh_token = response.data.refresh_token;
|
||||
// 將 token 儲存在 cookie
|
||||
// Store the token in a cookie
|
||||
setCookieWithoutExpiration("luciaToken", accessToken);
|
||||
const expiryDate = new Date();
|
||||
expiryDate.setMonth(expiryDate.getMonth() + 6);
|
||||
@@ -57,9 +57,9 @@ export const useLoginStore = defineStore('loginStore', {
|
||||
this.isLoggedIn = true;
|
||||
setCookie("isLuciaLoggedIn", "true");
|
||||
|
||||
// 大部分的情況下,預設導向至 FILES 頁面
|
||||
// 然而有一種情況是使用者在沒有登入的情況下貼上了某一個頁面的網址,
|
||||
// 則在此情況下時,我們會在使用者稍後登入後,把使用者帶到剛才記住的 return-to 網址
|
||||
// By default, redirect to the FILES page.
|
||||
// However, if the user pasted a URL while not logged in,
|
||||
// redirect them to the remembered return-to URL after login.
|
||||
if(this.rememberedReturnToUrl !== "") {
|
||||
const decodedUrl = atob(this.rememberedReturnToUrl);
|
||||
// Only allow relative paths to prevent open redirect attacks
|
||||
|
||||
@@ -92,7 +92,7 @@ export const useMapPathStore = defineStore('mapPathStore', {
|
||||
nodes: [],
|
||||
}; // second layer index
|
||||
|
||||
let curGraphNode, prevGraphNode, curEdge; // 配對 curGraphNode 與 nodeIndex 指向的 node
|
||||
let curGraphNode, prevGraphNode, curEdge; // Match curGraphNode with the node at nodeIndex
|
||||
for (let nodeIndex = 0; nodeIndex < curButton[listIndex].length; nodeIndex++) {
|
||||
if (nodeIndex === 0) { // special case, initialize curGraphNode
|
||||
curGraphNode = this.startNode.outgoers('node').filter(neighborOfStart =>
|
||||
@@ -109,7 +109,7 @@ export const useMapPathStore = defineStore('mapPathStore', {
|
||||
}
|
||||
this.insightWithPath[INSIGHTS_FIELDS_AND_LABELS[i][0]][listIndex].nodes.push(curGraphNode);
|
||||
this.insightWithPath[INSIGHTS_FIELDS_AND_LABELS[i][0]][listIndex].edges.push(curEdge);
|
||||
// 特殊狀況,在for迴圈之外額外插入最後一條線段
|
||||
// Special case: append the last edge segment outside the loop
|
||||
if (nodeIndex === curButton[listIndex].length - 1) {
|
||||
const endNode = curGraphNode.outgoers('node').filter(neighbor =>
|
||||
neighbor.data('label').toLocaleLowerCase() === 'end'
|
||||
@@ -142,14 +142,14 @@ export const useMapPathStore = defineStore('mapPathStore', {
|
||||
depthFirstSearchCreatePath(node, currentPathByNode, curPathByEdge) {
|
||||
const outgoingEdges = node.outgoers('edge');
|
||||
if (outgoingEdges.length === 0) {
|
||||
// 表示已經遇到尾聲
|
||||
// Reached the end node
|
||||
this.allPaths.push([...currentPathByNode]);
|
||||
this.allPathsByEdge.push([...curPathByEdge])
|
||||
} else {
|
||||
outgoingEdges.targets().forEach((targetNode) => {
|
||||
if (!currentPathByNode.includes(targetNode)) {
|
||||
const connectingEdge = targetNode.edgesWith(currentPathByNode[currentPathByNode.length - 1]);
|
||||
// 避免loop,只有當目標節點不在當前路徑中之時才繼續
|
||||
// Avoid loops: only continue if the target node is not already in the current path
|
||||
this.depthFirstSearchCreatePath(targetNode, [...currentPathByNode, targetNode],
|
||||
[...curPathByEdge, connectingEdge]
|
||||
);
|
||||
@@ -165,13 +165,13 @@ export const useMapPathStore = defineStore('mapPathStore', {
|
||||
for (let whichPath = 0; whichPath < this.allPaths.length; whichPath++) {
|
||||
const curPath = this.allPaths[whichPath];
|
||||
const curPathByEdge = this.allPathsByEdge[whichPath];
|
||||
// 針對這個path的第一個節點,找到它在insights中是對應到哪一個起點
|
||||
// For the first node in this path, find which insights starting point it corresponds to
|
||||
for (let i = 0; i < INSIGHTS_FIELDS_AND_LABELS.length; i++) {
|
||||
const curButton = this.insights[INSIGHTS_FIELDS_AND_LABELS[i][0]];
|
||||
for (let listIndex = 0; listIndex < curButton.length; listIndex++) {
|
||||
for (let nodeIndex = 0; nodeIndex < curButton[listIndex].length; nodeIndex++) {
|
||||
if (curPath[1].data('label') === curButton[listIndex][nodeIndex]) {
|
||||
// 從 1 開始而不是從 0 開始是因為 0 的label是start字串
|
||||
// Start from index 1 instead of 0 because index 0 is the "start" label
|
||||
const matchResult = this.depthFirstSearchMatchTwoPaths(curPath, 1, curButton, listIndex, nodeIndex)
|
||||
if (matchResult) {
|
||||
this.mapGraphPathToInsight[i] = {
|
||||
@@ -189,39 +189,39 @@ export const useMapPathStore = defineStore('mapPathStore', {
|
||||
} // end first for
|
||||
},
|
||||
depthFirstSearchMatchTwoPaths(curPath, curPathIndex, curButton, listIndex, nodeIndex) {
|
||||
if (listIndex >= curButton.length) { // 邊界條件檢查,防止超出範圍
|
||||
return; // nodeIndex表示是當選訂了五顆按鈕之一之後,清單上的第幾個path
|
||||
if (listIndex >= curButton.length) { // Bounds check to prevent out-of-range access
|
||||
return; // nodeIndex is the path index in the list after selecting one of the five buttons
|
||||
}
|
||||
if (nodeIndex >= curButton[listIndex]) { // 邊界條件檢查,防止超出範圍
|
||||
return; // 表示清單上這個path上的第幾個節點
|
||||
if (nodeIndex >= curButton[listIndex]) { // Bounds check to prevent out-of-range access
|
||||
return; // The node index within this path in the list
|
||||
}
|
||||
// 如果 `curPath` 和 `curButton[listIndex]` 完全匹配
|
||||
// If `curPath` and `curButton[listIndex]` fully match
|
||||
if (curPathIndex === curPath.length || nodeIndex === curButton[listIndex].length) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 邊界條件檢查,防止超出範圍
|
||||
// Bounds check to prevent out-of-range access
|
||||
if (curPathIndex >= curPath.length || nodeIndex >= curButton[listIndex].length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const nodeLabel = curPath[curPathIndex].data('label');
|
||||
// 如果當前節點匹配
|
||||
// If the current node matches
|
||||
if (nodeLabel === curButton[listIndex][nodeIndex]) {
|
||||
if (nodeIndex === curButton[listIndex].length - 1) {
|
||||
return true; // Reach
|
||||
}
|
||||
//從以下兩個選項選出答案可能是true的。但也可能答案都是false
|
||||
// 選項一是遞增insights的第一層的指標。這裡必須遞增path的指標
|
||||
// Pick the option that may return true from the two below; both could be false.
|
||||
// Option 1: increment the insights first-level index; must also increment the path index.
|
||||
if (this.depthFirstSearchMatchTwoPaths(curPath, curPathIndex + 1, curButton, listIndex + 1, nodeIndex)) {
|
||||
return true;
|
||||
}
|
||||
// 選項二是遞增insights的第一層的指標。這裡必須遞增path的指標
|
||||
// Option 2: increment the insights second-level index; must also increment the path index.
|
||||
if (this.depthFirstSearchMatchTwoPaths(curPath, curPathIndex + 1, curButton, listIndex, nodeIndex + 1)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false; // 當前節點不匹配時返回 false
|
||||
return false; // Return false when the current node does not match
|
||||
},
|
||||
highlightClickedPath(clickedActiveTraceIndex: number, clickedPathListIndex: number) {
|
||||
this.insightWithPath[INSIGHTS_FIELDS_AND_LABELS[clickedActiveTraceIndex][0]][clickedPathListIndex].edges.forEach(edgeToHighlight => {
|
||||
|
||||
Reference in New Issue
Block a user