diff --git a/ShimmerCapture/index.html b/ShimmerCapture/index.html
index be36ecb..be0b168 100644
--- a/ShimmerCapture/index.html
+++ b/ShimmerCapture/index.html
@@ -226,6 +226,112 @@
}
};
+ // For Upload CSV
+ const csvFileInput = document.createElement('input');
+ csvFileInput.type = 'file';
+ csvFileInput.accept = '.csv,text/csv';
+ csvFileInput.style.display = 'none';
+ document.body.appendChild(csvFileInput);
+
+ const apiBaseUrlInput = document.getElementById('apiBaseUrlInput');
+ const API_BASE_URL_KEY = 'shimmerApiBaseUrl';
+ apiBaseUrlInput.value = localStorage.getItem(API_BASE_URL_KEY) || '';
+
+ function getApiBaseUrl() {
+ return apiBaseUrlInput.value.trim().replace(/\/$/, '');
+ }
+
+ async function uploadCsvFile(file) {
+ if (!file) return;
+
+ const maxUploadBytes = 5 * 1024 * 1024;
+ if (file.size > maxUploadBytes) {
+ document.getElementById('status').textContent = 'File too large to upload! (>5MB)';
+ return;
+ }
+
+ try {
+ document.getElementById('status').textContent = 'Getting upload URL...';
+ const apiBaseUrl = getApiBaseUrl();
+ if (!apiBaseUrl) {
+ throw new Error('Enter the Shimmer API base URL first.');
+ }
+ localStorage.setItem(API_BASE_URL_KEY, apiBaseUrl);
+
+ const customPath = document.getElementById('s3FilePathInput')?.value.trim();
+ let s3FilePath;
+ if (customPath) {
+ s3FilePath = customPath.endsWith('.csv') ? customPath : `${customPath}/${file.name}`;
+ } else {
+ s3FilePath = file.name || `shimmer_${Date.now()}.csv`;
+ }
+
+ const response = await fetch(`${apiBaseUrl}/api/trial/s3/presigned_url_upload`, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify({ S3FilePath: s3FilePath })
+ });
+
+ const data = await response.json().catch(() => null);
+ if (!response.ok) {
+ let errText = data?.Error || data?.error;
+ if (!errText && data?.errors && typeof data.errors === 'object') {
+ errText = Object.entries(data.errors)
+ .map(([k, v]) => `${k}: ${(Array.isArray(v) ? v.join(', ') : v)}`)
+ .join(' | ');
+ }
+ if (!errText && data?.title) errText = data.title;
+ if (!errText) errText = `HTTP ${response.status}`;
+ throw new Error(`Failed to get pre-signed URL: ${errText}`);
+ }
+
+ console.log('Presigned response:', data);
+ const isSuccess = data?.IsSuccess ?? data?.isSuccess;
+ const entity = data?.Entity ?? data?.entity;
+ const uploadUrl = entity?.UploadUrl ?? entity?.uploadUrl;
+
+ if (isSuccess === false) {
+ throw new Error(data?.Error || data?.error || 'API returned failure');
+ }
+ if (!uploadUrl) {
+ throw new Error('No upload URL returned from API');
+ }
+
+ document.getElementById('status').textContent = 'Uploading...';
+
+ const upload = await fetch(uploadUrl, {
+ method: 'PUT',
+ headers: {
+ 'Content-Type': file.type || 'text/csv'
+ },
+ body: file
+ });
+
+ if (!upload.ok) {
+ throw new Error('Upload failed');
+ }
+
+ document.getElementById('status').textContent = 'Upload success';
+ } catch (err) {
+ console.error(err);
+ document.getElementById('status').textContent = 'Upload failed';
+ alert(err.message);
+ }
+ }
+
+ csvFileInput.onchange = async () => {
+ const [file] = csvFileInput.files || [];
+ csvFileInput.value = '';
+ if (!file) return;
+ await uploadCsvFile(file);
+ };
+
+ document.getElementById('uploadBtn').onclick = () => {
+ csvFileInput.click();
+ };
+
// -------- Build signal list --------
// -------- Build signal list (name + kind) --------
@@ -366,15 +472,48 @@
chart.update();
}
-
-
-
shimmer.onStatus = (msg) => document.getElementById('log').textContent = msg;