diff --git a/auth.js b/auth.js
index 7e5bda4..0e37cf9 100644
--- a/auth.js
+++ b/auth.js
@@ -21,9 +21,8 @@ export function initAuth() {
.then(data => {
console.log("Login response:", data);
if (data.success) {
- console.log("✅ Login successful.");
- updateUIOnLogin(data.isAdmin);
- checkAuthentication(); // Double-check session persistence.
+ console.log("✅ Login successful. Reloading page.");
+ window.location.reload();
} else {
alert("Login failed: " + (data.error || "Unknown error"));
}
diff --git a/fileManager.js b/fileManager.js
index c5dfe75..68b653e 100644
--- a/fileManager.js
+++ b/fileManager.js
@@ -85,90 +85,161 @@ export function loadFileList(folderParam) {
});
}
-// Render the file table with pagination controls.
export function renderFileTable(folder) {
const fileListContainer = document.getElementById("fileList");
const folderPath = (folder === "root")
? "uploads/"
: "uploads/" + encodeURIComponent(folder) + "/";
- // Pagination variables:
+ // Get current search term from the search input, if it exists.
+ let searchInputElement = document.getElementById("searchInput");
+ const searchHadFocus = searchInputElement && (document.activeElement === searchInputElement);
+ let searchTerm = searchInputElement ? searchInputElement.value : "";
+
+ // Filter fileData using the search term (case-insensitive).
+ const filteredFiles = fileData.filter(file =>
+ file.name.toLowerCase().includes(searchTerm.toLowerCase())
+ );
+
+ // Pagination variables.
const itemsPerPage = window.itemsPerPage || 10;
const currentPage = window.currentPage || 1;
- const totalFiles = fileData.length;
+ const totalFiles = filteredFiles.length;
const totalPages = Math.ceil(totalFiles / itemsPerPage);
- // Build table header.
- let tableHTML = `
-
-
- |
-
- File Name ${sortOrder.column === "name" ? (sortOrder.ascending ? "▲" : "▼") : ""}
- |
-
- Date Modified ${sortOrder.column === "modified" ? (sortOrder.ascending ? "▲" : "▼") : ""}
- |
-
- Upload Date ${sortOrder.column === "uploaded" ? (sortOrder.ascending ? "▲" : "▼") : ""}
- |
-
- File Size ${sortOrder.column === "size" ? (sortOrder.ascending ? "▲" : "▼") : ""}
- |
-
- Uploader ${sortOrder.column === "uploader" ? (sortOrder.ascending ? "▲" : "▼") : ""}
- |
- Actions |
-
- `;
+ // 1. Top controls: Responsive row with search box on the left and Prev/Next on the right.
+ const topControlsHTML = `
+
+
+
+
+
+
+ Page ${currentPage} of ${totalPages || 1}
+
+
+
+ `;
+
+ // 2. Build the File Table with Bootstrap styling.
+ let tableHTML = `
+ `;
- // Build pagination controls.
- let paginationHTML = ``;
+ // 3. Bottom controls: "Show [dropdown] items per page" with consistent 16px font.
+ const bottomControlsHTML = `
+
+
+
+ items per page
+
+ `;
- fileListContainer.innerHTML = tableHTML + tableBody + paginationHTML;
+ // Combine top controls, table, and bottom controls.
+ fileListContainer.innerHTML = topControlsHTML + tableHTML + tableBody + bottomControlsHTML;
+
+ // Re-focus the search input if it was previously focused.
+ const newSearchInput = document.getElementById("searchInput");
+ if (searchHadFocus && newSearchInput) {
+ newSearchInput.focus();
+ newSearchInput.setSelectionRange(newSearchInput.value.length, newSearchInput.value.length);
+ }
+
+ // Attach event listener for search input.
+ newSearchInput.addEventListener("input", function () {
+ window.currentPage = 1;
+ renderFileTable(folder);
+ });
// Attach sorting event listeners on header cells.
const headerCells = document.querySelectorAll("table.table thead th[data-column]");
@@ -179,52 +250,45 @@ export function renderFileTable(folder) {
});
});
- // After rendering the table, reattach the file checkbox event listener.
+ // Reattach event listeners for file checkboxes.
document.querySelectorAll('#fileList .file-checkbox').forEach(checkbox => {
- checkbox.addEventListener('change', updateFileActionButtons);
+ checkbox.addEventListener('change', function (e) {
+ updateRowHighlight(e.target);
+ updateFileActionButtons();
+ });
});
- // Finally, call updateFileActionButtons so the buttons show (or are disabled) correctly.
updateFileActionButtons();
}
-// Sort files and re-render the table.
-export function sortFiles(column, folder) {
- if (sortOrder.column === column) {
- sortOrder.ascending = !sortOrder.ascending;
- } else {
- sortOrder.column = column;
- sortOrder.ascending = true;
+/**
+ * Toggles row selection when the user clicks any part of the row (except buttons/links).
+ */
+window.toggleRowSelection = function (event, fileName) {
+ const targetTag = event.target.tagName.toLowerCase();
+ if (targetTag === 'a' || targetTag === 'button' || targetTag === 'input') {
+ return;
}
+ const row = event.currentTarget;
+ const checkbox = row.querySelector('.file-checkbox');
+ if (!checkbox) return;
+ checkbox.checked = !checkbox.checked;
+ updateRowHighlight(checkbox);
+ updateFileActionButtons();
+};
- fileData.sort((a, b) => {
- let valA = a[column] || "";
- let valB = b[column] || "";
-
- if (column === "modified" || column === "uploaded") {
- // Log the raw date strings.
- console.log(`Sorting ${column}: raw values ->`, valA, valB);
-
- const parsedA = parseCustomDate(valA);
- const parsedB = parseCustomDate(valB);
-
- // Log the parsed numeric timestamps.
- console.log(`Sorting ${column}: parsed values ->`, parsedA, parsedB);
-
- valA = parsedA;
- valB = parsedB;
- } else if (typeof valA === "string") {
- valA = valA.toLowerCase();
- valB = valB.toLowerCase();
- }
-
- if (valA < valB) return sortOrder.ascending ? -1 : 1;
- if (valA > valB) return sortOrder.ascending ? 1 : -1;
- return 0;
- });
-
- renderFileTable(folder);
-}
+/**
+ * Updates row highlight based on whether the checkbox is checked.
+ */
+window.updateRowHighlight = function (checkbox) {
+ const row = checkbox.closest('tr');
+ if (!row) return;
+ if (checkbox.checked) {
+ row.classList.add('row-selected');
+ } else {
+ row.classList.remove('row-selected');
+ }
+};
// Delete selected files.
export function handleDeleteSelected(e) {
diff --git a/index.html b/index.html
index a2339f0..b64d5a4 100644
--- a/index.html
+++ b/index.html
@@ -10,6 +10,7 @@
+
@@ -57,10 +58,13 @@
diff --git a/styles.css b/styles.css
index 07d234b..5b68d76 100644
--- a/styles.css
+++ b/styles.css
@@ -132,6 +132,12 @@ header {
gap: 5px;
}
+#uploadBtn {
+ font-size: 16px;
+ padding: 10px 22px;
+ align-items: center;
+}
+
/* UPLOAD PROGRESS */
#uploadProgressContainer ul {
list-style: none;
@@ -376,4 +382,23 @@ label {
#fileListContainer {
margin-top: 40px !important;
-}
\ No newline at end of file
+}
+
+.row-selected {
+ background-color: #f2f2f2 !important;
+}
+
+.custom-prev-next-btn {
+ background-color: #e0e0e0;
+ color: #000;
+ border: none;
+ padding: 6px 12px;
+ font-size: 14px;
+ border-radius: 4px;
+ margin: 0 4px;
+ cursor: pointer;
+}
+.custom-prev-next-btn:hover:not(:disabled) {
+ background-color: #d5d5d5;
+}
+
diff --git a/upload.js b/upload.js
index 5e615b8..95510f2 100644
--- a/upload.js
+++ b/upload.js
@@ -6,12 +6,100 @@ export function initUpload() {
const fileInput = document.getElementById("file");
const progressContainer = document.getElementById("uploadProgressContainer");
const uploadForm = document.getElementById("uploadFileForm");
+ const dropArea = document.getElementById("uploadDropArea");
- // Build progress list when files are selected.
+ // Helper function: set the drop area's default layout.
+ function setDropAreaDefault() {
+ if (dropArea) {
+ dropArea.innerHTML = `
+
+ Drop files here or click 'Choose files'
+
+
+
+
+ No files selected
+
+
+ `;
+ // Wire up the custom button.
+ /* const customChooseBtn = document.getElementById("customChooseBtn");
+ if (customChooseBtn) {
+ customChooseBtn.addEventListener("click", function () {
+ fileInput.click();
+ });
+ }*/
+ }
+ }
+
+ // Initialize drop area.
+ if (dropArea) {
+ dropArea.style.border = "2px dashed #ccc";
+ dropArea.style.padding = "20px";
+ dropArea.style.textAlign = "center";
+ dropArea.style.marginBottom = "15px";
+ dropArea.style.cursor = "pointer";
+ // Set default content once.
+ setDropAreaDefault();
+
+ // Prevent default behavior for drag events.
+ dropArea.addEventListener("dragover", function (e) {
+ e.preventDefault();
+ dropArea.style.backgroundColor = "#f8f8f8";
+ });
+ dropArea.addEventListener("dragleave", function (e) {
+ e.preventDefault();
+ dropArea.style.backgroundColor = "";
+ });
+ dropArea.addEventListener("drop", function (e) {
+ e.preventDefault();
+ dropArea.style.backgroundColor = "";
+ const dt = e.dataTransfer;
+ if (dt && dt.files && dt.files.length > 0) {
+ fileInput.files = dt.files;
+ fileInput.dispatchEvent(new Event("change"));
+ }
+ });
+
+ // Clicking the drop area triggers file selection.
+ dropArea.addEventListener("click", function () {
+ fileInput.click();
+ });
+ }
+
+ // When files are selected, update only the file info container.
if (fileInput) {
fileInput.addEventListener("change", function () {
- progressContainer.innerHTML = "";
const files = fileInput.files;
+ // Update the file info container without replacing the entire drop area.
+ const fileInfoContainer = document.getElementById("fileInfoContainer");
+ if (fileInfoContainer) {
+ if (files.length > 0) {
+ if (files.length === 1) {
+ fileInfoContainer.innerHTML = `
+
+ ${files[0].name}
+ `;
+ } else {
+ fileInfoContainer.innerHTML = `
+
+ ${files.length} files selected
+ `;
+ }
+ const previewContainer = document.getElementById("filePreviewContainer");
+ if (previewContainer) {
+ previewContainer.innerHTML = "";
+ displayFilePreview(files[0], previewContainer);
+ }
+ } else {
+ fileInfoContainer.innerHTML = `No files selected`;
+ }
+ }
+
+ // Build progress list as before.
+ progressContainer.innerHTML = "";
if (files.length > 0) {
const allFiles = Array.from(files);
const maxDisplay = 10;
@@ -22,7 +110,6 @@ export function initUpload() {
const li = document.createElement("li");
li.style.paddingTop = "20px";
li.style.marginBottom = "10px";
- // Only display progress items for the first maxDisplay files.
li.style.display = (index < maxDisplay) ? "flex" : "none";
li.style.alignItems = "center";
li.style.flexWrap = "wrap";
@@ -51,12 +138,10 @@ export function initUpload() {
li.appendChild(preview);
li.appendChild(nameDiv);
li.appendChild(progDiv);
- // Save references for later updates.
li.progressBar = progBar;
li.startTime = Date.now();
list.appendChild(li);
});
- // If more than maxDisplay files, add a note.
if (allFiles.length > maxDisplay) {
const extra = document.createElement("li");
extra.style.paddingTop = "20px";
@@ -70,7 +155,7 @@ export function initUpload() {
});
}
- // Submit handler: upload all files and then check the server's file list.
+ // Submit handler remains unchanged.
if (uploadForm) {
uploadForm.addEventListener("submit", function (e) {
e.preventDefault();
@@ -80,22 +165,21 @@ export function initUpload() {
return;
}
const allFiles = Array.from(files);
- const maxDisplay = 10; // Only show progress for first 10 items.
+ const maxDisplay = 10;
const folderToUse = window.currentFolder || "root";
const listItems = progressContainer.querySelectorAll("li");
let finishedCount = 0;
let allSucceeded = true;
- // Array to track each file's upload result.
const uploadResults = new Array(allFiles.length).fill(false);
-
+
allFiles.forEach((file, index) => {
const formData = new FormData();
formData.append("file[]", file);
formData.append("folder", folderToUse);
-
+
const xhr = new XMLHttpRequest();
let currentPercent = 0;
-
+
xhr.upload.addEventListener("progress", function (e) {
if (e.lengthComputable) {
currentPercent = Math.round((e.loaded / e.total) * 100);
@@ -113,7 +197,7 @@ export function initUpload() {
}
}
});
-
+
xhr.addEventListener("load", function () {
let jsonResponse;
try {
@@ -136,11 +220,10 @@ export function initUpload() {
finishedCount++;
console.log("Upload response for file", file.name, xhr.responseText);
if (finishedCount === allFiles.length) {
- // Immediately refresh the file list.
refreshFileList();
}
});
-
+
xhr.addEventListener("error", function () {
if (index < maxDisplay && listItems[index]) {
listItems[index].progressBar.innerText = "Error";
@@ -153,7 +236,7 @@ export function initUpload() {
refreshFileList();
}
});
-
+
xhr.addEventListener("abort", function () {
if (index < maxDisplay && listItems[index]) {
listItems[index].progressBar.innerText = "Aborted";
@@ -166,20 +249,16 @@ export function initUpload() {
refreshFileList();
}
});
-
+
xhr.open("POST", "upload.php", true);
xhr.send(formData);
});
-
+
function refreshFileList() {
- // Call loadFileList immediately (with a timestamp added inside loadFileList, if needed).
loadFileList(folderToUse)
.then(serverFiles => {
initFileActions();
- // Normalize server file names to lowercase.
serverFiles = (serverFiles || []).map(item => item.name.trim().toLowerCase());
- // For each file, if it was successful and is present on the server, leave its progress item;
- // if not, mark it as "Error".
allFiles.forEach((file, index) => {
const fileName = file.name.trim().toLowerCase();
if (index < maxDisplay && listItems[index]) {
@@ -189,11 +268,10 @@ export function initUpload() {
}
}
});
- // Now, the file list is refreshed instantly.
- // However, we want the progress list to remain visible for 10 seconds.
setTimeout(() => {
progressContainer.innerHTML = "";
fileInput.value = "";
+ if (dropArea) setDropAreaDefault();
}, 10000);
if (!allSucceeded) {
alert("Some files failed to upload. Please check the list.");
@@ -206,4 +284,4 @@ export function initUpload() {
}
});
}
-}
+}
\ No newline at end of file