@dexteel/mesf-core 7.5.2 → 7.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.esm.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { HubConnectionBuilder, LogLevel } from '@microsoft/signalr';
2
2
  export * from '@microsoft/signalr';
3
3
  export { LicenseManager } from 'ag-grid-enterprise';
4
- import { styled, DialogTitle as DialogTitle$1, DialogContent as DialogContent$1, DialogActions as DialogActions$1, Grid2, Button, Box, MenuItem, ListItemIcon, createTheme, TextField, Alert as Alert$2, useTheme, InputAdornment, Popover, MenuList, ListItemText, alpha, Dialog as Dialog$1, Paper, List, ListItem, Chip, SvgIcon, Typography as Typography$1, Checkbox, IconButton as IconButton$1, CircularProgress, FormControl, FormHelperText, FormControlLabel, Snackbar, DialogContentText, Badge, InputLabel, Select, Input, Divider, Card, CardContent, CardActions, Collapse, Tooltip, CssBaseline, AppBar, Toolbar, Container, Menu, Switch, Hidden, Drawer, useMediaQuery, ListSubheader, ListItemButton, Autocomplete as Autocomplete$1, TableContainer, Table, TableHead, TableRow, TableCell, TableBody, debounce, StyledEngineProvider, ThemeProvider, ListItemSecondaryAction } from '@mui/material';
4
+ import { styled, DialogTitle as DialogTitle$1, DialogContent as DialogContent$1, DialogActions as DialogActions$1, Grid2, Button, Box, MenuItem, ListItemIcon, createTheme, TextField, Alert as Alert$2, useTheme, InputAdornment, Popover, MenuList, ListItemText, alpha, Dialog as Dialog$1, Paper, List, ListItem, Chip, SvgIcon, Typography as Typography$1, Checkbox, IconButton as IconButton$1, CircularProgress, FormControl, FormHelperText, FormControlLabel, Snackbar, DialogContentText, Badge, InputLabel, Select, Input, Divider, Card, CardContent, CardActions, Collapse, Tooltip, CssBaseline, AppBar, Toolbar, Container, Menu, Switch, Hidden, Drawer, Grid, Accordion, AccordionSummary, AccordionDetails, Tabs, Tab, Autocomplete as Autocomplete$1, useMediaQuery, ListSubheader, ListItemButton, TableContainer, Table, TableHead, TableRow, TableCell, TableBody, debounce, StyledEngineProvider, ThemeProvider, ListItemSecondaryAction } from '@mui/material';
5
5
  import { useMutation, useQuery, useQueryClient, QueryClient, QueryClientProvider } from '@tanstack/react-query';
6
6
  import * as React from 'react';
7
7
  import React__default, { createContext, useContext, useRef, useState, useEffect, useCallback, useMemo, Component, lazy, Suspense } from 'react';
@@ -25,7 +25,7 @@ import DeleteIcon from '@mui/icons-material/Delete';
25
25
  import EditIcon from '@mui/icons-material/Edit';
26
26
  import FindInPageIcon from '@mui/icons-material/FindInPage';
27
27
  import PlaylistAddIcon from '@mui/icons-material/PlaylistAdd';
28
- import { ArrowRight, ArrowBackRounded, ArrowForwardRounded, SkipNext, ChevronLeft, ChevronRight, Cloud, Square as Square$1, Timeline, Send, Menu as Menu$1, People, Storage, Group, Assignment, Chat, ViewList, Build, Settings as Settings$2, FastRewind, FastForward, ZoomIn, Restore, Lock, Create, Delete, Folder, InsertChart, Search, PlaylistAdd, DragIndicator, Save, AttachFile, CloudUpload, GetApp } from '@mui/icons-material';
28
+ import { ArrowRight, ArrowBackRounded, ArrowForwardRounded, SkipNext, ChevronLeft, ChevronRight, Cloud, Square as Square$1, Timeline, Send, Menu as Menu$1, People, Storage, Group, Assignment, Chat, ViewList, Build, Settings as Settings$2, Code as Code$1, FastRewind, FastForward, ZoomIn, Restore, Lock, Create, Delete, Folder, InsertChart, Search, PlaylistAdd, DragIndicator, Save, AttachFile, CloudUpload, GetApp } from '@mui/icons-material';
29
29
  import FormatListBulletedSharpIcon from '@mui/icons-material/FormatListBulletedSharp';
30
30
  import LockOutlinedIcon from '@mui/icons-material/LockOutlined';
31
31
  import CloseIcon from '@mui/icons-material/Close';
@@ -55,6 +55,9 @@ import { DndProvider } from 'react-dnd';
55
55
  import { HTML5Backend } from 'react-dnd-html5-backend';
56
56
  import MenuIcon from '@mui/icons-material/Menu';
57
57
  import ShowChartIcon from '@mui/icons-material/ShowChart';
58
+ import { DateTimePicker as DateTimePicker$1 } from '@mui/x-date-pickers/DateTimePicker';
59
+ import ClearAllIcon from '@mui/icons-material/ClearAll';
60
+ import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
58
61
  import axios from 'axios';
59
62
  import { LocalizationProvider as LocalizationProvider$1 } from '@mui/x-date-pickers/LocalizationProvider';
60
63
  import InsertChartIcon from '@mui/icons-material/InsertChart';
@@ -10964,6 +10967,448 @@ var Settings = function () {
10964
10967
  return React__default.createElement(SettingsPage, null);
10965
10968
  };
10966
10969
 
10970
+ var getDefaultValue = function (dataType) {
10971
+ var type = dataType.toLowerCase();
10972
+ if ([
10973
+ "int",
10974
+ "bigint",
10975
+ "smallint",
10976
+ "tinyint",
10977
+ "decimal",
10978
+ "float",
10979
+ "real",
10980
+ "money",
10981
+ "numeric",
10982
+ ].includes(type)) {
10983
+ return "";
10984
+ }
10985
+ if (["datetime", "datetime2", "date", "smalldatetime"].includes(type)) {
10986
+ return null;
10987
+ }
10988
+ if (type === "bit") {
10989
+ return false;
10990
+ }
10991
+ return "";
10992
+ };
10993
+ var renderInput = function (param, onChange, disabled) {
10994
+ var type = param.dataType.toLowerCase();
10995
+ if (["int", "bigint", "smallint", "tinyint"].includes(type)) {
10996
+ return (React__default.createElement(TextField, { type: "number", label: param.parameterName, value: param.value, onChange: function (e) {
10997
+ return onChange(e.target.value === "" ? "" : Number(e.target.value));
10998
+ }, disabled: disabled, fullWidth: true, size: "small", inputProps: { step: 1 } }));
10999
+ }
11000
+ if (["decimal", "float", "real", "money", "numeric"].includes(type)) {
11001
+ return (React__default.createElement(TextField, { type: "number", label: param.parameterName, value: param.value, onChange: function (e) {
11002
+ return onChange(e.target.value === "" ? "" : Number(e.target.value));
11003
+ }, disabled: disabled, fullWidth: true, size: "small", inputProps: { step: "any" } }));
11004
+ }
11005
+ if (["datetime", "datetime2", "date", "smalldatetime"].includes(type)) {
11006
+ return (React__default.createElement(DateTimePicker$1, { label: param.parameterName, value: param.value ? moment$8(param.value) : null, onChange: function (newValue) { return onChange((newValue === null || newValue === void 0 ? void 0 : newValue.toDate()) || null); }, disabled: disabled, slotProps: {
11007
+ textField: {
11008
+ fullWidth: true,
11009
+ size: "small",
11010
+ },
11011
+ } }));
11012
+ }
11013
+ if (type === "bit") {
11014
+ return (React__default.createElement(FormControlLabel, { control: React__default.createElement(Checkbox, { checked: !!param.value, onChange: function (e) { return onChange(e.target.checked); }, disabled: disabled }), label: param.parameterName }));
11015
+ }
11016
+ // Default: text field for varchar, nvarchar, char, etc.
11017
+ return (React__default.createElement(TextField, { label: param.parameterName, value: param.value, onChange: function (e) { return onChange(e.target.value); }, disabled: disabled, fullWidth: true, size: "small", multiline: type.includes("text") || type.includes("max"), rows: type.includes("text") || type.includes("max") ? 3 : 1 }));
11018
+ };
11019
+ var DynamicParameterForm = function (_a) {
11020
+ var parameters = _a.parameters, onExecute = _a.onExecute, isExecuting = _a.isExecuting;
11021
+ var _b = useState([]), formState = _b[0], setFormState = _b[1];
11022
+ useEffect(function () {
11023
+ var initialState = parameters.map(function (p) { return ({
11024
+ parameterName: p.ParameterName,
11025
+ include: !p.IsOptional, // Required params start included and locked
11026
+ sendAsNull: false,
11027
+ value: getDefaultValue(p.DataType),
11028
+ dataType: p.DataType,
11029
+ isOptional: p.IsOptional,
11030
+ }); });
11031
+ setFormState(initialState);
11032
+ }, [parameters]);
11033
+ var updateParam = function (index, field, value) {
11034
+ setFormState(function (prev) {
11035
+ var _a;
11036
+ var updated = __spreadArray([], prev, true);
11037
+ updated[index] = __assign(__assign({}, updated[index]), (_a = {}, _a[field] = value, _a));
11038
+ // If "sendAsNull" is checked, include must be true
11039
+ if (field === "sendAsNull" && value === true) {
11040
+ updated[index].include = true;
11041
+ }
11042
+ // If "include" is unchecked, sendAsNull must be false
11043
+ if (field === "include" && value === false) {
11044
+ updated[index].sendAsNull = false;
11045
+ }
11046
+ return updated;
11047
+ });
11048
+ };
11049
+ var handleExecute = function () {
11050
+ onExecute(formState);
11051
+ };
11052
+ if (parameters.length === 0) {
11053
+ return (React__default.createElement(Paper, { sx: { p: 2 } },
11054
+ React__default.createElement(Typography$1, { variant: "body2", color: "text.secondary" }, "This stored procedure has no parameters."),
11055
+ React__default.createElement(Button, { variant: "contained", onClick: handleExecute, disabled: isExecuting, sx: { mt: 2 } }, isExecuting ? "Executing..." : "Execute")));
11056
+ }
11057
+ return (React__default.createElement(Paper, { sx: { p: 2 } },
11058
+ React__default.createElement(Typography$1, { variant: "subtitle1", sx: { mb: 2, fontWeight: 600 } }, "Parameters"),
11059
+ React__default.createElement(Grid, { container: true, spacing: 2 }, formState.map(function (param, index) {
11060
+ var isInputDisabled = !param.include || param.sendAsNull;
11061
+ var isIncludeDisabled = !param.isOptional; // Required params can't be unchecked
11062
+ return (React__default.createElement(Grid, { item: true, xs: 12, key: param.parameterName },
11063
+ React__default.createElement(Box, { sx: {
11064
+ display: "flex",
11065
+ alignItems: "center",
11066
+ gap: 2,
11067
+ p: 1,
11068
+ borderRadius: 1,
11069
+ bgcolor: param.include ? "transparent" : "action.hover",
11070
+ } },
11071
+ React__default.createElement(FormControlLabel, { control: React__default.createElement(Checkbox, { checked: param.include, onChange: function (e) {
11072
+ return updateParam(index, "include", e.target.checked);
11073
+ }, disabled: isIncludeDisabled, size: "small" }), label: "Include", sx: { minWidth: 100 } }),
11074
+ React__default.createElement(FormControlLabel, { control: React__default.createElement(Checkbox, { checked: param.sendAsNull, onChange: function (e) {
11075
+ return updateParam(index, "sendAsNull", e.target.checked);
11076
+ }, disabled: !param.include, size: "small" }), label: "NULL", sx: { minWidth: 80 } }),
11077
+ React__default.createElement(Box, { sx: { flexGrow: 1 } }, renderInput(param, function (value) { return updateParam(index, "value", value); }, isInputDisabled)),
11078
+ React__default.createElement(Typography$1, { variant: "caption", color: "text.secondary", sx: { minWidth: 80 } },
11079
+ param.dataType,
11080
+ !param.isOptional && (React__default.createElement(Typography$1, { component: "span", color: "error", sx: { ml: 0.5 } }, "*"))))));
11081
+ })),
11082
+ React__default.createElement(Box, { sx: { mt: 3, display: "flex", justifyContent: "flex-end" } },
11083
+ React__default.createElement(Button, { variant: "contained", onClick: handleExecute, disabled: isExecuting, size: "large" }, isExecuting ? "Executing..." : "Execute"))));
11084
+ };
11085
+
11086
+ var SESSION_KEY = "sp-executor-authenticated";
11087
+ var getCurrentPin = function () {
11088
+ var now = new Date();
11089
+ var hours = now.getHours().toString().padStart(2, "0");
11090
+ var minutes = now.getMinutes().toString().padStart(2, "0");
11091
+ return "".concat(hours).concat(minutes);
11092
+ };
11093
+ var PinGate = function (_a) {
11094
+ var children = _a.children;
11095
+ var _b = useState(function () {
11096
+ return sessionStorage.getItem(SESSION_KEY) === "true";
11097
+ }), isAuthenticated = _b[0], setIsAuthenticated = _b[1];
11098
+ var _c = useState(""), pin = _c[0], setPin = _c[1];
11099
+ var _d = useState(""), error = _d[0], setError = _d[1];
11100
+ var handleUnlock = function () {
11101
+ if (pin === getCurrentPin()) {
11102
+ sessionStorage.setItem(SESSION_KEY, "true");
11103
+ setIsAuthenticated(true);
11104
+ setError("");
11105
+ }
11106
+ else {
11107
+ setError("Incorrect PIN. Please try again.");
11108
+ setPin("");
11109
+ }
11110
+ };
11111
+ var handleKeyDown = function (e) {
11112
+ if (e.key === "Enter") {
11113
+ handleUnlock();
11114
+ }
11115
+ };
11116
+ if (isAuthenticated) {
11117
+ return React__default.createElement(React__default.Fragment, null, children);
11118
+ }
11119
+ return (React__default.createElement(Box, { sx: {
11120
+ display: "flex",
11121
+ justifyContent: "center",
11122
+ alignItems: "center",
11123
+ minHeight: "60vh",
11124
+ } },
11125
+ React__default.createElement(Paper, { elevation: 3, sx: {
11126
+ p: 4,
11127
+ maxWidth: 400,
11128
+ width: "100%",
11129
+ textAlign: "center",
11130
+ } },
11131
+ React__default.createElement(LockIcon, { sx: { fontSize: 48, color: "primary.main", mb: 2 } }),
11132
+ React__default.createElement(Typography$1, { variant: "h5", sx: { mb: 1, fontWeight: 600 } }, "Access Required"),
11133
+ React__default.createElement(Typography$1, { variant: "body2", color: "text.secondary", sx: { mb: 3 } }, "Enter PIN to access the SP Executor"),
11134
+ React__default.createElement(TextField, { type: "password", label: "PIN", variant: "outlined", fullWidth: true, value: pin, onChange: function (e) { return setPin(e.target.value); }, onKeyDown: handleKeyDown, error: !!error, helperText: error, sx: { mb: 2 }, autoFocus: true }),
11135
+ React__default.createElement(Button, { variant: "contained", fullWidth: true, onClick: handleUnlock, disabled: !pin }, "Unlock"))));
11136
+ };
11137
+
11138
+ var defaultColDef = {
11139
+ flex: 1,
11140
+ filter: false,
11141
+ sortable: true,
11142
+ resizable: true,
11143
+ };
11144
+ var parseResults = function (data) {
11145
+ if (!data || !data.tables)
11146
+ return [];
11147
+ return data.tables.map(function (table) {
11148
+ var _a;
11149
+ var rows = table.rows || [];
11150
+ var columns = rows.length > 0
11151
+ ? Object.keys(rows[0])
11152
+ : ((_a = table.columns) === null || _a === void 0 ? void 0 : _a.map(function (c) { return c.name; })) || [];
11153
+ return { rows: rows, columns: columns };
11154
+ });
11155
+ };
11156
+ var formatTimestamp = function (date) {
11157
+ return date.toLocaleTimeString("en-US", {
11158
+ hour: "2-digit",
11159
+ minute: "2-digit",
11160
+ second: "2-digit",
11161
+ hour12: false,
11162
+ });
11163
+ };
11164
+ var formatDuration = function (ms) {
11165
+ if (ms < 1000)
11166
+ return "".concat(ms, "ms");
11167
+ return "".concat((ms / 1000).toFixed(2), "s");
11168
+ };
11169
+ var formatParameters = function (params) {
11170
+ if (params.length === 0)
11171
+ return "No parameters";
11172
+ return params
11173
+ .map(function (p) { return "".concat(p.name, "=").concat(p.value === null ? "NULL" : p.value); })
11174
+ .join(", ");
11175
+ };
11176
+ var ResultItem = function (_a) {
11177
+ var result = _a.result, onDelete = _a.onDelete, defaultExpanded = _a.defaultExpanded;
11178
+ var _b = useState(0), activeTab = _b[0], setActiveTab = _b[1];
11179
+ var resultSets = useMemo(function () { return parseResults(result.data); }, [result.data]);
11180
+ var getColumnDefs = function (columns) {
11181
+ return columns.map(function (col) { return ({
11182
+ field: col,
11183
+ headerName: col,
11184
+ minWidth: 100,
11185
+ flex: 1,
11186
+ cellRenderer: function (params) {
11187
+ var value = params.value;
11188
+ if (value === null || value === undefined) {
11189
+ return (React__default.createElement("span", { style: { color: "#999", fontStyle: "italic" } }, "NULL"));
11190
+ }
11191
+ if (typeof value === "boolean") {
11192
+ return value ? "true" : "false";
11193
+ }
11194
+ return String(value);
11195
+ },
11196
+ }); });
11197
+ };
11198
+ var getTotalRows = function () {
11199
+ return resultSets.reduce(function (sum, rs) { return sum + rs.rows.length; }, 0);
11200
+ };
11201
+ return (React__default.createElement(Accordion, { defaultExpanded: defaultExpanded },
11202
+ React__default.createElement(AccordionSummary, { expandIcon: React__default.createElement(ExpandMoreIcon, null), sx: {
11203
+ bgcolor: result.isError ? "error.light" : "background.paper",
11204
+ "&:hover": { bgcolor: result.isError ? "error.light" : "grey.100" },
11205
+ } },
11206
+ React__default.createElement(Box, { sx: {
11207
+ display: "flex",
11208
+ alignItems: "center",
11209
+ width: "100%",
11210
+ gap: 2,
11211
+ pr: 2,
11212
+ } },
11213
+ React__default.createElement(Typography$1, { variant: "subtitle2", sx: { fontWeight: 600, minWidth: 80 } }, formatTimestamp(result.timestamp)),
11214
+ React__default.createElement(Typography$1, { variant: "body2", sx: {
11215
+ flexGrow: 1,
11216
+ overflow: "hidden",
11217
+ textOverflow: "ellipsis",
11218
+ whiteSpace: "nowrap",
11219
+ } }, result.procedureName),
11220
+ React__default.createElement(Chip, { label: formatDuration(result.duration), size: "small", color: result.duration > 5000 ? "warning" : "default", sx: { minWidth: 60 } }),
11221
+ !result.isError && (React__default.createElement(Chip, { label: "".concat(getTotalRows(), " rows"), size: "small", color: "primary", variant: "outlined", sx: { minWidth: 70 } })),
11222
+ result.isError && (React__default.createElement(Chip, { label: "Error", size: "small", color: "error", sx: { minWidth: 60 } })),
11223
+ React__default.createElement(Tooltip, { title: "Delete this result" },
11224
+ React__default.createElement(IconButton$1, { size: "small", onClick: function (e) {
11225
+ e.stopPropagation();
11226
+ onDelete();
11227
+ }, sx: { ml: 1 } },
11228
+ React__default.createElement(DeleteIcon, { fontSize: "small" }))))),
11229
+ React__default.createElement(AccordionDetails, null,
11230
+ React__default.createElement(Typography$1, { variant: "caption", color: "text.secondary", sx: { mb: 1, display: "block" } },
11231
+ "Parameters: ",
11232
+ formatParameters(result.parameters)),
11233
+ result.isError ? (React__default.createElement(Typography$1, { color: "error", variant: "body2" }, result.errorMessage)) : resultSets.length === 0 ? (React__default.createElement(Typography$1, { variant: "body2", color: "text.secondary" }, "The stored procedure executed successfully but returned no result sets.")) : (React__default.createElement(React__default.Fragment, null,
11234
+ resultSets.length > 1 && (React__default.createElement(Tabs, { value: activeTab, onChange: function (_, newValue) { return setActiveTab(newValue); }, sx: { mb: 1 } }, resultSets.map(function (rs, index) { return (React__default.createElement(Tab, { key: index, label: "Result Set ".concat(index + 1, " (").concat(rs.rows.length, ")") })); }))),
11235
+ resultSets.map(function (resultSet, index) { return (React__default.createElement(Box, { key: index, sx: {
11236
+ display: activeTab === index ? "block" : "none",
11237
+ height: 300,
11238
+ } }, resultSet.rows.length === 0 ? (React__default.createElement(Typography$1, { variant: "body2", color: "text.secondary" }, "No rows returned.")) : (React__default.createElement(AgGridReact, { rowData: resultSet.rows, columnDefs: getColumnDefs(resultSet.columns), defaultColDef: defaultColDef, domLayout: "normal" })))); }))))));
11239
+ };
11240
+ var ResultsList = function (_a) {
11241
+ var results = _a.results, onDeleteResult = _a.onDeleteResult, onClearAll = _a.onClearAll;
11242
+ if (results.length === 0) {
11243
+ return (React__default.createElement(Paper, { sx: { p: 3, textAlign: "center" } },
11244
+ React__default.createElement(Typography$1, { variant: "body2", color: "text.secondary" }, "Execute a stored procedure to see results here.")));
11245
+ }
11246
+ return (React__default.createElement(Paper, { sx: { p: 2 } },
11247
+ React__default.createElement(Box, { sx: {
11248
+ display: "flex",
11249
+ justifyContent: "space-between",
11250
+ alignItems: "center",
11251
+ mb: 2,
11252
+ } },
11253
+ React__default.createElement(Typography$1, { variant: "subtitle1", sx: { fontWeight: 600 } },
11254
+ "Execution Results (",
11255
+ results.length,
11256
+ ")"),
11257
+ React__default.createElement(Button, { variant: "outlined", color: "error", size: "small", startIcon: React__default.createElement(ClearAllIcon, null), onClick: onClearAll }, "Clear All")),
11258
+ React__default.createElement(Box, { sx: { display: "flex", flexDirection: "column", gap: 1 } }, results.map(function (result, index) { return (React__default.createElement(ResultItem, { key: result.id, result: result, onDelete: function () { return onDeleteResult(result.id); }, defaultExpanded: index === 0 })); }))));
11259
+ };
11260
+
11261
+ var SPAutocomplete = function (_a) {
11262
+ var procedures = _a.procedures, value = _a.value, onChange = _a.onChange, isLoading = _a.isLoading, _b = _a.disabled, disabled = _b === void 0 ? false : _b;
11263
+ return (React__default.createElement(Autocomplete$1, { options: procedures, value: value, onChange: function (_, newValue) { return onChange(newValue); }, getOptionLabel: function (option) { return option.FullName; }, groupBy: function (option) { return option.SchemaName; }, disabled: disabled, isOptionEqualToValue: function (option, val) { return option.FullName === val.FullName; }, renderInput: function (params) { return (React__default.createElement(TextField, __assign({}, params, { label: "Select Stored Procedure", variant: "outlined", fullWidth: true, slotProps: {
11264
+ input: __assign(__assign({}, params.InputProps), { endAdornment: (React__default.createElement(React__default.Fragment, null, isLoading ? (React__default.createElement(CircularProgress, { color: "inherit", size: 20 })) : (params.InputProps.endAdornment))) }),
11265
+ } }))); }, renderOption: function (props, option) { return (React__default.createElement("li", __assign({}, props, { key: option.FullName }),
11266
+ option.SchemaName,
11267
+ ".",
11268
+ option.ProcedureName)); } }));
11269
+ };
11270
+
11271
+ var getStoredProceduresWithParameters = function (schemaFilter) { return __awaiter(void 0, void 0, void 0, function () {
11272
+ var apiService, parameters;
11273
+ return __generator(this, function (_a) {
11274
+ switch (_a.label) {
11275
+ case 0:
11276
+ apiService = new MESApiService();
11277
+ parameters = [];
11278
+ if (schemaFilter) {
11279
+ parameters.push({ name: "SchemaFilter", value: schemaFilter });
11280
+ }
11281
+ return [4 /*yield*/, apiService.callV2("[SYSTEM].[GetStoredProceduresWithParameters]", parameters)];
11282
+ case 1: return [2 /*return*/, _a.sent()];
11283
+ }
11284
+ });
11285
+ }); };
11286
+ var executeStoredProcedure = function (procedureName, parameters) { return __awaiter(void 0, void 0, void 0, function () {
11287
+ var apiService;
11288
+ return __generator(this, function (_a) {
11289
+ switch (_a.label) {
11290
+ case 0:
11291
+ apiService = new MESApiService();
11292
+ return [4 /*yield*/, apiService.callV2(procedureName, parameters)];
11293
+ case 1: return [2 /*return*/, _a.sent()];
11294
+ }
11295
+ });
11296
+ }); };
11297
+
11298
+ var SPExecutorPage = function () {
11299
+ var _a = useState([]), procedures = _a[0], setProcedures = _a[1];
11300
+ var _b = useState([]), allParameters = _b[0], setAllParameters = _b[1];
11301
+ var _c = useState(null), selectedProcedure = _c[0], setSelectedProcedure = _c[1];
11302
+ var _d = useState([]), executionResults = _d[0], setExecutionResults = _d[1];
11303
+ var _e = useState(false), isLoading = _e[0], setIsLoading = _e[1];
11304
+ var _f = useState(false), isExecuting = _f[0], setIsExecuting = _f[1];
11305
+ var _g = useState(""), error = _g[0], setError = _g[1];
11306
+ var _h = useState(true), accumulateResults = _h[0], setAccumulateResults = _h[1];
11307
+ var loadData = function () { return __awaiter(void 0, void 0, void 0, function () {
11308
+ var resp, procs, params;
11309
+ return __generator(this, function (_a) {
11310
+ switch (_a.label) {
11311
+ case 0:
11312
+ setIsLoading(true);
11313
+ setError("");
11314
+ return [4 /*yield*/, getStoredProceduresWithParameters()];
11315
+ case 1:
11316
+ resp = _a.sent();
11317
+ if (resp.ok) {
11318
+ procs = get(resp, "data.tables[0].rows", []);
11319
+ params = get(resp, "data.tables[1].rows", []);
11320
+ setProcedures(procs);
11321
+ setAllParameters(params);
11322
+ }
11323
+ else {
11324
+ setError(resp.message);
11325
+ }
11326
+ setIsLoading(false);
11327
+ return [2 /*return*/];
11328
+ }
11329
+ });
11330
+ }); };
11331
+ useEffect(function () {
11332
+ loadData();
11333
+ }, []);
11334
+ var currentParameters = useMemo(function () {
11335
+ if (!selectedProcedure)
11336
+ return [];
11337
+ return allParameters.filter(function (p) { return p.FullName === selectedProcedure.FullName; });
11338
+ }, [selectedProcedure, allParameters]);
11339
+ var handleProcedureChange = function (proc) {
11340
+ setSelectedProcedure(proc);
11341
+ };
11342
+ var handleExecute = function (formState) { return __awaiter(void 0, void 0, void 0, function () {
11343
+ var startTime, parameters, resp, duration, newResult;
11344
+ return __generator(this, function (_a) {
11345
+ switch (_a.label) {
11346
+ case 0:
11347
+ if (!selectedProcedure)
11348
+ return [2 /*return*/];
11349
+ setIsExecuting(true);
11350
+ setError("");
11351
+ startTime = Date.now();
11352
+ parameters = formState
11353
+ .filter(function (p) { return p.include; })
11354
+ .map(function (p) { return ({
11355
+ name: p.parameterName,
11356
+ value: p.sendAsNull ? null : p.value,
11357
+ }); });
11358
+ return [4 /*yield*/, executeStoredProcedure(selectedProcedure.FullName, parameters)];
11359
+ case 1:
11360
+ resp = _a.sent();
11361
+ duration = Date.now() - startTime;
11362
+ newResult = {
11363
+ id: "".concat(Date.now(), "-").concat(Math.random().toString(36).substr(2, 9)),
11364
+ procedureName: selectedProcedure.FullName,
11365
+ parameters: parameters,
11366
+ timestamp: new Date(),
11367
+ duration: duration,
11368
+ data: resp.ok ? resp.data : null,
11369
+ isError: !resp.ok,
11370
+ errorMessage: resp.ok ? undefined : resp.message,
11371
+ };
11372
+ if (accumulateResults) {
11373
+ setExecutionResults(function (prev) { return __spreadArray([newResult], prev, true); });
11374
+ }
11375
+ else {
11376
+ setExecutionResults([newResult]);
11377
+ }
11378
+ if (!resp.ok) {
11379
+ setError(resp.message);
11380
+ }
11381
+ setIsExecuting(false);
11382
+ return [2 /*return*/];
11383
+ }
11384
+ });
11385
+ }); };
11386
+ var handleDeleteResult = function (id) {
11387
+ setExecutionResults(function (prev) { return prev.filter(function (r) { return r.id !== id; }); });
11388
+ };
11389
+ var handleClearAllResults = function () {
11390
+ setExecutionResults([]);
11391
+ };
11392
+ return (React__default.createElement(PinGate, null,
11393
+ React__default.createElement(HelmetDexteel, { title: "SP Executor" }),
11394
+ React__default.createElement(Grid, { container: true, spacing: 2, sx: { p: 2 } },
11395
+ React__default.createElement(Grid, { item: true, xs: 12 },
11396
+ React__default.createElement(Typography$1, { variant: "h5", sx: { fontWeight: 600 } }, "Stored Procedure Executor"),
11397
+ React__default.createElement(Typography$1, { variant: "body2", color: "text.secondary" }, "Select a stored procedure and configure its parameters to execute.")),
11398
+ React__default.createElement(Grid, { item: true, xs: 12 },
11399
+ React__default.createElement(Paper, { sx: { p: 2 } },
11400
+ React__default.createElement(Grid, { container: true, spacing: 2, alignItems: "center" },
11401
+ React__default.createElement(Grid, { item: true, xs: 12, md: 9 },
11402
+ React__default.createElement(SPAutocomplete, { procedures: procedures, value: selectedProcedure, onChange: handleProcedureChange, isLoading: isLoading })),
11403
+ React__default.createElement(Grid, { item: true, xs: 12, md: 3 },
11404
+ React__default.createElement(FormControlLabel, { control: React__default.createElement(Switch, { checked: accumulateResults, onChange: function (e) { return setAccumulateResults(e.target.checked); }, color: "primary" }), label: "Accumulate results" }))))),
11405
+ selectedProcedure && (React__default.createElement(Grid, { item: true, xs: 12 },
11406
+ React__default.createElement(DynamicParameterForm, { parameters: currentParameters, onExecute: handleExecute, isExecuting: isExecuting }))),
11407
+ React__default.createElement(Grid, { item: true, xs: 12 },
11408
+ React__default.createElement(ResultsList, { results: executionResults, onDeleteResult: handleDeleteResult, onClearAll: handleClearAllResults }))),
11409
+ React__default.createElement(ErrorModal, { error: error, onHide: function () { return setError(""); } })));
11410
+ };
11411
+
10967
11412
  var Configuration = function () {
10968
11413
  var option = useParams().option;
10969
11414
  var _a = useContext(ConfigurationContext), customConfiguration = _a[0], CustomSidebar = _a[1];
@@ -11017,6 +11462,11 @@ var Configuration = function () {
11017
11462
  path: "real-time/config",
11018
11463
  sidebar: function () { return React__default.createElement("div", null, "Config"); },
11019
11464
  main: function () { return React__default.createElement(QueryCacheInvalidations, null); },
11465
+ },
11466
+ {
11467
+ path: "sp-executor",
11468
+ sidebar: function () { return React__default.createElement("div", null, "SP Executor"); },
11469
+ main: function () { return React__default.createElement(SPExecutorPage, null); },
11020
11470
  }
11021
11471
  ], customConfiguration, true);
11022
11472
  // At the top level of your component, add a useEffect to handle window resizing
@@ -11094,7 +11544,11 @@ var Configuration = function () {
11094
11544
  React__default.createElement(ListItemButton, { selected: option === "settings", component: Link, to: "/configuration/settings" },
11095
11545
  React__default.createElement(ListItemIcon, null,
11096
11546
  React__default.createElement(Settings$2, null)),
11097
- React__default.createElement(ListItemText, { primary: "Settings" }))))); };
11547
+ React__default.createElement(ListItemText, { primary: "Settings" })),
11548
+ React__default.createElement(ListItemButton, { selected: option === "sp-executor", component: Link, to: "/configuration/sp-executor" },
11549
+ React__default.createElement(ListItemIcon, null,
11550
+ React__default.createElement(Code$1, null)),
11551
+ React__default.createElement(ListItemText, { primary: "SP Executor" }))))); };
11098
11552
  useEffect(function () {
11099
11553
  var handleListItemClick = function (event) {
11100
11554
  var target = event.target;
@@ -13154,6 +13608,170 @@ var TagsTreeModalV2 = function (_a) {
13154
13608
  React__default.createElement(ErrorModal, { error: error, onHide: function () { return setError(""); } })))));
13155
13609
  };
13156
13610
 
13611
+ var BitSelectorModal = function (_a) {
13612
+ var open = _a.open, handleClose = _a.handleClose, viewTags = _a.viewTags, onAddBits = _a.onAddBits, existingBitTags = _a.existingBitTags;
13613
+ var _b = useState(null), selectedTag = _b[0], setSelectedTag = _b[1];
13614
+ var _c = useState(new Set()), selectedBits = _c[0], setSelectedBits = _c[1];
13615
+ var _d = useState(false), isLoading = _d[0], setIsLoading = _d[1];
13616
+ // Get list of non-bit-extracted tags (source tags only)
13617
+ var availableTags = useMemo(function () {
13618
+ return Object.values(viewTags)
13619
+ .filter(function (t) { return t && t.viewTag && !t.viewTag.IsBitExtracted; })
13620
+ .map(function (t) { return t.viewTag; })
13621
+ .sort(function (a, b) { return a.TagName.localeCompare(b.TagName); });
13622
+ }, [viewTags]);
13623
+ // Check if a specific bit is already extracted for the selected tag
13624
+ var isBitAlreadyExtracted = useCallback(function (bitIndex) {
13625
+ if (!selectedTag)
13626
+ return false;
13627
+ return existingBitTags.has("".concat(selectedTag.TagId, "-").concat(bitIndex));
13628
+ }, [selectedTag, existingBitTags]);
13629
+ // Handle bit checkbox change
13630
+ var handleBitToggle = useCallback(function (bitIndex) {
13631
+ setSelectedBits(function (prev) {
13632
+ var newSet = new Set(prev);
13633
+ if (newSet.has(bitIndex)) {
13634
+ newSet.delete(bitIndex);
13635
+ }
13636
+ else {
13637
+ newSet.add(bitIndex);
13638
+ }
13639
+ return newSet;
13640
+ });
13641
+ }, []);
13642
+ // Select all available bits
13643
+ var handleSelectAll = useCallback(function () {
13644
+ var availableBits = new Set();
13645
+ for (var i = 0; i < 32; i++) {
13646
+ if (!isBitAlreadyExtracted(i)) {
13647
+ availableBits.add(i);
13648
+ }
13649
+ }
13650
+ setSelectedBits(availableBits);
13651
+ }, [isBitAlreadyExtracted]);
13652
+ // Clear all selections
13653
+ var handleClearAll = useCallback(function () {
13654
+ setSelectedBits(new Set());
13655
+ }, []);
13656
+ // Handle tag selection change
13657
+ var handleTagChange = useCallback(function (_event, value) {
13658
+ setSelectedTag(value);
13659
+ setSelectedBits(new Set()); // Clear bit selection when tag changes
13660
+ }, []);
13661
+ // Handle add button click
13662
+ var handleAdd = useCallback(function () {
13663
+ if (!selectedTag || selectedBits.size === 0)
13664
+ return;
13665
+ setIsLoading(true);
13666
+ onAddBits(selectedTag.TagId, Array.from(selectedBits).sort(function (a, b) { return a - b; }));
13667
+ setIsLoading(false);
13668
+ // Reset state and close
13669
+ setSelectedTag(null);
13670
+ setSelectedBits(new Set());
13671
+ handleClose();
13672
+ }, [selectedTag, selectedBits, onAddBits, handleClose]);
13673
+ // Handle cancel
13674
+ var handleCancel = useCallback(function () {
13675
+ setSelectedTag(null);
13676
+ setSelectedBits(new Set());
13677
+ handleClose();
13678
+ }, [handleClose]);
13679
+ // Render bit grid (4 rows x 8 columns)
13680
+ var renderBitGrid = function () {
13681
+ if (!selectedTag) {
13682
+ return (React__default.createElement(Box, { sx: {
13683
+ display: "flex",
13684
+ justifyContent: "center",
13685
+ alignItems: "center",
13686
+ minHeight: "200px",
13687
+ color: "text.secondary",
13688
+ } },
13689
+ React__default.createElement(Typography$1, null, "Select a tag to view available bits")));
13690
+ }
13691
+ var rows = [];
13692
+ for (var row = 0; row < 4; row++) {
13693
+ var bits = [];
13694
+ var _loop_1 = function (col) {
13695
+ var bitIndex = row * 8 + col;
13696
+ var isExtracted = isBitAlreadyExtracted(bitIndex);
13697
+ var isSelected = selectedBits.has(bitIndex);
13698
+ bits.push(React__default.createElement(Grid2, { key: bitIndex, size: 1.5 },
13699
+ React__default.createElement(FormControlLabel, { control: React__default.createElement(Checkbox, { checked: isSelected, onChange: function () { return handleBitToggle(bitIndex); }, disabled: isExtracted, size: "small", sx: {
13700
+ padding: "4px",
13701
+ } }), label: React__default.createElement(Typography$1, { variant: "body2", sx: {
13702
+ color: isExtracted ? "text.disabled" : "text.primary",
13703
+ fontSize: "0.8rem",
13704
+ minWidth: "20px",
13705
+ } }, bitIndex), sx: {
13706
+ margin: 0,
13707
+ "& .MuiFormControlLabel-label": {
13708
+ marginLeft: "2px",
13709
+ },
13710
+ } })));
13711
+ };
13712
+ for (var col = 0; col < 8; col++) {
13713
+ _loop_1(col);
13714
+ }
13715
+ rows.push(React__default.createElement(Grid2, { container: true, key: row, spacing: 0.5, sx: { mb: 0.5 } }, bits));
13716
+ }
13717
+ return rows;
13718
+ };
13719
+ var selectedCount = selectedBits.size;
13720
+ var availableCount = selectedTag
13721
+ ? 32 -
13722
+ Array.from({ length: 32 }, function (_, i) { return i; }).filter(isBitAlreadyExtracted)
13723
+ .length
13724
+ : 0;
13725
+ return (React__default.createElement(MesfModal, { open: open, handleClose: handleCancel, maxWidth: "md", "aria-labelledby": "bit-selector-modal-title", "aria-describedby": "bit-selector-modal-description", title: "Add Bits from Bit Array" },
13726
+ React__default.createElement(MesfModal.Content, { dividers: true },
13727
+ React__default.createElement(Grid2, { container: true, spacing: 2 },
13728
+ React__default.createElement(Grid2, { size: 12 },
13729
+ React__default.createElement(Typography$1, { variant: "subtitle2", sx: { mb: 1 } }, "Select Source Tag"),
13730
+ React__default.createElement(Autocomplete$1, { size: "small", id: "source-tag-selector", options: availableTags, getOptionLabel: function (option) { return option.Alias || option.TagName; }, value: selectedTag, onChange: handleTagChange, noOptionsText: "No tags available in current view", renderOption: function (props, option) { return (React__default.createElement(Box, __assign({}, props, { component: "li" }),
13731
+ React__default.createElement(Box, { sx: {
13732
+ display: "flex",
13733
+ justifyContent: "space-between",
13734
+ alignItems: "center",
13735
+ width: "100%",
13736
+ } },
13737
+ React__default.createElement(Typography$1, { variant: "body2" }, option.Alias || option.TagName),
13738
+ React__default.createElement(Typography$1, { variant: "caption", sx: { color: "text.secondary", ml: 2 } }, option.TagName !== option.Alias ? option.TagName : "")))); }, renderInput: function (params) { return (React__default.createElement(TextField, __assign({}, params, { label: "Source Tag", variant: "outlined", placeholder: "Search tags..." }))); }, sx: { width: "100%" } })),
13739
+ React__default.createElement(Grid2, { size: 12 },
13740
+ React__default.createElement(Box, { sx: {
13741
+ display: "flex",
13742
+ justifyContent: "space-between",
13743
+ alignItems: "center",
13744
+ mt: 1,
13745
+ } },
13746
+ React__default.createElement(Typography$1, { variant: "subtitle2" }, "Select Bit Positions (0-31, LSB = 0)"),
13747
+ selectedTag && (React__default.createElement(Box, { sx: { display: "flex", gap: 1 } },
13748
+ React__default.createElement(Button, { size: "small", variant: "outlined", onClick: handleSelectAll, disabled: availableCount === 0 }, "Select All"),
13749
+ React__default.createElement(Button, { size: "small", variant: "outlined", onClick: handleClearAll, disabled: selectedCount === 0 }, "Clear"))))),
13750
+ React__default.createElement(Grid2, { size: 12 },
13751
+ React__default.createElement(Box, { sx: {
13752
+ border: "1px solid",
13753
+ borderColor: "divider",
13754
+ borderRadius: 1,
13755
+ p: 2,
13756
+ minHeight: "200px",
13757
+ backgroundColor: "background.paper",
13758
+ } }, renderBitGrid())),
13759
+ selectedTag && (React__default.createElement(Grid2, { size: 12 },
13760
+ React__default.createElement(Typography$1, { variant: "body2", sx: { color: "text.secondary" } },
13761
+ selectedCount,
13762
+ " bit",
13763
+ selectedCount !== 1 ? "s" : "",
13764
+ " selected",
13765
+ availableCount < 32 &&
13766
+ " (".concat(32 - availableCount, " already extracted)")))))),
13767
+ React__default.createElement(MesfModal.Actions, null,
13768
+ React__default.createElement(Box, { sx: { display: "flex", gap: 1, pt: 1 } },
13769
+ React__default.createElement(Button, { variant: "outlined", color: "secondary", onClick: handleCancel }, "Cancel"),
13770
+ React__default.createElement(ButtonWithLoading, { onClick: handleAdd, variant: "contained", color: "primary", isLoading: isLoading, disabled: !selectedTag || selectedCount === 0 },
13771
+ "Add ",
13772
+ selectedCount > 0 ? "(".concat(selectedCount, ")") : "")))));
13773
+ };
13774
+
13157
13775
  var LoadViewModalV2 = function (_a) {
13158
13776
  var open = _a.open, handleClose = _a.handleClose;
13159
13777
  var queryClient = useQueryClient();
@@ -13316,11 +13934,12 @@ var TagsTableV2 = function () {
13316
13934
  var _f = useState(false); _f[0]; var setIsLoading = _f[1];
13317
13935
  var _g = useState(null); _g[0]; var setSelectedRowTagId = _g[1];
13318
13936
  var _h = useState(false), tagsTreeModalOpen = _h[0], setTagsTreeModalOpen = _h[1];
13319
- var _j = useState(false), saveAsViewModalOpen = _j[0], setSaveAsViewModalOpen = _j[1];
13320
- var _k = useState(false), loadViewOpen = _k[0], setLoadViewOpen = _k[1];
13321
- var _l = useState(""), snackbarMessage = _l[0], setSnackbarMessage = _l[1];
13322
- var _m = useState(null), draggedRowId = _m[0], setDraggedRowId = _m[1];
13323
- var _o = useState(null), dragOverRowId = _o[0], setDragOverRowId = _o[1];
13937
+ var _j = useState(false), bitSelectorModalOpen = _j[0], setBitSelectorModalOpen = _j[1];
13938
+ var _k = useState(false), saveAsViewModalOpen = _k[0], setSaveAsViewModalOpen = _k[1];
13939
+ var _l = useState(false), loadViewOpen = _l[0], setLoadViewOpen = _l[1];
13940
+ var _m = useState(""), snackbarMessage = _m[0], setSnackbarMessage = _m[1];
13941
+ var _o = useState(null), draggedRowId = _o[0], setDraggedRowId = _o[1];
13942
+ var _p = useState(null), dragOverRowId = _p[0], setDragOverRowId = _p[1];
13324
13943
  // Mutations
13325
13944
  var deleteAllViewTags = useMutation({
13326
13945
  mutationFn: function (viewId) { return deleteAllViewTagsFromView(viewId); },
@@ -13452,6 +14071,63 @@ var TagsTableV2 = function () {
13452
14071
  // Add the new tag
13453
14072
  handleAddTag(selectedTag);
13454
14073
  };
14074
+ // Generate synthetic ID for bit-extracted tags
14075
+ var generateBitTagId = function (sourceTagId, bitIndex) {
14076
+ return sourceTagId * 100 + bitIndex;
14077
+ };
14078
+ // Get set of existing bit tags as "sourceTagId-bitIndex" strings
14079
+ var existingBitTags = useMemo(function () {
14080
+ var bitTags = new Set();
14081
+ Object.values(viewTags).forEach(function (_a) {
14082
+ var viewTag = _a.viewTag;
14083
+ if (viewTag.IsBitExtracted &&
14084
+ viewTag.SourceTagId !== undefined &&
14085
+ viewTag.BitIndex !== undefined) {
14086
+ bitTags.add("".concat(viewTag.SourceTagId, "-").concat(viewTag.BitIndex));
14087
+ }
14088
+ });
14089
+ return bitTags;
14090
+ }, [viewTags]);
14091
+ // Handle adding bits from a bit array tag
14092
+ var handleAddBits = useCallback(function (sourceTagId, bitIndices) {
14093
+ var sourceTagEntry = viewTags[sourceTagId];
14094
+ if (!sourceTagEntry) {
14095
+ setError("Source tag not found");
14096
+ return;
14097
+ }
14098
+ var sourceTag = sourceTagEntry.viewTag;
14099
+ var newTags = __assign({}, viewTags);
14100
+ var maxOrder = Math.max.apply(Math, __spreadArray(__spreadArray([], Object.values(viewTags).map(function (v) { return v.order; }), false), [0], false));
14101
+ bitIndices.forEach(function (bitIndex) {
14102
+ // Generate unique ID for this bit extraction
14103
+ var syntheticId = generateBitTagId(sourceTagId, bitIndex);
14104
+ // Skip if already exists
14105
+ if (newTags[syntheticId])
14106
+ return;
14107
+ maxOrder++;
14108
+ newTags[syntheticId] = {
14109
+ viewTag: {
14110
+ TagId: syntheticId,
14111
+ TagName: "".concat(sourceTag.TagName, "[").concat(bitIndex, "]"),
14112
+ Alias: "".concat(sourceTag.Alias || sourceTag.TagName, "[").concat(bitIndex, "]"),
14113
+ TagType: "D", // Bit-extracted tags are always digital
14114
+ Color: getRandomColor(),
14115
+ MinScale: 0,
14116
+ MaxScale: 1,
14117
+ IsAutoScale: true,
14118
+ IsVisible: true,
14119
+ Unit: "",
14120
+ SourceTagId: sourceTagId,
14121
+ BitIndex: bitIndex,
14122
+ IsBitExtracted: true,
14123
+ },
14124
+ order: maxOrder,
14125
+ };
14126
+ });
14127
+ // Use setViewTags since bit-extracted tags don't need new API calls
14128
+ // They derive data from existing source tags
14129
+ setViewTags(newTags);
14130
+ }, [viewTags, setViewTags]);
13455
14131
  var handleRowReorder = useCallback(function (draggedId, targetId) {
13456
14132
  if (draggedId === targetId)
13457
14133
  return;
@@ -13581,6 +14257,13 @@ var TagsTableV2 = function () {
13581
14257
  setTagsTreeModalOpen(true);
13582
14258
  },
13583
14259
  },
14260
+ {
14261
+ key: "addBitFromArray",
14262
+ name: "Add Bit from Bit Array",
14263
+ onClick: function () {
14264
+ setBitSelectorModalOpen(true);
14265
+ },
14266
+ },
13584
14267
  {
13585
14268
  key: "saveView",
13586
14269
  name: "Save View",
@@ -13692,6 +14375,10 @@ var TagsTableV2 = function () {
13692
14375
  max: overallMinMax.max,
13693
14376
  overallMin: overallMinMax.min,
13694
14377
  overallMax: overallMinMax.max,
14378
+ // Bit extraction metadata
14379
+ isBitExtracted: viewTag.IsBitExtracted,
14380
+ sourceTagId: viewTag.SourceTagId,
14381
+ bitIndex: viewTag.BitIndex,
13695
14382
  };
13696
14383
  });
13697
14384
  }, [viewTags, cursorValues, seriesMinMax]);
@@ -13714,8 +14401,17 @@ var TagsTableV2 = function () {
13714
14401
  // Custom cell renderer for type (with colored badge)
13715
14402
  var renderTypeCell = useCallback(function (params) {
13716
14403
  var type = params.value;
13717
- return (React__default.createElement(Chip, { label: type, size: "small", style: {
13718
- backgroundColor: type === "A" ? "#AD48C4" : "#C46F48",
14404
+ var isBitExtracted = params.row.isBitExtracted;
14405
+ // Show "B" for bit-extracted tags, otherwise show the tag type
14406
+ var displayLabel = isBitExtracted ? "B" : type;
14407
+ // Use green for bit-extracted, purple for analog, orange for digital
14408
+ var backgroundColor = isBitExtracted
14409
+ ? "#48C4A0"
14410
+ : type === "A"
14411
+ ? "#AD48C4"
14412
+ : "#C46F48";
14413
+ return (React__default.createElement(Chip, { label: displayLabel, size: "small", style: {
14414
+ backgroundColor: backgroundColor,
13719
14415
  color: "white",
13720
14416
  fontWeight: "bold",
13721
14417
  fontSize: "0.7rem",
@@ -14112,6 +14808,7 @@ var TagsTableV2 = function () {
14112
14808
  },
14113
14809
  } }),
14114
14810
  React__default.createElement(TagsTreeModalV2, { open: tagsTreeModalOpen, handleClose: function () { return setTagsTreeModalOpen(false); }, onTagSelect: handleTagSelect }),
14811
+ React__default.createElement(BitSelectorModal, { open: bitSelectorModalOpen, handleClose: function () { return setBitSelectorModalOpen(false); }, viewTags: viewTags, onAddBits: handleAddBits, existingBitTags: existingBitTags }),
14115
14812
  React__default.createElement(SaveUpdateDeleteViewModalV2, { open: saveAsViewModalOpen, mode: "create", handleClose: function (shouldUpdate) {
14116
14813
  setSaveAsViewModalOpen(false);
14117
14814
  if (shouldUpdate) {
@@ -15481,27 +16178,37 @@ var TrendingsPageV2 = function () {
15481
16178
  .sort(function (a, b) { return b.order - a.order; }) // DESCENDING order - MUST match TrendingChartV2 sort!
15482
16179
  .map(function (tag) { return tag.viewTag.TagId; });
15483
16180
  }, [viewTags]);
16181
+ // Real tag IDs (excluding bit-extracted synthetic IDs) for API queries
16182
+ // Bit-extracted tags derive their data client-side from source tags
16183
+ var realTagIds = useMemo(function () {
16184
+ return Object.values(viewTags)
16185
+ .filter(function (tag) { return tag && tag.viewTag && !tag.viewTag.IsBitExtracted; })
16186
+ .sort(function (a, b) { return b.order - a.order; })
16187
+ .map(function (tag) { return tag.viewTag.TagId; });
16188
+ }, [viewTags]);
15484
16189
  // Stable query key that only changes on time scope or when explicitly refetching (add/zoom/pan)
15485
16190
  // This prevents refetch when removing tags from viewTags
16191
+ // IMPORTANT: Uses realTagIds (excludes bit-extracted tags) for API queries
15486
16192
  var stableTagIdsRef = useRef([]);
15487
16193
  var prevTimeScopeRef = useRef("");
15488
16194
  var queryTagIds = useMemo(function () {
15489
16195
  var currentTimeScope = "".concat(timeScopeStart.getTime(), "-").concat(timeScopeEnd.getTime());
15490
16196
  var timeScopeChanged = currentTimeScope !== prevTimeScopeRef.current;
15491
- // Check if any new tags were added (not present in stable ref)
15492
- var hasNewTags = tagIds.some(function (id) { return !stableTagIdsRef.current.includes(id); });
16197
+ // Check if any new REAL tags were added (not present in stable ref)
16198
+ // Bit-extracted tags don't trigger API refetch since they derive data client-side
16199
+ var hasNewTags = realTagIds.some(function (id) { return !stableTagIdsRef.current.includes(id); });
15493
16200
  // Update query tagIds if:
15494
16201
  // 1. It's empty (first load)
15495
- // 2. New tags were added (not just length change)
15496
- // 3. Time scope changed (zoom/pan) - use current tagIds to avoid fetching removed tags
16202
+ // 2. New REAL tags were added (not just length change)
16203
+ // 3. Time scope changed (zoom/pan) - use current realTagIds to avoid fetching removed tags
15497
16204
  if (stableTagIdsRef.current.length === 0 ||
15498
16205
  hasNewTags ||
15499
16206
  timeScopeChanged) {
15500
- stableTagIdsRef.current = tagIds;
16207
+ stableTagIdsRef.current = realTagIds;
15501
16208
  prevTimeScopeRef.current = currentTimeScope;
15502
16209
  }
15503
16210
  return stableTagIdsRef.current;
15504
- }, [tagIds, timeScopeStart, timeScopeEnd]);
16211
+ }, [realTagIds, timeScopeStart, timeScopeEnd]);
15505
16212
  // Fetch series data using stable query key
15506
16213
  var _m = useSearchSeries$1({
15507
16214
  start: timeScopeStart.getTime(),
@@ -15510,6 +16217,7 @@ var TrendingsPageV2 = function () {
15510
16217
  autoRefresh: autoRefresh,
15511
16218
  }), series = _m.data, seriesLoading = _m.isLoading, seriesIsError = _m.isError, seriesError = _m.error;
15512
16219
  // Filter and reorder series to match current viewTags order
16220
+ // Also handles bit-extracted tags by deriving their data from source tags
15513
16221
  var filteredSeries = useMemo(function () {
15514
16222
  if (!series)
15515
16223
  return [];
@@ -15525,12 +16233,39 @@ var TrendingsPageV2 = function () {
15525
16233
  seriesMap.set(tagId, series[index]);
15526
16234
  }
15527
16235
  });
16236
+ // Build viewTags array sorted by order (descending) to match tagIds order
16237
+ var viewTagsArray = Object.values(viewTags)
16238
+ .filter(function (tag) { return tag && tag.viewTag; })
16239
+ .sort(function (a, b) { return b.order - a.order; });
15528
16240
  // Return series in the SAME order as tagIds (which matches viewTags sort order)
15529
- // This ensures series[i] always corresponds to viewTagsArray[i] in the chart
16241
+ // For bit-extracted tags, derive data from source tag by extracting the specific bit
15530
16242
  return tagIds
15531
- .map(function (tagId) { return seriesMap.get(tagId); })
16243
+ .map(function (tagId, idx) {
16244
+ var viewTagEntry = viewTagsArray[idx];
16245
+ if (!viewTagEntry)
16246
+ return null;
16247
+ var viewTag = viewTagEntry.viewTag;
16248
+ // If this is a bit-extracted tag, derive data from source
16249
+ if (viewTag.IsBitExtracted &&
16250
+ viewTag.SourceTagId !== undefined &&
16251
+ viewTag.BitIndex !== undefined) {
16252
+ var sourceData = seriesMap.get(viewTag.SourceTagId);
16253
+ if (!sourceData)
16254
+ return null;
16255
+ // Extract bit from each data point
16256
+ // BitIndex 0 = LSB (least significant bit)
16257
+ return sourceData.map(function (point) { return ({
16258
+ timestamp: point.timestamp,
16259
+ value: point.value !== null
16260
+ ? (Math.floor(point.value) >> viewTag.BitIndex) & 1
16261
+ : null,
16262
+ }); });
16263
+ }
16264
+ // Regular tag - use series data directly
16265
+ return seriesMap.get(tagId);
16266
+ })
15532
16267
  .filter(Boolean);
15533
- }, [series, tagIds, queryTagIds]);
16268
+ }, [series, tagIds, queryTagIds, viewTags]);
15534
16269
  // Calculate overall min/max values from filtered series data
15535
16270
  var seriesMinMaxData = useSeriesMinMax(filteredSeries, tagIds);
15536
16271
  // Load initial view when views are fetched
@@ -21206,5 +21941,5 @@ var areaSelector = /*#__PURE__*/Object.freeze({
21206
21941
  AreaSelector: AreaSelector
21207
21942
  });
21208
21943
 
21209
- export { Account, AssetProvider, AssetTreePicker, AuthContext, AuthProvider, ButtonWithLoading, ChangePassword, CheckBoxControl, Configuration$1 as Configuration, ContextMenu$1 as ContextMenu, ContextMenuMESFProvider, CreateNewAssetDialog, CurrencyFormatter, DataGridControl, DateFormatter, DateTimeFormatter, ENTRY_INITIAL_VALUES, EditAssetDialog, ErrorModal, ExcelIcon, FetchError, FilterPanel, GenericPanel, GenericTable, GetCrewColor, GetShiftColor, HelmetDexteel, IntegerFormatter, LogbookSettingsInitialState, LogbookSettingsProvider, Login, Logout, LongFilterPanel, MESApiService, MESFLogbookEntry, MESFLogbookReport, MESFMain, MESSAGE_API, MESSAGE_ERRORS, MasterDetailPanel, MesfModal, ModalTreeFilterControl, MultipleSelectorControl, NumberFormatter, RemoveAssetDialog, ShiftDayNavigatorControl, ShiftNavigatorProvider, ShiftPeriodNavigatorControl, SimplePasswordControl, SimpleSelectorControl, TimeAndUserMenu, TimeFormatter, TimeService, TreePickerControl, TreePickerControlV2, TrendingsPage, USER_LABELS, UTLSettingsProvider, UserProvider, axiosInstance, deleteUser, dxtServerTimeZone, dxtToLocalServerTime, dxtToUTC, formatNumber, getAuthTypes, getCrewStyle, getDataUser, getEntries, getError, getMomentTz, getShiftByParameters, getShiftStyle, getShiftsRangeByParameters, getTokenFromLS, getUserPermissionsFromAPI, getUsers, logbookNavbar, logbookRoutesMESF, renewToken, routeLogbookEntry, routeLogbookReport, useSearchAssets as searchAssets, useSearchSeries as searchSeries, useSearchTagsTree as searchTagsTree, useSearchViewTags as searchViewTags, useSearchViews as searchViews, setPassword, setProfilesToUser, themeDXT, themeMESF, upsertUser, useAssetContext, useContextMenuMESF, useHasPermission, useHasProfile, useLogbookSettings, useMesfRealtime, useShiftNavigator, useShiftNavigatorManager, useToken, useUTLSettingsContext, useUserContext };
21944
+ export { Account, AssetProvider, AssetTreePicker, AuthContext, AuthProvider, ButtonWithLoading, ChangePassword, CheckBoxControl, Configuration$1 as Configuration, ContextMenu$1 as ContextMenu, ContextMenuMESFProvider, CreateNewAssetDialog, CurrencyFormatter, DataGridControl, DateFormatter, DateTimeFormatter, ENTRY_INITIAL_VALUES, EditAssetDialog, ErrorModal, ExcelIcon, FetchError, FilterPanel, GenericPanel, GenericTable, GetCrewColor, GetShiftColor, HelmetDexteel, IntegerFormatter, LogbookSettingsInitialState, LogbookSettingsProvider, Login, Logout, LongFilterPanel, MESApiService, MESFLogbookEntry, MESFLogbookReport, MESFMain, MESSAGE_API, MESSAGE_ERRORS, MasterDetailPanel, MesfModal, ModalTreeFilterControl, MultipleSelectorControl, NumberFormatter, RemoveAssetDialog, SPExecutorPage, ShiftDayNavigatorControl, ShiftNavigatorProvider, ShiftPeriodNavigatorControl, SimplePasswordControl, SimpleSelectorControl, TimeAndUserMenu, TimeFormatter, TimeService, TreePickerControl, TreePickerControlV2, TrendingsPage, USER_LABELS, UTLSettingsProvider, UserProvider, axiosInstance, deleteUser, dxtServerTimeZone, dxtToLocalServerTime, dxtToUTC, formatNumber, getAuthTypes, getCrewStyle, getDataUser, getEntries, getError, getMomentTz, getShiftByParameters, getShiftStyle, getShiftsRangeByParameters, getTokenFromLS, getUserPermissionsFromAPI, getUsers, logbookNavbar, logbookRoutesMESF, renewToken, routeLogbookEntry, routeLogbookReport, useSearchAssets as searchAssets, useSearchSeries as searchSeries, useSearchTagsTree as searchTagsTree, useSearchViewTags as searchViewTags, useSearchViews as searchViews, setPassword, setProfilesToUser, themeDXT, themeMESF, upsertUser, useAssetContext, useContextMenuMESF, useHasPermission, useHasProfile, useLogbookSettings, useMesfRealtime, useShiftNavigator, useShiftNavigatorManager, useToken, useUTLSettingsContext, useUserContext };
21210
21945
  //# sourceMappingURL=index.esm.js.map