fixes
Build And Push Image / docker (push) Successful in 3m5s
Details
Build And Push Image / docker (push) Successful in 3m5s
Details
This commit is contained in:
parent
4365cc53ff
commit
72492fb754
|
|
@ -112,6 +112,55 @@
|
|||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<!-- System Configuration -->
|
||||
<v-row class="mb-6">
|
||||
<v-col cols="12" md="6">
|
||||
<v-card elevation="2">
|
||||
<v-card-title>
|
||||
<v-icon left>mdi-shield-check</v-icon>
|
||||
reCAPTCHA Configuration
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
<p class="mb-4">Configure reCAPTCHA settings for form security.</p>
|
||||
|
||||
<v-btn
|
||||
color="warning"
|
||||
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>
|
||||
|
||||
<!-- NocoDB Configuration -->
|
||||
<v-row class="mb-6">
|
||||
<v-col cols="12">
|
||||
|
|
@ -165,6 +214,120 @@
|
|||
@settings-saved="handleAdminConfigSaved"
|
||||
/>
|
||||
|
||||
<!-- reCAPTCHA Configuration Dialog -->
|
||||
<v-dialog v-model="showRecaptchaConfig" max-width="600">
|
||||
<v-card>
|
||||
<v-card-title class="text-h5">
|
||||
<v-icon left>mdi-shield-account</v-icon>
|
||||
reCAPTCHA Configuration
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
<v-alert type="info" variant="tonal" class="mb-4">
|
||||
<v-alert-title>Security Configuration</v-alert-title>
|
||||
Configure Google reCAPTCHA settings for form protection on the registration page.
|
||||
</v-alert>
|
||||
|
||||
<v-form ref="recaptchaForm" v-model="recaptchaValid">
|
||||
<v-text-field
|
||||
v-model="recaptchaConfig.siteKey"
|
||||
label="Site Key"
|
||||
placeholder="6Lc6BAAAAAAAAChqRbQZcn_yyyyyyyyyyyyyyyyy"
|
||||
:rules="[v => !!v || 'Site key is required']"
|
||||
variant="outlined"
|
||||
required
|
||||
/>
|
||||
|
||||
<v-text-field
|
||||
v-model="recaptchaConfig.secretKey"
|
||||
label="Secret Key"
|
||||
placeholder="6Lc6BAAAAAAAAKN3DRm6VA_xxxxxxxxxxxxxxxxx"
|
||||
:rules="[v => !!v || 'Secret key is required']"
|
||||
variant="outlined"
|
||||
type="password"
|
||||
required
|
||||
/>
|
||||
|
||||
<v-alert type="warning" variant="tonal" class="mt-4">
|
||||
<v-alert-title>Important</v-alert-title>
|
||||
Keep your secret key confidential. You can get these keys from the Google reCAPTCHA admin console.
|
||||
</v-alert>
|
||||
</v-form>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-spacer />
|
||||
<v-btn text @click="showRecaptchaConfig = false">Cancel</v-btn>
|
||||
<v-btn
|
||||
color="primary"
|
||||
:loading="savingRecaptcha"
|
||||
:disabled="!recaptchaValid"
|
||||
@click="saveRecaptchaConfig"
|
||||
>
|
||||
Save Configuration
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
|
||||
<!-- Membership Configuration Dialog -->
|
||||
<v-dialog v-model="showMembershipConfig" max-width="600">
|
||||
<v-card>
|
||||
<v-card-title class="text-h5">
|
||||
<v-icon left>mdi-bank</v-icon>
|
||||
Membership Configuration
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
<v-alert type="info" variant="tonal" class="mb-4">
|
||||
<v-alert-title>Payment Configuration</v-alert-title>
|
||||
Configure membership fees and payment details displayed on the registration page.
|
||||
</v-alert>
|
||||
|
||||
<v-form ref="membershipForm" v-model="membershipValid">
|
||||
<v-text-field
|
||||
v-model="membershipConfig.membershipFee"
|
||||
label="Annual Membership Fee (€)"
|
||||
type="number"
|
||||
:rules="[
|
||||
v => !!v || 'Membership fee is required',
|
||||
v => v > 0 || 'Fee must be greater than 0'
|
||||
]"
|
||||
variant="outlined"
|
||||
required
|
||||
/>
|
||||
|
||||
<v-text-field
|
||||
v-model="membershipConfig.iban"
|
||||
label="IBAN"
|
||||
placeholder="DE89 3704 0044 0532 0130 00"
|
||||
:rules="[v => !!v || 'IBAN is required']"
|
||||
variant="outlined"
|
||||
required
|
||||
/>
|
||||
|
||||
<v-text-field
|
||||
v-model="membershipConfig.accountHolder"
|
||||
label="Account Holder Name"
|
||||
placeholder="MonacoUSA Association"
|
||||
:rules="[v => !!v || 'Account holder is required']"
|
||||
variant="outlined"
|
||||
required
|
||||
/>
|
||||
</v-form>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-spacer />
|
||||
<v-btn text @click="showMembershipConfig = false">Cancel</v-btn>
|
||||
<v-btn
|
||||
color="primary"
|
||||
:loading="savingMembership"
|
||||
:disabled="!membershipValid"
|
||||
@click="saveMembershipConfig"
|
||||
>
|
||||
Save Configuration
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
|
||||
<!-- Create User Dialog -->
|
||||
<v-dialog v-model="showCreateUserDialog" max-width="600">
|
||||
<v-card>
|
||||
|
|
@ -253,6 +416,8 @@ const userCount = ref(0);
|
|||
const loading = ref(false);
|
||||
const showCreateUserDialog = ref(false);
|
||||
const showAdminConfig = ref(false);
|
||||
const showRecaptchaConfig = ref(false);
|
||||
const showMembershipConfig = ref(false);
|
||||
|
||||
// Create user dialog data
|
||||
const createUserValid = ref(false);
|
||||
|
|
@ -270,6 +435,23 @@ const roleOptions = [
|
|||
{ title: 'Administrator', value: 'admin' }
|
||||
];
|
||||
|
||||
// reCAPTCHA configuration data
|
||||
const recaptchaValid = ref(false);
|
||||
const savingRecaptcha = ref(false);
|
||||
const recaptchaConfig = ref({
|
||||
siteKey: '',
|
||||
secretKey: ''
|
||||
});
|
||||
|
||||
// Membership configuration data
|
||||
const membershipValid = ref(false);
|
||||
const savingMembership = ref(false);
|
||||
const membershipConfig = ref({
|
||||
membershipFee: 50,
|
||||
iban: '',
|
||||
accountHolder: ''
|
||||
});
|
||||
|
||||
const recentActivity = ref([
|
||||
{
|
||||
id: 1,
|
||||
|
|
@ -349,6 +531,59 @@ const handleAdminConfigSaved = () => {
|
|||
showAdminConfig.value = false;
|
||||
};
|
||||
|
||||
const saveRecaptchaConfig = async () => {
|
||||
if (!recaptchaValid.value) return;
|
||||
|
||||
savingRecaptcha.value = true;
|
||||
try {
|
||||
const response = await $fetch('/api/admin/recaptcha-config', {
|
||||
method: 'POST',
|
||||
body: {
|
||||
siteKey: recaptchaConfig.value.siteKey,
|
||||
secretKey: recaptchaConfig.value.secretKey
|
||||
}
|
||||
}) as any;
|
||||
|
||||
if (response?.success) {
|
||||
showRecaptchaConfig.value = false;
|
||||
console.log('reCAPTCHA configuration saved successfully');
|
||||
// TODO: Show success notification
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to save reCAPTCHA configuration:', error);
|
||||
// TODO: Show error notification
|
||||
} finally {
|
||||
savingRecaptcha.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const saveMembershipConfig = async () => {
|
||||
if (!membershipValid.value) return;
|
||||
|
||||
savingMembership.value = true;
|
||||
try {
|
||||
const response = await $fetch('/api/admin/registration-config', {
|
||||
method: 'POST',
|
||||
body: {
|
||||
membershipFee: membershipConfig.value.membershipFee,
|
||||
iban: membershipConfig.value.iban,
|
||||
accountHolder: membershipConfig.value.accountHolder
|
||||
}
|
||||
}) as any;
|
||||
|
||||
if (response?.success) {
|
||||
showMembershipConfig.value = false;
|
||||
console.log('Membership configuration saved successfully');
|
||||
// TODO: Show success notification
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to save membership configuration:', error);
|
||||
// TODO: Show error notification
|
||||
} finally {
|
||||
savingMembership.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const createUserAccount = async () => {
|
||||
if (!createUserValid.value) return;
|
||||
|
||||
|
|
|
|||
|
|
@ -150,8 +150,11 @@
|
|||
@edit="editMember"
|
||||
@delete="confirmDeleteMember"
|
||||
@view="viewMember"
|
||||
@create-portal-account="createPortalAccount"
|
||||
:can-edit="canEditMembers"
|
||||
:can-delete="canDeleteMembers"
|
||||
:can-create-portal-account="canCreatePortalAccounts"
|
||||
:creating-portal-account="creatingPortalAccountIds.includes(member.Id)"
|
||||
/>
|
||||
</v-col>
|
||||
|
||||
|
|
@ -258,6 +261,7 @@ const { firstName, isBoard, isAdmin } = useAuth();
|
|||
const canCreateMembers = computed(() => isBoard.value || isAdmin.value);
|
||||
const canEditMembers = computed(() => isBoard.value || isAdmin.value);
|
||||
const canDeleteMembers = computed(() => isAdmin.value);
|
||||
const canCreatePortalAccounts = computed(() => isAdmin.value); // Only admins can create portal accounts
|
||||
|
||||
// Reactive data
|
||||
const members = ref<Member[]>([]);
|
||||
|
|
@ -282,6 +286,9 @@ const deleteLoading = ref(false);
|
|||
const showSuccess = ref(false);
|
||||
const successMessage = ref('');
|
||||
|
||||
// Portal account creation
|
||||
const creatingPortalAccountIds = ref<string[]>([]);
|
||||
|
||||
// Filter options
|
||||
const activeFilterOptions = [
|
||||
{ title: 'Active Members', value: 'active' },
|
||||
|
|
@ -493,6 +500,39 @@ const handleMemberUpdated = (updatedMember: Member) => {
|
|||
successMessage.value = `${updatedMember.FullName} has been updated successfully.`;
|
||||
};
|
||||
|
||||
const createPortalAccount = async (member: Member) => {
|
||||
if (!member.Id || creatingPortalAccountIds.value.includes(member.Id)) return;
|
||||
|
||||
// Add to creating array to show loading state
|
||||
creatingPortalAccountIds.value.push(member.Id);
|
||||
|
||||
try {
|
||||
const response = await $fetch(`/api/members/${member.Id}/create-portal-account`) as any;
|
||||
|
||||
if (response?.success) {
|
||||
// Update the member in the local array to reflect the new keycloak_id
|
||||
const index = members.value.findIndex(m => m.Id === member.Id);
|
||||
if (index !== -1) {
|
||||
members.value[index] = { ...members.value[index], keycloak_id: response.keycloak_id };
|
||||
}
|
||||
|
||||
showSuccess.value = true;
|
||||
successMessage.value = `Portal account created successfully for ${member.FullName}. They will receive an email with login instructions.`;
|
||||
} else {
|
||||
throw new Error(response?.message || 'Failed to create portal account');
|
||||
}
|
||||
} catch (err: any) {
|
||||
console.error('Error creating portal account:', err);
|
||||
error.value = err.data?.message || err.message || 'Failed to create portal account. Please try again.';
|
||||
} finally {
|
||||
// Remove from creating array
|
||||
const index = creatingPortalAccountIds.value.indexOf(member.Id);
|
||||
if (index > -1) {
|
||||
creatingPortalAccountIds.value.splice(index, 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Load members on mount
|
||||
onMounted(() => {
|
||||
loadMembers();
|
||||
|
|
|
|||
Loading…
Reference in New Issue