feat: enhance CSV upload handling with chunked requests and token support; improve form input limits
This commit is contained in:
@@ -15,9 +15,10 @@ const app = express();
|
|||||||
const port = 8002;
|
const port = 8002;
|
||||||
|
|
||||||
app.use(cors());
|
app.use(cors());
|
||||||
app.use(express.urlencoded({ extended: true }));
|
// Increase body size limits to support large CSV JSON payloads
|
||||||
|
app.use(express.urlencoded({ extended: true, limit: "10mb" }));
|
||||||
app.set("view engine", "ejs");
|
app.set("view engine", "ejs");
|
||||||
app.use(express.json());
|
app.use(express.json({ limit: "10mb" }));
|
||||||
|
|
||||||
app.get("/", (req, res) => {
|
app.get("/", (req, res) => {
|
||||||
res.render("index.ejs", { title: port });
|
res.render("index.ejs", { title: port });
|
||||||
@@ -32,22 +33,8 @@ app.post("/lose", async (req, res) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// !!!!!!! AUTHORISATION HINZUFÜGEN - DENN GEHT NICHT !!!!!!!!
|
// !!!!!!! AUTHORISATION HINZUFÜGEN - DENN GEHT NICHT !!!!!!!!
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
app.get("/table-data", authenticate, async (req, res) => {
|
app.get("/table-data", authenticate, async (req, res) => {
|
||||||
const result = await getTableData();
|
const result = await getTableData();
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
@@ -57,16 +44,21 @@ app.get("/table-data", authenticate, async (req, res) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
app.post("/create-entry", async (req, res) => {
|
app.post("/create-entry", authenticate, async (req, res) => {
|
||||||
const result = await createEntry(req.body.losnummer);
|
try {
|
||||||
if (result) {
|
const result = await createEntry(req.body.losnummer);
|
||||||
res.status(201).json({ success: true });
|
if (result) {
|
||||||
} else {
|
res.status(201).json({ success: true });
|
||||||
res.status(400).json({ success: false });
|
} else {
|
||||||
|
res.status(400).json({ success: false, message: "Insert failed" });
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error("/create-entry error", err);
|
||||||
|
res.status(500).json({ success: false, message: String(err) });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
app.delete("/remove-entries", async (req, res) => {
|
app.delete("/remove-entries", authenticate, async (req, res) => {
|
||||||
const result = await removeEntries(req.body.losnummern);
|
const result = await removeEntries(req.body.losnummern);
|
||||||
if (result) {
|
if (result) {
|
||||||
res.status(200).json({ success: true });
|
res.status(200).json({ success: true });
|
||||||
@@ -75,7 +67,7 @@ app.delete("/remove-entries", async (req, res) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
app.put("/save-row", async (req, res) => {
|
app.put("/save-row", authenticate, async (req, res) => {
|
||||||
const result = await saveRow(req.body);
|
const result = await saveRow(req.body);
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
res.status(200).json({ success: true });
|
res.status(200).json({ success: true });
|
||||||
|
@@ -52,22 +52,28 @@ export async function getTableData() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function createEntry(data) {
|
export async function createEntry(data) {
|
||||||
let { status } = { status: true };
|
if (!Array.isArray(data) || data.length === 0) return true;
|
||||||
for (const item of data) {
|
|
||||||
|
// Normalize values to strings and unique them to reduce duplicates
|
||||||
|
const values = Array.from(
|
||||||
|
new Set(data.map((v) => String(v).trim()).filter(Boolean))
|
||||||
|
);
|
||||||
|
|
||||||
|
// Prepare bulk insert values [[v1],[v2],...]
|
||||||
|
const rows = values.map((v) => [v]);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Use INSERT IGNORE to skip duplicates on UNIQUE(losnummer)
|
||||||
const [result] = await pool.query(
|
const [result] = await pool.query(
|
||||||
"INSERT INTO lose (losnummer) VALUES (?)",
|
"INSERT IGNORE INTO lose (losnummer) VALUES ?",
|
||||||
[item]
|
[rows]
|
||||||
);
|
);
|
||||||
|
// result.affectedRows may be less than rows.length if duplicates existed
|
||||||
if (result.affectedRows > 0) {
|
return true;
|
||||||
status = true;
|
} catch (e) {
|
||||||
} else {
|
console.error("Bulk insert failed", e);
|
||||||
status = false;
|
return false;
|
||||||
return status;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return status;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function removeEntries(losnummern) {
|
export async function removeEntries(losnummern) {
|
||||||
|
@@ -1,3 +1,852 @@
|
|||||||
test1
|
100000
|
||||||
test12
|
100001
|
||||||
test123
|
100002
|
||||||
|
100003
|
||||||
|
100004
|
||||||
|
100005
|
||||||
|
100006
|
||||||
|
100007
|
||||||
|
100008
|
||||||
|
100009
|
||||||
|
100010
|
||||||
|
100011
|
||||||
|
100012
|
||||||
|
100013
|
||||||
|
100014
|
||||||
|
100015
|
||||||
|
100016
|
||||||
|
100017
|
||||||
|
100018
|
||||||
|
100019
|
||||||
|
100020
|
||||||
|
100021
|
||||||
|
100022
|
||||||
|
100023
|
||||||
|
100024
|
||||||
|
100025
|
||||||
|
100026
|
||||||
|
100027
|
||||||
|
100028
|
||||||
|
100029
|
||||||
|
100030
|
||||||
|
100031
|
||||||
|
100032
|
||||||
|
100033
|
||||||
|
100034
|
||||||
|
100035
|
||||||
|
100036
|
||||||
|
100037
|
||||||
|
100038
|
||||||
|
100039
|
||||||
|
100040
|
||||||
|
100041
|
||||||
|
100042
|
||||||
|
100043
|
||||||
|
100044
|
||||||
|
100045
|
||||||
|
100046
|
||||||
|
100047
|
||||||
|
100048
|
||||||
|
100049
|
||||||
|
100050
|
||||||
|
100051
|
||||||
|
100052
|
||||||
|
100053
|
||||||
|
100054
|
||||||
|
100055
|
||||||
|
100056
|
||||||
|
100057
|
||||||
|
100058
|
||||||
|
100059
|
||||||
|
100060
|
||||||
|
100061
|
||||||
|
100062
|
||||||
|
100063
|
||||||
|
100064
|
||||||
|
100065
|
||||||
|
100066
|
||||||
|
100067
|
||||||
|
100068
|
||||||
|
100069
|
||||||
|
100070
|
||||||
|
100071
|
||||||
|
100072
|
||||||
|
100073
|
||||||
|
100074
|
||||||
|
100075
|
||||||
|
100076
|
||||||
|
100077
|
||||||
|
100078
|
||||||
|
100079
|
||||||
|
100080
|
||||||
|
100081
|
||||||
|
100082
|
||||||
|
100083
|
||||||
|
100084
|
||||||
|
100085
|
||||||
|
100086
|
||||||
|
100087
|
||||||
|
100088
|
||||||
|
100089
|
||||||
|
100090
|
||||||
|
100091
|
||||||
|
100092
|
||||||
|
100093
|
||||||
|
100094
|
||||||
|
100095
|
||||||
|
100096
|
||||||
|
100097
|
||||||
|
100098
|
||||||
|
100099
|
||||||
|
100100
|
||||||
|
100101
|
||||||
|
100102
|
||||||
|
100103
|
||||||
|
100104
|
||||||
|
100105
|
||||||
|
100106
|
||||||
|
100107
|
||||||
|
100108
|
||||||
|
100109
|
||||||
|
100110
|
||||||
|
100111
|
||||||
|
100112
|
||||||
|
100113
|
||||||
|
100114
|
||||||
|
100115
|
||||||
|
100116
|
||||||
|
100117
|
||||||
|
100118
|
||||||
|
100119
|
||||||
|
100120
|
||||||
|
100121
|
||||||
|
100122
|
||||||
|
100123
|
||||||
|
100124
|
||||||
|
100125
|
||||||
|
100126
|
||||||
|
100127
|
||||||
|
100128
|
||||||
|
100129
|
||||||
|
100130
|
||||||
|
100131
|
||||||
|
100132
|
||||||
|
100133
|
||||||
|
100134
|
||||||
|
100135
|
||||||
|
100136
|
||||||
|
100137
|
||||||
|
100138
|
||||||
|
100139
|
||||||
|
100140
|
||||||
|
100141
|
||||||
|
100142
|
||||||
|
100143
|
||||||
|
100144
|
||||||
|
100145
|
||||||
|
100146
|
||||||
|
100147
|
||||||
|
100148
|
||||||
|
100149
|
||||||
|
100150
|
||||||
|
100151
|
||||||
|
100152
|
||||||
|
100153
|
||||||
|
100154
|
||||||
|
100155
|
||||||
|
100156
|
||||||
|
100157
|
||||||
|
100158
|
||||||
|
100159
|
||||||
|
100160
|
||||||
|
100161
|
||||||
|
100162
|
||||||
|
100163
|
||||||
|
100164
|
||||||
|
100165
|
||||||
|
100166
|
||||||
|
100167
|
||||||
|
100168
|
||||||
|
100169
|
||||||
|
100170
|
||||||
|
100171
|
||||||
|
100172
|
||||||
|
100173
|
||||||
|
100174
|
||||||
|
100175
|
||||||
|
100176
|
||||||
|
100177
|
||||||
|
100178
|
||||||
|
100179
|
||||||
|
100180
|
||||||
|
100181
|
||||||
|
100182
|
||||||
|
100183
|
||||||
|
100184
|
||||||
|
100185
|
||||||
|
100186
|
||||||
|
100187
|
||||||
|
100188
|
||||||
|
100189
|
||||||
|
100190
|
||||||
|
100191
|
||||||
|
100192
|
||||||
|
100193
|
||||||
|
100194
|
||||||
|
100195
|
||||||
|
100196
|
||||||
|
100197
|
||||||
|
100198
|
||||||
|
100199
|
||||||
|
100200
|
||||||
|
100201
|
||||||
|
100202
|
||||||
|
100203
|
||||||
|
100204
|
||||||
|
100205
|
||||||
|
100206
|
||||||
|
100207
|
||||||
|
100208
|
||||||
|
100209
|
||||||
|
100210
|
||||||
|
100211
|
||||||
|
100212
|
||||||
|
100213
|
||||||
|
100214
|
||||||
|
100215
|
||||||
|
100216
|
||||||
|
100217
|
||||||
|
100218
|
||||||
|
100219
|
||||||
|
100220
|
||||||
|
100221
|
||||||
|
100222
|
||||||
|
100223
|
||||||
|
100224
|
||||||
|
100225
|
||||||
|
100226
|
||||||
|
100227
|
||||||
|
100228
|
||||||
|
100229
|
||||||
|
100230
|
||||||
|
100231
|
||||||
|
100232
|
||||||
|
100233
|
||||||
|
100234
|
||||||
|
100235
|
||||||
|
100236
|
||||||
|
100237
|
||||||
|
100238
|
||||||
|
100239
|
||||||
|
100240
|
||||||
|
100241
|
||||||
|
100242
|
||||||
|
100243
|
||||||
|
100244
|
||||||
|
100245
|
||||||
|
100246
|
||||||
|
100247
|
||||||
|
100248
|
||||||
|
100249
|
||||||
|
100250
|
||||||
|
100251
|
||||||
|
100252
|
||||||
|
100253
|
||||||
|
100254
|
||||||
|
100255
|
||||||
|
100256
|
||||||
|
100257
|
||||||
|
100258
|
||||||
|
100259
|
||||||
|
100260
|
||||||
|
100261
|
||||||
|
100262
|
||||||
|
100263
|
||||||
|
100264
|
||||||
|
100265
|
||||||
|
100266
|
||||||
|
100267
|
||||||
|
100268
|
||||||
|
100269
|
||||||
|
100270
|
||||||
|
100271
|
||||||
|
100272
|
||||||
|
100273
|
||||||
|
100274
|
||||||
|
100275
|
||||||
|
100276
|
||||||
|
100277
|
||||||
|
100278
|
||||||
|
100279
|
||||||
|
100280
|
||||||
|
100281
|
||||||
|
100282
|
||||||
|
100283
|
||||||
|
100284
|
||||||
|
100285
|
||||||
|
100286
|
||||||
|
100287
|
||||||
|
100288
|
||||||
|
100289
|
||||||
|
100290
|
||||||
|
100291
|
||||||
|
100292
|
||||||
|
100293
|
||||||
|
100294
|
||||||
|
100295
|
||||||
|
100296
|
||||||
|
100297
|
||||||
|
100298
|
||||||
|
100299
|
||||||
|
100300
|
||||||
|
100301
|
||||||
|
100302
|
||||||
|
100303
|
||||||
|
100304
|
||||||
|
100305
|
||||||
|
100306
|
||||||
|
100307
|
||||||
|
100308
|
||||||
|
100309
|
||||||
|
100310
|
||||||
|
100311
|
||||||
|
100312
|
||||||
|
100313
|
||||||
|
100314
|
||||||
|
100315
|
||||||
|
100316
|
||||||
|
100317
|
||||||
|
100318
|
||||||
|
100319
|
||||||
|
100320
|
||||||
|
100321
|
||||||
|
100322
|
||||||
|
100323
|
||||||
|
100324
|
||||||
|
100325
|
||||||
|
100326
|
||||||
|
100327
|
||||||
|
100328
|
||||||
|
100329
|
||||||
|
100330
|
||||||
|
100331
|
||||||
|
100332
|
||||||
|
100333
|
||||||
|
100334
|
||||||
|
100335
|
||||||
|
100336
|
||||||
|
100337
|
||||||
|
100338
|
||||||
|
100339
|
||||||
|
100340
|
||||||
|
100341
|
||||||
|
100342
|
||||||
|
100343
|
||||||
|
100344
|
||||||
|
100345
|
||||||
|
100346
|
||||||
|
100347
|
||||||
|
100348
|
||||||
|
100349
|
||||||
|
100350
|
||||||
|
100351
|
||||||
|
100352
|
||||||
|
100353
|
||||||
|
100354
|
||||||
|
100355
|
||||||
|
100356
|
||||||
|
100357
|
||||||
|
100358
|
||||||
|
100359
|
||||||
|
100360
|
||||||
|
100361
|
||||||
|
100362
|
||||||
|
100363
|
||||||
|
100364
|
||||||
|
100365
|
||||||
|
100366
|
||||||
|
100367
|
||||||
|
100368
|
||||||
|
100369
|
||||||
|
100370
|
||||||
|
100371
|
||||||
|
100372
|
||||||
|
100373
|
||||||
|
100374
|
||||||
|
100375
|
||||||
|
100376
|
||||||
|
100377
|
||||||
|
100378
|
||||||
|
100379
|
||||||
|
100380
|
||||||
|
100381
|
||||||
|
100382
|
||||||
|
100383
|
||||||
|
100384
|
||||||
|
100385
|
||||||
|
100386
|
||||||
|
100387
|
||||||
|
100388
|
||||||
|
100389
|
||||||
|
100390
|
||||||
|
100391
|
||||||
|
100392
|
||||||
|
100393
|
||||||
|
100394
|
||||||
|
100395
|
||||||
|
100396
|
||||||
|
100397
|
||||||
|
100398
|
||||||
|
100399
|
||||||
|
100400
|
||||||
|
100401
|
||||||
|
100402
|
||||||
|
100403
|
||||||
|
100404
|
||||||
|
100405
|
||||||
|
100406
|
||||||
|
100407
|
||||||
|
100408
|
||||||
|
100409
|
||||||
|
100410
|
||||||
|
100411
|
||||||
|
100412
|
||||||
|
100413
|
||||||
|
100414
|
||||||
|
100415
|
||||||
|
100416
|
||||||
|
100417
|
||||||
|
100418
|
||||||
|
100419
|
||||||
|
100420
|
||||||
|
100421
|
||||||
|
100422
|
||||||
|
100423
|
||||||
|
100424
|
||||||
|
100425
|
||||||
|
100426
|
||||||
|
100427
|
||||||
|
100428
|
||||||
|
100429
|
||||||
|
100430
|
||||||
|
100431
|
||||||
|
100432
|
||||||
|
100433
|
||||||
|
100434
|
||||||
|
100435
|
||||||
|
100436
|
||||||
|
100437
|
||||||
|
100438
|
||||||
|
100439
|
||||||
|
100440
|
||||||
|
100441
|
||||||
|
100442
|
||||||
|
100443
|
||||||
|
100444
|
||||||
|
100445
|
||||||
|
100446
|
||||||
|
100447
|
||||||
|
100448
|
||||||
|
100449
|
||||||
|
100450
|
||||||
|
100451
|
||||||
|
100452
|
||||||
|
100453
|
||||||
|
100454
|
||||||
|
100455
|
||||||
|
100456
|
||||||
|
100457
|
||||||
|
100458
|
||||||
|
100459
|
||||||
|
100460
|
||||||
|
100461
|
||||||
|
100462
|
||||||
|
100463
|
||||||
|
100464
|
||||||
|
100465
|
||||||
|
100466
|
||||||
|
100467
|
||||||
|
100468
|
||||||
|
100469
|
||||||
|
100470
|
||||||
|
100471
|
||||||
|
100472
|
||||||
|
100473
|
||||||
|
100474
|
||||||
|
100475
|
||||||
|
100476
|
||||||
|
100477
|
||||||
|
100478
|
||||||
|
100479
|
||||||
|
100480
|
||||||
|
100481
|
||||||
|
100482
|
||||||
|
100483
|
||||||
|
100484
|
||||||
|
100485
|
||||||
|
100486
|
||||||
|
100487
|
||||||
|
100488
|
||||||
|
100489
|
||||||
|
100490
|
||||||
|
100491
|
||||||
|
100492
|
||||||
|
100493
|
||||||
|
100494
|
||||||
|
100495
|
||||||
|
100496
|
||||||
|
100497
|
||||||
|
100498
|
||||||
|
100499
|
||||||
|
100500
|
||||||
|
100501
|
||||||
|
100502
|
||||||
|
100503
|
||||||
|
100504
|
||||||
|
100505
|
||||||
|
100506
|
||||||
|
100507
|
||||||
|
100508
|
||||||
|
100509
|
||||||
|
100510
|
||||||
|
100511
|
||||||
|
100512
|
||||||
|
100513
|
||||||
|
100514
|
||||||
|
100515
|
||||||
|
100516
|
||||||
|
100517
|
||||||
|
100518
|
||||||
|
100519
|
||||||
|
100520
|
||||||
|
100521
|
||||||
|
100522
|
||||||
|
100523
|
||||||
|
100524
|
||||||
|
100525
|
||||||
|
100526
|
||||||
|
100527
|
||||||
|
100528
|
||||||
|
100529
|
||||||
|
100530
|
||||||
|
100531
|
||||||
|
100532
|
||||||
|
100533
|
||||||
|
100534
|
||||||
|
100535
|
||||||
|
100536
|
||||||
|
100537
|
||||||
|
100538
|
||||||
|
100539
|
||||||
|
100540
|
||||||
|
100541
|
||||||
|
100542
|
||||||
|
100543
|
||||||
|
100544
|
||||||
|
100545
|
||||||
|
100546
|
||||||
|
100547
|
||||||
|
100548
|
||||||
|
100549
|
||||||
|
100550
|
||||||
|
100551
|
||||||
|
100552
|
||||||
|
100553
|
||||||
|
100554
|
||||||
|
100555
|
||||||
|
100556
|
||||||
|
100557
|
||||||
|
100558
|
||||||
|
100559
|
||||||
|
100560
|
||||||
|
100561
|
||||||
|
100562
|
||||||
|
100563
|
||||||
|
100564
|
||||||
|
100565
|
||||||
|
100566
|
||||||
|
100567
|
||||||
|
100568
|
||||||
|
100569
|
||||||
|
100570
|
||||||
|
100571
|
||||||
|
100572
|
||||||
|
100573
|
||||||
|
100574
|
||||||
|
100575
|
||||||
|
100576
|
||||||
|
100577
|
||||||
|
100578
|
||||||
|
100579
|
||||||
|
100580
|
||||||
|
100581
|
||||||
|
100582
|
||||||
|
100583
|
||||||
|
100584
|
||||||
|
100585
|
||||||
|
100586
|
||||||
|
100587
|
||||||
|
100588
|
||||||
|
100589
|
||||||
|
100590
|
||||||
|
100591
|
||||||
|
100592
|
||||||
|
100593
|
||||||
|
100594
|
||||||
|
100595
|
||||||
|
100596
|
||||||
|
100597
|
||||||
|
100598
|
||||||
|
100599
|
||||||
|
100600
|
||||||
|
100601
|
||||||
|
100602
|
||||||
|
100603
|
||||||
|
100604
|
||||||
|
100605
|
||||||
|
100606
|
||||||
|
100607
|
||||||
|
100608
|
||||||
|
100609
|
||||||
|
100610
|
||||||
|
100611
|
||||||
|
100612
|
||||||
|
100613
|
||||||
|
100614
|
||||||
|
100615
|
||||||
|
100616
|
||||||
|
100617
|
||||||
|
100618
|
||||||
|
100619
|
||||||
|
100620
|
||||||
|
100621
|
||||||
|
100622
|
||||||
|
100623
|
||||||
|
100624
|
||||||
|
100625
|
||||||
|
100626
|
||||||
|
100627
|
||||||
|
100628
|
||||||
|
100629
|
||||||
|
100630
|
||||||
|
100631
|
||||||
|
100632
|
||||||
|
100633
|
||||||
|
100634
|
||||||
|
100635
|
||||||
|
100636
|
||||||
|
100637
|
||||||
|
100638
|
||||||
|
100639
|
||||||
|
100640
|
||||||
|
100641
|
||||||
|
100642
|
||||||
|
100643
|
||||||
|
100644
|
||||||
|
100645
|
||||||
|
100646
|
||||||
|
100647
|
||||||
|
100648
|
||||||
|
100649
|
||||||
|
100650
|
||||||
|
100651
|
||||||
|
100652
|
||||||
|
100653
|
||||||
|
100654
|
||||||
|
100655
|
||||||
|
100656
|
||||||
|
100657
|
||||||
|
100658
|
||||||
|
100659
|
||||||
|
100660
|
||||||
|
100661
|
||||||
|
100662
|
||||||
|
100663
|
||||||
|
100664
|
||||||
|
100665
|
||||||
|
100666
|
||||||
|
100667
|
||||||
|
100668
|
||||||
|
100669
|
||||||
|
100670
|
||||||
|
100671
|
||||||
|
100672
|
||||||
|
100673
|
||||||
|
100674
|
||||||
|
100675
|
||||||
|
100676
|
||||||
|
100677
|
||||||
|
100678
|
||||||
|
100679
|
||||||
|
100680
|
||||||
|
100681
|
||||||
|
100682
|
||||||
|
100683
|
||||||
|
100684
|
||||||
|
100685
|
||||||
|
100686
|
||||||
|
100687
|
||||||
|
100688
|
||||||
|
100689
|
||||||
|
100690
|
||||||
|
100691
|
||||||
|
100692
|
||||||
|
100693
|
||||||
|
100694
|
||||||
|
100695
|
||||||
|
100696
|
||||||
|
100697
|
||||||
|
100698
|
||||||
|
100699
|
||||||
|
100700
|
||||||
|
100701
|
||||||
|
100702
|
||||||
|
100703
|
||||||
|
100704
|
||||||
|
100705
|
||||||
|
100706
|
||||||
|
100707
|
||||||
|
100708
|
||||||
|
100709
|
||||||
|
100710
|
||||||
|
100711
|
||||||
|
100712
|
||||||
|
100713
|
||||||
|
100714
|
||||||
|
100715
|
||||||
|
100716
|
||||||
|
100717
|
||||||
|
100718
|
||||||
|
100719
|
||||||
|
100720
|
||||||
|
100721
|
||||||
|
100722
|
||||||
|
100723
|
||||||
|
100724
|
||||||
|
100725
|
||||||
|
100726
|
||||||
|
100727
|
||||||
|
100728
|
||||||
|
100729
|
||||||
|
100730
|
||||||
|
100731
|
||||||
|
100732
|
||||||
|
100733
|
||||||
|
100734
|
||||||
|
100735
|
||||||
|
100736
|
||||||
|
100737
|
||||||
|
100738
|
||||||
|
100739
|
||||||
|
100740
|
||||||
|
100741
|
||||||
|
100742
|
||||||
|
100743
|
||||||
|
100744
|
||||||
|
100745
|
||||||
|
100746
|
||||||
|
100747
|
||||||
|
100748
|
||||||
|
100749
|
||||||
|
100750
|
||||||
|
100751
|
||||||
|
100752
|
||||||
|
100753
|
||||||
|
100754
|
||||||
|
100755
|
||||||
|
100756
|
||||||
|
100757
|
||||||
|
100758
|
||||||
|
100759
|
||||||
|
100760
|
||||||
|
100761
|
||||||
|
100762
|
||||||
|
100763
|
||||||
|
100764
|
||||||
|
100765
|
||||||
|
100766
|
||||||
|
100767
|
||||||
|
100768
|
||||||
|
100769
|
||||||
|
100770
|
||||||
|
100771
|
||||||
|
100772
|
||||||
|
100773
|
||||||
|
100774
|
||||||
|
100775
|
||||||
|
100776
|
||||||
|
100777
|
||||||
|
100778
|
||||||
|
100779
|
||||||
|
100780
|
||||||
|
100781
|
||||||
|
100782
|
||||||
|
100783
|
||||||
|
100784
|
||||||
|
100785
|
||||||
|
100786
|
||||||
|
100787
|
||||||
|
100788
|
||||||
|
100789
|
||||||
|
100790
|
||||||
|
100791
|
||||||
|
100792
|
||||||
|
100793
|
||||||
|
100794
|
||||||
|
100795
|
||||||
|
100796
|
||||||
|
100797
|
||||||
|
100798
|
||||||
|
100799
|
||||||
|
100800
|
||||||
|
100801
|
||||||
|
100802
|
||||||
|
100803
|
||||||
|
100804
|
||||||
|
100805
|
||||||
|
100806
|
||||||
|
100807
|
||||||
|
100808
|
||||||
|
100809
|
||||||
|
100810
|
||||||
|
100811
|
||||||
|
100812
|
||||||
|
100813
|
||||||
|
100814
|
||||||
|
100815
|
||||||
|
100816
|
||||||
|
100817
|
||||||
|
100818
|
||||||
|
100819
|
||||||
|
100820
|
||||||
|
100821
|
||||||
|
100822
|
||||||
|
100823
|
||||||
|
100824
|
||||||
|
100825
|
||||||
|
100826
|
||||||
|
100827
|
||||||
|
100828
|
||||||
|
100829
|
||||||
|
100830
|
||||||
|
100831
|
||||||
|
100832
|
||||||
|
100833
|
||||||
|
100834
|
||||||
|
100835
|
||||||
|
100836
|
||||||
|
100837
|
||||||
|
100838
|
||||||
|
100839
|
||||||
|
100840
|
||||||
|
100841
|
||||||
|
100842
|
||||||
|
100843
|
||||||
|
100844
|
||||||
|
100845
|
||||||
|
100846
|
||||||
|
100847
|
||||||
|
100848
|
||||||
|
100849
|
||||||
|
100850
|
||||||
|
100851
|
||||||
|
|
@@ -1,5 +1,5 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { Sheet, WholeWord } from "lucide-react";
|
import { Sheet, WholeWord, Search } from "lucide-react";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import ImportGUI from "./ImportGUI";
|
import ImportGUI from "./ImportGUI";
|
||||||
import { removeSelection } from "../utils/tableActions";
|
import { removeSelection } from "../utils/tableActions";
|
||||||
@@ -7,10 +7,17 @@ import { removeSelection } from "../utils/tableActions";
|
|||||||
type SubHeaderAdminProps = {
|
type SubHeaderAdminProps = {
|
||||||
setFiles: (files: File[]) => void;
|
setFiles: (files: File[]) => void;
|
||||||
files?: File[];
|
files?: File[];
|
||||||
|
search: string;
|
||||||
|
setSearch: (value: string) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Sub navigation bar for admin views: provides import + clear selection actions
|
// Sub navigation bar for admin views: provides import + clear selection actions
|
||||||
const SubHeaderAdmin: React.FC<SubHeaderAdminProps> = ({ setFiles, files }) => {
|
const SubHeaderAdmin: React.FC<SubHeaderAdminProps> = ({
|
||||||
|
setFiles,
|
||||||
|
files,
|
||||||
|
search,
|
||||||
|
setSearch,
|
||||||
|
}) => {
|
||||||
const [showImport, setShowImport] = useState(false);
|
const [showImport, setShowImport] = useState(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -30,6 +37,17 @@ const SubHeaderAdmin: React.FC<SubHeaderAdminProps> = ({ setFiles, files }) => {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
|
{/* Search */}
|
||||||
|
<div className="relative hidden sm:block">
|
||||||
|
<Search className="pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-gray-400" />
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={search}
|
||||||
|
onChange={(e) => setSearch(e.target.value)}
|
||||||
|
placeholder="Suchen… (Losnummer, Name, Adresse, PLZ, Email)"
|
||||||
|
className="w-72 rounded-md border border-gray-300 bg-white pl-9 pr-3 py-2 text-sm text-gray-900 placeholder-gray-400 shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={() => setShowImport(true)}
|
onClick={() => setShowImport(true)}
|
||||||
type="button"
|
type="button"
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useMemo, useState } from "react";
|
||||||
import Cookies from "js-cookie";
|
import Cookies from "js-cookie";
|
||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import { getTableData, readCachedTableData } from "../utils/userHandler";
|
import { getTableData, readCachedTableData } from "../utils/userHandler";
|
||||||
@@ -19,6 +19,7 @@ interface DataPackage {
|
|||||||
const Table: React.FC = () => {
|
const Table: React.FC = () => {
|
||||||
const [rows, setRows] = useState<DataPackage[]>([]); // holds normalized cache view
|
const [rows, setRows] = useState<DataPackage[]>([]); // holds normalized cache view
|
||||||
const [files, setFiles] = useState<File[]>([]);
|
const [files, setFiles] = useState<File[]>([]);
|
||||||
|
const [search, setSearch] = useState("");
|
||||||
|
|
||||||
// Einheitliche Input-Styles (nur Tailwind)
|
// Einheitliche Input-Styles (nur Tailwind)
|
||||||
const inputClasses =
|
const inputClasses =
|
||||||
@@ -81,9 +82,33 @@ const Table: React.FC = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Filter rows by search query (case-insensitive)
|
||||||
|
const filteredRows = useMemo(() => {
|
||||||
|
const q = search.trim().toLowerCase();
|
||||||
|
if (!q) return rows;
|
||||||
|
return rows.filter((r) => {
|
||||||
|
const values = [
|
||||||
|
r.losnummer,
|
||||||
|
r.vorname ?? "",
|
||||||
|
r.nachname ?? "",
|
||||||
|
r.adresse ?? "",
|
||||||
|
r.plz ?? "",
|
||||||
|
r.email ?? "",
|
||||||
|
]
|
||||||
|
.join(" ")
|
||||||
|
.toLowerCase();
|
||||||
|
return values.includes(q);
|
||||||
|
});
|
||||||
|
}, [rows, search]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<SubHeaderAdmin setFiles={setFiles} files={files} />
|
<SubHeaderAdmin
|
||||||
|
setFiles={setFiles}
|
||||||
|
files={files}
|
||||||
|
search={search}
|
||||||
|
setSearch={setSearch}
|
||||||
|
/>
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
{/*
|
{/*
|
||||||
<div className="mb-4 flex items-center gap-3">
|
<div className="mb-4 flex items-center gap-3">
|
||||||
@@ -156,7 +181,7 @@ const Table: React.FC = () => {
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody className="divide-y divide-gray-100 bg-white">
|
<tbody className="divide-y divide-gray-100 bg-white">
|
||||||
{rows.length === 0 && !tableQuery.isLoading && (
|
{filteredRows.length === 0 && !tableQuery.isLoading && (
|
||||||
<tr>
|
<tr>
|
||||||
<td
|
<td
|
||||||
colSpan={8}
|
colSpan={8}
|
||||||
@@ -166,7 +191,7 @@ const Table: React.FC = () => {
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
)}
|
)}
|
||||||
{rows.map((row, idx) => (
|
{filteredRows.map((row, idx) => (
|
||||||
<tr
|
<tr
|
||||||
key={row.losnummer ?? idx}
|
key={row.losnummer ?? idx}
|
||||||
className="hover:bg-gray-50 transition-colors"
|
className="hover:bg-gray-50 transition-colors"
|
||||||
|
@@ -46,21 +46,31 @@ export async function postCSV(file: File): Promise<boolean> {
|
|||||||
reader.readAsText(file, "utf-8");
|
reader.readAsText(file, "utf-8");
|
||||||
});
|
});
|
||||||
|
|
||||||
const res = await fetch("http://localhost:8002/create-entry", {
|
const lines = await readerLines;
|
||||||
method: "POST",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
losnummer: await readerLines,
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
if (res.ok) {
|
// Send token if available
|
||||||
myToast("CSV Datei erfolgreich importiert!", "success");
|
const token = (await import("js-cookie")).default.get("token");
|
||||||
queryClient.invalidateQueries({ queryKey: ["table-data"] });
|
|
||||||
return true;
|
// Chunk uploads to avoid huge single payloads
|
||||||
|
const chunkSize = 2000; // ~2k per request => 25 requests for 50k
|
||||||
|
for (let i = 0; i < lines.length; i += chunkSize) {
|
||||||
|
const chunk = lines.slice(i, i + chunkSize);
|
||||||
|
const res = await fetch("http://localhost:8002/create-entry", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
...(token ? { Authorization: `Bearer ${token}` } : {}),
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ losnummer: chunk }),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!res.ok) {
|
||||||
|
myToast(`Fehler beim Importieren (Batch ${i / chunkSize + 1}).`, "error");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
myToast("Fehler beim Importieren der CSV Datei.", "error");
|
|
||||||
return false;
|
myToast("CSV Datei erfolgreich importiert!", "success");
|
||||||
|
queryClient.invalidateQueries({ queryKey: ["table-data"] });
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user