@omniumretail/component-library 1.2.44 → 1.2.46

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.
@@ -0,0 +1,254 @@
1
+ import { BellNotifications } from '../BellNotifications';
2
+ import styles from './styles.module.scss';
3
+ import {
4
+ ArrowLeftOutlined,
5
+ PlusOutlined,
6
+ DeleteOutlined,
7
+ ReloadOutlined,
8
+ EditOutlined,
9
+ ExportOutlined,
10
+ PrinterOutlined
11
+ } from '@ant-design/icons';
12
+ import { t } from 'i18next';
13
+ import moment from 'moment';
14
+ import React, { useEffect, useRef, useState } from 'react';
15
+ import omniumLogo from '../../assets/images/omnium-retail-logo-white.png';
16
+
17
+ interface Action {
18
+ key: string;
19
+ label: string;
20
+ icon: React.ReactNode;
21
+ onClick?: () => void;
22
+ }
23
+
24
+ interface Criticality {
25
+ Id: string;
26
+ Name: string;
27
+ ColorHex: string;
28
+ }
29
+
30
+ interface BellNotification {
31
+ Id: string;
32
+ Title: string;
33
+ Description: string;
34
+ RedirectURL: string;
35
+ Icon: string;
36
+ ExpirationDate: number;
37
+ Criticality: Criticality;
38
+ IsRead: boolean;
39
+ }
40
+
41
+ export interface Footer2Props {
42
+ onBackClick?: () => void;
43
+ notifications: BellNotification[];
44
+ onFilterChange: (filter: 'all' | 'unread') => void;
45
+ onNotificationClick: (id: string, url: string) => void;
46
+ handleMarkAllAsRead: () => void;
47
+ actions?: Action[];
48
+ onDelete?: () => void;
49
+ onUpdate?: () => void;
50
+ onCreate?: () => void;
51
+ onEdit?: () => void;
52
+ onExport?: () => void;
53
+ onPrint?: () => void;
54
+ storeName: string;
55
+ }
56
+
57
+ export const Footer2 = (props: Footer2Props) => {
58
+ const {
59
+ onBackClick,
60
+ notifications,
61
+ onFilterChange,
62
+ onNotificationClick,
63
+ handleMarkAllAsRead,
64
+ actions = [],
65
+ onDelete,
66
+ onUpdate,
67
+ onCreate,
68
+ onEdit,
69
+ onExport,
70
+ onPrint,
71
+ storeName = '',
72
+ } = props;
73
+
74
+ const [date, setDate] = useState(moment());
75
+ const [isMenuOpen, setIsMenuOpen] = useState(false);
76
+ const menuRef = useRef<HTMLDivElement>(null);
77
+ const [isMobile, setIsMobile] = useState(window.innerWidth < 768);
78
+
79
+ useEffect(() => {
80
+ const time = (function updateDate() {
81
+ const nextMinute = moment().add(1, 'minute');
82
+ return setTimeout(() => {
83
+ setDate(moment());
84
+ updateDate();
85
+ }, nextMinute.valueOf() - moment().valueOf());
86
+ }());
87
+
88
+ return () => clearTimeout(time);
89
+ });
90
+
91
+ useEffect(() => {
92
+ const handleResize = () => {
93
+ setIsMobile(window.innerWidth < 768);
94
+ };
95
+
96
+ window.addEventListener('resize', handleResize);
97
+ return () => {
98
+ window.removeEventListener('resize', handleResize);
99
+ };
100
+ }, []);
101
+
102
+ const getDefaultAndCustomActions = (): Action[] => {
103
+ const combinedActions = [...actions];
104
+
105
+ if (onDelete) {
106
+ combinedActions.push({
107
+ key: 'delete',
108
+ label: t('navigation.delete'),
109
+ icon: <DeleteOutlined />,
110
+ onClick: onDelete
111
+ });
112
+ }
113
+
114
+ if (onUpdate) {
115
+ combinedActions.push({
116
+ key: 'update',
117
+ label: t('navigation.update'),
118
+ icon: <ReloadOutlined />,
119
+ onClick: onUpdate
120
+ });
121
+ }
122
+
123
+ if (onCreate) {
124
+ combinedActions.push({
125
+ key: 'create',
126
+ label: t('navigation.create'),
127
+ icon: <PlusOutlined />,
128
+ onClick: onCreate
129
+ });
130
+ }
131
+
132
+ if (onEdit) {
133
+ combinedActions.push({
134
+ key: 'edit',
135
+ label: t('navigation.edit'),
136
+ icon: <EditOutlined />,
137
+ onClick: onEdit
138
+ });
139
+ }
140
+
141
+ if (onExport) {
142
+ combinedActions.push({
143
+ key: 'export',
144
+ label: t('navigation.export'),
145
+ icon: <ExportOutlined />,
146
+ onClick: onExport
147
+ });
148
+ }
149
+
150
+ if (onPrint) {
151
+ combinedActions.push({
152
+ key: 'print',
153
+ label: t('navigation.print'),
154
+ icon: <PrinterOutlined />,
155
+ onClick: onPrint
156
+ });
157
+ }
158
+
159
+ return combinedActions.sort((a, b) => a.label.localeCompare(b.label));
160
+ };
161
+
162
+ const allActions = getDefaultAndCustomActions();
163
+
164
+ const toggleMenu = () => {
165
+ setIsMenuOpen(!isMenuOpen);
166
+ };
167
+
168
+ // Handle click outside to close menu
169
+ useEffect(() => {
170
+ const handleClickOutside = (event: MouseEvent) => {
171
+ if (
172
+ isMenuOpen &&
173
+ menuRef.current &&
174
+ !menuRef.current.contains(event.target as Node) &&
175
+ !(event.target as Element).closest(`.${styles.addButton}`)
176
+ ) {
177
+ setIsMenuOpen(false);
178
+ }
179
+ };
180
+
181
+ document.addEventListener('mousedown', handleClickOutside);
182
+
183
+ return () => {
184
+ document.removeEventListener('mousedown', handleClickOutside);
185
+ };
186
+ }, [isMenuOpen]);
187
+
188
+ const plusIconClass = isMenuOpen ? `${styles.addIcon} ${styles.rotateIcon}` : styles.addIcon;
189
+
190
+ return (
191
+ <div className={styles.footer}>
192
+ {!isMobile &&
193
+ <div className={styles.content}>
194
+ <div className={styles.brand}>{storeName}</div>
195
+ <div className={styles.date}>{date.format('DD/MM/yyyy')}</div>
196
+ <div className={styles.time}>{date.format('HH:mm')}</div>
197
+ </div>
198
+ }
199
+
200
+ <div className={styles.navigation}>
201
+ <div className={styles.navItem} onClick={onBackClick}>
202
+ <ArrowLeftOutlined className={styles.navIcon} />
203
+ <span className={styles.navText}>{t('navigation.back')}</span>
204
+ </div>
205
+
206
+ <div className={styles.addButtonContainer}>
207
+ <div className={styles.addButton} onClick={toggleMenu}>
208
+ <PlusOutlined className={plusIconClass} />
209
+ </div>
210
+
211
+ {isMenuOpen && (
212
+ <div className={styles.circularMenu} ref={menuRef}>
213
+ {allActions?.map((action) => (
214
+ <div
215
+ key={action.key}
216
+ className={styles.circleMenuItem}
217
+ onClick={() => {
218
+ if (action.onClick) {
219
+ action.onClick();
220
+ setIsMenuOpen(false);
221
+ }
222
+ }}
223
+ >
224
+ <div className={styles.circleMenuIcon}>
225
+ {action.icon}
226
+ </div>
227
+
228
+ <div className={styles.circleMenuLabel}>{action.label}</div>
229
+ </div>
230
+ ))}
231
+ </div>
232
+ )}
233
+ </div>
234
+
235
+ <div className={styles.navItem}>
236
+ <BellNotifications
237
+ notifications={notifications}
238
+ onFilterChange={onFilterChange}
239
+ onNotificationClick={onNotificationClick}
240
+ handleMarkAllAsRead={handleMarkAllAsRead}
241
+ />
242
+
243
+ <span className={styles.navText}>{t('navigation.notifications')}</span>
244
+ </div>
245
+ </div>
246
+
247
+ {!isMobile &&
248
+ <div>
249
+ <img src={omniumLogo} className={styles.omniumLogoImg} alt="omnium logo" />
250
+ </div>
251
+ }
252
+ </div>
253
+ );
254
+ };
@@ -0,0 +1,193 @@
1
+ .footer {
2
+ display: flex;
3
+ justify-content: space-between;
4
+ align-items: center;
5
+ position: fixed;
6
+ bottom: 10px;
7
+ width: 100%;
8
+ height: 60px;
9
+ padding: 0 10px;
10
+ box-sizing: border-box;
11
+ z-index: 10;
12
+
13
+ @media (min-width: 767px) {
14
+ background-color: var(--color-black);
15
+ bottom: 0px;
16
+ }
17
+
18
+ .navigation {
19
+ display: flex;
20
+ justify-content: space-between;
21
+ width: 100%;
22
+
23
+ @media (min-width: 767px) {
24
+ width: fit-content;
25
+ gap: 100px;
26
+ }
27
+ }
28
+
29
+ .content {
30
+ font-size: var(--font-size-body-1);
31
+ color: var(--color-grey-light);
32
+ }
33
+
34
+ .brand {
35
+ width: 100%;
36
+ margin-bottom: 4px;
37
+ }
38
+
39
+ .time {
40
+ margin-left: 8px;
41
+ }
42
+
43
+ .date,
44
+ .time {
45
+ display: inline-block;
46
+ }
47
+
48
+ .omniumLogoImg {
49
+ height: 25px;
50
+ align-self: flex-end;
51
+ }
52
+
53
+ .navItem {
54
+ display: flex;
55
+ flex-direction: column;
56
+ align-items: center;
57
+ justify-content: center;
58
+ flex: 1;
59
+
60
+ @media (min-width: 767px) {
61
+ flex-direction: row;
62
+ gap: 12px;
63
+ flex: unset;
64
+ }
65
+
66
+ .navIcon {
67
+ font-size: 26px;
68
+ color: #0033a0;
69
+ cursor: pointer;
70
+
71
+ @media (min-width: 767px) {
72
+ color: var(--color-grey-light);
73
+ }
74
+ }
75
+
76
+ .navText {
77
+ font-size: 12px;
78
+ margin-top: 4px;
79
+ color: #0033a0;
80
+
81
+ @media (min-width: 767px) {
82
+ color: var(--color-grey-light);
83
+ }
84
+ }
85
+ }
86
+
87
+ .addButtonContainer {
88
+ display: flex;
89
+ justify-content: center;
90
+ flex: 1;
91
+
92
+ @media (min-width: 767px) {
93
+ flex: unset;
94
+ }
95
+
96
+ .addButton {
97
+ display: flex;
98
+ justify-content: center;
99
+ align-items: center;
100
+ width: 50px;
101
+ height: 50px;
102
+ background-color: #0033a0;
103
+ border-radius: 8px;
104
+ cursor: pointer;
105
+
106
+ @media (min-width: 767px) {
107
+ background-color: var(--color-grey-light);
108
+ }
109
+
110
+ .addIcon {
111
+ font-size: 20px;
112
+ color: white;
113
+
114
+ @media (min-width: 767px) {
115
+ color: #0033a0;
116
+ }
117
+ }
118
+ }
119
+ }
120
+
121
+ .notificationContainer {
122
+ position: relative;
123
+ }
124
+ }
125
+
126
+ .rotateIcon {
127
+ transform: rotate(45deg);
128
+ transition: transform 0.3s ease;
129
+ }
130
+
131
+ .circularMenu {
132
+ position: absolute;
133
+ bottom: 70px;
134
+ width: 180px;
135
+ left: 50%;
136
+ transform: translateX(-50%);
137
+ display: flex;
138
+ flex-direction: column;
139
+ align-items: center;
140
+ gap: 12px;
141
+ z-index: 999;
142
+
143
+ @media (min-width: 767px) {
144
+ transform: translateX(-65%);
145
+ }
146
+ }
147
+
148
+ .circleMenuItem {
149
+ display: flex;
150
+ align-items: center;
151
+ background-color: white;
152
+ padding: 8px 16px;
153
+ border-radius: 20px;
154
+ box-shadow: 0 4px 12px rgba(0, 51, 160, 0.5);
155
+ cursor: pointer;
156
+ transition: all 0.2s ease;
157
+ animation: popIn 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
158
+
159
+ &:hover {
160
+ transform: translateY(-2px);
161
+ box-shadow: 0 6px 16px rgba(0, 0, 0, 0.12);
162
+ }
163
+ }
164
+
165
+ @keyframes popIn {
166
+ from {
167
+ opacity: 0;
168
+ transform: scale(0.8) translateY(10px);
169
+ }
170
+
171
+ to {
172
+ opacity: 1;
173
+ transform: scale(1) translateY(0);
174
+ }
175
+ }
176
+
177
+ .circleMenuIcon {
178
+ width: 28px;
179
+ height: 28px;
180
+ border-radius: 50%;
181
+ background-color: #f3f4f6;
182
+ display: flex;
183
+ align-items: center;
184
+ justify-content: center;
185
+ margin-right: 10px;
186
+ color: #4b5563;
187
+ }
188
+
189
+ .circleMenuLabel {
190
+ font-size: 14px;
191
+ font-weight: 500;
192
+ color: #374151;
193
+ }
@@ -31,3 +31,4 @@ export * from './ResponseType';
31
31
  export * from './Header';
32
32
  export * from './BellNotifications';
33
33
  export * from './MobileTable';
34
+ export * from './Footer2';
@@ -30,7 +30,14 @@
30
30
  "back": "Back",
31
31
  "logout": "Logout",
32
32
  "home": "Home",
33
- "profile": "Profile"
33
+ "profile": "Profile",
34
+ "notifications": "Notifications",
35
+ "create": "Create",
36
+ "delete": "Delete",
37
+ "edit": "Edit",
38
+ "export": "Export",
39
+ "print": "Print",
40
+ "update": "Update"
34
41
  },
35
42
  "actions": {
36
43
  "one": "One",
@@ -30,7 +30,14 @@
30
30
  "back": "Atrás",
31
31
  "logout": "Cerrar Sesión",
32
32
  "home": "Inicio",
33
- "profile": "Perfil"
33
+ "profile": "Perfil",
34
+ "notifications": "Notificaciones",
35
+ "create": "Crear",
36
+ "delete": "Eliminar",
37
+ "edit": "Editar",
38
+ "export": "Exportar",
39
+ "print": "Imprimir",
40
+ "update": "Actualizar"
34
41
  },
35
42
  "actions": {
36
43
  "one": "Uno",
@@ -27,10 +27,17 @@
27
27
  "Comments": "Comentários"
28
28
  },
29
29
  "navigation": {
30
- "back": "Voltar Atrás",
30
+ "back": "Voltar",
31
31
  "logout": "Terminar Sessão",
32
32
  "home": "Início",
33
- "profile": "Perfil"
33
+ "profile": "Perfil",
34
+ "notifications": "Notificações",
35
+ "create": "Criar",
36
+ "delete": "Apagar",
37
+ "edit": "Editar",
38
+ "export": "Exportar",
39
+ "print": "Imprimir",
40
+ "update": "Atualizar"
34
41
  },
35
42
  "actions": {
36
43
  "one": "um",