feat: add client-side permission gating to all write-action UIs

Gate all mutating UI elements with useCanDo() permission checks:
- BudgetPanel (budget_edit), PackingListPanel (packing_edit)
- DayPlanSidebar, DayDetailPanel (day_edit)
- ReservationsPanel, ReservationModal (reservation_edit)
- CollabNotes, CollabPolls, CollabChat (collab_edit)
- FileManager (file_edit, file_delete, file_upload)
- PlaceFormModal, PlaceInspector, PlacesSidebar (place_edit, file_upload)
- TripFormModal (trip_edit, trip_cover_upload)
- DashboardPage (trip_edit, trip_cover_upload, trip_delete, trip_archive)
- TripMembersModal (member_manage, share_manage)

Also: fix redundant getTripOwnerId queries in trips.ts, remove dead
getTripOwnerId function, fix TripMembersModal grid when share hidden,
fix canRemove logic, guard TripListItem empty actions div.
This commit is contained in:
Gérnyi Márk
2026-03-31 22:06:52 +02:00
parent d74133745a
commit 5f71b85c06
17 changed files with 333 additions and 221 deletions
@@ -3,6 +3,8 @@ import Modal from '../shared/Modal'
import CustomSelect from '../shared/CustomSelect'
import { mapsApi } from '../../api/client'
import { useAuthStore } from '../../store/authStore'
import { useCanDo } from '../../store/permissionsStore'
import { useTripStore } from '../../store/tripStore'
import { useToast } from '../shared/Toast'
import { Search, Paperclip, X, AlertTriangle } from 'lucide-react'
import { useTranslation } from '../../i18n'
@@ -66,6 +68,9 @@ export default function PlaceFormModal({
const toast = useToast()
const { t, language } = useTranslation()
const { hasMapsKey } = useAuthStore()
const can = useCanDo()
const tripObj = useTripStore((s) => s.trip)
const canUploadFiles = can('file_upload', tripObj)
useEffect(() => {
if (place) {
@@ -171,6 +176,7 @@ export default function PlaceFormModal({
// Paste support for files/images
const handlePaste = (e) => {
if (!canUploadFiles) return
const items = e.clipboardData?.items
if (!items) return
for (const item of Array.from(items)) {
@@ -386,7 +392,7 @@ export default function PlaceFormModal({
</div>
{/* File Attachments */}
{true && (
{canUploadFiles && (
<div className="border border-gray-200 rounded-xl p-3 space-y-2">
<div className="flex items-center justify-between">
<label className="block text-sm font-medium text-gray-700">{t('files.title')}</label>