Files
space-booking/frontend/src/components/PropertySelector.vue
Claude Agent e21cf03a16 feat: add multi-tenant system with properties, organizations, and public booking
Implement complete multi-property architecture:
- Properties (groups of spaces) with public/private visibility
- Property managers (many-to-many) with role-based permissions
- Organizations with member management
- Anonymous/guest booking support via public API (/api/public/*)
- Property-scoped spaces, bookings, and settings
- Frontend: property selector, organization management, public booking views
- Migration script and updated seed data

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 00:17:21 +00:00

88 lines
2.1 KiB
Vue

<template>
<div v-if="authStore.isAdminOrManager && propertyStore.properties.length > 0" class="property-selector">
<label class="selector-label">
<Landmark :size="14" />
<span>Property</span>
</label>
<select
:value="propertyStore.currentPropertyId"
@change="handleChange"
class="property-select"
>
<option :value="null">All Properties</option>
<option
v-for="prop in propertyStore.properties"
:key="prop.id"
:value="prop.id"
>
{{ prop.name }}
</option>
</select>
</div>
</template>
<script setup lang="ts">
import { onMounted } from 'vue'
import { useAuthStore } from '@/stores/auth'
import { usePropertyStore } from '@/stores/property'
import { Landmark } from 'lucide-vue-next'
const authStore = useAuthStore()
const propertyStore = usePropertyStore()
const handleChange = (event: Event) => {
const value = (event.target as HTMLSelectElement).value
propertyStore.setCurrentProperty(value ? Number(value) : null)
}
onMounted(() => {
if (authStore.isAdminOrManager && propertyStore.properties.length === 0) {
propertyStore.fetchMyProperties()
}
})
</script>
<style scoped>
.property-selector {
padding: 0.75rem 1rem;
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}
.selector-label {
display: flex;
align-items: center;
gap: 6px;
font-size: 11px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
color: var(--sidebar-text);
opacity: 0.6;
margin-bottom: 6px;
}
.property-select {
width: 100%;
padding: 8px 10px;
border-radius: var(--radius-sm);
border: 1px solid rgba(255, 255, 255, 0.15);
background: var(--sidebar-hover-bg);
color: var(--sidebar-text-active);
font-size: 13px;
font-weight: 500;
cursor: pointer;
outline: none;
transition: border-color var(--transition-fast);
}
.property-select:focus {
border-color: var(--color-accent);
box-shadow: 0 0 0 2px color-mix(in srgb, var(--color-accent) 20%, transparent);
}
.property-select option {
background: var(--color-surface);
color: var(--color-text-primary);
}
</style>