Zaktualizuj funkcję wysyłania e-maili, aby obsługiwała wiele załączników oraz dodaj walidację plików
This commit is contained in:
@@ -3,7 +3,7 @@ session_start();
|
||||
require_once 'phpmailer/class.phpmailer.php';
|
||||
require_once 'phpmailer/class.smtp.php';
|
||||
|
||||
function send_email( $email, $reply, $subject, $text, $attachment = null )
|
||||
function send_email( $email, $reply, $subject, $text, $attachments = array() )
|
||||
{
|
||||
$mail = new PHPMailer;
|
||||
$mail -> IsSMTP();
|
||||
@@ -27,17 +27,88 @@ function send_email( $email, $reply, $subject, $text, $attachment = null )
|
||||
$mail -> isHTML( true );
|
||||
$mail -> Subject = $subject;
|
||||
$mail -> Body = $text;
|
||||
|
||||
if ($attachment && isset($attachment['tmp_name']) && file_exists($attachment['tmp_name'])) {
|
||||
$mail->addAttachment($attachment['tmp_name'], $attachment['name']);
|
||||
|
||||
// Obsługa wielu załączników
|
||||
if (is_array($attachments) && count($attachments) > 0) {
|
||||
foreach ($attachments as $attachment) {
|
||||
if (isset($attachment['tmp_name']) && file_exists($attachment['tmp_name'])) {
|
||||
$mail->addAttachment($attachment['tmp_name'], $attachment['name']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $mail -> send();
|
||||
}
|
||||
|
||||
function validate_file($file) {
|
||||
$maxSize = 10 * 1024 * 1024; // 10MB
|
||||
$allowedExtensions = array('jpg', 'jpeg', 'png', 'pdf', 'doc', 'docx', 'xls', 'xlsx');
|
||||
$allowedMimeTypes = array(
|
||||
'image/jpeg',
|
||||
'image/jpg',
|
||||
'image/png',
|
||||
'application/pdf',
|
||||
'application/msword',
|
||||
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||||
'application/vnd.ms-excel',
|
||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||||
);
|
||||
|
||||
// Sprawdź czy plik istnieje
|
||||
if (!isset($file['tmp_name']) || !file_exists($file['tmp_name'])) {
|
||||
return array('valid' => false, 'error' => 'Plik nie istnieje');
|
||||
}
|
||||
|
||||
// Sprawdź rozmiar
|
||||
if ($file['size'] > $maxSize) {
|
||||
return array('valid' => false, 'error' => 'Plik jest za duży (max 10MB)');
|
||||
}
|
||||
|
||||
// Sprawdź rozszerzenie
|
||||
$fileExtension = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
|
||||
if (!in_array($fileExtension, $allowedExtensions)) {
|
||||
return array('valid' => false, 'error' => 'Niedozwolone rozszerzenie pliku');
|
||||
}
|
||||
|
||||
// Sprawdź MIME type
|
||||
$finfo = finfo_open(FILEINFO_MIME_TYPE);
|
||||
$mimeType = finfo_file($finfo, $file['tmp_name']);
|
||||
finfo_close($finfo);
|
||||
|
||||
if (!in_array($mimeType, $allowedMimeTypes)) {
|
||||
return array('valid' => false, 'error' => 'Niedozwolony typ pliku');
|
||||
}
|
||||
|
||||
return array('valid' => true);
|
||||
}
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$configData = json_decode($_POST['configData'], true);
|
||||
|
||||
$file = isset($_FILES['attachment']) ? $_FILES['attachment'] : null;
|
||||
// Pobierz wszystkie załączniki
|
||||
$attachments = array();
|
||||
$attachments_count = isset($_POST['attachments_count']) ? intval($_POST['attachments_count']) : 0;
|
||||
|
||||
// Walidacja liczby załączników (max 10)
|
||||
if ($attachments_count > 10) {
|
||||
echo json_encode(['status' => 'error', 'message' => 'Maksymalnie 10 załączników']);
|
||||
exit();
|
||||
}
|
||||
|
||||
for ($i = 0; $i < $attachments_count; $i++) {
|
||||
$fileKey = 'attachment_' . $i;
|
||||
if (isset($_FILES[$fileKey])) {
|
||||
$file = $_FILES[$fileKey];
|
||||
|
||||
// Walidacja pliku
|
||||
$validation = validate_file($file);
|
||||
if (!$validation['valid']) {
|
||||
echo json_encode(['status' => 'error', 'message' => 'Błąd walidacji pliku: ' . $validation['error']]);
|
||||
exit();
|
||||
}
|
||||
|
||||
$attachments[] = $file;
|
||||
}
|
||||
}
|
||||
|
||||
$to = 'kontakt@ostal.pl';
|
||||
$subject = 'ostal.pl - Konfigurator';
|
||||
@@ -94,7 +165,18 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$message .= '</ul>';
|
||||
}
|
||||
|
||||
if (send_email($to, $configData['step_4']['email'], $subject, $message, $file)) {
|
||||
// Dodaj informację o załącznikach
|
||||
if (count($attachments) > 0) {
|
||||
$message .= '<p>Załączniki (' . count($attachments) . '):</p>';
|
||||
$message .= '<ul>';
|
||||
foreach ($attachments as $attachment) {
|
||||
$fileSize = round($attachment['size'] / 1024, 2); // KB
|
||||
$message .= '<li>' . htmlspecialchars($attachment['name']) . ' (' . $fileSize . ' KB)</li>';
|
||||
}
|
||||
$message .= '</ul>';
|
||||
}
|
||||
|
||||
if (send_email($to, $configData['step_4']['email'], $subject, $message, $attachments)) {
|
||||
$_SESSION["configurator_sended"] = true;
|
||||
echo json_encode(['status' => 'ok', 'message' => 'Wiadmość wysłana']);
|
||||
} else {
|
||||
|
||||
@@ -112,47 +112,6 @@
|
||||
<p v-if="selected_option_1 === 0" class="budget-info" style="margin-top: 20px; font-size: 14px; color: #666; font-style: italic;">
|
||||
Orientacyjny budżet całorocznego ogrodu zimowego zwykle mieści się w przedziale 120 000 – 180 000 zł netto
|
||||
</p>
|
||||
|
||||
<div class="tabs_examples" v-if="selected_option_1 === 0">
|
||||
<h3>Przykłady cen ogrodu zimowego 4x5m</h3>
|
||||
<div class="tabs_examples-nav">
|
||||
<button :class="{ active: activeExampleTab === 'standard' }" @click="activeExampleTab = 'standard'">Standard</button>
|
||||
<button :class="{ active: activeExampleTab === 'comfort' }" @click="activeExampleTab = 'comfort'">Comfort</button>
|
||||
<button :class="{ active: activeExampleTab === 'premium' }" @click="activeExampleTab = 'premium'">Premium</button>
|
||||
</div>
|
||||
<div class="tabs_examples-content">
|
||||
<!-- Standard -->
|
||||
<div class="tabs_examples-panel" v-if="activeExampleTab === 'standard'">
|
||||
<div class="tabs_examples-img">
|
||||
<img src="/wp-content/uploads/2026/02/image-1.png" alt="Ogród zimowy Standard 4x5m">
|
||||
</div>
|
||||
<div class="tabs_examples-desc">
|
||||
<h4>Ogród zimowy Standard</h4>
|
||||
<p>Ogród zimowy standard z szybami Ug=1,1 na dachu i ścianach i z jednymi drzwiami tarasowymi uchylno-przesuwnymi PSK na frontowej ścianie 2,5x2,2m. Bez osłon przeciwsłonecznych (nadaje się w zacienione miejsca).</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Comfort -->
|
||||
<div class="tabs_examples-panel" v-if="activeExampleTab === 'comfort'">
|
||||
<div class="tabs_examples-img">
|
||||
<img src="/wp-content/uploads/2026/02/image-2.png" alt="Ogród zimowy Comfort 4x5m">
|
||||
</div>
|
||||
<div class="tabs_examples-desc">
|
||||
<h4>Ogród zimowy Comfort</h4>
|
||||
<p>Ogród zimowy Comfort z szybami Ug=1,1 na dachu i ścianach i z dwoma drzwiami tarasowymi uchylno-przesuwnymi PSK na frontowej ścianie 2,5x2,2m + boczna ściana 3 kwatery 1 skrzydło PSK + osłona przeciwsłoneczna dachu, 2 osłony Veranda ster. elektr.</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Premium -->
|
||||
<div class="tabs_examples-panel" v-if="activeExampleTab === 'premium'">
|
||||
<div class="tabs_examples-img">
|
||||
<img src="/wp-content/uploads/2026/02/image-3.png" alt="Ogród zimowy Premium 4x5m">
|
||||
</div>
|
||||
<div class="tabs_examples-desc">
|
||||
<h4>Ogród zimowy Premium</h4>
|
||||
<p>Ogród zimowy premium z szybami energooszczędnymi Ug=0,5 na dachu i ścianach i z dwoma drzwiami tarasowymi. 1 szt. uchylno-przesuwnymi PSK na frontowej ścianie 2,5x2,2m + 1 szt. drzwi HS na bocznej ścianie 1 skrzydło + komplet osłon przeciwsłonecznych na dachu, 2 osłony Veranda ster. elektr. z czujnikiem słońce wiatr + 1 szt. osłony przeciwsłoneczne żaluzje C80 na bocznych ścianach ster. el. + okno HS w dachu przesuwane, sterowane el. + oświetlenie punktowe LED z pilotem. Montaż na co drugiej krokwi.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -227,6 +186,56 @@
|
||||
</select>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="tabs_examples" v-if="selected_option_1 === 0" style="margin-top: 50px;">
|
||||
<h3>Przykłady cen ogrodu zimowego 4x5m</h3>
|
||||
<div class="tabs_examples-nav">
|
||||
<button :class="{ active: activeExampleTab === 'standard' }" @click="activeExampleTab = 'standard'">Standard</button>
|
||||
<button :class="{ active: activeExampleTab === 'comfort' }" @click="activeExampleTab = 'comfort'">Comfort</button>
|
||||
<button :class="{ active: activeExampleTab === 'premium' }" @click="activeExampleTab = 'premium'">Premium</button>
|
||||
</div>
|
||||
<div class="tabs_examples-content">
|
||||
<!-- Standard -->
|
||||
<div class="tabs_examples-panel" v-if="activeExampleTab === 'standard'">
|
||||
<div class="tabs_examples-img">
|
||||
<img src="/wp-content/uploads/2026/02/image-1.png" alt="Ogród zimowy Standard 4x5m">
|
||||
</div>
|
||||
<div class="tabs_examples-desc">
|
||||
<h4>Ogród zimowy Classic 1 Standard.<br>Cena brutto 114.390,00 zł (z VAT 8%)</h4>
|
||||
<p>- Szyby izolacyjne Ug=1,1 na dachu i ścianach.<br>
|
||||
- 1 szt. drzwi tarasowe uchylno-przesuwnymi PSK na frontowej ścianie.<br>
|
||||
- Bez osłon przeciwsłonecznych. (w zacienione miejsca)</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Comfort -->
|
||||
<div class="tabs_examples-panel" v-if="activeExampleTab === 'comfort'">
|
||||
<div class="tabs_examples-img">
|
||||
<img src="/wp-content/uploads/2026/02/image-2.png" alt="Ogród zimowy Comfort 4x5m">
|
||||
</div>
|
||||
<div class="tabs_examples-desc">
|
||||
<h4>Ogród zimowy Classic 2 Comfort.<br>Cena brutto 142.819,00 zł (z VAT 8%)</h4>
|
||||
<p>- Szyby izolacyjne Ug=1,1 na dachu i ścianach.<br>
|
||||
- 2 szt. drzwi tarasowych uchylno-przesuwnymi PSK na ścianie frontowej i ścianie bocznej.<br>
|
||||
- Komplet osłon przeciwsłonecznych na dachu. 2 osłony Veranda ster. Elektr + pilot.</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Premium -->
|
||||
<div class="tabs_examples-panel" v-if="activeExampleTab === 'premium'">
|
||||
<div class="tabs_examples-img">
|
||||
<img src="/wp-content/uploads/2026/02/image-3.png" alt="Ogród zimowy Premium 4x5m">
|
||||
</div>
|
||||
<div class="tabs_examples-desc">
|
||||
<h4>Ogród zimowy Classic 3 Premium.<br>Cena brutto 203.550,00 zł (z VAT 8%)</h4>
|
||||
<p>- Szyby Energooszczędne Ug=0,5 na dachu i ścianach.<br>
|
||||
- 1 szt. drzwi tarasowych uchylno-przesuwnymi PSK na frontowej ścianie + 1 szt. drzwi unoszono przesuwne HS na bocznej ścianie.<br>
|
||||
- Komplet osłon przeciwsłonecznych na dachu. 2 osłony Veranda ster. Elektr. + pilot + czujnik słoneczno-wiatrowy.<br>
|
||||
- Osłony przeciwsłoneczne ścian żaluzje C80 sterowane ele. + pilot.<br>
|
||||
- Okno HS w dachu przesuwane, sterowane ele.<br>
|
||||
- oświetlenie punktowe LED z pilotem.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="windowWidth > 1000">
|
||||
@@ -292,7 +301,46 @@
|
||||
</li>
|
||||
<li>
|
||||
<p>{{acfData.acf.step_4.attachment}}</p>
|
||||
<input type="file" ref="fileInput" name="attachment" id="calc_attachment" class="input">
|
||||
<div class="file-upload-area"
|
||||
:class="{ 'dragover': isDragOver }"
|
||||
@dragover.prevent="isDragOver = true"
|
||||
@dragleave.prevent="isDragOver = false"
|
||||
@drop.prevent="handleFileDrop">
|
||||
<input type="file"
|
||||
ref="fileInput"
|
||||
name="attachment"
|
||||
id="calc_attachment"
|
||||
class="file-input-hidden"
|
||||
multiple
|
||||
accept=".jpg,.jpeg,.png,.pdf,.doc,.docx,.xls,.xlsx"
|
||||
@change="handleFileSelect">
|
||||
<div class="file-upload-content" @click="$refs.fileInput.click()">
|
||||
<svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="#88b14b" stroke-width="2">
|
||||
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
|
||||
<polyline points="17 8 12 3 7 8"></polyline>
|
||||
<line x1="12" y1="3" x2="12" y2="15"></line>
|
||||
</svg>
|
||||
<p class="upload-text">Przeciągnij pliki tutaj lub kliknij aby wybrać</p>
|
||||
<p class="upload-hint">Dozwolone: JPG, PNG, PDF, DOC, DOCX, XLS, XLSX (max 10MB każdy)</p>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="uploadedFiles.length > 0" class="uploaded-files-list">
|
||||
<div v-for="(file, index) in uploadedFiles" :key="index" class="uploaded-file-item">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#88b14b" stroke-width="2">
|
||||
<path d="M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z"></path>
|
||||
<polyline points="13 2 13 9 20 9"></polyline>
|
||||
</svg>
|
||||
<span class="file-name">{{ file.name }}</span>
|
||||
<span class="file-size">({{ formatFileSize(file.size) }})</span>
|
||||
<button type="button" class="remove-file-btn" @click="removeFile(index)">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#dc3545" stroke-width="2">
|
||||
<line x1="18" y1="6" x2="6" y2="18"></line>
|
||||
<line x1="6" y1="6" x2="18" y2="18"></line>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<p v-if="fileError" class="file-error">{{ fileError }}</p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -451,6 +499,9 @@
|
||||
showDatepicker: false,
|
||||
dpMonth: new Date().getMonth(),
|
||||
dpYear: new Date().getFullYear(),
|
||||
uploadedFiles: [],
|
||||
isDragOver: false,
|
||||
fileError: '',
|
||||
|
||||
acfData: {
|
||||
acf: {}
|
||||
@@ -557,10 +608,11 @@
|
||||
|
||||
formData.append('configData', JSON.stringify(this.configData));
|
||||
|
||||
const file = this.$refs.fileInput?.files[0];
|
||||
if (file) {
|
||||
formData.append('attachment', file);
|
||||
}
|
||||
// Dodaj wszystkie załączone pliki
|
||||
this.uploadedFiles.forEach((file, index) => {
|
||||
formData.append(`attachment_${index}`, file);
|
||||
});
|
||||
formData.append('attachments_count', this.uploadedFiles.length);
|
||||
// const data = {
|
||||
// configData: this.configData,
|
||||
// };
|
||||
@@ -641,6 +693,8 @@
|
||||
|
||||
this.selected_option_1 = null
|
||||
this.selected_option_3 = null
|
||||
this.uploadedFiles = []
|
||||
this.fileError = ''
|
||||
|
||||
while (stack.length > 0) {
|
||||
const obj = stack.pop();
|
||||
@@ -696,6 +750,82 @@
|
||||
const now = new Date();
|
||||
return day === now.getDate() && this.dpMonth === now.getMonth() && this.dpYear === now.getFullYear();
|
||||
},
|
||||
handleFileDrop(e) {
|
||||
this.isDragOver = false;
|
||||
const files = Array.from(e.dataTransfer.files);
|
||||
this.addFiles(files);
|
||||
},
|
||||
handleFileSelect(e) {
|
||||
const files = Array.from(e.target.files);
|
||||
this.addFiles(files);
|
||||
},
|
||||
addFiles(files) {
|
||||
this.fileError = '';
|
||||
|
||||
for (const file of files) {
|
||||
const validation = this.validateFile(file);
|
||||
if (!validation.valid) {
|
||||
this.fileError = validation.error;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Sprawdź czy plik już nie został dodany
|
||||
const exists = this.uploadedFiles.some(f => f.name === file.name && f.size === file.size);
|
||||
if (!exists) {
|
||||
this.uploadedFiles.push(file);
|
||||
}
|
||||
}
|
||||
|
||||
// Wyczyść input aby móc dodać ten sam plik ponownie
|
||||
if (this.$refs.fileInput) {
|
||||
this.$refs.fileInput.value = '';
|
||||
}
|
||||
},
|
||||
validateFile(file) {
|
||||
const maxSize = 10 * 1024 * 1024; // 10MB
|
||||
const allowedTypes = [
|
||||
'image/jpeg',
|
||||
'image/jpg',
|
||||
'image/png',
|
||||
'application/pdf',
|
||||
'application/msword',
|
||||
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||||
'application/vnd.ms-excel',
|
||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||||
];
|
||||
const allowedExtensions = ['.jpg', '.jpeg', '.png', '.pdf', '.doc', '.docx', '.xls', '.xlsx'];
|
||||
|
||||
// Sprawdź rozszerzenie pliku
|
||||
const fileName = file.name.toLowerCase();
|
||||
const hasValidExtension = allowedExtensions.some(ext => fileName.endsWith(ext));
|
||||
|
||||
if (!hasValidExtension && !allowedTypes.includes(file.type)) {
|
||||
return {
|
||||
valid: false,
|
||||
error: `Nieprawidłowy typ pliku: ${file.name}. Dozwolone: JPG, PNG, PDF, DOC, DOCX, XLS, XLSX`
|
||||
};
|
||||
}
|
||||
|
||||
if (file.size > maxSize) {
|
||||
return {
|
||||
valid: false,
|
||||
error: `Plik ${file.name} jest za duży. Maksymalny rozmiar: 10MB`
|
||||
};
|
||||
}
|
||||
|
||||
return { valid: true };
|
||||
},
|
||||
removeFile(index) {
|
||||
this.uploadedFiles.splice(index, 1);
|
||||
this.fileError = '';
|
||||
},
|
||||
formatFileSize(bytes) {
|
||||
if (bytes === 0) return '0 B';
|
||||
const k = 1024;
|
||||
const sizes = ['B', 'KB', 'MB'];
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||
return Math.round((bytes / Math.pow(k, i)) * 100) / 100 + ' ' + sizes[i];
|
||||
},
|
||||
getWindowWidth() {
|
||||
this.windowWidth = window.innerWidth;
|
||||
},
|
||||
@@ -742,4 +872,121 @@
|
||||
|
||||
app.mount('#app')
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.file-upload-area {
|
||||
border: 2px dashed #88b14b;
|
||||
border-radius: 8px;
|
||||
padding: 30px;
|
||||
text-align: center;
|
||||
background-color: #f8f9fa;
|
||||
transition: all 0.3s ease;
|
||||
cursor: pointer;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.file-upload-area:hover {
|
||||
background-color: #e8f5e9;
|
||||
border-color: #6a8f3a;
|
||||
}
|
||||
|
||||
.file-upload-area.dragover {
|
||||
background-color: #e8f5e9;
|
||||
border-color: #6a8f3a;
|
||||
transform: scale(1.02);
|
||||
}
|
||||
|
||||
.file-input-hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.file-upload-content {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.file-upload-content svg {
|
||||
margin: 0 auto 15px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.upload-text {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
margin: 10px 0 5px;
|
||||
}
|
||||
|
||||
.upload-hint {
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.uploaded-files-list {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.uploaded-file-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 12px;
|
||||
background-color: #fff;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 6px;
|
||||
margin-bottom: 8px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.uploaded-file-item:hover {
|
||||
background-color: #f8f9fa;
|
||||
border-color: #88b14b;
|
||||
}
|
||||
|
||||
.uploaded-file-item svg:first-child {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.file-name {
|
||||
flex: 1;
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.file-size {
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.remove-file-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 4px;
|
||||
transition: background-color 0.2s ease;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.remove-file-btn:hover {
|
||||
background-color: #fee;
|
||||
}
|
||||
|
||||
.file-error {
|
||||
color: #dc3545;
|
||||
font-size: 13px;
|
||||
margin-top: 10px;
|
||||
padding: 8px 12px;
|
||||
background-color: #fee;
|
||||
border-radius: 4px;
|
||||
border-left: 3px solid #dc3545;
|
||||
}
|
||||
</style>
|
||||
<?php get_footer(); ?>
|
||||
Reference in New Issue
Block a user