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