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-col>
|
||||||
</v-row>
|
</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 -->
|
<!-- NocoDB Configuration -->
|
||||||
<v-row class="mb-6">
|
<v-row class="mb-6">
|
||||||
<v-col cols="12">
|
<v-col cols="12">
|
||||||
|
|
@ -165,6 +214,120 @@
|
||||||
@settings-saved="handleAdminConfigSaved"
|
@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 -->
|
<!-- Create User Dialog -->
|
||||||
<v-dialog v-model="showCreateUserDialog" max-width="600">
|
<v-dialog v-model="showCreateUserDialog" max-width="600">
|
||||||
<v-card>
|
<v-card>
|
||||||
|
|
@ -253,6 +416,8 @@ const userCount = ref(0);
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const showCreateUserDialog = ref(false);
|
const showCreateUserDialog = ref(false);
|
||||||
const showAdminConfig = ref(false);
|
const showAdminConfig = ref(false);
|
||||||
|
const showRecaptchaConfig = ref(false);
|
||||||
|
const showMembershipConfig = ref(false);
|
||||||
|
|
||||||
// Create user dialog data
|
// Create user dialog data
|
||||||
const createUserValid = ref(false);
|
const createUserValid = ref(false);
|
||||||
|
|
@ -270,6 +435,23 @@ const roleOptions = [
|
||||||
{ title: 'Administrator', value: 'admin' }
|
{ 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([
|
const recentActivity = ref([
|
||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
|
|
@ -349,6 +531,59 @@ const handleAdminConfigSaved = () => {
|
||||||
showAdminConfig.value = false;
|
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 () => {
|
const createUserAccount = async () => {
|
||||||
if (!createUserValid.value) return;
|
if (!createUserValid.value) return;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -150,8 +150,11 @@
|
||||||
@edit="editMember"
|
@edit="editMember"
|
||||||
@delete="confirmDeleteMember"
|
@delete="confirmDeleteMember"
|
||||||
@view="viewMember"
|
@view="viewMember"
|
||||||
|
@create-portal-account="createPortalAccount"
|
||||||
:can-edit="canEditMembers"
|
:can-edit="canEditMembers"
|
||||||
:can-delete="canDeleteMembers"
|
:can-delete="canDeleteMembers"
|
||||||
|
:can-create-portal-account="canCreatePortalAccounts"
|
||||||
|
:creating-portal-account="creatingPortalAccountIds.includes(member.Id)"
|
||||||
/>
|
/>
|
||||||
</v-col>
|
</v-col>
|
||||||
|
|
||||||
|
|
@ -258,6 +261,7 @@ const { firstName, isBoard, isAdmin } = useAuth();
|
||||||
const canCreateMembers = computed(() => isBoard.value || isAdmin.value);
|
const canCreateMembers = computed(() => isBoard.value || isAdmin.value);
|
||||||
const canEditMembers = computed(() => isBoard.value || isAdmin.value);
|
const canEditMembers = computed(() => isBoard.value || isAdmin.value);
|
||||||
const canDeleteMembers = computed(() => isAdmin.value);
|
const canDeleteMembers = computed(() => isAdmin.value);
|
||||||
|
const canCreatePortalAccounts = computed(() => isAdmin.value); // Only admins can create portal accounts
|
||||||
|
|
||||||
// Reactive data
|
// Reactive data
|
||||||
const members = ref<Member[]>([]);
|
const members = ref<Member[]>([]);
|
||||||
|
|
@ -282,6 +286,9 @@ const deleteLoading = ref(false);
|
||||||
const showSuccess = ref(false);
|
const showSuccess = ref(false);
|
||||||
const successMessage = ref('');
|
const successMessage = ref('');
|
||||||
|
|
||||||
|
// Portal account creation
|
||||||
|
const creatingPortalAccountIds = ref<string[]>([]);
|
||||||
|
|
||||||
// Filter options
|
// Filter options
|
||||||
const activeFilterOptions = [
|
const activeFilterOptions = [
|
||||||
{ title: 'Active Members', value: 'active' },
|
{ title: 'Active Members', value: 'active' },
|
||||||
|
|
@ -493,6 +500,39 @@ const handleMemberUpdated = (updatedMember: Member) => {
|
||||||
successMessage.value = `${updatedMember.FullName} has been updated successfully.`;
|
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
|
// Load members on mount
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
loadMembers();
|
loadMembers();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue