Add delete selected button
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
This commit is contained in:
parent
82315dd4ab
commit
b8d02c43f8
@ -451,8 +451,19 @@ async def delete_guest(
|
||||
|
||||
|
||||
# ============================================
|
||||
# Bulk Guest Import
|
||||
# Bulk Guest Delete
|
||||
# ============================================
|
||||
@app.post("/events/{event_id}/guests/bulk-delete")
|
||||
async def bulk_delete_guests(
|
||||
event_id: UUID,
|
||||
delete_data: schemas.GuestBulkDelete,
|
||||
db: Session = Depends(get_db),
|
||||
current_user_id: UUID = Depends(get_current_user_id)
|
||||
):
|
||||
"""Bulk delete guests (admin only)"""
|
||||
await authz.verify_event_admin(event_id, db, current_user_id)
|
||||
deleted_count = crud.delete_guests_bulk(db, event_id, delete_data.guest_ids)
|
||||
return {"message": f"{deleted_count} guests deleted successfully", "deleted_count": deleted_count}
|
||||
@app.post("/events/{event_id}/guests/import", response_model=dict)
|
||||
async def bulk_import_guests(
|
||||
event_id: UUID,
|
||||
|
||||
@ -154,6 +154,10 @@ class GuestBulkImport(BaseModel):
|
||||
guests: List[GuestImportItem]
|
||||
|
||||
|
||||
class GuestBulkDelete(BaseModel):
|
||||
guest_ids: List[UUID]
|
||||
|
||||
|
||||
# ============================================
|
||||
# Filter/Search Schemas
|
||||
# ============================================
|
||||
|
||||
@ -119,6 +119,11 @@ export const deleteGuest = async (eventId, guestId) => {
|
||||
return response.data
|
||||
}
|
||||
|
||||
export const bulkDeleteGuests = async (eventId, guestIds) => {
|
||||
const response = await api.post(`/events/${eventId}/guests/bulk-delete`, { guest_ids: guestIds })
|
||||
return response.data
|
||||
}
|
||||
|
||||
export const bulkImportGuests = async (eventId, guests) => {
|
||||
const response = await api.post(`/events/${eventId}/guests/import`, { guests })
|
||||
return response.data
|
||||
|
||||
@ -95,6 +95,7 @@
|
||||
.btn-tool,
|
||||
.btn-add-guest,
|
||||
.btn-whatsapp,
|
||||
.btn-delete-selected,
|
||||
.btn-export,
|
||||
.btn-duplicate {
|
||||
display: inline-flex;
|
||||
@ -160,6 +161,16 @@
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
/* delete selected */
|
||||
.btn-delete-selected {
|
||||
background: var(--color-danger, #e53e3e);
|
||||
color: #fff;
|
||||
}
|
||||
.btn-delete-selected:hover {
|
||||
background: #c53030;
|
||||
box-shadow: 0 2px 8px rgba(229, 62, 62, 0.35);
|
||||
}
|
||||
|
||||
/* ── legacy class aliases kept for any remaining refs ── */
|
||||
.header-actions { display: flex; gap: 0.5rem; flex-wrap: wrap; }
|
||||
.btn-members { display: none; }
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { getGuests, getGuestOwners, createGuest, updateGuest, deleteGuest, sendWhatsAppInvitationToGuests, getEvent } from '../api/api'
|
||||
import { getGuests, getGuestOwners, createGuest, updateGuest, deleteGuest, bulkDeleteGuests, sendWhatsAppInvitationToGuests, getEvent } from '../api/api'
|
||||
import GuestForm from './GuestForm'
|
||||
import GoogleImport from './GoogleImport'
|
||||
import ImportContacts from './ImportContacts'
|
||||
@ -46,7 +46,10 @@ const he = {
|
||||
failedToDelete: 'נכשל במחיקת אורח',
|
||||
sendWhatsApp: '💬 שלח בוואטסאפ',
|
||||
noGuestsSelected: 'בחר לפחות אורח אחד',
|
||||
selectGuestsFirst: 'בחר אורחים לשליחת הזמנה'
|
||||
selectGuestsFirst: 'בחר אורחים לשליחת הזמנה',
|
||||
deleteSelected: '🗑️ מחק נבחרים',
|
||||
confirmDeleteSelected: 'האם אתה בטוח שברצונך למחוק {count} אורחים?',
|
||||
failedToDeleteSelected: 'נכשל במחיקת האורחים הנבחרים'
|
||||
}
|
||||
|
||||
function GuestList({ eventId, onBack, onShowMembers }) {
|
||||
@ -168,6 +171,20 @@ function GuestList({ eventId, onBack, onShowMembers }) {
|
||||
}
|
||||
}
|
||||
|
||||
const handleDeleteSelected = async () => {
|
||||
if (selectedGuestIds.size === 0) return
|
||||
if (!window.confirm(he.confirmDeleteSelected.replace('{count}', selectedGuestIds.size))) return
|
||||
|
||||
try {
|
||||
await bulkDeleteGuests(eventId, Array.from(selectedGuestIds))
|
||||
setGuests(guests.filter(g => !selectedGuestIds.has(g.id)))
|
||||
setSelectedGuestIds(new Set())
|
||||
} catch (err) {
|
||||
setError(he.failedToDeleteSelected)
|
||||
console.error(err)
|
||||
}
|
||||
}
|
||||
|
||||
const handleEdit = (guest) => {
|
||||
setEditingGuest(guest)
|
||||
setShowGuestForm(true)
|
||||
@ -356,13 +373,21 @@ function GuestList({ eventId, onBack, onShowMembers }) {
|
||||
</div>
|
||||
<div className="btn-group btn-group-primary">
|
||||
{selectedGuestIds.size > 0 && (
|
||||
<button
|
||||
className="btn-whatsapp"
|
||||
onClick={() => setShowWhatsAppModal(true)}
|
||||
title={he.selectGuestsFirst}
|
||||
>
|
||||
{he.sendWhatsApp} ({selectedGuestIds.size})
|
||||
</button>
|
||||
<>
|
||||
<button
|
||||
className="btn-delete-selected"
|
||||
onClick={handleDeleteSelected}
|
||||
>
|
||||
{he.deleteSelected} ({selectedGuestIds.size})
|
||||
</button>
|
||||
<button
|
||||
className="btn-whatsapp"
|
||||
onClick={() => setShowWhatsAppModal(true)}
|
||||
title={he.selectGuestsFirst}
|
||||
>
|
||||
{he.sendWhatsApp} ({selectedGuestIds.size})
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
<button className="btn-add-guest" onClick={() => {
|
||||
setEditingGuest(null)
|
||||
|
||||
BIN
guest-list-2026-02-28.xlsx
Normal file
BIN
guest-list-2026-02-28.xlsx
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user