datastake-daf 0.6.501 → 0.6.503

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/layouts/index.css +1 -0
  2. package/dist/layouts/index.js +7137 -0
  3. package/dist/utils/index.js +287 -0
  4. package/package.json +1 -1
  5. package/rollup.config.js +25 -0
  6. package/src/@daf/hooks/usePermissions.js +20 -0
  7. package/src/@daf/layouts/AppLayout/AppLayout.stories.js +532 -0
  8. package/src/@daf/layouts/AppLayout/components/LoginPopup/index.js +116 -0
  9. package/src/@daf/layouts/AppLayout/components/LoginPopup/style.js +26 -0
  10. package/src/@daf/layouts/AppLayout/components/MobileDrawer/index.js +208 -0
  11. package/src/@daf/layouts/AppLayout/components/MobileDrawer/style.js +87 -0
  12. package/src/@daf/layouts/AppLayout/components/Notifications/Notification/index.js +31 -0
  13. package/src/@daf/layouts/AppLayout/components/Notifications/NotificationHistory/index.js +22 -0
  14. package/src/@daf/layouts/AppLayout/components/Notifications/context/index.js +79 -0
  15. package/src/@daf/layouts/AppLayout/components/Notifications/index.js +106 -0
  16. package/src/@daf/layouts/AppLayout/components/Notifications/style.js +61 -0
  17. package/src/@daf/layouts/AppLayout/components/Notifications/useNotifications.js +105 -0
  18. package/src/@daf/layouts/AppLayout/components/Sidenav/index.js +296 -0
  19. package/src/@daf/layouts/AppLayout/components/UserDropdown/UserIcon.js +96 -0
  20. package/src/@daf/layouts/AppLayout/components/UserDropdown/index.js +112 -0
  21. package/src/@daf/layouts/AppLayout/index.jsx +384 -0
  22. package/src/@daf/layouts/AppLayout/index.scss +5 -0
  23. package/src/@daf/layouts/AppLayout/styles/header.scss +257 -0
  24. package/src/@daf/layouts/AppLayout/styles/layout.scss +76 -0
  25. package/src/@daf/layouts/AppLayout/styles/responsive.scss +52 -0
  26. package/src/@daf/layouts/AppLayout/styles/sidebar.scss +79 -0
  27. package/src/@daf/layouts/AppLayout/styles/variables.scss +22 -0
  28. package/src/helpers/theme.js +304 -0
  29. package/src/helpers/user.js +4 -0
  30. package/src/layouts.js +1 -0
  31. package/src/utils.js +4 -2
@@ -0,0 +1,384 @@
1
+ /* eslint-disable react/prop-types */
2
+ import React, { useState, useEffect, useMemo } from "react";
3
+ import { useResizeContext } from "../../core/context/Resize/index.js";
4
+ import { useForms } from "../../core/context/Forms/index.js";
5
+ import { formatClassname } from '../../../helpers/ClassesHelper.js';
6
+ import { Layout, Select } from 'antd';
7
+ import {
8
+ MenuFoldOutlined,
9
+ MenuUnfoldOutlined,
10
+ MenuOutlined,
11
+ } from '@ant-design/icons';
12
+ import Sidenav from "./components/Sidenav/index.js";
13
+ import { UserDropdownMenu } from "./components/UserDropdown/index.js";
14
+ import MobileDrawer from "./components/MobileDrawer/index.js";
15
+ import Notifications from "./components/Notifications/index.js";
16
+ import LoginPopup from "./components/LoginPopup/index.js";
17
+ import { NotificationsProvider } from "./components/Notifications/context/index.js";
18
+ import './index.scss';
19
+
20
+ const { Content } = Layout;
21
+
22
+ /**
23
+ * AppLayout Component
24
+ *
25
+ * This component provides a complete layout structure with:
26
+ * - Collapsible sidebar navigation
27
+ * - Header with language selector, notifications, and user menu
28
+ * - Mobile responsive drawer
29
+ * - Login session management
30
+ *
31
+ * @param {Object} props
32
+ * @param {Object} props.user - Current user object
33
+ * @param {string} props.module - Current module/app name (e.g., 'sbg', 'pme')
34
+ * @param {Function} props.logout - Logout function
35
+ * @param {Function} props.exitImpersonation - Exit impersonation function
36
+ * @param {Function} props.updateLanguage - Language update callback
37
+ * @param {Function} props.getUserOptions - Get user options callback
38
+ * @param {Function} props.onCollapse - Callback when sidebar collapses
39
+ * @param {React.ReactNode} props.children - Child components to render
40
+ * @param {Function} props.navigate - Navigation function (from react-router-dom)
41
+ * @param {Function} props.t - Translation function (from react-i18next)
42
+ * @param {Object} props.location - Location object (from react-router-dom)
43
+ * @param {Function} props.matchPath - Match path function (from react-router-dom)
44
+ * @param {Function} props.Outlet - Outlet component (from react-router-dom)
45
+ * @param {Object} props.sidenavConfig - Sidebar navigation configuration
46
+ * @param {Object} props.permissions - User permissions object
47
+ * @param {Object} props.generalContext - General app context
48
+ * @param {Function} props.getUserPreference - Function to fetch user preferences
49
+ * @param {Object} props.notificationHandlers - Notification-related handlers
50
+ * @param {string} props.appName - Application name/identifier
51
+ * @param {Object} props.appLogos - Logo URLs { collapsed, expanded }
52
+ * @param {Object} props.languageConfig - Language selector configuration
53
+ * @param {Function} props.renderChildren - Custom render function for children
54
+ */
55
+ function AppLayout({
56
+ // User & Auth
57
+ user,
58
+ module,
59
+ logout,
60
+ exitImpersonation,
61
+ updateLanguage,
62
+ getUserOptions,
63
+
64
+ // Navigation (injected from app)
65
+ navigate,
66
+ location,
67
+ matchPath,
68
+ Outlet,
69
+
70
+ // i18n (injected from app)
71
+ t = (key) => key,
72
+
73
+ // Permissions (injected from app)
74
+ permissions = {},
75
+ checkPermission = () => false,
76
+
77
+ // General Context (injected from app)
78
+ generalContext = {},
79
+
80
+ // Services (injected from app)
81
+ getUserPreference,
82
+
83
+ // Notifications (injected from app)
84
+ notificationHandlers = {},
85
+ NotificationsHistoryProvider,
86
+
87
+ // Config
88
+ sidenavConfig = {},
89
+ appName = 'app',
90
+ appLogos = {
91
+ collapsed: '/assets/images/logo-small.svg',
92
+ expanded: '/assets/images/logo.svg'
93
+ },
94
+ languageConfig = [
95
+ { value: "en", label: "EN", flagUrl: "/assets/images/countries/gb.png" },
96
+ { value: "fr", label: "FR", flagUrl: "/assets/images/countries/fr.png" },
97
+ { value: "sp", label: "ES", flagUrl: "/assets/images/countries/sp.png" }
98
+ ],
99
+
100
+ // Callbacks
101
+ onCollapse = () => { },
102
+
103
+ // Custom renderers
104
+ renderChildren,
105
+
106
+ // Children
107
+ children,
108
+ }) {
109
+ const { notificationMode, changeNotificationState } = useForms();
110
+ const [userPreferences, setUserPreferences] = useState(null);
111
+ const isAppNavigation = module === 'app';
112
+ const [drawerOpened, setDrawerOpened] = useState(false);
113
+ const [settingsDrawerOpened, setSettingsDrawerOpened] = useState(false);
114
+ const { isCollapsed, setIsCollapsed } = useResizeContext();
115
+ const [hasPrevious, setPrevious] = useState(false);
116
+ const { logOutPopupVisible = false } = generalContext;
117
+
118
+ const canViewNotifications = checkPermission({
119
+ permission: 'notifications.canView',
120
+ permissions: user?.role?.permissions
121
+ }) && !isAppNavigation;
122
+
123
+ useEffect(() => {
124
+ const _fetchPreferences = async () => {
125
+ try {
126
+ if (getUserPreference) {
127
+ const { data } = await getUserPreference();
128
+ setUserPreferences(data);
129
+ }
130
+ } catch (err) {
131
+ console.log(err);
132
+ }
133
+ };
134
+
135
+ _fetchPreferences();
136
+ }, [getUserPreference]);
137
+
138
+ const setIsCollapse = (val = false) => {
139
+ localStorage.setItem('is_collapsed', val ? 'true' : 'false');
140
+ setIsCollapsed(val);
141
+ };
142
+
143
+ const logOut = () => {
144
+ sessionStorage.removeItem('notifications');
145
+ if (hasPrevious) {
146
+ exitImpersonation?.();
147
+ return;
148
+ }
149
+ logout?.();
150
+ };
151
+
152
+ const isUserApproved = user?.modules &&
153
+ user?.modules[module] &&
154
+ user?.modules[module].status === 'approved';
155
+
156
+ useEffect(() => {
157
+ if (localStorage.getItem('previous')) {
158
+ setPrevious(true);
159
+ }
160
+ }, [user?.language]);
161
+
162
+ useEffect(() => {
163
+ if (getUserOptions) {
164
+ getUserOptions(user?.language);
165
+ }
166
+ }, [user?.language, getUserOptions]);
167
+
168
+ const collapsedAppLogo = useMemo(() => appLogos.collapsed, [appLogos]);
169
+ const appLogo = useMemo(() => {
170
+ return isCollapsed ? collapsedAppLogo : appLogos.expanded;
171
+ }, [isCollapsed, collapsedAppLogo, appLogos]);
172
+
173
+ const toggleSettingsDrawer = () => {
174
+ if (!settingsDrawerOpened) {
175
+ setDrawerOpened(false);
176
+ setSettingsDrawerOpened(true);
177
+ return;
178
+ }
179
+ setSettingsDrawerOpened(false);
180
+ };
181
+
182
+ const toggleDrawer = () => {
183
+ if (!drawerOpened) {
184
+ setSettingsDrawerOpened(false);
185
+ setDrawerOpened(true);
186
+ return;
187
+ }
188
+ setDrawerOpened(false);
189
+ };
190
+
191
+ const handleLogoClick = () => {
192
+ if (Object.keys(user?.modules || {}).length <= 1) {
193
+ return;
194
+ }
195
+ changeNotificationState({ onYes: () => navigate?.('/app') });
196
+ };
197
+
198
+ return (
199
+ <div
200
+ style={{
201
+ display: 'flex',
202
+ flexDirection: 'row',
203
+ width: '100vw',
204
+ height: '100dvh',
205
+ maxHeight: '100dvh'
206
+ }}
207
+ className="components-layout"
208
+ >
209
+ {!isAppNavigation ? (
210
+ <div
211
+ className={formatClassname([
212
+ isCollapsed ? 'sidenav-sider-collapsed sidenav-sider desktop-sider' : 'sidenav-sider sidenav-sider-opened desktop-sider',
213
+ appName
214
+ ])}
215
+ style={{
216
+ width: isCollapsed ? '70px' : '250px',
217
+ minWidth: isCollapsed ? 'auto' : '250px'
218
+ }}
219
+ >
220
+ <div className="logo">
221
+ <img
222
+ src={appLogo}
223
+ onClick={handleLogoClick}
224
+ alt="logo"
225
+ className={appName}
226
+ />
227
+ </div>
228
+ <Sidenav
229
+ showMenu
230
+ module={module}
231
+ isCollapsed={isCollapsed}
232
+ user={user}
233
+ sidenavConfig={sidenavConfig}
234
+ navigate={navigate}
235
+ location={location}
236
+ matchPath={matchPath}
237
+ t={t}
238
+ changeNotificationState={changeNotificationState}
239
+ checkPermission={checkPermission}
240
+ appName={appName}
241
+ />
242
+ </div>
243
+ ) : null}
244
+
245
+ <div style={{ width: '100%', minHeight: '100dvh' }}>
246
+ <header
247
+ style={{ padding: 0 }}
248
+ className={formatClassname([
249
+ "site-layout-sub-header-background text-white d-flex justify-content-between",
250
+ !isCollapsed && 'header-collapsed',
251
+ 'layout-header',
252
+ appName
253
+ ])}
254
+ >
255
+ <div className="d-flex left-sidebar right-sidebar">
256
+ <div className="desktop">
257
+ {isAppNavigation ? null : React.createElement(
258
+ isCollapsed ? MenuUnfoldOutlined : MenuFoldOutlined,
259
+ {
260
+ className: 'trigger',
261
+ onClick: () => {
262
+ setIsCollapse(!isCollapsed);
263
+ onCollapse(!isCollapsed);
264
+ },
265
+ }
266
+ )}
267
+ </div>
268
+ <div className="mobile" onClick={toggleDrawer}>
269
+ <img src={collapsedAppLogo} alt="logo" />
270
+ </div>
271
+ <div className="mobile cursor-pointer pl-0" onClick={toggleDrawer}>
272
+ {module !== 'app' ? (
273
+ <MenuOutlined className="mobile-burger" />
274
+ ) : null}
275
+ </div>
276
+ </div>
277
+
278
+ <div className="d-flex right-sidebar">
279
+ {canViewNotifications ? (
280
+ <NotificationsProvider
281
+ user={user}
282
+ notificationHandlers={notificationHandlers}
283
+ NotificationsHistoryProvider={NotificationsHistoryProvider}
284
+ >
285
+ <NotificationsHistoryProvider>
286
+ <Notifications
287
+ userPreferences={userPreferences}
288
+ module={module}
289
+ t={t}
290
+ navigate={navigate}
291
+ appName={appName}
292
+ />
293
+ </NotificationsHistoryProvider>
294
+ </NotificationsProvider>
295
+ ) : <div className="notification-icon" />}
296
+
297
+ <div className="flex flex-column">
298
+ <Select
299
+ className="ln-22 dark-select language-select"
300
+ style={{ color: 'white', height: '30px' }}
301
+ defaultValue={localStorage.getItem('datastakeLng') || 'en'}
302
+ bordered={false}
303
+ onChange={lng => updateLanguage?.(lng)}
304
+ popupClassName={formatClassname(['dark-select-popup language-select', appName])}
305
+ >
306
+ {languageConfig.map(lang => (
307
+ <Select.Option key={lang.value} value={lang.value}>
308
+ <div className="row-cont flex">
309
+ <div className="cont mr-2">
310
+ <img
311
+ src={lang.flagUrl}
312
+ alt={lang.label}
313
+ style={{ width: 20, height: 12 }}
314
+ />
315
+ </div>
316
+ <div className="cont">
317
+ {lang.label}
318
+ </div>
319
+ </div>
320
+ </Select.Option>
321
+ ))}
322
+ </Select>
323
+ </div>
324
+
325
+ <div className="user-details d-flex">
326
+ <UserDropdownMenu
327
+ module={module}
328
+ user={user}
329
+ toggleSettingsDrawer={toggleSettingsDrawer}
330
+ settingsDrawerOpened={settingsDrawerOpened}
331
+ notificationMode={notificationMode}
332
+ isAppNavigation={isAppNavigation}
333
+ navigate={navigate}
334
+ changeNotificationState={changeNotificationState}
335
+ isUserApproved={isUserApproved}
336
+ t={t}
337
+ logOut={logOut}
338
+ checkPermission={checkPermission}
339
+ appName={appName}
340
+ />
341
+ </div>
342
+ </div>
343
+ </header>
344
+
345
+ <Content
346
+ className={formatClassname(["bg-white", "layout-children"])}
347
+ style={{ position: 'relative' }}
348
+ >
349
+ {renderChildren ? renderChildren() : (children || (Outlet && <Outlet />))}
350
+ </Content>
351
+
352
+ {module !== 'app' ? (
353
+ <MobileDrawer
354
+ mod={module}
355
+ toggle={toggleDrawer}
356
+ drawerOpened={drawerOpened}
357
+ user={user}
358
+ sidenavConfig={sidenavConfig}
359
+ navigate={navigate}
360
+ t={t}
361
+ checkPermission={checkPermission}
362
+ logOut={logOut}
363
+ changeNotificationState={changeNotificationState}
364
+ matchPath={matchPath}
365
+ appName={appName}
366
+ />
367
+ ) : null}
368
+
369
+ {logOutPopupVisible ? (
370
+ <LoginPopup
371
+ user={user}
372
+ t={t}
373
+ login={notificationHandlers.login}
374
+ loggingIn={notificationHandlers.loggingIn}
375
+ setLogOutPopupVisible={generalContext.setLogOutPopupVisible}
376
+ firebaseToken={notificationHandlers.firebaseToken}
377
+ />
378
+ ) : null}
379
+ </div>
380
+ </div>
381
+ );
382
+ }
383
+
384
+ export default AppLayout;
@@ -0,0 +1,5 @@
1
+ @import './styles/variables.scss';
2
+ @import './styles/layout.scss';
3
+ @import './styles/header.scss';
4
+ @import './styles/sidebar.scss';
5
+ @import './styles/responsive.scss';
@@ -0,0 +1,257 @@
1
+ @import './variables';
2
+
3
+ .layout-header {
4
+ background: $primary-bg;
5
+ height: $header-height;
6
+ display: flex;
7
+ align-items: center;
8
+ justify-content: space-between;
9
+ padding: 0 24px;
10
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
11
+ z-index: $z-header;
12
+ position: relative;
13
+
14
+ .left-sidebar,
15
+ .right-sidebar {
16
+ display: flex;
17
+ align-items: center;
18
+ gap: 16px;
19
+ }
20
+
21
+ .left-sidebar {
22
+ flex: 1;
23
+ }
24
+
25
+ // Collapse trigger
26
+ .trigger {
27
+ font-size: 18px;
28
+ line-height: $header-height;
29
+ padding: 0 24px;
30
+ margin: 0 -24px 0 0;
31
+ cursor: pointer;
32
+ transition: color $transition-speed;
33
+ color: $text-primary;
34
+
35
+ &:hover {
36
+ color: #1890ff;
37
+ }
38
+ }
39
+
40
+ // Desktop/Mobile visibility
41
+ .desktop {
42
+ display: flex;
43
+
44
+ @media (max-width: $mobile-breakpoint) {
45
+ display: none;
46
+ }
47
+ }
48
+
49
+ .mobile {
50
+ display: none;
51
+
52
+ @media (max-width: $mobile-breakpoint) {
53
+ display: flex;
54
+ align-items: center;
55
+ cursor: pointer;
56
+ }
57
+
58
+ img {
59
+ height: 32px;
60
+ width: auto;
61
+ }
62
+
63
+ .mobile-burger {
64
+ font-size: 20px;
65
+ color: $text-primary;
66
+ }
67
+ }
68
+
69
+ // Notification icon
70
+ .notification-icon {
71
+ position: relative;
72
+ cursor: pointer;
73
+ padding: 8px;
74
+ color: $text-primary;
75
+ font-size: 18px;
76
+ display: flex;
77
+ align-items: center;
78
+ transition: color $transition-speed;
79
+
80
+ &:hover {
81
+ color: #1890ff;
82
+ }
83
+
84
+ .noti-loader {
85
+ position: absolute;
86
+ top: 5px;
87
+ right: 5px;
88
+ width: 8px;
89
+ height: 8px;
90
+ background: #52c41a;
91
+ border-radius: 50%;
92
+ animation: pulse 2s infinite;
93
+ }
94
+
95
+ .noti-more-cont {
96
+ position: absolute;
97
+ top: 5px;
98
+ right: 5px;
99
+ background: #ff4d4f;
100
+ color: white;
101
+ border-radius: 10px;
102
+ padding: 2px 6px;
103
+ font-size: 12px;
104
+ min-width: 18px;
105
+ height: 18px;
106
+ display: flex;
107
+ align-items: center;
108
+ justify-content: center;
109
+ font-weight: 600;
110
+ }
111
+ }
112
+
113
+ // Language selector
114
+ .language-select {
115
+ min-width: 100px;
116
+
117
+ .ant-select-selector {
118
+ background: transparent !important;
119
+ border: none !important;
120
+ color: $text-primary !important;
121
+ box-shadow: none !important;
122
+ }
123
+
124
+ .ant-select-arrow {
125
+ color: $text-primary;
126
+ }
127
+
128
+ .row-cont {
129
+ display: flex;
130
+ align-items: center;
131
+ gap: 8px;
132
+ }
133
+ }
134
+
135
+ .user-details {
136
+ display: flex;
137
+ align-items: center;
138
+ }
139
+ }
140
+
141
+ // Dark select popup
142
+ .dark-select-popup {
143
+ &.ant-select-dropdown {
144
+ background: $primary-bg;
145
+
146
+ .ant-select-item {
147
+ color: $text-primary;
148
+
149
+ &:hover {
150
+ background: $hover-bg;
151
+ }
152
+
153
+ &-option-selected {
154
+ background: $hover-bg;
155
+ }
156
+ }
157
+ }
158
+ }
159
+
160
+ // User dropdown
161
+ .user-dropdown-layout {
162
+ &.ant-dropdown {
163
+ .ant-dropdown-menu {
164
+ padding: 0;
165
+ min-width: 200px;
166
+
167
+ .ant-dropdown-menu-item {
168
+ padding: 0;
169
+
170
+ &:hover {
171
+ background: transparent;
172
+ }
173
+ }
174
+ }
175
+ }
176
+
177
+ .drop-header {
178
+ padding: 16px;
179
+ border-bottom: 1px solid #f0f0f0;
180
+
181
+ h4 {
182
+ margin: 0 0 4px 0;
183
+ font-weight: 600;
184
+ font-size: 14px;
185
+ color: #262626;
186
+ }
187
+
188
+ p {
189
+ margin: 0;
190
+ color: $text-secondary;
191
+ font-size: 12px;
192
+ }
193
+ }
194
+
195
+ .list {
196
+ padding: 8px 0;
197
+
198
+ .list-item {
199
+ padding: 10px 16px;
200
+ cursor: pointer;
201
+ transition: background $transition-speed;
202
+ color: #262626;
203
+ font-size: 14px;
204
+
205
+ &:hover {
206
+ background: #f5f5f5;
207
+ }
208
+ }
209
+ }
210
+ }
211
+
212
+ // Dark menu
213
+ .dark-menu {
214
+ &.ant-dropdown {
215
+ .ant-dropdown-menu {
216
+ background: $primary-bg;
217
+ padding: 4px 0;
218
+
219
+ .ant-dropdown-menu-item {
220
+ color: $text-primary;
221
+ padding: 8px 12px;
222
+ display: flex;
223
+ align-items: center;
224
+ gap: 8px;
225
+
226
+ &:hover {
227
+ background: $hover-bg;
228
+ }
229
+
230
+ &-disabled {
231
+ color: rgba(255, 255, 255, 0.3);
232
+ cursor: not-allowed;
233
+
234
+ &:hover {
235
+ background: transparent;
236
+ }
237
+ }
238
+
239
+ .anticon {
240
+ color: $text-primary;
241
+ }
242
+ }
243
+ }
244
+ }
245
+ }
246
+
247
+ @keyframes pulse {
248
+ 0% {
249
+ box-shadow: 0 0 0 0 rgba(82, 196, 26, 0.7);
250
+ }
251
+ 70% {
252
+ box-shadow: 0 0 0 6px rgba(82, 196, 26, 0);
253
+ }
254
+ 100% {
255
+ box-shadow: 0 0 0 0 rgba(82, 196, 26, 0);
256
+ }
257
+ }
@@ -0,0 +1,76 @@
1
+ @import './variables';
2
+
3
+ .components-layout {
4
+ display: flex;
5
+ flex-direction: row;
6
+ width: 100vw;
7
+ height: 100dvh;
8
+ max-height: 100dvh;
9
+ overflow: hidden;
10
+
11
+ .layout-content-wrapper {
12
+ width: 100%;
13
+ min-height: 100dvh;
14
+ display: flex;
15
+ flex-direction: column;
16
+ }
17
+ }
18
+
19
+ .layout-children {
20
+ flex: 1;
21
+ min-height: calc(100vh - $header-height);
22
+ overflow-y: auto;
23
+ padding: 24px;
24
+ background: #ffffff;
25
+ position: relative;
26
+
27
+ @media (max-width: $mobile-breakpoint) {
28
+ padding: 16px;
29
+ }
30
+ }
31
+
32
+ // Module name section
33
+ .mod-name {
34
+ padding: 20px;
35
+ display: flex;
36
+ align-items: center;
37
+ gap: 12px;
38
+ border-bottom: 1px solid $border-color;
39
+ color: $text-primary;
40
+ cursor: pointer;
41
+ transition: all $transition-speed;
42
+
43
+ &:hover {
44
+ background: $hover-bg;
45
+ }
46
+
47
+ &.is-collapsed {
48
+ justify-content: center;
49
+ padding: 20px 10px;
50
+
51
+ h1 {
52
+ display: none;
53
+ }
54
+ }
55
+
56
+ .icon {
57
+ display: flex;
58
+ align-items: center;
59
+ justify-content: center;
60
+ width: 24px;
61
+ height: 24px;
62
+
63
+ svg, img {
64
+ width: 100%;
65
+ height: 100%;
66
+ object-fit: contain;
67
+ }
68
+ }
69
+
70
+ h1 {
71
+ margin: 0;
72
+ font-size: 16px;
73
+ font-weight: 600;
74
+ line-height: 1.2;
75
+ }
76
+ }