Enhance CreateStoreDialog component with improved layout and functionality
- Update form structure to allow for better scrolling and overflow handling. - Introduce collapsible section for creating new shipping zones, improving user experience. - Refactor error display and footer layout for clearer interaction during store creation. - Add scroll indicator for better navigation within the form. - Implement logic to check if content is scrollable, enhancing usability. These changes streamline the store creation process, providing a more intuitive and responsive interface for users.
This commit is contained in:
parent
68f1e527eb
commit
91c3dec064
1 changed files with 75 additions and 40 deletions
|
|
@ -1,13 +1,14 @@
|
|||
<template>
|
||||
<Dialog :open="isOpen" @update:open="(open) => !open && $emit('close')">
|
||||
<DialogContent class="sm:max-w-2xl">
|
||||
<DialogHeader>
|
||||
<DialogContent class="sm:max-w-2xl max-h-[90vh] overflow-hidden flex flex-col p-0">
|
||||
<DialogHeader class="flex-shrink-0 p-6 pb-0">
|
||||
<DialogTitle>Create New Store</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
<form @submit="onSubmit" class="space-y-6 py-4" autocomplete="off">
|
||||
<form @submit="onSubmit" class="flex flex-col flex-1 overflow-hidden" autocomplete="off">
|
||||
<div class="flex-1 overflow-y-auto space-y-4 p-6 pt-4">
|
||||
<!-- Basic Store Info -->
|
||||
<div class="space-y-4">
|
||||
<div class="space-y-3">
|
||||
<FormField v-slot="{ componentField }" name="name">
|
||||
<FormItem>
|
||||
<FormLabel>Store Name *</FormLabel>
|
||||
|
|
@ -20,7 +21,7 @@
|
|||
spellcheck="false"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormDescription>
|
||||
<FormDescription class="text-xs sm:text-sm">
|
||||
Choose a unique name for your store
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
|
|
@ -48,7 +49,7 @@
|
|||
<FormLabel>Currency *</FormLabel>
|
||||
<Select :disabled="isCreating" v-bind="componentField">
|
||||
<FormControl>
|
||||
<SelectTrigger>
|
||||
<SelectTrigger class="w-full">
|
||||
<SelectValue placeholder="Select currency" />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
|
|
@ -66,17 +67,19 @@
|
|||
<!-- Shipping Zones Section -->
|
||||
<FormField name="selectedZones">
|
||||
<FormItem>
|
||||
<div class="mb-4">
|
||||
<div class="mb-3">
|
||||
<FormLabel class="text-base">Shipping Zones *</FormLabel>
|
||||
<FormDescription>
|
||||
<FormDescription class="text-xs sm:text-sm">
|
||||
Select existing zones or create new ones for your store
|
||||
</FormDescription>
|
||||
</div>
|
||||
|
||||
<!-- Existing Zones -->
|
||||
<div v-if="availableZones.length > 0" class="space-y-3">
|
||||
<div v-if="availableZones.length > 0" class="space-y-2">
|
||||
<FormLabel class="text-sm font-medium">Available Zones:</FormLabel>
|
||||
<div class="space-y-2 max-h-32 overflow-y-auto">
|
||||
<div class="space-y-2 max-h-24 overflow-y-auto border rounded-md p-2 relative">
|
||||
<!-- Scroll indicator -->
|
||||
<div v-if="availableZones.length > 3" class="absolute bottom-0 left-0 right-0 h-4 bg-gradient-to-t from-background to-transparent pointer-events-none" />
|
||||
<FormField
|
||||
v-for="zone in availableZones"
|
||||
:key="zone.id"
|
||||
|
|
@ -94,9 +97,9 @@
|
|||
:disabled="isCreating"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormLabel class="text-sm cursor-pointer font-normal">
|
||||
{{ zone.name }} - {{ zone.cost }} {{ zone.currency }}
|
||||
<span class="text-muted-foreground ml-1">({{ zone.countries.slice(0, 2).join(', ') }}{{ zone.countries.length > 2 ? '...' : '' }})</span>
|
||||
<FormLabel class="text-xs sm:text-sm cursor-pointer font-normal">
|
||||
<span class="block">{{ zone.name }} - {{ zone.cost }} {{ zone.currency }}</span>
|
||||
<span class="text-muted-foreground text-xs">({{ zone.countries.slice(0, 2).join(', ') }}{{ zone.countries.length > 2 ? '...' : '' }})</span>
|
||||
</FormLabel>
|
||||
</FormItem>
|
||||
</FormField>
|
||||
|
|
@ -106,10 +109,19 @@
|
|||
</FormItem>
|
||||
</FormField>
|
||||
|
||||
<!-- Create New Zone -->
|
||||
<div class="border-t pt-4">
|
||||
<Label class="text-sm font-medium">Create New Zone:</Label>
|
||||
<div class="space-y-3 mt-2">
|
||||
<!-- Create New Zone (Collapsible) -->
|
||||
<div class="border-t pt-3 mb-2">
|
||||
<button
|
||||
type="button"
|
||||
@click="showNewZoneForm = !showNewZoneForm"
|
||||
class="flex items-center justify-between w-full text-sm font-medium py-2 hover:text-primary transition-colors"
|
||||
>
|
||||
<span>Create New Zone</span>
|
||||
<ChevronDown
|
||||
:class="['w-4 h-4 transition-transform', showNewZoneForm ? 'rotate-180' : '']"
|
||||
/>
|
||||
</button>
|
||||
<div v-show="showNewZoneForm" class="space-y-3 mt-2">
|
||||
<FormField v-slot="{ componentField }" name="newZone.name">
|
||||
<FormItem>
|
||||
<FormLabel>Zone Name</FormLabel>
|
||||
|
|
@ -156,7 +168,7 @@
|
|||
</Select>
|
||||
</FormControl>
|
||||
<div v-if="values.newZone?.selectedCountries && values.newZone.selectedCountries.length > 0" class="mt-2">
|
||||
<div class="flex flex-wrap gap-1">
|
||||
<div class="flex flex-wrap gap-1 max-h-20 overflow-y-auto">
|
||||
<Badge
|
||||
v-for="country in (values.newZone?.selectedCountries || [])"
|
||||
:key="country"
|
||||
|
|
@ -183,35 +195,43 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Error Display -->
|
||||
<div v-if="createError" class="text-sm text-destructive">
|
||||
{{ createError }}
|
||||
<!-- Error Display -->
|
||||
<div v-if="createError" class="text-sm text-destructive">
|
||||
{{ createError }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end space-x-2 pt-4">
|
||||
<Button
|
||||
type="button"
|
||||
@click="$emit('close')"
|
||||
variant="outline"
|
||||
:disabled="isCreating"
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
type="submit"
|
||||
:disabled="isCreating || !isFormValid"
|
||||
>
|
||||
<span v-if="isCreating">Creating...</span>
|
||||
<span v-else>Create Store</span>
|
||||
</Button>
|
||||
<!-- Footer inside form for submit to work -->
|
||||
<div class="flex-shrink-0 border-t bg-background p-6 pt-4">
|
||||
<div class="flex justify-end space-x-2">
|
||||
<Button
|
||||
type="button"
|
||||
@click="$emit('close')"
|
||||
variant="outline"
|
||||
:disabled="isCreating"
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
type="submit"
|
||||
:disabled="isCreating || !isFormValid"
|
||||
>
|
||||
<span v-if="isCreating">Creating...</span>
|
||||
<span v-else>Create Store</span>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<!-- Scroll indicator for main form -->
|
||||
<div v-if="showScrollIndicator" class="absolute bottom-20 left-0 right-0 flex justify-center pointer-events-none animate-bounce">
|
||||
<ChevronDown class="w-5 h-5 text-muted-foreground" />
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, watch, nextTick } from 'vue'
|
||||
import { ref, computed, watch, nextTick, onMounted } from 'vue'
|
||||
import { useForm } from 'vee-validate'
|
||||
import { toTypedSchema } from '@vee-validate/zod'
|
||||
import * as z from 'zod'
|
||||
|
|
@ -231,7 +251,7 @@ import {
|
|||
FormLabel,
|
||||
FormMessage,
|
||||
} from '@/components/ui/form'
|
||||
import { Plus } from 'lucide-vue-next'
|
||||
import { Plus, ChevronDown } from 'lucide-vue-next'
|
||||
import type { NostrmarketAPI, Zone, CreateStallRequest } from '../services/nostrmarketAPI'
|
||||
import { auth } from '@/composables/useAuthService'
|
||||
import { useToast } from '@/core/composables/useToast'
|
||||
|
|
@ -257,6 +277,8 @@ const isCreating = ref(false)
|
|||
const createError = ref<string | null>(null)
|
||||
const availableCurrencies = ref<string[]>(['sat'])
|
||||
const availableZones = ref<Zone[]>([])
|
||||
const showNewZoneForm = ref(false)
|
||||
const showScrollIndicator = ref(false)
|
||||
const countries = ref([
|
||||
'Free (digital)', 'Worldwide', 'Europe', 'Australia', 'Austria', 'Belgium', 'Brazil', 'Canada',
|
||||
'Denmark', 'Finland', 'France', 'Germany', 'Greece', 'Hong Kong', 'Hungary',
|
||||
|
|
@ -431,6 +453,16 @@ const createStore = async (formData: any) => {
|
|||
}
|
||||
}
|
||||
|
||||
// Check if content is scrollable
|
||||
const checkScrollable = () => {
|
||||
nextTick(() => {
|
||||
const content = document.querySelector('.overflow-y-auto')
|
||||
if (content) {
|
||||
showScrollIndicator.value = content.scrollHeight > content.clientHeight
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Initialize data when dialog opens
|
||||
watch(() => props.isOpen, async (isOpen) => {
|
||||
if (isOpen) {
|
||||
|
|
@ -466,6 +498,9 @@ watch(() => props.isOpen, async (isOpen) => {
|
|||
loadAvailableCurrencies(),
|
||||
loadAvailableZones()
|
||||
])
|
||||
|
||||
// Check if scrollable after loading
|
||||
checkScrollable()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
Loading…
Add table
Add a link
Reference in a new issue