@agilemotion/oui-react-js 1.8.43 → 1.8.44

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.
Files changed (31) hide show
  1. package/dist/ApplicationManager.js +10 -8
  2. package/dist/InteractionPortalApp.js +0 -1
  3. package/dist/InteractionPortalAppHome.js +69 -3
  4. package/dist/RestService.js +3 -3
  5. package/dist/Utils.js +7 -0
  6. package/dist/assets/jss/components/customInputStyle.js +0 -1
  7. package/dist/assets/jss/components/customInputStyle.jsx +0 -1
  8. package/dist/components/DataGrid.js +6 -2
  9. package/dist/components/SocketManager.js +4 -2
  10. package/dist/components/TitleBar.js +1 -1
  11. package/dist/components/Toolbar.js +1 -1
  12. package/dist/components/customInput/CustomInput.js +4 -1
  13. package/dist/components/dashboard/FoldingSideTabDashboard.css +3 -0
  14. package/dist/components/dashboard/FoldingSideTabDashboard.js +24 -12
  15. package/dist/components/dashboard/SideMenuModuleDashboard.css +6 -5
  16. package/dist/components/dashboard/SideMenuModuleDashboard.js +5 -2
  17. package/dist/components/dashboard/components/blackDashboard/sidebar/FoldingTabSidebar.css +94 -0
  18. package/dist/components/dashboard/components/blackDashboard/sidebar/FoldingTabSidebar.js +111 -203
  19. package/dist/components/form/BaseField.js +1 -1
  20. package/dist/components/form/Form.css +2 -3
  21. package/dist/components/form/Form.js +5 -1
  22. package/dist/components/layout/Layout.css +8 -0
  23. package/dist/components/layout/Layout.js +10 -1
  24. package/dist/components/layout/View.css +20 -6
  25. package/dist/components/layout/View.js +32 -1
  26. package/dist/components/layout/Window.js +1 -0
  27. package/dist/components/media/Chat.css +0 -0
  28. package/dist/components/media/Chat.js +86 -0
  29. package/dist/components/media/chat/ChatRoom.js +19 -12
  30. package/dist/components/signatures/AgilitySignaturePanel.js +2 -1
  31. package/package.json +1 -1
@@ -7,7 +7,6 @@ exports.default = exports.TEMPLATE_TOKEN_REGEX = exports.SYSTEM_EVENT = exports.
7
7
  var _Observable = _interopRequireDefault(require("./event/Observable"));
8
8
  var _Event = _interopRequireDefault(require("./event/Event"));
9
9
  var _EventType = _interopRequireDefault(require("./event/EventType"));
10
- var _Graph = _interopRequireDefault(require("./components/Graph"));
11
10
  var _Utils = _interopRequireDefault(require("./Utils"));
12
11
  var _DynamicJS = _interopRequireWildcard(require("./DynamicJS"));
13
12
  var _TypedValue = _interopRequireDefault(require("./TypedValue"));
@@ -80,13 +79,7 @@ class ApplicationManager {
80
79
  return instance.getActiveTenant();
81
80
  },
82
81
  getUniqueOrgForRole(role) {
83
- let profile = JSON.parse(instance.getUserDetails().profile);
84
- let roles = profile.organisations.filter(org => org.roles.includes(role)) // Filter organisations by role
85
- .map(org => org.code);
86
- if (roles.length > 1) {
87
- throw new Error("There is more than one organisation for role : [" + role + "]");
88
- }
89
- return roles[0];
82
+ return instance.getUniqueOrgForRole(role);
90
83
  }
91
84
  };
92
85
  if (!Array.isArray) {
@@ -98,6 +91,15 @@ class ApplicationManager {
98
91
  }
99
92
  return ApplicationManager.instance;
100
93
  }
94
+ getUniqueOrgForRole(role) {
95
+ let profile = JSON.parse(this.getUserDetails().profile);
96
+ let roles = profile.organisations.filter(org => org.roles.includes(role)) // Filter organisations by role
97
+ .map(org => org.code);
98
+ if (roles.length > 1) {
99
+ throw new Error("There is more than one organisation for role : [" + role + "]");
100
+ }
101
+ return roles[0];
102
+ }
101
103
  addApplicationContextSubscription = subscription => {
102
104
  return new Promise(resolve => {
103
105
  _Observable.default.addApplicationContextSubscription(subscription);
@@ -36,7 +36,6 @@ const InteractionPortalApp = props => {
36
36
  if (window.location.pathname !== '/reset-password' && window.location.pathname !== '/login' && window.location.pathname !== '/forgot-password' && window.location.pathname !== '/change-password') {
37
37
  navigate("/");
38
38
  }
39
- console.log("\n\n\n\nImpl : ", props.implConfig);
40
39
  }, []);
41
40
  return /*#__PURE__*/_react.default.createElement(_ThemeProvider.default, {
42
41
  theme: agilityTheme
@@ -12,6 +12,11 @@ var _Utils = _interopRequireDefault(require("./Utils"));
12
12
  var _reactRouterDom = require("react-router-dom");
13
13
  var _RestUtils = require("./RestUtils");
14
14
  var _SideMenuModuleDashboard = _interopRequireDefault(require("./components/dashboard/SideMenuModuleDashboard"));
15
+ var _SocketManager = _interopRequireDefault(require("./components/SocketManager"));
16
+ var _FoldingSideTabDashboard = _interopRequireDefault(require("./components/dashboard/FoldingSideTabDashboard"));
17
+ var _material = require("@mui/material");
18
+ var _ThemeProvider = _interopRequireDefault(require("@mui/styles/ThemeProvider"));
19
+ var _react2 = require("@emotion/react");
15
20
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
16
21
  const location = window.location.protocol + "//" + window.location.hostname;
17
22
  const status = response => {
@@ -49,6 +54,8 @@ const InteractionPortalAppHome = /*#__PURE__*/_react.default.memo((props, ref) =
49
54
  });
50
55
  (0, _RestUtils.sendRequest)(location + _ApplicationManager.default.getContextRoot() + '/auth/api/v1/user/find', response => {
51
56
  _ApplicationManager.default.setUserDetails(response);
57
+ _SocketManager.default.init();
58
+ _ApplicationManager.default.setActiveTenant(_ApplicationManager.default.getUniqueOrgForRole('VENDOR'));
52
59
  }, e => {
53
60
  console.log(e);
54
61
  });
@@ -81,15 +88,74 @@ const InteractionPortalAppHome = /*#__PURE__*/_react.default.memo((props, ref) =
81
88
  className: "main w-100 h-100 d-inline-block"
82
89
  }, /*#__PURE__*/_react.default.createElement("div", {
83
90
  className: "w-100 h-100"
84
- }, !loading && !_Utils.default.isNull(dashboardSettings) && !hasClientError ? /*#__PURE__*/_react.default.createElement(_SideMenuModuleDashboard.default, {
91
+ }, !loading && !_Utils.default.isNull(dashboardSettings) && !hasClientError ? /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement("div", {
92
+ className: `main w-100 h-100 d-inline-block coj-skin`
93
+ }, /*#__PURE__*/_react.default.createElement("div", {
94
+ className: "w-100 h-100"
95
+ }, /*#__PURE__*/_react.default.createElement(_react2.ThemeProvider, {
96
+ theme: (0, _material.createTheme)({
97
+ palette: {
98
+ primary: {
99
+ main: props.implConfig.primaryColor
100
+ },
101
+ secondary: {
102
+ main: props.implConfig.secondaryColor
103
+ }
104
+ }
105
+ })
106
+ }, /*#__PURE__*/_react.default.createElement(_ThemeProvider.default, {
107
+ theme: (0, _material.createTheme)({
108
+ palette: {
109
+ primary: {
110
+ main: dashboardSettings.systemProfileDto.firstCorporateBrandColor
111
+ },
112
+ secondary: {
113
+ main: dashboardSettings.systemProfileDto.secondCorporateBrandColor
114
+ }
115
+ },
116
+ components: {
117
+ MuiCssBaseline: {
118
+ styleOverrides: {
119
+ body: {
120
+ // Firefox
121
+ scrollbarColor: 'rgba(100, 100, 100, 0.4) transparent',
122
+ scrollbarWidth: 'thin'
123
+ },
124
+ // WebKit browsers
125
+ '::-webkit-scrollbar': {
126
+ width: '8px',
127
+ height: '8px'
128
+ },
129
+ '::-webkit-scrollbar-track': {
130
+ background: 'transparent'
131
+ },
132
+ '::-webkit-scrollbar-thumb': {
133
+ backgroundColor: 'rgba(100, 100, 100, 0.4)',
134
+ borderRadius: '4px',
135
+ transition: 'background-color 0.3s'
136
+ },
137
+ '::-webkit-scrollbar-thumb:hover': {
138
+ backgroundColor: 'rgba(100, 100, 100, 0.6)'
139
+ }
140
+ }
141
+ }
142
+ }
143
+ })
144
+ }, /*#__PURE__*/_react.default.createElement(_material.CssBaseline, null), /*#__PURE__*/_react.default.createElement(_FoldingSideTabDashboard.default, {
85
145
  logoutCallBack: () => {
86
146
  logout();
87
147
  },
88
148
  settings: dashboardSettings,
89
149
  avatar: !_Utils.default.isNull(avatarUrl) ? avatarUrl : null,
90
150
  logo: dashboardSettings.logo,
91
- appLogoPath: props.implConfig.appLogo
92
- }) : hasClientError ? /*#__PURE__*/_react.default.createElement("div", {
151
+ showDashboardMenu: false,
152
+ appLogoPath: /*dashboardSettings.logo*/null,
153
+ logoStyle: {
154
+ padding: '32px'
155
+ }
156
+ }), /*#__PURE__*/_react.default.createElement(_LoadingIndicator.default, {
157
+ color: loadingColor
158
+ })))))) : hasClientError ? /*#__PURE__*/_react.default.createElement("div", {
93
159
  style: "margin: 128px; color: red; font-size: 24px"
94
160
  }, "System error - Unknown client") : null), /*#__PURE__*/_react.default.createElement(_LoadingIndicator.default, {
95
161
  color: loadingColor
@@ -90,9 +90,9 @@ class RestService {
90
90
  } else {
91
91
  if (!this.isParamValueNull(parameterValue)) {
92
92
  requestBody = parameterValue.instanceType === 'TypedValue' ? parameterValue.value : parameterValue;
93
- if (typeof requestBody === 'object') {
94
- requestBody.type = parameterValue.type;
95
- }
93
+ /*if (typeof requestBody === 'object') {
94
+ requestBody.type = parameterValue.type;
95
+ }*/
96
96
  }
97
97
  }
98
98
  }
package/dist/Utils.js CHANGED
@@ -17,6 +17,13 @@ class Utils {
17
17
  static isNull(value) {
18
18
  return value === null || typeof value === 'undefined';
19
19
  }
20
+ static normalizeCssSize(value, fallback) {
21
+ if (value == null) return fallback;
22
+ // allow "72px", "5rem", etc.
23
+ if (typeof value === "string" && value.trim()) return value.trim();
24
+ if (typeof value === "number" && Number.isFinite(value)) return `${value}px`;
25
+ return fallback;
26
+ }
20
27
  static getInitials(name) {
21
28
  const parts = name.split(' ');
22
29
  let initials = '';
@@ -53,7 +53,6 @@ const customInputStyle = {
53
53
  },
54
54
  formControl: {
55
55
  margin: "0 0 17px 0",
56
- paddingTop: "27px",
57
56
  position: "relative",
58
57
  verticalAlign: "unset",
59
58
  "& svg,& .fab,& .far,& .fal,& .fas,& .material-icons": {
@@ -55,7 +55,6 @@ const customInputStyle = {
55
55
  },
56
56
  formControl: {
57
57
  margin: "0 0 17px 0",
58
- paddingTop: "27px",
59
58
  position: "relative",
60
59
  verticalAlign: "unset",
61
60
  "& svg,& .fab,& .far,& .fal,& .fas,& .material-icons": {
@@ -429,7 +429,9 @@ const DataGrid = exports.DataGrid = /*#__PURE__*/_react.default.memo(/*#__PURE__
429
429
  processData(result, conf);
430
430
  }).catch(e => {
431
431
  console.error(e);
432
- _Utils.default.publishErrorMessage(e, props.viewId);
432
+ if (e.errorType !== 'INVALID_PARAMETER') {
433
+ _Utils.default.publishErrorMessage(e, props.viewId);
434
+ }
433
435
  });
434
436
  } else {
435
437
  throw new Error('Unknown service type : ' + serviceType);
@@ -591,6 +593,7 @@ const DataGrid = exports.DataGrid = /*#__PURE__*/_react.default.memo(/*#__PURE__
591
593
  updateColumnReadOnlyStatus(column, props.rows);
592
594
  }
593
595
  rowsRef.current = rowValues;
596
+ setSelected([]);
594
597
  setRows(rowValues);
595
598
  }
596
599
  }, [props.rows]);
@@ -744,6 +747,7 @@ const DataGrid = exports.DataGrid = /*#__PURE__*/_react.default.memo(/*#__PURE__
744
747
  }
745
748
  return visible && /*#__PURE__*/_react.default.createElement("div", {
746
749
  id: props.config.id,
750
+ className: _Utils.default.getComponentAttribute(props.config, 'className', null),
747
751
  style: _Utils.default.isNull(props.hasBorder) || props.hasBorder === true ? _Utils.default.mergeStyles({
748
752
  margin: '0',
749
753
  border: '1px solid #e2e2e2',
@@ -808,7 +812,7 @@ const DataGrid = exports.DataGrid = /*#__PURE__*/_react.default.memo(/*#__PURE__
808
812
  },
809
813
  label: "Wrap table text"
810
814
  })), /*#__PURE__*/_react.default.createElement("div", {
811
- className: "responsive-table",
815
+ className: `responsive-table`,
812
816
  style: {
813
817
  height: props.height
814
818
  }
@@ -99,14 +99,16 @@ class SocketManager {
99
99
  });
100
100
  };
101
101
  registerOnline() {
102
- console.log('***** REGISTERING ONLINE *****');
103
102
  let userDetails = _ApplicationManager.default.getUserDetails();
103
+ // TODO : Resolve the hardcoding
104
+ let profile = typeof JSON.parse(userDetails.profile) === 'string' ? userDetails.profile : 'COJ';
105
+ console.log('***** REGISTERING ONLINE ***** [' + profile + ']');
104
106
  this.emitEvent(_SocketRequest.default.REGISTER_ONLINE, {
105
107
  user: {
106
108
  userId: userDetails.username,
107
109
  name: userDetails.name + ' ' + userDetails.surname
108
110
  },
109
- orgCode: userDetails.profile
111
+ orgCode: profile
110
112
  }).then(data => {
111
113
  console.log("REGISTERED ONLINE - " + userDetails.username);
112
114
  this.connected = true;
@@ -58,7 +58,7 @@ const TitleBar = /*#__PURE__*/_react.default.memo(/*#__PURE__*/_react.default.fo
58
58
  className: 'title-bar'
59
59
  }, /*#__PURE__*/_react.default.createElement("div", {
60
60
  style: {
61
- padding: '32px 0 4px 0',
61
+ padding: '4px 0 4px 0',
62
62
  fontSize: '24px',
63
63
  color: '#202124'
64
64
  }
@@ -326,7 +326,7 @@ const Toolbar = props => {
326
326
  className: 'oui-toolbar',
327
327
  style: _Utils.default.mergeStyles({
328
328
  borderBottom: '1px solid #e2e2e2',
329
- padding: '4px 0 0 0',
329
+ padding: '4px 0',
330
330
  overflow: 'hidden'
331
331
  }, props.config)
332
332
  }, render(props.config));
@@ -62,7 +62,10 @@ function CustomInput(props) {
62
62
  minLength: inputProps && inputProps.minLength ? inputProps.minLength : undefined
63
63
  };
64
64
  return /*#__PURE__*/_react.default.createElement(_FormControl.default, _extends({}, formControlProps, {
65
- className: formControlClasses
65
+ className: formControlClasses,
66
+ style: {
67
+ marginTop: '20px'
68
+ }
66
69
  }), labelText !== undefined ? /*#__PURE__*/_react.default.createElement(_InputLabel.default, _extends({
67
70
  className: classes.labelRoot + " " + labelClasses,
68
71
  htmlFor: id
@@ -0,0 +1,3 @@
1
+ .sidebar .nav {
2
+ margin-top: 0 !important;
3
+ }
@@ -18,11 +18,20 @@ var _DashboardStore = require("../../redux/store/DashboardStore");
18
18
  var _ApplicationManager = _interopRequireDefault(require("../../ApplicationManager"));
19
19
  var _SocketManager = _interopRequireDefault(require("../SocketManager"));
20
20
  var _WindowViewPort = _interopRequireDefault(require("../layout/WindowViewPort"));
21
- var _styles = require("@mui/material/styles");
22
- var _Button = _interopRequireDefault(require("@mui/material/Button"));
21
+ require("./FoldingSideTabDashboard.css");
23
22
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
24
23
  function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
25
24
  let ps;
25
+ const DATA_URL_PREFIX = 'data:image/png;base64,';
26
+ function normalizeLogo(src) {
27
+ if (!src) return undefined;
28
+ const s = src.trim();
29
+ if (/^data:/i.test(s)) {
30
+ const dup = new RegExp(`^(${DATA_URL_PREFIX})+`, 'i');
31
+ return s.replace(dup, DATA_URL_PREFIX);
32
+ }
33
+ return DATA_URL_PREFIX + s;
34
+ }
26
35
  const FoldingSideTabDashboard = props => {
27
36
  const [navDrawerOpen, setNavDrawerOpen] = _react.default.useState(true);
28
37
  const [loading, setLoading] = _react.default.useState(true);
@@ -31,7 +40,7 @@ const FoldingSideTabDashboard = props => {
31
40
  const [primaryThemeColor, setPrimaryThemeColor] = _react.default.useState('');
32
41
  const [themeTextColor, setThemeTextColor] = _react.default.useState('');
33
42
  const [routes, setRoutes] = _react.default.useState([]);
34
- const [logo, setLogo] = _react.default.useState('data:image/png;base64,' + props.logo);
43
+ const [logo, setLogo] = _react.default.useState(normalizeLogo(props.logo));
35
44
  const [sidebarOpened, setSidebarOpened] = _react.default.useState(document.documentElement.className.indexOf('nav-open') !== -1);
36
45
  const [sidebarMini, setSidebarMini] = _react.default.useState(true);
37
46
  const [opacity, setOpacity] = _react.default.useState(0);
@@ -119,13 +128,15 @@ const FoldingSideTabDashboard = props => {
119
128
  }
120
129
  let newRoutes = [];
121
130
  let newRoute = {};
122
- newRoute.name = 'Dashboard';
123
- newRoute.id = 'dashboard';
124
- newRoute.icon = 'fa fa-pie-chart';
125
- newRoute.layout = '/admin';
126
- newRoute.level = 0;
127
- newRoute.isParent = true;
128
- newRoutes.push(newRoute);
131
+ if (_Utils.default.isNull(props.showDashboardMenu) || props.showDashboardMenu) {
132
+ newRoute.name = 'Dashboard';
133
+ newRoute.id = 'dashboard';
134
+ newRoute.icon = 'fa fa-pie-chart';
135
+ newRoute.layout = '/admin';
136
+ newRoute.level = 0;
137
+ newRoute.isParent = true;
138
+ newRoutes.push(newRoute);
139
+ }
129
140
  if (!_Utils.default.isNull(props.settings.systemProfileDto.modules)) {
130
141
  for (let i = 0; i < props.settings.systemProfileDto.modules.length; i++) {
131
142
  let module = props.settings.systemProfileDto.modules[i];
@@ -255,10 +266,10 @@ const FoldingSideTabDashboard = props => {
255
266
  routes: routes,
256
267
  activeColor: 'agility',
257
268
  secondaryThemeColor: secondaryThemeColor,
258
- activeRouteMenu: {
269
+ activeRouteMenu: props.showDashboardMenu ? {
259
270
  id: 'dashboard',
260
271
  name: 'Dashboard'
261
- },
272
+ } : null,
262
273
  className: 'sidebar',
263
274
  dashboardLauncher: () => {
264
275
  launchDashboard();
@@ -269,6 +280,7 @@ const FoldingSideTabDashboard = props => {
269
280
  text: '',
270
281
  imgSrc: logo
271
282
  },
283
+ logoStyle: props.logoStyle,
272
284
  closeSidebar: closeSidebar
273
285
  })), ' ', /*#__PURE__*/_react.default.createElement("div", {
274
286
  className: "main-panel",
@@ -9,7 +9,7 @@
9
9
  }
10
10
 
11
11
  .side-menu-module-dashboard .viewport {
12
- width: calc(100% - 448px) !important;
12
+ width: calc(100% - 348px) !important;
13
13
  }
14
14
 
15
15
  .side-menu-module-dashboard .sidebar {
@@ -19,7 +19,7 @@
19
19
  margin-left: 0 !important;
20
20
  margin-top: 0 !important;
21
21
  border-radius: 0 !important;
22
- width: 440px !important;
22
+ width: 340px !important;
23
23
  }
24
24
 
25
25
  .side-menu-module-dashboard .main-panel {
@@ -32,8 +32,8 @@
32
32
  }
33
33
 
34
34
  .side-menu-module-dashboard .navbar.navbar-absolute {
35
- left: 440px !important;
36
- width: calc(100% - 440px) !important;
35
+ left: 340px !important;
36
+ width: calc(100% - 340px) !important;
37
37
  background-color: transparent !important;
38
38
  color: #1d253b !important;
39
39
  padding: 10px 12px 10px 0 !important;
@@ -49,7 +49,7 @@
49
49
  }
50
50
 
51
51
  .side-menu-module-dashboard .main-panel > .content {
52
- padding: 78px 12px 30px 448px !important;
52
+ padding: 78px 12px 30px 348px !important;
53
53
  }
54
54
 
55
55
  @media screen and (max-width: 991px){
@@ -60,5 +60,6 @@
60
60
 
61
61
  .side-menu-module-dashboard .sidebar {
62
62
  width: 260px !important;
63
+ border: 20px solid red;
63
64
  }
64
65
  }
@@ -18,6 +18,7 @@ var _ApplicationManager = _interopRequireDefault(require("../../ApplicationManag
18
18
  var _WindowViewPort = _interopRequireDefault(require("../layout/WindowViewPort"));
19
19
  require("./SideMenuModuleDashboard.css");
20
20
  var _ModuleMenuSidebar = _interopRequireDefault(require("./components/blackDashboard/sidebar/ModuleMenuSidebar"));
21
+ var _SocketManager = _interopRequireDefault(require("../SocketManager"));
21
22
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
22
23
  function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
23
24
  let ps;
@@ -60,7 +61,7 @@ const SideMenuModuleDashboard = props => {
60
61
  _react.default.useEffect(() => {
61
62
  //Sockets.init();
62
63
  return () => {
63
- //Sockets.disconnect();
64
+ _SocketManager.default.disconnect();
64
65
  };
65
66
  }, []);
66
67
  _react.default.useEffect(() => {
@@ -284,7 +285,9 @@ const SideMenuModuleDashboard = props => {
284
285
  style: {
285
286
  width: windowPinned ? 'calc(100% - 320px)' : '100%'
286
287
  }
287
- }, /*#__PURE__*/_react.default.createElement(_ViewPort.default, null)), /*#__PURE__*/_react.default.createElement("div", {
288
+ }, /*#__PURE__*/_react.default.createElement(_ViewPort.default, {
289
+ windowPinned: windowPinned
290
+ })), /*#__PURE__*/_react.default.createElement("div", {
288
291
  className: 'col no-margin no-padding',
289
292
  style: {
290
293
  width: windowPinned ? '320px' : '0'
@@ -5,3 +5,97 @@
5
5
  .sidebar .nav a:before {
6
6
  content: none !important;
7
7
  }
8
+
9
+ /* Root sidebar container (lives under a global header using --header-h) */
10
+ .sidebar {
11
+ display: flex;
12
+ flex-direction: column;
13
+ inline-size: 100%;
14
+ block-size: calc(100dvh - var(--header-h, 0px));
15
+ overflow: hidden; /* only the middle section scrolls */
16
+ }
17
+
18
+ /* If you don't have a global header for some screens, you can toggle this class instead */
19
+ /* .sidebar--no-header { block-size: 100dvh; } */
20
+
21
+ /* ---------- TOP: brand + heading ---------- */
22
+ .sidebar__top {
23
+ padding: 32px 0 12px 0;
24
+ margin-left: 8px;
25
+ margin-right: 8px;
26
+ display: grid;
27
+ gap: 8px;
28
+ justify-items: center;
29
+ align-content: center;
30
+ border-bottom: 1px solid #e1e1e1;
31
+ }
32
+
33
+ /* Control the height of the logo area via CSS var */
34
+ :root { --sidebar-logo-h: 96px; }
35
+
36
+ .sidebar__logo {
37
+ inline-size: 100%;
38
+ block-size: var(--sidebar-logo-h);
39
+ }
40
+
41
+ .sidebar__brand {
42
+ display: block;
43
+ inline-size: 100%;
44
+ block-size: 100%;
45
+ }
46
+
47
+ .sidebar__brand-img {
48
+ display: block;
49
+ inline-size: 100%;
50
+ block-size: 100%;
51
+ object-fit: contain; /* switch to 'cover' if you want edge-to-edge crop */
52
+ }
53
+
54
+ /* Heading text under the brand (optional) */
55
+ .sidebar__heading {
56
+ font-size: 20px;
57
+ font-weight: 600;
58
+ text-align: center;
59
+ }
60
+
61
+ /* ---------- MIDDLE: menu (the ONLY scroller) ---------- */
62
+ .sidebar__scroll {
63
+ flex: 1 1 auto;
64
+ min-block-size: 0; /* crucial for flex scrolling */
65
+ overflow-y: auto;
66
+ overflow-x: hidden;
67
+ padding: 8px 0;
68
+ }
69
+
70
+ /* ---------- BOTTOM: app logo + copyright ---------- */
71
+ .sidebar__bottom {
72
+ margin-top: auto; /* pins to bottom */
73
+ flex: 0 0 auto; /* never shrink off-screen */
74
+ padding: 12px 10px;
75
+ display: grid;
76
+ gap: 8px;
77
+ justify-items: center;
78
+ border-top: 1px solid #e1e1e1;
79
+ margin-left: 4px;
80
+ margin-right: 4px;
81
+ }
82
+
83
+ .sidebar__app-logo img {
84
+ display: block;
85
+ max-block-size: 72px;
86
+ inline-size: auto;
87
+ object-fit: contain;
88
+ }
89
+
90
+ .sidebar__copyright {
91
+ font-size: 12px;
92
+ opacity: 0.8;
93
+ text-align: center;
94
+ }
95
+
96
+ /* Optional: basic nav spacing (keep your existing nav/link styles as needed) */
97
+ .nav-wrapper {
98
+ padding: 16px;
99
+ margin: 8px;
100
+ }
101
+