From 01a7e6ee938fb4119af4d377607ab7ca5fc1b1ba Mon Sep 17 00:00:00 2001 From: maziggy Date: Fri, 1 May 2026 12:34:56 +0200 Subject: [PATCH] fix(archive,vp): strip .gcode.3mf properly + sync review/archive name (#1152) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit @smandon retested the original #1152 fix on the latest daily and surfaced two distinct holes: 1. ``Path(name).stem`` only strips the *last* suffix, so Bambu Studio's default ``Plate_1.gcode.3mf`` exports landed in the archive UI as ``Plate_1.gcode`` — never the bare ``Plate_1`` the user expected. 2. The pending-uploads review card always showed the raw FTP filename, while the eventual ``PrintArchive.print_name`` resolved from the 3MF's embedded title (or, with the toggle on ``filename``, the stripped stem). Net effect: same upload showed two different names depending on which view you were looking at, with no way for the toggle to flip both views in lockstep. Three changes: - ``resolve_display_stem`` helper in ``services/archive.py`` strips ``.gcode.3mf`` / ``.3mf`` / ``.gcode`` (case-insensitive). Applied at the archive-creation site so ``Plate_1.gcode.3mf`` → ``Plate_1`` for every flow that produces a ``PrintArchive`` row. - ``PendingUpload.metadata_print_name`` (new nullable column) is populated at FTP-receive time by peeking at the 3MF's embedded title via the existing ``ThreeMFParser``. Read happens once per upload — the list endpoint then doesn't have to reopen each 3MF on every render. Parser failures are swallowed and the column stays NULL; the response model gracefully falls back to the stripped filename. - ``PendingUploadResponse.display_name`` is a computed field that mirrors ``archive_print``'s exact precedence — ``filename`` toggle → stripped stem; ``metadata`` toggle (default) → cached title or stripped stem. The frontend's review card reads it (with ``upload.filename`` as a defensive fallback) and surfaces the raw FTP filename via tooltip so users can still inspect what arrived. Migration is one idempotent ``ALTER TABLE pending_uploads ADD COLUMN metadata_print_name VARCHAR(255)`` (Postgres/SQLite-safe). Pre-migration rows have NULL and degrade to filename-stem behaviour without any operator action. Tests: 14 unit tests in ``test_archive_display_stem.py`` covering the canonical normalisation rules (Bambu Studio default name, mixed case, dots-in-the-middle, edge cases like ``.gcode.3mf``-only, full-path inputs); 6 integration tests in ``test_pending_upload_display_name.py`` pinning the response contract (default toggle uses metadata title when present, falls back to stripped stem when absent, ``filename`` toggle overrides metadata, ``filename`` toggle still strips the double suffix, ``GET /{id}`` exposes the same field, whitespace-only metadata behaves like absent); 3 frontend tests in ``PendingUploadsPanel.test.tsx`` pinning the review card's render path (resolved name shown, fallback to filename when display_name is empty, raw filename available via tooltip). Full backend suite: 3598 passed; frontend build clean; no regressions in any flow that previously processed ``.3mf`` / ``.gcode`` / non-3D filenames. --- CHANGELOG.md | 2 + backend/app/api/routes/pending_uploads.py | 51 +++++- backend/app/core/database.py | 10 ++ backend/app/models/pending_upload.py | 6 + backend/app/services/archive.py | 27 ++- .../app/services/virtual_printer/manager.py | 17 ++ .../test_pending_upload_display_name.py | 159 ++++++++++++++++++ .../services/test_archive_display_stem.py | 51 ++++++ .../components/PendingUploadsPanel.test.tsx | 78 +++++++++ frontend/src/api/client.ts | 5 + .../src/components/PendingUploadsPanel.tsx | 2 +- .../{index-BSfASPDE.js => index-BlyoHMPZ.js} | 2 +- static/index.html | 2 +- 13 files changed, 405 insertions(+), 7 deletions(-) create mode 100644 backend/tests/integration/test_pending_upload_display_name.py create mode 100644 backend/tests/unit/services/test_archive_display_stem.py create mode 100644 frontend/src/__tests__/components/PendingUploadsPanel.test.tsx rename static/assets/{index-BSfASPDE.js => index-BlyoHMPZ.js} (99%) diff --git a/CHANGELOG.md b/CHANGELOG.md index ca5f4ee6f..ca4e13808 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,8 @@ All notable changes to Bambuddy will be documented in this file. - **Filament Track Switch (FTS) support — print modal filament dropdown is no longer empty when an X2D / H2D has the FTS accessory installed** ([#1162](https://github.com/maziggy/bambuddy/issues/1162), reported by @mkavalecz) — When the FTS accessory is installed the printer's MQTT changes one nibble of the per-AMS `info` bitmask: bits 8-11 flip from a fixed extruder ID (0x0 / 0x1) to `0xE` ("uninitialized"), because the AMS is no longer wired to a single nozzle — the FTS dynamically routes any slot to either extruder. Bambuddy's MQTT parser already skipped 0xE entries when building `ams_extruder_map` (matching BambuStudio's reading for boot-time transient state), so with the FTS installed the map ended up empty and the print modal's filament dropdown — which filters by `extruderId === nozzle_id` to prevent cross-nozzle assignment ("position of left hotend is abnormal" failures) — filtered out *every* loaded slot. Net effect: empty Filament Mapping dropdown on every dual-nozzle print with the FTS, even when the AMS was fully loaded with the right material. Detection comes from a new MQTT field — `print.device.fila_switch` — which is non-null only when the accessory is installed; it carries the routing topology as two arrays: `in[track] = currently fed slot (-1 = empty)` and `out[track] = extruder this track terminates at`. The fix surfaces this through a new `FilaSwitchState` dataclass on `PrinterState` (`installed`, `in_slots`, `out_extruders`, `stat`, `info`) and the equivalent `FilaSwitchResponse` Pydantic schema on the `GET /printers/{id}/status` route. Frontend (`useFilamentMapping.ts` + `FilamentMapping.tsx`) skips the per-extruder filter when `printerStatus.fila_switch?.installed === true` so any compatible AMS slot can satisfy any nozzle's filament requirement, since the FTS handles the routing. Slots currently fed into a track also get a routing badge in the dropdown — `[L]` or `[R]` — so the user can tell at a glance which slot the FTS is currently routing where (idle slots get no badge: they can be routed to either extruder on demand). The hard "no cross-nozzle assignment" filter on real dual-nozzle printers without the FTS stays untouched (still trips the same way it always has — `fila_switch == null` keeps the existing behaviour). 4 backend tests in `test_bambu_mqtt.py::TestFilamentTrackSwitchDetection` (default-not-installed, detect-from-MQTT-using-the-reporter's-bundle, no-fila_switch-field-stays-not-installed, missing-in-out-arrays-don't-crash) and 2 frontend tests in `useFilamentMapping.test.ts` (FTS-active drops the nozzle filter; explicit `fila_switch: null` keeps the filter applied). Upstream fila_switch payloads with anything other than the documented shape are tolerated — `installed` flips on the *presence* of the field, the routing arrays default to empty lists if missing, and the dropdown skips the badge for slots not currently in `in_slots`. ### Fixed +- **Pending review card and the resulting archive name disagreed; `.gcode.3mf` filename suffix wasn't fully stripped** ([#1152](https://github.com/maziggy/bambuddy/issues/1152) follow-up, reported by @smandon) — Two distinct holes in the original #1152 fix surfaced when @smandon retested on the daily build. **(1) Suffix stripping was incomplete:** Bambu Studio's "Send to printer" dialog typically writes files like `Plate_1.gcode.3mf` (a sliced gcode payload wrapped in a 3MF container), but the archive's display stem was computed via `Path(name).stem`, which only drops the *last* suffix and left the user staring at `Plate_1.gcode` in the archive UI. **(2) The review card and the archive disagreed on what the print was called:** the pending-uploads panel always rendered the raw FTP filename, while the eventual `PrintArchive.print_name` resolved from the 3MF's embedded title (or, with the toggle on `filename`, the filename stem). Net effect: the user saw `Plate_1.gcode` in the review card and `Some Creator's Title` in the archive grid for the same item, with no toggle that flipped both views in lockstep. Fix has three pieces: a new `resolve_display_stem()` helper in `archive.py` that strips `.gcode.3mf` / `.3mf` / `.gcode` (case-insensitive) so both the archive *and* the review-side normalisation produce the same canonical stem; a new `PendingUpload.metadata_print_name` column populated at FTP-receive time by peeking at the 3MF's embedded title (so `/pending-uploads/` list calls don't have to reopen every 3MF on every render); and a new `PendingUploadResponse.display_name` computed field that mirrors `archive_print`'s exact precedence — `filename` toggle: stripped stem; `metadata` toggle (default): cached title or stripped stem. Frontend's `PendingUploadsPanel` reads `upload.display_name` (with `upload.filename` as a defensive fallback for any pre-migration row), and the raw filename is exposed as a tooltip so users can still inspect what actually arrived over FTP. Migration is one idempotent `ALTER TABLE pending_uploads ADD COLUMN metadata_print_name VARCHAR(255)` (Postgres/SQLite-safe); existing pending rows have NULL there and gracefully fall back to filename-stem behaviour. 14 unit tests pin the stripping rules (`Plate_1.gcode.3mf` → `Plate_1`, mixed case, dots in the middle, edge `.3mf`-only / `.gcode`-only, full-path inputs); 6 integration tests pin the response contract (default toggle uses metadata title when present, falls back to stripped stem when absent, `filename` toggle overrides metadata, `filename` toggle still strips the double suffix, `GET /{id}` exposes the same field, whitespace-only metadata behaves like absent); 3 frontend tests pin the review card's render path (resolved name shown, fallback to filename when display_name is empty, raw filename available via tooltip). + - **SpoolBuddy SSH update fails with "permission denied for user spoolbuddy" after Bambuddy keypair rotation** (reported during user testing) — Bambuddy's data dir at `/spoolbuddy/ssh/` can get recreated outside the daemon's control (volume remount, container recreate, fresh deploy), at which point `get_or_create_keypair()` generates a new ed25519 keypair. The SpoolBuddy daemon previously only fetched and deployed Bambuddy's public key at registration time (`/devices/register`), so any rotation after a successful registration left the device's `~/.ssh/authorized_keys` pointing at a defunct public half — every "Update" click from the Bambuddy UI then failed with `Connection closed by authenticating user spoolbuddy [preauth]` until the daemon was restarted manually. Worse, every prior successful registration appended a fresh entry to `authorized_keys` without ever pruning the old one, so a typical device accumulated 5+ stale Bambuddy-tagged keys (each one a permanent backdoor for whichever Bambuddy keypair held the matching private half at the time it was deployed). Two-pronged fix: **(1)** the heartbeat response (`HeartbeatResponse`, `routes/spoolbuddy.py:282`) now carries the current `ssh_public_key` alongside the existing `pending_command` / calibration fields, so the daemon's heartbeat picks up a key rotation within one cycle instead of needing a service restart; the same `try/except Exception: pass` pattern as the registration response keeps a missing/unreadable backend key from breaking telemetry. **(2)** `_deploy_ssh_key()` in `daemon/main.py` now syncs rather than appends — it strips every line tagged `bambuddy-spoolbuddy`, writes the current key once, and is a no-op when already in sync (so it doesn't churn the file every heartbeat). User-managed entries (any line not tagged `bambuddy-spoolbuddy`) are preserved untouched. 5 new unit tests in `spoolbuddy/tests/test_deploy_ssh_key.py` (creates-when-missing → mode-600 file with the current key; pile-up-of-stale-keys → only current key remains, no growth; preserves-unrelated-user-keys → user's own SSH access untouched; idempotent-when-in-sync → no mtime change so heartbeat doesn't churn the file; swallows-write-errors → readonly-fs PermissionError doesn't crash the heartbeat loop). 2 new backend integration tests in `test_spoolbuddy.py::TestDeviceEndpoints` — `test_heartbeat_returns_ssh_public_key` (response carries the key on every heartbeat) and `test_heartbeat_ssh_key_failure_does_not_break_heartbeat` (backend key-read failure leaves `ssh_public_key: None` but the heartbeat still 200s). - **External-camera frames returned as black on go2rtc and other MJPEG sources** ([#1177](https://github.com/maziggy/bambuddy/issues/1177), reported by @nkm8) — `_capture_mjpeg_frame` returned the very first JPEG it found in the stream's bytes (`backend/app/services/external_camera.py:282`), but many MJPEG sources — go2rtc most notably, and several IP cameras — emit a "warm-up" frame on the byte that follows connection accept: usually the last keyframe held in the encoder, which is often black or stale until the encoder catches up to live content. Subsequent frames on the same connection are fine. The reporter saw it across snapshot UX, finish photos in notifications, and timelapse — every code path that opens a fresh capture connection (snapshot endpoint, `[PHOTO-BG]` finish photo, plate-detection CV, Obico ML inference, layer timelapse, Settings → Test). His own observation that go2rtc's `/api/frame.jpeg` (single-frame, internally already warmed) is never black while the first frame off `/api/stream.mjpeg` is, matched the hypothesis exactly. Support-bundle evidence was clean: every black notification frame in his log was 11095 bytes (a pure-black 1280×720 JPEG encodes to ~10–15 KB on standard libjpeg quality settings), while every captured-after-warm-up frame from the same source was 30–45 KB. Fix: read past the first frame and return the second; if the connection closes / times out / hits the 5 MB buffer cap before a second frame ever arrives, fall back to the first so callers still get *something* (degrading slow / single-frame streams to None would regress every code path that relied on pre-fix behaviour). The inner-loop now drains every complete frame already in the buffer before pulling the next chunk so high-FPS sources that pack multiple frames per chunk are handled correctly. The `snapshot` / `rtsp` / `usb` capture paths and the live-view streaming endpoint (`generate_mjpeg_stream`) are untouched. 7 new regression tests in `test_external_camera.py::TestCaptureMjpegFrameWarmupSkip` cover (a) two-frames-in-two-chunks → second returned, (b) two-frames-in-one-chunk → second returned, (c) frame split across chunk boundary → assembled correctly, (d) single-frame stream → first returned via fallback (no None regression), (e) timeout after first frame → first returned via fallback, (f) zero-frame stream → None, (g) non-200 status → None. Latency penalty: at most one frame interval (typically 50 ms – 1 s on a steady stream). diff --git a/backend/app/api/routes/pending_uploads.py b/backend/app/api/routes/pending_uploads.py index 9d092c2bb..d2473e1da 100644 --- a/backend/app/api/routes/pending_uploads.py +++ b/backend/app/api/routes/pending_uploads.py @@ -13,7 +13,7 @@ from backend.app.core.database import get_db from backend.app.core.permissions import Permission from backend.app.models.pending_upload import PendingUpload from backend.app.models.user import User -from backend.app.services.archive import ArchiveService +from backend.app.services.archive import ArchiveService, resolve_display_stem router = APIRouter(prefix="/pending-uploads", tags=["pending-uploads"]) @@ -31,6 +31,7 @@ class PendingUploadResponse(BaseModel): id: int filename: str + display_name: str # Resolved name that mirrors the eventual archive's print_name (#1152 follow-up) file_size: int source_ip: str | None status: str @@ -43,6 +44,50 @@ class PendingUploadResponse(BaseModel): from_attributes = True +def _resolve_display_name(pending: PendingUpload, prefer_filename: bool) -> str: + """Compute the name the review card should show, matching what archive_print + will eventually write to ``PrintArchive.print_name`` so the user sees the + same name in both places (#1152 follow-up). + + Mirrors ``ArchiveService.archive_print``: + - ``prefer_filename=True`` → stripped filename stem. + - ``prefer_filename=False`` → ``metadata_print_name`` if set, else stem. + """ + stem = resolve_display_stem(pending.filename) + if prefer_filename: + return stem + return (pending.metadata_print_name or "").strip() or stem + + +async def _augment_with_display_name( + db: AsyncSession, + pendings: list[PendingUpload], +) -> list[PendingUploadResponse]: + """Build response objects with display_name resolved against the toggle. + + Reads the ``virtual_printer_archive_name_source`` setting once per request + rather than per row. + """ + from backend.app.api.routes.settings import get_setting + + prefer_filename = (await get_setting(db, "virtual_printer_archive_name_source")) == "filename" + return [ + PendingUploadResponse( + id=p.id, + filename=p.filename, + display_name=_resolve_display_name(p, prefer_filename), + file_size=p.file_size, + source_ip=p.source_ip, + status=p.status, + tags=p.tags, + notes=p.notes, + project_id=p.project_id, + uploaded_at=p.uploaded_at, + ) + for p in pendings + ] + + @router.get("/", response_model=list[PendingUploadResponse]) async def list_pending_uploads( db: AsyncSession = Depends(get_db), @@ -53,7 +98,7 @@ async def list_pending_uploads( select(PendingUpload).where(PendingUpload.status == "pending").order_by(PendingUpload.uploaded_at.desc()) ) - return result.scalars().all() + return await _augment_with_display_name(db, list(result.scalars().all())) @router.get("/count") @@ -172,7 +217,7 @@ async def get_pending_upload( if not pending: raise HTTPException(status_code=404, detail="Upload not found") - return pending + return (await _augment_with_display_name(db, [pending]))[0] @router.post("/{upload_id}/archive") diff --git a/backend/app/core/database.py b/backend/app/core/database.py index 06026a21b..e0102da51 100644 --- a/backend/app/core/database.py +++ b/backend/app/core/database.py @@ -1760,6 +1760,16 @@ async def run_migrations(conn): "CREATE INDEX IF NOT EXISTS ix_library_files_source_url ON library_files(source_url)", ) + # Migration: Cache metadata title on pending uploads (#1152 follow-up). + # Without this column the review card always shows the FTP filename while + # the eventual archive's print_name comes from the 3MF metadata title, + # creating a confusing review→archive name mismatch. Captured at upload + # time so /pending-uploads/ list calls don't have to reopen each 3MF. + await _safe_execute( + conn, + "ALTER TABLE pending_uploads ADD COLUMN metadata_print_name VARCHAR(255)", + ) + # Migration: Per-user API key ownership + cloud-access scope (#1182). # user_id is nullable so legacy keys (created before #1182) survive the # migration; cloud routes reject calls from keys without an owner so the diff --git a/backend/app/models/pending_upload.py b/backend/app/models/pending_upload.py index 937a454e8..f72567ae1 100644 --- a/backend/app/models/pending_upload.py +++ b/backend/app/models/pending_upload.py @@ -20,6 +20,12 @@ class PendingUpload(Base): file_path: Mapped[str] = mapped_column(String(500)) # Temp storage path file_size: Mapped[int] = mapped_column(Integer) + # Embedded 3MF Title metadata, captured at FTP-receive time so the review + # card and the eventual archive's print_name agree on which name to show + # (#1152 follow-up). NULL when the 3MF has no title or the metadata read + # failed — the response model falls back to the filename stem in that case. + metadata_print_name: Mapped[str | None] = mapped_column(String(255), nullable=True) + # Source info source_ip: Mapped[str | None] = mapped_column(String(45), nullable=True) diff --git a/backend/app/services/archive.py b/backend/app/services/archive.py index 95709dbcc..967a4f143 100644 --- a/backend/app/services/archive.py +++ b/backend/app/services/archive.py @@ -41,6 +41,31 @@ def _copy_and_fsync(src: Path, dst: Path, chunk_size: int = 1024 * 1024) -> None shutil.copystat(src, dst) +def resolve_display_stem(filename: str) -> str: + """Return a clean human-readable stem from a 3MF/gcode filename. + + Bambu Studio's "Send to printer" dialog typically writes files like + ``Plate_1.gcode.3mf`` (a sliced gcode payload wrapped in a 3MF container). + The naive ``Path(filename).stem`` only drops the last suffix, leaving + ``Plate_1.gcode`` — which then surfaces in the archive UI as a confusing + ``Plate_1.gcode`` rather than ``Plate_1`` (#1152 follow-up). + + Strip the recognised print-format suffixes in order: + + - ``.gcode.3mf`` → bare stem (Bambu Studio FTP send) + - ``.3mf`` → bare stem + - ``.gcode`` → bare stem (rare standalone gcode upload) + + Anything else passes through unchanged. + """ + name = Path(filename).name # drop any path components + lower = name.lower() + for suffix in (".gcode.3mf", ".3mf", ".gcode"): + if lower.endswith(suffix): + return name[: -len(suffix)] + return Path(name).stem + + class ThreeMFParser: """Parser for Bambu Lab 3MF files.""" @@ -917,7 +942,7 @@ class ArchiveService: # Create archive directory structure timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") - display_stem = Path(original_filename).stem if original_filename else source_file.stem + display_stem = resolve_display_stem(original_filename if original_filename else source_file.name) archive_name = f"{timestamp}_{display_stem}" # Use "unassigned" folder for archives without a printer printer_folder = str(printer_id) if printer_id is not None else "unassigned" diff --git a/backend/app/services/virtual_printer/manager.py b/backend/app/services/virtual_printer/manager.py index b6b2844d2..75f5f7602 100644 --- a/backend/app/services/virtual_printer/manager.py +++ b/backend/app/services/virtual_printer/manager.py @@ -279,6 +279,22 @@ class VirtualPrinterInstance: pass return + # Peek at the 3MF for the embedded title BEFORE we hand it off to the + # DB. Storing it now means the /pending-uploads/ list doesn't have to + # reopen every 3MF on every render to keep the review card and the + # eventual archive name in sync (#1152 follow-up). Failure to parse is + # not fatal — the response model falls back to the filename stem. + metadata_print_name: str | None = None + try: + from backend.app.services.archive import ThreeMFParser + + parsed = ThreeMFParser(file_path).parse() + raw_name = parsed.get("print_name") + if isinstance(raw_name, str) and raw_name.strip(): + metadata_print_name = raw_name.strip()[:255] + except Exception as e: + logger.debug("[VP %s] Metadata title peek failed for %s: %s", self.name, file_path.name, e) + try: from backend.app.models.pending_upload import PendingUpload @@ -290,6 +306,7 @@ class VirtualPrinterInstance: source_ip=source_ip, status="pending", uploaded_at=datetime.now(timezone.utc), + metadata_print_name=metadata_print_name, ) db.add(pending) await db.commit() diff --git a/backend/tests/integration/test_pending_upload_display_name.py b/backend/tests/integration/test_pending_upload_display_name.py new file mode 100644 index 000000000..fa7c44337 --- /dev/null +++ b/backend/tests/integration/test_pending_upload_display_name.py @@ -0,0 +1,159 @@ +"""Integration tests for #1152 follow-up — pending-upload review card name +matches the eventual archive's print_name. + +Before this change the review card always showed the raw FTP filename while +the archive's ``print_name`` was resolved from the 3MF metadata title (or, with +the toggle on ``filename``, the stripped stem). That gave users two different +names for the same item — they'd see *Plate_1.gcode* in review and *Some +Creator's Title* in the archive grid. + +These tests pin the new contract: + - ``PendingUploadResponse.display_name`` mirrors what archive_print will + eventually write to ``PrintArchive.print_name``. + - The toggle (``virtual_printer_archive_name_source``) flips both views in + lockstep — never one without the other. + - Filename normalisation (``Plate_1.gcode.3mf`` → ``Plate_1``) is applied + consistently regardless of the toggle. +""" + +import pytest +from httpx import AsyncClient +from sqlalchemy.ext.asyncio import AsyncSession + +from backend.app.models.pending_upload import PendingUpload +from backend.app.models.settings import Settings + + +async def _set_archive_name_source(db: AsyncSession, value: str) -> None: + """Write the virtual_printer_archive_name_source setting directly.""" + db.add(Settings(key="virtual_printer_archive_name_source", value=value)) + await db.commit() + + +async def _seed_pending( + db: AsyncSession, + *, + filename: str, + metadata_print_name: str | None = None, +) -> int: + pending = PendingUpload( + filename=filename, + file_path=f"/tmp/{filename}", + file_size=42, + source_ip="192.168.1.50", + status="pending", + metadata_print_name=metadata_print_name, + ) + db.add(pending) + await db.commit() + await db.refresh(pending) + return pending.id + + +class TestDisplayNameResolution: + """``GET /pending-uploads/`` resolves ``display_name`` to the same value + ``archive_print`` would store on the eventual ``PrintArchive``.""" + + @pytest.mark.asyncio + @pytest.mark.integration + async def test_default_toggle_uses_metadata_title_when_present( + self, async_client: AsyncClient, db_session: AsyncSession + ): + """Default toggle is ``metadata`` — review card shows the embedded + title rather than the FTP filename, matching what the archived + PrintArchive.print_name will end up being.""" + await _seed_pending( + db_session, + filename="Plate_1.gcode.3mf", + metadata_print_name="Custom Cool Benchy", + ) + + resp = await async_client.get("/api/v1/pending-uploads/") + assert resp.status_code == 200 + rows = resp.json() + assert len(rows) == 1 + assert rows[0]["display_name"] == "Custom Cool Benchy" + # filename is still surfaced separately so the user can see what + # actually arrived over FTP if they want to (tooltip). + assert rows[0]["filename"] == "Plate_1.gcode.3mf" + + @pytest.mark.asyncio + @pytest.mark.integration + async def test_default_toggle_falls_back_to_stripped_stem_when_no_metadata( + self, async_client: AsyncClient, db_session: AsyncSession + ): + """No embedded title (Bambu Studio's default plate export) — both + review and archive end up showing the stripped filename stem.""" + await _seed_pending( + db_session, + filename="Plate_1.gcode.3mf", + metadata_print_name=None, + ) + + resp = await async_client.get("/api/v1/pending-uploads/") + assert resp.status_code == 200 + # ``Plate_1`` — both ``.gcode`` and ``.3mf`` stripped (#1152 + # follow-up: ``Path.stem`` only strips the last suffix and would + # leave ``Plate_1.gcode``). + assert resp.json()[0]["display_name"] == "Plate_1" + + @pytest.mark.asyncio + @pytest.mark.integration + async def test_filename_toggle_overrides_metadata_title(self, async_client: AsyncClient, db_session: AsyncSession): + """When the operator opts into ``filename`` (so a user-renamed + Bambu Studio job surfaces its renamed-at-send filename), the embedded + creator-baked title is ignored. Review must follow the same toggle + as the archive.""" + await _set_archive_name_source(db_session, "filename") + await _seed_pending( + db_session, + filename="MyRenamedJob.3mf", + metadata_print_name="Original Creator Title", + ) + + resp = await async_client.get("/api/v1/pending-uploads/") + assert resp.status_code == 200 + assert resp.json()[0]["display_name"] == "MyRenamedJob" + + @pytest.mark.asyncio + @pytest.mark.integration + async def test_filename_toggle_strips_double_suffix(self, async_client: AsyncClient, db_session: AsyncSession): + """Filename mode also drops .gcode.3mf — same normalisation as the + archive's print_name, so the names line up exactly.""" + await _set_archive_name_source(db_session, "filename") + await _seed_pending(db_session, filename="Plate_4.gcode.3mf", metadata_print_name=None) + + resp = await async_client.get("/api/v1/pending-uploads/") + assert resp.status_code == 200 + assert resp.json()[0]["display_name"] == "Plate_4" + + @pytest.mark.asyncio + @pytest.mark.integration + async def test_get_one_returns_display_name(self, async_client: AsyncClient, db_session: AsyncSession): + """The single-resource endpoint surfaces display_name too — UIs that + load a pending upload by id (e.g. detail modals) get the same name.""" + upload_id = await _seed_pending( + db_session, + filename="X.gcode.3mf", + metadata_print_name="Deep Detail Bear", + ) + + resp = await async_client.get(f"/api/v1/pending-uploads/{upload_id}") + assert resp.status_code == 200 + assert resp.json()["display_name"] == "Deep Detail Bear" + + @pytest.mark.asyncio + @pytest.mark.integration + async def test_blank_metadata_title_falls_back_to_stem(self, async_client: AsyncClient, db_session: AsyncSession): + """A whitespace-only metadata title behaves like absent metadata — + guards against 3MFs with broken/empty Title fields surfacing as a + blank review card.""" + await _seed_pending( + db_session, + filename="empty-title.gcode.3mf", + metadata_print_name=" ", + ) + + resp = await async_client.get("/api/v1/pending-uploads/") + assert resp.status_code == 200 + assert resp.json()[0]["display_name"] == "empty-title" diff --git a/backend/tests/unit/services/test_archive_display_stem.py b/backend/tests/unit/services/test_archive_display_stem.py new file mode 100644 index 000000000..5e3b2f177 --- /dev/null +++ b/backend/tests/unit/services/test_archive_display_stem.py @@ -0,0 +1,51 @@ +"""Tests for resolve_display_stem — Bambu Studio filename normalisation (#1152). + +Bambu Studio's "Send to printer" dialog typically writes ``Plate_1.gcode.3mf`` +(a sliced gcode payload wrapped in a 3MF container). ``Path(name).stem`` only +strips the last suffix and leaves ``Plate_1.gcode``, which then surfaces in +the archive UI as a confusing ``Plate_1.gcode`` rather than ``Plate_1``. + +Pin the canonicalisation rules so a future refactor can't silently regress +this path. We don't need a dedicated test for ``archive_print``'s consumption +of the helper — the existing test suite covers that flow end-to-end via the +integration tests and a behaviour change there would surface as a different +``archive.print_name`` value. +""" + +import pytest + +from backend.app.services.archive import resolve_display_stem + + +@pytest.mark.parametrize( + ("filename", "expected"), + [ + # The headline case: Bambu Studio's default name for a sliced 3MF. + ("Plate_1.gcode.3mf", "Plate_1"), + # User-renamed file with the double-suffix pattern. + ("MyAwesomeBenchy.gcode.3mf", "MyAwesomeBenchy"), + # Plain .3mf (already-clean export from Bambu Studio's Save As). + ("Benchy.3mf", "Benchy"), + # Standalone gcode upload — rare but supported. + ("standalone.gcode", "standalone"), + # Mixed-case suffix — many slicers / OSes preserve user-typed case. + ("UPPERCASE.GCODE.3MF", "UPPERCASE"), + ("mixed.GCode.3mf", "mixed"), + # Names that contain dots in the middle should keep them. + ("my.cool.model.gcode.3mf", "my.cool.model"), + ("v1.2.3-prototype.3mf", "v1.2.3-prototype"), + # No recognised suffix → fall through to Path.stem. + ("Cura_export.zip", "Cura_export"), + ("README.md", "README"), + # Edge: just the suffix with nothing in front. Strip honestly — the + # caller is responsible for sanity-checking empty stems. + (".gcode.3mf", ""), + (".3mf", ""), + # Path components must not leak in. The helper takes a filename, but + # callers occasionally pass a full path string. + ("/some/dir/Plate_1.gcode.3mf", "Plate_1"), + ("subdir/MyModel.3mf", "MyModel"), + ], +) +def test_resolve_display_stem(filename: str, expected: str) -> None: + assert resolve_display_stem(filename) == expected diff --git a/frontend/src/__tests__/components/PendingUploadsPanel.test.tsx b/frontend/src/__tests__/components/PendingUploadsPanel.test.tsx new file mode 100644 index 000000000..96f8b9406 --- /dev/null +++ b/frontend/src/__tests__/components/PendingUploadsPanel.test.tsx @@ -0,0 +1,78 @@ +/** + * Tests for PendingUploadsPanel — review-card name resolution (#1152 follow-up). + * + * The panel renders ``upload.display_name`` (the resolved name that mirrors + * what the eventual archive's ``print_name`` will be) and falls back to + * ``upload.filename`` when the display_name is missing — guards against a + * pending row created before the column landed (or a serialiser bug) showing + * a blank card. + */ + +import { describe, it, expect, beforeEach } from 'vitest'; +import { render } from '../utils'; +import { PendingUploadsPanel } from '../../components/PendingUploadsPanel'; +import { http, HttpResponse } from 'msw'; +import { server } from '../mocks/server'; + +interface MockUpload { + id: number; + filename: string; + display_name: string; + file_size: number; + source_ip: string | null; + status: string; + tags: string | null; + notes: string | null; + project_id: number | null; + uploaded_at: string; +} + +const baseUpload: MockUpload = { + id: 1, + filename: 'Plate_1.gcode.3mf', + display_name: 'My Resolved Name', + file_size: 12345, + source_ip: '192.168.1.50', + status: 'pending', + tags: null, + notes: null, + project_id: null, + uploaded_at: '2026-05-01T10:00:00Z', +}; + +describe('PendingUploadsPanel — display_name', () => { + beforeEach(() => { + server.use( + http.get('/api/v1/projects/', () => HttpResponse.json([])), + ); + }); + + it('renders the resolved display_name on the review card', async () => { + server.use(http.get('/api/v1/pending-uploads/', () => HttpResponse.json([baseUpload]))); + + const { findByText } = render(); + expect(await findByText('My Resolved Name')).toBeInTheDocument(); + }); + + it('falls back to filename when display_name is an empty string', async () => { + // Defensive: a bug in the resolver, a pre-migration row that was somehow + // re-fetched, or a partial JSON deserialisation must not produce a blank + // review card. The frontend keeps showing _something_ the user can click. + server.use( + http.get('/api/v1/pending-uploads/', () => + HttpResponse.json([{ ...baseUpload, display_name: '' }]), + ), + ); + + const { findByText } = render(); + expect(await findByText('Plate_1.gcode.3mf')).toBeInTheDocument(); + }); + + it('exposes the raw filename via tooltip so the user can see what arrived over FTP', async () => { + server.use(http.get('/api/v1/pending-uploads/', () => HttpResponse.json([baseUpload]))); + + const { findByText } = render(); + const nameEl = await findByText('My Resolved Name'); + expect(nameEl.getAttribute('title')).toBe('Plate_1.gcode.3mf'); + }); +}); diff --git a/frontend/src/api/client.ts b/frontend/src/api/client.ts index 62deeacc2..6fd3bd439 100644 --- a/frontend/src/api/client.ts +++ b/frontend/src/api/client.ts @@ -5652,6 +5652,11 @@ export interface VirtualPrinterModels { export interface PendingUpload { id: number; filename: string; + // Resolved name the review card should show — mirrors what archive_print + // will eventually write to PrintArchive.print_name (#1152 follow-up). Falls + // back to the stripped filename stem when the 3MF has no embedded title or + // the operator has chosen the "filename" archive-name source. + display_name: string; file_size: number; source_ip: string | null; status: string; diff --git a/frontend/src/components/PendingUploadsPanel.tsx b/frontend/src/components/PendingUploadsPanel.tsx index 285af5273..53c257a1b 100644 --- a/frontend/src/components/PendingUploadsPanel.tsx +++ b/frontend/src/components/PendingUploadsPanel.tsx @@ -54,7 +54,7 @@ function PendingUploadItem({
-

{upload.filename}

+

{upload.display_name || upload.filename}

{formatFileSize(upload.file_size)} · diff --git a/static/assets/index-BSfASPDE.js b/static/assets/index-BlyoHMPZ.js similarity index 99% rename from static/assets/index-BSfASPDE.js rename to static/assets/index-BlyoHMPZ.js index f686e3ea7..1c798ed78 100644 --- a/static/assets/index-BSfASPDE.js +++ b/static/assets/index-BlyoHMPZ.js @@ -8689,7 +8689,7 @@ ${f.slice(d+2)}`,m+=1;else break}e.push({indent:d,number:parseInt(l,10),content: [&::-webkit-slider-thumb]:bg-bambu-green [&::-webkit-slider-thumb]:rounded-full`}),a.jsx("button",{onClick:ue,className:"p-2 hover:bg-red-500/20 rounded transition-colors",children:a.jsx(Zt,{className:"w-4 h-4 text-red-400"})})]}):a.jsxs("label",{className:"flex flex-col items-center justify-center gap-2 p-6 border-2 border-dashed border-bambu-dark-tertiary rounded-lg cursor-pointer hover:border-bambu-green/50 transition-colors",children:[a.jsx(_a,{className:"w-8 h-8 text-bambu-gray"}),a.jsx("span",{className:"text-sm text-bambu-gray",children:"Drop audio file or click to upload"}),a.jsx("span",{className:"text-xs text-bambu-gray/60",children:"MP3, WAV, M4A, AAC, OGG"}),a.jsx("input",{type:"file",accept:".mp3,.wav,.m4a,.aac,.ogg,audio/*",onChange:J,className:"hidden"})]})]}),a.jsxs("div",{className:"p-3 bg-bambu-dark rounded-lg text-sm space-y-1",children:[a.jsxs("p",{className:"text-bambu-gray",children:[a.jsx("span",{className:"text-white",children:"Original:"})," ",Eh(m)," @ ",$?.width,"x",$?.height]}),a.jsxs("p",{className:"text-bambu-gray",children:[a.jsx("span",{className:"text-white",children:"Output:"})," ",Eh(se)," @ ",g,"x speed",N&&" + music overlay"]})]})]}),R.isPending&&a.jsxs("div",{className:"absolute inset-0 bg-black/80 flex flex-col items-center justify-center gap-4",children:[a.jsx(mt,{className:"w-12 h-12 text-bambu-green animate-spin"}),a.jsx("p",{className:"text-white text-lg",children:"Processing timelapse..."}),a.jsx("p",{className:"text-bambu-gray text-sm",children:"This may take a few moments"})]})]})})}const get=[.25,.5,.75,1,1.5,2,3,4];function Tle({src:t,title:e,downloadFilename:n,archiveId:r,onClose:i,onEdit:s}){const o=w.useRef(null),[l,c]=w.useState(!0),[d,u]=w.useState(1),[m,p]=w.useState(0),[f,x]=w.useState(0),[v,b]=w.useState(!1);w.useEffect(()=>{const A=o.current;A&&(A.playbackRate=d)},[d]),w.useEffect(()=>{const A=T=>{T.key==="Escape"&&i()};return window.addEventListener("keydown",A),()=>window.removeEventListener("keydown",A)},[i]),w.useEffect(()=>{const A=o.current;if(!A)return;const T=()=>p(A.currentTime),F=()=>x(A.duration),k=()=>c(!0),E=()=>c(!1);return A.addEventListener("timeupdate",T),A.addEventListener("durationchange",F),A.addEventListener("play",k),A.addEventListener("pause",E),()=>{A.removeEventListener("timeupdate",T),A.removeEventListener("durationchange",F),A.removeEventListener("play",k),A.removeEventListener("pause",E)}},[]);const g=()=>{const A=o.current;A&&(l?A.pause():A.play())},S=A=>{const T=o.current;T&&(T.currentTime=parseFloat(A.target.value))},N=()=>{const A=o.current;A&&(A.currentTime=Math.max(0,A.currentTime-5))},P=()=>{const A=o.current;A&&(A.currentTime=Math.min(f,A.currentTime+5))},C=()=>{const A=document.createElement("a");A.href=t,A.download=n,A.click()};return a.jsxs("div",{className:"fixed inset-0 z-50 flex items-center justify-center bg-black/80",children:[a.jsxs("div",{className:"relative bg-bambu-dark-secondary rounded-xl max-w-4xl w-full mx-4 overflow-hidden",children:[a.jsxs("div",{className:"flex items-center justify-between p-4 border-b border-bambu-dark-tertiary",children:[a.jsxs("h3",{className:"text-lg font-semibold text-white flex items-center gap-2",children:[a.jsx(op,{className:"w-5 h-5 text-bambu-green"}),e]}),a.jsxs("div",{className:"flex items-center gap-2",children:[r&&a.jsxs(De,{variant:"secondary",size:"sm",onClick:()=>b(!0),children:[a.jsx(li,{className:"w-4 h-4"}),"Edit"]}),a.jsxs(De,{variant:"secondary",size:"sm",onClick:C,children:[a.jsx(aa,{className:"w-4 h-4"}),"Download"]}),a.jsx("button",{onClick:i,className:"p-1 hover:bg-bambu-dark-tertiary rounded transition-colors",children:a.jsx(Bt,{className:"w-5 h-5 text-bambu-gray"})})]})]}),a.jsxs("div",{className:"p-4",children:[a.jsx("video",{ref:o,src:t,autoPlay:!0,className:"w-full rounded-lg",onClick:g}),a.jsxs("div",{className:"mt-4 space-y-3",children:[a.jsxs("div",{className:"flex items-center gap-3",children:[a.jsx("span",{className:"text-xs text-bambu-gray w-12 text-right",children:Eh(m)}),a.jsx("input",{type:"range",min:0,max:f||100,value:m,onChange:S,className:`flex-1 h-1 bg-bambu-dark-tertiary rounded-lg appearance-none cursor-pointer [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:w-3 [&::-webkit-slider-thumb]:h-3 [&::-webkit-slider-thumb]:bg-bambu-green [&::-webkit-slider-thumb]:rounded-full - [&::-webkit-slider-thumb]:cursor-pointer`}),a.jsx("span",{className:"text-xs text-bambu-gray w-12",children:Eh(f)})]}),a.jsxs("div",{className:"flex items-center justify-between",children:[a.jsxs("div",{className:"flex items-center gap-2",children:[a.jsx("button",{onClick:N,className:"p-2 hover:bg-bambu-dark-tertiary rounded-lg transition-colors",title:"Skip back 5s",children:a.jsx(Vxe,{className:"w-5 h-5 text-bambu-gray"})}),a.jsx("button",{onClick:g,className:"p-2 bg-bambu-green hover:bg-bambu-green-dark rounded-lg transition-colors",children:l?a.jsx(cS,{className:"w-5 h-5 text-white"}):a.jsx(rs,{className:"w-5 h-5 text-white"})}),a.jsx("button",{onClick:P,className:"p-2 hover:bg-bambu-dark-tertiary rounded-lg transition-colors",title:"Skip forward 5s",children:a.jsx(nT,{className:"w-5 h-5 text-bambu-gray"})})]}),a.jsxs("div",{className:"flex items-center gap-2",children:[a.jsx("span",{className:"text-sm text-bambu-gray",children:"Speed:"}),a.jsx("div",{className:"flex gap-1",children:get.map(A=>a.jsxs("button",{onClick:()=>u(A),className:`px-2 py-1 text-xs rounded transition-colors ${d===A?"bg-bambu-green text-white":"bg-bambu-dark-tertiary text-bambu-gray hover:bg-bambu-dark-tertiary/80"}`,children:[A,"x"]},A))})]})]})]})]})]}),v&&r&&a.jsx(fet,{archiveId:r,timelapseSrc:t,onClose:()=>b(!1),onSave:s})]})}function bet({archiveIds:t,onClose:e}){w.useEffect(()=>{const s=o=>{o.key==="Escape"&&e()};return window.addEventListener("keydown",s),()=>window.removeEventListener("keydown",s)},[e]);const{data:n,isLoading:r,error:i}=Qe({queryKey:["archive-comparison",t],queryFn:()=>oe.compareArchives(t)});return a.jsx("div",{className:"fixed inset-0 bg-black/80 flex items-center justify-center z-50 p-4",onClick:e,children:a.jsxs("div",{className:"bg-bambu-dark-secondary rounded-lg max-w-4xl w-full max-h-[90vh] flex flex-col border border-bambu-dark-tertiary",onClick:s=>s.stopPropagation(),children:[a.jsxs("div",{className:"flex items-center justify-between p-4 border-b border-bambu-dark-tertiary",children:[a.jsxs("h3",{className:"text-lg font-semibold text-white",children:["Compare Archives (",t.length,")"]}),a.jsx("button",{onClick:e,className:"text-bambu-gray hover:text-white p-1",children:a.jsx(Bt,{className:"w-5 h-5"})})]}),a.jsx("div",{className:"flex-1 overflow-auto p-4 bg-bambu-dark-secondary",children:r?a.jsx("div",{className:"flex items-center justify-center py-12",children:a.jsx(mt,{className:"w-8 h-8 text-bambu-green animate-spin"})}):i?a.jsxs("div",{className:"text-center py-12 text-red-400",children:[a.jsx(Cn,{className:"w-12 h-12 mx-auto mb-4 opacity-50"}),a.jsx("p",{children:"Failed to load comparison"}),a.jsx("p",{className:"text-sm text-bambu-gray mt-2",children:i instanceof Error?i.message:"Unknown error"})]}):n?a.jsx(xet,{comparison:n}):null}),a.jsx("div",{className:"p-4 border-t border-bambu-dark-tertiary",children:a.jsx(De,{variant:"secondary",onClick:e,className:"w-full",children:"Close"})})]})})}function xet({comparison:t}){return a.jsxs("div",{className:"space-y-6",children:[a.jsx("div",{className:"overflow-x-auto",children:a.jsxs("table",{className:"w-full",children:[a.jsx("thead",{children:a.jsxs("tr",{children:[a.jsx("th",{className:"text-left text-sm text-bambu-gray font-medium pb-2 pr-4 min-w-[150px]",children:"Setting"}),t.archives.map(e=>a.jsxs("th",{className:"text-left text-sm font-medium pb-2 px-2 min-w-[120px]",children:[a.jsx("div",{className:"text-white truncate max-w-[150px]",title:e.print_name,children:e.print_name}),a.jsx("div",{className:`text-xs ${e.status==="completed"?"text-status-ok":e.status==="failed"?"text-status-error":"text-bambu-gray"}`,children:e.status})]},e.id))]})}),a.jsx("tbody",{className:"divide-y divide-bambu-gray/20",children:t.comparison.map(e=>a.jsxs("tr",{className:e.has_difference?"bg-yellow-500/5":"",children:[a.jsx("td",{className:"py-2 pr-4 text-sm",children:a.jsxs("div",{className:"flex items-center gap-2",children:[e.has_difference&&a.jsx(Cn,{className:"w-3 h-3 text-yellow-400 flex-shrink-0"}),a.jsx("span",{className:e.has_difference?"text-yellow-400":"text-bambu-gray",children:e.label})]})}),e.values.map((n,r)=>a.jsxs("td",{className:"py-2 px-2 text-sm text-white",children:[n??a.jsx("span",{className:"text-bambu-gray/50",children:"-"}),e.unit&&n!==null&&a.jsx("span",{className:"text-bambu-gray ml-1",children:e.unit})]},r))]},e.field))})]})}),t.differences.length>0&&a.jsxs("div",{className:"p-4 bg-yellow-500/10 border border-yellow-500/30 rounded-lg",children:[a.jsxs("h4",{className:"text-sm font-medium text-yellow-400 mb-2 flex items-center gap-2",children:[a.jsx(Cn,{className:"w-4 h-4"}),t.differences.length," Difference",t.differences.length>1?"s":""," Found"]}),a.jsxs("ul",{className:"text-sm text-white/80 space-y-1",children:[t.differences.slice(0,5).map(e=>a.jsxs("li",{children:[a.jsx("span",{className:"text-yellow-400",children:e.label}),": ",e.values.join(" vs ")," ",e.unit||""]},e.field)),t.differences.length>5&&a.jsxs("li",{className:"text-bambu-gray",children:["...and ",t.differences.length-5," more"]})]})]}),t.success_correlation.has_both_outcomes?a.jsxs("div",{className:"p-4 bg-bambu-dark rounded-lg",children:[a.jsxs("h4",{className:"text-sm font-medium text-white mb-3 flex items-center gap-2",children:[a.jsx(jr,{className:"w-4 h-4 text-bambu-green"}),"Success/Failure Analysis"]}),a.jsxs("div",{className:"flex items-center gap-4 text-sm mb-3",children:[a.jsxs("span",{className:"text-bambu-green",children:[t.success_correlation.successful_count," successful"]}),a.jsxs("span",{className:"text-red-400",children:[t.success_correlation.failed_count," failed"]})]}),t.success_correlation.insights&&t.success_correlation.insights.length>0?a.jsx("div",{className:"space-y-2",children:t.success_correlation.insights.map(e=>a.jsxs("div",{className:"text-sm p-2 bg-bambu-dark-secondary rounded",children:[a.jsxs("span",{className:"text-white font-medium",children:[e.label,":"]})," ",a.jsx("span",{className:"text-white/80",children:e.insight})]},e.field))}):a.jsx("p",{className:"text-sm text-bambu-gray",children:"No clear correlations found between settings and outcomes."})]}):a.jsx("div",{className:"p-4 bg-bambu-dark rounded-lg text-sm text-bambu-gray",children:a.jsx("p",{children:t.success_correlation.message||"Need both successful and failed prints for correlation analysis."})})]})}function yet(t){const e=new Date(t),r=new Date().getTime()-e.getTime(),i=Math.floor(r/6e4);if(i<1)return"Just now";if(i<60)return`${i}m ago`;const s=Math.floor(i/60);return s<24?`${s}h ago`:`${Math.floor(s/24)}d ago`}function vet({upload:t,projects:e,onArchive:n,onDiscard:r,isArchiving:i,isDiscarding:s}){const[o,l]=w.useState(!1),[c,d]=w.useState(t.tags||""),[u,m]=w.useState(t.notes||""),[p,f]=w.useState(t.project_id),[x,v]=w.useState(!1);return a.jsx(wt,{children:a.jsxs(St,{className:"py-3",children:[a.jsxs("div",{className:"flex items-center justify-between",children:[a.jsxs("div",{className:"flex items-center gap-3",children:[a.jsx(sp,{className:"w-8 h-8 text-bambu-green flex-shrink-0"}),a.jsxs("div",{children:[a.jsx("p",{className:"text-white font-medium",children:t.filename}),a.jsxs("div",{className:"flex items-center gap-2 text-xs text-bambu-gray",children:[a.jsx("span",{children:Yi(t.file_size)}),a.jsx("span",{children:"·"}),a.jsxs("span",{className:"flex items-center gap-1",children:[a.jsx(Yn,{className:"w-3 h-3"}),yet(t.uploaded_at)]}),t.source_ip&&a.jsxs(a.Fragment,{children:[a.jsx("span",{children:"·"}),a.jsxs("span",{children:["from ",t.source_ip]})]})]})]})]}),a.jsxs("div",{className:"flex items-center gap-2",children:[a.jsx("button",{onClick:()=>l(!o),className:"p-1 text-bambu-gray hover:text-white transition-colors",children:o?a.jsx(lc,{className:"w-5 h-5"}):a.jsx(zn,{className:"w-5 h-5"})}),a.jsx(De,{variant:"primary",size:"sm",onClick:()=>n(t.id,{tags:c,notes:u,project_id:p||void 0}),disabled:i,children:i?a.jsx(mt,{className:"w-4 h-4 animate-spin"}):a.jsxs(a.Fragment,{children:[a.jsx(lo,{className:"w-4 h-4"}),"Archive"]})}),a.jsx(De,{variant:"secondary",size:"sm",onClick:()=>v(!0),disabled:s,children:s?a.jsx(mt,{className:"w-4 h-4 animate-spin"}):a.jsx(Zt,{className:"w-4 h-4 text-red-400"})})]})]}),x&&a.jsx(yn,{title:"Discard Upload",message:`Are you sure you want to discard "${t.filename}"? This cannot be undone.`,confirmText:"Discard",variant:"danger",onConfirm:()=>{r(t.id),v(!1)},onCancel:()=>v(!1)}),o&&a.jsxs("div",{className:"mt-4 pt-4 border-t border-bambu-dark-tertiary space-y-3",children:[a.jsxs("div",{children:[a.jsx("label",{className:"block text-sm text-bambu-gray mb-1",children:"Tags"}),a.jsx("input",{type:"text",value:c,onChange:b=>d(b.target.value),placeholder:"e.g., functional, prototype, gift",className:"w-full bg-bambu-dark-secondary border border-bambu-dark-tertiary rounded-md px-3 py-2 text-white placeholder-bambu-gray text-sm"})]}),a.jsxs("div",{children:[a.jsx("label",{className:"block text-sm text-bambu-gray mb-1",children:"Notes"}),a.jsx("textarea",{value:u,onChange:b=>m(b.target.value),placeholder:"Add notes about this print...",rows:2,className:"w-full bg-bambu-dark-secondary border border-bambu-dark-tertiary rounded-md px-3 py-2 text-white placeholder-bambu-gray text-sm resize-none"})]}),a.jsxs("div",{children:[a.jsx("label",{className:"block text-sm text-bambu-gray mb-1",children:"Project"}),a.jsxs("select",{value:p||"",onChange:b=>f(b.target.value?Number(b.target.value):null),className:"w-full bg-bambu-dark-secondary border border-bambu-dark-tertiary rounded-md px-3 py-2 text-white text-sm",children:[a.jsx("option",{value:"",children:"No project"}),e.map(b=>a.jsx("option",{value:b.id,children:b.name},b.id))]})]})]})]})})}function wet(){const t=tn(),{showToast:e}=rn(),[n,r]=w.useState(!1),[i,s]=w.useState(!1),[o,l]=w.useState(new Set),[c,d]=w.useState(new Set),{data:u,isLoading:m}=Qe({queryKey:["pending-uploads"],queryFn:vx.list,refetchInterval:1e4}),{data:p}=Qe({queryKey:["projects"],queryFn:()=>oe.getProjects(),select:g=>[...g].sort((S,N)=>S.name.localeCompare(N.name))}),f=ot({mutationFn:({id:g,data:S})=>vx.archive(g,S),onMutate:({id:g})=>{l(S=>new Set(S).add(g))},onSettled:(g,S,{id:N})=>{l(P=>{const C=new Set(P);return C.delete(N),C})},onSuccess:g=>{t.invalidateQueries({queryKey:["pending-uploads"]}),t.invalidateQueries({queryKey:["archives"]}),e(`Archived: ${g.print_name}`)},onError:g=>{e(g.message||"Failed to archive","error")}}),x=ot({mutationFn:g=>vx.discard(g),onMutate:g=>{d(S=>new Set(S).add(g))},onSettled:(g,S,N)=>{d(P=>{const C=new Set(P);return C.delete(N),C})},onSuccess:()=>{t.invalidateQueries({queryKey:["pending-uploads"]}),e("Upload discarded")},onError:g=>{e(g.message||"Failed to discard","error")}}),v=ot({mutationFn:vx.archiveAll,onSuccess:g=>{t.invalidateQueries({queryKey:["pending-uploads"]}),t.invalidateQueries({queryKey:["archives"]}),e(`Archived ${g.archived} files${g.failed>0?`, ${g.failed} failed`:""}`)},onError:g=>{e(g.message||"Failed to archive all","error")}}),b=ot({mutationFn:vx.discardAll,onSuccess:g=>{t.invalidateQueries({queryKey:["pending-uploads"]}),e(`Discarded ${g.discarded} files`)},onError:g=>{e(g.message||"Failed to discard all","error")}});return m?a.jsx(wt,{children:a.jsx(St,{className:"py-8 flex justify-center",children:a.jsx(mt,{className:"w-6 h-6 animate-spin text-bambu-green"})})}):!u||u.length===0?null:a.jsxs("div",{className:"mb-6",children:[a.jsxs(wt,{className:"border-l-4 border-l-yellow-500",children:[a.jsx(pn,{children:a.jsxs("div",{className:"flex items-center justify-between",children:[a.jsxs("div",{className:"flex items-center gap-2",children:[a.jsx(_a,{className:"w-5 h-5 text-yellow-500"}),a.jsxs("h2",{className:"text-lg font-semibold text-white",children:["Pending Uploads (",u.length,")"]})]}),a.jsxs("div",{className:"flex items-center gap-2",children:[a.jsx(De,{variant:"primary",size:"sm",onClick:()=>r(!0),disabled:v.isPending,children:v.isPending?a.jsx(mt,{className:"w-4 h-4 animate-spin"}):a.jsxs(a.Fragment,{children:[a.jsx(lo,{className:"w-4 h-4"}),"Archive All"]})}),a.jsx(De,{variant:"secondary",size:"sm",onClick:()=>s(!0),disabled:b.isPending,children:b.isPending?a.jsx(mt,{className:"w-4 h-4 animate-spin"}):a.jsxs(a.Fragment,{children:[a.jsx(Zt,{className:"w-4 h-4"}),"Discard All"]})})]})]})}),a.jsxs(St,{children:[a.jsx("p",{className:"text-sm text-bambu-gray mb-4",children:"These files were uploaded via the virtual printer. Review and archive them to add to your collection."}),a.jsx("div",{className:"space-y-3",children:u.map(g=>a.jsx(vet,{upload:g,projects:p||[],onArchive:(S,N)=>f.mutate({id:S,data:N}),onDiscard:S=>x.mutate(S),isArchiving:o.has(g.id),isDiscarding:c.has(g.id)},g.id))})]})]}),n&&a.jsx(yn,{title:"Archive All Uploads",message:`Are you sure you want to archive all ${u.length} pending uploads?`,confirmText:"Archive All",onConfirm:()=>{v.mutate(),r(!1)},onCancel:()=>r(!1)}),i&&a.jsx(yn,{title:"Discard All Uploads",message:`Are you sure you want to discard all ${u.length} pending uploads? This cannot be undone.`,confirmText:"Discard All",variant:"danger",onConfirm:()=>{b.mutate(),s(!1)},onCancel:()=>s(!1)})]})}function _et({onClose:t}){const e=tn(),{showToast:n}=rn(),[r,i]=w.useState(""),[s,o]=w.useState(null),[l,c]=w.useState(""),[d,u]=w.useState(null),[m,p]=w.useState("count");w.useEffect(()=>{const k=E=>{E.key==="Escape"&&(s?o(null):d?u(null):t())};return window.addEventListener("keydown",k),()=>window.removeEventListener("keydown",k)},[t,s,d]);const{data:f,isLoading:x}=Qe({queryKey:["tags"],queryFn:oe.getTags}),v=ot({mutationFn:({oldName:k,newName:E})=>oe.renameTag(k,E),onSuccess:(k,{oldName:E,newName:$})=>{e.invalidateQueries({queryKey:["tags"]}),e.invalidateQueries({queryKey:["archives"]}),n(`Renamed "${E}" to "${$}" in ${k.affected} archive${k.affected!==1?"s":""}`),o(null)},onError:k=>{n(k.message||"Failed to rename tag","error")}}),b=ot({mutationFn:k=>oe.deleteTag(k),onSuccess:(k,E)=>{e.invalidateQueries({queryKey:["tags"]}),e.invalidateQueries({queryKey:["archives"]}),n(`Deleted "${E}" from ${k.affected} archive${k.affected!==1?"s":""}`),u(null)},onError:k=>{n(k.message||"Failed to delete tag","error")}}),g=k=>{o(k.name),c(k.name),u(null)},S=()=>{o(null),c("")},N=()=>{if(!s||!l.trim())return;const k=l.trim();if(k===s){S();return}v.mutate({oldName:s,newName:k})},P=k=>{k.key==="Enter"?(k.preventDefault(),N()):k.key==="Escape"&&(k.preventDefault(),S())},C=k=>{u(k),o(null)},A=()=>{d&&b.mutate(d)},T=f?.filter(k=>k.name.toLowerCase().includes(r.toLowerCase())).sort((k,E)=>m==="count"&&E.count-k.count||k.name.localeCompare(E.name)),F=f?.reduce((k,E)=>k+E.count,0)||0;return a.jsx("div",{className:"fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4",children:a.jsx(wt,{className:"w-full max-w-lg max-h-[80vh] flex flex-col",children:a.jsxs(St,{className:"p-0 flex flex-col min-h-0",children:[a.jsxs("div",{className:"flex items-center justify-between p-4 border-b border-bambu-dark-tertiary flex-shrink-0",children:[a.jsxs("div",{className:"flex items-center gap-2",children:[a.jsx(ym,{className:"w-5 h-5 text-bambu-green"}),a.jsx("h2",{className:"text-xl font-semibold text-white",children:"Manage Tags"})]}),a.jsx("button",{onClick:t,className:"text-bambu-gray hover:text-white transition-colors",children:a.jsx(Bt,{className:"w-5 h-5"})})]}),a.jsxs("div",{className:"p-4 border-b border-bambu-dark-tertiary flex-shrink-0",children:[a.jsxs("div",{className:"flex gap-2",children:[a.jsxs("div",{className:"relative flex-1",children:[a.jsx(Nr,{className:"absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-bambu-gray"}),a.jsx("input",{type:"text",placeholder:"Search tags...",className:"w-full pl-9 pr-3 py-2 bg-bambu-dark border border-bambu-dark-tertiary rounded-lg text-white text-sm focus:border-bambu-green focus:outline-none",value:r,onChange:k=>i(k.target.value)})]}),a.jsxs("select",{className:"px-3 py-2 bg-bambu-dark border border-bambu-dark-tertiary rounded-lg text-white text-sm focus:border-bambu-green focus:outline-none",value:m,onChange:k=>p(k.target.value),children:[a.jsx("option",{value:"count",children:"Sort by Count"}),a.jsx("option",{value:"name",children:"Sort by Name"})]})]}),f&&a.jsxs("p",{className:"text-xs text-bambu-gray mt-2",children:[f.length," tag",f.length!==1?"s":""," across ",F," usage",F!==1?"s":""]})]}),a.jsx("div",{className:"flex-1 overflow-y-auto min-h-0 p-4",children:x?a.jsx("div",{className:"flex items-center justify-center py-8",children:a.jsx(mt,{className:"w-6 h-6 animate-spin text-bambu-gray"})}):T?.length?a.jsx("div",{className:"space-y-2",children:T.map(k=>a.jsx("div",{className:"flex items-center gap-2 p-2 rounded-lg bg-bambu-dark hover:bg-bambu-dark-tertiary transition-colors group",children:s===k.name?a.jsxs("div",{className:"flex-1 flex items-center gap-2",children:[a.jsx("input",{type:"text",className:"flex-1 px-2 py-1 bg-bambu-dark-tertiary border border-bambu-green rounded text-white text-sm focus:outline-none",value:l,onChange:E=>c(E.target.value),onKeyDown:P,autoFocus:!0}),a.jsx(De,{size:"sm",variant:"primary",onClick:N,disabled:!l.trim()||v.isPending,className:"p-1.5",children:v.isPending?a.jsx(mt,{className:"w-4 h-4 animate-spin"}):a.jsx(jr,{className:"w-4 h-4"})}),a.jsx(De,{size:"sm",variant:"ghost",onClick:S,className:"p-1.5",children:a.jsx(Bt,{className:"w-4 h-4"})})]}):d===k.name?a.jsxs("div",{className:"flex-1 flex items-center gap-2",children:[a.jsx(Cn,{className:"w-4 h-4 text-yellow-400 flex-shrink-0"}),a.jsxs("span",{className:"text-sm text-bambu-gray-light flex-1",children:['Delete "',k.name,'" from ',k.count," archive",k.count!==1?"s":"","?"]}),a.jsx(De,{size:"sm",variant:"danger",onClick:A,disabled:b.isPending,className:"p-1.5",children:b.isPending?a.jsx(mt,{className:"w-4 h-4 animate-spin"}):a.jsx(Zt,{className:"w-4 h-4"})}),a.jsx(De,{size:"sm",variant:"ghost",onClick:()=>u(null),className:"p-1.5",children:a.jsx(Bt,{className:"w-4 h-4"})})]}):a.jsxs(a.Fragment,{children:[a.jsx(ym,{className:"w-4 h-4 text-bambu-gray flex-shrink-0"}),a.jsx("span",{className:"text-white flex-1 truncate",children:k.name}),a.jsx("span",{className:"px-2 py-0.5 rounded-full bg-bambu-dark-tertiary text-bambu-gray text-xs",children:k.count}),a.jsxs("div",{className:"flex items-center gap-1 opacity-0 group-hover:opacity-100 transition-opacity",children:[a.jsx("button",{onClick:()=>g(k),className:"p-1.5 rounded hover:bg-bambu-dark text-bambu-gray hover:text-white transition-colors",title:"Rename tag",children:a.jsx(li,{className:"w-4 h-4"})}),a.jsx("button",{onClick:()=>C(k.name),className:"p-1.5 rounded hover:bg-bambu-dark text-bambu-gray hover:text-red-400 transition-colors",title:"Delete tag",children:a.jsx(Zt,{className:"w-4 h-4"})})]})]})},k.name))}):a.jsx("div",{className:"text-center py-8 text-bambu-gray",children:r?"No tags match your search":"No tags found"})}),a.jsx("div",{className:"flex gap-3 p-4 border-t border-bambu-dark-tertiary flex-shrink-0",children:a.jsx(De,{variant:"secondary",onClick:t,className:"flex-1",children:"Close"})})]})})})}function Vh(t){const e=t.filename;if(e){const n=e.toLowerCase();if(n.endsWith(".gcode")||n.includes(".gcode."))return!0}return!!(t.total_layers||t.print_time_seconds)}async function Gh(t,e,n,r){try{if(n==="source"){const{token:i}=await oe.createSourceSlicerToken(t),s=oe.getSourceSlicerDownloadUrl(t,i,e);im(`${window.location.origin}${s}`,r)}else{const{token:i}=await oe.createArchiveSlicerToken(t),s=oe.getArchiveSlicerDownloadUrl(t,i,e);im(`${window.location.origin}${s}`,r)}}catch{const i=n==="source"?oe.getSource3mfForSlicer(t,e):oe.getArchiveForSlicer(t,e);im(`${window.location.origin}${i}`,r)}}function ket({archive:t,printerName:e,isSelected:n,onSelect:r,selectionMode:i,projects:s,isHighlighted:o,timeFormat:l="system",preferredSlicer:c="bambu_studio",useSlicerApi:d=!1,currency:u,t:m,onNavigateToArchive:p}){o&&console.log("ArchiveCard isHighlighted=true for archive:",t.id);const f=tn(),{showToast:x}=rn(),{hasPermission:v,canModify:b}=gr(),g=Aie(),S=as(),[N,P]=w.useState(!1),[C,A]=w.useState(!1),[T,F]=w.useState(!1),[k,E]=w.useState(!1),[$,z]=w.useState(!1),[X,R]=w.useState(!1),[ne,re]=w.useState([]),[J,ue]=w.useState(!1),[ie,se]=w.useState(!1),[K,ae]=w.useState(!1),[pe,be]=w.useState(!1),[fe,B]=w.useState(!1),[Y,D]=w.useState(!1),[M,U]=w.useState(!1),[Q,O]=w.useState(null),[ce,I]=w.useState(null),[G,q]=w.useState(!1),[W,te]=w.useState(null),le=w.useRef(null),ye=w.useRef(null),he=w.useRef(null),{data:me}=Qe({queryKey:["archive-plates",t.id],queryFn:()=>oe.getArchivePlates(t.id),enabled:G,staleTime:300*1e3}),H=t.duplicate_sequence??0,L=t.original_archive_id??null,ee=me?.plates??[],ge=me?.is_multi_plate??!1,ke=ce??0,je=async()=>{try{const de=await oe.getArchivePlates(t.id);if(de.has_gcode===!1){x(m("archives.platePicker.noGcode"),"info");return}if(de.is_multi_plate&&de.plates.length>1){te(de.plates);return}}catch{}S(`/gcode-viewer?archive=${t.id}`)},Te=ot({mutationFn:()=>oe.deleteArchiveTimelapse(t.id),onSuccess:()=>{f.invalidateQueries({queryKey:["archives"]}),x(m("archives.toast.timelapseRemoved"))},onError:de=>{x(de.message||m("archives.toast.failedRemoveTimelapse"),"error")}}),xe=ot({mutationFn:de=>oe.uploadArchiveTimelapse(t.id,de),onSuccess:de=>{f.invalidateQueries({queryKey:["archives"]}),x(m("archives.toast.timelapseUploaded",{filename:de.filename}))},onError:de=>{x(de.message||m("archives.toast.failedUploadTimelapse"),"error")}}),Fe=ot({mutationFn:de=>oe.uploadSource3mf(t.id,de),onSuccess:de=>{f.invalidateQueries({queryKey:["archives"]}),x(m("archives.toast.source3mfAttached",{filename:de.filename}))},onError:de=>{x(de.message||m("archives.toast.failedUploadSource3mf"),"error")}}),Be=ot({mutationFn:()=>oe.deleteSource3mf(t.id),onSuccess:()=>{f.invalidateQueries({queryKey:["archives"]}),x(m("archives.toast.source3mfRemoved"))},onError:de=>{x(de.message||m("archives.toast.failedRemoveSource3mf"),"error")}}),Ce=ot({mutationFn:de=>oe.uploadF3d(t.id,de),onSuccess:de=>{f.invalidateQueries({queryKey:["archives"]}),x(m("archives.toast.f3dAttached",{filename:de.filename}))},onError:de=>{x(de.message||m("archives.toast.failedUploadF3d"),"error")}}),ze=ot({mutationFn:()=>oe.deleteF3d(t.id),onSuccess:()=>{f.invalidateQueries({queryKey:["archives"]}),x(m("archives.toast.f3dRemoved"))},onError:de=>{x(de.message||m("archives.toast.failedRemoveF3d"),"error")}}),Me=ot({mutationFn:()=>oe.scanArchiveTimelapse(t.id),onSuccess:de=>{de.status==="attached"?(f.invalidateQueries({queryKey:["archives"]}),x(m("archives.toast.timelapseAttached",{filename:de.filename}))):de.status==="exists"?x(m("archives.toast.timelapseAlreadyAttached")):de.status==="not_found"&&de.available_files&&de.available_files.length>0?(re(de.available_files),R(!0)):x(de.message||m("archives.toast.noMatchingTimelapse"),"warning")},onError:de=>{x(de.message||m("archives.toast.failedScanTimelapse"),"error")}}),Oe=ot({mutationFn:de=>oe.selectArchiveTimelapse(t.id,de),onSuccess:de=>{f.invalidateQueries({queryKey:["archives"]}),x(m("archives.toast.timelapseAttached",{filename:de.filename})),R(!1),re([])},onError:de=>{x(de.message||m("archives.toast.failedAttachTimelapse"),"error")}}),Ge=ot({mutationFn:()=>oe.deleteArchive(t.id),onSuccess:()=>{f.invalidateQueries({queryKey:["archives"]}),x(m("archives.toast.archiveDeleted"))},onError:()=>{x(m("archives.toast.failedDeleteArchive"),"error")}}),et=ot({mutationFn:()=>oe.toggleFavorite(t.id),onSuccess:de=>{f.invalidateQueries({queryKey:["archives"]}),x(de.is_favorite?m("archives.toast.addedToFavorites"):m("archives.toast.removedFromFavorites"))}}),{data:ve}=Qe({queryKey:["archive-folders",t.id],queryFn:()=>oe.getLibraryFoldersByArchive(t.id)}),Xe=ot({mutationFn:de=>oe.updateArchive(t.id,{project_id:de}),onSuccess:()=>{f.invalidateQueries({queryKey:["archives"]}),f.invalidateQueries({queryKey:["projects"]}),x(m("archives.toast.projectUpdated"))},onError:()=>{x(m("archives.toast.failedUpdateProject"),"error")}}),Pe=de=>{de.preventDefault(),O({x:de.clientX,y:de.clientY})},we=[...Vh(t)?[{label:m("archives.menu.print"),icon:a.jsx(Mr,{className:"w-4 h-4"}),onClick:()=>P(!0),disabled:!t.file_path||!b("archives","reprint",t.created_by_id),title:t.file_path?b("archives","reprint",t.created_by_id)?void 0:m("archives.permission.noReprint"):m("archives.card.noFileForReprint")},{label:m("archives.menu.schedule"),icon:a.jsx(da,{className:"w-4 h-4"}),onClick:()=>be(!0),disabled:!t.file_path||!v("queue:create"),title:t.file_path?v("queue:create")?void 0:m("archives.permission.noAddToQueue"):m("archives.card.noFileForReprint")},{label:m("archives.menu.openInBambuStudio"),icon:a.jsx(Ar,{className:"w-4 h-4"}),onClick:()=>{const de=t.print_name||t.filename||"model";Gh(t.id,de,"file",c)},disabled:!t.file_path,title:t.file_path?void 0:m("archives.card.noFileForReprint")}]:[{label:m("archives.menu.slice"),icon:d?a.jsx(Ud,{className:"w-4 h-4"}):a.jsx(Ar,{className:"w-4 h-4"}),onClick:()=>{if(d)A(!0);else{const de=t.print_name||t.filename||"model";Gh(t.id,de,"file",c)}}}],{label:t.external_url?m("archives.menu.externalLink"):m("archives.menu.viewOnMakerWorld"),icon:a.jsx(qs,{className:"w-4 h-4"}),onClick:()=>{const de=t.external_url||t.makerworld_url;de&&window.open(de,"_blank")},disabled:!t.external_url&&!t.makerworld_url},{label:"",divider:!0,onClick:()=>{}},{label:m("archives.menu.preview3d"),icon:a.jsx(xi,{className:"w-4 h-4"}),onClick:()=>{je()}},{label:m("archives.menu.viewTimelapse"),icon:a.jsx(op,{className:"w-4 h-4"}),onClick:()=>z(!0),disabled:!t.timelapse_path},{label:m("archives.menu.scanForTimelapse"),icon:a.jsx(FN,{className:"w-4 h-4"}),onClick:()=>Me.mutate(),disabled:!t.printer_id||!!t.timelapse_path||Me.isPending||!b("archives","update",t.created_by_id),title:b("archives","update",t.created_by_id)?void 0:m("archives.permission.noUpdateArchives")},{label:m("archives.menu.uploadTimelapse"),icon:a.jsx(_a,{className:"w-4 h-4"}),onClick:()=>he.current?.click(),disabled:!!t.timelapse_path||!b("archives","update",t.created_by_id),title:b("archives","update",t.created_by_id)?void 0:m("archives.permission.noUpdateArchives")},...t.timelapse_path?[{label:m("archives.menu.removeTimelapse"),icon:a.jsx(Zt,{className:"w-4 h-4"}),onClick:()=>U(!0),danger:!0,disabled:!b("archives","update",t.created_by_id),title:b("archives","update",t.created_by_id)?void 0:m("archives.permission.noUpdateArchives")}]:[],{label:"",divider:!0,onClick:()=>{}},{label:t.source_3mf_path?m("archives.menu.downloadSource3mf"):m("archives.menu.uploadSource3mf"),icon:a.jsx(TN,{className:"w-4 h-4"}),onClick:()=>{t.source_3mf_path?oe.downloadSource3mf(t.id).catch(de=>{console.error("Source 3MF download failed:",de)}):le.current?.click()},disabled:!t.source_3mf_path&&!b("archives","update",t.created_by_id),title:!t.source_3mf_path&&!b("archives","update",t.created_by_id)?m("archives.permission.noUploadFiles"):void 0},...t.source_3mf_path?[{label:m("archives.menu.replaceSource3mf"),icon:a.jsx(_a,{className:"w-4 h-4"}),onClick:()=>le.current?.click(),disabled:!b("archives","update",t.created_by_id),title:b("archives","update",t.created_by_id)?void 0:m("archives.permission.noUpdateArchives")},{label:m("archives.menu.removeSource3mf"),icon:a.jsx(Zt,{className:"w-4 h-4"}),onClick:()=>B(!0),danger:!0,disabled:!b("archives","update",t.created_by_id),title:b("archives","update",t.created_by_id)?void 0:m("archives.permission.noUpdateArchives")}]:[],{label:t.f3d_path?m("archives.menu.replaceF3d"):m("archives.menu.uploadF3d"),icon:a.jsx(xi,{className:"w-4 h-4"}),onClick:()=>ye.current?.click(),disabled:!b("archives","update",t.created_by_id),title:b("archives","update",t.created_by_id)?void 0:m("archives.permission.noUpdateArchives")},...t.f3d_path?[{label:m("archives.menu.downloadF3d"),icon:a.jsx(aa,{className:"w-4 h-4"}),onClick:()=>{oe.downloadF3d(t.id).catch(de=>{console.error("F3D download failed:",de)})}},{label:m("archives.menu.removeF3d"),icon:a.jsx(Zt,{className:"w-4 h-4"}),onClick:()=>D(!0),danger:!0,disabled:!b("archives","update",t.created_by_id),title:b("archives","update",t.created_by_id)?void 0:m("archives.permission.noUpdateArchives")}]:[],{label:"",divider:!0,onClick:()=>{}},{label:m("archives.menu.download"),icon:a.jsx(aa,{className:"w-4 h-4"}),onClick:()=>{oe.downloadArchive(t.id,`${t.print_name||t.filename}.3mf`).catch(de=>{console.error("Archive download failed:",de)})},disabled:!v("archives:read"),title:v("archives:read")?void 0:m("archives.permission.noDownload")},{label:m("archives.menu.copyDownloadLink"),icon:a.jsx(ns,{className:"w-4 h-4"}),onClick:()=>{const de=`${window.location.origin}${oe.getArchiveDownload(t.id)}`;navigator.clipboard.writeText(de).then(()=>{x(m("archives.toast.linkCopied"))}).catch(()=>{x(m("archives.toast.failedCopyLink"),"error")})},disabled:!v("archives:read"),title:v("archives:read")?void 0:m("archives.permission.noCopyLink")},{label:m("archives.menu.qrCode"),icon:a.jsx(cJ,{className:"w-4 h-4"}),onClick:()=>ue(!0)},{label:t.photos?.length?m("archives.menu.viewPhotosCount",{count:t.photos.length}):m("archives.menu.viewPhotos"),icon:a.jsx(Qx,{className:"w-4 h-4"}),onClick:()=>se(!0),disabled:!t.photos?.length},{label:m("archives.menu.projectPage"),icon:a.jsx(Ns,{className:"w-4 h-4"}),onClick:()=>ae(!0)},{label:"",divider:!0,onClick:()=>{}},{label:t.is_favorite?m("archives.menu.removeFromFavorites"):m("archives.menu.addToFavorites"),icon:a.jsx(bg,{className:`w-4 h-4 ${t.is_favorite?"fill-yellow-400 text-yellow-400":""}`}),onClick:()=>et.mutate(),disabled:!b("archives","update",t.created_by_id),title:b("archives","update",t.created_by_id)?void 0:m("archives.permission.noUpdateArchives")},{label:m("archives.menu.edit"),icon:a.jsx(li,{className:"w-4 h-4"}),onClick:()=>E(!0),disabled:!b("archives","update",t.created_by_id),title:b("archives","update",t.created_by_id)?void 0:m("archives.permission.noUpdateArchives")},...t.project_id&&t.project_name?[{label:m("archives.menu.goToProject",{name:t.project_name}),icon:a.jsx($s,{className:"w-4 h-4 text-bambu-green"}),onClick:()=>window.location.href="/projects"}]:[],{label:m("archives.menu.addToProject"),icon:a.jsx($s,{className:"w-4 h-4"}),onClick:()=>{},disabled:!b("archives","update",t.created_by_id),title:b("archives","update",t.created_by_id)?void 0:m("archives.permission.noUpdateArchives"),submenuSearchPlaceholder:(s?.filter(de=>de.status==="active").length??0)>5?m("archives.menu.searchProjects"):void 0,submenu:(()=>{const de=[];if(t.project_id&&de.push({label:m("archives.menu.removeFromProject"),icon:a.jsx(Bt,{className:"w-4 h-4"}),onClick:()=>Xe.mutate(null),disabled:!b("archives","update",t.created_by_id)}),!s)de.push({label:m("archives.menu.loading"),icon:a.jsx(mt,{className:"w-4 h-4 animate-spin"}),onClick:()=>{},disabled:!0});else{const Se=s.filter($e=>$e.status==="active").sort(($e,st)=>$e.name.localeCompare(st.name));Se.length===0?de.push({label:m("archives.menu.noProjectsAvailable"),icon:a.jsx($s,{className:"w-4 h-4 opacity-50"}),onClick:()=>{},disabled:!0}):Se.forEach($e=>{de.push({label:$e.name,icon:a.jsx("div",{className:"w-3 h-3 rounded-full flex-shrink-0",style:{backgroundColor:$e.color||"#888"}}),onClick:()=>Xe.mutate($e.id),disabled:t.project_id===$e.id||!b("archives","update",t.created_by_id)})})}return de})()},{label:m(n?"archives.menu.deselect":"archives.menu.select"),icon:n?a.jsx(As,{className:"w-4 h-4"}):a.jsx(mo,{className:"w-4 h-4"}),onClick:()=>r(t.id)},{label:"",divider:!0,onClick:()=>{}},{label:m("archives.menu.delete"),icon:a.jsx(Zt,{className:"w-4 h-4"}),onClick:()=>F(!0),danger:!0,disabled:!b("archives","delete",t.created_by_id),title:b("archives","delete",t.created_by_id)?void 0:m("archives.permission.noDelete")}];return a.jsxs(wt,{"data-archive-id":t.id,className:`relative flex flex-col group ${n?"ring-2 ring-bambu-green":""} ${i?"cursor-pointer":""}`,style:o?{outline:"4px solid #facc15",outlineOffset:"2px"}:void 0,onContextMenu:Pe,onClick:i?()=>r(t.id):void 0,children:[i&&a.jsx("button",{className:"absolute top-2 left-2 z-10 p-1 rounded bg-black/50 hover:bg-black/70 transition-colors",onClick:de=>{de.stopPropagation(),r(t.id)},children:n?a.jsx(As,{className:"w-5 h-5 text-bambu-green"}):a.jsx(mo,{className:"w-5 h-5 text-white"})}),a.jsxs("div",{className:"aspect-video bg-bambu-dark relative flex-shrink-0 overflow-hidden rounded-t-xl",onMouseEnter:()=>q(!0),onMouseLeave:()=>q(!1),children:[t.thumbnail_path?a.jsx("img",{src:ce!==null&&ee.length>0?oe.getArchivePlateThumbnail(t.id,ee[ke]?.index??0):oe.getArchiveThumbnail(t.id),alt:t.print_name||t.filename,className:"w-full h-full object-cover"}):a.jsx("div",{className:"w-full h-full flex items-center justify-center",children:a.jsx(Bd,{className:"w-12 h-12 text-bambu-dark-tertiary"})}),ge&&ee.length>1&&a.jsxs(a.Fragment,{children:[a.jsx("button",{className:`absolute left-1 top-1/2 -translate-y-1/2 p-1 rounded-full bg-black/60 hover:bg-black/80 transition-all ${g?"opacity-100":"opacity-0 group-hover:opacity-100"}`,onClick:de=>{de.stopPropagation(),I(Se=>{const $e=Se??0;return $e>0?$e-1:ee.length-1})},title:m("archives.card.previousPlate"),children:a.jsx(Uo,{className:"w-4 h-4 text-white"})}),a.jsx("button",{className:`absolute right-1 top-1/2 -translate-y-1/2 p-1 rounded-full bg-black/60 hover:bg-black/80 transition-all ${g?"opacity-100":"opacity-0 group-hover:opacity-100"}`,onClick:de=>{de.stopPropagation(),I(Se=>{const $e=Se??0;return $ea.jsx("button",{className:`w-2 h-2 rounded-full transition-colors ${Se===ke?"bg-bambu-green":"bg-white/50 hover:bg-white/80"}`,onClick:$e=>{$e.stopPropagation(),I(Se)},title:de.name||m("archives.card.plateNumber",{index:de.index})},de.index))})]}),a.jsx("button",{className:`absolute top-2 left-2 p-1.5 rounded bg-black/50 hover:bg-black/70 transition-all ${g?"opacity-100":"opacity-0 group-hover:opacity-100"} ${i?"left-10":""}`,onClick:de=>{de.stopPropagation();const Se=de.currentTarget.getBoundingClientRect();O({x:Se.left,y:Se.bottom+4})},title:m("archives.card.moreOptions"),children:a.jsx(ip,{className:"w-5 h-5 text-white"})}),a.jsx("button",{className:`absolute top-2 right-2 p-1 rounded transition-colors ${b("archives","update",t.created_by_id)?"bg-black/50 hover:bg-black/70":"bg-black/30 cursor-not-allowed"}`,onClick:de=>{de.stopPropagation(),b("archives","update",t.created_by_id)&&et.mutate()},disabled:!b("archives","update",t.created_by_id),title:b("archives","update",t.created_by_id)?t.is_favorite?m("archives.card.removeFromFavorites"):m("archives.card.addToFavorites"):m("archives.permission.noUpdateArchives"),children:a.jsx(bg,{className:`w-5 h-5 ${t.is_favorite?"text-yellow-400 fill-yellow-400":"text-white"} ${b("archives","update",t.created_by_id)?"":"opacity-50"}`})}),(t.status==="failed"||t.status==="aborted")&&a.jsx("div",{className:"absolute top-2 left-12 px-2 py-1 rounded text-xs bg-status-error/80 text-white",children:t.status==="aborted"?m("archives.card.cancelled"):m("archives.card.failed")}),t.duplicate_count>0&&H>0&&L&&a.jsxs("button",{onClick:de=>{de.stopPropagation(),p?.(L)},className:"absolute top-2 right-12 px-2 py-1 rounded text-xs bg-purple-500/80 hover:bg-purple-600/90 text-white flex items-center gap-1 transition-colors cursor-pointer",title:m("archives.viewOriginalPrint",{id:L}),children:[a.jsx(ns,{className:"w-3 h-3"}),"#",H]}),t.duplicate_count>0&&H===0&&a.jsxs("span",{className:"absolute top-2 right-12 px-2 py-1 rounded text-xs bg-purple-500/80 text-white flex items-center gap-1",title:`${t.duplicate_count} reprint${t.duplicate_count===1?"":"s"}`,children:[a.jsx(sJ,{className:"w-3 h-3"}),"+",t.duplicate_count]}),t.source_3mf_path&&a.jsx("button",{className:"absolute bottom-2 left-2 p-1.5 rounded bg-black/60 hover:bg-black/80 transition-colors",onClick:de=>{de.stopPropagation();const Se=(t.print_name||t.filename||"source").replace(/\.gcode\.3mf$/i,"")+"_source";Gh(t.id,Se,"source",c)},title:m("archives.card.openSource3mf"),children:a.jsx(TN,{className:"w-4 h-4 text-orange-400"})}),t.f3d_path&&a.jsx("button",{className:`absolute bottom-2 ${t.source_3mf_path?"left-12":"left-2"} p-1.5 rounded bg-black/60 hover:bg-black/80 transition-colors`,onClick:de=>{de.stopPropagation(),oe.downloadF3d(t.id).catch(Se=>{console.error("F3D download failed:",Se)})},title:m("archives.card.downloadF3d"),children:a.jsx(xi,{className:"w-4 h-4 text-cyan-400"})}),a.jsx("button",{className:"absolute bottom-2 right-2 p-1.5 rounded bg-black/60 hover:bg-black/80 transition-colors",onClick:de=>{de.stopPropagation(),je()},title:m("archives.card.preview3d"),children:a.jsx(ia,{className:"w-4 h-4 text-white"})}),t.timelapse_path&&a.jsx("button",{className:"absolute bottom-2 right-12 p-1.5 rounded bg-black/60 hover:bg-black/80 transition-colors",onClick:de=>{de.stopPropagation(),z(!0)},title:m("archives.card.viewTimelapse"),children:a.jsx(op,{className:"w-4 h-4 text-bambu-green"})}),t.photos&&t.photos.length>0&&a.jsxs("button",{className:`absolute bottom-2 ${t.timelapse_path?"right-[5.5rem]":"right-12"} p-1.5 rounded bg-black/60 hover:bg-black/80 transition-colors`,onClick:de=>{de.stopPropagation(),se(!0)},title:t.photos.length===1?m("archives.card.viewPhoto"):m("archives.card.viewPhotos",{count:t.photos.length}),children:[a.jsx(Qx,{className:"w-4 h-4 text-blue-400"}),t.photos.length>1&&a.jsx("span",{className:"absolute -top-1 -right-1 w-4 h-4 bg-blue-500 rounded-full text-[10px] text-white flex items-center justify-center",children:t.photos.length})]}),ve&&ve.length>0&&a.jsx(ws,{to:`/files?folder=${ve[0].id}`,className:"absolute bottom-2 p-1.5 rounded bg-black/60 hover:bg-black/80 transition-colors",onClick:de=>de.stopPropagation(),title:m("archives.card.openFolder",{name:ve[0].name}),style:{left:t.source_3mf_path?t.f3d_path?"5.5rem":"3rem":t.f3d_path?"3rem":"0.5rem"},children:a.jsx(fl,{className:"w-4 h-4 text-yellow-400"})})]}),a.jsxs(St,{className:"p-4 flex-1 flex flex-col",children:[a.jsxs("p",{className:"text-[10px] text-bambu-gray/70 mb-1",children:["#",t.id]}),a.jsxs("div",{className:"flex items-center justify-between gap-2 mb-1",children:[a.jsx("h3",{className:"min-w-0 font-medium text-white truncate",children:t.print_name||t.filename}),a.jsx(De,{variant:"ghost",size:"sm",className:"p-1 sm:p-1.5 shrink-0",onClick:()=>E(!0),disabled:!b("archives","update",t.created_by_id),title:b("archives","update",t.created_by_id)?m("archives.card.edit"):m("archives.card.noPermissionEdit"),children:a.jsx(li,{className:"w-3 h-3 sm:w-4 sm:h-4"})})]}),a.jsxs("div",{className:"flex items-center gap-2 mb-3 flex-wrap",children:[a.jsx("p",{className:"text-xs text-bambu-gray",children:e}),a.jsx("span",{className:`text-[10px] px-1.5 py-0.5 rounded font-medium ${Vh(t)?"bg-bambu-green/20 text-bambu-green":"bg-orange-500/20 text-orange-400"}`,title:Vh(t)?m("archives.card.slicedFile"):m("archives.card.sourceFile"),children:Vh(t)?m("archives.card.gcode"):m("archives.card.source")}),t.content_hash&&a.jsx("span",{className:"text-[10px] px-1.5 py-0.5 rounded font-mono bg-bambu-dark-tertiary/50 text-bambu-gray-light opacity-0 transition-opacity duration-150 group-hover:opacity-100",title:`SHA256: ${t.content_hash}`,children:t.content_hash.slice(0,8).toUpperCase()}),t.project_name&&a.jsx("span",{className:"text-xs px-1.5 py-0.5 rounded-full truncate max-w-[120px]",style:{backgroundColor:`${s?.find(de=>de.id===t.project_id)?.color||"#6b7280"}20`,color:s?.find(de=>de.id===t.project_id)?.color||"#6b7280"},title:m("archives.card.project",{name:t.project_name}),children:t.project_name})]}),a.jsxs("div",{className:"grid grid-cols-2 gap-2 text-xs mb-4 min-h-[48px]",children:[(t.print_time_seconds||t.actual_time_seconds)&&a.jsxs("div",{className:"flex items-center gap-1.5 text-bambu-gray",title:t.time_accuracy?`Estimated: ${Qi(t.print_time_seconds||0)} + [&::-webkit-slider-thumb]:cursor-pointer`}),a.jsx("span",{className:"text-xs text-bambu-gray w-12",children:Eh(f)})]}),a.jsxs("div",{className:"flex items-center justify-between",children:[a.jsxs("div",{className:"flex items-center gap-2",children:[a.jsx("button",{onClick:N,className:"p-2 hover:bg-bambu-dark-tertiary rounded-lg transition-colors",title:"Skip back 5s",children:a.jsx(Vxe,{className:"w-5 h-5 text-bambu-gray"})}),a.jsx("button",{onClick:g,className:"p-2 bg-bambu-green hover:bg-bambu-green-dark rounded-lg transition-colors",children:l?a.jsx(cS,{className:"w-5 h-5 text-white"}):a.jsx(rs,{className:"w-5 h-5 text-white"})}),a.jsx("button",{onClick:P,className:"p-2 hover:bg-bambu-dark-tertiary rounded-lg transition-colors",title:"Skip forward 5s",children:a.jsx(nT,{className:"w-5 h-5 text-bambu-gray"})})]}),a.jsxs("div",{className:"flex items-center gap-2",children:[a.jsx("span",{className:"text-sm text-bambu-gray",children:"Speed:"}),a.jsx("div",{className:"flex gap-1",children:get.map(A=>a.jsxs("button",{onClick:()=>u(A),className:`px-2 py-1 text-xs rounded transition-colors ${d===A?"bg-bambu-green text-white":"bg-bambu-dark-tertiary text-bambu-gray hover:bg-bambu-dark-tertiary/80"}`,children:[A,"x"]},A))})]})]})]})]})]}),v&&r&&a.jsx(fet,{archiveId:r,timelapseSrc:t,onClose:()=>b(!1),onSave:s})]})}function bet({archiveIds:t,onClose:e}){w.useEffect(()=>{const s=o=>{o.key==="Escape"&&e()};return window.addEventListener("keydown",s),()=>window.removeEventListener("keydown",s)},[e]);const{data:n,isLoading:r,error:i}=Qe({queryKey:["archive-comparison",t],queryFn:()=>oe.compareArchives(t)});return a.jsx("div",{className:"fixed inset-0 bg-black/80 flex items-center justify-center z-50 p-4",onClick:e,children:a.jsxs("div",{className:"bg-bambu-dark-secondary rounded-lg max-w-4xl w-full max-h-[90vh] flex flex-col border border-bambu-dark-tertiary",onClick:s=>s.stopPropagation(),children:[a.jsxs("div",{className:"flex items-center justify-between p-4 border-b border-bambu-dark-tertiary",children:[a.jsxs("h3",{className:"text-lg font-semibold text-white",children:["Compare Archives (",t.length,")"]}),a.jsx("button",{onClick:e,className:"text-bambu-gray hover:text-white p-1",children:a.jsx(Bt,{className:"w-5 h-5"})})]}),a.jsx("div",{className:"flex-1 overflow-auto p-4 bg-bambu-dark-secondary",children:r?a.jsx("div",{className:"flex items-center justify-center py-12",children:a.jsx(mt,{className:"w-8 h-8 text-bambu-green animate-spin"})}):i?a.jsxs("div",{className:"text-center py-12 text-red-400",children:[a.jsx(Cn,{className:"w-12 h-12 mx-auto mb-4 opacity-50"}),a.jsx("p",{children:"Failed to load comparison"}),a.jsx("p",{className:"text-sm text-bambu-gray mt-2",children:i instanceof Error?i.message:"Unknown error"})]}):n?a.jsx(xet,{comparison:n}):null}),a.jsx("div",{className:"p-4 border-t border-bambu-dark-tertiary",children:a.jsx(De,{variant:"secondary",onClick:e,className:"w-full",children:"Close"})})]})})}function xet({comparison:t}){return a.jsxs("div",{className:"space-y-6",children:[a.jsx("div",{className:"overflow-x-auto",children:a.jsxs("table",{className:"w-full",children:[a.jsx("thead",{children:a.jsxs("tr",{children:[a.jsx("th",{className:"text-left text-sm text-bambu-gray font-medium pb-2 pr-4 min-w-[150px]",children:"Setting"}),t.archives.map(e=>a.jsxs("th",{className:"text-left text-sm font-medium pb-2 px-2 min-w-[120px]",children:[a.jsx("div",{className:"text-white truncate max-w-[150px]",title:e.print_name,children:e.print_name}),a.jsx("div",{className:`text-xs ${e.status==="completed"?"text-status-ok":e.status==="failed"?"text-status-error":"text-bambu-gray"}`,children:e.status})]},e.id))]})}),a.jsx("tbody",{className:"divide-y divide-bambu-gray/20",children:t.comparison.map(e=>a.jsxs("tr",{className:e.has_difference?"bg-yellow-500/5":"",children:[a.jsx("td",{className:"py-2 pr-4 text-sm",children:a.jsxs("div",{className:"flex items-center gap-2",children:[e.has_difference&&a.jsx(Cn,{className:"w-3 h-3 text-yellow-400 flex-shrink-0"}),a.jsx("span",{className:e.has_difference?"text-yellow-400":"text-bambu-gray",children:e.label})]})}),e.values.map((n,r)=>a.jsxs("td",{className:"py-2 px-2 text-sm text-white",children:[n??a.jsx("span",{className:"text-bambu-gray/50",children:"-"}),e.unit&&n!==null&&a.jsx("span",{className:"text-bambu-gray ml-1",children:e.unit})]},r))]},e.field))})]})}),t.differences.length>0&&a.jsxs("div",{className:"p-4 bg-yellow-500/10 border border-yellow-500/30 rounded-lg",children:[a.jsxs("h4",{className:"text-sm font-medium text-yellow-400 mb-2 flex items-center gap-2",children:[a.jsx(Cn,{className:"w-4 h-4"}),t.differences.length," Difference",t.differences.length>1?"s":""," Found"]}),a.jsxs("ul",{className:"text-sm text-white/80 space-y-1",children:[t.differences.slice(0,5).map(e=>a.jsxs("li",{children:[a.jsx("span",{className:"text-yellow-400",children:e.label}),": ",e.values.join(" vs ")," ",e.unit||""]},e.field)),t.differences.length>5&&a.jsxs("li",{className:"text-bambu-gray",children:["...and ",t.differences.length-5," more"]})]})]}),t.success_correlation.has_both_outcomes?a.jsxs("div",{className:"p-4 bg-bambu-dark rounded-lg",children:[a.jsxs("h4",{className:"text-sm font-medium text-white mb-3 flex items-center gap-2",children:[a.jsx(jr,{className:"w-4 h-4 text-bambu-green"}),"Success/Failure Analysis"]}),a.jsxs("div",{className:"flex items-center gap-4 text-sm mb-3",children:[a.jsxs("span",{className:"text-bambu-green",children:[t.success_correlation.successful_count," successful"]}),a.jsxs("span",{className:"text-red-400",children:[t.success_correlation.failed_count," failed"]})]}),t.success_correlation.insights&&t.success_correlation.insights.length>0?a.jsx("div",{className:"space-y-2",children:t.success_correlation.insights.map(e=>a.jsxs("div",{className:"text-sm p-2 bg-bambu-dark-secondary rounded",children:[a.jsxs("span",{className:"text-white font-medium",children:[e.label,":"]})," ",a.jsx("span",{className:"text-white/80",children:e.insight})]},e.field))}):a.jsx("p",{className:"text-sm text-bambu-gray",children:"No clear correlations found between settings and outcomes."})]}):a.jsx("div",{className:"p-4 bg-bambu-dark rounded-lg text-sm text-bambu-gray",children:a.jsx("p",{children:t.success_correlation.message||"Need both successful and failed prints for correlation analysis."})})]})}function yet(t){const e=new Date(t),r=new Date().getTime()-e.getTime(),i=Math.floor(r/6e4);if(i<1)return"Just now";if(i<60)return`${i}m ago`;const s=Math.floor(i/60);return s<24?`${s}h ago`:`${Math.floor(s/24)}d ago`}function vet({upload:t,projects:e,onArchive:n,onDiscard:r,isArchiving:i,isDiscarding:s}){const[o,l]=w.useState(!1),[c,d]=w.useState(t.tags||""),[u,m]=w.useState(t.notes||""),[p,f]=w.useState(t.project_id),[x,v]=w.useState(!1);return a.jsx(wt,{children:a.jsxs(St,{className:"py-3",children:[a.jsxs("div",{className:"flex items-center justify-between",children:[a.jsxs("div",{className:"flex items-center gap-3",children:[a.jsx(sp,{className:"w-8 h-8 text-bambu-green flex-shrink-0"}),a.jsxs("div",{children:[a.jsx("p",{className:"text-white font-medium",title:t.filename,children:t.display_name||t.filename}),a.jsxs("div",{className:"flex items-center gap-2 text-xs text-bambu-gray",children:[a.jsx("span",{children:Yi(t.file_size)}),a.jsx("span",{children:"·"}),a.jsxs("span",{className:"flex items-center gap-1",children:[a.jsx(Yn,{className:"w-3 h-3"}),yet(t.uploaded_at)]}),t.source_ip&&a.jsxs(a.Fragment,{children:[a.jsx("span",{children:"·"}),a.jsxs("span",{children:["from ",t.source_ip]})]})]})]})]}),a.jsxs("div",{className:"flex items-center gap-2",children:[a.jsx("button",{onClick:()=>l(!o),className:"p-1 text-bambu-gray hover:text-white transition-colors",children:o?a.jsx(lc,{className:"w-5 h-5"}):a.jsx(zn,{className:"w-5 h-5"})}),a.jsx(De,{variant:"primary",size:"sm",onClick:()=>n(t.id,{tags:c,notes:u,project_id:p||void 0}),disabled:i,children:i?a.jsx(mt,{className:"w-4 h-4 animate-spin"}):a.jsxs(a.Fragment,{children:[a.jsx(lo,{className:"w-4 h-4"}),"Archive"]})}),a.jsx(De,{variant:"secondary",size:"sm",onClick:()=>v(!0),disabled:s,children:s?a.jsx(mt,{className:"w-4 h-4 animate-spin"}):a.jsx(Zt,{className:"w-4 h-4 text-red-400"})})]})]}),x&&a.jsx(yn,{title:"Discard Upload",message:`Are you sure you want to discard "${t.filename}"? This cannot be undone.`,confirmText:"Discard",variant:"danger",onConfirm:()=>{r(t.id),v(!1)},onCancel:()=>v(!1)}),o&&a.jsxs("div",{className:"mt-4 pt-4 border-t border-bambu-dark-tertiary space-y-3",children:[a.jsxs("div",{children:[a.jsx("label",{className:"block text-sm text-bambu-gray mb-1",children:"Tags"}),a.jsx("input",{type:"text",value:c,onChange:b=>d(b.target.value),placeholder:"e.g., functional, prototype, gift",className:"w-full bg-bambu-dark-secondary border border-bambu-dark-tertiary rounded-md px-3 py-2 text-white placeholder-bambu-gray text-sm"})]}),a.jsxs("div",{children:[a.jsx("label",{className:"block text-sm text-bambu-gray mb-1",children:"Notes"}),a.jsx("textarea",{value:u,onChange:b=>m(b.target.value),placeholder:"Add notes about this print...",rows:2,className:"w-full bg-bambu-dark-secondary border border-bambu-dark-tertiary rounded-md px-3 py-2 text-white placeholder-bambu-gray text-sm resize-none"})]}),a.jsxs("div",{children:[a.jsx("label",{className:"block text-sm text-bambu-gray mb-1",children:"Project"}),a.jsxs("select",{value:p||"",onChange:b=>f(b.target.value?Number(b.target.value):null),className:"w-full bg-bambu-dark-secondary border border-bambu-dark-tertiary rounded-md px-3 py-2 text-white text-sm",children:[a.jsx("option",{value:"",children:"No project"}),e.map(b=>a.jsx("option",{value:b.id,children:b.name},b.id))]})]})]})]})})}function wet(){const t=tn(),{showToast:e}=rn(),[n,r]=w.useState(!1),[i,s]=w.useState(!1),[o,l]=w.useState(new Set),[c,d]=w.useState(new Set),{data:u,isLoading:m}=Qe({queryKey:["pending-uploads"],queryFn:vx.list,refetchInterval:1e4}),{data:p}=Qe({queryKey:["projects"],queryFn:()=>oe.getProjects(),select:g=>[...g].sort((S,N)=>S.name.localeCompare(N.name))}),f=ot({mutationFn:({id:g,data:S})=>vx.archive(g,S),onMutate:({id:g})=>{l(S=>new Set(S).add(g))},onSettled:(g,S,{id:N})=>{l(P=>{const C=new Set(P);return C.delete(N),C})},onSuccess:g=>{t.invalidateQueries({queryKey:["pending-uploads"]}),t.invalidateQueries({queryKey:["archives"]}),e(`Archived: ${g.print_name}`)},onError:g=>{e(g.message||"Failed to archive","error")}}),x=ot({mutationFn:g=>vx.discard(g),onMutate:g=>{d(S=>new Set(S).add(g))},onSettled:(g,S,N)=>{d(P=>{const C=new Set(P);return C.delete(N),C})},onSuccess:()=>{t.invalidateQueries({queryKey:["pending-uploads"]}),e("Upload discarded")},onError:g=>{e(g.message||"Failed to discard","error")}}),v=ot({mutationFn:vx.archiveAll,onSuccess:g=>{t.invalidateQueries({queryKey:["pending-uploads"]}),t.invalidateQueries({queryKey:["archives"]}),e(`Archived ${g.archived} files${g.failed>0?`, ${g.failed} failed`:""}`)},onError:g=>{e(g.message||"Failed to archive all","error")}}),b=ot({mutationFn:vx.discardAll,onSuccess:g=>{t.invalidateQueries({queryKey:["pending-uploads"]}),e(`Discarded ${g.discarded} files`)},onError:g=>{e(g.message||"Failed to discard all","error")}});return m?a.jsx(wt,{children:a.jsx(St,{className:"py-8 flex justify-center",children:a.jsx(mt,{className:"w-6 h-6 animate-spin text-bambu-green"})})}):!u||u.length===0?null:a.jsxs("div",{className:"mb-6",children:[a.jsxs(wt,{className:"border-l-4 border-l-yellow-500",children:[a.jsx(pn,{children:a.jsxs("div",{className:"flex items-center justify-between",children:[a.jsxs("div",{className:"flex items-center gap-2",children:[a.jsx(_a,{className:"w-5 h-5 text-yellow-500"}),a.jsxs("h2",{className:"text-lg font-semibold text-white",children:["Pending Uploads (",u.length,")"]})]}),a.jsxs("div",{className:"flex items-center gap-2",children:[a.jsx(De,{variant:"primary",size:"sm",onClick:()=>r(!0),disabled:v.isPending,children:v.isPending?a.jsx(mt,{className:"w-4 h-4 animate-spin"}):a.jsxs(a.Fragment,{children:[a.jsx(lo,{className:"w-4 h-4"}),"Archive All"]})}),a.jsx(De,{variant:"secondary",size:"sm",onClick:()=>s(!0),disabled:b.isPending,children:b.isPending?a.jsx(mt,{className:"w-4 h-4 animate-spin"}):a.jsxs(a.Fragment,{children:[a.jsx(Zt,{className:"w-4 h-4"}),"Discard All"]})})]})]})}),a.jsxs(St,{children:[a.jsx("p",{className:"text-sm text-bambu-gray mb-4",children:"These files were uploaded via the virtual printer. Review and archive them to add to your collection."}),a.jsx("div",{className:"space-y-3",children:u.map(g=>a.jsx(vet,{upload:g,projects:p||[],onArchive:(S,N)=>f.mutate({id:S,data:N}),onDiscard:S=>x.mutate(S),isArchiving:o.has(g.id),isDiscarding:c.has(g.id)},g.id))})]})]}),n&&a.jsx(yn,{title:"Archive All Uploads",message:`Are you sure you want to archive all ${u.length} pending uploads?`,confirmText:"Archive All",onConfirm:()=>{v.mutate(),r(!1)},onCancel:()=>r(!1)}),i&&a.jsx(yn,{title:"Discard All Uploads",message:`Are you sure you want to discard all ${u.length} pending uploads? This cannot be undone.`,confirmText:"Discard All",variant:"danger",onConfirm:()=>{b.mutate(),s(!1)},onCancel:()=>s(!1)})]})}function _et({onClose:t}){const e=tn(),{showToast:n}=rn(),[r,i]=w.useState(""),[s,o]=w.useState(null),[l,c]=w.useState(""),[d,u]=w.useState(null),[m,p]=w.useState("count");w.useEffect(()=>{const k=E=>{E.key==="Escape"&&(s?o(null):d?u(null):t())};return window.addEventListener("keydown",k),()=>window.removeEventListener("keydown",k)},[t,s,d]);const{data:f,isLoading:x}=Qe({queryKey:["tags"],queryFn:oe.getTags}),v=ot({mutationFn:({oldName:k,newName:E})=>oe.renameTag(k,E),onSuccess:(k,{oldName:E,newName:$})=>{e.invalidateQueries({queryKey:["tags"]}),e.invalidateQueries({queryKey:["archives"]}),n(`Renamed "${E}" to "${$}" in ${k.affected} archive${k.affected!==1?"s":""}`),o(null)},onError:k=>{n(k.message||"Failed to rename tag","error")}}),b=ot({mutationFn:k=>oe.deleteTag(k),onSuccess:(k,E)=>{e.invalidateQueries({queryKey:["tags"]}),e.invalidateQueries({queryKey:["archives"]}),n(`Deleted "${E}" from ${k.affected} archive${k.affected!==1?"s":""}`),u(null)},onError:k=>{n(k.message||"Failed to delete tag","error")}}),g=k=>{o(k.name),c(k.name),u(null)},S=()=>{o(null),c("")},N=()=>{if(!s||!l.trim())return;const k=l.trim();if(k===s){S();return}v.mutate({oldName:s,newName:k})},P=k=>{k.key==="Enter"?(k.preventDefault(),N()):k.key==="Escape"&&(k.preventDefault(),S())},C=k=>{u(k),o(null)},A=()=>{d&&b.mutate(d)},T=f?.filter(k=>k.name.toLowerCase().includes(r.toLowerCase())).sort((k,E)=>m==="count"&&E.count-k.count||k.name.localeCompare(E.name)),F=f?.reduce((k,E)=>k+E.count,0)||0;return a.jsx("div",{className:"fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4",children:a.jsx(wt,{className:"w-full max-w-lg max-h-[80vh] flex flex-col",children:a.jsxs(St,{className:"p-0 flex flex-col min-h-0",children:[a.jsxs("div",{className:"flex items-center justify-between p-4 border-b border-bambu-dark-tertiary flex-shrink-0",children:[a.jsxs("div",{className:"flex items-center gap-2",children:[a.jsx(ym,{className:"w-5 h-5 text-bambu-green"}),a.jsx("h2",{className:"text-xl font-semibold text-white",children:"Manage Tags"})]}),a.jsx("button",{onClick:t,className:"text-bambu-gray hover:text-white transition-colors",children:a.jsx(Bt,{className:"w-5 h-5"})})]}),a.jsxs("div",{className:"p-4 border-b border-bambu-dark-tertiary flex-shrink-0",children:[a.jsxs("div",{className:"flex gap-2",children:[a.jsxs("div",{className:"relative flex-1",children:[a.jsx(Nr,{className:"absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-bambu-gray"}),a.jsx("input",{type:"text",placeholder:"Search tags...",className:"w-full pl-9 pr-3 py-2 bg-bambu-dark border border-bambu-dark-tertiary rounded-lg text-white text-sm focus:border-bambu-green focus:outline-none",value:r,onChange:k=>i(k.target.value)})]}),a.jsxs("select",{className:"px-3 py-2 bg-bambu-dark border border-bambu-dark-tertiary rounded-lg text-white text-sm focus:border-bambu-green focus:outline-none",value:m,onChange:k=>p(k.target.value),children:[a.jsx("option",{value:"count",children:"Sort by Count"}),a.jsx("option",{value:"name",children:"Sort by Name"})]})]}),f&&a.jsxs("p",{className:"text-xs text-bambu-gray mt-2",children:[f.length," tag",f.length!==1?"s":""," across ",F," usage",F!==1?"s":""]})]}),a.jsx("div",{className:"flex-1 overflow-y-auto min-h-0 p-4",children:x?a.jsx("div",{className:"flex items-center justify-center py-8",children:a.jsx(mt,{className:"w-6 h-6 animate-spin text-bambu-gray"})}):T?.length?a.jsx("div",{className:"space-y-2",children:T.map(k=>a.jsx("div",{className:"flex items-center gap-2 p-2 rounded-lg bg-bambu-dark hover:bg-bambu-dark-tertiary transition-colors group",children:s===k.name?a.jsxs("div",{className:"flex-1 flex items-center gap-2",children:[a.jsx("input",{type:"text",className:"flex-1 px-2 py-1 bg-bambu-dark-tertiary border border-bambu-green rounded text-white text-sm focus:outline-none",value:l,onChange:E=>c(E.target.value),onKeyDown:P,autoFocus:!0}),a.jsx(De,{size:"sm",variant:"primary",onClick:N,disabled:!l.trim()||v.isPending,className:"p-1.5",children:v.isPending?a.jsx(mt,{className:"w-4 h-4 animate-spin"}):a.jsx(jr,{className:"w-4 h-4"})}),a.jsx(De,{size:"sm",variant:"ghost",onClick:S,className:"p-1.5",children:a.jsx(Bt,{className:"w-4 h-4"})})]}):d===k.name?a.jsxs("div",{className:"flex-1 flex items-center gap-2",children:[a.jsx(Cn,{className:"w-4 h-4 text-yellow-400 flex-shrink-0"}),a.jsxs("span",{className:"text-sm text-bambu-gray-light flex-1",children:['Delete "',k.name,'" from ',k.count," archive",k.count!==1?"s":"","?"]}),a.jsx(De,{size:"sm",variant:"danger",onClick:A,disabled:b.isPending,className:"p-1.5",children:b.isPending?a.jsx(mt,{className:"w-4 h-4 animate-spin"}):a.jsx(Zt,{className:"w-4 h-4"})}),a.jsx(De,{size:"sm",variant:"ghost",onClick:()=>u(null),className:"p-1.5",children:a.jsx(Bt,{className:"w-4 h-4"})})]}):a.jsxs(a.Fragment,{children:[a.jsx(ym,{className:"w-4 h-4 text-bambu-gray flex-shrink-0"}),a.jsx("span",{className:"text-white flex-1 truncate",children:k.name}),a.jsx("span",{className:"px-2 py-0.5 rounded-full bg-bambu-dark-tertiary text-bambu-gray text-xs",children:k.count}),a.jsxs("div",{className:"flex items-center gap-1 opacity-0 group-hover:opacity-100 transition-opacity",children:[a.jsx("button",{onClick:()=>g(k),className:"p-1.5 rounded hover:bg-bambu-dark text-bambu-gray hover:text-white transition-colors",title:"Rename tag",children:a.jsx(li,{className:"w-4 h-4"})}),a.jsx("button",{onClick:()=>C(k.name),className:"p-1.5 rounded hover:bg-bambu-dark text-bambu-gray hover:text-red-400 transition-colors",title:"Delete tag",children:a.jsx(Zt,{className:"w-4 h-4"})})]})]})},k.name))}):a.jsx("div",{className:"text-center py-8 text-bambu-gray",children:r?"No tags match your search":"No tags found"})}),a.jsx("div",{className:"flex gap-3 p-4 border-t border-bambu-dark-tertiary flex-shrink-0",children:a.jsx(De,{variant:"secondary",onClick:t,className:"flex-1",children:"Close"})})]})})})}function Vh(t){const e=t.filename;if(e){const n=e.toLowerCase();if(n.endsWith(".gcode")||n.includes(".gcode."))return!0}return!!(t.total_layers||t.print_time_seconds)}async function Gh(t,e,n,r){try{if(n==="source"){const{token:i}=await oe.createSourceSlicerToken(t),s=oe.getSourceSlicerDownloadUrl(t,i,e);im(`${window.location.origin}${s}`,r)}else{const{token:i}=await oe.createArchiveSlicerToken(t),s=oe.getArchiveSlicerDownloadUrl(t,i,e);im(`${window.location.origin}${s}`,r)}}catch{const i=n==="source"?oe.getSource3mfForSlicer(t,e):oe.getArchiveForSlicer(t,e);im(`${window.location.origin}${i}`,r)}}function ket({archive:t,printerName:e,isSelected:n,onSelect:r,selectionMode:i,projects:s,isHighlighted:o,timeFormat:l="system",preferredSlicer:c="bambu_studio",useSlicerApi:d=!1,currency:u,t:m,onNavigateToArchive:p}){o&&console.log("ArchiveCard isHighlighted=true for archive:",t.id);const f=tn(),{showToast:x}=rn(),{hasPermission:v,canModify:b}=gr(),g=Aie(),S=as(),[N,P]=w.useState(!1),[C,A]=w.useState(!1),[T,F]=w.useState(!1),[k,E]=w.useState(!1),[$,z]=w.useState(!1),[X,R]=w.useState(!1),[ne,re]=w.useState([]),[J,ue]=w.useState(!1),[ie,se]=w.useState(!1),[K,ae]=w.useState(!1),[pe,be]=w.useState(!1),[fe,B]=w.useState(!1),[Y,D]=w.useState(!1),[M,U]=w.useState(!1),[Q,O]=w.useState(null),[ce,I]=w.useState(null),[G,q]=w.useState(!1),[W,te]=w.useState(null),le=w.useRef(null),ye=w.useRef(null),he=w.useRef(null),{data:me}=Qe({queryKey:["archive-plates",t.id],queryFn:()=>oe.getArchivePlates(t.id),enabled:G,staleTime:300*1e3}),H=t.duplicate_sequence??0,L=t.original_archive_id??null,ee=me?.plates??[],ge=me?.is_multi_plate??!1,ke=ce??0,je=async()=>{try{const de=await oe.getArchivePlates(t.id);if(de.has_gcode===!1){x(m("archives.platePicker.noGcode"),"info");return}if(de.is_multi_plate&&de.plates.length>1){te(de.plates);return}}catch{}S(`/gcode-viewer?archive=${t.id}`)},Te=ot({mutationFn:()=>oe.deleteArchiveTimelapse(t.id),onSuccess:()=>{f.invalidateQueries({queryKey:["archives"]}),x(m("archives.toast.timelapseRemoved"))},onError:de=>{x(de.message||m("archives.toast.failedRemoveTimelapse"),"error")}}),xe=ot({mutationFn:de=>oe.uploadArchiveTimelapse(t.id,de),onSuccess:de=>{f.invalidateQueries({queryKey:["archives"]}),x(m("archives.toast.timelapseUploaded",{filename:de.filename}))},onError:de=>{x(de.message||m("archives.toast.failedUploadTimelapse"),"error")}}),Fe=ot({mutationFn:de=>oe.uploadSource3mf(t.id,de),onSuccess:de=>{f.invalidateQueries({queryKey:["archives"]}),x(m("archives.toast.source3mfAttached",{filename:de.filename}))},onError:de=>{x(de.message||m("archives.toast.failedUploadSource3mf"),"error")}}),Be=ot({mutationFn:()=>oe.deleteSource3mf(t.id),onSuccess:()=>{f.invalidateQueries({queryKey:["archives"]}),x(m("archives.toast.source3mfRemoved"))},onError:de=>{x(de.message||m("archives.toast.failedRemoveSource3mf"),"error")}}),Ce=ot({mutationFn:de=>oe.uploadF3d(t.id,de),onSuccess:de=>{f.invalidateQueries({queryKey:["archives"]}),x(m("archives.toast.f3dAttached",{filename:de.filename}))},onError:de=>{x(de.message||m("archives.toast.failedUploadF3d"),"error")}}),ze=ot({mutationFn:()=>oe.deleteF3d(t.id),onSuccess:()=>{f.invalidateQueries({queryKey:["archives"]}),x(m("archives.toast.f3dRemoved"))},onError:de=>{x(de.message||m("archives.toast.failedRemoveF3d"),"error")}}),Me=ot({mutationFn:()=>oe.scanArchiveTimelapse(t.id),onSuccess:de=>{de.status==="attached"?(f.invalidateQueries({queryKey:["archives"]}),x(m("archives.toast.timelapseAttached",{filename:de.filename}))):de.status==="exists"?x(m("archives.toast.timelapseAlreadyAttached")):de.status==="not_found"&&de.available_files&&de.available_files.length>0?(re(de.available_files),R(!0)):x(de.message||m("archives.toast.noMatchingTimelapse"),"warning")},onError:de=>{x(de.message||m("archives.toast.failedScanTimelapse"),"error")}}),Oe=ot({mutationFn:de=>oe.selectArchiveTimelapse(t.id,de),onSuccess:de=>{f.invalidateQueries({queryKey:["archives"]}),x(m("archives.toast.timelapseAttached",{filename:de.filename})),R(!1),re([])},onError:de=>{x(de.message||m("archives.toast.failedAttachTimelapse"),"error")}}),Ge=ot({mutationFn:()=>oe.deleteArchive(t.id),onSuccess:()=>{f.invalidateQueries({queryKey:["archives"]}),x(m("archives.toast.archiveDeleted"))},onError:()=>{x(m("archives.toast.failedDeleteArchive"),"error")}}),et=ot({mutationFn:()=>oe.toggleFavorite(t.id),onSuccess:de=>{f.invalidateQueries({queryKey:["archives"]}),x(de.is_favorite?m("archives.toast.addedToFavorites"):m("archives.toast.removedFromFavorites"))}}),{data:ve}=Qe({queryKey:["archive-folders",t.id],queryFn:()=>oe.getLibraryFoldersByArchive(t.id)}),Xe=ot({mutationFn:de=>oe.updateArchive(t.id,{project_id:de}),onSuccess:()=>{f.invalidateQueries({queryKey:["archives"]}),f.invalidateQueries({queryKey:["projects"]}),x(m("archives.toast.projectUpdated"))},onError:()=>{x(m("archives.toast.failedUpdateProject"),"error")}}),Pe=de=>{de.preventDefault(),O({x:de.clientX,y:de.clientY})},we=[...Vh(t)?[{label:m("archives.menu.print"),icon:a.jsx(Mr,{className:"w-4 h-4"}),onClick:()=>P(!0),disabled:!t.file_path||!b("archives","reprint",t.created_by_id),title:t.file_path?b("archives","reprint",t.created_by_id)?void 0:m("archives.permission.noReprint"):m("archives.card.noFileForReprint")},{label:m("archives.menu.schedule"),icon:a.jsx(da,{className:"w-4 h-4"}),onClick:()=>be(!0),disabled:!t.file_path||!v("queue:create"),title:t.file_path?v("queue:create")?void 0:m("archives.permission.noAddToQueue"):m("archives.card.noFileForReprint")},{label:m("archives.menu.openInBambuStudio"),icon:a.jsx(Ar,{className:"w-4 h-4"}),onClick:()=>{const de=t.print_name||t.filename||"model";Gh(t.id,de,"file",c)},disabled:!t.file_path,title:t.file_path?void 0:m("archives.card.noFileForReprint")}]:[{label:m("archives.menu.slice"),icon:d?a.jsx(Ud,{className:"w-4 h-4"}):a.jsx(Ar,{className:"w-4 h-4"}),onClick:()=>{if(d)A(!0);else{const de=t.print_name||t.filename||"model";Gh(t.id,de,"file",c)}}}],{label:t.external_url?m("archives.menu.externalLink"):m("archives.menu.viewOnMakerWorld"),icon:a.jsx(qs,{className:"w-4 h-4"}),onClick:()=>{const de=t.external_url||t.makerworld_url;de&&window.open(de,"_blank")},disabled:!t.external_url&&!t.makerworld_url},{label:"",divider:!0,onClick:()=>{}},{label:m("archives.menu.preview3d"),icon:a.jsx(xi,{className:"w-4 h-4"}),onClick:()=>{je()}},{label:m("archives.menu.viewTimelapse"),icon:a.jsx(op,{className:"w-4 h-4"}),onClick:()=>z(!0),disabled:!t.timelapse_path},{label:m("archives.menu.scanForTimelapse"),icon:a.jsx(FN,{className:"w-4 h-4"}),onClick:()=>Me.mutate(),disabled:!t.printer_id||!!t.timelapse_path||Me.isPending||!b("archives","update",t.created_by_id),title:b("archives","update",t.created_by_id)?void 0:m("archives.permission.noUpdateArchives")},{label:m("archives.menu.uploadTimelapse"),icon:a.jsx(_a,{className:"w-4 h-4"}),onClick:()=>he.current?.click(),disabled:!!t.timelapse_path||!b("archives","update",t.created_by_id),title:b("archives","update",t.created_by_id)?void 0:m("archives.permission.noUpdateArchives")},...t.timelapse_path?[{label:m("archives.menu.removeTimelapse"),icon:a.jsx(Zt,{className:"w-4 h-4"}),onClick:()=>U(!0),danger:!0,disabled:!b("archives","update",t.created_by_id),title:b("archives","update",t.created_by_id)?void 0:m("archives.permission.noUpdateArchives")}]:[],{label:"",divider:!0,onClick:()=>{}},{label:t.source_3mf_path?m("archives.menu.downloadSource3mf"):m("archives.menu.uploadSource3mf"),icon:a.jsx(TN,{className:"w-4 h-4"}),onClick:()=>{t.source_3mf_path?oe.downloadSource3mf(t.id).catch(de=>{console.error("Source 3MF download failed:",de)}):le.current?.click()},disabled:!t.source_3mf_path&&!b("archives","update",t.created_by_id),title:!t.source_3mf_path&&!b("archives","update",t.created_by_id)?m("archives.permission.noUploadFiles"):void 0},...t.source_3mf_path?[{label:m("archives.menu.replaceSource3mf"),icon:a.jsx(_a,{className:"w-4 h-4"}),onClick:()=>le.current?.click(),disabled:!b("archives","update",t.created_by_id),title:b("archives","update",t.created_by_id)?void 0:m("archives.permission.noUpdateArchives")},{label:m("archives.menu.removeSource3mf"),icon:a.jsx(Zt,{className:"w-4 h-4"}),onClick:()=>B(!0),danger:!0,disabled:!b("archives","update",t.created_by_id),title:b("archives","update",t.created_by_id)?void 0:m("archives.permission.noUpdateArchives")}]:[],{label:t.f3d_path?m("archives.menu.replaceF3d"):m("archives.menu.uploadF3d"),icon:a.jsx(xi,{className:"w-4 h-4"}),onClick:()=>ye.current?.click(),disabled:!b("archives","update",t.created_by_id),title:b("archives","update",t.created_by_id)?void 0:m("archives.permission.noUpdateArchives")},...t.f3d_path?[{label:m("archives.menu.downloadF3d"),icon:a.jsx(aa,{className:"w-4 h-4"}),onClick:()=>{oe.downloadF3d(t.id).catch(de=>{console.error("F3D download failed:",de)})}},{label:m("archives.menu.removeF3d"),icon:a.jsx(Zt,{className:"w-4 h-4"}),onClick:()=>D(!0),danger:!0,disabled:!b("archives","update",t.created_by_id),title:b("archives","update",t.created_by_id)?void 0:m("archives.permission.noUpdateArchives")}]:[],{label:"",divider:!0,onClick:()=>{}},{label:m("archives.menu.download"),icon:a.jsx(aa,{className:"w-4 h-4"}),onClick:()=>{oe.downloadArchive(t.id,`${t.print_name||t.filename}.3mf`).catch(de=>{console.error("Archive download failed:",de)})},disabled:!v("archives:read"),title:v("archives:read")?void 0:m("archives.permission.noDownload")},{label:m("archives.menu.copyDownloadLink"),icon:a.jsx(ns,{className:"w-4 h-4"}),onClick:()=>{const de=`${window.location.origin}${oe.getArchiveDownload(t.id)}`;navigator.clipboard.writeText(de).then(()=>{x(m("archives.toast.linkCopied"))}).catch(()=>{x(m("archives.toast.failedCopyLink"),"error")})},disabled:!v("archives:read"),title:v("archives:read")?void 0:m("archives.permission.noCopyLink")},{label:m("archives.menu.qrCode"),icon:a.jsx(cJ,{className:"w-4 h-4"}),onClick:()=>ue(!0)},{label:t.photos?.length?m("archives.menu.viewPhotosCount",{count:t.photos.length}):m("archives.menu.viewPhotos"),icon:a.jsx(Qx,{className:"w-4 h-4"}),onClick:()=>se(!0),disabled:!t.photos?.length},{label:m("archives.menu.projectPage"),icon:a.jsx(Ns,{className:"w-4 h-4"}),onClick:()=>ae(!0)},{label:"",divider:!0,onClick:()=>{}},{label:t.is_favorite?m("archives.menu.removeFromFavorites"):m("archives.menu.addToFavorites"),icon:a.jsx(bg,{className:`w-4 h-4 ${t.is_favorite?"fill-yellow-400 text-yellow-400":""}`}),onClick:()=>et.mutate(),disabled:!b("archives","update",t.created_by_id),title:b("archives","update",t.created_by_id)?void 0:m("archives.permission.noUpdateArchives")},{label:m("archives.menu.edit"),icon:a.jsx(li,{className:"w-4 h-4"}),onClick:()=>E(!0),disabled:!b("archives","update",t.created_by_id),title:b("archives","update",t.created_by_id)?void 0:m("archives.permission.noUpdateArchives")},...t.project_id&&t.project_name?[{label:m("archives.menu.goToProject",{name:t.project_name}),icon:a.jsx($s,{className:"w-4 h-4 text-bambu-green"}),onClick:()=>window.location.href="/projects"}]:[],{label:m("archives.menu.addToProject"),icon:a.jsx($s,{className:"w-4 h-4"}),onClick:()=>{},disabled:!b("archives","update",t.created_by_id),title:b("archives","update",t.created_by_id)?void 0:m("archives.permission.noUpdateArchives"),submenuSearchPlaceholder:(s?.filter(de=>de.status==="active").length??0)>5?m("archives.menu.searchProjects"):void 0,submenu:(()=>{const de=[];if(t.project_id&&de.push({label:m("archives.menu.removeFromProject"),icon:a.jsx(Bt,{className:"w-4 h-4"}),onClick:()=>Xe.mutate(null),disabled:!b("archives","update",t.created_by_id)}),!s)de.push({label:m("archives.menu.loading"),icon:a.jsx(mt,{className:"w-4 h-4 animate-spin"}),onClick:()=>{},disabled:!0});else{const Se=s.filter($e=>$e.status==="active").sort(($e,st)=>$e.name.localeCompare(st.name));Se.length===0?de.push({label:m("archives.menu.noProjectsAvailable"),icon:a.jsx($s,{className:"w-4 h-4 opacity-50"}),onClick:()=>{},disabled:!0}):Se.forEach($e=>{de.push({label:$e.name,icon:a.jsx("div",{className:"w-3 h-3 rounded-full flex-shrink-0",style:{backgroundColor:$e.color||"#888"}}),onClick:()=>Xe.mutate($e.id),disabled:t.project_id===$e.id||!b("archives","update",t.created_by_id)})})}return de})()},{label:m(n?"archives.menu.deselect":"archives.menu.select"),icon:n?a.jsx(As,{className:"w-4 h-4"}):a.jsx(mo,{className:"w-4 h-4"}),onClick:()=>r(t.id)},{label:"",divider:!0,onClick:()=>{}},{label:m("archives.menu.delete"),icon:a.jsx(Zt,{className:"w-4 h-4"}),onClick:()=>F(!0),danger:!0,disabled:!b("archives","delete",t.created_by_id),title:b("archives","delete",t.created_by_id)?void 0:m("archives.permission.noDelete")}];return a.jsxs(wt,{"data-archive-id":t.id,className:`relative flex flex-col group ${n?"ring-2 ring-bambu-green":""} ${i?"cursor-pointer":""}`,style:o?{outline:"4px solid #facc15",outlineOffset:"2px"}:void 0,onContextMenu:Pe,onClick:i?()=>r(t.id):void 0,children:[i&&a.jsx("button",{className:"absolute top-2 left-2 z-10 p-1 rounded bg-black/50 hover:bg-black/70 transition-colors",onClick:de=>{de.stopPropagation(),r(t.id)},children:n?a.jsx(As,{className:"w-5 h-5 text-bambu-green"}):a.jsx(mo,{className:"w-5 h-5 text-white"})}),a.jsxs("div",{className:"aspect-video bg-bambu-dark relative flex-shrink-0 overflow-hidden rounded-t-xl",onMouseEnter:()=>q(!0),onMouseLeave:()=>q(!1),children:[t.thumbnail_path?a.jsx("img",{src:ce!==null&&ee.length>0?oe.getArchivePlateThumbnail(t.id,ee[ke]?.index??0):oe.getArchiveThumbnail(t.id),alt:t.print_name||t.filename,className:"w-full h-full object-cover"}):a.jsx("div",{className:"w-full h-full flex items-center justify-center",children:a.jsx(Bd,{className:"w-12 h-12 text-bambu-dark-tertiary"})}),ge&&ee.length>1&&a.jsxs(a.Fragment,{children:[a.jsx("button",{className:`absolute left-1 top-1/2 -translate-y-1/2 p-1 rounded-full bg-black/60 hover:bg-black/80 transition-all ${g?"opacity-100":"opacity-0 group-hover:opacity-100"}`,onClick:de=>{de.stopPropagation(),I(Se=>{const $e=Se??0;return $e>0?$e-1:ee.length-1})},title:m("archives.card.previousPlate"),children:a.jsx(Uo,{className:"w-4 h-4 text-white"})}),a.jsx("button",{className:`absolute right-1 top-1/2 -translate-y-1/2 p-1 rounded-full bg-black/60 hover:bg-black/80 transition-all ${g?"opacity-100":"opacity-0 group-hover:opacity-100"}`,onClick:de=>{de.stopPropagation(),I(Se=>{const $e=Se??0;return $ea.jsx("button",{className:`w-2 h-2 rounded-full transition-colors ${Se===ke?"bg-bambu-green":"bg-white/50 hover:bg-white/80"}`,onClick:$e=>{$e.stopPropagation(),I(Se)},title:de.name||m("archives.card.plateNumber",{index:de.index})},de.index))})]}),a.jsx("button",{className:`absolute top-2 left-2 p-1.5 rounded bg-black/50 hover:bg-black/70 transition-all ${g?"opacity-100":"opacity-0 group-hover:opacity-100"} ${i?"left-10":""}`,onClick:de=>{de.stopPropagation();const Se=de.currentTarget.getBoundingClientRect();O({x:Se.left,y:Se.bottom+4})},title:m("archives.card.moreOptions"),children:a.jsx(ip,{className:"w-5 h-5 text-white"})}),a.jsx("button",{className:`absolute top-2 right-2 p-1 rounded transition-colors ${b("archives","update",t.created_by_id)?"bg-black/50 hover:bg-black/70":"bg-black/30 cursor-not-allowed"}`,onClick:de=>{de.stopPropagation(),b("archives","update",t.created_by_id)&&et.mutate()},disabled:!b("archives","update",t.created_by_id),title:b("archives","update",t.created_by_id)?t.is_favorite?m("archives.card.removeFromFavorites"):m("archives.card.addToFavorites"):m("archives.permission.noUpdateArchives"),children:a.jsx(bg,{className:`w-5 h-5 ${t.is_favorite?"text-yellow-400 fill-yellow-400":"text-white"} ${b("archives","update",t.created_by_id)?"":"opacity-50"}`})}),(t.status==="failed"||t.status==="aborted")&&a.jsx("div",{className:"absolute top-2 left-12 px-2 py-1 rounded text-xs bg-status-error/80 text-white",children:t.status==="aborted"?m("archives.card.cancelled"):m("archives.card.failed")}),t.duplicate_count>0&&H>0&&L&&a.jsxs("button",{onClick:de=>{de.stopPropagation(),p?.(L)},className:"absolute top-2 right-12 px-2 py-1 rounded text-xs bg-purple-500/80 hover:bg-purple-600/90 text-white flex items-center gap-1 transition-colors cursor-pointer",title:m("archives.viewOriginalPrint",{id:L}),children:[a.jsx(ns,{className:"w-3 h-3"}),"#",H]}),t.duplicate_count>0&&H===0&&a.jsxs("span",{className:"absolute top-2 right-12 px-2 py-1 rounded text-xs bg-purple-500/80 text-white flex items-center gap-1",title:`${t.duplicate_count} reprint${t.duplicate_count===1?"":"s"}`,children:[a.jsx(sJ,{className:"w-3 h-3"}),"+",t.duplicate_count]}),t.source_3mf_path&&a.jsx("button",{className:"absolute bottom-2 left-2 p-1.5 rounded bg-black/60 hover:bg-black/80 transition-colors",onClick:de=>{de.stopPropagation();const Se=(t.print_name||t.filename||"source").replace(/\.gcode\.3mf$/i,"")+"_source";Gh(t.id,Se,"source",c)},title:m("archives.card.openSource3mf"),children:a.jsx(TN,{className:"w-4 h-4 text-orange-400"})}),t.f3d_path&&a.jsx("button",{className:`absolute bottom-2 ${t.source_3mf_path?"left-12":"left-2"} p-1.5 rounded bg-black/60 hover:bg-black/80 transition-colors`,onClick:de=>{de.stopPropagation(),oe.downloadF3d(t.id).catch(Se=>{console.error("F3D download failed:",Se)})},title:m("archives.card.downloadF3d"),children:a.jsx(xi,{className:"w-4 h-4 text-cyan-400"})}),a.jsx("button",{className:"absolute bottom-2 right-2 p-1.5 rounded bg-black/60 hover:bg-black/80 transition-colors",onClick:de=>{de.stopPropagation(),je()},title:m("archives.card.preview3d"),children:a.jsx(ia,{className:"w-4 h-4 text-white"})}),t.timelapse_path&&a.jsx("button",{className:"absolute bottom-2 right-12 p-1.5 rounded bg-black/60 hover:bg-black/80 transition-colors",onClick:de=>{de.stopPropagation(),z(!0)},title:m("archives.card.viewTimelapse"),children:a.jsx(op,{className:"w-4 h-4 text-bambu-green"})}),t.photos&&t.photos.length>0&&a.jsxs("button",{className:`absolute bottom-2 ${t.timelapse_path?"right-[5.5rem]":"right-12"} p-1.5 rounded bg-black/60 hover:bg-black/80 transition-colors`,onClick:de=>{de.stopPropagation(),se(!0)},title:t.photos.length===1?m("archives.card.viewPhoto"):m("archives.card.viewPhotos",{count:t.photos.length}),children:[a.jsx(Qx,{className:"w-4 h-4 text-blue-400"}),t.photos.length>1&&a.jsx("span",{className:"absolute -top-1 -right-1 w-4 h-4 bg-blue-500 rounded-full text-[10px] text-white flex items-center justify-center",children:t.photos.length})]}),ve&&ve.length>0&&a.jsx(ws,{to:`/files?folder=${ve[0].id}`,className:"absolute bottom-2 p-1.5 rounded bg-black/60 hover:bg-black/80 transition-colors",onClick:de=>de.stopPropagation(),title:m("archives.card.openFolder",{name:ve[0].name}),style:{left:t.source_3mf_path?t.f3d_path?"5.5rem":"3rem":t.f3d_path?"3rem":"0.5rem"},children:a.jsx(fl,{className:"w-4 h-4 text-yellow-400"})})]}),a.jsxs(St,{className:"p-4 flex-1 flex flex-col",children:[a.jsxs("p",{className:"text-[10px] text-bambu-gray/70 mb-1",children:["#",t.id]}),a.jsxs("div",{className:"flex items-center justify-between gap-2 mb-1",children:[a.jsx("h3",{className:"min-w-0 font-medium text-white truncate",children:t.print_name||t.filename}),a.jsx(De,{variant:"ghost",size:"sm",className:"p-1 sm:p-1.5 shrink-0",onClick:()=>E(!0),disabled:!b("archives","update",t.created_by_id),title:b("archives","update",t.created_by_id)?m("archives.card.edit"):m("archives.card.noPermissionEdit"),children:a.jsx(li,{className:"w-3 h-3 sm:w-4 sm:h-4"})})]}),a.jsxs("div",{className:"flex items-center gap-2 mb-3 flex-wrap",children:[a.jsx("p",{className:"text-xs text-bambu-gray",children:e}),a.jsx("span",{className:`text-[10px] px-1.5 py-0.5 rounded font-medium ${Vh(t)?"bg-bambu-green/20 text-bambu-green":"bg-orange-500/20 text-orange-400"}`,title:Vh(t)?m("archives.card.slicedFile"):m("archives.card.sourceFile"),children:Vh(t)?m("archives.card.gcode"):m("archives.card.source")}),t.content_hash&&a.jsx("span",{className:"text-[10px] px-1.5 py-0.5 rounded font-mono bg-bambu-dark-tertiary/50 text-bambu-gray-light opacity-0 transition-opacity duration-150 group-hover:opacity-100",title:`SHA256: ${t.content_hash}`,children:t.content_hash.slice(0,8).toUpperCase()}),t.project_name&&a.jsx("span",{className:"text-xs px-1.5 py-0.5 rounded-full truncate max-w-[120px]",style:{backgroundColor:`${s?.find(de=>de.id===t.project_id)?.color||"#6b7280"}20`,color:s?.find(de=>de.id===t.project_id)?.color||"#6b7280"},title:m("archives.card.project",{name:t.project_name}),children:t.project_name})]}),a.jsxs("div",{className:"grid grid-cols-2 gap-2 text-xs mb-4 min-h-[48px]",children:[(t.print_time_seconds||t.actual_time_seconds)&&a.jsxs("div",{className:"flex items-center gap-1.5 text-bambu-gray",title:t.time_accuracy?`Estimated: ${Qi(t.print_time_seconds||0)} Actual: ${Qi(t.actual_time_seconds||0)} Accuracy: ${t.time_accuracy.toFixed(0)}%`:t.actual_time_seconds?`Actual: ${Qi(t.actual_time_seconds)}`:`Estimated: ${Qi(t.print_time_seconds||0)}`,children:[a.jsx(Yn,{className:"w-3 h-3"}),Qi(t.actual_time_seconds||t.print_time_seconds||0),t.time_accuracy&&a.jsxs("span",{className:`text-[10px] px-1 rounded ${t.time_accuracy>=95&&t.time_accuracy<=105?"bg-bambu-green/20 text-bambu-green":t.time_accuracy>105?"bg-blue-500/20 text-blue-400":"bg-orange-500/20 text-orange-400"}`,children:[t.time_accuracy>100?"+":"",(t.time_accuracy-100).toFixed(0),"%"]})]}),t.filament_used_grams&&a.jsxs("div",{className:"flex items-center gap-1.5 text-bambu-gray",children:[a.jsx(Za,{className:"w-3 h-3"}),t.filament_used_grams.toFixed(1),"g"]}),(t.cost!=null||t.energy_cost!=null)&&a.jsxs("div",{className:"flex items-center gap-3 text-bambu-gray",children:[t.cost!=null&&a.jsxs("div",{className:"flex items-center gap-1.5",children:[a.jsx(ege,{className:"w-3 h-3"}),u,t.cost.toFixed(2)]}),t.energy_cost!=null&&a.jsxs("div",{className:"flex items-center gap-1.5",title:`${m("stats.energyUsed")}: ${t.energy_kwh?.toFixed(3)||"N/A"} kWh`,children:[a.jsx(cc,{className:"w-3 h-3"}),u,t.energy_cost.toFixed(2)]})]}),(t.layer_height||t.total_layers)&&a.jsxs("div",{className:"flex items-center gap-1.5 text-bambu-gray",children:[a.jsx(ia,{className:"w-3 h-3"}),t.total_layers&&a.jsx("span",{children:t.total_layers===1?m("archives.card.layer",{count:t.total_layers}):m("archives.card.layers",{count:t.total_layers})}),t.total_layers&&t.layer_height&&a.jsx("span",{className:"text-bambu-gray/50",children:"·"}),t.layer_height&&a.jsxs("span",{children:[t.layer_height,"mm"]})]}),t.object_count!=null&&t.object_count>0&&a.jsxs("div",{className:"flex items-center gap-1.5 text-bambu-gray",title:t.object_count===1?m("archives.card.object",{count:t.object_count}):m("archives.card.objects",{count:t.object_count}),children:[a.jsx(xi,{className:"w-3 h-3"}),t.object_count===1?m("archives.card.object",{count:t.object_count}):m("archives.card.objects",{count:t.object_count})]}),t.sliced_for_model&&a.jsxs("div",{className:"flex items-center gap-1.5 text-bambu-gray",title:m("archives.card.slicedFor",{model:t.sliced_for_model}),children:[a.jsx(Mr,{className:"w-3 h-3"}),t.sliced_for_model]}),t.filament_type&&a.jsxs("div",{className:"flex items-center gap-1.5 col-span-2",children:[a.jsx("span",{className:"text-bambu-gray text-xs",children:t.filament_type}),t.filament_color&&a.jsx("div",{className:"flex items-center gap-0.5 flex-wrap",children:t.filament_color.split(",").map((de,Se)=>a.jsx("div",{className:"w-3 h-3 rounded-full border border-black/20",style:{backgroundColor:de},title:de},Se))})]})]}),(t.tags||t.notes)&&a.jsxs("div",{className:"flex flex-wrap items-center gap-1.5 mb-3",children:[t.notes&&a.jsx("div",{className:"flex items-center gap-1 px-1.5 py-0.5 bg-blue-500/20 text-blue-400 rounded text-xs",title:t.notes,children:a.jsx(pJ,{className:"w-3 h-3"})}),t.tags?.split(",").map((de,Se)=>a.jsx("span",{className:"px-1.5 py-0.5 bg-bambu-dark-tertiary text-bambu-gray-light rounded text-xs",children:de.trim()},Se))]}),a.jsx("div",{className:"flex-1"}),a.jsxs("div",{className:"flex items-center justify-between text-xs text-bambu-gray border-t border-bambu-dark-tertiary pt-3",children:[a.jsx("span",{children:vg(t.created_at,l)}),a.jsxs("div",{className:"flex items-center gap-2",children:[t.created_by_username&&a.jsxs("span",{className:"flex items-center gap-1",title:m("archives.card.uploadedBy",{name:t.created_by_username}),children:[a.jsx(vm,{className:"w-3 h-3"}),t.created_by_username]}),a.jsx("span",{children:Yi(t.file_size)})]})]}),a.jsxs("div",{className:"flex gap-1 mt-3",children:[Vh(t)?a.jsxs(a.Fragment,{children:[a.jsxs(De,{variant:"primary",size:"sm",className:"flex-1 min-w-0 overflow-hidden",onClick:()=>P(!0),disabled:!t.file_path||!b("archives","reprint",t.created_by_id),title:t.file_path?b("archives","reprint",t.created_by_id)?void 0:m("archives.card.noPermissionReprint"):m("archives.card.noFileForReprint"),children:[a.jsx(Mr,{className:"w-3 h-3 flex-shrink-0"}),a.jsx("span",{className:"hidden sm:inline truncate",children:m("archives.card.reprint")})]}),a.jsxs(De,{variant:"secondary",size:"sm",className:"flex-1 min-w-0 overflow-hidden",onClick:()=>be(!0),disabled:!t.file_path||!v("queue:create"),title:t.file_path?v("queue:create")?m("archives.card.schedulePrint"):m("archives.permission.noAddToQueue"):m("archives.card.noFileForReprint"),children:[a.jsx(da,{className:"w-3 h-3 flex-shrink-0"}),a.jsx("span",{className:"hidden sm:inline truncate",children:m("archives.card.schedule")})]}),a.jsx(De,{variant:"secondary",size:"sm",className:"min-w-0 p-1 sm:p-1.5",onClick:()=>{const de=t.print_name||t.filename||"model";Gh(t.id,de,"file",c)},title:m("archives.card.openInBambuStudio"),children:a.jsx(Ar,{className:"w-3 h-3 sm:w-4 sm:h-4"})})]}):a.jsxs(De,{variant:"primary",size:"sm",className:"flex-1 min-w-0 overflow-hidden",onClick:()=>{if(d)A(!0);else{const de=t.print_name||t.filename||"model";Gh(t.id,de,"file",c)}},title:m(d?"slice.title":"archives.card.openInBambuStudioToSlice"),children:[d?a.jsx(Ud,{className:"w-3 h-3 flex-shrink-0"}):a.jsx(Ar,{className:"w-3 h-3 flex-shrink-0"}),a.jsx("span",{className:"hidden sm:inline truncate",children:m("archives.card.slice")})]}),a.jsx(De,{variant:"secondary",size:"sm",className:"min-w-0 p-1 sm:p-1.5",onClick:()=>{const de=t.external_url||t.makerworld_url;de&&window.open(de,"_blank")},disabled:!t.external_url&&!t.makerworld_url,title:t.external_url?m("archives.card.externalLink"):t.makerworld_url?m("archives.card.makerWorld",{designer:t.designer||m("archives.card.viewProject")}):m("archives.card.noExternalLink"),children:a.jsx(qs,{className:`w-3 h-3 sm:w-4 sm:h-4 ${!t.external_url&&!t.makerworld_url?"opacity-20":""}`})}),a.jsx(De,{variant:"secondary",size:"sm",className:"min-w-0 p-1 sm:p-1.5",onClick:()=>{oe.downloadArchive(t.id,`${t.print_name||t.filename}.3mf`).catch(de=>{console.error("Archive download failed:",de)})},title:m("archives.card.download"),children:a.jsx(aa,{className:"w-3 h-3 sm:w-4 sm:h-4"})}),a.jsx(De,{variant:"ghost",size:"sm",className:"min-w-0 p-1 sm:p-1.5",onClick:()=>F(!0),disabled:!b("archives","delete",t.created_by_id),title:b("archives","delete",t.created_by_id)?m("archives.card.delete"):m("archives.card.noPermissionDelete"),children:a.jsx(Zt,{className:"w-3 h-3 sm:w-4 sm:h-4 text-red-400"})})]})]}),k&&a.jsx(jie,{archive:t,onClose:()=>E(!1)}),W&&a.jsx(Bz,{plates:W,onSelect:de=>{te(null),S(`/gcode-viewer?archive=${t.id}&plate=${de}`)},onClose:()=>te(null)}),N&&a.jsx(ac,{mode:"reprint",archiveId:t.id,archiveName:t.print_name||t.filename,onClose:()=>P(!1)}),C&&a.jsx(dA,{source:{kind:"archive",id:t.id,filename:t.print_name||t.filename||"model"},onClose:()=>A(!1)}),T&&a.jsx(yn,{title:m("archives.modal.deleteArchive"),message:m("archives.modal.deleteConfirm",{name:t.print_name||t.filename}),confirmText:m("archives.modal.deleteButton"),variant:"danger",onConfirm:()=>{Ge.mutate(),F(!1)},onCancel:()=>F(!1)}),fe&&a.jsx(yn,{title:m("archives.modal.removeSource3mf"),message:m("archives.modal.removeSource3mfConfirm",{name:t.print_name||t.filename}),confirmText:m("archives.modal.removeButton"),variant:"danger",onConfirm:()=>{Be.mutate(),B(!1)},onCancel:()=>B(!1)}),Y&&a.jsx(yn,{title:m("archives.modal.removeF3d"),message:m("archives.modal.removeF3dConfirm",{name:t.print_name||t.filename}),confirmText:m("archives.modal.removeButton"),variant:"danger",onConfirm:()=>{ze.mutate(),D(!1)},onCancel:()=>D(!1)}),M&&a.jsx(yn,{title:m("archives.modal.removeTimelapse"),message:m("archives.modal.removeTimelapseConfirm",{name:t.print_name||t.filename}),confirmText:m("archives.modal.removeButton"),variant:"danger",onConfirm:()=>{Te.mutate(),U(!1)},onCancel:()=>U(!1)}),Q&&a.jsx(Mie,{x:Q.x,y:Q.y,items:we,onClose:()=>O(null)}),$&&t.timelapse_path&&a.jsx(Tle,{src:oe.getArchiveTimelapse(t.id),title:m("archives.modal.timelapse",{name:t.print_name||t.filename}),downloadFilename:`${t.print_name||t.filename}_timelapse.mp4`,archiveId:t.id,onClose:()=>z(!1),onEdit:()=>{f.invalidateQueries({queryKey:["archives"]}),z(!1)}}),X&&ne.length>0&&a.jsx("div",{className:"fixed inset-0 bg-black/80 flex items-center justify-center z-50 p-4",children:a.jsxs("div",{className:"bg-card-dark rounded-lg max-w-lg w-full max-h-[80vh] flex flex-col",children:[a.jsxs("div",{className:"flex items-center justify-between p-4 border-b border-gray-700",children:[a.jsxs("div",{children:[a.jsx("h3",{className:"text-lg font-semibold text-white",children:m("archives.modal.selectTimelapse")}),a.jsx("p",{className:"text-sm text-gray-400 mt-1",children:m("archives.modal.selectTimelapseDesc")})]}),a.jsx("button",{onClick:()=>{R(!1),re([])},className:"text-gray-400 hover:text-white p-1",children:a.jsx(Bt,{className:"w-5 h-5"})})]}),a.jsx("div",{className:"overflow-y-auto flex-1 p-2",children:ne.map(de=>a.jsxs("button",{onClick:()=>Oe.mutate(de.name),disabled:Oe.isPending,className:"w-full text-left p-3 rounded-lg hover:bg-gray-700 transition-colors flex items-center gap-3 disabled:opacity-50",children:[a.jsx(op,{className:"w-8 h-8 text-bambu-green flex-shrink-0"}),a.jsxs("div",{className:"flex-1 min-w-0",children:[a.jsx("p",{className:"text-white font-medium truncate",children:de.name}),a.jsxs("p",{className:"text-sm text-gray-400",children:[Yi(de.size),de.mtime&&` • ${vg(de.mtime,l)}`]})]})]},de.name))}),a.jsx("div",{className:"p-4 border-t border-gray-700",children:a.jsx(De,{variant:"secondary",onClick:()=>{R(!1),re([])},className:"w-full",children:"Cancel"})})]})}),J&&a.jsx(Eie,{archiveId:t.id,archiveName:t.print_name||t.filename,onClose:()=>ue(!1)}),ie&&t.photos&&t.photos.length>0&&a.jsx(Die,{archiveId:t.id,archiveName:t.print_name||t.filename,photos:t.photos,onClose:()=>se(!1),onDelete:async de=>{try{await oe.deleteArchivePhoto(t.id,de),f.invalidateQueries({queryKey:["archives"]}),x(m("archives.toast.photoDeleted"))}catch{x(m("archives.toast.failedDeletePhoto"),"error")}}}),K&&a.jsx(Ple,{archiveId:t.id,archiveName:t.print_name||t.filename,onClose:()=>ae(!1)}),pe&&a.jsx(ac,{mode:"add-to-queue",archiveId:t.id,archiveName:t.print_name||t.filename,onClose:()=>be(!1)}),a.jsx("input",{ref:le,type:"file",accept:".3mf",className:"hidden",onChange:de=>{const Se=de.target.files?.[0];Se&&Fe.mutate(Se),de.target.value=""}}),a.jsx("input",{ref:ye,type:"file",accept:".f3d",className:"hidden",onChange:de=>{const Se=de.target.files?.[0];Se&&Ce.mutate(Se),de.target.value=""}}),a.jsx("input",{ref:he,type:"file",accept:".mp4,.avi,.mkv",className:"hidden",onChange:de=>{const Se=de.target.files?.[0];Se&&xe.mutate(Se),de.target.value=""}})]})}function Net({archive:t,printerName:e,isSelected:n,onSelect:r,selectionMode:i,projects:s,isHighlighted:o,preferredSlicer:l="bambu_studio",useSlicerApi:c=!1,t:d,onNavigateToArchive:u}){const m=tn(),{showToast:p}=rn(),{hasPermission:f,canModify:x}=gr(),[v,b]=w.useState(!1),[g,S]=w.useState(!1),N=as(),[P,C]=w.useState(!1),[A,T]=w.useState(!1),[F,k]=w.useState(!1),[E,$]=w.useState(!1),[z,X]=w.useState(!1),[R,ne]=w.useState([]),[re,J]=w.useState(!1),[ue,ie]=w.useState(!1),[se,K]=w.useState(!1),[ae,pe]=w.useState(!1),[be,fe]=w.useState(!1),[B,Y]=w.useState(!1),[D,M]=w.useState(null),U=w.useRef(null),Q=w.useRef(null),O=w.useRef(null),[ce,I]=w.useState(null),G=t.duplicate_sequence??0,q=t.original_archive_id??null,W=async()=>{try{const Ce=await oe.getArchivePlates(t.id);if(Ce.is_multi_plate&&Ce.plates.length>1){I(Ce.plates);return}}catch{}N(`/gcode-viewer?archive=${t.id}`)},te=ot({mutationFn:()=>oe.deleteArchiveTimelapse(t.id),onSuccess:()=>{m.invalidateQueries({queryKey:["archives"]}),p(d("archives.toast.timelapseRemoved"))},onError:Ce=>{p(Ce.message||d("archives.toast.failedRemoveTimelapse"),"error")}}),le=ot({mutationFn:Ce=>oe.uploadArchiveTimelapse(t.id,Ce),onSuccess:Ce=>{m.invalidateQueries({queryKey:["archives"]}),p(d("archives.toast.timelapseUploaded",{filename:Ce.filename}))},onError:Ce=>{p(Ce.message||d("archives.toast.failedUploadTimelapse"),"error")}}),ye=ot({mutationFn:Ce=>oe.uploadSource3mf(t.id,Ce),onSuccess:Ce=>{m.invalidateQueries({queryKey:["archives"]}),p(d("archives.toast.source3mfAttached",{filename:Ce.filename}))},onError:Ce=>{p(Ce.message||d("archives.toast.failedUploadSource3mf"),"error")}}),he=ot({mutationFn:()=>oe.deleteSource3mf(t.id),onSuccess:()=>{m.invalidateQueries({queryKey:["archives"]}),p(d("archives.toast.source3mfRemoved"))},onError:Ce=>{p(Ce.message||d("archives.toast.failedRemoveSource3mf"),"error")}}),me=ot({mutationFn:Ce=>oe.uploadF3d(t.id,Ce),onSuccess:Ce=>{m.invalidateQueries({queryKey:["archives"]}),p(d("archives.toast.f3dAttached",{filename:Ce.filename}))},onError:Ce=>{p(Ce.message||d("archives.toast.failedUploadF3d"),"error")}}),H=ot({mutationFn:()=>oe.deleteF3d(t.id),onSuccess:()=>{m.invalidateQueries({queryKey:["archives"]}),p(d("archives.toast.f3dRemoved"))},onError:Ce=>{p(Ce.message||d("archives.toast.failedRemoveF3d"),"error")}}),L=ot({mutationFn:()=>oe.scanArchiveTimelapse(t.id),onSuccess:Ce=>{Ce.status==="attached"?(m.invalidateQueries({queryKey:["archives"]}),p(d("archives.toast.timelapseAttached",{filename:Ce.filename}))):Ce.status==="exists"?p(d("archives.toast.timelapseAlreadyAttached")):Ce.status==="not_found"&&Ce.available_files&&Ce.available_files.length>0?(ne(Ce.available_files),X(!0)):p(Ce.message||d("archives.toast.noMatchingTimelapse"),"warning")},onError:Ce=>{p(Ce.message||d("archives.toast.failedScanTimelapse"),"error")}}),ee=ot({mutationFn:Ce=>oe.selectArchiveTimelapse(t.id,Ce),onSuccess:Ce=>{m.invalidateQueries({queryKey:["archives"]}),p(d("archives.toast.timelapseAttached",{filename:Ce.filename})),X(!1),ne([])},onError:Ce=>{p(Ce.message||d("archives.toast.failedAttachTimelapse"),"error")}}),ge=ot({mutationFn:()=>oe.deleteArchive(t.id),onSuccess:()=>{m.invalidateQueries({queryKey:["archives"]}),p(d("archives.toast.archiveDeleted"))},onError:()=>{p(d("archives.toast.failedDeleteArchive"),"error")}}),ke=ot({mutationFn:()=>oe.toggleFavorite(t.id),onSuccess:Ce=>{m.invalidateQueries({queryKey:["archives"]}),p(Ce.is_favorite?d("archives.toast.addedToFavorites"):d("archives.toast.removedFromFavorites"))}}),{data:je}=Qe({queryKey:["archive-folders",t.id],queryFn:()=>oe.getLibraryFoldersByArchive(t.id)}),Te=ot({mutationFn:Ce=>oe.updateArchive(t.id,{project_id:Ce}),onSuccess:()=>{m.invalidateQueries({queryKey:["archives"]}),m.invalidateQueries({queryKey:["projects"]}),p(d("archives.toast.projectUpdated"))},onError:()=>{p(d("archives.toast.failedUpdateProject"),"error")}}),xe=Ce=>{Ce.preventDefault(),M({x:Ce.clientX,y:Ce.clientY})},Be=[...Vh(t)?[{label:d("archives.menu.print"),icon:a.jsx(Mr,{className:"w-4 h-4"}),onClick:()=>C(!0),disabled:!t.file_path||!x("archives","reprint",t.created_by_id),title:t.file_path?x("archives","reprint",t.created_by_id)?void 0:d("archives.permission.noReprint"):d("archives.card.noFileForReprint")},{label:d("archives.menu.schedule"),icon:a.jsx(da,{className:"w-4 h-4"}),onClick:()=>k(!0),disabled:!t.file_path||!f("queue:create"),title:t.file_path?f("queue:create")?void 0:d("archives.permission.noAddToQueue"):d("archives.card.noFileForReprint")},{label:d("archives.menu.openInBambuStudio"),icon:a.jsx(Ar,{className:"w-4 h-4"}),onClick:()=>{const Ce=t.print_name||t.filename||"model";Gh(t.id,Ce,"file",l)},disabled:!t.file_path,title:t.file_path?void 0:d("archives.card.noFileForReprint")}]:[{label:d("archives.menu.slice"),icon:c?a.jsx(Ud,{className:"w-4 h-4"}):a.jsx(Ar,{className:"w-4 h-4"}),onClick:()=>{if(c)T(!0);else{const Ce=t.print_name||t.filename||"model";Gh(t.id,Ce,"file",l)}}}],{label:t.external_url?d("archives.menu.externalLink"):d("archives.menu.viewOnMakerWorld"),icon:a.jsx(qs,{className:"w-4 h-4"}),onClick:()=>{const Ce=t.external_url||t.makerworld_url;Ce&&window.open(Ce,"_blank")},disabled:!t.external_url&&!t.makerworld_url},{label:"",divider:!0,onClick:()=>{}},{label:d("archives.menu.preview3d"),icon:a.jsx(xi,{className:"w-4 h-4"}),onClick:()=>{W()}},{label:d("archives.menu.viewTimelapse"),icon:a.jsx(op,{className:"w-4 h-4"}),onClick:()=>$(!0),disabled:!t.timelapse_path},{label:d("archives.menu.scanForTimelapse"),icon:a.jsx(FN,{className:"w-4 h-4"}),onClick:()=>L.mutate(),disabled:!t.printer_id||!!t.timelapse_path||L.isPending||!x("archives","update",t.created_by_id),title:x("archives","update",t.created_by_id)?void 0:d("archives.permission.noUpdateArchives")},{label:d("archives.menu.uploadTimelapse"),icon:a.jsx(_a,{className:"w-4 h-4"}),onClick:()=>O.current?.click(),disabled:!!t.timelapse_path||!x("archives","update",t.created_by_id),title:x("archives","update",t.created_by_id)?void 0:d("archives.permission.noUpdateArchives")},...t.timelapse_path?[{label:d("archives.menu.removeTimelapse"),icon:a.jsx(Zt,{className:"w-4 h-4"}),onClick:()=>Y(!0),danger:!0,disabled:!x("archives","update",t.created_by_id),title:x("archives","update",t.created_by_id)?void 0:d("archives.permission.noUpdateArchives")}]:[],{label:"",divider:!0,onClick:()=>{}},{label:t.source_3mf_path?d("archives.menu.downloadSource3mf"):d("archives.menu.uploadSource3mf"),icon:a.jsx(TN,{className:"w-4 h-4"}),onClick:()=>{t.source_3mf_path?oe.downloadSource3mf(t.id).catch(Ce=>{console.error("Source 3MF download failed:",Ce)}):U.current?.click()},disabled:!t.source_3mf_path&&!x("archives","update",t.created_by_id),title:!t.source_3mf_path&&!x("archives","update",t.created_by_id)?d("archives.permission.noUploadFiles"):void 0},...t.source_3mf_path?[{label:d("archives.menu.replaceSource3mf"),icon:a.jsx(_a,{className:"w-4 h-4"}),onClick:()=>U.current?.click(),disabled:!x("archives","update",t.created_by_id),title:x("archives","update",t.created_by_id)?void 0:d("archives.permission.noUpdateArchives")},{label:d("archives.menu.removeSource3mf"),icon:a.jsx(Zt,{className:"w-4 h-4"}),onClick:()=>pe(!0),danger:!0,disabled:!x("archives","update",t.created_by_id),title:x("archives","update",t.created_by_id)?void 0:d("archives.permission.noUpdateArchives")}]:[],{label:t.f3d_path?d("archives.menu.replaceF3d"):d("archives.menu.uploadF3d"),icon:a.jsx(xi,{className:"w-4 h-4"}),onClick:()=>Q.current?.click(),disabled:!x("archives","update",t.created_by_id),title:x("archives","update",t.created_by_id)?void 0:d("archives.permission.noUpdateArchives")},...t.f3d_path?[{label:d("archives.menu.downloadF3d"),icon:a.jsx(aa,{className:"w-4 h-4"}),onClick:()=>{oe.downloadF3d(t.id).catch(Ce=>{console.error("F3D download failed:",Ce)})}},{label:d("archives.menu.removeF3d"),icon:a.jsx(Zt,{className:"w-4 h-4"}),onClick:()=>fe(!0),danger:!0,disabled:!x("archives","update",t.created_by_id),title:x("archives","update",t.created_by_id)?void 0:d("archives.permission.noUpdateArchives")}]:[],{label:"",divider:!0,onClick:()=>{}},{label:d("archives.menu.download"),icon:a.jsx(aa,{className:"w-4 h-4"}),onClick:()=>{oe.downloadArchive(t.id,`${t.print_name||t.filename}.3mf`).catch(Ce=>{console.error("Archive download failed:",Ce)})},disabled:!f("archives:read"),title:f("archives:read")?void 0:d("archives.permission.noDownload")},{label:d("archives.menu.copyDownloadLink"),icon:a.jsx(ns,{className:"w-4 h-4"}),onClick:()=>{const Ce=`${window.location.origin}${oe.getArchiveDownload(t.id)}`;navigator.clipboard.writeText(Ce).then(()=>{p(d("archives.toast.linkCopied"))}).catch(()=>{p(d("archives.toast.failedCopyLink"),"error")})},disabled:!f("archives:read"),title:f("archives:read")?void 0:d("archives.permission.noCopyLink")},{label:d("archives.menu.qrCode"),icon:a.jsx(cJ,{className:"w-4 h-4"}),onClick:()=>J(!0)},{label:t.photos?.length?d("archives.menu.viewPhotosCount",{count:t.photos.length}):d("archives.menu.viewPhotos"),icon:a.jsx(Qx,{className:"w-4 h-4"}),onClick:()=>ie(!0),disabled:!t.photos?.length},{label:d("archives.menu.projectPage"),icon:a.jsx(Ns,{className:"w-4 h-4"}),onClick:()=>K(!0)},{label:"",divider:!0,onClick:()=>{}},{label:t.is_favorite?d("archives.menu.removeFromFavorites"):d("archives.menu.addToFavorites"),icon:a.jsx(bg,{className:`w-4 h-4 ${t.is_favorite?"fill-yellow-400 text-yellow-400":""}`}),onClick:()=>ke.mutate(),disabled:!x("archives","update",t.created_by_id),title:x("archives","update",t.created_by_id)?void 0:d("archives.permission.noUpdateArchives")},{label:d("archives.menu.edit"),icon:a.jsx(li,{className:"w-4 h-4"}),onClick:()=>b(!0),disabled:!x("archives","update",t.created_by_id),title:x("archives","update",t.created_by_id)?void 0:d("archives.permission.noUpdateArchives")},...t.project_id&&t.project_name?[{label:d("archives.menu.goToProject",{name:t.project_name}),icon:a.jsx($s,{className:"w-4 h-4 text-bambu-green"}),onClick:()=>window.location.href="/projects"}]:[],{label:d("archives.menu.addToProject"),icon:a.jsx($s,{className:"w-4 h-4"}),onClick:()=>{},submenuSearchPlaceholder:(s?.filter(Ce=>Ce.status==="active").length??0)>5?d("archives.menu.searchProjects"):void 0,submenu:(()=>{const Ce=[];if(t.project_id&&Ce.push({label:d("archives.menu.removeFromProject"),icon:a.jsx(Bt,{className:"w-4 h-4"}),onClick:()=>Te.mutate(null)}),!s)Ce.push({label:d("archives.menu.loading"),icon:a.jsx(mt,{className:"w-4 h-4 animate-spin"}),onClick:()=>{},disabled:!0});else{const ze=s.filter(Me=>Me.status==="active").sort((Me,Oe)=>Me.name.localeCompare(Oe.name));ze.length===0?Ce.push({label:d("archives.menu.noProjectsAvailable"),icon:a.jsx($s,{className:"w-4 h-4 opacity-50"}),onClick:()=>{},disabled:!0}):ze.forEach(Me=>{Ce.push({label:Me.name,icon:a.jsx("div",{className:"w-3 h-3 rounded-full flex-shrink-0",style:{backgroundColor:Me.color||"#888"}}),onClick:()=>Te.mutate(Me.id),disabled:t.project_id===Me.id})})}return Ce})()},{label:d(n?"archives.menu.deselect":"archives.menu.select"),icon:n?a.jsx(As,{className:"w-4 h-4"}):a.jsx(mo,{className:"w-4 h-4"}),onClick:()=>r(t.id)},{label:"",divider:!0,onClick:()=>{}},{label:d("archives.menu.delete"),icon:a.jsx(Zt,{className:"w-4 h-4"}),onClick:()=>S(!0),danger:!0,disabled:!x("archives","delete",t.created_by_id),title:x("archives","delete",t.created_by_id)?void 0:d("archives.permission.noDelete")}];return a.jsxs(a.Fragment,{children:[a.jsxs("div",{"data-archive-id":t.id,className:`grid grid-cols-12 gap-4 px-4 py-3 items-center hover:bg-bambu-dark-tertiary/30 ${n?"bg-bambu-green/10":""}`,style:o?{outline:"4px solid #facc15",outlineOffset:"-4px"}:void 0,onContextMenu:xe,children:[a.jsxs("div",{className:"col-span-1 flex items-center gap-2",children:[i&&a.jsx("button",{onClick:()=>r(t.id),children:n?a.jsx(As,{className:"w-4 h-4 text-bambu-green"}):a.jsx(mo,{className:"w-4 h-4 text-bambu-gray"})}),t.thumbnail_path?a.jsx("img",{src:oe.getArchiveThumbnail(t.id),alt:"",className:"w-10 h-10 object-cover rounded"}):a.jsx("div",{className:"w-10 h-10 bg-bambu-dark rounded flex items-center justify-center",children:a.jsx(Bd,{className:"w-5 h-5 text-bambu-dark-tertiary"})})]}),a.jsxs("div",{className:"col-span-4",children:[a.jsxs("div",{className:"flex items-center gap-2",children:[a.jsx("p",{className:"text-white text-sm truncate",children:t.print_name||t.filename}),(t.status==="failed"||t.status==="aborted")&&a.jsx("span",{className:"px-1.5 py-0.5 rounded text-[10px] leading-tight bg-status-error/80 text-white flex-shrink-0",children:t.status==="aborted"?d("archives.card.cancelled"):d("archives.card.failed")}),t.duplicate_count>0&&G>0&&q&&a.jsxs("button",{onClick:Ce=>{Ce.stopPropagation(),u?.(q)},className:"px-1.5 py-0.5 rounded text-[10px] leading-tight bg-purple-500/80 hover:bg-purple-600/90 text-white flex-shrink-0 transition-colors flex items-center gap-1",title:d("archives.viewOriginalPrint",{id:q}),children:[a.jsx(ns,{className:"w-3 h-3"}),"#",G]}),t.duplicate_count>0&&G===0&&a.jsxs("span",{className:"px-1.5 py-0.5 rounded text-[10px] leading-tight bg-purple-500/80 text-white flex-shrink-0 flex items-center gap-1",title:`${t.duplicate_count} reprint${t.duplicate_count===1?"":"s"}`,children:[a.jsx(sJ,{className:"w-3 h-3"}),"+",t.duplicate_count]}),t.timelapse_path&&a.jsx("span",{title:d("archives.list.hasTimelapse"),children:a.jsx(op,{className:"w-3.5 h-3.5 text-bambu-green flex-shrink-0"})}),je&&je.length>0&&a.jsx(ws,{to:`/files?folder=${je[0].id}`,className:"flex-shrink-0",title:d("archives.card.openFolder",{name:je[0].name}),onClick:Ce=>Ce.stopPropagation(),children:a.jsx(fl,{className:"w-3.5 h-3.5 text-yellow-400"})})]}),(t.filament_type||t.sliced_for_model)&&a.jsxs("div",{className:"flex items-center gap-1.5 mt-0.5",children:[t.sliced_for_model&&a.jsxs("span",{className:"text-xs text-bambu-gray flex items-center gap-1",title:d("archives.card.slicedFor",{model:t.sliced_for_model}),children:[a.jsx(Mr,{className:"w-2.5 h-2.5"}),t.sliced_for_model]}),t.sliced_for_model&&t.filament_type&&a.jsx("span",{className:"text-bambu-gray/50",children:"·"}),t.filament_type&&a.jsx("span",{className:"text-xs text-bambu-gray",children:t.filament_type}),t.filament_color&&a.jsx("div",{className:"flex items-center gap-0.5 flex-wrap",children:t.filament_color.split(",").map((Ce,ze)=>a.jsx("div",{className:"w-2.5 h-2.5 rounded-full border border-black/20",style:{backgroundColor:Ce},title:Ce},ze))})]})]}),a.jsx("div",{className:"col-span-2 text-sm text-bambu-gray truncate",children:e}),a.jsxs("div",{className:"col-span-2 text-sm text-bambu-gray",children:[a.jsx("div",{children:yg(t.created_at)}),t.created_by_username&&a.jsxs("div",{className:"flex items-center gap-1 text-xs opacity-75",title:d("archives.card.uploadedBy",{name:t.created_by_username}),children:[a.jsx(vm,{className:"w-3 h-3"}),t.created_by_username]})]}),a.jsx("div",{className:"col-span-1 text-sm text-bambu-gray",children:Yi(t.file_size)}),a.jsxs("div",{className:"col-span-2 flex justify-end gap-1",children:[Vh(t)&&a.jsx(De,{variant:"ghost",size:"sm",onClick:()=>C(!0),disabled:!x("archives","reprint",t.created_by_id),title:x("archives","reprint",t.created_by_id)?d("archives.card.reprint"):d("archives.card.noPermissionReprint"),className:"text-bambu-green hover:text-bambu-green-light hover:bg-bambu-green/10",children:a.jsx(rs,{className:"w-4 h-4"})}),a.jsx(De,{variant:"ghost",size:"sm",onClick:()=>{const Ce=t.print_name||t.filename||"model";Gh(t.id,Ce,"file",l)},title:d("archives.card.openInBambuStudio"),children:a.jsx(Ar,{className:"w-4 h-4"})}),(t.external_url||t.makerworld_url)&&a.jsx(De,{variant:"ghost",size:"sm",onClick:()=>window.open(t.external_url||t.makerworld_url,"_blank"),title:t.external_url?d("archives.card.externalLink"):"MakerWorld",children:a.jsx(qs,{className:"w-4 h-4"})}),a.jsx(De,{variant:"ghost",size:"sm",onClick:()=>{oe.downloadArchive(t.id,`${t.print_name||t.filename}.3mf`).catch(Ce=>{console.error("Archive download failed:",Ce)})},title:d("archives.card.download"),children:a.jsx(aa,{className:"w-4 h-4"})}),a.jsx(De,{variant:"ghost",size:"sm",onClick:()=>b(!0),disabled:!x("archives","update",t.created_by_id),title:x("archives","update",t.created_by_id)?d("archives.card.edit"):d("archives.card.noPermissionEdit"),children:a.jsx(li,{className:"w-4 h-4"})}),a.jsx(De,{variant:"ghost",size:"sm",onClick:()=>S(!0),disabled:!x("archives","delete",t.created_by_id),title:x("archives","delete",t.created_by_id)?d("archives.card.delete"):d("archives.card.noPermissionDelete"),children:a.jsx(Zt,{className:"w-4 h-4 text-red-400"})}),a.jsx(De,{variant:"ghost",size:"sm",onClick:Ce=>{const ze=Ce.currentTarget.getBoundingClientRect();M({x:ze.left,y:ze.bottom+4})},title:d("archives.card.moreOptions"),children:a.jsx(ip,{className:"w-4 h-4"})})]})]}),v&&a.jsx(jie,{archive:t,onClose:()=>b(!1)}),ce&&a.jsx(Bz,{plates:ce,onSelect:Ce=>{I(null),N(`/gcode-viewer?archive=${t.id}&plate=${Ce}`)},onClose:()=>I(null)}),P&&a.jsx(ac,{mode:"reprint",archiveId:t.id,archiveName:t.print_name||t.filename,onClose:()=>C(!1)}),A&&a.jsx(dA,{source:{kind:"archive",id:t.id,filename:t.print_name||t.filename||"model"},onClose:()=>T(!1)}),g&&a.jsx(yn,{title:d("archives.modal.deleteArchive"),message:d("archives.modal.deleteConfirm",{name:t.print_name||t.filename}),confirmText:d("archives.modal.deleteButton"),variant:"danger",onConfirm:()=>{ge.mutate(),S(!1)},onCancel:()=>S(!1)}),ae&&a.jsx(yn,{title:d("archives.modal.removeSource3mf"),message:d("archives.modal.removeSource3mfConfirm",{name:t.print_name||t.filename}),confirmText:d("archives.modal.removeButton"),variant:"danger",onConfirm:()=>{he.mutate(),pe(!1)},onCancel:()=>pe(!1)}),be&&a.jsx(yn,{title:d("archives.modal.removeF3d"),message:d("archives.modal.removeF3dConfirm",{name:t.print_name||t.filename}),confirmText:d("archives.modal.removeButton"),variant:"danger",onConfirm:()=>{H.mutate(),fe(!1)},onCancel:()=>fe(!1)}),B&&a.jsx(yn,{title:d("archives.modal.removeTimelapse"),message:d("archives.modal.removeTimelapseConfirm",{name:t.print_name||t.filename}),confirmText:d("archives.modal.removeButton"),variant:"danger",onConfirm:()=>{te.mutate(),Y(!1)},onCancel:()=>Y(!1)}),D&&a.jsx(Mie,{x:D.x,y:D.y,items:Be,onClose:()=>M(null)}),E&&t.timelapse_path&&a.jsx(Tle,{src:oe.getArchiveTimelapse(t.id),title:d("archives.modal.timelapse",{name:t.print_name||t.filename}),downloadFilename:`${t.print_name||t.filename}_timelapse.mp4`,archiveId:t.id,onClose:()=>$(!1),onEdit:()=>{m.invalidateQueries({queryKey:["archives"]}),$(!1)}}),z&&R.length>0&&a.jsx("div",{className:"fixed inset-0 bg-black/80 flex items-center justify-center z-50 p-4",children:a.jsxs("div",{className:"bg-card-dark rounded-lg max-w-lg w-full max-h-[80vh] flex flex-col",children:[a.jsxs("div",{className:"flex items-center justify-between p-4 border-b border-gray-700",children:[a.jsxs("div",{children:[a.jsx("h3",{className:"text-lg font-semibold text-white",children:d("archives.modal.selectTimelapse")}),a.jsx("p",{className:"text-sm text-gray-400 mt-1",children:d("archives.modal.selectTimelapseDesc")})]}),a.jsx("button",{onClick:()=>{X(!1),ne([])},className:"text-gray-400 hover:text-white p-1",children:a.jsx(Bt,{className:"w-5 h-5"})})]}),a.jsx("div",{className:"overflow-y-auto flex-1 p-2",children:R.map(Ce=>a.jsxs("button",{onClick:()=>ee.mutate(Ce.name),disabled:ee.isPending,className:"w-full text-left p-3 rounded-lg hover:bg-gray-700 transition-colors mb-1",children:[a.jsx("div",{className:"font-medium text-white",children:Ce.name}),a.jsxs("div",{className:"text-sm text-gray-400 flex gap-3",children:[a.jsx("span",{children:Yi(Ce.size)}),Ce.mtime&&a.jsx("span",{children:yg(Ce.mtime)})]})]},Ce.name))})]})}),re&&a.jsx(Eie,{archiveId:t.id,archiveName:t.print_name||t.filename,onClose:()=>J(!1)}),ue&&t.photos&&a.jsx(Die,{archiveId:t.id,archiveName:t.print_name||t.filename,photos:t.photos,onClose:()=>ie(!1),onDelete:async Ce=>{try{await oe.deleteArchivePhoto(t.id,Ce),m.invalidateQueries({queryKey:["archives"]}),p(d("archives.toast.photoDeleted"))}catch{p(d("archives.toast.failedDeletePhoto"),"error")}}}),se&&a.jsx(Ple,{archiveId:t.id,archiveName:t.print_name||t.filename,onClose:()=>K(!1)}),F&&a.jsx(ac,{mode:"add-to-queue",archiveId:t.id,archiveName:t.print_name||t.filename,onClose:()=>k(!1)}),a.jsx("input",{ref:U,type:"file",accept:".3mf",className:"hidden",onChange:Ce=>{const ze=Ce.target.files?.[0];ze&&ye.mutate(ze),Ce.target.value=""}}),a.jsx("input",{ref:Q,type:"file",accept:".f3d",className:"hidden",onChange:Ce=>{const ze=Ce.target.files?.[0];ze&&me.mutate(ze),Ce.target.value=""}}),a.jsx("input",{ref:O,type:"file",accept:".mp4,.avi,.mkv",className:"hidden",onChange:Ce=>{const ze=Ce.target.files?.[0];ze&&le.mutate(ze),Ce.target.value=""}})]})}const Cet=["completed","failed","aborted","cancelled","stopped"],Pet=[{id:"all",label:"All Archives",icon:a.jsx(fl,{className:"w-4 h-4"})},{id:"recent",label:"Last 24 Hours",icon:a.jsx(Yn,{className:"w-4 h-4"})},{id:"this-week",label:"This Week",icon:a.jsx(da,{className:"w-4 h-4"})},{id:"this-month",label:"This Month",icon:a.jsx(da,{className:"w-4 h-4"})},{id:"favorites",label:"Favorites",icon:a.jsx(bg,{className:"w-4 h-4"})},{id:"not-printed",label:"Not Printed",icon:a.jsx(_a,{className:"w-4 h-4"})},{id:"printed",label:"Printed",icon:a.jsx(Mr,{className:"w-4 h-4"})},{id:"failed",label:"Failed Prints",icon:a.jsx(Oa,{className:"w-4 h-4"})},{id:"duplicates",label:"Duplicates",icon:a.jsx(ns,{className:"w-4 h-4"})}];function Tet(){const{t}=_t(),e=tn(),{showToast:n}=rn(),{hasPermission:r,hasAnyPermission:i}=gr(),s=w.useRef(null),[o,l]=w.useState(""),[c,d]=w.useState(()=>{const Ke=localStorage.getItem("archiveFilterPrinter");return Ke?Number(Ke):null}),[u,m]=w.useState(()=>localStorage.getItem("archiveFilterMaterial")),[p,f]=w.useState(()=>{const Ke=localStorage.getItem("archiveFilterColors");return Ke?new Set(JSON.parse(Ke)):new Set}),[x,v]=w.useState(()=>localStorage.getItem("archiveColorFilterMode")||"or"),[b,g]=w.useState(()=>localStorage.getItem("archiveFilterFavorites")==="true"),[S,N]=w.useState(()=>localStorage.getItem("archiveHideFailed")==="true"),[P,C]=w.useState(()=>localStorage.getItem("archiveHideDuplicates")==="true"),[A,T]=w.useState(()=>localStorage.getItem("archiveFilterTag")),[F,k]=w.useState(()=>localStorage.getItem("archiveFilterFileType")||"all"),[E,$]=w.useState(!1),[z,X]=w.useState([]),[R,ne]=w.useState(!1),[re,J]=w.useState(new Set),[ue,ie]=w.useState(!1),[se,K]=w.useState(!1),[ae,pe]=w.useState(!1),[be,fe]=w.useState(!1),[B,Y]=w.useState(()=>localStorage.getItem("archiveViewMode")||"grid"),[D,M]=w.useState(()=>localStorage.getItem("archiveSortBy")||"date-desc"),[U,Q]=w.useState(()=>localStorage.getItem("archiveCollection")||"all"),[O,ce]=w.useState(0),[I,G]=w.useState(()=>{try{const Ke=localStorage.getItem("archivePageSize");return Ke?Number(Ke):50}catch{return 50}}),[q,W]=w.useState(!1),[te,le]=w.useState(!1),[ye,he]=w.useState(!1),[me,H]=w.useState(!1),[L,ee]=w.useState(!1),[ge,ke]=w.useState(null),[je,Te]=w.useState(null),[xe,Fe]=w.useState(()=>localStorage.getItem("logFilterUser")||null),[Be,Ce]=w.useState(()=>localStorage.getItem("logFilterStatus")),[ze,Me]=w.useState(()=>localStorage.getItem("logFilterDateFrom")||""),[Oe,Ge]=w.useState(()=>localStorage.getItem("logFilterDateTo")||""),[et,ve]=w.useState(()=>{const Ke=localStorage.getItem("logOffset");return Ke?Number(Ke):0}),[Xe,Pe]=w.useState(!1),[Ve,we]=w.useState(()=>{const Ke=localStorage.getItem("logPageSize");return Ke?Number(Ke):25}),de=w.useCallback(Ke=>{Te(Ke),ke(Ke)},[]);w.useEffect(()=>{if(ge){const Ke=setTimeout(()=>{const Yt=document.querySelector(`[data-archive-id="${ge}"]`);Yt?Yt.scrollIntoView({behavior:"smooth",block:"center"}):je===ge&&n(t("archives.originalPrintNotVisible"),"warning"),je===ge&&Te(null)},100),Ht=setTimeout(()=>ke(null),5e3);return()=>{clearTimeout(Ke),clearTimeout(Ht)}}},[ge,je,n,t]);const{data:Se,isLoading:$e}=Qe({queryKey:["archives",c],queryFn:()=>oe.getArchives(c||void 0)}),{data:st}=Qe({queryKey:["printers"],queryFn:oe.getPrinters}),{data:He}=Qe({queryKey:["projects"],queryFn:()=>oe.getProjects()}),{data:at}=Qe({queryKey:["settings"],queryFn:oe.getSettings}),{data:ht}=Qe({queryKey:["users"],queryFn:oe.getUsers,enabled:B==="log"}),{data:Mt,isLoading:qt}=Qe({queryKey:["print-log",c,xe,Be,ze,Oe,o,et,Ve],queryFn:()=>oe.getPrintLog({search:o||void 0,printerId:c||void 0,username:xe||void 0,status:Be||void 0,dateFrom:ze||void 0,dateTo:Oe||void 0,limit:Ve,offset:et}),enabled:B==="log"}),Vt=at?.time_format||"system",Gt=at?.preferred_slicer||"bambu_studio",Qt=at?.use_slicer_api??!1,an=Mo(at?.currency||"USD"),mn=ot({mutationFn:async Ke=>(await Promise.all(Ke.map(Ht=>oe.deleteArchive(Ht))),Ke.length),onSuccess:Ke=>{e.invalidateQueries({queryKey:["archives"]}),J(new Set),n(`${Ke} archive${Ke!==1?"s":""} deleted`)},onError:()=>{n(t("archives.toast.failedDeleteArchives"),"error")}}),dn=ot({mutationFn:()=>oe.clearPrintLog(),onSuccess:Ke=>{e.invalidateQueries({queryKey:["print-log"]}),ve(0),n(t("archives.log.cleared",{count:Ke.deleted}))},onError:()=>{n(t("archives.log.clearFailed"),"error")}});w.useEffect(()=>{c!==null?localStorage.setItem("archiveFilterPrinter",c.toString()):localStorage.removeItem("archiveFilterPrinter")},[c]),w.useEffect(()=>{u?localStorage.setItem("archiveFilterMaterial",u):localStorage.removeItem("archiveFilterMaterial")},[u]),w.useEffect(()=>{localStorage.setItem("archiveFilterColors",JSON.stringify([...p]))},[p]),w.useEffect(()=>{localStorage.setItem("archiveColorFilterMode",x)},[x]),w.useEffect(()=>{localStorage.setItem("archiveFilterFavorites",b.toString())},[b]),w.useEffect(()=>{localStorage.setItem("archiveHideFailed",S.toString())},[S]),w.useEffect(()=>{localStorage.setItem("archiveHideDuplicates",P.toString())},[P]),w.useEffect(()=>{A?localStorage.setItem("archiveFilterTag",A):localStorage.removeItem("archiveFilterTag")},[A]),w.useEffect(()=>{localStorage.setItem("archiveFilterFileType",F)},[F]),w.useEffect(()=>{ce(0)},[o,c,u,p,x,b,S,P,A,F,D,U]),w.useEffect(()=>{try{localStorage.setItem("archivePageSize",String(I))}catch{}},[I]),w.useEffect(()=>{localStorage.setItem("archiveViewMode",B)},[B]),w.useEffect(()=>{localStorage.setItem("archiveSortBy",D)},[D]),w.useEffect(()=>{localStorage.setItem("archiveCollection",U)},[U]),w.useEffect(()=>{xe?localStorage.setItem("logFilterUser",xe):localStorage.removeItem("logFilterUser")},[xe]),w.useEffect(()=>{Be?localStorage.setItem("logFilterStatus",Be):localStorage.removeItem("logFilterStatus")},[Be]),w.useEffect(()=>{ze?localStorage.setItem("logFilterDateFrom",ze):localStorage.removeItem("logFilterDateFrom")},[ze]),w.useEffect(()=>{Oe?localStorage.setItem("logFilterDateTo",Oe):localStorage.removeItem("logFilterDateTo")},[Oe]),w.useEffect(()=>{localStorage.setItem("logOffset",et.toString())},[et]),w.useEffect(()=>{localStorage.setItem("logPageSize",Ve.toString())},[Ve]);const jt=new Map(st?.map(Ke=>[Ke.id,Ke.name])||[]),Jt=[...new Set(Se?.flatMap(Ke=>Ke.filament_type?.split(", ")||[]).filter(Boolean)||[])].sort(),zt=[...new Set(Se?.flatMap(Ke=>Ke.filament_color?.split(",")||[]).filter(Boolean)||[])],Tt=[...new Set(Se?.flatMap(Ke=>Ke.tags?.split(",").map(Ht=>Ht.trim())||[]).filter(Boolean)||[])].sort(),Ie=Se?.filter(Ke=>{const Ht=new Date,Yt=Kn(Ke.created_at)||new Date(0);let on=!0;switch(U){case"recent":on=Ht.getTime()-Yt.getTime()<1440*60*1e3;break;case"this-week":on=Ht.getTime()-Yt.getTime()<10080*60*1e3;break;case"this-month":on=Yt.getMonth()===Ht.getMonth()&&Yt.getFullYear()===Ht.getFullYear();break;case"favorites":on=Ke.is_favorite===!0;break;case"not-printed":on=Ke.status==="archived";break;case"printed":on=Cet.includes(Ke.status);break;case"failed":on=Ke.status==="failed"||Ke.status==="aborted";break;case"duplicates":on=Ke.duplicate_count>0;break}const wn=(Ke.print_name||Ke.filename).toLowerCase().includes(o.toLowerCase()),gn=!u||Ke.filament_type?.split(", ").includes(u),_n=Ke.filament_color?.split(",")||[],xn=p.size===0||(x==="or"?_n.some(ur=>p.has(ur)):[...p].every(ur=>_n.includes(ur))),br=U==="favorites"||!b||Ke.is_favorite,Tr=U==="failed"||!S||Ke.status!=="failed"&&Ke.status!=="aborted",On=U==="duplicates"||!P||Ke.duplicate_count===0||Ke.duplicate_sequence===0,Ra=Ke.tags?.split(",").map(ur=>ur.trim())||[],$n=!A||Ra.includes(A),Br=Vh(Ke);return on&&wn&&gn&&xn&&br&&Tr&&On&&$n&&(F==="all"||F==="gcode"&&Br||F==="source"&&!Br)}).sort((Ke,Ht)=>{switch(D){case"date-desc":return(Kn(Ht.created_at)?.getTime()||0)-(Kn(Ke.created_at)?.getTime()||0);case"date-asc":return(Kn(Ke.created_at)?.getTime()||0)-(Kn(Ht.created_at)?.getTime()||0);case"name-asc":return(Ke.print_name||Ke.filename).localeCompare(Ht.print_name||Ht.filename);case"name-desc":return(Ht.print_name||Ht.filename).localeCompare(Ke.print_name||Ke.filename);case"size-desc":return Ht.file_size-Ke.file_size;case"size-asc":return Ke.file_size-Ht.file_size;default:return 0}}),Re=Ie?.length||0,Ye=I===-1,Ze=Ye?Re||1:I,Le=Math.max(1,Math.ceil(Re/Ze)),dt=Ye?Ie:Ie?.slice(O*Ze,(O+1)*Ze);w.useEffect(()=>{if(ge&&Ie&&!Ye){const Ke=Ie.findIndex(Ht=>Ht.id===ge);if(Ke>=0){const Ht=Math.floor(Ke/Ze);Ht!==O&&ce(Ht)}}},[ge]);const Ae=ue||re.size>0,We=Ke=>{J(Ht=>{const Yt=new Set(Ht);return Yt.has(Ke)?Yt.delete(Ke):Yt.add(Ke),Yt})},tt=()=>{Ie&&J(new Set(Ie.map(Ke=>Ke.id)))},nt=()=>{J(new Set),ie(!1)},Je=Ke=>{f(Ht=>{const Yt=new Set(Ht);return Yt.has(Ke)?Yt.delete(Ke):Yt.add(Ke),Yt})},Ue=()=>{f(new Set)},rt=()=>{l(""),d(null),m(null),g(!1),N(!1),C(!1),T(null),k("all")},Ct=o||c||u||b||S||P||A||F!=="all",bt=w.useCallback(Ke=>{Ke.preventDefault(),Ke.dataTransfer.types.includes("Files")&&ne(!0)},[]),Ot=w.useCallback(Ke=>{Ke.preventDefault(),Ke.currentTarget===Ke.target&&ne(!1)},[]),Lt=w.useCallback(Ke=>{Ke.preventDefault(),ne(!1);const Ht=Array.from(Ke.dataTransfer.files).filter(Yt=>Yt.name.endsWith(".3mf"));Ht.length>0?(X(Ht),$(!0)):Ke.dataTransfer.files.length>0&&n(t("archives.page.only3mfSupported"),"warning")},[n,t]),Ft=w.useCallback(Ke=>{const Ht=Ke.target;if(Ht.tagName==="INPUT"||Ht.tagName==="TEXTAREA"||Ht.isContentEditable){Ke.key==="Escape"&&Ht.blur();return}switch(Ke.key){case"/":Ke.preventDefault(),s.current?.focus();break;case"u":case"U":!Ke.metaKey&&!Ke.ctrlKey&&(Ke.preventDefault(),$(!0));break;case"Escape":Ae&&nt();break}},[Ae]);return w.useEffect(()=>(document.addEventListener("keydown",Ft),()=>document.removeEventListener("keydown",Ft)),[Ft]),a.jsxs("div",{className:"p-4 md:p-8 relative",onDragOver:bt,onDragLeave:Ot,onDrop:Lt,children:[R&&a.jsx("div",{className:"fixed inset-0 z-50 bg-bambu-dark/90 flex items-center justify-center pointer-events-none",children:a.jsxs("div",{className:"border-4 border-dashed border-bambu-green rounded-xl p-12 text-center",children:[a.jsx(_a,{className:"w-16 h-16 mx-auto mb-4 text-bambu-green"}),a.jsx("p",{className:"text-2xl font-semibold text-white mb-2",children:"Drop .3mf files here"}),a.jsx("p",{className:"text-bambu-gray",children:t("archives.releaseToUpload")})]})}),Ae&&a.jsxs("div",{className:"fixed bottom-6 left-1/2 -translate-x-1/2 z-40 bg-bambu-dark-secondary border border-bambu-dark-tertiary rounded-lg shadow-xl px-4 py-3 flex items-center gap-4",children:[a.jsxs(De,{variant:"secondary",size:"sm",onClick:nt,children:[a.jsx(Bt,{className:"w-4 h-4"}),"Close"]}),a.jsx("div",{className:"w-px h-6 bg-bambu-dark-tertiary"}),a.jsxs("span",{className:"text-white font-medium",children:[re.size," selected"]}),a.jsx("div",{className:"w-px h-6 bg-bambu-dark-tertiary"}),a.jsx(De,{variant:"secondary",size:"sm",onClick:tt,children:"Select All"}),a.jsx("div",{className:"w-px h-6 bg-bambu-dark-tertiary"}),a.jsxs(De,{variant:"secondary",size:"sm",onClick:()=>pe(!0),disabled:!i("archives:update_own","archives:update_all"),title:i("archives:update_own","archives:update_all")?void 0:t("archives.permission.noUpdateArchives"),children:[a.jsx(ym,{className:"w-4 h-4"}),"Tags"]}),a.jsxs(De,{variant:"secondary",size:"sm",onClick:()=>fe(!0),disabled:!i("archives:update_own","archives:update_all"),title:i("archives:update_own","archives:update_all")?void 0:t("archives.permission.noUpdateArchives"),children:[a.jsx($s,{className:"w-4 h-4"}),"Project"]}),a.jsxs(De,{variant:"secondary",size:"sm",disabled:!i("archives:update_own","archives:update_all"),title:i("archives:update_own","archives:update_all")?void 0:t("archives.permission.noUpdateArchives"),onClick:()=>{const Ke=Array.from(re);Promise.all(Ke.map(Ht=>oe.toggleFavorite(Ht))).then(()=>{e.invalidateQueries({queryKey:["archives"]}),n(`Toggled favorites for ${Ke.length} archive${Ke.length!==1?"s":""}`)}).catch(()=>{n(t("archives.toast.failedUpdateFavorites"),"error")})},children:[a.jsx(bg,{className:"w-4 h-4"}),"Favorite"]}),a.jsxs(De,{size:"sm",className:"bg-red-500 hover:bg-red-600",onClick:()=>K(!0),disabled:!i("archives:delete_own","archives:delete_all"),title:i("archives:delete_own","archives:delete_all")?void 0:t("archives.permission.noDelete"),children:[a.jsx(Zt,{className:"w-4 h-4"}),"Delete"]})]}),a.jsxs("div",{className:"flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4 mb-8",children:[a.jsxs("div",{children:[a.jsxs("div",{className:"flex items-center gap-3",children:[a.jsx("h1",{className:"text-2xl font-bold text-white",children:"Archives"}),a.jsx("select",{className:"px-3 py-1.5 bg-bambu-dark border border-bambu-dark-tertiary rounded-lg text-bambu-gray-light text-sm focus:border-bambu-green focus:outline-none",value:U,onChange:Ke=>Q(Ke.target.value),children:Pet.map(Ke=>a.jsx("option",{value:Ke.id,children:Ke.label},Ke.id))})]}),a.jsxs("p",{className:"text-bambu-gray",children:[Ie?.length||0," of ",Se?.length||0," prints"]})]}),a.jsxs("div",{className:"flex items-center gap-2 sm:gap-3 flex-wrap",children:[a.jsxs("div",{className:"relative",children:[a.jsxs(De,{variant:"secondary",onClick:()=>W(!q),disabled:te,children:[te?a.jsx(mt,{className:"w-4 h-4 animate-spin"}):a.jsx(AN,{className:"w-4 h-4"}),"Export"]}),q&&a.jsxs("div",{className:"absolute right-0 top-full mt-1 w-48 bg-bambu-dark-secondary border border-bambu-dark-tertiary rounded-lg shadow-xl z-20",children:[a.jsxs("button",{className:"w-full px-4 py-2 text-left text-white hover:bg-bambu-dark-tertiary transition-colors flex items-center gap-2 rounded-t-lg",onClick:async()=>{W(!1),le(!0);try{const{blob:Ke,filename:Ht}=await oe.exportArchives({format:"csv",printerId:c||void 0,status:U==="failed"?"failed":void 0,search:o||void 0}),Yt=URL.createObjectURL(Ke),on=document.createElement("a");on.href=Yt,on.download=Ht,on.click(),URL.revokeObjectURL(Yt),n(t("archives.toast.exportDownloaded"))}catch{n(t("archives.toast.exportFailed"),"error")}finally{le(!1)}},children:[a.jsx(Ns,{className:"w-4 h-4"}),"Export as CSV"]}),a.jsxs("button",{className:"w-full px-4 py-2 text-left text-white hover:bg-bambu-dark-tertiary transition-colors flex items-center gap-2 rounded-b-lg",onClick:async()=>{W(!1),le(!0);try{const{blob:Ke,filename:Ht}=await oe.exportArchives({format:"xlsx",printerId:c||void 0,status:U==="failed"?"failed":void 0,search:o||void 0}),Yt=URL.createObjectURL(Ke),on=document.createElement("a");on.href=Yt,on.download=Ht,on.click(),URL.revokeObjectURL(Yt),n(t("archives.toast.exportDownloaded"))}catch{n(t("archives.toast.exportFailed"),"error")}finally{le(!1)}},children:[a.jsx(AN,{className:"w-4 h-4"}),"Export as Excel"]})]})]}),re.size>=2&&re.size<=5&&a.jsxs(De,{variant:"secondary",onClick:()=>he(!0),children:[a.jsx(Rx,{className:"w-4 h-4"}),"Compare (",re.size,")"]}),!Ae&&a.jsxs(De,{variant:"secondary",onClick:()=>ie(!0),children:[a.jsx(As,{className:"w-4 h-4"}),"Select"]}),a.jsxs(De,{onClick:()=>$(!0),disabled:!r("archives:create"),title:r("archives:create")?void 0:t("archives.permission.noCreate"),children:[a.jsx(_a,{className:"w-4 h-4"}),"Upload 3MF"]}),r("archives:purge")&&a.jsxs(De,{variant:"secondary",onClick:()=>ee(!0),title:t("archivePurge.headerTooltip"),children:[a.jsx(Zt,{className:"w-4 h-4 mr-2"}),t("archivePurge.headerButton")]})]})]}),a.jsxs("div",{className:"flex items-center border border-bambu-dark-tertiary rounded-lg overflow-hidden flex-shrink-0 w-fit mb-4",children:[a.jsx("button",{className:`p-2 ${B==="grid"?"bg-bambu-green text-white":"bg-bambu-dark text-bambu-gray hover:text-white"}`,onClick:()=>Y("grid"),title:t("archives.gridView"),children:a.jsx(sS,{className:"w-4 h-4"})}),a.jsx("button",{className:`p-2 ${B==="list"?"bg-bambu-green text-white":"bg-bambu-dark text-bambu-gray hover:text-white"}`,onClick:()=>Y("list"),title:t("archives.listView"),children:a.jsx(oS,{className:"w-4 h-4"})}),a.jsx("button",{className:`p-2 ${B==="calendar"?"bg-bambu-green text-white":"bg-bambu-dark text-bambu-gray hover:text-white"}`,onClick:()=>Y("calendar"),title:t("archives.calendarView"),children:a.jsx(bfe,{className:"w-4 h-4"})}),a.jsx("button",{className:`p-2 ${B==="log"?"bg-bambu-green text-white":"bg-bambu-dark text-bambu-gray hover:text-white"}`,onClick:()=>Y("log"),title:t("archives.logView"),children:a.jsx($fe,{className:"w-4 h-4"})})]}),B!=="log"&&a.jsx(wt,{className:"mb-6",children:a.jsxs(St,{className:"py-4",children:[a.jsxs("div",{className:"flex flex-col md:flex-row gap-3 md:gap-4 md:items-center md:flex-wrap",children:[a.jsxs("div",{className:"w-full md:flex-1 relative md:min-w-[200px]",children:[a.jsx(Nr,{className:"absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-bambu-gray"}),a.jsx("input",{ref:s,type:"text",placeholder:t("archives.searchPlaceholder"),className:"w-full pl-10 pr-4 py-3 md:py-2 bg-bambu-dark border border-bambu-dark-tertiary rounded-lg text-white focus:border-bambu-green focus:outline-none",value:o,onChange:Ke=>l(Ke.target.value)})]}),a.jsxs("div",{className:"flex gap-2 md:gap-4 overflow-x-auto pb-1 md:pb-0 -mx-4 px-4 md:mx-0 md:px-0 md:flex-wrap scrollbar-hide",children:[a.jsxs("div",{className:"flex items-center gap-2 flex-shrink-0",children:[a.jsx(eT,{className:"w-4 h-4 text-bambu-gray hidden md:block"}),a.jsxs("select",{className:"px-3 py-2 bg-bambu-dark border border-bambu-dark-tertiary rounded-lg text-white focus:border-bambu-green focus:outline-none",value:c||"",onChange:Ke=>d(Ke.target.value?Number(Ke.target.value):null),children:[a.jsx("option",{value:"",children:"All Printers"}),st?.map(Ke=>a.jsx("option",{value:Ke.id,children:Ke.name},Ke.id))]})]}),a.jsxs("div",{className:"flex items-center gap-2 flex-shrink-0",children:[a.jsx(Za,{className:"w-4 h-4 text-bambu-gray hidden md:block"}),a.jsxs("select",{className:"px-3 py-2 bg-bambu-dark border border-bambu-dark-tertiary rounded-lg text-white focus:border-bambu-green focus:outline-none",value:u||"",onChange:Ke=>m(Ke.target.value||null),children:[a.jsx("option",{value:"",children:"All Materials"}),Jt.map(Ke=>a.jsx("option",{value:Ke,children:Ke},Ke))]})]}),a.jsxs("div",{className:"flex items-center gap-2 flex-shrink-0",children:[a.jsx(TN,{className:"w-4 h-4 text-bambu-gray hidden md:block"}),a.jsxs("select",{className:"px-3 py-2 bg-bambu-dark border border-bambu-dark-tertiary rounded-lg text-white focus:border-bambu-green focus:outline-none",value:F,onChange:Ke=>k(Ke.target.value),children:[a.jsx("option",{value:"all",children:"All Files"}),a.jsx("option",{value:"gcode",children:"Sliced (GCODE)"}),a.jsx("option",{value:"source",children:"Source Only"})]})]}),a.jsxs("button",{onClick:()=>g(!b),className:`flex items-center gap-2 px-3 py-2 rounded-lg border transition-colors flex-shrink-0 ${b?"bg-yellow-500/20 border-yellow-500 text-yellow-400":"bg-bambu-dark border-bambu-dark-tertiary text-bambu-gray hover:text-white"}`,title:t(b?"archives.showAll":"archives.showFavoritesOnly"),children:[a.jsx(bg,{className:`w-4 h-4 ${b?"fill-yellow-400":""}`}),a.jsx("span",{className:"text-sm hidden md:inline",children:"Favorites"})]}),a.jsxs("button",{onClick:()=>N(!S),className:`flex items-center gap-2 px-3 py-2 rounded-lg border transition-colors flex-shrink-0 ${S?"bg-red-500/20 border-red-500 text-red-400":"bg-bambu-dark border-bambu-dark-tertiary text-bambu-gray hover:text-white"}`,title:t(S?"archives.showFailedPrints":"archives.hideFailedPrints"),children:[a.jsx(Oa,{className:"w-4 h-4 "}),a.jsx("span",{className:"text-sm hidden md:inline",children:"Hide Failed"})]}),a.jsxs("button",{onClick:()=>C(!P),className:`flex items-center gap-2 px-3 py-2 rounded-lg border transition-colors flex-shrink-0 ${P?"bg-purple-500/20 border-purple-500 text-purple-400":"bg-bambu-dark border-bambu-dark-tertiary text-bambu-gray hover:text-white"}`,title:t("archives.hideDuplicates"),children:[a.jsx(ns,{className:"w-4 h-4"}),a.jsx("span",{className:"text-sm hidden md:inline",children:t("archives.hideDuplicates")})]}),Tt.length>0&&a.jsxs("div",{className:"flex items-center gap-2 flex-shrink-0",children:[a.jsx(ym,{className:"w-4 h-4 text-bambu-gray hidden md:block"}),a.jsxs("select",{className:"px-3 py-2 bg-bambu-dark border border-bambu-dark-tertiary rounded-lg text-white focus:border-bambu-green focus:outline-none",value:A||"",onChange:Ke=>T(Ke.target.value||null),children:[a.jsx("option",{value:"",children:"All Tags"}),Tt.map(Ke=>a.jsx("option",{value:Ke,children:Ke},Ke))]}),a.jsx("button",{onClick:()=>H(!0),className:"p-2 rounded-lg bg-bambu-dark border border-bambu-dark-tertiary text-bambu-gray hover:text-white hover:border-bambu-green transition-colors",title:t("archives.manageTags"),children:a.jsx(mS,{className:"w-4 h-4"})})]}),a.jsxs("div",{className:"flex items-center gap-2 flex-shrink-0",children:[a.jsx(WI,{className:"w-4 h-4 text-bambu-gray hidden md:block"}),a.jsxs("select",{className:"px-3 py-2 bg-bambu-dark border border-bambu-dark-tertiary rounded-lg text-white focus:border-bambu-green focus:outline-none",value:D,onChange:Ke=>M(Ke.target.value),children:[a.jsx("option",{value:"date-desc",children:t("archives.sortNewest")}),a.jsx("option",{value:"date-asc",children:t("archives.sortOldest")}),a.jsxs("option",{value:"name-asc",children:[t("archives.sortName")," A-Z"]}),a.jsxs("option",{value:"name-desc",children:[t("archives.sortName")," Z-A"]}),a.jsx("option",{value:"size-desc",children:t("archives.sortLargest")}),a.jsx("option",{value:"size-asc",children:t("archives.sortSmallest")})]})]})]}),Ct&&a.jsxs(De,{variant:"ghost",size:"sm",onClick:rt,className:"text-bambu-gray hover:text-white",children:[a.jsx(Bt,{className:"w-4 h-4"}),"Reset"]})]}),zt.length>0&&a.jsxs("div",{className:"flex items-center gap-3 mt-4 pt-4 border-t border-bambu-dark-tertiary",children:[a.jsx("span",{className:"text-xs text-bambu-gray",children:"Colors:"}),p.size>1&&a.jsx("button",{onClick:()=>v(Ke=>Ke==="or"?"and":"or"),className:`px-2 py-0.5 text-xs rounded transition-colors ${x==="and"?"bg-bambu-green text-white":"bg-bambu-dark-tertiary text-bambu-gray hover:text-white"}`,title:x==="or"?"Match ANY selected color":"Match ALL selected colors",children:x.toUpperCase()}),a.jsx("div",{className:"flex items-center gap-1.5 flex-wrap",children:zt.map(Ke=>a.jsx("button",{onClick:()=>Je(Ke),className:`w-6 h-6 rounded-full border-2 transition-all ${p.has(Ke)?"border-bambu-green scale-110":"border-white/20 hover:border-white/40"}`,style:{backgroundColor:Ke},title:Ke},Ke))}),p.size>0&&a.jsxs("button",{onClick:Ue,className:"text-xs text-bambu-gray hover:text-white flex items-center gap-1",children:[a.jsx(Bt,{className:"w-3 h-3"}),"Clear"]})]})]})}),a.jsx(wet,{}),$e?a.jsx("div",{className:"text-center py-12 text-bambu-gray",children:t("archives.loadingArchives")}):Ie?.length===0?a.jsx(wt,{children:a.jsxs(St,{className:"text-center py-12",children:[a.jsx("p",{className:"text-bambu-gray",children:t(o?"archives.noArchivesSearch":"archives.noArchivesYet")}),a.jsx("p",{className:"text-sm text-bambu-gray mt-2",children:"Archives are created automatically when prints complete"})]})}):B==="calendar"?a.jsx(wt,{className:"p-6",children:a.jsx($Ge,{archives:Ie||[],onArchiveClick:Ke=>{l(""),Y("grid"),ke(Ke.id)},highlightedArchiveId:ge})}):B==="grid"?a.jsxs(a.Fragment,{children:[a.jsx("div",{className:"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6",children:dt?.map(Ke=>a.jsx(ket,{archive:Ke,printerName:Ke.printer_id?jt.get(Ke.printer_id)||"Unknown":Ke.sliced_for_model?`Sliced for ${Ke.sliced_for_model}`:"No Printer",isSelected:re.has(Ke.id),onSelect:We,selectionMode:Ae,projects:He,isHighlighted:Ke.id===ge,timeFormat:Vt,preferredSlicer:Gt,useSlicerApi:Qt,currency:an,t,onNavigateToArchive:de},Ke.id))}),a.jsx(yY,{pageIndex:O,pageSize:I,totalRows:Re,totalPages:Le,onPageChange:ce,onPageSizeChange:Ke=>{G(Ke),ce(0)},t})]}):B==="list"?a.jsxs(a.Fragment,{children:[a.jsx(wt,{children:a.jsxs("div",{className:"divide-y divide-bambu-dark-tertiary",children:[a.jsxs("div",{className:"grid grid-cols-12 gap-4 px-4 py-3 text-xs text-bambu-gray font-medium",children:[a.jsx("div",{className:"col-span-1"}),a.jsx("div",{className:"col-span-4",children:"Name"}),a.jsx("div",{className:"col-span-2",children:"Printer"}),a.jsx("div",{className:"col-span-2",children:"Date"}),a.jsx("div",{className:"col-span-1",children:"Size"}),a.jsx("div",{className:"col-span-2 text-right",children:"Actions"})]}),dt?.map(Ke=>a.jsx(Net,{archive:Ke,printerName:Ke.printer_id?jt.get(Ke.printer_id)||"Unknown":Ke.sliced_for_model?`Sliced for ${Ke.sliced_for_model}`:"No Printer",isSelected:re.has(Ke.id),onSelect:We,selectionMode:Ae,projects:He,isHighlighted:Ke.id===ge,preferredSlicer:Gt,useSlicerApi:Qt,t,onNavigateToArchive:de},Ke.id))]})}),a.jsx(yY,{pageIndex:O,pageSize:I,totalRows:Re,totalPages:Le,onPageChange:ce,onPageSizeChange:Ke=>{G(Ke),ce(0)},t})]}):B==="log"?a.jsxs("div",{className:"space-y-4",children:[a.jsx(wt,{children:a.jsx(St,{className:"py-3",children:a.jsxs("div",{className:"flex flex-col md:flex-row gap-3 md:items-center md:flex-wrap",children:[a.jsxs("div",{className:"flex-1 relative md:min-w-[200px]",children:[a.jsx(Nr,{className:"absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-bambu-gray"}),a.jsx("input",{type:"text",placeholder:t("archives.searchPlaceholder"),className:"w-full pl-10 pr-4 py-2 bg-bambu-dark border border-bambu-dark-tertiary rounded-lg text-white focus:border-bambu-green focus:outline-none text-sm",value:o,onChange:Ke=>{l(Ke.target.value),ve(0)}})]}),a.jsxs("select",{className:"px-3 py-2 bg-bambu-dark border border-bambu-dark-tertiary rounded-lg text-white focus:border-bambu-green focus:outline-none text-sm",value:c||"",onChange:Ke=>{d(Ke.target.value?Number(Ke.target.value):null),ve(0)},children:[a.jsx("option",{value:"",children:t("archives.log.allPrinters")}),st?.map(Ke=>a.jsx("option",{value:Ke.id,children:Ke.name},Ke.id))]}),a.jsxs("select",{className:"px-3 py-2 bg-bambu-dark border border-bambu-dark-tertiary rounded-lg text-white focus:border-bambu-green focus:outline-none text-sm",value:xe||"",onChange:Ke=>{Fe(Ke.target.value||null),ve(0)},children:[a.jsx("option",{value:"",children:t("archives.log.allUsers")}),ht?.map(Ke=>a.jsx("option",{value:Ke.username,children:Ke.username},Ke.id))]}),a.jsxs("select",{className:"px-3 py-2 bg-bambu-dark border border-bambu-dark-tertiary rounded-lg text-white focus:border-bambu-green focus:outline-none text-sm",value:Be||"",onChange:Ke=>{Ce(Ke.target.value||null),ve(0)},children:[a.jsx("option",{value:"",children:t("archives.log.allStatuses")}),a.jsx("option",{value:"completed",children:t("archives.status.completed")}),a.jsx("option",{value:"failed",children:t("archives.status.failed")}),a.jsx("option",{value:"stopped",children:t("archives.status.stopped")}),a.jsx("option",{value:"cancelled",children:t("archives.log.cancelled")}),a.jsx("option",{value:"skipped",children:t("archives.log.skipped")})]}),a.jsxs("div",{className:"flex items-center gap-2",children:[a.jsx("label",{className:"text-sm text-bambu-gray",children:t("archives.log.dateFrom")}),a.jsx("input",{type:"date",className:"px-3 py-2 bg-bambu-dark border border-bambu-dark-tertiary rounded-lg text-white focus:border-bambu-green focus:outline-none text-sm",value:ze,onChange:Ke=>{Me(Ke.target.value),ve(0)}})]}),a.jsxs("div",{className:"flex items-center gap-2",children:[a.jsx("label",{className:"text-sm text-bambu-gray",children:t("archives.log.dateTo")}),a.jsx("input",{type:"date",className:"px-3 py-2 bg-bambu-dark border border-bambu-dark-tertiary rounded-lg text-white focus:border-bambu-green focus:outline-none text-sm",value:Oe,onChange:Ke=>{Ge(Ke.target.value),ve(0)}})]}),a.jsx("div",{className:"ml-auto",children:a.jsxs(De,{variant:"danger",size:"sm",onClick:()=>Pe(!0),disabled:!r("archives:delete_all")||dn.isPending,children:[a.jsx(Zt,{className:"w-4 h-4"}),t("archives.log.clearLog")]})})]})})}),a.jsx(wt,{children:qt?a.jsx("div",{className:"flex items-center justify-center py-12",children:a.jsx(mt,{className:"w-6 h-6 animate-spin text-bambu-green"})}):Mt?.items.length?a.jsxs(a.Fragment,{children:[a.jsx("div",{className:"overflow-x-auto",children:a.jsxs("table",{className:"w-full text-sm",children:[a.jsx("thead",{children:a.jsxs("tr",{className:"border-b border-bambu-dark-tertiary text-bambu-gray text-left",children:[a.jsx("th",{className:"px-4 py-3 font-medium",children:t("archives.log.date")}),a.jsx("th",{className:"px-4 py-3 font-medium",children:t("archives.log.printName")}),a.jsx("th",{className:"px-4 py-3 font-medium",children:t("archives.log.printer")}),a.jsx("th",{className:"px-4 py-3 font-medium",children:t("archives.log.user")}),a.jsx("th",{className:"px-4 py-3 font-medium",children:t("archives.log.status")}),a.jsx("th",{className:"px-4 py-3 font-medium",children:t("archives.log.duration")}),a.jsx("th",{className:"px-4 py-3 font-medium",children:t("archives.log.filament")})]})}),a.jsx("tbody",{className:"divide-y divide-bambu-dark-tertiary",children:Mt.items.map(Ke=>a.jsxs("tr",{className:"hover:bg-bambu-dark-secondary/50",children:[a.jsx("td",{className:"px-4 py-3 text-white whitespace-nowrap",children:vg(Ke.started_at||Ke.created_at,Vt)}),a.jsx("td",{className:"px-4 py-3",children:a.jsxs("div",{className:"flex items-center gap-2",children:[Ke.thumbnail_path&&a.jsx("img",{src:oe.getPrintLogThumbnail(Ke.id),alt:"",className:"w-8 h-8 rounded object-cover flex-shrink-0",onError:Ht=>{Ht.target.style.display="none"}}),a.jsx("span",{className:"text-white truncate max-w-[200px]",children:Ke.print_name||"—"})]})}),a.jsx("td",{className:"px-4 py-3 text-bambu-gray-light",children:Ke.printer_name||"—"}),a.jsx("td",{className:"px-4 py-3 text-bambu-gray-light",children:Ke.created_by_username||"—"}),a.jsx("td",{className:"px-4 py-3",children:a.jsx("span",{className:`inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium ${Ke.status==="completed"?"bg-green-500/20 text-green-400":Ke.status==="failed"?"bg-red-500/20 text-red-400":Ke.status==="stopped"?"bg-yellow-500/20 text-yellow-400":Ke.status==="cancelled"?"bg-orange-500/20 text-orange-400":Ke.status==="skipped"?"bg-blue-500/20 text-blue-400":"bg-gray-500/20 text-gray-400"}`,children:Ke.status})}),a.jsx("td",{className:"px-4 py-3 text-bambu-gray-light whitespace-nowrap",children:Ke.duration_seconds?Qi(Ke.duration_seconds):"—"}),a.jsx("td",{className:"px-4 py-3",children:a.jsxs("div",{className:"flex items-center gap-1.5",children:[Ke.filament_color&&a.jsx("span",{className:"w-3 h-3 rounded-full border border-black/20 flex-shrink-0",style:{backgroundColor:Ke.filament_color.startsWith("#")?Ke.filament_color:void 0}}),a.jsx("span",{className:"text-bambu-gray-light text-xs",children:Ke.filament_type||"—"})]})})]},Ke.id))})]})}),a.jsxs("div",{className:"flex items-center justify-between px-4 py-3 border-t border-bambu-dark-tertiary flex-wrap gap-2",children:[a.jsxs("div",{className:"flex items-center gap-3",children:[a.jsx("span",{className:"text-sm text-bambu-gray",children:t("archives.log.showing",{count:Math.min(et+Ve,Mt.total),total:Mt.total})}),a.jsxs("div",{className:"flex items-center gap-1.5",children:[a.jsx("label",{className:"text-xs text-bambu-gray",children:t("archives.log.rowsPerPage")}),a.jsxs("select",{className:"px-2 py-1 bg-bambu-dark border border-bambu-dark-tertiary rounded text-white text-xs focus:border-bambu-green focus:outline-none",value:Ve,onChange:Ke=>{we(Number(Ke.target.value)),ve(0)},children:[a.jsx("option",{value:10,children:"10"}),a.jsx("option",{value:25,children:"25"}),a.jsx("option",{value:50,children:"50"}),a.jsx("option",{value:100,children:"100"})]})]})]}),a.jsxs("div",{className:"flex items-center gap-2",children:[a.jsxs("span",{className:"text-sm text-bambu-gray",children:[t("archives.log.page")," ",Math.floor(et/Ve)+1," / ",Math.max(1,Math.ceil(Mt.total/Ve))]}),a.jsx(De,{variant:"secondary",size:"sm",onClick:()=>ve(Math.max(0,et-Ve)),disabled:et===0,children:a.jsx(Uo,{className:"w-4 h-4"})}),a.jsx(De,{variant:"secondary",size:"sm",onClick:()=>ve(et+Ve),disabled:et+Ve>=Mt.total,children:a.jsx(za,{className:"w-4 h-4"})})]})]})]}):a.jsx("div",{className:"text-center py-12 text-bambu-gray",children:t("archives.log.noEntries")})})]}):null,E&&a.jsx(EGe,{onClose:()=>{$(!1),X([])},initialFiles:z}),L&&a.jsx(FGe,{onClose:()=>ee(!1)}),se&&a.jsx(yn,{title:t("archives.modal.deleteArchives"),message:t("archives.modal.deleteArchivesConfirm",{count:re.size}),confirmText:t("archives.modal.deleteCount",{count:re.size}),variant:"danger",onConfirm:()=>{mn.mutate(Array.from(re)),K(!1)},onCancel:()=>K(!1)}),ae&&a.jsx(OGe,{selectedIds:Array.from(re),existingTags:Tt,onClose:()=>pe(!1)}),be&&a.jsx(zGe,{selectedIds:Array.from(re),onClose:()=>fe(!1)}),ye&&re.size>=2&&re.size<=5&&a.jsx(bet,{archiveIds:Array.from(re),onClose:()=>{he(!1),J(new Set),ie(!1)}}),me&&a.jsx(_et,{onClose:()=>H(!1)}),Xe&&a.jsx(yn,{title:t("archives.log.clearLogTitle"),message:t("archives.log.clearLogConfirm"),confirmText:t("archives.log.clearLogButton"),variant:"danger",onConfirm:()=>{dn.mutate(),Pe(!1)},onCancel:()=>Pe(!1)})]})}function yY({pageIndex:t,pageSize:e,totalRows:n,totalPages:r,onPageChange:i,onPageSizeChange:s,t:o}){const l=e===-1;if(r<=1&&!l)return null;const c=l?n||1:e;return a.jsxs("div",{className:"flex items-center justify-between pt-2 text-sm",children:[a.jsx("span",{className:"text-bambu-gray",children:l?`${n} ${o("archives.prints")}`:a.jsxs(a.Fragment,{children:[o("archives.pagination.showing")," ",t*c+1," ",o("archives.pagination.to")," ",Math.min((t+1)*c,n)," ",o("archives.pagination.of")," ",n," ",o("archives.prints")]})}),a.jsxs("div",{className:"flex items-center gap-2",children:[a.jsx("span",{className:"text-bambu-gray",children:o("archives.pagination.show")}),a.jsxs("select",{value:e,onChange:d=>s(Number(d.target.value)),className:"px-2 py-1 bg-bambu-dark-secondary border border-bambu-dark-tertiary rounded text-white text-sm focus:outline-none focus:border-bambu-green",children:[[25,50,100,200].map(d=>a.jsx("option",{value:d,children:d},d)),a.jsx("option",{value:-1,children:o("archives.pagination.all")})]}),!l&&a.jsxs(a.Fragment,{children:[a.jsx("button",{onClick:()=>i(0),disabled:t===0,className:"p-1.5 rounded text-bambu-gray hover:text-white disabled:opacity-30 disabled:cursor-not-allowed transition-colors",children:a.jsx(XI,{className:"w-4 h-4"})}),a.jsx("button",{onClick:()=>i(Math.max(0,t-1)),disabled:t===0,className:"p-1.5 rounded text-bambu-gray hover:text-white disabled:opacity-30 disabled:cursor-not-allowed transition-colors",children:a.jsx(Uo,{className:"w-4 h-4"})}),a.jsxs("span",{className:"text-bambu-gray px-2 whitespace-nowrap",children:[o("archives.pagination.page")," ",t+1," ",o("archives.pagination.of")," ",r]}),a.jsx("button",{onClick:()=>i(Math.min(r-1,t+1)),disabled:t>=r-1,className:"p-1.5 rounded text-bambu-gray hover:text-white disabled:opacity-30 disabled:cursor-not-allowed transition-colors",children:a.jsx(za,{className:"w-4 h-4"})}),a.jsx("button",{onClick:()=>i(r-1),disabled:t>=r-1,className:"p-1.5 rounded text-bambu-gray hover:text-white disabled:opacity-30 disabled:cursor-not-allowed transition-colors",children:a.jsx(YI,{className:"w-4 h-4"})})]})]})]})}function Aet(){for(var t=arguments.length,e=new Array(t),n=0;nr=>{e.forEach(i=>i(r))},e)}const CA=typeof window<"u"&&typeof window.document<"u"&&typeof window.document.createElement<"u";function Gy(t){const e=Object.prototype.toString.call(t);return e==="[object Window]"||e==="[object global]"}function A5(t){return"nodeType"in t}function qo(t){var e,n;return t?Gy(t)?t:A5(t)&&(e=(n=t.ownerDocument)==null?void 0:n.defaultView)!=null?e:window:window}function j5(t){const{Document:e}=qo(t);return t instanceof e}function WS(t){return Gy(t)?!1:t instanceof qo(t).HTMLElement}function Ale(t){return t instanceof qo(t).SVGElement}function Wy(t){return t?Gy(t)?t.document:A5(t)?j5(t)?t:WS(t)||Ale(t)?t.ownerDocument:document:document:document}const Qd=CA?w.useLayoutEffect:w.useEffect;function M5(t){const e=w.useRef(t);return Qd(()=>{e.current=t}),w.useCallback(function(){for(var n=arguments.length,r=new Array(n),i=0;i{t.current=setInterval(r,i)},[]),n=w.useCallback(()=>{t.current!==null&&(clearInterval(t.current),t.current=null)},[]);return[e,n]}function Yw(t,e){e===void 0&&(e=[t]);const n=w.useRef(t);return Qd(()=>{n.current!==t&&(n.current=t)},e),n}function KS(t,e){const n=w.useRef();return w.useMemo(()=>{const r=t(n.current);return n.current=r,r},[...e])}function BP(t){const e=M5(t),n=w.useRef(null),r=w.useCallback(i=>{i!==n.current&&e?.(i,n.current),n.current=i},[]);return[n,r]}function yI(t){const e=w.useRef();return w.useEffect(()=>{e.current=t},[t]),e.current}let M3={};function XS(t,e){return w.useMemo(()=>{if(e)return e;const n=M3[t]==null?0:M3[t]+1;return M3[t]=n,t+"-"+n},[t,e])}function jle(t){return function(e){for(var n=arguments.length,r=new Array(n>1?n-1:0),i=1;i{const l=Object.entries(o);for(const[c,d]of l){const u=s[c];u!=null&&(s[c]=u+t*d)}return s},{...e})}}const Xx=jle(1),Qw=jle(-1);function Met(t){return"clientX"in t&&"clientY"in t}function E5(t){if(!t)return!1;const{KeyboardEvent:e}=qo(t.target);return e&&t instanceof e}function Eet(t){if(!t)return!1;const{TouchEvent:e}=qo(t.target);return e&&t instanceof e}function vI(t){if(Eet(t)){if(t.touches&&t.touches.length){const{clientX:e,clientY:n}=t.touches[0];return{x:e,y:n}}else if(t.changedTouches&&t.changedTouches.length){const{clientX:e,clientY:n}=t.changedTouches[0];return{x:e,y:n}}}return Met(t)?{x:t.clientX,y:t.clientY}:null}const Sy=Object.freeze({Translate:{toString(t){if(!t)return;const{x:e,y:n}=t;return"translate3d("+(e?Math.round(e):0)+"px, "+(n?Math.round(n):0)+"px, 0)"}},Scale:{toString(t){if(!t)return;const{scaleX:e,scaleY:n}=t;return"scaleX("+e+") scaleY("+n+")"}},Transform:{toString(t){if(t)return[Sy.Translate.toString(t),Sy.Scale.toString(t)].join(" ")}},Transition:{toString(t){let{property:e,duration:n,easing:r}=t;return e+" "+n+"ms "+r}}}),vY="a,frame,iframe,input:not([type=hidden]):not(:disabled),select:not(:disabled),textarea:not(:disabled),button:not(:disabled),*[tabindex]";function Det(t){return t.matches(vY)?t:t.querySelector(vY)}const Fet={display:"none"};function Ret(t){let{id:e,value:n}=t;return la.createElement("div",{id:e,style:Fet},n)}function Let(t){let{id:e,announcement:n,ariaLiveType:r="assertive"}=t;const i={position:"fixed",top:0,left:0,width:1,height:1,margin:-1,border:0,padding:0,overflow:"hidden",clip:"rect(0 0 0 0)",clipPath:"inset(100%)",whiteSpace:"nowrap"};return la.createElement("div",{id:e,style:i,role:"status","aria-live":r,"aria-atomic":!0},n)}function Iet(){const[t,e]=w.useState("");return{announce:w.useCallback(r=>{r!=null&&e(r)},[]),announcement:t}}const Mle=w.createContext(null);function Oet(t){const e=w.useContext(Mle);w.useEffect(()=>{if(!e)throw new Error("useDndMonitor must be used within a children of ");return e(t)},[t,e])}function zet(){const[t]=w.useState(()=>new Set),e=w.useCallback(r=>(t.add(r),()=>t.delete(r)),[t]);return[w.useCallback(r=>{let{type:i,event:s}=r;t.forEach(o=>{var l;return(l=o[i])==null?void 0:l.call(o,s)})},[t]),e]}const Uet={draggable:` To pick up a draggable item, press the space bar. diff --git a/static/index.html b/static/index.html index 9a768ba7d..a43c00463 100644 --- a/static/index.html +++ b/static/index.html @@ -26,7 +26,7 @@ - +