# Advanced Patterns

Complex patterns for sophisticated application features.

## Table of Contents

- [Multi-Step Wizard](#multi-step-wizard)
- [Nested Forms](#nested-forms)
- [Optimistic Updates](#optimistic-updates)
- [Real-Time Data](#real-time-data)
- [File Upload with Progress](#file-upload-with-progress)
- [Infinite Scroll](#infinite-scroll)
- [Complex Filtering & URL State](#complex-filtering--url-state)
- [Permission-Based UI](#permission-based-ui)

## Multi-Step Wizard

Guide users through multi-step processes with state management.

### Complete Wizard Example

```markup
<template>
    <AwPageSingle
        hide-menu
        title="New Campaign Setup"
        :action="primaryAction"
        :progress="stepProgress"
        @action="handleAction"
    >
        <template #buttons>
            <AwButton
                v-if="currentStep > 1"
                @click="previousStep"
                text="Back"
            />
        </template>

        <!-- Step 1: Basic Info -->
        <AwCard v-show="currentStep === 1" title="Campaign Details">
            <AwInput
                v-model="campaign.name"
                :error="errors.name"
                label="Campaign Name"
                required
            />

            <AwTextarea
                v-model="campaign.description"
                :error="errors.description"
                label="Description"
                rows="4"
            />

            <AwSelect
                v-model="campaign.type"
                :error="errors.type"
                :options="campaignTypes"
                label="Campaign Type"
                required
            />
        </AwCard>

        <!-- Step 2: Audience -->
        <AwCard v-show="currentStep === 2" title="Target Audience">
            <AwSelect
                v-model="campaign.audience_type"
                :error="errors.audience_type"
                :options="['all_customers', 'segment', 'custom']"
                label="Audience Type"
                required
            />

            <AwSelect
                v-if="campaign.audience_type === 'segment'"
                v-model="campaign.segment_id"
                :error="errors.segment_id"
                :options="loadSegments"
                option-label="name"
                track-by="id"
                label="Segment"
            />

            <div v-if="campaign.audience_type === 'custom'">
                <AwDescription>Select specific customers</AwDescription>
                <CustomersSelector v-model="campaign.customer_ids" />
            </div>
        </AwCard>

        <!-- Step 3: Content -->
        <AwCard v-show="currentStep === 3" title="Campaign Content">
            <AwSelect
                v-model="campaign.template_key"
                :error="errors.template_key"
                :options="loadTemplates"
                :option-label="formatTemplateLabel"
                track-by="template_key"
                label="Message Template"
                required
            />

            <AwMarkdownEditor
                v-model="campaign.content"
                :error="errors.content"
                label="Message Content"
            />

            <AwCard title="Preview" class="mt-4">
                <div v-html="compiledPreview" />
            </AwCard>
        </AwCard>

        <!-- Step 4: Schedule -->
        <AwCard v-show="currentStep === 4" title="Schedule Campaign">
            <AwSwitcher
                v-model="campaign.send_immediately"
                label="Send Immediately"
            />

            <div v-if="!campaign.send_immediately">
                <AwDate
                    v-model="campaign.scheduled_date"
                    :error="errors.scheduled_date"
                    label="Schedule Date"
                    :min="minScheduleDate"
                />

                <AwInput
                    v-model="campaign.scheduled_time"
                    :error="errors.scheduled_time"
                    type="time"
                    label="Schedule Time"
                />
            </div>
        </AwCard>

        <!-- Step 5: Review -->
        <AwCard v-show="currentStep === 5" title="Review & Confirm">
            <AwGrid :col="2">
                <div>
                    <AwDescription>Campaign Name</AwDescription>
                    <p class="font-medium">{{ campaign.name }}</p>
                </div>

                <div>
                    <AwDescription>Type</AwDescription>
                    <p class="font-medium">{{ campaign.type }}</p>
                </div>

                <div>
                    <AwDescription>Audience</AwDescription>
                    <p class="font-medium">{{ audienceDescription }}</p>
                </div>

                <div>
                    <AwDescription>Schedule</AwDescription>
                    <p class="font-medium">{{ scheduleDescription }}</p>
                </div>
            </AwGrid>

            <AwAlert type="info" class="mt-6">
                Please review all details before creating the campaign.
            </AwAlert>
        </AwCard>
    </AwPageSingle>
</template>

<script>
export default {
    data() {
        return {
            currentStep: 1,
            totalSteps: 5,
            stepTitles: ['Details', 'Audience', 'Content', 'Schedule', 'Review'],
            campaign: {
                name: '',
                description: '',
                type: null,
                audience_type: 'all_customers',
                segment_id: null,
                customer_ids: [],
                template_key: null,
                content: '',
                send_immediately: true,
                scheduled_date: null,
                scheduled_time: null
            },
            errors: {},
            saving: false,
            campaignTypes: ['email', 'sms', 'push_notification']
        }
    },

    computed: {
        primaryAction() {
            // Primary action changes based on step
            if (this.currentStep < this.totalSteps) {
                return {
                    key: 'next',
                    label: 'Next',
                    disabled: !this.isStepValid,
                    color: 'accent'
                }
            }

            return {
                key: 'submit',
                label: 'Create Campaign',
                loading: this.saving,
                color: 'accent'
            }
        },

        stepProgress() {
            return Math.round((this.currentStep / this.totalSteps) * 100)
        },

        isStepValid() {
            switch (this.currentStep) {
                case 1:
                    return this.campaign.name && this.campaign.type
                case 2:
                    if (this.campaign.audience_type === 'segment') {
                        return !!this.campaign.segment_id
                    }
                    if (this.campaign.audience_type === 'custom') {
                        return this.campaign.customer_ids.length > 0
                    }
                    return true
                case 3:
                    return this.campaign.template_key && this.campaign.content
                case 4:
                    if (this.campaign.send_immediately) {
                        return true
                    }
                    return this.campaign.scheduled_date && this.campaign.scheduled_time
                case 5:
                    return true
                default:
                    return false
            }
        },

        audienceDescription() {
            if (this.campaign.audience_type === 'all_customers') {
                return 'All Customers'
            }
            if (this.campaign.audience_type === 'segment') {
                return `Segment: ${this.campaign.segment_id}`
            }
            return `${this.campaign.customer_ids.length} selected customers`
        },

        scheduleDescription() {
            if (this.campaign.send_immediately) {
                return 'Send Immediately'
            }
            return `${this.$dayjs(this.campaign.scheduled_date).format('ll')} at ${this.campaign.scheduled_time}`
        },

        compiledPreview() {
            // Compile template with sample data
            return this.campaign.content
        },

        minScheduleDate() {
            return this.$dayjs().format('YYYY-MM-DD')
        }
    },

    methods: {
        handleAction(action) {
            if (action.key === 'next') {
                this.nextStep()
            } else if (action.key === 'submit') {
                this.submit()
            }
        },

        nextStep() {
            if (this.isStepValid && this.currentStep < this.totalSteps) {
                this.currentStep++
            }
        },

        previousStep() {
            if (this.currentStep > 1) {
                this.currentStep--
            }
        },

        async submit() {
            this.saving = true

            try {
                await this.$axios.post('/api/campaigns', {
                    shop_uuid: this.$route.params.shop_uuid,
                    ...this.campaign
                })

                this.$notify({
                    message: 'Campaign created successfully',
                    type: 'success'
                })

                this.$router.push(`/shops/${this.$route.params.shop_uuid}/campaigns`)
            } catch (error) {
                if (error.response?.status === 422) {
                    this.errors = error.response.data.errors
                    this.$notify({
                        message: 'Please fix validation errors',
                        type: 'error'
                    })
                } else {
                    this.$notify({
                        message: 'Failed to create campaign',
                        type: 'error'
                    })
                }
            } finally {
                this.saving = false
            }
        },

        loadSegments(search) {
            return `/api/shops/${this.$route.params.shop_uuid}/segments?search=${search}`
        },

        loadTemplates(search) {
            return `/api/shops/${this.$route.params.shop_uuid}/templates?search=${search}&type=${this.campaign.type}`
        },

        formatTemplateLabel(template) {
            return `${template.name} (${template.channel})`
        }
    }
}
</script>
```

## Nested Forms

Manage parent-child relationships with dynamic fields.

### Order with Line Items

```markup
<template>
    <AwPageSingle
        hide-menu
        :title="order.isNew() ? 'New Order' : 'Edit Order'"
        :action="{
            key: 'save',
            label: 'Save Order',
            loading: saving,
            color: 'accent'
        }"
        @action="handleAction"
    >

        <!-- Customer Selection -->
        <AwCard title="Customer">
            <AwSelect
                v-model="order.customer_id"
                :error="order.errors.customer_id"
                :options="loadCustomers"
                option-label="name"
                track-by="id"
                label="Customer"
                required
            />
        </AwCard>

        <!-- Line Items -->
        <AwCard title="Order Items" class="mt-6">
            <div
                v-for="(item, index) in order.items"
                :key="item.uuid"
                class="mb-4 p-4 border rounded"
            >
                <AwFlow justify="between" align="start">
                    <AwGrid :col="3" class="flex-1">
                        <AwSelect
                            v-model="item.product_id"
                            :error="getItemError(index, 'product_id')"
                            :options="loadProducts"
                            option-label="name"
                            track-by="id"
                            label="Product"
                            @input="updateItemPrice(index)"
                        />

                        <AwInput
                            v-model.number="item.quantity"
                            :error="getItemError(index, 'quantity')"
                            type="number"
                            label="Quantity"
                            min="1"
                            @input="updateItemTotal(index)"
                        />

                        <AwInput
                            v-model.number="item.price"
                            :error="getItemError(index, 'price')"
                            type="number"
                            label="Price"
                            step="0.01"
                            @input="updateItemTotal(index)"
                        />
                    </AwGrid>

                    <AwButton
                        @click="removeItem(index)"
                        color="error"
                        size="sm"
                    >
                        Remove
                    </AwButton>
                </AwFlow>

                <div class="mt-2 text-right">
                    <strong>Total: ${{ item.total.toFixed(2) }}</strong>
                </div>
            </div>

            <AwButton @click="addItem" icon="plus">
                Add Item
            </AwButton>
        </AwCard>

        <!-- Order Summary -->
        <AwCard title="Order Summary" class="mt-6">
            <AwGrid :col="2">
                <div>
                    <AwDescription>Subtotal</AwDescription>
                    <p class="text-xl font-medium">${{ subtotal.toFixed(2) }}</p>
                </div>

                <div>
                    <AwDescription>Tax ({{ taxRate }}%)</AwDescription>
                    <p class="text-xl font-medium">${{ tax.toFixed(2) }}</p>
                </div>

                <div class="col-span-2">
                    <AwDescription>Total</AwDescription>
                    <p class="text-3xl font-bold">${{ total.toFixed(2) }}</p>
                </div>
            </AwGrid>
        </AwCard>
    </AwPageSingle>
</template>

<script>
import { v4 as uuidv4 } from 'uuid'

export default {
    data() {
        return {
            order: {
                customer_id: null,
                items: [],
                errors: {
                    get(field) {
                        return this[field]
                    },
                    isEmpty() {
                        return Object.keys(this).length === 1 // Only 'get' method
                    }
                }
            },
            itemErrors: [],
            saving: false,
            taxRate: 10
        }
    },

    computed: {
        subtotal() {
            return this.order.items.reduce((sum, item) => sum + (item.total || 0), 0)
        },

        tax() {
            return (this.subtotal * this.taxRate) / 100
        },

        total() {
            return this.subtotal + this.tax
        }
    },

    mounted() {
        // Start with one empty item
        this.addItem()
    },

    methods: {
        handleAction(action) {
            if (action.key === 'save') {
                this.save()
            }
        },

        addItem() {
            this.order.items.push({
                uuid: uuidv4(),
                product_id: null,
                quantity: 1,
                price: 0,
                total: 0
            })
        },

        removeItem(index) {
            this.order.items.splice(index, 1)
        },

        async updateItemPrice(index) {
            const item = this.order.items[index]
            if (!item.product_id) return

            try {
                const { data } = await this.$axios.get(`/api/products/${item.product_id}`)
                item.price = data.price
                this.updateItemTotal(index)
            } catch (error) {
                this.$notify({
                    message: 'Failed to load product price',
                    type: 'error'
                })
            }
        },

        updateItemTotal(index) {
            const item = this.order.items[index]
            item.total = (item.quantity || 0) * (item.price || 0)
        },

        getItemError(index, field) {
            return this.itemErrors[index]?.[field]
        },

        async save() {
            this.saving = true

            try {
                const payload = {
                    shop_uuid: this.$route.params.shop_uuid,
                    customer_id: this.order.customer_id,
                    items: this.order.items.map(item => ({
                        product_id: item.product_id,
                        quantity: item.quantity,
                        price: item.price
                    })),
                    subtotal: this.subtotal,
                    tax: this.tax,
                    total: this.total
                }

                const { data } = await this.$axios.post('/api/orders', payload)

                this.$notify({
                    message: 'Order saved',
                    type: 'success'
                })

                this.$router.push(`/shops/${this.$route.params.shop_uuid}/orders/${data.id}`)
            } catch (error) {
                if (error.response?.status === 422) {
                    const errors = error.response.data.errors

                    // Separate item errors
                    this.itemErrors = []
                    Object.keys(errors).forEach(key => {
                        const match = key.match(/^items\.(\d+)\.(.+)$/)
                        if (match) {
                            const [, index, field] = match
                            if (!this.itemErrors[index]) {
                                this.itemErrors[index] = {}
                            }
                            this.itemErrors[index][field] = errors[key][0]
                        } else {
                            this.order.errors[key] = errors[key][0]
                        }
                    })

                    this.$notify({
                        message: 'Please fix validation errors',
                        type: 'error'
                    })
                } else {
                    this.$notify({
                        message: 'Failed to save order',
                        type: 'error'
                    })
                }
            } finally {
                this.saving = false
            }
        },

        loadCustomers(search) {
            return `/api/shops/${this.$route.params.shop_uuid}/customers?search=${search}`
        },

        loadProducts(search) {
            return `/api/shops/${this.$route.params.shop_uuid}/products?search=${search}`
        }
    }
}
</script>
```

## Optimistic Updates

Update UI immediately before server confirmation for better UX.

### Toggle with Optimistic Update

```markup
<template>
    <AwCard>
        <AwList :items="settings">
            <template #item="{ item: setting }">
                <AwFlow justify="between" align="center">
                    <div>
                        <div class="font-medium">{{ setting.label }}</div>
                        <AwDescription>{{ setting.description }}</AwDescription>
                    </div>

                    <AwSwitcher
                        :value="setting.enabled"
                        @input="toggleSetting(setting)"
                    />
                </AwFlow>
            </template>
        </AwList>
    </AwCard>
</template>

<script>
export default {
    data() {
        return {
            settings: [
                {
                    key: 'email_notifications',
                    label: 'Email Notifications',
                    description: 'Receive email notifications for new orders',
                    enabled: true
                },
                {
                    key: 'sms_notifications',
                    label: 'SMS Notifications',
                    description: 'Receive SMS notifications for urgent updates',
                    enabled: false
                }
            ]
        }
    },

    methods: {
        async toggleSetting(setting) {
            // Store previous value for rollback
            const previousValue = setting.enabled

            // Optimistically update UI
            setting.enabled = !setting.enabled

            try {
                await this.$axios.patch(`/api/settings/${setting.key}`, {
                    shop_uuid: this.$route.params.shop_uuid,
                    enabled: setting.enabled
                })

                this.$notify({
                    message: `${setting.label} ${setting.enabled ? 'enabled' : 'disabled'}`,
                    type: 'success'
                })
            } catch (error) {
                // Rollback on error
                setting.enabled = previousValue

                this.$notify({
                    message: 'Failed to update setting',
                    type: 'error'
                })
            }
        }
    }
}
</script>
```

### List with Optimistic Delete

```markup
<script>
export default {
    methods: {
        async deleteCustomer(customer) {
            const confirmed = await this.$confirm({
                title: 'Delete Customer',
                message: `Are you sure you want to delete ${customer.name}?`
            })

            if (!confirmed) return

            // Store index for rollback
            const index = this.customers.models.indexOf(customer)
            const deletedCustomer = { ...customer }

            // Optimistically remove from list
            this.customers.models.splice(index, 1)

            try {
                await this.$axios.delete(`/api/customers/${customer.id}`, {
                    params: {
                        shop_uuid: this.$route.params.shop_uuid
                    }
                })

                this.$notify({
                    message: 'Customer deleted',
                    type: 'success'
                })
            } catch (error) {
                // Rollback on error
                this.customers.models.splice(index, 0, deletedCustomer)

                this.$notify({
                    message: 'Failed to delete customer',
                    type: 'error'
                })
            }
        }
    }
}
</script>
```

## Real-Time Data

Update UI automatically with WebSocket or polling.

### With Laravel Echo (WebSockets)

```markup
<template>
    <AwPage title="Orders">
        <AwAlert v-if="hasNewOrders" type="info" class="mb-6">
            New orders received.
            <AwButton size="sm" @click="loadNewOrders">
                Refresh
            </AwButton>
        </AwAlert>

        <AwTableBuilder :collection="orders">
            <AwTableCol field="number" title="Order #" />
            <AwTableCol field="customer_name" title="Customer" />
            <AwTableCol field="total" title="Total" />
            <AwTableCol field="status" title="Status" />
        </AwTableBuilder>
    </AwPage>
</template>

<script>
import Orders from '~/collections/Orders'

export default {
    data() {
        return {
            orders: new Orders([], {
                shop_uuid: this.$route.params.shop_uuid
            }),
            hasNewOrders: false
        }
    },

    mounted() {
        // Subscribe to shop channel
        window.Echo.private(`shop.${this.$route.params.shop_uuid}`)
            .listen('OrderCreated', this.onOrderCreated)
            .listen('OrderUpdated', this.onOrderUpdated)
    },

    beforeDestroy() {
        // Unsubscribe
        window.Echo.leave(`shop.${this.$route.params.shop_uuid}`)
    },

    methods: {
        onOrderCreated(event) {
            console.log('New order:', event.order)
            this.hasNewOrders = true

            this.$notify({
                message: `New order #${event.order.number}`,
                type: 'info'
            })
        },

        onOrderUpdated(event) {
            // Find and update order in collection
            const order = this.orders.models.find(o => o.id === event.order.id)
            if (order) {
                Object.assign(order, event.order)
            }
        },

        async loadNewOrders() {
            await this.orders.fetch()
            this.hasNewOrders = false
        }
    }
}
</script>
```

### With Polling

```markup
<script>
export default {
    data() {
        return {
            pollingInterval: null,
            lastUpdated: null
        }
    },

    mounted() {
        // Initial load
        this.loadData()

        // Poll every 30 seconds
        this.pollingInterval = setInterval(() => {
            this.loadData()
        }, 30000)

        // Stop polling when page hidden
        document.addEventListener('visibilitychange', this.handleVisibilityChange)
    },

    beforeDestroy() {
        if (this.pollingInterval) {
            clearInterval(this.pollingInterval)
        }
        document.removeEventListener('visibilitychange', this.handleVisibilityChange)
    },

    methods: {
        async loadData() {
            // Only poll if page is visible
            if (document.hidden) return

            await this.collection.fetch()
            this.lastUpdated = Date.now()
        },

        handleVisibilityChange() {
            if (document.hidden) {
                // Stop polling
                if (this.pollingInterval) {
                    clearInterval(this.pollingInterval)
                    this.pollingInterval = null
                }
            } else {
                // Resume polling
                this.loadData()
                this.pollingInterval = setInterval(() => {
                    this.loadData()
                }, 30000)
            }
        }
    }
}
</script>
```

## File Upload with Progress

Track upload progress for large files.

### Upload with Progress Bar

```markup
<template>
    <AwCard title="Upload Files">
        <AwUploader
            v-model="files"
            :uploading="uploading"
            :progress="uploadProgress"
            multiple
            @change="handleUpload"
        />

        <AwProgress
            v-if="uploading"
            :value="uploadProgress"
            class="mt-4"
        />

        <AwUploaderFiles
            v-model="uploadedFiles"
            @remove="removeFile"
        />
    </AwCard>
</template>

<script>
export default {
    data() {
        return {
            files: [],
            uploadedFiles: [],
            uploading: false,
            uploadProgress: 0
        }
    },

    methods: {
        async handleUpload(files) {
            if (!files || files.length === 0) return

            this.uploading = true
            this.uploadProgress = 0

            const formData = new FormData()
            files.forEach(file => {
                formData.append('files[]', file)
            })
            formData.append('shop_uuid', this.$route.params.shop_uuid)

            try {
                const { data } = await this.$axios.post('/api/files', formData, {
                    headers: {
                        'Content-Type': 'multipart/form-data'
                    },
                    onUploadProgress: (progressEvent) => {
                        this.uploadProgress = Math.round(
                            (progressEvent.loaded * 100) / progressEvent.total
                        )
                    }
                })

                this.uploadedFiles.push(...data.files)

                this.$notify({
                    message: `${data.files.length} file(s) uploaded`,
                    type: 'success'
                })
            } catch (error) {
                this.$notify({
                    message: 'Failed to upload files',
                    type: 'error'
                })
            } finally {
                this.uploading = false
                this.uploadProgress = 0
                this.files = []
            }
        },

        async removeFile(file) {
            try {
                await this.$axios.delete(`/api/files/${file.id}`)

                const index = this.uploadedFiles.indexOf(file)
                this.uploadedFiles.splice(index, 1)

                this.$notify({
                    message: 'File removed',
                    type: 'success'
                })
            } catch (error) {
                this.$notify({
                    message: 'Failed to remove file',
                    type: 'error'
                })
            }
        }
    }
}
</script>
```

## Infinite Scroll

Load more data as user scrolls.

### Infinite Scroll Table

```markup
<template>
    <AwPage title="Products">
        <div ref="scrollContainer" class="overflow-auto" style="max-height: 80vh;">
            <AwCard
                v-for="product in products"
                :key="product.id"
                class="mb-4"
            >
                <h3>{{ product.name }}</h3>
                <p>{{ product.description }}</p>
                <p class="text-2xl font-bold">${{ product.price }}</p>
            </AwCard>

            <div v-if="loading" class="py-6 text-center">
                <AwProgress indeterminate />
            </div>

            <div v-if="!hasMore" class="py-6 text-center text-gray-500">
                No more products
            </div>
        </div>
    </AwPage>
</template>

<script>
export default {
    data() {
        return {
            products: [],
            page: 1,
            loading: false,
            hasMore: true
        }
    },

    mounted() {
        this.loadProducts()

        // Add scroll listener
        this.$refs.scrollContainer.addEventListener('scroll', this.handleScroll)
    },

    beforeDestroy() {
        this.$refs.scrollContainer.removeEventListener('scroll', this.handleScroll)
    },

    methods: {
        async loadProducts() {
            if (this.loading || !this.hasMore) return

            this.loading = true

            try {
                const { data } = await this.$axios.get('/api/products', {
                    params: {
                        shop_uuid: this.$route.params.shop_uuid,
                        page: this.page,
                        per_page: 20
                    }
                })

                this.products.push(...data.data)
                this.page++
                this.hasMore = data.meta.current_page < data.meta.last_page
            } catch (error) {
                this.$notify({
                    message: 'Failed to load products',
                    type: 'error'
                })
            } finally {
                this.loading = false
            }
        },

        handleScroll() {
            const container = this.$refs.scrollContainer
            const scrollPosition = container.scrollTop + container.clientHeight
            const scrollHeight = container.scrollHeight

            // Load more when scrolled to 80% of content
            if (scrollPosition >= scrollHeight * 0.8) {
                this.loadProducts()
            }
        }
    }
}
</script>
```

## Complex Filtering & URL State

Sync filters with URL query parameters for shareable links.

### URL-Synced Filters

```markup
<template>
    <AwPage title="Orders">
        <AwCard class="mb-6">
            <AwGrid :col="4">
                <AwSelect
                    v-model="filters.status"
                    :options="statusOptions"
                    label="Status"
                    @input="updateFilters"
                />

                <AwSelect
                    v-model="filters.payment_method"
                    :options="paymentOptions"
                    label="Payment"
                    @input="updateFilters"
                />

                <AwDate
                    v-model="filters.date_from"
                    label="From"
                    @input="updateFilters"
                />

                <AwDate
                    v-model="filters.date_to"
                    label="To"
                    @input="updateFilters"
                />
            </AwGrid>

            <AwFlow justify="between" class="mt-4">
                <AwButton @click="resetFilters">
                    Reset Filters
                </AwButton>

                <AwButton @click="exportResults" color="accent">
                    Export Results
                </AwButton>
            </AwFlow>
        </AwCard>

        <AwTableBuilder :collection="orders">
            <AwTableCol field="number" title="Order #" />
            <AwTableCol field="total" title="Total" />
            <AwTableCol field="status" title="Status" />
        </AwTableBuilder>
    </AwPage>
</template>

<script>
import Orders from '~/collections/Orders'

export default {
    data() {
        return {
            orders: new Orders([], {
                shop_uuid: this.$route.params.shop_uuid
            }),
            filters: {
                status: null,
                payment_method: null,
                date_from: null,
                date_to: null
            },
            statusOptions: ['all', 'pending', 'processing', 'completed'],
            paymentOptions: ['all', 'card', 'cash', 'bank_transfer']
        }
    },

    mounted() {
        // Load filters from URL
        this.loadFiltersFromURL()

        // Apply filters
        this.applyFilters()
    },

    watch: {
        '$route.query': {
            handler() {
                this.loadFiltersFromURL()
                this.applyFilters()
            },
            deep: true
        }
    },

    methods: {
        loadFiltersFromURL() {
            this.filters = {
                status: this.$route.query.status || null,
                payment_method: this.$route.query.payment_method || null,
                date_from: this.$route.query.date_from || null,
                date_to: this.$route.query.date_to || null
            }
        },

        updateFilters() {
            // Update URL query params
            const query = {}

            Object.keys(this.filters).forEach(key => {
                if (this.filters[key] && this.filters[key] !== 'all') {
                    query[key] = this.filters[key]
                }
            })

            this.$router.push({ query })
        },

        applyFilters() {
            const options = {
                shop_uuid: this.$route.params.shop_uuid
            }

            Object.keys(this.filters).forEach(key => {
                if (this.filters[key] && this.filters[key] !== 'all') {
                    options[key] = this.filters[key]
                }
            })

            this.orders.setOptions(options)
            this.orders.fetch()
        },

        resetFilters() {
            this.$router.push({ query: {} })
        },

        async exportResults() {
            try {
                const response = await this.$axios.get('/api/orders/export', {
                    params: {
                        shop_uuid: this.$route.params.shop_uuid,
                        ...this.$route.query
                    },
                    responseType: 'blob'
                })

                // Download file
                const url = window.URL.createObjectURL(new Blob([response.data]))
                const link = document.createElement('a')
                link.href = url
                link.setAttribute('download', `orders-${Date.now()}.csv`)
                document.body.appendChild(link)
                link.click()
                link.remove()

                this.$notify({
                    message: 'Export completed',
                    type: 'success'
                })
            } catch (error) {
                this.$notify({
                    message: 'Failed to export',
                    type: 'error'
                })
            }
        }
    }
}
</script>
```

## Permission-Based UI

Show/hide features based on user permissions.

### With CASL Permissions

```markup
<template>
    <AwPage title="Customers">
        <template #buttons>
            <AwButton
                v-if="$can('create', 'Customer')"
                :href="`/shops/${$route.params.shop_uuid}/customers/new`"
                color="accent"
            >
                Add Customer
            </AwButton>

            <AwButton
                v-if="$can('export', 'Customer')"
                @click="exportCustomers"
            >
                Export
            </AwButton>
        </template>

        <AwTableBuilder :collection="customers">
            <AwTableCol field="name" title="Name" />
            <AwTableCol field="email" title="Email" />

            <template #dropdown="{ cell }">
                <AwDropdownButton>
                    <AwButton
                        v-if="$can('update', cell)"
                        @click="editCustomer(cell)"
                    >
                        Edit
                    </AwButton>

                    <AwButton
                        v-if="$can('delete', cell)"
                        @click="deleteCustomer(cell)"
                    >
                        Delete
                    </AwButton>

                    <AwButton
                        v-if="$can('view', 'Order') && model.orders_count > 0"
                        @click="viewOrders(model)"
                    >
                        View Orders ({{ model.orders_count }})
                    </AwButton>
                </AwDropdownButton>
            </template>
        </AwTableBuilder>
    </AwPage>
</template>

<script>
import Customers from '~/collections/Customers'

export default {
    data() {
        return {
            customers: new Customers([], {
                shop_uuid: this.$route.params.shop_uuid
            })
        }
    },

    methods: {
        editCustomer(customer) {
            if (!this.$can('update', customer)) {
                this.$notify({
                    message: 'You do not have permission to edit this customer',
                    type: 'error'
                })
                return
            }

            this.$router.push(`/shops/${this.$route.params.shop_uuid}/customers/${customer.id}/edit`)
        },

        async deleteCustomer(customer) {
            if (!this.$can('delete', customer)) {
                this.$notify({
                    message: 'You do not have permission to delete this customer',
                    type: 'error'
                })
                return
            }

            const confirmed = await this.$confirm({
                title: 'Delete Customer',
                message: 'Are you sure?'
            })

            if (!confirmed) return

            try {
                await this.$axios.delete(`/api/customers/${customer.id}`)

                this.$notify({
                    message: 'Customer deleted',
                    type: 'success'
                })

                await this.customers.fetch()
            } catch (error) {
                this.$notify({
                    message: 'Failed to delete customer',
                    type: 'error'
                })
            }
        },

        async exportCustomers() {
            // Implementation
        },

        viewOrders(customer) {
            this.$router.push(`/shops/${this.$route.params.shop_uuid}/orders?customer_id=${customer.id}`)
        }
    }
}
</script>
```

## See Also

- [Common Patterns](./common-patterns.md) - Standard application patterns
- [Page Patterns](../guides/page-patterns/) - Page structure guides
- [Data Fetching Guide](../guides/data-fetching.md) - Collection and model patterns
- [Best Practices](../guides/best-practices.md) - Framework best practices
