@visns-studio/visns-components 5.8.1 → 5.8.2

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/package.json CHANGED
@@ -82,7 +82,7 @@
82
82
  "react-dom": "^17.0.0 || ^18.0.0"
83
83
  },
84
84
  "name": "@visns-studio/visns-components",
85
- "version": "5.8.1",
85
+ "version": "5.8.2",
86
86
  "description": "Various packages to assist in the development of our Custom Applications.",
87
87
  "main": "src/index.js",
88
88
  "files": [
@@ -9,7 +9,8 @@ import imageCompression from 'browser-image-compression';
9
9
 
10
10
  import { confirmDialog } from '../utils/ConfirmDialog';
11
11
  import CustomFetch from '../crm/Fetch';
12
- import SortableList from './sorting/List';
12
+ import Gallery from './Gallery';
13
+ import styles from './styles/DropZone.module.css';
13
14
 
14
15
  function VisnsDropZone({ fetchData, files, settings }) {
15
16
  const [data, setData] = useState(files);
@@ -210,10 +211,92 @@ function VisnsDropZone({ fetchData, files, settings }) {
210
211
 
211
212
  return (
212
213
  <>
213
- {settings.type === 'gallery' ? (
214
- <div className="image-container">
215
- {data && data.length > 0 ? (
216
- <SortableList
214
+ <div className={styles.dropzoneWrapper}>
215
+ <div className={styles.progress}>
216
+ <motion.div
217
+ className={styles.progressBar}
218
+ initial={{ width: '0%' }}
219
+ animate={{
220
+ width: loadingProgress + '%',
221
+ }}
222
+ transition={{
223
+ duration: 1,
224
+ ease: [0.165, 0.84, 0.44, 1.0],
225
+ }}
226
+ />
227
+ </div>
228
+ <section className={styles.dropzoneContainer}>
229
+ <div {...getRootProps()}>
230
+ <input {...getInputProps()} />
231
+ {isDragActive ? (
232
+ <p>Drop the files here ...</p>
233
+ ) : (
234
+ <p>
235
+ Drag 'n' drop some files here, or click to
236
+ select files
237
+ </p>
238
+ )}
239
+ </div>
240
+ {settings.type !== 'gallery' ? (
241
+ <ul className={styles.dropzoneFiles}>
242
+ {data && data.length > 0
243
+ ? data.map((a, b) => (
244
+ <li
245
+ onClick={() => {
246
+ handleDownload(a.id);
247
+ }}
248
+ key={'tf-' + b}
249
+ className={styles.dropzoneFileItem}
250
+ >
251
+ <div
252
+ className={
253
+ styles.dropzoneFileContent
254
+ }
255
+ >
256
+ <File
257
+ strokeWidth={2}
258
+ size={20}
259
+ className={
260
+ styles.dropzoneFileIcon
261
+ }
262
+ />
263
+ <span
264
+ className={
265
+ styles.dropzoneFileName
266
+ }
267
+ >
268
+ {a.file_name}
269
+ </span>
270
+ </div>
271
+ <button
272
+ onClick={(e) => {
273
+ e.preventDefault();
274
+ e.stopPropagation();
275
+ handleDelete(
276
+ a.id,
277
+ a.file_name
278
+ );
279
+ }}
280
+ className={
281
+ styles.dropzoneDeleteBtn
282
+ }
283
+ aria-label="Delete file"
284
+ title="Delete file"
285
+ >
286
+ <TrashCan
287
+ strokeWidth={2}
288
+ size={18}
289
+ />
290
+ </button>
291
+ </li>
292
+ ))
293
+ : null}
294
+ </ul>
295
+ ) : null}
296
+ </section>
297
+ {settings.type === 'gallery' ? (
298
+ <div className={styles.imageContainer}>
299
+ <Gallery
217
300
  items={data}
218
301
  onSortEnd={onSortEnd}
219
302
  axis="xy"
@@ -222,115 +305,68 @@ function VisnsDropZone({ fetchData, files, settings }) {
222
305
  handleDelete={handleDelete}
223
306
  handleEdit={handleEdit}
224
307
  useDragHandle
225
- showImage={true}
308
+ distance={1}
226
309
  />
227
- ) : null}
228
- </div>
229
- ) : null}
230
- <div className="progress">
231
- <motion.div
232
- className="progress__bar"
233
- initial={{ width: '0%' }}
234
- animate={{
235
- width: loadingProgress + '%',
236
- }}
237
- transition={{
238
- duration: 1,
239
- ease: [0.165, 0.84, 0.44, 1.0],
240
- }}
241
- />
242
- </div>
243
- <section className="dropzone__container">
244
- <div {...getRootProps()}>
245
- <input {...getInputProps()} />
246
- {isDragActive ? (
247
- <p>Drop the files here ...</p>
248
- ) : (
249
- <p>
250
- Drag 'n' drop some files here, or click to select
251
- files
252
- </p>
253
- )}
254
- </div>
255
- {settings.type !== 'gallery' ? (
256
- <ul className="dropzone__files">
257
- {data && data.length > 0
258
- ? data.map((a, b) => (
259
- <li
260
- onClick={() => {
261
- handleDownload(a.id);
262
- }}
263
- key={'tf-' + b}
264
- >
265
- <File strokeWidth={2} size={18} />
266
- {a.file_name}
267
- <button
268
- onClick={(e) => {
269
- e.preventDefault();
270
- e.stopPropagation();
271
-
272
- handleDelete(a.id, a.file_name);
273
- }}
274
- >
275
- <TrashCan strokeWidth={2} size={18} />
276
- </button>
277
- </li>
278
- ))
279
- : null}
280
- </ul>
310
+ </div>
281
311
  ) : null}
282
- </section>
312
+ </div>
283
313
  <Popup
284
314
  open={modalShow}
285
315
  onClose={modalClose}
286
316
  closeOnDocumentClick={false}
287
317
  >
288
- <div className="modalwrap top--modal">
289
- <div className="modal">
290
- <div className="modal__header">
318
+ <div className={`${styles.modalwrap} top--modal`}>
319
+ <div className={styles.modal}>
320
+ <div className={styles.modalHeader}>
291
321
  <h1>Update File Metadata</h1>
292
322
  <button
293
- className="modal__close"
323
+ className={styles.modalClose}
294
324
  onClick={modalClose}
295
325
  >
296
326
  <CircleX strokeWidth={1} size={24} />
297
327
  </button>
298
328
  </div>
299
- <div className="modal__content">
329
+ <div className={styles.modalContent}>
300
330
  <div className="formcontainer">
301
331
  <form
302
332
  className="modalForm"
303
333
  onSubmit={handleSubmit}
304
334
  >
305
- <div className="formItem fwItem">
306
- <label className="fi__label">
335
+ <div
336
+ className={`${styles.formItem} fwItem`}
337
+ >
338
+ <label className={styles.fiLabel}>
307
339
  <input
308
340
  type="text"
309
341
  name="title"
310
342
  onChange={handleChange}
311
343
  value={modalData.title}
312
344
  />
313
- <span className="fi__span">
345
+ <span className={styles.fiSpan}>
314
346
  Title
315
347
  </span>
316
348
  </label>
317
349
  </div>
318
- <div className="formItem fwItem">
319
- <label className="fi__label">
350
+ <div
351
+ className={`${styles.formItem} fwItem`}
352
+ >
353
+ <label className={styles.fiLabel}>
320
354
  <textarea
321
355
  name="description"
322
356
  rows="5"
323
357
  onChange={handleChange}
324
358
  value={modalData.description}
325
359
  ></textarea>
326
- <span className="fi__span">
360
+ <span className={styles.fiSpan}>
327
361
  Description
328
362
  </span>
329
363
  </label>
330
364
  </div>
331
- <div className="formItem fwItem lastItem">
365
+ <div
366
+ className={`${styles.formItem} fwItem lastItem`}
367
+ >
332
368
  <button
333
- className="btn modalsave"
369
+ className={`btn ${styles.btnModalsave}`}
334
370
  type="submit"
335
371
  >
336
372
  Save
@@ -0,0 +1,147 @@
1
+ import React from 'react';
2
+ import {
3
+ SortableContainer,
4
+ SortableElement,
5
+ sortableHandle,
6
+ } from 'react-sortable-hoc';
7
+ import { ChevronVertical, Pencil, TrashCan } from 'akar-icons';
8
+ import styles from './styles/Gallery.module.css';
9
+
10
+ // Drag handle component
11
+ const DragHandle = sortableHandle(() => (
12
+ <div className={styles.galleryDragHandle}>
13
+ <ChevronVertical strokeWidth={1.5} size={16} />
14
+ </div>
15
+ ));
16
+
17
+ // Utility function to truncate text
18
+ const truncateText = (text, maxLength) => {
19
+ if (text && text.length > maxLength) {
20
+ return `${text.substring(0, maxLength)}...`;
21
+ }
22
+ return text;
23
+ };
24
+
25
+ // Sortable gallery item
26
+ const SortableGalleryItem = SortableElement(
27
+ ({ value, handleEdit, handleDelete }) => {
28
+ const imageUrl = value.image_url || value.file_url;
29
+ const title =
30
+ value.file_title || value.label || value.file_name || 'Untitled';
31
+ const description = value.file_description || '';
32
+
33
+ return (
34
+ <div className={styles.galleryItem}>
35
+ <DragHandle />
36
+
37
+ <div className={styles.galleryImage}>
38
+ {imageUrl ? (
39
+ <img src={imageUrl} alt={title} />
40
+ ) : (
41
+ <img
42
+ src="data:image/png;base64,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"
43
+ alt="Placeholder"
44
+ />
45
+ )}
46
+ </div>
47
+
48
+ <div className={styles.galleryInfo}>
49
+ <h3 className={styles.galleryTitle}>
50
+ {truncateText(title, 30)}
51
+ </h3>
52
+ {description && (
53
+ <p className={styles.galleryDescription}>
54
+ {truncateText(description, 100)}
55
+ </p>
56
+ )}
57
+ </div>
58
+
59
+ <div className={styles.galleryControls}>
60
+ {handleEdit && (
61
+ <button
62
+ className={`${styles.galleryControl} ${styles.edit}`}
63
+ onClick={(e) => {
64
+ e.preventDefault();
65
+ e.stopPropagation();
66
+ handleEdit(value);
67
+ }}
68
+ title="Edit"
69
+ >
70
+ <Pencil strokeWidth={1.5} size={16} />
71
+ </button>
72
+ )}
73
+
74
+ {handleDelete && (
75
+ <button
76
+ className={`${styles.galleryControl} ${styles.delete}`}
77
+ onClick={(e) => {
78
+ e.preventDefault();
79
+ e.stopPropagation();
80
+ handleDelete(
81
+ value.id,
82
+ value.file_name || value.label
83
+ );
84
+ }}
85
+ title="Delete"
86
+ >
87
+ <TrashCan strokeWidth={1.5} size={16} />
88
+ </button>
89
+ )}
90
+ </div>
91
+ </div>
92
+ );
93
+ }
94
+ );
95
+
96
+ // Sortable gallery container
97
+ const Gallery = SortableContainer(({ items, handleEdit, handleDelete }) => {
98
+ if (!items || items.length === 0) {
99
+ return (
100
+ <div className={styles.galleryEmpty}>
101
+ <div className={styles.galleryEmptyIcon}>
102
+ <svg
103
+ xmlns="http://www.w3.org/2000/svg"
104
+ width="48"
105
+ height="48"
106
+ viewBox="0 0 24 24"
107
+ fill="none"
108
+ stroke="currentColor"
109
+ strokeWidth="1"
110
+ strokeLinecap="round"
111
+ strokeLinejoin="round"
112
+ >
113
+ <rect
114
+ x="3"
115
+ y="3"
116
+ width="18"
117
+ height="18"
118
+ rx="2"
119
+ ry="2"
120
+ ></rect>
121
+ <circle cx="8.5" cy="8.5" r="1.5"></circle>
122
+ <polyline points="21 15 16 10 5 21"></polyline>
123
+ </svg>
124
+ </div>
125
+ <p className={styles.galleryEmptyText}>
126
+ No images to display. Drag and drop files to upload.
127
+ </p>
128
+ </div>
129
+ );
130
+ }
131
+
132
+ return (
133
+ <div className={styles.galleryGrid}>
134
+ {items.map((value, index) => (
135
+ <SortableGalleryItem
136
+ key={`item-${index}`}
137
+ index={index}
138
+ value={value}
139
+ handleEdit={handleEdit}
140
+ handleDelete={handleDelete}
141
+ />
142
+ ))}
143
+ </div>
144
+ );
145
+ });
146
+
147
+ export default Gallery;
@@ -11,9 +11,9 @@
11
11
 
12
12
  :global {
13
13
  .delete {
14
- width: 22px;
15
- height: 22px;
16
- color: var(--primary-color);
14
+ width: 32px;
15
+ height: 32px;
16
+ color: #fff;
17
17
  transition: color 0.2s ease;
18
18
  cursor: pointer;
19
19
  display: block;
@@ -24,8 +24,8 @@
24
24
  }
25
25
 
26
26
  .pencil-edit {
27
- width: 22px;
28
- height: 22px;
27
+ width: 32px;
28
+ height: 32px;
29
29
  color: var(--primary-color);
30
30
  transition: color 0.2s ease;
31
31
  cursor: pointer;
@@ -0,0 +1,347 @@
1
+ /* DropZone Component Styles */
2
+
3
+ /* Dropzone wrapper */
4
+ .dropzoneWrapper {
5
+ display: flex;
6
+ flex-direction: column;
7
+ width: 100%;
8
+ }
9
+
10
+ /* Gallery container */
11
+ .imageContainer {
12
+ width: 100%;
13
+ padding: 1rem;
14
+ box-sizing: border-box;
15
+ }
16
+
17
+ /* Override the sortlist styles for gallery view */
18
+ :global(.sortlist) {
19
+ display: grid;
20
+ grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
21
+ gap: 16px;
22
+ width: 100%;
23
+ }
24
+
25
+ :global(.sortlist li) {
26
+ position: relative;
27
+ display: flex;
28
+ flex-direction: column;
29
+ padding: 0;
30
+ border-radius: 8px;
31
+ overflow: hidden;
32
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
33
+ transition: transform 0.2s ease, box-shadow 0.2s ease;
34
+ margin-bottom: 16px;
35
+ }
36
+
37
+ :global(.sortlist li:hover) {
38
+ transform: translateY(-4px);
39
+ box-shadow: 0 8px 16px rgba(0, 0, 0, 0.15);
40
+ }
41
+
42
+ :global(.sortlist .sortimg) {
43
+ width: 100%;
44
+ height: auto;
45
+ max-height: 500px;
46
+ margin: 0;
47
+ overflow: hidden;
48
+ }
49
+
50
+ :global(.sortlist .sortimg img) {
51
+ width: 100%;
52
+ height: auto;
53
+ max-height: 500px;
54
+ object-fit: contain;
55
+ display: block;
56
+ background-color: #f5f5f5;
57
+ }
58
+
59
+ :global(.sortlist .drag-handle) {
60
+ position: absolute;
61
+ top: 10px;
62
+ left: 10px;
63
+ z-index: 10;
64
+ background-color: rgba(0, 0, 0, 0.7);
65
+ border-radius: 50%;
66
+ width: 32px;
67
+ height: 32px;
68
+ display: flex;
69
+ align-items: center;
70
+ justify-content: center;
71
+ cursor: grab;
72
+ color: rgba(255, 255, 255, 0.9);
73
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
74
+ }
75
+
76
+ :global(.sortlist .actionIcons) {
77
+ position: absolute;
78
+ top: 10px;
79
+ right: 10px;
80
+ display: flex;
81
+ gap: 8px;
82
+ z-index: 10;
83
+ }
84
+
85
+ :global(.sortlist .delete),
86
+ :global(.sortlist .pencil-edit) {
87
+ background-color: rgba(0, 0, 0, 0.7);
88
+ border-radius: 50%;
89
+ width: 32px;
90
+ height: 32px;
91
+ display: flex;
92
+ align-items: center;
93
+ justify-content: center;
94
+ transition: all 0.2s ease;
95
+ cursor: pointer;
96
+ color: rgba(255, 255, 255, 0.9);
97
+ }
98
+
99
+ :global(.sortlist .delete:hover),
100
+ :global(.sortlist .pencil-edit:hover) {
101
+ transform: scale(1.05);
102
+ background-color: rgba(0, 0, 0, 0.9);
103
+ color: rgba(255, 255, 255, 1);
104
+ }
105
+
106
+ /* Dropzone container */
107
+ .dropzoneContainer {
108
+ padding: 1rem;
109
+ display: flex;
110
+ flex-direction: column;
111
+ gap: 1rem;
112
+ margin-bottom: 1rem;
113
+ width: 100%;
114
+ box-sizing: border-box;
115
+ }
116
+
117
+ .dropzoneContainer > div {
118
+ flex: 1;
119
+ display: flex;
120
+ flex-direction: column;
121
+ align-items: center;
122
+ justify-content: center;
123
+ padding: 2rem;
124
+ border: 2px dashed rgba(var(--primary-rgb, 0, 0, 0), 0.2);
125
+ border-radius: 6px;
126
+ background-color: rgba(var(--tertiary-color-rgb, 240, 240, 240), 0.5);
127
+ color: var(--paragraph-color, #666);
128
+ outline: none;
129
+ transition: all 0.2s ease;
130
+ }
131
+
132
+ .dropzoneContainer > div:hover {
133
+ border-color: rgba(var(--primary-rgb, 0, 0, 0), 0.4);
134
+ background-color: rgba(var(--tertiary-color-rgb, 240, 240, 240), 0.7);
135
+ }
136
+
137
+ .dropzoneContainer p {
138
+ padding: 0;
139
+ margin: 0;
140
+ font-size: 1rem;
141
+ text-align: center;
142
+ }
143
+
144
+ /* Progress bar */
145
+ .progress {
146
+ width: 100%;
147
+ height: 4px;
148
+ background-color: rgba(var(--tertiary-color-rgb, 240, 240, 240), 1);
149
+ border-radius: 2px;
150
+ overflow: hidden;
151
+ margin: 0.5rem 0;
152
+ }
153
+
154
+ .progressBar {
155
+ height: 100%;
156
+ background-color: var(--primary-color, #333);
157
+ border-radius: 2px;
158
+ }
159
+
160
+ /* File list container */
161
+ .dropzoneFiles {
162
+ list-style: none;
163
+ padding: 0;
164
+ margin: 1em 0;
165
+ box-sizing: border-box;
166
+ display: grid;
167
+ grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
168
+ gap: 12px;
169
+ }
170
+
171
+ /* File item */
172
+ .dropzoneFileItem {
173
+ display: flex;
174
+ justify-content: space-between;
175
+ align-items: center;
176
+ background: rgba(var(--tertiary-color-rgb, 240, 240, 240), 1);
177
+ color: var(--primary-color, #333);
178
+ border-radius: 6px;
179
+ transition: all 0.2s ease;
180
+ cursor: pointer;
181
+ padding: 12px 16px;
182
+ box-sizing: border-box;
183
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
184
+ border: 1px solid rgba(0, 0, 0, 0.05);
185
+ }
186
+
187
+ .dropzoneFileItem:hover {
188
+ background: rgba(var(--secondary-color-rgb, 220, 220, 220), 0.65);
189
+ transform: translateY(-2px);
190
+ box-shadow: 0 3px 6px rgba(0, 0, 0, 0.1);
191
+ }
192
+
193
+ /* File content container */
194
+ .dropzoneFileContent {
195
+ display: flex;
196
+ align-items: center;
197
+ gap: 12px;
198
+ overflow: hidden;
199
+ flex: 1;
200
+ }
201
+
202
+ /* File icon */
203
+ .dropzoneFileIcon {
204
+ color: var(--primary-color, #333);
205
+ flex-shrink: 0;
206
+ }
207
+
208
+ /* File name */
209
+ .dropzoneFileName {
210
+ font-size: 0.95rem;
211
+ white-space: nowrap;
212
+ overflow: hidden;
213
+ text-overflow: ellipsis;
214
+ }
215
+
216
+ /* Delete button */
217
+ .dropzoneDeleteBtn {
218
+ background: none;
219
+ border: none;
220
+ outline: none;
221
+ cursor: pointer;
222
+ color: var(--primary-color, #fff);
223
+ padding: 6px;
224
+ border-radius: 4px;
225
+ display: flex;
226
+ align-items: center;
227
+ justify-content: center;
228
+ transition: all 0.2s ease;
229
+ margin-left: 8px;
230
+ }
231
+
232
+ .dropzoneDeleteBtn:hover {
233
+ color: var(--secondary-color, #f44336);
234
+ background-color: rgba(244, 67, 54, 0.1);
235
+ }
236
+
237
+ /* Modal styles */
238
+ .modalwrap {
239
+ width: 100%;
240
+ max-width: 500px;
241
+ }
242
+
243
+ .modal {
244
+ background: white;
245
+ border-radius: 8px;
246
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
247
+ overflow: hidden;
248
+ }
249
+
250
+ .modalHeader {
251
+ display: flex;
252
+ justify-content: space-between;
253
+ align-items: center;
254
+ padding: 1rem 1.5rem;
255
+ border-bottom: 1px solid rgba(0, 0, 0, 0.1);
256
+ }
257
+
258
+ .modalHeader h1 {
259
+ margin: 0;
260
+ font-size: 1.25rem;
261
+ font-weight: 600;
262
+ color: var(--heading-color, #333);
263
+ }
264
+
265
+ .modalClose {
266
+ background: none;
267
+ border: none;
268
+ cursor: pointer;
269
+ color: var(--paragraph-color, #666);
270
+ padding: 4px;
271
+ border-radius: 4px;
272
+ display: flex;
273
+ align-items: center;
274
+ justify-content: center;
275
+ transition: all 0.2s ease;
276
+ }
277
+
278
+ .modalClose:hover {
279
+ color: var(--secondary-color, #f44336);
280
+ background-color: rgba(244, 67, 54, 0.1);
281
+ }
282
+
283
+ .modalContent {
284
+ padding: 1.5rem;
285
+ }
286
+
287
+ .formItem {
288
+ margin-bottom: 1.25rem;
289
+ }
290
+
291
+ .fiLabel {
292
+ position: relative;
293
+ display: block;
294
+ }
295
+
296
+ .fiLabel input,
297
+ .fiLabel textarea {
298
+ width: 100%;
299
+ padding: 0.75rem 1rem;
300
+ border: 1px solid rgba(0, 0, 0, 0.2);
301
+ border-radius: 4px;
302
+ font-size: 1rem;
303
+ transition: all 0.2s ease;
304
+ }
305
+
306
+ .fiLabel input:focus,
307
+ .fiLabel textarea:focus {
308
+ border-color: var(--primary-color, #333);
309
+ outline: none;
310
+ box-shadow: 0 0 0 2px rgba(var(--primary-rgb, 0, 0, 0), 0.1);
311
+ }
312
+
313
+ .fiSpan {
314
+ position: absolute;
315
+ top: -0.5rem;
316
+ left: 0.75rem;
317
+ padding: 0 0.25rem;
318
+ background-color: white;
319
+ font-size: 0.8rem;
320
+ color: var(--paragraph-color, #666);
321
+ }
322
+
323
+ .btnModalsave {
324
+ background-color: var(--primary-color, #333);
325
+ color: white;
326
+ border: none;
327
+ border-radius: 4px;
328
+ padding: 0.75rem 1.5rem;
329
+ font-size: 1rem;
330
+ cursor: pointer;
331
+ transition: all 0.2s ease;
332
+ }
333
+
334
+ .btnModalsave:hover {
335
+ background-color: var(--secondary-color, #555);
336
+ }
337
+
338
+ /* Responsive adjustments */
339
+ @media (max-width: 768px) {
340
+ .dropzoneFiles {
341
+ grid-template-columns: 1fr;
342
+ }
343
+
344
+ .modalwrap {
345
+ width: 90%;
346
+ }
347
+ }
@@ -0,0 +1,197 @@
1
+ /* Gallery grid */
2
+ .galleryGrid {
3
+ display: grid;
4
+ grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
5
+ gap: 20px;
6
+ margin-top: 20px;
7
+ }
8
+
9
+ .galleryItem {
10
+ position: relative;
11
+ border-radius: 8px;
12
+ overflow: hidden;
13
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
14
+ background-color: white;
15
+ transition: transform 0.2s ease, box-shadow 0.2s ease;
16
+ height: 100%;
17
+ display: flex;
18
+ flex-direction: column;
19
+ }
20
+
21
+ .galleryItem:hover {
22
+ transform: translateY(-3px);
23
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
24
+ }
25
+
26
+ .galleryImage {
27
+ position: relative;
28
+ width: 100%;
29
+ height: 200px;
30
+ overflow: hidden;
31
+ background-color: #f5f5f5;
32
+ }
33
+
34
+ .galleryImage img {
35
+ width: 100%;
36
+ height: 100%;
37
+ object-fit: cover;
38
+ transition: transform 0.3s ease;
39
+ }
40
+
41
+ .galleryItem:hover .galleryImage img {
42
+ transform: scale(1.05);
43
+ }
44
+
45
+ .galleryInfo {
46
+ padding: 15px;
47
+ flex-grow: 1;
48
+ display: flex;
49
+ flex-direction: column;
50
+ }
51
+
52
+ .galleryTitle {
53
+ margin: 0 0 8px 0;
54
+ font-size: 16px;
55
+ font-weight: 600;
56
+ color: #333;
57
+ }
58
+
59
+ .galleryDescription {
60
+ margin: 0;
61
+ font-size: 14px;
62
+ color: #666;
63
+ line-height: 1.4;
64
+ display: -webkit-box;
65
+ -webkit-line-clamp: 3;
66
+ -webkit-box-orient: vertical;
67
+ overflow: hidden;
68
+ }
69
+
70
+ /* Gallery controls */
71
+ .galleryControls {
72
+ position: absolute;
73
+ top: 10px;
74
+ right: 10px;
75
+ display: flex;
76
+ gap: 8px;
77
+ z-index: 10;
78
+ }
79
+
80
+ .galleryControl {
81
+ background-color: rgba(0, 0, 0, 0.7);
82
+ border-radius: 50%;
83
+ width: 32px;
84
+ height: 32px;
85
+ display: flex;
86
+ align-items: center;
87
+ justify-content: center;
88
+ transition: all 0.2s ease;
89
+ cursor: pointer;
90
+ border: none;
91
+ outline: none;
92
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
93
+ padding: 0;
94
+ color: rgba(255, 255, 255, 0.9);
95
+ }
96
+
97
+ .galleryControl svg {
98
+ width: 16px;
99
+ height: 16px;
100
+ }
101
+
102
+ .galleryControl:hover {
103
+ transform: scale(1.05);
104
+ background-color: rgba(0, 0, 0, 0.9);
105
+ box-shadow: 0 3px 6px rgba(0, 0, 0, 0.25);
106
+ color: rgba(255, 255, 255, 1);
107
+ }
108
+
109
+ .edit {
110
+ background-color: rgba(0, 0, 0, 0.7);
111
+ }
112
+
113
+ .delete {
114
+ background-color: rgba(0, 0, 0, 0.7);
115
+ }
116
+
117
+ .galleryDragHandle {
118
+ position: absolute;
119
+ top: 10px;
120
+ left: 10px;
121
+ z-index: 10;
122
+ background-color: rgba(0, 0, 0, 0.7);
123
+ border-radius: 50%;
124
+ width: 32px;
125
+ height: 32px;
126
+ display: flex;
127
+ align-items: center;
128
+ justify-content: center;
129
+ cursor: grab;
130
+ color: rgba(255, 255, 255, 0.9);
131
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
132
+ }
133
+
134
+ .galleryDragHandle svg {
135
+ width: 16px;
136
+ height: 16px;
137
+ }
138
+
139
+ /* Empty state */
140
+ .galleryEmpty {
141
+ display: flex;
142
+ flex-direction: column;
143
+ align-items: center;
144
+ justify-content: center;
145
+ padding: 40px;
146
+ background-color: #f9f9f9;
147
+ border-radius: 8px;
148
+ border: 2px dashed #ddd;
149
+ margin-top: 20px;
150
+ }
151
+
152
+ .galleryEmptyIcon {
153
+ color: #ccc;
154
+ margin-bottom: 16px;
155
+ }
156
+
157
+ .galleryEmptyText {
158
+ color: #888;
159
+ text-align: center;
160
+ font-size: 16px;
161
+ margin: 0;
162
+ }
163
+
164
+ /* Responsive adjustments */
165
+ @media (max-width: 1200px) {
166
+ .galleryGrid {
167
+ grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
168
+ }
169
+ }
170
+
171
+ @media (max-width: 768px) {
172
+ .galleryGrid {
173
+ grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
174
+ }
175
+
176
+ .galleryImage {
177
+ height: 160px;
178
+ }
179
+
180
+ .galleryInfo {
181
+ padding: 12px;
182
+ }
183
+
184
+ .galleryTitle {
185
+ font-size: 14px;
186
+ }
187
+
188
+ .galleryDescription {
189
+ font-size: 12px;
190
+ }
191
+ }
192
+
193
+ @media (max-width: 480px) {
194
+ .galleryGrid {
195
+ grid-template-columns: 1fr;
196
+ }
197
+ }
@@ -8,7 +8,7 @@ import Download from '../Download';
8
8
  import { saveAs } from 'file-saver';
9
9
  import Swal from 'sweetalert2';
10
10
  import styles from './styles/GenericReport.module.scss';
11
- import './styles/SweetAlertCustom.css';
11
+ import './styles/SweetAlert.module.css';
12
12
 
13
13
  // Utility function to format names from camelCase or snake_case to Title Case with spaces
14
14
  const formatName = (name) => {
@@ -1,13 +1,13 @@
1
1
  /* Custom styles for SweetAlert2 popups */
2
2
 
3
- .swal2-popup {
3
+ :global(.swal2-popup) {
4
4
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
5
5
  Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
6
6
  border-radius: 6px;
7
7
  padding: 18px;
8
8
  }
9
9
 
10
- .swal2-title {
10
+ :global(.swal2-title) {
11
11
  font-size: 18px !important;
12
12
  font-weight: 600 !important;
13
13
  color: #1f2937 !important;
@@ -15,7 +15,7 @@
15
15
  padding: 0 !important;
16
16
  }
17
17
 
18
- .swal2-html-container {
18
+ :global(.swal2-html-container) {
19
19
  text-align: left !important;
20
20
  font-size: 14px !important;
21
21
  color: #4b5563 !important;
@@ -25,15 +25,15 @@
25
25
  }
26
26
 
27
27
  /* Button styles for symmetrical appearance */
28
- .swal2-actions {
28
+ :global(.swal2-actions) {
29
29
  display: flex !important;
30
30
  justify-content: center !important;
31
31
  gap: 10px !important;
32
32
  width: 100% !important;
33
33
  }
34
34
 
35
- .swal2-confirm,
36
- .swal2-cancel {
35
+ :global(.swal2-confirm),
36
+ :global(.swal2-cancel) {
37
37
  flex: 1 !important;
38
38
  max-width: 120px !important;
39
39
  min-width: 100px !important;
@@ -44,41 +44,41 @@
44
44
  margin: 0 !important;
45
45
  }
46
46
 
47
- .swal2-confirm {
47
+ :global(.swal2-confirm) {
48
48
  background-color: #2563eb !important;
49
49
  }
50
50
 
51
- .swal2-cancel {
51
+ :global(.swal2-cancel) {
52
52
  background-color: #6b7280 !important;
53
53
  }
54
54
 
55
- .swal2-confirm:focus {
55
+ :global(.swal2-confirm:focus) {
56
56
  box-shadow: 0 0 0 2px rgba(37, 99, 235, 0.3) !important;
57
57
  }
58
58
 
59
- .swal2-cancel:focus {
59
+ :global(.swal2-cancel:focus) {
60
60
  box-shadow: 0 0 0 2px rgba(107, 114, 128, 0.3) !important;
61
61
  }
62
62
 
63
63
  /* Make the modal more compact */
64
- .swal2-popup.swal2-modal {
64
+ :global(.swal2-popup.swal2-modal) {
65
65
  max-width: 90%;
66
66
  }
67
67
 
68
68
  /* Reduce spacing between elements */
69
- .swal2-icon {
69
+ :global(.swal2-icon) {
70
70
  margin: 0 auto 10px !important;
71
71
  }
72
72
 
73
73
  /* Adjust content spacing */
74
- .swal2-content {
74
+ :global(.swal2-content) {
75
75
  padding: 0 !important;
76
76
  }
77
77
 
78
78
  /* Override browser default font-size clamp for lists */
79
- .swal2-html-container ul,
80
- .swal2-html-container ol,
81
- .swal2-html-container li {
79
+ :global(.swal2-html-container ul),
80
+ :global(.swal2-html-container ol),
81
+ :global(.swal2-html-container li) {
82
82
  font-size: 14px !important;
83
83
  line-height: 1.4 !important;
84
84
  font-size: clamp(
@@ -89,21 +89,21 @@
89
89
  }
90
90
 
91
91
  /* Ensure consistent styling for all text elements */
92
- .swal2-html-container p,
93
- .swal2-html-container span,
94
- .swal2-html-container div,
95
- .swal2-html-container h4 {
92
+ :global(.swal2-html-container p),
93
+ :global(.swal2-html-container span),
94
+ :global(.swal2-html-container div),
95
+ :global(.swal2-html-container h4) {
96
96
  font-size: inherit !important;
97
97
  line-height: inherit !important;
98
98
  }
99
99
 
100
100
  /* Target the specific browser clamp issue */
101
- #swal2-html-container ul {
101
+ :global(#swal2-html-container ul) {
102
102
  font-size: 14px !important;
103
103
  font-size: clamp(14px, 14px, 14px) !important;
104
104
  }
105
105
 
106
- #swal2-html-container li {
106
+ :global(#swal2-html-container li) {
107
107
  font-size: 14px !important;
108
108
  font-size: clamp(14px, 14px, 14px) !important;
109
109
  }
@@ -1,5 +1,5 @@
1
1
  import Swal from 'sweetalert2';
2
- import '../crm/generic/styles/SweetAlertCustom.css';
2
+ import '../crm/generic/styles/SweetAlert.module.css';
3
3
 
4
4
  /**
5
5
  * A utility function that replaces react-confirm-alert with SweetAlert2