Enhance MerchantStore component with improved form handling and dialog management

- Introduce keys for reactivity in the stall creation dialog and form elements to ensure proper rendering and state management.
- Implement autocomplete and spellcheck attributes for input fields to enhance user experience during store creation.
- Refactor form initialization and reset logic to ensure a clean state upon dialog opening and closing, improving usability.
- Add validation triggers after loading data to ensure form integrity and user feedback during the stall creation process.

These changes streamline the store creation experience, providing a more responsive and user-friendly interface for merchants.
This commit is contained in:
padreug 2025-09-08 18:44:36 +02:00
parent 6f68c2320e
commit c3e599b3e4

View file

@ -449,13 +449,13 @@
</div> <!-- End of main container -->
<!-- Create Stall Dialog -->
<Dialog v-model:open="showStallDialog">
<Dialog v-model:open="showStallDialog" :key="dialogKey">
<DialogContent class="sm:max-w-2xl">
<DialogHeader>
<DialogTitle>Create New Store</DialogTitle>
</DialogHeader>
<form @submit="onSubmit" class="space-y-6 py-4">
<form @submit="onSubmit" class="space-y-6 py-4" :key="formKey" autocomplete="off">
<!-- Basic Store Info -->
<div class="space-y-4">
<FormField v-slot="{ componentField }" name="name">
@ -466,6 +466,9 @@
placeholder="Enter your store name"
:disabled="isCreatingStall"
v-bind="componentField"
:key="`store-name-${formKey}`"
autocomplete="off"
spellcheck="false"
/>
</FormControl>
<FormDescription>
@ -483,6 +486,9 @@
placeholder="Describe your store and products"
:disabled="isCreatingStall"
v-bind="componentField"
:key="`store-description-${formKey}`"
autocomplete="off"
spellcheck="false"
/>
</FormControl>
<FormMessage />
@ -564,6 +570,9 @@
placeholder="e.g., Europe, Worldwide"
:disabled="isCreatingStall"
v-bind="componentField"
:key="`zone-name-${formKey}`"
autocomplete="off"
spellcheck="false"
/>
</FormControl>
</FormItem>
@ -578,6 +587,7 @@
min="0"
:placeholder="`Cost in ${getFieldValue('currency') || 'sat'}`"
:disabled="isCreatingStall"
:key="`zone-cost-${formKey}`"
v-bind="componentField"
/>
</FormControl>
@ -634,7 +644,7 @@
<div class="flex justify-end space-x-2 pt-4">
<Button
@click="showStallDialog = false"
@click="closeStallDialog"
variant="outline"
:disabled="isCreatingStall"
>
@ -654,7 +664,7 @@
</template>
<script setup lang="ts">
import { ref, computed, onMounted, watch } from 'vue'
import { ref, computed, onMounted, watch, nextTick } from 'vue'
import { useRouter } from 'vue-router'
import { useForm } from 'vee-validate'
import { toTypedSchema } from '@vee-validate/zod'
@ -724,7 +734,9 @@ const activeStall = computed(() =>
// Stall creation state
const isCreatingStall = ref(false)
const stallCreateError = ref<string | null>(null)
const formKey = ref(0) // Force re-render of form
const showStallDialog = ref(false)
const dialogKey = ref(0) // Force complete dialog re-render
const availableCurrencies = ref<string[]>(['sat'])
const availableZones = ref<Zone[]>([])
const countries = ref([
@ -769,19 +781,20 @@ const form = useForm({
})
// Destructure form methods for easier access
const { setFieldValue, resetForm, values, meta } = form
const { setFieldValue, resetForm, values, meta, validate } = form
// Helper function to get field values safely
const getFieldValue = (fieldName: string) => {
return fieldName.split('.').reduce((obj, key) => obj?.[key], values)
}
// Form validation computed
const isFormValid = computed(() => meta.value.valid)
// Form validation computed with detailed debugging
const isFormValid = computed(() => {
return meta.value.valid
})
// Form submit handler
const onSubmit = form.handleSubmit(async (values) => {
console.log('Form submitted with values:', values)
await createStall(values)
})
@ -1076,8 +1089,45 @@ const initializeStallCreation = async () => {
return
}
// Set default wallet
setFieldValue('wallet', currentUser.wallets[0].id)
// Reset form to initial state first
resetForm({
values: {
name: '',
description: '',
currency: 'sat',
wallet: currentUser.wallets[0].id,
selectedZones: [],
newZone: {
name: '',
cost: 0,
selectedCountries: []
}
},
errors: {},
touched: {},
initialValues: {
name: '',
description: '',
currency: 'sat',
wallet: currentUser.wallets[0].id,
selectedZones: [],
newZone: {
name: '',
cost: 0,
selectedCountries: []
}
}
})
// Wait for Vue's reactivity to update
await nextTick()
// Force complete dialog re-render
dialogKey.value++
formKey.value++
// Clear any previous errors
stallCreateError.value = null
// Load currencies and zones
await Promise.all([
@ -1085,6 +1135,9 @@ const initializeStallCreation = async () => {
loadAvailableZones()
])
// Trigger validation after data is loaded to ensure form state is correct
await validate()
showStallDialog.value = true
}
@ -1105,6 +1158,14 @@ const loadAvailableZones = async () => {
try {
const zones = await nostrmarketAPI.getZones(currentUser.wallets[0].inkey)
availableZones.value = zones
// Auto-select the first available zone to make form valid
if (zones.length > 0) {
const currentSelectedZones = getFieldValue('selectedZones') || []
if (currentSelectedZones.length === 0) {
setFieldValue('selectedZones', [zones[0].id])
}
}
} catch (error) {
console.error('Failed to load zones:', error)
}
@ -1112,27 +1173,19 @@ const loadAvailableZones = async () => {
const loadStallsList = async () => {
const currentUser = auth.currentUser?.value
console.log('Loading stalls list - currentUser:', !!currentUser, 'wallets:', currentUser?.wallets?.length)
if (!currentUser?.wallets?.length) {
console.log('No user or wallets available, skipping stalls loading')
return
}
isLoadingStalls.value = true
try {
console.log('Calling getStalls with inkey:', currentUser.wallets[0].inkey.substring(0, 8) + '...')
const stalls = await nostrmarketAPI.getStalls(currentUser.wallets[0].inkey)
userStalls.value = stalls || []
console.log('Loaded user stalls:', {
stallsCount: stalls?.length || 0,
stalls: stalls?.map(s => ({ id: s.id, name: s.name }))
})
// If there are stalls but no active one selected, select the first
if (stalls?.length > 0 && !activeStallId.value) {
activeStallId.value = stalls[0].id
console.log('Set active stall ID to:', stalls[0].id)
}
} catch (error) {
console.error('Failed to load stalls:', error)
@ -1215,26 +1268,15 @@ const createStall = async (formData: any) => {
}
}
console.log('Creating stall:', {
name,
currency,
zonesCount: selectedZoneData.length
})
const newStall = await nostrmarketAPI.createStall(
currentUser.wallets[0].adminkey,
stallData
)
console.log('Stall created successfully:', {
stallId: newStall.id,
stallName: newStall.name
})
// Update stalls list
await loadStallsList()
// Reset form and close dialog
// Reset form and close dialog using standard Shadcn pattern
resetForm({
values: {
name: '',
@ -1247,8 +1289,27 @@ const createStall = async (formData: any) => {
cost: 0,
selectedCountries: []
}
},
errors: {},
touched: {},
initialValues: {
name: '',
description: '',
currency: 'sat',
wallet: currentUser.wallets[0].id,
selectedZones: [],
newZone: {
name: '',
cost: 0,
selectedCountries: []
}
}
})
// Force complete dialog re-render
dialogKey.value++
formKey.value++
showStallDialog.value = false
toast.success('Store created successfully! You can now add products.')
@ -1277,6 +1338,12 @@ const viewStallProducts = (stallId: string) => {
activeStallId.value = stallId
}
const closeStallDialog = () => {
// Reset form and clear errors when closing the dialog
stallCreateError.value = null
showStallDialog.value = false
}
const navigateToMarket = () => router.push('/market')
const viewAllOrders = () => router.push('/market-dashboard?tab=orders')
const generateBulkInvoices = () => console.log('Generate bulk invoices')