Organize imports: drag & drop, virtualization, dupes, undo, days select, localStorage
CI / check (push) Failing after 5m4s
CI / preflight (push) Skipped
CI / deploy (push) Skipped
CI / publish-container (push) Skipped

- Native HTML5 drag & drop between categories with drop-target highlight
- Virtualized item lists via @tanstack/react-virtual (fixed 34px rows)
- Auto-batching of large groups (500 items / 50 groups per run)
- Duplicate detection against the destination page with badge + summary
- Per-category layout preview grid
- Undo history for item moves (single + batch, tracks source groups)
- Days-range selector to load older imports (30/60/90/180)
- LocalStorage persistence of user settings (mode, destination, price, days)
- Added translation keys across all 25 locales
This commit is contained in:
openhands committed 2026-09-14 18:40:50 +02:00
1 parent 644d53ca16
commit 3a4089a5fa
26 files changed
+959 -260

No files matched your search

File diff suppressed because it is too large. Load diff
+11 -1
View File
@@ -926,7 +926,17 @@
"moveItemTo": "Move to…",
"batchMoveTo": "Move selected to…",
"selectedCount": "{count} selected",
"clearSelection": "Clear selection"
"clearSelection": "Clear selection",
"undo": "Undo",
"daysLabel": "Days",
"daysValue": "{days} days",
"dupeSummary": "Already in the destination page: {count} item{count, plural, one {} other {s}}",
"layoutPreview": "Layout preview",
"emptyCategory": "No items in this category",
"dupeOnDest": "Already present on the destination page",
"dupeOnDestShort": "duplicate",
"errorPartial": "{failed} of {total} runs failed. Review and retry.",
"successCreateRuns": "Created {count, plural, one {1 page} other {# pages}} in {runs, plural, one {1 run} other {# runs}} with {moved} moved and {added} added furniture"
}
},
"discovery": {
+11 -1
View File
@@ -2053,7 +2053,17 @@
"moveItemTo": "Move to…",
"batchMoveTo": "Move selected to…",
"selectedCount": "{count} selected",
"clearSelection": "Clear selection"
"clearSelection": "Clear selection",
"undo": "Undo",
"daysLabel": "Days",
"daysValue": "{days} days",
"dupeSummary": "Already in the destination page: {count} item{count, plural, one {} other {s}}",
"layoutPreview": "Layout preview",
"emptyCategory": "No items in this category",
"dupeOnDest": "Already present on the destination page",
"dupeOnDestShort": "duplicate",
"errorPartial": "{failed} of {total} runs failed. Review and retry.",
"successCreateRuns": "Created {count, plural, one {1 page} other {# pages}} in {runs, plural, one {1 run} other {# runs}} with {moved} moved and {added} added furniture"
}
},
"discovery": {
+11 -1
View File
@@ -2053,7 +2053,17 @@
"moveItemTo": "Move to…",
"batchMoveTo": "Move selected to…",
"selectedCount": "{count} selected",
"clearSelection": "Clear selection"
"clearSelection": "Clear selection",
"undo": "Undo",
"daysLabel": "Days",
"daysValue": "{days} days",
"dupeSummary": "Already in the destination page: {count} item{count, plural, one {} other {s}}",
"layoutPreview": "Layout preview",
"emptyCategory": "No items in this category",
"dupeOnDest": "Already present on the destination page",
"dupeOnDestShort": "duplicate",
"errorPartial": "{failed} of {total} runs failed. Review and retry.",
"successCreateRuns": "Created {count, plural, one {1 page} other {# pages}} in {runs, plural, one {1 run} other {# runs}} with {moved} moved and {added} added furniture"
}
},
"discovery": {
+11 -1
View File
@@ -2053,7 +2053,17 @@
"moveItemTo": "Move to…",
"batchMoveTo": "Move selected to…",
"selectedCount": "{count} selected",
"clearSelection": "Clear selection"
"clearSelection": "Clear selection",
"undo": "Undo",
"daysLabel": "Days",
"daysValue": "{days} days",
"dupeSummary": "Already in the destination page: {count} item{count, plural, one {} other {s}}",
"layoutPreview": "Layout preview",
"emptyCategory": "No items in this category",
"dupeOnDest": "Already present on the destination page",
"dupeOnDestShort": "duplicate",
"errorPartial": "{failed} of {total} runs failed. Review and retry.",
"successCreateRuns": "Created {count, plural, one {1 page} other {# pages}} in {runs, plural, one {1 run} other {# runs}} with {moved} moved and {added} added furniture"
}
},
"discovery": {
+11 -1
View File
@@ -2023,7 +2023,17 @@
"moveItemTo": "Verschieben zu…",
"batchMoveTo": "Auswahl verschieben zu…",
"selectedCount": "{count} ausgewählt",
"clearSelection": "Auswahl aufheben"
"clearSelection": "Auswahl aufheben",
"undo": "Undo",
"daysLabel": "Days",
"daysValue": "{days} days",
"dupeSummary": "Already in the destination page: {count} item{count, plural, one {} other {s}}",
"layoutPreview": "Layout preview",
"emptyCategory": "No items in this category",
"dupeOnDest": "Already present on the destination page",
"dupeOnDestShort": "duplicate",
"errorPartial": "{failed} of {total} runs failed. Review and retry.",
"successCreateRuns": "Created {count, plural, one {1 page} other {# pages}} in {runs, plural, one {1 run} other {# runs}} with {moved} moved and {added} added furniture"
}
},
"discovery": {
+11 -1
View File
@@ -2053,7 +2053,17 @@
"moveItemTo": "Move to…",
"batchMoveTo": "Move selected to…",
"selectedCount": "{count} selected",
"clearSelection": "Clear selection"
"clearSelection": "Clear selection",
"undo": "Undo",
"daysLabel": "Days",
"daysValue": "{days} days",
"dupeSummary": "Already in the destination page: {count} item{count, plural, one {} other {s}}",
"layoutPreview": "Layout preview",
"emptyCategory": "No items in this category",
"dupeOnDest": "Already present on the destination page",
"dupeOnDestShort": "duplicate",
"errorPartial": "{failed} of {total} runs failed. Review and retry.",
"successCreateRuns": "Created {count, plural, one {1 page} other {# pages}} in {runs, plural, one {1 run} other {# runs}} with {moved} moved and {added} added furniture"
}
},
"discovery": {
+11 -1
View File
@@ -2279,7 +2279,17 @@
"moveItemTo": "Move to…",
"batchMoveTo": "Move selected to…",
"selectedCount": "{count} selected",
"clearSelection": "Clear selection"
"clearSelection": "Clear selection",
"undo": "Undo",
"daysLabel": "Days",
"daysValue": "{days} days",
"dupeSummary": "Already in the destination page: {count} item{count, plural, one {} other {s}}",
"layoutPreview": "Layout preview",
"emptyCategory": "No items in this category",
"dupeOnDest": "Already present on the destination page",
"dupeOnDestShort": "duplicate",
"errorPartial": "{failed} of {total} runs failed. Review and retry.",
"successCreateRuns": "Created {count, plural, one {1 page} other {# pages}} in {runs, plural, one {1 run} other {# runs}} with {moved} moved and {added} added furniture"
}
},
"bulk": {
+11 -1
View File
@@ -2023,7 +2023,17 @@
"moveItemTo": "Mover a…",
"batchMoveTo": "Mover seleccionados a…",
"selectedCount": "{count} seleccionados",
"clearSelection": "Limpiar selección"
"clearSelection": "Limpiar selección",
"undo": "Undo",
"daysLabel": "Days",
"daysValue": "{days} days",
"dupeSummary": "Already in the destination page: {count} item{count, plural, one {} other {s}}",
"layoutPreview": "Layout preview",
"emptyCategory": "No items in this category",
"dupeOnDest": "Already present on the destination page",
"dupeOnDestShort": "duplicate",
"errorPartial": "{failed} of {total} runs failed. Review and retry.",
"successCreateRuns": "Created {count, plural, one {1 page} other {# pages}} in {runs, plural, one {1 run} other {# runs}} with {moved} moved and {added} added furniture"
}
},
"discovery": {
+11 -1
View File
@@ -926,7 +926,17 @@
"moveItemTo": "Move to…",
"batchMoveTo": "Move selected to…",
"selectedCount": "{count} selected",
"clearSelection": "Clear selection"
"clearSelection": "Clear selection",
"undo": "Undo",
"daysLabel": "Days",
"daysValue": "{days} days",
"dupeSummary": "Already in the destination page: {count} item{count, plural, one {} other {s}}",
"layoutPreview": "Layout preview",
"emptyCategory": "No items in this category",
"dupeOnDest": "Already present on the destination page",
"dupeOnDestShort": "duplicate",
"errorPartial": "{failed} of {total} runs failed. Review and retry.",
"successCreateRuns": "Created {count, plural, one {1 page} other {# pages}} in {runs, plural, one {1 run} other {# runs}} with {moved} moved and {added} added furniture"
}
},
"discovery": {
+11 -1
View File
@@ -2023,7 +2023,17 @@
"moveItemTo": "Déplacer vers…",
"batchMoveTo": "Déplacer la sélection vers…",
"selectedCount": "{count} sélectionné(s)",
"clearSelection": "Effacer la sélection"
"clearSelection": "Effacer la sélection",
"undo": "Undo",
"daysLabel": "Days",
"daysValue": "{days} days",
"dupeSummary": "Already in the destination page: {count} item{count, plural, one {} other {s}}",
"layoutPreview": "Layout preview",
"emptyCategory": "No items in this category",
"dupeOnDest": "Already present on the destination page",
"dupeOnDestShort": "duplicate",
"errorPartial": "{failed} of {total} runs failed. Review and retry.",
"successCreateRuns": "Created {count, plural, one {1 page} other {# pages}} in {runs, plural, one {1 run} other {# runs}} with {moved} moved and {added} added furniture"
}
},
"discovery": {
+11 -1
View File
@@ -2053,7 +2053,17 @@
"moveItemTo": "Move to…",
"batchMoveTo": "Move selected to…",
"selectedCount": "{count} selected",
"clearSelection": "Clear selection"
"clearSelection": "Clear selection",
"undo": "Undo",
"daysLabel": "Days",
"daysValue": "{days} days",
"dupeSummary": "Already in the destination page: {count} item{count, plural, one {} other {s}}",
"layoutPreview": "Layout preview",
"emptyCategory": "No items in this category",
"dupeOnDest": "Already present on the destination page",
"dupeOnDestShort": "duplicate",
"errorPartial": "{failed} of {total} runs failed. Review and retry.",
"successCreateRuns": "Created {count, plural, one {1 page} other {# pages}} in {runs, plural, one {1 run} other {# runs}} with {moved} moved and {added} added furniture"
}
},
"discovery": {
+11 -1
View File
@@ -2053,7 +2053,17 @@
"moveItemTo": "Move to…",
"batchMoveTo": "Move selected to…",
"selectedCount": "{count} selected",
"clearSelection": "Clear selection"
"clearSelection": "Clear selection",
"undo": "Undo",
"daysLabel": "Days",
"daysValue": "{days} days",
"dupeSummary": "Already in the destination page: {count} item{count, plural, one {} other {s}}",
"layoutPreview": "Layout preview",
"emptyCategory": "No items in this category",
"dupeOnDest": "Already present on the destination page",
"dupeOnDestShort": "duplicate",
"errorPartial": "{failed} of {total} runs failed. Review and retry.",
"successCreateRuns": "Created {count, plural, one {1 page} other {# pages}} in {runs, plural, one {1 run} other {# runs}} with {moved} moved and {added} added furniture"
}
},
"discovery": {
+11 -1
View File
@@ -2198,7 +2198,17 @@
"moveItemTo": "Sposta verso…",
"batchMoveTo": "Sposta la selezione verso…",
"selectedCount": "{count} selezionati",
"clearSelection": "Azzera selezione"
"clearSelection": "Azzera selezione",
"undo": "Undo",
"daysLabel": "Days",
"daysValue": "{days} days",
"dupeSummary": "Already in the destination page: {count} item{count, plural, one {} other {s}}",
"layoutPreview": "Layout preview",
"emptyCategory": "No items in this category",
"dupeOnDest": "Already present on the destination page",
"dupeOnDestShort": "duplicate",
"errorPartial": "{failed} of {total} runs failed. Review and retry.",
"successCreateRuns": "Created {count, plural, one {1 page} other {# pages}} in {runs, plural, one {1 run} other {# runs}} with {moved} moved and {added} added furniture"
}
},
"bulk": {
+11 -1
View File
@@ -926,7 +926,17 @@
"moveItemTo": "Move to…",
"batchMoveTo": "Move selected to…",
"selectedCount": "{count} selected",
"clearSelection": "Clear selection"
"clearSelection": "Clear selection",
"undo": "Undo",
"daysLabel": "Days",
"daysValue": "{days} days",
"dupeSummary": "Already in the destination page: {count} item{count, plural, one {} other {s}}",
"layoutPreview": "Layout preview",
"emptyCategory": "No items in this category",
"dupeOnDest": "Already present on the destination page",
"dupeOnDestShort": "duplicate",
"errorPartial": "{failed} of {total} runs failed. Review and retry.",
"successCreateRuns": "Created {count, plural, one {1 page} other {# pages}} in {runs, plural, one {1 run} other {# runs}} with {moved} moved and {added} added furniture"
}
},
"discovery": {
+11 -1
View File
@@ -2143,7 +2143,17 @@
"moveItemTo": "Verplaatsen naar…",
"batchMoveTo": "Geselecteerde verplaatsen naar…",
"selectedCount": "{count} geselecteerd",
"clearSelection": "Selectie wissen"
"clearSelection": "Selectie wissen",
"undo": "Ongedaan maken",
"daysLabel": "Dagen",
"daysValue": "{days} dagen",
"dupeSummary": "Al aanwezig op de bestemmingspagina: {count} item{count, plural, one {} other {s}}",
"layoutPreview": "Layoutvoorbeeld",
"emptyCategory": "Geen items in deze categorie",
"dupeOnDest": "Staat al op de bestemmingspagina",
"dupeOnDestShort": "duplicaat",
"errorPartial": "{failed} van de {total} rondes mislukt. Controleer en probeer opnieuw.",
"successCreateRuns": "{count, plural, one {1 pagina} other {# pagina's}} aangemaakt in {runs, plural, one {1 ronde} other {# rondes}} met {moved} verplaatst en {added} nieuw meubilair"
}
},
"bulk": {
+11 -1
View File
@@ -2053,7 +2053,17 @@
"moveItemTo": "Move to…",
"batchMoveTo": "Move selected to…",
"selectedCount": "{count} selected",
"clearSelection": "Clear selection"
"clearSelection": "Clear selection",
"undo": "Undo",
"daysLabel": "Days",
"daysValue": "{days} days",
"dupeSummary": "Already in the destination page: {count} item{count, plural, one {} other {s}}",
"layoutPreview": "Layout preview",
"emptyCategory": "No items in this category",
"dupeOnDest": "Already present on the destination page",
"dupeOnDestShort": "duplicate",
"errorPartial": "{failed} of {total} runs failed. Review and retry.",
"successCreateRuns": "Created {count, plural, one {1 page} other {# pages}} in {runs, plural, one {1 run} other {# runs}} with {moved} moved and {added} added furniture"
}
},
"discovery": {
+11 -1
View File
@@ -2053,7 +2053,17 @@
"moveItemTo": "Move to…",
"batchMoveTo": "Move selected to…",
"selectedCount": "{count} selected",
"clearSelection": "Clear selection"
"clearSelection": "Clear selection",
"undo": "Undo",
"daysLabel": "Days",
"daysValue": "{days} days",
"dupeSummary": "Already in the destination page: {count} item{count, plural, one {} other {s}}",
"layoutPreview": "Layout preview",
"emptyCategory": "No items in this category",
"dupeOnDest": "Already present on the destination page",
"dupeOnDestShort": "duplicate",
"errorPartial": "{failed} of {total} runs failed. Review and retry.",
"successCreateRuns": "Created {count, plural, one {1 page} other {# pages}} in {runs, plural, one {1 run} other {# runs}} with {moved} moved and {added} added furniture"
}
},
"discovery": {
+11 -1
View File
@@ -2053,7 +2053,17 @@
"moveItemTo": "Mover para…",
"batchMoveTo": "Mover seleção para…",
"selectedCount": "{count} selecionados",
"clearSelection": "Limpar seleção"
"clearSelection": "Limpar seleção",
"undo": "Undo",
"daysLabel": "Days",
"daysValue": "{days} days",
"dupeSummary": "Already in the destination page: {count} item{count, plural, one {} other {s}}",
"layoutPreview": "Layout preview",
"emptyCategory": "No items in this category",
"dupeOnDest": "Already present on the destination page",
"dupeOnDestShort": "duplicate",
"errorPartial": "{failed} of {total} runs failed. Review and retry.",
"successCreateRuns": "Created {count, plural, one {1 page} other {# pages}} in {runs, plural, one {1 run} other {# runs}} with {moved} moved and {added} added furniture"
}
},
"discovery": {
+11 -1
View File
@@ -2053,7 +2053,17 @@
"moveItemTo": "Move to…",
"batchMoveTo": "Move selected to…",
"selectedCount": "{count} selected",
"clearSelection": "Clear selection"
"clearSelection": "Clear selection",
"undo": "Undo",
"daysLabel": "Days",
"daysValue": "{days} days",
"dupeSummary": "Already in the destination page: {count} item{count, plural, one {} other {s}}",
"layoutPreview": "Layout preview",
"emptyCategory": "No items in this category",
"dupeOnDest": "Already present on the destination page",
"dupeOnDestShort": "duplicate",
"errorPartial": "{failed} of {total} runs failed. Review and retry.",
"successCreateRuns": "Created {count, plural, one {1 page} other {# pages}} in {runs, plural, one {1 run} other {# runs}} with {moved} moved and {added} added furniture"
}
},
"discovery": {
+11 -1
View File
@@ -2051,7 +2051,17 @@
"moveItemTo": "Move to…",
"batchMoveTo": "Move selected to…",
"selectedCount": "{count} selected",
"clearSelection": "Clear selection"
"clearSelection": "Clear selection",
"undo": "Undo",
"daysLabel": "Days",
"daysValue": "{days} days",
"dupeSummary": "Already in the destination page: {count} item{count, plural, one {} other {s}}",
"layoutPreview": "Layout preview",
"emptyCategory": "No items in this category",
"dupeOnDest": "Already present on the destination page",
"dupeOnDestShort": "duplicate",
"errorPartial": "{failed} of {total} runs failed. Review and retry.",
"successCreateRuns": "Created {count, plural, one {1 page} other {# pages}} in {runs, plural, one {1 run} other {# runs}} with {moved} moved and {added} added furniture"
}
},
"discovery": {
+11 -1
View File
@@ -2053,7 +2053,17 @@
"moveItemTo": "Move to…",
"batchMoveTo": "Move selected to…",
"selectedCount": "{count} selected",
"clearSelection": "Clear selection"
"clearSelection": "Clear selection",
"undo": "Undo",
"daysLabel": "Days",
"daysValue": "{days} days",
"dupeSummary": "Already in the destination page: {count} item{count, plural, one {} other {s}}",
"layoutPreview": "Layout preview",
"emptyCategory": "No items in this category",
"dupeOnDest": "Already present on the destination page",
"dupeOnDestShort": "duplicate",
"errorPartial": "{failed} of {total} runs failed. Review and retry.",
"successCreateRuns": "Created {count, plural, one {1 page} other {# pages}} in {runs, plural, one {1 run} other {# runs}} with {moved} moved and {added} added furniture"
}
},
"discovery": {
+11 -1
View File
@@ -2053,7 +2053,17 @@
"moveItemTo": "Move to…",
"batchMoveTo": "Move selected to…",
"selectedCount": "{count} selected",
"clearSelection": "Clear selection"
"clearSelection": "Clear selection",
"undo": "Undo",
"daysLabel": "Days",
"daysValue": "{days} days",
"dupeSummary": "Already in the destination page: {count} item{count, plural, one {} other {s}}",
"layoutPreview": "Layout preview",
"emptyCategory": "No items in this category",
"dupeOnDest": "Already present on the destination page",
"dupeOnDestShort": "duplicate",
"errorPartial": "{failed} of {total} runs failed. Review and retry.",
"successCreateRuns": "Created {count, plural, one {1 page} other {# pages}} in {runs, plural, one {1 run} other {# runs}} with {moved} moved and {added} added furniture"
}
},
"discovery": {
+11 -1
View File
@@ -2053,7 +2053,17 @@
"moveItemTo": "Move to…",
"batchMoveTo": "Move selected to…",
"selectedCount": "{count} selected",
"clearSelection": "Clear selection"
"clearSelection": "Clear selection",
"undo": "Undo",
"daysLabel": "Days",
"daysValue": "{days} days",
"dupeSummary": "Already in the destination page: {count} item{count, plural, one {} other {s}}",
"layoutPreview": "Layout preview",
"emptyCategory": "No items in this category",
"dupeOnDest": "Already present on the destination page",
"dupeOnDestShort": "duplicate",
"errorPartial": "{failed} of {total} runs failed. Review and retry.",
"successCreateRuns": "Created {count, plural, one {1 page} other {# pages}} in {runs, plural, one {1 run} other {# runs}} with {moved} moved and {added} added furniture"
}
},
"discovery": {
+11 -1
View File
@@ -2053,7 +2053,17 @@
"moveItemTo": "Taşı…",
"batchMoveTo": "Seçimi taşı…",
"selectedCount": "{count} seçildi",
"clearSelection": "Seçimi temizle"
"clearSelection": "Seçimi temizle",
"undo": "Undo",
"daysLabel": "Days",
"daysValue": "{days} days",
"dupeSummary": "Already in the destination page: {count} item{count, plural, one {} other {s}}",
"layoutPreview": "Layout preview",
"emptyCategory": "No items in this category",
"dupeOnDest": "Already present on the destination page",
"dupeOnDestShort": "duplicate",
"errorPartial": "{failed} of {total} runs failed. Review and retry.",
"successCreateRuns": "Created {count, plural, one {1 page} other {# pages}} in {runs, plural, one {1 run} other {# runs}} with {moved} moved and {added} added furniture"
}
},
"discovery": {
+11 -1
View File
@@ -2053,7 +2053,17 @@
"moveItemTo": "Move to…",
"batchMoveTo": "Move selected to…",
"selectedCount": "{count} selected",
"clearSelection": "Clear selection"
"clearSelection": "Clear selection",
"undo": "Undo",
"daysLabel": "Days",
"daysValue": "{days} days",
"dupeSummary": "Already in the destination page: {count} item{count, plural, one {} other {s}}",
"layoutPreview": "Layout preview",
"emptyCategory": "No items in this category",
"dupeOnDest": "Already present on the destination page",
"dupeOnDestShort": "duplicate",
"errorPartial": "{failed} of {total} runs failed. Review and retry.",
"successCreateRuns": "Created {count, plural, one {1 page} other {# pages}} in {runs, plural, one {1 run} other {# runs}} with {moved} moved and {added} added furniture"
}
},
"discovery": {