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>
This commit is contained in:
@@ -3,9 +3,17 @@
|
||||
<div class="row-time">
|
||||
{{ formatTimeRange(booking.start_datetime, booking.end_datetime) }}
|
||||
</div>
|
||||
<div class="row-space">{{ booking.space?.name || 'Space' }}</div>
|
||||
<div v-if="showUser && booking.user" class="row-user">
|
||||
{{ booking.user.full_name }}
|
||||
<div class="row-space">
|
||||
{{ booking.space?.name || 'Space' }}
|
||||
<span v-if="booking.space?.property_name" class="row-property">{{ booking.space.property_name }}</span>
|
||||
</div>
|
||||
<div v-if="showUser" class="row-user">
|
||||
<template v-if="booking.is_anonymous && booking.guest_name">
|
||||
{{ booking.guest_name }} <span class="guest-badge">Guest</span>
|
||||
</template>
|
||||
<template v-else-if="booking.user">
|
||||
{{ booking.user.full_name }}
|
||||
</template>
|
||||
</div>
|
||||
<div class="row-title" :title="booking.title">{{ booking.title }}</div>
|
||||
<span :class="['row-badge', `row-badge-${booking.status}`]">
|
||||
@@ -136,6 +144,18 @@ const formatTimeRange = (start: string, end: string): string => {
|
||||
color: var(--color-accent);
|
||||
white-space: nowrap;
|
||||
min-width: 80px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.row-property {
|
||||
font-size: 10px;
|
||||
font-weight: 600;
|
||||
color: var(--color-text-muted);
|
||||
background: var(--color-bg-tertiary);
|
||||
padding: 1px 6px;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.row-user {
|
||||
@@ -188,6 +208,18 @@ const formatTimeRange = (start: string, end: string): string => {
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
|
||||
.guest-badge {
|
||||
display: inline-block;
|
||||
padding: 1px 6px;
|
||||
font-size: 10px;
|
||||
font-weight: 600;
|
||||
border-radius: 6px;
|
||||
background: color-mix(in srgb, var(--color-warning) 15%, transparent);
|
||||
color: var(--color-warning);
|
||||
vertical-align: middle;
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.booking-row {
|
||||
flex-wrap: wrap;
|
||||
|
||||
Reference in New Issue
Block a user