diff --git a/TODOS.md b/TODOS.md new file mode 100644 index 0000000..b0de4ce --- /dev/null +++ b/TODOS.md @@ -0,0 +1,11 @@ +# TODOS + +## Din review-ul /autoplan „Linkuri publice" (2026-07-10) + +- [ ] **Istoric/alias slug după redenumire** (P3, M) — la editarea slug-ului, linkurile text vechi mor (QR-ul e pe ID și supraviețuiește). Un tabel `slug_history` cu redirect 301 ar păstra toate linkurile partajate. Context: decizia v1 = fără alias, cu warning în UI la editare. De făcut dacă apar plângeri de linkuri moarte. +- [ ] **Widget embed calendar public** (P3, L) — endpoint-ul busy e fundația; un ` + + diff --git a/frontend/src/components/PublicCalendar.vue b/frontend/src/components/PublicCalendar.vue new file mode 100644 index 0000000..3f673a5 --- /dev/null +++ b/frontend/src/components/PublicCalendar.vue @@ -0,0 +1,613 @@ + + + + + diff --git a/frontend/src/router/index.ts b/frontend/src/router/index.ts index f04b107..5b238e4 100644 --- a/frontend/src/router/index.ts +++ b/frontend/src/router/index.ts @@ -43,7 +43,7 @@ const router = createRouter({ meta: { requiresAuth: false } }, { - path: '/book/:propertyId?', + path: '/book/:slug?', name: 'PublicBooking', component: () => import('@/views/PublicBooking.vue'), meta: { requiresAuth: false, isPublic: true } diff --git a/frontend/src/services/api.ts b/frontend/src/services/api.ts index 6513717..069c887 100644 --- a/frontend/src/services/api.ts +++ b/frontend/src/services/api.ts @@ -28,7 +28,8 @@ import type { PropertyAccess, Organization, OrganizationMember, - AnonymousBookingCreate + AnonymousBookingCreate, + BusyInterval } from '@/types' const api = axios.create({ @@ -435,7 +436,7 @@ export const propertiesApi = { const response = await api.post('/manager/properties', data) return response.data }, - update: async (id: number, data: { name?: string; description?: string; address?: string; is_public?: boolean }): Promise => { + update: async (id: number, data: { name?: string; description?: string; address?: string; is_public?: boolean; slug?: string; list_on_landing?: boolean }): Promise => { const response = await api.put(`/manager/properties/${id}`, data) return response.data }, @@ -513,8 +514,18 @@ export const publicApi = { const response = await publicApiInstance.get('/public/properties') return response.data }, - getPropertySpaces: async (propertyId: number): Promise => { - const response = await publicApiInstance.get(`/public/properties/${propertyId}/spaces`) + getProperty: async (slugOrId: string | number): Promise => { + const response = await publicApiInstance.get(`/public/properties/${slugOrId}`) + return response.data + }, + getPropertySpaces: async (slugOrId: string | number): Promise => { + const response = await publicApiInstance.get(`/public/properties/${slugOrId}/spaces`) + return response.data + }, + getBusy: async (spaceId: number, start: string, end: string): Promise => { + const response = await publicApiInstance.get(`/public/spaces/${spaceId}/busy`, { + params: { start, end } + }) return response.data }, getSpaceAvailability: async (spaceId: number, start: string, end: string) => { diff --git a/frontend/src/types/index.ts b/frontend/src/types/index.ts index f88843b..9653be0 100644 --- a/frontend/src/types/index.ts +++ b/frontend/src/types/index.ts @@ -253,6 +253,14 @@ export interface Property { created_at: string space_count?: number managers?: PropertyManagerInfo[] + slug?: string + list_on_landing?: boolean + require_approval?: boolean +} + +export interface BusyInterval { + start_time: string + end_time: string } export interface PropertyWithSpaces extends Property { diff --git a/frontend/src/views/Landing.vue b/frontend/src/views/Landing.vue index a12f33e..2b4b751 100644 --- a/frontend/src/views/Landing.vue +++ b/frontend/src/views/Landing.vue @@ -46,6 +46,7 @@
{{ ctaText }} Vezi cum arată + Rezervă fără cont

Cont gratuit, gata în câteva secunde.

@@ -232,6 +233,39 @@ + +
+

Spații deschise rezervării acum

+

Alege un spațiu și rezervă fără cont, în câteva minute.

+ +
+ + +
+
+
@@ -262,8 +296,10 @@