Add JSDoc documentation and file headers to all source files

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-06 18:55:36 +08:00
parent 3b7b6ae859
commit 7fec6cb63f
199 changed files with 2764 additions and 503 deletions

View File

@@ -3,5 +3,14 @@
</template>
<script setup lang="ts">
// The Lucia project.
// Copyright 2023-2026 DSP, inc. All rights reserved.
// Authors:
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
/**
* @module App Root application component that renders the router view.
*/
import { RouterView } from "vue-router";
</script>

View File

@@ -1,10 +1,21 @@
// The Lucia project.
// Copyright 2023-2026 DSP, inc. All rights reserved.
// Authors:
// imacat.yang@dsp.im (imacat), 2023/9/23
/** @module auth Authentication token refresh utilities. */
import axios from 'axios';
import { getCookie, setCookie, setCookieWithoutExpiration } from '@/utils/cookieUtil.js';
/**
* Refresh the access token using the refresh token cookie.
* Uses plain axios (not apiClient) to avoid interceptor loops.
* Refreshes the access token using the stored refresh token cookie.
*
* Uses plain axios (not apiClient) to avoid interceptor loops. Updates
* both the access token (session cookie) and refresh token (6-month
* expiry) cookies.
*
* @returns {Promise<string>} The new access token.
* @throws {Error} If the refresh request fails.
*/
export async function refreshTokenAndGetNew() {
const api = '/api/oauth/token';

View File

@@ -1,6 +1,17 @@
// The Lucia project.
// Copyright 2023-2026 DSP, inc. All rights reserved.
// Authors:
// imacat.yang@dsp.im (imacat), 2023/9/23
/**
* @module apiClient Centralized axios instance with request/response
* interceptors for authentication token management and automatic
* 401 token refresh with request queuing.
*/
import axios from 'axios';
import { getCookie, deleteCookie } from '@/utils/cookieUtil.js';
/** Axios instance configured with auth interceptors. */
const apiClient = axios.create();
// Request interceptor: automatically attach Authorization header
@@ -16,11 +27,19 @@ apiClient.interceptors.request.use((config) => {
let isRefreshing = false;
let pendingRequests = [];
/**
* Resolves all pending requests with the new access token.
* @param {string} newToken - The refreshed access token.
*/
function onRefreshSuccess(newToken) {
pendingRequests.forEach((cb) => cb(newToken));
pendingRequests = [];
}
/**
* Rejects all pending requests with the refresh error.
* @param {Error} error - The token refresh error.
*/
function onRefreshFailure(error) {
pendingRequests.forEach((cb) => cb(null, error));
pendingRequests = [];

View File

@@ -1,3 +1,12 @@
/* The Lucia project.
Copyright 2023-2026 DSP, inc. All rights reserved.
Authors:
chiayin.kuo@dsp.im (chiayin), 2023/4/18
cindy.chang@dsp.im (Cindy Chang), 2024/8/16
Base CSS layer with global font, validation, height,
and PrimeVue sidebar overrides. */
/* 全域字型 */
@layer base {
html {

View File

@@ -1,3 +1,11 @@
/* The Lucia project.
Copyright 2023-2026 DSP, inc. All rights reserved.
Authors:
chiayin.kuo@dsp.im (chiayin), 2023/2/17
Reusable component styles including loaders, scrollbar,
buttons, and toggle buttons. */
/* loading */
.loader {
width: 64px;

View File

@@ -1,3 +1,10 @@
/* The Lucia project.
Copyright 2023-2026 DSP, inc. All rights reserved.
Authors:
chiayin.kuo@dsp.im (chiayin), 2023/4/18
Layout styles for navbar and heading elements. */
/* Navbar */
nav ul>li {
@apply px-2 py-3.5 duration-300 hover:bg-neutral-900 hover:text-neutral-10 ;

View File

@@ -1,3 +1,11 @@
/* The Lucia project.
Copyright 2023-2026 DSP, inc. All rights reserved.
Authors:
chiayin.kuo@dsp.im (chiayin), 2023/1/31
cindy.chang@dsp.im (Cindy Chang), 2024/6/18
Main CSS entry point that imports all stylesheet modules. */
@import './tailwind.css';
@import './base.css';
@import './components.css';

View File

@@ -1,3 +1,12 @@
/* The Lucia project.
Copyright 2023-2026 DSP, inc. All rights reserved.
Authors:
chiayin.kuo@dsp.im (chiayin), 2023/1/31
imacat.yang@dsp.im (imacat), 2026/3/6
Tailwind CSS theme configuration with custom colors,
font sizes, breakpoints, and animations. */
/* 引入 Google fonts */
@import url('https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap');

View File

@@ -1,3 +1,12 @@
/* The Lucia project.
Copyright 2023-2026 DSP, inc. All rights reserved.
Authors:
chiayin.kuo@dsp.im (chiayin), 2023/3/14
cindy.chang@dsp.im (Cindy Chang), 2024/7/26
Third-party vendor style overrides for Google Material
Icons, vue-toast-notification, and PrimeVue components. */
/* import Google font icon */
@import url('https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200');

View File

@@ -1,3 +1,10 @@
/* The Lucia project.
Copyright 2024-2026 DSP, inc. All rights reserved.
Authors:
cindy.chang@dsp.im (Cindy Chang), 2024/6/18
Z-index and positioning rules for the main content area. */
main.w-full {
z-index: 1;
position: absolute; /*if it were static, the acct mgmt menu would be overlapped*/

View File

@@ -30,6 +30,16 @@
</template>
<script setup>
// The Lucia project.
// Copyright 2024-2026 DSP, inc. All rights reserved.
// Authors:
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
// imacat.yang@dsp.im (imacat), 2023/9/23
/**
* @module components/AccountMenu/AcctMenu Dropdown account menu
* with links to account management, my account, and logout.
*/
import { computed, onMounted, ref } from 'vue';
import { storeToRefs } from 'pinia';
import i18next from '@/i18n/i18n';
@@ -58,6 +68,7 @@ const loginUserData = ref(null);
const currentViewingUserDetail = computed(() => acctMgmtStore.currentViewingUser.detail);
const isAdmin = ref(false);
/** Fetches user data and determines if the current user is an admin. */
const getIsAdminValue = async () => {
await loginStore.getUserData();
loginUserData.value = loginStore.userData;
@@ -65,6 +76,7 @@ const getIsAdminValue = async () => {
isAdmin.value = acctMgmtStore.currentViewingUser.is_admin;
};
/** Navigates to the My Account page. */
const onBtnMyAccountClick = async () => {
acctMgmtStore.closeAcctMenu();
await acctMgmtStore.getAllUserAccounts(); // in case we haven't fetched yet
@@ -72,6 +84,7 @@ const onBtnMyAccountClick = async () => {
await router.push('/my-account');
};
/** Registers a click listener to close the menu when clicking outside. */
const clickOtherPlacesThenCloseMenu = () => {
const acctMgmtButton = document.getElementById('acct_mgmt_button');
const acctMgmtMenu = document.getElementById('account_menu');
@@ -83,11 +96,13 @@ const clickOtherPlacesThenCloseMenu = () => {
});
};
/** Navigates to the Account Admin page. */
const onBtnAcctMgmtClick = () => {
router.push({name: 'AcctAdmin'});
acctMgmtStore.closeAcctMenu();
};
/** Handles logout with unsaved-changes confirmation for Map and Conformance pages. */
const onLogoutBtnClick = () => {
if ((route.name === 'Map' || route.name === 'CheckMap') && tempFilterId.value) {
// 傳給 Map通知 Sidebar 要關閉。

View File

@@ -10,6 +10,16 @@
</template>
<script setup>
// The Lucia project.
// Copyright 2024-2026 DSP, inc. All rights reserved.
// Authors:
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
// imacat.yang@dsp.im (imacat), 2023/9/23
/**
* @module components/AccountMenu/SearchBar Search input bar for
* filtering accounts, emits search query on click or Enter key.
*/
import { ref } from 'vue';
import i18next from '@/i18n/i18n.js';
@@ -17,11 +27,19 @@ const emit = defineEmits(['on-search-account-button-click']);
const inputQuery = ref("");
/**
* Emits the search query when the search icon is clicked.
* @param {Event} event - The click event.
*/
const onSearchClick = (event) => {
event.preventDefault();
emit('on-search-account-button-click', inputQuery.value);
};
/**
* Emits the search query when Enter key is pressed.
* @param {KeyboardEvent} event - The keypress event.
*/
const handleKeyPressOfSearch = (event) => {
if (event.key === 'Enter') {
emit('on-search-account-button-click', inputQuery.value);

View File

@@ -13,6 +13,16 @@
</template>
<script setup>
// The Lucia project.
// Copyright 2024-2026 DSP, inc. All rights reserved.
// Authors:
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
// imacat.yang@dsp.im (imacat), 2023/9/23
/**
* @module components/Badge Status badge component that displays
* an activated/deactivated state with colored background.
*/
defineProps({
isActivated: {
type: Boolean,

View File

@@ -16,6 +16,16 @@
</template>
<script setup>
// The Lucia project.
// Copyright 2024-2026 DSP, inc. All rights reserved.
// Authors:
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
// imacat.yang@dsp.im (imacat), 2023/9/23
/**
* @module components/Button Outlined button component with
* press-state ring effect.
*/
import { ref } from 'vue';
defineProps({

View File

@@ -17,6 +17,16 @@
</template>
<script setup>
// The Lucia project.
// Copyright 2024-2026 DSP, inc. All rights reserved.
// Authors:
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
// imacat.yang@dsp.im (imacat), 2023/9/23
/**
* @module components/ButtonFilled Filled button component with
* solid background and press-state ring effect.
*/
import { ref } from 'vue';
defineProps({

View File

@@ -147,6 +147,17 @@
</Sidebar>
</template>
<script setup>
// The Lucia project.
// Copyright 2024-2026 DSP, inc. All rights reserved.
// Authors:
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
// imacat.yang@dsp.im (imacat), 2023/9/23
/**
* @module components/Compare/SidebarStates Summary sidebar for
* the Compare view showing side-by-side statistics (cases,
* traces, activities, timeframes, durations) of two files.
*/
import { ref, onMounted } from 'vue';
import { useRoute } from 'vue-router';
import { useCompareStore } from '@/stores/compare';
@@ -175,9 +186,9 @@ const primaryStatData = ref(null);
const secondaryStatData = ref(null);
/**
* Number to percentage
* @param {number} val 原始數字
* @returns {string} 轉換完成的百分比字串
* Converts a ratio (01) to a percentage number (0100), capped at 100.
* @param {number} val - The ratio value to convert.
* @returns {number} The percentage value.
*/
const getPercentLabel = (val) => {
if((val * 100).toFixed(1) >= 100) return 100;
@@ -185,10 +196,10 @@ const getPercentLabel = (val) => {
};
/**
* setting stats data
* @param { object } data fetch API stats data
* @param { string } fileName file Name
* @returns { object } primaryStatData | secondaryStatData回傳 primaryStatData 或 secondaryStatData
* Transforms raw API stats into display-ready stat data.
* @param {Object} data - The raw stats from the API.
* @param {string} fileName - The file name to display.
* @returns {Object} The formatted stat data object.
*/
const getStatData = (data, fileName) => {
return {
@@ -224,9 +235,7 @@ const getStatData = (data, fileName) => {
}
};
/**
* Behavior when show
*/
/** Populates progress bar values when the sidebar is shown. */
const show = () => {
primaryValueCases.value = primaryStatData.value.cases.ratio;
primaryValueTraces.value = primaryStatData.value.traces.ratio;
@@ -238,9 +247,7 @@ const show = () => {
secondaryValueTasks.value = secondaryStatData.value.tasks.ratio;
};
/**
* Behavior when hidden
*/
/** Resets all progress bar values to zero when the sidebar is hidden. */
const hide = () => {
primaryValueCases.value = 0;
primaryValueTraces.value = 0;

View File

@@ -192,6 +192,18 @@
</section>
</template>
<script setup>
// The Lucia project.
// Copyright 2023-2026 DSP, inc. All rights reserved.
// Authors:
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
// imacat.yang@dsp.im (imacat), 2023/9/23
/**
* @module components/Discover/Conformance/ConformanceResults
* Conformance checking results panel displaying rule
* check outcomes in a data table with status indicators.
*/
import { ref, watch } from 'vue';
import { storeToRefs } from 'pinia';
import { useConformanceStore } from '@/stores/conformance';
@@ -274,8 +286,8 @@ const tooltip = ref({
/**
* set progress bar width
* @param {number} value 百分比數字
* @returns {string} 樣式的寬度設定
* @param {number} value - The percentage value.
* @returns {string} The CSS width style string.
*/
const progressWidth = (value) => {
return `width:${value}%;`
@@ -283,8 +295,8 @@ const progressWidth = (value) => {
/**
* Number to percentage
* @param {number} val 原始數字
* @returns {string} 轉換完成的百分比字串
* @param {number} val - The raw ratio value.
* @returns {string} The formatted percentage string.
*/
const getPercentLabel = (val) => {
if((val * 100).toFixed(1) >= 100) return 100;
@@ -293,7 +305,7 @@ const getPercentLabel = (val) => {
/**
* Convert seconds to days
* @param {number} sec 秒數
* @param {number} sec - The number of seconds.
* @returns {number} day
*/
const convertSecToDay = (sec) => {
@@ -302,7 +314,7 @@ const convertSecToDay = (sec) => {
/**
* Open Issues Modal.
* @param {number} no trace 編號
* @param {number} no - The trace number.
*/
const openMore = async (no) => {
// async await 解決非同步資料延遲傳遞導致未讀取到而出錯的問題
@@ -315,7 +327,7 @@ const openMore = async (no) => {
/**
* Open Loop Modal.
* @param {number} no trace 編號
* @param {number} no - The trace number.
*/
const openLoopMore = async (no) => {
// async await 解決非同步資料延遲傳遞導致未讀取到而出錯的問題
@@ -328,7 +340,7 @@ const openLoopMore = async (no) => {
/**
* set conformance report data
* @param {object} data new watch's value 監聽到後端傳來的報告 data
* @param {object} data - The report data received from the backend.
*/
const setConformanceTempReportData = (newData) => {
const total = getNumberLabel(Object.values(newData.counts).reduce((acc, val) => acc + val, 0));

View File

@@ -122,6 +122,19 @@
</template>
<script setup>
// The Lucia project.
// Copyright 2023-2026 DSP, inc. All rights reserved.
// Authors:
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
// imacat.yang@dsp.im (imacat), 2023/9/23
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
/**
* @module components/Discover/Conformance/ConformanceSidebar
* Main sidebar for conformance checking with rule
* configuration, activity selection, and submit/reset
* actions.
*/
import { ref, computed, watch, onBeforeUnmount } from 'vue';
import { storeToRefs } from 'pinia';
import { useToast } from 'vue-toast-notification';
@@ -622,14 +635,14 @@ watch(conformanceTempReportData, (newValue) => {
// methods
/**
* get min total seconds
* @param {Number} e 最小值總秒數
* @param {number} e - The minimum total seconds.
*/
function minTotalSeconds(e) {
selectTimeRangeMin.value = e;
}
/**
* get min total seconds
* @param {Number} e 最大值總秒數
* @param {number} e - The maximum total seconds.
*/
function maxTotalSeconds(e) {
selectTimeRangeMax.value = e;
@@ -715,7 +728,7 @@ function isSubmitReset() {
isSubmitTimeCfmCtEteSE.value = { base: {}, rule: {}};
}
/**
* 清空選單的行為
* Clears all form selections and resets the sidebar state.
*/
function reset() {
// Results page Cover Plate(遮罩為 ture)
@@ -733,7 +746,7 @@ function reset() {
isShowBarOpen.value = true;
}
/**
* 設定 Start & End Data 連動資料
* Sets linked Start & End data for activity selection.
* @param {string} start task
* @param {string} end task
* @returns {object}
@@ -758,7 +771,7 @@ function setSubmitShowDataByStartEnd(start, end) {
}
}
/**
* Apply button 發送選項,取得 Check Id.
* Submits the selected options via the Apply button and retrieves the Check ID.
*/
async function submitConformance() {
let dataToSave;
@@ -810,6 +823,9 @@ async function submitConformance() {
$toast.success(i18next.t("Conformance.RuleApplied"));
}
/** Builds the data payload for the 'Have activity' rule.
* @returns {object} The rule data to submit.
*/
function getHaveActivityData() {
const dataToSave = {
type: 'contains-tasks',
@@ -820,6 +836,9 @@ function getHaveActivityData() {
return dataToSave;
}
/** Builds the data payload for the 'Activity sequence' rule.
* @returns {object} The rule data to submit.
*/
function getActivitySequenceData() {
let dataToSave;
switch (selectedActivitySequence.value) {
@@ -843,6 +862,9 @@ function getActivitySequenceData() {
return dataToSave;
}
/** Builds the data payload for sequence sub-rules.
* @returns {object} The rule data to submit.
*/
function getSequenceData() {
let dataToSave;
switch (selectedMode.value) {
@@ -872,6 +894,9 @@ function getSequenceData() {
return dataToSave;
}
/** Builds the data payload for the 'Activity duration' rule.
* @returns {object} The rule data to submit.
*/
function getActivityDurationData() {
const dataToSave = {
type: 'task-duration',
@@ -885,6 +910,9 @@ function getActivityDurationData() {
return dataToSave;
}
/** Builds the data payload for the 'Processing time' rule.
* @returns {object} The rule data to submit.
*/
function getProcessingTimeData() {
let dataToSave;
switch (selectedProcessScope.value) {
@@ -898,6 +926,9 @@ function getProcessingTimeData() {
return dataToSave;
}
/** Builds end-to-end processing time rule data.
* @returns {object} The rule data to submit.
*/
function getEndToEndProcessingTimeData() {
let dataToSave;
switch (selectedActSeqMore.value) {
@@ -952,6 +983,9 @@ function getEndToEndProcessingTimeData() {
return dataToSave;
}
/** Builds partial processing time rule data.
* @returns {object} The rule data to submit.
*/
function getPartialProcessingTimeData() {
let dataToSave;
switch (selectedActSeqFromTo.value) {
@@ -997,6 +1031,9 @@ function getPartialProcessingTimeData() {
return dataToSave;
}
/** Builds the data payload for the 'Waiting time' rule.
* @returns {object} The rule data to submit.
*/
function getWaitingTimeData() {
let dataToSave;
switch (selectedProcessScope.value) {
@@ -1010,6 +1047,9 @@ function getWaitingTimeData() {
return dataToSave;
}
/** Builds end-to-end waiting time rule data.
* @returns {object} The rule data to submit.
*/
function getEndToEndWaitingTimeData() {
let dataToSave;
switch (selectedActSeqMore.value) {
@@ -1064,6 +1104,9 @@ function getEndToEndWaitingTimeData() {
return dataToSave;
}
/** Builds partial waiting time rule data.
* @returns {object} The rule data to submit.
*/
function getPartialWaitingTimeData() {
let dataToSave;
switch (selectedActSeqFromTo.value) {
@@ -1109,6 +1152,9 @@ function getPartialWaitingTimeData() {
return dataToSave;
}
/** Builds the data payload for the 'Cycle time' rule.
* @returns {object} The rule data to submit.
*/
function getCycleTimeData() {
let dataToSave;
switch (selectedActSeqMore.value) {
@@ -1164,7 +1210,7 @@ function getCycleTimeData() {
}
/**
* 設置根據類別的任務選擇邏輯
* Sets up category-based task selection logic.
*/
function setTaskByCategoryOnRadioEmitting() {
emitter.on('actRadioData', (data) => {
@@ -1201,6 +1247,10 @@ function setTaskByCategoryOnRadioEmitting() {
});
}
/**
* Handles start task selection for activity sequence.
* @param {string} task - The selected task.
*/
function handleCfmSeqStart(task) {
if (isStartSelected.value && task !== selectCfmSeqStart.value) {
selectCfmSeqEnd.value = null;
@@ -1208,6 +1258,10 @@ function handleCfmSeqStart(task) {
selectCfmSeqStart.value = task;
}
/**
* Handles end task selection for activity sequence.
* @param {string} task - The selected task.
*/
function handleCfmSeqEnd(task) {
if (isEndSelected.value && task !== selectCfmSeqEnd.value) {
selectCfmSeqStart.value = null;
@@ -1215,64 +1269,109 @@ function handleCfmSeqEnd(task) {
selectCfmSeqEnd.value = task;
}
/**
* Sets a simple task selection on a ref field.
* @param {object} field - The ref to set.
* @param {string} task - The selected task.
*/
function handleSimpleSelection(field, task) {
selectFieldRefs[field].value = task;
}
/**
* Handles start task for end-to-end processing time.
* @param {string} task - The selected task.
*/
function handleCfmPtEteSEStart(task) {
if (isStartSelected.value && task !== selectCfmPtEteSEStart.value) {
selectCfmPtEteSEEnd.value = null;
}
selectCfmPtEteSEStart.value = task;
}
/**
* Handles end task for end-to-end processing time.
* @param {string} task - The selected task.
*/
function handleCfmPtEteSEEnd(task) {
if (isEndSelected.value && task !== selectCfmPtEteSEEnd.value) {
selectCfmPtEteSEStart.value = null;
}
selectCfmPtEteSEEnd.value = task;
}
/**
* Handles start task for partial processing time.
* @param {string} task - The selected task.
*/
function handleCfmPtPSEStart(task) {
if (isStartSelected.value && task !== selectCfmPtPSEStart.value) {
selectCfmPtPSEEnd.value = null;
}
selectCfmPtPSEStart.value = task;
}
/**
* Handles end task for partial processing time.
* @param {string} task - The selected task.
*/
function handleCfmPtPSEEnd(task) {
if (isEndSelected.value && task !== selectCfmPtPSEEnd.value) {
selectCfmPtPSEStart.value = null;
}
selectCfmPtPSEEnd.value = task;
}
/**
* Handles start task for end-to-end waiting time.
* @param {string} task - The selected task.
*/
function handleCfmWtEteSEStart(task) {
if (isStartSelected.value && task !== selectCfmWtEteSEStart.value) {
selectCfmWtEteSEEnd.value = null;
}
selectCfmWtEteSEStart.value = task;
}
/**
* Handles end task for end-to-end waiting time.
* @param {string} task - The selected task.
*/
function handleCfmWtEteSEEnd(task) {
if (isEndSelected.value && task !== selectCfmWtEteSEEnd.value) {
selectCfmWtEteSEStart.value = null;
}
selectCfmWtEteSEEnd.value = task;
}
/**
* Handles start task for partial waiting time.
* @param {string} task - The selected task.
*/
function handleCfmWtPSEStart(task) {
if (isStartSelected.value && task !== selectCfmWtPSEStart.value) {
selectCfmWtPSEEnd.value = null;
}
selectCfmWtPSEStart.value = task;
}
/**
* Handles end task for partial waiting time.
* @param {string} task - The selected task.
*/
function handleCfmWtPSEEnd(task) {
if (isEndSelected.value && task !== selectCfmWtPSEEnd.value) {
selectCfmWtPSEStart.value = null;
}
selectCfmWtPSEEnd.value = task;
}
/**
* Handles start task for end-to-end cycle time.
* @param {string} task - The selected task.
*/
function handleCfmCtEteSEStart(task) {
if (isStartSelected.value && task !== selectCfmCtEteSEStart.value) {
selectCfmCtEteSEEnd.value = null;
}
selectCfmCtEteSEStart.value = task;
}
/**
* Handles end task for end-to-end cycle time.
* @param {string} task - The selected task.
*/
function handleCfmCtEteSEEnd(task) {
if (isEndSelected.value && task !== selectCfmCtEteSEEnd.value) {
selectCfmCtEteSEStart.value = null;
@@ -1280,6 +1379,7 @@ function handleCfmCtEteSEEnd(task) {
selectCfmCtEteSEEnd.value = task;
}
/** Sets task data when the list sequence emits a change. */
function setTaskByCategoryOnListSeqEmitting(){
emitter.on('getListSequence', (data) => {
switch (data.category) {
@@ -1295,15 +1395,21 @@ function setTaskByCategoryOnListSeqEmitting(){
});
}
/** Checks if the 'Have activity' rule has valid selections.
* @returns {boolean} Whether the button should be disabled.
*/
function checkHaveActivity() {
return !(selectConformanceTask.value?.length);
}
/** Checks if the 'Activity duration' rule has valid selections.
* @returns {boolean} Whether the button should be disabled.
*/
function checkActivityDuration() {
return !selectDurationData.value?.length;
}
/**
* 檢查活動序列的邏輯
* @returns {boolean} 是否禁用按鈕
* Checks the activity sequence logic.
* @returns {boolean} Whether the button should be disabled.
*/
function checkActivitySequence() {
switch (selectedActivitySequence.value) {
@@ -1316,15 +1422,18 @@ function checkActivitySequence() {
}
}
/**
* 檢查 Start & End 活動序列
* @param {string} start 活動開始
* @param {string} end 活動結束
* @returns {boolean} 是否禁用按鈕
* Checks the Start & End activity sequence.
* @param {string} start - The start activity.
* @param {string} end - The end activity.
* @returns {boolean} Whether the button should be disabled.
*/
function checkStartAndEndSequence(start, end) {
return !(start && end);
}
/** Checks if the 'Processing time' rule has valid selections.
* @returns {boolean} Whether the button should be disabled.
*/
function checkProcessingTime() {
let disabled = true;
switch (selectedProcessScope.value) {
@@ -1339,6 +1448,9 @@ function checkProcessingTime() {
}
return disabled;
}
/** Checks end-to-end scope selections.
* @returns {boolean} Whether the button should be disabled.
*/
function checkEndToEnd() {
let disabled = true;
switch (selectedActSeqMore.value) {
@@ -1362,6 +1474,9 @@ function checkEndToEnd() {
}
return disabled;
}
/** Checks partial scope selections.
* @returns {boolean} Whether the button should be disabled.
*/
function checkPartial() {
let disabled = true;
switch (selectedActSeqFromTo.value) {

View File

@@ -10,6 +10,16 @@
</div>
</template>
<script setup>
// The Lucia project.
// Copyright 2023-2026 DSP, inc. All rights reserved.
// Authors:
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
// imacat.yang@dsp.im (imacat), 2023/9/23
/**
* @module components/Discover/Conformance/ConformanceSidebar/ActList
* Checkbox-based activity list for conformance checking input.
*/
import { ref, watch } from 'vue';
import { sortNumEngZhtw } from '@/module/sortNumEngZhtw.js';
import emitter from '@/utils/emitter';
@@ -27,9 +37,7 @@ watch(() => props.select, (newValue) => {
actList.value = newValue;
});
/**
* 將選取的 Activities 傳出去
*/
/** Emits the selected activities list via the event bus. */
function actListData() {
emitter.emit('actListData', actList.value);
}

View File

@@ -10,6 +10,18 @@
</div>
</template>
<script setup>
// The Lucia project.
// Copyright 2023-2026 DSP, inc. All rights reserved.
// Authors:
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
// imacat.yang@dsp.im (imacat), 2023/9/23
/**
* @module components/Discover/Conformance/ConformanceSidebar/ActRadio
* Radio-button activity selector for conformance checking
* start/end activity input.
*/
import { ref, computed, watch } from 'vue';
import { useConformanceInputStore } from "@/stores/conformanceInput";
import { sortNumEngZhtw } from '@/module/sortNumEngZhtw.js';
@@ -37,9 +49,7 @@ const inputActivityRadioData = computed(() => ({
task: selectedRadio.value,
}));
/**
* 將選取的 Activity 傳出去
*/
/** Emits the selected activity via event bus and updates the store. */
function actRadioData() {
localSelect.value = null;
emitter.emit('actRadioData', inputActivityRadioData.value);
@@ -47,6 +57,7 @@ function actRadioData() {
conformanceInputStore.setActivityRadioStartEndData(inputActivityRadioData.value.task);
}
/** Sets the global activity radio data state in the conformance input store. */
function setGlobalActivityRadioDataState() {
//this.title: value might be "From" or "To"
conformanceInputStore.setActivityRadioStartEndData(inputActivityRadioData.value.task, props.title);

View File

@@ -42,6 +42,18 @@
</div>
</template>
<script setup>
// The Lucia project.
// Copyright 2023-2026 DSP, inc. All rights reserved.
// Authors:
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
// imacat.yang@dsp.im (imacat), 2023/9/23
/**
* @module components/Discover/Conformance/ConformanceSidebar/ActSeqDrag
* Drag-and-drop activity sequence builder for
* conformance rule configuration.
*/
import { ref, computed } from 'vue';
import { sortNumEngZhtw } from '@/module/sortNumEngZhtw.js';
import emitter from '@/utils/emitter';

View File

@@ -51,6 +51,17 @@
</section>
</template>
<script setup>
// The Lucia project.
// Copyright 2023-2026 DSP, inc. All rights reserved.
// Authors:
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
// imacat.yang@dsp.im (imacat), 2023/9/23
/**
* @module components/Discover/Conformance/ConformanceSidebar/ConformanceRadioGroup
* Radio button groups for conformance rule type, activity
* sequence, mode, and process scope selection.
*/
import { storeToRefs } from 'pinia';
import { useConformanceStore } from '@/stores/conformance';
import emitter from '@/utils/emitter';
@@ -92,9 +103,7 @@ const actSeqFromTo = [
{id: 3, name: 'From & To'},
];
/**
* 切換 Rule Type 的選項時的行為
*/
/** Resets dependent selections when the rule type radio changes. */
function changeRadio() {
selectedActivitySequence.value = 'Start & End';
selectedMode.value = 'Directly follows';
@@ -103,27 +112,19 @@ function changeRadio() {
selectedActSeqFromTo.value = 'From';
emitter.emit('isRadioChange', true); // Radio 切換時,資料要清空
}
/**
* 切換 Activity sequence 的選項時的行為
*/
/** Emits event when the activity sequence radio changes. */
function changeRadioSeq() {
emitter.emit('isRadioSeqChange',true);
}
/**
* 切換 Processing time 的選項時的行為
*/
/** Emits event when the process scope radio changes. */
function changeRadioProcessScope() {
emitter.emit('isRadioProcessScopeChange', true);
}
/**
* 切換 Process Scope 的選項時的行為
*/
/** Emits event when the extended activity sequence radio changes. */
function changeRadioActSeqMore() {
emitter.emit('isRadioActSeqMoreChange', true);
}
/**
* 切換 Activity Sequence 的選項時的行為
*/
/** Emits event when the from/to activity sequence radio changes. */
function changeRadioActSeqFromTo() {
emitter.emit('isRadioActSeqFromToChange', true);
}

View File

@@ -29,6 +29,18 @@
</div>
</template>
<script setup>
// The Lucia project.
// Copyright 2023-2026 DSP, inc. All rights reserved.
// Authors:
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
// imacat.yang@dsp.im (imacat), 2023/9/23
/**
* @module components/Discover/Conformance/ConformanceSidebar/ConformanceSelectResult
* Conformance result list with selectable items and
* scrollable display of check results.
*/
import { reactive, computed } from 'vue';
import { storeToRefs } from 'pinia';
import { useConformanceStore } from '@/stores/conformance';

View File

@@ -98,6 +98,18 @@
</section>
</template>
<script setup>
// The Lucia project.
// Copyright 2023-2026 DSP, inc. All rights reserved.
// Authors:
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
// imacat.yang@dsp.im (imacat), 2023/9/23
/**
* @module components/Discover/Conformance/ConformanceSidebar/ConformanceShowBar
* Horizontal bar chart component displaying conformance
* check result statistics.
*/
import { ref, computed, watch } from 'vue';
import { storeToRefs } from 'pinia';
import { useLoadingStore } from '@/stores/loading';
@@ -239,10 +251,9 @@ watch(() => props.isSubmitShowDataCt, (newValue) => {
});
/**
* 設定 start and end 的 Radio Data
* @param {object} data cfmSeqStart | cfmSeqEnd | cfmPtEteSE | cfmPtPSE | cfmWtEteSE | cfmWtPSE | cfmCtEteSE
* 傳入以上任一後端接到的 Activities 列表 Data。
* @param {string} category 'start' | 'end',傳入 'start' 或 'end'。
* Sets the start and end radio data.
* @param {object} data - Activities list data from the backend (cfmSeqStart, cfmSeqEnd, cfmPtEteSE, etc.).
* @param {string} category - 'start' or 'end'.
* @returns {array}
*/
function setTaskData(data, category) {
@@ -251,11 +262,10 @@ function setTaskData(data, category) {
return newData;
}
/**
* 重新設定連動的 start and end 的 Radio Data
* @param {object} data cfmPtEteSE | cfmPtPSE | cfmWtEteSE | cfmWtPSE | cfmCtEteSE
* 傳入以上任一後端接到的 Activities 列表 Data。
* @param {string} category 'start' | 'end',傳入 'start' 或 'end'。
* @param {string} task 已選擇的 Activity task
* Resets the linked start and end radio data.
* @param {object} data - Activities list data from the backend (cfmPtEteSE, cfmPtPSE, etc.).
* @param {string} category - 'start' or 'end'.
* @param {string} task - The selected activity task.
* @returns {array}
*/
function setStartAndEndData(data, category, taskVal) {
@@ -270,10 +280,10 @@ function setStartAndEndData(data, category, taskVal) {
return newData;
}
/**
* 重新設定 Activity sequence 連動的 start and end 的 Radio Data
* @param {object} data cfmSeqStart | cfmSeqEnd,傳入以上任一後端接到的 Activities 列表 Data。
* @param {string} category 'sources' | 'sinks',傳入 'sources' 或 'sinks'
* @param {string} task 已選擇的 Activity task
* Resets the activity sequence linked start and end radio data.
* @param {object} data - Activities list data from the backend (cfmSeqStart or cfmSeqEnd).
* @param {string} category - 'sources' or 'sinks'.
* @param {string} task - The selected activity task.
* @returns {array}
*/
function setSeqStartAndEndData(data, category, taskVal) {
@@ -283,7 +293,7 @@ function setSeqStartAndEndData(data, category, taskVal) {
}
/**
* select start list's task
* @param {event} e 觸發 input 的詳細事件
* @param {Event} e - The input event.
*/
function selectStart(e) {
taskStart.value = e;
@@ -300,7 +310,7 @@ function selectStart(e) {
}
/**
* select End list's task
* @param {event} e 觸發 input 的詳細事件
* @param {Event} e - The input event.
*/
function selectEnd(e) {
taskEnd.value = e;
@@ -326,8 +336,8 @@ function reset() {
taskEnd.value = null;
}
/**
* Radio 切換時,Start & End Data 連動改變
* @param {boolean} data true | false傳入 true 或 false
* Updates linked Start & End data when radio selection changes.
* @param {boolean} data - Whether data should be restored from submission state.
*/
function setResetData(data) {
if(data) {

View File

@@ -68,6 +68,18 @@
</div>
</template>
<script setup>
// The Lucia project.
// Copyright 2023-2026 DSP, inc. All rights reserved.
// Authors:
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
// imacat.yang@dsp.im (imacat), 2023/9/23
/**
* @module components/Discover/Conformance/ConformanceSidebar/ConformanceTimeRange
* Time range picker for conformance time-based rule
* configuration with calendar inputs.
*/
import { reactive } from 'vue';
import { storeToRefs } from 'pinia';
import { useConformanceStore } from '@/stores/conformance';
@@ -149,22 +161,22 @@ const storeRefs = {
/**
* get min total seconds
* @param {Number} e 最小值總秒數
* @param {number} e - The minimum total seconds.
*/
function minTotalSeconds(e) {
emit('min-total-seconds', e);
}
/**
* get min total seconds
* @param {Number} e 最大值總秒數
* @param {number} e - The maximum total seconds.
*/
function maxTotalSeconds(e) {
emit('max-total-seconds', e);
}
/**
* get Time Range(duration)
* @param {array} data API dataActivity 列表
* @param {string} category 'act' | 'single' | 'double',傳入以上任一值。
* @param {Array} data - Activity list data from the API.
* @param {string} category - 'act', 'single', or 'double'.
* @param {string} task select Radio task or start
* @param {string} taskTwo end
* @returns {object} {min:12, max:345}

View File

@@ -9,5 +9,16 @@
</ul>
</template>
<script setup>
// The Lucia project.
// Copyright 2023-2026 DSP, inc. All rights reserved.
// Authors:
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
// imacat.yang@dsp.im (imacat), 2023/9/23
/**
* @module components/Discover/Conformance/ConformanceSidebar/ResultArrow
* Conformance result display with arrow icons showing activity
* sequences.
*/
defineProps(['data', 'select']);
</script>

View File

@@ -9,6 +9,17 @@
</ul>
</template>
<script setup>
// The Lucia project.
// Copyright 2023-2026 DSP, inc. All rights reserved.
// Authors:
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
// imacat.yang@dsp.im (imacat), 2023/9/23
/**
* @module components/Discover/Conformance/ConformanceSidebar/ResultCheck
* Conformance result display with check-circle icons showing
* matched activities.
*/
import { ref, watch } from 'vue';
import emitter from '@/utils/emitter';

View File

@@ -8,6 +8,17 @@
</ul>
</template>
<script setup>
// The Lucia project.
// Copyright 2023-2026 DSP, inc. All rights reserved.
// Authors:
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
// imacat.yang@dsp.im (imacat), 2023/9/23
/**
* @module components/Discover/Conformance/ConformanceSidebar/ResultDot
* Conformance result display with dot icons showing category
* and task pairs.
*/
import { ref, watch } from 'vue';
import emitter from '@/utils/emitter';

View File

@@ -10,6 +10,18 @@
</div>
</template>
<script setup>
// The Lucia project.
// Copyright 2023-2026 DSP, inc. All rights reserved.
// Authors:
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
// imacat.yang@dsp.im (imacat), 2023/9/23
/**
* @module components/Discover/Conformance/ConformanceSidebar/TimeRangeDuration
* Time range duration picker with min/max duration inputs
* for conformance time-based rules.
*/
import { ref, watch } from 'vue';
import Durationjs from '@/components/durationjs.vue';
@@ -28,9 +40,7 @@ const updateMin = ref(null);
const durationMin = ref(null);
const durationMax = ref(null);
/**
* set props values
*/
/** Deep-copies timeData min/max values to the Vue component boundaries. */
function setTimeValue() {
// 深拷貝原始 timeData 的內容
minVuemin.value = JSON.parse(JSON.stringify(timeData.value.min));
@@ -40,8 +50,8 @@ function setTimeValue() {
}
/**
* get min total seconds
* @param {Number} e 元件傳來的最小值總秒數
* Handles the minimum duration total seconds update.
* @param {number} e - The total seconds from the min duration component.
*/
function minTotalSeconds(e) {
timeRangeMin.value = e;
@@ -50,8 +60,8 @@ function minTotalSeconds(e) {
}
/**
* get min total seconds
* @param {Number} e 元件傳來的最大值總秒數
* Handles the maximum duration total seconds update.
* @param {number} e - The total seconds from the max duration component.
*/
function maxTotalSeconds(e) {
timeRangeMax.value = e;

View File

@@ -62,6 +62,18 @@
</Dialog>
</template>
<script setup>
// The Lucia project.
// Copyright 2023-2026 DSP, inc. All rights reserved.
// Authors:
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
// imacat.yang@dsp.im (imacat), 2023/9/23
/**
* @module components/Discover/Conformance/MoreModal
* Modal dialog showing detailed conformance check
* results with expandable activity sequences.
*/
import { ref, computed, watch, nextTick, useTemplateRef } from 'vue';
import { storeToRefs } from 'pinia';
import { useConformanceStore } from '@/stores/conformance';
@@ -169,8 +181,8 @@ watch(infinite404, (newValue) => {
// methods
/**
* Number to percentage
* @param {number} val 原始數字
* @returns {string} 轉換完成的百分比字串
* @param {number} val - The raw ratio value.
* @returns {string} The formatted percentage string.
*/
function getPercentLabel(val){
if((val * 100).toFixed(1) >= 100) return 100;
@@ -178,8 +190,8 @@ function getPercentLabel(val){
}
/**
* set progress bar width
* @param {number} value 百分比數字
* @returns {string} 樣式的寬度設定
* @param {number} value - The percentage value.
* @returns {string} The CSS width style string.
*/
function progressWidth(value){
return `width:${value}%;`
@@ -201,7 +213,7 @@ async function switchCaseData(id) {
showTraceId.value = id; // 放 getDetail 為了 case table 載入完再切換 showTraceId
}
/**
* trace element nodes 資料彙整
* Assembles the trace element nodes data for Cytoscape rendering.
*/
function setNodesData(){
// 避免每次渲染都重複累加
@@ -224,7 +236,7 @@ function setNodesData(){
};
}
/**
* trace edge line 資料彙整
* Assembles the trace edge line data for Cytoscape rendering.
*/
function setEdgesData(){
processMap.value.edges = [];
@@ -256,7 +268,7 @@ function createCy(){
});
}
/**
* 無限滾動: 載入數據
* Infinite scroll: loads more data.
*/
async function fetchData() {
try {
@@ -270,8 +282,8 @@ async function fetchData() {
}
}
/**
* 無限滾動: 監聽 scroll 有沒有滾到底部
* @param {element} event 監聽時回傳的事件
* Infinite scroll: listens for scroll reaching the bottom.
* @param {Event} event - The scroll event.
*/
function handleScroll(event) {
if(maxItems.value || infiniteData.value.length < 20 || infiniteFinish.value === false) return;

View File

@@ -58,6 +58,16 @@
</div>
</template>
<script setup>
// The Lucia project.
// Copyright 2023-2026 DSP, inc. All rights reserved.
// Authors:
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
/**
* @module components/Discover/Map/Filter/ActAndSeq Activity list
* with drag-and-drop sequence builder for creating filter
* rules.
*/
import { ref, computed, watch } from 'vue';
import { sortNumEngZhtwForFilter } from '@/module/sortNumEngZhtw.js';
@@ -100,33 +110,31 @@ watch(() => props.filterTaskData, (newval) => {
});
/**
* double click Activity List
* @param {number} index data item index
* @param {object} element data item
* Moves an activity from the list to the sequence on double-click.
* @param {number} index - The item index in the activity list.
* @param {Object} element - The activity data object.
*/
function moveActItem(index, element) {
listSequence.value.push(element);
}
/**
* double click Sequence List
* @param {number} index data item index
* @param {object} element data item
* Removes an activity from the sequence on double-click.
* @param {number} index - The item index in the sequence.
* @param {Object} element - The activity data object.
*/
function moveSeqItem(index, element) {
listSequence.value.splice(index, 1);
}
/**
* get listSequence
*/
/** Emits the current sequence list to the parent component. */
function getComponentData() {
emit('update:listSeq', listSequence.value);
}
/**
* Element dragging started
* @param {event} evt input 傳入的事件
* Handles drag start: hides original element and last arrow.
* @param {Event} evt - The drag start event.
*/
function onStart(evt) {
const lastChild = evt.to.lastChild.lastChild;
@@ -140,8 +148,8 @@ function onStart(evt) {
}
/**
* Element dragging ended
* @param {event} evt input 傳入的事件
* Handles drag end: restores element visibility.
* @param {Event} evt - The drag end event.
*/
function onEnd(evt) {
// 顯示拖曳元素

View File

@@ -29,6 +29,15 @@
</div>
</template>
<script setup>
// The Lucia project.
// Copyright 2023-2026 DSP, inc. All rights reserved.
// Authors:
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
/**
* @module components/Discover/Map/Filter/ActOcc Activity
* occurrences filter table with single-row radio selection.
*/
import { ref, watch } from 'vue';
const props = defineProps({
@@ -60,8 +69,8 @@ watch(() => props.tableSelect, (newval) => {
});
/**
* 將選取的 row 傳到父層
* @param {event} e input 傳入的事件
* Emits the selected row to the parent component.
* @param {Event} e - The row selection event.
*/
function onRowSelect(e) {
emit('on-row-select', e);

View File

@@ -40,6 +40,16 @@
</template>
<script setup>
// The Lucia project.
// Copyright 2023-2026 DSP, inc. All rights reserved.
// Authors:
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
/**
* @module components/Discover/Map/Filter/ActOccCase Activity
* occurrences and cases filter table with multi-row checkbox
* selection.
*/
import { ref, watch } from 'vue';
const props = defineProps(['tableTitle', 'tableData', 'tableSelect', 'progressWidth']);
@@ -53,23 +63,19 @@ watch(() => props.tableSelect, (newval) => {
select.value = newval;
});
/**
* 選擇 Row 的行為
*/
/** Emits the current selection when a row is selected. */
function onRowSelect() {
emit('on-row-select', select.value);
}
/**
* 取消選取 Row 的行為
*/
/** Emits the current selection when a row is unselected. */
function onRowUnselect() {
emit('on-row-select', select.value);
}
/**
* 全選 Row 的行為
* @param {event} e input 傳入的事件
* Handles select-all rows action.
* @param {Event} e - The select-all event with data property.
*/
function onRowSelectAll(e) {
select.value = e.data;
@@ -77,8 +83,8 @@ function onRowSelectAll(e) {
}
/**
* 取消全選 Row 的行為
* @param {event} e input 傳入的事件
* Handles unselect-all rows action.
* @param {Event} e - The unselect-all event.
*/
function onRowUnelectAll(e) {
select.value = null;

View File

@@ -120,6 +120,18 @@
</section>
</template>
<script setup>
// The Lucia project.
// Copyright 2023-2026 DSP, inc. All rights reserved.
// Authors:
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
// imacat.yang@dsp.im (imacat), 2023/9/23
/**
* @module components/Discover/Map/Filter/Attributes
* Case attribute filter with dynamic form fields
* for filtering by attribute values.
*/
import { ref, computed, onMounted, onBeforeUnmount } from 'vue';
import { storeToRefs } from 'pinia';
import { useAllMapDataStore } from '@/stores/allMapData';
@@ -313,7 +325,7 @@ const labelsData = computed(() => {
});
/**
* 選取類別型 table 的選項
* Selects an option in the categorical table.
*/
function onRowSelect() {
const type = selectedAttName.value.type;
@@ -325,7 +337,7 @@ function onRowSelect() {
}
/**
* 取消類別型 table 的選項
* Deselects an option in the categorical table.
*/
function onRowUnselect() {
const type = selectedAttName.value.type;
@@ -337,8 +349,8 @@ function onRowUnselect() {
}
/**
* 選取類別型 table 的全選項
* @param {event} e input 傳入的事件
* Selects all options in the categorical table.
* @param {Event} e - The input event.
*/
function onRowSelectAll(e) {
selectedAttRange.value = e.data;
@@ -351,7 +363,7 @@ function onRowSelectAll(e) {
}
/**
* 取消類別型 table 的全選項
* Deselects all options in the categorical table.
*/
function onRowUnelectAll() {
selectedAttRange.value = null;
@@ -364,8 +376,8 @@ function onRowUnelectAll() {
}
/**
* 切換 Attribute Name Radio
* @param {event} e input 傳入的事件
* Switches the attribute name radio selection.
* @param {Event} e - The input event.
*/
function switchAttNameRadio(e) {
selectedAttRange.value = null;
@@ -416,8 +428,8 @@ function switchAttNameRadio(e) {
/**
* set progress bar width
* @param {number} value 百分比數字
* @returns {string} 樣式的寬度設定
* @param {number} value - The percentage value.
* @returns {string} The CSS width style string.
*/
function progressWidth(value){
return `width:${value}%;`
@@ -425,8 +437,8 @@ function progressWidth(value){
/**
* Number to percentage
* @param {number} val 原始數字
* @returns {string} 轉換完成的百分比字串
* @param {number} val - The raw ratio value.
* @returns {string} The formatted percentage string.
*/
function getPercentLabel(val){
if((val * 100).toFixed(1) >= 100) return `100%`;
@@ -434,8 +446,8 @@ function getPercentLabel(val){
}
/**
* 調整遮罩大小
* @param {object} chart 取得 chart.js 資料
* Adjusts the overlay mask size.
* @param {object} chart - The Chart.js instance data.
*/
function resizeMask(chart) {
const from = (selectArea.value[0] * 0.01) / (selectRange.value * 0.01);
@@ -445,8 +457,8 @@ function resizeMask(chart) {
}
/**
* 調整左邊遮罩大小
* @param {object} chart 取得 chart.js 資料
* Adjusts the left overlay mask size.
* @param {object} chart - The Chart.js instance data.
*/
function resizeLeftMask(chart, from) {
const canvas = document.querySelector('#chartCanvasId canvas');
@@ -458,8 +470,8 @@ function resizeLeftMask(chart, from) {
}
/**
* 調整右邊遮罩大小
* @param {object} chart 取得 chart.js 資料
* Adjusts the right overlay mask size.
* @param {object} chart - The Chart.js instance data.
*/
function resizeRightMask(chart, to) {
const canvas = document.querySelector('#chartCanvasId canvas');
@@ -471,7 +483,7 @@ function resizeRightMask(chart, to) {
}
/**
* create chart
* Creates and renders the Chart.js line chart for attribute data.
*/
function createChart() {
const vData = valueData.value;
@@ -632,7 +644,7 @@ function createChart() {
}
/**
* 滑塊改變的時候
* Handles slider value changes.
* @param {array} e [1, 100]
*/
function changeSelectArea(e) {
@@ -665,8 +677,8 @@ function changeSelectArea(e) {
}
/**
* 選取開始或結束時間時,要改變滑塊跟圖表
* @param {object} e Tue Jan 25 2022 00:00:00 GMT+0800 (台北標準時間) | Blur Event
* Updates the slider and chart when a start or end time is selected.
* @param {object} e - The date object or blur event.
* @param {string} direction start or end
*/
function sliderValueRange(e, direction) {

View File

@@ -39,6 +39,16 @@
</template>
<script setup>
// The Lucia project.
// Copyright 2023-2026 DSP, inc. All rights reserved.
// Authors:
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
/**
* @module components/Discover/Map/Filter/Funnel Filter funnel
* panel showing applied filter rules with toggle, delete, and
* apply-all actions.
*/
import { storeToRefs } from 'pinia';
import { useToast } from 'vue-toast-notification';
import { useLoadingStore } from '@/stores/loading';
@@ -54,8 +64,9 @@ const { isLoading } = storeToRefs(loadingStore);
const { hasResultRule, temporaryData, postRuleData, ruleData, isRuleData, tempFilterId } = storeToRefs(allMapDataStore);
/**
* @param {boolean} e ture | false可選 ture 或 false
* @param {numble} index rule's index
* Toggles a filter rule on or off.
* @param {boolean} e - Whether the rule is enabled.
* @param {number} index - The rule index.
*/
function isRule(e, index){
const rule = isRuleData.value[index];
@@ -66,8 +77,8 @@ function isRule(e, index){
}
/**
* header:Funnel 刪除全部的 Funnel
* @param {numble|string} index rule's index 或 全部
* Deletes a single filter rule or all rules.
* @param {number|string} index - The rule index, or 'all' to delete all.
*/
async function deleteRule(index) {
if(index === 'all') {
@@ -91,9 +102,7 @@ async function deleteRule(index) {
}
}
/**
* header:Funnel 發送暫存的選取資料
*/
/** Submits all enabled filter rules and refreshes the map data. */
async function submitAll() {
postRuleData.value = temporaryData.value.filter(item => item !== 0); // 取得 submit 的資料,有 toggle button 的話,找出並刪除陣列中為 0 的項目
if(!postRuleData.value?.length) return $toast.error('Not selected');

View File

@@ -31,6 +31,16 @@
</div>
</template>
<script setup>
// The Lucia project.
// Copyright 2023-2026 DSP, inc. All rights reserved.
// Authors:
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
/**
* @module components/Discover/Map/Filter/Timeframes
* Timeframe filter with date range pickers and
* duration range selectors.
*/
import { ref, computed, watch, onMounted } from 'vue';
import { storeToRefs } from 'pinia';
import { useAllMapDataStore } from '@/stores/allMapData';
@@ -151,8 +161,8 @@ watch(selectTimeFrame, (newValue, oldValue) => {
});
/**
* 調整遮罩大小
* @param {object} chartInstance 取得 chart.js 資料
* Adjusts the overlay mask size.
* @param {object} chartInstance - The Chart.js instance.
*/
function resizeMask(chartInstance) {
const from = (selectArea.value[0] * 0.01) / (selectRange.value * 0.01);
@@ -164,8 +174,8 @@ function resizeMask(chartInstance) {
}
/**
* 調整左邊遮罩大小
* @param {object} chartInstance 取得 chart.js 資料
* Adjusts the left overlay mask size.
* @param {object} chartInstance - The Chart.js instance.
*/
function resizeLeftMask(chartInstance, from) {
const canvas = document.getElementById("chartCanvasId");
@@ -177,8 +187,8 @@ function resizeLeftMask(chartInstance, from) {
}
/**
* 調整右邊遮罩大小
* @param {object} chartInstance 取得 chart.js 資料
* Adjusts the right overlay mask size.
* @param {object} chartInstance - The Chart.js instance.
*/
function resizeRightMask(chartInstance, to) {
const canvas = document.getElementById("chartCanvasId");
@@ -190,7 +200,7 @@ function resizeRightMask(chartInstance, to) {
}
/**
* create chart
* Creates and renders the Chart.js area chart for timeframe data.
*/
function createChart() {
const max = filterTimeframe.value.y_axis.max * 1.1;
@@ -289,7 +299,7 @@ function createChart() {
}
/**
* 滑塊改變的時候
* Handles slider value changes.
* @param {array} e [1, 100]
*/
function changeSelectArea(e) {
@@ -310,8 +320,8 @@ function changeSelectArea(e) {
}
/**
* 選取開始或結束時間時,要改變滑塊跟圖表
* @param {object} e Tue Jan 25 2022 00:00:00 GMT+0800 (台北標準時間)
* Updates the slider and chart when a start or end time is selected.
* @param {object} e - The selected date object.
* @param {string} direction start or end
*/
function sliderTimeRange(e, direction) {

View File

@@ -69,6 +69,16 @@
</template>
<script setup>
// The Lucia project.
// Copyright 2023-2026 DSP, inc. All rights reserved.
// Authors:
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
/**
* @module components/Discover/Map/Filter/Trace
* Trace filter with trace selection table and
* trace detail display.
*/
import { ref, computed, watch, onMounted } from 'vue';
import { storeToRefs } from 'pinia';
import { useAllMapDataStore } from '@/stores/allMapData';
@@ -238,8 +248,8 @@ function barOptions(){
/**
* Number to percentage
* @param {number} val 原始數字
* @returns {string} 轉換完成的百分比字串
* @param {number} val - The raw ratio value.
* @returns {string} The formatted percentage string.
*/
function getPercentLabel(val){
if((val * 100).toFixed(1) >= 100) return 100;
@@ -248,8 +258,8 @@ function getPercentLabel(val){
/**
* set progress bar width
* @param {number} value 百分比數字
* @returns {string} 樣式的寬度設定
* @param {number} value - The percentage value.
* @returns {string} The CSS width style string.
*/
function progressWidth(value){
return `width:${value}%;`
@@ -258,7 +268,7 @@ function progressWidth(value){
/**
* switch case data
* @param {number} id case id
* @param {number} count 所有的 case 數量
* @param {number} count - The total number of cases.
*/
async function switchCaseData(id, count) {
// 點同一筆 id 不要有動作
@@ -275,7 +285,7 @@ async function switchCaseData(id, count) {
}
/**
* trace element nodes 資料彙整
* Assembles the trace element nodes data for Cytoscape rendering.
*/
function setNodesData(){
// 避免每次渲染都重複累加
@@ -297,7 +307,7 @@ function setNodesData(){
}
/**
* trace edge line 資料彙整
* Assembles the trace edge line data for Cytoscape rendering.
*/
function setEdgesData(){
processMap.value.edges = [];
@@ -327,8 +337,8 @@ function createCy(){
}
/**
* 無限滾動: 監聽 scroll 有沒有滾到底部
* @param {element} event 滾動傳入的事件
* Infinite scroll: listens for scroll reaching the bottom.
* @param {Event} event - The scroll event.
*/
function handleScroll(event) {
if(infinitMaxItems.value || baseCases.value.length < 20 || infiniteFinish.value === false) return;
@@ -340,7 +350,7 @@ function handleScroll(event) {
}
/**
* 無限滾動: 滾到底後,要載入數據
* Infinite scroll: loads more data when the bottom is reached.
*/
async function fetchData() {
try {

View File

@@ -97,6 +97,17 @@
</template>
<script setup>
// The Lucia project.
// Copyright 2023-2026 DSP, inc. All rights reserved.
// Authors:
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
/**
* @module components/Discover/Map/SidebarFilter
* Filter sidebar for the Map view with tabbed panels
* for activity, timeframe, attribute, and trace
* filters.
*/
import { ref, reactive, computed } from 'vue';
import { storeToRefs } from 'pinia';
import { useToast } from 'vue-toast-notification';
@@ -227,6 +238,7 @@ const isDisabledButton = computed(() => {
/**
* Change Radio Filter Type
*/
/** Checks if the apply button should be disabled based on filter type. */
function radioFilterType() {
reset();
}
@@ -234,6 +246,7 @@ function radioFilterType() {
/**
* Change Radio Act Seq
*/
/** Checks if the apply button should be disabled for activity sequence. */
function radioActSeq() {
reset();
}
@@ -241,6 +254,7 @@ function radioActSeq() {
/**
* Change Radio Start And End
*/
/** Checks if the apply button should be disabled for start & end filter. */
function radioStartAndEnd() {
reset();
}
@@ -254,8 +268,8 @@ function switchTab(newTab) {
/**
* Number to percentage
* @param {number} val 原始數字
* @returns {string} 轉換完成的百分比字串
* @param {number} val - The raw ratio value.
* @returns {string} The formatted percentage string.
*/
function getPercentLabel(val){
if((val * 100).toFixed(1) >= 100) return `100%`;
@@ -264,8 +278,8 @@ function getPercentLabel(val){
/**
* set progress bar width
* @param {number} value 百分比數字
* @returns {string} 樣式的寬度設定
* @param {number} value - The percentage value.
* @returns {string} The CSS width style string.
*/
function progressWidth(value){
return `width:${value}%;`
@@ -293,7 +307,7 @@ function setHaveAct(data){
// 調整 filterStartData / filterEndData / filterStartToEndData / filterEndToStartData 的內容
/**
* @param {array} array filterStartToEnd / filterEndToStart,可傳入以上任一。
* @param {Array} array - filterStartToEnd or filterEndToStart data array.
*/
function setActData(array) {
const list = [];
@@ -376,7 +390,7 @@ function endRow(e) {
// 重新設定連動的 filterStartToEndData / filterEndToStartData 內容
/**
* @param {array} eventData Start or End List
* @param {object} rowDataVal 所選擇的 row's data
* @param {object} rowDataVal - The selected row's data.
* @param {string} event sinks / sources
*/
function setStartAndEndData(eventData, rowDataVal, event){
@@ -398,6 +412,11 @@ function setStartAndEndData(eventData, rowDataVal, event){
/**
* @param {object} e task's object
*/
/**
* Creates a display rule object from filter event data.
* @param {object} e - The filter rule data.
* @returns {object} The display rule with type, label, and toggle.
*/
function setRule(e) {
let label, type;
const includeStr = e.is_exclude? "exclude" : "include";
@@ -449,7 +468,7 @@ function setRule(e) {
}
/**
* @param {boolean} message true | false 清空選項,可選以上任一。
* @param {boolean} message - Whether to show the success toast message.
*/
function reset(message) {
// Sequence
@@ -480,7 +499,7 @@ function reset(message) {
}
/**
* header:Filter 發送選取的資料
* Submits the selected filter data to the backend.
*/
async function submit() {
isLoading.value = true;
@@ -520,10 +539,16 @@ async function submit() {
/**
* create map
*/
/** Triggers the submit-all event to create the map. */
function sumbitAll() {
emit('submit-all');
}
/**
* Checks if the sequence filter selection is valid.
* @param {Array} sele - The current selection values.
* @returns {boolean} Whether the button should be disabled.
*/
function handleSequenceSelection(sele) {
const secondSelection = sele[1];
@@ -539,6 +564,11 @@ function handleSequenceSelection(sele) {
}
}
/**
* Checks if the start & end sub-selection is valid.
* @param {string} option - 'Start', 'End', or 'Start & End'.
* @returns {boolean} Whether the button should be disabled.
*/
function handleStartEndSelection(option) {
switch (option) {
case 'Start':
@@ -552,6 +582,11 @@ function handleStartEndSelection(option) {
}
}
/**
* Checks if the attribute filter selection is valid.
* @param {string} type - The attribute type.
* @returns {boolean} Whether the button should be disabled.
*/
function handleAttributesSelection(type) {
switch (type) {
case 'string':
@@ -567,14 +602,25 @@ function handleAttributesSelection(type) {
}
}
/** Checks if the trace filter selection is valid.
* @returns {boolean} Whether the button should be disabled.
*/
function handleTraceSelection() {
return selectTraceArea.value[0] === selectTraceArea.value[1];
}
/** Checks if the timeframes filter selection is valid.
* @returns {boolean} Whether the button should be disabled.
*/
function handleTimeframesSelection() {
return selectTimeFrame.value.length === 0;
}
/**
* Gets the display label for a task filter rule.
* @param {object} e - The filter rule data.
* @returns {string} The formatted label.
*/
function getTaskLabel(e) {
switch (e.type) {
case "contains-task":
@@ -589,6 +635,11 @@ function getTaskLabel(e) {
}
}
/**
* Gets the display label for an attribute filter rule.
* @param {object} e - The filter rule data.
* @returns {string} The formatted label.
*/
function getAttributeLabel(e) {
switch (e.type) {
case 'string-attr':
@@ -603,6 +654,13 @@ function getAttributeLabel(e) {
}
}
/**
* Builds filter data from the current selections.
* @param {Array} sele - The current selection values.
* @param {boolean} isExclude - Whether this is an exclude filter.
* @param {object} containmentMap - Containment type mapping.
* @returns {object|Array} The filter data to submit.
*/
function getFilterData(sele, isExclude, containmentMap) {
switch (sele[0]) {
case 'Sequence':
@@ -623,6 +681,12 @@ function getFilterData(sele, isExclude, containmentMap) {
}
}
/**
* Builds sequence filter data.
* @param {Array} sele - The current selection values.
* @param {boolean} isExclude - Whether this is an exclude filter.
* @returns {object} The sequence filter data.
*/
function getSequenceData(sele, isExclude) {
switch (sele[1]) {
case 'Have activity(s)':
@@ -644,6 +708,12 @@ function getSequenceData(sele, isExclude) {
}
}
/**
* Builds start & end filter data.
* @param {Array} sele - The current selection values.
* @param {boolean} isExclude - Whether this is an exclude filter.
* @returns {object} The start & end filter data.
*/
function getStartEndData(sele, isExclude) {
switch (sele[2]) {
case 'Start':
@@ -670,6 +740,11 @@ function getStartEndData(sele, isExclude) {
}
}
/**
* Builds attributes filter data.
* @param {boolean} isExclude - Whether this is an exclude filter.
* @returns {object} The attributes filter data.
*/
function getAttributesData(isExclude) {
const attrTypeMap = {
'string': 'string-attr',
@@ -704,6 +779,11 @@ function getAttributesData(isExclude) {
}
}
/**
* Builds trace filter data.
* @param {boolean} isExclude - Whether this is an exclude filter.
* @returns {object} The trace filter data.
*/
function getTraceData(isExclude) {
const lowerIndex = filterTraceViewRef.value.selectArea[0];
const upperIndex = filterTraceViewRef.value.selectArea[1] - 1;
@@ -715,6 +795,10 @@ function getTraceData(isExclude) {
};
}
/**
* Updates the rules list with newly submitted filter data.
* @param {Array} postData - The submitted filter data.
*/
function updateRules(postData) {
if (!temporaryData.value?.length) {
temporaryData.value.push(...postData);
@@ -727,6 +811,11 @@ function updateRules(postData) {
}
}
/**
* Returns a promise that resolves after the given delay.
* @param {number} ms - The delay in milliseconds.
* @returns {Promise} Resolves after the delay.
*/
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}

View File

@@ -242,6 +242,17 @@
</template>
<script setup>
// The Lucia project.
// Copyright 2023-2026 DSP, inc. All rights reserved.
// Authors:
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
/**
* @module components/Discover/Map/SidebarState
* Summary statistics sidebar for the Map view
* displaying cases, traces, activities, timeframe,
* and case duration.
*/
import { computed, ref } from 'vue';
import { usePageAdminStore } from '@/stores/pageAdmin';
import { useMapPathStore } from '@/stores/mapPathStore';
@@ -282,17 +293,26 @@ const valueTraces = ref(0);
const valueTaskInstances = ref(0);
const valueTasks = ref(0);
/**
* Handles click on an active trace to highlight it.
* @param {number} clickedActiveTraceIndex - The clicked trace index.
*/
function onActiveTraceClick(clickedActiveTraceIndex) {
mapPathStore.clearAllHighlight();
activeTrace.value = clickedActiveTraceIndex;
mapPathStore.highlightClickedPath(clickedActiveTraceIndex, clickedPathListIndex.value);
}
/**
* Handles click on a path option to highlight it.
* @param {number} clickedPath - The clicked path index.
*/
function onPathOptionClick(clickedPath) {
clickedPathListIndex.value = clickedPath;
mapPathStore.highlightClickedPath(activeTrace.value, clickedPath);
}
/** Resets the trace highlight to default. */
function onResetTraceBtnClick() {
if(isBPMNOn.value) {
return;
@@ -326,8 +346,8 @@ function moment(time){
/**
* Number to percentage
* @param {number} val 原始數字
* @returns {string} 轉換完成的百分比字串
* @param {number} val - The raw ratio value.
* @returns {string} The formatted percentage string.
*/
function getPercentLabel(val){
if((val * 100).toFixed(1) >= 100) return `100%`;

View File

@@ -60,6 +60,16 @@
</Sidebar>
</template>
<script setup>
// The Lucia project.
// Copyright 2023-2026 DSP, inc. All rights reserved.
// Authors:
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
/**
* @module components/Discover/Map/SidebarTraces
* Traces sidebar showing path insights with
* clickable trace lists for highlighting on the map.
*/
import { ref, computed, watch } from 'vue';
import { storeToRefs } from 'pinia';
import { useLoadingStore } from '@/stores/loading';
@@ -150,8 +160,8 @@ watch(infiniteFirstCases, (newValue) => {
/**
* Number to percentage
* @param {number} val 原始數字
* @returns {string} 轉換完成的百分比字串
* @param {number} val - The raw ratio value.
* @returns {string} The formatted percentage string.
*/
function getPercentLabel(val){
if((val * 100).toFixed(1) >= 100) return `100%`;
@@ -160,8 +170,8 @@ function getPercentLabel(val){
/**
* set progress bar width
* @param {number} value 百分比數字
* @returns {string} 樣式的寬度設定
* @param {number} value - The percentage value.
* @returns {string} The CSS width style string.
*/
function progressWidth(value){
return `width:${value}%;`
@@ -170,7 +180,7 @@ function progressWidth(value){
/**
* switch case data
* @param {number} id case id
* @param {number} count case 數量
* @param {number} count - The total number of cases.
*/
async function switchCaseData(id, count) {
// 點同一筆 id 不要有動作
@@ -184,7 +194,7 @@ async function switchCaseData(id, count) {
}
/**
* trace element nodes 資料彙整
* Assembles the trace element nodes data for Cytoscape rendering.
*/
function setNodesData(){
// 避免每次渲染都重複累加
@@ -206,7 +216,7 @@ function setNodesData(){
}
/**
* trace edge line 資料彙整
* Assembles the trace edge line data for Cytoscape rendering.
*/
function setEdgesData(){
processMap.value.edges = [];
@@ -250,8 +260,8 @@ async function show() {
}
/**
* 無限滾動: 監聽 scroll 有沒有滾到底部
* @param {element} event 滾動傳入的事件
* Infinite scroll: listens for scroll reaching the bottom.
* @param {Event} event - The scroll event.
*/
function handleScroll(event) {
if(infinitMaxItems.value || props.cases.length < 20 || infiniteFinish.value === false) return;
@@ -263,7 +273,7 @@ function handleScroll(event) {
}
/**
* 無限滾動: 滾到底後,要載入數據
* Infinite scroll: loads more data when the bottom is reached.
*/
async function fetchData() {
try {

View File

@@ -70,6 +70,16 @@
</template>
<script setup>
// The Lucia project.
// Copyright 2023-2026 DSP, inc. All rights reserved.
// Authors:
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
/**
* @module components/Discover/Map/SidebarView Visualization
* settings sidebar for map view type (process/BPMN), curve
* style, direction, and data layer selection.
*/
import { ref, onMounted } from 'vue';
import { storeToRefs } from 'pinia';
import { useMapPathStore } from '@/stores/mapPathStore';
@@ -117,8 +127,8 @@ const selectedDuration = ref('');
const rank = ref('LR'); // 直向 TB | 橫向 LR
/**
* switch map type
* @param {string} type 'processMap' | 'bpmn',可傳入以上任一。
* Switches the map type and emits the change event.
* @param {string} type - 'processMap' or 'bpmn'.
*/
function switchMapType(type) {
mapType.value = type;
@@ -126,8 +136,8 @@ function switchMapType(type) {
}
/**
* switch curve style
* @param {string} style 直角 'unbundled-bezier' | 'taxi',可傳入以上任一。
* Switches the curve style and emits the change event.
* @param {string} style - 'unbundled-bezier' (curved) or 'taxi' (elbow).
*/
function switchCurveStyles(style) {
curveStyle.value = style;
@@ -135,8 +145,8 @@ function switchCurveStyles(style) {
}
/**
* switch rank
* @param {string} rank 直向 'TB' | 橫向 'LR',可傳入以上任一。
* Switches the graph layout direction and emits the change event.
* @param {string} rankValue - 'TB' (vertical) or 'LR' (horizontal).
*/
function switchRank(rankValue) {
rank.value = rankValue;
@@ -144,9 +154,9 @@ function switchRank(rankValue) {
}
/**
* switch Data Layoer Type or Option.
* @param {string} e 切換時傳入的選項
* @param {string} type 'freq' | 'duration',可傳入以上任一。
* Switches the data layer type (frequency or duration) and option.
* @param {Event} e - The change event from the radio or select.
* @param {string} type - 'freq' or 'duration'.
*/
function switchDataLayerType(e, type) {
let value = '';
@@ -169,11 +179,13 @@ function switchDataLayerType(e, type) {
emit('switch-data-layer-type', dataLayerType.value, dataLayerOption.value);
}
/** Switches to Process Map view. */
function onProcessMapClick() {
mapPathStore.setIsBPMNOn(false);
switchMapType('processMap');
}
/** Switches to BPMN Model view. */
function onBPMNClick() {
mapPathStore.setIsBPMNOn(true);
switchMapType('bpmn');

View File

@@ -79,6 +79,16 @@
</template>
<script setup>
// The Lucia project.
// Copyright 2024-2026 DSP, inc. All rights reserved.
// Authors:
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
/**
* @module components/Discover/StatusBar Collapsible status bar
* showing dataset statistics (cases, traces, activities,
* timeframe, case duration) for the Discover page.
*/
import { ref, onMounted, } from 'vue';
import { useRoute } from 'vue-router';
import { storeToRefs } from 'pinia';
@@ -95,18 +105,16 @@ const isPanel = ref(false);
const statData = ref(null);
/**
* Number to percentage
* @param {number} val 原始數字
* @returns {string} 轉換完成的百分比字串
* Converts a ratio (01) to a percentage number (0100), capped at 100.
* @param {number} val - The ratio value to convert.
* @returns {number} The percentage value.
*/
function getPercentLabel(val){
if((val * 100).toFixed(1) >= 100) return 100;
else return parseFloat((val * 100).toFixed(1));
}
/**
* setting stats data
*/
/** Transforms raw stats into display-ready format with localized numbers and time labels. */
function getStatData() {
statData.value = {
cases: {

View File

@@ -15,6 +15,17 @@
</Dialog>
</template>
<script setup>
// The Lucia project.
// Copyright 2023-2026 DSP, inc. All rights reserved.
// Authors:
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
// imacat.yang@dsp.im (imacat), 2023/9/23
/**
* @module components/File/UploadModal File upload modal dialog
* with drag-and-drop support for CSV files (max 90 MB).
*/
import { onBeforeUnmount, } from 'vue';
import { storeToRefs } from 'pinia';
import IconUploarding from '../icons/IconUploarding.vue';
@@ -30,8 +41,8 @@ const { uploadFileName } = storeToRefs(filesStore);
const contentClass = 'h-full';
/**
* 上傳的行為
* @param {event} event input 傳入的事件
* Handles CSV file upload: validates size, sends to API, extracts filename.
* @param {Event} event - The file input change event.
*/
async function upload(event) {
const fileInput = document.getElementById('uploadFiles');

View File

@@ -20,6 +20,17 @@
</template>
<script setup>
// The Lucia project.
// Copyright 2023-2026 DSP, inc. All rights reserved.
// Authors:
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
// imacat.yang@dsp.im (imacat), 2023/9/23
/**
* @module components/Header Application header with DSP logo and
* user account menu toggle button.
*/
import { ref, onMounted, } from 'vue';
import { useRoute } from 'vue-router';
import { storeToRefs, } from 'pinia';
@@ -49,7 +60,8 @@ const toggleIsAcctMenuOpen = () => {
};
/**
* 登出的行為
* Handles logout with unsaved-changes confirmation for Map
* and Conformance pages.
*/
function logOutButton() {
if ((route.name === 'Map' || route.name === 'CheckMap') && tempFilterId.value) {

View File

@@ -1,3 +1,7 @@
<!-- The Lucia project.
Copyright 2023-2026 DSP, inc. All rights reserved.
Authors:
chiayin.kuo@dsp.im (chiayin), 2023/1/31 -->
<template>
<div class="w-full h-full fixed inset-0 m-auto flex justify-center items-center bg-gradient-to-tr from-neutral-500/50 to-neutral-900/50 z-[9999]">
<span class="loader block"></span>

View File

@@ -45,6 +45,18 @@
</nav>
</template>
<script setup>
// The Lucia project.
// Copyright 2023-2026 DSP, inc. All rights reserved.
// Authors:
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
// imacat.yang@dsp.im (imacat), 2023/9/23
/**
* @module components/Navbar Navigation bar with breadcrumb-style
* page tabs, import button for Files, and save button for
* Map/Conformance pages.
*/
import { ref, computed, watch, onMounted, } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { storeToRefs, } from 'pinia';
@@ -123,8 +135,8 @@ watch(filterName, (newVal) => {
});
/**
* switch navbar item
* @param {event} event 選取 Navbar 選項後傳入的值
* Handles navigation when a navbar tab is clicked.
* @param {Event} event - The click event from the nav item.
*/
function onNavItemBtnClick(event) {
let type;
@@ -185,8 +197,8 @@ function onNavItemBtnClick(event) {
}
/**
* Based on the route.name, decide the navViewName.
* @returns {string} the string of navigation name to return
* Determines the navbar view name and active page from the current route.
* @returns {string} The navigation item name to highlight.
*/
function getNavViewName() {
@@ -244,9 +256,7 @@ function getNavViewName() {
return valueToSet;
}
/**
* Save button' modal
*/
/** Opens the save modal for Map or Conformance pages. */
async function saveModal() {
// 協助判斷 MAP, CONFORMANCE 儲存有「送出」或「取消」。
// 傳給 Map通知 Sidebar 要關閉。
@@ -268,15 +278,14 @@ async function saveModal() {
}
}
/**
* Set nav item button background color in case the variable is an empty string
*/
/** Sets nav item button background color when the active page is empty. */
function handleNavItemBtn() {
if(activePageComputedByRoute.value === "") {
setActivePageComputedByRoute(route.matched[route.matched.length - 1].name);
}
}
/** Saves or creates a filter for the Map page. */
async function handleMapSave() {
if (createFilterId.value) {
await allMapDataStore.updateFilter();
@@ -292,6 +301,7 @@ async function handleMapSave() {
}
}
/** Saves a filter from the Check Map page. */
async function handleCheckMapSave() {
const isSaved = await saveFilter(allMapDataStore.addFilterId);
if (isSaved) {
@@ -300,6 +310,7 @@ async function handleCheckMapSave() {
}
}
/** Saves or updates conformance check data. */
async function handleConformanceSave() {
if (conformanceFilterCreateCheckId.value || conformanceLogCreateCheckId.value) {
await conformanceStore.updateConformance();

View File

@@ -17,6 +17,17 @@
</template>
<script setup>
// The Lucia project.
// Copyright 2023-2026 DSP, inc. All rights reserved.
// Authors:
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
// imacat.yang@dsp.im (imacat), 2023/9/23
/**
* @module components/Search Search bar component with activity
* search input, settings icon, and search icon button.
*/
import IconSearch from '@/components/icons/IconSearch.vue';
import IconSetting from '@/components/icons/IconSetting.vue';
</script>

View File

@@ -40,6 +40,18 @@
</template>
<script setup>
// The Lucia project.
// Copyright 2023-2026 DSP, inc. All rights reserved.
// Authors:
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
// imacat.yang@dsp.im (imacat), 2023/9/23
/**
* @module components/durationjs
* Duration input component with day/hour/minute/second
* fields and bounded min/max validation.
*/
import { ref, computed, watch, onMounted } from 'vue';
import emitter from '@/utils/emitter';
@@ -137,15 +149,24 @@ const inputTimeFields = computed(() => {
return paddedTimeFields;
});
/** Closes the time selection dropdown. */
function onClose() {
openTimeSelect.value = false;
}
/**
* Selects all text in the focused input field.
* @param {FocusEvent} event - The focus event.
*/
function onFocus(event) {
lastInput.value = event.target;
lastInput.value.select();
}
/**
* Validates and updates the duration value when an input changes.
* @param {Event} event - The change event from a duration input.
*/
function onChange(event) {
const baseInputValue = event.target.value;
let decoratedInputValue;
@@ -184,6 +205,10 @@ function onChange(event) {
calculateTotalSeconds();
}
/**
* Handles arrow key up/down to increment/decrement duration values.
* @param {KeyboardEvent} event - The keyup event.
*/
function onKeyUp(event) {
event.target.value = event.target.value.replace(/\D/g, '');
if (event.keyCode === 38 || event.keyCode === 40) {
@@ -191,6 +216,12 @@ function onKeyUp(event) {
};
}
/**
* Increments or decrements a duration unit value with carry-over.
* @param {HTMLInputElement} input - The input element to update.
* @param {boolean} goUp - True to increment, false to decrement.
* @param {boolean} [selectIt=false] - Whether to select the input text after.
*/
function actionUpDown(input, goUp, selectIt = false) {
const tUnit = input.dataset.tunit;
let newVal = getNewValue(input);
@@ -259,6 +290,11 @@ function updateInputValue(input, newVal, tUnit) {
}
}
/**
* Converts total seconds to day/hour/minute/second components.
* @param {number} totalSec - The total seconds to convert.
* @param {string} [size] - 'max' or 'min' to set boundary days.
*/
function secondToDate(totalSec, size) {
totalSec = parseInt(totalSec);
if(!isNaN(totalSec)) {
@@ -276,6 +312,7 @@ function secondToDate(totalSec, size) {
};
}
/** Calculates total seconds from all duration units and emits the result. */
function calculateTotalSeconds() {
let total = 0;
@@ -299,6 +336,7 @@ function calculateTotalSeconds() {
emit('total-seconds', total);
}
/** Initializes the duration display based on min/max boundaries and preset value. */
async function createData() {
const size = props.size;

View File

@@ -1,3 +1,7 @@
<!-- The Lucia project.
Copyright 2023-2026 DSP, inc. All rights reserved.
Authors:
chiayin.kuo@dsp.im (chiayin), 2023/1/31 -->
<template>
<svg
width="124"

View File

@@ -9,6 +9,16 @@
</template>
<script setup>
// The Lucia project.
// Copyright 2024-2026 DSP, inc. All rights reserved.
// Authors:
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
// imacat.yang@dsp.im (imacat), 2023/9/23
/**
* @module components/icons/IconChecked Checkbox icon component
* that toggles between checked and unchecked states.
*/
import ImgCheckboxBlueFrame from "@/assets/icon-blue-checkbox.svg";
import ImgCheckboxCheckedMark from "@/assets/icon-checkbox-checked.svg";
import ImgCheckboxGrayFrame from "@/assets/icon-checkbox-empty.svg";

View File

@@ -1,3 +1,7 @@
<!-- The Lucia project.
Copyright 2023-2026 DSP, inc. All rights reserved.
Authors:
chiayin.kuo@dsp.im (chiayin), 2023/1/31 -->
<template>
<svg width="32" height="32" viewBox="0 0 32 32" fill="black" xmlns="http://www.w3.org/2000/svg">
<path d="M28.8571 31H3.14286C2.57471 30.9994 2.03 30.7735 1.62825 30.3717C1.22651 29.97 1.00057 29.4253 1 28.8571V3.14286C1.00057 2.57471 1.22651 2.03 1.62825 1.62825C2.03 1.22651 2.57471 1.00057 3.14286 1H28.8571C29.4253 1.00057 29.97 1.22651 30.3717 1.62825C30.7735 2.03 30.9994 2.57471 31 3.14286V28.8571C30.9994 29.4253 30.7735 29.97 30.3717 30.3717C29.97 30.7735 29.4253 30.9994 28.8571 31ZM3.14286 3.14286V28.8571H28.8571V3.14286H3.14286Z"/>

View File

@@ -1,3 +1,7 @@
<!-- The Lucia project.
Copyright 2023-2026 DSP, inc. All rights reserved.
Authors:
chiayin.kuo@dsp.im (chiayin), 2023/1/31 -->
<template>
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5.0529 3.24373C4.9174 3.10198 4.73177 3.01893 4.53578 3.01238C4.33979 3.00583 4.14903 3.07631 4.00437 3.20871C3.85971 3.34111 3.77266 3.5249 3.76187 3.7207C3.75108 3.9165 3.81741 4.10874 3.94665 4.25624L5.74665 6.23436C2.34352 8.32498 0.881025 11.55 0.8154 11.7C0.773866 11.7962 0.752441 11.8999 0.752441 12.0047C0.752441 12.1095 0.773866 12.2132 0.8154 12.3094C0.843525 12.375 1.6404 14.1375 3.4029 15.9094C5.75602 18.2531 8.7279 19.5 11.9998 19.5C13.6807 19.5067 15.3444 19.1618 16.8842 18.4875L18.9467 20.7562C19.0169 20.833 19.1023 20.8943 19.1976 20.9363C19.2928 20.9783 19.3957 21 19.4998 21C19.6869 20.9982 19.8669 20.9282 20.006 20.8031C20.0797 20.7373 20.1396 20.6574 20.1819 20.5681C20.2243 20.4789 20.2484 20.382 20.2528 20.2833C20.2571 20.1846 20.2417 20.086 20.2074 19.9933C20.173 19.9007 20.1205 19.8158 20.0529 19.7437L5.0529 3.24373ZM9.48727 10.3594L13.3966 14.6531C12.9666 14.8818 12.4868 15.0009 11.9998 15C11.4591 15.0001 10.9284 14.8542 10.4639 14.5775C9.99933 14.3009 9.61819 13.9038 9.36076 13.4283C9.10334 12.9528 8.97919 12.4166 9.00146 11.8764C9.02373 11.3362 9.19159 10.812 9.48727 10.3594V10.3594ZM11.9998 18C9.11228 18 6.5904 16.95 4.50915 14.8781C3.64691 14.0329 2.91685 13.0626 2.34352 12C2.78415 11.175 4.19978 8.85936 6.7779 7.36873L8.4654 9.22499C7.81494 10.0615 7.48054 11.1007 7.52112 12.1596C7.5617 13.2185 7.97465 14.2291 8.68723 15.0134C9.39981 15.7976 10.3663 16.3053 11.4164 16.4469C12.4666 16.5885 13.533 16.355 14.4279 15.7875L15.806 17.3062C14.5911 17.771 13.3005 18.0063 11.9998 18V18ZM23.1842 12.3094C23.1467 12.3937 22.1998 14.4937 20.0529 16.4156C19.9152 16.5359 19.7388 16.6025 19.556 16.6031C19.4509 16.6044 19.3467 16.5823 19.2511 16.5385C19.1555 16.4948 19.0707 16.4304 19.0029 16.35C18.937 16.2767 18.8862 16.1912 18.8534 16.0983C18.8205 16.0055 18.8063 15.907 18.8116 15.8086C18.8168 15.7103 18.8413 15.6139 18.8838 15.525C18.9263 15.4361 18.9859 15.3565 19.0592 15.2906C20.107 14.3507 20.9854 13.2376 21.656 12C21.0811 10.9356 20.3513 9.96244 19.4904 9.11248C17.4091 7.04998 14.8873 5.99999 11.9998 5.99999C11.3903 5.99753 10.7818 6.04772 10.181 6.14998C9.98493 6.17994 9.78489 6.13197 9.62371 6.01634C9.46252 5.90071 9.35299 5.72659 9.31852 5.53124C9.30244 5.43397 9.30569 5.33448 9.32809 5.23847C9.35049 5.14246 9.3916 5.0518 9.44907 4.9717C9.50654 4.89159 9.57924 4.8236 9.66301 4.77161C9.74678 4.71963 9.83998 4.68467 9.93727 4.66873C10.6189 4.55517 11.3088 4.49873 11.9998 4.49998C15.2717 4.49998 18.2435 5.74686 20.5966 8.09061C22.3591 9.86249 23.156 11.625 23.1842 11.7C23.2257 11.7962 23.2471 11.8999 23.2471 12.0047C23.2471 12.1095 23.2257 12.2132 23.1842 12.3094V12.3094ZM12.5623 9.05624C12.465 9.03777 12.3723 9.00032 12.2896 8.94604C12.2068 8.89176 12.1355 8.82171 12.0798 8.73987C11.9672 8.57461 11.925 8.37141 11.9623 8.17498C11.9996 7.97856 12.1134 7.80499 12.2786 7.69247C12.4439 7.57995 12.6471 7.53769 12.8435 7.57499C13.7999 7.76202 14.6703 8.25257 15.3257 8.97375C15.981 9.69494 16.3862 10.6083 16.481 11.5781C16.4993 11.7757 16.4385 11.9725 16.312 12.1254C16.1855 12.2782 16.0035 12.3747 15.806 12.3937H15.731C15.5455 12.3945 15.3664 12.3255 15.2292 12.2005C15.0921 12.0755 15.0068 11.9036 14.9904 11.7187C14.9257 11.0729 14.6546 10.4651 14.2172 9.98546C13.7798 9.50585 13.1995 9.18 12.5623 9.05624V9.05624Z" fill="#C4C4C4"/>

View File

@@ -1,3 +1,7 @@
<!-- The Lucia project.
Copyright 2023-2026 DSP, inc. All rights reserved.
Authors:
chiayin.kuo@dsp.im (chiayin), 2023/1/31 -->
<template>
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M23.1842 11.7C23.1561 11.625 22.3592 9.8625 20.5967 8.09063C18.2436 5.74688 15.2717 4.5 11.9998 4.5C8.72793 4.5 5.75606 5.74688 3.40293 8.09063C1.64043 9.8625 0.843558 11.625 0.815433 11.7C0.773576 11.7945 0.751953 11.8967 0.751953 12C0.751953 12.1033 0.773576 12.2055 0.815433 12.3C0.843558 12.375 1.64043 14.1375 3.40293 15.9094C5.75606 18.2531 8.72793 19.5 11.9998 19.5C15.2717 19.5 18.2436 18.2531 20.5967 15.9094C22.3592 14.1375 23.1561 12.375 23.1842 12.3C23.226 12.2055 23.2477 12.1033 23.2477 12C23.2477 11.8967 23.226 11.7945 23.1842 11.7ZM11.9998 18C9.11231 18 6.59043 16.95 4.50918 14.8781C3.64906 14.031 2.91925 13.0611 2.34356 12C2.91847 10.9356 3.64831 9.96245 4.50918 9.1125C6.59043 7.05 9.11231 6 11.9998 6C14.8873 6 17.4092 7.05 19.4904 9.1125C20.3513 9.96245 21.0811 10.9356 21.6561 12C20.9811 13.2656 18.0373 18 11.9998 18ZM11.9998 7.5C11.1098 7.5 10.2398 7.76392 9.49974 8.25839C8.75972 8.75285 8.18295 9.45566 7.84235 10.2779C7.50176 11.1002 7.41264 12.005 7.58628 12.8779C7.75991 13.7508 8.18849 14.5526 8.81783 15.182C9.44716 15.8113 10.249 16.2399 11.1219 16.4135C11.9948 16.5872 12.8996 16.4981 13.7219 16.1575C14.5442 15.8169 15.247 15.2401 15.7414 14.5001C16.2359 13.76 16.4998 12.89 16.4998 12C16.4998 10.8065 16.0257 9.66193 15.1818 8.81802C14.3379 7.97411 13.1933 7.5 11.9998 7.5ZM11.9998 15C11.4065 15 10.8264 14.8241 10.3331 14.4944C9.83975 14.1648 9.45523 13.6962 9.22817 13.1481C9.00111 12.5999 8.9417 11.9967 9.05745 11.4147C9.17321 10.8328 9.45893 10.2982 9.87849 9.87868C10.298 9.45912 10.8326 9.1734 11.4145 9.05764C11.9965 8.94189 12.5997 9.0013 13.1479 9.22836C13.696 9.45542 14.1646 9.83994 14.4942 10.3333C14.8239 10.8266 14.9998 11.4067 14.9998 12C14.9973 12.7949 14.6805 13.5565 14.1184 14.1186C13.5563 14.6807 12.7947 14.9975 11.9998 15Z" fill="#0099FF"/>

View File

@@ -1,3 +1,7 @@
<!-- The Lucia project.
Copyright 2023-2026 DSP, inc. All rights reserved.
Authors:
chiayin.kuo@dsp.im (chiayin), 2023/1/31 -->
<template>
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_2381_1348)">

View File

@@ -1,3 +1,7 @@
<!-- The Lucia project.
Copyright 2023-2026 DSP, inc. All rights reserved.
Authors:
chiayin.kuo@dsp.im (chiayin), 2023/1/31 -->
<template>
<svg width="24" height="24" viewBox="0 0 24 24" fill="#86909C" xmlns="http://www.w3.org/2000/svg">
<path d="M1.5 3.58929C1.5 3.03517 1.72012 2.50375 2.11194 2.11194C2.50375 1.72012 3.03517 1.5 3.58929 1.5H7.76786C8.32197 1.5 8.85339 1.72012 9.24521 2.11194C9.63702 2.50375 9.85714 3.03517 9.85714 3.58929V7.76786C9.85714 8.32197 9.63702 8.85339 9.24521 9.24521C8.85339 9.63702 8.32197 9.85714 7.76786 9.85714H3.58929C3.03517 9.85714 2.50375 9.63702 2.11194 9.24521C1.72012 8.85339 1.5 8.32197 1.5 7.76786V3.58929ZM3.58929 2.89286C3.40458 2.89286 3.22744 2.96623 3.09684 3.09684C2.96623 3.22744 2.89286 3.40458 2.89286 3.58929V7.76786C2.89286 7.95256 2.96623 8.1297 3.09684 8.26031C3.22744 8.39091 3.40458 8.46429 3.58929 8.46429H7.76786C7.95256 8.46429 8.1297 8.39091 8.26031 8.26031C8.39091 8.1297 8.46429 7.95256 8.46429 7.76786V3.58929C8.46429 3.40458 8.39091 3.22744 8.26031 3.09684C8.1297 2.96623 7.95256 2.89286 7.76786 2.89286H3.58929ZM12.6429 3.58929C12.6429 3.03517 12.863 2.50375 13.2548 2.11194C13.6466 1.72012 14.178 1.5 14.7321 1.5H18.9107C19.4648 1.5 19.9962 1.72012 20.3881 2.11194C20.7799 2.50375 21 3.03517 21 3.58929V7.76786C21 8.32197 20.7799 8.85339 20.3881 9.24521C19.9962 9.63702 19.4648 9.85714 18.9107 9.85714H14.7321C14.178 9.85714 13.6466 9.63702 13.2548 9.24521C12.863 8.85339 12.6429 8.32197 12.6429 7.76786V3.58929ZM14.7321 2.89286C14.5474 2.89286 14.3703 2.96623 14.2397 3.09684C14.1091 3.22744 14.0357 3.40458 14.0357 3.58929V7.76786C14.0357 7.95256 14.1091 8.1297 14.2397 8.26031C14.3703 8.39091 14.5474 8.46429 14.7321 8.46429H18.9107C19.0954 8.46429 19.2726 8.39091 19.4032 8.26031C19.5338 8.1297 19.6071 7.95256 19.6071 7.76786V3.58929C19.6071 3.40458 19.5338 3.22744 19.4032 3.09684C19.2726 2.96623 19.0954 2.89286 18.9107 2.89286H14.7321ZM1.5 14.7321C1.5 14.178 1.72012 13.6466 2.11194 13.2548C2.50375 12.863 3.03517 12.6429 3.58929 12.6429H7.76786C8.32197 12.6429 8.85339 12.863 9.24521 13.2548C9.63702 13.6466 9.85714 14.178 9.85714 14.7321V18.9107C9.85714 19.4648 9.63702 19.9962 9.24521 20.3881C8.85339 20.7799 8.32197 21 7.76786 21H3.58929C3.03517 21 2.50375 20.7799 2.11194 20.3881C1.72012 19.9962 1.5 19.4648 1.5 18.9107V14.7321ZM3.58929 14.0357C3.40458 14.0357 3.22744 14.1091 3.09684 14.2397C2.96623 14.3703 2.89286 14.5474 2.89286 14.7321V18.9107C2.89286 19.0954 2.96623 19.2726 3.09684 19.4032C3.22744 19.5338 3.40458 19.6071 3.58929 19.6071H7.76786C7.95256 19.6071 8.1297 19.5338 8.26031 19.4032C8.39091 19.2726 8.46429 19.0954 8.46429 18.9107V14.7321C8.46429 14.5474 8.39091 14.3703 8.26031 14.2397C8.1297 14.1091 7.95256 14.0357 7.76786 14.0357H3.58929ZM12.6429 14.7321C12.6429 14.178 12.863 13.6466 13.2548 13.2548C13.6466 12.863 14.178 12.6429 14.7321 12.6429H18.9107C19.4648 12.6429 19.9962 12.863 20.3881 13.2548C20.7799 13.6466 21 14.178 21 14.7321V18.9107C21 19.4648 20.7799 19.9962 20.3881 20.3881C19.9962 20.7799 19.4648 21 18.9107 21H14.7321C14.178 21 13.6466 20.7799 13.2548 20.3881C12.863 19.9962 12.6429 19.4648 12.6429 18.9107V14.7321ZM14.7321 14.0357C14.5474 14.0357 14.3703 14.1091 14.2397 14.2397C14.1091 14.3703 14.0357 14.5474 14.0357 14.7321V18.9107C14.0357 19.0954 14.1091 19.2726 14.2397 19.4032C14.3703 19.5338 14.5474 19.6071 14.7321 19.6071H18.9107C19.0954 19.6071 19.2726 19.5338 19.4032 19.4032C19.5338 19.2726 19.6071 19.0954 19.6071 18.9107V14.7321C19.6071 14.5474 19.5338 14.3703 19.4032 14.2397C19.2726 14.1091 19.0954 14.0357 18.9107 14.0357H14.7321Z" />

View File

@@ -1,3 +1,7 @@
<!-- The Lucia project.
Copyright 2023-2026 DSP, inc. All rights reserved.
Authors:
chiayin.kuo@dsp.im (chiayin), 2023/1/31 -->
<template>
<svg width="24" height="24" viewBox="0 0 24 24" fill="#86909C" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.75 18C3.75 17.8011 3.82902 17.6103 3.96967 17.4697C4.11032 17.329 4.30109 17.25 4.5 17.25H19.5C19.6989 17.25 19.8897 17.329 20.0303 17.4697C20.171 17.6103 20.25 17.8011 20.25 18C20.25 18.1989 20.171 18.3897 20.0303 18.5303C19.8897 18.671 19.6989 18.75 19.5 18.75H4.5C4.30109 18.75 4.11032 18.671 3.96967 18.5303C3.82902 18.3897 3.75 18.1989 3.75 18ZM3.75 12C3.75 11.8011 3.82902 11.6103 3.96967 11.4697C4.11032 11.329 4.30109 11.25 4.5 11.25H19.5C19.6989 11.25 19.8897 11.329 20.0303 11.4697C20.171 11.6103 20.25 11.8011 20.25 12C20.25 12.1989 20.171 12.3897 20.0303 12.5303C19.8897 12.671 19.6989 12.75 19.5 12.75H4.5C4.30109 12.75 4.11032 12.671 3.96967 12.5303C3.82902 12.3897 3.75 12.1989 3.75 12ZM3.75 6C3.75 5.80109 3.82902 5.61032 3.96967 5.46967C4.11032 5.32902 4.30109 5.25 4.5 5.25H19.5C19.6989 5.25 19.8897 5.32902 20.0303 5.46967C20.171 5.61032 20.25 5.80109 20.25 6C20.25 6.19891 20.171 6.38968 20.0303 6.53033C19.8897 6.67098 19.6989 6.75 19.5 6.75H4.5C4.30109 6.75 4.11032 6.67098 3.96967 6.53033C3.82902 6.38968 3.75 6.19891 3.75 6Z" />

View File

@@ -1,3 +1,7 @@
<!-- The Lucia project.
Copyright 2023-2026 DSP, inc. All rights reserved.
Authors:
chiayin.kuo@dsp.im (chiayin), 2023/1/31 -->
<template>
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 11.75C11.3713 11.7524 10.7643 11.9805 10.2895 12.3926C9.81465 12.8048 9.50353 13.3736 9.41271 13.9958C9.32189 14.6179 9.45738 15.252 9.79456 15.7827C10.1317 16.3134 10.6482 16.7054 11.25 16.8875V18.5C11.25 18.6989 11.329 18.8897 11.4697 19.0303C11.6103 19.171 11.8011 19.25 12 19.25C12.1989 19.25 12.3897 19.171 12.5303 19.0303C12.671 18.8897 12.75 18.6989 12.75 18.5V16.8875C13.3518 16.7054 13.8683 16.3134 14.2054 15.7827C14.5426 15.252 14.6781 14.6179 14.5873 13.9958C14.4965 13.3736 14.1854 12.8048 13.7105 12.3926C13.2357 11.9805 12.6287 11.7524 12 11.75ZM12 15.5C11.7775 15.5 11.56 15.434 11.375 15.3104C11.19 15.1868 11.0458 15.0111 10.9606 14.8055C10.8755 14.6 10.8532 14.3738 10.8966 14.1555C10.94 13.9373 11.0472 13.7368 11.2045 13.5795C11.3618 13.4222 11.5623 13.315 11.7805 13.2716C11.9988 13.2282 12.225 13.2505 12.4305 13.3356C12.6361 13.4208 12.8118 13.565 12.9354 13.75C13.059 13.935 13.125 14.1525 13.125 14.375C13.125 14.6734 13.0065 14.9595 12.7955 15.1705C12.5845 15.3815 12.2984 15.5 12 15.5ZM19.5 8.75H16.125V6.125C16.125 5.03098 15.6904 3.98177 14.9168 3.20818C14.1432 2.4346 13.094 2 12 2C10.906 2 9.85677 2.4346 9.08318 3.20818C8.3096 3.98177 7.875 5.03098 7.875 6.125V8.75H4.5C4.10218 8.75 3.72064 8.90804 3.43934 9.18934C3.15804 9.47064 3 9.85218 3 10.25V20.75C3 21.1478 3.15804 21.5294 3.43934 21.8107C3.72064 22.092 4.10218 22.25 4.5 22.25H19.5C19.8978 22.25 20.2794 22.092 20.5607 21.8107C20.842 21.5294 21 21.1478 21 20.75V10.25C21 9.85218 20.842 9.47064 20.5607 9.18934C20.2794 8.90804 19.8978 8.75 19.5 8.75ZM9.375 6.125C9.375 5.42881 9.65156 4.76113 10.1438 4.26884C10.6361 3.77656 11.3038 3.5 12 3.5C12.6962 3.5 13.3639 3.77656 13.8562 4.26884C14.3484 4.76113 14.625 5.42881 14.625 6.125V8.75H9.375V6.125ZM19.5 20.75H4.5V10.25H19.5V20.75Z" fill="#1A1A1A"/>

View File

@@ -1,3 +1,7 @@
<!-- The Lucia project.
Copyright 2023-2026 DSP, inc. All rights reserved.
Authors:
chiayin.kuo@dsp.im (chiayin), 2023/1/31 -->
<template>
<svg width="40" height="40" viewBox="0 0 40 40" fill="black" xmlns="http://www.w3.org/2000/svg" id="iconMember">
<circle cx="20" cy="20" r="20" fill="white"/>

View File

@@ -1,3 +1,7 @@
<!-- The Lucia project.
Copyright 2023-2026 DSP, inc. All rights reserved.
Authors:
chiayin.kuo@dsp.im (chiayin), 2023/1/31 -->
<template>
<svg width="16" height="16" viewBox="3 0 20 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<g filter="url(#filter0_d_4086_5522)">

View File

@@ -1,3 +1,7 @@
<!-- The Lucia project.
Copyright 2023-2026 DSP, inc. All rights reserved.
Authors:
chiayin.kuo@dsp.im (chiayin), 2023/1/31 -->
<template>
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10 16H22V18H10V16ZM10 10H22V12H10V10Z" fill="#191C21"/>

View File

@@ -1,3 +1,7 @@
<!-- The Lucia project.
Copyright 2023-2026 DSP, inc. All rights reserved.
Authors:
chiayin.kuo@dsp.im (chiayin), 2023/1/31 -->
<template>
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10.5 19C15.1944 19 19 15.1944 19 10.5C19 5.80558 15.1944 2 10.5 2C5.80558 2 2 5.80558 2 10.5C2 15.1944 5.80558 19 10.5 19Z" stroke="#4E5969" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>

View File

@@ -1,3 +1,7 @@
<!-- The Lucia project.
Copyright 2023-2026 DSP, inc. All rights reserved.
Authors:
chiayin.kuo@dsp.im (chiayin), 2023/1/31 -->
<template>
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M22.5 6H19.425C19.05 4.275 17.55 3 15.75 3C13.95 3 12.45 4.275 12.075 6H1.5V7.5H12.075C12.45 9.225 13.95 10.5 15.75 10.5C17.55 10.5 19.05 9.225 19.425 7.5H22.5V6ZM15.75 9C14.475 9 13.5 8.025 13.5 6.75C13.5 5.475 14.475 4.5 15.75 4.5C17.025 4.5 18 5.475 18 6.75C18 8.025 17.025 9 15.75 9ZM1.5 18H4.575C4.95 19.725 6.45 21 8.25 21C10.05 21 11.55 19.725 11.925 18H22.5V16.5H11.925C11.55 14.775 10.05 13.5 8.25 13.5C6.45 13.5 4.95 14.775 4.575 16.5H1.5V18ZM8.25 15C9.525 15 10.5 15.975 10.5 17.25C10.5 18.525 9.525 19.5 8.25 19.5C6.975 19.5 6 18.525 6 17.25C6 15.975 6.975 15 8.25 15Z" fill="#64748B"/>

View File

@@ -1,3 +1,7 @@
<!-- The Lucia project.
Copyright 2023-2026 DSP, inc. All rights reserved.
Authors:
chiayin.kuo@dsp.im (chiayin), 2023/1/31 -->
<template>
<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64" fill="none">
<g clip-path="url(#clip0_2395_2657)">

View File

@@ -1,3 +1,7 @@
<!-- The Lucia project.
Copyright 2023-2026 DSP, inc. All rights reserved.
Authors:
chiayin.kuo@dsp.im (chiayin), 2023/1/31 -->
<template>
<svg width="16" height="16" viewBox="0 0 16 16" fill="black" xmlns="http://www.w3.org/2000/svg">
<path d="M7.9999 16C7.71657 16 7.47924 15.904 7.2879 15.712C7.0959 15.5207 6.9999 15.2833 6.9999 15V3.82499L2.1249 8.69999C1.9249 8.89999 1.68724 8.99999 1.4119 8.99999C1.13724 8.99999 0.899902 8.89999 0.699902 8.69999C0.499902 8.49999 0.399902 8.26665 0.399902 7.99999C0.399902 7.73332 0.499902 7.49999 0.699902 7.29999L7.2999 0.699987C7.3999 0.599987 7.50824 0.528988 7.6249 0.486988C7.74157 0.445654 7.86657 0.424988 7.9999 0.424988C8.13324 0.424988 8.26257 0.445654 8.3879 0.486988C8.51257 0.528988 8.61657 0.599987 8.6999 0.699987L15.2999 7.29999C15.4999 7.49999 15.5999 7.73332 15.5999 7.99999C15.5999 8.26665 15.4999 8.49999 15.2999 8.69999C15.0999 8.89999 14.8622 8.99999 14.5869 8.99999C14.3122 8.99999 14.0749 8.89999 13.8749 8.69999L8.9999 3.82499V15C8.9999 15.2833 8.90424 15.5207 8.7129 15.712C8.5209 15.904 8.28324 16 7.9999 16Z"/>

View File

@@ -1,3 +1,7 @@
<!-- The Lucia project.
Copyright 2023-2026 DSP, inc. All rights reserved.
Authors:
chiayin.kuo@dsp.im (chiayin), 2023/1/31 -->
<template>
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.78488 2.03878C9.62343 1.72766 9.37026 1.46508 9.05455 1.28129C8.73885 1.09751 8.37345 1 8.00047 1C7.62748 1 7.26209 1.09751 6.94638 1.28129C6.63068 1.46508 6.37751 1.72766 6.21605 2.03878L0.521086 12.4048C-0.160262 13.6423 0.70072 15.2857 2.3048 15.2857H13.6954C15.3002 15.2857 16.1598 13.643 15.4799 12.4048L9.78488 2.03878ZM8.00047 5.54628C8.18657 5.54628 8.36505 5.61468 8.49664 5.73645C8.62824 5.85822 8.70216 6.02337 8.70216 6.19557V9.44205C8.70216 9.61425 8.62824 9.77941 8.49664 9.90117C8.36505 10.0229 8.18657 10.0913 8.00047 10.0913C7.81437 10.0913 7.63589 10.0229 7.50429 9.90117C7.3727 9.77941 7.29877 9.61425 7.29877 9.44205V6.19557C7.29877 6.02337 7.3727 5.85822 7.50429 5.73645C7.63589 5.61468 7.81437 5.54628 8.00047 5.54628ZM8.00047 11.0653C8.18657 11.0653 8.36505 11.1337 8.49664 11.2555C8.62824 11.3772 8.70216 11.5424 8.70216 11.7146V12.0392C8.70216 12.2114 8.62824 12.3766 8.49664 12.4984C8.36505 12.6201 8.18657 12.6885 8.00047 12.6885C7.81437 12.6885 7.63589 12.6201 7.50429 12.4984C7.3727 12.3766 7.29877 12.2114 7.29877 12.0392V11.7146C7.29877 11.5424 7.3727 11.3772 7.50429 11.2555C7.63589 11.1337 7.81437 11.0653 8.00047 11.0653Z" fill="#FF3366"/>

View File

@@ -1,3 +1,7 @@
<!-- The Lucia project.
Copyright 2023-2026 DSP, inc. All rights reserved.
Authors:
chiayin.kuo@dsp.im (chiayin), 2023/1/31 -->
<template>
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M16 9.33333C22.6274 9.33333 28 8.13943 28 6.66667C28 5.19391 22.6274 4 16 4C9.37258 4 4 5.19391 4 6.66667C4 8.13943 9.37258 9.33333 16 9.33333Z" stroke="#191C21" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>

View File

@@ -1,18 +1,40 @@
// The Lucia project.
// Copyright 2023-2026 DSP, inc. All rights reserved.
// Authors:
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
// imacat.yang@dsp.im (imacat), 2023/9/23
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
/** @module constants Application-wide constants and chart configuration. */
/** @constant {number} Maximum number of ticks on PrimeVue chart axes. */
export const PRIME_VUE_TICKS_LIMIT = 6;
/** @constant {number} Number of data items to render per batch. */
export const ONCE_RENDER_NUM_OF_DATA = 9;
/** @constant {number} Minimum valid password length. */
export const PWD_VALID_LENGTH = 6;
/** @constant {string} Default grid line color (Tailwind slate-500). */
export const GRID_COLOR = '#64748b';
/** @constant {string} Modal type for creating a new account. */
export const MODAL_CREATE_NEW = 'MODAL_CREATE_NEW';
/** @constant {string} Modal type for editing an account. */
export const MODAL_ACCT_EDIT = 'MODAL_ACCT_EDIT';
/** @constant {string} Modal type for viewing account info. */
export const MODAL_ACCT_INFO = 'MODAL_ACCT_INFO';
/** @constant {string} Modal type for deleting an account. */
export const MODAL_DELETE = 'MODAL_DELETE';
/** @constant {string} LocalStorage key for saved Cytoscape node positions. */
export const SAVE_KEY_NAME = 'CYTOSCAPE_NODE_POSITION';
/** @constant {number} Duration (minutes) to highlight newly created accounts. */
export const JUST_CREATE_ACCOUNT_HOT_DURATION_MINS = 2;
/**
* @constant {Array<[string, string]>} Field keys and display labels for
* process insights (self-loops, short-loops, traces).
*/
export const INSIGHTS_FIELDS_AND_LABELS = [
['self_loops', 'Self-Loop'],
['short_loops', 'Short-Loop'],
@@ -21,6 +43,7 @@ export const INSIGHTS_FIELDS_AND_LABELS = [
['most_freq_traces', 'Most Frequent Trace'],
];
/** @constant {Object} Default Chart.js layout padding options. */
export const knownLayoutChartOption = {
padding: {
top: 16,
@@ -29,6 +52,7 @@ export const knownLayoutChartOption = {
}
};
/** @constant {Object} Default Chart.js scale options for line charts. */
export const knownScaleLineChartOptions = {
x: {
type: 'time',
@@ -84,6 +108,7 @@ y: {
},
},
};
/** @constant {Object} Default Chart.js scale options for horizontal charts. */
export const knownScaleHorizontalChartOptions = {
x: {
title: {
@@ -131,6 +156,7 @@ y: {
},
},
};
/** @constant {Object} Default Chart.js scale options for bar charts. */
export const knownScaleBarChartOptions = {
x: {
title: {

View File

@@ -1,3 +1,14 @@
// The Lucia project.
// Copyright 2023-2026 DSP, inc. All rights reserved.
// Authors:
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
// imacat.yang@dsp.im (imacat), 2023/9/23
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
/**
* @module i18n Internationalization configuration using i18next
* with browser language detection.
*/
import i18next from 'i18next';
import LanguageDetector from 'i18next-browser-languagedetector';
import enLocale from './en.json';

View File

@@ -1,3 +1,15 @@
// The Lucia project.
// Copyright 2023-2026 DSP, inc. All rights reserved.
// Authors:
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
// imacat.yang@dsp.im (imacat), 2023/9/23
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
/**
* @module main Application entry point. Creates the Vue app instance,
* registers plugins (Pinia, Router, PrimeVue, SweetAlert2, Toast),
* global components, and Cytoscape.js extensions.
*/
import { createApp, markRaw } from "vue";
import App from "./App.vue";

View File

@@ -1,7 +1,18 @@
// The Lucia project.
// Copyright 2023-2026 DSP, inc. All rights reserved.
// Authors:
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
// imacat.yang@dsp.im (imacat), 2023/9/23
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
/** @module abbreviateNumber Duration abbreviation formatting. */
/**
* 將數字轉換成簡寫的形式,設定 dhms 的數值
* @param {number} totalSeconds 總秒數
* @returns {string}
* Converts a total number of seconds into a human-readable abbreviated
* duration string using days, hours, minutes, and seconds.
*
* @param {number} totalSeconds - The total number of seconds to convert.
* @returns {string} The abbreviated duration string (e.g. "2d 3h 15m 30s"),
* or "0" if totalSeconds is zero.
*/
export default function abbreviateNumber(totalSeconds) {
let seconds = 0;

View File

@@ -1,3 +1,11 @@
// The Lucia project.
// Copyright 2023-2026 DSP, inc. All rights reserved.
// Authors:
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
// imacat.yang@dsp.im (imacat), 2023/9/23
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
/** @module alertModal SweetAlert2 modal dialogs for user interactions. */
import Swal from 'sweetalert2';
import { useAllMapDataStore } from '@/stores/allMapData';
import { useConformanceStore } from '@/stores/conformance';
@@ -18,8 +26,11 @@ const customClass = {
cancelButton: '!inline-block !rounded-full !text-sm !font-medium !text-center !align-middle !transition-colors !duration-300 !px-5 !py-2 !w-[100px] !h-[40px] ',
};
/**
* Map Saved
* @param { function } addFilterId 後端 API
* Shows a modal dialog to save a new filter with a user-provided name.
*
* @param {Function} addFilterId - Backend API function to create the filter.
* @param {Function|null} [next=null] - Vue Router next() guard callback.
* @returns {Promise<boolean>} True if the filter was saved, false otherwise.
*/
export async function saveFilter(addFilterId, next = null) {
let fileName = '';
@@ -67,8 +78,10 @@ export async function saveFilter(addFilterId, next = null) {
}
}
/**
* Saved Success
* @param { string } value File's name
* Shows a timed success notification after a file has been saved.
*
* @param {string} value - The name of the saved file.
* @returns {Promise<void>}
*/
export async function savedSuccessfully(value) {
value = value || '';
@@ -83,11 +96,16 @@ export async function savedSuccessfully(value) {
})
};
/**
* leave Map page
* @param { function } next 執行完函式後的步驟
* @param { function } addFilterId 後端 API
* @param { string } toPath route path
* @param { function } logOut 登出函式
* Prompts the user to save unsaved filter changes before leaving the
* Map page. Handles confirm (save), cancel (discard), and backdrop
* (stay) scenarios.
*
* @param {Function} next - Vue Router next() guard callback.
* @param {Function} addFilterId - Backend API function to create the filter.
* @param {string} toPath - The destination route path.
* @param {Function} [logOut] - Optional logout function to call instead
* of navigating.
* @returns {Promise<void>}
*/
export async function leaveFilter(next, addFilterId, toPath, logOut) {
const allMapDataStore = useAllMapDataStore();
@@ -140,8 +158,12 @@ export async function leaveFilter(next, addFilterId, toPath, logOut) {
};
/**
* Conformance Saved
* @param { function } addConformanceCreateCheckId 後端 API
* Shows a modal dialog to save a new conformance rule with a
* user-provided name.
*
* @param {Function} addConformanceCreateCheckId - Backend API function
* to create the conformance check.
* @returns {Promise<boolean>} True if the rule was saved, false otherwise.
*/
export async function saveConformance(addConformanceCreateCheckId) {
let fileName = '';
@@ -179,11 +201,16 @@ export async function saveConformance(addConformanceCreateCheckId) {
}
}
/**
* leave Conformance page
* @param { function } next 執行完函式後的步驟
* @param { function } addConformanceCreateCheckId 後端 API
* @param { string } toPath route path
* @param { function } logOut 登出函式
* Prompts the user to save unsaved conformance rule changes before
* leaving the Conformance page. Delegates to helper functions for
* confirm, cancel, and backdrop scenarios.
*
* @param {Function} next - Vue Router next() guard callback.
* @param {Function} addConformanceCreateCheckId - Backend API function
* to create the conformance check.
* @param {string} toPath - The destination route path.
* @param {Function} [logOut] - Optional logout function.
* @returns {Promise<void>}
*/
export async function leaveConformance(next, addConformanceCreateCheckId, toPath, logOut) {
const conformanceStore = useConformanceStore();
@@ -195,6 +222,10 @@ export async function leaveConformance(next, addConformanceCreateCheckId, toPath
await handleDismiss(result.dismiss, conformanceStore, next, toPath, logOut);
}
}
/**
* Displays the "SAVE YOUR RULE?" confirmation dialog.
* @returns {Promise<Object>} The SweetAlert2 result object.
*/
async function showConfirmationDialog() {
return Swal.fire({
title: 'SAVE YOUR RULE?',
@@ -210,6 +241,12 @@ async function showConfirmationDialog() {
});
}
/**
* Handles the confirmed save action for conformance rules.
* @param {Object} conformanceStore - The conformance Pinia store.
* @param {Function} addConformanceCreateCheckId - API function to create check.
* @returns {Promise<void>}
*/
async function handleConfirmed(conformanceStore, addConformanceCreateCheckId) {
if (conformanceStore.conformanceFilterCreateCheckId || conformanceStore.conformanceLogCreateCheckId) {
await conformanceStore.updateConformance();
@@ -221,6 +258,15 @@ async function handleConfirmed(conformanceStore, addConformanceCreateCheckId) {
}
}
/**
* Handles dismiss actions (cancel or backdrop click) for conformance modals.
* @param {string} dismissType - The SweetAlert2 dismiss reason.
* @param {Object} conformanceStore - The conformance Pinia store.
* @param {Function} next - Vue Router next() guard callback.
* @param {string} toPath - The destination route path.
* @param {Function} [logOut] - Optional logout function.
* @returns {Promise<void>}
*/
async function handleDismiss(dismissType, conformanceStore, next, toPath, logOut) {
switch (dismissType) {
case 'cancel':
@@ -237,15 +283,23 @@ async function handleDismiss(dismissType, conformanceStore, next, toPath, logOut
}
}
/**
* Resets temporary conformance check IDs to null.
* @param {Object} conformanceStore - The conformance Pinia store.
*/
function resetTempCheckId(conformanceStore) {
conformanceStore.conformanceFilterTempCheckId = null;
conformanceStore.conformanceLogTempCheckId = null;
}
/**
* Upload failde First
* @param { string } failureType 後端檔案錯誤類型
* @param { string } failureMsg 後端檔案錯誤訊息
* Shows an error modal for the first stage of upload validation failure.
*
* @param {string} failureType - The error type from the backend (e.g.
* "encoding", "insufficient_columns", "empty", "name_suffix", "mime_type").
* @param {string} failureMsg - The raw error message from the backend.
* @param {number} [failureLoc] - The row number where the error occurred.
* @returns {Promise<void>}
*/
export async function uploadFailedFirst(failureType, failureMsg, failureLoc) {
// msg: 'not in UTF-8' | 'insufficient columns' | 'the csv file is empty' | 'the filename does not ends with .csv' | 'not a CSV file'
@@ -283,8 +337,12 @@ export async function uploadFailedFirst(failureType, failureMsg, failureLoc) {
})
};
/**
* Upload failde Second
* @param { array } detail 後端回傳的失敗訊息
* Shows an error modal for the second stage of upload validation failure,
* listing individual row-level errors.
*
* @param {Array<{type: string, loc: Array, input: string}>} detail -
* Array of error detail objects from the backend.
* @returns {Promise<void>}
*/
export async function uploadFailedSecond(detail) {
let srt = '';
@@ -341,8 +399,8 @@ export async function uploadFailedSecond(detail) {
srt = '';
};
/**
* Upload Success
* @param { string } value File's name
* Shows a timed success notification after a file upload completes.
* @returns {Promise<void>}
*/
export async function uploadSuccess() {
await Swal.fire({
@@ -355,8 +413,11 @@ export async function uploadSuccess() {
})
};
/**
* Confirm whether to upload the file
* @param { object } fetchData 後端 API POST data
* Shows a confirmation dialog before uploading a file. Proceeds with
* the upload only if the user confirms.
*
* @param {Object} fetchData - The upload request payload for the backend.
* @returns {Promise<void>}
*/
export async function uploadConfirm(fetchData) {
const filesStore = useFilesStore();
@@ -379,7 +440,8 @@ export async function uploadConfirm(fetchData) {
}
};
/**
* Upload loader
* Shows a non-dismissable loading spinner during file upload.
* @returns {Promise<void>}
*/
export async function uploadloader() {
await Swal.fire({
@@ -390,11 +452,13 @@ export async function uploadloader() {
})
};
/**
* Rename Modal
* @param { function } rename 後端 API
* @param { string } type File 檔案類型
* @param { number } id File ID
* @param { string } baseName 改名前的檔名
* Shows a modal dialog for renaming a file.
*
* @param {Function} rename - Backend API function to rename the file.
* @param {string} type - The file type (e.g. "log", "filter").
* @param {number} id - The file ID.
* @param {string} baseName - The current file name.
* @returns {Promise<void>}
*/
export async function renameModal(rename, type, id, baseName) {
const fileName = baseName;
@@ -434,11 +498,13 @@ export async function renameModal(rename, type, id, baseName) {
// 清空欄位 fileName = '';
}
/**
* Delete File
* @param { string } files 有關連的檔案
* @param { string } type File 檔案類型
* @param { number } id File ID
* @param { string } name 原本的檔案
* Shows a confirmation dialog for deleting a file and its dependents.
*
* @param {string} files - HTML string listing dependent files to be deleted.
* @param {string} type - The file type (e.g. "log", "filter").
* @param {number} id - The file ID.
* @param {string} name - The file name.
* @returns {Promise<void>}
*/
export async function deleteFileModal(files, type, id, name) {
const filesStore = useFilesStore();
@@ -471,7 +537,8 @@ export async function deleteFileModal(files, type, id, name) {
}
};
/**
* Delete Success
* Shows a timed success notification after file deletion.
* @returns {Promise<void>}
*/
export async function deleteSuccess() {
await Swal.fire({
@@ -484,9 +551,13 @@ export async function deleteSuccess() {
})
};
/**
* Really deleted information
* @param {string} files 被刪除的檔案 html
* @param {array} reallyDeleteData 被刪除的檔案 data未取得 recordId 而傳入
* Shows an informational modal about files deleted by other users,
* then records the deletions and refreshes the file list.
*
* @param {string} files - HTML string listing the deleted files.
* @param {Array<{id: number}>} reallyDeleteData - Array of deleted file
* objects with their IDs.
* @returns {Promise<void>}
*/
export async function reallyDeleteInformation(files, reallyDeleteData) {
const filesStore = useFilesStore();
@@ -513,8 +584,9 @@ export async function reallyDeleteInformation(files, reallyDeleteData) {
}
/**
* When user is leaving the acct mgmt page but hasn't finished editing,
* we jump out this alert modal to remind her.
* Shows a reminder modal when the user leaves the account management
* page with unsaved edits.
* @returns {Promise<void>}
*/
export async function leaveAccountManagementToRemind(){
const modalStore = useModalStore();

View File

@@ -1,15 +1,30 @@
// The Lucia project.
// Copyright 2023-2026 DSP, inc. All rights reserved.
// Authors:
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
// imacat.yang@dsp.im (imacat), 2023/9/23
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
/** @module apiError Centralized API error handler with toast notifications. */
import { useLoadingStore } from '@/stores/loading';
import {useToast} from 'vue-toast-notification';
import 'vue-toast-notification/dist/theme-sugar.css';
// Delay loading and toast
/**
* Returns a promise that resolves after the specified milliseconds.
* @param {number} [s=0] - The delay in milliseconds.
* @returns {Promise<void>} A promise that resolves after the delay.
*/
const delay = (s = 0) => new Promise((resolve, reject) => setTimeout(resolve, s));
/**
* API catch error function.
* 401 errors are handled by the axios response interceptor in api/client.js.
* @param {object} error 後端 ERROR
* @param {string} toastMessage Toast 的提示文字
* Handles API errors by showing a loading spinner followed by a toast
* notification. 401 errors are handled by the axios response interceptor
* in api/client.js.
*
* @param {Object} error - The error object from the API call.
* @param {string} toastMessage - The message to display in the toast.
* @returns {Promise<void>}
*/
export default async function apiError(error, toastMessage) {
const loading = useLoadingStore();

View File

@@ -1,3 +1,14 @@
// The Lucia project.
// Copyright 2023-2026 DSP, inc. All rights reserved.
// Authors:
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
// imacat.yang@dsp.im (imacat), 2023/9/23
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
/**
* @module cytoscapeMap Cytoscape.js process map rendering with
* interactive node/edge highlighting, tooltips, and position persistence.
*/
import cytoscape from 'cytoscape';
import spread from 'cytoscape-spread';
import dagre from 'cytoscape-dagre';
@@ -6,11 +17,20 @@ import cola from 'cytoscape-cola';
import tippy from 'tippy.js';
import 'tippy.js/dist/tippy.css';
import { useMapPathStore } from '@/stores/mapPathStore';
import { getTimeLabel } from '@/module/timeLabel.js'; // 時間格式轉換器
import { getTimeLabel } from '@/module/timeLabel.js';
import { useCytoscapeStore } from '@/stores/cytoscapeStore';
import { SAVE_KEY_NAME } from '@/constants/constants.js';
const composeFreqTypeText = (baseText, dataLayerOption, optionValue) => { //sonar-qube
/**
* Composes display text for frequency-type data layer values.
*
* @param {string} baseText - The base label text prefix.
* @param {string} dataLayerOption - The data layer option key
* (e.g. "rel_freq" for relative frequency).
* @param {number} optionValue - The numeric value to format.
* @returns {string} The formatted text with the value appended.
*/
const composeFreqTypeText = (baseText, dataLayerOption, optionValue) => {
let text = baseText;
const textInt = dataLayerOption === 'rel_freq' ? baseText + optionValue * 100 + "%" : baseText + optionValue;
const textFloat = dataLayerOption === 'rel_freq' ? baseText + (optionValue * 100).toFixed(2) + "%" : baseText + optionValue.toFixed(2);
@@ -26,18 +46,23 @@ cytoscape.use(fcose);
cytoscape.use(cola);
/**
* @param {object} mapData processMapData | bpmnData可選以上任一。
* mapData:{
* startId: 0,
* endId: 1,
* nodes: [],
* edges: []
* }
* @param {string} dataLayerType DataLayer's type
* @param {string} dataLayerOption DataLayer's options
* @param {string} curve Curve's type
* @param {string} graphId cytoscape's container
* @return {cytoscape.Core} cy
* Creates and configures a Cytoscape.js process map instance with
* interactive features including node/edge highlighting, tooltips,
* and position persistence via localStorage.
*
* @param {Object} mapData - The map data containing nodes and edges.
* @param {number} mapData.startId - The start node ID.
* @param {number} mapData.endId - The end node ID.
* @param {Array} mapData.nodes - Array of node data objects.
* @param {Array} mapData.edges - Array of edge data objects.
* @param {string} dataLayerType - The data layer type ("freq" or "duration").
* @param {string} dataLayerOption - The data layer option key
* (e.g. "abs_freq", "rel_freq", "rel_duration").
* @param {string} curveStyle - The edge curve style
* ("unbundled-bezier" or "taxi").
* @param {string} rank - The layout direction ("TB" or "LR").
* @param {HTMLElement} graphId - The DOM container element for Cytoscape.
* @returns {cytoscape.Core} The configured Cytoscape instance.
*/
export default function cytoscapeMap(mapData, dataLayerType, dataLayerOption, curveStyle, rank, graphId) {
// 設定每個 node, edges 的顏色與樣式

View File

@@ -1,3 +1,14 @@
// The Lucia project.
// Copyright 2023-2026 DSP, inc. All rights reserved.
// Authors:
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
// imacat.yang@dsp.im (imacat), 2023/9/23
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
/**
* @module cytoscapeMapTrace Cytoscape.js process map rendering for
* individual trace visualization.
*/
import cytoscape from 'cytoscape';
import dagre from 'cytoscape-dagre';
import tippy from 'tippy.js';
@@ -6,19 +17,14 @@ import 'tippy.js/dist/tippy.css';
cytoscape.use( dagre );
/**
* draw processmap for trace
* @param {array} nodes array of an object, it contains
* data:{
* backgroundColor: string
* bordercolor: string
* height: number
* id: number
* label: string
* shape: string
* width: number
* }
* @param {array} edges it's similar to nodes
* @param {string} graphId graph Id
* Creates a Cytoscape.js instance for rendering a single trace's
* process map with left-to-right dagre layout and tooltips.
*
* @param {Array<Object>} nodes - Array of node data objects with
* backgroundColor, bordercolor, height, id, label, shape, and width.
* @param {Array<Object>} edges - Array of edge data objects.
* @param {HTMLElement} graphId - The DOM container element for Cytoscape.
* @returns {cytoscape.Core} The configured Cytoscape instance.
*/
export default function cytoscapeMapTrace(nodes, edges, graphId) {
// create Cytoscape

View File

@@ -1,25 +1,32 @@
// sonar-qube replace regex method
// The Lucia project.
// Copyright 2023-2026 DSP, inc. All rights reserved.
// Authors:
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
// imacat.yang@dsp.im (imacat), 2023/9/23
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
/** @module numberLabel Number formatting with comma separators. */
/**
* Formats an integer string by inserting commas every three digits.
* @param {string} numberStr - A string of digits to format.
* @returns {string} The formatted string with commas (e.g. "1,000,000").
*/
const formatNumberWithCommas = (numberStr) => {
// 反轉字符串
let reversedStr = numberStr.split('').reverse().join('');
// 將反轉後的字符串每 3 個字符為一組進行分割
let groupedStr = reversedStr.match(/.{1,3}/g);
// 用逗號將這些組連接起來
let joinedStr = groupedStr.join(',');
// 再次反轉回原來的順序
let finalStr = joinedStr.split('').reverse().join('');
return finalStr;
}
/**
* 將數字轉換成帶有逗號格式(ex: 1000 -> 1,000)
* 也可以改用原生 JS 方法 `.toLocaleString('en-US')`
* @param {number} num 數字
* @returns
* Converts a number to a string with comma-separated thousands.
*
* Handles decimal numbers by formatting only the integer part.
*
* @param {number} num - The number to format.
* @returns {string} The formatted number string (e.g. "1,234.56").
*/
export default function numberLabel(num) {
let parts = num.toString().split('.');

View File

@@ -1,14 +1,27 @@
// The Lucia project.
// Copyright 2023-2026 DSP, inc. All rights reserved.
// Authors:
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
// imacat.yang@dsp.im (imacat), 2023/9/23
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
/** @module setChartData Chart.js data transformation utilities. */
import getMoment from 'moment';
/**
* 將後端的 chart data 加入最大、最小值,折線圖
* @param {array} baseData 後端給的 10 個時間點
* @param {number} xMax 2022-05-23T18:00:00
* @param {number} xMin 2022-05-23T08:00:00
* @param {boolean} isPercent 是否以百分比顯示
* @param {number} yMax case
* @param {number} yMin case
* @returns {array} 可直接套入 chart.js 的 data
* Extends backend chart data with extrapolated boundary points for
* line charts. Prepends a calculated minimum and appends a calculated
* maximum data point based on linear extrapolation.
*
* @param {Array<{x: number, y: number}>} baseData - The 10 data points
* from the backend.
* @param {number} xMax - The maximum x-axis timestamp.
* @param {number} xMin - The minimum x-axis timestamp.
* @param {boolean} isPercent - Whether values are percentages (0-1 range).
* @param {number} yMax - The maximum y-axis value for clamping.
* @param {number} yMin - The minimum y-axis value for clamping.
* @returns {Array<{x: number, y: number}>} The extended data array
* with boundary points.
*/
export function setLineChartData(baseData, xMax, xMin, isPercent, yMax, yMin) {
// 將 baseData 轉換為包含 x 和 y 屬性的物件陣列
@@ -35,11 +48,14 @@ export function setLineChartData(baseData, xMax, xMin, isPercent, yMax, yMin) {
return data;
};
/**
* 計算 y 軸最小值
*
* x 值為 0 ~ 11,
* 將三的座標(ax, ay), (bx, by), (cx, cy)命名為 (a, b), (c, d), (e, f)
* 最小值: (f - b)(c - a) = (e - a)(d - b),求 b = (ed - ad - fa - fc) / (e - c - a)
* Extrapolates the Y-axis minimum boundary value using linear
* interpolation from the first two data points.
*
* @param {Array<{x: number, y: number}>} baseData - The base data points.
* @param {boolean} isPercent - Whether to clamp to 0-1 range.
* @param {number} yMin - The minimum allowed Y value.
* @param {number} yMax - The maximum allowed Y value.
* @returns {number} The extrapolated and clamped Y minimum value.
*/
function calculateYMin(baseData, isPercent, yMin, yMax) {
let a = 0;
@@ -51,11 +67,14 @@ function calculateYMin(baseData, isPercent, yMin, yMax) {
return clampValue(b, isPercent, yMin, yMax);
};
/**
* 計算 y 軸最大值
*
* x 值為 9 ~ 11,
* 將三的座標(ax, ay), (bx, by), (cx, cy)命名為 (a, b), (c, d), (e, f)
* 最大值: (f - b)(e - c) = (f - d)(e - a),求 f = (be - bc -de + da) / (a - c)
* Extrapolates the Y-axis maximum boundary value using linear
* interpolation from the last two data points.
*
* @param {Array<{x: number, y: number}>} baseData - The base data points.
* @param {boolean} isPercent - Whether to clamp to 0-1 range.
* @param {number} yMin - The minimum allowed Y value.
* @param {number} yMax - The maximum allowed Y value.
* @returns {number} The extrapolated and clamped Y maximum value.
*/
function calculateYMax(baseData, isPercent, yMin, yMax) {
let ma = 9;
@@ -67,15 +86,14 @@ function calculateYMax(baseData, isPercent, yMin, yMax) {
return clampValue(mf, isPercent, yMin, yMax);
};
/**
* 將值限制在指定範圍內
* 如果 isPercent 為 true則將值限制在 0 到 1 之間
* 否則,將值限制在 yMin 和 yMax 之間
*
* @param {number} value 需要被限制的值
* @param {boolean} isPercent 如果為 true表示值應該被限制在百分比範圍內0 到 1
* @param {number} min 非百分比情況下的最小允許值yMin
* @param {number} max 非百分比情況下的最大允許值yMax
* @returns {number} 被限制在指定範圍內的值
* Clamps a value within a specified range. If isPercent is true, the
* value is clamped to [0, 1]; otherwise to [min, max].
*
* @param {number} value - The value to clamp.
* @param {boolean} isPercent - Whether to use the percentage range [0, 1].
* @param {number} min - The minimum bound (used when isPercent is false).
* @param {number} max - The maximum bound (used when isPercent is false).
* @returns {number} The clamped value.
*/
function clampValue(value, isPercent, min, max) {
if (isPercent) {
@@ -95,9 +113,13 @@ function clampValue(value, isPercent, min, max) {
return value;
};
/**
* 將後端的 chart data x 值轉換時間格式,長條圖
* @param {array} baseData 後端給的 10 個時間點
* @returns {array} 可直接套入 chart.js 的 data
* Converts backend chart data timestamps to formatted date strings
* for bar charts.
*
* @param {Array<{x: string, y: number}>} baseData - The data points from
* the backend with ISO timestamp x values.
* @returns {Array<{x: string, y: number}>} Data with x values formatted
* as "YYYY/M/D hh:mm:ss".
*/
export function setBarChartData(baseData) {
let data = baseData.map(i =>{
@@ -109,11 +131,13 @@ export function setBarChartData(baseData) {
return data
};
/**
* 將一段時間均分成多段的時間點
* @param {string} startTime 開始時間(ex: 434) 總秒數
* @param {string} endTime 結束時間(ex: 259065) 總秒數
* @param {number} amount 切分成多少段
* @returns {array} 均分成多段的時間 array
* Divides a time range into evenly spaced time points.
*
* @param {number} minTime - The start time in seconds.
* @param {number} maxTime - The end time in seconds.
* @param {number} amount - The number of time points to generate.
* @returns {Array<number>} An array of evenly spaced, rounded time
* values in seconds.
*/
export function timeRange(minTime, maxTime, amount) {
// x 軸(時間軸)的範圍是最大-最小,從最小值按照 index 累加間距到最大值
@@ -129,11 +153,13 @@ export function timeRange(minTime, maxTime, amount) {
return timeRange;
};
/**
* 將 y 軸的值分割成跟 x 時間軸一樣的等份,使用統計學 R 語言算出貝茲曲線攻勢
* @param {array} data 切分成多少段
* @param {number} yAmount 切分成多少段
* @param {number} yMax y 最大值
* @returns {array} 均分成多段的時間 array
* Generates smooth Y-axis values using cubic Bezier interpolation
* to produce evenly spaced ticks matching the X-axis divisions.
*
* @param {Array<{x: number, y: number}>} data - The source data points.
* @param {number} yAmount - The desired number of Y-axis ticks.
* @param {number} yMax - The maximum Y value (unused, kept for API).
* @returns {Array<number>} An array of interpolated Y values.
*/
export function yTimeRange(data, yAmount, yMax) {
const yRange = [];
@@ -171,10 +197,11 @@ export function yTimeRange(data, yAmount, yMax) {
return yRange;
};
/**
* 找出選擇的時間點的 index
* @param {array} data xVal
* @param {number} xValue x 值
* @returns {numver} x index
* Finds the index of the closest value in an array to the given target.
*
* @param {Array<number>} data - The array of values to search.
* @param {number} xValue - The target value to find the closest match for.
* @returns {number} The index of the closest value in the array.
*/
export function getXIndex(data, xValue) {
let closestIndex = xValue; // 假定第一个元素的索引是 0
@@ -192,9 +219,11 @@ export function getXIndex(data, xValue) {
return closestIndex;
};
/**
* 有 dhms 表達的時間單位
* @param {number} seconds 總秒數
* @returns {string}
* Formats a duration in seconds to a compact string with d/h/m/s units.
*
* @param {number} seconds - The total number of seconds.
* @returns {string|null} The formatted string (e.g. "2d3h15m30s"),
* or null if the input is NaN.
*/
export function formatTime(seconds) {
if(!isNaN(seconds)) {
@@ -221,9 +250,10 @@ export function formatTime(seconds) {
}
}
/**
* 只顯示最大的兩個單位
* @param {array} times 時間
* @returns {array}
* Truncates each time string to show only the two largest time units.
*
* @param {Array<string>} times - Array of duration strings (e.g. "2d3h15m30s").
* @returns {Array<string>} Array of truncated strings (e.g. "2d 3h").
*/
export function formatMaxTwo(times) {
const formattedTimes = [];

View File

@@ -1,19 +1,29 @@
// The Lucia project.
// Copyright 2023-2026 DSP, inc. All rights reserved.
// Authors:
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
// imacat.yang@dsp.im (imacat), 2023/9/23
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
/** @module shortScaleNumber Short-scale number formatting. */
/**
* 數量單位轉換,輸出 k m b t 數值。
* @param {number} number 總秒數
* @returns {string}
* Converts a number to a short-scale abbreviated string with a suffix
* (k for thousands, m for millions, b for billions, t for trillions).
*
* Values are rounded up to one decimal place.
*
* @param {number} number - The number to abbreviate.
* @returns {string} The abbreviated string (e.g. "1.5k ", "2.3m ").
*/
export default function shortScaleNumber(number) {
const abbreviations = ["", "k", "m", "b", "t"];
let index = 0;
let num = number;
// 確保在轉換數字時,不會超出索引範圍。如果 index 已經達到了最後一個單位縮寫t那麼我們就不再進行轉換以免超出數組的範圍。
while (num >= 1000 && index < abbreviations.length - 1) {
num /= 1000;
index++;
}
// 使用 Math.ceil 來無條件進位
num = Math.ceil(num * 10) / 10;
return num + abbreviations[index] + " " ;
}

View File

@@ -1,43 +1,52 @@
// The Lucia project.
// Copyright 2023-2026 DSP, inc. All rights reserved.
// Authors:
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
// imacat.yang@dsp.im (imacat), 2023/9/23
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
/** @module sortNumEngZhtw Sorting for mixed numeric/text data. */
/**
* 數字、英文、中文,排序
* @param {array} data List
* @returns
* Sorts an array of mixed numeric and text values in place.
*
* Numbers come first (ascending), followed by text sorted by
* Traditional Chinese locale (zh-Hant-TW).
*
* @param {Array<string|number>} data - The array to sort.
* @returns {Array<string|number>} The sorted array (same reference).
*/
export function sortNumEngZhtw(data) {
return data.sort((a, b) => {
// 檢查兩個值是否都是數字
const isANumber = !isNaN(parseFloat(a)) && isFinite(a);
const isBNumber = !isNaN(parseFloat(b)) && isFinite(b);
// 如果兩個值都是數字,直接比較大小
if (isANumber && isBNumber) return parseFloat(a) - parseFloat(b);
// 如果其中一個值是數字,將數字視為最小,排在前面
if (isANumber) return -1;
if (isBNumber) return 1;
// 其他情況下,使用 localeCompare 方法進行中文排序
return a.localeCompare(b, 'zh-Hant-TW', { sensitivity: 'accent' });
});
}
/**
* 數字、英文、中文,給 Filter Table 排序
* @param {string} a label
* @param {string} b label
* @returns
* Comparator function for sorting mixed numeric and text values.
*
* Suitable for use as an Array.sort() callback. Numbers sort before
* text, and text is sorted by Traditional Chinese locale.
*
* @param {string|number} a - First value to compare.
* @param {string|number} b - Second value to compare.
* @returns {number} Negative if a < b, positive if a > b, zero if equal.
*/
export function sortNumEngZhtwForFilter(a, b) {
// 檢查兩個值是否都是數字
const isANumber = !isNaN(parseFloat(a)) && isFinite(a);
const isBNumber = !isNaN(parseFloat(b)) && isFinite(b);
// 如果兩個值都是數字,直接比較大小
if (isANumber && isBNumber) return parseFloat(a) - parseFloat(b);
// 如果其中一個值是數字,將數字視為最小,排在前面
if (isANumber) return -1;
if (isBNumber) return 1;
// 其他情況下,使用 localeCompare 方法進行中文排序
return a.localeCompare(b, 'zh-Hant-TW', { sensitivity: 'accent' });
}

View File

@@ -1,11 +1,23 @@
// The Lucia project.
// Copyright 2023-2026 DSP, inc. All rights reserved.
// Authors:
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
// imacat.yang@dsp.im (imacat), 2023/9/23
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
/** @module timeLabel Time formatting and chart axis tick utilities. */
import moment from 'moment';
/** @constant {number} Number of decimal places for formatted time values. */
const TOFIXED_DEICMAL = 1;
/**
* 若想要有等差的Y軸刻度則必須確保index是乘以一個共通的被乘數
* 此被乘數就是 stepSize
* @param {number} maxTimeInSecond
* @returns {object} {resultStepSize, unitToUse}
* Calculates the step size and time unit for evenly spaced Y-axis ticks.
*
* @param {number} maxTimeInSecond - The maximum time value in seconds.
* @param {number} numOfParts - The number of equal parts to divide into.
* @returns {{resultStepSize: number, unitToUse: string}} The step size
* and the time unit character ("d", "h", "m", or "s").
*/
export const getStepSizeOfYTicks = (maxTimeInSecond, numOfParts) => {
const {unitToUse, timeValue} = getTimeUnitAndValueToUse(maxTimeInSecond);
@@ -16,9 +28,12 @@ export const getStepSizeOfYTicks = (maxTimeInSecond, numOfParts) => {
}
/**
* Convert second to possibly day or hour or mins.
* @param {number} secondToDecide
* @returns {object} {unitToUse, timeValue} where timeValue is measured in unitToUse
* Determines the most appropriate time unit for a given number of seconds
* and converts the value to that unit.
*
* @param {number} secondToDecide - The time in seconds to evaluate.
* @returns {{unitToUse: string, timeValue: number}} The chosen unit
* character ("d", "h", "m", or "s") and the converted time value.
*/
const getTimeUnitAndValueToUse = (secondToDecide) => {
const day = 24 * 60 * 60;
@@ -52,12 +67,13 @@ const getTimeUnitAndValueToUse = (secondToDecide) => {
};
/**
* For the display of PrimeVue chart.
* According to the max time of the data given,
* split the Y axis into equal part as ticks.
* @param {number} stepSize stepSize of Y axis
* @param {number} index index of current visiting tick on Y axis
* @param {string} unitToUse time unit to display; "dhms" usually
* Formats a Y-axis tick value by multiplying the step size by the
* tick index and appending the time unit suffix.
*
* @param {number} stepSize - The step size between Y-axis ticks.
* @param {number} index - The zero-based index of the current tick.
* @param {string} unitToUse - The time unit suffix ("d", "h", "m", or "s").
* @returns {string} The formatted tick label (e.g. "2.5h").
*/
export function getYTicksByIndex(stepSize, index, unitToUse){
const rawStepsizeMultIndex = (stepSize * index).toString();
@@ -67,10 +83,12 @@ export function getYTicksByIndex(stepSize, index, unitToUse){
};
/**
* 將秒數轉換成帶有時間單位的格式
* @param {number} second 總秒數
* @param {number} fixedNumber 小數點後幾位
* @returns {string} 轉換完的格式(ex: 1 day, 6.8 hrs)
* Converts seconds to a human-readable time string with full unit names.
*
* @param {number} second - The total number of seconds.
* @param {number} [fixedNumber=0] - Number of decimal places.
* @returns {string} The formatted string (e.g. "1 days", "6.8 hrs",
* "30 mins", "5 sec").
*/
export function getTimeLabel(second, fixedNumber = 0) {
const day = 24 * 60 * 60;
@@ -99,15 +117,14 @@ export function getTimeLabel(second, fixedNumber = 0) {
}
/**
* 將秒數轉換成帶有縮寫時間單位的格式
* @param {number} second 總秒數
* @param {number} fixedNumber 小數點後幾位
* @returns {string} 轉換完的格式(ex: 1 d, 6.8 h)
* 如果秒數second大於等於一天86400 秒),返回以天為單位的時間,帶有 d 標誌。
* 如果秒數小於一天但大於等於一小時3600 秒),返回以小時為單位的時間,帶有 h 標誌。
* 如果秒數小於一小時但大於等於一分鐘60 秒),返回以分鐘為單位的時間,帶有 m 標誌。
* 如果秒數等於 0 秒,返回 "0s"。
* 如果秒數小於 60 秒但大於 0 秒,返回原始秒數,帶有 s 標誌。
* Converts seconds to a compact time string with abbreviated unit suffixes.
*
* Automatically selects the most appropriate unit: "d" for days,
* "h" for hours, "m" for minutes, or "s" for seconds.
*
* @param {number} second - The total number of seconds.
* @param {number} [fixedNumber=0] - Number of decimal places.
* @returns {string} The formatted string (e.g. "1d", "6.8h", "30m", "5s").
*/
export function simpleTimeLabel(second, fixedNumber = 0) {
const day = 24 * 60 * 60;
@@ -132,14 +149,16 @@ export function simpleTimeLabel(second, fixedNumber = 0) {
return second + "s";
}
/**
* 考慮到每包資料內的時間範圍有的大有的小,
* 需要根據不同時間範圍級別的資料刻劃不同的Y軸座標
* 因此需要根據最大的時間值來決定Y軸刻度怎麼切分。
* 跟隨最大值的時間單位,將秒數轉換成帶有縮寫時間單位的格式
* @param {number} second 要轉換單位的秒數
* @param {number} max 該 data 中的最大值
* @param {number} fixedNumber 小數點後幾位
* @returns {string} 轉換完的格式(ex: 1 d, 6.8 h)
* Converts seconds to a time string using the same unit as the maximum
* value in the dataset, ensuring consistent Y-axis labels.
*
* The unit is determined by the max value: days if max > 1 day,
* hours if max > 1 hour, minutes if max > 1 minute, else seconds.
*
* @param {number} second - The time value in seconds to format.
* @param {number} max - The maximum time value in the dataset (seconds).
* @param {number} [fixedNumber=0] - Number of decimal places.
* @returns {string} The formatted string (e.g. "1.5d", "6.8h").
*/
export function followTimeLabel(second, max, fixedNumber = 0) {
const day = 24 * 60 * 60;
@@ -192,15 +211,15 @@ export function followTimeLabel(second, max, fixedNumber = 0) {
/**
* Select an appropriate time format based on the time difference.
* 根據時間差距選擇適合的時間格式
* 舉例 月: 2022/06
* 舉例 日: 06/06
* 舉例 時: 03/05 12:00
* 舉例 分: 03/05 12:15
* 舉例 秒: 09:05:32
* @param {Array<number>} timestamps - An array of timestamps.
* @returns {string} - The suitable time format string.
* Selects an appropriate moment.js date format string based on the
* difference between the minimum and maximum timestamps.
*
* Returns "HH:mm:ss" for sub-minute ranges, "MM/DD HH:mm" for
* sub-day ranges, and "YYYY/MM/DD" for longer ranges.
*
* @param {number} minTimeStamp - The minimum timestamp in seconds.
* @param {number} maxTimeStamp - The maximum timestamp in seconds.
* @returns {string} A moment.js format string.
*/
export const setTimeStringFormatBaseOnTimeDifference = (minTimeStamp, maxTimeStamp) => {
const timeDifferenceInSeconds = maxTimeStamp - minTimeStamp;
@@ -222,11 +241,13 @@ export const setTimeStringFormatBaseOnTimeDifference = (minTimeStamp, maxTimeSta
};
/**
* Converts an array of Unix timestamps to formatted date strings based on the
* specified format for use as axis ticks.
* 根據指定的格式將 Unix 時間戳數組轉換為軸標籤的格式化日期字符串。
* @param {Array<number>} timeStampArr
* @param {string} timeFormat For example, 'MM/DD'
* Converts an array of Unix timestamps to formatted date strings for
* use as chart axis tick labels.
*
* @param {Array<number>} timeStampArr - Array of Unix timestamps.
* @param {string} timeFormat - A moment.js format string (e.g. "MM/DD").
* @returns {Array<string>|undefined} Array of formatted date strings,
* or undefined if timeStampArr is falsy.
*/
export const mapTimestampToAxisTicksByFormat = (timeStampArr, timeFormat) => {
if (timeStampArr) {

View File

@@ -1,5 +1,19 @@
// The Lucia project.
// Copyright 2023-2026 DSP, inc. All rights reserved.
// Authors:
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
// imacat.yang@dsp.im (imacat), 2023/9/23
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
/** @module myPiniaPlugin Custom Pinia plugin for store debugging. */
import { PiniaPluginContext } from 'pinia';
/**
* A Pinia plugin that logs all store mutations and state changes
* to the console for debugging purposes.
*
* @param {PiniaPluginContext} context - The Pinia plugin context.
*/
const myPiniaPlugin = (context: PiniaPluginContext) => {
const { store } = context;

View File

@@ -1,3 +1,14 @@
// The Lucia project.
// Copyright 2023-2026 DSP, inc. All rights reserved.
// Authors:
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
// imacat.yang@dsp.im (imacat), 2023/9/23
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
/**
* @module router Vue Router configuration with route definitions,
* navigation guards, and authentication redirect logic.
*/
import { createRouter, createWebHistory, } from "vue-router";
import AuthContainer from '@/views/AuthContainer.vue';
import MainContainer from '@/views/MainContainer.vue';

View File

@@ -1,3 +1,14 @@
// The Lucia project.
// Copyright 2023-2026 DSP, inc. All rights reserved.
// Authors:
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
// imacat.yang@dsp.im (imacat), 2023/9/23
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
/**
* @module stores/acctMgmt Account management store for CRUD
* operations on user accounts (admin functionality).
*/
import { defineStore } from 'pinia';
import apiClient from '@/api/client.js';
import apiError from '@/module/apiError';

View File

@@ -1,9 +1,22 @@
// The Lucia project.
// Copyright 2023-2026 DSP, inc. All rights reserved.
// Authors:
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
// imacat.yang@dsp.im (imacat), 2023/9/23
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
/**
* @module stores/allMapData Process map data store for fetching,
* filtering, and managing Discover Map page state including
* filter rules, data layers, and process map data.
*/
import { defineStore } from "pinia";
import moment from "moment";
import apiClient from "@/api/client.js";
import apiError from '@/module/apiError.js';
import { Decimal } from 'decimal.js';
/** Pinia store for Discover Map page data and filter management. */
export const useAllMapDataStore = defineStore('allMapDataStore', {
state: () => ({
baseLogId: null,

View File

@@ -1,7 +1,19 @@
// The Lucia project.
// Copyright 2023-2026 DSP, inc. All rights reserved.
// Authors:
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
// imacat.yang@dsp.im (imacat), 2023/9/23
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
/**
* @module stores/compare Performance comparison dashboard store
* for fetching and managing comparison data between two files.
*/
import { defineStore } from "pinia";
import apiClient from "@/api/client.js";
import apiError from '@/module/apiError.js';
/** Pinia store for the Compare Dashboard page data. */
export const useCompareStore = defineStore('compareStore', {
state: () => ({
allCompareDashboardData: null,

View File

@@ -1,3 +1,14 @@
// The Lucia project.
// Copyright 2023-2026 DSP, inc. All rights reserved.
// Authors:
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
// imacat.yang@dsp.im (imacat), 2023/9/23
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
/**
* @module stores/conformance Conformance checking store for managing
* rule definitions, conformance check results, and report data.
*/
import { defineStore } from "pinia";
import moment from "moment";
import { Decimal } from 'decimal.js';
@@ -5,6 +16,7 @@ import abbreviateNumber from '@/module/abbreviateNumber.js';
import apiClient from "@/api/client.js";
import apiError from '@/module/apiError.js';
/** Pinia store for conformance checking and rule management. */
export const useConformanceStore = defineStore('conformanceStore', {
state: () => ({
conformanceLogId: null, // log 檔

View File

@@ -1,12 +1,22 @@
// The Lucia project.
// Copyright 2023-2026 DSP, inc. All rights reserved.
// Authors:
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
// imacat.yang@dsp.im (imacat), 2023/9/23
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
/**
* @module stores/conformanceInput Temporary storage for user input
* during conformance rule configuration. Unlike stores/conformance
* which handles API integration, this store focuses on caching
* user selections before submission.
*/
import { defineStore } from "pinia";
import moment from 'moment';
/**
* stores/conformanceInput.js 與 stores/conformance.js 的差別在於
* 本store側重在暫存使用者的輸入
* 而後者則側重在 API 的串接之所需
* Pinia store for caching user input during conformance rule editing.
*/
export const useConformanceInputStore = defineStore('conformanceInputStore', {
state: () => ({
inputDataToSave: {

View File

@@ -1,10 +1,19 @@
// The Lucia project.
// Copyright 2023-2026 DSP, inc. All rights reserved.
// Authors:
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
// imacat.yang@dsp.im (imacat), 2023/9/23
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
/**
* @module stores/cytoscapeStore Cytoscape node position persistence
* store for saving and restoring graph node positions to/from
* localStorage.
*/
import { defineStore } from 'pinia';
import { SAVE_KEY_NAME } from '@/constants/constants.js';
// nodePositions[graphId][direction][id]
// nodePositions[graphId][position]
interface Position {
x: number;

View File

@@ -1,3 +1,14 @@
// The Lucia project.
// Copyright 2023-2026 DSP, inc. All rights reserved.
// Authors:
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
// imacat.yang@dsp.im (imacat), 2023/9/23
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
/**
* @module stores/files File management store for listing, uploading,
* renaming, downloading, and deleting log/filter/rule files.
*/
import { defineStore } from "pinia";
import apiClient from "@/api/client.js";
import moment from 'moment';
@@ -6,6 +17,7 @@ import Swal from 'sweetalert2';
import { uploadFailedFirst, uploadFailedSecond, uploadloader, uploadSuccess, deleteSuccess } from '@/module/alertModal.js';
import { useLoadingStore } from '@/stores/loading';
/** Pinia store for file CRUD operations and upload workflow. */
export const useFilesStore = defineStore('filesStore', {
state: () => ({
allEventFiles: [

View File

@@ -1,10 +1,24 @@
// The Lucia project.
// Copyright 2023-2026 DSP, inc. All rights reserved.
// Authors:
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
// imacat.yang@dsp.im (imacat), 2023/9/23
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
/** @module stores/loading Global loading spinner state management. */
import { defineStore } from "pinia";
/** Pinia store for managing the global loading spinner visibility. */
export const useLoadingStore = defineStore('loadingStore', {
state: () => ({
/** Whether the loading spinner is currently visible. */
isLoading: true,
}),
actions: {
/**
* Sets the loading spinner visibility.
* @param {boolean} isLoadingBoolean - Whether to show the spinner.
*/
setIsLoading(isLoadingBoolean) {
this.isLoading = isLoadingBoolean;
}

View File

@@ -1,9 +1,21 @@
// The Lucia project.
// Copyright 2023-2026 DSP, inc. All rights reserved.
// Authors:
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
// imacat.yang@dsp.im (imacat), 2023/9/23
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
/**
* @module stores/login Authentication store for login, logout,
* token management, and user data.
*/
import { defineStore } from "pinia";
import axios from 'axios';
import apiClient from '@/api/client.js';
import apiError from '@/module/apiError.js';
import { deleteCookie, setCookie, setCookieWithoutExpiration, getCookie } from "../utils/cookieUtil";
/** Pinia store for authentication and user session management. */
export const useLoginStore = defineStore('loginStore', {
// data, methods, computed
// state, actions, getters

View File

@@ -1,3 +1,13 @@
// The Lucia project.
// Copyright 2023-2026 DSP, inc. All rights reserved.
// Authors:
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
// imacat.yang@dsp.im (imacat), 2023/9/23
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
/**
* @module stores/main Pinia store instance with persisted state plugin.
*/
import { createPinia } from 'pinia';
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate';

View File

@@ -1,3 +1,14 @@
// The Lucia project.
// Copyright 2023-2026 DSP, inc. All rights reserved.
// Authors:
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
// imacat.yang@dsp.im (imacat), 2023/9/23
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
/**
* @module stores/mapCompareStore Map comparison store for managing
* side-by-side process map comparison with insights data.
*/
import { defineStore } from 'pinia';
import { useAllMapDataStore } from '@/stores/allMapData';
import { INSIGHTS_FIELDS_AND_LABELS } from '@/constants/constants';

View File

@@ -1,3 +1,14 @@
// The Lucia project.
// Copyright 2023-2026 DSP, inc. All rights reserved.
// Authors:
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
// imacat.yang@dsp.im (imacat), 2023/9/23
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
/**
* @module stores/mapPathStore Cytoscape map interaction store for
* node/edge click highlighting with path insights visualization.
*/
import { defineStore } from 'pinia';
import { useAllMapDataStore } from '@/stores/allMapData';
import { INSIGHTS_FIELDS_AND_LABELS } from '@/constants/constants';

View File

@@ -1,18 +1,34 @@
// The Lucia project.
// Copyright 2023-2026 DSP, inc. All rights reserved.
// Authors:
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
// imacat.yang@dsp.im (imacat), 2023/9/23
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
/** @module stores/modal Account management modal state. */
import { defineStore } from 'pinia';
import { MODAL_ACCT_INFO, } from '@/constants/constants.js';
/** Pinia store for controlling account management modal visibility. */
export const useModalStore = defineStore('modalStore', {
state: () => ({
/** Whether a modal is currently open. */
isModalOpen: false,
/** The current modal type constant (e.g. MODAL_ACCT_INFO). */
whichModal: MODAL_ACCT_INFO,
}),
actions: {
/**
* Opens a modal of the specified type.
* @param {string} whichModal - The modal type constant.
*/
openModal(whichModal) {
this.isModalOpen = true;
this.whichModal = whichModal;
},
/** Closes the currently open modal. */
async closeModal(){
this.isModalOpen = false;
},
},
});
});

View File

@@ -1,12 +1,28 @@
// The Lucia project.
// Copyright 2023-2026 DSP, inc. All rights reserved.
// Authors:
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
// imacat.yang@dsp.im (imacat), 2023/9/23
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
/**
* @module stores/pageAdmin Navigation state management for tracking
* active, previous, and pending page states across route transitions
* and SweetAlert2 modal interactions.
*/
import { defineStore } from "pinia";
import { mapPageNameToCapitalUnifiedName } from "../utils/pageUtils";
/** @constant {boolean} Enable console logging for page admin debugging. */
const printPageAdminLog = false;
// There are at least two ways to trigger a page navigation:
// clicking the navigation button and refreshing the page.
// Therefore, we need to handle page transitions caused by both of these methods.
// 至少有兩種方式引起畫面的導向: 點選導覽按鈕與重新整理網頁
// 因此至少要處理這兩種方式所引起的畫面切換
/**
* Pinia store for managing navigation page state transitions.
*
* Handles two navigation triggers: clicking navigation buttons and
* page refreshes. Manages pending states for SweetAlert2 modal
* confirmation flows.
*/
export const usePageAdminStore = defineStore('pageAdminStore', {
state: () => ({
activePage: 'MAP',
@@ -20,6 +36,10 @@ export const usePageAdminStore = defineStore('pageAdminStore', {
getters: {
},
actions: {
/**
* Sets the active page based on the last matched route record.
* @param {Array} routeMatched - The route.matched array.
*/
setActivePageComputedByRoute(routeMatched){
if (routeMatched.length && routeMatched[routeMatched.length - 1]
&& routeMatched[routeMatched.length - 1].name) {
@@ -98,6 +118,10 @@ export const usePageAdminStore = defineStore('pageAdminStore', {
printPageAdminLog && console.log('clearShouldKeepPreviousPageBoolean()');
this.shouldKeepPreviousPage = false;
},
/**
* Stores the name of the currently opened map file.
* @param {string} fileName - The file name.
*/
setCurrentMapFile(fileName){
this.currentMapFile = fileName;
},

View File

@@ -1,7 +1,19 @@
// The Lucia project.
// Copyright 2023-2026 DSP, inc. All rights reserved.
// Authors:
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
// imacat.yang@dsp.im (imacat), 2023/9/23
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
/**
* @module stores/performance Performance analysis store for fetching
* and managing frequency/duration chart data.
*/
import { defineStore } from "pinia";
import apiClient from "@/api/client.js";
import apiError from '@/module/apiError.js';
/** Pinia store for the Discover Performance page data. */
export const usePerformanceStore = defineStore('performanceStore', {
state: () => ({
allPerformanceData: null,

View File

@@ -1,4 +1,15 @@
// The Lucia project.
// Copyright 2024-2026 DSP, inc. All rights reserved.
// Authors:
// cindy.chang@dsp.im (Cindy Chang), 2024/7/9
/**
* @module types/apiError.d
* Type declaration for the apiError module.
*/
/** Type declaration for the apiError module. */
declare module '@/module/apiError' {
/** The API error handler. */
const apiError: any;
export default apiError;
}

18
src/types/env.d.ts vendored
View File

@@ -1,9 +1,23 @@
// The Lucia project.
// Copyright 2024-2026 DSP, inc. All rights reserved.
// Authors:
// cindy.chang@dsp.im (Cindy Chang), 2024/7/9
/**
* @module types/env.d
* Type declarations for Vite environment variables.
*/
/** Vite environment variable types. */
interface ImportMetaEnv {
/** The application title. */
readonly VITE_APP_TITLE: string;
/** The base URL for the application. */
readonly BASE_URL: string;
// 更多環境變量...
// Add more environment variables as needed.
}
/** Augments ImportMeta with typed env. */
interface ImportMeta {
/** The typed environment variables. */
readonly env: ImportMetaEnv;
}

View File

@@ -1,6 +1,18 @@
// src/types/shims-vue.d.ts
// The Lucia project.
// Copyright 2024-2026 DSP, inc. All rights reserved.
// Authors:
// cindy.chang@dsp.im (Cindy Chang), 2024/7/9
// imacat.yang@dsp.im (imacat), 2026/3/6
/**
* @module types/shims-vue.d
* Vue single-file component type shim for
* TypeScript module resolution.
*/
/** Declares *.vue files as Vue components for TypeScript. */
declare module '*.vue' {
import { DefineComponent } from 'vue';
/** The default-exported Vue component. */
const component: DefineComponent<{}, {}, any>;
export default component;
}

13
src/types/store.d.ts vendored
View File

@@ -1,8 +1,21 @@
// The Lucia project.
// Copyright 2024-2026 DSP, inc. All rights reserved.
// Authors:
// cindy.chang@dsp.im (Cindy Chang), 2024/7/9
// imacat.yang@dsp.im (imacat), 2026/3/6
/**
* @module types/store.d
* Pinia custom properties type augmentation
* for router access in stores.
*/
import 'pinia';
import { Router } from 'vue-router';
declare module 'pinia' {
/** Extends Pinia stores with router access. */
export interface PiniaCustomProperties {
/** The Vue Router instance. */
$router: Router;
}
}

Some files were not shown because too many files have changed in this diff Show More