fix: Selection not working in country autocomplete
Some checks failed
Build / build (pull_request) Successful in 39s
Check formatting / check-formatting (pull_request) Failing after 16s

This commit is contained in:
Magnus Leßmann (MarkL4YG) 2025-07-12 15:16:04 +02:00
parent ed948d642a
commit 0e60183372
Signed by: Mark.TwoFive
GPG key ID: 5B5EBCBE331F1E6F
2 changed files with 39 additions and 36 deletions

View file

@ -1,7 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, useId, useTemplateRef } from "vue"; import { computed, ref, useId, useTemplateRef } from "vue";
import CountryListItem from "./CountryListItem.vue"; import CountryListItem from "./CountryListItem.vue";
import { type Country, getAvailableCountries } from "../utils/countries.ts"; import { type Country, getAvailableCountries, userCountry } from "../utils/countries.ts";
const formModel = ref(false); const formModel = ref(false);
const formRef = useTemplateRef("form"); const formRef = useTemplateRef("form");
@ -16,7 +16,7 @@ const commonName = ref("");
const emailAddress = ref(""); const emailAddress = ref("");
const organization = ref(""); const organization = ref("");
const organizationalUnit = ref(""); const organizationalUnit = ref("");
const country = ref(""); const country = ref(userCountry?.alpha2);
const countryName = ref(""); const countryName = ref("");
const state = ref(""); const state = ref("");
const locality = ref(""); const locality = ref("");
@ -32,25 +32,25 @@ const noExpiry = ref(false);
// Validation rules // Validation rules
const domainRules = [ const domainRules = [
(value: string) => !!value || "A value must be provided to add the domain.", (value: string) => !!value || "A value must be provided to add the domain.",
(value: string) => /^[a-z0-9][a-z0-9.\-_]+$|^xn--[a-z0-9.\-_]+$/i.test(value) || "Invalid domain characters provided. (To use some special characters, convert the domain to xn-domain format.)", (value: string) => /^[a-z0-9][a-z0-9.\-_]+$|^xn--[a-z0-9.\-_]+$/i.test(value) || "Invalid domain characters provided. (To use some special characters, convert the domain to xn-domain format.)"
] ];
const emailRules = [ const emailRules = [
(value: string) => !value || /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(value) || "Invalid email format.", (value: string) => !value || /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(value) || "Invalid email format."
]; ];
const keyLengthRules = [ const keyLengthRules = [
(value: number) => value >= 3000 || "Key length must be at least 3000 bits.", (value: number) => value >= 3000 || "Key length must be at least 3000 bits.",
(value: number) => value <= 6000 || "Key length must not exceed 6000 bits.", (value: number) => value <= 6000 || "Key length must not exceed 6000 bits."
]; ];
const validityDaysRules = [ const validityDaysRules = [
(value: number) => !value || value > 0 || "Validity period (in days) must be a positive integer.", (value: number) => !value || value > 0 || "Validity period (in days) must be a positive integer."
]; ];
function addDomain() { function addDomain() {
if (formRef.value?.errors.some(({id}) => domainInputId === id)) { if (formRef.value?.errors.some(({ id }) => domainInputId === id)) {
return; return;
} }
@ -64,6 +64,7 @@ function addDomain() {
} }
const isSubmitting = ref(false); const isSubmitting = ref(false);
async function submitNewCertificate() { async function submitNewCertificate() {
if (!formRef.value?.validate()) { if (!formRef.value?.validate()) {
return; return;
@ -72,7 +73,7 @@ async function submitNewCertificate() {
isSubmitting.value = true; isSubmitting.value = true;
try { try {
// Extract country code if country is an object // Extract country code if country is an object
const countryCode = typeof country.value === 'object' && country.value !== null const countryCode = typeof country.value === "object" && country.value !== null
? (country.value as Country).alpha2 ? (country.value as Country).alpha2
: country.value; : country.value;
@ -88,10 +89,10 @@ async function submitNewCertificate() {
organizationalUnit: organizationalUnit.value, organizationalUnit: organizationalUnit.value,
country: countryCode, country: countryCode,
state: state.value, state: state.value,
locality: locality.value, locality: locality.value
}, },
extension: { extension: {
alternativeDnsNames: domains.value, alternativeDnsNames: domains.value
} }
}); });
} finally { } finally {
@ -144,8 +145,11 @@ async function submitNewCertificate() {
hint="Search and select your country" hint="Search and select your country"
class="mb-2" class="mb-2"
> >
<template #item="slotProps"> <template #item="{ props, item }">
<CountryListItem :item="slotProps.item" :props="slotProps.props" /> <CountryListItem
:item="item"
:item-props="props"
/>
</template> </template>
</v-autocomplete> </v-autocomplete>

View file

@ -1,45 +1,44 @@
<script setup lang="ts"> <script setup lang="ts">
// Define props with proper TypeScript typing // Define props with proper TypeScript typing
import { computed } from "vue"; import { computed } from "vue";
import type { CountryListItemType } from "@/utils/countries.ts";
interface Country { type ListItem<T> = {
country: string; raw: T;
alpha2: string;
alpha3: string;
numeric: string;
} }
interface Divider {
divider: boolean;
header: string;
}
// Define a type that can be either a Country or a Divider
type CountryListItemType = Country | Divider;
// Define props with proper TypeScript typing // Define props with proper TypeScript typing
const props = defineProps<{ const props = defineProps<{
item: { raw: CountryListItemType }; item: ListItem<CountryListItemType>;
props?: Record<string, unknown>; itemProps?: Record<string, unknown>;
}>(); }>();
// Helper functions to check the type of the item // Helper functions to check the type of the item
const isDivider = computed(() => { const isDivider = computed(() => {
return 'divider' in props.item.raw && props.item.raw.divider; return "divider" in props.item.raw && props.item.raw.divider;
}); });
const headerText = computed(() => { const headerText = computed(() => {
return 'header' in props.item.raw ? props.item.raw.header : ""; return "header" in props.item.raw ? props.item.raw.header : "";
}); });
const countryItem = computed(() => { const countryItem = computed(() => {
return !isDivider.value && !headerText.value && 'country' in props.item.raw ? props.item.raw: undefined; return !isDivider.value && !headerText.value && "country" in props.item.raw ? props.item.raw : undefined;
}) });
</script> </script>
<template> <template>
<v-divider v-if="isDivider" class="my-2" /> <v-divider
<v-list-subheader v-if="headerText">{{ headerText }}</v-list-subheader> v-if="isDivider"
<v-list-item v-if="countryItem" v-bind="props" :title="countryItem.country" :value="countryItem.alpha2" /> class="my-2"
/>
<v-list-subheader v-if="headerText">
{{ headerText }}
</v-list-subheader>
<v-list-item
v-if="countryItem"
v-bind="itemProps"
:title="countryItem.country"
/>
</template> </template>