@tradly/asset 1.0.3 → 1.0.5

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 (33) hide show
  1. package/dist/components/FileUpload.js +163 -0
  2. package/dist/components/Icons.js +39 -0
  3. package/dist/components/ImagesSkeleton.js +21 -0
  4. package/dist/components/MediaGallery.js +153 -0
  5. package/dist/components/MediaPopup.js +104 -0
  6. package/dist/components/MediaTab.js +141 -0
  7. package/dist/components/Pagination.js +141 -0
  8. package/dist/components/VideosGallery.js +153 -0
  9. package/dist/esm/components/FileUpload.js +155 -0
  10. package/dist/esm/components/Icons.js +32 -0
  11. package/dist/esm/components/ImagesSkeleton.js +14 -0
  12. package/dist/esm/components/MediaGallery.js +144 -0
  13. package/dist/esm/components/MediaPopup.js +97 -0
  14. package/dist/esm/components/MediaTab.js +132 -0
  15. package/dist/esm/components/Pagination.js +134 -0
  16. package/dist/esm/components/VideosGallery.js +144 -0
  17. package/{index.js → dist/esm/index.js} +6 -6
  18. package/dist/esm/services/apiService.js +373 -0
  19. package/dist/index.js +60 -0
  20. package/dist/services/apiService.js +379 -0
  21. package/package.json +17 -6
  22. package/EXAMPLE_USAGE.jsx +0 -128
  23. package/MIGRATION_GUIDE.md +0 -249
  24. package/STYLING_GUIDE.md +0 -219
  25. package/src/components/FileUpload.jsx +0 -114
  26. package/src/components/Icons.jsx +0 -23
  27. package/src/components/ImagesSkeleton.jsx +0 -18
  28. package/src/components/MediaGallery.jsx +0 -125
  29. package/src/components/MediaPopup.jsx +0 -101
  30. package/src/components/MediaTab.jsx +0 -152
  31. package/src/components/Pagination.jsx +0 -175
  32. package/src/components/VideosGallery.jsx +0 -121
  33. package/src/services/apiService.js +0 -270
@@ -1,249 +0,0 @@
1
- # Migration Guide: From Existing Media Components to @tradly/asset Package
2
-
3
- ## Overview
4
-
5
- This guide helps you migrate from the existing media components in
6
- `src/Shared/fields/MediaSections/` to the new reusable `@tradly/asset` package.
7
-
8
- ## Benefits of Using the Package
9
-
10
- 1. **Reusability**: Use the same media gallery across multiple projects
11
- 2. **Maintainability**: Single source of truth for media gallery functionality
12
- 3. **Flexibility**: Easy to configure with different auth keys and API endpoints
13
- 4. **Separation of Concerns**: Media gallery logic is isolated from your main
14
- app
15
-
16
- ## Package Structure
17
-
18
- ```
19
- packages/asset/
20
- ├── src/
21
- │ ├── components/
22
- │ │ ├── MediaPopup.jsx # Main popup component
23
- │ │ ├── MediaTab.jsx # Tab navigation
24
- │ │ ├── MediaGallery.jsx # Images gallery
25
- │ │ ├── VideosGallery.jsx # Videos gallery
26
- │ │ ├── FileUpload.jsx # File upload component
27
- │ │ ├── Pagination.jsx # Pagination component
28
- │ │ ├── ImagesSkeleton.jsx # Loading skeleton
29
- │ │ └── Icons.jsx # Icon components
30
- │ ├── services/
31
- │ │ └── apiService.js # API service layer
32
- │ └── index.js # Main export
33
- ├── package.json
34
- ├── README.md
35
- └── EXAMPLE_USAGE.jsx
36
- ```
37
-
38
- ## Migration Steps
39
-
40
- ### Step 1: Install/Setup the Package
41
-
42
- Since this is a local package, you can either:
43
-
44
- **Option A: Use as local package (for now)**
45
-
46
- ```json
47
- // In your main package.json, add:
48
- {
49
- "dependencies": {
50
- "@tradly/asset": "file:./packages/asset"
51
- }
52
- }
53
- ```
54
-
55
- **Option B: Publish to npm (later)**
56
-
57
- ```bash
58
- cd packages/asset
59
- npm publish
60
- ```
61
-
62
- ### Step 2: Update Your Component
63
-
64
- **Before (Old way):**
65
-
66
- ```jsx
67
- import MediaGallery from "@/Shared/fields/MediaSections/MediaPopup";
68
-
69
- function MyComponent() {
70
- const [isOpen, setIsOpen] = useState(false);
71
-
72
- return (
73
- <MediaGallery
74
- isOpenPopup={isOpen}
75
- setIsOpenPopup={setIsOpen}
76
- current_data={selectedMedia}
77
- update_data={setSelectedMedia}
78
- options={["image"]}
79
- />
80
- );
81
- }
82
- ```
83
-
84
- **After (New way):**
85
-
86
- ```jsx
87
- import { MediaPopup, MediaApiService } from "@tradly/asset";
88
- import { getAuthKey } from "constant/functions";
89
-
90
- function MyComponent() {
91
- const [isOpen, setIsOpen] = useState(false);
92
- const [selectedMedia, setSelectedMedia] = useState(null);
93
-
94
- // Initialize API service with Tradly SDK
95
- const apiService = new MediaApiService({
96
- authKey: getAuthKey(),
97
- baseUrl: "/api",
98
- tradly: tradly, // Pass Tradly SDK instance (required for uploads)
99
- });
100
-
101
- return (
102
- <MediaPopup
103
- isOpen={isOpen}
104
- onClose={() => setIsOpen(false)}
105
- onSelect={setSelectedMedia}
106
- currentData={selectedMedia}
107
- options={["image"]}
108
- apiService={apiService}
109
- />
110
- );
111
- }
112
- ```
113
-
114
- ### Step 3: Configure API Service
115
-
116
- The package handles all upload logic internally using direct API calls. The API
117
- base URL is automatically detected from your environment:
118
-
119
- ```jsx
120
- const apiService = new MediaApiService({
121
- authKey: getAuthKey(), // X-Auth-Key header
122
- bearerToken: "your-bearer-token", // Required: Bearer token
123
- // environment is auto-detected from process.env.ENVIRONMENT
124
- // apiBaseUrl is auto-set based on environment:
125
- // - Dev: https://api.dev.tradly.app
126
- // - Prod: https://api.tradly.app
127
- });
128
- ```
129
-
130
- The package will automatically:
131
-
132
- 1. Detect environment from `process.env.ENVIRONMENT` (or you can pass
133
- `environment: 'dev'` or `'production'`)
134
- 2. Set the correct API base URL (`https://api.dev.tradly.app` for dev,
135
- `https://api.tradly.app` for prod)
136
- 3. Get S3 signed URLs via API call to `POST /v1/utils/S3signedUploadURL`
137
- 4. Upload files to S3 using the signed URLs
138
- 5. Save media metadata via `POST /v1/media` with `{ media: [...] }` format
139
- 6. Fetch media list via `GET /v1/media?page=1&parent=0&mime_type=...`
140
-
141
- ## Key Differences
142
-
143
- | Old Component | New Package |
144
- | --------------------------------------- | -------------------------------- |
145
- | `isOpenPopup` prop | `isOpen` prop |
146
- | `setIsOpenPopup` prop | `onClose` callback |
147
- | `update_data` prop | `onSelect` callback |
148
- | `current_data` prop | `currentData` prop |
149
- | Uses `proxy_api_call` directly | Uses `MediaApiService` |
150
- | Auth key from `getAuthKey()` internally | Auth key passed via `apiService` |
151
- | Hardcoded API paths | Configurable via `apiService` |
152
-
153
- ## Complete Example
154
-
155
- Here's a complete example showing how to replace the old component:
156
-
157
- ```jsx
158
- import React, { useState } from "react";
159
- import { MediaPopup, MediaApiService } from "@tradly/asset";
160
- import { getAuthKey } from "constant/functions";
161
-
162
- function MyMediaSelector() {
163
- const [isOpen, setIsOpen] = useState(false);
164
- const [selectedImage, setSelectedImage] = useState(null);
165
-
166
- // Create API service instance
167
- // API base URL is automatically detected from ENVIRONMENT
168
- // All API calls go directly to Tradly API (no proxy needed)
169
- const apiService = React.useMemo(() => {
170
- return new MediaApiService({
171
- authKey: getAuthKey(),
172
- bearerToken: "your-bearer-token", // Required: Bearer token
173
- // apiBaseUrl is auto-detected from process.env.ENVIRONMENT
174
- onError: (error) => {
175
- console.error("Media API Error:", error);
176
- // Show error toast, etc.
177
- },
178
- });
179
- }, []);
180
-
181
- return (
182
- <div>
183
- <button onClick={() => setIsOpen(true)}>
184
- Select Media
185
- </button>
186
-
187
- {selectedImage && (
188
- <img
189
- src={selectedImage}
190
- alt="Selected"
191
- />
192
- )}
193
-
194
- <MediaPopup
195
- isOpen={isOpen}
196
- onClose={() => setIsOpen(false)}
197
- onSelect={(url) => {
198
- setSelectedImage(url);
199
- setIsOpen(false);
200
- }}
201
- currentData={selectedImage}
202
- options={["image", "video"]}
203
- apiService={apiService}
204
- onError={(error) => {
205
- console.error("Gallery error:", error);
206
- }}
207
- />
208
- </div>
209
- );
210
- }
211
-
212
- export default MyMediaSelector;
213
- ```
214
-
215
- ## Testing
216
-
217
- After migration, test the following:
218
-
219
- 1. ✅ Opening/closing the media gallery
220
- 2. ✅ Uploading new images/videos
221
- 3. ✅ Selecting existing media
222
- 4. ✅ Pagination works correctly
223
- 5. ✅ Error handling works
224
- 6. ✅ Authentication is working
225
-
226
- ## Rollback Plan
227
-
228
- If you need to rollback:
229
-
230
- 1. Keep the old components in `src/Shared/fields/MediaSections/`
231
- 2. Import from the old location instead of the package
232
- 3. The old components will continue to work as before
233
-
234
- ## Next Steps
235
-
236
- 1. Test the package in a development environment
237
- 2. Gradually migrate components one by one
238
- 3. Once stable, consider removing old components
239
- 4. Optionally publish to npm for use in other projects
240
-
241
- ## Support
242
-
243
- If you encounter issues during migration:
244
-
245
- 1. Check the `EXAMPLE_USAGE.jsx` file
246
- 2. Review the `README.md` for API documentation
247
- 3. Ensure all peer dependencies are installed
248
- 4. Verify your auth key and API base URL are correct
249
-
package/STYLING_GUIDE.md DELETED
@@ -1,219 +0,0 @@
1
- # Styling Guide - Customizing Media Gallery with Tailwind CSS
2
-
3
- The `@tradly/asset` package supports full customization of all components using
4
- Tailwind CSS class names. You can customize styles at every level of the
5
- component hierarchy.
6
-
7
- ## Overview
8
-
9
- All components accept `className` props that allow you to override default
10
- styles. The package uses sensible Tailwind defaults, but you can customize
11
- everything to match your design system.
12
-
13
- ## MediaPopup Styling Props
14
-
15
- The main popup component supports these styling props:
16
-
17
- ```jsx
18
- <MediaPopup
19
- // ... other props
20
- overlayClassName="..." // Overlay/backdrop styles
21
- containerClassName="..." // Main popup container
22
- headerClassName="..." // Header container
23
- titleClassName="..." // Title text
24
- closeButtonClassName="..." // Close button
25
- />
26
- ```
27
-
28
- ### Example
29
-
30
- ```jsx
31
- <MediaPopup
32
- isOpen={isOpen}
33
- onClose={() => setIsOpen(false)}
34
- onSelect={handleSelect}
35
- apiService={apiService}
36
- overlayClassName="fixed inset-0 bg-black bg-opacity-50 backdrop-blur-sm"
37
- containerClassName="max-w-4xl bg-gray-900 text-white rounded-xl shadow-2xl"
38
- titleClassName="text-3xl font-extrabold text-blue-400"
39
- closeButtonClassName="text-white hover:text-red-400 hover:bg-red-900/20"
40
- />
41
- ```
42
-
43
- ## MediaTab Styling Props
44
-
45
- Customize the tab navigation:
46
-
47
- ```jsx
48
- <MediaTab
49
- // ... other props
50
- className="..." // Tab group container
51
- tabListClassName="..." // Tab list container
52
- tabButtonClassName="..." // Base tab button styles
53
- tabButtonActiveClassName="..." // Active tab button styles
54
- tabButtonInactiveClassName="..." // Inactive tab button styles
55
- tabPanelClassName="..." // Tab panel container
56
- />
57
- ```
58
-
59
- ### Example
60
-
61
- ```jsx
62
- <MediaTab
63
- options={["image", "video"]}
64
- apiService={apiService}
65
- tabListClassName="bg-gray-800 border-b-2 border-blue-500"
66
- tabButtonClassName="text-gray-300 hover:text-white px-6 py-3 transition-all"
67
- tabButtonActiveClassName="text-blue-400 border-b-4 border-blue-400 font-bold"
68
- tabButtonInactiveClassName="text-gray-400"
69
- />
70
- ```
71
-
72
- ## MediaGallery / VideosGallery Styling Props
73
-
74
- Customize the media grid and items:
75
-
76
- ```jsx
77
- <ImagesGallery
78
- // ... other props
79
- className="..." // Container
80
- gridClassName="..." // Grid layout
81
- imageItemClassName="..." // Individual image item
82
- paginationContainerClassName="..." // Pagination container
83
- />
84
- ```
85
-
86
- ### Example
87
-
88
- ```jsx
89
- <ImagesGallery
90
- apiService={apiService}
91
- gridClassName="grid grid-cols-2 md:grid-cols-4 lg:grid-cols-6 gap-4"
92
- imageItemClassName="rounded-lg border-2 border-gray-300 hover:border-blue-500 transition-all cursor-pointer transform hover:scale-105"
93
- paginationContainerClassName="bg-gray-100 p-6 rounded-lg mt-4"
94
- />
95
- ```
96
-
97
- ## FileUpload Styling Props
98
-
99
- Customize the upload button:
100
-
101
- ```jsx
102
- <FileUpload
103
- // ... other props
104
- className="..." // Container
105
- buttonClassName="..." // Upload button
106
- iconContainerClassName="..." // Icon container
107
- titleClassName="..." // Title text
108
- loadingClassName="..." // Loading state
109
- />
110
- ```
111
-
112
- ### Example
113
-
114
- ```jsx
115
- <FileUpload
116
- accept="image/*"
117
- title="Upload Image"
118
- apiService={apiService}
119
- buttonClassName="border-2 border-dashed border-blue-500 bg-blue-50 hover:bg-blue-100 rounded-xl p-8"
120
- iconContainerClassName="bg-blue-500 p-4 rounded-full"
121
- titleClassName="text-blue-600 font-semibold mt-4"
122
- loadingClassName="bg-blue-100 border-2 border-blue-300 rounded-xl"
123
- />
124
- ```
125
-
126
- ## Pagination Styling Props
127
-
128
- Customize pagination controls:
129
-
130
- ```jsx
131
- <Pagination
132
- // ... other props
133
- className="..." // Container
134
- navClassName="..." // Nav element
135
- previousButtonClassName="..." // Previous button
136
- nextButtonClassName="..." // Next button
137
- pageButtonClassName="..." // Page number buttons
138
- pageButtonActiveClassName="..." // Active page button
139
- ellipsisClassName="..." // Ellipsis (...)
140
- />
141
- ```
142
-
143
- ### Example
144
-
145
- ```jsx
146
- <Pagination
147
- nextPage={handlePageChange}
148
- current_page={currentPage}
149
- pageCount={totalPages}
150
- navClassName="flex items-center space-x-2"
151
- previousButtonClassName="px-4 py-2 bg-gray-200 hover:bg-gray-300 rounded-lg"
152
- nextButtonClassName="px-4 py-2 bg-gray-200 hover:bg-gray-300 rounded-lg"
153
- pageButtonClassName="px-4 py-2 bg-white border border-gray-300 hover:bg-gray-100 rounded"
154
- pageButtonActiveClassName="px-4 py-2 bg-blue-500 text-white font-bold rounded"
155
- />
156
- ```
157
-
158
- ## Complete Customization Example
159
-
160
- Here's a fully customized example:
161
-
162
- ```jsx
163
- import { MediaPopup, MediaApiService } from "@tradly/asset";
164
-
165
- function CustomStyledGallery() {
166
- const apiService = new MediaApiService({
167
- authKey: "your-key",
168
- bearerToken: "your-token",
169
- });
170
-
171
- return (
172
- <MediaPopup
173
- isOpen={isOpen}
174
- onClose={() => setIsOpen(false)}
175
- onSelect={handleSelect}
176
- apiService={apiService}
177
- // Popup styles
178
- overlayClassName="fixed inset-0 bg-black/60 backdrop-blur-sm"
179
- containerClassName="max-w-5xl bg-gradient-to-br from-gray-900 to-gray-800 rounded-2xl shadow-2xl border border-gray-700"
180
- headerClassName="flex items-center justify-between p-6 border-b border-gray-700"
181
- titleClassName="text-2xl font-bold text-white"
182
- closeButtonClassName="text-gray-400 hover:text-white hover:bg-gray-700 rounded-full p-2 transition-all"
183
- // Tab styles (passed to MediaTab internally)
184
- tabListClassName="bg-gray-800/50 border-b border-gray-700"
185
- tabButtonClassName="text-gray-400 hover:text-white px-6 py-4 transition-colors"
186
- tabButtonActiveClassName="text-blue-400 border-b-2 border-blue-400 font-semibold"
187
- tabButtonInactiveClassName="text-gray-500"
188
- // Gallery styles (passed to MediaGallery internally)
189
- gridClassName="grid grid-cols-3 md:grid-cols-5 gap-4 p-6"
190
- imageItemClassName="rounded-xl border-2 border-gray-700 hover:border-blue-500 cursor-pointer transition-all hover:scale-105 shadow-lg"
191
- paginationContainerClassName="bg-gray-800/50 p-4 border-t border-gray-700"
192
- />
193
- );
194
- }
195
- ```
196
-
197
- ## Styling Tips
198
-
199
- 1. **Use Tailwind's responsive prefixes**: `md:`, `lg:`, etc.
200
- 2. **Combine with your design system**: Use your existing color palette and
201
- spacing
202
- 3. **Maintain accessibility**: Keep contrast ratios and focus states
203
- 4. **Test hover states**: Ensure interactive elements have clear hover feedback
204
- 5. **Use transitions**: Add `transition-all` or `transition-colors` for smooth
205
- animations
206
-
207
- ## Default Classes Reference
208
-
209
- If you want to see the default classes, check the component source files. All
210
- default classes use Tailwind utility classes and can be completely overridden.
211
-
212
- ## Notes
213
-
214
- - All className props are optional
215
- - If not provided, sensible defaults are used
216
- - You can override individual parts without affecting others
217
- - Classes are merged/applied using the `||` operator, so your custom classes
218
- completely replace defaults
219
-
@@ -1,114 +0,0 @@
1
- import React, { useState } from 'react'
2
- import { CameraIcon } from './Icons'
3
-
4
- const FileUpload = ({
5
- loadMedia,
6
- accept,
7
- title,
8
- apiService,
9
- onUploadStart,
10
- onUploadComplete,
11
- onUploadError,
12
- // Styling props
13
- className,
14
- buttonClassName,
15
- iconContainerClassName,
16
- titleClassName,
17
- loadingClassName,
18
- }) => {
19
- const [files, setFiles] = useState([])
20
- const [isLoading, setISLoading] = useState(false)
21
- const [length, setLength] = useState(0)
22
-
23
- // Upload files
24
- const uploadFiles = async (fileList) => {
25
- if (onUploadStart) {
26
- onUploadStart(fileList)
27
- }
28
-
29
- setISLoading(true)
30
- setLength(fileList.length)
31
-
32
- try {
33
- // Use the API service to upload files
34
- // The apiService.uploadMedia handles:
35
- // 1. Generate S3 signed URLs via tradly.app.generateS3ImageURL
36
- // 2. Upload files to S3
37
- // 3. Save media metadata to API
38
- const uploadedUrls = await apiService.uploadMedia(fileList, apiService.authKey)
39
-
40
- if (onUploadComplete) {
41
- onUploadComplete(uploadedUrls)
42
- }
43
-
44
- setLength(0)
45
- if (loadMedia) {
46
- loadMedia()
47
- }
48
- } catch (error) {
49
- console.error('Upload error:', error)
50
- if (onUploadError) {
51
- onUploadError(error)
52
- }
53
- } finally {
54
- setISLoading(false)
55
- }
56
- }
57
-
58
- // Default classes with customization support
59
- const defaultContainerClass = 'min-w-40 h-40'
60
- const defaultButtonClass =
61
- 'w-full h-full flex flex-col justify-center items-center text-sm border border-primary border-dashed rounded-lg hover:bg-gray-50 transition-colors'
62
- const defaultIconContainerClass = 'p-[10px] bg-primary rounded-full'
63
- const defaultTitleClass = 'mt-2'
64
- const defaultLoadingClass =
65
- 'flex items-center justify-center h-40 bg-gray-200 rounded-lg shadow-md animate-pulse'
66
-
67
- return (
68
- <>
69
- <div className={className || defaultContainerClass}>
70
- <input
71
- required
72
- id={`media_select_${files?.length}`}
73
- type="file"
74
- className="hidden"
75
- accept={accept}
76
- placeholder=""
77
- onChange={async (e) => {
78
- e.stopPropagation()
79
- const all_files = Array.from(e.target.files)
80
- if (all_files?.length > 0) {
81
- await uploadFiles(all_files)
82
- }
83
- // Reset input
84
- e.target.value = ''
85
- }}
86
- multiple={true}
87
- />
88
-
89
- <button
90
- type="button"
91
- className={buttonClassName || defaultButtonClass}
92
- onClick={() => document.getElementById(`media_select_${files?.length}`).click()}
93
- disabled={isLoading}
94
- >
95
- <span className={iconContainerClassName || defaultIconContainerClass}>
96
- <CameraIcon />
97
- </span>
98
- <span className={titleClassName || defaultTitleClass}>{title}</span>
99
- </button>
100
- </div>
101
-
102
- {isLoading &&
103
- Array.from({ length: length }).map((_, index) => {
104
- return (
105
- <div key={index} className={loadingClassName || defaultLoadingClass}>
106
- File Uploading... {index + 1}/{length}
107
- </div>
108
- )
109
- })}
110
- </>
111
- )
112
- }
113
-
114
- export default FileUpload
@@ -1,23 +0,0 @@
1
- import React from 'react'
2
-
3
- export const CloseIcon = ({ className = 'w-8 h-8' }) => (
4
- <svg
5
- xmlns="http://www.w3.org/2000/svg"
6
- fill="none"
7
- viewBox="0 0 24 24"
8
- strokeWidth="1.5"
9
- stroke="currentColor"
10
- className={className}
11
- >
12
- <path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" />
13
- </svg>
14
- )
15
-
16
- export const CameraIcon = () => (
17
- <svg width="22" height="18" viewBox="0 0 22 20" fill="none" xmlns="http://www.w3.org/2000/svg">
18
- <path
19
- d="M7.75033 0.166672L5.76783 2.33334H2.33366C1.14199 2.33334 0.166992 3.30834 0.166992 4.50001V17.5C0.166992 18.6917 1.14199 19.6667 2.33366 19.6667H19.667C20.8587 19.6667 21.8337 18.6917 21.8337 17.5V4.50001C21.8337 3.30834 20.8587 2.33334 19.667 2.33334H16.2328L14.2503 0.166672H7.75033ZM11.0003 16.4167C8.01033 16.4167 5.58366 13.99 5.58366 11C5.58366 8.01001 8.01033 5.58334 11.0003 5.58334C13.9903 5.58334 16.417 8.01001 16.417 11C16.417 13.99 13.9903 16.4167 11.0003 16.4167Z"
20
- fill="white"
21
- />
22
- </svg>
23
- )
@@ -1,18 +0,0 @@
1
- import React from 'react'
2
-
3
- const ImagesSkeleton = ({ per_page = 30 }) => {
4
- const gridItems = []
5
-
6
- for (let i = 0; i < per_page; i++) {
7
- gridItems.push(
8
- <div
9
- key={i}
10
- className="w-full min-h-[160px] bg-[#3B3269] bg-opacity-[20%] rounded-card animate-pulse"
11
- ></div>
12
- )
13
- }
14
-
15
- return gridItems
16
- }
17
-
18
- export default ImagesSkeleton