Refactor admin dashboard and fix mobile phone input dropdown
Build And Push Image / docker (push) Successful in 2m54s
Details
Build And Push Image / docker (push) Successful in 2m54s
Details
- Simplify admin dashboard by consolidating configuration sections - Fix mobile dropdown display issues in PhoneInputWrapper component - Add proper flex layout and minimum height for mobile country list - Update email configuration and testing functionality - Remove redundant configuration cards in favor of unified portal settings
This commit is contained in:
parent
dcb7840825
commit
8d872f9a04
|
|
@ -68,6 +68,7 @@
|
||||||
class="country-dropdown"
|
class="country-dropdown"
|
||||||
:class="{ 'country-dropdown--mobile': isMobile }"
|
:class="{ 'country-dropdown--mobile': isMobile }"
|
||||||
elevation="8"
|
elevation="8"
|
||||||
|
:style="isMobile ? 'display: block !important;' : ''"
|
||||||
>
|
>
|
||||||
<!-- Mobile Header -->
|
<!-- Mobile Header -->
|
||||||
<div v-if="isMobile" class="mobile-header">
|
<div v-if="isMobile" class="mobile-header">
|
||||||
|
|
@ -562,15 +563,21 @@ watch(() => props.modelValue, (newValue) => {
|
||||||
right: 5% !important;
|
right: 5% !important;
|
||||||
width: 90vw !important;
|
width: 90vw !important;
|
||||||
max-width: 400px !important;
|
max-width: 400px !important;
|
||||||
|
min-height: 400px !important;
|
||||||
max-height: 80vh !important;
|
max-height: 80vh !important;
|
||||||
|
height: auto !important;
|
||||||
margin: 0 auto !important;
|
margin: 0 auto !important;
|
||||||
border-radius: 16px !important;
|
border-radius: 16px !important;
|
||||||
z-index: 2000 !important;
|
z-index: 2000 !important;
|
||||||
box-shadow: 0 24px 48px rgba(0, 0, 0, 0.3) !important;
|
box-shadow: 0 24px 48px rgba(0, 0, 0, 0.3) !important;
|
||||||
overflow: hidden !important;
|
overflow: hidden !important;
|
||||||
|
display: flex !important;
|
||||||
|
flex-direction: column !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.country-list--mobile {
|
.country-list--mobile {
|
||||||
|
flex: 1 !important;
|
||||||
|
min-height: 200px !important;
|
||||||
max-height: calc(60vh - 120px) !important;
|
max-height: calc(60vh - 120px) !important;
|
||||||
overflow-y: auto !important;
|
overflow-y: auto !important;
|
||||||
-webkit-overflow-scrolling: touch;
|
-webkit-overflow-scrolling: touch;
|
||||||
|
|
|
||||||
|
|
@ -112,92 +112,21 @@
|
||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
|
|
||||||
<!-- System Configuration -->
|
<!-- Portal Configuration -->
|
||||||
<v-row class="mb-6">
|
<v-row class="mb-6">
|
||||||
<v-col cols="12" md="6">
|
<v-col cols="12">
|
||||||
<v-card elevation="2">
|
<v-card elevation="2">
|
||||||
<v-card-title>
|
<v-card-title>
|
||||||
<v-icon left>mdi-shield-check</v-icon>
|
<v-icon left>mdi-cog</v-icon>
|
||||||
reCAPTCHA Configuration
|
Portal Configuration
|
||||||
</v-card-title>
|
</v-card-title>
|
||||||
<v-card-text>
|
<v-card-text>
|
||||||
<p class="mb-4">Configure reCAPTCHA settings for form security.</p>
|
<p class="mb-4">Configure all portal settings including database, email, reCAPTCHA, and membership fees in one centralized location.</p>
|
||||||
|
|
||||||
|
<v-row>
|
||||||
|
<v-col cols="12" md="4">
|
||||||
<v-btn
|
<v-btn
|
||||||
color="warning"
|
color="primary"
|
||||||
variant="outlined"
|
|
||||||
block
|
|
||||||
size="large"
|
|
||||||
@click="showRecaptchaConfig = true"
|
|
||||||
>
|
|
||||||
<v-icon start>mdi-shield-account</v-icon>
|
|
||||||
Configure reCAPTCHA
|
|
||||||
</v-btn>
|
|
||||||
</v-card-text>
|
|
||||||
</v-card>
|
|
||||||
</v-col>
|
|
||||||
|
|
||||||
<v-col cols="12" md="6">
|
|
||||||
<v-card elevation="2">
|
|
||||||
<v-card-title>
|
|
||||||
<v-icon left>mdi-account-plus</v-icon>
|
|
||||||
Membership Configuration
|
|
||||||
</v-card-title>
|
|
||||||
<v-card-text>
|
|
||||||
<p class="mb-4">Configure membership fees and payment details.</p>
|
|
||||||
|
|
||||||
<v-btn
|
|
||||||
color="success"
|
|
||||||
variant="outlined"
|
|
||||||
block
|
|
||||||
size="large"
|
|
||||||
@click="showMembershipConfig = true"
|
|
||||||
>
|
|
||||||
<v-icon start>mdi-bank</v-icon>
|
|
||||||
Configure Membership
|
|
||||||
</v-btn>
|
|
||||||
</v-card-text>
|
|
||||||
</v-card>
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
|
|
||||||
<!-- Email Configuration -->
|
|
||||||
<v-row class="mb-6">
|
|
||||||
<v-col cols="12" md="6">
|
|
||||||
<v-card elevation="2">
|
|
||||||
<v-card-title>
|
|
||||||
<v-icon left>mdi-email-cog</v-icon>
|
|
||||||
Email Configuration
|
|
||||||
</v-card-title>
|
|
||||||
<v-card-text>
|
|
||||||
<p class="mb-4">Configure SMTP settings for email notifications and verification.</p>
|
|
||||||
|
|
||||||
<v-btn
|
|
||||||
color="info"
|
|
||||||
variant="outlined"
|
|
||||||
block
|
|
||||||
size="large"
|
|
||||||
@click="openEmailConfig"
|
|
||||||
>
|
|
||||||
<v-icon start>mdi-email-settings</v-icon>
|
|
||||||
Configure Email
|
|
||||||
</v-btn>
|
|
||||||
</v-card-text>
|
|
||||||
</v-card>
|
|
||||||
</v-col>
|
|
||||||
|
|
||||||
<v-col cols="12" md="6">
|
|
||||||
<v-card elevation="2">
|
|
||||||
<v-card-title>
|
|
||||||
<v-icon left>mdi-cog-outline</v-icon>
|
|
||||||
All Settings
|
|
||||||
</v-card-title>
|
|
||||||
<v-card-text>
|
|
||||||
<p class="mb-4">Access all portal configuration settings in one place.</p>
|
|
||||||
|
|
||||||
<v-btn
|
|
||||||
color="secondary"
|
|
||||||
variant="outlined"
|
|
||||||
block
|
block
|
||||||
size="large"
|
size="large"
|
||||||
@click="showAdminConfig = true"
|
@click="showAdminConfig = true"
|
||||||
|
|
@ -205,43 +134,35 @@
|
||||||
<v-icon start>mdi-cog</v-icon>
|
<v-icon start>mdi-cog</v-icon>
|
||||||
Portal Settings
|
Portal Settings
|
||||||
</v-btn>
|
</v-btn>
|
||||||
</v-card-text>
|
</v-col>
|
||||||
</v-card>
|
|
||||||
|
<v-col cols="12" md="8">
|
||||||
|
<v-row dense>
|
||||||
|
<v-col cols="6" sm="3">
|
||||||
|
<v-chip color="success" variant="tonal" size="small" block>
|
||||||
|
<v-icon start size="14">mdi-database</v-icon>
|
||||||
|
NocoDB
|
||||||
|
</v-chip>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="6" sm="3">
|
||||||
|
<v-chip color="info" variant="tonal" size="small" block>
|
||||||
|
<v-icon start size="14">mdi-email</v-icon>
|
||||||
|
Email
|
||||||
|
</v-chip>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="6" sm="3">
|
||||||
|
<v-chip color="warning" variant="tonal" size="small" block>
|
||||||
|
<v-icon start size="14">mdi-shield</v-icon>
|
||||||
|
reCAPTCHA
|
||||||
|
</v-chip>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="6" sm="3">
|
||||||
|
<v-chip color="primary" variant="tonal" size="small" block>
|
||||||
|
<v-icon start size="14">mdi-bank</v-icon>
|
||||||
|
Membership
|
||||||
|
</v-chip>
|
||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
|
|
||||||
<!-- NocoDB Configuration -->
|
|
||||||
<v-row class="mb-6">
|
|
||||||
<v-col cols="12">
|
|
||||||
<v-card elevation="2">
|
|
||||||
<v-card-title>
|
|
||||||
<v-icon left>mdi-database-cog</v-icon>
|
|
||||||
Database Configuration
|
|
||||||
</v-card-title>
|
|
||||||
<v-card-text>
|
|
||||||
<p class="mb-4">Configure the NocoDB database connection for the Member Management system.</p>
|
|
||||||
|
|
||||||
<v-row>
|
|
||||||
<v-col cols="12" md="6">
|
|
||||||
<v-btn
|
|
||||||
color="info"
|
|
||||||
variant="outlined"
|
|
||||||
block
|
|
||||||
size="large"
|
|
||||||
@click="showNocoDBSettings = true"
|
|
||||||
>
|
|
||||||
<v-icon start>mdi-database-settings</v-icon>
|
|
||||||
Configure NocoDB
|
|
||||||
</v-btn>
|
|
||||||
</v-col>
|
|
||||||
|
|
||||||
<v-col cols="12" md="6">
|
|
||||||
<div class="d-flex align-center" style="height: 100%;">
|
|
||||||
<v-chip color="success" variant="tonal" size="small">
|
|
||||||
<v-icon start size="14">mdi-database</v-icon>
|
|
||||||
Ready for configuration
|
|
||||||
</v-chip>
|
|
||||||
</div>
|
|
||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
</v-card-text>
|
</v-card-text>
|
||||||
|
|
|
||||||
|
|
@ -51,16 +51,18 @@ export default defineEventHandler(async (event) => {
|
||||||
const { getEmailService } = await import('~/server/utils/email');
|
const { getEmailService } = await import('~/server/utils/email');
|
||||||
const emailService = await getEmailService();
|
const emailService = await getEmailService();
|
||||||
|
|
||||||
// Verify connection first
|
// Try to verify connection but don't fail if verification doesn't work
|
||||||
|
// Some SMTP servers have issues with verify() but work fine for sending
|
||||||
|
try {
|
||||||
const connectionOk = await emailService.verifyConnection();
|
const connectionOk = await emailService.verifyConnection();
|
||||||
if (!connectionOk) {
|
if (connectionOk) {
|
||||||
throw createError({
|
console.log('[api/admin/test-email.post] SMTP connection verified successfully');
|
||||||
statusCode: 500,
|
}
|
||||||
statusMessage: 'SMTP connection verification failed. Please check your SMTP configuration.'
|
} catch (verifyError: any) {
|
||||||
});
|
console.warn('[api/admin/test-email.post] SMTP verification failed, attempting to send anyway:', verifyError.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send test email
|
// Attempt to send test email regardless of verification result
|
||||||
await emailService.sendTestEmail(body.testEmail);
|
await emailService.sendTestEmail(body.testEmail);
|
||||||
|
|
||||||
console.log('[api/admin/test-email.post] ✅ Test email sent successfully');
|
console.log('[api/admin/test-email.post] ✅ Test email sent successfully');
|
||||||
|
|
|
||||||
|
|
@ -60,20 +60,67 @@ export class EmailService {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.transporter = nodemailer.createTransport({
|
// Determine security settings based on port
|
||||||
|
let useSecure = this.config.secure;
|
||||||
|
let requireTLS = false;
|
||||||
|
|
||||||
|
// Auto-configure based on standard ports if not explicitly set
|
||||||
|
if (this.config.port === 587) {
|
||||||
|
// Port 587 typically uses STARTTLS
|
||||||
|
useSecure = false;
|
||||||
|
requireTLS = true;
|
||||||
|
} else if (this.config.port === 465) {
|
||||||
|
// Port 465 typically uses SSL/TLS
|
||||||
|
useSecure = true;
|
||||||
|
requireTLS = false;
|
||||||
|
} else if (this.config.port === 25) {
|
||||||
|
// Port 25 typically unencrypted (not recommended)
|
||||||
|
useSecure = false;
|
||||||
|
requireTLS = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build transporter options
|
||||||
|
const transporterOptions: any = {
|
||||||
host: this.config.host,
|
host: this.config.host,
|
||||||
port: this.config.port,
|
port: this.config.port,
|
||||||
secure: this.config.secure, // true for 465, false for other ports
|
secure: useSecure,
|
||||||
auth: this.config.username && this.config.password ? {
|
// Connection timeout settings
|
||||||
|
connectionTimeout: 30000, // 30 seconds
|
||||||
|
greetingTimeout: 30000,
|
||||||
|
socketTimeout: 30000,
|
||||||
|
// Debug logging
|
||||||
|
logger: false,
|
||||||
|
debug: false
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add requireTLS if needed (for STARTTLS)
|
||||||
|
if (requireTLS && !useSecure) {
|
||||||
|
transporterOptions.requireTLS = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure TLS options
|
||||||
|
transporterOptions.tls = {
|
||||||
|
rejectUnauthorized: false, // Accept self-signed certificates
|
||||||
|
// Don't specify minVersion or ciphers to allow auto-negotiation
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add authentication only if credentials are provided
|
||||||
|
if (this.config.username && this.config.password) {
|
||||||
|
transporterOptions.auth = {
|
||||||
user: this.config.username,
|
user: this.config.username,
|
||||||
pass: this.config.password
|
pass: this.config.password
|
||||||
} : undefined,
|
};
|
||||||
tls: {
|
|
||||||
rejectUnauthorized: false // Accept self-signed certificates in development
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
console.log('[EmailService] ✅ SMTP transporter initialized');
|
this.transporter = nodemailer.createTransport(transporterOptions);
|
||||||
|
|
||||||
|
console.log('[EmailService] ✅ SMTP transporter initialized with options:', {
|
||||||
|
host: this.config.host,
|
||||||
|
port: this.config.port,
|
||||||
|
secure: transporterOptions.secure,
|
||||||
|
requireTLS: transporterOptions.requireTLS,
|
||||||
|
auth: !!transporterOptions.auth
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[EmailService] ❌ Failed to initialize SMTP transporter:', error);
|
console.error('[EmailService] ❌ Failed to initialize SMTP transporter:', error);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue