Add export button

This commit is contained in:
dvirlabs 2026-02-02 21:03:41 +02:00
parent 6496b13d49
commit 2144b4c446
4 changed files with 166 additions and 2 deletions

View File

@ -10,7 +10,8 @@
"dependencies": {
"axios": "^1.6.2",
"react": "^18.2.0",
"react-dom": "^18.2.0"
"react-dom": "^18.2.0",
"xlsx": "^0.18.5"
},
"devDependencies": {
"@types/react": "^18.2.43",
@ -1158,6 +1159,15 @@
"vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
}
},
"node_modules/adler-32": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/adler-32/-/adler-32-1.3.1.tgz",
"integrity": "sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==",
"license": "Apache-2.0",
"engines": {
"node": ">=0.8"
}
},
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
@ -1253,6 +1263,28 @@
],
"license": "CC-BY-4.0"
},
"node_modules/cfb": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/cfb/-/cfb-1.2.2.tgz",
"integrity": "sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==",
"license": "Apache-2.0",
"dependencies": {
"adler-32": "~1.3.0",
"crc-32": "~1.2.0"
},
"engines": {
"node": ">=0.8"
}
},
"node_modules/codepage": {
"version": "1.15.0",
"resolved": "https://registry.npmjs.org/codepage/-/codepage-1.15.0.tgz",
"integrity": "sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==",
"license": "Apache-2.0",
"engines": {
"node": ">=0.8"
}
},
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
@ -1272,6 +1304,18 @@
"dev": true,
"license": "MIT"
},
"node_modules/crc-32": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz",
"integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==",
"license": "Apache-2.0",
"bin": {
"crc32": "bin/crc32.njs"
},
"engines": {
"node": ">=0.8"
}
},
"node_modules/csstype": {
"version": "3.2.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
@ -1457,6 +1501,15 @@
"node": ">= 6"
}
},
"node_modules/frac": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/frac/-/frac-1.1.2.tgz",
"integrity": "sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==",
"license": "Apache-2.0",
"engines": {
"node": ">=0.8"
}
},
"node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
@ -1844,6 +1897,18 @@
"node": ">=0.10.0"
}
},
"node_modules/ssf": {
"version": "0.11.2",
"resolved": "https://registry.npmjs.org/ssf/-/ssf-0.11.2.tgz",
"integrity": "sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==",
"license": "Apache-2.0",
"dependencies": {
"frac": "~1.1.2"
},
"engines": {
"node": ">=0.8"
}
},
"node_modules/update-browserslist-db": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
@ -1935,6 +2000,45 @@
}
}
},
"node_modules/wmf": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wmf/-/wmf-1.0.2.tgz",
"integrity": "sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==",
"license": "Apache-2.0",
"engines": {
"node": ">=0.8"
}
},
"node_modules/word": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/word/-/word-0.3.0.tgz",
"integrity": "sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==",
"license": "Apache-2.0",
"engines": {
"node": ">=0.8"
}
},
"node_modules/xlsx": {
"version": "0.18.5",
"resolved": "https://registry.npmjs.org/xlsx/-/xlsx-0.18.5.tgz",
"integrity": "sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==",
"license": "Apache-2.0",
"dependencies": {
"adler-32": "~1.3.0",
"cfb": "~1.2.1",
"codepage": "~1.15.0",
"crc-32": "~1.2.1",
"ssf": "~0.11.2",
"wmf": "~1.0.1",
"word": "~0.3.0"
},
"bin": {
"xlsx": "bin/xlsx.njs"
},
"engines": {
"node": ">=0.8"
}
},
"node_modules/yallist": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",

View File

@ -11,7 +11,8 @@
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0",
"axios": "^1.6.2"
"axios": "^1.6.2",
"xlsx": "^0.18.5"
},
"devDependencies": {
"@types/react": "^18.2.43",

View File

@ -105,6 +105,17 @@ header h1 {
background: #dc2626;
}
.btn-success {
background: #10b981;
color: white;
}
.btn-success:hover {
background: #059669;
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(16, 185, 129, 0.4);
}
.loading {
text-align: center;
padding: 40px;

View File

@ -1,5 +1,6 @@
import { useState } from 'react'
import { deleteGuest, deleteGuestsBulk } from '../api/api'
import * as XLSX from 'xlsx'
import './GuestList.css'
function GuestList({ guests, onEdit, onUpdate }) {
@ -80,6 +81,50 @@ function GuestList({ guests, onEdit, onUpdate }) {
}
}
const exportToExcel = () => {
// Prepare data for export
const exportData = guests.map(guest => ({
'שם פרטי': guest.first_name,
'שם משפחה': guest.last_name,
'אימייל': guest.email || '',
'טלפון': guest.phone_number || '',
'סטטוס אישור': getRsvpLabel(guest.rsvp_status),
'העדפת ארוחה': guest.meal_preference || '',
'פלאס ואן': guest.has_plus_one ? 'כן' : 'לא',
'שם פלאס ואן': guest.plus_one_name || '',
'מספר שולחן': guest.table_number || '',
'מקור': guest.owner || ''
}))
// Create worksheet
const ws = XLSX.utils.json_to_sheet(exportData)
// Set column widths
ws['!cols'] = [
{ wch: 15 }, // שם פרטי
{ wch: 15 }, // שם משפחה
{ wch: 25 }, // אימייל
{ wch: 15 }, // טלפון
{ wch: 12 }, // סטטוס אישור
{ wch: 15 }, // העדפת ארוחה
{ wch: 10 }, // פלאס ואן
{ wch: 15 }, // שם פלאס ואן
{ wch: 12 }, // מספר שולחן
{ wch: 20 } // מקור
]
// Create workbook
const wb = XLSX.utils.book_new()
XLSX.utils.book_append_sheet(wb, ws, 'רשימת אורחים')
// Generate file name with date
const date = new Date().toISOString().split('T')[0]
const fileName = `guest-list-${date}.xlsx`
// Save file
XLSX.writeFile(wb, fileName)
}
if (guests.length === 0) {
return (
<div className="no-guests">
@ -93,6 +138,9 @@ function GuestList({ guests, onEdit, onUpdate }) {
<div className="list-header">
<h2>רשימת אורחים ({guests.length})</h2>
<div className="list-controls">
<button className="btn btn-success" onClick={exportToExcel}>
📥 ייצוא לאקסל
</button>
<label>
הצג:
<select value={pageSize} onChange={(e) => {