gbs-add-block 0.0.13 → 0.0.15-rc1100-beta

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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gbs-add-block",
3
- "version": "0.0.13",
3
+ "version": "0.0.15rc1100-beta",
4
4
  "description": "React Component Library",
5
5
  "files": [
6
6
  "index.js",
@@ -53,7 +53,11 @@ export const Grid = forwardRef((props: GridProps, ref) => {
53
53
  gridContainerClass = "flex flex-col min-w-screen border rounded-md overflow-hidden dark:bg-black",
54
54
  gridColumnStyleSelectAll = "border-b px-4 text-sm dark:text-white",
55
55
  gridColumnStyle = "border-b p-2 text-sm dark:text-white",
56
+ rowChange = () => {},
57
+ pageStatus = () => {},
56
58
  } = props;
59
+
60
+ // States Handling Grid
57
61
  const [workingDataSource, setWorkingDataSource] = useState<any>([]);
58
62
  const [fallbackSourceData, setFallbackSourceData] = useState([]);
59
63
  const [workingColumns, setWorkingColumns] = useState<any>([]);
@@ -65,6 +69,14 @@ export const Grid = forwardRef((props: GridProps, ref) => {
65
69
  const [activeFilterArray, setActiveFilterArray] = useState<any>([]);
66
70
  const [selectedRows, setSelectedRows] = useState<any[]>([]);
67
71
 
72
+ // This will returns the Page Navigation Status of Grid
73
+ // Such as totalPages in the grid, currentpage, etc..
74
+ useEffect(() => {
75
+ if (pageStatus) {
76
+ pageStatus({ currentPage: currentPage, totalPages: totalPages });
77
+ }
78
+ }, [pageStatus, currentPage, totalPages]);
79
+
68
80
  // Function to handle API datasource
69
81
  const getGridData = useCallback(async () => {
70
82
  try {
@@ -225,6 +237,9 @@ export const Grid = forwardRef((props: GridProps, ref) => {
225
237
  <column.template
226
238
  rowData={rowData}
227
239
  rowIndex={rowIndex + currentPage * pageSettings.pageNumber}
240
+ rowChange={(changes: any) => {
241
+ if (rowChange) rowChange(changes);
242
+ }}
228
243
  />
229
244
  );
230
245
  }
@@ -330,19 +345,24 @@ export const Grid = forwardRef((props: GridProps, ref) => {
330
345
  // RowSelection Handling Logic Ends Here ***
331
346
 
332
347
  // Making Grid Functions Accessible in Parent
333
- useImperativeHandle(ref, () => ({
334
- goToPage,
335
- nextPage,
336
- prevPage,
337
- goToFirstPage,
338
- goToEndPage,
339
- handleSearch,
340
- handleApplyFilter,
341
- clearFilter,
342
- workingDataSource,
343
- dataSource,
344
- }));
348
+ useImperativeHandle(
349
+ ref,
350
+ () => {
351
+ return {
352
+ goToPage,
353
+ nextPage,
354
+ prevPage,
355
+ goToFirstPage,
356
+ goToEndPage,
357
+ handleSearch,
358
+ handleApplyFilter,
359
+ clearFilter,
360
+ };
361
+ },
362
+ []
363
+ );
345
364
 
365
+ // JSX Rendering
346
366
  return (
347
367
  <div className={gridContainerClass}>
348
368
  <React.Fragment>
@@ -25,4 +25,6 @@ export type GridProps = {
25
25
  tableHeaderStyle?: string;
26
26
  gridColumnStyleSelectAll?: string;
27
27
  gridColumnStyle?: string;
28
+ rowChange?: any;
29
+ pageStatus?: any;
28
30
  };
@@ -74,6 +74,7 @@ export const Input = ({
74
74
  {/* If Type is password, this will show a password visible toggle button */}
75
75
  {props.type && props.type === "password" && (
76
76
  <button
77
+ type="button"
77
78
  className="absolute inset-y-0 right-0 flex items-center pr-2"
78
79
  onClick={toggleTypeForPassword}
79
80
  >
@@ -98,7 +99,9 @@ export const Input = ({
98
99
  value={otpValues[index]}
99
100
  onChange={(e) => updateOtpValue(index, e)}
100
101
  onKeyDown={(e) => handleKeydown(index, e)}
101
- // ref={(ref) => (inputRefs.current[index] = ref)}
102
+ ref={(ref) => {
103
+ inputRefs.current[index] = ref;
104
+ }}
102
105
  className={OTPClass}
103
106
  />
104
107
  ))}
@@ -8,6 +8,7 @@
8
8
  import React, {
9
9
  forwardRef,
10
10
  memo,
11
+ useCallback,
11
12
  useEffect,
12
13
  useImperativeHandle,
13
14
  useRef,
@@ -50,10 +51,6 @@ const MultiSelect = forwardRef<any, MultiSelectProps>((props, ref) => {
50
51
  handleDataSource();
51
52
  }, [items]);
52
53
 
53
- useEffect(() => {
54
- if (onSelect) onSelect(selected);
55
- }, [selected, onSelect]);
56
-
57
54
  useEffect(() => {
58
55
  const handleClickOutside = (event: MouseEvent) => {
59
56
  if (
@@ -87,13 +84,16 @@ const MultiSelect = forwardRef<any, MultiSelectProps>((props, ref) => {
87
84
  setShowPopover(!showPopover);
88
85
  };
89
86
 
90
- const handleSelect = (value: string) => {
91
- setSelected((prev) =>
92
- prev.includes(value)
93
- ? prev.filter((item) => item !== value)
94
- : [...prev, value]
95
- );
96
- };
87
+ const handleSelect = useCallback(
88
+ (value: string) => {
89
+ const newSelected = selected.includes(value)
90
+ ? selected.filter((item) => item !== value)
91
+ : [...selected, value];
92
+ setSelected(newSelected);
93
+ onSelect?.(newSelected);
94
+ },
95
+ [selected, onSelect]
96
+ );
97
97
 
98
98
  const getSelectedDisplay = () => {
99
99
  if (selected.length === 0) return placeholder;
@@ -127,6 +127,7 @@ const MultiSelect = forwardRef<any, MultiSelectProps>((props, ref) => {
127
127
  const clearSelected = () => {
128
128
  setSelected([]);
129
129
  setShowPopover(false);
130
+ if (onSelect) onSelect([]);
130
131
  };
131
132
 
132
133
  useImperativeHandle(ref, () => ({
@@ -142,19 +143,27 @@ const MultiSelect = forwardRef<any, MultiSelectProps>((props, ref) => {
142
143
  return (
143
144
  <div className="relative w-[200px]" ref={selectRef}>
144
145
  <div className="relative border w-[200px] flex items-center px-4 py-2 rounded-lg">
145
- <button onClick={togglePopover} className="flex-grow text-sm text-left">
146
+ <button
147
+ onClick={togglePopover}
148
+ className="flex-grow text-sm text-left"
149
+ type="button"
150
+ >
146
151
  {getSelectedDisplay()}
147
152
  </button>
148
153
  <div className="flex items-center space-x-2">
149
154
  {selected.length > 0 && (
150
- <button className="flex items-center px-2" onClick={clearSelected}>
155
+ <button
156
+ className="flex items-center px-2"
157
+ onClick={clearSelected}
158
+ type="button"
159
+ >
151
160
  <Icon
152
161
  elements={x}
153
162
  svgClass="h-4 w-4 stroke-gray-500 fill-none dark:stroke-white"
154
163
  />
155
164
  </button>
156
165
  )}
157
- <button onClick={togglePopover}>
166
+ <button onClick={togglePopover} type="button">
158
167
  <Icon
159
168
  elements={upDown}
160
169
  svgClass="h-4 w-4 stroke-gray-500 fill-none dark:stroke-white"
@@ -187,6 +196,7 @@ const MultiSelect = forwardRef<any, MultiSelectProps>((props, ref) => {
187
196
  {filteredItems.length > 0 ? (
188
197
  filteredItems.map(({ value, label }) => (
189
198
  <button
199
+ type="button"
190
200
  key={value}
191
201
  className="flex items-center w-full px-2 py-1 text-left hover:bg-blue-100 gap-2 rounded-lg mt-1 text-sm dark:hover:bg-blue-600"
192
202
  onClick={() => handleSelect(value)}
@@ -1,4 +1,7 @@
1
1
  /**
2
+ * Important!!!!
3
+ * Work In Progress Code
4
+ *
2
5
  * Copyright (c) Grampro Business Services and affiliates.
3
6
  *
4
7
  * This source code is licensed under the MIT license found in the
@@ -6,30 +9,68 @@
6
9
  */
7
10
 
8
11
  import React, { useState, useRef, useEffect } from "react";
9
- import { upload, x } from "@/src/component-lib/icon/iconPaths";
10
- import Icon from "@/src/component-lib/icon/Icon";
12
+ import { upload, x, circleCheck } from "../icon/iconPaths";
13
+ import Icon from "../icon/Icon";
11
14
  import { GetFileIcon } from "./uploaderIcon";
12
15
  import AestheticProcessingAnimationWithStyles from "./ProgressAnimation";
16
+ import { sendFiles } from "./uploaderService";
17
+ import { cloudDownload } from "../../icon/iconPaths";
13
18
 
14
19
  export const FileUploader = ({
15
20
  showImagePreview = false,
16
- multiple = true,
21
+ multiple = false,
17
22
  onChange,
18
- startsUpload = false,
19
23
  selectedFiles = [],
20
24
  accept,
21
25
  fileCount,
22
26
  disabled = false,
23
27
  inputFileSize, // Max file size in MB
28
+ startUpload = false,
29
+ apiURL = "",
30
+ chunk_size = "",
31
+ uploadedFileIdArray = () => {},
32
+ fileData = [],
24
33
  }: any) => {
25
34
  const [files, setFiles] = useState<any[]>([]);
26
35
  const [previewUrls, setPreviewUrls] = useState<{ [key: string]: string }>({});
27
36
  const [isDragging, setIsDragging] = useState(false);
28
37
  const [fileCountError, setFileCountError] = useState(false);
29
38
  const [fileSizeErrors, setFileSizeErrors] = useState<string[]>([]); // Array for file size errors
39
+ const [isUploading, setIsUploading] = useState(false);
40
+ const [fileUploadStatus, setFileUploadStatus] = useState<string | undefined>(
41
+ undefined
42
+ );
43
+ const [progress, setProgress] = useState(0);
30
44
  const fileInputRef = useRef<any>(null);
31
45
  const dropZoneRef = useRef<any>(null);
32
46
 
47
+ // Starts uploads to server
48
+ useEffect(() => {
49
+ if (startUpload) {
50
+ // Upload Handler
51
+ const handleUpload = async () => {
52
+ if (files.length > 0) {
53
+ setFileUploadStatus(undefined);
54
+ await sendFiles(
55
+ files,
56
+ chunk_size,
57
+ apiURL,
58
+ ({ uploading, progress, uploadedFileIds }) => {
59
+ setIsUploading(uploading);
60
+ if (progress !== undefined) setProgress(progress);
61
+ // Returns Uploaded File ID's
62
+ if (uploadedFileIds) uploadedFileIdArray(uploadedFileIds);
63
+ }
64
+ );
65
+ setFileUploadStatus("Upload completed");
66
+ } else {
67
+ setFileUploadStatus("No files selected");
68
+ }
69
+ };
70
+ handleUpload();
71
+ }
72
+ }, [startUpload]);
73
+
33
74
  // For Binding values on edit
34
75
  useEffect(() => {
35
76
  if (selectedFiles && selectedFiles.length > 0) {
@@ -128,6 +169,22 @@ export const FileUploader = ({
128
169
  handleFileChange(multiple ? droppedFiles : [droppedFiles[0]]);
129
170
  };
130
171
 
172
+ const handleDownload = (fileId: any, fileName: any) => {
173
+ const downloadUrl = constructDownloadUrl(fileId);
174
+ window.open(downloadUrl, "_blank");
175
+ };
176
+
177
+ const constructDownloadUrl = (fileId: any) => {
178
+ const url = new URL("download", apiURL);
179
+ url.searchParams.append("id", fileId);
180
+ return url.href;
181
+ };
182
+
183
+ const isImageFile = (fileName: any) => {
184
+ const imageExtensions = [".jpg", ".jpeg", ".png", ".gif", ".bmp", ".webp"];
185
+ return imageExtensions.some((ext) => fileName.toLowerCase().endsWith(ext));
186
+ };
187
+
131
188
  return (
132
189
  <div className="w-full max-w-md mx-auto">
133
190
  <div
@@ -157,6 +214,18 @@ export const FileUploader = ({
157
214
  : "Single file upload only"}
158
215
  </p>
159
216
 
217
+ {/* Shows FileUpload Status Here */}
218
+ {fileUploadStatus && (
219
+ <p className="text-xs mt-2 text-green-500 flex gap-1">
220
+ <Icon
221
+ dimensions={{ width: "16", height: "16" }}
222
+ elements={circleCheck}
223
+ svgClass={"stroke-green-500 fill-none dark:stroke-white"}
224
+ />
225
+ {fileUploadStatus}
226
+ </p>
227
+ )}
228
+
160
229
  {/* Display file size errors */}
161
230
  {fileSizeErrors.length > 0 && (
162
231
  <div className="mt-2 space-y-1">
@@ -186,7 +255,10 @@ export const FileUploader = ({
186
255
  </span>
187
256
  )}
188
257
 
189
- {startsUpload && <AestheticProcessingAnimationWithStyles />}
258
+ {/* Loading Animation */}
259
+ {isUploading && (
260
+ <AestheticProcessingAnimationWithStyles progressPercentage={progress} />
261
+ )}
190
262
 
191
263
  {files.length > 0 && (
192
264
  <div className="mt-4 space-y-2">
@@ -225,6 +297,44 @@ export const FileUploader = ({
225
297
  ))}
226
298
  </div>
227
299
  )}
300
+
301
+ {fileData.length > 0 &&
302
+ fileData.map((item: any, index: any) => (
303
+ <div
304
+ key={index}
305
+ className="flex items-center justify-between bg-gray-50 p-2 rounded-md mt-2"
306
+ >
307
+ <div className="flex items-center space-x-2">
308
+ {/* <GetFileIcon
309
+ file={item.FileName}
310
+ showImagePreview={showImagePreview}
311
+ previewUrls={previewUrls}
312
+ /> */}
313
+ {isImageFile(item.FileName) && (
314
+ <img
315
+ src={constructDownloadUrl(item.FileID)}
316
+ alt={item.FileName}
317
+ className="mt-2 w-8 h-8 object-cover rounded-lg"
318
+ />
319
+ )}
320
+ <div>
321
+ <p className="text-sm font-medium text-gray-700">
322
+ {item.FileName}
323
+ </p>
324
+ <p className="text-xs text-gray-500">
325
+ {(item.FileSize / 1024 / 1024).toFixed(2)} MB
326
+ </p>
327
+ </div>
328
+ </div>
329
+ <button onClick={() => handleDownload(item.FileID, item.FileName)}>
330
+ <Icon
331
+ dimensions={{ width: "18", height: "18" }}
332
+ elements={cloudDownload}
333
+ svgClass={"stroke-red-500 fill-none dark:stroke-white"}
334
+ />
335
+ </button>
336
+ </div>
337
+ ))}
228
338
  </div>
229
339
  );
230
340
  };
@@ -20,7 +20,13 @@ function processFiles(files: File[], chunkSize: number) {
20
20
  export async function sendFiles(
21
21
  files: File[],
22
22
  chunkSize: number,
23
- apiUrl = "http://localhost:8080/upload"
23
+ apiUrl = "http://localhost:8080/upload",
24
+ isUploading = (status: {
25
+ uploading: boolean;
26
+ progress?: number;
27
+ file?: string;
28
+ uploadedFileIds?: string[];
29
+ }) => {}
24
30
  ) {
25
31
  const processedFiles = processFiles(files, chunkSize);
26
32
 
@@ -33,6 +39,10 @@ export async function sendFiles(
33
39
  // To track chunk index per file
34
40
  const chunkIndexTracker: { [key: string]: number } = {};
35
41
 
42
+ let totalChunks = processedFiles.length;
43
+ let uploadedChunks = 0;
44
+ const uploadedFileIds: string[] = [];
45
+
36
46
  for (let i = 0; i < processedFiles.length; i++) {
37
47
  const { file, originalFile } = processedFiles[i];
38
48
  const formData = new FormData();
@@ -59,13 +69,12 @@ export async function sendFiles(
59
69
  chunkIndexTracker[originalFile.name]++;
60
70
  }
61
71
 
62
- // Don't Remove the following console
63
- // console.log("FormData contents:");
64
- // formData.forEach((value, key) => {
65
- // console.log(`${key}: ${value}`);
66
- // });
67
-
68
72
  try {
73
+ isUploading({
74
+ uploading: true,
75
+ progress: (uploadedChunks / totalChunks) * 100,
76
+ file: originalFile.name,
77
+ });
69
78
  const response = await fetch(apiUrl, {
70
79
  method: "POST",
71
80
  body: formData,
@@ -76,18 +85,40 @@ export async function sendFiles(
76
85
  }
77
86
 
78
87
  const data = await response.json();
88
+ if (data.documentId) uploadedFileIds.push(data.documentId);
79
89
 
80
- if (data.documentId) {
81
- console.log(
82
- `File uploaded successfully. Document ID: ${data.documentId}`
83
- );
84
- } else {
85
- console.log(
86
- `Chunk ${chunkIndexTracker[originalFile.name]}/${chunkCount} uploaded`
87
- );
88
- }
90
+ uploadedChunks++;
91
+ isUploading({
92
+ uploading: true,
93
+ progress: (uploadedChunks / totalChunks) * 100,
94
+ file: originalFile.name,
95
+ });
89
96
  } catch (error) {
90
97
  console.error("Error uploading file chunk:", error);
98
+ isUploading({
99
+ uploading: false,
100
+ progress: (uploadedChunks / totalChunks) * 100,
101
+ file: originalFile.name,
102
+ });
91
103
  }
92
104
  }
105
+
106
+ // Indicate that the upload is complete
107
+ isUploading({
108
+ uploading: false,
109
+ progress: 100,
110
+ uploadedFileIds: uploadedFileIds,
111
+ });
112
+ }
113
+
114
+ // returns all files
115
+ export async function getAllFiles(apiUrl: string) {
116
+ let files: any[] = [];
117
+ return files;
118
+ }
119
+
120
+ // returns files by ID
121
+ export async function getFilesById(apiUrl: string, documentId: string) {
122
+ let files: any[] = [];
123
+ return files;
93
124
  }
@@ -166,3 +166,14 @@ export const filespreadsheet: any = [
166
166
  { type: "path", d: "M8 17h2", key: "2yhykz" },
167
167
  { type: "path", d: "M14 17h2", key: "10kma7" },
168
168
  ];
169
+
170
+ export const cloudDownload: any = [
171
+ { type: "path", d: "m12 21 4-4", key: "1lfcce" },
172
+ { type: "path", d: "M12 13v8l-4-4", key: "1f5nwf" },
173
+ {
174
+ type: "path",
175
+
176
+ d: "M4.393 15.269A7 7 0 1 1 15.71 8h1.79a4.5 4.5 0 0 1 2.436 8.284",
177
+ key: "ui1hmy",
178
+ },
179
+ ];