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:
padreug 2025-09-08 23:31:44 +02:00
parent 68f1e527eb
commit 91c3dec064

View file

@ -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"
@ -187,8 +199,11 @@
<div v-if="createError" class="text-sm text-destructive">
{{ createError }}
</div>
</div>
<div class="flex justify-end space-x-2 pt-4">
<!-- 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')"
@ -205,13 +220,18 @@
<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>