Refactor software version update logic into a reusable function; enhance UI for file uploads and table responsiveness
This commit is contained in:
@@ -161,25 +161,8 @@ if ($sw_version_latest_update == 1){
|
|||||||
//------------------------------------------
|
//------------------------------------------
|
||||||
//UPDATE SW_STATUS
|
//UPDATE SW_STATUS
|
||||||
//------------------------------------------
|
//------------------------------------------
|
||||||
//UPDATE ASSETS-> SW_LATEST_VERSION WITH NO PRODUCT_SOFTWARE TO 2
|
// Use the reusable function to update software version status for all equipment
|
||||||
$sql = 'UPDATE equipment e LEFT JOIN products_software ps ON e.productrowid = ps.productrowid SET e.sw_version_latest = 2 WHERE ps.rowID IS NULL';
|
updateSoftwareVersionStatus($pdo);
|
||||||
$stmt = $pdo->prepare($sql);
|
|
||||||
$stmt->execute();
|
|
||||||
|
|
||||||
//UPDATE ASSETS-> SW_LATEST_VERSION WITH PRODUCT_SOFTWARE FROM 2 TO 0
|
|
||||||
$sql = 'UPDATE equipment e LEFT JOIN products_software ps ON e.productrowid = ps.productrowid SET e.sw_version_latest = 0 WHERE ps.rowID IS NOT NULL AND sw_version_latest = 2';
|
|
||||||
$stmt = $pdo->prepare($sql);
|
|
||||||
$stmt->execute();
|
|
||||||
|
|
||||||
//UPDATE LATEST TO NO IN CASE HW_VERSION ARE EQUAL AND SW_VERSIONS NOT AND NOT LATEST
|
|
||||||
$sql = 'UPDATE equipment e JOIN products_software ps ON e.productrowid = ps.productrowid SET e.sw_version_latest = 0 WHERE ps.latest = 1 AND lower(e.sw_version) <> lower(ps.version) AND lower(e.hw_version) = lower(ps.hw_version) AND e.sw_version_latest = 1';
|
|
||||||
$stmt = $pdo->prepare($sql);
|
|
||||||
$stmt->execute();
|
|
||||||
|
|
||||||
//UPDATE LATEST TO YES IN CASE HW_VERSION ARE EQUAL AND SW_VERSIONS ARE EQUAL
|
|
||||||
$sql = 'UPDATE equipment e JOIN products_software ps ON e.productrowid = ps.productrowid SET e.sw_version_latest = 1 WHERE ps.latest = 1 AND lower(e.sw_version) = lower(ps.version) AND lower(e.hw_version) = lower(ps.hw_version) AND e.sw_version_latest = 0';
|
|
||||||
$stmt = $pdo->prepare($sql);
|
|
||||||
$stmt->execute();
|
|
||||||
//------------------------------------------
|
//------------------------------------------
|
||||||
//------------------------------------------
|
//------------------------------------------
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5111,4 +5111,84 @@ function checkAndInsertIdentityDealer($pdo, $identityUserkey) {
|
|||||||
error_log('Database error in checkAndInsertIdentityDealer: ' . $e->getMessage());
|
error_log('Database error in checkAndInsertIdentityDealer: ' . $e->getMessage());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------
|
||||||
|
// Update software version status for equipment
|
||||||
|
// Parameters:
|
||||||
|
// $pdo - PDO database connection
|
||||||
|
// $serialnumber - Optional serial number. If null, updates all equipment
|
||||||
|
// Returns: boolean - true on success, false on error
|
||||||
|
//------------------------------------------
|
||||||
|
function updateSoftwareVersionStatus($pdo, $serialnumber = null) {
|
||||||
|
try {
|
||||||
|
// Build WHERE clause for serial number filtering if provided
|
||||||
|
$sn_clause = '';
|
||||||
|
$bind_params = [];
|
||||||
|
|
||||||
|
if ($serialnumber !== null) {
|
||||||
|
$sn_clause = ' AND e.serialnumber = ?';
|
||||||
|
$bind_params = [$serialnumber];
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------
|
||||||
|
// STEP 1: Set sw_version_latest = 2 for equipment with NO software assignments
|
||||||
|
//------------------------------------------
|
||||||
|
$sql = 'UPDATE equipment e
|
||||||
|
LEFT JOIN products_software_assignment psa ON e.productrowid = psa.product_id AND psa.status = 1
|
||||||
|
SET e.sw_version_latest = 2
|
||||||
|
WHERE psa.product_id IS NULL' . $sn_clause;
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare($sql);
|
||||||
|
$stmt->execute($bind_params);
|
||||||
|
|
||||||
|
//------------------------------------------
|
||||||
|
// STEP 2: Set sw_version_latest = 0 for equipment WITH software assignments (from previous 2)
|
||||||
|
//------------------------------------------
|
||||||
|
$sql = 'UPDATE equipment e
|
||||||
|
JOIN products_software_assignment psa ON e.productrowid = psa.product_id AND psa.status = 1
|
||||||
|
SET e.sw_version_latest = 0
|
||||||
|
WHERE e.sw_version_latest = 2' . $sn_clause;
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare($sql);
|
||||||
|
$stmt->execute($bind_params);
|
||||||
|
|
||||||
|
//------------------------------------------
|
||||||
|
// STEP 3: Set sw_version_latest = 0 for equipment NOT matching latest version
|
||||||
|
//------------------------------------------
|
||||||
|
$sql = 'UPDATE equipment e
|
||||||
|
JOIN products_software_assignment psa ON e.productrowid = psa.product_id AND psa.status = 1
|
||||||
|
JOIN products_software_versions psv ON psa.software_version_id = psv.rowID
|
||||||
|
SET e.sw_version_latest = 0
|
||||||
|
WHERE psv.latest = 1
|
||||||
|
AND psv.status = 1
|
||||||
|
AND lower(e.sw_version) <> lower(psv.version)
|
||||||
|
AND (psv.hw_version = e.hw_version OR psv.hw_version IS NULL OR psv.hw_version = "")
|
||||||
|
AND e.sw_version_latest = 1' . $sn_clause;
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare($sql);
|
||||||
|
$stmt->execute($bind_params);
|
||||||
|
|
||||||
|
//------------------------------------------
|
||||||
|
// STEP 4: Set sw_version_latest = 1 for equipment matching latest version
|
||||||
|
//------------------------------------------
|
||||||
|
$sql = 'UPDATE equipment e
|
||||||
|
JOIN products_software_assignment psa ON e.productrowid = psa.product_id AND psa.status = 1
|
||||||
|
JOIN products_software_versions psv ON psa.software_version_id = psv.rowID
|
||||||
|
SET e.sw_version_latest = 1
|
||||||
|
WHERE psv.latest = 1
|
||||||
|
AND psv.status = 1
|
||||||
|
AND lower(e.sw_version) = lower(psv.version)
|
||||||
|
AND (psv.hw_version = e.hw_version OR psv.hw_version IS NULL OR psv.hw_version = "")
|
||||||
|
AND e.sw_version_latest = 0' . $sn_clause;
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare($sql);
|
||||||
|
$stmt->execute($bind_params);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
error_log('Database error in updateSoftwareVersionStatus: ' . $e->getMessage());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -44,7 +44,7 @@ $filter_version_id = $_GET['from_version_id'] ?? $_GET['to_version_id'] ?? $_GET
|
|||||||
if (isset($_GET['id']) && $_GET['id'] != '') {
|
if (isset($_GET['id']) && $_GET['id'] != '') {
|
||||||
$api_url = '/v2/products_software_upgrade_paths/rowID=' . $_GET['id'];
|
$api_url = '/v2/products_software_upgrade_paths/rowID=' . $_GET['id'];
|
||||||
$response = ioServer($api_url, '');
|
$response = ioServer($api_url, '');
|
||||||
var_dump($response);
|
|
||||||
if (!empty($response)) {
|
if (!empty($response)) {
|
||||||
$existing = json_decode($response);
|
$existing = json_decode($response);
|
||||||
if (!empty($existing)) {
|
if (!empty($existing)) {
|
||||||
@@ -145,7 +145,7 @@ if ($_SERVER['REQUEST_METHOD'] == 'POST') {
|
|||||||
$api_url = '/v2/products_software_upgrade_paths/';
|
$api_url = '/v2/products_software_upgrade_paths/';
|
||||||
$result = ioServer($api_url, json_encode($data));
|
$result = ioServer($api_url, json_encode($data));
|
||||||
|
|
||||||
if ($result) {
|
if ($result !== 'NOK') {
|
||||||
$success = isset($_POST['delete']) ? 3 : (isset($_POST['rowID']) && $_POST['rowID'] != '' ? 2 : 1);
|
$success = isset($_POST['delete']) ? 3 : (isset($_POST['rowID']) && $_POST['rowID'] != '' ? 2 : 1);
|
||||||
header('Location: ' . $url . '&success_msg=' . $success);
|
header('Location: ' . $url . '&success_msg=' . $success);
|
||||||
exit;
|
exit;
|
||||||
|
|||||||
@@ -143,7 +143,6 @@ $view = '
|
|||||||
<th>Currency</th>
|
<th>Currency</th>
|
||||||
<th>Description</th>
|
<th>Description</th>
|
||||||
<th>Active</th>
|
<th>Active</th>
|
||||||
<th>Actions</th>
|
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@@ -151,18 +150,17 @@ $view = '
|
|||||||
|
|
||||||
$all_paths = array_merge($upgrade_paths_from ?: [], $upgrade_paths_to ?: []);
|
$all_paths = array_merge($upgrade_paths_from ?: [], $upgrade_paths_to ?: []);
|
||||||
if (empty($all_paths)){
|
if (empty($all_paths)){
|
||||||
$view .= '<tr><td colspan="7">No upgrade paths found.</td></tr>';
|
$view .= '<tr><td colspan="6">No upgrade paths found.</td></tr>';
|
||||||
} else {
|
} else {
|
||||||
foreach ($all_paths as $path){
|
foreach ($all_paths as $path){
|
||||||
$view .= '
|
$view .= '
|
||||||
<tr>
|
<tr onclick="window.location.href=\'index.php?page=products_software_upgrade_paths_manage&id='.$path->rowID.'\'" style="cursor: pointer;">
|
||||||
<td>' . ($version_map[$path->from_version_id] ?? $path->from_version_id) . '</td>
|
<td>' . ($version_map[$path->from_version_id] ?? $path->from_version_id) . '</td>
|
||||||
<td>' . ($version_map[$path->to_version_id] ?? $path->to_version_id) . '</td>
|
<td>' . ($version_map[$path->to_version_id] ?? $path->to_version_id) . '</td>
|
||||||
<td>'.$path->price.'</td>
|
<td>'.$path->price.'</td>
|
||||||
<td>'.$path->currency.'</td>
|
<td>'.$path->currency.'</td>
|
||||||
<td>'.$path->description.'</td>
|
<td>'.$path->description.'</td>
|
||||||
<td>'.($path->is_active ? 'Yes' : 'No').'</td>
|
<td>'.($path->is_active ? 'Yes' : 'No').'</td>
|
||||||
<td><a href="index.php?page=products_software_upgrade_paths_manage&id='.$path->rowID.'" class="btn_link">Edit</a></td>
|
|
||||||
</tr>
|
</tr>
|
||||||
';
|
';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ if ($_SERVER['REQUEST_METHOD'] == 'POST') {
|
|||||||
$api_url = '/v2/products_software_versions/';
|
$api_url = '/v2/products_software_versions/';
|
||||||
$result = ioServer($api_url, json_encode($data));
|
$result = ioServer($api_url, json_encode($data));
|
||||||
|
|
||||||
if ($result) {
|
if ($result !== 'NOK') {
|
||||||
$success = isset($_POST['delete']) ? 3 : (isset($_POST['rowID']) && $_POST['rowID'] != '' ? 2 : 1);
|
$success = isset($_POST['delete']) ? 3 : (isset($_POST['rowID']) && $_POST['rowID'] != '' ? 2 : 1);
|
||||||
header('Location: ' . $url . '&success_msg=' . $success);
|
header('Location: ' . $url . '&success_msg=' . $success);
|
||||||
exit;
|
exit;
|
||||||
@@ -147,7 +147,14 @@ $view .= '<div class="content-block">
|
|||||||
<label for="hw_version">HW Version</label>
|
<label for="hw_version">HW Version</label>
|
||||||
<input id="hw_version" type="text" name="hw_version" placeholder="HW Version" value="' . htmlspecialchars($version['hw_version']) . '">
|
<input id="hw_version" type="text" name="hw_version" placeholder="HW Version" value="' . htmlspecialchars($version['hw_version']) . '">
|
||||||
<label for="fileToUpload">Upload File</label>
|
<label for="fileToUpload">Upload File</label>
|
||||||
<input type="file" name="fileToUpload" id="fileToUpload" onchange="updateFields()">
|
<div class="file-upload-wrapper">
|
||||||
|
<button type="button" class="file-upload-btn" onclick="document.getElementById(\'fileToUpload\').click()">
|
||||||
|
<i class="fas fa-cloud-upload-alt"></i>
|
||||||
|
<span id="uploadBtnText">Choose File</span>
|
||||||
|
</button>
|
||||||
|
<input type="file" name="fileToUpload" id="fileToUpload" style="display: none;" onchange="updateFields()" accept=".hex,.bin,.fw">
|
||||||
|
<span class="file-upload-info" id="fileUploadInfo">No file selected</span>
|
||||||
|
</div>
|
||||||
<label for="file_path">File Path</label>
|
<label for="file_path">File Path</label>
|
||||||
<input id="file_path" type="text" name="file_path" placeholder="File Path" value="' . htmlspecialchars($version['file_path']) . '" readonly>
|
<input id="file_path" type="text" name="file_path" placeholder="File Path" value="' . htmlspecialchars($version['file_path']) . '" readonly>
|
||||||
<label class="checkbox">
|
<label class="checkbox">
|
||||||
@@ -164,7 +171,10 @@ $view .= '<div class="content-block">
|
|||||||
<script>
|
<script>
|
||||||
function updateFields() {
|
function updateFields() {
|
||||||
var fileInput = document.getElementById(\'fileToUpload\');
|
var fileInput = document.getElementById(\'fileToUpload\');
|
||||||
|
var uploadBtnText = document.getElementById(\'uploadBtnText\');
|
||||||
|
var fileUploadInfo = document.getElementById(\'fileUploadInfo\');
|
||||||
var file = fileInput.files[0];
|
var file = fileInput.files[0];
|
||||||
|
|
||||||
if (file) {
|
if (file) {
|
||||||
var fileName = file.name;
|
var fileName = file.name;
|
||||||
var filePathInput = document.getElementById(\'file_path\');
|
var filePathInput = document.getElementById(\'file_path\');
|
||||||
@@ -175,8 +185,25 @@ function updateFields() {
|
|||||||
var nameWithoutExt = fileName.replace(/\.[^/.]+$/, "");
|
var nameWithoutExt = fileName.replace(/\.[^/.]+$/, "");
|
||||||
versionInput.value = nameWithoutExt;
|
versionInput.value = nameWithoutExt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update button and info text
|
||||||
|
uploadBtnText.textContent = \'Change File\';
|
||||||
|
fileUploadInfo.textContent = fileName + \' (\' + formatFileSize(file.size) + \')\';
|
||||||
|
fileUploadInfo.style.color = \'#28a745\';
|
||||||
|
} else {
|
||||||
|
uploadBtnText.textContent = \'Choose File\';
|
||||||
|
fileUploadInfo.textContent = \'No file selected\';
|
||||||
|
fileUploadInfo.style.color = \'#6c757d\';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function formatFileSize(bytes) {
|
||||||
|
if (bytes === 0) return \'0 Bytes\';
|
||||||
|
const k = 1024;
|
||||||
|
const sizes = [\'Bytes\', \'KB\', \'MB\', \'GB\'];
|
||||||
|
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||||
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + \' \' + sizes[i];
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
';
|
';
|
||||||
|
|
||||||
|
|||||||
94
profiles.php
94
profiles.php
@@ -298,59 +298,63 @@ $view .= '</form>';
|
|||||||
// Add basic JavaScript functionality
|
// Add basic JavaScript functionality
|
||||||
$view .= '
|
$view .= '
|
||||||
<style>
|
<style>
|
||||||
.profile-content { display: none !important; }
|
.profile-content { display: none; }
|
||||||
|
.profile-content.active { display: block !important; }
|
||||||
.profile-tab { background: #f8f9fa; color: #333; }
|
.profile-tab { background: #f8f9fa; color: #333; }
|
||||||
.profile-tab.active { background: #007bff; color: white; }
|
.profile-tab.active { background: #007bff; color: white; }
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener("DOMContentLoaded", function() {
|
// Use the working tab functionality from admin.js
|
||||||
|
document.querySelectorAll(\'.profile-tab\').forEach((element, index) => {
|
||||||
// Ensure all tabs start inactive
|
element.onclick = event => {
|
||||||
document.querySelectorAll(".profile-tab").forEach(tab => {
|
event.preventDefault();
|
||||||
tab.classList.remove("active");
|
|
||||||
});
|
|
||||||
document.querySelectorAll(".profile-content").forEach(content => {
|
|
||||||
content.style.display = "none";
|
|
||||||
});
|
|
||||||
|
|
||||||
// Tab functionality
|
|
||||||
document.addEventListener("click", function(e) {
|
|
||||||
if (e.target.classList.contains("profile-tab")) {
|
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
// Hide all content
|
|
||||||
document.querySelectorAll(".profile-content").forEach(content => {
|
|
||||||
content.style.display = "none";
|
|
||||||
});
|
|
||||||
|
|
||||||
// Remove active class from all tabs
|
|
||||||
document.querySelectorAll(".profile-tab").forEach(t => {
|
|
||||||
t.classList.remove("active");
|
|
||||||
});
|
|
||||||
|
|
||||||
// Show selected content and mark tab as active
|
|
||||||
const profileId = e.target.dataset.profile;
|
|
||||||
const content = document.getElementById(profileId + "-content");
|
|
||||||
if (content) {
|
|
||||||
content.style.display = "block";
|
|
||||||
e.target.classList.add("active");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bulk select functionality
|
// Toggle the clicked tab
|
||||||
if (e.target.classList.contains("select-all")) {
|
const isActive = element.classList.contains(\'active\');
|
||||||
const profile = e.target.dataset.profile;
|
const profileId = element.dataset.profile;
|
||||||
const checkboxes = document.querySelectorAll("[name=\\"" + profile + "[]\\"]");
|
const tabContent = document.getElementById(profileId + \'-content\');
|
||||||
checkboxes.forEach(cb => cb.checked = true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (e.target.classList.contains("select-none")) {
|
// Remove active class from all tabs and contents
|
||||||
const profile = e.target.dataset.profile;
|
document.querySelectorAll(\'.profile-tab\').forEach(el => el.classList.remove(\'active\'));
|
||||||
const checkboxes = document.querySelectorAll("[name=\\"" + profile + "[]\\"]");
|
document.querySelectorAll(\'.profile-content\').forEach(content => {
|
||||||
checkboxes.forEach(cb => cb.checked = false);
|
content.classList.remove(\'active\');
|
||||||
|
content.style.display = \'none\';
|
||||||
|
});
|
||||||
|
|
||||||
|
// If it wasn\'t active, make it active (collapsible behavior)
|
||||||
|
if (!isActive && tabContent) {
|
||||||
|
element.classList.add(\'active\');
|
||||||
|
tabContent.classList.add(\'active\');
|
||||||
|
tabContent.style.display = \'block\';
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// Initialize first tab as open by default
|
||||||
|
if (document.querySelectorAll(\'.profile-tab\').length > 0) {
|
||||||
|
const firstTab = document.querySelectorAll(\'.profile-tab\')[0];
|
||||||
|
const profileId = firstTab.dataset.profile;
|
||||||
|
const firstContent = document.getElementById(profileId + \'-content\');
|
||||||
|
if (firstTab && firstContent) {
|
||||||
|
firstTab.classList.add(\'active\');
|
||||||
|
firstContent.classList.add(\'active\');
|
||||||
|
firstContent.style.display = \'block\';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bulk select functionality
|
||||||
|
document.addEventListener("click", function(e) {
|
||||||
|
if (e.target.classList.contains("select-all")) {
|
||||||
|
const profile = e.target.dataset.profile;
|
||||||
|
const checkboxes = document.querySelectorAll("[name=\\"" + profile + "[]\\"]");
|
||||||
|
checkboxes.forEach(cb => cb.checked = true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.target.classList.contains("select-none")) {
|
||||||
|
const profile = e.target.dataset.profile;
|
||||||
|
const checkboxes = document.querySelectorAll("[name=\\"" + profile + "[]\\"]");
|
||||||
|
checkboxes.forEach(cb => cb.checked = false);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
</script>';
|
</script>';
|
||||||
|
|
||||||
|
|||||||
@@ -3006,4 +3006,83 @@ main .products .product .price, main .products .products-wrapper .product .price
|
|||||||
.filter-actions {
|
.filter-actions {
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Fix table scrolling on smaller screens */
|
||||||
|
main .content-block {
|
||||||
|
overflow: visible !important;
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
main .content-block .table {
|
||||||
|
overflow-x: auto !important;
|
||||||
|
overflow-y: visible !important;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
max-width: 100%;
|
||||||
|
margin: 0 -10px; /* Extend to container edges */
|
||||||
|
padding: 0 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
main .content-block .table table {
|
||||||
|
min-width: 700px; /* Ensure table maintains minimum width */
|
||||||
|
width: max-content; /* Allow table to expand naturally */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ensure table cells don't wrap */
|
||||||
|
main .content-block .table table td,
|
||||||
|
main .content-block .table table th {
|
||||||
|
white-space: nowrap;
|
||||||
|
min-width: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make version columns wider as they contain longer text */
|
||||||
|
main .content-block .table table th:first-child,
|
||||||
|
main .content-block .table table td:first-child,
|
||||||
|
main .content-block .table table th:nth-child(2),
|
||||||
|
main .content-block .table table td:nth-child(2) {
|
||||||
|
min-width: 120px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* File Upload Button Styles */
|
||||||
|
.file-upload-wrapper {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 15px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-upload-btn {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 10px 10px;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 6px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 123, 255, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-upload-btn:hover {
|
||||||
|
background: linear-gradient(135deg, #0056b3, #004085);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
box-shadow: 0 4px 8px rgba(0, 123, 255, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-upload-btn:active {
|
||||||
|
transform: translateY(0);
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 123, 255, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-upload-btn i {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-upload-info {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #6c757d;
|
||||||
|
font-style: italic;
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user